@cuna_labs/cli 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 (624) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +9 -0
  3. package/README.md +169 -0
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/dist/api/capability-evidence.d.ts +19 -0
  6. package/dist/api/capability-evidence.d.ts.map +1 -0
  7. package/dist/api/capability-evidence.js +38 -0
  8. package/dist/api/capability-evidence.js.map +1 -0
  9. package/dist/api/client.d.ts +167 -0
  10. package/dist/api/client.d.ts.map +1 -0
  11. package/dist/api/client.js +937 -0
  12. package/dist/api/client.js.map +1 -0
  13. package/dist/api/contracts.d.ts +345 -0
  14. package/dist/api/contracts.d.ts.map +1 -0
  15. package/dist/api/contracts.js +976 -0
  16. package/dist/api/contracts.js.map +1 -0
  17. package/dist/api/execution-workspaces.d.ts +17 -0
  18. package/dist/api/execution-workspaces.d.ts.map +1 -0
  19. package/dist/api/execution-workspaces.js +46 -0
  20. package/dist/api/execution-workspaces.js.map +1 -0
  21. package/dist/api/http.d.ts +76 -0
  22. package/dist/api/http.d.ts.map +1 -0
  23. package/dist/api/http.js +838 -0
  24. package/dist/api/http.js.map +1 -0
  25. package/dist/api/managed-executions.d.ts +37 -0
  26. package/dist/api/managed-executions.d.ts.map +1 -0
  27. package/dist/api/managed-executions.js +80 -0
  28. package/dist/api/managed-executions.js.map +1 -0
  29. package/dist/api/observer-v2-schema.d.ts +206 -0
  30. package/dist/api/observer-v2-schema.d.ts.map +1 -0
  31. package/dist/api/observer-v2-schema.js +264 -0
  32. package/dist/api/observer-v2-schema.js.map +1 -0
  33. package/dist/api/observer-v2.d.ts +33 -0
  34. package/dist/api/observer-v2.d.ts.map +1 -0
  35. package/dist/api/observer-v2.js +60 -0
  36. package/dist/api/observer-v2.js.map +1 -0
  37. package/dist/api/owner-observe-grants-v2-schema.d.ts +678 -0
  38. package/dist/api/owner-observe-grants-v2-schema.d.ts.map +1 -0
  39. package/dist/api/owner-observe-grants-v2-schema.js +887 -0
  40. package/dist/api/owner-observe-grants-v2-schema.js.map +1 -0
  41. package/dist/api/owner-observe-grants-v2.d.ts +268 -0
  42. package/dist/api/owner-observe-grants-v2.d.ts.map +1 -0
  43. package/dist/api/owner-observe-grants-v2.js +604 -0
  44. package/dist/api/owner-observe-grants-v2.js.map +1 -0
  45. package/dist/api/provider-v2-schema.d.ts +293 -0
  46. package/dist/api/provider-v2-schema.d.ts.map +1 -0
  47. package/dist/api/provider-v2-schema.js +381 -0
  48. package/dist/api/provider-v2-schema.js.map +1 -0
  49. package/dist/api/provider-v2.d.ts +55 -0
  50. package/dist/api/provider-v2.d.ts.map +1 -0
  51. package/dist/api/provider-v2.js +54 -0
  52. package/dist/api/provider-v2.js.map +1 -0
  53. package/dist/api/remote-workspace.d.ts +29 -0
  54. package/dist/api/remote-workspace.d.ts.map +1 -0
  55. package/dist/api/remote-workspace.js +62 -0
  56. package/dist/api/remote-workspace.js.map +1 -0
  57. package/dist/api/supervisor-live-update.d.ts +248 -0
  58. package/dist/api/supervisor-live-update.d.ts.map +1 -0
  59. package/dist/api/supervisor-live-update.js +434 -0
  60. package/dist/api/supervisor-live-update.js.map +1 -0
  61. package/dist/api/terminal-reason.d.ts +5 -0
  62. package/dist/api/terminal-reason.d.ts.map +1 -0
  63. package/dist/api/terminal-reason.js +12 -0
  64. package/dist/api/terminal-reason.js.map +1 -0
  65. package/dist/auth/browser-handoff.d.ts +59 -0
  66. package/dist/auth/browser-handoff.d.ts.map +1 -0
  67. package/dist/auth/browser-handoff.js +33 -0
  68. package/dist/auth/browser-handoff.js.map +1 -0
  69. package/dist/auth/browser.d.ts +11 -0
  70. package/dist/auth/browser.d.ts.map +1 -0
  71. package/dist/auth/browser.js +47 -0
  72. package/dist/auth/browser.js.map +1 -0
  73. package/dist/auth/human-client.d.ts +39 -0
  74. package/dist/auth/human-client.d.ts.map +1 -0
  75. package/dist/auth/human-client.js +66 -0
  76. package/dist/auth/human-client.js.map +1 -0
  77. package/dist/auth/human-contracts.d.ts +52 -0
  78. package/dist/auth/human-contracts.d.ts.map +1 -0
  79. package/dist/auth/human-contracts.js +229 -0
  80. package/dist/auth/human-contracts.js.map +1 -0
  81. package/dist/auth/human-session.d.ts +48 -0
  82. package/dist/auth/human-session.d.ts.map +1 -0
  83. package/dist/auth/human-session.js +902 -0
  84. package/dist/auth/human-session.js.map +1 -0
  85. package/dist/auth/onboarding-state.d.ts +31 -0
  86. package/dist/auth/onboarding-state.d.ts.map +1 -0
  87. package/dist/auth/onboarding-state.js +52 -0
  88. package/dist/auth/onboarding-state.js.map +1 -0
  89. package/dist/auth/pkce.d.ts +15 -0
  90. package/dist/auth/pkce.d.ts.map +1 -0
  91. package/dist/auth/pkce.js +19 -0
  92. package/dist/auth/pkce.js.map +1 -0
  93. package/dist/bin/cuna.d.ts +3 -0
  94. package/dist/bin/cuna.d.ts.map +1 -0
  95. package/dist/bin/cuna.js +4 -0
  96. package/dist/bin/cuna.js.map +1 -0
  97. package/dist/build-identity.d.ts +20 -0
  98. package/dist/build-identity.d.ts.map +1 -0
  99. package/dist/build-identity.js +77 -0
  100. package/dist/build-identity.js.map +1 -0
  101. package/dist/cli/command-help.d.ts +9 -0
  102. package/dist/cli/command-help.d.ts.map +1 -0
  103. package/dist/cli/command-help.js +324 -0
  104. package/dist/cli/command-help.js.map +1 -0
  105. package/dist/cli/help.d.ts +40 -0
  106. package/dist/cli/help.d.ts.map +1 -0
  107. package/dist/cli/help.js +256 -0
  108. package/dist/cli/help.js.map +1 -0
  109. package/dist/cli/output.d.ts +28 -0
  110. package/dist/cli/output.d.ts.map +1 -0
  111. package/dist/cli/output.js +95 -0
  112. package/dist/cli/output.js.map +1 -0
  113. package/dist/cli/parser.d.ts +65 -0
  114. package/dist/cli/parser.d.ts.map +1 -0
  115. package/dist/cli/parser.js +294 -0
  116. package/dist/cli/parser.js.map +1 -0
  117. package/dist/cli/process-entrypoint.d.ts +18 -0
  118. package/dist/cli/process-entrypoint.d.ts.map +1 -0
  119. package/dist/cli/process-entrypoint.js +26 -0
  120. package/dist/cli/process-entrypoint.js.map +1 -0
  121. package/dist/cli/run.d.ts +114 -0
  122. package/dist/cli/run.d.ts.map +1 -0
  123. package/dist/cli/run.js +1969 -0
  124. package/dist/cli/run.js.map +1 -0
  125. package/dist/commands/commands.d.ts +74 -0
  126. package/dist/commands/commands.d.ts.map +1 -0
  127. package/dist/commands/commands.js +2814 -0
  128. package/dist/commands/commands.js.map +1 -0
  129. package/dist/config/config.d.ts +86 -0
  130. package/dist/config/config.d.ts.map +1 -0
  131. package/dist/config/config.js +323 -0
  132. package/dist/config/config.js.map +1 -0
  133. package/dist/config/infra-contract-witness.d.ts +29 -0
  134. package/dist/config/infra-contract-witness.d.ts.map +1 -0
  135. package/dist/config/infra-contract-witness.js +30 -0
  136. package/dist/config/infra-contract-witness.js.map +1 -0
  137. package/dist/core/browser-url.d.ts +2 -0
  138. package/dist/core/browser-url.d.ts.map +1 -0
  139. package/dist/core/browser-url.js +20 -0
  140. package/dist/core/browser-url.js.map +1 -0
  141. package/dist/core/deployed-wire-compatibility.d.ts +15 -0
  142. package/dist/core/deployed-wire-compatibility.d.ts.map +1 -0
  143. package/dist/core/deployed-wire-compatibility.js +15 -0
  144. package/dist/core/deployed-wire-compatibility.js.map +1 -0
  145. package/dist/core/errors.d.ts +59 -0
  146. package/dist/core/errors.d.ts.map +1 -0
  147. package/dist/core/errors.js +82 -0
  148. package/dist/core/errors.js.map +1 -0
  149. package/dist/core/exit-codes.d.ts +68 -0
  150. package/dist/core/exit-codes.d.ts.map +1 -0
  151. package/dist/core/exit-codes.js +84 -0
  152. package/dist/core/exit-codes.js.map +1 -0
  153. package/dist/core/instant.d.ts +60 -0
  154. package/dist/core/instant.d.ts.map +1 -0
  155. package/dist/core/instant.js +90 -0
  156. package/dist/core/instant.js.map +1 -0
  157. package/dist/core/namespace.d.ts +171 -0
  158. package/dist/core/namespace.d.ts.map +1 -0
  159. package/dist/core/namespace.js +227 -0
  160. package/dist/core/namespace.js.map +1 -0
  161. package/dist/core/observation-budget.d.ts +185 -0
  162. package/dist/core/observation-budget.d.ts.map +1 -0
  163. package/dist/core/observation-budget.js +209 -0
  164. package/dist/core/observation-budget.js.map +1 -0
  165. package/dist/core/product-web.d.ts +81 -0
  166. package/dist/core/product-web.d.ts.map +1 -0
  167. package/dist/core/product-web.js +87 -0
  168. package/dist/core/product-web.js.map +1 -0
  169. package/dist/core/validation.d.ts +90 -0
  170. package/dist/core/validation.d.ts.map +1 -0
  171. package/dist/core/validation.js +199 -0
  172. package/dist/core/validation.js.map +1 -0
  173. package/dist/credentials/contracts.d.ts +82 -0
  174. package/dist/credentials/contracts.d.ts.map +1 -0
  175. package/dist/credentials/contracts.js +2 -0
  176. package/dist/credentials/contracts.js.map +1 -0
  177. package/dist/credentials/errors.d.ts +19 -0
  178. package/dist/credentials/errors.d.ts.map +1 -0
  179. package/dist/credentials/errors.js +16 -0
  180. package/dist/credentials/errors.js.map +1 -0
  181. package/dist/credentials/index.d.ts +6 -0
  182. package/dist/credentials/index.d.ts.map +1 -0
  183. package/dist/credentials/index.js +6 -0
  184. package/dist/credentials/index.js.map +1 -0
  185. package/dist/credentials/local-session.d.ts +101 -0
  186. package/dist/credentials/local-session.d.ts.map +1 -0
  187. package/dist/credentials/local-session.js +839 -0
  188. package/dist/credentials/local-session.js.map +1 -0
  189. package/dist/credentials/secret-material.d.ts +17 -0
  190. package/dist/credentials/secret-material.d.ts.map +1 -0
  191. package/dist/credentials/secret-material.js +49 -0
  192. package/dist/credentials/secret-material.js.map +1 -0
  193. package/dist/credentials/vault.d.ts +41 -0
  194. package/dist/credentials/vault.d.ts.map +1 -0
  195. package/dist/credentials/vault.js +719 -0
  196. package/dist/credentials/vault.js.map +1 -0
  197. package/dist/daemon/endpoint.d.ts +29 -0
  198. package/dist/daemon/endpoint.d.ts.map +1 -0
  199. package/dist/daemon/endpoint.js +66 -0
  200. package/dist/daemon/endpoint.js.map +1 -0
  201. package/dist/daemon/journal.d.ts +62 -0
  202. package/dist/daemon/journal.d.ts.map +1 -0
  203. package/dist/daemon/journal.js +299 -0
  204. package/dist/daemon/journal.js.map +1 -0
  205. package/dist/daemon/lease.d.ts +25 -0
  206. package/dist/daemon/lease.d.ts.map +1 -0
  207. package/dist/daemon/lease.js +94 -0
  208. package/dist/daemon/lease.js.map +1 -0
  209. package/dist/daemon/lifecycle.d.ts +14 -0
  210. package/dist/daemon/lifecycle.d.ts.map +1 -0
  211. package/dist/daemon/lifecycle.js +29 -0
  212. package/dist/daemon/lifecycle.js.map +1 -0
  213. package/dist/daemon/protocol.d.ts +65 -0
  214. package/dist/daemon/protocol.d.ts.map +1 -0
  215. package/dist/daemon/protocol.js +194 -0
  216. package/dist/daemon/protocol.js.map +1 -0
  217. package/dist/daemon/supervisor.d.ts +29 -0
  218. package/dist/daemon/supervisor.d.ts.map +1 -0
  219. package/dist/daemon/supervisor.js +87 -0
  220. package/dist/daemon/supervisor.js.map +1 -0
  221. package/dist/daemon/views.d.ts +31 -0
  222. package/dist/daemon/views.d.ts.map +1 -0
  223. package/dist/daemon/views.js +86 -0
  224. package/dist/daemon/views.js.map +1 -0
  225. package/dist/index.d.ts +38 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +38 -0
  228. package/dist/index.js.map +1 -0
  229. package/dist/journey/api-effects.d.ts +59 -0
  230. package/dist/journey/api-effects.d.ts.map +1 -0
  231. package/dist/journey/api-effects.js +386 -0
  232. package/dist/journey/api-effects.js.map +1 -0
  233. package/dist/journey/derived-identity.d.ts +91 -0
  234. package/dist/journey/derived-identity.d.ts.map +1 -0
  235. package/dist/journey/derived-identity.js +123 -0
  236. package/dist/journey/derived-identity.js.map +1 -0
  237. package/dist/journey/index.d.ts +6 -0
  238. package/dist/journey/index.d.ts.map +1 -0
  239. package/dist/journey/index.js +6 -0
  240. package/dist/journey/index.js.map +1 -0
  241. package/dist/journey/intent.d.ts +36 -0
  242. package/dist/journey/intent.d.ts.map +1 -0
  243. package/dist/journey/intent.js +191 -0
  244. package/dist/journey/intent.js.map +1 -0
  245. package/dist/journey/orchestrator.d.ts +140 -0
  246. package/dist/journey/orchestrator.d.ts.map +1 -0
  247. package/dist/journey/orchestrator.js +415 -0
  248. package/dist/journey/orchestrator.js.map +1 -0
  249. package/dist/journey/provider-launch-intent.d.ts +12 -0
  250. package/dist/journey/provider-launch-intent.d.ts.map +1 -0
  251. package/dist/journey/provider-launch-intent.js +40 -0
  252. package/dist/journey/provider-launch-intent.js.map +1 -0
  253. package/dist/journey/remote-workspace.d.ts +34 -0
  254. package/dist/journey/remote-workspace.d.ts.map +1 -0
  255. package/dist/journey/remote-workspace.js +91 -0
  256. package/dist/journey/remote-workspace.js.map +1 -0
  257. package/dist/journey/root-entry.d.ts +56 -0
  258. package/dist/journey/root-entry.d.ts.map +1 -0
  259. package/dist/journey/root-entry.js +19 -0
  260. package/dist/journey/root-entry.js.map +1 -0
  261. package/dist/journey/selection.d.ts +165 -0
  262. package/dist/journey/selection.d.ts.map +1 -0
  263. package/dist/journey/selection.js +565 -0
  264. package/dist/journey/selection.js.map +1 -0
  265. package/dist/journey/session-failure.d.ts +4 -0
  266. package/dist/journey/session-failure.d.ts.map +1 -0
  267. package/dist/journey/session-failure.js +28 -0
  268. package/dist/journey/session-failure.js.map +1 -0
  269. package/dist/journey/workspace-effects.d.ts +30 -0
  270. package/dist/journey/workspace-effects.d.ts.map +1 -0
  271. package/dist/journey/workspace-effects.js +348 -0
  272. package/dist/journey/workspace-effects.js.map +1 -0
  273. package/dist/local-actions/broker.d.ts +32 -0
  274. package/dist/local-actions/broker.d.ts.map +1 -0
  275. package/dist/local-actions/broker.js +400 -0
  276. package/dist/local-actions/broker.js.map +1 -0
  277. package/dist/local-actions/browser-action.d.ts +78 -0
  278. package/dist/local-actions/browser-action.d.ts.map +1 -0
  279. package/dist/local-actions/browser-action.js +307 -0
  280. package/dist/local-actions/browser-action.js.map +1 -0
  281. package/dist/local-actions/callback-relay.d.ts +45 -0
  282. package/dist/local-actions/callback-relay.d.ts.map +1 -0
  283. package/dist/local-actions/callback-relay.js +265 -0
  284. package/dist/local-actions/callback-relay.js.map +1 -0
  285. package/dist/local-actions/clipboard.d.ts +3 -0
  286. package/dist/local-actions/clipboard.d.ts.map +1 -0
  287. package/dist/local-actions/clipboard.js +18 -0
  288. package/dist/local-actions/clipboard.js.map +1 -0
  289. package/dist/local-actions/contracts.d.ts +63 -0
  290. package/dist/local-actions/contracts.d.ts.map +1 -0
  291. package/dist/local-actions/contracts.js +18 -0
  292. package/dist/local-actions/contracts.js.map +1 -0
  293. package/dist/local-actions/desktop-presentation.d.ts +141 -0
  294. package/dist/local-actions/desktop-presentation.d.ts.map +1 -0
  295. package/dist/local-actions/desktop-presentation.js +253 -0
  296. package/dist/local-actions/desktop-presentation.js.map +1 -0
  297. package/dist/local-actions/device-selection.d.ts +43 -0
  298. package/dist/local-actions/device-selection.d.ts.map +1 -0
  299. package/dist/local-actions/device-selection.js +154 -0
  300. package/dist/local-actions/device-selection.js.map +1 -0
  301. package/dist/local-actions/executor.d.ts +49 -0
  302. package/dist/local-actions/executor.d.ts.map +1 -0
  303. package/dist/local-actions/executor.js +329 -0
  304. package/dist/local-actions/executor.js.map +1 -0
  305. package/dist/local-actions/file-transfers.d.ts +141 -0
  306. package/dist/local-actions/file-transfers.d.ts.map +1 -0
  307. package/dist/local-actions/file-transfers.js +446 -0
  308. package/dist/local-actions/file-transfers.js.map +1 -0
  309. package/dist/local-actions/foreground-notifications.d.ts +45 -0
  310. package/dist/local-actions/foreground-notifications.d.ts.map +1 -0
  311. package/dist/local-actions/foreground-notifications.js +163 -0
  312. package/dist/local-actions/foreground-notifications.js.map +1 -0
  313. package/dist/local-actions/git-signing.d.ts +39 -0
  314. package/dist/local-actions/git-signing.d.ts.map +1 -0
  315. package/dist/local-actions/git-signing.js +135 -0
  316. package/dist/local-actions/git-signing.js.map +1 -0
  317. package/dist/local-actions/index.d.ts +19 -0
  318. package/dist/local-actions/index.d.ts.map +1 -0
  319. package/dist/local-actions/index.js +19 -0
  320. package/dist/local-actions/index.js.map +1 -0
  321. package/dist/local-actions/local-service-actions.d.ts +87 -0
  322. package/dist/local-actions/local-service-actions.d.ts.map +1 -0
  323. package/dist/local-actions/local-service-actions.js +347 -0
  324. package/dist/local-actions/local-service-actions.js.map +1 -0
  325. package/dist/local-actions/loopback.d.ts +5 -0
  326. package/dist/local-actions/loopback.d.ts.map +1 -0
  327. package/dist/local-actions/loopback.js +15 -0
  328. package/dist/local-actions/loopback.js.map +1 -0
  329. package/dist/local-actions/policy-store.d.ts +7 -0
  330. package/dist/local-actions/policy-store.d.ts.map +1 -0
  331. package/dist/local-actions/policy-store.js +70 -0
  332. package/dist/local-actions/policy-store.js.map +1 -0
  333. package/dist/local-actions/policy.d.ts +21 -0
  334. package/dist/local-actions/policy.d.ts.map +1 -0
  335. package/dist/local-actions/policy.js +43 -0
  336. package/dist/local-actions/policy.js.map +1 -0
  337. package/dist/local-actions/private-port-forward.d.ts +36 -0
  338. package/dist/local-actions/private-port-forward.d.ts.map +1 -0
  339. package/dist/local-actions/private-port-forward.js +154 -0
  340. package/dist/local-actions/private-port-forward.js.map +1 -0
  341. package/dist/local-actions/provider-mcp-config.d.ts +101 -0
  342. package/dist/local-actions/provider-mcp-config.d.ts.map +1 -0
  343. package/dist/local-actions/provider-mcp-config.js +166 -0
  344. package/dist/local-actions/provider-mcp-config.js.map +1 -0
  345. package/dist/local-actions/providers.d.ts +12 -0
  346. package/dist/local-actions/providers.d.ts.map +1 -0
  347. package/dist/local-actions/providers.js +42 -0
  348. package/dist/local-actions/providers.js.map +1 -0
  349. package/dist/local-actions/schemas.d.ts +4 -0
  350. package/dist/local-actions/schemas.d.ts.map +1 -0
  351. package/dist/local-actions/schemas.js +314 -0
  352. package/dist/local-actions/schemas.js.map +1 -0
  353. package/dist/local-actions/sensitive-consent.d.ts +32 -0
  354. package/dist/local-actions/sensitive-consent.d.ts.map +1 -0
  355. package/dist/local-actions/sensitive-consent.js +47 -0
  356. package/dist/local-actions/sensitive-consent.js.map +1 -0
  357. package/dist/machines/command-launch.d.ts +17 -0
  358. package/dist/machines/command-launch.d.ts.map +1 -0
  359. package/dist/machines/command-launch.js +52 -0
  360. package/dist/machines/command-launch.js.map +1 -0
  361. package/dist/machines/execution-receipt.d.ts +18 -0
  362. package/dist/machines/execution-receipt.d.ts.map +1 -0
  363. package/dist/machines/execution-receipt.js +87 -0
  364. package/dist/machines/execution-receipt.js.map +1 -0
  365. package/dist/machines/executions-screen.d.ts +6 -0
  366. package/dist/machines/executions-screen.d.ts.map +1 -0
  367. package/dist/machines/executions-screen.js +410 -0
  368. package/dist/machines/executions-screen.js.map +1 -0
  369. package/dist/machines/explorer.d.ts +75 -0
  370. package/dist/machines/explorer.d.ts.map +1 -0
  371. package/dist/machines/explorer.js +1726 -0
  372. package/dist/machines/explorer.js.map +1 -0
  373. package/dist/machines/live-supervisor-update.d.ts +325 -0
  374. package/dist/machines/live-supervisor-update.d.ts.map +1 -0
  375. package/dist/machines/live-supervisor-update.js +513 -0
  376. package/dist/machines/live-supervisor-update.js.map +1 -0
  377. package/dist/machines/machine-first.d.ts +123 -0
  378. package/dist/machines/machine-first.d.ts.map +1 -0
  379. package/dist/machines/machine-first.js +119 -0
  380. package/dist/machines/machine-first.js.map +1 -0
  381. package/dist/machines/opencode-supervisor.d.ts +51 -0
  382. package/dist/machines/opencode-supervisor.d.ts.map +1 -0
  383. package/dist/machines/opencode-supervisor.js +124 -0
  384. package/dist/machines/opencode-supervisor.js.map +1 -0
  385. package/dist/machines/pagination.d.ts +4 -0
  386. package/dist/machines/pagination.d.ts.map +1 -0
  387. package/dist/machines/pagination.js +7 -0
  388. package/dist/machines/pagination.js.map +1 -0
  389. package/dist/machines/provider-availability.d.ts +42 -0
  390. package/dist/machines/provider-availability.d.ts.map +1 -0
  391. package/dist/machines/provider-availability.js +82 -0
  392. package/dist/machines/provider-availability.js.map +1 -0
  393. package/dist/machines/provider-screen.d.ts +12 -0
  394. package/dist/machines/provider-screen.d.ts.map +1 -0
  395. package/dist/machines/provider-screen.js +129 -0
  396. package/dist/machines/provider-screen.js.map +1 -0
  397. package/dist/machines/session-actionability.d.ts +86 -0
  398. package/dist/machines/session-actionability.d.ts.map +1 -0
  399. package/dist/machines/session-actionability.js +125 -0
  400. package/dist/machines/session-actionability.js.map +1 -0
  401. package/dist/machines/session-visibility.d.ts +139 -0
  402. package/dist/machines/session-visibility.d.ts.map +1 -0
  403. package/dist/machines/session-visibility.js +109 -0
  404. package/dist/machines/session-visibility.js.map +1 -0
  405. package/dist/platform/adapter.d.ts +62 -0
  406. package/dist/platform/adapter.d.ts.map +1 -0
  407. package/dist/platform/adapter.js +240 -0
  408. package/dist/platform/adapter.js.map +1 -0
  409. package/dist/platform/support.d.ts +12 -0
  410. package/dist/platform/support.d.ts.map +1 -0
  411. package/dist/platform/support.js +25 -0
  412. package/dist/platform/support.js.map +1 -0
  413. package/dist/pty/contracts.d.ts +45 -0
  414. package/dist/pty/contracts.d.ts.map +1 -0
  415. package/dist/pty/contracts.js +2 -0
  416. package/dist/pty/contracts.js.map +1 -0
  417. package/dist/pty/evidence-gate.d.ts +14 -0
  418. package/dist/pty/evidence-gate.d.ts.map +1 -0
  419. package/dist/pty/evidence-gate.js +35 -0
  420. package/dist/pty/evidence-gate.js.map +1 -0
  421. package/dist/pty/index.d.ts +5 -0
  422. package/dist/pty/index.d.ts.map +1 -0
  423. package/dist/pty/index.js +5 -0
  424. package/dist/pty/index.js.map +1 -0
  425. package/dist/pty/node-host-terminal.d.ts +14 -0
  426. package/dist/pty/node-host-terminal.d.ts.map +1 -0
  427. package/dist/pty/node-host-terminal.js +173 -0
  428. package/dist/pty/node-host-terminal.js.map +1 -0
  429. package/dist/pty/node-process.d.ts +26 -0
  430. package/dist/pty/node-process.d.ts.map +1 -0
  431. package/dist/pty/node-process.js +102 -0
  432. package/dist/pty/node-process.js.map +1 -0
  433. package/dist/runtime/api-terminal-control-plane.d.ts +7 -0
  434. package/dist/runtime/api-terminal-control-plane.d.ts.map +1 -0
  435. package/dist/runtime/api-terminal-control-plane.js +95 -0
  436. package/dist/runtime/api-terminal-control-plane.js.map +1 -0
  437. package/dist/runtime/backoff.d.ts +17 -0
  438. package/dist/runtime/backoff.d.ts.map +1 -0
  439. package/dist/runtime/backoff.js +61 -0
  440. package/dist/runtime/backoff.js.map +1 -0
  441. package/dist/runtime/boundary.d.ts +171 -0
  442. package/dist/runtime/boundary.d.ts.map +1 -0
  443. package/dist/runtime/boundary.js +1858 -0
  444. package/dist/runtime/boundary.js.map +1 -0
  445. package/dist/runtime/capability-gate.d.ts +25 -0
  446. package/dist/runtime/capability-gate.d.ts.map +1 -0
  447. package/dist/runtime/capability-gate.js +77 -0
  448. package/dist/runtime/capability-gate.js.map +1 -0
  449. package/dist/runtime/contracts.d.ts +25 -0
  450. package/dist/runtime/contracts.d.ts.map +1 -0
  451. package/dist/runtime/contracts.js +66 -0
  452. package/dist/runtime/contracts.js.map +1 -0
  453. package/dist/runtime/errors.d.ts +20 -0
  454. package/dist/runtime/errors.d.ts.map +1 -0
  455. package/dist/runtime/errors.js +22 -0
  456. package/dist/runtime/errors.js.map +1 -0
  457. package/dist/runtime/index.d.ts +9 -0
  458. package/dist/runtime/index.d.ts.map +1 -0
  459. package/dist/runtime/index.js +9 -0
  460. package/dist/runtime/index.js.map +1 -0
  461. package/dist/runtime/node-foreground-session.d.ts +50 -0
  462. package/dist/runtime/node-foreground-session.d.ts.map +1 -0
  463. package/dist/runtime/node-foreground-session.js +431 -0
  464. package/dist/runtime/node-foreground-session.js.map +1 -0
  465. package/dist/runtime/node-websocket-connector.d.ts +6 -0
  466. package/dist/runtime/node-websocket-connector.d.ts.map +1 -0
  467. package/dist/runtime/node-websocket-connector.js +344 -0
  468. package/dist/runtime/node-websocket-connector.js.map +1 -0
  469. package/dist/runtime/observer-screen.d.ts +6 -0
  470. package/dist/runtime/observer-screen.d.ts.map +1 -0
  471. package/dist/runtime/observer-screen.js +144 -0
  472. package/dist/runtime/observer-screen.js.map +1 -0
  473. package/dist/runtime/owner-grant-operations.d.ts +136 -0
  474. package/dist/runtime/owner-grant-operations.d.ts.map +1 -0
  475. package/dist/runtime/owner-grant-operations.js +216 -0
  476. package/dist/runtime/owner-grant-operations.js.map +1 -0
  477. package/dist/runtime/owner-grants-screen.d.ts +40 -0
  478. package/dist/runtime/owner-grants-screen.d.ts.map +1 -0
  479. package/dist/runtime/owner-grants-screen.js +987 -0
  480. package/dist/runtime/owner-grants-screen.js.map +1 -0
  481. package/dist/runtime/terminal-transport.d.ts +85 -0
  482. package/dist/runtime/terminal-transport.d.ts.map +1 -0
  483. package/dist/runtime/terminal-transport.js +152 -0
  484. package/dist/runtime/terminal-transport.js.map +1 -0
  485. package/dist/sync/continuous-sync-supervisor.d.ts +107 -0
  486. package/dist/sync/continuous-sync-supervisor.d.ts.map +1 -0
  487. package/dist/sync/continuous-sync-supervisor.js +1117 -0
  488. package/dist/sync/continuous-sync-supervisor.js.map +1 -0
  489. package/dist/sync/index.d.ts +9 -0
  490. package/dist/sync/index.d.ts.map +1 -0
  491. package/dist/sync/index.js +9 -0
  492. package/dist/sync/index.js.map +1 -0
  493. package/dist/sync/journal.d.ts +57 -0
  494. package/dist/sync/journal.d.ts.map +1 -0
  495. package/dist/sync/journal.js +685 -0
  496. package/dist/sync/journal.js.map +1 -0
  497. package/dist/sync/revisions.d.ts +118 -0
  498. package/dist/sync/revisions.d.ts.map +1 -0
  499. package/dist/sync/revisions.js +364 -0
  500. package/dist/sync/revisions.js.map +1 -0
  501. package/dist/sync/supervisor.d.ts +107 -0
  502. package/dist/sync/supervisor.d.ts.map +1 -0
  503. package/dist/sync/supervisor.js +246 -0
  504. package/dist/sync/supervisor.js.map +1 -0
  505. package/dist/sync/workspace-sync-client.d.ts +18 -0
  506. package/dist/sync/workspace-sync-client.d.ts.map +1 -0
  507. package/dist/sync/workspace-sync-client.js +150 -0
  508. package/dist/sync/workspace-sync-client.js.map +1 -0
  509. package/dist/sync/workspace-sync-coordinator.d.ts +76 -0
  510. package/dist/sync/workspace-sync-coordinator.d.ts.map +1 -0
  511. package/dist/sync/workspace-sync-coordinator.js +621 -0
  512. package/dist/sync/workspace-sync-coordinator.js.map +1 -0
  513. package/dist/sync/workspace-sync-product-service.d.ts +79 -0
  514. package/dist/sync/workspace-sync-product-service.d.ts.map +1 -0
  515. package/dist/sync/workspace-sync-product-service.js +398 -0
  516. package/dist/sync/workspace-sync-product-service.js.map +1 -0
  517. package/dist/sync/workspace-sync-protocol.d.ts +145 -0
  518. package/dist/sync/workspace-sync-protocol.d.ts.map +1 -0
  519. package/dist/sync/workspace-sync-protocol.js +347 -0
  520. package/dist/sync/workspace-sync-protocol.js.map +1 -0
  521. package/dist/terminal/appbar.d.ts +40 -0
  522. package/dist/terminal/appbar.d.ts.map +1 -0
  523. package/dist/terminal/appbar.js +55 -0
  524. package/dist/terminal/appbar.js.map +1 -0
  525. package/dist/terminal/cell-width.d.ts +3 -0
  526. package/dist/terminal/cell-width.d.ts.map +1 -0
  527. package/dist/terminal/cell-width.js +53 -0
  528. package/dist/terminal/cell-width.js.map +1 -0
  529. package/dist/terminal/codec.d.ts +107 -0
  530. package/dist/terminal/codec.d.ts.map +1 -0
  531. package/dist/terminal/codec.js +538 -0
  532. package/dist/terminal/codec.js.map +1 -0
  533. package/dist/terminal/foreground.d.ts +135 -0
  534. package/dist/terminal/foreground.d.ts.map +1 -0
  535. package/dist/terminal/foreground.js +2097 -0
  536. package/dist/terminal/foreground.js.map +1 -0
  537. package/dist/terminal/generated/terminal-wire-v1.d.ts +26 -0
  538. package/dist/terminal/generated/terminal-wire-v1.d.ts.map +1 -0
  539. package/dist/terminal/generated/terminal-wire-v1.js +27 -0
  540. package/dist/terminal/generated/terminal-wire-v1.js.map +1 -0
  541. package/dist/terminal/mode.d.ts +33 -0
  542. package/dist/terminal/mode.d.ts.map +1 -0
  543. package/dist/terminal/mode.js +99 -0
  544. package/dist/terminal/mode.js.map +1 -0
  545. package/dist/terminal/passthrough.d.ts +47 -0
  546. package/dist/terminal/passthrough.d.ts.map +1 -0
  547. package/dist/terminal/passthrough.js +666 -0
  548. package/dist/terminal/passthrough.js.map +1 -0
  549. package/dist/terminal/resume.d.ts +58 -0
  550. package/dist/terminal/resume.d.ts.map +1 -0
  551. package/dist/terminal/resume.js +185 -0
  552. package/dist/terminal/resume.js.map +1 -0
  553. package/dist/terminal/viewport.d.ts +93 -0
  554. package/dist/terminal/viewport.d.ts.map +1 -0
  555. package/dist/terminal/viewport.js +266 -0
  556. package/dist/terminal/viewport.js.map +1 -0
  557. package/dist/terminal/workbench.d.ts +32 -0
  558. package/dist/terminal/workbench.d.ts.map +1 -0
  559. package/dist/terminal/workbench.js +316 -0
  560. package/dist/terminal/workbench.js.map +1 -0
  561. package/dist/terminal/xterm-vte.d.ts +49 -0
  562. package/dist/terminal/xterm-vte.d.ts.map +1 -0
  563. package/dist/terminal/xterm-vte.js +602 -0
  564. package/dist/terminal/xterm-vte.js.map +1 -0
  565. package/dist/version.d.ts +3 -0
  566. package/dist/version.d.ts.map +1 -0
  567. package/dist/version.js +3 -0
  568. package/dist/version.js.map +1 -0
  569. package/dist/workspace/binding-path.d.ts +17 -0
  570. package/dist/workspace/binding-path.d.ts.map +1 -0
  571. package/dist/workspace/binding-path.js +91 -0
  572. package/dist/workspace/binding-path.js.map +1 -0
  573. package/dist/workspace/binding-store.d.ts +107 -0
  574. package/dist/workspace/binding-store.d.ts.map +1 -0
  575. package/dist/workspace/binding-store.js +783 -0
  576. package/dist/workspace/binding-store.js.map +1 -0
  577. package/dist/workspace/binding.d.ts +35 -0
  578. package/dist/workspace/binding.d.ts.map +1 -0
  579. package/dist/workspace/binding.js +70 -0
  580. package/dist/workspace/binding.js.map +1 -0
  581. package/dist/workspace/errors.d.ts +4 -0
  582. package/dist/workspace/errors.d.ts.map +1 -0
  583. package/dist/workspace/errors.js +19 -0
  584. package/dist/workspace/errors.js.map +1 -0
  585. package/dist/workspace/exclusion.d.ts +21 -0
  586. package/dist/workspace/exclusion.d.ts.map +1 -0
  587. package/dist/workspace/exclusion.js +243 -0
  588. package/dist/workspace/exclusion.js.map +1 -0
  589. package/dist/workspace/index.d.ts +8 -0
  590. package/dist/workspace/index.d.ts.map +1 -0
  591. package/dist/workspace/index.js +8 -0
  592. package/dist/workspace/index.js.map +1 -0
  593. package/dist/workspace/lifecycle.d.ts +56 -0
  594. package/dist/workspace/lifecycle.d.ts.map +1 -0
  595. package/dist/workspace/lifecycle.js +94 -0
  596. package/dist/workspace/lifecycle.js.map +1 -0
  597. package/dist/workspace/manifest.d.ts +47 -0
  598. package/dist/workspace/manifest.d.ts.map +1 -0
  599. package/dist/workspace/manifest.js +243 -0
  600. package/dist/workspace/manifest.js.map +1 -0
  601. package/dist/workspace/paths.d.ts +14 -0
  602. package/dist/workspace/paths.d.ts.map +1 -0
  603. package/dist/workspace/paths.js +67 -0
  604. package/dist/workspace/paths.js.map +1 -0
  605. package/dist/workspace/schema.d.ts +15 -0
  606. package/dist/workspace/schema.d.ts.map +1 -0
  607. package/dist/workspace/schema.js +33 -0
  608. package/dist/workspace/schema.js.map +1 -0
  609. package/dist/workspace/selection-screen.d.ts +4 -0
  610. package/dist/workspace/selection-screen.d.ts.map +1 -0
  611. package/dist/workspace/selection-screen.js +216 -0
  612. package/dist/workspace/selection-screen.js.map +1 -0
  613. package/dist/workspace/selection-service.d.ts +16 -0
  614. package/dist/workspace/selection-service.d.ts.map +1 -0
  615. package/dist/workspace/selection-service.js +65 -0
  616. package/dist/workspace/selection-service.js.map +1 -0
  617. package/node_modules/@xterm/headless/README.md +31 -0
  618. package/node_modules/@xterm/headless/lib-headless/xterm-headless.js +2 -0
  619. package/node_modules/@xterm/headless/lib-headless/xterm-headless.js.map +1 -0
  620. package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
  621. package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs.map +7 -0
  622. package/node_modules/@xterm/headless/package.json +29 -0
  623. package/node_modules/@xterm/headless/typings/xterm-headless.d.ts +1385 -0
  624. package/package.json +75 -0
