@dynamicagents/plugins 0.14.0 → 0.16.0

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 (141) hide show
  1. package/README.md +14 -14
  2. package/dist/claude-code/cards.d.ts +2 -0
  3. package/dist/claude-code/cards.js +11 -0
  4. package/dist/claude-code/config.d.ts +10 -83
  5. package/dist/claude-code/config.js +0 -2
  6. package/dist/claude-code/credentials.d.ts +32 -50
  7. package/dist/claude-code/credentials.js +115 -74
  8. package/dist/claude-code/daemon/index.d.ts +24 -0
  9. package/dist/claude-code/daemon/index.js +203 -0
  10. package/dist/claude-code/egress.d.ts +35 -117
  11. package/dist/claude-code/egress.js +80 -325
  12. package/dist/claude-code/events.d.ts +14 -17
  13. package/dist/claude-code/events.js +19 -15
  14. package/dist/claude-code/harness-wire.d.ts +45 -0
  15. package/dist/claude-code/harness-wire.js +47 -0
  16. package/dist/claude-code/index.d.ts +19 -29
  17. package/dist/claude-code/index.js +17 -25
  18. package/dist/claude-code/language-model.d.ts +19 -0
  19. package/dist/claude-code/language-model.js +71 -0
  20. package/dist/claude-code/model.d.ts +77 -70
  21. package/dist/claude-code/model.js +327 -327
  22. package/dist/claude-code/placeholder.d.ts +12 -0
  23. package/dist/claude-code/placeholder.js +12 -0
  24. package/dist/claude-code/spec.d.ts +4 -4
  25. package/dist/claude-code/spec.js +4 -4
  26. package/dist/computer/client.d.ts +88 -0
  27. package/dist/computer/client.js +6 -0
  28. package/dist/computer/context.d.ts +7 -7
  29. package/dist/computer/context.js +14 -18
  30. package/dist/computer/gate.d.ts +4 -23
  31. package/dist/computer/gate.js +1 -37
  32. package/dist/computer/index.d.ts +44 -43
  33. package/dist/computer/index.js +29 -50
  34. package/dist/computer/paths.d.ts +5 -27
  35. package/dist/computer/paths.js +7 -30
  36. package/dist/computer/proxy.d.ts +17 -31
  37. package/dist/computer/proxy.js +9 -44
  38. package/dist/computer/render.d.ts +3 -40
  39. package/dist/computer/render.js +3 -36
  40. package/dist/computer/sandbox.d.ts +55 -0
  41. package/dist/computer/sandbox.js +236 -0
  42. package/dist/computer/shell.d.ts +36 -0
  43. package/dist/computer/shell.js +41 -0
  44. package/dist/computer/tools-exec.js +3 -9
  45. package/dist/computer/tools-file.d.ts +1 -1
  46. package/dist/computer/tools-file.js +1 -1
  47. package/dist/{repo → repo-github}/checkout.d.ts +3 -4
  48. package/dist/{repo → repo-github}/checkout.js +3 -4
  49. package/dist/{repo → repo-github}/context.d.ts +14 -32
  50. package/dist/{repo → repo-github}/context.js +46 -81
  51. package/dist/{repo → repo-github}/index.d.ts +30 -13
  52. package/dist/{repo → repo-github}/index.js +25 -6
  53. package/dist/repo-github/mcp.d.ts +95 -0
  54. package/dist/repo-github/mcp.js +107 -0
  55. package/dist/repo-github/sandbox-egress.d.ts +68 -0
  56. package/dist/repo-github/sandbox-egress.js +272 -0
  57. package/dist/repo-github/sandbox-git.d.ts +30 -0
  58. package/dist/repo-github/sandbox-git.js +113 -0
  59. package/dist/{repo → repo-github}/tools-checks.js +43 -4
  60. package/dist/{repo → repo-github}/tools-clone.js +2 -21
  61. package/dist/{repo → repo-github}/tools-pr.d.ts +0 -1
  62. package/dist/{repo → repo-github}/tools-pr.js +85 -20
  63. package/dist/{repo → repo-github}/tools-review.d.ts +6 -0
  64. package/dist/{repo → repo-github}/tools-review.js +62 -176
  65. package/dist/{repo → repo-github}/url.d.ts +11 -0
  66. package/dist/{repo → repo-github}/url.js +12 -1
  67. package/dist/{workspace → sandbox}/advisory.d.ts +16 -35
  68. package/dist/{workspace → sandbox}/advisory.js +2 -29
  69. package/dist/sandbox/agent-exec.d.ts +31 -0
  70. package/dist/sandbox/agent-exec.js +62 -0
  71. package/dist/sandbox/ca-trust.d.ts +9 -0
  72. package/dist/sandbox/ca-trust.js +21 -0
  73. package/dist/sandbox/constants.d.ts +22 -0
  74. package/dist/sandbox/constants.js +22 -0
  75. package/dist/sandbox/daemon/composite.d.ts +9 -0
  76. package/dist/sandbox/daemon/composite.js +35 -0
  77. package/dist/sandbox/daemon/index.d.ts +47 -0
  78. package/dist/sandbox/daemon/index.js +116 -0
  79. package/dist/sandbox/egress.d.ts +81 -0
  80. package/dist/sandbox/egress.js +109 -0
  81. package/dist/sandbox/exec.d.ts +57 -0
  82. package/dist/sandbox/exec.js +52 -0
  83. package/dist/sandbox/format.d.ts +2 -0
  84. package/dist/sandbox/format.js +7 -0
  85. package/dist/sandbox/header.d.ts +50 -0
  86. package/dist/sandbox/header.js +94 -0
  87. package/dist/sandbox/index.d.ts +17 -0
  88. package/dist/sandbox/index.js +17 -0
  89. package/dist/{workspace/install.d.ts → sandbox/install-plan.d.ts} +2 -2
  90. package/dist/{workspace/install.js → sandbox/install-plan.js} +2 -2
  91. package/dist/sandbox/name.d.ts +27 -0
  92. package/dist/sandbox/name.js +32 -0
  93. package/dist/sandbox/object.d.ts +244 -0
  94. package/dist/sandbox/object.js +1139 -0
  95. package/dist/scratch/index.d.ts +1 -9
  96. package/dist/scratch/index.js +2 -18
  97. package/package.json +24 -20
  98. package/dist/claude-code/github.d.ts +0 -39
  99. package/dist/claude-code/github.js +0 -82
  100. package/dist/claude-code/run.d.ts +0 -491
  101. package/dist/claude-code/run.js +0 -913
  102. package/dist/claude-code/session.d.ts +0 -131
  103. package/dist/claude-code/session.js +0 -288
  104. package/dist/workspace/ca-trust.d.ts +0 -95
  105. package/dist/workspace/ca-trust.js +0 -312
  106. package/dist/workspace/container-deps.d.ts +0 -58
  107. package/dist/workspace/container-deps.js +0 -249
  108. package/dist/workspace/container-fault.d.ts +0 -38
  109. package/dist/workspace/container-fault.js +0 -73
  110. package/dist/workspace/deps-snapshot.d.ts +0 -110
  111. package/dist/workspace/deps-snapshot.js +0 -424
  112. package/dist/workspace/exec.d.ts +0 -94
  113. package/dist/workspace/exec.js +0 -153
  114. package/dist/workspace/format.d.ts +0 -36
  115. package/dist/workspace/format.js +0 -57
  116. package/dist/workspace/git-host.d.ts +0 -79
  117. package/dist/workspace/git-host.js +0 -226
  118. package/dist/workspace/git-identity.d.ts +0 -57
  119. package/dist/workspace/git-identity.js +0 -147
  120. package/dist/workspace/index.d.ts +0 -50
  121. package/dist/workspace/index.js +0 -58
  122. package/dist/workspace/install-job.d.ts +0 -170
  123. package/dist/workspace/install-job.js +0 -934
  124. package/dist/workspace/object.d.ts +0 -522
  125. package/dist/workspace/object.js +0 -1541
  126. package/dist/workspace/open.d.ts +0 -95
  127. package/dist/workspace/open.js +0 -63
  128. package/dist/workspace/read.d.ts +0 -16
  129. package/dist/workspace/read.js +0 -22
  130. package/dist/workspace/shell.d.ts +0 -45
  131. package/dist/workspace/shell.js +0 -80
  132. package/dist/workspace/sync.d.ts +0 -111
  133. package/dist/workspace/sync.js +0 -124
  134. package/dist/workspace/wake.d.ts +0 -21
  135. package/dist/workspace/wake.js +0 -10
  136. /package/dist/{repo → repo-github}/tools-checks.d.ts +0 -0
  137. /package/dist/{repo → repo-github}/tools-clone.d.ts +0 -0
  138. /package/dist/{repo → repo-github}/tools-worktree.d.ts +0 -0
  139. /package/dist/{repo → repo-github}/tools-worktree.js +0 -0
  140. /package/dist/{repo → repo-github}/tools-worktrees.d.ts +0 -0
  141. /package/dist/{repo → repo-github}/tools-worktrees.js +0 -0
