@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,349 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { lstatSync, mkdirSync, realpathSync } from 'node:fs';
3
+ import { readBoundedFile } from '../bounded-read.js';
4
+ import { isIP } from 'node:net';
5
+ import { dirname, isAbsolute, relative, resolve } from 'node:path';
6
+ import { errorCause, snapshotJson, RefusalError, hasErrorCode } from '../environment/contract.js';
7
+ import {} from '../environment/state.js';
8
+ import { cockpitCommand } from '../fix-catalogue.js';
9
+ import { assertBrowserStorageStateCurrent, browserRuntimeControlAuthorities, browserRuntimeControlPorts } from './runtime.js';
10
+ import { authenticateBrowserProjectClient, executeBrowserProjectOperation, withBrowserProjectOperationAdmission } from './surfaces.js';
11
+ const issuedPolicies = new WeakMap();
12
+ const issuedReceipts = new WeakSet();
13
+ const kinds = new Set(['drive', 'screenshot']);
14
+ const sha256 = /^[a-f0-9]{64}$/;
15
+ const MAX_SCREENSHOT_BYTES = 8_388_608;
16
+ const COMMAND_TIMEOUT_MS = 30_000;
17
+ const browserFailureCategories = Object.freeze({
18
+ connect: 'availability', context: 'setup', navigation: 'navigation',
19
+ screenshot: 'observation', artifact: 'artifact', cleanup: 'cleanup',
20
+ });
21
+ function refusal(reason) {
22
+ return { kind: 'refusal', reason };
23
+ }
24
+ // The reason vocabulary the operation program may report: identifiers only, never page text.
25
+ const FAILURE_REASON = /^(?:ERR_[A-Z0-9_]{1,64}|HTTP_[1-5][0-9]{2}|TIMEOUT)$/;
26
+ const UNANSWERED = new Set(['ERR_CONNECTION_REFUSED', 'ERR_SOCKS_CONNECTION_FAILED', 'ERR_CONNECTION_RESET',
27
+ 'ERR_CONNECTION_CLOSED', 'ERR_EMPTY_RESPONSE', 'ERR_ADDRESS_UNREACHABLE']);
28
+ function parseBrowserFailure(value) {
29
+ if (value.byteLength === 0 || value.byteLength > 1_024)
30
+ return undefined;
31
+ let parsed;
32
+ // The bytes come from the child's stderr and a parse error can quote them; the caller reports
33
+ // the failure from its own fixed text when this returns undefined.
34
+ try {
35
+ parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(value));
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
41
+ return undefined;
42
+ const row = parsed;
43
+ const keys = Object.keys(row).sort().join(',');
44
+ if (keys !== 'category,cleanup,phase,schema' && keys !== 'category,cleanup,phase,reason,schema'
45
+ || row.schema !== 'cockpit-browser-failure/v1' || typeof row.phase !== 'string'
46
+ || !(row.phase in browserFailureCategories) || typeof row.cleanup !== 'boolean'
47
+ || row.reason !== undefined && (typeof row.reason !== 'string' || !FAILURE_REASON.test(row.reason)))
48
+ return undefined;
49
+ const phase = row.phase;
50
+ const category = browserFailureCategories[phase];
51
+ return row.category === category
52
+ ? { phase, category, cleanup: row.cleanup, ...(typeof row.reason === 'string' ? { reason: row.reason } : {}) }
53
+ : undefined;
54
+ }
55
+ // What a person can do about a failed navigation, keyed by the reason the browser reported.
56
+ function failureHint(reason, url, runtime) {
57
+ if (reason === undefined)
58
+ return '';
59
+ if (UNANSWERED.has(reason)) {
60
+ return `: nothing answered at ${url} (${reason}); check that the application is up with ${cockpitCommand('doctor', { config: runtime.configurationPath })}`;
61
+ }
62
+ if (reason === 'ERR_NAME_NOT_RESOLVED')
63
+ return `: ${new URL(url).hostname} did not resolve (${reason}); use the declared .localhost hostname or 127.0.0.1`;
64
+ if (reason.startsWith('ERR_CERT_')) {
65
+ return `: the browser does not trust the certificate at ${url} (${reason}); add the issuing CA to the ${runtime.connection.kind === 'remote' ? 'remote browser\'s image' : 'host trust store'}`;
66
+ }
67
+ if (reason.startsWith('HTTP_'))
68
+ return `: ${url} answered HTTP ${reason.slice(5)}, and a surface must answer 2xx`;
69
+ if (reason === 'TIMEOUT')
70
+ return `: ${url} did not finish loading within the 30-second command deadline`;
71
+ return ` (${reason})`;
72
+ }
73
+ // Mirrors Playwright's exposeNetwork matching: a remote browser reaches an address through the
74
+ // connecting client only when a rule covers it, and goes to its own network for everything else.
75
+ // Chromium hands the tunnel an IPv6 literal without brackets, which no rule matches (its
76
+ // <loopback> names only the bracketed form), so an IPv6 origin is never covered.
77
+ function networkExposureCovers(exposure, origin) {
78
+ const hostname = origin.hostname.toLowerCase();
79
+ if (hostname.startsWith('['))
80
+ return false;
81
+ const port = Number(origin.port || (origin.protocol === 'http:' ? '80' : '443'));
82
+ const ip = isIP(hostname) !== 0;
83
+ return exposure.split(',').some(rule => {
84
+ const match = /^(.*?)(?::(\d+))?$/.exec(rule);
85
+ if (!match || match[2] !== undefined && Number(match[2]) !== port)
86
+ return false;
87
+ const host = match[1].toLowerCase();
88
+ if (host === '<loopback>')
89
+ return ['localhost', '127.0.0.1'].includes(hostname) || hostname.endsWith('.localhost');
90
+ if (host === '*')
91
+ return true;
92
+ if (isIP(host) !== 0)
93
+ return host === hostname;
94
+ if (ip)
95
+ return false;
96
+ const pattern = host.startsWith('.') ? `*${host}` : host;
97
+ return pattern.startsWith('*.') ? hostname.endsWith(pattern.slice(1)) : hostname === pattern;
98
+ });
99
+ }
100
+ export function issueBrowserRoutePolicy(runtime, input) {
101
+ assertBrowserStorageStateCurrent(runtime);
102
+ const captured = snapshotJson(input);
103
+ if (!captured || Object.keys(captured).sort().join(',') !== 'origin,routes') {
104
+ throw new RefusalError('browser route policy requires closed origin and route declarations');
105
+ }
106
+ if (typeof captured.origin !== 'string' || !Array.isArray(captured.routes)
107
+ || captured.routes.length === 0 || captured.routes.length > 64)
108
+ throw new RefusalError('browser route policy requires bounded declared routes');
109
+ let origin;
110
+ try {
111
+ origin = new URL(captured.origin);
112
+ }
113
+ catch {
114
+ throw new RefusalError('browser application origin must be an absolute HTTP(S) origin');
115
+ }
116
+ if (!['http:', 'https:'].includes(origin.protocol) || origin.username || origin.password || origin.pathname !== '/'
117
+ || origin.search || origin.hash || origin.origin !== captured.origin.replace(/\/$/, '')) {
118
+ throw new RefusalError('browser application origin must be an absolute HTTP(S) origin without credentials, path, query or fragment');
119
+ }
120
+ const controlAuthorities = browserRuntimeControlAuthorities(runtime);
121
+ const controlPorts = browserRuntimeControlPorts(runtime);
122
+ const originPort = Number(origin.port || (origin.protocol === 'http:' ? '80' : '443'));
123
+ const originAuthority = `${origin.hostname}:${originPort}`;
124
+ if (!controlAuthorities || !controlPorts || controlAuthorities.includes(originAuthority) || controlPorts.includes(originPort)) {
125
+ throw new RefusalError('browser application origin must be distinct from the browser-control proxy authority');
126
+ }
127
+ const names = new Set();
128
+ const urls = new Set();
129
+ const surfaces = [];
130
+ for (const candidate of captured.routes) {
131
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate))
132
+ throw new RefusalError('browser routes must be declared relative paths');
133
+ const row = candidate;
134
+ if (Object.keys(row).sort().join(',') !== 'name,path')
135
+ throw new RefusalError('browser route policy requires closed route declarations');
136
+ if (typeof row.name !== 'string' || !/^[a-z0-9][a-z0-9_-]{0,63}$/.test(row.name)
137
+ || typeof row.path !== 'string' || !row.path.startsWith('/') || row.path.startsWith('//') || row.path.includes('#')) {
138
+ throw new RefusalError('browser routes must use unique names and declared relative HTTP paths without fragments');
139
+ }
140
+ const url = new URL(row.path, origin);
141
+ if (url.origin !== origin.origin || url.username || url.password || url.hash || names.has(row.name) || urls.has(url.href)) {
142
+ throw new RefusalError('browser routes must remain unique and relative to the application origin');
143
+ }
144
+ names.add(row.name);
145
+ urls.add(url.href);
146
+ surfaces.push({ name: row.name, url: url.href });
147
+ }
148
+ const policy = { origin: origin.origin, surfaces };
149
+ issuedPolicies.set(policy, runtime);
150
+ return policy;
151
+ }
152
+ function authentication(input) {
153
+ const auth = input.runtime.authentication;
154
+ if (auth.posture !== 'real')
155
+ return input.role === undefined && input.state === undefined ? { posture: auth.posture } : undefined;
156
+ if (typeof input.role !== 'string' || auth.policy === 'none')
157
+ return undefined;
158
+ const state = auth.states.find(candidate => candidate.worker === input.role);
159
+ if (!state)
160
+ return undefined;
161
+ return { posture: 'real', role: input.role, policy: auth.policy, statePath: resolve(input.client.cwd, state.path) };
162
+ }
163
+ function publicAuthentication(value) {
164
+ return value.posture !== 'real' ? { posture: value.posture }
165
+ : { posture: 'real', role: value.role, policy: value.policy };
166
+ }
167
+ function parseObservation(value, kind, origin) {
168
+ let parsed;
169
+ // Child stdout can be quoted by a parse error; the caller refuses with its own fixed message.
170
+ try {
171
+ parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(value));
172
+ }
173
+ catch {
174
+ return undefined;
175
+ }
176
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
177
+ return undefined;
178
+ const row = parsed;
179
+ const expected = kind === 'drive' ? ['finalUrl', 'operation', 'schema'] : ['evidence', 'finalUrl', 'operation', 'schema'];
180
+ if (Object.keys(row).sort().join(',') !== expected.sort().join(',') || row.schema !== 'cockpit-browser-observation/v1' || row.operation !== kind || typeof row.finalUrl !== 'string')
181
+ return undefined;
182
+ let final;
183
+ // A URL error quotes the child-reported address; the caller refuses with its own fixed message.
184
+ try {
185
+ final = new URL(row.finalUrl);
186
+ }
187
+ catch {
188
+ return undefined;
189
+ }
190
+ if (final.origin !== origin || final.username || final.password || final.hash)
191
+ return undefined;
192
+ if (kind === 'drive')
193
+ return { finalUrl: final.href };
194
+ if (!row.evidence || typeof row.evidence !== 'object' || Array.isArray(row.evidence))
195
+ return undefined;
196
+ const evidence = row.evidence;
197
+ if (Object.keys(evidence).sort().join(',') !== 'byteLength,sha256' || typeof evidence.sha256 !== 'string' || !sha256.test(evidence.sha256)
198
+ || !Number.isSafeInteger(evidence.byteLength) || evidence.byteLength < 0
199
+ || evidence.byteLength > MAX_SCREENSHOT_BYTES)
200
+ return undefined;
201
+ return { finalUrl: final.href, evidence: { sha256: evidence.sha256, byteLength: evidence.byteLength } };
202
+ }
203
+ // Returns undefined when the file fails a check; a filesystem error is thrown for the caller to name.
204
+ function postcheckScreenshot(root, cockpitRoot, path, expected) {
205
+ const rootStat = lstatSync(root);
206
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || realpathSync(root) !== root)
207
+ return undefined;
208
+ const rel = relative(root, path);
209
+ if (!rel || rel.startsWith('..') || isAbsolute(rel) || resolve(root, rel) !== path || realpathSync(path) !== path)
210
+ return undefined;
211
+ const read = readBoundedFile(path, expected.byteLength);
212
+ const linked = lstatSync(path);
213
+ if (read.kind !== 'complete' || read.bytes.byteLength !== expected.byteLength || linked.nlink !== 1 || (linked.mode & 0o7777) !== 0o600
214
+ || createHash('sha256').update(read.bytes).digest('hex') !== expected.sha256)
215
+ return undefined;
216
+ const receiptPath = relative(cockpitRoot, path);
217
+ return receiptPath && !receiptPath.startsWith('..') && !isAbsolute(receiptPath) ? receiptPath : undefined;
218
+ }
219
+ export function isIssuedBrowserCommandReceipt(value) {
220
+ return value !== null && typeof value === 'object' && issuedReceipts.has(value);
221
+ }
222
+ // The artifact root is declared, not shipped, so the first screenshot meets a missing directory.
223
+ // Create it here, at the one place that writes into it, from the nearest existing ancestor, which
224
+ // must be a real directory: the operation re-authenticates the root's identity before it writes.
225
+ function ensureArtifactRoot(root) {
226
+ let ancestor = root;
227
+ for (;;) {
228
+ try {
229
+ const status = lstatSync(ancestor);
230
+ if (!status.isDirectory() || status.isSymbolicLink() || realpathSync(ancestor) !== ancestor) {
231
+ return `Browser screenshot artifact root ${root} lies behind ${ancestor}, which is not a real directory`;
232
+ }
233
+ break;
234
+ }
235
+ catch (error) {
236
+ if (!hasErrorCode(error, 'ENOENT'))
237
+ return `Browser screenshot artifact root ${root} could not be inspected at ${ancestor}: ${errorCause(error)}`;
238
+ const parent = dirname(ancestor);
239
+ if (parent === ancestor)
240
+ return `Browser screenshot artifact root ${root} has no existing ancestor`;
241
+ ancestor = parent;
242
+ }
243
+ }
244
+ try {
245
+ mkdirSync(root, { recursive: true, mode: 0o700 });
246
+ }
247
+ catch (error) {
248
+ return `Browser screenshot artifact root ${root} could not be created: ${errorCause(error)}`;
249
+ }
250
+ return undefined;
251
+ }
252
+ async function run(input, client, policy, auth, deadline, authorization) {
253
+ const receipts = [];
254
+ if (input.kind === 'screenshot') {
255
+ const unavailable = ensureArtifactRoot(client.artifactRoot);
256
+ if (unavailable)
257
+ return refusal(unavailable);
258
+ }
259
+ for (const surface of policy.surfaces) {
260
+ // Only the runtime's own fixed-text TypeError escapes the revalidation (it replaces filesystem
261
+ // errors with that text), so the refusal names the condition instead.
262
+ try {
263
+ assertBrowserStorageStateCurrent(input.runtime);
264
+ }
265
+ catch {
266
+ return refusal('Browser seeded state changed before application driving');
267
+ }
268
+ const output = input.kind === 'screenshot' ? resolve(client.artifactRoot, `screenshot-${randomUUID()}.png`) : undefined;
269
+ if (deadline <= Date.now())
270
+ return refusal('Browser command exceeded its total 30-second deadline');
271
+ const authenticated = await executeBrowserProjectOperation(client, input.runtime, {
272
+ kind: input.kind, surface, deadline, ...(auth.posture === 'real' ? { role: auth.role } : {}), ...(output ? { output } : {}),
273
+ }, authorization);
274
+ if (!authenticated || authenticated.kind === 'unadmitted') {
275
+ return refusal(`Browser ${input.kind} operation was not admitted to the bounded executor for ${surface.name}${authenticated ? ` (${authenticated.cause})` : ''}`);
276
+ }
277
+ const failure = authenticated.execution.exitCode === 0 ? undefined : parseBrowserFailure(authenticated.stderr);
278
+ if (failure) {
279
+ return refusal(`Browser ${input.kind} operation failed during ${failure.phase} (${failure.category})${failure.cleanup && failure.phase !== 'cleanup'
280
+ ? ' and cleanup also failed' : ''} for ${surface.name}${failureHint(failure.reason, surface.url, input.runtime)}`);
281
+ }
282
+ if (authenticated.kind !== 'complete') {
283
+ return refusal(`Browser ${input.kind} operation was refused by the bounded executor for ${surface.name}`);
284
+ }
285
+ if (authenticated.execution.exitCode !== 0) {
286
+ return refusal(`Browser ${input.kind} operation failed without a valid bounded diagnostic for ${surface.name}`);
287
+ }
288
+ const observation = parseObservation(authenticated.stdout, input.kind, policy.origin);
289
+ if (!observation)
290
+ return refusal(`Browser ${input.kind} returned no bound application-driving observation for ${surface.name}`);
291
+ try {
292
+ assertBrowserStorageStateCurrent(input.runtime);
293
+ }
294
+ catch {
295
+ return refusal('Browser seeded state changed while the application was being driven');
296
+ }
297
+ const base = { name: surface.name, url: observation.finalUrl };
298
+ if (input.kind === 'screenshot') {
299
+ let path;
300
+ let cause = '';
301
+ try {
302
+ path = postcheckScreenshot(client.artifactRoot, client.cwd, output, observation.evidence);
303
+ }
304
+ catch (error) {
305
+ cause = ` (${errorCause(error)})`;
306
+ }
307
+ if (!path)
308
+ return refusal(`Browser screenshot output failed its contained mode-600 postcheck for ${surface.name}${cause}`);
309
+ receipts.push({ ...base, screenshot: { path, ...observation.evidence } });
310
+ }
311
+ else
312
+ receipts.push(base);
313
+ }
314
+ const receipt = { kind: input.kind, activation: 'application',
315
+ authentication: publicAuthentication(auth), surfaces: receipts };
316
+ issuedReceipts.add(receipt);
317
+ return { kind: 'complete', receipt };
318
+ }
319
+ export async function executeBrowserCommand(value) {
320
+ const deadline = Date.now() + COMMAND_TIMEOUT_MS;
321
+ const input = value;
322
+ if (!kinds.has(input.kind))
323
+ return refusal('Browser command kind is unsupported');
324
+ let client;
325
+ let clientCause = '';
326
+ try {
327
+ client = authenticateBrowserProjectClient(input.client, input.runtime);
328
+ }
329
+ catch (error) {
330
+ clientCause = ` (${errorCause(error)})`;
331
+ }
332
+ if (!client)
333
+ return refusal(`Browser command requires an issued project module client${clientCause}`);
334
+ if (issuedPolicies.get(input.policy) !== input.runtime)
335
+ return refusal('Browser command requires an issued route policy for the current runtime');
336
+ if (input.runtime.connection.kind === 'remote' && !networkExposureCovers(input.runtime.networkExposure, new URL(input.policy.origin))) {
337
+ return refusal(`Browser application origin ${input.policy.origin} is outside browser.network_exposure (${input.runtime.networkExposure}): `
338
+ + 'a remote browser reaches host addresses only through that exposure, so it would look for the application inside its own container. '
339
+ + 'Use a 127.0.0.1, localhost or .localhost origin (an IPv6 literal is never tunnelled), or widen network_exposure with a network_exposure_reason');
340
+ }
341
+ const auth = authentication(input);
342
+ if (!auth)
343
+ return refusal(input.runtime.authentication.posture === 'real'
344
+ ? 'Browser command requires a declared seeded role and matching coordination authority' : 'Bypassed browser authentication cannot accept role or state input');
345
+ const admitted = await withBrowserProjectOperationAdmission(client, input.runtime, {
346
+ origin: input.policy.origin, ...(auth.posture === 'real' ? { role: auth.role } : {}), ...(input.state ? { state: input.state } : {}),
347
+ }, authorization => run(input, client, input.policy, auth, deadline, authorization));
348
+ return admitted.kind === 'admitted' ? admitted.value : refusal(admitted.reason);
349
+ }
@@ -0,0 +1 @@
1
+ export declare function isPackageDescendant(root: string, candidate: string): boolean;
@@ -0,0 +1,6 @@
1
+ import { isAbsolute, relative, sep } from 'node:path';
2
+ // Lexical confinement complements the caller's canonical filesystem and byte-identity checks.
3
+ export function isPackageDescendant(root, candidate) {
4
+ const offset = relative(root, candidate);
5
+ return candidate.startsWith(`${root}${sep}`) && offset.length > 0 && offset !== '..' && !offset.startsWith(`..${sep}`) && !isAbsolute(offset);
6
+ }
@@ -0,0 +1,22 @@
1
+ export interface BrowserProxyInput {
2
+ readonly serverPort: number;
3
+ readonly proxyPort: number;
4
+ readonly allowedHosts: readonly string[];
5
+ readonly bind?: '127.0.0.1' | '::1';
6
+ }
7
+ export declare function browserProxyAuthorityMatches(value: string, expectedPort: number): boolean;
8
+ export declare function resolveBrowserProxy(input: BrowserProxyInput): {
9
+ server: {
10
+ bind: '127.0.0.1';
11
+ port: number;
12
+ publish: false;
13
+ };
14
+ proxy: {
15
+ bind: "127.0.0.1" | "::1";
16
+ port: number;
17
+ allowedHosts: any[];
18
+ checkHost: true;
19
+ };
20
+ };
21
+ export type BrowserProxyProjection = ReturnType<typeof resolveBrowserProxy>;
22
+ export declare function browserHostAllowed(policy: BrowserProxyProjection, host: unknown): boolean;
@@ -0,0 +1,37 @@
1
+ import { RefusalError } from '../environment/contract.js';
2
+ function port(value) {
3
+ return Number.isSafeInteger(value) && value > 0 && value <= 65535;
4
+ }
5
+ export function browserProxyAuthorityMatches(value, expectedPort) {
6
+ if (!port(expectedPort) || typeof value !== 'string' || value.length > 260 || !/^[a-z0-9.[\]:-]+$/.test(value))
7
+ return false;
8
+ try {
9
+ const parsed = new URL(`http://${value}`);
10
+ return value === `${parsed.hostname}:${expectedPort}`
11
+ && parsed.hostname !== 'undefined' && parsed.hostname !== 'null';
12
+ }
13
+ catch {
14
+ // A URL parse error quotes the authority; a boolean answer is the whole contract.
15
+ return false;
16
+ }
17
+ }
18
+ export function resolveBrowserProxy(input) {
19
+ if (!port(input.serverPort) || !port(input.proxyPort) || input.serverPort === input.proxyPort) {
20
+ throw new RefusalError('browser proxy needs distinct valid server and proxy ports');
21
+ }
22
+ const bind = input.bind ?? '127.0.0.1';
23
+ if (bind !== '127.0.0.1' && bind !== '::1')
24
+ throw new RefusalError('browser proxy control publication must remain on loopback until authenticated transport is available');
25
+ if (!Array.isArray(input.allowedHosts) || input.allowedHosts.length === 0 || input.allowedHosts.length > 64
26
+ || new Set(input.allowedHosts).size !== input.allowedHosts.length
27
+ || !input.allowedHosts.every((host) => browserProxyAuthorityMatches(host, input.proxyPort))) {
28
+ throw new RefusalError('browser proxy Host allowlist must contain exact hostname:port authorities');
29
+ }
30
+ return {
31
+ server: { bind: '127.0.0.1', port: input.serverPort, publish: false },
32
+ proxy: { bind, port: input.proxyPort, allowedHosts: [...input.allowedHosts], checkHost: true },
33
+ };
34
+ }
35
+ export function browserHostAllowed(policy, host) {
36
+ return typeof host === 'string' && browserProxyAuthorityMatches(host, policy.proxy.port) && policy.proxy.allowedHosts.includes(host);
37
+ }
@@ -0,0 +1,117 @@
1
+ import { type PreflightContext } from '../environment/posture-observation.js';
2
+ import { type StateNamespace } from '../environment/state.js';
3
+ import { type BrowserProxyInput } from './proxy.js';
4
+ interface BrowserImage {
5
+ readonly reference: string;
6
+ readonly driverVersion: string;
7
+ }
8
+ interface BrowserStorageState {
9
+ readonly worker: string;
10
+ readonly path: string;
11
+ }
12
+ export interface BrowserRuntimeInput {
13
+ readonly postures: PreflightContext;
14
+ readonly images: Readonly<{
15
+ mcp: BrowserImage;
16
+ }>;
17
+ readonly project: Readonly<{
18
+ lockfile: string;
19
+ sha256: string;
20
+ driverVersion: string;
21
+ manifest?: string;
22
+ manifestSha256?: string;
23
+ }>;
24
+ readonly engine?: 'chromium' | 'firefox' | 'webkit';
25
+ readonly hostVariable?: string;
26
+ readonly networkExposure?: string;
27
+ readonly networkExposureReason?: string;
28
+ readonly endpoint?: string;
29
+ readonly maxClients: number;
30
+ readonly proxy: BrowserProxyInput;
31
+ readonly resolver: readonly Readonly<{
32
+ hostname: string;
33
+ address: string;
34
+ }>[];
35
+ readonly authentication: Readonly<{
36
+ policy: 'none' | 'serialized' | 'per-worker';
37
+ states: readonly BrowserStorageState[];
38
+ }>;
39
+ readonly data: Readonly<{
40
+ mode: 'empty' | 'seeded' | 'restored';
41
+ isolated: boolean;
42
+ reachesProduction: boolean;
43
+ }>;
44
+ readonly requestPreapproval: boolean;
45
+ readonly artifacts: Readonly<Record<'mcp' | 'cli' | 'auth', string>>;
46
+ }
47
+ export declare function resolveBrowserRuntime(input: BrowserRuntimeInput, coordinationNamespace?: StateNamespace): {
48
+ configurationPath: string;
49
+ connection: Readonly<{
50
+ kind: 'remote';
51
+ endpoint: string;
52
+ launchLocal: false;
53
+ }> | Readonly<{
54
+ kind: 'local-fallback';
55
+ reason: 'endpoint-absent';
56
+ launchLocal: true;
57
+ }>;
58
+ project: {
59
+ lockfile: string;
60
+ sha256: string;
61
+ driverVersion: string;
62
+ manifest?: string | undefined;
63
+ manifestSha256?: string | undefined;
64
+ };
65
+ mcp: {
66
+ image: string;
67
+ endpoint?: string | undefined;
68
+ };
69
+ server: {
70
+ bind: '127.0.0.1';
71
+ port: number;
72
+ publish: false;
73
+ maxClients: number;
74
+ engine: "chromium" | "firefox" | "webkit";
75
+ headless: true;
76
+ };
77
+ proxy: {
78
+ bind: "127.0.0.1" | "::1";
79
+ port: number;
80
+ allowedHosts: any[];
81
+ checkHost: true;
82
+ };
83
+ addressing: {
84
+ posture: "hostname" | "port" | null;
85
+ resolverRules: string[] | null;
86
+ };
87
+ requirements: readonly Readonly<{
88
+ requirement: string;
89
+ addressing?: import("../schema.js").AddressingPosture;
90
+ authentication?: import("../schema.js").AuthPosture;
91
+ }>[];
92
+ authentication: {
93
+ posture: "bypassed" | "none";
94
+ policy: 'none';
95
+ states: never[];
96
+ claim?: undefined;
97
+ } | {
98
+ posture: 'real';
99
+ policy: "none" | "per-worker" | "serialized";
100
+ states: any[];
101
+ claim: "exclusive" | "per-worker";
102
+ };
103
+ preapproval: "eligible" | "not-requested";
104
+ networkExposure: string;
105
+ networkExposureReason?: string | undefined;
106
+ generatedHostPaths: string[];
107
+ cliArtifactRoot: string;
108
+ };
109
+ export type BrowserRuntimeProjection = ReturnType<typeof resolveBrowserRuntime>;
110
+ export declare function browserRuntimeControlAuthorities(runtime: BrowserRuntimeProjection): readonly string[] | undefined;
111
+ export declare function browserRuntimeControlPorts(runtime: BrowserRuntimeProjection): readonly number[] | undefined;
112
+ export declare function browserRuntimeCoordination(runtime: BrowserRuntimeProjection): Readonly<{
113
+ namespace: StateNamespace;
114
+ stackId: import('../environment/contract.js').StackId;
115
+ }> | undefined;
116
+ export declare function assertBrowserStorageStateCurrent(runtime: BrowserRuntimeProjection): void;
117
+ export {};