@@ -0,0 +1,987 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createNodeForegroundTerminalHost } from '../pty/node-host-terminal.js';
3
+ import { sanitizeHumanTerminalOutput } from '../cli/output.js';
4
+ import { describeGrantState, OwnerGrantError, classifyTransportFailure, audienceRefusalCanResend } from '../api/owner-observe-grants-v2.js';
5
+ import { audienceFactIsOlder, audienceFactSameRun } from './owner-grant-operations.js';
6
+ const DURATIONS = [['1', '1 hour', 3600000], ['2', '8 hours', 28800000], ['3', '24 hours', 86400000]];
7
+ const CLOSE = 'Esc / Ctrl+C closes this local view. Observation is read-only: it never grants keyboard control.';
8
+ /**
9
+ * CUNA-COL-016-R4: state the real history scope BEFORE the key that grants.
10
+ *
11
+ * Plain language only. Every line is a property of the shipped producer rather
12
+ * than a promise, and the proof stays here instead of on the terminal: the
13
+ * supervisor's `publish_public_audience` documents itself as publishing "future
14
+ * provider output"; `PublicScreenView.reset` starts a blank model and
15
+ * `PublicAudience.publish` returns `starting_sequence: 0`, after which each poll
16
+ * encodes `snapshot()` -- a full redraw of the CURRENTLY VISIBLE screen. So no
17
+ * scrollback and no earlier output is replayed, but the screen still visible on
18
+ * attach is transmitted whole. Migration 0183's
19
+ * `resolve_collab_v2_observer_admission` admits nobody unless the session's
20
+ * latest audience result is `public`, so a grant and a live publication are
21
+ * separate facts and stay separate keypresses here. Which stream and generation
22
+ * a member joins is decided at attach time, which is why the lines below promise
23
+ * a boundary rather than a moment; the moment is named on the receipt the
24
+ * publication itself returns.
25
+ */
26
+ const DISCLOSURE = [
27
+ 'What the member will see:',
28
+ " Only what happens from the moment they start watching - a live view of this session's screen.",
29
+ ' Nothing from before that: earlier output and scrollback are never sent to them.',
30
+ ' Whatever is still on the screen when they start watching is visible to them, even if it is from earlier.',
31
+ ' Watching also requires this session to be shared live. That is a separate decision: press s on the session list.',
32
+ ' They can never type, resize or send a signal.',
33
+ ];
34
+ /**
35
+ * What starting a live share actually exposes, stated before the key that does it.
36
+ *
37
+ * Every line is a property of the shipped producer.
38
+ * `terminal-public-audience.py`'s `PublicAudience.publish` resets to a blank
39
+ * public view and answers `starting_sequence: 0`, after which each poll encodes
40
+ * `snapshot()` -- a full redraw of the CURRENTLY VISIBLE screen -- so no
41
+ * scrollback and no earlier output is replayed, while whatever is still on the
42
+ * screen is transmitted whole. CUNA-COL-016 is explicit that screen inspection
43
+ * cannot detect an authentication prompt and that bytes already delivered cannot
44
+ * be recalled; both are said here rather than implied.
45
+ */
46
+ const SHARE_DISCLOSURE = [
47
+ ' Everyone you have granted observation of this session can watch it from the moment this starts.',
48
+ ' They see whatever is on the screen now, and everything printed from now on.',
49
+ ' Nothing from before is replayed to them: no scrollback, no earlier output.',
50
+ ' Anything a program prints while this is on is part of that - a token, a login code, a file it opens.',
51
+ ' Cuna cannot detect that and will not hide it. Stopping later ends what comes next; it cannot recall what was already sent.',
52
+ ' They can never type, resize or send a signal.',
53
+ ];
54
+ export async function runOwnerGrantsScreen(api, sessions, store, identity, options = {}) {
55
+ const host = options.host ?? createNodeForegroundTerminalHost(), now = options.now ?? Date.now, lease = await host.acquire('rich'), abort = new AbortController(), encoder = new TextEncoder();
56
+ let stopped = false, busy = false, phase = 'sessions', index = 0, items = [], listing, members = [], membersAfter = null, membersNext = null, session, member, grant, failure, hostWrites = Promise.resolve();
57
+ // Every operation whose outcome is unknown HERE. The record is on disk before
58
+ // the request leaves, so an exit, a crash or a lost answer cannot lose the
59
+ // operation identity a replay needs. An unresolved operation blocks every NEW
60
+ // change of the SAME authority, so it is never overwritten -- and only that
61
+ // authority, because a grant and a publication are separate things.
62
+ let unresolved = [], cursor = 0, refusal;
63
+ // Operations whose exact request Cuna has already answered, so resending it
64
+ // again cannot settle anything. In memory only: a new process has not seen that
65
+ // refusal and must not pretend it has.
66
+ const unresendable = new Set();
67
+ // The operation a settled answer just closed. It is kept for the OUTCOME
68
+ // screen only: once the record is gone this string is the last thing that can
69
+ // tie what the owner is reading to what they asked Cuna to do.
70
+ let settled;
71
+ // The last sharing decision Cuna confirmed IN THIS PROCESS, and the session a
72
+ // pending sharing decision is about. A confirmed decision describes the moment
73
+ // Cuna confirmed it; it is never rendered as what the session is doing later.
74
+ let audience, audienceTarget, audienceFrom = 'sessions';
75
+ // The last answer the SESSION gave to the question "what are you sharing".
76
+ // It is an observation at the moment it was answered -- the strongest statement
77
+ // anything here can make -- and it is never merged with the history half beside
78
+ // it, nor promoted to a claim about a later moment.
79
+ let audienceState, audienceAsked;
80
+ // What this computer had already recorded about the run a reading describes.
81
+ // `stale` means the answer is from earlier in the run than a fact recorded
82
+ // here, so it must not be shown as this session's state; `otherRun` means the
83
+ // recorded fact is about a different run and could not order this answer at
84
+ // all. Both are reported rather than resolved by guessing.
85
+ let audienceStale, audienceOtherRun, factNote;
86
+ // Which surface the failure on screen came from, so a recovery offered for a
87
+ // grant refusal is not offered for a sharing refusal that reads alike.
88
+ let failureSubject = 'grant';
89
+ // Set while `grant` holds a receipt Cuna REPLAYED from storage rather than a
90
+ // reading of current authority. Never presented as the grant's state.
91
+ let replayed;
92
+ let forgetTarget, forgotten;
93
+ let finish;
94
+ const done = new Promise(resolve => { finish = resolve; });
95
+ const write = (text) => { hostWrites = hostWrites.then(async () => { if (!stopped)
96
+ await host.write(encoder.encode(text)); }).catch(() => { stop(); }); return hostWrites; };
97
+ const stop = () => { if (stopped)
98
+ return; stopped = true; abort.abort(); finish(); };
99
+ const screen = (title, lines, footer) => write('\x1b[H\x1b[2J' + sanitizeHumanTerminalOutput([title, '', ...lines, '', footer, CLOSE].join('\n')).replaceAll('\n', '\r\n'));
100
+ const mark = (i) => index === i ? '>' : ' ';
101
+ const short = (id) => id.slice(0, 8);
102
+ /** What one operation was, in the words the owner chose it with. */
103
+ const noun = (op) => op.kind === 'create' ? 'grant' : op.kind === 'revoke' ? 'revocation' : op.action === 'publish' ? 'start of live sharing' : 'stop of live sharing';
104
+ const sessionLine = (s, i) => `${mark(i)} ${s.machineName} / ${s.name} ${s.agent} ${s.state}${s.projectId === undefined ? ' (Project not named by server)' : ''} ${short(s.id)}`;
105
+ const memberLine = (m, i) => `${mark(i)} ${m.recipient_email ?? '(no email on record)'} ${short(m.principal_id)} membership rev ${m.membership_revision}`;
106
+ const authorityOf = (op) => op.kind === 'audience' ? 'audience' : 'grant';
107
+ const pendingIn = (authority) => unresolved.filter(op => authorityOf(op) === authority);
108
+ const banner = () => {
109
+ if (!unresolved.length)
110
+ return [];
111
+ const grants = pendingIn('grant').length, shares = pendingIn('audience').length;
112
+ return ['', `${unresolved.length} earlier change${unresolved.length === 1 ? ' has' : 's have'} an unknown outcome. Press p to return to ${unresolved.length === 1 ? 'it' : 'them'}.`,
113
+ ...(grants ? ['No new grant or revocation can start until it is resolved or its local record is forgotten.'] : []),
114
+ ...(shares ? ['No new start of live sharing can begin until it is resolved or its local record is forgotten. Stopping, asking what the session is sharing, and granting or revoking observation are all still available.'] : [])];
115
+ };
116
+ /** Say what this listing could not see. Silence here would claim a completeness the reads do not have. */
117
+ const limits = () => {
118
+ if (!listing)
119
+ return [];
120
+ const parts = [];
121
+ if (listing.omittedMachines > 0)
122
+ parts.push(`${listing.omittedMachines} further Machine${listing.omittedMachines === 1 ? ' was' : 's were'} not read`);
123
+ if (listing.machinesWithMoreSessions > 0)
124
+ parts.push(`${listing.machinesWithMoreSessions} Machine${listing.machinesWithMoreSessions === 1 ? ' has' : 's have'} more AgentSessions than one page`);
125
+ return parts.length ? ['', `This list is incomplete: ${parts.join(', and ')}. Sessions not shown here cannot be shared from this screen.`] : [];
126
+ };
127
+ const paintSessions = () => {
128
+ phase = 'sessions';
129
+ return screen('Share a session - read-only observation', [...(items.length ? items.map(sessionLine) : ['No AgentSessions in this Project were found on your Machines.']), ...limits(),
130
+ ...(items.length ? ['', 'Granting a member access and sharing the session live are two separate decisions. Cuna needs both before anyone can watch.'] : []), ...banner()], items.length ? 'Enter grants a member access; s starts sharing this session live; e stops it; c asks what it is sharing now; Up/Down selects; r refreshes.' : 'r refreshes.');
131
+ };
132
+ const paintMembers = () => { phase = 'members'; return screen(`Choose the member who may observe ${session.name}`, [...(members.length ? members.map(memberLine) : ['No other members hold observer membership in this Project. Invite one in the web app first; an invitation alone grants no observation.']), '', 'Membership lets a member be granted observation; it grants nothing by itself.', ...banner()], 'Up/Down selects; Enter chooses; n next page; r refreshes; b back.'); };
133
+ const paintDuration = () => { phase = 'duration'; return screen(`Grant ${member.recipient_email ?? short(member.principal_id)} read-only observation of ${session.name}`, [...DURATIONS.map(([key, label]) => ` ${key} ${label}`), '', ...DISCLOSURE, ...banner()], 'Press 1, 2 or 3 to create the grant; b back.'); };
134
+ const paintGrant = () => {
135
+ phase = 'grant';
136
+ const g = grant, d = describeGrantState(g, now()), stored = replayed;
137
+ return screen(`Observation grant ${short(g.grant_id)} - ${stored ? 'stored answer, not current authority' : d.label}`, [`Session ${g.agent_session_id}`, `Member ${g.subject_principal_id}`, `State ${g.state}${g.revocation_state === null ? '' : ` / ${g.revocation_state}`} revision ${g.revision}`, `Expires ${new Date(g.expires_at).toISOString()}`, '',
138
+ ...(stored
139
+ ? [`This is the receipt Cuna stored when operation ${stored.operationId} was first answered, replayed unchanged. It cannot show a revocation made since, so it is not evidence that this grant is still ${g.state}.`,
140
+ ...(stored.reason === undefined ? [] : [`Reading the current state did not answer: ${stored.reason}`]),
141
+ 'Press i to read the grant\'s current state.']
142
+ : [d.detail]),
143
+ // A grant on its own shows nobody anything, and this is the screen where
144
+ // that is asked. Neither key claims to know how the session is shared now.
145
+ '', 'Watching also needs this session to be shared live, which is a separate decision from this grant.',
146
+ ...banner()], `${stored ? 'i reads the current state' : d.final ? 'i inspects again' : g.state === 'active' ? 'x revokes; i inspects the current state' : 'i inspects again (no scheduled polling)'}; s starts sharing this session live; e stops it; c asks what it is sharing now; b back to sessions.`);
147
+ };
148
+ /**
149
+ * The two sharing decisions, each asked before it is made.
150
+ *
151
+ * Publication is the authority that lets a granted member see anything, so it
152
+ * is never a side effect of granting: the owner reaches these screens by
153
+ * pressing s or e and confirms with y.
154
+ */
155
+ const paintConfirmShare = () => {
156
+ phase = 'confirm-share';
157
+ const target = audienceTarget;
158
+ return screen("Share this session's screen live?", [`Session ${target.label}`, '', ...SHARE_DISCLOSURE, '',
159
+ 'It gives nobody keyboard control, and it creates no grant: a member still needs one, and Cuna still applies its own checks before letting anyone watch.'], 'Press y to start sharing; any other key cancels and sends nothing.');
160
+ };
161
+ const paintConfirmStop = () => {
162
+ phase = 'confirm-stop';
163
+ const target = audienceTarget;
164
+ return screen('Stop sharing this session live?', [`Session ${target.label}`, '',
165
+ ' Anyone watching stops receiving this session the moment Cuna confirms it.',
166
+ ' What they have already seen cannot be recalled.',
167
+ ' Observation grants are left exactly as they are: this revokes nothing and expires nothing.',
168
+ ' If the session is already private, this changes nothing.'], 'Press y to stop sharing; any other key cancels and sends nothing.');
169
+ };
170
+ /**
171
+ * The receipt screen for a confirmed sharing decision.
172
+ *
173
+ * CUNA-COL-016-R4 asks for the starting point of what a watcher receives. The
174
+ * publication receipt is the only place it exists: the producer answers with a
175
+ * generation exactly one past the one it was issued against and
176
+ * `first_sequence: '1'`, meaning a new public stream whose first frame is a
177
+ * redraw of the screen as it is now. It is rendered as a plain share number and
178
+ * a plain sentence; the stream and request identifiers stay off the terminal
179
+ * because no decision here depends on them.
180
+ */
181
+ const paintAudience = () => {
182
+ phase = 'shared';
183
+ const a = audience, target = audienceTarget;
184
+ // An answer that a later confirmed decision has already replaced never reaches
185
+ // this screen: `setAudience` refuses it at the adapter, so there is no path by
186
+ // which a superseded receipt can be painted as the session's state.
187
+ return screen(a.state === 'public' ? 'Live sharing started - Cuna confirmed it' : 'Live sharing stopped - Cuna confirmed it', [`Session ${target?.label ?? a.agentSessionId}`,
188
+ ...(a.state === 'public'
189
+ ? [`Share #${a.generation} of this run of the session`, '',
190
+ 'Watching starts here: the first thing anyone sees is the screen as it is now. Nothing printed before this moment is sent to them.',
191
+ 'Everyone who holds an active observation grant on this session can watch it, if Cuna\'s own checks also allow it. Sharing grants no keyboard, resize or signal.']
192
+ : ['',
193
+ 'Cuna confirmed with the session itself that its output is fenced: nothing more is sent to anyone who was watching.',
194
+ 'What was already sent cannot be recalled. Observation grants are untouched - stopping the live share revokes nothing.']),
195
+ ...banner()], a.state === 'public' ? 'e stops sharing; c asks what the session is sharing now; b back to sessions.' : 's starts sharing again; c asks what the session is sharing now; b back to sessions.');
196
+ };
197
+ /**
198
+ * What one earlier change is RECORDED to have done, said next to -- never
199
+ * merged into -- what the session is doing now.
200
+ *
201
+ * The distinction is the whole repair: a receipt Cuna stored describes the
202
+ * moment that request was answered, and an owner reading it as the present is
203
+ * exactly how a session stays shared while its owner believes it is private.
204
+ */
205
+ const historyLines = (operation, asked) => {
206
+ // `unknown` carries no action of its own -- there is no request to read one
207
+ // from -- so the noun comes from the durable record the owner is asking about.
208
+ const named = `the earlier ${asked === 'publish' ? 'start of live sharing' : 'stop of live sharing'} (operation ${operation.operationId})`;
209
+ if (operation.status === 'unknown')
210
+ return [`Cuna has no record of ${named} at all. It never reached the session, so it changed nothing.`];
211
+ const opened = `${named[0].toUpperCase()}${named.slice(1)}`;
212
+ if (operation.status !== 'recorded')
213
+ return operation.status === 'pending'
214
+ ? [`${opened} is still inside the few seconds Cuna allows it, with no answer recorded yet. Ask again in a moment.`]
215
+ : [`${opened} ran out of time with no answer recorded.`,
216
+ 'Whether it took effect is unknown, and it will stay unknown: Cuna will not raise that exact request with the session again.',
217
+ 'What the session is doing now is the line above, and it is not an answer about that request.'];
218
+ if (operation.outcome.kind === 'refused')
219
+ return [
220
+ `Cuna has a recorded answer for ${named}: the session refused it, so that request did not change what it asked to change.`,
221
+ 'That is what that one request did. It is not what the session is doing now.'
222
+ ];
223
+ return [
224
+ `Cuna has a recorded answer for ${named}: the session ${operation.outcome.state === 'public' ? 'started sharing at share' : 'stopped sharing at share'} #${operation.outcome.generation}.`,
225
+ 'That is what that one request did. It is not what the session is doing now.'
226
+ ];
227
+ };
228
+ /**
229
+ * The answer to "what is this session sharing", as the session itself gave it.
230
+ *
231
+ * Asking is a query: `consume_collab_v2_session_audience_response` returns
232
+ * before it retires an attachment or closes an endpoint when the action is
233
+ * `observe`, so nothing here starts a share, stops one, creates a grant or
234
+ * disconnects anybody -- and that is stated rather than assumed.
235
+ */
236
+ /** The one sentence every reading has to carry. A reading is answered at a
237
+ * moment; nothing about that moment binds the next one, and this computer can
238
+ * only compare it against changes that were made HERE. */
239
+ const AS_OF = [
240
+ 'That is what the session answered when Cuna asked, not a promise about now: any window signed in to this account can change it at any moment.',
241
+ 'Cuna is asked again every time you press c. This computer can only compare an answer against sharing changes made on this computer.',
242
+ ];
243
+ const factLine = (fact) => `${fact.kind === 'transition' ? 'A sharing change confirmed on this computer' : 'An answer read on this computer'}: ${fact.state === 'public' ? `sharing live at share #${fact.generation}` : fact.generation === '0' ? 'never shared live on that run' : `not sharing live, last share #${fact.generation}`}.`;
244
+ const paintAudienceState = () => {
245
+ phase = 'state';
246
+ const s = audienceState, target = audienceTarget, current = s.current, stale = audienceStale;
247
+ const answered = current.state === 'public'
248
+ ? [`Share #${current.generation} of this run of the session`, '',
249
+ 'Cuna put the question to the session itself, and the session answered that it was sharing live.',
250
+ 'Everyone holding an active observation grant on this session could watch it from that moment, if Cuna\'s own checks also allowed it.']
251
+ : [...(current.generation === '0' ? ['', 'The session answered that this run has never been shared live.'] : [`Last share #${current.generation} of this run of the session`, '',
252
+ 'Cuna put the question to the session itself, and the session answered that it was not sharing live.']),
253
+ 'Nothing new was being sent to anyone who had been watching. What was already sent cannot be recalled.'];
254
+ return screen(stale !== undefined
255
+ ? 'Cuna answered about an earlier moment than this computer already knows about'
256
+ : current.state === 'public' ? 'When Cuna asked, this session was sharing live' : 'When Cuna asked, this session was not sharing live', [`Session ${target?.label ?? current.agentSessionId}`,
257
+ ...(stale !== undefined
258
+ // Painting this as the session's state is the exact failure this route
259
+ // exists to end, so it is described and set aside instead.
260
+ ? ['', `Cuna's answer describes ${current.state === 'public' ? `sharing live at share #${current.generation}` : `not sharing live at share #${current.generation}`}, which comes earlier in this run than what this computer has already recorded.`,
261
+ factLine(stale),
262
+ 'So that answer is not shown as this session\'s state. Press c to ask again.']
263
+ : answered),
264
+ ...(audienceOtherRun === undefined ? [] : ['',
265
+ 'This computer also holds a sharing record for a different run of this session, so it could not be used to order this answer.',
266
+ factLine(audienceOtherRun)]),
267
+ '', ...AS_OF,
268
+ 'Asking changed nothing: it started no share, stopped none, created no grant and disconnected nobody.',
269
+ ...(factNote === undefined ? [] : ['', factNote]),
270
+ ...(s.operation === undefined || audienceAsked === undefined ? [] : ['', ...historyLines(s.operation, audienceAsked),
271
+ // The record is gone by the time this renders, so this is the last link
272
+ // between the answer above and the thing the owner asked Cuna to do.
273
+ ...(settled === undefined ? [] : [`Its local record was dropped: Cuna answered operation ${settled.operationId}, so nothing here is left to finish.`])]),
274
+ ...banner()], `${stale !== undefined ? 's starts sharing; e stops sharing' : current.state === 'public' ? 'e stops sharing' : 's starts sharing'}; c asks again; b back to sessions.`);
275
+ };
276
+ /** True when rereading the Project members is the actual recovery for this refusal. */
277
+ const rereadsMembers = (f) => failureSubject === 'grant' && !grant && session !== undefined && (f.kind === 'stale_revision' || f.kind === 'grant_unavailable');
278
+ const paintFailure = () => {
279
+ phase = 'failure';
280
+ const f = failure;
281
+ return screen(`${failureSubject === 'reading' ? 'Not read' : 'Not done'} - ${f.kind.replaceAll('_', ' ')}`, [f.message,
282
+ // A question that failed leaves everything as it was, including every
283
+ // earlier change it might have been asked about.
284
+ ...(failureSubject === 'reading' ? ['', 'Asking changed nothing, and this answer is about the question only: it says nothing about any earlier start or stop.'] : []),
285
+ // `settled` is set only when a MUTATION was refused with its effect known,
286
+ // so this is the one place the no-effect claim is proved by the answer. A
287
+ // read reaching this screen changed nothing by construction and needs no
288
+ // such line; asserting one for every failure claimed more than was known.
289
+ ...(settled === undefined ? [] : ['',
290
+ 'This attempt applied nothing.',
291
+ // The record is gone by the time this renders, so this is the last link
292
+ // between the sentence above and the thing the owner asked Cuna to do.
293
+ `Operation ${settled.operationId} (${noun(settled)}) is settled: Cuna answered it, so its local record was dropped.`]),
294
+ ...banner()], rereadsMembers(f) ? 'r rereads the members; b back.' : grant ? 'i inspects the grant; b back.' : 'b back to sessions.');
295
+ };
296
+ /**
297
+ * The recovery screen, reached on relaunch as well as in-session.
298
+ *
299
+ * It states what is known and what is not, and does not claim the CLI can
300
+ * determine a create's outcome: the canonical contract has no owner-side
301
+ * listing of a session's grants, so an unanswered create can only be replayed
302
+ * under its own operation identity or checked in the web app. A revoke is
303
+ * different -- the grant ID is known, so inspecting it answers the question.
304
+ */
305
+ const paintUnresolved = () => {
306
+ phase = 'unresolved';
307
+ // A resend the server SETTLES empties this list, and the reason it settled on
308
+ // is then the only explanation that exists -- the durable record it would
309
+ // otherwise have been read from has just been destroyed. Falling through to
310
+ // the session list here rendered nothing at all, which is what the owner saw
311
+ // on a 404 or a 409 replay.
312
+ if (!unresolved.length)
313
+ return failure ? paintFailure() : paintSessions();
314
+ cursor = Math.min(cursor, unresolved.length - 1);
315
+ const row = unresolved[cursor];
316
+ const lines = unresolved.map((op, i) => `${cursor === i ? '>' : ' '} ${op.kind === 'create' ? 'grant ' : op.kind === 'revoke' ? 'revoke ' : op.action === 'publish' ? 'share ' : 'unshare'} operation ${op.operationId}`);
317
+ lines.push('', row.kind === 'create'
318
+ ? `Grant for member ${row.subjectPrincipalId} on session ${row.agentSessionId}, membership revision ${row.expectedMembershipRevision}, expiring ${new Date(row.expiresAtMs).toISOString()}.`
319
+ : row.kind === 'revoke'
320
+ ? `Revocation of grant ${row.grantId} (member ${row.subjectPrincipalId}, session ${row.agentSessionId}) against revision ${row.expectedRevision}.`
321
+ : `${row.action === 'publish' ? 'Start' : 'Stop'} of live sharing on session ${row.agentSessionId}.`);
322
+ // Cuna has closed the door on resending this exact request: the stored request
323
+ // has expired, or Cuna has already recorded an answer for it. Offering `r`
324
+ // would be offering an action that cannot settle anything -- and the standing
325
+ // description must stop promising it, or the screen contradicts itself one
326
+ // line later.
327
+ const resendable = !unresendable.has(row.operationId);
328
+ lines.push('', row.kind === 'create'
329
+ ? "Cuna never confirmed this. The grant may or may not exist, and this CLI cannot tell: the collaboration contract has no owner-side listing of a session's grants. Resending reuses this exact operation ID, so it cannot create a second grant. The web app is the other way to find out."
330
+ : row.kind === 'revoke'
331
+ ? 'Cuna never confirmed this. Inspecting the grant answers it directly, because the grant ID is known. Resending reuses this exact operation ID and the same expected revision.'
332
+ // Resending is not one option among several here: the session refuses a
333
+ // fresh request issued against a sharing state it has already moved past,
334
+ // so this exact identity is the only thing that can ever settle this -- for
335
+ // as long as Cuna will still raise it.
336
+ : `Cuna never confirmed this. Press c to ask the session what it is sharing now and what this exact request is recorded to have done; that is the question this record exists around, and asking changes nothing.${resendable ? ' Resending instead reuses this exact operation ID, which asks the session about the same request rather than starting a second one.' : ' Cuna will not raise this request with the session again, so resending cannot settle it now.'}${row.action === 'publish' ? ' If what you need is for nobody to be watching, press e: stopping never depends on this answer.' : ''}`);
337
+ if (!resendable)
338
+ lines.push('', 'What that operation did stays unknown, and it will stay unknown. Asking what the session is sharing now still answers what the session is doing; forgetting the record is the only other thing left here, and it changes nothing at Cuna.');
339
+ if (failure)
340
+ lines.push('', `Last reason: ${failure.message}`);
341
+ if (refusal)
342
+ lines.push('', refusal);
343
+ return screen(`Unresolved change${unresolved.length === 1 ? '' : 's'} - outcome unknown`, lines, `Up/Down selects; ${resendable ? 'r resends this exact operation; ' : ''}${row.kind === 'revoke' ? 'i inspects the grant; ' : row.kind === 'audience' ? `c asks what the session is sharing now${row.action === 'publish' ? '; e stops live sharing now' : ''}; ` : ''}d forgets the local record only; b back to sessions.`);
344
+ };
345
+ /**
346
+ * Deleting the record is irreversible and revokes nothing, so it is asked
347
+ * before it is done and named after it is done.
348
+ */
349
+ const paintConfirmForget = () => {
350
+ phase = 'confirm-forget';
351
+ const op = forgetTarget;
352
+ return screen('Forget this local record?', [`${op.kind === 'create' ? 'Grant' : op.kind === 'revoke' ? 'Revocation' : op.action === 'publish' ? 'Start of live sharing' : 'Stop of live sharing'} operation ${op.operationId}`, '',
353
+ 'This deletes the local record only. Cuna is not contacted and nothing is revoked.',
354
+ // Reaching Cuna is not the same as taking effect, and the outcome here is
355
+ // unknown by definition. Every branch stays conditional on the effect, not
356
+ // on delivery, and none states that an authority exists.
357
+ op.kind === 'create'
358
+ ? "The outcome stays unknown. If that request did take effect, a grant exists and the member may be able to observe this session - and after this, no screen in this CLI can name or replay that operation, because the collaboration contract has no owner-side listing of a session's grants. The web app becomes the only way to find out."
359
+ : op.kind === 'revoke'
360
+ ? `The outcome stays unknown. If that request did take effect, the revocation applied. Grant ${op.grantId} stays readable by ID either way, so its state is still reachable; the operation identity is not.`
361
+ : op.action === 'publish'
362
+ ? 'The outcome of that request stays unknown, and after this no screen here can name or replay it. Asking what the session is sharing now does not need it and answers what the session is doing; stopping the sharing does not need it either and is the only way to be sure nobody is watching.'
363
+ : 'The outcome of that request stays unknown, and after this no screen here can name or replay it. Asking what the session is sharing now does not need it. Asking Cuna to stop sharing again is a separate decision that does not need it either.',
364
+ '', 'This cannot be undone.'], 'Press y to forget it; any other key keeps it.');
365
+ };
366
+ const paintForgotten = () => {
367
+ phase = 'forgotten';
368
+ const op = forgotten;
369
+ return screen('Local record forgotten - nothing was revoked', [`${op.kind === 'create' ? 'Grant' : op.kind === 'revoke' ? 'Revocation' : op.action === 'publish' ? 'Start of live sharing' : 'Stop of live sharing'} operation ${op.operationId}`, '',
370
+ 'Cuna was not contacted, so its side is exactly as it was. Whatever that operation did or did not do still stands, unchanged.',
371
+ op.kind === 'create'
372
+ ? 'This CLI can no longer name or replay it. Check the web app if you need to know whether a grant exists.'
373
+ : op.kind === 'revoke'
374
+ ? `Grant ${op.grantId} can still be read: cuna share --project ${identity.project} --grant ${op.grantId}`
375
+ : 'This CLI can no longer replay it. Asking what the session is sharing now, and stopping live sharing, are both still available and depend on nothing that was just deleted.',
376
+ ...banner()], unresolved.length ? 'p returns to the remaining unresolved changes; b back to sessions.' : 'b back to sessions.');
377
+ };
378
+ /**
379
+ * Run a local-state operation and keep its failure local.
380
+ *
381
+ * These never touch the network, so classifying them with the transport
382
+ * classifier reported "Cuna could not be reached for this read", which names
383
+ * the wrong boundary and hides the file the person has to fix.
384
+ */
385
+ const localState = async (what, work) => {
386
+ try {
387
+ return await work();
388
+ }
389
+ catch (error) {
390
+ if (error instanceof OwnerGrantError || (error instanceof Error && error.name === 'AbortError'))
391
+ throw error;
392
+ throw new OwnerGrantError('unavailable', `Cuna was not contacted. This CLI could not ${what}: ${error instanceof Error ? error.message : String(error)}`, error);
393
+ }
394
+ };
395
+ const run = async (label, work) => {
396
+ if (busy || stopped)
397
+ return;
398
+ busy = true;
399
+ const previous = phase;
400
+ phase = 'working';
401
+ settled = undefined;
402
+ await screen(label, ['Waiting for Cuna...'], '');
403
+ try {
404
+ await work();
405
+ }
406
+ catch (error) {
407
+ if (stopped)
408
+ return;
409
+ failure = error instanceof OwnerGrantError ? error : classifyTransportFailure(error, false);
410
+ if (failure.effectUnknown || previous === 'unresolved') {
411
+ cursor = 0;
412
+ await paintUnresolved();
413
+ }
414
+ else
415
+ await paintFailure();
416
+ }
417
+ finally {
418
+ busy = false;
419
+ }
420
+ };
421
+ /**
422
+ * Drop a resolved record.
423
+ *
424
+ * Failing to drop it over-reports, which is the safe direction, and it must
425
+ * never replace the answer that made this call: this runs inside the catch
426
+ * that carries a server refusal.
427
+ */
428
+ const forget = async (operationId) => {
429
+ try {
430
+ await store.settle(operationId);
431
+ unresolved = unresolved.filter(op => op.operationId !== operationId);
432
+ }
433
+ catch (error) {
434
+ refusal = `Cuna answered this operation, but its local record could not be removed: ${error instanceof Error ? error.message : String(error)}. It is still listed as unresolved here.`;
435
+ }
436
+ };
437
+ /**
438
+ * Send one mutation under an already-durable identity.
439
+ *
440
+ * On success, and on any refusal the server issued WITHOUT acting, the record
441
+ * is dropped. When the effect is undetermined the record stays exactly as it
442
+ * was, which is what makes the next process able to finish this operation.
443
+ */
444
+ const settleWith = async (op, call, accept) => {
445
+ const identity = { operationId: op.operationId, kind: op.kind, ...(op.action === undefined ? {} : { action: op.action }) };
446
+ let value;
447
+ try {
448
+ value = await call();
449
+ }
450
+ catch (error) {
451
+ if (!(error instanceof OwnerGrantError) || !error.effectUnknown) {
452
+ settled = identity;
453
+ await forget(op.operationId);
454
+ }
455
+ throw error;
456
+ }
457
+ await forget(op.operationId);
458
+ failure = undefined;
459
+ settled = identity;
460
+ await accept(value);
461
+ };
462
+ const acceptGrant = async (value) => { grant = value; replayed = undefined; refusal = undefined; await paintGrant(); };
463
+ /**
464
+ * Write down what Cuna confirmed, so a second `cuna share` on this computer
465
+ * cannot later paint an older answer as this session's state.
466
+ *
467
+ * A store that will not record it is reported rather than swallowed: the
468
+ * consequence is that another window may show something stale, which the owner
469
+ * has to be able to know.
470
+ */
471
+ const rememberAudience = async (fact) => {
472
+ try {
473
+ await store.recordAudienceFact(fact);
474
+ factNote = undefined;
475
+ }
476
+ catch (error) {
477
+ factNote = `Cuna answered, but this computer could not record it where other windows would see it: ${error instanceof Error ? error.message : String(error)}. Another window of this account may still show an older answer as current.`;
478
+ }
479
+ };
480
+ const acceptAudience = async (value) => {
481
+ audience = value;
482
+ refusal = undefined;
483
+ await rememberAudience({ kind: 'transition', agentSessionId: value.agentSessionId, sessionIncarnation: value.sessionIncarnation, processEpoch: value.processEpoch, state: value.state, generation: value.generation });
484
+ await paintAudience();
485
+ };
486
+ const reserve = async (intent) => {
487
+ let reserved;
488
+ try {
489
+ reserved = await store.reserve(intent);
490
+ }
491
+ catch (error) {
492
+ throw new OwnerGrantError('unavailable', 'Cuna could not record this change locally, so nothing was sent. Fix the local state directory and try again.', error);
493
+ }
494
+ unresolved = [reserved, ...unresolved];
495
+ return reserved;
496
+ };
497
+ /**
498
+ * Re-read the durable scope immediately before reserving.
499
+ *
500
+ * `unresolved` is this process's view, loaded at start. A second `cuna share`
501
+ * on the same Project writes to the same scope directory, so the in-memory
502
+ * guard alone lets two processes each start a mutation. This closes all but
503
+ * the window between this read and the reserve below; there is no cross-process
504
+ * lock, so it narrows the race rather than removing it.
505
+ */
506
+ const cleared = async (action, authority) => {
507
+ unresolved = [...await localState('read its record of unresolved changes', () => store.list(abort.signal))];
508
+ const blocking = pendingIn(authority);
509
+ if (!blocking.length)
510
+ return true;
511
+ cursor = Math.max(0, unresolved.indexOf(blocking[0]));
512
+ refusal = `Cannot ${action} while an earlier change has an unknown outcome. Resend it, inspect it, or forget its local record first.`;
513
+ await paintUnresolved();
514
+ return false;
515
+ };
516
+ const readSessions = async () => { listing = await sessions.list(abort.signal); items = listing.items.filter(s => s.projectId === undefined || s.projectId === identity.project); };
517
+ const loadSessions = () => run('Finding your AgentSessions', async () => { failureSubject = 'grant'; await readSessions(); index = 0; failure = undefined; refusal = undefined; await paintSessions(); });
518
+ const loadMembers = (after) => run('Reading Project members', async () => { failureSubject = 'grant'; const page = await api.listMembers(after, abort.signal); members = page.items.filter(m => m.principal_id !== identity.owner); membersAfter = after; membersNext = page.next_after_principal_id; index = 0; await paintMembers(); });
519
+ const create = (input) => run('Creating the observation grant', async () => { failureSubject = 'grant'; if (!await cleared('create a grant', 'grant'))
520
+ return; const op = await reserve({ kind: 'create', operationId: randomUUID(), ...input }); await settleWith(op, () => api.create(input, op.operationId, abort.signal), acceptGrant); });
521
+ const revoke = (g) => run('Requesting revocation', async () => { failureSubject = 'grant'; if (!await cleared('revoke', 'grant'))
522
+ return; const op = await reserve({ kind: 'revoke', operationId: randomUUID(), grantId: g.grant_id, agentSessionId: g.agent_session_id, subjectPrincipalId: g.subject_principal_id, expectedRevision: g.revision }); await settleWith(op, () => api.revoke(g, op.operationId, abort.signal), acceptGrant); });
523
+ /** The session one sharing decision is about, named the way the owner saw it if this process has seen it listed. */
524
+ const audienceTargetFor = (agentSessionId, incarnation) => {
525
+ const known = items.find(s => s.id === agentSessionId);
526
+ return { id: agentSessionId, label: known ? `${known.machineName} / ${known.name}` : agentSessionId, ...(incarnation === undefined ? {} : { incarnation }) };
527
+ };
528
+ /**
529
+ * Start or stop live sharing of one session.
530
+ *
531
+ * Stopping is deliberately NOT gated on an unresolved change. Every other
532
+ * mutation here is blocked while one exists, because starting a second could
533
+ * create a second authority. This one only ever removes access, and an owner
534
+ * who cannot tell whether a session is live is exactly the owner who most needs
535
+ * to be able to make it private. It still takes its own operation identity and
536
+ * never touches, replaces or replays the unresolved record.
537
+ */
538
+ const changeAudience = (action, target) => run(action === 'publish' ? 'Starting live sharing' : 'Stopping live sharing', async () => {
539
+ failureSubject = 'audience';
540
+ audienceTarget = target;
541
+ if (action === 'publish') {
542
+ if (!await cleared('start live sharing', 'audience'))
543
+ return;
544
+ }
545
+ else
546
+ unresolved = [...await localState('read its record of unresolved changes', () => store.list(abort.signal))];
547
+ const op = await reserve({ kind: 'audience', operationId: randomUUID(), action, agentSessionId: target.id });
548
+ await settleWith(op, () => api.setAudience({ agentSessionId: target.id, action, ...(target.incarnation === undefined ? {} : { sessionIncarnation: target.incarnation }) }, op.operationId, abort.signal), acceptAudience);
549
+ });
550
+ /**
551
+ * Reconcile the receipt a CREATE replay returned.
552
+ *
553
+ * `create_collab_v2_session_observe_grant` answers a replayed operation ID with
554
+ * `return old.result` -- the receipt as it stood when the original request was
555
+ * first answered. Migration 0190 gave the revoke replay a freshly built receipt
556
+ * but left this one a stored snapshot, and it always says `active`, so showing
557
+ * it as the grant's state would report authority that may have been revoked
558
+ * since. Read the current state instead; if that read does not answer, keep
559
+ * the replayed receipt and label it for exactly what it is.
560
+ */
561
+ const reconcileReplay = async (operationId) => {
562
+ const stored = grant;
563
+ replayed = { operationId };
564
+ try {
565
+ grant = await api.inspect({ grant_id: stored.grant_id, agent_session_id: stored.agent_session_id, subject_principal_id: stored.subject_principal_id, revision: stored.revision }, abort.signal);
566
+ replayed = undefined;
567
+ refusal = undefined;
568
+ }
569
+ catch (error) {
570
+ grant = stored;
571
+ replayed = { operationId, reason: (error instanceof OwnerGrantError ? error : classifyTransportFailure(error, false, 'inspect')).message };
572
+ }
573
+ await paintGrant();
574
+ };
575
+ /**
576
+ * Ask the session what it is sharing, optionally about one unfinished change.
577
+ *
578
+ * It takes NO durable record and reserves nothing. A reading is a query with no
579
+ * effect to recover, and its stored request keeps a twenty-second deadline, so
580
+ * a fresh identity every time is the only thing that can work. It is also never
581
+ * gated on an unresolved change: an owner who cannot tell whether a session is
582
+ * shared is exactly the owner who most needs to be able to ask.
583
+ *
584
+ * An answer about the named change SETTLES its record when one exists --
585
+ * `recorded` because the answer is durable, `unknown` because Cuna never issued
586
+ * the request at all. `expired_unrecorded` settles nothing: the effect stays
587
+ * unknown, and only the owner removes that record. It does establish that
588
+ * resending can no longer settle it, which is recorded the same way a refusal
589
+ * that closed the door is.
590
+ */
591
+ const askAudience = (target, about) => run('Asking what this session is sharing now', async () => {
592
+ failureSubject = 'reading';
593
+ audienceTarget = target;
594
+ audienceAsked = about?.action;
595
+ audienceStale = undefined;
596
+ audienceOtherRun = undefined;
597
+ factNote = undefined;
598
+ const answer = await api.readAudience({ agentSessionId: target.id,
599
+ ...(target.incarnation === undefined ? {} : { sessionIncarnation: target.incarnation }),
600
+ ...(about === undefined ? {} : { reconcile: { operationId: about.operationId, action: about.action } }) }, randomUUID(), abort.signal);
601
+ // The history half is about one named operation and is independent of every
602
+ // ordering question below, so it settles its record either way.
603
+ if (about !== undefined && answer.operation !== undefined) {
604
+ if (answer.operation.status === 'recorded' || answer.operation.status === 'unknown') {
605
+ settled = { operationId: about.operationId, kind: 'audience', action: about.action };
606
+ await forget(about.operationId);
607
+ }
608
+ else if (answer.operation.status === 'expired_unrecorded')
609
+ unresendable.add(about.operationId);
610
+ }
611
+ // Order the answer against what this computer recorded, read AFTER the answer
612
+ // arrived: another process may have confirmed a change while this question was
613
+ // in flight, and that is exactly the case a floor held in this closure missed.
614
+ const current = answer.current;
615
+ let held = null;
616
+ try {
617
+ held = await store.readAudienceFact(current.agentSessionId);
618
+ }
619
+ catch (error) {
620
+ factNote = `This computer holds a sharing record for this session that could not be read: ${error instanceof Error ? error.message : String(error)}. It was not used to order this answer.`;
621
+ }
622
+ if (held !== null && !audienceFactSameRun(held, current))
623
+ audienceOtherRun = held;
624
+ else if (held !== null && audienceFactIsOlder(current, held))
625
+ audienceStale = held;
626
+ // An answer this computer already knows to be out of date must never become
627
+ // the newest thing it knows.
628
+ if (audienceStale === undefined)
629
+ await rememberAudience({ kind: 'reading', agentSessionId: current.agentSessionId, sessionIncarnation: current.sessionIncarnation, processEpoch: current.processEpoch, state: current.state, generation: current.generation });
630
+ audienceState = answer;
631
+ failure = undefined;
632
+ refusal = undefined;
633
+ await paintAudienceState();
634
+ });
635
+ /** Replay an operation the durable record already owns. It is never re-reserved and never re-identified. */
636
+ const resend = (op) => run(op.kind === 'create' ? 'Resending the same grant request' : op.kind === 'revoke' ? 'Resending the same revocation' : op.action === 'publish' ? 'Resending the same request to share' : 'Resending the same request to stop sharing', async () => {
637
+ failureSubject = op.kind === 'audience' ? 'audience' : 'grant';
638
+ if (op.kind === 'audience') {
639
+ audienceTarget = audienceTargetFor(op.agentSessionId);
640
+ try {
641
+ await settleWith(op, () => api.setAudience({ agentSessionId: op.agentSessionId, action: op.action, replay: true }, op.operationId, abort.signal), acceptAudience);
642
+ }
643
+ catch (error) {
644
+ // Record the one thing this refusal does establish: whether this request can
645
+ // still be sent at all. The outcome of the operation stays unknown.
646
+ if (error instanceof OwnerGrantError && !audienceRefusalCanResend(error.details?.reason))
647
+ unresendable.add(op.operationId);
648
+ throw error;
649
+ }
650
+ return;
651
+ }
652
+ if (op.kind === 'create') {
653
+ await settleWith(op, () => api.create({ agentSessionId: op.agentSessionId, subjectPrincipalId: op.subjectPrincipalId, expectedMembershipRevision: op.expectedMembershipRevision, expiresAtMs: op.expiresAtMs }, op.operationId, abort.signal), async (value) => { grant = value; await reconcileReplay(op.operationId); });
654
+ return;
655
+ }
656
+ await settleWith(op, () => api.revoke({ grant_id: op.grantId, agent_session_id: op.agentSessionId, subject_principal_id: op.subjectPrincipalId, revision: op.expectedRevision }, op.operationId, abort.signal), acceptGrant);
657
+ });
658
+ const inspect = (reference, settling) => run('Inspecting the grant', async () => {
659
+ failureSubject = 'grant';
660
+ grant = await api.inspect(grant?.grant_id === reference.grant_id ? grant : reference, abort.signal);
661
+ // A trustworthy receipt answers the question the record existed to ask,
662
+ // whichever way it went: the revocation either applied or never did.
663
+ if (settling !== undefined)
664
+ await forget(settling);
665
+ failure = undefined;
666
+ refusal = undefined;
667
+ replayed = undefined;
668
+ await paintGrant();
669
+ });
670
+ /** One unresolved operation blocks every NEW change of the SAME authority, so it can never be silently replaced. */
671
+ const blocked = (action, authority) => {
672
+ const blocking = pendingIn(authority);
673
+ if (!blocking.length)
674
+ return false;
675
+ refusal = `Cannot ${action} while an earlier change has an unknown outcome. Resend it, inspect it, or forget its local record first.`;
676
+ cursor = Math.max(0, unresolved.indexOf(blocking[0]));
677
+ void paintUnresolved();
678
+ return true;
679
+ };
680
+ const toUnresolved = (key) => { if (key !== 'p' || !unresolved.length)
681
+ return false; refusal = undefined; cursor = 0; void paintUnresolved(); return true; };
682
+ const remove = host.onInput(bytes => {
683
+ if (stopped)
684
+ return;
685
+ const key = new TextDecoder().decode(bytes);
686
+ if (key === '\x03' || key === '\x1b') {
687
+ stop();
688
+ return;
689
+ }
690
+ if (busy)
691
+ return;
692
+ const up = () => { index = Math.max(0, index - 1); }, down = (n) => { index = Math.min(Math.max(0, n - 1), index + 1); };
693
+ if (phase === 'sessions') {
694
+ if (key === '\x1b[A') {
695
+ up();
696
+ void paintSessions();
697
+ }
698
+ else if (key === '\x1b[B') {
699
+ down(items.length);
700
+ void paintSessions();
701
+ }
702
+ else if (key === '\r' && items[index]) {
703
+ session = items[index];
704
+ void loadMembers(null);
705
+ }
706
+ // Publication is asked for on the session the cursor names, and confirmed
707
+ // before anything is sent.
708
+ else if (key === 's' && items[index]) {
709
+ if (blocked('start live sharing', 'audience'))
710
+ return;
711
+ audienceFrom = 'sessions';
712
+ audienceTarget = audienceTargetFor(items[index].id);
713
+ void paintConfirmShare();
714
+ }
715
+ else if (key === 'e' && items[index]) {
716
+ audienceFrom = 'sessions';
717
+ audienceTarget = audienceTargetFor(items[index].id);
718
+ void paintConfirmStop();
719
+ }
720
+ // Asking is never gated: it changes nothing, and an owner who cannot tell
721
+ // whether a session is shared is the one who most needs to be able to ask.
722
+ else if (key === 'c' && items[index]) {
723
+ audienceFrom = 'sessions';
724
+ void askAudience(audienceTargetFor(items[index].id));
725
+ }
726
+ else if (key === 'r')
727
+ void loadSessions();
728
+ else
729
+ toUnresolved(key);
730
+ }
731
+ else if (phase === 'members') {
732
+ if (key === '\x1b[A') {
733
+ up();
734
+ void paintMembers();
735
+ }
736
+ else if (key === '\x1b[B') {
737
+ down(members.length);
738
+ void paintMembers();
739
+ }
740
+ else if (key === '\r' && members[index]) {
741
+ member = members[index];
742
+ void paintDuration();
743
+ }
744
+ else if (key === 'n' && membersNext)
745
+ void loadMembers(membersNext);
746
+ else if (key === 'r')
747
+ void loadMembers(membersAfter);
748
+ else if (key === 'b')
749
+ void paintSessions();
750
+ else
751
+ toUnresolved(key);
752
+ }
753
+ else if (phase === 'duration') {
754
+ const choice = DURATIONS.find(([k]) => k === key);
755
+ if (choice) {
756
+ if (blocked('create a grant', 'grant'))
757
+ return;
758
+ void create({ agentSessionId: session.id, subjectPrincipalId: member.principal_id, expectedMembershipRevision: member.membership_revision, expiresAtMs: now() + choice[2] });
759
+ }
760
+ else if (key === 'b')
761
+ void paintMembers();
762
+ else
763
+ toUnresolved(key);
764
+ }
765
+ else if (phase === 'grant') {
766
+ if (key === 'i' && grant)
767
+ void inspect(grant);
768
+ // A stored replay is not a reading of current authority, so it may not be
769
+ // the revision a revocation is aimed at. `i` first.
770
+ else if (key === 'x' && grant && !replayed && grant.state === 'active' && !describeGrantState(grant, now()).final) {
771
+ if (blocked('revoke', 'grant'))
772
+ return;
773
+ void revoke(grant);
774
+ }
775
+ // The grant names its session and the exact run of it, so a sharing receipt
776
+ // about a different run can be refused rather than believed.
777
+ else if (key === 's' && grant) {
778
+ if (blocked('start live sharing', 'audience'))
779
+ return;
780
+ audienceFrom = 'grant';
781
+ audienceTarget = audienceTargetFor(grant.agent_session_id, grant.session_incarnation);
782
+ void paintConfirmShare();
783
+ }
784
+ else if (key === 'e' && grant) {
785
+ audienceFrom = 'grant';
786
+ audienceTarget = audienceTargetFor(grant.agent_session_id, grant.session_incarnation);
787
+ void paintConfirmStop();
788
+ }
789
+ else if (key === 'c' && grant) {
790
+ audienceFrom = 'grant';
791
+ void askAudience(audienceTargetFor(grant.agent_session_id, grant.session_incarnation));
792
+ }
793
+ else if (key === 'b') {
794
+ grant = undefined;
795
+ failure = undefined;
796
+ replayed = undefined;
797
+ void loadSessions();
798
+ }
799
+ else
800
+ toUnresolved(key);
801
+ }
802
+ else if (phase === 'confirm-share' || phase === 'confirm-stop') {
803
+ const action = phase === 'confirm-share' ? 'publish' : 'private', target = audienceTarget;
804
+ if (key === 'y')
805
+ void changeAudience(action, target);
806
+ // Cancelling returns to the screen the decision was reached from, so the
807
+ // receipt a person was reading is not lost by declining the next step.
808
+ else if (audienceFrom === 'shared' && audience)
809
+ void paintAudience();
810
+ else if (audienceFrom === 'state' && audienceState)
811
+ void paintAudienceState();
812
+ else {
813
+ audienceTarget = undefined;
814
+ void (audienceFrom === 'grant' && grant ? paintGrant() : items.length ? paintSessions() : loadSessions());
815
+ }
816
+ }
817
+ else if (phase === 'shared') {
818
+ if (key === 's' && audienceTarget) {
819
+ if (blocked('start live sharing', 'audience'))
820
+ return;
821
+ audienceFrom = 'shared';
822
+ void paintConfirmShare();
823
+ }
824
+ else if (key === 'e' && audienceTarget) {
825
+ audienceFrom = 'shared';
826
+ void paintConfirmStop();
827
+ }
828
+ else if (key === 'c' && audienceTarget) {
829
+ audienceFrom = 'shared';
830
+ void askAudience(audienceTarget);
831
+ }
832
+ else if (key === 'b') {
833
+ audience = undefined;
834
+ audienceTarget = undefined;
835
+ void (items.length ? paintSessions() : loadSessions());
836
+ }
837
+ else
838
+ toUnresolved(key);
839
+ }
840
+ // What the session itself answered. A fresh decision from here is issued
841
+ // against the generation this reading recorded, which is what makes starting
842
+ // or stopping possible again after an acknowledgement was lost.
843
+ else if (phase === 'state') {
844
+ if (key === 's' && audienceTarget) {
845
+ if (blocked('start live sharing', 'audience'))
846
+ return;
847
+ audienceFrom = 'state';
848
+ void paintConfirmShare();
849
+ }
850
+ else if (key === 'e' && audienceTarget) {
851
+ audienceFrom = 'state';
852
+ void paintConfirmStop();
853
+ }
854
+ else if (key === 'c' && audienceTarget) {
855
+ audienceFrom = 'state';
856
+ void askAudience(audienceTarget);
857
+ }
858
+ else if (key === 'b') {
859
+ audienceState = undefined;
860
+ audienceAsked = undefined;
861
+ audienceTarget = undefined;
862
+ void (items.length ? paintSessions() : loadSessions());
863
+ }
864
+ else
865
+ toUnresolved(key);
866
+ }
867
+ else if (phase === 'failure') {
868
+ if (key === 'r' && failure && rereadsMembers(failure))
869
+ void loadMembers(null);
870
+ else if (key === 'i' && grant)
871
+ void inspect(grant);
872
+ else if (key === 'b') {
873
+ failure = undefined;
874
+ grant = undefined;
875
+ replayed = undefined;
876
+ void loadSessions();
877
+ }
878
+ else
879
+ toUnresolved(key);
880
+ }
881
+ else if (phase === 'unresolved') {
882
+ const row = unresolved[cursor];
883
+ if (key === '\x1b[A') {
884
+ cursor = Math.max(0, cursor - 1);
885
+ void paintUnresolved();
886
+ }
887
+ else if (key === '\x1b[B') {
888
+ cursor = Math.min(Math.max(0, unresolved.length - 1), cursor + 1);
889
+ void paintUnresolved();
890
+ }
891
+ else if (key === 'r' && row && !unresendable.has(row.operationId)) {
892
+ refusal = undefined;
893
+ void resend(row);
894
+ }
895
+ else if (key === 'i' && row?.kind === 'revoke') {
896
+ refusal = undefined;
897
+ void inspect({ grant_id: row.grantId }, row.operationId);
898
+ }
899
+ // The safe direction out of an unconfirmed publication, reachable without
900
+ // first settling it: it removes access and starts its own operation.
901
+ else if (key === 'e' && row?.kind === 'audience' && row.action === 'publish') {
902
+ refusal = undefined;
903
+ audienceFrom = 'sessions';
904
+ audienceTarget = audienceTargetFor(row.agentSessionId);
905
+ void paintConfirmStop();
906
+ }
907
+ // The question this record exists around, asked about this exact operation.
908
+ // It is the one action here that can settle the record without a resend.
909
+ else if (key === 'c' && row?.kind === 'audience') {
910
+ refusal = undefined;
911
+ audienceFrom = 'state';
912
+ void askAudience(audienceTargetFor(row.agentSessionId), row);
913
+ }
914
+ else if (key === 'd' && row) {
915
+ forgetTarget = row;
916
+ void paintConfirmForget();
917
+ }
918
+ else if (key === 'b') {
919
+ refusal = undefined;
920
+ void (items.length ? paintSessions() : loadSessions());
921
+ }
922
+ }
923
+ else if (phase === 'confirm-forget') {
924
+ const op = forgetTarget;
925
+ if (key === 'y')
926
+ void run('Forgetting the local record', async () => {
927
+ await forget(op.operationId);
928
+ forgetTarget = undefined;
929
+ refusal = undefined;
930
+ failure = undefined;
931
+ if (unresolved.some(o => o.operationId === op.operationId))
932
+ await paintUnresolved();
933
+ else {
934
+ forgotten = op;
935
+ await paintForgotten();
936
+ }
937
+ });
938
+ else {
939
+ forgetTarget = undefined;
940
+ void paintUnresolved();
941
+ }
942
+ }
943
+ else if (phase === 'forgotten') {
944
+ if (key === 'b') {
945
+ forgotten = undefined;
946
+ void (items.length ? paintSessions() : loadSessions());
947
+ }
948
+ else
949
+ toUnresolved(key);
950
+ }
951
+ });
952
+ options.signal?.addEventListener('abort', stop, { once: true });
953
+ try {
954
+ if (options.signal?.aborted)
955
+ stop();
956
+ else
957
+ await run('Reading local state', async () => {
958
+ // Recover operation identity from an earlier process before anything else.
959
+ unresolved = [...await localState('read its record of unresolved changes', () => store.list(abort.signal))];
960
+ await readSessions();
961
+ // `--grant` names one explicit subject the owner came here for, and reaching
962
+ // it is how a grant is revoked. An unresolved SHARING change must not hide
963
+ // it: they are separate authorities, the record outlives every retry, and
964
+ // revoking only ever removes access. Recovery is not lost either -- the
965
+ // banner on that screen names it and `p` opens it.
966
+ if (options.initialGrantId) {
967
+ grant = await api.inspect({ grant_id: options.initialGrantId }, abort.signal);
968
+ await paintGrant();
969
+ }
970
+ else if (unresolved.length) {
971
+ cursor = 0;
972
+ await paintUnresolved();
973
+ }
974
+ else
975
+ await paintSessions();
976
+ });
977
+ await done;
978
+ }
979
+ finally {
980
+ stop();
981
+ remove();
982
+ options.signal?.removeEventListener('abort', stop);
983
+ await hostWrites;
984
+ await lease.restore();
985
+ }
986
+ }
987
+ //# sourceMappingURL=owner-grants-screen.js.map