@stravica-ai/digital-operator-pa 0.1.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/LICENSE +21 -0
- package/README.md +170 -0
- package/bin/desk +9 -0
- package/bin/distill +2 -0
- package/bin/estate-sync +2 -0
- package/bin/launch +2 -0
- package/bin/memory-sync +2 -0
- package/bin/migrate-inspect +2 -0
- package/bin/pa +2 -0
- package/bin/pa-coordinator-hook +2 -0
- package/bin/pa-init +2 -0
- package/bin/research +2 -0
- package/bin/scheduler +2 -0
- package/bin/secrets +2 -0
- package/bin/slice +2 -0
- package/bin/smoke +9 -0
- package/bin/sync +2 -0
- package/dist/engineering-pa-cli/README.md +205 -0
- package/dist/engineering-pa-cli/bin/estate-sync +2 -0
- package/dist/engineering-pa-cli/bin/launch +2 -0
- package/dist/engineering-pa-cli/bin/pa +2 -0
- package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
- package/dist/engineering-pa-cli/bin/research +2 -0
- package/dist/engineering-pa-cli/bin/scheduler +2 -0
- package/dist/engineering-pa-cli/bin/secrets +2 -0
- package/dist/engineering-pa-cli/bin/smoke +9 -0
- package/dist/engineering-pa-cli/bin/sync +2 -0
- package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
- package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
- package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
- package/dist/engineering-pa-cli/docs/gsc.md +175 -0
- package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
- package/dist/engineering-pa-cli/docs/secrets.md +221 -0
- package/dist/engineering-pa-cli/docs/smoke.md +113 -0
- package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
- package/dist/engineering-pa-cli/index.mjs +17 -0
- package/dist/engineering-pa-cli/package.json +37 -0
- package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
- package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
- package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
- package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
- package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
- package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
- package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
- package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
- package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
- package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
- package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
- package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
- package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
- package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
- package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
- package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
- package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
- package/dist/engineering-pa-cli/src/launch/README.md +214 -0
- package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
- package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
- package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
- package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
- package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
- package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
- package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
- package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
- package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
- package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
- package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
- package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
- package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
- package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
- package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
- package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
- package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
- package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
- package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
- package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
- package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
- package/dist/engineering-pa-cli/src/research/README.md +360 -0
- package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
- package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
- package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
- package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
- package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
- package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
- package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
- package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
- package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
- package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
- package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
- package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
- package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
- package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
- package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
- package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
- package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
- package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
- package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
- package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
- package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
- package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
- package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
- package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
- package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
- package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
- package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
- package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
- package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
- package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
- package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
- package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
- package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
- package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
- package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
- package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
- package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
- package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
- package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
- package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
- package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
- package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
- package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
- package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
- package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
- package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
- package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
- package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
- package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
- package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
- package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
- package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
- package/dist/engineering-pa-core/README.md +60 -0
- package/dist/engineering-pa-core/bin/desk +9 -0
- package/dist/engineering-pa-core/bin/distill +2 -0
- package/dist/engineering-pa-core/bin/memory-sync +2 -0
- package/dist/engineering-pa-core/bin/slice +2 -0
- package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
- package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
- package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
- package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
- package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
- package/dist/engineering-pa-core/docs/commands/init.md +159 -0
- package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
- package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
- package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
- package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
- package/dist/engineering-pa-core/docs/lock.md +155 -0
- package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
- package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
- package/dist/engineering-pa-core/docs/scheduling.md +35 -0
- package/dist/engineering-pa-core/docs/standards/README.md +24 -0
- package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
- package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
- package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
- package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
- package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
- package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
- package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
- package/dist/engineering-pa-core/docs/working-directory.md +29 -0
- package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
- package/dist/engineering-pa-core/index.mjs +21 -0
- package/dist/engineering-pa-core/knowledge/README.md +53 -0
- package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
- package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
- package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
- package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
- package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
- package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
- package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
- package/dist/engineering-pa-core/package.json +26 -0
- package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
- package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
- package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
- package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
- package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
- package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
- package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
- package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
- package/dist/engineering-pa-core/schema/project.json +114 -0
- package/dist/engineering-pa-core/schema/schedule.json +63 -0
- package/dist/engineering-pa-core/schema/work-item.json +92 -0
- package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
- package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
- package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
- package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
- package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
- package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
- package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
- package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
- package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
- package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
- package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
- package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
- package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
- package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
- package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
- package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
- package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
- package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
- package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
- package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
- package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
- package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
- package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
- package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
- package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
- package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
- package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
- package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
- package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
- package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
- package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
- package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
- package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
- package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
- package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
- package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
- package/dist/engineering-pa-librarian/README.md +38 -0
- package/dist/engineering-pa-librarian/index.mjs +12 -0
- package/dist/engineering-pa-librarian/package.json +19 -0
- package/dist/engineering-pa-migrate-inspect/README.md +131 -0
- package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
- package/dist/engineering-pa-migrate-inspect/package.json +21 -0
- package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
- package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
- package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
- package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
- package/dist/engineering-pa-runbook/README.md +103 -0
- package/dist/engineering-pa-runbook/package.json +18 -0
- package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
- package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
- package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
- package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
- package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
- package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
- package/dist/engineering-pa-scaffold/README.md +164 -0
- package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
- package/dist/engineering-pa-scaffold/index.mjs +42 -0
- package/dist/engineering-pa-scaffold/package.json +22 -0
- package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
- package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
- package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
- package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
- package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
- package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
- package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
- package/dist/engineering-pa-types-engineering/README.md +35 -0
- package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
- package/dist/engineering-pa-types-engineering/index.mjs +9 -0
- package/dist/engineering-pa-types-engineering/package.json +19 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
- package/dist/engineering-pa-workspace/README.md +197 -0
- package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
- package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
- package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
- package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
- package/dist/engineering-pa-workspace/app/banner.js +320 -0
- package/dist/engineering-pa-workspace/app/delegations.html +345 -0
- package/dist/engineering-pa-workspace/app/home.html +1060 -0
- package/dist/engineering-pa-workspace/app/index.html +361 -0
- package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
- package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
- package/dist/engineering-pa-workspace/app/nav.json +75 -0
- package/dist/engineering-pa-workspace/app/notes.html +199 -0
- package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
- package/dist/engineering-pa-workspace/app/profile.html +243 -0
- package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
- package/dist/engineering-pa-workspace/app/projects.html +297 -0
- package/dist/engineering-pa-workspace/app/repos.html +598 -0
- package/dist/engineering-pa-workspace/app/schedules.html +486 -0
- package/dist/engineering-pa-workspace/app/secrets.html +164 -0
- package/dist/engineering-pa-workspace/app/staging.html +319 -0
- package/dist/engineering-pa-workspace/app/tasks.html +465 -0
- package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
- package/dist/engineering-pa-workspace/app/viewer.html +116 -0
- package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
- package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
- package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
- package/dist/engineering-pa-workspace/index.mjs +12 -0
- package/dist/engineering-pa-workspace/nav.json.example +8 -0
- package/dist/engineering-pa-workspace/package.json +27 -0
- package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
- package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
- package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
- package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
- package/dist/engineering-pa-workspace/src/server.mjs +516 -0
- package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
- package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
- package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
- package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
- package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
- package/index.mjs +60 -0
- package/package.json +58 -0
- package/templates/banned-ai-tells.md +102 -0
- package/templates/purpose.md +105 -0
- package/templates/voice-guide.md +98 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// runner.mjs — external-effects chokepoint for the smoke tool.
|
|
2
|
+
//
|
|
3
|
+
// Everything that touches the outside world (spawning processes, copying
|
|
4
|
+
// files, HTTP requests) lives in this module. Commands and checks depend
|
|
5
|
+
// on this module; nothing else spawns or fetches.
|
|
6
|
+
//
|
|
7
|
+
// All helpers return plain Promises resolving to structured results —
|
|
8
|
+
// they never throw for caller-recoverable outcomes (non-zero exits,
|
|
9
|
+
// HTTP errors, missing files). They throw only for programmer errors
|
|
10
|
+
// (bad arguments, missing binaries that the host platform lacks).
|
|
11
|
+
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { mkdtemp, cp, rm, access, stat } from 'node:fs/promises';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { constants as fsConstants } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Spawn a process without a shell. Returns { code, signal, stdout, stderr }.
|
|
20
|
+
* Never rejects for non-zero exits — callers must inspect `code`.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} command
|
|
23
|
+
* @param {string[]} args
|
|
24
|
+
* @param {object} [opts] { cwd, env, timeoutMs, stdin }
|
|
25
|
+
*/
|
|
26
|
+
export function spawnProcess(command, args = [], opts = {}) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const child = spawn(command, args, {
|
|
29
|
+
cwd: opts.cwd,
|
|
30
|
+
env: opts.env ?? process.env,
|
|
31
|
+
shell: false,
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let stdout = '';
|
|
36
|
+
let stderr = '';
|
|
37
|
+
let timedOut = false;
|
|
38
|
+
|
|
39
|
+
child.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
|
|
40
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
|
|
41
|
+
|
|
42
|
+
const timer = opts.timeoutMs
|
|
43
|
+
? setTimeout(() => { timedOut = true; child.kill('SIGTERM'); }, opts.timeoutMs)
|
|
44
|
+
: null;
|
|
45
|
+
|
|
46
|
+
child.on('error', (err) => {
|
|
47
|
+
if (timer) clearTimeout(timer);
|
|
48
|
+
reject(err);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on('close', (code, signal) => {
|
|
52
|
+
if (timer) clearTimeout(timer);
|
|
53
|
+
resolve({ code, signal, stdout, stderr, timedOut });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (opts.stdin != null) {
|
|
57
|
+
child.stdin.write(opts.stdin);
|
|
58
|
+
}
|
|
59
|
+
child.stdin.end();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Launch a long-running process and return the ChildProcess handle.
|
|
65
|
+
* The caller is responsible for killing it.
|
|
66
|
+
*/
|
|
67
|
+
export function launchProcess(command, args = [], opts = {}) {
|
|
68
|
+
return spawn(command, args, {
|
|
69
|
+
cwd: opts.cwd,
|
|
70
|
+
env: opts.env ?? process.env,
|
|
71
|
+
shell: false,
|
|
72
|
+
stdio: opts.stdio ?? ['ignore', 'ignore', 'ignore'],
|
|
73
|
+
detached: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Kill a child process and await exit. Safe to call on an already-exited child.
|
|
79
|
+
*/
|
|
80
|
+
export async function killProcess(child) {
|
|
81
|
+
if (!child || child.exitCode != null || child.signalCode != null) return;
|
|
82
|
+
child.kill('SIGTERM');
|
|
83
|
+
await new Promise((resolve) => {
|
|
84
|
+
const timer = setTimeout(() => { try { child.kill('SIGKILL'); } catch {} resolve(); }, 2000);
|
|
85
|
+
child.once('close', () => { clearTimeout(timer); resolve(); });
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a temp directory with a stable prefix. Returns the absolute path.
|
|
91
|
+
*/
|
|
92
|
+
export async function makeTempDir(prefix = 'dave-smoke-') {
|
|
93
|
+
return mkdtemp(join(tmpdir(), prefix));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Remove a directory recursively. Never rejects for missing paths.
|
|
98
|
+
*/
|
|
99
|
+
export async function removeDir(dir) {
|
|
100
|
+
if (!dir) return;
|
|
101
|
+
await rm(dir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Copy a directory tree from src to dest, excluding a fixed set of top-level
|
|
106
|
+
* names. Matches the behaviour of `rsync -a --exclude=.git --exclude=node_modules`.
|
|
107
|
+
*/
|
|
108
|
+
export async function copyTree(src, dest, excludes = ['.git', 'node_modules']) {
|
|
109
|
+
const excludeSet = new Set(excludes);
|
|
110
|
+
await cp(src, dest, {
|
|
111
|
+
recursive: true,
|
|
112
|
+
dereference: false,
|
|
113
|
+
preserveTimestamps: true,
|
|
114
|
+
filter: (source) => {
|
|
115
|
+
// Only filter at the top level — once inside an included dir, copy
|
|
116
|
+
// everything. This matches rsync --exclude='.git' semantics where
|
|
117
|
+
// only top-level matches are excluded (our exclusion list is
|
|
118
|
+
// ".git" and "node_modules", both only meaningful at the root).
|
|
119
|
+
const rel = source.slice(src.length).replace(/^[\\/]+/, '');
|
|
120
|
+
if (rel === '') return true;
|
|
121
|
+
const firstSegment = rel.split(/[\\/]/)[0];
|
|
122
|
+
return !excludeSet.has(firstSegment);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check that a path exists. Returns true/false.
|
|
129
|
+
*/
|
|
130
|
+
export async function pathExists(p) {
|
|
131
|
+
try {
|
|
132
|
+
await access(p, fsConstants.F_OK);
|
|
133
|
+
return true;
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check that a path exists and is executable.
|
|
141
|
+
*/
|
|
142
|
+
export async function pathExecutable(p) {
|
|
143
|
+
try {
|
|
144
|
+
await access(p, fsConstants.F_OK | fsConstants.X_OK);
|
|
145
|
+
const s = await stat(p);
|
|
146
|
+
return s.isFile();
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Perform an HTTP GET. Returns { status, body }. Never rejects for HTTP
|
|
154
|
+
* errors — callers inspect status. Rejects only for network/DNS/abort.
|
|
155
|
+
*
|
|
156
|
+
* Uses the global `fetch` available in Node 20+.
|
|
157
|
+
*/
|
|
158
|
+
export async function httpGet(url, opts = {}) {
|
|
159
|
+
const controller = new AbortController();
|
|
160
|
+
const timer = opts.timeoutMs
|
|
161
|
+
? setTimeout(() => controller.abort(), opts.timeoutMs)
|
|
162
|
+
: null;
|
|
163
|
+
try {
|
|
164
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
165
|
+
const body = await res.text();
|
|
166
|
+
return { status: res.status, body };
|
|
167
|
+
} finally {
|
|
168
|
+
if (timer) clearTimeout(timer);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Poll until an HTTP endpoint becomes reachable or the attempt budget
|
|
174
|
+
* is exhausted. Returns true on success.
|
|
175
|
+
*/
|
|
176
|
+
export async function waitForHttp(url, { attempts = 20, intervalMs = 500, timeoutMs = 1000 } = {}) {
|
|
177
|
+
for (let i = 0; i < attempts; i++) {
|
|
178
|
+
try {
|
|
179
|
+
const res = await httpGet(url, { timeoutMs });
|
|
180
|
+
if (res.status >= 200 && res.status < 500) return true;
|
|
181
|
+
} catch {
|
|
182
|
+
// swallow — connection refused / reset during boot is expected
|
|
183
|
+
}
|
|
184
|
+
await sleep(intervalMs);
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function sleep(ms) {
|
|
190
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
191
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// staging-alias — verb router for `pa staging-alias <verb>`.
|
|
2
|
+
//
|
|
3
|
+
// Pattern: a tiny "router worker" bound as a Custom Domain on
|
|
4
|
+
// `staging.<site>` proxies to the desired preview-URL on workers.dev.
|
|
5
|
+
// Cloudflare Workers Custom Domains cannot be pointed at a specific
|
|
6
|
+
// version, and Preview URLs cannot run on a non-workers.dev subdomain
|
|
7
|
+
// (CF docs, confirmed 2026-05-21), so the router-worker indirection is
|
|
8
|
+
// the cleanest available pattern.
|
|
9
|
+
//
|
|
10
|
+
// The CLI enforces the multi-CF-account safety drill (read /accounts/{id}
|
|
11
|
+
// and assert `result.name` matches the per-site expected name) before
|
|
12
|
+
// any write. Aborts loudly on mismatch.
|
|
13
|
+
//
|
|
14
|
+
// Exit codes (engineering-pa-core/docs/standards/tooling.md §6):
|
|
15
|
+
// 0 ok | 1 usage error | 2 operational failure | 3 transport/IO failure
|
|
16
|
+
|
|
17
|
+
import { runSet } from './commands/set.mjs';
|
|
18
|
+
import { runUnset } from './commands/unset.mjs';
|
|
19
|
+
import { runStatus } from './commands/status.mjs';
|
|
20
|
+
import { runList } from './commands/list.mjs';
|
|
21
|
+
|
|
22
|
+
const HELP = `pa staging-alias — canonical staging.<site> alias for CF-hosted sites
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
pa staging-alias set --site <hostname> --branch <branch> [--version <id>]
|
|
26
|
+
Wire staging.<hostname> to the source worker's latest preview
|
|
27
|
+
version (or the explicit --version). Idempotent — re-runs flip
|
|
28
|
+
the alias to a new branch's preview cleanly.
|
|
29
|
+
|
|
30
|
+
pa staging-alias unset --site <hostname> [--keep-worker]
|
|
31
|
+
Remove the staging.<hostname> binding and delete the router worker.
|
|
32
|
+
--keep-worker leaves the router script in place (binding still
|
|
33
|
+
removed).
|
|
34
|
+
|
|
35
|
+
pa staging-alias status --site <hostname>
|
|
36
|
+
Show current target (router worker, STAGING_TARGET, custom-domain
|
|
37
|
+
binding) for one site. JSON on stdout.
|
|
38
|
+
|
|
39
|
+
pa staging-alias list
|
|
40
|
+
Across every registered site, the current staging target. JSON
|
|
41
|
+
array on stdout.
|
|
42
|
+
|
|
43
|
+
pa staging-alias -h, --help
|
|
44
|
+
Show this help.
|
|
45
|
+
|
|
46
|
+
How it works:
|
|
47
|
+
staging.<hostname> --CF Custom Domain--> <site>-staging worker
|
|
48
|
+
\\__ proxies STAGING_TARGET
|
|
49
|
+
(the workers.dev preview URL)
|
|
50
|
+
|
|
51
|
+
Multi-account safety:
|
|
52
|
+
Every write runs the 6-step CF-account drill. Vault keys are picked from
|
|
53
|
+
a per-site lookup table; the CLI then reads /accounts/{id} and aborts
|
|
54
|
+
unless result.name matches the site's expected account name.
|
|
55
|
+
|
|
56
|
+
Registered sites: stravica.ai, cliveelliott.net, besafetraining.co.uk.
|
|
57
|
+
Add a fourth via src/staging-alias/sites.mjs (one entry).
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export async function run(argv, overrides = {}) {
|
|
61
|
+
const verb = argv[0];
|
|
62
|
+
const rest = argv.slice(1);
|
|
63
|
+
if (!verb || verb === '--help' || verb === '-h' || verb === 'help') {
|
|
64
|
+
process.stdout.write(HELP);
|
|
65
|
+
return verb ? 0 : 1;
|
|
66
|
+
}
|
|
67
|
+
switch (verb) {
|
|
68
|
+
case 'set': return runSet(rest, overrides);
|
|
69
|
+
case 'unset': return runUnset(rest, overrides);
|
|
70
|
+
case 'status': return runStatus(rest, overrides);
|
|
71
|
+
case 'list': return runList(rest, overrides);
|
|
72
|
+
default: {
|
|
73
|
+
process.stderr.write(`[pa staging-alias] unknown verb: ${verb}\n`);
|
|
74
|
+
process.stderr.write(HELP);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function main(argv, overrides = {}) {
|
|
81
|
+
try {
|
|
82
|
+
const code = await run(argv, overrides);
|
|
83
|
+
return code ?? 0;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
const code = err && typeof err.exitCode === 'number' ? err.exitCode : 2;
|
|
86
|
+
if (!err || err.exitCode == null) {
|
|
87
|
+
process.stderr.write(`pa staging-alias: ${err && err.message ? err.message : err}\n`);
|
|
88
|
+
}
|
|
89
|
+
return code;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// cloudflare.mjs — Cloudflare API helpers for staging-alias.
|
|
2
|
+
//
|
|
3
|
+
// Builds on the lower-level `cfFetch` from src/gsc/cloudflare.mjs. The
|
|
4
|
+
// added surface here is the set of operations the staging-alias CLI
|
|
5
|
+
// performs:
|
|
6
|
+
//
|
|
7
|
+
// - verifyAccount(creds, expectedName) — the safety drill
|
|
8
|
+
// - listWorkerVersions(creds, worker) — to pick "latest" for --branch
|
|
9
|
+
// - resolveZoneId(creds, zoneName) — re-exported from gsc helpers
|
|
10
|
+
// - listWorkerDomains(creds, worker) — for status + idempotency
|
|
11
|
+
// - bindWorkerCustomDomain(creds, …) — bind hostname → router worker
|
|
12
|
+
// - unbindWorkerCustomDomain(creds, domainId) — remove the binding
|
|
13
|
+
// - uploadRouterWorker(creds, name, target) — push the router worker w/ env
|
|
14
|
+
// - workerScriptExists(creds, name) — for idempotent ensure
|
|
15
|
+
// - resolveAccountSubdomain(creds) — for the workers.dev URL
|
|
16
|
+
//
|
|
17
|
+
// The router worker is a tiny proxy. Its source is built into this module
|
|
18
|
+
// (no separate file to ship) so the CLI is self-contained.
|
|
19
|
+
|
|
20
|
+
import { cfFetch, resolveZoneId } from '../gsc/cloudflare.mjs';
|
|
21
|
+
|
|
22
|
+
export { cfFetch, resolveZoneId };
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read /accounts/{account_id} and assert the display name matches.
|
|
26
|
+
* Throws with exitCode=2 on mismatch — this is the load-bearing safety
|
|
27
|
+
* drill per `feedback_cf_multi_account_discipline.md`.
|
|
28
|
+
*
|
|
29
|
+
* Returns the verified account object so the caller can echo it.
|
|
30
|
+
*
|
|
31
|
+
* @param {{token: string, accountId: string}} creds
|
|
32
|
+
* @param {string} expectedName
|
|
33
|
+
* @param {Function} [fetchImpl]
|
|
34
|
+
*/
|
|
35
|
+
export async function verifyAccount(creds, expectedName, fetchImpl) {
|
|
36
|
+
const body = await cfFetch(
|
|
37
|
+
creds.token,
|
|
38
|
+
`/accounts/${creds.accountId}`,
|
|
39
|
+
{},
|
|
40
|
+
fetchImpl,
|
|
41
|
+
);
|
|
42
|
+
const actual = body && body.result && body.result.name;
|
|
43
|
+
if (!actual) {
|
|
44
|
+
throw Object.assign(
|
|
45
|
+
new Error(`CF /accounts/${creds.accountId} returned no result.name (got ${JSON.stringify(body).slice(0, 200)})`),
|
|
46
|
+
{ exitCode: 2 },
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (actual !== expectedName) {
|
|
50
|
+
throw Object.assign(
|
|
51
|
+
new Error(
|
|
52
|
+
`CF account-name mismatch — refusing to write.\n` +
|
|
53
|
+
` account_id : ${creds.accountId}\n` +
|
|
54
|
+
` expected name : ${expectedName}\n` +
|
|
55
|
+
` actual name : ${actual}\n` +
|
|
56
|
+
`Check that the vault keys you loaded match the site you are operating on ` +
|
|
57
|
+
`(see operator/knowledge/systems/web-properties/).`,
|
|
58
|
+
),
|
|
59
|
+
{ exitCode: 2 },
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return { id: body.result.id, name: actual };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Read the account-level workers.dev subdomain (e.g. `barry-ab9`). Used to
|
|
67
|
+
* build the preview-URL the router worker proxies to.
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveAccountSubdomain(creds, fetchImpl) {
|
|
70
|
+
const body = await cfFetch(
|
|
71
|
+
creds.token,
|
|
72
|
+
`/accounts/${creds.accountId}/workers/subdomain`,
|
|
73
|
+
{},
|
|
74
|
+
fetchImpl,
|
|
75
|
+
);
|
|
76
|
+
const subdomain = body && body.result && body.result.subdomain;
|
|
77
|
+
if (!subdomain) {
|
|
78
|
+
throw Object.assign(
|
|
79
|
+
new Error('CF account has no workers.dev subdomain. Enable it in the dashboard.'),
|
|
80
|
+
{ exitCode: 2 },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return subdomain;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* List Worker versions for `worker`, newest-first. Returns the parsed
|
|
88
|
+
* result.items array (limited by CF default page size).
|
|
89
|
+
*
|
|
90
|
+
* Throws with exitCode=1 when the worker doesn't exist.
|
|
91
|
+
*/
|
|
92
|
+
export async function listWorkerVersions(creds, worker, fetchImpl) {
|
|
93
|
+
try {
|
|
94
|
+
const body = await cfFetch(
|
|
95
|
+
creds.token,
|
|
96
|
+
`/accounts/${creds.accountId}/workers/scripts/${encodeURIComponent(worker)}/versions`,
|
|
97
|
+
{},
|
|
98
|
+
fetchImpl,
|
|
99
|
+
);
|
|
100
|
+
return (body.result && body.result.items) || [];
|
|
101
|
+
} catch (e) {
|
|
102
|
+
if (e.status === 404) {
|
|
103
|
+
throw Object.assign(
|
|
104
|
+
new Error(`Worker "${worker}" not found in account ${creds.accountId}.`),
|
|
105
|
+
{ exitCode: 1 },
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
throw e;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Return the version-id prefix used to build the workers.dev preview URL.
|
|
114
|
+
* CF documents the preview URL as `<version-id-prefix>-<worker>.<subdomain>.workers.dev`
|
|
115
|
+
* with the prefix being the first 8 chars of the full UUID, OR (more
|
|
116
|
+
* recently) the first hyphen-separated segment. We use the first 8 chars,
|
|
117
|
+
* which matches both formats since UUIDs begin with 8 hex chars.
|
|
118
|
+
*/
|
|
119
|
+
export function versionPreviewPrefix(versionId) {
|
|
120
|
+
const s = String(versionId || '');
|
|
121
|
+
// UUID first segment is 8 hex chars.
|
|
122
|
+
const m = s.match(/^[0-9a-f]{8}/i);
|
|
123
|
+
if (!m) {
|
|
124
|
+
throw Object.assign(
|
|
125
|
+
new Error(`version_id "${s}" is not a valid UUID prefix`),
|
|
126
|
+
{ exitCode: 1 },
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return m[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build the workers.dev preview URL for a (version, worker, subdomain) trio.
|
|
134
|
+
* https://<prefix>-<worker>.<subdomain>.workers.dev
|
|
135
|
+
*/
|
|
136
|
+
export function previewHostname(versionId, worker, subdomain) {
|
|
137
|
+
const prefix = versionPreviewPrefix(versionId);
|
|
138
|
+
return `${prefix}-${worker}.${subdomain}.workers.dev`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* List Worker domains (Custom Domains bound to the script). Returns the
|
|
143
|
+
* result array, possibly empty.
|
|
144
|
+
*/
|
|
145
|
+
export async function listWorkerDomains(creds, worker, fetchImpl) {
|
|
146
|
+
const q = new URLSearchParams({ service: worker });
|
|
147
|
+
const body = await cfFetch(
|
|
148
|
+
creds.token,
|
|
149
|
+
`/accounts/${creds.accountId}/workers/domains?${q.toString()}`,
|
|
150
|
+
{},
|
|
151
|
+
fetchImpl,
|
|
152
|
+
);
|
|
153
|
+
return body.result || [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Find a Worker domain binding by hostname (case-insensitive). Returns
|
|
158
|
+
* the binding object or null. Used for idempotency.
|
|
159
|
+
*/
|
|
160
|
+
export async function findWorkerDomain(creds, hostname, fetchImpl) {
|
|
161
|
+
const target = String(hostname || '').toLowerCase();
|
|
162
|
+
const body = await cfFetch(
|
|
163
|
+
creds.token,
|
|
164
|
+
`/accounts/${creds.accountId}/workers/domains`,
|
|
165
|
+
{},
|
|
166
|
+
fetchImpl,
|
|
167
|
+
);
|
|
168
|
+
const list = body.result || [];
|
|
169
|
+
return list.find((d) => String(d.hostname || '').toLowerCase() === target) || null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Bind a hostname to a Worker as a Custom Domain. Idempotent: if the
|
|
174
|
+
* binding already exists pointing at the same worker, returns
|
|
175
|
+
* { action: 'unchanged', record }.
|
|
176
|
+
*/
|
|
177
|
+
export async function bindWorkerCustomDomain(creds, hostname, worker, zoneId, fetchImpl) {
|
|
178
|
+
const existing = await findWorkerDomain(creds, hostname, fetchImpl);
|
|
179
|
+
if (existing) {
|
|
180
|
+
if (existing.service === worker) {
|
|
181
|
+
return { action: 'unchanged', record: existing };
|
|
182
|
+
}
|
|
183
|
+
// Different worker — rebinding is a delete + create rather than
|
|
184
|
+
// an update; the CF API does not expose a PATCH on this resource.
|
|
185
|
+
await cfFetch(
|
|
186
|
+
creds.token,
|
|
187
|
+
`/accounts/${creds.accountId}/workers/domains/${existing.id}`,
|
|
188
|
+
{ method: 'DELETE' },
|
|
189
|
+
fetchImpl,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const body = await cfFetch(
|
|
193
|
+
creds.token,
|
|
194
|
+
`/accounts/${creds.accountId}/workers/domains`,
|
|
195
|
+
{
|
|
196
|
+
method: 'PUT',
|
|
197
|
+
body: JSON.stringify({
|
|
198
|
+
environment: 'production',
|
|
199
|
+
hostname,
|
|
200
|
+
service: worker,
|
|
201
|
+
zone_id: zoneId,
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
204
|
+
fetchImpl,
|
|
205
|
+
);
|
|
206
|
+
return { action: existing ? 'rebound' : 'created', record: body.result };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Remove a Worker Custom Domain binding by hostname. Idempotent — no-op
|
|
211
|
+
* when nothing is bound.
|
|
212
|
+
*/
|
|
213
|
+
export async function unbindWorkerCustomDomain(creds, hostname, fetchImpl) {
|
|
214
|
+
const existing = await findWorkerDomain(creds, hostname, fetchImpl);
|
|
215
|
+
if (!existing) return { action: 'unchanged' };
|
|
216
|
+
await cfFetch(
|
|
217
|
+
creds.token,
|
|
218
|
+
`/accounts/${creds.accountId}/workers/domains/${existing.id}`,
|
|
219
|
+
{ method: 'DELETE' },
|
|
220
|
+
fetchImpl,
|
|
221
|
+
);
|
|
222
|
+
return { action: 'deleted', record: existing };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Check whether a Worker script exists in the account.
|
|
227
|
+
*/
|
|
228
|
+
export async function workerScriptExists(creds, worker, fetchImpl) {
|
|
229
|
+
try {
|
|
230
|
+
await cfFetch(
|
|
231
|
+
creds.token,
|
|
232
|
+
`/accounts/${creds.accountId}/workers/scripts/${encodeURIComponent(worker)}`,
|
|
233
|
+
{},
|
|
234
|
+
fetchImpl,
|
|
235
|
+
);
|
|
236
|
+
return true;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
if (e.status === 404) return false;
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Router-worker source. Reads `STAGING_TARGET` from env (set per-version
|
|
245
|
+
* via the script binding) and proxies the incoming request through.
|
|
246
|
+
*
|
|
247
|
+
* Two important details:
|
|
248
|
+
* 1. We preserve the request method, headers, body, and pathname/query
|
|
249
|
+
* so the staging hostname is fully transparent.
|
|
250
|
+
* 2. We strip the `Host:` header on the outgoing fetch so the upstream
|
|
251
|
+
* Worker sees its own hostname (and the assets handler picks the
|
|
252
|
+
* right file). The fetch API does this implicitly when we change
|
|
253
|
+
* the URL host, but we set it explicitly for clarity.
|
|
254
|
+
*/
|
|
255
|
+
export const ROUTER_SOURCE = String.raw`// staging-router — proxies staging.<site> -> STAGING_TARGET workers.dev URL.
|
|
256
|
+
// Generated by @stravica-ai/engineering-pa-cli (pa staging-alias).
|
|
257
|
+
export default {
|
|
258
|
+
async fetch(request, env, ctx) {
|
|
259
|
+
const target = env && env.STAGING_TARGET;
|
|
260
|
+
if (!target) {
|
|
261
|
+
return new Response('staging-router: STAGING_TARGET not configured', { status: 503 });
|
|
262
|
+
}
|
|
263
|
+
const inUrl = new URL(request.url);
|
|
264
|
+
const outUrl = new URL(inUrl.pathname + inUrl.search, 'https://' + target);
|
|
265
|
+
const init = {
|
|
266
|
+
method: request.method,
|
|
267
|
+
headers: new Headers(request.headers),
|
|
268
|
+
redirect: 'manual',
|
|
269
|
+
};
|
|
270
|
+
init.headers.delete('host');
|
|
271
|
+
if (!/^(GET|HEAD)$/i.test(request.method)) {
|
|
272
|
+
init.body = request.body;
|
|
273
|
+
}
|
|
274
|
+
const upstream = await fetch(outUrl.toString(), init);
|
|
275
|
+
return new Response(upstream.body, {
|
|
276
|
+
status: upstream.status,
|
|
277
|
+
statusText: upstream.statusText,
|
|
278
|
+
headers: upstream.headers,
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
`;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Upload the router worker with `STAGING_TARGET` bound to the given target
|
|
286
|
+
* hostname. Uses the modules-format multipart upload.
|
|
287
|
+
*
|
|
288
|
+
* `compatibility_date` defaults to 2026-05-21 — the day this CLI was
|
|
289
|
+
* written. Workers Builds normally pins a date; we pin ours so the
|
|
290
|
+
* router behaves consistently regardless of when it was first deployed.
|
|
291
|
+
*
|
|
292
|
+
* Returns { etag, deployedTarget }.
|
|
293
|
+
*/
|
|
294
|
+
export async function uploadRouterWorker(creds, worker, targetHost, fetchImpl, opts = {}) {
|
|
295
|
+
const compatDate = opts.compatibilityDate || '2026-05-21';
|
|
296
|
+
const metadata = {
|
|
297
|
+
main_module: 'worker.mjs',
|
|
298
|
+
compatibility_date: compatDate,
|
|
299
|
+
bindings: [
|
|
300
|
+
{ type: 'plain_text', name: 'STAGING_TARGET', text: targetHost },
|
|
301
|
+
],
|
|
302
|
+
};
|
|
303
|
+
const form = new FormData();
|
|
304
|
+
form.set('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
|
305
|
+
form.set(
|
|
306
|
+
'worker.mjs',
|
|
307
|
+
new Blob([ROUTER_SOURCE], { type: 'application/javascript+module' }),
|
|
308
|
+
'worker.mjs',
|
|
309
|
+
);
|
|
310
|
+
const fetchFn = fetchImpl || globalThis.fetch;
|
|
311
|
+
const url = `https://api.cloudflare.com/client/v4/accounts/${creds.accountId}/workers/scripts/${encodeURIComponent(worker)}`;
|
|
312
|
+
const res = await fetchFn(url, {
|
|
313
|
+
method: 'PUT',
|
|
314
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
315
|
+
body: form,
|
|
316
|
+
});
|
|
317
|
+
const text = await res.text();
|
|
318
|
+
let body;
|
|
319
|
+
try { body = text ? JSON.parse(text) : {}; } catch {
|
|
320
|
+
throw Object.assign(
|
|
321
|
+
new Error(`CF worker upload: non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`),
|
|
322
|
+
{ exitCode: 3 },
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
if (!res.ok || body.success === false) {
|
|
326
|
+
const detail = (body.errors || []).map((e) => `${e.code}: ${e.message}`).join('; ')
|
|
327
|
+
|| `HTTP ${res.status}`;
|
|
328
|
+
throw Object.assign(
|
|
329
|
+
new Error(`CF worker upload PUT /workers/scripts/${worker} failed: ${detail}`),
|
|
330
|
+
{ exitCode: 2, status: res.status, body },
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
etag: body.result && body.result.etag,
|
|
335
|
+
deployedTarget: targetHost,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Delete a Worker script entirely. Used by `unset` to clean up the
|
|
341
|
+
* router worker. Idempotent: 404 is treated as success.
|
|
342
|
+
*/
|
|
343
|
+
export async function deleteWorkerScript(creds, worker, fetchImpl) {
|
|
344
|
+
try {
|
|
345
|
+
await cfFetch(
|
|
346
|
+
creds.token,
|
|
347
|
+
`/accounts/${creds.accountId}/workers/scripts/${encodeURIComponent(worker)}?force=true`,
|
|
348
|
+
{ method: 'DELETE' },
|
|
349
|
+
fetchImpl,
|
|
350
|
+
);
|
|
351
|
+
return { action: 'deleted' };
|
|
352
|
+
} catch (e) {
|
|
353
|
+
if (e.status === 404) return { action: 'unchanged' };
|
|
354
|
+
throw e;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Read the current `STAGING_TARGET` binding on the router worker by
|
|
360
|
+
* fetching its settings. Returns null when the worker doesn't exist
|
|
361
|
+
* or the binding is absent.
|
|
362
|
+
*/
|
|
363
|
+
export async function readRouterTarget(creds, worker, fetchImpl) {
|
|
364
|
+
let body;
|
|
365
|
+
try {
|
|
366
|
+
body = await cfFetch(
|
|
367
|
+
creds.token,
|
|
368
|
+
`/accounts/${creds.accountId}/workers/scripts/${encodeURIComponent(worker)}/settings`,
|
|
369
|
+
{},
|
|
370
|
+
fetchImpl,
|
|
371
|
+
);
|
|
372
|
+
} catch (e) {
|
|
373
|
+
if (e.status === 404) return null;
|
|
374
|
+
throw e;
|
|
375
|
+
}
|
|
376
|
+
const bindings = (body.result && body.result.bindings) || [];
|
|
377
|
+
const target = bindings.find((b) => b && b.name === 'STAGING_TARGET');
|
|
378
|
+
if (!target) return null;
|
|
379
|
+
return target.text || target.value || null;
|
|
380
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// list.mjs — `pa staging-alias list`
|
|
2
|
+
//
|
|
3
|
+
// Status across all registered sites. Per-site failures are surfaced as
|
|
4
|
+
// `error` fields on the entry rather than aborting the whole listing.
|
|
5
|
+
|
|
6
|
+
import { allSites } from '../sites.mjs';
|
|
7
|
+
import { loadCloudflareCreds } from '../../gsc/cloudflare.mjs';
|
|
8
|
+
import { buildStatus } from './status.mjs';
|
|
9
|
+
|
|
10
|
+
export async function runList(args, overrides = {}) {
|
|
11
|
+
if (args.length > 0) {
|
|
12
|
+
process.stderr.write(`[staging-alias list] unexpected arguments: ${args.join(' ')}\n`);
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
const out = overrides.stdout || process.stdout;
|
|
16
|
+
const entries = [];
|
|
17
|
+
|
|
18
|
+
for (const site of allSites()) {
|
|
19
|
+
try {
|
|
20
|
+
const creds = await loadCloudflareCreds({ ...overrides, account: site.accountKey });
|
|
21
|
+
const report = await buildStatus(site, creds, overrides.fetchImpl);
|
|
22
|
+
entries.push(report);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
entries.push({
|
|
25
|
+
site: site.hostname,
|
|
26
|
+
staging: `staging.${site.hostname}`,
|
|
27
|
+
accountKey: site.accountKey,
|
|
28
|
+
error: e.message,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
out.write(JSON.stringify(entries, null, 2) + '\n');
|
|
34
|
+
// Any per-site error -> exit 2 so CI/operators notice.
|
|
35
|
+
return entries.some((e) => e.error || e.accountWarning) ? 2 : 0;
|
|
36
|
+
}
|