
























@@ -33,6 +33,10 @@ const FETCH_BODY_MAX_BYTES = readPositiveInt(
33331024 * 1024,
3434);
3535const MAX_RSS_MIB = readPositiveInt(process.env.OPENCLAW_KITCHEN_SINK_MAX_RSS_MIB, 2048);
36+const MAX_COMMAND_RSS_MIB = readPositiveInt(
37+process.env.OPENCLAW_KITCHEN_SINK_COMMAND_MAX_RSS_MIB,
38+8192,
39+);
3640const GATEWAY_TEARDOWN_GRACE_MS = 10000;
3741const GATEWAY_TEARDOWN_KILL_GRACE_MS = 2000;
3842const OUTPUT_CAPTURE_CHARS = readPositiveInt(
@@ -74,6 +78,7 @@ Environment:
7478 OPENCLAW_KITCHEN_SINK_RPC_INSTALL_MS Plugin install timeout.
7579 OPENCLAW_KITCHEN_SINK_RPC_CALL_MS RPC call timeout.
7680 OPENCLAW_KITCHEN_SINK_MAX_RSS_MIB Gateway RSS ceiling.
81+ OPENCLAW_KITCHEN_SINK_COMMAND_MAX_RSS_MIB Install/CLI command RSS ceiling.
7782 OPENCLAW_KITCHEN_SINK_KEEP_TMP=1 Preserve the isolated temp home.
7883`;
7984}
@@ -482,11 +487,19 @@ async function rpcCallViaCli(method, params, options) {
482487JSON.stringify(params ?? {}),
483488],
484489options.env,
485-{ timeoutMs: RPC_TIMEOUT_MS + 30000 },
490+createRpcCliRunOptions(method, options),
486491);
487492return parseJsonOutput(stdout);
488493}
489494495+export function createRpcCliRunOptions(method, options = {}) {
496+return {
497+ ...options.commandResourceOptions,
498+resourceLabel: `gateway call ${method}`,
499+timeoutMs: RPC_TIMEOUT_MS + 30000,
500+};
501+}
502+490503export function findDistCallGatewayModuleFiles(cwd = process.cwd()) {
491504const distDir = path.join(cwd, "dist");
492505return fs.existsSync(distDir)
@@ -1331,20 +1344,38 @@ async function sampleWindowsProcess(pid, run, commandLineNeedles = []) {
13311344return null;
13321345}
133313461334-export function assertResourceCeiling(sample) {
1347+function assertProcessResourceCeiling(sample, { label, maxRssMiB, requireSample = true }) {
13351348if (!sample) {
1336-throw new Error("gateway RSS sample was not captured");
1349+if (requireSample) {
1350+throw new Error(`${label} RSS sample was not captured`);
1351+}
1352+return;
13371353}
1338-if (sample.rssMiB > MAX_RSS_MIB) {
1339-throw new Error(`gateway RSS exceeded ${MAX_RSS_MIB} MiB: ${sample.rssMiB} MiB`);
1354+if (sample.rssMiB > maxRssMiB) {
1355+throw new Error(`${label} RSS exceeded ${maxRssMiB} MiB: ${sample.rssMiB} MiB`);
13401356}
1341-if ((sample.aggregateRssMiB ?? sample.rssMiB) > MAX_RSS_MIB) {
1357+if ((sample.aggregateRssMiB ?? sample.rssMiB) > maxRssMiB) {
13421358throw new Error(
1343-`gateway aggregate RSS exceeded ${MAX_RSS_MIB} MiB: ${sample.aggregateRssMiB} MiB`,
1359+`${label} aggregate RSS exceeded ${maxRssMiB} MiB: ${sample.aggregateRssMiB} MiB`,
13441360);
13451361}
13461362}
134713631364+export function assertResourceCeiling(sample) {
1365+assertProcessResourceCeiling(sample, {
1366+label: "gateway",
1367+maxRssMiB: MAX_RSS_MIB,
1368+});
1369+}
1370+1371+export function assertCommandResourceCeiling(sample) {
1372+assertProcessResourceCeiling(sample, {
1373+label: "command",
1374+maxRssMiB: MAX_COMMAND_RSS_MIB,
1375+requireSample: false,
1376+});
1377+}
1378+13481379export function findErrorLogFindings(logPath) {
13491380if (!fs.existsSync(logPath)) {
13501381return [];
@@ -1509,6 +1540,7 @@ export async function main() {
15091540assertIncludesAny(inspectProviders, EXPECTED_PROVIDERS, "plugins inspect providers");
1510154115111542child = await startGateway(runner, port, env, logPath);
1543+const rpcOptions = { commandResourceOptions, env, port, runner };
15121544const sampleGateway = async () => {
15131545const gatewayCommandLineNeedles = ["gateway", "--port", String(port)];
15141546const processSampleOptions = runner.pnpm
@@ -1548,27 +1580,27 @@ export async function main() {
15481580throw new Error(`/readyz did not report ready: ${JSON.stringify(readyz)}`);
15491581}
155015821551-await retryRpcCall("health", {}, { runner, port, env });
1552-await retryRpcCall("status", {}, { runner, port, env });
1583+await retryRpcCall("health", {}, rpcOptions);
1584+await retryRpcCall("status", {}, rpcOptions);
15531585const channelStatus = await retryRpcCall(
15541586"channels.status",
15551587{ probe: true, timeoutMs: 10000 },
1556-{ runner, port, env },
1588+rpcOptions,
15571589);
15581590const channelAccount = assertChannelAccountRunning(channelStatus);
1559159115601592const commands = await retryRpcCall(
15611593"commands.list",
15621594{ agentId: "main", scope: "text" },
1563-{ runner, port, env },
1595+rpcOptions,
15641596);
15651597const commandNames = extractPluginCommandNames(commands);
15661598assertIncludesAll(commandNames, EXPECTED_COMMANDS, "commands.list plugin commands");
1567159915681600const catalog = await retryRpcCall(
15691601"tools.catalog",
15701602{ agentId: "main", includePlugins: true },
1571-{ runner, port, env },
1603+rpcOptions,
15721604);
15731605const catalogTools = extractToolEntries(catalog);
15741606const catalogToolIds = catalogTools.map((entry) => entry?.id).filter(isNonEmptyString);
@@ -1581,12 +1613,12 @@ export async function main() {
15811613const createdSession = await retryRpcCall(
15821614"sessions.create",
15831615{ key: SESSION_KEY, agentId: "main", label: "kitchen-sink-rpc" },
1584-{ runner, port, env },
1616+rpcOptions,
15851617);
15861618const effective = await retryRpcCall(
15871619"tools.effective",
15881620{ sessionKey: createdSession.key, agentId: "main" },
1589-{ runner, port, env },
1621+rpcOptions,
15901622);
15911623const effectiveToolIds = extractToolEntries(effective).map((entry) => entry?.id);
15921624assertIncludesAny(effectiveToolIds, EXPECTED_TOOLS, "tools.effective plugin tools");
@@ -1600,29 +1632,30 @@ export async function main() {
16001632agentId: "main",
16011633idempotencyKey: "kitchen-sink-rpc-search",
16021634},
1603-{ runner, port, env },
1635+rpcOptions,
16041636);
16051637assertToolInvokeResult(invoked);
160616381607-const ttsProviders = await retryRpcCall("tts.providers", {}, { runner, port, env });
1608-const ttsStatus = await retryRpcCall("tts.status", {}, { runner, port, env });
1639+const ttsProviders = await retryRpcCall("tts.providers", {}, rpcOptions);
1640+const ttsStatus = await retryRpcCall("tts.status", {}, rpcOptions);
16091641assertIncludesAny(extractProviderIds(ttsProviders), EXPECTED_SPEECH_PROVIDERS, "tts.providers");
16101642assertIncludesAny(extractProviderIds(ttsStatus), EXPECTED_SPEECH_PROVIDERS, "tts.status");
161116431612-const uiDescriptors = await retryRpcCall("plugins.uiDescriptors", {}, { runner, port, env });
1644+const uiDescriptors = await retryRpcCall("plugins.uiDescriptors", {}, rpcOptions);
16131645if (!uiDescriptors || typeof uiDescriptors !== "object") {
16141646throw new Error(
16151647`plugins.uiDescriptors returned invalid payload: ${JSON.stringify(uiDescriptors)}`,
16161648);
16171649}
1618-const stability = await retryRpcCall("diagnostics.stability", {}, { runner, port, env });
1650+const stability = await retryRpcCall("diagnostics.stability", {}, rpcOptions);
16191651assertDiagnosticStabilityClean(stability);
16201652await sampleInFlight?.catch(() => {});
16211653const finalSample = await sampleGateway();
16221654assertResourceCeiling(finalSample);
16231655const peakSample = summarizeProcessSamples(processSamples);
16241656const commandPeakSample = summarizeProcessSamples(commandSamples);
16251657assertResourceCeiling(peakSample);
1658+assertCommandResourceCeiling(commandPeakSample);
16261659assertNoErrorLogs(logPath);
1627166016281661console.log(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。