fix(crestodian): fail no-tty startup

This commit is contained in:
Peter Steinberger
2026-05-02 07:40:12 +01:00
parent 4a4aad8935
commit e7a9623968
3 changed files with 45 additions and 0 deletions

View File

@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
- Active Memory: use the configured recall timeout as the blocking prompt-build hook budget by default and move cold-start setup grace behind explicit `setupGraceTimeoutMs` config, so the plugin no longer silently extends 15000 ms configs to 45000 ms on the main lane. Fixes #75843. Thanks @vishutdhar.
- Plugins/web-provider: reuse the active gateway plugin registry for runtime web provider resolution after deriving the same candidate plugin ids as the loader path, avoiding a redundant `loadOpenClawPlugins` call on every request while preserving origin and scope filters. Fixes #75513. Thanks @jochen.
- Crestodian/CLI: exit non-zero when interactive Crestodian is invoked without a TTY, so scripts and CI no longer treat the setup error as success. Fixes #73646 and supersedes #73928 and #74059. Thanks @bittoby, @luyao618, and @Linux2010.
- Agents/sandbox: preserve existing workspace file modes when sandbox edits atomically replace files, so 0644 files do not collapse to 0600 after Write/Edit/apply_patch. Fixes #44077. Thanks @patosullivan.
- Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval.
- Codex/app-server: restart the shared Codex app-server client once when it closes during startup thread resume, preserving the existing thread binding instead of retrying `thread/start` on a closed client. Thanks @vincentkoc.

View File

@@ -112,4 +112,47 @@ describe("runCrestodian", () => {
expect(onReadyCalls).toBe(1);
expect(lines.join("\n")).not.toContain("Say: status");
});
it.each([
{
name: "stdin is not a TTY",
input: { isTTY: false } as unknown as NodeJS.ReadableStream,
output: { isTTY: true } as unknown as NodeJS.WritableStream,
interactive: true,
},
{
name: "stdout is not a TTY",
input: { isTTY: true } as unknown as NodeJS.ReadableStream,
output: { isTTY: false } as unknown as NodeJS.WritableStream,
interactive: true,
},
{
name: "interactive mode is disabled",
input: { isTTY: true } as unknown as NodeJS.ReadableStream,
output: { isTTY: true } as unknown as NodeJS.WritableStream,
interactive: false,
},
])("exits non-zero when $name", async ({ input, output, interactive }) => {
const { runtime, lines } = createCrestodianTestRuntime();
let runInteractiveTuiCalls = 0;
await expect(
runCrestodian(
{
input,
output,
interactive,
runInteractiveTui: async () => {
runInteractiveTuiCalls += 1;
},
},
runtime,
),
).rejects.toThrow("exit 1");
expect(runInteractiveTuiCalls).toBe(0);
expect(lines.join("\n")).toContain(
"Crestodian needs an interactive TTY. Use --message for one command.",
);
});
});

View File

@@ -92,6 +92,7 @@ export async function runCrestodian(
const outputIsTty = (output as { isTTY?: boolean }).isTTY === true;
if (!interactive || !inputIsTty || !outputIsTty) {
runtime.error("Crestodian needs an interactive TTY. Use --message for one command.");
runtime.exit(1);
return;
}