@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,320 @@
1
+ import { SSH_FAILURE } from "@usine/agents/child-host";
2
+ import { unsupportedCodexVersion } from "@usine/agents/preflight";
3
+ import { HarnessReady, HarnessUnavailableError } from "@usine/core/agent/capability";
4
+ import { Effect, Option, Redacted, Ref } from "effect";
5
+ import { ExecutorOperationError, WorkerCapabilityError } from "../../executor.js";
6
+ import { sendingShell } from "../../remote/commands.js";
7
+ import { forwardProxyUrl, GATEWAY_SENT_VARIABLES, gatewayEnvironment } from "./environment.js";
8
+ /** A placeholder caller token that lets preflight check the sent variables without attaching anything. */
9
+ const PROBE_TOKEN = Redacted.make("usine-preflight");
10
+ /** A script that takes name and value pairs and prints each name whose variable doesn't have that value on the machine. */
11
+ const SENT_VARIABLES_SCRIPT = `
12
+ while [ "$#" -gt 1 ]; do
13
+ test "$(printenv "$1")" = "$2" || echo "$1"
14
+ shift 2
15
+ done
16
+ `.trim();
17
+ /** Matches the Gateway's refusal header for a request without a caller token. */
18
+ const REFUSED_WITHOUT_TOKEN = /^x-usine-gateway-refusal:\s*missing-caller-token\s*$/imu;
19
+ const gatewayGap = (worker, message, cause) => new WorkerCapabilityError({ worker, capability: "gateway", message, cause });
20
+ /** Returns the error for an SSH transport failure, which isn't a capability gap. */
21
+ const unreachable = (host, command, result) => new ExecutorOperationError({
22
+ operation: "preflight",
23
+ message: `Can't reach ${host} to run ${command}`,
24
+ cause: new Error(result.stderr.trim() || `ssh exited with code ${SSH_FAILURE}`),
25
+ });
26
+ /**
27
+ * Logs a warning, once per Worker, that the Gateway is too old to report its Anthropic
28
+ * credential kind (#248). Later preflights log at debug level.
29
+ */
30
+ const warnOfUnreportedCredential = (worker, gateway) => Ref.getAndSet(gateway.warnedOfUnreportedCredential, true).pipe(Effect.flatMap((warned) => warned
31
+ ? Effect.logDebug("SSH Worker's Gateway still does not report the kind of its Anthropic credential; already warned for this Worker", { worker, gateway: gateway.forward.gateway })
32
+ : Effect.logWarning("SSH Worker's Gateway does not report the kind of its Anthropic credential; its Claude Code children count in the Claude plan pool until the Gateway is upgraded", { worker, gateway: gateway.forward.gateway })));
33
+ /** Each provider's display name and the Gateway variables that set its credential. */
34
+ const PROVIDER_CREDENTIALS = {
35
+ anthropic: {
36
+ label: "Anthropic",
37
+ variables: "ANTHROPIC_API_KEY or USINE_GATEWAY_ANTHROPIC_SUBSCRIPTION_TOKEN",
38
+ },
39
+ openai: { label: "OpenAI", variables: "OPENAI_API_KEY or USINE_GATEWAY_CHATGPT_AUTH_FILE" },
40
+ };
41
+ const NO_CREDENTIALS = { anthropic: Option.none(), openai: Option.none() };
42
+ /**
43
+ * Reads the Gateway's credential kind for each provider in the Worker's `llmProviders` and
44
+ * records them for the Worker (ADR 0013). Fails if the Gateway has no credential for a listed
45
+ * provider. If the Gateway is too old to report credentials, keeps the Claude plan pool for
46
+ * Anthropic with a warning, and fails for OpenAI, which such a Gateway can't serve for Codex.
47
+ */
48
+ const recordProviderCredentials = Effect.fn("SshExecutor.recordProviderCredentials")(function* (worker, gateway) {
49
+ const listed = gateway.settings.llmProviders;
50
+ yield* Effect.annotateCurrentSpan({
51
+ worker,
52
+ gateway: gateway.forward.gateway,
53
+ llmProviders: listed.join(","),
54
+ });
55
+ if (listed.length === 0) {
56
+ yield* Ref.set(gateway.credentials, NO_CREDENTIALS);
57
+ return NO_CREDENTIALS;
58
+ }
59
+ const reported = yield* gateway.admin.providers.pipe(Effect.mapError((error) => gatewayGap(worker, error.message, error)));
60
+ if (Option.isNone(reported)) {
61
+ if (listed.includes("openai"))
62
+ return yield* gatewayGap(worker, `The gateway of SSH worker "${worker}" doesn't report its LLM credentials, so Codex can't use its OpenAI credential. Upgrade @usine/gateway on the gateway, or remove openai from llm_providers.`, new Error("the gateway answered 404 for GET /v1/providers"));
63
+ yield* warnOfUnreportedCredential(worker, gateway);
64
+ yield* Ref.set(gateway.credentials, NO_CREDENTIALS);
65
+ return NO_CREDENTIALS;
66
+ }
67
+ const { providers } = reported.value;
68
+ const kindOf = (provider) => Option.fromNullishOr(providers.find((held) => held.provider === provider)?.credential);
69
+ const missing = listed.find((provider) => Option.isNone(kindOf(provider)));
70
+ if (missing !== undefined)
71
+ return yield* gatewayGap(worker, `The gateway of SSH worker "${worker}" has no ${PROVIDER_CREDENTIALS[missing].label} credential, but the worker's llm_providers includes ${missing}. Set ${PROVIDER_CREDENTIALS[missing].variables} on the gateway, or remove ${missing} from llm_providers.`, new Error(`the gateway reports ${providers.map(({ provider }) => provider).join(", ") || "no provider"}`));
72
+ const credentials = {
73
+ anthropic: listed.includes("anthropic") ? kindOf("anthropic") : Option.none(),
74
+ openai: listed.includes("openai") ? kindOf("openai") : Option.none(),
75
+ };
76
+ yield* Ref.set(gateway.credentials, credentials);
77
+ return credentials;
78
+ });
79
+ /** Returns how a provider's harness authenticates on the Worker, for log lines. */
80
+ const credentialMode = (kind, listed) => Option.match(kind, {
81
+ onNone: () => (listed ? "gateway-unreported" : "worker-login"),
82
+ onSome: (credential) => `gateway-${credential}`,
83
+ });
84
+ /**
85
+ * Returns the credential kind the Gateway injects for `harness`, as recorded by the last
86
+ * preflight: Anthropic's for Claude Code, OpenAI's for Codex. Pi uses its own login.
87
+ */
88
+ export const gatewayCredentialFor = (gateway, harness) => {
89
+ switch (harness) {
90
+ case "claude":
91
+ return Effect.map(Ref.get(gateway.credentials), ({ anthropic }) => anthropic);
92
+ case "codex":
93
+ return Effect.map(Ref.get(gateway.credentials), ({ openai }) => openai);
94
+ case "pi":
95
+ return Effect.succeedNone;
96
+ }
97
+ };
98
+ /**
99
+ * Verifies the Gateway's admin API for an SSH Worker without attaching anything: the forward
100
+ * opens and the admin API accepts the admin token. Records the Gateway's credential kind for
101
+ * each provider in the Worker's `llmProviders`. Needs no Worker machine.
102
+ */
103
+ export const verifyGatewayAdmin = Effect.fn("SshExecutor.verifyGatewayAdmin")(function* (worker, gateway) {
104
+ const { admin, forward, settings } = gateway;
105
+ yield* Effect.annotateCurrentSpan({ worker, domain: settings.domain, gateway: forward.gateway });
106
+ const open = yield* forward.current.pipe(Effect.catchTag("GatewayForwardClosed", (closed) => forward.reopen(closed.generation)), Effect.mapError((closed) => gatewayGap(worker, `Can't open the forward to the gateway admin API of SSH worker "${worker}" through ${closed.gateway}: ${closed.reason}. Run ${forward.command} to see the SSH error.`, closed)));
107
+ const { attachments } = yield* admin.list.pipe(Effect.mapError((error) => gatewayGap(worker, error.message, error)));
108
+ const credentials = yield* recordProviderCredentials(worker, gateway);
109
+ yield* Effect.logInfo("SSH Worker Gateway admin API verified", {
110
+ worker,
111
+ domain: settings.domain,
112
+ gateway: forward.gateway,
113
+ adminPort: settings.adminPort,
114
+ forwardPort: open.url.port,
115
+ llmProviders: settings.llmProviders.join(","),
116
+ anthropic: credentialMode(credentials.anthropic, settings.llmProviders.includes("anthropic")),
117
+ openai: credentialMode(credentials.openai, settings.llmProviders.includes("openai")),
118
+ attachments: attachments.filter((attachment) => attachment.worker === worker).length,
119
+ });
120
+ });
121
+ /**
122
+ * Verifies the Gateway from an SSH Worker's machine without attaching anything: the machine's
123
+ * sshd accepts the caller-token variables, and the machine can connect to `git.<domain>`, which
124
+ * rejects a request without a caller token.
125
+ */
126
+ const verifyGatewayFromMachine = Effect.fn("SshExecutor.verifyGatewayFromMachine")(function* (commands, worker, machine, gateway) {
127
+ const { forward, settings } = gateway;
128
+ const gitHost = `git.${settings.domain}`;
129
+ yield* Effect.annotateCurrentSpan({ worker, domain: settings.domain, gateway: forward.gateway });
130
+ const { sent } = gatewayEnvironment({
131
+ domain: settings.domain,
132
+ llmProviders: settings.llmProviders,
133
+ ghConfigDirectory: machine.workerDirectory,
134
+ codexHome: machine.workerDirectory,
135
+ callerToken: PROBE_TOKEN,
136
+ });
137
+ const received = yield* commands.run("preflight", sendingShell(machine.shell, sent), "sh", [
138
+ "-c",
139
+ SENT_VARIABLES_SCRIPT,
140
+ "sh",
141
+ ...Object.entries(sent).flatMap(([name, value]) => [name, Redacted.value(value)]),
142
+ ]);
143
+ const dropped = received.split("\n").filter((name) => name !== "");
144
+ if (dropped.length > 0)
145
+ return yield* gatewayGap(worker, `The sshd on SSH worker "${worker}" doesn't accept ${dropped.join(", ")}. Add AcceptEnv ${GATEWAY_SENT_VARIABLES.join(" ")} to its sshd configuration.`, new Error(`variables not received: ${dropped.join(" ")}`));
146
+ const probed = yield* commands.inspect("preflight", machine.shell, "curl", [
147
+ "-sS",
148
+ "--noproxy",
149
+ "*",
150
+ "--max-time",
151
+ "15",
152
+ "-o",
153
+ "/dev/null",
154
+ "-D",
155
+ "-",
156
+ `http://${gitHost}/`,
157
+ ]);
158
+ if (probed.exitCode === SSH_FAILURE)
159
+ return yield* unreachable(machine.shell.destination, "curl", probed);
160
+ if (probed.exitCode !== 0 || !REFUSED_WITHOUT_TOKEN.test(probed.stdout))
161
+ return yield* gatewayGap(worker, `SSH worker "${worker}" can't reach its gateway at ${gitHost}`, new Error(probed.stderr.trim() ||
162
+ probed.stdout.split("\n")[0]?.trim() ||
163
+ `curl exited with code ${String(probed.exitCode)}`));
164
+ yield* Effect.logInfo("SSH Worker reaches its Gateway", {
165
+ worker,
166
+ host: machine.shell.destination,
167
+ domain: settings.domain,
168
+ gateway: forward.gateway,
169
+ });
170
+ });
171
+ /**
172
+ * Verifies the Gateway in front of an SSH Worker without attaching anything: the admin API
173
+ * checks of `verifyGatewayAdmin`, then the machine checks of `verifyGatewayFromMachine`.
174
+ */
175
+ export const verifyGateway = (commands, worker, machine, gateway) => verifyGatewayAdmin(worker, gateway).pipe(Effect.andThen(verifyGatewayFromMachine(commands, worker, machine, gateway)));
176
+ /**
177
+ * Checks only the binary of a harness whose credential the machine doesn't keep, the Gateway's
178
+ * or an API key in the Attempt's environment, named by `credential` in the log. Codex's version
179
+ * must also be supported.
180
+ */
181
+ export const harnessBinaryProbe = Effect.fn("SshExecutor.probeHarnessBinary")(function* (commands, host, shell, harness, credential) {
182
+ const label = harness === "claude" ? "Claude Code" : "Codex";
183
+ yield* Effect.annotateCurrentSpan({ host, harness, credential });
184
+ const result = yield* commands.inspect("preflight", shell, harness, ["--version"]);
185
+ if (result.exitCode === SSH_FAILURE)
186
+ return yield* unreachable(host, harness, result);
187
+ if (result.exitCode !== 0)
188
+ return new HarnessUnavailableError({
189
+ harness,
190
+ capability: "binary",
191
+ message: `${harness} --version failed. Check the ${label} installation on the worker.`,
192
+ });
193
+ const version = result.stdout.trim();
194
+ const unsupported = harness === "codex" ? unsupportedCodexVersion(version) : Option.none();
195
+ if (Option.isSome(unsupported))
196
+ return unsupported.value;
197
+ yield* Effect.logInfo(`${label} uses ${credential}; checked its binary only`, {
198
+ host,
199
+ harness,
200
+ version,
201
+ });
202
+ return new HarnessReady({ harness });
203
+ });
204
+ /**
205
+ * The harnesses that can use the operator's login on the machine or the Gateway's credential
206
+ * for their provider, with the hosts each login calls through the forward proxy, which
207
+ * `infra/hetzner/gateway/egress-allowlist` lists, and the API host that an API key in the
208
+ * Attempt's environment calls.
209
+ */
210
+ const LOGINS = {
211
+ claude: {
212
+ label: "Claude Code",
213
+ provider: "Anthropic",
214
+ gatewayProvider: "anthropic",
215
+ hosts: ["api.anthropic.com", "platform.claude.com"],
216
+ apiHost: "api.anthropic.com",
217
+ },
218
+ codex: {
219
+ label: "Codex",
220
+ provider: "OpenAI",
221
+ gatewayProvider: "openai",
222
+ hosts: ["chatgpt.com", "auth.openai.com"],
223
+ apiHost: "api.openai.com",
224
+ },
225
+ };
226
+ /**
227
+ * A script that takes the forward proxy URL and host names, and prints one line per host: the
228
+ * host, curl's exit code, the proxy's CONNECT status, and the host's HTTP status (`000` if the
229
+ * host didn't respond).
230
+ */
231
+ const LOGIN_HOSTS_SCRIPT = `
232
+ proxy=$1
233
+ shift
234
+ for host in "$@"; do
235
+ codes=$(curl -s -o /dev/null --max-time 10 --proxy "$proxy" -w '%{http_connect} %{http_code}' "https://$host/")
236
+ echo "$host $? $codes"
237
+ done
238
+ `.trim();
239
+ const LOGIN_HOST_ANSWER = /^(\S+) (\d+) (\d{3}) (\d{3})$/u;
240
+ /** The CONNECT status the forward proxy returns for a host that isn't in its allowlist. */
241
+ const PROXY_REFUSED = "403";
242
+ /**
243
+ * Probes a brain on a Worker behind a Gateway (ADR 0027). If the Gateway has the credential of
244
+ * the harness's provider, checks only the harness binary. Otherwise, Claude Code and Codex use
245
+ * the operator's login (ADR 0026), or an API key in the Attempt's environment if `apiKeyed`
246
+ * returns `true` for the harness: `probe` checks the login or the binary, then the machine must
247
+ * connect to the login's hosts, or the provider's API host, through the forward proxy. Pi goes
248
+ * to `probe`, which rejects it on any remote machine. The checks run through `shell`, on the
249
+ * machine or in a container on it.
250
+ */
251
+ export const gatewayHarnessProbe = (commands, machine, gateway, probe, shell, apiKeyed) => {
252
+ const worker = machine.address.name;
253
+ const host = machine.shell.destination;
254
+ const { domain } = gateway.settings;
255
+ const proxy = forwardProxyUrl(domain);
256
+ /** Checks only the binary of a harness whose provider credential the Gateway holds. */
257
+ const binaryOnly = (harness) => harnessBinaryProbe(commands, host, shell, harness, `the Gateway's ${LOGINS[harness].provider} credential`);
258
+ const loginHostsReachable = Effect.fn("SshExecutor.probeLoginHostsBehindGateway")(function* (harness) {
259
+ const { label } = LOGINS[harness];
260
+ const keyed = apiKeyed(harness);
261
+ const hosts = keyed ? [LOGINS[harness].apiHost] : LOGINS[harness].hosts;
262
+ const uses = keyed ? "an API key" : "its login";
263
+ const alternative = keyed
264
+ ? `, or add ${LOGINS[harness].gatewayProvider} to the worker's llm_providers with the key on the gateway`
265
+ : "";
266
+ yield* Effect.annotateCurrentSpan({ worker, host, domain, harness });
267
+ const result = yield* commands.inspect("preflight", shell, "sh", [
268
+ "-c",
269
+ LOGIN_HOSTS_SCRIPT,
270
+ "sh",
271
+ proxy,
272
+ ...hosts,
273
+ ]);
274
+ if (result.exitCode === SSH_FAILURE)
275
+ return yield* unreachable(host, "curl", result);
276
+ const answers = new Map(result.stdout.split("\n").flatMap((line) => {
277
+ const match = LOGIN_HOST_ANSWER.exec(line.trim());
278
+ return match === null
279
+ ? []
280
+ : [[match[1], { exitCode: match[2], connect: match[3], status: match[4] }]];
281
+ }));
282
+ for (const loginHost of hosts) {
283
+ const answer = answers.get(loginHost);
284
+ if (answer !== undefined && answer.status !== "000")
285
+ continue;
286
+ const cause = new Error(answer === undefined
287
+ ? `no response from ${loginHost}: ${result.stderr.trim() || `sh exited with code ${String(result.exitCode)}`}`
288
+ : `curl exited with code ${answer.exitCode}; the proxy returned ${answer.connect} for CONNECT`);
289
+ return yield* gatewayGap(worker, answer?.connect === PROXY_REFUSED
290
+ ? `${label} uses ${uses} on SSH worker "${worker}", and the gateway's forward proxy blocks ${loginHost}. Add ${loginHost} to the gateway's egress allowlist${alternative}.`
291
+ : `${label} uses ${uses} on SSH worker "${worker}", which can't reach ${loginHost} through the gateway's forward proxy at ${proxy}`, cause);
292
+ }
293
+ yield* Effect.logInfo("SSH Worker reaches its harness login's hosts through the Gateway", {
294
+ worker,
295
+ harness,
296
+ domain,
297
+ hosts: hosts.join(","),
298
+ });
299
+ });
300
+ const loginOnMachine = Effect.fn("SshExecutor.probeLoginBehindGateway")(function* (brain, harness) {
301
+ yield* Effect.annotateCurrentSpan({ worker, host, domain, harness });
302
+ const readiness = yield* probe(brain);
303
+ if (readiness._tag === "HarnessUnavailableError")
304
+ return readiness;
305
+ yield* loginHostsReachable(harness);
306
+ return readiness;
307
+ });
308
+ return (brain) => {
309
+ switch (brain.harness) {
310
+ case "claude":
311
+ case "codex":
312
+ return gateway.settings.llmProviders.includes(LOGINS[brain.harness].gatewayProvider)
313
+ ? binaryOnly(brain.harness)
314
+ : loginOnMachine(brain, brain.harness);
315
+ case "pi":
316
+ return probe(brain);
317
+ }
318
+ };
319
+ };
320
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../../src/ssh/gateway/preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { type ContainerLimit, ContainerLimits } from "@usine/core/worker";
2
+ import { Option, Schema } from "effect";
3
+ import type { SshWorkerContainer } from "./model.ts";
4
+ declare const MachineResources_base: Schema.Class<MachineResources, Schema.Struct<{
5
+ /** The CPUs available to the SSH user, as `nproc` counts them. */
6
+ readonly cpus: Schema.Int;
7
+ readonly memoryBytes: Schema.Int;
8
+ /** The most processes and threads the kernel runs at once: the lower of `threads-max` and `pid_max`. */
9
+ readonly tasks: Schema.Int;
10
+ }>, {}>;
11
+ /** What a Linux machine's kernel reports of its CPUs, memory, and tasks. */
12
+ export declare class MachineResources extends MachineResources_base {
13
+ }
14
+ /**
15
+ * A script that prints the number of CPUs, the kernel's `threads-max` and `pid_max`, and
16
+ * `/proc/meminfo`.
17
+ */
18
+ export declare const RESOURCES_SCRIPT = "nproc && cat /proc/sys/kernel/threads-max /proc/sys/kernel/pid_max /proc/meminfo";
19
+ /** Parses the output of `RESOURCES_SCRIPT`. Returns `None` if a value is missing or not a positive integer. */
20
+ export declare const resourcesFromOutput: (output: string) => Option.Option<MachineResources>;
21
+ /** The limits of a Worker's containers and where they come from. */
22
+ export type ResolvedLimits =
23
+ /** `usine.yaml` sets every limit. */
24
+ {
25
+ readonly _tag: "Configured";
26
+ readonly limits: ContainerLimits;
27
+ }
28
+ /** `derived` come from `machine` for `slots` concurrent Attempts; `usine.yaml` sets the others. */
29
+ | {
30
+ readonly _tag: "Derived";
31
+ readonly limits: ContainerLimits;
32
+ readonly derived: ReadonlyArray<ContainerLimit>;
33
+ readonly machine: MachineResources;
34
+ readonly slots: number;
35
+ };
36
+ /** Returns the limits of `container` if `usine.yaml` sets every one, or `None`. */
37
+ export declare const configuredLimits: (container: SshWorkerContainer) => Option.Option<ResolvedLimits>;
38
+ /**
39
+ * Returns the limits of `container` when `slots` Attempts run on `machine` at once: the ones
40
+ * `usine.yaml` sets, and the others from `deriveLimits`.
41
+ */
42
+ export declare const resolveLimits: (container: SshWorkerContainer, machine: MachineResources, slots: number) => ResolvedLimits;
43
+ /** Returns the log fields of the limits of a Worker's containers. */
44
+ export declare const limitFields: (resolved: ResolvedLimits) => {
45
+ cpus: number & import("effect/Brand").Brand<"ContainerCpus">;
46
+ memoryMib: number & import("effect/Brand").Brand<"ContainerMemoryMib">;
47
+ pids: number & import("effect/Brand").Brand<"ContainerPids">;
48
+ derivedLimits: string;
49
+ } | {
50
+ cpus: number & import("effect/Brand").Brand<"ContainerCpus">;
51
+ memoryMib: number & import("effect/Brand").Brand<"ContainerMemoryMib">;
52
+ pids: number & import("effect/Brand").Brand<"ContainerPids">;
53
+ derivedLimits: string;
54
+ slots: number;
55
+ machineCpus: number;
56
+ machineMemoryMib: number;
57
+ machineTasks: number;
58
+ };
59
+ export {};
60
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/ssh/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,eAAe,EAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAS,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;;IAQnD,kEAAkE;;;IAGlE,wGAAwG;;;AAP1G,4EAA4E;AAC5E,qBAAa,gBAAiB,SAAQ,qBAQpC;CAAG;AAEL;;;GAGG;AACH,eAAO,MAAM,gBAAgB,qFACuD,CAAC;AAIrF,+GAA+G;AAC/G,eAAO,MAAM,mBAAmB,WAAY,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CASlF,CAAC;AA6BF,oEAAoE;AACpE,MAAM,MAAM,cAAc;AACxB,qCAAqC;AACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;CAAE;AACnE,mGAAmG;GACjG;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,cAAe,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAI1F,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,aAAa,cACb,kBAAkB,WACpB,gBAAgB,SAClB,MAAM,KACZ,cAkBF,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,WAAW,aAAc,cAAc;;;;;;;;;;;;;;CAclD,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { CONTAINER_LIMITS, ContainerCpus, ContainerLimits, ContainerMemoryMib, ContainerPids, } from "@usine/core/worker";
2
+ import { Match, Option, Schema } from "effect";
3
+ import { memoryFromMeminfo } from "../memory-preflight.js";
4
+ const MIB = 1024 ** 2;
5
+ /** What a Linux machine's kernel reports of its CPUs, memory, and tasks. */
6
+ export class MachineResources extends Schema.Class("@usine/executor/MachineResources")({
7
+ /** The CPUs available to the SSH user, as `nproc` counts them. */
8
+ cpus: Schema.Int.check(Schema.isGreaterThan(0)),
9
+ memoryBytes: Schema.Int.check(Schema.isGreaterThan(0)),
10
+ /** The most processes and threads the kernel runs at once: the lower of `threads-max` and `pid_max`. */
11
+ tasks: Schema.Int.check(Schema.isGreaterThan(0)),
12
+ }) {
13
+ }
14
+ /**
15
+ * A script that prints the number of CPUs, the kernel's `threads-max` and `pid_max`, and
16
+ * `/proc/meminfo`.
17
+ */
18
+ export const RESOURCES_SCRIPT = "nproc && cat /proc/sys/kernel/threads-max /proc/sys/kernel/pid_max /proc/meminfo";
19
+ const decodeResources = Schema.decodeUnknownOption(MachineResources);
20
+ /** Parses the output of `RESOURCES_SCRIPT`. Returns `None` if a value is missing or not a positive integer. */
21
+ export const resourcesFromOutput = (output) => {
22
+ const [cpus, threadsMax, pidMax, ...meminfo] = output.split("\n");
23
+ return Option.flatMap(memoryFromMeminfo(meminfo.join("\n")), ({ totalBytes }) => decodeResources({
24
+ cpus: Number(cpus),
25
+ memoryBytes: totalBytes,
26
+ tasks: Math.min(Number(threadsMax), Number(pidMax)),
27
+ }));
28
+ };
29
+ /** What the Attempt containers leave to the machine's own processes, such as sshd, Docker, and the cached clones' fetches. */
30
+ const HOST_RESERVE = { cpus: 0.5, memoryMib: 1024, tasks: 1024 };
31
+ /** The smallest limits that `usine.yaml` and Docker accept. */
32
+ const SMALLEST = { cpus: 0.01, memoryMib: 256, pids: 64 };
33
+ /**
34
+ * Returns the limits of each container when `slots` Attempts run on `machine` at once: the
35
+ * machine's total less a reserve for its own processes, divided by `slots`, rounded down to a
36
+ * hundredth of a CPU, a MiB, and a task, and no lower than the smallest limits Docker accepts.
37
+ */
38
+ const deriveLimits = (machine, slots) => new ContainerLimits({
39
+ cpus: ContainerCpus.make(Math.max(SMALLEST.cpus, Math.floor(((machine.cpus - HOST_RESERVE.cpus) / slots) * 100) / 100)),
40
+ memoryMib: ContainerMemoryMib.make(Math.max(SMALLEST.memoryMib, Math.floor((machine.memoryBytes / MIB - HOST_RESERVE.memoryMib) / slots))),
41
+ pids: ContainerPids.make(Math.max(SMALLEST.pids, Math.floor((machine.tasks - HOST_RESERVE.tasks) / slots))),
42
+ });
43
+ /** Returns the limits of `container` if `usine.yaml` sets every one, or `None`. */
44
+ export const configuredLimits = (container) => Option.map(Option.all({ cpus: container.cpus, memoryMib: container.memoryMib, pids: container.pids }), (limits) => ({ _tag: "Configured", limits: new ContainerLimits(limits) }));
45
+ /**
46
+ * Returns the limits of `container` when `slots` Attempts run on `machine` at once: the ones
47
+ * `usine.yaml` sets, and the others from `deriveLimits`.
48
+ */
49
+ export const resolveLimits = (container, machine, slots) => {
50
+ const derived = deriveLimits(machine, slots);
51
+ const unset = {
52
+ cpus: Option.isNone(container.cpus),
53
+ memoryMib: Option.isNone(container.memoryMib),
54
+ pids: Option.isNone(container.pids),
55
+ };
56
+ return {
57
+ _tag: "Derived",
58
+ limits: new ContainerLimits({
59
+ cpus: Option.getOrElse(container.cpus, () => derived.cpus),
60
+ memoryMib: Option.getOrElse(container.memoryMib, () => derived.memoryMib),
61
+ pids: Option.getOrElse(container.pids, () => derived.pids),
62
+ }),
63
+ derived: CONTAINER_LIMITS.filter((limit) => unset[limit]),
64
+ machine,
65
+ slots,
66
+ };
67
+ };
68
+ /** Returns the log fields of the limits of a Worker's containers. */
69
+ export const limitFields = (resolved) => ({
70
+ cpus: resolved.limits.cpus,
71
+ memoryMib: resolved.limits.memoryMib,
72
+ pids: resolved.limits.pids,
73
+ ...Match.valueTags(resolved, {
74
+ Configured: () => ({ derivedLimits: "none" }),
75
+ Derived: ({ derived, machine, slots }) => ({
76
+ derivedLimits: derived.join(","),
77
+ slots,
78
+ machineCpus: machine.cpus,
79
+ machineMemoryMib: Math.floor(machine.memoryBytes / MIB),
80
+ machineTasks: machine.tasks,
81
+ }),
82
+ }),
83
+ });
84
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/ssh/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,aAAa,EAEb,eAAe,EACf,kBAAkB,EAClB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC;AAEtB,4EAA4E;AAC5E,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,KAAK,CAChD,kCAAkC,CACnC,CAAC;IACA,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC/C,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACtD,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CACjD,CAAC;CAAG;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,kFAAkF,CAAC;AAErF,MAAM,eAAe,GAAG,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;AAErE,+GAA+G;AAC/G,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAc,EAAmC,EAAE;IACrF,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAC9E,eAAe,CAAC;QACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,WAAW,EAAE,UAAU;QACvB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;KACpD,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,8HAA8H;AAC9H,MAAM,YAAY,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAW,CAAC;AAE1E,+DAA+D;AAC/D,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAW,CAAC;AAEnE;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,OAAyB,EAAE,KAAa,EAAmB,EAAE,CACjF,IAAI,eAAe,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC,IAAI,CACtB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAC9F;IACD,SAAS,EAAE,kBAAkB,CAAC,IAAI,CAChC,IAAI,CAAC,GAAG,CACN,QAAQ,CAAC,SAAS,EAClB,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,GAAG,GAAG,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,CACzE,CACF;IACD,IAAI,EAAE,aAAa,CAAC,IAAI,CACtB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAClF;CACF,CAAC,CAAC;AAeL,mFAAmF;AACnF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAA6B,EAAiC,EAAE,CAC/F,MAAM,CAAC,GAAG,CACR,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,EAC1F,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAC1E,CAAC;AAEJ;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,SAA6B,EAC7B,OAAyB,EACzB,KAAa,EACG,EAAE;IAClB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG;QACZ,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;QAC7C,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;KACM,CAAC;IAC5C,OAAO;QACL,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,IAAI,eAAe,CAAC;YAC1B,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;YAC1D,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;YACzE,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;SAC3D,CAAC;QACF,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzD,OAAO;QACP,KAAK;KACN,CAAC;AACJ,CAAC,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAwB,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI;IAC1B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS;IACpC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI;IAC1B,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE;QAC3B,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACzC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAChC,KAAK;YACL,WAAW,EAAE,OAAO,CAAC,IAAI;YACzB,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC;YACvD,YAAY,EAAE,OAAO,CAAC,KAAK;SAC5B,CAAC;KACH,CAAC;CACH,CAAC,CAAC"}