@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.
Files changed (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/bin/desk +9 -0
  4. package/bin/distill +2 -0
  5. package/bin/estate-sync +2 -0
  6. package/bin/launch +2 -0
  7. package/bin/memory-sync +2 -0
  8. package/bin/migrate-inspect +2 -0
  9. package/bin/pa +2 -0
  10. package/bin/pa-coordinator-hook +2 -0
  11. package/bin/pa-init +2 -0
  12. package/bin/research +2 -0
  13. package/bin/scheduler +2 -0
  14. package/bin/secrets +2 -0
  15. package/bin/slice +2 -0
  16. package/bin/smoke +9 -0
  17. package/bin/sync +2 -0
  18. package/dist/engineering-pa-cli/README.md +205 -0
  19. package/dist/engineering-pa-cli/bin/estate-sync +2 -0
  20. package/dist/engineering-pa-cli/bin/launch +2 -0
  21. package/dist/engineering-pa-cli/bin/pa +2 -0
  22. package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
  23. package/dist/engineering-pa-cli/bin/research +2 -0
  24. package/dist/engineering-pa-cli/bin/scheduler +2 -0
  25. package/dist/engineering-pa-cli/bin/secrets +2 -0
  26. package/dist/engineering-pa-cli/bin/smoke +9 -0
  27. package/dist/engineering-pa-cli/bin/sync +2 -0
  28. package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
  29. package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
  30. package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
  31. package/dist/engineering-pa-cli/docs/gsc.md +175 -0
  32. package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
  33. package/dist/engineering-pa-cli/docs/secrets.md +221 -0
  34. package/dist/engineering-pa-cli/docs/smoke.md +113 -0
  35. package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
  36. package/dist/engineering-pa-cli/index.mjs +17 -0
  37. package/dist/engineering-pa-cli/package.json +37 -0
  38. package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
  39. package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
  40. package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
  41. package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
  42. package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
  43. package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
  44. package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
  45. package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
  46. package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
  47. package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
  48. package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
  49. package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
  50. package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
  51. package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
  52. package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
  53. package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
  54. package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
  55. package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
  56. package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
  57. package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
  58. package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
  59. package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
  60. package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
  61. package/dist/engineering-pa-cli/src/launch/README.md +214 -0
  62. package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
  63. package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
  64. package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
  65. package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
  66. package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
  67. package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
  68. package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
  69. package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
  70. package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
  71. package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
  72. package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
  73. package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
  74. package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
  75. package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
  76. package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
  77. package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
  78. package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
  79. package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
  80. package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
  81. package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
  82. package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
  83. package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
  84. package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
  85. package/dist/engineering-pa-cli/src/research/README.md +360 -0
  86. package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
  87. package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
  88. package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
  89. package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
  90. package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
  91. package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
  92. package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
  93. package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
  94. package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
  95. package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
  96. package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
  97. package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
  98. package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
  99. package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
  100. package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
  101. package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
  102. package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
  103. package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
  104. package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
  105. package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
  106. package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
  107. package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
  108. package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
  109. package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
  110. package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
  111. package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
  112. package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
  113. package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
  114. package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
  115. package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
  116. package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
  117. package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
  118. package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
  119. package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
  120. package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
  121. package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
  122. package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
  123. package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
  124. package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
  125. package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
  126. package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
  127. package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
  128. package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
  129. package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
  130. package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
  131. package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
  132. package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
  133. package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
  134. package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
  135. package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
  136. package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
  137. package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
  138. package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
  139. package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
  140. package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
  141. package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
  142. package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
  143. package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
  144. package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
  145. package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
  146. package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
  147. package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
  148. package/dist/engineering-pa-core/README.md +60 -0
  149. package/dist/engineering-pa-core/bin/desk +9 -0
  150. package/dist/engineering-pa-core/bin/distill +2 -0
  151. package/dist/engineering-pa-core/bin/memory-sync +2 -0
  152. package/dist/engineering-pa-core/bin/slice +2 -0
  153. package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
  154. package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
  155. package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
  156. package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
  157. package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
  158. package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
  159. package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
  160. package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
  161. package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
  162. package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
  163. package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
  164. package/dist/engineering-pa-core/docs/commands/init.md +159 -0
  165. package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
  166. package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
  167. package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
  168. package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
  169. package/dist/engineering-pa-core/docs/lock.md +155 -0
  170. package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
  171. package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
  172. package/dist/engineering-pa-core/docs/scheduling.md +35 -0
  173. package/dist/engineering-pa-core/docs/standards/README.md +24 -0
  174. package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
  175. package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
  176. package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
  177. package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
  178. package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
  179. package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
  180. package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
  181. package/dist/engineering-pa-core/docs/working-directory.md +29 -0
  182. package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
  183. package/dist/engineering-pa-core/index.mjs +21 -0
  184. package/dist/engineering-pa-core/knowledge/README.md +53 -0
  185. package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
  186. package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
  187. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
  188. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
  189. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
  190. package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
  191. package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
  192. package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
  193. package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
  194. package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
  195. package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
  196. package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
  197. package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
  198. package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
  199. package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
  200. package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
  201. package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
  202. package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
  203. package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
  204. package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
  205. package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
  206. package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
  207. package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
  208. package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
  209. package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
  210. package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
  211. package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
  212. package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
  213. package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
  214. package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
  215. package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
  216. package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
  217. package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
  218. package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
  219. package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
  220. package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
  221. package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
  222. package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
  223. package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
  224. package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
  225. package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
  226. package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
  227. package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
  228. package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
  229. package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
  230. package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
  231. package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
  232. package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
  233. package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
  234. package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
  235. package/dist/engineering-pa-core/package.json +26 -0
  236. package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
  237. package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
  238. package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
  239. package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
  240. package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
  241. package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
  242. package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
  243. package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
  244. package/dist/engineering-pa-core/schema/project.json +114 -0
  245. package/dist/engineering-pa-core/schema/schedule.json +63 -0
  246. package/dist/engineering-pa-core/schema/work-item.json +92 -0
  247. package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
  248. package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
  249. package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
  250. package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
  251. package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
  252. package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
  253. package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
  254. package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
  255. package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
  256. package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
  257. package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
  258. package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
  259. package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
  260. package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
  261. package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
  262. package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
  263. package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
  264. package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
  265. package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
  266. package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
  267. package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
  268. package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
  269. package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
  270. package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
  271. package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
  272. package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
  273. package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
  274. package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
  275. package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
  276. package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
  277. package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
  278. package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
  279. package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
  280. package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
  281. package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
  282. package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
  283. package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
  284. package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
  285. package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
  286. package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
  287. package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
  288. package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
  289. package/dist/engineering-pa-librarian/README.md +38 -0
  290. package/dist/engineering-pa-librarian/index.mjs +12 -0
  291. package/dist/engineering-pa-librarian/package.json +19 -0
  292. package/dist/engineering-pa-migrate-inspect/README.md +131 -0
  293. package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
  294. package/dist/engineering-pa-migrate-inspect/package.json +21 -0
  295. package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
  296. package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
  297. package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
  298. package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
  299. package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
  300. package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
  301. package/dist/engineering-pa-runbook/README.md +103 -0
  302. package/dist/engineering-pa-runbook/package.json +18 -0
  303. package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
  304. package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
  305. package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
  306. package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
  307. package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
  308. package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
  309. package/dist/engineering-pa-scaffold/README.md +164 -0
  310. package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
  311. package/dist/engineering-pa-scaffold/index.mjs +42 -0
  312. package/dist/engineering-pa-scaffold/package.json +22 -0
  313. package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
  314. package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
  315. package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
  316. package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
  317. package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
  318. package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
  319. package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
  320. package/dist/engineering-pa-types-engineering/README.md +35 -0
  321. package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
  322. package/dist/engineering-pa-types-engineering/index.mjs +9 -0
  323. package/dist/engineering-pa-types-engineering/package.json +19 -0
  324. package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
  325. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
  326. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
  327. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
  328. package/dist/engineering-pa-workspace/README.md +197 -0
  329. package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
  330. package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
  331. package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
  332. package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
  333. package/dist/engineering-pa-workspace/app/banner.js +320 -0
  334. package/dist/engineering-pa-workspace/app/delegations.html +345 -0
  335. package/dist/engineering-pa-workspace/app/home.html +1060 -0
  336. package/dist/engineering-pa-workspace/app/index.html +361 -0
  337. package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
  338. package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
  339. package/dist/engineering-pa-workspace/app/nav.json +75 -0
  340. package/dist/engineering-pa-workspace/app/notes.html +199 -0
  341. package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
  342. package/dist/engineering-pa-workspace/app/profile.html +243 -0
  343. package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
  344. package/dist/engineering-pa-workspace/app/projects.html +297 -0
  345. package/dist/engineering-pa-workspace/app/repos.html +598 -0
  346. package/dist/engineering-pa-workspace/app/schedules.html +486 -0
  347. package/dist/engineering-pa-workspace/app/secrets.html +164 -0
  348. package/dist/engineering-pa-workspace/app/staging.html +319 -0
  349. package/dist/engineering-pa-workspace/app/tasks.html +465 -0
  350. package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
  351. package/dist/engineering-pa-workspace/app/viewer.html +116 -0
  352. package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
  353. package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
  354. package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
  355. package/dist/engineering-pa-workspace/index.mjs +12 -0
  356. package/dist/engineering-pa-workspace/nav.json.example +8 -0
  357. package/dist/engineering-pa-workspace/package.json +27 -0
  358. package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
  359. package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
  360. package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
  361. package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
  362. package/dist/engineering-pa-workspace/src/server.mjs +516 -0
  363. package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
  364. package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
  365. package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
  366. package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
  367. package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
  368. package/index.mjs +60 -0
  369. package/package.json +58 -0
  370. package/templates/banned-ai-tells.md +102 -0
  371. package/templates/purpose.md +105 -0
  372. package/templates/voice-guide.md +98 -0
@@ -0,0 +1,318 @@
1
+ // cloudflare.mjs — Cloudflare API helpers for DNS planting.
2
+ //
3
+ // Scope is intentionally narrow: GSC verification flow only needs
4
+ // 1. resolve the zone id for a domain
5
+ // 2. find an existing TXT record by name (idempotency)
6
+ // 3. create or update a TXT record
7
+ //
8
+ // Credentials are read from the SOPS vault. The vault holds two key
9
+ // shapes for Cloudflare:
10
+ //
11
+ // - Account-suffixed pair (preferred — multi-account safe):
12
+ // CLOUDFLARE_PAGES_API_KEY_<SUFFIX>
13
+ // CLOUDFLARE_PAGES_ACCOUNT_ID_<SUFFIX>
14
+ // The caller picks the suffix via the `account` option (lower-cased
15
+ // externally; matched case-insensitively to the upper-case suffix).
16
+ //
17
+ // - Unsuffixed pair (single-account default):
18
+ // CLOUDFLARE_PAGES_API_KEY
19
+ // CLOUDFLARE_PAGES_ACCOUNT_ID
20
+ //
21
+ // - Legacy bare pair (back-compat fallback):
22
+ // CLOUDFLARE_API_TOKEN
23
+ // CLOUDFLARE_ACCOUNT_ID
24
+ //
25
+ // `fetchImpl` and `now` are injectable for tests.
26
+
27
+ import { defaultPaths, checkVault, secretsFileExists, sopsDecrypt, VaultError } from '../secrets/vault.mjs';
28
+
29
+ const CF_API = 'https://api.cloudflare.com/client/v4';
30
+
31
+ // Vault-key shapes. `*_TEMPLATE` uses `<S>` as the suffix placeholder.
32
+ const VAULT_KEYS = Object.freeze({
33
+ // Account-suffixed (preferred).
34
+ tokenTemplate: 'CLOUDFLARE_PAGES_API_KEY_<S>',
35
+ accountIdTemplate: 'CLOUDFLARE_PAGES_ACCOUNT_ID_<S>',
36
+ // Unsuffixed (single-account default).
37
+ tokenUnsuffixed: 'CLOUDFLARE_PAGES_API_KEY',
38
+ accountIdUnsuffixed: 'CLOUDFLARE_PAGES_ACCOUNT_ID',
39
+ // Legacy bare (back-compat fallback).
40
+ tokenLegacy: 'CLOUDFLARE_API_TOKEN',
41
+ accountIdLegacy: 'CLOUDFLARE_ACCOUNT_ID',
42
+ });
43
+
44
+ /**
45
+ * Normalise an `--account` value to the upper-case suffix used in the
46
+ * vault keys. Letters + digits only — anything else is rejected so a
47
+ * stray "/" or " " can't smuggle into a vault key lookup.
48
+ *
49
+ * @param {string} account
50
+ * @returns {string} upper-case suffix
51
+ * @throws {VaultError} on invalid input
52
+ */
53
+ export function accountSuffix(account) {
54
+ const raw = String(account || '').trim();
55
+ if (!raw) {
56
+ throw new VaultError('--account: value is required', 1);
57
+ }
58
+ if (!/^[A-Za-z0-9]+$/.test(raw)) {
59
+ throw new VaultError(
60
+ `--account: "${raw}" is invalid. Use letters/digits only (e.g. stravica, clive).`,
61
+ 1,
62
+ );
63
+ }
64
+ return raw.toUpperCase();
65
+ }
66
+
67
+ /**
68
+ * Build the vault key names a given `account` selection resolves to.
69
+ * If `account` is omitted, returns the unsuffixed pair (with a legacy
70
+ * fallback list for back-compat).
71
+ *
72
+ * @param {string} [account]
73
+ * @returns {{ tokenKey: string, accountIdKey: string, fallbacks?: Array<{tokenKey: string, accountIdKey: string}> }}
74
+ */
75
+ export function resolveVaultKeys(account) {
76
+ if (account) {
77
+ const s = accountSuffix(account);
78
+ return {
79
+ tokenKey: VAULT_KEYS.tokenTemplate.replace('<S>', s),
80
+ accountIdKey: VAULT_KEYS.accountIdTemplate.replace('<S>', s),
81
+ };
82
+ }
83
+ return {
84
+ tokenKey: VAULT_KEYS.tokenUnsuffixed,
85
+ accountIdKey: VAULT_KEYS.accountIdUnsuffixed,
86
+ fallbacks: [
87
+ { tokenKey: VAULT_KEYS.tokenLegacy, accountIdKey: VAULT_KEYS.accountIdLegacy },
88
+ ],
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Load Cloudflare credentials from the SOPS vault. Override paths or
94
+ * provide raw credentials for tests.
95
+ *
96
+ * Resolution order:
97
+ * 1. Test overrides (`cloudflareToken` + `cloudflareAccountId`).
98
+ * 2. If `account` provided: only the suffixed pair, hard-fail if missing.
99
+ * 3. Otherwise: unsuffixed pair, then the legacy bare pair as fallback.
100
+ *
101
+ * @param {object} overrides
102
+ * @param {object} [overrides.paths]
103
+ * @param {string} [overrides.cloudflareToken]
104
+ * @param {string} [overrides.cloudflareAccountId]
105
+ * @param {string} [overrides.account] Account selector (e.g. "stravica").
106
+ * @param {object} [overrides.vaultData] Pre-decrypted vault map (test seam).
107
+ * When provided, the function uses this instead of running the SOPS
108
+ * check + decrypt. Lets tests cover the suffix-resolution and
109
+ * fallback paths without touching the filesystem.
110
+ * @returns {Promise<{token: string, accountId: string, account: string|null, vaultKeys: {tokenKey: string, accountIdKey: string}}>}
111
+ */
112
+ export async function loadCloudflareCreds(overrides = {}) {
113
+ if (overrides.cloudflareToken && overrides.cloudflareAccountId) {
114
+ return {
115
+ token: overrides.cloudflareToken,
116
+ accountId: overrides.cloudflareAccountId,
117
+ account: overrides.account || null,
118
+ vaultKeys: null,
119
+ };
120
+ }
121
+ let data;
122
+ if (overrides.vaultData) {
123
+ data = overrides.vaultData;
124
+ } else {
125
+ const paths = overrides.paths || defaultPaths();
126
+ const state = checkVault(paths);
127
+ if (!state.ok) throw new VaultError(state.message, state.exitCode ?? 1);
128
+ if (!secretsFileExists(paths)) {
129
+ throw new VaultError(
130
+ `No secrets file at ${paths.secretsFile}. Run 'secrets init' first.`,
131
+ 1,
132
+ );
133
+ }
134
+ data = await sopsDecrypt(paths, paths.secretsFile);
135
+ }
136
+
137
+ const plan = resolveVaultKeys(overrides.account);
138
+
139
+ // Account-suffixed: must hit exactly the suffixed pair.
140
+ if (overrides.account) {
141
+ const token = data[plan.tokenKey];
142
+ const accountId = data[plan.accountIdKey];
143
+ if (!token || !accountId) {
144
+ const missing = [];
145
+ if (!token) missing.push(plan.tokenKey);
146
+ if (!accountId) missing.push(plan.accountIdKey);
147
+ throw new VaultError(
148
+ `--account "${overrides.account}" expects vault keys [${missing.join(', ')}] but they are not set. ` +
149
+ `Store them with:\n secrets set ${plan.tokenKey} <token>\n secrets set ${plan.accountIdKey} <id>`,
150
+ 1,
151
+ );
152
+ }
153
+ return {
154
+ token,
155
+ accountId,
156
+ account: overrides.account,
157
+ vaultKeys: { tokenKey: plan.tokenKey, accountIdKey: plan.accountIdKey },
158
+ };
159
+ }
160
+
161
+ // No --account: try unsuffixed pair first, then legacy bare pair.
162
+ const candidates = [
163
+ { tokenKey: plan.tokenKey, accountIdKey: plan.accountIdKey },
164
+ ...(plan.fallbacks || []),
165
+ ];
166
+ for (const cand of candidates) {
167
+ const token = data[cand.tokenKey];
168
+ const accountId = data[cand.accountIdKey];
169
+ if (token && accountId) {
170
+ return { token, accountId, account: null, vaultKeys: cand };
171
+ }
172
+ }
173
+ // None matched — error message lists the primary pair the operator
174
+ // is expected to set; legacy fallback is a back-compat courtesy, not
175
+ // a thing we advertise.
176
+ throw new VaultError(
177
+ `Cloudflare vault keys not found. Pass --account <name> (e.g. --account stravica) ` +
178
+ `to use the per-account pair, or store the default pair:\n` +
179
+ ` secrets set ${VAULT_KEYS.tokenUnsuffixed} <token>\n` +
180
+ ` secrets set ${VAULT_KEYS.accountIdUnsuffixed} <id>`,
181
+ 1,
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Tiny wrapper around fetch with CF auth headers + structured error handling.
187
+ * Returns the parsed JSON body on 2xx; throws an Error with .exitCode on
188
+ * non-2xx or on a CF `success: false` payload.
189
+ */
190
+ export async function cfFetch(token, path, init = {}, fetchImpl) {
191
+ const fetchFn = fetchImpl || globalThis.fetch;
192
+ if (typeof fetchFn !== 'function') {
193
+ throw Object.assign(new Error('fetch is not available in this runtime'), { exitCode: 3 });
194
+ }
195
+ const url = path.startsWith('http') ? path : `${CF_API}${path}`;
196
+ const res = await fetchFn(url, {
197
+ ...init,
198
+ headers: {
199
+ Authorization: `Bearer ${token}`,
200
+ 'Content-Type': 'application/json',
201
+ ...(init.headers || {}),
202
+ },
203
+ });
204
+ const text = await res.text();
205
+ let body;
206
+ try { body = text ? JSON.parse(text) : {}; } catch {
207
+ throw Object.assign(
208
+ new Error(`Cloudflare API: non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`),
209
+ { exitCode: 3 },
210
+ );
211
+ }
212
+ if (!res.ok || body.success === false) {
213
+ const detail = (body.errors || [])
214
+ .map((e) => `${e.code}: ${e.message}`)
215
+ .join('; ') || `HTTP ${res.status}`;
216
+ throw Object.assign(
217
+ new Error(`Cloudflare API ${init.method || 'GET'} ${path} failed: ${detail}`),
218
+ { exitCode: 2, status: res.status, body },
219
+ );
220
+ }
221
+ return body;
222
+ }
223
+
224
+ /**
225
+ * Resolve a Cloudflare zone id for `domain`. Caller can pass either the
226
+ * exact zone name (foo.com) or a subdomain — we try exact match first,
227
+ * then walk up the domain segments.
228
+ */
229
+ export async function resolveZoneId(creds, domain, fetchImpl) {
230
+ const candidates = candidateZoneNames(domain);
231
+ for (const candidate of candidates) {
232
+ const q = new URLSearchParams({ name: candidate, 'account.id': creds.accountId });
233
+ const body = await cfFetch(creds.token, `/zones?${q.toString()}`, {}, fetchImpl);
234
+ if (body.result && body.result.length > 0) {
235
+ return { zoneId: body.result[0].id, zoneName: body.result[0].name };
236
+ }
237
+ }
238
+ throw Object.assign(
239
+ new Error(
240
+ `No Cloudflare zone matches "${domain}" in account ${creds.accountId}. ` +
241
+ `Tried candidates: ${candidates.join(', ')}`,
242
+ ),
243
+ { exitCode: 1 },
244
+ );
245
+ }
246
+
247
+ /** Build descending zone-name candidates from a domain. */
248
+ export function candidateZoneNames(domain) {
249
+ const stripped = String(domain || '')
250
+ .toLowerCase()
251
+ .replace(/^sc-domain:/, '')
252
+ .replace(/^https?:\/\//, '')
253
+ .split('/')[0]
254
+ .replace(/\.$/, '');
255
+ const parts = stripped.split('.');
256
+ if (parts.length < 2) return [stripped];
257
+ // Try most-specific first (e.g. foo.example.com), then parent zones.
258
+ // Stop at two labels — single-label TLDs don't host CF zones.
259
+ const out = [];
260
+ for (let i = 0; i <= parts.length - 2; i++) {
261
+ out.push(parts.slice(i).join('.'));
262
+ }
263
+ return out;
264
+ }
265
+
266
+ /**
267
+ * Find a TXT record by exact name within a zone. Returns the record object
268
+ * or null if not found. Cloudflare returns names without trailing dots.
269
+ */
270
+ export async function findTxtRecord(creds, zoneId, name, fetchImpl) {
271
+ const q = new URLSearchParams({ type: 'TXT', name });
272
+ const body = await cfFetch(creds.token, `/zones/${zoneId}/dns_records?${q.toString()}`, {}, fetchImpl);
273
+ return (body.result && body.result[0]) || null;
274
+ }
275
+
276
+ /**
277
+ * Plant (create or update) a TXT record. Idempotent:
278
+ * - identical content => no-op, returns { action: 'unchanged', record }
279
+ * - same name, different content => PATCH, returns { action: 'updated', record }
280
+ * - missing => POST, returns { action: 'created', record }
281
+ */
282
+ export async function plantTxtRecord(creds, zoneId, name, content, fetchImpl) {
283
+ const existing = await findTxtRecord(creds, zoneId, name, fetchImpl);
284
+ // CF stores TXT values quoted in API responses ("value"); strip quotes
285
+ // before comparison so we don't churn on a benign formatting diff.
286
+ const stripQuotes = (v) => String(v || '').replace(/^"|"$/g, '');
287
+ if (existing) {
288
+ if (stripQuotes(existing.content) === content) {
289
+ return { action: 'unchanged', record: existing };
290
+ }
291
+ const body = await cfFetch(
292
+ creds.token,
293
+ `/zones/${zoneId}/dns_records/${existing.id}`,
294
+ {
295
+ method: 'PATCH',
296
+ body: JSON.stringify({ content, ttl: 1, proxied: false }),
297
+ },
298
+ fetchImpl,
299
+ );
300
+ return { action: 'updated', record: body.result };
301
+ }
302
+ const body = await cfFetch(
303
+ creds.token,
304
+ `/zones/${zoneId}/dns_records`,
305
+ {
306
+ method: 'POST',
307
+ body: JSON.stringify({ type: 'TXT', name, content, ttl: 1, proxied: false }),
308
+ },
309
+ fetchImpl,
310
+ );
311
+ return { action: 'created', record: body.result };
312
+ }
313
+
314
+ /** Sleep helper — injectable for tests so timeouts don't slow the suite. */
315
+ export function sleep(ms, sleepImpl) {
316
+ if (typeof sleepImpl === 'function') return sleepImpl(ms);
317
+ return new Promise((res) => setTimeout(res, ms));
318
+ }
@@ -0,0 +1,70 @@
1
+ // add-property — Obtain the verification TXT token for a domain property.
2
+ //
3
+ // GSC domain-property verification (the modern, recommended kind) is
4
+ // gated by Google's Site Verification API, not the Search Console
5
+ // `sites.add` call directly. The two-stage flow is:
6
+ //
7
+ // 1. siteVerification.webResource.getToken({verificationMethod: DNS_TXT,
8
+ // site: {type: INET_DOMAIN, identifier: <domain>}})
9
+ // -> returns the TXT token to plant (string like "google-site-verification=...")
10
+ // 2. operator plants TXT at apex
11
+ // 3. siteVerification.webResource.insert(...) confirms ownership
12
+ // 4. (optional) webmasters.sites.add(siteUrl="sc-domain:<domain>") registers
13
+ // the property in Search Console explicitly. In practice it surfaces
14
+ // automatically post-verification.
15
+ //
16
+ // This verb covers step 1 — it's the entry point. `verify-via-cf-dns`
17
+ // chains 1→2→3→4 in one call.
18
+ //
19
+ // URL-prefix properties (legacy https://foo.com/ shape) are not supported
20
+ // here — domain-property is the default for new properties.
21
+
22
+ import { buildGscClient, toDomainProperty, bareDomain } from '../auth.mjs';
23
+
24
+ export async function runAddProperty(args, overrides = {}) {
25
+ const domain = args[0];
26
+ if (!domain) {
27
+ process.stderr.write('Usage: pa gsc add-property <domain>\n');
28
+ return 1;
29
+ }
30
+ const siteUrl = toDomainProperty(domain);
31
+ const bare = bareDomain(siteUrl);
32
+
33
+ let client;
34
+ try {
35
+ client = await buildGscClient(overrides);
36
+ } catch (e) {
37
+ process.stderr.write(`[gsc add-property] auth failed: ${e.message}\n`);
38
+ return e.exitCode || 2;
39
+ }
40
+
41
+ let token;
42
+ try {
43
+ const res = await client.siteVerification.webResource.getToken({
44
+ requestBody: {
45
+ verificationMethod: 'DNS_TXT',
46
+ site: { type: 'INET_DOMAIN', identifier: bare },
47
+ },
48
+ });
49
+ token = res?.data?.token;
50
+ } catch (e) {
51
+ process.stderr.write(`[gsc add-property] getToken failed: ${e.message}\n`);
52
+ return 2;
53
+ }
54
+
55
+ if (!token) {
56
+ process.stderr.write('[gsc add-property] no token returned from siteVerification API\n');
57
+ return 2;
58
+ }
59
+
60
+ const out = overrides.stdout || process.stdout;
61
+ const result = {
62
+ domain: bare,
63
+ siteUrl,
64
+ verificationMethod: 'DNS_TXT',
65
+ txtRecordName: bare,
66
+ txtRecordValue: token,
67
+ };
68
+ out.write(JSON.stringify(result, null, 2) + '\n');
69
+ return 0;
70
+ }
@@ -0,0 +1,33 @@
1
+ // list-properties — Enumerate all GSC properties the SA can see.
2
+ //
3
+ // Wraps webmasters.sites.list. Useful as the canonical "did I add the
4
+ // service-account email as a user on the right property?" check.
5
+
6
+ import { buildGscClient } from '../auth.mjs';
7
+
8
+ export async function runListProperties(_args, overrides = {}) {
9
+ let client;
10
+ try {
11
+ client = await buildGscClient(overrides);
12
+ } catch (e) {
13
+ process.stderr.write(`[gsc list-properties] auth failed: ${e.message}\n`);
14
+ return e.exitCode || 2;
15
+ }
16
+
17
+ let response;
18
+ try {
19
+ response = await client.webmasters.sites.list({});
20
+ } catch (e) {
21
+ process.stderr.write(`[gsc list-properties] sites.list failed: ${e.message}\n`);
22
+ return 2;
23
+ }
24
+
25
+ const entries = (response?.data?.siteEntry || []).map((s) => ({
26
+ siteUrl: s.siteUrl,
27
+ permissionLevel: s.permissionLevel,
28
+ }));
29
+
30
+ const out = overrides.stdout || process.stdout;
31
+ out.write(JSON.stringify({ count: entries.length, properties: entries }, null, 2) + '\n');
32
+ return 0;
33
+ }
@@ -0,0 +1,130 @@
1
+ // pull-analytics — Search-analytics rows for a property.
2
+ //
3
+ // Wraps webmasters.searchanalytics.query. Defaults to last 28 days, top
4
+ // queries, 100 rows. JSON array on stdout — one element per row, with
5
+ // dimensions hydrated into named fields.
6
+
7
+ import { buildGscClient, toDomainProperty } from '../auth.mjs';
8
+
9
+ const VALID_DIMENSIONS = new Set(['query', 'page', 'country', 'device', 'date', 'searchAppearance']);
10
+
11
+ function parseFlags(args) {
12
+ const out = {
13
+ positional: [],
14
+ days: 28,
15
+ dimensions: ['query'],
16
+ rows: 100,
17
+ };
18
+ for (let i = 0; i < args.length; i++) {
19
+ const a = args[i];
20
+ if (a === '--days') {
21
+ out.days = Number(args[++i]);
22
+ } else if (a.startsWith('--days=')) {
23
+ out.days = Number(a.slice('--days='.length));
24
+ } else if (a === '--dimensions') {
25
+ out.dimensions = String(args[++i] || '').split(',').map((s) => s.trim()).filter(Boolean);
26
+ } else if (a.startsWith('--dimensions=')) {
27
+ out.dimensions = a.slice('--dimensions='.length).split(',').map((s) => s.trim()).filter(Boolean);
28
+ } else if (a === '--rows') {
29
+ out.rows = Number(args[++i]);
30
+ } else if (a.startsWith('--rows=')) {
31
+ out.rows = Number(a.slice('--rows='.length));
32
+ } else {
33
+ out.positional.push(a);
34
+ }
35
+ }
36
+
37
+ if (!Number.isInteger(out.days) || out.days < 1 || out.days > 16 * 30) {
38
+ throw Object.assign(new Error('--days must be an integer between 1 and 480'), { exitCode: 1 });
39
+ }
40
+ if (!Number.isInteger(out.rows) || out.rows < 1 || out.rows > 25000) {
41
+ throw Object.assign(new Error('--rows must be an integer between 1 and 25000'), { exitCode: 1 });
42
+ }
43
+ for (const d of out.dimensions) {
44
+ if (!VALID_DIMENSIONS.has(d)) {
45
+ throw Object.assign(
46
+ new Error(`--dimensions: unknown dimension "${d}". Valid: ${[...VALID_DIMENSIONS].join(',')}`),
47
+ { exitCode: 1 },
48
+ );
49
+ }
50
+ }
51
+ return out;
52
+ }
53
+
54
+ /** Format a Date as YYYY-MM-DD (UTC). */
55
+ export function ymd(date) {
56
+ const y = date.getUTCFullYear();
57
+ const m = String(date.getUTCMonth() + 1).padStart(2, '0');
58
+ const d = String(date.getUTCDate()).padStart(2, '0');
59
+ return `${y}-${m}-${d}`;
60
+ }
61
+
62
+ export async function runPullAnalytics(args, overrides = {}) {
63
+ let flags;
64
+ try { flags = parseFlags(args); } catch (e) {
65
+ process.stderr.write(`[gsc pull-analytics] ${e.message}\n`);
66
+ return e.exitCode || 1;
67
+ }
68
+
69
+ const domain = flags.positional[0];
70
+ if (!domain) {
71
+ process.stderr.write('Usage: pa gsc pull-analytics <domain> [--days N] [--dimensions D1,D2,...] [--rows N]\n');
72
+ return 1;
73
+ }
74
+
75
+ const siteUrl = toDomainProperty(domain);
76
+ const now = overrides.now ? new Date(overrides.now) : new Date();
77
+ // GSC data lags ~2 days. Always end "today" — server-side, GSC will
78
+ // simply return zeros for the trailing rows; the API has no minimum-lag
79
+ // requirement on the request side.
80
+ const endDate = ymd(now);
81
+ const start = new Date(now.getTime() - flags.days * 24 * 60 * 60 * 1000);
82
+ const startDate = ymd(start);
83
+
84
+ let client;
85
+ try {
86
+ client = await buildGscClient(overrides);
87
+ } catch (e) {
88
+ process.stderr.write(`[gsc pull-analytics] auth failed: ${e.message}\n`);
89
+ return e.exitCode || 2;
90
+ }
91
+
92
+ let response;
93
+ try {
94
+ response = await client.webmasters.searchanalytics.query({
95
+ siteUrl,
96
+ requestBody: {
97
+ startDate,
98
+ endDate,
99
+ dimensions: flags.dimensions,
100
+ rowLimit: flags.rows,
101
+ },
102
+ });
103
+ } catch (e) {
104
+ process.stderr.write(`[gsc pull-analytics] searchanalytics.query failed: ${e.message}\n`);
105
+ return 2;
106
+ }
107
+
108
+ const rows = (response?.data?.rows || []).map((row) => {
109
+ const hydrated = {};
110
+ for (let i = 0; i < flags.dimensions.length; i++) {
111
+ hydrated[flags.dimensions[i]] = row.keys?.[i];
112
+ }
113
+ hydrated.clicks = row.clicks ?? 0;
114
+ hydrated.impressions = row.impressions ?? 0;
115
+ hydrated.ctr = row.ctr ?? 0;
116
+ hydrated.position = row.position ?? 0;
117
+ return hydrated;
118
+ });
119
+
120
+ const out = overrides.stdout || process.stdout;
121
+ out.write(JSON.stringify({
122
+ siteUrl,
123
+ startDate,
124
+ endDate,
125
+ dimensions: flags.dimensions,
126
+ rowCount: rows.length,
127
+ rows,
128
+ }, null, 2) + '\n');
129
+ return 0;
130
+ }
@@ -0,0 +1,41 @@
1
+ // submit-sitemap — Submit a sitemap to GSC for a property.
2
+ //
3
+ // Wraps webmasters.sitemaps.submit. The property must already be
4
+ // verified (see `pa gsc verify-via-cf-dns`).
5
+
6
+ import { buildGscClient, toDomainProperty } from '../auth.mjs';
7
+
8
+ export async function runSubmitSitemap(args, overrides = {}) {
9
+ const domain = args[0];
10
+ const sitemapUrl = args[1];
11
+
12
+ if (!domain || !sitemapUrl) {
13
+ process.stderr.write('Usage: pa gsc submit-sitemap <domain> <sitemap-url>\n');
14
+ return 1;
15
+ }
16
+ if (!/^https?:\/\//i.test(sitemapUrl)) {
17
+ process.stderr.write(`[gsc submit-sitemap] sitemap-url must be an absolute http(s) URL, got "${sitemapUrl}"\n`);
18
+ return 1;
19
+ }
20
+
21
+ const siteUrl = toDomainProperty(domain);
22
+
23
+ let client;
24
+ try {
25
+ client = await buildGscClient(overrides);
26
+ } catch (e) {
27
+ process.stderr.write(`[gsc submit-sitemap] auth failed: ${e.message}\n`);
28
+ return e.exitCode || 2;
29
+ }
30
+
31
+ try {
32
+ await client.webmasters.sitemaps.submit({ siteUrl, feedpath: sitemapUrl });
33
+ } catch (e) {
34
+ process.stderr.write(`[gsc submit-sitemap] sitemaps.submit failed: ${e.message}\n`);
35
+ return 2;
36
+ }
37
+
38
+ const out = overrides.stdout || process.stdout;
39
+ out.write(JSON.stringify({ siteUrl, sitemap: sitemapUrl, submitted: true }, null, 2) + '\n');
40
+ return 0;
41
+ }