@openrig/cli 0.4.3 → 0.4.6

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 (634) hide show
  1. package/daemon/assets/guidance/openrig-start.md +6 -0
  2. package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +47 -21
  3. package/daemon/assets/plugins/openrig-core/skills/openrig-cmux/SKILL.md +86 -0
  4. package/daemon/assets/plugins/openrig-core/skills/openrig-herdr/SKILL.md +158 -0
  5. package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +742 -94
  6. package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
  7. package/daemon/dist/adapters/codex-runtime-adapter.js +12 -8
  8. package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
  9. package/daemon/dist/adapters/pi-resume.d.ts +32 -0
  10. package/daemon/dist/adapters/pi-resume.d.ts.map +1 -0
  11. package/daemon/dist/adapters/pi-resume.js +125 -0
  12. package/daemon/dist/adapters/pi-resume.js.map +1 -0
  13. package/daemon/dist/adapters/pi-runner-protocol.d.ts +88 -0
  14. package/daemon/dist/adapters/pi-runner-protocol.d.ts.map +1 -0
  15. package/daemon/dist/adapters/pi-runner-protocol.js +153 -0
  16. package/daemon/dist/adapters/pi-runner-protocol.js.map +1 -0
  17. package/daemon/dist/adapters/pi-runner.d.ts +99 -0
  18. package/daemon/dist/adapters/pi-runner.d.ts.map +1 -0
  19. package/daemon/dist/adapters/pi-runner.js +519 -0
  20. package/daemon/dist/adapters/pi-runner.js.map +1 -0
  21. package/daemon/dist/adapters/pi-runtime-adapter.d.ts +61 -0
  22. package/daemon/dist/adapters/pi-runtime-adapter.d.ts.map +1 -0
  23. package/daemon/dist/adapters/pi-runtime-adapter.js +356 -0
  24. package/daemon/dist/adapters/pi-runtime-adapter.js.map +1 -0
  25. package/daemon/dist/adapters/tmux.d.ts +22 -1
  26. package/daemon/dist/adapters/tmux.d.ts.map +1 -1
  27. package/daemon/dist/adapters/tmux.js +40 -1
  28. package/daemon/dist/adapters/tmux.js.map +1 -1
  29. package/daemon/dist/build-info.d.ts +12 -0
  30. package/daemon/dist/build-info.d.ts.map +1 -0
  31. package/daemon/dist/build-info.js +11 -0
  32. package/daemon/dist/build-info.js.map +1 -0
  33. package/daemon/dist/builtins/workflow-specs/branched-remediation.yaml +31 -0
  34. package/daemon/dist/builtins/workflow-specs/factory-rsi.yaml +152 -0
  35. package/daemon/dist/builtins/workflow-specs/gated-release.yaml +26 -0
  36. package/daemon/dist/builtins/workflow-specs/linear-build.yaml +21 -0
  37. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts +18 -0
  38. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts.map +1 -0
  39. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js +22 -0
  40. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js.map +1 -0
  41. package/daemon/dist/db/migrations/049_workflow_instance_version.d.ts +20 -0
  42. package/daemon/dist/db/migrations/049_workflow_instance_version.d.ts.map +1 -0
  43. package/daemon/dist/db/migrations/049_workflow_instance_version.js +24 -0
  44. package/daemon/dist/db/migrations/049_workflow_instance_version.js.map +1 -0
  45. package/daemon/dist/db/migrations/050_workflow_spec_json.d.ts +22 -0
  46. package/daemon/dist/db/migrations/050_workflow_spec_json.d.ts.map +1 -0
  47. package/daemon/dist/db/migrations/050_workflow_spec_json.js +26 -0
  48. package/daemon/dist/db/migrations/050_workflow_spec_json.js.map +1 -0
  49. package/daemon/dist/db/migrations/051_workflow_resume.d.ts +22 -0
  50. package/daemon/dist/db/migrations/051_workflow_resume.d.ts.map +1 -0
  51. package/daemon/dist/db/migrations/051_workflow_resume.js +27 -0
  52. package/daemon/dist/db/migrations/051_workflow_resume.js.map +1 -0
  53. package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.d.ts +26 -0
  54. package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.d.ts.map +1 -0
  55. package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.js +30 -0
  56. package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.js.map +1 -0
  57. package/daemon/dist/db/migrations/053_sessions_node_id_index.d.ts +48 -0
  58. package/daemon/dist/db/migrations/053_sessions_node_id_index.d.ts.map +1 -0
  59. package/daemon/dist/db/migrations/053_sessions_node_id_index.js +53 -0
  60. package/daemon/dist/db/migrations/053_sessions_node_id_index.js.map +1 -0
  61. package/daemon/dist/db/migrations/054_queue_transitions_archive.d.ts +34 -0
  62. package/daemon/dist/db/migrations/054_queue_transitions_archive.d.ts.map +1 -0
  63. package/daemon/dist/db/migrations/054_queue_transitions_archive.js +49 -0
  64. package/daemon/dist/db/migrations/054_queue_transitions_archive.js.map +1 -0
  65. package/daemon/dist/domain/claim-service.d.ts +11 -0
  66. package/daemon/dist/domain/claim-service.d.ts.map +1 -1
  67. package/daemon/dist/domain/claim-service.js +3 -1
  68. package/daemon/dist/domain/claim-service.js.map +1 -1
  69. package/daemon/dist/domain/discovery-types.d.ts +1 -1
  70. package/daemon/dist/domain/discovery-types.d.ts.map +1 -1
  71. package/daemon/dist/domain/feed/attention-aggregator.d.ts +31 -0
  72. package/daemon/dist/domain/feed/attention-aggregator.d.ts.map +1 -0
  73. package/daemon/dist/domain/feed/attention-aggregator.js +119 -0
  74. package/daemon/dist/domain/feed/attention-aggregator.js.map +1 -0
  75. package/daemon/dist/domain/files/file-write-service.d.ts +11 -2
  76. package/daemon/dist/domain/files/file-write-service.d.ts.map +1 -1
  77. package/daemon/dist/domain/files/file-write-service.js +77 -0
  78. package/daemon/dist/domain/files/file-write-service.js.map +1 -1
  79. package/daemon/dist/domain/hosts/fanout-contract.d.ts +33 -0
  80. package/daemon/dist/domain/hosts/fanout-contract.d.ts.map +1 -0
  81. package/daemon/dist/domain/hosts/fanout-contract.js +23 -0
  82. package/daemon/dist/domain/hosts/fanout-contract.js.map +1 -0
  83. package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts +50 -0
  84. package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts.map +1 -0
  85. package/daemon/dist/domain/hosts/hosts-registry-reader.js +184 -0
  86. package/daemon/dist/domain/hosts/hosts-registry-reader.js.map +1 -0
  87. package/daemon/dist/domain/hosts/hosts-registry-writer.d.ts +11 -0
  88. package/daemon/dist/domain/hosts/hosts-registry-writer.d.ts.map +1 -0
  89. package/daemon/dist/domain/hosts/hosts-registry-writer.js +54 -0
  90. package/daemon/dist/domain/hosts/hosts-registry-writer.js.map +1 -0
  91. package/daemon/dist/domain/hosts/read-through.d.ts +28 -0
  92. package/daemon/dist/domain/hosts/read-through.d.ts.map +1 -0
  93. package/daemon/dist/domain/hosts/read-through.js +139 -0
  94. package/daemon/dist/domain/hosts/read-through.js.map +1 -0
  95. package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +56 -0
  96. package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -0
  97. package/daemon/dist/domain/hosts/remote-daemon-http.js +154 -0
  98. package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -0
  99. package/daemon/dist/domain/human-route-enforcer.d.ts +71 -0
  100. package/daemon/dist/domain/human-route-enforcer.d.ts.map +1 -0
  101. package/daemon/dist/domain/human-route-enforcer.js +80 -0
  102. package/daemon/dist/domain/human-route-enforcer.js.map +1 -0
  103. package/daemon/dist/domain/mission-control/audit-browse.d.ts +8 -0
  104. package/daemon/dist/domain/mission-control/audit-browse.d.ts.map +1 -1
  105. package/daemon/dist/domain/mission-control/audit-browse.js +19 -0
  106. package/daemon/dist/domain/mission-control/audit-browse.js.map +1 -1
  107. package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts +1 -1
  108. package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts.map +1 -1
  109. package/daemon/dist/domain/mission-control/mission-control-action-log.js +4 -0
  110. package/daemon/dist/domain/mission-control/mission-control-action-log.js.map +1 -1
  111. package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js +1 -1
  112. package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js.map +1 -1
  113. package/daemon/dist/domain/mission-control/mission-control-read-layer.js +1 -1
  114. package/daemon/dist/domain/mission-control/mission-control-read-layer.js.map +1 -1
  115. package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts +28 -0
  116. package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts.map +1 -1
  117. package/daemon/dist/domain/mission-control/mission-control-write-contract.js +125 -0
  118. package/daemon/dist/domain/mission-control/mission-control-write-contract.js.map +1 -1
  119. package/daemon/dist/domain/node-inventory.d.ts +18 -0
  120. package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
  121. package/daemon/dist/domain/node-inventory.js +206 -95
  122. package/daemon/dist/domain/node-inventory.js.map +1 -1
  123. package/daemon/dist/domain/node-launcher.d.ts +11 -0
  124. package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
  125. package/daemon/dist/domain/node-launcher.js +10 -0
  126. package/daemon/dist/domain/node-launcher.js.map +1 -1
  127. package/daemon/dist/domain/policies/workflow-keepalive.d.ts +7 -0
  128. package/daemon/dist/domain/policies/workflow-keepalive.d.ts.map +1 -1
  129. package/daemon/dist/domain/policies/workflow-keepalive.js +93 -13
  130. package/daemon/dist/domain/policies/workflow-keepalive.js.map +1 -1
  131. package/daemon/dist/domain/ps-projection.d.ts +28 -1
  132. package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
  133. package/daemon/dist/domain/ps-projection.js +76 -26
  134. package/daemon/dist/domain/ps-projection.js.map +1 -1
  135. package/daemon/dist/domain/queue-repository.d.ts +130 -1
  136. package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
  137. package/daemon/dist/domain/queue-repository.js +304 -11
  138. package/daemon/dist/domain/queue-repository.js.map +1 -1
  139. package/daemon/dist/domain/queue-retention.d.ts +130 -0
  140. package/daemon/dist/domain/queue-retention.d.ts.map +1 -0
  141. package/daemon/dist/domain/queue-retention.js +224 -0
  142. package/daemon/dist/domain/queue-retention.js.map +1 -0
  143. package/daemon/dist/domain/restore-orchestrator.d.ts +5 -0
  144. package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
  145. package/daemon/dist/domain/restore-orchestrator.js +29 -2
  146. package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
  147. package/daemon/dist/domain/resume-token-capture.d.ts +13 -0
  148. package/daemon/dist/domain/resume-token-capture.d.ts.map +1 -1
  149. package/daemon/dist/domain/resume-token-capture.js +14 -1
  150. package/daemon/dist/domain/resume-token-capture.js.map +1 -1
  151. package/daemon/dist/domain/resume-token-validation.d.ts +1 -1
  152. package/daemon/dist/domain/resume-token-validation.d.ts.map +1 -1
  153. package/daemon/dist/domain/resume-token-validation.js +58 -10
  154. package/daemon/dist/domain/resume-token-validation.js.map +1 -1
  155. package/daemon/dist/domain/review/brief-spine.d.ts +19 -0
  156. package/daemon/dist/domain/review/brief-spine.d.ts.map +1 -0
  157. package/daemon/dist/domain/review/brief-spine.js +86 -0
  158. package/daemon/dist/domain/review/brief-spine.js.map +1 -0
  159. package/daemon/dist/domain/review/compose.d.ts +277 -0
  160. package/daemon/dist/domain/review/compose.d.ts.map +1 -0
  161. package/daemon/dist/domain/review/compose.js +862 -0
  162. package/daemon/dist/domain/review/compose.js.map +1 -0
  163. package/daemon/dist/domain/review/fleet-compose.d.ts +52 -0
  164. package/daemon/dist/domain/review/fleet-compose.d.ts.map +1 -0
  165. package/daemon/dist/domain/review/fleet-compose.js +294 -0
  166. package/daemon/dist/domain/review/fleet-compose.js.map +1 -0
  167. package/daemon/dist/domain/review/freeze.d.ts +31 -0
  168. package/daemon/dist/domain/review/freeze.d.ts.map +1 -0
  169. package/daemon/dist/domain/review/freeze.js +257 -0
  170. package/daemon/dist/domain/review/freeze.js.map +1 -0
  171. package/daemon/dist/domain/review/gather.d.ts +115 -0
  172. package/daemon/dist/domain/review/gather.d.ts.map +1 -0
  173. package/daemon/dist/domain/review/gather.js +812 -0
  174. package/daemon/dist/domain/review/gather.js.map +1 -0
  175. package/daemon/dist/domain/review/types.d.ts +361 -0
  176. package/daemon/dist/domain/review/types.d.ts.map +1 -0
  177. package/daemon/dist/domain/review/types.js +35 -0
  178. package/daemon/dist/domain/review/types.js.map +1 -0
  179. package/daemon/dist/domain/rig-expansion-service.d.ts.map +1 -1
  180. package/daemon/dist/domain/rig-expansion-service.js +3 -0
  181. package/daemon/dist/domain/rig-expansion-service.js.map +1 -1
  182. package/daemon/dist/domain/rig-repository.d.ts +7 -6
  183. package/daemon/dist/domain/rig-repository.d.ts.map +1 -1
  184. package/daemon/dist/domain/rig-repository.js +33 -11
  185. package/daemon/dist/domain/rig-repository.js.map +1 -1
  186. package/daemon/dist/domain/rigspec-codec.d.ts.map +1 -1
  187. package/daemon/dist/domain/rigspec-codec.js +3 -0
  188. package/daemon/dist/domain/rigspec-codec.js.map +1 -1
  189. package/daemon/dist/domain/rigspec-exporter.d.ts.map +1 -1
  190. package/daemon/dist/domain/rigspec-exporter.js +4 -0
  191. package/daemon/dist/domain/rigspec-exporter.js.map +1 -1
  192. package/daemon/dist/domain/rigspec-instantiator.d.ts.map +1 -1
  193. package/daemon/dist/domain/rigspec-instantiator.js +11 -0
  194. package/daemon/dist/domain/rigspec-instantiator.js.map +1 -1
  195. package/daemon/dist/domain/rigspec-preflight.d.ts +6 -0
  196. package/daemon/dist/domain/rigspec-preflight.d.ts.map +1 -1
  197. package/daemon/dist/domain/rigspec-preflight.js +26 -1
  198. package/daemon/dist/domain/rigspec-preflight.js.map +1 -1
  199. package/daemon/dist/domain/rigspec-schema.d.ts.map +1 -1
  200. package/daemon/dist/domain/rigspec-schema.js +20 -1
  201. package/daemon/dist/domain/rigspec-schema.js.map +1 -1
  202. package/daemon/dist/domain/runtime-verifier.d.ts +7 -1
  203. package/daemon/dist/domain/runtime-verifier.d.ts.map +1 -1
  204. package/daemon/dist/domain/runtime-verifier.js +39 -1
  205. package/daemon/dist/domain/runtime-verifier.js.map +1 -1
  206. package/daemon/dist/domain/scope/scope-approve.d.ts +64 -0
  207. package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -0
  208. package/daemon/dist/domain/scope/scope-approve.js +153 -0
  209. package/daemon/dist/domain/scope/scope-approve.js.map +1 -0
  210. package/daemon/dist/domain/scope/scope-audit.d.ts +9 -1
  211. package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -1
  212. package/daemon/dist/domain/scope/scope-audit.js +173 -1
  213. package/daemon/dist/domain/scope/scope-audit.js.map +1 -1
  214. package/daemon/dist/domain/seat-handover-service.d.ts +9 -0
  215. package/daemon/dist/domain/seat-handover-service.d.ts.map +1 -1
  216. package/daemon/dist/domain/seat-handover-service.js +2 -0
  217. package/daemon/dist/domain/seat-handover-service.js.map +1 -1
  218. package/daemon/dist/domain/seat-identity-store.d.ts +7 -0
  219. package/daemon/dist/domain/seat-identity-store.d.ts.map +1 -1
  220. package/daemon/dist/domain/seat-identity-store.js +27 -0
  221. package/daemon/dist/domain/seat-identity-store.js.map +1 -1
  222. package/daemon/dist/domain/seat-status-service.d.ts.map +1 -1
  223. package/daemon/dist/domain/seat-status-service.js +6 -4
  224. package/daemon/dist/domain/seat-status-service.js.map +1 -1
  225. package/daemon/dist/domain/session-name.d.ts +22 -0
  226. package/daemon/dist/domain/session-name.d.ts.map +1 -1
  227. package/daemon/dist/domain/session-name.js +29 -0
  228. package/daemon/dist/domain/session-name.js.map +1 -1
  229. package/daemon/dist/domain/slices/slice-detail-projector.d.ts.map +1 -1
  230. package/daemon/dist/domain/slices/slice-detail-projector.js +4 -5
  231. package/daemon/dist/domain/slices/slice-detail-projector.js.map +1 -1
  232. package/daemon/dist/domain/spec-library-workflow-scanner.d.ts +18 -2
  233. package/daemon/dist/domain/spec-library-workflow-scanner.d.ts.map +1 -1
  234. package/daemon/dist/domain/spec-library-workflow-scanner.js +23 -1
  235. package/daemon/dist/domain/spec-library-workflow-scanner.js.map +1 -1
  236. package/daemon/dist/domain/startup-proof.d.ts +10 -6
  237. package/daemon/dist/domain/startup-proof.d.ts.map +1 -1
  238. package/daemon/dist/domain/startup-proof.js +35 -7
  239. package/daemon/dist/domain/startup-proof.js.map +1 -1
  240. package/daemon/dist/domain/successor-session-launcher.d.ts +16 -0
  241. package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -1
  242. package/daemon/dist/domain/successor-session-launcher.js +13 -0
  243. package/daemon/dist/domain/successor-session-launcher.js.map +1 -1
  244. package/daemon/dist/domain/terminal/cmux-provider-adapter.d.ts +27 -0
  245. package/daemon/dist/domain/terminal/cmux-provider-adapter.d.ts.map +1 -0
  246. package/daemon/dist/domain/terminal/cmux-provider-adapter.js +67 -0
  247. package/daemon/dist/domain/terminal/cmux-provider-adapter.js.map +1 -0
  248. package/daemon/dist/domain/terminal/herdr-adapter.d.ts +77 -0
  249. package/daemon/dist/domain/terminal/herdr-adapter.d.ts.map +1 -0
  250. package/daemon/dist/domain/terminal/herdr-adapter.js +266 -0
  251. package/daemon/dist/domain/terminal/herdr-adapter.js.map +1 -0
  252. package/daemon/dist/domain/terminal/herdr-transport.d.ts +50 -0
  253. package/daemon/dist/domain/terminal/herdr-transport.d.ts.map +1 -0
  254. package/daemon/dist/domain/terminal/herdr-transport.js +145 -0
  255. package/daemon/dist/domain/terminal/herdr-transport.js.map +1 -0
  256. package/daemon/dist/domain/terminal/terminal-provider.d.ts +88 -0
  257. package/daemon/dist/domain/terminal/terminal-provider.d.ts.map +1 -0
  258. package/daemon/dist/domain/terminal/terminal-provider.js +17 -0
  259. package/daemon/dist/domain/terminal/terminal-provider.js.map +1 -0
  260. package/daemon/dist/domain/terminal/terminal-service.d.ts +60 -0
  261. package/daemon/dist/domain/terminal/terminal-service.d.ts.map +1 -0
  262. package/daemon/dist/domain/terminal/terminal-service.js +174 -0
  263. package/daemon/dist/domain/terminal/terminal-service.js.map +1 -0
  264. package/daemon/dist/domain/terminal/terminal-views-store.d.ts +77 -0
  265. package/daemon/dist/domain/terminal/terminal-views-store.d.ts.map +1 -0
  266. package/daemon/dist/domain/terminal/terminal-views-store.js +146 -0
  267. package/daemon/dist/domain/terminal/terminal-views-store.js.map +1 -0
  268. package/daemon/dist/domain/terminal/view-composer.d.ts +42 -0
  269. package/daemon/dist/domain/terminal/view-composer.d.ts.map +1 -0
  270. package/daemon/dist/domain/terminal/view-composer.js +137 -0
  271. package/daemon/dist/domain/terminal/view-composer.js.map +1 -0
  272. package/daemon/dist/domain/tmux-option-defaults.d.ts +89 -0
  273. package/daemon/dist/domain/tmux-option-defaults.d.ts.map +1 -0
  274. package/daemon/dist/domain/tmux-option-defaults.js +132 -0
  275. package/daemon/dist/domain/tmux-option-defaults.js.map +1 -0
  276. package/daemon/dist/domain/topology/multi-rig-launcher.d.ts +49 -0
  277. package/daemon/dist/domain/topology/multi-rig-launcher.d.ts.map +1 -0
  278. package/daemon/dist/domain/topology/multi-rig-launcher.js +132 -0
  279. package/daemon/dist/domain/topology/multi-rig-launcher.js.map +1 -0
  280. package/daemon/dist/domain/topology/remote-up-leaf.d.ts +29 -0
  281. package/daemon/dist/domain/topology/remote-up-leaf.d.ts.map +1 -0
  282. package/daemon/dist/domain/topology/remote-up-leaf.js +60 -0
  283. package/daemon/dist/domain/topology/remote-up-leaf.js.map +1 -0
  284. package/daemon/dist/domain/topology/topology-manifest.d.ts +34 -0
  285. package/daemon/dist/domain/topology/topology-manifest.d.ts.map +1 -0
  286. package/daemon/dist/domain/topology/topology-manifest.js +195 -0
  287. package/daemon/dist/domain/topology/topology-manifest.js.map +1 -0
  288. package/daemon/dist/domain/types.d.ts +39 -1
  289. package/daemon/dist/domain/types.d.ts.map +1 -1
  290. package/daemon/dist/domain/types.js.map +1 -1
  291. package/daemon/dist/domain/up-command-router.d.ts +1 -1
  292. package/daemon/dist/domain/up-command-router.d.ts.map +1 -1
  293. package/daemon/dist/domain/up-command-router.js +34 -2
  294. package/daemon/dist/domain/up-command-router.js.map +1 -1
  295. package/daemon/dist/domain/user-settings/settings-store.d.ts +18 -1
  296. package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
  297. package/daemon/dist/domain/user-settings/settings-store.js +201 -0
  298. package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
  299. package/daemon/dist/domain/workflow/slice-workflow-projection.d.ts.map +1 -1
  300. package/daemon/dist/domain/workflow/slice-workflow-projection.js +6 -2
  301. package/daemon/dist/domain/workflow/slice-workflow-projection.js.map +1 -1
  302. package/daemon/dist/domain/workflow-boot-sweep.d.ts +28 -0
  303. package/daemon/dist/domain/workflow-boot-sweep.d.ts.map +1 -0
  304. package/daemon/dist/domain/workflow-boot-sweep.js +102 -0
  305. package/daemon/dist/domain/workflow-boot-sweep.js.map +1 -0
  306. package/daemon/dist/domain/workflow-deadline.d.ts +74 -0
  307. package/daemon/dist/domain/workflow-deadline.d.ts.map +1 -0
  308. package/daemon/dist/domain/workflow-deadline.js +165 -0
  309. package/daemon/dist/domain/workflow-deadline.js.map +1 -0
  310. package/daemon/dist/domain/workflow-exception-escalation.d.ts +33 -0
  311. package/daemon/dist/domain/workflow-exception-escalation.d.ts.map +1 -0
  312. package/daemon/dist/domain/workflow-exception-escalation.js +104 -0
  313. package/daemon/dist/domain/workflow-exception-escalation.js.map +1 -0
  314. package/daemon/dist/domain/workflow-exception-router.d.ts +33 -0
  315. package/daemon/dist/domain/workflow-exception-router.d.ts.map +1 -0
  316. package/daemon/dist/domain/workflow-exception-router.js +96 -0
  317. package/daemon/dist/domain/workflow-exception-router.js.map +1 -0
  318. package/daemon/dist/domain/workflow-exception.d.ts +90 -0
  319. package/daemon/dist/domain/workflow-exception.d.ts.map +1 -0
  320. package/daemon/dist/domain/workflow-exception.js +122 -0
  321. package/daemon/dist/domain/workflow-exception.js.map +1 -0
  322. package/daemon/dist/domain/workflow-frontier-guard.d.ts +24 -0
  323. package/daemon/dist/domain/workflow-frontier-guard.d.ts.map +1 -0
  324. package/daemon/dist/domain/workflow-frontier-guard.js +30 -0
  325. package/daemon/dist/domain/workflow-frontier-guard.js.map +1 -0
  326. package/daemon/dist/domain/workflow-instance-store.d.ts +28 -0
  327. package/daemon/dist/domain/workflow-instance-store.d.ts.map +1 -1
  328. package/daemon/dist/domain/workflow-instance-store.js +66 -10
  329. package/daemon/dist/domain/workflow-instance-store.js.map +1 -1
  330. package/daemon/dist/domain/workflow-keepalive-arming.d.ts +35 -0
  331. package/daemon/dist/domain/workflow-keepalive-arming.d.ts.map +1 -0
  332. package/daemon/dist/domain/workflow-keepalive-arming.js +87 -0
  333. package/daemon/dist/domain/workflow-keepalive-arming.js.map +1 -0
  334. package/daemon/dist/domain/workflow-projector.d.ts +117 -2
  335. package/daemon/dist/domain/workflow-projector.d.ts.map +1 -1
  336. package/daemon/dist/domain/workflow-projector.js +672 -41
  337. package/daemon/dist/domain/workflow-projector.js.map +1 -1
  338. package/daemon/dist/domain/workflow-role-context.d.ts +57 -0
  339. package/daemon/dist/domain/workflow-role-context.d.ts.map +1 -0
  340. package/daemon/dist/domain/workflow-role-context.js +120 -0
  341. package/daemon/dist/domain/workflow-role-context.js.map +1 -0
  342. package/daemon/dist/domain/workflow-role-resolver.d.ts +65 -0
  343. package/daemon/dist/domain/workflow-role-resolver.d.ts.map +1 -0
  344. package/daemon/dist/domain/workflow-role-resolver.js +108 -0
  345. package/daemon/dist/domain/workflow-role-resolver.js.map +1 -0
  346. package/daemon/dist/domain/workflow-runtime.d.ts +157 -1
  347. package/daemon/dist/domain/workflow-runtime.d.ts.map +1 -1
  348. package/daemon/dist/domain/workflow-runtime.js +718 -11
  349. package/daemon/dist/domain/workflow-runtime.js.map +1 -1
  350. package/daemon/dist/domain/workflow-spec-cache.d.ts +26 -1
  351. package/daemon/dist/domain/workflow-spec-cache.d.ts.map +1 -1
  352. package/daemon/dist/domain/workflow-spec-cache.js +327 -13
  353. package/daemon/dist/domain/workflow-spec-cache.js.map +1 -1
  354. package/daemon/dist/domain/workflow-types.d.ts +143 -8
  355. package/daemon/dist/domain/workflow-types.d.ts.map +1 -1
  356. package/daemon/dist/domain/workflow-types.js +14 -1
  357. package/daemon/dist/domain/workflow-types.js.map +1 -1
  358. package/daemon/dist/domain/workflow-validator.d.ts +11 -1
  359. package/daemon/dist/domain/workflow-validator.d.ts.map +1 -1
  360. package/daemon/dist/domain/workflow-validator.js +246 -1
  361. package/daemon/dist/domain/workflow-validator.js.map +1 -1
  362. package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
  363. package/daemon/dist/domain/workspace/default-workspace-scaffold.js +51 -6
  364. package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
  365. package/daemon/dist/domain/workspace/workspace-doctor.d.ts +11 -0
  366. package/daemon/dist/domain/workspace/workspace-doctor.d.ts.map +1 -1
  367. package/daemon/dist/domain/workspace/workspace-doctor.js +82 -0
  368. package/daemon/dist/domain/workspace/workspace-doctor.js.map +1 -1
  369. package/daemon/dist/index.d.ts +17 -0
  370. package/daemon/dist/index.d.ts.map +1 -1
  371. package/daemon/dist/index.js +44 -0
  372. package/daemon/dist/index.js.map +1 -1
  373. package/daemon/dist/routes/activity.js +22 -0
  374. package/daemon/dist/routes/activity.js.map +1 -1
  375. package/daemon/dist/routes/config.d.ts.map +1 -1
  376. package/daemon/dist/routes/config.js +15 -6
  377. package/daemon/dist/routes/config.js.map +1 -1
  378. package/daemon/dist/routes/files.d.ts.map +1 -1
  379. package/daemon/dist/routes/files.js +45 -1
  380. package/daemon/dist/routes/files.js.map +1 -1
  381. package/daemon/dist/routes/hosts.d.ts +5 -0
  382. package/daemon/dist/routes/hosts.d.ts.map +1 -0
  383. package/daemon/dist/routes/hosts.js +346 -0
  384. package/daemon/dist/routes/hosts.js.map +1 -0
  385. package/daemon/dist/routes/mission-control.d.ts.map +1 -1
  386. package/daemon/dist/routes/mission-control.js +67 -2
  387. package/daemon/dist/routes/mission-control.js.map +1 -1
  388. package/daemon/dist/routes/queue.d.ts +10 -9
  389. package/daemon/dist/routes/queue.d.ts.map +1 -1
  390. package/daemon/dist/routes/queue.js +305 -10
  391. package/daemon/dist/routes/queue.js.map +1 -1
  392. package/daemon/dist/routes/review.d.ts +3 -0
  393. package/daemon/dist/routes/review.d.ts.map +1 -0
  394. package/daemon/dist/routes/review.js +195 -0
  395. package/daemon/dist/routes/review.js.map +1 -0
  396. package/daemon/dist/routes/scope-approve.d.ts +3 -0
  397. package/daemon/dist/routes/scope-approve.d.ts.map +1 -0
  398. package/daemon/dist/routes/scope-approve.js +55 -0
  399. package/daemon/dist/routes/scope-approve.js.map +1 -0
  400. package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
  401. package/daemon/dist/routes/scope-audit.js +28 -1
  402. package/daemon/dist/routes/scope-audit.js.map +1 -1
  403. package/daemon/dist/routes/seat.js +13 -0
  404. package/daemon/dist/routes/seat.js.map +1 -1
  405. package/daemon/dist/routes/slices.d.ts.map +1 -1
  406. package/daemon/dist/routes/slices.js +49 -9
  407. package/daemon/dist/routes/slices.js.map +1 -1
  408. package/daemon/dist/routes/terminal-ws.d.ts.map +1 -1
  409. package/daemon/dist/routes/terminal-ws.js +58 -21
  410. package/daemon/dist/routes/terminal-ws.js.map +1 -1
  411. package/daemon/dist/routes/terminal.d.ts +11 -0
  412. package/daemon/dist/routes/terminal.d.ts.map +1 -0
  413. package/daemon/dist/routes/terminal.js +104 -0
  414. package/daemon/dist/routes/terminal.js.map +1 -0
  415. package/daemon/dist/routes/up.d.ts.map +1 -1
  416. package/daemon/dist/routes/up.js +84 -0
  417. package/daemon/dist/routes/up.js.map +1 -1
  418. package/daemon/dist/routes/workflow.d.ts.map +1 -1
  419. package/daemon/dist/routes/workflow.js +102 -10
  420. package/daemon/dist/routes/workflow.js.map +1 -1
  421. package/daemon/dist/routes/workspace.js +2 -2
  422. package/daemon/dist/server.d.ts +6 -0
  423. package/daemon/dist/server.d.ts.map +1 -1
  424. package/daemon/dist/server.js +36 -1
  425. package/daemon/dist/server.js.map +1 -1
  426. package/daemon/dist/startup.d.ts.map +1 -1
  427. package/daemon/dist/startup.js +238 -14
  428. package/daemon/dist/startup.js.map +1 -1
  429. package/daemon/docs/reference/agent-startup-guide.md +1 -1
  430. package/daemon/docs/reference/product-factory-vps-runbook.md +132 -0
  431. package/daemon/docs/reference/sdlc-conventions.md +187 -0
  432. package/daemon/specs/agents/conveyor/builder/agent.yaml +1 -1
  433. package/daemon/specs/agents/conveyor/builder/guidance/role.md +1 -0
  434. package/daemon/specs/agents/conveyor/lead/agent.yaml +1 -1
  435. package/daemon/specs/agents/conveyor/lead/guidance/role.md +1 -0
  436. package/daemon/specs/agents/conveyor/planner/agent.yaml +1 -1
  437. package/daemon/specs/agents/conveyor/planner/guidance/role.md +1 -0
  438. package/daemon/specs/agents/conveyor/reviewer/agent.yaml +1 -1
  439. package/daemon/specs/agents/conveyor/reviewer/guidance/role.md +1 -0
  440. package/daemon/specs/agents/design/product-designer/agent.yaml +1 -1
  441. package/daemon/specs/agents/design/product-designer/guidance/role.md +1 -0
  442. package/daemon/specs/agents/development/implementer/agent.yaml +1 -1
  443. package/daemon/specs/agents/development/implementer/guidance/role.md +1 -0
  444. package/daemon/specs/agents/development/qa/agent.yaml +1 -1
  445. package/daemon/specs/agents/development/qa/guidance/role.md +1 -0
  446. package/daemon/specs/agents/factory-rsi/dogfood/agent.yaml +29 -0
  447. package/daemon/specs/agents/factory-rsi/dogfood/guidance/role.md +37 -0
  448. package/daemon/specs/agents/factory-rsi/release-manager/agent.yaml +29 -0
  449. package/daemon/specs/agents/factory-rsi/release-manager/guidance/role.md +40 -0
  450. package/daemon/specs/agents/orchestration/orchestrator/agent.yaml +1 -1
  451. package/daemon/specs/agents/orchestration/orchestrator/guidance/role.md +1 -0
  452. package/daemon/specs/agents/product-management/pm/agent.yaml +1 -0
  453. package/daemon/specs/agents/product-management/pm/guidance/role.md +13 -0
  454. package/daemon/specs/agents/review/independent-reviewer/agent.yaml +1 -1
  455. package/daemon/specs/agents/review/independent-reviewer/guidance/role.md +1 -0
  456. package/daemon/specs/agents/shared/agent.yaml +2 -0
  457. package/daemon/specs/agents/shared/skills/core/mission-slice-sop/SKILL.md +113 -0
  458. package/daemon/specs/agents/shared/skills/core/openrig-cmux/SKILL.md +86 -0
  459. package/daemon/specs/agents/shared/skills/core/openrig-herdr/SKILL.md +158 -0
  460. package/daemon/specs/agents/shared/skills/core/openrig-operator/SKILL.md +4 -4
  461. package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +75 -29
  462. package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +2 -2
  463. package/daemon/specs/rigs/focused/pm-team/CULTURE.md +4 -0
  464. package/daemon/specs/rigs/launch/factory-rsi/CULTURE.md +47 -0
  465. package/daemon/specs/rigs/launch/factory-rsi/rig.yaml +115 -0
  466. package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +1 -1
  467. package/daemon/specs/rigs/launch/kernel/agents/operator/agent/startup/context.md +1 -1
  468. package/daemon/specs/rigs/preview/product-team/CULTURE.md +4 -0
  469. package/dist/build-info.d.ts +8 -0
  470. package/dist/build-info.d.ts.map +1 -0
  471. package/dist/build-info.js +11 -0
  472. package/dist/build-info.js.map +1 -0
  473. package/dist/commands/broadcast.d.ts +6 -1
  474. package/dist/commands/broadcast.d.ts.map +1 -1
  475. package/dist/commands/broadcast.js +101 -15
  476. package/dist/commands/broadcast.js.map +1 -1
  477. package/dist/commands/capture.d.ts.map +1 -1
  478. package/dist/commands/capture.js +99 -8
  479. package/dist/commands/capture.js.map +1 -1
  480. package/dist/commands/config-init-workspace.d.ts.map +1 -1
  481. package/dist/commands/config-init-workspace.js +20 -4
  482. package/dist/commands/config-init-workspace.js.map +1 -1
  483. package/dist/commands/config.d.ts.map +1 -1
  484. package/dist/commands/config.js +4 -0
  485. package/dist/commands/config.js.map +1 -1
  486. package/dist/commands/doctor.js +2 -2
  487. package/dist/commands/doctor.js.map +1 -1
  488. package/dist/commands/down.d.ts.map +1 -1
  489. package/dist/commands/down.js +5 -0
  490. package/dist/commands/down.js.map +1 -1
  491. package/dist/commands/expand.js +1 -1
  492. package/dist/commands/expand.js.map +1 -1
  493. package/dist/commands/file.d.ts +7 -0
  494. package/dist/commands/file.d.ts.map +1 -0
  495. package/dist/commands/file.js +77 -0
  496. package/dist/commands/file.js.map +1 -0
  497. package/dist/commands/host.d.ts +48 -0
  498. package/dist/commands/host.d.ts.map +1 -0
  499. package/dist/commands/host.js +772 -0
  500. package/dist/commands/host.js.map +1 -0
  501. package/dist/commands/launch.d.ts.map +1 -1
  502. package/dist/commands/launch.js +5 -0
  503. package/dist/commands/launch.js.map +1 -1
  504. package/dist/commands/proof.d.ts +54 -0
  505. package/dist/commands/proof.d.ts.map +1 -0
  506. package/dist/commands/proof.js +311 -0
  507. package/dist/commands/proof.js.map +1 -0
  508. package/dist/commands/ps.d.ts +41 -7
  509. package/dist/commands/ps.d.ts.map +1 -1
  510. package/dist/commands/ps.js +353 -135
  511. package/dist/commands/ps.js.map +1 -1
  512. package/dist/commands/queue.d.ts +36 -0
  513. package/dist/commands/queue.d.ts.map +1 -1
  514. package/dist/commands/queue.js +149 -7
  515. package/dist/commands/queue.js.map +1 -1
  516. package/dist/commands/restore-packet.d.ts.map +1 -1
  517. package/dist/commands/restore-packet.js +13 -2
  518. package/dist/commands/restore-packet.js.map +1 -1
  519. package/dist/commands/scope.d.ts.map +1 -1
  520. package/dist/commands/scope.js +140 -3
  521. package/dist/commands/scope.js.map +1 -1
  522. package/dist/commands/send.d.ts.map +1 -1
  523. package/dist/commands/send.js +127 -13
  524. package/dist/commands/send.js.map +1 -1
  525. package/dist/commands/terminal.d.ts +6 -0
  526. package/dist/commands/terminal.d.ts.map +1 -0
  527. package/dist/commands/terminal.js +116 -0
  528. package/dist/commands/terminal.js.map +1 -0
  529. package/dist/commands/transcript.d.ts +6 -1
  530. package/dist/commands/transcript.d.ts.map +1 -1
  531. package/dist/commands/transcript.js +113 -30
  532. package/dist/commands/transcript.js.map +1 -1
  533. package/dist/commands/up.d.ts +2 -0
  534. package/dist/commands/up.d.ts.map +1 -1
  535. package/dist/commands/up.js +78 -2
  536. package/dist/commands/up.js.map +1 -1
  537. package/dist/commands/whoami.d.ts.map +1 -1
  538. package/dist/commands/whoami.js +11 -0
  539. package/dist/commands/whoami.js.map +1 -1
  540. package/dist/commands/workflow-errors.d.ts +23 -0
  541. package/dist/commands/workflow-errors.d.ts.map +1 -0
  542. package/dist/commands/workflow-errors.js +100 -0
  543. package/dist/commands/workflow-errors.js.map +1 -0
  544. package/dist/commands/workflow-follow.d.ts +71 -0
  545. package/dist/commands/workflow-follow.d.ts.map +1 -0
  546. package/dist/commands/workflow-follow.js +259 -0
  547. package/dist/commands/workflow-follow.js.map +1 -0
  548. package/dist/commands/workflow-render.d.ts +114 -0
  549. package/dist/commands/workflow-render.d.ts.map +1 -0
  550. package/dist/commands/workflow-render.js +223 -0
  551. package/dist/commands/workflow-render.js.map +1 -0
  552. package/dist/commands/workflow.d.ts +1 -0
  553. package/dist/commands/workflow.d.ts.map +1 -1
  554. package/dist/commands/workflow.js +278 -14
  555. package/dist/commands/workflow.js.map +1 -1
  556. package/dist/commands/workspace.js +6 -6
  557. package/dist/commands/workspace.js.map +1 -1
  558. package/dist/config-store.d.ts +19 -1
  559. package/dist/config-store.d.ts.map +1 -1
  560. package/dist/config-store.js +171 -0
  561. package/dist/config-store.js.map +1 -1
  562. package/dist/cross-host-cli-helpers.d.ts +13 -0
  563. package/dist/cross-host-cli-helpers.d.ts.map +1 -1
  564. package/dist/cross-host-cli-helpers.js +27 -0
  565. package/dist/cross-host-cli-helpers.js.map +1 -1
  566. package/dist/cross-host-executor.d.ts +1 -0
  567. package/dist/cross-host-executor.d.ts.map +1 -1
  568. package/dist/cross-host-executor.js +4 -1
  569. package/dist/cross-host-executor.js.map +1 -1
  570. package/dist/cross-host-target.d.ts +49 -0
  571. package/dist/cross-host-target.d.ts.map +1 -0
  572. package/dist/cross-host-target.js +35 -0
  573. package/dist/cross-host-target.js.map +1 -0
  574. package/dist/daemon-lifecycle.d.ts.map +1 -1
  575. package/dist/daemon-lifecycle.js +4 -1
  576. package/dist/daemon-lifecycle.js.map +1 -1
  577. package/dist/host-registry.d.ts +17 -0
  578. package/dist/host-registry.d.ts.map +1 -1
  579. package/dist/host-registry.js +64 -2
  580. package/dist/host-registry.js.map +1 -1
  581. package/dist/host-selection.d.ts +13 -0
  582. package/dist/host-selection.d.ts.map +1 -0
  583. package/dist/host-selection.js +54 -0
  584. package/dist/host-selection.js.map +1 -0
  585. package/dist/index.d.ts +2 -0
  586. package/dist/index.d.ts.map +1 -1
  587. package/dist/index.js +9 -0
  588. package/dist/index.js.map +1 -1
  589. package/dist/lib/file-transfer.d.ts +89 -0
  590. package/dist/lib/file-transfer.d.ts.map +1 -0
  591. package/dist/lib/file-transfer.js +300 -0
  592. package/dist/lib/file-transfer.js.map +1 -0
  593. package/dist/lib/hosts/fanout-contract.d.ts +33 -0
  594. package/dist/lib/hosts/fanout-contract.d.ts.map +1 -0
  595. package/dist/lib/hosts/fanout-contract.js +23 -0
  596. package/dist/lib/hosts/fanout-contract.js.map +1 -0
  597. package/dist/lib/scope/scope-audit.d.ts +9 -1
  598. package/dist/lib/scope/scope-audit.d.ts.map +1 -1
  599. package/dist/lib/scope/scope-audit.js +173 -1
  600. package/dist/lib/scope/scope-audit.js.map +1 -1
  601. package/dist/lib/scope/templates.d.ts +1 -0
  602. package/dist/lib/scope/templates.d.ts.map +1 -1
  603. package/dist/lib/scope/templates.js +9 -0
  604. package/dist/lib/scope/templates.js.map +1 -1
  605. package/dist/lib/scope-templates/backlog-deprecation.md +16 -0
  606. package/dist/lib/scope-templates/backlog-tech-debt.md +16 -0
  607. package/dist/lib/scope-templates/bug-fix.md +16 -0
  608. package/dist/lib/scope-templates/implementation-prd.md +33 -0
  609. package/dist/lib/scope-templates/mission-placeholder.md +1 -1
  610. package/dist/lib/scope-templates/mission-progress.md +1 -1
  611. package/dist/lib/scope-templates/mission-release.md +1 -1
  612. package/dist/lib/scope-templates/placeholder.md +11 -7
  613. package/dist/lib/scope-templates/proof.md +5 -1
  614. package/dist/lib/scope-templates/release-feature.md +14 -6
  615. package/dist/lib/scope-templates/research.md +16 -0
  616. package/dist/lib/scope-templates/slice-progress.md +1 -1
  617. package/dist/remote-host-ops.d.ts +1 -0
  618. package/dist/remote-host-ops.d.ts.map +1 -1
  619. package/dist/remote-host-ops.js +11 -4
  620. package/dist/remote-host-ops.js.map +1 -1
  621. package/dist/session-name.d.ts +23 -0
  622. package/dist/session-name.d.ts.map +1 -0
  623. package/dist/session-name.js +61 -0
  624. package/dist/session-name.js.map +1 -0
  625. package/dist/version.d.ts +5 -0
  626. package/dist/version.d.ts.map +1 -1
  627. package/dist/version.js +11 -1
  628. package/dist/version.js.map +1 -1
  629. package/package.json +1 -1
  630. package/ui/dist/assets/index-CFqxa0zW.css +32 -0
  631. package/ui/dist/assets/index-zrFqS7gL.js +623 -0
  632. package/ui/dist/index.html +2 -2
  633. package/ui/dist/assets/index-DYZniQcc.js +0 -597
  634. package/ui/dist/assets/index-trcb4Yf_.css +0 -32
