@b00dea/cockpit 0.0.0-bootstrap.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/CHANGELOG.md +375 -0
  2. package/LICENSE +21 -0
  3. package/README.md +21 -1
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2574 -0
  6. package/dist/autonomy.d.ts +44 -0
  7. package/dist/autonomy.js +156 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +62 -0
  11. package/dist/browser/commands.js +349 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +117 -0
  17. package/dist/browser/runtime.js +183 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +556 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/cli-guard.d.ts +5 -0
  23. package/dist/cli-guard.js +71 -0
  24. package/dist/cli-main.d.ts +139 -0
  25. package/dist/cli-main.js +1405 -0
  26. package/dist/cli.d.ts +3 -0
  27. package/dist/cli.js +16 -0
  28. package/dist/command-table.d.ts +113 -0
  29. package/dist/command-table.js +512 -0
  30. package/dist/config/load.d.ts +171 -0
  31. package/dist/config/load.js +345 -0
  32. package/dist/config/sibling-toml.d.ts +48 -0
  33. package/dist/config/sibling-toml.js +160 -0
  34. package/dist/config/surface-tools-toml.d.ts +9 -0
  35. package/dist/config/surface-tools-toml.js +37 -0
  36. package/dist/config/verified-write.d.ts +43 -0
  37. package/dist/config/verified-write.js +89 -0
  38. package/dist/diagnostics.d.ts +26 -0
  39. package/dist/diagnostics.js +335 -0
  40. package/dist/emit.d.ts +22 -0
  41. package/dist/emit.js +43 -0
  42. package/dist/entrypoint-kinds.d.ts +3 -0
  43. package/dist/entrypoint-kinds.js +11 -0
  44. package/dist/environment/application/destructive.d.ts +6 -0
  45. package/dist/environment/application/destructive.js +425 -0
  46. package/dist/environment/application/doctor.d.ts +89 -0
  47. package/dist/environment/application/doctor.js +235 -0
  48. package/dist/environment/application/guard.d.ts +43 -0
  49. package/dist/environment/application/guard.js +622 -0
  50. package/dist/environment/application/health.d.ts +134 -0
  51. package/dist/environment/application/health.js +379 -0
  52. package/dist/environment/application/lifecycle-context.d.ts +228 -0
  53. package/dist/environment/application/lifecycle-context.js +154 -0
  54. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  55. package/dist/environment/application/lifecycle-state.js +201 -0
  56. package/dist/environment/application/lifecycle.d.ts +17 -0
  57. package/dist/environment/application/lifecycle.js +729 -0
  58. package/dist/environment/application/logs.d.ts +45 -0
  59. package/dist/environment/application/logs.js +127 -0
  60. package/dist/environment/application/setup.d.ts +129 -0
  61. package/dist/environment/application/setup.js +741 -0
  62. package/dist/environment/application/shared.d.ts +11 -0
  63. package/dist/environment/application/shared.js +61 -0
  64. package/dist/environment/claims.d.ts +389 -0
  65. package/dist/environment/claims.js +1709 -0
  66. package/dist/environment/codex-hook-trust.d.ts +11 -0
  67. package/dist/environment/codex-hook-trust.js +185 -0
  68. package/dist/environment/compose-environment.d.ts +1 -0
  69. package/dist/environment/compose-environment.js +6 -0
  70. package/dist/environment/compose-ownership.d.ts +21 -0
  71. package/dist/environment/compose-ownership.js +117 -0
  72. package/dist/environment/compose-topology.d.ts +117 -0
  73. package/dist/environment/compose-topology.js +413 -0
  74. package/dist/environment/contract.d.ts +489 -0
  75. package/dist/environment/contract.js +761 -0
  76. package/dist/environment/declaration.d.ts +14 -0
  77. package/dist/environment/declaration.js +39 -0
  78. package/dist/environment/destructive-composition.d.ts +29 -0
  79. package/dist/environment/destructive-composition.js +333 -0
  80. package/dist/environment/docker-daemon.d.ts +30 -0
  81. package/dist/environment/docker-daemon.js +78 -0
  82. package/dist/environment/docker-endpoint.d.ts +10 -0
  83. package/dist/environment/docker-endpoint.js +19 -0
  84. package/dist/environment/env-schema-observation.d.ts +46 -0
  85. package/dist/environment/env-schema-observation.js +241 -0
  86. package/dist/environment/evidence.d.ts +136 -0
  87. package/dist/environment/evidence.js +437 -0
  88. package/dist/environment/executable.d.ts +26 -0
  89. package/dist/environment/executable.js +167 -0
  90. package/dist/environment/generated-application/authorities.d.ts +5 -0
  91. package/dist/environment/generated-application/authorities.js +61 -0
  92. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  93. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  94. package/dist/environment/generated-application/check.d.ts +13 -0
  95. package/dist/environment/generated-application/check.js +189 -0
  96. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  97. package/dist/environment/generated-application/custom-surface.js +289 -0
  98. package/dist/environment/generated-application/prepare.d.ts +29 -0
  99. package/dist/environment/generated-application/prepare.js +276 -0
  100. package/dist/environment/generated-application/publication.d.ts +33 -0
  101. package/dist/environment/generated-application/publication.js +547 -0
  102. package/dist/environment/generated-application/shared.d.ts +484 -0
  103. package/dist/environment/generated-application/shared.js +349 -0
  104. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  105. package/dist/environment/generated-application/surface-observation.js +69 -0
  106. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  107. package/dist/environment/generated-application/surface-outputs.js +389 -0
  108. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  109. package/dist/environment/generated-application/surfaces.js +699 -0
  110. package/dist/environment/generated-application/sync.d.ts +6 -0
  111. package/dist/environment/generated-application/sync.js +109 -0
  112. package/dist/environment/git-observation.d.ts +183 -0
  113. package/dist/environment/git-observation.js +544 -0
  114. package/dist/environment/health-composition.d.ts +11 -0
  115. package/dist/environment/health-composition.js +628 -0
  116. package/dist/environment/host-observation.d.ts +56 -0
  117. package/dist/environment/host-observation.js +52 -0
  118. package/dist/environment/identity.d.ts +48 -0
  119. package/dist/environment/identity.js +85 -0
  120. package/dist/environment/init-application.d.ts +308 -0
  121. package/dist/environment/init-application.js +697 -0
  122. package/dist/environment/install-policy-observation.d.ts +71 -0
  123. package/dist/environment/install-policy-observation.js +284 -0
  124. package/dist/environment/install-policy.d.ts +48 -0
  125. package/dist/environment/install-policy.js +96 -0
  126. package/dist/environment/instance.d.ts +33 -0
  127. package/dist/environment/instance.js +122 -0
  128. package/dist/environment/instruction-pointer.d.ts +19 -0
  129. package/dist/environment/instruction-pointer.js +144 -0
  130. package/dist/environment/lifecycle-composition.d.ts +29 -0
  131. package/dist/environment/lifecycle-composition.js +675 -0
  132. package/dist/environment/lifecycle-stage.d.ts +115 -0
  133. package/dist/environment/lifecycle-stage.js +398 -0
  134. package/dist/environment/lifecycle.d.ts +75 -0
  135. package/dist/environment/lifecycle.js +354 -0
  136. package/dist/environment/logs.d.ts +132 -0
  137. package/dist/environment/logs.js +519 -0
  138. package/dist/environment/observations.d.ts +46 -0
  139. package/dist/environment/observations.js +344 -0
  140. package/dist/environment/operator-application.d.ts +50 -0
  141. package/dist/environment/operator-application.js +155 -0
  142. package/dist/environment/ports.d.ts +182 -0
  143. package/dist/environment/ports.js +1205 -0
  144. package/dist/environment/posture-observation.d.ts +68 -0
  145. package/dist/environment/posture-observation.js +253 -0
  146. package/dist/environment/read-composition.d.ts +27 -0
  147. package/dist/environment/read-composition.js +352 -0
  148. package/dist/environment/readiness-composition.d.ts +38 -0
  149. package/dist/environment/readiness-composition.js +130 -0
  150. package/dist/environment/reclaim.d.ts +333 -0
  151. package/dist/environment/reclaim.js +1835 -0
  152. package/dist/environment/repository-identity.d.ts +13 -0
  153. package/dist/environment/repository-identity.js +108 -0
  154. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  155. package/dist/environment/runtime-measurement-record.js +111 -0
  156. package/dist/environment/runtime-measurement.d.ts +27 -0
  157. package/dist/environment/runtime-measurement.js +224 -0
  158. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  159. package/dist/environment/runtime-probe-residue.js +148 -0
  160. package/dist/environment/runtime-probe.d.ts +72 -0
  161. package/dist/environment/runtime-probe.js +725 -0
  162. package/dist/environment/runtime.d.ts +505 -0
  163. package/dist/environment/runtime.js +2335 -0
  164. package/dist/environment/seed.d.ts +16 -0
  165. package/dist/environment/seed.js +43 -0
  166. package/dist/environment/selftest-application.d.ts +86 -0
  167. package/dist/environment/selftest-application.js +713 -0
  168. package/dist/environment/sensitive.d.ts +3 -0
  169. package/dist/environment/sensitive.js +48 -0
  170. package/dist/environment/setup-observation.d.ts +62 -0
  171. package/dist/environment/setup-observation.js +1473 -0
  172. package/dist/environment/setup.d.ts +43 -0
  173. package/dist/environment/setup.js +176 -0
  174. package/dist/environment/sibling-instructions.d.ts +33 -0
  175. package/dist/environment/sibling-instructions.js +224 -0
  176. package/dist/environment/sibling-registration.d.ts +45 -0
  177. package/dist/environment/sibling-registration.js +538 -0
  178. package/dist/environment/state.d.ts +371 -0
  179. package/dist/environment/state.js +2013 -0
  180. package/dist/environment/templates.d.ts +140 -0
  181. package/dist/environment/templates.js +685 -0
  182. package/dist/environment/tool-registration.d.ts +9 -0
  183. package/dist/environment/tool-registration.js +96 -0
  184. package/dist/environment/upgrade-application.d.ts +19 -0
  185. package/dist/environment/upgrade-application.js +175 -0
  186. package/dist/environment-exec/execute.d.ts +102 -0
  187. package/dist/environment-exec/execute.js +1175 -0
  188. package/dist/environment-exec/libexec-program.d.ts +6 -0
  189. package/dist/environment-exec/libexec-program.js +22 -0
  190. package/dist/environment-exec/verified-digest.d.ts +4 -0
  191. package/dist/environment-exec/verified-digest.js +16 -0
  192. package/dist/exact-keys.d.ts +7 -0
  193. package/dist/exact-keys.js +15 -0
  194. package/dist/fix-catalogue.d.ts +32 -0
  195. package/dist/fix-catalogue.js +60 -0
  196. package/dist/freshness.d.ts +29 -0
  197. package/dist/freshness.js +75 -0
  198. package/dist/generated-environment/adopt.d.ts +16 -0
  199. package/dist/generated-environment/adopt.js +275 -0
  200. package/dist/generated-environment/command.d.ts +47 -0
  201. package/dist/generated-environment/command.js +75 -0
  202. package/dist/generated-environment/transaction.d.ts +186 -0
  203. package/dist/generated-environment/transaction.js +1770 -0
  204. package/dist/grading/evaluate.d.ts +71 -0
  205. package/dist/grading/evaluate.js +403 -0
  206. package/dist/grading/grade.d.ts +33 -0
  207. package/dist/grading/grade.js +57 -0
  208. package/dist/guards/evaluator.d.ts +113 -0
  209. package/dist/guards/evaluator.js +1226 -0
  210. package/dist/guards/harness-adapter.d.ts +17 -0
  211. package/dist/guards/harness-adapter.js +57 -0
  212. package/dist/guards/host.d.ts +8 -0
  213. package/dist/guards/host.js +10 -0
  214. package/dist/guards/invocation-parser.d.ts +66 -0
  215. package/dist/guards/invocation-parser.js +595 -0
  216. package/dist/guards/local-identity.d.ts +40 -0
  217. package/dist/guards/local-identity.js +123 -0
  218. package/dist/guards/policy-pin.d.ts +13 -0
  219. package/dist/guards/policy-pin.js +175 -0
  220. package/dist/guards/protected-locations.d.ts +62 -0
  221. package/dist/guards/protected-locations.js +461 -0
  222. package/dist/guards/read-only.d.ts +37 -0
  223. package/dist/guards/read-only.js +315 -0
  224. package/dist/init/command.d.ts +40 -0
  225. package/dist/init/command.js +310 -0
  226. package/dist/init/derive.d.ts +51 -0
  227. package/dist/init/derive.js +715 -0
  228. package/dist/init/draft.d.ts +138 -0
  229. package/dist/init/draft.js +1205 -0
  230. package/dist/init/render.d.ts +35 -0
  231. package/dist/init/render.js +82 -0
  232. package/dist/init/scaffold.d.ts +53 -0
  233. package/dist/init/scaffold.js +121 -0
  234. package/dist/invocation.d.ts +13 -0
  235. package/dist/invocation.js +20 -0
  236. package/dist/package-metadata.d.ts +9 -0
  237. package/dist/package-metadata.js +19 -0
  238. package/dist/readiness.d.ts +53 -0
  239. package/dist/readiness.js +88 -0
  240. package/dist/references/named.d.ts +24 -0
  241. package/dist/references/named.js +27 -0
  242. package/dist/refusal.d.ts +15 -0
  243. package/dist/refusal.js +7 -0
  244. package/dist/registry-derivation.d.ts +31 -0
  245. package/dist/registry-derivation.js +32 -0
  246. package/dist/registry-resolve/command.d.ts +47 -0
  247. package/dist/registry-resolve/command.js +226 -0
  248. package/dist/registry-resolve/registry.d.ts +68 -0
  249. package/dist/registry-resolve/registry.js +292 -0
  250. package/dist/registry-resolve/render-shell.d.ts +18 -0
  251. package/dist/registry-resolve/render-shell.js +36 -0
  252. package/dist/repair.d.ts +29 -0
  253. package/dist/repair.js +8 -0
  254. package/dist/schema-json.d.ts +1 -0
  255. package/dist/schema-json.js +177 -0
  256. package/dist/schema.d.ts +1322 -0
  257. package/dist/schema.js +934 -0
  258. package/dist/setup/command.d.ts +4 -0
  259. package/dist/setup/command.js +4 -0
  260. package/dist/sha256.d.ts +1 -0
  261. package/dist/sha256.js +4 -0
  262. package/dist/surfaces/adoption.d.ts +41 -0
  263. package/dist/surfaces/adoption.js +217 -0
  264. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  265. package/dist/surfaces/consumer-instructions.js +43 -0
  266. package/dist/surfaces/contract.d.ts +42 -0
  267. package/dist/surfaces/contract.js +169 -0
  268. package/dist/surfaces/core-declarations.d.ts +24 -0
  269. package/dist/surfaces/core-declarations.js +79 -0
  270. package/dist/surfaces/development-markers.d.ts +1 -0
  271. package/dist/surfaces/development-markers.js +18 -0
  272. package/dist/surfaces/generator-adapter.d.ts +71 -0
  273. package/dist/surfaces/generator-adapter.js +809 -0
  274. package/dist/surfaces/harness-registry.d.ts +180 -0
  275. package/dist/surfaces/harness-registry.js +117 -0
  276. package/dist/surfaces/mcp.d.ts +95 -0
  277. package/dist/surfaces/mcp.js +186 -0
  278. package/dist/surfaces/merge.d.ts +30 -0
  279. package/dist/surfaces/merge.js +105 -0
  280. package/dist/surfaces/ownership.d.ts +190 -0
  281. package/dist/surfaces/ownership.js +795 -0
  282. package/dist/surfaces/projection.d.ts +136 -0
  283. package/dist/surfaces/projection.js +681 -0
  284. package/dist/surfaces/render.d.ts +118 -0
  285. package/dist/surfaces/render.js +633 -0
  286. package/dist/surfaces/restrictions.d.ts +130 -0
  287. package/dist/surfaces/restrictions.js +447 -0
  288. package/dist/surfaces/selftest.d.ts +162 -0
  289. package/dist/surfaces/selftest.js +393 -0
  290. package/dist/surfaces/skill-contract.d.ts +85 -0
  291. package/dist/surfaces/skill-contract.js +449 -0
  292. package/dist/surfaces/sync.d.ts +57 -0
  293. package/dist/surfaces/sync.js +164 -0
  294. package/dist/team-metadata.d.ts +41 -0
  295. package/dist/team-metadata.js +512 -0
  296. package/dist/testing/gates.d.ts +91 -0
  297. package/dist/testing/gates.js +182 -0
  298. package/dist/testing/hooks.d.ts +59 -0
  299. package/dist/testing/hooks.js +139 -0
  300. package/dist/testing/verdict.d.ts +29 -0
  301. package/dist/testing/verdict.js +62 -0
  302. package/dist/work-orders/application.d.ts +99 -0
  303. package/dist/work-orders/application.js +383 -0
  304. package/dist/work-orders/composition.d.ts +124 -0
  305. package/dist/work-orders/composition.js +1010 -0
  306. package/dist/work-orders/contract.d.ts +61 -0
  307. package/dist/work-orders/contract.js +122 -0
  308. package/dist/work-orders/store.d.ts +158 -0
  309. package/dist/work-orders/store.js +197 -0
  310. package/docs/README.md +65 -0
  311. package/docs/architecture.md +136 -0
  312. package/docs/capability-matrix.md +50 -0
  313. package/docs/cli-reference.md +599 -0
  314. package/docs/command-list.md +51 -0
  315. package/docs/compose-patterns.md +99 -0
  316. package/docs/concepts.md +171 -0
  317. package/docs/configuration-essentials.md +172 -0
  318. package/docs/configuration-reference.md +621 -0
  319. package/docs/environment.md +760 -0
  320. package/docs/extending.md +43 -0
  321. package/docs/faq.md +111 -0
  322. package/docs/getting-started.md +608 -0
  323. package/docs/harness-surfaces.md +433 -0
  324. package/docs/joining-a-team.md +76 -0
  325. package/docs/release-policy.md +49 -0
  326. package/docs/safety-and-autonomy.md +419 -0
  327. package/docs/troubleshooting.md +361 -0
  328. package/docs/tutorial.md +170 -0
  329. package/docs/work-and-browser.md +254 -0
  330. package/libexec/browser-operation.cjs +186 -0
  331. package/libexec/fence-marker.cjs +101 -0
  332. package/libexec/fence-marker.d.cts +24 -0
  333. package/libexec/fenced-group-runner.cjs +77 -0
  334. package/libexec/fenced-supervisor.cjs +137 -0
  335. package/libexec/generated-filesystem.cjs +731 -0
  336. package/libexec/install-filesystem.cjs +225 -0
  337. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  338. package/libexec/rulesync-generator.cjs +743 -0
  339. package/libexec/surface-generator-worker.cjs +266 -0
  340. package/libexec/surface-output-reader.cjs +99 -0
  341. package/libexec/surface-scratch-cleanup.cjs +14 -0
  342. package/libexec/validate-harness-evidence.d.mts +183 -0
  343. package/libexec/validate-harness-evidence.mjs +1312 -0
  344. package/package.json +76 -1
  345. package/surfaces/README.md +33 -0
  346. package/surfaces/agents/evaluator.agent.md +76 -0
  347. package/surfaces/guards/bash-guard.sh +48 -0
  348. package/surfaces/guards/claude.adapter.json +25 -0
  349. package/surfaces/guards/codex.adapter.json +24 -0
  350. package/surfaces/guards/opencode.adapter.json +25 -0
  351. package/surfaces/references/browser.md +63 -0
  352. package/surfaces/references/test-data-and-personas.md +84 -0
  353. package/surfaces/references/testing-tiers.md +11 -0
  354. package/surfaces/skills/cockpit-browser.skill.md +54 -0
  355. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  356. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  357. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  358. package/surfaces/skills/cockpit-work.skill.md +34 -0
  359. package/surfaces/skills/collection.json +5 -0
  360. package/surfaces/skills/procedures/explain.md +61 -0
  361. package/surfaces/skills/procedures/fix.md +81 -0
  362. package/surfaces/skills/procedures/setup.md +379 -0
  363. package/surfaces/skills/procedures/work.md +105 -0
