@usine/executor 0.0.1 → 0.1.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 (62) hide show
  1. package/README.md +54 -2
  2. package/dist/child-command.d.ts +18 -0
  3. package/dist/child-command.d.ts.map +1 -0
  4. package/dist/child-command.js +47 -0
  5. package/dist/child-command.js.map +1 -0
  6. package/dist/exe-dev/channel.d.ts +8 -0
  7. package/dist/exe-dev/channel.d.ts.map +1 -0
  8. package/dist/exe-dev/channel.js +45 -0
  9. package/dist/exe-dev/channel.js.map +1 -0
  10. package/dist/exe-dev/commands.d.ts +22 -0
  11. package/dist/exe-dev/commands.d.ts.map +1 -0
  12. package/dist/exe-dev/commands.js +67 -0
  13. package/dist/exe-dev/commands.js.map +1 -0
  14. package/dist/exe-dev/grants.d.ts +9 -0
  15. package/dist/exe-dev/grants.d.ts.map +1 -0
  16. package/dist/exe-dev/grants.js +88 -0
  17. package/dist/exe-dev/grants.js.map +1 -0
  18. package/dist/exe-dev/index.d.ts +8 -0
  19. package/dist/exe-dev/index.d.ts.map +1 -0
  20. package/dist/exe-dev/index.js +239 -0
  21. package/dist/exe-dev/index.js.map +1 -0
  22. package/dist/exe-dev/model.d.ts +58 -0
  23. package/dist/exe-dev/model.d.ts.map +1 -0
  24. package/dist/exe-dev/model.js +33 -0
  25. package/dist/exe-dev/model.js.map +1 -0
  26. package/dist/exe-dev/preflight.d.ts +9 -0
  27. package/dist/exe-dev/preflight.d.ts.map +1 -0
  28. package/dist/exe-dev/preflight.js +82 -0
  29. package/dist/exe-dev/preflight.js.map +1 -0
  30. package/dist/exe-dev/shell.d.ts +12 -0
  31. package/dist/exe-dev/shell.d.ts.map +1 -0
  32. package/dist/exe-dev/shell.js +31 -0
  33. package/dist/exe-dev/shell.js.map +1 -0
  34. package/dist/exe-dev/sweep.d.ts +12 -0
  35. package/dist/exe-dev/sweep.d.ts.map +1 -0
  36. package/dist/exe-dev/sweep.js +119 -0
  37. package/dist/exe-dev/sweep.js.map +1 -0
  38. package/dist/index.d.ts +80 -6
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +81 -7
  41. package/dist/index.js.map +1 -1
  42. package/dist/invalid-frame.d.ts +17 -0
  43. package/dist/invalid-frame.d.ts.map +1 -0
  44. package/dist/invalid-frame.js +110 -0
  45. package/dist/invalid-frame.js.map +1 -0
  46. package/dist/keyed-lock.d.ts +11 -0
  47. package/dist/keyed-lock.d.ts.map +1 -0
  48. package/dist/keyed-lock.js +21 -0
  49. package/dist/keyed-lock.js.map +1 -0
  50. package/dist/local/index.d.ts +15 -0
  51. package/dist/local/index.d.ts.map +1 -0
  52. package/dist/local/index.js +302 -0
  53. package/dist/local/index.js.map +1 -0
  54. package/dist/provision.d.ts +33 -0
  55. package/dist/provision.d.ts.map +1 -0
  56. package/dist/provision.js +76 -0
  57. package/dist/provision.js.map +1 -0
  58. package/dist/run-host-session.d.ts +22 -0
  59. package/dist/run-host-session.d.ts.map +1 -0
  60. package/dist/run-host-session.js +228 -0
  61. package/dist/run-host-session.js.map +1 -0
  62. package/package.json +19 -3
