@doist/automations-cli 1.20.3 → 1.21.1
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
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
collectSkillStatus,
|
|
8
8
|
getInstaller,
|
|
9
9
|
updateAllInstalledSkills
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZWFZ2DD3.js";
|
|
11
11
|
|
|
12
12
|
// ../../node_modules/ajv/dist/compile/codegen/code.js
|
|
13
13
|
var require_code = __commonJS({
|
|
@@ -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.21.1",
|
|
6903
6903
|
description: "Command-line client for Todoist Automations",
|
|
6904
6904
|
type: "module",
|
|
6905
6905
|
bin: {
|
|
@@ -6944,7 +6944,7 @@ var package_default = {
|
|
|
6944
6944
|
},
|
|
6945
6945
|
dependencies: {
|
|
6946
6946
|
"@doist/cli-core": "1.7.0",
|
|
6947
|
-
"@napi-rs/keyring": "2.1.
|
|
6947
|
+
"@napi-rs/keyring": "2.1.1",
|
|
6948
6948
|
ai: "7.0.137",
|
|
6949
6949
|
commander: "15.0.0",
|
|
6950
6950
|
oauth4webapi: "3.8.8",
|
|
@@ -28149,6 +28149,7 @@ var cronScheduleSchema = (() => {
|
|
|
28149
28149
|
});
|
|
28150
28150
|
})();
|
|
28151
28151
|
var scheduleValueSchema = external_exports.union([intervalScheduleSchema, cronScheduleSchema]).meta({ description: "The schedule definition for the trigger." });
|
|
28152
|
+
var TRIGGER_TYPES = ["schedule", "webhook", "api"];
|
|
28152
28153
|
var scheduleTriggerSchema = external_exports.object({
|
|
28153
28154
|
type: external_exports.literal("schedule"),
|
|
28154
28155
|
value: scheduleValueSchema,
|
|
@@ -29326,8 +29327,29 @@ var automationDraftListResponseSchema = external_exports.object({
|
|
|
29326
29327
|
var finalizeAutomationDraftResponseSchema = external_exports.object({
|
|
29327
29328
|
automation: automationSchema
|
|
29328
29329
|
});
|
|
29330
|
+
var draftTestTriggerSelectorSchema = external_exports.union([
|
|
29331
|
+
external_exports.object({ index: external_exports.int().min(0) }),
|
|
29332
|
+
external_exports.object({ event: external_exports.string().min(1) })
|
|
29333
|
+
]);
|
|
29334
|
+
var testAutomationDraftRequestSchema = external_exports.object({
|
|
29335
|
+
trigger: draftTestTriggerSelectorSchema.optional(),
|
|
29336
|
+
payload: external_exports.unknown().optional(),
|
|
29337
|
+
metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
29338
|
+
}).meta({ id: "TestAutomationDraftRequest" });
|
|
29339
|
+
var draftTestTriggerSchema = external_exports.object({
|
|
29340
|
+
index: external_exports.int().min(0),
|
|
29341
|
+
count: external_exports.int().min(1),
|
|
29342
|
+
type: external_exports.enum(TRIGGER_TYPES),
|
|
29343
|
+
event: external_exports.string().optional(),
|
|
29344
|
+
inputSource: external_exports.enum(["schedule", "sample-event", "supplied", "empty"])
|
|
29345
|
+
});
|
|
29329
29346
|
var testAutomationDraftResponseSchema = external_exports.object({
|
|
29330
|
-
success: external_exports.literal(true)
|
|
29347
|
+
success: external_exports.literal(true),
|
|
29348
|
+
draftId: external_exports.string(),
|
|
29349
|
+
testRunId: external_exports.string(),
|
|
29350
|
+
status: external_exports.enum(AUTOMATION_RUN_STATUSES),
|
|
29351
|
+
connectorCalls: external_exports.literal("mocked"),
|
|
29352
|
+
trigger: draftTestTriggerSchema
|
|
29331
29353
|
});
|
|
29332
29354
|
var AUTOMATION_OPEN_DRAFT_CONFLICT_CODE = "automation_open_draft_conflict";
|
|
29333
29355
|
var AutomationOpenDraftConflictSchema = external_exports.object({
|
|
@@ -29340,6 +29362,18 @@ var AutomationDraftNotReadySchema = external_exports.object({
|
|
|
29340
29362
|
code: external_exports.literal(AUTOMATION_DRAFT_NOT_READY_CODE),
|
|
29341
29363
|
message: external_exports.string()
|
|
29342
29364
|
});
|
|
29365
|
+
var DRAFT_TEST_REQUEST_ERROR_CODES = [
|
|
29366
|
+
"draft_test_not_ready",
|
|
29367
|
+
"draft_test_trigger_not_found",
|
|
29368
|
+
"draft_test_payload_required",
|
|
29369
|
+
"draft_test_payload_filtered",
|
|
29370
|
+
"draft_test_payload_invalid",
|
|
29371
|
+
"draft_test_payload_unsupported"
|
|
29372
|
+
];
|
|
29373
|
+
var DraftTestRequestErrorSchema = external_exports.object({
|
|
29374
|
+
code: external_exports.enum(DRAFT_TEST_REQUEST_ERROR_CODES),
|
|
29375
|
+
message: external_exports.string()
|
|
29376
|
+
});
|
|
29343
29377
|
|
|
29344
29378
|
// ../../packages/core/dist/esm/schemas/automation-draft-connection.schema.js
|
|
29345
29379
|
var CreateAutomationDraftConnectionSchema = external_exports.object({
|
|
@@ -29651,10 +29685,23 @@ var automationTestRunStepStatusSchema = external_exports.object({
|
|
|
29651
29685
|
stepId: external_exports.string().min(1),
|
|
29652
29686
|
status: external_exports.enum(AUTOMATION_RUN_STATUSES)
|
|
29653
29687
|
});
|
|
29688
|
+
var automationTestRunFailedStepSchema = external_exports.object({
|
|
29689
|
+
stepId: external_exports.string().min(1),
|
|
29690
|
+
description: external_exports.string().optional(),
|
|
29691
|
+
error: external_exports.string().optional()
|
|
29692
|
+
});
|
|
29654
29693
|
var automationTestRunProgressSchema = external_exports.object({
|
|
29655
29694
|
draftId: external_exports.string().min(1),
|
|
29656
29695
|
status: external_exports.enum(AUTOMATION_RUN_STATUSES),
|
|
29657
|
-
steps: external_exports.array(automationTestRunStepStatusSchema).default([])
|
|
29696
|
+
steps: external_exports.array(automationTestRunStepStatusSchema).default([]),
|
|
29697
|
+
// Optional so results stored before these fields existed still parse.
|
|
29698
|
+
runId: external_exports.string().min(1).optional(),
|
|
29699
|
+
failedStep: automationTestRunFailedStepSchema.optional(),
|
|
29700
|
+
/** Why a failed or skipped run stopped. */
|
|
29701
|
+
error: external_exports.string().optional(),
|
|
29702
|
+
result: AutomationRunResultSchema.nullable().optional(),
|
|
29703
|
+
/** A test run simulates every connector call, reads included. */
|
|
29704
|
+
connectorCalls: external_exports.literal("mocked").optional()
|
|
29658
29705
|
});
|
|
29659
29706
|
var automationTestRunPreviewTriggerSchema = external_exports.union([
|
|
29660
29707
|
external_exports.object({
|
|
@@ -29947,6 +29994,47 @@ var cohortMatrixResponseSchema = external_exports.object({
|
|
|
29947
29994
|
rows: external_exports.array(cohortMatrixRowSchema)
|
|
29948
29995
|
});
|
|
29949
29996
|
|
|
29997
|
+
// ../../packages/core/dist/esm/schemas/draft-test-run.schema.js
|
|
29998
|
+
var DraftTestRunFailedStepSchema = external_exports.object({
|
|
29999
|
+
stepId: external_exports.string(),
|
|
30000
|
+
description: external_exports.string(),
|
|
30001
|
+
error: external_exports.string().nullable()
|
|
30002
|
+
});
|
|
30003
|
+
var draftTestRunDetailDtoBaseShape = {
|
|
30004
|
+
id: external_exports.string(),
|
|
30005
|
+
draftId: external_exports.string(),
|
|
30006
|
+
// The automation an edit draft belongs to; null for a draft of a new automation.
|
|
30007
|
+
automationId: external_exports.string().nullable(),
|
|
30008
|
+
mode: external_exports.literal("test"),
|
|
30009
|
+
// A test run simulates every connector call, reads included, with a model.
|
|
30010
|
+
connectorCalls: external_exports.literal("mocked"),
|
|
30011
|
+
createdAt: external_exports.string(),
|
|
30012
|
+
updatedAt: external_exports.string(),
|
|
30013
|
+
result: AutomationRunResultSchema.nullable(),
|
|
30014
|
+
temporalUrl: external_exports.string().nullish(),
|
|
30015
|
+
steps: external_exports.array(AutomationRunStepDtoSchema),
|
|
30016
|
+
failedStep: DraftTestRunFailedStepSchema.nullable()
|
|
30017
|
+
};
|
|
30018
|
+
var DraftTestRunDetailDtoSchema = external_exports.discriminatedUnion("status", [
|
|
30019
|
+
external_exports.object({ ...draftTestRunDetailDtoBaseShape, status: external_exports.literal("pending") }),
|
|
30020
|
+
external_exports.object({ ...draftTestRunDetailDtoBaseShape, status: external_exports.literal("running") }),
|
|
30021
|
+
external_exports.object({ ...draftTestRunDetailDtoBaseShape, status: external_exports.literal("completed") }),
|
|
30022
|
+
external_exports.object({ ...draftTestRunDetailDtoBaseShape, status: external_exports.literal("cancelled") }),
|
|
30023
|
+
external_exports.object({ ...draftTestRunDetailDtoBaseShape, status: external_exports.literal("data-expired") }),
|
|
30024
|
+
external_exports.object({
|
|
30025
|
+
...draftTestRunDetailDtoBaseShape,
|
|
30026
|
+
status: external_exports.literal("failed"),
|
|
30027
|
+
failureReason: external_exports.string().nullish(),
|
|
30028
|
+
failureCategory: external_exports.enum(FAILURE_CATEGORIES).nullish().catch(null)
|
|
30029
|
+
}),
|
|
30030
|
+
external_exports.object({
|
|
30031
|
+
...draftTestRunDetailDtoBaseShape,
|
|
30032
|
+
status: external_exports.literal("skipped"),
|
|
30033
|
+
skipReason: external_exports.string().nullish(),
|
|
30034
|
+
skipCategory: external_exports.enum(SKIP_CATEGORIES).nullish()
|
|
30035
|
+
})
|
|
30036
|
+
]);
|
|
30037
|
+
|
|
29950
30038
|
// ../../packages/core/dist/esm/types/workspace.types.js
|
|
29951
30039
|
var WORKSPACE_PLANS = ["STARTER", "BUSINESS"];
|
|
29952
30040
|
var WORKSPACE_ROLES = ["ADMIN", "MEMBER", "GUEST"];
|
|
@@ -31117,7 +31205,8 @@ var automationDraftsEndpoints = {
|
|
|
31117
31205
|
list: root3,
|
|
31118
31206
|
detail: (id) => `${root3}/${encodeURIComponent(id)}`,
|
|
31119
31207
|
finalize: (id) => `${root3}/${encodeURIComponent(id)}/finalize`,
|
|
31120
|
-
test: (id) => `${root3}/${encodeURIComponent(id)}/test
|
|
31208
|
+
test: (id) => `${root3}/${encodeURIComponent(id)}/test`,
|
|
31209
|
+
testRun: (id, runId) => `${root3}/${encodeURIComponent(id)}/test-runs/${encodeURIComponent(runId)}`
|
|
31121
31210
|
};
|
|
31122
31211
|
|
|
31123
31212
|
// ../../packages/sdk/dist/esm/clients/automation-drafts/automation-drafts-client.js
|
|
@@ -31131,6 +31220,16 @@ function getDraftNotReadyError(error62) {
|
|
|
31131
31220
|
const parsed = AutomationDraftNotReadySchema.safeParse(error62.responseData);
|
|
31132
31221
|
return parsed.success ? parsed.data : null;
|
|
31133
31222
|
}
|
|
31223
|
+
function getDraftTestRequestError(error62) {
|
|
31224
|
+
if (!(error62 instanceof AutomationsRequestError) || error62.httpStatusCode !== 400) {
|
|
31225
|
+
return null;
|
|
31226
|
+
}
|
|
31227
|
+
const parsed = DraftTestRequestErrorSchema.safeParse(error62.responseData);
|
|
31228
|
+
return parsed.success ? parsed.data : null;
|
|
31229
|
+
}
|
|
31230
|
+
function isRequestOptions(value) {
|
|
31231
|
+
return Boolean(value && ("signal" in value || "headers" in value));
|
|
31232
|
+
}
|
|
31134
31233
|
var AutomationDraftsClient = class extends BaseClient {
|
|
31135
31234
|
async list(args = {}, options) {
|
|
31136
31235
|
const { cursor, limit } = CursorPaginationSchema.parse(args);
|
|
@@ -31191,16 +31290,35 @@ var AutomationDraftsClient = class extends BaseClient {
|
|
|
31191
31290
|
});
|
|
31192
31291
|
return finalizeAutomationDraftResponseSchema.parse(data);
|
|
31193
31292
|
}
|
|
31194
|
-
|
|
31293
|
+
/** Starts a test run; `request` picks the trigger and supplies the event to test with. */
|
|
31294
|
+
async test(draftId, requestOrOptions, options) {
|
|
31195
31295
|
const id = IdSchema.parse(draftId);
|
|
31296
|
+
const request2 = isRequestOptions(requestOrOptions) ? void 0 : requestOrOptions;
|
|
31196
31297
|
const data = await this.request({
|
|
31197
31298
|
method: "POST",
|
|
31198
31299
|
path: automationDraftsEndpoints.test(id),
|
|
31199
|
-
|
|
31300
|
+
body: request2 ? testAutomationDraftRequestSchema.parse(request2) : void 0,
|
|
31301
|
+
options: isRequestOptions(requestOrOptions) ? requestOrOptions : options
|
|
31200
31302
|
});
|
|
31201
31303
|
return testAutomationDraftResponseSchema.parse(data);
|
|
31202
31304
|
}
|
|
31305
|
+
/** A test run of this draft, with its steps; `args` match the run detail's. */
|
|
31306
|
+
async getTestRun(draftId, runId, args = {}, options) {
|
|
31307
|
+
const data = await this.request({
|
|
31308
|
+
method: "GET",
|
|
31309
|
+
path: automationDraftsEndpoints.testRun(IdSchema.parse(draftId), IdSchema.parse(runId)),
|
|
31310
|
+
query: toTestRunQuery(args),
|
|
31311
|
+
options
|
|
31312
|
+
});
|
|
31313
|
+
return DraftTestRunDetailDtoSchema.parse(data);
|
|
31314
|
+
}
|
|
31203
31315
|
};
|
|
31316
|
+
function toTestRunQuery(args) {
|
|
31317
|
+
return {
|
|
31318
|
+
includeGeneratedCode: args.includeGeneratedCode ? true : void 0,
|
|
31319
|
+
includeToolCallPreviews: args.includeToolCallPreviews
|
|
31320
|
+
};
|
|
31321
|
+
}
|
|
31204
31322
|
|
|
31205
31323
|
// ../../packages/sdk/dist/esm/clients/automation-runs/endpoints.js
|
|
31206
31324
|
var automationRunsEndpoints = {
|
|
@@ -31209,7 +31327,7 @@ var automationRunsEndpoints = {
|
|
|
31209
31327
|
};
|
|
31210
31328
|
|
|
31211
31329
|
// ../../packages/sdk/dist/esm/clients/automation-runs/automation-runs-client.js
|
|
31212
|
-
function
|
|
31330
|
+
function isRequestOptions2(value) {
|
|
31213
31331
|
return Boolean(value && ("signal" in value || "headers" in value));
|
|
31214
31332
|
}
|
|
31215
31333
|
var AutomationRunsClient = class extends BaseClient {
|
|
@@ -31228,8 +31346,8 @@ var AutomationRunsClient = class extends BaseClient {
|
|
|
31228
31346
|
async get(automationId, runId, argsOrOptions, options) {
|
|
31229
31347
|
const aId = IdSchema.parse(automationId);
|
|
31230
31348
|
const rId = IdSchema.parse(runId);
|
|
31231
|
-
const args =
|
|
31232
|
-
const requestOptions =
|
|
31349
|
+
const args = isRequestOptions2(argsOrOptions) ? void 0 : argsOrOptions;
|
|
31350
|
+
const requestOptions = isRequestOptions2(argsOrOptions) ? argsOrOptions : options;
|
|
31233
31351
|
const data = await this.request({
|
|
31234
31352
|
method: "GET",
|
|
31235
31353
|
path: automationRunsEndpoints.detail(aId, rId),
|
|
@@ -31488,7 +31606,9 @@ var WorkspaceAutomationDraftConnectionsClient = class extends BaseClient {
|
|
|
31488
31606
|
var workspaceAutomationDraftsEndpoints = {
|
|
31489
31607
|
list: (workspaceId) => `${root7(workspaceId)}`,
|
|
31490
31608
|
detail: (workspaceId, id) => `${root7(workspaceId)}/${encodeURIComponent(id)}`,
|
|
31491
|
-
finalize: (workspaceId, id) => `${root7(workspaceId)}/${encodeURIComponent(id)}/finalize
|
|
31609
|
+
finalize: (workspaceId, id) => `${root7(workspaceId)}/${encodeURIComponent(id)}/finalize`,
|
|
31610
|
+
test: (workspaceId, id) => `${root7(workspaceId)}/${encodeURIComponent(id)}/test`,
|
|
31611
|
+
testRun: (workspaceId, id, runId) => `${root7(workspaceId)}/${encodeURIComponent(id)}/test-runs/${encodeURIComponent(runId)}`
|
|
31492
31612
|
};
|
|
31493
31613
|
function root7(workspaceId) {
|
|
31494
31614
|
return `/workspaces/${encodeURIComponent(workspaceId)}/automation-drafts`;
|
|
@@ -31554,6 +31674,24 @@ var WorkspaceAutomationDraftsClient = class extends BaseClient {
|
|
|
31554
31674
|
});
|
|
31555
31675
|
return finalizeAutomationDraftResponseSchema.parse(data);
|
|
31556
31676
|
}
|
|
31677
|
+
async test(workspaceId, draftId, request2 = {}, options) {
|
|
31678
|
+
const data = await this.request({
|
|
31679
|
+
method: "POST",
|
|
31680
|
+
path: workspaceAutomationDraftsEndpoints.test(IdSchema.parse(workspaceId), IdSchema.parse(draftId)),
|
|
31681
|
+
body: testAutomationDraftRequestSchema.parse(request2),
|
|
31682
|
+
options
|
|
31683
|
+
});
|
|
31684
|
+
return testAutomationDraftResponseSchema.parse(data);
|
|
31685
|
+
}
|
|
31686
|
+
async getTestRun(workspaceId, draftId, runId, args = {}, options) {
|
|
31687
|
+
const data = await this.request({
|
|
31688
|
+
method: "GET",
|
|
31689
|
+
path: workspaceAutomationDraftsEndpoints.testRun(IdSchema.parse(workspaceId), IdSchema.parse(draftId), IdSchema.parse(runId)),
|
|
31690
|
+
query: toTestRunQuery(args),
|
|
31691
|
+
options
|
|
31692
|
+
});
|
|
31693
|
+
return DraftTestRunDetailDtoSchema.parse(data);
|
|
31694
|
+
}
|
|
31557
31695
|
};
|
|
31558
31696
|
|
|
31559
31697
|
// ../../packages/sdk/dist/esm/clients/workspace-chat/endpoints.js
|
|
@@ -32497,25 +32635,47 @@ function summarizeRun(run) {
|
|
|
32497
32635
|
result: run.result,
|
|
32498
32636
|
temporalUrl: run.temporalUrl,
|
|
32499
32637
|
triggeredBy: run.triggeredBy,
|
|
32500
|
-
steps: run.steps
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
|
|
32514
|
-
|
|
32515
|
-
|
|
32516
|
-
|
|
32517
|
-
|
|
32638
|
+
steps: summarizeRunSteps(run.steps)
|
|
32639
|
+
};
|
|
32640
|
+
}
|
|
32641
|
+
function summarizeRunSteps(steps) {
|
|
32642
|
+
return steps.map((step) => ({
|
|
32643
|
+
stepId: step.stepId,
|
|
32644
|
+
type: step.type,
|
|
32645
|
+
description: step.description,
|
|
32646
|
+
status: step.status,
|
|
32647
|
+
startedAt: step.startedAt,
|
|
32648
|
+
completedAt: step.completedAt,
|
|
32649
|
+
error: step.error,
|
|
32650
|
+
toolCalls: step.toolCalls?.map((toolCall) => ({
|
|
32651
|
+
id: toolCall.id,
|
|
32652
|
+
name: toolCall.name,
|
|
32653
|
+
argumentKeys: toolCall.argumentKeys,
|
|
32654
|
+
hasResult: toolCall.hasResult,
|
|
32655
|
+
hasError: toolCall.hasError,
|
|
32656
|
+
resultPreview: toolCall.resultPreview,
|
|
32657
|
+
errorPreview: toolCall.errorPreview,
|
|
32658
|
+
timestamp: toolCall.timestamp
|
|
32518
32659
|
}))
|
|
32660
|
+
}));
|
|
32661
|
+
}
|
|
32662
|
+
|
|
32663
|
+
// ../../packages/sdk/dist/esm/summaries/draft-test-run-summary.js
|
|
32664
|
+
function summarizeDraftTestRun(run) {
|
|
32665
|
+
return {
|
|
32666
|
+
id: run.id,
|
|
32667
|
+
draftId: run.draftId,
|
|
32668
|
+
automationId: run.automationId,
|
|
32669
|
+
status: run.status,
|
|
32670
|
+
connectorCalls: run.connectorCalls,
|
|
32671
|
+
createdAt: run.createdAt,
|
|
32672
|
+
updatedAt: run.updatedAt,
|
|
32673
|
+
failureCategory: getRunOutcomeCategory(run),
|
|
32674
|
+
failureReason: getRunOutcomeReason(run),
|
|
32675
|
+
failedStep: run.failedStep,
|
|
32676
|
+
result: run.result,
|
|
32677
|
+
temporalUrl: run.temporalUrl,
|
|
32678
|
+
steps: summarizeRunSteps(run.steps)
|
|
32519
32679
|
};
|
|
32520
32680
|
}
|
|
32521
32681
|
|
|
@@ -32712,6 +32872,313 @@ async function findAutomationsUsingConnection(api, connectionId, options) {
|
|
|
32712
32872
|
}
|
|
32713
32873
|
}
|
|
32714
32874
|
|
|
32875
|
+
// ../../packages/sdk/dist/esm/drafts/draft-api.js
|
|
32876
|
+
function bindDraftApi(api, workspaceId, { signal } = {}) {
|
|
32877
|
+
const createOptions = { signal, translate: false };
|
|
32878
|
+
const listConnections = (args) => api.connections.list(args, { signal });
|
|
32879
|
+
if (workspaceId === null) {
|
|
32880
|
+
return {
|
|
32881
|
+
workspaceId: null,
|
|
32882
|
+
get: (draftId) => api.automationDrafts.get(draftId, { signal }),
|
|
32883
|
+
create: (manifest) => api.automationDrafts.create(manifest, createOptions),
|
|
32884
|
+
update: (draftId, manifest) => api.automationDrafts.update(draftId, manifest, { signal }),
|
|
32885
|
+
createAutomationDraft: (automationId) => api.automations.createDraft(automationId, { signal }),
|
|
32886
|
+
upsertConnection: (draftId, body) => api.automationDraftConnections.upsert(draftId, body, { signal }),
|
|
32887
|
+
getConnectionStatus: async (draftId) => (await api.automationDraftConnections.getStatus(draftId, { signal })).validation,
|
|
32888
|
+
listConnections,
|
|
32889
|
+
finalize: (draftId) => api.automationDrafts.finalize(draftId, { signal }),
|
|
32890
|
+
getAutomation: (automationId) => api.automations.get(automationId, { signal }),
|
|
32891
|
+
test: (draftId, request2) => api.automationDrafts.test(draftId, request2, { signal }),
|
|
32892
|
+
getTestRun: (draftId, runId, args, requestSignal) => api.automationDrafts.getTestRun(draftId, runId, args, {
|
|
32893
|
+
signal: requestSignal ?? signal
|
|
32894
|
+
})
|
|
32895
|
+
};
|
|
32896
|
+
}
|
|
32897
|
+
return {
|
|
32898
|
+
workspaceId,
|
|
32899
|
+
get: (draftId) => api.workspaceAutomationDrafts.get(workspaceId, draftId, { signal }),
|
|
32900
|
+
create: (manifest) => api.workspaceAutomationDrafts.create(workspaceId, manifest, createOptions),
|
|
32901
|
+
update: (draftId, manifest) => api.workspaceAutomationDrafts.update(workspaceId, draftId, manifest, { signal }),
|
|
32902
|
+
createAutomationDraft: (automationId) => api.workspaceAutomations.createDraft(workspaceId, automationId, { signal }),
|
|
32903
|
+
upsertConnection: (draftId, body) => api.workspaceAutomationDraftConnections.upsert(workspaceId, draftId, body, { signal }),
|
|
32904
|
+
getConnectionStatus: async (draftId) => (await api.workspaceAutomationDraftConnections.getStatus(workspaceId, draftId, {
|
|
32905
|
+
signal
|
|
32906
|
+
})).validation,
|
|
32907
|
+
listConnections,
|
|
32908
|
+
finalize: (draftId) => api.workspaceAutomationDrafts.finalize(workspaceId, draftId, { signal }),
|
|
32909
|
+
getAutomation: (automationId) => api.workspaceAutomations.get(workspaceId, automationId, { signal }),
|
|
32910
|
+
test: (draftId, request2) => api.workspaceAutomationDrafts.test(workspaceId, draftId, request2, { signal }),
|
|
32911
|
+
getTestRun: (draftId, runId, args, requestSignal) => api.workspaceAutomationDrafts.getTestRun(workspaceId, draftId, runId, args, {
|
|
32912
|
+
signal: requestSignal ?? signal
|
|
32913
|
+
})
|
|
32914
|
+
};
|
|
32915
|
+
}
|
|
32916
|
+
|
|
32917
|
+
// ../../packages/sdk/dist/esm/drafts/draft-workflow-error.js
|
|
32918
|
+
var DraftWorkflowError = class extends Error {
|
|
32919
|
+
code;
|
|
32920
|
+
draftId;
|
|
32921
|
+
automationId;
|
|
32922
|
+
constructor(code, message, options = {}) {
|
|
32923
|
+
super(message, { cause: options.cause });
|
|
32924
|
+
this.name = "DraftWorkflowError";
|
|
32925
|
+
this.code = code;
|
|
32926
|
+
this.draftId = options.draftId;
|
|
32927
|
+
this.automationId = options.automationId;
|
|
32928
|
+
}
|
|
32929
|
+
};
|
|
32930
|
+
function getErrorMessage(error62) {
|
|
32931
|
+
return error62 instanceof Error ? error62.message : String(error62);
|
|
32932
|
+
}
|
|
32933
|
+
|
|
32934
|
+
// ../../packages/sdk/dist/esm/drafts/draft-changes.js
|
|
32935
|
+
async function createDraft(draftApi, { manifest, automationId }) {
|
|
32936
|
+
const partialManifest = parsePartialManifest(manifest);
|
|
32937
|
+
if (automationId === void 0) {
|
|
32938
|
+
const result = await draftApi.create(partialManifest);
|
|
32939
|
+
return { action: "created", target: toDraftTarget(result.draft), ...result };
|
|
32940
|
+
}
|
|
32941
|
+
const { draft } = await openEditDraft(draftApi, automationId);
|
|
32942
|
+
try {
|
|
32943
|
+
const result = await draftApi.update(draft.id, partialManifest);
|
|
32944
|
+
return { action: "created", target: toDraftTarget(result.draft), ...result };
|
|
32945
|
+
} catch (error62) {
|
|
32946
|
+
if (!isManifestRejection(error62))
|
|
32947
|
+
throw error62;
|
|
32948
|
+
throw new DraftWorkflowError("draft_manifest_rejected", `Opened draft ${draft.id} for automation ${automationId}, but the manifest was rejected: ${getErrorMessage(error62)}`, { draftId: draft.id, automationId, cause: error62 });
|
|
32949
|
+
}
|
|
32950
|
+
}
|
|
32951
|
+
async function updateDraft(draftApi, { draftId, manifest }) {
|
|
32952
|
+
const partialManifest = parsePartialManifest(manifest);
|
|
32953
|
+
const result = await draftApi.update(draftId, partialManifest);
|
|
32954
|
+
return { action: "updated", target: toDraftTarget(result.draft), ...result };
|
|
32955
|
+
}
|
|
32956
|
+
function toDraftTarget(draft) {
|
|
32957
|
+
return draft.automationId === null ? { kind: "new" } : { kind: "automation", automationId: draft.automationId };
|
|
32958
|
+
}
|
|
32959
|
+
function parsePartialManifest(value) {
|
|
32960
|
+
const validation = validatePartialManifest(value);
|
|
32961
|
+
if (!validation.isValid) {
|
|
32962
|
+
throw new DraftWorkflowError("manifest_invalid", `Manifest is invalid: ${validation.errors.join("; ")}`);
|
|
32963
|
+
}
|
|
32964
|
+
return validation.data;
|
|
32965
|
+
}
|
|
32966
|
+
async function openEditDraft(draftApi, automationId) {
|
|
32967
|
+
try {
|
|
32968
|
+
return await draftApi.createAutomationDraft(automationId);
|
|
32969
|
+
} catch (error62) {
|
|
32970
|
+
const conflict = getOpenDraftConflictError(error62);
|
|
32971
|
+
if (!conflict)
|
|
32972
|
+
throw error62;
|
|
32973
|
+
throw new DraftWorkflowError("draft_conflict", `Automation ${automationId} already has an open draft ${conflict.draftId}.`, { draftId: conflict.draftId, automationId, cause: error62 });
|
|
32974
|
+
}
|
|
32975
|
+
}
|
|
32976
|
+
function isManifestRejection(error62) {
|
|
32977
|
+
return error62 instanceof AutomationsRequestError && (error62.httpStatusCode === 400 || error62.httpStatusCode === 422);
|
|
32978
|
+
}
|
|
32979
|
+
|
|
32980
|
+
// ../../packages/sdk/dist/esm/drafts/connect-draft.js
|
|
32981
|
+
async function connectDraft(draftApi, { draftId, selections, auto }) {
|
|
32982
|
+
const bound = [];
|
|
32983
|
+
const required2 = selections.length > 0 ? getRequiredConnectorsFromManifest((await draftApi.get(draftId)).draft.partialManifest) : /* @__PURE__ */ new Map();
|
|
32984
|
+
const explicit = selections.flatMap((selection) => {
|
|
32985
|
+
const types = required2.get(selection.connectorId);
|
|
32986
|
+
if (!types || selection.connectionType && !types.has(selection.connectionType)) {
|
|
32987
|
+
const slot = selection.connectionType ? `${selection.connectorId} (${selection.connectionType})` : selection.connectorId;
|
|
32988
|
+
throw new DraftWorkflowError("connector_not_required", `Connector ${slot} is not used by draft ${draftId}. Required: ${describeRequired(required2)}.`, { draftId });
|
|
32989
|
+
}
|
|
32990
|
+
const connectionTypes = selection.connectionType ? [selection.connectionType] : [...types];
|
|
32991
|
+
return connectionTypes.map((connectionType) => ({ ...selection, connectionType }));
|
|
32992
|
+
});
|
|
32993
|
+
for (const { connectorId, connectionType, connectionId } of explicit) {
|
|
32994
|
+
await draftApi.upsertConnection(draftId, { connectorId, connectionType, connectionId });
|
|
32995
|
+
bound.push({ connectorId, connectionType, connectionId, mode: "explicit" });
|
|
32996
|
+
}
|
|
32997
|
+
const unresolved = [];
|
|
32998
|
+
if (auto) {
|
|
32999
|
+
const status = await draftApi.getConnectionStatus(draftId);
|
|
33000
|
+
for (const connector of status.connectors) {
|
|
33001
|
+
for (const connectionType of ["mcp", "trigger"]) {
|
|
33002
|
+
const slot = connector[connectionType];
|
|
33003
|
+
if (!slot.required || slot.status !== "missing")
|
|
33004
|
+
continue;
|
|
33005
|
+
const { connections } = await draftApi.listConnections({
|
|
33006
|
+
connectorId: connector.id,
|
|
33007
|
+
capability: connectionType,
|
|
33008
|
+
status: "authorized"
|
|
33009
|
+
});
|
|
33010
|
+
const [only] = connections;
|
|
33011
|
+
if (connections.length === 1 && only) {
|
|
33012
|
+
await draftApi.upsertConnection(draftId, {
|
|
33013
|
+
connectorId: connector.id,
|
|
33014
|
+
connectionType,
|
|
33015
|
+
connectionId: only.id
|
|
33016
|
+
});
|
|
33017
|
+
bound.push({
|
|
33018
|
+
connectorId: connector.id,
|
|
33019
|
+
connectionType,
|
|
33020
|
+
connectionId: only.id,
|
|
33021
|
+
mode: "auto"
|
|
33022
|
+
});
|
|
33023
|
+
continue;
|
|
33024
|
+
}
|
|
33025
|
+
unresolved.push({
|
|
33026
|
+
connectorId: connector.id,
|
|
33027
|
+
connectionType,
|
|
33028
|
+
reason: connections.length === 0 ? "no_connection" : "multiple_connections",
|
|
33029
|
+
candidateConnectionIds: connections.map((connection) => connection.id)
|
|
33030
|
+
});
|
|
33031
|
+
}
|
|
33032
|
+
}
|
|
33033
|
+
}
|
|
33034
|
+
const { isValid, missing } = await draftApi.getConnectionStatus(draftId);
|
|
33035
|
+
return { action: "connected", draftId, bound, unresolved, isValid, missing };
|
|
33036
|
+
}
|
|
33037
|
+
function describeRequired(required2) {
|
|
33038
|
+
if (required2.size === 0)
|
|
33039
|
+
return "none";
|
|
33040
|
+
return [...required2].map(([connectorId, types]) => `${connectorId} (${[...types].join(", ")})`).join(", ");
|
|
33041
|
+
}
|
|
33042
|
+
|
|
33043
|
+
// ../../packages/sdk/dist/esm/drafts/manifest-diff.js
|
|
33044
|
+
function stableStringify(value) {
|
|
33045
|
+
return JSON.stringify(sortKeys(value));
|
|
33046
|
+
}
|
|
33047
|
+
function isDeepEqual(left, right) {
|
|
33048
|
+
return stableStringify(left) === stableStringify(right);
|
|
33049
|
+
}
|
|
33050
|
+
function diffManifests(before, after) {
|
|
33051
|
+
const previous = asManifest(before);
|
|
33052
|
+
const next = asManifest(after);
|
|
33053
|
+
const changedProperties = [.../* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => key !== "steps").filter((key) => !isDeepEqual(previous[key], next[key])).sort();
|
|
33054
|
+
const previousSteps = stepsById(previous.steps);
|
|
33055
|
+
const nextSteps = stepsById(next.steps);
|
|
33056
|
+
const added = [...nextSteps.keys()].filter((id) => !previousSteps.has(id));
|
|
33057
|
+
const removed = [...previousSteps.keys()].filter((id) => !nextSteps.has(id));
|
|
33058
|
+
const changed = [...nextSteps].filter(([id, step]) => previousSteps.has(id) && !isDeepEqual(previousSteps.get(id), step)).map(([id]) => id);
|
|
33059
|
+
const sharedBefore = [...previousSteps.keys()].filter((id) => nextSteps.has(id));
|
|
33060
|
+
const sharedAfter = [...nextSteps.keys()].filter((id) => previousSteps.has(id));
|
|
33061
|
+
const reordered = sharedBefore.some((id, index) => sharedAfter[index] !== id);
|
|
33062
|
+
return {
|
|
33063
|
+
changedProperties,
|
|
33064
|
+
steps: { added, removed, changed, reordered },
|
|
33065
|
+
isEmpty: changedProperties.length === 0 && added.length === 0 && removed.length === 0 && changed.length === 0 && !reordered
|
|
33066
|
+
};
|
|
33067
|
+
}
|
|
33068
|
+
function asManifest(value) {
|
|
33069
|
+
return isManifestRecord(value) ? value : {};
|
|
33070
|
+
}
|
|
33071
|
+
function stepsById(steps) {
|
|
33072
|
+
const byId = /* @__PURE__ */ new Map();
|
|
33073
|
+
if (!Array.isArray(steps))
|
|
33074
|
+
return byId;
|
|
33075
|
+
steps.forEach((step, index) => {
|
|
33076
|
+
const id = isManifestRecord(step) && typeof step.id === "string" ? step.id : `#${index}`;
|
|
33077
|
+
byId.set(id, step);
|
|
33078
|
+
});
|
|
33079
|
+
return byId;
|
|
33080
|
+
}
|
|
33081
|
+
function sortKeys(value) {
|
|
33082
|
+
if (Array.isArray(value))
|
|
33083
|
+
return value.map(sortKeys);
|
|
33084
|
+
if (!isManifestRecord(value))
|
|
33085
|
+
return value;
|
|
33086
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortKeys(value[key])]));
|
|
33087
|
+
}
|
|
33088
|
+
|
|
33089
|
+
// ../../packages/sdk/dist/esm/drafts/publish-draft.js
|
|
33090
|
+
async function publishDraft(draftApi, { draftId, confirmed }) {
|
|
33091
|
+
const { summary, manifest } = await summarizeForPublish(draftApi, draftId);
|
|
33092
|
+
if (!confirmed) {
|
|
33093
|
+
return { action: "publish_preview", summary };
|
|
33094
|
+
}
|
|
33095
|
+
let published;
|
|
33096
|
+
try {
|
|
33097
|
+
published = await draftApi.finalize(draftId);
|
|
33098
|
+
} catch (error62) {
|
|
33099
|
+
const notReady = getDraftNotReadyError(error62);
|
|
33100
|
+
if (!notReady)
|
|
33101
|
+
throw error62;
|
|
33102
|
+
throw new DraftWorkflowError("draft_not_ready", notReady.message, {
|
|
33103
|
+
draftId,
|
|
33104
|
+
cause: error62
|
|
33105
|
+
});
|
|
33106
|
+
}
|
|
33107
|
+
let automation;
|
|
33108
|
+
try {
|
|
33109
|
+
automation = await draftApi.getAutomation(published.automation.id);
|
|
33110
|
+
} catch (error62) {
|
|
33111
|
+
return {
|
|
33112
|
+
action: "published",
|
|
33113
|
+
summary,
|
|
33114
|
+
automation: published.automation,
|
|
33115
|
+
verification: { status: "unavailable", reason: getErrorMessage(error62) }
|
|
33116
|
+
};
|
|
33117
|
+
}
|
|
33118
|
+
const expected = normaliseManifest(manifest);
|
|
33119
|
+
const actual = normaliseManifest(automation.manifest);
|
|
33120
|
+
return {
|
|
33121
|
+
action: "published",
|
|
33122
|
+
summary,
|
|
33123
|
+
automation,
|
|
33124
|
+
verification: isDeepEqual(expected, actual) ? { status: "matched" } : { status: "mismatched", mismatch: diffManifests(expected, actual) }
|
|
33125
|
+
};
|
|
33126
|
+
}
|
|
33127
|
+
async function summarizeForPublish(draftApi, draftId) {
|
|
33128
|
+
const [detail, connections] = await Promise.all([
|
|
33129
|
+
draftApi.get(draftId),
|
|
33130
|
+
draftApi.getConnectionStatus(draftId)
|
|
33131
|
+
]);
|
|
33132
|
+
const { draft } = detail;
|
|
33133
|
+
const target = toDraftTarget(draft);
|
|
33134
|
+
const published = target.kind === "automation" ? await draftApi.getAutomation(target.automationId) : null;
|
|
33135
|
+
const diff = published ? diffManifests(normaliseManifest(published.manifest), normaliseManifest(draft.partialManifest)) : null;
|
|
33136
|
+
const summary = {
|
|
33137
|
+
draftId,
|
|
33138
|
+
name: readManifestName(draft.partialManifest),
|
|
33139
|
+
scope: draft.workspaceId === null ? { kind: "personal" } : {
|
|
33140
|
+
kind: "workspace",
|
|
33141
|
+
workspaceId: draft.workspaceId,
|
|
33142
|
+
// A new workspace automation publishes team-visible; an
|
|
33143
|
+
// edit keeps whatever the automation already has.
|
|
33144
|
+
visibility: published?.visibility ?? "workspace"
|
|
33145
|
+
},
|
|
33146
|
+
target,
|
|
33147
|
+
enabled: published?.enabled ?? true,
|
|
33148
|
+
validation: { state: detail.state, issues: detail.issues },
|
|
33149
|
+
connections: {
|
|
33150
|
+
isValid: connections.isValid,
|
|
33151
|
+
connected: connections.connected.map(({ connectorId, connectionType, connectionId }) => ({
|
|
33152
|
+
connectorId,
|
|
33153
|
+
connectionType,
|
|
33154
|
+
connectionId
|
|
33155
|
+
})),
|
|
33156
|
+
missing: connections.missing
|
|
33157
|
+
},
|
|
33158
|
+
schedule: describeSchedule(draft.partialManifest),
|
|
33159
|
+
diff
|
|
33160
|
+
};
|
|
33161
|
+
return { summary, manifest: draft.partialManifest };
|
|
33162
|
+
}
|
|
33163
|
+
function describeSchedule(manifest) {
|
|
33164
|
+
if (!hasTriggerType(manifest))
|
|
33165
|
+
return null;
|
|
33166
|
+
const schedule = getManifestTriggers(manifest).find((trigger) => trigger.type === "schedule");
|
|
33167
|
+
if (!schedule || schedule.type !== "schedule")
|
|
33168
|
+
return null;
|
|
33169
|
+
return getScheduleTriggerDescription({ value: schedule.value, timeFormat: "24h" });
|
|
33170
|
+
}
|
|
33171
|
+
function normaliseManifest(manifest) {
|
|
33172
|
+
const parsed = manifestSchema.safeParse(manifest);
|
|
33173
|
+
return parsed.success ? parsed.data : manifest;
|
|
33174
|
+
}
|
|
33175
|
+
function readManifestName(manifest) {
|
|
33176
|
+
if (!isManifestRecord(manifest))
|
|
33177
|
+
return "(unnamed)";
|
|
33178
|
+
const { name } = manifest;
|
|
33179
|
+
return typeof name === "string" && name.trim() ? name : "(unnamed)";
|
|
33180
|
+
}
|
|
33181
|
+
|
|
32715
33182
|
// src/lib/auth-environment.ts
|
|
32716
33183
|
import { InvalidArgumentError } from "commander";
|
|
32717
33184
|
var ENVIRONMENTS = {
|
|
@@ -32935,7 +33402,7 @@ function createAutomationsTokenStore(options = {}) {
|
|
|
32935
33402
|
}
|
|
32936
33403
|
|
|
32937
33404
|
// src/lib/credentials.ts
|
|
32938
|
-
import { CliError, getConfigPath as getConfigPath3, getErrorMessage as
|
|
33405
|
+
import { CliError, getConfigPath as getConfigPath3, getErrorMessage as getErrorMessage4 } from "@doist/cli-core";
|
|
32939
33406
|
import { refreshAccessToken } from "@doist/cli-core/auth";
|
|
32940
33407
|
|
|
32941
33408
|
// src/lib/auth-scopes.ts
|
|
@@ -33025,7 +33492,7 @@ function formatScopesHelp() {
|
|
|
33025
33492
|
}
|
|
33026
33493
|
|
|
33027
33494
|
// src/lib/auth-provider.ts
|
|
33028
|
-
import { getErrorMessage } from "@doist/cli-core";
|
|
33495
|
+
import { getErrorMessage as getErrorMessage2 } from "@doist/cli-core";
|
|
33029
33496
|
import { createPkceProvider } from "@doist/cli-core/auth";
|
|
33030
33497
|
var ERROR_HINTS = ["Try again: tda auth login"];
|
|
33031
33498
|
function createAutomationsAuthProvider() {
|
|
@@ -33065,7 +33532,7 @@ async function fetchTodoistUser(token, todoistApiUrl) {
|
|
|
33065
33532
|
});
|
|
33066
33533
|
} catch (error62) {
|
|
33067
33534
|
throw new Error(
|
|
33068
|
-
`Could not reach Todoist to identify the account: ${
|
|
33535
|
+
`Could not reach Todoist to identify the account: ${getErrorMessage2(error62)}`
|
|
33069
33536
|
);
|
|
33070
33537
|
}
|
|
33071
33538
|
if (!response.ok) {
|
|
@@ -33078,7 +33545,7 @@ async function fetchTodoistUser(token, todoistApiUrl) {
|
|
|
33078
33545
|
user = await response.json();
|
|
33079
33546
|
} catch (error62) {
|
|
33080
33547
|
throw new Error(
|
|
33081
|
-
`Todoist returned an unreadable identity response: ${
|
|
33548
|
+
`Todoist returned an unreadable identity response: ${getErrorMessage2(error62)}`
|
|
33082
33549
|
);
|
|
33083
33550
|
}
|
|
33084
33551
|
return { id: requireUserId(user.id), email: typeof user.email === "string" ? user.email : "" };
|
|
@@ -33090,13 +33557,13 @@ function requireUserId(id) {
|
|
|
33090
33557
|
}
|
|
33091
33558
|
|
|
33092
33559
|
// src/lib/errors.ts
|
|
33093
|
-
import { getErrorMessage as
|
|
33560
|
+
import { getErrorMessage as getErrorMessage3 } from "@doist/cli-core";
|
|
33094
33561
|
function getErrorCode(error62) {
|
|
33095
33562
|
return typeof error62 === "object" && error62 !== null && "code" in error62 ? String(error62.code) : void 0;
|
|
33096
33563
|
}
|
|
33097
33564
|
function formatError2(error62) {
|
|
33098
33565
|
const hints = errorHints(error62);
|
|
33099
|
-
return [
|
|
33566
|
+
return [getErrorMessage3(error62), ...hints.map((hint) => ` - ${hint}`)].join("\n");
|
|
33100
33567
|
}
|
|
33101
33568
|
function errorHints(error62) {
|
|
33102
33569
|
if (typeof error62 !== "object" || error62 === null || !("hints" in error62)) return [];
|
|
@@ -33176,7 +33643,7 @@ function accessTokenAfterFailedRefresh(error62, snapshot) {
|
|
|
33176
33643
|
if (hasTokenLifeLeft(snapshot)) return snapshot.bundle.accessToken;
|
|
33177
33644
|
throw new CredentialError(
|
|
33178
33645
|
"REFRESH_FAILED",
|
|
33179
|
-
`Could not refresh your access token: ${
|
|
33646
|
+
`Could not refresh your access token: ${getErrorMessage4(error62)}`,
|
|
33180
33647
|
["Check your network connection and try again."]
|
|
33181
33648
|
);
|
|
33182
33649
|
}
|
|
@@ -33204,19 +33671,19 @@ function resolveAfterStoreFailure(error62) {
|
|
|
33204
33671
|
if (code !== "AUTH_STORE_READ_FAILED") {
|
|
33205
33672
|
throw new CredentialError(
|
|
33206
33673
|
"CREDENTIAL_UNREADABLE",
|
|
33207
|
-
`Could not read the stored credential: ${
|
|
33674
|
+
`Could not read the stored credential: ${getErrorMessage4(error62)}`,
|
|
33208
33675
|
["Run `tda auth logout` and then `tda auth login` to replace it."]
|
|
33209
33676
|
);
|
|
33210
33677
|
}
|
|
33211
33678
|
const fallback = fromEnvironment();
|
|
33212
33679
|
if (fallback) {
|
|
33213
33680
|
process.stderr.write(
|
|
33214
|
-
`Warning: could not read the stored credential (${
|
|
33681
|
+
`Warning: could not read the stored credential (${getErrorMessage4(error62)}). Using ${TOKEN_ENV_VAR}.
|
|
33215
33682
|
`
|
|
33216
33683
|
);
|
|
33217
33684
|
return fallback;
|
|
33218
33685
|
}
|
|
33219
|
-
throw new CredentialError("CREDENTIAL_UNREADABLE",
|
|
33686
|
+
throw new CredentialError("CREDENTIAL_UNREADABLE", getErrorMessage4(error62), [
|
|
33220
33687
|
`Set ${TOKEN_ENV_VAR} to bypass the credential store.`,
|
|
33221
33688
|
"Or re-run `tda auth login --credential-store plaintext`."
|
|
33222
33689
|
]);
|
|
@@ -33879,7 +34346,7 @@ function attachAutomationsLoginCommand(auth) {
|
|
|
33879
34346
|
}
|
|
33880
34347
|
|
|
33881
34348
|
// src/commands/auth/logout.ts
|
|
33882
|
-
import { getErrorMessage as
|
|
34349
|
+
import { getErrorMessage as getErrorMessage5 } from "@doist/cli-core";
|
|
33883
34350
|
import { attachLogoutCommand } from "@doist/cli-core/auth";
|
|
33884
34351
|
|
|
33885
34352
|
// src/lib/spinner.ts
|
|
@@ -33927,7 +34394,7 @@ async function revokeToken(token) {
|
|
|
33927
34394
|
});
|
|
33928
34395
|
return response.ok ? void 0 : `HTTP ${response.status}`;
|
|
33929
34396
|
} catch (error62) {
|
|
33930
|
-
return
|
|
34397
|
+
return getErrorMessage5(error62);
|
|
33931
34398
|
}
|
|
33932
34399
|
}
|
|
33933
34400
|
|
|
@@ -34058,7 +34525,7 @@ function parsePositiveInteger(value) {
|
|
|
34058
34525
|
}
|
|
34059
34526
|
|
|
34060
34527
|
// src/lib/command-error.ts
|
|
34061
|
-
import { formatJson as formatJson4, formatNdjson as formatNdjson4, getErrorMessage as
|
|
34528
|
+
import { formatJson as formatJson4, formatNdjson as formatNdjson4, getErrorMessage as getErrorMessage6 } from "@doist/cli-core";
|
|
34062
34529
|
|
|
34063
34530
|
// src/lib/billing-errors.ts
|
|
34064
34531
|
import { CliError as CliError2 } from "@doist/cli-core";
|
|
@@ -34137,7 +34604,7 @@ function renderFailure(error62, context = {}) {
|
|
|
34137
34604
|
const explained = explainBillingFailure(authExplained);
|
|
34138
34605
|
const code = getErrorCode(explained);
|
|
34139
34606
|
return {
|
|
34140
|
-
message:
|
|
34607
|
+
message: getErrorMessage6(explained),
|
|
34141
34608
|
humanMessage: formatError2(explained),
|
|
34142
34609
|
...code ? { code } : {}
|
|
34143
34610
|
};
|
|
@@ -34168,7 +34635,9 @@ async function runApiCommand(command, operation, presentation, execution = {}) {
|
|
|
34168
34635
|
try {
|
|
34169
34636
|
const globalOptions = getGlobalOptions(command);
|
|
34170
34637
|
mode = getOutputMode(globalOptions);
|
|
34171
|
-
const apiConfiguration = await resolveApiConfiguration(globalOptions
|
|
34638
|
+
const apiConfiguration = await resolveApiConfiguration(globalOptions, {
|
|
34639
|
+
minimumTokenLifetimeMs: execution.minimumTokenLifetimeMs
|
|
34640
|
+
});
|
|
34172
34641
|
source = apiConfiguration.source;
|
|
34173
34642
|
const context = {
|
|
34174
34643
|
api: createAutomationsApi(apiConfiguration),
|
|
@@ -34184,6 +34653,8 @@ async function runApiCommand(command, operation, presentation, execution = {}) {
|
|
|
34184
34653
|
);
|
|
34185
34654
|
stopEarlySpinner();
|
|
34186
34655
|
writeResult(mode, result, presentation);
|
|
34656
|
+
const exitCode = execution.exitCode?.(result);
|
|
34657
|
+
if (exitCode !== void 0) process.exitCode = exitCode;
|
|
34187
34658
|
} catch (error62) {
|
|
34188
34659
|
if (abortController.signal.aborted) {
|
|
34189
34660
|
writeFailure(mode, "interrupted", {
|
|
@@ -34351,16 +34822,21 @@ function formatRun(run) {
|
|
|
34351
34822
|
`Failure reason: ${getRunOutcomeReason(run) ?? "none"}`,
|
|
34352
34823
|
`Result: ${run.result?.text ?? "none"}`,
|
|
34353
34824
|
`Steps: ${run.steps.length}`,
|
|
34354
|
-
...isInsufficientCredit(run) ? ["", topUpAdvisory()] : []
|
|
34825
|
+
...isInsufficientCredit(run) ? ["", topUpAdvisory()] : [],
|
|
34826
|
+
...formatRunSteps(run.steps)
|
|
34355
34827
|
];
|
|
34356
|
-
|
|
34828
|
+
return lines.join("\n");
|
|
34829
|
+
}
|
|
34830
|
+
function formatRunSteps(steps, { toolCallLabel = "Tool" } = {}) {
|
|
34831
|
+
const lines = [];
|
|
34832
|
+
for (const [index, step] of steps.entries()) {
|
|
34357
34833
|
lines.push("", `${index + 1}. ${step.stepId} [${step.status}] ${step.description}`);
|
|
34358
34834
|
lines.push(` Type: ${step.type}`);
|
|
34359
34835
|
if (step.startedAt) lines.push(` Started: ${step.startedAt}`);
|
|
34360
34836
|
if (step.completedAt) lines.push(` Completed: ${step.completedAt}`);
|
|
34361
34837
|
if (step.error) lines.push(` Error: ${step.error}`);
|
|
34362
34838
|
for (const toolCall of step.toolCalls ?? []) {
|
|
34363
|
-
lines.push(`
|
|
34839
|
+
lines.push(` ${toolCallLabel}: ${toolCall.name} (${toolCall.id})`);
|
|
34364
34840
|
if (toolCall.argumentKeys.length > 0) {
|
|
34365
34841
|
lines.push(` Args: ${toolCall.argumentKeys.join(", ")}`);
|
|
34366
34842
|
}
|
|
@@ -34370,7 +34846,7 @@ function formatRun(run) {
|
|
|
34370
34846
|
if (toolCall.resultPreview) lines.push(` Result preview: ${toolCall.resultPreview}`);
|
|
34371
34847
|
}
|
|
34372
34848
|
}
|
|
34373
|
-
return lines
|
|
34849
|
+
return lines;
|
|
34374
34850
|
}
|
|
34375
34851
|
function getResourceName(manifest) {
|
|
34376
34852
|
if (!isRecord2(manifest)) return "(unnamed)";
|
|
@@ -36786,7 +37262,7 @@ function registerConnectorCommand(program) {
|
|
|
36786
37262
|
}
|
|
36787
37263
|
|
|
36788
37264
|
// src/lib/manifest-input.ts
|
|
36789
|
-
import { CliError as CliError3, getErrorMessage as
|
|
37265
|
+
import { CliError as CliError3, getErrorMessage as getErrorMessage7 } from "@doist/cli-core";
|
|
36790
37266
|
import { Option as Option6 } from "commander";
|
|
36791
37267
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
36792
37268
|
import { text } from "node:stream/consumers";
|
|
@@ -36802,20 +37278,10 @@ async function readManifestInput(options, input2 = process.stdin) {
|
|
|
36802
37278
|
} catch (error62) {
|
|
36803
37279
|
throw new CliError3(
|
|
36804
37280
|
"MANIFEST_UNREADABLE",
|
|
36805
|
-
`${source.label} is not valid JSON: ${
|
|
37281
|
+
`${source.label} is not valid JSON: ${getErrorMessage7(error62)}`
|
|
36806
37282
|
);
|
|
36807
37283
|
}
|
|
36808
37284
|
}
|
|
36809
|
-
function parsePartialManifestInput(value) {
|
|
36810
|
-
const validation = validatePartialManifest(value);
|
|
36811
|
-
if (!validation.isValid) {
|
|
36812
|
-
throw new CliError3(
|
|
36813
|
-
"MANIFEST_INVALID",
|
|
36814
|
-
`Manifest is invalid: ${validation.errors.join("; ")}`
|
|
36815
|
-
);
|
|
36816
|
-
}
|
|
36817
|
-
return validation.data;
|
|
36818
|
-
}
|
|
36819
37285
|
async function readSource(options, input2) {
|
|
36820
37286
|
if (options.file !== void 0) {
|
|
36821
37287
|
try {
|
|
@@ -36823,7 +37289,7 @@ async function readSource(options, input2) {
|
|
|
36823
37289
|
} catch (error62) {
|
|
36824
37290
|
throw new CliError3(
|
|
36825
37291
|
"MANIFEST_UNREADABLE",
|
|
36826
|
-
`Could not read ${options.file}: ${
|
|
37292
|
+
`Could not read ${options.file}: ${getErrorMessage7(error62)}`
|
|
36827
37293
|
);
|
|
36828
37294
|
}
|
|
36829
37295
|
}
|
|
@@ -36833,7 +37299,7 @@ async function readSource(options, input2) {
|
|
|
36833
37299
|
} catch (error62) {
|
|
36834
37300
|
throw new CliError3(
|
|
36835
37301
|
"MANIFEST_UNREADABLE",
|
|
36836
|
-
`Could not read standard input: ${
|
|
37302
|
+
`Could not read standard input: ${getErrorMessage7(error62)}`
|
|
36837
37303
|
);
|
|
36838
37304
|
}
|
|
36839
37305
|
}
|
|
@@ -36841,124 +37307,56 @@ async function readSource(options, input2) {
|
|
|
36841
37307
|
}
|
|
36842
37308
|
|
|
36843
37309
|
// src/commands/draft/draft-service.ts
|
|
36844
|
-
import { CliError as CliError4, getErrorMessage as
|
|
36845
|
-
|
|
36846
|
-
|
|
36847
|
-
function
|
|
36848
|
-
return
|
|
36849
|
-
}
|
|
36850
|
-
function isDeepEqual(left, right) {
|
|
36851
|
-
return stableStringify(left) === stableStringify(right);
|
|
37310
|
+
import { CliError as CliError4, getErrorMessage as getErrorMessage8 } from "@doist/cli-core";
|
|
37311
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
37312
|
+
import { setTimeout as wait2 } from "node:timers/promises";
|
|
37313
|
+
function createDraft2(draftApi, input2) {
|
|
37314
|
+
return withRecoveryHints(draftApi, createDraft(draftApi, input2));
|
|
36852
37315
|
}
|
|
36853
|
-
function
|
|
36854
|
-
|
|
36855
|
-
const next = asManifest(after);
|
|
36856
|
-
const changedProperties = [.../* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => key !== "steps").filter((key) => !isDeepEqual(previous[key], next[key])).sort();
|
|
36857
|
-
const previousSteps = stepsById(previous.steps);
|
|
36858
|
-
const nextSteps = stepsById(next.steps);
|
|
36859
|
-
const added = [...nextSteps.keys()].filter((id) => !previousSteps.has(id));
|
|
36860
|
-
const removed = [...previousSteps.keys()].filter((id) => !nextSteps.has(id));
|
|
36861
|
-
const changed = [...nextSteps].filter(([id, step]) => previousSteps.has(id) && !isDeepEqual(previousSteps.get(id), step)).map(([id]) => id);
|
|
36862
|
-
const sharedBefore = [...previousSteps.keys()].filter((id) => nextSteps.has(id));
|
|
36863
|
-
const sharedAfter = [...nextSteps.keys()].filter((id) => previousSteps.has(id));
|
|
36864
|
-
const reordered = sharedBefore.some((id, index) => sharedAfter[index] !== id);
|
|
36865
|
-
return {
|
|
36866
|
-
changedProperties,
|
|
36867
|
-
steps: { added, removed, changed, reordered },
|
|
36868
|
-
isEmpty: changedProperties.length === 0 && added.length === 0 && removed.length === 0 && changed.length === 0 && !reordered
|
|
36869
|
-
};
|
|
37316
|
+
function updateDraft2(draftApi, input2) {
|
|
37317
|
+
return withRecoveryHints(draftApi, updateDraft(draftApi, input2));
|
|
36870
37318
|
}
|
|
36871
|
-
function
|
|
36872
|
-
return
|
|
37319
|
+
function connectDraft2(draftApi, input2) {
|
|
37320
|
+
return withRecoveryHints(draftApi, connectDraft(draftApi, input2));
|
|
36873
37321
|
}
|
|
36874
|
-
function
|
|
36875
|
-
const
|
|
36876
|
-
if (
|
|
36877
|
-
|
|
36878
|
-
|
|
36879
|
-
byId.set(id, step);
|
|
36880
|
-
});
|
|
36881
|
-
return byId;
|
|
36882
|
-
}
|
|
36883
|
-
function sortKeys(value) {
|
|
36884
|
-
if (Array.isArray(value)) return value.map(sortKeys);
|
|
36885
|
-
if (!isRecord3(value)) return value;
|
|
36886
|
-
return Object.fromEntries(
|
|
36887
|
-
Object.keys(value).sort().map((key) => [key, sortKeys(value[key])])
|
|
36888
|
-
);
|
|
36889
|
-
}
|
|
36890
|
-
function isRecord3(value) {
|
|
36891
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36892
|
-
}
|
|
36893
|
-
|
|
36894
|
-
// src/commands/draft/draft-service.ts
|
|
36895
|
-
function resolveDraftApi(api, workspaceId, signal) {
|
|
36896
|
-
const createOptions = { signal, translate: false };
|
|
36897
|
-
if (workspaceId === null) {
|
|
36898
|
-
return {
|
|
36899
|
-
scope: null,
|
|
36900
|
-
get: (draftId) => api.automationDrafts.get(draftId, { signal }),
|
|
36901
|
-
create: (manifest) => api.automationDrafts.create(manifest, createOptions),
|
|
36902
|
-
update: (draftId, manifest) => api.automationDrafts.update(draftId, manifest, { signal }),
|
|
36903
|
-
createAutomationDraft: (automationId) => api.automations.createDraft(automationId, { signal }),
|
|
36904
|
-
upsertConnection: (draftId, body) => api.automationDraftConnections.upsert(draftId, body, { signal }),
|
|
36905
|
-
getConnectionStatus: async (draftId) => (await api.automationDraftConnections.getStatus(draftId, { signal })).validation,
|
|
36906
|
-
finalize: (draftId) => api.automationDrafts.finalize(draftId, { signal }),
|
|
36907
|
-
getAutomation: (automationId) => api.automations.get(automationId, { signal })
|
|
36908
|
-
};
|
|
36909
|
-
}
|
|
37322
|
+
async function publishDraft2(draftApi, input2) {
|
|
37323
|
+
const result = await withRecoveryHints(draftApi, publishDraft(draftApi, input2));
|
|
37324
|
+
if (result.action === "publish_preview") return result;
|
|
37325
|
+
const { verification } = result;
|
|
37326
|
+
if (verification.status !== "unavailable") return { ...result, verification };
|
|
36910
37327
|
return {
|
|
36911
|
-
|
|
36912
|
-
|
|
36913
|
-
|
|
36914
|
-
|
|
36915
|
-
|
|
36916
|
-
upsertConnection: (draftId, body) => api.workspaceAutomationDraftConnections.upsert(workspaceId, draftId, body, { signal }),
|
|
36917
|
-
getConnectionStatus: async (draftId) => (await api.workspaceAutomationDraftConnections.getStatus(workspaceId, draftId, {
|
|
36918
|
-
signal
|
|
36919
|
-
})).validation,
|
|
36920
|
-
finalize: (draftId) => api.workspaceAutomationDrafts.finalize(workspaceId, draftId, { signal }),
|
|
36921
|
-
getAutomation: (automationId) => api.workspaceAutomations.get(workspaceId, automationId, { signal })
|
|
37328
|
+
...result,
|
|
37329
|
+
verification: {
|
|
37330
|
+
...verification,
|
|
37331
|
+
command: `tda automation view ${result.automation.id} ${scopeFlag(draftApi.workspaceId)}`
|
|
37332
|
+
}
|
|
36922
37333
|
};
|
|
36923
37334
|
}
|
|
36924
|
-
async function
|
|
36925
|
-
const partialManifest = parsePartialManifestInput(manifest);
|
|
36926
|
-
if (automationId === void 0) {
|
|
36927
|
-
const result = await draftApi.create(partialManifest);
|
|
36928
|
-
return { action: "created", target: toTarget(result.draft), ...result };
|
|
36929
|
-
}
|
|
36930
|
-
const { draft } = await openEditDraft(draftApi, automationId);
|
|
37335
|
+
async function withRecoveryHints(draftApi, operation) {
|
|
36931
37336
|
try {
|
|
36932
|
-
|
|
36933
|
-
return { action: "created", target: toTarget(result.draft), ...result };
|
|
37337
|
+
return await operation;
|
|
36934
37338
|
} catch (error62) {
|
|
36935
|
-
if (!
|
|
36936
|
-
|
|
36937
|
-
|
|
36938
|
-
|
|
36939
|
-
|
|
36940
|
-
|
|
36941
|
-
|
|
36942
|
-
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
36949
|
-
|
|
36950
|
-
|
|
36951
|
-
|
|
36952
|
-
|
|
36953
|
-
|
|
36954
|
-
|
|
36955
|
-
|
|
36956
|
-
if (!conflict) throw error62;
|
|
36957
|
-
throw new CliError4(
|
|
36958
|
-
"DRAFT_CONFLICT",
|
|
36959
|
-
`Automation ${automationId} already has an open draft ${conflict.draftId}.`,
|
|
36960
|
-
{ hints: draftHints(draftApi.scope, conflict.draftId) }
|
|
36961
|
-
);
|
|
37339
|
+
if (!(error62 instanceof DraftWorkflowError)) throw error62;
|
|
37340
|
+
const scope = draftApi.workspaceId;
|
|
37341
|
+
const draftId = error62.draftId ?? "<draft-id>";
|
|
37342
|
+
switch (error62.code) {
|
|
37343
|
+
case "manifest_invalid":
|
|
37344
|
+
throw new CliError4("MANIFEST_INVALID", error62.message);
|
|
37345
|
+
case "draft_conflict":
|
|
37346
|
+
throw new CliError4("DRAFT_CONFLICT", error62.message, {
|
|
37347
|
+
hints: draftHints(scope, draftId)
|
|
37348
|
+
});
|
|
37349
|
+
case "draft_manifest_rejected":
|
|
37350
|
+
throw new CliError4("DRAFT_MANIFEST_REJECTED", error62.message, {
|
|
37351
|
+
hints: draftHints(scope, draftId)
|
|
37352
|
+
});
|
|
37353
|
+
case "draft_not_ready":
|
|
37354
|
+
throw draftNotReadyError(error62.message, draftId, scope);
|
|
37355
|
+
case "connector_not_required":
|
|
37356
|
+
throw new Error(error62.message);
|
|
37357
|
+
default:
|
|
37358
|
+
throw error62;
|
|
37359
|
+
}
|
|
36962
37360
|
}
|
|
36963
37361
|
}
|
|
36964
37362
|
function draftHints(scope, draftId) {
|
|
@@ -36970,172 +37368,135 @@ function draftHints(scope, draftId) {
|
|
|
36970
37368
|
function scopeFlag(scope) {
|
|
36971
37369
|
return scope === null ? "--personal" : `--workspace ${scope}`;
|
|
36972
37370
|
}
|
|
36973
|
-
|
|
36974
|
-
|
|
36975
|
-
|
|
36976
|
-
|
|
37371
|
+
var DEFAULT_POLL_INTERVALS_MS = { first: 2e3, max: 1e4 };
|
|
37372
|
+
var STATUS_REQUEST_GRACE_MS = 1e4;
|
|
37373
|
+
var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
37374
|
+
"pending",
|
|
37375
|
+
"running"
|
|
37376
|
+
]);
|
|
37377
|
+
async function startDraftTest(draftApi, {
|
|
36977
37378
|
draftId,
|
|
36978
|
-
|
|
36979
|
-
|
|
37379
|
+
request: request2,
|
|
37380
|
+
wait: waitFor,
|
|
37381
|
+
signal
|
|
36980
37382
|
}) {
|
|
36981
|
-
|
|
36982
|
-
|
|
36983
|
-
|
|
36984
|
-
|
|
36985
|
-
|
|
36986
|
-
|
|
36987
|
-
|
|
36988
|
-
|
|
36989
|
-
|
|
36990
|
-
|
|
37383
|
+
let started;
|
|
37384
|
+
try {
|
|
37385
|
+
started = await draftApi.test(draftId, request2);
|
|
37386
|
+
} catch (error62) {
|
|
37387
|
+
const refusal = getDraftTestRequestError(error62);
|
|
37388
|
+
if (!refusal) throw error62;
|
|
37389
|
+
throw toDraftTestCliError(refusal, draftId, draftApi.workspaceId);
|
|
37390
|
+
}
|
|
37391
|
+
const base = {
|
|
37392
|
+
draftId,
|
|
37393
|
+
testRunId: started.testRunId,
|
|
37394
|
+
trigger: started.trigger,
|
|
37395
|
+
connectorCalls: started.connectorCalls,
|
|
37396
|
+
command: `tda draft test-run ${draftId} ${started.testRunId} ${scopeFlag(draftApi.workspaceId)}`
|
|
37397
|
+
};
|
|
37398
|
+
if (!waitFor) {
|
|
37399
|
+
return { action: "test_started", ...base, status: started.status };
|
|
37400
|
+
}
|
|
37401
|
+
const intervals = waitFor.pollIntervalsMs ?? DEFAULT_POLL_INTERVALS_MS;
|
|
37402
|
+
const deadline = Date.now() + waitFor.timeoutMs;
|
|
37403
|
+
let intervalMs = intervals.first;
|
|
37404
|
+
let last = null;
|
|
37405
|
+
while (true) {
|
|
37406
|
+
const requestTimeout = AbortSignal.timeout(
|
|
37407
|
+
Math.max(deadline - Date.now(), 0) + (waitFor.requestGraceMs ?? STATUS_REQUEST_GRACE_MS)
|
|
37408
|
+
);
|
|
37409
|
+
try {
|
|
37410
|
+
last = await draftApi.getTestRun(
|
|
37411
|
+
draftId,
|
|
37412
|
+
started.testRunId,
|
|
37413
|
+
void 0,
|
|
37414
|
+
AbortSignal.any([signal, requestTimeout])
|
|
36991
37415
|
);
|
|
37416
|
+
} catch (error62) {
|
|
37417
|
+
if (signal.aborted || !requestTimeout.aborted) throw error62;
|
|
37418
|
+
return toFinished(base, last, "timed_out");
|
|
36992
37419
|
}
|
|
36993
|
-
|
|
36994
|
-
|
|
36995
|
-
await draftApi.upsertConnection(draftId, {
|
|
36996
|
-
connectorId: selection.connectorId,
|
|
36997
|
-
connectionType,
|
|
36998
|
-
connectionId: selection.connectionId
|
|
36999
|
-
});
|
|
37000
|
-
bound.push({ ...selection, connectionType, mode: "explicit" });
|
|
37420
|
+
if (!OPEN_RUN_STATUSES.has(last.status)) {
|
|
37421
|
+
return toFinished(base, last, toOutcome(last.status));
|
|
37001
37422
|
}
|
|
37002
|
-
|
|
37003
|
-
|
|
37004
|
-
|
|
37005
|
-
const status = await draftApi.getConnectionStatus(draftId);
|
|
37006
|
-
for (const connector of status.connectors) {
|
|
37007
|
-
for (const connectionType of ["mcp", "trigger"]) {
|
|
37008
|
-
const slot = connector[connectionType];
|
|
37009
|
-
if (!slot.required || slot.status !== "missing") continue;
|
|
37010
|
-
const { connections } = await api.connections.list({
|
|
37011
|
-
connectorId: connector.id,
|
|
37012
|
-
capability: connectionType,
|
|
37013
|
-
status: "authorized"
|
|
37014
|
-
});
|
|
37015
|
-
const [only] = connections;
|
|
37016
|
-
if (connections.length === 1 && only) {
|
|
37017
|
-
await draftApi.upsertConnection(draftId, {
|
|
37018
|
-
connectorId: connector.id,
|
|
37019
|
-
connectionType,
|
|
37020
|
-
connectionId: only.id
|
|
37021
|
-
});
|
|
37022
|
-
bound.push({
|
|
37023
|
-
connectorId: connector.id,
|
|
37024
|
-
connectionType,
|
|
37025
|
-
connectionId: only.id,
|
|
37026
|
-
mode: "auto"
|
|
37027
|
-
});
|
|
37028
|
-
continue;
|
|
37029
|
-
}
|
|
37030
|
-
unresolved.push({
|
|
37031
|
-
connectorId: connector.id,
|
|
37032
|
-
connectionType,
|
|
37033
|
-
reason: connections.length === 0 ? "no_connection" : "multiple_connections",
|
|
37034
|
-
candidateConnectionIds: connections.map((connection) => connection.id)
|
|
37035
|
-
});
|
|
37036
|
-
}
|
|
37423
|
+
const remainingMs = deadline - Date.now();
|
|
37424
|
+
if (remainingMs <= 0) {
|
|
37425
|
+
return toFinished(base, last, "timed_out");
|
|
37037
37426
|
}
|
|
37427
|
+
await wait2(Math.min(intervalMs, remainingMs), void 0, { signal });
|
|
37428
|
+
intervalMs = Math.min(intervalMs * 2, intervals.max);
|
|
37038
37429
|
}
|
|
37039
|
-
const { isValid, missing } = await draftApi.getConnectionStatus(draftId);
|
|
37040
|
-
return { action: "connected", draftId, bound, unresolved, isValid, missing };
|
|
37041
37430
|
}
|
|
37042
|
-
function
|
|
37043
|
-
|
|
37044
|
-
|
|
37431
|
+
function toFinished(base, run, outcome) {
|
|
37432
|
+
return {
|
|
37433
|
+
action: "test_finished",
|
|
37434
|
+
...base,
|
|
37435
|
+
outcome,
|
|
37436
|
+
status: run?.status ?? "pending",
|
|
37437
|
+
failedStep: run?.failedStep ?? null,
|
|
37438
|
+
error: run?.failedStep?.error ?? (run ? getRunOutcomeReason(run) : null) ?? (run?.status === "data-expired" ? "Temporal no longer holds this run's history, so its outcome is unknown" : null),
|
|
37439
|
+
result: run?.result ?? null
|
|
37440
|
+
};
|
|
37441
|
+
}
|
|
37442
|
+
function toOutcome(status) {
|
|
37443
|
+
switch (status) {
|
|
37444
|
+
case "completed":
|
|
37445
|
+
return "passed";
|
|
37446
|
+
case "skipped":
|
|
37447
|
+
return "skipped";
|
|
37448
|
+
case "cancelled":
|
|
37449
|
+
return "cancelled";
|
|
37450
|
+
default:
|
|
37451
|
+
return "failed";
|
|
37452
|
+
}
|
|
37045
37453
|
}
|
|
37046
|
-
|
|
37047
|
-
const
|
|
37048
|
-
|
|
37049
|
-
|
|
37454
|
+
function draftNotReadyError(message, draftId, scope) {
|
|
37455
|
+
const flag = scopeFlag(scope);
|
|
37456
|
+
return new CliError4("DRAFT_NOT_READY", message, {
|
|
37457
|
+
hints: [
|
|
37458
|
+
`See the issues: tda draft view ${draftId} ${flag}`,
|
|
37459
|
+
`Bind connections: tda draft connect ${draftId} --auto ${flag}`
|
|
37460
|
+
]
|
|
37461
|
+
});
|
|
37462
|
+
}
|
|
37463
|
+
function toDraftTestCliError(refusal, draftId, scope) {
|
|
37464
|
+
const flag = scopeFlag(scope);
|
|
37465
|
+
switch (refusal.code) {
|
|
37466
|
+
case "draft_test_not_ready":
|
|
37467
|
+
return draftNotReadyError(refusal.message, draftId, scope);
|
|
37468
|
+
case "draft_test_payload_required":
|
|
37469
|
+
case "draft_test_payload_filtered":
|
|
37470
|
+
return new CliError4(refusal.code.toUpperCase(), refusal.message, {
|
|
37471
|
+
hints: [
|
|
37472
|
+
`Test with a real event: tda draft test ${draftId} --trigger <event> --payload-file <path> ${flag}`
|
|
37473
|
+
]
|
|
37474
|
+
});
|
|
37475
|
+
default:
|
|
37476
|
+
return new CliError4(refusal.code.toUpperCase(), refusal.message);
|
|
37050
37477
|
}
|
|
37051
|
-
|
|
37478
|
+
}
|
|
37479
|
+
function parseTriggerOption(value) {
|
|
37480
|
+
return /^\d+$/.test(value) ? { index: Number(value) } : { event: value };
|
|
37481
|
+
}
|
|
37482
|
+
async function readTestPayloadFile(path) {
|
|
37483
|
+
let content;
|
|
37052
37484
|
try {
|
|
37053
|
-
|
|
37485
|
+
content = await readFile3(path, "utf8");
|
|
37054
37486
|
} catch (error62) {
|
|
37055
|
-
|
|
37056
|
-
|
|
37057
|
-
|
|
37058
|
-
|
|
37059
|
-
`See the issues: tda draft view ${draftId}`,
|
|
37060
|
-
`Bind connections: tda draft connect ${draftId} --auto`
|
|
37061
|
-
]
|
|
37062
|
-
});
|
|
37487
|
+
throw new CliError4(
|
|
37488
|
+
"PAYLOAD_UNREADABLE",
|
|
37489
|
+
`Could not read ${path}: ${getErrorMessage8(error62)}`
|
|
37490
|
+
);
|
|
37063
37491
|
}
|
|
37064
|
-
const automationId = published.automation.id;
|
|
37065
|
-
let automation;
|
|
37066
37492
|
try {
|
|
37067
|
-
|
|
37493
|
+
return JSON.parse(content);
|
|
37068
37494
|
} catch (error62) {
|
|
37069
|
-
|
|
37070
|
-
|
|
37071
|
-
|
|
37072
|
-
|
|
37073
|
-
verification: {
|
|
37074
|
-
status: "unavailable",
|
|
37075
|
-
reason: getErrorMessage7(error62),
|
|
37076
|
-
command: `tda automation view ${automationId} ${scopeFlag(draftApi.scope)}`
|
|
37077
|
-
}
|
|
37078
|
-
};
|
|
37495
|
+
throw new CliError4(
|
|
37496
|
+
"PAYLOAD_UNREADABLE",
|
|
37497
|
+
`${path} is not valid JSON: ${getErrorMessage8(error62)}`
|
|
37498
|
+
);
|
|
37079
37499
|
}
|
|
37080
|
-
const expected = normaliseManifest(manifest);
|
|
37081
|
-
const actual = normaliseManifest(automation.manifest);
|
|
37082
|
-
return {
|
|
37083
|
-
action: "published",
|
|
37084
|
-
summary,
|
|
37085
|
-
automation,
|
|
37086
|
-
verification: isDeepEqual(expected, actual) ? { status: "matched" } : { status: "mismatched", mismatch: diffManifests(expected, actual) }
|
|
37087
|
-
};
|
|
37088
|
-
}
|
|
37089
|
-
async function summarizeForPublish(draftApi, draftId) {
|
|
37090
|
-
const [detail, connections] = await Promise.all([
|
|
37091
|
-
draftApi.get(draftId),
|
|
37092
|
-
draftApi.getConnectionStatus(draftId)
|
|
37093
|
-
]);
|
|
37094
|
-
const { draft } = detail;
|
|
37095
|
-
const target = toTarget(draft);
|
|
37096
|
-
const published = target.kind === "automation" ? await draftApi.getAutomation(target.automationId) : null;
|
|
37097
|
-
const diff = published ? diffManifests(
|
|
37098
|
-
normaliseManifest(published.manifest),
|
|
37099
|
-
normaliseManifest(draft.partialManifest)
|
|
37100
|
-
) : null;
|
|
37101
|
-
const summary = {
|
|
37102
|
-
draftId,
|
|
37103
|
-
name: getResourceName(draft.partialManifest),
|
|
37104
|
-
scope: draft.workspaceId === null ? { kind: "personal" } : {
|
|
37105
|
-
kind: "workspace",
|
|
37106
|
-
workspaceId: draft.workspaceId,
|
|
37107
|
-
// A new workspace automation publishes team-visible; an
|
|
37108
|
-
// edit keeps whatever the automation already has.
|
|
37109
|
-
visibility: published?.visibility ?? "workspace"
|
|
37110
|
-
},
|
|
37111
|
-
target,
|
|
37112
|
-
enabled: published?.enabled ?? true,
|
|
37113
|
-
validation: { state: detail.state, issues: detail.issues },
|
|
37114
|
-
connections: {
|
|
37115
|
-
isValid: connections.isValid,
|
|
37116
|
-
connected: connections.connected.map(
|
|
37117
|
-
({ connectorId, connectionType, connectionId }) => ({
|
|
37118
|
-
connectorId,
|
|
37119
|
-
connectionType,
|
|
37120
|
-
connectionId
|
|
37121
|
-
})
|
|
37122
|
-
),
|
|
37123
|
-
missing: connections.missing
|
|
37124
|
-
},
|
|
37125
|
-
schedule: describeSchedule(draft.partialManifest),
|
|
37126
|
-
diff
|
|
37127
|
-
};
|
|
37128
|
-
return { summary, manifest: draft.partialManifest };
|
|
37129
|
-
}
|
|
37130
|
-
function describeSchedule(manifest) {
|
|
37131
|
-
if (!hasTriggerType(manifest)) return null;
|
|
37132
|
-
const schedule = getManifestTriggers(manifest).find((trigger) => trigger.type === "schedule");
|
|
37133
|
-
if (!schedule || schedule.type !== "schedule") return null;
|
|
37134
|
-
return getScheduleTriggerDescription({ value: schedule.value, timeFormat: "24h" });
|
|
37135
|
-
}
|
|
37136
|
-
function normaliseManifest(manifest) {
|
|
37137
|
-
const parsed = manifestSchema.safeParse(manifest);
|
|
37138
|
-
return parsed.success ? parsed.data : manifest;
|
|
37139
37500
|
}
|
|
37140
37501
|
|
|
37141
37502
|
// src/commands/draft/format.ts
|
|
@@ -37246,6 +37607,72 @@ function describeDiff(diff) {
|
|
|
37246
37607
|
if (diff.steps.reordered) parts.push("steps reordered");
|
|
37247
37608
|
return parts.join("; ");
|
|
37248
37609
|
}
|
|
37610
|
+
var MOCKED_CONNECTOR_CALLS_LINE = "Connector calls: mocked (simulated by a model; nothing was read from or written to your services)";
|
|
37611
|
+
function formatDraftTestRun(run) {
|
|
37612
|
+
const reason = getRunOutcomeReason(run);
|
|
37613
|
+
const lines = [
|
|
37614
|
+
`Test run: ${run.id}`,
|
|
37615
|
+
`Draft: ${run.draftId}`,
|
|
37616
|
+
`Automation: ${run.automationId ?? "none (the draft creates a new one)"}`,
|
|
37617
|
+
`Status: ${run.status}`,
|
|
37618
|
+
MOCKED_CONNECTOR_CALLS_LINE,
|
|
37619
|
+
`Created: ${run.createdAt}`,
|
|
37620
|
+
`Updated: ${run.updatedAt}`
|
|
37621
|
+
];
|
|
37622
|
+
if (run.failedStep) {
|
|
37623
|
+
lines.push(
|
|
37624
|
+
`Failed step: ${run.failedStep.stepId} (${run.failedStep.description})`,
|
|
37625
|
+
`Error: ${run.failedStep.error ?? reason ?? "none reported"}`
|
|
37626
|
+
);
|
|
37627
|
+
} else if (reason) {
|
|
37628
|
+
lines.push(`${run.status === "skipped" ? "Skip" : "Failure"} reason: ${reason}`);
|
|
37629
|
+
}
|
|
37630
|
+
lines.push(
|
|
37631
|
+
`Result: ${run.result?.text ?? "none"}`,
|
|
37632
|
+
`Steps: ${run.steps.length}`,
|
|
37633
|
+
...formatRunSteps(run.steps, { toolCallLabel: "Tool (mocked)" })
|
|
37634
|
+
);
|
|
37635
|
+
return lines.join("\n");
|
|
37636
|
+
}
|
|
37637
|
+
var INPUT_SOURCE_LABELS = {
|
|
37638
|
+
schedule: "as a scheduled run",
|
|
37639
|
+
"sample-event": "with the built-in sample event",
|
|
37640
|
+
supplied: "with the supplied payload",
|
|
37641
|
+
empty: "with an empty request body"
|
|
37642
|
+
};
|
|
37643
|
+
var OUTCOME_LABELS = {
|
|
37644
|
+
passed: "passed",
|
|
37645
|
+
failed: "failed",
|
|
37646
|
+
skipped: "was skipped",
|
|
37647
|
+
cancelled: "was cancelled",
|
|
37648
|
+
timed_out: "did not finish before the timeout"
|
|
37649
|
+
};
|
|
37650
|
+
function formatDraftTest(result) {
|
|
37651
|
+
const { trigger } = result;
|
|
37652
|
+
const which = trigger.type === "webhook" ? `webhook ${trigger.event}` : trigger.type;
|
|
37653
|
+
const lines = [
|
|
37654
|
+
result.action === "test_started" ? `Started test run ${result.testRunId} for draft ${result.draftId}.` : `Test run ${result.testRunId} for draft ${result.draftId} ${OUTCOME_LABELS[result.outcome]}.`,
|
|
37655
|
+
`Trigger: ${trigger.index} of ${trigger.count} (${which}), ${INPUT_SOURCE_LABELS[trigger.inputSource]}`
|
|
37656
|
+
];
|
|
37657
|
+
if (result.action === "test_finished") {
|
|
37658
|
+
if (result.outcome === "timed_out") lines.push(`Status: ${result.status}`);
|
|
37659
|
+
if (result.failedStep) {
|
|
37660
|
+
lines.push(
|
|
37661
|
+
`Failed step: ${result.failedStep.stepId} (${result.failedStep.description})`
|
|
37662
|
+
);
|
|
37663
|
+
}
|
|
37664
|
+
if (result.error)
|
|
37665
|
+
lines.push(`${result.outcome === "skipped" ? "Reason" : "Error"}: ${result.error}`);
|
|
37666
|
+
if (result.result) lines.push(`Result: ${result.result.text}`);
|
|
37667
|
+
}
|
|
37668
|
+
lines.push(MOCKED_CONNECTOR_CALLS_LINE, `Details: ${result.command}`);
|
|
37669
|
+
if (trigger.count > 1) {
|
|
37670
|
+
lines.push(
|
|
37671
|
+
`This draft has ${trigger.count} triggers; test another with --trigger <index|event>.`
|
|
37672
|
+
);
|
|
37673
|
+
}
|
|
37674
|
+
return lines.join("\n");
|
|
37675
|
+
}
|
|
37249
37676
|
|
|
37250
37677
|
// src/commands/draft/validate-command.ts
|
|
37251
37678
|
import { CliError as CliError5 } from "@doist/cli-core";
|
|
@@ -37300,8 +37727,10 @@ function formatManifestValidation(result) {
|
|
|
37300
37727
|
}
|
|
37301
37728
|
|
|
37302
37729
|
// src/commands/draft/index.ts
|
|
37730
|
+
var DEFAULT_TEST_WAIT = "10m";
|
|
37731
|
+
var TOKEN_LIFETIME_MARGIN_MS = 2 * 60 * 1e3;
|
|
37303
37732
|
function registerDraftCommand(program) {
|
|
37304
|
-
const draft = program.command("draft").description("Author and inspect automation drafts");
|
|
37733
|
+
const draft = program.command("draft").description("Author, test and inspect automation drafts");
|
|
37305
37734
|
registerDraftValidateCommand(draft);
|
|
37306
37735
|
addWorkspaceScopeOptions(
|
|
37307
37736
|
addCursorPaginationOptions(
|
|
@@ -37358,8 +37787,8 @@ function registerDraftCommand(program) {
|
|
|
37358
37787
|
command,
|
|
37359
37788
|
async ({ api, signal }) => {
|
|
37360
37789
|
const manifest = await readManifestInput(options);
|
|
37361
|
-
const draftApi =
|
|
37362
|
-
return
|
|
37790
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37791
|
+
return createDraft2(draftApi, { manifest, automationId: options.automation });
|
|
37363
37792
|
},
|
|
37364
37793
|
{ human: formatDraftChange },
|
|
37365
37794
|
{ spinner: "Creating draft..." }
|
|
@@ -37374,8 +37803,8 @@ function registerDraftCommand(program) {
|
|
|
37374
37803
|
command,
|
|
37375
37804
|
async ({ api, signal }) => {
|
|
37376
37805
|
const manifest = await readManifestInput(options);
|
|
37377
|
-
const draftApi =
|
|
37378
|
-
return
|
|
37806
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37807
|
+
return updateDraft2(draftApi, { draftId, manifest });
|
|
37379
37808
|
},
|
|
37380
37809
|
{ human: formatDraftChange },
|
|
37381
37810
|
{ spinner: "Updating draft..." }
|
|
@@ -37401,8 +37830,8 @@ function registerDraftCommand(program) {
|
|
|
37401
37830
|
);
|
|
37402
37831
|
}
|
|
37403
37832
|
const selections = options.connection.map(parseConnectionSelectionOption);
|
|
37404
|
-
const draftApi =
|
|
37405
|
-
return
|
|
37833
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37834
|
+
return connectDraft2(draftApi, {
|
|
37406
37835
|
draftId,
|
|
37407
37836
|
selections,
|
|
37408
37837
|
auto: options.auto === true
|
|
@@ -37418,13 +37847,70 @@ function registerDraftCommand(program) {
|
|
|
37418
37847
|
await runApiCommand(
|
|
37419
37848
|
command,
|
|
37420
37849
|
async ({ api, signal }) => {
|
|
37421
|
-
const draftApi =
|
|
37422
|
-
return
|
|
37850
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37851
|
+
return publishDraft2(draftApi, { draftId, confirmed: options.yes === true });
|
|
37423
37852
|
},
|
|
37424
37853
|
{ human: formatDraftPublish, json: summarizeDraftPublish },
|
|
37425
37854
|
{ spinner: options.yes === true ? "Publishing..." : "Loading draft..." }
|
|
37426
37855
|
);
|
|
37427
37856
|
});
|
|
37857
|
+
addWorkspaceScopeOptions(
|
|
37858
|
+
draft.command("test").description("Start a test run of a draft, with mocked connector calls").argument("<draft-id>", "Draft ID").option("--wait", "Wait for the run to end and exit 1 unless it passed").option(
|
|
37859
|
+
"--timeout <duration>",
|
|
37860
|
+
`How long --wait waits, such as 90s or 15m (default ${DEFAULT_TEST_WAIT})`
|
|
37861
|
+
).option(
|
|
37862
|
+
"--trigger <index|event>",
|
|
37863
|
+
"The trigger to test: its position in the manifest's triggers, or a webhook event"
|
|
37864
|
+
).option(
|
|
37865
|
+
"--payload-file <path>",
|
|
37866
|
+
"A JSON event to test with: a webhook trigger's event or an API trigger's body"
|
|
37867
|
+
)
|
|
37868
|
+
).action(async (draftId, options, command) => {
|
|
37869
|
+
await runApiCommand(
|
|
37870
|
+
command,
|
|
37871
|
+
async ({ api, signal }) => {
|
|
37872
|
+
if (options.timeout !== void 0 && options.wait !== true) {
|
|
37873
|
+
throw new Error("--timeout only applies with --wait.");
|
|
37874
|
+
}
|
|
37875
|
+
const timeoutMs = parseTestTimeout(options.timeout ?? DEFAULT_TEST_WAIT);
|
|
37876
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37877
|
+
return startDraftTest(draftApi, {
|
|
37878
|
+
draftId,
|
|
37879
|
+
request: {
|
|
37880
|
+
...options.trigger !== void 0 ? { trigger: parseTriggerOption(options.trigger) } : {},
|
|
37881
|
+
...options.payloadFile !== void 0 ? { payload: await readTestPayloadFile(options.payloadFile) } : {}
|
|
37882
|
+
},
|
|
37883
|
+
wait: options.wait === true ? { timeoutMs } : null,
|
|
37884
|
+
signal
|
|
37885
|
+
});
|
|
37886
|
+
},
|
|
37887
|
+
{ human: formatDraftTest },
|
|
37888
|
+
{
|
|
37889
|
+
spinner: options.wait === true ? "Testing draft..." : "Starting test run...",
|
|
37890
|
+
minimumTokenLifetimeMs: options.wait === true ? tryParseTestTimeout(options.timeout ?? DEFAULT_TEST_WAIT) + TOKEN_LIFETIME_MARGIN_MS : void 0,
|
|
37891
|
+
exitCode: (result) => result.action === "test_finished" && result.outcome !== "passed" ? 1 : void 0
|
|
37892
|
+
}
|
|
37893
|
+
);
|
|
37894
|
+
});
|
|
37895
|
+
addWorkspaceScopeOptions(
|
|
37896
|
+
draft.command("test-run").description("Show a draft's test run with step and failure details").argument("<draft-id>", "Draft ID").argument("<run-id>", "Test run ID").option("--full", "Include raw step results and generated code in machine output")
|
|
37897
|
+
).action(async (draftId, runId, options, command) => {
|
|
37898
|
+
await runApiCommand(
|
|
37899
|
+
command,
|
|
37900
|
+
async ({ api, signal }) => {
|
|
37901
|
+
const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
|
|
37902
|
+
return draftApi.getTestRun(draftId, runId, {
|
|
37903
|
+
includeGeneratedCode: options.full === true,
|
|
37904
|
+
includeToolCallPreviews: true
|
|
37905
|
+
});
|
|
37906
|
+
},
|
|
37907
|
+
{
|
|
37908
|
+
human: formatDraftTestRun,
|
|
37909
|
+
json: options.full === true ? void 0 : summarizeDraftTestRun
|
|
37910
|
+
},
|
|
37911
|
+
{ spinner: "Loading test run..." }
|
|
37912
|
+
);
|
|
37913
|
+
});
|
|
37428
37914
|
addWorkspaceScopeOptions(
|
|
37429
37915
|
draft.command("delete").description("Delete a draft after previewing which one it is").argument("<draft-id>", "Draft ID").option("--yes", "Confirm deletion")
|
|
37430
37916
|
).action(async (draftId, options, command) => {
|
|
@@ -37461,6 +37947,20 @@ function streamDraftList(options, fetchPage) {
|
|
|
37461
37947
|
async (args) => summarizeDraftListPage(await fetchPage(args))
|
|
37462
37948
|
);
|
|
37463
37949
|
}
|
|
37950
|
+
function tryParseTestTimeout(value) {
|
|
37951
|
+
try {
|
|
37952
|
+
return durationStringToMilliseconds(value);
|
|
37953
|
+
} catch {
|
|
37954
|
+
return 0;
|
|
37955
|
+
}
|
|
37956
|
+
}
|
|
37957
|
+
function parseTestTimeout(value) {
|
|
37958
|
+
try {
|
|
37959
|
+
return durationStringToMilliseconds(value);
|
|
37960
|
+
} catch {
|
|
37961
|
+
throw new Error(`Invalid --timeout ${value}; use a duration such as 90s, 15m or 1h.`);
|
|
37962
|
+
}
|
|
37963
|
+
}
|
|
37464
37964
|
|
|
37465
37965
|
// src/commands/run/index.ts
|
|
37466
37966
|
import { Option as Option7 } from "commander";
|