@dynamicagents/plugins 0.13.0 → 0.15.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 +4 -0
  3. package/dist/claude-code/cards.js +22 -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 +328 -304
  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/claude-code/verified.d.ts +1 -1
  27. package/dist/claude-code/verified.js +1 -1
  28. package/dist/computer/client.d.ts +88 -0
  29. package/dist/computer/client.js +6 -0
  30. package/dist/computer/context.d.ts +7 -7
  31. package/dist/computer/context.js +14 -18
  32. package/dist/computer/gate.d.ts +4 -23
  33. package/dist/computer/gate.js +1 -37
  34. package/dist/computer/index.d.ts +44 -43
  35. package/dist/computer/index.js +29 -50
  36. package/dist/computer/paths.d.ts +5 -27
  37. package/dist/computer/paths.js +7 -30
  38. package/dist/computer/proxy.d.ts +17 -31
  39. package/dist/computer/proxy.js +9 -44
  40. package/dist/computer/render.d.ts +3 -40
  41. package/dist/computer/render.js +3 -36
  42. package/dist/computer/sandbox.d.ts +55 -0
  43. package/dist/computer/sandbox.js +236 -0
  44. package/dist/computer/shell.d.ts +36 -0
  45. package/dist/computer/shell.js +41 -0
  46. package/dist/computer/tools-exec.js +3 -9
  47. package/dist/computer/tools-file.d.ts +1 -1
  48. package/dist/computer/tools-file.js +1 -1
  49. package/dist/{repo → repo-github}/checkout.d.ts +3 -4
  50. package/dist/{repo → repo-github}/checkout.js +3 -4
  51. package/dist/{repo → repo-github}/context.d.ts +14 -32
  52. package/dist/{repo → repo-github}/context.js +46 -81
  53. package/dist/{repo → repo-github}/index.d.ts +30 -13
  54. package/dist/{repo → repo-github}/index.js +25 -6
  55. package/dist/repo-github/mcp.d.ts +95 -0
  56. package/dist/repo-github/mcp.js +107 -0
  57. package/dist/repo-github/sandbox-egress.d.ts +68 -0
  58. package/dist/repo-github/sandbox-egress.js +272 -0
  59. package/dist/repo-github/sandbox-git.d.ts +30 -0
  60. package/dist/repo-github/sandbox-git.js +113 -0
  61. package/dist/{repo → repo-github}/tools-checks.js +43 -4
  62. package/dist/{repo → repo-github}/tools-clone.js +2 -21
  63. package/dist/{repo → repo-github}/tools-pr.d.ts +0 -1
  64. package/dist/{repo → repo-github}/tools-pr.js +85 -20
  65. package/dist/{repo → repo-github}/tools-review.d.ts +6 -0
  66. package/dist/{repo → repo-github}/tools-review.js +62 -176
  67. package/dist/{repo → repo-github}/url.d.ts +11 -0
  68. package/dist/{repo → repo-github}/url.js +12 -1
  69. package/dist/{workspace → sandbox}/advisory.d.ts +16 -35
  70. package/dist/{workspace → sandbox}/advisory.js +2 -29
  71. package/dist/sandbox/agent-exec.d.ts +31 -0
  72. package/dist/sandbox/agent-exec.js +62 -0
  73. package/dist/sandbox/ca-trust.d.ts +9 -0
  74. package/dist/sandbox/ca-trust.js +21 -0
  75. package/dist/sandbox/constants.d.ts +22 -0
  76. package/dist/sandbox/constants.js +22 -0
  77. package/dist/sandbox/daemon/composite.d.ts +9 -0
  78. package/dist/sandbox/daemon/composite.js +35 -0
  79. package/dist/sandbox/daemon/index.d.ts +47 -0
  80. package/dist/sandbox/daemon/index.js +116 -0
  81. package/dist/sandbox/egress.d.ts +81 -0
  82. package/dist/sandbox/egress.js +109 -0
  83. package/dist/sandbox/exec.d.ts +57 -0
  84. package/dist/sandbox/exec.js +52 -0
  85. package/dist/sandbox/format.d.ts +2 -0
  86. package/dist/sandbox/format.js +7 -0
  87. package/dist/sandbox/header.d.ts +50 -0
  88. package/dist/sandbox/header.js +94 -0
  89. package/dist/sandbox/index.d.ts +17 -0
  90. package/dist/sandbox/index.js +17 -0
  91. package/dist/{workspace/install.d.ts → sandbox/install-plan.d.ts} +10 -1
  92. package/dist/{workspace/install.js → sandbox/install-plan.js} +22 -4
  93. package/dist/sandbox/name.d.ts +27 -0
  94. package/dist/sandbox/name.js +32 -0
  95. package/dist/sandbox/object.d.ts +244 -0
  96. package/dist/sandbox/object.js +1139 -0
  97. package/dist/scratch/index.d.ts +1 -9
  98. package/dist/scratch/index.js +2 -18
  99. package/package.json +26 -22
  100. package/dist/claude-code/github.d.ts +0 -39
  101. package/dist/claude-code/github.js +0 -82
  102. package/dist/claude-code/run.d.ts +0 -474
  103. package/dist/claude-code/run.js +0 -901
  104. package/dist/claude-code/session.d.ts +0 -131
  105. package/dist/claude-code/session.js +0 -285
  106. package/dist/workspace/ca-trust.d.ts +0 -95
  107. package/dist/workspace/ca-trust.js +0 -312
  108. package/dist/workspace/container-deps.d.ts +0 -54
  109. package/dist/workspace/container-deps.js +0 -224
  110. package/dist/workspace/container-fault.d.ts +0 -38
  111. package/dist/workspace/container-fault.js +0 -73
  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 -49
  121. package/dist/workspace/index.js +0 -58
  122. package/dist/workspace/install-job.d.ts +0 -121
  123. package/dist/workspace/install-job.js +0 -866
  124. package/dist/workspace/object.d.ts +0 -502
  125. package/dist/workspace/object.js +0 -1427
  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 -19
  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,95 +0,0 @@
