tessel / t2-cli Goto Github PK
View Code? Open in Web Editor NEWTessel 2 Command Line Interface
License: MIT License
Tessel 2 Command Line Interface
License: MIT License
Every once in a while, I receive an error that a process is attempting to exec
before the SSH process is opened:
INFO Connected over LAN. IP ADDRESS: 192.168.128.149
Unhandled rejection Error: Not connected
at Client.exec (/Users/Jon/Work/technical/t2-cli/node_modules/ssh2/lib/client.js:544:11)
at LANConnection.exec (/Users/Jon/Work/technical/t2-cli/lib/lan_connection.js:39:12)
at stopRunningScript (/Users/Jon/Work/technical/t2-cli/lib/tessel/deploy.js:51:16)
at Tessel.deployScript (/Users/Jon/Work/technical/t2-cli/lib/tessel/deploy.js:25:3)
at /Users/Jon/Work/technical/t2-cli/lib/controller.js:173:14
at tryCatcher (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/util.js:24:31)
at Promise._settlePromiseFromHandler (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/promise.js:454:31)
at Promise._settlePromiseAt (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/promise.js:530:18)
at Promise._settlePromises (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/promise.js:646:14)
at Async._drainQueue (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/async.js:79:16)
at Async._drainQueues (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/async.js:89:10)
at Async.drainQueues (/Users/Jon/Work/technical/t2-cli/node_modules/bluebird/js/main/async.js:14:14)
at process._tickCallback (node.js:355:11)
Currently, the tessel list
command waits an arbitrary number of seconds while searching for Tessels on the LAN. This should be changed so that USB Tessels are returned immediately and asynchronously from the LAN Tessels and the LAN Tessels should be returned faster than 2 seconds.
This should be a general solution. Ideally: I can add several hardcoded IPs and ports for remote tessels, or a local Tessel VM operating on port 222, and have them show up on tessel list
and tessel select
.
Current status: no such command
Spec: T2 CLI spec
tessel rename [-r | --reset]
Names the Tessel by changing the hostname of the device. Name restrictions found here: http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
$ tessel rename
> Usage: tessel rename <name> [-r | --reset] [--help]
> name: the name to rename the currently selected tessel
> -r | --reset: reset the name of the device
$
$ tessel rename Jigglypuff
> Changed name of device Tessel-0123456789ab to Jigglypuff
$
$ tessel rename Snorlax
> Changed name of device Jigglypuff to Snorlax
$
$ tessel rename --reset
> Reset the name of device to Tessel-0123456789ab
$
I believe the /tmp/ folder uses onboard RAM so it's faster to write and read data.
There are a couple of issues with getTessel
(that, frankly, I should have caught in my last code review).
First, I believe the lan.findConnections
and usb.findConnection
functions in the Promise.join need to be invoked with parenthesis.
Second, Promise.join returns two arrays and those need to be consolidated.
Currently, you have to manage a config file in this repo to be able to connect to a Tessel 2 remotely. We should be able to get up and running with Tessel over USB (like sending SSH keys over to the device).
Generate an ssh key for a tessel user and store it in a tessel specific folder (like ~/.tessel on OSX). When a Prime is plugged in via USB, transfer that ssh key to the Tessel under a 'tessel' account.
We want to have a clean abstraction for communication between a host computer and Tessel 2. Because they can connect over USB or SSH (WiFi), it would be much simpler to provide a "Connection" object which handles the actual transfer of data.
Current status: no such command
Spec: T2 CLI spec
tessel root [-i ]
Provides access to the underlying Linux system running on Tessel 2: exactly SSH (copies regular SSH keys to authorize and SSHes in as Linux computer)
$ tessel root --help
> Usage: tessel root [-i <path>] [--help]
> -i <path>: provide a path to the desired ssh key
$
$ tessel root
> Accessing root...
[email protected] #
Current status: no such command
Spec: T2 CLI spec
tessel [--help | -h]
Prints out all the commands and what they do (usage)
$ tessel
> tessel <command> [--help]
> help prints usage. Add ‘--help’ after any command for more info
> init initialize the current directory as your working directory
> list list nearby tessel devices
> select choose which tessel you want to interact with
> name change the hostname of the selected tessel
> erase erase code pushed to flash
> logs display the console.logs from the currently running process
> push push a file to flash to be run anytime tessel is powered
> root provides access to the underlying linux system on board
> run run a file in RAM immediately
> status get the current status of the selected tessel
> update update the selected tessel to latest firmware build
> wap create a wireless access point on the selected tessel
> wifi configure wifi connection on the selected tessel
$
$ tessel --help
> tessel <command> [--help]
> ...
$
This is an issue on the jon-usb
branch.
Currently, if a new process is created and then immediately writes and closes stdin, the bundle will not be written in its entirety.
tessel.connection.exec(someBashCommand, function(err, remoteProcess) {
// Not all of someBundle will be written
remoteProcess.stdin.end(someBundle);
});
That's because the USBDaemon on Tessel 2 may not have sent an acknowledgement that it can receive data on stdin yet. So when the _write
function is called internally by the stdin Node stream, the data is placed in the backPressure Buffer and the callback is called right away. Once the callback is called, the finish
event is fired and the CLI tells the daemon to close stdin. Meanwhile, that bundle is still sitting in the backPressure buffer.
Each command in tessel-2.js has a .help option, e.g.:
parser.command('push')
.callback(function(opts) {
// true: push=true
controller.deployScript(opts, true)
.catch(function (err) {
if(err instanceof Error){
throw err;
}
tessel.logs.warn(err);
process.exit(1);
});
})
.option('name', nameOption)
.option('entryPoint', {
position: 1,
required: true,
help: 'the entry point file to deploy to Tessel'
})
.option('verbose', {
flag : true,
abbr: 'v',
help: 'choose to view more debugging information'
})
.help('Deploy a script to memory on Tessel and run it with Node whenever Tessel boots up.');
They're not really being used. When you call tessel push -h
, it should print out Deploy a script to memory on Tessel and run it with Node whenever Tessel boots up.
. But instead it just gives you the generic Usage: t2 <command>
.
This should work on flags, too, e.g. tessel push -entryPoint -h
should print the entry point file to deploy to Tessel
.
Current status:
Spec: T2 CLI spec
tessel list
Lists all connected tessels. Shows which tessel is selected and authorization status of each tessel.
Find default per #93
# Help command example
$ tessel list --help
> Lists all connected tessels. Shows which tessel is selected and authorization status of each tessel.
$
# Single Tessel found example
$ tessel list
> Searching for nearby Tessels...
> Jigglypuff USB
$
# Same Tessel with both USB and LAN connections
$ tessel list
> Searching for nearby Tessels...
> Jigglypuff USB
> Jigglypuff LAN
$
# Many Tessels available
$ tessel list
> Searching for nearby Tessels…
> Rafael LAN
> Bulbasaur USB (run `tessel provision` to authorize)
> Mewcat LAN (USB connect and run `tessel provision` to authorize)
> Jigglypuff USB
> Jigglypuff LAN
>
> Multiple Tessels found. Will default to Bulbasaur.
> Set default Tessel with environment variable (e.g. export TESSEL=Bulbasaur) or use the --name flag.
$
# No Tessels available
$ tessel list
> Searching for nearby tessels...
> ERR! No Tessels Found
$
Option: tessel list -t to set a timeout
To do:
Current status: t2 run <index.js> will push index.js to RAM and run it immediately.
Spec: T2 CLI spec
tessel run <index.js> [--lan | --usb]
Run the file in RAM immediately
$ tessel run --help
>
> Usage: /usr/local/bin/iojs t2 run <entryPoint> [options]
>
> entryPoint The entry point file to deploy to Tessel
>
> Options:
> --name NAME The name of the tessel on which the command will be executed
> --lan Use LAN connection
> --usb Use USB connection
> -v, --verbose Choose to view more debugging information
>
> Deploy a script to Tessel and run it with Node
$ tessel run index.js
> Writing index.js to RAM on Mewcat (16kB)...
> Deployed.
> Running index.js...
> Hello world!
$
To do:
CLI is under design. This needs to be finalized and codified into issues.
Design completeness of commands:
I don't think the CLI spec lists all the commands we're planning to provide. What is missing?
E.g. something like tessel wap --start
turns tessel into a wireless access point
(Moved here from #5)
We really need a test bench with the CLI that will test all available commands over USB and over SSH. Currently, we've been just running all commands we can think of manually and it's not good. This is critical for making a CLI that will scale.
I think the first task is to break up all of the commands into a parsable JSON file so they can be gobbled up by the test runner.
Current status:
Spec: T2 CLI spec
tessel wifi
$ tessel wifi -h
> Usage: tessel wifi [-l] [-n <ssid>] [-p <pass>] [-s <security>] [--off] [-t]
> -l: list available networks
> -n <ssid>: name of the network to connect with
> -p <pass>: password to access network
> -s <security>: security protocol to connect with (i.e. WEP, WPA, PSK)
> -t: attempt to connect for the provided number of seconds
> --off: disconnect from a wifi connection
$
$ tessel wifi
> Connected to "technicallyWifi"
> IP Address: 192.168.128.149
> Signal Strength: 27/70
> Bitrate: 145mbps up / 24 mbps down
$ tessel wifi
> Not Connected (run 'tessel wifi -l' to see available networks)
$ tessel wifi -l
> INFO Requesting wifi status...
> Currently visible networks (10):
> Gaumenkitzel (47/127)
> XFBSECA7HE6H (30/127)
> HP-Print-8f-LaserJet 200 color (49/127)
> technicallyWifi (40/127)
> tessel (38/127)
> KeetsaBerkeley (37/127)
> ATT219_EXT (38/127)
> xfinitywifi (38/127)
> CableWiFi (32/127)
> Max4 (30/127)
$ tessel wifi -n test-network
> Connecting to network “test-network”...
> Connected to network “test-network”
$
$ tessel wifi -n test-network -p super-secret
> Connecting to network “test-network”...
> Failed to connect to “test-network” (ssid or password incorrect)
$ tessel wifi -n test-network -p secret
> Connecting to network “test-network”...
> Connected to network “test-network”
$
$ tessel wifi -n test-network -p secret -s WPA
> Connecting to network “test-network”...
> Connected to network “test-network”
$
$ tessel wifi -n test-network -t 1
> Connecting to network “test-network”...
> Failed to connect to “test-network” (timeout)
$
$ tessel wifi --list
> Currently connected to “test-network”
> Scanning for wifi connections...
> technicallyWifi (67/75)
> test-network (65/75) [connected]
> Gaumenkitzel (33/75)
$
To do:
tessel list
should show all Tessel 2s available over the network and USB.
Currently, the V2 CLI has a dependency on a branch of the V1 CLI. That needs to be surfaced into an npm release.
For now the workaround is to:
// Clone the CLI if you haven't already, outside the v2-cli repo
git clone [email protected]:tessel/cli.git
// Checkout the relevant branch
git checkout jon-export-v2
// Make this repo linkable
npm link --local
// Within the v2-cli repo, install all deps
npm install
// Delete the tessel cli module downloaded from npm
rm -rf node_modules/tessel
// Link it to the checked out branch
npm link tessel
Current status: erases everything in /app folder
Spec: T2 CLI spec
tessel erase
Erases files pushed to Flash using the tessel push command.
$ tessel erase --help
> Erases files pushed to Flash using the tessel push command.
$
$ tessel erase
> Erasing files from flash...
> Files erased
$
To do:
Does it also print out how to use/refer to any connected tessels?
(Moved here from #5)
Current status: no such command
Spec: T2 CLI spec
tessel ap [-n ] [-p ] [-s ] [on | off]
Creates a wireless access point.
$ tessel ap
> Usage: tessel ap [-n <ssid>] [-p <pass>] [-s <security>] [on | off]
> -n <ssid>: name of the network
> -p <pass>: password to access network
> -s <security>: encryption to use on network (i.e. WEP, WPA, PSK)
> on: bring up an access point that’s turned off
> off: turn off the access point
$
$ tessel ap -n test-network
> Created access point “test-network”
$
$ tessel ap off
> Turned off access point “test-network”
$
$ tessel ap -n test-network -p secret
> Created password protected access point “test-network”
$
$ tessel ap -n test-network -p secret -s WPA
> Created password protected access point “test-network” secured with WPA
$
$ tessel ap off
> Turned off access point “test-network”
$ tessel ap on
> Turned on access point “test-network”
$
Current status: t2 init creates a package.json and index.js containing Hello World example.
T2 CLI spec
To match spec, it needs:
$ tessel init --help
> Usage: tessel init [dir] [--help]
> dir: initialize in the provided directory
$
$ tessel init
> Initializing tessel repository...
> Created package.json.
> Wrote “Hello World” to index.js.
$
command t2 provision
T2 CLI spec: A silent command (not exposed in CLI): [ONLY WORKS OVER USB] creates a .tessel folder with ssh keys in your home directory and uses those ssh keys to authorize you to push code to the USB-connected Tessel
Current status:
t2 setup
is a callable commandRemaining issues:
Do we have a format planned for all of the information this is supposed to provide, or any -tags to make this interaction cleaner?
(Moved here from #5)
Does tessel wifi
do something by itself?
(moved here from #5)
Is there a reason these are missing?
[-p PASS] connects with the given PASS
[-t TIMEOUT] connects with the given TIMEOUT
[-s SECURITY] connects using the provided SECURITY protocol
(moved here from #5)
Current status: no such command
Spec: T2 CLI spec
tessel update [-l | --list] [-b | --build] [-f | --force]
Updates tessel to most recent build if it’s not already on that build, displays info including when tessel was last updated, when build was published
$ tessel update --help
> Usage: tessel update [-l | --list] [-b | --build]
> -l | --list: show the last five builds and current build
> -b | --build: update to a specific build
> -f | --force: force tessel to update to the specified version
$
$ tessel update
> New firmware version found...
> Updating Bulbasaur to latest build...
> Updated Bulbasaur from v0.3.12 to v0.3.14
$ tessel update
> Bulbasaur is already on the latest firmware build. You can force an update with “tessel update --force”
$
$ tessel update -b
> Usage: tessel update -b <build_number>
> Example: “tessel update -b 0.3.12”
> tessel update -b 0.3.12
> Updating Bulbasaur to v0.3.12...
> Updated Bulbasaur to v0.3.12
$
$ tessel update -l
> Latest builds:
> v0.3.14 Published 04/1/2015 03:35:57
> v0.3.13
> v0.3.12 Bulbasaur last updated 03/31/2015 08:22:03
> v0.3.11
> v0.3.10
> v0.3.09
$ tessel update
> New firmware version found...
> Updating Bulbasaur to latest build...
> Updated Bulbasaur from v0.3.12 to v0.3.14
$ tessel update -l
> Latest builds:
> v0.3.14 Bulbasaur last updated 03/31/2015 08:22:03 to latest firmware
> v0.3.13
> v0.3.12
> v0.3.11
> v0.3.10
> v0.3.09
$
Currently, I am picking up two instances of the same Tessel when running tessel list
. The objects returned by the mdns-js
library look like this (where the only difference is the networkInterface
and interfaceIndex
fields:
update { addresses: [ '172.20.10.16' ],
query: [ '_tessel._tcp.local' ],
type:
[ { name: 'tessel',
protocol: 'tcp',
subtypes: [],
description: undefined } ],
port: 22,
fullname: 't2._tessel._tcp.local',
txt: [ '' ],
host: 't2.local',
interfaceIndex: 0,
networkInterface: 'en0' }
update { addresses: [ '172.20.10.16' ],
query: [ '_tessel._tcp.local' ],
type:
[ { name: 'tessel',
protocol: 'tcp',
subtypes: [],
description: undefined } ],
port: 22,
fullname: 't2._tessel._tcp.local',
txt: [ '' ],
host: 't2.local',
interfaceIndex: 1,
networkInterface: 'en4' }
I'm not sure exactly what those fields indicate and whether or not this is an issue with the mdns implementation on the T2 or should just be accounted for in the CLI.
If you use tessel run
, it will deploy the code properly but you cannot control+c to exit.
Currently, the USB connection is presenting the serial number of the SAMD21 device descriptor. It should be reporting the hostname of the device.
This is required to be able to locate Tessel 2's available on a wireless network.
This should work essentially the same as tessel run except the file needs to be placed in a folder that won't be overwritten on power cycle and we need to add an "init.d" script to run node on the folder on startup.
@tcr suggested that both tessel push
and tessel run
push code to /app to maintain the absolute path.
@kevinmehall points out that Flash hardware wears out much faster than RAM and suggests we deploy to RAM.
I agree with @kevinmehall about deploying to RAM because folks really shouldn't be using absolute paths in their code anyway.
What exactly does this do? Should it be tessel ssh
instead?
(Moved here from #5)
Current status: no such command
Spec: T2 CLI spec
tessel logs [-n ] [-t | --tail] [stdout | stderr]
Lists the recent activity and console.logs (100 lines default) from the running program
$ tessel logs --help
> Usage: tessel logs [-n <lines>] [-t | --tail] [--help]
> -n: the number of lines of log to display
> -m|--monitor: continuously display the logs as they are updated
> stdout|stderr: display the logs from the specified output stream
$
$ tessel logs -n 2
> Bulbasaur is running index.js
> #[03/31/2015 18:10:28] This is my logggggg
> #[03/31/2015 18:10:29] Look at it!
$
$ tessel logs --tail
> Bulbasaur is running index.js
> #[03/31/2015 18:10:28] This is my logggggg
> #[03/31/2015 18:10:29] Look at it!
> #[03/31/2015 18:11:29] Look at it!
> #[03/31/2015 18:12:29] Look at it!
> #[03/31/2015 18:13:29] Look at it!
> #[03/31/2015 18:14:29] Look at it!
> #[03/31/2015 18:15:29] Look at it!
> #[03/31/2015 18:16:29] Look at it!
> #[03/31/2015 18:17:29] Look at it!
> #[03/31/2015 18:18:29] Look at it!
$
$ tessel logs -t stderr
> Bulbasaur is running index.js
> #[03/31/2015 18:10:28] Error: index.js:146 - index out of bounds yo
$
Test Cast from within this repo:
prime run test/test-deploy-script.js
Error:
Error: ENOENT, no such file or directory '/Users/Jon/Work/technical/prime-cli/test/index.js'
at Error (native)
at Object.fs.lstatSync (fs.js:796:18)
at Object.root (/Users/Jon/Work/technical/cli/node_modules/hardware-resolve/index.js:160:6)
at Object.analyzeScript (/Users/Jon/Work/technical/cli/src/script.js:37:19)
at /Users/Jon/Work/technical/prime-cli/lib/deploy.js:24:24
at /Users/Jon/Work/technical/prime-cli/node_modules/ssh2/lib/client.js:1165:5
at SSH2Stream.<anonymous> (/Users/Jon/Work/technical/prime-cli/node_modules/ssh2/lib/Channel.js:179:24)
at SSH2Stream.emit (events.js:104:17)
at parsePacket (/Users/Jon/Work/technical/prime-cli/node_modules/ssh2/node_modules/ssh2-streams/lib/ssh.js:3137:10)
Currently, if the /tmp/remote-script
folder is non-existant, the CLI will throw an error:
INFO Deploying code of size 23468 bytes ...
events.js:85
throw er; // Unhandled 'error' event
^
Error: write after end
at writeAfterEnd (_stream_writable.js:167:12)
at Channel.Writable.write (_stream_writable.js:214:5)
at Channel.Writable.end (_stream_writable.js:449:10)
at /Users/Jon/Work/technical/prime-cli/lib/deploy.js:34:16
at Parse.<anonymous> (/Users/Jon/Work/technical/cli/src/bundle.js:179:7)
at Parse.emit (events.js:104:17)
at Parse._streamEnd (/Users/Jon/Work/technical/cli/node_modules/tar/lib/parse.js:65:6)
at BlockStream.<anonymous> (/Users/Jon/Work/technical/cli/node_modules/tar/lib/parse.js:51:8)
at BlockStream.emit (events.js:104:17)
at BlockStream._emitChunk (/Users/Jon/Work/technical/cli/node_modules/tar/node_modules/block-stream/block-stream.js:203:10)
We need a way for people to define the root of their project. What better way than a tessel init
command? It should essentially call npm init
with all of the defaults and add a few relevant tags to the package.json ("hardware", "tessel", etc.)
It should just delete any contents of the folder that tessel push
commands deploy to.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.