subprocess maintenance

This commit is contained in:
Rob Colbert 2026-05-26 16:53:01 -04:00
parent 3be6ebbffa
commit 2a2bbcd075
3 changed files with 22 additions and 12 deletions

View File

@ -696,6 +696,11 @@ class GadgetDrone extends GadgetProcess {
process.exit(exitCode); process.exit(exitCode);
}); });
process.on("uncaughtException", (error) => {
this.log.fatal("uncaught exception", { err: error });
process.exit(1);
});
process.on("warning", (error) => { process.on("warning", (error) => {
if (error.name === "DeprecationWarning") return; if (error.name === "DeprecationWarning") return;
this.log.alert("warning", { error }); this.log.alert("warning", { error });

View File

@ -2,8 +2,6 @@
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us> // Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
// Licensed under the Apache License, Version 2.0 // Licensed under the Apache License, Version 2.0
import assert from "node:assert";
import path from "node:path"; import path from "node:path";
import fs from "node:fs"; import fs from "node:fs";
@ -63,17 +61,24 @@ class SubProcessService extends GadgetService {
args?: string[], args?: string[],
): Promise<SubProcess> { ): Promise<SubProcess> {
const NOW = new Date(); const NOW = new Date();
assert( const gadgetDir = WorkspaceService.gadgetDir;
WorkspaceService.gadgetDir, if (!gadgetDir) {
"Gadget workspace directory not initialized", throw new Error("Gadget workspace directory not initialized");
); }
const logDir = path.join(WorkspaceService.gadgetDir, "subprocess-logs"); const logDir = path.join(gadgetDir, "subprocess-logs");
await fs.promises.mkdir(logDir, { recursive: true }); await fs.promises.mkdir(logDir, { recursive: true });
const projectDir = WorkspaceService.getProjectDirectory(project.slug); const projectDir = WorkspaceService.getProjectDirectory(project.slug);
const child = spawn(cmd, args, { cwd: projectDir }); const spawnArgs = args ?? [];
assert(child.pid, "subprocess did not define a process id"); const useShell = spawnArgs.length === 0;
const child = spawn(cmd, spawnArgs, { cwd: projectDir, shell: useShell });
child.on("error", (err) => {
this.log.error("subprocess spawn failed", { cmd, args, error: err.message });
});
if (!child.pid) {
throw new Error("Failed to spawn subprocess: child process has no pid");
}
const pid = child.pid; const pid = child.pid;
const stdoutFileName = path.join(logDir, `subproc-${pid}.stdout.log`); const stdoutFileName = path.join(logDir, `subproc-${pid}.stdout.log`);

View File

@ -45,7 +45,7 @@ export class SubprocessTool extends GadgetTool {
command: { command: {
type: "string", type: "string",
description: description:
"The shell command to execute (required for cmd=create). Example: 'node', 'npm', 'python', './my-server'.", "The full shell command to execute (required for cmd=create). Examples: 'npm run dev', 'pnpm dev:frontend', 'node server.js'. The command is executed via /bin/sh.",
}, },
args: { args: {
type: "array", type: "array",
@ -128,7 +128,7 @@ export class SubprocessTool extends GadgetTool {
message: "The 'command' parameter is required for cmd=create.", message: "The 'command' parameter is required for cmd=create.",
parameter: "command", parameter: "command",
recoveryHint: recoveryHint:
"Provide the command to execute, e.g. 'node', 'npm', 'python'.", "Provide the full command string to execute, e.g. 'npm run dev', 'pnpm dev:frontend'. The command is executed via /bin/sh.",
}); });
} }
@ -168,7 +168,7 @@ export class SubprocessTool extends GadgetTool {
}); });
return formatError({ return formatError({
code: "OPERATION_FAILED", code: "OPERATION_FAILED",
message: `Failed to spawn subprocess: ${message}`, message: `Failed to spawn subprocess: ${message}. Make sure the command is installed and available in PATH, or use the full path to the executable.`,
}); });
} }
} }