@zaip-ai/cli 1.0.7 → 1.0.8
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 +6 -4
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +15 -5
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/adapters.test.js +1 -1
- package/dist/services/agent-adapters.d.ts +9 -0
- package/dist/services/agent-adapters.d.ts.map +1 -1
- package/dist/services/agent-adapters.js +38 -3
- package/dist/services/agent-adapters.js.map +1 -1
- package/dist/services/agent-adapters.test.js +8 -1
- package/dist/services/agent-adapters.test.js.map +1 -1
- package/dist/services/codex-config-template.d.ts +2 -1
- package/dist/services/codex-config-template.d.ts.map +1 -1
- package/dist/services/codex-config-template.js +13 -2
- package/dist/services/codex-config-template.js.map +1 -1
- package/dist/services/codex-rules.d.ts +1 -1
- package/dist/services/codex-rules.d.ts.map +1 -1
- package/dist/services/codex-rules.js +31 -7
- package/dist/services/codex-rules.js.map +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
- package/dist/services/codex-subagent-hook-template.js +4 -1
- package/dist/services/codex-subagent-hook-template.js.map +1 -1
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +3 -1
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-codex-config.d.ts +6 -3
- package/dist/services/global-codex-config.d.ts.map +1 -1
- package/dist/services/global-codex-config.js +82 -13
- package/dist/services/global-codex-config.js.map +1 -1
- package/dist/services/global-codex-config.test.js +57 -10
- package/dist/services/global-codex-config.test.js.map +1 -1
- package/dist/services/install-agents-template.d.ts.map +1 -1
- package/dist/services/install-agents-template.js +15 -2
- package/dist/services/install-agents-template.js.map +1 -1
- package/dist/services/logger.js +1 -1
- package/dist/services/logger.js.map +1 -1
- package/dist/services/logger.test.js +3 -3
- package/dist/services/logger.test.js.map +1 -1
- package/dist/services/runtime-template.test.js +21 -5
- package/dist/services/runtime-template.test.js.map +1 -1
- package/dist/services/template-manifest.d.ts.map +1 -1
- package/dist/services/template-manifest.js +2 -0
- package/dist/services/template-manifest.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/config/agents/catalog.yaml +310 -10
- package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +30 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +5 -5
- package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -1
- package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +30 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +30 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +30 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +8 -8
- package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +5 -3
- package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +30 -0
- package/templates/.agents/config/project.yaml +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +659 -163
- package/templates/.agents/dashboard/DATA-DICTIONARY.md +2227 -0
- package/templates/.agents/dashboard/README.md +48 -97
- package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-Z60df2r-.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-Cv-JcoxP.js → DashboardHelpDialog-Cs6dzDmW.js} +294 -109
- package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-Coc6pA-1.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-QE1k9yZB.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-_Pf5DA_A.js +3 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BVlz3DLZ.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CaKwQP43.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cf9ctPVt.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-C98LGC-Q.js → dashboard-localization-CLJ0lT9v.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/icons-vendor-DFBuc_qp.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8A4DcNXM.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/react-vendor-CCQOHdVI.js +9 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-D9-fqq9M.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DFY7ZDlu.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-pC2_0J8x.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +9 -2
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +952 -595
- package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +48 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +2 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +247 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +95 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppSetupRoutes.tsx +87 -0
- package/templates/.agents/dashboard/apps/frontend/src/CostCalculationGuidePage.tsx +641 -0
- package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +159 -0
- package/templates/.agents/dashboard/apps/frontend/src/DashboardCapabilityNav.tsx +13 -13
- package/templates/.agents/dashboard/apps/frontend/src/DashboardGlobalSettings.tsx +56 -0
- package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +257 -313
- package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +149 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +35 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +111 -12
- package/templates/.agents/dashboard/apps/frontend/src/app-preferences.ts +15 -0
- package/templates/.agents/dashboard/apps/frontend/src/app-session-token.ts +20 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.css +267 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.test.ts +69 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +70 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +111 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +432 -13
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.ts +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/main.tsx +1 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.test.ts +55 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.ts +36 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.test.ts +126 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.ts +181 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +138 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +105 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +6 -3
- package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +9 -5
- package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +24 -0
- package/templates/.agents/dashboard/apps/frontend/src/period-transition-overlay.css +44 -0
- package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +121 -62
- package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +471 -2
- package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +84 -17
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +44 -0
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +196 -39
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-notice.ts +15 -0
- package/templates/.agents/dashboard/apps/frontend/src/sqlite-cost-adapters.ts +26 -0
- package/templates/.agents/dashboard/apps/frontend/src/styles.css +14 -2
- package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +38 -2
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +187 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +190 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +121 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +398 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +141 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +45 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +46 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +37 -0
- package/templates/.agents/dashboard/apps/frontend/vite.config.ts +55 -5
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +47 -6
- package/templates/.agents/dashboard/modules/cost-monitor/src/duration-estimate.ts +13 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +64 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +275 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +100 -15
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +131 -40
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +137 -19
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.test.ts +2 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +13 -10
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +303 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +226 -6
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +479 -402
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostSummary.tsx +5 -13
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +312 -72
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +373 -321
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.test.ts +17 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.tsx +25 -6
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +0 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-helpers.ts +17 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-responsive.css +6 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-row-filters.ts +123 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-summary.ts +8 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +32 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +52 -14
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +121 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +3 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.test.ts +68 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +32 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +108 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +12 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +60 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +106 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.test.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +5 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-pricing-fallback.test.ts +53 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.basics.test.ts +34 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +168 -137
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +188 -29
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +956 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +775 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +95 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/index.ts +1 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/refactor-map-xr9l.json +61 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.test.ts +41 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.ts +55 -11
- package/templates/.agents/dashboard/modules/operations/package.json +2 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +182 -0
- package/templates/.agents/dashboard/modules/operations/src/duration-population.test.ts +63 -1
- package/templates/.agents/dashboard/modules/operations/src/duration-population.ts +60 -5
- package/templates/.agents/dashboard/modules/operations/src/react/EntityDetailsDialog.tsx +14 -3
- package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.test.ts +44 -1
- package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +325 -18
- package/templates/.agents/dashboard/modules/operations/src/react/MacroPlanningDialog.tsx +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.test.ts +9 -5
- package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.tsx +2 -2
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +168 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +95 -6
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +365 -14
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +532 -131
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +314 -358
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +68 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +169 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +51 -8
- package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.test.tsx +32 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.tsx +62 -6
- package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.test.ts +55 -0
- package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.tsx +27 -19
- package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +99 -42
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +19 -0
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +255 -34
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +163 -8
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +101 -68
- package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.test.ts +72 -1
- package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +78 -16
- package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +6 -4
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +50 -1
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +143 -9
- package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +29 -4
- package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog-content.css +49 -0
- package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog.css +93 -50
- package/templates/.agents/dashboard/modules/operations/src/react/manual-work-priority.test.ts +82 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +29 -2
- package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +22 -1
- package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +77 -4
- package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +65 -5
- package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +54 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +19 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +149 -12
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +27 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +136 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.test.ts +160 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.ts +47 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-query.ts +10 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-responsive.css +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-view-labels.ts +28 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +24 -7
- package/templates/.agents/dashboard/modules/operations/src/react/operations.css +42 -0
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.test.ts +54 -0
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.ts +32 -0
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +165 -6
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +148 -6
- package/templates/.agents/dashboard/modules/operations/src/react/popup-localization.test.ts +1 -1
- package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css +132 -0
- package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css.test.ts +13 -0
- package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.header.test.ts +16 -0
- package/templates/.agents/dashboard/modules/operations/src/react/use-infinite-column.ts +11 -5
- package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +5 -3
- package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +4 -83
- package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.test.ts +291 -15
- package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.ts +182 -472
- package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.test.ts +40 -0
- package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.ts +19 -6
- package/templates/.agents/dashboard/modules/operations/src/server/canonical-run-projection.ts +31 -6
- package/templates/.agents/dashboard/modules/operations/src/server/daily-duration-projection.ts +220 -0
- package/templates/.agents/dashboard/modules/operations/src/server/execution-duration-projection.ts +28 -0
- package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +27 -2
- package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +22 -13
- package/templates/.agents/dashboard/modules/operations/src/server/filter.test.ts +3 -3
- package/templates/.agents/dashboard/modules/operations/src/server/filter.ts +7 -2
- package/templates/.agents/dashboard/modules/operations/src/server/index.ts +3 -0
- package/templates/.agents/dashboard/modules/operations/src/server/macro.ts +3 -12
- package/templates/.agents/dashboard/modules/operations/src/server/manifest-usage.ts +65 -0
- package/templates/.agents/dashboard/modules/operations/src/server/operations-projection-aggregates.ts +297 -0
- package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +324 -0
- package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.test.ts +505 -0
- package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.ts +609 -0
- package/templates/.agents/dashboard/modules/operations/src/server/runs.ts +18 -19
- package/templates/.agents/dashboard/modules/operations/src/work-board-model.ts +76 -0
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/modules/traceability/src/server/build-task-trace.ts +3 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/src/index.ts +1 -0
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1756 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1608 -0
- package/templates/.agents/dashboard/packages/local-runtime/package.json +8 -3
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +12 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +83 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +28 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +262 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +41 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +105 -0
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1643 -107
- package/templates/.agents/dashboard/packages/local-runtime/src/agent-adapter-sync.ts +30 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +3 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.ts +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +72 -33
- package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +335 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +392 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/runtime.mjs +5 -5
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +71 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +179 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cross-layer-parity.test.ts +245 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models-route.ts +64 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.test.ts +204 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.ts +194 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.test.ts +163 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.ts +354 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.test.ts +74 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.ts +113 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-overview.test.ts +68 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period.ts +47 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +483 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-settings.test.ts +5 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +163 -84
- package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +145 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +551 -266
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +1145 -308
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.test.ts +53 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +708 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +2232 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +6151 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/work-priority-contract.test.ts +33 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-history.mjs +41 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-processes.mjs +210 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +437 -190
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +299 -24
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/packages/windows-launcher/LauncherVerificationBehaviorTests.cs +206 -0
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +95 -8
- package/templates/.agents/dashboard/packages/windows-launcher/build-dashboard-launcher.ps1 +2 -2
- package/templates/.agents/dashboard/packages/windows-launcher/run-launcher-verification-tests.ps1 +22 -0
- package/templates/.agents/dashboard/read-model.dbml +2201 -0
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +1 -1
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +214 -41
- package/templates/.agents/dashboard/work-priority.json +34 -0
- package/templates/.agents/dashboard/zaip-dashboard-linux.sh +45 -0
- package/templates/.agents/dashboard/zaip-dashboard-mac.command +3 -0
- package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +24 -0
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +4 -3
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +9 -6
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +9 -3
- package/templates/.agents/requirements/zaip-strategic-95.md +4 -4
- package/templates/.agents/rules/autonomy-decision.md +15 -0
- package/templates/.agents/rules/core.md +1 -1
- package/templates/.agents/rules/run-lifecycle-closure.md +1 -1
- package/templates/.agents/rules/specialist-routing.md +19 -0
- package/templates/.agents/rules/workflow.md +1 -1
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +114 -64
- package/templates/.agents/runtime/checksums.sha256 +6 -4
- package/templates/.agents/runtime/manifest.json +3 -1
- package/templates/.agents/runtime/pnpm-lock.sha256 +1 -0
- package/templates/.agents/runtime/provenance.json +11 -0
- package/templates/.agents/runtime/sbom.cdx.json +1929 -39
- package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +15 -0
- package/templates/.agents/skills/zaip-skill-brand-governance/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +30 -0
- package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +25 -0
- package/templates/.agents/skills/zaip-skill-dashboard-sqlite/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +18 -0
- package/templates/.agents/skills/zaip-skill-executar-fila-pronta/agents/openai.yaml +6 -0
- package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +71 -0
- package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +60 -0
- package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +111 -0
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +2 -2
- package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +3 -1
- package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-general-operations/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +9 -7
- package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +11 -38
- package/templates/.agents/skills/zaip-skill-governance-compliance/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-governance-compliance/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +15 -0
- package/templates/.agents/skills/zaip-skill-growth-strategy-revops/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-growth-strategy-revops/references/growth-revops-framework.md +25 -0
- package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +17 -0
- package/templates/.agents/skills/zaip-skill-instalar-agendamento/agents/openai.yaml +6 -0
- package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +3 -3
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +76 -27
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +128 -24
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +122 -4
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +66 -19
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +193 -7
- package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +14 -0
- package/templates/.agents/skills/zaip-skill-sales-methodology/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-sales-methodology/references/sales-process-framework.md +77 -0
- package/templates/.agents/skills/zaip-skill-setup/SKILL.md +5 -0
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +96 -0
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +18 -0
- package/templates/.agents/zaip-aios/INDEX.md +1 -0
- package/templates/.agents/zaip-aios/README.md +37 -8
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +43 -6
- package/templates/.agents/zaip-aios/guides/10-seguranca.md +21 -7
- package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +32 -0
- package/templates/template-manifest.json +945 -214
- package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-CLYIi7Vo.js +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-DTDNVQGX.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CzEggcRb.js +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-05YKn6Z4.js +0 -3
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-YO3yqtgV.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8AzCnl3a.js +0 -77
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DdBYQ_8D.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/src/AgentCatalogSettingsPage.tsx +0 -617
- package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings-page.css +0 -660
- package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings.test.ts +0 -249
- package/templates/.agents/dashboard/dashboard.config.json +0 -4
- package/templates/.agents/dashboard/zaip-dashboard.exe +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
-
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { appendFileSync, closeSync, mkdirSync, openSync } from "node:fs";
|
|
4
4
|
import {
|
|
5
5
|
access,
|
|
6
6
|
mkdir,
|
|
@@ -15,35 +15,57 @@ import {
|
|
|
15
15
|
import { createServer as createHttpServer } from "node:http";
|
|
16
16
|
import { createServer } from "node:net";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
|
-
import { dirname, join, resolve, win32 } from "node:path";
|
|
18
|
+
import { dirname, join, relative, resolve, win32 } from "node:path";
|
|
19
19
|
import process from "node:process";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { collectPreviousDashboardSessions } from "./start-dashboard-history.mjs";
|
|
22
|
+
import {
|
|
23
|
+
stopOwnedDashboardProcess,
|
|
24
|
+
stopProcessTree,
|
|
25
|
+
teardownWarning,
|
|
26
|
+
} from "./start-dashboard-processes.mjs";
|
|
21
27
|
|
|
28
|
+
export {
|
|
29
|
+
isAncestorProcess,
|
|
30
|
+
isProcessAlive,
|
|
31
|
+
registeredListenerMatches,
|
|
32
|
+
stopOwnedDashboardProcess,
|
|
33
|
+
teardownWarning,
|
|
34
|
+
} from "./start-dashboard-processes.mjs";
|
|
22
35
|
export const host = "127.0.0.1";
|
|
23
|
-
export const defaultPort =
|
|
36
|
+
export const defaultPort = 4417;
|
|
24
37
|
export const maxPortAttempts = 25;
|
|
25
|
-
export const runtimeKind = "zaip-dashboard-
|
|
26
|
-
|
|
38
|
+
export const runtimeKind = "zaip-dashboard-sqlite-runtime";
|
|
27
39
|
const packageRoot = dirname(fileURLToPath(import.meta.url));
|
|
28
40
|
export const workspaceRoot = resolve(packageRoot, "../../../..");
|
|
29
|
-
export const workspaceKey = createHash("sha256").update(workspaceRoot).digest("hex").slice(0, 16);
|
|
30
|
-
export const sessionDirectory = join(tmpdir(), "zaip-aios", workspaceKey);
|
|
31
|
-
export const sessionPath = join(sessionDirectory, "dashboard-session.json");
|
|
32
|
-
export const portRegistryPath = join(sessionDirectory, "dashboard-ports.json");
|
|
33
|
-
export const lockPath = join(sessionDirectory, "dashboard-launcher.lock");
|
|
34
|
-
export const launcherLogPath = join(sessionDirectory, "dashboard-launcher.log");
|
|
35
|
-
|
|
41
|
+
export const workspaceKey = createHash("sha256").update(`${workspaceRoot}:dashboard-sqlite`).digest("hex").slice(0, 16);
|
|
42
|
+
export const sessionDirectory = join(tmpdir(), "zaip-aios", `dashboard-sqlite-${workspaceKey}`);
|
|
43
|
+
export const sessionPath = join(sessionDirectory, "dashboard-sqlite-session.json");
|
|
44
|
+
export const portRegistryPath = join(sessionDirectory, "dashboard-sqlite-ports.json");
|
|
45
|
+
export const lockPath = join(sessionDirectory, "dashboard-sqlite-launcher.lock");
|
|
46
|
+
export const launcherLogPath = join(sessionDirectory, "dashboard-sqlite-launcher.log");
|
|
36
47
|
const frontendRoot = resolve(workspaceRoot, ".agents/dashboard/apps/frontend/dist");
|
|
48
|
+
const sqliteReadModelDirectory = resolve(workspaceRoot, ".local", "dashboard-sqlite");
|
|
49
|
+
// Keep the immutable seed cache in one durable workspace location. Runtime
|
|
50
|
+
// generations and the isolated background refresher must never derive a
|
|
51
|
+
// different cache directory from their temporary SQLite path.
|
|
52
|
+
const sqliteSnapshotCacheDirectory = join(sqliteReadModelDirectory, "cache");
|
|
53
|
+
// Node's bytecode cache is disposable runtime acceleration, never dashboard
|
|
54
|
+
// data. Keep it beside the private SQLite generations so fresh-port semantics
|
|
55
|
+
// and canonical sources remain unchanged.
|
|
56
|
+
const nodeCompileCacheDirectory = join(sqliteReadModelDirectory, "node-compile-cache");
|
|
37
57
|
const installedServer = resolve(packageRoot, "server.mjs");
|
|
38
|
-
const sourceServer = resolve(packageRoot, "dist/server.
|
|
58
|
+
const sourceServer = resolve(packageRoot, "dist/server.cjs");
|
|
39
59
|
const healthPath = "/api/v2/health";
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// and then coalesces with it instead of surfacing a spurious lock failure.
|
|
60
|
+
// Fresh-generation probe marker: changes the immutable source fingerprint only.
|
|
61
|
+
// Keep the preparation lock through a full build so concurrent clicks coalesce safely.
|
|
43
62
|
const lockTimeoutMs = 120_000;
|
|
44
63
|
const staleLockMs = 30_000;
|
|
45
64
|
const lockHeartbeatFloorMs = 10;
|
|
46
|
-
|
|
65
|
+
// A cold immutable seed can legitimately take ~176 s on a busy Windows
|
|
66
|
+
// volume. Keep the immediate preparation page and fresh-port handoff, but do
|
|
67
|
+
// not kill that one-time canonical build before it can populate the cache.
|
|
68
|
+
const startupTimeoutMs = 180_000;
|
|
47
69
|
const maxServerStartAttempts = 4;
|
|
48
70
|
const maxPortHistory = 50;
|
|
49
71
|
const dashboardSourceRoots = [
|
|
@@ -52,20 +74,16 @@ const dashboardSourceRoots = [
|
|
|
52
74
|
resolve(workspaceRoot, ".agents/dashboard/packages/ui/src"),
|
|
53
75
|
resolve(packageRoot, "src"),
|
|
54
76
|
];
|
|
55
|
-
|
|
56
77
|
export function preferredPort(value = process.env.ZAIP_SERVER_PORT) {
|
|
57
78
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
58
79
|
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535 ? parsed : defaultPort;
|
|
59
80
|
}
|
|
60
|
-
|
|
61
81
|
export function dashboardHealthUrl(port) {
|
|
62
82
|
return `http://${host}:${port}${healthPath}`;
|
|
63
83
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return `http://${host}:${port}/?token=${encodeURIComponent(token)}`;
|
|
84
|
+
export function dashboardUrl(port, token, snapshotUpdating = false) {
|
|
85
|
+
return `http://${host}:${port}/?token=${encodeURIComponent(token)}${snapshotUpdating ? "&snapshot=updating" : ""}`;
|
|
67
86
|
}
|
|
68
|
-
|
|
69
87
|
export function appendLauncherLog(message) {
|
|
70
88
|
try {
|
|
71
89
|
mkdirSync(sessionDirectory, { recursive: true });
|
|
@@ -78,7 +96,6 @@ export function appendLauncherLog(message) {
|
|
|
78
96
|
// Best-effort diagnostics must not hide the launcher failure.
|
|
79
97
|
}
|
|
80
98
|
}
|
|
81
|
-
|
|
82
99
|
export function stripTerminalControlCodes(value) {
|
|
83
100
|
const escapeCode = String.fromCharCode(27);
|
|
84
101
|
const bell = String.fromCharCode(7);
|
|
@@ -93,16 +110,37 @@ export function stripTerminalControlCodes(value) {
|
|
|
93
110
|
.replace(/\r/g, "")
|
|
94
111
|
.trim();
|
|
95
112
|
}
|
|
96
|
-
|
|
97
113
|
export function formatLauncherError(value, maximumLength = 3_600) {
|
|
98
114
|
const message = stripTerminalControlCodes(value);
|
|
99
115
|
if (message.length <= maximumLength) return message;
|
|
100
|
-
|
|
101
116
|
const firstHalf = Math.floor(maximumLength / 2);
|
|
102
117
|
const lastHalf = maximumLength - firstHalf;
|
|
103
118
|
return `${message.slice(0, firstHalf)}\n\n… detalhes reduzidos; consulte o log temporário …\n\n${message.slice(-lastHalf)}`;
|
|
104
119
|
}
|
|
105
|
-
|
|
120
|
+
export async function assertDashboardPrerequisites() {
|
|
121
|
+
const major = Number.parseInt(process.versions.node.split(".")[0] ?? "", 10);
|
|
122
|
+
if (!Number.isInteger(major) || major < 22)
|
|
123
|
+
throw new Error(
|
|
124
|
+
`O Dashboard SQLite requer Node.js 22 ou superior; a versão encontrada foi ${process.versions.node}. Instale o Node.js 22 e tente novamente.`,
|
|
125
|
+
);
|
|
126
|
+
try {
|
|
127
|
+
await import("node:sqlite");
|
|
128
|
+
} catch {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"O Node.js instalado não disponibiliza node:sqlite. Reinstale uma versão suportada do Node.js 22 ou superior.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
for (const required of [
|
|
134
|
+
resolve(workspaceRoot, ".agents/records"),
|
|
135
|
+
resolve(workspaceRoot, ".agents/config"),
|
|
136
|
+
resolve(workspaceRoot, "node_modules/esbuild"),
|
|
137
|
+
]) {
|
|
138
|
+
if (!(await exists(required)))
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Pré-requisito do Dashboard SQLite ausente: ${relative(workspaceRoot, required) || required}. Execute corepack pnpm install na raiz do ZAIP-AIOS e tente novamente.`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
106
144
|
export async function readDashboardSession(path = sessionPath) {
|
|
107
145
|
try {
|
|
108
146
|
return JSON.parse(await readFile(path, "utf8"));
|
|
@@ -110,11 +148,9 @@ export async function readDashboardSession(path = sessionPath) {
|
|
|
110
148
|
return null;
|
|
111
149
|
}
|
|
112
150
|
}
|
|
113
|
-
|
|
114
151
|
export async function writeDashboardSession(session, path = sessionPath) {
|
|
115
152
|
await writeJsonAtomic(path, session);
|
|
116
153
|
}
|
|
117
|
-
|
|
118
154
|
async function writeJsonAtomic(path, value) {
|
|
119
155
|
const directory = dirname(path);
|
|
120
156
|
await mkdir(directory, { recursive: true });
|
|
@@ -125,7 +161,6 @@ async function writeJsonAtomic(path, value) {
|
|
|
125
161
|
});
|
|
126
162
|
await rename(temporaryPath, path);
|
|
127
163
|
}
|
|
128
|
-
|
|
129
164
|
export async function readPortRegistry(path = portRegistryPath) {
|
|
130
165
|
try {
|
|
131
166
|
const registry = JSON.parse(await readFile(path, "utf8"));
|
|
@@ -141,7 +176,6 @@ export async function readPortRegistry(path = portRegistryPath) {
|
|
|
141
176
|
return [];
|
|
142
177
|
}
|
|
143
178
|
}
|
|
144
|
-
|
|
145
179
|
export async function recordPortStatus(session, status, path = portRegistryPath) {
|
|
146
180
|
const entries = await readPortRegistry(path);
|
|
147
181
|
const now = new Date().toISOString();
|
|
@@ -152,6 +186,7 @@ export async function recordPortStatus(session, status, path = portRegistryPath)
|
|
|
152
186
|
port: session.port,
|
|
153
187
|
rootPid: session.rootPid,
|
|
154
188
|
serverPid: session.serverPid ?? null,
|
|
189
|
+
dbPath: typeof session.dbPath === "string" ? session.dbPath : null,
|
|
155
190
|
openedAt: session.createdAt ?? session.openedAt ?? now,
|
|
156
191
|
status,
|
|
157
192
|
closedAt: status === "active" ? null : now,
|
|
@@ -166,12 +201,10 @@ export async function recordPortStatus(session, status, path = portRegistryPath)
|
|
|
166
201
|
ports: entries.slice(-maxPortHistory),
|
|
167
202
|
});
|
|
168
203
|
}
|
|
169
|
-
|
|
170
204
|
export async function clearOwnedDashboardSession(rootPid = process.pid, path = sessionPath) {
|
|
171
205
|
const session = await readDashboardSession(path);
|
|
172
206
|
if (session?.rootPid === rootPid) await rm(path, { force: true });
|
|
173
207
|
}
|
|
174
|
-
|
|
175
208
|
export async function probeDashboardPort(
|
|
176
209
|
port,
|
|
177
210
|
fetchImpl = fetch,
|
|
@@ -189,13 +222,13 @@ export async function probeDashboardPort(
|
|
|
189
222
|
return false;
|
|
190
223
|
}
|
|
191
224
|
}
|
|
192
|
-
|
|
193
225
|
export async function findReusableDashboardSession({
|
|
194
226
|
path = sessionPath,
|
|
195
227
|
startPort = preferredPort(),
|
|
196
228
|
fetchImpl = fetch,
|
|
197
229
|
expectedWorkspaceKey = workspaceKey,
|
|
198
230
|
expectedBuildSignature = null,
|
|
231
|
+
expectedSnapshotState = "current",
|
|
199
232
|
} = {}) {
|
|
200
233
|
const session = await readDashboardSession(path);
|
|
201
234
|
if (
|
|
@@ -204,7 +237,8 @@ export async function findReusableDashboardSession({
|
|
|
204
237
|
session?.workspaceKey !== expectedWorkspaceKey ||
|
|
205
238
|
typeof session?.token !== "string" ||
|
|
206
239
|
session.token.length === 0 ||
|
|
207
|
-
(expectedBuildSignature !== null && session?.buildSignature !== expectedBuildSignature)
|
|
240
|
+
(expectedBuildSignature !== null && session?.buildSignature !== expectedBuildSignature) ||
|
|
241
|
+
(expectedSnapshotState !== null && session?.snapshotState !== expectedSnapshotState)
|
|
208
242
|
) {
|
|
209
243
|
return null;
|
|
210
244
|
}
|
|
@@ -214,7 +248,6 @@ export async function findReusableDashboardSession({
|
|
|
214
248
|
) {
|
|
215
249
|
return session;
|
|
216
250
|
}
|
|
217
|
-
|
|
218
251
|
const ports = Array.from({ length: maxPortAttempts }, (_, index) => startPort + index).filter(
|
|
219
252
|
(port) => port <= 65_535 && port !== session.port,
|
|
220
253
|
);
|
|
@@ -226,7 +259,6 @@ export async function findReusableDashboardSession({
|
|
|
226
259
|
const port = matches.find((candidate) => candidate !== null);
|
|
227
260
|
return port === undefined ? null : { ...session, port };
|
|
228
261
|
}
|
|
229
|
-
|
|
230
262
|
async function latestModifiedAt(paths) {
|
|
231
263
|
let latest = 0;
|
|
232
264
|
for (const path of paths) {
|
|
@@ -246,15 +278,14 @@ async function latestModifiedAt(paths) {
|
|
|
246
278
|
}
|
|
247
279
|
return latest;
|
|
248
280
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const command = process.
|
|
252
|
-
const args =
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
: ["pnpm", "--filter", filter, "build"];
|
|
281
|
+
async function runSqliteBuild(target) {
|
|
282
|
+
const frontend = target === "frontend";
|
|
283
|
+
const command = process.execPath;
|
|
284
|
+
const args = frontend
|
|
285
|
+
? [resolve(workspaceRoot, "node_modules/vite/bin/vite.js"), "build"]
|
|
286
|
+
: [resolve(workspaceRoot, ".agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs")];
|
|
256
287
|
const output = await new Promise((resolveBuild, rejectBuild) => {
|
|
257
|
-
const child = spawn(command, args, { cwd: workspaceRoot, windowsHide: true, stdio: "pipe" });
|
|
288
|
+
const child = spawn(command, args, { cwd: frontend ? resolve(workspaceRoot, ".agents/dashboard/apps/frontend") : packageRoot, windowsHide: true, stdio: "pipe" });
|
|
258
289
|
let outputText = "";
|
|
259
290
|
const appendOutput = (chunk) => {
|
|
260
291
|
outputText = `${outputText}${chunk}`.slice(-8_000);
|
|
@@ -268,13 +299,12 @@ async function runWorkspaceBuild(filter) {
|
|
|
268
299
|
if (code === 0) resolveBuild();
|
|
269
300
|
else
|
|
270
301
|
rejectBuild(
|
|
271
|
-
new Error(`
|
|
302
|
+
new Error(`SQLite dashboard ${target} build failed (${code}): ${formatLauncherError(outputText)}`),
|
|
272
303
|
);
|
|
273
304
|
});
|
|
274
305
|
});
|
|
275
306
|
return output;
|
|
276
307
|
}
|
|
277
|
-
|
|
278
308
|
async function ensureFreshDashboardBuild() {
|
|
279
309
|
const artifacts = [resolve(frontendRoot, "index.html"), sourceServer];
|
|
280
310
|
const [sourcesModifiedAt, artifactsModifiedAt] = await Promise.all([
|
|
@@ -283,8 +313,8 @@ async function ensureFreshDashboardBuild() {
|
|
|
283
313
|
]);
|
|
284
314
|
const forceRebuild = process.env.ZAIP_DASHBOARD_FORCE_REBUILD === "1";
|
|
285
315
|
if (forceRebuild || artifactsModifiedAt < sourcesModifiedAt) {
|
|
286
|
-
await
|
|
287
|
-
await
|
|
316
|
+
await runSqliteBuild("frontend");
|
|
317
|
+
await runSqliteBuild("runtime");
|
|
288
318
|
}
|
|
289
319
|
const artifactFingerprint = await Promise.all(
|
|
290
320
|
[...artifacts, resolve(packageRoot, "start-dashboard.mjs")].map(async (path) => {
|
|
@@ -294,7 +324,6 @@ async function ensureFreshDashboardBuild() {
|
|
|
294
324
|
);
|
|
295
325
|
return createHash("sha256").update(artifactFingerprint.join("|")).digest("hex");
|
|
296
326
|
}
|
|
297
|
-
|
|
298
327
|
export async function withLauncherLock(
|
|
299
328
|
action,
|
|
300
329
|
{
|
|
@@ -333,7 +362,6 @@ export async function withLauncherLock(
|
|
|
333
362
|
}
|
|
334
363
|
throw new Error("Timed out waiting for the ZAIP dashboard launcher lock");
|
|
335
364
|
}
|
|
336
|
-
|
|
337
365
|
export async function findAvailablePort(startPort = preferredPort()) {
|
|
338
366
|
for (let port = startPort; port < Math.min(startPort + maxPortAttempts, 65_536); port += 1) {
|
|
339
367
|
if (await isPortFree(port)) return port;
|
|
@@ -342,7 +370,6 @@ export async function findAvailablePort(startPort = preferredPort()) {
|
|
|
342
370
|
`No free dashboard port found between ${startPort} and ${Math.min(startPort + maxPortAttempts - 1, 65_535)}`,
|
|
343
371
|
);
|
|
344
372
|
}
|
|
345
|
-
|
|
346
373
|
export async function findAvailablePortWithFallback({
|
|
347
374
|
preferred = preferredPort(),
|
|
348
375
|
excludedPorts = [],
|
|
@@ -364,7 +391,6 @@ export async function findAvailablePortWithFallback({
|
|
|
364
391
|
"No free dashboard port was found in the preferred, fallback, or ephemeral ranges",
|
|
365
392
|
);
|
|
366
393
|
}
|
|
367
|
-
|
|
368
394
|
async function findEphemeralPort() {
|
|
369
395
|
return new Promise((resolvePort, rejectPort) => {
|
|
370
396
|
const server = createServer();
|
|
@@ -380,7 +406,6 @@ async function findEphemeralPort() {
|
|
|
380
406
|
});
|
|
381
407
|
});
|
|
382
408
|
}
|
|
383
|
-
|
|
384
409
|
export function browserLaunchCommands(
|
|
385
410
|
target,
|
|
386
411
|
platform = process.platform,
|
|
@@ -401,7 +426,6 @@ export function browserLaunchCommands(
|
|
|
401
426
|
}
|
|
402
427
|
return [[platform === "darwin" ? "open" : "xdg-open", [target]]];
|
|
403
428
|
}
|
|
404
|
-
|
|
405
429
|
export async function openBrowser(target, spawnImpl = spawn) {
|
|
406
430
|
if (process.env.ZAIP_UI_NO_BROWSER === "1") return true;
|
|
407
431
|
const commands = browserLaunchCommands(target);
|
|
@@ -431,9 +455,14 @@ export async function openBrowser(target, spawnImpl = spawn) {
|
|
|
431
455
|
appendLauncherLog(`Could not dispatch the dashboard URL to the default browser: ${target}`);
|
|
432
456
|
return false;
|
|
433
457
|
}
|
|
434
|
-
|
|
435
458
|
export async function startDashboardLaunchPage(port) {
|
|
436
459
|
let redirectUrl = null;
|
|
460
|
+
let progress = {
|
|
461
|
+
percent: 4,
|
|
462
|
+
phase: "Iniciando o launcher local",
|
|
463
|
+
detail: "Abrindo a página de preparação.",
|
|
464
|
+
};
|
|
465
|
+
const connections = new Set();
|
|
437
466
|
let resolveRedirectObserved;
|
|
438
467
|
const redirectObserved = new Promise((resolveObserved) => {
|
|
439
468
|
resolveRedirectObserved = resolveObserved;
|
|
@@ -446,6 +475,11 @@ export async function startDashboardLaunchPage(port) {
|
|
|
446
475
|
response.end(JSON.stringify({ target: redirectUrl }));
|
|
447
476
|
return;
|
|
448
477
|
}
|
|
478
|
+
if (request.url?.startsWith("/api/v2/progress")) {
|
|
479
|
+
response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
480
|
+
response.end(JSON.stringify(progress));
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
449
483
|
if (request.url?.startsWith(healthPath)) {
|
|
450
484
|
response.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
|
|
451
485
|
response.end(JSON.stringify({ kind: "zaip-dashboard-launcher", status: "starting" }));
|
|
@@ -460,40 +494,43 @@ export async function startDashboardLaunchPage(port) {
|
|
|
460
494
|
<title>Dashboard Zaip AIOS</title>
|
|
461
495
|
<style>
|
|
462
496
|
:root{color-scheme:dark;font-family:Inter,system-ui,sans-serif;background:#010610;color:#f1f5f9}
|
|
463
|
-
*{box-sizing:border-box}body{margin:0;
|
|
464
|
-
|
|
465
|
-
.eyebrow,.line,.value,.action,.chart,.pill{display:block;position:relative;overflow:hidden;border-radius:999px;background:#172033}.eyebrow{width:110px;height:10px}.actions{display:flex;gap:10px}.action{width:48px;height:42px;border-radius:12px}
|
|
466
|
-
.kpis,.panels,.board{display:grid;gap:14px}.kpis{grid-template-columns:repeat(4,minmax(0,1fr))}.kpis article,.panels article,.board article{border:1px solid rgba(255,255,255,.08);border-radius:16px;background:linear-gradient(145deg,rgba(17,24,39,.86),rgba(10,14,23,.92))}
|
|
467
|
-
.kpis article{min-height:138px;padding:18px}.line{width:86%;height:10px;margin-top:12px}.value{width:42%;height:30px;margin-top:18px}.panels{grid-template-columns:1.7fr 1fr;margin-top:14px}.panels article{padding:18px}.chart{height:156px;margin-top:22px;border-radius:12px;background:repeating-linear-gradient(to bottom,rgba(51,65,85,.3) 0,rgba(51,65,85,.3) 1px,transparent 1px,transparent 38px)}
|
|
468
|
-
.board{grid-template-columns:repeat(5,minmax(210px,1fr));margin-top:18px;overflow:hidden}.board article{min-height:220px;padding:14px}.board h2{min-height:34px;margin:0 0 12px;color:#cbd5e1;font-size:.82rem}.card{min-height:92px;border:1px solid rgba(255,255,255,.06);border-radius:12px;padding:10px;background:rgba(2,6,23,.56)}.pill{width:52px;height:15px;margin-top:14px}
|
|
469
|
-
.eyebrow:after,.line:after,.value:after,.action:after,.chart:after,.pill:after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,transparent,rgba(125,211,252,.12),transparent);animation:shine 1.35s ease-in-out infinite;transform:translateX(-100%)}
|
|
470
|
-
@keyframes shine{to{transform:translateX(100%)}}@media(max-width:860px){.kpis{grid-template-columns:repeat(2,1fr)}.panels{grid-template-columns:1fr}.board{overflow-x:auto}}
|
|
497
|
+
*{box-sizing:border-box}body{margin:0;background:#010610}.launch-overlay{position:fixed;inset:0;width:100vw;height:100vh;display:grid;place-items:center;padding:clamp(18px,5vw,72px);background:radial-gradient(circle at 50% 35%,rgba(0,176,240,.11),transparent 42%),#010610}
|
|
498
|
+
.progress{width:min(100%,760px);padding:clamp(24px,4vw,42px);border:1px solid rgba(125,211,252,.2);border-radius:20px;background:rgba(3,9,22,.94);box-shadow:0 24px 80px rgba(0,0,0,.45);backdrop-filter:blur(10px)}.progress strong,.progress span{display:block}.progress strong{font-size:clamp(1.05rem,2vw,1.35rem)}.progress span{color:#b6c6db;font-size:clamp(.88rem,1.5vw,1rem);margin-top:7px}.track{height:9px;margin-top:20px;border-radius:999px;background:#172033;overflow:hidden}.bar{height:100%;width:4%;border-radius:inherit;background:linear-gradient(90deg,#38bdf8,#818cf8);transition:width .35s ease}.handoff{display:none;margin-top:16px;color:#dbeafe}.handoff a{color:#7dd3fc;font-weight:700}
|
|
471
499
|
</style>
|
|
472
500
|
</head>
|
|
473
501
|
<body>
|
|
474
|
-
<main aria-label="Preparando dashboard">
|
|
475
|
-
<
|
|
476
|
-
<section class="kpis">${Array.from({ length: 4 }, () => '<article><span class="line"></span><span class="value"></span><span class="line"></span></article>').join("")}</section>
|
|
477
|
-
<section class="panels"><article><span class="line"></span><div class="chart"></div></article><article><span class="line"></span><div class="chart"></div></article></section>
|
|
478
|
-
<section class="board">${["Definição", "Planejamento", "Pronto para Executar", "Execução", "Verificação"].map((label) => `<article><h2>${label}</h2><div class="card"><span class="line"></span><span class="line"></span><span class="pill"></span></div></article>`).join("")}</section>
|
|
502
|
+
<main class="launch-overlay" aria-label="Preparando dashboard">
|
|
503
|
+
<section class="progress" aria-live="polite" role="status"><strong id="phase">Iniciando o launcher local</strong><span id="detail">Abrindo a página de preparação.</span><div class="track"><div class="bar" id="bar"></div></div><span class="handoff" id="handoff">O dashboard está pronto. Se ele não abrir automaticamente, <a id="handoff-link" href="#">abra o dashboard agora</a>.</span></section>
|
|
479
504
|
</main>
|
|
480
505
|
<script>
|
|
506
|
+
let handoffTarget=null;
|
|
507
|
+
function showHandoff(target){handoffTarget=target;const box=document.getElementById("handoff");const link=document.getElementById("handoff-link");link.href=target;box.style.display="block";document.getElementById("phase").textContent="Dashboard pronto";document.getElementById("detail").textContent="Concluindo a abertura da tela principal."}
|
|
508
|
+
async function updateProgress(){try{const r=await fetch("/api/v2/progress",{cache:"no-store"});const p=await r.json();if(!handoffTarget){document.getElementById("phase").textContent=p.phase;document.getElementById("detail").textContent=p.detail};document.getElementById("bar").style.width=Math.max(4,Math.min(100,Number(p.percent)||4))+"%"}catch{}}
|
|
481
509
|
async function waitForDashboard(){
|
|
482
510
|
try{
|
|
511
|
+
await updateProgress();
|
|
483
512
|
const ready=await fetch("/api/v2/ready",{cache:"no-store"});
|
|
484
513
|
const next=await ready.json();
|
|
485
|
-
if(next.target){location.replace(next.target);return}
|
|
514
|
+
if(next.target){showHandoff(next.target);setTimeout(()=>location.replace(next.target),0);return}
|
|
486
515
|
const response=await fetch("${healthPath}",{cache:"no-store"});
|
|
487
516
|
const payload=await response.json();
|
|
488
517
|
if(response.ok&&payload.kind==="${runtimeKind}"){location.reload();return}
|
|
489
518
|
}catch{}
|
|
490
519
|
setTimeout(waitForDashboard,250)
|
|
491
520
|
}
|
|
492
|
-
setTimeout(waitForDashboard,100)
|
|
521
|
+
updateProgress();setTimeout(waitForDashboard,100)
|
|
522
|
+
// A browser extension or a failed navigation must never leave a silent
|
|
523
|
+
// skeleton. The visible link remains actionable while the temporary
|
|
524
|
+
// launcher server keeps serving this page through the handoff grace.
|
|
525
|
+
setTimeout(()=>{if(handoffTarget)showHandoff(handoffTarget)},4000)
|
|
493
526
|
</script>
|
|
494
527
|
</body>
|
|
495
528
|
</html>`);
|
|
496
529
|
});
|
|
530
|
+
server.on("connection", (connection) => {
|
|
531
|
+
connections.add(connection);
|
|
532
|
+
connection.once("close", () => connections.delete(connection));
|
|
533
|
+
});
|
|
497
534
|
await new Promise((resolveListen, rejectListen) => {
|
|
498
535
|
server.once("error", rejectListen);
|
|
499
536
|
server.listen({ host, port }, resolveListen);
|
|
@@ -506,25 +543,52 @@ export async function startDashboardLaunchPage(port) {
|
|
|
506
543
|
redirect: (target) => {
|
|
507
544
|
redirectUrl = target;
|
|
508
545
|
},
|
|
546
|
+
setProgress: (next) => {
|
|
547
|
+
progress = { ...progress, ...next, percent: Math.max(progress.percent, next.percent ?? progress.percent) };
|
|
548
|
+
},
|
|
509
549
|
waitForRedirect: async (timeoutMs = 1_500) =>
|
|
510
550
|
Promise.race([redirectObserved, delay(timeoutMs).then(() => false)]),
|
|
511
|
-
|
|
551
|
+
closeAfterHandoffGrace: (graceMs = 12_000) => {
|
|
552
|
+
const timer = setTimeout(() => {
|
|
553
|
+
void server.close();
|
|
554
|
+
server.closeAllConnections?.();
|
|
555
|
+
for (const connection of connections) connection.destroy();
|
|
556
|
+
}, graceMs);
|
|
557
|
+
timer.unref?.();
|
|
558
|
+
},
|
|
559
|
+
close: ({ forceAfterMs = 750 } = {}) =>
|
|
512
560
|
new Promise((resolveClose, rejectClose) => {
|
|
513
|
-
|
|
561
|
+
let forceTimer = null;
|
|
562
|
+
const finish = (error) => {
|
|
563
|
+
if (forceTimer !== null) clearTimeout(forceTimer);
|
|
564
|
+
if (error) rejectClose(error);
|
|
565
|
+
else resolveClose();
|
|
566
|
+
};
|
|
567
|
+
server.close(finish);
|
|
568
|
+
// The preparation page polls while redirecting. A browser keep-alive
|
|
569
|
+
// or an interrupted polling request must not keep this transient
|
|
570
|
+
// server alive indefinitely after its handoff was issued.
|
|
571
|
+
forceTimer = setTimeout(() => {
|
|
572
|
+
server.closeAllConnections?.();
|
|
573
|
+
for (const connection of connections) connection.destroy();
|
|
574
|
+
}, forceAfterMs);
|
|
575
|
+
forceTimer.unref?.();
|
|
514
576
|
}),
|
|
515
577
|
};
|
|
516
578
|
}
|
|
517
|
-
|
|
518
579
|
async function redirectLaunchPage(launchPage, target) {
|
|
519
580
|
launchPage.redirect(target);
|
|
520
581
|
const redirected =
|
|
521
582
|
process.env.ZAIP_UI_NO_BROWSER === "1" ? true : await launchPage.waitForRedirect();
|
|
522
|
-
await launchPage.close();
|
|
523
583
|
if (!redirected && !(await openBrowser(target))) {
|
|
524
584
|
throw new Error("Could not open the dashboard after the preparation page did not redirect.");
|
|
525
585
|
}
|
|
586
|
+
// Do not tear down the preparation page merely because its ready poll was
|
|
587
|
+
// observed: that happens before location.replace and used to strand users
|
|
588
|
+
// on the skeleton when navigation was blocked. Its watchdog/link remains
|
|
589
|
+
// available for a bounded grace period.
|
|
590
|
+
launchPage.closeAfterHandoffGrace();
|
|
526
591
|
}
|
|
527
|
-
|
|
528
592
|
async function assertRuntimeReady() {
|
|
529
593
|
const serverPath = (await exists(sourceServer)) ? sourceServer : installedServer;
|
|
530
594
|
if (!(await exists(serverPath))) {
|
|
@@ -539,53 +603,32 @@ async function assertRuntimeReady() {
|
|
|
539
603
|
}
|
|
540
604
|
return serverPath;
|
|
541
605
|
}
|
|
542
|
-
|
|
543
606
|
async function stopOwnedDashboard(session) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
return false;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
async function stopProcessTree(pid) {
|
|
556
|
-
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return;
|
|
557
|
-
if (process.platform === "win32") {
|
|
558
|
-
await new Promise((resolveStop) => {
|
|
559
|
-
const killer = spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
|
|
560
|
-
stdio: "ignore",
|
|
561
|
-
windowsHide: true,
|
|
562
|
-
});
|
|
563
|
-
killer.once("error", () => resolveStop());
|
|
564
|
-
killer.once("exit", () => resolveStop());
|
|
565
|
-
});
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
try {
|
|
569
|
-
process.kill(pid, "SIGTERM");
|
|
570
|
-
} catch {
|
|
571
|
-
// A stale recorded PID is already stopped.
|
|
572
|
-
}
|
|
607
|
+
return stopOwnedDashboardProcess(session, {
|
|
608
|
+
probeDashboardPort,
|
|
609
|
+
workspaceKey,
|
|
610
|
+
fetchImpl: fetch,
|
|
611
|
+
host,
|
|
612
|
+
packageRoot,
|
|
613
|
+
delay,
|
|
614
|
+
});
|
|
573
615
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
616
|
+
export async function previousDashboardSessions({
|
|
617
|
+
sessionPath: candidateSessionPath = sessionPath,
|
|
618
|
+
registryPath: candidateRegistryPath = portRegistryPath,
|
|
619
|
+
probe = (port) => probeDashboardPort(port, fetch, workspaceKey, false),
|
|
620
|
+
} = {}) {
|
|
621
|
+
return collectPreviousDashboardSessions({
|
|
622
|
+
candidateSessionPath,
|
|
623
|
+
candidateRegistryPath,
|
|
624
|
+
probe,
|
|
625
|
+
readDashboardSession,
|
|
626
|
+
readPortRegistry,
|
|
627
|
+
workspaceRoot,
|
|
628
|
+
workspaceKey,
|
|
629
|
+
});
|
|
586
630
|
}
|
|
587
|
-
|
|
588
|
-
async function startDashboardServer(serverPath, token, excludedPorts, initialPort = null) {
|
|
631
|
+
async function startDashboardServer(serverPath, token, excludedPorts, initialPort = null, onProgress = () => {}) {
|
|
589
632
|
const failures = [];
|
|
590
633
|
for (let attempt = 1; attempt <= maxServerStartAttempts; attempt += 1) {
|
|
591
634
|
const port =
|
|
@@ -593,7 +636,13 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
593
636
|
? initialPort
|
|
594
637
|
: await findAvailablePortWithFallback({ excludedPorts: [...excludedPorts] });
|
|
595
638
|
excludedPorts.add(port);
|
|
639
|
+
const dbPath = resolve(
|
|
640
|
+
sqliteReadModelDirectory,
|
|
641
|
+
`read-model-${port}-${process.pid}-${attempt}.sqlite`,
|
|
642
|
+
);
|
|
596
643
|
let stderr = "";
|
|
644
|
+
let startupTelemetryBuffer = "";
|
|
645
|
+
let snapshotCache = null;
|
|
597
646
|
const child = spawn(process.execPath, [serverPath], {
|
|
598
647
|
cwd: packageRoot,
|
|
599
648
|
env: {
|
|
@@ -602,6 +651,15 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
602
651
|
ZAIP_UI_ROOT: frontendRoot,
|
|
603
652
|
ZAIP_UI_TOKEN: token,
|
|
604
653
|
ZAIP_SERVER_PORT: String(port),
|
|
654
|
+
NODE_COMPILE_CACHE: nodeCompileCacheDirectory,
|
|
655
|
+
NODE_NO_WARNINGS: "1",
|
|
656
|
+
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "1",
|
|
657
|
+
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "1",
|
|
658
|
+
ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,
|
|
659
|
+
// A retry or a fresh-port handoff never reuses a live SQLite file.
|
|
660
|
+
// Windows keeps an open read-only handle locked, so each child owns a
|
|
661
|
+
// generation identified by launcher PID, attempt and selected port.
|
|
662
|
+
ZAIP_SQLITE_DB_PATH: dbPath,
|
|
605
663
|
},
|
|
606
664
|
stdio: ["ignore", "ignore", "pipe"],
|
|
607
665
|
windowsHide: true,
|
|
@@ -609,14 +667,50 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
609
667
|
child.stderr?.setEncoding("utf8");
|
|
610
668
|
child.stderr?.on("data", (chunk) => {
|
|
611
669
|
stderr = `${stderr}${chunk}`.slice(-4_000);
|
|
670
|
+
startupTelemetryBuffer = `${startupTelemetryBuffer}${String(chunk)}`;
|
|
671
|
+
const lines = startupTelemetryBuffer.split("\n");
|
|
672
|
+
startupTelemetryBuffer = lines.pop() ?? "";
|
|
673
|
+
for (const line of lines) {
|
|
674
|
+
try {
|
|
675
|
+
const event = JSON.parse(line);
|
|
676
|
+
if (event?.event === "dashboard_sqlite_rebuild_progress")
|
|
677
|
+
onProgress({
|
|
678
|
+
percent: event.percent,
|
|
679
|
+
phase: "Configurando o banco SQLite local",
|
|
680
|
+
detail: "Esta é a instalação/atualização da cópia local descartável; nenhum servidor externo é instalado.",
|
|
681
|
+
});
|
|
682
|
+
if (event?.event === "dashboard_sqlite_startup_phase" && event.phase === "main_entered")
|
|
683
|
+
onProgress({ percent: 50, phase: "Lendo os registros do projeto", detail: "Preparando o read model SQLite local." });
|
|
684
|
+
if (event?.event === "dashboard_sqlite_read_model") snapshotCache = event.cache;
|
|
685
|
+
if (event?.event === "dashboard_sqlite_read_model")
|
|
686
|
+
onProgress({ percent: 82, phase: "Banco local pronto", detail: event.cache === "rebuilt" ? "O read model SQLite foi reconstruído para os dados atuais." : "Uma cópia local do read model foi preparada." });
|
|
687
|
+
if (event?.event === "dashboard_sqlite_startup_phase" && event.phase === "listening")
|
|
688
|
+
onProgress({ percent: 88, phase: "Iniciando a interface", detail: "Verificando saúde e disponibilidade dos dados." });
|
|
689
|
+
} catch {
|
|
690
|
+
// Only structured startup telemetry affects the user-facing progress.
|
|
691
|
+
}
|
|
692
|
+
}
|
|
612
693
|
});
|
|
613
694
|
try {
|
|
614
695
|
await waitForDashboard(port, child);
|
|
615
|
-
|
|
696
|
+
const timings = stripTerminalControlCodes(stderr)
|
|
697
|
+
.split("\n")
|
|
698
|
+
.filter(
|
|
699
|
+
(line) =>
|
|
700
|
+
line.includes('"event":"dashboard_sqlite_read_model"') ||
|
|
701
|
+
line.includes('"event":"dashboard_sqlite_startup_phase"'),
|
|
702
|
+
);
|
|
703
|
+
for (const timing of timings) appendLauncherLog(timing);
|
|
704
|
+
return { child, port, dbPath, snapshotCache };
|
|
616
705
|
} catch (error) {
|
|
617
706
|
await stopProcessTree(child.pid);
|
|
618
|
-
|
|
619
|
-
|
|
707
|
+
await removeGeneratedReadModel(dbPath);
|
|
708
|
+
const detail = stripTerminalControlCodes(stderr)
|
|
709
|
+
.split("\n")
|
|
710
|
+
.filter((line) => !line.includes("ExperimentalWarning") && !line.includes("node --trace-warnings"))
|
|
711
|
+
.join("\n")
|
|
712
|
+
.trim();
|
|
713
|
+
failures.push(`porta ${port}: ${error.message}${detail ? ` — ${formatLauncherError(detail, 600)}` : ""}`);
|
|
620
714
|
appendLauncherLog(
|
|
621
715
|
`Dashboard start attempt ${attempt} failed on port ${port}: ${error.message}`,
|
|
622
716
|
);
|
|
@@ -628,6 +722,82 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
628
722
|
);
|
|
629
723
|
}
|
|
630
724
|
|
|
725
|
+
async function removeGeneratedReadModel(candidate) {
|
|
726
|
+
if (typeof candidate !== "string") return false;
|
|
727
|
+
const resolvedCandidate = resolve(candidate);
|
|
728
|
+
const fromReadModelDirectory = relative(sqliteReadModelDirectory, resolvedCandidate);
|
|
729
|
+
const generatedName = /^read-model-\d+-\d+-\d+\.sqlite$/;
|
|
730
|
+
if (
|
|
731
|
+
fromReadModelDirectory === "" ||
|
|
732
|
+
fromReadModelDirectory.startsWith("..") ||
|
|
733
|
+
fromReadModelDirectory.includes("/") ||
|
|
734
|
+
fromReadModelDirectory.includes("\\") ||
|
|
735
|
+
!generatedName.test(fromReadModelDirectory)
|
|
736
|
+
)
|
|
737
|
+
return false;
|
|
738
|
+
for (let attempt = 1; attempt <= 20; attempt += 1) {
|
|
739
|
+
try {
|
|
740
|
+
await rm(resolvedCandidate, { force: true });
|
|
741
|
+
return true;
|
|
742
|
+
} catch (error) {
|
|
743
|
+
if (error?.code !== "EBUSY" && error?.code !== "EPERM") throw error;
|
|
744
|
+
if (attempt < 20) await delay(250);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
appendLauncherLog(`Could not remove closed SQLite read model after retries: ${resolvedCandidate}`);
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
function refreshSnapshotSeedInBackground() {
|
|
751
|
+
const refreshPath = resolve(sqliteReadModelDirectory, `refresh-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`);
|
|
752
|
+
const refreshStatusPath = join(sessionDirectory, "dashboard-sqlite-refresh.json");
|
|
753
|
+
const refreshLogPath = join(sessionDirectory, "dashboard-sqlite-refresh.log");
|
|
754
|
+
mkdirSync(sessionDirectory, { recursive: true });
|
|
755
|
+
const refreshLogDescriptor = openSync(refreshLogPath, "a");
|
|
756
|
+
let worker;
|
|
757
|
+
try {
|
|
758
|
+
worker = spawn(process.execPath, [resolve(packageRoot, "scripts/rebuild-sqlite-read-model.mjs")], {
|
|
759
|
+
cwd: workspaceRoot,
|
|
760
|
+
env: {
|
|
761
|
+
...process.env,
|
|
762
|
+
ZAIP_SQLITE_DB_PATH: refreshPath,
|
|
763
|
+
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "0",
|
|
764
|
+
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "0",
|
|
765
|
+
ZAIP_SQLITE_REFRESH_CLEANUP: "1",
|
|
766
|
+
ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,
|
|
767
|
+
},
|
|
768
|
+
// A detached worker keeps its own append-only evidence file. Pipes can
|
|
769
|
+
// disappear with a fast launcher exit and silently abort the refresh.
|
|
770
|
+
stdio: ["ignore", refreshLogDescriptor, refreshLogDescriptor],
|
|
771
|
+
detached: true,
|
|
772
|
+
windowsHide: true,
|
|
773
|
+
});
|
|
774
|
+
} finally {
|
|
775
|
+
closeSync(refreshLogDescriptor);
|
|
776
|
+
}
|
|
777
|
+
void writeJsonAtomic(refreshStatusPath, {
|
|
778
|
+
status: "running",
|
|
779
|
+
pid: worker.pid ?? null,
|
|
780
|
+
path: refreshPath,
|
|
781
|
+
cacheDirectory: sqliteSnapshotCacheDirectory,
|
|
782
|
+
startedAt: new Date().toISOString(),
|
|
783
|
+
}).catch(() => {});
|
|
784
|
+
worker.once("error", (error) => {
|
|
785
|
+
appendLauncherLog(`Background SQLite seed refresh failed to start: ${error.message}`);
|
|
786
|
+
void writeJsonAtomic(refreshStatusPath, { status: "failed", error: error.message, finishedAt: new Date().toISOString() }).catch(() => {});
|
|
787
|
+
});
|
|
788
|
+
worker.once("exit", (code) => {
|
|
789
|
+
const status = code === 0 ? "completed" : "failed";
|
|
790
|
+
appendLauncherLog(`Background SQLite seed refresh ${status} (${code ?? "unknown"}); inspect ${refreshLogPath}.`);
|
|
791
|
+
void writeJsonAtomic(refreshStatusPath, {
|
|
792
|
+
status,
|
|
793
|
+
exitCode: code ?? null,
|
|
794
|
+
logPath: refreshLogPath,
|
|
795
|
+
finishedAt: new Date().toISOString(),
|
|
796
|
+
}).catch(() => {});
|
|
797
|
+
});
|
|
798
|
+
worker.unref();
|
|
799
|
+
appendLauncherLog(`Started background SQLite seed refresh at ${refreshPath}; cache ${sqliteSnapshotCacheDirectory}; worker log ${refreshLogPath}.`);
|
|
800
|
+
}
|
|
631
801
|
async function writeLauncherStatus(status, message = "") {
|
|
632
802
|
const path = process.env.ZAIP_LAUNCH_STATUS;
|
|
633
803
|
if (!path) return;
|
|
@@ -635,74 +805,142 @@ async function writeLauncherStatus(status, message = "") {
|
|
|
635
805
|
await writeFile(temporaryPath, `${status}\n${message}`.trimEnd(), "utf8");
|
|
636
806
|
await rename(temporaryPath, path);
|
|
637
807
|
}
|
|
638
|
-
|
|
808
|
+
function phaseTiming(phase, startedAt, details = {}) {
|
|
809
|
+
appendLauncherLog(JSON.stringify({ event: "dashboard_sqlite_phase", phase, elapsed_ms: Date.now() - startedAt, launcher_pid: process.pid, ...details }));
|
|
810
|
+
}
|
|
639
811
|
async function main() {
|
|
640
812
|
let ownedChild = null;
|
|
641
813
|
let currentSession = null;
|
|
642
814
|
let launchPage = null;
|
|
643
815
|
const token = process.env.ZAIP_UI_TOKEN ?? randomBytes(24).toString("base64url");
|
|
644
|
-
|
|
645
|
-
//
|
|
646
|
-
// launch
|
|
647
|
-
// preparation page
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const concurrentSession = await findReusableDashboardSession();
|
|
653
|
-
if (concurrentSession !== null && concurrentSession.rootPid !== observedRootPid) {
|
|
654
|
-
appendLauncherLog(
|
|
655
|
-
`Coalesced dashboard launcher request with healthy concurrent session on port ${concurrentSession.port}.`,
|
|
656
|
-
);
|
|
657
|
-
await writeLauncherStatus("ok");
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const launchPort = await findAvailablePortWithFallback();
|
|
662
|
-
launchPage = await startDashboardLaunchPage(launchPort);
|
|
816
|
+
const launchStartedAt = Date.now();
|
|
817
|
+
// Bind the preparation page atomically on an ephemeral port before waiting
|
|
818
|
+
// for the launch lock. Looking up a free port and binding it later lets a
|
|
819
|
+
// concurrent preparation page claim that port, which makes the runtime
|
|
820
|
+
// probe mistake its intentional 503 for a server failure.
|
|
821
|
+
launchPage = await startDashboardLaunchPage(0);
|
|
822
|
+
const launchPort = launchPage.port;
|
|
823
|
+
try {
|
|
663
824
|
if (!(await openBrowser(dashboardUrl(launchPort, token)))) {
|
|
664
|
-
await launchPage.close();
|
|
665
|
-
launchPage = null;
|
|
666
825
|
throw new Error("Could not open the dashboard preparation page in the default browser.");
|
|
667
826
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
827
|
+
launchPage.setProgress({
|
|
828
|
+
percent: 10,
|
|
829
|
+
phase: "Validando pré-requisitos locais",
|
|
830
|
+
detail: "Verificando Node.js 22 e o suporte a node:sqlite; isto não instala um banco de dados.",
|
|
831
|
+
});
|
|
832
|
+
await assertDashboardPrerequisites();
|
|
833
|
+
launchPage.setProgress({
|
|
834
|
+
percent: 18,
|
|
835
|
+
phase: "Preparando a sessão do dashboard",
|
|
836
|
+
detail: "Coordenando esta abertura local com segurança.",
|
|
837
|
+
});
|
|
838
|
+
await withLauncherLock(async () => {
|
|
839
|
+
phaseTiming("lock_acquired", launchStartedAt);
|
|
840
|
+
const buildStartedAt = Date.now();
|
|
841
|
+
launchPage.setProgress({
|
|
842
|
+
percent: 28,
|
|
843
|
+
phase: "Verificando os artefatos do dashboard",
|
|
844
|
+
detail: "Usando o build existente quando ele já está atualizado.",
|
|
845
|
+
});
|
|
846
|
+
const buildSignature = await ensureFreshDashboardBuild();
|
|
847
|
+
phaseTiming("build_complete", buildStartedAt, { build_signature: buildSignature });
|
|
848
|
+
// A second deliberate click must use the already healthy compatible
|
|
849
|
+
// runtime. Build signature makes this safe across dashboard updates;
|
|
850
|
+
// an incompatible session continues through a replacement handoff.
|
|
851
|
+
const reusable = await findReusableDashboardSession({
|
|
852
|
+
expectedBuildSignature: buildSignature,
|
|
853
|
+
expectedSnapshotState: "current",
|
|
854
|
+
});
|
|
855
|
+
if (reusable !== null) {
|
|
856
|
+
appendLauncherLog(`Reused healthy compatible dashboard session on port ${reusable.port}, PID ${reusable.serverPid ?? "unknown"}.`);
|
|
857
|
+
await redirectLaunchPage(launchPage, dashboardUrl(reusable.port, reusable.token));
|
|
858
|
+
launchPage = null;
|
|
859
|
+
await writeLauncherStatus("ok");
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const previousSessions = await previousDashboardSessions();
|
|
863
|
+
const excludedPorts = new Set([
|
|
864
|
+
...previousSessions.map((session) => session.port),
|
|
865
|
+
launchPort,
|
|
866
|
+
]);
|
|
867
|
+
const serverPath = await assertRuntimeReady();
|
|
868
|
+
const readyStartedAt = Date.now();
|
|
869
|
+
launchPage.setProgress({
|
|
870
|
+
percent: 42,
|
|
871
|
+
phase: "Abrindo o banco local descartável",
|
|
872
|
+
detail: "Criando uma cópia privada do read model SQLite para esta sessão.",
|
|
873
|
+
});
|
|
874
|
+
const { child, port, dbPath, snapshotCache } = await startDashboardServer(
|
|
875
|
+
serverPath,
|
|
685
876
|
token,
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
877
|
+
excludedPorts,
|
|
878
|
+
null,
|
|
879
|
+
(progress) => launchPage?.setProgress(progress),
|
|
880
|
+
);
|
|
881
|
+
phaseTiming("server_ready", readyStartedAt, { server_pid: child.pid, port, db_path: dbPath });
|
|
882
|
+
ownedChild = child;
|
|
883
|
+
try {
|
|
884
|
+
launchPage.setProgress({
|
|
885
|
+
percent: 92,
|
|
886
|
+
phase: snapshotCache === "stale_seed" ? "Abrindo snapshot local validado" : "Dashboard pronto",
|
|
887
|
+
detail: snapshotCache === "stale_seed"
|
|
888
|
+
? "Os dados canônicos estão sendo atualizados em segundo plano para a próxima abertura."
|
|
889
|
+
: "Validando saúde e dados iniciais antes de abrir a tela principal.",
|
|
890
|
+
});
|
|
891
|
+
if (snapshotCache === "stale_seed") await delay(350);
|
|
892
|
+
await redirectLaunchPage(launchPage, dashboardUrl(port, token, snapshotCache === "stale_seed"));
|
|
893
|
+
phaseTiming("handoff_complete", launchStartedAt, { server_pid: child.pid, port });
|
|
894
|
+
launchPage = null;
|
|
895
|
+
if (snapshotCache === "stale_seed")
|
|
896
|
+
refreshSnapshotSeedInBackground();
|
|
897
|
+
currentSession = {
|
|
898
|
+
kind: runtimeKind,
|
|
899
|
+
workspace: workspaceRoot,
|
|
900
|
+
workspaceKey,
|
|
901
|
+
rootPid: process.pid,
|
|
902
|
+
serverPid: child.pid,
|
|
903
|
+
port,
|
|
904
|
+
token,
|
|
905
|
+
dbPath,
|
|
906
|
+
buildSignature,
|
|
907
|
+
generationId: `${process.pid}-${child.pid}-${port}-${randomBytes(6).toString("hex")}`,
|
|
908
|
+
// Never pin subsequent clicks to a stale seed. A completed
|
|
909
|
+
// background refresh is consumed by a fresh handoff on the next
|
|
910
|
+
// click, then the old updating generation is retired below.
|
|
911
|
+
snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",
|
|
912
|
+
createdAt: new Date().toISOString(),
|
|
913
|
+
};
|
|
914
|
+
await writeDashboardSession(currentSession);
|
|
915
|
+
await recordPortStatus(currentSession, "active");
|
|
916
|
+
const warnings = [];
|
|
917
|
+
for (const previous of previousSessions) {
|
|
918
|
+
const status = await stopOwnedDashboard(previous);
|
|
919
|
+
await recordPortStatus(previous, status);
|
|
920
|
+
if (status === "closed") await removeGeneratedReadModel(previous.dbPath);
|
|
921
|
+
if (teardownWarning(status))
|
|
922
|
+
warnings.push(`não foi possível fechar a porta anterior ${previous.port}`);
|
|
923
|
+
else if (status === "skipped_unowned")
|
|
924
|
+
appendLauncherLog(
|
|
925
|
+
`Skipped teardown of port ${previous.port}: registered server PID does not own its live listener.`,
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
if (warnings.length > 0)
|
|
929
|
+
appendLauncherLog(
|
|
930
|
+
`Dashboard launched on port ${port}; prior-port cleanup: ${warnings.join("; ")}`,
|
|
931
|
+
);
|
|
932
|
+
await writeLauncherStatus("ok");
|
|
933
|
+
} catch (error) {
|
|
934
|
+
await stopProcessTree(child.pid);
|
|
935
|
+
if (currentSession !== null) await recordPortStatus(currentSession, "failed");
|
|
936
|
+
await clearOwnedDashboardSession();
|
|
937
|
+
throw error;
|
|
696
938
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
throw error;
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
|
|
939
|
+
});
|
|
940
|
+
} catch (error) {
|
|
941
|
+
if (launchPage !== null) await launchPage.close().catch(() => {});
|
|
942
|
+
throw error;
|
|
943
|
+
}
|
|
706
944
|
if (ownedChild === null) return;
|
|
707
945
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
708
946
|
process.once(signal, () => ownedChild?.kill(signal));
|
|
@@ -712,10 +950,12 @@ async function main() {
|
|
|
712
950
|
ownedChild.once("exit", (code) => resolveExit(code ?? 0));
|
|
713
951
|
});
|
|
714
952
|
await clearOwnedDashboardSession();
|
|
715
|
-
if (currentSession !== null)
|
|
953
|
+
if (currentSession !== null) {
|
|
954
|
+
await recordPortStatus(currentSession, "closed");
|
|
955
|
+
await removeGeneratedReadModel(currentSession.dbPath);
|
|
956
|
+
}
|
|
716
957
|
process.exitCode = exitCode;
|
|
717
958
|
}
|
|
718
|
-
|
|
719
959
|
function isPortFree(port) {
|
|
720
960
|
return new Promise((resolvePort) => {
|
|
721
961
|
const server = createServer();
|
|
@@ -724,7 +964,6 @@ function isPortFree(port) {
|
|
|
724
964
|
server.listen({ host, port }, () => server.close(() => resolvePort(true)));
|
|
725
965
|
});
|
|
726
966
|
}
|
|
727
|
-
|
|
728
967
|
async function waitForDashboard(port, child) {
|
|
729
968
|
const deadline = Date.now() + startupTimeoutMs;
|
|
730
969
|
while (Date.now() < deadline) {
|
|
@@ -735,7 +974,19 @@ async function waitForDashboard(port, child) {
|
|
|
735
974
|
const response = await fetch(dashboardHealthUrl(port), { signal: AbortSignal.timeout(750) });
|
|
736
975
|
const payload = await response.json().catch(() => null);
|
|
737
976
|
if (response.ok && payload?.kind === runtimeKind && payload?.workspaceKey === workspaceKey) {
|
|
738
|
-
|
|
977
|
+
const readiness = await fetch(`http://${host}:${port}/api/v2/ready`, {
|
|
978
|
+
signal: AbortSignal.timeout(750),
|
|
979
|
+
cache: "no-store",
|
|
980
|
+
});
|
|
981
|
+
if (readiness.ok) return;
|
|
982
|
+
if (readiness.status >= 500) {
|
|
983
|
+
const detail = await readiness.json().catch(() => null);
|
|
984
|
+
const unavailable = new Error(
|
|
985
|
+
`Dashboard data readiness returned ${readiness.status}: ${detail?.error ?? "projection unavailable"}`,
|
|
986
|
+
);
|
|
987
|
+
unavailable.retryable = false;
|
|
988
|
+
throw unavailable;
|
|
989
|
+
}
|
|
739
990
|
}
|
|
740
991
|
if (response.status >= 500) {
|
|
741
992
|
const unavailable = new Error(
|
|
@@ -747,26 +998,22 @@ async function waitForDashboard(port, child) {
|
|
|
747
998
|
} catch (error) {
|
|
748
999
|
if (error?.retryable === false) throw error;
|
|
749
1000
|
}
|
|
750
|
-
await delay(
|
|
1001
|
+
await delay(100);
|
|
751
1002
|
}
|
|
752
1003
|
throw new Error(`Timed out waiting for ${dashboardHealthUrl(port)}`);
|
|
753
1004
|
}
|
|
754
|
-
|
|
755
1005
|
function exists(path) {
|
|
756
1006
|
return access(path).then(
|
|
757
1007
|
() => true,
|
|
758
1008
|
() => false,
|
|
759
1009
|
);
|
|
760
1010
|
}
|
|
761
|
-
|
|
762
1011
|
function delay(milliseconds) {
|
|
763
1012
|
return new Promise((resolveDelay) => setTimeout(resolveDelay, milliseconds));
|
|
764
1013
|
}
|
|
765
|
-
|
|
766
1014
|
const isMain = process.argv[1]
|
|
767
1015
|
? resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
768
1016
|
: false;
|
|
769
|
-
|
|
770
1017
|
if (isMain) {
|
|
771
1018
|
main().catch(async (error) => {
|
|
772
1019
|
const logMessage = error instanceof Error ? (error.stack ?? error.message) : String(error);
|