@crvouga/dev-fleet 0.0.0-stage → 0.2.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 (363) hide show
  1. package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
  2. package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
  3. package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
  4. package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
  5. package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
  6. package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
  7. package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
  8. package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
  9. package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
  10. package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
  11. package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
  12. package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
  13. package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
  14. package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
  15. package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
  16. package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
  17. package/LICENSE +21 -0
  18. package/README.md +357 -2
  19. package/agents/antigravity-proxy.ts +3 -0
  20. package/agents/audit.md +89 -0
  21. package/agents/claude-proxy +5 -0
  22. package/agents/claude-proxy.ts +3 -0
  23. package/agents/codex-proxy.ts +3 -0
  24. package/agents/grok-proxy.ts +3 -0
  25. package/agents/managed-proxy.ts +55 -0
  26. package/agents/opencode-proxy.ts +3 -0
  27. package/agents/registry.json +209 -0
  28. package/agents/registry.schema.json +149 -0
  29. package/agents/setup.md +158 -0
  30. package/agents/t3.json +40 -0
  31. package/ansible/macos/manager.yml +30 -0
  32. package/ansible/macos/policy.json +9 -0
  33. package/ansible/monitoring/README.md +215 -0
  34. package/ansible/monitoring/agent-darwin.yml +198 -0
  35. package/ansible/monitoring/agent.yml +172 -0
  36. package/ansible/monitoring/beszel-agent.service +42 -0
  37. package/ansible/monitoring/compose.yml +74 -0
  38. package/ansible/monitoring/hub-darwin.yml +102 -0
  39. package/ansible/monitoring/hub-route-darwin.yml +51 -0
  40. package/ansible/monitoring/lock.json +16 -0
  41. package/ansible/net/remote-probe.sh +13 -0
  42. package/ansible/pyproject.toml +12 -0
  43. package/ansible/ubuntu/authorize-sudo.sh +51 -0
  44. package/ansible/ubuntu/baseline.yml +89 -0
  45. package/ansible/ubuntu/disk-gc.py +336 -0
  46. package/ansible/ubuntu/disk-guard.py +104 -0
  47. package/ansible/ubuntu/policy.json +52 -0
  48. package/ansible/ubuntu/recovery.yml +15 -0
  49. package/ansible/ubuntu/t3/policy.json +7 -0
  50. package/ansible/ubuntu/t3-desktop.yml +220 -0
  51. package/ansible/ubuntu/t3-health.sh +33 -0
  52. package/ansible/ubuntu/t3-policy.yml +76 -0
  53. package/ansible/ubuntu/t3-route.yml +28 -0
  54. package/ansible/ubuntu/tailscale.yml +78 -0
  55. package/ansible/ubuntu/tasks/credentials.yml +47 -0
  56. package/ansible/ubuntu/tasks/docker.yml +31 -0
  57. package/ansible/ubuntu/tasks/packages.yml +56 -0
  58. package/ansible/ubuntu/tasks/power.yml +51 -0
  59. package/ansible/ubuntu/tasks/recovery.yml +39 -0
  60. package/ansible/ubuntu/tasks/resources.yml +312 -0
  61. package/ansible/ubuntu/tasks/services.yml +31 -0
  62. package/ansible/ubuntu/tasks/ssh.yml +71 -0
  63. package/ansible/ubuntu/tasks/user.yml +120 -0
  64. package/ansible/uv.lock +190 -0
  65. package/bin/devfleet.mjs +36 -0
  66. package/bootstrap/bootstrap.ps1 +50 -0
  67. package/bootstrap/bootstrap.sh +80 -0
  68. package/bootstrap/devfleet +7 -0
  69. package/bootstrap/docker-desktop.sh +33 -0
  70. package/bootstrap/doctor.ts +234 -0
  71. package/bootstrap/fleet-manager-run.sh +103 -0
  72. package/bootstrap/fleet-remote-bootstrap.sh +258 -0
  73. package/bootstrap/global-config.ts +171 -0
  74. package/bootstrap/install-launcher.sh +10 -0
  75. package/bootstrap/native-aws.ts +86 -0
  76. package/bootstrap/native-t3.ts +154 -0
  77. package/bootstrap/npm.ts +358 -0
  78. package/bootstrap/release-version.ts +157 -0
  79. package/bootstrap/setup-status.ts +51 -0
  80. package/bootstrap/setup.ts +88 -0
  81. package/bootstrap/shell-path.ts +105 -0
  82. package/bootstrap/verify-t3-archive.py +49 -0
  83. package/bootstrap/worker-auth.ts +65 -0
  84. package/bootstrap/worker-github.ts +47 -0
  85. package/bootstrap/worker-tools.ts +24 -0
  86. package/cliproxy/AGENTS.md +11 -0
  87. package/cliproxy/README.md +269 -0
  88. package/cliproxy/clients/bootstrap.py +143 -0
  89. package/cliproxy/clients/launcher.py +57 -0
  90. package/cliproxy/compose.yaml +90 -0
  91. package/cliproxy/config.template.yaml +70 -0
  92. package/cliproxy/docs/cloud-adoption.md +16 -0
  93. package/cliproxy/docs/deployment.png +0 -0
  94. package/cliproxy/docs/deployment.svg +109 -0
  95. package/cliproxy/docs/health-workflow.example.yml +185 -0
  96. package/cliproxy/docs/migration.md +131 -0
  97. package/cliproxy/docs/model-health-report.schema.json +364 -0
  98. package/cliproxy/docs/model-health.md +393 -0
  99. package/cliproxy/image.lock.json +5 -0
  100. package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
  101. package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
  102. package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
  103. package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
  104. package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
  105. package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
  106. package/cliproxy/models.json +1618 -0
  107. package/cliproxy/requirements.lock +78 -0
  108. package/cliproxy/scripts/accounts.py +38 -0
  109. package/cliproxy/scripts/audit.py +123 -0
  110. package/cliproxy/scripts/backup +234 -0
  111. package/cliproxy/scripts/check.py +34 -0
  112. package/cliproxy/scripts/checkpoint.py +226 -0
  113. package/cliproxy/scripts/cli.py +64 -0
  114. package/cliproxy/scripts/cliproxy_host.py +257 -0
  115. package/cliproxy/scripts/cloud_state.py +150 -0
  116. package/cliproxy/scripts/container-health +6 -0
  117. package/cliproxy/scripts/docker +24 -0
  118. package/cliproxy/scripts/external-health.py +79 -0
  119. package/cliproxy/scripts/fleet_config.py +43 -0
  120. package/cliproxy/scripts/fleet_sync.py +274 -0
  121. package/cliproxy/scripts/initialize +84 -0
  122. package/cliproxy/scripts/install-dependencies +48 -0
  123. package/cliproxy/scripts/install.py +406 -0
  124. package/cliproxy/scripts/model_health.py +16 -0
  125. package/cliproxy/scripts/modelhealth/__init__.py +5 -0
  126. package/cliproxy/scripts/modelhealth/checks.py +429 -0
  127. package/cliproxy/scripts/modelhealth/cli.py +117 -0
  128. package/cliproxy/scripts/modelhealth/config.py +186 -0
  129. package/cliproxy/scripts/modelhealth/emulator.py +536 -0
  130. package/cliproxy/scripts/modelhealth/expect.py +88 -0
  131. package/cliproxy/scripts/modelhealth/findings.py +183 -0
  132. package/cliproxy/scripts/modelhealth/protocol.py +330 -0
  133. package/cliproxy/scripts/modelhealth/redact.py +28 -0
  134. package/cliproxy/scripts/modelhealth/report.py +150 -0
  135. package/cliproxy/scripts/modelhealth/runner.py +390 -0
  136. package/cliproxy/scripts/modelhealth/scenario.py +131 -0
  137. package/cliproxy/scripts/modelhealth/schema.py +63 -0
  138. package/cliproxy/scripts/modelhealth/simulate.py +111 -0
  139. package/cliproxy/scripts/modelhealth/transport.py +213 -0
  140. package/cliproxy/scripts/monitor +68 -0
  141. package/cliproxy/scripts/ops +527 -0
  142. package/cliproxy/scripts/public-endpoint +127 -0
  143. package/cliproxy/scripts/recover_node.py +177 -0
  144. package/cliproxy/scripts/restore-verify +60 -0
  145. package/cliproxy/scripts/retention.py +144 -0
  146. package/cliproxy/scripts/service-start +7 -0
  147. package/cliproxy/scripts/validate-config +56 -0
  148. package/cliproxy/scripts/vault-access.py +57 -0
  149. package/cliproxy/scripts/vault-backup-login +55 -0
  150. package/cliproxy/scripts/vault-recovery +56 -0
  151. package/cliproxy/scripts/vault-renew +30 -0
  152. package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
  153. package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
  154. package/cliproxy/systemd/cliproxy-backup.service +13 -0
  155. package/cliproxy/systemd/cliproxy-backup.timer +8 -0
  156. package/cliproxy/systemd/cliproxy-fleet.service +17 -0
  157. package/cliproxy/systemd/cliproxy-gc.service +13 -0
  158. package/cliproxy/systemd/cliproxy-gc.timer +8 -0
  159. package/cliproxy/systemd/cliproxy-monitor.service +8 -0
  160. package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
  161. package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
  162. package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
  163. package/cliproxy/vendor/LICENSE +22 -0
  164. package/cliproxy/vendor/config.upstream.yaml +1349 -0
  165. package/docs/consumer-setup.md +408 -0
  166. package/docs/declared.md +89 -0
  167. package/docs/machine-setup.md +399 -0
  168. package/docs/operations.md +276 -0
  169. package/docs/releasing.md +103 -0
  170. package/docs/state.md +254 -0
  171. package/docs/topology.md +67 -0
  172. package/docs/ubuntu-sync.md +351 -0
  173. package/docs/worker-contract.md +67 -0
  174. package/examples/fleet.json +100 -0
  175. package/examples/roster.json +13 -0
  176. package/llms.txt +35 -0
  177. package/mise.lock +1521 -0
  178. package/mise.toml +110 -0
  179. package/package.json +74 -4
  180. package/schema/fleet.schema.json +420 -0
  181. package/schema/roster.schema.json +77 -0
  182. package/src/agents/account-relay.ts +111 -0
  183. package/src/agents/accounts.ts +85 -0
  184. package/src/agents/audit.ts +77 -0
  185. package/src/agents/cli.ts +13 -0
  186. package/src/agents/command.ts +204 -0
  187. package/src/agents/converge.ts +123 -0
  188. package/src/agents/doctor.ts +107 -0
  189. package/src/agents/errors.ts +2 -0
  190. package/src/agents/files.ts +73 -0
  191. package/src/agents/fleet-verify.ts +165 -0
  192. package/src/agents/launch.ts +290 -0
  193. package/src/agents/native-verify.ts +371 -0
  194. package/src/agents/provider-errors.ts +57 -0
  195. package/src/agents/registry.ts +61 -0
  196. package/src/agents/render.ts +110 -0
  197. package/src/agents/round-trip.ts +192 -0
  198. package/src/agents/secrets.ts +13 -0
  199. package/src/agents/toolchain.ts +34 -0
  200. package/src/agents/verify-cache.ts +103 -0
  201. package/src/agents/verify.ts +127 -0
  202. package/src/auth-checks.ts +163 -0
  203. package/src/cli/agent-guide.ts +129 -0
  204. package/src/cli/app.ts +122 -0
  205. package/src/cli/cli.ts +28 -0
  206. package/src/cli/command-index.ts +57 -0
  207. package/src/cli/contracts.ts +25 -0
  208. package/src/cli/fleet-reference.ts +17 -0
  209. package/src/cli/fleet-state.ts +499 -0
  210. package/src/cli/fleet-sync-checkout.ts +130 -0
  211. package/src/cli/fleet-sync-enrollment.ts +140 -0
  212. package/src/cli/fleet-sync-forward.ts +134 -0
  213. package/src/cli/fleet-sync-manager.ts +249 -0
  214. package/src/cli/fleet-sync-monitoring.ts +185 -0
  215. package/src/cli/fleet-sync-output.ts +317 -0
  216. package/src/cli/fleet-sync-privilege.ts +27 -0
  217. package/src/cli/fleet-sync-progress.ts +109 -0
  218. package/src/cli/fleet-sync-remote.ts +242 -0
  219. package/src/cli/fleet-sync-report.ts +26 -0
  220. package/src/cli/fleet-sync-schedule.ts +138 -0
  221. package/src/cli/fleet-sync-state.ts +86 -0
  222. package/src/cli/fleet-sync-vault.ts +166 -0
  223. package/src/cli/fleet-sync.ts +375 -0
  224. package/src/cli/fleet.ts +165 -0
  225. package/src/cli/managed-health.ts +219 -0
  226. package/src/cli/node-deployment.ts +34 -0
  227. package/src/cli/node-sync.ts +121 -0
  228. package/src/cli/node-t3.ts +31 -0
  229. package/src/cli/options.ts +141 -0
  230. package/src/cli/probe-source.ts +7 -0
  231. package/src/cli/program.ts +185 -0
  232. package/src/cli/project.ts +51 -0
  233. package/src/cli/register-fleet.ts +296 -0
  234. package/src/cli/registry.ts +185 -0
  235. package/src/cli/repair-apply.ts +67 -0
  236. package/src/cli/repair-guard.ts +11 -0
  237. package/src/cli/repair-inspect.ts +150 -0
  238. package/src/cli/repair-script.ts +76 -0
  239. package/src/cli/repair.ts +209 -0
  240. package/src/cli/roster.ts +105 -0
  241. package/src/cli/search.ts +35 -0
  242. package/src/cli/settings.ts +67 -0
  243. package/src/cli/source-operations.ts +102 -0
  244. package/src/cli/t3/command.ts +90 -0
  245. package/src/cli/t3/register.ts +55 -0
  246. package/src/cli/tailnet.ts +127 -0
  247. package/src/cli/ui-actions.ts +133 -0
  248. package/src/cli/ui-controller.ts +113 -0
  249. package/src/cli/ui-data.ts +106 -0
  250. package/src/cli/ui-execute.ts +163 -0
  251. package/src/cli/ui-repairs.ts +34 -0
  252. package/src/cli/ui-reports.ts +211 -0
  253. package/src/cli/ui-targets.ts +100 -0
  254. package/src/cli/ui-terminal.ts +109 -0
  255. package/src/cli/ui-types.ts +49 -0
  256. package/src/cli/ui-wizard.ts +101 -0
  257. package/src/cli/ui.ts +9 -0
  258. package/src/cli/worker-health-output.ts +37 -0
  259. package/src/config.ts +186 -0
  260. package/src/configure.ts +255 -0
  261. package/src/definition.ts +290 -0
  262. package/src/diagnostics.ts +92 -0
  263. package/src/doctor-check.ts +10 -0
  264. package/src/errors.ts +1 -0
  265. package/src/fingerprint.ts +120 -0
  266. package/src/fleet-config.ts +174 -0
  267. package/src/input-hashes.ts +73 -0
  268. package/src/machine/local-source.ts +52 -0
  269. package/src/machine/lock-error.ts +2 -0
  270. package/src/machine/platform.ts +44 -0
  271. package/src/machine/source.ts +68 -0
  272. package/src/machine/state.ts +173 -0
  273. package/src/machine-health.ts +95 -0
  274. package/src/managed-source.ts +47 -0
  275. package/src/manager-failure.ts +48 -0
  276. package/src/manager-sync.ts +405 -0
  277. package/src/manager.ts +62 -0
  278. package/src/monitoring/agent-evidence.ts +146 -0
  279. package/src/monitoring/apply.ts +183 -0
  280. package/src/monitoring/beszel.ts +198 -0
  281. package/src/monitoring/enroll.ts +111 -0
  282. package/src/monitoring/hub-access.ts +114 -0
  283. package/src/monitoring/hub-backup.ts +121 -0
  284. package/src/monitoring/hub-viewer.ts +142 -0
  285. package/src/monitoring/hub.ts +93 -0
  286. package/src/monitoring/manager-agent.ts +73 -0
  287. package/src/monitoring/manager-hub.ts +248 -0
  288. package/src/monitoring/remote.ts +65 -0
  289. package/src/network-definition.ts +83 -0
  290. package/src/node-converge-linux.ts +374 -0
  291. package/src/node-converge-shared.ts +83 -0
  292. package/src/node-converge.ts +40 -0
  293. package/src/onboard.ts +35 -0
  294. package/src/package-info.ts +45 -0
  295. package/src/platform/ansible-receipt.ts +128 -0
  296. package/src/platform/ansible.ts +66 -0
  297. package/src/platform/darwin.ts +203 -0
  298. package/src/platform/detect.ts +11 -0
  299. package/src/platform/fallback.ts +110 -0
  300. package/src/platform/index.ts +38 -0
  301. package/src/platform/launchd.ts +98 -0
  302. package/src/platform/linux.ts +139 -0
  303. package/src/platform/ps.ts +45 -0
  304. package/src/platform/resource-verify.ts +132 -0
  305. package/src/platform/systemd-user.ts +95 -0
  306. package/src/platform/t3-policy.ts +137 -0
  307. package/src/platform/t3-route.ts +68 -0
  308. package/src/platform/t3-service.ts +75 -0
  309. package/src/platform/types.ts +123 -0
  310. package/src/platform/ubuntu-audit.ts +66 -0
  311. package/src/platform/ubuntu-verify.ts +105 -0
  312. package/src/platform/windows.ts +148 -0
  313. package/src/power-recovery.ts +55 -0
  314. package/src/probe.ts +51 -0
  315. package/src/protocol.ts +119 -0
  316. package/src/proxy-converge.ts +85 -0
  317. package/src/remote-bootstrap.ts +210 -0
  318. package/src/remote-entry.ts +89 -0
  319. package/src/remote-lock.ts +12 -0
  320. package/src/repair-catalog.ts +206 -0
  321. package/src/repair-plan.ts +78 -0
  322. package/src/repair-runner.ts +84 -0
  323. package/src/report.ts +71 -0
  324. package/src/runner.ts +104 -0
  325. package/src/secrets.ts +169 -0
  326. package/src/service-auth.ts +115 -0
  327. package/src/ssh.ts +135 -0
  328. package/src/state/bucket.ts +208 -0
  329. package/src/state/check.ts +74 -0
  330. package/src/state/connection.ts +92 -0
  331. package/src/state/enrollment.ts +45 -0
  332. package/src/state/legacy.ts +89 -0
  333. package/src/state/litestream.ts +143 -0
  334. package/src/state/operator.ts +20 -0
  335. package/src/state/paths.ts +40 -0
  336. package/src/state/roster.ts +62 -0
  337. package/src/state/store.ts +169 -0
  338. package/src/state/sync.ts +352 -0
  339. package/src/sync.ts +104 -0
  340. package/src/t3/accounts.ts +81 -0
  341. package/src/t3/desired.ts +165 -0
  342. package/src/t3/files.ts +81 -0
  343. package/src/t3/names-check.ts +53 -0
  344. package/src/t3/names.ts +155 -0
  345. package/src/t3/plan.ts +82 -0
  346. package/src/t3/providers.ts +74 -0
  347. package/src/t3/reconcile.ts +123 -0
  348. package/src/t3-endpoint.ts +104 -0
  349. package/src/t3-linked-check.ts +123 -0
  350. package/src/tailnet-health.ts +231 -0
  351. package/src/tailnet-matching.ts +37 -0
  352. package/src/tailscale.ts +141 -0
  353. package/src/toolchain-checks.ts +108 -0
  354. package/src/topology.ts +24 -0
  355. package/src/transport.ts +122 -0
  356. package/src/types.ts +99 -0
  357. package/src/vault-config.ts +38 -0
  358. package/src/worker-access-checks.ts +168 -0
  359. package/src/worker-capacity.ts +34 -0
  360. package/src/worker-host-checks.ts +181 -0
  361. package/src/worker-policy.ts +64 -0
  362. package/src/worker-readiness.ts +64 -0
  363. package/src/workstation-checks.ts +29 -0