@@ -0,0 +1,239 @@
1
+ import { Attempt, Repository } from "@usine/core";
2
+ import { Effect, Layer, Ref, Result, Schema } from "effect";
3
+ import { ChildProcessSpawner } from "effect/unstable/process";
4
+ import { CheckoutRequest, CredentialUnavailableError, Executor, ExecutorError, ExecutorOperationError, PreparedAttempt, } from "../index.js";
5
+ import { keyedLockFor } from "../keyed-lock.js";
6
+ import { provisionWorktree, safeSegment } from "../provision.js";
7
+ import { attemptChannelFor } from "./channel.js";
8
+ import { commandsFor } from "./commands.js";
9
+ import { credentialGrantsFor } from "./grants.js";
10
+ import { CredentialGrantMinutes, GitHubIntegration, HarnessLoginCheck, IntegrationName, } from "./model.js";
11
+ import { preflightFor } from "./preflight.js";
12
+ import { RUN_HOST_KILL_SCRIPT, runHostPidFile } from "./shell.js";
13
+ import { abandonedWorktreeSweepFor } from "./sweep.js";
14
+ export { CredentialGrantMinutes, GitHubIntegration, HarnessLoginCheck, IntegrationName, };
15
+ const isExecutorError = Schema.is(ExecutorError);
16
+ const executorError = (message, cause) => new ExecutorOperationError({ operation: "prepare", message, cause });
17
+ const makeExeDevExecutor = (options) => Effect.gen(function* () {
18
+ const commands = yield* commandsFor(options.ssh);
19
+ const fetches = yield* keyedLockFor();
20
+ const preparedStates = yield* Ref.make(new Map());
21
+ const integrations = new Map(options.githubIntegrations.map(({ name, repository }) => [repository, name]));
22
+ const integrationNames = new Set(options.githubIntegrations.map(({ name }) => name));
23
+ const runHost = options.runHost ?? { command: "usine-run-host", args: [] };
24
+ // The grace covers an SSH round trip plus the agent flushing its final
25
+ // events, not just a local pipe: budget accordingly.
26
+ const interruptGrace = options.interruptGrace ?? "10 seconds";
27
+ if (integrations.size !== options.githubIntegrations.length) {
28
+ return yield* Effect.die(new Error("ExeDevExecutorOptions.githubIntegrations contains duplicate repositories"));
29
+ }
30
+ if (integrationNames.size !== options.githubIntegrations.length) {
31
+ return yield* Effect.die(new Error("ExeDevExecutorOptions.githubIntegrations contains duplicate integration names"));
32
+ }
33
+ const machines = yield* preflightFor(options, commands, runHost);
34
+ const credentialGrants = yield* credentialGrantsFor(options, commands);
35
+ const sweepAbandonedWorktrees = yield* abandonedWorktreeSweepFor(commands);
36
+ const attemptChannel = yield* attemptChannelFor(commands, preparedStates, runHost, interruptGrace);
37
+ const prepare = Effect.fn("ExeDevExecutor.prepare")(function* (request) {
38
+ yield* Effect.annotateCurrentSpan({
39
+ attemptId: request.attemptId,
40
+ worker: request.worker,
41
+ repository: request.repository,
42
+ });
43
+ const integration = integrations.get(request.repository);
44
+ if (integration === undefined) {
45
+ return yield* new CredentialUnavailableError({
46
+ repository: request.repository,
47
+ message: `No exe.dev GitHub integration is configured for ${request.repository}`,
48
+ cause: new Error("repository integration missing"),
49
+ });
50
+ }
51
+ const vm = yield* machines.vmFor(request.worker);
52
+ const root = `${vm.home}/.usine/workers/${safeSegment(request.worker)}`;
53
+ const cache = `${root}/repos/${safeSegment(request.repository)}.git`;
54
+ const worktree = `${root}/attempts/${safeSegment(request.attemptId)}`;
55
+ const staging = `${cache}.cloning`;
56
+ const remoteUrl = `https://github.int.exe.xyz/${request.repository}.git`;
57
+ yield* Effect.annotateCurrentSpan({ host: vm.sshDest });
58
+ yield* sweepAbandonedWorktrees(request.attemptId, request.worker, vm, root);
59
+ const remove = (target) => Effect.asVoid(commands.remote("prepare", vm.sshDest, "rm", ["-rf", target]));
60
+ const ops = {
61
+ ensureRoots: Effect.asVoid(commands.remote("prepare", vm.sshDest, "mkdir", [
62
+ "-p",
63
+ `${root}/repos`,
64
+ `${root}/attempts`,
65
+ ])),
66
+ removePath: remove,
67
+ cacheExists: Effect.gen(function* () {
68
+ const inspected = yield* commands.remoteRaw("prepare", vm.sshDest, "test", ["-d", cache]);
69
+ if (inspected.exitCode === 0)
70
+ return true;
71
+ if (inspected.exitCode === 1)
72
+ return false;
73
+ return yield* executorError(`Could not inspect the cached clone on Worker ${request.worker}`, new Error(inspected.stderr.trim()));
74
+ }),
75
+ promoteStaging: Effect.asVoid(commands.remote("prepare", vm.sshDest, "mv", [staging, cache])),
76
+ git: (args) => commands.remote("prepare", vm.sshDest, "git", args),
77
+ };
78
+ const prepared = yield* fetches
79
+ .withLock(request.repository)(Effect.gen(function* () {
80
+ // The grant attaches inside the compensated region: an interrupt
81
+ // parked on the repository lock must not leak an attachment.
82
+ yield* credentialGrants.acquire(request.worker, request.repository, integration);
83
+ const provisioned = yield* provisionWorktree({
84
+ request,
85
+ url: remoteUrl,
86
+ host: vm.sshDest,
87
+ layout: { cache, staging, worktree },
88
+ ops,
89
+ });
90
+ yield* Ref.update(preparedStates, (current) => {
91
+ const next = new Map(current);
92
+ next.set(request.attemptId, { cache, integration, vm });
93
+ return next;
94
+ });
95
+ return provisioned;
96
+ }))
97
+ .pipe(Effect.mapError((cause) => isExecutorError(cause)
98
+ ? cause
99
+ : executorError("exe.dev workspace preparation failed", cause)), Effect.onError(() => Effect.gen(function* () {
100
+ yield* Ref.update(preparedStates, (current) => {
101
+ const next = new Map(current);
102
+ next.delete(request.attemptId);
103
+ return next;
104
+ });
105
+ yield* commands.remote("cleanup", vm.sshDest, "rm", ["-rf", worktree, staging]).pipe(Effect.tapError((error) => Effect.logWarning("Could not remove a partial exe.dev workspace", {
106
+ attemptId: request.attemptId,
107
+ worker: request.worker,
108
+ host: vm.sshDest,
109
+ message: error.message,
110
+ })), Effect.ignore);
111
+ yield* credentialGrants.release(request.worker, integration).pipe(Effect.tapError((error) => Effect.logWarning("Could not detach credentials after prepare failed", {
112
+ attemptId: request.attemptId,
113
+ worker: request.worker,
114
+ host: vm.sshDest,
115
+ message: error.message,
116
+ })), Effect.ignore);
117
+ })));
118
+ yield* Effect.logInfo("exe.dev Attempt workspace ready", {
119
+ attemptId: request.attemptId,
120
+ worker: request.worker,
121
+ host: vm.sshDest,
122
+ repository: request.repository,
123
+ baseCommit: prepared.spec.baseCommit,
124
+ worktree,
125
+ });
126
+ return prepared;
127
+ });
128
+ const cleanWorktree = Effect.fn("ExeDevExecutor.cleanWorktree")(function* (prepared, state) {
129
+ yield* Effect.annotateCurrentSpan({
130
+ attemptId: prepared.spec.attemptId,
131
+ worker: prepared.worker,
132
+ host: state.vm.sshDest,
133
+ });
134
+ // Killing the local SSH client says nothing about the VM: confirm the
135
+ // recorded run host is dead before destroying the workspace under it.
136
+ const confirmed = yield* commands.remoteRaw("cleanup", state.vm.sshDest, "sh", [
137
+ "-c",
138
+ RUN_HOST_KILL_SCRIPT,
139
+ "sh",
140
+ runHostPidFile(prepared.worktree),
141
+ ]);
142
+ yield* Effect.logInfo("Confirmed the remote run host before workspace removal", {
143
+ attemptId: prepared.spec.attemptId,
144
+ worker: prepared.worker,
145
+ host: state.vm.sshDest,
146
+ runHost: confirmed.stdout.trim() || `exit ${String(confirmed.exitCode)}`,
147
+ });
148
+ const removed = yield* commands.remoteRaw("cleanup", state.vm.sshDest, "git", [
149
+ "--git-dir",
150
+ state.cache,
151
+ "worktree",
152
+ "remove",
153
+ "--force",
154
+ prepared.worktree,
155
+ ]);
156
+ if (removed.exitCode !== 0) {
157
+ yield* Effect.logDebug("Remote Git worktree removal returned non-zero", {
158
+ attemptId: prepared.spec.attemptId,
159
+ worker: prepared.worker,
160
+ host: state.vm.sshDest,
161
+ exitCode: String(removed.exitCode),
162
+ });
163
+ }
164
+ yield* commands.remote("cleanup", state.vm.sshDest, "rm", ["-rf", prepared.worktree]);
165
+ yield* commands.remote("cleanup", state.vm.sshDest, "git", [
166
+ "--git-dir",
167
+ state.cache,
168
+ "worktree",
169
+ "prune",
170
+ ]);
171
+ });
172
+ const cleanup = Effect.fn("ExeDevExecutor.cleanup")(function* (prepared) {
173
+ const attemptId = prepared.spec.attemptId;
174
+ yield* Effect.annotateCurrentSpan({
175
+ attemptId,
176
+ worker: prepared.worker,
177
+ });
178
+ yield* Effect.logInfo("Cleaning exe.dev Attempt workspace", {
179
+ attemptId,
180
+ worker: prepared.worker,
181
+ worktree: prepared.worktree,
182
+ });
183
+ const state = (yield* Ref.get(preparedStates)).get(attemptId);
184
+ if (state === undefined) {
185
+ yield* Effect.logDebug("exe.dev Attempt cleanup was already complete", {
186
+ attemptId,
187
+ worker: prepared.worker,
188
+ });
189
+ return;
190
+ }
191
+ yield* Effect.annotateCurrentSpan({ host: state.vm.sshDest });
192
+ // The state entry drops exactly once whatever the outcome: a failed
193
+ // cleanup must never leave a retry path that releases the grant twice
194
+ // and detaches a running sibling's credentials.
195
+ const [worktreeResult, grantResult] = yield* Effect.all([
196
+ cleanWorktree(prepared, state).pipe(Effect.result),
197
+ credentialGrants.release(prepared.worker, state.integration).pipe(Effect.result),
198
+ ]).pipe(Effect.ensuring(Ref.update(preparedStates, (current) => {
199
+ const next = new Map(current);
200
+ next.delete(attemptId);
201
+ return next;
202
+ })));
203
+ const results = [
204
+ ["worktree", worktreeResult],
205
+ ["credential-grant", grantResult],
206
+ ];
207
+ for (const [resource, result] of results) {
208
+ if (Result.isFailure(result)) {
209
+ yield* Effect.logError("exe.dev Attempt cleanup operation failed", {
210
+ attemptId,
211
+ worker: prepared.worker,
212
+ host: state.vm.sshDest,
213
+ resource,
214
+ message: result.failure.message,
215
+ });
216
+ }
217
+ }
218
+ if (Result.isFailure(worktreeResult))
219
+ return yield* worktreeResult.failure;
220
+ if (Result.isFailure(grantResult))
221
+ return yield* grantResult.failure;
222
+ yield* Effect.logInfo("exe.dev Attempt cleanup completed", {
223
+ attemptId,
224
+ worker: prepared.worker,
225
+ host: state.vm.sshDest,
226
+ });
227
+ });
228
+ return Executor.of({
229
+ kind: "exe.dev",
230
+ cancel: attemptChannel.cancel,
231
+ cleanup,
232
+ launch: attemptChannel.launch,
233
+ preflight: machines.preflight,
234
+ prepare,
235
+ });
236
+ });
237
+ /** Node-only exe.dev adapter; the package root remains platform-free. */
238
+ export const layer = (options) => Layer.effect(Executor, makeExeDevExecutor(options));
239
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exe-dev/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { Attempt } from "@usine/core";
2
+ import { Schema, type Effect, type Ref } from "effect";
3
+ import type { RunHostCommand } from "../index.ts";
4
+ /** The name of one exe.dev edge-injected integration. */
5
+ export declare const IntegrationName: Schema.brand<Schema.String, "ExeDevIntegrationName">;
6
+ export type IntegrationName = typeof IntegrationName.Type;
7
+ /**
8
+ * The SSH argv boundary: this string is handed to the local `ssh` client as a
9
+ * destination, so anything that could read as an option (`-oProxyCommand=...`)
10
+ * must be unrepresentable, not merely unexpected.
11
+ */
12
+ export declare const SshDestination: Schema.brand<Schema.String, "ExeDevSshDestination">;
13
+ export type SshDestination = typeof SshDestination.Type;
14
+ /** An absolute remote home directory that cannot smuggle bytes past shell quoting. */
15
+ export declare const HomePath: Schema.brand<Schema.String, "ExeDevHomePath">;
16
+ export type HomePath = typeof HomePath.Type;
17
+ declare const GitHubIntegration_base: Schema.Class<GitHubIntegration, Schema.Struct<{
18
+ readonly repository: Schema.brand<Schema.String, "RepoName">;
19
+ readonly name: Schema.brand<Schema.String, "ExeDevIntegrationName">;
20
+ }>, {}>;
21
+ /** One standing exe.dev GitHub integration; only its VM attachment is per Attempt. */
22
+ export declare class GitHubIntegration extends GitHubIntegration_base {
23
+ }
24
+ declare const HarnessLoginCheck_base: Schema.Class<HarnessLoginCheck, Schema.Struct<{
25
+ readonly harness: Schema.NonEmptyString;
26
+ readonly command: Schema.NonEmptyString;
27
+ readonly args: Schema.$Array<Schema.String>;
28
+ }>, {}>;
29
+ /** A harness-owned command that exits zero only while its operator login remains usable. */
30
+ export declare class HarnessLoginCheck extends HarnessLoginCheck_base {
31
+ }
32
+ export declare const CredentialGrantMinutes: Schema.brand<Schema.Int, "ExeDevCredentialGrantMinutes">;
33
+ export type CredentialGrantMinutes = typeof CredentialGrantMinutes.Type;
34
+ export interface ExeDevExecutorOptions {
35
+ /** Existing native GitHub integrations, one per repository this Worker may receive. */
36
+ readonly githubIntegrations: ReadonlyArray<GitHubIntegration>;
37
+ /** Every harness used on this Worker must prove that its operator login is still valid. */
38
+ readonly harnessLoginChecks: readonly [HarnessLoginCheck, ...ReadonlyArray<HarnessLoginCheck>];
39
+ /** Installed in the baked image; defaults to the published worker executable. */
40
+ readonly runHost?: RunHostCommand;
41
+ /** Local SSH executable and its control-plane-only arguments. */
42
+ readonly ssh?: RunHostCommand;
43
+ /** Time-box for the edge credential attachment. Defaults to 65 minutes. */
44
+ readonly credentialGrantMinutes?: CredentialGrantMinutes;
45
+ readonly interruptGrace?: Parameters<typeof Effect.timeoutOption>[1];
46
+ }
47
+ export interface VmState {
48
+ readonly home: HomePath;
49
+ readonly sshDest: SshDestination;
50
+ }
51
+ export interface PreparedState {
52
+ readonly cache: string;
53
+ readonly integration: IntegrationName;
54
+ readonly vm: VmState;
55
+ }
56
+ export type PreparedStates = Ref.Ref<Map<Attempt.AttemptId, PreparedState>>;
57
+ export {};
58
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/exe-dev/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,GAAG,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,yDAAyD;AACzD,eAAO,MAAM,eAAe,sDAIiB,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,cAAc,qDAIiB,CAAC;AAC7C,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAC;AAExD,sFAAsF;AACtF,eAAO,MAAM,QAAQ,+CAIiB,CAAC;AACvC,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;;;;;AAE5C,sFAAsF;AACtF,qBAAa,iBAAkB,SAAQ,sBAKrC;CAAG;;;;;;AAEL,4FAA4F;AAC5F,qBAAa,iBAAkB,SAAQ,sBAMrC;CAAG;AAEL,eAAO,MAAM,sBAAsB,0DAElC,CAAC;AACF,MAAM,MAAM,sBAAsB,GAAG,OAAO,sBAAsB,CAAC,IAAI,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC9D,2FAA2F;IAC3F,QAAQ,CAAC,kBAAkB,EAAE,SAAS,CAAC,iBAAiB,EAAE,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC/F,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,iEAAiE;IACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IACzD,QAAQ,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { Attempt, Repository } from "@usine/core";
2
+ import { Schema } from "effect";
3
+ /** The name of one exe.dev edge-injected integration. */
4
+ export const IntegrationName = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/u, {
5
+ message: "Expected an exe.dev integration name safe for the lobby command boundary",
6
+ })).pipe(Schema.brand("ExeDevIntegrationName"));
7
+ /**
8
+ * The SSH argv boundary: this string is handed to the local `ssh` client as a
9
+ * destination, so anything that could read as an option (`-oProxyCommand=...`)
10
+ * must be unrepresentable, not merely unexpected.
11
+ */
12
+ export const SshDestination = Schema.String.check(Schema.isPattern(/^(?:[A-Za-z0-9._-]+@)?[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/u, {
13
+ message: "Expected an exe.dev SSH hostname, optionally prefixed by a user",
14
+ })).pipe(Schema.brand("ExeDevSshDestination"));
15
+ /** An absolute remote home directory that cannot smuggle bytes past shell quoting. */
16
+ export const HomePath = Schema.String.check(Schema.isPattern(/^\/[^\0\r\n]*$/u, {
17
+ message: "Expected an absolute remote home directory without control bytes",
18
+ })).pipe(Schema.brand("ExeDevHomePath"));
19
+ /** One standing exe.dev GitHub integration; only its VM attachment is per Attempt. */
20
+ export class GitHubIntegration extends Schema.Class("@usine/executor/ExeDevGitHubIntegration")({
21
+ repository: Repository.Name,
22
+ name: IntegrationName,
23
+ }) {
24
+ }
25
+ /** A harness-owned command that exits zero only while its operator login remains usable. */
26
+ export class HarnessLoginCheck extends Schema.Class("@usine/executor/ExeDevHarnessLoginCheck")({
27
+ harness: Schema.NonEmptyString,
28
+ command: Schema.NonEmptyString,
29
+ args: Schema.Array(Schema.String),
30
+ }) {
31
+ }
32
+ export const CredentialGrantMinutes = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)).pipe(Schema.brand("ExeDevCredentialGrantMinutes"));
33
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/exe-dev/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,MAAM,EAAyB,MAAM,QAAQ,CAAC;AAGvD,yDAAyD;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAChD,MAAM,CAAC,SAAS,CAAC,uCAAuC,EAAE;IACxD,OAAO,EAAE,0EAA0E;CACpF,CAAC,CACH,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAG9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAC/C,MAAM,CAAC,SAAS,CAAC,mEAAmE,EAAE;IACpF,OAAO,EAAE,iEAAiE;CAC3E,CAAC,CACH,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;AAG7C,sFAAsF;AACtF,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CACzC,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE;IAClC,OAAO,EAAE,kEAAkE;CAC5E,CAAC,CACH,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAGvC,sFAAsF;AACtF,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,KAAK,CACjD,yCAAyC,CAC1C,CAAC;IACA,UAAU,EAAE,UAAU,CAAC,IAAI;IAC3B,IAAI,EAAE,eAAe;CACtB,CAAC;CAAG;AAEL,4FAA4F;AAC5F,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,KAAK,CACjD,yCAAyC,CAC1C,CAAC;IACA,OAAO,EAAE,MAAM,CAAC,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;CAClC,CAAC;CAAG;AAEL,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC3F,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAC7C,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ import { ExecutorOperationError, type RunHostCommand, WorkerCapabilityError } from "../index.ts";
3
+ import type { ExeDevCommands } from "./commands.ts";
4
+ import { type ExeDevExecutorOptions, type VmState } from "./model.ts";
5
+ export declare const preflightFor: (options: ExeDevExecutorOptions, commands: ExeDevCommands, runHost: RunHostCommand) => Effect.Effect<{
6
+ preflight: (worker: string & import("effect/Brand").Brand<"WorkerName">) => Effect.Effect<undefined, import("../index.ts").CredentialUnavailableError | ExecutorOperationError | WorkerCapabilityError, never>;
7
+ vmFor: (worker: string & import("effect/Brand").Brand<"WorkerName">) => Effect.Effect<VmState, ExecutorOperationError | WorkerCapabilityError, never>;
8
+ }, never, never>;
9
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/exe-dev/preflight.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAC7C,OAAO,EACL,sBAAsB,EAEtB,KAAK,cAAc,EACnB,qBAAqB,EAEtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAA4B,KAAK,qBAAqB,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAoBhG,eAAO,MAAM,YAAY;;;gBA6JvB,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { AttemptChannel, Worker } from "@usine/core";
2
+ import { Effect, Ref, Schema } from "effect";
3
+ import { ExecutorOperationError, logPreflightFailure, WorkerCapabilityError, } from "../index.js";
4
+ import { HomePath, SshDestination } from "./model.js";
5
+ const MINIMUM_NODE_MAJOR = 22;
6
+ const Vm = Schema.Struct({
7
+ vmName: Schema.NonEmptyString,
8
+ sshDest: SshDestination,
9
+ status: Schema.NonEmptyString,
10
+ }).pipe(Schema.encodeKeys({ vmName: "vm_name", sshDest: "ssh_dest" }));
11
+ const VmList = Schema.Struct({ vms: Schema.Array(Vm) });
12
+ const decodeVmList = Schema.decodeUnknownEffect(Schema.fromJsonString(VmList));
13
+ const decodeHome = Schema.decodeUnknownEffect(HomePath);
14
+ const capabilityError = (worker, capability, message, cause) => new WorkerCapabilityError({ worker, capability, message, cause });
15
+ export const preflightFor = Effect.fn("ExeDevExecutor.preflightFor")(function* (options, commands, runHost) {
16
+ const workers = yield* Ref.make(new Map());
17
+ const resolveVm = Effect.fn("ExeDevExecutor.resolveVm")(function* (worker) {
18
+ const response = yield* commands.lobbyJson("preflight", ["ls", worker]);
19
+ const decoded = yield* decodeVmList(response).pipe(Effect.mapError((cause) => capabilityError(worker, "machine", `exe.dev returned invalid machine metadata for Worker ${worker}`, cause)));
20
+ const vm = decoded.vms.find(({ vmName }) => vmName === worker);
21
+ if (vm === undefined) {
22
+ return yield* capabilityError(worker, "machine", `exe.dev has no VM named ${worker}`, new Error("worker VM missing"));
23
+ }
24
+ if (vm.status !== "running") {
25
+ return yield* capabilityError(worker, "machine", `exe.dev VM ${worker} is ${vm.status}, not running`, new Error(`VM status: ${vm.status}`));
26
+ }
27
+ const reported = (yield* commands.remote("preflight", vm.sshDest, "printenv", ["HOME"])).trim();
28
+ const home = yield* decodeHome(reported).pipe(Effect.mapError((cause) => capabilityError(worker, "toolchain", `exe.dev VM ${worker} returned an invalid home directory`, cause)));
29
+ return { home, sshDest: vm.sshDest };
30
+ });
31
+ const asCapability = (worker, capability, message, effect) => effect.pipe(Effect.mapError((cause) => capabilityError(worker, capability, message, cause)));
32
+ const preflight = Effect.fn("ExeDevExecutor.preflight")(function* (worker) {
33
+ const vm = yield* resolveVm(worker).pipe(Effect.catchTag("ExecutorOperationError", (cause) => capabilityError(worker, "machine", `Could not reach exe.dev Worker ${worker}`, cause)));
34
+ yield* Effect.annotateCurrentSpan({ worker, host: vm.sshDest });
35
+ const gitVersion = (yield* asCapability(worker, "toolchain", `Worker ${worker} cannot run Git`, commands.remote("preflight", vm.sshDest, "git", ["--version"]))).trim();
36
+ if (!gitVersion.startsWith("git version ")) {
37
+ return yield* capabilityError(worker, "toolchain", `Worker ${worker} returned an unrecognized Git version`, new Error(gitVersion));
38
+ }
39
+ const nodeVersion = (yield* asCapability(worker, "toolchain", `Worker ${worker} cannot run Node.js`, commands.remote("preflight", vm.sshDest, "node", ["--version"]))).trim();
40
+ const nodeMajor = Number(/^v(?<major>\d+)/u.exec(nodeVersion)?.groups?.major ?? Number.NaN);
41
+ if (!Number.isSafeInteger(nodeMajor) || nodeMajor < MINIMUM_NODE_MAJOR) {
42
+ return yield* capabilityError(worker, "toolchain", `Worker ${worker} has Node.js ${nodeVersion || "unknown"}; expected ${MINIMUM_NODE_MAJOR}+`, new Error("Node.js version mismatch"));
43
+ }
44
+ const protocol = (yield* asCapability(worker, "run-host", `Worker ${worker} cannot run ${runHost.command}`, commands.remote("preflight", vm.sshDest, runHost.command, [
45
+ ...runHost.args,
46
+ "--protocol-version",
47
+ ]))).trim();
48
+ if (protocol !== String(AttemptChannel.PROTOCOL_VERSION)) {
49
+ return yield* capabilityError(worker, "run-host", `Worker ${worker} has run-host protocol ${protocol || "unknown"}; expected ${AttemptChannel.PROTOCOL_VERSION}`, new Error("run-host protocol mismatch"));
50
+ }
51
+ for (const check of options.harnessLoginChecks) {
52
+ yield* asCapability(worker, "harness-login", `${check.harness} is not logged in on Worker ${worker}`, commands.remote("preflight", vm.sshDest, check.command, check.args));
53
+ }
54
+ for (const integration of options.githubIntegrations) {
55
+ yield* asCapability(worker, "credentials", `exe.dev GitHub integration ${integration.name} is not ready for ${integration.repository}`, commands.lobby("preflight", ["integrations", "test", integration.name]));
56
+ }
57
+ yield* Ref.update(workers, (current) => {
58
+ const next = new Map(current);
59
+ next.set(worker, vm);
60
+ return next;
61
+ });
62
+ yield* Effect.logInfo("exe.dev Worker preflight verified", {
63
+ worker,
64
+ host: vm.sshDest,
65
+ gitVersion,
66
+ nodeVersion,
67
+ protocolVersion: AttemptChannel.PROTOCOL_VERSION,
68
+ harnesses: options.harnessLoginChecks.map(({ harness }) => harness).join(","),
69
+ integrations: options.githubIntegrations.length,
70
+ });
71
+ }, logPreflightFailure);
72
+ // Both resolveVm failures sit inside the ExecutorError union prepare
73
+ // reports, so a stopped VM keeps its typed capability shape.
74
+ const vmFor = Effect.fn("ExeDevExecutor.vmFor")(function* (worker) {
75
+ const cached = (yield* Ref.get(workers)).get(worker);
76
+ if (cached !== undefined)
77
+ return cached;
78
+ return yield* resolveVm(worker);
79
+ });
80
+ return { preflight, vmFor };
81
+ });
82
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/exe-dev/preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /** One shell-safe command string for OpenSSH's remote-command boundary. */
2
+ export declare const shellCommand: (command: string, args: ReadonlyArray<string>) => string;
3
+ /** Sibling of the worktree, so the sweep reclaims it with the other leftovers. */
4
+ export declare const runHostPidFile: (worktree: string) => string;
5
+ /** The run host needs a working directory and one public edge-host selector. */
6
+ export declare const runHostShellCommand: (worktree: string, command: string, args: ReadonlyArray<string>) => string;
7
+ /**
8
+ * Confirms a recorded run host is dead before workspace removal: TERM first,
9
+ * a bounded wait, then KILL. `$1` is the pid file; stdout reports the outcome.
10
+ */
11
+ export declare const RUN_HOST_KILL_SCRIPT: string;
12
+ //# sourceMappingURL=shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/exe-dev/shell.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,eAAO,MAAM,YAAY,YAAa,MAAM,QAAQ,aAAa,CAAC,MAAM,CAAC,KAAG,MACzB,CAAC;AAEpD,kFAAkF;AAClF,eAAO,MAAM,cAAc,aAAc,MAAM,KAAG,MAA2B,CAAC;AAE9E,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,aACpB,MAAM,WACP,MAAM,QACT,aAAa,CAAC,MAAM,CAAC,KAC1B,MAIsJ,CAAC;AAE1J;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAczB,CAAC"}
@@ -0,0 +1,31 @@
1
+ const quote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
2
+ /** One shell-safe command string for OpenSSH's remote-command boundary. */
3
+ export const shellCommand = (command, args) => `exec ${[command, ...args].map(quote).join(" ")}`;
4
+ /** Sibling of the worktree, so the sweep reclaims it with the other leftovers. */
5
+ export const runHostPidFile = (worktree) => `${worktree}.pid`;
6
+ /** The run host needs a working directory and one public edge-host selector. */
7
+ export const runHostShellCommand = (worktree, command, args) =>
8
+ // `$$` names the remote shell that `exec` replaces with the run host, so the
9
+ // pid file identifies the run host itself; cleanup uses it to confirm the
10
+ // process is gone before destroying the worktree under it.
11
+ `cd ${quote(worktree)} && export GH_HOST=github.int.exe.xyz && printf '%s' "$$" > ${quote(runHostPidFile(worktree))} && ${shellCommand(command, args)}`;
12
+ /**
13
+ * Confirms a recorded run host is dead before workspace removal: TERM first,
14
+ * a bounded wait, then KILL. `$1` is the pid file; stdout reports the outcome.
15
+ */
16
+ export const RUN_HOST_KILL_SCRIPT = `
17
+ if test -f "$1"; then
18
+ pid=$(cat "$1")
19
+ if kill -TERM "$pid" 2>/dev/null; then
20
+ n=0
21
+ while kill -0 "$pid" 2>/dev/null && [ "$n" -lt 10 ]; do sleep 0.5; n=$((n+1)); done
22
+ if kill -0 "$pid" 2>/dev/null; then kill -KILL "$pid" 2>/dev/null; echo killed; else echo stopped; fi
23
+ else
24
+ echo absent
25
+ fi
26
+ rm -f "$1"
27
+ else
28
+ echo no-pidfile
29
+ fi
30
+ `.trim();
31
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/exe-dev/shell.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AAE/E,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAE,IAA2B,EAAU,EAAE,CACnF,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAEpD,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,GAAG,QAAQ,MAAM,CAAC;AAE9E,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,QAAgB,EAChB,OAAe,EACf,IAA2B,EACnB,EAAE;AACV,6EAA6E;AAC7E,0EAA0E;AAC1E,2DAA2D;AAC3D,MAAM,KAAK,CAAC,QAAQ,CAAC,+DAA+D,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAE1J;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;CAcnC,CAAC,IAAI,EAAE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Effect } from "effect";
2
+ import type { ExeDevCommands } from "./commands.ts";
3
+ import type { VmState } from "./model.ts";
4
+ /**
5
+ * Reclaims crash leftovers once, before this process starts work on a VM.
6
+ * Every removal is isolated: one immovable leftover must never block the
7
+ * sweep's siblings or, worse, every future prepare on the Worker — so the
8
+ * sweep is marked done even when parts of it failed, and each failure is
9
+ * logged instead of propagated.
10
+ */
11
+ export declare const abandonedWorktreeSweepFor: (commands: ExeDevCommands) => Effect.Effect<(attemptId: string & import("effect/Brand").Brand<"AttemptId">, worker: string & import("effect/Brand").Brand<"WorkerName">, vm: VmState, root: string) => Effect.Effect<void, never, never>, never, never>;
12
+ //# sourceMappingURL=sweep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.d.ts","sourceRoot":"","sources":["../../src/exe-dev/sweep.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAG7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAwC1C;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,yPAkIrC,CAAC"}