@usine/executor 0.2.1 → 0.4.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 (250) hide show
  1. package/LICENSE +661 -21
  2. package/README.md +212 -37
  3. package/dist/child-command.d.ts +5 -8
  4. package/dist/child-command.d.ts.map +1 -1
  5. package/dist/child-command.js +10 -14
  6. package/dist/child-command.js.map +1 -1
  7. package/dist/child-environment.d.ts +19 -0
  8. package/dist/child-environment.d.ts.map +1 -0
  9. package/dist/child-environment.js +18 -0
  10. package/dist/child-environment.js.map +1 -0
  11. package/dist/claude-login-lock.d.ts +9 -0
  12. package/dist/claude-login-lock.d.ts.map +1 -0
  13. package/dist/claude-login-lock.js +15 -0
  14. package/dist/claude-login-lock.js.map +1 -0
  15. package/dist/cloudflare/client.d.ts +50 -0
  16. package/dist/cloudflare/client.d.ts.map +1 -0
  17. package/dist/cloudflare/client.js +108 -0
  18. package/dist/cloudflare/client.js.map +1 -0
  19. package/dist/cloudflare/cloudflare.d.ts +17 -0
  20. package/dist/cloudflare/cloudflare.d.ts.map +1 -0
  21. package/dist/cloudflare/cloudflare.js +704 -0
  22. package/dist/cloudflare/cloudflare.js.map +1 -0
  23. package/dist/cloudflare/model.d.ts +42 -0
  24. package/dist/cloudflare/model.d.ts.map +1 -0
  25. package/dist/cloudflare/model.js +2 -0
  26. package/dist/cloudflare/model.js.map +1 -0
  27. package/dist/cloudflare/shell/environment.d.ts +12 -0
  28. package/dist/cloudflare/shell/environment.d.ts.map +1 -0
  29. package/dist/cloudflare/shell/environment.js +12 -0
  30. package/dist/cloudflare/shell/environment.js.map +1 -0
  31. package/dist/cloudflare/shell/shell.d.ts +2 -0
  32. package/dist/cloudflare/shell/shell.d.ts.map +1 -0
  33. package/dist/cloudflare/shell/shell.js +155 -0
  34. package/dist/cloudflare/shell/shell.js.map +1 -0
  35. package/dist/credential-environment.d.ts +20 -0
  36. package/dist/credential-environment.d.ts.map +1 -0
  37. package/dist/credential-environment.js +20 -0
  38. package/dist/credential-environment.js.map +1 -0
  39. package/dist/durable-session.d.ts +81 -0
  40. package/dist/durable-session.d.ts.map +1 -0
  41. package/dist/durable-session.js +550 -0
  42. package/dist/durable-session.js.map +1 -0
  43. package/dist/exe-dev/commands.d.ts +23 -13
  44. package/dist/exe-dev/commands.d.ts.map +1 -1
  45. package/dist/exe-dev/commands.js +11 -57
  46. package/dist/exe-dev/commands.js.map +1 -1
  47. package/dist/exe-dev/exe-dev.d.ts +9 -3
  48. package/dist/exe-dev/exe-dev.d.ts.map +1 -1
  49. package/dist/exe-dev/exe-dev.js +61 -128
  50. package/dist/exe-dev/exe-dev.js.map +1 -1
  51. package/dist/exe-dev/grants.d.ts.map +1 -1
  52. package/dist/exe-dev/grants.js +35 -15
  53. package/dist/exe-dev/grants.js.map +1 -1
  54. package/dist/exe-dev/model.d.ts +15 -19
  55. package/dist/exe-dev/model.d.ts.map +1 -1
  56. package/dist/exe-dev/model.js +9 -12
  57. package/dist/exe-dev/model.js.map +1 -1
  58. package/dist/exe-dev/preflight.d.ts +8 -4
  59. package/dist/exe-dev/preflight.d.ts.map +1 -1
  60. package/dist/exe-dev/preflight.js +26 -35
  61. package/dist/exe-dev/preflight.js.map +1 -1
  62. package/dist/executor.d.ts +292 -44
  63. package/dist/executor.d.ts.map +1 -1
  64. package/dist/executor.js +187 -19
  65. package/dist/executor.js.map +1 -1
  66. package/dist/guard.d.ts +57 -7
  67. package/dist/guard.d.ts.map +1 -1
  68. package/dist/guard.js +274 -59
  69. package/dist/guard.js.map +1 -1
  70. package/dist/harness-preflight.d.ts +27 -5
  71. package/dist/harness-preflight.d.ts.map +1 -1
  72. package/dist/harness-preflight.js +32 -31
  73. package/dist/harness-preflight.js.map +1 -1
  74. package/dist/keyed-lock.d.ts +2 -3
  75. package/dist/keyed-lock.d.ts.map +1 -1
  76. package/dist/keyed-lock.js +2 -3
  77. package/dist/keyed-lock.js.map +1 -1
  78. package/dist/leftovers.d.ts +9 -0
  79. package/dist/leftovers.d.ts.map +1 -0
  80. package/dist/leftovers.js +14 -0
  81. package/dist/leftovers.js.map +1 -0
  82. package/dist/local/credentials.d.ts +21 -0
  83. package/dist/local/credentials.d.ts.map +1 -0
  84. package/dist/local/credentials.js +39 -0
  85. package/dist/local/credentials.js.map +1 -0
  86. package/dist/local/leftovers.d.ts +8 -0
  87. package/dist/local/leftovers.d.ts.map +1 -0
  88. package/dist/local/leftovers.js +23 -0
  89. package/dist/local/leftovers.js.map +1 -0
  90. package/dist/local/local.d.ts +17 -11
  91. package/dist/local/local.d.ts.map +1 -1
  92. package/dist/local/local.js +205 -132
  93. package/dist/local/local.js.map +1 -1
  94. package/dist/memory-preflight.d.ts +27 -0
  95. package/dist/memory-preflight.d.ts.map +1 -0
  96. package/dist/memory-preflight.js +83 -0
  97. package/dist/memory-preflight.js.map +1 -0
  98. package/dist/pre-push-hook.d.ts +20 -0
  99. package/dist/pre-push-hook.d.ts.map +1 -0
  100. package/dist/pre-push-hook.js +33 -0
  101. package/dist/pre-push-hook.js.map +1 -0
  102. package/dist/prepared-attempt.d.ts +41 -3
  103. package/dist/prepared-attempt.d.ts.map +1 -1
  104. package/dist/prepared-attempt.js +109 -31
  105. package/dist/prepared-attempt.js.map +1 -1
  106. package/dist/provision.d.ts +129 -15
  107. package/dist/provision.d.ts.map +1 -1
  108. package/dist/provision.js +428 -69
  109. package/dist/provision.js.map +1 -1
  110. package/dist/remote/commands.d.ts +51 -0
  111. package/dist/remote/commands.d.ts.map +1 -0
  112. package/dist/remote/commands.js +94 -0
  113. package/dist/remote/commands.js.map +1 -0
  114. package/dist/remote/container.d.ts +90 -0
  115. package/dist/remote/container.d.ts.map +1 -0
  116. package/dist/remote/container.js +480 -0
  117. package/dist/remote/container.js.map +1 -0
  118. package/dist/remote/leftovers.d.ts +14 -0
  119. package/dist/remote/leftovers.d.ts.map +1 -0
  120. package/dist/remote/leftovers.js +22 -0
  121. package/dist/remote/leftovers.js.map +1 -0
  122. package/dist/remote/liveness.d.ts +33 -0
  123. package/dist/remote/liveness.d.ts.map +1 -0
  124. package/dist/remote/liveness.js +196 -0
  125. package/dist/remote/liveness.js.map +1 -0
  126. package/dist/remote/machine.d.ts +110 -0
  127. package/dist/remote/machine.d.ts.map +1 -0
  128. package/dist/remote/machine.js +2 -0
  129. package/dist/remote/machine.js.map +1 -0
  130. package/dist/remote/remote.d.ts +28 -0
  131. package/dist/remote/remote.d.ts.map +1 -0
  132. package/dist/remote/remote.js +556 -0
  133. package/dist/remote/remote.js.map +1 -0
  134. package/dist/remote/sweep.d.ts +17 -0
  135. package/dist/remote/sweep.d.ts.map +1 -0
  136. package/dist/remote/sweep.js +117 -0
  137. package/dist/remote/sweep.js.map +1 -0
  138. package/dist/renewable-credential.d.ts +17 -0
  139. package/dist/renewable-credential.d.ts.map +1 -0
  140. package/dist/renewable-credential.js +58 -0
  141. package/dist/renewable-credential.js.map +1 -0
  142. package/dist/salvage.d.ts +38 -0
  143. package/dist/salvage.d.ts.map +1 -0
  144. package/dist/salvage.js +198 -0
  145. package/dist/salvage.js.map +1 -0
  146. package/dist/ssh/api-keys.d.ts +36 -0
  147. package/dist/ssh/api-keys.d.ts.map +1 -0
  148. package/dist/ssh/api-keys.js +72 -0
  149. package/dist/ssh/api-keys.js.map +1 -0
  150. package/dist/ssh/codex-homes.d.ts +21 -0
  151. package/dist/ssh/codex-homes.d.ts.map +1 -0
  152. package/dist/ssh/codex-homes.js +78 -0
  153. package/dist/ssh/codex-homes.js.map +1 -0
  154. package/dist/ssh/container.d.ts +32 -0
  155. package/dist/ssh/container.d.ts.map +1 -0
  156. package/dist/ssh/container.js +282 -0
  157. package/dist/ssh/container.js.map +1 -0
  158. package/dist/ssh/credentials.d.ts +43 -0
  159. package/dist/ssh/credentials.d.ts.map +1 -0
  160. package/dist/ssh/credentials.js +86 -0
  161. package/dist/ssh/credentials.js.map +1 -0
  162. package/dist/ssh/gateway/admin.d.ts +45 -0
  163. package/dist/ssh/gateway/admin.d.ts.map +1 -0
  164. package/dist/ssh/gateway/admin.js +129 -0
  165. package/dist/ssh/gateway/admin.js.map +1 -0
  166. package/dist/ssh/gateway/attachments.d.ts +31 -0
  167. package/dist/ssh/gateway/attachments.d.ts.map +1 -0
  168. package/dist/ssh/gateway/attachments.js +168 -0
  169. package/dist/ssh/gateway/attachments.js.map +1 -0
  170. package/dist/ssh/gateway/environment.d.ts +71 -0
  171. package/dist/ssh/gateway/environment.d.ts.map +1 -0
  172. package/dist/ssh/gateway/environment.js +148 -0
  173. package/dist/ssh/gateway/environment.js.map +1 -0
  174. package/dist/ssh/gateway/forward.d.ts +74 -0
  175. package/dist/ssh/gateway/forward.d.ts.map +1 -0
  176. package/dist/ssh/gateway/forward.js +247 -0
  177. package/dist/ssh/gateway/forward.js.map +1 -0
  178. package/dist/ssh/gateway/preflight.d.ts +67 -0
  179. package/dist/ssh/gateway/preflight.d.ts.map +1 -0
  180. package/dist/ssh/gateway/preflight.js +320 -0
  181. package/dist/ssh/gateway/preflight.js.map +1 -0
  182. package/dist/ssh/limits.d.ts +60 -0
  183. package/dist/ssh/limits.d.ts.map +1 -0
  184. package/dist/ssh/limits.js +84 -0
  185. package/dist/ssh/limits.js.map +1 -0
  186. package/dist/ssh/model.d.ts +153 -0
  187. package/dist/ssh/model.d.ts.map +1 -0
  188. package/dist/ssh/model.js +2 -0
  189. package/dist/ssh/model.js.map +1 -0
  190. package/dist/ssh/preflight.d.ts +35 -0
  191. package/dist/ssh/preflight.d.ts.map +1 -0
  192. package/dist/ssh/preflight.js +87 -0
  193. package/dist/ssh/preflight.js.map +1 -0
  194. package/dist/ssh/provisioner/boat.d.ts +33 -0
  195. package/dist/ssh/provisioner/boat.d.ts.map +1 -0
  196. package/dist/ssh/provisioner/boat.js +247 -0
  197. package/dist/ssh/provisioner/boat.js.map +1 -0
  198. package/dist/ssh/provisioner/hetzner.d.ts +32 -0
  199. package/dist/ssh/provisioner/hetzner.d.ts.map +1 -0
  200. package/dist/ssh/provisioner/hetzner.js +337 -0
  201. package/dist/ssh/provisioner/hetzner.js.map +1 -0
  202. package/dist/ssh/provisioner/host-key.d.ts +11 -0
  203. package/dist/ssh/provisioner/host-key.d.ts.map +1 -0
  204. package/dist/ssh/provisioner/host-key.js +57 -0
  205. package/dist/ssh/provisioner/host-key.js.map +1 -0
  206. package/dist/ssh/provisioner/http.d.ts +45 -0
  207. package/dist/ssh/provisioner/http.d.ts.map +1 -0
  208. package/dist/ssh/provisioner/http.js +72 -0
  209. package/dist/ssh/provisioner/http.js.map +1 -0
  210. package/dist/ssh/provisioner/machines.d.ts +48 -0
  211. package/dist/ssh/provisioner/machines.d.ts.map +1 -0
  212. package/dist/ssh/provisioner/machines.js +686 -0
  213. package/dist/ssh/provisioner/machines.js.map +1 -0
  214. package/dist/ssh/provisioner/model.d.ts +92 -0
  215. package/dist/ssh/provisioner/model.d.ts.map +1 -0
  216. package/dist/ssh/provisioner/model.js +37 -0
  217. package/dist/ssh/provisioner/model.js.map +1 -0
  218. package/dist/ssh/ssh.d.ts +23 -0
  219. package/dist/ssh/ssh.d.ts.map +1 -0
  220. package/dist/ssh/ssh.js +145 -0
  221. package/dist/ssh/ssh.js.map +1 -0
  222. package/dist/ssh/worker-records.d.ts +18 -0
  223. package/dist/ssh/worker-records.d.ts.map +1 -0
  224. package/dist/ssh/worker-records.js +100 -0
  225. package/dist/ssh/worker-records.js.map +1 -0
  226. package/dist/ssh/workers.d.ts +80 -0
  227. package/dist/ssh/workers.d.ts.map +1 -0
  228. package/dist/ssh/workers.js +247 -0
  229. package/dist/ssh/workers.js.map +1 -0
  230. package/package.json +25 -9
  231. package/dist/exe-dev/channel.d.ts +0 -12
  232. package/dist/exe-dev/channel.d.ts.map +0 -1
  233. package/dist/exe-dev/channel.js +0 -42
  234. package/dist/exe-dev/channel.js.map +0 -1
  235. package/dist/exe-dev/shell.d.ts +0 -12
  236. package/dist/exe-dev/shell.d.ts.map +0 -1
  237. package/dist/exe-dev/shell.js +0 -31
  238. package/dist/exe-dev/shell.js.map +0 -1
  239. package/dist/exe-dev/sweep.d.ts +0 -10
  240. package/dist/exe-dev/sweep.d.ts.map +0 -1
  241. package/dist/exe-dev/sweep.js +0 -117
  242. package/dist/exe-dev/sweep.js.map +0 -1
  243. package/dist/invalid-frame.d.ts +0 -17
  244. package/dist/invalid-frame.d.ts.map +0 -1
  245. package/dist/invalid-frame.js +0 -111
  246. package/dist/invalid-frame.js.map +0 -1
  247. package/dist/run-host-session.d.ts +0 -27
  248. package/dist/run-host-session.d.ts.map +0 -1
  249. package/dist/run-host-session.js +0 -303
  250. package/dist/run-host-session.js.map +0 -1
