@quatrain/cli 1.1.8 → 1.1.9
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/Command.d.ts +25 -0
- package/dist/Command.d.ts.map +1 -0
- package/dist/Command.js +96 -0
- package/dist/Command.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/Command.test.ts +62 -0
- package/src/Command.ts +153 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare class Command {
|
|
2
|
+
private bin;
|
|
3
|
+
private argsList;
|
|
4
|
+
private workingDir?;
|
|
5
|
+
private envVars;
|
|
6
|
+
private stdoutBehavior;
|
|
7
|
+
private stderrBehavior;
|
|
8
|
+
private useShell;
|
|
9
|
+
private shellType?;
|
|
10
|
+
constructor(bin: string);
|
|
11
|
+
static create(bin: string): Command;
|
|
12
|
+
arg(value: string): this;
|
|
13
|
+
args(values: string[]): this;
|
|
14
|
+
cwd(dir: string): this;
|
|
15
|
+
env(vars: Record<string, string>): this;
|
|
16
|
+
inherit(): this;
|
|
17
|
+
usePowerShell(use?: boolean, type?: 'powershell' | 'pwsh'): this;
|
|
18
|
+
execute(): Promise<{
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
code: number | null;
|
|
22
|
+
success: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=Command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Command.d.ts","sourceRoot":"","sources":["../src/Command.ts"],"names":[],"mappings":"AAMA,qBAAa,OAAO;IAClB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAwB;gBAM9B,GAAG,EAAE,MAAM;IAQvB,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQnC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IASxB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAS5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAStB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAQvC,OAAO,IAAI,IAAI;IAWf,aAAa,CAAC,GAAG,UAAO,EAAE,IAAI,GAAE,YAAY,GAAG,MAAqB,GAAG,IAAI;IAUrE,OAAO,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CA4DpG"}
|
package/dist/Command.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Command = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
class Command {
|
|
6
|
+
bin;
|
|
7
|
+
argsList = [];
|
|
8
|
+
workingDir;
|
|
9
|
+
envVars = {};
|
|
10
|
+
stdoutBehavior = 'pipe';
|
|
11
|
+
stderrBehavior = 'pipe';
|
|
12
|
+
useShell = false;
|
|
13
|
+
shellType;
|
|
14
|
+
constructor(bin) {
|
|
15
|
+
this.bin = bin;
|
|
16
|
+
}
|
|
17
|
+
static create(bin) {
|
|
18
|
+
return new Command(bin);
|
|
19
|
+
}
|
|
20
|
+
arg(value) {
|
|
21
|
+
this.argsList.push(value);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
args(values) {
|
|
25
|
+
this.argsList.push(...values);
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
cwd(dir) {
|
|
29
|
+
this.workingDir = dir;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
env(vars) {
|
|
33
|
+
Object.assign(this.envVars, vars);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
inherit() {
|
|
37
|
+
this.stdoutBehavior = 'inherit';
|
|
38
|
+
this.stderrBehavior = 'inherit';
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
usePowerShell(use = true, type = 'powershell') {
|
|
42
|
+
this.useShell = use;
|
|
43
|
+
this.shellType = type;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
async execute() {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let spawnBin = this.bin;
|
|
49
|
+
let spawnArgs = [...this.argsList];
|
|
50
|
+
const isWin = process.platform === 'win32';
|
|
51
|
+
if (this.useShell || (isWin && this.shellType)) {
|
|
52
|
+
const shell = this.shellType || (isWin ? 'powershell.exe' : 'pwsh');
|
|
53
|
+
const cmdString = [this.bin, ...this.argsList]
|
|
54
|
+
.map((arg) => {
|
|
55
|
+
if (arg.includes(' ') || arg.includes('"') || arg.includes("'")) {
|
|
56
|
+
return `"${arg.replace(/"/g, '`"')}"`;
|
|
57
|
+
}
|
|
58
|
+
return arg;
|
|
59
|
+
})
|
|
60
|
+
.join(' ');
|
|
61
|
+
spawnBin = shell;
|
|
62
|
+
spawnArgs = ['-NoProfile', '-NonInteractive', '-Command', cmdString];
|
|
63
|
+
}
|
|
64
|
+
const child = (0, node_child_process_1.spawn)(spawnBin, spawnArgs, {
|
|
65
|
+
cwd: this.workingDir,
|
|
66
|
+
env: { ...process.env, ...this.envVars },
|
|
67
|
+
stdio: ['inherit', this.stdoutBehavior, this.stderrBehavior],
|
|
68
|
+
});
|
|
69
|
+
let stdout = '';
|
|
70
|
+
let stderr = '';
|
|
71
|
+
if (child.stdout) {
|
|
72
|
+
child.stdout.on('data', (data) => {
|
|
73
|
+
stdout += data.toString();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (child.stderr) {
|
|
77
|
+
child.stderr.on('data', (data) => {
|
|
78
|
+
stderr += data.toString();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
child.on('close', (code) => {
|
|
82
|
+
resolve({
|
|
83
|
+
stdout,
|
|
84
|
+
stderr,
|
|
85
|
+
code,
|
|
86
|
+
success: code === 0,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
child.on('error', (err) => {
|
|
90
|
+
reject(err);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.Command = Command;
|
|
96
|
+
//# sourceMappingURL=Command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Command.js","sourceRoot":"","sources":["../src/Command.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,QAAQ,EAAE,CAAC;AAKpB,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAUrF;AAKD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF;AAKD,wBAAsB,SAAS,CAAC,CAAC,GAAG,MAAM,EACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,EAAE,GAC/C,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,qBAAa,UAAW,SAAQ,OAAO;CAAG"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,QAAQ,EAAE,CAAC;AAKpB,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAUrF;AAKD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF;AAKD,wBAAsB,SAAS,CAAC,CAAC,GAAG,MAAM,EACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,EAAE,GAC/C,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,qBAAa,UAAW,SAAQ,OAAO;CAAG;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CliCommand = exports.inquirer = void 0;
|
|
6
|
+
exports.Command = exports.CliCommand = exports.inquirer = void 0;
|
|
7
7
|
exports.askConfirm = askConfirm;
|
|
8
8
|
exports.askInput = askInput;
|
|
9
9
|
exports.askChoice = askChoice;
|
|
@@ -46,4 +46,6 @@ const commander_1 = require("commander");
|
|
|
46
46
|
class CliCommand extends commander_1.Command {
|
|
47
47
|
}
|
|
48
48
|
exports.CliCommand = CliCommand;
|
|
49
|
+
var Command_1 = require("./Command");
|
|
50
|
+
Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return Command_1.Command; } });
|
|
49
51
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAOA,gCAUC;AAKD,4BAUC;AAKD,8BAaC;AAlDD,wDAAgC;AAEvB,mBAFF,kBAAQ,CAEE;AAKV,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,UAAU,GAAG,IAAI;IACjE,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO,EAAE,UAAU;SACpB;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAKM,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,UAAmB;IACjE,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO,EAAE,UAAU;SACpB;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAKM,KAAK,UAAU,SAAS,CAC7B,OAAe,EACf,OAAgD;IAEhD,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO;SACR;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAED,yCAAoC;AAKpC,MAAa,UAAW,SAAQ,mBAAO;CAAG;AAA1C,gCAA0C"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAOA,gCAUC;AAKD,4BAUC;AAKD,8BAaC;AAlDD,wDAAgC;AAEvB,mBAFF,kBAAQ,CAEE;AAKV,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,UAAU,GAAG,IAAI;IACjE,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO,EAAE,UAAU;SACpB;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAKM,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,UAAmB;IACjE,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO,EAAE,UAAU;SACpB;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAKM,KAAK,UAAU,SAAS,CAC7B,OAAe,EACf,OAAgD;IAEhD,MAAM,MAAM,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;QACnC;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,OAAO;YACb,OAAO;YACP,OAAO;SACR;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAED,yCAAoC;AAKpC,MAAa,UAAW,SAAQ,mBAAO;CAAG;AAA1C,gCAA0C;AAE1C,qCAAoC;AAA3B,kGAAA,OAAO,OAAA"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Command } from './Command';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
describe('Command', () => {
|
|
5
|
+
test('should construct and return static instance', () => {
|
|
6
|
+
const cmd = Command.create('node');
|
|
7
|
+
expect(cmd).toBeInstanceOf(Command);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('should execute system echo command and retrieve stdout', async () => {
|
|
11
|
+
const cmd = Command.create('echo')
|
|
12
|
+
.arg('hello world');
|
|
13
|
+
|
|
14
|
+
const result = await cmd.execute();
|
|
15
|
+
expect(result.success).toBe(true);
|
|
16
|
+
expect(result.code).toBe(0);
|
|
17
|
+
expect(result.stdout.trim()).toBe('hello world');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should pass environment variables correctly', async () => {
|
|
21
|
+
// Run a node script that prints the environment variable
|
|
22
|
+
const cmd = Command.create('node')
|
|
23
|
+
.args(['-e', 'console.log(process.env.TEST_VAR)'])
|
|
24
|
+
.env({ TEST_VAR: 'quatrain-cli' });
|
|
25
|
+
|
|
26
|
+
const result = await cmd.execute();
|
|
27
|
+
expect(result.success).toBe(true);
|
|
28
|
+
expect(result.stdout.trim()).toBe('quatrain-cli');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should execute within specified working directory', async () => {
|
|
32
|
+
const targetDir = path.resolve(__dirname);
|
|
33
|
+
const cmd = Command.create('node')
|
|
34
|
+
.args(['-e', 'console.log(process.cwd())'])
|
|
35
|
+
.cwd(targetDir);
|
|
36
|
+
|
|
37
|
+
const result = await cmd.execute();
|
|
38
|
+
expect(result.success).toBe(true);
|
|
39
|
+
// CWD returned from subprocess should match our targetDir
|
|
40
|
+
// Note: On Mac, /var/folders can resolve to /private/var/folders, so we resolve both paths
|
|
41
|
+
const resolvedPath = fsResolve(result.stdout.trim());
|
|
42
|
+
const expectedPath = fsResolve(targetDir);
|
|
43
|
+
expect(resolvedPath).toBe(expectedPath);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('should report process failure on bad commands', async () => {
|
|
47
|
+
const cmd = Command.create('node')
|
|
48
|
+
.args(['-e', 'process.exit(5)']);
|
|
49
|
+
|
|
50
|
+
const result = await cmd.execute();
|
|
51
|
+
expect(result.success).toBe(false);
|
|
52
|
+
expect(result.code).toBe(5);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
function fsResolve(p: string): string {
|
|
57
|
+
try {
|
|
58
|
+
return path.resolve(p);
|
|
59
|
+
} catch {
|
|
60
|
+
return p;
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/Command.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fluent builder for launching and managing system subprocesses.
|
|
5
|
+
* Supports cross-platform execution and shell redirection (e.g. PowerShell).
|
|
6
|
+
*/
|
|
7
|
+
export class Command {
|
|
8
|
+
private bin: string;
|
|
9
|
+
private argsList: string[] = [];
|
|
10
|
+
private workingDir?: string;
|
|
11
|
+
private envVars: Record<string, string> = {};
|
|
12
|
+
private stdoutBehavior: 'pipe' | 'inherit' | 'ignore' = 'pipe';
|
|
13
|
+
private stderrBehavior: 'pipe' | 'inherit' | 'ignore' = 'pipe';
|
|
14
|
+
private useShell = false;
|
|
15
|
+
private shellType?: 'powershell' | 'pwsh';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Instantiate a new Command.
|
|
19
|
+
* @param bin Name or path of the binary/command.
|
|
20
|
+
*/
|
|
21
|
+
constructor(bin: string) {
|
|
22
|
+
this.bin = bin;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Static factory to instantiate a new Command.
|
|
27
|
+
* @param bin Name or path of the binary/command.
|
|
28
|
+
*/
|
|
29
|
+
static create(bin: string): Command {
|
|
30
|
+
return new Command(bin);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Add a single command-line argument.
|
|
35
|
+
* @param value Argument string.
|
|
36
|
+
*/
|
|
37
|
+
arg(value: string): this {
|
|
38
|
+
this.argsList.push(value);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Add multiple command-line arguments.
|
|
44
|
+
* @param values List of argument strings.
|
|
45
|
+
*/
|
|
46
|
+
args(values: string[]): this {
|
|
47
|
+
this.argsList.push(...values);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set the working directory for the subprocess.
|
|
53
|
+
* @param dir Absolute or relative directory path.
|
|
54
|
+
*/
|
|
55
|
+
cwd(dir: string): this {
|
|
56
|
+
this.workingDir = dir;
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Set or extend environment variables for the subprocess.
|
|
62
|
+
* @param vars Key-value dictionary of environment variables.
|
|
63
|
+
*/
|
|
64
|
+
env(vars: Record<string, string>): this {
|
|
65
|
+
Object.assign(this.envVars, vars);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Configure the subprocess to inherit stdout and stderr from the parent process.
|
|
71
|
+
*/
|
|
72
|
+
inherit(): this {
|
|
73
|
+
this.stdoutBehavior = 'inherit';
|
|
74
|
+
this.stderrBehavior = 'inherit';
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Enable executing the command via PowerShell (powershell.exe or pwsh).
|
|
80
|
+
* @param use Whether to use PowerShell.
|
|
81
|
+
* @param type Shell binary choice ('powershell' or 'pwsh').
|
|
82
|
+
*/
|
|
83
|
+
usePowerShell(use = true, type: 'powershell' | 'pwsh' = 'powershell'): this {
|
|
84
|
+
this.useShell = use;
|
|
85
|
+
this.shellType = type;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Execute the configured command and return a Promise resolving on process exit.
|
|
91
|
+
* @returns Promise resolving with standard output, error streams, and status.
|
|
92
|
+
*/
|
|
93
|
+
async execute(): Promise<{ stdout: string; stderr: string; code: number | null; success: boolean }> {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
let spawnBin = this.bin;
|
|
96
|
+
let spawnArgs = [...this.argsList];
|
|
97
|
+
|
|
98
|
+
const isWin = process.platform === 'win32';
|
|
99
|
+
|
|
100
|
+
// Route execution via PowerShell if configured or if on Windows with shellType set
|
|
101
|
+
if (this.useShell || (isWin && this.shellType)) {
|
|
102
|
+
const shell = this.shellType || (isWin ? 'powershell.exe' : 'pwsh');
|
|
103
|
+
|
|
104
|
+
// Assemble argument list safely escaping quotes for PowerShell Command parsing
|
|
105
|
+
const cmdString = [this.bin, ...this.argsList]
|
|
106
|
+
.map((arg) => {
|
|
107
|
+
if (arg.includes(' ') || arg.includes('"') || arg.includes("'")) {
|
|
108
|
+
return `"${arg.replace(/"/g, '`"')}"`;
|
|
109
|
+
}
|
|
110
|
+
return arg;
|
|
111
|
+
})
|
|
112
|
+
.join(' ');
|
|
113
|
+
|
|
114
|
+
spawnBin = shell;
|
|
115
|
+
spawnArgs = ['-NoProfile', '-NonInteractive', '-Command', cmdString];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const child = spawn(spawnBin, spawnArgs, {
|
|
119
|
+
cwd: this.workingDir,
|
|
120
|
+
env: { ...process.env, ...this.envVars },
|
|
121
|
+
stdio: ['inherit', this.stdoutBehavior, this.stderrBehavior],
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
let stdout = '';
|
|
125
|
+
let stderr = '';
|
|
126
|
+
|
|
127
|
+
if (child.stdout) {
|
|
128
|
+
child.stdout.on('data', (data) => {
|
|
129
|
+
stdout += data.toString();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (child.stderr) {
|
|
134
|
+
child.stderr.on('data', (data) => {
|
|
135
|
+
stderr += data.toString();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
child.on('close', (code) => {
|
|
140
|
+
resolve({
|
|
141
|
+
stdout,
|
|
142
|
+
stderr,
|
|
143
|
+
code,
|
|
144
|
+
success: code === 0,
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
child.on('error', (err) => {
|
|
149
|
+
reject(err);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|