Merge pull request #118 from resin-io/resin-cli-form

Integrate resin-cli-form
This commit is contained in:
Juan Cruz Viotti 2015-07-27 13:30:48 -04:00
commit d6ae689593
16 changed files with 438 additions and 131 deletions

View File

@ -1,5 +1,5 @@
(function() {
var _, async, commandOptions, path, resin, vcs, visuals;
var _, async, commandOptions, form, path, resin, vcs, visuals;
path = require('path');
@ -15,6 +15,8 @@
vcs = require('resin-vcs');
form = require('resin-cli-form');
exports.create = {
signature: 'app create <name>',
description: 'create an application',
@ -39,7 +41,13 @@
if (options.type != null) {
return callback(null, options.type);
}
return visuals.patterns.selectDeviceType(callback);
return resin.models.device.getSupportedDeviceTypes().then(function(supportedDeviceTypes) {
return form.ask({
message: 'Device Type',
type: 'list',
choices: supportedDeviceTypes
});
}).nodeify(callback);
}, function(type, callback) {
options.type = type;
return resin.models.application.create(params.name, options.type).nodeify(callback);
@ -92,9 +100,24 @@
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('application', options.yes, function(callback) {
return resin.models.application.remove(params.name).nodeify(callback);
}, done);
return async.waterfall([
function(callback) {
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: 'Are you sure you want to delete the application?',
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return callback();
}
return resin.models.application.remove(params.name).nodeify(callback);
}
], done);
}
};
@ -116,7 +139,15 @@
return callback(new Error("Invalid application: " + params.name));
}
message = "Are you sure you want to associate " + currentDirectory + " with " + params.name + "?";
return visuals.patterns.confirm(options.yes, message, callback);
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: message,
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return done();
@ -149,12 +180,11 @@
function(callback) {
var currentDirectoryBasename;
currentDirectoryBasename = path.basename(currentDirectory);
return visuals.form.ask({
label: 'What is the name of your application?',
name: 'application',
type: 'text',
value: currentDirectoryBasename
}, callback);
return form.ask({
message: 'What is the name of your application?',
type: 'input',
"default": currentDirectoryBasename
}).nodeify(callback);
}, function(applicationName, callback) {
return exports.create.action({
name: applicationName

View File

@ -1,5 +1,5 @@
(function() {
var TOKEN_URL, _, async, open, resin, settings, url, visuals;
var TOKEN_URL, _, async, form, open, resin, settings, url;
open = require('open');
@ -13,7 +13,7 @@
settings = require('resin-settings-client');
visuals = require('resin-cli-visuals');
form = require('resin-cli-form');
TOKEN_URL = url.resolve(settings.get('dashboardUrl'), '/preferences');
@ -32,7 +32,10 @@
if (error != null) {
console.error("Unable to open a web browser in the current environment.\nPlease visit " + TOKEN_URL + " manually.");
}
return visuals.patterns.loginWithToken(callback);
return form.ask({
message: 'What\'s your token? (visible in the preferences page)',
type: 'input'
}).nodeify(callback);
});
}, function(token, callback) {
return resin.auth.loginWithToken(token).nodeify(callback);
@ -97,7 +100,27 @@
if (hasOptionCredentials) {
return callback(null, options);
}
return visuals.patterns.register(callback);
return form.run([
{
message: 'Email:',
name: 'email',
type: 'input'
}, {
message: 'Username:',
name: 'username',
type: 'input'
}, {
message: 'Password:',
name: 'password',
type: 'password',
validate: function(input) {
if (input.length < 8) {
return 'Password should be 8 characters long';
}
return true;
}
}
]).nodeify(callback);
}, function(credentials, callback) {
return resin.auth.register(credentials)["return"](credentials).nodeify(callback);
}, function(credentials, callback) {

View File

@ -1,5 +1,5 @@
(function() {
var _, async, capitano, commandOptions, deviceConfig, fse, image, inject, manager, path, pine, registerDevice, resin, tmp, vcs, visuals;
var _, async, capitano, commandOptions, deviceConfig, form, fse, image, inject, manager, path, pine, registerDevice, resin, tmp, vcs, visuals;
fse = require('fs-extra');
@ -31,6 +31,8 @@
deviceConfig = require('resin-device-config');
form = require('resin-cli-form');
tmp.setGracefulCleanup();
commandOptions = require('./command-options');
@ -80,9 +82,24 @@
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('device', options.yes, function(callback) {
return resin.models.device.remove(params.uuid).nodeify(callback);
}, done);
return async.waterfall([
function(callback) {
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: 'Are you sure you want to delete the device?',
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return callback();
}
return resin.models.device.remove(params.uuid).nodeify(callback);
}
], done);
}
};
@ -107,11 +124,10 @@
if (!_.isEmpty(params.newName)) {
return callback(null, params.newName);
}
return visuals.form.ask({
label: 'How do you want to name this device?',
name: 'device',
type: 'text'
}, callback);
return form.ask({
message: 'How do you want to name this device?',
type: 'input'
}).nodeify(callback);
}, function(newName, callback) {
return resin.models.device.rename(params.uuid, newName).nodeify(callback);
}
@ -194,12 +210,39 @@
if (params.device != null) {
return callback(null, params.device);
}
return visuals.patterns.selectDrive(callback);
return drivelist.list(function(error, drives) {
if (error != null) {
return callback(error);
}
return async.reject(drives, drivelist.isSystem, function(removableDrives) {
if (_.isEmpty(removableDrives)) {
return callback(new Error('No available drives'));
}
return form.ask({
message: 'Drive',
type: 'list',
choices: _.map(removableDrives, function(item) {
return {
name: item.device + " (" + item.size + ") - " + item.description,
value: item.device
};
})
}).nodeify(callback);
});
});
}, function(device, callback) {
var message;
params.device = device;
message = "This will completely erase " + params.device + ". Are you sure you want to continue?";
return visuals.patterns.confirm(options.yes, message, callback);
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: message,
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return done();
@ -207,13 +250,30 @@
if (networkOptions.network != null) {
return callback();
}
return visuals.patterns.selectNetworkParameters(function(error, parameters) {
if (error != null) {
return callback(error);
return form.run([
{
message: 'Network Type',
name: 'network',
type: 'list',
choices: ['ethernet', 'wifi']
}, {
message: 'Wifi Ssid',
name: 'wifiSsid',
type: 'input',
when: {
network: 'wifi'
}
}, {
message: 'Wifi Key',
name: 'wifiKey',
type: 'input',
when: {
network: 'wifi'
}
}
_.extend(networkOptions, parameters);
return callback();
});
]).then(function(parameters) {
return _.extend(networkOptions, parameters);
}).nodeify(callback);
}, function(callback) {
console.info("Checking application: " + options.application);
return resin.models.application.get(options.application).nodeify(callback);

View File

@ -54,13 +54,28 @@
options: [commandOptions.yes, commandOptions.booleanDevice],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('environment variable', options.yes, function(callback) {
if (options.device) {
return resin.models.environmentVariables.device.remove(params.id).nodeify(callback);
} else {
return resin.models.environmentVariables.remove(params.id).nodeify(callback);
return async.waterfall([
function(callback) {
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: 'Are you sure you want to delete the environment variable?',
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return callback();
}
if (options.device) {
return resin.models.environmentVariables.device.remove(params.id).nodeify(callback);
} else {
return resin.models.environmentVariables.remove(params.id).nodeify(callback);
}
}
}, done);
], done);
}
};

