@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,633 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ws.mjs — Workspace server CLI
|
|
5
|
+
*
|
|
6
|
+
* Manages the workspace dev server (server.mjs) as a background process.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node ws.mjs start [--port N] [--no-open] Start server as background daemon
|
|
10
|
+
* node ws.mjs stop Stop the running server
|
|
11
|
+
* node ws.mjs restart [--port N] [--no-open] Stop then start
|
|
12
|
+
* node ws.mjs status Show server status
|
|
13
|
+
* node ws.mjs open Open browser to server URL
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawn, execSync } from 'node:child_process';
|
|
17
|
+
import { readFileSync, writeFileSync, unlinkSync, existsSync, statSync, realpathSync } from 'node:fs';
|
|
18
|
+
import { createConnection } from 'node:net';
|
|
19
|
+
import { join, resolve, dirname } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { platform } from 'node:os';
|
|
22
|
+
import { get } from 'node:http';
|
|
23
|
+
import {
|
|
24
|
+
detectSupervisor,
|
|
25
|
+
readSupervisorState,
|
|
26
|
+
findPortListener,
|
|
27
|
+
restartCommand,
|
|
28
|
+
} from './supervisor.mjs';
|
|
29
|
+
|
|
30
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
31
|
+
const __dirname = dirname(__filename);
|
|
32
|
+
const REPO_ROOT = resolve(__dirname, '..', '..', '..');
|
|
33
|
+
|
|
34
|
+
const SERVER_SCRIPT = join(__dirname, 'server.mjs');
|
|
35
|
+
// Env overrides let tests point at a temp directory so the real repo's
|
|
36
|
+
// .ws.pid / .ws.log are never written to during the subprocess test runs.
|
|
37
|
+
const PID_FILE = process.env.WS_PID_FILE || join(REPO_ROOT, '.ws.pid');
|
|
38
|
+
const LOG_FILE = process.env.WS_LOG_FILE || join(REPO_ROOT, '.ws.log');
|
|
39
|
+
const PREFS_FILE = join(REPO_ROOT, 'operator', 'config', 'preferences.json');
|
|
40
|
+
|
|
41
|
+
// Test-only override: lets e2e tests assert cross-platform parity by forcing
|
|
42
|
+
// the supervisor probe to behave as if it were running on a different OS.
|
|
43
|
+
// Honours the same dependency-injection contract as detectSupervisor's
|
|
44
|
+
// `platform` argument — when set, it is the *only* value the CLI passes
|
|
45
|
+
// through, so the production code path (no env var) remains untouched.
|
|
46
|
+
const PLATFORM_OVERRIDE = process.env.WORKSPACE_SUPERVISOR_PLATFORM_OVERRIDE || undefined;
|
|
47
|
+
|
|
48
|
+
// Single-call helper so the override env var is honoured everywhere the
|
|
49
|
+
// CLI asks "which supervisor manages this host?". Tests set the env var
|
|
50
|
+
// when spawning the ws.mjs subprocess; production callers pass nothing and
|
|
51
|
+
// detectSupervisor falls back to os.platform().
|
|
52
|
+
function getSupervisor() {
|
|
53
|
+
return detectSupervisor(PLATFORM_OVERRIDE ? { platform: PLATFORM_OVERRIDE } : undefined);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// --- Arg parsing ---
|
|
57
|
+
|
|
58
|
+
const args = process.argv.slice(2);
|
|
59
|
+
const command = args[0];
|
|
60
|
+
const flags = args.slice(1);
|
|
61
|
+
|
|
62
|
+
function getFlag(name) {
|
|
63
|
+
const idx = flags.indexOf(name);
|
|
64
|
+
if (idx === -1) return undefined;
|
|
65
|
+
return flags[idx + 1];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hasFlag(name) {
|
|
69
|
+
return flags.includes(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- Port resolution ---
|
|
73
|
+
|
|
74
|
+
function resolvePort() {
|
|
75
|
+
// 1. Default
|
|
76
|
+
let port = 4200;
|
|
77
|
+
|
|
78
|
+
// 2. Preferences override
|
|
79
|
+
try {
|
|
80
|
+
const prefs = JSON.parse(readFileSync(PREFS_FILE, 'utf-8'));
|
|
81
|
+
if (prefs.workspacePort) port = prefs.workspacePort;
|
|
82
|
+
} catch { /* no prefs — use default */ }
|
|
83
|
+
|
|
84
|
+
// 3. CLI flag override
|
|
85
|
+
const flagPort = getFlag('--port');
|
|
86
|
+
if (flagPort) {
|
|
87
|
+
const parsed = parseInt(flagPort, 10);
|
|
88
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
|
|
89
|
+
console.error(`Error: Invalid port "${flagPort}". Must be 1-65535.`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
port = parsed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return port;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// --- PID file helpers ---
|
|
99
|
+
|
|
100
|
+
function readPid() {
|
|
101
|
+
try {
|
|
102
|
+
const raw = JSON.parse(readFileSync(PID_FILE, 'utf-8'));
|
|
103
|
+
return { pid: raw.pid, port: raw.port };
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function writePid(pid, port) {
|
|
110
|
+
writeFileSync(PID_FILE, JSON.stringify({ pid, port }, null, 2) + '\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function removePid() {
|
|
114
|
+
try { unlinkSync(PID_FILE); } catch { /* already gone */ }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --- Process checks ---
|
|
118
|
+
|
|
119
|
+
function isProcessAlive(pid) {
|
|
120
|
+
try {
|
|
121
|
+
process.kill(pid, 0);
|
|
122
|
+
return true;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function checkPort(port, timeoutMs = 1000) {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
const sock = createConnection({ host: '127.0.0.1', port }, () => {
|
|
131
|
+
sock.destroy();
|
|
132
|
+
resolve(true);
|
|
133
|
+
});
|
|
134
|
+
sock.on('error', () => resolve(false));
|
|
135
|
+
sock.setTimeout(timeoutMs, () => {
|
|
136
|
+
sock.destroy();
|
|
137
|
+
resolve(false);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Best-effort: identify the PID currently listening on a port. Returns
|
|
143
|
+
// { pid, command } or null if the information is unavailable (unsupported
|
|
144
|
+
// platform, no listener, or the probe fails). Used for diagnostic output
|
|
145
|
+
// when a stale pidfile collides with an unrelated port holder.
|
|
146
|
+
function findPortHolder(port) {
|
|
147
|
+
try {
|
|
148
|
+
if (platform() === 'win32') {
|
|
149
|
+
const raw = execSync(`netstat -ano -p tcp | findstr :${port}`, { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
150
|
+
const line = raw.split(/\r?\n/).find((l) => l.includes('LISTENING'));
|
|
151
|
+
if (!line) return null;
|
|
152
|
+
const parts = line.trim().split(/\s+/);
|
|
153
|
+
const pid = parseInt(parts[parts.length - 1], 10);
|
|
154
|
+
if (!pid || Number.isNaN(pid)) return null;
|
|
155
|
+
return { pid, command: null };
|
|
156
|
+
}
|
|
157
|
+
const raw = execSync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -Fpc`, { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
158
|
+
const lines = raw.split(/\r?\n/).filter(Boolean);
|
|
159
|
+
let pid = null; let command = null;
|
|
160
|
+
for (const l of lines) {
|
|
161
|
+
if (l.startsWith('p')) pid = parseInt(l.slice(1), 10);
|
|
162
|
+
else if (l.startsWith('c')) command = l.slice(1);
|
|
163
|
+
}
|
|
164
|
+
if (!pid || Number.isNaN(pid)) return null;
|
|
165
|
+
return { pid, command };
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function httpCheck(port, timeoutMs = 2000) {
|
|
172
|
+
return new Promise((resolve) => {
|
|
173
|
+
const req = get(`http://127.0.0.1:${port}/`, { timeout: timeoutMs }, (res) => {
|
|
174
|
+
res.resume();
|
|
175
|
+
resolve(res.statusCode);
|
|
176
|
+
});
|
|
177
|
+
req.on('error', () => resolve(null));
|
|
178
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// --- Wait for port to become available ---
|
|
183
|
+
|
|
184
|
+
function waitForPort(port, maxMs = 3000) {
|
|
185
|
+
const start = Date.now();
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const check = async () => {
|
|
188
|
+
if (await checkPort(port)) return resolve(true);
|
|
189
|
+
if (Date.now() - start > maxMs) return resolve(false);
|
|
190
|
+
setTimeout(check, 200);
|
|
191
|
+
};
|
|
192
|
+
check();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Kill process ---
|
|
197
|
+
|
|
198
|
+
async function killProcess(pid) {
|
|
199
|
+
const isWindows = platform() === 'win32';
|
|
200
|
+
|
|
201
|
+
if (!isProcessAlive(pid)) return true;
|
|
202
|
+
|
|
203
|
+
// Graceful shutdown
|
|
204
|
+
if (isWindows) {
|
|
205
|
+
try { execSync(`taskkill /PID ${pid}`, { stdio: 'ignore' }); } catch { /* ignore */ }
|
|
206
|
+
} else {
|
|
207
|
+
try { process.kill(pid, 'SIGTERM'); } catch { /* ignore */ }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Wait up to 5s for graceful exit
|
|
211
|
+
const start = Date.now();
|
|
212
|
+
while (Date.now() - start < 5000) {
|
|
213
|
+
if (!isProcessAlive(pid)) return true;
|
|
214
|
+
await new Promise(r => setTimeout(r, 200));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Force kill
|
|
218
|
+
if (isProcessAlive(pid)) {
|
|
219
|
+
if (isWindows) {
|
|
220
|
+
try { execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' }); } catch { /* ignore */ }
|
|
221
|
+
} else {
|
|
222
|
+
try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ }
|
|
223
|
+
}
|
|
224
|
+
// Brief wait for force kill
|
|
225
|
+
await new Promise(r => setTimeout(r, 500));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return !isProcessAlive(pid);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// --- Open browser ---
|
|
232
|
+
|
|
233
|
+
function openBrowser(url) {
|
|
234
|
+
const plat = platform();
|
|
235
|
+
try {
|
|
236
|
+
if (plat === 'darwin') {
|
|
237
|
+
execSync(`open "${url}"`);
|
|
238
|
+
} else if (plat === 'win32') {
|
|
239
|
+
execSync(`start "" "${url}"`);
|
|
240
|
+
} else {
|
|
241
|
+
execSync(`xdg-open "${url}"`);
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
console.error('Could not open browser automatically. Visit: ' + url);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// --- Uptime formatting ---
|
|
249
|
+
|
|
250
|
+
function formatUptime(ms) {
|
|
251
|
+
const s = Math.floor(ms / 1000);
|
|
252
|
+
if (s < 60) return `${s}s`;
|
|
253
|
+
const m = Math.floor(s / 60);
|
|
254
|
+
if (m < 60) return `${m}m ${s % 60}s`;
|
|
255
|
+
const h = Math.floor(m / 60);
|
|
256
|
+
return `${h}h ${m % 60}m`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// --- Commands ---
|
|
260
|
+
|
|
261
|
+
async function cmdStart() {
|
|
262
|
+
const port = resolvePort();
|
|
263
|
+
const noOpen = hasFlag('--no-open');
|
|
264
|
+
|
|
265
|
+
// Supervisor-aware short-circuit. When launchd / systemd is managing the
|
|
266
|
+
// workspace, ws.mjs MUST NOT spawn a competing process — KeepAlive /
|
|
267
|
+
// Restart=always would otherwise hand the port back to the supervised
|
|
268
|
+
// instance and ours would lose the bind race. Friendly exit-0 if the
|
|
269
|
+
// supervised process is up; helpful message if it's not, telling the
|
|
270
|
+
// operator how to bounce it.
|
|
271
|
+
const supervisor = getSupervisor();
|
|
272
|
+
if (supervisor.kind) {
|
|
273
|
+
const sup = readSupervisorState(supervisor);
|
|
274
|
+
if (sup.state === 'running' && sup.pid) {
|
|
275
|
+
const url = `http://127.0.0.1:${port}`;
|
|
276
|
+
console.log(`Workspace server already running (supervised by ${supervisor.kind}, PID ${sup.pid}).`);
|
|
277
|
+
console.log(` ${url}`);
|
|
278
|
+
if (!noOpen) openBrowser(url);
|
|
279
|
+
process.exit(0);
|
|
280
|
+
}
|
|
281
|
+
// Supervisor configured but not currently running. Don't spawn a
|
|
282
|
+
// detached one — that would race the supervisor's next relaunch.
|
|
283
|
+
// Tell the operator how to bring it up via the supervisor.
|
|
284
|
+
const restartCmd = restartCommand(supervisor);
|
|
285
|
+
console.error(`Error: Workspace is ${supervisor.kind}-managed at ${supervisor.unitPath} but is not currently running.`);
|
|
286
|
+
if (restartCmd) console.error(` Bring it up with: ${restartCmd}`);
|
|
287
|
+
if (supervisor.kind === 'launchd') {
|
|
288
|
+
console.error(` Or reload the plist: launchctl load -w ${supervisor.unitPath}`);
|
|
289
|
+
} else if (supervisor.kind === 'systemd') {
|
|
290
|
+
console.error(` Or start the unit: systemctl --user start ${supervisor.label}`);
|
|
291
|
+
}
|
|
292
|
+
process.exit(2);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Check if already running
|
|
296
|
+
const existing = readPid();
|
|
297
|
+
if (existing && isProcessAlive(existing.pid)) {
|
|
298
|
+
const portUp = await checkPort(existing.port);
|
|
299
|
+
if (portUp) {
|
|
300
|
+
console.log(`Workspace server already running (PID ${existing.pid}, port ${existing.port}).`);
|
|
301
|
+
console.log(` http://127.0.0.1:${existing.port}`);
|
|
302
|
+
process.exit(0);
|
|
303
|
+
}
|
|
304
|
+
// Process alive but port not listening — stale state, clean up
|
|
305
|
+
await killProcess(existing.pid);
|
|
306
|
+
removePid();
|
|
307
|
+
} else if (existing) {
|
|
308
|
+
// PID file exists but process is dead. Before deleting the pidfile, check
|
|
309
|
+
// whether the port it claims is actually held by something unrelated — if
|
|
310
|
+
// so, surface a diagnostic and stop. We MUST NOT kill an unrelated holder.
|
|
311
|
+
if (existing.port && await checkPort(existing.port)) {
|
|
312
|
+
const holder = findPortHolder(existing.port);
|
|
313
|
+
const who = holder ? ` (held by PID ${holder.pid}${holder.command ? ` — ${holder.command}` : ''})` : '';
|
|
314
|
+
console.error(`Error: Stale PID file references dead PID ${existing.pid}, but port ${existing.port} is in use${who}.`);
|
|
315
|
+
console.error('Refusing to kill an unrelated process. Remove the PID file manually or use a different port.');
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
removePid();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check if port is already in use by something else
|
|
322
|
+
if (await checkPort(port)) {
|
|
323
|
+
console.error(`Error: Port ${port} is already in use by another process.`);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Build spawn args
|
|
328
|
+
const serverArgs = ['--no-timeout'];
|
|
329
|
+
serverArgs.push('--port', String(port));
|
|
330
|
+
serverArgs.push('--no-open'); // CLI handles browser open, not server
|
|
331
|
+
|
|
332
|
+
// Open log file for stdout/stderr
|
|
333
|
+
const { openSync, close } = await import('node:fs');
|
|
334
|
+
const logFd = openSync(LOG_FILE, 'w');
|
|
335
|
+
|
|
336
|
+
// Spawn detached
|
|
337
|
+
const child = spawn(process.execPath, [SERVER_SCRIPT, ...serverArgs], {
|
|
338
|
+
detached: true,
|
|
339
|
+
stdio: ['ignore', logFd, logFd],
|
|
340
|
+
cwd: REPO_ROOT,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
child.unref();
|
|
344
|
+
close(logFd);
|
|
345
|
+
|
|
346
|
+
const pid = child.pid;
|
|
347
|
+
writePid(pid, port);
|
|
348
|
+
|
|
349
|
+
// Wait for the port to come up
|
|
350
|
+
const ready = await waitForPort(port, 3000);
|
|
351
|
+
|
|
352
|
+
if (!ready) {
|
|
353
|
+
// Check if process died (e.g. port conflict detected by server itself)
|
|
354
|
+
if (!isProcessAlive(pid)) {
|
|
355
|
+
removePid();
|
|
356
|
+
// Try to read log for clues
|
|
357
|
+
try {
|
|
358
|
+
const log = readFileSync(LOG_FILE, 'utf-8').trim();
|
|
359
|
+
if (log) console.error('Server output:\n' + log);
|
|
360
|
+
} catch { /* ignore */ }
|
|
361
|
+
console.error('Error: Server failed to start. Check .ws.log for details.');
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
console.warn('Warning: Server started but port not yet responding. It may still be initialising.');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const url = `http://127.0.0.1:${port}`;
|
|
368
|
+
console.log(`Workspace server started (PID ${pid}).`);
|
|
369
|
+
console.log(` ${url}`);
|
|
370
|
+
console.log(` Log: ${LOG_FILE}`);
|
|
371
|
+
|
|
372
|
+
if (!noOpen) {
|
|
373
|
+
openBrowser(url);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function cmdStop() {
|
|
378
|
+
// Supervisor-aware refusal. Killing a supervised process triggers an
|
|
379
|
+
// instant relaunch via KeepAlive / Restart=always — `stop` is a no-op at
|
|
380
|
+
// best and a confusing port-conflict at worst. Refuse and point the
|
|
381
|
+
// operator at the right tool.
|
|
382
|
+
const supervisor = getSupervisor();
|
|
383
|
+
if (supervisor.kind) {
|
|
384
|
+
console.error(`Error: Workspace is ${supervisor.kind}-managed at ${supervisor.unitPath}.`);
|
|
385
|
+
if (supervisor.kind === 'launchd') {
|
|
386
|
+
console.error(` To stop it: launchctl unload -w ${supervisor.unitPath}`);
|
|
387
|
+
} else {
|
|
388
|
+
console.error(` To stop it: systemctl --user stop ${supervisor.label}`);
|
|
389
|
+
}
|
|
390
|
+
console.error(' To bounce it for in-place version pickup: ws.mjs restart');
|
|
391
|
+
process.exit(1);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const existing = readPid();
|
|
395
|
+
if (!existing) {
|
|
396
|
+
console.log('Workspace server is not running (no PID file).');
|
|
397
|
+
process.exit(0);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (!isProcessAlive(existing.pid)) {
|
|
401
|
+
console.log('Workspace server is not running (PID ' + existing.pid + ' is dead). Cleaning up.');
|
|
402
|
+
removePid();
|
|
403
|
+
process.exit(0);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log(`Stopping workspace server (PID ${existing.pid})...`);
|
|
407
|
+
const killed = await killProcess(existing.pid);
|
|
408
|
+
removePid();
|
|
409
|
+
|
|
410
|
+
if (killed) {
|
|
411
|
+
console.log('Server stopped.');
|
|
412
|
+
} else {
|
|
413
|
+
console.error('Warning: Could not confirm process termination. PID file cleaned up.');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function cmdRestart() {
|
|
418
|
+
// Supervisor-aware restart. Under launchd / systemd we ask the supervisor
|
|
419
|
+
// to bounce the service — `launchctl kickstart -k` and
|
|
420
|
+
// `systemctl --user restart` are both KeepAlive-safe and idempotent.
|
|
421
|
+
// Under no supervisor we fall back to the legacy stop-then-start dance.
|
|
422
|
+
const supervisor = getSupervisor();
|
|
423
|
+
if (supervisor.kind) {
|
|
424
|
+
const cmd = restartCommand(supervisor);
|
|
425
|
+
if (!cmd) {
|
|
426
|
+
console.error(`Error: Could not build restart command for ${supervisor.kind}.`);
|
|
427
|
+
process.exit(2);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
execSync(cmd, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
431
|
+
} catch (err) {
|
|
432
|
+
console.error(`Error: Supervisor restart failed: ${err.message || err}`);
|
|
433
|
+
process.exit(3);
|
|
434
|
+
}
|
|
435
|
+
// Re-read state so we can report the new PID.
|
|
436
|
+
const sup = readSupervisorState(supervisor);
|
|
437
|
+
if (sup.pid) {
|
|
438
|
+
console.log(`Workspace server restarted (supervised by ${supervisor.kind}, PID ${sup.pid}).`);
|
|
439
|
+
} else {
|
|
440
|
+
// launchctl kickstart -k can return before the new PID is published.
|
|
441
|
+
console.log(`Workspace server restart requested (supervised by ${supervisor.kind}).`);
|
|
442
|
+
}
|
|
443
|
+
const port = resolvePort();
|
|
444
|
+
console.log(` http://127.0.0.1:${port}`);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
await cmdStop();
|
|
448
|
+
await cmdStart();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function cmdStatus() {
|
|
452
|
+
const supervisor = getSupervisor();
|
|
453
|
+
|
|
454
|
+
// Supervisor-aware status. When a unit file is installed, the supervisor
|
|
455
|
+
// is the source of truth — the legacy pidfile is only written when ws.mjs
|
|
456
|
+
// itself spawned the server, so a supervised process is invisible to the
|
|
457
|
+
// pidfile-only check (bug 1 of 2 in this PR).
|
|
458
|
+
if (supervisor.kind) {
|
|
459
|
+
const port = resolvePort();
|
|
460
|
+
const sup = readSupervisorState(supervisor);
|
|
461
|
+
const listener = findPortListener(port);
|
|
462
|
+
const portUp = await checkPort(port);
|
|
463
|
+
const httpStatus = portUp ? await httpCheck(port) : null;
|
|
464
|
+
|
|
465
|
+
const running = sup.state === 'running' || portUp;
|
|
466
|
+
console.log(`Status: ${running ? 'running' : (sup.state === 'loaded' ? 'loaded (not running)' : 'stopped')} (supervised by ${supervisor.kind})`);
|
|
467
|
+
console.log(` Supervisor: ${supervisor.kind} — ${supervisor.unitPath}`);
|
|
468
|
+
console.log(` State: ${sup.state}`);
|
|
469
|
+
if (sup.pid) console.log(` PID: ${sup.pid}`);
|
|
470
|
+
console.log(` Port: ${port}`);
|
|
471
|
+
if (listener) {
|
|
472
|
+
const cmdName = listener.command ? ` (${listener.command})` : '';
|
|
473
|
+
console.log(` Listener: PID ${listener.pid}${cmdName}`);
|
|
474
|
+
if (sup.pid && listener.pid !== sup.pid) {
|
|
475
|
+
console.log(' Warning: port listener PID differs from supervised PID — another process is on this port.');
|
|
476
|
+
}
|
|
477
|
+
} else if (portUp) {
|
|
478
|
+
console.log(' Listener: port held (lsof/ss unavailable to identify PID)');
|
|
479
|
+
} else {
|
|
480
|
+
console.log(' Listener: none');
|
|
481
|
+
}
|
|
482
|
+
console.log(` URL: http://127.0.0.1:${port}`);
|
|
483
|
+
if (portUp && httpStatus !== null) {
|
|
484
|
+
console.log(` Health: OK (HTTP ${httpStatus})`);
|
|
485
|
+
} else if (portUp) {
|
|
486
|
+
console.log(' Health: port open, HTTP check failed');
|
|
487
|
+
} else {
|
|
488
|
+
console.log(' Health: port not responding');
|
|
489
|
+
}
|
|
490
|
+
process.exit(running ? 0 : 2);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Legacy pidfile-managed status (no supervisor installed).
|
|
494
|
+
const existing = readPid();
|
|
495
|
+
|
|
496
|
+
if (!existing) {
|
|
497
|
+
console.log('Status: stopped');
|
|
498
|
+
console.log(' No PID file found.');
|
|
499
|
+
process.exit(0);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const alive = isProcessAlive(existing.pid);
|
|
503
|
+
if (!alive) {
|
|
504
|
+
console.log('Status: stopped (stale PID file)');
|
|
505
|
+
console.log(` PID ${existing.pid} is no longer running.`);
|
|
506
|
+
removePid();
|
|
507
|
+
process.exit(0);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Uptime from PID file mtime
|
|
511
|
+
let uptime = '';
|
|
512
|
+
try {
|
|
513
|
+
const pidStat = statSync(PID_FILE);
|
|
514
|
+
const ms = Date.now() - pidStat.mtimeMs;
|
|
515
|
+
uptime = formatUptime(ms);
|
|
516
|
+
} catch { /* ignore */ }
|
|
517
|
+
|
|
518
|
+
const portUp = await checkPort(existing.port);
|
|
519
|
+
let httpStatus = null;
|
|
520
|
+
if (portUp) {
|
|
521
|
+
httpStatus = await httpCheck(existing.port);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
console.log('Status: running');
|
|
525
|
+
console.log(` PID: ${existing.pid}`);
|
|
526
|
+
console.log(` Port: ${existing.port}`);
|
|
527
|
+
if (uptime) console.log(` Uptime: ${uptime}`);
|
|
528
|
+
console.log(` URL: http://127.0.0.1:${existing.port}`);
|
|
529
|
+
if (portUp && httpStatus !== null) {
|
|
530
|
+
console.log(` Health: OK (HTTP ${httpStatus})`);
|
|
531
|
+
} else if (portUp) {
|
|
532
|
+
console.log(' Health: port open, HTTP check failed');
|
|
533
|
+
} else {
|
|
534
|
+
console.log(' Health: port not responding');
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function cmdOpen() {
|
|
539
|
+
// Supervisor-aware open. Under launchd/systemd there is no pidfile, so
|
|
540
|
+
// we resolve the port via the standard preference chain and probe the
|
|
541
|
+
// socket directly.
|
|
542
|
+
const supervisor = getSupervisor();
|
|
543
|
+
if (supervisor.kind) {
|
|
544
|
+
const port = resolvePort();
|
|
545
|
+
const portUp = await checkPort(port);
|
|
546
|
+
if (!portUp) {
|
|
547
|
+
console.error(`Error: Workspace is ${supervisor.kind}-managed but port ${port} is not responding.`);
|
|
548
|
+
const cmd = restartCommand(supervisor);
|
|
549
|
+
if (cmd) console.error(` Try: ${cmd}`);
|
|
550
|
+
process.exit(2);
|
|
551
|
+
}
|
|
552
|
+
const url = `http://127.0.0.1:${port}`;
|
|
553
|
+
console.log('Opening ' + url);
|
|
554
|
+
openBrowser(url);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const existing = readPid();
|
|
559
|
+
|
|
560
|
+
if (!existing || !isProcessAlive(existing.pid)) {
|
|
561
|
+
console.error('Error: Workspace server is not running. Start it first: node ws.mjs start');
|
|
562
|
+
process.exit(1);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const portUp = await checkPort(existing.port);
|
|
566
|
+
if (!portUp) {
|
|
567
|
+
console.error('Error: Server process exists but port ' + existing.port + ' is not responding.');
|
|
568
|
+
process.exit(1);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const url = `http://127.0.0.1:${existing.port}`;
|
|
572
|
+
console.log('Opening ' + url);
|
|
573
|
+
openBrowser(url);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// --- Exports for tests ---
|
|
577
|
+
// Helpers are exported so test.mjs can exercise them without shelling out.
|
|
578
|
+
// Importing this module must not trigger CLI dispatch — the entrypoint guard
|
|
579
|
+
// below ensures the switch only runs when ws.mjs is invoked directly.
|
|
580
|
+
export { isProcessAlive, checkPort, findPortHolder };
|
|
581
|
+
|
|
582
|
+
// --- Main ---
|
|
583
|
+
|
|
584
|
+
const USAGE = `Usage: node ws.mjs <command> [options]
|
|
585
|
+
|
|
586
|
+
Commands:
|
|
587
|
+
start [--port N] [--no-open] Start workspace server
|
|
588
|
+
stop Stop workspace server
|
|
589
|
+
restart [--port N] [--no-open] Restart workspace server
|
|
590
|
+
status Show server status
|
|
591
|
+
open Open browser to server URL`;
|
|
592
|
+
|
|
593
|
+
// Only dispatch the CLI when this file is the entrypoint. When imported as a
|
|
594
|
+
// module (e.g. by tests that pull in exported helpers), skip the switch so
|
|
595
|
+
// the importing process is not terminated by the default-case process.exit.
|
|
596
|
+
//
|
|
597
|
+
// ISMAIN-AUDIT (bug-3 sweep): the previous check compared import.meta.url
|
|
598
|
+
// (always realpath-resolved by Node) against `pathToFileURL(argv[1])`
|
|
599
|
+
// with NO symlink resolution on the argv[1] side — same class of
|
|
600
|
+
// fragility as the endsWith idiom fixed in the scheduler. It also threw
|
|
601
|
+
// outright if argv[1] was undefined. `pa workspace <verb>`'s fallback
|
|
602
|
+
// path spawns this file via an explicit absolute path (not a pnpm .bin
|
|
603
|
+
// symlink), so this has not been proven to misfire in production, but is
|
|
604
|
+
// fixed defensively to the same realpath-both-sides pattern used
|
|
605
|
+
// elsewhere (and already correct in server.mjs / install-daemon.mjs in
|
|
606
|
+
// this same package).
|
|
607
|
+
function isDirectlyInvoked() {
|
|
608
|
+
if (!process.argv[1]) return false;
|
|
609
|
+
try {
|
|
610
|
+
return fileURLToPath(import.meta.url) === realpathSync(resolve(process.argv[1]));
|
|
611
|
+
} catch {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (isDirectlyInvoked()) {
|
|
617
|
+
switch (command) {
|
|
618
|
+
case 'start': await cmdStart(); break;
|
|
619
|
+
case 'stop': await cmdStop(); break;
|
|
620
|
+
case 'restart': await cmdRestart(); break;
|
|
621
|
+
case 'status': await cmdStatus(); break;
|
|
622
|
+
case 'open': await cmdOpen(); break;
|
|
623
|
+
case '--help':
|
|
624
|
+
case '-h':
|
|
625
|
+
case 'help':
|
|
626
|
+
console.log(USAGE);
|
|
627
|
+
process.exit(0);
|
|
628
|
+
default:
|
|
629
|
+
if (command) console.error(`Unknown command: ${command}\n`);
|
|
630
|
+
console.log(USAGE);
|
|
631
|
+
process.exit(command ? 1 : 0);
|
|
632
|
+
}
|
|
633
|
+
}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// @stravica-ai/digital-operator-pa — public package surface.
|
|
2
|
+
//
|
|
3
|
+
// Single-bundled wrapper for the engineering-pa platform (approach A2 — see
|
|
4
|
+
// ./README.md and build.mjs). The wrapper ships ONE tarball that contains
|
|
5
|
+
// the wrapper code plus a `dist/` tree of the internal workspace packages
|
|
6
|
+
// with `@stravica-ai/engineering-pa-*` imports rewritten to relative paths.
|
|
7
|
+
//
|
|
8
|
+
// Operators install one package, pin one version. Internals stay
|
|
9
|
+
// `private: true` in the monorepo and are not individually publishable.
|
|
10
|
+
|
|
11
|
+
export const name = '@stravica-ai/digital-operator-pa';
|
|
12
|
+
export const version = '0.1.1';
|
|
13
|
+
|
|
14
|
+
// Bin entry points exposed by the wrapper (mirrors package.json#bin).
|
|
15
|
+
// Programmatic consumers normally spawn the bins; this list is for
|
|
16
|
+
// introspection / scaffolders.
|
|
17
|
+
export const bins = [
|
|
18
|
+
'pa',
|
|
19
|
+
'desk',
|
|
20
|
+
'launch',
|
|
21
|
+
'sync',
|
|
22
|
+
'secrets',
|
|
23
|
+
'scheduler',
|
|
24
|
+
'smoke',
|
|
25
|
+
'distill',
|
|
26
|
+
'memory-sync',
|
|
27
|
+
'migrate-inspect',
|
|
28
|
+
'pa-init',
|
|
29
|
+
'slice',
|
|
30
|
+
'research',
|
|
31
|
+
'pa-coordinator-hook',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Bundled internal packages (mirrors build.mjs PACKAGES catalog).
|
|
35
|
+
// Useful for debugging install layouts.
|
|
36
|
+
export const bundled = [
|
|
37
|
+
'@stravica-ai/engineering-pa-core',
|
|
38
|
+
'@stravica-ai/engineering-pa-runbook',
|
|
39
|
+
'@stravica-ai/engineering-pa-cli',
|
|
40
|
+
'@stravica-ai/engineering-pa-migrate-inspect',
|
|
41
|
+
'@stravica-ai/engineering-pa-workspace',
|
|
42
|
+
'@stravica-ai/engineering-pa-scaffold',
|
|
43
|
+
'@stravica-ai/engineering-pa-librarian',
|
|
44
|
+
'@stravica-ai/engineering-pa-types-engineering',
|
|
45
|
+
'@stravica-ai/engineering-pa-core-extensions-atlassian',
|
|
46
|
+
'@stravica-ai/engineering-pa-core-extensions-n8n',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns a metadata snapshot describing this installed wrapper. Useful for
|
|
51
|
+
* `pa status` and operator-side install verification.
|
|
52
|
+
*/
|
|
53
|
+
export function describe() {
|
|
54
|
+
return {
|
|
55
|
+
name,
|
|
56
|
+
version,
|
|
57
|
+
bins,
|
|
58
|
+
bundled,
|
|
59
|
+
};
|
|
60
|
+
}
|