diff --git a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs index a4a577a65db..6ef81cab90d 100644 --- a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs +++ b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs @@ -8,42 +8,33 @@ import { setTimeout as delay } from "node:timers/promises"; import { fileURLToPath } from "node:url"; const TOKEN = "bundled-plugin-runtime-smoke-token"; -const OUTPUT_CAPTURE_CHARS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS, +const OUTPUT_CAPTURE_CHARS = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS", 1024 * 1024, ); -const LOG_SCAN_BYTES = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES, +const LOG_SCAN_BYTES = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES", 256 * 1024, ); -const GATEWAY_LOG_CAPTURE_BYTES = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES, +const GATEWAY_LOG_CAPTURE_BYTES = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES", 16 * 1024 * 1024, ); -const WATCHDOG_MS = readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS, 1000); -const READY_TIMEOUT_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS, - 900000, -); -const RPC_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS, 60000); -const RPC_READY_TIMEOUT_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS, +const WATCHDOG_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS", 1000); +const READY_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS", 900000); +const RPC_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS", 60000); +const RPC_READY_TIMEOUT_MS = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS", 210000, ); -const COMMAND_TIMEOUT_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS, - 120000, -); -const HTTP_PROBE_TIMEOUT_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS, - 5000, -); -const GATEWAY_TEARDOWN_GRACE_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS, +const COMMAND_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS", 120000); +const HTTP_PROBE_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS", 5000); +const GATEWAY_TEARDOWN_GRACE_MS = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS", 10000, ); -const GATEWAY_TEARDOWN_KILL_GRACE_MS = readPositiveInt( - process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS, +const GATEWAY_TEARDOWN_KILL_GRACE_MS = readPositiveIntEnv( + "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS", 1000, ); const GATEWAY_READY_LOG_NEEDLE = Buffer.from("[gateway] ready"); @@ -62,13 +53,23 @@ const activeGatewayChildren = new Set(); const parentSignalHandlers = new Map(); let parentCleanupInstalled = false; -function readPositiveInt(raw, fallback) { +function readPositiveIntEnv(name, fallback) { + return readPositiveInt(process.env[name], fallback, name); +} + +function readPositiveInt(raw, fallback, name) { const text = String(raw ?? "").trim(); - if (!/^\d+$/u.test(text)) { + if (!text) { return fallback; } + if (!/^\d+$/u.test(text)) { + throw new Error(`invalid ${name}: ${text}`); + } const parsed = Number(text); - return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; + if (!Number.isInteger(parsed) || parsed <= 0) { + throw new Error(`invalid ${name}: ${text}`); + } + return parsed; } function readJson(file) { @@ -866,7 +867,7 @@ async function smokePlugin(pluginId, pluginDir, requiresConfig, pluginIndex, plu const manifest = loadManifest(pluginDir, pluginRoot); const plan = buildPluginPlan(manifest); const port = - readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) + pluginIndex * 3; + readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3; const config = ensureGatewayConfig( activateSmokePlugin(readConfig(), pluginId, plan.channels), port, @@ -1196,9 +1197,7 @@ async function smokeTtsGlobalDisable(pluginId, pluginDir, provider, pluginIndex, return; } const port = - readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) + - pluginIndex * 3 + - 1; + readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3 + 1; const env = createIsolatedStateEnv(`tts-disabled-${pluginId}`); writeConfig( ensureGatewayConfig( @@ -1251,9 +1250,7 @@ async function smokeOpenAiTts(pluginIndex) { return; } const port = - readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) + - pluginIndex * 3 + - 2; + readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3 + 2; const env = createIsolatedStateEnv("tts-openai-live"); writeConfig( ensureGatewayConfig( diff --git a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts index 80b0cf990b5..88951c57975 100644 --- a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts +++ b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts @@ -341,14 +341,11 @@ describe("bundled plugin install/uninstall probe", () => { }); it("rejects loose runtime output limit env values instead of parsing prefixes", async () => { - const runtimeSmoke = await importRuntimeSmokeWithEnv({ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars", - }); - - expect(runtimeSmoke.appendBoundedOutput({ text: "", truncatedChars: 0 }, "abcdef")).toEqual({ - text: "abcdef", - truncatedChars: 0, - }); + await expect( + importRuntimeSmokeWithEnv({ + OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars", + }), + ).rejects.toThrow("invalid OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: 5chars"); }); it("keeps runtime log tail reads bounded", async () => { @@ -366,18 +363,11 @@ describe("bundled plugin install/uninstall probe", () => { }); it("rejects loose runtime log scan byte env values instead of parsing prefixes", async () => { - const runtimeSmoke = await importRuntimeSmokeWithEnv({ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: "64bytes", - }); - const root = makePackageRoot(); - const logPath = path.join(root, "gateway.log"); - fs.writeFileSync(logPath, `${"old log line\n".repeat(20)}[gateway] ready\n`, "utf8"); - - const tail = runtimeSmoke.readFileTail(logPath); - - expect(Buffer.byteLength(tail)).toBeGreaterThan(64); - expect(tail).toContain("old log line"); - expect(tail).toContain("[gateway] ready"); + await expect( + importRuntimeSmokeWithEnv({ + OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: "64bytes", + }), + ).rejects.toThrow("invalid OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: 64bytes"); }); it("remembers runtime ready logs after they fall outside the tail", async () => { diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 0f52a631375..93dccc2dd3f 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -2239,8 +2239,9 @@ output="$(run_logged_print_heartbeat plugins-run 08 bash -c 'printf "captured co expect(runner).toContain('tee "$RUN_LOG"'); expect(runner).not.toContain('cat "$RUN_LOG"'); expect(probe).toContain('"openclaw.plugin.json"'); - expect(runtimeSmoke).toContain("process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS"); - expect(runtimeSmoke).toContain("900000"); + expect(runtimeSmoke).toContain( + 'readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS", 900000)', + ); expect(sweep).toContain("read -r plugin_id plugin_dir requires_config"); expect(sweep).toContain('node "$OPENCLAW_ENTRY" plugins install "$plugin_id"'); expect(sweep).toContain('node "$OPENCLAW_ENTRY" plugins uninstall "$plugin_id" --force');