View File

@ -1,5 +1,5 @@
(function() {
var SSH_KEY_WIDTH, _, async, capitano, commandOptions, fs, resin, visuals;
var SSH_KEY_WIDTH, _, async, capitano, commandOptions, form, fs, resin, visuals;
_ = require('lodash');
@ -17,6 +17,8 @@
commandOptions = require('./command-options');
form = require('resin-cli-form');
exports.list = {
signature: 'keys',
description: 'list all ssh keys',
@ -50,9 +52,24 @@
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('key', options.yes, function(callback) {
return resin.models.key.remove(params.id).nodeify(callback);
}, done);
return async.waterfall([
function(callback) {
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: 'Are you sure you want to delete the key?',
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return callback();
}
return resin.models.key.remove(params.id).nodeify(callback);
}
], done);
}
};

View File

@ -1,5 +1,5 @@
(function() {
var _, commandOptions, plugins, visuals;
var _, async, commandOptions, form, plugins, visuals;
_ = require('lodash');
@ -9,6 +9,10 @@
plugins = require('../plugins');
form = require('resin-cli-form');
async = require('async');
exports.list = {
signature: 'plugins',
description: 'list all plugins',
@ -68,15 +72,30 @@
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('plugin', options.yes, function(callback) {
return plugins.remove(params.name, callback);
}, function(error) {
if (error != null) {
return done(error);
return async.waterfall([
function(callback) {
if (options.yes) {
return callback(null, true);
} else {
return form.ask({
message: 'Are you sure you want to delete the plugin?',
type: 'confirm',
"default": false
}).nodeify(callback);
}
}, function(confirmed, callback) {
if (!confirmed) {
return callback();
}
return plugins.remove(params.name, callback);
}, function(error) {
if (error != null) {
return done(error);
}
console.info("Plugin removed: " + params.name);
return done();
}
console.info("Plugin removed: " + params.name);
return done();
});
]);
}
};

View File

@ -5,6 +5,7 @@ resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
commandOptions = require('./command-options')
vcs = require('resin-vcs')
form = require('resin-cli-form')
exports.create =
signature: 'app create <name>'
@ -34,7 +35,7 @@ exports.create =
]
permission: 'user'
action: (params, options, done) ->
async.waterfall([
async.waterfall [
(callback) ->
resin.models.application.has(params.name).nodeify(callback)
@ -44,7 +45,12 @@ exports.create =
return callback(new Error('You already have an application with that name!'))
return callback(null, options.type) if options.type?
visuals.patterns.selectDeviceType(callback)
resin.models.device.getSupportedDeviceTypes().then (supportedDeviceTypes) ->
form.ask
message: 'Device Type'
type: 'list'
choices: supportedDeviceTypes
.nodeify(callback)
(type, callback) ->
options.type = type
@ -54,7 +60,7 @@ exports.create =
console.info("Application created: #{params.name} (#{options.type}, id #{applicationId})")
return callback()
], done)
], done
exports.list =
signature: 'apps'
@ -134,9 +140,22 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'application', options.yes, (callback) ->
resin.models.application.remove(params.name).nodeify(callback)
, done
async.waterfall [
(callback) ->
if options.yes
return callback(null, true)
else
form.ask
message: 'Are you sure you want to delete the application?'
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return callback() if not confirmed
resin.models.application.remove(params.name).nodeify(callback)
], done
exports.associate =
signature: 'app associate <name>'
@ -169,7 +188,14 @@ exports.associate =
return callback(new Error("Invalid application: #{params.name}"))
message = "Are you sure you want to associate #{currentDirectory} with #{params.name}?"
visuals.patterns.confirm(options.yes, message, callback)
if options.yes
return callback(null, true)
else
form.ask
message: message
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return done() if not confirmed
@ -207,16 +233,15 @@ exports.init =
currentDirectory = process.cwd()
async.waterfall([
async.waterfall [
(callback) ->
currentDirectoryBasename = path.basename(currentDirectory)
visuals.form.ask
label: 'What is the name of your application?'
name: 'application'
type: 'text'
value: currentDirectoryBasename
, callback
form.ask
message: 'What is the name of your application?'
type: 'input'
default: currentDirectoryBasename
.nodeify(callback)
(applicationName, callback) ->
@ -229,4 +254,4 @@ exports.init =
(applicationName, callback) ->
exports.associate.action(name: applicationName, options, callback)
], done)
], done

View File

@ -4,7 +4,7 @@ url = require('url')
async = require('async')
resin = require('resin-sdk')
settings = require('resin-settings-client')
visuals = require('resin-cli-visuals')
form = require('resin-cli-form')
TOKEN_URL = url.resolve(settings.get('dashboardUrl'), '/preferences')
@ -25,7 +25,7 @@ exports.login =
"""
action: (params, options, done) ->
async.waterfall([
async.waterfall [
(callback) ->
return callback(null, params.token) if params.token?
@ -44,7 +44,10 @@ exports.login =
Please visit #{TOKEN_URL} manually.
"""
visuals.patterns.loginWithToken(callback)
form.ask
message: 'What\'s your token? (visible in the preferences page)'
type: 'input'
.nodeify(callback)
(token, callback) ->
resin.auth.loginWithToken(token).nodeify(callback)
@ -56,7 +59,7 @@ exports.login =
console.info("Successfully logged in as: #{username}")
return callback()
], done)
], done
exports.logout =
signature: 'logout'
@ -127,11 +130,29 @@ exports.signup =
if not options.password?
return done(new Error('Missing password'))
async.waterfall([
async.waterfall [
(callback) ->
return callback(null, options) if hasOptionCredentials
visuals.patterns.register(callback)
form.run [
message: 'Email:'
name: 'email'
type: 'input'
,
message: 'Username:'
name: 'username'
type: 'input'
,
message: 'Password:'
name: 'password'
type: 'password',
validate: (input) ->
if input.length < 8
return 'Password should be 8 characters long'
return true
]
.nodeify(callback)
(credentials, callback) ->
resin.auth.register(credentials).return(credentials).nodeify(callback)
@ -139,7 +160,7 @@ exports.signup =
(credentials, callback) ->
resin.auth.login(credentials).nodeify(callback)
], done)
], done
exports.whoami =
signature: 'whoami'

