@opengeni/api-router 0.30.1 → 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 (436) 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-office-import.d.ts +7 -4
  35. package/dist/editable-artifact-source-session.d.ts +3 -0
  36. package/dist/editable-artifact-websocket.d.ts +10 -18
  37. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  38. package/dist/fatal-process-boundary.d.ts +25 -0
  39. package/dist/github-access.d.ts +31 -2
  40. package/dist/http/acting-person.d.ts +24 -0
  41. package/dist/http/api-contract-compat.d.ts +19 -0
  42. package/dist/http/api-error.d.ts +27 -1
  43. package/dist/http/cors.d.ts +17 -0
  44. package/dist/http/deprecation.d.ts +42 -0
  45. package/dist/http/interaction-control-error.d.ts +13 -0
  46. package/dist/http/keyed-admission.d.ts +12 -0
  47. package/dist/http/local-browser-boundary.d.ts +72 -0
  48. package/dist/http/metrics-listener.d.ts +22 -0
  49. package/dist/http/path-identifier.d.ts +17 -0
  50. package/dist/http/registered-route-label.d.ts +16 -0
  51. package/dist/http/request-body.d.ts +27 -0
  52. package/dist/http/request-source.d.ts +52 -0
  53. package/dist/http/sse.d.ts +17 -6
  54. package/dist/http/unmatched-route.d.ts +18 -0
  55. package/dist/http/user-content.d.ts +36 -0
  56. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  57. package/dist/index.d.ts +10 -3
  58. package/dist/index.js +1129 -166
  59. package/dist/index.js.map +1 -1
  60. package/dist/integration-endpoint-test.d.ts +20 -0
  61. package/dist/integrations/api-integrations.d.ts +2 -2
  62. package/dist/integrations/atlassian.d.ts +29 -3
  63. package/dist/integrations/connect-authority.d.ts +1 -0
  64. package/dist/integrations/connect-callback-return.d.ts +6 -0
  65. package/dist/integrations/fiken.d.ts +16 -2
  66. package/dist/integrations/github-app-connect.d.ts +47 -0
  67. package/dist/integrations/github-installation-proof.d.ts +3 -0
  68. package/dist/integrations/github-lens-connect.d.ts +116 -0
  69. package/dist/integrations/github-skill-source.d.ts +1 -5
  70. package/dist/integrations/google-drive.d.ts +25 -0
  71. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  72. package/dist/integrations/marketplace-plugin.d.ts +2691 -0
  73. package/dist/integrations/oauth-client.d.ts +178 -18
  74. package/dist/integrations/oauth-profiles.d.ts +222 -0
  75. package/dist/integrations/oauth-return-path.d.ts +1 -0
  76. package/dist/integrations/personal-github-repositories.d.ts +84 -0
  77. package/dist/integrations/personal-github.d.ts +42 -0
  78. package/dist/integrations/pr-review-provider.d.ts +22 -0
  79. package/dist/integrations/provider-oauth.d.ts +19 -1
  80. package/dist/integrations/slack-app-home.d.ts +24 -0
  81. package/dist/integrations/slack-bot.d.ts +367 -12
  82. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  83. package/dist/integrations/slack-install.d.ts +20 -0
  84. package/dist/integrations/slack-interactions.d.ts +113 -10
  85. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  86. package/dist/integrations/slack-routing.d.ts +120 -0
  87. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  88. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  89. package/dist/integrations/social-oauth.d.ts +7 -0
  90. package/dist/interaction-agent-access.d.ts +15 -0
  91. package/dist/interaction-frame-proxy.d.ts +68 -0
  92. package/dist/interaction-metrics.d.ts +2 -0
  93. package/dist/mcp/action-catalog-types.d.ts +9 -0
  94. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  95. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  96. package/dist/mcp/child-read-evidence.d.ts +9 -0
  97. package/dist/mcp/command-read.d.ts +24 -0
  98. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  99. package/dist/mcp/contract-input.d.ts +11 -0
  100. package/dist/mcp/documents.d.ts +7 -5
  101. package/dist/mcp/files.d.ts +2 -2
  102. package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
  103. package/dist/mcp/knowledge-entries.d.ts +5 -0
  104. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  105. package/dist/mcp/receipts.d.ts +9 -0
  106. package/dist/mcp/request-abort.d.ts +19 -0
  107. package/dist/mcp/scheduled-task-input.d.ts +1189 -0
  108. package/dist/mcp/scheduled-task-view.d.ts +22 -4
  109. package/dist/mcp/server.d.ts +255 -3
  110. package/dist/mcp/session-event-view.d.ts +94 -0
  111. package/dist/mcp/session-view.d.ts +66 -2
  112. package/dist/mcp/session-wait.d.ts +174 -0
  113. package/dist/mcp-oauth.d.ts +52 -0
  114. package/dist/model-catalog.d.ts +11 -31
  115. package/dist/observability.d.ts +3 -1
  116. package/dist/organization-mcp.d.ts +52 -0
  117. package/dist/organization-recovery-notifications.d.ts +41 -0
  118. package/dist/routes/analytics-consent.d.ts +26 -0
  119. package/dist/routes/api-integrations.d.ts +25 -0
  120. package/dist/routes/api-keys.d.ts +17 -0
  121. package/dist/routes/artifact-catalog.d.ts +6 -0
  122. package/dist/routes/automations.d.ts +19 -0
  123. package/dist/routes/billing.d.ts +49 -0
  124. package/dist/routes/browser-sessions.d.ts +71 -3
  125. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  126. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  127. package/dist/routes/client-errors.d.ts +146 -0
  128. package/dist/routes/codex.d.ts +76 -2
  129. package/dist/routes/company-brain.d.ts +3 -0
  130. package/dist/routes/company-profile.d.ts +2 -1
  131. package/dist/routes/computer-sessions.d.ts +14 -0
  132. package/dist/routes/connect.d.ts +5 -0
  133. package/dist/routes/editable-artifacts.d.ts +2 -0
  134. package/dist/routes/external-identity-links.d.ts +6 -0
  135. package/dist/routes/feedback.d.ts +3 -0
  136. package/dist/routes/files.d.ts +4 -0
  137. package/dist/routes/github-browser-pages.d.ts +12 -0
  138. package/dist/routes/github.d.ts +2 -0
  139. package/dist/routes/insights-response-cache.d.ts +10 -0
  140. package/dist/routes/insights-usage.d.ts +12 -0
  141. package/dist/routes/insights.d.ts +30 -1
  142. package/dist/routes/knowledge.d.ts +3 -0
  143. package/dist/routes/managed-auth-session-sets.d.ts +26 -0
  144. package/dist/routes/managed-onboarding.d.ts +29 -0
  145. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  146. package/dist/routes/model-connection-access.d.ts +3 -0
  147. package/dist/routes/organization-integration-policy.d.ts +3 -0
  148. package/dist/routes/organization-integrations.d.ts +6 -0
  149. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  150. package/dist/routes/organization-model-providers.d.ts +3 -0
  151. package/dist/routes/organization-recovery.d.ts +19 -0
  152. package/dist/routes/organization-service-accounts.d.ts +3 -0
  153. package/dist/routes/organization-sessions.d.ts +26 -0
  154. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  155. package/dist/routes/personal-github.d.ts +3 -0
  156. package/dist/routes/pr-review-github.d.ts +3 -0
  157. package/dist/routes/pr-review.d.ts +3 -0
  158. package/dist/routes/session-artifact-associations.d.ts +4 -0
  159. package/dist/routes/session-history-imports.d.ts +9 -0
  160. package/dist/routes/sessions.d.ts +44 -6
  161. package/dist/routes/skill-content.d.ts +4 -0
  162. package/dist/routes/skill-install-authority.d.ts +6 -0
  163. package/dist/routes/subscription-account-pools.d.ts +34 -0
  164. package/dist/routes/subscription-pool-access.d.ts +16 -0
  165. package/dist/routes/supergrok.d.ts +6 -1
  166. package/dist/routes/usage-allowances.d.ts +12 -0
  167. package/dist/routes/user-resource-authorities.d.ts +3 -0
  168. package/dist/routes/workspace-artifacts.d.ts +2 -1
  169. package/dist/routes/workspace-capture.d.ts +10 -2
  170. package/dist/routes/workspace-integrations.d.ts +35 -0
  171. package/dist/routes/workspace-learning.d.ts +3 -0
  172. package/dist/routes/workspace-model-providers.d.ts +4 -0
  173. package/dist/routes/workspaces.d.ts +29 -0
  174. package/dist/sandbox/auth-callout.d.ts +8 -0
  175. package/dist/sandbox/channel-a.d.ts +19 -1
  176. package/dist/sandbox/connection-authority.d.ts +3 -0
  177. package/dist/sandbox/enrollment.d.ts +9 -1
  178. package/dist/sandbox/machines.d.ts +5 -2
  179. package/dist/sandbox/metrics-ingestion.d.ts +28 -20
  180. package/dist/sandbox/rematerialize.d.ts +2 -0
  181. package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
  182. package/dist/sandbox/viewer.d.ts +26 -1
  183. package/dist/sandbox-file-artifacts.d.ts +50 -0
  184. package/dist/scheduled-task-deletion.d.ts +13 -0
  185. package/dist/session-create-observability.d.ts +5 -0
  186. package/dist/session-event-view-MPS2OZD3.js +292 -0
  187. package/dist/session-event-view-MPS2OZD3.js.map +1 -0
  188. package/dist/site-session-origin.d.ts +13 -0
  189. package/dist/site-uploads.d.ts +41 -0
  190. package/dist/slack-reaction-files.d.ts +1 -1
  191. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  192. package/dist/temporal-schedule-sync.d.ts +26 -0
  193. package/dist/transcription/providers/xai-subscription.d.ts +1 -0
  194. package/dist/transcription/service.d.ts +2 -1
  195. package/dist/work-discovery-observability.d.ts +33 -0
  196. package/dist/workspace-artifact-content.d.ts +28 -0
  197. package/dist/workspace-artifact-provenance.d.ts +7 -0
  198. package/dist/workspace-delete-observability.d.ts +10 -0
  199. package/dist/workspace-deletion.d.ts +6 -0
  200. package/dist/workspace-state-projection.d.ts +3 -5
  201. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  202. package/dist/workspace-tool-gateway.d.ts +123 -0
  203. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  204. package/package.json +23 -19
  205. package/src/access-grant-rls.ts +75 -0
  206. package/src/admission-trace.ts +14 -0
  207. package/src/api-websocket.ts +23 -0
  208. package/src/app.ts +1974 -256
  209. package/src/artifact-catalog-cursor.ts +60 -0
  210. package/src/auth/managed-auth-attempt-context.ts +70 -0
  211. package/src/auth/managed-auth-rate-limits.ts +252 -0
  212. package/src/auth/managed-auth-session-adapter.ts +222 -0
  213. package/src/auth/managed-auth.ts +536 -60
  214. package/src/auth/managed-email.ts +174 -0
  215. package/src/auth/managed-sign-in-notifications.ts +81 -0
  216. package/src/auth/new-signups-gate.ts +49 -0
  217. package/src/auth/organization-user-setup.ts +307 -0
  218. package/src/auth/signup-funnel-metrics.ts +164 -0
  219. package/src/browser-controller-authority.ts +1 -186
  220. package/src/browser-ephemeral.ts +39 -0
  221. package/src/browser-state-authority.ts +1 -236
  222. package/src/claude-subscription-account-conflicts.ts +12 -0
  223. package/src/claude-subscription-account-usage.ts +48 -0
  224. package/src/claude-subscription-oauth.ts +362 -0
  225. package/src/claude-subscription-profile.ts +73 -0
  226. package/src/claude-subscription-usage.ts +97 -0
  227. package/src/claude-workspace-connection.ts +82 -0
  228. package/src/codemode.ts +194 -30
  229. package/src/codex-realtime.ts +8 -2
  230. package/src/company-brain-okf.ts +340 -0
  231. package/src/connected-machine-computer-access.ts +33 -0
  232. package/src/connection-ownership.ts +199 -0
  233. package/src/controller-data-plane.ts +81 -0
  234. package/src/editable-artifact-native-kernel.ts +26 -15
  235. package/src/editable-artifact-office-import.ts +77 -9
  236. package/src/editable-artifact-production.ts +23 -7
  237. package/src/editable-artifact-source-session.ts +123 -0
  238. package/src/editable-artifact-websocket.ts +27 -21
  239. package/src/editable-artifact-workspace-files.ts +31 -6
  240. package/src/fatal-process-boundary.ts +246 -0
  241. package/src/gateway-realtime.ts +1 -1
  242. package/src/github-access.ts +147 -5
  243. package/src/http/acting-person.ts +71 -0
  244. package/src/http/api-contract-compat.ts +49 -0
  245. package/src/http/api-error.ts +115 -1
  246. package/src/http/auth.ts +48 -0
  247. package/src/http/cors.ts +67 -0
  248. package/src/http/deprecation.ts +118 -0
  249. package/src/http/interaction-control-error.ts +164 -0
  250. package/src/http/keyed-admission.ts +29 -0
  251. package/src/http/local-browser-boundary.ts +343 -0
  252. package/src/http/metrics-listener.ts +57 -0
  253. package/src/http/path-identifier.ts +54 -0
  254. package/src/http/registered-route-label.ts +34 -0
  255. package/src/http/request-body.ts +127 -0
  256. package/src/http/request-source.ts +178 -0
  257. package/src/http/sse.ts +486 -132
  258. package/src/http/unmatched-route.ts +60 -0
  259. package/src/http/user-content.ts +158 -0
  260. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  261. package/src/index.ts +157 -52
  262. package/src/integration-endpoint-test.ts +291 -0
  263. package/src/integrations/api-integrations.ts +98 -11
  264. package/src/integrations/atlassian.ts +474 -136
  265. package/src/integrations/connect-authority.ts +2 -0
  266. package/src/integrations/connect-callback-return.ts +86 -0
  267. package/src/integrations/fiken.ts +354 -64
  268. package/src/integrations/github-app-connect.ts +435 -0
  269. package/src/integrations/github-installation-proof.ts +60 -0
  270. package/src/integrations/github-lens-connect.ts +76 -0
  271. package/src/integrations/github-skill-source.ts +2 -142
  272. package/src/integrations/google-drive.ts +820 -222
  273. package/src/integrations/integration-commit-authority.ts +69 -0
  274. package/src/integrations/marketplace-plugin.ts +48 -0
  275. package/src/integrations/oauth-client.ts +1356 -459
  276. package/src/integrations/oauth-profiles.ts +537 -0
  277. package/src/integrations/oauth-return-path.ts +18 -0
  278. package/src/integrations/personal-github-repositories.ts +674 -0
  279. package/src/integrations/personal-github.ts +825 -0
  280. package/src/integrations/pr-review-provider.ts +246 -0
  281. package/src/integrations/provider-oauth.ts +250 -22
  282. package/src/integrations/slack-app-home.ts +300 -0
  283. package/src/integrations/slack-bot.ts +963 -239
  284. package/src/integrations/slack-file-upload-flow.ts +147 -0
  285. package/src/integrations/slack-install.ts +83 -0
  286. package/src/integrations/slack-interactions.ts +3170 -474
  287. package/src/integrations/slack-mrkdwn.ts +193 -0
  288. package/src/integrations/slack-routing.ts +337 -0
  289. package/src/integrations/slack-session-defaults.ts +224 -0
  290. package/src/integrations/slack-task-file-upload.ts +323 -0
  291. package/src/integrations/social-oauth.ts +213 -15
  292. package/src/interaction-agent-access.ts +48 -0
  293. package/src/interaction-frame-proxy.ts +409 -0
  294. package/src/interaction-metrics.ts +30 -0
  295. package/src/mcp/action-catalog-types.ts +9 -0
  296. package/src/mcp/action-catalog.gen.ts +6696 -0
  297. package/src/mcp/capability-account-readiness.ts +17 -0
  298. package/src/mcp/child-read-evidence.ts +19 -0
  299. package/src/mcp/command-read.ts +109 -0
  300. package/src/mcp/company-profile-agent-admin.ts +198 -0
  301. package/src/mcp/contract-input.ts +141 -0
  302. package/src/mcp/documents.ts +65 -377
  303. package/src/mcp/editable-artifacts.ts +12 -6
  304. package/src/mcp/files.ts +80 -38
  305. package/src/mcp/first-party-tool-permissions.ts +305 -0
  306. package/src/mcp/knowledge-entries.ts +322 -0
  307. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  308. package/src/mcp/receipts.ts +34 -0
  309. package/src/mcp/request-abort.ts +44 -0
  310. package/src/mcp/scheduled-task-input.ts +76 -0
  311. package/src/mcp/scheduled-task-view.ts +44 -3
  312. package/src/mcp/server.ts +3030 -1267
  313. package/src/mcp/session-event-view.ts +383 -0
  314. package/src/mcp/session-view.ts +84 -3
  315. package/src/mcp/session-wait.ts +665 -0
  316. package/src/mcp-oauth.ts +1123 -0
  317. package/src/model-catalog.ts +91 -333
  318. package/src/observability.ts +15 -1
  319. package/src/organization-mcp.ts +397 -0
  320. package/src/organization-recovery-notifications.ts +103 -0
  321. package/src/routes/analytics-consent.ts +123 -0
  322. package/src/routes/api-integrations.ts +364 -74
  323. package/src/routes/api-keys.ts +336 -10
  324. package/src/routes/artifact-catalog.ts +279 -0
  325. package/src/routes/automations.ts +768 -0
  326. package/src/routes/billing.ts +461 -46
  327. package/src/routes/browser-sessions.ts +839 -131
  328. package/src/routes/canonical-human-identities.ts +90 -14
  329. package/src/routes/capabilities.ts +107 -6
  330. package/src/routes/channels.ts +20 -3
  331. package/src/routes/claude-subscription-accounts.ts +334 -0
  332. package/src/routes/claude-subscription-oauth.ts +142 -0
  333. package/src/routes/client-errors.ts +362 -0
  334. package/src/routes/codex.ts +833 -116
  335. package/src/routes/company-brain.ts +322 -0
  336. package/src/routes/company-profile.ts +65 -1
  337. package/src/routes/computer-sessions.ts +557 -106
  338. package/src/routes/connect.ts +1938 -0
  339. package/src/routes/connections.ts +1040 -349
  340. package/src/routes/documents.ts +488 -287
  341. package/src/routes/editable-artifacts.ts +79 -9
  342. package/src/routes/enrollments.ts +135 -39
  343. package/src/routes/environments.ts +154 -58
  344. package/src/routes/external-identity-links.ts +201 -0
  345. package/src/routes/feedback.ts +113 -0
  346. package/src/routes/files.ts +337 -39
  347. package/src/routes/github-browser-pages.ts +162 -0
  348. package/src/routes/github.ts +446 -144
  349. package/src/routes/insights-response-cache.ts +83 -0
  350. package/src/routes/insights-usage.ts +345 -0
  351. package/src/routes/insights.ts +153 -19
  352. package/src/routes/install.ts +133 -38
  353. package/src/routes/integration-facets.ts +107 -33
  354. package/src/routes/interaction-resources.ts +30 -3
  355. package/src/routes/knowledge.ts +488 -0
  356. package/src/routes/machines.ts +313 -15
  357. package/src/routes/managed-auth-session-sets.ts +1218 -0
  358. package/src/routes/managed-onboarding.ts +337 -0
  359. package/src/routes/managed-sign-in-methods.ts +433 -0
  360. package/src/routes/memory-slack-publications.ts +3 -2
  361. package/src/routes/model-connection-access.ts +208 -0
  362. package/src/routes/organization-integration-policy.ts +116 -0
  363. package/src/routes/organization-integrations.ts +326 -0
  364. package/src/routes/organization-mcp-connections.ts +178 -0
  365. package/src/routes/organization-memberships.ts +1019 -27
  366. package/src/routes/organization-model-providers.ts +293 -0
  367. package/src/routes/organization-recovery.ts +325 -0
  368. package/src/routes/organization-service-accounts.ts +128 -0
  369. package/src/routes/organization-sessions.ts +350 -0
  370. package/src/routes/personal-github-git-broker.ts +785 -0
  371. package/src/routes/personal-github.ts +370 -0
  372. package/src/routes/plugins.ts +251 -22
  373. package/src/routes/pr-review-github.ts +881 -0
  374. package/src/routes/pr-review.ts +622 -0
  375. package/src/routes/preference-registry.ts +12 -93
  376. package/src/routes/rigs.ts +167 -50
  377. package/src/routes/scheduled-tasks.ts +346 -41
  378. package/src/routes/session-artifact-associations.ts +87 -0
  379. package/src/routes/session-history-imports.ts +280 -0
  380. package/src/routes/sessions.ts +2306 -264
  381. package/src/routes/skill-content.ts +297 -0
  382. package/src/routes/skill-install-authority.ts +64 -0
  383. package/src/routes/skills.ts +62 -9
  384. package/src/routes/social.ts +46 -18
  385. package/src/routes/subscription-account-pools.ts +436 -0
  386. package/src/routes/subscription-pool-access.ts +126 -0
  387. package/src/routes/supergrok.ts +219 -303
  388. package/src/routes/transcription-recordings.ts +38 -3
  389. package/src/routes/transcriptions.ts +4 -0
  390. package/src/routes/usage-allowances.ts +378 -0
  391. package/src/routes/user-resource-authorities.ts +138 -0
  392. package/src/routes/video-generation.ts +14 -10
  393. package/src/routes/workspace-artifacts.ts +275 -69
  394. package/src/routes/workspace-capture.ts +21 -0
  395. package/src/routes/workspace-integrations.ts +576 -0
  396. package/src/routes/workspace-learning.ts +159 -0
  397. package/src/routes/workspace-model-providers.ts +148 -0
  398. package/src/routes/workspace-state.ts +38 -25
  399. package/src/routes/workspaces.ts +1004 -127
  400. package/src/sandbox/auth-callout.ts +96 -19
  401. package/src/sandbox/channel-a.ts +323 -24
  402. package/src/sandbox/connection-authority.ts +3 -0
  403. package/src/sandbox/enrollment.ts +81 -6
  404. package/src/sandbox/machines.ts +218 -76
  405. package/src/sandbox/metrics-ingestion.ts +411 -62
  406. package/src/sandbox/rematerialize.ts +121 -19
  407. package/src/sandbox/session-attach-variable-sets.ts +131 -0
  408. package/src/sandbox/viewer.ts +280 -25
  409. package/src/sandbox-file-artifacts.ts +413 -0
  410. package/src/scheduled-task-deletion.ts +138 -0
  411. package/src/session-create-observability.ts +46 -0
  412. package/src/session-realtime-context.ts +6 -3
  413. package/src/site-session-origin.ts +48 -0
  414. package/src/site-uploads.ts +224 -0
  415. package/src/slack-reaction-files.ts +16 -5
  416. package/src/temporal-schedule-cleanup.ts +13 -0
  417. package/src/temporal-schedule-sync.ts +74 -0
  418. package/src/transcription/providers/codex-subscription.ts +2 -0
  419. package/src/transcription/providers/openai.ts +1 -0
  420. package/src/transcription/providers/xai-subscription.ts +49 -3
  421. package/src/transcription/segmenter.ts +8 -2
  422. package/src/transcription/service.ts +43 -6
  423. package/src/work-discovery-observability.ts +121 -0
  424. package/src/workspace-artifact-content.ts +144 -0
  425. package/src/workspace-artifact-provenance.ts +104 -0
  426. package/src/workspace-delete-observability.ts +75 -0
  427. package/src/workspace-deletion.ts +73 -0
  428. package/src/workspace-state-projection.ts +18 -148
  429. package/src/workspace-tool-gateway-observability.ts +100 -0
  430. package/src/workspace-tool-gateway.ts +870 -0
  431. package/src/workspace-webhook-dispatch.ts +190 -0
  432. package/src/xai-subscription-auth.ts +9 -2
  433. package/dist/chunk-2PQMSRCB.js +0 -49759
  434. package/dist/chunk-2PQMSRCB.js.map +0 -1
  435. package/dist/routes/packs.d.ts +0 -3
  436. package/src/routes/packs.ts +0 -729
