mirror of
https://github.com/clawdbot/clawdbot.git
synced 2026-02-01 11:57:48 +01:00
fix: unblock claude-cli live runs
This commit is contained in:
@@ -177,7 +177,8 @@ CLAWDBOT_LIVE_TEST=1 CLAWDBOT_LIVE_SETUP_TOKEN=1 CLAWDBOT_LIVE_SETUP_TOKEN_PROFI
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_MODEL="claude-cli/claude-opus-4-5"`
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_COMMAND="/full/path/to/claude"`
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_ARGS='["-p","--output-format","json","--permission-mode","bypassPermissions"]'`
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_CLEAR_ENV='["ANTHROPIC_API_KEY"]'`
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_CLEAR_ENV='["ANTHROPIC_API_KEY","ANTHROPIC_API_KEY_OLD"]'`
|
||||
- `CLAWDBOT_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG=0` to keep Claude CLI MCP config enabled (default disables MCP config with a temporary empty file).
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const DEFAULT_CLAUDE_BACKEND: CliBackendConfig = {
|
||||
systemPromptArg: "--append-system-prompt",
|
||||
systemPromptMode: "append",
|
||||
systemPromptWhen: "first",
|
||||
clearEnv: ["ANTHROPIC_API_KEY"],
|
||||
clearEnv: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD"],
|
||||
serialize: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -455,6 +455,7 @@ export async function runCliAgent(params: {
|
||||
backend,
|
||||
prompt: params.prompt,
|
||||
});
|
||||
const stdinPayload = stdin ?? "";
|
||||
const args = buildCliArgs({
|
||||
backend,
|
||||
modelId: normalizedModel,
|
||||
@@ -526,7 +527,7 @@ export async function runCliAgent(params: {
|
||||
timeoutMs: params.timeoutMs,
|
||||
cwd: workspaceDir,
|
||||
env,
|
||||
...(stdin ? { input: stdin } : {}),
|
||||
input: stdinPayload,
|
||||
});
|
||||
|
||||
const stdout = result.stdout.trim();
|
||||
|
||||
@@ -21,7 +21,7 @@ const DEFAULT_ARGS = [
|
||||
"json",
|
||||
"--dangerously-skip-permissions",
|
||||
];
|
||||
const DEFAULT_CLEAR_ENV: string[] = [];
|
||||
const DEFAULT_CLEAR_ENV = ["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD"];
|
||||
|
||||
function extractPayloadText(result: unknown): string {
|
||||
const record = result as Record<string, unknown>;
|
||||
@@ -52,6 +52,20 @@ function parseJsonStringArray(
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function withMcpConfigOverrides(
|
||||
args: string[],
|
||||
mcpConfigPath: string,
|
||||
): string[] {
|
||||
const next = [...args];
|
||||
if (!next.includes("--strict-mcp-config")) {
|
||||
next.push("--strict-mcp-config");
|
||||
}
|
||||
if (!next.includes("--mcp-config")) {
|
||||
next.push("--mcp-config", mcpConfigPath);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
async function getFreePort(): Promise<number> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const srv = createServer();
|
||||
@@ -134,12 +148,16 @@ describeLive("gateway live (cli backend)", () => {
|
||||
skipGmail: process.env.CLAWDBOT_SKIP_GMAIL_WATCHER,
|
||||
skipCron: process.env.CLAWDBOT_SKIP_CRON,
|
||||
skipCanvas: process.env.CLAWDBOT_SKIP_CANVAS_HOST,
|
||||
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
||||
anthropicApiKeyOld: process.env.ANTHROPIC_API_KEY_OLD,
|
||||
};
|
||||
|
||||
process.env.CLAWDBOT_SKIP_PROVIDERS = "1";
|
||||
process.env.CLAWDBOT_SKIP_GMAIL_WATCHER = "1";
|
||||
process.env.CLAWDBOT_SKIP_CRON = "1";
|
||||
process.env.CLAWDBOT_SKIP_CANVAS_HOST = "1";
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
delete process.env.ANTHROPIC_API_KEY_OLD;
|
||||
|
||||
const token = `test-${randomUUID()}`;
|
||||
process.env.CLAWDBOT_GATEWAY_TOKEN = token;
|
||||
@@ -156,7 +174,7 @@ describeLive("gateway live (cli backend)", () => {
|
||||
|
||||
const cliCommand =
|
||||
process.env.CLAWDBOT_LIVE_CLI_BACKEND_COMMAND ?? "claude";
|
||||
const cliArgs =
|
||||
const baseCliArgs =
|
||||
parseJsonStringArray(
|
||||
"CLAWDBOT_LIVE_CLI_BACKEND_ARGS",
|
||||
process.env.CLAWDBOT_LIVE_CLI_BACKEND_ARGS,
|
||||
@@ -167,6 +185,20 @@ describeLive("gateway live (cli backend)", () => {
|
||||
process.env.CLAWDBOT_LIVE_CLI_BACKEND_CLEAR_ENV,
|
||||
) ?? DEFAULT_CLEAR_ENV;
|
||||
|
||||
const tempDir = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdbot-live-cli-"),
|
||||
);
|
||||
const mcpConfigPath = path.join(tempDir, "claude-mcp.json");
|
||||
await fs.writeFile(
|
||||
mcpConfigPath,
|
||||
`${JSON.stringify({ mcpServers: {} }, null, 2)}\n`,
|
||||
);
|
||||
const disableMcpConfig =
|
||||
process.env.CLAWDBOT_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG !== "0";
|
||||
const cliArgs = disableMcpConfig
|
||||
? withMcpConfigOverrides(baseCliArgs, mcpConfigPath)
|
||||
: baseCliArgs;
|
||||
|
||||
const cfg = loadConfig();
|
||||
const existingBackends = cfg.agents?.defaults?.cliBackends ?? {};
|
||||
const nextCfg = {
|
||||
@@ -185,16 +217,13 @@ describeLive("gateway live (cli backend)", () => {
|
||||
command: cliCommand,
|
||||
args: cliArgs,
|
||||
clearEnv: cliClearEnv,
|
||||
systemPromptWhen: "never",
|
||||
},
|
||||
},
|
||||
sandbox: { mode: "off" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tempDir = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdbot-live-cli-"),
|
||||
);
|
||||
const tempConfigPath = path.join(tempDir, "clawdbot.json");
|
||||
await fs.writeFile(tempConfigPath, `${JSON.stringify(nextCfg, null, 2)}\n`);
|
||||
process.env.CLAWDBOT_CONFIG_PATH = tempConfigPath;
|
||||
@@ -252,6 +281,12 @@ describeLive("gateway live (cli backend)", () => {
|
||||
if (previous.skipCanvas === undefined)
|
||||
delete process.env.CLAWDBOT_SKIP_CANVAS_HOST;
|
||||
else process.env.CLAWDBOT_SKIP_CANVAS_HOST = previous.skipCanvas;
|
||||
if (previous.anthropicApiKey === undefined)
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
else process.env.ANTHROPIC_API_KEY = previous.anthropicApiKey;
|
||||
if (previous.anthropicApiKeyOld === undefined)
|
||||
delete process.env.ANTHROPIC_API_KEY_OLD;
|
||||
else process.env.ANTHROPIC_API_KEY_OLD = previous.anthropicApiKeyOld;
|
||||
}
|
||||
}, 60_000);
|
||||
});
|
||||
|
||||
@@ -59,13 +59,14 @@ export async function runCommandWithTimeout(
|
||||
? { timeoutMs: optionsOrTimeout }
|
||||
: optionsOrTimeout;
|
||||
const { timeoutMs, cwd, input, env } = options;
|
||||
const hasInput = input !== undefined;
|
||||
|
||||
// Spawn with inherited stdin (TTY) so tools like `pi` stay interactive when needed.
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn(argv[0], argv.slice(1), {
|
||||
stdio: [input ? "pipe" : "inherit", "pipe", "pipe"],
|
||||
stdio: [hasInput ? "pipe" : "inherit", "pipe", "pipe"],
|
||||
cwd,
|
||||
env: env ? { ...process.env, ...env } : process.env,
|
||||
env: env ?? process.env,
|
||||
});
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
@@ -74,8 +75,8 @@ export async function runCommandWithTimeout(
|
||||
child.kill("SIGKILL");
|
||||
}, timeoutMs);
|
||||
|
||||
if (input && child.stdin) {
|
||||
child.stdin.write(input);
|
||||
if (hasInput && child.stdin) {
|
||||
child.stdin.write(input ?? "");
|
||||
child.stdin.end();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user