@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
@@ -1,312 +0,0 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
- import { deploymentFault } from "./container-fault.js";
54
- /**
55
- * Making a container able to speak TLS, once there is a CA to trust.
56
- *
57
- * Only `egress: { mode: "http-gateway" }` needs this, but it lives on every
58
- * workspace because the cost of getting it wrong is asymmetric: the trust
59
- * install against a container with no CA prints one line and exits 0, while a
60
- * container that missed it fails every later HTTPS connection with an error
61
- * that names no cause.
62
- */
63
- /**
64
- * Install the egress interception CA, and say what was there to install.
65
- *
66
- * One shell string rather than a script in the image, because **the image cannot
67
- * run this at all**, and the reason is ordering rather than preference.
68
- * Cloudflare's own recipe assumes interception is declared as container
69
- * configuration, so the runtime mounts the CA before the container starts.
70
- * `@cloudflare/computer` uses the raw container API, where the only interception
71
- * hooks are methods called *after* `start()` — so an entrypoint runs strictly
72
- * before the CA can exist, finds nothing, and reports the container
73
- * un-intercepted while every later TLS connection fails.
74
- *
75
- * The listing comes first and runs unconditionally: when the CA is missing,
76
- * *what else is in that directory* is the only evidence distinguishing "mounted
77
- * somewhere else" from "never provisioned", and it costs one line.
78
- */
79
- export const TRUST_CA_COMMAND = [
80
- 'ls -A /etc/cloudflare/certs 2>&1 || echo "(no /etc/cloudflare/certs)"',
81
- "CA=/etc/cloudflare/certs/cloudflare-containers-ca.crt",
82
- 'if [ -r "$CA" ]; then',
83
- // Trailing operators, not leading ones: a newline ends a command in sh, so an
84
- // `&&` opening the next line is a syntax error rather than a continuation.
85
- ' install -m 644 "$CA" /usr/local/share/ca-certificates/cloudflare-containers-ca.crt &&',
86
- " update-ca-certificates > /dev/null 2>&1 &&",
87
- ' echo TRUSTED || echo "TRUST FAILED"',
88
- "else",
89
- ' echo "NO CA AT $CA"',
90
- "fi"
91
- ].join("\n");
92
- /** How long the trust install may take before it is not going to happen. */
93
- const TRUST_TIMEOUT_MS = 30_000;
94
- /**
95
- * Whether the container this object is talking to has had the CA installed.
96
- *
97
- * ## Why the answer is in memory
98
- *
99
- * What it describes is a *container*, which does not outlive the isolate in any
100
- * way worth persisting. A stored `true` would be the exact bug this exists to
101
- * prevent — a flag surviving the container it describes, and vouching for its
102
- * replacement.
103
- *
104
- * It is wrong only in the safe direction. An isolate that lost it re-runs an
105
- * idempotent `install -m 644`.
106
- *
107
- * ## What clears it
108
- *
109
- * **The container's own exit**, watched through `monitor()`. This is the signal
110
- * that does not depend on anyone noticing: a container replaced underneath a
111
- * running command — by a reconnect, a relaunch, a crash — exits, and the watch
112
- * fires whoever caused it. Nothing else here is in a position to know, because
113
- * most commands run through the stub this object hands out and report their
114
- * failures to their own caller, not to it.
115
- *
116
- * The watch lives in memory beside the flag it clears, which is the right
117
- * lifetime: an isolate that loses one loses the other, and the replacement
118
- * starts out untrusted. It is deliberately not held open with `waitUntil` —
119
- * that would keep an invocation alive for the life of the container.
120
- *
121
- * A container seen not running, and a command reporting `EEXEC_LOST`, both call
122
- * {@link forget} as well. They are cheap, they are strictly earlier in some
123
- * orderings, and neither is load-bearing on its own.
124
- */
125
- export class ContainerTrust {
126
- deps;
127
- #trusted = false;
128
- /** Whether an exit watch is standing for the container now running. */
129
- #watching = false;
130
- /** Bumped by {@link forget}, so a watch armed before it is ignored. */
131
- #generation = 0;
132
- /**
133
- * Whether this container's deployment fault has already been reported.
134
- *
135
- * Same lifetime as {@link ContainerTrust.forget}'s other flag, and for the
136
- * same reason: what it describes is one container. A fault that outlived it
137
- * would silence the report for the replacement, which is the container an
138
- * operator is waiting to hear about.
139
- */
140
- #faultReported = false;
141
- constructor(deps) {
142
- this.deps = deps;
143
- }
144
- /**
145
- * Forget that any container was trusted.
146
- *
147
- * Drops the standing watch too, because this is also reached for a container
148
- * that keeps running — a `running` read, a lost exec. The watch it leaves
149
- * behind ignores that container's real exit, so without a fresh one nothing
150
- * would clear the flag again and the replacement would be vouched for.
151
- */
152
- forget() {
153
- this.#generation += 1;
154
- this.#trusted = false;
155
- this.#watching = false;
156
- this.#faultReported = false;
157
- }
158
- /**
159
- * Watch this container's exit, so its replacement is not trusted on its
160
- * behalf.
161
- *
162
- * Re-armed rather than kept: the promise settles once, for the container it
163
- * was taken from, so the next {@link ensure} takes a fresh one for whatever is
164
- * running then.
165
- *
166
- * A rejection is an exit too — the runtime reporting the container went away
167
- * badly — so both settlements clear the flag, and neither is allowed to
168
- * surface as an unhandled rejection.
169
- *
170
- * An exit already accounted for — the container was forgotten since — is not
171
- * reported: a replacement may be running by then, and it is not the one that
172
- * exited.
173
- */
174
- #watch() {
175
- if (this.#watching)
176
- return;
177
- const container = this.deps.container();
178
- if (!container)
179
- return;
180
- const generation = this.#generation;
181
- try {
182
- // A stale watch returns before touching anything: `#watching` belongs to
183
- // whichever watch was armed since.
184
- const done = () => {
185
- if (generation !== this.#generation)
186
- return;
187
- this.forget();
188
- this.deps.onExit?.();
189
- };
190
- container.monitor().then(done, done);
191
- this.#watching = true;
192
- }
193
- catch (err) {
194
- // No watch, so the flag keeps its other two ways of being cleared. Worth a
195
- // line because it silently widens the window this class exists to close.
196
- console.warn(`[${this.deps.tag()}] could not watch the container`, {
197
- id: this.deps.id(),
198
- err: String(err)
199
- });
200
- }
201
- }
202
- /**
203
- * Trust the CA if this container has not been trusted yet.
204
- *
205
- * Called from the workspace's `ready()` path, which is the earliest point that
206
- * is not too early: the backend's `connect()` has completed, so start and
207
- * interception have both happened.
208
- *
209
- * **Never throws.** A workspace that cannot trust the CA still has a checkout,
210
- * a shell and a git history, and whatever runs next will say plainly what went
211
- * wrong — refusing to proceed here would replace a legible TLS error with an
212
- * opaque one.
213
- */
214
- async ensure() {
215
- this.#watch();
216
- if (this.#trusted)
217
- return;
218
- // An exec pushes the workspace before it spawns, and a new container is
219
- // empty, so this one — the first in every container — carries the whole
220
- // tree. {@link TRUST_TIMEOUT_MS} bounds the process, not any of that.
221
- const startedAt = Date.now();
222
- try {
223
- const env_1 = { stack: [], error: void 0, hasError: false };
224
- try {
225
- const handle = __addDisposableResource(env_1, await this.deps
226
- .workspace()
227
- .runtime.exec(TRUST_CA_COMMAND, {
228
- cwd: "/",
229
- encoding: "utf8",
230
- timeoutMs: TRUST_TIMEOUT_MS
231
- }), false);
232
- const result = await handle.result();
233
- const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
234
- // Marked only when the command reported an outcome that will not change
235
- // inside this container. `TRUSTED` is the success; `NO CA AT …` is the
236
- // intentional terminal case, since interception is configured by the
237
- // `connect()` already awaited and a CA absent now stays absent until the
238
- // container is replaced — retrying that on the object's hottest path would
239
- // buy nothing.
240
- //
241
- // `TRUST FAILED` is neither. The command exits 0 whatever happens, so
242
- // treating "it ran" as "it worked" would mark a container trusted whose
243
- // `install` or `update-ca-certificates` failed — and every later HTTPS
244
- // connection in it fails with nothing naming the cause, permanently,
245
- // because nothing tries again.
246
- //
247
- // A throw leaves this false too, so an unreachable container is retried.
248
- this.#trusted = !output.includes("TRUST FAILED");
249
- // Logged at info once per container, deliberately. The container's own
250
- // stdout does not reach Workers Observability, so this line is the only
251
- // place an operator can see whether the container can speak TLS at all —
252
- // and its absence is itself the answer when a workspace never got this far.
253
- //
254
- // Once per container rather than per call is also what makes it readable:
255
- // on the busiest entry point in the object, a line per call would bury the
256
- // one that matters under thousands that say the same thing.
257
- console.info(`[${this.deps.tag()}] container TLS trust`, {
258
- id: this.deps.id(),
259
- exitCode: result.exitCode,
260
- trusted: this.#trusted,
261
- // Where a cold workspace's time went, on the line already logged once
262
- // per container.
263
- pushed: result.pushed,
264
- ms: Date.now() - startedAt,
265
- output
266
- });
267
- }
268
- catch (e_1) {
269
- env_1.error = e_1;
270
- env_1.hasError = true;
271
- }
272
- finally {
273
- __disposeResources(env_1);
274
- }
275
- }
276
- catch (err) {
277
- // A deployment fault is reported as itself, at error, once per container.
278
- // The generic line below is the right one for a container that is merely
279
- // unreachable — a retry is what clears that. It is the wrong one for a
280
- // Worker whose image or container application cannot work at all:
281
- // retrying logs the same symptom until something else gives up, and the
282
- // sentence naming the cause is the one an operator needs.
283
- //
284
- // Only the *log* stops. The trust command is still sent on the next call,
285
- // because the classification is a string match and this is the direction
286
- // that is safe to be wrong in: a misread costs a line nobody needed, where
287
- // refusing to retry would leave a container permanently untrusted on the
288
- // strength of a message the library is free to reword. What actually ends
289
- // the reporting is the container going away, which clears the flag through
290
- // the same watch that clears the trust.
291
- const fault = deploymentFault(err);
292
- if (fault) {
293
- if (!this.#faultReported) {
294
- this.#faultReported = true;
295
- console.error(`[${this.deps.tag()}] ${fault.summary}`, {
296
- id: this.deps.id(),
297
- remedy: fault.remedy,
298
- err: String(err)
299
- });
300
- }
301
- return;
302
- }
303
- console.warn(`[${this.deps.tag()}] could not trust the interception CA`, {
304
- id: this.deps.id(),
305
- // Separates a container that refused at once from one that was pushed
306
- // to and then dropped.
307
- ms: Date.now() - startedAt,
308
- err: String(err)
309
- });
310
- }
311
- }
312
- }
@@ -1,58 +0,0 @@
1
- import { type Workspace } from "@cloudflare/computer";
2
- /** Written into the tree by a successful host install; see `./install-job.ts`. */
3
- export declare const INSTALLED_MARKER = ".da-installed";
4
- /** Every package root with a tree on this container's disk, one per line. */
5
- export declare const DEPS_ROOTS_COMMAND = "for f in /var/lib/workspace-deps/*.root; do [ -f \"$f\" ] && { cat \"$f\"; echo; }; done; true";
6
- /** {@link DEPS_ROOTS_COMMAND}'s output as paths, each once. */
7
- export declare function parseDepsRoots(stdout: string): string[];
8
- /**
9
- * The per-container setup, and a read of the marker under `dir`.
10
- *
11
- * The helper mounts the package root the command targets — its directory, or
12
- * `--prefix`, `--cwd`, `--dir`, `-C` — only under the workspace, never inside
13
- * another tree. Workspace members are not mounted: a mount point cannot be
14
- * removed, so one guessed into `dist/` breaks `rm -rf dist`, and an install at
15
- * the root puts the bulk of the tree there.
16
- *
17
- * Wrappers are written with `install`, never through the path: `npm` is
18
- * commonly a symlink to `npm-cli.js`, and writing through it replaces npm with a
19
- * wrapper that execs itself. For the same reason a wrapper finds its tool on
20
- * `PATH` with `WRAPPER_DIR` skipped, when it runs. `corepack enable` puts its
21
- * shims beside the `corepack` on `PATH` — the wrapper — so it is pointed at the
22
- * real one's directory instead.
23
- */
24
- export declare function depsSetupCommand(workspaceDir: string, dir?: string): string;
25
- /** What a setup run found in the container. */
26
- export interface DepsFound {
27
- /** The marker's contents, or `null` when no host install completed here. */
28
- marker: string | null;
29
- }
30
- export interface ContainerDepsDeps {
31
- workspace: () => Workspace;
32
- workspaceDir: string;
33
- tag: () => string;
34
- id: () => string;
35
- }
36
- /**
37
- * The setup above, once per container.
38
- *
39
- * In memory, with {@link file://./ca-trust.ts}'s lifetime and for its reason: it
40
- * describes one container, and an isolate that lost it re-runs an idempotent
41
- * command.
42
- */
43
- export declare class ContainerDeps {
44
- #private;
45
- private readonly deps;
46
- constructor(deps: ContainerDepsDeps);
47
- forget(): void;
48
- /**
49
- * Run the setup if this container has not had it, and report the marker under
50
- * `dir`. `undefined` when it had, or with no `dir`.
51
- *
52
- * Throws when the container answered and the setup did not finish: nothing
53
- * would say so later, since an unwrapped install succeeds, into the workspace.
54
- * An unreachable container is only logged, like the CA install: the command
55
- * after it fails the same way.
56
- */
57
- ensure(dir?: string): Promise<DepsFound | undefined>;
58
- }
@@ -1,249 +0,0 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
- import { shellQuote } from "@cloudflare/computer";
54
- import { deploymentFault } from "./container-fault.js";
55
- /**
56
- * Dependency trees live on the container's disk, never in the workspace.
57
- *
58
- * A tree in the workspace is written through FUSE into computerd's in-memory
59
- * store, pulled into this object's SQLite, and pushed back into every fresh
60
- * container before its first command — measured at 76,740 entries and nine
61
- * minutes for one cold container. So a package root's `node_modules` is a bind
62
- * mount of container disk, and a new container reinstalls.
63
- *
64
- * The mount is made by the package manager's own invocation, through wrappers
65
- * this installs ahead of the real binaries on `PATH`. Mounting earlier cannot
66
- * work: a bootstrap that initialises submodules and installs each of them in one
67
- * command creates the directories the mounts belong in.
68
- *
69
- * A container restored from a snapshot (see `./deps-snapshot.ts`) has the trees
70
- * on its disk and none of the mounts, which are kernel state. So each mount
71
- * records its package root beside its tree, and the setup mounts every recorded
72
- * root again before it reads the marker through one.
73
- */
74
- /** Where the trees live on the container's disk. */
75
- const DEPS_DISK = "/var/lib/workspace-deps";
76
- /** First on Debian's default `PATH`, so a wrapper here shadows the real tool. */
77
- const WRAPPER_DIR = "/usr/local/sbin";
78
- const MOUNT_HELPER = `${WRAPPER_DIR}/workspace-deps-mount`;
79
- /**
80
- * Every entry point that can write a `node_modules`, wrapped whether or not it
81
- * is installed yet: `corepack enable` or `npm i -g pnpm` adds one later.
82
- */
83
- const WRAPPED = [
84
- "npm",
85
- "npx",
86
- "pnpm",
87
- "yarn",
88
- "corepack",
89
- "bun",
90
- "bunx"
91
- ];
92
- /** Written into the tree by a successful host install; see `./install-job.ts`. */
93
- export const INSTALLED_MARKER = ".da-installed";
94
- /**
95
- * The package root a tree belongs to, written beside it as `<tree>.root`.
96
- *
97
- * Beside rather than inside: `npm ci` empties the tree it installs into, and the
98
- * hash a tree is named by cannot be turned back into the path.
99
- */
100
- const ROOT_SUFFIX = ".root";
101
- /** Every package root with a tree on this container's disk, one per line. */
102
- export const DEPS_ROOTS_COMMAND = `for f in ${DEPS_DISK}/*${ROOT_SUFFIX}; do [ -f "$f" ] && { cat "$f"; echo; }; done; true`;
103
- /** {@link DEPS_ROOTS_COMMAND}'s output as paths, each once. */
104
- export function parseDepsRoots(stdout) {
105
- const roots = stdout
106
- .split("\n")
107
- .map((line) => line.trim())
108
- .filter((line) => line.startsWith("/"));
109
- return [...new Set(roots)];
110
- }
111
- /**
112
- * The per-container setup, and a read of the marker under `dir`.
113
- *
114
- * The helper mounts the package root the command targets — its directory, or
115
- * `--prefix`, `--cwd`, `--dir`, `-C` — only under the workspace, never inside
116
- * another tree. Workspace members are not mounted: a mount point cannot be
117
- * removed, so one guessed into `dist/` breaks `rm -rf dist`, and an install at
118
- * the root puts the bulk of the tree there.
119
- *
120
- * Wrappers are written with `install`, never through the path: `npm` is
121
- * commonly a symlink to `npm-cli.js`, and writing through it replaces npm with a
122
- * wrapper that execs itself. For the same reason a wrapper finds its tool on
123
- * `PATH` with `WRAPPER_DIR` skipped, when it runs. `corepack enable` puts its
124
- * shims beside the `corepack` on `PATH` — the wrapper — so it is pointed at the
125
- * real one's directory instead.
126
- */
127
- export function depsSetupCommand(workspaceDir, dir) {
128
- const helper = [
129
- "#!/bin/sh",
130
- 'cwd="$1"; dir="$1"; shift',
131
- 'while [ $# -gt 0 ]; do case "$1" in',
132
- " --) break;;",
133
- ' --prefix=*|--cwd=*|--dir=*) dir="${1#*=}";;',
134
- ' --prefix|--cwd|--dir|-C) [ $# -gt 1 ] && { dir="$2"; shift; };;',
135
- "esac; shift; done",
136
- 'case "$dir" in /*) ;; *) dir="$cwd/$dir";; esac',
137
- 'dir=$(cd "$dir" 2>/dev/null && pwd -P) || exit 0',
138
- 'while [ "$dir" != / ] && [ ! -f "$dir/package.json" ]; do dir=$(dirname "$dir"); done',
139
- `case "$dir" in */node_modules/*) exit 0;; ${workspaceDir}|${workspaceDir}/*) ;; *) exit 0;; esac`,
140
- 'nm="$dir/node_modules"',
141
- 'mountpoint -q "$nm" && exit 0',
142
- `disk="${DEPS_DISK}/$(printf %s "$dir" | sha256sum | cut -c1-16)"`,
143
- `mkdir -p "$disk" "$nm" && mount --bind "$disk" "$nm" && { printf %s "$dir" > "$disk${ROOT_SUFFIX}"; exit 0; }`,
144
- `echo "workspace-deps: could not put $nm on the container's disk" >&2`,
145
- "exit 1"
146
- ].join("\n");
147
- const wrapper = [
148
- "#!/bin/sh",
149
- `${MOUNT_HELPER} "$PWD" "$@" || exit 1`,
150
- 'tool=$(basename "$0"); real=',
151
- `IFS=:; for d in $PATH; do [ "$d" = ${WRAPPER_DIR} ] && continue; [ -x "$d/$tool" ] && { real="$d"; break; }; done; unset IFS`,
152
- '[ -n "$real" ] || { echo "$tool: not found" >&2; exit 127; }',
153
- 'case "$tool:${1:-}:$*" in',
154
- " *--install-directory*) ;;",
155
- ' corepack:enable:*|corepack:disable:*) cmd="$1"; shift; set -- "$cmd" --install-directory "$real" "$@";;',
156
- "esac",
157
- 'exec "$real/$tool" "$@"'
158
- ].join("\n");
159
- return [
160
- `printf '%s\\n' ${shellQuote(helper)} > /tmp/workspace-deps-mount`,
161
- `install -m 755 /tmp/workspace-deps-mount ${MOUNT_HELPER} || exit 1`,
162
- `printf '%s\\n' ${shellQuote(wrapper)} > /tmp/workspace-deps-wrap`,
163
- `for tool in ${WRAPPED.join(" ")}; do install -m 755 /tmp/workspace-deps-wrap "${WRAPPER_DIR}/$tool" || exit 1; done`,
164
- // A root that is not in this checkout is left alone by the helper, and one
165
- // that cannot be mounted reads as no marker below — an install, not a fault.
166
- `for f in ${DEPS_DISK}/*${ROOT_SUFFIX}; do [ -f "$f" ] && ${MOUNT_HELPER} "$(cat "$f")"; done`,
167
- ...(dir
168
- ? [
169
- `echo "MARKER $(cat ${shellQuote(`${dir}/node_modules/${INSTALLED_MARKER}`)} 2>/dev/null || echo -)"`
170
- ]
171
- : []),
172
- "echo SETUP OK"
173
- ].join("\n");
174
- }
175
- /**
176
- * The setup above, once per container.
177
- *
178
- * In memory, with {@link file://./ca-trust.ts}'s lifetime and for its reason: it
179
- * describes one container, and an isolate that lost it re-runs an idempotent
180
- * command.
181
- */
182
- export class ContainerDeps {
183
- deps;
184
- #done = false;
185
- constructor(deps) {
186
- this.deps = deps;
187
- }
188
- forget() {
189
- this.#done = false;
190
- }
191
- /**
192
- * Run the setup if this container has not had it, and report the marker under
193
- * `dir`. `undefined` when it had, or with no `dir`.
194
- *
195
- * Throws when the container answered and the setup did not finish: nothing
196
- * would say so later, since an unwrapped install succeeds, into the workspace.
197
- * An unreachable container is only logged, like the CA install: the command
198
- * after it fails the same way.
199
- */
200
- async ensure(dir) {
201
- if (this.#done)
202
- return undefined;
203
- const startedAt = Date.now();
204
- let output;
205
- try {
206
- const env_1 = { stack: [], error: void 0, hasError: false };
207
- try {
208
- const handle = __addDisposableResource(env_1, await this.deps
209
- .workspace()
210
- .runtime.exec(depsSetupCommand(this.deps.workspaceDir, dir), {
211
- cwd: "/",
212
- encoding: "utf8",
213
- timeoutMs: 30_000
214
- }), false);
215
- const result = await handle.result();
216
- output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
217
- this.#done = result.exitCode === 0 && output.includes("SETUP OK");
218
- }
219
- catch (e_1) {
220
- env_1.error = e_1;
221
- env_1.hasError = true;
222
- }
223
- finally {
224
- __disposeResources(env_1);
225
- }
226
- }
227
- catch (err) {
228
- const fault = deploymentFault(err);
229
- console.warn(`[${this.deps.tag()}] ${fault?.summary ?? "could not set up container dependencies"}`, {
230
- id: this.deps.id(),
231
- ...(fault ? { remedy: fault.remedy } : {}),
232
- err: String(err)
233
- });
234
- return undefined;
235
- }
236
- console.info(`[${this.deps.tag()}] container dependency setup`, {
237
- id: this.deps.id(),
238
- ok: this.#done,
239
- ms: Date.now() - startedAt,
240
- ...(this.#done ? {} : { output })
241
- });
242
- if (!this.#done)
243
- throw new Error(`could not put node_modules on the container's disk: ${output.trim() || "the setup exited without output"}`);
244
- const marker = /^MARKER (.*)$/m.exec(output)?.[1]?.trim();
245
- if (marker === undefined)
246
- return undefined;
247
- return { marker: marker === "-" ? null : marker };
248
- }
249
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * The container failures that are the deployment's fault, not the workspace's.
3
- *
4
- * Everything else this host catches is the kind of thing a retry clears: a
5
- * container that is starting, a connection that dropped, a command that died
6
- * with it. These are not. They describe a Worker and a container application
7
- * that cannot work together no matter how many times they are asked, and they
8
- * end only when an operator deploys.
9
- *
10
- * **This changes what is said, not what is done.** No caller branches its
11
- * control flow on the answer, and none should start: the match is on a string
12
- * the library is free to reword, so a misread has to cost a misleading sentence
13
- * rather than a workspace that stopped trying. What it earns is the log line.
14
- * The cause is already in the thrown message, but a caller logs its own
15
- * sentence and the cause travels in a detail field a log digest truncates — so
16
- * an operator scanning for what broke reads the symptom ("could not trust the
17
- * interception CA") and not the reason. {@link deploymentFault} is how a caller
18
- * puts the reason in the message instead, and how the install record stops
19
- * offering advice that cannot work.
20
- */
21
- /**
22
- * A failure no retry can clear, in the two registers a caller needs.
23
- *
24
- * `summary` is the log message: what is wrong, short enough to scan. `remedy`
25
- * goes to whoever is blocked by it — it reaches a model through the install
26
- * record, so it says what a person has to do rather than what a tool should try.
27
- */
28
- export interface DeploymentFault {
29
- readonly summary: string;
30
- readonly remedy: string;
31
- }
32
- /**
33
- * Whether this error says the deployment is wrong, and what to say about it.
34
- *
35
- * `undefined` for everything else, which is the answer that keeps the existing
36
- * behaviour: a caller that gets it logs and retries exactly as before.
37
- */
38
- export declare function deploymentFault(err: unknown): DeploymentFault | undefined;