@koda-sl/baker-bridge 0.122.0-dev.3613db443 → 0.122.0-dev.42b55fa66

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.
Files changed (146) hide show
  1. package/README.md +27 -4
  2. package/dist/broker/baker-api.d.ts +23 -0
  3. package/dist/broker/baker-api.d.ts.map +1 -0
  4. package/dist/broker/baker-api.js +55 -0
  5. package/dist/broker/baker-api.js.map +1 -0
  6. package/dist/broker/broker-client.d.ts +10 -0
  7. package/dist/broker/broker-client.d.ts.map +1 -0
  8. package/dist/broker/broker-client.js +60 -0
  9. package/dist/broker/broker-client.js.map +1 -0
  10. package/dist/broker/ca.d.ts +38 -0
  11. package/dist/broker/ca.d.ts.map +1 -0
  12. package/dist/broker/ca.js +80 -0
  13. package/dist/broker/ca.js.map +1 -0
  14. package/dist/broker/ca.test.d.ts +2 -0
  15. package/dist/broker/ca.test.d.ts.map +1 -0
  16. package/dist/broker/ca.test.js +58 -0
  17. package/dist/broker/ca.test.js.map +1 -0
  18. package/dist/broker/credential-cache.d.ts +24 -0
  19. package/dist/broker/credential-cache.d.ts.map +1 -0
  20. package/dist/broker/credential-cache.js +69 -0
  21. package/dist/broker/credential-cache.js.map +1 -0
  22. package/dist/broker/credential-cache.test.d.ts +2 -0
  23. package/dist/broker/credential-cache.test.d.ts.map +1 -0
  24. package/dist/broker/credential-cache.test.js +120 -0
  25. package/dist/broker/credential-cache.test.js.map +1 -0
  26. package/dist/broker/credential-text.d.ts +45 -0
  27. package/dist/broker/credential-text.d.ts.map +1 -0
  28. package/dist/broker/credential-text.js +103 -0
  29. package/dist/broker/credential-text.js.map +1 -0
  30. package/dist/broker/credential-text.test.d.ts +2 -0
  31. package/dist/broker/credential-text.test.d.ts.map +1 -0
  32. package/dist/broker/credential-text.test.js +54 -0
  33. package/dist/broker/credential-text.test.js.map +1 -0
  34. package/dist/broker/env.d.ts +19 -0
  35. package/dist/broker/env.d.ts.map +1 -0
  36. package/dist/broker/env.js +16 -0
  37. package/dist/broker/env.js.map +1 -0
  38. package/dist/broker/main.d.ts +3 -0
  39. package/dist/broker/main.d.ts.map +1 -0
  40. package/dist/broker/main.js +51 -0
  41. package/dist/broker/main.js.map +1 -0
  42. package/dist/broker/proxy.d.ts +24 -0
  43. package/dist/broker/proxy.d.ts.map +1 -0
  44. package/dist/broker/proxy.integration.test.d.ts +2 -0
  45. package/dist/broker/proxy.integration.test.d.ts.map +1 -0
  46. package/dist/broker/proxy.integration.test.js +201 -0
  47. package/dist/broker/proxy.integration.test.js.map +1 -0
  48. package/dist/broker/proxy.js +147 -0
  49. package/dist/broker/proxy.js.map +1 -0
  50. package/dist/broker/rewrite.d.ts +44 -0
  51. package/dist/broker/rewrite.d.ts.map +1 -0
  52. package/dist/broker/rewrite.js +291 -0
  53. package/dist/broker/rewrite.js.map +1 -0
  54. package/dist/broker/rewrite.test.d.ts +2 -0
  55. package/dist/broker/rewrite.test.d.ts.map +1 -0
  56. package/dist/broker/rewrite.test.js +171 -0
  57. package/dist/broker/rewrite.test.js.map +1 -0
  58. package/dist/broker/socket-service.d.ts +18 -0
  59. package/dist/broker/socket-service.d.ts.map +1 -0
  60. package/dist/broker/socket-service.js +103 -0
  61. package/dist/broker/socket-service.js.map +1 -0
  62. package/dist/broker/socket-service.test.d.ts +2 -0
  63. package/dist/broker/socket-service.test.d.ts.map +1 -0
  64. package/dist/broker/socket-service.test.js +73 -0
  65. package/dist/broker/socket-service.test.js.map +1 -0
  66. package/dist/broker/token-bucket.d.ts +9 -0
  67. package/dist/broker/token-bucket.d.ts.map +1 -0
  68. package/dist/broker/token-bucket.js +18 -0
  69. package/dist/broker/token-bucket.js.map +1 -0
  70. package/dist/broker/usage-reporter.d.ts +18 -0
  71. package/dist/broker/usage-reporter.d.ts.map +1 -0
  72. package/dist/broker/usage-reporter.js +31 -0
  73. package/dist/broker/usage-reporter.js.map +1 -0
  74. package/dist/broker/usage-reporter.test.d.ts +2 -0
  75. package/dist/broker/usage-reporter.test.d.ts.map +1 -0
  76. package/dist/broker/usage-reporter.test.js +20 -0
  77. package/dist/broker/usage-reporter.test.js.map +1 -0
  78. package/dist/hono/agent-session.d.ts +3 -0
  79. package/dist/hono/agent-session.d.ts.map +1 -1
  80. package/dist/hono/agent-session.js +15 -0
  81. package/dist/hono/agent-session.js.map +1 -1
  82. package/dist/hono/agent.d.ts +2 -0
  83. package/dist/hono/agent.d.ts.map +1 -1
  84. package/dist/hono/agent.js +10 -2
  85. package/dist/hono/agent.js.map +1 -1
  86. package/dist/hono/avatar-casting.d.ts +1 -0
  87. package/dist/hono/avatar-casting.d.ts.map +1 -1
  88. package/dist/hono/avatar-casting.js +15 -2
  89. package/dist/hono/avatar-casting.js.map +1 -1
  90. package/dist/hono/avatar-casting.test.js +5 -0
  91. package/dist/hono/avatar-casting.test.js.map +1 -1
  92. package/dist/hono/baker-ui-tool-schemas.test.js +6 -0
  93. package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
  94. package/dist/hono/bridge-job.d.ts +15 -1
  95. package/dist/hono/bridge-job.d.ts.map +1 -1
  96. package/dist/hono/bridge-job.js +85 -9
  97. package/dist/hono/bridge-job.js.map +1 -1
  98. package/dist/hono/bridge-job.test.js +145 -1
  99. package/dist/hono/bridge-job.test.js.map +1 -1
  100. package/dist/hono/connection-input.d.ts +11 -1
  101. package/dist/hono/connection-input.d.ts.map +1 -1
  102. package/dist/hono/connection-input.js +30 -5
  103. package/dist/hono/connection-input.js.map +1 -1
  104. package/dist/hono/connection-input.test.js +28 -8
  105. package/dist/hono/connection-input.test.js.map +1 -1
  106. package/dist/hono/convex.d.ts +8 -0
  107. package/dist/hono/convex.d.ts.map +1 -1
  108. package/dist/hono/convex.js +8 -0
  109. package/dist/hono/convex.js.map +1 -1
  110. package/dist/hono/credential-input.d.ts +39 -0
  111. package/dist/hono/credential-input.d.ts.map +1 -0
  112. package/dist/hono/credential-input.js +78 -0
  113. package/dist/hono/credential-input.js.map +1 -0
  114. package/dist/hono/credential-scrub.d.ts +41 -0
  115. package/dist/hono/credential-scrub.d.ts.map +1 -0
  116. package/dist/hono/credential-scrub.js +152 -0
  117. package/dist/hono/credential-scrub.js.map +1 -0
  118. package/dist/hono/credential-scrub.test.d.ts +2 -0
  119. package/dist/hono/credential-scrub.test.d.ts.map +1 -0
  120. package/dist/hono/credential-scrub.test.js +105 -0
  121. package/dist/hono/credential-scrub.test.js.map +1 -0
  122. package/dist/hono/latitude-telemetry.d.ts.map +1 -1
  123. package/dist/hono/latitude-telemetry.js +8 -2
  124. package/dist/hono/latitude-telemetry.js.map +1 -1
  125. package/dist/hono/prompt-context.d.ts +8 -1
  126. package/dist/hono/prompt-context.d.ts.map +1 -1
  127. package/dist/hono/prompt-context.js +45 -7
  128. package/dist/hono/prompt-context.js.map +1 -1
  129. package/dist/hono/prompt-context.test.js +35 -1
  130. package/dist/hono/prompt-context.test.js.map +1 -1
  131. package/dist/hono/relay.d.ts +10 -1
  132. package/dist/hono/relay.d.ts.map +1 -1
  133. package/dist/hono/relay.js +6 -0
  134. package/dist/hono/relay.js.map +1 -1
  135. package/dist/hono/server.d.ts.map +1 -1
  136. package/dist/hono/server.js +22 -0
  137. package/dist/hono/server.js.map +1 -1
  138. package/dist/hono/tag-input.d.ts +106 -3
  139. package/dist/hono/tag-input.d.ts.map +1 -1
  140. package/dist/hono/tag-input.js +20 -9
  141. package/dist/hono/tag-input.js.map +1 -1
  142. package/dist/hono/tag-input.test.js +19 -1
  143. package/dist/hono/tag-input.test.js.map +1 -1
  144. package/dist/hono/types.d.ts +8 -0
  145. package/dist/hono/types.d.ts.map +1 -1
  146. package/package.json +5 -2