View File

@ -13,6 +13,7 @@ registerDevice = require('resin-register-device')
pine = require('resin-pine')
tmp = require('tmp')
deviceConfig = require('resin-device-config')
form = require('resin-cli-form')
# Cleanup the temporary files even when an uncaught exception occurs
tmp.setGracefulCleanup()
@ -110,9 +111,22 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'device', options.yes, (callback) ->
resin.models.device.remove(params.uuid).nodeify(callback)
, done
async.waterfall [
(callback) ->
if options.yes
return callback(null, true)
else
form.ask
message: 'Are you sure you want to delete the device?'
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return callback() if not confirmed
resin.models.device.remove(params.uuid).nodeify(callback)
], done
exports.identify =
signature: 'device identify <uuid>'
@ -151,11 +165,10 @@ exports.rename =
if not _.isEmpty(params.newName)
return callback(null, params.newName)
visuals.form.ask
label: 'How do you want to name this device?'
name: 'device'
type: 'text'
, callback
form.ask
message: 'How do you want to name this device?'
type: 'input'
.nodeify(callback)
(newName, callback) ->
resin.models.device.rename(params.uuid, newName).nodeify(callback)
@ -263,7 +276,7 @@ exports.init =
wifiSsid: options.ssid
wifiKey: options.key
async.waterfall([
async.waterfall [
(callback) ->
return callback(null, options.application) if options.application?
@ -278,20 +291,60 @@ exports.init =
return callback(new Error("Invalid application: #{options.application}"))
return callback(null, params.device) if params.device?
visuals.patterns.selectDrive(callback)
drivelist.list (error, drives) ->
return callback(error) if error?
async.reject drives, drivelist.isSystem, (removableDrives) ->
if _.isEmpty(removableDrives)
return callback(new Error('No available drives'))
form.ask
message: 'Drive'
type: 'list'
choices: _.map removableDrives, (item) ->
return {
name: "#{item.device} (#{item.size}) - #{item.description}"
value: item.device
}
.nodeify(callback)
(device, callback) ->
params.device = device
message = "This will completely erase #{params.device}. Are you sure you want to continue?"
visuals.patterns.confirm(options.yes, message, callback)
if options.yes
return callback(null, true)
else
form.ask
message: message
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return done() if not confirmed
return callback() if networkOptions.network?
visuals.patterns.selectNetworkParameters (error, parameters) ->
return callback(error) if error?
form.run [
message: 'Network Type'
name: 'network'
type: 'list'
choices: [ 'ethernet', 'wifi' ]
,
message: 'Wifi Ssid'
name: 'wifiSsid'
type: 'input'
when:
network: 'wifi'
,
message: 'Wifi Key'
name: 'wifiKey'
type: 'input'
when:
network: 'wifi'
]
.then (parameters) ->
_.extend(networkOptions, parameters)
return callback()
.nodeify(callback)
(callback) ->
console.info("Checking application: #{options.application}")
@ -366,4 +419,4 @@ exports.init =
console.info("Device created: #{device.name}")
return callback(null, device.name)
], done)
], done

