@usine/executor 0.2.1 → 0.3.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 (177) hide show
  1. package/README.md +115 -37
  2. package/dist/child-command.d.ts +4 -7
  3. package/dist/child-command.d.ts.map +1 -1
  4. package/dist/child-command.js +6 -12
  5. package/dist/child-command.js.map +1 -1
  6. package/dist/claude-login-lock.d.ts +8 -0
  7. package/dist/claude-login-lock.d.ts.map +1 -0
  8. package/dist/claude-login-lock.js +9 -0
  9. package/dist/claude-login-lock.js.map +1 -0
  10. package/dist/credential-environment.d.ts +20 -0
  11. package/dist/credential-environment.d.ts.map +1 -0
  12. package/dist/credential-environment.js +20 -0
  13. package/dist/credential-environment.js.map +1 -0
  14. package/dist/durable-session.d.ts +63 -0
  15. package/dist/durable-session.d.ts.map +1 -0
  16. package/dist/durable-session.js +491 -0
  17. package/dist/durable-session.js.map +1 -0
  18. package/dist/exe-dev/commands.d.ts +23 -13
  19. package/dist/exe-dev/commands.d.ts.map +1 -1
  20. package/dist/exe-dev/commands.js +11 -57
  21. package/dist/exe-dev/commands.js.map +1 -1
  22. package/dist/exe-dev/exe-dev.d.ts +9 -3
  23. package/dist/exe-dev/exe-dev.d.ts.map +1 -1
  24. package/dist/exe-dev/exe-dev.js +53 -128
  25. package/dist/exe-dev/exe-dev.js.map +1 -1
  26. package/dist/exe-dev/grants.d.ts.map +1 -1
  27. package/dist/exe-dev/grants.js +35 -15
  28. package/dist/exe-dev/grants.js.map +1 -1
  29. package/dist/exe-dev/model.d.ts +15 -19
  30. package/dist/exe-dev/model.d.ts.map +1 -1
  31. package/dist/exe-dev/model.js +9 -12
  32. package/dist/exe-dev/model.js.map +1 -1
  33. package/dist/exe-dev/preflight.d.ts +8 -4
  34. package/dist/exe-dev/preflight.d.ts.map +1 -1
  35. package/dist/exe-dev/preflight.js +23 -33
  36. package/dist/exe-dev/preflight.js.map +1 -1
  37. package/dist/executor.d.ts +140 -41
  38. package/dist/executor.d.ts.map +1 -1
  39. package/dist/executor.js +90 -19
  40. package/dist/executor.js.map +1 -1
  41. package/dist/guard.d.ts +57 -7
  42. package/dist/guard.d.ts.map +1 -1
  43. package/dist/guard.js +273 -59
  44. package/dist/guard.js.map +1 -1
  45. package/dist/harness-preflight.d.ts +25 -4
  46. package/dist/harness-preflight.d.ts.map +1 -1
  47. package/dist/harness-preflight.js +25 -29
  48. package/dist/harness-preflight.js.map +1 -1
  49. package/dist/keyed-lock.d.ts +2 -3
  50. package/dist/keyed-lock.d.ts.map +1 -1
  51. package/dist/keyed-lock.js +2 -3
  52. package/dist/keyed-lock.js.map +1 -1
  53. package/dist/leftovers.d.ts +9 -0
  54. package/dist/leftovers.d.ts.map +1 -0
  55. package/dist/leftovers.js +14 -0
  56. package/dist/leftovers.js.map +1 -0
  57. package/dist/local/credentials.d.ts +21 -0
  58. package/dist/local/credentials.d.ts.map +1 -0
  59. package/dist/local/credentials.js +39 -0
  60. package/dist/local/credentials.js.map +1 -0
  61. package/dist/local/leftovers.d.ts +8 -0
  62. package/dist/local/leftovers.d.ts.map +1 -0
  63. package/dist/local/leftovers.js +23 -0
  64. package/dist/local/leftovers.js.map +1 -0
  65. package/dist/local/local.d.ts +16 -10
  66. package/dist/local/local.d.ts.map +1 -1
  67. package/dist/local/local.js +163 -129
  68. package/dist/local/local.js.map +1 -1
  69. package/dist/memory-preflight.d.ts +25 -0
  70. package/dist/memory-preflight.d.ts.map +1 -0
  71. package/dist/memory-preflight.js +83 -0
  72. package/dist/memory-preflight.js.map +1 -0
  73. package/dist/pre-push-hook.d.ts +20 -0
  74. package/dist/pre-push-hook.d.ts.map +1 -0
  75. package/dist/pre-push-hook.js +33 -0
  76. package/dist/pre-push-hook.js.map +1 -0
  77. package/dist/prepared-attempt.d.ts +33 -3
  78. package/dist/prepared-attempt.d.ts.map +1 -1
  79. package/dist/prepared-attempt.js +109 -31
  80. package/dist/prepared-attempt.js.map +1 -1
  81. package/dist/provision.d.ts +77 -14
  82. package/dist/provision.d.ts.map +1 -1
  83. package/dist/provision.js +357 -55
  84. package/dist/provision.js.map +1 -1
  85. package/dist/remote/commands.d.ts +51 -0
  86. package/dist/remote/commands.d.ts.map +1 -0
  87. package/dist/remote/commands.js +94 -0
  88. package/dist/remote/commands.js.map +1 -0
  89. package/dist/remote/leftovers.d.ts +14 -0
  90. package/dist/remote/leftovers.d.ts.map +1 -0
  91. package/dist/remote/leftovers.js +22 -0
  92. package/dist/remote/leftovers.js.map +1 -0
  93. package/dist/remote/liveness.d.ts +33 -0
  94. package/dist/remote/liveness.d.ts.map +1 -0
  95. package/dist/remote/liveness.js +194 -0
  96. package/dist/remote/liveness.js.map +1 -0
  97. package/dist/remote/machine.d.ts +64 -0
  98. package/dist/remote/machine.d.ts.map +1 -0
  99. package/dist/remote/machine.js +2 -0
  100. package/dist/remote/machine.js.map +1 -0
  101. package/dist/remote/remote.d.ts +24 -0
  102. package/dist/remote/remote.d.ts.map +1 -0
  103. package/dist/remote/remote.js +308 -0
  104. package/dist/remote/remote.js.map +1 -0
  105. package/dist/remote/sweep.d.ts +15 -0
  106. package/dist/remote/sweep.d.ts.map +1 -0
  107. package/dist/remote/sweep.js +116 -0
  108. package/dist/remote/sweep.js.map +1 -0
  109. package/dist/renewable-credential.d.ts +17 -0
  110. package/dist/renewable-credential.d.ts.map +1 -0
  111. package/dist/renewable-credential.js +58 -0
  112. package/dist/renewable-credential.js.map +1 -0
  113. package/dist/salvage.d.ts +30 -0
  114. package/dist/salvage.d.ts.map +1 -0
  115. package/dist/salvage.js +104 -0
  116. package/dist/salvage.js.map +1 -0
  117. package/dist/ssh/credentials.d.ts +40 -0
  118. package/dist/ssh/credentials.d.ts.map +1 -0
  119. package/dist/ssh/credentials.js +83 -0
  120. package/dist/ssh/credentials.js.map +1 -0
  121. package/dist/ssh/gateway/admin.d.ts +45 -0
  122. package/dist/ssh/gateway/admin.d.ts.map +1 -0
  123. package/dist/ssh/gateway/admin.js +129 -0
  124. package/dist/ssh/gateway/admin.js.map +1 -0
  125. package/dist/ssh/gateway/attachments.d.ts +22 -0
  126. package/dist/ssh/gateway/attachments.d.ts.map +1 -0
  127. package/dist/ssh/gateway/attachments.js +154 -0
  128. package/dist/ssh/gateway/attachments.js.map +1 -0
  129. package/dist/ssh/gateway/environment.d.ts +52 -0
  130. package/dist/ssh/gateway/environment.d.ts.map +1 -0
  131. package/dist/ssh/gateway/environment.js +78 -0
  132. package/dist/ssh/gateway/environment.js.map +1 -0
  133. package/dist/ssh/gateway/forward.d.ts +74 -0
  134. package/dist/ssh/gateway/forward.d.ts.map +1 -0
  135. package/dist/ssh/gateway/forward.js +247 -0
  136. package/dist/ssh/gateway/forward.js.map +1 -0
  137. package/dist/ssh/gateway/preflight.d.ts +28 -0
  138. package/dist/ssh/gateway/preflight.d.ts.map +1 -0
  139. package/dist/ssh/gateway/preflight.js +234 -0
  140. package/dist/ssh/gateway/preflight.js.map +1 -0
  141. package/dist/ssh/model.d.ts +65 -0
  142. package/dist/ssh/model.d.ts.map +1 -0
  143. package/dist/ssh/model.js +2 -0
  144. package/dist/ssh/model.js.map +1 -0
  145. package/dist/ssh/preflight.d.ts +16 -0
  146. package/dist/ssh/preflight.d.ts.map +1 -0
  147. package/dist/ssh/preflight.js +47 -0
  148. package/dist/ssh/preflight.js.map +1 -0
  149. package/dist/ssh/ssh.d.ts +20 -0
  150. package/dist/ssh/ssh.d.ts.map +1 -0
  151. package/dist/ssh/ssh.js +78 -0
  152. package/dist/ssh/ssh.js.map +1 -0
  153. package/dist/ssh/workers.d.ts +53 -0
  154. package/dist/ssh/workers.d.ts.map +1 -0
  155. package/dist/ssh/workers.js +173 -0
  156. package/dist/ssh/workers.js.map +1 -0
  157. package/package.json +12 -4
  158. package/dist/exe-dev/channel.d.ts +0 -12
  159. package/dist/exe-dev/channel.d.ts.map +0 -1
  160. package/dist/exe-dev/channel.js +0 -42
  161. package/dist/exe-dev/channel.js.map +0 -1
  162. package/dist/exe-dev/shell.d.ts +0 -12
  163. package/dist/exe-dev/shell.d.ts.map +0 -1
  164. package/dist/exe-dev/shell.js +0 -31
  165. package/dist/exe-dev/shell.js.map +0 -1
  166. package/dist/exe-dev/sweep.d.ts +0 -10
  167. package/dist/exe-dev/sweep.d.ts.map +0 -1
  168. package/dist/exe-dev/sweep.js +0 -117
  169. package/dist/exe-dev/sweep.js.map +0 -1
  170. package/dist/invalid-frame.d.ts +0 -17
  171. package/dist/invalid-frame.d.ts.map +0 -1
  172. package/dist/invalid-frame.js +0 -111
  173. package/dist/invalid-frame.js.map +0 -1
  174. package/dist/run-host-session.d.ts +0 -27
  175. package/dist/run-host-session.d.ts.map +0 -1
  176. package/dist/run-host-session.js +0 -303
  177. package/dist/run-host-session.js.map +0 -1
