@koda-sl/baker-bridge 0.109.0-dev.f28c07bd2 → 0.109.0-dev.f7ef4c7e1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -51
- package/dist/broker/baker-api.d.ts +23 -0
- package/dist/broker/baker-api.d.ts.map +1 -0
- package/dist/broker/baker-api.js +55 -0
- package/dist/broker/baker-api.js.map +1 -0
- package/dist/broker/broker-client.d.ts +10 -0
- package/dist/broker/broker-client.d.ts.map +1 -0
- package/dist/broker/broker-client.js +60 -0
- package/dist/broker/broker-client.js.map +1 -0
- package/dist/broker/broker-process.d.ts +10 -0
- package/dist/broker/broker-process.d.ts.map +1 -0
- package/dist/broker/broker-process.js +20 -0
- package/dist/broker/broker-process.js.map +1 -0
- package/dist/broker/broker-process.test.d.ts +2 -0
- package/dist/broker/broker-process.test.d.ts.map +1 -0
- package/dist/broker/broker-process.test.js +13 -0
- package/dist/broker/broker-process.test.js.map +1 -0
- package/dist/broker/ca.d.ts +38 -0
- package/dist/broker/ca.d.ts.map +1 -0
- package/dist/broker/ca.js +80 -0
- package/dist/broker/ca.js.map +1 -0
- package/dist/broker/ca.test.d.ts +2 -0
- package/dist/broker/ca.test.d.ts.map +1 -0
- package/dist/broker/ca.test.js +58 -0
- package/dist/broker/ca.test.js.map +1 -0
- package/dist/broker/credential-cache.d.ts +24 -0
- package/dist/broker/credential-cache.d.ts.map +1 -0
- package/dist/broker/credential-cache.js +76 -0
- package/dist/broker/credential-cache.js.map +1 -0
- package/dist/broker/credential-cache.test.d.ts +2 -0
- package/dist/broker/credential-cache.test.d.ts.map +1 -0
- package/dist/broker/credential-cache.test.js +136 -0
- package/dist/broker/credential-cache.test.js.map +1 -0
- package/dist/broker/credential-text.d.ts +45 -0
- package/dist/broker/credential-text.d.ts.map +1 -0
- package/dist/broker/credential-text.js +103 -0
- package/dist/broker/credential-text.js.map +1 -0
- package/dist/broker/credential-text.test.d.ts +2 -0
- package/dist/broker/credential-text.test.d.ts.map +1 -0
- package/dist/broker/credential-text.test.js +54 -0
- package/dist/broker/credential-text.test.js.map +1 -0
- package/dist/broker/env.d.ts +21 -0
- package/dist/broker/env.d.ts.map +1 -0
- package/dist/broker/env.js +20 -0
- package/dist/broker/env.js.map +1 -0
- package/dist/broker/main.d.ts +3 -0
- package/dist/broker/main.d.ts.map +1 -0
- package/dist/broker/main.js +57 -0
- package/dist/broker/main.js.map +1 -0
- package/dist/broker/proxy.d.ts +24 -0
- package/dist/broker/proxy.d.ts.map +1 -0
- package/dist/broker/proxy.integration.test.d.ts +2 -0
- package/dist/broker/proxy.integration.test.d.ts.map +1 -0
- package/dist/broker/proxy.integration.test.js +197 -0
- package/dist/broker/proxy.integration.test.js.map +1 -0
- package/dist/broker/proxy.js +147 -0
- package/dist/broker/proxy.js.map +1 -0
- package/dist/broker/rewrite.d.ts +44 -0
- package/dist/broker/rewrite.d.ts.map +1 -0
- package/dist/broker/rewrite.js +291 -0
- package/dist/broker/rewrite.js.map +1 -0
- package/dist/broker/rewrite.test.d.ts +2 -0
- package/dist/broker/rewrite.test.d.ts.map +1 -0
- package/dist/broker/rewrite.test.js +171 -0
- package/dist/broker/rewrite.test.js.map +1 -0
- package/dist/broker/socket-service.d.ts +18 -0
- package/dist/broker/socket-service.d.ts.map +1 -0
- package/dist/broker/socket-service.js +113 -0
- package/dist/broker/socket-service.js.map +1 -0
- package/dist/broker/socket-service.test.d.ts +2 -0
- package/dist/broker/socket-service.test.d.ts.map +1 -0
- package/dist/broker/socket-service.test.js +80 -0
- package/dist/broker/socket-service.test.js.map +1 -0
- package/dist/broker/supervisor-main.d.ts +3 -0
- package/dist/broker/supervisor-main.d.ts.map +1 -0
- package/dist/broker/supervisor-main.js +70 -0
- package/dist/broker/supervisor-main.js.map +1 -0
- package/dist/broker/supervisor.d.ts +25 -0
- package/dist/broker/supervisor.d.ts.map +1 -0
- package/dist/broker/supervisor.js +88 -0
- package/dist/broker/supervisor.js.map +1 -0
- package/dist/broker/supervisor.test.d.ts +2 -0
- package/dist/broker/supervisor.test.d.ts.map +1 -0
- package/dist/broker/supervisor.test.js +161 -0
- package/dist/broker/supervisor.test.js.map +1 -0
- package/dist/broker/token-bucket.d.ts +9 -0
- package/dist/broker/token-bucket.d.ts.map +1 -0
- package/dist/broker/token-bucket.js +18 -0
- package/dist/broker/token-bucket.js.map +1 -0
- package/dist/broker/usage-reporter.d.ts +18 -0
- package/dist/broker/usage-reporter.d.ts.map +1 -0
- package/dist/broker/usage-reporter.js +31 -0
- package/dist/broker/usage-reporter.js.map +1 -0
- package/dist/broker/usage-reporter.test.d.ts +2 -0
- package/dist/broker/usage-reporter.test.d.ts.map +1 -0
- package/dist/broker/usage-reporter.test.js +20 -0
- package/dist/broker/usage-reporter.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/hono/agent-session.d.ts +4 -0
- package/dist/hono/agent-session.d.ts.map +1 -1
- package/dist/hono/agent-session.js +28 -2
- package/dist/hono/agent-session.js.map +1 -1
- package/dist/hono/agent-session.test.js +37 -0
- package/dist/hono/agent-session.test.js.map +1 -1
- package/dist/hono/agent-tasks.d.ts.map +1 -1
- package/dist/hono/agent-tasks.js +11 -7
- package/dist/hono/agent-tasks.js.map +1 -1
- package/dist/hono/agent-tasks.test.js +15 -0
- package/dist/hono/agent-tasks.test.js.map +1 -1
- package/dist/hono/agent.d.ts +2 -0
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +19 -2
- package/dist/hono/agent.js.map +1 -1
- package/dist/hono/agent.test.js +8 -2
- package/dist/hono/agent.test.js.map +1 -1
- package/dist/hono/avatar-accent.d.ts +23 -5
- package/dist/hono/avatar-accent.d.ts.map +1 -1
- package/dist/hono/avatar-accent.js +54 -63
- package/dist/hono/avatar-accent.js.map +1 -1
- package/dist/hono/avatar-accent.test.js +64 -35
- package/dist/hono/avatar-accent.test.js.map +1 -1
- package/dist/hono/avatar-casting.d.ts +1 -0
- package/dist/hono/avatar-casting.d.ts.map +1 -1
- package/dist/hono/avatar-casting.js +18 -4
- package/dist/hono/avatar-casting.js.map +1 -1
- package/dist/hono/avatar-casting.test.js +5 -0
- package/dist/hono/avatar-casting.test.js.map +1 -1
- package/dist/hono/avatar-face-question.d.ts +23 -6
- package/dist/hono/avatar-face-question.d.ts.map +1 -1
- package/dist/hono/avatar-face-question.js +48 -59
- package/dist/hono/avatar-face-question.js.map +1 -1
- package/dist/hono/avatar-face-question.test.js +46 -101
- package/dist/hono/avatar-face-question.test.js.map +1 -1
- package/dist/hono/baker-ui-tool-schemas.test.js +30 -4
- package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
- package/dist/hono/blocking-card.d.ts +12 -0
- package/dist/hono/blocking-card.d.ts.map +1 -0
- package/dist/hono/blocking-card.js +12 -0
- package/dist/hono/blocking-card.js.map +1 -0
- package/dist/hono/bridge-job.d.ts +68 -2
- package/dist/hono/bridge-job.d.ts.map +1 -1
- package/dist/hono/bridge-job.js +301 -64
- package/dist/hono/bridge-job.js.map +1 -1
- package/dist/hono/bridge-job.test.js +489 -38
- package/dist/hono/bridge-job.test.js.map +1 -1
- package/dist/hono/check-content.d.ts +49 -0
- package/dist/hono/check-content.d.ts.map +1 -0
- package/dist/hono/check-content.js +182 -0
- package/dist/hono/check-content.js.map +1 -0
- package/dist/hono/check-content.test.d.ts +2 -0
- package/dist/hono/check-content.test.d.ts.map +1 -0
- package/dist/hono/check-content.test.js +87 -0
- package/dist/hono/check-content.test.js.map +1 -0
- package/dist/hono/connection-input.d.ts +170 -12
- package/dist/hono/connection-input.d.ts.map +1 -1
- package/dist/hono/connection-input.js +166 -14
- package/dist/hono/connection-input.js.map +1 -1
- package/dist/hono/connection-input.test.js +112 -9
- package/dist/hono/connection-input.test.js.map +1 -1
- package/dist/hono/convex.d.ts +8 -0
- package/dist/hono/convex.d.ts.map +1 -1
- package/dist/hono/convex.js +8 -0
- package/dist/hono/convex.js.map +1 -1
- package/dist/hono/convex.test.js +4 -1
- package/dist/hono/convex.test.js.map +1 -1
- package/dist/hono/credential-input.d.ts +39 -0
- package/dist/hono/credential-input.d.ts.map +1 -0
- package/dist/hono/credential-input.js +78 -0
- package/dist/hono/credential-input.js.map +1 -0
- package/dist/hono/credential-scrub.d.ts +42 -0
- package/dist/hono/credential-scrub.d.ts.map +1 -0
- package/dist/hono/credential-scrub.js +173 -0
- package/dist/hono/credential-scrub.js.map +1 -0
- package/dist/hono/credential-scrub.test.d.ts +2 -0
- package/dist/hono/credential-scrub.test.d.ts.map +1 -0
- package/dist/hono/credential-scrub.test.js +130 -0
- package/dist/hono/credential-scrub.test.js.map +1 -0
- package/dist/hono/env.d.ts +1 -0
- package/dist/hono/env.d.ts.map +1 -1
- package/dist/hono/env.js +2 -0
- package/dist/hono/env.js.map +1 -1
- package/dist/hono/flow-crypto.d.ts +2 -1
- package/dist/hono/flow-crypto.d.ts.map +1 -1
- package/dist/hono/flow-crypto.js +50 -26
- package/dist/hono/flow-crypto.js.map +1 -1
- package/dist/hono/flow-crypto.test.js +32 -0
- package/dist/hono/flow-crypto.test.js.map +1 -1
- package/dist/hono/flow-input-card-fields.test.d.ts +10 -0
- package/dist/hono/flow-input-card-fields.test.d.ts.map +1 -0
- package/dist/hono/flow-input-card-fields.test.js +26 -0
- package/dist/hono/flow-input-card-fields.test.js.map +1 -0
- package/dist/hono/flow-input-fields.test.js +1 -1
- package/dist/hono/flow-input-fields.test.js.map +1 -1
- package/dist/hono/flow-input.d.ts +23 -2
- package/dist/hono/flow-input.d.ts.map +1 -1
- package/dist/hono/flow-input.js +63 -14
- package/dist/hono/flow-input.js.map +1 -1
- package/dist/hono/flow-input.test.js +67 -11
- package/dist/hono/flow-input.test.js.map +1 -1
- package/dist/hono/jev.d.ts +16 -0
- package/dist/hono/jev.d.ts.map +1 -0
- package/dist/hono/jev.js +60 -0
- package/dist/hono/jev.js.map +1 -0
- package/dist/hono/jev.test.d.ts +2 -0
- package/dist/hono/jev.test.d.ts.map +1 -0
- package/dist/hono/jev.test.js +15 -0
- package/dist/hono/jev.test.js.map +1 -0
- package/dist/hono/knowledge-content.d.ts.map +1 -1
- package/dist/hono/knowledge-content.js +4 -2
- package/dist/hono/knowledge-content.js.map +1 -1
- package/dist/hono/knowledge-content.test.js +6 -5
- package/dist/hono/knowledge-content.test.js.map +1 -1
- package/dist/hono/mcp-servers.d.ts +8 -1
- package/dist/hono/mcp-servers.d.ts.map +1 -1
- package/dist/hono/mcp-servers.js +17 -6
- package/dist/hono/mcp-servers.js.map +1 -1
- package/dist/hono/mcp-servers.test.js +25 -0
- package/dist/hono/mcp-servers.test.js.map +1 -1
- package/dist/hono/model-capabilities.d.ts.map +1 -1
- package/dist/hono/model-capabilities.js +20 -9
- package/dist/hono/model-capabilities.js.map +1 -1
- package/dist/hono/model-capabilities.test.js +16 -8
- package/dist/hono/model-capabilities.test.js.map +1 -1
- package/dist/hono/oom-priority.agent.test.d.ts +2 -0
- package/dist/hono/oom-priority.agent.test.d.ts.map +1 -0
- package/dist/hono/oom-priority.agent.test.js +138 -0
- package/dist/hono/oom-priority.agent.test.js.map +1 -0
- package/dist/hono/oom-priority.d.ts +39 -0
- package/dist/hono/oom-priority.d.ts.map +1 -0
- package/dist/hono/oom-priority.js +138 -0
- package/dist/hono/oom-priority.js.map +1 -0
- package/dist/hono/oom-priority.test.d.ts +2 -0
- package/dist/hono/oom-priority.test.d.ts.map +1 -0
- package/dist/hono/oom-priority.test.js +52 -0
- package/dist/hono/oom-priority.test.js.map +1 -0
- package/dist/hono/openrouter-session.d.ts.map +1 -1
- package/dist/hono/openrouter-session.js +12 -1
- package/dist/hono/openrouter-session.js.map +1 -1
- package/dist/hono/openrouter-session.test.js +9 -3
- package/dist/hono/openrouter-session.test.js.map +1 -1
- package/dist/hono/prompt-context.d.ts +8 -1
- package/dist/hono/prompt-context.d.ts.map +1 -1
- package/dist/hono/prompt-context.js +45 -7
- package/dist/hono/prompt-context.js.map +1 -1
- package/dist/hono/prompt-context.test.js +32 -1
- package/dist/hono/prompt-context.test.js.map +1 -1
- package/dist/hono/publish-recovery.d.ts +1 -1
- package/dist/hono/publish-recovery.d.ts.map +1 -1
- package/dist/hono/publish-recovery.js +9 -6
- package/dist/hono/publish-recovery.js.map +1 -1
- package/dist/hono/publish-recovery.test.js +11 -2
- package/dist/hono/publish-recovery.test.js.map +1 -1
- package/dist/hono/relay.d.ts +10 -1
- package/dist/hono/relay.d.ts.map +1 -1
- package/dist/hono/relay.js +6 -0
- package/dist/hono/relay.js.map +1 -1
- package/dist/hono/sandbox-content.d.ts +2 -0
- package/dist/hono/sandbox-content.d.ts.map +1 -1
- package/dist/hono/sandbox-content.js +8 -4
- package/dist/hono/sandbox-content.js.map +1 -1
- package/dist/hono/sandbox-content.test.js +20 -1
- package/dist/hono/sandbox-content.test.js.map +1 -1
- package/dist/hono/server.d.ts.map +1 -1
- package/dist/hono/server.js +32 -0
- package/dist/hono/server.js.map +1 -1
- package/dist/hono/server.test.js +21 -1
- package/dist/hono/server.test.js.map +1 -1
- package/dist/hono/session-continuity.integration.test.js +10 -1
- package/dist/hono/session-continuity.integration.test.js.map +1 -1
- package/dist/hono/tag-fields.d.ts.map +1 -1
- package/dist/hono/tag-fields.js +2 -0
- package/dist/hono/tag-fields.js.map +1 -1
- package/dist/hono/tag-input.d.ts +124 -3
- package/dist/hono/tag-input.d.ts.map +1 -1
- package/dist/hono/tag-input.js +27 -21
- package/dist/hono/tag-input.js.map +1 -1
- package/dist/hono/tag-input.test.js +19 -1
- package/dist/hono/tag-input.test.js.map +1 -1
- package/dist/hono/tool-mentions.d.ts.map +1 -1
- package/dist/hono/tool-mentions.js +1 -0
- package/dist/hono/tool-mentions.js.map +1 -1
- package/dist/hono/tool-mentions.test.js +5 -0
- package/dist/hono/tool-mentions.test.js.map +1 -1
- package/dist/hono/types.d.ts +8 -0
- package/dist/hono/types.d.ts.map +1 -1
- package/dist/hono/workspace-script.d.ts +10 -0
- package/dist/hono/workspace-script.d.ts.map +1 -0
- package/dist/hono/workspace-script.js +19 -0
- package/dist/hono/workspace-script.js.map +1 -0
- package/dist/hono/workspace-settle.d.ts +20 -0
- package/dist/hono/workspace-settle.d.ts.map +1 -0
- package/dist/hono/workspace-settle.js +50 -0
- package/dist/hono/workspace-settle.js.map +1 -0
- package/dist/review/digest.d.ts +8 -1
- package/dist/review/digest.d.ts.map +1 -1
- package/dist/review/digest.js +10 -43
- package/dist/review/digest.js.map +1 -1
- package/dist/review/digest.test.js +11 -6
- package/dist/review/digest.test.js.map +1 -1
- package/dist/review/evidence-block.js +1 -1
- package/dist/review/evidence-block.js.map +1 -1
- package/dist/review/evidence-block.test.js +0 -10
- package/dist/review/evidence-block.test.js.map +1 -1
- package/dist/review/shell-policy.d.ts +6 -0
- package/dist/review/shell-policy.d.ts.map +1 -1
- package/dist/review/shell-policy.js +240 -72
- package/dist/review/shell-policy.js.map +1 -1
- package/dist/review/shell-policy.test.js +114 -0
- package/dist/review/shell-policy.test.js.map +1 -1
- package/package.json +9 -5
- package/dist/hono/baker-ui-server.test.d.ts +0 -2
- package/dist/hono/baker-ui-server.test.d.ts.map +0 -1
- package/dist/hono/baker-ui-server.test.js +0 -46
- package/dist/hono/baker-ui-server.test.js.map +0 -1
- package/dist/hono/latitude-telemetry.d.ts +0 -17
- package/dist/hono/latitude-telemetry.d.ts.map +0 -1
- package/dist/hono/latitude-telemetry.js +0 -52
- package/dist/hono/latitude-telemetry.js.map +0 -1
- package/dist/hono/latitude-telemetry.test.d.ts +0 -2
- package/dist/hono/latitude-telemetry.test.d.ts.map +0 -1
- package/dist/hono/latitude-telemetry.test.js +0 -18
- package/dist/hono/latitude-telemetry.test.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The broker's local service for the bridge, over a unix socket the agent user
|
|
3
|
+
* can reach: scrub values out of text, and health. It
|
|
4
|
+
* never returns a value — `/scrub` only ever turns values into placeholders.
|
|
5
|
+
*
|
|
6
|
+
* The agent writes the text it asks to have scrubbed, so a scrub is an answer
|
|
7
|
+
* to "is any of these my password?". `/scrub` therefore leaves alone a value a
|
|
8
|
+
* wordlist could reach (`resistsGuessing`); the proxy still scrubs it from
|
|
9
|
+
* what a website sends back, where the agent does not choose the text.
|
|
10
|
+
*/
|
|
11
|
+
import { chmod, mkdir, rm } from "node:fs/promises";
|
|
12
|
+
import { createServer } from "node:http";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { scrub } from "./credential-text.js";
|
|
16
|
+
import { createTokenBucket } from "./token-bucket.js";
|
|
17
|
+
export const MAX_SCRUB_REQUEST_BYTES = 2 * 1024 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* Sized for dozens of agents reading at once, not for one: every agent's tool
|
|
20
|
+
* results share this budget. It only bounds runaway use; what keeps the socket
|
|
21
|
+
* from being an oracle is that it confirms no value a wordlist could reach
|
|
22
|
+
* (`resistsGuessing`). At 256 KB/s a 20-agent run withheld 40% of its results.
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_SCRUB_BYTES = { capacity: 256 * 1024 * 1024, perSecond: 64 * 1024 * 1024 };
|
|
25
|
+
const DEFAULT_REQUESTS_PER_SECOND = 1_000;
|
|
26
|
+
const scrubRequestSchema = z.object({ text: z.string() });
|
|
27
|
+
/** Bits a value must carry — its length times its per-character entropy — before `/scrub` may confirm it. */
|
|
28
|
+
const MIN_SCRUB_BITS = 64;
|
|
29
|
+
function resistsGuessing(value) {
|
|
30
|
+
const counts = new Map();
|
|
31
|
+
for (const char of value)
|
|
32
|
+
counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
33
|
+
let perChar = 0;
|
|
34
|
+
for (const count of counts.values()) {
|
|
35
|
+
const p = count / value.length;
|
|
36
|
+
perChar -= p * Math.log2(p);
|
|
37
|
+
}
|
|
38
|
+
return perChar * value.length >= MIN_SCRUB_BITS;
|
|
39
|
+
}
|
|
40
|
+
class BodyTooLarge extends Error {
|
|
41
|
+
}
|
|
42
|
+
async function readBody(req, limit) {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
let size = 0;
|
|
45
|
+
for await (const chunk of req) {
|
|
46
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
47
|
+
size += buffer.byteLength;
|
|
48
|
+
if (size > limit)
|
|
49
|
+
throw new BodyTooLarge();
|
|
50
|
+
chunks.push(buffer);
|
|
51
|
+
}
|
|
52
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
53
|
+
}
|
|
54
|
+
function parseJson(raw) {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function send(res, status, body) {
|
|
63
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
64
|
+
res.end(JSON.stringify(body));
|
|
65
|
+
}
|
|
66
|
+
export function createSocketService(options) {
|
|
67
|
+
const bucket = createTokenBucket({
|
|
68
|
+
ratePerSecond: options.ratePerSecond ?? DEFAULT_REQUESTS_PER_SECOND,
|
|
69
|
+
burst: options.ratePerSecond ?? DEFAULT_REQUESTS_PER_SECOND,
|
|
70
|
+
});
|
|
71
|
+
const scrubBytes = options.scrubBytes ?? DEFAULT_SCRUB_BYTES;
|
|
72
|
+
const bytes = createTokenBucket({ ratePerSecond: scrubBytes.perSecond, burst: scrubBytes.capacity });
|
|
73
|
+
async function handle(req, res) {
|
|
74
|
+
if (req.method === "GET" && req.url === "/health") {
|
|
75
|
+
return send(res, 200, { ok: true, credentials: options.cache.size() });
|
|
76
|
+
}
|
|
77
|
+
if (req.method !== "POST" || req.url !== "/scrub")
|
|
78
|
+
return send(res, 404, { error: "not found" });
|
|
79
|
+
if (!bucket.take())
|
|
80
|
+
return send(res, 429, { error: "too many scrub requests" });
|
|
81
|
+
const raw = await readBody(req, MAX_SCRUB_REQUEST_BYTES);
|
|
82
|
+
if (!bytes.take(Buffer.byteLength(raw)))
|
|
83
|
+
return send(res, 429, { error: "too much text scrubbed, slow down" });
|
|
84
|
+
const parsed = scrubRequestSchema.safeParse(parseJson(raw));
|
|
85
|
+
if (!parsed.success)
|
|
86
|
+
return send(res, 400, { error: "expected {text}" });
|
|
87
|
+
const credentials = (await options.cache.scrubbable()).filter((credential) => resistsGuessing(credential.value));
|
|
88
|
+
return send(res, 200, { text: scrub(parsed.data.text, credentials) });
|
|
89
|
+
}
|
|
90
|
+
const server = createServer((req, res) => {
|
|
91
|
+
handle(req, res).catch((error) => {
|
|
92
|
+
if (error instanceof BodyTooLarge)
|
|
93
|
+
return send(res, 413, { error: "text too large to scrub" });
|
|
94
|
+
options.log?.(`socket request failed: ${error instanceof Error ? error.name : "unknown"}`);
|
|
95
|
+
send(res, 500, { error: "scrub failed" });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
async listen() {
|
|
100
|
+
await mkdir(dirname(options.socketPath), { recursive: true });
|
|
101
|
+
await rm(options.socketPath, { force: true });
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
server.once("error", reject);
|
|
104
|
+
server.listen(options.socketPath, () => resolve());
|
|
105
|
+
});
|
|
106
|
+
await chmod(options.socketPath, 0o666);
|
|
107
|
+
},
|
|
108
|
+
async close() {
|
|
109
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=socket-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-service.js","sourceRoot":"","sources":["../../src/broker/socket-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-service.test.d.ts","sourceRoot":"","sources":["../../src/broker/socket-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { mkdtempSync } from "node:fs";
|
|
2
|
+
import { request } from "node:http";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { scrubViaBroker } from "./broker-client.js";
|
|
7
|
+
import { createCredentialCache } from "./credential-cache.js";
|
|
8
|
+
import { createSocketService } from "./socket-service.js";
|
|
9
|
+
const VALUE = "shpat_0a1b2c3d4e5f6a7b";
|
|
10
|
+
const WEAK_PASSWORD = ["Hunter", "2024!"].join("");
|
|
11
|
+
let service = null;
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await service?.close();
|
|
14
|
+
service = null;
|
|
15
|
+
});
|
|
16
|
+
async function start(limits = {}) {
|
|
17
|
+
const socketPath = join(mkdtempSync(join(tmpdir(), "broker-")), "broker.sock");
|
|
18
|
+
const cache = createCredentialCache({
|
|
19
|
+
fetchCredentials: () => Promise.resolve([
|
|
20
|
+
{ name: "shop", value: VALUE, allowedHosts: ["acme.myshopify.com"] },
|
|
21
|
+
{ name: "login", value: WEAK_PASSWORD, allowedHosts: ["app.example.com"] },
|
|
22
|
+
]),
|
|
23
|
+
});
|
|
24
|
+
service = createSocketService({ cache, socketPath, ...limits });
|
|
25
|
+
await service.listen();
|
|
26
|
+
return { socketPath };
|
|
27
|
+
}
|
|
28
|
+
function get(socketPath, path) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const req = request({ socketPath, path, method: "GET" }, (res) => {
|
|
31
|
+
let body = "";
|
|
32
|
+
res.setEncoding("utf8");
|
|
33
|
+
res.on("data", (chunk) => {
|
|
34
|
+
body += chunk;
|
|
35
|
+
});
|
|
36
|
+
res.on("end", () => resolve(body));
|
|
37
|
+
});
|
|
38
|
+
req.on("error", reject);
|
|
39
|
+
req.end();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
describe("broker socket service", () => {
|
|
43
|
+
it("scrubs a value back to its placeholder for the bridge", async () => {
|
|
44
|
+
const { socketPath } = await start();
|
|
45
|
+
const out = await scrubViaBroker(`tool said ${VALUE} and ${btoa(VALUE)}`, { socketPath });
|
|
46
|
+
expect(out).toBe("tool said {{credential:shop}} and {{credential:shop}}");
|
|
47
|
+
});
|
|
48
|
+
it("never confirms a guess at a value a wordlist could reach", async () => {
|
|
49
|
+
// The agent writes the text it asks to have scrubbed: scrubbing a guessable
|
|
50
|
+
// password would tell it which of a million guesses was right.
|
|
51
|
+
const { socketPath } = await start();
|
|
52
|
+
const out = await scrubViaBroker(`guesses: password1 ${WEAK_PASSWORD} letmein`, { socketPath });
|
|
53
|
+
expect(out).toBe(`guesses: password1 ${WEAK_PASSWORD} letmein`);
|
|
54
|
+
});
|
|
55
|
+
it("reports how many credentials it holds, never what they are", async () => {
|
|
56
|
+
const { socketPath } = await start();
|
|
57
|
+
await scrubViaBroker("warm the cache", { socketPath });
|
|
58
|
+
const health = await get(socketPath, "/health");
|
|
59
|
+
expect(JSON.parse(health)).toEqual({ ok: true, credentials: 2 });
|
|
60
|
+
expect(health).not.toContain(VALUE);
|
|
61
|
+
});
|
|
62
|
+
it("keeps up with dozens of agents whose tool results all arrive at once", async () => {
|
|
63
|
+
// A 20-researcher run withheld 40% of its results on a 256 KB/s budget, with the broker healthy.
|
|
64
|
+
const { socketPath } = await start();
|
|
65
|
+
const result = `${"r".repeat(1024 * 1024 - 64)} ${VALUE}`;
|
|
66
|
+
const scrubbed = await Promise.all(Array.from({ length: 80 }, () => scrubViaBroker(result, { socketPath, timeoutMs: 30_000 })));
|
|
67
|
+
expect(scrubbed.every((text) => text.endsWith(" {{credential:shop}}"))).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it("answers 429 once a caller has sent more text than its byte budget allows", async () => {
|
|
70
|
+
const { socketPath } = await start({ scrubBytes: { capacity: 100, perSecond: 1 } });
|
|
71
|
+
const text = "x".repeat(60);
|
|
72
|
+
await scrubViaBroker(text, { socketPath });
|
|
73
|
+
await expect(scrubViaBroker(text, { socketPath })).rejects.toThrow("429");
|
|
74
|
+
});
|
|
75
|
+
it("returns the text unchanged when no broker is running", async () => {
|
|
76
|
+
const socketPath = join(mkdtempSync(join(tmpdir(), "broker-")), "missing.sock");
|
|
77
|
+
expect(await scrubViaBroker(`raw ${VALUE}`, { socketPath })).toBe(`raw ${VALUE}`);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=socket-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-service.test.js","sourceRoot":"","sources":["../../src/broker/socket-service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-main.d.ts","sourceRoot":"","sources":["../../src/broker/supervisor-main.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `baker-credential-broker-supervisor` — what Runtime activation starts, as
|
|
4
|
+
* root: it runs `baker-credential-broker` and starts it again whenever it
|
|
5
|
+
* exits or stops answering on its socket (`superviseBroker`). Its command line
|
|
6
|
+
* names the same install as the broker's, so the activation's stop reaches both.
|
|
7
|
+
*/
|
|
8
|
+
import { writeFileSync } from "node:fs";
|
|
9
|
+
import { request } from "node:http";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { spawnBroker } from "./broker-process.js";
|
|
12
|
+
import { brokerChildEnv, parseBrokerEnv } from "./env.js";
|
|
13
|
+
import { superviseBroker } from "./supervisor.js";
|
|
14
|
+
const BROKER_MAIN = fileURLToPath(new URL("./main.js", import.meta.url));
|
|
15
|
+
const HEALTH_TIMEOUT_MS = 2_000;
|
|
16
|
+
function log(message) {
|
|
17
|
+
console.error(`[credential-broker-supervisor] ${message}`);
|
|
18
|
+
}
|
|
19
|
+
// Parsed only for the socket path; the broker it starts inherits this environment and parses it again.
|
|
20
|
+
const parsed = parseBrokerEnv();
|
|
21
|
+
if (!parsed.success) {
|
|
22
|
+
log(`invalid environment: ${parsed.error.issues.map((issue) => issue.path.join(".")).join(", ")}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const SOCKET = parsed.data.BAKER_BROKER_SOCKET;
|
|
26
|
+
/**
|
|
27
|
+
* Out of the OOM killer's reach, and its brokers with it (the setting is
|
|
28
|
+
* inherited). With every process at the same score the kernel kills the
|
|
29
|
+
* biggest, and a busy broker was it: a 20-agent run filled the sandbox's 8 GB
|
|
30
|
+
* with the agent's own commands, the broker died, and the turn could read
|
|
31
|
+
* nothing more. The bridge marks the agent's commands as the ones to kill.
|
|
32
|
+
*/
|
|
33
|
+
try {
|
|
34
|
+
writeFileSync("/proc/self/oom_score_adj", "-1000");
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
log(`could not leave the OOM killer's reach: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Its heap, capped: V8 otherwise lets garbage pile up to hundreds of MB before
|
|
41
|
+
* collecting, and memory the broker holds is memory the agent's commands lack.
|
|
42
|
+
* 40 agents scrubbing at once fit in a 64 MB heap; a 10 MB proxied body needs room too.
|
|
43
|
+
*/
|
|
44
|
+
const BROKER_NODE_OPTIONS = "--max-old-space-size=192";
|
|
45
|
+
function healthy() {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const req = request({ socketPath: SOCKET, path: "/health", method: "GET", timeout: HEALTH_TIMEOUT_MS }, (res) => {
|
|
48
|
+
res.resume();
|
|
49
|
+
resolve(res.statusCode === 200);
|
|
50
|
+
});
|
|
51
|
+
req.on("timeout", () => req.destroy());
|
|
52
|
+
req.on("error", () => resolve(false));
|
|
53
|
+
req.end();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const supervisor = superviseBroker({
|
|
57
|
+
start: () => spawnBroker(process.execPath, [BROKER_MAIN], brokerChildEnv(BROKER_NODE_OPTIONS), log),
|
|
58
|
+
healthy,
|
|
59
|
+
log,
|
|
60
|
+
});
|
|
61
|
+
let stopping = false;
|
|
62
|
+
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
63
|
+
process.on(signal, () => {
|
|
64
|
+
if (stopping)
|
|
65
|
+
return;
|
|
66
|
+
stopping = true;
|
|
67
|
+
void supervisor.stop(signal).then(() => process.exit(0));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=supervisor-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-main.js","sourceRoot":"","sources":["../../src/broker/supervisor-main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps the credential broker running for the life of the Runtime.
|
|
3
|
+
*
|
|
4
|
+
* The bridge withholds every tool result it cannot have scrubbed, and it takes
|
|
5
|
+
* the socket file's existence to mean a broker is running. A broker that dies
|
|
6
|
+
* hard (the kernel's OOM killer, a crash) leaves that file behind, and one that
|
|
7
|
+
* hangs keeps it open; either way, without a supervisor every result is
|
|
8
|
+
* withheld until the next activation — a whole turn.
|
|
9
|
+
*/
|
|
10
|
+
export type BrokerExit = {
|
|
11
|
+
code: number | null;
|
|
12
|
+
signal: NodeJS.Signals | null;
|
|
13
|
+
};
|
|
14
|
+
export type BrokerProcess = {
|
|
15
|
+
exited: Promise<BrokerExit>;
|
|
16
|
+
kill(signal: NodeJS.Signals): void;
|
|
17
|
+
};
|
|
18
|
+
export declare function superviseBroker(options: {
|
|
19
|
+
start: () => BrokerProcess;
|
|
20
|
+
healthy: () => Promise<boolean>;
|
|
21
|
+
log?: (message: string) => void;
|
|
22
|
+
}): {
|
|
23
|
+
stop(signal: NodeJS.Signals): Promise<BrokerExit>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=supervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/broker/supervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAChF,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAkBhG,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,KAAK,EAAE,MAAM,aAAa,CAAC;IAC3B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,GAAG;IAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,CAuExD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const HEALTH_INTERVAL_MS = 5_000;
|
|
2
|
+
/** Consecutive missed health checks before a live broker is taken for hung. */
|
|
3
|
+
const UNHEALTHY_AFTER = 3;
|
|
4
|
+
/** A broker that has never answered gets this long to start (a first start writes its CA). */
|
|
5
|
+
const STARTUP_GRACE_MS = 30_000;
|
|
6
|
+
/** Pauses before each restart in a row; a broker that exits as soon as it starts is retried ever more slowly, never given up on. */
|
|
7
|
+
const RESTART_DELAYS_MS = [250, 1_000, 2_000, 5_000, 10_000, 30_000];
|
|
8
|
+
/** A broker still running this long after the stop signal is killed. */
|
|
9
|
+
const STOP_GRACE_MS = 5_000;
|
|
10
|
+
/** A broker that ran this long was not crash-looping: the next restart is quick again. */
|
|
11
|
+
const STABLE_AFTER_MS = 60_000;
|
|
12
|
+
function describeExit(exit) {
|
|
13
|
+
return exit.signal === null ? `code ${exit.code}` : `signal ${exit.signal}`;
|
|
14
|
+
}
|
|
15
|
+
export function superviseBroker(options) {
|
|
16
|
+
let stopping = false;
|
|
17
|
+
let restarts = 0;
|
|
18
|
+
/** A broker that could not be spawned (ENOMEM, EAGAIN under memory pressure) is one that exited at once. */
|
|
19
|
+
function startOrFail() {
|
|
20
|
+
try {
|
|
21
|
+
return options.start();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
options.log?.(`could not start broker: ${error instanceof Error ? error.message : String(error)}`);
|
|
25
|
+
return { exited: Promise.resolve({ code: null, signal: null }), kill: () => undefined };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function run() {
|
|
29
|
+
const broker = startOrFail();
|
|
30
|
+
const startedAt = Date.now();
|
|
31
|
+
let answered = false;
|
|
32
|
+
let missed = 0;
|
|
33
|
+
let probing = false;
|
|
34
|
+
const watchdog = setInterval(() => {
|
|
35
|
+
if (probing)
|
|
36
|
+
return;
|
|
37
|
+
probing = true;
|
|
38
|
+
void options
|
|
39
|
+
.healthy()
|
|
40
|
+
.catch(() => false)
|
|
41
|
+
.then((ok) => {
|
|
42
|
+
probing = false;
|
|
43
|
+
if (ok) {
|
|
44
|
+
answered = true;
|
|
45
|
+
missed = 0;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!answered && Date.now() - startedAt < STARTUP_GRACE_MS)
|
|
49
|
+
return;
|
|
50
|
+
missed += 1;
|
|
51
|
+
if (missed >= UNHEALTHY_AFTER) {
|
|
52
|
+
options.log?.(`broker missed ${missed} health checks in a row; killing it`);
|
|
53
|
+
broker.kill("SIGKILL");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}, HEALTH_INTERVAL_MS);
|
|
57
|
+
void broker.exited.then((exit) => {
|
|
58
|
+
clearInterval(watchdog);
|
|
59
|
+
if (stopping)
|
|
60
|
+
return;
|
|
61
|
+
const ranMs = Date.now() - startedAt;
|
|
62
|
+
if (ranMs >= STABLE_AFTER_MS)
|
|
63
|
+
restarts = 0;
|
|
64
|
+
const delay = RESTART_DELAYS_MS[Math.min(restarts, RESTART_DELAYS_MS.length - 1)];
|
|
65
|
+
restarts += 1;
|
|
66
|
+
options.log?.(`broker exited (${describeExit(exit)}) after ${Math.round(ranMs / 1000)}s; restarting in ${delay}ms`);
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
if (!stopping)
|
|
69
|
+
current = run();
|
|
70
|
+
}, delay);
|
|
71
|
+
});
|
|
72
|
+
return broker;
|
|
73
|
+
}
|
|
74
|
+
let current = run();
|
|
75
|
+
return {
|
|
76
|
+
stop(signal) {
|
|
77
|
+
stopping = true;
|
|
78
|
+
const broker = current;
|
|
79
|
+
broker.kill(signal);
|
|
80
|
+
const escalate = setTimeout(() => {
|
|
81
|
+
options.log?.(`broker still running ${STOP_GRACE_MS}ms after ${signal}; killing it`);
|
|
82
|
+
broker.kill("SIGKILL");
|
|
83
|
+
}, STOP_GRACE_MS);
|
|
84
|
+
return broker.exited.finally(() => clearTimeout(escalate));
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=supervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../src/broker/supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.test.d.ts","sourceRoot":"","sources":["../../src/broker/supervisor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { superviseBroker } from "./supervisor.js";
|
|
3
|
+
function fakeBroker() {
|
|
4
|
+
const settle = [];
|
|
5
|
+
const exited = new Promise((resolve) => settle.push(resolve));
|
|
6
|
+
const resolveExit = (exit) => settle[0]?.(exit);
|
|
7
|
+
const signals = [];
|
|
8
|
+
return {
|
|
9
|
+
exited,
|
|
10
|
+
signals,
|
|
11
|
+
kill(signal) {
|
|
12
|
+
signals.push(signal);
|
|
13
|
+
resolveExit({ code: null, signal });
|
|
14
|
+
},
|
|
15
|
+
die: (exit) => resolveExit(exit),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function launcher() {
|
|
19
|
+
const started = [];
|
|
20
|
+
return {
|
|
21
|
+
started,
|
|
22
|
+
start: () => {
|
|
23
|
+
const broker = fakeBroker();
|
|
24
|
+
started.push(broker);
|
|
25
|
+
return broker;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.useFakeTimers();
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.useRealTimers();
|
|
34
|
+
});
|
|
35
|
+
describe("superviseBroker", () => {
|
|
36
|
+
it("starts a new broker when the running one is killed", async () => {
|
|
37
|
+
const brokers = launcher();
|
|
38
|
+
superviseBroker({ start: brokers.start, healthy: async () => true });
|
|
39
|
+
brokers.started[0]?.die({ code: null, signal: "SIGKILL" });
|
|
40
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
41
|
+
expect(brokers.started).toHaveLength(2);
|
|
42
|
+
});
|
|
43
|
+
it("kills and replaces a broker that stays alive but stops answering", async () => {
|
|
44
|
+
const brokers = launcher();
|
|
45
|
+
let answering = true;
|
|
46
|
+
superviseBroker({ start: brokers.start, healthy: async () => answering });
|
|
47
|
+
await vi.advanceTimersByTimeAsync(10_000);
|
|
48
|
+
answering = false;
|
|
49
|
+
await vi.advanceTimersByTimeAsync(20_000);
|
|
50
|
+
expect(brokers.started[0]?.signals).toEqual(["SIGKILL"]);
|
|
51
|
+
expect(brokers.started).toHaveLength(2);
|
|
52
|
+
});
|
|
53
|
+
// A first start writes the broker's CA before it can answer.
|
|
54
|
+
it("gives a broker that has never answered 30 seconds to start before taking it for hung", async () => {
|
|
55
|
+
const brokers = launcher();
|
|
56
|
+
superviseBroker({ start: brokers.start, healthy: async () => false });
|
|
57
|
+
await vi.advanceTimersByTimeAsync(29_000);
|
|
58
|
+
const killedDuringStartup = [...(brokers.started[0]?.signals ?? [])];
|
|
59
|
+
await vi.advanceTimersByTimeAsync(20_000);
|
|
60
|
+
expect(killedDuringStartup).toEqual([]);
|
|
61
|
+
expect(brokers.started[0]?.signals).toEqual(["SIGKILL"]);
|
|
62
|
+
});
|
|
63
|
+
it("stops the broker and starts no other once told to stop — an activation's stop revokes its token", async () => {
|
|
64
|
+
const brokers = launcher();
|
|
65
|
+
const supervisor = superviseBroker({ start: brokers.start, healthy: async () => true });
|
|
66
|
+
await supervisor.stop("SIGTERM");
|
|
67
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
68
|
+
expect(brokers.started).toHaveLength(1);
|
|
69
|
+
expect(brokers.started[0]?.signals).toEqual(["SIGTERM"]);
|
|
70
|
+
});
|
|
71
|
+
it("starts no broker when told to stop while waiting to restart one", async () => {
|
|
72
|
+
const brokers = launcher();
|
|
73
|
+
const supervisor = superviseBroker({ start: brokers.start, healthy: async () => true });
|
|
74
|
+
brokers.started[0]?.die({ code: 1, signal: null });
|
|
75
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
76
|
+
await supervisor.stop("SIGTERM");
|
|
77
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
78
|
+
expect(brokers.started).toHaveLength(1);
|
|
79
|
+
});
|
|
80
|
+
// A broker hung in its own shutdown would keep the supervisor, a root process, alive past its token's revocation.
|
|
81
|
+
it("kills a broker that has not stopped five seconds after being told to", async () => {
|
|
82
|
+
const signals = [];
|
|
83
|
+
let settle = () => undefined;
|
|
84
|
+
const exited = new Promise((resolve) => {
|
|
85
|
+
settle = resolve;
|
|
86
|
+
});
|
|
87
|
+
const supervisor = superviseBroker({
|
|
88
|
+
start: () => ({
|
|
89
|
+
exited,
|
|
90
|
+
kill(signal) {
|
|
91
|
+
signals.push(signal);
|
|
92
|
+
if (signal === "SIGKILL")
|
|
93
|
+
settle({ code: null, signal });
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
healthy: async () => true,
|
|
97
|
+
});
|
|
98
|
+
const stopped = supervisor.stop("SIGTERM");
|
|
99
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
100
|
+
await expect(stopped).resolves.toEqual({ code: null, signal: "SIGKILL" });
|
|
101
|
+
expect(signals).toEqual(["SIGTERM", "SIGKILL"]);
|
|
102
|
+
});
|
|
103
|
+
it("backs off from a broker that exits as soon as it starts, and keeps trying", async () => {
|
|
104
|
+
const started = [];
|
|
105
|
+
superviseBroker({
|
|
106
|
+
start: () => {
|
|
107
|
+
const broker = fakeBroker();
|
|
108
|
+
started.push(broker);
|
|
109
|
+
broker.die({ code: 1, signal: null });
|
|
110
|
+
return broker;
|
|
111
|
+
},
|
|
112
|
+
healthy: async () => false,
|
|
113
|
+
});
|
|
114
|
+
await vi.advanceTimersByTimeAsync(120_000);
|
|
115
|
+
const afterTwoMinutes = started.length;
|
|
116
|
+
await vi.advanceTimersByTimeAsync(120_000);
|
|
117
|
+
expect(afterTwoMinutes).toBeLessThan(15);
|
|
118
|
+
expect(started.length).toBeGreaterThan(afterTwoMinutes);
|
|
119
|
+
});
|
|
120
|
+
// Memory pressure, the case the supervisor exists for, is when a spawn fails.
|
|
121
|
+
it("retries a broker that could not even be started", async () => {
|
|
122
|
+
const brokers = launcher();
|
|
123
|
+
let attempts = 0;
|
|
124
|
+
const log = [];
|
|
125
|
+
superviseBroker({
|
|
126
|
+
start: () => {
|
|
127
|
+
attempts += 1;
|
|
128
|
+
if (attempts === 1)
|
|
129
|
+
throw new Error("spawn ENOMEM");
|
|
130
|
+
return brokers.start();
|
|
131
|
+
},
|
|
132
|
+
healthy: async () => true,
|
|
133
|
+
log: (line) => log.push(line),
|
|
134
|
+
});
|
|
135
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
136
|
+
expect(brokers.started).toHaveLength(1);
|
|
137
|
+
expect(log[0]).toContain("spawn ENOMEM");
|
|
138
|
+
});
|
|
139
|
+
it("restarts quickly again after a broker that ran for a minute, however slow the restarts before it", async () => {
|
|
140
|
+
const brokers = launcher();
|
|
141
|
+
const log = [];
|
|
142
|
+
superviseBroker({ start: brokers.start, healthy: async () => true, log: (line) => log.push(line) });
|
|
143
|
+
for (const index of [0, 1, 2]) {
|
|
144
|
+
brokers.started[index]?.die({ code: 1, signal: null });
|
|
145
|
+
await vi.advanceTimersByTimeAsync(3_000);
|
|
146
|
+
}
|
|
147
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
148
|
+
brokers.started[3]?.die({ code: null, signal: "SIGKILL" });
|
|
149
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
150
|
+
expect(log.map((line) => line.split("restarting in ")[1])).toEqual(["250ms", "1000ms", "2000ms", "250ms"]);
|
|
151
|
+
});
|
|
152
|
+
it("logs how each broker ended and when the next one starts", async () => {
|
|
153
|
+
const brokers = launcher();
|
|
154
|
+
const log = [];
|
|
155
|
+
superviseBroker({ start: brokers.start, healthy: async () => true, log: (line) => log.push(line) });
|
|
156
|
+
brokers.started[0]?.die({ code: null, signal: "SIGKILL" });
|
|
157
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
158
|
+
expect(log).toEqual(["broker exited (signal SIGKILL) after 0s; restarting in 250ms"]);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=supervisor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.test.js","sourceRoot":"","sources":["../../src/broker/supervisor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** A token bucket: up to `burst` tokens at once, refilled at `ratePerSecond`; a take may cost more than one. */
|
|
2
|
+
export declare function createTokenBucket(options: {
|
|
3
|
+
ratePerSecond: number;
|
|
4
|
+
burst: number;
|
|
5
|
+
now?: () => number;
|
|
6
|
+
}): {
|
|
7
|
+
take(cost?: number): boolean;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=token-bucket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-bucket.d.ts","sourceRoot":"","sources":["../../src/broker/token-bucket.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAE;IAKnG,IAAI,iBAAY,OAAO;EAS1B"}
|