View File

@ -34,7 +34,7 @@ exports.list =
]
permission: 'user'
action: (params, options, done) ->
async.waterfall([
async.waterfall [
(callback) ->
if options.application?
@ -58,7 +58,7 @@ exports.list =
return callback()
], done)
], done
exports.remove =
signature: 'env rm <id>'
@ -85,12 +85,25 @@ exports.remove =
]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'environment variable', options.yes, (callback) ->
if options.device
resin.models.environmentVariables.device.remove(params.id).nodeify(callback)
else
resin.models.environmentVariables.remove(params.id).nodeify(callback)
, done
async.waterfall [
(callback) ->
if options.yes
return callback(null, true)
else
form.ask
message: 'Are you sure you want to delete the environment variable?'
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return callback() if not confirmed
if options.device
resin.models.environmentVariables.device.remove(params.id).nodeify(callback)
else
resin.models.environmentVariables.remove(params.id).nodeify(callback)
], done
exports.add =
signature: 'env add <key> [value]'

View File

@ -6,6 +6,7 @@ resin = require('resin-sdk')
capitano = require('capitano')
visuals = require('resin-cli-visuals')
commandOptions = require('./command-options')
form = require('resin-cli-form')
exports.list =
signature: 'keys'
@ -58,9 +59,22 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'key', options.yes, (callback) ->
resin.models.key.remove(params.id).nodeify(callback)
, done
async.waterfall [
(callback) ->
if options.yes
return callback(null, true)
else
form.ask
message: 'Are you sure you want to delete the key?'
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return callback() if not confirmed
resin.models.key.remove(params.id).nodeify(callback)
], done
exports.add =
signature: 'key add <name> [path]'

