From c9b9951c1a09fffc4b7226d9b32b0efe175a4103 Mon Sep 17 00:00:00 2001 From: Danilo Tuler Date: Fri, 20 Feb 2026 11:19:32 -0500 Subject: [PATCH] feat(cli): changes to run to work better with detached stdin (no shell) --- .changeset/tame-cobras-kick.md | 5 ++ apps/cli/src/commands/run.ts | 95 +++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 36 deletions(-) create mode 100644 .changeset/tame-cobras-kick.md diff --git a/.changeset/tame-cobras-kick.md b/.changeset/tame-cobras-kick.md new file mode 100644 index 00000000..eec42fc2 --- /dev/null +++ b/.changeset/tame-cobras-kick.md @@ -0,0 +1,5 @@ +--- +"@cartesi/cli": patch +--- + +changes to run to work better with detached stdin (no shell) diff --git a/apps/cli/src/commands/run.ts b/apps/cli/src/commands/run.ts index 3d02f11f..9b20bee1 100755 --- a/apps/cli/src/commands/run.ts +++ b/apps/cli/src/commands/run.ts @@ -38,32 +38,17 @@ const commaSeparatedList = (value: string) => value.split(","); const shell = async (options: { build?: CommandUnknownOpts; + deployment?: RollupsDeployment; epochLength: number; log?: CommandUnknownOpts; projectName: string; prt?: boolean; + salt: number; }) => { const { build, epochLength, log, projectName, prt } = options; - // keep track of last deployment - let lastDeployment: RollupsDeployment | undefined; - let salt = 0; - - // deploy for the first time - const hash = getMachineHash(); - if (hash) { - lastDeployment = await deploy({ - epochLength, - hash, - projectName, - prt, - salt: numberToHex(salt++, { size: 32 }), - }); - } else { - console.warn( - chalk.yellow("machine snapshot not found, waiting for build"), - ); - } + let lastDeployment = options.deployment; + let salt = options.salt; while (true) { try { @@ -346,6 +331,27 @@ export const createRunCommand = () => { services, }); + // deploy the application + let deployment: RollupsDeployment | undefined; + let salt = 0; + const prt = !authority; + const hash = getMachineHash(); + if (hash) { + deployment = await deploy({ + epochLength, + hash, + projectName, + prt, + salt: numberToHex(salt++, { size: 32 }), + }); + } else { + console.warn( + chalk.yellow( + "machine snapshot not found, waiting for build", + ), + ); + } + const shutdown = async () => { progress.start(`${chalk.cyan(projectName)} stopping...`); try { @@ -359,23 +365,40 @@ export const createRunCommand = () => { process.exit(0); }; - // inhibit SIGINT and SIGTERM, will be handled gracefully by the shell - process.on("SIGINT", () => {}); - process.on("SIGTERM", () => {}); + if (process.stdin.isTTY) { + // inhibit SIGINT and SIGTERM, will be handled gracefully by the shell + process.on("SIGINT", () => {}); + process.on("SIGTERM", () => {}); - const log = program.parent?.commands.find( - (c) => c.name() === "logs", - ); - const build = program.parent?.commands.find( - (c) => c.name() === "build", - ); - await shell({ - build, - epochLength, - log, - projectName, - prt: !authority, - }); - await shutdown(); + const log = program.parent?.commands.find( + (c) => c.name() === "logs", + ); + const build = program.parent?.commands.find( + (c) => c.name() === "build", + ); + await shell({ + build, + deployment, + epochLength, + log, + projectName, + prt, + salt, + }); + await shutdown(); + } else { + // non-interactive mode: wait for SIGINT or SIGTERM to shutdown + await new Promise((resolve) => { + // keep the event loop alive + const keepAlive = setInterval(() => {}, 1 << 30); + const handler = () => { + clearInterval(keepAlive); + resolve(); + }; + process.on("SIGINT", handler); + process.on("SIGTERM", handler); + }); + await shutdown(); + } }); };