@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.
- package/daemon/assets/guidance/openrig-start.md +6 -0
- package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +47 -21
- package/daemon/assets/plugins/openrig-core/skills/openrig-cmux/SKILL.md +86 -0
- package/daemon/assets/plugins/openrig-core/skills/openrig-herdr/SKILL.md +158 -0
- package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +742 -94
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.js +12 -8
- package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/pi-resume.d.ts +32 -0
- package/daemon/dist/adapters/pi-resume.d.ts.map +1 -0
- package/daemon/dist/adapters/pi-resume.js +125 -0
- package/daemon/dist/adapters/pi-resume.js.map +1 -0
- package/daemon/dist/adapters/pi-runner-protocol.d.ts +88 -0
- package/daemon/dist/adapters/pi-runner-protocol.d.ts.map +1 -0
- package/daemon/dist/adapters/pi-runner-protocol.js +153 -0
- package/daemon/dist/adapters/pi-runner-protocol.js.map +1 -0
- package/daemon/dist/adapters/pi-runner.d.ts +99 -0
- package/daemon/dist/adapters/pi-runner.d.ts.map +1 -0
- package/daemon/dist/adapters/pi-runner.js +519 -0
- package/daemon/dist/adapters/pi-runner.js.map +1 -0
- package/daemon/dist/adapters/pi-runtime-adapter.d.ts +61 -0
- package/daemon/dist/adapters/pi-runtime-adapter.d.ts.map +1 -0
- package/daemon/dist/adapters/pi-runtime-adapter.js +356 -0
- package/daemon/dist/adapters/pi-runtime-adapter.js.map +1 -0
- package/daemon/dist/adapters/tmux.d.ts +22 -1
- package/daemon/dist/adapters/tmux.d.ts.map +1 -1
- package/daemon/dist/adapters/tmux.js +40 -1
- package/daemon/dist/adapters/tmux.js.map +1 -1
- package/daemon/dist/build-info.d.ts +12 -0
- package/daemon/dist/build-info.d.ts.map +1 -0
- package/daemon/dist/build-info.js +11 -0
- package/daemon/dist/build-info.js.map +1 -0
- package/daemon/dist/builtins/workflow-specs/branched-remediation.yaml +31 -0
- package/daemon/dist/builtins/workflow-specs/factory-rsi.yaml +152 -0
- package/daemon/dist/builtins/workflow-specs/gated-release.yaml +26 -0
- package/daemon/dist/builtins/workflow-specs/linear-build.yaml +21 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts +18 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts.map +1 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js +22 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js.map +1 -0
- package/daemon/dist/db/migrations/049_workflow_instance_version.d.ts +20 -0
- package/daemon/dist/db/migrations/049_workflow_instance_version.d.ts.map +1 -0
- package/daemon/dist/db/migrations/049_workflow_instance_version.js +24 -0
- package/daemon/dist/db/migrations/049_workflow_instance_version.js.map +1 -0
- package/daemon/dist/db/migrations/050_workflow_spec_json.d.ts +22 -0
- package/daemon/dist/db/migrations/050_workflow_spec_json.d.ts.map +1 -0
- package/daemon/dist/db/migrations/050_workflow_spec_json.js +26 -0
- package/daemon/dist/db/migrations/050_workflow_spec_json.js.map +1 -0
- package/daemon/dist/db/migrations/051_workflow_resume.d.ts +22 -0
- package/daemon/dist/db/migrations/051_workflow_resume.d.ts.map +1 -0
- package/daemon/dist/db/migrations/051_workflow_resume.js +27 -0
- package/daemon/dist/db/migrations/051_workflow_resume.js.map +1 -0
- package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.d.ts +26 -0
- package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.d.ts.map +1 -0
- package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.js +30 -0
- package/daemon/dist/db/migrations/052_workflow_instance_bound_rig.js.map +1 -0
- package/daemon/dist/db/migrations/053_sessions_node_id_index.d.ts +48 -0
- package/daemon/dist/db/migrations/053_sessions_node_id_index.d.ts.map +1 -0
- package/daemon/dist/db/migrations/053_sessions_node_id_index.js +53 -0
- package/daemon/dist/db/migrations/053_sessions_node_id_index.js.map +1 -0
- package/daemon/dist/db/migrations/054_queue_transitions_archive.d.ts +34 -0
- package/daemon/dist/db/migrations/054_queue_transitions_archive.d.ts.map +1 -0
- package/daemon/dist/db/migrations/054_queue_transitions_archive.js +49 -0
- package/daemon/dist/db/migrations/054_queue_transitions_archive.js.map +1 -0
- package/daemon/dist/domain/claim-service.d.ts +11 -0
- package/daemon/dist/domain/claim-service.d.ts.map +1 -1
- package/daemon/dist/domain/claim-service.js +3 -1
- package/daemon/dist/domain/claim-service.js.map +1 -1
- package/daemon/dist/domain/discovery-types.d.ts +1 -1
- package/daemon/dist/domain/discovery-types.d.ts.map +1 -1
- package/daemon/dist/domain/feed/attention-aggregator.d.ts +31 -0
- package/daemon/dist/domain/feed/attention-aggregator.d.ts.map +1 -0
- package/daemon/dist/domain/feed/attention-aggregator.js +119 -0
- package/daemon/dist/domain/feed/attention-aggregator.js.map +1 -0
- package/daemon/dist/domain/files/file-write-service.d.ts +11 -2
- package/daemon/dist/domain/files/file-write-service.d.ts.map +1 -1
- package/daemon/dist/domain/files/file-write-service.js +77 -0
- package/daemon/dist/domain/files/file-write-service.js.map +1 -1
- package/daemon/dist/domain/hosts/fanout-contract.d.ts +33 -0
- package/daemon/dist/domain/hosts/fanout-contract.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/fanout-contract.js +23 -0
- package/daemon/dist/domain/hosts/fanout-contract.js.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts +50 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js +184 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-writer.d.ts +11 -0
- package/daemon/dist/domain/hosts/hosts-registry-writer.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-writer.js +54 -0
- package/daemon/dist/domain/hosts/hosts-registry-writer.js.map +1 -0
- package/daemon/dist/domain/hosts/read-through.d.ts +28 -0
- package/daemon/dist/domain/hosts/read-through.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/read-through.js +139 -0
- package/daemon/dist/domain/hosts/read-through.js.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +56 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js +154 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts +71 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.js +80 -0
- package/daemon/dist/domain/human-route-enforcer.js.map +1 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts +8 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/audit-browse.js +19 -0
- package/daemon/dist/domain/mission-control/audit-browse.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.js +4 -0
- package/daemon/dist/domain/mission-control/mission-control-action-log.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts +28 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js +125 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js.map +1 -1
- package/daemon/dist/domain/node-inventory.d.ts +18 -0
- package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
- package/daemon/dist/domain/node-inventory.js +206 -95
- package/daemon/dist/domain/node-inventory.js.map +1 -1
- package/daemon/dist/domain/node-launcher.d.ts +11 -0
- package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/node-launcher.js +10 -0
- package/daemon/dist/domain/node-launcher.js.map +1 -1
- package/daemon/dist/domain/policies/workflow-keepalive.d.ts +7 -0
- package/daemon/dist/domain/policies/workflow-keepalive.d.ts.map +1 -1
- package/daemon/dist/domain/policies/workflow-keepalive.js +93 -13
- package/daemon/dist/domain/policies/workflow-keepalive.js.map +1 -1
- package/daemon/dist/domain/ps-projection.d.ts +28 -1
- package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
- package/daemon/dist/domain/ps-projection.js +76 -26
- package/daemon/dist/domain/ps-projection.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +130 -1
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +304 -11
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/queue-retention.d.ts +130 -0
- package/daemon/dist/domain/queue-retention.d.ts.map +1 -0
- package/daemon/dist/domain/queue-retention.js +224 -0
- package/daemon/dist/domain/queue-retention.js.map +1 -0
- package/daemon/dist/domain/restore-orchestrator.d.ts +5 -0
- package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.js +29 -2
- package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
- package/daemon/dist/domain/resume-token-capture.d.ts +13 -0
- package/daemon/dist/domain/resume-token-capture.d.ts.map +1 -1
- package/daemon/dist/domain/resume-token-capture.js +14 -1
- package/daemon/dist/domain/resume-token-capture.js.map +1 -1
- package/daemon/dist/domain/resume-token-validation.d.ts +1 -1
- package/daemon/dist/domain/resume-token-validation.d.ts.map +1 -1
- package/daemon/dist/domain/resume-token-validation.js +58 -10
- package/daemon/dist/domain/resume-token-validation.js.map +1 -1
- package/daemon/dist/domain/review/brief-spine.d.ts +19 -0
- package/daemon/dist/domain/review/brief-spine.d.ts.map +1 -0
- package/daemon/dist/domain/review/brief-spine.js +86 -0
- package/daemon/dist/domain/review/brief-spine.js.map +1 -0
- package/daemon/dist/domain/review/compose.d.ts +277 -0
- package/daemon/dist/domain/review/compose.d.ts.map +1 -0
- package/daemon/dist/domain/review/compose.js +862 -0
- package/daemon/dist/domain/review/compose.js.map +1 -0
- package/daemon/dist/domain/review/fleet-compose.d.ts +52 -0
- package/daemon/dist/domain/review/fleet-compose.d.ts.map +1 -0
- package/daemon/dist/domain/review/fleet-compose.js +294 -0
- package/daemon/dist/domain/review/fleet-compose.js.map +1 -0
- package/daemon/dist/domain/review/freeze.d.ts +31 -0
- package/daemon/dist/domain/review/freeze.d.ts.map +1 -0
- package/daemon/dist/domain/review/freeze.js +257 -0
- package/daemon/dist/domain/review/freeze.js.map +1 -0
- package/daemon/dist/domain/review/gather.d.ts +115 -0
- package/daemon/dist/domain/review/gather.d.ts.map +1 -0
- package/daemon/dist/domain/review/gather.js +812 -0
- package/daemon/dist/domain/review/gather.js.map +1 -0
- package/daemon/dist/domain/review/types.d.ts +361 -0
- package/daemon/dist/domain/review/types.d.ts.map +1 -0
- package/daemon/dist/domain/review/types.js +35 -0
- package/daemon/dist/domain/review/types.js.map +1 -0
- package/daemon/dist/domain/rig-expansion-service.d.ts.map +1 -1
- package/daemon/dist/domain/rig-expansion-service.js +3 -0
- package/daemon/dist/domain/rig-expansion-service.js.map +1 -1
- package/daemon/dist/domain/rig-repository.d.ts +7 -6
- package/daemon/dist/domain/rig-repository.d.ts.map +1 -1
- package/daemon/dist/domain/rig-repository.js +33 -11
- package/daemon/dist/domain/rig-repository.js.map +1 -1
- package/daemon/dist/domain/rigspec-codec.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-codec.js +3 -0
- package/daemon/dist/domain/rigspec-codec.js.map +1 -1
- package/daemon/dist/domain/rigspec-exporter.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-exporter.js +4 -0
- package/daemon/dist/domain/rigspec-exporter.js.map +1 -1
- package/daemon/dist/domain/rigspec-instantiator.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-instantiator.js +11 -0
- package/daemon/dist/domain/rigspec-instantiator.js.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.d.ts +6 -0
- package/daemon/dist/domain/rigspec-preflight.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.js +26 -1
- package/daemon/dist/domain/rigspec-preflight.js.map +1 -1
- package/daemon/dist/domain/rigspec-schema.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-schema.js +20 -1
- package/daemon/dist/domain/rigspec-schema.js.map +1 -1
- package/daemon/dist/domain/runtime-verifier.d.ts +7 -1
- package/daemon/dist/domain/runtime-verifier.d.ts.map +1 -1
- package/daemon/dist/domain/runtime-verifier.js +39 -1
- package/daemon/dist/domain/runtime-verifier.js.map +1 -1
- package/daemon/dist/domain/scope/scope-approve.d.ts +64 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -0
- package/daemon/dist/domain/scope/scope-approve.js +153 -0
- package/daemon/dist/domain/scope/scope-approve.js.map +1 -0
- package/daemon/dist/domain/scope/scope-audit.d.ts +9 -1
- package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-audit.js +173 -1
- package/daemon/dist/domain/scope/scope-audit.js.map +1 -1
- package/daemon/dist/domain/seat-handover-service.d.ts +9 -0
- package/daemon/dist/domain/seat-handover-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-handover-service.js +2 -0
- package/daemon/dist/domain/seat-handover-service.js.map +1 -1
- package/daemon/dist/domain/seat-identity-store.d.ts +7 -0
- package/daemon/dist/domain/seat-identity-store.d.ts.map +1 -1
- package/daemon/dist/domain/seat-identity-store.js +27 -0
- package/daemon/dist/domain/seat-identity-store.js.map +1 -1
- package/daemon/dist/domain/seat-status-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-status-service.js +6 -4
- package/daemon/dist/domain/seat-status-service.js.map +1 -1
- package/daemon/dist/domain/session-name.d.ts +22 -0
- package/daemon/dist/domain/session-name.d.ts.map +1 -1
- package/daemon/dist/domain/session-name.js +29 -0
- package/daemon/dist/domain/session-name.js.map +1 -1
- package/daemon/dist/domain/slices/slice-detail-projector.d.ts.map +1 -1
- package/daemon/dist/domain/slices/slice-detail-projector.js +4 -5
- package/daemon/dist/domain/slices/slice-detail-projector.js.map +1 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.d.ts +18 -2
- package/daemon/dist/domain/spec-library-workflow-scanner.d.ts.map +1 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.js +23 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.js.map +1 -1
- package/daemon/dist/domain/startup-proof.d.ts +10 -6
- package/daemon/dist/domain/startup-proof.d.ts.map +1 -1
- package/daemon/dist/domain/startup-proof.js +35 -7
- package/daemon/dist/domain/startup-proof.js.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.d.ts +16 -0
- package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.js +13 -0
- package/daemon/dist/domain/successor-session-launcher.js.map +1 -1
- package/daemon/dist/domain/terminal/cmux-provider-adapter.d.ts +27 -0
- package/daemon/dist/domain/terminal/cmux-provider-adapter.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/cmux-provider-adapter.js +67 -0
- package/daemon/dist/domain/terminal/cmux-provider-adapter.js.map +1 -0
- package/daemon/dist/domain/terminal/herdr-adapter.d.ts +77 -0
- package/daemon/dist/domain/terminal/herdr-adapter.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/herdr-adapter.js +266 -0
- package/daemon/dist/domain/terminal/herdr-adapter.js.map +1 -0
- package/daemon/dist/domain/terminal/herdr-transport.d.ts +50 -0
- package/daemon/dist/domain/terminal/herdr-transport.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/herdr-transport.js +145 -0
- package/daemon/dist/domain/terminal/herdr-transport.js.map +1 -0
- package/daemon/dist/domain/terminal/terminal-provider.d.ts +88 -0
- package/daemon/dist/domain/terminal/terminal-provider.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/terminal-provider.js +17 -0
- package/daemon/dist/domain/terminal/terminal-provider.js.map +1 -0
- package/daemon/dist/domain/terminal/terminal-service.d.ts +60 -0
- package/daemon/dist/domain/terminal/terminal-service.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/terminal-service.js +174 -0
- package/daemon/dist/domain/terminal/terminal-service.js.map +1 -0
- package/daemon/dist/domain/terminal/terminal-views-store.d.ts +77 -0
- package/daemon/dist/domain/terminal/terminal-views-store.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/terminal-views-store.js +146 -0
- package/daemon/dist/domain/terminal/terminal-views-store.js.map +1 -0
- package/daemon/dist/domain/terminal/view-composer.d.ts +42 -0
- package/daemon/dist/domain/terminal/view-composer.d.ts.map +1 -0
- package/daemon/dist/domain/terminal/view-composer.js +137 -0
- package/daemon/dist/domain/terminal/view-composer.js.map +1 -0
- package/daemon/dist/domain/tmux-option-defaults.d.ts +89 -0
- package/daemon/dist/domain/tmux-option-defaults.d.ts.map +1 -0
- package/daemon/dist/domain/tmux-option-defaults.js +132 -0
- package/daemon/dist/domain/tmux-option-defaults.js.map +1 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts +49 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts.map +1 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js +132 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts +29 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js +60 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts +34 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.js +195 -0
- package/daemon/dist/domain/topology/topology-manifest.js.map +1 -0
- package/daemon/dist/domain/types.d.ts +39 -1
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/up-command-router.d.ts +1 -1
- package/daemon/dist/domain/up-command-router.d.ts.map +1 -1
- package/daemon/dist/domain/up-command-router.js +34 -2
- package/daemon/dist/domain/up-command-router.js.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts +18 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.js +201 -0
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/workflow/slice-workflow-projection.d.ts.map +1 -1
- package/daemon/dist/domain/workflow/slice-workflow-projection.js +6 -2
- package/daemon/dist/domain/workflow/slice-workflow-projection.js.map +1 -1
- package/daemon/dist/domain/workflow-boot-sweep.d.ts +28 -0
- package/daemon/dist/domain/workflow-boot-sweep.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-boot-sweep.js +102 -0
- package/daemon/dist/domain/workflow-boot-sweep.js.map +1 -0
- package/daemon/dist/domain/workflow-deadline.d.ts +74 -0
- package/daemon/dist/domain/workflow-deadline.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-deadline.js +165 -0
- package/daemon/dist/domain/workflow-deadline.js.map +1 -0
- package/daemon/dist/domain/workflow-exception-escalation.d.ts +33 -0
- package/daemon/dist/domain/workflow-exception-escalation.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-exception-escalation.js +104 -0
- package/daemon/dist/domain/workflow-exception-escalation.js.map +1 -0
- package/daemon/dist/domain/workflow-exception-router.d.ts +33 -0
- package/daemon/dist/domain/workflow-exception-router.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-exception-router.js +96 -0
- package/daemon/dist/domain/workflow-exception-router.js.map +1 -0
- package/daemon/dist/domain/workflow-exception.d.ts +90 -0
- package/daemon/dist/domain/workflow-exception.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-exception.js +122 -0
- package/daemon/dist/domain/workflow-exception.js.map +1 -0
- package/daemon/dist/domain/workflow-frontier-guard.d.ts +24 -0
- package/daemon/dist/domain/workflow-frontier-guard.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-frontier-guard.js +30 -0
- package/daemon/dist/domain/workflow-frontier-guard.js.map +1 -0
- package/daemon/dist/domain/workflow-instance-store.d.ts +28 -0
- package/daemon/dist/domain/workflow-instance-store.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-instance-store.js +66 -10
- package/daemon/dist/domain/workflow-instance-store.js.map +1 -1
- package/daemon/dist/domain/workflow-keepalive-arming.d.ts +35 -0
- package/daemon/dist/domain/workflow-keepalive-arming.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-keepalive-arming.js +87 -0
- package/daemon/dist/domain/workflow-keepalive-arming.js.map +1 -0
- package/daemon/dist/domain/workflow-projector.d.ts +117 -2
- package/daemon/dist/domain/workflow-projector.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-projector.js +672 -41
- package/daemon/dist/domain/workflow-projector.js.map +1 -1
- package/daemon/dist/domain/workflow-role-context.d.ts +57 -0
- package/daemon/dist/domain/workflow-role-context.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-role-context.js +120 -0
- package/daemon/dist/domain/workflow-role-context.js.map +1 -0
- package/daemon/dist/domain/workflow-role-resolver.d.ts +65 -0
- package/daemon/dist/domain/workflow-role-resolver.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-role-resolver.js +108 -0
- package/daemon/dist/domain/workflow-role-resolver.js.map +1 -0
- package/daemon/dist/domain/workflow-runtime.d.ts +157 -1
- package/daemon/dist/domain/workflow-runtime.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-runtime.js +718 -11
- package/daemon/dist/domain/workflow-runtime.js.map +1 -1
- package/daemon/dist/domain/workflow-spec-cache.d.ts +26 -1
- package/daemon/dist/domain/workflow-spec-cache.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-spec-cache.js +327 -13
- package/daemon/dist/domain/workflow-spec-cache.js.map +1 -1
- package/daemon/dist/domain/workflow-types.d.ts +143 -8
- package/daemon/dist/domain/workflow-types.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-types.js +14 -1
- package/daemon/dist/domain/workflow-types.js.map +1 -1
- package/daemon/dist/domain/workflow-validator.d.ts +11 -1
- package/daemon/dist/domain/workflow-validator.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-validator.js +246 -1
- package/daemon/dist/domain/workflow-validator.js.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js +51 -6
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts +11 -0
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.js +82 -0
- package/daemon/dist/domain/workspace/workspace-doctor.js.map +1 -1
- package/daemon/dist/index.d.ts +17 -0
- package/daemon/dist/index.d.ts.map +1 -1
- package/daemon/dist/index.js +44 -0
- package/daemon/dist/index.js.map +1 -1
- package/daemon/dist/routes/activity.js +22 -0
- package/daemon/dist/routes/activity.js.map +1 -1
- package/daemon/dist/routes/config.d.ts.map +1 -1
- package/daemon/dist/routes/config.js +15 -6
- package/daemon/dist/routes/config.js.map +1 -1
- package/daemon/dist/routes/files.d.ts.map +1 -1
- package/daemon/dist/routes/files.js +45 -1
- package/daemon/dist/routes/files.js.map +1 -1
- package/daemon/dist/routes/hosts.d.ts +5 -0
- package/daemon/dist/routes/hosts.d.ts.map +1 -0
- package/daemon/dist/routes/hosts.js +346 -0
- package/daemon/dist/routes/hosts.js.map +1 -0
- package/daemon/dist/routes/mission-control.d.ts.map +1 -1
- package/daemon/dist/routes/mission-control.js +67 -2
- package/daemon/dist/routes/mission-control.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts +10 -9
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +305 -10
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/review.d.ts +3 -0
- package/daemon/dist/routes/review.d.ts.map +1 -0
- package/daemon/dist/routes/review.js +195 -0
- package/daemon/dist/routes/review.js.map +1 -0
- package/daemon/dist/routes/scope-approve.d.ts +3 -0
- package/daemon/dist/routes/scope-approve.d.ts.map +1 -0
- package/daemon/dist/routes/scope-approve.js +55 -0
- package/daemon/dist/routes/scope-approve.js.map +1 -0
- package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
- package/daemon/dist/routes/scope-audit.js +28 -1
- package/daemon/dist/routes/scope-audit.js.map +1 -1
- package/daemon/dist/routes/seat.js +13 -0
- package/daemon/dist/routes/seat.js.map +1 -1
- package/daemon/dist/routes/slices.d.ts.map +1 -1
- package/daemon/dist/routes/slices.js +49 -9
- package/daemon/dist/routes/slices.js.map +1 -1
- package/daemon/dist/routes/terminal-ws.d.ts.map +1 -1
- package/daemon/dist/routes/terminal-ws.js +58 -21
- package/daemon/dist/routes/terminal-ws.js.map +1 -1
- package/daemon/dist/routes/terminal.d.ts +11 -0
- package/daemon/dist/routes/terminal.d.ts.map +1 -0
- package/daemon/dist/routes/terminal.js +104 -0
- package/daemon/dist/routes/terminal.js.map +1 -0
- package/daemon/dist/routes/up.d.ts.map +1 -1
- package/daemon/dist/routes/up.js +84 -0
- package/daemon/dist/routes/up.js.map +1 -1
- package/daemon/dist/routes/workflow.d.ts.map +1 -1
- package/daemon/dist/routes/workflow.js +102 -10
- package/daemon/dist/routes/workflow.js.map +1 -1
- package/daemon/dist/routes/workspace.js +2 -2
- package/daemon/dist/server.d.ts +6 -0
- package/daemon/dist/server.d.ts.map +1 -1
- package/daemon/dist/server.js +36 -1
- package/daemon/dist/server.js.map +1 -1
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +238 -14
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/docs/reference/agent-startup-guide.md +1 -1
- package/daemon/docs/reference/product-factory-vps-runbook.md +132 -0
- package/daemon/docs/reference/sdlc-conventions.md +187 -0
- package/daemon/specs/agents/conveyor/builder/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/builder/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/lead/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/lead/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/planner/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/planner/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/design/product-designer/agent.yaml +1 -1
- package/daemon/specs/agents/design/product-designer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/implementer/agent.yaml +1 -1
- package/daemon/specs/agents/development/implementer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/qa/agent.yaml +1 -1
- package/daemon/specs/agents/development/qa/guidance/role.md +1 -0
- package/daemon/specs/agents/factory-rsi/dogfood/agent.yaml +29 -0
- package/daemon/specs/agents/factory-rsi/dogfood/guidance/role.md +37 -0
- package/daemon/specs/agents/factory-rsi/release-manager/agent.yaml +29 -0
- package/daemon/specs/agents/factory-rsi/release-manager/guidance/role.md +40 -0
- package/daemon/specs/agents/orchestration/orchestrator/agent.yaml +1 -1
- package/daemon/specs/agents/orchestration/orchestrator/guidance/role.md +1 -0
- package/daemon/specs/agents/product-management/pm/agent.yaml +1 -0
- package/daemon/specs/agents/product-management/pm/guidance/role.md +13 -0
- package/daemon/specs/agents/review/independent-reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/review/independent-reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/shared/agent.yaml +2 -0
- package/daemon/specs/agents/shared/skills/core/mission-slice-sop/SKILL.md +113 -0
- package/daemon/specs/agents/shared/skills/core/openrig-cmux/SKILL.md +86 -0
- package/daemon/specs/agents/shared/skills/core/openrig-herdr/SKILL.md +158 -0
- package/daemon/specs/agents/shared/skills/core/openrig-operator/SKILL.md +4 -4
- package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +75 -29
- package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +2 -2
- package/daemon/specs/rigs/focused/pm-team/CULTURE.md +4 -0
- package/daemon/specs/rigs/launch/factory-rsi/CULTURE.md +47 -0
- package/daemon/specs/rigs/launch/factory-rsi/rig.yaml +115 -0
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +1 -1
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/startup/context.md +1 -1
- package/daemon/specs/rigs/preview/product-team/CULTURE.md +4 -0
- package/dist/build-info.d.ts +8 -0
- package/dist/build-info.d.ts.map +1 -0
- package/dist/build-info.js +11 -0
- package/dist/build-info.js.map +1 -0
- package/dist/commands/broadcast.d.ts +6 -1
- package/dist/commands/broadcast.d.ts.map +1 -1
- package/dist/commands/broadcast.js +101 -15
- package/dist/commands/broadcast.js.map +1 -1
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +99 -8
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/config-init-workspace.d.ts.map +1 -1
- package/dist/commands/config-init-workspace.js +20 -4
- package/dist/commands/config-init-workspace.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +4 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +5 -0
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/expand.js +1 -1
- package/dist/commands/expand.js.map +1 -1
- package/dist/commands/file.d.ts +7 -0
- package/dist/commands/file.d.ts.map +1 -0
- package/dist/commands/file.js +77 -0
- package/dist/commands/file.js.map +1 -0
- package/dist/commands/host.d.ts +48 -0
- package/dist/commands/host.d.ts.map +1 -0
- package/dist/commands/host.js +772 -0
- package/dist/commands/host.js.map +1 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +5 -0
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/proof.d.ts +54 -0
- package/dist/commands/proof.d.ts.map +1 -0
- package/dist/commands/proof.js +311 -0
- package/dist/commands/proof.js.map +1 -0
- package/dist/commands/ps.d.ts +41 -7
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +353 -135
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts +36 -0
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +149 -7
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/restore-packet.d.ts.map +1 -1
- package/dist/commands/restore-packet.js +13 -2
- package/dist/commands/restore-packet.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +140 -3
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +127 -13
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/terminal.d.ts +6 -0
- package/dist/commands/terminal.d.ts.map +1 -0
- package/dist/commands/terminal.js +116 -0
- package/dist/commands/terminal.js.map +1 -0
- package/dist/commands/transcript.d.ts +6 -1
- package/dist/commands/transcript.d.ts.map +1 -1
- package/dist/commands/transcript.js +113 -30
- package/dist/commands/transcript.js.map +1 -1
- package/dist/commands/up.d.ts +2 -0
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +78 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +11 -0
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/workflow-errors.d.ts +23 -0
- package/dist/commands/workflow-errors.d.ts.map +1 -0
- package/dist/commands/workflow-errors.js +100 -0
- package/dist/commands/workflow-errors.js.map +1 -0
- package/dist/commands/workflow-follow.d.ts +71 -0
- package/dist/commands/workflow-follow.d.ts.map +1 -0
- package/dist/commands/workflow-follow.js +259 -0
- package/dist/commands/workflow-follow.js.map +1 -0
- package/dist/commands/workflow-render.d.ts +114 -0
- package/dist/commands/workflow-render.d.ts.map +1 -0
- package/dist/commands/workflow-render.js +223 -0
- package/dist/commands/workflow-render.js.map +1 -0
- package/dist/commands/workflow.d.ts +1 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +278 -14
- package/dist/commands/workflow.js.map +1 -1
- package/dist/commands/workspace.js +6 -6
- package/dist/commands/workspace.js.map +1 -1
- package/dist/config-store.d.ts +19 -1
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +171 -0
- package/dist/config-store.js.map +1 -1
- package/dist/cross-host-cli-helpers.d.ts +13 -0
- package/dist/cross-host-cli-helpers.d.ts.map +1 -1
- package/dist/cross-host-cli-helpers.js +27 -0
- package/dist/cross-host-cli-helpers.js.map +1 -1
- package/dist/cross-host-executor.d.ts +1 -0
- package/dist/cross-host-executor.d.ts.map +1 -1
- package/dist/cross-host-executor.js +4 -1
- package/dist/cross-host-executor.js.map +1 -1
- package/dist/cross-host-target.d.ts +49 -0
- package/dist/cross-host-target.d.ts.map +1 -0
- package/dist/cross-host-target.js +35 -0
- package/dist/cross-host-target.js.map +1 -0
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +4 -1
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/host-registry.d.ts +17 -0
- package/dist/host-registry.d.ts.map +1 -1
- package/dist/host-registry.js +64 -2
- package/dist/host-registry.js.map +1 -1
- package/dist/host-selection.d.ts +13 -0
- package/dist/host-selection.d.ts.map +1 -0
- package/dist/host-selection.js +54 -0
- package/dist/host-selection.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/file-transfer.d.ts +89 -0
- package/dist/lib/file-transfer.d.ts.map +1 -0
- package/dist/lib/file-transfer.js +300 -0
- package/dist/lib/file-transfer.js.map +1 -0
- package/dist/lib/hosts/fanout-contract.d.ts +33 -0
- package/dist/lib/hosts/fanout-contract.d.ts.map +1 -0
- package/dist/lib/hosts/fanout-contract.js +23 -0
- package/dist/lib/hosts/fanout-contract.js.map +1 -0
- package/dist/lib/scope/scope-audit.d.ts +9 -1
- package/dist/lib/scope/scope-audit.d.ts.map +1 -1
- package/dist/lib/scope/scope-audit.js +173 -1
- package/dist/lib/scope/scope-audit.js.map +1 -1
- package/dist/lib/scope/templates.d.ts +1 -0
- package/dist/lib/scope/templates.d.ts.map +1 -1
- package/dist/lib/scope/templates.js +9 -0
- package/dist/lib/scope/templates.js.map +1 -1
- package/dist/lib/scope-templates/backlog-deprecation.md +16 -0
- package/dist/lib/scope-templates/backlog-tech-debt.md +16 -0
- package/dist/lib/scope-templates/bug-fix.md +16 -0
- package/dist/lib/scope-templates/implementation-prd.md +33 -0
- package/dist/lib/scope-templates/mission-placeholder.md +1 -1
- package/dist/lib/scope-templates/mission-progress.md +1 -1
- package/dist/lib/scope-templates/mission-release.md +1 -1
- package/dist/lib/scope-templates/placeholder.md +11 -7
- package/dist/lib/scope-templates/proof.md +5 -1
- package/dist/lib/scope-templates/release-feature.md +14 -6
- package/dist/lib/scope-templates/research.md +16 -0
- package/dist/lib/scope-templates/slice-progress.md +1 -1
- package/dist/remote-host-ops.d.ts +1 -0
- package/dist/remote-host-ops.d.ts.map +1 -1
- package/dist/remote-host-ops.js +11 -4
- package/dist/remote-host-ops.js.map +1 -1
- package/dist/session-name.d.ts +23 -0
- package/dist/session-name.d.ts.map +1 -0
- package/dist/session-name.js +61 -0
- package/dist/session-name.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +11 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/index-CFqxa0zW.css +32 -0
- package/ui/dist/assets/index-zrFqS7gL.js +623 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DYZniQcc.js +0 -597
- 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
|