orbital-command 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -42
- package/bin/commands/config.js +19 -0
- package/bin/commands/events.js +40 -0
- package/bin/commands/launch.js +126 -0
- package/bin/commands/manifest.js +283 -0
- package/bin/commands/registry.js +104 -0
- package/bin/commands/update.js +24 -0
- package/bin/lib/helpers.js +229 -0
- package/bin/orbital.js +147 -319
- package/dist/assets/Landing-CfQdHR0N.js +11 -0
- package/dist/assets/PrimitivesConfig-DThSipFy.js +32 -0
- package/dist/assets/QualityGates-B4kxM5UU.js +26 -0
- package/dist/assets/SessionTimeline-Bz1iZnmg.js +1 -0
- package/dist/assets/Settings-DLcZwbCT.js +12 -0
- package/dist/assets/SourceControl-BMNIz7Lt.js +36 -0
- package/dist/assets/WorkflowVisualizer-CxuSBOYu.js +69 -0
- package/dist/assets/arrow-down-DVPp6_qp.js +6 -0
- package/dist/assets/bot-NFaJBDn_.js +6 -0
- package/dist/assets/charts-LGLb8hyU.js +68 -0
- package/dist/assets/circle-x-IsFCkBZu.js +6 -0
- package/dist/assets/file-text-J1cebZXF.js +6 -0
- package/dist/assets/globe-WzeyHsUc.js +6 -0
- package/dist/assets/index-BdJ57EhC.css +1 -0
- package/dist/assets/index-o4ScMAuR.js +349 -0
- package/dist/assets/key-CKR8JJSj.js +6 -0
- package/dist/assets/minus-CHBsJyjp.js +6 -0
- package/dist/assets/radio-xqZaR-Uk.js +6 -0
- package/dist/assets/rocket-D_xvvNG6.js +6 -0
- package/dist/assets/shield-TdB1yv_a.js +6 -0
- package/dist/assets/ui-BmsSg9jU.js +53 -0
- package/dist/assets/useSocketListener-0L5yiN5i.js +1 -0
- package/dist/assets/useWorkflowEditor-CqeRWVQX.js +11 -0
- package/dist/assets/{vendor-Dzv9lrRc.js → vendor-Bqt8AJn2.js} +1 -1
- package/dist/assets/workflow-constants-Rw-GmgHZ.js +6 -0
- package/dist/assets/zap-C9wqYMpl.js +6 -0
- package/dist/favicon.svg +1 -0
- package/dist/index.html +6 -5
- package/dist/server/server/__tests__/data-routes.test.js +126 -0
- package/dist/server/server/__tests__/helpers/db.js +17 -0
- package/dist/server/server/__tests__/helpers/mock-emitter.js +8 -0
- package/dist/server/server/__tests__/scope-routes.test.js +138 -0
- package/dist/server/server/__tests__/sprint-routes.test.js +102 -0
- package/dist/server/server/__tests__/workflow-routes.test.js +107 -0
- package/dist/server/server/config-migrator.js +135 -0
- package/dist/server/server/config.js +51 -7
- package/dist/server/server/database.js +21 -28
- package/dist/server/server/global-config.js +143 -0
- package/dist/server/server/index.js +118 -276
- package/dist/server/server/init.js +243 -225
- package/dist/server/server/launch.js +29 -0
- package/dist/server/server/manifest-types.js +8 -0
- package/dist/server/server/manifest.js +454 -0
- package/dist/server/server/migrate-legacy.js +229 -0
- package/dist/server/server/parsers/event-parser.js +4 -1
- package/dist/server/server/parsers/event-parser.test.js +117 -0
- package/dist/server/server/parsers/scope-parser.js +74 -28
- package/dist/server/server/parsers/scope-parser.test.js +230 -0
- package/dist/server/server/project-context.js +265 -0
- package/dist/server/server/project-emitter.js +41 -0
- package/dist/server/server/project-manager.js +297 -0
- package/dist/server/server/routes/aggregate-routes.js +871 -0
- package/dist/server/server/routes/config-routes.js +41 -90
- package/dist/server/server/routes/data-routes.js +25 -123
- package/dist/server/server/routes/dispatch-routes.js +37 -15
- package/dist/server/server/routes/git-routes.js +74 -0
- package/dist/server/server/routes/manifest-routes.js +319 -0
- package/dist/server/server/routes/scope-routes.js +45 -28
- package/dist/server/server/routes/sync-routes.js +134 -0
- package/dist/server/server/routes/version-routes.js +1 -15
- package/dist/server/server/routes/workflow-routes.js +9 -3
- package/dist/server/server/schema.js +3 -0
- package/dist/server/server/services/batch-orchestrator.js +41 -17
- package/dist/server/server/services/claude-session-service.js +17 -14
- package/dist/server/server/services/config-service.js +10 -1
- package/dist/server/server/services/deploy-service.test.js +119 -0
- package/dist/server/server/services/event-service.js +64 -1
- package/dist/server/server/services/event-service.test.js +191 -0
- package/dist/server/server/services/gate-service.test.js +105 -0
- package/dist/server/server/services/git-service.js +108 -4
- package/dist/server/server/services/github-service.js +110 -2
- package/dist/server/server/services/readiness-service.test.js +190 -0
- package/dist/server/server/services/scope-cache.js +5 -1
- package/dist/server/server/services/scope-cache.test.js +142 -0
- package/dist/server/server/services/scope-service.js +222 -131
- package/dist/server/server/services/scope-service.test.js +137 -0
- package/dist/server/server/services/sprint-orchestrator.js +29 -15
- package/dist/server/server/services/sprint-service.js +23 -3
- package/dist/server/server/services/sprint-service.test.js +238 -0
- package/dist/server/server/services/sync-service.js +434 -0
- package/dist/server/server/services/sync-types.js +2 -0
- package/dist/server/server/services/workflow-service.js +26 -5
- package/dist/server/server/services/workflow-service.test.js +159 -0
- package/dist/server/server/settings-sync.js +284 -0
- package/dist/server/server/uninstall.js +195 -0
- package/dist/server/server/update-planner.js +279 -0
- package/dist/server/server/update.js +212 -0
- package/dist/server/server/utils/cc-hooks-parser.js +3 -0
- package/dist/server/server/utils/cc-hooks-parser.test.js +86 -0
- package/dist/server/server/utils/dispatch-utils.js +83 -24
- package/dist/server/server/utils/dispatch-utils.test.js +182 -0
- package/dist/server/server/utils/flag-builder.js +54 -0
- package/dist/server/server/utils/json-fields.js +14 -0
- package/dist/server/server/utils/json-fields.test.js +73 -0
- package/dist/server/server/utils/logger.js +37 -3
- package/dist/server/server/utils/package-info.js +30 -0
- package/dist/server/server/utils/route-helpers.js +47 -0
- package/dist/server/server/utils/route-helpers.test.js +115 -0
- package/dist/server/server/utils/terminal-launcher.js +79 -25
- package/dist/server/server/utils/worktree-manager.js +13 -4
- package/dist/server/server/validator.js +230 -0
- package/dist/server/server/watchers/event-watcher.js +28 -13
- package/dist/server/server/watchers/global-watcher.js +63 -0
- package/dist/server/server/watchers/scope-watcher.js +27 -12
- package/dist/server/server/wizard/config-editor.js +237 -0
- package/dist/server/server/wizard/detect.js +96 -0
- package/dist/server/server/wizard/doctor.js +115 -0
- package/dist/server/server/wizard/index.js +340 -0
- package/dist/server/server/wizard/phases/confirm.js +39 -0
- package/dist/server/server/wizard/phases/project-setup.js +90 -0
- package/dist/server/server/wizard/phases/setup-wizard.js +66 -0
- package/dist/server/server/wizard/phases/welcome.js +32 -0
- package/dist/server/server/wizard/phases/workflow-setup.js +22 -0
- package/dist/server/server/wizard/types.js +29 -0
- package/dist/server/server/wizard/ui.js +73 -0
- package/dist/server/shared/__fixtures__/workflow-configs.js +75 -0
- package/dist/server/shared/api-types.js +80 -1
- package/dist/server/shared/default-workflow.json +65 -0
- package/dist/server/shared/onboarding-tour.test.js +81 -0
- package/dist/server/shared/project-colors.js +24 -0
- package/dist/server/shared/workflow-config.test.js +84 -0
- package/dist/server/shared/workflow-engine.js +1 -1
- package/dist/server/shared/workflow-engine.test.js +302 -0
- package/dist/server/shared/workflow-normalizer.js +101 -0
- package/dist/server/shared/workflow-normalizer.test.js +100 -0
- package/dist/server/src/components/onboarding/tour-steps.js +84 -0
- package/package.json +34 -29
- package/schemas/orbital.config.schema.json +2 -5
- package/scripts/postinstall.js +18 -6
- package/scripts/release.sh +53 -0
- package/server/__tests__/data-routes.test.ts +151 -0
- package/server/__tests__/helpers/db.ts +19 -0
- package/server/__tests__/helpers/mock-emitter.ts +10 -0
- package/server/__tests__/scope-routes.test.ts +158 -0
- package/server/__tests__/sprint-routes.test.ts +118 -0
- package/server/__tests__/workflow-routes.test.ts +120 -0
- package/server/config-migrator.ts +160 -0
- package/server/config.ts +64 -12
- package/server/database.ts +22 -31
- package/server/global-config.ts +204 -0
- package/server/index.ts +139 -316
- package/server/init.ts +266 -234
- package/server/launch.ts +32 -0
- package/server/manifest-types.ts +145 -0
- package/server/manifest.ts +494 -0
- package/server/migrate-legacy.ts +290 -0
- package/server/parsers/event-parser.test.ts +135 -0
- package/server/parsers/event-parser.ts +4 -1
- package/server/parsers/scope-parser.test.ts +270 -0
- package/server/parsers/scope-parser.ts +79 -31
- package/server/project-context.ts +325 -0
- package/server/project-emitter.ts +50 -0
- package/server/project-manager.ts +368 -0
- package/server/routes/aggregate-routes.ts +968 -0
- package/server/routes/config-routes.ts +43 -85
- package/server/routes/data-routes.ts +34 -156
- package/server/routes/dispatch-routes.ts +46 -17
- package/server/routes/git-routes.ts +77 -0
- package/server/routes/manifest-routes.ts +388 -0
- package/server/routes/scope-routes.ts +39 -30
- package/server/routes/sync-routes.ts +175 -0
- package/server/routes/version-routes.ts +1 -16
- package/server/routes/workflow-routes.ts +9 -3
- package/server/schema.ts +3 -0
- package/server/services/batch-orchestrator.ts +41 -17
- package/server/services/claude-session-service.ts +16 -14
- package/server/services/config-service.ts +10 -1
- package/server/services/deploy-service.test.ts +145 -0
- package/server/services/deploy-service.ts +2 -2
- package/server/services/event-service.test.ts +242 -0
- package/server/services/event-service.ts +92 -3
- package/server/services/gate-service.test.ts +131 -0
- package/server/services/gate-service.ts +2 -2
- package/server/services/git-service.ts +137 -4
- package/server/services/github-service.ts +120 -2
- package/server/services/readiness-service.test.ts +217 -0
- package/server/services/scope-cache.test.ts +167 -0
- package/server/services/scope-cache.ts +4 -1
- package/server/services/scope-service.test.ts +169 -0
- package/server/services/scope-service.ts +224 -130
- package/server/services/sprint-orchestrator.ts +30 -15
- package/server/services/sprint-service.test.ts +271 -0
- package/server/services/sprint-service.ts +29 -5
- package/server/services/sync-service.ts +482 -0
- package/server/services/sync-types.ts +77 -0
- package/server/services/workflow-service.test.ts +190 -0
- package/server/services/workflow-service.ts +29 -9
- package/server/settings-sync.ts +359 -0
- package/server/uninstall.ts +214 -0
- package/server/update-planner.ts +346 -0
- package/server/update.ts +263 -0
- package/server/utils/cc-hooks-parser.test.ts +96 -0
- package/server/utils/cc-hooks-parser.ts +4 -0
- package/server/utils/dispatch-utils.test.ts +245 -0
- package/server/utils/dispatch-utils.ts +102 -30
- package/server/utils/flag-builder.ts +56 -0
- package/server/utils/json-fields.test.ts +83 -0
- package/server/utils/json-fields.ts +14 -0
- package/server/utils/logger.ts +40 -3
- package/server/utils/package-info.ts +32 -0
- package/server/utils/route-helpers.test.ts +144 -0
- package/server/utils/route-helpers.ts +50 -0
- package/server/utils/terminal-launcher.ts +85 -25
- package/server/utils/worktree-manager.ts +9 -4
- package/server/validator.ts +270 -0
- package/server/watchers/event-watcher.ts +24 -12
- package/server/watchers/global-watcher.ts +77 -0
- package/server/watchers/scope-watcher.ts +21 -9
- package/server/wizard/config-editor.ts +248 -0
- package/server/wizard/detect.ts +104 -0
- package/server/wizard/doctor.ts +114 -0
- package/server/wizard/index.ts +438 -0
- package/server/wizard/phases/confirm.ts +45 -0
- package/server/wizard/phases/project-setup.ts +106 -0
- package/server/wizard/phases/setup-wizard.ts +78 -0
- package/server/wizard/phases/welcome.ts +39 -0
- package/server/wizard/phases/workflow-setup.ts +28 -0
- package/server/wizard/types.ts +56 -0
- package/server/wizard/ui.ts +92 -0
- package/shared/__fixtures__/workflow-configs.ts +80 -0
- package/shared/api-types.ts +106 -0
- package/shared/onboarding-tour.test.ts +94 -0
- package/shared/project-colors.ts +24 -0
- package/shared/workflow-config.test.ts +111 -0
- package/shared/workflow-config.ts +7 -0
- package/shared/workflow-engine.test.ts +388 -0
- package/shared/workflow-engine.ts +1 -1
- package/shared/workflow-normalizer.test.ts +119 -0
- package/shared/workflow-normalizer.ts +118 -0
- package/templates/agents/QUICK-REFERENCE.md +1 -0
- package/templates/agents/README.md +1 -0
- package/templates/agents/SKILL-TRIGGERS.md +11 -0
- package/templates/agents/green-team/deep-dive.md +361 -0
- package/templates/hooks/end-session.sh +4 -1
- package/templates/hooks/init-session.sh +1 -0
- package/templates/hooks/orbital-emit.sh +2 -2
- package/templates/hooks/orbital-report-deploy.sh +4 -4
- package/templates/hooks/orbital-report-gates.sh +4 -4
- package/templates/hooks/orbital-scope-update.sh +1 -1
- package/templates/hooks/scope-commit-logger.sh +2 -2
- package/templates/hooks/scope-create-cleanup.sh +2 -2
- package/templates/hooks/scope-create-gate.sh +2 -5
- package/templates/hooks/scope-gate.sh +4 -6
- package/templates/hooks/scope-helpers.sh +28 -1
- package/templates/hooks/scope-lifecycle-gate.sh +14 -5
- package/templates/hooks/scope-prepare.sh +67 -12
- package/templates/hooks/scope-transition.sh +14 -6
- package/templates/hooks/time-tracker.sh +2 -5
- package/templates/migrations/renames.json +1 -0
- package/templates/orbital.config.json +8 -6
- package/{shared/default-workflow.json → templates/presets/default.json} +65 -0
- package/templates/presets/development.json +4 -4
- package/templates/presets/gitflow.json +7 -0
- package/templates/prompts/README.md +23 -0
- package/templates/prompts/deep-dive-audit.md +94 -0
- package/templates/quick/rules.md +56 -5
- package/templates/settings-hooks.json +1 -1
- package/templates/skills/git-commit/SKILL.md +27 -7
- package/templates/skills/git-dev/SKILL.md +13 -4
- package/templates/skills/git-main/SKILL.md +13 -3
- package/templates/skills/git-production/SKILL.md +9 -2
- package/templates/skills/git-staging/SKILL.md +11 -3
- package/templates/skills/scope-create/SKILL.md +17 -3
- package/templates/skills/scope-fix-review/SKILL.md +14 -7
- package/templates/skills/scope-implement/SKILL.md +15 -4
- package/templates/skills/scope-post-review/SKILL.md +77 -7
- package/templates/skills/scope-pre-review/SKILL.md +11 -4
- package/templates/skills/scope-verify/SKILL.md +5 -3
- package/templates/skills/test-code-review/SKILL.md +41 -33
- package/templates/skills/test-scaffold/SKILL.md +222 -0
- package/dist/assets/WorkflowVisualizer-BZ21PIIF.js +0 -84
- package/dist/assets/charts-D__PA1zp.js +0 -72
- package/dist/assets/index-D1G6i0nS.css +0 -1
- package/dist/assets/index-DpItvKpf.js +0 -419
- package/dist/assets/ui-BvF022GT.js +0 -53
- package/index.html +0 -15
- package/postcss.config.js +0 -6
- package/src/App.tsx +0 -33
- package/src/components/AgentBadge.tsx +0 -40
- package/src/components/BatchPreflightModal.tsx +0 -115
- package/src/components/CardDisplayToggle.tsx +0 -74
- package/src/components/ColumnHeaderActions.tsx +0 -55
- package/src/components/ColumnMenu.tsx +0 -99
- package/src/components/DeployHistory.tsx +0 -141
- package/src/components/DispatchModal.tsx +0 -164
- package/src/components/DispatchPopover.tsx +0 -139
- package/src/components/DragOverlay.tsx +0 -25
- package/src/components/DriftSidebar.tsx +0 -140
- package/src/components/EnvironmentStrip.tsx +0 -88
- package/src/components/ErrorBoundary.tsx +0 -62
- package/src/components/FilterChip.tsx +0 -105
- package/src/components/GateIndicator.tsx +0 -33
- package/src/components/IdeaDetailModal.tsx +0 -190
- package/src/components/IdeaFormDialog.tsx +0 -113
- package/src/components/KanbanColumn.tsx +0 -201
- package/src/components/MarkdownRenderer.tsx +0 -114
- package/src/components/NeonGrid.tsx +0 -128
- package/src/components/PromotionQueue.tsx +0 -89
- package/src/components/ScopeCard.tsx +0 -234
- package/src/components/ScopeDetailModal.tsx +0 -255
- package/src/components/ScopeFilterBar.tsx +0 -152
- package/src/components/SearchInput.tsx +0 -102
- package/src/components/SessionPanel.tsx +0 -335
- package/src/components/SprintContainer.tsx +0 -303
- package/src/components/SprintDependencyDialog.tsx +0 -78
- package/src/components/SprintPreflightModal.tsx +0 -138
- package/src/components/StatusBar.tsx +0 -168
- package/src/components/SwimCell.tsx +0 -67
- package/src/components/SwimLaneRow.tsx +0 -94
- package/src/components/SwimlaneBoardView.tsx +0 -108
- package/src/components/VersionBadge.tsx +0 -139
- package/src/components/ViewModeSelector.tsx +0 -114
- package/src/components/config/AgentChip.tsx +0 -53
- package/src/components/config/AgentCreateDialog.tsx +0 -321
- package/src/components/config/AgentEditor.tsx +0 -175
- package/src/components/config/DirectoryTree.tsx +0 -582
- package/src/components/config/FileEditor.tsx +0 -550
- package/src/components/config/HookChip.tsx +0 -50
- package/src/components/config/StageCard.tsx +0 -198
- package/src/components/config/TransitionZone.tsx +0 -173
- package/src/components/config/UnifiedWorkflowPipeline.tsx +0 -216
- package/src/components/config/WorkflowPipeline.tsx +0 -161
- package/src/components/source-control/BranchList.tsx +0 -93
- package/src/components/source-control/BranchPanel.tsx +0 -105
- package/src/components/source-control/CommitLog.tsx +0 -100
- package/src/components/source-control/CommitRow.tsx +0 -47
- package/src/components/source-control/GitHubPanel.tsx +0 -110
- package/src/components/source-control/GitHubSetupGuide.tsx +0 -52
- package/src/components/source-control/GitOverviewBar.tsx +0 -101
- package/src/components/source-control/PullRequestList.tsx +0 -69
- package/src/components/source-control/WorktreeList.tsx +0 -80
- package/src/components/ui/badge.tsx +0 -41
- package/src/components/ui/button.tsx +0 -55
- package/src/components/ui/card.tsx +0 -78
- package/src/components/ui/dialog.tsx +0 -94
- package/src/components/ui/popover.tsx +0 -33
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -52
- package/src/components/ui/toggle-switch.tsx +0 -35
- package/src/components/ui/tooltip.tsx +0 -27
- package/src/components/workflow/AddEdgeDialog.tsx +0 -217
- package/src/components/workflow/AddListDialog.tsx +0 -201
- package/src/components/workflow/ChecklistEditor.tsx +0 -239
- package/src/components/workflow/CommandPrefixManager.tsx +0 -118
- package/src/components/workflow/ConfigSettingsPanel.tsx +0 -189
- package/src/components/workflow/DirectionSelector.tsx +0 -133
- package/src/components/workflow/DispatchConfigPanel.tsx +0 -180
- package/src/components/workflow/EdgeDetailPanel.tsx +0 -236
- package/src/components/workflow/EdgePropertyEditor.tsx +0 -251
- package/src/components/workflow/EditToolbar.tsx +0 -138
- package/src/components/workflow/HookDetailPanel.tsx +0 -250
- package/src/components/workflow/HookExecutionLog.tsx +0 -24
- package/src/components/workflow/HookSourceModal.tsx +0 -129
- package/src/components/workflow/HooksDashboard.tsx +0 -363
- package/src/components/workflow/ListPropertyEditor.tsx +0 -251
- package/src/components/workflow/MigrationPreviewDialog.tsx +0 -237
- package/src/components/workflow/MovementRulesPanel.tsx +0 -188
- package/src/components/workflow/NodeDetailPanel.tsx +0 -245
- package/src/components/workflow/PresetSelector.tsx +0 -414
- package/src/components/workflow/SkillCommandBuilder.tsx +0 -174
- package/src/components/workflow/WorkflowEdgeComponent.tsx +0 -145
- package/src/components/workflow/WorkflowNode.tsx +0 -147
- package/src/components/workflow/graphLayout.ts +0 -186
- package/src/components/workflow/mergeHooks.ts +0 -85
- package/src/components/workflow/useEditHistory.ts +0 -88
- package/src/components/workflow/useWorkflowEditor.ts +0 -262
- package/src/components/workflow/validateConfig.ts +0 -70
- package/src/hooks/useActiveDispatches.ts +0 -198
- package/src/hooks/useBoardSettings.ts +0 -170
- package/src/hooks/useCardDisplay.ts +0 -57
- package/src/hooks/useCcHooks.ts +0 -24
- package/src/hooks/useConfigTree.ts +0 -51
- package/src/hooks/useEnforcementRules.ts +0 -46
- package/src/hooks/useEvents.ts +0 -59
- package/src/hooks/useFileEditor.ts +0 -165
- package/src/hooks/useGates.ts +0 -57
- package/src/hooks/useIdeaActions.ts +0 -53
- package/src/hooks/useKanbanDnd.ts +0 -410
- package/src/hooks/useOrbitalConfig.ts +0 -54
- package/src/hooks/usePipeline.ts +0 -47
- package/src/hooks/usePipelineData.ts +0 -338
- package/src/hooks/useReconnect.ts +0 -25
- package/src/hooks/useScopeFilters.ts +0 -125
- package/src/hooks/useScopeSessions.ts +0 -44
- package/src/hooks/useScopes.ts +0 -67
- package/src/hooks/useSearch.ts +0 -67
- package/src/hooks/useSettings.tsx +0 -187
- package/src/hooks/useSocket.ts +0 -25
- package/src/hooks/useSourceControl.ts +0 -105
- package/src/hooks/useSprintPreflight.ts +0 -55
- package/src/hooks/useSprints.ts +0 -154
- package/src/hooks/useStatusBarHighlight.ts +0 -18
- package/src/hooks/useSwimlaneBoardSettings.ts +0 -104
- package/src/hooks/useTheme.ts +0 -9
- package/src/hooks/useTransitionReadiness.ts +0 -53
- package/src/hooks/useVersion.ts +0 -155
- package/src/hooks/useViolations.ts +0 -65
- package/src/hooks/useWorkflow.tsx +0 -125
- package/src/hooks/useZoomModifier.ts +0 -19
- package/src/index.css +0 -797
- package/src/layouts/DashboardLayout.tsx +0 -113
- package/src/lib/collisionDetection.ts +0 -20
- package/src/lib/scope-fields.ts +0 -61
- package/src/lib/swimlane.ts +0 -146
- package/src/lib/utils.ts +0 -15
- package/src/main.tsx +0 -19
- package/src/socket.ts +0 -11
- package/src/types/index.ts +0 -497
- package/src/views/AgentFeed.tsx +0 -339
- package/src/views/DeployPipeline.tsx +0 -59
- package/src/views/EnforcementView.tsx +0 -378
- package/src/views/PrimitivesConfig.tsx +0 -500
- package/src/views/QualityGates.tsx +0 -1012
- package/src/views/ScopeBoard.tsx +0 -454
- package/src/views/SessionTimeline.tsx +0 -516
- package/src/views/Settings.tsx +0 -183
- package/src/views/SourceControl.tsx +0 -95
- package/src/views/WorkflowVisualizer.tsx +0 -382
- package/tailwind.config.js +0 -161
- package/tsconfig.json +0 -25
- package/vite.config.ts +0 -38
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { createLogger } from './utils/logger.js';
|
|
6
|
+
const log = createLogger('global-config');
|
|
7
|
+
// ─── Constants ──────────────────────────────────────────────
|
|
8
|
+
/** Global Orbital Command directory */
|
|
9
|
+
export const ORBITAL_HOME = path.join(os.homedir(), '.orbital');
|
|
10
|
+
/** Path to the global registry file */
|
|
11
|
+
export const REGISTRY_PATH = path.join(ORBITAL_HOME, 'config.json');
|
|
12
|
+
/** Path to global primitives directory */
|
|
13
|
+
export const GLOBAL_PRIMITIVES_DIR = path.join(ORBITAL_HOME, 'primitives');
|
|
14
|
+
/** Path to global workflow config */
|
|
15
|
+
export const GLOBAL_WORKFLOW_PATH = path.join(ORBITAL_HOME, 'workflow.json');
|
|
16
|
+
/** Import from shared — re-exported for convenience */
|
|
17
|
+
import { PROJECT_COLORS } from '../shared/project-colors.js';
|
|
18
|
+
export { PROJECT_COLORS };
|
|
19
|
+
// ─── Slug Generation ────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Generate a project slug from a filesystem path.
|
|
22
|
+
* Uses the directory basename, lowercased, with non-alphanumeric chars
|
|
23
|
+
* replaced by hyphens. Deduplicates against existing IDs with a 4-char hash.
|
|
24
|
+
*/
|
|
25
|
+
export function generateProjectId(projectRoot, existingIds) {
|
|
26
|
+
const base = path.basename(projectRoot)
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
29
|
+
.replace(/-+/g, '-')
|
|
30
|
+
.replace(/^-|-$/g, '');
|
|
31
|
+
const slug = base || 'project';
|
|
32
|
+
if (!existingIds.includes(slug))
|
|
33
|
+
return slug;
|
|
34
|
+
// Collision — append 4-char hash of the full path
|
|
35
|
+
const hash = crypto.createHash('sha256').update(projectRoot).digest('hex').slice(0, 4);
|
|
36
|
+
return `${slug}-${hash}`;
|
|
37
|
+
}
|
|
38
|
+
// ─── Registry I/O ───────────────────────────────────────────
|
|
39
|
+
/** Ensure ~/.orbital/ directory structure exists, including primitives subdirectories. */
|
|
40
|
+
export function ensureOrbitalHome() {
|
|
41
|
+
if (!fs.existsSync(ORBITAL_HOME)) {
|
|
42
|
+
fs.mkdirSync(ORBITAL_HOME, { recursive: true });
|
|
43
|
+
log.info('Created ~/.orbital/', { path: ORBITAL_HOME });
|
|
44
|
+
}
|
|
45
|
+
// Ensure primitives subdirectories exist so the global watcher can start
|
|
46
|
+
for (const sub of ['agents', 'skills', 'hooks', 'config']) {
|
|
47
|
+
const dir = path.join(GLOBAL_PRIMITIVES_DIR, sub);
|
|
48
|
+
if (!fs.existsSync(dir))
|
|
49
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Load the global registry. Creates default if missing. */
|
|
53
|
+
export function loadGlobalConfig() {
|
|
54
|
+
ensureOrbitalHome();
|
|
55
|
+
if (!fs.existsSync(REGISTRY_PATH)) {
|
|
56
|
+
const config = { version: 1, projects: [] };
|
|
57
|
+
saveGlobalConfig(config);
|
|
58
|
+
return config;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const raw = fs.readFileSync(REGISTRY_PATH, 'utf-8');
|
|
62
|
+
return JSON.parse(raw);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
log.error('Failed to read global config, creating fresh', { error: String(err) });
|
|
66
|
+
const config = { version: 1, projects: [] };
|
|
67
|
+
saveGlobalConfig(config);
|
|
68
|
+
return config;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Save the global registry atomically. */
|
|
72
|
+
export function saveGlobalConfig(config) {
|
|
73
|
+
ensureOrbitalHome();
|
|
74
|
+
const tmpPath = REGISTRY_PATH + '.tmp';
|
|
75
|
+
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
76
|
+
fs.renameSync(tmpPath, REGISTRY_PATH);
|
|
77
|
+
}
|
|
78
|
+
// ─── Project Registration ───────────────────────────────────
|
|
79
|
+
/** Pick the next unused color from the palette. */
|
|
80
|
+
function nextColor(usedColors) {
|
|
81
|
+
const available = PROJECT_COLORS.filter(c => !usedColors.includes(c));
|
|
82
|
+
return available[0] ?? PROJECT_COLORS[0];
|
|
83
|
+
}
|
|
84
|
+
/** Register a project in the global config. Returns the registration. */
|
|
85
|
+
export function registerProject(projectRoot, options) {
|
|
86
|
+
const absPath = path.resolve(projectRoot);
|
|
87
|
+
const config = loadGlobalConfig();
|
|
88
|
+
// Check if already registered
|
|
89
|
+
const existing = config.projects.find(p => p.path === absPath);
|
|
90
|
+
if (existing) {
|
|
91
|
+
log.info('Project already registered', { id: existing.id, path: absPath });
|
|
92
|
+
return existing;
|
|
93
|
+
}
|
|
94
|
+
const existingIds = config.projects.map(p => p.id);
|
|
95
|
+
const usedColors = config.projects.map(p => p.color);
|
|
96
|
+
const registration = {
|
|
97
|
+
id: generateProjectId(absPath, existingIds),
|
|
98
|
+
path: absPath,
|
|
99
|
+
name: options?.name ?? path.basename(absPath),
|
|
100
|
+
color: options?.color ?? nextColor(usedColors),
|
|
101
|
+
registeredAt: new Date().toISOString(),
|
|
102
|
+
enabled: true,
|
|
103
|
+
};
|
|
104
|
+
config.projects.push(registration);
|
|
105
|
+
saveGlobalConfig(config);
|
|
106
|
+
log.info('Project registered', { id: registration.id, path: absPath });
|
|
107
|
+
return registration;
|
|
108
|
+
}
|
|
109
|
+
/** Unregister a project by ID or path. Returns true if found. */
|
|
110
|
+
export function unregisterProject(idOrPath) {
|
|
111
|
+
const config = loadGlobalConfig();
|
|
112
|
+
const absPath = path.isAbsolute(idOrPath) ? idOrPath : path.resolve(idOrPath);
|
|
113
|
+
const idx = config.projects.findIndex(p => p.id === idOrPath || p.path === absPath);
|
|
114
|
+
if (idx === -1)
|
|
115
|
+
return false;
|
|
116
|
+
const removed = config.projects.splice(idx, 1)[0];
|
|
117
|
+
saveGlobalConfig(config);
|
|
118
|
+
log.info('Project unregistered', { id: removed.id, path: removed.path });
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/** Update a project's registration (color, name, enabled). */
|
|
122
|
+
export function updateProject(id, updates) {
|
|
123
|
+
const config = loadGlobalConfig();
|
|
124
|
+
const project = config.projects.find(p => p.id === id);
|
|
125
|
+
if (!project)
|
|
126
|
+
return null;
|
|
127
|
+
if (updates.name !== undefined)
|
|
128
|
+
project.name = updates.name;
|
|
129
|
+
if (updates.color !== undefined)
|
|
130
|
+
project.color = updates.color;
|
|
131
|
+
if (updates.enabled !== undefined)
|
|
132
|
+
project.enabled = updates.enabled;
|
|
133
|
+
saveGlobalConfig(config);
|
|
134
|
+
return project;
|
|
135
|
+
}
|
|
136
|
+
/** Get all registered projects. */
|
|
137
|
+
export function getRegisteredProjects() {
|
|
138
|
+
return loadGlobalConfig().projects;
|
|
139
|
+
}
|
|
140
|
+
/** Find a registered project by ID. */
|
|
141
|
+
export function findProject(id) {
|
|
142
|
+
return loadGlobalConfig().projects.find(p => p.id === id);
|
|
143
|
+
}
|
|
@@ -4,72 +4,36 @@ import { Server } from 'socket.io';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
-
import { getDatabase, closeDatabase } from './database.js';
|
|
8
|
-
import { getConfig, resetConfig } from './config.js';
|
|
9
|
-
import { ScopeCache } from './services/scope-cache.js';
|
|
10
|
-
import { ScopeService } from './services/scope-service.js';
|
|
11
|
-
import { EventService } from './services/event-service.js';
|
|
12
|
-
import { GateService } from './services/gate-service.js';
|
|
13
|
-
import { DeployService } from './services/deploy-service.js';
|
|
14
|
-
import { SprintService } from './services/sprint-service.js';
|
|
15
|
-
import { SprintOrchestrator } from './services/sprint-orchestrator.js';
|
|
16
|
-
import { BatchOrchestrator } from './services/batch-orchestrator.js';
|
|
17
|
-
import { ReadinessService } from './services/readiness-service.js';
|
|
18
|
-
import { startScopeWatcher } from './watchers/scope-watcher.js';
|
|
19
|
-
import { startEventWatcher } from './watchers/event-watcher.js';
|
|
20
|
-
import { ensureDynamicProfiles } from './utils/terminal-launcher.js';
|
|
21
|
-
import { syncClaudeSessionsToDB } from './services/claude-session-service.js';
|
|
22
|
-
import { resolveStaleDispatches, resolveActiveDispatchesForScope, resolveDispatchesByPid, resolveDispatchesByDispatchId, linkPidToDispatch } from './utils/dispatch-utils.js';
|
|
23
|
-
import { createScopeRoutes } from './routes/scope-routes.js';
|
|
24
|
-
import { createDataRoutes } from './routes/data-routes.js';
|
|
25
|
-
import { createDispatchRoutes } from './routes/dispatch-routes.js';
|
|
26
|
-
import { createSprintRoutes } from './routes/sprint-routes.js';
|
|
27
|
-
import { createWorkflowRoutes } from './routes/workflow-routes.js';
|
|
28
|
-
import { createConfigRoutes } from './routes/config-routes.js';
|
|
29
|
-
import { createGitRoutes } from './routes/git-routes.js';
|
|
30
7
|
import { createVersionRoutes } from './routes/version-routes.js';
|
|
31
|
-
import {
|
|
32
|
-
import { GitService } from './services/git-service.js';
|
|
33
|
-
import { GitHubService } from './services/github-service.js';
|
|
34
|
-
import { WorkflowEngine } from '../shared/workflow-engine.js';
|
|
35
|
-
import defaultWorkflow from '../shared/default-workflow.json' with { type: 'json' };
|
|
8
|
+
import { createAggregateRoutes } from './routes/aggregate-routes.js';
|
|
36
9
|
import { createLogger, setLogLevel } from './utils/logger.js';
|
|
37
|
-
// ─── Server
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
10
|
+
// ─── Central Server ─────────────────────────────────────────
|
|
11
|
+
import { ProjectManager } from './project-manager.js';
|
|
12
|
+
import { SyncService } from './services/sync-service.js';
|
|
13
|
+
import { startGlobalWatcher } from './watchers/global-watcher.js';
|
|
14
|
+
import { createSyncRoutes } from './routes/sync-routes.js';
|
|
15
|
+
import { seedGlobalPrimitives } from './init.js';
|
|
16
|
+
import { ensureOrbitalHome, loadGlobalConfig, registerProject as registerProjectGlobal, GLOBAL_PRIMITIVES_DIR, ORBITAL_HOME, } from './global-config.js';
|
|
17
|
+
export async function startCentralServer(overrides) {
|
|
18
|
+
ensureOrbitalHome();
|
|
45
19
|
const envLevel = process.env.ORBITAL_LOG_LEVEL;
|
|
46
20
|
if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {
|
|
47
21
|
setLogLevel(envLevel);
|
|
48
22
|
}
|
|
49
|
-
|
|
50
|
-
|
|
23
|
+
const log = createLogger('central');
|
|
24
|
+
const port = overrides?.port ?? (Number(process.env.ORBITAL_SERVER_PORT) || 4444);
|
|
25
|
+
const clientPort = overrides?.clientPort ?? (Number(process.env.ORBITAL_CLIENT_PORT) || 4445);
|
|
26
|
+
// Auto-register current project if registry is empty
|
|
27
|
+
const globalConfig = loadGlobalConfig();
|
|
28
|
+
if (globalConfig.projects.length === 0 && overrides?.autoRegisterPath) {
|
|
29
|
+
registerProjectGlobal(overrides.autoRegisterPath);
|
|
30
|
+
log.info('Auto-registered current project', { path: overrides.autoRegisterPath });
|
|
51
31
|
}
|
|
52
|
-
const log = createLogger('server');
|
|
53
|
-
const port = overrides?.port ?? config.serverPort;
|
|
54
|
-
const workflowEngine = new WorkflowEngine(defaultWorkflow);
|
|
55
|
-
// Generate shell manifest for bash hooks (config-driven lifecycle)
|
|
56
|
-
const MANIFEST_PATH = path.join(config.configDir, 'workflow-manifest.sh');
|
|
57
|
-
if (!fs.existsSync(config.configDir))
|
|
58
|
-
fs.mkdirSync(config.configDir, { recursive: true });
|
|
59
|
-
fs.writeFileSync(MANIFEST_PATH, workflowEngine.generateShellManifest(), 'utf-8');
|
|
60
|
-
const ICEBOX_DIR = path.join(config.scopesDir, 'icebox');
|
|
61
|
-
// Resolve path to the bundled default workflow config.
|
|
62
|
-
const __selfDir2 = path.dirname(fileURLToPath(import.meta.url));
|
|
63
|
-
const DEFAULT_CONFIG_PATH = path.resolve(__selfDir2, '../shared/default-workflow.json');
|
|
64
|
-
// Ensure icebox directory exists for idea files
|
|
65
|
-
if (!fs.existsSync(ICEBOX_DIR))
|
|
66
|
-
fs.mkdirSync(ICEBOX_DIR, { recursive: true });
|
|
67
32
|
const app = express();
|
|
68
33
|
const httpServer = createServer(app);
|
|
69
34
|
const io = new Server(httpServer, {
|
|
70
35
|
cors: {
|
|
71
36
|
origin: (origin, callback) => {
|
|
72
|
-
// Allow all localhost origins (dev tool, not production)
|
|
73
37
|
if (!origin || origin.startsWith('http://localhost:')) {
|
|
74
38
|
callback(null, true);
|
|
75
39
|
}
|
|
@@ -80,122 +44,70 @@ export async function startServer(overrides) {
|
|
|
80
44
|
methods: ['GET', 'POST'],
|
|
81
45
|
},
|
|
82
46
|
});
|
|
83
|
-
// Middleware
|
|
84
47
|
app.use(express.json());
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const sprintOrchestrator = new SprintOrchestrator(db, io, sprintService, scopeService, workflowEngine);
|
|
95
|
-
const batchOrchestrator = new BatchOrchestrator(db, io, sprintService, scopeService, workflowEngine);
|
|
96
|
-
const readinessService = new ReadinessService(scopeService, gateService, workflowEngine, config.projectRoot);
|
|
97
|
-
const workflowService = new WorkflowService(config.configDir, workflowEngine, config.scopesDir, DEFAULT_CONFIG_PATH);
|
|
98
|
-
workflowService.setSocketServer(io);
|
|
99
|
-
// Ensure in-memory engine reflects the actual active config (may differ from bundled default
|
|
100
|
-
// if the user applied a custom preset)
|
|
101
|
-
workflowEngine.reload(workflowService.getActive());
|
|
102
|
-
const gitService = new GitService(config.projectRoot, scopeCache);
|
|
103
|
-
const githubService = new GitHubService(config.projectRoot);
|
|
104
|
-
// Wire active-group guard into scope service (blocks manual moves for scopes in active batches/sprints)
|
|
105
|
-
scopeService.setActiveGroupCheck((scopeId) => sprintService.getActiveGroupForScope(scopeId));
|
|
106
|
-
// ─── Event Wiring ──────────────────────────────────────────
|
|
107
|
-
function inferScopeStatus(eventType, scopeId, data) {
|
|
108
|
-
if (scopeId == null)
|
|
109
|
-
return;
|
|
110
|
-
const id = Number(scopeId);
|
|
111
|
-
if (isNaN(id) || id <= 0)
|
|
112
|
-
return;
|
|
113
|
-
// Don't infer status for icebox idea cards
|
|
114
|
-
const current = scopeService.getById(id);
|
|
115
|
-
if (current?.status === 'icebox')
|
|
116
|
-
return;
|
|
117
|
-
const currentStatus = current?.status ?? '';
|
|
118
|
-
const result = workflowEngine.inferStatus(eventType, currentStatus, data);
|
|
119
|
-
if (result === null)
|
|
120
|
-
return;
|
|
121
|
-
// Handle dispatch resolution (AGENT_COMPLETED with outcome)
|
|
122
|
-
if (typeof result === 'object' && 'dispatchResolution' in result) {
|
|
123
|
-
resolveActiveDispatchesForScope(db, io, id, result.resolution);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
scopeService.updateStatus(id, result, 'event');
|
|
127
|
-
}
|
|
128
|
-
eventService.onIngest((eventType, scopeId, data) => {
|
|
129
|
-
// Handle SESSION_START: link PID to dispatch via dispatch_id env var
|
|
130
|
-
if (eventType === 'SESSION_START' && typeof data.dispatch_id === 'string' && typeof data.pid === 'number') {
|
|
131
|
-
linkPidToDispatch(db, data.dispatch_id, data.pid);
|
|
132
|
-
log.info('SESSION_START: linked PID to dispatch', { pid: data.pid, dispatch_id: data.dispatch_id });
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
// Handle SESSION_END: resolve dispatches by dispatch_id (preferred) or PID (fallback)
|
|
136
|
-
if (eventType === 'SESSION_END') {
|
|
137
|
-
let count = 0;
|
|
138
|
-
if (typeof data.dispatch_id === 'string') {
|
|
139
|
-
count = resolveDispatchesByDispatchId(db, io, data.dispatch_id);
|
|
140
|
-
if (count > 0) {
|
|
141
|
-
log.info('SESSION_END: resolved dispatches', { count, dispatch_id: data.dispatch_id });
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// PID fallback for old hooks without dispatch_id
|
|
145
|
-
if (count === 0 && typeof data.pid === 'number') {
|
|
146
|
-
count = resolveDispatchesByPid(db, io, data.pid);
|
|
147
|
-
if (count > 0) {
|
|
148
|
-
log.info('SESSION_END: resolved dispatches by PID fallback', { count, pid: data.pid });
|
|
149
|
-
}
|
|
48
|
+
// ─── Bind port early ──────────────────────────────────────
|
|
49
|
+
// Listen before async init so Vite's proxy doesn't get ECONNREFUSED
|
|
50
|
+
const actualPort = await new Promise((resolve, reject) => {
|
|
51
|
+
let attempt = 0;
|
|
52
|
+
const maxAttempts = 10;
|
|
53
|
+
httpServer.on('error', (err) => {
|
|
54
|
+
if (err.code === 'EADDRINUSE' && attempt < maxAttempts) {
|
|
55
|
+
attempt++;
|
|
56
|
+
httpServer.listen(port + attempt);
|
|
150
57
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (count > 0) {
|
|
154
|
-
batchOrchestrator.resolveStaleBatches();
|
|
58
|
+
else {
|
|
59
|
+
reject(new Error(`Failed to start server: ${err.message}`));
|
|
155
60
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
sprintOrchestrator.onScopeReachedDev(scopeId);
|
|
163
|
-
}
|
|
164
|
-
// Batch orchestrator tracks all status transitions (dev, staging, production)
|
|
165
|
-
batchOrchestrator.onScopeStatusChanged(scopeId, newStatus);
|
|
61
|
+
});
|
|
62
|
+
httpServer.on('listening', () => {
|
|
63
|
+
const addr = httpServer.address();
|
|
64
|
+
resolve(typeof addr === 'object' && addr ? addr.port : port);
|
|
65
|
+
});
|
|
66
|
+
httpServer.listen(port);
|
|
166
67
|
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
68
|
+
// Initialize ProjectManager and boot all registered projects
|
|
69
|
+
const projectManager = new ProjectManager(io);
|
|
70
|
+
await projectManager.initializeAll();
|
|
71
|
+
// Seed global primitives if empty (lazy fallback for first launch)
|
|
72
|
+
const globalPrimitivesEmpty = ['agents', 'skills', 'hooks'].every(t => {
|
|
73
|
+
const dir = path.join(GLOBAL_PRIMITIVES_DIR, t);
|
|
74
|
+
return !fs.existsSync(dir) || fs.readdirSync(dir).filter(f => !f.startsWith('.')).length === 0;
|
|
171
75
|
});
|
|
172
|
-
|
|
76
|
+
if (globalPrimitivesEmpty) {
|
|
77
|
+
seedGlobalPrimitives();
|
|
78
|
+
log.info('Seeded global primitives from package templates');
|
|
79
|
+
}
|
|
80
|
+
// Initialize SyncService and global watcher
|
|
81
|
+
const syncService = new SyncService();
|
|
82
|
+
const globalWatcher = startGlobalWatcher(syncService, io);
|
|
83
|
+
// ─── Routes ──────────────────────────────────────────────
|
|
84
|
+
// Health check
|
|
173
85
|
app.get('/api/orbital/health', (_req, res) => {
|
|
174
86
|
res.json({ status: 'ok', uptime: process.uptime(), timestamp: new Date().toISOString() });
|
|
175
87
|
});
|
|
176
|
-
//
|
|
177
|
-
app.
|
|
178
|
-
res.json({
|
|
179
|
-
projectName: config.projectName,
|
|
180
|
-
categories: config.categories,
|
|
181
|
-
agents: config.agents,
|
|
182
|
-
serverPort: config.serverPort,
|
|
183
|
-
clientPort: config.clientPort,
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
app.use('/api/orbital', createScopeRoutes({ db, io, scopeService, readinessService, projectRoot: config.projectRoot, engine: workflowEngine }));
|
|
187
|
-
app.use('/api/orbital', createDataRoutes({ db, io, gateService, deployService, engine: workflowEngine, projectRoot: config.projectRoot, inferScopeStatus }));
|
|
188
|
-
app.use('/api/orbital', createDispatchRoutes({ db, io, scopeService, projectRoot: config.projectRoot, engine: workflowEngine }));
|
|
189
|
-
app.use('/api/orbital', createSprintRoutes({ sprintService, sprintOrchestrator, batchOrchestrator }));
|
|
190
|
-
app.use('/api/orbital', createWorkflowRoutes({ workflowService, projectRoot: config.projectRoot }));
|
|
191
|
-
app.use('/api/orbital', createConfigRoutes({ projectRoot: config.projectRoot, workflowService, io }));
|
|
192
|
-
app.use('/api/orbital', createGitRoutes({ gitService, githubService, engine: workflowEngine }));
|
|
88
|
+
// Project management + sync routes (top-level)
|
|
89
|
+
app.use('/api/orbital', createSyncRoutes({ syncService, projectManager }));
|
|
193
90
|
app.use('/api/orbital', createVersionRoutes({ io }));
|
|
194
|
-
//
|
|
195
|
-
|
|
91
|
+
// Per-project routes — dynamic middleware that resolves :projectId
|
|
92
|
+
app.use('/api/orbital/projects/:projectId', (req, res, next) => {
|
|
93
|
+
const projectId = req.params.projectId;
|
|
94
|
+
const router = projectManager.getRouter(projectId);
|
|
95
|
+
if (!router) {
|
|
96
|
+
const ctx = projectManager.getContext(projectId);
|
|
97
|
+
if (!ctx)
|
|
98
|
+
return res.status(404).json({ error: `Project '${projectId}' not found` });
|
|
99
|
+
return res.status(503).json({ error: `Project '${projectId}' is offline` });
|
|
100
|
+
}
|
|
101
|
+
router(req, res, next);
|
|
102
|
+
});
|
|
103
|
+
// Aggregate endpoints (cross-project)
|
|
104
|
+
app.use('/api/orbital', createAggregateRoutes({ projectManager, io, syncService }));
|
|
105
|
+
// ─── Static File Serving ─────────────────────────────────
|
|
196
106
|
const __selfDir = path.dirname(fileURLToPath(import.meta.url));
|
|
197
107
|
const distDir = path.resolve(__selfDir, '../dist');
|
|
198
|
-
|
|
108
|
+
const devMode = clientPort !== port;
|
|
109
|
+
const hasBuiltFrontend = !devMode && fs.existsSync(path.join(distDir, 'index.html'));
|
|
110
|
+
if (hasBuiltFrontend) {
|
|
199
111
|
app.use(express.static(distDir));
|
|
200
112
|
app.get('*', (req, res, next) => {
|
|
201
113
|
if (req.path.startsWith('/api/') || req.path.startsWith('/socket.io'))
|
|
@@ -204,159 +116,89 @@ export async function startServer(overrides) {
|
|
|
204
116
|
});
|
|
205
117
|
}
|
|
206
118
|
else {
|
|
207
|
-
|
|
208
|
-
app.get('/', (_req, res) => res.redirect(`http://localhost:${config.clientPort}`));
|
|
119
|
+
app.get('/', (_req, res) => res.redirect(`http://localhost:${clientPort}`));
|
|
209
120
|
}
|
|
210
|
-
// ─── Socket.io
|
|
121
|
+
// ─── Socket.io ───────────────────────────────────────────
|
|
211
122
|
io.on('connection', (socket) => {
|
|
212
123
|
log.debug('Client connected', { socketId: socket.id });
|
|
213
|
-
socket.on('
|
|
214
|
-
|
|
124
|
+
socket.on('subscribe', (payload) => {
|
|
125
|
+
if (payload.scope === 'all') {
|
|
126
|
+
socket.join('all-projects');
|
|
127
|
+
}
|
|
128
|
+
else if (payload.projectId) {
|
|
129
|
+
socket.join(`project:${payload.projectId}`);
|
|
130
|
+
}
|
|
215
131
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
let scopeWatcher;
|
|
220
|
-
let eventWatcher;
|
|
221
|
-
let batchRecoveryInterval;
|
|
222
|
-
let staleCleanupInterval;
|
|
223
|
-
let sessionSyncInterval;
|
|
224
|
-
let gitPollInterval;
|
|
225
|
-
const actualPort = await new Promise((resolve, reject) => {
|
|
226
|
-
let attempt = 0;
|
|
227
|
-
const maxAttempts = 10;
|
|
228
|
-
httpServer.on('error', (err) => {
|
|
229
|
-
if (err.code === 'EADDRINUSE' && attempt < maxAttempts) {
|
|
230
|
-
attempt++;
|
|
231
|
-
const nextPort = port + attempt;
|
|
232
|
-
log.warn('Port in use, trying next', { tried: port + attempt - 1, next: nextPort });
|
|
233
|
-
httpServer.listen(nextPort);
|
|
132
|
+
socket.on('unsubscribe', (payload) => {
|
|
133
|
+
if (payload.scope === 'all') {
|
|
134
|
+
socket.leave('all-projects');
|
|
234
135
|
}
|
|
235
|
-
else {
|
|
236
|
-
|
|
136
|
+
else if (payload.projectId) {
|
|
137
|
+
socket.leave(`project:${payload.projectId}`);
|
|
237
138
|
}
|
|
238
139
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const listenPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
242
|
-
resolve(listenPort);
|
|
140
|
+
socket.on('disconnect', () => {
|
|
141
|
+
log.debug('Client disconnected', { socketId: socket.id });
|
|
243
142
|
});
|
|
244
|
-
httpServer.listen(port);
|
|
245
143
|
});
|
|
246
|
-
// ───
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
// Write iTerm2 dispatch profiles (idempotent, fire-and-forget)
|
|
255
|
-
ensureDynamicProfiles(workflowEngine);
|
|
256
|
-
// Start file watchers
|
|
257
|
-
scopeWatcher = startScopeWatcher(config.scopesDir, scopeService);
|
|
258
|
-
eventWatcher = startEventWatcher(config.eventsDir, eventService);
|
|
259
|
-
// Recover any active sprints/batches from before server restart
|
|
260
|
-
sprintOrchestrator.recoverActiveSprints().catch(err => log.error('Sprint recovery failed', { error: err.message }));
|
|
261
|
-
batchOrchestrator.recoverActiveBatches().catch(err => log.error('Batch recovery failed', { error: err.message }));
|
|
262
|
-
// Resolve stale batches on startup (catches stuck dispatches from previous runs)
|
|
263
|
-
const staleBatchesResolved = batchOrchestrator.resolveStaleBatches();
|
|
264
|
-
if (staleBatchesResolved > 0) {
|
|
265
|
-
log.info('Resolved stale batches', { count: staleBatchesResolved });
|
|
266
|
-
}
|
|
267
|
-
// Poll active batch PIDs every 30s for two-phase completion (B-1)
|
|
268
|
-
batchRecoveryInterval = setInterval(() => {
|
|
269
|
-
batchOrchestrator.recoverActiveBatches().catch(err => log.error('Batch recovery failed', { error: err.message }));
|
|
270
|
-
}, 30_000);
|
|
271
|
-
// Periodic stale dispatch + batch cleanup (crash recovery — catches SIGKILL'd sessions)
|
|
272
|
-
staleCleanupInterval = setInterval(() => {
|
|
273
|
-
const count = resolveStaleDispatches(db, io, scopeService, workflowEngine);
|
|
274
|
-
if (count > 0) {
|
|
275
|
-
log.info('Periodic cleanup: resolved stale dispatches', { count });
|
|
144
|
+
// ─── Error Handling Middleware ─────────────────────────────
|
|
145
|
+
// Catches unhandled errors thrown from route handlers.
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
147
|
+
app.use((err, _req, res, _next) => {
|
|
148
|
+
log.error('Unhandled route error', { error: err.message, stack: err.stack });
|
|
149
|
+
if (!res.headersSent) {
|
|
150
|
+
res.status(500).json({ ok: false, error: 'Internal server error' });
|
|
276
151
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
// Sync frontmatter-derived sessions into DB (non-blocking)
|
|
283
|
-
syncClaudeSessionsToDB(db, scopeService).then((count) => {
|
|
284
|
-
log.info('Synced frontmatter sessions', { count });
|
|
285
|
-
// Purge legacy pattern-matched rows (no action = old regex system)
|
|
286
|
-
const purged = db.prepare("DELETE FROM sessions WHERE action IS NULL AND id LIKE 'claude-%'").run();
|
|
287
|
-
if (purged.changes > 0) {
|
|
288
|
-
log.info('Purged legacy pattern-matched session rows', { count: purged.changes });
|
|
289
|
-
}
|
|
290
|
-
}).catch(err => log.error('Session sync failed', { error: err.message }));
|
|
291
|
-
// Re-sync every 5 minutes so new sessions appear without restart
|
|
292
|
-
sessionSyncInterval = setInterval(() => {
|
|
293
|
-
syncClaudeSessionsToDB(db, scopeService)
|
|
294
|
-
.then((count) => {
|
|
295
|
-
if (count > 0)
|
|
296
|
-
io.emit('session:updated', { type: 'resync', count });
|
|
297
|
-
})
|
|
298
|
-
.catch(err => log.error('Session resync failed', { error: err.message }));
|
|
299
|
-
}, 5 * 60 * 1000);
|
|
300
|
-
// Poll git status every 10s — emit socket event on change
|
|
301
|
-
let lastGitHash = '';
|
|
302
|
-
gitPollInterval = setInterval(async () => {
|
|
303
|
-
try {
|
|
304
|
-
const hash = await gitService.getStatusHash();
|
|
305
|
-
if (lastGitHash && hash !== lastGitHash) {
|
|
306
|
-
gitService.clearCache();
|
|
307
|
-
io.emit('git:status:changed');
|
|
308
|
-
}
|
|
309
|
-
lastGitHash = hash;
|
|
310
|
-
}
|
|
311
|
-
catch { /* ok */ }
|
|
312
|
-
}, 10_000);
|
|
152
|
+
});
|
|
153
|
+
// ─── Startup Banner ──────────────────────────────────────
|
|
154
|
+
const projectList = projectManager.getProjectList();
|
|
155
|
+
const projectLines = projectList.map(p => `║ ${p.status === 'active' ? '●' : '○'} ${p.name.padEnd(20)} ${String(p.scopeCount).padStart(3)} scopes ${p.status.padEnd(8)} ║`).join('\n');
|
|
156
|
+
const dashboardPort = devMode ? clientPort : actualPort;
|
|
313
157
|
// eslint-disable-next-line no-console
|
|
314
158
|
console.log(`
|
|
315
159
|
╔══════════════════════════════════════════════════════╗
|
|
316
|
-
║ Orbital Command
|
|
317
|
-
║ ${config.projectName.padEnd(42)} ║
|
|
160
|
+
║ Orbital Command — Central Server ║
|
|
318
161
|
║ ║
|
|
319
|
-
║ >>> Open: http://localhost:${
|
|
162
|
+
║ >>> Open: http://localhost:${String(dashboardPort).padEnd(25)} <<<║
|
|
320
163
|
║ ║
|
|
321
164
|
╠══════════════════════════════════════════════════════╣
|
|
322
|
-
|
|
165
|
+
${projectLines}
|
|
166
|
+
╠══════════════════════════════════════════════════════╣
|
|
323
167
|
║ API: http://localhost:${actualPort}/api/orbital/* ║
|
|
324
168
|
║ Socket.io: ws://localhost:${actualPort} ║
|
|
169
|
+
║ Home: ${ORBITAL_HOME.padEnd(39)} ║
|
|
325
170
|
╚══════════════════════════════════════════════════════╝
|
|
326
171
|
`);
|
|
327
|
-
// ─── Graceful Shutdown
|
|
172
|
+
// ─── Graceful Shutdown ───────────────────────────────────
|
|
328
173
|
let shuttingDown = false;
|
|
329
|
-
function shutdown() {
|
|
174
|
+
async function shutdown() {
|
|
330
175
|
if (shuttingDown)
|
|
331
|
-
return
|
|
176
|
+
return;
|
|
332
177
|
shuttingDown = true;
|
|
333
|
-
log.info('Shutting down');
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
clearInterval(staleCleanupInterval);
|
|
338
|
-
clearInterval(sessionSyncInterval);
|
|
339
|
-
clearInterval(gitPollInterval);
|
|
178
|
+
log.info('Shutting down central server');
|
|
179
|
+
if (globalWatcher)
|
|
180
|
+
await globalWatcher.close();
|
|
181
|
+
await projectManager.shutdownAll();
|
|
340
182
|
return new Promise((resolve) => {
|
|
341
|
-
const forceTimeout = setTimeout(
|
|
342
|
-
closeDatabase();
|
|
343
|
-
resolve();
|
|
344
|
-
}, 2000);
|
|
183
|
+
const forceTimeout = setTimeout(resolve, 2000);
|
|
345
184
|
io.close(() => {
|
|
346
185
|
clearTimeout(forceTimeout);
|
|
347
|
-
closeDatabase();
|
|
348
186
|
resolve();
|
|
349
187
|
});
|
|
350
188
|
});
|
|
351
189
|
}
|
|
352
|
-
return { app, io,
|
|
190
|
+
return { app, io, projectManager, syncService, httpServer, shutdown };
|
|
353
191
|
}
|
|
354
192
|
// ─── Direct Execution (backward compat: tsx watch server/index.ts) ───
|
|
355
193
|
const isDirectRun = process.argv[1] && (process.argv[1].endsWith('server/index.ts') ||
|
|
356
194
|
process.argv[1].endsWith('server/index.js') ||
|
|
357
195
|
process.argv[1].endsWith('server'));
|
|
358
196
|
if (isDirectRun) {
|
|
359
|
-
|
|
197
|
+
const projectRoot = process.env.ORBITAL_PROJECT_ROOT || process.cwd();
|
|
198
|
+
startCentralServer({
|
|
199
|
+
port: Number(process.env.ORBITAL_SERVER_PORT) || 4444,
|
|
200
|
+
autoRegisterPath: projectRoot,
|
|
201
|
+
}).then(({ shutdown }) => {
|
|
360
202
|
process.on('SIGINT', async () => {
|
|
361
203
|
await shutdown();
|
|
362
204
|
process.exit(0);
|