@@ -0,0 +1,203 @@
1
+ import { parseStream, toProgress } from "../events.js";
2
+ import { ANTHROPIC_PLACEHOLDER_HOST, encodeNote, encodeRunEnd, encodeSessionId } from "../harness-wire.js";
3
+ import { CREDENTIAL_PLACEHOLDER } from "../placeholder.js";
4
+ /** What the end of stderr is kept to, when a process leaves with no result. */
5
+ const STDERR_KEPT = 2_000;
6
+ /** Claude Code, as `serveSandboxDaemon`'s `cli`. */
7
+ export function claudeCodeDaemonCli(options) {
8
+ // Moved, the conversations land outside `.claude/projects`, which is all the
9
+ // harness mirrors, and a lost container could not resume one.
10
+ if (options.env && "CLAUDE_CONFIG_DIR" in options.env) {
11
+ throw new Error("claude-code: CLAUDE_CONFIG_DIR cannot be set through `env`: each session " +
12
+ "keeps its conversations under its own home's .claude/projects, which " +
13
+ "is what the harness keeps for a lost container to resume from");
14
+ }
15
+ return {
16
+ id: options.id,
17
+ // Where the CLI keeps its conversations, under the session's home: the
18
+ // daemon mirrors them to the object, so a lost container resumes them.
19
+ stateDirs: [".claude/projects"],
20
+ command: (turn) => command(options, turn),
21
+ parser: (turn) => parser(turn.operationId)
22
+ };
23
+ }
24
+ /**
25
+ * One turn's command.
26
+ *
27
+ * `stream-json` with `--verbose`, because the stream is the only way to report
28
+ * progress before a run of minutes ends, and without `--verbose` the CLI emits
29
+ * only the final result even in stream mode. The mode is always passed, never
30
+ * left to the CLI's own — see `../config.ts` `ClaudeCodeConfig.permissionMode`.
31
+ *
32
+ * A resumed turn's prompt is the next user turn. An id whose conversation is
33
+ * not there is not a failed session: the CLI emits a `result` naming it, with
34
+ * no model call and nothing billed. A fork continues the conversation under a
35
+ * new id, which is what a change of mode wants — a plan's conversation stays
36
+ * whole for an edit afterwards.
37
+ *
38
+ * What is absent: no `--bare` and no `--settings` override. A cloned
39
+ * repository's `CLAUDE.md`, skills and hooks are what make the agent good at
40
+ * it, and the container already runs that repository's `postinstall` and test
41
+ * suite by design.
42
+ */
43
+ function command(options, turn) {
44
+ const header = turn.header;
45
+ const mode = header?.mode === "plan"
46
+ ? "plan"
47
+ : (options.permissionMode ?? "bypassPermissions");
48
+ const argv = [
49
+ "claude",
50
+ "-p",
51
+ "--output-format",
52
+ "stream-json",
53
+ "--verbose",
54
+ "--permission-mode",
55
+ mode
56
+ ];
57
+ const sessionId = turn.state.sessionId;
58
+ if (sessionId) {
59
+ argv.push("--resume", sessionId);
60
+ if (header?.fork)
61
+ argv.push("--fork-session");
62
+ }
63
+ const model = header?.model ?? turn.settings.model ?? options.model;
64
+ if (model)
65
+ argv.push("--model", model);
66
+ const effort = header?.effort ?? options.effort;
67
+ if (effort)
68
+ argv.push("--effort", effort);
69
+ if (header?.schema !== undefined) {
70
+ const schema = options.schemas[header.schema];
71
+ if (!schema)
72
+ throw new Error(`no schema named ${header.schema} in this image`);
73
+ argv.push("--json-schema", JSON.stringify(schema));
74
+ }
75
+ return { argv, stdin: turn.prompt, env: sessionEnv(options) };
76
+ }
77
+ /**
78
+ * The session's environment. No autoupdate: the image is pinned, and an update
79
+ * would move the wire shape the egress and the parser are written against,
80
+ * mid-run. No nonessential traffic: telemetry and feature-flag fetches, off at
81
+ * the source. The CLI runs as the agent rather than root, so no `IS_SANDBOX`,
82
+ * and each session has its own home, so no `CLAUDE_CONFIG_DIR`.
83
+ *
84
+ * Git's identity is an environment, not a `git config`: every git the session
85
+ * starts inherits it — a superproject's submodules, a scratch clone — and it
86
+ * outranks every config level. Both pairs, because an amend or a rebase keeps
87
+ * the author and stamps a fresh committer. Applied after `env`, so the four
88
+ * keys are one answer.
89
+ *
90
+ * The credential and its base URL are applied last, so a deployment's `env`
91
+ * cannot point the CLI at a real one. What other hosts need — git's and `gh`'s
92
+ * placeholders — the CLI inherits from the daemon, whose environment the
93
+ * sandbox fills from its egress rules.
94
+ */
95
+ function sessionEnv(options) {
96
+ const author = options.author;
97
+ return {
98
+ DISABLE_AUTOUPDATER: "1",
99
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
100
+ ...(options.maxSubagentDepth === undefined
101
+ ? {}
102
+ : {
103
+ CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH: String(options.maxSubagentDepth)
104
+ }),
105
+ ...(options.maxConcurrentSubagents === undefined
106
+ ? {}
107
+ : {
108
+ CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS: String(options.maxConcurrentSubagents)
109
+ }),
110
+ ...options.env,
111
+ ...(author
112
+ ? {
113
+ GIT_AUTHOR_NAME: author.name,
114
+ GIT_AUTHOR_EMAIL: author.email,
115
+ GIT_COMMITTER_NAME: author.name,
116
+ GIT_COMMITTER_EMAIL: author.email
117
+ }
118
+ : {}),
119
+ ANTHROPIC_BASE_URL: `http://${ANTHROPIC_PLACEHOLDER_HOST}`,
120
+ CLAUDE_CODE_OAUTH_TOKEN: CREDENTIAL_PLACEHOLDER
121
+ };
122
+ }
123
+ function parser(operationId) {
124
+ let thinkingFrom;
125
+ /** Notes so far, which positions each note's key. */
126
+ let n = 0;
127
+ let result;
128
+ let rateLimit;
129
+ let sessionId;
130
+ return {
131
+ line(line) {
132
+ const at = Date.now();
133
+ const parsed = parseStream(`${line}\n`, {
134
+ now: at,
135
+ ...(thinkingFrom === undefined ? {} : { thinkingFrom })
136
+ });
137
+ thinkingFrom = parsed.thinkingFrom;
138
+ // The resume state the harness hands the next turn of this session.
139
+ let state;
140
+ for (const event of parsed.events) {
141
+ if (event.kind === "init") {
142
+ sessionId = event.sessionId;
143
+ state = { sessionId };
144
+ }
145
+ else if (event.kind === "rateLimit")
146
+ rateLimit = event.info;
147
+ else if (event.kind === "result")
148
+ result = event.result;
149
+ }
150
+ const message = (id, text) => ({
151
+ type: "message",
152
+ message: {
153
+ id: `${operationId}:${id}`,
154
+ role: "assistant",
155
+ parts: [{ type: "text", text }],
156
+ operationId,
157
+ createdAt: at
158
+ }
159
+ });
160
+ const events = [];
161
+ if (state)
162
+ events.push(message("session", encodeSessionId(state.sessionId)));
163
+ const notes = toProgress(parsed.events, n);
164
+ n += notes.length;
165
+ for (const note of notes)
166
+ events.push(message(note.key, encodeNote(note)));
167
+ if (parsed.skipped > 0) {
168
+ console.warn("[claude-code] a stream line was not recognised", {
169
+ operationId,
170
+ sample: parsed.sample
171
+ });
172
+ }
173
+ return { events, ...(state ? { state } : {}) };
174
+ },
175
+ /**
176
+ * The outcome, once the process has exited — the harness settles nothing
177
+ * before that either. A result with the code the CLI exited with, since a
178
+ * result can be an error it exits non-zero on; with no result, its stderr.
179
+ */
180
+ end(exit) {
181
+ if (result) {
182
+ return {
183
+ status: "done",
184
+ text: encodeRunEnd({
185
+ result,
186
+ exitCode: exit.code,
187
+ ...(rateLimit ? { rateLimit } : {}),
188
+ ...(sessionId ? { sessionId } : {})
189
+ })
190
+ };
191
+ }
192
+ return {
193
+ status: "unanswered",
194
+ reason: encodeRunEnd({
195
+ exitCode: exit.code,
196
+ stderr: exit.stderr.slice(-STDERR_KEPT),
197
+ ...(rateLimit ? { rateLimit } : {}),
198
+ ...(sessionId ? { sessionId } : {})
199
+ })
200
+ };
201
+ }
202
+ };
203
+ }
@@ -1,126 +1,44 @@
1
- import { type CredentialStore } from "./credentials.js";
1
+ import type { EgressCall, SandboxEgressRule } from "../sandbox/egress.js";
2
+ import { type Lead } from "./credentials.js";
2
3
  /**
3
- * `@dynamicagents/plugins/claude-code` — the container's only way out.
4
- *
5
- * `egress: { mode: "http-gateway", gateway }` makes `computerd` intercept every
6
- * outbound request in the container and tunnel it back to the workspace Durable
7
- * Object, which reconstructs it against its original absolute URL and hands it
8
- * here. So this function sits on the **Worker side of the trust boundary** and
9
- * sees the whole of what the container tries to reach.
10
- *
11
- * Intercepting HTTPS means terminating it, which has a cost this file does not
12
- * pay and the container does: the runtime presents its own certificate, so an
13
- * image that has not installed the interception CA has no working HTTPS client
14
- * at all — not even `npm`. Nothing here can fix that from the Worker side; the
15
- * image's entrypoint has to, and the README's "Your image must trust the
16
- * interception CA" section is the whole of what that takes.
17
- *
18
- * That position is what makes three otherwise-hard things easy, and they are the
19
- * reason this file exists rather than a plain allowlist:
20
- *
21
- * 1. **The real credential never enters the container.** Claude Code is launched
22
- * with a placeholder; the swap happens here. A `postinstall` script in a
23
- * cloned repository can read every environment variable the container has and
24
- * still learn nothing — which is the repository's standing rule ("hand it the
25
- * action, not the credential") applied to a process nobody can constrain. A
26
- * GitHub token, when configured, is held the same way — see `./github.ts`.
27
- * 2. **Any restriction that is applied is total.** Not a policy the container
28
- * cooperates with; the only route out. Restriction is **off by default** —
29
- * see {@link EgressConfig.restrictToHosts} for why.
30
- * 3. **Exhaustion is seen, so it can be routed around.** This is the only place
31
- * Anthropic's actual response is visible, and therefore the only place that
32
- * can learn a subscription bucket is empty. See {@link EgressConfig.store}
33
- * and `credentials.ts`.
34
- *
35
- * ## What this deliberately does not do
36
- *
37
- * It does not meter, and a budget gate must not come back: an estimate of spend
38
- * is a guess about a bucket nobody can read, moves only once per run, and the
39
- * bucket announces the answer exactly — see the docblock in `credentials.ts`.
40
- *
41
- * And it does not, by default, **bound exfiltration**. The container holds the
42
- * checkout, and with no restriction configured it can send it anywhere. That is
43
- * a deliberate default rather than an oversight — the reasoning is on
44
- * {@link EgressConfig.restrictToHosts} — and what it means for a reader is that
45
- * the containment here is "the container holds no credential", full stop. Do not
46
- * read a host restriction that is switched off as a boundary that exists.
4
+ * The Anthropic credential, swapped in on the Worker side of a sandbox.
5
+ *
6
+ * The CLI holds a placeholder OAuth token, this sets the pool's lead as the
7
+ * Bearer, and a refusal that spends or rejects the credential moves the lead
8
+ * and tells the CLI to retry on the next one — `./credentials.ts` reads the
9
+ * refusal. A sandbox's egress entrypoint is stateless, so the pool lives in the
10
+ * sandbox the request came from, reached through `call.sandbox()` as a
11
+ * {@link CredentialLease}.
12
+ *
13
+ * The CLI reaches this over plain HTTP to a placeholder host the harness
14
+ * intercepts, so nothing in the container terminates TLS. The daemon points
15
+ * the CLI at it (`./daemon/index.ts`), after the deployment's own environment,
16
+ * so the rule sets no `env`.
47
17
  */
