@doist/automations-cli 1.17.1 → 1.18.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/index.js +302 -23
- package/dist/index.js.map +4 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6899,7 +6899,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6899
6899
|
// package.json
|
|
6900
6900
|
var package_default = {
|
|
6901
6901
|
name: "@doist/automations-cli",
|
|
6902
|
-
version: "1.
|
|
6902
|
+
version: "1.18.0",
|
|
6903
6903
|
description: "Command-line client for Todoist Automations",
|
|
6904
6904
|
type: "module",
|
|
6905
6905
|
bin: {
|
|
@@ -6953,13 +6953,13 @@ var package_default = {
|
|
|
6953
6953
|
devDependencies: {
|
|
6954
6954
|
"@doist/automations-core": "*",
|
|
6955
6955
|
"@doist/automations-sdk": "*",
|
|
6956
|
-
"@types/node": "24.
|
|
6956
|
+
"@types/node": "24.19.1",
|
|
6957
6957
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
6958
6958
|
esbuild: "0.28.2",
|
|
6959
6959
|
"semantic-release": "25.0.9",
|
|
6960
6960
|
"semantic-release-monorepo": "8.0.2",
|
|
6961
6961
|
typescript: "6.0.3",
|
|
6962
|
-
vitest: "
|
|
6962
|
+
vitest: "5.0.3"
|
|
6963
6963
|
},
|
|
6964
6964
|
engines: {
|
|
6965
6965
|
node: ">=24"
|
|
@@ -7037,6 +7037,10 @@ var API_SCOPES = {
|
|
|
7037
7037
|
};
|
|
7038
7038
|
var API_SCOPE_CODES = Object.keys(API_SCOPES);
|
|
7039
7039
|
|
|
7040
|
+
// ../../packages/core/dist/esm/constants/cli-client.constants.js
|
|
7041
|
+
var AUTOMATIONS_CLI_CLIENT_NAME = "Todoist Automations CLI";
|
|
7042
|
+
var AUTOMATIONS_CLI_LOGO_URL = "https://todoist.b-cdn.net/agentist-icons/service_automations-cli_color_192px.png";
|
|
7043
|
+
|
|
7040
7044
|
// ../../packages/core/dist/esm/constants/connection.constants.js
|
|
7041
7045
|
var CONNECTION_STATUSES = ["pending", "authorized", "expired", "error"];
|
|
7042
7046
|
var CONNECTION_TYPES = ["mcp", "trigger", "api"];
|
|
@@ -7124,71 +7128,98 @@ var MODEL_CATALOG = {
|
|
|
7124
7128
|
"gemini-2.5-flash-lite": {
|
|
7125
7129
|
provider: "gemini",
|
|
7126
7130
|
family: "2.5",
|
|
7127
|
-
providerModelId: "gemini-2.5-flash-lite"
|
|
7131
|
+
providerModelId: "gemini-2.5-flash-lite",
|
|
7132
|
+
manifestSelectable: true
|
|
7128
7133
|
},
|
|
7129
7134
|
"gemini-2.5-flash": {
|
|
7130
7135
|
provider: "gemini",
|
|
7131
7136
|
family: "2.5",
|
|
7132
|
-
providerModelId: "gemini-2.5-flash"
|
|
7137
|
+
providerModelId: "gemini-2.5-flash",
|
|
7138
|
+
manifestSelectable: true
|
|
7133
7139
|
},
|
|
7134
7140
|
"gemini-3.1-pro": {
|
|
7135
7141
|
provider: "gemini",
|
|
7136
7142
|
family: "3",
|
|
7137
|
-
providerModelId: "gemini-3.1-pro-preview"
|
|
7143
|
+
providerModelId: "gemini-3.1-pro-preview",
|
|
7144
|
+
manifestSelectable: true
|
|
7138
7145
|
},
|
|
7139
7146
|
"gemini-3.8-flash": {
|
|
7140
7147
|
provider: "gemini",
|
|
7141
7148
|
family: "3",
|
|
7142
|
-
providerModelId: "gemini-3.8-flash"
|
|
7149
|
+
providerModelId: "gemini-3.8-flash",
|
|
7150
|
+
manifestSelectable: true
|
|
7143
7151
|
},
|
|
7144
7152
|
"claude-opus-4-6": {
|
|
7145
7153
|
provider: "anthropic",
|
|
7146
7154
|
family: "4.6",
|
|
7147
|
-
providerModelId: "claude-opus-4-6"
|
|
7155
|
+
providerModelId: "claude-opus-4-6",
|
|
7156
|
+
manifestSelectable: true
|
|
7148
7157
|
},
|
|
7149
7158
|
"claude-sonnet-5": {
|
|
7150
7159
|
provider: "anthropic",
|
|
7151
7160
|
family: "5",
|
|
7152
|
-
providerModelId: "claude-sonnet-5"
|
|
7161
|
+
providerModelId: "claude-sonnet-5",
|
|
7162
|
+
manifestSelectable: true
|
|
7163
|
+
},
|
|
7164
|
+
"claude-sonnet-5-5": {
|
|
7165
|
+
provider: "anthropic",
|
|
7166
|
+
family: "5",
|
|
7167
|
+
providerModelId: "claude-sonnet-5-5",
|
|
7168
|
+
manifestSelectable: false
|
|
7169
|
+
},
|
|
7170
|
+
"claude-haiku-5-5": {
|
|
7171
|
+
provider: "anthropic",
|
|
7172
|
+
family: "5",
|
|
7173
|
+
providerModelId: "claude-haiku-5-5",
|
|
7174
|
+
manifestSelectable: false
|
|
7153
7175
|
},
|
|
7154
7176
|
"claude-sonnet-4-6": {
|
|
7155
7177
|
provider: "anthropic",
|
|
7156
7178
|
family: "4.6",
|
|
7157
|
-
providerModelId: "claude-sonnet-4-6"
|
|
7179
|
+
providerModelId: "claude-sonnet-4-6",
|
|
7180
|
+
manifestSelectable: true
|
|
7158
7181
|
},
|
|
7159
7182
|
"claude-haiku-4-5": {
|
|
7160
7183
|
provider: "anthropic",
|
|
7161
7184
|
family: "4.5",
|
|
7162
|
-
providerModelId: "claude-haiku-4-5"
|
|
7185
|
+
providerModelId: "claude-haiku-4-5",
|
|
7186
|
+
manifestSelectable: true
|
|
7163
7187
|
},
|
|
7164
7188
|
"deepseek-v4-flash": {
|
|
7165
7189
|
provider: "openrouter",
|
|
7166
7190
|
family: "deepseek",
|
|
7167
|
-
providerModelId: "deepseek/deepseek-v4-flash-0731"
|
|
7191
|
+
providerModelId: "deepseek/deepseek-v4-flash-0731",
|
|
7192
|
+
manifestSelectable: true
|
|
7168
7193
|
},
|
|
7169
7194
|
"deepseek-v4-pro": {
|
|
7170
7195
|
provider: "openrouter",
|
|
7171
7196
|
family: "deepseek",
|
|
7172
|
-
providerModelId: "deepseek/deepseek-v4-pro"
|
|
7197
|
+
providerModelId: "deepseek/deepseek-v4-pro",
|
|
7198
|
+
manifestSelectable: true
|
|
7173
7199
|
},
|
|
7174
7200
|
"mimo-v2.5": {
|
|
7175
7201
|
provider: "openrouter",
|
|
7176
7202
|
family: "mimo",
|
|
7177
|
-
providerModelId: "xiaomi/mimo-v2.5"
|
|
7203
|
+
providerModelId: "xiaomi/mimo-v2.5",
|
|
7204
|
+
manifestSelectable: true
|
|
7178
7205
|
},
|
|
7179
7206
|
"mimo-v2.5-pro": {
|
|
7180
7207
|
provider: "openrouter",
|
|
7181
7208
|
family: "mimo",
|
|
7182
|
-
providerModelId: "xiaomi/mimo-v2.5-pro"
|
|
7209
|
+
providerModelId: "xiaomi/mimo-v2.5-pro",
|
|
7210
|
+
manifestSelectable: true
|
|
7183
7211
|
},
|
|
7184
7212
|
"glm-5.2": {
|
|
7185
7213
|
provider: "openrouter",
|
|
7186
7214
|
family: "glm",
|
|
7187
|
-
providerModelId: "z-ai/glm-5.2"
|
|
7215
|
+
providerModelId: "z-ai/glm-5.2",
|
|
7216
|
+
manifestSelectable: true
|
|
7188
7217
|
}
|
|
7189
7218
|
};
|
|
7190
7219
|
var SUPPORTED_LLM_MODELS = Object.keys(MODEL_CATALOG);
|
|
7191
7220
|
var SUPPORTED_SEARCH_MODELS = SUPPORTED_LLM_MODELS.filter(supportsWebSearch);
|
|
7221
|
+
var MANIFEST_LLM_MODELS = SUPPORTED_LLM_MODELS.filter((modelId) => MODEL_CATALOG[modelId].manifestSelectable);
|
|
7222
|
+
var MANIFEST_SEARCH_MODELS = MANIFEST_LLM_MODELS.filter((modelId) => supportsWebSearch(modelId));
|
|
7192
7223
|
function supportsWebSearch(modelId) {
|
|
7193
7224
|
const { provider } = MODEL_CATALOG[modelId];
|
|
7194
7225
|
return provider === "gemini" || provider === "anthropic";
|
|
@@ -27675,7 +27706,12 @@ var ConnectorSchema = external_exports.object({
|
|
|
27675
27706
|
* connection types — one onboarding covers both action and trigger uses.
|
|
27676
27707
|
* The FE collapses per-type rows into a single account when this is true.
|
|
27677
27708
|
*/
|
|
27678
|
-
isManaged: external_exports.boolean().optional()
|
|
27709
|
+
isManaged: external_exports.boolean().optional(),
|
|
27710
|
+
/**
|
|
27711
|
+
* The service only admits loopback OAuth clients, so account setup runs in
|
|
27712
|
+
* the Automations CLI on the user's machine instead of the web app.
|
|
27713
|
+
*/
|
|
27714
|
+
connectsViaCli: external_exports.boolean().optional()
|
|
27679
27715
|
});
|
|
27680
27716
|
var GetConnectorsQuerySchema = external_exports.object({
|
|
27681
27717
|
query: external_exports.string().optional()
|
|
@@ -27701,6 +27737,11 @@ var connectorListResponseSchema = external_exports.object({
|
|
|
27701
27737
|
nextCursor: external_exports.string().nullable()
|
|
27702
27738
|
});
|
|
27703
27739
|
|
|
27740
|
+
// ../../packages/core/dist/esm/utils/cli-command.js
|
|
27741
|
+
function connectViaCliCommand(connectorId, capability) {
|
|
27742
|
+
return `tda connector connect ${connectorId} --capability ${capability}`;
|
|
27743
|
+
}
|
|
27744
|
+
|
|
27704
27745
|
// ../../packages/core/dist/esm/utils/duration.js
|
|
27705
27746
|
var durationUnits = ["ms", "s", "m", "h", "d", "w"];
|
|
27706
27747
|
var createDurationPattern = (units) => units.join("|");
|
|
@@ -28068,7 +28109,7 @@ var baseStepSchema = external_exports.object({
|
|
|
28068
28109
|
})
|
|
28069
28110
|
});
|
|
28070
28111
|
var MODEL_TIERS = ["lite", "flash", "pro"];
|
|
28071
|
-
var SUPPORTED_MODEL_REFERENCES = [...MODEL_TIERS, ...
|
|
28112
|
+
var SUPPORTED_MODEL_REFERENCES = [...MODEL_TIERS, ...MANIFEST_LLM_MODELS];
|
|
28072
28113
|
var modelReferenceSchema = external_exports.enum(SUPPORTED_MODEL_REFERENCES).meta({
|
|
28073
28114
|
description: "A stable model tier (lite, flash, pro) or a supported literal model override."
|
|
28074
28115
|
});
|
|
@@ -28124,7 +28165,7 @@ var searchStepSchema = baseStepSchema.merge(external_exports.object({
|
|
|
28124
28165
|
description: "The search prompt to execute using web search."
|
|
28125
28166
|
}),
|
|
28126
28167
|
outputSchema: searchStepOutputSchema,
|
|
28127
|
-
model: external_exports.enum([...MODEL_TIERS, ...
|
|
28168
|
+
model: external_exports.enum([...MODEL_TIERS, ...MANIFEST_SEARCH_MODELS]).optional().meta({
|
|
28128
28169
|
description: "A stable tier (lite, flash, pro) or a supported search-enabled literal model override."
|
|
28129
28170
|
})
|
|
28130
28171
|
}));
|
|
@@ -28753,6 +28794,29 @@ var CreateConnectionParamsSchema = external_exports.object({
|
|
|
28753
28794
|
var CreateConnectionBodySchema = external_exports.object({
|
|
28754
28795
|
automationIds: external_exports.array(external_exports.uuid()).optional()
|
|
28755
28796
|
});
|
|
28797
|
+
var ImportLocalOAuthConnectionBodySchema = external_exports.object({
|
|
28798
|
+
/** The public client the CLI registered; the server bound the grant to it. */
|
|
28799
|
+
clientId: external_exports.string().min(1),
|
|
28800
|
+
accessToken: external_exports.string().min(1),
|
|
28801
|
+
refreshToken: external_exports.string().min(1).optional(),
|
|
28802
|
+
/** Seconds until `accessToken` expires, as the token endpoint reported it. */
|
|
28803
|
+
expiresIn: external_exports.number().int().positive().optional(),
|
|
28804
|
+
/**
|
|
28805
|
+
* A broken connection of the caller's, for the same connector, whose
|
|
28806
|
+
* automation bindings move to the new grant when the account cannot be
|
|
28807
|
+
* matched to it.
|
|
28808
|
+
*/
|
|
28809
|
+
repairConnectionId: external_exports.uuid().optional()
|
|
28810
|
+
});
|
|
28811
|
+
var LocalOAuthSetupSchema = external_exports.object({
|
|
28812
|
+
/** RFC 7591 registration endpoint; null when the server offers none. */
|
|
28813
|
+
registrationEndpoint: external_exports.string().url().nullable(),
|
|
28814
|
+
authorizationEndpoint: external_exports.string().url(),
|
|
28815
|
+
tokenEndpoint: external_exports.string().url(),
|
|
28816
|
+
/** RFC 8707 resource indicator for the MCP server, when it advertises one. */
|
|
28817
|
+
resource: external_exports.string().url().nullable(),
|
|
28818
|
+
scopes: external_exports.array(external_exports.string())
|
|
28819
|
+
});
|
|
28756
28820
|
var OAuthConnectionSetupSchema = external_exports.object({
|
|
28757
28821
|
kind: external_exports.literal("oauth"),
|
|
28758
28822
|
authorizationUrl: external_exports.string().url()
|
|
@@ -29572,7 +29636,9 @@ var FRONTEND_COHORT_EVENTS = [
|
|
|
29572
29636
|
"chat:sent",
|
|
29573
29637
|
"automation:repaired",
|
|
29574
29638
|
"template:used",
|
|
29575
|
-
"top-up:completed"
|
|
29639
|
+
"top-up:completed",
|
|
29640
|
+
"guided-creation:started",
|
|
29641
|
+
"guided-creation:completed"
|
|
29576
29642
|
];
|
|
29577
29643
|
var BACKEND_COHORT_EVENTS = [
|
|
29578
29644
|
// API-process (publish lifecycle)
|
|
@@ -31326,6 +31392,7 @@ var connectionsEndpoints = {
|
|
|
31326
31392
|
token: (id) => `${root10}/${encodeURIComponent(id)}/token`,
|
|
31327
31393
|
dependentAutomations: (connectorId) => `${root10}/${encodeURIComponent(connectorId)}/automations`,
|
|
31328
31394
|
create: (connectorId, capability) => `${root10}/${encodeURIComponent(connectorId)}/${encodeURIComponent(capability)}`,
|
|
31395
|
+
localOAuth: (connectorId, capability) => `${root10}/${encodeURIComponent(connectorId)}/${encodeURIComponent(capability)}/local-oauth`,
|
|
31329
31396
|
checkManifest: `${root10}/check-manifest`
|
|
31330
31397
|
};
|
|
31331
31398
|
|
|
@@ -31405,6 +31472,41 @@ var ConnectionsClient = class extends BaseClient {
|
|
|
31405
31472
|
});
|
|
31406
31473
|
return CreateConnectionResponseSchema.parse(data);
|
|
31407
31474
|
}
|
|
31475
|
+
/**
|
|
31476
|
+
* The authorization server endpoints and scopes for running a
|
|
31477
|
+
* `connectsViaCli` connector's OAuth flow with a loopback client.
|
|
31478
|
+
*/
|
|
31479
|
+
async getLocalOAuthSetup(args, options) {
|
|
31480
|
+
const { connectorId, capability } = CreateConnectionParamsSchema.parse({
|
|
31481
|
+
connectorId: IdSchema.parse(args.connectorId),
|
|
31482
|
+
capability: args.capability
|
|
31483
|
+
});
|
|
31484
|
+
const data = await this.request({
|
|
31485
|
+
method: "GET",
|
|
31486
|
+
path: connectionsEndpoints.localOAuth(connectorId, capability),
|
|
31487
|
+
options
|
|
31488
|
+
});
|
|
31489
|
+
return LocalOAuthSetupSchema.parse(data);
|
|
31490
|
+
}
|
|
31491
|
+
/**
|
|
31492
|
+
* Stores a grant the CLI obtained with a loopback OAuth client, for a
|
|
31493
|
+
* connector whose authorization server admits no hosted clients
|
|
31494
|
+
* (`connectsViaCli`). The connection comes back already authorized.
|
|
31495
|
+
*/
|
|
31496
|
+
async importLocalOAuth(args, options) {
|
|
31497
|
+
const { connectorId, capability } = CreateConnectionParamsSchema.parse({
|
|
31498
|
+
connectorId: IdSchema.parse(args.connectorId),
|
|
31499
|
+
capability: args.capability
|
|
31500
|
+
});
|
|
31501
|
+
const body = ImportLocalOAuthConnectionBodySchema.parse(args.grant);
|
|
31502
|
+
const data = await this.request({
|
|
31503
|
+
method: "POST",
|
|
31504
|
+
path: connectionsEndpoints.localOAuth(connectorId, capability),
|
|
31505
|
+
body,
|
|
31506
|
+
options
|
|
31507
|
+
});
|
|
31508
|
+
return CreateConnectionResponseSchema.parse(data);
|
|
31509
|
+
}
|
|
31408
31510
|
async checkManifest(manifest, options) {
|
|
31409
31511
|
const parsedManifest = partialManifestSchema.parse(manifest);
|
|
31410
31512
|
const data = await this.request({
|
|
@@ -35815,6 +35917,13 @@ async function* streamConnectorNdjsonItems(api, options, signal) {
|
|
|
35815
35917
|
}
|
|
35816
35918
|
yield { _meta: true, connectionsIncluded: options.connections };
|
|
35817
35919
|
}
|
|
35920
|
+
async function findListedConnector(api, connectorId, signal) {
|
|
35921
|
+
for await (const page of iterateConnectorPages(api, void 0, { signal })) {
|
|
35922
|
+
const connector = page.data.find((candidate) => candidate.id === connectorId);
|
|
35923
|
+
if (connector) return connector;
|
|
35924
|
+
}
|
|
35925
|
+
return void 0;
|
|
35926
|
+
}
|
|
35818
35927
|
function findConnector2(api, connectorId, signal) {
|
|
35819
35928
|
return findConnector(api, connectorId, { signal });
|
|
35820
35929
|
}
|
|
@@ -35842,6 +35951,8 @@ async function runAuthorizationCommand(command, operation) {
|
|
|
35842
35951
|
return operation({
|
|
35843
35952
|
api,
|
|
35844
35953
|
signal,
|
|
35954
|
+
output: output2,
|
|
35955
|
+
openBrowser: options.open,
|
|
35845
35956
|
authorizer: new ApiConnectionAuthorizer({
|
|
35846
35957
|
connections: api.connections,
|
|
35847
35958
|
output: output2,
|
|
@@ -35931,17 +36042,172 @@ function summarizeAffectedAutomation(automation) {
|
|
|
35931
36042
|
};
|
|
35932
36043
|
}
|
|
35933
36044
|
|
|
36045
|
+
// src/commands/connector/local-oauth-connect.ts
|
|
36046
|
+
import { createDcrProvider, runOAuthFlow } from "@doist/cli-core/auth";
|
|
36047
|
+
var PREFERRED_CALLBACK_PORT = 24369;
|
|
36048
|
+
async function connectViaLocalOAuth({
|
|
36049
|
+
api,
|
|
36050
|
+
connector,
|
|
36051
|
+
capability,
|
|
36052
|
+
repairConnectionId,
|
|
36053
|
+
output: output2,
|
|
36054
|
+
openBrowser,
|
|
36055
|
+
signal
|
|
36056
|
+
}) {
|
|
36057
|
+
const setup = await api.connections.getLocalOAuthSetup(
|
|
36058
|
+
{ connectorId: connector.id, capability },
|
|
36059
|
+
{ signal }
|
|
36060
|
+
);
|
|
36061
|
+
const { registrationEndpoint } = setup;
|
|
36062
|
+
if (!registrationEndpoint) {
|
|
36063
|
+
throw new Error(
|
|
36064
|
+
`${connector.name} does not support dynamic client registration, so the CLI cannot connect it.`
|
|
36065
|
+
);
|
|
36066
|
+
}
|
|
36067
|
+
const grant = await runLoopbackGrant({
|
|
36068
|
+
connector,
|
|
36069
|
+
capability,
|
|
36070
|
+
setup,
|
|
36071
|
+
registrationEndpoint,
|
|
36072
|
+
output: output2,
|
|
36073
|
+
openBrowser,
|
|
36074
|
+
signal
|
|
36075
|
+
});
|
|
36076
|
+
const created = await api.connections.importLocalOAuth(
|
|
36077
|
+
{
|
|
36078
|
+
connectorId: connector.id,
|
|
36079
|
+
capability,
|
|
36080
|
+
grant: {
|
|
36081
|
+
clientId: grant.account.clientId,
|
|
36082
|
+
accessToken: grant.bundle.accessToken,
|
|
36083
|
+
refreshToken: grant.bundle.refreshToken,
|
|
36084
|
+
expiresIn: expiresInSeconds(grant.bundle.accessTokenExpiresAt),
|
|
36085
|
+
...repairConnectionId ? { repairConnectionId } : {}
|
|
36086
|
+
}
|
|
36087
|
+
},
|
|
36088
|
+
{ signal }
|
|
36089
|
+
);
|
|
36090
|
+
output2.emit({
|
|
36091
|
+
type: "connection_authorized",
|
|
36092
|
+
connectionId: created.connectionId,
|
|
36093
|
+
connectorId: connector.id,
|
|
36094
|
+
serviceName: connector.name,
|
|
36095
|
+
connectionType: capability,
|
|
36096
|
+
state: "completed",
|
|
36097
|
+
terminal: false
|
|
36098
|
+
});
|
|
36099
|
+
return created.connectionId;
|
|
36100
|
+
}
|
|
36101
|
+
async function runLoopbackGrant({
|
|
36102
|
+
connector,
|
|
36103
|
+
capability,
|
|
36104
|
+
setup,
|
|
36105
|
+
registrationEndpoint,
|
|
36106
|
+
output: output2,
|
|
36107
|
+
openBrowser,
|
|
36108
|
+
signal
|
|
36109
|
+
}) {
|
|
36110
|
+
const retryHint = `Try again: ${connectViaCliCommand(connector.id, capability)}`;
|
|
36111
|
+
const provider = createDcrProvider({
|
|
36112
|
+
registrationUrl: registrationEndpoint,
|
|
36113
|
+
authorizeUrl: setup.authorizationEndpoint,
|
|
36114
|
+
tokenUrl: setup.tokenEndpoint,
|
|
36115
|
+
resource: setup.resource ?? void 0,
|
|
36116
|
+
clientMetadata: {
|
|
36117
|
+
clientName: AUTOMATIONS_CLI_CLIENT_NAME,
|
|
36118
|
+
logoUri: AUTOMATIONS_CLI_LOGO_URL,
|
|
36119
|
+
applicationType: "native",
|
|
36120
|
+
// The backend refreshes with the client id alone, so the client
|
|
36121
|
+
// must be public: no secret to copy across.
|
|
36122
|
+
tokenEndpointAuthMethod: "none",
|
|
36123
|
+
grantTypes: ["authorization_code", "refresh_token"]
|
|
36124
|
+
},
|
|
36125
|
+
// The grant is handed to the backend as-is; here the "account" only
|
|
36126
|
+
// carries the client id the server bound it to.
|
|
36127
|
+
validate: async ({ handshake }) => ({
|
|
36128
|
+
id: connector.id,
|
|
36129
|
+
label: connector.name,
|
|
36130
|
+
clientId: requireClientId(handshake)
|
|
36131
|
+
}),
|
|
36132
|
+
errorHints: [retryHint]
|
|
36133
|
+
});
|
|
36134
|
+
let captured;
|
|
36135
|
+
const store2 = {
|
|
36136
|
+
active: async () => null,
|
|
36137
|
+
set: async () => {
|
|
36138
|
+
},
|
|
36139
|
+
setBundle: async (account, bundle) => {
|
|
36140
|
+
captured = { account, bundle };
|
|
36141
|
+
},
|
|
36142
|
+
clear: async () => null,
|
|
36143
|
+
list: async () => [],
|
|
36144
|
+
setDefault: async () => {
|
|
36145
|
+
}
|
|
36146
|
+
};
|
|
36147
|
+
await runOAuthFlow({
|
|
36148
|
+
provider,
|
|
36149
|
+
store: store2,
|
|
36150
|
+
scopes: setup.scopes,
|
|
36151
|
+
readOnly: false,
|
|
36152
|
+
flags: {},
|
|
36153
|
+
preferredPort: PREFERRED_CALLBACK_PORT,
|
|
36154
|
+
renderSuccess: renderAuthSuccessPage,
|
|
36155
|
+
renderError: renderAuthErrorPage,
|
|
36156
|
+
...openBrowser ? {} : { openBrowser: async () => {
|
|
36157
|
+
} },
|
|
36158
|
+
onAuthorizeUrl: (url2) => {
|
|
36159
|
+
output2.emit({
|
|
36160
|
+
type: "connection_authorization_started",
|
|
36161
|
+
connectorId: connector.id,
|
|
36162
|
+
serviceName: connector.name,
|
|
36163
|
+
connectionType: capability,
|
|
36164
|
+
state: "waiting_for_user",
|
|
36165
|
+
terminal: false,
|
|
36166
|
+
setupKind: "oauth",
|
|
36167
|
+
authorizationUrl: url2
|
|
36168
|
+
});
|
|
36169
|
+
},
|
|
36170
|
+
timeoutMs: CONNECTION_AUTHORIZATION_TIMEOUT_MS,
|
|
36171
|
+
signal
|
|
36172
|
+
});
|
|
36173
|
+
if (!captured) {
|
|
36174
|
+
throw new Error(`${connector.name} sign-in finished without a token. ${retryHint}`);
|
|
36175
|
+
}
|
|
36176
|
+
return captured;
|
|
36177
|
+
}
|
|
36178
|
+
function requireClientId(handshake) {
|
|
36179
|
+
const clientId = handshake.clientId;
|
|
36180
|
+
if (typeof clientId !== "string" || clientId.length === 0) {
|
|
36181
|
+
throw new Error("The authorization server did not return a client id.");
|
|
36182
|
+
}
|
|
36183
|
+
return clientId;
|
|
36184
|
+
}
|
|
36185
|
+
function expiresInSeconds(expiresAt) {
|
|
36186
|
+
if (expiresAt === void 0) return void 0;
|
|
36187
|
+
const seconds = Math.floor((expiresAt - Date.now()) / 1e3);
|
|
36188
|
+
return seconds > 0 ? seconds : void 0;
|
|
36189
|
+
}
|
|
36190
|
+
|
|
35934
36191
|
// src/commands/connector/mutation-commands.ts
|
|
35935
36192
|
function registerConnectorMutationCommands(connector) {
|
|
35936
36193
|
connector.command("connect").description("Create a service connection and wait for authorization").argument("<connector-id>", "Connector ID").addOption(
|
|
35937
36194
|
new Option4("--capability <capability>", "Capability to authorize").choices(["mcp", "trigger"]).makeOptionMandatory()
|
|
35938
36195
|
).action(async (connectorId, options, command) => {
|
|
35939
|
-
await runAuthorizationCommand(command, async (
|
|
36196
|
+
await runAuthorizationCommand(command, async (context) => {
|
|
36197
|
+
const { api, authorizer, signal } = context;
|
|
35940
36198
|
const selected2 = await findConnector2(api, connectorId, signal);
|
|
35941
36199
|
assertConnectorSupportsCapability(selected2, options.capability);
|
|
35942
36200
|
if (!selected2.requiresConnection) {
|
|
35943
36201
|
throw new Error(`${selected2.name} does not require a connection.`);
|
|
35944
36202
|
}
|
|
36203
|
+
if (selected2.connectsViaCli) {
|
|
36204
|
+
const connectionId2 = await connectViaLocalOAuth({
|
|
36205
|
+
...context,
|
|
36206
|
+
connector: selected2,
|
|
36207
|
+
capability: options.capability
|
|
36208
|
+
});
|
|
36209
|
+
return { action: "connected", connectionId: connectionId2 };
|
|
36210
|
+
}
|
|
35945
36211
|
const connectionId = await authorizer.authorize(
|
|
35946
36212
|
{
|
|
35947
36213
|
connectorId: selected2.id,
|
|
@@ -35978,7 +36244,8 @@ function registerConnectorMutationCommands(connector) {
|
|
|
35978
36244
|
});
|
|
35979
36245
|
});
|
|
35980
36246
|
connector.command("repair").description("Start a replacement setup for a broken or expired connection").argument("<connection-id>", "Connection ID").action(async (connectionId, _options, command) => {
|
|
35981
|
-
await runAuthorizationCommand(command, async (
|
|
36247
|
+
await runAuthorizationCommand(command, async (context) => {
|
|
36248
|
+
const { api, authorizer, signal } = context;
|
|
35982
36249
|
const connection = await api.connections.get(connectionId, { signal });
|
|
35983
36250
|
if (connection.status === "authorized") {
|
|
35984
36251
|
throw new Error(`Connection ${connectionId} is already authorized.`);
|
|
@@ -35992,7 +36259,19 @@ function registerConnectorMutationCommands(connector) {
|
|
|
35992
36259
|
await authorizer.resume(request2, connection.id, connection.setup, signal);
|
|
35993
36260
|
return { action: "connected", connectionId };
|
|
35994
36261
|
}
|
|
35995
|
-
const affectedAutomations = await
|
|
36262
|
+
const [selected2, affectedAutomations] = await Promise.all([
|
|
36263
|
+
findListedConnector(api, connection.connectorId, signal),
|
|
36264
|
+
findAffectedAutomations(api, connectionId, signal)
|
|
36265
|
+
]);
|
|
36266
|
+
if (selected2?.connectsViaCli) {
|
|
36267
|
+
const repairedConnectionId2 = await connectViaLocalOAuth({
|
|
36268
|
+
...context,
|
|
36269
|
+
connector: selected2,
|
|
36270
|
+
capability: request2.connectionType,
|
|
36271
|
+
repairConnectionId: connection.id
|
|
36272
|
+
});
|
|
36273
|
+
return { action: "connected", connectionId: repairedConnectionId2 };
|
|
36274
|
+
}
|
|
35996
36275
|
const repairedConnectionId = await authorizer.authorize(
|
|
35997
36276
|
{
|
|
35998
36277
|
...request2,
|