@doist/automations-cli 1.21.2 → 1.22.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 CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  collectSkillStatus,
8
8
  getInstaller,
9
9
  updateAllInstalledSkills
10
- } from "./chunk-ZWFZ2DD3.js";
10
+ } from "./chunk-NB7HIIJZ.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.21.2",
6902
+ version: "1.22.0",
6903
6903
  description: "Command-line client for Todoist Automations",
6904
6904
  type: "module",
6905
6905
  bin: {
@@ -6945,7 +6945,7 @@ var package_default = {
6945
6945
  dependencies: {
6946
6946
  "@doist/cli-core": "1.7.0",
6947
6947
  "@napi-rs/keyring": "2.1.1",
6948
- ai: "7.0.137",
6948
+ ai: "7.0.141",
6949
6949
  commander: "15.0.0",
6950
6950
  oauth4webapi: "3.8.8",
6951
6951
  open: "11.0.4"
@@ -6959,7 +6959,7 @@ var package_default = {
6959
6959
  "semantic-release": "25.0.9",
6960
6960
  "semantic-release-monorepo": "8.0.2",
6961
6961
  typescript: "6.0.3",
6962
- vitest: "5.0.4"
6962
+ vitest: "5.0.5"
6963
6963
  },
6964
6964
  engines: {
6965
6965
  node: ">=24"
@@ -9830,7 +9830,7 @@ ${content.join("\n")}
9830
9830
  var version = {
9831
9831
  major: 4,
9832
9832
  minor: 6,
9833
- patch: 6
9833
+ patch: 7
9834
9834
  };
9835
9835
 
9836
9836
  // ../../node_modules/zod/v4/core/schemas.js
@@ -33105,15 +33105,27 @@ function diffManifests(before, after) {
33105
33105
  const added = [...nextSteps.keys()].filter((id) => !previousSteps.has(id));
33106
33106
  const removed = [...previousSteps.keys()].filter((id) => !nextSteps.has(id));
33107
33107
  const changed = [...nextSteps].filter(([id, step]) => previousSteps.has(id) && !isDeepEqual(previousSteps.get(id), step)).map(([id]) => id);
33108
+ const changedFields = Object.fromEntries(changed.map((id) => [id, differingPaths(previousSteps.get(id), nextSteps.get(id), "")]));
33108
33109
  const sharedBefore = [...previousSteps.keys()].filter((id) => nextSteps.has(id));
33109
33110
  const sharedAfter = [...nextSteps.keys()].filter((id) => previousSteps.has(id));
33110
33111
  const reordered = sharedBefore.some((id, index) => sharedAfter[index] !== id);
33111
33112
  return {
33112
33113
  changedProperties,
33113
- steps: { added, removed, changed, reordered },
33114
+ steps: { added, removed, changed, changedFields, reordered },
33114
33115
  isEmpty: changedProperties.length === 0 && added.length === 0 && removed.length === 0 && changed.length === 0 && !reordered
33115
33116
  };
33116
33117
  }
33118
+ function differingPaths(before, after, path) {
33119
+ if (isDeepEqual(before, after))
33120
+ return [];
33121
+ if (isManifestRecord(before) && isManifestRecord(after)) {
33122
+ return [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].sort().flatMap((key) => differingPaths(before[key], after[key], path ? `${path}.${key}` : key));
33123
+ }
33124
+ if (path && Array.isArray(before) && Array.isArray(after) && before.length === after.length) {
33125
+ return before.flatMap((item, index) => differingPaths(item, after[index], `${path}[${index}]`));
33126
+ }
33127
+ return path ? [path] : [];
33128
+ }
33117
33129
  function asManifest(value) {
33118
33130
  return isManifestRecord(value) ? value : {};
33119
33131
  }
@@ -33137,7 +33149,7 @@ function sortKeys(value) {
33137
33149
 
33138
33150
  // ../../packages/sdk/dist/esm/drafts/publish-draft.js
33139
33151
  async function publishDraft(draftApi, { draftId, confirmed }) {
33140
- const { summary, manifest } = await summarizeForPublish(draftApi, draftId);
33152
+ const { summary, detail } = await previewPublish(draftApi, draftId);
33141
33153
  if (!confirmed) {
33142
33154
  return { action: "publish_preview", summary };
33143
33155
  }
@@ -33164,7 +33176,7 @@ async function publishDraft(draftApi, { draftId, confirmed }) {
33164
33176
  verification: { status: "unavailable", reason: getErrorMessage(error62) }
33165
33177
  };
33166
33178
  }
33167
- const expected = normaliseManifest(manifest);
33179
+ const expected = normaliseManifest(detail.draft.partialManifest);
33168
33180
  const actual = normaliseManifest(automation.manifest);
33169
33181
  return {
33170
33182
  action: "published",
@@ -33173,7 +33185,7 @@ async function publishDraft(draftApi, { draftId, confirmed }) {
33173
33185
  verification: isDeepEqual(expected, actual) ? { status: "matched" } : { status: "mismatched", mismatch: diffManifests(expected, actual) }
33174
33186
  };
33175
33187
  }
33176
- async function summarizeForPublish(draftApi, draftId) {
33188
+ async function previewPublish(draftApi, draftId) {
33177
33189
  const [detail, connections] = await Promise.all([
33178
33190
  draftApi.get(draftId),
33179
33191
  draftApi.getConnectionStatus(draftId)
@@ -33207,7 +33219,7 @@ async function summarizeForPublish(draftApi, draftId) {
33207
33219
  schedule: describeSchedule(draft.partialManifest),
33208
33220
  diff
33209
33221
  };
33210
- return { summary, manifest: draft.partialManifest };
33222
+ return { summary, detail };
33211
33223
  }
33212
33224
  function describeSchedule(manifest) {
33213
33225
  if (!hasTriggerType(manifest))
@@ -34513,6 +34525,9 @@ function parsePositiveInteger(value) {
34513
34525
  return parsed;
34514
34526
  }
34515
34527
 
34528
+ // src/lib/api-command.ts
34529
+ import { CliError as CliError3 } from "@doist/cli-core";
34530
+
34516
34531
  // src/lib/command-error.ts
34517
34532
  import { formatJson as formatJson4, formatNdjson as formatNdjson4, getErrorMessage as getErrorMessage6 } from "@doist/cli-core";
34518
34533
 
@@ -34646,10 +34661,11 @@ async function runApiCommand(command, operation, presentation, execution = {}) {
34646
34661
  if (exitCode !== void 0) process.exitCode = exitCode;
34647
34662
  } catch (error62) {
34648
34663
  if (abortController.signal.aborted) {
34649
- writeFailure(mode, "interrupted", {
34650
- message: "Interrupted.",
34651
- humanMessage: "Interrupted."
34652
- });
34664
+ writeFailure(
34665
+ mode,
34666
+ "interrupted",
34667
+ error62 instanceof CliError3 ? renderFailure(error62, { mode, source }) : { message: "Interrupted.", humanMessage: "Interrupted." }
34668
+ );
34653
34669
  process.exitCode = 130;
34654
34670
  return;
34655
34671
  }
@@ -37251,7 +37267,7 @@ function registerConnectorCommand(program) {
37251
37267
  }
37252
37268
 
37253
37269
  // src/lib/manifest-input.ts
37254
- import { CliError as CliError3, getErrorMessage as getErrorMessage7 } from "@doist/cli-core";
37270
+ import { CliError as CliError4, getErrorMessage as getErrorMessage7 } from "@doist/cli-core";
37255
37271
  import { Option as Option6 } from "commander";
37256
37272
  import { readFile as readFile2 } from "node:fs/promises";
37257
37273
  import { text } from "node:stream/consumers";
@@ -37265,7 +37281,7 @@ async function readManifestInput(options, input2 = process.stdin) {
37265
37281
  try {
37266
37282
  return JSON.parse(source.content);
37267
37283
  } catch (error62) {
37268
- throw new CliError3(
37284
+ throw new CliError4(
37269
37285
  "MANIFEST_UNREADABLE",
37270
37286
  `${source.label} is not valid JSON: ${getErrorMessage7(error62)}`
37271
37287
  );
@@ -37276,7 +37292,7 @@ async function readSource(options, input2) {
37276
37292
  try {
37277
37293
  return { label: options.file, content: await readFile2(options.file, "utf8") };
37278
37294
  } catch (error62) {
37279
- throw new CliError3(
37295
+ throw new CliError4(
37280
37296
  "MANIFEST_UNREADABLE",
37281
37297
  `Could not read ${options.file}: ${getErrorMessage7(error62)}`
37282
37298
  );
@@ -37286,17 +37302,17 @@ async function readSource(options, input2) {
37286
37302
  try {
37287
37303
  return { label: "Standard input", content: await text(input2) };
37288
37304
  } catch (error62) {
37289
- throw new CliError3(
37305
+ throw new CliError4(
37290
37306
  "MANIFEST_UNREADABLE",
37291
37307
  `Could not read standard input: ${getErrorMessage7(error62)}`
37292
37308
  );
37293
37309
  }
37294
37310
  }
37295
- throw new CliError3("MANIFEST_UNREADABLE", "Provide the manifest with --file <path> or --stdin.");
37311
+ throw new CliError4("MANIFEST_UNREADABLE", "Provide the manifest with --file <path> or --stdin.");
37296
37312
  }
37297
37313
 
37298
37314
  // src/commands/draft/draft-service.ts
37299
- import { CliError as CliError4, getErrorMessage as getErrorMessage8 } from "@doist/cli-core";
37315
+ import { CliError as CliError5, getErrorMessage as getErrorMessage8 } from "@doist/cli-core";
37300
37316
  import { readFile as readFile3 } from "node:fs/promises";
37301
37317
  function createDraft2(draftApi, input2) {
37302
37318
  return withRecoveryHints(draftApi, createDraft(draftApi, input2));
@@ -37329,13 +37345,13 @@ async function withRecoveryHints(draftApi, operation) {
37329
37345
  const draftId = error62.draftId ?? "<draft-id>";
37330
37346
  switch (error62.code) {
37331
37347
  case "manifest_invalid":
37332
- throw new CliError4("MANIFEST_INVALID", error62.message);
37348
+ throw new CliError5("MANIFEST_INVALID", error62.message);
37333
37349
  case "draft_conflict":
37334
- throw new CliError4("DRAFT_CONFLICT", error62.message, {
37350
+ throw new CliError5("DRAFT_CONFLICT", error62.message, {
37335
37351
  hints: draftHints(scope, draftId)
37336
37352
  });
37337
37353
  case "draft_manifest_rejected":
37338
- throw new CliError4("DRAFT_MANIFEST_REJECTED", error62.message, {
37354
+ throw new CliError5("DRAFT_MANIFEST_REJECTED", error62.message, {
37339
37355
  hints: draftHints(scope, draftId)
37340
37356
  });
37341
37357
  case "draft_not_ready":
@@ -37380,7 +37396,12 @@ async function startDraftTest2(draftApi, {
37380
37396
  if (!waitFor) {
37381
37397
  return { action: "test_started", ...identity, status: started.status };
37382
37398
  }
37383
- const finished = await waitForDraftTest(draftApi, started, { ...waitFor, signal });
37399
+ let finished;
37400
+ try {
37401
+ finished = await waitForDraftTest(draftApi, started, { ...waitFor, signal });
37402
+ } catch (error62) {
37403
+ throw toDraftTestWaitError(error62, identity, signal);
37404
+ }
37384
37405
  return {
37385
37406
  action: "test_finished",
37386
37407
  ...identity,
@@ -37391,9 +37412,25 @@ async function startDraftTest2(draftApi, {
37391
37412
  result: finished.result
37392
37413
  };
37393
37414
  }
37415
+ function toDraftTestWaitError(error62, identity, signal) {
37416
+ const run = `test run ${identity.testRunId} of draft ${identity.draftId}`;
37417
+ const hints = [`See how it ends: ${identity.command}`];
37418
+ if (signal.aborted) {
37419
+ return new CliError5(
37420
+ "DRAFT_TEST_WAIT_INTERRUPTED",
37421
+ `Stopped waiting for ${run}. It keeps running.`,
37422
+ { hints }
37423
+ );
37424
+ }
37425
+ return new CliError5(
37426
+ "DRAFT_TEST_WAIT_FAILED",
37427
+ `Checking on ${run} failed: ${getErrorMessage8(error62).replace(/\.?$/, ".")} It keeps running.`,
37428
+ { hints }
37429
+ );
37430
+ }
37394
37431
  function draftNotReadyError(message, draftId, scope) {
37395
37432
  const flag = scopeFlag(scope);
37396
- return new CliError4("DRAFT_NOT_READY", message, {
37433
+ return new CliError5("DRAFT_NOT_READY", message, {
37397
37434
  hints: [
37398
37435
  `See the issues: tda draft view ${draftId} ${flag}`,
37399
37436
  `Bind connections: tda draft connect ${draftId} --auto ${flag}`
@@ -37407,13 +37444,13 @@ function toDraftTestCliError(refusal, draftId, scope) {
37407
37444
  return draftNotReadyError(refusal.message, draftId, scope);
37408
37445
  case "draft_test_payload_required":
37409
37446
  case "draft_test_payload_filtered":
37410
- return new CliError4(refusal.code.toUpperCase(), refusal.message, {
37447
+ return new CliError5(refusal.code.toUpperCase(), refusal.message, {
37411
37448
  hints: [
37412
37449
  `Test with a real event: tda draft test ${draftId} --trigger <event> --payload-file <path> ${flag}`
37413
37450
  ]
37414
37451
  });
37415
37452
  default:
37416
- return new CliError4(refusal.code.toUpperCase(), refusal.message);
37453
+ return new CliError5(refusal.code.toUpperCase(), refusal.message);
37417
37454
  }
37418
37455
  }
37419
37456
  async function readTestPayloadFile(path) {
@@ -37421,7 +37458,7 @@ async function readTestPayloadFile(path) {
37421
37458
  try {
37422
37459
  content = await readFile3(path, "utf8");
37423
37460
  } catch (error62) {
37424
- throw new CliError4(
37461
+ throw new CliError5(
37425
37462
  "PAYLOAD_UNREADABLE",
37426
37463
  `Could not read ${path}: ${getErrorMessage8(error62)}`
37427
37464
  );
@@ -37429,7 +37466,7 @@ async function readTestPayloadFile(path) {
37429
37466
  try {
37430
37467
  return JSON.parse(content);
37431
37468
  } catch (error62) {
37432
- throw new CliError4(
37469
+ throw new CliError5(
37433
37470
  "PAYLOAD_UNREADABLE",
37434
37471
  `${path} is not valid JSON: ${getErrorMessage8(error62)}`
37435
37472
  );
@@ -37540,7 +37577,13 @@ function describeDiff(diff) {
37540
37577
  }
37541
37578
  if (diff.steps.added.length > 0) parts.push(`steps added ${diff.steps.added.join(", ")}`);
37542
37579
  if (diff.steps.removed.length > 0) parts.push(`steps removed ${diff.steps.removed.join(", ")}`);
37543
- if (diff.steps.changed.length > 0) parts.push(`steps changed ${diff.steps.changed.join(", ")}`);
37580
+ if (diff.steps.changed.length > 0) {
37581
+ const changed = diff.steps.changed.map((id) => {
37582
+ const fields = diff.steps.changedFields[id] ?? [];
37583
+ return fields.length > 0 ? `${id} (${fields.join(", ")})` : id;
37584
+ });
37585
+ parts.push(`steps changed ${changed.join(", ")}`);
37586
+ }
37544
37587
  if (diff.steps.reordered) parts.push("steps reordered");
37545
37588
  return parts.join("; ");
37546
37589
  }
@@ -37612,7 +37655,7 @@ function formatDraftTest(result) {
37612
37655
  }
37613
37656
 
37614
37657
  // src/commands/draft/validate-command.ts
37615
- import { CliError as CliError5 } from "@doist/cli-core";
37658
+ import { CliError as CliError6 } from "@doist/cli-core";
37616
37659
 
37617
37660
  // src/lib/local-command.ts
37618
37661
  async function runLocalCommand(command, operation, presentation, execution = {}) {
@@ -37644,7 +37687,7 @@ function registerDraftValidateCommand(draft) {
37644
37687
  function validateManifestInput(manifest) {
37645
37688
  const validation = validateManifest(manifest);
37646
37689
  if (!validation.isValid) {
37647
- throw new CliError5(
37690
+ throw new CliError6(
37648
37691
  "MANIFEST_INVALID",
37649
37692
  `Manifest is invalid: ${validation.errors.join("; ")}`
37650
37693
  );
@@ -37993,11 +38036,11 @@ function registerRunCommand(program) {
37993
38036
  }
37994
38037
 
37995
38038
  // src/commands/skill/shared.ts
37996
- import { CliError as CliError6 } from "@doist/cli-core";
38039
+ import { CliError as CliError7 } from "@doist/cli-core";
37997
38040
  function requireInstaller(agent) {
37998
38041
  const installer = getInstaller(agent);
37999
38042
  if (!installer) {
38000
- throw new CliError6("UNKNOWN_AGENT", `Unknown agent: ${agent}.`, {
38043
+ throw new CliError7("UNKNOWN_AGENT", `Unknown agent: ${agent}.`, {
38001
38044
  hints: [`Available agents: ${AGENT_NAMES.join(", ")}.`]
38002
38045
  });
38003
38046
  }
@@ -38075,7 +38118,7 @@ async function uninstallSkill(agent, options, command) {
38075
38118
  }
38076
38119
 
38077
38120
  // src/commands/skill/update.ts
38078
- import { CliError as CliError7 } from "@doist/cli-core";
38121
+ import { CliError as CliError8 } from "@doist/cli-core";
38079
38122
  async function updateSkill(agent, options, command) {
38080
38123
  await runLocalCommand(
38081
38124
  command,
@@ -38101,7 +38144,7 @@ async function updateAllSkills(options, command) {
38101
38144
  async () => {
38102
38145
  const result = await updateAllInstalledSkills(options.local === true);
38103
38146
  if (result.errors.length > 0) {
38104
- throw new CliError7(
38147
+ throw new CliError8(
38105
38148
  "UPDATE_FAILED",
38106
38149
  `Could not update ${result.errors.length} skill file(s).`,
38107
38150
  { hints: result.errors.map((error62) => `${error62.agent}: ${error62.message}`) }