@opengeni/api-router 2.11.3 → 2.12.3-canary.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 (66) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-5X3XM6CE.js → chunk-4AQMKVQM.js} +17727 -14427
  3. package/dist/chunk-4AQMKVQM.js.map +1 -0
  4. package/dist/connection-ownership.d.ts +5 -1
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/atlassian.d.ts +4 -0
  8. package/dist/integrations/connect-authority.d.ts +1 -0
  9. package/dist/integrations/connect-callback-return.d.ts +6 -0
  10. package/dist/integrations/fiken.d.ts +7 -0
  11. package/dist/integrations/github-app-connect.d.ts +47 -0
  12. package/dist/integrations/github-installation-proof.d.ts +3 -0
  13. package/dist/integrations/github-lens-connect.d.ts +153 -0
  14. package/dist/integrations/google-drive.d.ts +4 -0
  15. package/dist/integrations/oauth-client.d.ts +4 -0
  16. package/dist/integrations/oauth-return-path.d.ts +1 -0
  17. package/dist/integrations/personal-github.d.ts +2 -0
  18. package/dist/integrations/provider-oauth.d.ts +11 -1
  19. package/dist/integrations/slack-install.d.ts +20 -0
  20. package/dist/integrations/social-oauth.d.ts +2 -0
  21. package/dist/mcp/scheduled-task-view.d.ts +3 -3
  22. package/dist/routes/api-integrations.d.ts +14 -0
  23. package/dist/routes/connect.d.ts +5 -0
  24. package/dist/routes/external-identity-links.d.ts +6 -0
  25. package/dist/routes/host-mcp-bindings.d.ts +5 -0
  26. package/dist/sandbox/rematerialize.d.ts +1 -1
  27. package/dist/workspace-tool-gateway.d.ts +1 -0
  28. package/package.json +19 -19
  29. package/src/app.ts +22 -2
  30. package/src/connection-ownership.ts +14 -1
  31. package/src/index.ts +2 -0
  32. package/src/integrations/atlassian.ts +143 -39
  33. package/src/integrations/connect-authority.ts +2 -0
  34. package/src/integrations/connect-callback-return.ts +86 -0
  35. package/src/integrations/fiken.ts +222 -40
  36. package/src/integrations/github-app-connect.ts +389 -0
  37. package/src/integrations/github-installation-proof.ts +60 -0
  38. package/src/integrations/github-lens-connect.ts +97 -0
  39. package/src/integrations/google-drive.ts +152 -47
  40. package/src/integrations/oauth-client.ts +180 -84
  41. package/src/integrations/oauth-return-path.ts +18 -0
  42. package/src/integrations/personal-github.ts +146 -52
  43. package/src/integrations/provider-oauth.ts +132 -21
  44. package/src/integrations/slack-install.ts +83 -0
  45. package/src/integrations/social-oauth.ts +142 -1
  46. package/src/mcp/documents.ts +1 -1
  47. package/src/mcp/server.ts +64 -17
  48. package/src/routes/api-integrations.ts +74 -64
  49. package/src/routes/connect.ts +1869 -0
  50. package/src/routes/connections.ts +263 -221
  51. package/src/routes/documents.ts +2 -2
  52. package/src/routes/external-identity-links.ts +200 -0
  53. package/src/routes/github.ts +39 -64
  54. package/src/routes/host-mcp-bindings.ts +122 -0
  55. package/src/routes/organization-memberships.ts +23 -0
  56. package/src/routes/organization-sessions.ts +2 -2
  57. package/src/routes/personal-github.ts +8 -1
  58. package/src/routes/pr-review-github.ts +30 -3
  59. package/src/routes/scheduled-tasks.ts +29 -4
  60. package/src/routes/sessions.ts +61 -32
  61. package/src/routes/social.ts +5 -1
  62. package/src/routes/supergrok.ts +90 -3
  63. package/src/routes/workspaces.ts +16 -0
  64. package/src/sandbox/rematerialize.ts +33 -1
  65. package/src/workspace-tool-gateway.ts +136 -24
  66. package/dist/chunk-5X3XM6CE.js.map +0 -1
