@north-light/cli 0.3.561 → 0.3.562

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 (573) hide show
  1. package/dist/api/client.d.ts +27 -4
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/bash-jobs.d.ts +5 -1
  6. package/dist/api/dto/broker-ops.d.ts +25 -1
  7. package/dist/api/dto/charges.d.ts +9 -2
  8. package/dist/api/dto/delivery.d.ts +26 -2
  9. package/dist/api/dto/execution-start.d.ts +57 -0
  10. package/dist/api/dto/execution-start.js +0 -0
  11. package/dist/api/dto/modelauth.d.ts +5 -0
  12. package/dist/api/dto/node-records.d.ts +16 -0
  13. package/dist/api/dto/objects.d.ts +1 -1
  14. package/dist/api/dto/reports.d.ts +0 -3
  15. package/dist/api/dto/run-events.d.ts +7 -1
  16. package/dist/api/dto/settings.d.ts +55 -0
  17. package/dist/api/dto/settings.js +0 -0
  18. package/dist/api/index.d.ts +2 -0
  19. package/dist/api/index.js +1 -1
  20. package/dist/api/routes.d.ts +11 -0
  21. package/dist/api/routes.js +1 -1
  22. package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
  23. package/dist/cli.js +7 -7
  24. package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
  25. package/dist/clients/attach/chrome/bash-jobs.js +1 -1
  26. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  27. package/dist/clients/attach/chrome/roster.js +6 -4
  28. package/dist/clients/attach/overlays/auth.js +2 -2
  29. package/dist/clients/attach/render/chat-view.js +1 -1
  30. package/dist/clients/attach/viewer.js +550 -567
  31. package/dist/commands/canvas-config.js +2 -2
  32. package/dist/commands/canvas-use.js +1 -1
  33. package/dist/commands/human.js +2 -2
  34. package/dist/commands/node/bash.js +3 -3
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/person-settings.d.ts +1 -0
  37. package/dist/commands/person-settings.js +1 -0
  38. package/dist/commands/pkg/market-manage.d.ts +2 -2
  39. package/dist/commands/pkg/market-manage.js +1 -1
  40. package/dist/commands/pkg/plugin-manage.js +13 -13
  41. package/dist/commands/pkg/shared.js +1 -1
  42. package/dist/commands/profile/default.js +1 -1
  43. package/dist/commands/push.js +2 -3
  44. package/dist/commands/sys/config.js +1 -1
  45. package/dist/commands/sys/connect.js +1 -1
  46. package/dist/commands/sys/context/sysprompt.js +2 -2
  47. package/dist/commands/sys/doctor.js +1 -1
  48. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  49. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  50. package/dist/commands/sys/panels/models-panel.d.ts +8 -2
  51. package/dist/commands/sys/panels/models-panel.js +1 -1
  52. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
  53. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  54. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  55. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  56. package/dist/commands/sys/panels/provider-panel.js +1 -1
  57. package/dist/commands/sys/provider-login.js +5 -5
  58. package/dist/commands/sys/setup-core.js +3 -3
  59. package/dist/commands/sys/setup-settings-runner.js +1 -1
  60. package/dist/commands/sys/spend.js +3 -3
  61. package/dist/commands/sys/update.js +1 -1
  62. package/dist/core/auth-file.d.ts +11 -19
  63. package/dist/core/auth-file.js +1 -2
  64. package/dist/core/auto-update.d.ts +1 -1
  65. package/dist/core/auto-update.js +4 -4
  66. package/dist/core/bash-jobs.d.ts +29 -71
  67. package/dist/core/bash-jobs.js +9 -10
  68. package/dist/core/blobs/directory.d.ts +3 -0
  69. package/dist/core/blobs/directory.js +1 -0
  70. package/dist/core/blobs/fault.d.ts +3 -0
  71. package/dist/core/blobs/fault.js +1 -0
  72. package/dist/core/blobs/index.d.ts +21 -0
  73. package/dist/core/blobs/index.js +1 -0
  74. package/dist/core/blobs/r2.d.ts +9 -0
  75. package/dist/core/blobs/r2.js +4 -0
  76. package/dist/core/bootstrap.d.ts +1 -1
  77. package/dist/core/bootstrap.js +2 -2
  78. package/dist/core/canvas/canvas.d.ts +9 -7
  79. package/dist/core/canvas/canvas.js +8 -8
  80. package/dist/core/canvas/crons.d.ts +3 -2
  81. package/dist/core/canvas/crons.js +3 -3
  82. package/dist/core/canvas/daemon-owner.d.ts +5 -0
  83. package/dist/core/canvas/daemon-owner.js +1 -1
  84. package/dist/core/canvas/db.d.ts +10 -9
  85. package/dist/core/canvas/db.js +4 -4
  86. package/dist/core/canvas/executions.d.ts +100 -4
  87. package/dist/core/canvas/executions.js +1 -1
  88. package/dist/core/canvas/export-sqlite.js +1 -1
  89. package/dist/core/canvas/focuses.d.ts +4 -0
  90. package/dist/core/canvas/focuses.js +1 -1
  91. package/dist/core/canvas/history.js +9 -9
  92. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
  93. package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
  94. package/dist/core/canvas/node-tokens.d.ts +1 -4
  95. package/dist/core/canvas/node-tokens.js +3 -3
  96. package/dist/core/canvas/paths.d.ts +0 -1
  97. package/dist/core/canvas/paths.js +1 -1
  98. package/dist/core/canvas/person-state.d.ts +26 -0
  99. package/dist/core/canvas/person-state.js +1 -0
  100. package/dist/core/canvas/person.d.ts +3 -10
  101. package/dist/core/canvas/person.js +7 -7
  102. package/dist/core/canvas/reconciler-leases.d.ts +12 -0
  103. package/dist/core/canvas/reconciler-leases.js +8 -0
  104. package/dist/core/canvas/recovery.d.ts +4 -0
  105. package/dist/core/canvas/recovery.js +1 -1
  106. package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
  107. package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
  108. package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
  109. package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
  110. package/dist/core/canvas/schema-version.d.ts +1 -1
  111. package/dist/core/canvas/schema-version.js +1 -1
  112. package/dist/core/canvas/sessions.d.ts +8 -11
  113. package/dist/core/canvas/sessions.js +1 -4
  114. package/dist/core/canvas/tree.d.ts +4 -2
  115. package/dist/core/canvas/tree.js +6 -6
  116. package/dist/core/canvas/types.d.ts +7 -0
  117. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  118. package/dist/core/command-plugins/bundle.d.ts +1 -1
  119. package/dist/core/command-plugins/bundle.js +1 -1
  120. package/dist/core/command-plugins/compose.d.ts +1 -1
  121. package/dist/core/command-plugins/discovery.d.ts +0 -1
  122. package/dist/core/command-plugins/discovery.js +1 -1
  123. package/dist/core/command-plugins/help-addenda.js +1 -1
  124. package/dist/core/command-plugins/presence.d.ts +2 -0
  125. package/dist/core/command-plugins/presence.js +1 -1
  126. package/dist/core/config.d.ts +16 -15
  127. package/dist/core/config.js +1 -1
  128. package/dist/core/conversation-store/client-worker.js +1 -1
  129. package/dist/core/conversation-store/client.js +2 -2
  130. package/dist/core/conversation-store/index.d.ts +9 -11
  131. package/dist/core/conversation-store/index.js +1 -1
  132. package/dist/core/conversation-store/patch.js +1 -1
  133. package/dist/core/conversation-store/row-store.d.ts +14 -0
  134. package/dist/core/conversation-store/row-store.js +13 -0
  135. package/dist/core/directory/join.js +1 -1
  136. package/dist/core/env-name.d.ts +1 -0
  137. package/dist/core/env-name.js +1 -1
  138. package/dist/core/environment-recipe.d.ts +15 -0
  139. package/dist/core/environment-recipe.js +1 -0
  140. package/dist/core/environments.js +1 -1
  141. package/dist/core/events/emit.js +2 -2
  142. package/dist/core/events/run-event-chunks.d.ts +25 -0
  143. package/dist/core/events/run-event-chunks.js +5 -0
  144. package/dist/core/feed/messages.js +5 -5
  145. package/dist/core/grants/mirror.d.ts +15 -0
  146. package/dist/core/grants/mirror.js +15 -3
  147. package/dist/core/grants/on-sync.d.ts +9 -5
  148. package/dist/core/grants/on-sync.js +1 -1
  149. package/dist/core/grants/remove.d.ts +1 -6
  150. package/dist/core/grants/remove.js +1 -1
  151. package/dist/core/grants/sync.js +1 -1
  152. package/dist/core/grants/watch.d.ts +15 -9
  153. package/dist/core/grants/watch.js +1 -1
  154. package/dist/core/graph/custom.js +8 -8
  155. package/dist/core/graph/jobs.d.ts +83 -5
  156. package/dist/core/graph/jobs.js +11 -2
  157. package/dist/core/graph/kinds/job.js +2 -2
  158. package/dist/core/graph/names.js +7 -7
  159. package/dist/core/graph/objects.d.ts +5 -1
  160. package/dist/core/graph/objects.js +11 -11
  161. package/dist/core/graph/reader.d.ts +3 -0
  162. package/dist/core/graph/reader.js +3 -3
  163. package/dist/core/graph/repo-sync/identity.d.ts +3 -1
  164. package/dist/core/graph/repo-sync/identity.js +1 -1
  165. package/dist/core/graph/repo-sync/repos.d.ts +1 -1
  166. package/dist/core/graph/repo-sync/repos.js +2 -2
  167. package/dist/core/graph/repo-sync/sync.js +2 -2
  168. package/dist/core/installed-plugins.js +1 -1
  169. package/dist/core/keybindings/persistence.d.ts +1 -1
  170. package/dist/core/keybindings/persistence.js +1 -1
  171. package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
  172. package/dist/core/layout-migrate/loose-keys.js +1 -1
  173. package/dist/core/ledger/account.d.ts +12 -0
  174. package/dist/core/ledger/account.js +2 -0
  175. package/dist/core/ledger/charges.d.ts +2 -2
  176. package/dist/core/ledger/charges.js +1 -1
  177. package/dist/core/ledger/check.d.ts +5 -1
  178. package/dist/core/ledger/check.js +1 -7
  179. package/dist/core/ledger/chunks.d.ts +8 -0
  180. package/dist/core/ledger/chunks.js +2 -0
  181. package/dist/core/ledger/index.d.ts +2 -1
  182. package/dist/core/ledger/index.js +1 -1
  183. package/dist/core/ledger/infra-allowance.d.ts +9 -0
  184. package/dist/core/ledger/infra-allowance.js +1 -0
  185. package/dist/core/ledger/limits.js +3 -3
  186. package/dist/core/ledger/payer.d.ts +1 -1
  187. package/dist/core/ledger/payer.js +1 -1
  188. package/dist/core/ledger/read.d.ts +14 -4
  189. package/dist/core/ledger/read.js +2 -3
  190. package/dist/core/ledger/totals.d.ts +5 -16
  191. package/dist/core/ledger/totals.js +4 -4
  192. package/dist/core/ledger/types.d.ts +12 -3
  193. package/dist/core/llm/run.js +2 -2
  194. package/dist/core/managed-providers.d.ts +72 -0
  195. package/dist/core/managed-providers.js +1 -0
  196. package/dist/core/model-auth/api-credential-store.d.ts +10 -0
  197. package/dist/core/model-auth/api-credential-store.js +1 -0
  198. package/dist/core/model-auth/daemon-login.d.ts +1 -1
  199. package/dist/core/model-auth/daemon-login.js +1 -1
  200. package/dist/core/model-auth/flows.d.ts +5 -4
  201. package/dist/core/model-auth/flows.js +1 -1
  202. package/dist/core/model-auth/handoff-jti.d.ts +3 -4
  203. package/dist/core/model-auth/handoff-jti.js +1 -1
  204. package/dist/core/model-auth/readiness.js +1 -1
  205. package/dist/core/model-config.d.ts +1 -1
  206. package/dist/core/model-config.js +1 -1
  207. package/dist/core/model-routes-pool.d.ts +14 -0
  208. package/dist/core/model-routes-pool.js +1 -0
  209. package/dist/core/model-routes.d.ts +11 -16
  210. package/dist/core/model-routes.js +1 -1
  211. package/dist/core/peer/ingest.d.ts +2 -0
  212. package/dist/core/peer/ingest.js +1 -1
  213. package/dist/core/peer/stubs.d.ts +8 -0
  214. package/dist/core/peer/stubs.js +1 -1
  215. package/dist/core/permissions/evaluator.d.ts +7 -2
  216. package/dist/core/permissions/evaluator.js +2 -2
  217. package/dist/core/plugin-env.js +1 -1
  218. package/dist/core/pool-credential-store.d.ts +2 -5
  219. package/dist/core/pool-credential-store.js +1 -1
  220. package/dist/core/profiles/app-env-store.d.ts +5 -5
  221. package/dist/core/profiles/app-env-store.js +1 -2
  222. package/dist/core/profiles/default-binding.d.ts +5 -5
  223. package/dist/core/profiles/default-binding.js +1 -1
  224. package/dist/core/profiles/env-store.d.ts +10 -21
  225. package/dist/core/profiles/env-store.js +1 -2
  226. package/dist/core/profiles/manifest.d.ts +14 -18
  227. package/dist/core/profiles/manifest.js +1 -1
  228. package/dist/core/profiles/relocate.js +1 -1
  229. package/dist/core/profiles/select.js +21 -21
  230. package/dist/core/provider-credential.d.ts +1 -1
  231. package/dist/core/provider-credential.js +1 -1
  232. package/dist/core/provider-management.d.ts +3 -4
  233. package/dist/core/provider-management.js +1 -1
  234. package/dist/core/providers/bundles.js +2 -2
  235. package/dist/core/providers/call.d.ts +3 -34
  236. package/dist/core/providers/description.d.ts +34 -0
  237. package/dist/core/providers/description.js +0 -0
  238. package/dist/core/providers/token-cache.d.ts +14 -14
  239. package/dist/core/providers/token-cache.js +1 -1
  240. package/dist/core/review/companion.js +1 -1
  241. package/dist/core/runs/activity.d.ts +1 -1
  242. package/dist/core/runs/activity.js +1 -1
  243. package/dist/core/runs/events.d.ts +7 -5
  244. package/dist/core/runs/events.js +2 -6
  245. package/dist/core/runs/operations.d.ts +1 -1
  246. package/dist/core/runs/operations.js +6 -6
  247. package/dist/core/runtime/bearings.js +1 -1
  248. package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
  249. package/dist/core/runtime/broker/attachments-fetch.js +1 -0
  250. package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
  251. package/dist/core/runtime/broker/bash-job-launch.js +1 -1
  252. package/dist/core/runtime/broker/boot-state.d.ts +9 -0
  253. package/dist/core/runtime/broker/boot-state.js +1 -0
  254. package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
  255. package/dist/core/runtime/broker/cli-relay.js +1 -1
  256. package/dist/core/runtime/broker/client-registry.d.ts +3 -3
  257. package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
  258. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  259. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  260. package/dist/core/runtime/broker/fault-retry.js +1 -1
  261. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  262. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
  263. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  264. package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
  265. package/dist/core/runtime/broker/job-reporter.js +2 -0
  266. package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
  267. package/dist/core/runtime/broker/model-adapter.js +3 -2
  268. package/dist/core/runtime/broker/signal-stream.js +1 -1
  269. package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
  270. package/dist/core/runtime/broker/spilled-body.js +1 -0
  271. package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
  272. package/dist/core/runtime/broker/summaries-post.js +1 -0
  273. package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
  274. package/dist/core/runtime/broker/tool-groups.js +1 -1
  275. package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
  276. package/dist/core/runtime/broker/turn-replies.js +1 -1
  277. package/dist/core/runtime/broker-persona-guidance.js +2 -2
  278. package/dist/core/runtime/broker.js +1 -1
  279. package/dist/core/runtime/deliver-live.js +1 -1
  280. package/dist/core/runtime/first-boot-rows.d.ts +8 -0
  281. package/dist/core/runtime/first-boot-rows.js +2 -0
  282. package/dist/core/runtime/fleet.d.ts +13 -12
  283. package/dist/core/runtime/front-door-login.js +1 -1
  284. package/dist/core/runtime/front-door.js +6 -6
  285. package/dist/core/runtime/headless-pi.js +1 -1
  286. package/dist/core/runtime/host.d.ts +14 -3
  287. package/dist/core/runtime/host.js +3 -3
  288. package/dist/core/runtime/invocation.d.ts +6 -0
  289. package/dist/core/runtime/kickoff.d.ts +2 -14
  290. package/dist/core/runtime/kickoff.js +17 -19
  291. package/dist/core/runtime/launch-config-worker.js +1 -1
  292. package/dist/core/runtime/launch.js +2 -2
  293. package/dist/core/runtime/model-registry.js +1 -1
  294. package/dist/core/runtime/node-read.d.ts +4 -6
  295. package/dist/core/runtime/node-read.js +3 -3
  296. package/dist/core/runtime/nodes.js +1 -1
  297. package/dist/core/runtime/outcome-document.d.ts +0 -1
  298. package/dist/core/runtime/outcome-document.js +1 -1
  299. package/dist/core/runtime/output-result.d.ts +15 -0
  300. package/dist/core/runtime/output-result.js +5 -0
  301. package/dist/core/runtime/placement.d.ts +17 -1
  302. package/dist/core/runtime/placement.js +1 -1
  303. package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
  304. package/dist/core/runtime/provider-credential-gate.js +2 -2
  305. package/dist/core/runtime/recovery-hold.d.ts +21 -2
  306. package/dist/core/runtime/recovery-hold.js +2 -1
  307. package/dist/core/runtime/recycle.js +1 -1
  308. package/dist/core/runtime/reset.js +1 -1
  309. package/dist/core/runtime/revive.d.ts +2 -2
  310. package/dist/core/runtime/revive.js +2 -2
  311. package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
  312. package/dist/core/runtime/runner/blaxel/client.js +1 -0
  313. package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
  314. package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
  315. package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
  316. package/dist/core/runtime/runner/blaxel/runner.js +3 -0
  317. package/dist/core/runtime/runner/index.d.ts +8 -0
  318. package/dist/core/runtime/runner/index.js +1 -0
  319. package/dist/core/runtime/runner/process.d.ts +11 -0
  320. package/dist/core/runtime/runner/process.js +1 -0
  321. package/dist/core/runtime/runner/types.d.ts +95 -0
  322. package/dist/core/runtime/runner/types.js +0 -0
  323. package/dist/core/runtime/situational-context.d.ts +3 -6
  324. package/dist/core/runtime/situational-context.js +2 -2
  325. package/dist/core/runtime/situational-live.js +1 -1
  326. package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
  327. package/dist/core/runtime/spawn-env-rows.js +1 -0
  328. package/dist/core/runtime/spawn-env.d.ts +4 -18
  329. package/dist/core/runtime/spawn-env.js +1 -1
  330. package/dist/core/runtime/spawn.js +2 -2
  331. package/dist/core/runtime/stop-guard.d.ts +1 -1
  332. package/dist/core/runtime/stop-guard.js +2 -8
  333. package/dist/core/runtime/structured-output.d.ts +0 -67
  334. package/dist/core/runtime/structured-output.js +3 -8
  335. package/dist/core/runtime/tmux-driver.d.ts +8 -0
  336. package/dist/core/runtime/tmux-driver.js +6 -6
  337. package/dist/core/runtime/view-socket.d.ts +20 -0
  338. package/dist/core/runtime/view-socket.js +3 -1
  339. package/dist/core/runtime/warm-pool.d.ts +13 -1
  340. package/dist/core/runtime/warm-pool.js +1 -1
  341. package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
  342. package/dist/core/scoped-state/legacy-profiles.js +1 -0
  343. package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
  344. package/dist/core/scoped-state/materialized-plugins.js +1 -0
  345. package/dist/core/scoped-state/migrate.d.ts +8 -1
  346. package/dist/core/scoped-state/migrate.js +3 -3
  347. package/dist/core/scoped-state/person-rows.d.ts +10 -0
  348. package/dist/core/scoped-state/person-rows.js +6 -0
  349. package/dist/core/scoped-state/person-store.d.ts +72 -0
  350. package/dist/core/scoped-state/person-store.js +1 -0
  351. package/dist/core/secrets/blaxel-launch.d.ts +24 -0
  352. package/dist/core/secrets/blaxel-launch.js +4 -0
  353. package/dist/core/secrets/blaxel-register.d.ts +2 -0
  354. package/dist/core/secrets/blaxel-register.js +1 -0
  355. package/dist/core/secrets/env-rows.d.ts +17 -0
  356. package/dist/core/secrets/env-rows.js +4 -0
  357. package/dist/core/secrets/environment-owner.d.ts +8 -0
  358. package/dist/core/secrets/environment-owner.js +1 -0
  359. package/dist/core/secrets/environment-secrets.d.ts +14 -0
  360. package/dist/core/secrets/environment-secrets.js +1 -0
  361. package/dist/core/secrets/index.d.ts +2 -0
  362. package/dist/core/secrets/index.js +1 -0
  363. package/dist/core/secrets/key.d.ts +3 -0
  364. package/dist/core/secrets/key.js +2 -0
  365. package/dist/core/secrets/seal.d.ts +18 -0
  366. package/dist/core/secrets/seal.js +1 -0
  367. package/dist/core/spaces/mounts.d.ts +1 -1
  368. package/dist/core/spaces/mounts.js +1 -1
  369. package/dist/core/spaces/process.d.ts +1 -1
  370. package/dist/core/spaces/stop.d.ts +7 -5
  371. package/dist/core/spaces/stop.js +2 -2
  372. package/dist/core/spaces/users.d.ts +2 -1
  373. package/dist/core/spaces/users.js +1 -1
  374. package/dist/core/storage/attachments.d.ts +11 -8
  375. package/dist/core/storage/attachments.js +1 -1
  376. package/dist/core/storage/tables.d.ts +4 -0
  377. package/dist/core/storage/tables.js +3 -3
  378. package/dist/core/subscription-state.d.ts +34 -72
  379. package/dist/core/subscription-state.js +1 -4
  380. package/dist/core/substrate/delivery/plan.js +1 -1
  381. package/dist/core/user-settings.js +1 -1
  382. package/dist/daemon/api/app-listener.js +10 -10
  383. package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
  384. package/dist/daemon/api/auth/person-bearer.js +1 -0
  385. package/dist/daemon/api/bridge.js +4 -4
  386. package/dist/daemon/api/document-listener.js +3 -3
  387. package/dist/daemon/api/environment-of.d.ts +18 -0
  388. package/dist/daemon/api/environment-of.js +5 -0
  389. package/dist/daemon/api/handlers/analytics.js +1 -1
  390. package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
  391. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  392. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  393. package/dist/daemon/api/handlers/bash.js +1 -1
  394. package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
  395. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  396. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  397. package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
  398. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  399. package/dist/daemon/api/handlers/canvas.js +4 -4
  400. package/dist/daemon/api/handlers/charges.d.ts +1 -1
  401. package/dist/daemon/api/handlers/charges.js +1 -1
  402. package/dist/daemon/api/handlers/conversations.d.ts +2 -0
  403. package/dist/daemon/api/handlers/conversations.js +3 -0
  404. package/dist/daemon/api/handlers/crons.js +1 -1
  405. package/dist/daemon/api/handlers/daemon.js +1 -1
  406. package/dist/daemon/api/handlers/delivery.d.ts +1 -1
  407. package/dist/daemon/api/handlers/delivery.js +1 -1
  408. package/dist/daemon/api/handlers/docs.js +1 -1
  409. package/dist/daemon/api/handlers/exec.js +1 -1
  410. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  411. package/dist/daemon/api/handlers/execution-start.js +2 -0
  412. package/dist/daemon/api/handlers/files.js +1 -1
  413. package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
  414. package/dist/daemon/api/handlers/git-credential.js +1 -0
  415. package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
  416. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  417. package/dist/daemon/api/handlers/human-requests.js +1 -1
  418. package/dist/daemon/api/handlers/inbox.js +1 -1
  419. package/dist/daemon/api/handlers/messages.js +1 -1
  420. package/dist/daemon/api/handlers/model-config.js +1 -1
  421. package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
  422. package/dist/daemon/api/handlers/modelauth.js +1 -1
  423. package/dist/daemon/api/handlers/node-events.d.ts +6 -0
  424. package/dist/daemon/api/handlers/node-events.js +3 -3
  425. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  426. package/dist/daemon/api/handlers/node-records.js +3 -2
  427. package/dist/daemon/api/handlers/nodes.js +1 -1
  428. package/dist/daemon/api/handlers/objects.js +3 -3
  429. package/dist/daemon/api/handlers/profiles.js +1 -1
  430. package/dist/daemon/api/handlers/providers.js +1 -1
  431. package/dist/daemon/api/handlers/reports.js +9 -13
  432. package/dist/daemon/api/handlers/reviews.js +1 -1
  433. package/dist/daemon/api/handlers/runs.js +1 -1
  434. package/dist/daemon/api/handlers/settings.d.ts +2 -0
  435. package/dist/daemon/api/handlers/settings.js +1 -0
  436. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  437. package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
  438. package/dist/daemon/api/handlers/vendor-status.js +1 -1
  439. package/dist/daemon/api/handlers/worktree.js +1 -1
  440. package/dist/daemon/api/map.d.ts +2 -2
  441. package/dist/daemon/api/map.js +2 -2
  442. package/dist/daemon/api/operations.js +1 -1
  443. package/dist/daemon/api/reader.d.ts +8 -1
  444. package/dist/daemon/api/reader.js +1 -1
  445. package/dist/daemon/api/router.d.ts +4 -2
  446. package/dist/daemon/api/router.js +1 -1
  447. package/dist/daemon/api/server.d.ts +15 -3
  448. package/dist/daemon/api/server.js +1 -1
  449. package/dist/daemon/api/vendor-signin.d.ts +2 -1
  450. package/dist/daemon/api/vendor-signin.js +19 -19
  451. package/dist/daemon/api/viewer-presence.d.ts +20 -6
  452. package/dist/daemon/api/viewer-presence.js +2 -1
  453. package/dist/daemon/broker-signals/bus.d.ts +48 -11
  454. package/dist/daemon/broker-signals/bus.js +1 -1
  455. package/dist/daemon/cron/armed-context.js +1 -1
  456. package/dist/daemon/cron/lease-recovery.js +1 -1
  457. package/dist/daemon/cron/live-runs.d.ts +6 -0
  458. package/dist/daemon/cron/live-runs.js +1 -1
  459. package/dist/daemon/cron/run-socket.js +1 -1
  460. package/dist/daemon/cron-run.js +2 -2
  461. package/dist/daemon/crtrd-cli.js +1 -1
  462. package/dist/daemon/crtrd.d.ts +10 -0
  463. package/dist/daemon/crtrd.js +18 -17
  464. package/dist/daemon/delivery/run-delivery.d.ts +1 -1
  465. package/dist/daemon/delivery/run-delivery.js +1 -1
  466. package/dist/daemon/due-sources.d.ts +1 -1
  467. package/dist/daemon/due-sources.js +1 -1
  468. package/dist/daemon/due.js +7 -6
  469. package/dist/daemon/fleet.d.ts +6 -6
  470. package/dist/daemon/fleet.js +9 -9
  471. package/dist/daemon/park-activity.d.ts +2 -1
  472. package/dist/daemon/park-activity.js +1 -1
  473. package/dist/daemon/park-pending.d.ts +20 -13
  474. package/dist/daemon/park-pending.js +3 -1
  475. package/dist/daemon/profile-delete.js +1 -1
  476. package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
  477. package/dist/daemon/reconcilers/bash-deadline.js +1 -3
  478. package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
  479. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  480. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
  481. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  482. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
  483. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  484. package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
  485. package/dist/daemon/reconcilers/grant-effects.js +1 -0
  486. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
  487. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  488. package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
  489. package/dist/daemon/reconcilers/ledger-r2.js +7 -0
  490. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
  491. package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
  492. package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
  493. package/dist/daemon/reconcilers/ledger-storage.js +5 -0
  494. package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
  495. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  496. package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
  497. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  498. package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
  499. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  500. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
  501. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  502. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
  503. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
  504. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
  505. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
  506. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  507. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  508. package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
  509. package/dist/daemon/reconcilers/recipe-image.js +7 -0
  510. package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
  511. package/dist/daemon/reconcilers/row-claim.js +2 -0
  512. package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
  513. package/dist/daemon/reconcilers/storage-due-work.js +2 -1
  514. package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
  515. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  516. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  517. package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
  518. package/dist/daemon/reconcilers/throttled-lane.js +1 -0
  519. package/dist/daemon/review/finish.js +1 -1
  520. package/dist/daemon/run-notices.d.ts +3 -0
  521. package/dist/daemon/run-notices.js +1 -1
  522. package/dist/daemon/runs/append-section.d.ts +4 -1
  523. package/dist/daemon/runs/append-section.js +2 -2
  524. package/dist/daemon/runs/observer.d.ts +3 -2
  525. package/dist/daemon/runs/observer.js +3 -2
  526. package/dist/daemon/runs/view-transport.d.ts +19 -0
  527. package/dist/daemon/runs/view-transport.js +1 -0
  528. package/dist/daemon/structured-requests/boot.d.ts +2 -1
  529. package/dist/daemon/structured-requests/boot.js +1 -1
  530. package/dist/daemon/structured-requests/effects.d.ts +4 -3
  531. package/dist/daemon/structured-requests/effects.js +1 -1
  532. package/dist/daemon/structured-requests/notifier.d.ts +2 -0
  533. package/dist/daemon/structured-requests/notifier.js +1 -1
  534. package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
  535. package/dist/daemon/structured-requests/sidecar.js +1 -1
  536. package/dist/launcher/validate.js +1 -1
  537. package/dist/migrations/004-canvas-documents/index.js +2 -2
  538. package/dist/pi-extensions/broker-local.d.ts +3 -3
  539. package/dist/pi-extensions/broker-local.js +1 -2
  540. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  541. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  542. package/dist/pi-extensions/canvas-elapsed.js +2 -2
  543. package/dist/pi-extensions/canvas-goal-capture.js +1 -1
  544. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  545. package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
  546. package/dist/pi-extensions/canvas-review-boundary.js +3 -3
  547. package/dist/pi-extensions/canvas-stophook.js +1 -1
  548. package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
  549. package/dist/pi-extensions/canvas-structured-output.js +6 -11
  550. package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
  551. package/dist/pi-extensions/canvas-tool-guide.js +6 -6
  552. package/dist/shared/inbox-entry-body.js +7 -7
  553. package/dist/types.d.ts +12 -0
  554. package/docs/sdk/release-notes.md +4 -0
  555. package/package.json +7 -4
  556. package/packages/crouter-identity/package.json +1 -1
  557. package/runtime.lock.json +13 -13
  558. package/scripts/git-credential-crtr +33 -0
  559. package/scripts/sandbox-bootstrap.sh +54 -0
  560. package/scripts/sandbox-start.mjs +320 -0
  561. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
  562. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
  563. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
  564. package/dist/core/conversation-store/file-store.d.ts +0 -37
  565. package/dist/core/conversation-store/file-store.js +0 -9
  566. package/dist/core/conversation-store/listener.d.ts +0 -4
  567. package/dist/core/conversation-store/listener.js +0 -8
  568. package/dist/core/scoped-state/profiles.d.ts +0 -27
  569. package/dist/core/scoped-state/profiles.js +0 -1
  570. package/dist/core/scoped-state/providers.d.ts +0 -24
  571. package/dist/core/scoped-state/providers.js +0 -1
  572. package/dist/core/scoped-state/settings.d.ts +0 -28
  573. package/dist/core/scoped-state/settings.js +0 -1
