@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,237 @@
|
|
|
1
|
+
// inspector.mjs — orchestrates platform modules to produce inspection.json.
|
|
2
|
+
// READ-ONLY: never writes anything (cli.mjs handles --out file writes).
|
|
3
|
+
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Run a full host inspection.
|
|
11
|
+
* @param {object} opts
|
|
12
|
+
* @param {string|null} [opts.daveRoot]
|
|
13
|
+
* @param {object} runner - injected runner (spawnCapture, readPlist)
|
|
14
|
+
* @returns {Promise<object>} inspection result
|
|
15
|
+
*/
|
|
16
|
+
export async function inspect(opts = {}, runner) {
|
|
17
|
+
const [host, runtimes, platformData, daveEnvData] = await Promise.all([
|
|
18
|
+
_collectHost(),
|
|
19
|
+
_collectRuntimes(runner),
|
|
20
|
+
_collectPlatformData(runner),
|
|
21
|
+
Promise.resolve(_collectDaveEnv(opts)),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const daveRoot = daveEnvData.daveRoot;
|
|
25
|
+
const [paths, extensions, ageKey] = await Promise.all([
|
|
26
|
+
_collectPaths(daveRoot),
|
|
27
|
+
_collectExtensions(daveRoot),
|
|
28
|
+
_collectAgeKey(opts),
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
host,
|
|
33
|
+
runtimes,
|
|
34
|
+
scheduled: platformData.scheduled,
|
|
35
|
+
mounts: platformData.mounts,
|
|
36
|
+
daveEnv: daveEnvData,
|
|
37
|
+
paths,
|
|
38
|
+
extensions,
|
|
39
|
+
ageKey,
|
|
40
|
+
inspectedAt: new Date().toISOString(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Collect age private key locations. Reports v1 (`~/.config/dave/age-key.txt`)
|
|
46
|
+
* and v2 (`~/.config/stravica-pa/age-key.txt`) so callers can plan or perform
|
|
47
|
+
* a migration. Pure file existence checks — never reads key contents.
|
|
48
|
+
*
|
|
49
|
+
* @param {object} opts — may include { home } for tests.
|
|
50
|
+
* @returns {Promise<{v1Path: string, v1Exists: boolean, v2Path: string,
|
|
51
|
+
* v2Exists: boolean, recommendation: string}>}
|
|
52
|
+
*/
|
|
53
|
+
export async function _collectAgeKey(opts = {}) {
|
|
54
|
+
const home = opts.home ?? homedir();
|
|
55
|
+
const v1Path = join(home, '.config', 'dave', 'age-key.txt');
|
|
56
|
+
const v2Path = join(home, '.config', 'stravica-pa', 'age-key.txt');
|
|
57
|
+
const [v1Exists, v2Exists] = await Promise.all([_exists(v1Path), _exists(v2Path)]);
|
|
58
|
+
|
|
59
|
+
// Recommendation matrix:
|
|
60
|
+
// v1 only -> migrate (copy v1 -> v2, keep v1 as one-session safety net)
|
|
61
|
+
// v2 only -> ok (canonical location populated)
|
|
62
|
+
// both -> v2-already-populated (no action needed; v1 can be removed later)
|
|
63
|
+
// neither -> none-present (host genuinely has no key — e.g. smarthome01)
|
|
64
|
+
let recommendation;
|
|
65
|
+
if (v1Exists && !v2Exists) recommendation = 'migrate';
|
|
66
|
+
else if (!v1Exists && v2Exists) recommendation = 'ok';
|
|
67
|
+
else if (v1Exists && v2Exists) recommendation = 'v2-already-populated';
|
|
68
|
+
else recommendation = 'none-present';
|
|
69
|
+
|
|
70
|
+
return { v1Path, v1Exists, v2Path, v2Exists, recommendation };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _collectHost() {
|
|
74
|
+
return {
|
|
75
|
+
hostname: os.hostname(),
|
|
76
|
+
os: process.platform,
|
|
77
|
+
osVersion: os.release(),
|
|
78
|
+
arch: process.arch,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function _collectRuntimes(runner) {
|
|
83
|
+
const [nodeRes, pnpmRes, gitRes] = await Promise.all([
|
|
84
|
+
runner.spawnCapture('node', ['--version']),
|
|
85
|
+
runner.spawnCapture('pnpm', ['--version']),
|
|
86
|
+
runner.spawnCapture('git', ['--version']),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
function parseVersion(res, pattern) {
|
|
90
|
+
if (res.exitCode !== 0) return null;
|
|
91
|
+
const m = res.stdout.trim().match(pattern);
|
|
92
|
+
return m ? m[1] : (res.stdout.trim() || null);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
nodeVersion: parseVersion(nodeRes, /v?(\d+\.\d+\.\d+)/),
|
|
97
|
+
pnpmVersion: parseVersion(pnpmRes, /(\d+\.\d+\.\d+)/),
|
|
98
|
+
gitVersion: parseVersion(gitRes, /git version (.+)/),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function _collectPlatformData(runner) {
|
|
103
|
+
const platform = process.platform;
|
|
104
|
+
if (platform === 'darwin') {
|
|
105
|
+
const { collectScheduled, collectMounts } = await import('./platforms/macos.mjs');
|
|
106
|
+
const [scheduled, mounts] = await Promise.all([
|
|
107
|
+
collectScheduled(runner),
|
|
108
|
+
collectMounts(runner),
|
|
109
|
+
]);
|
|
110
|
+
return { scheduled, mounts };
|
|
111
|
+
} else if (platform === 'linux') {
|
|
112
|
+
const { collectScheduled, collectMounts } = await import('./platforms/linux.mjs');
|
|
113
|
+
const [scheduled, mounts] = await Promise.all([
|
|
114
|
+
collectScheduled(runner),
|
|
115
|
+
collectMounts(runner),
|
|
116
|
+
]);
|
|
117
|
+
return { scheduled, mounts };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
scheduled: { crontab: [], launchdPlists: [] },
|
|
121
|
+
mounts: [],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function _collectDaveEnv(opts) {
|
|
126
|
+
const daveRoot =
|
|
127
|
+
opts.daveRoot ||
|
|
128
|
+
process.env.DAVE_ROOT ||
|
|
129
|
+
process.env.PA_REPO_ROOT ||
|
|
130
|
+
null;
|
|
131
|
+
|
|
132
|
+
const daveRelevantEnvVars = {};
|
|
133
|
+
for (const [key, val] of Object.entries(process.env)) {
|
|
134
|
+
if (key.startsWith('DAVE_') || key === 'PA_REPO_ROOT') {
|
|
135
|
+
daveRelevantEnvVars[key] = val;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const pathSegments = [];
|
|
140
|
+
if (daveRoot && process.env.PATH) {
|
|
141
|
+
const resolved = resolve(daveRoot);
|
|
142
|
+
for (const seg of process.env.PATH.split(':')) {
|
|
143
|
+
if (seg.startsWith(resolved)) {
|
|
144
|
+
pathSegments.push(seg);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { daveRoot, daveRelevantEnvVars, pathSegments };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function _collectPaths(daveRoot) {
|
|
153
|
+
if (!daveRoot) {
|
|
154
|
+
return {
|
|
155
|
+
daveRoot: null,
|
|
156
|
+
coreToolsPath: null,
|
|
157
|
+
operatorPath: null,
|
|
158
|
+
layoutVersion: 'unknown',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const coreToolsPath = join(daveRoot, 'core', 'tools');
|
|
163
|
+
const operatorPath = join(daveRoot, 'operator');
|
|
164
|
+
const pkgJsonPath = join(daveRoot, 'package.json');
|
|
165
|
+
|
|
166
|
+
const [hasCoreTools, hasOperator, hasPkgJson] = await Promise.all([
|
|
167
|
+
_exists(coreToolsPath),
|
|
168
|
+
_exists(operatorPath),
|
|
169
|
+
_exists(pkgJsonPath),
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
let layoutVersion = 'unknown';
|
|
173
|
+
if (hasCoreTools) {
|
|
174
|
+
layoutVersion = 'v1';
|
|
175
|
+
} else if (hasPkgJson) {
|
|
176
|
+
layoutVersion = 'v2';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
daveRoot,
|
|
181
|
+
coreToolsPath: hasCoreTools ? coreToolsPath : null,
|
|
182
|
+
operatorPath: hasOperator ? operatorPath : null,
|
|
183
|
+
layoutVersion,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function _collectExtensions(daveRoot) {
|
|
188
|
+
if (!daveRoot) return [];
|
|
189
|
+
|
|
190
|
+
const extensionsToolsDir = join(daveRoot, 'operator', 'extensions', 'tools');
|
|
191
|
+
let entries;
|
|
192
|
+
try {
|
|
193
|
+
entries = await readdir(extensionsToolsDir);
|
|
194
|
+
} catch {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Manifest may exist at operator/extensions/manifest.json. Accept array of
|
|
199
|
+
// { name } / { id } / plain strings, or object keyed by extension name.
|
|
200
|
+
let manifestNames = null;
|
|
201
|
+
try {
|
|
202
|
+
const manifestPath = join(daveRoot, 'operator', 'extensions', 'manifest.json');
|
|
203
|
+
const raw = await readFile(manifestPath, 'utf8');
|
|
204
|
+
const parsed = JSON.parse(raw);
|
|
205
|
+
if (Array.isArray(parsed)) {
|
|
206
|
+
manifestNames = new Set(parsed.map(e => (typeof e === 'string' ? e : e.name || e.id)).filter(Boolean));
|
|
207
|
+
} else if (parsed && typeof parsed === 'object') {
|
|
208
|
+
manifestNames = new Set(Object.keys(parsed));
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// no manifest — leave hasManifestEntry as false for all entries
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const extensions = [];
|
|
215
|
+
for (const name of entries) {
|
|
216
|
+
const path = join(extensionsToolsDir, name);
|
|
217
|
+
let isDir = false;
|
|
218
|
+
try {
|
|
219
|
+
isDir = (await stat(path)).isDirectory();
|
|
220
|
+
} catch {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (!isDir) continue;
|
|
224
|
+
const hasManifestEntry = manifestNames ? manifestNames.has(name) : false;
|
|
225
|
+
extensions.push({ name, path, hasManifestEntry });
|
|
226
|
+
}
|
|
227
|
+
return extensions;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function _exists(p) {
|
|
231
|
+
try {
|
|
232
|
+
await stat(p);
|
|
233
|
+
return true;
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// linux.mjs — pure Linux-specific collectors.
|
|
2
|
+
// All spawning is done via the injected runner for testability.
|
|
3
|
+
|
|
4
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Collect scheduled tasks: crontab entries + (empty) launchdPlists.
|
|
9
|
+
* @param {import('../runner.mjs')} runner
|
|
10
|
+
* @returns {Promise<{crontab: Array<{schedule: string, command: string}>, launchdPlists: []}>}
|
|
11
|
+
*/
|
|
12
|
+
export async function collectScheduled(runner) {
|
|
13
|
+
const crontab = await _collectCrontab(runner);
|
|
14
|
+
const cronD = await _collectCronD();
|
|
15
|
+
return { crontab: [...crontab, ...cronD], launchdPlists: [] };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function _collectCrontab(runner) {
|
|
19
|
+
const { stdout, exitCode } = await runner.spawnCapture('crontab', ['-l']);
|
|
20
|
+
if (exitCode !== 0) return [];
|
|
21
|
+
return _parseCrontab(stdout);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function _collectCronD() {
|
|
25
|
+
const cronDDir = '/etc/cron.d';
|
|
26
|
+
let files;
|
|
27
|
+
try {
|
|
28
|
+
files = await readdir(cronDDir);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') return [];
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const entries = [];
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
try {
|
|
36
|
+
const content = await readFile(join(cronDDir, file), 'utf8');
|
|
37
|
+
const parsed = _parseCrontab(content);
|
|
38
|
+
entries.push(...parsed);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') continue;
|
|
41
|
+
// skip other errors silently
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return entries;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _parseCrontab(text) {
|
|
48
|
+
const entries = [];
|
|
49
|
+
for (const line of text.split('\n')) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
52
|
+
// skip env var lines (e.g. MAILTO=...)
|
|
53
|
+
if (trimmed.match(/^\w+=\S*/)) continue;
|
|
54
|
+
const parts = trimmed.split(/\s+/);
|
|
55
|
+
if (parts.length < 6) continue;
|
|
56
|
+
const schedule = parts.slice(0, 5).join(' ');
|
|
57
|
+
const command = parts.slice(5).join(' ');
|
|
58
|
+
entries.push({ schedule, command });
|
|
59
|
+
}
|
|
60
|
+
return entries;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collect network/remote mounts from mount output.
|
|
65
|
+
* @param {import('../runner.mjs')} runner
|
|
66
|
+
* @returns {Promise<Array<{source: string, target: string, fsType: string}>>}
|
|
67
|
+
*/
|
|
68
|
+
export async function collectMounts(runner) {
|
|
69
|
+
const { stdout } = await runner.spawnCapture('mount', []);
|
|
70
|
+
const REMOTE_FS_TYPES = new Set(['nfs', 'cifs', 'smbfs', 'fuse', 'nfs4', 'ceph', 'glusterfs']);
|
|
71
|
+
const mounts = [];
|
|
72
|
+
for (const line of stdout.split('\n')) {
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
if (!trimmed) continue;
|
|
75
|
+
// Linux mount format: source on target type fstype (options)
|
|
76
|
+
const m = trimmed.match(/^(\S+)\s+on\s+(\S+)\s+type\s+(\S+)/);
|
|
77
|
+
if (!m) continue;
|
|
78
|
+
const [, source, target, fsType] = m;
|
|
79
|
+
const isRemote =
|
|
80
|
+
REMOTE_FS_TYPES.has(fsType) ||
|
|
81
|
+
source.includes('//') ||
|
|
82
|
+
source.match(/\S+:\//);
|
|
83
|
+
if (!isRemote) continue;
|
|
84
|
+
mounts.push({ source, target, fsType });
|
|
85
|
+
}
|
|
86
|
+
return mounts;
|
|
87
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// macos.mjs — pure macOS-specific collectors.
|
|
2
|
+
// All spawning is done via the injected runner for testability.
|
|
3
|
+
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { readdir } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Collect scheduled tasks: crontab entries + launchd plists.
|
|
10
|
+
* @param {import('../runner.mjs')} runner
|
|
11
|
+
* @returns {Promise<{crontab: Array<{schedule: string, command: string}>, launchdPlists: Array<object>}>}
|
|
12
|
+
*/
|
|
13
|
+
export async function collectScheduled(runner) {
|
|
14
|
+
const crontab = await _collectCrontab(runner);
|
|
15
|
+
const launchdPlists = await _collectLaunchdPlists(runner);
|
|
16
|
+
return { crontab, launchdPlists };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function _collectCrontab(runner) {
|
|
20
|
+
const { stdout, exitCode } = await runner.spawnCapture('crontab', ['-l']);
|
|
21
|
+
if (exitCode !== 0) return [];
|
|
22
|
+
return _parseCrontab(stdout);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function _parseCrontab(text) {
|
|
26
|
+
const entries = [];
|
|
27
|
+
for (const line of text.split('\n')) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
30
|
+
// first 5 fields = schedule, rest = command
|
|
31
|
+
const parts = trimmed.split(/\s+/);
|
|
32
|
+
if (parts.length < 6) continue;
|
|
33
|
+
const schedule = parts.slice(0, 5).join(' ');
|
|
34
|
+
const command = parts.slice(5).join(' ');
|
|
35
|
+
entries.push({ schedule, command });
|
|
36
|
+
}
|
|
37
|
+
return entries;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function _collectLaunchdPlists(runner) {
|
|
41
|
+
const launchAgentsDir = join(os.homedir(), 'Library', 'LaunchAgents');
|
|
42
|
+
let files;
|
|
43
|
+
try {
|
|
44
|
+
files = await readdir(launchAgentsDir);
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const plists = [];
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
if (!file.endsWith('.plist')) continue;
|
|
51
|
+
const filePath = join(launchAgentsDir, file);
|
|
52
|
+
try {
|
|
53
|
+
const parsed = await runner.readPlist(filePath);
|
|
54
|
+
plists.push(parsed);
|
|
55
|
+
} catch {
|
|
56
|
+
// skip unreadable plists
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return plists;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Collect network/remote mounts from mount output.
|
|
64
|
+
* @param {import('../runner.mjs')} runner
|
|
65
|
+
* @returns {Promise<Array<{source: string, target: string, fsType: string}>>}
|
|
66
|
+
*/
|
|
67
|
+
export async function collectMounts(runner) {
|
|
68
|
+
const { stdout } = await runner.spawnCapture('mount', []);
|
|
69
|
+
const mounts = [];
|
|
70
|
+
for (const line of stdout.split('\n')) {
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
if (!trimmed) continue;
|
|
73
|
+
// macOS mount format: source on target (type, options)
|
|
74
|
+
// or: //user@host/share on /mount/point (type smbfs, ...)
|
|
75
|
+
const typeMatch = trimmed.match(/\(type\s+(\S+)/);
|
|
76
|
+
const fsType = typeMatch ? typeMatch[1].replace(',', '') : '';
|
|
77
|
+
const isRemote =
|
|
78
|
+
fsType === 'nfs' ||
|
|
79
|
+
fsType === 'smbfs' ||
|
|
80
|
+
fsType === 'afpfs' ||
|
|
81
|
+
trimmed.includes('//') ||
|
|
82
|
+
trimmed.match(/\S+:\//);
|
|
83
|
+
if (!isRemote) continue;
|
|
84
|
+
// parse: source on target (...)
|
|
85
|
+
const onMatch = trimmed.match(/^(.+?)\s+on\s+(.+?)\s+\(/);
|
|
86
|
+
if (!onMatch) continue;
|
|
87
|
+
mounts.push({
|
|
88
|
+
source: onMatch[1].trim(),
|
|
89
|
+
target: onMatch[2].trim(),
|
|
90
|
+
fsType: fsType || 'unknown',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return mounts;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Detect chip architecture and total memory.
|
|
98
|
+
* @returns {{ chip: string, memory: number }}
|
|
99
|
+
*/
|
|
100
|
+
export function detectChipAndMemory() {
|
|
101
|
+
return {
|
|
102
|
+
chip: process.arch,
|
|
103
|
+
memory: os.totalmem(),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// runner.mjs — CHOKEPOINT: all subprocess spawning happens here.
|
|
2
|
+
// Zero external deps; Node stdlib only.
|
|
3
|
+
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Spawns a process and captures stdout + stderr.
|
|
9
|
+
* Returns { stdout, stderr, exitCode }.
|
|
10
|
+
* Never throws unless spawn itself fails (in which case exitCode=3).
|
|
11
|
+
*
|
|
12
|
+
* @param {string} cmd
|
|
13
|
+
* @param {string[]} args
|
|
14
|
+
* @returns {Promise<{stdout: string, stderr: string, exitCode: number}>}
|
|
15
|
+
*/
|
|
16
|
+
export async function spawnCapture(cmd, args) {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
let stdout = '';
|
|
19
|
+
let stderr = '';
|
|
20
|
+
let child;
|
|
21
|
+
try {
|
|
22
|
+
child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
23
|
+
} catch (err) {
|
|
24
|
+
resolve({ stdout: '', stderr: err.message, exitCode: 3 });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
29
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
30
|
+
|
|
31
|
+
child.on('error', (err) => {
|
|
32
|
+
resolve({ stdout, stderr: stderr + err.message, exitCode: 3 });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
child.on('close', (code) => {
|
|
36
|
+
resolve({ stdout, stderr, exitCode: code ?? 0 });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reads a macOS plist XML file and parses the relevant fields.
|
|
43
|
+
* Handles only the specific keys we need: Label, Program, ProgramArguments,
|
|
44
|
+
* WorkingDirectory, RunAtLoad, KeepAlive.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} filePath
|
|
47
|
+
* @returns {Promise<{label: string|null, program: string|null, args: string[]|null, workingDirectory: string|null, runAtLoad: boolean, keepAlive: boolean}>}
|
|
48
|
+
*/
|
|
49
|
+
export async function readPlist(filePath) {
|
|
50
|
+
const xml = await readFile(filePath, 'utf8');
|
|
51
|
+
|
|
52
|
+
function extractStringAfterKey(key) {
|
|
53
|
+
// Matches <key>KeyName</key>\s*<string>value</string>
|
|
54
|
+
const re = new RegExp(`<key>${key}</key>\\s*<string>([^<]*)</string>`, 's');
|
|
55
|
+
const m = xml.match(re);
|
|
56
|
+
return m ? m[1].trim() : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractBoolAfterKey(key) {
|
|
60
|
+
const re = new RegExp(`<key>${key}</key>\\s*<(true|false)\\s*/>`, 's');
|
|
61
|
+
const m = xml.match(re);
|
|
62
|
+
if (!m) return false;
|
|
63
|
+
return m[1] === 'true';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function extractArrayAfterKey(key) {
|
|
67
|
+
const re = new RegExp(`<key>${key}</key>\\s*<array>(.*?)</array>`, 's');
|
|
68
|
+
const m = xml.match(re);
|
|
69
|
+
if (!m) return null;
|
|
70
|
+
const strings = [];
|
|
71
|
+
const strRe = /<string>([^<]*)<\/string>/g;
|
|
72
|
+
let sm;
|
|
73
|
+
while ((sm = strRe.exec(m[1])) !== null) {
|
|
74
|
+
strings.push(sm[1].trim());
|
|
75
|
+
}
|
|
76
|
+
return strings;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
label: extractStringAfterKey('Label'),
|
|
81
|
+
program: extractStringAfterKey('Program'),
|
|
82
|
+
args: extractArrayAfterKey('ProgramArguments'),
|
|
83
|
+
workingDirectory: extractStringAfterKey('WorkingDirectory'),
|
|
84
|
+
runAtLoad: extractBoolAfterKey('RunAtLoad'),
|
|
85
|
+
keepAlive: extractBoolAfterKey('KeepAlive'),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @stravica-ai/engineering-pa-runbook
|
|
2
|
+
|
|
3
|
+
Migration runbook JSON Schema, reference runbooks, and structural validator.
|
|
4
|
+
|
|
5
|
+
## What the runbook schema is for
|
|
6
|
+
|
|
7
|
+
A migration runbook is a structured, machine-readable document that describes the sequence of steps needed to migrate a Dave PA instance from v1 (fork layout) to v2 (operator-repo layout). Each step has a declared `intent`, optional `params`, optional `preconditions`, expected outcomes (`expect`), and a `rollback` spec.
|
|
8
|
+
|
|
9
|
+
Runbooks are designed to be executed by an autonomous agent that reads `inspection.json` (output of `pa migrate-inspect`), substitutes template variables, and dispatches each step to host-specific actuators.
|
|
10
|
+
|
|
11
|
+
## Intent vocabulary
|
|
12
|
+
|
|
13
|
+
| Intent | Description |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `snapshot-v1-fork` | Preserve the v1 fork as a rollback point (git tag or directory copy) before migration begins |
|
|
16
|
+
| `run-migrate-to-v2` | Invoke the `migrate-to-v2` CLI to produce the v2 operator-repo layout from the v1 fork |
|
|
17
|
+
| `rewrite-crontab` | Rewrite one or more crontab entries to use the new v2 instance working directory |
|
|
18
|
+
| `drop-cron-entry` | Remove a specific named cron entry (e.g. `upstream-triage`) that is not carried forward to v2 |
|
|
19
|
+
| `update-host-service` | Update a service/daemon entry — launchd plist on macOS, systemd unit on Linux |
|
|
20
|
+
| `verify-sync-continuity` | Verify sync jobs run and reach their destinations (e.g. NFS/SMB mount reachable, sync exits 0) |
|
|
21
|
+
| `verify-boot-health` | Run `pa status` in the v2 instance and assert exit 0 |
|
|
22
|
+
| `define-roster-entry` | Add or update a host entry in the operator roster, recording layout version and daveRoot |
|
|
23
|
+
|
|
24
|
+
## The inspect → runbook → execute flow
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
1. pa migrate-inspect --out inspection-<host>.json
|
|
28
|
+
Produces a structured snapshot of host state: layout version, runtimes,
|
|
29
|
+
crontab entries, launchd plists, mounts, env vars, extensions.
|
|
30
|
+
|
|
31
|
+
2. Agent reads inspection-<host>.json and selects the appropriate reference
|
|
32
|
+
runbook from reference/ (or a custom runbook), substituting {{daveRoot}}
|
|
33
|
+
and {{inspectedAt}} template variables.
|
|
34
|
+
|
|
35
|
+
3. Agent executes each step sequentially:
|
|
36
|
+
- Evaluates preconditions against inspection data or live host state
|
|
37
|
+
- Dispatches the intent to a host-specific actuator
|
|
38
|
+
- Checks expect outcomes
|
|
39
|
+
- On failure: triggers rollback action
|
|
40
|
+
|
|
41
|
+
4. On completion: define-roster-entry registers the host as v2.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Executing-agent contract
|
|
45
|
+
|
|
46
|
+
When an agent interprets a runbook step, it maps `intent` to host-specific commands as follows:
|
|
47
|
+
|
|
48
|
+
| Intent | macOS | Linux |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `snapshot-v1-fork` | `git tag` or `cp -r` | Same |
|
|
51
|
+
| `run-migrate-to-v2` | `node migrate-to-v2/cli.mjs` | Same |
|
|
52
|
+
| `rewrite-crontab` | `crontab -l` + edit + `crontab -` | Same |
|
|
53
|
+
| `drop-cron-entry` | `crontab -l` + filter + `crontab -` | Same |
|
|
54
|
+
| `update-host-service` | Edit `~/Library/LaunchAgents/<label>.plist`, `launchctl unload/load` | Edit `/etc/systemd/system/<unit>.service`, `systemctl daemon-reload && restart` |
|
|
55
|
+
| `verify-sync-continuity` | Check mount reachable, run sync script | Same |
|
|
56
|
+
| `verify-boot-health` | `pa status` in `daveRoot` | Same |
|
|
57
|
+
| `define-roster-entry` | Write to `operator/roster.json` | Same |
|
|
58
|
+
|
|
59
|
+
The agent reads the `host.os` field from `inspection.json` to determine which platform-specific path to take for `update-host-service`. All other intents are platform-neutral.
|
|
60
|
+
|
|
61
|
+
## Schema reference
|
|
62
|
+
|
|
63
|
+
- `schema/runbook.schema.json` — JSON Schema 2020-12 for runbook documents
|
|
64
|
+
- `schema/inspection.schema.json` — JSON Schema 2020-12 for inspection.json output
|
|
65
|
+
|
|
66
|
+
## How to validate a runbook
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { validate } from '@stravica-ai/engineering-pa-runbook/src/validator.mjs';
|
|
70
|
+
import { readFileSync } from 'node:fs';
|
|
71
|
+
|
|
72
|
+
const schema = JSON.parse(readFileSync('./schema/runbook.schema.json', 'utf8'));
|
|
73
|
+
const runbook = JSON.parse(readFileSync('./reference/hq-dave.runbook.json', 'utf8'));
|
|
74
|
+
const result = validate(schema, runbook);
|
|
75
|
+
if (!result.valid) {
|
|
76
|
+
console.error(result.errors);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or from the CLI:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node -e "
|
|
84
|
+
import('./src/validator.mjs').then(async ({validate}) => {
|
|
85
|
+
const {readFile} = await import('node:fs/promises');
|
|
86
|
+
const schema = JSON.parse(await readFile('./schema/runbook.schema.json','utf8'));
|
|
87
|
+
const rb = JSON.parse(await readFile('./reference/hq-dave.runbook.json','utf8'));
|
|
88
|
+
const r = validate(schema, rb);
|
|
89
|
+
console.log(r.valid ? 'VALID' : 'INVALID: ' + r.errors.join('; '));
|
|
90
|
+
});
|
|
91
|
+
"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Tests
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pnpm --filter @stravica-ai/engineering-pa-runbook test
|
|
98
|
+
# or
|
|
99
|
+
node --test 'test/**/*.test.mjs'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- `test/validator.test.mjs` — unit tests for the structural validator
|
|
103
|
+
- `test/runbooks.test.mjs` — validates all 3 reference runbooks against the schema
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stravica-ai/engineering-pa-runbook",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Migration runbook JSON Schema, reference runbooks, and structural validator.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/validator.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/validator.mjs",
|
|
9
|
+
"./src/*": "./src/*"
|
|
10
|
+
},
|
|
11
|
+
"private": true,
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"engines": { "node": ">=24" },
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test --test-isolation=none 'test/**/*.test.mjs'"
|
|
16
|
+
},
|
|
17
|
+
"//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper — see packages/cli/test/secrets. Counts must never lie."
|
|
18
|
+
}
|