@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,902 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import { EXIT_CODES, CunaError } from "../core/errors.js";
3
+ import { automationCredentialHint } from "../core/product-web.js";
4
+ import { isAccessToken, isLoginCode } from "../core/namespace.js";
5
+ import { CredentialBoundaryError } from "../credentials/errors.js";
6
+ import { SecretMaterial } from "../credentials/secret-material.js";
7
+ import { CredentialVault } from "../credentials/vault.js";
8
+ import { handOffContinuationToBrowser } from "./browser-handoff.js";
9
+ import { assertProfile, assertUuid, decodeCliIdentityContext, } from "./human-contracts.js";
10
+ import { createPkceAuthorization } from "./pkce.js";
11
+ /**
12
+ * The automation-only hint used when a command has no admitted interactive
13
+ * session. Browser-link login uses the encrypted local session store; this
14
+ * message must not imply that an API key is a login fallback.
15
+ *
16
+ * `CUNA_API_KEY` never touches the encrypted local session store:
17
+ * `cli/run.ts` selects automation mode from it and hands the key straight to
18
+ * the HTTP transport. Measured on this host — the vault reports an unverified
19
+ * backend, so an explicit automation credential remains separate from the
20
+ * browser-link preview session and never changes the user's login mode.
21
+ *
22
+ * The text promises COMMANDS, not sign-in, deliberately: `login`, `logout`,
23
+ * `whoami`, `signup` and `access` reject with `cuna.auth.mode_conflict` while
24
+ * `CUNA_API_KEY` is set, so a hint promising that this makes sign-in work would
25
+ * be the same class of lie in the other direction.
26
+ *
27
+ * This lives in one place because it is one fact. Copying the sentence to each
28
+ * mint site is how the two spellings of a namespace drift apart.
29
+ */
30
+ const AUTOMATION_CREDENTIAL_HINT = `Sign-in is unavailable here, so use an automation credential instead. ${automationCredentialHint()}`;
31
+ function authError(code, message, options = {}) {
32
+ return new CunaError({ code, message, exitCode: EXIT_CODES.auth, ...options });
33
+ }
34
+ /**
35
+ * The service advertised a sign-in completion mode this build cannot drive.
36
+ *
37
+ * Reached only through the exhaustive switch in `login`, so the parameter is
38
+ * `never`: adding a mode to the decoded bootstrap without teaching `login` how
39
+ * to complete it fails the build here rather than silently falling through to
40
+ * the paste path. Refusing loudly is the point — a CLI that quietly runs the
41
+ * wrong completion tells the user to paste a code the service is not going to
42
+ * show them.
43
+ */
44
+ function unsupportedCompletionMode(mode) {
45
+ return authError("cuna.auth.completion_mode_unsupported", "Cuna advertised a sign-in completion mode this CLI version does not support.", {
46
+ hint: "Update the Cuna CLI to the latest version, then run `cuna login` again.",
47
+ details: { completion_mode: String(mode) },
48
+ });
49
+ }
50
+ function binding(config) {
51
+ return Object.freeze({
52
+ profileId: config.profile,
53
+ accountId: config.baseUrl,
54
+ workspaceId: "cli-human-auth",
55
+ kind: "login-code-session-v1",
56
+ });
57
+ }
58
+ function contextWire(context) {
59
+ return {
60
+ required_terms_version: context.requiredTermsVersion,
61
+ identity: context.identity,
62
+ admission: context.admission,
63
+ workspace: { state: context.workspace.state, ...(context.workspace.id === undefined ? {} : { id: context.workspace.id }) },
64
+ ...(context.waitlistPosition === undefined ? {} : { waitlist_position: context.waitlistPosition }),
65
+ };
66
+ }
67
+ function encodeStored(session) {
68
+ return SecretMaterial.fromUtf8(JSON.stringify({
69
+ version: session.version,
70
+ base_url: session.baseUrl,
71
+ profile: session.profile,
72
+ client_instance_id: session.clientInstanceId,
73
+ session_id: session.sessionId,
74
+ login_code: session.loginCode,
75
+ login_code_expires_at: session.loginCodeExpiresAt,
76
+ continuation_id: session.continuationId,
77
+ state: session.state,
78
+ code_verifier: session.codeVerifier,
79
+ redirect_uri: session.redirectUri,
80
+ context: contextWire(session.context),
81
+ intent_class: session.intentClass,
82
+ ...(session.access === undefined ? {} : {
83
+ access_token: session.access.token,
84
+ access_expires_at: session.access.expiresAt,
85
+ access_observed_at: session.access.observedAt,
86
+ }),
87
+ }));
88
+ }
89
+ /**
90
+ * How much life a stored bearer must have left before it is reused instead of
91
+ * re-exchanged. 90 s is the longest single request this bearer authorizes
92
+ * (`MACHINE_CREATE_REQUEST_BUDGET_MS`), plus 30 s of headroom, so a request
93
+ * admitted on a reused token cannot outlive it. At this margin a continuously
94
+ * used CLI exchanges at most ~7.5 times an hour against a 600/hour ceiling.
95
+ */
96
+ const ACCESS_REUSE_MARGIN_MS = 120_000;
97
+ function usableStoredAccess(session, nowMs, rejectedToken) {
98
+ const access = session.access;
99
+ if (access === undefined)
100
+ return undefined;
101
+ // A bearer the server just refused must never be handed back from disk, or a
102
+ // 401 becomes an infinite loop against a dead token.
103
+ if (rejectedToken !== undefined && access.token === rejectedToken)
104
+ return undefined;
105
+ const expiresAt = Date.parse(access.expiresAt);
106
+ if (!Number.isSafeInteger(expiresAt) || expiresAt - nowMs <= ACCESS_REUSE_MARGIN_MS)
107
+ return undefined;
108
+ return access;
109
+ }
110
+ function decodeStored(bytes, config) {
111
+ let parsed;
112
+ try {
113
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
114
+ }
115
+ catch {
116
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna sign-in session is malformed.");
117
+ }
118
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
119
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna sign-in session is malformed.");
120
+ }
121
+ const record = parsed;
122
+ const expected = [
123
+ "base_url", "client_instance_id", "code_verifier", "context", "continuation_id", "intent_class", "login_code",
124
+ "login_code_expires_at", "profile", "redirect_uri", "session_id", "state", "version",
125
+ ];
126
+ // Two shapes are legal, and only two: the original key set, and that set
127
+ // plus the cached-bearer triple. Accepting the original one is what keeps an
128
+ // already-stored session readable instead of forcing a fresh `cuna login`;
129
+ // demanding all three of the triple together is what stops a half-written
130
+ // record from being read as "no bearer" and silently losing the guard.
131
+ const accessKeys = ["access_expires_at", "access_observed_at", "access_token"];
132
+ const withAccess = [...expected, ...accessKeys].sort();
133
+ const keys = Object.keys(record).sort();
134
+ const matches = (shape) => keys.length === shape.length && keys.every((key, index) => key === shape[index]);
135
+ const carriesAccess = matches(withAccess);
136
+ if (!carriesAccess && !matches(expected)) {
137
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna sign-in session has an invalid shape.");
138
+ }
139
+ if (carriesAccess && (typeof record.access_token !== "string" || !isAccessToken(record.access_token) ||
140
+ typeof record.access_expires_at !== "string" ||
141
+ Number.isNaN(Date.parse(record.access_expires_at)) ||
142
+ new Date(record.access_expires_at).toISOString() !== record.access_expires_at ||
143
+ typeof record.access_observed_at !== "string" ||
144
+ Number.isNaN(Date.parse(record.access_observed_at)) ||
145
+ new Date(record.access_observed_at).toISOString() !== record.access_observed_at)) {
146
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna sign-in session failed validation.");
147
+ }
148
+ if (record.version !== 3 ||
149
+ record.base_url !== config.baseUrl || record.profile !== config.profile ||
150
+ typeof record.login_code !== "string" || !isLoginCode(record.login_code) ||
151
+ typeof record.login_code_expires_at !== "string" || Number.isNaN(Date.parse(record.login_code_expires_at)) ||
152
+ new Date(record.login_code_expires_at).toISOString() !== record.login_code_expires_at ||
153
+ typeof record.client_instance_id !== "string" || typeof record.session_id !== "string" ||
154
+ typeof record.continuation_id !== "string" || typeof record.state !== "string" ||
155
+ typeof record.code_verifier !== "string" || typeof record.redirect_uri !== "string" ||
156
+ (!new Set([
157
+ "signup", "login", "account.read", "machines.read", "machines.create",
158
+ "agent_sessions.read", "agent_sessions.create",
159
+ ]).has(record.intent_class))) {
160
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna sign-in session failed validation.");
161
+ }
162
+ return Object.freeze({
163
+ version: 3,
164
+ ...(carriesAccess ? {
165
+ access: Object.freeze({
166
+ token: record.access_token,
167
+ expiresAt: record.access_expires_at,
168
+ observedAt: record.access_observed_at,
169
+ }),
170
+ } : {}),
171
+ baseUrl: config.baseUrl,
172
+ profile: assertProfile(config.profile),
173
+ clientInstanceId: assertUuid(record.client_instance_id, "client instance ID"),
174
+ sessionId: assertUuid(record.session_id, "session ID"),
175
+ loginCode: record.login_code,
176
+ loginCodeExpiresAt: record.login_code_expires_at,
177
+ continuationId: assertUuid(record.continuation_id, "continuation ID"),
178
+ state: record.state,
179
+ codeVerifier: record.code_verifier,
180
+ redirectUri: record.redirect_uri,
181
+ context: decodeCliIdentityContext(record.context),
182
+ intentClass: record.intent_class,
183
+ });
184
+ }
185
+ function storedFromExchange(config, clientInstanceId, loginCode, exchange, intentClass, continuation) {
186
+ return Object.freeze({
187
+ version: 3,
188
+ // The login exchange already produced a bearer. Keeping it means the first
189
+ // command after `cuna login` reuses it rather than immediately spending a
190
+ // second exchange out of the ten-per-minute budget.
191
+ access: Object.freeze({
192
+ token: exchange.accessToken,
193
+ expiresAt: exchange.accessExpiresAt,
194
+ observedAt: new Date().toISOString(),
195
+ }),
196
+ baseUrl: config.baseUrl,
197
+ profile: config.profile,
198
+ clientInstanceId,
199
+ sessionId: exchange.sessionId,
200
+ loginCode,
201
+ loginCodeExpiresAt: exchange.loginCodeExpiresAt,
202
+ continuationId: continuation.id,
203
+ state: continuation.state,
204
+ codeVerifier: continuation.codeVerifier,
205
+ redirectUri: continuation.redirectUri,
206
+ context: exchange.context,
207
+ intentClass,
208
+ });
209
+ }
210
+ function isAuthoritativeReexchangeRejection(error) {
211
+ if (!(error instanceof CunaError))
212
+ return false;
213
+ const status = error.details?.http_status;
214
+ const reason = error.details?.reason;
215
+ // A generic 4xx is not proof that this exact login-code family was revoked:
216
+ // the current OpenAPI `Problem` response is intentionally extensible. Only
217
+ // these server-defined terminal pairs may remove the durable code.
218
+ return (status === 401 &&
219
+ (reason === "cli_auth_rejected" || reason === "cli_session_revoked"));
220
+ }
221
+ /**
222
+ * A second logout is allowed to finish local cleanup only after the server has
223
+ * made an irreversible fact visible. In particular, a lost response and a
224
+ * later 401 are not equivalent: the former is indeterminate and preserves the
225
+ * encrypted login code; the latter proves the family cannot be used again.
226
+ */
227
+ function isDefinitiveLogoutTermination(error) {
228
+ if (!(error instanceof CunaError))
229
+ return false;
230
+ const reason = error.details?.reason;
231
+ return (
232
+ // Re-exchange emits this marker only after CredentialVault used a
233
+ // revision-fenced deletion (or observed the exact old record absent).
234
+ error.code === "cuna.auth.reauthentication_required" && reason === "durable_session_removed") ||
235
+ isAuthoritativeReexchangeRejection(error);
236
+ }
237
+ function ensureOnboardingReady(context) {
238
+ if (context.identity === "active" && context.admission === "admitted" && context.workspace.state === "assigned")
239
+ return;
240
+ throw authError("cuna.auth.onboarding_incomplete", "Cuna sign-in completed, but the account is not ready for CLI work.", { hint: "Finish identity verification, admission, terms, and workspace assignment in the browser." });
241
+ }
242
+ function ensureSignupContext(context, allowAdmitted) {
243
+ const waitlisted = context.identity === "active" &&
244
+ context.admission === "waitlisted" &&
245
+ context.workspace.state === "unavailable";
246
+ const admitted = allowAdmitted &&
247
+ context.identity === "active" &&
248
+ context.admission === "admitted" &&
249
+ context.workspace.state === "assigned" &&
250
+ context.workspace.id !== undefined;
251
+ if (waitlisted || admitted)
252
+ return;
253
+ throw authError("cuna.auth.signup_state_invalid", "Cuna could not prove a valid waitlist-only signup state.");
254
+ }
255
+ function signupContextTransitionAllowed(previous, next) {
256
+ if (previous.requiredTermsVersion !== next.requiredTermsVersion ||
257
+ previous.identity !== "active" ||
258
+ next.identity !== "active") {
259
+ return false;
260
+ }
261
+ if (previous.admission === "waitlisted") {
262
+ return (next.admission === "waitlisted" &&
263
+ previous.workspace.state === "unavailable" &&
264
+ next.workspace.state === "unavailable") || (next.admission === "admitted" &&
265
+ previous.workspace.state === "unavailable" &&
266
+ next.workspace.state === "assigned" &&
267
+ next.workspace.id !== undefined);
268
+ }
269
+ return previous.admission === "admitted" &&
270
+ next.admission === "admitted" &&
271
+ previous.workspace.state === "assigned" &&
272
+ next.workspace.state === "assigned" &&
273
+ previous.workspace.id !== undefined &&
274
+ previous.workspace.id === next.workspace.id;
275
+ }
276
+ function randomLoopbackUri(random) {
277
+ const bytes = random(2);
278
+ if (bytes.byteLength !== 2)
279
+ throw new Error("Invalid cryptographic random source.");
280
+ const port = 49152 + (((bytes[0] ?? 0) << 8 | (bytes[1] ?? 0)) % 16384);
281
+ return `http://127.0.0.1:${port}/callback`;
282
+ }
283
+ export function createHumanAuthService(input) {
284
+ const clock = input.clock ?? Date.now;
285
+ const random = input.random ?? randomBytes;
286
+ const credentialBinding = binding(input.config);
287
+ let lastObservedNow;
288
+ // Profile and session ID are non-secret identity metadata decoded from the
289
+ // same encrypted record that authorizes a re-exchange. Keeping that validated
290
+ // metadata with the in-memory access token avoids a second durable-store
291
+ // read merely to render `whoami`.
292
+ let access;
293
+ let reexchangeFlight;
294
+ function now() {
295
+ const observed = clock();
296
+ if (!Number.isSafeInteger(observed) || observed < 0 || (lastObservedNow !== undefined && observed < lastObservedNow)) {
297
+ throw authError("cuna.auth.clock_untrusted", "The local authentication clock is not trustworthy.");
298
+ }
299
+ lastObservedNow = observed;
300
+ return observed;
301
+ }
302
+ function cacheAccessToken(token, expiresAt, session, revision) {
303
+ if (!Number.isSafeInteger(expiresAt) || expiresAt - now() <= 30_000 || !Number.isSafeInteger(revision) || revision < 1)
304
+ return false;
305
+ const material = SecretMaterial.fromUtf8(token);
306
+ access?.material.dispose();
307
+ access = { material, expiresAt, profile: session.profile, sessionId: session.sessionId, revision };
308
+ return true;
309
+ }
310
+ function cachedAccessToken() {
311
+ if (access === undefined || access.expiresAt - now() <= 30_000) {
312
+ throw authError("cuna.auth.reexchange_unknown", "Cuna could not establish a fresh in-memory access token.", { retryable: true });
313
+ }
314
+ return access.material.withBytes((bytes) => new TextDecoder("utf-8", { fatal: true }).decode(bytes));
315
+ }
316
+ function assertNotCancelled(signal) {
317
+ if (signal?.aborted === true) {
318
+ throw authError("cuna.auth.cancelled", "Cuna sign-in was cancelled.");
319
+ }
320
+ }
321
+ async function readLoginCodeBeforeExpiry(expiresAt, signal) {
322
+ const deadline = Date.parse(expiresAt);
323
+ const remaining = deadline - now();
324
+ if (remaining <= 0) {
325
+ throw authError("cuna.auth.continuation_expired", "Cuna sign-in expired before the browser code could be entered.");
326
+ }
327
+ const controller = new AbortController();
328
+ let expired = false;
329
+ let cancelled = false;
330
+ let timer;
331
+ let removeCancellation;
332
+ const deadlineReached = new Promise((resolve) => {
333
+ timer = setTimeout(() => {
334
+ expired = true;
335
+ controller.abort();
336
+ resolve(Object.freeze({ kind: "expired" }));
337
+ }, remaining);
338
+ });
339
+ const callerCancelled = signal === undefined
340
+ ? new Promise(() => { })
341
+ : new Promise((resolve) => {
342
+ const cancel = () => {
343
+ cancelled = true;
344
+ controller.abort();
345
+ resolve(Object.freeze({ kind: "cancelled" }));
346
+ };
347
+ removeCancellation = () => signal.removeEventListener("abort", cancel);
348
+ signal.addEventListener("abort", cancel, { once: true });
349
+ // AbortSignal does not replay an event delivered just before listener
350
+ // registration. Recheck the state so cancellation cannot leave the
351
+ // hidden reader blocked until the continuation deadline.
352
+ if (signal.aborted)
353
+ cancel();
354
+ });
355
+ try {
356
+ // Do not start a reader after the post-registration recheck found the
357
+ // caller cancelled. If cancellation arrives immediately afterwards, the
358
+ // registered handler aborts this controller and wins the race below.
359
+ const codeRead = cancelled
360
+ ? new Promise(() => { })
361
+ : input.readLoginCode(controller.signal).then((loginCode) => Object.freeze({ kind: "login_code", loginCode }));
362
+ const outcome = await Promise.race([
363
+ codeRead,
364
+ deadlineReached,
365
+ callerCancelled,
366
+ ]);
367
+ if (outcome.kind === "expired") {
368
+ throw authError("cuna.auth.continuation_expired", "Cuna sign-in expired before the browser code could be entered.");
369
+ }
370
+ if (outcome.kind === "cancelled") {
371
+ throw authError("cuna.auth.cancelled", "Cuna sign-in was cancelled.");
372
+ }
373
+ const loginCode = outcome.loginCode;
374
+ if (expired || Date.parse(expiresAt) <= now()) {
375
+ throw authError("cuna.auth.continuation_expired", "Cuna sign-in expired before the browser code could be entered.");
376
+ }
377
+ assertNotCancelled(signal);
378
+ return loginCode;
379
+ }
380
+ catch (error) {
381
+ if (expired) {
382
+ throw authError("cuna.auth.continuation_expired", "Cuna sign-in expired before the browser code could be entered.");
383
+ }
384
+ if (signal?.aborted === true)
385
+ throw authError("cuna.auth.cancelled", "Cuna sign-in was cancelled.");
386
+ throw error;
387
+ }
388
+ finally {
389
+ if (timer !== undefined)
390
+ clearTimeout(timer);
391
+ removeCancellation?.();
392
+ }
393
+ }
394
+ async function waitForReexchange(flight, signal) {
395
+ assertNotCancelled(signal);
396
+ if (signal === undefined)
397
+ return await flight;
398
+ return await new Promise((resolve, reject) => {
399
+ const abort = () => {
400
+ signal.removeEventListener("abort", abort);
401
+ reject(authError("cuna.auth.cancelled", "Cuna sign-in was cancelled."));
402
+ };
403
+ signal.addEventListener("abort", abort, { once: true });
404
+ void flight.then(() => {
405
+ signal.removeEventListener("abort", abort);
406
+ resolve();
407
+ }, (error) => {
408
+ signal.removeEventListener("abort", abort);
409
+ reject(error);
410
+ });
411
+ });
412
+ }
413
+ async function login(request = {}) {
414
+ const intentClass = request.intentClass ?? "login";
415
+ assertNotCancelled(request.signal);
416
+ const vaultStatus = await input.vault.status(credentialBinding);
417
+ if (vaultStatus.backendStatus !== "verified") {
418
+ throw authError("cuna.auth.session_store_unavailable", "The encrypted local session store is unavailable or cannot be verified.", {
419
+ hint: "Run `cuna doctor` to verify encrypted local session storage, then retry `cuna login`.",
420
+ });
421
+ }
422
+ if (vaultStatus.state === "present") {
423
+ throw authError("cuna.auth.already_signed_in", "This Cuna profile already has an interactive session.", { hint: "Run `cuna logout` before signing in again." });
424
+ }
425
+ if (vaultStatus.state === "corrupt") {
426
+ throw authError("cuna.auth.session_corrupt", "The protected Cuna session is corrupt and cannot be replaced implicitly.", { hint: "Remove the damaged encrypted local session files, then run `cuna login` again." });
427
+ }
428
+ const bootstrap = await input.client.bootstrap(request.signal);
429
+ if (!bootstrap.enabled || bootstrap.browserOrigin === null) {
430
+ throw authError("cuna.auth.unavailable", "Interactive Cuna sign-in is not enabled for this environment.", { hint: AUTOMATION_CREDENTIAL_HINT });
431
+ }
432
+ if (intentClass === "signup") {
433
+ const signup = await input.client.signupCapability(request.signal);
434
+ if (!signup.enabled || signup.enrollment !== "waitlist_only") {
435
+ throw authError("cuna.auth.signup_unavailable", "Waitlist-only self-service signup is not enabled for this environment.");
436
+ }
437
+ }
438
+ const pkce = createPkceAuthorization(random);
439
+ const clientInstanceId = assertUuid((input.uuid ?? randomUUID)(), "client instance ID");
440
+ const redirectUri = randomLoopbackUri(random);
441
+ const issued = await input.client.createContinuation({
442
+ state: pkce.state,
443
+ codeChallenge: pkce.challenge,
444
+ redirectUri,
445
+ clientInstanceId,
446
+ profile: input.config.profile,
447
+ intentClass,
448
+ browserOrigin: bootstrap.browserOrigin,
449
+ ...(request.signal === undefined ? {} : { signal: request.signal }),
450
+ });
451
+ // Terminal cancellation is local. Browser cancellation is a separately
452
+ // authenticated cuna_cb_ callback owned by the approved browser flow;
453
+ // this CLI never has a continuation secret or a cancel endpoint.
454
+ {
455
+ if (Date.parse(issued.expiresAt) <= now()) {
456
+ throw authError("cuna.auth.continuation_expired", "Cuna issued an already-expired sign-in continuation.");
457
+ }
458
+ // How this sign-in completes is the service's decision, read from the
459
+ // bootstrap it just served — never assumed here. The switch is
460
+ // exhaustive on purpose: widening `CliAuthBootstrap["completionMode"]`
461
+ // when the service starts advertising a second mode is a compile error
462
+ // in this function, which is the only control that makes "the CLI
463
+ // hard-coded a mode" unexpressible rather than merely discouraged.
464
+ const completionMode = bootstrap.completionMode;
465
+ switch (completionMode) {
466
+ case "paste_login_code":
467
+ break;
468
+ default:
469
+ throw unsupportedCompletionMode(completionMode);
470
+ }
471
+ await handOffContinuationToBrowser({
472
+ url: issued.browserUrl,
473
+ opener: input.browser,
474
+ reporter: input.browserHandoff,
475
+ });
476
+ const loginCode = (await readLoginCodeBeforeExpiry(issued.expiresAt, request.signal)).trim();
477
+ if (!isLoginCode(loginCode)) {
478
+ throw authError("cuna.auth.login_code_invalid", "The pasted Cuna login code is invalid.", {
479
+ hint: "Copy the complete cuna_login_ code shown by app.getcuna.com and paste it once.",
480
+ });
481
+ }
482
+ input.onLoginCodeAccepted?.();
483
+ const exchange = await input.client.exchange({
484
+ id: issued.id,
485
+ clientInstanceId,
486
+ profile: input.config.profile,
487
+ state: pkce.state,
488
+ codeVerifier: pkce.verifier,
489
+ redirectUri,
490
+ loginCode,
491
+ ...(request.signal === undefined ? {} : { signal: request.signal }),
492
+ });
493
+ let persistedRevision;
494
+ try {
495
+ // The remote family now exists even if the caller interrupted the
496
+ // exchange request. Never persist or cache it after that interruption.
497
+ assertNotCancelled(request.signal);
498
+ const exchangedAt = now();
499
+ if (Date.parse(exchange.loginCodeExpiresAt) <= exchangedAt || Date.parse(exchange.accessExpiresAt) <= exchangedAt) {
500
+ throw authError("cuna.auth.token_expired", "Cuna returned an already-expired sign-in session.");
501
+ }
502
+ if (intentClass === "signup") {
503
+ ensureSignupContext(exchange.context, false);
504
+ }
505
+ else {
506
+ ensureOnboardingReady(exchange.context);
507
+ }
508
+ const stored = storedFromExchange(input.config, clientInstanceId, loginCode, exchange, intentClass, { id: issued.id, state: pkce.state, codeVerifier: pkce.verifier, redirectUri });
509
+ const material = encodeStored(stored);
510
+ try {
511
+ const rotated = await input.vault.rotate({
512
+ binding: credentialBinding,
513
+ material,
514
+ expiresAt: Date.parse(stored.loginCodeExpiresAt),
515
+ });
516
+ persistedRevision = rotated.revision;
517
+ if (persistedRevision === undefined) {
518
+ throw authError("cuna.auth.session_cleanup_failed", "Cuna could not prove the encrypted session revision required for cancellation cleanup.", { retryable: true });
519
+ }
520
+ }
521
+ finally {
522
+ material.dispose();
523
+ }
524
+ // Rotation is an await boundary. If cancellation arrived while it was
525
+ // committing, the catch below removes only this fenced revision before
526
+ // returning the cancellation to the caller.
527
+ assertNotCancelled(request.signal);
528
+ cacheAccessToken(exchange.accessToken, Date.parse(exchange.accessExpiresAt), stored, persistedRevision);
529
+ return Object.freeze({
530
+ profile: stored.profile,
531
+ sessionId: stored.sessionId,
532
+ context: stored.context,
533
+ storageMode: "encrypted-local",
534
+ });
535
+ }
536
+ catch (error) {
537
+ let localCleanupFailed = false;
538
+ if (persistedRevision !== undefined) {
539
+ try {
540
+ await input.vault.deleteIfRevision({
541
+ binding: credentialBinding,
542
+ expectedRevision: persistedRevision,
543
+ });
544
+ }
545
+ catch {
546
+ // Do not expose a backend cause: it may include protected-path or
547
+ // provider diagnostics. Still revoke the remote family below.
548
+ localCleanupFailed = true;
549
+ }
550
+ }
551
+ try {
552
+ await input.client.logout(exchange.accessToken);
553
+ }
554
+ catch { /* family remains server-expiring */ }
555
+ if (localCleanupFailed) {
556
+ throw authError("cuna.auth.session_cleanup_failed", "Cuna stopped sign-in but could not prove removal of its encrypted local session.", { retryable: true });
557
+ }
558
+ throw error;
559
+ }
560
+ }
561
+ }
562
+ async function reexchangeAccess(signal, rejectedToken, joinedRetry = false) {
563
+ assertNotCancelled(signal);
564
+ // `vault.refresh` already reads, validates, and locks the exact encrypted
565
+ // record before invoking this callback. An initial `vault.load` duplicated
566
+ // that secure-store round trip in every fresh CLI process.
567
+ let captured;
568
+ let capturedRevision;
569
+ try {
570
+ const snapshot = await input.vault.refresh(credentialBinding, async (current) => {
571
+ if (current === undefined) {
572
+ return { status: "missing" };
573
+ }
574
+ const stored = current.material.withBytes((bytes) => decodeStored(bytes, input.config));
575
+ if (Date.parse(stored.loginCodeExpiresAt) <= now()) {
576
+ return { status: "rejected", reason: "local_expired" };
577
+ }
578
+ // The whole point: a bearer with real life left is reused instead of
579
+ // buying a new one. Every authenticated command used to exchange, and
580
+ // the server allows ten exchanges per rolling minute, so the eleventh
581
+ // command in a minute failed. No HTTP happens on this path.
582
+ const reusable = usableStoredAccess(stored, now(), rejectedToken);
583
+ if (reusable !== undefined) {
584
+ captured?.material.dispose();
585
+ captured = {
586
+ material: SecretMaterial.fromUtf8(reusable.token),
587
+ expiresAt: Date.parse(reusable.expiresAt),
588
+ profile: stored.profile,
589
+ sessionId: stored.sessionId,
590
+ };
591
+ return { status: "retained" };
592
+ }
593
+ try {
594
+ const exchange = await input.client.exchange({
595
+ id: stored.continuationId,
596
+ loginCode: stored.loginCode,
597
+ clientInstanceId: stored.clientInstanceId,
598
+ profile: stored.profile,
599
+ state: stored.state,
600
+ codeVerifier: stored.codeVerifier,
601
+ redirectUri: stored.redirectUri,
602
+ expectedLoginCodeExpiresAt: stored.loginCodeExpiresAt,
603
+ ...(signal === undefined ? {} : { signal }),
604
+ });
605
+ const exchangedAt = now();
606
+ if (exchange.sessionId !== stored.sessionId ||
607
+ exchange.loginCodeExpiresAt !== stored.loginCodeExpiresAt ||
608
+ (stored.intentClass === "signup"
609
+ ? !signupContextTransitionAllowed(stored.context, exchange.context)
610
+ : JSON.stringify(contextWire(exchange.context)) !== JSON.stringify(contextWire(stored.context))) ||
611
+ Date.parse(exchange.loginCodeExpiresAt) <= exchangedAt ||
612
+ Date.parse(exchange.accessExpiresAt) <= exchangedAt) {
613
+ try {
614
+ await input.client.logout(exchange.accessToken, signal);
615
+ }
616
+ catch { /* best-effort family cleanup */ }
617
+ return { status: "rejected", reason: "local_integrity" };
618
+ }
619
+ try {
620
+ if (stored.intentClass === "signup") {
621
+ ensureSignupContext(exchange.context, true);
622
+ }
623
+ else {
624
+ ensureOnboardingReady(exchange.context);
625
+ }
626
+ }
627
+ catch {
628
+ try {
629
+ await input.client.logout(exchange.accessToken, signal);
630
+ }
631
+ catch { /* best-effort family cleanup */ }
632
+ return { status: "rejected", reason: "local_integrity" };
633
+ }
634
+ captured?.material.dispose();
635
+ captured = {
636
+ material: SecretMaterial.fromUtf8(exchange.accessToken),
637
+ expiresAt: Date.parse(exchange.accessExpiresAt),
638
+ profile: stored.profile,
639
+ sessionId: stored.sessionId,
640
+ };
641
+ // The exact encrypted browser code still stays in place: this
642
+ // rewrites the SAME record, adding only the bearer just obtained so
643
+ // the next process can reuse it instead of buying another one.
644
+ //
645
+ // `expiresAt` is the LOGIN CODE's expiry, never the bearer's. The
646
+ // vault compare-deletes an envelope once its header expiry passes,
647
+ // so passing the ten-minute access expiry here would delete the
648
+ // thirty-day login code every ten minutes. It is also not optional:
649
+ // an omitted `expiresAt` writes a header with no expiry at all.
650
+ return {
651
+ status: "rotated",
652
+ material: encodeStored({
653
+ ...stored,
654
+ access: {
655
+ token: exchange.accessToken,
656
+ expiresAt: exchange.accessExpiresAt,
657
+ observedAt: new Date(exchangedAt).toISOString(),
658
+ },
659
+ }),
660
+ expiresAt: Date.parse(stored.loginCodeExpiresAt),
661
+ };
662
+ }
663
+ catch (error) {
664
+ if (isAuthoritativeReexchangeRejection(error)) {
665
+ return { status: "rejected", reason: "authoritative_remote" };
666
+ }
667
+ throw error;
668
+ }
669
+ });
670
+ try {
671
+ capturedRevision = snapshot.revision;
672
+ // Another service sharing this vault can own the refresh callback.
673
+ // Admit its returned authority through the same stored-token checks;
674
+ // a joined flight must never hand back the bearer this caller rejected.
675
+ if (captured === undefined) {
676
+ const stored = snapshot.material.withBytes((bytes) => decodeStored(bytes, input.config));
677
+ const reusable = usableStoredAccess(stored, now(), rejectedToken);
678
+ if (Date.parse(stored.loginCodeExpiresAt) > now() && reusable !== undefined) {
679
+ captured = {
680
+ material: SecretMaterial.fromUtf8(reusable.token),
681
+ expiresAt: Date.parse(reusable.expiresAt),
682
+ profile: stored.profile,
683
+ sessionId: stored.sessionId,
684
+ };
685
+ }
686
+ }
687
+ }
688
+ finally {
689
+ snapshot.material.dispose();
690
+ }
691
+ }
692
+ catch (error) {
693
+ captured?.material.dispose();
694
+ if (error instanceof CredentialBoundaryError && error.code === "credential_missing") {
695
+ throw authError("cuna.auth.required", "No interactive Cuna session is stored.", { hint: "Run `cuna login`." });
696
+ }
697
+ if (error instanceof CredentialBoundaryError && error.code === "credential_revoked") {
698
+ throw authError("cuna.auth.reauthentication_required", "The Cuna session was rejected and removed.", {
699
+ hint: "Run `cuna login`.",
700
+ ...(error.safeDetails?.refreshRejection === "authoritative_remote"
701
+ ? { details: { reason: "durable_session_removed" } }
702
+ : {}),
703
+ });
704
+ }
705
+ throw error;
706
+ }
707
+ if (captured === undefined && !joinedRetry) {
708
+ // The joined flight may have retained the rejected token. Its vault
709
+ // snapshot is now released; permit one fresh, rejection-aware attempt.
710
+ return await reexchangeAccess(signal, rejectedToken, true);
711
+ }
712
+ if (captured === undefined || capturedRevision === undefined || !Number.isSafeInteger(capturedRevision) || capturedRevision < 1) {
713
+ captured?.material.dispose();
714
+ throw authError("cuna.auth.reexchange_unknown", "Cuna could not establish a new in-memory access token.", {
715
+ retryable: true,
716
+ hint: "Open `cuna` and choose the existing Machine and session to reconnect.",
717
+ });
718
+ }
719
+ access?.material.dispose();
720
+ access = { ...captured, revision: capturedRevision };
721
+ }
722
+ async function acquireAccessToken(signal) {
723
+ assertNotCancelled(signal);
724
+ if (access !== undefined && access.expiresAt - now() > 30_000)
725
+ return cachedAccessToken();
726
+ if (reexchangeFlight === undefined) {
727
+ const created = reexchangeAccess();
728
+ reexchangeFlight = created;
729
+ void created.then(() => { if (reexchangeFlight === created)
730
+ reexchangeFlight = undefined; }, () => { if (reexchangeFlight === created)
731
+ reexchangeFlight = undefined; });
732
+ }
733
+ await waitForReexchange(reexchangeFlight, signal);
734
+ return cachedAccessToken();
735
+ }
736
+ async function refreshRejectedAccessToken(rejectedAccessToken, signal) {
737
+ assertNotCancelled(signal);
738
+ if (!isAccessToken(rejectedAccessToken)) {
739
+ throw authError("cuna.auth.reexchange_unknown", "Cuna refused an invalid rejected-token refresh request.");
740
+ }
741
+ // A concurrent request may already have replaced the rejected bearer. In
742
+ // that case return the newer authority instead of rotating the family a
743
+ // second time. The rejected string already exists at the HTTP boundary;
744
+ // it is never persisted or included in an error.
745
+ if (access !== undefined) {
746
+ const currentAccessToken = access.material.withBytes((bytes) => new TextDecoder("utf-8", { fatal: true }).decode(bytes));
747
+ if (currentAccessToken !== rejectedAccessToken && access.expiresAt - now() > 30_000) {
748
+ return currentAccessToken;
749
+ }
750
+ }
751
+ access?.material.dispose();
752
+ access = undefined;
753
+ // Name the refused bearer so the stored copy of that same string cannot be
754
+ // handed straight back, which would answer a 401 with the dead token that
755
+ // caused it.
756
+ //
757
+ // An in-flight exchange started BEFORE this rejection does not know the
758
+ // token is dead, so joining it can return that same string and spin the
759
+ // 401 forever. Always start a fresh one that carries the rejection.
760
+ assertNotCancelled(signal);
761
+ // Cancellation belongs to this waiter, not to other callers sharing the
762
+ // vault refresh. waitForReexchange still releases a cancelled caller.
763
+ const created = reexchangeAccess(undefined, rejectedAccessToken);
764
+ reexchangeFlight = created;
765
+ void created.then(() => { if (reexchangeFlight === created)
766
+ reexchangeFlight = undefined; }, () => { if (reexchangeFlight === created)
767
+ reexchangeFlight = undefined; });
768
+ await waitForReexchange(created, signal);
769
+ return cachedAccessToken();
770
+ }
771
+ async function whoami(signal) {
772
+ const token = await acquireAccessToken(signal);
773
+ const session = access;
774
+ if (session === undefined) {
775
+ throw authError("cuna.auth.reexchange_unknown", "Cuna could not establish a fresh in-memory access token.", { retryable: true });
776
+ }
777
+ const context = await input.client.context(token, signal);
778
+ return Object.freeze({
779
+ profile: session.profile,
780
+ sessionId: session.sessionId,
781
+ context,
782
+ storageMode: "encrypted-local",
783
+ });
784
+ }
785
+ function discardCachedAccess() {
786
+ access?.material.dispose();
787
+ access = undefined;
788
+ }
789
+ /**
790
+ * Drop the bearer from the encrypted record while keeping the durable login
791
+ * code exactly where it is.
792
+ *
793
+ * Clearing only the in-process copy stopped being enough once the bearer
794
+ * began outliving the process: after an ambiguous logout the server may have
795
+ * revoked it, and the next command would otherwise read that same possibly
796
+ * dead bearer straight back off disk. The login code is untouched, so a
797
+ * fresh acquisition still works without a new browser sign-in.
798
+ */
799
+ async function forgetStoredAccess() {
800
+ await input.vault.refresh(credentialBinding, async (current) => {
801
+ if (current === undefined)
802
+ return { status: "retained" };
803
+ const stored = current.material.withBytes((bytes) => decodeStored(bytes, input.config));
804
+ if (stored.access === undefined)
805
+ return { status: "retained" };
806
+ const { access: _discarded, ...withoutAccess } = stored;
807
+ return {
808
+ status: "rotated",
809
+ material: encodeStored(withoutAccess),
810
+ // The envelope keeps the LOGIN CODE's expiry, never the bearer's; the
811
+ // vault deletes a record once its header expiry passes.
812
+ expiresAt: Date.parse(stored.loginCodeExpiresAt),
813
+ };
814
+ });
815
+ }
816
+ /**
817
+ * A remote logout fact applies only to the durable revision that supplied
818
+ * this command's bearer. A different shell may have completed a new browser
819
+ * sign-in while this request was in flight, so a plain delete would turn a
820
+ * confirmed old-family logout into destruction of the new family locally.
821
+ */
822
+ async function removeConfirmedLocalSession(expectedRevision) {
823
+ discardCachedAccess();
824
+ // Re-exchange reached this state only after CredentialVault fenced the
825
+ // exact rejected envelope. Do not issue a second unfenced delete.
826
+ if (expectedRevision === undefined)
827
+ return;
828
+ let outcome;
829
+ try {
830
+ outcome = await input.vault.deleteIfRevision({
831
+ binding: credentialBinding,
832
+ expectedRevision,
833
+ });
834
+ }
835
+ catch {
836
+ throw authError("cuna.auth.session_cleanup_failed", "Cuna confirmed sign-out but could not prove removal of its encrypted local session.", { retryable: true });
837
+ }
838
+ if (outcome === "conflict") {
839
+ throw authError("cuna.auth.session_cleanup_conflict", "Cuna confirmed sign-out for the prior session, but a newer encrypted local session was preserved.", {
840
+ hint: "Inspect the current session before deciding whether to sign out again.",
841
+ details: { reason: "newer_local_session" },
842
+ });
843
+ }
844
+ }
845
+ async function logout(signal) {
846
+ let cleanupRevision;
847
+ try {
848
+ const token = await acquireAccessToken(signal);
849
+ const session = access;
850
+ if (session === undefined) {
851
+ throw authError("cuna.auth.session_cleanup_failed", "Cuna could not prove which encrypted local session authorized sign-out.", { retryable: true });
852
+ }
853
+ cleanupRevision = session.revision;
854
+ const revoked = await input.client.logout(token, signal);
855
+ if (revoked !== true) {
856
+ throw authError("cuna.auth.logout_unknown", "Cuna could not confirm server-side logout.", { retryable: true });
857
+ }
858
+ }
859
+ catch (error) {
860
+ if (!isDefinitiveLogoutTermination(error)) {
861
+ // An ambiguous remote write must not leave a possibly-revoked bearer
862
+ // usable — in this process or the next one. The encrypted durable code
863
+ // remains for a fresh acquisition and is never destroyed on this path.
864
+ discardCachedAccess();
865
+ // If this clear fails the bearer stays readable, which is bad; masking
866
+ // the logout failure that brought us here would be worse, so the
867
+ // original error is still the one raised.
868
+ try {
869
+ await forgetStoredAccess();
870
+ }
871
+ catch { /* the logout error below is the one that matters */ }
872
+ throw error;
873
+ }
874
+ if (error instanceof CunaError &&
875
+ error.code === "cuna.auth.reauthentication_required" &&
876
+ error.details?.reason === "durable_session_removed") {
877
+ // The re-exchange path proved a terminal server result and already
878
+ // compare-deleted the exact envelope it read.
879
+ cleanupRevision = undefined;
880
+ }
881
+ else {
882
+ const session = access;
883
+ if (session === undefined) {
884
+ discardCachedAccess();
885
+ throw authError("cuna.auth.session_cleanup_failed", "Cuna received a terminal sign-out response but could not identify the encrypted local session to remove.", { retryable: true });
886
+ }
887
+ cleanupRevision = session.revision;
888
+ }
889
+ }
890
+ await removeConfirmedLocalSession(cleanupRevision);
891
+ return Object.freeze({ revoked: true });
892
+ }
893
+ return Object.freeze({
894
+ login,
895
+ signup: (request = {}) => login({ intentClass: "signup", ...request }),
896
+ acquireAccessToken,
897
+ refreshRejectedAccessToken,
898
+ whoami,
899
+ logout,
900
+ });
901
+ }
902
+ //# sourceMappingURL=human-session.js.map