@b00dea/cockpit 0.0.0-stage → 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 (363) hide show
  1. package/CHANGELOG.md +375 -0
  2. package/LICENSE +21 -0
  3. package/README.md +22 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2574 -0
  6. package/dist/autonomy.d.ts +44 -0
  7. package/dist/autonomy.js +156 -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 +62 -0
  11. package/dist/browser/commands.js +349 -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 +117 -0
  17. package/dist/browser/runtime.js +183 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +556 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/cli-guard.d.ts +5 -0
  23. package/dist/cli-guard.js +71 -0
  24. package/dist/cli-main.d.ts +139 -0
  25. package/dist/cli-main.js +1405 -0
  26. package/dist/cli.d.ts +3 -0
  27. package/dist/cli.js +16 -0
  28. package/dist/command-table.d.ts +113 -0
  29. package/dist/command-table.js +512 -0
  30. package/dist/config/load.d.ts +171 -0
  31. package/dist/config/load.js +345 -0
  32. package/dist/config/sibling-toml.d.ts +48 -0
  33. package/dist/config/sibling-toml.js +160 -0
  34. package/dist/config/surface-tools-toml.d.ts +9 -0
  35. package/dist/config/surface-tools-toml.js +37 -0
  36. package/dist/config/verified-write.d.ts +43 -0
  37. package/dist/config/verified-write.js +89 -0
  38. package/dist/diagnostics.d.ts +26 -0
  39. package/dist/diagnostics.js +335 -0
  40. package/dist/emit.d.ts +22 -0
  41. package/dist/emit.js +43 -0
  42. package/dist/entrypoint-kinds.d.ts +3 -0
  43. package/dist/entrypoint-kinds.js +11 -0
  44. package/dist/environment/application/destructive.d.ts +6 -0
  45. package/dist/environment/application/destructive.js +425 -0
  46. package/dist/environment/application/doctor.d.ts +89 -0
  47. package/dist/environment/application/doctor.js +235 -0
  48. package/dist/environment/application/guard.d.ts +43 -0
  49. package/dist/environment/application/guard.js +622 -0
  50. package/dist/environment/application/health.d.ts +134 -0
  51. package/dist/environment/application/health.js +379 -0
  52. package/dist/environment/application/lifecycle-context.d.ts +228 -0
  53. package/dist/environment/application/lifecycle-context.js +154 -0
  54. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  55. package/dist/environment/application/lifecycle-state.js +201 -0
  56. package/dist/environment/application/lifecycle.d.ts +17 -0
  57. package/dist/environment/application/lifecycle.js +729 -0
  58. package/dist/environment/application/logs.d.ts +45 -0
  59. package/dist/environment/application/logs.js +127 -0
  60. package/dist/environment/application/setup.d.ts +129 -0
  61. package/dist/environment/application/setup.js +741 -0
  62. package/dist/environment/application/shared.d.ts +11 -0
  63. package/dist/environment/application/shared.js +61 -0
  64. package/dist/environment/claims.d.ts +389 -0
  65. package/dist/environment/claims.js +1709 -0
  66. package/dist/environment/codex-hook-trust.d.ts +11 -0
  67. package/dist/environment/codex-hook-trust.js +185 -0
  68. package/dist/environment/compose-environment.d.ts +1 -0
  69. package/dist/environment/compose-environment.js +6 -0
  70. package/dist/environment/compose-ownership.d.ts +21 -0
  71. package/dist/environment/compose-ownership.js +117 -0
  72. package/dist/environment/compose-topology.d.ts +117 -0
  73. package/dist/environment/compose-topology.js +413 -0
  74. package/dist/environment/contract.d.ts +489 -0
  75. package/dist/environment/contract.js +761 -0
  76. package/dist/environment/declaration.d.ts +14 -0
  77. package/dist/environment/declaration.js +39 -0
  78. package/dist/environment/destructive-composition.d.ts +29 -0
  79. package/dist/environment/destructive-composition.js +333 -0
  80. package/dist/environment/docker-daemon.d.ts +30 -0
  81. package/dist/environment/docker-daemon.js +78 -0
  82. package/dist/environment/docker-endpoint.d.ts +10 -0
  83. package/dist/environment/docker-endpoint.js +19 -0
  84. package/dist/environment/env-schema-observation.d.ts +46 -0
  85. package/dist/environment/env-schema-observation.js +241 -0
  86. package/dist/environment/evidence.d.ts +136 -0
  87. package/dist/environment/evidence.js +437 -0
  88. package/dist/environment/executable.d.ts +26 -0
  89. package/dist/environment/executable.js +167 -0
  90. package/dist/environment/generated-application/authorities.d.ts +5 -0
  91. package/dist/environment/generated-application/authorities.js +61 -0
  92. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  93. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  94. package/dist/environment/generated-application/check.d.ts +13 -0
  95. package/dist/environment/generated-application/check.js +189 -0
  96. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  97. package/dist/environment/generated-application/custom-surface.js +289 -0
  98. package/dist/environment/generated-application/prepare.d.ts +29 -0
  99. package/dist/environment/generated-application/prepare.js +276 -0
  100. package/dist/environment/generated-application/publication.d.ts +33 -0
  101. package/dist/environment/generated-application/publication.js +547 -0
  102. package/dist/environment/generated-application/shared.d.ts +484 -0
  103. package/dist/environment/generated-application/shared.js +349 -0
  104. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  105. package/dist/environment/generated-application/surface-observation.js +69 -0
  106. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  107. package/dist/environment/generated-application/surface-outputs.js +389 -0
  108. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  109. package/dist/environment/generated-application/surfaces.js +699 -0
  110. package/dist/environment/generated-application/sync.d.ts +6 -0
  111. package/dist/environment/generated-application/sync.js +109 -0
  112. package/dist/environment/git-observation.d.ts +183 -0
  113. package/dist/environment/git-observation.js +544 -0
  114. package/dist/environment/health-composition.d.ts +11 -0
  115. package/dist/environment/health-composition.js +628 -0
  116. package/dist/environment/host-observation.d.ts +56 -0
  117. package/dist/environment/host-observation.js +52 -0
  118. package/dist/environment/identity.d.ts +48 -0
  119. package/dist/environment/identity.js +85 -0
  120. package/dist/environment/init-application.d.ts +308 -0
  121. package/dist/environment/init-application.js +697 -0
  122. package/dist/environment/install-policy-observation.d.ts +71 -0
  123. package/dist/environment/install-policy-observation.js +284 -0
  124. package/dist/environment/install-policy.d.ts +48 -0
  125. package/dist/environment/install-policy.js +96 -0
  126. package/dist/environment/instance.d.ts +33 -0
  127. package/dist/environment/instance.js +122 -0
  128. package/dist/environment/instruction-pointer.d.ts +19 -0
  129. package/dist/environment/instruction-pointer.js +144 -0
  130. package/dist/environment/lifecycle-composition.d.ts +29 -0
  131. package/dist/environment/lifecycle-composition.js +675 -0
  132. package/dist/environment/lifecycle-stage.d.ts +115 -0
  133. package/dist/environment/lifecycle-stage.js +398 -0
  134. package/dist/environment/lifecycle.d.ts +75 -0
  135. package/dist/environment/lifecycle.js +354 -0
  136. package/dist/environment/logs.d.ts +132 -0
  137. package/dist/environment/logs.js +519 -0
  138. package/dist/environment/observations.d.ts +46 -0
  139. package/dist/environment/observations.js +344 -0
  140. package/dist/environment/operator-application.d.ts +50 -0
  141. package/dist/environment/operator-application.js +155 -0
  142. package/dist/environment/ports.d.ts +182 -0
  143. package/dist/environment/ports.js +1205 -0
  144. package/dist/environment/posture-observation.d.ts +68 -0
  145. package/dist/environment/posture-observation.js +253 -0
  146. package/dist/environment/read-composition.d.ts +27 -0
  147. package/dist/environment/read-composition.js +352 -0
  148. package/dist/environment/readiness-composition.d.ts +38 -0
  149. package/dist/environment/readiness-composition.js +130 -0
  150. package/dist/environment/reclaim.d.ts +333 -0
  151. package/dist/environment/reclaim.js +1835 -0
  152. package/dist/environment/repository-identity.d.ts +13 -0
  153. package/dist/environment/repository-identity.js +108 -0
  154. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  155. package/dist/environment/runtime-measurement-record.js +111 -0
  156. package/dist/environment/runtime-measurement.d.ts +27 -0
  157. package/dist/environment/runtime-measurement.js +224 -0
  158. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  159. package/dist/environment/runtime-probe-residue.js +148 -0
  160. package/dist/environment/runtime-probe.d.ts +72 -0
  161. package/dist/environment/runtime-probe.js +725 -0
  162. package/dist/environment/runtime.d.ts +505 -0
  163. package/dist/environment/runtime.js +2335 -0
  164. package/dist/environment/seed.d.ts +16 -0
  165. package/dist/environment/seed.js +43 -0
  166. package/dist/environment/selftest-application.d.ts +86 -0
  167. package/dist/environment/selftest-application.js +713 -0
  168. package/dist/environment/sensitive.d.ts +3 -0
  169. package/dist/environment/sensitive.js +48 -0
  170. package/dist/environment/setup-observation.d.ts +62 -0
  171. package/dist/environment/setup-observation.js +1473 -0
  172. package/dist/environment/setup.d.ts +43 -0
  173. package/dist/environment/setup.js +176 -0
  174. package/dist/environment/sibling-instructions.d.ts +33 -0
  175. package/dist/environment/sibling-instructions.js +224 -0
  176. package/dist/environment/sibling-registration.d.ts +45 -0
  177. package/dist/environment/sibling-registration.js +538 -0
  178. package/dist/environment/state.d.ts +371 -0
  179. package/dist/environment/state.js +2013 -0
  180. package/dist/environment/templates.d.ts +140 -0
  181. package/dist/environment/templates.js +685 -0
  182. package/dist/environment/tool-registration.d.ts +9 -0
  183. package/dist/environment/tool-registration.js +96 -0
  184. package/dist/environment/upgrade-application.d.ts +19 -0
  185. package/dist/environment/upgrade-application.js +175 -0
  186. package/dist/environment-exec/execute.d.ts +102 -0
  187. package/dist/environment-exec/execute.js +1175 -0
  188. package/dist/environment-exec/libexec-program.d.ts +6 -0
  189. package/dist/environment-exec/libexec-program.js +22 -0
  190. package/dist/environment-exec/verified-digest.d.ts +4 -0
  191. package/dist/environment-exec/verified-digest.js +16 -0
  192. package/dist/exact-keys.d.ts +7 -0
  193. package/dist/exact-keys.js +15 -0
  194. package/dist/fix-catalogue.d.ts +32 -0
  195. package/dist/fix-catalogue.js +60 -0
  196. package/dist/freshness.d.ts +29 -0
  197. package/dist/freshness.js +75 -0
  198. package/dist/generated-environment/adopt.d.ts +16 -0
  199. package/dist/generated-environment/adopt.js +275 -0
  200. package/dist/generated-environment/command.d.ts +47 -0
  201. package/dist/generated-environment/command.js +75 -0
  202. package/dist/generated-environment/transaction.d.ts +186 -0
  203. package/dist/generated-environment/transaction.js +1770 -0
  204. package/dist/grading/evaluate.d.ts +71 -0
  205. package/dist/grading/evaluate.js +403 -0
  206. package/dist/grading/grade.d.ts +33 -0
  207. package/dist/grading/grade.js +57 -0
  208. package/dist/guards/evaluator.d.ts +113 -0
  209. package/dist/guards/evaluator.js +1226 -0
  210. package/dist/guards/harness-adapter.d.ts +17 -0
  211. package/dist/guards/harness-adapter.js +57 -0
  212. package/dist/guards/host.d.ts +8 -0
  213. package/dist/guards/host.js +10 -0
  214. package/dist/guards/invocation-parser.d.ts +66 -0
  215. package/dist/guards/invocation-parser.js +595 -0
  216. package/dist/guards/local-identity.d.ts +40 -0
  217. package/dist/guards/local-identity.js +123 -0
  218. package/dist/guards/policy-pin.d.ts +13 -0
  219. package/dist/guards/policy-pin.js +175 -0
  220. package/dist/guards/protected-locations.d.ts +62 -0
  221. package/dist/guards/protected-locations.js +461 -0
  222. package/dist/guards/read-only.d.ts +37 -0
  223. package/dist/guards/read-only.js +315 -0
  224. package/dist/init/command.d.ts +40 -0
  225. package/dist/init/command.js +310 -0
  226. package/dist/init/derive.d.ts +51 -0
  227. package/dist/init/derive.js +715 -0
  228. package/dist/init/draft.d.ts +138 -0
  229. package/dist/init/draft.js +1205 -0
  230. package/dist/init/render.d.ts +35 -0
  231. package/dist/init/render.js +82 -0
  232. package/dist/init/scaffold.d.ts +53 -0
  233. package/dist/init/scaffold.js +121 -0
  234. package/dist/invocation.d.ts +13 -0
  235. package/dist/invocation.js +20 -0
  236. package/dist/package-metadata.d.ts +9 -0
  237. package/dist/package-metadata.js +19 -0
  238. package/dist/readiness.d.ts +53 -0
  239. package/dist/readiness.js +88 -0
  240. package/dist/references/named.d.ts +24 -0
  241. package/dist/references/named.js +27 -0
  242. package/dist/refusal.d.ts +15 -0
  243. package/dist/refusal.js +7 -0
  244. package/dist/registry-derivation.d.ts +31 -0
  245. package/dist/registry-derivation.js +32 -0
  246. package/dist/registry-resolve/command.d.ts +47 -0
  247. package/dist/registry-resolve/command.js +226 -0
  248. package/dist/registry-resolve/registry.d.ts +68 -0
  249. package/dist/registry-resolve/registry.js +292 -0
  250. package/dist/registry-resolve/render-shell.d.ts +18 -0
  251. package/dist/registry-resolve/render-shell.js +36 -0
  252. package/dist/repair.d.ts +29 -0
  253. package/dist/repair.js +8 -0
  254. package/dist/schema-json.d.ts +1 -0
  255. package/dist/schema-json.js +177 -0
  256. package/dist/schema.d.ts +1322 -0
  257. package/dist/schema.js +934 -0
  258. package/dist/setup/command.d.ts +4 -0
  259. package/dist/setup/command.js +4 -0
  260. package/dist/sha256.d.ts +1 -0
  261. package/dist/sha256.js +4 -0
  262. package/dist/surfaces/adoption.d.ts +41 -0
  263. package/dist/surfaces/adoption.js +217 -0
  264. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  265. package/dist/surfaces/consumer-instructions.js +43 -0
  266. package/dist/surfaces/contract.d.ts +42 -0
  267. package/dist/surfaces/contract.js +169 -0
  268. package/dist/surfaces/core-declarations.d.ts +24 -0
  269. package/dist/surfaces/core-declarations.js +79 -0
  270. package/dist/surfaces/development-markers.d.ts +1 -0
  271. package/dist/surfaces/development-markers.js +18 -0
  272. package/dist/surfaces/generator-adapter.d.ts +71 -0
  273. package/dist/surfaces/generator-adapter.js +809 -0
  274. package/dist/surfaces/harness-registry.d.ts +180 -0
  275. package/dist/surfaces/harness-registry.js +117 -0
  276. package/dist/surfaces/mcp.d.ts +95 -0
  277. package/dist/surfaces/mcp.js +186 -0
  278. package/dist/surfaces/merge.d.ts +30 -0
  279. package/dist/surfaces/merge.js +105 -0
  280. package/dist/surfaces/ownership.d.ts +190 -0
  281. package/dist/surfaces/ownership.js +795 -0
  282. package/dist/surfaces/projection.d.ts +136 -0
  283. package/dist/surfaces/projection.js +681 -0
  284. package/dist/surfaces/render.d.ts +118 -0
  285. package/dist/surfaces/render.js +633 -0
  286. package/dist/surfaces/restrictions.d.ts +130 -0
  287. package/dist/surfaces/restrictions.js +447 -0
  288. package/dist/surfaces/selftest.d.ts +162 -0
  289. package/dist/surfaces/selftest.js +393 -0
  290. package/dist/surfaces/skill-contract.d.ts +85 -0
  291. package/dist/surfaces/skill-contract.js +449 -0
  292. package/dist/surfaces/sync.d.ts +57 -0
  293. package/dist/surfaces/sync.js +164 -0
  294. package/dist/team-metadata.d.ts +41 -0
  295. package/dist/team-metadata.js +512 -0
  296. package/dist/testing/gates.d.ts +91 -0
  297. package/dist/testing/gates.js +182 -0
  298. package/dist/testing/hooks.d.ts +59 -0
  299. package/dist/testing/hooks.js +139 -0
  300. package/dist/testing/verdict.d.ts +29 -0
  301. package/dist/testing/verdict.js +62 -0
  302. package/dist/work-orders/application.d.ts +99 -0
  303. package/dist/work-orders/application.js +383 -0
  304. package/dist/work-orders/composition.d.ts +124 -0
  305. package/dist/work-orders/composition.js +1010 -0
  306. package/dist/work-orders/contract.d.ts +61 -0
  307. package/dist/work-orders/contract.js +122 -0
  308. package/dist/work-orders/store.d.ts +158 -0
  309. package/dist/work-orders/store.js +197 -0
  310. package/docs/README.md +65 -0
  311. package/docs/architecture.md +136 -0
  312. package/docs/capability-matrix.md +50 -0
  313. package/docs/cli-reference.md +599 -0
  314. package/docs/command-list.md +51 -0
  315. package/docs/compose-patterns.md +99 -0
  316. package/docs/concepts.md +171 -0
  317. package/docs/configuration-essentials.md +172 -0
  318. package/docs/configuration-reference.md +621 -0
  319. package/docs/environment.md +760 -0
  320. package/docs/extending.md +43 -0
  321. package/docs/faq.md +111 -0
  322. package/docs/getting-started.md +608 -0
  323. package/docs/harness-surfaces.md +433 -0
  324. package/docs/joining-a-team.md +76 -0
  325. package/docs/release-policy.md +49 -0
  326. package/docs/safety-and-autonomy.md +419 -0
  327. package/docs/troubleshooting.md +361 -0
  328. package/docs/tutorial.md +170 -0
  329. package/docs/work-and-browser.md +254 -0
  330. package/libexec/browser-operation.cjs +186 -0
  331. package/libexec/fence-marker.cjs +101 -0
  332. package/libexec/fence-marker.d.cts +24 -0
  333. package/libexec/fenced-group-runner.cjs +77 -0
  334. package/libexec/fenced-supervisor.cjs +137 -0
  335. package/libexec/generated-filesystem.cjs +731 -0
  336. package/libexec/install-filesystem.cjs +225 -0
  337. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  338. package/libexec/rulesync-generator.cjs +743 -0
  339. package/libexec/surface-generator-worker.cjs +266 -0
  340. package/libexec/surface-output-reader.cjs +99 -0
  341. package/libexec/surface-scratch-cleanup.cjs +14 -0
  342. package/libexec/validate-harness-evidence.d.mts +183 -0
  343. package/libexec/validate-harness-evidence.mjs +1312 -0
  344. package/package.json +74 -4
  345. package/surfaces/README.md +33 -0
  346. package/surfaces/agents/evaluator.agent.md +76 -0
  347. package/surfaces/guards/bash-guard.sh +48 -0
  348. package/surfaces/guards/claude.adapter.json +25 -0
  349. package/surfaces/guards/codex.adapter.json +24 -0
  350. package/surfaces/guards/opencode.adapter.json +25 -0
  351. package/surfaces/references/browser.md +63 -0
  352. package/surfaces/references/test-data-and-personas.md +84 -0
  353. package/surfaces/references/testing-tiers.md +11 -0
  354. package/surfaces/skills/cockpit-browser.skill.md +54 -0
  355. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  356. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  357. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  358. package/surfaces/skills/cockpit-work.skill.md +34 -0
  359. package/surfaces/skills/collection.json +5 -0
  360. package/surfaces/skills/procedures/explain.md +61 -0
  361. package/surfaces/skills/procedures/fix.md +81 -0
  362. package/surfaces/skills/procedures/setup.md +379 -0
  363. package/surfaces/skills/procedures/work.md +105 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { CliHandlerOverrides, CliIo, CliRuntime } from './cli-main.js';
