@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.
- package/dist/app.js +1 -1
- package/dist/{chunk-5X3XM6CE.js → chunk-4AQMKVQM.js} +17727 -14427
- package/dist/chunk-4AQMKVQM.js.map +1 -0
- package/dist/connection-ownership.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/atlassian.d.ts +4 -0
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +7 -0
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +153 -0
- package/dist/integrations/google-drive.d.ts +4 -0
- package/dist/integrations/oauth-client.d.ts +4 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github.d.ts +2 -0
- package/dist/integrations/provider-oauth.d.ts +11 -1
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/social-oauth.d.ts +2 -0
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/routes/api-integrations.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/host-mcp-bindings.d.ts +5 -0
- package/dist/sandbox/rematerialize.d.ts +1 -1
- package/dist/workspace-tool-gateway.d.ts +1 -0
- package/package.json +19 -19
- package/src/app.ts +22 -2
- package/src/connection-ownership.ts +14 -1
- package/src/index.ts +2 -0
- package/src/integrations/atlassian.ts +143 -39
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +222 -40
- package/src/integrations/github-app-connect.ts +389 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +97 -0
- package/src/integrations/google-drive.ts +152 -47
- package/src/integrations/oauth-client.ts +180 -84
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github.ts +146 -52
- package/src/integrations/provider-oauth.ts +132 -21
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/social-oauth.ts +142 -1
- package/src/mcp/documents.ts +1 -1
- package/src/mcp/server.ts +64 -17
- package/src/routes/api-integrations.ts +74 -64
- package/src/routes/connect.ts +1869 -0
- package/src/routes/connections.ts +263 -221
- package/src/routes/documents.ts +2 -2
- package/src/routes/external-identity-links.ts +200 -0
- package/src/routes/github.ts +39 -64
- package/src/routes/host-mcp-bindings.ts +122 -0
- package/src/routes/organization-memberships.ts +23 -0
- package/src/routes/organization-sessions.ts +2 -2
- package/src/routes/personal-github.ts +8 -1
- package/src/routes/pr-review-github.ts +30 -3
- package/src/routes/scheduled-tasks.ts +29 -4
- package/src/routes/sessions.ts +61 -32
- package/src/routes/social.ts +5 -1
- package/src/routes/supergrok.ts +90 -3
- package/src/routes/workspaces.ts +16 -0
- package/src/sandbox/rematerialize.ts +33 -1
- package/src/workspace-tool-gateway.ts +136 -24
- package/dist/chunk-5X3XM6CE.js.map +0 -1
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
ToolGatewayApprovalResponse,
|
|
22
22
|
ToolGatewayDeclarationsResponse,
|
|
23
23
|
type AccessGrant,
|
|
24
|
+
type McpGatewayCredentialAuthority,
|
|
24
25
|
type ToolGatewayCatalog,
|
|
25
26
|
type ToolGatewayIdentity,
|
|
26
27
|
type ToolRef,
|
|
@@ -28,6 +29,9 @@ import {
|
|
|
28
29
|
import {
|
|
29
30
|
buildApiIntegrationMcpServers,
|
|
30
31
|
hasPermission,
|
|
32
|
+
externalActorContinuationForAuthorization,
|
|
33
|
+
isVerifiedOrganizationServiceAuthorization,
|
|
34
|
+
externalContinuationCommitAuthorizer,
|
|
31
35
|
requireResolvedAccessGrantAuthorization,
|
|
32
36
|
resolveCodexAppsCredentialIdForRun,
|
|
33
37
|
resolveWorkspaceCatalogSettings,
|
|
@@ -38,6 +42,10 @@ import {
|
|
|
38
42
|
import {
|
|
39
43
|
buildCodexTokenResolver,
|
|
40
44
|
buildConnectionTokenResolver,
|
|
45
|
+
buildHostGatewayConnectionTokenResolver,
|
|
46
|
+
lockActiveExternalOrganizationKey,
|
|
47
|
+
withAccountRls,
|
|
48
|
+
requireWorkspace,
|
|
41
49
|
withCodexAppsRequestAuthorization,
|
|
42
50
|
consumeToolGatewayApproval,
|
|
43
51
|
getWorkspaceArtifact,
|
|
@@ -80,6 +88,7 @@ export type PreparedWorkspaceToolGateway = Pick<
|
|
|
80
88
|
> & {
|
|
81
89
|
toolGateway: NonNullable<PreparedWorkspaceToolGatewayTools["toolGateway"]>;
|
|
82
90
|
toolGatewayCatalog: NonNullable<PreparedWorkspaceToolGatewayTools["toolGatewayCatalog"]>;
|
|
91
|
+
reauthorize?: () => Promise<void>;
|
|
83
92
|
};
|
|
84
93
|
|
|
85
94
|
type WorkspaceSiteToolContext = {
|
|
@@ -118,8 +127,19 @@ export function requireWorkspaceToolGatewayAuthorization(
|
|
|
118
127
|
authorization,
|
|
119
128
|
authorization.grant.workspaceId,
|
|
120
129
|
);
|
|
130
|
+
if (isVerifiedOrganizationServiceAuthorization(authorization)) {
|
|
131
|
+
if (grantUsesAttemptScopedMcp(grant))
|
|
132
|
+
throw new HTTPException(403, {
|
|
133
|
+
message: "service tool access cannot carry attempt authority",
|
|
134
|
+
});
|
|
135
|
+
return grant;
|
|
136
|
+
}
|
|
121
137
|
requireWorkspaceToolGatewayGrant(grant);
|
|
122
|
-
if (
|
|
138
|
+
if (
|
|
139
|
+
!authorization.canonicalManagedHumanSession &&
|
|
140
|
+
!authorization.canonicalLocalHumanSession &&
|
|
141
|
+
!externalActorContinuationForAuthorization(authorization)
|
|
142
|
+
) {
|
|
123
143
|
throw new HTTPException(403, { message: "current-human tool access required" });
|
|
124
144
|
}
|
|
125
145
|
return grant;
|
|
@@ -130,7 +150,61 @@ export async function prepareWorkspaceToolGateway(
|
|
|
130
150
|
authorization: AccessGrantAuthorization,
|
|
131
151
|
): Promise<PreparedWorkspaceToolGateway> {
|
|
132
152
|
const grant = requireWorkspaceToolGatewayAuthorization(authorization);
|
|
133
|
-
|
|
153
|
+
const external = externalActorContinuationForAuthorization(authorization);
|
|
154
|
+
const reauthorizeExternal = externalContinuationCommitAuthorizer(authorization);
|
|
155
|
+
const service = isVerifiedOrganizationServiceAuthorization(authorization);
|
|
156
|
+
const scope = {
|
|
157
|
+
accountId: grant.accountId,
|
|
158
|
+
workspaceId: grant.workspaceId,
|
|
159
|
+
subjectId: grant.subjectId,
|
|
160
|
+
};
|
|
161
|
+
const permissions = [...grant.permissions];
|
|
162
|
+
const reauthorize =
|
|
163
|
+
external || service
|
|
164
|
+
? async () => {
|
|
165
|
+
await withAccountRls(routeDeps.db, scope.accountId, async (tx) => {
|
|
166
|
+
if (reauthorizeExternal) await reauthorizeExternal(tx);
|
|
167
|
+
else {
|
|
168
|
+
const live = await lockActiveExternalOrganizationKey(
|
|
169
|
+
tx,
|
|
170
|
+
scope.accountId,
|
|
171
|
+
scope.subjectId.slice("api_key:".length),
|
|
172
|
+
);
|
|
173
|
+
const workspace = await requireWorkspace(tx, scope.workspaceId);
|
|
174
|
+
if (
|
|
175
|
+
!live ||
|
|
176
|
+
permissions.some((permission) => !hasPermission(live, permission)) ||
|
|
177
|
+
workspace.accountId !== scope.accountId ||
|
|
178
|
+
workspace.kind !== "shared"
|
|
179
|
+
)
|
|
180
|
+
throw new HTTPException(403, { message: "service gateway authority changed" });
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
: undefined;
|
|
185
|
+
await reauthorize?.();
|
|
186
|
+
const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
|
|
187
|
+
routeDeps,
|
|
188
|
+
grant,
|
|
189
|
+
undefined,
|
|
190
|
+
reauthorize
|
|
191
|
+
? {
|
|
192
|
+
authority: {
|
|
193
|
+
kind: external ? "external_user" : "organization_service",
|
|
194
|
+
subjectId: scope.subjectId,
|
|
195
|
+
permissions,
|
|
196
|
+
},
|
|
197
|
+
reauthorize,
|
|
198
|
+
}
|
|
199
|
+
: undefined,
|
|
200
|
+
);
|
|
201
|
+
try {
|
|
202
|
+
await reauthorize?.();
|
|
203
|
+
} catch (error) {
|
|
204
|
+
await prepared.close();
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
return { ...prepared, ...(reauthorize ? { reauthorize } : {}) };
|
|
134
208
|
}
|
|
135
209
|
|
|
136
210
|
export async function prepareMcpOAuthWorkspaceToolGateway(
|
|
@@ -148,6 +222,15 @@ export async function prepareWorkspaceToolGatewayForGrant(
|
|
|
148
222
|
routeDeps: ApiRouteDeps,
|
|
149
223
|
grant: AccessGrant,
|
|
150
224
|
allowedIdentities?: readonly { serverId: string; toolName: string }[],
|
|
225
|
+
): Promise<PreparedWorkspaceToolGateway> {
|
|
226
|
+
return await prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, allowedIdentities);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
230
|
+
routeDeps: ApiRouteDeps,
|
|
231
|
+
grant: AccessGrant,
|
|
232
|
+
allowedIdentities?: readonly { serverId: string; toolName: string }[],
|
|
233
|
+
hostContext?: { authority: McpGatewayCredentialAuthority; reauthorize: () => Promise<void> },
|
|
151
234
|
): Promise<PreparedWorkspaceToolGateway> {
|
|
152
235
|
const catalogSourceSettings = routeDeps.catalogSourceSettings ?? routeDeps.settings;
|
|
153
236
|
const resolvedCatalog = await resolveWorkspaceCatalogSettings(
|
|
@@ -173,7 +256,24 @@ export async function prepareWorkspaceToolGatewayForGrant(
|
|
|
173
256
|
const gatewaySettings = workspaceToolGatewaySettingsForGrant(settings, grant, allowedIdentities);
|
|
174
257
|
const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
|
|
175
258
|
const deps = { ...routeDeps, catalogSourceSettings, settings: gatewaySettings };
|
|
176
|
-
const
|
|
259
|
+
const nativeResolveConnection = buildConnectionTokenResolver(routeDeps.db, gatewaySettings);
|
|
260
|
+
const hostPort = routeDeps.connectionCredentials?.mcpGatewayCredentials;
|
|
261
|
+
const hostResolveConnection =
|
|
262
|
+
hostContext && hostPort
|
|
263
|
+
? buildHostGatewayConnectionTokenResolver(
|
|
264
|
+
hostPort,
|
|
265
|
+
{
|
|
266
|
+
accountId: grant.accountId,
|
|
267
|
+
workspaceId: grant.workspaceId,
|
|
268
|
+
authority: hostContext.authority,
|
|
269
|
+
},
|
|
270
|
+
hostContext.reauthorize,
|
|
271
|
+
)
|
|
272
|
+
: undefined;
|
|
273
|
+
const resolveConnection: typeof nativeResolveConnection = async (input) =>
|
|
274
|
+
input.connectionRef.authoritySource === "host" && hostResolveConnection
|
|
275
|
+
? await hostResolveConnection(input)
|
|
276
|
+
: await nativeResolveConnection(input);
|
|
177
277
|
const resolveCredential = async (
|
|
178
278
|
input: ResolveConnectionCredentialInput,
|
|
179
279
|
): Promise<ResolveConnectionCredentialResult> =>
|
|
@@ -208,7 +308,8 @@ export async function prepareWorkspaceToolGatewayForGrant(
|
|
|
208
308
|
routeDeps.db,
|
|
209
309
|
grant.workspaceId,
|
|
210
310
|
grant.metadata["sessionId"],
|
|
211
|
-
|
|
311
|
+
grant.metadata,
|
|
312
|
+
)) ?? { mode: "off" as const, userSubjectId: null, rootSessionId: null })
|
|
212
313
|
: null,
|
|
213
314
|
},
|
|
214
315
|
),
|
|
@@ -355,24 +456,27 @@ export function buildWorkspaceToolGatewayMcpServer(
|
|
|
355
456
|
{ name: "opengeni-tool-gateway", version: "1.0.0" },
|
|
356
457
|
{ capabilities: { tools: {} } },
|
|
357
458
|
);
|
|
358
|
-
server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
459
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
460
|
+
await prepared.reauthorize?.();
|
|
461
|
+
return {
|
|
462
|
+
tools: callableEntries.map((entry) => ({
|
|
463
|
+
name: entry.modelName,
|
|
464
|
+
...(entry.title ? { title: entry.title } : {}),
|
|
465
|
+
...(entry.description ? { description: entry.description } : {}),
|
|
466
|
+
inputSchema: entry.inputSchema,
|
|
467
|
+
...(entry.outputSchema ? { outputSchema: entry.outputSchema } : {}),
|
|
468
|
+
...(entry.annotations ? { annotations: entry.annotations } : {}),
|
|
469
|
+
...(entry.icons ? { icons: entry.icons } : {}),
|
|
470
|
+
_meta: {
|
|
471
|
+
"opengeni/identity": entry.identity,
|
|
472
|
+
"opengeni/path": entry.codemodePath,
|
|
473
|
+
"opengeni/source": entry.source,
|
|
474
|
+
"opengeni/approval": entry.approval,
|
|
475
|
+
"opengeni/catalogDigest": prepared.toolGatewayCatalog.digest,
|
|
476
|
+
},
|
|
477
|
+
})),
|
|
478
|
+
};
|
|
479
|
+
});
|
|
376
480
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
377
481
|
const entry = callableEntries.find((candidate) => candidate.modelName === request.params.name);
|
|
378
482
|
if (!entry) throw new ToolGatewayToolNotFoundError();
|
|
@@ -382,7 +486,8 @@ export function buildWorkspaceToolGatewayMcpServer(
|
|
|
382
486
|
source: entry.source,
|
|
383
487
|
});
|
|
384
488
|
try {
|
|
385
|
-
|
|
489
|
+
await prepared.reauthorize?.();
|
|
490
|
+
const call = await prepared.toolGateway.prepareCall(
|
|
386
491
|
{
|
|
387
492
|
operationId: crypto.randomUUID(),
|
|
388
493
|
catalogDigest: prepared.toolGatewayCatalog.digest,
|
|
@@ -391,7 +496,9 @@ export function buildWorkspaceToolGatewayMcpServer(
|
|
|
391
496
|
caller: { kind: "mcp", subjectId: grant.subjectId },
|
|
392
497
|
},
|
|
393
498
|
{ signal: extra.signal },
|
|
394
|
-
)
|
|
499
|
+
);
|
|
500
|
+
await prepared.reauthorize?.();
|
|
501
|
+
const result = (await call.execute()) as CallToolResult;
|
|
395
502
|
observation.end(result.isError ? "tool_error" : "ok");
|
|
396
503
|
return result;
|
|
397
504
|
} catch (error) {
|
|
@@ -433,6 +540,7 @@ export async function callWorkspaceToolGateway(
|
|
|
433
540
|
identity: request.identity,
|
|
434
541
|
}
|
|
435
542
|
: null;
|
|
543
|
+
await prepared.reauthorize?.();
|
|
436
544
|
if (siteContext) {
|
|
437
545
|
if (!db) throw new HTTPException(503, { message: "site_tool_authorization_unavailable" });
|
|
438
546
|
await authorizeSiteTool(db, grant, siteContext);
|
|
@@ -450,6 +558,7 @@ export async function callWorkspaceToolGateway(
|
|
|
450
558
|
},
|
|
451
559
|
{ transportMeta },
|
|
452
560
|
);
|
|
561
|
+
await prepared.reauthorize?.();
|
|
453
562
|
let approvalConfirmed = false;
|
|
454
563
|
const approvalRequired = preparedCall.entry.approval === "human";
|
|
455
564
|
if (approvalRequired && request.approvalToken && db) {
|
|
@@ -478,6 +587,7 @@ export async function callWorkspaceToolGateway(
|
|
|
478
587
|
siteContext.siteVersionId,
|
|
479
588
|
)
|
|
480
589
|
: null;
|
|
590
|
+
await prepared.reauthorize?.();
|
|
481
591
|
const result = await (origin
|
|
482
592
|
? withSiteSessionOrigin(origin, () => preparedCall.execute())
|
|
483
593
|
: preparedCall.execute());
|
|
@@ -504,6 +614,7 @@ export async function approveWorkspaceToolGatewayCall(
|
|
|
504
614
|
const request = ToolGatewayApprovalRequest.parse(input);
|
|
505
615
|
let preparedCall: PreparedToolGatewayCall;
|
|
506
616
|
try {
|
|
617
|
+
await prepared.reauthorize?.();
|
|
507
618
|
preparedCall = await prepared.toolGateway.prepareCall(
|
|
508
619
|
{
|
|
509
620
|
operationId: request.operationId,
|
|
@@ -528,6 +639,7 @@ export async function approveWorkspaceToolGatewayCall(
|
|
|
528
639
|
const approvalToken = `ogta_${randomBytes(32).toString("base64url")}`;
|
|
529
640
|
const expiresAt = new Date(Date.now() + 5 * 60_000);
|
|
530
641
|
try {
|
|
642
|
+
await prepared.reauthorize?.();
|
|
531
643
|
await issueApproval(db, {
|
|
532
644
|
tokenHash: hashOpaqueValue(approvalToken),
|
|
533
645
|
accountId: grant.accountId,
|