48
18
  /** Anthropic's API host — the one destination that gets a credential. */
49
19
  export declare const ANTHROPIC_HOST = "api.anthropic.com";
50
- export interface EgressConfig {
51
- /**
52
- * The credential pool — see {@link file://./config.ts ClaudeCodeConfig.credentials}.
53
- *
54
- * Called per request, so it must be cheap. This is the one place a real
55
- * credential is read: the session is launched with `CREDENTIAL_PLACEHOLDER`
56
- * and the swap happens here.
57
- */
58
- credentials: () => readonly string[];
59
- /**
60
- * Where the pool's `{ index → resetAt }` map is persisted.
61
- *
62
- * Supplied by the host because the state belongs to the host's storage: it is
63
- * kept per workspace Durable Object, so each workspace learns a rotation
64
- * independently. The cost of that is one wasted `429` per workspace per
65
- * rotation; the saving is a Durable Object class, a binding and a migration.
66
- */
67
- store: CredentialStore;
68
- /**
69
- * Restrict the container to these hosts, plus `api.anthropic.com`.
70
- *
71
- * **Three-way, and each value means literally what it says:**
72
- *
73
- * | Value | Effect |
74
- * |---|---|
75
- * | omitted | unrestricted — the default |
76
- * | `["registry.npmjs.org"]` | that host, plus Anthropic |
77
- * | `[]` | Anthropic only |
78
- *
79
- * An empty array is *not* the same as omitting the field, deliberately. A host
80
- * computing this list — `repos.flatMap(hostsFor)`, say — that happens to
81
- * produce `[]` means "nothing extra", and collapsing that into "everything"
82
- * would hand the widest possible policy to an expression that returned
83
- * nothing.
84
- *
85
- * ## Why unrestricted is the default
86
- *
87
- * A curated list is permanently wrong for a coding agent. `esbuild`, `swc` and
88
- * `sharp` fetch prebuilt binaries from release CDNs; Playwright downloads
89
- * browsers from a third host; corepack fetches package managers; and reading
90
- * documentation is part of the job. The failure mode is the bad one — `npm ci`
91
- * dying inside a `postinstall` with a network error nobody connects to a list
92
- * three files away.
93
- *
94
- * Little is lost by defaulting open, because **the restriction was never what
95
- * protected the credential**. The swap is keyed on the destination being
96
- * Anthropic and credential headers are stripped from everything else, so both
97
- * hold whatever this says. What an unrestricted policy does give up is a bound
98
- * on *exfiltration*: the container holds the checkout and can send it
99
- * anywhere. That matches `/computer`, whose egress has always been
100
- * unrestricted, for the same reason.
101
- *
102
- * Matched **exactly** on hostname when set. No wildcards, and that is not an
103
- * omission: `*.example.com` is how a restriction quietly becomes no
104
- * restriction, the first time a host serves user-controlled subdomains.
105
- */
106
- restrictToHosts?: readonly string[];
107
- /**
108
- * The GitHub token to swap for its placeholder, read per request. It reaches
109
- * {@link GITHUB_HOSTS} and nowhere else. Unset, GitHub is reached
110
- * anonymously like any other host. See `./github.ts`.
111
- */
112
- githubToken?: () => string | undefined;
113
- /** Named in log lines so one Worker's several gateways stay tellable apart. */
20
+ /** The pool, as the object holding it answers over RPC. */
21
+ export interface CredentialLease {
22
+ lead(): Promise<Lead>;
23
+ spend(id: string, resetAt: number): Promise<Lead>;
24
+ reject(id: string): Promise<Lead>;
25
+ }
26
+ export interface ClaudeCodeEgressOptions {
27
+ /** The pool `call` leases from. Default: the sandbox it came from, which holds one through {@link credentialHost}. */
28
+ pool?: (call: EgressCall) => CredentialLease;
114
29
  label?: string;
115
30
  now?: () => number;
116
31
  }
32
+ /** The egress rule of a sandbox that runs Claude Code. */
33
+ export declare function claudeCodeEgress(options?: ClaudeCodeEgressOptions): SandboxEgressRule;
117
34
  /**
118
- * Build the `Fetcher` a `WorkspaceEgressPolicy` takes.
119
- *
120
- * A real object rather than a cast: `Fetcher` requires `connect` as well as
121
- * `fetch`, and container egress is HTTP only, so `connect` throws a sentence
122
- * naming why instead of being absent. If a future `computerd` ever opens a raw
123
- * socket through this policy, the failure says so rather than arriving as
124
- * `undefined is not a function` several frames away.
35
+ * The pool a sandbox object holds, for its RPCs to answer {@link CredentialLease}
36
+ * with — and `admit`, which a session start calls: a mark lasts one session.
125
37
  */
126
- export declare function claudeCodeEgress(config: EgressConfig): Fetcher;
38
+ export declare function credentialHost(options: {
39
+ storage: DurableObjectStorage;
40
+ credentials: () => readonly string[];
41
+ key?: string;
42
+ }): CredentialLease & {
43
+ admit(): Promise<Lead>;
44
+ };