@@ -0,0 +1,153 @@
1
+ import type { SshCommand } from "@usine/agents/child-host";
2
+ import type { InstanceId } from "@usine/core/instance";
3
+ import type { BoatSize, ContainerCpus, ContainerImage, ContainerMemoryMib, ContainerPids, GatewayAdminPort, GatewayCredentialKind, GatewayDomain, GatewayLlmProvider, HetznerImage, HetznerLabels, HetznerLocation, HetznerResourceName, HetznerServerType, MachineProvider, SshDestination, SshIdentityFile, SshPort, SshUser, WorkerName, WorkerRoot } from "@usine/core/worker";
4
+ import type { Duration, Effect, Option, Redacted, Ref } from "effect";
5
+ import type { HarnessProbe } from "../harness-preflight.ts";
6
+ import type { RemoteMachine } from "../remote/machine.ts";
7
+ import type { GatewayAdmin } from "./gateway/admin.ts";
8
+ import type { GatewayForward } from "./gateway/forward.ts";
9
+ /** The Gateway in front of an SSH Worker's machine on a private network (ADR 0027). */
10
+ export interface SshWorkerGateway {
11
+ /** The Gateway's private DNS domain. GitHub is at `git.<domain>`, LLM providers at `llm.<domain>`, and the forward proxy at `<domain>:3128`. */
12
+ readonly domain: GatewayDomain;
13
+ /** The `ssh` destination for the Gateway: `user@host` or a Host alias. The admin API forward connects through it. */
14
+ readonly destination: SshDestination;
15
+ /** The admin API port on the Gateway's loopback interface. */
16
+ readonly adminPort: GatewayAdminPort;
17
+ /** The admin API bearer token. */
18
+ readonly adminToken: Redacted.Redacted<string>;
19
+ /** The LLM providers the Gateway has credentials for. A harness whose provider isn't listed uses its own login through the forward proxy. */
20
+ readonly llmProviders: ReadonlyArray<GatewayLlmProvider>;
21
+ }
22
+ /**
23
+ * The container of each Attempt on an SSH Worker whose machine has Docker. Each limit that is
24
+ * `None` is derived from the machine at preflight: its total less a reserve for the machine's own
25
+ * processes, divided by the Attempts that run on it at once.
26
+ */
27
+ export interface SshWorkerContainer {
28
+ /** The image to run. If `None`, `ATTEMPT_IMAGE`. */
29
+ readonly image: Option.Option<ContainerImage>;
30
+ readonly cpus: Option.Option<ContainerCpus>;
31
+ readonly memoryMib: Option.Option<ContainerMemoryMib>;
32
+ /** The maximum number of processes and threads. */
33
+ readonly pids: Option.Option<ContainerPids>;
34
+ }
35
+ /** The LLM provider API keys that each Attempt on a provisioned Worker gets in its environment. */
36
+ export interface ProviderApiKeys {
37
+ /** Claude Code's `ANTHROPIC_API_KEY`. */
38
+ readonly anthropic: Option.Option<Redacted.Redacted<string>>;
39
+ /** Codex's `OPENAI_API_KEY`. */
40
+ readonly openai: Option.Option<Redacted.Redacted<string>>;
41
+ }
42
+ /** Creates each Attempt's machine as a Hetzner Cloud server from a snapshot. */
43
+ export interface HetznerProvisioner {
44
+ readonly provider: "hetzner";
45
+ /** The Hetzner Cloud API token, with read and write access to the project. */
46
+ readonly token: Redacted.Redacted<string>;
47
+ readonly serverType: HetznerServerType;
48
+ readonly location: HetznerLocation;
49
+ /** A snapshot's ID or description, or a system image's name. */
50
+ readonly image: HetznerImage;
51
+ /** The private network to attach each server to. Behind a Gateway, each server gets no public address. */
52
+ readonly network: Option.Option<HetznerResourceName>;
53
+ /** The project's firewall to apply to each server. */
54
+ readonly firewall: Option.Option<HetznerResourceName>;
55
+ /** The project's SSH key that each server authorizes. */
56
+ readonly sshKey: HetznerResourceName;
57
+ readonly sshUser: SshUser;
58
+ readonly labels: HetznerLabels;
59
+ readonly apiKeys: ProviderApiKeys;
60
+ }
61
+ /** Creates each Attempt's machine as a Boat sandbox. */
62
+ export interface BoatProvisioner {
63
+ readonly provider: "boat";
64
+ /** The Boat API key. */
65
+ readonly token: Redacted.Redacted<string>;
66
+ readonly size: BoatSize;
67
+ readonly apiKeys: ProviderApiKeys;
68
+ }
69
+ export type SshProvisioner = HetznerProvisioner | BoatProvisioner;
70
+ /** How the executor finds a Worker's machines: one long-lived machine, or one per Attempt. */
71
+ export type SshWorkerMachine = {
72
+ readonly _tag: "Fixed";
73
+ /** The `ssh` destination: `user@host` or a Host alias. */
74
+ readonly destination: SshDestination;
75
+ } | {
76
+ readonly _tag: "Provisioned";
77
+ readonly provisioner: SshProvisioner;
78
+ };
79
+ export interface SshWorkerAddress {
80
+ /**
81
+ * The Worker's name in `usine.yaml`; its state is in `workers/<name>` under `root`. A request
82
+ * for a Worker without an address fails with a `machine` capability error.
83
+ */
84
+ readonly name: WorkerName;
85
+ readonly machine: SshWorkerMachine;
86
+ /** The private key for authentication. If `None`, the SSH agent and configuration are used. */
87
+ readonly identityFile: Option.Option<SshIdentityFile>;
88
+ /** The SSH server port. If `None`, the SSH configuration's port, or a provisioned machine's, is used. */
89
+ readonly port: Option.Option<SshPort>;
90
+ /** The machine directory that contains `workers/<name>`. */
91
+ readonly root: WorkerRoot;
92
+ /** The Gateway in front of the machine. If `None`, each Attempt's token is written on the machine (ADR 0026). */
93
+ readonly gateway: Option.Option<SshWorkerGateway>;
94
+ /** Each Attempt's container. Used only if the machine has Docker. */
95
+ readonly container: SshWorkerContainer;
96
+ }
97
+ export interface SshExecutorOptions {
98
+ readonly workers: ReadonlyArray<SshWorkerAddress>;
99
+ /**
100
+ * The identity of this usine instance on the Workers' machines, which the instances that share
101
+ * a GitHub App share too. It labels each Attempt container, and tags each provisioned machine.
102
+ */
103
+ readonly instance: string;
104
+ /**
105
+ * Tells apart the instances that share a GitHub App. It labels each Attempt container too, so
106
+ * that a later process removes only this instance's containers.
107
+ */
108
+ readonly instanceId: InstanceId;
109
+ /** The local SSH command and the options placed before each Worker's port and identity file. Defaults to `ssh` in batch mode with keepalives. */
110
+ readonly ssh?: SshCommand;
111
+ /** The timeout for the parent task's cancellation, then separately for the Attempt to finish. Defaults to 10 seconds. */
112
+ readonly interruptGrace?: Parameters<typeof Effect.timeoutOption>[1];
113
+ /** Probes a brain's binary and login on a Worker's machine. Defaults to the `@usine/agents/preflight` checks over SSH, which send no prompt. Behind a Gateway, the Gateway checks also apply. */
114
+ readonly harnessReadiness?: (machine: SshMachine) => HarnessProbe;
115
+ /**
116
+ * The local directory for the host keys of provisioned machines, one file per machine, which
117
+ * the executor creates. Defaults to a temporary directory removed with the executor.
118
+ */
119
+ readonly knownHostsDirectory?: string;
120
+ /**
121
+ * The local directory where the executor records how to reach each long-lived Worker's machine
122
+ * when it adds the Worker, which `sshWorkerRecordsFor` reads. Defaults to a temporary
123
+ * directory removed with the executor.
124
+ */
125
+ readonly workerRecordsDirectory?: string;
126
+ /** The provider API endpoints, which tests replace. Defaults to the providers' own. */
127
+ readonly providerEndpoints?: Partial<Record<MachineProvider, string>>;
128
+ /** The longest wait for a created machine to answer over SSH. Defaults to 10 minutes. */
129
+ readonly machineReadyTimeout?: Duration.Input;
130
+ /** The pause between checks of a machine being created. Defaults to 3 seconds. */
131
+ readonly machinePollInterval?: Duration.Input;
132
+ }
133
+ export interface MachineGateway {
134
+ readonly settings: SshWorkerGateway;
135
+ readonly forward: GatewayForward;
136
+ readonly admin: GatewayAdmin;
137
+ /**
138
+ * The credential kind the Gateway uses for each provider, from the last preflight: Anthropic
139
+ * for Claude Code, OpenAI for Codex. `None` before the first preflight, for a provider that
140
+ * `llmProviders` doesn't list, or for Anthropic if the Gateway is too old to report it.
141
+ */
142
+ readonly credentials: Ref.Ref<GatewayCredentials>;
143
+ /** Whether a preflight already warned that the Gateway is too old to report its Anthropic credential kind. */
144
+ readonly warnedOfUnreportedCredential: Ref.Ref<boolean>;
145
+ }
146
+ /** The credential kind the Gateway holds for each provider; see `MachineGateway.credentials`. */
147
+ export type GatewayCredentials = Readonly<Record<GatewayLlmProvider, Option.Option<GatewayCredentialKind>>>;
148
+ export interface SshMachine extends RemoteMachine {
149
+ /** The Worker's address from `usine.yaml`; preflight checks its root. */
150
+ readonly address: SshWorkerAddress;
151
+ readonly gateway: Option.Option<MachineGateway>;
152
+ }
153
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/ssh/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/ssh/model.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import type { RemoteShell } from "@usine/agents/child-host";
2
+ import type { ClaudeLoginLock } from "@usine/agents/runtime";
3
+ import type { BrainSpec } from "@usine/core/agent/brain";
4
+ import type { WorkerName } from "@usine/core/worker";
5
+ import { Effect, Option } from "effect";
6
+ import { type ExecutorError, type PreflightReport, WorkerCapabilityError } from "../executor.ts";
7
+ import { type HarnessProbe } from "../harness-preflight.ts";
8
+ import type { RemoteCommands } from "../remote/commands.ts";
9
+ import type { SshContainers } from "./container.ts";
10
+ import type { SshMachine } from "./model.ts";
11
+ import type { SshWorkerEntry } from "./workers.ts";
12
+ /**
13
+ * Verifies an SSH Worker's machine: SSH connects, the root is a writable directory, Git runs,
14
+ * memory is enough for the concurrent Attempts, the Gateway (if any) responds and is reachable,
15
+ * Docker and the Attempt image work if the machine has Docker, and each brain is ready. Pulls
16
+ * the Attempt image and changes nothing else. With Docker, it derives the container limits that
17
+ * `usine.yaml` leaves unset for `maxConcurrentAttempts` containers, which the report gives, and
18
+ * brains are probed in a container from the image; without, as the SSH user, which the report
19
+ * gives as degraded. Claude brains are checked under the Worker's lock in `logins`.
20
+ */
21
+ export declare const sshMachinePreflightFor: (commands: RemoteCommands, harnessProbe: (machine: SshMachine, shell: RemoteShell) => HarnessProbe, logins: ClaudeLoginLock["Service"], containers: SshContainers) => (machine: SshMachine, brains: readonly BrainSpec[], maxConcurrentAttempts: number) => Effect.Effect<{
22
+ degraded: Option.Option<string>;
23
+ deferred: Option.Option<string>;
24
+ containerLimits: Option.Option<import("@usine/core/worker").ContainerLimits>;
25
+ }, import("../executor.ts").CredentialUnavailableError | import("../executor.ts").ExecutorOperationError | WorkerCapabilityError, never>;
26
+ /**
27
+ * Verifies an SSH Worker: a long-lived Worker's machine with `verifyMachine`, or a provisioned
28
+ * Worker with `verifyProvisioned`, which has no machine before an Attempt.
29
+ */
30
+ export declare const sshPreflightFor: (workerFor: (worker: WorkerName) => Effect.Effect<SshWorkerEntry, ExecutorError>, verifyMachine: ReturnType<typeof sshMachinePreflightFor>, verifyProvisioned: (entry: SshWorkerEntry, brains: ReadonlyArray<BrainSpec>, maxConcurrentAttempts: number) => Effect.Effect<PreflightReport, ExecutorError>) => (worker: string & import("effect/Brand").Brand<"WorkerName">, brains: readonly BrainSpec[], maxConcurrentAttempts: number) => Effect.Effect<PreflightReport | {
31
+ degraded: Option.Option<string>;
32
+ deferred: Option.Option<string>;
33
+ containerLimits: Option.Option<import("@usine/core/worker").ContainerLimits>;
34
+ }, import("../executor.ts").CredentialUnavailableError | import("../executor.ts").ExecutorOperationError | WorkerCapabilityError, never>;
35
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/ssh/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAgB,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,qBAAqB,EAEtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,YAAY,EAA0B,MAAM,yBAAyB,CAAC;AAEpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAYnD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,aACvB,cAAc,gBACV,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,KAAK,YAAY,UAC/D,eAAe,CAAC,SAAS,CAAC,cACtB,aAAa;;;;wIAqHvB,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,eAAe,cACf,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC,iBAChE,UAAU,CAAC,OAAO,sBAAsB,CAAC,qBACrC,CACjB,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,EAChC,qBAAqB,EAAE,MAAM,KAC1B,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC;;;;wIAa3B,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { Array as Arr, Effect, Match, Option } from "effect";
2
+ import { logPreflightFailure, WorkerCapabilityError, } from "../executor.js";
3
+ import { verifyHarnessReadiness } from "../harness-preflight.js";
4
+ import { verifyLinuxMemory } from "../memory-preflight.js";
5
+ import { gatewayExportedEnvironment } from "./gateway/environment.js";
6
+ import { verifyGateway } from "./gateway/preflight.js";
7
+ import { limitFields } from "./limits.js";
8
+ /** A script that succeeds if `$1` is a directory the SSH user can write to. */
9
+ const WRITABLE_DIRECTORY_SCRIPT = 'test -d "$1" && test -w "$1"';
10
+ const capabilityError = (worker, capability, message, cause) => new WorkerCapabilityError({ worker, capability, message, cause });
11
+ /**
12
+ * Verifies an SSH Worker's machine: SSH connects, the root is a writable directory, Git runs,
13
+ * memory is enough for the concurrent Attempts, the Gateway (if any) responds and is reachable,
14
+ * Docker and the Attempt image work if the machine has Docker, and each brain is ready. Pulls
15
+ * the Attempt image and changes nothing else. With Docker, it derives the container limits that
16
+ * `usine.yaml` leaves unset for `maxConcurrentAttempts` containers, which the report gives, and
17
+ * brains are probed in a container from the image; without, as the SSH user, which the report
18
+ * gives as degraded. Claude brains are checked under the Worker's lock in `logins`.
19
+ */
20
+ export const sshMachinePreflightFor = (commands, harnessProbe, logins, containers) => Effect.fn("SshExecutor.verifyMachine")(function* (machine, brains, maxConcurrentAttempts) {
21
+ const worker = machine.address.name;
22
+ const host = machine.shell.destination;
23
+ const root = machine.address.root;
24
+ yield* Effect.annotateCurrentSpan({ worker, host, maxConcurrentAttempts });
25
+ const connected = yield* commands
26
+ .inspect("preflight", machine.shell, "true", [])
27
+ .pipe(Effect.mapError((cause) => capabilityError(worker, "machine", `Can't connect to SSH worker "${worker}" at ${host}`, cause)));
28
+ if (connected.exitCode !== 0)
29
+ return yield* capabilityError(worker, "machine", `Can't connect to SSH worker "${worker}" at ${host}`, new Error(connected.stderr.trim() || `ssh exited with code ${String(connected.exitCode)}`));
30
+ const rootChecked = yield* commands
31
+ .inspect("preflight", machine.shell, "sh", ["-c", WRITABLE_DIRECTORY_SCRIPT, "sh", root])
32
+ .pipe(Effect.mapError((cause) => capabilityError(worker, "machine", `Can't inspect ${root} on ${host}`, cause)));
33
+ if (rootChecked.exitCode !== 0)
34
+ return yield* capabilityError(worker, "machine", `The root ${root} of SSH worker "${worker}" isn't a writable directory on ${host}`, new Error(rootChecked.stderr.trim() || `exit code ${String(rootChecked.exitCode)}`));
35
+ const gitVersion = (yield* commands
36
+ .run("preflight", machine.shell, "git", ["--version"])
37
+ .pipe(Effect.mapError((cause) => capabilityError(worker, "toolchain", `Worker "${worker}" can't run Git`, cause)))).trim();
38
+ if (!gitVersion.startsWith("git version "))
39
+ return yield* capabilityError(worker, "toolchain", `Worker "${worker}" returned an unrecognized Git version`, new Error(gitVersion));
40
+ yield* verifyLinuxMemory(worker, maxConcurrentAttempts, commands.run("preflight", machine.shell, "cat", ["/proc/meminfo"]));
41
+ if (Option.isSome(machine.gateway))
42
+ yield* verifyGateway(commands, worker, machine, machine.gateway.value);
43
+ const containerCheck = yield* containers.verify(machine, maxConcurrentAttempts);
44
+ const harnesses = Arr.dedupe(brains.map(({ harness }) => harness));
45
+ const probe = (shell) => verifyHarnessReadiness(worker, machine.provisioned, brains, harnessProbe(machine, shell), logins);
46
+ yield* containerCheck._tag === "Container" && harnesses.length > 0
47
+ ? Effect.scoped(containers
48
+ .preflightShell(machine, containerCheck.settings, harnesses, Option.match(machine.gateway, {
49
+ onNone: () => ({}),
50
+ onSome: ({ settings }) => gatewayExportedEnvironment(settings, `${machine.workerDirectory}/gh`),
51
+ }))
52
+ .pipe(Effect.flatMap(probe)))
53
+ : probe(machine.shell);
54
+ const fields = {
55
+ worker,
56
+ host,
57
+ root,
58
+ gitVersion,
59
+ isolation: containerCheck._tag,
60
+ harnesses: harnesses.join(","),
61
+ };
62
+ return yield* Match.valueTags(containerCheck, {
63
+ Container: ({ limits }) => Effect.as(Effect.logInfo("SSH Worker preflight verified", { ...fields, ...limitFields(limits) }), {
64
+ degraded: Option.none(),
65
+ deferred: Option.none(),
66
+ containerLimits: Option.some(limits.limits),
67
+ }),
68
+ SharedUser: ({ degraded }) => Effect.as(Effect.logInfo("SSH Worker preflight verified", fields), {
69
+ degraded: Option.some(degraded),
70
+ deferred: Option.none(),
71
+ containerLimits: Option.none(),
72
+ }),
73
+ });
74
+ });
75
+ /**
76
+ * Verifies an SSH Worker: a long-lived Worker's machine with `verifyMachine`, or a provisioned
77
+ * Worker with `verifyProvisioned`, which has no machine before an Attempt.
78
+ */
79
+ export const sshPreflightFor = (workerFor, verifyMachine, verifyProvisioned) => Effect.fn("SshExecutor.preflight")(function* (worker, brains, maxConcurrentAttempts) {
80
+ yield* Effect.annotateCurrentSpan({ worker, maxConcurrentAttempts });
81
+ const entry = yield* workerFor(worker);
82
+ return yield* Option.match(entry.machine, {
83
+ onNone: () => verifyProvisioned(entry, brains, maxConcurrentAttempts),
84
+ onSome: (machine) => verifyMachine(machine, brains, maxConcurrentAttempts),
85
+ });
86
+ }, logPreflightFailure);
87
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/ssh/preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type WorkerName } from "@usine/core/worker";
2
+ import { Effect, Option } from "effect";
3
+ import { MachineUnavailableError, WorkerCapabilityError } from "../../executor.ts";
4
+ import type { BoatProvisioner } from "../model.ts";
5
+ import { type ListedMachine, type MachineTags, ProviderApiError } from "./model.ts";
6
+ /** Boat's public API. */
7
+ export declare const BOAT_API = "https://boat.dev/api/v1";
8
+ /**
9
+ * Returns the Boat driver for `worker`'s sandboxes (#550). Each sandbox is created with none of
10
+ * the account's secrets (`noEnv`), no snapshots, which Boat can store outside the EU, no command
11
+ * monitoring, and no automatic stop, and the executor deletes it. Its name carries the instance,
12
+ * Worker, Run, creating process, and Attempt, because Boat has no labels. `publicKey` is the
13
+ * OpenSSH public key each sandbox authorizes.
14
+ */
15
+ export declare const boatDriverFor: (worker: WorkerName, settings: BoatProvisioner, publicKey: string, base?: string) => Effect.Effect<{
16
+ provider: "boat";
17
+ verify: (_worker: string & import("effect/Brand").Brand<"WorkerName">, maxConcurrentAttempts: number) => Effect.Effect<{
18
+ warnings: string[];
19
+ }, WorkerCapabilityError, never>;
20
+ create: (_name: string, tags: MachineTags) => Effect.Effect<{
21
+ id: string & import("effect/Brand").Brand<"MachineId">;
22
+ hostKey: Option.Option<never>;
23
+ }, MachineUnavailableError, never>;
24
+ endpoint: (id: string & import("effect/Brand").Brand<"MachineId">, interval: Effect.Effect<void, never, never>) => Effect.Effect<{
25
+ destination: string & import("effect/Brand").Brand<"SshDestination">;
26
+ port: Option.Option<number & import("effect/Brand").Brand<"SshPort">>;
27
+ hostKey: Option.Option<string>;
28
+ }, MachineUnavailableError, never>;
29
+ tag: (id: string & import("effect/Brand").Brand<"MachineId">, tags: MachineTags) => Effect.Effect<void, ProviderApiError, never>;
30
+ remove: (id: string & import("effect/Brand").Brand<"MachineId">) => Effect.Effect<void, ProviderApiError, never>;
31
+ list: (instance: string, listed: string & import("effect/Brand").Brand<"WorkerName">) => Effect.Effect<ListedMachine[], ProviderApiError, never>;
32
+ }, never, import("effect/http/HttpClient").HttpClient>;
33
+ //# sourceMappingURL=boat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boat.d.ts","sourceRoot":"","sources":["../../../src/ssh/provisioner/boat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EAAY,MAAM,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,uBAAuB,EAEvB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAEpB,yBAAyB;AACzB,eAAO,MAAM,QAAQ,4BAA4B,CAAC;AA+FlD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,WAChB,UAAU,YACR,eAAe,aACd,MAAM,SACX,MAAM;;;;;;;;;;;;;;;;;sDAsPV,CAAC"}
@@ -0,0 +1,247 @@
1
+ import { MachineId, SshDestination, SshPort } from "@usine/core/worker";
2
+ import { DateTime, Effect, Option, Schema } from "effect";
3
+ import { MachineUnavailableError, WorkerCapabilityError, } from "../../executor.js";
4
+ import { failureText, providerApiFor } from "./http.js";
5
+ import { attemptTag, ProviderApiError, tagValue, } from "./model.js";
6
+ /** Boat's public API. */
7
+ export const BOAT_API = "https://boat.dev/api/v1";
8
+ /** The prefix of the name of every sandbox that usine creates. */
9
+ const NAME_PREFIX = "usine";
10
+ const Sandbox = Schema.Struct({
11
+ id: Schema.String,
12
+ name: Schema.String,
13
+ state: Schema.String,
14
+ error: Schema.optionalKey(Schema.NullOr(Schema.String)),
15
+ createdAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
16
+ });
17
+ const OneSandbox = Schema.Struct({ sandbox: Sandbox });
18
+ const SandboxList = Schema.Struct({
19
+ sandboxes: Schema.Array(Sandbox),
20
+ pageInfo: Schema.optionalKey(Schema.Struct({ nextCursor: Schema.NullOr(Schema.String), hasMore: Schema.Boolean })),
21
+ });
22
+ const SshKey = Schema.Struct({
23
+ machineIp: Schema.optionalKey(Schema.NullOr(Schema.String)),
24
+ sshUser: Schema.optionalKey(Schema.String),
25
+ sshEndpoint: Schema.optionalKey(Schema.NullOr(Schema.String)),
26
+ hostKey: Schema.optionalKey(Schema.String),
27
+ });
28
+ const Limits = Schema.Struct({
29
+ canStart: Schema.optionalKey(Schema.Boolean),
30
+ activeSandboxes: Schema.optionalKey(Schema.Int),
31
+ maxActiveSandboxes: Schema.optionalKey(Schema.NullOr(Schema.Int)),
32
+ starts: Schema.optionalKey(Schema.Struct({
33
+ unlimited: Schema.optionalKey(Schema.Boolean),
34
+ day: Schema.optionalKey(Schema.Struct({ remaining: Schema.NullOr(Schema.Int) })),
35
+ })),
36
+ machineTypes: Schema.optionalKey(Schema.Array(Schema.Struct({
37
+ type: Schema.String,
38
+ available: Schema.Boolean,
39
+ reason: Schema.optionalKey(Schema.NullOr(Schema.String)),
40
+ }))),
41
+ });
42
+ /** The states in which a sandbox accepts SSH. */
43
+ const RUNNING = new Set(["ready", "idle", "running"]);
44
+ /** The states from which a new sandbox never runs. */
45
+ const ENDED = new Set(["error", "cancelled", "archiving", "archived"]);
46
+ /** Returns the name that carries a sandbox's tags; at most 120 characters, Boat's limit. */
47
+ const nameOf = (tags) => [
48
+ NAME_PREFIX,
49
+ tagValue(tags.instance, 24),
50
+ tagValue(tags.worker, 24),
51
+ tagValue(tags.run, 32),
52
+ tagValue(tags.process, 12),
53
+ Option.match(tags.attempt, { onNone: () => "-", onSome: attemptTag }),
54
+ ].join(" ");
55
+ /** Returns the reason a creation failure is retried, by Boat's documented statuses. */
56
+ const creationReason = (failure) => {
57
+ if (failure._tag === "ProviderUnanswered")
58
+ return "unreachable";
59
+ if (failure.status === 503)
60
+ return "capacity";
61
+ if (failure.status === 402 || failure.status === 429)
62
+ return "quota";
63
+ if (failure.status === 401 || failure.status === 403 || failure.status === 400)
64
+ return "refused";
65
+ return "provider";
66
+ };
67
+ /** Returns where to connect: Boat's `sshEndpoint` forward if set, else the machine's address. */
68
+ const destinationOf = (answer) => {
69
+ const forwarded = answer.sshEndpoint ?? undefined;
70
+ if (forwarded !== undefined) {
71
+ const separator = forwarded.lastIndexOf(":");
72
+ const port = Number(forwarded.slice(separator + 1));
73
+ if (separator > 0 && Number.isInteger(port) && port > 0 && port < 65_536)
74
+ return Option.some({
75
+ host: forwarded.slice(0, separator),
76
+ port: Option.some(SshPort.make(port)),
77
+ });
78
+ }
79
+ return Option.map(Option.fromNullishOr(answer.machineIp), (host) => ({
80
+ host,
81
+ port: Option.none(),
82
+ }));
83
+ };
84
+ /**
85
+ * Returns the Boat driver for `worker`'s sandboxes (#550). Each sandbox is created with none of
86
+ * the account's secrets (`noEnv`), no snapshots, which Boat can store outside the EU, no command
87
+ * monitoring, and no automatic stop, and the executor deletes it. Its name carries the instance,
88
+ * Worker, Run, creating process, and Attempt, because Boat has no labels. `publicKey` is the
89
+ * OpenSSH public key each sandbox authorizes.
90
+ */
91
+ export const boatDriverFor = (worker, settings, publicKey, base = BOAT_API) => Effect.gen(function* () {
92
+ const api = yield* providerApiFor("boat", base, settings.token);
93
+ const apiError = (operation) => (failure) => new ProviderApiError({
94
+ provider: "boat",
95
+ operation,
96
+ status: failure._tag === "ProviderRefused" ? Option.some(failure.status) : Option.none(),
97
+ message: `Boat couldn't ${operation}: ${failureText(failure)}`,
98
+ });
99
+ const providerGap = (message, cause) => new WorkerCapabilityError({ worker, capability: "provider", message, cause });
100
+ const unavailable = (reason, message) => new MachineUnavailableError({ worker, provider: "boat", reason, message });
101
+ const verify = Effect.fn("BoatDriver.verify")(function* (_worker, maxConcurrentAttempts) {
102
+ yield* Effect.annotateCurrentSpan({ worker, maxConcurrentAttempts });
103
+ yield* api.send("GET", "/me");
104
+ const limits = yield* api
105
+ .send("GET", "/limits")
106
+ .pipe(Effect.flatMap(api.decode(Limits, "read the limits")));
107
+ const machineType = limits.machineTypes?.find(({ type }) => type === settings.size);
108
+ if (machineType !== undefined && !machineType.available)
109
+ return yield* providerGap(`Boat doesn't offer machine size ${settings.size} to the account of worker "${worker}"${machineType.reason === undefined || machineType.reason === null
110
+ ? ""
111
+ : `: ${machineType.reason}`}`, new Error(`size ${settings.size} unavailable`));
112
+ const maximum = limits.maxActiveSandboxes ?? undefined;
113
+ const warnings = [
114
+ ...(limits.canStart === false
115
+ ? [`Boat reports that the account of worker "${worker}" can't start sandboxes now.`]
116
+ : []),
117
+ ...(maximum !== undefined && maximum < maxConcurrentAttempts
118
+ ? [
119
+ `Boat allows ${String(maximum)} active sandboxes, fewer than max_concurrent_attempts (${String(maxConcurrentAttempts)}).`,
120
+ ]
121
+ : []),
122
+ ];
123
+ yield* Effect.logInfo("Boat provisioner verified", {
124
+ worker,
125
+ size: settings.size,
126
+ canStart: limits.canStart ?? "not reported",
127
+ activeSandboxes: limits.activeSandboxes ?? "not reported",
128
+ maxActiveSandboxes: maximum ?? "not reported",
129
+ startsLeftToday: limits.starts?.unlimited === true
130
+ ? "unlimited"
131
+ : (limits.starts?.day?.remaining ?? "not reported"),
132
+ });
133
+ return { warnings };
134
+ }, (effect) => effect.pipe(Effect.catchTags({
135
+ ProviderUnanswered: (failure) => Effect.fail(providerGap(`Can't reach the Boat API for worker "${worker}": ${failure.detail}`, failure)),
136
+ ProviderRefused: (failure) => Effect.fail(providerGap(failure.status === 401
137
+ ? `Boat refused the API key of worker "${worker}". Create one with boat api-key create, and set it in provisioner.token_env.`
138
+ : `Boat refused a preflight request of worker "${worker}": ${failureText(failure)}`, failure)),
139
+ })));
140
+ const create = Effect.fn("BoatDriver.create")(function* (_name, tags) {
141
+ yield* Effect.annotateCurrentSpan({ worker, run: tags.run });
142
+ const response = yield* api.send("POST", "/sandboxes", {
143
+ body: {
144
+ type: settings.size,
145
+ name: nameOf(tags),
146
+ ttlSeconds: null,
147
+ noEnv: true,
148
+ snapshots: false,
149
+ monitoring: false,
150
+ failFast: true,
151
+ },
152
+ });
153
+ const created = yield* api.decode(OneSandbox, "create a sandbox")(response);
154
+ return { id: MachineId.make(created.sandbox.id), hostKey: Option.none() };
155
+ }, (effect) => effect.pipe(Effect.catchTags({
156
+ ProviderUnanswered: (failure) => Effect.fail(unavailable("unreachable", `Can't reach the Boat API to create a sandbox: ${failure.detail}`)),
157
+ ProviderRefused: (failure) => Effect.fail(unavailable(creationReason(failure), `Boat didn't create a ${settings.size} sandbox: ${failureText(failure)}`)),
158
+ })));
159
+ const endpoint = Effect.fn("BoatDriver.endpoint")(function* (id, interval) {
160
+ yield* Effect.annotateCurrentSpan({ worker, machine: id });
161
+ while (true) {
162
+ const { sandbox } = yield* api
163
+ .send("GET", `/sandboxes/${id}`)
164
+ .pipe(Effect.flatMap(api.decode(OneSandbox, "read the sandbox")));
165
+ if (RUNNING.has(sandbox.state))
166
+ break;
167
+ if (ENDED.has(sandbox.state))
168
+ return yield* unavailable("capacity", `The new Boat sandbox ended in state ${sandbox.state}${sandbox.error === undefined || sandbox.error === null ? "" : `: ${sandbox.error}`}`);
169
+ yield* Effect.logDebug("Waiting for the Boat sandbox to be ready", {
170
+ worker,
171
+ machine: id,
172
+ state: sandbox.state,
173
+ });
174
+ yield* interval;
175
+ }
176
+ const answer = yield* api
177
+ .send("POST", `/sandboxes/${id}/sshkey`, { body: { key: publicKey } })
178
+ .pipe(Effect.flatMap(api.decode(SshKey, "add the SSH key")));
179
+ const target = destinationOf(answer);
180
+ if (Option.isNone(target))
181
+ return yield* unavailable("provider", "Boat reported no SSH address for the new sandbox");
182
+ const user = answer.sshUser ?? "user";
183
+ return {
184
+ destination: SshDestination.make(`${user}@${target.value.host}`),
185
+ port: target.value.port,
186
+ hostKey: Option.fromUndefinedOr(answer.hostKey),
187
+ };
188
+ }, (effect) => effect.pipe(Effect.catchTags({
189
+ ProviderUnanswered: (failure) => Effect.fail(unavailable("unreachable", `Can't reach the Boat API to read a new sandbox: ${failure.detail}`)),
190
+ ProviderRefused: (failure) => Effect.fail(unavailable(failure.status === 404 ? "capacity" : creationReason(failure), failure.status === 404
191
+ ? "Boat removed the new sandbox before it was ready"
192
+ : `Boat couldn't ready the new sandbox: ${failureText(failure)}`)),
193
+ })));
194
+ const tag = Effect.fn("BoatDriver.tag")(function* (id, tags) {
195
+ yield* Effect.annotateCurrentSpan({ worker, machine: id });
196
+ yield* api
197
+ .send("PATCH", `/sandboxes/${id}`, { body: { name: nameOf(tags) } })
198
+ .pipe(Effect.mapError(apiError("rename the sandbox")));
199
+ });
200
+ const remove = Effect.fn("BoatDriver.remove")(function* (id) {
201
+ yield* Effect.annotateCurrentSpan({ worker, machine: id });
202
+ yield* api
203
+ .send("DELETE", `/sandboxes/${id}`, { headers: { "X-Ascii-Confirm-Delete": id } })
204
+ .pipe(Effect.asVoid, Effect.catchIf((failure) => failure._tag === "ProviderRefused" && failure.status === 404, () => Effect.void), Effect.mapError(apiError("delete the sandbox")));
205
+ });
206
+ const list = Effect.fn("BoatDriver.list")(function* (instance, listed) {
207
+ yield* Effect.annotateCurrentSpan({ worker: listed });
208
+ const prefix = `${NAME_PREFIX} ${tagValue(instance, 24)} ${tagValue(listed, 24)} `;
209
+ const machines = [];
210
+ let cursor = null;
211
+ do {
212
+ const query = new Map([["limit", "200"]]);
213
+ if (cursor !== null)
214
+ query.set("cursor", cursor);
215
+ const page = yield* api
216
+ .send("GET", "/sandboxes", { query: Object.fromEntries(query) })
217
+ .pipe(Effect.flatMap(api.decode(SandboxList, "list sandboxes")), Effect.mapError(apiError("list the sandboxes")));
218
+ for (const sandbox of page.sandboxes) {
219
+ if (!sandbox.name.startsWith(prefix))
220
+ continue;
221
+ const [, , workerTag = "", run = "", process = "", attempt = "-"] = sandbox.name.split(" ");
222
+ machines.push({
223
+ id: MachineId.make(sandbox.id),
224
+ tags: {
225
+ worker: workerTag,
226
+ run,
227
+ process,
228
+ attempt: attempt === "-" ? Option.none() : Option.some(attempt),
229
+ },
230
+ createdAt: Option.getOrElse(Option.flatMap(Option.fromNullishOr(sandbox.createdAt), (at) => DateTime.make(at)), () => DateTime.makeUnsafe(0)),
231
+ });
232
+ }
233
+ cursor = page.pageInfo?.hasMore === true ? page.pageInfo.nextCursor : null;
234
+ } while (cursor !== null);
235
+ return machines;
236
+ });
237
+ return {
238
+ provider: "boat",
239
+ verify,
240
+ create,
241
+ endpoint,
242
+ tag,
243
+ remove,
244
+ list,
245
+ };
246
+ });
247
+ //# sourceMappingURL=boat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boat.js","sourceRoot":"","sources":["../../../src/ssh/provisioner/boat.ts"],"names":[],"mappings":"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"}