chore(xo-cli): use modern ES
This commit is contained in:
parent
37f0bffcaa
commit
50581c539c
22
packages/xo-cli/.babelrc.js
Normal file
22
packages/xo-cli/.babelrc.js
Normal file
@ -0,0 +1,22 @@
|
||||
const { NODE_ENV = 'development' } = process.env
|
||||
|
||||
module.exports = {
|
||||
comments: false,
|
||||
compact: true,
|
||||
ignore: NODE_ENV === 'test' ? undefined : ['*.spec.js'],
|
||||
// plugins: ['lodash']
|
||||
presets: [
|
||||
[
|
||||
'env',
|
||||
{
|
||||
debug: true,
|
||||
loose: true,
|
||||
targets: {
|
||||
node: process.env.NODE_ENV === 'production' ? '6' : 'current'
|
||||
},
|
||||
useBuiltIns: 'usage'
|
||||
}
|
||||
],
|
||||
'flow'
|
||||
]
|
||||
}
|
12
packages/xo-cli/.flowconfig
Normal file
12
packages/xo-cli/.flowconfig
Normal file
@ -0,0 +1,12 @@
|
||||
[ignore]
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
experimental.const_params=true
|
||||
module.use_strict=true
|
||||
unsafe.enable_getters_and_setters=true
|
24
packages/xo-cli/.npmignore
Normal file
24
packages/xo-cli/.npmignore
Normal file
@ -0,0 +1,24 @@
|
||||
/benchmark/
|
||||
/benchmarks/
|
||||
*.bench.js
|
||||
*.bench.js.map
|
||||
|
||||
/examples/
|
||||
example.js
|
||||
example.js.map
|
||||
*.example.js
|
||||
*.example.js.map
|
||||
|
||||
/fixture/
|
||||
/fixtures/
|
||||
*.fixture.js
|
||||
*.fixture.js.map
|
||||
*.fixtures.js
|
||||
*.fixtures.js.map
|
||||
|
||||
/test/
|
||||
/tests/
|
||||
*.spec.js
|
||||
*.spec.js.map
|
||||
|
||||
__snapshots__/
|
8
packages/xo-cli/.travis.yml
Normal file
8
packages/xo-cli/.travis.yml
Normal file
@ -0,0 +1,8 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
@ -5,7 +5,7 @@
|
||||
|
||||
> Basic CLI for Xen-Orchestra
|
||||
|
||||
## Installation
|
||||
## Install
|
||||
|
||||
#### [npm](https://npmjs.org/package/xo-cli)
|
||||
|
||||
@ -117,9 +117,31 @@ encoding by prefixing with `json:`:
|
||||
> xo-cli vm.import host=60a6939e-8b0a-4352-9954-5bde44bcdf7d @=vm.xva
|
||||
```
|
||||
|
||||
## Contributing
|
||||
## Development
|
||||
|
||||
Contributions are *very* welcome, either on the documentation or on
|
||||
```
|
||||
# Install dependencies
|
||||
> yarn
|
||||
|
||||
# Run the tests
|
||||
> yarn test
|
||||
|
||||
# Continuously compile
|
||||
> yarn dev
|
||||
|
||||
# Continuously run the tests
|
||||
> yarn dev-test
|
||||
|
||||
# Build for production (automatically called by npm install)
|
||||
> yarn build
|
||||
|
||||
# Commit changes
|
||||
> yarn cz
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are *very* welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
@ -1,432 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
var Bluebird = require('bluebird')
|
||||
Bluebird.longStackTraces()
|
||||
|
||||
var createReadStream = require('fs').createReadStream
|
||||
var createWriteStream = require('fs').createWriteStream
|
||||
var resolveUrl = require('url').resolve
|
||||
var stat = require('fs-promise').stat
|
||||
|
||||
var chalk = require('chalk')
|
||||
var eventToPromise = require('event-to-promise')
|
||||
var forEach = require('lodash/forEach')
|
||||
var getKeys = require('lodash/keys')
|
||||
var got = require('got')
|
||||
var humanFormat = require('human-format')
|
||||
var identity = require('lodash/identity')
|
||||
var isArray = require('lodash/isArray')
|
||||
var isObject = require('lodash/isObject')
|
||||
var micromatch = require('micromatch')
|
||||
var multiline = require('multiline')
|
||||
var nicePipe = require('nice-pipe')
|
||||
var pairs = require('lodash/toPairs')
|
||||
var pick = require('lodash/pick')
|
||||
var prettyMs = require('pretty-ms')
|
||||
var progressStream = require('progress-stream')
|
||||
var pw = require('pw')
|
||||
var Xo = require('xo-lib').default
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var config = require('./config')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
function connect () {
|
||||
return config.load().bind({}).then(function (config) {
|
||||
if (!config.server) {
|
||||
throw new Error('no server to connect to!')
|
||||
}
|
||||
|
||||
if (!config.token) {
|
||||
throw new Error('no token available')
|
||||
}
|
||||
|
||||
var xo = new Xo({ url: config.server })
|
||||
|
||||
return xo.open().then(function () {
|
||||
return xo.signIn({ token: config.token })
|
||||
}).then(function () {
|
||||
return xo
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function _startsWith (string, search) {
|
||||
return string.lastIndexOf(search, 0) === 0
|
||||
}
|
||||
|
||||
var FLAG_RE = /^--([^=]+)(?:=([^]*))?$/
|
||||
function extractFlags (args) {
|
||||
var flags = {}
|
||||
|
||||
var i = 0
|
||||
var n = args.length
|
||||
var matches
|
||||
while (i < n && (matches = args[i].match(FLAG_RE))) {
|
||||
var value = matches[2]
|
||||
|
||||
flags[matches[1]] = value === undefined ? true : value
|
||||
++i
|
||||
}
|
||||
args.splice(0, i)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
var PARAM_RE = /^([^=]+)=([^]*)$/
|
||||
function parseParameters (args) {
|
||||
var params = {}
|
||||
forEach(args, function (arg) {
|
||||
var matches
|
||||
if (!(matches = arg.match(PARAM_RE))) {
|
||||
throw new Error('invalid arg: ' + arg)
|
||||
}
|
||||
var name = matches[1]
|
||||
var value = matches[2]
|
||||
|
||||
if (_startsWith(value, 'json:')) {
|
||||
value = JSON.parse(value.slice(5))
|
||||
}
|
||||
|
||||
if (name === '@') {
|
||||
params['@'] = value
|
||||
return
|
||||
}
|
||||
|
||||
if (value === 'true') {
|
||||
value = true
|
||||
} else if (value === 'false') {
|
||||
value = false
|
||||
}
|
||||
|
||||
params[name] = value
|
||||
})
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
var humanFormatOpts = {
|
||||
unit: 'B',
|
||||
scale: 'binary'
|
||||
}
|
||||
|
||||
function printProgress (progress) {
|
||||
if (progress.length) {
|
||||
console.warn('%s% of %s @ %s/s - ETA %s',
|
||||
Math.round(progress.percentage),
|
||||
humanFormat(progress.length, humanFormatOpts),
|
||||
humanFormat(progress.speed, humanFormatOpts),
|
||||
prettyMs(progress.eta * 1e3)
|
||||
)
|
||||
} else {
|
||||
console.warn('%s @ %s/s',
|
||||
humanFormat(progress.transferred, humanFormatOpts),
|
||||
humanFormat(progress.speed, humanFormatOpts)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function wrap (val) {
|
||||
return function wrappedValue () {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
var help = wrap((function (pkg) {
|
||||
return multiline.stripIndent(function () { /*
|
||||
Usage:
|
||||
|
||||
$name --register [--expiresIn duration] <XO-Server URL> <username> [<password>]
|
||||
Registers the XO instance to use.
|
||||
|
||||
--expiresIn duration
|
||||
Can be used to change the validity duration of the
|
||||
authorization token (default: one month).
|
||||
|
||||
$name --unregister
|
||||
Remove stored credentials.
|
||||
|
||||
$name --list-commands [--json] [<pattern>]...
|
||||
Returns the list of available commands on the current XO instance.
|
||||
|
||||
The patterns can be used to filter on command names.
|
||||
|
||||
$name --list-objects [--<property>]… [<property>=<value>]...
|
||||
Returns a list of XO objects.
|
||||
|
||||
--<property>
|
||||
Restricts displayed properties to those listed.
|
||||
|
||||
<property>=<value>
|
||||
Restricted displayed objects to those matching the patterns.
|
||||
|
||||
$name <command> [<name>=<value>]...
|
||||
Executes a command on the current XO instance.
|
||||
|
||||
$name v$version
|
||||
*/ }).replace(/<([^>]+)>|\$(\w+)/g, function (_, arg, key) {
|
||||
if (arg) {
|
||||
return '<' + chalk.yellow(arg) + '>'
|
||||
}
|
||||
|
||||
if (key === 'name') {
|
||||
return chalk.bold(pkg[key])
|
||||
}
|
||||
|
||||
return pkg[key]
|
||||
})
|
||||
})(require('./package')))
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function main (args) {
|
||||
if (!args || !args.length || args[0] === '-h') {
|
||||
return help()
|
||||
}
|
||||
|
||||
var fnName = args[0].replace(/^--|-\w/g, function (match) {
|
||||
if (match === '--') {
|
||||
return ''
|
||||
}
|
||||
|
||||
return match[1].toUpperCase()
|
||||
})
|
||||
if (fnName in exports) {
|
||||
return exports[fnName](args.slice(1))
|
||||
}
|
||||
|
||||
return exports.call(args)
|
||||
}
|
||||
exports = module.exports = main
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
exports.help = help
|
||||
|
||||
function register (args) {
|
||||
var i = 0
|
||||
var expiresIn
|
||||
if (args[i] === '--expiresIn') {
|
||||
expiresIn = args[i + 1]
|
||||
i += 2
|
||||
}
|
||||
|
||||
var url = args[i++]
|
||||
var email = args[i++]
|
||||
var password = args[i++]
|
||||
|
||||
var xo
|
||||
return Promise.resolve(password || new Promise(function (resolve) {
|
||||
process.stdout.write('Password: ')
|
||||
pw(resolve)
|
||||
})).then(function (password_) {
|
||||
password = password_
|
||||
|
||||
xo = new Xo({ url })
|
||||
return xo.open()
|
||||
}).then(function () {
|
||||
return xo.signIn({
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
}).then(function () {
|
||||
console.log('Successfully logged with', xo.user.email)
|
||||
|
||||
return xo.call('token.create', { expiresIn: expiresIn })
|
||||
}).then(function (token) {
|
||||
return config.set({
|
||||
server: url,
|
||||
token: token
|
||||
})
|
||||
})
|
||||
}
|
||||
exports.register = register
|
||||
|
||||
function unregister () {
|
||||
return config.unset([
|
||||
'server',
|
||||
'token'
|
||||
])
|
||||
}
|
||||
exports.unregister = unregister
|
||||
|
||||
function listCommands (args) {
|
||||
return connect().then(function getMethodsInfo (xo) {
|
||||
return xo.call('system.getMethodsInfo')
|
||||
}).then(function formatMethodsInfo (methods) {
|
||||
var json = false
|
||||
var patterns = []
|
||||
forEach(args, function (arg) {
|
||||
if (arg === '--json') {
|
||||
json = true
|
||||
} else {
|
||||
patterns.push(arg)
|
||||
}
|
||||
})
|
||||
|
||||
if (patterns.length) {
|
||||
methods = pick(methods, micromatch(Object.keys(methods), patterns))
|
||||
}
|
||||
|
||||
if (json) {
|
||||
return methods
|
||||
}
|
||||
|
||||
methods = pairs(methods)
|
||||
methods.sort(function (a, b) {
|
||||
a = a[0]
|
||||
b = b[0]
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
return +(a > b)
|
||||
})
|
||||
|
||||
var str = []
|
||||
forEach(methods, function (method) {
|
||||
var name = method[0]
|
||||
var info = method[1]
|
||||
str.push(chalk.bold.blue(name))
|
||||
forEach(info.params || [], function (info, name) {
|
||||
str.push(' ')
|
||||
if (info.optional) {
|
||||
str.push('[')
|
||||
}
|
||||
|
||||
var type = info.type
|
||||
str.push(
|
||||
name,
|
||||
'=<',
|
||||
type == null
|
||||
? 'unknown type'
|
||||
: isArray(type)
|
||||
? type.join('|')
|
||||
: type,
|
||||
'>'
|
||||
)
|
||||
|
||||
if (info.optional) {
|
||||
str.push(']')
|
||||
}
|
||||
})
|
||||
str.push('\n')
|
||||
if (info.description) {
|
||||
str.push(' ', info.description, '\n')
|
||||
}
|
||||
})
|
||||
return str.join('')
|
||||
})
|
||||
}
|
||||
exports.listCommands = listCommands
|
||||
|
||||
function listObjects (args) {
|
||||
var properties = getKeys(extractFlags(args))
|
||||
var filterProperties = properties.length
|
||||
? function (object) {
|
||||
return pick(object, properties)
|
||||
}
|
||||
: identity
|
||||
|
||||
var sieve = args.length ? parseParameters(args) : undefined
|
||||
|
||||
return connect().then(function getXoObjects (xo) {
|
||||
return xo.call('xo.getAllObjects', { filter: sieve })
|
||||
}).then(function filterObjects (objects) {
|
||||
const stdout = process.stdout
|
||||
stdout.write('[\n')
|
||||
const keys = Object.keys(objects)
|
||||
for (var i = 0, n = keys.length; i < n;) {
|
||||
stdout.write(JSON.stringify(filterProperties(objects[keys[i]]), null, 2))
|
||||
stdout.write(++i < n ? ',\n' : '\n')
|
||||
}
|
||||
stdout.write(']\n')
|
||||
})
|
||||
}
|
||||
exports.listObjects = listObjects
|
||||
|
||||
function call (args) {
|
||||
if (!args.length) {
|
||||
throw new Error('missing command name')
|
||||
}
|
||||
|
||||
var method = args.shift()
|
||||
var params = parseParameters(args)
|
||||
|
||||
var file = params['@']
|
||||
delete params['@']
|
||||
|
||||
var baseUrl
|
||||
return connect().then(function (xo) {
|
||||
// FIXME: do not use private properties.
|
||||
baseUrl = xo._url.replace(/^ws/, 'http')
|
||||
|
||||
return xo.call(method, params)
|
||||
}).then(function handleResult (result) {
|
||||
var keys, key, url
|
||||
if (
|
||||
isObject(result) &&
|
||||
(keys = getKeys(result)).length === 1
|
||||
) {
|
||||
key = keys[0]
|
||||
|
||||
if (key === '$getFrom') {
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
var output = createWriteStream(file)
|
||||
|
||||
var progress = progressStream({ time: 1e3 }, printProgress)
|
||||
|
||||
return eventToPromise(nicePipe([
|
||||
got.stream(url).on('response', function (response) {
|
||||
var length = response.headers['content-length']
|
||||
if (length) {
|
||||
progress.length(length)
|
||||
}
|
||||
}),
|
||||
progress,
|
||||
output
|
||||
]), 'finish')
|
||||
}
|
||||
|
||||
if (key === '$sendTo') {
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
|
||||
return stat(file).then(function (stats) {
|
||||
var length = stats.size
|
||||
|
||||
var input = nicePipe([
|
||||
createReadStream(file),
|
||||
progressStream({
|
||||
length: length,
|
||||
time: 1e3
|
||||
}, printProgress)
|
||||
])
|
||||
|
||||
return got.post(url, {
|
||||
body: input,
|
||||
headers: {
|
||||
'content-length': length
|
||||
},
|
||||
method: 'POST'
|
||||
}).then(function (response) {
|
||||
return response.body
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
exports.call = call
|
||||
|
||||
// ===================================================================
|
||||
|
||||
if (!module.parent) {
|
||||
require('exec-promise')(exports)
|
||||
}
|
@ -11,21 +11,26 @@
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-cli",
|
||||
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||
"author": "Julien Fontanet <julien.fontanet@vates.fr>",
|
||||
"preferGlobal": true,
|
||||
"bin": {
|
||||
"xo-cli": "index.js"
|
||||
},
|
||||
"files": [
|
||||
"*.js"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": "Julien Fontanet <julien.fontanet@vates.fr>",
|
||||
"preferGlobal": true,
|
||||
"main": "dist/",
|
||||
"bin": {
|
||||
"xo-cli": "dist/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^7.0.0-beta.3",
|
||||
"bluebird": "^3.5.1",
|
||||
"chalk": "^2.1.0",
|
||||
"chalk": "^2.2.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"fs-promise": "^2.0.3",
|
||||
@ -33,25 +38,60 @@
|
||||
"human-format": "^0.8.0",
|
||||
"l33teral": "^3.0.3",
|
||||
"lodash": "^4.17.4",
|
||||
"micromatch": "^3.1.0",
|
||||
"micromatch": "^3.1.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"multiline": "^1.0.2",
|
||||
"nice-pipe": "0.0.0",
|
||||
"pretty-ms": "^3.0.1",
|
||||
"progress-stream": "^2.0.0",
|
||||
"pw": "^0.0.4",
|
||||
"strip-indent": "^2.0.0",
|
||||
"xdg-basedir": "^3.0.0",
|
||||
"xo-lib": "^0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^7.0.0-beta.3",
|
||||
"babel-eslint": "^8.0.1",
|
||||
"babel-plugin-lodash": "^3.2.11",
|
||||
"babel-preset-env": "^7.0.0-beta.3",
|
||||
"babel-preset-flow": "^7.0.0-beta.3",
|
||||
"commitizen": "^2.9.6",
|
||||
"cross-env": "^5.1.0",
|
||||
"cz-conventional-changelog": "^2.0.0",
|
||||
"dependency-check": "^2.9.1",
|
||||
"flow-bin": "^0.57.3",
|
||||
"jest": "^21.2.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"standard": "^10.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "standard"
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"cz": "git-cz",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"dev-test": "jest --bail --watch",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build",
|
||||
"pretest": "standard --fix && flow status",
|
||||
"test": "jest"
|
||||
},
|
||||
"greenkeeper": {
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node",
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
],
|
||||
"testRegex": "\\.spec\\.js$"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"nice-pipe"
|
||||
]
|
||||
"dist"
|
||||
],
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
}
|
||||
|
407
packages/xo-cli/src/index.js
Executable file
407
packages/xo-cli/src/index.js
Executable file
@ -0,0 +1,407 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
const Bluebird = require('bluebird')
|
||||
Bluebird.longStackTraces()
|
||||
|
||||
const createReadStream = require('fs').createReadStream
|
||||
const createWriteStream = require('fs').createWriteStream
|
||||
const resolveUrl = require('url').resolve
|
||||
const stat = require('fs-promise').stat
|
||||
|
||||
const chalk = require('chalk')
|
||||
const eventToPromise = require('event-to-promise')
|
||||
const forEach = require('lodash/forEach')
|
||||
const getKeys = require('lodash/keys')
|
||||
const got = require('got')
|
||||
const humanFormat = require('human-format')
|
||||
const identity = require('lodash/identity')
|
||||
const isArray = require('lodash/isArray')
|
||||
const isObject = require('lodash/isObject')
|
||||
const micromatch = require('micromatch')
|
||||
const nicePipe = require('nice-pipe')
|
||||
const pairs = require('lodash/toPairs')
|
||||
const pick = require('lodash/pick')
|
||||
const startsWith = require('lodash/startsWith')
|
||||
const prettyMs = require('pretty-ms')
|
||||
const progressStream = require('progress-stream')
|
||||
const pw = require('pw')
|
||||
const Xo = require('xo-lib').default
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const config = require('./config')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
async function connect () {
|
||||
const { server, token } = await config.load()
|
||||
if (server === undefined) {
|
||||
throw new Error('no server to connect to!')
|
||||
}
|
||||
|
||||
if (token === undefined) {
|
||||
throw new Error('no token available')
|
||||
}
|
||||
|
||||
const xo = new Xo({ url: server })
|
||||
await xo.open()
|
||||
await xo.signIn({ token })
|
||||
return xo
|
||||
}
|
||||
|
||||
const FLAG_RE = /^--([^=]+)(?:=([^]*))?$/
|
||||
function extractFlags (args) {
|
||||
const flags = {}
|
||||
|
||||
let i = 0
|
||||
const n = args.length
|
||||
let matches
|
||||
while (i < n && (matches = args[i].match(FLAG_RE))) {
|
||||
const value = matches[2]
|
||||
|
||||
flags[matches[1]] = value === undefined ? true : value
|
||||
++i
|
||||
}
|
||||
args.splice(0, i)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
const PARAM_RE = /^([^=]+)=([^]*)$/
|
||||
function parseParameters (args) {
|
||||
const params = {}
|
||||
forEach(args, function (arg) {
|
||||
let matches
|
||||
if (!(matches = arg.match(PARAM_RE))) {
|
||||
throw new Error('invalid arg: ' + arg)
|
||||
}
|
||||
const name = matches[1]
|
||||
let value = matches[2]
|
||||
|
||||
if (startsWith(value, 'json:')) {
|
||||
value = JSON.parse(value.slice(5))
|
||||
}
|
||||
|
||||
if (name === '@') {
|
||||
params['@'] = value
|
||||
return
|
||||
}
|
||||
|
||||
if (value === 'true') {
|
||||
value = true
|
||||
} else if (value === 'false') {
|
||||
value = false
|
||||
}
|
||||
|
||||
params[name] = value
|
||||
})
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
const humanFormatOpts = {
|
||||
unit: 'B',
|
||||
scale: 'binary'
|
||||
}
|
||||
|
||||
function printProgress (progress) {
|
||||
if (progress.length) {
|
||||
console.warn('%s% of %s @ %s/s - ETA %s',
|
||||
Math.round(progress.percentage),
|
||||
humanFormat(progress.length, humanFormatOpts),
|
||||
humanFormat(progress.speed, humanFormatOpts),
|
||||
prettyMs(progress.eta * 1e3)
|
||||
)
|
||||
} else {
|
||||
console.warn('%s @ %s/s',
|
||||
humanFormat(progress.transferred, humanFormatOpts),
|
||||
humanFormat(progress.speed, humanFormatOpts)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function wrap (val) {
|
||||
return function wrappedValue () {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const help = wrap((function (pkg) {
|
||||
return require('strip-indent')(`
|
||||
Usage:
|
||||
|
||||
$name --register [--expiresIn duration] <XO-Server URL> <username> [<password>]
|
||||
Registers the XO instance to use.
|
||||
|
||||
--expiresIn duration
|
||||
Can be used to change the validity duration of the
|
||||
authorization token (default: one month).
|
||||
|
||||
$name --unregister
|
||||
Remove stored credentials.
|
||||
|
||||
$name --list-commands [--json] [<pattern>]...
|
||||
Returns the list of available commands on the current XO instance.
|
||||
|
||||
The patterns can be used to filter on command names.
|
||||
|
||||
$name --list-objects [--<property>]… [<property>=<value>]...
|
||||
Returns a list of XO objects.
|
||||
|
||||
--<property>
|
||||
Restricts displayed properties to those listed.
|
||||
|
||||
<property>=<value>
|
||||
Restricted displayed objects to those matching the patterns.
|
||||
|
||||
$name <command> [<name>=<value>]...
|
||||
Executes a command on the current XO instance.
|
||||
|
||||
$name v$version
|
||||
`).replace(/<([^>]+)>|\$(\w+)/g, function (_, arg, key) {
|
||||
if (arg) {
|
||||
return '<' + chalk.yellow(arg) + '>'
|
||||
}
|
||||
|
||||
if (key === 'name') {
|
||||
return chalk.bold(pkg[key])
|
||||
}
|
||||
|
||||
return pkg[key]
|
||||
})
|
||||
})(require('../package')))
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function main (args) {
|
||||
if (!args || !args.length || args[0] === '-h') {
|
||||
return help()
|
||||
}
|
||||
|
||||
const fnName = args[0].replace(/^--|-\w/g, function (match) {
|
||||
if (match === '--') {
|
||||
return ''
|
||||
}
|
||||
|
||||
return match[1].toUpperCase()
|
||||
})
|
||||
if (fnName in exports) {
|
||||
return exports[fnName](args.slice(1))
|
||||
}
|
||||
|
||||
return exports.call(args)
|
||||
}
|
||||
exports = module.exports = main
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
exports.help = help
|
||||
|
||||
async function register (args) {
|
||||
let expiresIn
|
||||
if (args[0] === '--expiresIn') {
|
||||
expiresIn = args[1]
|
||||
args = args.slice(2)
|
||||
}
|
||||
|
||||
const [
|
||||
url,
|
||||
email,
|
||||
password = await new Promise(function (resolve) {
|
||||
process.stdout.write('Password: ')
|
||||
pw(resolve)
|
||||
})
|
||||
] = args
|
||||
|
||||
const xo = new Xo({ url })
|
||||
await xo.open()
|
||||
await xo.signIn({ email, password })
|
||||
console.log('Successfully logged with', xo.user.email)
|
||||
|
||||
await config.set({
|
||||
server: url,
|
||||
token: await xo.call('token.create', { expiresIn })
|
||||
})
|
||||
}
|
||||
exports.register = register
|
||||
|
||||
function unregister () {
|
||||
return config.unset([
|
||||
'server',
|
||||
'token'
|
||||
])
|
||||
}
|
||||
exports.unregister = unregister
|
||||
|
||||
async function listCommands (args) {
|
||||
const xo = await connect()
|
||||
let methods = await xo.call('system.getMethodsInfo')
|
||||
|
||||
let json = false
|
||||
const patterns = []
|
||||
forEach(args, function (arg) {
|
||||
if (arg === '--json') {
|
||||
json = true
|
||||
} else {
|
||||
patterns.push(arg)
|
||||
}
|
||||
})
|
||||
|
||||
if (patterns.length) {
|
||||
methods = pick(methods, micromatch(Object.keys(methods), patterns))
|
||||
}
|
||||
|
||||
if (json) {
|
||||
return methods
|
||||
}
|
||||
|
||||
methods = pairs(methods)
|
||||
methods.sort(function (a, b) {
|
||||
a = a[0]
|
||||
b = b[0]
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
return +(a > b)
|
||||
})
|
||||
|
||||
const str = []
|
||||
forEach(methods, function (method) {
|
||||
const name = method[0]
|
||||
const info = method[1]
|
||||
str.push(chalk.bold.blue(name))
|
||||
forEach(info.params || [], function (info, name) {
|
||||
str.push(' ')
|
||||
if (info.optional) {
|
||||
str.push('[')
|
||||
}
|
||||
|
||||
const type = info.type
|
||||
str.push(
|
||||
name,
|
||||
'=<',
|
||||
type == null
|
||||
? 'unknown type'
|
||||
: isArray(type)
|
||||
? type.join('|')
|
||||
: type,
|
||||
'>'
|
||||
)
|
||||
|
||||
if (info.optional) {
|
||||
str.push(']')
|
||||
}
|
||||
})
|
||||
str.push('\n')
|
||||
if (info.description) {
|
||||
str.push(' ', info.description, '\n')
|
||||
}
|
||||
})
|
||||
return str.join('')
|
||||
}
|
||||
exports.listCommands = listCommands
|
||||
|
||||
async function listObjects (args) {
|
||||
const properties = getKeys(extractFlags(args))
|
||||
const filterProperties = properties.length
|
||||
? function (object) {
|
||||
return pick(object, properties)
|
||||
}
|
||||
: identity
|
||||
|
||||
const filter = args.length ? parseParameters(args) : undefined
|
||||
|
||||
const xo = await connect()
|
||||
const objects = await xo.call('xo.getAllObjects', { filter })
|
||||
|
||||
const stdout = process.stdout
|
||||
stdout.write('[\n')
|
||||
const keys = Object.keys(objects)
|
||||
for (let i = 0, n = keys.length; i < n;) {
|
||||
stdout.write(JSON.stringify(filterProperties(objects[keys[i]]), null, 2))
|
||||
stdout.write(++i < n ? ',\n' : '\n')
|
||||
}
|
||||
stdout.write(']\n')
|
||||
}
|
||||
exports.listObjects = listObjects
|
||||
|
||||
async function call (args) {
|
||||
if (!args.length) {
|
||||
throw new Error('missing command name')
|
||||
}
|
||||
|
||||
const method = args.shift()
|
||||
const params = parseParameters(args)
|
||||
|
||||
const file = params['@']
|
||||
delete params['@']
|
||||
|
||||
const xo = await connect()
|
||||
|
||||
// FIXME: do not use private properties.
|
||||
const baseUrl = xo._url.replace(/^ws/, 'http')
|
||||
|
||||
const result = await xo.call(method, params)
|
||||
let keys, key, url
|
||||
if (
|
||||
isObject(result) &&
|
||||
(keys = getKeys(result)).length === 1
|
||||
) {
|
||||
key = keys[0]
|
||||
|
||||
if (key === '$getFrom') {
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
const output = createWriteStream(file)
|
||||
|
||||
const progress = progressStream({ time: 1e3 }, printProgress)
|
||||
|
||||
return eventToPromise(nicePipe([
|
||||
got.stream(url).on('response', function (response) {
|
||||
const length = response.headers['content-length']
|
||||
if (length !== undefined) {
|
||||
progress.length(length)
|
||||
}
|
||||
}),
|
||||
progress,
|
||||
output
|
||||
]), 'finish')
|
||||
}
|
||||
|
||||
if (key === '$sendTo') {
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
|
||||
const stats = await stat(file)
|
||||
const length = stats.size
|
||||
|
||||
const input = nicePipe([
|
||||
createReadStream(file),
|
||||
progressStream({
|
||||
length: length,
|
||||
time: 1e3
|
||||
}, printProgress)
|
||||
])
|
||||
|
||||
const response = await got.post(url, {
|
||||
body: input,
|
||||
headers: {
|
||||
'content-length': length
|
||||
},
|
||||
method: 'POST'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
exports.call = call
|
||||
|
||||
// ===================================================================
|
||||
|
||||
if (!module.parent) {
|
||||
require('exec-promise')(exports)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user