@opengeni/api-router 0.30.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/dist/access-grant-rls.d.ts +3 -0
  2. package/dist/admission-trace.d.ts +8 -0
  3. package/dist/api-websocket.d.ts +18 -0
  4. package/dist/app.d.ts +50 -2
  5. package/dist/app.js +12 -1
  6. package/dist/artifact-catalog-cursor.d.ts +27 -0
  7. package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
  8. package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
  9. package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
  10. package/dist/auth/managed-auth.d.ts +102 -3
  11. package/dist/auth/managed-email.d.ts +29 -0
  12. package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
  13. package/dist/auth/new-signups-gate.d.ts +23 -0
  14. package/dist/auth/organization-user-setup.d.ts +80 -0
  15. package/dist/auth/signup-funnel-metrics.d.ts +44 -0
  16. package/dist/browser-controller-authority.d.ts +1 -61
  17. package/dist/browser-ephemeral.d.ts +5 -0
  18. package/dist/browser-state-authority.d.ts +1 -27
  19. package/dist/chunk-6TZ5LG3O.js +123948 -0
  20. package/dist/chunk-6TZ5LG3O.js.map +1 -0
  21. package/dist/chunk-OGQDXVXY.js +18 -0
  22. package/dist/chunk-OGQDXVXY.js.map +1 -0
  23. package/dist/claude-subscription-account-conflicts.d.ts +1 -0
  24. package/dist/claude-subscription-account-usage.d.ts +39 -0
  25. package/dist/claude-subscription-oauth.d.ts +25 -0
  26. package/dist/claude-subscription-profile.d.ts +9 -0
  27. package/dist/claude-subscription-usage.d.ts +44 -0
  28. package/dist/claude-workspace-connection.d.ts +15 -0
  29. package/dist/codemode.d.ts +32 -1
  30. package/dist/company-brain-okf.d.ts +24 -0
  31. package/dist/connected-machine-computer-access.d.ts +11 -0
  32. package/dist/connection-ownership.d.ts +54 -0
  33. package/dist/controller-data-plane.d.ts +32 -0
  34. package/dist/editable-artifact-source-session.d.ts +3 -0
  35. package/dist/editable-artifact-websocket.d.ts +10 -18
  36. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  37. package/dist/fatal-process-boundary.d.ts +25 -0
  38. package/dist/github-access.d.ts +31 -2
  39. package/dist/http/acting-person.d.ts +24 -0
  40. package/dist/http/api-contract-compat.d.ts +19 -0
  41. package/dist/http/api-error.d.ts +27 -1
  42. package/dist/http/cors.d.ts +17 -0
  43. package/dist/http/deprecation.d.ts +42 -0
  44. package/dist/http/interaction-control-error.d.ts +13 -0
  45. package/dist/http/keyed-admission.d.ts +12 -0
  46. package/dist/http/local-browser-boundary.d.ts +72 -0
  47. package/dist/http/metrics-listener.d.ts +22 -0
  48. package/dist/http/path-identifier.d.ts +17 -0
  49. package/dist/http/registered-route-label.d.ts +16 -0
  50. package/dist/http/request-body.d.ts +27 -0
  51. package/dist/http/request-source.d.ts +52 -0
  52. package/dist/http/sse.d.ts +17 -6
  53. package/dist/http/unmatched-route.d.ts +18 -0
  54. package/dist/http/user-content.d.ts +36 -0
  55. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  56. package/dist/index.d.ts +10 -3
  57. package/dist/index.js +1091 -162
  58. package/dist/index.js.map +1 -1
  59. package/dist/integration-endpoint-test.d.ts +20 -0
  60. package/dist/integrations/api-integrations.d.ts +2 -2
  61. package/dist/integrations/atlassian.d.ts +29 -3
  62. package/dist/integrations/connect-authority.d.ts +1 -0
  63. package/dist/integrations/connect-callback-return.d.ts +6 -0
  64. package/dist/integrations/fiken.d.ts +16 -2
  65. package/dist/integrations/github-app-connect.d.ts +47 -0
  66. package/dist/integrations/github-installation-proof.d.ts +3 -0
  67. package/dist/integrations/github-lens-connect.d.ts +116 -0
  68. package/dist/integrations/github-skill-source.d.ts +1 -5
  69. package/dist/integrations/google-drive.d.ts +24 -0
  70. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  71. package/dist/integrations/marketplace-plugin.d.ts +2691 -0
  72. package/dist/integrations/oauth-client.d.ts +178 -18
  73. package/dist/integrations/oauth-profiles.d.ts +222 -0
  74. package/dist/integrations/oauth-return-path.d.ts +1 -0
  75. package/dist/integrations/personal-github-repositories.d.ts +84 -0
  76. package/dist/integrations/personal-github.d.ts +42 -0
  77. package/dist/integrations/pr-review-provider.d.ts +22 -0
  78. package/dist/integrations/provider-oauth.d.ts +19 -1
  79. package/dist/integrations/slack-app-home.d.ts +24 -0
  80. package/dist/integrations/slack-bot.d.ts +367 -12
  81. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  82. package/dist/integrations/slack-install.d.ts +20 -0
  83. package/dist/integrations/slack-interactions.d.ts +107 -9
  84. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  85. package/dist/integrations/slack-routing.d.ts +120 -0
  86. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  87. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  88. package/dist/integrations/social-oauth.d.ts +7 -0
  89. package/dist/interaction-agent-access.d.ts +15 -0
  90. package/dist/interaction-frame-proxy.d.ts +68 -0
  91. package/dist/interaction-metrics.d.ts +2 -0
  92. package/dist/mcp/action-catalog-types.d.ts +9 -0
  93. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  94. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  95. package/dist/mcp/child-read-evidence.d.ts +9 -0
  96. package/dist/mcp/command-read.d.ts +24 -0
  97. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  98. package/dist/mcp/contract-input.d.ts +11 -0
  99. package/dist/mcp/documents.d.ts +7 -5
  100. package/dist/mcp/files.d.ts +2 -2
  101. package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
  102. package/dist/mcp/knowledge-entries.d.ts +5 -0
  103. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  104. package/dist/mcp/receipts.d.ts +9 -0
  105. package/dist/mcp/request-abort.d.ts +19 -0
  106. package/dist/mcp/scheduled-task-input.d.ts +1189 -0
  107. package/dist/mcp/scheduled-task-view.d.ts +22 -4
  108. package/dist/mcp/server.d.ts +255 -3
  109. package/dist/mcp/session-event-view.d.ts +94 -0
  110. package/dist/mcp/session-view.d.ts +66 -2
  111. package/dist/mcp/session-wait.d.ts +174 -0
  112. package/dist/mcp-oauth.d.ts +52 -0
  113. package/dist/model-catalog.d.ts +11 -31
  114. package/dist/observability.d.ts +3 -1
  115. package/dist/organization-mcp.d.ts +52 -0
  116. package/dist/organization-recovery-notifications.d.ts +41 -0
  117. package/dist/routes/analytics-consent.d.ts +26 -0
  118. package/dist/routes/api-integrations.d.ts +25 -0
  119. package/dist/routes/api-keys.d.ts +17 -0
  120. package/dist/routes/artifact-catalog.d.ts +6 -0
  121. package/dist/routes/automations.d.ts +19 -0
  122. package/dist/routes/billing.d.ts +49 -0
  123. package/dist/routes/browser-sessions.d.ts +59 -2
  124. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  125. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  126. package/dist/routes/client-errors.d.ts +146 -0
  127. package/dist/routes/codex.d.ts +76 -2
  128. package/dist/routes/company-brain.d.ts +3 -0
  129. package/dist/routes/company-profile.d.ts +2 -1
  130. package/dist/routes/computer-sessions.d.ts +14 -0
  131. package/dist/routes/connect.d.ts +5 -0
  132. package/dist/routes/editable-artifacts.d.ts +2 -0
  133. package/dist/routes/external-identity-links.d.ts +6 -0
  134. package/dist/routes/feedback.d.ts +3 -0
  135. package/dist/routes/github-browser-pages.d.ts +12 -0
  136. package/dist/routes/github.d.ts +2 -0
  137. package/dist/routes/insights-response-cache.d.ts +10 -0
  138. package/dist/routes/insights-usage.d.ts +12 -0
  139. package/dist/routes/insights.d.ts +30 -1
  140. package/dist/routes/knowledge.d.ts +3 -0
  141. package/dist/routes/managed-auth-session-sets.d.ts +26 -0
  142. package/dist/routes/managed-onboarding.d.ts +29 -0
  143. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  144. package/dist/routes/model-connection-access.d.ts +3 -0
  145. package/dist/routes/organization-integration-policy.d.ts +3 -0
  146. package/dist/routes/organization-integrations.d.ts +6 -0
  147. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  148. package/dist/routes/organization-model-providers.d.ts +3 -0
  149. package/dist/routes/organization-recovery.d.ts +19 -0
  150. package/dist/routes/organization-service-accounts.d.ts +3 -0
  151. package/dist/routes/organization-sessions.d.ts +26 -0
  152. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  153. package/dist/routes/personal-github.d.ts +3 -0
  154. package/dist/routes/pr-review-github.d.ts +3 -0
  155. package/dist/routes/pr-review.d.ts +3 -0
  156. package/dist/routes/session-artifact-associations.d.ts +4 -0
  157. package/dist/routes/session-history-imports.d.ts +9 -0
  158. package/dist/routes/sessions.d.ts +44 -6
  159. package/dist/routes/skill-content.d.ts +4 -0
  160. package/dist/routes/skill-install-authority.d.ts +6 -0
  161. package/dist/routes/subscription-account-pools.d.ts +34 -0
  162. package/dist/routes/subscription-pool-access.d.ts +16 -0
  163. package/dist/routes/supergrok.d.ts +6 -1
  164. package/dist/routes/usage-allowances.d.ts +12 -0
  165. package/dist/routes/user-resource-authorities.d.ts +3 -0
  166. package/dist/routes/workspace-artifacts.d.ts +2 -1
  167. package/dist/routes/workspace-capture.d.ts +10 -2
  168. package/dist/routes/workspace-integrations.d.ts +35 -0
  169. package/dist/routes/workspace-learning.d.ts +3 -0
  170. package/dist/routes/workspace-model-providers.d.ts +4 -0
  171. package/dist/routes/workspaces.d.ts +29 -0
  172. package/dist/sandbox/auth-callout.d.ts +8 -0
  173. package/dist/sandbox/channel-a.d.ts +19 -1
  174. package/dist/sandbox/connection-authority.d.ts +3 -0
  175. package/dist/sandbox/enrollment.d.ts +9 -1
  176. package/dist/sandbox/machines.d.ts +5 -2
  177. package/dist/sandbox/metrics-ingestion.d.ts +28 -20
  178. package/dist/sandbox/rematerialize.d.ts +2 -0
  179. package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
  180. package/dist/sandbox/viewer.d.ts +26 -1
  181. package/dist/sandbox-file-artifacts.d.ts +50 -0
  182. package/dist/scheduled-task-deletion.d.ts +13 -0
  183. package/dist/session-create-observability.d.ts +5 -0
  184. package/dist/session-event-view-MPS2OZD3.js +292 -0
  185. package/dist/session-event-view-MPS2OZD3.js.map +1 -0
  186. package/dist/site-session-origin.d.ts +13 -0
  187. package/dist/site-uploads.d.ts +41 -0
  188. package/dist/slack-reaction-files.d.ts +1 -1
  189. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  190. package/dist/temporal-schedule-sync.d.ts +26 -0
  191. package/dist/transcription/providers/xai-subscription.d.ts +1 -0
  192. package/dist/transcription/service.d.ts +2 -1
  193. package/dist/work-discovery-observability.d.ts +33 -0
  194. package/dist/workspace-artifact-content.d.ts +28 -0
  195. package/dist/workspace-artifact-provenance.d.ts +7 -0
  196. package/dist/workspace-delete-observability.d.ts +10 -0
  197. package/dist/workspace-deletion.d.ts +6 -0
  198. package/dist/workspace-state-projection.d.ts +3 -5
  199. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  200. package/dist/workspace-tool-gateway.d.ts +123 -0
  201. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  202. package/package.json +23 -19
  203. package/src/access-grant-rls.ts +75 -0
  204. package/src/admission-trace.ts +14 -0
  205. package/src/api-websocket.ts +23 -0
  206. package/src/app.ts +1967 -252
  207. package/src/artifact-catalog-cursor.ts +60 -0
  208. package/src/auth/managed-auth-attempt-context.ts +70 -0
  209. package/src/auth/managed-auth-rate-limits.ts +252 -0
  210. package/src/auth/managed-auth-session-adapter.ts +222 -0
  211. package/src/auth/managed-auth.ts +536 -60
  212. package/src/auth/managed-email.ts +174 -0
  213. package/src/auth/managed-sign-in-notifications.ts +81 -0
  214. package/src/auth/new-signups-gate.ts +49 -0
  215. package/src/auth/organization-user-setup.ts +307 -0
  216. package/src/auth/signup-funnel-metrics.ts +164 -0
  217. package/src/browser-controller-authority.ts +1 -186
  218. package/src/browser-ephemeral.ts +39 -0
  219. package/src/browser-state-authority.ts +1 -236
  220. package/src/claude-subscription-account-conflicts.ts +12 -0
  221. package/src/claude-subscription-account-usage.ts +48 -0
  222. package/src/claude-subscription-oauth.ts +362 -0
  223. package/src/claude-subscription-profile.ts +73 -0
  224. package/src/claude-subscription-usage.ts +97 -0
  225. package/src/claude-workspace-connection.ts +82 -0
  226. package/src/codemode.ts +194 -30
  227. package/src/codex-realtime.ts +8 -2
  228. package/src/company-brain-okf.ts +340 -0
  229. package/src/connected-machine-computer-access.ts +33 -0
  230. package/src/connection-ownership.ts +199 -0
  231. package/src/controller-data-plane.ts +81 -0
  232. package/src/editable-artifact-native-kernel.ts +26 -15
  233. package/src/editable-artifact-office-import.ts +28 -3
  234. package/src/editable-artifact-production.ts +23 -7
  235. package/src/editable-artifact-source-session.ts +123 -0
  236. package/src/editable-artifact-websocket.ts +27 -21
  237. package/src/editable-artifact-workspace-files.ts +31 -6
  238. package/src/fatal-process-boundary.ts +246 -0
  239. package/src/gateway-realtime.ts +1 -1
  240. package/src/github-access.ts +147 -5
  241. package/src/http/acting-person.ts +71 -0
  242. package/src/http/api-contract-compat.ts +49 -0
  243. package/src/http/api-error.ts +115 -1
  244. package/src/http/auth.ts +48 -0
  245. package/src/http/cors.ts +67 -0
  246. package/src/http/deprecation.ts +118 -0
  247. package/src/http/interaction-control-error.ts +164 -0
  248. package/src/http/keyed-admission.ts +29 -0
  249. package/src/http/local-browser-boundary.ts +343 -0
  250. package/src/http/metrics-listener.ts +57 -0
  251. package/src/http/path-identifier.ts +54 -0
  252. package/src/http/registered-route-label.ts +34 -0
  253. package/src/http/request-body.ts +127 -0
  254. package/src/http/request-source.ts +178 -0
  255. package/src/http/sse.ts +486 -132
  256. package/src/http/unmatched-route.ts +60 -0
  257. package/src/http/user-content.ts +158 -0
  258. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  259. package/src/index.ts +157 -52
  260. package/src/integration-endpoint-test.ts +291 -0
  261. package/src/integrations/api-integrations.ts +98 -11
  262. package/src/integrations/atlassian.ts +474 -136
  263. package/src/integrations/connect-authority.ts +2 -0
  264. package/src/integrations/connect-callback-return.ts +86 -0
  265. package/src/integrations/fiken.ts +354 -64
  266. package/src/integrations/github-app-connect.ts +435 -0
  267. package/src/integrations/github-installation-proof.ts +60 -0
  268. package/src/integrations/github-lens-connect.ts +76 -0
  269. package/src/integrations/github-skill-source.ts +2 -142
  270. package/src/integrations/google-drive.ts +717 -225
  271. package/src/integrations/integration-commit-authority.ts +69 -0
  272. package/src/integrations/marketplace-plugin.ts +48 -0
  273. package/src/integrations/oauth-client.ts +1356 -459
  274. package/src/integrations/oauth-profiles.ts +537 -0
  275. package/src/integrations/oauth-return-path.ts +18 -0
  276. package/src/integrations/personal-github-repositories.ts +674 -0
  277. package/src/integrations/personal-github.ts +825 -0
  278. package/src/integrations/pr-review-provider.ts +246 -0
  279. package/src/integrations/provider-oauth.ts +250 -22
  280. package/src/integrations/slack-app-home.ts +300 -0
  281. package/src/integrations/slack-bot.ts +963 -239
  282. package/src/integrations/slack-file-upload-flow.ts +147 -0
  283. package/src/integrations/slack-install.ts +83 -0
  284. package/src/integrations/slack-interactions.ts +3167 -474
  285. package/src/integrations/slack-mrkdwn.ts +193 -0
  286. package/src/integrations/slack-routing.ts +337 -0
  287. package/src/integrations/slack-session-defaults.ts +224 -0
  288. package/src/integrations/slack-task-file-upload.ts +323 -0
  289. package/src/integrations/social-oauth.ts +213 -15
  290. package/src/interaction-agent-access.ts +48 -0
  291. package/src/interaction-frame-proxy.ts +409 -0
  292. package/src/interaction-metrics.ts +30 -0
  293. package/src/mcp/action-catalog-types.ts +9 -0
  294. package/src/mcp/action-catalog.gen.ts +6696 -0
  295. package/src/mcp/capability-account-readiness.ts +17 -0
  296. package/src/mcp/child-read-evidence.ts +19 -0
  297. package/src/mcp/command-read.ts +109 -0
  298. package/src/mcp/company-profile-agent-admin.ts +198 -0
  299. package/src/mcp/contract-input.ts +141 -0
  300. package/src/mcp/documents.ts +65 -377
  301. package/src/mcp/editable-artifacts.ts +12 -6
  302. package/src/mcp/files.ts +80 -43
  303. package/src/mcp/first-party-tool-permissions.ts +305 -0
  304. package/src/mcp/knowledge-entries.ts +322 -0
  305. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  306. package/src/mcp/receipts.ts +34 -0
  307. package/src/mcp/request-abort.ts +44 -0
  308. package/src/mcp/scheduled-task-input.ts +76 -0
  309. package/src/mcp/scheduled-task-view.ts +44 -3
  310. package/src/mcp/server.ts +3030 -1267
  311. package/src/mcp/session-event-view.ts +383 -0
  312. package/src/mcp/session-view.ts +84 -3
  313. package/src/mcp/session-wait.ts +665 -0
  314. package/src/mcp-oauth.ts +1123 -0
  315. package/src/model-catalog.ts +91 -333
  316. package/src/observability.ts +15 -1
  317. package/src/organization-mcp.ts +397 -0
  318. package/src/organization-recovery-notifications.ts +103 -0
  319. package/src/routes/analytics-consent.ts +123 -0
  320. package/src/routes/api-integrations.ts +364 -74
  321. package/src/routes/api-keys.ts +336 -10
  322. package/src/routes/artifact-catalog.ts +279 -0
  323. package/src/routes/automations.ts +768 -0
  324. package/src/routes/billing.ts +461 -46
  325. package/src/routes/browser-sessions.ts +780 -112
  326. package/src/routes/canonical-human-identities.ts +90 -14
  327. package/src/routes/capabilities.ts +107 -6
  328. package/src/routes/channels.ts +20 -3
  329. package/src/routes/claude-subscription-accounts.ts +334 -0
  330. package/src/routes/claude-subscription-oauth.ts +142 -0
  331. package/src/routes/client-errors.ts +362 -0
  332. package/src/routes/codex.ts +833 -116
  333. package/src/routes/company-brain.ts +322 -0
  334. package/src/routes/company-profile.ts +65 -1
  335. package/src/routes/computer-sessions.ts +557 -106
  336. package/src/routes/connect.ts +1938 -0
  337. package/src/routes/connections.ts +1039 -349
  338. package/src/routes/documents.ts +488 -287
  339. package/src/routes/editable-artifacts.ts +79 -9
  340. package/src/routes/enrollments.ts +135 -39
  341. package/src/routes/environments.ts +154 -58
  342. package/src/routes/external-identity-links.ts +201 -0
  343. package/src/routes/feedback.ts +113 -0
  344. package/src/routes/files.ts +267 -33
  345. package/src/routes/github-browser-pages.ts +162 -0
  346. package/src/routes/github.ts +446 -144
  347. package/src/routes/insights-response-cache.ts +83 -0
  348. package/src/routes/insights-usage.ts +345 -0
  349. package/src/routes/insights.ts +153 -19
  350. package/src/routes/install.ts +133 -38
  351. package/src/routes/integration-facets.ts +107 -33
  352. package/src/routes/interaction-resources.ts +30 -3
  353. package/src/routes/knowledge.ts +488 -0
  354. package/src/routes/machines.ts +313 -15
  355. package/src/routes/managed-auth-session-sets.ts +1218 -0
  356. package/src/routes/managed-onboarding.ts +337 -0
  357. package/src/routes/managed-sign-in-methods.ts +433 -0
  358. package/src/routes/memory-slack-publications.ts +3 -2
  359. package/src/routes/model-connection-access.ts +208 -0
  360. package/src/routes/organization-integration-policy.ts +116 -0
  361. package/src/routes/organization-integrations.ts +326 -0
  362. package/src/routes/organization-mcp-connections.ts +178 -0
  363. package/src/routes/organization-memberships.ts +1019 -27
  364. package/src/routes/organization-model-providers.ts +293 -0
  365. package/src/routes/organization-recovery.ts +325 -0
  366. package/src/routes/organization-service-accounts.ts +128 -0
  367. package/src/routes/organization-sessions.ts +350 -0
  368. package/src/routes/personal-github-git-broker.ts +785 -0
  369. package/src/routes/personal-github.ts +370 -0
  370. package/src/routes/plugins.ts +251 -22
  371. package/src/routes/pr-review-github.ts +881 -0
  372. package/src/routes/pr-review.ts +622 -0
  373. package/src/routes/preference-registry.ts +12 -93
  374. package/src/routes/rigs.ts +167 -50
  375. package/src/routes/scheduled-tasks.ts +346 -41
  376. package/src/routes/session-artifact-associations.ts +87 -0
  377. package/src/routes/session-history-imports.ts +280 -0
  378. package/src/routes/sessions.ts +2306 -264
  379. package/src/routes/skill-content.ts +297 -0
  380. package/src/routes/skill-install-authority.ts +64 -0
  381. package/src/routes/skills.ts +62 -9
  382. package/src/routes/social.ts +46 -18
  383. package/src/routes/subscription-account-pools.ts +436 -0
  384. package/src/routes/subscription-pool-access.ts +126 -0
  385. package/src/routes/supergrok.ts +219 -303
  386. package/src/routes/transcription-recordings.ts +38 -3
  387. package/src/routes/transcriptions.ts +4 -0
  388. package/src/routes/usage-allowances.ts +378 -0
  389. package/src/routes/user-resource-authorities.ts +138 -0
  390. package/src/routes/video-generation.ts +14 -10
  391. package/src/routes/workspace-artifacts.ts +275 -69
  392. package/src/routes/workspace-capture.ts +21 -0
  393. package/src/routes/workspace-integrations.ts +576 -0
  394. package/src/routes/workspace-learning.ts +159 -0
  395. package/src/routes/workspace-model-providers.ts +148 -0
  396. package/src/routes/workspace-state.ts +38 -25
  397. package/src/routes/workspaces.ts +1004 -127
  398. package/src/sandbox/auth-callout.ts +96 -19
  399. package/src/sandbox/channel-a.ts +323 -24
  400. package/src/sandbox/connection-authority.ts +3 -0
  401. package/src/sandbox/enrollment.ts +81 -6
  402. package/src/sandbox/machines.ts +218 -76
  403. package/src/sandbox/metrics-ingestion.ts +411 -62
  404. package/src/sandbox/rematerialize.ts +121 -19
  405. package/src/sandbox/session-attach-variable-sets.ts +131 -0
  406. package/src/sandbox/viewer.ts +280 -25
  407. package/src/sandbox-file-artifacts.ts +413 -0
  408. package/src/scheduled-task-deletion.ts +138 -0
  409. package/src/session-create-observability.ts +46 -0
  410. package/src/session-realtime-context.ts +6 -3
  411. package/src/site-session-origin.ts +48 -0
  412. package/src/site-uploads.ts +224 -0
  413. package/src/slack-reaction-files.ts +16 -5
  414. package/src/temporal-schedule-cleanup.ts +13 -0
  415. package/src/temporal-schedule-sync.ts +74 -0
  416. package/src/transcription/providers/codex-subscription.ts +2 -0
  417. package/src/transcription/providers/openai.ts +1 -0
  418. package/src/transcription/providers/xai-subscription.ts +49 -3
  419. package/src/transcription/segmenter.ts +8 -2
  420. package/src/transcription/service.ts +43 -6
  421. package/src/work-discovery-observability.ts +121 -0
  422. package/src/workspace-artifact-content.ts +144 -0
  423. package/src/workspace-artifact-provenance.ts +104 -0
  424. package/src/workspace-delete-observability.ts +75 -0
  425. package/src/workspace-deletion.ts +73 -0
  426. package/src/workspace-state-projection.ts +18 -148
  427. package/src/workspace-tool-gateway-observability.ts +100 -0
  428. package/src/workspace-tool-gateway.ts +870 -0
  429. package/src/workspace-webhook-dispatch.ts +190 -0
  430. package/src/xai-subscription-auth.ts +9 -2
  431. package/dist/chunk-WCXHI3FF.js +0 -49920
  432. package/dist/chunk-WCXHI3FF.js.map +0 -1
  433. package/dist/routes/packs.d.ts +0 -3
  434. package/src/routes/packs.ts +0 -729