2
+ export type { CliHandlerOverrides, CliIo, CliRuntime, CommandHandlers, CommandOutcome, ComposeScaffoldRequest, } from './cli-main.js';
3
+ export declare function runCli(args: readonly string[], io?: CliIo, handlerOverrides?: CliHandlerOverrides, runtime?: CliRuntime): Promise<number>;
package/dist/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ import { resolve } from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { realpathSync } from 'node:fs';
4
+ import { commandSpelling } from './command-table.js';
5
+ import { runGuardCommand } from './cli-guard.js';
6
+ // The shell guard runs this entry on every agent tool call, so a guard decision loads only its own
7
+ // modules; every other command loads the full CLI. Native Windows goes to the full CLI for its refusal.
8
+ export async function runCli(args, io = process, handlerOverrides = {}, runtime = {}) {
9
+ if (process.platform !== 'win32' && commandSpelling(args).name === 'guard-evaluate') {
10
+ return runGuardCommand(commandSpelling(args).rest, io, runtime.env ?? process.env);
11
+ }
12
+ return (await import('./cli-main.js')).runCli(args, io, handlerOverrides, runtime);
13
+ }
14
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(resolve(process.argv[1]))).href) {
15
+ process.exitCode = await runCli(process.argv.slice(2));
16
+ }
@@ -0,0 +1,113 @@
1
+ import type { ParseArgsConfig } from 'node:util';
2
+ import type { GuardInvocationProfile } from './guards/evaluator.js';
3
+ import { GUARD_HANDLER_ARGV } from './guards/harness-adapter.js';
4
+ export declare function isRecordedAnswerField(value: string): boolean;
5
+ export type WorkHandlerIdentifier = 'work-status' | 'work-accept' | 'work-check' | 'work-note' | 'work-answer' | 'work-grade' | 'work-signoff' | 'work-recover' | 'work-verify' | 'grade-criteria' | 'browser-drive' | 'browser-screenshot' | 'test-run';
6
+ export type HandlerIdentifier = WorkHandlerIdentifier | 'check' | 'doctor' | 'down' | 'init' | 'logs' | 'preflight' | 'registry-resolve' | 'reset' | 'runtime' | 'setup' | 'selftest' | 'sibling' | 'tool' | 'session' | 'sleep' | 'sync' | 'up' | 'upgrade';
7
+ export type CommandStage = 'set-up' | 'generate-and-verify' | 'run-the-stack' | 'diagnose' | 'test-and-grade';
8
+ /** The order a newcomer meets the commands in, as the README's "Commands at a glance" table names it. */
9
+ export declare const commandStages: readonly Readonly<{
10
+ id: CommandStage;
11
+ title: string;
12
+ }>[];
13
+ /**
14
+ * A group is a first command word whose commands are named `<group> <subcommand>`. Each subcommand
15
+ * keeps its own row, options and handler, so an option one subcommand reads is never accepted and
16
+ * ignored by another.
17
+ */
18
+ export declare const commandGroups: readonly Readonly<{
19
+ name: string;
20
+ summary: string;
21
+ }>[];
22
+ /**
23
+ * The public command an argv names, and the arguments after it. A group word takes the next word
24
+ * as its subcommand; `subcommand` is absent when none follows, so the caller can show the group.
25
+ */
26
+ export declare function commandSpelling(args: readonly string[]): Readonly<{
27
+ name: string | undefined;
28
+ group?: string;
29
+ rest: readonly string[];
30
+ }>;
31
+ export interface CommandDefinition {
32
+ /** The words a person types: one word, or `<group> <subcommand>` for a member of commandGroups. */
33
+ name: string;
34
+ stage: CommandStage;
35
+ summary: string;
36
+ /** What follows the command name in its help usage line, when it takes more than options. */
37
+ usage?: string;
38
+ handler: HandlerIdentifier;
39
+ options: NonNullable<ParseArgsConfig['options']>;
40
+ visibility: 'public';
41
+ ordinary: OrdinaryCommandMetadata;
42
+ }
43
+ export declare function describePublicOption(name: string): string;
44
+ type OrdinaryValueRole = 'confined-path' | 'resolved-config' | 'declared-service' | 'declared-gate' | 'gate-mode' | 'gate-selector' | 'identifier' | 'label' | 'field' | 'tail' | 'since' | 'sha256' | 'surface-target' | 'local-image' | 'evaluator-tool';
45
+ export interface OrdinaryOption {
46
+ readonly kind: 'flag' | 'value';
47
+ readonly role: 'boolean' | OrdinaryValueRole;
48
+ readonly repetition: 'singleton';
49
+ readonly required?: true;
50
+ }
51
+ export type OrdinaryCommandMetadata = Readonly<{
52
+ availability: 'unavailable';
53
+ reason: string;
54
+ }> | Readonly<{
55
+ availability: 'available';
56
+ executableRole: 'installed-cockpit';
57
+ command: HandlerIdentifier;
58
+ positional: Readonly<{
59
+ min: number;
60
+ max: number;
61
+ roles: readonly ['confined-root'] | readonly ['operation'] | readonly ['version'] | readonly [];
62
+ operations?: readonly string[];
63
+ }>;
64
+ options: Readonly<Record<string, OrdinaryOption>>;
65
+ cwdRole: 'canonical-root';
66
+ configuration: 'resolved' | 'none';
67
+ forbidden: Readonly<{
68
+ presence: readonly string[];
69
+ }>;
70
+ requires?: OrdinaryGrant;
71
+ }>;
72
+ export declare function isWorkHandler(value: HandlerIdentifier): value is WorkHandlerIdentifier;
73
+ export declare const commands: readonly CommandDefinition[];
74
+ export interface InternalCommandDefinition {
75
+ readonly name: 'guard-evaluate';
76
+ readonly handler: 'guard-evaluate';
77
+ readonly visibility: 'internal';
78
+ readonly ordinary: Readonly<{
79
+ availability: 'unavailable';
80
+ reason: string;
81
+ }>;
82
+ readonly argv: typeof GUARD_HANDLER_ARGV;
83
+ readonly options: Readonly<{
84
+ harness: {
85
+ readonly type: 'string';
86
+ };
87
+ }>;
88
+ }
89
+ export declare const internalCommands: readonly InternalCommandDefinition[];
90
+ export interface OrdinaryInvocationAuthority {
91
+ readonly executable: string;
92
+ readonly canonicalRoot: string;
93
+ readonly allowedRoots: readonly string[];
94
+ readonly configPath: string;
95
+ readonly services: readonly string[];
96
+ /** Test gate ids the configuration declares. */
97
+ readonly gates: readonly string[];
98
+ /** What the configuration grants an agent: browser preapproval, and the [autonomy] decisions set to agent. */
99
+ readonly grants: readonly OrdinaryGrant[];
100
+ }
101
+ export type OrdinaryGrant = 'browser-preapproval' | 'autonomy-sync' | 'autonomy-reset';
102
+ /**
103
+ * The inner command of an exact `session --config <configured file> -- <command…>` run from the
104
+ * cockpit root, or undefined. The guard admits the session only when it admits the inner command,
105
+ * so a session never runs what the agent could not run directly.
106
+ */
107
+ export declare function ordinarySessionCommand(argv: readonly string[], cwd: string, authority: Readonly<{
108
+ canonicalRoot: string;
109
+ configPath: string;
110
+ }>): readonly string[] | undefined;
111
+ /** Classify one exact candidate; the caller authenticates filesystem and executable authority. */
112
+ export declare function resolveOrdinaryInvocation(input: GuardInvocationProfile, authority: OrdinaryInvocationAuthority, inspectPath: (path: string) => boolean): GuardInvocationProfile | undefined;
113
+ export {};
@@ -0,0 +1,512 @@
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 { GUARD_HANDLER_ARGV } from './guards/harness-adapter.js';
6
+ import { GUARD_INPUT_LIMITS } from './guards/invocation-parser.js';
7
+ export function isRecordedAnswerField(value) {
8
+ return value.length <= 256 && /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(value);
9
+ }
10
+ /** The order a newcomer meets the commands in, as the README's "Commands at a glance" table names it. */
11
+ export const commandStages = Object.freeze([
12
+ { id: 'set-up', title: 'Set up' },
13
+ { id: 'generate-and-verify', title: 'Generate and verify' },
14
+ { id: 'run-the-stack', title: 'Run the stack' },
15
+ { id: 'diagnose', title: 'Diagnose' },
16
+ { id: 'test-and-grade', title: 'Test and grade' },
17
+ ]);
18
+ /**
19
+ * A group is a first command word whose commands are named `<group> <subcommand>`. Each subcommand
20
+ * keeps its own row, options and handler, so an option one subcommand reads is never accepted and
21
+ * ignored by another.
22
+ */
23
+ export const commandGroups = Object.freeze([
24
+ { name: 'work', summary: 'Accept, check, grade and close work orders' },
25
+ { name: 'browser', summary: 'Drive, inspect and capture the declared application in a browser' },
26
+ ]);
27
+ /**
28
+ * The public command an argv names, and the arguments after it. A group word takes the next word
29
+ * as its subcommand; `subcommand` is absent when none follows, so the caller can show the group.
30
+ */
31
+ export function commandSpelling(args) {
32
+ const [first, second, ...rest] = args;
33
+ const group = commandGroups.find(row => row.name === first);
34
+ if (!group)
35
+ return { name: first, rest: second === undefined ? [] : [second, ...rest] };
36
+ if (second === undefined || second.startsWith('-'))
37
+ return { name: undefined, group: group.name, rest: second === undefined ? [] : [second, ...rest] };
38
+ return { name: `${group.name} ${second}`, group: group.name, rest };
39
+ }
40
+ const FLAG_DESCRIPTIONS = Object.freeze({
41
+ 'accept-defaults': 'Accept the displayed defaults with their exact SHA-256 digest',
42
+ 'accept-preview': 'Accept the displayed init preview with its exact SHA-256 digest',
43
+ 'accept-reset': 'Accept the displayed answer reset with its exact SHA-256 digest',
44
+ abandon: 'Close the order as abandoned; --note says why',
45
+ adopt: 'Move one existing harness settings path under cockpit ownership',
46
+ adoption: 'Path to the legacy adoption shell data',
47
+ answers: 'Path to the init answers file',
48
+ ask: 'Record the note as an open question, which blocks the order until it is answered',
49
+ apply: 'Apply the displayed reversible operation',
50
+ command: 'With --scaffold-compose: the command that starts the app, as words separated by spaces',
51
+ config: 'Path to cockpit.toml',
52
+ create: 'Create a new local sibling repository',
53
+ criteria: 'Path to a file whose YAML front-matter lists the criteria to grade',
54
+ dir: 'Directory for the sibling checkout',
55
+ evaluator: 'Grade with only this agent tool (codexcli, claudecode or opencode), with no fallback to another',
56
+ 'dry-run': 'Preview without writing or installing anything',
57
+ follow: 'Continue collecting logs until cancelled',
58
+ from: 'Path to an existing local repository',
59
+ gate: 'Declared product gate to run',
60
+ image: 'Local container image the runtime measurement probe runs; it is never pulled',
61
+ 'instruction-pointer': 'Offer a marked AGENTS.md link for this separate sibling',
62
+ interactive: 'Ask for explicit approval in a terminal',
63
+ json: 'Emit one machine-readable JSON response',
64
+ mode: 'Declared gate execution mode',
65
+ mount: 'How the stack uses the sibling: always, none or profile:<name>',
66
+ note: 'What was checked, or what was and was not done',
67
+ partial: 'Close the order as partly done; --note says what is left',
68
+ port: 'With --scaffold-compose: the port the app listens on inside its container',
69
+ 'probe-specifications': 'Path to the harness probe specifications; with --write-probe-specifications, the earlier file whose filled rows for unchanged cells carry forward',
70
+ purpose: 'Short purpose of the sibling repository',
71
+ question: 'Number of the open question to answer, as work status lists it',
72
+ registry: 'Path to the legacy registry input',
73
+ 'reset-answer': 'Preview clearing one recorded init answer field',
74
+ remote: 'Repository remote URL recorded for the sibling',
75
+ role: 'Declared browser role',
76
+ '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',
77
+ select: 'Optional subset of the gate to run',
78
+ service: 'Declared service whose logs are collected',
79
+ 'siblings-root': 'Root directory containing sibling checkouts',
80
+ since: 'Collect logs no older than this duration',
81
+ tail: 'Maximum number of log records to collect',
82
+ target: 'Agent tool to set up; comma-separate several (claudecode,opencode)',
83
+ text: 'Text of the note or answer',
84
+ var: 'Environment variable mapping for the sibling',
85
+ verdict: 'Under [work] grading = "self": met or unmet',
86
+ with: 'With --scaffold-compose: backing services to add, from postgres and redis, separated by commas',
87
+ 'work-order': 'Work-order identifier',
88
+ 'write-probe-specifications': 'Write a probe-specification file for the current manifest to this new path, with the probe fields left for you',
89
+ 'write-instruction-pointers': 'Write the reviewed instruction links into opted-in sibling guides',
90
+ 'write-instruction-files': 'Write the reviewed AGENTS.md and CLAUDE.md changes that let every agent tool load a sibling\'s instructions',
91
+ });
92
+ export function describePublicOption(name) {
93
+ const description = FLAG_DESCRIPTIONS[name];
94
+ if (!description)
95
+ throw new Error(`Public option --${name} has no help description.`);
96
+ return description;
97
+ }
98
+ // Mirrors EvaluatorTool in schema.ts, which this data table may not import.
99
+ const EVALUATOR_TOOLS = ['codexcli', 'claudecode', 'opencode'];
100
+ const EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
101
+ const flag = Object.freeze({ kind: 'flag', role: 'boolean', repetition: 'singleton' });
102
+ function value(role, required = false) {
103
+ return { kind: 'value', role, repetition: 'singleton', ...(required ? { required: true } : {}) };
104
+ }
105
+ const str = (agent, fallback) => ({ type: 'string', ...fallback === undefined ? {} : { default: fallback }, ...agent ? { agent } : {} });
106
+ const bool = (agent) => ({ type: 'boolean', default: false, ...agent ? { agent } : {} });
107
+ const configFlag = str(value('resolved-config', true));
108
+ const jsonFlag = bool(flag);
109
+ const configured = { config: configFlag, json: jsonFlag };
110
+ const workOrderFlag = str(value('identifier', true));
111
+ // The flags and their roles are filled in from `flags` when the table is built.
112
+ function ordinary(command, extra = {}) {
113
+ return { availability: 'available', executableRole: 'installed-cockpit', command,
114
+ positional: { min: 0, max: 0, roles: [] }, options: {},
115
+ cwdRole: 'canonical-root', configuration: 'resolved',
116
+ forbidden: { presence: [] }, ...extra };
117
+ }
118
+ function unavailable(reason) { return { availability: 'unavailable', reason }; }
119
+ const browserAdmission = Object.freeze({ requires: 'browser-preapproval' });
120
+ // --interactive asks for a person at a terminal, so an agent invocation never carries it.
121
+ const personFlag = Object.freeze({ forbidden: Object.freeze({ presence: Object.freeze(['interactive']) }) });
122
+ export function isWorkHandler(value) {
123
+ return value.startsWith('work-') || value.startsWith('browser-') || value === 'test-run' || value === 'grade-criteria';
124
+ }
125
+ const sources = [
126
+ {
127
+ name: 'init',
128
+ stage: 'set-up',
129
+ summary: 'Preview, then accept, a first cockpit setup for one or more agent tools',
130
+ usage: '[<root>] [options]',
131
+ handler: 'init',
132
+ visibility: 'public',
133
+ ordinary: ordinary('init', {
134
+ configuration: 'none',
135
+ positional: Object.freeze({ min: 1, max: 1, roles: Object.freeze(['confined-root']) }),
136
+ forbidden: Object.freeze({ presence: Object.freeze(['interactive', 'answers', 'accept-preview', 'accept-defaults']) }),
137
+ }),
138
+ flags: { target: str(value('surface-target', true)), registry: str(value('confined-path')),
139
+ interactive: bool(), json: jsonFlag,
140
+ answers: str(), 'accept-preview': str(), 'accept-defaults': str(),
141
+ 'reset-answer': str(value('field')), 'accept-reset': str(value('sha256')),
142
+ 'scaffold-compose': str(), command: str(), port: str(), with: str() },
143
+ },
144
+ {
145
+ name: 'setup',
146
+ stage: 'set-up',
147
+ summary: 'Clone or locate the declared sibling repositories and link them',
148
+ handler: 'setup',
149
+ visibility: 'public',
150
+ ordinary: ordinary('setup', { forbidden: Object.freeze({ presence: Object.freeze(['write-instruction-pointers', 'write-instruction-files']) }) }),
151
+ flags: { config: configFlag, 'write-instruction-pointers': bool(), 'write-instruction-files': bool(), json: jsonFlag },
152
+ },
153
+ {
154
+ name: 'sibling',
155
+ stage: 'set-up',
156
+ summary: 'Add or remove a repository in cockpit.toml (sibling add, sibling remove)',
157
+ usage: 'add|remove <slug> [options]',
158
+ handler: 'sibling',
159
+ visibility: 'public',
160
+ ordinary: unavailable('sibling add and remove rewrite cockpit.toml'),
161
+ flags: { config: str(), create: bool(), from: str(), remote: str(), dir: str(), var: str(), purpose: str(), mount: str(),
162
+ 'instruction-pointer': bool(), json: bool() },
163
+ },
164
+ {
165
+ name: 'tool',
166
+ stage: 'set-up',
167
+ summary: 'Add or remove an agent tool in cockpit.toml (tool add, tool remove)',
168
+ usage: `add|remove <${SURFACE_TARGETS.join('|')}> [options]`,
169
+ handler: 'tool',
170
+ visibility: 'public',
171
+ // Classified with sibling add and remove: both rewrite cockpit.toml.
172
+ ordinary: unavailable('tool add and remove rewrite cockpit.toml'),
173
+ flags: { config: str(), 'dry-run': bool(), json: bool() },
174
+ },
175
+ {
176
+ name: 'registry-resolve',
177
+ stage: 'set-up',
178
+ summary: 'Turn a validated shell registry into data shell scripts can source',
179
+ handler: 'registry-resolve',
180
+ visibility: 'public',
181
+ ordinary: ordinary('registry-resolve', { configuration: 'none' }),
182
+ flags: { registry: str(value('confined-path', true)), 'siblings-root': str(value('confined-path', true)), adoption: str(value('confined-path', true)), json: jsonFlag },
183
+ },
184
+ {
185
+ name: 'upgrade',
186
+ stage: 'set-up',
187
+ summary: 'Move the exact cockpit pin to a newer version and re-run sync',
188
+ usage: '[<version>] [options]',
189
+ handler: 'upgrade',
190
+ visibility: 'public',
191
+ // The install is exact and its sync child decides as sync does, so an agent runs the real upgrade
192
+ // exactly where [autonomy] sync lets it sync; the version is the one positional.
193
+ ordinary: ordinary('upgrade', {
194
+ positional: Object.freeze({ min: 0, max: 1, roles: Object.freeze(['version']) }), requires: 'autonomy-sync'
195
+ }),
196
+ flags: { config: configFlag, 'dry-run': bool(flag) },
197
+ },
198
+ {
199
+ name: 'sync',
200
+ stage: 'generate-and-verify',
201
+ summary: 'Write the Compose ownership override and agent files from cockpit.toml',
202
+ handler: 'sync',
203
+ visibility: 'public',
204
+ // Adoption rewrites cockpit.toml and moves a team's harness settings, so it stays a person's. A
205
+ // real sync rewrites the guard's own files from a configuration the guard already pins and
206
+ // holds every call after an edit to, so it is an agent's only where [autonomy] sync = "agent".
207
+ ordinary: ordinary('sync', { forbidden: Object.freeze({ presence: Object.freeze(['interactive', 'adopt']) }), requires: 'autonomy-sync' }),
208
+ flags: { config: configFlag, 'dry-run': bool(flag), adopt: { type: 'string', multiple: true }, interactive: bool(), json: jsonFlag },
209
+ },
210
+ {
211
+ name: 'check',
212
+ stage: 'generate-and-verify',
213
+ summary: 'Report generated files that no longer match cockpit.toml',
214
+ handler: 'check',
215
+ visibility: 'public',
216
+ ordinary: ordinary('check'),
217
+ flags: configured,
218
+ },
219
+ {
220
+ name: 'selftest',
221
+ stage: 'generate-and-verify',
222
+ summary: 'Test whether agent tools enforce the generated controls',
223
+ handler: 'selftest',
224
+ visibility: 'public',
225
+ ordinary: ordinary('selftest', { forbidden: Object.freeze({ presence: Object.freeze(['interactive']) }) }),
226
+ flags: { config: configFlag, 'probe-specifications': str(value('confined-path', true)), 'write-probe-specifications': str(),
227
+ interactive: bool(), json: jsonFlag },
228
+ },
229
+ {
230
+ name: 'runtime',
231
+ stage: 'run-the-stack',
232
+ summary: 'Measure the local container runtime (runtime measure) and record the result for this machine',
233
+ usage: 'measure [options]',
234
+ handler: 'runtime',
235
+ visibility: 'public',
236
+ // The probe runs a disposable stack under its own project, cleans it up, and records the result
237
+ // only in this machine's ignored state, so an agent can measure before its first `up`.
238
+ ordinary: ordinary('runtime', {
239
+ positional: Object.freeze({ min: 1, max: 1, roles: Object.freeze(['operation']), operations: Object.freeze(['measure']) }),
240
+ }),
241
+ flags: { config: configFlag, image: str(value('local-image', true)), json: jsonFlag },
242
+ },
243
+ {
244
+ name: 'up',
245
+ stage: 'run-the-stack',
246
+ summary: 'Start or resume the declared environment',
247
+ handler: 'up',
248
+ visibility: 'public',
249
+ ordinary: ordinary('up'),
250
+ flags: configured,
251
+ },
252
+ {
253
+ name: 'preflight',
254
+ stage: 'run-the-stack',
255
+ summary: 'Check that the environment is healthy and ready for work',
256
+ handler: 'preflight',
257
+ visibility: 'public',
258
+ ordinary: ordinary('preflight'),
259
+ flags: configured,
260
+ },
261
+ {
262
+ name: 'session',
263
+ stage: 'run-the-stack',
264
+ summary: 'Keep the environment available for one command, then release it',
265
+ usage: '[options] -- <command> [args...]',
266
+ handler: 'session',
267
+ visibility: 'public',
268
+ // The guard admits `session --config <file> -- <command>` only when it admits the inner command.
269
+ ordinary: unavailable('session wraps an arbitrary foreground command'),
270
+ flags: { config: str() },
271
+ },
272
+ {
273
+ name: 'logs',
274
+ stage: 'run-the-stack',
275
+ summary: 'Show recent logs for one declared service',
276
+ handler: 'logs',
277
+ visibility: 'public',
278
+ ordinary: ordinary('logs'),
279
+ flags: { config: configFlag, service: str(value('declared-service', true)), tail: str(value('tail'), '100'), since: str(value('since'), '15m'),
280
+ follow: bool(flag), json: jsonFlag },
281
+ },
282
+ {
283
+ name: 'sleep',
284
+ stage: 'run-the-stack',
285
+ summary: 'Release this checkout and stop the stack if no other holder remains',
286
+ handler: 'sleep',
287
+ visibility: 'public',
288
+ ordinary: ordinary('sleep'),
289
+ flags: configured,
290
+ },
291
+ {
292
+ name: 'down',
293
+ stage: 'run-the-stack',
294
+ summary: 'Stop and remove the stack (on the active local Docker context)',
295
+ handler: 'down',
296
+ visibility: 'public',
297
+ // down removes only the containers and networks cockpit created; reset, which deletes data, stays out.
298
+ ordinary: ordinary('down'),
299
+ flags: configured,
300
+ },
301
+ {
302
+ name: 'reset',
303
+ stage: 'run-the-stack',
304
+ summary: 'Preview a reset, then approve it interactively or apply it as [autonomy] reset allows (on the active local Docker context)',
305
+ handler: 'reset',
306
+ visibility: 'public',
307
+ // Reset deletes stack data, so an agent applies one only where [autonomy] reset = "agent".
308
+ ordinary: ordinary('reset', { requires: 'autonomy-reset' }),
309
+ flags: { config: configFlag, interactive: bool(), apply: bool(Object.freeze({ ...flag, required: true })), json: jsonFlag },
310
+ },
311
+ {
312
+ name: 'doctor',
313
+ stage: 'diagnose',
314
+ summary: 'Report environment problems without changing anything',
315
+ handler: 'doctor',
316
+ visibility: 'public',
317
+ ordinary: ordinary('doctor'),
318
+ flags: configured,
319
+ },
320
+ // A gate runs only the stages a person declared in the reviewed cockpit.toml, which is the trust
321
+ // `up` already has; a selector reaches a stage only through the arguments that stage declares.
322
+ { 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',
323
+ ordinary: ordinary('test-run'),
324
+ flags: { ...configured, gate: str(value('declared-gate', true)), mode: str(value('gate-mode'), 'canonical'), select: str(value('gate-selector')) } },
325
+ // A gated step passes for the agent only where [work.gates] says agent; under person the command
326
+ // waits for the digest a person types after --interactive, which no agent invocation carries.
327
+ { name: 'work status', handler: 'work-status', stage: 'test-and-grade', summary: 'Report each work order, its state and how it closed', visibility: 'public',
328
+ ordinary: ordinary('work-status'),
329
+ flags: { ...configured, 'work-order': str(value('identifier')) } },
330
+ { 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',
331
+ ordinary: ordinary('work-accept', personFlag),
332
+ flags: { ...configured, 'work-order': workOrderFlag, interactive: bool() } },
333
+ { name: 'work check', handler: 'work-check', stage: 'test-and-grade', summary: "Run every criterion's check and the order's gates", visibility: 'public',
334
+ ordinary: ordinary('work-check'),
335
+ flags: { ...configured, 'work-order': workOrderFlag } },
336
+ { name: 'work note', handler: 'work-note', stage: 'test-and-grade', summary: 'Record a note on an order, or with --ask an open question that blocks it', visibility: 'public',
337
+ ordinary: ordinary('work-note'),
338
+ flags: { ...configured, 'work-order': workOrderFlag, text: str(value('label', true)), ask: bool(flag) } },
339
+ { name: 'work answer', handler: 'work-answer', stage: 'test-and-grade', summary: 'Answer an open question on an order, as [work.gates] allows', visibility: 'public',
340
+ ordinary: ordinary('work-answer', personFlag),
341
+ flags: { ...configured, 'work-order': workOrderFlag, question: str(value('tail', true)), text: str(value('label', true)), interactive: bool() } },
342
+ // Grading hands the order to an evaluator running apart from the agent that did the work, unless
343
+ // [work] grading names checks or self.
344
+ { name: 'work grade', handler: 'work-grade', stage: 'test-and-grade', summary: 'Grade an accepted order the way [work] grading says', visibility: 'public',
345
+ ordinary: ordinary('work-grade'),
346
+ flags: { ...configured, 'work-order': workOrderFlag, verdict: str(value('label')), note: str(value('label')), evaluator: str(value('evaluator-tool')) } },
347
+ { 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',
348
+ ordinary: ordinary('work-signoff', personFlag),
349
+ flags: { ...configured, 'work-order': workOrderFlag, partial: bool(flag), abandon: bool(flag), note: str(value('label')), interactive: bool() } },
350
+ // Inspection only reports the holder. --apply deletes the lock once a pid probe calls its holder
351
+ // dead, and that probe sees only this machine's process table: a holder running in a container or
352
+ // on another host that shares the checkout looks dead to it, so a person confirms it is gone.
353
+ { name: 'work recover', handler: 'work-recover', stage: 'test-and-grade', summary: "Inspect or remove a crashed command's work-order lock", visibility: 'public',
354
+ ordinary: ordinary('work-recover', { forbidden: Object.freeze({ presence: Object.freeze(['apply']) }) }),
355
+ flags: { ...configured, 'work-order': workOrderFlag, apply: bool() } },
356
+ { 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',
357
+ ordinary: ordinary('work-verify'),
358
+ flags: configured },
359
+ { name: 'grade', handler: 'grade-criteria', stage: 'test-and-grade', summary: 'Independently grade uncommitted work against a criteria file, with no work order', visibility: 'public',
360
+ ordinary: ordinary('grade-criteria'),
361
+ flags: { ...configured, criteria: str(value('confined-path', true)), evaluator: str(value('evaluator-tool')) } },
362
+ // Driving signs in and captures the application, so it runs unprompted only on the data posture a
363
+ // person wrote down as safe for that.
364
+ { name: 'browser drive', handler: 'browser-drive', stage: 'test-and-grade', summary: 'Drive the declared application browser surfaces', visibility: 'public',
365
+ ordinary: ordinary('browser-drive', browserAdmission),
366
+ flags: { ...configured, role: str(value('identifier')) } },
367
+ { name: 'browser screenshot', handler: 'browser-screenshot', stage: 'test-and-grade', summary: 'Capture the declared application surfaces', visibility: 'public',
368
+ ordinary: ordinary('browser-screenshot', browserAdmission),
369
+ flags: { ...configured, role: str(value('identifier')) } },
370
+ ];
371
+ export const commands = sources.map(({ flags, ...command }) => {
372
+ const entries = Object.entries(flags);
373
+ return { ...command,
374
+ options: Object.fromEntries(entries.map(([name, { agent: _agent, ...option }]) => [name, option])),
375
+ ordinary: command.ordinary.availability === 'available'
376
+ ? { ...command.ordinary, options: Object.fromEntries(entries.flatMap(([name, { agent }]) => agent ? [[name, agent]] : [])) }
377
+ : command.ordinary };
378
+ });
379
+ export const internalCommands = Object.freeze([Object.freeze({
380
+ name: 'guard-evaluate', handler: 'guard-evaluate', visibility: 'internal',
381
+ ordinary: Object.freeze({ availability: 'unavailable', reason: 'the guard hook calls this internally; it is not a user command' }),
382
+ argv: GUARD_HANDLER_ARGV, options: Object.freeze({ harness: Object.freeze({ type: 'string' }) }),
383
+ })]);
384
+ const ORDINARY_GRANTS = ['browser-preapproval', 'autonomy-sync', 'autonomy-reset'];
385
+ // An image reference or ID as Docker spells it; the command itself refuses one that is not local.
386
+ const LOCAL_IMAGE = /^[A-Za-z0-9][A-Za-z0-9._/:@-]{0,255}$/;
387
+ // A selected value that starts with `-` would reach the product's tool as an option of its own.
388
+ const GATE_SELECTOR = /^(?:spec|file|test|route):[^-]/;
389
+ function canonicalPath(path) {
390
+ return typeof path === 'string' && path !== '/' && isAbsolute(path) && normalize(path) === path
391
+ && !path.endsWith('/') && !/[\x00-\x1f\x7f-\x9f\\\uD800-\uDFFF]/u.test(path)
392
+ && Buffer.byteLength(path) <= GUARD_INPUT_LIMITS.pathBytes;
393
+ }
394
+ function record(value) {
395
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
396
+ }
397
+ /**
398
+ * The inner command of an exact `session --config <configured file> -- <command…>` run from the
399
+ * cockpit root, or undefined. The guard admits the session only when it admits the inner command,
400
+ * so a session never runs what the agent could not run directly.
401
+ */
402
+ export function ordinarySessionCommand(argv, cwd, authority) {
403
+ const separator = argv.indexOf('--');
404
+ if (argv[0] !== 'session' || separator < 0 || separator === argv.length - 1 || cwd !== authority.canonicalRoot)
405
+ return undefined;
406
+ const options = argv.slice(1, separator);
407
+ const config = options.length === 2 && options[0] === '--config' ? options[1]
408
+ : options.length === 1 && options[0].startsWith('--config=') ? options[0].slice('--config='.length) : undefined;
409
+ return config && resolve(cwd, config) === authority.configPath ? argv.slice(separator + 1) : undefined;
410
+ }
411
+ /** Classify one exact candidate; the caller authenticates filesystem and executable authority. */
412
+ export function resolveOrdinaryInvocation(input, authority, inspectPath) {
413
+ const candidate = snapshotJson(input);
414
+ const captured = snapshotJson(authority);
415
+ if (!record(candidate)
416
+ || Object.keys(candidate).sort().join(',') !== 'argv,cwd,executable'
417
+ || !record(captured)
418
+ || Object.keys(captured).sort().join(',') !== 'allowedRoots,canonicalRoot,configPath,executable,gates,grants,services')
419
+ return undefined;
420
+ const source = captured;
421
+ if (!canonicalPath(source.executable) || !canonicalPath(source.canonicalRoot) || !canonicalPath(source.configPath)
422
+ || !Array.isArray(source.allowedRoots) || source.allowedRoots.length > GUARD_INPUT_LIMITS.paths
423
+ || !source.allowedRoots.every(canonicalPath) || !source.allowedRoots.includes(source.canonicalRoot)
424
+ || !Array.isArray(source.services) || !source.services.every(service => typeof service === 'string' && /^[a-z0-9][a-z0-9_.-]{0,127}$/.test(service))
425
+ || !Array.isArray(source.gates) || !source.gates.every(gate => typeof gate === 'string' && /^[a-z][a-z0-9-]{0,63}$/.test(gate))
426
+ || !Array.isArray(source.grants) || !source.grants.every(grant => ORDINARY_GRANTS.includes(grant))
427
+ || candidate.executable !== source.executable || candidate.cwd !== source.canonicalRoot
428
+ || !Array.isArray(candidate.argv) || candidate.argv.length === 0 || candidate.argv.length > GUARD_INPUT_LIMITS.words
429
+ || !candidate.argv.every(arg => typeof arg === 'string' && arg.length > 0
430
+ && Buffer.byteLength(arg) <= GUARD_INPUT_LIMITS.wordBytes && !/[\x00-\x1f\x7f-\x9f\uD800-\uDFFF]/u.test(arg)))
431
+ return undefined;
432
+ const args = candidate.argv;
433
+ // A group member is named by its first two words; its options follow both.
434
+ const spelling = commandSpelling(args);
435
+ const definition = commands.find(row => row.name === spelling.name);
436
+ const available = definition?.ordinary;
437
+ if (!definition || definition.visibility !== 'public' || available?.availability !== 'available')
438
+ return undefined;
439
+ const grammar = available;
440
+ const paths = new Set();
441
+ const confined = (path) => {
442
+ if (!path || path.includes('\\') || path.split('/').includes('..') || /[*?\[\]]/.test(path))
443
+ return false;
444
+ const absolute = resolve(source.canonicalRoot, path);
445
+ if (!canonicalPath(absolute) || !source.allowedRoots.some(root => absolute === root || absolute.startsWith(root + '/')))
446
+ return false;
447
+ paths.add(absolute);
448
+ return true;
449
+ };
450
+ if (grammar.configuration === 'resolved' && !confined(source.configPath))
451
+ return undefined;
452
+ const reads = [];
453
+ const positionals = [];
454
+ const options = new Map();
455
+ const words = spelling.rest;
456
+ for (let index = 0; index < words.length; index++) {
457
+ const arg = words[index];
458
+ if (!arg.startsWith('-')) {
459
+ positionals.push(arg);
460
+ continue;
461
+ }
462
+ if (!arg.startsWith('--') || arg === '--')
463
+ return undefined;
464
+ const equal = arg.indexOf('=');
465
+ const name = arg.slice(2, equal < 0 ? undefined : equal);
466
+ const option = grammar.options[name];
467
+ if (!option || options.has(name) || grammar.forbidden.presence.includes(name))
468
+ return undefined;
469
+ if (option.kind === 'flag') {
470
+ if (equal >= 0)
471
+ return undefined;
472
+ options.set(name, true);
473
+ continue;
474
+ }
475
+ const text = equal < 0 ? words[++index] : arg.slice(equal + 1);
476
+ if (!text || text.startsWith('-'))
477
+ return undefined;
478
+ if (option.role === 'confined-path')
479
+ reads.push(resolve(source.canonicalRoot, text));
480
+ const valid = option.role === 'confined-path' ? confined(text)
481
+ : option.role === 'resolved-config' ? confined(text) && resolve(source.canonicalRoot, text) === source.configPath
482
+ : option.role === 'declared-service' ? source.services.includes(text)
483
+ : option.role === 'declared-gate' ? source.gates.includes(text)
484
+ : option.role === 'gate-mode' ? ['canonical', 'full', 'staged'].includes(text)
485
+ : option.role === 'gate-selector' ? GATE_SELECTOR.test(text)
486
+ : option.role === 'identifier' ? WORK_ORDER_ID.test(text)
487
+ : option.role === 'label' ? true
488
+ : option.role === 'field' ? isRecordedAnswerField(text)
489
+ : option.role === 'tail' ? /^[1-9]\d{0,4}$/.test(text) && Number(text) <= 10_000
490
+ : option.role === 'since' ? text.length <= 64 && (/^\d+(?:\.\d+)?[smhd]$/.test(text)
491
+ || /^\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)))
492
+ : option.role === 'surface-target' ? text.split(',').every(tool => SURFACE_TARGETS.some(target => target === tool))
493
+ : option.role === 'evaluator-tool' ? EVALUATOR_TOOLS.includes(text)
494
+ : option.role === 'local-image' ? LOCAL_IMAGE.test(text)
495
+ : option.role === 'sha256' && /^[a-f0-9]{64}$/.test(text);
496
+ if (!valid)
497
+ return undefined;
498
+ options.set(name, text);
499
+ }
500
+ if (positionals.length < grammar.positional.min || positionals.length > grammar.positional.max
501
+ || positionals.some((word, index) => grammar.positional.roles[index] === 'operation'
502
+ ? !grammar.positional.operations?.includes(word)
503
+ : grammar.positional.roles[index] === 'version' ? !EXACT_VERSION.test(word)
504
+ : grammar.positional.roles[index] !== 'confined-root' || !confined(word))
505
+ || Object.entries(grammar.options).some(([name, option]) => option.required && !options.has(name))
506
+ // A dry run writes nothing, so no grant gates it.
507
+ || grammar.requires !== undefined && !options.has('dry-run') && !source.grants.includes(grammar.requires)
508
+ || typeof inspectPath !== 'function' || [...paths].some(path => !inspectPath(path)))
509
+ return undefined;
510
+ return { executable: source.executable, cwd: source.canonicalRoot, argv: [...args],
511
+ ...(reads.length ? { reads } : {}) };
512
+ }