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.
@@ -9,5 +9,5 @@ export declare class PluginProcess {
9
9
  apply(data: ApplyRequestData): Promise<void>;
10
10
  import(data: ImportRequestData): Promise<ImportResponseData>;
11
11
  kill(): void;
12
- private handleSudoRequests;
12
+ private handleIncomingRequests;
13
13
  }
@@ -1,14 +1,17 @@
1
1
  import Ajv from 'ajv';
2
- import { IpcMessageSchema, MessageCmd, SpawnStatus, SudoRequestDataSchema } from 'codify-schemas';
2
+ import { CommandRequestDataSchema, IpcMessageSchema, MessageCmd } from 'codify-schemas';
3
3
  import { nanoid } from 'nanoid';
4
- import { fork, spawn } from 'node:child_process';
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 sudoRequestValidator = ajv.compile(SudoRequestDataSchema);
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.handleSudoRequests(this.childProcess);
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
- handleSudoRequests(process) {
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.SUDO_REQUEST) {
74
+ if (message.cmd === MessageCmd.COMMAND_REQUEST) {
72
75
  const { data, requestId } = message;
73
- if (!sudoRequestValidator(data)) {
74
- throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(sudoRequestValidator.errors, null, 2)}`);
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 sudoSpawn(command, options);
80
+ const result = await spawnSafe(command, options);
78
81
  process.send({
79
- cmd: MessageCmd.SUDO_REQUEST + '_Response',
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":"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"}
1
+ {"version":3,"file":"plugin-process.js","sourceRoot":"","sources":["../src/plugin-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare enum Shell {
2
+ ZSH = "zsh",
3
+ BASH = "bash",
4
+ SH = "sh",
5
+ KSH = "ksh",
6
+ CSH = "csh",
7
+ FISH = "fish"
8
+ }
9
+ export declare const ShellUtils: {
10
+ getShell(): Shell | undefined;
11
+ getDefaultShell(): string;
12
+ };
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"}
@@ -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.51",
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.61",
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.171"
42
+ "codify-plugin-lib": "1.0.182-beta9"
41
43
  },
42
44
  "engines": {
43
45
  "node": ">=18.0.0"
@@ -1,18 +1,26 @@
1
1
  import Ajv from 'ajv';
2
2
  import {
3
- ApplyRequestData, ImportRequestData, ImportResponseData,
3
+ ApplyRequestData,
4
+ CommandRequestData,
5
+ CommandRequestDataSchema,
6
+ ImportRequestData,
7
+ ImportResponseData,
4
8
  InitializeResponseData,
5
9
  IpcMessageSchema,
6
10
  IpcMessageV2,
7
- MessageCmd, PlanRequestData, PlanResponseData,
8
- SpawnStatus,
9
- SudoRequestData,
10
- SudoRequestDataSchema, ValidateRequestData, ValidateResponseData
11
+ MessageCmd,
12
+ PlanRequestData,
13
+ PlanResponseData,
14
+ ValidateRequestData,
15
+ ValidateResponseData
11
16
  } from 'codify-schemas';
12
17
  import { nanoid } from 'nanoid';
13
- import { ChildProcess, SpawnOptions, fork, spawn } from 'node:child_process';
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 sudoRequestValidator = ajv.compile(SudoRequestDataSchema);
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.handleSudoRequests(this.childProcess);
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 handleSudoRequests(process: ChildProcess) {
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.SUDO_REQUEST) {
117
+ if (message.cmd === MessageCmd.COMMAND_REQUEST) {
110
118
  const { data, requestId } = message;
111
- if (!sudoRequestValidator(data)) {
112
- throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(sudoRequestValidator.errors, null, 2)}`);
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 SudoRequestData;
116
- const result = await sudoSpawn(command, options);
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.SUDO_REQUEST + '_Response',
127
+ cmd: MessageCmd.COMMAND_REQUEST + '_Response',
120
128
  data: result,
121
129
  requestId,
122
130
  })
123
131
  }
124
- })
125
- }
126
-
127
- }
128
132
 
129
- type CodifySpawnOptions = {
130
- cwd?: string;
131
- throws?: boolean,
132
- } & Omit<SpawnOptions, 'detached' | 'shell' | 'stdio'>
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
- const { stderr, stdout } = _process
162
- stdout.setEncoding('utf8');
163
- stderr.setEncoding('utf8');
139
+ process.send(<IpcMessageV2>{
140
+ cmd: MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST + '_Response',
141
+ data: {},
142
+ requestId,
143
+ })
144
+ }
164
145
 
165
- stdout.on('data', (data) => {
166
- output.push(data.toString());
167
- })
146
+ if (message.cmd === MessageCmd.CODIFY_CREDENTIALS_REQUEST) {
147
+ const { requestId } = message;
168
148
 
169
- stderr.on('data', (data) => {
170
- output.push(data.toString());
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
- stdout.pipe(process.stdout);
174
- stderr.pipe(process.stderr);
154
+ const login = JSON.parse(loginJson);
155
+ if (!login) {
156
+ throw new Error('Unable to parse login credentials')
157
+ }
175
158
 
176
- _process.on('close', (code) => {
177
- resolve({
178
- data: output.join(''),
179
- status: code === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
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
+ }