@executor-js/sdk 1.5.16 → 1.5.18
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/{chunk-EIHWBY6T.js → chunk-4XPVLX62.js} +36 -1
- package/dist/chunk-4XPVLX62.js.map +1 -0
- package/dist/{chunk-SK3PCJHJ.js → chunk-EJKXSREX.js} +10 -1
- package/dist/chunk-EJKXSREX.js.map +1 -0
- package/dist/{chunk-6EED5LAL.js → chunk-PCSRC6WP.js} +1 -1
- package/dist/{chunk-6EED5LAL.js.map → chunk-PCSRC6WP.js.map} +1 -1
- package/dist/{chunk-VICUTMT6.js → chunk-UWBP7WLB.js} +192 -22
- package/dist/chunk-UWBP7WLB.js.map +1 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/core-schema.d.ts +2 -0
- package/dist/core-schema.d.ts.map +1 -1
- package/dist/core-tools.d.ts +3 -0
- package/dist/core-tools.d.ts.map +1 -1
- package/dist/core.js +4 -4
- package/dist/executor.d.ts +1 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/host-internal.js +1 -1
- package/dist/index.js +3 -3
- package/dist/oauth-client.d.ts +5 -0
- package/dist/oauth-client.d.ts.map +1 -1
- package/dist/oauth-helpers.d.ts +1 -0
- package/dist/oauth-helpers.d.ts.map +1 -1
- package/dist/oauth-service.d.ts +4 -0
- package/dist/oauth-service.d.ts.map +1 -1
- package/dist/server-connection.d.ts +7 -0
- package/dist/server-connection.d.ts.map +1 -1
- package/dist/shared.js +2 -2
- package/dist/testing.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-EIHWBY6T.js.map +0 -1
- package/dist/chunk-SK3PCJHJ.js.map +0 -1
- package/dist/chunk-VICUTMT6.js.map +0 -1
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
isValidPattern,
|
|
25
25
|
resolveEffectivePolicy,
|
|
26
26
|
rowToToolPolicy
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-PCSRC6WP.js";
|
|
28
28
|
import {
|
|
29
29
|
OAUTH2_DEFAULT_TIMEOUT_MS,
|
|
30
30
|
assertSupportedOAuthEndpointUrl,
|
|
@@ -35,9 +35,10 @@ import {
|
|
|
35
35
|
exchangeAuthorizationCode,
|
|
36
36
|
exchangeClientCredentials,
|
|
37
37
|
providerAuthorizeExtras,
|
|
38
|
+
rebindTokenEndpointHostToCallbackDomain,
|
|
38
39
|
refreshAccessToken,
|
|
39
40
|
shouldRefreshToken
|
|
40
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-4XPVLX62.js";
|
|
41
42
|
import {
|
|
42
43
|
AuthTemplateSlug,
|
|
43
44
|
ConnectionAddress,
|
|
@@ -549,6 +550,11 @@ var coreTables = defineTables({
|
|
|
549
550
|
refresh_item_id: nullableTextColumn("refresh_item_id"),
|
|
550
551
|
expires_at: nullableBigintColumn("expires_at"),
|
|
551
552
|
oauth_scope: nullableTextColumn("oauth_scope"),
|
|
553
|
+
// Per-connection token endpoint override. Set only when the code was
|
|
554
|
+
// redeemed at a region other than the oauth_client's configured token host
|
|
555
|
+
// (multi-site providers like Datadog signal the org's region on the
|
|
556
|
+
// callback). Null means refresh uses the oauth_client's `token_url`.
|
|
557
|
+
oauth_token_url: nullableTextColumn("oauth_token_url"),
|
|
552
558
|
provider_state: nullableJsonColumn("provider_state"),
|
|
553
559
|
created_at: dateColumn("created_at"),
|
|
554
560
|
updated_at: dateColumn("updated_at")
|
|
@@ -3114,10 +3120,26 @@ var ConnectionOutput = Schema2.Struct({
|
|
|
3114
3120
|
});
|
|
3115
3121
|
var ConnectionsListInput = Schema2.Struct({
|
|
3116
3122
|
integration: Schema2.optional(Schema2.String),
|
|
3117
|
-
owner: Schema2.optional(OwnerSchema)
|
|
3123
|
+
owner: Schema2.optional(OwnerSchema),
|
|
3124
|
+
verbose: Schema2.optional(Schema2.Boolean)
|
|
3125
|
+
});
|
|
3126
|
+
var ConnectionListItem = Schema2.Struct({
|
|
3127
|
+
owner: OwnerSchema,
|
|
3128
|
+
name: Schema2.String,
|
|
3129
|
+
integration: Schema2.String,
|
|
3130
|
+
template: Schema2.String,
|
|
3131
|
+
provider: Schema2.String,
|
|
3132
|
+
address: Schema2.String,
|
|
3133
|
+
identityLabel: Schema2.optional(Schema2.NullOr(Schema2.String)),
|
|
3134
|
+
description: Schema2.optional(Schema2.NullOr(Schema2.String)),
|
|
3135
|
+
expiresAt: Schema2.NullOr(Schema2.Number),
|
|
3136
|
+
oauthClient: Schema2.NullOr(Schema2.String),
|
|
3137
|
+
oauthClientOwner: Schema2.NullOr(OwnerSchema),
|
|
3138
|
+
oauthScopeCount: Schema2.NullOr(Schema2.Number),
|
|
3139
|
+
oauthScope: Schema2.optional(Schema2.NullOr(Schema2.String))
|
|
3118
3140
|
});
|
|
3119
3141
|
var ConnectionsListOutput = Schema2.Struct({
|
|
3120
|
-
connections: Schema2.Array(
|
|
3142
|
+
connections: Schema2.Array(ConnectionListItem)
|
|
3121
3143
|
});
|
|
3122
3144
|
var ConnectionCreateHandoffInput = Schema2.Struct({
|
|
3123
3145
|
integration: Schema2.String,
|
|
@@ -3145,7 +3167,14 @@ var ConnectionCreateInput = Schema2.Struct({
|
|
|
3145
3167
|
}).check(
|
|
3146
3168
|
Schema2.makeFilter((payload) => {
|
|
3147
3169
|
const originCount = (payload.from === void 0 ? 0 : 1) + (payload.inputs === void 0 ? 0 : 1);
|
|
3148
|
-
|
|
3170
|
+
const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE);
|
|
3171
|
+
if (isNoAuth) {
|
|
3172
|
+
if (originCount > 0) {
|
|
3173
|
+
return 'A no-auth connection (template "none") takes no provider credential origin';
|
|
3174
|
+
}
|
|
3175
|
+
} else if (originCount !== 1) {
|
|
3176
|
+
return "Expected exactly one provider credential origin";
|
|
3177
|
+
}
|
|
3149
3178
|
if (payload.inputs !== void 0 && Object.keys(payload.inputs).length === 0) {
|
|
3150
3179
|
return "Expected at least one provider credential input";
|
|
3151
3180
|
}
|
|
@@ -3229,9 +3258,23 @@ var OAuthCreateClientInput = Schema2.Struct({
|
|
|
3229
3258
|
tokenUrl: Schema2.String,
|
|
3230
3259
|
grant: OAuthGrantSchema,
|
|
3231
3260
|
clientId: Schema2.String,
|
|
3232
|
-
clientSecret: Schema2.String,
|
|
3233
3261
|
resource: Schema2.optional(Schema2.NullOr(Schema2.String))
|
|
3234
3262
|
});
|
|
3263
|
+
var OAuthCreateClientHandoffInput = Schema2.Struct({
|
|
3264
|
+
integration: Schema2.String,
|
|
3265
|
+
owner: Schema2.optional(OwnerSchema),
|
|
3266
|
+
slug: Schema2.optional(Schema2.String),
|
|
3267
|
+
grant: Schema2.optional(OAuthGrantSchema),
|
|
3268
|
+
clientId: Schema2.optional(Schema2.String),
|
|
3269
|
+
authorizationUrl: Schema2.optional(Schema2.String),
|
|
3270
|
+
tokenUrl: Schema2.optional(Schema2.String),
|
|
3271
|
+
resource: Schema2.optional(Schema2.NullOr(Schema2.String)),
|
|
3272
|
+
label: Schema2.optional(Schema2.String)
|
|
3273
|
+
});
|
|
3274
|
+
var OAuthCreateClientHandoffOutput = Schema2.Struct({
|
|
3275
|
+
url: Schema2.String,
|
|
3276
|
+
instructions: Schema2.String
|
|
3277
|
+
});
|
|
3235
3278
|
var OAuthClientOutputRef = Schema2.Struct({
|
|
3236
3279
|
client: Schema2.String
|
|
3237
3280
|
});
|
|
@@ -3310,6 +3353,8 @@ var PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);
|
|
|
3310
3353
|
var PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);
|
|
3311
3354
|
var OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);
|
|
3312
3355
|
var OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);
|
|
3356
|
+
var OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);
|
|
3357
|
+
var OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);
|
|
3313
3358
|
var OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);
|
|
3314
3359
|
var OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);
|
|
3315
3360
|
var OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);
|
|
@@ -3332,6 +3377,22 @@ var connectionToOutput = (connection) => ({
|
|
|
3332
3377
|
oauthClientOwner: connection.oauthClientOwner ?? null,
|
|
3333
3378
|
oauthScope: connection.oauthScope ?? null
|
|
3334
3379
|
});
|
|
3380
|
+
var oauthScopeCount = (scope) => scope == null ? null : scope.split(/\s+/).filter(Boolean).length;
|
|
3381
|
+
var connectionToListItem = (connection, verbose) => ({
|
|
3382
|
+
owner: connection.owner,
|
|
3383
|
+
name: String(connection.name),
|
|
3384
|
+
integration: String(connection.integration),
|
|
3385
|
+
template: String(connection.template),
|
|
3386
|
+
provider: String(connection.provider),
|
|
3387
|
+
address: String(connection.address),
|
|
3388
|
+
identityLabel: connection.identityLabel ?? null,
|
|
3389
|
+
description: connection.description ?? null,
|
|
3390
|
+
expiresAt: connection.expiresAt ?? null,
|
|
3391
|
+
oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient),
|
|
3392
|
+
oauthClientOwner: connection.oauthClientOwner ?? null,
|
|
3393
|
+
oauthScopeCount: oauthScopeCount(connection.oauthScope),
|
|
3394
|
+
...verbose ? { oauthScope: connection.oauthScope ?? null } : {}
|
|
3395
|
+
});
|
|
3335
3396
|
var toolToOutput = (toolRow) => ({
|
|
3336
3397
|
address: String(toolRow.address),
|
|
3337
3398
|
owner: toolRow.owner,
|
|
@@ -3379,12 +3440,28 @@ var createConnectionInputFromTool = (input) => {
|
|
|
3379
3440
|
)
|
|
3380
3441
|
};
|
|
3381
3442
|
};
|
|
3382
|
-
var connectionCreateHandoffUrl = (webBaseUrl, input) => {
|
|
3443
|
+
var connectionCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
|
|
3383
3444
|
const search = new URLSearchParams({ addAccount: "1" });
|
|
3384
3445
|
if (input.owner !== void 0) search.set("owner", input.owner);
|
|
3385
3446
|
if (input.template !== void 0) search.set("template", input.template);
|
|
3386
3447
|
if (input.label !== void 0) search.set("label", input.label);
|
|
3387
|
-
const
|
|
3448
|
+
const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
|
|
3449
|
+
const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
|
|
3450
|
+
if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
|
|
3451
|
+
return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
|
|
3452
|
+
};
|
|
3453
|
+
var oauthClientCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
|
|
3454
|
+
const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" });
|
|
3455
|
+
if (input.owner !== void 0) search.set("owner", input.owner);
|
|
3456
|
+
if (input.slug !== void 0) search.set("clientSlug", input.slug);
|
|
3457
|
+
if (input.grant !== void 0) search.set("grant", input.grant);
|
|
3458
|
+
if (input.clientId !== void 0) search.set("clientId", input.clientId);
|
|
3459
|
+
if (input.authorizationUrl !== void 0) search.set("authorizationUrl", input.authorizationUrl);
|
|
3460
|
+
if (input.tokenUrl !== void 0) search.set("tokenUrl", input.tokenUrl);
|
|
3461
|
+
if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource);
|
|
3462
|
+
if (input.label !== void 0) search.set("label", input.label);
|
|
3463
|
+
const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
|
|
3464
|
+
const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
|
|
3388
3465
|
if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
|
|
3389
3466
|
return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
|
|
3390
3467
|
};
|
|
@@ -3430,7 +3507,7 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3430
3507
|
}),
|
|
3431
3508
|
tool({
|
|
3432
3509
|
name: "connections.list",
|
|
3433
|
-
description: "List saved connections (the credential for one integration). Never returns the credential value. Optionally filter by integration or owner.",
|
|
3510
|
+
description: "List saved connections (the credential for one integration). Never returns the credential value. Optionally filter by integration or owner. OAuth scopes are summarized as `oauthScopeCount` by default; pass `verbose: true` to include the full `oauthScope` grant string per connection.",
|
|
3434
3511
|
inputSchema: ConnectionsListInputStd,
|
|
3435
3512
|
outputSchema: ConnectionsListOutputStd,
|
|
3436
3513
|
execute: (input, { ctx }) => Effect5.map(
|
|
@@ -3439,15 +3516,24 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3439
3516
|
owner: input.owner === void 0 ? void 0 : input.owner
|
|
3440
3517
|
}),
|
|
3441
3518
|
(connections) => ({
|
|
3442
|
-
connections: connections.map(
|
|
3519
|
+
connections: connections.map(
|
|
3520
|
+
(connection) => connectionToListItem(connection, input.verbose === true)
|
|
3521
|
+
)
|
|
3443
3522
|
})
|
|
3444
3523
|
)
|
|
3445
3524
|
}),
|
|
3446
3525
|
tool({
|
|
3447
3526
|
name: "connections.create",
|
|
3448
|
-
description:
|
|
3527
|
+
description: 'Low-level create or replace for a saved connection from provider item references. For a no-auth integration (public MCP server, public REST API), pass `template: "none"` with no `from`/`inputs` to wire it up directly. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.',
|
|
3449
3528
|
inputSchema: ConnectionCreateInputStd,
|
|
3450
3529
|
outputSchema: ConnectionOutputStd,
|
|
3530
|
+
// Creating a connection binds a credential reference and roots a new
|
|
3531
|
+
// tool catalog: every tool that connection produces then becomes
|
|
3532
|
+
// callable. Even the no-auth (`template: "none"`) path pulls tools
|
|
3533
|
+
// from an arbitrary endpoint. Prompt-injected code could silently
|
|
3534
|
+
// wire an attacker-chosen integration or credential, so this is
|
|
3535
|
+
// approval-gated (the v1 `sources.configure` carried the same guard).
|
|
3536
|
+
annotations: { requiresApproval: true },
|
|
3451
3537
|
execute: (input, { ctx }) => Effect5.map(
|
|
3452
3538
|
ctx.connections.create(createConnectionInputFromTool(input)),
|
|
3453
3539
|
connectionToOutput
|
|
@@ -3459,7 +3545,7 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3459
3545
|
inputSchema: ConnectionCreateHandoffInputStd,
|
|
3460
3546
|
outputSchema: ConnectionCreateHandoffOutputStd,
|
|
3461
3547
|
execute: (input) => {
|
|
3462
|
-
const url = connectionCreateHandoffUrl(options.webBaseUrl, input);
|
|
3548
|
+
const url = connectionCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
|
|
3463
3549
|
return Effect5.succeed({
|
|
3464
3550
|
url,
|
|
3465
3551
|
instructions: "Ask the user to open this URL and add the account in the Executor web UI. Do not ask them to paste the credential value into chat. After they finish, call connections.list for the integration to discover the created connection."
|
|
@@ -3471,6 +3557,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3471
3557
|
description: "Remove a saved connection and its produced tools by owner, integration, and connection name.",
|
|
3472
3558
|
inputSchema: ConnectionRefInputStd,
|
|
3473
3559
|
outputSchema: RemovedOutputStd,
|
|
3560
|
+
// Deleting a connection drops it and every tool it produced, which
|
|
3561
|
+
// prompt-injected code could use to disrupt an integration or force a
|
|
3562
|
+
// re-add flow. Approval-gated, matching v1 `sources.remove`.
|
|
3563
|
+
annotations: { requiresApproval: true },
|
|
3474
3564
|
execute: (input, { ctx }) => Effect5.map(ctx.connections.remove(connectionRefFromInput(input)), () => ({
|
|
3475
3565
|
removed: true
|
|
3476
3566
|
}))
|
|
@@ -3480,6 +3570,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3480
3570
|
description: "Re-run an integration's tool production for a saved connection, replacing that connection's persisted tools.",
|
|
3481
3571
|
inputSchema: ConnectionRefInputStd,
|
|
3482
3572
|
outputSchema: ConnectionsRefreshOutputStd,
|
|
3573
|
+
// Refresh replaces a connection's persisted tool set; for a mutable
|
|
3574
|
+
// upstream (an MCP server whose catalog can change) this can swap in
|
|
3575
|
+
// different tools without confirmation. Approval-gated, matching v1
|
|
3576
|
+
// `sources.refresh`.
|
|
3577
|
+
annotations: { requiresApproval: true },
|
|
3483
3578
|
execute: (input, { ctx }) => Effect5.map(ctx.connections.refresh(connectionRefFromInput(input)), (tools) => ({
|
|
3484
3579
|
tools: tools.map(toolToOutput)
|
|
3485
3580
|
}))
|
|
@@ -3523,9 +3618,20 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3523
3618
|
}),
|
|
3524
3619
|
tool({
|
|
3525
3620
|
name: "oauth.clients.create",
|
|
3526
|
-
description: "Register or replace an owner-scoped OAuth client
|
|
3621
|
+
description: "Register or replace an owner-scoped OAuth client WITHOUT a client secret: a PUBLIC client (PKCE / authorization_code) or a discovery-prefill placeholder. To register a CONFIDENTIAL client that has a secret, call `oauth.clients.createHandoff` instead so the human enters the secret in the web UI; never pass a client secret through this tool.",
|
|
3527
3622
|
inputSchema: OAuthCreateClientInputStd,
|
|
3528
3623
|
outputSchema: OAuthClientOutputRefStd,
|
|
3624
|
+
// This persists an OAuth client and REPLACES on slug collision. It
|
|
3625
|
+
// takes NO client secret: a secret would have to travel through the
|
|
3626
|
+
// agent's context window, so a confidential app is registered by the
|
|
3627
|
+
// human via `oauth.clients.createHandoff`. An empty secret registers a
|
|
3628
|
+
// PUBLIC client. The remaining risk is the write itself: prompt-injected
|
|
3629
|
+
// code could register a client with an attacker-controlled
|
|
3630
|
+
// authorizationUrl/tokenUrl, then drive `oauth.start` to mint a
|
|
3631
|
+
// connection and route the user's tokens to the attacker. The
|
|
3632
|
+
// highest-value gate here; matches v1 `sources.bindings.set`, which
|
|
3633
|
+
// guarded credential writes.
|
|
3634
|
+
annotations: { requiresApproval: true },
|
|
3529
3635
|
execute: (input, { ctx }) => Effect5.map(
|
|
3530
3636
|
ctx.oauth.createClient({
|
|
3531
3637
|
owner: input.owner,
|
|
@@ -3534,17 +3640,40 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3534
3640
|
tokenUrl: input.tokenUrl,
|
|
3535
3641
|
grant: input.grant,
|
|
3536
3642
|
clientId: input.clientId,
|
|
3537
|
-
|
|
3643
|
+
// No secret crosses the agent boundary; an empty secret registers
|
|
3644
|
+
// a public client. Confidential clients go through
|
|
3645
|
+
// `oauth.clients.createHandoff`.
|
|
3646
|
+
clientSecret: "",
|
|
3538
3647
|
resource: input.resource ?? null
|
|
3539
3648
|
}),
|
|
3540
3649
|
(client) => ({ client: String(client) })
|
|
3541
3650
|
)
|
|
3542
3651
|
}),
|
|
3652
|
+
tool({
|
|
3653
|
+
name: "oauth.clients.createHandoff",
|
|
3654
|
+
description: "Return a browser URL that opens the Register-OAuth-app form for one integration, pre-filled with the non-secret fields (client id, endpoints, grant). Use this for any CONFIDENTIAL OAuth app: the user types the client secret directly in the web UI instead of sending it through the agent. After they register the app, call `oauth.clients.list` to discover its owner and slug, then `oauth.start`.",
|
|
3655
|
+
inputSchema: OAuthCreateClientHandoffInputStd,
|
|
3656
|
+
outputSchema: OAuthCreateClientHandoffOutputStd,
|
|
3657
|
+
// Pure URL builder: no DB write, no token, no secret. This is the SAFE
|
|
3658
|
+
// path (it routes the secret to the human in the browser), so it is
|
|
3659
|
+
// deliberately NOT approval-gated, mirroring `connections.createHandoff`.
|
|
3660
|
+
execute: (input) => {
|
|
3661
|
+
const url = oauthClientCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
|
|
3662
|
+
return Effect5.succeed({
|
|
3663
|
+
url,
|
|
3664
|
+
instructions: "Ask the user to open this URL and register the OAuth app in the Executor web UI, entering the client secret there. Do not ask them to paste the client secret into chat. After they finish, call oauth.clients.list to find the registered client (owner + slug), then oauth.start."
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
}),
|
|
3543
3668
|
tool({
|
|
3544
3669
|
name: "oauth.clients.registerDynamic",
|
|
3545
3670
|
description: "Register an OAuth client through RFC 7591 Dynamic Client Registration and save the minted client for later `oauth.start` calls.",
|
|
3546
3671
|
inputSchema: OAuthRegisterDynamicInputStd,
|
|
3547
3672
|
outputSchema: OAuthClientOutputRefStd,
|
|
3673
|
+
// Same risk class as `oauth.clients.create`: registers a client at a
|
|
3674
|
+
// caller-supplied endpoint and persists the minted credentials for
|
|
3675
|
+
// later `oauth.start` abuse. Approval-gated. See `oauth.clients.create`.
|
|
3676
|
+
annotations: { requiresApproval: true },
|
|
3548
3677
|
execute: (input, { ctx }) => Effect5.map(
|
|
3549
3678
|
ctx.oauth.registerDynamicClient({
|
|
3550
3679
|
owner: input.owner,
|
|
@@ -3567,6 +3696,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3567
3696
|
description: "Remove an owner-scoped OAuth client by owner and slug. Existing connections are not cascaded.",
|
|
3568
3697
|
inputSchema: OAuthRemoveClientInputStd,
|
|
3569
3698
|
outputSchema: RemovedOutputStd,
|
|
3699
|
+
// Removing a client breaks token refresh for every connection that
|
|
3700
|
+
// depends on it (a silent DoS) and can force re-auth through an
|
|
3701
|
+
// attacker-supplied replacement. Approval-gated, matching v1
|
|
3702
|
+
// `sources.bindings.remove`.
|
|
3703
|
+
annotations: { requiresApproval: true },
|
|
3570
3704
|
execute: (input, { ctx }) => Effect5.map(
|
|
3571
3705
|
ctx.oauth.removeClient(input.owner, OAuthClientSlug.make(input.slug)),
|
|
3572
3706
|
() => ({ removed: true })
|
|
@@ -3591,6 +3725,14 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3591
3725
|
description: "Start OAuth through a registered client to mint a connection for an integration. `client_credentials` clients return `connected`; authorization-code clients return an authorization URL and state.",
|
|
3592
3726
|
inputSchema: OAuthStartInputStd,
|
|
3593
3727
|
outputSchema: OAuthStartOutputStd,
|
|
3728
|
+
// This is the materialization step that turns a registered client
|
|
3729
|
+
// into a live connection. For `client_credentials` it completes
|
|
3730
|
+
// synchronously (status `connected`) with no browser step, so a
|
|
3731
|
+
// prompt-injected call against an attacker-registered client mints a
|
|
3732
|
+
// credentialed connection with no human in the loop. The
|
|
3733
|
+
// authorization-code path already returns a URL the user must visit,
|
|
3734
|
+
// but one gate on the whole tool covers the silent path cleanly.
|
|
3735
|
+
annotations: { requiresApproval: true },
|
|
3594
3736
|
execute: (input, { ctx }) => Effect5.map(
|
|
3595
3737
|
ctx.oauth.start({
|
|
3596
3738
|
client: OAuthClientSlug.make(input.client),
|
|
@@ -3638,6 +3780,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3638
3780
|
description: "Create a tool policy. `pattern` matches a tool address tail (`integration.connection.tool`, `integration.*`, `*`); `action` is approve/require_approval/block. `owner` is org (workspace guardrail) or user (personal).",
|
|
3639
3781
|
inputSchema: PolicyCreateInputStd,
|
|
3640
3782
|
outputSchema: PolicyOutputStd,
|
|
3783
|
+
// A policy decides which tools run without confirmation, so creating
|
|
3784
|
+
// one can silence every other approval gate (e.g. `approve *`). It
|
|
3785
|
+
// must itself require approval, otherwise prompt-injected code could
|
|
3786
|
+
// disable approvals by writing its own bypass policy.
|
|
3787
|
+
annotations: { requiresApproval: true },
|
|
3641
3788
|
execute: (input, { ctx }) => Effect5.map(
|
|
3642
3789
|
ctx.core.policies.create({
|
|
3643
3790
|
owner: input.owner,
|
|
@@ -3658,6 +3805,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3658
3805
|
description: "Update a tool policy's pattern and/or action by id + owner.",
|
|
3659
3806
|
inputSchema: PolicyUpdateInputStd,
|
|
3660
3807
|
outputSchema: PolicyOutputStd,
|
|
3808
|
+
// Editing a policy can broaden a pattern or flip an action to
|
|
3809
|
+
// `approve`, weakening an approval gate just as creation can, so it
|
|
3810
|
+
// requires approval too. See `policies.create`.
|
|
3811
|
+
annotations: { requiresApproval: true },
|
|
3661
3812
|
execute: (input, { ctx }) => Effect5.map(
|
|
3662
3813
|
ctx.core.policies.update({
|
|
3663
3814
|
id: input.id,
|
|
@@ -3679,6 +3830,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3679
3830
|
description: "Remove a tool policy by id + owner.",
|
|
3680
3831
|
inputSchema: PolicyRemoveInputStd,
|
|
3681
3832
|
outputSchema: RemovedOutputStd,
|
|
3833
|
+
// Removing a policy can drop a `block` or `require_approval`
|
|
3834
|
+
// guardrail, so deletion is also approval-gated. See
|
|
3835
|
+
// `policies.create`.
|
|
3836
|
+
annotations: { requiresApproval: true },
|
|
3682
3837
|
execute: (input, { ctx }) => Effect5.map(
|
|
3683
3838
|
ctx.core.policies.remove({
|
|
3684
3839
|
id: input.id,
|
|
@@ -4339,7 +4494,9 @@ var makeOAuthService = (deps) => {
|
|
|
4339
4494
|
client,
|
|
4340
4495
|
token,
|
|
4341
4496
|
requestedScopes,
|
|
4342
|
-
input.clientOwner
|
|
4497
|
+
input.clientOwner,
|
|
4498
|
+
// client_credentials has no callback, so no regional rebind applies.
|
|
4499
|
+
null
|
|
4343
4500
|
).pipe(
|
|
4344
4501
|
Effect7.mapError(
|
|
4345
4502
|
(cause) => new OAuthStartError({
|
|
@@ -4452,8 +4609,12 @@ var makeOAuthService = (deps) => {
|
|
|
4452
4609
|
restartRequired: true
|
|
4453
4610
|
});
|
|
4454
4611
|
}
|
|
4612
|
+
const tokenUrl = rebindTokenEndpointHostToCallbackDomain(
|
|
4613
|
+
client.tokenUrl,
|
|
4614
|
+
input.callbackDomain
|
|
4615
|
+
);
|
|
4455
4616
|
const token = yield* exchangeAuthorizationCode({
|
|
4456
|
-
tokenUrl
|
|
4617
|
+
tokenUrl,
|
|
4457
4618
|
clientId: client.clientId,
|
|
4458
4619
|
clientSecret: client.clientSecret,
|
|
4459
4620
|
redirectUrl: session.redirectUrl,
|
|
@@ -4483,7 +4644,10 @@ var makeOAuthService = (deps) => {
|
|
|
4483
4644
|
// The scopes `start` requested (the integration's declared set), persisted
|
|
4484
4645
|
// on the session. Empty only for a corrupt/legacy session with no payload.
|
|
4485
4646
|
session.requestedScopes ?? [],
|
|
4486
|
-
session.clientOwner
|
|
4647
|
+
session.clientOwner,
|
|
4648
|
+
// Persist the regional token endpoint ONLY when it differs from the
|
|
4649
|
+
// client's configured one, so refresh redeems against the same region.
|
|
4650
|
+
tokenUrl === client.tokenUrl ? null : tokenUrl
|
|
4487
4651
|
).pipe(
|
|
4488
4652
|
Effect7.mapError(
|
|
4489
4653
|
(cause) => new OAuthCompleteError({
|
|
@@ -4496,7 +4660,7 @@ var makeOAuthService = (deps) => {
|
|
|
4496
4660
|
yield* deleteSession(input.state);
|
|
4497
4661
|
return connection;
|
|
4498
4662
|
});
|
|
4499
|
-
const mintFromToken = (target, client, token, requestedScopes, clientOwner) => Effect7.gen(function* () {
|
|
4663
|
+
const mintFromToken = (target, client, token, requestedScopes, clientOwner, oauthTokenUrl) => Effect7.gen(function* () {
|
|
4500
4664
|
const provider = deps.defaultWritableProvider();
|
|
4501
4665
|
if (!provider || !provider.set) {
|
|
4502
4666
|
return yield* new StorageError({
|
|
@@ -4527,7 +4691,8 @@ var makeOAuthService = (deps) => {
|
|
|
4527
4691
|
// Microsoft, issue a refresh token for `offline_access` but omit that
|
|
4528
4692
|
// non-resource scope from the token `scope` string, so preserve it when
|
|
4529
4693
|
// the refresh token proves it was granted.
|
|
4530
|
-
oauthScope: recordedOAuthScope(token, requestedScopes)
|
|
4694
|
+
oauthScope: recordedOAuthScope(token, requestedScopes),
|
|
4695
|
+
oauthTokenUrl
|
|
4531
4696
|
});
|
|
4532
4697
|
});
|
|
4533
4698
|
const deleteSession = (state) => deps.fuma.use(
|
|
@@ -5128,6 +5293,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5128
5293
|
const plugins = config.coreTools ? [
|
|
5129
5294
|
coreToolsPlugin({
|
|
5130
5295
|
webBaseUrl: config.coreTools.webBaseUrl,
|
|
5296
|
+
orgSlug: config.coreTools.orgSlug,
|
|
5131
5297
|
includeProviders: config.coreTools.includeProviders
|
|
5132
5298
|
}),
|
|
5133
5299
|
...userPlugins
|
|
@@ -5249,8 +5415,9 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5249
5415
|
}
|
|
5250
5416
|
const clientSecret = clientRow.client_secret_item_id ? (yield* provider.get(ProviderItemId.make(String(clientRow.client_secret_item_id)))) ?? "" : "";
|
|
5251
5417
|
const grantedScopes = row.oauth_scope ? String(row.oauth_scope).split(/\s+/).filter(Boolean) : [];
|
|
5418
|
+
const tokenUrl = row.oauth_token_url ? String(row.oauth_token_url) : String(clientRow.token_url);
|
|
5252
5419
|
const token = String(clientRow.grant) === "client_credentials" ? yield* exchangeClientCredentials({
|
|
5253
|
-
tokenUrl
|
|
5420
|
+
tokenUrl,
|
|
5254
5421
|
clientId: String(clientRow.client_id),
|
|
5255
5422
|
clientSecret,
|
|
5256
5423
|
scopes: grantedScopes,
|
|
@@ -5277,7 +5444,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5277
5444
|
return yield* reauth("Stored refresh token could not be resolved.");
|
|
5278
5445
|
}
|
|
5279
5446
|
return yield* refreshAccessToken({
|
|
5280
|
-
tokenUrl
|
|
5447
|
+
tokenUrl,
|
|
5281
5448
|
clientId: String(clientRow.client_id),
|
|
5282
5449
|
clientSecret,
|
|
5283
5450
|
refreshToken,
|
|
@@ -5817,6 +5984,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5817
5984
|
refresh_item_id: input.refreshItemId,
|
|
5818
5985
|
expires_at: input.expiresAt,
|
|
5819
5986
|
oauth_scope: input.oauthScope,
|
|
5987
|
+
oauth_token_url: input.oauthTokenUrl ?? null,
|
|
5820
5988
|
updated_at: now
|
|
5821
5989
|
};
|
|
5822
5990
|
if (existing) {
|
|
@@ -5847,6 +6015,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5847
6015
|
refresh_item_id: input.refreshItemId,
|
|
5848
6016
|
expires_at: input.expiresAt,
|
|
5849
6017
|
oauth_scope: input.oauthScope,
|
|
6018
|
+
oauth_token_url: input.oauthTokenUrl ?? null,
|
|
5850
6019
|
provider_state: null,
|
|
5851
6020
|
created_at: now,
|
|
5852
6021
|
updated_at: now
|
|
@@ -5874,6 +6043,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5874
6043
|
refresh_item_id: input.refreshItemId,
|
|
5875
6044
|
expires_at: input.expiresAt,
|
|
5876
6045
|
oauth_scope: input.oauthScope,
|
|
6046
|
+
oauth_token_url: input.oauthTokenUrl ?? null,
|
|
5877
6047
|
provider_state: null,
|
|
5878
6048
|
created_at: now,
|
|
5879
6049
|
updated_at: now
|
|
@@ -6685,4 +6855,4 @@ export {
|
|
|
6685
6855
|
collectTables,
|
|
6686
6856
|
createExecutor
|
|
6687
6857
|
};
|
|
6688
|
-
//# sourceMappingURL=chunk-
|
|
6858
|
+
//# sourceMappingURL=chunk-UWBP7WLB.js.map
|