@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,595 @@
1
+ import { posix } from 'node:path';
2
+ import { isProxy, isUint8Array } from 'node:util/types';
3
+ import { hasErrorCode, RefusalError, snapshotJson } from '../environment/contract.js';
4
+ import { exactKeys } from '../exact-keys.js';
5
+ export const GUARD_INPUT_LIMITS = Object.freeze({ inputBytes: 65_536, invocations: 256, words: 4_096, wordBytes: 4_096, paths: 256, pathBytes: 4_096 });
6
+ const parsedInputs = new WeakSet();
7
+ export function isParsedGuardInput(value) {
8
+ return typeof value === 'object' && value !== null && parsedInputs.has(value);
9
+ }
10
+ const shown = (word) => shownGuardWord(word);
11
+ function refuse(reason) { throw new RefusalError(reason); }
12
+ /** A word as a refusal quotes it: JSON-escaped, with the controls a harness rejects in a reason escaped too, and bounded. */
13
+ export function shownGuardWord(word) {
14
+ const characters = [...word];
15
+ const quoted = JSON.stringify(characters.length > 80 ? `${characters.slice(0, 80).join('')}…` : word);
16
+ return quoted.replace(/[\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
17
+ }
18
+ function record(value) {
19
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
20
+ }
21
+ function text(value) {
22
+ return typeof value === 'string' && !/[\uD800-\uDFFF]/u.test(value) && !value.includes('\0');
23
+ }
24
+ function path(value) {
25
+ if (!value || /[\x00-\x1f\x7f]/.test(value) || Buffer.byteLength(value) > GUARD_INPUT_LIMITS.pathBytes)
26
+ refuse('Invalid or oversized guard path.');
27
+ return value;
28
+ }
29
+ function lex(command) {
30
+ if (/[\x00-\x08\x0b-\x1f\x7f]/.test(command))
31
+ refuse('Unsupported shell control character.');
32
+ const tokens = [];
33
+ const documents = [];
34
+ let awaitingDocument;
35
+ let words = 0;
36
+ let index = 0;
37
+ while (index < command.length) {
38
+ const char = command[index];
39
+ if (char === ' ' || char === '\t') {
40
+ index++;
41
+ continue;
42
+ }
43
+ if (char === '\n') {
44
+ index++;
45
+ for (const document of documents) {
46
+ let body = '';
47
+ let closed = false;
48
+ while (index <= command.length) {
49
+ const end = command.indexOf('\n', index);
50
+ const line = command.slice(index, end < 0 ? command.length : end);
51
+ const content = document.tabs ? line.replace(/^\t+/, '') : line;
52
+ index = end < 0 ? command.length + 1 : end + 1;
53
+ if (content === document.delimiter) {
54
+ closed = true;
55
+ break;
56
+ }
57
+ body += content + (end < 0 ? '' : '\n');
58
+ }
59
+ if (!closed)
60
+ refuse('Unterminated heredoc.');
61
+ if (!document.quoted && /[$`\\]/.test(body))
62
+ refuse('Dynamic heredoc expansion is unsupported.');
63
+ tokens.push({ kind: 'body', value: body });
64
+ }
65
+ documents.length = 0;
66
+ tokens.push({ kind: 'op', value: '\n' });
67
+ continue;
68
+ }
69
+ if (char === '#') {
70
+ const end = command.indexOf('\n', index);
71
+ tokens.push({ kind: 'comment', value: command.slice(index + 1, end < 0 ? command.length : end) });
72
+ index = end < 0 ? command.length : end;
73
+ continue;
74
+ }
75
+ const descriptor = /^\d+(?=[<>])/.exec(command.slice(index))?.[0];
76
+ if (descriptor) {
77
+ tokens.push({ kind: 'descriptor', value: descriptor });
78
+ index += descriptor.length;
79
+ continue;
80
+ }
81
+ // A brace is a group delimiter only as a word of its own; inside a word it is literal text
82
+ // unless it forms a brace expansion, which the word loop refuses.
83
+ const operator = /^(?:&&|\|\||<<-|<<|>>|>&|<&|<>|>\||[;&|<>()]|[{}](?=[ \t\n;&|<>()]|$))/.exec(command.slice(index))?.[0];
84
+ if (operator) {
85
+ if (awaitingDocument !== undefined)
86
+ refuse('Missing heredoc delimiter.');
87
+ tokens.push({ kind: 'op', value: operator });
88
+ if (operator === '<<' || operator === '<<-')
89
+ awaitingDocument = operator === '<<-';
90
+ index += operator.length;
91
+ continue;
92
+ }
93
+ // zsh, the default macOS shell, expands an unquoted word starting with `=` to a command's path.
94
+ const equals = command[index] === '=';
95
+ let value = '';
96
+ let quote = '';
97
+ let quoted = false;
98
+ let dynamic = false;
99
+ let status = false;
100
+ const braces = [];
101
+ while (index < command.length) {
102
+ const c = command[index];
103
+ if (!quote && /[ \t\n;&|<>()]/.test(c))
104
+ break;
105
+ if (c === "'" && quote !== '"' || c === '"' && quote !== "'") {
106
+ quoted = true;
107
+ quote = quote ? '' : c;
108
+ index++;
109
+ continue;
110
+ }
111
+ if (c === '\\' && quote !== "'") {
112
+ const next = command[++index];
113
+ if (next === undefined)
114
+ refuse('Incomplete shell escape.');
115
+ if (next === '\n') {
116
+ index++;
117
+ continue;
118
+ }
119
+ if (quote === '"' && !['$', '`', '"', '\\'].includes(next))
120
+ value += '\\';
121
+ value += next;
122
+ index++;
123
+ continue;
124
+ }
125
+ // `$?` is the previous command's exit status: digits, never a word, glob or command.
126
+ if (quote !== "'" && c === '$' && command[index + 1] === '?') {
127
+ status = true;
128
+ value += '$?';
129
+ index += 2;
130
+ continue;
131
+ }
132
+ // Bash and zsh expand `{a,b}` and `{1..3}` only; `@{u}` and `HEAD@{1}` stay literal. A comma
133
+ // or `..` anywhere inside, quoted or not, counts, which refuses a few literal spellings too.
134
+ if (!quote && c === '{')
135
+ braces.push(value.length + 1);
136
+ if (!quote && c === '}' && braces.length && /,|\.\./.test(value.slice(braces.pop()))) {
137
+ refuse(`Unsupported brace expansion in ${shown(value + c)}: the shell turns it into several words the guard does not see. Write each word out.`);
138
+ }
139
+ // A tilde expands only where a word starts, or after `=` or `:` in an assignment; `HEAD~1` stays literal.
140
+ if (quote !== "'" && (c === '$' || c === '`' || !quote && (/[*?\[]/.test(c) || c === '~' && (value === '' && !quoted || /[=:]$/.test(value)))))
141
+ dynamic = true;
142
+ value += c;
143
+ index++;
144
+ }
145
+ if (quote)
146
+ refuse('Unterminated shell quote.');
147
+ if (equals && value.length > 1)
148
+ dynamic = true;
149
+ if (++words > GUARD_INPUT_LIMITS.words || Buffer.byteLength(value) > GUARD_INPUT_LIMITS.wordBytes)
150
+ refuse('Guard word limit exceeded.');
151
+ tokens.push({ kind: 'word', value, quoted, dynamic, status });
152
+ if (awaitingDocument !== undefined) {
153
+ if (dynamic || status || !value)
154
+ refuse('Unsupported heredoc delimiter.');
155
+ documents.push({ delimiter: value, quoted, tabs: awaitingDocument });
156
+ awaitingDocument = undefined;
157
+ }
158
+ }
159
+ if (documents.length || awaitingDocument !== undefined)
160
+ refuse('Unterminated heredoc.');
161
+ return tokens;
162
+ }
163
+ const separators = new Set([';', '\n', '&&', '||', '|', '&']);
164
+ const redirects = new Set(['<', '>', '>>', '<<', '<<-', '>&', '<&', '<>', '>|']);
165
+ const wrappers = new Set(['timeout', 'nice', 'nohup', 'setsid', 'env', 'command', 'exec']);
166
+ const dynamicExecutables = new Set(['eval', 'source', '.', 'sh', 'bash', 'zsh', 'dash', 'ksh', 'fish', 'sudo', 'doas', 'xargs', 'builtin']);
167
+ // Builtins that move the shell as `cd` does, which only `cd` is judged as.
168
+ const directoryChangers = new Set(['cd', 'pushd', 'popd', 'chdir']);
169
+ const CD_REFUSAL = 'cd is judged only as `cd <dir> && <command>` (or `;`), alone in its command, with one literal directory that is absolute or starts '
170
+ + 'with ./ or ../. A bare name such as `cd api` may resolve through CDPATH, which the guard cannot see: write `cd ./api`, or `git -C api` for Git.';
171
+ // Writing to these discards or forwards output and reading /dev/null yields nothing, so they touch
172
+ // no file; only these exact spellings qualify.
173
+ const DEVICE_SINKS = new Set(['/dev/null', '/dev/stdout', '/dev/stderr']);
174
+ const reserved = new Set(['if', 'then', 'else', 'fi', 'for', 'while', 'until', 'do', 'done', 'case', 'esac', 'select', 'time', '!']);
175
+ function invocation(tokens, cwd, definitions) {
176
+ const words = [];
177
+ const redirectPaths = [];
178
+ const args = [];
179
+ for (let i = 0; i < tokens.length; i++) {
180
+ const token = tokens[i];
181
+ if (token.kind === 'comment' || token.kind === 'body') {
182
+ words.push({ role: token.kind === 'body' ? 'heredoc-body' : 'comment', value: token.value });
183
+ }
184
+ else if (token.kind === 'descriptor') {
185
+ if (tokens[i + 1]?.kind !== 'op' || !redirects.has(tokens[i + 1].value))
186
+ refuse('Descriptor lacks a redirect.');
187
+ }
188
+ else if (token.kind === 'op' && redirects.has(token.value)) {
189
+ const target = tokens[++i];
190
+ if (!target || target.kind !== 'word' || target.dynamic || target.status)
191
+ refuse('Missing or dynamic redirect target.');
192
+ words.push({ role: 'redirect-target', value: target.value });
193
+ if (!token.value.startsWith('<<') && !['>&', '<&'].includes(token.value)
194
+ && !(token.value === '<' ? target.value === '/dev/null' : DEVICE_SINKS.has(target.value))) {
195
+ redirectPaths.push({ target: path(target.value), write: token.value !== '<', truncate: token.value === '>' || token.value === '>|' });
196
+ }
197
+ else if (['>&', '<&'].includes(token.value) && !/^(?:\d+|-)$/.test(target.value))
198
+ refuse('Unsupported descriptor redirect.');
199
+ }
200
+ else if (token.kind !== 'word')
201
+ refuse('Unsupported shell operator.');
202
+ else
203
+ args.push(token);
204
+ }
205
+ // Definitions are data until invoked. Never guess the shell's alias expansion mode.
206
+ if (args[0]?.value === 'alias') {
207
+ for (const item of args.slice(1)) {
208
+ const match = /^([A-Za-z_][A-Za-z0-9_-]*)=(.*)$/s.exec(item.value);
209
+ if (!match || item.dynamic)
210
+ refuse('Unsupported alias definition.');
211
+ definitions.add(match[1]);
212
+ }
213
+ return { executable: null, argv: [], cwd, wrapped: false, words: [...words, ...args.map(({ value }) => ({ role: 'definition', value }))], redirects: redirectPaths };
214
+ }
215
+ const expanded = args.find((token) => token.dynamic);
216
+ if (expanded)
217
+ refuse(`Dynamic command construction is unsupported: ${shown(expanded.value)} holds a variable, command substitution, glob, home-directory tilde or zsh =command whose expansion the guard cannot see. Write the command and its paths out literally.`);
218
+ let cursor = 0;
219
+ const assignments = () => {
220
+ while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(args[cursor]?.value ?? '')) {
221
+ const value = args[cursor++].value;
222
+ words.push({ role: 'environment-assignment-value', value: value.slice(value.indexOf('=') + 1) });
223
+ }
224
+ };
225
+ assignments();
226
+ let wrapped = false;
227
+ while (cursor < args.length && wrappers.has(posix.basename(args[cursor].value))) {
228
+ const wrapper = posix.basename(args[cursor++].value);
229
+ wrapped = true;
230
+ const take = () => {
231
+ const token = args[cursor++];
232
+ if (!token)
233
+ refuse(`Missing ${wrapper} operand.`);
234
+ words.push({ role: 'wrapper-option-value', value: token.value });
235
+ return token;
236
+ };
237
+ while (args[cursor]?.value.startsWith('-')) {
238
+ const optionToken = take();
239
+ const option = optionToken.value;
240
+ if (option === '--')
241
+ break;
242
+ const [flag, attached] = option.split(/=(.*)/s);
243
+ const valueFlags = wrapper === 'timeout' ? ['-s', '--signal', '-k', '--kill-after']
244
+ : wrapper === 'nice' ? ['-n', '--adjustment']
245
+ : wrapper === 'env' ? ['-C', '--chdir', '-u', '--unset']
246
+ : wrapper === 'exec' ? ['-a'] : [];
247
+ const plainFlags = wrapper === 'timeout' ? ['--foreground', '--preserve-status', '-v', '--verbose']
248
+ : wrapper === 'env' ? ['-i', '--ignore-environment']
249
+ : wrapper === 'setsid' ? ['-f', '--fork', '-w', '--wait']
250
+ : wrapper === 'command' ? ['-p'] : wrapper === 'exec' ? ['-c', '-l'] : [];
251
+ if (valueFlags.includes(flag)) {
252
+ const valueToken = attached === undefined ? take() : optionToken;
253
+ const value = attached === undefined ? valueToken.value : attached;
254
+ if (!value)
255
+ refuse(`Empty ${wrapper} option value.`);
256
+ if (wrapper === 'env' && ['-C', '--chdir'].includes(flag)) {
257
+ // Every path the command names resolves from this directory, so it must be known.
258
+ if (valueToken.status)
259
+ refuse(`env ${flag} holding $? runs the command in a directory the guard cannot see. Write the directory out.`);
260
+ // env changes directory as the system does, resolving `..` from where a link leads.
261
+ if (climbsOutOfName(value))
262
+ refuse(`env ${flag} ${shown(value)} climbs out of a named directory with .., which resolves from wherever a link there leads. Write the directory without it.`);
263
+ cwd = path(posix.resolve(cwd, value));
264
+ }
265
+ }
266
+ else if (!(attached === undefined && (plainFlags.includes(flag) || wrapper === 'nice' && /^-\d+$/.test(flag)))) {
267
+ refuse(`Unsupported ${wrapper} option.`);
268
+ }
269
+ }
270
+ if (wrapper === 'timeout' && !/^\d+(?:\.\d+)?[smhd]?$/.test(take().value))
271
+ refuse('Invalid timeout duration.');
272
+ if (wrapper === 'env')
273
+ assignments();
274
+ if (cursor >= args.length)
275
+ refuse(`Missing ${wrapper} executable.`);
276
+ }
277
+ const executable = args[cursor]?.value ?? null;
278
+ if (executable !== null) {
279
+ if (!executable || args[cursor].status || definitions.has(executable))
280
+ refuse('Unsupported executable or defined command invocation.');
281
+ if (directoryChangers.has(executable))
282
+ refuse(CD_REFUSAL);
283
+ if (dynamicExecutables.has(posix.basename(executable)))
284
+ refuse(`${shown(executable)} runs a command or script built from its arguments, which the guard does not parse. Run the command itself.`);
285
+ if (reserved.has(executable))
286
+ refuse(`Shell control flow (${executable}) is not parsed by the guard. Run each command on its own.`);
287
+ words.push({ role: 'executable', value: executable });
288
+ cursor++;
289
+ }
290
+ const operands = args.slice(cursor);
291
+ const argv = operands.map(({ value }) => value);
292
+ words.push(...argv.map((value) => ({ role: 'argument', value })));
293
+ return { executable, argv, cwd, wrapped, words, redirects: redirectPaths, ...(args.some((token) => token.status) ? { expandsStatus: true } : {}) };
294
+ }
295
+ /**
296
+ * Whether `..` follows a named component, as in `a/../b`. The system resolves such a `..` from where
297
+ * a link named `a` leads, which the spelling cannot show; only a leading `..` leaves a directory the
298
+ * guard has already inspected as the working directory.
299
+ */
300
+ function climbsOutOfName(directory) {
301
+ return directory.split('/').some((name, index, names) => name === '..' && names.slice(0, index).some((before) => !['', '.', '..'].includes(before)));
302
+ }
303
+ /** The directory a command that is exactly `cd <dir>` moves to, as written, or undefined for any other command. */
304
+ function changedDirectory(tokens, definitions) {
305
+ if (tokens[0]?.kind !== 'word' || tokens[0].value !== 'cd' || definitions.has('cd'))
306
+ return undefined;
307
+ const operands = tokens.slice(1).filter((token) => token.kind !== 'comment');
308
+ if (operands[0]?.kind === 'word' && operands[0].value === '--')
309
+ operands.shift();
310
+ const target = operands.length === 1 && operands[0].kind === 'word' && !operands[0].dynamic && !operands[0].status ? operands[0].value : undefined;
311
+ // Both shells search CDPATH before the working directory for any other relative spelling.
312
+ if (target === undefined || !/^(?:\/|\.\.?(?:\/|$))/.test(target))
313
+ refuse(CD_REFUSAL);
314
+ // Both shells resolve `cd` by spelling unless set to follow links (`set -P`, zsh's chase_links),
315
+ // which the guard cannot see, as it cannot see CDPATH.
316
+ if (climbsOutOfName(target))
317
+ refuse(`${CD_REFUSAL} A .. after a directory name is not: a shell set to follow links resolves it from wherever a link there leads.`);
318
+ return target;
319
+ }
320
+ function parseShell(command, cwd) {
321
+ const tokens = lex(command);
322
+ const result = [];
323
+ const definitions = new Set();
324
+ let current = [];
325
+ let braces = 0;
326
+ let needsCommand = false;
327
+ let states = [{ directory: cwd, ok: true }];
328
+ // The operator before the current command: `&&`, `||`, `|`, or undefined where it always runs.
329
+ let gate;
330
+ // Where the previous command ran, which is where a pipeline's next stage runs.
331
+ let ran = [cwd];
332
+ // A list ending in `&` runs in a background subshell, so its cd moves nothing after it.
333
+ let list = states;
334
+ const append = (item) => {
335
+ if (result.length === GUARD_INPUT_LIMITS.invocations)
336
+ refuse('Guard invocation limit exceeded.');
337
+ result.push(item);
338
+ };
339
+ const flush = (separator) => {
340
+ const target = current.length ? changedDirectory(current, definitions) : undefined;
341
+ // A pipeline or background job runs `cd` in a subshell in bash, and the last stage of a pipeline in the current shell in zsh.
342
+ if (target !== undefined && (gate === '|' || separator === '|' || separator === '&'))
343
+ refuse(`${CD_REFUSAL} A cd in a pipeline or background job is not.`);
344
+ if (current.length && gate === '|')
345
+ for (const directory of ran)
346
+ append(invocation(current, directory, definitions));
347
+ else if (current.length) {
348
+ const runs = states.filter((state) => gate === undefined || state.ok === (gate === '&&'));
349
+ ran = [...new Set(runs.map((state) => state.directory))];
350
+ const next = states.filter((state) => !runs.includes(state));
351
+ if (target !== undefined) {
352
+ const moved = [...new Set(ran.map((directory) => path(posix.resolve(directory, target))))];
353
+ // The directory itself is inspected as each later command's working directory is.
354
+ for (const directory of moved)
355
+ append({ executable: null, argv: [], cwd: directory, wrapped: false, words: [{ role: 'directory', value: target }], redirects: [] });
356
+ next.push(...ran.map((directory) => ({ directory, ok: false })), ...moved.map((directory) => ({ directory, ok: true })));
357
+ }
358
+ else {
359
+ for (const directory of ran)
360
+ append(invocation(current, directory, definitions));
361
+ next.push(...ran.flatMap((directory) => [{ directory, ok: true }, { directory, ok: false }]));
362
+ }
363
+ states = [...new Map(next.map((state) => [`${state.ok}\0${state.directory}`, state])).values()];
364
+ }
365
+ current = [];
366
+ if (separator === '&')
367
+ states = list;
368
+ if (separator === '&&' || separator === '||' || separator === '|')
369
+ gate = separator;
370
+ else {
371
+ gate = undefined;
372
+ list = states;
373
+ }
374
+ };
375
+ for (let i = 0; i < tokens.length; i++) {
376
+ const token = tokens[i];
377
+ const functionStyle = token.value === 'function' && token.kind === 'word';
378
+ if (!current.length && token.kind === 'word' && (functionStyle || tokens[i + 1]?.value === '(' && tokens[i + 2]?.value === ')')) {
379
+ const name = functionStyle ? tokens[++i]?.value : token.value;
380
+ if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
381
+ refuse('Invalid function definition.');
382
+ if (tokens[i + 1]?.value === '(' && tokens[i + 2]?.value === ')')
383
+ i += 2;
384
+ if (tokens[++i]?.value !== '{')
385
+ refuse('Unsupported function body.');
386
+ const body = [{ role: 'definition', value: name }];
387
+ let depth = 1;
388
+ while (++i < tokens.length && depth) {
389
+ const item = tokens[i];
390
+ // A brace is a reserved word only where a command starts; elsewhere bash reads it as an
391
+ // argument, so counting it would hide the commands after the body inside it.
392
+ if (item.kind === 'op' && ['{', '}'].includes(item.value)
393
+ && !(tokens[i - 1]?.kind === 'op' && ['{', '}', ';', '\n', '&&', '||', '|', '&'].includes(tokens[i - 1].value)))
394
+ refuse('Unsupported function body.');
395
+ if (item.kind === 'op' && item.value === '{')
396
+ depth++;
397
+ if (item.kind === 'op' && item.value === '}')
398
+ depth--;
399
+ body.push({ role: 'definition', value: item.value });
400
+ }
401
+ if (depth)
402
+ refuse('Unterminated function definition.');
403
+ i--;
404
+ definitions.add(name);
405
+ append({ executable: null, argv: [], cwd: states[0].directory, wrapped: false, words: body, redirects: [] });
406
+ needsCommand = false;
407
+ continue;
408
+ }
409
+ if (token.kind === 'op' && separators.has(token.value)) {
410
+ // A newline after `&&`, `||` or `|` continues the list, as a line continuation would.
411
+ if (token.value === '\n' && needsCommand)
412
+ continue;
413
+ if (['&&', '||', '|', '&'].includes(token.value) && !current.length)
414
+ refuse('Missing shell command before separator.');
415
+ flush(token.value);
416
+ if (token.value !== '\n')
417
+ needsCommand = ['&&', '||', '|'].includes(token.value);
418
+ }
419
+ else if (token.kind === 'op' && token.value === '{') {
420
+ if (current.length)
421
+ refuse('A standalone { is parsed only where it opens a command group, `{ command; }`. Quote a literal brace.');
422
+ braces++;
423
+ }
424
+ else if (token.kind === 'op' && token.value === '}') {
425
+ if (!braces || current.length || needsCommand)
426
+ refuse('Malformed brace group.');
427
+ braces--;
428
+ }
429
+ else {
430
+ current.push(token);
431
+ if (token.kind === 'word')
432
+ needsCommand = false;
433
+ }
434
+ }
435
+ if (braces || needsCommand)
436
+ refuse('Incomplete shell command.');
437
+ flush();
438
+ if (!result.length)
439
+ refuse('Empty shell command.');
440
+ // Definitions can affect commands regardless of textual order in compound forms.
441
+ if (result.some((item) => item.executable !== null && definitions.has(item.executable) || definitions.has('cd') && item.words[0]?.role === 'directory')) {
442
+ refuse('Defined command invocation is unsupported.');
443
+ }
444
+ return result;
445
+ }
446
+ function patchPaths(command) {
447
+ const lines = command.trimEnd().split('\n');
448
+ if (lines.shift() !== '*** Begin Patch' || lines.pop() !== '*** End Patch')
449
+ refuse('Malformed patch envelope.');
450
+ const paths = [];
451
+ const removed = [];
452
+ let update = false;
453
+ for (const line of lines) {
454
+ const header = /^\*\*\* (Add File|Update File|Delete File|Move to): (.+)$/.exec(line);
455
+ if (header) {
456
+ if (header[1] === 'Move to' && !update)
457
+ refuse('Patch move lacks an update source.');
458
+ if (header[1] === 'Move to')
459
+ removed.push(paths.at(-1));
460
+ if (header[1] === 'Delete File')
461
+ removed.push(path(header[2]));
462
+ paths.push(path(header[2]));
463
+ update = header[1] === 'Update File';
464
+ }
465
+ else if (!paths.length || !(line.startsWith('+') || line.startsWith('-') || line.startsWith(' ') || line.startsWith('@@') || line === '*** End of File' || line === '')) {
466
+ refuse('Unsupported patch structure.');
467
+ }
468
+ }
469
+ if (!paths.length)
470
+ refuse('Patch has no targets.');
471
+ return { paths, removed };
472
+ }
473
+ function captureBytes(input) {
474
+ const prototype = Object.getPrototypeOf(input);
475
+ if (prototype !== Uint8Array.prototype && prototype !== Buffer.prototype)
476
+ refuse('Foreign guard byte prototype.');
477
+ // Native accessors bypass shadow properties; copying happens only after own-shape validation.
478
+ const length = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), 'byteLength').get.call(input);
479
+ if (length > GUARD_INPUT_LIMITS.inputBytes)
480
+ refuse('Guard input byte limit exceeded.');
481
+ const descriptors = Object.getOwnPropertyDescriptors(input);
482
+ const ownKeys = Reflect.ownKeys(descriptors);
483
+ if (ownKeys.length !== length || ownKeys.some((key) => typeof key !== 'string' || !/^(?:0|[1-9]\d*)$/.test(key)
484
+ || Number(key) >= length || !('value' in descriptors[key])))
485
+ refuse('Guard bytes must have only native indexed data.');
486
+ const copy = new Uint8Array(length);
487
+ Uint8Array.prototype.set.call(copy, input);
488
+ return copy;
489
+ }
490
+ /** Capture once: the evaluator never consumes native payloads or reparses shell text. */
491
+ export function parseGuardInput(input) {
492
+ try {
493
+ let decoded = input;
494
+ if (typeof input === 'object' && input !== null && isProxy(input))
495
+ refuse('Guard input must be own data.');
496
+ if (isUint8Array(input)) {
497
+ const bytes = captureBytes(input);
498
+ try {
499
+ decoded = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
500
+ }
501
+ catch (error) {
502
+ if (!hasErrorCode(error, 'ERR_ENCODING_INVALID_ENCODED_DATA'))
503
+ throw error;
504
+ refuse('Guard input is not valid UTF-8.');
505
+ }
506
+ }
507
+ if (typeof decoded === 'string') {
508
+ if (/[\uD800-\uDFFF]/u.test(decoded) || Buffer.byteLength(decoded) > GUARD_INPUT_LIMITS.inputBytes)
509
+ refuse('Invalid or oversized guard input.');
510
+ try {
511
+ decoded = JSON.parse(decoded);
512
+ }
513
+ catch (error) {
514
+ if (!(error instanceof SyntaxError))
515
+ throw error;
516
+ refuse('Guard input is not valid JSON.');
517
+ }
518
+ }
519
+ const captured = snapshotJson(decoded);
520
+ if (!record(captured) || Buffer.byteLength(JSON.stringify(captured)) > GUARD_INPUT_LIMITS.inputBytes)
521
+ refuse('Guard input must be bounded own data.');
522
+ if (!exactKeys(captured, ['hook_event_name', 'tool_name', 'tool_input', 'cwd']) || captured.hook_event_name !== 'PreToolUse'
523
+ || !text(captured.cwd) || !posix.isAbsolute(path(captured.cwd)) || !record(captured.tool_input))
524
+ refuse('Malformed normalized guard payload.');
525
+ const cwd = captured.cwd;
526
+ const tool = captured.tool_name;
527
+ const fields = captured.tool_input;
528
+ let paths = [];
529
+ let removed = [];
530
+ let invocations = [];
531
+ let content;
532
+ if (tool === 'Bash' || tool === 'apply_patch') {
533
+ // Native Bash calls carry execution options beside the command; they are type-checked
534
+ // and otherwise ignored, because the command text alone decides what runs.
535
+ const options = tool === 'Bash' ? {
536
+ description: text, timeout: (value) => typeof value === 'number' && Number.isFinite(value),
537
+ run_in_background: (value) => typeof value === 'boolean', dangerouslyDisableSandbox: (value) => typeof value === 'boolean',
538
+ } : {};
539
+ if (!exactKeys(fields, ['command'], Object.keys(options)) || !text(fields.command) || !fields.command
540
+ || Object.entries(options).some(([key, valid]) => Object.hasOwn(fields, key) && !valid(fields[key])))
541
+ refuse('Malformed command payload.');
542
+ if (tool === 'Bash')
543
+ invocations = parseShell(fields.command, cwd);
544
+ else {
545
+ ({ paths, removed } = patchPaths(fields.command));
546
+ content = { kind: 'patch', text: fields.command };
547
+ }
548
+ }
549
+ else if (tool === 'NotebookEdit') {
550
+ if (!exactKeys(fields, ['notebook_path', 'new_source'], ['cell_id', 'cell_type', 'edit_mode'])
551
+ || !text(fields.notebook_path) || !posix.isAbsolute(fields.notebook_path)
552
+ || Object.values(fields).some(value => !text(value))
553
+ || fields.cell_type !== undefined && !['code', 'markdown'].includes(String(fields.cell_type))
554
+ || fields.edit_mode !== undefined && !['replace', 'insert', 'delete'].includes(String(fields.edit_mode)))
555
+ refuse('Malformed notebook payload.');
556
+ paths = [path(fields.notebook_path)];
557
+ }
558
+ else if (tool === 'Edit' || tool === 'Write') {
559
+ if (!exactKeys(fields, ['file_path'], tool === 'Edit' ? ['old_string', 'new_string', 'replace_all'] : ['content'])
560
+ || !text(fields.file_path))
561
+ refuse('Malformed file payload.');
562
+ if (Object.entries(fields).some(([key, value]) => key === 'replace_all' ? typeof value !== 'boolean' : !text(value)))
563
+ refuse('Malformed file field.');
564
+ paths = [path(fields.file_path)];
565
+ // Writing nothing empties the file, which is the shell's `: > file`.
566
+ if (tool === 'Write' && fields.content === '')
567
+ removed = [...paths];
568
+ // OpenCode's plugin passes new text only for the configuration file, so a payload may lack it.
569
+ if (tool === 'Write' && typeof fields.content === 'string')
570
+ content = { kind: 'write', path: paths[0], content: fields.content };
571
+ if (tool === 'Edit' && typeof fields.old_string === 'string' && typeof fields.new_string === 'string') {
572
+ content = { kind: 'replace', path: paths[0], old: fields.old_string, new: fields.new_string, all: fields.replace_all === true };
573
+ }
574
+ }
575
+ else
576
+ refuse('Unsupported guard tool.');
577
+ if (paths.length + invocations.reduce((sum, item) => sum + item.redirects.length, 0) > GUARD_INPUT_LIMITS.paths)
578
+ refuse('Guard path limit exceeded.');
579
+ for (const item of invocations) {
580
+ Object.freeze(item.argv);
581
+ item.words.forEach(Object.freeze);
582
+ Object.freeze(item.words);
583
+ item.redirects.forEach(Object.freeze);
584
+ Object.freeze(item.redirects);
585
+ Object.freeze(item);
586
+ }
587
+ const result = { status: 'parsed', tool, cwd, paths, removed, invocations,
588
+ ...content ? { content } : {} };
589
+ parsedInputs.add(result);
590
+ return result;
591
+ }
592
+ catch (error) {
593
+ return { status: 'unavailable', reason: error instanceof RefusalError ? error.message : 'Guard input capture failed.' };
594
+ }
595
+ }
@@ -0,0 +1,44 @@
1
+ export interface GuardHandlerIdentity {
2
+ readonly root: string;
3
+ readonly rootDevice: string;
4
+ readonly rootInode: string;
5
+ readonly configPath: string;
6
+ readonly configSha256: string;
7
+ readonly configDevice: string;
8
+ readonly configInode: string;
9
+ readonly node: string;
10
+ readonly nodeSha256: string;
11
+ /** The stat fingerprint of the Node file `sync` hashed; while it holds, the guard skips re-hashing. */
12
+ readonly nodeFingerprint: string;
13
+ readonly module: string;
14
+ readonly moduleSha256: string;
15
+ }
16
+ export declare const GUARD_LOCAL_PATHS: Readonly<{
17
+ readonly directory: '.cockpit/local';
18
+ readonly identity: '.cockpit/local/guard-identity';
19
+ readonly generatedConfig: '.cockpit/local/guard-config';
20
+ readonly ignore: '.cockpit/local/.gitignore';
21
+ }>;
22
+ export declare function encodeGuardLocalIdentity(identity: GuardHandlerIdentity): string;
23
+ /** The exact identity the bytes carry, or undefined when they are not a complete current-format file. */
24
+ export declare function parseGuardLocalIdentity(bytes: string): GuardHandlerIdentity | undefined;
25
+ /**
26
+ * Publish the identity after the tracked surfaces it authenticates, with the configuration text it
27
+ * names written first; the reason names what failed.
28
+ */
29
+ export declare function writeGuardLocalIdentity(root: string, identity: GuardHandlerIdentity, configText: string): Promise<{
30
+ readonly kind: 'complete';
31
+ } | {
32
+ readonly kind: 'refusal';
33
+ readonly message: string;
34
+ }>;
35
+ /** Replace machine-local files under `.cockpit/local`, a directory that ignores itself; the reason names what failed. */
36
+ export declare function writeCockpitLocalFiles(root: string, files: readonly Readonly<{
37
+ name: string;
38
+ bytes: string;
39
+ }>[]): Promise<{
40
+ readonly kind: 'complete';
41
+ } | {
42
+ readonly kind: 'refusal';
43
+ readonly message: string;
44
+ }>;