@zaip-ai/cli 1.3.0 → 1.3.2
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 +3 -2
- package/dist/services/agent-adapters.js +1 -1
- package/dist/services/agent-adapters.js.map +1 -1
- package/dist/services/agent-adapters.test.js +2 -1
- package/dist/services/agent-adapters.test.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +89 -7
- package/dist/services/codex-pre-tool-hook-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 +7 -0
- package/dist/services/codex-rules.js.map +1 -1
- package/dist/services/install-agents-template.d.ts.map +1 -1
- package/dist/services/install-agents-template.js +1 -0
- package/dist/services/install-agents-template.js.map +1 -1
- package/dist/services/runtime-template.test.js +31 -7
- 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 -3
- package/dist/services/template-manifest.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/config/agents/catalog.yaml +7 -7
- package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
- package/templates/.agents/dashboard/.zaip-managed.json +373 -205
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DxR_pgSV.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-4jZLTWE_.js → DashboardHelpDialog-BXy6nti1.js} +268 -86
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CKcsqb-r.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DmmJD2Th.js → ProjectSettingsPage-zH758vfs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CTH2wDs5.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-C0fj2uU9.js → icons-vendor-B_lDDHn1.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CuMcoUni.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BEsJ-FYH.js → react-vendor-B3TJrD4O.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B6y6Gw12.js → traceability-7f1j5wx3.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
- package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +132 -245
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
- package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +9 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +18 -1
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +75 -8
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +25 -1
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -9
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
- package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +7 -0
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +20 -8
- package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
- package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +48 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +31 -27
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
- package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +38 -14
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +8 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +47 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +50 -58
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +128 -138
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +17 -67
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +16 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +44 -8
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +6 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +20 -3
- package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
- package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +20 -28
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +162 -165
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +13 -46
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +4 -2
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +13 -0
- package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
- package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
- package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +8 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -5
- package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
- package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +21 -11
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +8 -1
- package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +527 -335
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +501 -309
- package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +3 -1
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +3 -3
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +25 -15
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +527 -335
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +34 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +15 -8
- package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +3 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +98 -123
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +111 -135
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +293 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +371 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +440 -863
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1120 -1086
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +188 -299
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +51 -70
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
- package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +183 -210
- package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
- package/templates/.agents/requirements/autonomous-assurance.md +6 -6
- 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 +1 -1
- 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 +3 -3
- package/templates/.agents/requirements/kanban-dashboard.md +10 -2
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- 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 +4 -4
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
- package/templates/.agents/rules/frontend-lite.md +4 -4
- package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
- package/templates/.agents/runtime/bin/zaip.mjs +1137 -1037
- package/templates/.agents/runtime/checksums.sha256 +2 -2
- package/templates/.agents/runtime/provenance.json +2 -2
- package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
- package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
- package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
- package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
- package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
- package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
- package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
- package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +72 -22
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +42 -1
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +101 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +53 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
- package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
- package/templates/.agents/zaip-aios/INDEX.md +57 -32
- package/templates/.agents/zaip-aios/README.md +22 -12
- package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
- package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
- package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
- package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
- package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
- package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
- package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
- package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
- package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +23 -0
- package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +44 -8
- package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +8 -14
- package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +5 -0
- package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +6 -6
- package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
- package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +3 -2
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +92 -10
- package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +8 -3
- package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +5 -0
- package/templates/template-manifest.json +555 -170
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +0 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +0 -7333
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
-
import { appendFileSync,
|
|
3
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
4
4
|
import {
|
|
5
5
|
access,
|
|
6
6
|
mkdir,
|
|
@@ -12,18 +12,20 @@ import {
|
|
|
12
12
|
utimes,
|
|
13
13
|
writeFile,
|
|
14
14
|
} from "node:fs/promises";
|
|
15
|
-
import { createServer as createHttpServer } from "node:http";
|
|
16
15
|
import { createServer } from "node:net";
|
|
17
16
|
import { tmpdir } from "node:os";
|
|
18
17
|
import { dirname, join, relative, resolve, win32 } from "node:path";
|
|
19
18
|
import process from "node:process";
|
|
20
19
|
import { fileURLToPath } from "node:url";
|
|
21
20
|
import { collectPreviousDashboardSessions } from "./start-dashboard-history.mjs";
|
|
21
|
+
import { createDashboardLaunchPage } from "./start-dashboard-launch-page.mjs";
|
|
22
22
|
import {
|
|
23
|
+
isProcessAlive,
|
|
23
24
|
stopOwnedDashboardProcess,
|
|
24
25
|
stopProcessTree,
|
|
25
26
|
teardownWarning,
|
|
26
27
|
} from "./start-dashboard-processes.mjs";
|
|
28
|
+
import { refreshIsRunning, startSeedRefresh } from "./start-dashboard-refresh.mjs";
|
|
27
29
|
|
|
28
30
|
export {
|
|
29
31
|
isAncestorProcess,
|
|
@@ -49,20 +51,15 @@ export const lockPath = join(sessionDirectory, "dashboard-sqlite-launcher.lock")
|
|
|
49
51
|
export const launcherLogPath = join(sessionDirectory, "dashboard-sqlite-launcher.log");
|
|
50
52
|
const frontendRoot = resolve(workspaceRoot, ".agents/dashboard/apps/frontend/dist");
|
|
51
53
|
const sqliteReadModelDirectory = resolve(workspaceRoot, ".local", "dashboard-sqlite");
|
|
52
|
-
// Keep the immutable seed cache
|
|
53
|
-
// generations and the isolated background refresher must never derive a
|
|
54
|
-
// different cache directory from their temporary SQLite path.
|
|
54
|
+
// Keep the immutable seed cache durable and independent of temporary SQLite generations.
|
|
55
55
|
const sqliteSnapshotCacheDirectory = join(sqliteReadModelDirectory, "cache");
|
|
56
|
-
// Node's bytecode cache is disposable
|
|
57
|
-
// data. Keep it beside the private SQLite generations so fresh-port semantics
|
|
58
|
-
// and canonical sources remain unchanged.
|
|
56
|
+
// Node's bytecode cache is disposable acceleration beside private SQLite generations.
|
|
59
57
|
const nodeCompileCacheDirectory = join(sqliteReadModelDirectory, "node-compile-cache");
|
|
60
58
|
const installedServer = resolve(packageRoot, "server.mjs");
|
|
61
59
|
const sourceServer = resolve(packageRoot, "dist/server.cjs");
|
|
62
60
|
const healthPath = "/api/v2/health";
|
|
63
|
-
// Fresh-generation probe marker: changes the immutable source fingerprint only.
|
|
64
61
|
// Keep the preparation lock through a full build so concurrent clicks coalesce safely.
|
|
65
|
-
const lockTimeoutMs =
|
|
62
|
+
const lockTimeoutMs = 210_000;
|
|
66
63
|
const staleLockMs = 30_000;
|
|
67
64
|
const lockHeartbeatFloorMs = 10;
|
|
68
65
|
// A cold immutable seed can legitimately take ~176 s on a busy Windows
|
|
@@ -262,6 +259,10 @@ export async function findReusableDashboardSession({
|
|
|
262
259
|
const port = matches.find((candidate) => candidate !== null);
|
|
263
260
|
return port === undefined ? null : { ...session, port };
|
|
264
261
|
}
|
|
262
|
+
|
|
263
|
+
async function backgroundRefreshIsRunning() {
|
|
264
|
+
return refreshIsRunning(join(sessionDirectory, "dashboard-sqlite-refresh.json"), isProcessAlive);
|
|
265
|
+
}
|
|
265
266
|
async function latestModifiedAt(paths) {
|
|
266
267
|
let latest = 0;
|
|
267
268
|
for (const path of paths) {
|
|
@@ -345,6 +346,7 @@ export async function withLauncherLock(
|
|
|
345
346
|
path = lockPath,
|
|
346
347
|
timeoutMs = lockTimeoutMs,
|
|
347
348
|
staleAfterMs = staleLockMs,
|
|
349
|
+
onWait = () => {},
|
|
348
350
|
} = {},
|
|
349
351
|
) {
|
|
350
352
|
await mkdir(directory, { recursive: true });
|
|
@@ -371,6 +373,7 @@ export async function withLauncherLock(
|
|
|
371
373
|
await rm(path, { recursive: true, force: true });
|
|
372
374
|
continue;
|
|
373
375
|
}
|
|
376
|
+
onWait();
|
|
374
377
|
await delay(100);
|
|
375
378
|
}
|
|
376
379
|
}
|
|
@@ -438,7 +441,12 @@ export function browserLaunchCommands(
|
|
|
438
441
|
],
|
|
439
442
|
];
|
|
440
443
|
}
|
|
441
|
-
return
|
|
444
|
+
return platform === "darwin"
|
|
445
|
+
? [["open", [target]]]
|
|
446
|
+
: [
|
|
447
|
+
["xdg-open", [target]],
|
|
448
|
+
["gio", ["open", target]],
|
|
449
|
+
];
|
|
442
450
|
}
|
|
443
451
|
export async function openBrowser(target, spawnImpl = spawn) {
|
|
444
452
|
if (process.env.ZAIP_UI_NO_BROWSER === "1") return true;
|
|
@@ -469,130 +477,8 @@ export async function openBrowser(target, spawnImpl = spawn) {
|
|
|
469
477
|
appendLauncherLog(`Could not dispatch the dashboard URL to the default browser: ${target}`);
|
|
470
478
|
return false;
|
|
471
479
|
}
|
|
472
|
-
export
|
|
473
|
-
|
|
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();
|
|
480
|
-
let resolveRedirectObserved;
|
|
481
|
-
const redirectObserved = new Promise((resolveObserved) => {
|
|
482
|
-
resolveRedirectObserved = resolveObserved;
|
|
483
|
-
});
|
|
484
|
-
const server = createHttpServer((request, response) => {
|
|
485
|
-
response.setHeader("Cache-Control", "no-store");
|
|
486
|
-
if (request.url?.startsWith("/api/v2/ready")) {
|
|
487
|
-
if (redirectUrl !== null) resolveRedirectObserved(true);
|
|
488
|
-
response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
489
|
-
response.end(JSON.stringify({ target: redirectUrl }));
|
|
490
|
-
return;
|
|
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
|
-
}
|
|
497
|
-
if (request.url?.startsWith(healthPath)) {
|
|
498
|
-
response.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
|
|
499
|
-
response.end(JSON.stringify({ kind: "zaip-dashboard-launcher", status: "starting" }));
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
503
|
-
response.end(`<!doctype html>
|
|
504
|
-
<html lang="pt-BR">
|
|
505
|
-
<head>
|
|
506
|
-
<meta charset="utf-8">
|
|
507
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
508
|
-
<title>Dashboard Zaip AIOS</title>
|
|
509
|
-
<style>
|
|
510
|
-
:root{color-scheme:dark;font-family:Inter,system-ui,sans-serif;background:#010610;color:#f1f5f9}
|
|
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}
|
|
513
|
-
</style>
|
|
514
|
-
</head>
|
|
515
|
-
<body>
|
|
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>
|
|
518
|
-
</main>
|
|
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{}}
|
|
523
|
-
async function waitForDashboard(){
|
|
524
|
-
try{
|
|
525
|
-
await updateProgress();
|
|
526
|
-
const ready=await fetch("/api/v2/ready",{cache:"no-store"});
|
|
527
|
-
const next=await ready.json();
|
|
528
|
-
if(next.target){showHandoff(next.target);setTimeout(()=>location.replace(next.target),0);return}
|
|
529
|
-
const response=await fetch("${healthPath}",{cache:"no-store"});
|
|
530
|
-
const payload=await response.json();
|
|
531
|
-
if(response.ok&&payload.kind==="${runtimeKind}"){location.reload();return}
|
|
532
|
-
}catch{}
|
|
533
|
-
setTimeout(waitForDashboard,250)
|
|
534
|
-
}
|
|
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)
|
|
540
|
-
</script>
|
|
541
|
-
</body>
|
|
542
|
-
</html>`);
|
|
543
|
-
});
|
|
544
|
-
server.on("connection", (connection) => {
|
|
545
|
-
connections.add(connection);
|
|
546
|
-
connection.once("close", () => connections.delete(connection));
|
|
547
|
-
});
|
|
548
|
-
await new Promise((resolveListen, rejectListen) => {
|
|
549
|
-
server.once("error", rejectListen);
|
|
550
|
-
server.listen({ host, port }, resolveListen);
|
|
551
|
-
});
|
|
552
|
-
server.unref();
|
|
553
|
-
const address = server.address();
|
|
554
|
-
const listeningPort = typeof address === "object" && address !== null ? address.port : port;
|
|
555
|
-
return {
|
|
556
|
-
port: listeningPort,
|
|
557
|
-
redirect: (target) => {
|
|
558
|
-
redirectUrl = target;
|
|
559
|
-
},
|
|
560
|
-
setProgress: (next) => {
|
|
561
|
-
progress = {
|
|
562
|
-
...progress,
|
|
563
|
-
...next,
|
|
564
|
-
percent: Math.max(progress.percent, next.percent ?? progress.percent),
|
|
565
|
-
};
|
|
566
|
-
},
|
|
567
|
-
waitForRedirect: async (timeoutMs = 1_500) =>
|
|
568
|
-
Promise.race([redirectObserved, delay(timeoutMs).then(() => false)]),
|
|
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 } = {}) =>
|
|
578
|
-
new Promise((resolveClose, rejectClose) => {
|
|
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?.();
|
|
594
|
-
}),
|
|
595
|
-
};
|
|
480
|
+
export function startDashboardLaunchPage(port) {
|
|
481
|
+
return createDashboardLaunchPage({ port, host, runtimeKind, healthPath, delay });
|
|
596
482
|
}
|
|
597
483
|
async function redirectLaunchPage(launchPage, target) {
|
|
598
484
|
launchPage.redirect(target);
|
|
@@ -794,71 +680,24 @@ async function removeGeneratedReadModel(candidate) {
|
|
|
794
680
|
);
|
|
795
681
|
return false;
|
|
796
682
|
}
|
|
797
|
-
function refreshSnapshotSeedInBackground() {
|
|
798
|
-
const refreshPath = resolve(
|
|
799
|
-
sqliteReadModelDirectory,
|
|
800
|
-
`refresh-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`,
|
|
801
|
-
);
|
|
683
|
+
async function refreshSnapshotSeedInBackground() {
|
|
802
684
|
const refreshStatusPath = join(sessionDirectory, "dashboard-sqlite-refresh.json");
|
|
803
685
|
const refreshLogPath = join(sessionDirectory, "dashboard-sqlite-refresh.log");
|
|
804
|
-
|
|
805
|
-
|
|
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,
|
|
686
|
+
return startSeedRefresh({
|
|
687
|
+
appendLog: appendLauncherLog,
|
|
835
688
|
cacheDirectory: sqliteSnapshotCacheDirectory,
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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(() => {});
|
|
689
|
+
isRunning: backgroundRefreshIsRunning,
|
|
690
|
+
isProcessAlive,
|
|
691
|
+
lockPath: join(sessionDirectory, "dashboard-sqlite-refresh.lock"),
|
|
692
|
+
packageRoot,
|
|
693
|
+
randomBytes,
|
|
694
|
+
refreshLogPath,
|
|
695
|
+
refreshStatusPath,
|
|
696
|
+
sqliteDirectory: sqliteReadModelDirectory,
|
|
697
|
+
stopProcessTree,
|
|
698
|
+
workspaceRoot,
|
|
699
|
+
writeJsonAtomic,
|
|
857
700
|
});
|
|
858
|
-
worker.unref();
|
|
859
|
-
appendLauncherLog(
|
|
860
|
-
`Started background SQLite seed refresh at ${refreshPath}; cache ${sqliteSnapshotCacheDirectory}; worker log ${refreshLogPath}.`,
|
|
861
|
-
);
|
|
862
701
|
}
|
|
863
702
|
async function writeLauncherStatus(status, message = "") {
|
|
864
703
|
const path = process.env.ZAIP_LAUNCH_STATUS;
|
|
@@ -906,116 +745,162 @@ async function main() {
|
|
|
906
745
|
phase: "Preparando a sessão do dashboard",
|
|
907
746
|
detail: "Coordenando esta abertura local com segurança.",
|
|
908
747
|
});
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
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,
|
|
949
|
-
token,
|
|
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 {
|
|
748
|
+
let reportedLockWait = false;
|
|
749
|
+
await withLauncherLock(
|
|
750
|
+
async () => {
|
|
751
|
+
phaseTiming("lock_acquired", launchStartedAt);
|
|
752
|
+
const buildStartedAt = Date.now();
|
|
957
753
|
launchPage.setProgress({
|
|
958
|
-
percent:
|
|
959
|
-
phase:
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
754
|
+
percent: 28,
|
|
755
|
+
phase: "Verificando os artefatos do dashboard",
|
|
756
|
+
detail: "Usando o build existente quando ele já está atualizado.",
|
|
757
|
+
});
|
|
758
|
+
const buildSignature = await ensureFreshDashboardBuild();
|
|
759
|
+
phaseTiming("build_complete", buildStartedAt, { build_signature: buildSignature });
|
|
760
|
+
// A second deliberate click must use the already healthy compatible
|
|
761
|
+
// runtime. Build signature makes this safe across dashboard updates;
|
|
762
|
+
// an incompatible session continues through a replacement handoff.
|
|
763
|
+
const reusable = await findReusableDashboardSession({
|
|
764
|
+
expectedBuildSignature: buildSignature,
|
|
765
|
+
expectedSnapshotState: "current",
|
|
965
766
|
});
|
|
966
|
-
if (
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
767
|
+
if (reusable !== null) {
|
|
768
|
+
appendLauncherLog(
|
|
769
|
+
`Reused healthy compatible dashboard session on port ${reusable.port}, PID ${reusable.serverPid ?? "unknown"}.`,
|
|
770
|
+
);
|
|
771
|
+
await redirectLaunchPage(launchPage, dashboardUrl(reusable.port, reusable.token));
|
|
772
|
+
launchPage = null;
|
|
773
|
+
await writeLauncherStatus("ok");
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
// A stale seed is still a valid read-only dashboard while its one
|
|
777
|
+
// canonical refresh runs. Reuse it instead of turning repeated clicks
|
|
778
|
+
// into competing full SQLite rebuilds and private server generations.
|
|
779
|
+
if (await backgroundRefreshIsRunning()) {
|
|
780
|
+
const updating = await findReusableDashboardSession({
|
|
781
|
+
expectedBuildSignature: buildSignature,
|
|
782
|
+
expectedSnapshotState: "updating",
|
|
783
|
+
});
|
|
784
|
+
if (updating !== null) {
|
|
785
|
+
appendLauncherLog(
|
|
786
|
+
`Reused updating dashboard session on port ${updating.port}; canonical seed refresh is already running.`,
|
|
787
|
+
);
|
|
788
|
+
await redirectLaunchPage(launchPage, dashboardUrl(updating.port, updating.token, true));
|
|
789
|
+
launchPage = null;
|
|
790
|
+
await writeLauncherStatus("ok");
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const previousSessions = await previousDashboardSessions();
|
|
795
|
+
const excludedPorts = new Set([
|
|
796
|
+
...previousSessions.map((session) => session.port),
|
|
797
|
+
launchPort,
|
|
798
|
+
]);
|
|
799
|
+
const serverPath = await assertRuntimeReady();
|
|
800
|
+
const readyStartedAt = Date.now();
|
|
801
|
+
launchPage.setProgress({
|
|
802
|
+
percent: 42,
|
|
803
|
+
phase: "Abrindo o banco local descartável",
|
|
804
|
+
detail: "Criando uma cópia privada do read model SQLite para esta sessão.",
|
|
805
|
+
});
|
|
806
|
+
const { child, port, dbPath, snapshotCache } = await startDashboardServer(
|
|
807
|
+
serverPath,
|
|
808
|
+
token,
|
|
809
|
+
excludedPorts,
|
|
810
|
+
null,
|
|
811
|
+
(progress) => launchPage?.setProgress(progress),
|
|
970
812
|
);
|
|
971
|
-
phaseTiming("
|
|
972
|
-
|
|
973
|
-
if (snapshotCache === "stale_seed") refreshSnapshotSeedInBackground();
|
|
974
|
-
currentSession = {
|
|
975
|
-
kind: runtimeKind,
|
|
976
|
-
workspace: workspaceRoot,
|
|
977
|
-
workspaceKey,
|
|
978
|
-
rootPid: process.pid,
|
|
979
|
-
serverPid: child.pid,
|
|
813
|
+
phaseTiming("server_ready", readyStartedAt, {
|
|
814
|
+
server_pid: child.pid,
|
|
980
815
|
port,
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
816
|
+
db_path: dbPath,
|
|
817
|
+
});
|
|
818
|
+
ownedChild = child;
|
|
819
|
+
try {
|
|
820
|
+
launchPage.setProgress({
|
|
821
|
+
percent: 92,
|
|
822
|
+
phase:
|
|
823
|
+
snapshotCache === "stale_seed"
|
|
824
|
+
? "Abrindo snapshot local validado"
|
|
825
|
+
: "Dashboard pronto",
|
|
826
|
+
detail:
|
|
827
|
+
snapshotCache === "stale_seed"
|
|
828
|
+
? "Os dados canônicos estão sendo atualizados em segundo plano para a próxima abertura."
|
|
829
|
+
: "Validando saúde e dados iniciais antes de abrir a tela principal.",
|
|
830
|
+
});
|
|
831
|
+
if (snapshotCache === "stale_seed") await delay(350);
|
|
832
|
+
await redirectLaunchPage(
|
|
833
|
+
launchPage,
|
|
834
|
+
dashboardUrl(port, token, snapshotCache === "stale_seed"),
|
|
835
|
+
);
|
|
836
|
+
phaseTiming("handoff_complete", launchStartedAt, { server_pid: child.pid, port });
|
|
837
|
+
launchPage = null;
|
|
838
|
+
if (snapshotCache === "stale_seed") await refreshSnapshotSeedInBackground();
|
|
839
|
+
currentSession = {
|
|
840
|
+
kind: runtimeKind,
|
|
841
|
+
workspace: workspaceRoot,
|
|
842
|
+
workspaceKey,
|
|
843
|
+
rootPid: process.pid,
|
|
844
|
+
serverPid: child.pid,
|
|
845
|
+
port,
|
|
846
|
+
token,
|
|
847
|
+
dbPath,
|
|
848
|
+
buildSignature,
|
|
849
|
+
generationId: `${process.pid}-${child.pid}-${port}-${randomBytes(6).toString("hex")}`,
|
|
850
|
+
// Never pin subsequent clicks to a stale seed. A completed
|
|
851
|
+
// background refresh is consumed by a fresh handoff on the next
|
|
852
|
+
// click, then the old updating generation is retired below.
|
|
853
|
+
snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",
|
|
854
|
+
createdAt: new Date().toISOString(),
|
|
855
|
+
};
|
|
856
|
+
await writeDashboardSession(currentSession);
|
|
857
|
+
await recordPortStatus(currentSession, "active");
|
|
858
|
+
const warnings = [];
|
|
859
|
+
for (const previous of previousSessions) {
|
|
860
|
+
const status = await stopOwnedDashboard(previous);
|
|
861
|
+
await recordPortStatus(previous, status);
|
|
862
|
+
if (status === "closed") await removeGeneratedReadModel(previous.dbPath);
|
|
863
|
+
if (teardownWarning(status))
|
|
864
|
+
warnings.push(`não foi possível fechar a porta anterior ${previous.port}`);
|
|
865
|
+
else if (status === "skipped_unowned")
|
|
866
|
+
appendLauncherLog(
|
|
867
|
+
`Skipped teardown of port ${previous.port}: registered server PID does not own its live listener.`,
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
if (warnings.length > 0)
|
|
1001
871
|
appendLauncherLog(
|
|
1002
|
-
`
|
|
872
|
+
`Dashboard launched on port ${port}; prior-port cleanup: ${warnings.join("; ")}`,
|
|
1003
873
|
);
|
|
874
|
+
await writeLauncherStatus("ok");
|
|
875
|
+
} catch (error) {
|
|
876
|
+
await stopProcessTree(child.pid);
|
|
877
|
+
if (currentSession !== null) await recordPortStatus(currentSession, "failed");
|
|
878
|
+
await clearOwnedDashboardSession();
|
|
879
|
+
throw error;
|
|
1004
880
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
);
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
onWait: () => {
|
|
884
|
+
if (reportedLockWait) return;
|
|
885
|
+
reportedLockWait = true;
|
|
886
|
+
launchPage?.setProgress({
|
|
887
|
+
percent: 22,
|
|
888
|
+
phase: "Aguardando outra abertura do dashboard",
|
|
889
|
+
detail: "A outra sessão está concluindo o carregamento local antes desta continuar.",
|
|
890
|
+
});
|
|
891
|
+
},
|
|
892
|
+
},
|
|
893
|
+
);
|
|
1017
894
|
} catch (error) {
|
|
1018
|
-
if (launchPage !== null)
|
|
895
|
+
if (launchPage !== null) {
|
|
896
|
+
launchPage.setFailure({
|
|
897
|
+
percent: 100,
|
|
898
|
+
phase: "Não foi possível preparar o dashboard",
|
|
899
|
+
detail: formatLauncherError(error instanceof Error ? error.message : String(error), 900),
|
|
900
|
+
});
|
|
901
|
+
launchPage.closeAfterFailureObservation();
|
|
902
|
+
launchPage = null;
|
|
903
|
+
}
|
|
1019
904
|
throw error;
|
|
1020
905
|
}
|
|
1021
906
|
if (ownedChild === null) return;
|
|
@@ -1077,7 +962,11 @@ async function waitForDashboard(port, child) {
|
|
|
1077
962
|
}
|
|
1078
963
|
await delay(100);
|
|
1079
964
|
}
|
|
1080
|
-
|
|
965
|
+
// A cold read-model timeout is not a port collision. Retrying would start
|
|
966
|
+
// up to four more full builds and exceed the launcher deadline.
|
|
967
|
+
const timedOut = new Error(`Timed out waiting for ${dashboardHealthUrl(port)}`);
|
|
968
|
+
timedOut.retryable = false;
|
|
969
|
+
throw timedOut;
|
|
1081
970
|
}
|
|
1082
971
|
function exists(path) {
|
|
1083
972
|
return access(path).then(
|