























@@ -12,35 +12,55 @@ import http from "node:http";
1212import os from "node:os";
1313import path from "node:path";
1414import process from "node:process";
15+import { pathToFileURL } from "node:url";
1516import { resolveDefaultAgentDir } from "../src/agents/agent-scope.js";
1617import { ensureAuthProfileStore, type AuthProfileCredential } from "../src/agents/auth-profiles.js";
1718import { normalizeProviderId } from "../src/agents/model-selection.js";
1819import { validateAnthropicSetupToken } from "../src/commands/auth-token.js";
1920import { callGateway } from "../src/gateway/call.js";
2021import { extractPayloadText } from "../src/gateway/test-helpers.agent-results.js";
2122import { getFreePortBlockWithPermissionFallback } from "../src/test-utils/ports.js";
23+import {
24+parseBooleanEnv,
25+parseStrictIntegerOption,
26+redactForDevToolLog,
27+} from "./lib/dev-tooling-safety.ts";
22282329const TRANSPORT = process.env.OPENCLAW_PROMPT_TRANSPORT?.trim() === "direct" ? "direct" : "gateway";
2430const GATEWAY_PROMPT_MODE =
2531process.env.OPENCLAW_PROMPT_MODE?.trim() === "override" ? "override" : "extra";
2632const PROMPT_TEXT = process.env.OPENCLAW_PROMPT_TEXT?.trim() ?? "";
2733const PROMPT_LIST_JSON = process.env.OPENCLAW_PROMPT_LIST_JSON?.trim() ?? "";
2834const USER_PROMPT = process.env.OPENCLAW_USER_PROMPT?.trim() || "is clawd here?";
29-const ENABLE_CAPTURE = process.env.OPENCLAW_PROMPT_CAPTURE === "1";
30-const INCLUDE_RAW = process.env.OPENCLAW_PROMPT_INCLUDE_RAW === "1";
35+const ENABLE_CAPTURE = parseBooleanEnv({
36+fallback: false,
37+name: "OPENCLAW_PROMPT_CAPTURE",
38+raw: process.env.OPENCLAW_PROMPT_CAPTURE,
39+});
40+const INCLUDE_RAW = parseBooleanEnv({
41+fallback: false,
42+name: "OPENCLAW_PROMPT_INCLUDE_RAW",
43+raw: process.env.OPENCLAW_PROMPT_INCLUDE_RAW,
44+});
3145const CLAUDE_BIN = process.env.CLAUDE_BIN?.trim() || "claude";
3246const NODE_BIN = process.env.OPENCLAW_NODE_BIN?.trim() || process.execPath;
33-const TIMEOUT_MS = Number(process.env.OPENCLAW_PROMPT_TIMEOUT_MS ?? "45000");
34-const GATEWAY_TIMEOUT_MS = Number(process.env.OPENCLAW_PROMPT_GATEWAY_TIMEOUT_MS ?? "120000");
47+const TIMEOUT_MS = parseStrictIntegerOption({
48+fallback: 45_000,
49+label: "OPENCLAW_PROMPT_TIMEOUT_MS",
50+min: 1,
51+raw: process.env.OPENCLAW_PROMPT_TIMEOUT_MS,
52+});
53+const GATEWAY_TIMEOUT_MS = parseStrictIntegerOption({
54+fallback: 120_000,
55+label: "OPENCLAW_PROMPT_GATEWAY_TIMEOUT_MS",
56+min: 1,
57+raw: process.env.OPENCLAW_PROMPT_GATEWAY_TIMEOUT_MS,
58+});
3559const SETUP_TOKEN_RAW = process.env.OPENCLAW_LIVE_SETUP_TOKEN?.trim() ?? "";
3660const SETUP_TOKEN_VALUE = process.env.OPENCLAW_LIVE_SETUP_TOKEN_VALUE?.trim() ?? "";
3761const SETUP_TOKEN_PROFILE = process.env.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE?.trim() ?? "";
3862const DIRECT_CLAUDE_ARGS = ["-p", "--append-system-prompt"];
396340-if (!PROMPT_TEXT && !PROMPT_LIST_JSON) {
41-throw new Error("missing OPENCLAW_PROMPT_TEXT or OPENCLAW_PROMPT_LIST_JSON");
42-}
43-4464type CaptureSummary = {
4565url?: string;
4666authScheme?: string;
@@ -125,6 +145,22 @@ function summarizeCapture(
125145};
126146}
127147148+function resolveAnthropicUpstreamUrl(
149+requestUrl: string | undefined,
150+upstreamBaseUrl: string,
151+): string {
152+const raw = requestUrl || "/";
153+if (!raw.startsWith("/") || raw.startsWith("//")) {
154+throw new Error(`refusing non-origin proxy request URL: ${JSON.stringify(raw)}`);
155+}
156+const upstream = new URL(upstreamBaseUrl);
157+if (upstream.protocol !== "https:" || upstream.hostname !== "api.anthropic.com") {
158+throw new Error(`refusing unexpected Anthropic upstream origin: ${upstream.origin}`);
159+}
160+const requestPath = new URL(raw, "http://127.0.0.1");
161+return new URL(`${requestPath.pathname}${requestPath.search}`, upstream).toString();
162+}
163+128164function matchesExtraUsage400(...parts: Array<string | undefined>): boolean {
129165return parts
130166.filter((value): value is string => typeof value === "string" && value.length > 0)
@@ -283,7 +319,7 @@ async function startAnthropicProxy(params: { port: number; upstreamBaseUrl: stri
283319const rawBody = requestBody.toString("utf8");
284320lastCapture = extractProxyCapture(rawBody, req);
285321286-const upstreamUrl = new URL(req.url ?? "/", params.upstreamBaseUrl).toString();
322+const upstreamUrl = resolveAnthropicUpstreamUrl(req.url, params.upstreamBaseUrl);
287323const headers = new Headers();
288324for (const [key, value] of Object.entries(req.headers)) {
289325if (value === undefined) {
@@ -327,7 +363,7 @@ async function startAnthropicProxy(params: { port: number; upstreamBaseUrl: stri
327363res.end();
328364} catch (error) {
329365res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
330-res.end(`proxy error: ${String(error)}`);
366+res.end(redactForDevToolLog(`proxy error: ${String(error)}`));
331367}
332368});
333369server.on("connection", (socket) => {
@@ -405,8 +441,8 @@ async function runDirectPrompt(prompt: string): Promise<PromptResult> {
405441transport: "direct",
406442exitCode: exit.code,
407443signal: exit.signal,
408-stdout: joinedStdout.trim() || undefined,
409-stderr: joinedStderr.trim() || undefined,
444+stdout: redactForDevToolLog(joinedStdout.trim()) || undefined,
445+stderr: redactForDevToolLog(joinedStderr.trim()) || undefined,
410446matchedExtraUsage400: matchesExtraUsage400(joinedStdout, joinedStderr),
411447capture: summarizeCapture(proxy?.getLastCapture(), prompt),
412448 tmpDir,
@@ -485,7 +521,7 @@ async function waitForGatewayReady(url: string, token: string): Promise<void> {
485521486522async function readLogTail(logPath: string): Promise<string> {
487523const raw = await fs.readFile(logPath, "utf8").catch(() => "");
488-return raw.split(/\r?\n/).slice(-40).join("\n").trim();
524+return redactForDevToolLog(raw.split(/\r?\n/).slice(-40).join("\n").trim());
489525}
490526491527async function runGatewayPrompt(prompt: string): Promise<PromptResult> {
@@ -601,7 +637,7 @@ async function runGatewayPrompt(prompt: string): Promise<PromptResult> {
601637ok: false,
602638transport: "gateway",
603639promptMode: GATEWAY_PROMPT_MODE,
604-error: `missing runId: ${JSON.stringify(agentRes)}`,
640+error: redactForDevToolLog(`missing runId: ${JSON.stringify(agentRes)}`),
605641matchedExtraUsage400: false,
606642capture: summarizeCapture(proxy?.getLastCapture(), prompt),
607643 tmpDir,
@@ -626,7 +662,10 @@ async function runGatewayPrompt(prompt: string): Promise<PromptResult> {
626662promptMode: GATEWAY_PROMPT_MODE,
627663status: waitRes.status,
628664text: text || undefined,
629-error: waitRes.status === "ok" ? undefined : waitRes.error || logTail || "agent.wait failed",
665+error:
666+waitRes.status === "ok"
667+ ? undefined
668+ : redactForDevToolLog(waitRes.error || logTail || "agent.wait failed"),
630669matchedExtraUsage400: matched400,
631670capture: summarizeCapture(proxy?.getLastCapture(), prompt),
632671 tmpDir,
@@ -638,6 +677,9 @@ async function runGatewayPrompt(prompt: string): Promise<PromptResult> {
638677}
639678640679async function main() {
680+if (!PROMPT_TEXT && !PROMPT_LIST_JSON) {
681+throw new Error("missing OPENCLAW_PROMPT_TEXT or OPENCLAW_PROMPT_LIST_JSON");
682+}
641683const prompts = PROMPT_LIST_JSON ? (JSON.parse(PROMPT_LIST_JSON) as string[]) : [PROMPT_TEXT];
642684const results: PromptResult[] = [];
643685for (const prompt of prompts) {
@@ -659,4 +701,16 @@ async function main() {
659701);
660702}
661703662-await main();
704+export const testing = {
705+ matchesExtraUsage400,
706+ resolveAnthropicUpstreamUrl,
707+ summarizeCapture,
708+ summarizeText,
709+};
710+711+if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
712+await main().catch((error) => {
713+console.error(redactForDevToolLog(error instanceof Error ? error.message : String(error)));
714+process.exitCode = 1;
715+});
716+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。