@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,78 @@
1
+ # Manager LaunchAgent Python dependencies (hash-locked). Regenerate with:
2
+ # echo pyyaml==VERSION | uv pip compile - --generate-hashes --universal --no-header
3
+
4
+ pyyaml==6.0.3 \
5
+ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
6
+ --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \
7
+ --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
8
+ --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
9
+ --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
10
+ --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
11
+ --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
12
+ --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
13
+ --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \
14
+ --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
15
+ --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
16
+ --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \
17
+ --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \
18
+ --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
19
+ --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \
20
+ --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
21
+ --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
22
+ --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \
23
+ --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \
24
+ --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
25
+ --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
26
+ --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
27
+ --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
28
+ --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
29
+ --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
30
+ --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
31
+ --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
32
+ --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \
33
+ --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
34
+ --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \
35
+ --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
36
+ --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
37
+ --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \
38
+ --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
39
+ --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
40
+ --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
41
+ --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
42
+ --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
43
+ --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
44
+ --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
45
+ --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
46
+ --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
47
+ --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
48
+ --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
49
+ --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
50
+ --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
51
+ --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
52
+ --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
53
+ --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
54
+ --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \
55
+ --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
56
+ --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
57
+ --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
58
+ --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
59
+ --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
60
+ --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
61
+ --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \
62
+ --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
63
+ --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
64
+ --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
65
+ --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
66
+ --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \
67
+ --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \
68
+ --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
69
+ --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
70
+ --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
71
+ --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
72
+ --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
73
+ --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
74
+ --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \
75
+ --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
76
+ --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \
77
+ --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
78
+ # via -r requirements.in
@@ -0,0 +1,38 @@
1
+ """Account routing: each signed-in provider account gets its own model prefix.
2
+
3
+ The proxy's credential files are the only source of truth for accounts. A
4
+ credential of a routed type is given the prefix `<type>-<email>`, so a client
5
+ that asks for `<type>-<email>/<model>` is served only by that account, and the
6
+ proxy's model list tells every client which accounts exist. Unprefixed models
7
+ keep using the whole fill-first pool.
8
+ """
9
+ import json
10
+ import pathlib
11
+ import re
12
+
13
+ # Provider credential types that T3 offers once per account.
14
+ ROUTED = ('claude', 'codex', 'antigravity')
15
+
16
+
17
+ def prefix(kind, email):
18
+ """The model prefix for one credential, or None when it is not routed."""
19
+ if kind in ROUTED and isinstance(email, str) and re.fullmatch(r'[^\s@/]{1,64}@[^\s@/]{1,255}', email):
20
+ return kind + '-' + email
21
+ return None
22
+
23
+
24
+ def pending(auths):
25
+ """[(credential file name, prefix)] for credentials without their prefix.
26
+ Reads only type, email and prefix; never a token."""
27
+ result = []
28
+ for path in sorted(pathlib.Path(auths).glob('*.json')):
29
+ try:
30
+ data = json.loads(path.read_text())
31
+ except (OSError, ValueError):
32
+ continue
33
+ if not isinstance(data, dict):
34
+ continue
35
+ wanted = prefix(data.get('type'), data.get('email'))
36
+ if wanted and data.get('prefix') != wanted:
37
+ result.append((path.name, wanted))
38
+ return result
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env python3
2
+ """Require runtime code and unit links to resolve to clean, Git-tracked files."""
3
+
4
+ import sys
5
+
6
+ sys.dont_write_bytecode = True
7
+
8
+ import pathlib as _pathlib
9
+
10
+ sys.path.insert(0, str(_pathlib.Path(__file__).resolve().parent))
11
+ import cliproxy_host
12
+
13
+ import hashlib
14
+ import json
15
+ import os
16
+ import pathlib
17
+ import subprocess
18
+ import sys
19
+
20
+
21
+ def quiet(args):
22
+ try:
23
+ return (
24
+ subprocess.run(
25
+ args, capture_output=True, timeout=30, check=False
26
+ ).returncode
27
+ == 0
28
+ )
29
+ except (OSError, subprocess.TimeoutExpired):
30
+ return False
31
+
32
+
33
+ def audit(root, check_units=True, backend=None, run=quiet):
34
+ """Check provenance; with check_units, also the platform's unit links (and,
35
+ for launchd, that the required agents are loaded in gui/$UID)."""
36
+ manifest = json.loads((root / "INSTALLATION.json").read_text())
37
+ errors = []
38
+ if manifest.get("schema_version") != 2:
39
+ return manifest, [
40
+ "legacy copied-code installation; reinstall from the repository"
41
+ ]
42
+ worktree = pathlib.Path(manifest["worktree"])
43
+ command = [
44
+ "git",
45
+ "--no-optional-locks",
46
+ "-c",
47
+ "safe.directory=" + str(worktree),
48
+ "-C",
49
+ str(worktree),
50
+ ]
51
+ revision = (
52
+ subprocess.check_output(command + ["rev-parse", "HEAD"], stderr=subprocess.PIPE)
53
+ .decode()
54
+ .strip()
55
+ )
56
+ tracked = set(
57
+ subprocess.check_output(
58
+ command + ["ls-files", "-z", "--", manifest["package"]],
59
+ stderr=subprocess.PIPE,
60
+ )
61
+ .decode()
62
+ .strip("\0")
63
+ .split("\0")
64
+ )
65
+ if revision != manifest["revision"]:
66
+ errors.append("deployment worktree revision")
67
+ if subprocess.check_output(
68
+ command + ["status", "--porcelain", "--", manifest["package"]],
69
+ stderr=subprocess.PIPE,
70
+ ).strip():
71
+ errors.append("deployment worktree has local changes")
72
+ # Resolve once: macOS temporary/home paths may route through symlinks.
73
+ source = worktree.resolve() / manifest["package"]
74
+ units = (backend or cliproxy_host.backend()) if check_units else None
75
+ for name, expected in manifest["files"].items():
76
+ path = root / name
77
+ if (
78
+ manifest["package"] + "/" + name not in tracked
79
+ or not path.is_file()
80
+ or path.resolve() != source / name
81
+ or hashlib.sha256(path.read_bytes()).hexdigest() != expected
82
+ ):
83
+ errors.append(name)
84
+ if units and name.startswith(units.source_directory + "/"):
85
+ unit = units.unit_directory / path.name
86
+ if not unit.is_symlink() or unit.resolve() != source / name:
87
+ errors.append(str(unit))
88
+ for name, expected in manifest["generated"].items():
89
+ if hashlib.sha256((root / name).read_bytes()).hexdigest() != expected:
90
+ errors.append(name)
91
+ if units and units.name == "launchd":
92
+ required = [units.service, *units.scheduled]
93
+ if not (root / "state/secrets/recovery-confirmed").exists():
94
+ required = [label for label in required if label not in units.recovery_gated]
95
+ for label in required:
96
+ if not units.loaded(label, run):
97
+ errors.append("not loaded: " + units.domain + "/" + label)
98
+ for folder in ["scripts", "clients", "systemd", "launchd"]:
99
+ for path in (root / folder).rglob("*"):
100
+ if (
101
+ (root / folder).is_dir()
102
+ and path.is_file()
103
+ and "__pycache__" not in path.parts
104
+ and str(path.relative_to(root)) not in manifest["files"]
105
+ ):
106
+ errors.append("unmanaged: " + str(path.relative_to(root)))
107
+ return manifest, errors
108
+
109
+
110
+ if __name__ == "__main__":
111
+ try:
112
+ manifest, errors = audit(cliproxy_host.default_root())
113
+ if errors:
114
+ print("Code is not clean tracked repository source: " + ", ".join(errors))
115
+ sys.exit(1)
116
+ print(
117
+ "Runtime code is Git-tracked at crvouga/dev-fleet commit "
118
+ + manifest["revision"]
119
+ )
120
+ except (OSError, ValueError, KeyError, subprocess.CalledProcessError):
121
+ sys.exit(
122
+ "Git source unavailable or provenance invalid; reinstall from the repository."
123
+ )
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env python3
2
+ import sys
3
+
4
+ sys.dont_write_bytecode = True
5
+
6
+ import pathlib as _pathlib
7
+
8
+ sys.path.insert(0, str(_pathlib.Path(__file__).resolve().parent))
9
+ import cliproxy_host
10
+
11
+ import datetime, json, os, pathlib, subprocess, sys, tempfile, fcntl, importlib.util
12
+ import checkpoint
13
+ import retention
14
+
15
+ root = cliproxy_host.default_root().resolve()
16
+ os.umask(0o077)
17
+ os.environ["PATH"] = str(root / "scripts") + ":" + os.environ["PATH"]
18
+ state = root / "state"
19
+ status = state / "status"
20
+ status.mkdir(parents=True, exist_ok=True)
21
+ lock = open(status / "operation.lock", "w")
22
+ if os.geteuid() == 0:
23
+ os.chown(status / "operation.lock", 1000, 1000)
24
+ fcntl.flock(lock, fcntl.LOCK_EX)
25
+ command = sys.argv[1] if len(sys.argv) > 1 else "backup"
26
+ failure = status / (
27
+ "backup-failed"
28
+ if command == "backup"
29
+ else "backup-check-failed"
30
+ if command in ["check", "check-full"]
31
+ else "gc-failed"
32
+ if command == "gc"
33
+ else "backup-operation-failed"
34
+ )
35
+
36
+
37
+ def run(args, **kw):
38
+ return subprocess.run(args, check=True, **kw)
39
+
40
+
41
+ def write(path, text):
42
+ path.write_text(text)
43
+ if os.geteuid() == 0:
44
+ os.chown(path, 1000, 1000)
45
+
46
+
47
+ try:
48
+ retention.staging(root)
49
+ spec = importlib.util.spec_from_file_location(
50
+ "vault_access", root / "scripts/vault-access.py"
51
+ )
52
+ vault = importlib.util.module_from_spec(spec)
53
+ spec.loader.exec_module(vault)
54
+ vault.renew()
55
+ cfg = vault.credentials()
56
+ identity = {k: cfg[k] for k in ["S3_ENDPOINT", "S3_BUCKET", "S3_REGION"]}
57
+ destination = state / "repository.json"
58
+ if destination.exists() and json.loads(destination.read_text()) != identity:
59
+ sys.exit(
60
+ "Vault repository location changed. Review and update state/repository.json explicitly; refusing to silently switch repositories."
61
+ )
62
+ if not destination.exists() and command != "restore-cache":
63
+ write(destination, json.dumps(identity) + "\n")
64
+ if not cfg["S3_ENDPOINT"].startswith("https://"):
65
+ raise ValueError("Storage endpoint must use TLS")
66
+ env = os.environ.copy()
67
+ env.update(
68
+ AWS_ACCESS_KEY_ID=cfg["S3_ACCESS_KEY_ID"],
69
+ AWS_SECRET_ACCESS_KEY=cfg["S3_SECRET_ACCESS_KEY"],
70
+ AWS_DEFAULT_REGION=cfg["S3_REGION"],
71
+ RESTIC_REPOSITORY="s3:"
72
+ + cfg["S3_ENDPOINT"].rstrip("/")
73
+ + "/"
74
+ + cfg["S3_BUCKET"]
75
+ + "/cliproxy-fleet/restic",
76
+ RESTIC_PASSWORD_FILE=str(state / "secrets/restic-password"),
77
+ )
78
+
79
+ def restic(*args, **kw):
80
+ return run(
81
+ ["restic", "-o", "s3.bucket-lookup=path", *args, "--no-cache"],
82
+ env=env,
83
+ **kw,
84
+ )
85
+
86
+ if command == "init":
87
+ restic("init")
88
+ elif command == "check":
89
+ restic("check", "--read-data-subset=10%")
90
+ elif command == "check-full":
91
+ restic("check", "--read-data")
92
+ elif command == "gc":
93
+ retention.prune(restic)
94
+ removed = retention.revisions(root)
95
+ write(
96
+ status / "gc-success.json",
97
+ json.dumps(
98
+ {
99
+ "time": datetime.datetime.now(datetime.timezone.utc).isoformat(),
100
+ "retired_worktrees": removed,
101
+ }
102
+ )
103
+ + "\n",
104
+ )
105
+ print(
106
+ "Cloud garbage collected and integrity checked; retired clean worktrees removed:",
107
+ removed,
108
+ )
109
+ elif command == "snapshots":
110
+ restic("snapshots")
111
+ elif command == "restore":
112
+ if len(sys.argv) != 4:
113
+ sys.exit("Usage: backup restore SNAPSHOT EMPTY_DIRECTORY")
114
+ target = pathlib.Path(sys.argv[3]).resolve()
115
+ if target.exists() and any(target.iterdir()):
116
+ sys.exit("Restore target must be empty.")
117
+ target.mkdir(parents=True, exist_ok=True)
118
+ restic("restore", sys.argv[2], "--target", str(target), "--verify")
119
+ elif command == "restore-cache":
120
+ if sys.argv[2:] != ["--previous-node-stopped"]:
121
+ raise ValueError("Explicit previous-node-stopped acknowledgement required")
122
+ # This local check complements operator fencing of the previous machine.
123
+ active = subprocess.check_output(
124
+ [
125
+ "docker",
126
+ "compose",
127
+ "--profile",
128
+ "portable-endpoint",
129
+ "--profile",
130
+ "public-endpoint",
131
+ "ps",
132
+ "--status",
133
+ "running",
134
+ "--services",
135
+ ],
136
+ cwd=root,
137
+ text=True,
138
+ ).strip()
139
+ if active:
140
+ raise ValueError("Stop the local stack before recovery")
141
+ with tempfile.TemporaryDirectory(
142
+ prefix="restore-", dir=root / "recovery"
143
+ ) as temporary:
144
+ target = pathlib.Path(temporary)
145
+ restic(
146
+ "restore",
147
+ "latest",
148
+ "--tag",
149
+ "cliproxy-checkpoint-v1",
150
+ "--host",
151
+ "cliproxy-primary",
152
+ "--target",
153
+ str(target),
154
+ "--verify",
155
+ stdout=subprocess.PIPE,
156
+ stderr=subprocess.PIPE,
157
+ )
158
+ payload = checkpoint.locate(target)
159
+ saved_destination = payload / "state/repository.json"
160
+ if (
161
+ not saved_destination.exists()
162
+ or json.loads(saved_destination.read_text()) != identity
163
+ ):
164
+ raise ValueError("Recovered repository identity differs from Vault")
165
+ checkpoint.hydrate(payload, state)
166
+ print(
167
+ "Cloud checkpoint recovered into empty cache. No services started. Run install, then health verification."
168
+ )
169
+ elif command == "backup":
170
+ if not (state / "secrets/recovery-confirmed").exists():
171
+ sys.exit(
172
+ "Store Restic password independently; then create state/secrets/recovery-confirmed."
173
+ )
174
+
175
+ recovery = root / "recovery"
176
+ recovery.mkdir(mode=0o700, exist_ok=True)
177
+ if os.geteuid() == 0:
178
+ os.chown(recovery, 1000, 1000)
179
+ with tempfile.TemporaryDirectory(
180
+ prefix="checkpoint-", dir=recovery
181
+ ) as temporary:
182
+ captured = pathlib.Path(temporary) / "payload"
183
+ # The stack keeps running: a live, verified-stable copy.
184
+ checkpoint.capture(root, captured)
185
+ result = restic(
186
+ "backup",
187
+ "--json",
188
+ str(captured),
189
+ "--tag",
190
+ "cliproxy-fleet",
191
+ "--tag",
192
+ "cliproxy-checkpoint-v1",
193
+ "--host",
194
+ "cliproxy-primary",
195
+ stdout=subprocess.PIPE,
196
+ stderr=subprocess.PIPE,
197
+ text=True,
198
+ )
199
+ summaries = [json.loads(line) for line in result.stdout.splitlines()]
200
+ summary = next(
201
+ item
202
+ for item in reversed(summaries)
203
+ if item.get("message_type") == "summary"
204
+ )
205
+ snapshot = summary["snapshot_id"]
206
+ if not isinstance(snapshot, str) or not snapshot:
207
+ raise ValueError("Missing snapshot acknowledgement")
208
+ print("Complete encrypted checkpoint saved:", snapshot)
209
+ retention.forget(restic)
210
+ write(
211
+ status / "backup-success.json",
212
+ json.dumps(
213
+ {
214
+ "snapshot": snapshot,
215
+ "time": datetime.datetime.now(datetime.timezone.utc).isoformat(),
216
+ "destination": "configured R2 bucket / cliproxy-fleet/restic",
217
+ }
218
+ )
219
+ + "\n",
220
+ )
221
+ else:
222
+ sys.exit(
223
+ "Use init, backup, gc, snapshots, check, check-full, restore SNAPSHOT EMPTY_DIRECTORY, or restore-cache --previous-node-stopped."
224
+ )
225
+ except (Exception, SystemExit):
226
+ write(failure, datetime.datetime.now(datetime.timezone.utc).isoformat() + "\n")
227
+ sys.exit(
228
+ "Backup operation failed. Inspect systemd unit status; credentials suppressed."
229
+ )
230
+ else:
231
+ failure.unlink(missing_ok=True)
232
+
233
+ finally:
234
+ lock.close()
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ """Offline syntax/config checks; no cloud access or running deployment required."""
3
+
4
+ import sys
5
+
6
+ sys.dont_write_bytecode = True
7
+
8
+
9
+ import ast
10
+ import json
11
+ import pathlib
12
+ import re
13
+ import subprocess
14
+
15
+ root = pathlib.Path(__file__).resolve().parent.parent
16
+ for directory in ["scripts", "clients", "tests"]:
17
+ for path in (root / directory).rglob("*"):
18
+ if not path.is_file() or "__pycache__" in path.parts:
19
+ continue
20
+ data = path.read_text()
21
+ if path.suffix == ".py" or data.startswith("#!/usr/bin/env python3"):
22
+ ast.parse(data, filename=str(path))
23
+ elif data.startswith("#!/usr/bin/env bash") or data.startswith("#!/bin/bash"):
24
+ subprocess.run(["bash", "-n", str(path)], check=True)
25
+ images = json.loads((root / "image.lock.json").read_text())
26
+ assert all(
27
+ re.fullmatch(r"[a-z0-9/.-]+@sha256:[0-9a-f]{64}", image)
28
+ for image in images.values()
29
+ )
30
+ sys.path.insert(0, str(root / "scripts"))
31
+ from modelhealth import config as model_config
32
+
33
+ model_config.load(root / "models.json") # declarative model-health contract
34
+ print("Python/shell syntax, immutable image pins and models.json passed.")