@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
166 lines • 6.52 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const error_1 = __importDefault(require("@sprucelabs/error"));
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
const escapeRegExp_1 = __importDefault(require("lodash/escapeRegExp"));
const string_argv_1 = __importDefault(require("string-argv"));
const tree_kill_1 = __importDefault(require("tree-kill"));
const SpruceError_1 = __importDefault(require("../errors/SpruceError"));
process.setMaxListeners(100);
class CommandServiceImpl {
cwd;
activeChildProcess;
ignoreCloseErrors = false;
static fakeResponses = [];
static commandsRunCapturedByMockResponses = [];
constructor(cwd) {
this.cwd = cwd;
}
getCwd() {
return this.cwd;
}
setCwd(cwd) {
this.cwd = cwd;
}
async execute(cmd, options) {
const cwd = this.cwd;
const args = options?.args || (0, string_argv_1.default)(cmd);
const executable = options?.args ? cmd : args.shift();
const boundKill = this.kill.bind(this);
if (!executable) {
throw new Error('Bad params sent to command service');
}
const { mockResponse, mockKey } = this.getMockResponse(executable, args);
if (mockResponse) {
CommandServiceImpl.commandsRunCapturedByMockResponses.push(mockKey);
mockResponse.callback?.(executable, args);
if (mockResponse.code !== 0) {
throw new SpruceError_1.default({
code: 'EXECUTING_COMMAND_FAILED',
cmd: `${executable} ${args.join(' ')}`,
cwd,
stdout: mockResponse.stdout,
stderr: mockResponse.stderr,
});
}
return { stdout: mockResponse.stdout ?? '' };
}
process.on('exit', boundKill);
return new Promise((resolve, reject) => {
let stdout = '';
let stderr = '';
const spawnOptions = options?.shouldStream
? {
stdio: 'inherit',
cwd,
env: {
PATH: process.env.PATH,
IS_CLI: 'true',
FORCE_COLOR: options?.forceColor ? '1' : '0',
...options?.env,
},
}
: {
cwd,
env: {
PATH: process.env.PATH,
IS_CLI: 'true',
FORCE_COLOR: options?.forceColor ? '1' : '0',
...options?.env,
},
shell: true,
...options?.spawnOptions,
};
let child;
if (spawnOptions.shell) {
const commandStr = options?.args
? [executable, ...args].join(' ')
: cmd;
child = (0, child_process_1.spawn)(commandStr, spawnOptions);
}
else {
child = (0, child_process_1.spawn)(executable, args, spawnOptions);
}
this.activeChildProcess = child;
if (options?.outStream) {
child.stdout?.pipe(options.outStream);
}
child.stdout?.addListener('data', (data) => {
options?.onData?.(data.toString());
stdout += data;
});
child.stderr?.addListener('data', (data) => {
options?.onError?.(data.toString());
stderr += data;
});
const closeHandler = (code) => {
process.off('exit', boundKill);
if (!this.activeChildProcess) {
return;
}
this.activeChildProcess = undefined;
setTimeout(() => {
child.stdout?.removeAllListeners();
child.stderr?.removeAllListeners();
child.removeAllListeners();
if (code === 0 ||
this.ignoreCloseErrors ||
options?.ignoreErrors) {
resolve({ stdout });
this.ignoreCloseErrors = false;
}
else {
if (stderr.search((0, escapeRegExp_1.default)(spruce_skill_utils_1.ERROR_DIVIDER)) > -1) {
const stderrParts = stderr.split(spruce_skill_utils_1.ERROR_DIVIDER);
const err = error_1.default.parse(stderrParts[1], SpruceError_1.default);
reject(err);
return;
}
reject(new SpruceError_1.default({
code: 'EXECUTING_COMMAND_FAILED',
cmd: `${executable} ${args.join(' ')}`,
cwd,
stdout,
stderr,
}));
}
}, 0);
};
child.addListener('close', closeHandler);
child.addListener('exit', closeHandler);
});
}
kill = () => {
if (this.activeChildProcess?.pid) {
this.ignoreCloseErrors = true;
(0, tree_kill_1.default)(this.activeChildProcess.pid, 'SIGTERM');
}
};
pid = () => {
return this.activeChildProcess?.pid;
};
getMockResponse(executable, args) {
const mockKey = `${executable} ${args.join(' ')}`.trim();
const commands = CommandServiceImpl.fakeResponses;
const match = commands.find((r) => r.command instanceof RegExp
? mockKey.search(r.command) > -1
: r.command.replace(/ +/gis, '') ===
mockKey.replace(/ +/gis, ''));
return { mockResponse: match?.response, mockKey };
}
static fakeCommand(command, response) {
this.fakeResponses.unshift({
command,
response,
});
}
static clearFakedResponses() {
this.fakeResponses = [];
}
}
exports.default = CommandServiceImpl;
//# sourceMappingURL=CommandService.js.map