@@ -5,11 +5,18 @@ import type { Charge, Limit } from './types.js';
5
5
  * `usd_equivalent` is what the grant's limit reads: every `user:` payer charge for this grantee, model and tool, refused and
6
6
  * take rows excluded. `spend_cap` is the grant limit's `usd_per_month` for this grantee, null when none exists. */
7
7
  export declare function usageFor(grantee: string, month?: string): Promise<SpendUsageDTO>;
8
+ /** Who is reading: a person sees the rows where they are `sub`, `payer` or `account`; an app the rows where it is `grantee`
9
+ * or `payer`. Absent means the install's person, who sees every row. */
10
+ export type Reader = {
11
+ person: string;
12
+ } | {
13
+ app: string;
14
+ };
8
15
  export interface ListFilter {
9
16
  /** Only rows of this month. */
10
17
  month?: string;
11
- /** Only rows of this grantee (an app caller sees its own). */
12
- grantee?: string;
18
+ /** The reader; absent reads every row. */
19
+ reader?: Reader;
13
20
  /** A reference: `message:<id>`, `request:<id>`, `cron_run:<id>`, `run:<id>`, or a charge id. */
14
21
  under?: string;
15
22
  /** Page size, 1 to 10,000; default 1,000. */
@@ -38,7 +45,10 @@ export interface MonthTotals {
38
45
  }>;
39
46
  limits: Limit[];
40
47
  }
41
- /** One month's dollars per payer and per payer and funding, from the rollup, with the limit rows. Optionally for one payer. */
42
- export declare function monthTotals(month?: string, payer?: string): Promise<MonthTotals>;
48
+ /** The payers a `user:<id>` answers for: itself, and every app held (`ledger_holder`) by it or by a team it belongs to. */
49
+ export declare function payersOf(owner: string): Promise<string[]>;
50
+ /** One month's dollars per payer and per payer and funding, from the rollup, with the limit rows. For one person: the rows of the
51
+ * person's `payersOf` (itself and the apps it or its teams hold now), and the limits on those payers or on the person's account. */
52
+ export declare function monthTotals(month?: string, person?: string): Promise<MonthTotals>;
43
53
  /** A reference's shape, or a 400. */
44
54
  export declare function assertReference(value: string): string;
