@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,397 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { realpath } from 'node:fs/promises';
3
+ import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
4
+ import { loadCockpitConfig, consumeLoadedConfigProjection } from '../config/load.js';
5
+ import {} from './application/health.js';
6
+ import { decodeLifecycleRecord } from './application/lifecycle.js';
7
+ import {} from './application/lifecycle-context.js';
8
+ import {} from './application/logs.js';
9
+ import { publishLifecycleRecord } from './application/lifecycle-state.js';
10
+ import {} from './application/services.js';
11
+ import { createLifecycleCommandAdapters } from './lifecycle-composition.js';
12
+ import { deriveEntrypoints, observeRetainedPortFamily, projectRetainedPortFamilyResult } from './ports.js';
13
+ import { DOCKER_CLIENT_ENVIRONMENT, ENVIRONMENT_CODES, RefusalError } from './contract.js';
14
+ import { loadServedSiblings, servedSiblingVariables } from './sibling-checkouts.js';
15
+ import { DeclarationRefusalError, runtimeNotDeclared, serviceNotDeclared } from './declaration.js';
16
+ import { admitRuntimeServiceHealth, revalidateRuntimeService, } from './runtime.js';
17
+ import { resolveDockerExecutable } from './executable.js';
18
+ import { STABILITY_CAPTURE_OUTPUT_LIMIT_BYTES, authenticateStabilityCapture, captureRuntimeEvidence, createEnvironmentSnapshot, createStabilityCapturePlan, } from './evidence.js';
19
+ import { readState } from './state.js';
20
+ import { readBoundedFile } from '../bounded-read.js';
21
+ const COMPOSE_BYTE_LIMIT = 1_048_576;
22
+ const DEFAULT_OUTPUT_LIMIT = 1_048_576;
23
+ function freezeJson(value) {
24
+ if (Array.isArray(value))
25
+ return value.map(freezeJson);
26
+ if (value !== null && typeof value === 'object') {
27
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, freezeJson(child)]));
28
+ }
29
+ return value;
30
+ }
31
+ function captureInput(value) {
32
+ try {
33
+ if (!value || typeof value !== 'object' || Array.isArray(value))
34
+ return undefined;
35
+ const descriptors = Object.getOwnPropertyDescriptors(value);
36
+ const required = ['configPath', 'env', 'sessionId', 'timeoutMs'];
37
+ const optional = ['outputLimitBytes', 'signal', 'runtimeMeasurementProbeAuthority'];
38
+ const keys = Reflect.ownKeys(descriptors);
39
+ if (keys.some((key) => typeof key !== 'string' || !required.includes(key) && !optional.includes(key))
40
+ || required.some((key) => !descriptors[key] || !('value' in descriptors[key]))
41
+ || keys.some((key) => typeof key === 'string' && !('value' in descriptors[key])))
42
+ return undefined;
43
+ const configPath = descriptors.configPath.value;
44
+ const rawEnvironment = descriptors.env.value;
45
+ const sessionId = descriptors.sessionId.value;
46
+ const timeoutMs = descriptors.timeoutMs.value;
47
+ const outputLimitBytes = descriptors.outputLimitBytes?.value ?? DEFAULT_OUTPUT_LIMIT;
48
+ const signal = descriptors.signal?.value;
49
+ const runtimeMeasurementProbeAuthority = descriptors.runtimeMeasurementProbeAuthority?.value;
50
+ if (typeof configPath !== 'string' || configPath.length === 0 || configPath.includes('\0')
51
+ || typeof sessionId !== 'string' || sessionId.length === 0 || sessionId.includes('\0')
52
+ || typeof timeoutMs !== 'number' || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000
53
+ || typeof outputLimitBytes !== 'number' || !Number.isSafeInteger(outputLimitBytes) || outputLimitBytes < 1 || outputLimitBytes > 16_777_216
54
+ || signal !== undefined && !(signal instanceof AbortSignal)
55
+ || !rawEnvironment || typeof rawEnvironment !== 'object' || Array.isArray(rawEnvironment))
56
+ return undefined;
57
+ const env = {};
58
+ for (const [name, entry] of Object.entries(rawEnvironment)) {
59
+ if (name.length === 0 || name.includes('\0') || name.includes('=')
60
+ || entry !== undefined && (typeof entry !== 'string' || entry.includes('\0')))
61
+ return undefined;
62
+ env[name] = entry;
63
+ }
64
+ return {
65
+ configPath: resolve(configPath),
66
+ env,
67
+ sessionId,
68
+ timeoutMs,
69
+ outputLimitBytes,
70
+ ...(signal ? { signal } : {}),
71
+ ...(runtimeMeasurementProbeAuthority === undefined ? {} : { runtimeMeasurementProbeAuthority }),
72
+ };
73
+ }
74
+ catch { // A throwing accessor or proxy trap makes the input malformed; the caller refuses it.
75
+ return undefined;
76
+ }
77
+ }
78
+ function runtimeEnvironment(env) {
79
+ return Object.fromEntries(Object.entries(env)
80
+ .filter((entry) => DOCKER_CLIENT_ENVIRONMENT.has(entry[0]) && typeof entry[1] === 'string'));
81
+ }
82
+ function confined(root, path) {
83
+ const relation = relative(root, path);
84
+ return relation === '' || relation !== '..' && !relation.startsWith(`..${sep}`) && !isAbsolute(relation);
85
+ }
86
+ async function composeArtifact(root, reference) {
87
+ if (typeof reference !== 'string' || reference.length === 0 || reference.includes('\0'))
88
+ return undefined;
89
+ const expected = resolve(root, reference);
90
+ if (!confined(root, expected))
91
+ return undefined;
92
+ try {
93
+ const canonicalPath = await realpath(expected);
94
+ if (canonicalPath !== expected)
95
+ return undefined;
96
+ const read = readBoundedFile(canonicalPath, COMPOSE_BYTE_LIMIT);
97
+ if (read.kind !== 'complete')
98
+ return undefined;
99
+ const bytes = read.bytes;
100
+ const source = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
101
+ return {
102
+ reference,
103
+ canonicalPath,
104
+ source,
105
+ sha256: createHash('sha256').update(bytes).digest('hex'),
106
+ };
107
+ }
108
+ catch {
109
+ // An artifact that cannot be read carries no authority; callers refuse it as unavailable compose artifacts.
110
+ return undefined;
111
+ }
112
+ }
113
+ async function composeArtifacts(root, references) {
114
+ const artifacts = [];
115
+ for (const reference of references) {
116
+ const artifact = await composeArtifact(root, reference);
117
+ if (!artifact)
118
+ return undefined;
119
+ artifacts.push(artifact);
120
+ }
121
+ return artifacts.length > 0 ? artifacts : undefined;
122
+ }
123
+ function sameSource(left, right) {
124
+ return left.path === right.path && left.byteLength === right.byteLength && left.sha256 === right.sha256;
125
+ }
126
+ async function authenticateService(topology, service, containerId, execute, signal) {
127
+ const revalidated = await revalidateRuntimeService({
128
+ topology, service, expectedContainerId: containerId,
129
+ execute: (step) => execute(step, signal ? { signal } : undefined),
130
+ });
131
+ return revalidated.kind === 'observed' ? admitRuntimeServiceHealth(revalidated.observation) : undefined;
132
+ }
133
+ function exactCompletedTopology(context) {
134
+ const topologyServices = context.topology.services.map(({ id }) => id).sort();
135
+ const completedServices = context.lifecycle.completedServices.map(({ service }) => service).sort();
136
+ return topologyServices.length === completedServices.length
137
+ && topologyServices.every((service, index) => service === completedServices[index]);
138
+ }
139
+ export function createReadCommandAdapters(rawInput, execute, executeForeground) {
140
+ const input = captureInput(rawInput);
141
+ // `refuse` receives the lifecycle's own refusal, such as an unmeasured runtime, so a caller can
142
+ // report it instead of a generic unavailable context.
143
+ // A service verb may act on a stack whose start stopped part-way: rebuilding the service that
144
+ // failed is how its fixed code gets running. Every other reader needs a started service.
145
+ const prepare = async (signal, refuse, partial = false) => {
146
+ if (!input || typeof execute !== 'function')
147
+ throw new RefusalError('read command composition is unavailable');
148
+ const lifecycle = createLifecycleCommandAdapters({
149
+ configPath: input.configPath,
150
+ env: input.env,
151
+ sessionId: input.sessionId,
152
+ timeoutMs: input.timeoutMs,
153
+ ...(input.runtimeMeasurementProbeAuthority === undefined ? {} : {
154
+ runtimeMeasurementProbeAuthority: input.runtimeMeasurementProbeAuthority,
155
+ }),
156
+ }, (step) => execute(step, signal ? { signal } : undefined));
157
+ const loaded = await lifecycle.load({ configPath: input.configPath });
158
+ if (loaded.kind === 'refusal' && loaded.diagnostics.some(({ code }) => code === ENVIRONMENT_CODES.ENVIRONMENT_NOT_DECLARED)) {
159
+ throw new DeclarationRefusalError(runtimeNotDeclared(input.configPath));
160
+ }
161
+ if (loaded.kind !== 'complete') {
162
+ if (refuse && loaded.diagnostics.length > 0)
163
+ refuse(loaded.diagnostics);
164
+ throw new RefusalError('authenticated lifecycle context is unavailable');
165
+ }
166
+ const context = loaded.result;
167
+ const configurationResult = await loadCockpitConfig(input.configPath);
168
+ if (configurationResult.kind !== 'complete')
169
+ throw new RefusalError('configuration projection is unavailable');
170
+ const configuration = consumeLoadedConfigProjection(configurationResult.result);
171
+ if (!configuration || !sameSource(context.source, configuration.source)) {
172
+ throw new RefusalError('configuration authority does not match');
173
+ }
174
+ const checkoutRoot = dirname(configuration.source.path);
175
+ const references = configuration.config.environment.compose_files;
176
+ const composeFiles = await composeArtifacts(checkoutRoot, references);
177
+ if (!composeFiles)
178
+ throw new RefusalError('compose artifacts are unavailable');
179
+ const retained = await observeRetainedPortFamily({ namespace: context.namespace, stackId: context.stackId,
180
+ holderId: context.holderId, checkoutId: context.checkoutId, config: context.ports });
181
+ if (retained.kind !== 'complete')
182
+ throw new RefusalError('retained port authority is unavailable');
183
+ const entrypoints = deriveEntrypoints({ lease: retained.lease, declarations: context.entrypointDerivatives });
184
+ const ports = await projectRetainedPortFamilyResult({ retainedResult: retained, entrypointResult: entrypoints });
185
+ if (!ports)
186
+ throw new RefusalError('retained port projection is unavailable');
187
+ const readiness = await lifecycle.withRuntimeReadiness(context, async (value) => value, ports, refuse);
188
+ if (!readiness)
189
+ throw new RefusalError('authenticated runtime topology is unavailable');
190
+ if (readiness.topology.cwd !== checkoutRoot
191
+ || readiness.topology.stackId !== context.stackId
192
+ || readiness.topology.configSha256 !== context.runtimeSha256) {
193
+ throw new RefusalError('configuration and runtime authority do not match');
194
+ }
195
+ const lifecycleRead = await readState(context.namespace.paths.lifecycle(context.stackId));
196
+ if (lifecycleRead.kind !== 'complete')
197
+ throw new RefusalError('lifecycle state is unavailable');
198
+ const lifecycleValue = decodeLifecycleRecord(lifecycleRead.value, context)?.value;
199
+ if (!lifecycleValue)
200
+ throw new RefusalError('lifecycle state is malformed');
201
+ if (lifecycleValue.teardownComplete)
202
+ throw new RefusalError('down removed the stack');
203
+ if (lifecycleValue.completedServices.length === 0 && !partial)
204
+ throw new RefusalError('lifecycle state is malformed');
205
+ if (lifecycleValue.holderId !== context.holderId)
206
+ throw new RefusalError('lifecycle holder authority changed');
207
+ const env = runtimeEnvironment(input.env);
208
+ const executable = await resolveDockerExecutable(input.env);
209
+ if (executable.state !== 'available') {
210
+ throw new RefusalError(`runtime executable authority changed: ${executable.reason}`);
211
+ }
212
+ if (executable.value.sha256 !== readiness.topology.executableSha256) {
213
+ throw new RefusalError('runtime executable authority changed');
214
+ }
215
+ const confirmedComposeFiles = await composeArtifacts(checkoutRoot, references);
216
+ if (!confirmedComposeFiles || JSON.stringify(confirmedComposeFiles) !== JSON.stringify(composeFiles)) {
217
+ throw new RefusalError('compose artifacts changed during runtime observation');
218
+ }
219
+ // The stack interpolates the checkout `use` recorded, so reading it must name the same one.
220
+ const served = await loadServedSiblings(checkoutRoot, configuration.config);
221
+ if (served.kind === 'refusal')
222
+ throw new RefusalError(served.diagnostic.message);
223
+ const ownComposeAuthority = {
224
+ ownComposeFiles: true,
225
+ cockpitId: context.cockpitId,
226
+ stackId: context.stackId,
227
+ portLeaseProjection: ports,
228
+ siblingDirectories: servedSiblingVariables(configuration.config, served.siblings),
229
+ };
230
+ return {
231
+ run: context,
232
+ ownComposeAuthority,
233
+ configuration,
234
+ namespace: context.namespace,
235
+ cockpitId: context.cockpitId,
236
+ stackId: context.stackId,
237
+ holderId: context.holderId,
238
+ topology: readiness.topology,
239
+ lifecycleRecord: lifecycleRead.value,
240
+ lifecycle: lifecycleValue,
241
+ executable,
242
+ composeFiles,
243
+ env,
244
+ };
245
+ };
246
+ const observeLogs = async (request) => {
247
+ if (!input || resolve(request.configPath) !== input.configPath)
248
+ throw new RefusalError('log configuration path does not match');
249
+ const context = await prepare(input.signal, (diagnostics) => { throw new DeclarationRefusalError(diagnostics[0]); });
250
+ const declared = context.topology.services.map(({ id }) => id);
251
+ if (!declared.includes(request.service))
252
+ throw new DeclarationRefusalError(serviceNotDeclared(request.service, declared));
253
+ const completed = context.lifecycle.completedServices.find(({ service }) => service === request.service);
254
+ if (!completed) {
255
+ throw new RefusalError('requested service is not in the selected running stack');
256
+ }
257
+ const evidence = await authenticateService(context.topology, completed.service, completed.containerId, execute, input.signal);
258
+ if (!evidence)
259
+ throw new RefusalError('requested service identity is unavailable');
260
+ const after = await readState(context.namespace.paths.lifecycle(context.stackId));
261
+ if (after.kind !== 'complete' || JSON.stringify(after.value) !== JSON.stringify(context.lifecycleRecord)) {
262
+ throw new RefusalError('lifecycle state changed during log observation');
263
+ }
264
+ return {
265
+ ownComposeAuthority: context.ownComposeAuthority,
266
+ request: {
267
+ runtime: 'docker-compose',
268
+ executable: context.executable.value,
269
+ cwd: context.topology.cwd,
270
+ stackId: context.stackId,
271
+ configSha256: context.configuration.runtimeSha256,
272
+ batchId: context.topology.batchId,
273
+ containerId: evidence.containerId,
274
+ runtimeVersion: context.topology.runtimeVersion,
275
+ composeVersion: context.topology.composeVersion,
276
+ projectName: context.topology.projectName,
277
+ composeFiles: context.composeFiles,
278
+ services: context.topology.services.map(({ id }) => id),
279
+ service: request.service,
280
+ timeoutMs: input.timeoutMs,
281
+ outputLimitBytes: input.outputLimitBytes,
282
+ env: context.env,
283
+ },
284
+ };
285
+ };
286
+ const observeEnvironment = async (request) => {
287
+ if (!input || resolve(request.configPath) !== input.configPath)
288
+ throw new RefusalError('environment configuration path does not match');
289
+ const context = await prepare();
290
+ if (!exactCompletedTopology(context)) {
291
+ throw new RefusalError('a complete stable runtime cohort is unavailable');
292
+ }
293
+ let evidenceSubject;
294
+ for (const completed of context.lifecycle.completedServices) {
295
+ const service = await authenticateService(context.topology, completed.service, completed.containerId, execute);
296
+ if (!service)
297
+ throw new RefusalError('runtime service cohort changed');
298
+ evidenceSubject = service;
299
+ }
300
+ if (!evidenceSubject)
301
+ throw new RefusalError('runtime evidence subject is unavailable');
302
+ const createdAt = new Date().toISOString();
303
+ const capturePlan = createStabilityCapturePlan({
304
+ stackId: context.stackId,
305
+ configSha256: context.configuration.runtimeSha256,
306
+ runtimeExecutable: context.executable.value,
307
+ cwd: context.topology.cwd,
308
+ env: Object.fromEntries(Object.entries(context.env).filter(([name]) => name === 'PATH')),
309
+ containerId: evidenceSubject.containerId,
310
+ batchId: context.topology.batchId,
311
+ runtime: { name: 'docker', version: context.topology.runtimeVersion },
312
+ contentPaths: ['/proc/1/exe'],
313
+ timeoutMs: input.timeoutMs,
314
+ outputLimitBytes: STABILITY_CAPTURE_OUTPUT_LIMIT_BYTES,
315
+ });
316
+ if (capturePlan.kind !== 'preview' || capturePlan.plan.steps.length !== 2) {
317
+ const reason = capturePlan.diagnostics.map(({ message }) => message).join('; ');
318
+ throw new RefusalError(`runtime evidence capture plan is unavailable: ${reason || 'the plan does not hold two capture steps'}`);
319
+ }
320
+ const results = [];
321
+ for (const step of capturePlan.plan.steps)
322
+ results.push(await execute(step));
323
+ const authenticated = authenticateStabilityCapture({
324
+ plan: capturePlan,
325
+ results: results,
326
+ });
327
+ if ('state' in authenticated) {
328
+ throw new RefusalError(`runtime evidence capture is unavailable: ${authenticated.reason} (${authenticated.code})`);
329
+ }
330
+ const evidence = captureRuntimeEvidence({ capture: authenticated });
331
+ if (evidence.state !== 'available')
332
+ throw new RefusalError(`runtime evidence is unavailable: ${evidence.reason}`);
333
+ const after = await readState(context.namespace.paths.lifecycle(context.stackId));
334
+ if (after.kind !== 'complete' || JSON.stringify(after.value) !== JSON.stringify(context.lifecycleRecord)) {
335
+ throw new RefusalError('lifecycle state changed during environment observation');
336
+ }
337
+ const snapshot = createEnvironmentSnapshot({
338
+ stackId: context.stackId,
339
+ configSha256: context.configuration.runtimeSha256,
340
+ createdAt,
341
+ creationSource: 'resolved-config',
342
+ intendedState: context.configuration.config,
343
+ });
344
+ const ownedSnapshot = { ...snapshot, intendedState: freezeJson(snapshot.intendedState) };
345
+ const ownedEvidence = {
346
+ ...evidence,
347
+ runtime: { ...evidence.runtime },
348
+ command: {
349
+ ...evidence.command,
350
+ stepIds: [...evidence.command.stepIds],
351
+ },
352
+ observedBytes: { ...evidence.observedBytes },
353
+ stability: { ...evidence.stability },
354
+ };
355
+ return { snapshot: ownedSnapshot, evidence: ownedEvidence };
356
+ };
357
+ const observeServices = async (request) => {
358
+ if (!input || resolve(request.configPath) !== input.configPath)
359
+ throw new RefusalError('service configuration path does not match');
360
+ const context = await prepare(input.signal, (diagnostics) => { throw new DeclarationRefusalError(diagnostics[0]); }, true);
361
+ return {
362
+ topology: context.topology,
363
+ completedServices: context.lifecycle.completedServices,
364
+ // The recorded container is what `up`, `preflight` and `logs` revalidate, so a rebuild that
365
+ // replaced it updates the record rather than leaving the stack looking broken.
366
+ recordContainer: async (service, containerId) => {
367
+ const value = { ...context.lifecycle, completedServices: context.lifecycle.completedServices
368
+ .map((row) => row.service === service ? { service, containerId } : row) };
369
+ const published = await publishLifecycleRecord(context.run, context.lifecycleRecord, value);
370
+ if ('status' in published)
371
+ return published.diagnostics;
372
+ if (published.kind === 'complete')
373
+ return [];
374
+ return [{ severity: 'error',
375
+ code: published.kind === 'missing' ? ENVIRONMENT_CODES.STATE_RECORD_MALFORMED : published.code,
376
+ target: context.namespace.paths.lifecycle(context.stackId).path,
377
+ message: `the rebuilt container of service ${service} runs, and the stack record could not be updated to it${published.kind === 'missing' ? '' : `: ${published.message}`}`,
378
+ fix: 'run npx --no cockpit up --config <your cockpit.toml> to record the running containers again' }];
379
+ },
380
+ };
381
+ };
382
+ const services = {
383
+ observe: observeServices,
384
+ execute: (step) => execute(step, input?.signal ? { signal: input.signal } : undefined),
385
+ executeForeground: async (planned) => {
386
+ if (!executeForeground)
387
+ throw new RefusalError('foreground execution is not bound');
388
+ return executeForeground(planned, input?.signal ? { signal: input.signal } : {});
389
+ },
390
+ };
391
+ const logs = {
392
+ observe: observeLogs,
393
+ execute: (step, options) => execute(step, options),
394
+ ...(input?.signal ? { signal: input.signal } : {}),
395
+ };
396
+ return { logs, services, environment: { observe: observeEnvironment } };
397
+ }
@@ -0,0 +1,57 @@
1
+ import { loadCockpitConfig } from '../config/load.js';
2
+ import type { ExecuteProbeOutcome } from '../environment-exec/execute.js';
3
+ import { type EnvironmentDiagnostic, type ExecutionStep } from './contract.js';
4
+ import type { ReadinessReport } from '../readiness.js';
5
+ interface ProjectReadinessInput {
6
+ /** The cockpit root, where cockpit.toml is or will be written. */
7
+ readonly root: string;
8
+ /** Present once cockpit.toml exists; its siblings are the repositories asked. */
9
+ readonly configPath?: string;
10
+ /** Repositories observed before cockpit.toml exists. */
11
+ readonly repositories?: readonly Readonly<{
12
+ slug: string;
13
+ root: string;
14
+ }>[];
15
+ /** Doctor already names a cockpit root outside Git through its checkout observation. */
16
+ readonly checkRoot: boolean;
17
+ readonly env: Readonly<Record<string, string | undefined>>;
18
+ readonly timeoutMs: number;
19
+ /** The agent tools init was asked to set up, before cockpit.toml records them. */
20
+ readonly tools?: readonly string[];
21
+ }
22
+ type LoadedConfigResult = Awaited<ReturnType<typeof loadCockpitConfig>>;
23
+ export declare function inspectProjectReadiness(input: ProjectReadinessInput, execute: (step: ExecutionStep) => Promise<ExecuteProbeOutcome>, preloaded?: LoadedConfigResult): Promise<readonly EnvironmentDiagnostic[]>;
24
+ /**
25
+ * init's readiness probes. Before cockpit.toml exists, whether a runtime will be declared is read
26
+ * from the estate classification: an estate with no runnable product gets a surfaces-only cockpit.
27
+ */
28
+ export declare function inspectInitReadiness(input: Omit<ProjectReadinessInput, 'env' | 'timeoutMs'> & Readonly<{
29
+ classification?: string;
30
+ }>, binding: Readonly<{
31
+ env: ProjectReadinessInput['env'];
32
+ timeoutMs: number;
33
+ execute: (step: ExecutionStep) => Promise<ExecuteProbeOutcome>;
34
+ }>): Promise<Readonly<{
35
+ state: ReadinessReport['state'];
36
+ findings: readonly EnvironmentDiagnostic[];
37
+ }>>;
38
+ /**
39
+ * What the container runtime on this machine offers, measured by the same collector preflight
40
+ * compares the machine floor with, so init can propose a floor where no Compose limit records one.
41
+ */
42
+ export declare function measureInitMachine(input: Readonly<{
43
+ root: string;
44
+ }>, binding: Readonly<{
45
+ env: ProjectReadinessInput['env'];
46
+ timeoutMs: number;
47
+ execute: (step: ExecutionStep) => Promise<ExecuteProbeOutcome>;
48
+ }>): Promise<Readonly<{
49
+ kind: 'complete';
50
+ memoryMb: number;
51
+ cpus: number;
52
+ observedAt: string;
53
+ }> | Readonly<{
54
+ kind: 'unavailable';
55
+ reason: string;
56
+ }>>;
57
+ export {};
@@ -0,0 +1,151 @@
1
+ // Binds the readiness probes no health row covers to the production executor, for doctor and for
2
+ // init: whether the cockpit root is a Git working tree before any cockpit.toml exists, the cockpit's
3
+ // own install and recorded sibling remotes, secret files a repository commits, and root instruction
4
+ // files that leave some agent tool without a repository's instructions. Each finding
5
+ // comes from its owner; this module only chooses which repositories to ask.
6
+ import { lstat } from 'node:fs/promises';
7
+ import { dirname, resolve } from 'node:path';
8
+ import { loadCockpitConfig } from '../config/load.js';
9
+ import { authenticatedExecutionOutcome } from '../environment-exec/execute.js';
10
+ import { inspectTeamReadiness } from '../team-metadata.js';
11
+ import { guardSearchPathRelativeEntry } from '../guards/protected-locations.js';
12
+ import { DOCKER_CLIENT_ENVIRONMENT, ENVIRONMENT_CODES, snapshotExecutionStep, RefusalError, isMissing } from './contract.js';
13
+ import { resolveDiskCapacityExecutable, resolveDockerExecutable, resolveGitExecutable } from './executable.js';
14
+ import { measureMachineFloor } from './runtime.js';
15
+ import { gitRepositoryRefusal, inspectTrackedSecretFiles } from './git-observation.js';
16
+ import { declaresNothingToRun } from './declaration.js';
17
+ import { inspectSiblingInstructions } from './sibling-instructions.js';
18
+ import { DEFAULT_SURFACE_TOOLS } from '../surfaces/harness-registry.js';
19
+ async function isDirectory(path) {
20
+ try {
21
+ return (await lstat(path)).isDirectory();
22
+ }
23
+ catch (error) {
24
+ if (isMissing(error))
25
+ return false;
26
+ throw error;
27
+ }
28
+ }
29
+ export async function inspectProjectReadiness(input, execute,
30
+ // A caller that already loaded cockpit.toml hands the result over rather than reading it again.
31
+ preloaded) {
32
+ // A cockpit.toml that does not load is the finding; its own refusal says what to fix first.
33
+ const loaded = preloaded ?? (input.configPath === undefined ? undefined : await loadCockpitConfig(input.configPath));
34
+ if (loaded && loaded.kind !== 'complete')
35
+ return [];
36
+ const findings = input.configPath === undefined ? [] : [...await inspectTeamReadiness(input.configPath)];
37
+ const tools = loaded?.kind === 'complete' ? loaded.result.config.surfaces.tools : input.tools ?? DEFAULT_SURFACE_TOOLS;
38
+ const refusers = ['codexcli', 'opencode'].filter(tool => tools.includes(tool)).map(tool => tool === 'codexcli' ? 'Codex' : 'OpenCode');
39
+ // The guard reads the PATH its hook inherits from the harness, which is started from a shell like
40
+ // this one; while that PATH holds a relative entry it asks before every command.
41
+ const relativeEntry = guardSearchPathRelativeEntry(input.env.PATH ?? '');
42
+ if (input.configPath !== undefined && relativeEntry !== null)
43
+ findings.push({
44
+ severity: 'warning',
45
+ // A literal, so the diagnostic inventory captures the fix beside the code.
46
+ code: 'GUARD_SEARCH_PATH_RELATIVE',
47
+ message: `PATH holds the relative entry ${JSON.stringify(relativeEntry)}${relativeEntry === '' ? ' (an empty entry is the working directory)' : ''}; an agent harness started with this PATH gets a guard that asks before every command, because a file the agent wrote could run in place of the command${refusers.length > 0 ? `, and ${refusers.join(' and ')} refuse${refusers.length === 1 ? 's' : ''} what it asks about` : ''}`,
48
+ target: 'PATH',
49
+ fix: 'remove every relative or empty entry from PATH where the shell profile or launcher that starts the agent harness sets it, then restart the harness',
50
+ });
51
+ let repositories = input.repositories ?? [];
52
+ if (loaded?.kind === 'complete') {
53
+ const siblingRoot = resolve(dirname(loaded.result.source.path), loaded.result.config.cockpit.siblings_root);
54
+ repositories = loaded.result.config.sibling.map((sibling) => ({ slug: sibling.slug, root: resolve(siblingRoot, sibling.dir) }));
55
+ }
56
+ // Only an existing directory has files to ask about; an absent one is reported as missing elsewhere.
57
+ const present = [];
58
+ for (const repository of repositories)
59
+ if (await isDirectory(repository.root))
60
+ present.push(repository);
61
+ // The cockpit root's own AGENTS.md and CLAUDE.md are generated by sync.
62
+ for (const repository of present)
63
+ if (resolve(repository.root) !== resolve(input.root))
64
+ findings.push(...await inspectSiblingInstructions(repository, tools));
65
+ const git = await resolveGitExecutable(input.env);
66
+ if (git.state !== 'available') {
67
+ // Without Git, the root and secret checks did not run; their absence from the report is no finding.
68
+ findings.push({
69
+ severity: 'warning',
70
+ code: ENVIRONMENT_CODES.EVIDENCE_UNAVAILABLE,
71
+ message: `the readiness checks that ask Git (the cockpit root's working tree, committed secret files) did not run: ${git.reason}`,
72
+ target: 'git',
73
+ fix: 'install Git and put it on PATH, then rerun this command',
74
+ });
75
+ return findings;
76
+ }
77
+ // A greenfield root init writes into in place stays outside Git until someone runs git init, and
78
+ // setup refuses it only later; init reached environment-verified without saying so.
79
+ if (input.checkRoot && await isDirectory(input.root)) {
80
+ const step = snapshotExecutionStep({
81
+ id: 'git:readiness-working-tree', argv: [git.value.canonicalPath, '-C', input.root, 'rev-parse', '--show-toplevel'],
82
+ argv0: git.value.invocationPath, cwd: input.root, env: {}, timeoutMs: input.timeoutMs,
83
+ stdoutLimitBytes: 4_096, stderrLimitBytes: 4_096, paths: [input.root], ports: [], labels: { source: input.root },
84
+ expected: [], consumedFiles: [{ path: git.value.canonicalPath, sha256: git.value.sha256, maxBytes: 536_870_912,
85
+ argvIndexes: [0], executable: true, executionMode: git.value.executionMode }],
86
+ filesystemChecks: [], compensation: [],
87
+ });
88
+ if (!step)
89
+ throw new RefusalError('the cockpit root Git probe could not be planned');
90
+ const outcome = authenticatedExecutionOutcome(await execute(step), step);
91
+ if (!outcome)
92
+ throw new RefusalError('the cockpit root Git probe returned unauthenticated evidence');
93
+ // Git exits non-zero outside a working tree and in one it refuses to read; any other refusal is a
94
+ // probe that did not run.
95
+ if (outcome.kind === 'refusal' && outcome.code !== ENVIRONMENT_CODES.EXECUTION_STEP_FAILED) {
96
+ throw new RefusalError(`the cockpit root Git probe did not run (${outcome.code})`);
97
+ }
98
+ const refused = outcome.kind === 'refusal' ? gitRepositoryRefusal(outcome.stderr) : undefined;
99
+ if (refused?.kind === 'not-a-repository')
100
+ findings.push({
101
+ severity: 'warning',
102
+ code: ENVIRONMENT_CODES.SETUP_PATH_TYPE_INVALID,
103
+ message: `${input.root} is not inside a Git working tree, so setup will refuse to run there and no teammate can clone this cockpit`,
104
+ target: input.root,
105
+ fix: `run git -C ${input.root} init, then commit the cockpit files once a person has reviewed them`,
106
+ });
107
+ if (refused?.kind === 'refused')
108
+ findings.push({
109
+ severity: 'warning',
110
+ code: ENVIRONMENT_CODES.EVIDENCE_UNAVAILABLE,
111
+ message: `whether ${input.root} is inside a Git working tree could not be checked: ${refused.reason}`,
112
+ target: input.root,
113
+ fix: refused.fix,
114
+ });
115
+ }
116
+ findings.push(...await inspectTrackedSecretFiles({ executable: git.value, repositories: present, timeoutMs: input.timeoutMs }, execute));
117
+ return findings;
118
+ }
119
+ /**
120
+ * init's readiness probes. Before cockpit.toml exists, whether a runtime will be declared is read
121
+ * from the estate classification: an estate with no runnable product gets a surfaces-only cockpit.
122
+ */
123
+ export async function inspectInitReadiness(input, binding) {
124
+ const { classification, ...probe } = input;
125
+ const loaded = input.configPath === undefined ? undefined : await loadCockpitConfig(input.configPath);
126
+ const findings = await inspectProjectReadiness({ ...probe, env: binding.env, timeoutMs: binding.timeoutMs }, binding.execute, loaded);
127
+ const state = loaded?.kind === 'complete'
128
+ ? declaresNothingToRun(loaded.result.config.environment) ? 'surfaces-only' : 'runtime'
129
+ : classification === undefined ? 'undetermined' : classification === 'no-runnable-product' ? 'surfaces-only' : 'runtime';
130
+ return { state, findings };
131
+ }
132
+ /**
133
+ * What the container runtime on this machine offers, measured by the same collector preflight
134
+ * compares the machine floor with, so init can propose a floor where no Compose limit records one.
135
+ */
136
+ export async function measureInitMachine(input, binding) {
137
+ const [docker, disk] = await Promise.all([resolveDockerExecutable(binding.env), resolveDiskCapacityExecutable()]);
138
+ if (docker.state !== 'available')
139
+ return { kind: 'unavailable', reason: docker.reason };
140
+ if (disk.state !== 'available')
141
+ return { kind: 'unavailable', reason: disk.reason };
142
+ const env = Object.fromEntries(Object.entries(binding.env)
143
+ .filter((entry) => DOCKER_CLIENT_ENVIRONMENT.has(entry[0]) && typeof entry[1] === 'string'));
144
+ const measured = await measureMachineFloor({ runtimeExecutable: docker.value, diskExecutable: disk.value, cwd: input.root,
145
+ env, timeoutMs: binding.timeoutMs }, binding.execute);
146
+ if (measured.kind !== 'complete')
147
+ return { kind: 'unavailable', reason: measured.kind === 'refusal'
148
+ ? measured.diagnostics.map(row => row.message).join('; ') : 'docker info did not report the runtime\'s memory and CPUs' };
149
+ return { kind: 'complete', memoryMb: Math.floor(measured.evidence.memoryBytes / 1024 ** 2),
150
+ cpus: measured.evidence.cpuMillis / 1_000, observedAt: measured.evidence.observedAt };
151
+ }