@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,628 @@
1
+ import { lstat, realpath } from 'node:fs/promises';
2
+ import { delimiter, dirname, isAbsolute, join, resolve } from 'node:path';
3
+ import { loadCockpitConfig } from '../config/load.js';
4
+ import { evaluatorUnavailable, findEvaluators } from '../grading/evaluate.js';
5
+ import { createWorkStore } from '../work-orders/store.js';
6
+ import { cockpitCommand } from '../fix-catalogue.js';
7
+ import {} from './application/doctor.js';
8
+ import { ObservationProducerRefusal } from './application/health.js';
9
+ import { decodeLifecycleRecord, revalidateService } from './application/lifecycle.js';
10
+ import {} from './application/lifecycle-context.js';
11
+ import { decodeCoordinationRecord, observeClaimState, readRootPortInventory } from './claims.js';
12
+ import { inspectCodexHookTrust } from './codex-hook-trust.js';
13
+ import { observeEnvSchemas } from './env-schema-observation.js';
14
+ import { hasErrorCode, RefusalError, ENVIRONMENT_CODES, errorCause, } from './contract.js';
15
+ import { declaresNothingToRun, runtimeNotDeclared } from './declaration.js';
16
+ import { resolveDockerExecutable, resolveGitExecutable } from './executable.js';
17
+ import { createGitObservationStep, observeGit, observeRunningCheckout } from './git-observation.js';
18
+ import { inspectProjectReadiness } from './readiness-composition.js';
19
+ import { stackId } from './identity.js';
20
+ import { createLifecycleCommandAdapters } from './lifecycle-composition.js';
21
+ import { observeStateSeed, resolveLifecycleStage } from './lifecycle-stage.js';
22
+ import { refreshObservationPostures, resolveObservationPostures } from './observations.js';
23
+ import { deriveEntrypoints, observeRetainedPortFamily, projectRetainedPortFamilyResult, revalidatePortLeaseProjection, } from './ports.js';
24
+ import { generatedApplicationProjection } from './generated-application/authorities.js';
25
+ import { checkGeneratedEnvironment } from './generated-application/check.js';
26
+ import { observeGeneratedApplicationSurfaces } from './generated-application/surface-observation.js';
27
+ import { syncGeneratedEnvironment } from './generated-application/sync.js';
28
+ import { admitRuntimeMeasurement, observeDockerRuntime, observeRuntimeMeasurement, observeRuntimeMountMaterialization, observeRuntimePortFamily, } from './runtime.js';
29
+ import { readLocalRuntimeMeasurements } from './runtime-measurement-record.js';
30
+ import { consumeSetupCheckoutProjection, observeSetupCheckout, withSetupCheckoutProjection, } from './setup-observation.js';
31
+ import { observeConfiguration, readState, selectStateRoot } from './state.js';
32
+ import { observeTemplateHealth } from './templates.js';
33
+ function captureInput(value) {
34
+ try {
35
+ if (!value || typeof value !== 'object' || Array.isArray(value))
36
+ return undefined;
37
+ const descriptors = Object.getOwnPropertyDescriptors(value);
38
+ const keys = ['configPath', 'env', 'timeoutMs'];
39
+ if (Reflect.ownKeys(descriptors).length !== keys.length
40
+ || keys.some((key) => !descriptors[key] || !('value' in descriptors[key])))
41
+ return undefined;
42
+ const configPath = descriptors.configPath.value;
43
+ const rawEnvironment = descriptors.env.value;
44
+ const timeoutMs = descriptors.timeoutMs.value;
45
+ if (typeof configPath !== 'string' || configPath.length === 0 || configPath.includes('\0')
46
+ || typeof timeoutMs !== 'number' || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000
47
+ || !rawEnvironment || typeof rawEnvironment !== 'object' || Array.isArray(rawEnvironment))
48
+ return undefined;
49
+ const env = {};
50
+ for (const [name, entry] of Object.entries(rawEnvironment)) {
51
+ if (name.length === 0 || name.includes('\0') || name.includes('=')
52
+ || entry !== undefined && (typeof entry !== 'string' || entry.includes('\0')))
53
+ return undefined;
54
+ env[name] = entry;
55
+ }
56
+ return { configPath: resolve(configPath), env, timeoutMs };
57
+ }
58
+ catch {
59
+ // A throwing accessor or proxy trap makes the input malformed, which the caller refuses.
60
+ return undefined;
61
+ }
62
+ }
63
+ function gitObservationEnvironment(env) {
64
+ return Object.fromEntries(Object.entries(env)
65
+ .filter((entry) => (entry[0] === 'LANG' || entry[0] === 'LC_ALL' || entry[0] === 'TZ') && typeof entry[1] === 'string'));
66
+ }
67
+ function checkoutObservationEnvironment(env) {
68
+ return Object.fromEntries(Object.entries(env)
69
+ .filter((entry) => typeof entry[1] === 'string'));
70
+ }
71
+ function commandConfigPath(value) {
72
+ try {
73
+ if (!value || typeof value !== 'object' || Array.isArray(value))
74
+ return undefined;
75
+ const descriptors = Object.getOwnPropertyDescriptors(value);
76
+ const descriptor = descriptors.configPath;
77
+ return Reflect.ownKeys(descriptors).length === 1 && descriptor && 'value' in descriptor
78
+ && typeof descriptor.value === 'string'
79
+ ? descriptor.value
80
+ : undefined;
81
+ }
82
+ catch {
83
+ // A throwing accessor or proxy trap makes the command malformed, which the caller refuses.
84
+ return undefined;
85
+ }
86
+ }
87
+ // `sleep` releases the last holder and stops the services, so every runtime probe after it fails;
88
+ // reporting that as missing observation evidence hid the one command that brings the stack back.
89
+ async function sleepingStack(namespace, stack, configPath) {
90
+ const stored = await readState(namespace.paths.coordination(stack));
91
+ if (stored.kind !== 'complete')
92
+ return undefined;
93
+ const coordination = decodeCoordinationRecord(stored.value, stack);
94
+ if (!coordination || coordination.holders.length > 0)
95
+ return undefined;
96
+ const fix = cockpitCommand('up', { config: configPath });
97
+ return {
98
+ severity: 'error', code: ENVIRONMENT_CODES.STACK_NOT_RUNNING, target: stack,
99
+ message: 'the stack is asleep: no session holds it and its services are stopped, so there is nothing running to check; run up to start it, then rerun this command',
100
+ fix,
101
+ };
102
+ }
103
+ // Before the first `up` doctor still measures the machine, so a missing runtime is named there
104
+ // rather than first at bring-up.
105
+ function runtimeUnavailable(reason, target) {
106
+ return {
107
+ severity: 'error',
108
+ code: ENVIRONMENT_CODES.RUNTIME_UNAVAILABLE,
109
+ message: `the container runtime that runs this stack is not usable on this machine: ${reason}`,
110
+ target,
111
+ fix: 'install or start Docker so that docker info succeeds, then rerun this command',
112
+ };
113
+ }
114
+ function producers(input) {
115
+ const observedAt = () => new Date().toISOString();
116
+ const siblingRoot = resolve(dirname(input.config.result.source.path), input.config.result.config.cockpit.siblings_root);
117
+ const declaresStack = input.config.result.config.environment.compose_files.length > 0;
118
+ const runningStack = declaresStack && !input.stopped;
119
+ // Only doctor reaches here for a cockpit that declares nothing to run; a mounted sibling there
120
+ // gives Docker nothing to do, so a missing runtime must not be reported as a gap.
121
+ const configuredRuntime = !declaresNothingToRun(input.config.result.config.environment);
122
+ const runtimeMeasurement = configuredRuntime && !runningStack ? (async () => {
123
+ const docker = await resolveDockerExecutable(input.env);
124
+ if (docker.state !== 'available') {
125
+ if (input.stopped)
126
+ input.notice([runtimeUnavailable(docker.reason, input.stack)]);
127
+ return undefined;
128
+ }
129
+ const observed = await observeDockerRuntime({
130
+ executable: docker.value,
131
+ cwd: input.checkout.currentCheckoutPath,
132
+ env: Object.fromEntries(Object.entries(input.env).filter((entry) => ['PATH', 'DOCKER_HOST', 'DOCKER_CONTEXT', 'DOCKER_CONFIG', 'DOCKER_TLS_VERIFY', 'DOCKER_CERT_PATH'].includes(entry[0])
133
+ && typeof entry[1] === 'string')),
134
+ timeoutMs: input.timeoutMs,
135
+ }, input.execute);
136
+ if (observed.kind !== 'complete')
137
+ return undefined;
138
+ return observeRuntimeMeasurement(admitRuntimeMeasurement(observed, await readLocalRuntimeMeasurements(input.namespace.root)), input.stack);
139
+ })() : undefined;
140
+ const runtimeSession = runningStack ? (async () => {
141
+ const adapters = createLifecycleCommandAdapters({
142
+ configPath: input.config.result.source.path,
143
+ env: input.env,
144
+ sessionId: 'cockpit-health-read-only',
145
+ timeoutMs: input.timeoutMs,
146
+ }, input.execute);
147
+ const loaded = await adapters.load({ configPath: input.config.result.source.path });
148
+ if (loaded.kind === 'refusal')
149
+ return input.refuse?.(loaded.diagnostics);
150
+ if (loaded.kind !== 'complete' || loaded.result.stackId !== input.stack)
151
+ return undefined;
152
+ const stored = await readState(loaded.result.namespace.paths.lifecycle(loaded.result.stackId));
153
+ if (stored.kind !== 'complete')
154
+ return undefined;
155
+ const lifecycle = decodeLifecycleRecord(stored.value, loaded.result);
156
+ if (!lifecycle)
157
+ return undefined;
158
+ const retained = await observeRetainedPortFamily({
159
+ namespace: loaded.result.namespace,
160
+ stackId: loaded.result.stackId,
161
+ holderId: lifecycle.value.holderId,
162
+ checkoutId: loaded.result.checkoutId,
163
+ config: loaded.result.ports,
164
+ });
165
+ if (retained.kind !== 'complete')
166
+ return undefined;
167
+ const entrypoints = deriveEntrypoints({ lease: retained.lease, declarations: loaded.result.entrypointDerivatives });
168
+ if (entrypoints.kind !== 'complete')
169
+ return undefined;
170
+ const ports = await projectRetainedPortFamilyResult({ retainedResult: retained, entrypointResult: entrypoints });
171
+ if (!ports)
172
+ return undefined;
173
+ const readiness = await adapters.withRuntimeReadiness(loaded.result, async (value) => value, ports, (diagnostics) => input.refuse?.(diagnostics));
174
+ return readiness ? { adapters, context: loaded.result, readiness, lifecycle, ports,
175
+ lifecycleSnapshot: JSON.stringify(stored.value) } : undefined;
176
+ })() : undefined;
177
+ const stableSession = async () => {
178
+ const session = await runtimeSession;
179
+ if (!session)
180
+ return undefined;
181
+ const [configuration, stored, leaseCurrent] = await Promise.all([
182
+ loadCockpitConfig(input.config.result.source.path),
183
+ readState(session.context.namespace.paths.lifecycle(session.context.stackId)),
184
+ revalidatePortLeaseProjection({ projection: session.ports, namespace: session.context.namespace }),
185
+ ]);
186
+ return configuration.kind === 'complete' && configuration.result.source.sha256 === input.config.result.source.sha256
187
+ && stored.kind === 'complete' && JSON.stringify(stored.value) === session.lifecycleSnapshot && leaseCurrent
188
+ ? session : undefined;
189
+ };
190
+ // What stood in the way when the generated publication could not be checked: check's own errors.
191
+ // Their codes belong to check's owners (GENERATED_*, COMPOSE_*), which the environment registry does not list.
192
+ let checkRefusal = [];
193
+ const checkedApplication = (async () => {
194
+ const session = await runtimeSession;
195
+ if (runningStack && !session)
196
+ return undefined;
197
+ const checked = await checkGeneratedEnvironment({ configPath: input.config.result.source.path }, { execute: input.execute });
198
+ if (checked.kind !== 'complete')
199
+ checkRefusal = checked.diagnostics.filter(({ severity }) => severity === 'error');
200
+ return checked.kind === 'complete' ? generatedApplicationProjection(checked.result.application) : undefined;
201
+ })();
202
+ const machineFloor = !runningStack && input.config.result.config.environment.machine ? (async () => {
203
+ const adapters = createLifecycleCommandAdapters({
204
+ configPath: input.config.result.source.path,
205
+ env: input.env,
206
+ sessionId: 'cockpit-health-machine-floor',
207
+ timeoutMs: input.timeoutMs,
208
+ }, input.execute);
209
+ const loaded = await adapters.load({ configPath: input.config.result.source.path });
210
+ if (loaded.kind !== 'complete' || loaded.result.stackId !== input.stack)
211
+ return undefined;
212
+ return adapters.withMachineFloorReadiness(loaded.result, async (value) => value, (diagnostics) => { input.notice(diagnostics); return undefined; });
213
+ })() : undefined;
214
+ const inspectCurrentServices = async () => {
215
+ const session = await stableSession();
216
+ const completed = session?.lifecycle.value.completedServices;
217
+ if (!session || !completed
218
+ || completed.length !== session.readiness.topology.services.length
219
+ || JSON.stringify(completed.map(({ service }) => service).sort())
220
+ !== JSON.stringify(session.readiness.topology.services.map(({ id }) => id).sort()))
221
+ return undefined;
222
+ const values = new Map();
223
+ for (const service of completed) {
224
+ const admitted = await revalidateService(session.readiness.topology, service, { execute: input.execute });
225
+ if (admitted.kind !== 'complete')
226
+ return undefined;
227
+ values.set(service.service, admitted.evidence);
228
+ }
229
+ return await stableSession() ? values : undefined;
230
+ };
231
+ const currentServices = runningStack ? inspectCurrentServices() : undefined;
232
+ const observations = {
233
+ surfaces: async () => {
234
+ const application = await checkedApplication;
235
+ const observations = application && observeGeneratedApplicationSurfaces(application);
236
+ if (!observations && checkRefusal.length > 0)
237
+ throw new ObservationProducerRefusal('surface health needs a checked generated publication, and check refused it', checkRefusal);
238
+ if (!observations)
239
+ throw new RefusalError('surface health needs a current checked generated publication; run npx --no cockpit sync --config <your cockpit.toml>');
240
+ return observations;
241
+ },
242
+ claims: async () => [await observeClaimState({
243
+ namespace: input.namespace, stackId: input.stack, observedAt: observedAt(),
244
+ })],
245
+ git: async () => {
246
+ // Git status may refresh its index while checked publication authenticates checkout authority.
247
+ await machineFloor;
248
+ await checkedApplication;
249
+ const step = createGitObservationStep({
250
+ executable: input.git,
251
+ checkoutRoot: input.checkout.currentCheckoutPath,
252
+ env: gitObservationEnvironment(input.env),
253
+ timeoutMs: input.timeoutMs,
254
+ stackId: input.stack,
255
+ source: input.config.result.source.path,
256
+ checkoutId: input.checkout.checkoutId,
257
+ });
258
+ if (!step)
259
+ throw new RefusalError('Git health observation could not be planned');
260
+ const result = observeGit(step, await input.execute(step), {
261
+ stackId: input.stack,
262
+ checkoutId: input.checkout.checkoutId,
263
+ source: input.config.result.source.path,
264
+ });
265
+ if (result.kind !== 'complete')
266
+ throw new RefusalError('Git health observation is unavailable');
267
+ if (!runningStack)
268
+ return result.observations;
269
+ const session = await stableSession();
270
+ if (!session)
271
+ throw new RefusalError('running checkout health needs unchanged runtime and lifecycle authority');
272
+ const time = observedAt();
273
+ const setup = await observeSetupCheckout({ source: input.config.result.source,
274
+ cockpitName: input.config.result.config.cockpit.name, observedAt: time,
275
+ env: checkoutObservationEnvironment(input.env), gitExecutable: input.git });
276
+ const running = await withSetupCheckoutProjection(setup, { source: input.config.result.source,
277
+ cockpitName: input.config.result.config.cockpit.name, evaluatedAt: Date.parse(time) }, (projection) => observeRunningCheckout(session.readiness.topology, projection, session.context.runtimeSha256, time));
278
+ if (running?.kind !== 'complete')
279
+ throw new RefusalError('running checkout identity observation is unavailable');
280
+ return [...result.observations, running.observation];
281
+ },
282
+ host: async () => {
283
+ if (input.config.result.config.environment.machine) {
284
+ if (machineFloor) {
285
+ const current = await machineFloor;
286
+ // A stopped stack's unusable runtime is already a notice that names why.
287
+ if (!current && input.stopped)
288
+ return [];
289
+ if (!current)
290
+ throw new RefusalError('configured host capacity needs authenticated machine-floor execution evidence');
291
+ return current;
292
+ }
293
+ const current = await runtimeSession;
294
+ if (!current)
295
+ throw new RefusalError('configured host capacity needs authenticated machine-floor execution evidence');
296
+ return current.readiness.hostObservations;
297
+ }
298
+ return [];
299
+ },
300
+ lifecycle: async () => [await observeStateSeed({
301
+ namespace: input.namespace, stackId: input.stack, observedAt: observedAt(),
302
+ })],
303
+ ports: async () => {
304
+ if (!runningStack)
305
+ return [];
306
+ const [session, services] = await Promise.all([stableSession(), currentServices]);
307
+ if (!session || !services)
308
+ throw new RefusalError('runtime port health needs current lifecycle, lease and service identities');
309
+ const observation = await observeRuntimePortFamily({ topology: session.readiness.topology,
310
+ services: [...services.values()], namespace: session.context.namespace,
311
+ listenerTimeoutMs: input.timeoutMs,
312
+ revalidate: async () => {
313
+ const fresh = await inspectCurrentServices();
314
+ if (!fresh)
315
+ throw new RefusalError('runtime services or lifecycle changed during port observation');
316
+ return [...fresh.values()];
317
+ } });
318
+ return [observation];
319
+ },
320
+ runtime: async () => {
321
+ if (!configuredRuntime)
322
+ return [];
323
+ if (!runningStack) {
324
+ const measured = await runtimeMeasurement;
325
+ // A stopped stack's missing runtime is already a notice; its rows are left unobserved.
326
+ if (!measured && input.stopped)
327
+ return [];
328
+ if (!measured)
329
+ throw new RefusalError('configured runtime measurement needs authenticated runtime identity evidence');
330
+ return [measured];
331
+ }
332
+ const [session, services] = await Promise.all([stableSession(), currentServices]);
333
+ if (!session || !services)
334
+ throw new RefusalError('configured runtime health needs current runtime execution evidence');
335
+ return [...session.readiness.runtimeObservations,
336
+ observeRuntimeMountMaterialization({ topology: session.readiness.topology,
337
+ services: [...services.values()], observedAt: observedAt() })];
338
+ },
339
+ envSchema: async () => await observeEnvSchemas({
340
+ stackId: input.stack,
341
+ siblings: input.config.result.config.sibling.map((sibling) => ({
342
+ slug: sibling.slug, dir: sibling.dir, root: resolve(siblingRoot, sibling.dir),
343
+ })),
344
+ env: input.env,
345
+ timeoutMs: input.timeoutMs,
346
+ }, { execute: input.execute }),
347
+ state: async () => observeConfiguration({
348
+ path: input.config.result.source.path, stackId: input.stack, observedAt: observedAt(),
349
+ }),
350
+ templates: async () => observeTemplateHealth({
351
+ stackId: input.stack, observedAt: observedAt(), siblingRoot,
352
+ siblings: input.config.result.config.sibling,
353
+ }),
354
+ };
355
+ return { observations, revalidate: async () => !runningStack || await stableSession() !== undefined };
356
+ }
357
+ async function checkoutProjection(input, loaded, git) {
358
+ const observedAt = new Date().toISOString();
359
+ const observed = await observeSetupCheckout({
360
+ source: loaded.result.source,
361
+ cockpitName: loaded.result.config.cockpit.name,
362
+ observedAt,
363
+ env: checkoutObservationEnvironment(input.env),
364
+ gitExecutable: git,
365
+ });
366
+ // The owner's reason (for example, a cockpit root that is not a Git checkout) says what to do; dropping
367
+ // it left preflight with only "observation source is unavailable".
368
+ if (observed.state === 'failed')
369
+ return { failed: { severity: 'error',
370
+ code: observed.code, message: observed.reason, target: loaded.result.source.path } };
371
+ return withSetupCheckoutProjection(observed, {
372
+ source: loaded.result.source,
373
+ cockpitName: loaded.result.config.cockpit.name,
374
+ evaluatedAt: Date.parse(observedAt),
375
+ }, consumeSetupCheckoutProjection);
376
+ }
377
+ // Work orders exist once workorders/ does, and from then on work grade and cockpit grade run the first
378
+ // tool work.evaluators lists that can run, whichever agent tool did the work; doctor names the case
379
+ // where none is on PATH before a grade fails on it, and says how each closed order closed.
380
+ async function inspectWorkEvaluator(root, env, evaluators) {
381
+ try {
382
+ if (!(await lstat(join(root, 'workorders'))).isDirectory())
383
+ return [];
384
+ }
385
+ catch (error) {
386
+ if (hasErrorCode(error, 'ENOENT'))
387
+ return [];
388
+ throw error;
389
+ }
390
+ // The evaluator search looks only in canonical PATH directories, so doctor does too.
391
+ const directories = [];
392
+ for (const directory of (env.PATH ?? '').split(delimiter)) {
393
+ if (!directory || !isAbsolute(directory) || resolve(directory) !== directory)
394
+ continue;
395
+ try {
396
+ if (await realpath(directory) === directory)
397
+ directories.push(directory);
398
+ }
399
+ catch (error) {
400
+ if (hasErrorCode(error, 'ENOENT', 'ENOTDIR', 'EACCES'))
401
+ continue;
402
+ throw error;
403
+ }
404
+ }
405
+ const closed = (await createWorkStore(root).list()).flatMap(record => record.closure ? [{ severity: 'info',
406
+ code: ENVIRONMENT_CODES.WORK_ORDER_CLOSED, target: `workorders/${record.id}.md`,
407
+ message: `${record.id} closed as ${record.closure.how}, signed off by ${record.closure.by}${record.closure.note ? `: ${record.closure.note}` : ''}` }] : []);
408
+ const { found, missing } = await findEvaluators(evaluators, directories);
409
+ if (found.length)
410
+ return closed;
411
+ return [{ severity: 'warning', target: 'workorders',
412
+ ...evaluatorUnavailable(`this cockpit has work orders, but no evaluator work.evaluators lists is on PATH to run work grade and cockpit grade: ${missing.map(row => row.message).join('; ')}`) }, ...closed];
413
+ }
414
+ export function createHealthCommandAdapters(rawInput, execute) {
415
+ const input = captureInput(rawInput);
416
+ let selectedNamespace;
417
+ const load = async (command, issue, refuse, purpose) => {
418
+ const requestedPath = commandConfigPath(command);
419
+ if (!input || typeof execute !== 'function' || typeof issue !== 'function'
420
+ || requestedPath !== input.configPath)
421
+ return undefined;
422
+ const loaded = await loadCockpitConfig(input.configPath);
423
+ // A malformed cockpit.toml is the user's to fix, so the loader's own diagnostics reach them.
424
+ if (loaded.kind === 'refusal')
425
+ return refuse?.(loaded.diagnostics);
426
+ if (loaded.kind !== 'complete')
427
+ return undefined;
428
+ // Health is about a running stack or a machine floor; with neither declared it is not declared,
429
+ // never healthy, and preflight refuses. Doctor still checks what such a cockpit does depend on:
430
+ // its Git working tree, its siblings and its surfaces.
431
+ const notDeclared = declaresNothingToRun(loaded.result.config.environment)
432
+ ? runtimeNotDeclared(loaded.result.source.path) : undefined;
433
+ if (notDeclared && purpose !== 'doctor')
434
+ return refuse?.([notDeclared]);
435
+ const git = await resolveGitExecutable(input.env);
436
+ if (git.state !== 'available')
437
+ return undefined;
438
+ const checkout = await checkoutProjection(input, loaded, git.value);
439
+ if (checkout && 'failed' in checkout)
440
+ return refuse?.([...notDeclared ? [notDeclared] : [], checkout.failed]);
441
+ if (!checkout || process.platform !== 'darwin' && process.platform !== 'linux')
442
+ return undefined;
443
+ const namespace = await selectStateRoot({
444
+ platform: process.platform,
445
+ home: input.env.HOME,
446
+ xdgStateHome: input.env.XDG_STATE_HOME,
447
+ checkoutRoots: [checkout.currentCheckoutPath, checkout.mainCheckoutPath],
448
+ cockpitId: checkout.cockpitId,
449
+ });
450
+ // Without the state root's own refusal, doctor reported only that its observation source
451
+ // was unavailable, which named neither the directory nor why it could not be used.
452
+ if (namespace.kind === 'refusal')
453
+ return refuse?.([{ severity: 'error',
454
+ code: namespace.code, message: namespace.message, target: namespace.target }]);
455
+ if (namespace.kind !== 'complete')
456
+ return undefined;
457
+ selectedNamespace = namespace.value;
458
+ const currentStack = stackId({
459
+ cockpitId: checkout.cockpitId,
460
+ checkoutId: checkout.checkoutId,
461
+ variant: { kind: 'shared' },
462
+ });
463
+ let stopped = notDeclared ?? null;
464
+ const refreshPreflight = async (previous) => {
465
+ const observedAt = new Date().toISOString();
466
+ const configuration = (await observeConfiguration({
467
+ path: loaded.result.source.path,
468
+ stackId: currentStack,
469
+ observedAt,
470
+ })).find(({ code }) => code === ENVIRONMENT_CODES.CONFIG_STALE);
471
+ if (!configuration)
472
+ return undefined;
473
+ try {
474
+ const authorityInput = {
475
+ configuration, cockpitId: checkout.cockpitId,
476
+ checkoutPath: checkout.currentCheckoutPath, variant: { kind: 'shared' },
477
+ };
478
+ const authority = previous === undefined
479
+ ? await resolveObservationPostures(authorityInput)
480
+ : previous.authorities.length === 1
481
+ ? await refreshObservationPostures({ ...authorityInput, previous: previous.authorities[0] })
482
+ : undefined;
483
+ if (!authority)
484
+ return undefined;
485
+ const stage = await resolveLifecycleStage({
486
+ namespace: namespace.value,
487
+ stackId: currentStack,
488
+ observedAt,
489
+ });
490
+ if (stage.kind !== 'complete')
491
+ return refuse?.(stage.diagnostics);
492
+ if (previous === undefined && !notDeclared) {
493
+ if (stage.authority.stage !== 'steady') {
494
+ const fix = cockpitCommand('up', { config: loaded.result.source.path });
495
+ stopped = {
496
+ severity: 'error', code: ENVIRONMENT_CODES.STATE_UNSEEDED, target: loaded.result.source.path,
497
+ message: `lifecycle is still in bootstrap — the stack has never completed a first bring-up, so only the checks that need no running stack ran; fix: ${fix}; then retry the command that stopped`,
498
+ fix,
499
+ };
500
+ }
501
+ else
502
+ stopped = await sleepingStack(namespace.value, currentStack, loaded.result.source.path) ?? null;
503
+ }
504
+ return {
505
+ authorities: [authority],
506
+ stages: [stage.authority],
507
+ };
508
+ }
509
+ catch (error) {
510
+ return refuse?.([{
511
+ severity: 'error', code: ENVIRONMENT_CODES.EVIDENCE_UNAVAILABLE,
512
+ message: `preflight evidence could not be read: ${errorCause(error)}`, target: loaded.result.source.path,
513
+ }]);
514
+ }
515
+ };
516
+ const preflight = await refreshPreflight();
517
+ if (!preflight)
518
+ return undefined;
519
+ const notices = [];
520
+ const sourceProducers = producers({
521
+ checkout,
522
+ config: loaded,
523
+ namespace: namespace.value,
524
+ stack: currentStack,
525
+ git: git.value,
526
+ env: input.env,
527
+ timeoutMs: input.timeoutMs,
528
+ execute,
529
+ ...(refuse ? { refuse: (diagnostics) => refuse(diagnostics) } : {}),
530
+ stopped: stopped !== null,
531
+ notice: (diagnostics) => {
532
+ for (const diagnostic of diagnostics) {
533
+ if (!notices.some(({ code, target }) => code === diagnostic.code && target === diagnostic.target))
534
+ notices.push(diagnostic);
535
+ }
536
+ },
537
+ });
538
+ const secondObservedAt = new Date().toISOString();
539
+ const secondObservation = await observeSetupCheckout({
540
+ source: loaded.result.source,
541
+ cockpitName: loaded.result.config.cockpit.name,
542
+ observedAt: secondObservedAt,
543
+ env: checkoutObservationEnvironment(input.env),
544
+ gitExecutable: git.value,
545
+ });
546
+ return withSetupCheckoutProjection(secondObservation, {
547
+ source: loaded.result.source,
548
+ cockpitName: loaded.result.config.cockpit.name,
549
+ evaluatedAt: Date.parse(secondObservedAt),
550
+ }, (projection) => issue({
551
+ checkout: projection,
552
+ configuration: loaded.result,
553
+ preflight,
554
+ env: gitObservationEnvironment(input.env),
555
+ gitExecutable: git.value,
556
+ timeoutMs: input.timeoutMs,
557
+ producers: sourceProducers.observations,
558
+ refreshPreflight: async (previous) => await sourceProducers.revalidate() ? refreshPreflight(previous) : undefined,
559
+ stopped,
560
+ notices: () => [...notices],
561
+ }));
562
+ };
563
+ const inspectSurfaceOverrides = async (configPath) => {
564
+ if (!input) {
565
+ return {
566
+ kind: 'unavailable',
567
+ code: 'SURFACE_OVERRIDE_INSPECTION_UNAVAILABLE',
568
+ reason: 'surface override inspection needs valid command composition input',
569
+ };
570
+ }
571
+ if (configPath !== input.configPath) {
572
+ return {
573
+ kind: 'refusal',
574
+ code: 'SURFACE_OVERRIDE_INSPECTION_PATH_MISMATCH',
575
+ reason: 'surface override inspection is bound to the configured cockpit source',
576
+ };
577
+ }
578
+ try {
579
+ const outcome = await syncGeneratedEnvironment({ configPath: input.configPath, dryRun: true }, { execute });
580
+ if (outcome.kind !== 'complete') {
581
+ const diagnostic = outcome.diagnostics[0];
582
+ return {
583
+ kind: 'refusal',
584
+ code: diagnostic?.code || 'SURFACE_OVERRIDE_INSPECTION_REFUSED',
585
+ reason: diagnostic?.message || 'generated surface override inspection was refused',
586
+ };
587
+ }
588
+ const application = generatedApplicationProjection(outcome.result.application);
589
+ if (!application
590
+ || application.sourceSha256 !== outcome.result.sourceSha256
591
+ || application.generation !== outcome.result.generation) {
592
+ return {
593
+ kind: 'refusal',
594
+ code: 'SURFACE_OVERRIDE_INSPECTION_INVALID',
595
+ reason: 'generated surface override inspection did not return an authenticated application projection',
596
+ };
597
+ }
598
+ return {
599
+ kind: 'complete',
600
+ activeOverrides: application.activeSurfaceOverrides,
601
+ changedOverrideOriginals: application.changedOverrideOriginals,
602
+ };
603
+ }
604
+ catch (error) {
605
+ return {
606
+ kind: 'unavailable',
607
+ code: 'SURFACE_OVERRIDE_INSPECTION_UNAVAILABLE',
608
+ reason: `authenticated generated surface override inspection is unavailable: ${errorCause(error)}`,
609
+ };
610
+ }
611
+ };
612
+ // Codex skips a project hook nobody on this machine has trusted, so doctor says so.
613
+ const inspectHookTrust = input ? () => inspectCodexHookTrust({ root: dirname(input.configPath), env: input.env })
614
+ : undefined;
615
+ const inspectEvaluator = input ? (evaluators) => inspectWorkEvaluator(dirname(input.configPath), input.env, evaluators) : undefined;
616
+ const inspectReadiness = input ? () => inspectProjectReadiness({ root: dirname(input.configPath), configPath: input.configPath,
617
+ checkRoot: false, env: input.env, timeoutMs: input.timeoutMs }, execute) : undefined;
618
+ // Read after load, which selects this cockpit's state namespace; before that there is no state root to read.
619
+ const readUnreadableState = async () => {
620
+ if (!selectedNamespace)
621
+ return { kind: 'complete', skipped: [] };
622
+ const inventory = await readRootPortInventory(selectedNamespace);
623
+ return inventory.kind === 'complete' ? { kind: 'complete', skipped: inventory.skipped } : inventory;
624
+ };
625
+ return { load, inspectSurfaceOverrides, ...(inspectHookTrust ? { inspectHookTrust } : {}),
626
+ ...(inspectEvaluator ? { inspectEvaluator } : {}), ...(inspectReadiness ? { inspectReadiness } : {}), readUnreadableState,
627
+ execute: (step) => execute(step) };
628
+ }