@b00dea/cockpit 0.0.0-stage → 0.1.1

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 (426) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/LICENSE +21 -0
  3. package/README.md +25 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2644 -0
  6. package/dist/autonomy.d.ts +45 -0
  7. package/dist/autonomy.js +173 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +117 -0
  11. package/dist/browser/commands.js +453 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +118 -0
  17. package/dist/browser/runtime.js +186 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +561 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/canonical-json.d.ts +2 -0
  23. package/dist/canonical-json.js +5 -0
  24. package/dist/cli-guard.d.ts +5 -0
  25. package/dist/cli-guard.js +71 -0
  26. package/dist/cli-main.d.ts +176 -0
  27. package/dist/cli-main.js +1584 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +16 -0
  30. package/dist/command-table.d.ts +121 -0
  31. package/dist/command-table.js +664 -0
  32. package/dist/config/load.d.ts +183 -0
  33. package/dist/config/load.js +367 -0
  34. package/dist/config/sibling-toml.d.ts +48 -0
  35. package/dist/config/sibling-toml.js +160 -0
  36. package/dist/config/surface-tools-toml.d.ts +9 -0
  37. package/dist/config/surface-tools-toml.js +37 -0
  38. package/dist/config/unconfirmed-toml.d.ts +9 -0
  39. package/dist/config/unconfirmed-toml.js +29 -0
  40. package/dist/config/verified-write.d.ts +43 -0
  41. package/dist/config/verified-write.js +89 -0
  42. package/dist/diagnostics.d.ts +26 -0
  43. package/dist/diagnostics.js +344 -0
  44. package/dist/docs/check.d.ts +16 -0
  45. package/dist/docs/check.js +197 -0
  46. package/dist/docs/decision-record.d.ts +34 -0
  47. package/dist/docs/decision-record.js +135 -0
  48. package/dist/docs/decisions.d.ts +16 -0
  49. package/dist/docs/decisions.js +103 -0
  50. package/dist/docs/markdown.d.ts +36 -0
  51. package/dist/docs/markdown.js +88 -0
  52. package/dist/docs/references.d.ts +32 -0
  53. package/dist/docs/references.js +148 -0
  54. package/dist/emit.d.ts +22 -0
  55. package/dist/emit.js +43 -0
  56. package/dist/entrypoint-kinds.d.ts +3 -0
  57. package/dist/entrypoint-kinds.js +11 -0
  58. package/dist/environment/application/confirm.d.ts +35 -0
  59. package/dist/environment/application/confirm.js +122 -0
  60. package/dist/environment/application/destructive.d.ts +6 -0
  61. package/dist/environment/application/destructive.js +429 -0
  62. package/dist/environment/application/doctor.d.ts +94 -0
  63. package/dist/environment/application/doctor.js +247 -0
  64. package/dist/environment/application/guard.d.ts +49 -0
  65. package/dist/environment/application/guard.js +677 -0
  66. package/dist/environment/application/health.d.ts +143 -0
  67. package/dist/environment/application/health.js +385 -0
  68. package/dist/environment/application/lifecycle-context.d.ts +235 -0
  69. package/dist/environment/application/lifecycle-context.js +154 -0
  70. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  71. package/dist/environment/application/lifecycle-state.js +202 -0
  72. package/dist/environment/application/lifecycle.d.ts +18 -0
  73. package/dist/environment/application/lifecycle.js +737 -0
  74. package/dist/environment/application/logs.d.ts +45 -0
  75. package/dist/environment/application/logs.js +127 -0
  76. package/dist/environment/application/services.d.ts +60 -0
  77. package/dist/environment/application/services.js +89 -0
  78. package/dist/environment/application/setup.d.ts +129 -0
  79. package/dist/environment/application/setup.js +741 -0
  80. package/dist/environment/application/shared.d.ts +11 -0
  81. package/dist/environment/application/shared.js +61 -0
  82. package/dist/environment/application/siblings.d.ts +43 -0
  83. package/dist/environment/application/siblings.js +269 -0
  84. package/dist/environment/claims.d.ts +389 -0
  85. package/dist/environment/claims.js +1701 -0
  86. package/dist/environment/codex-hook-trust.d.ts +11 -0
  87. package/dist/environment/codex-hook-trust.js +185 -0
  88. package/dist/environment/compose-environment.d.ts +1 -0
  89. package/dist/environment/compose-environment.js +6 -0
  90. package/dist/environment/compose-ownership.d.ts +21 -0
  91. package/dist/environment/compose-ownership.js +117 -0
  92. package/dist/environment/compose-topology.d.ts +125 -0
  93. package/dist/environment/compose-topology.js +490 -0
  94. package/dist/environment/contract.d.ts +522 -0
  95. package/dist/environment/contract.js +794 -0
  96. package/dist/environment/declaration.d.ts +14 -0
  97. package/dist/environment/declaration.js +39 -0
  98. package/dist/environment/destructive-composition.d.ts +29 -0
  99. package/dist/environment/destructive-composition.js +333 -0
  100. package/dist/environment/docker-daemon.d.ts +30 -0
  101. package/dist/environment/docker-daemon.js +78 -0
  102. package/dist/environment/docker-endpoint.d.ts +10 -0
  103. package/dist/environment/docker-endpoint.js +19 -0
  104. package/dist/environment/env-schema-observation.d.ts +46 -0
  105. package/dist/environment/env-schema-observation.js +241 -0
  106. package/dist/environment/evidence.d.ts +136 -0
  107. package/dist/environment/evidence.js +437 -0
  108. package/dist/environment/executable.d.ts +38 -0
  109. package/dist/environment/executable.js +190 -0
  110. package/dist/environment/generated-application/authorities.d.ts +5 -0
  111. package/dist/environment/generated-application/authorities.js +61 -0
  112. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  113. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  114. package/dist/environment/generated-application/check.d.ts +13 -0
  115. package/dist/environment/generated-application/check.js +191 -0
  116. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  117. package/dist/environment/generated-application/custom-surface.js +289 -0
  118. package/dist/environment/generated-application/prepare.d.ts +29 -0
  119. package/dist/environment/generated-application/prepare.js +278 -0
  120. package/dist/environment/generated-application/publication.d.ts +33 -0
  121. package/dist/environment/generated-application/publication.js +547 -0
  122. package/dist/environment/generated-application/shared.d.ts +492 -0
  123. package/dist/environment/generated-application/shared.js +354 -0
  124. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  125. package/dist/environment/generated-application/surface-observation.js +69 -0
  126. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  127. package/dist/environment/generated-application/surface-outputs.js +400 -0
  128. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  129. package/dist/environment/generated-application/surfaces.js +704 -0
  130. package/dist/environment/generated-application/sync.d.ts +6 -0
  131. package/dist/environment/generated-application/sync.js +124 -0
  132. package/dist/environment/git-observation.d.ts +202 -0
  133. package/dist/environment/git-observation.js +553 -0
  134. package/dist/environment/health-composition.d.ts +12 -0
  135. package/dist/environment/health-composition.js +645 -0
  136. package/dist/environment/host-observation.d.ts +56 -0
  137. package/dist/environment/host-observation.js +52 -0
  138. package/dist/environment/identity.d.ts +48 -0
  139. package/dist/environment/identity.js +85 -0
  140. package/dist/environment/init-application.d.ts +333 -0
  141. package/dist/environment/init-application.js +726 -0
  142. package/dist/environment/install-policy-observation.d.ts +71 -0
  143. package/dist/environment/install-policy-observation.js +284 -0
  144. package/dist/environment/install-policy.d.ts +48 -0
  145. package/dist/environment/install-policy.js +96 -0
  146. package/dist/environment/instance.d.ts +33 -0
  147. package/dist/environment/instance.js +122 -0
  148. package/dist/environment/instruction-pointer.d.ts +19 -0
  149. package/dist/environment/instruction-pointer.js +144 -0
  150. package/dist/environment/lifecycle-composition.d.ts +31 -0
  151. package/dist/environment/lifecycle-composition.js +742 -0
  152. package/dist/environment/lifecycle-stage.d.ts +120 -0
  153. package/dist/environment/lifecycle-stage.js +408 -0
  154. package/dist/environment/lifecycle.d.ts +75 -0
  155. package/dist/environment/lifecycle.js +354 -0
  156. package/dist/environment/logs.d.ts +132 -0
  157. package/dist/environment/logs.js +519 -0
  158. package/dist/environment/map-limit.d.ts +2 -0
  159. package/dist/environment/map-limit.js +13 -0
  160. package/dist/environment/mcp-observation.d.ts +9 -0
  161. package/dist/environment/mcp-observation.js +46 -0
  162. package/dist/environment/observations.d.ts +46 -0
  163. package/dist/environment/observations.js +346 -0
  164. package/dist/environment/operator-application.d.ts +79 -0
  165. package/dist/environment/operator-application.js +197 -0
  166. package/dist/environment/ports.d.ts +182 -0
  167. package/dist/environment/ports.js +1205 -0
  168. package/dist/environment/posture-observation.d.ts +68 -0
  169. package/dist/environment/posture-observation.js +253 -0
  170. package/dist/environment/presence.d.ts +4 -0
  171. package/dist/environment/presence.js +26 -0
  172. package/dist/environment/read-composition.d.ts +32 -0
  173. package/dist/environment/read-composition.js +397 -0
  174. package/dist/environment/readiness-composition.d.ts +57 -0
  175. package/dist/environment/readiness-composition.js +151 -0
  176. package/dist/environment/reclaim.d.ts +370 -0
  177. package/dist/environment/reclaim.js +1940 -0
  178. package/dist/environment/repository-identity.d.ts +13 -0
  179. package/dist/environment/repository-identity.js +108 -0
  180. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  181. package/dist/environment/runtime-measurement-record.js +111 -0
  182. package/dist/environment/runtime-measurement.d.ts +27 -0
  183. package/dist/environment/runtime-measurement.js +224 -0
  184. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  185. package/dist/environment/runtime-probe-residue.js +148 -0
  186. package/dist/environment/runtime-probe.d.ts +72 -0
  187. package/dist/environment/runtime-probe.js +725 -0
  188. package/dist/environment/runtime.d.ts +551 -0
  189. package/dist/environment/runtime.js +2405 -0
  190. package/dist/environment/seed.d.ts +16 -0
  191. package/dist/environment/seed.js +43 -0
  192. package/dist/environment/selftest-application.d.ts +86 -0
  193. package/dist/environment/selftest-application.js +713 -0
  194. package/dist/environment/sensitive.d.ts +3 -0
  195. package/dist/environment/sensitive.js +48 -0
  196. package/dist/environment/served-checkout-grants.d.ts +53 -0
  197. package/dist/environment/served-checkout-grants.js +182 -0
  198. package/dist/environment/setup-observation.d.ts +62 -0
  199. package/dist/environment/setup-observation.js +1473 -0
  200. package/dist/environment/setup.d.ts +43 -0
  201. package/dist/environment/setup.js +176 -0
  202. package/dist/environment/sibling-checkouts.d.ts +119 -0
  203. package/dist/environment/sibling-checkouts.js +254 -0
  204. package/dist/environment/sibling-instructions.d.ts +33 -0
  205. package/dist/environment/sibling-instructions.js +224 -0
  206. package/dist/environment/sibling-observation.d.ts +27 -0
  207. package/dist/environment/sibling-observation.js +45 -0
  208. package/dist/environment/sibling-pull.d.ts +23 -0
  209. package/dist/environment/sibling-pull.js +95 -0
  210. package/dist/environment/sibling-registration.d.ts +45 -0
  211. package/dist/environment/sibling-registration.js +538 -0
  212. package/dist/environment/stack-inputs.d.ts +18 -0
  213. package/dist/environment/stack-inputs.js +210 -0
  214. package/dist/environment/stack-inventory-prune.d.ts +31 -0
  215. package/dist/environment/stack-inventory-prune.js +129 -0
  216. package/dist/environment/stack-inventory-report.d.ts +44 -0
  217. package/dist/environment/stack-inventory-report.js +78 -0
  218. package/dist/environment/stack-inventory.d.ts +94 -0
  219. package/dist/environment/stack-inventory.js +418 -0
  220. package/dist/environment/state.d.ts +410 -0
  221. package/dist/environment/state.js +2153 -0
  222. package/dist/environment/templates.d.ts +140 -0
  223. package/dist/environment/templates.js +685 -0
  224. package/dist/environment/tool-registration.d.ts +9 -0
  225. package/dist/environment/tool-registration.js +92 -0
  226. package/dist/environment/upgrade-application.d.ts +19 -0
  227. package/dist/environment/upgrade-application.js +175 -0
  228. package/dist/environment-exec/execute.d.ts +105 -0
  229. package/dist/environment-exec/execute.js +1179 -0
  230. package/dist/environment-exec/libexec-program.d.ts +6 -0
  231. package/dist/environment-exec/libexec-program.js +22 -0
  232. package/dist/environment-exec/verified-digest.d.ts +4 -0
  233. package/dist/environment-exec/verified-digest.js +16 -0
  234. package/dist/exact-keys.d.ts +7 -0
  235. package/dist/exact-keys.js +15 -0
  236. package/dist/fix-catalogue.d.ts +32 -0
  237. package/dist/fix-catalogue.js +60 -0
  238. package/dist/freshness.d.ts +29 -0
  239. package/dist/freshness.js +75 -0
  240. package/dist/generated-environment/adopt.d.ts +16 -0
  241. package/dist/generated-environment/adopt.js +271 -0
  242. package/dist/generated-environment/command.d.ts +47 -0
  243. package/dist/generated-environment/command.js +75 -0
  244. package/dist/generated-environment/transaction.d.ts +186 -0
  245. package/dist/generated-environment/transaction.js +1770 -0
  246. package/dist/grading/evaluate.d.ts +71 -0
  247. package/dist/grading/evaluate.js +403 -0
  248. package/dist/grading/grade.d.ts +33 -0
  249. package/dist/grading/grade.js +57 -0
  250. package/dist/guards/answer-confirmation.d.ts +41 -0
  251. package/dist/guards/answer-confirmation.js +109 -0
  252. package/dist/guards/evaluator.d.ts +130 -0
  253. package/dist/guards/evaluator.js +1258 -0
  254. package/dist/guards/harness-adapter.d.ts +17 -0
  255. package/dist/guards/harness-adapter.js +57 -0
  256. package/dist/guards/host.d.ts +8 -0
  257. package/dist/guards/host.js +10 -0
  258. package/dist/guards/invocation-parser.d.ts +66 -0
  259. package/dist/guards/invocation-parser.js +595 -0
  260. package/dist/guards/local-identity.d.ts +44 -0
  261. package/dist/guards/local-identity.js +129 -0
  262. package/dist/guards/policy-change.d.ts +43 -0
  263. package/dist/guards/policy-change.js +159 -0
  264. package/dist/guards/policy-pin.d.ts +13 -0
  265. package/dist/guards/policy-pin.js +177 -0
  266. package/dist/guards/protected-locations.d.ts +62 -0
  267. package/dist/guards/protected-locations.js +461 -0
  268. package/dist/guards/read-only.d.ts +37 -0
  269. package/dist/guards/read-only.js +315 -0
  270. package/dist/init/command.d.ts +41 -0
  271. package/dist/init/command.js +330 -0
  272. package/dist/init/derive.d.ts +53 -0
  273. package/dist/init/derive.js +819 -0
  274. package/dist/init/draft.d.ts +140 -0
  275. package/dist/init/draft.js +1228 -0
  276. package/dist/init/render.d.ts +47 -0
  277. package/dist/init/render.js +104 -0
  278. package/dist/init/scaffold.d.ts +53 -0
  279. package/dist/init/scaffold.js +121 -0
  280. package/dist/invocation.d.ts +13 -0
  281. package/dist/invocation.js +20 -0
  282. package/dist/package-metadata.d.ts +9 -0
  283. package/dist/package-metadata.js +19 -0
  284. package/dist/readiness.d.ts +53 -0
  285. package/dist/readiness.js +88 -0
  286. package/dist/references/named.d.ts +24 -0
  287. package/dist/references/named.js +27 -0
  288. package/dist/refusal.d.ts +15 -0
  289. package/dist/refusal.js +7 -0
  290. package/dist/registry-derivation.d.ts +31 -0
  291. package/dist/registry-derivation.js +32 -0
  292. package/dist/registry-resolve/command.d.ts +47 -0
  293. package/dist/registry-resolve/command.js +226 -0
  294. package/dist/registry-resolve/registry.d.ts +68 -0
  295. package/dist/registry-resolve/registry.js +292 -0
  296. package/dist/registry-resolve/render-shell.d.ts +18 -0
  297. package/dist/registry-resolve/render-shell.js +36 -0
  298. package/dist/repair.d.ts +29 -0
  299. package/dist/repair.js +8 -0
  300. package/dist/repository-path.d.ts +9 -0
  301. package/dist/repository-path.js +16 -0
  302. package/dist/schema-json.d.ts +1 -0
  303. package/dist/schema-json.js +177 -0
  304. package/dist/schema.d.ts +1289 -0
  305. package/dist/schema.js +985 -0
  306. package/dist/setup/command.d.ts +4 -0
  307. package/dist/setup/command.js +4 -0
  308. package/dist/sha256.d.ts +1 -0
  309. package/dist/sha256.js +4 -0
  310. package/dist/surfaces/adoption.d.ts +41 -0
  311. package/dist/surfaces/adoption.js +217 -0
  312. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  313. package/dist/surfaces/consumer-instructions.js +43 -0
  314. package/dist/surfaces/contract.d.ts +42 -0
  315. package/dist/surfaces/contract.js +169 -0
  316. package/dist/surfaces/core-declarations.d.ts +24 -0
  317. package/dist/surfaces/core-declarations.js +79 -0
  318. package/dist/surfaces/development-markers.d.ts +1 -0
  319. package/dist/surfaces/development-markers.js +18 -0
  320. package/dist/surfaces/generator-adapter.d.ts +71 -0
  321. package/dist/surfaces/generator-adapter.js +809 -0
  322. package/dist/surfaces/harness-registry.d.ts +260 -0
  323. package/dist/surfaces/harness-registry.js +146 -0
  324. package/dist/surfaces/mcp.d.ts +145 -0
  325. package/dist/surfaces/mcp.js +349 -0
  326. package/dist/surfaces/merge.d.ts +30 -0
  327. package/dist/surfaces/merge.js +105 -0
  328. package/dist/surfaces/ownership.d.ts +190 -0
  329. package/dist/surfaces/ownership.js +795 -0
  330. package/dist/surfaces/projection.d.ts +136 -0
  331. package/dist/surfaces/projection.js +716 -0
  332. package/dist/surfaces/render.d.ts +79 -0
  333. package/dist/surfaces/render.js +636 -0
  334. package/dist/surfaces/restrictions.d.ts +130 -0
  335. package/dist/surfaces/restrictions.js +447 -0
  336. package/dist/surfaces/selftest.d.ts +162 -0
  337. package/dist/surfaces/selftest.js +393 -0
  338. package/dist/surfaces/skill-contract.d.ts +85 -0
  339. package/dist/surfaces/skill-contract.js +449 -0
  340. package/dist/surfaces/sync.d.ts +57 -0
  341. package/dist/surfaces/sync.js +164 -0
  342. package/dist/team-metadata.d.ts +41 -0
  343. package/dist/team-metadata.js +513 -0
  344. package/dist/testing/gates.d.ts +91 -0
  345. package/dist/testing/gates.js +182 -0
  346. package/dist/testing/hooks.d.ts +59 -0
  347. package/dist/testing/hooks.js +139 -0
  348. package/dist/testing/test-protection.d.ts +34 -0
  349. package/dist/testing/test-protection.js +137 -0
  350. package/dist/testing/verdict.d.ts +29 -0
  351. package/dist/testing/verdict.js +62 -0
  352. package/dist/testing/weakening.d.ts +15 -0
  353. package/dist/testing/weakening.js +30 -0
  354. package/dist/work-orders/application-closure.d.ts +7 -0
  355. package/dist/work-orders/application-closure.js +76 -0
  356. package/dist/work-orders/application-context.d.ts +162 -0
  357. package/dist/work-orders/application-context.js +137 -0
  358. package/dist/work-orders/application-decisions.d.ts +27 -0
  359. package/dist/work-orders/application-decisions.js +121 -0
  360. package/dist/work-orders/application-grading.d.ts +11 -0
  361. package/dist/work-orders/application-grading.js +212 -0
  362. package/dist/work-orders/application.d.ts +6 -0
  363. package/dist/work-orders/application.js +223 -0
  364. package/dist/work-orders/composition.d.ts +101 -0
  365. package/dist/work-orders/composition.js +1202 -0
  366. package/dist/work-orders/contract.d.ts +77 -0
  367. package/dist/work-orders/contract.js +140 -0
  368. package/dist/work-orders/decisions.d.ts +23 -0
  369. package/dist/work-orders/decisions.js +55 -0
  370. package/dist/work-orders/store.d.ts +207 -0
  371. package/dist/work-orders/store.js +291 -0
  372. package/docs/README.md +68 -0
  373. package/docs/architecture.md +136 -0
  374. package/docs/capability-matrix.md +58 -0
  375. package/docs/cli-reference.md +807 -0
  376. package/docs/command-list.md +62 -0
  377. package/docs/compose-patterns.md +99 -0
  378. package/docs/concepts.md +173 -0
  379. package/docs/configuration-essentials.md +177 -0
  380. package/docs/configuration-reference.md +636 -0
  381. package/docs/docs-check.md +76 -0
  382. package/docs/environment.md +948 -0
  383. package/docs/extending.md +43 -0
  384. package/docs/faq.md +113 -0
  385. package/docs/getting-started.md +692 -0
  386. package/docs/harness-surfaces.md +500 -0
  387. package/docs/joining-a-team.md +76 -0
  388. package/docs/release-policy.md +49 -0
  389. package/docs/safety-and-autonomy.md +463 -0
  390. package/docs/troubleshooting.md +382 -0
  391. package/docs/tutorial.md +171 -0
  392. package/docs/work-and-browser.md +420 -0
  393. package/libexec/browser-operation.cjs +270 -0
  394. package/libexec/fence-marker.cjs +101 -0
  395. package/libexec/fence-marker.d.cts +24 -0
  396. package/libexec/fenced-group-runner.cjs +77 -0
  397. package/libexec/fenced-supervisor.cjs +137 -0
  398. package/libexec/generated-filesystem.cjs +731 -0
  399. package/libexec/install-filesystem.cjs +225 -0
  400. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  401. package/libexec/rulesync-generator.cjs +743 -0
  402. package/libexec/surface-generator-worker.cjs +266 -0
  403. package/libexec/surface-output-reader.cjs +99 -0
  404. package/libexec/surface-scratch-cleanup.cjs +14 -0
  405. package/libexec/validate-harness-evidence.d.mts +183 -0
  406. package/libexec/validate-harness-evidence.mjs +1312 -0
  407. package/package.json +74 -4
  408. package/surfaces/README.md +33 -0
  409. package/surfaces/agents/evaluator.agent.md +76 -0
  410. package/surfaces/guards/bash-guard.sh +48 -0
  411. package/surfaces/guards/claude.adapter.json +25 -0
  412. package/surfaces/guards/codex.adapter.json +24 -0
  413. package/surfaces/guards/opencode.adapter.json +25 -0
  414. package/surfaces/references/browser.md +63 -0
  415. package/surfaces/references/test-data-and-personas.md +84 -0
  416. package/surfaces/references/testing-tiers.md +11 -0
  417. package/surfaces/skills/cockpit-browser.skill.md +59 -0
  418. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  419. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  420. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  421. package/surfaces/skills/cockpit-work.skill.md +34 -0
  422. package/surfaces/skills/collection.json +5 -0
  423. package/surfaces/skills/procedures/explain.md +66 -0
  424. package/surfaces/skills/procedures/fix.md +95 -0
  425. package/surfaces/skills/procedures/setup.md +452 -0
  426. package/surfaces/skills/procedures/work.md +134 -0