@@ -0,0 +1,35 @@
1
+ import type { CockpitConfig, configurationDefaults, UNCONFIRMABLE_ANSWERS } from '../schema.js';
2
+ import type { DocumentedSibling, Gap } from '../registry-resolve/registry.js';
3
+ /**
4
+ * Who gave an interview answer: a person, the setup agent choosing a default it was allowed to
5
+ * choose, or no one said (an answers file that does not attribute a value cockpit never proposes).
6
+ * Only a person's answer is written as stated; the others are listed under `unconfirmed`.
7
+ */
8
+ export declare const ANSWER_PROVENANCES: readonly ['operator-confirmed', 'agent-default', 'unattributed'];
9
+ export type AnswerProvenance = typeof ANSWER_PROVENANCES[number];
10
+ export type UnconfirmableAnswer = typeof UNCONFIRMABLE_ANSWERS[number];
11
+ export type UnconfirmedAnswer = Readonly<{
12
+ field: UnconfirmableAnswer;
13
+ provenance: Exclude<AnswerProvenance, 'operator-confirmed'>;
14
+ }>;
15
+ export declare function unconfirmedReason(row: UnconfirmedAnswer): string;
16
+ export declare const UNCONFIRMED_FIX = "confirm the value with whoever owns the product and correct it in cockpit.toml if it is wrong, then delete its entry from unconfirmed and run npx --no cockpit sync --config <your cockpit.toml>";
17
+ export interface Attribution {
18
+ target: string;
19
+ value: unknown;
20
+ from: 'inferred' | 'default';
21
+ because: string;
22
+ }
23
+ export interface DocumentModel {
24
+ name?: string;
25
+ defaults: ReturnType<typeof configurationDefaults>;
26
+ siblings: readonly DocumentedSibling[];
27
+ attributions: readonly Attribution[];
28
+ gaps: readonly Gap[];
29
+ unconfirmed?: readonly UnconfirmedAnswer[];
30
+ /** Values read from the cockpit root's own files, keyed by their target. */
31
+ inferred?: Readonly<Partial<Record<'ports.bases' | 'environment.compose_files', unknown>>>;
32
+ }
33
+ export declare function renderIncompleteDocument(model: DocumentModel, schemaDirective: string, configuration?: CockpitConfig): string;
34
+ /** Decisions are comments: the schema has no field for them, and the reader of the file needs them. */
35
+ export declare function renderCompleteDocument(config: CockpitConfig, schemaDirective: string, decisions?: readonly string[], unconfirmed?: readonly UnconfirmedAnswer[]): string;
@@ -0,0 +1,82 @@
1
+ import { AUTONOMY_DEFAULTS, GATE_DEFAULTS } from '../autonomy.js';
2
+ import { emitGaps, emitToml, toSiblingRows } from '../emit.js';
3
+ /**
4
+ * Who gave an interview answer: a person, the setup agent choosing a default it was allowed to
5
+ * choose, or no one said (an answers file that does not attribute a value cockpit never proposes).
6
+ * Only a person's answer is written as stated; the others are listed under `unconfirmed`.
7
+ */
8
+ export const ANSWER_PROVENANCES = ['operator-confirmed', 'agent-default', 'unattributed'];
9
+ export function unconfirmedReason(row) {
10
+ return row.provenance === 'agent-default'
11
+ ? `${row.field} was chosen by the setup agent as a default; no person stated it`
12
+ : `${row.field} came from an answers file that did not say who chose it; no person is known to have stated it`;
13
+ }
14
+ export const UNCONFIRMED_FIX = 'confirm the value with whoever owns the product and correct it in cockpit.toml if it is wrong, then delete its entry from unconfirmed and run npx --no cockpit sync --config <your cockpit.toml>';
15
+ function renderUnconfirmed(rows = []) {
16
+ if (rows.length === 0)
17
+ return '';
18
+ return `${rows.map(row => `# Unconfirmed: ${unconfirmedReason(row)}.\n`).join('')}# To confirm: ${UNCONFIRMED_FIX}; until then doctor warns.\n`;
19
+ }
20
+ /** Every decision with its default, commented out: flip one to "person" to take that decision yourself. */
21
+ function renderDecisionDefaults() {
22
+ const table = (name, defaults) => [`# [${name}]`, ...Object.entries(defaults).map(([key, value]) => `# ${key} = "${value}"`)];
23
+ return `\n${[
24
+ '# Who takes each decision. All default to "agent" except reset and mcp_approve; set one to "person" to take it yourself.',
25
+ '# mcp_approve = "agent" lets an agent start processes outside the guard: sync then enables every server',
26
+ '# a sibling MCP source declares, and an agent can write that file.',
27
+ ...table('work.gates', GATE_DEFAULTS), ...table('autonomy', AUTONOMY_DEFAULTS),
28
+ ].join('\n')}\n`;
29
+ }
30
+ function renderAttributions(attributions) {
31
+ if (attributions.length === 0)
32
+ return '';
33
+ const lines = ['# Provenance — every unread value names its source and reason.'];
34
+ for (const attribution of attributions) {
35
+ lines.push(`# [${attribution.from}] ${attribution.target} = ${JSON.stringify(attribution.value)}: ${attribution.because}`);
36
+ }
37
+ return `${lines.join('\n')}\n`;
38
+ }
39
+ function draftConfig(model) {
40
+ // Written, the guard hosts read as a decision that none exists, even as the schema default [].
41
+ const { hosted_db_hosts: _hosted, vendor_endpoint_hosts: _vendors, ...guards } = model.defaults.guards;
42
+ // No registry dialect records the work-order time budgets, grading, gates, evaluator tools or
43
+ // autonomy, so a draft that wrote the schema's values would present them as this product's
44
+ // decisions; left out, the loader applies the same defaults.
45
+ const { autonomy: _autonomy, ...defaults } = model.defaults;
46
+ const work = { attempt_cap: model.defaults.work.attempt_cap, wip_cap: model.defaults.work.wip_cap };
47
+ const bases = model.inferred?.['ports.bases'];
48
+ const composeFiles = model.inferred?.['environment.compose_files'];
49
+ return {
50
+ ...defaults,
51
+ cockpit: {
52
+ ...model.defaults.cockpit,
53
+ ...(model.name ? { name: model.name } : {}),
54
+ },
55
+ sibling: toSiblingRows(model.siblings).map((sibling) => ({
56
+ ...sibling,
57
+ ...model.defaults.sibling,
58
+ })),
59
+ ports: { ...model.defaults.ports, ...(bases === undefined ? {} : { bases }) },
60
+ guards,
61
+ work,
62
+ environment: { ...model.defaults.environment, ...(composeFiles === undefined ? {} : { compose_files: composeFiles }) },
63
+ };
64
+ }
65
+ export function renderIncompleteDocument(model, schemaDirective, configuration) {
66
+ const config = configuration ?? draftConfig(model);
67
+ return [
68
+ `#:schema ${schemaDirective}\n`,
69
+ configuration || !model.gaps.some(gap => gap.blocking)
70
+ ? '# COCKPIT DOCUMENT: REVIEW DRAFT — confirm the inferred and default values before publication.\n'
71
+ : '# COCKPIT DOCUMENT: INCOMPLETE DRAFT — this file intentionally fails validation.\n',
72
+ renderAttributions(model.attributions),
73
+ renderUnconfirmed(model.unconfirmed),
74
+ emitGaps(model.gaps),
75
+ emitToml(config),
76
+ renderDecisionDefaults(),
77
+ ].join('');
78
+ }
79
+ /** Decisions are comments: the schema has no field for them, and the reader of the file needs them. */
80
+ export function renderCompleteDocument(config, schemaDirective, decisions = [], unconfirmed = []) {
81
+ return `#:schema ${schemaDirective}\n${decisions.map(decision => `# Decision: ${decision}\n`).join('')}${renderUnconfirmed(unconfirmed)}${emitToml(config)}${renderDecisionDefaults()}`;
82
+ }
@@ -0,0 +1,53 @@
1
+ export declare const SCAFFOLD_BACKING_SERVICES: Readonly<{
2
+ postgres: Readonly<{
3
+ image: "postgres:17-alpine";
4
+ environment: Readonly<{
5
+ POSTGRES_PASSWORD: "postgres";
6
+ }>;
7
+ volume: "/var/lib/postgresql/data";
8
+ healthcheck: readonly string[];
9
+ url: readonly string[];
10
+ }>;
11
+ redis: Readonly<{
12
+ image: "redis:7-alpine";
13
+ environment: Readonly<{}>;
14
+ volume: "/data";
15
+ healthcheck: readonly string[];
16
+ url: readonly string[];
17
+ }>;
18
+ }>;
19
+ export type ScaffoldBackingService = keyof typeof SCAFFOLD_BACKING_SERVICES;
20
+ export interface ComposeScaffoldInput {
21
+ /** The app directory, relative to the cockpit root; `.` for the root itself. */
22
+ readonly dir: string;
23
+ /** Whether the app directory holds a Dockerfile, which then builds the app image. */
24
+ readonly dockerfile: boolean;
25
+ /** Whether it holds a package.json, so a stock Node image can run it without a Dockerfile. */
26
+ readonly packageJson: boolean;
27
+ /** The command the app service runs; required without a Dockerfile, which otherwise sets it. */
28
+ readonly command?: readonly string[];
29
+ readonly port: number;
30
+ readonly with: readonly ScaffoldBackingService[];
31
+ }
32
+ export type ComposeScaffold = Readonly<{
33
+ kind: 'complete';
34
+ service: string;
35
+ portVariable: string;
36
+ volumes: readonly string[];
37
+ bytes: string;
38
+ }> | Readonly<{
39
+ kind: 'refusal';
40
+ message: string;
41
+ fix: string;
42
+ }>;
43
+ /** The compose.yml text for the declared app, with the [ports.bases] variable and volumes it introduces. */
44
+ export declare function renderComposeScaffold(input: ComposeScaffoldInput): ComposeScaffold;
45
+ export type ComposeScaffoldWrite = Readonly<{
46
+ kind: 'written';
47
+ path: string;
48
+ }> | Readonly<{
49
+ kind: 'exists';
50
+ path: string;
51
+ }>;
52
+ /** Creates root/compose.yml unless any Compose file is already there; it never replaces one. */
53
+ export declare function writeComposeScaffold(root: string, bytes: string): Promise<ComposeScaffoldWrite>;
@@ -0,0 +1,121 @@
1
+ // Writes a starting compose.yml for a small app that has none, once. The file is the user's from then
2
+ // on: cockpit never rewrites it, and cockpit.toml runs it as written through environment.compose_files.
3
+ // It is deliberately small (one app service, an optional database and cache from a fixed list), so
4
+ // anything beyond that is plain Compose the user adds, not more options here.
5
+ import { lstat, open } from 'node:fs/promises';
6
+ import { basename, isAbsolute, join, normalize } from 'node:path';
7
+ import { deriveSiblingVariable } from '../schema.js';
8
+ import { hasErrorCode } from '../environment/contract.js';
9
+ /** The names Compose looks for; any one of them already present means the product has a Compose file. */
10
+ const COMPOSE_FILE_NAMES = Object.freeze(['compose.yml', 'compose.yaml', 'docker-compose.yml', 'docker-compose.yaml']);
11
+ // Tags pinned to a major version, so a scaffold written today runs the same release line tomorrow.
12
+ // The credentials are local-only defaults for a stack bound to loopback; the app reads the URL.
13
+ export const SCAFFOLD_BACKING_SERVICES = Object.freeze({
14
+ postgres: Object.freeze({
15
+ image: 'postgres:17-alpine',
16
+ environment: Object.freeze({ POSTGRES_PASSWORD: 'postgres' }),
17
+ volume: '/var/lib/postgresql/data',
18
+ healthcheck: Object.freeze(['CMD', 'pg_isready', '-U', 'postgres']),
19
+ url: Object.freeze(['DATABASE_URL', 'postgres://postgres:postgres@postgres:5432/postgres']),
20
+ }),
21
+ redis: Object.freeze({
22
+ image: 'redis:7-alpine',
23
+ environment: Object.freeze({}),
24
+ volume: '/data',
25
+ healthcheck: Object.freeze(['CMD', 'redis-cli', 'ping']),
26
+ url: Object.freeze(['REDIS_URL', 'redis://redis:6379']),
27
+ }),
28
+ });
29
+ // One image per toolchain cockpit can recognise without a Dockerfile; anything else brings its own.
30
+ const TOOLCHAIN_IMAGE = 'node:22-alpine';
31
+ const json = (value) => JSON.stringify(value);
32
+ /** The compose.yml text for the declared app, with the [ports.bases] variable and volumes it introduces. */
33
+ export function renderComposeScaffold(input) {
34
+ const dir = input.dir === '.' ? '.' : normalize(input.dir);
35
+ if (isAbsolute(dir) || dir.split('/').includes('..') || dir.includes('\\') || /[\x00-\x1f\x7f$]/.test(dir))
36
+ return {
37
+ kind: 'refusal', message: `the app directory ${json(input.dir)} is not a plain path inside the cockpit root`,
38
+ fix: 'pass --scaffold-compose the app directory relative to the cockpit root, or . for the root itself'
39
+ };
40
+ if (!Number.isInteger(input.port) || input.port < 1 || input.port > 65_535)
41
+ return {
42
+ kind: 'refusal', message: `--port ${input.port} is not a TCP port`, fix: 'pass --port the port the app listens on inside its container'
43
+ };
44
+ if (!input.dockerfile && !input.packageJson)
45
+ return {
46
+ kind: 'refusal', message: `${dir === '.' ? 'the cockpit root' : dir} has neither a Dockerfile nor a package.json, so no image is known to run it`,
47
+ fix: 'add a Dockerfile to the app directory, then rerun the scaffold'
48
+ };
49
+ if (!input.dockerfile && !input.command?.length)
50
+ return {
51
+ kind: 'refusal', message: `${dir === '.' ? 'the cockpit root' : dir} has no Dockerfile, so the scaffold needs the command that starts the app`,
52
+ fix: 'pass --command with the start command, such as --command "npm start", or add a Dockerfile to the app directory'
53
+ };
54
+ const service = (dir === '.' ? 'app' : basename(dir)).toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '') || 'app';
55
+ const portVariable = `${service.toUpperCase().replaceAll('-', '_')}_HOST_PORT`;
56
+ const siblingVariable = deriveSiblingVariable(service);
57
+ const source = dir === '.' ? '.' : `./${dir}`;
58
+ const backing = [...new Set(input.with)].sort();
59
+ const lines = [
60
+ "# Written once by cockpit's Compose scaffold; it is yours to edit. cockpit runs it as",
61
+ '# written and never rewrites it. Every published port binds 127.0.0.1 and comes from a',
62
+ '# [ports.bases] variable, so parallel stacks do not collide.',
63
+ 'services:',
64
+ ` ${service}:`,
65
+ ...input.dockerfile ? [` build: \${${siblingVariable}:-${source}}`] : [` image: ${TOOLCHAIN_IMAGE}`],
66
+ // A minimal init as PID 1 forwards SIGTERM, so sleep and down stop the app promptly.
67
+ ' init: true',
68
+ ' working_dir: /app',
69
+ ...input.command?.length ? [` command: ${json(input.command)}`] : [],
70
+ ` ports: [${json(`127.0.0.1:\${${portVariable}:-${input.port}}:${input.port}`)}]`,
71
+ ` volumes: [${json(`${source}:/app`)}]`,
72
+ ...backing.length > 0 ? [
73
+ ' environment:',
74
+ ...backing.map(name => ` ${SCAFFOLD_BACKING_SERVICES[name].url[0]}: ${json(SCAFFOLD_BACKING_SERVICES[name].url[1])}`),
75
+ ' depends_on:',
76
+ ...backing.flatMap(name => [` ${name}:`, ' condition: service_healthy']),
77
+ ] : [],
78
+ ...backing.flatMap(name => {
79
+ const declared = SCAFFOLD_BACKING_SERVICES[name];
80
+ return [
81
+ ` ${name}:`,
82
+ ` image: ${declared.image}`,
83
+ ...Object.keys(declared.environment).length > 0 ? [' environment:',
84
+ ...Object.entries(declared.environment).map(([key, value]) => ` ${key}: ${json(value)}`)] : [],
85
+ ` volumes: [${json(`${name}-data:${declared.volume}`)}]`,
86
+ ' healthcheck:',
87
+ ` test: ${json(declared.healthcheck)}`,
88
+ ' interval: 5s',
89
+ ' retries: 10',
90
+ ];
91
+ }),
92
+ ...backing.length > 0 ? ['volumes:', ...backing.map(name => ` ${name}-data: {}`)] : [],
93
+ '',
94
+ ];
95
+ return { kind: 'complete', service, portVariable, volumes: backing.map(name => `${name}-data`),
96
+ bytes: lines.join('\n') };
97
+ }
98
+ /** Creates root/compose.yml unless any Compose file is already there; it never replaces one. */
99
+ export async function writeComposeScaffold(root, bytes) {
100
+ for (const name of COMPOSE_FILE_NAMES) {
101
+ const path = join(root, name);
102
+ try {
103
+ await lstat(path);
104
+ return { kind: 'exists', path };
105
+ }
106
+ catch (error) {
107
+ if (!hasErrorCode(error, 'ENOENT'))
108
+ throw error;
109
+ }
110
+ }
111
+ const path = join(root, 'compose.yml');
112
+ // `wx` fails if the file appeared since the check, so a concurrent write is never replaced.
113
+ const handle = await open(path, 'wx', 0o644);
114
+ try {
115
+ await handle.writeFile(bytes);
116
+ }
117
+ finally {
118
+ await handle.close();
119
+ }
120
+ return { kind: 'written', path };
121
+ }
@@ -0,0 +1,13 @@
1
+ export declare const EXIT_CODES: Readonly<{
2
+ readonly success: 0;
3
+ readonly failure: 2;
4
+ readonly cancelled: 130;
5
+ }>;
6
+ export type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];
7
+ export interface InvocationRecord {
8
+ readonly verb: string;
9
+ readonly durationMs: number;
10
+ readonly exitCode: ExitCode;
11
+ readonly resolvedConfigPath: string | null;
12
+ }
13
+ export declare function createInvocationRecord(input: InvocationRecord): InvocationRecord;
@@ -0,0 +1,20 @@
1
+ import { isAbsolute, normalize } from 'node:path';
2
+ export const EXIT_CODES = Object.freeze({ success: 0, failure: 2, cancelled: 130 });
3
+ export function createInvocationRecord(input) {
4
+ const { verb, durationMs, exitCode, resolvedConfigPath } = input;
5
+ if (typeof verb !== 'string' || !/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*)*$/.test(verb)) {
6
+ throw new Error('Invocation verb must be non-empty command words without control characters');
7
+ }
8
+ if (!Number.isSafeInteger(durationMs) || durationMs < 0) {
9
+ throw new Error('Invocation durationMs must be a non-negative finite safe integer');
10
+ }
11
+ if (!Object.values(EXIT_CODES).includes(exitCode)) {
12
+ throw new Error('Invocation exitCode must be success (0), failure (2), or cancellation (130)');
13
+ }
14
+ if (resolvedConfigPath !== null && (typeof resolvedConfigPath !== 'string'
15
+ || !isAbsolute(resolvedConfigPath) || normalize(resolvedConfigPath) !== resolvedConfigPath
16
+ || /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/.test(resolvedConfigPath))) {
17
+ throw new Error('Invocation resolvedConfigPath must be null or a normalized absolute path without control characters');
18
+ }
19
+ return { verb, durationMs, exitCode, resolvedConfigPath };
20
+ }
@@ -0,0 +1,9 @@
1
+ export interface InstalledPackageMetadata {
2
+ readonly name: string;
3
+ readonly version: string;
4
+ readonly engines: Readonly<{
5
+ node: string;
6
+ }>;
7
+ readonly cockpitSourceRevision: string;
8
+ }
9
+ export declare const installedPackageMetadata: InstalledPackageMetadata;
@@ -0,0 +1,19 @@
1
+ import { readFileSync } from 'node:fs';
2
+ const actual = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
3
+ const record = actual !== null && typeof actual === 'object' && !Array.isArray(actual) ? actual : undefined;
4
+ const fields = record;
5
+ const engines = fields?.engines;
6
+ const text = (value) => typeof value === 'string' && value.length > 0
7
+ && value.length <= 256 && value.trim() === value && !/[\u0000-\u0020\u007f-\u009f]/.test(value);
8
+ if (!fields || !text(fields.name) || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(fields.name)
9
+ || !text(fields.version) || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(fields.version)
10
+ || !engines || typeof engines.node !== 'string' || engines.node.length === 0 || engines.node.length > 256
11
+ || !/^[0-9v<>=~^.*|xX+ -]+$/.test(engines.node) || !/\d/.test(engines.node)
12
+ || !text(fields.cockpitSourceRevision))
13
+ throw new Error('Installed package metadata is invalid: name, version, engines.node and cockpitSourceRevision are required');
14
+ export const installedPackageMetadata = Object.freeze({
15
+ name: fields.name,
16
+ version: fields.version,
17
+ engines: Object.freeze({ node: engines.node }),
18
+ cockpitSourceRevision: fields.cockpitSourceRevision,
19
+ });
@@ -0,0 +1,53 @@
1
+ /** A cockpit that only publishes agent surfaces, or one that also declares a runtime. */
2
+ export type ReadinessState = 'surfaces-only' | 'runtime';
3
+ export interface ReadinessEntry {
4
+ readonly id: string;
5
+ /** Why cockpit needs it, in one sentence. */
6
+ readonly needs: string;
7
+ /** Required prerequisites are refused by `refusedBy`; recommended ones only warn. */
8
+ readonly level: 'required' | 'recommended';
9
+ readonly appliesTo: readonly ReadinessState[];
10
+ /** The existing diagnostics that report this prerequisite missing. */
11
+ readonly codes: readonly string[];
12
+ /** The command that refuses while a required prerequisite is missing, and the code it refuses with. */
13
+ readonly refusedBy?: Readonly<{
14
+ command: string;
15
+ code: string;
16
+ }>;
17
+ /** What to do when a finding carries no fix of its own. */
18
+ readonly fix: string;
19
+ /** The agent tools the prerequisite concerns; it applies only while one of them is listed. */
20
+ readonly tools?: readonly string[];
21
+ }
22
+ export declare const READINESS_CATALOGUE: readonly ReadinessEntry[];
23
+ export interface ReadinessFinding {
24
+ readonly code: string;
25
+ readonly severity: string;
26
+ readonly target?: string;
27
+ readonly message: string;
28
+ readonly fix: string;
29
+ }
30
+ export interface ReadinessGap {
31
+ readonly id: string;
32
+ readonly level: ReadinessEntry['level'];
33
+ readonly needs: string;
34
+ readonly findings: readonly ReadinessFinding[];
35
+ }
36
+ export interface ReadinessReport {
37
+ /** Undetermined before init has classified the estate and while no cockpit.toml says. */
38
+ readonly state: ReadinessState | 'undetermined';
39
+ /** Prerequisites some reported diagnostic says are missing, in catalogue order. */
40
+ readonly gaps: readonly ReadinessGap[];
41
+ /** Catalogue ids that apply to this state and that no reported diagnostic named. */
42
+ readonly noFinding: readonly string[];
43
+ }
44
+ type ReportedDiagnostic = Readonly<{
45
+ code: string;
46
+ severity: string;
47
+ message: string;
48
+ target?: string;
49
+ fix?: string;
50
+ }>;
51
+ /** Groups the diagnostics a command reported under the prerequisites they name, for the listed tools. */
52
+ export declare function readinessReport(state: ReadinessReport['state'], diagnostics: readonly ReportedDiagnostic[], tools: readonly string[]): ReadinessReport;
53
+ export {};
@@ -0,0 +1,88 @@
1
+ // The prerequisites cockpit itself depends on in a product and its cockpit, each with why it is
2
+ // needed and the diagnostics that already report its absence. It detects nothing: the owners of
3
+ // those diagnostics do, and a required prerequisite is refused by the command that needs it. This
4
+ // module only gathers what a command reported into one list a person, or the setup agent walking
5
+ // them through it, can act on. Judgement (which test runner, whether existing tests mean anything)
6
+ // stays with them; a gap here is a fact.
7
+ import { ENVIRONMENT_CODES } from './environment/contract.js';
8
+ const C = ENVIRONMENT_CODES;
9
+ const BOTH = Object.freeze(['surfaces-only', 'runtime']);
10
+ const RUNTIME = Object.freeze(['runtime']);
11
+ export const READINESS_CATALOGUE = [
12
+ { id: 'cockpit-git', level: 'required', appliesTo: BOTH, codes: [C.SETUP_PATH_TYPE_INVALID],
13
+ refusedBy: { command: 'setup', code: C.SETUP_PATH_TYPE_INVALID },
14
+ needs: 'setup confines every repository write to the cockpit\'s Git working tree, and teammates get the cockpit by cloning it',
15
+ fix: 'run git init in the cockpit root' },
16
+ { id: 'cockpit-committed', level: 'required', appliesTo: BOTH, codes: [C.GIT_STATE_UNHEALTHY, C.CHECKOUT_ADJACENCY_UNHEALTHY],
17
+ refusedBy: { command: 'preflight', code: C.GIT_STATE_UNHEALTHY },
18
+ needs: 'preflight vouches only for a committed cockpit, and a teammate\'s clone holds only what was committed',
19
+ fix: 'review git status --short in the cockpit root with a person, then commit the cockpit files' },
20
+ { id: 'cockpit-installed', level: 'recommended', appliesTo: BOTH, codes: [C.COCKPIT_NOT_INSTALLED],
21
+ needs: 'every instruction cockpit generates runs npx --no cockpit from the cockpit root, and the guard hook runs the copy installed there',
22
+ fix: 'run npm install in the cockpit root, then npx --no cockpit sync --config <your cockpit.toml> in a terminal' },
23
+ { id: 'cockpit-launcher-proven', level: 'recommended', appliesTo: BOTH, codes: [C.COCKPIT_LAUNCHER_UNPROVEN],
24
+ needs: 'the guard runs a printed npx --no cockpit command without asking only when it can prove npx starts the installed cockpit, and a tool that cannot ask a person refuses what it would ask about',
25
+ fix: 'run cockpit as ./node_modules/.bin/cockpit <command> --config <your cockpit.toml> from the cockpit root, or remove what stops npx from proving it' },
26
+ { id: 'siblings-present', level: 'required', appliesTo: BOTH, codes: [C.SIBLING_MISSING, C.SETUP_NONE_ABSENT],
27
+ refusedBy: { command: 'check', code: C.SIBLING_MISSING },
28
+ needs: 'mounts, directory grants, guides and generated services all point at each declared repository',
29
+ fix: 'run npx --no cockpit setup --config <your cockpit.toml>, or clone the repository where cockpit.toml declares it' },
30
+ { id: 'sibling-remote', level: 'recommended', appliesTo: BOTH, codes: [C.SIBLING_REMOTE_UNRECORDED],
31
+ needs: 'setup in a teammate\'s clone of the cockpit obtains only the repositories whose remote cockpit.toml records',
32
+ fix: 'record the pushed repository with npx --no cockpit sibling add <slug> --remote <url> --config <your cockpit.toml>' },
33
+ { id: 'sibling-instructions', level: 'recommended', appliesTo: BOTH,
34
+ codes: [C.SIBLING_INSTRUCTIONS_AGENTS_ONLY, C.SIBLING_INSTRUCTIONS_CLAUDE_ONLY, C.SIBLING_INSTRUCTIONS_UNLINKED,
35
+ C.SIBLING_INSTRUCTIONS_MISSING, C.SIBLING_INSTRUCTIONS_TOOL_ONLY],
36
+ needs: 'each agent tool loads only the instruction files it reads in a repository, so an agent started there gets the repository\'s instructions in every listed tool only when they sit in the files those tools read',
37
+ fix: 'follow the fix on each finding; npx --no cockpit setup --config <your cockpit.toml> proposes the changes that need no judgement' },
38
+ { id: 'container-runtime', level: 'required', appliesTo: RUNTIME, codes: [C.RUNTIME_UNAVAILABLE, C.RUNTIME_UNMEASURED],
39
+ refusedBy: { command: 'up', code: C.RUNTIME_UNMEASURED },
40
+ needs: 'up runs the stack with Docker Compose, and only on a runtime this machine has measured',
41
+ fix: 'start Docker, then run npx --no cockpit runtime measure --config <your cockpit.toml> --image <local-image>' },
42
+ { id: 'machine-floor', level: 'required', appliesTo: RUNTIME,
43
+ codes: [C.MACHINE_MEMORY_BELOW_FLOOR, C.MACHINE_CPU_BELOW_FLOOR, C.MACHINE_DISK_BELOW_FLOOR],
44
+ refusedBy: { command: 'up', code: C.MACHINE_MEMORY_BELOW_FLOOR },
45
+ needs: 'up refuses to start a stack the container runtime cannot hold, measured against environment.machine',
46
+ fix: 'give the container runtime more capacity, or lower environment.machine deliberately with whoever owns the product' },
47
+ { id: 'env-schema', level: 'required', appliesTo: RUNTIME, codes: [C.ENV_SCHEMA_UNHEALTHY, C.ENV_SCHEMA_TOOL_MISSING],
48
+ refusedBy: { command: 'preflight', code: C.ENV_SCHEMA_UNHEALTHY },
49
+ needs: 'preflight validates a repository\'s .env.schema with Varlock, without reading a value, wherever the repository has one',
50
+ fix: 'install Varlock, then make the environment satisfy the schema' },
51
+ { id: 'guard-hosts', level: 'recommended', appliesTo: RUNTIME, codes: [C.GUARD_HOSTS_UNDECLARED],
52
+ needs: 'the guard denies agent commands that reach a non-local database or vendor API only when cockpit.toml names its hosts',
53
+ fix: 'list the hosts under [guards] in cockpit.toml, or write both lists empty to record that none exists' },
54
+ { id: 'testing-gate', level: 'recommended', appliesTo: RUNTIME, codes: [C.TESTING_GATE_UNDECLARED],
55
+ needs: 'test-run runs only the declared [testing] gates, and no work is graded PASS without one',
56
+ fix: 'declare the product test commands as [testing] stages with a targeted and a full gate' },
57
+ { id: 'secrets-untracked', level: 'recommended', appliesTo: BOTH, codes: [C.SECRET_FILE_TRACKED],
58
+ needs: 'the guard keeps agents from reading secret files, but a committed one is in every clone already',
59
+ fix: 'stop tracking the file, ignore it, rotate what it held, and commit a .env.example of the variable names' },
60
+ { id: 'harness-path-absolute', level: 'recommended', appliesTo: BOTH, codes: [C.GUARD_SEARCH_PATH_RELATIVE],
61
+ needs: 'the guard asks a person before every agent command while the PATH the agent harness runs with holds a relative entry, and a tool that cannot ask a person refuses what it asks about',
62
+ fix: 'remove relative and empty entries from the PATH the agent harness starts with' },
63
+ { id: 'codex-hook-trusted', level: 'recommended', appliesTo: BOTH, codes: [C.CODEX_HOOK_UNTRUSTED], tools: ['codexcli'],
64
+ needs: 'Codex runs the guard hook only once a person on this machine has trusted it',
65
+ fix: 'a person trusts the hook from codex in a terminal in the cockpit root' },
66
+ { id: 'work-evaluator', level: 'recommended', appliesTo: BOTH, codes: [C.EVALUATOR_UNAVAILABLE],
67
+ needs: 'work grade and cockpit grade run the first agent tool work.evaluators lists that can run, as the evaluator independent of the agent that did the work',
68
+ fix: 'install and sign in to an evaluator tool work.evaluators lists, or list one you have' },
69
+ ];
70
+ /** Groups the diagnostics a command reported under the prerequisites they name, for the listed tools. */
71
+ export function readinessReport(state, diagnostics, tools) {
72
+ const gaps = [];
73
+ const noFinding = [];
74
+ for (const entry of READINESS_CATALOGUE) {
75
+ if (entry.tools && !entry.tools.some(tool => tools.includes(tool)))
76
+ continue;
77
+ const findings = diagnostics.filter(({ code }) => entry.codes.includes(code));
78
+ if (findings.length > 0) {
79
+ gaps.push({ id: entry.id, level: entry.level, needs: entry.needs,
80
+ findings: findings.map(({ code, severity, target, message, fix }) => ({
81
+ code, severity, ...(target === undefined ? {} : { target }), message, fix: fix ?? entry.fix
82
+ })) });
83
+ }
84
+ else if (state === 'undetermined' ? entry.appliesTo.length === BOTH.length : entry.appliesTo.includes(state))
85
+ noFinding.push(entry.id);
86
+ }
87
+ return { state, gaps, noFinding };
88
+ }
@@ -0,0 +1,24 @@
1
+ import { type GeneratedPathProducer, type OwnershipAuthority, type OwnershipManifest } from '../surfaces/ownership.js';
2
+ interface NamedAuthority {
3
+ readonly manifest: OwnershipManifest;
4
+ readonly authority: OwnershipAuthority;
5
+ readonly declarations: unknown;
6
+ }
7
+ export interface NamedPathInput extends NamedAuthority {
8
+ readonly path: string;
9
+ readonly exists: boolean;
10
+ }
11
+ export type NamedPathResult = Readonly<{
12
+ kind: 'resolved';
13
+ path: string;
14
+ }> | Readonly<{
15
+ kind: 'declared-before-sync' | 'unavailable';
16
+ path: string;
17
+ producer: GeneratedPathProducer;
18
+ }> | Readonly<{
19
+ kind: 'refusal';
20
+ path: string;
21
+ reason: string;
22
+ }>;
23
+ export declare function resolveNamedPath(input: NamedPathInput): NamedPathResult;
24
+ export {};
@@ -0,0 +1,27 @@
1
+ import { projectGeneratedPathFacts, } from '../surfaces/ownership.js';
2
+ function safePath(path) {
3
+ return typeof path === 'string' && path.length > 0 && !/[\\\u0000-\u001f\u007f]/.test(path)
4
+ && !path.startsWith('/') && !/^[A-Za-z]:/.test(path)
5
+ && path.split('/').every(part => part !== '' && part !== '.' && part !== '..');
6
+ }
7
+ function resolveProjectedPath(path, exists, facts) {
8
+ if (!safePath(path))
9
+ return { kind: 'refusal', path, reason: 'reference must name an exact repository-relative path' };
10
+ if (exists)
11
+ return { kind: 'resolved', path };
12
+ const fact = facts.find(fact => fact.path === path);
13
+ const declaration = fact?.declaration;
14
+ if (declaration?.applicability === 'sibling-unavailable') {
15
+ return { kind: 'unavailable', path, producer: declaration.producer };
16
+ }
17
+ if (fact?.allowsMissing && declaration?.applicability === 'declared-before-sync') {
18
+ return { kind: 'declared-before-sync', path, producer: declaration.producer };
19
+ }
20
+ return { kind: 'refusal', path, reason: 'missing reference has no applicable exact producer declaration' };
21
+ }
22
+ export function resolveNamedPath(input) {
23
+ const projected = projectGeneratedPathFacts(input.manifest, input.authority, input.declarations);
24
+ if (projected.kind === 'refusal')
25
+ return { kind: 'refusal', path: input.path, reason: 'generated path authority was refused' };
26
+ return resolveProjectedPath(input.path, input.exists, projected.facts);
27
+ }
@@ -0,0 +1,15 @@
1
+ /** A command refusal with one error diagnostic: exit status 2, no result and nothing to write. */
2
+ export declare function refusalOutcome(code: string, message: string, target?: string, fix?: string): {
3
+ kind: 'refusal';
4
+ status: 2;
5
+ artifact: null;
6
+ destination: null;
7
+ result: null;
8
+ diagnostics: {
9
+ severity: 'error';
10
+ code: string;
11
+ message: string;
12
+ target?: string | undefined;
13
+ fix?: string | undefined;
14
+ }[];
15
+ };
@@ -0,0 +1,7 @@
1
+ /** A command refusal with one error diagnostic: exit status 2, no result and nothing to write. */
2
+ export function refusalOutcome(code, message, target, fix) {
3
+ return {
4
+ kind: 'refusal', status: 2, artifact: null, destination: null, result: null,
5
+ diagnostics: [{ severity: 'error', code, message, ...(target === undefined ? {} : { target }), ...(fix === undefined ? {} : { fix }) }],
6
+ };
7
+ }
@@ -0,0 +1,31 @@
1
+ import { type CockpitConfig as CockpitConfigValue, type Provenance } from './schema.js';
2
+ export interface RegistryMcpSource {
3
+ readonly slug: string;
4
+ readonly siblingDir: string;
5
+ readonly path: string;
6
+ readonly provenance: Provenance;
7
+ }
8
+ export interface RegistrySecretPath {
9
+ readonly slug: string;
10
+ readonly siblingDir: string;
11
+ readonly path: string;
12
+ readonly kind: 'instance';
13
+ }
14
+ type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer Item)[] ? readonly DeepReadonly<Item>[] : T extends object ? {
15
+ readonly [Key in keyof T]: DeepReadonly<T[Key]>;
16
+ } : T;
17
+ export interface DerivedRegistry {
18
+ readonly config: DeepReadonly<CockpitConfigValue>;
19
+ readonly mcpSources: readonly RegistryMcpSource[];
20
+ readonly secretPaths: readonly RegistrySecretPath[];
21
+ }
22
+ export type RegistryDerivationResult = Readonly<{
23
+ kind: 'complete';
24
+ result: DerivedRegistry;
25
+ }> | Readonly<{
26
+ kind: 'refusal';
27
+ code: 'REGISTRY_INPUT_INVALID';
28
+ message: string;
29
+ }>;
30
+ export declare function deriveRegistry(input: unknown): RegistryDerivationResult;
31
+ export {};