View File

@ -2,6 +2,8 @@ _ = require('lodash')
visuals = require('resin-cli-visuals')
commandOptions = require('./command-options')
plugins = require('../plugins')
form = require('resin-cli-form')
async = require('async')
exports.list =
signature: 'plugins'
@ -86,9 +88,23 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'plugin', options.yes, (callback) ->
plugins.remove(params.name, callback)
async.waterfall [
(callback) ->
if options.yes
return callback(null, true)
else
form.ask
message: 'Are you sure you want to delete the plugin?'
type: 'confirm'
default: false
.nodeify(callback)
(confirmed, callback) ->
return callback() if not confirmed
plugins.remove(params.name, callback)
, (error) ->
return done(error) if error?
console.info("Plugin removed: #{params.name}")
return done()
]

View File

@ -3,9 +3,9 @@
\fBresin\fR \- tab completion for resin
.SH DESCRIPTION
.P
It provides basic completion capabilities for \fBzsh\fR and \fBbash\fR\|\.
It provides basic completion capabilities for \fBzsh\fP and \fBbash\fP\|\.
.P
If you're using \fBbash\fR, add the following line to your \fB~/\.bashrc\fR:
If you're using \fBbash\fP, add the following line to your \fB~/\.bashrc\fP:
.P
.RS 2
.nf
@ -29,7 +29,7 @@ ln \- /path/to/resin/completion/resin\.sh /usr/local/etc/bash\-completion\.d/res
.fi
.RE
.P
If you're using \fBzsh\fR, add the following to your \fB~/\.zshrc\fR:
If you're using \fBzsh\fP, add the following to your \fB~/\.zshrc\fP:
.P
.RS 2
.nf
@ -40,9 +40,9 @@ source /path/to/resin/completion/resin\.sh
.RE
.SH RESIN PATH
.P
\fB/path/to/resin\fR refers to the place where resin was globally installed in your system\.
\fB/path/to/resin\fP refers to the place where resin was globally installed in your system\.
.P
This is usually something like \fB/usr/lib/node_modules/resin\fR, or \fBC:\\Users\\AppData\\Roaming\\npm\\node_modules\\resin\fR on Windows\.
This is usually something like \fB/usr/lib/node_modules/resin\fP, or \fBC:\\Users\\AppData\\Roaming\\npm\\node_modules\\resin\fP on Windows\.
.SH COPYRIGHT
.P
resin is Copyright (C) 2014 Resin\.io https://resin\.io