@@ -1,3 +1,2 @@
1
- var _=Object.defineProperty;var a=(e,n)=>_(e,"name",{value:n,configurable:!0});import{openDb as f}from"../canvas/db.js";import{RunError as h}from"../runs/operations.js";import{chargeOf as g}from"./charges.js";import{listLimits as E}from"./limits.js";import{resetAt as y,usageMonth as l}from"./months.js";const N="id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, node, run, provider, caused_by, vendor_ref, refused";async function v(e,n=l()){const t=await f(),s=await t.prepare(`SELECT payer, what, kind, units, usd FROM charges
2
- WHERE grantee = ? AND month = ? AND refused IS NULL AND kind <> 'take'`).all(e,n),o=a(u=>{const p=new Map;for(const r of s){if(r.kind!=="model"||!r.payer.startsWith(u))continue;const m=r.units===null?{}:JSON.parse(r.units),c=p.get(r.what)??{served_model:r.what,input_tokens:0,output_tokens:0,usd_equivalent:null};c.input_tokens+=m.input??0,c.output_tokens+=m.output??0,r.usd!==null&&(c.usd_equivalent=(c.usd_equivalent??0)+Number(r.usd)),p.set(r.what,c)}return[...p.values()].sort((r,m)=>r.served_model<m.served_model?-1:1)},"byModel"),i=a(u=>s.filter(p=>p.payer.startsWith(u)).reduce((p,r)=>p+Number(r.usd??0),0),"sum"),d=await t.prepare("SELECT usd_per_month FROM limits WHERE source = 'grant_sync' AND grantee = ? AND payer LIKE 'user:%' AND funding IS NULL AND provider IS NULL ORDER BY id LIMIT 1").get(e);return{grantee:e,month:n,usd_equivalent:i("user:"),by_model:o("user:"),app_paid:{usd_equivalent:i("app:"),by_model:o("app:")},spend_cap:d===void 0?null:Number(d.usd_per_month),reset_at:y(n)}}a(v,"usageFor");const R=/^(message|request|cron_run|run):(.+)$/;function b(e){const n=R.exec(e);if(n===null)return{sql:"id = ?",params:[e]};if(n[1]==="message"||n[1]==="cron_run")return{sql:"caused_by = ?",params:[e]};if(n[1]==="request")return{sql:"(request = ? OR caused_by = ?)",params:[n[2],e]};const t=n[2];return{sql:`(run = ? OR run IN (SELECT n.run_id FROM nodes n WHERE n.run_id IS NOT NULL AND n.node_id = n.run_id AND n.caused_by IN (
3
- SELECT 'request:' || c.request FROM charges c WHERE c.run = ? AND c.request IS NOT NULL)))`,params:[t,t]}}a(b,"referenceCondition");const L=a(e=>Buffer.from(JSON.stringify([e.at,e.id]),"utf8").toString("base64url"),"encodeCursor");function O(e){try{const n=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(Array.isArray(n)&&n.length===2&&typeof n[0]=="string"&&typeof n[1]=="string")return[n[0],n[1]]}catch{}throw new h(400,"invalid_request","cursor must be a `next` value from a previous page","after")}a(O,"decodeCursor");async function C(e={}){const n=Math.min(Math.max(Math.trunc(e.limit??1e3),1),1e4),t=[],s=[];if(e.under!==void 0){const d=b(e.under);t.push(d.sql),s.push(...d.params),e.month!==void 0&&(t.push("month = ?"),s.push(e.month))}else t.push("month = ?"),s.push(e.month??l());e.grantee!==void 0&&(t.push("grantee = ?"),s.push(e.grantee)),e.after!==void 0&&(t.push("(at, id) > (?, ?)"),s.push(...O(e.after)));const o=(await(await f()).prepare(`SELECT ${N} FROM charges WHERE ${t.join(" AND ")} ORDER BY at, id LIMIT ${n+1}`).all(...s)).map(g),i=o.slice(0,n);return{charges:i,next:o.length>n?L(i[n-1]):null}}a(C,"listCharges");async function I(e=l(),n){const t=await f(),s=n===void 0?"":" AND payer = ?",o=n===void 0?[e]:[e,n],i=await t.prepare(`SELECT payer, SUM(usd) AS usd FROM charge_totals WHERE month = ?${s} GROUP BY payer ORDER BY payer`).all(...o),d=await t.prepare(`SELECT payer, funding, SUM(usd) AS usd FROM charge_totals WHERE month = ?${s} GROUP BY payer, funding ORDER BY payer, funding`).all(...o);return{month:e,by_payer:i.map(u=>({payer:u.payer,usd:Number(u.usd)})),by_funding:d.map(u=>({payer:u.payer,funding:u.funding,usd:Number(u.usd)})),limits:await E(n===void 0?{}:{payer:n})}}a(I,"monthTotals");function T(e){if(e.length===0||e.length>512)throw new h(400,"invalid_request","under must be a reference or a charge id","under");return e}a(T,"assertReference");export{T as assertReference,C as listCharges,I as monthTotals,v as usageFor};
1
+ var O=Object.defineProperty;var o=(t,e)=>O(t,"name",{value:e,configurable:!0});import{openDb as m}from"../canvas/db.js";import{subjectSet as S}from"../permissions/evaluator.js";import{RunError as E}from"../runs/operations.js";import{appsHeldBy as A}from"./account.js";import{chargeOf as w}from"./charges.js";import{ChunkRows as R}from"./chunks.js";import{listLimits as b}from"./limits.js";import{resetAt as L,usageMonth as _}from"./months.js";const N="id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, node, run, provider, caused_by, vendor_ref, refused, account, billed_usd";async function J(t,e=_()){const r=await m(),a=await r.prepare(`SELECT payer, what, kind, units, usd FROM charges
2
+ WHERE grantee = ? AND month = ? AND refused IS NULL AND kind <> 'take'`).all(t,e);for(const n of await new R().rows(e))n.grantee===t&&n.refused===null&&n.kind!=="take"&&a.push(n);const i=o(n=>{const u=new Map;for(const s of a){if(s.kind!=="model"||!s.payer.startsWith(n))continue;const f=s.units===null?{}:typeof s.units=="string"?JSON.parse(s.units):s.units,h=u.get(s.what)??{served_model:s.what,input_tokens:0,output_tokens:0,usd_equivalent:null};h.input_tokens+=f.input??0,h.output_tokens+=f.output??0,s.usd!==null&&(h.usd_equivalent=(h.usd_equivalent??0)+Number(s.usd)),u.set(s.what,h)}return[...u.values()].sort((s,f)=>s.served_model<f.served_model?-1:1)},"byModel"),p=o(n=>a.filter(u=>u.payer.startsWith(n)).reduce((u,s)=>u+Number(s.usd??0),0),"sum"),d=await r.prepare("SELECT usd_per_month FROM limits WHERE source = 'grant_sync' AND grantee = ? AND payer LIKE 'user:%' AND funding IS NULL AND provider IS NULL ORDER BY id LIMIT 1").get(t);return{grantee:t,month:e,usd_equivalent:p("user:"),by_model:i("user:"),app_paid:{usd_equivalent:p("app:"),by_model:i("app:")},spend_cap:d===void 0?null:Number(d.usd_per_month),reset_at:L(e)}}o(J,"usageFor");const M=/^(message|request|cron_run|run):(.+)$/;async function v(t,e){const r=M.exec(t);if(r===null)return{sql:"id = ?",params:[t],test:o(n=>n.id===t,"test")};if(r[1]==="message"||r[1]==="cron_run")return{sql:"caused_by = ?",params:[t],test:o(n=>n.caused_by===t,"test")};if(r[1]==="request")return{sql:"(request = ? OR caused_by = ?)",params:[r[2],t],test:o(n=>n.request===r[2]||n.caused_by===t,"test")};const a=r[2],i=await m(),p=new Set((await i.prepare("SELECT DISTINCT request FROM charges WHERE run = ? AND request IS NOT NULL").all(a)).map(n=>n.request));for(const n of await e.rows())n.run===a&&n.request!==null&&p.add(n.request);const d=new Set([a]);if(p.size>0){const n=await i.prepare("SELECT n.run_id FROM nodes n WHERE n.run_id IS NOT NULL AND n.node_id = n.run_id AND n.caused_by = ANY(?::text[])").all([...p].map(u=>`request:${u}`));for(const u of n)d.add(u.run_id)}return{sql:"run = ANY(?::text[])",params:[[...d]],test:o(n=>n.run!==null&&d.has(n.run),"test")}}o(v,"referenceMatch");function D(t){return"person"in t?{sql:"(sub = ? OR payer = ? OR account = ?)",params:[t.person,t.person,t.person]}:{sql:"(grantee = ? OR payer = ?)",params:[t.app,t.app]}}o(D,"readerCondition");const g=o((t,e)=>"person"in e?t.sub===e.person||t.payer===e.person||t.account===e.person:t.grantee===e.app||t.payer===e.app,"mayRead");async function C(t,e,r){const a=await m(),i=new Map,p=o(async d=>{const n=d.caused_by;if(n===null||!n.startsWith("request:"))return;const u=n.slice(8),s=[...(await a.prepare(`SELECT ${N} FROM charges WHERE request = ? AND id <> ?`).all(u,d.id)).map(w),...(await r.rows()).filter(f=>f.request===u&&f.id!==d.id)].sort(q);return s.find(f=>g(f,e))??s[0]},"parentOf");for(const d of t){let n=d;for(let u=0;n!==void 0&&!g(n,e)&&u<64;u+=1)n=await p(n);n!==void 0&&g(n,e)&&i.set(n.id,n)}return[...i.values()]}o(C,"visibleChain");const q=o((t,e)=>t.at<e.at?-1:t.at>e.at?1:t.id<e.id?-1:t.id>e.id?1:0,"byAtId"),T=o(t=>Buffer.from(JSON.stringify([t.at,t.id]),"utf8").toString("base64url"),"encodeCursor");function k(t){try{const e=JSON.parse(Buffer.from(t,"base64url").toString("utf8"));if(Array.isArray(e)&&e.length===2&&typeof e[0]=="string"&&typeof e[1]=="string")return[e[0],e[1]]}catch{}throw new E(400,"invalid_request","cursor must be a `next` value from a previous page","after")}o(k,"decodeCursor");async function P(t={}){const e=Math.min(Math.max(Math.trunc(t.limit??1e3),1),1e4),r=new R,a=[],i=[],p=[];let d;if(t.under!==void 0){const c=await v(t.under,r);a.push(c.sql),i.push(...c.params),p.push(c.test),t.month!==void 0&&(a.push("month = ?"),i.push(t.month),p.push(l=>l.month===t.month),d=t.month)}else{const c=t.month??_();a.push("month = ?"),i.push(c),d=c}if(t.under===void 0&&t.reader!==void 0){const c=D(t.reader);a.push(c.sql),i.push(...c.params);const l=t.reader;p.push(y=>g(y,l))}if(t.after!==void 0){const[c,l]=k(t.after);a.push("(at, id) > (?, ?)"),i.push(c,l),p.push(y=>y.at>c||y.at===c&&y.id>l)}const n=(await(await m()).prepare(`SELECT ${N} FROM charges WHERE ${a.join(" AND ")} ORDER BY at, id LIMIT ${e+1}`).all(...i)).map(w),u=(await r.rows(d)).filter(c=>p.every(l=>l(c))),s=[...n,...u].sort(q).slice(0,e+1),f=s.slice(0,e);return{charges:t.under!==void 0&&t.reader!==void 0?await C(f,t.reader,r):f,next:s.length>e?T(f[e-1]):null}}o(P,"listCharges");async function I(t){const e=await m(),{groupIds:r}=await S(e,t.slice(5));return[t,...await A(e,[t,...r])]}o(I,"payersOf");async function j(t=_(),e){const r=await m(),a=e===void 0?void 0:await I(e),i=a===void 0?"":" AND payer = ANY(?::text[])",p=a===void 0?[t]:[t,a],d=await r.prepare(`SELECT payer, SUM(usd) AS usd FROM charge_totals WHERE month = ?${i} GROUP BY payer ORDER BY payer`).all(...p),n=await r.prepare(`SELECT payer, funding, SUM(usd) AS usd FROM charge_totals WHERE month = ?${i} GROUP BY payer, funding ORDER BY payer, funding`).all(...p),u=a===void 0?await b():(await b()).filter(s=>s.payer!==null&&a.includes(s.payer)||s.account===e);return{month:t,by_payer:d.map(s=>({payer:s.payer,usd:Number(s.usd)})),by_funding:n.map(s=>({payer:s.payer,funding:s.funding,usd:Number(s.usd)})),limits:u}}o(j,"monthTotals");function z(t){if(t.length===0||t.length>512)throw new E(400,"invalid_request","under must be a reference or a charge id","under");return t}o(z,"assertReference");export{z as assertReference,P as listCharges,j as monthTotals,I as payersOf,J as usageFor};
@@ -1,21 +1,10 @@
1
1
  import type { CanvasDb } from '../canvas/db.js';
2
- /** `charge_totals` stores an absent `sub` or `provider` as ''. */
3
- export declare const absent: (value: string | null | undefined) => string;
4
- /** Add `usd` (null adds 0) to the rollup row of one charge, in the caller's transaction. Concurrent writers to one key
5
- * serialize on the conflicting row, so the total is exact. */
6
- export declare function addToTotals(db: CanvasDb, charge: {
7
- month: string;
8
- payer: string;
9
- funding: string;
10
- sub: string | null;
11
- grantee: string;
12
- provider: string | null;
13
- usd: number | null;
14
- }): Promise<void>;
15
2
  /** Rebuild the whole rollup from `charges`, `billed_usd` with `usd`: a rebuild that dropped it would zero the billed totals
16
3
  * 002's trigger keeps, and an older build that may still open the database must not do that either. Plain SQL, so the SQLite
17
4
  * import runs it on its own client. */
18
5
  export declare const REBUILD_ALL_TOTALS: readonly ["DELETE FROM charge_totals", "INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)\n FROM charges WHERE refused IS NULL AND kind <> 'take' GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')"];