package/README.md CHANGED
@@ -51,10 +51,27 @@ await server.start();
51
51
  | `NO_COLOR` | Set to `1` on every Agent SDK subprocess so `baker --help` output carries no ANSI escapes — see [Session surface](#session-surface) |
52
52
  | `BAKER_AGENT_MODEL` | Optional. Runs every turn in this runtime on the named model instead of the default, and is forwarded to subagents as `CLAUDE_CODE_SUBAGENT_MODEL`. Ignored unless it names a model with a known context window — see [Model selection](#model-selection) |
53
53
  | `CLAUDE_CODE_SUBAGENT_MODEL`, `CLAUDE_CODE_SUBAGENT_MODEL_FORCE` | Set on the Agent SDK subprocess only when `BAKER_AGENT_MODEL` is set. Together they override the `model:` each subagent pins in its own frontmatter |
54
+ | `BAKER_CREDENTIALS` | Set to `1` in a Runtime with Credentials (the credential broker is running). Only then is `request_credential` offered to the agent; otherwise a call to it is refused |
54
55
  | `LATITUDE_API_KEY` / `LATITUDE_PROJECT` | Optional, both or neither. When set, every turn is traced to that Latitude project — see [Latitude tracing](#latitude-tracing) |
55
56
 
56
57
  All variables are validated at startup via `@t3-oss/env-core` + `zod`.
57
58
 
59
+ ## Credential broker (`baker-credential-broker`)
60
+
61
+ A second bin, run as root in the sandbox before the agent starts, so the agent can use the user's stored credentials without ever seeing them. The agent writes `{{credential:NAME}}` in a request; the broker is an HTTP(S) proxy on `127.0.0.1` that swaps in the real value and swaps it back out of the response.
62
+
63
+ | Variable | Description |
64
+ |---|---|
65
+ | `CREDENTIAL_BROKER_TOKEN` | Required. Authenticates the broker to Baker's credential routes |
66
+ | `BAKER_CONVEX_SITE_URL` | Required. Baker's site URL |
67
+ | `BAKER_CREDENTIAL_PROXY_PORT` | Proxy port (default `8877`) |
68
+ | `BAKER_CA_DIR` | Where the broker's CA key (0600) and cert live (default `/etc/baker-ca`); run as root it also installs the cert into the system bundle |
69
+ | `BAKER_BROKER_SOCKET` | Unix socket for the bridge (default `/run/baker/broker.sock`): `POST /scrub`, `GET /health` |
70
+
71
+ The agent's processes get `agentProxyEnv()`: `HTTP(S)_PROXY` pointing at the broker, `NO_PROXY` for loopback plus the model gateway, `NODE_USE_ENV_PROXY=1`, `NODE_EXTRA_CA_CERTS` set to the broker's CA, and `REQUESTS_CA_BUNDLE` / `SSL_CERT_FILE` / `CURL_CA_BUNDLE` set to the system bundle.
72
+
73
+ Security model: a value is only substituted over https, and only for a host the user allowed for that credential. A placeholder in the URL or a header that is not allowed for the host, or that names no stored credential, is refused (403 / 404, or 503 while credentials can't be fetched) before anything leaves the sandbox. A placeholder that appears only in the body is left as plain text unless it names a credential allowed for that host. Response headers and text bodies up to 10 MB are scrubbed back to placeholders; those responses arrive buffered rather than streamed. The socket never returns a value.
74
+
58
75
  ## Session surface
59
76
 
60
77
  The Agent SDK ships developer-facing surfaces a marketer's session can never
@@ -240,7 +257,8 @@ its tokens, every tool call with its result, subagents and auto-memory. Each
240
257
  trace carries the workspace's git repo (`metadata.git.repo`, the company) and
241
258
  branch (`metadata.git.branch`, the chat); the Latitude session is the Claude
242
259
  session. Session reviews are not traced. Without both
243
- variables nothing is registered. Content is sent in full; mask attributes with
260
+ variables nothing is registered, and Baker's backend does not set them today:
261
+ the transcript may hold a secret the credential scrub did not rewrite. Content is sent in full; mask attributes with
244
262
  `LATITUDE_REDACT_ATTRIBUTES` if needed.
245
263
 
246
264
  ## Endpoints
@@ -276,7 +294,7 @@ A ChatGPT Ads tag pre-fills its Pixel ID and requests the optional Conversions A
276
294
 
277
295
  Its second tool, `request_flow_input`, is the **blocking form-configuration form**: the agent invokes it to configure the confidential parts of a flow ("Form") it cannot author itself — side-effect credentials/connections (`encryptedConfig`, `oauthProviderId`) and widget third-party field definitions (`nodeData.form.external` for HubSpot/Calendly/HighLevel/SavvyCal). The agent authors the surrounding structure in `_data.json` and references it by `nodeId`/`sideEffectId`. Same `canUseTool` park pattern: the bridge POSTs `/api/chat/flow-input-request`, the dashboard renders a tabbed form (secret inputs / OAuth connect+account picker / live resource picker), and on submit Convex delivers the resolved values to `POST /flow-input-decision`. Unlike tags, the resolved values are written **into the branch flow file** through the encrypting `writeFlow` (read → patch node/side-effect → write); the model only ever sees a redacted result: `{ status, nodeId, sideEffectId?, secretFieldsSet, destination?, connected?, fields?, note? }`. `destination` is the non-secret half of what the user chose — the spreadsheet, the sheet, the portal, the Calendly event — so an agent that has just configured an integration does not have to spend a turn on `baker flows show --full` to learn what it configured, and does not map columns against a guess. For a widget node, `fields` carries the autofill keys the picked resource accepts — literally the left-hand side of an `autofilledFields` entry, adapter prefix included, so a SavvyCal custom field arrives as `questions.<id>` because that is what its adapter reads. They come from each provider's own field container (`fieldGroups[].fields[]`, `customQuestions[]`, `customParams.formFields[]`, `customFormFields[]`, `fields[]`, `customFields[]`) rather than from a search for name-shaped strings, so a select's option labels and the resource's own title are not among them (a HubSpot form's `name` is "Contact us"; it is reported under `destination`). A widget with no field list of its own returns none rather than a guess. Both targets go through the same filter: only plain strings and numbers are echoed, and anything in that type's secret list is redacted to a name in `secretFieldsSet`. A few non-secret keys are withheld from `destination` on top of that, because they are not an answer either — an `httpWebhook`'s basic-auth `username` is not a typed credential, so it is not in the secret list, but echoing it hands the model half a credential and nothing on the side effect is addressed by it. Leaked secret values in `prefilledConfig` are stripped bridge-side (and again backend-side). Live wait matches `AskUserQuestion` (15 min); like all blocking prompts, the form itself never expires — the thread parks and a later submission recovers the turn.
278
296
 
279
- Its third tool, `request_connection`, is the **blocking connection request**: the agent invokes it when a data platform the task needs — Google Ads, LinkedIn Ads, Meta Ads, X Ads, Google Analytics 4, Google Search Console, or Google Tag Manager — is not connected yet, instead of telling the user to go to Settings. Same `canUseTool` park pattern: the bridge POSTs `/api/chat/connection-input-request`, the dashboard renders one tab per platform with the OAuth connect button and the account/property/site/container picker, and on submit Convex delivers the outcome to `POST /connection-input-decision`. Nothing confidential passes through the bridge or the model: the dashboard writes the selection onto the company's connection, and the resolution carries only `{ status, platform, accountLabel?, selected[] }`. A Google Drive tab may add `choose: "media-folder"` (kept only on Google Drive): the user picks a folder that Baker imports as a media source, and the connected result also carries its `mediaSourceKey`. Live wait matches `AskUserQuestion` (15 min).
297
+ Its third tool, `request_connection`, is the **blocking connection request**: the agent invokes it when a data platform the task needs — Google Ads, LinkedIn Ads, Meta Ads, X Ads, Google Analytics 4, Google Search Console, or Google Tag Manager — is not connected yet, instead of telling the user to go to Settings. Same `canUseTool` park pattern: the bridge POSTs `/api/chat/connection-input-request`, the dashboard renders one tab per platform with the OAuth connect button and the account/property/site/container picker, and on submit Convex delivers the outcome to `POST /connection-input-decision`. Nothing confidential passes through the bridge or the model: the dashboard writes the selection onto the company's connection, and the resolution carries only `{ status, platform, accountLabel?, selected[] }`. A Google Drive tab may add `choose: "media-folder"` (kept only on Google Drive): the user picks a folder that Baker imports as a media source, and the connected result also carries its `mediaSourceKey`. An ad or data platform tab may add `pick: true` (kept only on platforms with accounts to choose): when the platform is already connected, the card opens its account picker straight away, for the agent that found the account it needs is not selected. Live wait matches `AskUserQuestion` (15 min).
280
298
 
281
299
  The same card adds tools. A `{ server: { name, url, auth?, keyHeader?, scope? } }` entry opens a remote MCP server filled in from the user's instructions (`auth`: `none` \| `key` \| `sign-in`; the key is typed into the card, never into the chat), and an `{ app }` entry connects a managed app from `baker mcp catalog` — the fallback for a vendor with no server of its own. When an app or server comes through, the bridge fetches the turn's MCP servers afresh (`McpServersProvider` with `{ fresh: true }`) and loads them into the running query with `setMcpServers` — `baker_ui` included, since the SDK drops any in-process server left out — before the tool answers, and adds `toolsReady` to the resolution. The provider keeps that set for the next turn, so the tool list changes once. A platform-only resolution never reloads.
282
300
 
@@ -284,6 +302,8 @@ Its fourth tool, `request_avatar_casting`, is the **blocking face picker**: an a
284
302
 
285
303
  It is also the one blocking tool the bridge opens **on its own**. The agent forgot to call it in every recorded run, in a new way each time, so the trigger is the EVENT rather than the prose around it: a `baker avatars create` for a synthetic avatar registers a pending casting, and it opens before whatever the agent does next. The create is read off the **assistant message** as well as off `canUseTool`, because `canUseTool` is raised only for the ask path and the scaffold allows `Bash(baker:*)` — so every create the product actually runs bypassed the permission callback entirely, and the avatar sat in `generating` with no portraits and no sheet while the agent reported the picker was open. When the create is the turn's last tool call there is no next call to open it before, so the request is posted at turn end and the thread parks on it. An unattended Session has nobody to ask, so the create is rewritten to `--face auto` instead and nothing is left pending — the flag is inserted right after `avatars create` rather than appended, so a piped or redirected command does not hand it to the next program, and an explicit `--face ask` is replaced rather than joined.
286
304
 
305
+ The picker does not wait forever. Once every face is on screen, Convex schedules a pick 30 seconds out: if the user has not chosen (or pressed "Give me more time", which clears the deadline), the first face is kept and delivered as `{ status: "cast", avatarHandle, imageId, chosenBy: "auto" }`. The pick runs server-side, so a tab left in the background never stalls the job, and the result text tells the agent nobody chose that face so it never credits the user with it.
306
+
287
307
  A decline comes back to the agent with the remedy attached, not as a bare `{"status":"declined"}`. The tool's description says to sharpen the subject with `baker avatars update` and call it again, but that text sits thousands of tokens up the context and was written before the turn began; read against the bare object, the agent replied "the casting portraits should be showing on your screen right now" to a user who had just rejected all four and whose picker was gone. The result now states the outcome and the next move together, the way a CLI `error.fix` does, and says to ask what to change when no reason was given.
288
308
 
289
309
  When the agent *does* call `request_avatar_casting` for the avatar a pending casting is already owed to — the tool's description says the picker opens by itself, but the call is tolerated — that call becomes the casting rather than triggering a second one, so the pick is returned to the tool that asked for it. Every other blocking prompt that follows a create still reaches its own handler; the auto-cast runs first, then the original call is routed normally.
@@ -775,8 +795,11 @@ or path is data, not a second command; anything it cannot parse is refused.
775
795
  ends input itself once the turn is really over: a clean result with no
776
796
  in-flight tasks, re-checked when the last task settles (a settle isn't
777
797
  guaranteed to wake the model into a second result). Turns that ran tasks get
778
- a 1s settle window so a just-settled task's queued wake isn't cut off; only
779
- genuine model activity cancels it. Model activity after a result also retires
798
+ a 1s settle window after a result so a just-settled task's queued wake isn't
799
+ cut off, and a 45s window after the last task settles, because the CLI streams
800
+ nothing between that settle and the woken turn's first token; only genuine
801
+ model activity cancels either. The agent's Bash calls may run 30 minutes
802
+ (`BASH_MAX_TIMEOUT_MS`), the longest the workspace's timeout hook grants. Model activity after a result also retires
780
803
  that result: the CLI has woken into a new turn, so the settle re-check waits
781
804
  for the woken turn's own result rather than closing stdin under a model that
782
805
  is still on time-to-first-token. A 60s exit guard closes the query if the
@@ -0,0 +1,23 @@
1
+ import type { ResolvedCredential } from "./credential-text.ts";
2
+ export type CredentialUse = {
3
+ name: string;
4
+ host: string;
5
+ };
6
+ /**
7
+ * Baker refused the broker's token: Credentials were switched off, the company
8
+ * suspended, or the Runtime is no longer active. Unlike a failed request, this
9
+ * is an answer — the broker must stop using what it holds.
10
+ */
11
+ export declare class CredentialsRefused extends Error {
12
+ readonly status: number;
13
+ constructor(status: number);
14
+ }
15
+ export type BakerBrokerApi = {
16
+ fetchCredentials(): Promise<ResolvedCredential[]>;
17
+ reportUsage(uses: CredentialUse[]): Promise<void>;
18
+ };
19
+ export declare function createBakerBrokerApi(options: {
20
+ siteUrl: string;
21
+ token: string;
22
+ }): BakerBrokerApi;
23
+ //# sourceMappingURL=baker-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baker-api.d.ts","sourceRoot":"","sources":["../../src/broker/baker-api.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS/D,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAnC,YAAqB,MAAM,EAAE,MAAM,EAElC;CACF;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAClD,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAIF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CA0BhG"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The broker's two calls to Baker: fetch the company's credentials (the only
3
+ * route that ever returns a value) and report which were used where. The
4
+ * shapes mirror `brokerResolveResponseSchema` / `brokerUsageRequestSchema` in
5
+ * `@baker/api/credentials`, which the published bridge cannot import.
6
+ * Success is `{ ok: true, data }`; a failure is any non-2xx status with
7
+ * `{ code, error }`.
8
+ */
9
+ import { z } from "zod";
10
+ const resolveResponseSchema = z.object({
11
+ credentials: z.array(z.object({ name: z.string(), value: z.string(), allowedHosts: z.array(z.string()) })),
12
+ });
13
+ const successSchema = z.object({ ok: z.literal(true), data: z.unknown().optional() });
14
+ const failureSchema = z.object({ code: z.string() });
15
+ /**
16
+ * Baker refused the broker's token: Credentials were switched off, the company
17
+ * suspended, or the Runtime is no longer active. Unlike a failed request, this
18
+ * is an answer — the broker must stop using what it holds.
19
+ */
20
+ export class CredentialsRefused extends Error {
21
+ status;
22
+ constructor(status) {
23
+ super(`Baker refused the broker (${status})`);
24
+ this.status = status;
25
+ }
26
+ }
27
+ const TIMEOUT_MS = 10_000;
28
+ export function createBakerBrokerApi(options) {
29
+ async function post(path, body) {
30
+ const response = await fetch(new URL(path, options.siteUrl), {
31
+ method: "POST",
32
+ headers: { authorization: `Broker ${options.token}`, "content-type": "application/json" },
33
+ body: JSON.stringify(body),
34
+ signal: AbortSignal.timeout(TIMEOUT_MS),
35
+ });
36
+ const payload = await response.json().catch(() => null);
37
+ if (response.status === 401 || response.status === 403)
38
+ throw new CredentialsRefused(response.status);
39
+ if (!response.ok) {
40
+ const code = failureSchema.safeParse(payload).data?.code ?? "UNKNOWN";
41
+ throw new Error(`${path} answered ${response.status} ${code}`);
42
+ }
43
+ return successSchema.parse(payload).data;
44
+ }
45
+ return {
46
+ async fetchCredentials() {
47
+ const data = resolveResponseSchema.parse(await post("/api/runtime/credentials/resolve", {}));
48
+ return data.credentials;
49
+ },
50
+ async reportUsage(uses) {
51
+ await post("/api/runtime/credentials/usage", { uses });
52
+ },
53
+ };
54
+ }
55
+ //# sourceMappingURL=baker-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baker-api.js","sourceRoot":"","sources":["../../src/broker/baker-api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;CAC3G,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACtF,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAIrD;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACtB,MAAM;IAA3B,YAAqB,MAAc;QACjC,KAAK,CAAC,6BAA6B,MAAM,GAAG,CAAC,CAAC;sBAD3B,MAAM;IAE3B,CAAC;CACF;AAOD,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,MAAM,UAAU,oBAAoB,CAAC,OAA2C;IAC9E,KAAK,UAAU,IAAI,CAAC,IAAY,EAAE,IAAa;QAC7C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YAC3D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YACzF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QACH,MAAM,OAAO,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACjE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,aAAa,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,KAAK,CAAC,gBAAgB;YACpB,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7F,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,IAAI;YACpB,MAAM,IAAI,CAAC,gCAAgC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const DEFAULT_BROKER_SOCKET = "/run/baker/broker.sock";
2
+ export type BrokerClientOptions = {
3
+ socketPath?: string;
4
+ timeoutMs?: number;
5
+ };
6
+ /** Whether a broker runs in this sandbox: its socket exists. */
7
+ export declare function brokerRunning(options?: BrokerClientOptions): boolean;
8
+ /** `text` with every stored credential value replaced by its placeholder. */
9
+ export declare function scrubViaBroker(text: string, options?: BrokerClientOptions): Promise<string>;
10
+ //# sourceMappingURL=broker-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker-client.d.ts","sourceRoot":"","sources":["../../src/broker/broker-client.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAG9D,MAAM,MAAM,mBAAmB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAM9E,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAExE;AA+BD,6EAA6E;AAC7E,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQrG"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The bridge's side of the broker socket. Dependency-free on purpose: the
3
+ * bridge imports it on the hot path of every tool result.
4
+ *
5
+ * When no broker runs (no socket file — local dev, or a Runtime without
6
+ * credentials) every call is a fast no-op and `scrubViaBroker` returns the
7
+ * text unchanged. Any other failure (timeout, 413, 429, a crashed broker) is
8
+ * thrown so the caller decides whether to hold the text back.
9
+ */
10
+ import { existsSync } from "node:fs";
11
+ import { request } from "node:http";
12
+ export const DEFAULT_BROKER_SOCKET = "/run/baker/broker.sock";
13
+ const DEFAULT_TIMEOUT_MS = 2_000;
14
+ function isMissingSocket(error) {
15
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
16
+ }
17
+ /** Whether a broker runs in this sandbox: its socket exists. */
18
+ export function brokerRunning(options = {}) {
19
+ return existsSync(options.socketPath ?? DEFAULT_BROKER_SOCKET);
20
+ }
21
+ function call(path, body, options) {
22
+ const socketPath = options.socketPath ?? DEFAULT_BROKER_SOCKET;
23
+ if (!brokerRunning(options))
24
+ return Promise.resolve(null);
25
+ return new Promise((resolve, reject) => {
26
+ const req = request({
27
+ socketPath,
28
+ path,
29
+ method: "POST",
30
+ headers: { "content-type": "application/json", "content-length": Buffer.byteLength(body) },
31
+ timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
32
+ }, (res) => {
33
+ const chunks = [];
34
+ res.on("data", (chunk) => chunks.push(chunk));
35
+ res.on("end", () => {
36
+ const text = Buffer.concat(chunks).toString("utf8");
37
+ if (res.statusCode === 200)
38
+ resolve(text);
39
+ else
40
+ reject(new Error(`credential broker ${path} answered ${res.statusCode}`));
41
+ });
42
+ res.on("error", reject);
43
+ });
44
+ req.on("timeout", () => req.destroy(new Error(`credential broker ${path} timed out`)));
45
+ req.on("error", (error) => (isMissingSocket(error) ? resolve(null) : reject(error)));
46
+ req.end(body);
47
+ });
48
+ }
49
+ /** `text` with every stored credential value replaced by its placeholder. */
50
+ export async function scrubViaBroker(text, options = {}) {
51
+ const response = await call("/scrub", JSON.stringify({ text }), options);
52
+ if (response === null)
53
+ return text;
54
+ const parsed = JSON.parse(response);
55
+ if (typeof parsed === "object" && parsed !== null && "text" in parsed && typeof parsed.text === "string") {
56
+ return parsed.text;
57
+ }
58
+ throw new Error("credential broker /scrub answered without text");
59
+ }
60
+ //# sourceMappingURL=broker-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker-client.js","sourceRoot":"","sources":["../../src/broker/broker-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export declare const DEFAULT_CA_DIR = "/etc/baker-ca";
2
+ export declare const SYSTEM_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
3
+ export type CertificateAuthority = {
4
+ keyPath: string;
5
+ certPath: string;
6
+ key: string;
7
+ cert: string;
8
+ };
9
+ /**
10
+ * Env for every process the agent runs. `NODE_EXTRA_CA_CERTS` adds our CA to
11
+ * Node's roots; the other three *replace* a client's bundle, so they name the
12
+ * system bundle, which carries our CA once `update-ca-certificates` ran.
13
+ */
14
+ export declare function agentProxyEnv(options: {
15
+ port: number;
16
+ caCertPath: string;
17
+ /** Hosts that bypass the broker entirely, e.g. the model gateway carrying the agent's own conversation. */
18
+ noProxyHosts: readonly string[];
19
+ }): Record<string, string>;
20
+ /**
21
+ * Reuses the CA in `dir`, or creates one (key 0600, cert 0644). With
22
+ * `installSystemTrust`, also adds the cert to the system bundle — needs root;
23
+ * a failure is logged, not thrown, so the proxy still starts.
24
+ */
25
+ export declare function ensureCertificateAuthority(options: {
26
+ dir?: string;
27
+ installSystemTrust: boolean;
28
+ log?: (message: string) => void;
29
+ }): Promise<CertificateAuthority>;
30
+ /**
31
+ * Writes the certificate to `target` as a new file. The system trust directory
32
+ * is under /usr/local, which the agent can write, and the broker is root: a
33
+ * copy would follow a link the agent planted there onto any file. Removing the
34
+ * name first drops such a link, and an exclusive create refuses one planted
35
+ * again in between rather than following it.
36
+ */
37
+ export declare function placeTrustedCert(certPath: string, target: string): Promise<void>;
38
+ //# sourceMappingURL=ca.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ca.d.ts","sourceRoot":"","sources":["../../src/broker/ca.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAC9C,eAAO,MAAM,gBAAgB,uCAAuC,CAAC;AAKrE,MAAM,MAAM,oBAAoB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBzB;AASD;;;;GAIG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,OAAO,CAAC;IAC5B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAahC;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtF"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The broker's certificate authority — what lets it read HTTPS the agent sends
3
+ * through it — and the environment that points the agent's clients at it.
4
+ */
5
+ import { execFile } from "node:child_process";
6
+ import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { promisify } from "node:util";
9
+ import { generateCACertificate } from "mockttp";
10
+ export const DEFAULT_CA_DIR = "/etc/baker-ca";
11
+ export const SYSTEM_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
12
+ /** By absolute path: the broker runs as root and must never resolve a binary through PATH. */
13
+ const UPDATE_CA_CERTIFICATES = "/usr/sbin/update-ca-certificates";
14
+ const SYSTEM_TRUST_PATH = "/usr/local/share/ca-certificates/baker-credential-broker.crt";
15
+ /**
16
+ * Env for every process the agent runs. `NODE_EXTRA_CA_CERTS` adds our CA to
17
+ * Node's roots; the other three *replace* a client's bundle, so they name the
18
+ * system bundle, which carries our CA once `update-ca-certificates` ran.
19
+ */
20
+ export function agentProxyEnv(options) {
21
+ const proxy = `http://127.0.0.1:${options.port}`;
22
+ const noProxy = ["localhost", "127.0.0.1", "::1", ...options.noProxyHosts].join(",");
23
+ return {
24
+ HTTPS_PROXY: proxy,
25
+ HTTP_PROXY: proxy,
26
+ https_proxy: proxy,
27
+ http_proxy: proxy,
28
+ NO_PROXY: noProxy,
29
+ no_proxy: noProxy,
30
+ NODE_USE_ENV_PROXY: "1",
31
+ NODE_EXTRA_CA_CERTS: options.caCertPath,
32
+ REQUESTS_CA_BUNDLE: SYSTEM_CA_BUNDLE,
33
+ SSL_CERT_FILE: SYSTEM_CA_BUNDLE,
34
+ CURL_CA_BUNDLE: SYSTEM_CA_BUNDLE,
35
+ };
36
+ }
37
+ function exists(path) {
38
+ return stat(path).then(() => true, () => false);
39
+ }
40
+ /**
41
+ * Reuses the CA in `dir`, or creates one (key 0600, cert 0644). With
42
+ * `installSystemTrust`, also adds the cert to the system bundle — needs root;
43
+ * a failure is logged, not thrown, so the proxy still starts.
44
+ */
45
+ export async function ensureCertificateAuthority(options) {
46
+ const dir = options.dir ?? DEFAULT_CA_DIR;
47
+ const keyPath = join(dir, "ca.key");
48
+ const certPath = join(dir, "ca.pem");
49
+ if (!(await exists(keyPath)) || !(await exists(certPath))) {
50
+ const ca = await generateCACertificate({ subject: { commonName: "Baker Credential Broker" }, bits: 2048 });
51
+ await mkdir(dir, { recursive: true, mode: 0o755 });
52
+ await writeFile(keyPath, ca.key, { mode: 0o600 });
53
+ await writeFile(certPath, ca.cert, { mode: 0o644 });
54
+ options.log?.(`created certificate authority in ${dir}`);
55
+ }
56
+ if (options.installSystemTrust)
57
+ await installSystemTrust(certPath, options.log);
58
+ return { keyPath, certPath, key: await readFile(keyPath, "utf8"), cert: await readFile(certPath, "utf8") };
59
+ }
60
+ /**
61
+ * Writes the certificate to `target` as a new file. The system trust directory
62
+ * is under /usr/local, which the agent can write, and the broker is root: a
63
+ * copy would follow a link the agent planted there onto any file. Removing the
64
+ * name first drops such a link, and an exclusive create refuses one planted
65
+ * again in between rather than following it.
66
+ */
67
+ export async function placeTrustedCert(certPath, target) {
68
+ await rm(target, { force: true });
69
+ await writeFile(target, await readFile(certPath), { flag: "wx", mode: 0o644 });
70
+ }
71
+ async function installSystemTrust(certPath, log) {
72
+ try {
73
+ await placeTrustedCert(certPath, SYSTEM_TRUST_PATH);
74
+ await promisify(execFile)(UPDATE_CA_CERTIFICATES);
75
+ }
76
+ catch (error) {
77
+ log?.(`could not add the certificate authority to the system bundle: ${String(error)}`);
78
+ }
79
+ }
80
+ //# sourceMappingURL=ca.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ca.js","sourceRoot":"","sources":["../../src/broker/ca.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ca.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ca.test.d.ts","sourceRoot":"","sources":["../../src/broker/ca.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { lstatSync, mkdtempSync, readFileSync, statSync, symlinkSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { describe, expect, it } from "vitest";
5
+ import { agentProxyEnv, ensureCertificateAuthority, placeTrustedCert } from "./ca.js";
6
+ describe("agentProxyEnv", () => {
7
+ it("routes every client through the broker and trusts its CA without dropping the system roots", () => {
8
+ const env = agentProxyEnv({ port: 8877, caCertPath: "/etc/baker-ca/ca.pem", noProxyHosts: [] });
9
+ expect(env).toEqual({
10
+ HTTPS_PROXY: "http://127.0.0.1:8877",
11
+ HTTP_PROXY: "http://127.0.0.1:8877",
12
+ https_proxy: "http://127.0.0.1:8877",
13
+ http_proxy: "http://127.0.0.1:8877",
14
+ NO_PROXY: "localhost,127.0.0.1,::1",
15
+ no_proxy: "localhost,127.0.0.1,::1",
16
+ NODE_USE_ENV_PROXY: "1",
17
+ NODE_EXTRA_CA_CERTS: "/etc/baker-ca/ca.pem",
18
+ REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt",
19
+ SSL_CERT_FILE: "/etc/ssl/certs/ca-certificates.crt",
20
+ CURL_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt",
21
+ });
22
+ });
23
+ it("keeps the named hosts off the broker, so the agent's own model traffic is never rewritten", () => {
24
+ const env = agentProxyEnv({ port: 8877, caCertPath: "/etc/baker-ca/ca.pem", noProxyHosts: ["openrouter.ai"] });
25
+ expect([env.NO_PROXY, env.no_proxy]).toEqual([
26
+ "localhost,127.0.0.1,::1,openrouter.ai",
27
+ "localhost,127.0.0.1,::1,openrouter.ai",
28
+ ]);
29
+ });
30
+ });
31
+ describe("ensureCertificateAuthority", () => {
32
+ it("creates a private key once and reuses it on the next start", async () => {
33
+ const dir = join(mkdtempSync(join(tmpdir(), "baker-ca-")), "ca");
34
+ const first = await ensureCertificateAuthority({ dir, installSystemTrust: false });
35
+ const second = await ensureCertificateAuthority({ dir, installSystemTrust: false });
36
+ expect(second).toEqual(first);
37
+ expect(statSync(first.keyPath).mode & 0o777).toBe(0o600);
38
+ expect(statSync(first.certPath).mode & 0o777).toBe(0o644);
39
+ expect(readFileSync(first.certPath, "utf8")).toContain("BEGIN CERTIFICATE");
40
+ });
41
+ });
42
+ describe("placeTrustedCert", () => {
43
+ it("replaces a link the agent planted where the certificate goes, and never writes through it", async () => {
44
+ // The trust directory is under /usr/local, which the agent can write; the broker is root.
45
+ const dir = mkdtempSync(join(tmpdir(), "baker-trust-"));
46
+ const victim = join(dir, "sudoers");
47
+ writeFileSync(victim, "user ALL=(ALL) ALL\n");
48
+ const target = join(dir, "baker-credential-broker.crt");
49
+ symlinkSync(victim, target);
50
+ const cert = join(dir, "ca.pem");
51
+ writeFileSync(cert, "-----BEGIN CERTIFICATE-----\n");
52
+ await placeTrustedCert(cert, target);
53
+ expect(readFileSync(victim, "utf8")).toBe("user ALL=(ALL) ALL\n");
54
+ expect(lstatSync(target).isSymbolicLink()).toBe(false);
55
+ expect(readFileSync(target, "utf8")).toBe("-----BEGIN CERTIFICATE-----\n");
56
+ });
57
+ });
58
+ //# sourceMappingURL=ca.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ca.test.js","sourceRoot":"","sources":["../../src/broker/ca.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ResolvedCredential } from "./credential-text.ts";
2
+ export declare const CREDENTIAL_TTL_MS = 10000;
3
+ /** Each fetch decrypts every credential the company has; a refusal loop may force one this often at most. */
4
+ export declare const MIN_REFRESH_INTERVAL_MS = 5000;
5
+ export type FetchCredentials = () => Promise<ResolvedCredential[]>;
6
+ export type CredentialCache = {
7
+ get(): Promise<ResolvedCredential[] | null>;
8
+ /** Every value to scrub: the current table plus any value that has left it. */
9
+ scrubbable(): Promise<ResolvedCredential[]>;
10
+ /**
11
+ * Drops the table so the next read fetches it again — unless an early
12
+ * refresh already happened within 5 seconds. Returns whether it dropped it.
13
+ */
14
+ refresh(): boolean;
15
+ /** How many credentials the last fetch returned (0 before the first). */
16
+ size(): number;
17
+ };
18
+ export declare function createCredentialCache(options: {
19
+ fetchCredentials: FetchCredentials;
20
+ now?: () => number;
21
+ ttlMs?: number;
22
+ log?: (message: string) => void;
23
+ }): CredentialCache;
24
+ //# sourceMappingURL=credential-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-cache.d.ts","sourceRoot":"","sources":["../../src/broker/credential-cache.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,6GAA6G;AAC7G,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEnE,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,IAAI,OAAO,CAAC,kBAAkB,EAAE,GAAG,IAAI,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,UAAU,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC5C;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC;IACnB,yEAAyE;IACzE,IAAI,IAAI,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAC7C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,GAAG,eAAe,CAsDlB"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The broker's copy of the company's credentials, fetched from Baker on first
3
+ * need and kept for 10 seconds. Concurrent reads share one fetch. A failed
4
+ * fetch keeps the last good table until the next refresh, but a refusal
5
+ * (`CredentialsRefused`) drops it: Baker has said no. Before any fetch
6
+ * has succeeded there is no table (null), which callers must treat as
7
+ * "credentials unavailable", not "no credentials".
8
+ *
9
+ * A value that leaves the table — refused or removed — may still be on its
10
+ * way back in a response to a request that carried it, so `scrubbable()`
11
+ * keeps scrubbing it for the broker's lifetime. It is never sent again.
12
+ */
13
+ import { CredentialsRefused } from "./baker-api.js";
14
+ export const CREDENTIAL_TTL_MS = 10_000;
15
+ /** Each fetch decrypts every credential the company has; a refusal loop may force one this often at most. */
16
+ export const MIN_REFRESH_INTERVAL_MS = 5_000;
17
+ export function createCredentialCache(options) {
18
+ const now = options.now ?? Date.now;
19
+ const ttlMs = options.ttlMs ?? CREDENTIAL_TTL_MS;
20
+ let entry = null;
21
+ let lastGood = null;
22
+ let lastRefreshAt = Number.NEGATIVE_INFINITY;
23
+ const retired = new Map();
24
+ function replaceTable(next) {
25
+ const kept = new Set(next?.map((credential) => credential.value));
26
+ for (const credential of lastGood ?? []) {
27
+ if (!kept.has(credential.value))
28
+ retired.set(credential.value, credential);
29
+ }
30
+ for (const value of kept)
31
+ retired.delete(value);
32
+ lastGood = next;
33
+ }
34
+ function get() {
35
+ if (entry === null || now() - entry.at >= ttlMs) {
36
+ const table = options.fetchCredentials().then((credentials) => {
37
+ replaceTable(credentials);
38
+ return credentials;
39
+ }, (error) => {
40
+ if (error instanceof CredentialsRefused) {
41
+ options.log?.(`${error.message}; dropping ${lastGood?.length ?? 0} cached credentials`);
42
+ replaceTable(null);
43
+ return null;
44
+ }
45
+ options.log?.(`credential fetch failed, keeping ${lastGood?.length ?? 0} cached: ${String(error)}`);
46
+ return lastGood;
47
+ });
48
+ entry = { at: now(), table };
49
+ }
50
+ return entry.table;
51
+ }
52
+ return {
53
+ get,
54
+ async scrubbable() {
55
+ return [...((await get()) ?? []), ...retired.values()];
56
+ },
57
+ refresh() {
58
+ if (now() - lastRefreshAt < MIN_REFRESH_INTERVAL_MS)
59
+ return false;
60
+ lastRefreshAt = now();
61
+ entry = null;
62
+ return true;
63
+ },
64
+ size() {
65
+ return lastGood?.length ?? 0;
66
+ },
67
+ };
68
+ }
69
+ //# sourceMappingURL=credential-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-cache.js","sourceRoot":"","sources":["../../src/broker/credential-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=credential-cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-cache.test.d.ts","sourceRoot":"","sources":["../../src/broker/credential-cache.test.ts"],"names":[],"mappings":""}