@@ -0,0 +1,772 @@
1
+ // OPR.0.4.4.13 — the host-registry verbs (add / list / doctor — arch
2
+ // R13-2a: no edit/remove/tunnel/bootstrap verbs; hand-editing hosts.yaml
3
+ // remains the path for exotica; the bootstrap ships as script + runbook).
4
+ // OPR.0.4.6.MH1 FR-1 extends the family with `select` — the persisted
5
+ // host-selection pointer (kubectl current-context shape). The pointer
6
+ // lives in the config twins as `host.selected`; this verb is a THIN
7
+ // CLIENT of the daemon config write (one write path for CLI+UI
8
+ // convergence, arch ruling); reads resolve from the local ConfigStore
9
+ // (env > file > default — zero new daemon lookups on read paths).
10
+ //
11
+ // Secret hygiene (FR-1): every render carries bearer POINTERS (env var /
12
+ // file NAMES) only — no code path in this file resolves a bearer value
13
+ // for display.
14
+ import { Command } from "commander";
15
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { hostname as osHostname, userInfo } from "node:os";
18
+ import { connect } from "node:net";
19
+ import { getOpenRigHome } from "../openrig-compat.js";
20
+ import { addHostEntry, defaultHostRegistryPath, loadHostRegistry, validateHostRegistry, hostDisplayTarget, resolveHost, resolveRemoteBearer } from "../host-registry.js";
21
+ import { runCrossHostCommand } from "../cross-host-executor.js";
22
+ import { DaemonClient } from "../client.js";
23
+ import { readOwnHostName, readSelectedHost } from "../host-selection.js";
24
+ function nestedBoolean(obj, path) {
25
+ let cur = obj;
26
+ for (const part of path) {
27
+ if (typeof cur !== "object" || cur === null)
28
+ return undefined;
29
+ cur = cur[part];
30
+ }
31
+ return typeof cur === "boolean" ? cur : undefined;
32
+ }
33
+ function tailscaleRunSshFromPrefs(text) {
34
+ if (!text.trim().startsWith("{"))
35
+ return undefined;
36
+ const parsed = JSON.parse(text);
37
+ return nestedBoolean(parsed, ["RunSSH"])
38
+ ?? nestedBoolean(parsed, ["Prefs", "RunSSH"])
39
+ ?? nestedBoolean(parsed, ["CurrentProfile", "RunSSH"]);
40
+ }
41
+ function defaultDoctorDeps() {
42
+ return {
43
+ run: (host, argv) => runCrossHostCommand(host, argv),
44
+ httpGet: async (url, headers) => {
45
+ const res = await fetch(url, { headers, signal: AbortSignal.timeout(10_000) });
46
+ return { status: res.status, body: await res.text() };
47
+ },
48
+ httpPost: async (url, body) => {
49
+ const res = await fetch(url, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify(body),
53
+ signal: AbortSignal.timeout(10_000),
54
+ });
55
+ return { status: res.status, body: await res.text() };
56
+ },
57
+ tcpProbe: (target, port, timeoutMs) => new Promise((resolve) => {
58
+ const sock = connect({ host: target, port, timeout: timeoutMs });
59
+ sock.on("connect", () => { sock.destroy(); resolve("open"); });
60
+ sock.on("timeout", () => { sock.destroy(); resolve("closed"); });
61
+ sock.on("error", () => { sock.destroy(); resolve("closed"); });
62
+ }),
63
+ };
64
+ }
65
+ /** FR-1 doctor legs — each failing step maps to a DISTINCT actionable error:
66
+ * "SSH works but daemon is down" ≠ "daemon works but registry is wrong" ≠
67
+ * "remote rig binary missing/old". */
68
+ /** OPR.0.4.6.MH1 FR-3 — the coarse ls status word: a BOUNDED tcp dial
69
+ * against the host's transport endpoint (ssh: target:22; http: the
70
+ * URL's host:port). Coarse by design (the kubectl STATUS / docker
71
+ * context ls precedent): reachable | unreachable | unknown — never a
72
+ * hang (hard timeout), never a guess (dial failure = unreachable,
73
+ * probe error/unparseable endpoint = unknown). */
74
+ export async function probeHostStatus(host, tcpProbe, timeoutMs = 1500) {
75
+ try {
76
+ let target;
77
+ let port;
78
+ if (host.transport === "ssh") {
79
+ target = host.target;
80
+ port = 22;
81
+ }
82
+ else {
83
+ const u = new URL(host.url);
84
+ target = u.hostname;
85
+ port = u.port ? Number(u.port) : u.protocol === "https:" ? 443 : 80;
86
+ }
87
+ const r = await tcpProbe(target, port, timeoutMs);
88
+ return r === "open" ? "reachable" : r === "closed" ? "unreachable" : "unknown";
89
+ }
90
+ catch {
91
+ return "unknown";
92
+ }
93
+ }
94
+ export async function doctorLegs(host, deps) {
95
+ const rows = [];
96
+ if (host.transport === "ssh") {
97
+ const reach = await deps.run(host, ["true"]);
98
+ if (!reach.ok && (reach.failedStep === "ssh-unreachable" || reach.failedStep === "permission-gate")) {
99
+ rows.push({
100
+ step: "transport-reachability",
101
+ status: "fail",
102
+ detail: `ssh to ${host.target} failed (${reach.failedStep}): ${reach.sshStderr.trim()}`,
103
+ fix: reach.failedStep === "permission-gate"
104
+ ? "SSH auth/permission gate — check the key, agent, and remote authorized_keys"
105
+ : "verify the ssh target/alias, tailnet connectivity, and that sshd is running",
106
+ });
107
+ return rows; // later legs are meaningless without the shell
108
+ }
109
+ rows.push({ step: "transport-reachability", status: "pass", detail: `ssh to ${host.target} ok` });
110
+ const version = await deps.run(host, ["rig", "--version"]);
111
+ if (!version.ok) {
112
+ rows.push({
113
+ step: "remote-rig-binary",
114
+ status: "fail",
115
+ detail: "reached the remote shell, but `rig --version` failed — remote rig binary missing or broken",
116
+ fix: "install the published artifact on the host: npm install -g @openrig/cli (see the product-factory runbook)",
117
+ });
118
+ return rows;
119
+ }
120
+ rows.push({ step: "remote-rig-binary", status: "pass", detail: `remote rig ${version.stdout.trim() || "(version unreadable)"}` });
121
+ const daemon = await deps.run(host, ["rig", "daemon", "status"]);
122
+ const daemonUp = daemon.ok && /running/i.test(daemon.stdout);
123
+ rows.push(daemonUp
124
+ ? { step: "remote-daemon-health", status: "pass", detail: "remote daemon reports running" }
125
+ : {
126
+ step: "remote-daemon-health",
127
+ status: "fail",
128
+ detail: "SSH works and rig is installed, but the remote daemon is not running",
129
+ fix: "on the host: rig daemon start (then re-run doctor)",
130
+ });
131
+ if (!daemonUp)
132
+ return rows;
133
+ const whoami = await deps.run(host, ["rig", "ps", "--json", "--limit", "5"]);
134
+ let identityOk = false;
135
+ if (whoami.ok) {
136
+ try {
137
+ identityOk = typeof JSON.parse(whoami.stdout) === "object";
138
+ }
139
+ catch {
140
+ identityOk = false;
141
+ }
142
+ }
143
+ rows.push(identityOk
144
+ ? { step: "remote-identity", status: "pass", detail: "remote daemon identity/list resolves (rig ps --json parses)" }
145
+ : {
146
+ step: "remote-identity",
147
+ status: "fail",
148
+ detail: "daemon is up but remote identity/list did not resolve",
149
+ fix: "on the host: check `rig ps --json` directly; the daemon API may be unhealthy even though status says running",
150
+ });
151
+ return rows;
152
+ }
153
+ // http transport
154
+ const bearer = resolveRemoteBearer(host);
155
+ if (!bearer.ok) {
156
+ rows.push({ step: "transport-reachability", status: "fail", detail: bearer.error, fix: "set the bearer env var / file the registry entry points at (pointer names: rig host list)" });
157
+ return rows;
158
+ }
159
+ try {
160
+ const health = await deps.httpGet(`${host.url}/healthz`, { Authorization: `Bearer ${bearer.token}` });
161
+ if (health.status === 401 || health.status === 403) {
162
+ rows.push({ step: "transport-reachability", status: "fail", detail: `daemon reachable but rejected the bearer (HTTP ${health.status})`, fix: "registry is reachable but the token is wrong — rotate/set the bearer the entry points at" });
163
+ return rows;
164
+ }
165
+ if (health.status < 200 || health.status >= 300) {
166
+ rows.push({ step: "transport-reachability", status: "fail", detail: `healthz returned HTTP ${health.status}`, fix: "the daemon answered abnormally — check remote daemon logs" });
167
+ return rows;
168
+ }
169
+ rows.push({ step: "transport-reachability", status: "pass", detail: `healthz ok at ${host.url}` });
170
+ rows.push({ step: "remote-daemon-health", status: "pass", detail: "healthz IS the daemon health check on http transport" });
171
+ }
172
+ catch (err) {
173
+ rows.push({ step: "transport-reachability", status: "fail", detail: `daemon unreachable at ${host.url}: ${err.message}`, fix: "check tailnet connectivity and that the remote daemon is running (rig daemon start on the host)" });
174
+ return rows;
175
+ }
176
+ rows.push({
177
+ step: "remote-rig-binary",
178
+ status: "unknown",
179
+ detail: "not determinable over http transport (no shell)",
180
+ fix: "verify via an ssh-transport entry for this host, or on the host directly: rig --version",
181
+ });
182
+ try {
183
+ const ps = await deps.httpGet(`${host.url}/api/ps`, { Authorization: `Bearer ${bearer.token}` });
184
+ rows.push(ps.status >= 200 && ps.status < 300
185
+ ? { step: "remote-identity", status: "pass", detail: "authenticated daemon API answers (/api/ps)" }
186
+ : { step: "remote-identity", status: "fail", detail: `/api/ps returned HTTP ${ps.status}`, fix: "daemon healthz is up but the API is unhealthy — check remote daemon logs" });
187
+ }
188
+ catch (err) {
189
+ rows.push({ step: "remote-identity", status: "fail", detail: `/api/ps unreachable: ${err.message}`, fix: "check remote daemon logs" });
190
+ }
191
+ return rows;
192
+ }
193
+ /** FR-2 — THE one built-in posture profile (arch R13-2b: a named constant
194
+ * table, no framework). Every item is three-valued; UNKNOWN is never
195
+ * smoothed to pass, and every non-pass carries its fix. */
196
+ export const POSTURE_PROFILE_ID = "product-factory-vps";
197
+ export async function postureCheck(host, deps, opts = {}) {
198
+ if (host.transport !== "ssh") {
199
+ // Posture needs a shell. Report every item unknown — honestly.
200
+ return POSTURE_ITEM_IDS.map((id) => ({
201
+ step: id,
202
+ status: "unknown",
203
+ detail: "posture checks need shell access; this host is registered with http transport",
204
+ fix: "register an ssh-transport entry for this host (or run the checks on the host per the runbook)",
205
+ }));
206
+ }
207
+ const rows = [];
208
+ const sh = async (cmd) => deps.run(host, ["sh", "-c", cmd]);
209
+ // 1. non-root openrig user
210
+ const idOut = await sh("id -u openrig 2>/dev/null");
211
+ if (idOut.ok && idOut.stdout.trim() !== "") {
212
+ rows.push(idOut.stdout.trim() === "0"
213
+ ? { step: "nonroot-openrig-user", status: "fail", detail: "user 'openrig' resolves to uid 0", fix: "create a non-root openrig user per the runbook" }
214
+ : { step: "nonroot-openrig-user", status: "pass", detail: `user 'openrig' uid ${idOut.stdout.trim()}` });
215
+ }
216
+ else {
217
+ rows.push({ step: "nonroot-openrig-user", status: "fail", detail: "user 'openrig' does not exist", fix: "adduser openrig + key-only SSH per the runbook" });
218
+ }
219
+ // 2+3. sshd effective config (root SSH off, password auth off)
220
+ const sshd = await sh("sudo -n sshd -T 2>/dev/null || sshd -T 2>/dev/null || true");
221
+ const sshdOut = sshd.ok ? sshd.stdout.toLowerCase() : "";
222
+ const sshdItem = (step, key, want) => {
223
+ if (!sshdOut.includes(key)) {
224
+ return { step, status: "unknown", detail: "effective sshd config not readable from this vantage (sshd -T needs root)", fix: "on the host: sudo sshd -T | grep " + key };
225
+ }
226
+ return sshdOut.includes(`${key} ${want}`)
227
+ ? { step, status: "pass", detail: `${key} ${want}` }
228
+ : { step, status: "fail", detail: `sshd -T reports ${key} != ${want}`, fix: `set ${key} ${want} in /etc/ssh/sshd_config.d/99-openrig-hardening.conf and reload sshd` };
229
+ };
230
+ rows.push(sshdItem("root-ssh-disabled", "permitrootlogin", "no"));
231
+ rows.push(sshdItem("key-only-ssh", "passwordauthentication", "no"));
232
+ // 4+5. UFW default-deny + tailnet ingress
233
+ const ufw = await sh("sudo -n ufw status verbose 2>/dev/null || true");
234
+ const ufwOut = ufw.ok ? ufw.stdout.toLowerCase() : "";
235
+ if (ufwOut.trim() === "") {
236
+ const unknownUfw = (step) => ({ step, status: "unknown", detail: "ufw status not readable from this vantage (needs sudo)", fix: "on the host: sudo ufw status verbose" });
237
+ rows.push(unknownUfw("ufw-default-deny-incoming"));
238
+ rows.push(unknownUfw("tailnet-ingress-allowed"));
239
+ }
240
+ else {
241
+ rows.push(/default:\s*deny\s*\(incoming/.test(ufwOut)
242
+ ? { step: "ufw-default-deny-incoming", status: "pass", detail: "ufw default deny (incoming)" }
243
+ : { step: "ufw-default-deny-incoming", status: "fail", detail: "ufw incoming default is not deny", fix: "sudo ufw default deny incoming" });
244
+ // R2-B1: mentioning tailscale0 is NOT enough — a DENY on tailscale0
245
+ // must never pass. Pass requires an ALLOW IN rule on the interface.
246
+ const tailnetAllowIn = ufwOut.split("\n").some((line) => line.includes("tailscale0") && /allow\s+in/.test(line));
247
+ if (tailnetAllowIn) {
248
+ rows.push({ step: "tailnet-ingress-allowed", status: "pass", detail: "ufw has an ALLOW IN rule on tailscale0" });
249
+ }
250
+ else if (ufwOut.includes("tailscale0")) {
251
+ rows.push({ step: "tailnet-ingress-allowed", status: "fail", detail: "tailscale0 appears in ufw rules but with no ALLOW IN rule (deny/other only)", fix: "sudo ufw allow in on tailscale0" });
252
+ }
253
+ else {
254
+ rows.push({ step: "tailnet-ingress-allowed", status: "fail", detail: "no tailscale0 allow rule found", fix: "sudo ufw allow in on tailscale0" });
255
+ }
256
+ }
257
+ // 6. tailscale flags (no subnet routes / exit node / ts-SSH)
258
+ const ts = await sh("tailscale status --json 2>/dev/null || true");
259
+ const prefs = await sh("tailscale debug prefs --json 2>/dev/null || true");
260
+ let routes;
261
+ let exitNode;
262
+ let runSsh;
263
+ const unknownReasons = [];
264
+ try {
265
+ if (ts.ok && ts.stdout.trim().startsWith("{")) {
266
+ const parsed = JSON.parse(ts.stdout);
267
+ routes = parsed.Self?.PrimaryRoutes?.length ?? 0;
268
+ exitNode = (parsed.ExitNodeStatus !== undefined && parsed.ExitNodeStatus !== null) || parsed.Self?.ExitNodeOption === true;
269
+ }
270
+ else {
271
+ unknownReasons.push("tailscale status unreadable");
272
+ }
273
+ }
274
+ catch {
275
+ unknownReasons.push("tailscale status did not parse");
276
+ }
277
+ try {
278
+ runSsh = prefs.ok ? tailscaleRunSshFromPrefs(prefs.stdout) : undefined;
279
+ if (runSsh === undefined)
280
+ unknownReasons.push("tailscale debug prefs unreadable");
281
+ }
282
+ catch {
283
+ unknownReasons.push("tailscale debug prefs did not parse");
284
+ }
285
+ if ((routes ?? 0) > 0 || exitNode === true || runSsh === true) {
286
+ rows.push({
287
+ step: "tailscale-minimal-trust",
288
+ status: "fail",
289
+ detail: `subnet routes: ${routes ?? "unknown"}; exit node in use: ${exitNode ?? "unknown"}; Tailscale SSH enabled: ${runSsh ?? "unknown"}`,
290
+ fix: "remove advertised routes / exit-node use (tailscale set) and disable Tailscale SSH: tailscale set --ssh=false",
291
+ });
292
+ }
293
+ else if (routes === 0 && exitNode === false && runSsh === false) {
294
+ rows.push({ step: "tailscale-minimal-trust", status: "pass", detail: "no advertised subnet routes; no exit-node use; Tailscale SSH disabled" });
295
+ }
296
+ else {
297
+ rows.push({
298
+ step: "tailscale-minimal-trust",
299
+ status: "unknown",
300
+ detail: `tailscale posture not fully determined (${unknownReasons.join("; ")})`,
301
+ fix: "on the host: tailscale status --json && tailscale debug prefs --json; disable Tailscale SSH with tailscale set --ssh=false",
302
+ });
303
+ }
304
+ // 7. daemon bound loopback/tailnet — never public. R2-B1: a SPECIFIC
305
+ // public IP bind must fail exactly like a wildcard — pass is an
306
+ // ALLOWLIST (loopback or the tailnet CGNAT range 100.64.0.0/10), never
307
+ // "not wildcard".
308
+ const ss = await sh("ss -tln 2>/dev/null | grep ':7433' || true");
309
+ const ssOut = ss.ok ? ss.stdout : "";
310
+ const listenAddrs = ssOut
311
+ .split("\n")
312
+ .map((line) => line.match(/(\S+):7433\b/)?.[1])
313
+ .filter((a) => a !== undefined);
314
+ if (listenAddrs.length === 0) {
315
+ rows.push({ step: "daemon-bind-not-public", status: "unknown", detail: "no listener on :7433 observed (daemon may be down)", fix: "start the daemon, then re-check: ss -tln | grep 7433" });
316
+ }
317
+ else {
318
+ const isLoopbackOrTailnet = (addr) => {
319
+ const a = addr.replace(/^\[|\]$/g, "");
320
+ if (a.startsWith("127.") || a === "::1")
321
+ return true;
322
+ const m = a.match(/^100\.(\d+)\./);
323
+ return m !== null && Number(m[1]) >= 64 && Number(m[1]) <= 127;
324
+ };
325
+ const offending = listenAddrs.filter((a) => !isLoopbackOrTailnet(a));
326
+ rows.push(offending.length === 0
327
+ ? { step: "daemon-bind-not-public", status: "pass", detail: `listener(s) bound loopback/tailnet only: ${listenAddrs.join(", ")}` }
328
+ : { step: "daemon-bind-not-public", status: "fail", detail: `daemon listens on a non-loopback/non-tailnet address: ${offending.join(", ")}`, fix: "bind the daemon to loopback/tailnet only (OPENRIG daemon host config); never expose :7433 publicly" });
329
+ }
330
+ // 8+9. public reachability probes — only meaningful with a known PUBLIC address.
331
+ if (opts.publicAddr) {
332
+ for (const [step, port] of [["public-daemon-port-unreachable", 7433], ["public-ssh-unreachable-or-accepted", 22]]) {
333
+ const probe = await deps.tcpProbe(opts.publicAddr, port, 5000);
334
+ rows.push(probe === "open"
335
+ ? { step, status: "fail", detail: `${opts.publicAddr}:${port} is publicly reachable`, fix: "remove the public allow rule / firewall the port (the smoke-test hardening bar: public :22/:7433 time out)" }
336
+ : { step, status: "pass", detail: `${opts.publicAddr}:${port} not reachable (${probe})` });
337
+ }
338
+ }
339
+ else {
340
+ const unknownProbe = (step, port) => ({
341
+ step, status: "unknown",
342
+ detail: `public reachability of :${port} not probed (no public address known — the registry target may be a tailnet alias)`,
343
+ fix: `re-run with --public-addr <ip> to probe from this vantage`,
344
+ });
345
+ rows.push(unknownProbe("public-daemon-port-unreachable", 7433));
346
+ rows.push(unknownProbe("public-ssh-unreachable-or-accepted", 22));
347
+ }
348
+ return rows;
349
+ }
350
+ export const POSTURE_ITEM_IDS = [
351
+ "nonroot-openrig-user",
352
+ "root-ssh-disabled",
353
+ "key-only-ssh",
354
+ "ufw-default-deny-incoming",
355
+ "tailnet-ingress-allowed",
356
+ "tailscale-minimal-trust",
357
+ "daemon-bind-not-public",
358
+ "public-daemon-port-unreachable",
359
+ "public-ssh-unreachable-or-accepted",
360
+ ];
361
+ function renderRows(rows, json) {
362
+ if (json) {
363
+ console.log(JSON.stringify(rows));
364
+ return;
365
+ }
366
+ const glyph = { pass: "✓", fail: "✗", unknown: "?" };
367
+ for (const r of rows) {
368
+ console.log(`${glyph[r.status]} ${r.step}: ${r.detail}${r.fix ? `\n fix: ${r.fix}` : ""}`);
369
+ }
370
+ }
371
+ function authPointer(h) {
372
+ if (h.transport === "ssh")
373
+ return "ssh-key";
374
+ return h.bearer_env ? `env:${h.bearer_env}` : h.bearer_file ? `file:${h.bearer_file}` : "—";
375
+ }
376
+ export function hostCommand(doctorDepsOverride) {
377
+ const cmd = new Command("host").description("Manage the multi-host registry (~/.openrig/hosts.yaml)");
378
+ cmd
379
+ .command("add")
380
+ .description("Add a host entry (validated with the registry loader's own rules)")
381
+ .requiredOption("--id <id>", "Unique host id")
382
+ .requiredOption("--transport <transport>", "ssh or http")
383
+ .option("--target <target>", "SSH target (DNS name, ssh-config alias, or IP) — ssh transport")
384
+ .option("--user <user>", "SSH user — ssh transport")
385
+ .option("--url <url>", "Remote daemon base URL — http transport")
386
+ .option("--bearer-env <name>", "Env var NAME holding the bearer token — http transport (pointer, never a value)")
387
+ .option("--bearer-file <path>", "File PATH holding the bearer token — http transport (pointer, never a value)")
388
+ .option("--notes <text>", "Free-form operator note")
389
+ .option("--json", "JSON output")
390
+ .action((opts) => {
391
+ const rawEntry = { id: opts.id, transport: opts.transport };
392
+ if (opts.target !== undefined)
393
+ rawEntry["target"] = opts.target;
394
+ if (opts.user !== undefined)
395
+ rawEntry["user"] = opts.user;
396
+ if (opts.url !== undefined)
397
+ rawEntry["url"] = opts.url;
398
+ if (opts.bearerEnv !== undefined)
399
+ rawEntry["bearer_env"] = opts.bearerEnv;
400
+ if (opts.bearerFile !== undefined)
401
+ rawEntry["bearer_file"] = opts.bearerFile;
402
+ if (opts.notes !== undefined)
403
+ rawEntry["notes"] = opts.notes;
404
+ const res = addHostEntry(rawEntry);
405
+ if (!res.ok) {
406
+ console.error(res.error);
407
+ process.exitCode = 1;
408
+ return;
409
+ }
410
+ if (opts.json) {
411
+ console.log(JSON.stringify({ ok: true, path: res.path, entry: res.entry }));
412
+ return;
413
+ }
414
+ console.log(`Added host '${res.entry.id}' (${res.entry.transport}) to ${res.path}`);
415
+ console.log(`Verify it: rig host doctor ${res.entry.id}`);
416
+ });
417
+ cmd
418
+ .command("select")
419
+ .description("Select which host you are viewing/acting on (persisted; 'local' returns to this host)")
420
+ .argument("<id>", "A registered host id, or 'local'")
421
+ .option("--json", "JSON output")
422
+ .action(async (id, opts) => {
423
+ // Validate BEFORE persisting: 'local' is always legal; anything
424
+ // else must be a registered host id.
425
+ if (id !== "local") {
426
+ const registryPath = defaultHostRegistryPath();
427
+ const loaded = existsSync(registryPath) ? loadHostRegistry(registryPath) : null;
428
+ if (!loaded || !loaded.ok) {
429
+ console.error(loaded && !loaded.ok
430
+ ? loaded.error
431
+ : `cannot select '${id}': no hosts registered (no registry at ${registryPath}). Add one first: rig host add --id <id> --transport <ssh|http> ...`);
432
+ process.exitCode = 1;
433
+ return;
434
+ }
435
+ const ids = loaded.registry.hosts.map((h) => h.id);
436
+ if (!ids.includes(id)) {
437
+ console.error(`cannot select '${id}': not a registered host id. Registered: ${ids.length > 0 ? ids.join(", ") : "(none)"}. Add it first: rig host add --id ${id} --transport <ssh|http> ... (or select 'local').`);
438
+ process.exitCode = 1;
439
+ return;
440
+ }
441
+ }
442
+ // ONE write path: the daemon config write (the CLI is a thin
443
+ // client — arch FR-1 ruling). Daemon-down surfaces the existing
444
+ // structured connection error (trade named in the PRD).
445
+ const client = new DaemonClient();
446
+ let res;
447
+ try {
448
+ res = await client.post(`/api/config/${encodeURIComponent("host.selected")}`, { value: id });
449
+ }
450
+ catch (err) {
451
+ // Daemon down: the existing structured connection error, named
452
+ // trade in the PRD (selection writes need the local daemon up).
453
+ console.error(err.message);
454
+ process.exitCode = 1;
455
+ return;
456
+ }
457
+ if (res.status !== 200 || res.data?.error) {
458
+ console.error(res.data?.error ?? `daemon config write failed (HTTP ${res.status})`);
459
+ process.exitCode = 1;
460
+ return;
461
+ }
462
+ if (opts.json) {
463
+ console.log(JSON.stringify({ ok: true, selected: id }));
464
+ return;
465
+ }
466
+ console.log(id === "local" ? "Selected host: local (this host)" : `Selected host: ${id}`);
467
+ console.log(id === "local" ? "Commands run against this host." : `Flagless commands that support --host now run against '${id}'. Return with: rig host select local`);
468
+ });
469
+ cmd
470
+ .command("rename")
471
+ .description("Rename THIS host's display name (renders in dashboard, explorer, ls, whoami)")
472
+ .argument("<name>", 'The new display name, e.g. "Mac mini 2"')
473
+ .option("--json", "JSON output")
474
+ .action(async (name, opts) => {
475
+ // OPR.0.4.6.MH1 FR-4 — one stored name (the settings twins, arch
476
+ // Ruling 1), one write path (the daemon config write, same as
477
+ // select). The name is free text; only emptiness is rejected.
478
+ const trimmed = name.trim();
479
+ if (!trimmed) {
480
+ console.error("host name must not be empty");
481
+ process.exitCode = 1;
482
+ return;
483
+ }
484
+ const client = new DaemonClient();
485
+ let res;
486
+ try {
487
+ res = await client.post(`/api/config/${encodeURIComponent("host.name")}`, { value: trimmed });
488
+ }
489
+ catch (err) {
490
+ console.error(err.message);
491
+ process.exitCode = 1;
492
+ return;
493
+ }
494
+ if (res.status !== 200 || res.data?.error) {
495
+ console.error(res.data?.error ?? `daemon config write failed (HTTP ${res.status})`);
496
+ process.exitCode = 1;
497
+ return;
498
+ }
499
+ if (opts.json) {
500
+ console.log(JSON.stringify({ ok: true, name: trimmed }));
501
+ return;
502
+ }
503
+ console.log(`This host is now named: ${trimmed}`);
504
+ });
505
+ cmd
506
+ .command("pair")
507
+ .description("Pair with a remote host from one pasted address — one approval on the target, done (FR-6)")
508
+ .argument("<url>", "The target daemon's address (http[s]://host:port, or host:port)")
509
+ .option("--id <id>", "Registry id for the new host (default: derived from the hostname)")
510
+ .option("--timeout <seconds>", "How long to wait for the target-side approval", "600")
511
+ .option("--json", "JSON output")
512
+ .action(async (rawUrl, opts) => {
513
+ // OPR.0.4.6.MH1 FR-6 — THE founder-simple add path: one pasted
514
+ // address, one approval ON the target, done. The flag-heavy
515
+ // `rig host add` stays as the unchanged advanced path. B1: the CLI
516
+ // keeps its direct-fs write (addHostEntry); the browser surface
517
+ // pairs through its local daemon — both converge on the ONE write
518
+ // contract.
519
+ let target;
520
+ try {
521
+ target = new URL(/^https?:\/\//.test(rawUrl.trim()) ? rawUrl.trim() : `http://${rawUrl.trim()}`);
522
+ }
523
+ catch {
524
+ console.error(`'${rawUrl}' is not a usable address. Paste the target daemon's URL, e.g. http://vps-a:7433`);
525
+ process.exitCode = 1;
526
+ return;
527
+ }
528
+ const targetBase = target.origin;
529
+ const deps = doctorDepsOverride ?? defaultDoctorDeps();
530
+ // B1 fixback (guard code-review 2026-07-07): PREFLIGHT before any
531
+ // network call or credential mutation. The candidate entry runs the
532
+ // SAME validation contract the add will use (duplicate ids, reserved
533
+ // ids, invalid existing registry all fail HERE — before the target
534
+ // is asked for approval), and a pre-existing token file is
535
+ // pre-existing CREDENTIAL STATE: rejected, never overwritten, and
536
+ // never deleted by this request's cleanup.
537
+ const id = (opts.id ?? "").trim() || target.hostname.toLowerCase().replace(/[^a-z0-9.-]/g, "-").replace(/\./g, "-").replace(/^-+|-+$/g, "") || "paired-host";
538
+ const secretsDir = join(getOpenRigHome(), "secrets");
539
+ const tokenPath = join(secretsDir, `host-${id}.token`);
540
+ {
541
+ const registryPath = defaultHostRegistryPath();
542
+ let existing = [];
543
+ if (existsSync(registryPath)) {
544
+ const loaded = loadHostRegistry(registryPath);
545
+ if (!loaded.ok) {
546
+ console.error(`cannot pair: ${loaded.error}`);
547
+ process.exitCode = 1;
548
+ return;
549
+ }
550
+ existing = loaded.registry.hosts;
551
+ }
552
+ const preflight = validateHostRegistry({ hosts: [...existing, { id, transport: "http", url: targetBase, bearer_file: tokenPath }] }, registryPath);
553
+ if (!preflight.ok) {
554
+ console.error(`cannot pair as '${id}': ${preflight.error}`);
555
+ process.exitCode = 1;
556
+ return;
557
+ }
558
+ if (existsSync(tokenPath)) {
559
+ console.error(`a credential file already exists at ${tokenPath} — pre-existing credential state is never overwritten. Pass --id <different-id>, or remove the file yourself if you know it is stale.`);
560
+ process.exitCode = 1;
561
+ return;
562
+ }
563
+ }
564
+ const httpPost = deps.httpPost ?? (async (url, payload) => {
565
+ const res = await fetch(url, {
566
+ method: "POST",
567
+ headers: { "Content-Type": "application/json" },
568
+ body: JSON.stringify(payload),
569
+ signal: AbortSignal.timeout(10_000),
570
+ });
571
+ return { status: res.status, body: await res.text() };
572
+ });
573
+ const parseJson = (s) => {
574
+ try {
575
+ return JSON.parse(s);
576
+ }
577
+ catch {
578
+ return {};
579
+ }
580
+ };
581
+ let issued;
582
+ try {
583
+ const res = await httpPost(`${targetBase}/api/hosts/pair-request`, {
584
+ requester: `${userInfo().username}@${osHostname()}`,
585
+ });
586
+ issued = parseJson(res.body);
587
+ if (res.status !== 200 || !issued.pairId || !issued.code) {
588
+ console.error(issued.message ?? `pairing refused: target responded HTTP ${res.status}${issued.error ? ` (${issued.error})` : ""}`);
589
+ process.exitCode = 1;
590
+ return;
591
+ }
592
+ }
593
+ catch (err) {
594
+ console.error(`could not reach ${targetBase}: ${err.message}`);
595
+ process.exitCode = 1;
596
+ return;
597
+ }
598
+ if (!opts.json) {
599
+ console.log(`Pairing code: ${issued.code}`);
600
+ console.log(`Waiting for approval on the target (its attention queue carries item ${issued.approvalQitemId ?? "?"}).`);
601
+ console.log(`On the target: rig queue update ${issued.approvalQitemId ?? "<qitem-id>"} --state done --closure-reason no-follow-on`);
602
+ }
603
+ const timeoutMs = Math.max(1, Number(opts.timeout ?? "600") || 600) * 1000;
604
+ const deadline = Date.now() + timeoutMs;
605
+ let outcome = {};
606
+ while (Date.now() < deadline) {
607
+ await new Promise((r) => setTimeout(r, 2000));
608
+ try {
609
+ const res = await deps.httpGet(`${targetBase}/api/hosts/pair-request/${issued.pairId}`);
610
+ outcome = parseJson(res.body);
611
+ }
612
+ catch {
613
+ continue; // transient poll failure: keep waiting until the deadline
614
+ }
615
+ if (outcome.status && outcome.status !== "pending")
616
+ break;
617
+ }
618
+ if (outcome.status !== "approved" || !outcome.token) {
619
+ const why = outcome.status === "denied" ? "the target DENIED the pairing"
620
+ : outcome.status === "expired" ? "the pairing EXPIRED on the target"
621
+ : "no approval arrived before the timeout";
622
+ console.error(`pairing failed: ${why}. Nothing was persisted (no registry entry, no token file).`);
623
+ process.exitCode = 1;
624
+ return;
625
+ }
626
+ // Approved: token → EXCLUSIVE-CREATE 0600 file (open flag "wx" —
627
+ // rev1-r2 B3: check-then-rename had a window where a concurrent
628
+ // same-id pair could clobber the winner's file and then delete it
629
+ // on its own add failure; "wx" is atomic at the filesystem, so
630
+ // creation SUCCESS is the proof of ownership the cleanup relies
631
+ // on), then the registry entry via the ONE shipped write path.
632
+ // addHostEntry re-validates authoritatively (the registry may have
633
+ // changed since preflight).
634
+ try {
635
+ mkdirSync(secretsDir, { recursive: true, mode: 0o700 });
636
+ writeFileSync(tokenPath, `${outcome.token}\n`, { mode: 0o600, flag: "wx" });
637
+ }
638
+ catch (err) {
639
+ if (err.code === "EEXIST") {
640
+ console.error(`a credential file appeared at ${tokenPath} during pairing — refusing to overwrite it. Nothing was persisted by this request.`);
641
+ }
642
+ else {
643
+ console.error(`failed to store the pairing token: ${err.message}`);
644
+ }
645
+ process.exitCode = 1;
646
+ return;
647
+ }
648
+ const added = addHostEntry({
649
+ id,
650
+ transport: "http",
651
+ url: targetBase,
652
+ bearer_file: tokenPath,
653
+ notes: `paired ${new Date().toISOString().slice(0, 10)}`,
654
+ });
655
+ if (!added.ok) {
656
+ rmSync(tokenPath, { force: true });
657
+ console.error(`pairing approved but the registry write failed: ${added.error}. The token file (created by this request) was removed — nothing persisted.`);
658
+ process.exitCode = 1;
659
+ return;
660
+ }
661
+ if (opts.json) {
662
+ console.log(JSON.stringify({ ok: true, entry: added.entry }));
663
+ return;
664
+ }
665
+ console.log(`Paired. Host '${id}' registered (${targetBase}, bearer_file ${tokenPath}).`);
666
+ console.log(`Verify it: rig host doctor ${id}`);
667
+ });
668
+ cmd
669
+ .command("list")
670
+ // OPR.0.4.6.MH1 QA fixback: the PRD/proof contract spells the verb
671
+ // `rig host ls` (FR-3); `list` stays the canonical name, `ls` is the
672
+ // contract-required alias (same action, same --json).
673
+ .alias("ls")
674
+ .description("List registered hosts w/ status + selected marker (config pointers only — never secret values)")
675
+ .option("--json", "JSON output")
676
+ .action(async (opts) => {
677
+ const registryPath = defaultHostRegistryPath();
678
+ if (!existsSync(registryPath)) {
679
+ if (opts.json) {
680
+ console.log(JSON.stringify([]));
681
+ return;
682
+ }
683
+ console.log(`No hosts registered in ${registryPath}. Pair one from a single address: rig host pair <url> (advanced/manual path: rig host add --id <id> --transport <ssh|http> ...)`);
684
+ return;
685
+ }
686
+ const loaded = loadHostRegistry(registryPath);
687
+ if (!loaded.ok) {
688
+ console.error(loaded.error);
689
+ process.exitCode = 1;
690
+ return;
691
+ }
692
+ // OPR.0.4.6.MH1 FR-1/FR-3: selected marker (local ConfigStore read —
693
+ // env > file > default; zero daemon dependency) + a bounded status
694
+ // probe per host (parallel; hard per-host timeout — never a hang).
695
+ const selected = readSelectedHost();
696
+ const probeDeps = doctorDepsOverride ?? defaultDoctorDeps();
697
+ const statuses = await Promise.all(loaded.registry.hosts.map((h) => probeHostStatus(h, probeDeps.tcpProbe)));
698
+ if (opts.json) {
699
+ // Entries as validated: bearer fields are NAMES by construction.
700
+ // ADDITIVE ONLY (the shipped bare-array shape is a contract —
701
+ // pre-MH1 consumers keep working): each row gains `selected` +
702
+ // `status`; the scriptable selection pointer is the true row (or
703
+ // `rig config get host.selected`).
704
+ console.log(JSON.stringify(loaded.registry.hosts.map((h, i) => ({ ...h, selected: h.id === selected, status: statuses[i] }))));
705
+ return;
706
+ }
707
+ if (loaded.registry.hosts.length === 0) {
708
+ console.log(`No hosts registered in ${registryPath}. Pair one from a single address: rig host pair <url> (advanced/manual path: rig host add --id <id> --transport <ssh|http> ...)`);
709
+ return;
710
+ }
711
+ // OPR.0.4.6.MH1 FR-4: the own-host name renders here when RENAMED;
712
+ // unnamed (default "localhost") keeps today's output byte-identical
713
+ // (the FR-4 zero-regression AC). Scriptable read: rig config get
714
+ // host.name — the --json array stays registry rows only (shipped
715
+ // bare-array contract).
716
+ const ownName = readOwnHostName();
717
+ if (ownName !== "localhost") {
718
+ console.log(`This host: ${ownName}\n`);
719
+ }
720
+ const pad = (s, n) => (s.length > n ? s.slice(0, n - 1) + "…" : s.padEnd(n));
721
+ console.log(`${pad("", 2)}${pad("ID", 20)} ${pad("TRANSPORT", 10)} ${pad("TARGET", 30)} ${pad("STATUS", 12)} ${pad("AUTH", 24)} NOTES`);
722
+ for (let i = 0; i < loaded.registry.hosts.length; i++) {
723
+ const h = loaded.registry.hosts[i];
724
+ const marker = h.id === selected ? "* " : " ";
725
+ console.log(`${marker}${pad(h.id, 20)} ${pad(h.transport, 10)} ${pad(hostDisplayTarget(h), 30)} ${pad(statuses[i], 12)} ${pad(authPointer(h), 24)} ${h.notes ?? "—"}`);
726
+ }
727
+ if (selected !== "local") {
728
+ console.log(`\nSelected host: ${selected} (return with: rig host select local)`);
729
+ }
730
+ });
731
+ cmd
732
+ .command("doctor")
733
+ .description("Stepwise host verification (+ --posture for the product-factory-vps baseline)")
734
+ .argument("<id>", "Registered host id")
735
+ .option("--posture <profile>", `Run the posture baseline (only built-in profile: ${POSTURE_PROFILE_ID})`)
736
+ .option("--public-addr <ip>", "Public address for the outside-vantage reachability probes (posture items 8-9)")
737
+ .option("--json", "JSON output")
738
+ .action(async (id, opts) => {
739
+ const deps = doctorDepsOverride ?? defaultDoctorDeps();
740
+ const loaded = loadHostRegistry();
741
+ if (!loaded.ok) {
742
+ console.error(loaded.error);
743
+ process.exitCode = 1;
744
+ return;
745
+ }
746
+ const resolved = resolveHost(loaded.registry, id);
747
+ if (!resolved.ok) {
748
+ // The DISTINCT "registry is wrong" error class.
749
+ console.error(`registry: ${resolved.error}`);
750
+ process.exitCode = 1;
751
+ return;
752
+ }
753
+ if (opts.posture !== undefined && opts.posture !== POSTURE_PROFILE_ID) {
754
+ console.error(`unknown posture profile '${opts.posture}'. The only built-in profile is: ${POSTURE_PROFILE_ID}`);
755
+ process.exitCode = 1;
756
+ return;
757
+ }
758
+ const rows = await doctorLegs(resolved.host, deps);
759
+ if (opts.posture) {
760
+ rows.push(...await postureCheck(resolved.host, deps, { publicAddr: opts.publicAddr }));
761
+ }
762
+ renderRows(rows, opts.json);
763
+ if (rows.some((r) => r.status === "fail"))
764
+ process.exitCode = 1;
765
+ const unknowns = rows.filter((r) => r.status === "unknown").length;
766
+ if (!opts.json && unknowns > 0) {
767
+ console.log(`${unknowns} item${unknowns === 1 ? "" : "s"} UNKNOWN — unknown is not pass; see the fix lines to verify.`);
768
+ }
769
+ });
770
+ return cmd;
771
+ }
772
+ //# sourceMappingURL=host.js.map