19
- /** Delete the rollup rows that name `a` or `b` as payer, sub or grantee and rebuild them from `charges`, so two rows that
20
- * now share a key are summed once. For person substitution, in the same transaction as the rename. */
21
- export declare function rebuildTotalsFor(db: CanvasDb, a: string, b: string): Promise<void>;
6
+ /** Rename person `from` to `to` in the rollup rows that name it as payer, sub or grantee, summing rows that now share a key.
7
+ * For person substitution, in the same transaction as the rename of `charges`. The rollup already holds every charge, so renaming
8
+ * keeps the months `ledger-r2` moved out of `charges` (a rebuild from `charges` would drop them), and the sums stay exactly what a
9
+ * rebuild of the rows still in `charges` plus the moved months would give. Returns the rollup rows renamed. */
10
+ export declare function rebuildTotalsFor(db: CanvasDb, from: string, to: string): Promise<number>;
@@ -1,4 +1,4 @@
1
- var E=Object.defineProperty;var t=(r,e)=>E(r,"name",{value:e,configurable:!0});const s=t(r=>r??"","absent");async function o(r,e){await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd) VALUES (?, ?, ?, ?, ?, ?, ?)
2
- ON CONFLICT (month, payer, funding, sub, grantee, provider) DO UPDATE SET usd = charge_totals.usd + excluded.usd`).run(e.month,e.payer,e.funding,s(e.sub),e.grantee,s(e.provider),e.usd??0)}t(o,"addToTotals");const a=`SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)
3
- FROM charges WHERE refused IS NULL AND kind <> 'take'`,u="GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')",p=["DELETE FROM charge_totals",`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${a} ${u}`];async function i(r,e,n){await r.prepare("DELETE FROM charge_totals WHERE payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)").run(e,n,e,n,e,n),await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${a}
4
- AND (payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)) ${u}`).run(e,n,e,n,e,n)}t(i,"rebuildTotalsFor");export{p as REBUILD_ALL_TOTALS,s as absent,o as addToTotals,i as rebuildTotalsFor};
1
+ var u=Object.defineProperty;var E=(r,n)=>u(r,"name",{value:n,configurable:!0});import{CrtrError as h}from"../errors.js";const i=`SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)
2
+ FROM charges WHERE refused IS NULL AND kind <> 'take'`,p="GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')",l=["DELETE FROM charge_totals",`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${i} ${p}`];async function L(r,n,a){const t=await r.prepare("SELECT a.month FROM charge_chunks a JOIN charge_chunks b ON b.month = a.month AND b.payer = ? WHERE a.payer = ? ORDER BY a.month LIMIT 1").get(a,n);if(t!==void 0)throw new h("person_conflict",`${n} and ${a} both have the moved month ${t.month} (charge_chunks holds one blob per month and payer), so they cannot be merged`,void 0,{from:n,to:a,month:t.month});const o=await r.prepare(`DELETE FROM charge_totals WHERE payer = ? OR sub = ? OR grantee = ?
3
+ RETURNING month, payer, funding, sub, grantee, provider, usd, billed_usd`).all(n,n,n),s=E(e=>e===n?a:e,"rename");for(const e of o)await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
4
+ ON CONFLICT (month, payer, funding, sub, grantee, provider) DO UPDATE SET usd = charge_totals.usd + EXCLUDED.usd, billed_usd = charge_totals.billed_usd + EXCLUDED.billed_usd`).run(e.month,s(e.payer),e.funding,s(e.sub),s(e.grantee),e.provider,e.usd,e.billed_usd);const d=await r.prepare("UPDATE charge_chunks SET payer = ? WHERE payer = ?").run(a,n);return o.length+d.changes}E(L,"rebuildTotalsFor");export{l as REBUILD_ALL_TOTALS,L as rebuildTotalsFor};
@@ -1,6 +1,6 @@
1
1
  export type ChargeKind = 'model' | 'tool' | 'sandbox' | 'job' | 'storage' | 'take';
2
2
  export type Funding = 'bill' | 'subscription' | 'key' | 'endpoint' | 'take' | 'legacy';
3
- export type LimitSource = 'grant_sync' | 'person' | 'operator';
3
+ export type LimitSource = 'grant_sync' | 'person' | 'operator' | 'billing' | 'card';
4
4
  /** Why the check refused a call: the limit's id and a sentence. Stored on the refused row. */
5
5
  export interface RefusedMark {
6
6
  limit: string;
@@ -27,6 +27,8 @@ export interface Charge {
27
27
  caused_by: string | null;
28
28
  vendor_ref: string | null;
29
29
  refused: RefusedMark | null;
30
+ account: string | null;
31
+ billed_usd: number | null;
30
32
  }
31
33
  /** What a site hands `writeCharge`. */
32
34
  export interface ChargeInput {
@@ -50,11 +52,18 @@ export interface ChargeInput {
50
52
  vendorRef?: string | null;
51
53
  /** Set when the check refused this call: the row is stored with usd 0 and is not added to the rollup. */
52
54
  refused?: RefusedMark | null;
55
+ /** The principal Northlight invoices; null when none or house-paid. */
56
+ account?: string | null;
57
+ /** The amount Northlight bills, when known at write time (a sandbox or storage row). */
58
+ billedUsd?: number | null;
53
59
  }
54
60
  /** One `limits` row. A null filter means any. */
55
61
  export interface Limit {
56
62
  id: string;
57
- payer: string;
63
+ /** Null when `account` is set. */
64
+ payer: string | null;
65
+ /** Counts the account's own charges and those of the apps it holds. */
66
+ account: string | null;
58
67
  funding: Funding | null;
59
68
  grantee: string | null;
60
69
  provider: string | null;
@@ -63,4 +72,4 @@ export interface Limit {
63
72
  source: LimitSource;
64
73
  updated_at: string;
65
74
  }
66
- export type LimitInput = Omit<Limit, 'updated_at' | 'funding' | 'grantee' | 'provider' | 'sub'> & Partial<Pick<Limit, 'funding' | 'grantee' | 'provider' | 'sub'>>;
75
+ export type LimitInput = Omit<Limit, 'updated_at' | 'funding' | 'grantee' | 'provider' | 'sub' | 'account'> & Partial<Pick<Limit, 'funding' | 'grantee' | 'provider' | 'sub' | 'account'>>;
@@ -1,3 +1,3 @@
1
- var Ae=Object.defineProperty;var c=(e,t)=>Ae(e,"name",{value:t,configurable:!0});import{randomUUID as Re}from"node:crypto";import{streamSimple as $e}from"@earendil-works/pi-ai/compat";import{APP_FAMILY as Ie,expandModelCandidates as ue,parseModelSpec as fe,probeRouteAvailability as Ce}from"../model-routes.js";import{readMergedLaunchConfig as me}from"../config.js";import{openModelRegistry as Oe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as pe,coolingUntilFor as he,isManagedProvider as K,markSubscriptionRateLimited as Se,markSubscriptionSuccess as Le,readSubscriptionPool as Ee,retryAfterMsFromHeaders as z,retryAfterMsFromError as Fe,refreshSubscriptionCredential as Ne,subscriptionNeedsSuccessWrite as De}from"../subscription-state.js";import{classifyRateLimit as Pe,describeRateLimitScope as _e,rateLimitHeaders as Te}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as re}from"../provider-error-recognition.js";import{isSafetyRefusal as Je}from"./refusal.js";import{findRefusal as We,payerOf as ge,refusalError as Ue,refusedMark as Be,writeCharge as ye}from"../ledger/index.js";import{personIdAsync as He}from"../canvas/person.js";import{getNode as we}from"../canvas/canvas.js";import{outboundToken as Ke}from"../providers/mint.js";import{discardFile as ze}from"../providers/token-cache.js";import{ProviderError as Xe}from"../providers/errors.js";import{appCandidates as Ge}from"./app-rungs.js";function Ye(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}c(Ye,"validContent");function be(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,i]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:i}}):e??""}c(be,"imageContent");function Ze(e,t){const n=(e instanceof Error?e.message:String(e)).split("; stack=",1)[0],i=re({message:n}).kind;return i!=="connection"&&i!=="connection-only"?e:new s(503,"upstream_model_unavailable",n.slice(0,500),void 0,{served_model:t})}c(Ze,"refreshFailure");function qt(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&Ye(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(i=>i&&typeof i.id=="string"&&typeof i.function?.name=="string"&&typeof i.function.arguments=="string"&&Qe(i.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,i]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(i!==void 0&&(typeof i!="number"||!Number.isFinite(i)||n==="temperature"&&(i<0||i>2)||n==="top_p"&&(i<0||i>1)||n==="max_tokens"&&(!Number.isSafeInteger(i)||i<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}c(qt,"parseLlmRequest");function Qe(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}c(Qe,"isJsonObject");function Ve(e){const t=Date.now(),n=e.messages.map(r=>r.role==="system"?{role:"system",content:r.content??"",timestamp:t}:r.role==="tool"?{role:"toolResult",toolCallId:r.tool_call_id??"",toolName:"",content:typeof r.content=="string"||r.content===null?[{type:"text",text:r.content??""}]:be(r.content),isError:!1,timestamp:t}:r.role==="assistant"?{role:"assistant",content:[...r.content?[{type:"text",text:r.content}]:[],...(r.tool_calls??[]).map(_=>({type:"toolCall",id:_.id,name:_.function.name,arguments:JSON.parse(_.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:be(r.content),timestamp:t}),i=n.findIndex(r=>r.role!=="system"),d=n.slice(0,i<0?n.length:i).map(r=>r.content),y=d.join(`
1
+ var Ae=Object.defineProperty;var c=(e,t)=>Ae(e,"name",{value:t,configurable:!0});import{randomUUID as Re}from"node:crypto";import{streamSimple as $e}from"@earendil-works/pi-ai/compat";import{probeRouteAvailability as Ie}from"../model-routes-pool.js";import{APP_FAMILY as Ce,expandModelCandidates as ue,parseModelSpec as fe}from"../model-routes.js";import{readMergedLaunchConfig as me}from"../config.js";import{openModelRegistry as Oe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as pe,coolingUntilFor as he,isManagedProvider as K,markSubscriptionRateLimited as Se,markSubscriptionSuccess as Le,readSubscriptionPool as Ee,retryAfterMsFromHeaders as z,retryAfterMsFromError as Fe,refreshSubscriptionCredential as Ne,subscriptionNeedsSuccessWrite as De}from"../subscription-state.js";import{classifyRateLimit as Pe,describeRateLimitScope as _e,rateLimitHeaders as Te}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as re}from"../provider-error-recognition.js";import{isSafetyRefusal as Je}from"./refusal.js";import{findRefusal as We,payerOf as ge,refusalError as Ue,refusedMark as Be,writeCharge as ye}from"../ledger/index.js";import{personIdAsync as He}from"../canvas/person.js";import{getNode as we}from"../canvas/canvas.js";import{outboundToken as Ke}from"../providers/mint.js";import{discardFile as ze}from"../providers/token-cache.js";import{ProviderError as Xe}from"../providers/errors.js";import{appCandidates as Ge}from"./app-rungs.js";function Ye(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}c(Ye,"validContent");function be(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,i]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:i}}):e??""}c(be,"imageContent");function Ze(e,t){const n=(e instanceof Error?e.message:String(e)).split("; stack=",1)[0],i=re({message:n}).kind;return i!=="connection"&&i!=="connection-only"?e:new s(503,"upstream_model_unavailable",n.slice(0,500),void 0,{served_model:t})}c(Ze,"refreshFailure");function Mt(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&Ye(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(i=>i&&typeof i.id=="string"&&typeof i.function?.name=="string"&&typeof i.function.arguments=="string"&&Qe(i.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,i]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(i!==void 0&&(typeof i!="number"||!Number.isFinite(i)||n==="temperature"&&(i<0||i>2)||n==="top_p"&&(i<0||i>1)||n==="max_tokens"&&(!Number.isSafeInteger(i)||i<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}c(Mt,"parseLlmRequest");function Qe(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}c(Qe,"isJsonObject");function Ve(e){const t=Date.now(),n=e.messages.map(r=>r.role==="system"?{role:"system",content:r.content??"",timestamp:t}:r.role==="tool"?{role:"toolResult",toolCallId:r.tool_call_id??"",toolName:"",content:typeof r.content=="string"||r.content===null?[{type:"text",text:r.content??""}]:be(r.content),isError:!1,timestamp:t}:r.role==="assistant"?{role:"assistant",content:[...r.content?[{type:"text",text:r.content}]:[],...(r.tool_calls??[]).map(_=>({type:"toolCall",id:_.id,name:_.function.name,arguments:JSON.parse(_.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:be(r.content),timestamp:t}),i=n.findIndex(r=>r.role!=="system"),d=n.slice(0,i<0?n.length:i).map(r=>r.content),y=d.join(`
2
2
 
3
- `),h=e.tools?.map(({function:r})=>({name:r.name,description:r.description??"",parameters:r.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(d.length),...h?{tools:h}:{}}}c(Ve,"contextOf");function ve(e,t,n,i){const d=c(()=>{const h=me(n,i),r=Object.values({...h.modelRoutes??{},...h.legacyModelRoutes??{}});return r.length?[h.modelLadders.defaultProvider,...h.modelRouting?.familyOrder??[],...r.map(_=>_.family)].find(_=>r.some($=>$.family===_)):h.modelLadders.defaultProvider??"anthropic"},"ownerFamily");if(!e){const h=d();return{kind:"logical",family:h,strength:"strong",spec:`${h}/strong`}}const y=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);if(y){if(y[1]===Ie){const h=d();return{kind:"logical",family:h,strength:y[2],spec:`${h}/${y[2]}`}}return{kind:"logical",family:y[1],strength:y[2],spec:e}}return{kind:t==="exact"?"exact":"concrete",spec:e}}c(ve,"requestFor");function et(e,t){const{input:n,output:i,cacheRead:d,cacheWrite:y}=e.cost;return n===0&&i===0&&d===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}c(et,"usdEquivalent");function tt(e,t){return e.compat?.allowedFallbackModels?.find(i=>i.provider===e.provider&&i.model===t)}c(tt,"allowedFallback");function Mt(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}c(Mt,"llmCallerOf");function ot(e){return e instanceof Xe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}c(ot,"mintFailure");const nt=/\bmodel is not supported when using Codex with a ChatGPT account\b/i;function rt(e,t,n){if(!(e!=="openai-codex"||t===void 0||!nt.test(t)))return new s(404,"model_unavailable",t,void 0,{served_model:n})}c(rt,"codexAccountModelRejection");function it(e){return e.get("request-id")??e.get("x-request-id")}c(it,"vendorRequestId");async function jt(e,t,n,i,d,y,h){const r=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!r)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&r)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(r&&!r.inference_endpoint)throw new s(404,"model_unavailable",`${r.grantee} is app-paid but its grant carries no inference endpoint`);const _=r?.inference_endpoint??void 0,$=d===void 0?null:r?.sub??d.sub??await He(),E=await Oe(),ie=c(u=>u.routeId!=="concrete"&&u.routeId!=="exact","configured"),se=c(u=>{const g=E.find(u.providerId,u.modelId);return g!==void 0&&(!e.reasoning_effort||g.reasoning)&&(e.seed===void 0&&e.response_format===void 0||g.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(g.api))},"matchesFeatures"),xe=c(()=>{const u=ve(e.model,e.model_policy,i?.cwd,i?.profileId),g=ue(u.kind==="exact"?{kind:"concrete",spec:u.spec}:u,i?.cwd,i?.profileId).filter(ie);let a;if(u.kind==="logical")a=e.model_policy==="exact"?g.filter(f=>f.family===u.family&&f.strength===u.strength).slice(0,1):g;else{const f=fe(u.spec),A=g.filter(q=>q.providerId===f?.providerId&&q.modelId===f.modelId&&(e.model_policy!=="exact"||q.thinkingLevel===f.thinkingLevel)&&se(q));if(e.model_policy==="exact")a=A.slice(0,1);else if(A.length)a=A;else{const q=me(i?.cwd,i?.profileId),k=[...Object.values(q.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[q.modelLadders.anthropic,q.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===u.spec)?.[0],m=ve(void 0,"substitute",i?.cwd,i?.profileId);a=k&&m.kind==="logical"?ue({...m,strength:k,spec:u.spec},i?.cwd,i?.profileId).filter(v=>ie(v)&&v.strength===k):[]}}return a},"ownerCandidates");let C;if(_?C=Ge(_,e.model,e.model_policy==="exact").flatMap((u,g)=>{const a=fe(u.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:u.strength,...a,rank:g}]:[]}):C=xe(),C=C.filter(se),C.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const D=`chatcmpl-${Re()}`;let O=1/0,b,X,G;const ae=c(async()=>h??(d?.node?(await we(d.node))?.caused_by??null:null)??(d?.run?(await we(d.run))?.caused_by??null:null),"causeOf");for(const u of C){if(!r&&!Ce(u,E).authenticated)continue;const g=E.find(u.providerId,u.modelId);if(!g)continue;const a=_?{...g,baseUrl:`${_.base_url}/${g.provider}`}:g,f=u.providerId,A=!r&&K(f)?Ee(f).filter(k=>!k.authFailure):[],q=A.filter(k=>he(k,a.id)<=Date.now());if(A.length&&!q.length){const k=Math.min(...A.map(m=>he(m,a.id)));k<O&&(O=k,X=`${a.provider}/${a.id}`);continue}const le=r?[void 0,void 0]:A.length?q:[void 0];for(const[k,m]of le.entries()){const v=Date.now();let de=m?.access;if(m&&m.expires<=Date.now()&&K(f)){const o=E.getProvider(f)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${f}`,void 0,{served_model:`${a.provider}/${a.id}`});de=(await Ne(f,m.label,async(x,ce)=>{const H=await o.refresh({type:"oauth",refresh:x,access:"",expires:0,...ce?{clientId:ce}:{}},t??new AbortController().signal).catch(je=>{throw Ze(je,`${a.provider}/${a.id}`)}),ne=H.clientId;return{refresh:H.refresh,access:H.access,expires:H.expires,...typeof ne=="string"&&ne?{clientId:ne}:{}}})).access}const ke=Ve(e),S=m||r?void 0:await E.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const p=`${a.provider}/${a.id}`,L=e.model??p,R=r?ge({call:"model",route:"endpoint",grantee:r.grantee}):$===null?null:ge({call:"model",route:m?"subscription":"key",sub:$});if(d!==void 0&&R!==null){const o=await We({scope:"call",payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,provider:f});if(o!==null){await ye({request:d.request??D,kind:"model",what:p,usd:null,payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,node:d.node??null,run:d.run??null,provider:f,causedBy:await ae(),refused:Be(o)}),G??=o;break}}let P;if(r&&_)try{P=await Ke({audience:_.base_url,sub:r.sub,grantee:r.grantee,initiating_app:r.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw ot(o)}let j=!1,F,Y=null,M=3e4,I;const Z=[],Q=[],T=[];let l,J;const W=c((o,w=null)=>({id:D,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:p,served_model:p,requested_model:L,substituted:L!==p&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(L),choices:[{index:0,delta:o,finish_reason:w}]}),"chunk"),U=c(o=>{Z.push(o),n?.(o)},"emit"),qe=$e(a,ke,{apiKey:P?.access_token??de??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??u.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:c(o=>{if(!o||typeof o!="object")return;const w=e.tool_choice,x=w==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof w=="object"&&w!==null?a.api==="anthropic-messages"?{type:"tool",name:w.function.name}:w:void 0;return{...o,...x?{tool_choice:x}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:c(async(o,w)=>{const x=await fetch(o,w);return F=x.status,Y=it(x.headers)??Y,x.status===429&&(j=!0,I=x.headers,M=z(x.headers)??15e3),x},"fetch"),onResponse:c(o=>{o.status===429&&(j=!0,I=o.headers,M=z(o.headers)??15e3)},"onResponse")});try{for await(const o of qe)o.type==="text_delta"&&(Q.push(o.delta),U(W({content:o.delta}))),o.type==="thinking_delta"&&(T.push(o.delta),U(W({reasoning_content:o.delta}))),o.type==="done"&&(l=o.message),o.type==="error"&&(l=o.error)}catch(o){const w=Fe(o);I===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Te(o.headers)).length&&(I=o.headers);const x=re({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(r)J=o;else{if(!j&&x.kind!=="rate-limit")throw Z.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:p}):o;j=!0,M=w??M}}if(r){if(F===401){if(P&&ze(P.key),k===0)continue;throw new s(404,"model_unavailable",`${_.base_url} refuses this runtime's tokens`,void 0,{served_model:p})}if(j&&z(I)===void 0)throw new s(503,"upstream_model_unavailable",`${p} was rate-limited without a retry-after`,void 0,{served_model:p});if(!j&&(J!==void 0||F!==void 0&&F>=500))throw new s(503,"upstream_model_unavailable",J instanceof Error?J.message:l?.errorMessage??"inference endpoint failed",void 0,{served_model:p})}if(!j&&l?.stopReason==="error"&&re({message:l.errorMessage}).kind==="rate-limit"&&(j=!0,M=15e3),j){const o=Pe(I,a.id);if(z(I)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:f,model:a.id,...m?{account:m.label}:{},observation:o},console.error(`[llm] 429 from ${p}${m?` account "${m.label}"`:""}: scope=${_e(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),m&&K(f)&&await Se(f,m.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,pe)<O&&(O=Date.now()+Math.min(M,pe),X=p),Q.length||T.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:p});if(r)break;continue}const B=Je(l);if(F===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(l?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(l?.errorMessage??""))throw new s(400,"invalid_request",l.errorMessage,"tool_choice",{served_model:p});if(l?.stopReason==="error"){const o=rt(a.provider,l.errorMessage,p);if(o)throw o}if(!l||l.stopReason==="error"&&!B||l.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",l?.errorMessage??"model request failed",void 0,{served_model:p});m&&K(f)&&De(m,a.id)&&await Le(f,m.label,v,a.id);const V=l.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),ee=B?"content_filter":l.stopReason==="toolUse"?"tool_calls":l.stopReason==="length"?"length":"stop",te=l.responseModel?tt(a,l.responseModel):void 0,N=te?`${a.provider}/${te.model}`:p,oe={prompt_tokens:l.usage.input,completion_tokens:l.usage.output,total_tokens:l.usage.totalTokens,cache_read_tokens:l.usage.cacheRead,cache_write_tokens:l.usage.cacheWrite,usd_equivalent:et(te??a,l.usage.cost.total),served_model:N};d!==void 0&&R!==null&&await ye({request:d.request??D,kind:"model",what:N,units:{input:l.usage.input,output:l.usage.output,cache_read:l.usage.cacheRead,cache_write:l.usage.cacheWrite},usd:oe.usd_equivalent,payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,node:d.node??null,run:d.run??null,provider:f,causedBy:await ae(),vendorRef:Y});const Me={id:D,object:"chat.completion",created:Math.floor(v/1e3),model:N,served_model:N,requested_model:L,substituted:L!==N&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(L),choices:[{index:0,message:{role:"assistant",content:Q.join("")||null,...T.length?{reasoning_content:T.join("")}:{},...V.length?{tool_calls:V}:{},...B?{refusal:l.errorMessage??"Content filtered"}:{}},finish_reason:ee}],usage:oe,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,w]of V.entries())U(W({tool_calls:[{index:o,...w}]}));return U({...W({},ee),...B?{choices:[{index:0,delta:{},finish_reason:ee,message:{refusal:l.errorMessage??"Content filtered"}}]}:{},usage:oe}),{completion:Me,chunks:Z}}}throw G!==void 0?Ue(G):O!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((O-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:X,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:_e(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}c(jt,"runLlm");export{Mt as llmCallerOf,qt as parseLlmRequest,jt as runLlm};
3
+ `),h=e.tools?.map(({function:r})=>({name:r.name,description:r.description??"",parameters:r.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(d.length),...h?{tools:h}:{}}}c(Ve,"contextOf");function ve(e,t,n,i){const d=c(()=>{const h=me(n,i),r=Object.values({...h.modelRoutes??{},...h.legacyModelRoutes??{}});return r.length?[h.modelLadders.defaultProvider,...h.modelRouting?.familyOrder??[],...r.map(_=>_.family)].find(_=>r.some($=>$.family===_)):h.modelLadders.defaultProvider??"anthropic"},"ownerFamily");if(!e){const h=d();return{kind:"logical",family:h,strength:"strong",spec:`${h}/strong`}}const y=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);if(y){if(y[1]===Ce){const h=d();return{kind:"logical",family:h,strength:y[2],spec:`${h}/${y[2]}`}}return{kind:"logical",family:y[1],strength:y[2],spec:e}}return{kind:t==="exact"?"exact":"concrete",spec:e}}c(ve,"requestFor");function et(e,t){const{input:n,output:i,cacheRead:d,cacheWrite:y}=e.cost;return n===0&&i===0&&d===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}c(et,"usdEquivalent");function tt(e,t){return e.compat?.allowedFallbackModels?.find(i=>i.provider===e.provider&&i.model===t)}c(tt,"allowedFallback");function jt(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}c(jt,"llmCallerOf");function ot(e){return e instanceof Xe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}c(ot,"mintFailure");const nt=/\bmodel is not supported when using Codex with a ChatGPT account\b/i;function rt(e,t,n){if(!(e!=="openai-codex"||t===void 0||!nt.test(t)))return new s(404,"model_unavailable",t,void 0,{served_model:n})}c(rt,"codexAccountModelRejection");function it(e){return e.get("request-id")??e.get("x-request-id")}c(it,"vendorRequestId");async function At(e,t,n,i,d,y,h){const r=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!r)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&r)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(r&&!r.inference_endpoint)throw new s(404,"model_unavailable",`${r.grantee} is app-paid but its grant carries no inference endpoint`);const _=r?.inference_endpoint??void 0,$=d===void 0?null:r?.sub??d.sub??await He(),E=await Oe(),ie=c(u=>u.routeId!=="concrete"&&u.routeId!=="exact","configured"),se=c(u=>{const g=E.find(u.providerId,u.modelId);return g!==void 0&&(!e.reasoning_effort||g.reasoning)&&(e.seed===void 0&&e.response_format===void 0||g.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(g.api))},"matchesFeatures"),xe=c(async()=>{const u=ve(e.model,e.model_policy,i?.cwd,i?.profileId),g=(await ue(u.kind==="exact"?{kind:"concrete",spec:u.spec}:u,i?.cwd,i?.profileId)).filter(ie);let a;if(u.kind==="logical")a=e.model_policy==="exact"?g.filter(f=>f.family===u.family&&f.strength===u.strength).slice(0,1):g;else{const f=fe(u.spec),A=g.filter(q=>q.providerId===f?.providerId&&q.modelId===f.modelId&&(e.model_policy!=="exact"||q.thinkingLevel===f.thinkingLevel)&&se(q));if(e.model_policy==="exact")a=A.slice(0,1);else if(A.length)a=A;else{const q=me(i?.cwd,i?.profileId),k=[...Object.values(q.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[q.modelLadders.anthropic,q.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===u.spec)?.[0],m=ve(void 0,"substitute",i?.cwd,i?.profileId);a=k&&m.kind==="logical"?(await ue({...m,strength:k,spec:u.spec},i?.cwd,i?.profileId)).filter(v=>ie(v)&&v.strength===k):[]}}return a},"ownerCandidates");let C;if(_?C=Ge(_,e.model,e.model_policy==="exact").flatMap((u,g)=>{const a=fe(u.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:u.strength,...a,rank:g}]:[]}):C=await xe(),C=C.filter(se),C.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const D=`chatcmpl-${Re()}`;let O=1/0,b,X,G;const ae=c(async()=>h??(d?.node?(await we(d.node))?.caused_by??null:null)??(d?.run?(await we(d.run))?.caused_by??null:null),"causeOf");for(const u of C){if(!r&&!(await Ie(u,E)).authenticated)continue;const g=E.find(u.providerId,u.modelId);if(!g)continue;const a=_?{...g,baseUrl:`${_.base_url}/${g.provider}`}:g,f=u.providerId,A=!r&&K(f)?(await Ee(f)).filter(k=>!k.authFailure):[],q=A.filter(k=>he(k,a.id)<=Date.now());if(A.length&&!q.length){const k=Math.min(...A.map(m=>he(m,a.id)));k<O&&(O=k,X=`${a.provider}/${a.id}`);continue}const le=r?[void 0,void 0]:A.length?q:[void 0];for(const[k,m]of le.entries()){const v=Date.now();let de=m?.access;if(m&&m.expires<=Date.now()&&K(f)){const o=E.getProvider(f)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${f}`,void 0,{served_model:`${a.provider}/${a.id}`});de=(await Ne(f,m.label,async(x,ce)=>{const H=await o.refresh({type:"oauth",refresh:x,access:"",expires:0,...ce?{clientId:ce}:{}},t??new AbortController().signal).catch(je=>{throw Ze(je,`${a.provider}/${a.id}`)}),ne=H.clientId;return{refresh:H.refresh,access:H.access,expires:H.expires,...typeof ne=="string"&&ne?{clientId:ne}:{}}})).access}const ke=Ve(e),S=m||r?void 0:await E.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const p=`${a.provider}/${a.id}`,L=e.model??p,R=r?ge({call:"model",route:"endpoint",grantee:r.grantee}):$===null?null:ge({call:"model",route:m?"subscription":"key",sub:$});if(d!==void 0&&R!==null){const o=await We({scope:"call",payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,provider:f});if(o!==null){await ye({request:d.request??D,kind:"model",what:p,usd:null,payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,node:d.node??null,run:d.run??null,provider:f,causedBy:await ae(),refused:Be(o)}),G??=o;break}}let P;if(r&&_)try{P=await Ke({audience:_.base_url,sub:r.sub,grantee:r.grantee,initiating_app:r.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw ot(o)}let j=!1,F,Y=null,M=3e4,I;const Z=[],Q=[],T=[];let l,J;const W=c((o,w=null)=>({id:D,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:p,served_model:p,requested_model:L,substituted:L!==p&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(L),choices:[{index:0,delta:o,finish_reason:w}]}),"chunk"),U=c(o=>{Z.push(o),n?.(o)},"emit"),qe=$e(a,ke,{apiKey:P?.access_token??de??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??u.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:c(o=>{if(!o||typeof o!="object")return;const w=e.tool_choice,x=w==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof w=="object"&&w!==null?a.api==="anthropic-messages"?{type:"tool",name:w.function.name}:w:void 0;return{...o,...x?{tool_choice:x}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:c(async(o,w)=>{const x=await fetch(o,w);return F=x.status,Y=it(x.headers)??Y,x.status===429&&(j=!0,I=x.headers,M=z(x.headers)??15e3),x},"fetch"),onResponse:c(o=>{o.status===429&&(j=!0,I=o.headers,M=z(o.headers)??15e3)},"onResponse")});try{for await(const o of qe)o.type==="text_delta"&&(Q.push(o.delta),U(W({content:o.delta}))),o.type==="thinking_delta"&&(T.push(o.delta),U(W({reasoning_content:o.delta}))),o.type==="done"&&(l=o.message),o.type==="error"&&(l=o.error)}catch(o){const w=Fe(o);I===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Te(o.headers)).length&&(I=o.headers);const x=re({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(r)J=o;else{if(!j&&x.kind!=="rate-limit")throw Z.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:p}):o;j=!0,M=w??M}}if(r){if(F===401){if(P&&ze(P.key),k===0)continue;throw new s(404,"model_unavailable",`${_.base_url} refuses this runtime's tokens`,void 0,{served_model:p})}if(j&&z(I)===void 0)throw new s(503,"upstream_model_unavailable",`${p} was rate-limited without a retry-after`,void 0,{served_model:p});if(!j&&(J!==void 0||F!==void 0&&F>=500))throw new s(503,"upstream_model_unavailable",J instanceof Error?J.message:l?.errorMessage??"inference endpoint failed",void 0,{served_model:p})}if(!j&&l?.stopReason==="error"&&re({message:l.errorMessage}).kind==="rate-limit"&&(j=!0,M=15e3),j){const o=Pe(I,a.id);if(z(I)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:f,model:a.id,...m?{account:m.label}:{},observation:o},console.error(`[llm] 429 from ${p}${m?` account "${m.label}"`:""}: scope=${_e(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),m&&K(f)&&await Se(f,m.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,pe)<O&&(O=Date.now()+Math.min(M,pe),X=p),Q.length||T.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:p});if(r)break;continue}const B=Je(l);if(F===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(l?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(l?.errorMessage??""))throw new s(400,"invalid_request",l.errorMessage,"tool_choice",{served_model:p});if(l?.stopReason==="error"){const o=rt(a.provider,l.errorMessage,p);if(o)throw o}if(!l||l.stopReason==="error"&&!B||l.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",l?.errorMessage??"model request failed",void 0,{served_model:p});m&&K(f)&&De(m,a.id)&&await Le(f,m.label,v,a.id);const V=l.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),ee=B?"content_filter":l.stopReason==="toolUse"?"tool_calls":l.stopReason==="length"?"length":"stop",te=l.responseModel?tt(a,l.responseModel):void 0,N=te?`${a.provider}/${te.model}`:p,oe={prompt_tokens:l.usage.input,completion_tokens:l.usage.output,total_tokens:l.usage.totalTokens,cache_read_tokens:l.usage.cacheRead,cache_write_tokens:l.usage.cacheWrite,usd_equivalent:et(te??a,l.usage.cost.total),served_model:N};d!==void 0&&R!==null&&await ye({request:d.request??D,kind:"model",what:N,units:{input:l.usage.input,output:l.usage.output,cache_read:l.usage.cacheRead,cache_write:l.usage.cacheWrite},usd:oe.usd_equivalent,payer:R.payer,funding:R.funding,sub:$,grantee:d.grantee,node:d.node??null,run:d.run??null,provider:f,causedBy:await ae(),vendorRef:Y});const Me={id:D,object:"chat.completion",created:Math.floor(v/1e3),model:N,served_model:N,requested_model:L,substituted:L!==N&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(L),choices:[{index:0,message:{role:"assistant",content:Q.join("")||null,...T.length?{reasoning_content:T.join("")}:{},...V.length?{tool_calls:V}:{},...B?{refusal:l.errorMessage??"Content filtered"}:{}},finish_reason:ee}],usage:oe,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,w]of V.entries())U(W({tool_calls:[{index:o,...w}]}));return U({...W({},ee),...B?{choices:[{index:0,delta:{},finish_reason:ee,message:{refusal:l.errorMessage??"Content filtered"}}]}:{},usage:oe}),{completion:Me,chunks:Z}}}throw G!==void 0?Ue(G):O!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((O-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:X,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:_e(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}c(At,"runLlm");export{jt as llmCallerOf,Mt as parseLlmRequest,At as runLlm};
@@ -0,0 +1,72 @@
1
+ export declare const ANTHROPIC_PROVIDER_ID = "anthropic";
2
+ export declare const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
3
+ /** pi's built-in `openai` provider signed in with a ChatGPT account (dynamic OAuth client). Distinct from Codex: its own pool, credential file and sign-in. */
4
+ export declare const OPENAI_CHATGPT_PROVIDER_ID = "openai";
5
+ /** Longest a recorded 429 keeps an account or model family cooling, whatever the provider's Retry-After or window reset says: after this the next real call probes the provider, and a success clears the row. */
6
+ export declare const MAX_RATE_LIMIT_COOLDOWN_MS: number;
7
+ export declare const INVALID_REFRESH_TOKEN_BACKOFF_MS: number;
8
+ export type ManagedProviderId = typeof ANTHROPIC_PROVIDER_ID | typeof OPENAI_CODEX_PROVIDER_ID | typeof OPENAI_CHATGPT_PROVIDER_ID;
9
+ export declare const MANAGED_PROVIDER_IDS: readonly ManagedProviderId[];
10
+ export type ModelStrength = 'ultra' | 'strong' | 'medium' | 'light';
11
+ export type RotationConfig = {
12
+ defaultFallbackStrength?: ModelStrength;
13
+ revertWhenAvailable?: boolean;
14
+ };
15
+ /** `rateLimitedUntil` cools the whole account; `modelCooldowns` cools one model family on it (family → epoch ms, stored rows including expired ones). Read both through `coolingUntilFor`. */
16
+ export type SubscriptionCredential = {
17
+ label: string;
18
+ refresh: string;
19
+ access: string;
20
+ expires: number;
21
+ rateLimitedUntil: number;
22
+ lastAttemptAt: number;
23
+ lastRateLimitedAt: number;
24
+ authFailure?: 'invalid_grant';
25
+ accountId?: string;
26
+ clientId?: string;
27
+ modelCooldowns?: Record<string, number>;
28
+ };
29
+ export type ProviderCandidate = {
30
+ providerId: string;
31
+ modelId: string;
32
+ thinkingLevel?: string;
33
+ registered: boolean;
34
+ authenticated: boolean;
35
+ coolingUntil?: number;
36
+ automaticFallback?: boolean;
37
+ routeId?: string;
38
+ credentialSource?: string;
39
+ family?: string;
40
+ strength?: ModelStrength;
41
+ rank?: number;
42
+ };
43
+ export type ProviderCandidateRejection = 'unregistered' | 'non-managed-fallback' | 'unauthenticated' | 'cooling';
44
+ /** When this account can next serve `modelId`: the later of its account-wide cooldown and the cooldown on that model's family. Without a model only the account-wide cooldown applies. 0 or a past time means ready. */
45
+ export declare function coolingUntilFor(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, modelId?: string): number;
46
+ /** Every cooldown on an account still in force at `now`: `scope` is `account` or the model family. */
47
+ export declare function activeCooldowns(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, now?: number): Array<{
48
+ scope: string;
49
+ until: number;
50
+ }>;
51
+ /** Human text for an account's active cooldowns (`cooldown 12m`, `opus cooldown 3d 4h`), or undefined when it is ready. */
52
+ export declare function formatCooldowns(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, now?: number): string | undefined;
53
+ /** Whether a successful call on `modelId` has stored cooldown or auth health to clear; callers skip the write otherwise (every provider write bumps provider_revision). */
54
+ export declare function subscriptionNeedsSuccessWrite(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'authFailure' | 'modelCooldowns'>, modelId?: string): boolean;
55
+ export declare function isManagedProvider(providerId: string | undefined): providerId is ManagedProviderId;
56
+ export declare function getProviderLabel(providerId: ManagedProviderId): string;
57
+ export declare function resolveProviderCandidates(candidates: readonly ProviderCandidate[], now?: number, options?: {
58
+ pinnedToFirst?: boolean;
59
+ }): {
60
+ candidates: ProviderCandidate[];
61
+ rejected: Array<{
62
+ candidate: ProviderCandidate;
63
+ reason: ProviderCandidateRejection;
64
+ }>;
65
+ };
66
+ export declare class DuplicateSubscriptionError extends Error {
67
+ constructor(message: string);
68
+ }
69
+ export declare function isInvalidRefreshTokenError(error: unknown): boolean;
70
+ export declare function parseRetryAfterHeader(value: string | undefined, now?: number): number | undefined;
71
+ export declare function retryAfterMsFromHeaders(headers: Headers | Record<string, string> | undefined): number | undefined;
72
+ export declare function retryAfterMsFromError(error: unknown): number | undefined;
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty;var o=(e,t)=>m(e,"name",{value:t,configurable:!0});import{modelCooldownFamily as c}from"./rate-limit-scope.js";const d="anthropic",f="openai-codex",l="openai",M=600*1e3,C=720*60*60*1e3,O=[d,f,l],p={anthropic:{label:"Claude"},"openai-codex":{label:"OpenAI Codex"},openai:{label:"ChatGPT"}};function R(e,t){const n=c(t);return Math.max(e.rateLimitedUntil||0,n===void 0?0:e.modelCooldowns?.[n]??0)}o(R,"coolingUntilFor");function h(e,t=Date.now()){return[...(e.rateLimitedUntil||0)>t?[{scope:"account",until:e.rateLimitedUntil}]:[],...Object.entries(e.modelCooldowns??{}).filter(([,n])=>n>t).map(([n,r])=>({scope:n,until:r}))]}o(h,"activeCooldowns");function A(e,t=Date.now()){const n=h(e,t).map(({scope:r,until:i})=>`${r==="account"?"":`${r} `}cooldown ${x(i-t)}`);return n.length?n.join(", "):void 0}o(A,"formatCooldowns");function x(e){const t=Math.max(1,Math.ceil(e/6e4));if(t<60)return`${t}m`;const n=Math.floor(t/60);return n<24?`${n}h${t%60?` ${t%60}m`:""}`:`${Math.floor(n/24)}d${n%24?` ${n%24}h`:""}`}o(x,"formatWait");function b(e,t){const n=c(t);return n!==void 0&&e.modelCooldowns?.[n]!==void 0}o(b,"hasModelCooldownRow");function I(e,t){return!!(e.rateLimitedUntil||e.authFailure)||b(e,t)}o(I,"subscriptionNeedsSuccessWrite");function N(e){return e===d||e===f||e===l}o(N,"isManagedProvider");function F(e){return p[e].label}o(F,"getProviderLabel");function P(e,t=Date.now(),n={}){const r=[],i=[];for(let s=0;s<e.length;s++){const a=e[s],u=a.registered?a.automaticFallback===!1?"non-managed-fallback":a.authenticated?(a.coolingUntil??0)>t?"cooling":void 0:"unauthenticated":"unregistered";if(!u)r.push(a);else if(i.push({candidate:a,reason:u}),n.pinnedToFirst&&s===0&&u!=="cooling")break}return{candidates:r,rejected:i}}o(P,"resolveProviderCandidates");class w extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(t){super(t),this.name="DuplicateSubscriptionError"}}function $(e){const t=e,n=[t?.errorMessage,t?.message,e instanceof Error?e.message:""].filter(i=>typeof i=="string").join(" ");return(typeof t?.status=="number"?t.status:Number(n.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(n)}o($,"isInvalidRefreshTokenError");function D(e,t=Date.now()){if(!e?.trim())return;const n=Number(e.trim());if(Number.isFinite(n))return Math.max(0,Math.round(n*1e3));const r=Date.parse(e);return Number.isFinite(r)?Math.max(0,r-t):void 0}o(D,"parseRetryAfterHeader");function _(e){if(!e)return;const t=o(r=>e instanceof Headers?e.get(r)??void 0:Object.entries(e).find(([i])=>i.toLowerCase()===r)?.[1],"get"),n=t("retry-after-ms");return n!==void 0&&n.trim()!==""&&Number.isFinite(Number(n))?Math.max(0,Number(n)):D(t("retry-after"))}o(_,"retryAfterMsFromHeaders");function y(e){if(!e||typeof e!="object"||!("headers"in e))return;const t=e.headers;if(!(!(t instanceof Headers)&&(!t||typeof t!="object"||Array.isArray(t))))return _(t)}o(y,"retryAfterMsFromError");export{d as ANTHROPIC_PROVIDER_ID,w as DuplicateSubscriptionError,C as INVALID_REFRESH_TOKEN_BACKOFF_MS,O as MANAGED_PROVIDER_IDS,M as MAX_RATE_LIMIT_COOLDOWN_MS,l as OPENAI_CHATGPT_PROVIDER_ID,f as OPENAI_CODEX_PROVIDER_ID,h as activeCooldowns,R as coolingUntilFor,A as formatCooldowns,F as getProviderLabel,$ as isInvalidRefreshTokenError,N as isManagedProvider,D as parseRetryAfterHeader,P as resolveProviderCandidates,y as retryAfterMsFromError,_ as retryAfterMsFromHeaders,I as subscriptionNeedsSuccessWrite};
@@ -0,0 +1,10 @@
1
+ import type { Credential, CredentialInfo, CredentialStore } from '@earendil-works/pi-ai';
2
+ import type { CrtrClient } from '../../api/client.js';
3
+ export declare class ApiCredentialStore implements CredentialStore {
4
+ private readonly client;
5
+ constructor(client: () => CrtrClient);
6
+ read(providerId: string): Promise<Credential | undefined>;
7
+ list(): Promise<readonly CredentialInfo[]>;
8
+ modify(providerId: string, fn: (current: Credential | undefined) => Promise<Credential | undefined>): Promise<Credential | undefined>;
9
+ delete(providerId: string): Promise<void>;
10
+ }
@@ -0,0 +1 @@
1
+ var h=Object.defineProperty;var s=(a,e)=>h(a,"name",{value:e,configurable:!0});import{isManagedProvider as l}from"../managed-providers.js";class y{static{s(this,"ApiCredentialStore")}client;constructor(e){this.client=e}async read(e){const i=(await this.client().listModelAuth()).providers.find(t=>t.provider===e);if(!(i===void 0||!i.configured))return i.managed?{type:"oauth",access:"",refresh:"",expires:Math.max(...i.accounts.map(n=>Date.parse(n.expires_at)))}:i.kind==="api_key"?{type:"api_key",key:""}:{type:"oauth",access:"",refresh:"",expires:Number.MAX_SAFE_INTEGER}}async list(){return(await this.client().listModelAuth()).providers.filter(e=>e.configured).map(e=>({providerId:e.provider,type:e.managed?"oauth":e.kind}))}async modify(e,i){if(l(e))throw new Error(`${e} signs in through the daemon (model_auth.start), not through the credential store`);const t=await i(await this.read(e));if(t!==void 0){if(t.type==="api_key")await this.client().installCredential(e,{kind:"api_key",api_key:t.key??""});else{const{type:n,access:o,refresh:c,expires:d,...r}=t;await this.client().installCredential(e,{kind:"oauth",access_token:o,refresh_token:c,expires_at:new Date(d).toISOString(),...Object.keys(r).length?{extra:r}:{}})}return t}}async delete(e){await this.client().removeCredential(e)}}export{y as ApiCredentialStore};
@@ -1,6 +1,6 @@
1
1
  import type { ProviderAuthInteraction } from '@earendil-works/pi-ai';
2
2
  import type { CrtrClient } from '../../api/client.js';
3
- import { type ManagedProviderId } from '../subscription-state.js';
3
+ import { type ManagedProviderId } from '../managed-providers.js';
4
4
  /** A self-host box with a browser on this machine: the vendor's redirect can
5
5
  * reach the daemon's loopback listener (RT-LLM-41). Over SSH or on a headless
6
6
  * host the code page / device flow is the only one that works. */
@@ -1 +1 @@
1
- var m=Object.defineProperty;var s=(e,t)=>m(e,"name",{value:t,configurable:!0});import{ApiError as c}from"../../api/errors.js";import{OPENAI_CHATGPT_PROVIDER_ID as f}from"../subscription-state.js";function g(e=process.env){return e.SSH_CONNECTION||e.SSH_TTY?!1:process.platform==="darwin"||process.platform==="win32"?!0:!!(e.DISPLAY||e.WAYLAND_DISPLAY)}s(g,"localBrowserAvailable");function p(e){return e.aborted}s(p,"cancelled");function h(e,t){return t.aborted?Promise.reject(new Error("Login cancelled")):new Promise((a,r)=>{const o=s(()=>r(new Error("Login cancelled")),"onAbort");t.addEventListener("abort",o,{once:!0}),e.then(a,r).finally(()=>t.removeEventListener("abort",o))})}s(h,"raceAbort");function y(e){if(!(!(e instanceof c)||e.status!==404||!/no route for/.test(e.message)))return new Error("The running crtr daemon predates daemon-run sign-in. Run `crtr sys daemon restart`, then sign in again.")}s(y,"olderDaemon");async function u(e,t){try{return await e.startModelAuth(t)}catch(a){throw y(a)??a}}s(u,"startFlow");async function b(e,t,a,r){const o={provider:t===f?"openai-chatgpt":t,...a?{label:a}:{}};if(!r)return{flow:await u(e,o),loopback:!1};try{return{flow:await u(e,{...o,redirect:"loopback"}),loopback:!0}}catch(n){if(n instanceof c&&n.status===409&&n.param==="redirect")return{flow:await u(e,o),loopback:!1};throw n}}s(b,"start");async function d(e,t,a){for(;!p(a);){const r=await h(e.getModelAuthFlow(t,25),a);if(r.status==="connected")return;if(r.status!=="pending")throw new Error(r.message??`sign-in ${r.status}`)}throw new Error("Login cancelled")}s(d,"awaitFlow");async function v(e,t,a,r={}){const{flow:o,loopback:n}=await b(e,t,r.label,r.loopback??g()),i=s(()=>{e.cancelModelAuth(o.flow_id).catch(()=>{})},"cancel");if(p(a.signal))throw i(),new Error("Login cancelled");a.signal.addEventListener("abort",i,{once:!0});try{await A(e,o,n,a)}finally{a.signal.removeEventListener("abort",i)}}s(v,"daemonManagedLogin");async function A(e,t,a,r){if(t.user_code!==void 0){r.notify({type:"device_code",userCode:t.user_code,verificationUri:t.url,expiresInSeconds:900}),await d(e,t.flow_id,r.signal);return}r.notify({type:"auth_url",url:t.url,instructions:t.instructions});const o=t.provider===f;if(a&&!o){await d(e,t.flow_id,r.signal);return}if(a){const n=new AbortController,i=AbortSignal.any([r.signal,n.signal]),l=w(e,t,r,i);l.catch(()=>{});try{await Promise.race([d(e,t.flow_id,r.signal),l])}finally{n.abort()}return}if(o){await w(e,t,r,r.signal);return}for(;;){const n=await r.prompt({type:"manual_code",message:"Paste the code Claude shows"});try{await e.completeModelAuth(t.flow_id,{input:n});return}catch(i){if(i instanceof c&&i.status===400&&(i.param==="input"||i.param==="state")){r.notify({type:"info",message:`${i.message} \u2014 paste it again.`});continue}throw i}}}s(A,"drive");async function w(e,t,a,r){for(;;){const o=await a.prompt({type:"manual_code",message:"Paste the final redirect URL from your browser",placeholder:"http://127.0.0.1:1455/auth/callback?...",signal:r});try{await e.completeModelAuth(t.flow_id,{input:o});return}catch(n){if(n instanceof c&&n.status===400&&(n.param==="input"||n.param==="state")){a.notify({type:"info",message:`${n.message} \u2014 paste it again.`});continue}throw n}}}s(w,"pasteRedirect");export{v as daemonManagedLogin,g as localBrowserAvailable};
1
+ var m=Object.defineProperty;var s=(e,t)=>m(e,"name",{value:t,configurable:!0});import{ApiError as c}from"../../api/errors.js";import{OPENAI_CHATGPT_PROVIDER_ID as f}from"../managed-providers.js";function g(e=process.env){return e.SSH_CONNECTION||e.SSH_TTY?!1:process.platform==="darwin"||process.platform==="win32"?!0:!!(e.DISPLAY||e.WAYLAND_DISPLAY)}s(g,"localBrowserAvailable");function p(e){return e.aborted}s(p,"cancelled");function h(e,t){return t.aborted?Promise.reject(new Error("Login cancelled")):new Promise((a,r)=>{const o=s(()=>r(new Error("Login cancelled")),"onAbort");t.addEventListener("abort",o,{once:!0}),e.then(a,r).finally(()=>t.removeEventListener("abort",o))})}s(h,"raceAbort");function y(e){if(!(!(e instanceof c)||e.status!==404||!/no route for/.test(e.message)))return new Error("The running crtr daemon predates daemon-run sign-in. Run `crtr sys daemon restart`, then sign in again.")}s(y,"olderDaemon");async function u(e,t){try{return await e.startModelAuth(t)}catch(a){throw y(a)??a}}s(u,"startFlow");async function b(e,t,a,r){const o={provider:t===f?"openai-chatgpt":t,...a?{label:a}:{}};if(!r)return{flow:await u(e,o),loopback:!1};try{return{flow:await u(e,{...o,redirect:"loopback"}),loopback:!0}}catch(n){if(n instanceof c&&n.status===409&&n.param==="redirect")return{flow:await u(e,o),loopback:!1};throw n}}s(b,"start");async function d(e,t,a){for(;!p(a);){const r=await h(e.getModelAuthFlow(t,25),a);if(r.status==="connected")return;if(r.status!=="pending")throw new Error(r.message??`sign-in ${r.status}`)}throw new Error("Login cancelled")}s(d,"awaitFlow");async function v(e,t,a,r={}){const{flow:o,loopback:n}=await b(e,t,r.label,r.loopback??g()),i=s(()=>{e.cancelModelAuth(o.flow_id).catch(()=>{})},"cancel");if(p(a.signal))throw i(),new Error("Login cancelled");a.signal.addEventListener("abort",i,{once:!0});try{await A(e,o,n,a)}finally{a.signal.removeEventListener("abort",i)}}s(v,"daemonManagedLogin");async function A(e,t,a,r){if(t.user_code!==void 0){r.notify({type:"device_code",userCode:t.user_code,verificationUri:t.url,expiresInSeconds:900}),await d(e,t.flow_id,r.signal);return}r.notify({type:"auth_url",url:t.url,instructions:t.instructions});const o=t.provider===f;if(a&&!o){await d(e,t.flow_id,r.signal);return}if(a){const n=new AbortController,i=AbortSignal.any([r.signal,n.signal]),l=w(e,t,r,i);l.catch(()=>{});try{await Promise.race([d(e,t.flow_id,r.signal),l])}finally{n.abort()}return}if(o){await w(e,t,r,r.signal);return}for(;;){const n=await r.prompt({type:"manual_code",message:"Paste the code Claude shows"});try{await e.completeModelAuth(t.flow_id,{input:n});return}catch(i){if(i instanceof c&&i.status===400&&(i.param==="input"||i.param==="state")){r.notify({type:"info",message:`${i.message} \u2014 paste it again.`});continue}throw i}}}s(A,"drive");async function w(e,t,a,r){for(;;){const o=await a.prompt({type:"manual_code",message:"Paste the final redirect URL from your browser",placeholder:"http://127.0.0.1:1455/auth/callback?...",signal:r});try{await e.completeModelAuth(t.flow_id,{input:o});return}catch(n){if(n instanceof c&&n.status===400&&(n.param==="input"||n.param==="state")){a.notify({type:"info",message:`${n.message} \u2014 paste it again.`});continue}throw n}}}s(w,"pasteRedirect");export{v as daemonManagedLogin,g as localBrowserAvailable};
@@ -41,7 +41,7 @@ export declare function startModelAuthFlow(input: {
41
41
  /** `model_auth.cancel`: the person gave up at the CLI. Ends the flow so it stops
42
42
  * polling, frees its loopback port, and never writes a credential. Unknown or
43
43
  * finished flows are left alone. */
44
- export declare function cancelModelAuthFlow(flowId: string): FlowView | undefined;
44
+ export declare function cancelModelAuthFlow(flowId: string): Promise<FlowView | undefined>;
45
45
  /** `model_auth.complete` (RT-LLM-22, 31, 43, 49). Returns once the credential is
46
46
  * written; a vendor refusal ends the flow as failed and throws. */
47
47
  export declare function completeModelAuthFlow(flowId: unknown, input: unknown): Promise<{
@@ -56,15 +56,16 @@ export interface FlowView {
56
56
  message?: string;
57
57
  jti?: string;
58
58
  }
59
- /** A flow's current state, or undefined when it is unknown or long finished. */
60
- export declare function readModelAuthFlow(flowId: string): FlowView | undefined;
59
+ /** A flow's current state, or undefined when it is unknown or long finished. A flow this process does not hold but whose row exists is pending. */
60
+ export declare function readModelAuthFlow(flowId: string): Promise<FlowView | undefined>;
61
61
  /** How a finished flow failed, for the log and the redirect to the directory. An unknown or lost flow is a `runtime` failure. */
62
62
  export declare function modelAuthFlowFailure(flowId: string): {
63
63
  category: SignInFailureCategory;
64
64
  step?: string;
65
65
  fields: Record<string, EventField>;
66
66
  };
67
- /** Wait up to `waitMs` for a flow to leave `pending`; answers its state either way. */
67
+ /** Wait up to `waitMs` for a flow to leave `pending`; answers its state either way. A flow another API process holds is followed through its row:
68
+ * the row is deleted when that process settles the flow, and a provider that then holds an account answers `connected`, else `failed`. */
68
69
  export declare function waitModelAuthFlow(flowId: string, waitMs: number, signal?: AbortSignal): Promise<FlowView | undefined>;
69
70
  /** Test seam: forget every flow (a daemon restart does this by construction). */
70
71
  export declare function resetModelAuthFlowsForTest(): void;
@@ -1 +1 @@
1
- var Z=Object.defineProperty;var s=(e,t)=>Z(e,"name",{value:t,configurable:!0});import{createHash as W,randomBytes as C,randomUUID as Q}from"node:crypto";import{createServer as ee}from"node:http";import{RunError as u}from"../runs/operations.js";import{describeOutboundFailure as te}from"../events/outbound.js";import{ANTHROPIC_PROVIDER_ID as y,OPENAI_CHATGPT_PROVIDER_ID as w,OPENAI_CODEX_PROVIDER_ID as D,commitManagedLogin as ne,openAIDeviceId as re,readSubscriptionPool as ie}from"../subscription-state.js";const $=900,G="9d1c250a-e61b-44d9-88ed-5944d1962f5e",oe="https://claude.ai/oauth/authorize",ae="https://console.anthropic.com/oauth/code/callback",H={port:53692,path:"/callback",redirect:"http://localhost:53692/callback"},se="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",E="app_EMoamEEZ73f0CkXaXp7hrann",ce="https://auth.openai.com/codex/device",M="https://auth.openai.com/deviceauth/callback",F={port:1455,path:"/auth/callback",redirect:"http://localhost:1455/auth/callback"},de="openid profile email offline_access",ue=900,q="https://api.openai.com/auth",he="dynamic_agent_client",pe="crtr",le="https://auth.openai.com/api/accounts/authorize",j="https://api.openai.com/v1",b={port:1455,path:"/auth/callback",redirect:"http://127.0.0.1:1455/auth/callback"},O="chatgpt.tokens.use.direct",fe=`openid profile email offline_access resource.invoke ${O}`,_e=180*1e3,ge=s(()=>process.env.CRTR_MODEL_AUTH_ANTHROPIC_TOKEN_URL||"https://platform.claude.com/v1/oauth/token","anthropicTokenUrl"),S=s(()=>(process.env.CRTR_MODEL_AUTH_OPENAI_BASE||"https://auth.openai.com").replace(/\/$/,""),"openaiBase");class A extends u{static{s(this,"VendorSignInError")}category;step;url;httpStatus;constructor(t,n,i,o,r,c){super(502,"upstream_refused",o),this.category=t,this.step=n,this.url=i,this.httpStatus=c,r!==void 0&&(this.cause=r)}logFields(){return{category:this.category,...this.httpStatus===void 0?{}:{http_status:this.httpStatus},...te(this.cause??this,{step:this.step,url:this.url})}}}async function P(e,t,n){try{return await fetch(t,n)}catch(i){throw new A("network",e,t,`could not reach ${new URL(t).host}${new URL(t).pathname}: ${i instanceof Error?i.message:String(i)}`,i)}}s(P,"vendorFetch");async function x(e,t,n){let i;try{i=await n.text()}catch(o){throw new A("network",e,t,`connection to ${new URL(t).host}${new URL(t).pathname} dropped while reading the answer: ${o instanceof Error?o.message:String(o)}`,o)}try{return JSON.parse(i)}catch{throw _(e,t,`${new URL(t).host}${new URL(t).pathname} answered with a body that is not JSON`,n.status)}}s(x,"vendorJson");const _=s((e,t,n,i)=>new A("refused",e,t,n,void 0,i),"refusal"),g=new Map,T=new Map;function R(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}s(R,"base64url");function me(){const e=R(C(32));return{verifier:e,challenge:R(W("sha256").update(e).digest())}}s(me,"pkce");const z="openai-chatgpt";function J(e){if(e==="openai")return D;if(e===z)return w;if(e===y||e===D)return e;throw new u(400,"invalid_request","provider must be anthropic, openai-codex, or openai-chatgpt","provider")}s(J,"managedProvider");function Re(e){return e===w?z:e}s(Re,"modelAuthApiProvider");function Le(e){return J(e)}s(Le,"modelAuthProvider");function f(e,t,n,i){if(e.status==="pending"){e.status=t,n!==void 0&&(e.message=n),i instanceof A&&(e.failure=i),clearTimeout(e.timer),e.abort.abort(),e.server?.close(),e.server?.closeIdleConnections(),T.get(e.provider)===e.id&&T.delete(e.provider);for(const o of e.waiters)o();e.waiters.clear(),setTimeout(()=>{g.get(e.id)===e&&g.delete(e.id)},6e4).unref()}}s(f,"settle");async function L(e,t){if(e.status!=="pending")throw new Error("the sign-in ended before the credential was written");await ne(e.provider,e.label,{access:t.access,refresh:t.refresh,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{},...t.email?{email:t.email}:{},lastRateLimitedAt:e.lastRateLimitedAt,autoLabel:e.label===void 0})}s(L,"writeCredential");async function B(e,t,n){const i=ge(),o=await P("token_exchange",i,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({grant_type:"authorization_code",client_id:G,code:t,state:n,redirect_uri:e.redirectUri,code_verifier:e.verifier}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!o.ok)throw _("token_exchange",i,`Anthropic token exchange failed (${o.status})`,o.status);const r=await x("token_exchange",i,o);if(typeof r.access_token!="string"||typeof r.refresh_token!="string"||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in))throw _("token_exchange",i,"Anthropic token exchange returned no credential");await L(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-300*1e3})}s(B,"exchangeAnthropic");function we(e){const t=e.split(".")[1];let n;try{n=JSON.parse(Buffer.from(t??"","base64url").toString("utf8"))[q]?.chatgpt_account_id}catch{n=void 0}if(typeof n!="string"||n==="")throw new Error("OpenAI token carries no ChatGPT account id");return n}s(we,"openaiAccountId");async function V(e,t,n,i){const o=`${S()}/oauth/token`,r=await P("token_exchange",o,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:E,code:t,code_verifier:n,redirect_uri:i}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!r.ok)throw _("token_exchange",o,`OpenAI token exchange failed (${r.status})`,r.status);const c=await x("token_exchange",o,r);if(typeof c.access_token!="string"||typeof c.refresh_token!="string"||typeof c.expires_in!="number")throw _("token_exchange",o,"OpenAI token exchange returned no credential");await L(e,{access:c.access_token,refresh:c.refresh_token,expires:Date.now()+c.expires_in*1e3,accountId:we(c.access_token)})}s(V,"exchangeOpenAI");function K(e){try{const t=JSON.parse(Buffer.from(e?.split(".")[1]??"","base64url").toString("utf8"));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}s(K,"jwtPayload");function ye(...e){for(const t of e){const n=K(t)?.[q];if(typeof n?.chatgpt_account_id=="string"&&n.chatgpt_account_id)return n.chatgpt_account_id}}s(ye,"chatgptAccountId");function X(e,t){const n=e.searchParams.get("error");if(n)throw new u(400,"invalid_request",`ChatGPT authorization failed: ${n}`,"input");const i=e.searchParams.get("code");if(!i)throw new u(400,"invalid_request","the redirect URL has no authorization code","input");if(e.searchParams.get("state")!==t)throw new u(400,"invalid_request","the pasted redirect URL belongs to a different sign-in","state");const o=e.searchParams.get("client_id")?.trim();if(!o)throw new u(400,"invalid_request","the redirect URL carries no issued client id","input");return{code:i,clientId:o}}s(X,"chatgptCallback");async function Y(e,t,n){const i=`${S()}/api/accounts/oauth/token`,o=await P("token_exchange",i,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:n,code:t,code_verifier:e.verifier,redirect_uri:e.redirectUri,resource:j}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!o.ok)throw _("token_exchange",i,`ChatGPT token exchange failed (${o.status})`,o.status);const r=await x("token_exchange",i,o);if(typeof r.access_token!="string"||!r.access_token||typeof r.refresh_token!="string"||!r.refresh_token||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in)||r.expires_in<=0||typeof r.id_token!="string"||!r.id_token)throw _("token_exchange",i,"ChatGPT token exchange returned no credential");if(!(typeof r.scope=="string"?r.scope.trim().split(/\s+/):[]).includes(O))throw _("token_exchange",i,`ChatGPT sign-in did not grant ${O}`);const p=ye(r.access_token,r.id_token),a=K(r.id_token)?.email;await L(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-_e,clientId:n,...p?{accountId:p}:{},...typeof a=="string"&&a?{email:a}:{}})}s(Y,"exchangeChatGPT");function ve(e,t){return new Promise((n,i)=>{if(t.aborted){i(new Error("flow ended"));return}const o=s(()=>{clearTimeout(r),i(new Error("flow ended"))},"onAbort"),r=setTimeout(()=>{t.removeEventListener("abort",o),n()},e);t.addEventListener("abort",o,{once:!0})})}s(ve,"sleep");async function be(e,t){const n=Math.min(e.expiresAt,Date.now()+ue*1e3);let i=Math.max(1e3,Math.floor(t.intervalS*1e3));try{for(;Date.now()<n&&(await ve(Math.min(i,Math.max(0,n-Date.now())),e.abort.signal),!(Date.now()>=n));){const o=`${S()}/api/accounts/deviceauth/token`,r=await P("poll",o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:t.deviceAuthId,user_code:t.userCode}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(r.ok){const a=await x("poll",o,r);if(typeof a.authorization_code!="string"||typeof a.code_verifier!="string")throw _("poll",o,"OpenAI device approval returned no code");await V(e,a.authorization_code,a.code_verifier,M),f(e,"connected");return}if(r.status===403||r.status===404)continue;let c;try{c=await r.text()}catch(a){throw new A("network","poll",o,`connection to ${new URL(o).host}${new URL(o).pathname} dropped while reading the answer: ${a instanceof Error?a.message:String(a)}`,a)}let p;try{const a=JSON.parse(c).error;p=typeof a=="object"&&a!==null?a.code:a}catch{p=void 0}if(p!=="deviceauth_authorization_pending"){if(p==="slow_down"){i+=5e3;continue}throw _("poll",o,`OpenAI device sign-in failed (${r.status})`,r.status)}}f(e,"expired","the OpenAI code expired")}catch(o){e.status==="pending"&&f(e,"failed",o instanceof Error?o.message:String(o),o)}}s(be,"pollDevice");function U(e,t){const n=ee((i,o)=>{const r=new URL(i.url??"/","http://localhost"),c=s((l,h)=>{o.writeHead(l,{"Content-Type":"text/plain; charset=utf-8",Connection:"close"}),o.end(h)},"reply");if(r.pathname!==t.path){c(404,"Not found.");return}const p=r.searchParams.get("code");if(r.searchParams.get("state")!==e.state||!p){c(400,"Sign-in could not be completed: state mismatch or missing code.");return}let a="";if(e.provider===w)try{a=X(r,e.state).clientId}catch(l){c(400,`Sign-in could not be completed: ${l instanceof Error?l.message:String(l)}.`);return}if(e.status!=="pending"||e.exchanging){c(409,"This sign-in has already finished.");return}e.exchanging=!0,(e.provider===y?B(e,p,e.state):e.provider===w?Y(e,p,a):V(e,p,e.verifier,e.redirectUri)).then(()=>{c(200,"Signed in. You can close this window."),f(e,"connected")},l=>{c(502,"The vendor refused the sign-in."),f(e,"failed",l instanceof Error?l.message:String(l),l)})});return e.server=n,new Promise((i,o)=>{n.once("error",r=>o(new u(409,"conflict",`cannot listen on localhost:${t.port} for the vendor redirect (${r.code??r.message})`,"redirect"))),n.listen(t.port,"127.0.0.1",()=>i())})}s(U,"listenLoopback");async function Ue(e){const t=J(e.provider);if(e.redirect!==void 0&&e.redirect!=="loopback")throw new u(400,"invalid_request",'redirect must be "loopback" when present',"redirect");if(e.label!==void 0&&typeof e.label!="string")throw new u(400,"invalid_request","label must be a string when present","label");const n=typeof e.label=="string"&&e.label.trim()?e.label.trim():void 0,i=T.get(t);if(i!==void 0){const h=g.get(i);h&&f(h,"failed","replaced by a newer sign-in")}const{verifier:o,challenge:r}=me(),c=e.redirect==="loopback",p=Date.now()+$*1e3,a={id:Q(),provider:t,kind:c?"loopback":t===y?"code_page":t===w?"redirect_paste":"device",state:t===y?o:C(16).toString("hex"),verifier:o,redirectUri:t===y?c?H.redirect:ae:t===w?b.redirect:c?F.redirect:M,expiresAt:p,status:"pending",...e.jti?{jti:e.jti}:{},...n?{label:n}:{},lastRateLimitedAt:Math.max(0,...ie(t).map(h=>h.lastRateLimitedAt)),abort:new AbortController,timer:setTimeout(()=>f(a,"expired","the sign-in expired"),$*1e3),waiters:new Set,exchanging:!1};a.timer.unref(),Ae(a);const v=s(()=>new u(409,"conflict","a newer sign-in for this provider replaced this one","provider"),"replacedDuringStart"),l={flow_id:a.id,provider:t,expires_at:new Date(p).toISOString()};try{if(t===y){const d=new URLSearchParams;if(d.set("code","true"),d.set("client_id",G),d.set("response_type","code"),d.set("redirect_uri",a.redirectUri),d.set("scope",se),d.set("code_challenge",r),d.set("code_challenge_method","S256"),d.set("state",a.state),c&&await U(a,H),a.status!=="pending")throw v();return{...l,url:`${oe}?${d.toString()}`,instructions:c?"Sign in to Claude in your browser; this finishes by itself.":"Sign in to Claude, then copy the code Claude shows and paste it here."}}if(t===w){const d=new URL(le);if(d.search=new URLSearchParams({client_id:he,agent_name_hint:pe,ext_agent_host_id:`urn:uuid:${re()}`,response_type:"code",redirect_uri:a.redirectUri,resource:j,scope:fe,state:a.state,code_challenge:r,code_challenge_method:"S256",nonce:R(C(32))}).toString(),c&&await U(a,b),a.status!=="pending")throw v();return{...l,url:d.toString(),instructions:c?"Sign in with ChatGPT in your browser; this finishes by itself. If it does not, paste the final redirect URL here.":`Sign in with ChatGPT, then copy the full URL your browser lands on (it starts with ${b.redirect} and may show a connection error) and paste it here.`}}if(c){const d=new URL("https://auth.openai.com/oauth/authorize");if(d.searchParams.set("response_type","code"),d.searchParams.set("client_id",E),d.searchParams.set("redirect_uri",a.redirectUri),d.searchParams.set("scope",de),d.searchParams.set("code_challenge",r),d.searchParams.set("code_challenge_method","S256"),d.searchParams.set("state",a.state),d.searchParams.set("id_token_add_organizations","true"),d.searchParams.set("codex_cli_simplified_flow","true"),d.searchParams.set("originator","pi"),await U(a,F),a.status!=="pending")throw v();return{...l,url:d.toString(),instructions:"Sign in to ChatGPT in your browser; this finishes by itself."}}const h=`${S()}/api/accounts/deviceauth/usercode`,I=await P("device_code_start",h,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:E}),signal:AbortSignal.timeout(3e4)});if(!I.ok)throw _("device_code_start",h,`OpenAI refused to issue a device code (${I.status})`,I.status);const m=await x("device_code_start",h,I),k=typeof m.interval=="string"?Number(m.interval.trim()):m.interval;if(typeof m.device_auth_id!="string"||typeof m.user_code!="string"||typeof k!="number"||!Number.isFinite(k)||k<0)throw _("device_code_start",h,"OpenAI returned an invalid device code");if(a.status!=="pending")throw v();return be(a,{deviceAuthId:m.device_auth_id,userCode:m.user_code,intervalS:k}),{...l,url:ce,user_code:m.user_code,instructions:"Open the link, sign in to ChatGPT, and enter the code. The code expires in 15 minutes."}}catch(h){throw f(a,"failed",h instanceof Error?h.message:String(h),h),g.delete(a.id),h instanceof u?h:new u(502,"upstream_refused",`vendor sign-in could not start: ${h instanceof Error?h.message:String(h)}`)}}s(Ue,"startModelAuthFlow");function Ae(e){g.set(e.id,e),T.set(e.provider,e.id)}s(Ae,"register");function Ne(e){const t=g.get(e);if(t)return f(t,"failed","cancelled"),N(t)}s(Ne,"cancelModelAuthFlow");function Pe(e){const t=e.trim();if(!t)return{};try{const n=new URL(t);return{code:n.searchParams.get("code")??void 0,state:n.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[n,i]=t.split("#",2);return{code:n,state:i}}if(t.includes("code=")){const n=new URLSearchParams(t);return{code:n.get("code")??void 0,state:n.get("state")??void 0}}return{code:t}}s(Pe,"parseCodeInput");function xe(e){const t=typeof e=="string"?g.get(e):void 0;if(!t||t.status!=="pending")throw new u(400,"invalid_request","no in-flight sign-in has this flow_id","flow_id");return t}s(xe,"pendingFlow");async function De(e,t){const n=xe(e);if(n.provider===w)return Te(n,t);if(n.kind!=="code_page")throw new u(400,"invalid_request","this sign-in completes without a pasted code","flow_id");if(n.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");if(typeof t!="string")throw new u(400,"invalid_request","input must be the code Claude shows","input");const{code:i,state:o}=Pe(t);if(!i)throw new u(400,"invalid_request","input must be the code Claude shows","input");if(o!==void 0&&o!==n.state)throw new u(400,"invalid_request","the pasted code belongs to a different sign-in","state");n.exchanging=!0;try{await B(n,i,o??n.state)}catch(r){throw f(n,"failed",r instanceof Error?r.message:String(r),r),new u(502,"upstream_refused","Anthropic refused the code")}return f(n,"connected"),{flow_id:n.id,provider:n.provider,status:"connected"}}s(De,"completeModelAuthFlow");async function Te(e,t){if(e.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");const n=new URL(b.redirect);let i;try{i=typeof t=="string"?new URL(t.trim()):void 0}catch{i=void 0}if(!i||i.origin!==n.origin||i.pathname!==n.pathname)throw new u(400,"invalid_request",`input must be the full redirect URL starting with ${b.redirect}`,"input");const{code:o,clientId:r}=X(i,e.state);e.exchanging=!0;try{await Y(e,o,r)}catch(c){throw f(e,"failed",c instanceof Error?c.message:String(c),c),new u(502,"upstream_refused","OpenAI refused the ChatGPT sign-in")}return f(e,"connected"),{flow_id:e.id,provider:e.provider,status:"connected"}}s(Te,"completeChatGPTFlow");function N(e){return{flow_id:e.id,provider:e.provider,status:e.status,...e.message?{message:e.message}:{},...e.jti?{jti:e.jti}:{}}}s(N,"view");function $e(e){const t=g.get(e);return t?N(t):void 0}s($e,"readModelAuthFlow");function Ge(e){const t=g.get(e);return t?.status==="expired"?{category:"expired",fields:{category:"expired"}}:t?.failure?{category:t.failure.category,step:t.failure.step,fields:t.failure.logFields()}:{category:"runtime",fields:{category:"runtime"}}}s(Ge,"modelAuthFlowFailure");async function He(e,t,n){const i=g.get(e);if(i)return i.status==="pending"&&t>0&&!n?.aborted&&await new Promise(o=>{const r=s(()=>{clearTimeout(c),n?.removeEventListener("abort",r),i.waiters.delete(r),o()},"done"),c=setTimeout(r,t);n?.addEventListener("abort",r,{once:!0}),i.waiters.add(r)}),N(i)}s(He,"waitModelAuthFlow");function Me(){for(const e of g.values())f(e,"failed","reset");g.clear(),T.clear()}s(Me,"resetModelAuthFlowsForTest");export{$ as MODEL_AUTH_FLOW_TTL_S,z as OPENAI_CHATGPT_API_PROVIDER,A as VendorSignInError,Ne as cancelModelAuthFlow,De as completeModelAuthFlow,Re as modelAuthApiProvider,Ge as modelAuthFlowFailure,Le as modelAuthProvider,$e as readModelAuthFlow,Me as resetModelAuthFlowsForTest,Ue as startModelAuthFlow,He as waitModelAuthFlow};
1
+ var he=Object.defineProperty;var o=(e,t)=>he(e,"name",{value:t,configurable:!0});import{createHash as z,randomBytes as V,randomUUID as le}from"node:crypto";import{createServer as _e}from"node:http";import{RunError as p}from"../runs/operations.js";import{openDb as O}from"../canvas/db.js";import{personIdAsync as R,runAsPerson as fe}from"../canvas/person.js";import{loadSecretsKey as N,open as B,seal as ge}from"../secrets/index.js";import{describeOutboundFailure as me}from"../events/outbound.js";import{ANTHROPIC_PROVIDER_ID as m,OPENAI_CHATGPT_PROVIDER_ID as w,OPENAI_CODEX_PROVIDER_ID as W,commitManagedLogin as we,openAIDeviceId as ye,readSubscriptionPool as U}from"../subscription-state.js";const K=900,X="9d1c250a-e61b-44d9-88ed-5944d1962f5e",ve="https://claude.ai/oauth/authorize",Y="https://console.anthropic.com/oauth/code/callback",Z={port:53692,path:"/callback",redirect:"http://localhost:53692/callback"},be="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",$="app_EMoamEEZ73f0CkXaXp7hrann",Ae="https://auth.openai.com/codex/device",Q="https://auth.openai.com/deviceauth/callback",ee={port:1455,path:"/auth/callback",redirect:"http://localhost:1455/auth/callback"},xe="openid profile email offline_access",Pe=900,te="https://api.openai.com/auth",Se="dynamic_agent_client",Ee="crtr",Te="https://auth.openai.com/api/accounts/authorize",ne="https://api.openai.com/v1",b={port:1455,path:"/auth/callback",redirect:"http://127.0.0.1:1455/auth/callback"},M="chatgpt.tokens.use.direct",ke=`openid profile email offline_access resource.invoke ${M}`,Oe=180*1e3,Re=o(()=>process.env.CRTR_MODEL_AUTH_ANTHROPIC_TOKEN_URL||"https://platform.claude.com/v1/oauth/token","anthropicTokenUrl"),I=o(()=>(process.env.CRTR_MODEL_AUTH_OPENAI_BASE||"https://auth.openai.com").replace(/\/$/,""),"openaiBase");class A extends p{static{o(this,"VendorSignInError")}category;step;url;httpStatus;constructor(t,n,r,a,i,s){super(502,"upstream_refused",a),this.category=t,this.step=n,this.url=r,this.httpStatus=s,i!==void 0&&(this.cause=i)}logFields(){return{category:this.category,...this.httpStatus===void 0?{}:{http_status:this.httpStatus},...me(this.cause??this,{step:this.step,url:this.url})}}}async function x(e,t,n){try{return await fetch(t,n)}catch(r){throw new A("network",e,t,`could not reach ${new URL(t).host}${new URL(t).pathname}: ${r instanceof Error?r.message:String(r)}`,r)}}o(x,"vendorFetch");async function P(e,t,n){let r;try{r=await n.text()}catch(a){throw new A("network",e,t,`connection to ${new URL(t).host}${new URL(t).pathname} dropped while reading the answer: ${a instanceof Error?a.message:String(a)}`,a)}try{return JSON.parse(r)}catch{throw f(e,t,`${new URL(t).host}${new URL(t).pathname} answered with a body that is not JSON`,n.status)}}o(P,"vendorJson");const f=o((e,t,n,r)=>new A("refused",e,t,n,void 0,r),"refusal"),g=new Map,S=new Map,C=o((e,t)=>`${e}|${t}`,"providerKey"),F=o((e,t)=>`model_sign_in_flows|${e}|${t}`,"flowAad"),re=o(e=>z("sha256").update(`state:${e}`).digest("hex").slice(0,32),"openaiState");async function Ie(e){const t=e.person,{sealed:n,keyId:r}=ge(JSON.stringify({verifier:e.verifier,...e.label?{label:e.label}:{},...e.jti?{jti:e.jti}:{}}),N(),F(t,e.id)),a=await O();await a.prepare("DELETE FROM model_sign_in_flows WHERE person = ? AND expires_at < ?").run(t,new Date().toISOString()),await a.prepare("INSERT INTO model_sign_in_flows (flow_id, provider, person, verifier_sealed, key_id, expires_at) VALUES (?, ?, ?, ?, ?, ?)").run(e.id,e.provider,t,n,r,new Date(e.expiresAt).toISOString())}o(Ie,"writeFlowRow");async function Ce(e){const t=await R(),n=await(await O()).prepare("SELECT provider, verifier_sealed, key_id, expires_at FROM model_sign_in_flows WHERE flow_id = ? AND person = ?").get(e,t);if(!n||Date.parse(n.expires_at)<=Date.now())return;const r=JSON.parse(B(n.verifier_sealed,n.key_id,N(),F(t,e))),a=n.provider;if(a!==m&&a!==w)return;const i={id:e,person:t,provider:a,kind:a===m?"code_page":"redirect_paste",state:a===m?r.verifier:re(r.verifier),verifier:r.verifier,redirectUri:a===m?Y:b.redirect,expiresAt:Date.parse(n.expires_at),status:"pending",...r.jti?{jti:r.jti}:{},...r.label?{label:r.label}:{},lastRateLimitedAt:Math.max(0,...(await U(a)).map(s=>s.lastRateLimitedAt)),abort:new AbortController,timer:setTimeout(()=>_(i,"expired","the sign-in expired"),Math.max(0,Date.parse(n.expires_at)-Date.now())),waiters:new Set,exchanging:!1};return i.timer.unref(),i}o(Ce,"readFlowRow");function H(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}o(H,"base64url");function Le(){const e=H(V(32));return{verifier:e,challenge:H(z("sha256").update(e).digest())}}o(Le,"pkce");const ie="openai-chatgpt";function ae(e){if(e==="openai")return W;if(e===ie)return w;if(e===m||e===W)return e;throw new p(400,"invalid_request","provider must be anthropic, openai-codex, or openai-chatgpt","provider")}o(ae,"managedProvider");function Ze(e){return e===w?ie:e}o(Ze,"modelAuthApiProvider");function Qe(e){return ae(e)}o(Qe,"modelAuthProvider");function _(e,t,n,r){if(e.status==="pending"){e.status=t,n!==void 0&&(e.message=n),r instanceof A&&(e.failure=r),clearTimeout(e.timer),e.abort.abort(),e.server?.close(),e.server?.closeIdleConnections(),S.get(C(e.person,e.provider))===e.id&&S.delete(C(e.person,e.provider)),O().then(async a=>{await a.prepare("DELETE FROM model_sign_in_flows WHERE flow_id = ? AND person = ?").run(e.id,e.person)}).catch(()=>{});for(const a of e.waiters)a();e.waiters.clear(),setTimeout(()=>{g.get(e.id)===e&&g.delete(e.id)},6e4).unref()}}o(_,"settle");async function G(e,t){if(e.status!=="pending")throw new Error("the sign-in ended before the credential was written");await fe(e.person,()=>we(e.provider,e.label,{access:t.access,refresh:t.refresh,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{},...t.email?{email:t.email}:{},lastRateLimitedAt:e.lastRateLimitedAt,autoLabel:e.label===void 0}))}o(G,"writeCredential");async function oe(e,t,n){const r=Re(),a=await x("token_exchange",r,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({grant_type:"authorization_code",client_id:X,code:t,state:n,redirect_uri:e.redirectUri,code_verifier:e.verifier}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!a.ok)throw f("token_exchange",r,`Anthropic token exchange failed (${a.status})`,a.status);const i=await P("token_exchange",r,a);if(typeof i.access_token!="string"||typeof i.refresh_token!="string"||typeof i.expires_in!="number"||!Number.isFinite(i.expires_in))throw f("token_exchange",r,"Anthropic token exchange returned no credential");await G(e,{access:i.access_token,refresh:i.refresh_token,expires:Date.now()+i.expires_in*1e3-300*1e3})}o(oe,"exchangeAnthropic");function De(e){const t=e.split(".")[1];let n;try{n=JSON.parse(Buffer.from(t??"","base64url").toString("utf8"))[te]?.chatgpt_account_id}catch{n=void 0}if(typeof n!="string"||n==="")throw new Error("OpenAI token carries no ChatGPT account id");return n}o(De,"openaiAccountId");async function se(e,t,n,r){const a=`${I()}/oauth/token`,i=await x("token_exchange",a,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:$,code:t,code_verifier:n,redirect_uri:r}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!i.ok)throw f("token_exchange",a,`OpenAI token exchange failed (${i.status})`,i.status);const s=await P("token_exchange",a,i);if(typeof s.access_token!="string"||typeof s.refresh_token!="string"||typeof s.expires_in!="number")throw f("token_exchange",a,"OpenAI token exchange returned no credential");await G(e,{access:s.access_token,refresh:s.refresh_token,expires:Date.now()+s.expires_in*1e3,accountId:De(s.access_token)})}o(se,"exchangeOpenAI");function ce(e){try{const t=JSON.parse(Buffer.from(e?.split(".")[1]??"","base64url").toString("utf8"));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}o(ce,"jwtPayload");function Ne(...e){for(const t of e){const n=ce(t)?.[te];if(typeof n?.chatgpt_account_id=="string"&&n.chatgpt_account_id)return n.chatgpt_account_id}}o(Ne,"chatgptAccountId");function de(e,t){const n=e.searchParams.get("error");if(n)throw new p(400,"invalid_request",`ChatGPT authorization failed: ${n}`,"input");const r=e.searchParams.get("code");if(!r)throw new p(400,"invalid_request","the redirect URL has no authorization code","input");if(e.searchParams.get("state")!==t)throw new p(400,"invalid_request","the pasted redirect URL belongs to a different sign-in","state");const a=e.searchParams.get("client_id")?.trim();if(!a)throw new p(400,"invalid_request","the redirect URL carries no issued client id","input");return{code:r,clientId:a}}o(de,"chatgptCallback");async function ue(e,t,n){const r=`${I()}/api/accounts/oauth/token`,a=await x("token_exchange",r,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:n,code:t,code_verifier:e.verifier,redirect_uri:e.redirectUri,resource:ne}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!a.ok)throw f("token_exchange",r,`ChatGPT token exchange failed (${a.status})`,a.status);const i=await P("token_exchange",r,a);if(typeof i.access_token!="string"||!i.access_token||typeof i.refresh_token!="string"||!i.refresh_token||typeof i.expires_in!="number"||!Number.isFinite(i.expires_in)||i.expires_in<=0||typeof i.id_token!="string"||!i.id_token)throw f("token_exchange",r,"ChatGPT token exchange returned no credential");if(!(typeof i.scope=="string"?i.scope.trim().split(/\s+/):[]).includes(M))throw f("token_exchange",r,`ChatGPT sign-in did not grant ${M}`);const l=Ne(i.access_token,i.id_token),d=ce(i.id_token)?.email;await G(e,{access:i.access_token,refresh:i.refresh_token,expires:Date.now()+i.expires_in*1e3-Oe,clientId:n,...l?{accountId:l}:{},...typeof d=="string"&&d?{email:d}:{}})}o(ue,"exchangeChatGPT");function Ue(e,t){return new Promise((n,r)=>{if(t.aborted){r(new Error("flow ended"));return}const a=o(()=>{clearTimeout(i),r(new Error("flow ended"))},"onAbort"),i=setTimeout(()=>{t.removeEventListener("abort",a),n()},e);t.addEventListener("abort",a,{once:!0})})}o(Ue,"sleep");async function $e(e,t){const n=Math.min(e.expiresAt,Date.now()+Pe*1e3);let r=Math.max(1e3,Math.floor(t.intervalS*1e3));try{for(;Date.now()<n&&(await Ue(Math.min(r,Math.max(0,n-Date.now())),e.abort.signal),!(Date.now()>=n));){const a=`${I()}/api/accounts/deviceauth/token`,i=await x("poll",a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:t.deviceAuthId,user_code:t.userCode}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(i.ok){const d=await P("poll",a,i);if(typeof d.authorization_code!="string"||typeof d.code_verifier!="string")throw f("poll",a,"OpenAI device approval returned no code");await se(e,d.authorization_code,d.code_verifier,Q),_(e,"connected");return}if(i.status===403||i.status===404)continue;let s;try{s=await i.text()}catch(d){throw new A("network","poll",a,`connection to ${new URL(a).host}${new URL(a).pathname} dropped while reading the answer: ${d instanceof Error?d.message:String(d)}`,d)}let l;try{const d=JSON.parse(s).error;l=typeof d=="object"&&d!==null?d.code:d}catch{l=void 0}if(l!=="deviceauth_authorization_pending"){if(l==="slow_down"){r+=5e3;continue}throw f("poll",a,`OpenAI device sign-in failed (${i.status})`,i.status)}}_(e,"expired","the OpenAI code expired")}catch(a){e.status==="pending"&&_(e,"failed",a instanceof Error?a.message:String(a),a)}}o($e,"pollDevice");function j(e,t){const n=_e((r,a)=>{const i=new URL(r.url??"/","http://localhost"),s=o((c,v)=>{a.writeHead(c,{"Content-Type":"text/plain; charset=utf-8",Connection:"close"}),a.end(v)},"reply");if(i.pathname!==t.path){s(404,"Not found.");return}const l=i.searchParams.get("code");if(i.searchParams.get("state")!==e.state||!l){s(400,"Sign-in could not be completed: state mismatch or missing code.");return}let d="";if(e.provider===w)try{d=de(i,e.state).clientId}catch(c){s(400,`Sign-in could not be completed: ${c instanceof Error?c.message:String(c)}.`);return}if(e.status!=="pending"||e.exchanging){s(409,"This sign-in has already finished.");return}e.exchanging=!0,(e.provider===m?oe(e,l,e.state):e.provider===w?ue(e,l,d):se(e,l,e.verifier,e.redirectUri)).then(()=>{s(200,"Signed in. You can close this window."),_(e,"connected")},c=>{s(502,"The vendor refused the sign-in."),_(e,"failed",c instanceof Error?c.message:String(c),c)})});return e.server=n,new Promise((r,a)=>{n.once("error",i=>a(new p(409,"conflict",`cannot listen on localhost:${t.port} for the vendor redirect (${i.code??i.message})`,"redirect"))),n.listen(t.port,"127.0.0.1",()=>r())})}o(j,"listenLoopback");async function et(e){const t=ae(e.provider);if(e.redirect!==void 0&&e.redirect!=="loopback")throw new p(400,"invalid_request",'redirect must be "loopback" when present',"redirect");if(e.label!==void 0&&typeof e.label!="string")throw new p(400,"invalid_request","label must be a string when present","label");const n=typeof e.label=="string"&&e.label.trim()?e.label.trim():void 0,r=await R(),a=Math.max(0,...(await U(t)).map(h=>h.lastRateLimitedAt)),i=S.get(C(r,t));if(i!==void 0){const h=g.get(i);h&&_(h,"failed","replaced by a newer sign-in")}const{verifier:s,challenge:l}=Le(),d=e.redirect==="loopback",D=Date.now()+K*1e3,c={id:le(),person:r,provider:t,kind:d?"loopback":t===m?"code_page":t===w?"redirect_paste":"device",state:t===m?s:re(s),verifier:s,redirectUri:t===m?d?Z.redirect:Y:t===w?b.redirect:d?ee.redirect:Q,expiresAt:D,status:"pending",...e.jti?{jti:e.jti}:{},...n?{label:n}:{},lastRateLimitedAt:a,abort:new AbortController,timer:setTimeout(()=>_(c,"expired","the sign-in expired"),K*1e3),waiters:new Set,exchanging:!1};c.timer.unref(),pe(c),await Ie(c);const v=o(()=>new p(409,"conflict","a newer sign-in for this provider replaced this one","provider"),"replacedDuringStart"),E={flow_id:c.id,provider:t,expires_at:new Date(D).toISOString()};try{if(t===m){const u=new URLSearchParams;if(u.set("code","true"),u.set("client_id",X),u.set("response_type","code"),u.set("redirect_uri",c.redirectUri),u.set("scope",be),u.set("code_challenge",l),u.set("code_challenge_method","S256"),u.set("state",c.state),d&&await j(c,Z),c.status!=="pending")throw v();return{...E,url:`${ve}?${u.toString()}`,instructions:d?"Sign in to Claude in your browser; this finishes by itself.":"Sign in to Claude, then copy the code Claude shows and paste it here."}}if(t===w){const u=new URL(Te);if(u.search=new URLSearchParams({client_id:Se,agent_name_hint:Ee,ext_agent_host_id:`urn:uuid:${await ye()}`,response_type:"code",redirect_uri:c.redirectUri,resource:ne,scope:ke,state:c.state,code_challenge:l,code_challenge_method:"S256",nonce:H(V(32))}).toString(),d&&await j(c,b),c.status!=="pending")throw v();return{...E,url:u.toString(),instructions:d?"Sign in with ChatGPT in your browser; this finishes by itself. If it does not, paste the final redirect URL here.":`Sign in with ChatGPT, then copy the full URL your browser lands on (it starts with ${b.redirect} and may show a connection error) and paste it here.`}}if(d){const u=new URL("https://auth.openai.com/oauth/authorize");if(u.searchParams.set("response_type","code"),u.searchParams.set("client_id",$),u.searchParams.set("redirect_uri",c.redirectUri),u.searchParams.set("scope",xe),u.searchParams.set("code_challenge",l),u.searchParams.set("code_challenge_method","S256"),u.searchParams.set("state",c.state),u.searchParams.set("id_token_add_organizations","true"),u.searchParams.set("codex_cli_simplified_flow","true"),u.searchParams.set("originator","pi"),await j(c,ee),c.status!=="pending")throw v();return{...E,url:u.toString(),instructions:"Sign in to ChatGPT in your browser; this finishes by itself."}}const h=`${I()}/api/accounts/deviceauth/usercode`,T=await x("device_code_start",h,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:$}),signal:AbortSignal.timeout(3e4)});if(!T.ok)throw f("device_code_start",h,`OpenAI refused to issue a device code (${T.status})`,T.status);const y=await P("device_code_start",h,T),k=typeof y.interval=="string"?Number(y.interval.trim()):y.interval;if(typeof y.device_auth_id!="string"||typeof y.user_code!="string"||typeof k!="number"||!Number.isFinite(k)||k<0)throw f("device_code_start",h,"OpenAI returned an invalid device code");if(c.status!=="pending")throw v();return $e(c,{deviceAuthId:y.device_auth_id,userCode:y.user_code,intervalS:k}),{...E,url:Ae,user_code:y.user_code,instructions:"Open the link, sign in to ChatGPT, and enter the code. The code expires in 15 minutes."}}catch(h){throw _(c,"failed",h instanceof Error?h.message:String(h),h),g.delete(c.id),h instanceof p?h:new p(502,"upstream_refused",`vendor sign-in could not start: ${h instanceof Error?h.message:String(h)}`)}}o(et,"startModelAuthFlow");async function L(e){const t=g.get(e);return t&&t.person===await R()?t:void 0}o(L,"ownFlow");function pe(e){g.set(e.id,e),S.set(C(e.person,e.provider),e.id)}o(pe,"register");async function tt(e){const t=await L(e);if(t)return _(t,"failed","cancelled"),q(t)}o(tt,"cancelModelAuthFlow");function Me(e){const t=e.trim();if(!t)return{};try{const n=new URL(t);return{code:n.searchParams.get("code")??void 0,state:n.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[n,r]=t.split("#",2);return{code:n,state:r}}if(t.includes("code=")){const n=new URLSearchParams(t);return{code:n.get("code")??void 0,state:n.get("state")??void 0}}return{code:t}}o(Me,"parseCodeInput");async function Fe(e){let t=typeof e=="string"?await L(e):void 0;if(!t&&typeof e=="string"&&!g.has(e)&&(t=await Ce(e),t&&pe(t)),!t||t.status!=="pending")throw new p(400,"invalid_request","no in-flight sign-in has this flow_id","flow_id");return t}o(Fe,"pendingFlow");async function nt(e,t){const n=await Fe(e);if(n.provider===w)return He(n,t);if(n.kind!=="code_page")throw new p(400,"invalid_request","this sign-in completes without a pasted code","flow_id");if(n.exchanging)throw new p(400,"invalid_request","this sign-in is already being completed","flow_id");if(typeof t!="string")throw new p(400,"invalid_request","input must be the code Claude shows","input");const{code:r,state:a}=Me(t);if(!r)throw new p(400,"invalid_request","input must be the code Claude shows","input");if(a!==void 0&&a!==n.state)throw new p(400,"invalid_request","the pasted code belongs to a different sign-in","state");n.exchanging=!0;try{await oe(n,r,a??n.state)}catch(i){throw _(n,"failed",i instanceof Error?i.message:String(i),i),new p(502,"upstream_refused","Anthropic refused the code")}return _(n,"connected"),{flow_id:n.id,provider:n.provider,status:"connected"}}o(nt,"completeModelAuthFlow");async function He(e,t){if(e.exchanging)throw new p(400,"invalid_request","this sign-in is already being completed","flow_id");const n=new URL(b.redirect);let r;try{r=typeof t=="string"?new URL(t.trim()):void 0}catch{r=void 0}if(!r||r.origin!==n.origin||r.pathname!==n.pathname)throw new p(400,"invalid_request",`input must be the full redirect URL starting with ${b.redirect}`,"input");const{code:a,clientId:i}=de(r,e.state);e.exchanging=!0;try{await ue(e,a,i)}catch(s){throw _(e,"failed",s instanceof Error?s.message:String(s),s),new p(502,"upstream_refused","OpenAI refused the ChatGPT sign-in")}return _(e,"connected"),{flow_id:e.id,provider:e.provider,status:"connected"}}o(He,"completeChatGPTFlow");function q(e){return{flow_id:e.id,provider:e.provider,status:e.status,...e.message?{message:e.message}:{},...e.jti?{jti:e.jti}:{}}}o(q,"view");async function J(e){const t=await R(),n=await(await O()).prepare("SELECT provider, verifier_sealed, key_id, expires_at FROM model_sign_in_flows WHERE flow_id = ? AND person = ?").get(e,t);if(!n||Date.parse(n.expires_at)<=Date.now())return;const r=JSON.parse(B(n.verifier_sealed,n.key_id,N(),F(t,e)));return{flow_id:e,provider:n.provider,status:"pending",...r.jti?{jti:r.jti}:{}}}o(J,"pendingRowView");async function rt(e){const t=await L(e);return t?q(t):g.has(e)?void 0:await J(e)}o(rt,"readModelAuthFlow");function it(e){const t=g.get(e);return t?.status==="expired"?{category:"expired",fields:{category:"expired"}}:t?.failure?{category:t.failure.category,step:t.failure.step,fields:t.failure.logFields()}:{category:"runtime",fields:{category:"runtime"}}}o(it,"modelAuthFlowFailure");async function at(e,t,n){const r=await L(e);return r?(r.status==="pending"&&t>0&&!n?.aborted&&await new Promise(a=>{const i=o(()=>{clearTimeout(s),n?.removeEventListener("abort",i),r.waiters.delete(i),a()},"done"),s=setTimeout(i,t);n?.addEventListener("abort",i,{once:!0}),r.waiters.add(i)}),q(r)):g.has(e)?void 0:await je(e,t,n)}o(at,"waitModelAuthFlow");const Ge=1e3;async function je(e,t,n){const r=await J(e);if(!r)return;const a=Date.now()+t;for(;!n?.aborted&&Date.now()<a&&(await new Promise(i=>setTimeout(i,Math.min(Ge,Math.max(0,a-Date.now())))),!n?.aborted);)if(!await J(e)){const i=(await U(r.provider)).length>0;return{...r,status:i?"connected":"failed"}}return r}o(je,"waitRowFlow");function ot(){for(const e of g.values())_(e,"failed","reset");g.clear(),S.clear()}o(ot,"resetModelAuthFlowsForTest");export{K as MODEL_AUTH_FLOW_TTL_S,ie as OPENAI_CHATGPT_API_PROVIDER,A as VendorSignInError,tt as cancelModelAuthFlow,nt as completeModelAuthFlow,Ze as modelAuthApiProvider,it as modelAuthFlowFailure,Qe as modelAuthProvider,rt as readModelAuthFlow,ot as resetModelAuthFlowsForTest,et as startModelAuthFlow,at as waitModelAuthFlow};
@@ -1,4 +1,3 @@
1
- export declare function handoffJtiConsumed(jti: string): boolean;
2
- /** Record `jti` as consumed until `exp`; false when it already was. Expired
3
- * entries are pruned on every write. */
4
- export declare function consumeHandoffJti(jti: string, exp: number): boolean;
1
+ export declare function handoffJtiConsumed(jti: string): Promise<boolean>;
2
+ /** Record `jti` as consumed until `exp`; false when it already was. Expired rows are pruned on every write. */
3
+ export declare function consumeHandoffJti(jti: string, exp: number): Promise<boolean>;
@@ -1 +1 @@
1
- var c=Object.defineProperty;var e=(t,n)=>c(t,"name",{value:n,configurable:!0});import{join as u}from"node:path";import{atomicWriteJson as a,readJsonIfExists as m}from"../fs-utils.js";import{secretsDir as d}from"../layout.js";function i(){return u(d(),"vendor-handoff-jti.json")}e(i,"jtiPath");function f(t){const n=m(i())??{},o={};for(const[s,r]of Object.entries(n))typeof r=="number"&&r>t&&(o[s]=r);return o}e(f,"readLive");function b(t){return Object.hasOwn(f(Date.now()/1e3),t)}e(b,"handoffJtiConsumed");function v(t,n){const o=f(Date.now()/1e3);return Object.hasOwn(o,t)?!1:(o[t]=n,a(i(),o,{mode:384}),!0)}e(v,"consumeHandoffJti");export{v as consumeHandoffJti,b as handoffJtiConsumed};
1
+ var u=Object.defineProperty;var n=(e,a)=>u(e,"name",{value:a,configurable:!0});import{openDb as r}from"../canvas/db.js";const t="handoff_jti|";async function s(e){const a=await(await r()).prepare("SELECT value FROM canvas_meta WHERE key = ?").get(`${t}${e}`);return a!==void 0&&Number(a.value)>Date.now()/1e3}n(s,"handoffJtiConsumed");async function f(e,a){const o=await r();await o.prepare("DELETE FROM canvas_meta WHERE key LIKE ? AND CAST(value AS DOUBLE PRECISION) <= ?").run(`${t}%`,Date.now()/1e3);const{changes:E}=await o.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT (key) DO NOTHING").run(`${t}${e}`,String(a));return E>0}n(f,"consumeHandoffJti");export{f as consumeHandoffJti,s as handoffJtiConsumed};
@@ -1 +1 @@
1
- var w=Object.defineProperty;var l=(e,t)=>w(e,"name",{value:t,configurable:!0});import{join as g}from"node:path";import{ApiError as c}from"../../api/index.js";import{secretsDir as v}from"../layout.js";import{PoolCredentialStore as y}from"../pool-credential-store.js";import{resolveLaunchTarget as _}from"../runtime/launch-target.js";import{coolingUntilFor as p,isManagedProvider as h,readSubscriptionPool as k}from"../subscription-state.js";function S(e){const t=e.indexOf("/"),o=t>0?e.slice(0,t):"";if(o==="")throw new c(422,"unresolved_model","the selected launch has no concrete model provider");return o}l(S,"providerFromModel");async function O(e){const o=(await _({root:!0,...e.profile!=null?{profile:e.profile}:{},...e.cwd!=null?{cwd:e.cwd}:{},...e.pin_cwd!=null?{pin_cwd:e.pin_cwd}:{},...e.mode!=null?{mode:e.mode}:{},...e.kind!=null?{kind:e.kind}:{},...e.model!=null?{model:e.model}:{}},{readOnlyProfileSelection:!0})).launch.model;if(o===void 0)throw new c(422,"unresolved_model","the selected launch has no concrete model");const r=S(o);if(h(r)){const a=k(r);if(a.length>0){const d=a.filter(n=>n.authFailure===void 0);if(d.length===0)return{provider:r,model:o,credential:"unusable",reason:"invalid_grant"};const s=Date.now(),u=d.some(n=>p(n,o)<=s),m=Math.min(...d.map(n=>p(n,o)));return{provider:r,model:o,credential:"ready",...!u&&m>s?{rate_limited_until:new Date(m).toISOString()}:{}}}}const i=await new y(g(v(),"auth.json")).read(r),f=i?.type==="oauth"||i?.type==="api_key"&&!!i.key;return{provider:r,model:o,credential:f?"ready":"missing"}}l(O,"modelAuthReadiness");export{O as modelAuthReadiness};
1
+ var w=Object.defineProperty;var l=(e,t)=>w(e,"name",{value:t,configurable:!0});import{ApiError as m}from"../../api/index.js";import{PoolCredentialStore as g}from"../pool-credential-store.js";import{resolveLaunchTarget as v}from"../runtime/launch-target.js";import{coolingUntilFor as p,isManagedProvider as y,readSubscriptionPool as _}from"../subscription-state.js";function h(e){const t=e.indexOf("/"),o=t>0?e.slice(0,t):"";if(o==="")throw new m(422,"unresolved_model","the selected launch has no concrete model provider");return o}l(h,"providerFromModel");async function A(e){const o=(await v({root:!0,...e.profile!=null?{profile:e.profile}:{},...e.cwd!=null?{cwd:e.cwd}:{},...e.pin_cwd!=null?{pin_cwd:e.pin_cwd}:{},...e.mode!=null?{mode:e.mode}:{},...e.kind!=null?{kind:e.kind}:{},...e.model!=null?{model:e.model}:{}},{readOnlyProfileSelection:!0})).launch.model;if(o===void 0)throw new m(422,"unresolved_model","the selected launch has no concrete model");const r=h(o);if(y(r)){const a=await _(r);if(a.length>0){const d=a.filter(n=>n.authFailure===void 0);if(d.length===0)return{provider:r,model:o,credential:"unusable",reason:"invalid_grant"};const s=Date.now(),f=d.some(n=>p(n,o)<=s),c=Math.min(...d.map(n=>p(n,o)));return{provider:r,model:o,credential:"ready",...!f&&c>s?{rate_limited_until:new Date(c).toISOString()}:{}}}}const i=await new g().read(r),u=i?.type==="oauth"||i?.type==="api_key"&&!!i.key;return{provider:r,model:o,credential:u?"ready":"missing"}}l(A,"modelAuthReadiness");export{A as modelAuthReadiness};
@@ -17,4 +17,4 @@ export type CoreModelConfig = {
17
17
  /** Validate the complete Core-owned payload before its single user-scope write. */
18
18
  export declare function validateCoreModelConfig(value: unknown): CoreModelConfig;
19
19
  /** Write Core-owned fields atomically while retaining unrelated config and kind fields. */
20
- export declare function replaceCoreModelConfig(config: CoreModelConfig): void;
20
+ export declare function replaceCoreModelConfig(config: CoreModelConfig): Promise<void>;