codify-plugin-test 0.0.51 → 0.0.53-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugin-process.d.ts +1 -1
- package/dist/plugin-process.js +40 -38
- package/dist/plugin-process.js.map +1 -1
- package/dist/shell.d.ts +12 -0
- package/dist/shell.js +37 -0
- package/dist/shell.js.map +1 -0
- package/dist/spawn.d.ts +14 -0
- package/dist/spawn.js +58 -0
- package/dist/spawn.js.map +1 -0
- package/package.json +6 -4
- package/src/plugin-process.ts +52 -67
- package/src/shell.ts +45 -0
- package/src/spawn.ts +91 -0
package/dist/plugin-process.d.ts
CHANGED
package/dist/plugin-process.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
|
-
import { IpcMessageSchema, MessageCmd
|
|
2
|
+
import { CommandRequestDataSchema, IpcMessageSchema, MessageCmd } from 'codify-schemas';
|
|
3
3
|
import { nanoid } from 'nanoid';
|
|
4
|
-
import { fork
|
|
4
|
+
import { fork } from 'node:child_process';
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import * as os from 'node:os';
|
|
5
7
|
import path from 'node:path';
|
|
8
|
+
import { spawnSafe } from './spawn.js';
|
|
6
9
|
import { CodifyTestUtils } from './test-utils.js';
|
|
7
10
|
const ajv = new Ajv.default({
|
|
8
11
|
strict: true
|
|
9
12
|
});
|
|
10
13
|
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
11
|
-
const
|
|
14
|
+
const commandRequestValidator = ajv.compile(CommandRequestDataSchema);
|
|
12
15
|
export class PluginProcess {
|
|
13
16
|
childProcess;
|
|
14
17
|
constructor(pluginPath) {
|
|
@@ -23,7 +26,7 @@ export class PluginProcess {
|
|
|
23
26
|
});
|
|
24
27
|
this.childProcess.stderr?.pipe(process.stderr);
|
|
25
28
|
this.childProcess.stdout?.pipe(process.stdout);
|
|
26
|
-
this.
|
|
29
|
+
this.handleIncomingRequests(this.childProcess);
|
|
27
30
|
}
|
|
28
31
|
async initialize() {
|
|
29
32
|
return CodifyTestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
@@ -63,53 +66,52 @@ export class PluginProcess {
|
|
|
63
66
|
kill() {
|
|
64
67
|
this.childProcess.kill();
|
|
65
68
|
}
|
|
66
|
-
|
|
69
|
+
handleIncomingRequests(process) {
|
|
67
70
|
process.on('message', async (message) => {
|
|
68
71
|
if (!ipcMessageValidator(message)) {
|
|
69
72
|
throw new Error(`Invalid message from plugin. ${JSON.stringify(message, null, 2)}`);
|
|
70
73
|
}
|
|
71
|
-
if (message.cmd === MessageCmd.
|
|
74
|
+
if (message.cmd === MessageCmd.COMMAND_REQUEST) {
|
|
72
75
|
const { data, requestId } = message;
|
|
73
|
-
if (!
|
|
74
|
-
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(
|
|
76
|
+
if (!commandRequestValidator(data)) {
|
|
77
|
+
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(commandRequestValidator.errors, null, 2)}`);
|
|
75
78
|
}
|
|
76
79
|
const { command, options } = data;
|
|
77
|
-
const result = await
|
|
80
|
+
const result = await spawnSafe(command, options);
|
|
78
81
|
process.send({
|
|
79
|
-
cmd: MessageCmd.
|
|
82
|
+
cmd: MessageCmd.COMMAND_REQUEST + '_Response',
|
|
80
83
|
data: result,
|
|
81
84
|
requestId,
|
|
82
85
|
});
|
|
83
86
|
}
|
|
87
|
+
if (message.cmd === MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST) {
|
|
88
|
+
const { data, requestId } = message;
|
|
89
|
+
if (!commandRequestValidator(data)) {
|
|
90
|
+
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(commandRequestValidator.errors, null, 2)}`);
|
|
91
|
+
}
|
|
92
|
+
process.send({
|
|
93
|
+
cmd: MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST + '_Response',
|
|
94
|
+
data: {},
|
|
95
|
+
requestId,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (message.cmd === MessageCmd.CODIFY_CREDENTIALS_REQUEST) {
|
|
99
|
+
const { requestId } = message;
|
|
100
|
+
const loginJson = await fs.readFile(path.join(os.homedir(), '.codify', 'credentials.json'), 'utf8');
|
|
101
|
+
if (!loginJson) {
|
|
102
|
+
throw new Error('Unable to get login credentials');
|
|
103
|
+
}
|
|
104
|
+
const login = JSON.parse(loginJson);
|
|
105
|
+
if (!login) {
|
|
106
|
+
throw new Error('Unable to parse login credentials');
|
|
107
|
+
}
|
|
108
|
+
process.send({
|
|
109
|
+
cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST + '_Response',
|
|
110
|
+
data: login.accessToken,
|
|
111
|
+
requestId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
84
114
|
});
|
|
85
115
|
}
|
|
86
116
|
}
|
|
87
|
-
async function sudoSpawn(cmd, opts) {
|
|
88
|
-
return new Promise((resolve) => {
|
|
89
|
-
const output = [];
|
|
90
|
-
const _cmd = `sudo ${cmd}`;
|
|
91
|
-
const _process = spawn(`source ~/.zshrc; ${_cmd}`, [], {
|
|
92
|
-
...opts,
|
|
93
|
-
shell: 'zsh',
|
|
94
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
95
|
-
});
|
|
96
|
-
const { stderr, stdout } = _process;
|
|
97
|
-
stdout.setEncoding('utf8');
|
|
98
|
-
stderr.setEncoding('utf8');
|
|
99
|
-
stdout.on('data', (data) => {
|
|
100
|
-
output.push(data.toString());
|
|
101
|
-
});
|
|
102
|
-
stderr.on('data', (data) => {
|
|
103
|
-
output.push(data.toString());
|
|
104
|
-
});
|
|
105
|
-
stdout.pipe(process.stdout);
|
|
106
|
-
stderr.pipe(process.stderr);
|
|
107
|
-
_process.on('close', (code) => {
|
|
108
|
-
resolve({
|
|
109
|
-
data: output.join(''),
|
|
110
|
-
status: code === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
117
|
//# sourceMappingURL=plugin-process.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-process.js","sourceRoot":"","sources":["../src/plugin-process.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAGL,gBAAgB,EAEhB,UAAU,
|
|
1
|
+
{"version":3,"file":"plugin-process.js","sourceRoot":"","sources":["../src/plugin-process.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAGL,wBAAwB,EAIxB,gBAAgB,EAEhB,UAAU,EAKX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAgB,IAAI,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;IAC1B,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAC1D,MAAM,uBAAuB,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAEtE,MAAM,OAAO,aAAa;IACxB,YAAY,CAAc;IAO1B,YAAY,UAAkB;QAC5B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAEhE,IAAI,CAAC,YAAY,GAAG,IAAI,CACtB,UAAU,EACV,EAAE,EACF;YAGE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;YACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;YAC3C,KAAK,EAAE,MAAM;SACd,CACF,CAAA;QAED,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE/C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,eAAe,CAAC,2BAA2B,CAAC,IAAI,CAAC,YAAY,EAAE;YACpE,GAAG,EAAE,YAAY;YACjB,IAAI,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE;YAC3B,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAyB;QACtC,OAAO,eAAe,CAAC,2BAA2B,CAAC,IAAI,CAAC,YAAY,EAAE;YACpE,GAAG,EAAE,UAAU;YACf,IAAI;YACJ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAqB;QAC9B,OAAO,eAAe,CAAC,2BAA2B,CAAC,IAAI,CAAC,YAAY,EAAE;YACpE,GAAG,EAAE,MAAM;YACX,IAAI;YACJ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAsB;QAChC,OAAO,eAAe,CAAC,2BAA2B,CAAC,IAAI,CAAC,YAAY,EAAE;YACpE,GAAG,EAAE,OAAO;YACZ,IAAI;YACJ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAuB;QAClC,OAAO,eAAe,CAAC,2BAA2B,CAAC,IAAI,CAAC,YAAY,EAAE;YACpE,GAAG,EAAE,QAAQ;YACb,IAAI;YACJ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAEO,sBAAsB,CAAC,OAAqB;QAElD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACtC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,KAAK,UAAU,CAAC,eAAe,EAAE,CAAC;gBAC/C,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;gBACpC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;gBAClH,CAAC;gBAED,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAqC,CAAC;gBACnE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAEjD,OAAO,CAAC,IAAI,CAAe;oBACzB,GAAG,EAAE,UAAU,CAAC,eAAe,GAAG,WAAW;oBAC7C,IAAI,EAAE,MAAM;oBACZ,SAAS;iBACV,CAAC,CAAA;YACJ,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,KAAK,UAAU,CAAC,6BAA6B,EAAE,CAAC;gBAC7D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;gBACpC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;gBAClH,CAAC;gBAED,OAAO,CAAC,IAAI,CAAe;oBACzB,GAAG,EAAE,UAAU,CAAC,6BAA6B,GAAG,WAAW;oBAC3D,IAAI,EAAE,EAAE;oBACR,SAAS;iBACV,CAAC,CAAA;YACJ,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,KAAK,UAAU,CAAC,0BAA0B,EAAE,CAAC;gBAC1D,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;gBAE9B,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC,CAAC;gBACpG,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;gBACpD,CAAC;gBAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACpC,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;gBACtD,CAAC;gBAED,OAAO,CAAC,IAAI,CAAe;oBACzB,GAAG,EAAE,UAAU,CAAC,0BAA0B,GAAG,WAAW;oBACxD,IAAI,EAAE,KAAK,CAAC,WAAW;oBACvB,SAAS;iBACV,CAAC,CAAA;YACJ,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;CAEF"}
|
package/dist/shell.d.ts
ADDED
package/dist/shell.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export var Shell;
|
|
2
|
+
(function (Shell) {
|
|
3
|
+
Shell["ZSH"] = "zsh";
|
|
4
|
+
Shell["BASH"] = "bash";
|
|
5
|
+
Shell["SH"] = "sh";
|
|
6
|
+
Shell["KSH"] = "ksh";
|
|
7
|
+
Shell["CSH"] = "csh";
|
|
8
|
+
Shell["FISH"] = "fish";
|
|
9
|
+
})(Shell || (Shell = {}));
|
|
10
|
+
export const ShellUtils = {
|
|
11
|
+
getShell() {
|
|
12
|
+
const shell = process.env.SHELL || '';
|
|
13
|
+
if (shell.endsWith('bash')) {
|
|
14
|
+
return Shell.BASH;
|
|
15
|
+
}
|
|
16
|
+
if (shell.endsWith('zsh')) {
|
|
17
|
+
return Shell.ZSH;
|
|
18
|
+
}
|
|
19
|
+
if (shell.endsWith('sh')) {
|
|
20
|
+
return Shell.SH;
|
|
21
|
+
}
|
|
22
|
+
if (shell.endsWith('csh')) {
|
|
23
|
+
return Shell.CSH;
|
|
24
|
+
}
|
|
25
|
+
if (shell.endsWith('ksh')) {
|
|
26
|
+
return Shell.KSH;
|
|
27
|
+
}
|
|
28
|
+
if (shell.endsWith('fish')) {
|
|
29
|
+
return Shell.FISH;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
getDefaultShell() {
|
|
34
|
+
return process.env.SHELL;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,KAOX;AAPD,WAAY,KAAK;IACf,oBAAW,CAAA;IACX,sBAAa,CAAA;IACb,kBAAS,CAAA;IACT,oBAAW,CAAA;IACX,oBAAW,CAAA;IACX,sBAAa,CAAA;AACf,CAAC,EAPW,KAAK,KAAL,KAAK,QAOhB;AAGD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,QAAQ;QACN,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;QAEtC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC,IAAI,CAAA;QACnB,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,GAAG,CAAA;QAClB,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,EAAE,CAAA;QACjB,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,GAAG,CAAA;QAClB,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,GAAG,CAAA;QAClB,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC,IAAI,CAAA;QACnB,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,eAAe;QACb,OAAO,OAAO,CAAC,GAAG,CAAC,KAAM,CAAC;IAC5B,CAAC;CACF,CAAA"}
|
package/dist/spawn.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SpawnStatus } from 'codify-schemas';
|
|
2
|
+
export interface SpawnResult {
|
|
3
|
+
status: SpawnStatus;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
data: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SpawnOptions {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
env?: Record<string, unknown>;
|
|
10
|
+
interactive?: boolean;
|
|
11
|
+
requiresRoot?: boolean;
|
|
12
|
+
stdin?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function spawnSafe(cmd: string, options?: SpawnOptions): Promise<SpawnResult>;
|
package/dist/spawn.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as pty from '@homebridge/node-pty-prebuilt-multiarch';
|
|
2
|
+
import { SpawnStatus } from 'codify-schemas';
|
|
3
|
+
import stripAnsi from 'strip-ansi';
|
|
4
|
+
import { Shell, ShellUtils } from './shell.js';
|
|
5
|
+
export function spawnSafe(cmd, options) {
|
|
6
|
+
if (cmd.toLowerCase().includes('sudo')) {
|
|
7
|
+
throw new Error('Command must not include sudo');
|
|
8
|
+
}
|
|
9
|
+
process.stdout.write(`Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : ''));
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const output = [];
|
|
12
|
+
const historyIgnore = ShellUtils.getShell() === Shell.ZSH ? { HISTORY_IGNORE: '*' } : { HISTIGNORE: '*' };
|
|
13
|
+
const env = {
|
|
14
|
+
...process.env, ...options?.env,
|
|
15
|
+
TERM_PROGRAM: 'codify',
|
|
16
|
+
COMMAND_MODE: 'unix2003',
|
|
17
|
+
COLORTERM: 'truecolor',
|
|
18
|
+
...historyIgnore
|
|
19
|
+
};
|
|
20
|
+
const initialCols = process.stdout.columns ?? 80;
|
|
21
|
+
const initialRows = process.stdout.rows ?? 24;
|
|
22
|
+
const command = options?.requiresRoot ? `sudo ${cmd}` : cmd;
|
|
23
|
+
const args = options?.interactive ? ['-i', '-c', command] : ['-c', command];
|
|
24
|
+
const mPty = pty.spawn(ShellUtils.getDefaultShell(), args, {
|
|
25
|
+
...options,
|
|
26
|
+
cols: initialCols,
|
|
27
|
+
rows: initialRows,
|
|
28
|
+
env
|
|
29
|
+
});
|
|
30
|
+
mPty.onData((data) => {
|
|
31
|
+
process.stdout.write(data);
|
|
32
|
+
output.push(data.toString());
|
|
33
|
+
});
|
|
34
|
+
const resizeListener = () => {
|
|
35
|
+
const { columns, rows } = process.stdout;
|
|
36
|
+
mPty.resize(columns, rows);
|
|
37
|
+
};
|
|
38
|
+
const stdinListener = (data) => {
|
|
39
|
+
mPty.write(data.toString());
|
|
40
|
+
};
|
|
41
|
+
process.stdout.on('resize', resizeListener);
|
|
42
|
+
if (options?.stdin) {
|
|
43
|
+
process.stdin.on('data', stdinListener);
|
|
44
|
+
}
|
|
45
|
+
mPty.onExit((result) => {
|
|
46
|
+
process.stdout.off('resize', resizeListener);
|
|
47
|
+
if (options?.stdin) {
|
|
48
|
+
process.stdin.off('data', stdinListener);
|
|
49
|
+
}
|
|
50
|
+
resolve({
|
|
51
|
+
status: result.exitCode === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
|
|
52
|
+
exitCode: result.exitCode,
|
|
53
|
+
data: stripAnsi(output.join('\n').trim()),
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-test",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.53-beta1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"ajv": "^8.12.0",
|
|
18
18
|
"ajv-formats": "^3.0.1",
|
|
19
|
-
"codify-schemas": "1.0.
|
|
19
|
+
"codify-schemas": "1.0.86-beta5",
|
|
20
20
|
"lodash.matches": "^4.6.0",
|
|
21
21
|
"lodash.differencewith": "4.5.0",
|
|
22
22
|
"lodash.unionby": "^4.8.0",
|
|
23
23
|
"nanoid": "^5.0.9",
|
|
24
|
-
"chalk": "^5.4.1"
|
|
24
|
+
"chalk": "^5.4.1",
|
|
25
|
+
"@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
|
|
26
|
+
"strip-ansi": "^7.1.2"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
27
29
|
"@oclif/prettier-config": "^0.2.1",
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
"tsx": "^4.7.3",
|
|
38
40
|
"typescript": "^5",
|
|
39
41
|
"vitest": "^1.4.0",
|
|
40
|
-
"codify-plugin-lib": "1.0.
|
|
42
|
+
"codify-plugin-lib": "1.0.182-beta9"
|
|
41
43
|
},
|
|
42
44
|
"engines": {
|
|
43
45
|
"node": ">=18.0.0"
|
package/src/plugin-process.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
2
|
import {
|
|
3
|
-
ApplyRequestData,
|
|
3
|
+
ApplyRequestData,
|
|
4
|
+
CommandRequestData,
|
|
5
|
+
CommandRequestDataSchema,
|
|
6
|
+
ImportRequestData,
|
|
7
|
+
ImportResponseData,
|
|
4
8
|
InitializeResponseData,
|
|
5
9
|
IpcMessageSchema,
|
|
6
10
|
IpcMessageV2,
|
|
7
|
-
MessageCmd,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
MessageCmd,
|
|
12
|
+
PlanRequestData,
|
|
13
|
+
PlanResponseData,
|
|
14
|
+
ValidateRequestData,
|
|
15
|
+
ValidateResponseData
|
|
11
16
|
} from 'codify-schemas';
|
|
12
17
|
import { nanoid } from 'nanoid';
|
|
13
|
-
import { ChildProcess,
|
|
18
|
+
import { ChildProcess, fork } from 'node:child_process';
|
|
19
|
+
import fs from 'node:fs/promises';
|
|
20
|
+
import * as os from 'node:os';
|
|
14
21
|
import path from 'node:path';
|
|
15
22
|
|
|
23
|
+
import { spawnSafe } from './spawn.js';
|
|
16
24
|
import { CodifyTestUtils } from './test-utils.js';
|
|
17
25
|
|
|
18
26
|
const ajv = new Ajv.default({
|
|
@@ -20,7 +28,7 @@ const ajv = new Ajv.default({
|
|
|
20
28
|
});
|
|
21
29
|
|
|
22
30
|
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
23
|
-
const
|
|
31
|
+
const commandRequestValidator = ajv.compile(CommandRequestDataSchema);
|
|
24
32
|
|
|
25
33
|
export class PluginProcess {
|
|
26
34
|
childProcess: ChildProcess
|
|
@@ -52,7 +60,7 @@ export class PluginProcess {
|
|
|
52
60
|
this.childProcess.stderr?.pipe(process.stderr);
|
|
53
61
|
this.childProcess.stdout?.pipe(process.stdout);
|
|
54
62
|
|
|
55
|
-
this.
|
|
63
|
+
this.handleIncomingRequests(this.childProcess);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
async initialize(): Promise<InitializeResponseData> {
|
|
@@ -99,85 +107,62 @@ export class PluginProcess {
|
|
|
99
107
|
this.childProcess.kill();
|
|
100
108
|
}
|
|
101
109
|
|
|
102
|
-
private
|
|
110
|
+
private handleIncomingRequests(process: ChildProcess) {
|
|
103
111
|
// Listen for incoming sudo incoming sudo requests
|
|
104
112
|
process.on('message', async (message) => {
|
|
105
113
|
if (!ipcMessageValidator(message)) {
|
|
106
114
|
throw new Error(`Invalid message from plugin. ${JSON.stringify(message, null, 2)}`);
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
if (message.cmd === MessageCmd.
|
|
117
|
+
if (message.cmd === MessageCmd.COMMAND_REQUEST) {
|
|
110
118
|
const { data, requestId } = message;
|
|
111
|
-
if (!
|
|
112
|
-
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(
|
|
119
|
+
if (!commandRequestValidator(data)) {
|
|
120
|
+
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(commandRequestValidator.errors, null, 2)}`);
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
const { command, options } = data as unknown as
|
|
116
|
-
const result = await
|
|
123
|
+
const { command, options } = data as unknown as CommandRequestData;
|
|
124
|
+
const result = await spawnSafe(command, options);
|
|
117
125
|
|
|
118
126
|
process.send(<IpcMessageV2>{
|
|
119
|
-
cmd: MessageCmd.
|
|
127
|
+
cmd: MessageCmd.COMMAND_REQUEST + '_Response',
|
|
120
128
|
data: result,
|
|
121
129
|
requestId,
|
|
122
130
|
})
|
|
123
131
|
}
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
}
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
*
|
|
136
|
-
* @param cmd Command to run. Ex: `rm -rf`
|
|
137
|
-
* @param opts Options for spawn
|
|
138
|
-
*
|
|
139
|
-
* @see promiseSpawn
|
|
140
|
-
* @see spawn
|
|
141
|
-
*
|
|
142
|
-
* @returns SpawnResult { status: SUCCESS | ERROR; data: string }
|
|
143
|
-
*/
|
|
144
|
-
async function sudoSpawn(
|
|
145
|
-
cmd: string,
|
|
146
|
-
opts: CodifySpawnOptions,
|
|
147
|
-
): Promise<{ data: string, status: SpawnStatus }> {
|
|
148
|
-
return new Promise((resolve) => {
|
|
149
|
-
const output: string[] = [];
|
|
150
|
-
|
|
151
|
-
const _cmd = `sudo ${cmd}`;
|
|
152
|
-
|
|
153
|
-
// Source start up shells to emulate a users environment vs. a non-interactive non-login shell script
|
|
154
|
-
// Ignore all stdin
|
|
155
|
-
const _process = spawn(`source ~/.zshrc; ${_cmd}`, [], {
|
|
156
|
-
...opts,
|
|
157
|
-
shell: 'zsh',
|
|
158
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
159
|
-
});
|
|
133
|
+
if (message.cmd === MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST) {
|
|
134
|
+
const { data, requestId } = message;
|
|
135
|
+
if (!commandRequestValidator(data)) {
|
|
136
|
+
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(commandRequestValidator.errors, null, 2)}`);
|
|
137
|
+
}
|
|
160
138
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
process.send(<IpcMessageV2>{
|
|
140
|
+
cmd: MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST + '_Response',
|
|
141
|
+
data: {},
|
|
142
|
+
requestId,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
164
145
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
})
|
|
146
|
+
if (message.cmd === MessageCmd.CODIFY_CREDENTIALS_REQUEST) {
|
|
147
|
+
const { requestId } = message;
|
|
168
148
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
149
|
+
const loginJson = await fs.readFile(path.join(os.homedir(), '.codify', 'credentials.json'), 'utf8');
|
|
150
|
+
if (!loginJson) {
|
|
151
|
+
throw new Error('Unable to get login credentials')
|
|
152
|
+
}
|
|
172
153
|
|
|
173
|
-
|
|
174
|
-
|
|
154
|
+
const login = JSON.parse(loginJson);
|
|
155
|
+
if (!login) {
|
|
156
|
+
throw new Error('Unable to parse login credentials')
|
|
157
|
+
}
|
|
175
158
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
159
|
+
process.send(<IpcMessageV2>{
|
|
160
|
+
cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST + '_Response',
|
|
161
|
+
data: login.accessToken,
|
|
162
|
+
requestId,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
181
165
|
})
|
|
182
|
-
}
|
|
166
|
+
}
|
|
167
|
+
|
|
183
168
|
}
|
package/src/shell.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export enum Shell {
|
|
2
|
+
ZSH = 'zsh',
|
|
3
|
+
BASH = 'bash',
|
|
4
|
+
SH = 'sh',
|
|
5
|
+
KSH = 'ksh',
|
|
6
|
+
CSH = 'csh',
|
|
7
|
+
FISH = 'fish',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export const ShellUtils = {
|
|
12
|
+
getShell(): Shell | undefined {
|
|
13
|
+
const shell = process.env.SHELL || '';
|
|
14
|
+
|
|
15
|
+
if (shell.endsWith('bash')) {
|
|
16
|
+
return Shell.BASH
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (shell.endsWith('zsh')) {
|
|
20
|
+
return Shell.ZSH
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (shell.endsWith('sh')) {
|
|
24
|
+
return Shell.SH
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (shell.endsWith('csh')) {
|
|
28
|
+
return Shell.CSH
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (shell.endsWith('ksh')) {
|
|
32
|
+
return Shell.KSH
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (shell.endsWith('fish')) {
|
|
36
|
+
return Shell.FISH
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return undefined;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
getDefaultShell(): string {
|
|
43
|
+
return process.env.SHELL!;
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/spawn.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as pty from '@homebridge/node-pty-prebuilt-multiarch';
|
|
2
|
+
import { SpawnStatus } from 'codify-schemas';
|
|
3
|
+
import stripAnsi from 'strip-ansi';
|
|
4
|
+
|
|
5
|
+
import { Shell, ShellUtils } from './shell.js';
|
|
6
|
+
|
|
7
|
+
export interface SpawnResult {
|
|
8
|
+
status: SpawnStatus;
|
|
9
|
+
exitCode: number;
|
|
10
|
+
data: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SpawnOptions {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
env?: Record<string, unknown>,
|
|
16
|
+
interactive?: boolean,
|
|
17
|
+
requiresRoot?: boolean,
|
|
18
|
+
stdin?: boolean,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function spawnSafe(cmd: string, options?: SpawnOptions): Promise<SpawnResult> {
|
|
22
|
+
if (cmd.toLowerCase().includes('sudo')) {
|
|
23
|
+
throw new Error('Command must not include sudo')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
process.stdout.write(`Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : ''))
|
|
27
|
+
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const output: string[] = [];
|
|
30
|
+
const historyIgnore = ShellUtils.getShell() === Shell.ZSH ? { HISTORY_IGNORE: '*' } : { HISTIGNORE: '*' };
|
|
31
|
+
|
|
32
|
+
// If TERM_PROGRAM=Apple_Terminal is set then ANSI escape characters may be included
|
|
33
|
+
// in the response.
|
|
34
|
+
const env = {
|
|
35
|
+
...process.env, ...options?.env,
|
|
36
|
+
TERM_PROGRAM: 'codify',
|
|
37
|
+
COMMAND_MODE: 'unix2003',
|
|
38
|
+
COLORTERM: 'truecolor',
|
|
39
|
+
...historyIgnore
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Initial terminal dimensions
|
|
43
|
+
const initialCols = process.stdout.columns ?? 80;
|
|
44
|
+
const initialRows = process.stdout.rows ?? 24;
|
|
45
|
+
|
|
46
|
+
const command = options?.requiresRoot ? `sudo ${cmd}` : cmd;
|
|
47
|
+
const args = options?.interactive ? ['-i', '-c', command] : ['-c', command]
|
|
48
|
+
|
|
49
|
+
// Run the command in a pty for interactivity
|
|
50
|
+
const mPty = pty.spawn(ShellUtils.getDefaultShell(), args, {
|
|
51
|
+
...options,
|
|
52
|
+
cols: initialCols,
|
|
53
|
+
rows: initialRows,
|
|
54
|
+
env
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
mPty.onData((data) => {
|
|
58
|
+
process.stdout.write(data);
|
|
59
|
+
output.push(data.toString());
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const resizeListener = () => {
|
|
63
|
+
const { columns, rows } = process.stdout;
|
|
64
|
+
mPty.resize(columns, rows);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const stdinListener = (data: any) => {
|
|
68
|
+
// console.log('stdinListener', data);
|
|
69
|
+
mPty.write(data.toString());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Listen to resize events for the terminal window;
|
|
73
|
+
process.stdout.on('resize', resizeListener);
|
|
74
|
+
if (options?.stdin) {
|
|
75
|
+
process.stdin.on('data', stdinListener)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
mPty.onExit((result) => {
|
|
79
|
+
process.stdout.off('resize', resizeListener);
|
|
80
|
+
if (options?.stdin) {
|
|
81
|
+
process.stdin.off('data', stdinListener);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
resolve({
|
|
85
|
+
status: result.exitCode === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
|
|
86
|
+
exitCode: result.exitCode,
|
|
87
|
+
data: stripAnsi(output.join('\n').trim()),
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
}
|