@@ -0,0 +1,825 @@
1
+ import { withOrganizationIntegrationAcquisition } from "@opengeni/db/organization-integration-policy";
2
+ import { claimOAuthAcquisition, finishOAuthAcquisition } from "./oauth-client";
3
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
4
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
5
+ import { requireConnectOwnerAuthority } from "./connect-authority";
6
+ import { personalGitHubOAuthCallbackUrl, type Settings } from "@opengeni/config";
7
+ import {
8
+ PERSONAL_GITHUB_AUTHORIZATION_URL,
9
+ PERSONAL_GITHUB_CREDENTIAL_ROLE,
10
+ PERSONAL_GITHUB_PROVIDER_DOMAIN,
11
+ PERSONAL_GITHUB_PROVIDER_FAMILY,
12
+ PERSONAL_GITHUB_REQUESTED_SCOPES,
13
+ PERSONAL_GITHUB_TOKEN_URL,
14
+ PERSONAL_GITHUB_USER_URL,
15
+ PersonalGitHubConnectionMetadata,
16
+ type PersonalGitHubDisconnectRequest,
17
+ PersonalGitHubOAuthStartResponse,
18
+ isPersonalGitHubConnection,
19
+ } from "@opengeni/contracts/personal-github";
20
+ import {
21
+ externalActorContinuationForAuthorization,
22
+ requireEnvironmentEncryption,
23
+ type AccessGrantAuthorization,
24
+ type ApiRouteDeps,
25
+ } from "@opengeni/core";
26
+ import {
27
+ consumeIntegrationOAuthStateNonce,
28
+ finishConnectOperation,
29
+ getConnectAttempt,
30
+ type Database,
31
+ ConnectionDisconnectGenerationError,
32
+ ConnectionDisconnectIdempotencyError,
33
+ decryptEnvironmentValue,
34
+ disconnectConnectionIdempotently,
35
+ encryptEnvironmentValue,
36
+ getConnectionMetadata,
37
+ listConnectionsMetadata,
38
+ persistProviderOAuthConnection,
39
+ } from "@opengeni/db";
40
+ import { createSignedState, readSignedState } from "@opengeni/github";
41
+ import { readResponseJsonBounded, type FetchLike } from "@opengeni/network";
42
+ import { HTTPException } from "hono/http-exception";
43
+ import {
44
+ PERSONAL_OWNER_VERIFIED_STATE_CLAIM,
45
+ personalOnlyConnectionPrincipalMessage,
46
+ personalOwnerStateAccepted,
47
+ personalOwnerVerifiedInState,
48
+ } from "../connection-ownership";
49
+ import {
50
+ integrationBaseUrl,
51
+ oauthStateFailureReturn,
52
+ oauthStateTtlMs,
53
+ requireIntegrationsStateSecret,
54
+ workspaceIntegrationsPath,
55
+ } from "./oauth-client";
56
+
57
+ const PERSONAL_GITHUB_OAUTH_STATE_KIND = "personal_github_oauth";
58
+ const GITHUB_RESPONSE_MAX_BYTES = 256 * 1024;
59
+ const GITHUB_REQUEST_TIMEOUT_MS = 10_000;
60
+ const GITHUB_API_VERSION = "2022-11-28";
61
+
62
+ type PersonalGitHubOAuthState = {
63
+ connectAttemptId?: string;
64
+ externalContinuation?: ExternalActorContinuation;
65
+ accountId: string;
66
+ workspaceId: string;
67
+ subjectId: string;
68
+ personalOwnerVerified: boolean;
69
+ canonicalManagedHumanSession: boolean;
70
+ returnPath: string;
71
+ encryptedPkceVerifier: string;
72
+ oauthEnvironment: string;
73
+ oauthClientMarker: string;
74
+ connectionId?: string;
75
+ connectionVersion?: number;
76
+ nonce: string;
77
+ iat: number;
78
+ };
79
+
80
+ type GitHubToken = {
81
+ accessToken: string;
82
+ refreshToken: string | null;
83
+ tokenType: "Bearer";
84
+ expiresAt: Date | null;
85
+ refreshTokenExpiresAt: Date | null;
86
+ scopes: string[];
87
+ };
88
+
89
+ type GitHubIdentity = {
90
+ id: string;
91
+ login: string;
92
+ };
93
+
94
+ type PersonalGitHubFailureReason =
95
+ | "account_mismatch"
96
+ | "client_changed"
97
+ | "connection_conflict"
98
+ | "disabled"
99
+ | "identity_failed"
100
+ | "invalid_state"
101
+ | "missing_code"
102
+ | "not_authorized"
103
+ | "provider_denied"
104
+ | "scope_not_granted"
105
+ | "state_replayed"
106
+ | "token_exchange_failed";
107
+
108
+ class PersonalGitHubCallbackError extends Error {
109
+ constructor(readonly reason: PersonalGitHubFailureReason) {
110
+ super(reason);
111
+ this.name = "PersonalGitHubCallbackError";
112
+ }
113
+ }
114
+
115
+ export function personalGitHubClientMarker(clientId: string): string {
116
+ return createHash("sha256").update(clientId).digest("hex").slice(0, 32);
117
+ }
118
+
119
+ export function requirePersonalGitHubOAuthSettings(settings: Settings): {
120
+ clientId: string;
121
+ clientSecret: string;
122
+ callbackUrl: string;
123
+ oauthEnvironment: string;
124
+ oauthClientMarker: string;
125
+ } {
126
+ if (!settings.githubPersonalOauthEnabled) {
127
+ throw new HTTPException(404, {
128
+ message: "personal GitHub OAuth is not enabled for this deployment",
129
+ });
130
+ }
131
+ const clientId = settings.githubPersonalOauthClientId?.trim();
132
+ const clientSecret = settings.githubPersonalOauthClientSecret?.trim();
133
+ const callbackUrl = personalGitHubOAuthCallbackUrl(settings.publicBaseUrl);
134
+ if (!clientId || !clientSecret || !callbackUrl) {
135
+ throw new HTTPException(503, { message: "personal GitHub OAuth is not configured" });
136
+ }
137
+ return {
138
+ clientId,
139
+ clientSecret,
140
+ callbackUrl,
141
+ oauthEnvironment: settings.environment,
142
+ oauthClientMarker: personalGitHubClientMarker(clientId),
143
+ };
144
+ }
145
+
146
+ export async function listPersonalGitHubConnections(
147
+ deps: ApiRouteDeps,
148
+ input: { workspaceId: string; subjectId: string },
149
+ ) {
150
+ const visible = await listConnectionsMetadata(deps.db, input.workspaceId, input.subjectId);
151
+ return visible.filter(
152
+ (connection) =>
153
+ connection.subjectId === input.subjectId && isPersonalGitHubConnection(connection),
154
+ );
155
+ }
156
+
157
+ export async function disconnectPersonalGitHub(
158
+ deps: ApiRouteDeps,
159
+ input: {
160
+ workspaceId: string;
161
+ subjectId: string;
162
+ connectionId: string;
163
+ payload: PersonalGitHubDisconnectRequest;
164
+ },
165
+ ) {
166
+ const existing = await getConnectionMetadata(
167
+ deps.db,
168
+ input.workspaceId,
169
+ input.connectionId,
170
+ input.subjectId,
171
+ );
172
+ if (
173
+ !existing ||
174
+ existing.subjectId !== input.subjectId ||
175
+ !isPersonalGitHubConnection(existing)
176
+ ) {
177
+ throw new HTTPException(404, { message: "personal GitHub connection not found" });
178
+ }
179
+ const metadata = PersonalGitHubConnectionMetadata.parse(existing.metadata);
180
+ try {
181
+ return await disconnectConnectionIdempotently(deps.db, {
182
+ accountId: existing.accountId,
183
+ workspaceId: input.workspaceId,
184
+ subjectId: input.subjectId,
185
+ connectionId: existing.id,
186
+ expectedVersion: input.payload.expectedVersion,
187
+ idempotencyKey: input.payload.idempotencyKey,
188
+ metadata: PersonalGitHubConnectionMetadata.parse({
189
+ ...metadata,
190
+ disconnectedAt: new Date().toISOString(),
191
+ }),
192
+ lastError: null,
193
+ updatedBySubjectId: input.subjectId,
194
+ });
195
+ } catch (error) {
196
+ if (error instanceof ConnectionDisconnectIdempotencyError) {
197
+ throw new HTTPException(409, {
198
+ message: "personal GitHub disconnect key was already used for another operation",
199
+ });
200
+ }
201
+ if (error instanceof ConnectionDisconnectGenerationError) {
202
+ throw new HTTPException(409, {
203
+ message: "personal GitHub connection changed; refresh before disconnecting",
204
+ });
205
+ }
206
+ throw error;
207
+ }
208
+ }
209
+
210
+ export async function startPersonalGitHubOAuth(
211
+ deps: ApiRouteDeps,
212
+ input: {
213
+ access: AccessGrantAuthorization;
214
+ workspaceId: string;
215
+ connectionId?: string;
216
+ returnPath?: string;
217
+ connectAttemptId?: string;
218
+ },
219
+ ) {
220
+ const oauth = requirePersonalGitHubOAuthSettings(deps.settings);
221
+ const { grant } = input.access;
222
+ await withOrganizationIntegrationAcquisition(deps.db, grant, ["github-personal"], async () => {});
223
+ const existing = input.connectionId
224
+ ? await getConnectionMetadata(deps.db, input.workspaceId, input.connectionId, grant.subjectId)
225
+ : null;
226
+ if (input.connectionId && !existing) {
227
+ throw new HTTPException(404, { message: "personal GitHub connection not found" });
228
+ }
229
+ if (
230
+ existing &&
231
+ (existing.subjectId !== grant.subjectId || !isPersonalGitHubConnection(existing))
232
+ ) {
233
+ throw new HTTPException(422, {
234
+ message: "connectionId is not the caller's personal GitHub connection",
235
+ });
236
+ }
237
+
238
+ const key = requireEnvironmentEncryption(deps.settings);
239
+ const verifier = randomBytes(48).toString("base64url");
240
+ const returnPath = personalGitHubReturnPath(input.workspaceId, input.returnPath);
241
+ const state = createSignedState(requireIntegrationsStateSecret(deps.settings), {
242
+ accountId: grant.accountId,
243
+ workspaceId: input.workspaceId,
244
+ subjectId: grant.subjectId,
245
+ kind: PERSONAL_GITHUB_OAUTH_STATE_KIND,
246
+ ...(input.connectAttemptId ? { connectAttemptId: input.connectAttemptId } : {}),
247
+ ...(externalActorContinuationForAuthorization(input.access)
248
+ ? {
249
+ encryptedExternalContinuation: encryptEnvironmentValue(
250
+ key,
251
+ JSON.stringify(externalActorContinuationForAuthorization(input.access)),
252
+ ),
253
+ }
254
+ : {}),
255
+ [PERSONAL_OWNER_VERIFIED_STATE_CLAIM]: true,
256
+ canonicalManagedHumanSession: input.access.canonicalManagedHumanSession,
257
+ returnPath,
258
+ encryptedPkceVerifier: encryptEnvironmentValue(key, verifier),
259
+ oauthEnvironment: oauth.oauthEnvironment,
260
+ oauthClientMarker: oauth.oauthClientMarker,
261
+ ...(existing ? { connectionId: existing.id, connectionVersion: existing.version } : {}),
262
+ });
263
+ const authorizationUrl = new URL(PERSONAL_GITHUB_AUTHORIZATION_URL);
264
+ authorizationUrl.searchParams.set("client_id", oauth.clientId);
265
+ authorizationUrl.searchParams.set("redirect_uri", oauth.callbackUrl);
266
+ authorizationUrl.searchParams.set("scope", PERSONAL_GITHUB_REQUESTED_SCOPES.join(" "));
267
+ authorizationUrl.searchParams.set("state", state);
268
+ authorizationUrl.searchParams.set(
269
+ "code_challenge",
270
+ createHash("sha256").update(verifier).digest("base64url"),
271
+ );
272
+ authorizationUrl.searchParams.set("code_challenge_method", "S256");
273
+ return PersonalGitHubOAuthStartResponse.parse({
274
+ authorizationUrl: authorizationUrl.toString(),
275
+ expiresAt: new Date(Date.now() + oauthStateTtlMs).toISOString(),
276
+ });
277
+ }
278
+
279
+ export async function completePersonalGitHubOAuthCallback(
280
+ deps: ApiRouteDeps,
281
+ input: {
282
+ code?: string;
283
+ state?: string;
284
+ error?: string;
285
+ requestUrl: string;
286
+ },
287
+ ): Promise<{ redirectTo: string; exactReturn?: boolean }> {
288
+ const apiBaseUrl = integrationBaseUrl(deps.settings.publicBaseUrl, input.requestUrl);
289
+ const returnBaseUrl = deps.settings.webBaseUrl?.replace(/\/+$/u, "") ?? apiBaseUrl;
290
+ let state: PersonalGitHubOAuthState | null = null;
291
+ let exactReturnUrl: string | undefined;
292
+ let operation: { attemptId: string; operationId: string; inputDigest: string } | undefined;
293
+ try {
294
+ state = readPersonalGitHubOAuthState(input.state, deps.settings);
295
+ if (state.connectAttemptId) {
296
+ const stored = await getConnectAttempt(deps.db, state, state.connectAttemptId);
297
+ if (
298
+ stored.attempt.providerId !== "github-personal" ||
299
+ stored.attempt.ownership !== "personal"
300
+ )
301
+ throw new PersonalGitHubCallbackError("invalid_state");
302
+ exactReturnUrl = stored.returnUrl;
303
+ operation = {
304
+ attemptId: state.connectAttemptId,
305
+ operationId: `oauth:${state.nonce}`,
306
+ inputDigest: createHash("sha256").update(input.state!).digest("hex"),
307
+ };
308
+ const claim = await claimOAuthAcquisition(
309
+ deps.db,
310
+ state,
311
+ {
312
+ ...operation,
313
+ expectedRevision: stored.attempt.revision,
314
+ authorize: (tx, _attempt, origin) =>
315
+ requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
316
+ },
317
+ "github-personal",
318
+ Boolean(input.code && !input.error),
319
+ );
320
+ if (claim.status === "replayed") return { redirectTo: exactReturnUrl, exactReturn: true };
321
+ }
322
+ await requirePersonalGitHubCallbackGrant(deps, state);
323
+ const consumed = await consumeIntegrationOAuthStateNonce(deps.db, {
324
+ accountId: state.accountId,
325
+ workspaceId: state.workspaceId,
326
+ subjectId: state.subjectId,
327
+ nonce: state.nonce,
328
+ expiresAt: new Date(state.iat * 1000 + oauthStateTtlMs),
329
+ now: new Date(),
330
+ });
331
+ if (!consumed) throw new PersonalGitHubCallbackError("state_replayed");
332
+ if (operation && (input.error || !input.code)) {
333
+ await finishConnectOperation(deps.db, state, {
334
+ ...operation,
335
+ authorize: (tx, _attempt, origin) =>
336
+ requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
337
+ commit: async (_tx, current) => ({
338
+ ...current,
339
+ revision: current.revision + 1,
340
+ state: input.error === "access_denied" ? "cancelled" : "failed",
341
+ nextAction: { type: "none" },
342
+ error: {
343
+ code: input.error ? "provider_denied" : "missing_code",
344
+ message: "Authorization was not completed. Start a new connection attempt.",
345
+ retryable: false,
346
+ },
347
+ }),
348
+ });
349
+ return { redirectTo: exactReturnUrl!, exactReturn: true };
350
+ }
351
+ if (input.error) throw new PersonalGitHubCallbackError("provider_denied");
352
+ if (!input.code) throw new PersonalGitHubCallbackError("missing_code");
353
+ await withOrganizationIntegrationAcquisition(
354
+ deps.db,
355
+ state,
356
+ ["github-personal"],
357
+ async () => {},
358
+ );
359
+
360
+ const oauth = requirePersonalGitHubOAuthSettings(deps.settings);
361
+ if (
362
+ oauth.oauthEnvironment !== state.oauthEnvironment ||
363
+ oauth.oauthClientMarker !== state.oauthClientMarker
364
+ ) {
365
+ throw new PersonalGitHubCallbackError("client_changed");
366
+ }
367
+ const key = requireEnvironmentEncryption(deps.settings);
368
+ let verifier: string;
369
+ try {
370
+ verifier = decryptEnvironmentValue(key, state.encryptedPkceVerifier);
371
+ } catch {
372
+ throw new PersonalGitHubCallbackError("invalid_state");
373
+ }
374
+ const fetchImpl = deps.githubPersonalFetch ?? fetch;
375
+ const token = await exchangePersonalGitHubAuthorizationCode(
376
+ fetchImpl,
377
+ oauth,
378
+ input.code,
379
+ verifier,
380
+ );
381
+ assertExactPersonalGitHubScopes(token.scopes);
382
+ const identity = await fetchPersonalGitHubIdentity(fetchImpl, token.accessToken);
383
+ await requirePersonalGitHubCallbackGrant(deps, state);
384
+
385
+ const requested = state.connectionId
386
+ ? await getConnectionMetadata(deps.db, state.workspaceId, state.connectionId, state.subjectId)
387
+ : null;
388
+ if (
389
+ state.connectionId &&
390
+ (!requested ||
391
+ requested.version !== state.connectionVersion ||
392
+ !isPersonalGitHubConnection(requested))
393
+ ) {
394
+ throw new PersonalGitHubCallbackError("connection_conflict");
395
+ }
396
+ const requestedMetadata = requested
397
+ ? PersonalGitHubConnectionMetadata.parse(requested.metadata)
398
+ : null;
399
+ if (requestedMetadata && requestedMetadata.providerPrincipalId !== identity.id) {
400
+ throw new PersonalGitHubCallbackError("account_mismatch");
401
+ }
402
+ if (!requested) {
403
+ const active = (
404
+ await listPersonalGitHubConnections(deps, {
405
+ workspaceId: state.workspaceId,
406
+ subjectId: state.subjectId,
407
+ })
408
+ ).filter((connection) => connection.status !== "revoked");
409
+ if (
410
+ active.some(
411
+ (connection) =>
412
+ PersonalGitHubConnectionMetadata.parse(connection.metadata).providerPrincipalId !==
413
+ identity.id,
414
+ )
415
+ ) {
416
+ throw new PersonalGitHubCallbackError("account_mismatch");
417
+ }
418
+ }
419
+
420
+ const now = new Date().toISOString();
421
+ const metadata = PersonalGitHubConnectionMetadata.parse({
422
+ ...(requested?.metadata ?? {}),
423
+ credentialRole: PERSONAL_GITHUB_CREDENTIAL_ROLE,
424
+ providerFamily: PERSONAL_GITHUB_PROVIDER_FAMILY,
425
+ providerPrincipalId: identity.id,
426
+ githubUserId: identity.id,
427
+ githubLogin: identity.login,
428
+ oauthEnvironment: oauth.oauthEnvironment,
429
+ oauthClientMarker: oauth.oauthClientMarker,
430
+ credentialBindingId: requestedMetadata?.credentialBindingId ?? randomUUID(),
431
+ connectedAt: requestedMetadata?.connectedAt ?? now,
432
+ lastVerifiedAt: now,
433
+ disconnectedAt: null,
434
+ refreshTokenExpiresAt: token.refreshTokenExpiresAt?.toISOString() ?? null,
435
+ });
436
+ const credentialEncrypted = encryptEnvironmentValue(
437
+ key,
438
+ JSON.stringify({
439
+ access_token: token.accessToken,
440
+ ...(token.refreshToken ? { refresh_token: token.refreshToken } : {}),
441
+ token_type: token.tokenType,
442
+ ...(token.expiresAt ? { expires_at: token.expiresAt.toISOString() } : {}),
443
+ ...(token.refreshTokenExpiresAt
444
+ ? { refresh_token_expires_at: token.refreshTokenExpiresAt.toISOString() }
445
+ : {}),
446
+ scope: token.scopes.join(" "),
447
+ token_endpoint: PERSONAL_GITHUB_TOKEN_URL,
448
+ client_id: oauth.clientId,
449
+ token_endpoint_auth_method: "client_secret_post",
450
+ scope_parameter_supported: false,
451
+ }),
452
+ );
453
+ const acceptedState = state;
454
+ const persist = (tx: Database) => {
455
+ // Capture the validated immutable callback state for delayed persistence.
456
+ // eslint-disable-next-line no-shadow
457
+ const state = acceptedState;
458
+ return persistProviderOAuthConnection(tx, {
459
+ accountId: state.accountId,
460
+ workspaceId: state.workspaceId,
461
+ subjectId: state.subjectId,
462
+ visibleToSubjectId: state.subjectId,
463
+ providerDomain: PERSONAL_GITHUB_PROVIDER_DOMAIN,
464
+ kind: "oauth2",
465
+ status: "active",
466
+ credentialEncrypted,
467
+ grantedScopes: token.scopes,
468
+ expiresAt: token.expiresAt,
469
+ metadata,
470
+ createdBySubjectId: state.subjectId,
471
+ updatedBySubjectId: state.subjectId,
472
+ credentialRole: PERSONAL_GITHUB_CREDENTIAL_ROLE,
473
+ providerFamily: PERSONAL_GITHUB_PROVIDER_FAMILY,
474
+ providerPrincipalId: identity.id,
475
+ requireLiveUserAuthority: true,
476
+ requiredLiveUserPermission: "connections:write",
477
+ // The external lane has passed its own owning-user proof; it does not
478
+ // impersonate a native login. Both use the same personal workspace anchor.
479
+ allowCanonicalPersonalWorkspaceOwner:
480
+ state.canonicalManagedHumanSession || Boolean(state.externalContinuation),
481
+ authorize: (locked) => requireConnectOwnerAuthority(locked, state, "connections:write"),
482
+ exclusiveProviderPrincipalPerOwner: true,
483
+ preserveExistingMetadataKeys: ["credentialBindingId", "connectedAt"],
484
+ ...(state.connectionId
485
+ ? {
486
+ requestedConnectionId: state.connectionId,
487
+ requestedConnectionVersion: state.connectionVersion,
488
+ }
489
+ : {}),
490
+ });
491
+ };
492
+ if (operation) {
493
+ await finishOAuthAcquisition(
494
+ deps.db,
495
+ acceptedState,
496
+ {
497
+ ...operation,
498
+ authorize: (tx, _attempt, origin) =>
499
+ requireConnectOwnerAuthority(tx, acceptedState, "connections:write", origin),
500
+ commit: async (tx, current) => {
501
+ const connection = await persist(tx);
502
+ if (!connection) throw new PersonalGitHubCallbackError("connection_conflict");
503
+ return {
504
+ ...current,
505
+ revision: current.revision + 1,
506
+ state: "complete",
507
+ credentialsCommitted: true,
508
+ nextAction: { type: "none" },
509
+ account: {
510
+ id: connection.id,
511
+ version: connection.version,
512
+ providerId: "github-personal",
513
+ label: identity.login,
514
+ ownership: "personal",
515
+ status: "connected",
516
+ },
517
+ };
518
+ },
519
+ },
520
+ "github-personal",
521
+ );
522
+ return { redirectTo: exactReturnUrl!, exactReturn: true };
523
+ }
524
+ const connection = await withOrganizationIntegrationAcquisition(
525
+ deps.db,
526
+ acceptedState,
527
+ ["github-personal"],
528
+ persist,
529
+ );
530
+ if (!connection) throw new PersonalGitHubCallbackError("connection_conflict");
531
+ return {
532
+ redirectTo: personalGitHubCallbackReturnUrl(returnBaseUrl, state.returnPath, "success", {
533
+ connectionId: connection.id,
534
+ }),
535
+ };
536
+ } catch (error) {
537
+ if (exactReturnUrl) return { redirectTo: exactReturnUrl, exactReturn: true };
538
+ return {
539
+ redirectTo: personalGitHubCallbackReturnUrl(
540
+ returnBaseUrl,
541
+ state?.returnPath ?? oauthStateFailureReturn(deps.settings, input.state).returnPath,
542
+ "error",
543
+ { reason: personalGitHubFailureReason(error) },
544
+ ),
545
+ };
546
+ }
547
+ }
548
+
549
+ export function personalGitHubReviewUrl(settings: Settings): string | null {
550
+ const clientId = settings.githubPersonalOauthClientId?.trim();
551
+ return clientId
552
+ ? `https://github.com/settings/connections/applications/${encodeURIComponent(clientId)}`
553
+ : null;
554
+ }
555
+
556
+ function readPersonalGitHubOAuthState(
557
+ raw: string | undefined,
558
+ settings: Settings,
559
+ ): PersonalGitHubOAuthState {
560
+ if (!raw) throw new PersonalGitHubCallbackError("invalid_state");
561
+ let secret: string;
562
+ try {
563
+ secret = requireIntegrationsStateSecret(settings);
564
+ } catch {
565
+ throw new PersonalGitHubCallbackError("disabled");
566
+ }
567
+ const payload = readSignedState(raw, secret) as Record<string, unknown> | null;
568
+ if (!payload || payload.kind !== PERSONAL_GITHUB_OAUTH_STATE_KIND) {
569
+ throw new PersonalGitHubCallbackError("invalid_state");
570
+ }
571
+ const iat = numberValue(payload.iat);
572
+ const connectionId = optionalString(payload.connectionId);
573
+ const connectionVersion = numberValue(payload.connectionVersion);
574
+ const issuedAtMs = iat === null ? null : iat * 1000;
575
+ const nowMs = Date.now();
576
+ if (
577
+ iat === null ||
578
+ !Number.isInteger(iat) ||
579
+ issuedAtMs === null ||
580
+ issuedAtMs > nowMs + 60_000 ||
581
+ nowMs >= issuedAtMs + oauthStateTtlMs ||
582
+ (connectionVersion !== null && !Number.isInteger(connectionVersion)) ||
583
+ Boolean(connectionId) !== Boolean(connectionVersion)
584
+ ) {
585
+ throw new PersonalGitHubCallbackError("invalid_state");
586
+ }
587
+ const workspaceId = requiredString(payload.workspaceId);
588
+ return {
589
+ accountId: requiredString(payload.accountId),
590
+ workspaceId,
591
+ subjectId: requiredString(payload.subjectId),
592
+ personalOwnerVerified: personalOwnerVerifiedInState(payload),
593
+ canonicalManagedHumanSession: payload.canonicalManagedHumanSession === true,
594
+ ...(typeof payload.connectAttemptId === "string"
595
+ ? { connectAttemptId: payload.connectAttemptId }
596
+ : {}),
597
+ ...(typeof payload.encryptedExternalContinuation === "string"
598
+ ? {
599
+ externalContinuation: ExternalActorContinuation.parse(
600
+ JSON.parse(
601
+ decryptEnvironmentValue(
602
+ requireEnvironmentEncryption(settings),
603
+ payload.encryptedExternalContinuation,
604
+ ),
605
+ ),
606
+ ),
607
+ }
608
+ : {}),
609
+ returnPath: personalGitHubReturnPath(workspaceId, requiredString(payload.returnPath)),
610
+ encryptedPkceVerifier: requiredString(payload.encryptedPkceVerifier),
611
+ oauthEnvironment: requiredString(payload.oauthEnvironment),
612
+ oauthClientMarker: requiredString(payload.oauthClientMarker),
613
+ ...(connectionId ? { connectionId, connectionVersion: connectionVersion! } : {}),
614
+ nonce: requiredString(payload.nonce),
615
+ iat,
616
+ };
617
+ }
618
+
619
+ async function requirePersonalGitHubCallbackGrant(
620
+ deps: ApiRouteDeps,
621
+ state: PersonalGitHubOAuthState,
622
+ ): Promise<void> {
623
+ if (
624
+ !personalOwnerStateAccepted({
625
+ ownership: "personal",
626
+ subjectId: state.subjectId,
627
+ personalOwnerVerified: state.personalOwnerVerified,
628
+ })
629
+ ) {
630
+ throw new PersonalGitHubCallbackError("not_authorized");
631
+ }
632
+ await requireConnectOwnerAuthority(deps.db, state, "connections:write");
633
+ }
634
+
635
+ async function exchangePersonalGitHubAuthorizationCode(
636
+ fetchImpl: FetchLike,
637
+ oauth: ReturnType<typeof requirePersonalGitHubOAuthSettings>,
638
+ code: string,
639
+ verifier: string,
640
+ ): Promise<GitHubToken> {
641
+ let response: Response;
642
+ try {
643
+ response = await fetchImpl(PERSONAL_GITHUB_TOKEN_URL, {
644
+ method: "POST",
645
+ redirect: "manual",
646
+ headers: {
647
+ accept: "application/json",
648
+ "content-type": "application/x-www-form-urlencoded",
649
+ "user-agent": "OpenGeni",
650
+ },
651
+ body: new URLSearchParams({
652
+ client_id: oauth.clientId,
653
+ client_secret: oauth.clientSecret,
654
+ code,
655
+ redirect_uri: oauth.callbackUrl,
656
+ code_verifier: verifier,
657
+ }),
658
+ signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
659
+ });
660
+ } catch {
661
+ throw new PersonalGitHubCallbackError("token_exchange_failed");
662
+ }
663
+ if (!response.ok || (response.status >= 300 && response.status < 400)) {
664
+ await consumeBoundedJson(response, "GitHub OAuth error response");
665
+ throw new PersonalGitHubCallbackError("token_exchange_failed");
666
+ }
667
+ const payload = await readResponseJsonBounded<Record<string, unknown>>(
668
+ response,
669
+ GITHUB_RESPONSE_MAX_BYTES,
670
+ "GitHub OAuth token response",
671
+ ).catch(() => {
672
+ throw new PersonalGitHubCallbackError("token_exchange_failed");
673
+ });
674
+ const accessToken = optionalString(payload.access_token);
675
+ const tokenType = optionalString(payload.token_type)?.toLowerCase();
676
+ if (!accessToken || tokenType !== "bearer") {
677
+ throw new PersonalGitHubCallbackError("token_exchange_failed");
678
+ }
679
+ const expiresIn = positiveInteger(payload.expires_in);
680
+ const refreshToken = optionalString(payload.refresh_token);
681
+ if (expiresIn !== null && !refreshToken) {
682
+ throw new PersonalGitHubCallbackError("token_exchange_failed");
683
+ }
684
+ const refreshExpiresIn = positiveInteger(payload.refresh_token_expires_in);
685
+ return {
686
+ accessToken,
687
+ refreshToken,
688
+ tokenType: "Bearer",
689
+ expiresAt: expiresIn === null ? null : new Date(Date.now() + expiresIn * 1000),
690
+ refreshTokenExpiresAt:
691
+ refreshExpiresIn === null ? null : new Date(Date.now() + refreshExpiresIn * 1000),
692
+ scopes: parseGitHubScopes(optionalString(payload.scope)),
693
+ };
694
+ }
695
+
696
+ async function fetchPersonalGitHubIdentity(
697
+ fetchImpl: FetchLike,
698
+ accessToken: string,
699
+ ): Promise<GitHubIdentity> {
700
+ let response: Response;
701
+ try {
702
+ response = await fetchImpl(PERSONAL_GITHUB_USER_URL, {
703
+ method: "GET",
704
+ redirect: "manual",
705
+ headers: {
706
+ accept: "application/vnd.github+json",
707
+ authorization: `Bearer ${accessToken}`,
708
+ "user-agent": "OpenGeni",
709
+ "x-github-api-version": GITHUB_API_VERSION,
710
+ },
711
+ signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
712
+ });
713
+ } catch {
714
+ throw new PersonalGitHubCallbackError("identity_failed");
715
+ }
716
+ if (!response.ok || (response.status >= 300 && response.status < 400)) {
717
+ await consumeBoundedJson(response, "GitHub identity error response");
718
+ throw new PersonalGitHubCallbackError("identity_failed");
719
+ }
720
+ const headerScopes = response.headers.get("x-oauth-scopes");
721
+ if (headerScopes) assertExactPersonalGitHubScopes(parseGitHubScopes(headerScopes));
722
+ const payload = await readResponseJsonBounded<Record<string, unknown>>(
723
+ response,
724
+ GITHUB_RESPONSE_MAX_BYTES,
725
+ "GitHub identity response",
726
+ ).catch(() => {
727
+ throw new PersonalGitHubCallbackError("identity_failed");
728
+ });
729
+ const id = positiveInteger(payload.id);
730
+ const login = optionalString(payload.login);
731
+ if (id === null || !Number.isSafeInteger(id) || !login || login.length > 100) {
732
+ throw new PersonalGitHubCallbackError("identity_failed");
733
+ }
734
+ return { id: String(id), login };
735
+ }
736
+
737
+ function assertExactPersonalGitHubScopes(scopes: string[]): void {
738
+ const exact = [...new Set(scopes)].sort();
739
+ if (exact.length !== 1 || exact[0] !== "repo") {
740
+ throw new PersonalGitHubCallbackError("scope_not_granted");
741
+ }
742
+ }
743
+
744
+ function parseGitHubScopes(raw: string | null): string[] {
745
+ if (!raw) return [];
746
+ return [
747
+ ...new Set(
748
+ raw
749
+ .split(/[\s,]+/u)
750
+ .map((scope) => scope.trim())
751
+ .filter(Boolean),
752
+ ),
753
+ ].sort();
754
+ }
755
+
756
+ function personalGitHubReturnPath(workspaceId: string, raw?: string): string {
757
+ const fallback = workspaceIntegrationsPath(workspaceId);
758
+ if (!raw) return fallback;
759
+ let parsed: URL;
760
+ try {
761
+ parsed = new URL(raw, "https://opengeni.invalid");
762
+ } catch {
763
+ throw new HTTPException(400, { message: "invalid personal GitHub return path" });
764
+ }
765
+ const workspacePrefix = `/workspaces/${workspaceId}`;
766
+ if (
767
+ parsed.origin !== "https://opengeni.invalid" ||
768
+ (parsed.pathname !== workspacePrefix && !parsed.pathname.startsWith(`${workspacePrefix}/`))
769
+ ) {
770
+ throw new HTTPException(400, { message: "invalid personal GitHub return path" });
771
+ }
772
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
773
+ }
774
+
775
+ function personalGitHubCallbackReturnUrl(
776
+ baseUrl: string,
777
+ returnPath: string,
778
+ status: "success" | "error",
779
+ params: Record<string, string>,
780
+ ): string {
781
+ let safePath: string;
782
+ try {
783
+ const match = returnPath.match(/^\/workspaces\/([^/]+)/u);
784
+ safePath = match ? personalGitHubReturnPath(match[1]!, returnPath) : "/integrations";
785
+ } catch {
786
+ safePath = "/integrations";
787
+ }
788
+ const url = new URL(safePath, `${baseUrl.replace(/\/+$/u, "")}/`);
789
+ url.searchParams.set("github_personal_oauth", status);
790
+ for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value.slice(0, 256));
791
+ return url.toString();
792
+ }
793
+
794
+ function personalGitHubFailureReason(error: unknown): PersonalGitHubFailureReason {
795
+ if (error instanceof PersonalGitHubCallbackError) return error.reason;
796
+ if (error instanceof HTTPException && error.status === 404) return "disabled";
797
+ return "connection_conflict";
798
+ }
799
+
800
+ async function consumeBoundedJson(response: Response, label: string): Promise<void> {
801
+ await readResponseJsonBounded(response, GITHUB_RESPONSE_MAX_BYTES, label).catch(async () => {
802
+ await response.body?.cancel().catch(() => undefined);
803
+ });
804
+ }
805
+
806
+ function requiredString(value: unknown): string {
807
+ const parsed = optionalString(value);
808
+ if (!parsed) throw new PersonalGitHubCallbackError("invalid_state");
809
+ return parsed;
810
+ }
811
+
812
+ function optionalString(value: unknown): string | null {
813
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
814
+ }
815
+
816
+ function numberValue(value: unknown): number | null {
817
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
818
+ }
819
+
820
+ function positiveInteger(value: unknown): number | null {
821
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
822
+ }
823
+
824
+ export const PERSONAL_GITHUB_CONNECTION_PRINCIPAL_MESSAGE =
825
+ personalOnlyConnectionPrincipalMessage("My GitHub account");