@@ -1,4 +1,6 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
3
+ import { requireConnectOwnerAuthority } from "./connect-authority";
2
4
  import {
3
5
  configuredGoogleDriveSyncLimits,
4
6
  googleDriveOAuthCallbackUrl,
@@ -46,7 +48,6 @@ import {
46
48
  import {
47
49
  captureScheduledTaskRestoreState,
48
50
  createValidatedScheduledTask,
49
- hasPermission,
50
51
  manualScheduledTaskTriggerWorkflowId,
51
52
  requireEnvironmentEncryption,
52
53
  syncCreatedScheduledTask,
@@ -62,6 +63,10 @@ import {
62
63
  ConnectionDisconnectGenerationError,
63
64
  ConnectionDisconnectIdempotencyError,
64
65
  consumeIntegrationOAuthStateNonce,
66
+ claimConnectOperation,
67
+ finishConnectOperation,
68
+ getConnectAttempt,
69
+ type Database,
65
70
  createConnection,
66
71
  decryptEnvironmentValue,
67
72
  disconnectConnectionIdempotently,
@@ -70,7 +75,6 @@ import {
70
75
  getConnectionMetadata,
71
76
  getKnowledgeSourceByExternalIdentityForSyncAuthority,
72
77
  getKnowledgeSourceForSyncAuthority,
73
- getWorkspaceGrant,
74
78
  listKnowledgeSourceSyncTasksForConnection,
75
79
  loadConnectionCredentialForBroker,
76
80
  listIntegrationInstanceFacets,
@@ -127,6 +131,8 @@ const GOOGLE_DRIVE_RECONSENT_ERROR_CODES = new Set([
127
131
  ]);
128
132
 
129
133
  type GoogleDriveOAuthState = {
134
+ connectAttemptId?: string;
135
+ externalContinuation?: ExternalActorContinuation;
130
136
  accountId: string;
131
137
  workspaceId: string;
132
138
  subjectId: string;
@@ -239,6 +245,8 @@ export async function startGoogleDriveOAuth(
239
245
  subjectId: string;
240
246
  requestUrl: string;
241
247
  payload: GoogleDriveOAuthStartRequest;
248
+ connectAttemptId?: string;
249
+ externalContinuation?: ExternalActorContinuation;
242
250
  },
243
251
  ): Promise<GoogleDriveOAuthStartResponse> {
244
252
  const google = requireGoogleDriveSettings(deps.settings);
@@ -270,6 +278,15 @@ export async function startGoogleDriveOAuth(
270
278
  workspaceId: input.workspaceId,
271
279
  subjectId: input.subjectId,
272
280
  kind: GOOGLE_DRIVE_OAUTH_STATE_KIND,
281
+ ...(input.connectAttemptId ? { connectAttemptId: input.connectAttemptId } : {}),
282
+ ...(input.externalContinuation
283
+ ? {
284
+ encryptedExternalContinuation: encryptEnvironmentValue(
285
+ key,
286
+ JSON.stringify(ExternalActorContinuation.parse(input.externalContinuation)),
287
+ ),
288
+ }
289
+ : {}),
273
290
  // The route admits only a managed human, so reaching here is the proof; the
274
291
  // callback has no live principal and enforces exactly this claim.
275
292
  [PERSONAL_OWNER_VERIFIED_STATE_CLAIM]: true,
@@ -319,13 +336,37 @@ export async function completeGoogleDriveOAuthCallback(
319
336
  pickedFileIds?: string | undefined;
320
337
  requestUrl: string;
321
338
  },
322
- ): Promise<{ redirectTo: string }> {
339
+ ): Promise<{ redirectTo: string; exactReturn?: boolean }> {
323
340
  const redirectUri = requireGoogleDriveOAuthCallbackUrl(deps.settings);
324
341
  const baseUrl = integrationBaseUrl(deps.settings.publicBaseUrl, input.requestUrl);
325
342
  const returnBaseUrl = deps.settings.webBaseUrl?.replace(/\/+$/, "") ?? baseUrl;
326
343
  let state: GoogleDriveOAuthState | null = null;
344
+ let exactReturnUrl: string | undefined;
345
+ let operation: { attemptId: string; operationId: string; inputDigest: string } | undefined;
327
346
  try {
328
347
  state = readGoogleDriveOAuthState(input.state, deps.settings);
348
+ if (state.connectAttemptId) {
349
+ const stored = await getConnectAttempt(deps.db, state, state.connectAttemptId);
350
+ if (
351
+ stored.attempt.providerId !==
352
+ (state.capability === "publish" ? "google-drive-publish" : "google-drive-knowledge") ||
353
+ stored.attempt.ownership !== "personal"
354
+ )
355
+ throw new GoogleDriveCallbackError("connection_conflict");
356
+ exactReturnUrl = stored.returnUrl;
357
+ operation = {
358
+ attemptId: state.connectAttemptId,
359
+ operationId: `oauth:${state.nonce}`,
360
+ inputDigest: createHash("sha256").update(input.state!).digest("hex"),
361
+ };
362
+ const claim = await claimConnectOperation(deps.db, state, {
363
+ ...operation,
364
+ expectedRevision: stored.attempt.revision,
365
+ authorize: (tx, _attempt, origin) =>
366
+ requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
367
+ });
368
+ if (claim.status === "replayed") return { redirectTo: exactReturnUrl, exactReturn: true };
369
+ }
329
370
  await requireGoogleDriveCallbackGrant(deps, state);
330
371
  const consumed = await consumeIntegrationOAuthStateNonce(deps.db, {
331
372
  accountId: state.accountId,
@@ -338,6 +379,25 @@ export async function completeGoogleDriveOAuthCallback(
338
379
  if (!consumed) {
339
380
  throw new HTTPException(400, { message: "Google Drive OAuth state has already been used" });
340
381
  }
382
+ if (operation && (input.error || !input.code)) {
383
+ await finishConnectOperation(deps.db, state, {
384
+ ...operation,
385
+ authorize: (tx, _attempt, origin) =>
386
+ requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
387
+ commit: async (_tx, current) => ({
388
+ ...current,
389
+ revision: current.revision + 1,
390
+ state: input.error === "access_denied" ? "cancelled" : "failed",
391
+ nextAction: { type: "none" },
392
+ error: {
393
+ code: input.error ? "provider_denied" : "missing_code",
394
+ message: "Authorization was not completed. Start a new connection attempt.",
395
+ retryable: false,
396
+ },
397
+ }),
398
+ });
399
+ return { redirectTo: exactReturnUrl!, exactReturn: true };
400
+ }
341
401
  if (input.error) {
342
402
  throw new GoogleDriveCallbackError("provider_denied");
343
403
  }
@@ -402,16 +462,6 @@ export async function completeGoogleDriveOAuthCallback(
402
462
  : previousMetadata?.outputDestination;
403
463
  if (state.capability === "publish") {
404
464
  if (!existing) throw new GoogleDriveCallbackError("connection_conflict");
405
- await ensureConnectorActionPolicyDefault(deps.db, {
406
- accountId: state.accountId,
407
- workspaceId: state.workspaceId,
408
- subjectId: state.subjectId,
409
- connectionId: existing.id,
410
- serverId: GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
411
- toolName: GOOGLE_DRIVE_PUBLICATION_TOOL_NAME,
412
- actionName: GOOGLE_DRIVE_PUBLICATION_CREATE_ACTION,
413
- policy: "ask",
414
- });
415
465
  }
416
466
  let refreshToken = token.refreshToken;
417
467
  if (!refreshToken && existing) {
@@ -461,34 +511,82 @@ export async function completeGoogleDriveOAuthCallback(
461
511
  ? { selectedSources: [previousMetadata.selectedSource] }
462
512
  : {}),
463
513
  });
464
- const connection = existing
465
- ? await updateConnection(deps.db, {
466
- workspaceId: state.workspaceId,
467
- connectionId: existing.id,
468
- visibleToSubjectId: state.subjectId,
469
- expectedVersion: existing.version,
470
- subjectId: state.subjectId,
471
- providerDomain: GOOGLE_DRIVE_PROVIDER_DOMAIN,
472
- kind: "oauth2",
473
- status: "active",
474
- credentialEncrypted,
475
- grantedScopes,
476
- expiresAt: token.expiresAt,
477
- metadata,
478
- updatedBySubjectId: state.subjectId,
479
- })
480
- : await createConnection(deps.db, {
514
+ const acceptedState = state;
515
+ const persist = async (tx: Database) => {
516
+ // Capture the validated immutable callback state for delayed persistence.
517
+ // eslint-disable-next-line no-shadow
518
+ const state = acceptedState;
519
+ if (state.capability === "publish" && existing)
520
+ await ensureConnectorActionPolicyDefault(tx, {
481
521
  accountId: state.accountId,
482
522
  workspaceId: state.workspaceId,
483
523
  subjectId: state.subjectId,
484
- providerDomain: GOOGLE_DRIVE_PROVIDER_DOMAIN,
485
- kind: "oauth2",
486
- credentialEncrypted,
487
- grantedScopes,
488
- expiresAt: token.expiresAt,
489
- metadata,
490
- createdBySubjectId: state.subjectId,
524
+ connectionId: existing.id,
525
+ serverId: GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
526
+ toolName: GOOGLE_DRIVE_PUBLICATION_TOOL_NAME,
527
+ actionName: GOOGLE_DRIVE_PUBLICATION_CREATE_ACTION,
528
+ policy: "ask",
491
529
  });
530
+ return existing
531
+ ? updateConnection(tx, {
532
+ workspaceId: state.workspaceId,
533
+ connectionId: existing.id,
534
+ visibleToSubjectId: state.subjectId,
535
+ expectedVersion: existing.version,
536
+ subjectId: state.subjectId,
537
+ providerDomain: GOOGLE_DRIVE_PROVIDER_DOMAIN,
538
+ kind: "oauth2",
539
+ status: "active",
540
+ credentialEncrypted,
541
+ grantedScopes,
542
+ expiresAt: token.expiresAt,
543
+ metadata,
544
+ updatedBySubjectId: state.subjectId,
545
+ })
546
+ : createConnection(tx, {
547
+ accountId: state.accountId,
548
+ workspaceId: state.workspaceId,
549
+ subjectId: state.subjectId,
550
+ providerDomain: GOOGLE_DRIVE_PROVIDER_DOMAIN,
551
+ kind: "oauth2",
552
+ credentialEncrypted,
553
+ grantedScopes,
554
+ expiresAt: token.expiresAt,
555
+ metadata,
556
+ createdBySubjectId: state.subjectId,
557
+ });
558
+ };
559
+ if (operation) {
560
+ await finishConnectOperation(deps.db, acceptedState, {
561
+ ...operation,
562
+ authorize: (tx, _attempt, origin) =>
563
+ requireConnectOwnerAuthority(tx, acceptedState, "connections:write", origin),
564
+ commit: async (tx, current) => {
565
+ const connection = await persist(tx);
566
+ if (!connection) throw new GoogleDriveCallbackError("connection_conflict");
567
+ return {
568
+ ...current,
569
+ revision: current.revision + 1,
570
+ state: "complete",
571
+ credentialsCommitted: true,
572
+ nextAction: { type: "none" },
573
+ account: {
574
+ id: connection.id,
575
+ version: connection.version,
576
+ providerId: current.providerId,
577
+ label: identity.emailAddress ?? identity.displayName,
578
+ ownership: "personal",
579
+ status: "connected",
580
+ },
581
+ };
582
+ },
583
+ });
584
+ return { redirectTo: exactReturnUrl!, exactReturn: true };
585
+ }
586
+ const connection = await deps.db.transaction(async (tx) => {
587
+ await requireGoogleDriveCallbackGrant({ ...deps, db: tx }, acceptedState);
588
+ return persist(tx);
589
+ });
492
590
  if (!connection) {
493
591
  throw new GoogleDriveCallbackError("connection_conflict");
494
592
  }
@@ -496,6 +594,7 @@ export async function completeGoogleDriveOAuthCallback(
496
594
  redirectTo: googleDriveReturnUrl(returnBaseUrl, state.returnPath, "connected", connection.id),
497
595
  };
498
596
  } catch (error) {
597
+ if (exactReturnUrl) return { redirectTo: exactReturnUrl, exactReturn: true };
499
598
  return {
500
599
  redirectTo: googleDriveReturnUrl(
501
600
  returnBaseUrl,
@@ -2017,6 +2116,21 @@ function readGoogleDriveOAuthState(
2017
2116
  workspaceId,
2018
2117
  subjectId,
2019
2118
  personalOwnerVerified: personalOwnerVerifiedInState(payload),
2119
+ ...(typeof payload.connectAttemptId === "string"
2120
+ ? { connectAttemptId: payload.connectAttemptId }
2121
+ : {}),
2122
+ ...(typeof payload.encryptedExternalContinuation === "string"
2123
+ ? {
2124
+ externalContinuation: ExternalActorContinuation.parse(
2125
+ JSON.parse(
2126
+ decryptEnvironmentValue(
2127
+ requireEnvironmentEncryption(settings),
2128
+ payload.encryptedExternalContinuation,
2129
+ ),
2130
+ ),
2131
+ ),
2132
+ }
2133
+ : {}),
2020
2134
  returnPath,
2021
2135
  encryptedPkceVerifier: requiredString(
2022
2136
  payload.encryptedPkceVerifier,
@@ -2048,16 +2162,7 @@ async function requireGoogleDriveCallbackGrant(
2048
2162
  message: personalOnlyConnectionPrincipalMessage("Google Drive"),
2049
2163
  });
2050
2164
  }
2051
- const grant = await getWorkspaceGrant(deps.db, state.subjectId, state.workspaceId);
2052
- if (
2053
- !grant ||
2054
- grant.accountId !== state.accountId ||
2055
- !hasPermission(grant.permissions, "connections:write")
2056
- ) {
2057
- throw new HTTPException(403, {
2058
- message: "Google Drive OAuth subject no longer has permission for this workspace",
2059
- });
2060
- }
2165
+ await requireConnectOwnerAuthority(deps.db, state, "connections:write");
2061
2166
  }
2062
2167
 
2063
2168
  async function exchangeGoogleAuthorizationCode(