@@ -0,0 +1,308 @@
1
+ import { CHILD_KILL_SCRIPT } from "@usine/agents/child-host";
2
+ import { ClaudeLoginLock } from "@usine/agents/runtime";
3
+ import { InfraFailure } from "@usine/core/attempt/failure";
4
+ import { RepositoryName, safeSegment } from "@usine/core/repository";
5
+ import { harvestFacts, harvestPostedComments, } from "@usine/core/step/harvest";
6
+ import { AttemptRuntime } from "@usine/durable/attempt-runtime";
7
+ import { Deferred, Effect, Exit, Option, Schema, Scope } from "effect";
8
+ import { durableSessionFor } from "../durable-session.js";
9
+ import { CheckoutRequest, Executor, ExecutorError, ExecutorOperationError, WorkerCapabilityError, } from "../executor.js";
10
+ import { verifyHarnessReadiness } from "../harness-preflight.js";
11
+ import { keyedLockFor } from "../keyed-lock.js";
12
+ import { withGitHooks } from "../pre-push-hook.js";
13
+ import { cacheDirectoryName, provisionWorktree, reattachWorktree, recoverWorktree, } from "../provision.js";
14
+ import { workerLeftoversFor } from "./leftovers.js";
15
+ import { workerWatchesFor } from "./liveness.js";
16
+ import { abandonedWorktreeSweepFor } from "./sweep.js";
17
+ const isExecutorError = Schema.is(ExecutorError);
18
+ const executorError = (message, cause) => new ExecutorOperationError({ operation: "prepare", message, cause });
19
+ /** A script that writes `$2` to the file `$1`, creating its directory, and makes it executable. */
20
+ const WRITE_EXECUTABLE_SCRIPT = String.raw `
21
+ set -e
22
+ mkdir -p "$(dirname "$1")"
23
+ printf '%s' "$2" > "$1"
24
+ chmod 755 "$1"
25
+ `.trim();
26
+ /**
27
+ * Returns the executor for Workers accessed over SSH. Each Attempt's Pi parent is a durable task
28
+ * in this process, and each child runs on the machine as one SSH command with a pid file next
29
+ * to the worktree. `worker` provides the machine, preflight, and repository access.
30
+ */
31
+ export const remoteExecutorFor = (worker) => Effect.gen(function* () {
32
+ const { commands, label } = worker;
33
+ const span = (name) => `${worker.spanPrefix}.${name}`;
34
+ const fetches = yield* keyedLockFor();
35
+ const logins = yield* ClaudeLoginLock;
36
+ const sweepAbandonedWorktrees = yield* abandonedWorktreeSweepFor(commands);
37
+ const workerLeftovers = yield* workerLeftoversFor(commands, worker.machineFor, worker.reclaim);
38
+ const session = yield* durableSessionFor({ interruptGrace: worker.interruptGrace });
39
+ const runtime = yield* AttemptRuntime;
40
+ const workerWatches = yield* workerWatchesFor(commands);
41
+ const stopChildren = Effect.fn(span("stopChildren"))(function* (operation, machine, worktree) {
42
+ const host = machine.shell.destination;
43
+ yield* Effect.annotateCurrentSpan({ host, worktree });
44
+ const outcome = yield* commands.run(operation, machine.shell, "sh", [
45
+ "-c",
46
+ CHILD_KILL_SCRIPT,
47
+ "sh",
48
+ worktree,
49
+ ]);
50
+ yield* Effect.logInfo("Confirmed no child of the Attempt runs on the Worker's machine", {
51
+ host,
52
+ worktree,
53
+ children: outcome.trim().split("\n"),
54
+ });
55
+ });
56
+ /** Runs git on the machine with the Attempt's repository access, refreshing the credential first if it's about to expire. */
57
+ const accessGit = (operation, access) => (args) => access.refresh.pipe(Effect.andThen(commands.run(operation, access.shell, "git", args, {
58
+ environment: access.gitEnvironment,
59
+ scrub: access.scrub,
60
+ })));
61
+ const harvestCommand = (operation, worktree, access) => (command, args) => access.refresh
62
+ .pipe(Effect.andThen(commands.run(operation, access.shell, command, args, {
63
+ cwd: worktree,
64
+ environment: access.environment,
65
+ scrub: access.scrub,
66
+ })))
67
+ .pipe(Effect.map((stdout) => (stdout === "" ? [] : stdout.replace(/\n$/u, "").split("\n"))));
68
+ const recover = Effect.fn(span("recover"))(function* (attempt, record) {
69
+ yield* Effect.annotateCurrentSpan({ ...attempt });
70
+ yield* session.stopForRecovery(attempt);
71
+ const { machine, checkouts } = yield* workerLeftovers(attempt.worker);
72
+ const worktree = `${machine.workerDirectory}/attempts/${safeSegment(attempt.attemptId)}`;
73
+ const inspectCheckout = Effect.gen(function* () {
74
+ const inspected = yield* commands.inspect("cleanup", machine.shell, "test", [
75
+ "-d",
76
+ worktree,
77
+ ]);
78
+ if (inspected.exitCode === 1)
79
+ return false;
80
+ if (inspected.exitCode !== 0)
81
+ return yield* executorError("Can't inspect the recovery checkout", new Error(inspected.stderr));
82
+ return true;
83
+ });
84
+ // Checks the cached listing first to avoid an SSH round trip per persisted Attempt.
85
+ const present = checkouts.has(worktree) && (yield* inspectCheckout);
86
+ yield* session.recoverPostedComments(attempt, present
87
+ ? Option.some((spec, windows) => Effect.scoped(Effect.gen(function* () {
88
+ const grant = yield* worker.grantFor(attempt.repository);
89
+ const access = yield* grant.acquire(attempt.attemptId, attempt.worker, attempt.permissions, machine, "cleanup");
90
+ return yield* harvestPostedComments(spec, harvestCommand("cleanup", worktree, access), windows);
91
+ })))
92
+ : Option.none(), record);
93
+ if (!present)
94
+ return;
95
+ const reconcile = (git) => recoverWorktree({
96
+ attempt,
97
+ host: machine.shell.destination,
98
+ worktree,
99
+ cache: `${machine.workerDirectory}/repos/${cacheDirectoryName(attempt.repository)}`,
100
+ git,
101
+ }, record.salvage);
102
+ if (attempt.succeeded || attempt.permissions === "read") {
103
+ yield* stopChildren("cleanup", machine, worktree);
104
+ yield* reconcile((args) => commands.run("cleanup", machine.shell, "git", args));
105
+ return;
106
+ }
107
+ const grant = yield* worker.grantFor(attempt.repository);
108
+ yield* Effect.scoped(Effect.gen(function* () {
109
+ const access = yield* grant.acquire(attempt.attemptId, attempt.worker, attempt.permissions, machine, "cleanup");
110
+ yield* stopChildren("cleanup", machine, worktree);
111
+ yield* reconcile(accessGit("cleanup", access));
112
+ }));
113
+ });
114
+ const harvest = (workspace, access, windows) => harvestFacts(workspace.spec, harvestCommand("launch", workspace.worktree, access), windows).pipe(Effect.mapError((error) => new InfraFailure({
115
+ message: `Can't harvest the attempt's checkout: ${error.message}`,
116
+ cause: error,
117
+ })));
118
+ /**
119
+ * Checks that a reattached Attempt's parent task was created for this worktree and the
120
+ * worktree still exists, then stops children left from before the restart. Returns the
121
+ * parent's RunSpec.
122
+ */
123
+ const takeOver = Effect.fn(span("takeOver"))(function* (request, machine, worktree) {
124
+ const { cwd, spec } = yield* session.storedRequest(request.attemptId);
125
+ if (cwd !== worktree)
126
+ return yield* executorError(`The reattached attempt's checkout isn't on the machine of this ${label} worker`, new Error(cwd));
127
+ const inspected = yield* commands.inspect("prepare", machine.shell, "test", ["-d", worktree]);
128
+ if (inspected.exitCode === 1)
129
+ return yield* executorError("The reattached attempt's checkout no longer exists", new Error(worktree));
130
+ if (inspected.exitCode !== 0)
131
+ return yield* executorError("Can't inspect the reattached attempt's checkout", new Error(inspected.stderr.trim()));
132
+ yield* stopChildren("prepare", machine, worktree);
133
+ return spec;
134
+ });
135
+ /** Returns the `WorkspaceOps` for the machine; git runs with the Attempt's repository access. */
136
+ const workspaceOps = (request, machine, access, layout) => ({
137
+ ensureRoots: Effect.asVoid(commands.run("prepare", machine.shell, "mkdir", [
138
+ "-p",
139
+ `${machine.workerDirectory}/repos`,
140
+ `${machine.workerDirectory}/attempts`,
141
+ ])),
142
+ removePath: (operation, target) => Effect.asVoid(commands.run(operation, machine.shell, "rm", ["-rf", target])),
143
+ cacheExists: Effect.gen(function* () {
144
+ const inspected = yield* commands.inspect("prepare", machine.shell, "test", [
145
+ "-d",
146
+ layout.cache,
147
+ ]);
148
+ if (inspected.exitCode === 0)
149
+ return true;
150
+ if (inspected.exitCode === 1)
151
+ return false;
152
+ return yield* executorError(`Can't inspect the cached clone on worker "${request.worker}"`, new Error(inspected.stderr.trim()));
153
+ }),
154
+ promoteStaging: Effect.asVoid(commands.run("prepare", machine.shell, "mv", [layout.staging, layout.cache])),
155
+ git: (operation, args) => accessGit(operation, access)(args),
156
+ writeExecutable: (target, content) => Effect.asVoid(commands.run("prepare", machine.shell, "sh", [
157
+ "-c",
158
+ WRITE_EXECUTABLE_SCRIPT,
159
+ "sh",
160
+ target,
161
+ content,
162
+ ])),
163
+ });
164
+ const prepare = Effect.fn(span("prepare"))(function* (request) {
165
+ yield* Effect.annotateCurrentSpan({
166
+ attemptId: request.attemptId,
167
+ worker: request.worker,
168
+ repository: request.repository,
169
+ loop: request.loop,
170
+ permissions: request.permissions,
171
+ reattach: request.reattach,
172
+ });
173
+ if (request.permissions === "read" && Option.isSome(worker.readOnlyAccessGap)) {
174
+ const message = `${label} worker "${request.worker}" can't run loop "${request.loop}", which has permissions: read. ${worker.readOnlyAccessGap.value}.`;
175
+ yield* Effect.logWarning("Refused an Attempt that needs read-only repository access", {
176
+ attemptId: request.attemptId,
177
+ worker: request.worker,
178
+ loop: request.loop,
179
+ permissions: request.permissions,
180
+ message,
181
+ });
182
+ return yield* new WorkerCapabilityError({
183
+ worker: request.worker,
184
+ capability: "credentials",
185
+ message,
186
+ cause: new Error(worker.readOnlyAccessGap.value),
187
+ });
188
+ }
189
+ const grant = yield* worker.grantFor(request.repository);
190
+ const machine = yield* worker.machineFor(request.worker);
191
+ yield* verifyHarnessReadiness(request.worker, [request.agent.brain], worker.harnessProbe(machine), logins);
192
+ const host = machine.shell.destination;
193
+ const cache = `${machine.workerDirectory}/repos/${cacheDirectoryName(request.repository)}`;
194
+ const worktree = `${machine.workerDirectory}/attempts/${safeSegment(request.attemptId)}`;
195
+ const staging = `${cache}.cloning`;
196
+ yield* Effect.annotateCurrentSpan({ host });
197
+ yield* Effect.logInfo(request.reattach
198
+ ? `Reattaching the Attempt to its ${label} checkout`
199
+ : `Preparing ${label} Attempt workspace`, {
200
+ attemptId: request.attemptId,
201
+ worker: request.worker,
202
+ host,
203
+ repository: request.repository,
204
+ loop: request.loop,
205
+ permissions: request.permissions,
206
+ worktree,
207
+ });
208
+ yield* sweepAbandonedWorktrees(request.attemptId, request.worker, machine.shell, machine.workerDirectory);
209
+ // A reattachment reuses its checkout before any credential is granted.
210
+ const reattached = request.reattach
211
+ ? Option.some(yield* takeOver(request, machine, worktree))
212
+ : Option.none();
213
+ // A checkout kept after a failed remote stop never closes its scope, so its salvage result
214
+ // is completed here instead of by the checkout finalizer.
215
+ const salvaged = yield* Deferred.make();
216
+ const releaseWorkspace = Effect.fn(span("releaseWorkspace"))(function* (resource, exit) {
217
+ yield* Effect.annotateCurrentSpan({
218
+ attemptId: request.attemptId,
219
+ worker: request.worker,
220
+ host,
221
+ });
222
+ // Closes the checkout scope only if stopping the remote children succeeds.
223
+ yield* stopChildren("cleanup", machine, resource.provisioned.workspace.worktree);
224
+ yield* Scope.close(resource.scope, exit);
225
+ const salvage = yield* Effect.exit(resource.provisioned.salvaged);
226
+ yield* Deferred.done(salvaged, salvage);
227
+ }, (effect) => effect.pipe(Effect.tapError((error) => Effect.logError("Remote termination failed; Attempt checkout retained for recovery", {
228
+ attemptId: request.attemptId,
229
+ worker: request.worker,
230
+ host,
231
+ worktree,
232
+ message: error.message,
233
+ })), Effect.ignore, Effect.andThen(Deferred.succeed(salvaged, Option.none()))));
234
+ const acquire = Effect.gen(function* () {
235
+ // Credentials are scoped to the Attempt, not the checkout, so they are released exactly once
236
+ // even when checkout cleanup is skipped.
237
+ const access = yield* grant.acquire(request.attemptId, request.worker, request.permissions, machine, "prepare");
238
+ const ops = workspaceOps(request, machine, access, { cache, staging });
239
+ const checkout = Option.match(reattached, {
240
+ onNone: () => provisionWorktree({
241
+ request,
242
+ url: worker.repositoryUrl(machine, request.repository),
243
+ host,
244
+ layout: { cache, staging, worktree },
245
+ ops,
246
+ }),
247
+ onSome: (spec) => reattachWorktree({ request, host, layout: { cache, worktree }, ops, spec }),
248
+ });
249
+ const { provisioned } = yield* Effect.acquireRelease(Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
250
+ const scope = yield* Scope.make();
251
+ // No child can exist before preparation succeeds, so a partial checkout is always rolled
252
+ // back; only a completed workspace uses the guarded release above.
253
+ const provisioned = yield* restore(checkout).pipe(Scope.provide(scope), Effect.onError((cause) => Scope.close(scope, Exit.failCause(cause))));
254
+ return { provisioned, scope };
255
+ })), releaseWorkspace, { interruptible: true });
256
+ return { provisioned, access };
257
+ });
258
+ const { provisioned, access } = yield* (request.reattach ? acquire : fetches.withLock(request.repository)(acquire)).pipe(Effect.mapError((cause) => isExecutorError(cause)
259
+ ? cause
260
+ : executorError(`Can't prepare the ${label} attempt workspace`, cause)));
261
+ const prepared = provisioned.workspace;
262
+ yield* Effect.logInfo(`${label} Attempt workspace ready`, {
263
+ attemptId: request.attemptId,
264
+ worker: request.worker,
265
+ host,
266
+ repository: request.repository,
267
+ baseCommit: prepared.spec.baseCommit,
268
+ resume: Option.map(prepared.spec.resume, ({ ref }) => ref),
269
+ reattach: request.reattach,
270
+ worktree,
271
+ });
272
+ return {
273
+ workspace: prepared,
274
+ launch: (io) => Effect.scoped(Effect.gen(function* () {
275
+ // Uses the RunSpec's brain, including on reattachment. Each child reads the kind when it
276
+ // starts, so a preflight during the Attempt takes effect.
277
+ const gatewayCredential = worker.gatewayCredential(machine, prepared.spec.agent.brain.harness);
278
+ yield* workerWatches.watch({
279
+ attemptId: request.attemptId,
280
+ worker: request.worker,
281
+ shell: machine.shell,
282
+ report: (notice) => runtime.notify(request.attemptId, notice),
283
+ });
284
+ return yield* session.launch(prepared, io, {
285
+ host: access.shell,
286
+ environment: withGitHooks(access.environment, provisioned.gitHooks),
287
+ gatewayCredential,
288
+ harvest: (windows) => harvest(prepared, access, windows),
289
+ harvestComments: (windows) => harvestPostedComments(prepared.spec, harvestCommand("launch", prepared.worktree, access), windows),
290
+ keepForReattach: provisioned.keepForReattach,
291
+ });
292
+ })),
293
+ salvaged: Deferred.await(salvaged),
294
+ recordSalvage: provisioned.recordSalvage,
295
+ };
296
+ }, session.prepare);
297
+ return Executor.of({
298
+ kind: worker.kind,
299
+ recover,
300
+ cancel: session.cancel,
301
+ intervene: session.intervene,
302
+ isLive: session.isLive,
303
+ outcome: session.outcome,
304
+ preflight: worker.preflight,
305
+ prepare,
306
+ });
307
+ });
308
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/remote/remote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { RemoteShell } from "@usine/agents/child-host";
2
+ import { Effect } from "effect";
3
+ import { ExecutorOperationError } from "../executor.ts";
4
+ import type { RemoteCommands } from "./commands.ts";
5
+ /**
6
+ * Lists paths directly under a Worker's `attempts` directory, creating it first so a new Worker
7
+ * lists as empty. Verifies that each path is a direct child of `attemptsRoot`.
8
+ */
9
+ export declare const attemptPathsOn: (commands: RemoteCommands, operation: "cancel" | "cleanup" | "launch" | "preflight" | "prepare", shell: RemoteShell, attemptsRoot: string) => Effect.Effect<readonly string[], ExecutorOperationError, never>;
10
+ /**
11
+ * Returns a sweep that prunes stale cache registrations once per Worker and keeps crash
12
+ * leftovers. Only durable Attempt recovery removes an existing checkout.
13
+ */
14
+ export declare const abandonedWorktreeSweepFor: (commands: RemoteCommands) => Effect.Effect<(attemptId: string & import("effect/Brand").Brand<"AttemptId">, worker: string & import("effect/Brand").Brand<"WorkerName">, shell: RemoteShell, workerDirectory: string) => Effect.Effect<void, never, never>, never, never>;
15
+ //# sourceMappingURL=sweep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.d.ts","sourceRoot":"","sources":["../../src/remote/sweep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAA0B,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAyCpD;;;GAGG;AACH,eAAO,MAAM,cAAc,+MAiBzB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,yBAAyB,2QA0GrC,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { AttemptId } from "@usine/core/attempt";
2
+ import { WorkerName } from "@usine/core/worker";
3
+ import { Effect, Ref, Schema } from "effect";
4
+ import { ExecutorOperationError } from "../executor.js";
5
+ import { keyedLockFor } from "../keyed-lock.js";
6
+ const SWEEP_CONCURRENCY = 4;
7
+ const directChildSchema = (parent) => {
8
+ const prefix = `${parent}/`;
9
+ return Schema.String.check(Schema.makeFilter((value) => {
10
+ const segment = value.slice(prefix.length);
11
+ return (value.startsWith(prefix) &&
12
+ segment.length > 0 &&
13
+ segment !== "." &&
14
+ segment !== ".." &&
15
+ !segment.includes("/"));
16
+ }, { message: `Expected a direct child of ${parent}` }));
17
+ };
18
+ const directChildren = (operation, parent, output) => Schema.decodeUnknownEffect(Schema.Array(directChildSchema(parent)))(output.split("\0").filter((value) => value.length > 0)).pipe(Effect.mapError((cause) => new ExecutorOperationError({
19
+ operation,
20
+ message: `Worker returned an invalid path while listing ${parent}`,
21
+ cause,
22
+ })));
23
+ /**
24
+ * Lists paths directly under a Worker's `attempts` directory, creating it first so a new Worker
25
+ * lists as empty. Verifies that each path is a direct child of `attemptsRoot`.
26
+ */
27
+ export const attemptPathsOn = Effect.fn("RemoteExecutor.attemptPathsOn")(function* (commands, operation, shell, attemptsRoot) {
28
+ yield* Effect.annotateCurrentSpan({ host: shell.destination, operation, attemptsRoot });
29
+ yield* commands.run(operation, shell, "mkdir", ["-p", attemptsRoot]);
30
+ const output = yield* commands.run(operation, shell, "find", [
31
+ attemptsRoot,
32
+ "-mindepth",
33
+ "1",
34
+ "-maxdepth",
35
+ "1",
36
+ "-print0",
37
+ ]);
38
+ return yield* directChildren(operation, attemptsRoot, output);
39
+ });
40
+ /**
41
+ * Returns a sweep that prunes stale cache registrations once per Worker and keeps crash
42
+ * leftovers. Only durable Attempt recovery removes an existing checkout.
43
+ */
44
+ export const abandonedWorktreeSweepFor = Effect.fn("RemoteExecutor.abandonedWorktreeSweepFor")(function* (commands) {
45
+ const locks = yield* keyedLockFor();
46
+ const swept = yield* Ref.make(new Set());
47
+ const sweep = Effect.fn("RemoteExecutor.sweepAbandonedWorktrees")(function* (attemptId, worker, shell, workerDirectory) {
48
+ const host = shell.destination;
49
+ yield* Effect.annotateCurrentSpan({ attemptId, worker, host });
50
+ yield* locks.withLock(worker)(Effect.gen(function* () {
51
+ if ((yield* Ref.get(swept)).has(worker)) {
52
+ yield* Effect.logDebug("Remote abandoned-worktree sweep was already complete", {
53
+ attemptId,
54
+ worker,
55
+ host,
56
+ });
57
+ return;
58
+ }
59
+ // Marked done before running so a failed sweep is not retried before every prepare; the
60
+ // next process start retries it.
61
+ yield* Ref.update(swept, (current) => new Set(current).add(worker));
62
+ const attemptsRoot = `${workerDirectory}/attempts`;
63
+ const reposRoot = `${workerDirectory}/repos`;
64
+ yield* Effect.logInfo("Sweeping abandoned Attempt worktrees on the remote Worker", {
65
+ attemptId,
66
+ worker,
67
+ host,
68
+ });
69
+ yield* commands.run("prepare", shell, "mkdir", ["-p", reposRoot]);
70
+ const attempts = yield* attemptPathsOn(commands, "prepare", shell, attemptsRoot);
71
+ // An unrecognized path can contain unsalvaged work from a crash; only recovery from the
72
+ // durable Attempt record removes it.
73
+ yield* Effect.forEach(attempts, (worktree) => Effect.logWarning("Preserving abandoned remote path for salvage reconciliation", {
74
+ attemptId,
75
+ worker,
76
+ host,
77
+ worktree,
78
+ }), { discard: true });
79
+ const caches = yield* commands
80
+ .run("prepare", shell, "find", [
81
+ reposRoot,
82
+ "-mindepth",
83
+ "1",
84
+ "-maxdepth",
85
+ "1",
86
+ "-type",
87
+ "d",
88
+ "-name",
89
+ "*.git",
90
+ "-print0",
91
+ ])
92
+ .pipe(Effect.flatMap((output) => directChildren("prepare", reposRoot, output)));
93
+ yield* Effect.forEach(caches, (cache) => commands.run("prepare", shell, "git", ["--git-dir", cache, "worktree", "prune"]).pipe(Effect.tapError((error) => Effect.logWarning("Could not prune a remote cache during the sweep", {
94
+ attemptId,
95
+ worker,
96
+ host,
97
+ cache,
98
+ message: error.message,
99
+ })), Effect.ignore), { concurrency: SWEEP_CONCURRENCY, discard: true });
100
+ yield* Effect.logInfo("Remote abandoned-worktree sweep completed", {
101
+ attemptId,
102
+ worker,
103
+ host,
104
+ retained: attempts.length,
105
+ caches: caches.length,
106
+ });
107
+ }).pipe(Effect.tapError((error) => Effect.logWarning("Remote abandoned-worktree sweep failed; continuing", {
108
+ attemptId,
109
+ worker,
110
+ host,
111
+ message: error.message,
112
+ })), Effect.ignore));
113
+ });
114
+ return sweep;
115
+ });
116
+ //# sourceMappingURL=sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.js","sourceRoot":"","sources":["../../src/remote/sweep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AttemptId } from "@usine/core/attempt";
2
+ import type { LoopPermissions } from "@usine/core/loop";
3
+ import type { RepositoryName } from "@usine/core/repository";
4
+ import { Effect } from "effect";
5
+ import type { CredentialUnavailableError, WorkerCredential } from "./executor.ts";
6
+ /**
7
+ * Issues the Attempt's single-repository token with its Loop `permissions`, passes it to
8
+ * `publish`, and publishes a new one before each expiry until the caller's scope closes. Then
9
+ * wipes every issued token, so `Redacted.value` throws. Register cleanup for what `publish`
10
+ * writes before calling this, so the cleanup runs after renewal stops.
11
+ */
12
+ export declare const renewableCredential: <A, E>(issue: (repository: RepositoryName, attemptId: AttemptId, permissions: LoopPermissions) => Effect.Effect<WorkerCredential, CredentialUnavailableError>, repository: string & import("effect/Brand").Brand<"RepositoryName">, attemptId: string & import("effect/Brand").Brand<"AttemptId">, permissions: "read" | "write", publish: (credential: WorkerCredential) => Effect.Effect<A, E>) => Effect.Effect<{
13
+ published: A;
14
+ refresh: () => Effect.Effect<void, E | CredentialUnavailableError, never>;
15
+ scrub: (text: string) => string;
16
+ }, E | CredentialUnavailableError, import("effect/Scope").Scope>;
17
+ //# sourceMappingURL=renewable-credential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renewable-credential.d.ts","sourceRoot":"","sources":["../src/renewable-credential.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAmB,MAAM,EAA6B,MAAM,QAAQ,CAAC;AAC5E,OAAO,KAAK,EAEV,0BAA0B,EAC1B,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAcvB;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAwD,CAAC,EAAE,CAAC,mVAKpE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;;;kBAoD9C,MAAM,KAAG,MAAM;gEAK/B,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { Clock, DateTime, Effect, Redacted, SynchronizedRef } from "effect";
2
+ /** The remaining lifetime, in milliseconds, below which a new token is issued. */
3
+ const RENEWAL_WINDOW_MILLIS = 5 * 60_000;
4
+ /**
5
+ * Issues the Attempt's single-repository token with its Loop `permissions`, passes it to
6
+ * `publish`, and publishes a new one before each expiry until the caller's scope closes. Then
7
+ * wipes every issued token, so `Redacted.value` throws. Register cleanup for what `publish`
8
+ * writes before calling this, so the cleanup runs after renewal stops.
9
+ */
10
+ export const renewableCredential = Effect.fn("Executor.renewableCredential")(function* (issue, repository, attemptId, permissions, publish) {
11
+ yield* Effect.annotateCurrentSpan({ repository, attemptId, permissions });
12
+ const issued = new Set();
13
+ yield* Effect.addFinalizer(() => Effect.sync(() => {
14
+ for (const token of issued)
15
+ Redacted.wipeUnsafe(token);
16
+ }));
17
+ const publishFresh = Effect.fn("Executor.publishCredential")(function* () {
18
+ yield* Effect.annotateCurrentSpan({ repository, attemptId, permissions });
19
+ const credential = yield* issue(repository, attemptId, permissions);
20
+ issued.add(credential.value);
21
+ const published = yield* publish(credential);
22
+ yield* Effect.logInfo("Published renewed Attempt repository credential", {
23
+ repository,
24
+ attemptId,
25
+ permissions,
26
+ expiresAt: DateTime.formatIso(credential.expiresAt),
27
+ });
28
+ return { expiresAt: credential.expiresAt, published };
29
+ });
30
+ const first = yield* publishFresh();
31
+ const expiry = yield* SynchronizedRef.make(first.expiresAt);
32
+ const refresh = Effect.fn("Executor.refreshCredential")(function* () {
33
+ yield* Effect.annotateCurrentSpan({ repository, attemptId });
34
+ yield* SynchronizedRef.updateEffect(expiry, (expiresAt) => Effect.gen(function* () {
35
+ const now = yield* Clock.currentTimeMillis;
36
+ if (DateTime.toEpochMillis(expiresAt) - now > RENEWAL_WINDOW_MILLIS)
37
+ return expiresAt;
38
+ return (yield* publishFresh()).expiresAt;
39
+ }));
40
+ });
41
+ // Forked in a scope opened before the checkout finalizers, so renewal continues through
42
+ // salvage and stops before the published credential is removed.
43
+ yield* Effect.forever(Effect.sleep("30 seconds").pipe(Effect.andThen(refresh()), Effect.tapError((error) => Effect.logWarning("Attempt credential renewal failed; will retry", {
44
+ repository,
45
+ attemptId,
46
+ message: String(error),
47
+ })), Effect.ignore)).pipe(Effect.forkScoped);
48
+ return {
49
+ published: first.published,
50
+ refresh,
51
+ scrub: (text) => {
52
+ for (const token of issued)
53
+ text = text.replaceAll(Redacted.value(token), "[REDACTED]");
54
+ return text;
55
+ },
56
+ };
57
+ });
58
+ //# sourceMappingURL=renewable-credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renewable-credential.js","sourceRoot":"","sources":["../src/renewable-credential.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { AttemptId } from "@usine/core/attempt";
2
+ import { type SalvageRef, SalvagePushed, SalvageRetained } from "@usine/core/attempt/salvage";
3
+ import { CommitSha } from "@usine/core/repository";
4
+ import type { WorkerName } from "@usine/core/worker";
5
+ import { Effect, Option } from "effect";
6
+ /** The git identity for commits usine creates in an Attempt's worktree. */
7
+ export declare const SALVAGE_GIT_CONFIG: string[];
8
+ export interface SalvageOptions<E extends Error> {
9
+ readonly attemptId: AttemptId;
10
+ readonly worker: WorkerName;
11
+ readonly host: string;
12
+ readonly worktree: string;
13
+ readonly baseCommit: Option.Option<CommitSha>;
14
+ readonly ref: SalvageRef;
15
+ /** Runs git with the Worker's repository credentials; `salvageWorktree` prepends `-C <worktree>`. */
16
+ readonly git: (args: ReadonlyArray<string>) => Effect.Effect<string, E>;
17
+ }
18
+ /**
19
+ * Returns the operator-facing line for the run drawer: the failure's message followed by its
20
+ * cause's message. For a failed command, the cause is the stderr, which local and SSH executors
21
+ * scrub of the Attempt's token.
22
+ */
23
+ export declare const failureMessage: (failure: Error) => string;
24
+ /**
25
+ * Commits everything in an unsuccessful Attempt's checkout and pushes HEAD to its salvage ref
26
+ * before the worktree is removed. Skips a clean checkout at its base commit. If the push fails,
27
+ * keeps the worktree and reports its path. Never fails; skipped and failed stages are logged.
28
+ */
29
+ export declare const salvageWorktree: <E extends Error>(options: SalvageOptions<E>) => Effect.Effect<Option.Option<SalvagePushed | SalvageRetained>, never, never>;
30
+ //# sourceMappingURL=salvage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"salvage.d.ts","sourceRoot":"","sources":["../src/salvage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAEL,KAAK,UAAU,EACf,aAAa,EACb,eAAe,EAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAGxD,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,UAO9B,CAAC;AAEF,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,KAAK;IAC7C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,qGAAqG;IACrG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CACzE;AAID;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAAa,KAAK,KAAG,MAG/C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAoD,CAAC,SAAS,KAAK,4GA8F7F,CAAC"}