@@ -0,0 +1,664 @@
1
+ import { SURFACE_TARGETS } from './surfaces/contract.js';
2
+ import { isAbsolute, normalize, resolve } from 'node:path';
3
+ import { WORK_ORDER_ID } from './work-orders/contract.js';
4
+ import { snapshotJson } from './environment/contract.js';
5
+ import { innerCommand, WRAPPING_COMMANDS } from './guards/evaluator.js';
6
+ import { GUARD_HANDLER_ARGV } from './guards/harness-adapter.js';
7
+ import { GUARD_INPUT_LIMITS } from './guards/invocation-parser.js';
8
+ export function isRecordedAnswerField(value) {
9
+ return value.length <= 256 && /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(value);
10
+ }
11
+ /** The order a newcomer meets the commands in, as the README's "Commands at a glance" table names it. */
12
+ export const commandStages = Object.freeze([
13
+ { id: 'set-up', title: 'Set up' },
14
+ { id: 'generate-and-verify', title: 'Generate and verify' },
15
+ { id: 'run-the-stack', title: 'Run the stack' },
16
+ { id: 'diagnose', title: 'Diagnose' },
17
+ { id: 'test-and-grade', title: 'Test and grade' },
18
+ ]);
19
+ /**
20
+ * A group is a first command word whose commands are named `<group> <subcommand>`. Each subcommand
21
+ * keeps its own row, options and handler, so an option one subcommand reads is never accepted and
22
+ * ignored by another.
23
+ */
24
+ export const commandGroups = Object.freeze([
25
+ { name: 'work', summary: 'Accept, check, grade and close work orders' },
26
+ { name: 'browser', summary: 'Drive, inspect and capture the declared application in a browser' },
27
+ { name: 'docs', summary: 'Check living documents and decision records against the repositories' },
28
+ ]);
29
+ /**
30
+ * The public command an argv names, and the arguments after it. A group word takes the next word
31
+ * as its subcommand; `subcommand` is absent when none follows, so the caller can show the group.
32
+ */
33
+ export function commandSpelling(args) {
34
+ const [first, second, ...rest] = args;
35
+ const group = commandGroups.find(row => row.name === first);
36
+ if (!group)
37
+ return { name: first, rest: second === undefined ? [] : [second, ...rest] };
38
+ if (second === undefined || second.startsWith('-'))
39
+ return { name: undefined, group: group.name, rest: second === undefined ? [] : [second, ...rest] };
40
+ return { name: `${group.name} ${second}`, group: group.name, rest };
41
+ }
42
+ const FLAG_DESCRIPTIONS = Object.freeze({
43
+ 'accept-defaults': 'Accept the displayed defaults with their exact SHA-256 digest',
44
+ 'accept-preview': 'Accept the displayed init preview with its exact SHA-256 digest',
45
+ 'accept-reset': 'Accept the displayed answer reset with its exact SHA-256 digest',
46
+ abandon: 'Close the order as abandoned; --note says why',
47
+ clear: 'Serve the sibling from its declared checkout again',
48
+ adopt: 'Move one existing harness settings path under cockpit ownership',
49
+ adoption: 'Path to the legacy adoption shell data',
50
+ answers: 'Path to the init answers file',
51
+ ask: 'Record the note as an open question, which blocks the order until it is answered',
52
+ apply: 'Apply the displayed reversible operation',
53
+ command: 'With --scaffold-compose: the command that starts the app, as words separated by spaces',
54
+ config: 'Path to cockpit.toml',
55
+ create: 'Create a new local sibling repository',
56
+ decision: 'With work note: record the text as a decision the order made, resolved with work decide before signoff. With work decide: number of the decision to resolve, as work status lists it',
57
+ criteria: 'Path to a file whose YAML front-matter lists the criteria to grade',
58
+ dir: 'Directory for the sibling checkout',
59
+ evaluator: 'Grade with only this agent tool (codexcli, claudecode or opencode), with no fallback to another',
60
+ 'dry-run': 'Preview without writing or installing anything',
61
+ follow: 'Continue collecting logs until cancelled',
62
+ from: 'Path to an existing local repository',
63
+ gate: 'Declared product gate to run',
64
+ image: 'Local container image the runtime measurement probe runs; it is never pulled',
65
+ 'instruction-pointer': 'Offer a marked AGENTS.md link for this separate sibling',
66
+ interactive: 'Ask for explicit approval in a terminal',
67
+ json: 'Emit one machine-readable JSON response',
68
+ mode: 'Declared gate execution mode',
69
+ mount: 'How the stack uses the sibling: always, none or profile:<name>',
70
+ note: 'What was checked, or what was and was not done',
71
+ partial: 'Close the order as partly done; --note says what is left',
72
+ port: 'With --scaffold-compose: the port the app listens on inside its container',
73
+ profile: 'Compose profile to activate beside environment.profiles; repeat it for several',
74
+ 'probe-specifications': 'Path to the harness probe specifications; with --write-probe-specifications, the earlier file whose filled rows for unchanged cells carry forward',
75
+ purpose: 'Short purpose of the sibling repository',
76
+ question: 'Number of the open question to answer, as work status lists it',
77
+ registry: 'Path to the legacy registry input',
78
+ reason: 'With --none: why the decision is not a lasting one',
79
+ record: 'Write a new decision record under [docs] decisions for the decision',
80
+ 'record-file': 'An existing decision record under [docs] decisions that cites the order',
81
+ supersedes: 'With --record: the number of the decision record the new one supersedes, which is marked superseded',
82
+ none: 'Resolve the decision as not lasting; --reason says why',
83
+ 'reset-answer': 'Preview clearing one recorded init answer field',
84
+ remote: 'Repository remote URL recorded for the sibling',
85
+ role: 'Declared browser role',
86
+ 'fail-on-findings': 'Exit 2 when a route has console errors, uncaught errors or failed requests, for a criterion that wants a clean route',
87
+ route: 'Declared browser route to visit, by name or path; every declared route when omitted',
88
+ 'scaffold-compose': 'Write a starting compose.yml for the app in this directory, relative to the root, once; it refuses when a Compose file exists',
89
+ select: 'Optional subset of the gate to run',
90
+ service: 'Declared Compose service the command acts on',
91
+ 'siblings-root': 'Root directory containing sibling checkouts',
92
+ since: 'Collect logs no older than this duration',
93
+ tail: 'Maximum number of log records to collect',
94
+ target: 'Agent tool to set up; comma-separate several (claudecode,opencode)',
95
+ text: 'Text of the note or answer',
96
+ var: 'Environment variable mapping for the sibling',
97
+ verdict: 'Under [work] grading = "self": met or unmet',
98
+ with: 'With --scaffold-compose: backing services to add, from postgres and redis, separated by commas',
99
+ 'work-order': 'Work-order identifier',
100
+ 'write-probe-specifications': 'Write a probe-specification file for the current manifest to this new path, with the probe fields left for you',
101
+ 'write-instruction-pointers': 'Write the reviewed instruction links into opted-in sibling guides',
102
+ 'write-instruction-files': 'Write the reviewed AGENTS.md and CLAUDE.md changes that let every agent tool load a sibling\'s instructions',
103
+ });
104
+ export function describePublicOption(name) {
105
+ const description = FLAG_DESCRIPTIONS[name];
106
+ if (!description)
107
+ throw new Error(`Public option --${name} has no help description.`);
108
+ return description;
109
+ }
110
+ // Mirrors EvaluatorTool in schema.ts, which this data table may not import.
111
+ const EVALUATOR_TOOLS = ['codexcli', 'claudecode', 'opencode'];
112
+ // Mirrors UNCONFIRMABLE_ANSWERS in schema.ts. Every answer is admitted, so confirm itself names the
113
+ // person an answer needs instead of the guard refusing it unexplained.
114
+ const UNCONFIRMED_ANSWER_WORDS = Object.freeze(['cockpit.name', 'sibling', 'estate.runnable', 'ports.bases',
115
+ 'environment.install_policies', 'environment.test_data', 'environment.compose_files', 'environment.machine', 'environment.posture',
116
+ 'testing', 'guards']);
117
+ // A declared route's name or its path; the command checks it against the declared routes.
118
+ const ROUTE_SELECTOR = /^(?:[a-z0-9][a-z0-9_-]{0,63}|\/(?!\/)[^\s#\x00-\x1f\x7f]{0,511})$/;
119
+ const EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
120
+ const flag = Object.freeze({ kind: 'flag', role: 'boolean', repetition: 'singleton' });
121
+ function value(role, required = false) {
122
+ return { kind: 'value', role, repetition: 'singleton', ...(required ? { required: true } : {}) };
123
+ }
124
+ const str = (agent, fallback) => ({ type: 'string', ...fallback === undefined ? {} : { default: fallback }, ...agent ? { agent } : {} });
125
+ const bool = (agent) => ({ type: 'boolean', default: false, ...agent ? { agent } : {} });
126
+ const configFlag = str(value('resolved-config', true));
127
+ const jsonFlag = bool(flag);
128
+ const configured = { config: configFlag, json: jsonFlag };
129
+ const workOrderFlag = str(value('identifier', true));
130
+ const profileFlag = { type: 'string', multiple: true, agent: { kind: 'value', role: 'compose-profile', repetition: 'repeated' } };
131
+ const serviceFlag = str(value('declared-service', true));
132
+ // The flags and their roles are filled in from `flags` when the table is built.
133
+ function ordinary(command, extra = {}) {
134
+ return { availability: 'available', executableRole: 'installed-cockpit', command,
135
+ positional: { min: 0, max: 0, roles: [] }, options: {},
136
+ cwdRole: 'canonical-root', configuration: 'resolved',
137
+ forbidden: { presence: [] }, ...extra };
138
+ }
139
+ function unavailable(reason) { return { availability: 'unavailable', reason }; }
140
+ const browserAdmission = Object.freeze({ requires: 'browser-preapproval' });
141
+ // --interactive asks for a person at a terminal, so an agent invocation never carries it.
142
+ const personFlag = Object.freeze({ forbidden: Object.freeze({ presence: Object.freeze(['interactive']) }) });
143
+ export function isWorkHandler(value) {
144
+ return value.startsWith('work-') || value.startsWith('browser-') || value === 'test-run' || value === 'grade-criteria';
145
+ }
146
+ const sources = [
147
+ {
148
+ name: 'init',
149
+ stage: 'set-up',
150
+ summary: 'Preview, then accept, a first cockpit setup for one or more agent tools',
151
+ usage: '[<root>] [options]',
152
+ handler: 'init',
153
+ visibility: 'public',
154
+ ordinary: ordinary('init', {
155
+ configuration: 'none',
156
+ positional: Object.freeze({ min: 1, max: 1, roles: Object.freeze(['confined-root']) }),
157
+ forbidden: Object.freeze({ presence: Object.freeze(['interactive', 'answers', 'accept-preview', 'accept-defaults']) }),
158
+ }),
159
+ flags: { target: str(value('surface-target', true)), registry: str(value('confined-path')),
160
+ interactive: bool(), json: jsonFlag,
161
+ answers: str(), 'accept-preview': str(), 'accept-defaults': str(),
162
+ 'reset-answer': str(value('field')), 'accept-reset': str(value('sha256')),
163
+ 'scaffold-compose': str(), command: str(), port: str(), with: str() },
164
+ },
165
+ {
166
+ name: 'setup',
167
+ stage: 'set-up',
168
+ summary: 'Clone or locate the declared sibling repositories and link them',
169
+ handler: 'setup',
170
+ visibility: 'public',
171
+ ordinary: ordinary('setup', { forbidden: Object.freeze({ presence: Object.freeze(['write-instruction-pointers', 'write-instruction-files']) }) }),
172
+ flags: { config: configFlag, 'write-instruction-pointers': bool(), 'write-instruction-files': bool(), json: jsonFlag },
173
+ },
174
+ {
175
+ name: 'use',
176
+ stage: 'set-up',
177
+ summary: 'Serve a sibling from another of its checkouts, such as a worktree',
178
+ usage: '<slug> <checkout> | <slug> --clear [options]',
179
+ handler: 'use',
180
+ visibility: 'public',
181
+ // The checkout must already lie inside a directory the guard lets the agent write, so an agent
182
+ // can point the stack at its own worktree but never widen its reach to a new directory.
183
+ ordinary: ordinary('use', { positional: Object.freeze({ min: 1, max: 2, roles: Object.freeze(['slug', 'confined-root']) }) }),
184
+ flags: { config: configFlag, clear: bool(flag), json: jsonFlag },
185
+ },
186
+ {
187
+ name: 'pull',
188
+ stage: 'set-up',
189
+ summary: 'Fast-forward the cockpit and its siblings to their upstreams',
190
+ handler: 'pull',
191
+ visibility: 'public',
192
+ // Fetch and a fast-forward only: it never merges, rebases, stashes or touches a modified checkout.
193
+ ordinary: ordinary('pull'),
194
+ flags: configured,
195
+ },
196
+ {
197
+ name: 'sibling',
198
+ stage: 'set-up',
199
+ summary: 'Add or remove a repository in cockpit.toml (sibling add, sibling remove)',
200
+ usage: 'add|remove <slug> [options]',
201
+ handler: 'sibling',
202
+ visibility: 'public',
203
+ ordinary: unavailable('sibling add and remove rewrite cockpit.toml'),
204
+ flags: { config: str(), create: bool(), from: str(), remote: str(), dir: str(), var: str(), purpose: str(), mount: str(),
205
+ 'instruction-pointer': bool(), json: bool() },
206
+ },
207
+ {
208
+ name: 'tool',
209
+ stage: 'set-up',
210
+ summary: 'Add or remove an agent tool in cockpit.toml (tool add, tool remove)',
211
+ usage: `add|remove <${SURFACE_TARGETS.join('|')}> [options]`,
212
+ handler: 'tool',
213
+ visibility: 'public',
214
+ // tool add edits only [surfaces] tools, and an added tool never widens what agents may do, so the
215
+ // agent syncs it as [autonomy] sync allows. tool remove deletes that tool's guard, so it is a person's.
216
+ ordinary: ordinary('tool', { positional: Object.freeze({ min: 2, max: 2, roles: Object.freeze(['operation', 'surface-target']),
217
+ operations: Object.freeze(['add']) }) }),
218
+ flags: { config: configFlag, 'dry-run': bool(flag), json: jsonFlag },
219
+ },
220
+ {
221
+ name: 'confirm',
222
+ stage: 'set-up',
223
+ summary: 'Confirm an init answer no person stated by running the observation that verifies it',
224
+ usage: '<answer> [options]',
225
+ handler: 'confirm',
226
+ visibility: 'public',
227
+ // It removes an entry from unconfirmed only after an observation of this machine and the running
228
+ // stack passes, and records that observation where only cockpit writes; the guard then treats the
229
+ // removal as non-widening. It refuses the answers a person must confirm.
230
+ ordinary: ordinary('confirm', { positional: Object.freeze({ min: 1, max: 1, roles: Object.freeze(['operation']),
231
+ operations: UNCONFIRMED_ANSWER_WORDS }) }),
232
+ flags: configured,
233
+ },
234
+ {
235
+ name: 'registry-resolve',
236
+ stage: 'set-up',
237
+ summary: 'Turn a validated shell registry into data shell scripts can source',
238
+ handler: 'registry-resolve',
239
+ visibility: 'public',
240
+ ordinary: ordinary('registry-resolve', { configuration: 'none' }),
241
+ flags: { registry: str(value('confined-path', true)), 'siblings-root': str(value('confined-path', true)), adoption: str(value('confined-path', true)), json: jsonFlag },
242
+ },
243
+ {
244
+ name: 'upgrade',
245
+ stage: 'set-up',
246
+ summary: 'Move the exact cockpit pin to a newer version and re-run sync',
247
+ usage: '[<version>] [options]',
248
+ handler: 'upgrade',
249
+ visibility: 'public',
250
+ // The install is exact and its sync child decides as sync does, so an agent runs the real upgrade
251
+ // exactly where [autonomy] sync lets it sync; the version is the one positional.
252
+ ordinary: ordinary('upgrade', {
253
+ positional: Object.freeze({ min: 0, max: 1, roles: Object.freeze(['version']) }), requires: 'autonomy-sync'
254
+ }),
255
+ flags: { config: configFlag, 'dry-run': bool(flag) },
256
+ },
257
+ {
258
+ name: 'sync',
259
+ stage: 'generate-and-verify',
260
+ summary: 'Write the Compose ownership override and agent files from cockpit.toml',
261
+ handler: 'sync',
262
+ visibility: 'public',
263
+ // Adoption rewrites cockpit.toml and moves a team's harness settings, so it stays a person's. A
264
+ // real sync rewrites the guard's own files from a configuration the guard already pins and
265
+ // holds every call after an edit to, so it is an agent's only where [autonomy] sync = "agent".
266
+ ordinary: ordinary('sync', { forbidden: Object.freeze({ presence: Object.freeze(['interactive', 'adopt']) }), requires: 'autonomy-sync',
267
+ admittedWhileUnsynced: true }),
268
+ flags: { config: configFlag, 'dry-run': bool(flag), adopt: { type: 'string', multiple: true }, interactive: bool(), json: jsonFlag },
269
+ },
270
+ {
271
+ name: 'check',
272
+ stage: 'generate-and-verify',
273
+ summary: 'Report generated files that no longer match cockpit.toml',
274
+ handler: 'check',
275
+ visibility: 'public',
276
+ ordinary: ordinary('check', { admittedWhileUnsynced: true }),
277
+ flags: configured,
278
+ },
279
+ {
280
+ name: 'selftest',
281
+ stage: 'generate-and-verify',
282
+ summary: 'Test whether agent tools enforce the generated controls',
283
+ handler: 'selftest',
284
+ visibility: 'public',
285
+ ordinary: ordinary('selftest', { forbidden: Object.freeze({ presence: Object.freeze(['interactive']) }) }),
286
+ flags: { config: configFlag, 'probe-specifications': str(value('confined-path', true)), 'write-probe-specifications': str(),
287
+ interactive: bool(), json: jsonFlag },
288
+ },
289
+ {
290
+ name: 'runtime',
291
+ stage: 'run-the-stack',
292
+ summary: 'Measure the local container runtime (runtime measure) and record the result for this machine',
293
+ usage: 'measure [options]',
294
+ handler: 'runtime',
295
+ visibility: 'public',
296
+ // The probe runs a disposable stack under its own project, cleans it up, and records the result
297
+ // only in this machine's ignored state, so an agent can measure before its first `up`.
298
+ ordinary: ordinary('runtime', {
299
+ positional: Object.freeze({ min: 1, max: 1, roles: Object.freeze(['operation']), operations: Object.freeze(['measure']) }),
300
+ }),
301
+ flags: { config: configFlag, image: str(value('local-image', true)), json: jsonFlag },
302
+ },
303
+ {
304
+ name: 'up',
305
+ stage: 'run-the-stack',
306
+ summary: 'Start or resume the declared environment',
307
+ handler: 'up',
308
+ visibility: 'public',
309
+ ordinary: ordinary('up'),
310
+ flags: { ...configured, profile: profileFlag },
311
+ },
312
+ {
313
+ name: 'preflight',
314
+ stage: 'run-the-stack',
315
+ summary: 'Check that the environment is healthy and ready for work',
316
+ handler: 'preflight',
317
+ visibility: 'public',
318
+ ordinary: ordinary('preflight'),
319
+ flags: configured,
320
+ },
321
+ {
322
+ name: 'session',
323
+ stage: 'run-the-stack',
324
+ summary: 'Keep the environment available for one command, then release it',
325
+ usage: '[options] -- <command> [args...]',
326
+ handler: 'session',
327
+ visibility: 'public',
328
+ // The guard admits `session --config <file> [--profile <name>]... -- <command>` only when it
329
+ // admits the inner command.
330
+ ordinary: unavailable('session wraps an arbitrary foreground command'),
331
+ flags: { config: str(), profile: { type: 'string', multiple: true } },
332
+ },
333
+ {
334
+ name: 'logs',
335
+ stage: 'run-the-stack',
336
+ summary: 'Show recent logs for one declared service',
337
+ handler: 'logs',
338
+ visibility: 'public',
339
+ ordinary: ordinary('logs'),
340
+ flags: { config: configFlag, service: serviceFlag, tail: str(value('tail'), '100'), since: str(value('since'), '15m'),
341
+ follow: bool(flag), json: jsonFlag },
342
+ },
343
+ {
344
+ name: 'ps',
345
+ stage: 'run-the-stack',
346
+ summary: 'Show the state and health of each service in this checkout\'s stack',
347
+ handler: 'ps',
348
+ visibility: 'public',
349
+ ordinary: ordinary('ps'),
350
+ flags: configured,
351
+ },
352
+ // The service verbs act only on this checkout's own leased stack, as up does, so an agent runs
353
+ // them as it runs up; reset, which deletes data, stays gated.
354
+ {
355
+ name: 'restart',
356
+ stage: 'run-the-stack',
357
+ summary: 'Restart one service of this checkout\'s stack and wait until it is healthy',
358
+ handler: 'restart',
359
+ visibility: 'public',
360
+ ordinary: ordinary('restart'),
361
+ flags: { ...configured, service: serviceFlag },
362
+ },
363
+ {
364
+ name: 'rebuild',
365
+ stage: 'run-the-stack',
366
+ summary: 'Rebuild one service\'s image, replace its container and wait until it is healthy',
367
+ handler: 'rebuild',
368
+ visibility: 'public',
369
+ ordinary: ordinary('rebuild'),
370
+ flags: { ...configured, service: serviceFlag },
371
+ },
372
+ {
373
+ name: 'exec',
374
+ stage: 'run-the-stack',
375
+ summary: 'Run a command inside one service\'s container of this checkout\'s stack',
376
+ usage: '--service <name> [options] -- <command> [args...]',
377
+ handler: 'exec',
378
+ visibility: 'public',
379
+ // The grammar admits the spelling; the evaluator judges the container command.
380
+ ordinary: ordinary('exec'),
381
+ flags: { config: configFlag, service: serviceFlag },
382
+ },
383
+ {
384
+ name: 'sleep',
385
+ stage: 'run-the-stack',
386
+ summary: 'Release this checkout and stop the stack if no other holder remains',
387
+ handler: 'sleep',
388
+ visibility: 'public',
389
+ ordinary: ordinary('sleep'),
390
+ flags: configured,
391
+ },
392
+ {
393
+ name: 'down',
394
+ stage: 'run-the-stack',
395
+ summary: 'Stop and remove the stack (on the active local Docker context)',
396
+ handler: 'down',
397
+ visibility: 'public',
398
+ // down removes only the containers and networks cockpit created; reset, which deletes data, stays out.
399
+ ordinary: ordinary('down'),
400
+ flags: configured,
401
+ },
402
+ {
403
+ name: 'reset',
404
+ stage: 'run-the-stack',
405
+ summary: 'Preview a reset, then approve it interactively or apply it as [autonomy] reset allows (on the active local Docker context)',
406
+ handler: 'reset',
407
+ visibility: 'public',
408
+ // Reset deletes stack data, so an agent applies one only where [autonomy] reset = "agent".
409
+ ordinary: ordinary('reset', { requires: 'autonomy-reset' }),
410
+ flags: { config: configFlag, interactive: bool(), apply: bool(Object.freeze({ ...flag, required: true })), json: jsonFlag },
411
+ },
412
+ {
413
+ name: 'stacks',
414
+ stage: 'run-the-stack',
415
+ summary: 'List every stack on this machine, or preview and remove those whose checkout is gone (stacks prune)',
416
+ usage: '[prune] [options]',
417
+ handler: 'stacks',
418
+ visibility: 'public',
419
+ // Listing and the prune preview change nothing. --apply removes only stacks whose checkout is
420
+ // gone; the command itself refuses one that deletes volumes unless [autonomy] reset allows it.
421
+ ordinary: ordinary('stacks', {
422
+ positional: Object.freeze({ min: 0, max: 1, roles: Object.freeze(['operation']), operations: Object.freeze(['prune']) }),
423
+ ...personFlag,
424
+ }),
425
+ flags: { config: configFlag, apply: bool(flag), interactive: bool(), json: jsonFlag },
426
+ },
427
+ {
428
+ name: 'doctor',
429
+ stage: 'diagnose',
430
+ summary: 'Report environment problems without changing anything',
431
+ handler: 'doctor',
432
+ visibility: 'public',
433
+ ordinary: ordinary('doctor'),
434
+ flags: configured,
435
+ },
436
+ // A gate runs only the stages a person declared in the reviewed cockpit.toml, which is the trust
437
+ // `up` already has; a selector reaches a stage only through the arguments that stage declares.
438
+ { name: 'test-run', handler: 'test-run', stage: 'test-and-grade', summary: 'Run a declared product test gate and report which tests it selected', visibility: 'public',
439
+ ordinary: ordinary('test-run'),
440
+ flags: { ...configured, gate: str(value('declared-gate', true)), mode: str(value('gate-mode'), 'canonical'), select: str(value('gate-selector')) } },
441
+ // A gated step passes for the agent only where [work.gates] says agent; under person the command
442
+ // waits for the digest a person types after --interactive, which no agent invocation carries.
443
+ { name: 'work status', handler: 'work-status', stage: 'test-and-grade', summary: 'Report each work order, its state and how it closed', visibility: 'public',
444
+ ordinary: ordinary('work-status'),
445
+ flags: { ...configured, 'work-order': str(value('identifier')) } },
446
+ { name: 'work accept', handler: 'work-accept', stage: 'test-and-grade', summary: 'Accept a work order, or a change to an accepted one, as [work.gates] allows', visibility: 'public',
447
+ ordinary: ordinary('work-accept', personFlag),
448
+ flags: { ...configured, 'work-order': workOrderFlag, interactive: bool() } },
449
+ { name: 'work check', handler: 'work-check', stage: 'test-and-grade', summary: "Run every criterion's check and the order's gates", visibility: 'public',
450
+ ordinary: ordinary('work-check'),
451
+ flags: { ...configured, 'work-order': workOrderFlag } },
452
+ { name: 'work note', handler: 'work-note', stage: 'test-and-grade', summary: 'Record a note on an order, an open question with --ask, or a decision with --decision', visibility: 'public',
453
+ ordinary: ordinary('work-note'),
454
+ flags: { ...configured, 'work-order': workOrderFlag, text: str(value('label', true)), ask: bool(flag), decision: bool(flag) } },
455
+ // Resolving a decision passes no gate of its own: a --none resolution is shown in the signoff prompt, which the signoff gate covers.
456
+ { name: 'work decide', handler: 'work-decide', stage: 'test-and-grade', summary: 'Resolve a recorded decision: write or cite its decision record, or say it is not lasting', visibility: 'public',
457
+ ordinary: ordinary('work-decide'),
458
+ flags: { ...configured, 'work-order': workOrderFlag, decision: str(value('tail', true)), record: bool(flag), 'record-file': str(value('label')),
459
+ supersedes: str(value('label')), none: bool(flag), reason: str(value('label')) } },
460
+ { name: 'work answer', handler: 'work-answer', stage: 'test-and-grade', summary: 'Answer an open question on an order, as [work.gates] allows', visibility: 'public',
461
+ ordinary: ordinary('work-answer', personFlag),
462
+ flags: { ...configured, 'work-order': workOrderFlag, question: str(value('tail', true)), text: str(value('label', true)), interactive: bool() } },
463
+ { name: 'work acknowledge', handler: 'work-acknowledge', stage: 'test-and-grade', summary: 'Acknowledge that an order weakened tests, as [work.gates] allows', visibility: 'public',
464
+ ordinary: ordinary('work-acknowledge', personFlag),
465
+ flags: { ...configured, 'work-order': workOrderFlag, note: str(value('label', true)), interactive: bool() } },
466
+ // Grading hands the order to an evaluator running apart from the agent that did the work, unless
467
+ // [work] grading names checks or self.
468
+ { name: 'work grade', handler: 'work-grade', stage: 'test-and-grade', summary: 'Grade an accepted order the way [work] grading says', visibility: 'public',
469
+ ordinary: ordinary('work-grade'),
470
+ flags: { ...configured, 'work-order': workOrderFlag, verdict: str(value('label')), note: str(value('label')), evaluator: str(value('evaluator-tool')) } },
471
+ { name: 'work signoff', handler: 'work-signoff', stage: 'test-and-grade', summary: 'Close a graded order as done, or any open one as partial or abandoned', visibility: 'public',
472
+ ordinary: ordinary('work-signoff', personFlag),
473
+ flags: { ...configured, 'work-order': workOrderFlag, partial: bool(flag), abandon: bool(flag), note: str(value('label')), interactive: bool() } },
474
+ // Inspection only reports the holder. --apply removes the lock only once the command has shown its
475
+ // holder gone where a pid probe cannot see: the lock's host and the containers cockpit labels.
476
+ { name: 'work recover', handler: 'work-recover', stage: 'test-and-grade', summary: "Inspect or remove a crashed command's work-order lock", visibility: 'public',
477
+ ordinary: ordinary('work-recover'),
478
+ flags: { ...configured, 'work-order': workOrderFlag, apply: bool(flag) } },
479
+ { name: 'work verify', handler: 'work-verify', stage: 'test-and-grade', summary: 'Check every record against its order; in CI, pass the steps gated on ci', visibility: 'public',
480
+ ordinary: ordinary('work-verify'),
481
+ flags: configured },
482
+ { name: 'grade', handler: 'grade-criteria', stage: 'test-and-grade', summary: 'Independently grade uncommitted work against a criteria file, with no work order', visibility: 'public',
483
+ ordinary: ordinary('grade-criteria'),
484
+ flags: { ...configured, criteria: str(value('confined-path', true)), evaluator: str(value('evaluator-tool')) } },
485
+ // Driving signs in and captures the application, so it runs unprompted only on the data posture a
486
+ // person wrote down as safe for that.
487
+ { name: 'browser drive', handler: 'browser-drive', stage: 'test-and-grade', summary: 'Drive the declared application browser surfaces', visibility: 'public',
488
+ ordinary: ordinary('browser-drive', browserAdmission),
489
+ // Findings are evidence by default; this makes them fail the command, for a criterion that wants a clean route.
490
+ flags: { ...configured, role: str(value('identifier')), route: str(value('route')), 'fail-on-findings': bool(flag) } },
491
+ { name: 'browser screenshot', handler: 'browser-screenshot', stage: 'test-and-grade', summary: 'Capture the declared application surfaces', visibility: 'public',
492
+ ordinary: ordinary('browser-screenshot', browserAdmission),
493
+ flags: { ...configured, role: str(value('identifier')), route: str(value('route')) } },
494
+ // Reads documents and runs read-only Git, like check; it writes nothing, so an agent runs it freely.
495
+ { name: 'docs check', handler: 'docs-check', stage: 'test-and-grade', summary: 'Report removed paths and make targets living docs still cite, and broken decision records', visibility: 'public',
496
+ ordinary: ordinary('docs-check'),
497
+ flags: configured },
498
+ ];
499
+ export const commands = sources.map(({ flags, ...command }) => {
500
+ const entries = Object.entries(flags);
501
+ return { ...command,
502
+ options: Object.fromEntries(entries.map(([name, { agent: _agent, ...option }]) => [name, option])),
503
+ ordinary: command.ordinary.availability === 'available'
504
+ ? { ...command.ordinary, options: Object.fromEntries(entries.flatMap(([name, { agent }]) => agent ? [[name, agent]] : [])) }
505
+ : command.ordinary };
506
+ });
507
+ export const internalCommands = Object.freeze([Object.freeze({
508
+ name: 'guard-evaluate', handler: 'guard-evaluate', visibility: 'internal',
509
+ ordinary: Object.freeze({ availability: 'unavailable', reason: 'the guard hook calls this internally; it is not a user command' }),
510
+ argv: GUARD_HANDLER_ARGV, options: Object.freeze({ harness: Object.freeze({ type: 'string' }) }),
511
+ })]);
512
+ const ORDINARY_GRANTS = ['browser-preapproval', 'autonomy-sync', 'autonomy-reset'];
513
+ // An image reference or ID as Docker spells it; the command itself refuses one that is not local.
514
+ const LOCAL_IMAGE = /^[A-Za-z0-9][A-Za-z0-9._/:@-]{0,255}$/;
515
+ // The spelling environment.profiles accepts.
516
+ const COMPOSE_PROFILE = /^[a-z0-9][a-z0-9_.-]{0,63}$/;
517
+ // A selected value that starts with `-` would reach the product's tool as an option of its own.
518
+ const GATE_SELECTOR = /^(?:spec|file|test|route):[^-]/;
519
+ function canonicalPath(path) {
520
+ return typeof path === 'string' && path !== '/' && isAbsolute(path) && normalize(path) === path
521
+ && !path.endsWith('/') && !/[\x00-\x1f\x7f-\x9f\\\uD800-\uDFFF]/u.test(path)
522
+ && Buffer.byteLength(path) <= GUARD_INPUT_LIMITS.pathBytes;
523
+ }
524
+ function record(value) {
525
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
526
+ }
527
+ /** The commands the guard admits while a non-widening edit of cockpit.toml waits for sync. */
528
+ export const UNSYNCED_ADMITTED_COMMANDS = Object.freeze(commands.flatMap(row => row.ordinary.availability === 'available' && row.ordinary.admittedWhileUnsynced ? [row.name] : []));
529
+ /** The same commands as prose, for the hold that names them: "cockpit sync and check". */
530
+ export const UNSYNCED_ADMITTED_PROSE = `cockpit ${new Intl.ListFormat('en', { type: 'conjunction' }).format(UNSYNCED_ADMITTED_COMMANDS)}`;
531
+ /**
532
+ * The inner command of an exact `session --config <configured file> -- <command…>` run from the
533
+ * cockpit root, or undefined. The guard admits the session only when it admits the inner command,
534
+ * so a session never runs what the agent could not run directly.
535
+ */
536
+ export function ordinarySessionCommand(argv, cwd, authority) {
537
+ const inner = innerCommand(argv);
538
+ if (argv[0] !== 'session' || !inner || cwd !== authority.canonicalRoot)
539
+ return undefined;
540
+ const options = argv.slice(1, argv.length - inner.words.length - 1);
541
+ let config;
542
+ for (let index = 0; index < options.length; index++) {
543
+ const [name, inline] = options[index].startsWith('--') && options[index].includes('=')
544
+ ? [options[index].slice(0, options[index].indexOf('=')), options[index].slice(options[index].indexOf('=') + 1)]
545
+ : [options[index], options[++index]];
546
+ if (name === '--config' && config === undefined && inline !== undefined)
547
+ config = inline;
548
+ else if (name !== '--profile' || inline === undefined || !authority.profiles.includes(inline))
549
+ return undefined;
550
+ }
551
+ return config && resolve(cwd, config) === authority.configPath ? inner.words : undefined;
552
+ }
553
+ /** Classify one exact candidate; the caller authenticates filesystem and executable authority. */
554
+ export function resolveOrdinaryInvocation(input, authority, inspectPath) {
555
+ const candidate = snapshotJson(input);
556
+ const captured = snapshotJson(authority);
557
+ if (!record(candidate)
558
+ || Object.keys(candidate).sort().join(',') !== 'argv,cwd,executable'
559
+ || !record(captured)
560
+ || Object.keys(captured).sort().join(',') !== 'allowedRoots,canonicalRoot,configPath,executable,gates,grants,profiles,services')
561
+ return undefined;
562
+ const source = captured;
563
+ if (!canonicalPath(source.executable) || !canonicalPath(source.canonicalRoot) || !canonicalPath(source.configPath)
564
+ || !Array.isArray(source.allowedRoots) || source.allowedRoots.length > GUARD_INPUT_LIMITS.paths
565
+ || !source.allowedRoots.every(canonicalPath) || !source.allowedRoots.includes(source.canonicalRoot)
566
+ || !Array.isArray(source.services) || !source.services.every(service => typeof service === 'string' && /^[a-z0-9][a-z0-9_.-]{0,127}$/.test(service))
567
+ || !Array.isArray(source.gates) || !source.gates.every(gate => typeof gate === 'string' && /^[a-z][a-z0-9-]{0,63}$/.test(gate))
568
+ || !Array.isArray(source.grants) || !source.grants.every(grant => ORDINARY_GRANTS.includes(grant))
569
+ || !Array.isArray(source.profiles) || !source.profiles.every(profile => typeof profile === 'string' && COMPOSE_PROFILE.test(profile))
570
+ || candidate.executable !== source.executable || candidate.cwd !== source.canonicalRoot
571
+ || !Array.isArray(candidate.argv) || candidate.argv.length === 0 || candidate.argv.length > GUARD_INPUT_LIMITS.words
572
+ || !candidate.argv.every(arg => typeof arg === 'string' && arg.length > 0
573
+ && Buffer.byteLength(arg) <= GUARD_INPUT_LIMITS.wordBytes && !/[\x00-\x1f\x7f-\x9f\uD800-\uDFFF]/u.test(arg)))
574
+ return undefined;
575
+ const args = candidate.argv;
576
+ // A group member is named by its first two words; its options follow both.
577
+ const spelling = commandSpelling(args);
578
+ const definition = commands.find(row => row.name === spelling.name);
579
+ const available = definition?.ordinary;
580
+ if (!definition || definition.visibility !== 'public' || available?.availability !== 'available')
581
+ return undefined;
582
+ const grammar = available;
583
+ const paths = new Set();
584
+ const confined = (path) => {
585
+ if (!path || path.includes('\\') || path.split('/').includes('..') || /[*?\[\]]/.test(path))
586
+ return false;
587
+ const absolute = resolve(source.canonicalRoot, path);
588
+ if (!canonicalPath(absolute) || !source.allowedRoots.some(root => absolute === root || absolute.startsWith(root + '/')))
589
+ return false;
590
+ paths.add(absolute);
591
+ return true;
592
+ };
593
+ if (grammar.configuration === 'resolved' && !confined(source.configPath))
594
+ return undefined;
595
+ const reads = [];
596
+ const positionals = [];
597
+ const options = new Map();
598
+ const wraps = WRAPPING_COMMANDS.has(definition.name);
599
+ const separator = wraps ? spelling.rest.indexOf('--') : -1;
600
+ // The wrapped command is required and is the only thing after `--`.
601
+ if (wraps && (separator < 0 || separator === spelling.rest.length - 1))
602
+ return undefined;
603
+ const words = separator < 0 ? spelling.rest : spelling.rest.slice(0, separator);
604
+ for (let index = 0; index < words.length; index++) {
605
+ const arg = words[index];
606
+ if (!arg.startsWith('-')) {
607
+ positionals.push(arg);
608
+ continue;
609
+ }
610
+ if (!arg.startsWith('--') || arg === '--')
611
+ return undefined;
612
+ const equal = arg.indexOf('=');
613
+ const name = arg.slice(2, equal < 0 ? undefined : equal);
614
+ const option = grammar.options[name];
615
+ if (!option || options.has(name) && option.repetition !== 'repeated' || grammar.forbidden.presence.includes(name))
616
+ return undefined;
617
+ if (option.kind === 'flag') {
618
+ if (equal >= 0)
619
+ return undefined;
620
+ options.set(name, true);
621
+ continue;
622
+ }
623
+ const text = equal < 0 ? words[++index] : arg.slice(equal + 1);
624
+ if (!text || text.startsWith('-'))
625
+ return undefined;
626
+ if (option.role === 'confined-path')
627
+ reads.push(resolve(source.canonicalRoot, text));
628
+ const valid = option.role === 'confined-path' ? confined(text)
629
+ : option.role === 'resolved-config' ? confined(text) && resolve(source.canonicalRoot, text) === source.configPath
630
+ : option.role === 'declared-service' ? source.services.includes(text)
631
+ : option.role === 'declared-gate' ? source.gates.includes(text)
632
+ : option.role === 'gate-mode' ? ['canonical', 'full', 'staged'].includes(text)
633
+ : option.role === 'gate-selector' ? GATE_SELECTOR.test(text)
634
+ : option.role === 'identifier' ? WORK_ORDER_ID.test(text)
635
+ : option.role === 'route' ? ROUTE_SELECTOR.test(text)
636
+ : option.role === 'label' ? true
637
+ : option.role === 'field' ? isRecordedAnswerField(text)
638
+ : option.role === 'tail' ? /^[1-9]\d{0,4}$/.test(text) && Number(text) <= 10_000
639
+ : option.role === 'since' ? text.length <= 64 && (/^\d+(?:\.\d+)?[smhd]$/.test(text)
640
+ || /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,9})?(?:Z|[+-]\d\d:\d\d)$/.test(text) && Number.isFinite(Date.parse(text)))
641
+ : option.role === 'surface-target' ? text.split(',').every(tool => SURFACE_TARGETS.some(target => target === tool))
642
+ : option.role === 'evaluator-tool' ? EVALUATOR_TOOLS.includes(text)
643
+ : option.role === 'local-image' ? LOCAL_IMAGE.test(text)
644
+ : option.role === 'compose-profile' ? source.profiles.includes(text)
645
+ : option.role === 'sha256' && /^[a-f0-9]{64}$/.test(text);
646
+ if (!valid)
647
+ return undefined;
648
+ options.set(name, text);
649
+ }
650
+ if (positionals.length < grammar.positional.min || positionals.length > grammar.positional.max
651
+ || positionals.some((word, index) => grammar.positional.roles[index] === 'operation'
652
+ ? !grammar.positional.operations?.includes(word)
653
+ : grammar.positional.roles[index] === 'version' ? !EXACT_VERSION.test(word)
654
+ : grammar.positional.roles[index] === 'slug' ? !/^[a-z0-9][a-z0-9-]{0,62}$/.test(word)
655
+ : grammar.positional.roles[index] === 'surface-target' ? !SURFACE_TARGETS.some(target => target === word)
656
+ : grammar.positional.roles[index] !== 'confined-root' || !confined(word))
657
+ || Object.entries(grammar.options).some(([name, option]) => option.required && !options.has(name))
658
+ // A dry run writes nothing, so no grant gates it.
659
+ || grammar.requires !== undefined && !options.has('dry-run') && !source.grants.includes(grammar.requires)
660
+ || typeof inspectPath !== 'function' || [...paths].some(path => !inspectPath(path)))
661
+ return undefined;
662
+ return { executable: source.executable, cwd: source.canonicalRoot, argv: [...args],
663
+ ...(reads.length ? { reads } : {}) };
664
+ }