@zaip-ai/cli 1.0.7 → 1.3.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 +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 +39 -4
- package/dist/services/agent-adapters.js.map +1 -1
- package/dist/services/agent-adapters.test.js +13 -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 +7 -0
- package/dist/services/template-manifest.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/architecture/software-architecture.md +131 -0
- package/templates/.agents/branding/brandbook.docx +0 -0
- package/templates/.agents/branding/brandbook.md +60 -0
- 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 +2 -2
- 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/.tmp-run-list.json +254 -0
- package/templates/.agents/dashboard/.zaip-managed.json +775 -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-DxR_pgSV.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-4jZLTWE_.js +2156 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CKcsqb-r.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-DmmJD2Th.js +3 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.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-N5Csr0RP.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-C0fj2uU9.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.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-BEsJ-FYH.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-B6y6Gw12.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DFY7ZDlu.css +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 +1129 -600
- 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 +266 -315
- package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +151 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +37 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +114 -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 +85 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +133 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +453 -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 +26 -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 +482 -3
- 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 +78 -0
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +234 -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 +253 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +380 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +175 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +395 -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 +36 -0
- package/templates/.agents/dashboard/apps/frontend/vite.config.ts +58 -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 +134 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +290 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +133 -19
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +134 -43
- 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 +328 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +302 -6
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +627 -400
- 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 +370 -72
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +467 -326
- 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 +55 -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 +33 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +119 -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 +146 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +118 -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 -161
- 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 +757 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +784 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +100 -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/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -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 +185 -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 +345 -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 +367 -14
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +562 -131
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +353 -355
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +65 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +169 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +73 -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 +105 -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 +265 -36
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +171 -8
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +98 -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 -17
- 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 +431 -1
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +574 -20
- package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +42 -4
- package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
- 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 +67 -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 +22 -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 +176 -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 +21 -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 +241 -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 +7 -4
- package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +5 -84
- 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 +39 -2
- package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +25 -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 +330 -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 +2 -0
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1759 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +7333 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1611 -0
- package/templates/.agents/dashboard/packages/local-runtime/package.json +8 -3
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +20 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +114 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +31 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +589 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +53 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +203 -0
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1646 -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 +339 -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 +110 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +189 -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 +178 -84
- package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +149 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +675 -267
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +1212 -309
- 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 +722 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +3743 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +7017 -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 +514 -190
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +324 -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 +216 -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/growth/estrategia-bootstrap-90-dias.md +169 -0
- package/templates/.agents/growth/strategy.md +27 -0
- package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
- package/templates/.agents/marketing/strategy.md +29 -0
- 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 +118 -67
- 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/sales/playbook.md +32 -0
- package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
- package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +22 -0
- package/templates/.agents/skills/zaip-skill-brand-governance/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -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-enforce-architecture/SKILL.md +2 -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 +21 -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 +9 -3
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +99 -93
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +132 -23
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +149 -1
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +76 -19
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +217 -7
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
- package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +20 -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 +41 -0
- package/templates/.agents/zaip-aios/INDEX.md +11 -0
- package/templates/.agents/zaip-aios/README.md +38 -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/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
- package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +72 -0
- package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +330 -0
- package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +52 -0
- package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +69 -0
- package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
- package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
- package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +18 -0
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +44 -0
- package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +167 -0
- package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +144 -0
- package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
- package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
- package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
- package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
- package/templates/template-manifest.json +1082 -215
- 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/DashboardHelpDialog-Cv-JcoxP.js +0 -1320
- 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,60 @@ 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")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export const
|
|
34
|
-
export const
|
|
35
|
-
|
|
41
|
+
export const workspaceKey = createHash("sha256")
|
|
42
|
+
.update(`${workspaceRoot}:dashboard-sqlite`)
|
|
43
|
+
.digest("hex")
|
|
44
|
+
.slice(0, 16);
|
|
45
|
+
export const sessionDirectory = join(tmpdir(), "zaip-aios", `dashboard-sqlite-${workspaceKey}`);
|
|
46
|
+
export const sessionPath = join(sessionDirectory, "dashboard-sqlite-session.json");
|
|
47
|
+
export const portRegistryPath = join(sessionDirectory, "dashboard-sqlite-ports.json");
|
|
48
|
+
export const lockPath = join(sessionDirectory, "dashboard-sqlite-launcher.lock");
|
|
49
|
+
export const launcherLogPath = join(sessionDirectory, "dashboard-sqlite-launcher.log");
|
|
36
50
|
const frontendRoot = resolve(workspaceRoot, ".agents/dashboard/apps/frontend/dist");
|
|
51
|
+
const sqliteReadModelDirectory = resolve(workspaceRoot, ".local", "dashboard-sqlite");
|
|
52
|
+
// Keep the immutable seed cache in one durable workspace location. Runtime
|
|
53
|
+
// generations and the isolated background refresher must never derive a
|
|
54
|
+
// different cache directory from their temporary SQLite path.
|
|
55
|
+
const sqliteSnapshotCacheDirectory = join(sqliteReadModelDirectory, "cache");
|
|
56
|
+
// Node's bytecode cache is disposable runtime acceleration, never dashboard
|
|
57
|
+
// data. Keep it beside the private SQLite generations so fresh-port semantics
|
|
58
|
+
// and canonical sources remain unchanged.
|
|
59
|
+
const nodeCompileCacheDirectory = join(sqliteReadModelDirectory, "node-compile-cache");
|
|
37
60
|
const installedServer = resolve(packageRoot, "server.mjs");
|
|
38
|
-
const sourceServer = resolve(packageRoot, "dist/server.
|
|
61
|
+
const sourceServer = resolve(packageRoot, "dist/server.cjs");
|
|
39
62
|
const healthPath = "/api/v2/health";
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// and then coalesces with it instead of surfacing a spurious lock failure.
|
|
63
|
+
// Fresh-generation probe marker: changes the immutable source fingerprint only.
|
|
64
|
+
// Keep the preparation lock through a full build so concurrent clicks coalesce safely.
|
|
43
65
|
const lockTimeoutMs = 120_000;
|
|
44
66
|
const staleLockMs = 30_000;
|
|
45
67
|
const lockHeartbeatFloorMs = 10;
|
|
46
|
-
|
|
68
|
+
// A cold immutable seed can legitimately take ~176 s on a busy Windows
|
|
69
|
+
// volume. Keep the immediate preparation page and fresh-port handoff, but do
|
|
70
|
+
// not kill that one-time canonical build before it can populate the cache.
|
|
71
|
+
const startupTimeoutMs = 180_000;
|
|
47
72
|
const maxServerStartAttempts = 4;
|
|
48
73
|
const maxPortHistory = 50;
|
|
49
74
|
const dashboardSourceRoots = [
|
|
@@ -52,20 +77,16 @@ const dashboardSourceRoots = [
|
|
|
52
77
|
resolve(workspaceRoot, ".agents/dashboard/packages/ui/src"),
|
|
53
78
|
resolve(packageRoot, "src"),
|
|
54
79
|
];
|
|
55
|
-
|
|
56
80
|
export function preferredPort(value = process.env.ZAIP_SERVER_PORT) {
|
|
57
81
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
58
82
|
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535 ? parsed : defaultPort;
|
|
59
83
|
}
|
|
60
|
-
|
|
61
84
|
export function dashboardHealthUrl(port) {
|
|
62
85
|
return `http://${host}:${port}${healthPath}`;
|
|
63
86
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return `http://${host}:${port}/?token=${encodeURIComponent(token)}`;
|
|
87
|
+
export function dashboardUrl(port, token, snapshotUpdating = false) {
|
|
88
|
+
return `http://${host}:${port}/?token=${encodeURIComponent(token)}${snapshotUpdating ? "&snapshot=updating" : ""}`;
|
|
67
89
|
}
|
|
68
|
-
|
|
69
90
|
export function appendLauncherLog(message) {
|
|
70
91
|
try {
|
|
71
92
|
mkdirSync(sessionDirectory, { recursive: true });
|
|
@@ -78,7 +99,6 @@ export function appendLauncherLog(message) {
|
|
|
78
99
|
// Best-effort diagnostics must not hide the launcher failure.
|
|
79
100
|
}
|
|
80
101
|
}
|
|
81
|
-
|
|
82
102
|
export function stripTerminalControlCodes(value) {
|
|
83
103
|
const escapeCode = String.fromCharCode(27);
|
|
84
104
|
const bell = String.fromCharCode(7);
|
|
@@ -93,16 +113,37 @@ export function stripTerminalControlCodes(value) {
|
|
|
93
113
|
.replace(/\r/g, "")
|
|
94
114
|
.trim();
|
|
95
115
|
}
|
|
96
|
-
|
|
97
116
|
export function formatLauncherError(value, maximumLength = 3_600) {
|
|
98
117
|
const message = stripTerminalControlCodes(value);
|
|
99
118
|
if (message.length <= maximumLength) return message;
|
|
100
|
-
|
|
101
119
|
const firstHalf = Math.floor(maximumLength / 2);
|
|
102
120
|
const lastHalf = maximumLength - firstHalf;
|
|
103
121
|
return `${message.slice(0, firstHalf)}\n\n… detalhes reduzidos; consulte o log temporário …\n\n${message.slice(-lastHalf)}`;
|
|
104
122
|
}
|
|
105
|
-
|
|
123
|
+
export async function assertDashboardPrerequisites() {
|
|
124
|
+
const major = Number.parseInt(process.versions.node.split(".")[0] ?? "", 10);
|
|
125
|
+
if (!Number.isInteger(major) || major < 22)
|
|
126
|
+
throw new Error(
|
|
127
|
+
`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.`,
|
|
128
|
+
);
|
|
129
|
+
try {
|
|
130
|
+
await import("node:sqlite");
|
|
131
|
+
} catch {
|
|
132
|
+
throw new Error(
|
|
133
|
+
"O Node.js instalado não disponibiliza node:sqlite. Reinstale uma versão suportada do Node.js 22 ou superior.",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
for (const required of [
|
|
137
|
+
resolve(workspaceRoot, ".agents/records"),
|
|
138
|
+
resolve(workspaceRoot, ".agents/config"),
|
|
139
|
+
resolve(workspaceRoot, "node_modules/esbuild"),
|
|
140
|
+
]) {
|
|
141
|
+
if (!(await exists(required)))
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Pré-requisito do Dashboard SQLite ausente: ${relative(workspaceRoot, required) || required}. Execute corepack pnpm install na raiz do ZAIP-AIOS e tente novamente.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
106
147
|
export async function readDashboardSession(path = sessionPath) {
|
|
107
148
|
try {
|
|
108
149
|
return JSON.parse(await readFile(path, "utf8"));
|
|
@@ -110,11 +151,9 @@ export async function readDashboardSession(path = sessionPath) {
|
|
|
110
151
|
return null;
|
|
111
152
|
}
|
|
112
153
|
}
|
|
113
|
-
|
|
114
154
|
export async function writeDashboardSession(session, path = sessionPath) {
|
|
115
155
|
await writeJsonAtomic(path, session);
|
|
116
156
|
}
|
|
117
|
-
|
|
118
157
|
async function writeJsonAtomic(path, value) {
|
|
119
158
|
const directory = dirname(path);
|
|
120
159
|
await mkdir(directory, { recursive: true });
|
|
@@ -125,7 +164,6 @@ async function writeJsonAtomic(path, value) {
|
|
|
125
164
|
});
|
|
126
165
|
await rename(temporaryPath, path);
|
|
127
166
|
}
|
|
128
|
-
|
|
129
167
|
export async function readPortRegistry(path = portRegistryPath) {
|
|
130
168
|
try {
|
|
131
169
|
const registry = JSON.parse(await readFile(path, "utf8"));
|
|
@@ -141,7 +179,6 @@ export async function readPortRegistry(path = portRegistryPath) {
|
|
|
141
179
|
return [];
|
|
142
180
|
}
|
|
143
181
|
}
|
|
144
|
-
|
|
145
182
|
export async function recordPortStatus(session, status, path = portRegistryPath) {
|
|
146
183
|
const entries = await readPortRegistry(path);
|
|
147
184
|
const now = new Date().toISOString();
|
|
@@ -152,6 +189,7 @@ export async function recordPortStatus(session, status, path = portRegistryPath)
|
|
|
152
189
|
port: session.port,
|
|
153
190
|
rootPid: session.rootPid,
|
|
154
191
|
serverPid: session.serverPid ?? null,
|
|
192
|
+
dbPath: typeof session.dbPath === "string" ? session.dbPath : null,
|
|
155
193
|
openedAt: session.createdAt ?? session.openedAt ?? now,
|
|
156
194
|
status,
|
|
157
195
|
closedAt: status === "active" ? null : now,
|
|
@@ -166,12 +204,10 @@ export async function recordPortStatus(session, status, path = portRegistryPath)
|
|
|
166
204
|
ports: entries.slice(-maxPortHistory),
|
|
167
205
|
});
|
|
168
206
|
}
|
|
169
|
-
|
|
170
207
|
export async function clearOwnedDashboardSession(rootPid = process.pid, path = sessionPath) {
|
|
171
208
|
const session = await readDashboardSession(path);
|
|
172
209
|
if (session?.rootPid === rootPid) await rm(path, { force: true });
|
|
173
210
|
}
|
|
174
|
-
|
|
175
211
|
export async function probeDashboardPort(
|
|
176
212
|
port,
|
|
177
213
|
fetchImpl = fetch,
|
|
@@ -189,13 +225,13 @@ export async function probeDashboardPort(
|
|
|
189
225
|
return false;
|
|
190
226
|
}
|
|
191
227
|
}
|
|
192
|
-
|
|
193
228
|
export async function findReusableDashboardSession({
|
|
194
229
|
path = sessionPath,
|
|
195
230
|
startPort = preferredPort(),
|
|
196
231
|
fetchImpl = fetch,
|
|
197
232
|
expectedWorkspaceKey = workspaceKey,
|
|
198
233
|
expectedBuildSignature = null,
|
|
234
|
+
expectedSnapshotState = "current",
|
|
199
235
|
} = {}) {
|
|
200
236
|
const session = await readDashboardSession(path);
|
|
201
237
|
if (
|
|
@@ -204,7 +240,8 @@ export async function findReusableDashboardSession({
|
|
|
204
240
|
session?.workspaceKey !== expectedWorkspaceKey ||
|
|
205
241
|
typeof session?.token !== "string" ||
|
|
206
242
|
session.token.length === 0 ||
|
|
207
|
-
(expectedBuildSignature !== null && session?.buildSignature !== expectedBuildSignature)
|
|
243
|
+
(expectedBuildSignature !== null && session?.buildSignature !== expectedBuildSignature) ||
|
|
244
|
+
(expectedSnapshotState !== null && session?.snapshotState !== expectedSnapshotState)
|
|
208
245
|
) {
|
|
209
246
|
return null;
|
|
210
247
|
}
|
|
@@ -214,7 +251,6 @@ export async function findReusableDashboardSession({
|
|
|
214
251
|
) {
|
|
215
252
|
return session;
|
|
216
253
|
}
|
|
217
|
-
|
|
218
254
|
const ports = Array.from({ length: maxPortAttempts }, (_, index) => startPort + index).filter(
|
|
219
255
|
(port) => port <= 65_535 && port !== session.port,
|
|
220
256
|
);
|
|
@@ -226,7 +262,6 @@ export async function findReusableDashboardSession({
|
|
|
226
262
|
const port = matches.find((candidate) => candidate !== null);
|
|
227
263
|
return port === undefined ? null : { ...session, port };
|
|
228
264
|
}
|
|
229
|
-
|
|
230
265
|
async function latestModifiedAt(paths) {
|
|
231
266
|
let latest = 0;
|
|
232
267
|
for (const path of paths) {
|
|
@@ -246,15 +281,23 @@ async function latestModifiedAt(paths) {
|
|
|
246
281
|
}
|
|
247
282
|
return latest;
|
|
248
283
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const command = process.
|
|
252
|
-
const args =
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
284
|
+
async function runSqliteBuild(target) {
|
|
285
|
+
const frontend = target === "frontend";
|
|
286
|
+
const command = process.execPath;
|
|
287
|
+
const args = frontend
|
|
288
|
+
? [resolve(workspaceRoot, "node_modules/vite/bin/vite.js"), "build"]
|
|
289
|
+
: [
|
|
290
|
+
resolve(
|
|
291
|
+
workspaceRoot,
|
|
292
|
+
".agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs",
|
|
293
|
+
),
|
|
294
|
+
];
|
|
256
295
|
const output = await new Promise((resolveBuild, rejectBuild) => {
|
|
257
|
-
const child = spawn(command, args, {
|
|
296
|
+
const child = spawn(command, args, {
|
|
297
|
+
cwd: frontend ? resolve(workspaceRoot, ".agents/dashboard/apps/frontend") : packageRoot,
|
|
298
|
+
windowsHide: true,
|
|
299
|
+
stdio: "pipe",
|
|
300
|
+
});
|
|
258
301
|
let outputText = "";
|
|
259
302
|
const appendOutput = (chunk) => {
|
|
260
303
|
outputText = `${outputText}${chunk}`.slice(-8_000);
|
|
@@ -268,13 +311,14 @@ async function runWorkspaceBuild(filter) {
|
|
|
268
311
|
if (code === 0) resolveBuild();
|
|
269
312
|
else
|
|
270
313
|
rejectBuild(
|
|
271
|
-
new Error(
|
|
314
|
+
new Error(
|
|
315
|
+
`SQLite dashboard ${target} build failed (${code}): ${formatLauncherError(outputText)}`,
|
|
316
|
+
),
|
|
272
317
|
);
|
|
273
318
|
});
|
|
274
319
|
});
|
|
275
320
|
return output;
|
|
276
321
|
}
|
|
277
|
-
|
|
278
322
|
async function ensureFreshDashboardBuild() {
|
|
279
323
|
const artifacts = [resolve(frontendRoot, "index.html"), sourceServer];
|
|
280
324
|
const [sourcesModifiedAt, artifactsModifiedAt] = await Promise.all([
|
|
@@ -283,8 +327,8 @@ async function ensureFreshDashboardBuild() {
|
|
|
283
327
|
]);
|
|
284
328
|
const forceRebuild = process.env.ZAIP_DASHBOARD_FORCE_REBUILD === "1";
|
|
285
329
|
if (forceRebuild || artifactsModifiedAt < sourcesModifiedAt) {
|
|
286
|
-
await
|
|
287
|
-
await
|
|
330
|
+
await runSqliteBuild("frontend");
|
|
331
|
+
await runSqliteBuild("runtime");
|
|
288
332
|
}
|
|
289
333
|
const artifactFingerprint = await Promise.all(
|
|
290
334
|
[...artifacts, resolve(packageRoot, "start-dashboard.mjs")].map(async (path) => {
|
|
@@ -294,7 +338,6 @@ async function ensureFreshDashboardBuild() {
|
|
|
294
338
|
);
|
|
295
339
|
return createHash("sha256").update(artifactFingerprint.join("|")).digest("hex");
|
|
296
340
|
}
|
|
297
|
-
|
|
298
341
|
export async function withLauncherLock(
|
|
299
342
|
action,
|
|
300
343
|
{
|
|
@@ -333,7 +376,6 @@ export async function withLauncherLock(
|
|
|
333
376
|
}
|
|
334
377
|
throw new Error("Timed out waiting for the ZAIP dashboard launcher lock");
|
|
335
378
|
}
|
|
336
|
-
|
|
337
379
|
export async function findAvailablePort(startPort = preferredPort()) {
|
|
338
380
|
for (let port = startPort; port < Math.min(startPort + maxPortAttempts, 65_536); port += 1) {
|
|
339
381
|
if (await isPortFree(port)) return port;
|
|
@@ -342,7 +384,6 @@ export async function findAvailablePort(startPort = preferredPort()) {
|
|
|
342
384
|
`No free dashboard port found between ${startPort} and ${Math.min(startPort + maxPortAttempts - 1, 65_535)}`,
|
|
343
385
|
);
|
|
344
386
|
}
|
|
345
|
-
|
|
346
387
|
export async function findAvailablePortWithFallback({
|
|
347
388
|
preferred = preferredPort(),
|
|
348
389
|
excludedPorts = [],
|
|
@@ -364,7 +405,6 @@ export async function findAvailablePortWithFallback({
|
|
|
364
405
|
"No free dashboard port was found in the preferred, fallback, or ephemeral ranges",
|
|
365
406
|
);
|
|
366
407
|
}
|
|
367
|
-
|
|
368
408
|
async function findEphemeralPort() {
|
|
369
409
|
return new Promise((resolvePort, rejectPort) => {
|
|
370
410
|
const server = createServer();
|
|
@@ -380,7 +420,6 @@ async function findEphemeralPort() {
|
|
|
380
420
|
});
|
|
381
421
|
});
|
|
382
422
|
}
|
|
383
|
-
|
|
384
423
|
export function browserLaunchCommands(
|
|
385
424
|
target,
|
|
386
425
|
platform = process.platform,
|
|
@@ -401,7 +440,6 @@ export function browserLaunchCommands(
|
|
|
401
440
|
}
|
|
402
441
|
return [[platform === "darwin" ? "open" : "xdg-open", [target]]];
|
|
403
442
|
}
|
|
404
|
-
|
|
405
443
|
export async function openBrowser(target, spawnImpl = spawn) {
|
|
406
444
|
if (process.env.ZAIP_UI_NO_BROWSER === "1") return true;
|
|
407
445
|
const commands = browserLaunchCommands(target);
|
|
@@ -431,9 +469,14 @@ export async function openBrowser(target, spawnImpl = spawn) {
|
|
|
431
469
|
appendLauncherLog(`Could not dispatch the dashboard URL to the default browser: ${target}`);
|
|
432
470
|
return false;
|
|
433
471
|
}
|
|
434
|
-
|
|
435
472
|
export async function startDashboardLaunchPage(port) {
|
|
436
473
|
let redirectUrl = null;
|
|
474
|
+
let progress = {
|
|
475
|
+
percent: 4,
|
|
476
|
+
phase: "Iniciando o launcher local",
|
|
477
|
+
detail: "Abrindo a página de preparação.",
|
|
478
|
+
};
|
|
479
|
+
const connections = new Set();
|
|
437
480
|
let resolveRedirectObserved;
|
|
438
481
|
const redirectObserved = new Promise((resolveObserved) => {
|
|
439
482
|
resolveRedirectObserved = resolveObserved;
|
|
@@ -446,6 +489,11 @@ export async function startDashboardLaunchPage(port) {
|
|
|
446
489
|
response.end(JSON.stringify({ target: redirectUrl }));
|
|
447
490
|
return;
|
|
448
491
|
}
|
|
492
|
+
if (request.url?.startsWith("/api/v2/progress")) {
|
|
493
|
+
response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
494
|
+
response.end(JSON.stringify(progress));
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
449
497
|
if (request.url?.startsWith(healthPath)) {
|
|
450
498
|
response.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
|
|
451
499
|
response.end(JSON.stringify({ kind: "zaip-dashboard-launcher", status: "starting" }));
|
|
@@ -460,40 +508,43 @@ export async function startDashboardLaunchPage(port) {
|
|
|
460
508
|
<title>Dashboard Zaip AIOS</title>
|
|
461
509
|
<style>
|
|
462
510
|
: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}}
|
|
511
|
+
*{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}
|
|
512
|
+
.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
513
|
</style>
|
|
472
514
|
</head>
|
|
473
515
|
<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>
|
|
516
|
+
<main class="launch-overlay" aria-label="Preparando dashboard">
|
|
517
|
+
<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
518
|
</main>
|
|
480
519
|
<script>
|
|
520
|
+
let handoffTarget=null;
|
|
521
|
+
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."}
|
|
522
|
+
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
523
|
async function waitForDashboard(){
|
|
482
524
|
try{
|
|
525
|
+
await updateProgress();
|
|
483
526
|
const ready=await fetch("/api/v2/ready",{cache:"no-store"});
|
|
484
527
|
const next=await ready.json();
|
|
485
|
-
if(next.target){location.replace(next.target);return}
|
|
528
|
+
if(next.target){showHandoff(next.target);setTimeout(()=>location.replace(next.target),0);return}
|
|
486
529
|
const response=await fetch("${healthPath}",{cache:"no-store"});
|
|
487
530
|
const payload=await response.json();
|
|
488
531
|
if(response.ok&&payload.kind==="${runtimeKind}"){location.reload();return}
|
|
489
532
|
}catch{}
|
|
490
533
|
setTimeout(waitForDashboard,250)
|
|
491
534
|
}
|
|
492
|
-
setTimeout(waitForDashboard,100)
|
|
535
|
+
updateProgress();setTimeout(waitForDashboard,100)
|
|
536
|
+
// A browser extension or a failed navigation must never leave a silent
|
|
537
|
+
// skeleton. The visible link remains actionable while the temporary
|
|
538
|
+
// launcher server keeps serving this page through the handoff grace.
|
|
539
|
+
setTimeout(()=>{if(handoffTarget)showHandoff(handoffTarget)},4000)
|
|
493
540
|
</script>
|
|
494
541
|
</body>
|
|
495
542
|
</html>`);
|
|
496
543
|
});
|
|
544
|
+
server.on("connection", (connection) => {
|
|
545
|
+
connections.add(connection);
|
|
546
|
+
connection.once("close", () => connections.delete(connection));
|
|
547
|
+
});
|
|
497
548
|
await new Promise((resolveListen, rejectListen) => {
|
|
498
549
|
server.once("error", rejectListen);
|
|
499
550
|
server.listen({ host, port }, resolveListen);
|
|
@@ -506,25 +557,56 @@ export async function startDashboardLaunchPage(port) {
|
|
|
506
557
|
redirect: (target) => {
|
|
507
558
|
redirectUrl = target;
|
|
508
559
|
},
|
|
560
|
+
setProgress: (next) => {
|
|
561
|
+
progress = {
|
|
562
|
+
...progress,
|
|
563
|
+
...next,
|
|
564
|
+
percent: Math.max(progress.percent, next.percent ?? progress.percent),
|
|
565
|
+
};
|
|
566
|
+
},
|
|
509
567
|
waitForRedirect: async (timeoutMs = 1_500) =>
|
|
510
568
|
Promise.race([redirectObserved, delay(timeoutMs).then(() => false)]),
|
|
511
|
-
|
|
569
|
+
closeAfterHandoffGrace: (graceMs = 12_000) => {
|
|
570
|
+
const timer = setTimeout(() => {
|
|
571
|
+
void server.close();
|
|
572
|
+
server.closeAllConnections?.();
|
|
573
|
+
for (const connection of connections) connection.destroy();
|
|
574
|
+
}, graceMs);
|
|
575
|
+
timer.unref?.();
|
|
576
|
+
},
|
|
577
|
+
close: ({ forceAfterMs = 750 } = {}) =>
|
|
512
578
|
new Promise((resolveClose, rejectClose) => {
|
|
513
|
-
|
|
579
|
+
let forceTimer = null;
|
|
580
|
+
const finish = (error) => {
|
|
581
|
+
if (forceTimer !== null) clearTimeout(forceTimer);
|
|
582
|
+
if (error) rejectClose(error);
|
|
583
|
+
else resolveClose();
|
|
584
|
+
};
|
|
585
|
+
server.close(finish);
|
|
586
|
+
// The preparation page polls while redirecting. A browser keep-alive
|
|
587
|
+
// or an interrupted polling request must not keep this transient
|
|
588
|
+
// server alive indefinitely after its handoff was issued.
|
|
589
|
+
forceTimer = setTimeout(() => {
|
|
590
|
+
server.closeAllConnections?.();
|
|
591
|
+
for (const connection of connections) connection.destroy();
|
|
592
|
+
}, forceAfterMs);
|
|
593
|
+
forceTimer.unref?.();
|
|
514
594
|
}),
|
|
515
595
|
};
|
|
516
596
|
}
|
|
517
|
-
|
|
518
597
|
async function redirectLaunchPage(launchPage, target) {
|
|
519
598
|
launchPage.redirect(target);
|
|
520
599
|
const redirected =
|
|
521
600
|
process.env.ZAIP_UI_NO_BROWSER === "1" ? true : await launchPage.waitForRedirect();
|
|
522
|
-
await launchPage.close();
|
|
523
601
|
if (!redirected && !(await openBrowser(target))) {
|
|
524
602
|
throw new Error("Could not open the dashboard after the preparation page did not redirect.");
|
|
525
603
|
}
|
|
604
|
+
// Do not tear down the preparation page merely because its ready poll was
|
|
605
|
+
// observed: that happens before location.replace and used to strand users
|
|
606
|
+
// on the skeleton when navigation was blocked. Its watchdog/link remains
|
|
607
|
+
// available for a bounded grace period.
|
|
608
|
+
launchPage.closeAfterHandoffGrace();
|
|
526
609
|
}
|
|
527
|
-
|
|
528
610
|
async function assertRuntimeReady() {
|
|
529
611
|
const serverPath = (await exists(sourceServer)) ? sourceServer : installedServer;
|
|
530
612
|
if (!(await exists(serverPath))) {
|
|
@@ -539,53 +621,38 @@ async function assertRuntimeReady() {
|
|
|
539
621
|
}
|
|
540
622
|
return serverPath;
|
|
541
623
|
}
|
|
542
|
-
|
|
543
624
|
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
|
-
}
|
|
625
|
+
return stopOwnedDashboardProcess(session, {
|
|
626
|
+
probeDashboardPort,
|
|
627
|
+
workspaceKey,
|
|
628
|
+
fetchImpl: fetch,
|
|
629
|
+
host,
|
|
630
|
+
packageRoot,
|
|
631
|
+
delay,
|
|
632
|
+
});
|
|
573
633
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
634
|
+
export async function previousDashboardSessions({
|
|
635
|
+
sessionPath: candidateSessionPath = sessionPath,
|
|
636
|
+
registryPath: candidateRegistryPath = portRegistryPath,
|
|
637
|
+
probe = (port) => probeDashboardPort(port, fetch, workspaceKey, false),
|
|
638
|
+
} = {}) {
|
|
639
|
+
return collectPreviousDashboardSessions({
|
|
640
|
+
candidateSessionPath,
|
|
641
|
+
candidateRegistryPath,
|
|
642
|
+
probe,
|
|
643
|
+
readDashboardSession,
|
|
644
|
+
readPortRegistry,
|
|
645
|
+
workspaceRoot,
|
|
646
|
+
workspaceKey,
|
|
647
|
+
});
|
|
586
648
|
}
|
|
587
|
-
|
|
588
|
-
|
|
649
|
+
async function startDashboardServer(
|
|
650
|
+
serverPath,
|
|
651
|
+
token,
|
|
652
|
+
excludedPorts,
|
|
653
|
+
initialPort = null,
|
|
654
|
+
onProgress = () => {},
|
|
655
|
+
) {
|
|
589
656
|
const failures = [];
|
|
590
657
|
for (let attempt = 1; attempt <= maxServerStartAttempts; attempt += 1) {
|
|
591
658
|
const port =
|
|
@@ -593,7 +660,13 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
593
660
|
? initialPort
|
|
594
661
|
: await findAvailablePortWithFallback({ excludedPorts: [...excludedPorts] });
|
|
595
662
|
excludedPorts.add(port);
|
|
663
|
+
const dbPath = resolve(
|
|
664
|
+
sqliteReadModelDirectory,
|
|
665
|
+
`read-model-${port}-${process.pid}-${attempt}.sqlite`,
|
|
666
|
+
);
|
|
596
667
|
let stderr = "";
|
|
668
|
+
let startupTelemetryBuffer = "";
|
|
669
|
+
let snapshotCache = null;
|
|
597
670
|
const child = spawn(process.execPath, [serverPath], {
|
|
598
671
|
cwd: packageRoot,
|
|
599
672
|
env: {
|
|
@@ -602,6 +675,15 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
602
675
|
ZAIP_UI_ROOT: frontendRoot,
|
|
603
676
|
ZAIP_UI_TOKEN: token,
|
|
604
677
|
ZAIP_SERVER_PORT: String(port),
|
|
678
|
+
NODE_COMPILE_CACHE: nodeCompileCacheDirectory,
|
|
679
|
+
NODE_NO_WARNINGS: "1",
|
|
680
|
+
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "1",
|
|
681
|
+
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "1",
|
|
682
|
+
ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,
|
|
683
|
+
// A retry or a fresh-port handoff never reuses a live SQLite file.
|
|
684
|
+
// Windows keeps an open read-only handle locked, so each child owns a
|
|
685
|
+
// generation identified by launcher PID, attempt and selected port.
|
|
686
|
+
ZAIP_SQLITE_DB_PATH: dbPath,
|
|
605
687
|
},
|
|
606
688
|
stdio: ["ignore", "ignore", "pipe"],
|
|
607
689
|
windowsHide: true,
|
|
@@ -609,14 +691,71 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
609
691
|
child.stderr?.setEncoding("utf8");
|
|
610
692
|
child.stderr?.on("data", (chunk) => {
|
|
611
693
|
stderr = `${stderr}${chunk}`.slice(-4_000);
|
|
694
|
+
startupTelemetryBuffer = `${startupTelemetryBuffer}${String(chunk)}`;
|
|
695
|
+
const lines = startupTelemetryBuffer.split("\n");
|
|
696
|
+
startupTelemetryBuffer = lines.pop() ?? "";
|
|
697
|
+
for (const line of lines) {
|
|
698
|
+
try {
|
|
699
|
+
const event = JSON.parse(line);
|
|
700
|
+
if (event?.event === "dashboard_sqlite_rebuild_progress")
|
|
701
|
+
onProgress({
|
|
702
|
+
percent: event.percent,
|
|
703
|
+
phase: "Configurando o banco SQLite local",
|
|
704
|
+
detail:
|
|
705
|
+
"Esta é a instalação/atualização da cópia local descartável; nenhum servidor externo é instalado.",
|
|
706
|
+
});
|
|
707
|
+
if (event?.event === "dashboard_sqlite_startup_phase" && event.phase === "main_entered")
|
|
708
|
+
onProgress({
|
|
709
|
+
percent: 50,
|
|
710
|
+
phase: "Lendo os registros do projeto",
|
|
711
|
+
detail: "Preparando o read model SQLite local.",
|
|
712
|
+
});
|
|
713
|
+
if (event?.event === "dashboard_sqlite_read_model") snapshotCache = event.cache;
|
|
714
|
+
if (event?.event === "dashboard_sqlite_read_model")
|
|
715
|
+
onProgress({
|
|
716
|
+
percent: 82,
|
|
717
|
+
phase: "Banco local pronto",
|
|
718
|
+
detail:
|
|
719
|
+
event.cache === "rebuilt"
|
|
720
|
+
? "O read model SQLite foi reconstruído para os dados atuais."
|
|
721
|
+
: "Uma cópia local do read model foi preparada.",
|
|
722
|
+
});
|
|
723
|
+
if (event?.event === "dashboard_sqlite_startup_phase" && event.phase === "listening")
|
|
724
|
+
onProgress({
|
|
725
|
+
percent: 88,
|
|
726
|
+
phase: "Iniciando a interface",
|
|
727
|
+
detail: "Verificando saúde e disponibilidade dos dados.",
|
|
728
|
+
});
|
|
729
|
+
} catch {
|
|
730
|
+
// Only structured startup telemetry affects the user-facing progress.
|
|
731
|
+
}
|
|
732
|
+
}
|
|
612
733
|
});
|
|
613
734
|
try {
|
|
614
735
|
await waitForDashboard(port, child);
|
|
615
|
-
|
|
736
|
+
const timings = stripTerminalControlCodes(stderr)
|
|
737
|
+
.split("\n")
|
|
738
|
+
.filter(
|
|
739
|
+
(line) =>
|
|
740
|
+
line.includes('"event":"dashboard_sqlite_read_model"') ||
|
|
741
|
+
line.includes('"event":"dashboard_sqlite_startup_phase"'),
|
|
742
|
+
);
|
|
743
|
+
for (const timing of timings) appendLauncherLog(timing);
|
|
744
|
+
return { child, port, dbPath, snapshotCache };
|
|
616
745
|
} catch (error) {
|
|
617
746
|
await stopProcessTree(child.pid);
|
|
618
|
-
|
|
619
|
-
|
|
747
|
+
await removeGeneratedReadModel(dbPath);
|
|
748
|
+
const detail = stripTerminalControlCodes(stderr)
|
|
749
|
+
.split("\n")
|
|
750
|
+
.filter(
|
|
751
|
+
(line) =>
|
|
752
|
+
!line.includes("ExperimentalWarning") && !line.includes("node --trace-warnings"),
|
|
753
|
+
)
|
|
754
|
+
.join("\n")
|
|
755
|
+
.trim();
|
|
756
|
+
failures.push(
|
|
757
|
+
`porta ${port}: ${error.message}${detail ? ` — ${formatLauncherError(detail, 600)}` : ""}`,
|
|
758
|
+
);
|
|
620
759
|
appendLauncherLog(
|
|
621
760
|
`Dashboard start attempt ${attempt} failed on port ${port}: ${error.message}`,
|
|
622
761
|
);
|
|
@@ -628,6 +767,99 @@ async function startDashboardServer(serverPath, token, excludedPorts, initialPor
|
|
|
628
767
|
);
|
|
629
768
|
}
|
|
630
769
|
|
|
770
|
+
async function removeGeneratedReadModel(candidate) {
|
|
771
|
+
if (typeof candidate !== "string") return false;
|
|
772
|
+
const resolvedCandidate = resolve(candidate);
|
|
773
|
+
const fromReadModelDirectory = relative(sqliteReadModelDirectory, resolvedCandidate);
|
|
774
|
+
const generatedName = /^read-model-\d+-\d+-\d+\.sqlite$/;
|
|
775
|
+
if (
|
|
776
|
+
fromReadModelDirectory === "" ||
|
|
777
|
+
fromReadModelDirectory.startsWith("..") ||
|
|
778
|
+
fromReadModelDirectory.includes("/") ||
|
|
779
|
+
fromReadModelDirectory.includes("\\") ||
|
|
780
|
+
!generatedName.test(fromReadModelDirectory)
|
|
781
|
+
)
|
|
782
|
+
return false;
|
|
783
|
+
for (let attempt = 1; attempt <= 20; attempt += 1) {
|
|
784
|
+
try {
|
|
785
|
+
await rm(resolvedCandidate, { force: true });
|
|
786
|
+
return true;
|
|
787
|
+
} catch (error) {
|
|
788
|
+
if (error?.code !== "EBUSY" && error?.code !== "EPERM") throw error;
|
|
789
|
+
if (attempt < 20) await delay(250);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
appendLauncherLog(
|
|
793
|
+
`Could not remove closed SQLite read model after retries: ${resolvedCandidate}`,
|
|
794
|
+
);
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
function refreshSnapshotSeedInBackground() {
|
|
798
|
+
const refreshPath = resolve(
|
|
799
|
+
sqliteReadModelDirectory,
|
|
800
|
+
`refresh-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`,
|
|
801
|
+
);
|
|
802
|
+
const refreshStatusPath = join(sessionDirectory, "dashboard-sqlite-refresh.json");
|
|
803
|
+
const refreshLogPath = join(sessionDirectory, "dashboard-sqlite-refresh.log");
|
|
804
|
+
mkdirSync(sessionDirectory, { recursive: true });
|
|
805
|
+
const refreshLogDescriptor = openSync(refreshLogPath, "a");
|
|
806
|
+
let worker;
|
|
807
|
+
try {
|
|
808
|
+
worker = spawn(
|
|
809
|
+
process.execPath,
|
|
810
|
+
[resolve(packageRoot, "scripts/rebuild-sqlite-read-model.mjs")],
|
|
811
|
+
{
|
|
812
|
+
cwd: workspaceRoot,
|
|
813
|
+
env: {
|
|
814
|
+
...process.env,
|
|
815
|
+
ZAIP_SQLITE_DB_PATH: refreshPath,
|
|
816
|
+
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "0",
|
|
817
|
+
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "0",
|
|
818
|
+
ZAIP_SQLITE_REFRESH_CLEANUP: "1",
|
|
819
|
+
ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,
|
|
820
|
+
},
|
|
821
|
+
// A detached worker keeps its own append-only evidence file. Pipes can
|
|
822
|
+
// disappear with a fast launcher exit and silently abort the refresh.
|
|
823
|
+
stdio: ["ignore", refreshLogDescriptor, refreshLogDescriptor],
|
|
824
|
+
detached: true,
|
|
825
|
+
windowsHide: true,
|
|
826
|
+
},
|
|
827
|
+
);
|
|
828
|
+
} finally {
|
|
829
|
+
closeSync(refreshLogDescriptor);
|
|
830
|
+
}
|
|
831
|
+
void writeJsonAtomic(refreshStatusPath, {
|
|
832
|
+
status: "running",
|
|
833
|
+
pid: worker.pid ?? null,
|
|
834
|
+
path: refreshPath,
|
|
835
|
+
cacheDirectory: sqliteSnapshotCacheDirectory,
|
|
836
|
+
startedAt: new Date().toISOString(),
|
|
837
|
+
}).catch(() => {});
|
|
838
|
+
worker.once("error", (error) => {
|
|
839
|
+
appendLauncherLog(`Background SQLite seed refresh failed to start: ${error.message}`);
|
|
840
|
+
void writeJsonAtomic(refreshStatusPath, {
|
|
841
|
+
status: "failed",
|
|
842
|
+
error: error.message,
|
|
843
|
+
finishedAt: new Date().toISOString(),
|
|
844
|
+
}).catch(() => {});
|
|
845
|
+
});
|
|
846
|
+
worker.once("exit", (code) => {
|
|
847
|
+
const status = code === 0 ? "completed" : "failed";
|
|
848
|
+
appendLauncherLog(
|
|
849
|
+
`Background SQLite seed refresh ${status} (${code ?? "unknown"}); inspect ${refreshLogPath}.`,
|
|
850
|
+
);
|
|
851
|
+
void writeJsonAtomic(refreshStatusPath, {
|
|
852
|
+
status,
|
|
853
|
+
exitCode: code ?? null,
|
|
854
|
+
logPath: refreshLogPath,
|
|
855
|
+
finishedAt: new Date().toISOString(),
|
|
856
|
+
}).catch(() => {});
|
|
857
|
+
});
|
|
858
|
+
worker.unref();
|
|
859
|
+
appendLauncherLog(
|
|
860
|
+
`Started background SQLite seed refresh at ${refreshPath}; cache ${sqliteSnapshotCacheDirectory}; worker log ${refreshLogPath}.`,
|
|
861
|
+
);
|
|
862
|
+
}
|
|
631
863
|
async function writeLauncherStatus(status, message = "") {
|
|
632
864
|
const path = process.env.ZAIP_LAUNCH_STATUS;
|
|
633
865
|
if (!path) return;
|
|
@@ -635,74 +867,157 @@ async function writeLauncherStatus(status, message = "") {
|
|
|
635
867
|
await writeFile(temporaryPath, `${status}\n${message}`.trimEnd(), "utf8");
|
|
636
868
|
await rename(temporaryPath, path);
|
|
637
869
|
}
|
|
638
|
-
|
|
870
|
+
function phaseTiming(phase, startedAt, details = {}) {
|
|
871
|
+
appendLauncherLog(
|
|
872
|
+
JSON.stringify({
|
|
873
|
+
event: "dashboard_sqlite_phase",
|
|
874
|
+
phase,
|
|
875
|
+
elapsed_ms: Date.now() - startedAt,
|
|
876
|
+
launcher_pid: process.pid,
|
|
877
|
+
...details,
|
|
878
|
+
}),
|
|
879
|
+
);
|
|
880
|
+
}
|
|
639
881
|
async function main() {
|
|
640
882
|
let ownedChild = null;
|
|
641
883
|
let currentSession = null;
|
|
642
884
|
let launchPage = null;
|
|
643
885
|
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);
|
|
886
|
+
const launchStartedAt = Date.now();
|
|
887
|
+
// Bind the preparation page atomically on an ephemeral port before waiting
|
|
888
|
+
// for the launch lock. Looking up a free port and binding it later lets a
|
|
889
|
+
// concurrent preparation page claim that port, which makes the runtime
|
|
890
|
+
// probe mistake its intentional 503 for a server failure.
|
|
891
|
+
launchPage = await startDashboardLaunchPage(0);
|
|
892
|
+
const launchPort = launchPage.port;
|
|
893
|
+
try {
|
|
663
894
|
if (!(await openBrowser(dashboardUrl(launchPort, token)))) {
|
|
664
|
-
await launchPage.close();
|
|
665
|
-
launchPage = null;
|
|
666
895
|
throw new Error("Could not open the dashboard preparation page in the default browser.");
|
|
667
896
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
897
|
+
launchPage.setProgress({
|
|
898
|
+
percent: 10,
|
|
899
|
+
phase: "Validando pré-requisitos locais",
|
|
900
|
+
detail:
|
|
901
|
+
"Verificando Node.js 22 e o suporte a node:sqlite; isto não instala um banco de dados.",
|
|
902
|
+
});
|
|
903
|
+
await assertDashboardPrerequisites();
|
|
904
|
+
launchPage.setProgress({
|
|
905
|
+
percent: 18,
|
|
906
|
+
phase: "Preparando a sessão do dashboard",
|
|
907
|
+
detail: "Coordenando esta abertura local com segurança.",
|
|
908
|
+
});
|
|
909
|
+
await withLauncherLock(async () => {
|
|
910
|
+
phaseTiming("lock_acquired", launchStartedAt);
|
|
911
|
+
const buildStartedAt = Date.now();
|
|
912
|
+
launchPage.setProgress({
|
|
913
|
+
percent: 28,
|
|
914
|
+
phase: "Verificando os artefatos do dashboard",
|
|
915
|
+
detail: "Usando o build existente quando ele já está atualizado.",
|
|
916
|
+
});
|
|
917
|
+
const buildSignature = await ensureFreshDashboardBuild();
|
|
918
|
+
phaseTiming("build_complete", buildStartedAt, { build_signature: buildSignature });
|
|
919
|
+
// A second deliberate click must use the already healthy compatible
|
|
920
|
+
// runtime. Build signature makes this safe across dashboard updates;
|
|
921
|
+
// an incompatible session continues through a replacement handoff.
|
|
922
|
+
const reusable = await findReusableDashboardSession({
|
|
923
|
+
expectedBuildSignature: buildSignature,
|
|
924
|
+
expectedSnapshotState: "current",
|
|
925
|
+
});
|
|
926
|
+
if (reusable !== null) {
|
|
927
|
+
appendLauncherLog(
|
|
928
|
+
`Reused healthy compatible dashboard session on port ${reusable.port}, PID ${reusable.serverPid ?? "unknown"}.`,
|
|
929
|
+
);
|
|
930
|
+
await redirectLaunchPage(launchPage, dashboardUrl(reusable.port, reusable.token));
|
|
931
|
+
launchPage = null;
|
|
932
|
+
await writeLauncherStatus("ok");
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const previousSessions = await previousDashboardSessions();
|
|
936
|
+
const excludedPorts = new Set([
|
|
937
|
+
...previousSessions.map((session) => session.port),
|
|
938
|
+
launchPort,
|
|
939
|
+
]);
|
|
940
|
+
const serverPath = await assertRuntimeReady();
|
|
941
|
+
const readyStartedAt = Date.now();
|
|
942
|
+
launchPage.setProgress({
|
|
943
|
+
percent: 42,
|
|
944
|
+
phase: "Abrindo o banco local descartável",
|
|
945
|
+
detail: "Criando uma cópia privada do read model SQLite para esta sessão.",
|
|
946
|
+
});
|
|
947
|
+
const { child, port, dbPath, snapshotCache } = await startDashboardServer(
|
|
948
|
+
serverPath,
|
|
685
949
|
token,
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
950
|
+
excludedPorts,
|
|
951
|
+
null,
|
|
952
|
+
(progress) => launchPage?.setProgress(progress),
|
|
953
|
+
);
|
|
954
|
+
phaseTiming("server_ready", readyStartedAt, { server_pid: child.pid, port, db_path: dbPath });
|
|
955
|
+
ownedChild = child;
|
|
956
|
+
try {
|
|
957
|
+
launchPage.setProgress({
|
|
958
|
+
percent: 92,
|
|
959
|
+
phase:
|
|
960
|
+
snapshotCache === "stale_seed" ? "Abrindo snapshot local validado" : "Dashboard pronto",
|
|
961
|
+
detail:
|
|
962
|
+
snapshotCache === "stale_seed"
|
|
963
|
+
? "Os dados canônicos estão sendo atualizados em segundo plano para a próxima abertura."
|
|
964
|
+
: "Validando saúde e dados iniciais antes de abrir a tela principal.",
|
|
965
|
+
});
|
|
966
|
+
if (snapshotCache === "stale_seed") await delay(350);
|
|
967
|
+
await redirectLaunchPage(
|
|
968
|
+
launchPage,
|
|
969
|
+
dashboardUrl(port, token, snapshotCache === "stale_seed"),
|
|
970
|
+
);
|
|
971
|
+
phaseTiming("handoff_complete", launchStartedAt, { server_pid: child.pid, port });
|
|
972
|
+
launchPage = null;
|
|
973
|
+
if (snapshotCache === "stale_seed") refreshSnapshotSeedInBackground();
|
|
974
|
+
currentSession = {
|
|
975
|
+
kind: runtimeKind,
|
|
976
|
+
workspace: workspaceRoot,
|
|
977
|
+
workspaceKey,
|
|
978
|
+
rootPid: process.pid,
|
|
979
|
+
serverPid: child.pid,
|
|
980
|
+
port,
|
|
981
|
+
token,
|
|
982
|
+
dbPath,
|
|
983
|
+
buildSignature,
|
|
984
|
+
generationId: `${process.pid}-${child.pid}-${port}-${randomBytes(6).toString("hex")}`,
|
|
985
|
+
// Never pin subsequent clicks to a stale seed. A completed
|
|
986
|
+
// background refresh is consumed by a fresh handoff on the next
|
|
987
|
+
// click, then the old updating generation is retired below.
|
|
988
|
+
snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",
|
|
989
|
+
createdAt: new Date().toISOString(),
|
|
990
|
+
};
|
|
991
|
+
await writeDashboardSession(currentSession);
|
|
992
|
+
await recordPortStatus(currentSession, "active");
|
|
993
|
+
const warnings = [];
|
|
994
|
+
for (const previous of previousSessions) {
|
|
995
|
+
const status = await stopOwnedDashboard(previous);
|
|
996
|
+
await recordPortStatus(previous, status);
|
|
997
|
+
if (status === "closed") await removeGeneratedReadModel(previous.dbPath);
|
|
998
|
+
if (teardownWarning(status))
|
|
999
|
+
warnings.push(`não foi possível fechar a porta anterior ${previous.port}`);
|
|
1000
|
+
else if (status === "skipped_unowned")
|
|
1001
|
+
appendLauncherLog(
|
|
1002
|
+
`Skipped teardown of port ${previous.port}: registered server PID does not own its live listener.`,
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
if (warnings.length > 0)
|
|
1006
|
+
appendLauncherLog(
|
|
1007
|
+
`Dashboard launched on port ${port}; prior-port cleanup: ${warnings.join("; ")}`,
|
|
1008
|
+
);
|
|
1009
|
+
await writeLauncherStatus("ok");
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
await stopProcessTree(child.pid);
|
|
1012
|
+
if (currentSession !== null) await recordPortStatus(currentSession, "failed");
|
|
1013
|
+
await clearOwnedDashboardSession();
|
|
1014
|
+
throw error;
|
|
696
1015
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
throw error;
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
|
|
1016
|
+
});
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
if (launchPage !== null) await launchPage.close().catch(() => {});
|
|
1019
|
+
throw error;
|
|
1020
|
+
}
|
|
706
1021
|
if (ownedChild === null) return;
|
|
707
1022
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
708
1023
|
process.once(signal, () => ownedChild?.kill(signal));
|
|
@@ -712,10 +1027,12 @@ async function main() {
|
|
|
712
1027
|
ownedChild.once("exit", (code) => resolveExit(code ?? 0));
|
|
713
1028
|
});
|
|
714
1029
|
await clearOwnedDashboardSession();
|
|
715
|
-
if (currentSession !== null)
|
|
1030
|
+
if (currentSession !== null) {
|
|
1031
|
+
await recordPortStatus(currentSession, "closed");
|
|
1032
|
+
await removeGeneratedReadModel(currentSession.dbPath);
|
|
1033
|
+
}
|
|
716
1034
|
process.exitCode = exitCode;
|
|
717
1035
|
}
|
|
718
|
-
|
|
719
1036
|
function isPortFree(port) {
|
|
720
1037
|
return new Promise((resolvePort) => {
|
|
721
1038
|
const server = createServer();
|
|
@@ -724,7 +1041,6 @@ function isPortFree(port) {
|
|
|
724
1041
|
server.listen({ host, port }, () => server.close(() => resolvePort(true)));
|
|
725
1042
|
});
|
|
726
1043
|
}
|
|
727
|
-
|
|
728
1044
|
async function waitForDashboard(port, child) {
|
|
729
1045
|
const deadline = Date.now() + startupTimeoutMs;
|
|
730
1046
|
while (Date.now() < deadline) {
|
|
@@ -735,7 +1051,19 @@ async function waitForDashboard(port, child) {
|
|
|
735
1051
|
const response = await fetch(dashboardHealthUrl(port), { signal: AbortSignal.timeout(750) });
|
|
736
1052
|
const payload = await response.json().catch(() => null);
|
|
737
1053
|
if (response.ok && payload?.kind === runtimeKind && payload?.workspaceKey === workspaceKey) {
|
|
738
|
-
|
|
1054
|
+
const readiness = await fetch(`http://${host}:${port}/api/v2/ready`, {
|
|
1055
|
+
signal: AbortSignal.timeout(750),
|
|
1056
|
+
cache: "no-store",
|
|
1057
|
+
});
|
|
1058
|
+
if (readiness.ok) return;
|
|
1059
|
+
if (readiness.status >= 500) {
|
|
1060
|
+
const detail = await readiness.json().catch(() => null);
|
|
1061
|
+
const unavailable = new Error(
|
|
1062
|
+
`Dashboard data readiness returned ${readiness.status}: ${detail?.error ?? "projection unavailable"}`,
|
|
1063
|
+
);
|
|
1064
|
+
unavailable.retryable = false;
|
|
1065
|
+
throw unavailable;
|
|
1066
|
+
}
|
|
739
1067
|
}
|
|
740
1068
|
if (response.status >= 500) {
|
|
741
1069
|
const unavailable = new Error(
|
|
@@ -747,26 +1075,22 @@ async function waitForDashboard(port, child) {
|
|
|
747
1075
|
} catch (error) {
|
|
748
1076
|
if (error?.retryable === false) throw error;
|
|
749
1077
|
}
|
|
750
|
-
await delay(
|
|
1078
|
+
await delay(100);
|
|
751
1079
|
}
|
|
752
1080
|
throw new Error(`Timed out waiting for ${dashboardHealthUrl(port)}`);
|
|
753
1081
|
}
|
|
754
|
-
|
|
755
1082
|
function exists(path) {
|
|
756
1083
|
return access(path).then(
|
|
757
1084
|
() => true,
|
|
758
1085
|
() => false,
|
|
759
1086
|
);
|
|
760
1087
|
}
|
|
761
|
-
|
|
762
1088
|
function delay(milliseconds) {
|
|
763
1089
|
return new Promise((resolveDelay) => setTimeout(resolveDelay, milliseconds));
|
|
764
1090
|
}
|
|
765
|
-
|
|
766
1091
|
const isMain = process.argv[1]
|
|
767
1092
|
? resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
768
1093
|
: false;
|
|
769
|
-
|
|
770
1094
|
if (isMain) {
|
|
771
1095
|
main().catch(async (error) => {
|
|
772
1096
|
const logMessage = error instanceof Error ? (error.stack ?? error.message) : String(error);
|