@@ -0,0 +1,174 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import type {
3
+ ManagedEmailDeliveryResult,
4
+ ManagedEmailMessage,
5
+ ManagedEmailTransport,
6
+ } from "@opengeni/core";
7
+ import { createHmac } from "node:crypto";
8
+ import { Resend } from "resend";
9
+
10
+ const CAPTURE_MAX_MESSAGES = 250;
11
+ const CAPTURE_TTL_MS = 15 * 60 * 1000;
12
+
13
+ export type CapturedManagedEmail = ManagedEmailMessage & {
14
+ capturedAt: string;
15
+ };
16
+
17
+ /**
18
+ * Process-local test/development transport. Captures are count/TTL bounded and
19
+ * one-time readable; there is deliberately no route, disk, database, or log
20
+ * projection for message bodies or setup bearers.
21
+ */
22
+ export class InMemoryManagedEmailTransport implements ManagedEmailTransport {
23
+ private readonly messages: CapturedManagedEmail[] = [];
24
+ readonly sender: string;
25
+ readonly idempotency: ManagedEmailTransport["idempotency"];
26
+
27
+ constructor(
28
+ private readonly options: {
29
+ maxMessages?: number;
30
+ ttlMs?: number;
31
+ now?: () => number;
32
+ sender?: string;
33
+ idempotency?: ManagedEmailTransport["idempotency"];
34
+ } = {},
35
+ ) {
36
+ this.sender = options.sender ?? "Opengeni <auth@mail.opengeni.ai>";
37
+ this.idempotency = options.idempotency ?? {
38
+ scope: "opengeni-in-memory-v1",
39
+ retentionSeconds: 86_400,
40
+ };
41
+ }
42
+
43
+ async send(message: ManagedEmailMessage): Promise<ManagedEmailDeliveryResult> {
44
+ this.prune();
45
+ const maxMessages = this.options.maxMessages ?? CAPTURE_MAX_MESSAGES;
46
+ if (this.messages.length >= maxMessages) this.messages.splice(0, 1);
47
+ this.messages.push({
48
+ ...message,
49
+ capturedAt: new Date(this.now()).toISOString(),
50
+ });
51
+ return { status: "sent", providerMessageId: null };
52
+ }
53
+
54
+ take(predicate: (message: CapturedManagedEmail) => boolean): CapturedManagedEmail | null {
55
+ this.prune();
56
+ const index = this.messages.findIndex(predicate);
57
+ if (index < 0) return null;
58
+ return this.messages.splice(index, 1)[0] ?? null;
59
+ }
60
+
61
+ size(): number {
62
+ this.prune();
63
+ return this.messages.length;
64
+ }
65
+
66
+ private prune(): void {
67
+ const cutoff = this.now() - (this.options.ttlMs ?? CAPTURE_TTL_MS);
68
+ while (this.messages[0] && Date.parse(this.messages[0].capturedAt) <= cutoff) {
69
+ this.messages.shift();
70
+ }
71
+ }
72
+
73
+ private now(): number {
74
+ return this.options.now?.() ?? Date.now();
75
+ }
76
+ }
77
+
78
+ class UnconfiguredManagedEmailTransport implements ManagedEmailTransport {
79
+ readonly idempotency = {
80
+ scope: "opengeni-unconfigured-v1",
81
+ retentionSeconds: 0,
82
+ } as const;
83
+
84
+ constructor(readonly sender: string) {}
85
+
86
+ async send(): Promise<ManagedEmailDeliveryResult> {
87
+ return { status: "failed", errorClass: "provider_not_configured" };
88
+ }
89
+ }
90
+
91
+ class ResendManagedEmailTransport implements ManagedEmailTransport {
92
+ private readonly client: Resend;
93
+ readonly idempotency: ManagedEmailTransport["idempotency"];
94
+
95
+ constructor(
96
+ apiKey: string,
97
+ readonly sender: string,
98
+ scopeSecret: string,
99
+ ) {
100
+ this.client = new Resend(apiKey);
101
+ this.idempotency = {
102
+ // The keyed digest binds this delivery to the Resend account without
103
+ // persisting an API-key derivative that can be tested offline.
104
+ scope: `resend-v1-24h:${createHmac("sha256", scopeSecret).update(apiKey).digest("hex")}`,
105
+ retentionSeconds: 86_400,
106
+ };
107
+ }
108
+
109
+ async send(message: ManagedEmailMessage): Promise<ManagedEmailDeliveryResult> {
110
+ if (message.from !== this.sender) {
111
+ return { status: "failed", errorClass: "sender_changed" };
112
+ }
113
+ try {
114
+ const result = await this.client.emails.send(
115
+ {
116
+ from: message.from,
117
+ to: message.to,
118
+ subject: message.subject,
119
+ text: message.text,
120
+ html: message.html,
121
+ },
122
+ message.idempotencyKey ? { idempotencyKey: message.idempotencyKey } : undefined,
123
+ );
124
+ if (!result.error) {
125
+ return { status: "sent", providerMessageId: result.data?.id ?? null };
126
+ }
127
+ const statusCode = "statusCode" in result.error ? Number(result.error.statusCode) : NaN;
128
+ return clearProviderRefusal(statusCode)
129
+ ? { status: "failed", errorClass: boundedErrorClass(result.error.name, "provider_refused") }
130
+ : {
131
+ status: "outcome_unknown",
132
+ errorClass: boundedErrorClass(result.error.name, "provider_ambiguous"),
133
+ };
134
+ } catch (error) {
135
+ return {
136
+ status: "outcome_unknown",
137
+ errorClass: boundedErrorClass(
138
+ error instanceof Error ? error.name : "transport_error",
139
+ "transport_error",
140
+ ),
141
+ };
142
+ }
143
+ }
144
+ }
145
+
146
+ export function createManagedEmailTransport(settings: Settings): ManagedEmailTransport {
147
+ if (settings.resendApiKey) {
148
+ if (!settings.betterAuthSecret) {
149
+ throw new Error("OPENGENI_BETTER_AUTH_SECRET is required for managed email delivery");
150
+ }
151
+ return new ResendManagedEmailTransport(
152
+ settings.resendApiKey,
153
+ settings.emailFrom,
154
+ settings.betterAuthSecret,
155
+ );
156
+ }
157
+ if (settings.environment === "local" || settings.environment === "test") {
158
+ return new InMemoryManagedEmailTransport({ sender: settings.emailFrom });
159
+ }
160
+ return new UnconfiguredManagedEmailTransport(settings.emailFrom);
161
+ }
162
+
163
+ function clearProviderRefusal(statusCode: number): boolean {
164
+ return statusCode >= 400 && statusCode < 500 && ![408, 409, 425, 429].includes(statusCode);
165
+ }
166
+
167
+ function boundedErrorClass(value: string, fallback: string): string {
168
+ const normalized = value
169
+ .trim()
170
+ .toLowerCase()
171
+ .replaceAll(/[^a-z0-9_-]+/g, "_")
172
+ .slice(0, 64);
173
+ return normalized || fallback;
174
+ }
@@ -0,0 +1,81 @@
1
+ import type { ManagedEmailTransport } from "@opengeni/core";
2
+ import type { Database } from "@opengeni/db";
3
+ import { sql } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ const Claim = z.object({
7
+ operationId: z.string().uuid(),
8
+ leaseId: z.string().uuid(),
9
+ email: z.string().email(),
10
+ provider: z.enum(["password", "google", "github"]),
11
+ sender: z.string(),
12
+ });
13
+ const started = new WeakSet<object>();
14
+
15
+ export async function deliverManagedSignInNotification(
16
+ db: Database,
17
+ transport: ManagedEmailTransport,
18
+ operationId: string | null = null,
19
+ ): Promise<"sent" | "failed" | "outcome_unknown"> {
20
+ try {
21
+ const result = await db.execute(
22
+ sql`select claim_managed_sign_in_notification(${operationId}::uuid,${transport.sender},${transport.idempotency.scope},${transport.idempotency.retentionSeconds}::integer) as claim`,
23
+ );
24
+ const row = (Array.isArray(result) ? result : result.rows)[0] as { claim: unknown } | undefined;
25
+ if (!row?.claim) return "outcome_unknown";
26
+ const claim = Claim.parse(row.claim);
27
+ let status: "sent" | "failed" | "outcome_unknown" = "outcome_unknown";
28
+ let timer: ReturnType<typeof setTimeout> | undefined;
29
+ try {
30
+ const response = await Promise.race([
31
+ transport.send({
32
+ kind: "sign_in_method_changed",
33
+ from: claim.sender,
34
+ to: claim.email,
35
+ idempotencyKey: `sign-in-method:${claim.operationId}`,
36
+ subject: "Your Opengeni sign-in methods changed",
37
+ text: `Your Opengeni ${claim.provider} sign-in method changed. If this was not you, reset your password and contact your administrator.`,
38
+ html: `<p>Your Opengeni ${claim.provider} sign-in method changed. If this was not you, reset your password and contact your administrator.</p>`,
39
+ }),
40
+ new Promise<{ status: "outcome_unknown" }>((resolve) => {
41
+ timer = setTimeout(() => resolve({ status: "outcome_unknown" }), 5_000);
42
+ }),
43
+ ]);
44
+ status = response.status;
45
+ } catch {
46
+ /* Retain the durable unknown claim for idempotent retry. */
47
+ } finally {
48
+ if (timer) clearTimeout(timer);
49
+ }
50
+ await db.execute(
51
+ sql`select settle_managed_sign_in_notification(${claim.operationId}::uuid,${claim.leaseId}::uuid,${status})`,
52
+ );
53
+ return status;
54
+ } catch {
55
+ // Security state is already committed. A failed claim/ack must never be
56
+ // reported as a failed method change; its durable obligation remains live.
57
+ return "outcome_unknown";
58
+ }
59
+ }
60
+
61
+ export function startManagedSignInNotificationDelivery(
62
+ db: Database,
63
+ transport: ManagedEmailTransport,
64
+ ): void {
65
+ if (started.has(db as object)) return;
66
+ started.add(db as object);
67
+ let active = false;
68
+ const drain = async () => {
69
+ if (active) return;
70
+ active = true;
71
+ try {
72
+ for (let count = 0; count < 10; count++)
73
+ if ((await deliverManagedSignInNotification(db, transport)) === "outcome_unknown") break;
74
+ } finally {
75
+ active = false;
76
+ }
77
+ };
78
+ const timer = setInterval(() => void drain(), 60_000);
79
+ (timer as ReturnType<typeof setInterval> & { unref?: () => void }).unref?.();
80
+ void drain();
81
+ }
@@ -0,0 +1,49 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import { readManagedAuthNewSignupsSwitch, type Database } from "@opengeni/db";
3
+ import type { Observability } from "@opengeni/observability";
4
+
5
+ /**
6
+ * Decides whether a new managed account may be created right now.
7
+ *
8
+ * `OPENGENI_MANAGED_AUTH_NEW_SIGNUPS_ENABLED` is the deployment ceiling (read at
9
+ * startup). While it allows sign-ups, the newest revision of the operator
10
+ * runtime switch (migration 0585) decides, read on every call so a flip
11
+ * applies to the next request on every API replica without a restart.
12
+ *
13
+ * Fail-safe: if the read fails, the last value this process observed wins;
14
+ * before any successful read, the deployment ceiling wins. A missing revision
15
+ * (pre-0585 schema) also follows the ceiling.
16
+ */
17
+ export type ManagedAuthNewSignupsGate = {
18
+ signupsOpen(): Promise<boolean>;
19
+ };
20
+
21
+ export function createManagedAuthNewSignupsGate(input: {
22
+ db: Database;
23
+ settings: Pick<Settings, "managedAuthNewSignupsEnabled">;
24
+ observability?: Pick<Observability, "warn"> | undefined;
25
+ }): ManagedAuthNewSignupsGate {
26
+ const ceiling = input.settings.managedAuthNewSignupsEnabled !== false;
27
+ let lastKnown: boolean | null = null;
28
+ return {
29
+ async signupsOpen() {
30
+ if (!ceiling) return false;
31
+ try {
32
+ const current = await readManagedAuthNewSignupsSwitch(input.db);
33
+ lastKnown = current ? current.signupsEnabled : true;
34
+ return lastKnown;
35
+ } catch (error) {
36
+ try {
37
+ input.observability?.warn("Managed auth new signups switch read failed", {
38
+ dependency: "managed_auth_new_signups_switch",
39
+ fallback: lastKnown === null ? "deployment_ceiling" : "last_known",
40
+ errorClass: error instanceof Error ? error.name : "UnknownError",
41
+ });
42
+ } catch {
43
+ // An observer failure must not decide a sign-up.
44
+ }
45
+ return lastKnown ?? ceiling;
46
+ }
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,307 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import type { ManagedEmailTransport } from "@opengeni/core";
3
+
4
+ const encoder = new TextEncoder();
5
+ export type OrganizationUserSetupEmailTokenTransport = "fragment" | "query";
6
+
7
+ /**
8
+ * Prove that the stable invited-user setup bearer can be constructed before an
9
+ * invitation commits. Provider availability is intentionally outside this
10
+ * precondition: the durable delivery journal records a failed or ambiguous
11
+ * transport outcome after the invitation exists.
12
+ */
13
+ export function assertOrganizationUserSetupDeliveryConfigured(
14
+ settings: Settings,
15
+ transport: ManagedEmailTransport,
16
+ ): void {
17
+ assertOrganizationUserSetupQueryTransportConfigured(settings);
18
+ requiredSetupSecret(settings);
19
+ requiredPublicBaseUrl(settings);
20
+ assertManagedEmailTransportMetadata(transport);
21
+ }
22
+
23
+ /** Enforce the query-bearing edge proof for env and embedded settings alike. */
24
+ export function assertOrganizationUserSetupQueryTransportConfigured(
25
+ settings: Pick<
26
+ Settings,
27
+ | "organizationUserSetupEmailTokenTransport"
28
+ | "organizationUserSetupQueryEdgeSanitizationConfirmed"
29
+ >,
30
+ ): void {
31
+ if (
32
+ settings.organizationUserSetupEmailTokenTransport === "query" &&
33
+ !settings.organizationUserSetupQueryEdgeSanitizationConfirmed
34
+ ) {
35
+ throw new Error(
36
+ "OPENGENI_ORGANIZATION_USER_SETUP_QUERY_EDGE_SANITIZATION_CONFIRMED=true is required when OPENGENI_ORGANIZATION_USER_SETUP_EMAIL_TOKEN_TRANSPORT=query",
37
+ );
38
+ }
39
+ }
40
+
41
+ /** Reject an invalid embedded-provider contract before any durable boundary. */
42
+ export function assertManagedEmailTransportMetadata(transport: ManagedEmailTransport): void {
43
+ if (
44
+ transport.sender.trim() !== transport.sender ||
45
+ encoder.encode(transport.sender).byteLength < 3 ||
46
+ encoder.encode(transport.sender).byteLength > 320
47
+ ) {
48
+ throw new Error("Managed email sender is invalid");
49
+ }
50
+ const { scope, retentionSeconds } = transport.idempotency;
51
+ if (
52
+ scope.trim() !== scope ||
53
+ !/^[a-z0-9][a-z0-9:._-]*$/.test(scope) ||
54
+ encoder.encode(scope).byteLength > 200 ||
55
+ !Number.isInteger(retentionSeconds) ||
56
+ retentionSeconds < 0 ||
57
+ retentionSeconds > 31_536_000
58
+ ) {
59
+ throw new Error("Managed email idempotency contract is invalid");
60
+ }
61
+ }
62
+
63
+ export async function deriveOrganizationUserSetupToken(
64
+ settings: Settings,
65
+ input: {
66
+ invitationId: string;
67
+ deliveryId: string;
68
+ transport?: OrganizationUserSetupEmailTokenTransport;
69
+ },
70
+ ): Promise<{ token: string; digest: string; url: string }> {
71
+ const secret = requiredSetupSecret(settings);
72
+ const key = await crypto.subtle.importKey(
73
+ "raw",
74
+ encoder.encode(secret),
75
+ { name: "HMAC", hash: "SHA-256" },
76
+ false,
77
+ ["sign"],
78
+ );
79
+ const signature = await crypto.subtle.sign(
80
+ "HMAC",
81
+ key,
82
+ encoder.encode(
83
+ `opengeni:organization-user-setup-delivery:v1:${input.deliveryId}:${input.invitationId}`,
84
+ ),
85
+ );
86
+ const token = base64Url(new Uint8Array(signature));
87
+ const digest = await sha256Hex(token);
88
+ const url = new URL("/setup-account", requiredPublicBaseUrl(settings));
89
+ if ((input.transport ?? settings.organizationUserSetupEmailTokenTransport) === "query") {
90
+ // Email security gateways routinely wrap links and may discard URL
91
+ // fragments. Enable this only after the compatibility web rollout; the
92
+ // browser's first executable head bootstrap scrubs query authority without
93
+ // redirecting or allowing a query-bearing subresource Referer.
94
+ url.searchParams.set("token", token);
95
+ } else {
96
+ url.hash = new URLSearchParams({ token }).toString();
97
+ }
98
+ return { token, digest, url: url.toString() };
99
+ }
100
+
101
+ export type OrganizationUserSetupEmailSnapshot = {
102
+ senderEmail: string;
103
+ recipientEmail: string;
104
+ recipientName: string | null;
105
+ organizationName: string;
106
+ organizationRole: "owner" | "admin" | "member";
107
+ sharedWorkspaceAccess: Array<{
108
+ workspaceId: string;
109
+ workspaceName: string;
110
+ role: "viewer" | "member" | "admin";
111
+ }>;
112
+ setupUrl: string;
113
+ };
114
+
115
+ export function renderOrganizationUserSetupEmail(input: OrganizationUserSetupEmailSnapshot): {
116
+ from: string;
117
+ to: string;
118
+ subject: string;
119
+ text: string;
120
+ html: string;
121
+ } {
122
+ const greeting = input.recipientName ? `Hi ${input.recipientName},` : "Hello,";
123
+ const role = titleCase(input.organizationRole);
124
+ const workspaceSummary =
125
+ input.sharedWorkspaceAccess.length === 0
126
+ ? "No shared workspaces are assigned yet."
127
+ : `Shared workspace access:\n${input.sharedWorkspaceAccess
128
+ .map((workspace) => `- ${workspace.workspaceName}: ${titleCase(workspace.role)}`)
129
+ .join("\n")}`;
130
+ const workspaceHtml =
131
+ input.sharedWorkspaceAccess.length === 0
132
+ ? "<p>No shared workspaces are assigned yet.</p>"
133
+ : `<p>Shared workspace access:</p><ul>${input.sharedWorkspaceAccess
134
+ .map(
135
+ (workspace) =>
136
+ `<li>${escapeHtml(workspace.workspaceName)}: ${escapeHtml(titleCase(workspace.role))}</li>`,
137
+ )
138
+ .join("")}</ul>`;
139
+ return {
140
+ from: input.senderEmail,
141
+ to: input.recipientEmail,
142
+ subject: `You're invited to join ${input.organizationName} on Opengeni`,
143
+ text: `${greeting}\n\nYou've been invited to join ${input.organizationName} on Opengeni as ${role}.\n\n${workspaceSummary}\n\nThis invitation grants only the organization role and shared workspace access listed above. It never shares anyone's Personal workspace.\n\nAccept invitation to ${input.organizationName}: ${input.setupUrl}\n\nYou'll sign in or create an account before joining. Already use Opengeni? Open this invitation and choose Sign in as ${input.recipientEmail}. Opengeni will show the invitation immediately after you sign in.`,
144
+ html: `<p>${escapeHtml(greeting)}</p><p>You've been invited to join <strong>${escapeHtml(input.organizationName)}</strong> on Opengeni as ${escapeHtml(role)}.</p>${workspaceHtml}<p>This invitation grants only the organization role and shared workspace access listed above. It never shares anyone's Personal workspace.</p><p><a href="${escapeHtml(input.setupUrl)}">Accept invitation to ${escapeHtml(input.organizationName)}</a></p><p>You'll sign in or create an account before joining. Already use Opengeni? Open this invitation and choose <strong>Sign in as ${escapeHtml(input.recipientEmail)}</strong>. Opengeni will show the invitation immediately after you sign in.</p>`,
145
+ };
146
+ }
147
+
148
+ export async function organizationUserSetupPayloadDigest(input: {
149
+ from: string;
150
+ to: string;
151
+ subject: string;
152
+ text: string;
153
+ html: string;
154
+ providerIdempotencyScope: string;
155
+ }): Promise<string> {
156
+ return await sha256Hex(
157
+ JSON.stringify({
158
+ version: 2,
159
+ providerIdempotencyScope: input.providerIdempotencyScope,
160
+ from: input.from,
161
+ to: input.to,
162
+ subject: input.subject,
163
+ text: input.text,
164
+ html: input.html,
165
+ }),
166
+ );
167
+ }
168
+
169
+ export async function resolveOrganizationUserSetupDeliveryEmail(
170
+ settings: Settings,
171
+ input: {
172
+ invitationId: string;
173
+ deliveryId: string;
174
+ senderEmail: string;
175
+ recipientEmail: string;
176
+ recipientName: string | null;
177
+ organizationName: string;
178
+ organizationRole: "owner" | "admin" | "member";
179
+ sharedWorkspaceAccess: OrganizationUserSetupEmailSnapshot["sharedWorkspaceAccess"];
180
+ providerIdempotencyScope: string;
181
+ frozenTransport: OrganizationUserSetupEmailTokenTransport | null;
182
+ frozenPayloadDigest: string | null;
183
+ },
184
+ ): Promise<{
185
+ transport: OrganizationUserSetupEmailTokenTransport;
186
+ token: string;
187
+ tokenDigest: string;
188
+ payloadDigest: string;
189
+ message: ReturnType<typeof renderOrganizationUserSetupEmail>;
190
+ }> {
191
+ const candidates: OrganizationUserSetupEmailTokenTransport[] = input.frozenTransport
192
+ ? [input.frozenTransport]
193
+ : input.frozenPayloadDigest
194
+ ? ["fragment", "query"]
195
+ : [settings.organizationUserSetupEmailTokenTransport];
196
+ for (const transport of candidates) {
197
+ const setup = await deriveOrganizationUserSetupToken(settings, {
198
+ invitationId: input.invitationId,
199
+ deliveryId: input.deliveryId,
200
+ transport,
201
+ });
202
+ const message = renderOrganizationUserSetupEmail({
203
+ senderEmail: input.senderEmail,
204
+ recipientEmail: input.recipientEmail,
205
+ recipientName: input.recipientName,
206
+ organizationName: input.organizationName,
207
+ organizationRole: input.organizationRole,
208
+ sharedWorkspaceAccess: input.sharedWorkspaceAccess,
209
+ setupUrl: setup.url,
210
+ });
211
+ const payloadDigest = await organizationUserSetupPayloadDigest({
212
+ ...message,
213
+ providerIdempotencyScope: input.providerIdempotencyScope,
214
+ });
215
+ if (input.frozenPayloadDigest && payloadDigest !== input.frozenPayloadDigest) continue;
216
+ return {
217
+ transport,
218
+ token: setup.token,
219
+ tokenDigest: setup.digest,
220
+ payloadDigest,
221
+ message,
222
+ };
223
+ }
224
+ throw new Error("Organization setup delivery transport does not match its frozen payload");
225
+ }
226
+
227
+ export async function organizationUserSetupRequestFingerprint(
228
+ settings: Settings,
229
+ input: { tokenDigest: string; name: string; password: string },
230
+ ): Promise<string> {
231
+ const key = await crypto.subtle.importKey(
232
+ "raw",
233
+ encoder.encode(requiredSetupSecret(settings)),
234
+ { name: "HMAC", hash: "SHA-256" },
235
+ false,
236
+ ["sign"],
237
+ );
238
+ const signature = await crypto.subtle.sign(
239
+ "HMAC",
240
+ key,
241
+ encoder.encode(
242
+ JSON.stringify({
243
+ tokenDigest: input.tokenDigest,
244
+ name: input.name,
245
+ password: input.password,
246
+ }),
247
+ ),
248
+ );
249
+ return hex(new Uint8Array(signature));
250
+ }
251
+
252
+ export async function selfServiceOrganizationSetupRequestFingerprint(input: {
253
+ authUserId: string;
254
+ organizationName: string;
255
+ }): Promise<string> {
256
+ const actorSubjectId = `user:${input.authUserId}`;
257
+ const organizationNameBytes = encoder.encode(input.organizationName);
258
+ return await sha256Hex(
259
+ `opengeni:self-service-organization:v1:${actorSubjectId}:${organizationNameBytes.byteLength}:${input.organizationName}`,
260
+ );
261
+ }
262
+
263
+ export async function organizationUserSetupTokenDigest(token: string): Promise<string> {
264
+ return await sha256Hex(token);
265
+ }
266
+
267
+ function requiredSetupSecret(settings: Settings): string {
268
+ if (!settings.betterAuthSecret) {
269
+ throw new Error("OPENGENI_BETTER_AUTH_SECRET is required for organization user setup");
270
+ }
271
+ return settings.betterAuthSecret;
272
+ }
273
+
274
+ function requiredPublicBaseUrl(settings: Settings): string {
275
+ if (!settings.publicBaseUrl) {
276
+ throw new Error("OPENGENI_PUBLIC_BASE_URL is required for organization user setup");
277
+ }
278
+ return settings.publicBaseUrl;
279
+ }
280
+
281
+ async function sha256Hex(value: string): Promise<string> {
282
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(value));
283
+ return hex(new Uint8Array(digest));
284
+ }
285
+
286
+ function hex(bytes: Uint8Array): string {
287
+ return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
288
+ }
289
+
290
+ function base64Url(bytes: Uint8Array): string {
291
+ let binary = "";
292
+ for (const byte of bytes) binary += String.fromCharCode(byte);
293
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
294
+ }
295
+
296
+ function titleCase(value: string): string {
297
+ return `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`;
298
+ }
299
+
300
+ function escapeHtml(value: string): string {
301
+ return value
302
+ .replaceAll("&", "&amp;")
303
+ .replaceAll("<", "&lt;")
304
+ .replaceAll(">", "&gt;")
305
+ .replaceAll('"', "&quot;")
306
+ .replaceAll("'", "&#039;");
307
+ }