1
- import type { WorkspaceClient, WorkspaceStub } from "@cloudflare/computer";
2
- import type { WorkspaceAdvisory } from "./advisory.js";
3
- /**
4
- * Reaching a workspace: which one a call names, and the two ways to open it.
5
- *
6
- * A leaf, so the object, `/computer`'s tools and its proxy in
7
- * `../computer/proxy.ts` can import it without
8
- * going through the barrel that re-exports them.
9
- */
10
- /**
11
- * Where a parent puts the workspace name so its sub-agents reach the same one.
12
- *
13
- * A sub-agent cannot compute this itself. The name is derived from the caller
14
- * and the repository, and the checkout it should work in is one its parent
15
- * chose — so a sub-agent that named its own would land beside the parent's
16
- * checkout, in a workspace with nothing in it.
17
- *
18
- * A spec's `prepare` runs on the parent, where the name resolves, and its
19
- * return value reaches the sub-agent's plugins as `runtime()`. That is the
20
- * channel core built for exactly this, and it is emphatically *not* the
21
- * sub-agent's input, which the parent's model writes — a workspace name there
22
- * would be model-authored, and a model naming another caller's workspace would
23
- * get that caller's files.
24
- */
25
- export declare const WORKSPACE_RUNTIME_KEY = "workspaceName";
26
- /**
27
- * Read a parent-resolved workspace name off a sub-agent's `runtime()`.
28
- *
29
- * Returns `undefined` rather than throwing on anything unexpected: a parent has
30
- * no runtime at all, and falling back to the configured thunk is right there.
31
- */
32
- export declare function workspaceNameFromRuntime(runtime: unknown): string | undefined;
33
- /**
34
- * The Durable Object the workspace lives in, as this plugin needs to see it.
35
- *
36
- * Structural rather than imported: the class is the host's — it owns the
37
- * container binding, the alarm and the install job — and a plugin that imported
38
- * it would stop a second host from bringing its own. `__getWorkspaceStub` is the
39
- * one method `getWorkspace()` calls across the boundary, and it is what
40
- * `withWorkspace` installs (or what a host that constructs `Workspace` itself
41
- * reimplements, which is the same three lines).
42
- */
43
- export interface WorkspaceHost extends Rpc.DurableObjectBranded {
44
- __getWorkspaceStub(): Promise<WorkspaceStub>;
45
- /**
46
- * The same workspace for filesystem-only work, which the host must serve
47
- * **without starting a container**.
48
- *
49
- * The filesystem is the Durable Object's own SQLite, while the first command
50
- * in a fresh container waits for the whole tree to be pushed into it — so a
51
- * file tool served the other way waits minutes for a local `readdir`. See
52
- * `WorkspaceObjectBase.__getWorkspaceFsStub` in `./object.ts`.
53
- *
54
- * Required rather than optional, for the reason {@link advisories} gives. A
55
- * host with nothing to distinguish returns its own `__getWorkspaceStub()`.
56
- */
57
- __getWorkspaceFsStub(): Promise<WorkspaceStub>;
58
- /**
59
- * Everything currently true about the workspace that a caller must not assume
60
- * away — see {@link file://./advisory.ts}. An empty array is the good case.
61
- *
62
- * Required rather than optional, and not only because Workers RPC types an
63
- * optional method as a union nothing can call. A host with nothing to report
64
- * returns `[]` in one line; a host that forgot to expose it gets a compile
65
- * error instead of a `bash` that silently runs against a half-built
66
- * `node_modules`, or against a workspace whose writes are being dropped.
67
- *
68
- * `deriveAdvisories` builds the array from what the host already knows, so
69
- * implementing this is gathering what the host has rather than writing policy.
70
- */
71
- advisories(): Promise<readonly WorkspaceAdvisory[]>;
72
- }
73
- /**
74
- * Open the workspace behind a Durable Object stub.
75
- *
76
- * The cast is unavoidable, and narrow enough to be worth isolating here rather
77
- * than repeating. `getWorkspace` wants a handle whose `__getWorkspaceStub()`
78
- * resolves to a `WorkspaceStub` — the concrete class, private fields and all.
79
- * Across a Durable Object boundary Workers RPC hands back a structural
80
- * `Stub<WorkspaceStub>`, which forwards every method faithfully but carries none
81
- * of the class's private brand: runtime-compatible, type-incompatible. This is
82
- * the only place in the plugin that gap is crossed.
83
- */
84
- export declare function openWorkspace(host: DurableObjectStub<WorkspaceHost>): Promise<WorkspaceClient>;
85
- /**
86
- * The same workspace, opened for filesystem work alone.
87
- *
88
- * `getWorkspace` calls exactly one method on what it is handed, so the host is
89
- * wrapped in an object answering it from the other side of the seam — see
90
- * {@link WorkspaceHost.__getWorkspaceFsStub}. Everything past that is identical.
91
- *
92
- * **Only the file tools take this.** `bash` and `workspaceExec` need the
93
- * container started and its CA installed, so they open it the other way.
94
- */
95
- export declare function openWorkspaceFs(host: DurableObjectStub<WorkspaceHost>): Promise<WorkspaceClient>;
@@ -1,63 +0,0 @@
1
- import { getWorkspace } from "@cloudflare/computer";
2
- /**
3
- * Reaching a workspace: which one a call names, and the two ways to open it.
4
- *
5
- * A leaf, so the object, `/computer`'s tools and its proxy in
6
- * `../computer/proxy.ts` can import it without
7
- * going through the barrel that re-exports them.
8
- */
9
- /**
10
- * Where a parent puts the workspace name so its sub-agents reach the same one.
11
- *
12
- * A sub-agent cannot compute this itself. The name is derived from the caller
13
- * and the repository, and the checkout it should work in is one its parent
14
- * chose — so a sub-agent that named its own would land beside the parent's
15
- * checkout, in a workspace with nothing in it.
16
- *
17
- * A spec's `prepare` runs on the parent, where the name resolves, and its
18
- * return value reaches the sub-agent's plugins as `runtime()`. That is the
19
- * channel core built for exactly this, and it is emphatically *not* the
20
- * sub-agent's input, which the parent's model writes — a workspace name there
21
- * would be model-authored, and a model naming another caller's workspace would
22
- * get that caller's files.
23
- */
24
- export const WORKSPACE_RUNTIME_KEY = "workspaceName";
25
- /**
26
- * Read a parent-resolved workspace name off a sub-agent's `runtime()`.
27
- *
28
- * Returns `undefined` rather than throwing on anything unexpected: a parent has
29
- * no runtime at all, and falling back to the configured thunk is right there.
30
- */
31
- export function workspaceNameFromRuntime(runtime) {
32
- const value = runtime?.[WORKSPACE_RUNTIME_KEY];
33
- return typeof value === "string" && value.length > 0 ? value : undefined;
34
- }
35
- /**
36
- * Open the workspace behind a Durable Object stub.
37
- *
38
- * The cast is unavoidable, and narrow enough to be worth isolating here rather
39
- * than repeating. `getWorkspace` wants a handle whose `__getWorkspaceStub()`
40
- * resolves to a `WorkspaceStub` — the concrete class, private fields and all.
41
- * Across a Durable Object boundary Workers RPC hands back a structural
42
- * `Stub<WorkspaceStub>`, which forwards every method faithfully but carries none
43
- * of the class's private brand: runtime-compatible, type-incompatible. This is
44
- * the only place in the plugin that gap is crossed.
45
- */
46
- export function openWorkspace(host) {
47
- return getWorkspace(host);
48
- }
49
- /**
50
- * The same workspace, opened for filesystem work alone.
51
- *
52
- * `getWorkspace` calls exactly one method on what it is handed, so the host is
53
- * wrapped in an object answering it from the other side of the seam — see
54
- * {@link WorkspaceHost.__getWorkspaceFsStub}. Everything past that is identical.
55
- *
56
- * **Only the file tools take this.** `bash` and `workspaceExec` need the
57
- * container started and its CA installed, so they open it the other way.
58
- */
59
- export function openWorkspaceFs(host) {
60
- return getWorkspace({
61
- __getWorkspaceStub: () => host.__getWorkspaceFsStub()
62
- });
63
- }
@@ -1,16 +0,0 @@
1
- import type { WorkspaceClient } from "@cloudflare/computer";
2
- /**
3
- * Does this path exist?
4
- *
5
- * `getWorkspace()` types `fs` as the in-process `WorkspaceFilesystem`, but the
6
- * value on the far side of a Durable Object boundary is a
7
- * `WorkspaceFilesystemStub`, which carries a few methods the local class does
8
- * not — `exists` among them. So the fast path is asked for rather than cast to,
9
- * and `stat` is the fallback for a host that hands back a local workspace.
10
- *
11
- * The fallback treats *any* `stat` failure as absence, which is the honest
12
- * reading for a tool whose entire answer is a boolean: there is no error channel
13
- * to distinguish "missing" from "unreadable", and the caller's next move — look
14
- * somewhere else — is the same either way.
15
- */
16
- export declare function pathExists(fs: WorkspaceClient["fs"], path: string): Promise<boolean>;
@@ -1,22 +0,0 @@
1
- /**
2
- * Does this path exist?
3
- *
4
- * `getWorkspace()` types `fs` as the in-process `WorkspaceFilesystem`, but the
5
- * value on the far side of a Durable Object boundary is a
6
- * `WorkspaceFilesystemStub`, which carries a few methods the local class does
7
- * not — `exists` among them. So the fast path is asked for rather than cast to,
8
- * and `stat` is the fallback for a host that hands back a local workspace.
9
- *
10
- * The fallback treats *any* `stat` failure as absence, which is the honest
11
- * reading for a tool whose entire answer is a boolean: there is no error channel
12
- * to distinguish "missing" from "unreadable", and the caller's next move — look
13
- * somewhere else — is the same either way.
14
- */
15
- export async function pathExists(fs, path) {
16
- // Called as a method, never through `.call`: on an RPC stub `.call` is read as
17
- // a remote method, and the stub it would pass as `this` cannot be serialised.
18
- const remote = fs;
19
- if (typeof remote.exists === "function")
20
- return remote.exists(path);
21
- return fs.stat(path).then(() => true, () => false);
22
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Run a command under {@link file://./exec.ts WorkspaceClientConfig.shell} with its
3
- * two output streams left as they are, or hand it back untouched when no shell
4
- * is configured.
5
- *
6
- * This is the variant for a caller that **reads the result in code**: `stdout` is
7
- * a data channel it compares or parses, and `stderr` is a separate diagnostic.
8
- * {@link file://./exec.ts workspaceExec} is that caller, on behalf of
9
- * `@dynamicagents/plugins/repo`, which asks git questions like
10
- * `symbolic-ref --short refs/remotes/origin/HEAD` and `rev-list --count` and
11
- * needs the answer alone.
12
- *
13
- * Two functions rather than one with a flag, because the difference is a change
14
- * to the *output contract* and a boolean hides it at the call site. Merging the
15
- * streams here would give every `/repo` git command an empty `stderr` and a
16
- * `stdout` that is a transcript rather than an answer — which fails silently,
17
- * since the commands `/repo` parses are quiet ones and a single `warning:` is
18
- * enough to skip the empty-branch guard.
19
- */
20
- export declare function withShell(command: string, shell: string | undefined): string;
21
- /**
22
- * Run a command under {@link file://./exec.ts WorkspaceClientConfig.shell} with its
23
- * two output streams merged into one transcript, in the order they were
24
- * written.
25
- *
26
- * This is the variant for a caller whose consumer is **a model reading output**.
27
- * `bash` is that caller. A project's check is a chain — `wrangler types &&
28
- * prettier && eslint && tsc` — and *which tool spoke last* is how you know which
29
- * one failed. A stdout block and a separate stderr block destroy that ordering,
30
- * and a model that wants it back re-runs the whole gate as
31
- * `npm run check > /tmp/out 2>&1; cat /tmp/out`. This is that workaround, done
32
- * once, for free.
33
- *
34
- * The redirect binds to the wrapper process, so it applies to everything the
35
- * command writes however deeply nested — and there is no inner brace group or
36
- * subshell to mis-parse a command that already contains `&&`, quotes or redirects
37
- * of its own.
38
- *
39
- * With no shell configured there is no wrapper process to redirect, so the
40
- * command goes to the runtime untouched and the two streams arrive separate.
41
- * That is not a gap: {@link file://../computer/render.ts renderResult} renders them as a
42
- * labelled `--- stderr ---` block for exactly this case. The transcript is the
43
- * better answer, not the only supported one.
44
- */
45
- export declare function withShellTranscript(command: string, shell: string | undefined): string;
@@ -1,80 +0,0 @@
1
- import { shellQuote } from "@cloudflare/computer";
2
- /** How a command reaches the shell, and what its two output streams do there. */
3
- /**
4
- * The shell wrapper both variants share, minus the choice they differ on.
5
- *
6
- * One quoted argument, not string concatenation: the command is model-authored
7
- * and routinely contains quotes of its own (`git commit -m "…"`), so anything
8
- * less than `shellQuote` re-parses the model's quoting and mangles it.
9
- *
10
- * ## Why `-o pipefail`
11
- *
12
- * Without it a pipeline's exit status is its **last** stage's, so
13
- * `npm run check | tail -100` reports `exit 0` for a gate that failed outright —
14
- * and models pipe into `tail` constantly. A tool that reports success for a
15
- * failed build is worse than one that reports nothing. It also costs a re-run
16
- * every time: a model that cannot trust a piped exit code runs the whole thing
17
- * again unpiped to get one.
18
- *
19
- * The trade is worth stating. `pipefail` surfaces SIGPIPE too, so
20
- * `ls big-dir | head -1` reports 141 rather than 0 — noisy, but *visible*, where
21
- * the alternative is a failing gate that looks clean. The tool description tells
22
- * the model not to pipe into `head`/`tail` at all, since output is already
23
- * truncated with both ends kept.
24
- *
25
- * Requires a shell that implements it — `bash`, `zsh`, `ksh`, not `sh`/dash. A
26
- * host setting {@link file://./exec.ts WorkspaceClientConfig.shell} is choosing that
27
- * shell explicitly.
28
- */
29
- function wrapped(command, shell) {
30
- return `${shell} -o pipefail -c ${shellQuote(command)}`;
31
- }
32
- /**
33
- * Run a command under {@link file://./exec.ts WorkspaceClientConfig.shell} with its
34
- * two output streams left as they are, or hand it back untouched when no shell
35
- * is configured.
36
- *
37
- * This is the variant for a caller that **reads the result in code**: `stdout` is
38
- * a data channel it compares or parses, and `stderr` is a separate diagnostic.
39
- * {@link file://./exec.ts workspaceExec} is that caller, on behalf of
40
- * `@dynamicagents/plugins/repo`, which asks git questions like
41
- * `symbolic-ref --short refs/remotes/origin/HEAD` and `rev-list --count` and
42
- * needs the answer alone.
43
- *
44
- * Two functions rather than one with a flag, because the difference is a change
45
- * to the *output contract* and a boolean hides it at the call site. Merging the
46
- * streams here would give every `/repo` git command an empty `stderr` and a
47
- * `stdout` that is a transcript rather than an answer — which fails silently,
48
- * since the commands `/repo` parses are quiet ones and a single `warning:` is
49
- * enough to skip the empty-branch guard.
50
- */
51
- export function withShell(command, shell) {
52
- return shell ? wrapped(command, shell) : command;
53
- }
54
- /**
55
- * Run a command under {@link file://./exec.ts WorkspaceClientConfig.shell} with its
56
- * two output streams merged into one transcript, in the order they were
57
- * written.
58
- *
59
- * This is the variant for a caller whose consumer is **a model reading output**.
60
- * `bash` is that caller. A project's check is a chain — `wrangler types &&
61
- * prettier && eslint && tsc` — and *which tool spoke last* is how you know which
62
- * one failed. A stdout block and a separate stderr block destroy that ordering,
63
- * and a model that wants it back re-runs the whole gate as
64
- * `npm run check > /tmp/out 2>&1; cat /tmp/out`. This is that workaround, done
65
- * once, for free.
66
- *
67
- * The redirect binds to the wrapper process, so it applies to everything the
68
- * command writes however deeply nested — and there is no inner brace group or
69
- * subshell to mis-parse a command that already contains `&&`, quotes or redirects
70
- * of its own.
71
- *
72
- * With no shell configured there is no wrapper process to redirect, so the
73
- * command goes to the runtime untouched and the two streams arrive separate.
74
- * That is not a gap: {@link file://../computer/render.ts renderResult} renders them as a
75
- * labelled `--- stderr ---` block for exactly this case. The transcript is the
76
- * better answer, not the only supported one.
77
- */
78
- export function withShellTranscript(command, shell) {
79
- return shell ? `${wrapped(command, shell)} 2>&1` : command;
80
- }
@@ -1,111 +0,0 @@
1
- import type { Workspace } from "@cloudflare/computer";
2
- import type { Deadline } from "@dynamicagents/core/alarm";
3
- /**
4
- * The host's half of syncing: moving what the container wrote into the object.
5
- *
6
- * ## Why the host has a half at all
7
- *
8
- * Every command is bracketed by a sync — a push before it runs, a pull after it
9
- * exits — and the pull is what carries its writes back into this object's
10
- * SQLite. That pull can fail while the command itself succeeds: a transport that
11
- * dropped, a container replaced underneath it. The library reports that on the
12
- * command's result and schedules nothing of its own afterwards. What it does
13
- * keep is the cursor, so a later `pull()` resumes the same operation rather than
14
- * starting one.
15
- *
16
- * That later `pull()` is this. Without it the workspace silently lags the
17
- * container: the file tools read a tree missing the last command's work, and git
18
- * — which runs on this side — would commit and push it that way.
19
- */
20
- /**
21
- * How long one drain spends moving blocks before it comes back.
22
- *
23
- * The alarm is shared. Every other reason a workspace wakes is queued behind
24
- * whatever the alarm is doing, so a drain that ran to completion in one
25
- * invocation would hold the object for as long as the pull takes. A minute
26
- * finishes an ordinary pull outright and keeps a large one from starving the
27
- * install watchdog.
28
- */
29
- export declare const SYNC_DRAIN_BUDGET_MS = 60000;
30
- /** How soon a drain that is making progress comes back for the next block. */
31
- export declare const SYNC_DRAIN_RESUME_MS = 1000;
32
- /**
33
- * The ceiling on the wait after a pull that failed.
34
- *
35
- * A failure is not a slow success: whatever broke — a container that stopped
36
- * answering, a transport that will not open — is usually still broken a second
37
- * later, and coming back at the resume interval would be an alarm and an error
38
- * line every second for as long as the workspace stays up. Backing off turns
39
- * that into a handful of attempts an hour, which is the rate a recoverable
40
- * fault actually clears at.
41
- *
42
- * Bounded rather than abandoned, because the cursor is durable and the cost of
43
- * one more attempt is one round trip: a container that comes back should be
44
- * drained without anyone asking.
45
- */
46
- export declare const SYNC_DRAIN_MAX_BACKOFF_MS: number;
47
- /** How long to wait before a retry, after this many consecutive failures. */
48
- export declare function syncRetryDelayMs(attempt: number): number;
49
- /**
50
- * What one drain achieved.
51
- *
52
- * `incomplete` and `failed` are separated because they want opposite responses.
53
- * `incomplete` is a healthy drain that ran out of budget with the cursor moved,
54
- * and the right answer is to come straight back. `failed` is a pull that threw
55
- * with nothing to show for it, and coming straight back would spin against
56
- * whatever is broken — see {@link syncRetryDelayMs}.
57
- *
58
- * `unavailable` is not a failure either: it is the answer when there is no
59
- * container to pull from, which also means there is nothing outstanding that
60
- * could still be recovered. Kept distinct from `complete` so a caller deciding
61
- * whether to act on the workspace — a git push, say — can tell "nothing to move"
62
- * from "nothing reachable".
63
- */
64
- export type DrainOutcome = "complete" | "incomplete" | "failed" | "unavailable";
65
- /**
66
- * What a schedule waiting on a pull carries: how many times it has failed in a
67
- * row. Both the drain's own deadline and the container-idle deferral that waits
68
- * for one hold it, since both have to back off against the same fault.
69
- */
70
- export interface SyncDrainIntent {
71
- attempt: number;
72
- }
73
- export interface WorkspaceSyncDeps {
74
- workspace: () => Workspace;
75
- /** Whether a container is up. A drain must never start one — see {@link drain}. */
76
- containerRunning: () => boolean;
77
- /** Where the next block is scheduled. */
78
- deadline: () => Deadline<SyncDrainIntent | undefined>;
79
- tag: () => string;
80
- id: () => string;
81
- }
82
- export declare class WorkspaceSync {
83
- private readonly deps;
84
- constructor(deps: WorkspaceSyncDeps);
85
- /**
86
- * Come back shortly and move what is still outstanding.
87
- *
88
- * A deadline rather than an immediate drain, because the caller is usually
89
- * holding something that should not wait on a pull — an install's drain, or a
90
- * request. `set` replaces whatever stood before, so arming twice is one
91
- * schedule rather than two.
92
- */
93
- arm(attempt?: number): Promise<void>;
94
- /**
95
- * Move what the container has written into this object's storage.
96
- *
97
- * One block per `next()`, under a budget rather than to completion, because
98
- * the iterator is disposable by design: the durable state is the cursor the
99
- * block just committed, so stopping is a normal thing to do and a recreated
100
- * iterable picks up where this one stopped.
101
- *
102
- * **Never starts a container.** Opening a backend handle would launch one, and
103
- * a pull against a replacement finds an empty filesystem rather than the
104
- * writes it was after — the library fences that to the runtime that ran the
105
- * command, but launching a container to discover so is pure cost. A stopped
106
- * container also means there is nothing left to pull: whatever it held that
107
- * never arrived is gone, which is why a drain runs *before* an idle deadline
108
- * stops one.
109
- */
110
- drain(budgetMs: number): Promise<DrainOutcome>;
111
- }
@@ -1,124 +0,0 @@
1
- /**
2
- * The host's half of syncing: moving what the container wrote into the object.
3
- *
4
- * ## Why the host has a half at all
5
- *
6
- * Every command is bracketed by a sync — a push before it runs, a pull after it
7
- * exits — and the pull is what carries its writes back into this object's
8
- * SQLite. That pull can fail while the command itself succeeds: a transport that
9
- * dropped, a container replaced underneath it. The library reports that on the
10
- * command's result and schedules nothing of its own afterwards. What it does
11
- * keep is the cursor, so a later `pull()` resumes the same operation rather than
12
- * starting one.
13
- *
14
- * That later `pull()` is this. Without it the workspace silently lags the
15
- * container: the file tools read a tree missing the last command's work, and git
16
- * — which runs on this side — would commit and push it that way.
17
- */
18
- /**
19
- * How long one drain spends moving blocks before it comes back.
20
- *
21
- * The alarm is shared. Every other reason a workspace wakes is queued behind
22
- * whatever the alarm is doing, so a drain that ran to completion in one
23
- * invocation would hold the object for as long as the pull takes. A minute
24
- * finishes an ordinary pull outright and keeps a large one from starving the
25
- * install watchdog.
26
- */
27
- export const SYNC_DRAIN_BUDGET_MS = 60_000;
28
- /** How soon a drain that is making progress comes back for the next block. */
29
- export const SYNC_DRAIN_RESUME_MS = 1_000;
30
- /**
31
- * The ceiling on the wait after a pull that failed.
32
- *
33
- * A failure is not a slow success: whatever broke — a container that stopped
34
- * answering, a transport that will not open — is usually still broken a second
35
- * later, and coming back at the resume interval would be an alarm and an error
36
- * line every second for as long as the workspace stays up. Backing off turns
37
- * that into a handful of attempts an hour, which is the rate a recoverable
38
- * fault actually clears at.
39
- *
40
- * Bounded rather than abandoned, because the cursor is durable and the cost of
41
- * one more attempt is one round trip: a container that comes back should be
42
- * drained without anyone asking.
43
- */
44
- export const SYNC_DRAIN_MAX_BACKOFF_MS = 5 * 60_000;
45
- /** How long to wait before a retry, after this many consecutive failures. */
46
- export function syncRetryDelayMs(attempt) {
47
- return Math.min(SYNC_DRAIN_RESUME_MS * 2 ** Math.max(0, attempt), SYNC_DRAIN_MAX_BACKOFF_MS);
48
- }
49
- export class WorkspaceSync {
50
- deps;
51
- constructor(deps) {
52
- this.deps = deps;
53
- }
54
- /**
55
- * Come back shortly and move what is still outstanding.
56
- *
57
- * A deadline rather than an immediate drain, because the caller is usually
58
- * holding something that should not wait on a pull — an install's drain, or a
59
- * request. `set` replaces whatever stood before, so arming twice is one
60
- * schedule rather than two.
61
- */
62
- async arm(attempt = 0) {
63
- await this.deps
64
- .deadline()
65
- .set(new Date(Date.now() + syncRetryDelayMs(attempt)), { attempt });
66
- }
67
- /**
68
- * Move what the container has written into this object's storage.
69
- *
70
- * One block per `next()`, under a budget rather than to completion, because
71
- * the iterator is disposable by design: the durable state is the cursor the
72
- * block just committed, so stopping is a normal thing to do and a recreated
73
- * iterable picks up where this one stopped.
74
- *
75
- * **Never starts a container.** Opening a backend handle would launch one, and
76
- * a pull against a replacement finds an empty filesystem rather than the
77
- * writes it was after — the library fences that to the runtime that ran the
78
- * command, but launching a container to discover so is pure cost. A stopped
79
- * container also means there is nothing left to pull: whatever it held that
80
- * never arrived is gone, which is why a drain runs *before* an idle deadline
81
- * stops one.
82
- */
83
- async drain(budgetMs) {
84
- if (!this.deps.containerRunning())
85
- return "unavailable";
86
- const stopAt = Date.now() + budgetMs;
87
- let blocks = 0;
88
- let entries = 0;
89
- try {
90
- for await (const progress of this.deps.workspace().pull()) {
91
- blocks += 1;
92
- entries += progress.entries;
93
- if (progress.complete) {
94
- // Silent for the ordinary case — one empty block, which is what a
95
- // workspace with nothing outstanding returns on every check.
96
- if (blocks > 1 || entries > 0) {
97
- console.info(`[${this.deps.tag()}] pull drained`, {
98
- id: this.deps.id(),
99
- blocks,
100
- entries
101
- });
102
- }
103
- return "complete";
104
- }
105
- if (Date.now() >= stopAt)
106
- return "incomplete";
107
- }
108
- // The iterable ended without a block claiming completion, which is what a
109
- // backend with nothing to sync returns. Nothing is outstanding either way.
110
- return "complete";
111
- }
112
- catch (err) {
113
- // Left outstanding rather than retried here: the cursor is durable, so the
114
- // next drain resumes whatever this one committed before it threw.
115
- console.error(`[${this.deps.tag()}] a pull could not be drained`, {
116
- id: this.deps.id(),
117
- blocks,
118
- entries,
119
- err: String(err)
120
- });
121
- return "failed";
122
- }
123
- }
124
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * What a workspace object wakes for, as one map.
3
- *
4
- * Its own module because the object that registers these callbacks and the
5
- * `JobLifecycle` in `./install-job.ts` that schedules some of them by name both type
6
- * against it, and neither should own it. A name that exists in one and not the
7
- * other is then a compile error rather than a schedule that is rejected at
8
- * runtime.
9
- */
10
- import type { SyncDrainIntent } from "./sync.js";
11
- export type WorkspaceWakeHandlers = {
12
- installRun: () => Promise<void>;
13
- installWatch: () => Promise<void>;
14
- idleReclaim: () => Promise<void>;
15
- /** Start a cold container before a command needs it — see `./object.ts`. */
16
- containerWarm: () => Promise<void>;
17
- containerIdle: (payload?: SyncDrainIntent) => Promise<void>;
18
- syncRetry: (payload?: SyncDrainIntent) => Promise<void>;
19
- };
@@ -1,10 +0,0 @@
1
- /**
2
- * What a workspace object wakes for, as one map.
3
- *
4
- * Its own module because the object that registers these callbacks and the
5
- * `JobLifecycle` in `./install-job.ts` that schedules some of them by name both type
6
- * against it, and neither should own it. A name that exists in one and not the
7
- * other is then a compile error rather than a schedule that is rejected at
8
- * runtime.
9
- */
10
- export {};
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes