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
This commit is contained in:
mastertyko 2026-03-27 21:30:13 +01:00 committed by GitHub
parent 2175f59522
commit 24c4e393a7
2 changed files with 35 additions and 23 deletions

View file

@ -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<void> {
if ((ensureRtkBootstrap as { _done?: boolean })._done) return
@ -170,6 +155,28 @@ async function ensureRtkBootstrap(): Promise<void> {
}
}
// `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 <subcommand> --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()

View file

@ -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
// ═══════════════════════════════════════════════════════════════════════════