mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-24 07:46:39 +00:00
Fix privilege elevation (sudo) for 'os initialize', 'join', 'leave'
* sudo shell arguments required escaping for 'os initialize' * sudo was not working for standalone zip packages (incorrect Node.js path in argv[0]) * Interactive 'join' and 'leave' not working on Windows because 'windosu' does not capture stderr. Change-type: patch Signed-off-by: Paulo Castro <paulo@balena.io>
This commit is contained in:
parent
43ae9b672c
commit
05cb89725e
@ -20,11 +20,10 @@ import chalk from 'chalk';
|
|||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
import os = require('os');
|
import os = require('os');
|
||||||
import visuals = require('resin-cli-visuals');
|
import visuals = require('resin-cli-visuals');
|
||||||
import rindle = require('rindle');
|
import * as ShellEscape from 'shell-escape';
|
||||||
|
|
||||||
import { InitializeEmitter, OperationState } from 'balena-device-init';
|
import { InitializeEmitter, OperationState } from 'balena-device-init';
|
||||||
|
|
||||||
const waitStreamAsync = Bluebird.promisify(rindle.wait);
|
|
||||||
const balena = BalenaSdk.fromSharedOptions();
|
const balena = BalenaSdk.fromSharedOptions();
|
||||||
|
|
||||||
export function getGroupDefaults(group: {
|
export function getGroupDefaults(group: {
|
||||||
@ -59,19 +58,44 @@ export function stateToString(state: OperationState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sudo(
|
/**
|
||||||
|
* Execute a child process with admin / superuser privileges, prompting the user for
|
||||||
|
* elevation as needed, and taking care of shell-escaping arguments in a suitable way
|
||||||
|
* for Windows and Linux/Mac.
|
||||||
|
*
|
||||||
|
* @param command Unescaped array of command and args to be executed. If isCLIcmd is
|
||||||
|
* true, the command should not include the 'node' or 'balena' components, for example:
|
||||||
|
* ['internal', 'osinit', ...]. This function will add argv[0] and argv[1] as needed
|
||||||
|
* (taking process.pkg into account -- CLI standalone zip package), and will also
|
||||||
|
* shell-escape the arguments as needed, taking into account the differences between
|
||||||
|
* bash/sh and the Windows cmd.exe in relation to escape characters.
|
||||||
|
* @param msg Optional message for the user, before the password prompt
|
||||||
|
* @param stderr Optional stream to which stderr should be piped
|
||||||
|
* @param isCLIcmd (default: true) Whether the command array is a balena CLI command
|
||||||
|
* (e.g. ['internal', 'osinit', ...]), in which case process.argv[0] and argv[1] are
|
||||||
|
* added as necessary, depending on whether the CLI is running as a standalone zip
|
||||||
|
* package (with Node built in).
|
||||||
|
*/
|
||||||
|
export async function sudo(
|
||||||
command: string[],
|
command: string[],
|
||||||
{ stderr, msg }: { stderr?: NodeJS.WritableStream; msg?: string } = {},
|
{
|
||||||
|
stderr,
|
||||||
|
msg,
|
||||||
|
isCLIcmd,
|
||||||
|
}: { stderr?: NodeJS.WritableStream; msg?: string; isCLIcmd?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
const { executeWithPrivileges } = require('./sudo');
|
const { executeWithPrivileges } = await import('./sudo');
|
||||||
|
|
||||||
if (os.platform() !== 'win32') {
|
if (os.platform() !== 'win32') {
|
||||||
console.log(
|
console.log(
|
||||||
msg || 'If asked please type your computer password to continue',
|
msg ||
|
||||||
|
'Admin privileges required: you may be asked for your computer password to continue.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (isCLIcmd == null) {
|
||||||
return executeWithPrivileges(command, stderr);
|
isCLIcmd = true;
|
||||||
|
}
|
||||||
|
await executeWithPrivileges(command, stderr, isCLIcmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runCommand(command: string): Bluebird<void> {
|
export function runCommand(command: string): Bluebird<void> {
|
||||||
@ -106,7 +130,7 @@ export async function getOsVersion(
|
|||||||
return init.getImageOsVersion(image, manifest);
|
return init.getImageOsVersion(image, manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function osProgressHandler(step: InitializeEmitter) {
|
export async function osProgressHandler(step: InitializeEmitter) {
|
||||||
step.on('stdout', process.stdout.write.bind(process.stdout));
|
step.on('stdout', process.stdout.write.bind(process.stdout));
|
||||||
step.on('stderr', process.stderr.write.bind(process.stderr));
|
step.on('stderr', process.stderr.write.bind(process.stderr));
|
||||||
|
|
||||||
@ -124,7 +148,10 @@ export function osProgressHandler(step: InitializeEmitter) {
|
|||||||
|
|
||||||
step.on('burn', state => progressBars[state.type].update(state));
|
step.on('burn', state => progressBars[state.type].update(state));
|
||||||
|
|
||||||
return waitStreamAsync(step);
|
await new Promise((resolve, reject) => {
|
||||||
|
step.on('error', reject);
|
||||||
|
step.on('end', resolve);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getArchAndDeviceType(
|
export function getArchAndDeviceType(
|
||||||
@ -272,3 +299,50 @@ export function getManualSortCompareFunction<T, U = T>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shell argument escaping compatible with sh, bash and Windows cmd.exe.
|
||||||
|
* @param arg Arguments to be escaped
|
||||||
|
* @param detectShell Whether to use the SHELL and ComSpec environment
|
||||||
|
* variables to determine the shell type (sh / bash / cmd.exe). This may be
|
||||||
|
* useful to detect MSYS / MSYS2, which use bash on Windows. However, if the
|
||||||
|
* purpose is to use child_process.spawn(..., {shell: true}) and related
|
||||||
|
* functions, set this to false because child_process.spawn() always uses
|
||||||
|
* env.ComSpec (cmd.exe) on Windows, even when running on MSYS / MSYS2.
|
||||||
|
*/
|
||||||
|
export function shellEscape(args: string[], detectShell = false): string[] {
|
||||||
|
let isWindowsCmdExeShell: boolean;
|
||||||
|
if (detectShell) {
|
||||||
|
isWindowsCmdExeShell =
|
||||||
|
// neither bash nor sh (e.g. not MSYS, MSYS2, WSL)
|
||||||
|
process.env.SHELL == null &&
|
||||||
|
// Windows cmd.exe or PowerShell
|
||||||
|
process.env.ComSpec != null &&
|
||||||
|
process.env.ComSpec.endsWith('cmd.exe');
|
||||||
|
} else {
|
||||||
|
isWindowsCmdExeShell = process.platform === 'win32';
|
||||||
|
}
|
||||||
|
if (isWindowsCmdExeShell) {
|
||||||
|
return args.map(v => windowsCmdExeEscapeArg(v));
|
||||||
|
} else {
|
||||||
|
const shellEscapeFunc: typeof ShellEscape = require('shell-escape');
|
||||||
|
return args.map(v => shellEscapeFunc([v]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape a string argument to be passed through the Windows cmd.exe shell.
|
||||||
|
* cmd.exe escaping has some peculiarities, like using the caret character
|
||||||
|
* instead of a backslash for reserved / metacharacters. Reference:
|
||||||
|
* https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||||
|
*/
|
||||||
|
function windowsCmdExeEscapeArg(arg: string): string {
|
||||||
|
// if it is already double quoted, remove the double quotes
|
||||||
|
if (arg.length > 1 && arg.startsWith('"') && arg.endsWith('"')) {
|
||||||
|
arg = arg.slice(1, -1);
|
||||||
|
}
|
||||||
|
// escape cmd.exe metacharacters with the '^' (caret) character
|
||||||
|
arg = arg.replace(/[()%!^<>&|]/g, '^$&');
|
||||||
|
// duplicate internal double quotes, and double quote overall
|
||||||
|
return `"${arg.replace(/["]/g, '""')}"`;
|
||||||
|
}
|
||||||
|
@ -174,7 +174,7 @@ async function getOrSelectLocalDevice(deviceIp?: string): Promise<string> {
|
|||||||
stream.pipe(process.stderr);
|
stream.pipe(process.stderr);
|
||||||
|
|
||||||
const { sudo } = await import('../utils/helpers');
|
const { sudo } = await import('../utils/helpers');
|
||||||
const command = process.argv.slice(0, 2).concat(['internal', 'scandevices']);
|
const command = ['internal', 'scandevices'];
|
||||||
await sudo(command, {
|
await sudo(command, {
|
||||||
stderr: stream,
|
stderr: stream,
|
||||||
msg:
|
msg:
|
||||||
|
@ -14,34 +14,107 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { spawn, StdioOptions } from 'child_process';
|
|
||||||
|
|
||||||
import * as Bluebird from 'bluebird';
|
import { ChildProcess, spawn, SpawnOptions } from 'child_process';
|
||||||
import * as rindle from 'rindle';
|
import { stripIndent } from 'common-tags';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a child process with admin / superuser privileges, prompting the user for
|
||||||
|
* elevation as needed, and taking care of shell-escaping arguments in a suitable way
|
||||||
|
* for Windows and Linux/Mac.
|
||||||
|
*
|
||||||
|
* @param command Unescaped array of command and args to be executed. If isCLIcmd is
|
||||||
|
* true, the command should not include the 'node' or 'balena' components, for
|
||||||
|
* example: ['internal', 'osinit', ...]. This function will add argv[0] and argv[1]
|
||||||
|
* as needed (taking process.pkg into account -- CLI standalone zip package), and
|
||||||
|
* will also shell-escape the arguments as needed, taking into account the
|
||||||
|
* differences between bash/sh and the Windows cmd.exe in relation to escape
|
||||||
|
* characters.
|
||||||
|
* @param stderr Optional stream to which stderr should be piped
|
||||||
|
* @param isCLIcmd (default: true) Whether the command array is a balena CLI command
|
||||||
|
* (e.g. ['internal', 'osinit', ...]), in which case process.argv[0] and argv[1] are
|
||||||
|
* added as necessary, depending on whether the CLI is running as a standalone zip
|
||||||
|
* package (with Node built in).
|
||||||
|
*/
|
||||||
export async function executeWithPrivileges(
|
export async function executeWithPrivileges(
|
||||||
command: string[],
|
command: string[],
|
||||||
stderr?: NodeJS.WritableStream,
|
stderr?: NodeJS.WritableStream,
|
||||||
): Promise<string> {
|
isCLIcmd = true,
|
||||||
const stdio: StdioOptions = [
|
): Promise<void> {
|
||||||
'inherit',
|
// whether the CLI is already running with admin / super user privileges
|
||||||
'inherit',
|
const isElevated = await (await import('is-elevated'))();
|
||||||
stderr ? 'pipe' : 'inherit',
|
const { shellEscape } = await import('./helpers');
|
||||||
];
|
const opts: SpawnOptions = {
|
||||||
const opts = {
|
|
||||||
env: process.env,
|
env: process.env,
|
||||||
stdio,
|
stdio: ['inherit', 'inherit', stderr ? 'pipe' : 'inherit'],
|
||||||
};
|
};
|
||||||
|
if (isElevated) {
|
||||||
|
if (isCLIcmd) {
|
||||||
|
// opts.shell is false, so preserve pkg's '/snapshot' at argv[1]
|
||||||
|
command = [process.argv[0], process.argv[1], ...command];
|
||||||
|
}
|
||||||
|
// already running with privileges: simply spawn the command
|
||||||
|
await spawnAndPipe(command[0], command.slice(1), opts, stderr);
|
||||||
|
} else {
|
||||||
|
if (isCLIcmd) {
|
||||||
|
// In the case of a CLI standalone zip package (process.pkg is truthy),
|
||||||
|
// the Node executable is bundled with the source code and node_modules
|
||||||
|
// folder in a single file named in argv[0]. In this case, argv[1]
|
||||||
|
// contains a "/snapshot" path that should be discarded when opts.shell
|
||||||
|
// is true.
|
||||||
|
command = (process as any).pkg
|
||||||
|
? [process.argv[0], ...command]
|
||||||
|
: [process.argv[0], process.argv[1], ...command];
|
||||||
|
}
|
||||||
|
opts.shell = true;
|
||||||
|
const escapedCmd = shellEscape(command);
|
||||||
|
// running as ordinary user: elevate privileges
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
await windosuExec(escapedCmd, stderr);
|
||||||
|
} else {
|
||||||
|
await spawnAndPipe('sudo', escapedCmd, opts, stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const args = process.argv
|
async function spawnAndPipe(
|
||||||
.slice(0, 2)
|
spawnCmd: string,
|
||||||
.concat(['internal', 'sudo', command.join(' ')]);
|
spawnArgs: string[],
|
||||||
|
spawnOpts: SpawnOptions,
|
||||||
const ps = spawn(args[0], args.slice(1), opts);
|
stderr?: NodeJS.WritableStream,
|
||||||
|
) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const ps: ChildProcess = spawn(spawnCmd, spawnArgs, spawnOpts);
|
||||||
|
ps.on('error', reject);
|
||||||
|
ps.on('exit', codeOrSignal => {
|
||||||
|
if (codeOrSignal !== 0) {
|
||||||
|
const errMsgCmd = `[${[spawnCmd, ...spawnArgs].join()}]`;
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Child process exited with error code "${codeOrSignal}" for command:\n${errMsgCmd}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
ps.stderr.pipe(stderr);
|
ps.stderr.pipe(stderr);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return Bluebird.fromCallback<string>(callback => rindle.wait(ps, callback));
|
}
|
||||||
|
|
||||||
|
async function windosuExec(
|
||||||
|
escapedArgs: string[],
|
||||||
|
stderr?: NodeJS.WritableStream,
|
||||||
|
): Promise<void> {
|
||||||
|
if (stderr) {
|
||||||
|
const msg = stripIndent`
|
||||||
|
Error: unable to elevate privileges. Please run the command prompt as an Administrator:
|
||||||
|
https://www.howtogeek.com/194041/how-to-open-the-command-prompt-as-administrator-in-windows-8.1/
|
||||||
|
`;
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
return require('windosu').exec(escapedArgs.join(' '));
|
||||||
}
|
}
|
||||||
|
68
npm-shrinkwrap.json
generated
68
npm-shrinkwrap.json
generated
@ -2704,6 +2704,20 @@
|
|||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
||||||
|
},
|
||||||
|
"is-elevated": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-+IThcowajY1ez2I/iHM8bm7h4u4=",
|
||||||
|
"requires": {
|
||||||
|
"is-admin": "^1.0.2",
|
||||||
|
"is-root": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-root": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -7599,12 +7613,22 @@
|
|||||||
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
|
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
|
||||||
},
|
},
|
||||||
"is-elevated": {
|
"is-elevated": {
|
||||||
"version": "1.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-3.0.0.tgz",
|
||||||
"integrity": "sha1-+IThcowajY1ez2I/iHM8bm7h4u4=",
|
"integrity": "sha512-wjcp6RkouU9jpg55zERl+BglvV5j4jx5c/EMvQ+d12j/+nIEenNWPu+qc0tCg3JkLodbKZMg1qhJzEwG4qjclg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-admin": "^1.0.2",
|
"is-admin": "^3.0.0",
|
||||||
"is-root": "^1.0.0"
|
"is-root": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"is-admin": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-admin/-/is-admin-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-wOa3CXFJAu8BZ2BDtG9xYOOrsq6oiSvc2jFPy4X/HINx5bmJUcW8e+apItVbU2E7GIfBVaFVO7Zit4oAWtTJcw==",
|
||||||
|
"requires": {
|
||||||
|
"execa": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-extendable": {
|
"is-extendable": {
|
||||||
@ -7741,9 +7765,9 @@
|
|||||||
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
|
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
|
||||||
},
|
},
|
||||||
"is-root": {
|
"is-root": {
|
||||||
"version": "1.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz",
|
||||||
"integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU="
|
"integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg=="
|
||||||
},
|
},
|
||||||
"is-scoped": {
|
"is-scoped": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -14993,15 +15017,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resin-cli-visuals": {
|
"resin-cli-visuals": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/resin-cli-visuals/-/resin-cli-visuals-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/resin-cli-visuals/-/resin-cli-visuals-1.4.4.tgz",
|
||||||
"integrity": "sha512-vzj6iTdWd0zq1pRXNJZmgMPL6cKS66lLOZlT4lr2kam/ohWFRQNZfmwigZRbrtUOOtBwdVujqSqYAP9USjX9NQ==",
|
"integrity": "sha512-EchgufKC2loXoxB4xjnFtR2bJZbR5xNtJ/pVLc8G2But45TwgkORC5RDbWNe6fCdXJvgWFW1RgZ0Z0tc7Zd+Nw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"chalk": "^2.3.1",
|
"chalk": "^2.3.1",
|
||||||
"cli-spinner": "0.2.7",
|
"cli-spinner": "0.2.7",
|
||||||
"columnify": "^1.5.1",
|
"columnify": "^1.5.1",
|
||||||
"drivelist": "^8.0.6",
|
"drivelist": "^8.0.7",
|
||||||
"inquirer": "^0.11.0",
|
"inquirer": "^0.11.0",
|
||||||
"inquirer-dynamic-list": "^1.0.0",
|
"inquirer-dynamic-list": "^1.0.0",
|
||||||
"is-promise": "^2.1.0",
|
"is-promise": "^2.1.0",
|
||||||
@ -15041,14 +15065,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"drivelist": {
|
"drivelist": {
|
||||||
"version": "8.0.6",
|
"version": "8.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/drivelist/-/drivelist-8.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/drivelist/-/drivelist-8.0.7.tgz",
|
||||||
"integrity": "sha512-KkXaGslqZP7Y13rofyFZBUl9dQrs0wqT5aXq5PAr67uPE2IDXEUYH+LNGTv69xpd7cUImjQvvB9H4bkziLIs1w==",
|
"integrity": "sha512-KgFVzXux+rdRjzt1b1Vv4btqCmB/XC0wOfodtUev9MkJcL8VyxjTsopt+lE3GlSv4M+XPgS9dqf9fBowJ8I/2w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bindings": "^1.3.0",
|
"bindings": "^1.3.0",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"mz": "^2.7.0",
|
"mz": "^2.7.0",
|
||||||
"nan": "^2.10.0",
|
"nan": "^2.14.0",
|
||||||
"prebuild-install": "^5.2.4"
|
"prebuild-install": "^5.2.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -15099,6 +15123,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
|
||||||
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
|
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
|
||||||
},
|
},
|
||||||
|
"nan": {
|
||||||
|
"version": "2.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||||
|
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
|
||||||
|
},
|
||||||
"run-async": {
|
"run-async": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
|
||||||
@ -17839,13 +17868,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"windosu": {
|
"windosu": {
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/windosu/-/windosu-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/windosu/-/windosu-0.3.0.tgz",
|
||||||
"integrity": "sha1-oqGqymWHczDqCxtoXvPACQIKY4k=",
|
"integrity": "sha1-Bhx/5KgRikrZ9P420/kA0ivApHk=",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cli-width": "^1.1.0",
|
"cli-width": "^1.1.0",
|
||||||
"q": "~0.9.7",
|
"q": "~0.9.7",
|
||||||
|
"rimraf": "^2.5.2",
|
||||||
"tail": "~0.3.1",
|
"tail": "~0.3.1",
|
||||||
"temp": "~0.6.0"
|
"temp": "~0.6.0"
|
||||||
},
|
},
|
||||||
|
13
package.json
13
package.json
@ -33,7 +33,9 @@
|
|||||||
"build/actions-oclif",
|
"build/actions-oclif",
|
||||||
"build/auth/pages/*.ejs",
|
"build/auth/pages/*.ejs",
|
||||||
"build/hooks",
|
"build/hooks",
|
||||||
"node_modules/resin-discoverable-services/services/**/*"
|
"node_modules/resin-discoverable-services/services/**/*",
|
||||||
|
"node_modules/windosu/*.bat",
|
||||||
|
"node_modules/windosu/*.cmd"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -135,7 +137,6 @@
|
|||||||
"publish-release": "^1.6.0",
|
"publish-release": "^1.6.0",
|
||||||
"resin-lint": "^3.0.1",
|
"resin-lint": "^3.0.1",
|
||||||
"rewire": "^3.0.2",
|
"rewire": "^3.0.2",
|
||||||
"shell-escape": "^0.2.0",
|
|
||||||
"sinon": "^7.4.1",
|
"sinon": "^7.4.1",
|
||||||
"ts-node": "^8.1.0",
|
"ts-node": "^8.1.0",
|
||||||
"typescript": "^3.4.5"
|
"typescript": "^3.4.5"
|
||||||
@ -184,7 +185,8 @@
|
|||||||
"humanize": "0.0.9",
|
"humanize": "0.0.9",
|
||||||
"ignore": "^5.1.1",
|
"ignore": "^5.1.1",
|
||||||
"inquirer": "^3.1.1",
|
"inquirer": "^3.1.1",
|
||||||
"is-root": "^1.0.0",
|
"is-elevated": "^3.0.0",
|
||||||
|
"is-root": "^2.1.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"klaw": "^3.0.0",
|
"klaw": "^3.0.0",
|
||||||
"livepush": "^2.0.1",
|
"livepush": "^2.0.1",
|
||||||
@ -205,7 +207,7 @@
|
|||||||
"reconfix": "^0.1.0",
|
"reconfix": "^0.1.0",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
"resin-cli-form": "^2.0.1",
|
"resin-cli-form": "^2.0.1",
|
||||||
"resin-cli-visuals": "^1.4.0",
|
"resin-cli-visuals": "^1.4.4",
|
||||||
"resin-compose-parse": "^2.1.0",
|
"resin-compose-parse": "^2.1.0",
|
||||||
"resin-doodles": "0.0.1",
|
"resin-doodles": "0.0.1",
|
||||||
"resin-image-fs": "^5.0.8",
|
"resin-image-fs": "^5.0.8",
|
||||||
@ -216,6 +218,7 @@
|
|||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"rindle": "^1.3.4",
|
"rindle": "^1.3.4",
|
||||||
"semver": "^5.7.0",
|
"semver": "^5.7.0",
|
||||||
|
"shell-escape": "^0.2.0",
|
||||||
"split": "^1.0.1",
|
"split": "^1.0.1",
|
||||||
"string-width": "^2.1.1",
|
"string-width": "^2.1.1",
|
||||||
"strip-ansi-stream": "^1.0.0",
|
"strip-ansi-stream": "^1.0.0",
|
||||||
@ -231,6 +234,6 @@
|
|||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"net-keepalive": "^1.2.1",
|
"net-keepalive": "^1.2.1",
|
||||||
"windosu": "^0.2.0"
|
"windosu": "^0.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
typings/balena-device-init/index.d.ts
vendored
2
typings/balena-device-init/index.d.ts
vendored
@ -75,6 +75,8 @@ declare module 'balena-device-init' {
|
|||||||
on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void;
|
on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void;
|
||||||
on(event: 'state', callback: (state: OperationState) => void): void;
|
on(event: 'state', callback: (state: OperationState) => void): void;
|
||||||
on(event: 'burn', callback: (state: BurnProgress) => void): void;
|
on(event: 'burn', callback: (state: BurnProgress) => void): void;
|
||||||
|
on(event: 'end', callback: () => void): void;
|
||||||
|
on(event: 'error', callback: (error: Error) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function configure(
|
export function configure(
|
||||||
|
Loading…
Reference in New Issue
Block a user