View File

@ -3,18 +3,18 @@
\fBresin-plugins\fR \- Creating Resin CLI plugins
.SH DESCRIPTION
.P
Resin CLI plugins are managed by NPM\. Installing an NPM module that starts with \fBresin\-plugin\-*\fR globally will automatically make it available to the Resin CLI\.
Resin CLI plugins are managed by NPM\. Installing an NPM module that starts with \fBresin\-plugin\-*\fP globally will automatically make it available to the Resin CLI\.
.SH TUTORIAL
.P
In this guide, we'll create a simple hello plugin that greets the user\.
.P
Create a directory called \fBresin\-plugin\-hello\fR, containing a single \fBindex\.js\fR file\.
Create a directory called \fBresin\-plugin\-hello\fP, containing a single \fBindex\.js\fP file\.
.P
Within the new project, run \fBnpm init\fR and make sure the package name is set to \fBresin\-plugin\-hello\fR as well\.
Within the new project, run \fBnpm init\fP and make sure the package name is set to \fBresin\-plugin\-hello\fP as well\.
.P
Also make sure that you have a \fBmain\fR field in \fBpackage\.json\fR that points to the \fBindex\.js\fR file you created above\.
Also make sure that you have a \fBmain\fP field in \fBpackage\.json\fP that points to the \fBindex\.js\fP file you created above\.
.P
Your \fBpackage\.json\fR should look something like this:
Your \fBpackage\.json\fP should look something like this:
.P
.RS 2
.nf
@ -30,26 +30,26 @@ Your \fBpackage\.json\fR should look something like this:
.P
Your index file should export an object (if exposing a single command) or an array of objects (if exposing multiple commands)\.
.P
Notice that is very important that your \fBpackage\.json\fR \fBmain\fR field points to the file that is exporting the commands for the plugin to work correctly\.
Notice that is very important that your \fBpackage\.json\fP \fBmain\fP field points to the file that is exporting the commands for the plugin to work correctly\.
.P
Each object describes a single command\. The accepted fields are:
.RS 0
.IP \(bu 2
\fBsignature\fR: A Capitano \fIhttps://github\.com/resin\-io/capitano\fR signature\.
\fBsignature\fP: A Capitano \fIhttps://github\.com/resin\-io/capitano\fR signature\.
.IP \(bu 2
\fBdescription\fR: A string containing a short description of the command\. This will be shown on the Resin general help\.
\fBdescription\fP: A string containing a short description of the command\. This will be shown on the Resin general help\.
.IP \(bu 2
\fBhelp\fR: A string containing an usage help page\. This will be shown when passing the signature to the \fBhelp\fR command\.
\fBhelp\fP: A string containing an usage help page\. This will be shown when passing the signature to the \fBhelp\fP command\.
.IP \(bu 2
\fBaction\fR: A function that defines the action to take when the command is matched\. The function will be given 3 arguments (\fBparams\fR, \fBoptions\fR, \fBdone\fR)\.
\fBaction\fP: A function that defines the action to take when the command is matched\. The function will be given 3 arguments (\fBparams\fP, \fBoptions\fP, \fBdone\fP)\.
.IP \(bu 2
\fBpermission\fR: A string describing the required permissions to run the command\.
\fBpermission\fP: A string describing the required permissions to run the command\.
.IP \(bu 2
\fBoptions\fR: An array of Capitano \fIhttps://github\.com/resin\-io/capitano\fR options\.
\fBoptions\fP: An array of Capitano \fIhttps://github\.com/resin\-io/capitano\fR options\.
.RE
.P
The \fBindex\.js\fR file should look something like:
The \fBindex\.js\fP file should look something like:
.P
.RS 2
.nf
@ -83,7 +83,7 @@ module\.exports = {
.fi
.RE
.P
This example will register a \fBhello\fR command which requires a \fBname\fR parameter, and greets the user in result\.
This example will register a \fBhello\fP command which requires a \fBname\fP parameter, and greets the user in result\.
.P
To test the plugin, first create a global link by running the following command inside your plugin directory:
.P
@ -93,7 +93,7 @@ $ npm link
.fi
.RE
.P
Now if you run \fB$ resin help\fR you should see your new command at the bottom of the list\.
Now if you run \fB$ resin help\fP you should see your new command at the bottom of the list\.
.P
Try it out:
.P
@ -105,9 +105,9 @@ Hey there Juan!
.RE
.SH DONE CALLBACK
.P
It's very important that you call the \fBdone()\fR callback after your action finishes\. If you pass an \fBError\fR instance to \fBdone()\fR, its message will be displayed by the Resin CLI, exiting with an error code 1\.
It's very important that you call the \fBdone()\fP callback after your action finishes\. If you pass an \fBError\fP instance to \fBdone()\fP, its message will be displayed by the Resin CLI, exiting with an error code 1\.
.P
If your action is synchronous and doesn't return any error, you can omit the \fBdone()\fR callback all together\. For example:
If your action is synchronous and doesn't return any error, you can omit the \fBdone()\fP callback all together\. For example:
.P
.RS 2
.nf
@ -123,9 +123,9 @@ module\.exports = {
.RE
.SH PERMISSIONS
.P
You can set a command permission to restrict access to the commands\. Currently, the only registered permission is \fBuser\fR, which requires the user to log in to Resin from the CLI\.
You can set a command permission to restrict access to the commands\. Currently, the only registered permission is \fBuser\fP, which requires the user to log in to Resin from the CLI\.
.P
To require the user to login before calling our hello plugin, we can add \fBpermission: 'user'\fR to the command description:
To require the user to login before calling our hello plugin, we can add \fBpermission: 'user'\fP to the command description:
.P
.RS 2
.nf
@ -155,7 +155,7 @@ $ resin hello Juan \-\-language spanish
.fi
.RE
.P
We first need to register the \fBlanguage option\fR:
We first need to register the \fBlanguage option\fP:
.P
.RS 2
.nf
@ -184,10 +184,10 @@ module\.exports = {
.fi
.RE
.P
Here, we declared an option with a signature of \fBlanguage\fR (so we can use it as \fB\-\-language\fR), a parameter name of \fBlanguage\fR as well (this means we'll be able to access the option as the \fBlanguage\fR key: \fBoptions\.language\fR), a nice description and an alias \fBl\fR (which means we can use \fB\-l <language>\fR too)\.
Here, we declared an option with a signature of \fBlanguage\fP (so we can use it as \fB\-\-language\fP), a parameter name of \fBlanguage\fP as well (this means we'll be able to access the option as the \fBlanguage\fP key: \fBoptions\.language\fP), a nice description and an alias \fBl\fP (which means we can use \fB\-l <language>\fP too)\.
.SH COFFEESCRIPT
.P
We have CoffeeScript support out of the box\. Implement your commands in \fBindex\.coffee\fR and point \fBpackage\.json\fR \fBmain\fR to that file\.
We have CoffeeScript support out of the box\. Implement your commands in \fBindex\.coffee\fP and point \fBpackage\.json\fP \fBmain\fP to that file\.
.SH RESIN\-SDK
.P
You can use the Resin SDK NodeJS module within your own plugins to communicate with Resin\.

View File

@ -3,7 +3,7 @@
\fBresin\fR \- command line tool to interact with resin\.io
.SH SYNOPSIS
.P
\fBresin\fR [options] <command>
\fBresin\fP [options] <command>
.SH DESCRIPTION
.P
\fBresin\fR is a powerful command line application to interact, develop and deploy resin\.io applications and devices\.

View File

@ -59,7 +59,8 @@
"nplugm": "^2.2.0",
"npm": "^2.13.0",
"open": "0.0.5",
"resin-cli-visuals": "^0.3.3",
"resin-cli-form": "^1.1.0",
"resin-cli-visuals": "^1.0.0",
"resin-config-inject": "^2.0.0",
"resin-device-config": "^1.0.0",
"resin-image": "^1.1.3",