@@ -0,0 +1,171 @@
1
+ /** Publish one managed global mise config without clobbering unrelated configuration. */
2
+ import {
3
+ cpSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ readdirSync,
8
+ statSync,
9
+ writeFileSync,
10
+ } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+
14
+ const MARKER = '# Managed by dev-fleet bootstrap/setup.ts\n';
15
+ /**
16
+ * Where agent harnesses create a fresh checkout per task. Every new path would
17
+ * need its own `mise trust`, and an untrusted mise.toml makes every mise shim
18
+ * (claude, codex, bun, ...) exit before the agent starts. mise honours this
19
+ * setting only in the global config, so it is added at publish time and never
20
+ * written into the repository's mise.toml.
21
+ */
22
+ export const AGENT_WORKTREE_ROOTS = ['~/.t3/worktrees'];
23
+ const MANAGED = /^# Managed by \S+ \S*setup\.ts\n/;
24
+ export const workspaceRoot = join(import.meta.dirname, '..');
25
+
26
+ export function globalConfigDir(): string {
27
+ if (process.env['MISE_GLOBAL_CONFIG_FILE']) {
28
+ throw new Error(
29
+ 'Unset MISE_GLOBAL_CONFIG_FILE before applying the shared toolchain.'
30
+ );
31
+ }
32
+ return (
33
+ process.env['MISE_CONFIG_DIR'] ??
34
+ join(process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config'), 'mise')
35
+ );
36
+ }
37
+
38
+ export function assertManagedConfig(dir: string): void {
39
+ const config = join(dir, 'config.toml');
40
+ const lock = join(dir, 'mise.lock');
41
+ // Any earlier owner name counts: the marker text identifies the file as
42
+ // managed, never the repository or path it was published from.
43
+ const managed =
44
+ existsSync(config) && MANAGED.test(readFileSync(config, 'utf8'));
45
+ if ((existsSync(config) && !managed) || (existsSync(lock) && !managed)) {
46
+ throw new Error(
47
+ `Existing mise configuration at ${dir}. Back up and move config.toml and mise.lock, then rerun. Nothing was overwritten.`
48
+ );
49
+ }
50
+ }
51
+
52
+ /** What `publishGlobalConfig` writes to config.toml, given what is there. */
53
+ function publishedConfig(root: string, previous: string | undefined): string {
54
+ return (
55
+ MARKER +
56
+ withTrustedPaths(
57
+ readFileSync(join(root, 'mise.toml'), 'utf8'),
58
+ trustedPaths(previous)
59
+ )
60
+ );
61
+ }
62
+
63
+ export function publishGlobalConfig(dir: string, root = workspaceRoot): void {
64
+ assertManagedConfig(dir);
65
+ mkdirSync(dir, { recursive: true });
66
+ const path = join(dir, 'config.toml');
67
+ const previous = existsSync(path) ? readFileSync(path, 'utf8') : undefined;
68
+ const config = publishedConfig(root, previous);
69
+ if (previous !== config) writeFileSync(path, config);
70
+ cpSync(join(root, 'mise.lock'), join(dir, 'mise.lock'), {
71
+ filter: changedFile,
72
+ });
73
+ const sidecars = join(root, '.mise/locks');
74
+ if (existsSync(sidecars))
75
+ cpSync(sidecars, join(dir, '.mise/locks'), {
76
+ recursive: true,
77
+ filter: changedFile,
78
+ });
79
+ }
80
+
81
+ /**
82
+ * How the global mise configuration stands against what this revision
83
+ * publishes. On a fleet machine it belongs to dev-fleet: a project pins its
84
+ * tools in its own mise.toml and never publishes a global one.
85
+ *
86
+ * - `published`: dev-fleet's marker, this revision's pins and lock
87
+ * - `missing`: there is no global config.toml
88
+ * - `replaced`: another owner (or nobody's marker) wrote it
89
+ * - `changed`: dev-fleet's marker, but not this revision's content
90
+ */
91
+ export type GlobalConfigStanding =
92
+ 'published' | 'missing' | 'replaced' | 'changed';
93
+
94
+ export function globalConfigStanding(
95
+ dir: string,
96
+ root = workspaceRoot
97
+ ): GlobalConfigStanding {
98
+ const path = join(dir, 'config.toml');
99
+ if (!existsSync(path)) return 'missing';
100
+ const current = readFileSync(path, 'utf8');
101
+ if (!current.startsWith(MARKER)) return 'replaced';
102
+ const same = (source: string, target: string): boolean =>
103
+ existsSync(target) && readFileSync(source).equals(readFileSync(target));
104
+ const sidecars = join(root, '.mise/locks');
105
+ const locks = existsSync(sidecars)
106
+ ? (readdirSync(sidecars, { recursive: true }) as string[])
107
+ .map((name) => join('.mise/locks', name))
108
+ .filter((name) => statSync(join(root, name)).isFile())
109
+ : [];
110
+ // Paths trusted on the machine are kept by every publish, in any order;
111
+ // the agent worktree roots must be among them.
112
+ const TRUSTED = /^trusted_config_paths\s*=.*\n/m;
113
+ const trusted = trustedPaths(current);
114
+ let listed: unknown;
115
+ try {
116
+ listed = (Bun.TOML.parse(current) as { settings?: Record<string, unknown> })
117
+ .settings?.['trusted_config_paths'];
118
+ } catch {
119
+ listed = undefined;
120
+ }
121
+ const unchanged =
122
+ Array.isArray(listed) &&
123
+ trusted.every((path) => listed.includes(path)) &&
124
+ current.replace(TRUSTED, '') ===
125
+ publishedConfig(root, current).replace(TRUSTED, '') &&
126
+ ['mise.lock', ...locks].every((name) =>
127
+ same(join(root, name), join(dir, name))
128
+ );
129
+ return unchanged ? 'published' : 'changed';
130
+ }
131
+
132
+ /**
133
+ * The managed roots plus any entries already in the published config, so a
134
+ * local `mise settings add trusted_config_paths ...` survives the next publish.
135
+ */
136
+ export function trustedPaths(previous: string | undefined): string[] {
137
+ let local: unknown;
138
+ try {
139
+ local = previous
140
+ ? (Bun.TOML.parse(previous) as { settings?: Record<string, unknown> })
141
+ .settings?.['trusted_config_paths']
142
+ : undefined;
143
+ } catch {
144
+ local = undefined;
145
+ }
146
+ const extra = Array.isArray(local)
147
+ ? local.filter((path): path is string => typeof path === 'string')
148
+ : [];
149
+ return [...new Set([...AGENT_WORKTREE_ROOTS, ...extra])];
150
+ }
151
+
152
+ /** Adds trusted_config_paths to the [settings] table, creating it if absent. */
153
+ export function withTrustedPaths(config: string, paths: string[]): string {
154
+ const line = `trusted_config_paths = [${paths.map((path) => JSON.stringify(path)).join(', ')}]`;
155
+ if (/^trusted_config_paths\s*=/m.test(config))
156
+ throw new Error(
157
+ 'mise.toml sets trusted_config_paths; mise ignores it outside the global config. Add roots to AGENT_WORKTREE_ROOTS instead.'
158
+ );
159
+ const header = /^\[settings\][^\S\n]*\n/m;
160
+ return header.test(config)
161
+ ? config.replace(header, (match) => `${match}${line}\n`)
162
+ : `${config.replace(/\n*$/, '\n')}\n[settings]\n${line}\n`;
163
+ }
164
+
165
+ function changedFile(source: string, destination: string): boolean {
166
+ return (
167
+ statSync(source).isDirectory() ||
168
+ !existsSync(destination) ||
169
+ !readFileSync(source).equals(readFileSync(destination))
170
+ );
171
+ }
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+ # Links ~/.local/bin/devfleet to this checkout (or the one given). Idempotent.
3
+ set -eu
4
+ root=${1:-$(cd "$(dirname "$0")/.." && pwd)}
5
+ [ -x "$root/bootstrap/devfleet" ] || exit 1
6
+ # Only ever a checkout: an installed package sits wherever a package manager
7
+ # put it and may be gone tomorrow.
8
+ [ -e "$root/.git" ] || { echo 'The devfleet launcher links to a checkout of dev-fleet; fleet-sync installs it.' >&2; exit 1; }
9
+ mkdir -p "$HOME/.local/bin"
10
+ ln -sfn "$root/bootstrap/devfleet" "$HOME/.local/bin/devfleet"
@@ -0,0 +1,86 @@
1
+ /** AWS v2's official Windows MSI; aqua only packages AWS for Unix. */
2
+ import { createHash } from 'node:crypto';
3
+ import { appendFileSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+ import { workspaceRoot } from './global-config';
8
+
9
+ type AwsConfig = {
10
+ tools: { aws: { version: string } };
11
+ vars: { aws_windows_msi_sha256: string };
12
+ };
13
+ export function awsWindowsArtifact(config: AwsConfig): {
14
+ url: string;
15
+ sha256: string;
16
+ version: string;
17
+ } {
18
+ const version = config.tools.aws.version;
19
+ const sha256 = config.vars.aws_windows_msi_sha256;
20
+ if (!/^2\.\d+\.\d+$/.test(version) || !/^[a-f0-9]{64}$/.test(sha256))
21
+ throw new Error('Invalid pinned AWS Windows artifact.');
22
+ return {
23
+ version,
24
+ url: `https://awscli.amazonaws.com/AWSCLIV2-${version}.msi`,
25
+ sha256,
26
+ };
27
+ }
28
+
29
+ export async function installNativeAws(): Promise<void> {
30
+ if (process.platform !== 'win32') return;
31
+ const config = Bun.TOML.parse(
32
+ readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
33
+ ) as AwsConfig;
34
+ const artifact = awsWindowsArtifact(config);
35
+ const bin = join(
36
+ process.env['ProgramFiles'] ?? 'C:\\Program Files',
37
+ 'Amazon',
38
+ 'AWSCLIV2'
39
+ );
40
+ const executable = join(bin, 'aws.exe');
41
+ const probe = () =>
42
+ spawnSync(executable, ['--version'], { encoding: 'utf8', timeout: 30_000 });
43
+ if (!probe().stdout?.includes(`aws-cli/${artifact.version} `)) {
44
+ const directory = mkdtempSync(join(tmpdir(), 'workspace-aws-'));
45
+ try {
46
+ const response = await fetch(artifact.url);
47
+ if (!response.ok)
48
+ throw new Error(`AWS MSI download failed (${response.status}).`);
49
+ const bytes = Buffer.from(await response.arrayBuffer());
50
+ if (createHash('sha256').update(bytes).digest('hex') !== artifact.sha256)
51
+ throw new Error('AWS MSI checksum mismatch.');
52
+ const msi = join(directory, 'aws.msi');
53
+ await Bun.write(msi, bytes);
54
+ const script = String.raw`$arguments = @('/i', ('"' + $env:WORKSPACE_AWS_MSI + '"'), '/qn', '/norestart'); $options = @{FilePath='msiexec.exe'; ArgumentList=$arguments; Wait=$true; PassThru=$true}; if ($env:WORKSPACE_AWS_ELEVATE -eq '1') { $options.Verb = 'RunAs' }; $p = Start-Process @options; exit $p.ExitCode`;
55
+ const result = spawnSync(
56
+ 'powershell.exe',
57
+ ['-NoProfile', '-Command', script],
58
+ {
59
+ stdio: 'inherit',
60
+ env: {
61
+ ...process.env,
62
+ WORKSPACE_AWS_MSI: msi,
63
+ WORKSPACE_AWS_ELEVATE:
64
+ process.argv.includes('--non-interactive') || process.env['CI']
65
+ ? '0'
66
+ : '1',
67
+ },
68
+ }
69
+ );
70
+ if (result.status !== 0 && result.status !== 3010)
71
+ throw new Error(
72
+ `AWS MSI installation failed (exit ${result.status}, signal ${result.signal}, error ${result.error?.message ?? 'none'}); check for a conflicting installed version or run the reviewed bootstrap as Administrator.`
73
+ );
74
+ } finally {
75
+ rmSync(directory, { recursive: true, force: true });
76
+ }
77
+ }
78
+ if (!probe().stdout?.includes(`aws-cli/${artifact.version} `))
79
+ throw new Error(
80
+ `Expected AWS CLI ${artifact.version}; resolve the existing installation before retrying.`
81
+ );
82
+ process.env['PATH'] = `${bin};${process.env['PATH'] ?? ''}`;
83
+ // Persist across subsequent hosted-runner steps; normal users reopen the shell.
84
+ if (process.env['GITHUB_PATH'])
85
+ appendFileSync(process.env['GITHUB_PATH'], `${bin}\n`);
86
+ }
@@ -0,0 +1,154 @@
1
+ /** Pinned Ubuntu T3 distribution. Shared tool installation owns artifacts;
2
+ * Workstation owns services. Never touches ~/.t3 or replaces an existing launcher.
3
+ */
4
+ import { createHash } from 'node:crypto';
5
+ import {
6
+ existsSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ renameSync,
10
+ rmSync,
11
+ writeFileSync,
12
+ } from 'node:fs';
13
+ import { homedir } from 'node:os';
14
+ import { join } from 'node:path';
15
+ import { spawnSync } from 'node:child_process';
16
+ import { workspaceRoot } from './global-config';
17
+ interface Configuration {
18
+ vars: {
19
+ t3_version: string;
20
+ t3_linux_x64_sha256: string;
21
+ t3_linux_arm64_sha256: string;
22
+ t3_desktop_amd64_sha256: string;
23
+ t3_desktop_arm64_sha256: string;
24
+ };
25
+ }
26
+ /** Pinned desktop .deb for Ubuntu workers. The GUI app is the worker's only T3
27
+ * process and embeds the server; the CLI tarball stays for policy commands. */
28
+ export function desktopT3Artifact(config: Configuration, architecture: string) {
29
+ const deb = { x64: 'amd64', arm64: 'arm64' }[architecture];
30
+ if (!deb) throw new Error('Unsupported T3 architecture.');
31
+ const version = config.vars.t3_version;
32
+ const sha256 =
33
+ deb === 'amd64'
34
+ ? config.vars.t3_desktop_amd64_sha256
35
+ : config.vars.t3_desktop_arm64_sha256;
36
+ if (
37
+ !/^\d+\.\d+\.\d+(?:-nightly\.\d{8}\.\d+)?$/.test(version) ||
38
+ !/^[a-f0-9]{64}$/.test(sha256)
39
+ )
40
+ throw new Error('Invalid T3 desktop pin.');
41
+ const name = `T3-Code-${version}-${deb}.deb`;
42
+ return {
43
+ version,
44
+ sha256,
45
+ name,
46
+ url: `https://github.com/pingdotgg/t3code/releases/download/v${version}/${name}`,
47
+ };
48
+ }
49
+ export function managedT3Desktop() {
50
+ const config = Bun.TOML.parse(
51
+ readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
52
+ ) as Configuration;
53
+ return desktopT3Artifact(config, process.arch);
54
+ }
55
+ export function nativeT3Artifact(config: Configuration, architecture: string) {
56
+ if (!['x64', 'arm64'].includes(architecture))
57
+ throw new Error('Unsupported T3 architecture.');
58
+ const version = config.vars.t3_version;
59
+ const sha256 =
60
+ architecture === 'x64'
61
+ ? config.vars.t3_linux_x64_sha256
62
+ : config.vars.t3_linux_arm64_sha256;
63
+ if (
64
+ !/^\d+\.\d+\.\d+(?:-nightly\.\d{8}\.\d+)?$/.test(version) ||
65
+ !/^[a-f0-9]{64}$/.test(sha256)
66
+ )
67
+ throw new Error('Invalid T3 pin.');
68
+ const name = `t3-${version}-linux-${architecture}`;
69
+ return {
70
+ version,
71
+ sha256,
72
+ name,
73
+ url: `https://github.com/pingdotgg/t3code/releases/download/v${version}/${name}.tar.gz`,
74
+ };
75
+ }
76
+ export function managedT3(): { path: string; version: string } {
77
+ const config = Bun.TOML.parse(
78
+ readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
79
+ ) as Configuration;
80
+ const artifact = nativeT3Artifact(config, process.arch);
81
+ return {
82
+ path: join(homedir(), '.local/share/personal/tools', artifact.name, 't3'),
83
+ version: artifact.version,
84
+ };
85
+ }
86
+ export async function installNativeT3(): Promise<void> {
87
+ if (process.platform !== 'linux') return;
88
+ const config = Bun.TOML.parse(
89
+ readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
90
+ ) as Configuration;
91
+ const artifact = nativeT3Artifact(config, process.arch);
92
+ const root = join(homedir(), '.local/share/personal/tools');
93
+ const archive = join(root, `${artifact.name}.tar.gz`);
94
+ mkdirSync(root, { recursive: true, mode: 0o700 });
95
+ if (!existsSync(archive)) {
96
+ const response = await fetch(artifact.url, {
97
+ signal: AbortSignal.timeout(120_000),
98
+ });
99
+ if (!response.ok) throw new Error('Pinned T3 artifact unavailable.');
100
+ const bytes = Buffer.from(await response.arrayBuffer());
101
+ if (createHash('sha256').update(bytes).digest('hex') !== artifact.sha256)
102
+ throw new Error('T3 artifact checksum mismatch.');
103
+ writeFileSync(`${archive}.partial`, bytes, { mode: 0o600 });
104
+ renameSync(`${archive}.partial`, archive);
105
+ }
106
+ if (
107
+ createHash('sha256').update(readFileSync(archive)).digest('hex') !==
108
+ artifact.sha256
109
+ )
110
+ throw new Error(
111
+ 'T3 cached artifact drift; preserve running service and review maintenance recovery.'
112
+ );
113
+ const directory = join(root, artifact.name);
114
+ if (!existsSync(directory)) {
115
+ const staging = join(root, `${artifact.name}.staging`);
116
+ mkdirSync(staging, { recursive: true, mode: 0o700 });
117
+ try {
118
+ if (
119
+ spawnSync(
120
+ 'tar',
121
+ ['--same-permissions', '-xzf', archive, '-C', staging],
122
+ { timeout: 120_000 }
123
+ ).status !== 0
124
+ )
125
+ throw new Error('T3 extraction failed.');
126
+ verifyArchive(archive, staging);
127
+ renameSync(join(staging, artifact.name), directory);
128
+ } finally {
129
+ rmSync(staging, { recursive: true, force: true });
130
+ }
131
+ } else verifyArchive(archive, root);
132
+ const version = spawnSync(join(directory, 't3'), ['--version'], {
133
+ encoding: 'utf8',
134
+ timeout: 30_000,
135
+ });
136
+ if (
137
+ version.status !== 0 ||
138
+ version.stdout.trim() !== `t3 v${artifact.version}`
139
+ )
140
+ throw new Error('Pinned T3 executable verification failed.');
141
+ }
142
+
143
+ function verifyArchive(archive: string, root: string): void {
144
+ if (
145
+ spawnSync(
146
+ 'python3',
147
+ [join(workspaceRoot, 'bootstrap/verify-t3-archive.py'), archive, root],
148
+ { timeout: 120_000 }
149
+ ).status !== 0
150
+ )
151
+ throw new Error(
152
+ 'Managed T3 files differ from the pinned artifact; maintenance is required.'
153
+ );
154
+ }