From 24c4e393a7aeced870733637361cace29ca3f9b3 Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:30:13 +0100 Subject: [PATCH] fix(cli): let gsd update bypass version mismatch gate (#2845) * test(integration): suppress npm pack buffer overflows * fix(cli): let gsd update bypass version mismatch gate --- src/cli.ts | 45 ++++++++++++++++++------------------- src/tests/app-smoke.test.ts | 13 +++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 467760153..a5b255fa9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -133,21 +133,6 @@ const isPrintMode = cliFlags.print || cliFlags.mode !== undefined // Early resource-skew check — must run before TTY gate so version mismatch // errors surface even in non-TTY environments. -exitIfManagedResourcesAreNewer(agentDir) - -// Early TTY check — must come before heavy initialization to avoid dangling -// handles that prevent process.exit() from completing promptly. -const hasSubcommand = cliFlags.messages.length > 0 -if (!process.stdin.isTTY && !isPrintMode && !hasSubcommand && !cliFlags.listModels && !cliFlags.web) { - process.stderr.write('[gsd] Error: Interactive mode requires a terminal (TTY).\n') - process.stderr.write('[gsd] Non-interactive alternatives:\n') - process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n') - process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n') - process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n') - process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n') - process.exit(1) -} - async function ensureRtkBootstrap(): Promise { if ((ensureRtkBootstrap as { _done?: boolean })._done) return @@ -170,6 +155,28 @@ async function ensureRtkBootstrap(): Promise { } } +// `gsd update` — update to the latest version via npm +if (cliFlags.messages[0] === 'update') { + const { runUpdate } = await import('./update-cmd.js') + await runUpdate() + process.exit(0) +} + +exitIfManagedResourcesAreNewer(agentDir) + +// Early TTY check — must come before heavy initialization to avoid dangling +// handles that prevent process.exit() from completing promptly. +const hasSubcommand = cliFlags.messages.length > 0 +if (!process.stdin.isTTY && !isPrintMode && !hasSubcommand && !cliFlags.listModels && !cliFlags.web) { + process.stderr.write('[gsd] Error: Interactive mode requires a terminal (TTY).\n') + process.stderr.write('[gsd] Non-interactive alternatives:\n') + process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n') + process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n') + process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n') + process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n') + process.exit(1) +} + // `gsd --help` — show subcommand-specific help const subcommand = cliFlags.messages[0] if (subcommand && process.argv.includes('--help')) { @@ -199,13 +206,6 @@ if (cliFlags.messages[0] === 'config') { process.exit(0) } -// `gsd update` — update to the latest version via npm -if (cliFlags.messages[0] === 'update') { - const { runUpdate } = await import('./update-cmd.js') - await runUpdate() - process.exit(0) -} - // `gsd web stop [path|all]` — stop web server before anything else if (cliFlags.messages[0] === 'web' && cliFlags.messages[1] === 'stop') { const webFlags = parseWebCliArgs(process.argv) @@ -688,4 +688,3 @@ const interactiveMode = new InteractiveMode(session) markStartup('InteractiveMode') printStartupTimings() await interactiveMode.run() - diff --git a/src/tests/app-smoke.test.ts b/src/tests/app-smoke.test.ts index d68512937..8a43d8cbb 100644 --- a/src/tests/app-smoke.test.ts +++ b/src/tests/app-smoke.test.ts @@ -187,6 +187,19 @@ test("loader MIN_NODE_MAJOR matches package.json engines field", () => { `loader MIN_NODE_MAJOR (${loaderMin}) must match package.json engines.node (>=${engineMin}.0.0)`); }); +test("cli.ts lets gsd update bypass the managed-resource mismatch gate", () => { + const cliSrc = readFileSync(join(projectRoot, "src", "cli.ts"), "utf-8"); + const updateBranchIndex = cliSrc.indexOf("if (cliFlags.messages[0] === 'update')") + const mismatchGateIndex = cliSrc.indexOf("exitIfManagedResourcesAreNewer(agentDir)") + + assert.ok(updateBranchIndex !== -1, "cli.ts contains an update branch") + assert.ok(mismatchGateIndex !== -1, "cli.ts contains the managed-resource mismatch gate") + assert.ok( + updateBranchIndex < mismatchGateIndex, + "gsd update must run before the managed-resource mismatch gate", + ) +}); + // ═══════════════════════════════════════════════════════════════════════════ // 3. resource-loader syncs bundled resources // ═══════════════════════════════════════════════════════════════════════════