@doist/automations-cli 1.21.0 → 1.21.2

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
@@ -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.0",
6902
+ version: "1.21.2",
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.0",
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",
@@ -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.3"
6962
+ vitest: "5.0.4"
6963
6963
  },
6964
6964
  engines: {
6965
6965
  node: ">=24"
@@ -7878,6 +7878,7 @@ __export(util_exports, {
7878
7878
  required: () => required,
7879
7879
  safeExtend: () => safeExtend,
7880
7880
  shallowClone: () => shallowClone,
7881
+ sizeCaps: () => sizeCaps,
7881
7882
  slugify: () => slugify,
7882
7883
  stringifyPrimitive: () => stringifyPrimitive,
7883
7884
  toZod: () => toZod,
@@ -8397,6 +8398,18 @@ function prefixIssues(path, issues) {
8397
8398
  function unwrapMessage(message) {
8398
8399
  return typeof message === "string" ? message : message?.message;
8399
8400
  }
8401
+ function sizeCaps(defChecks) {
8402
+ const caps = [];
8403
+ for (const ch of defChecks ?? []) {
8404
+ const d = ch._zod.def;
8405
+ if (!/_(length|size)$|_equals$/.test(d.check))
8406
+ break;
8407
+ const max = d.maximum ?? d.length ?? d.size;
8408
+ if (max >= 0)
8409
+ caps.push([max, ch]);
8410
+ }
8411
+ return caps;
8412
+ }
8400
8413
  function attachSchema(issues, start, inst) {
8401
8414
  var _a3;
8402
8415
  for (let i = start; i < issues.length; i++) {
@@ -9817,7 +9830,7 @@ ${content.join("\n")}
9817
9830
  var version = {
9818
9831
  major: 4,
9819
9832
  minor: 6,
9820
- patch: 5
9833
+ patch: 6
9821
9834
  };
9822
9835
 
9823
9836
  // ../../node_modules/zod/v4/core/schemas.js
@@ -10573,6 +10586,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
10573
10586
  return payload;
10574
10587
  };
10575
10588
  });
10589
+ function capSize(inst, size) {
10590
+ const caps = sizeCaps(inst._zod.def.checks);
10591
+ if (!caps.length)
10592
+ return;
10593
+ const cap = Math.min(...caps.map((c) => c[0]));
10594
+ const parse3 = inst._zod.parse;
10595
+ inst._zod.parse = (payload, ctx) => {
10596
+ const n = size(payload.value);
10597
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
10598
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
10599
+ if (n > max && ch._zod.def.when(payload)) {
10600
+ const start = payload.issues.length;
10601
+ ch._zod.check(payload);
10602
+ payload.issues[start].continue = false;
10603
+ attachSchema(payload.issues, start, inst);
10604
+ return payload;
10605
+ }
10606
+ }
10607
+ }
10608
+ return parse3(payload, ctx);
10609
+ };
10610
+ }
10611
+ var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
10612
+ var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
10613
+ var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
10576
10614
  function handleArrayResult(result, final, index) {
10577
10615
  if (result.issues.length) {
10578
10616
  final.issues.push(...prefixIssues(index, result.issues));
@@ -10616,6 +10654,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
10616
10654
  }
10617
10655
  return payload;
10618
10656
  };
10657
+ capSize(inst, arraySize);
10619
10658
  });
10620
10659
  function handlePropertyResult(result, final, key, input2, optin, optout) {
10621
10660
  const isPresent = key in input2;
@@ -11572,6 +11611,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
11572
11611
  return Promise.all(proms).then(() => payload);
11573
11612
  return payload;
11574
11613
  };
11614
+ capSize(inst, mapSize);
11575
11615
  });
11576
11616
  function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
11577
11617
  if (keyResult.issues.length) {
@@ -11638,6 +11678,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
11638
11678
  return Promise.all(proms).then(() => payload);
11639
11679
  return payload;
11640
11680
  };
11681
+ capSize(inst, setSize);
11641
11682
  });
11642
11683
  function handleSetResult(result, final) {
11643
11684
  if (result.issues.length) {
@@ -21168,9 +21209,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
21168
21209
  doc.write(`}`);
21169
21210
  return outputVar;
21170
21211
  }
21212
+ function generateSizeGate(doc, schema, size) {
21213
+ for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
21214
+ doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
21215
+ }
21216
+ }
21171
21217
  function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
21172
21218
  const def = schema._zod.def;
21173
21219
  doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
21220
+ generateSizeGate(doc, schema, `${accessor}.length`);
21174
21221
  const outputVar = buildsValue ? newVar(ctx) : null;
21175
21222
  const iVar = newVar(ctx);
21176
21223
  const elemVar = newVar(ctx);
@@ -21570,6 +21617,7 @@ function literalPropertyKey(ctx, key) {
21570
21617
  function generateMapCheck(doc, ctx, schema, accessor) {
21571
21618
  const def = schema._zod.def;
21572
21619
  doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
21620
+ generateSizeGate(doc, schema, `${accessor}.size`);
21573
21621
  const outputVar = newVar(ctx);
21574
21622
  const kVar = newVar(ctx);
21575
21623
  const valVar = newVar(ctx);
@@ -21586,6 +21634,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
21586
21634
  function generateSetCheck(doc, ctx, schema, accessor) {
21587
21635
  const def = schema._zod.def;
21588
21636
  doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
21637
+ generateSizeGate(doc, schema, `${accessor}.size`);
21589
21638
  const outputVar = newVar(ctx);
21590
21639
  const valVar = newVar(ctx);
21591
21640
  doc.write(`const ${outputVar} = new Set();`);
@@ -32872,125 +32921,520 @@ async function findAutomationsUsingConnection(api, connectionId, options) {
32872
32921
  }
32873
32922
  }
32874
32923
 
32875
- // src/lib/auth-environment.ts
32876
- import { InvalidArgumentError } from "commander";
32877
- var ENVIRONMENTS = {
32878
- prod: {
32879
- name: "prod",
32880
- resource: "https://automations.todoist.com",
32881
- apiUrl: DEFAULT_BASE_URL,
32882
- appUrl: "https://automations.todoist.com",
32883
- todoistApiUrl: DEFAULT_TODOIST_BASE_URL,
32884
- authorizationServerOrigin: "https://todoist.com",
32885
- clientId: "https://automations.todoist.com/api/.well-known/oauth-client-cli"
32886
- },
32887
- staging: {
32888
- name: "staging",
32889
- resource: "https://automations.staging.todoist.net",
32890
- apiUrl: "https://automations.staging.todoist.net/api",
32891
- appUrl: "https://automations.staging.todoist.net",
32892
- todoistApiUrl: "https://staging.todoist.com/api",
32893
- authorizationServerOrigin: "https://staging.todoist.com",
32894
- clientId: "https://automations.staging.todoist.net/api/.well-known/oauth-client-cli"
32895
- },
32896
- // The backend serves this document at APP_URL, and a client ID metadata
32897
- // document is only valid when its own client_id equals the URL it was
32898
- // fetched from. Local dev therefore needs APP_URL set to the Traefik host
32899
- // below; on the default http://localhost:24368 the authorization server can
32900
- // neither reach the document nor match what it contains.
32901
- dev: {
32902
- name: "dev",
32903
- resource: "https://local-agentist.todoist.com",
32904
- apiUrl: "https://local-agentist.todoist.com",
32905
- appUrl: "https://local-agentist.todoist.com",
32906
- todoistApiUrl: "https://local.todoist.com/api",
32907
- authorizationServerOrigin: "https://local.todoist.com",
32908
- clientId: "https://local-agentist.todoist.com/.well-known/oauth-client-cli"
32924
+ // ../../packages/sdk/dist/esm/drafts/draft-api.js
32925
+ function bindDraftApi(api, workspaceId, { signal } = {}) {
32926
+ const createOptions = { signal, translate: false };
32927
+ const listConnections = (args) => api.connections.list(args, { signal });
32928
+ if (workspaceId === null) {
32929
+ return {
32930
+ workspaceId: null,
32931
+ get: (draftId) => api.automationDrafts.get(draftId, { signal }),
32932
+ create: (manifest) => api.automationDrafts.create(manifest, createOptions),
32933
+ update: (draftId, manifest) => api.automationDrafts.update(draftId, manifest, { signal }),
32934
+ createAutomationDraft: (automationId) => api.automations.createDraft(automationId, { signal }),
32935
+ upsertConnection: (draftId, body) => api.automationDraftConnections.upsert(draftId, body, { signal }),
32936
+ getConnectionStatus: async (draftId) => (await api.automationDraftConnections.getStatus(draftId, { signal })).validation,
32937
+ listConnections,
32938
+ finalize: (draftId) => api.automationDrafts.finalize(draftId, { signal }),
32939
+ getAutomation: (automationId) => api.automations.get(automationId, { signal }),
32940
+ test: (draftId, request2) => api.automationDrafts.test(draftId, request2, { signal }),
32941
+ getTestRun: (draftId, runId, args, requestSignal) => api.automationDrafts.getTestRun(draftId, runId, args, {
32942
+ signal: requestSignal ?? signal
32943
+ })
32944
+ };
32909
32945
  }
32910
- };
32911
- var ENVIRONMENT_NAMES = Object.keys(ENVIRONMENTS);
32912
- var DEFAULT_ENVIRONMENT = "prod";
32913
- function authorizeUrl(target) {
32914
- return `${target.authorizationServerOrigin}/oauth/authorize`;
32915
- }
32916
- function tokenUrl(target) {
32917
- return `${target.authorizationServerOrigin}/oauth/access_token`;
32946
+ return {
32947
+ workspaceId,
32948
+ get: (draftId) => api.workspaceAutomationDrafts.get(workspaceId, draftId, { signal }),
32949
+ create: (manifest) => api.workspaceAutomationDrafts.create(workspaceId, manifest, createOptions),
32950
+ update: (draftId, manifest) => api.workspaceAutomationDrafts.update(workspaceId, draftId, manifest, { signal }),
32951
+ createAutomationDraft: (automationId) => api.workspaceAutomations.createDraft(workspaceId, automationId, { signal }),
32952
+ upsertConnection: (draftId, body) => api.workspaceAutomationDraftConnections.upsert(workspaceId, draftId, body, { signal }),
32953
+ getConnectionStatus: async (draftId) => (await api.workspaceAutomationDraftConnections.getStatus(workspaceId, draftId, {
32954
+ signal
32955
+ })).validation,
32956
+ listConnections,
32957
+ finalize: (draftId) => api.workspaceAutomationDrafts.finalize(workspaceId, draftId, { signal }),
32958
+ getAutomation: (automationId) => api.workspaceAutomations.get(workspaceId, automationId, { signal }),
32959
+ test: (draftId, request2) => api.workspaceAutomationDrafts.test(workspaceId, draftId, request2, { signal }),
32960
+ getTestRun: (draftId, runId, args, requestSignal) => api.workspaceAutomationDrafts.getTestRun(workspaceId, draftId, runId, args, {
32961
+ signal: requestSignal ?? signal
32962
+ })
32963
+ };
32918
32964
  }
32919
- function revokeUrl(target) {
32920
- return `${target.authorizationServerOrigin}/oauth/revoke_token`;
32965
+
32966
+ // ../../packages/sdk/dist/esm/drafts/draft-workflow-error.js
32967
+ var DraftWorkflowError = class extends Error {
32968
+ code;
32969
+ draftId;
32970
+ automationId;
32971
+ constructor(code, message, options = {}) {
32972
+ super(message, { cause: options.cause });
32973
+ this.name = "DraftWorkflowError";
32974
+ this.code = code;
32975
+ this.draftId = options.draftId;
32976
+ this.automationId = options.automationId;
32977
+ }
32978
+ };
32979
+ function getErrorMessage(error62) {
32980
+ return error62 instanceof Error ? error62.message : String(error62);
32921
32981
  }
32922
- function parseEnvironmentName(value) {
32923
- if (!isEnvironmentName(value)) {
32924
- throw new InvalidArgumentError(`Expected one of: ${ENVIRONMENT_NAMES.join(", ")}.`);
32982
+
32983
+ // ../../packages/sdk/dist/esm/drafts/draft-changes.js
32984
+ async function createDraft(draftApi, { manifest, automationId }) {
32985
+ const partialManifest = parsePartialManifest(manifest);
32986
+ if (automationId === void 0) {
32987
+ const result = await draftApi.create(partialManifest);
32988
+ return { action: "created", target: toDraftTarget(result.draft), ...result };
32989
+ }
32990
+ const { draft } = await openEditDraft(draftApi, automationId);
32991
+ try {
32992
+ const result = await draftApi.update(draft.id, partialManifest);
32993
+ return { action: "created", target: toDraftTarget(result.draft), ...result };
32994
+ } catch (error62) {
32995
+ if (!isManifestRejection(error62))
32996
+ throw error62;
32997
+ 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 });
32925
32998
  }
32926
- return value;
32927
32999
  }
32928
- function isEnvironmentName(value) {
32929
- return Object.hasOwn(ENVIRONMENTS, value);
33000
+ async function updateDraft(draftApi, { draftId, manifest }) {
33001
+ const partialManifest = parsePartialManifest(manifest);
33002
+ const result = await draftApi.update(draftId, partialManifest);
33003
+ return { action: "updated", target: toDraftTarget(result.draft), ...result };
32930
33004
  }
32931
- var selected = DEFAULT_ENVIRONMENT;
32932
- function setEnvironment(name) {
32933
- selected = name;
33005
+ function toDraftTarget(draft) {
33006
+ return draft.automationId === null ? { kind: "new" } : { kind: "automation", automationId: draft.automationId };
32934
33007
  }
32935
- function getEnvironment() {
32936
- return ENVIRONMENTS[selected];
33008
+ function parsePartialManifest(value) {
33009
+ const validation = validatePartialManifest(value);
33010
+ if (!validation.isValid) {
33011
+ throw new DraftWorkflowError("manifest_invalid", `Manifest is invalid: ${validation.errors.join("; ")}`);
33012
+ }
33013
+ return validation.data;
32937
33014
  }
32938
- function parseEnvironmentOption(value) {
32939
- const name = parseEnvironmentName(value);
32940
- setEnvironment(name);
32941
- return name;
33015
+ async function openEditDraft(draftApi, automationId) {
33016
+ try {
33017
+ return await draftApi.createAutomationDraft(automationId);
33018
+ } catch (error62) {
33019
+ const conflict = getOpenDraftConflictError(error62);
33020
+ if (!conflict)
33021
+ throw error62;
33022
+ throw new DraftWorkflowError("draft_conflict", `Automation ${automationId} already has an open draft ${conflict.draftId}.`, { draftId: conflict.draftId, automationId, cause: error62 });
33023
+ }
32942
33024
  }
32943
- function initialEnvironmentName() {
32944
- const configured = process.env.TDA_ENV;
32945
- if (!configured) {
32946
- setEnvironment(DEFAULT_ENVIRONMENT);
32947
- return DEFAULT_ENVIRONMENT;
33025
+ function isManifestRejection(error62) {
33026
+ return error62 instanceof AutomationsRequestError && (error62.httpStatusCode === 400 || error62.httpStatusCode === 422);
33027
+ }
33028
+
33029
+ // ../../packages/sdk/dist/esm/drafts/connect-draft.js
33030
+ async function connectDraft(draftApi, { draftId, selections, auto }) {
33031
+ const bound = [];
33032
+ const required2 = selections.length > 0 ? getRequiredConnectorsFromManifest((await draftApi.get(draftId)).draft.partialManifest) : /* @__PURE__ */ new Map();
33033
+ const explicit = selections.flatMap((selection) => {
33034
+ const types = required2.get(selection.connectorId);
33035
+ if (!types || selection.connectionType && !types.has(selection.connectionType)) {
33036
+ const slot = selection.connectionType ? `${selection.connectorId} (${selection.connectionType})` : selection.connectorId;
33037
+ throw new DraftWorkflowError("connector_not_required", `Connector ${slot} is not used by draft ${draftId}. Required: ${describeRequired(required2)}.`, { draftId });
33038
+ }
33039
+ const connectionTypes = selection.connectionType ? [selection.connectionType] : [...types];
33040
+ return connectionTypes.map((connectionType) => ({ ...selection, connectionType }));
33041
+ });
33042
+ for (const { connectorId, connectionType, connectionId } of explicit) {
33043
+ await draftApi.upsertConnection(draftId, { connectorId, connectionType, connectionId });
33044
+ bound.push({ connectorId, connectionType, connectionId, mode: "explicit" });
32948
33045
  }
32949
- if (!isEnvironmentName(configured)) {
32950
- throw new Error(
32951
- `Invalid TDA_ENV '${configured}'. Expected one of: ${ENVIRONMENT_NAMES.join(", ")}.`
32952
- );
33046
+ const unresolved = [];
33047
+ if (auto) {
33048
+ const status = await draftApi.getConnectionStatus(draftId);
33049
+ for (const connector of status.connectors) {
33050
+ for (const connectionType of ["mcp", "trigger"]) {
33051
+ const slot = connector[connectionType];
33052
+ if (!slot.required || slot.status !== "missing")
33053
+ continue;
33054
+ const { connections } = await draftApi.listConnections({
33055
+ connectorId: connector.id,
33056
+ capability: connectionType,
33057
+ status: "authorized"
33058
+ });
33059
+ const [only] = connections;
33060
+ if (connections.length === 1 && only) {
33061
+ await draftApi.upsertConnection(draftId, {
33062
+ connectorId: connector.id,
33063
+ connectionType,
33064
+ connectionId: only.id
33065
+ });
33066
+ bound.push({
33067
+ connectorId: connector.id,
33068
+ connectionType,
33069
+ connectionId: only.id,
33070
+ mode: "auto"
33071
+ });
33072
+ continue;
33073
+ }
33074
+ unresolved.push({
33075
+ connectorId: connector.id,
33076
+ connectionType,
33077
+ reason: connections.length === 0 ? "no_connection" : "multiple_connections",
33078
+ candidateConnectionIds: connections.map((connection) => connection.id)
33079
+ });
33080
+ }
33081
+ }
32953
33082
  }
32954
- setEnvironment(configured);
32955
- return configured;
33083
+ const { isValid, missing } = await draftApi.getConnectionStatus(draftId);
33084
+ return { action: "connected", draftId, bound, unresolved, isValid, missing };
32956
33085
  }
32957
-
32958
- // src/lib/auth-records.ts
32959
- var CONFIG_VERSION = 1;
32960
- async function getDefaultWorkspaceId() {
32961
- return getEnvironmentDefault("default_workspace", getEnvironment().name);
33086
+ function describeRequired(required2) {
33087
+ if (required2.size === 0)
33088
+ return "none";
33089
+ return [...required2].map(([connectorId, types]) => `${connectorId} (${[...types].join(", ")})`).join(", ");
32962
33090
  }
32963
- async function setDefaultWorkspaceId(workspaceId) {
32964
- await setEnvironmentDefault("default_workspace", getEnvironment().name, workspaceId);
33091
+
33092
+ // ../../packages/sdk/dist/esm/drafts/manifest-diff.js
33093
+ function stableStringify(value) {
33094
+ return JSON.stringify(sortKeys(value));
32965
33095
  }
32966
- async function getEnvironmentDefault(key, environment) {
32967
- return (await read())[key]?.[environment] ?? null;
33096
+ function isDeepEqual(left, right) {
33097
+ return stableStringify(left) === stableStringify(right);
32968
33098
  }
32969
- async function setEnvironmentDefault(key, environment, id) {
32970
- const config2 = await read();
32971
- const defaults = { ...config2[key] };
32972
- if (id === null) delete defaults[environment];
32973
- else defaults[environment] = id;
32974
- await write({ ...config2, [key]: defaults });
33099
+ function diffManifests(before, after) {
33100
+ const previous = asManifest(before);
33101
+ const next = asManifest(after);
33102
+ const changedProperties = [.../* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => key !== "steps").filter((key) => !isDeepEqual(previous[key], next[key])).sort();
33103
+ const previousSteps = stepsById(previous.steps);
33104
+ const nextSteps = stepsById(next.steps);
33105
+ const added = [...nextSteps.keys()].filter((id) => !previousSteps.has(id));
33106
+ const removed = [...previousSteps.keys()].filter((id) => !nextSteps.has(id));
33107
+ const changed = [...nextSteps].filter(([id, step]) => previousSteps.has(id) && !isDeepEqual(previousSteps.get(id), step)).map(([id]) => id);
33108
+ const sharedBefore = [...previousSteps.keys()].filter((id) => nextSteps.has(id));
33109
+ const sharedAfter = [...nextSteps.keys()].filter((id) => previousSteps.has(id));
33110
+ const reordered = sharedBefore.some((id, index) => sharedAfter[index] !== id);
33111
+ return {
33112
+ changedProperties,
33113
+ steps: { added, removed, changed, reordered },
33114
+ isEmpty: changedProperties.length === 0 && added.length === 0 && removed.length === 0 && changed.length === 0 && !reordered
33115
+ };
32975
33116
  }
32976
- function configPath() {
32977
- return getConfigPath(SERVICE_NAME);
33117
+ function asManifest(value) {
33118
+ return isManifestRecord(value) ? value : {};
32978
33119
  }
32979
- async function read() {
32980
- return readConfig(configPath());
33120
+ function stepsById(steps) {
33121
+ const byId = /* @__PURE__ */ new Map();
33122
+ if (!Array.isArray(steps))
33123
+ return byId;
33124
+ steps.forEach((step, index) => {
33125
+ const id = isManifestRecord(step) && typeof step.id === "string" ? step.id : `#${index}`;
33126
+ byId.set(id, step);
33127
+ });
33128
+ return byId;
32981
33129
  }
32982
- async function write(config2) {
32983
- await writeConfig(configPath(), { ...config2, config_version: CONFIG_VERSION });
33130
+ function sortKeys(value) {
33131
+ if (Array.isArray(value))
33132
+ return value.map(sortKeys);
33133
+ if (!isManifestRecord(value))
33134
+ return value;
33135
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortKeys(value[key])]));
32984
33136
  }
32985
- function toRecord(stored) {
32986
- return {
32987
- account: {
32988
- id: stored.id,
32989
- label: stored.label ?? stored.email,
32990
- environment: stored.environment,
32991
- user_id: stored.user_id,
32992
- email: stored.email,
32993
- scope: stored.scope
33137
+
33138
+ // ../../packages/sdk/dist/esm/drafts/publish-draft.js
33139
+ async function publishDraft(draftApi, { draftId, confirmed }) {
33140
+ const { summary, manifest } = await summarizeForPublish(draftApi, draftId);
33141
+ if (!confirmed) {
33142
+ return { action: "publish_preview", summary };
33143
+ }
33144
+ let published;
33145
+ try {
33146
+ published = await draftApi.finalize(draftId);
33147
+ } catch (error62) {
33148
+ const notReady = getDraftNotReadyError(error62);
33149
+ if (!notReady)
33150
+ throw error62;
33151
+ throw new DraftWorkflowError("draft_not_ready", notReady.message, {
33152
+ draftId,
33153
+ cause: error62
33154
+ });
33155
+ }
33156
+ let automation;
33157
+ try {
33158
+ automation = await draftApi.getAutomation(published.automation.id);
33159
+ } catch (error62) {
33160
+ return {
33161
+ action: "published",
33162
+ summary,
33163
+ automation: published.automation,
33164
+ verification: { status: "unavailable", reason: getErrorMessage(error62) }
33165
+ };
33166
+ }
33167
+ const expected = normaliseManifest(manifest);
33168
+ const actual = normaliseManifest(automation.manifest);
33169
+ return {
33170
+ action: "published",
33171
+ summary,
33172
+ automation,
33173
+ verification: isDeepEqual(expected, actual) ? { status: "matched" } : { status: "mismatched", mismatch: diffManifests(expected, actual) }
33174
+ };
33175
+ }
33176
+ async function summarizeForPublish(draftApi, draftId) {
33177
+ const [detail, connections] = await Promise.all([
33178
+ draftApi.get(draftId),
33179
+ draftApi.getConnectionStatus(draftId)
33180
+ ]);
33181
+ const { draft } = detail;
33182
+ const target = toDraftTarget(draft);
33183
+ const published = target.kind === "automation" ? await draftApi.getAutomation(target.automationId) : null;
33184
+ const diff = published ? diffManifests(normaliseManifest(published.manifest), normaliseManifest(draft.partialManifest)) : null;
33185
+ const summary = {
33186
+ draftId,
33187
+ name: readManifestName(draft.partialManifest),
33188
+ scope: draft.workspaceId === null ? { kind: "personal" } : {
33189
+ kind: "workspace",
33190
+ workspaceId: draft.workspaceId,
33191
+ // A new workspace automation publishes team-visible; an
33192
+ // edit keeps whatever the automation already has.
33193
+ visibility: published?.visibility ?? "workspace"
33194
+ },
33195
+ target,
33196
+ enabled: published?.enabled ?? true,
33197
+ validation: { state: detail.state, issues: detail.issues },
33198
+ connections: {
33199
+ isValid: connections.isValid,
33200
+ connected: connections.connected.map(({ connectorId, connectionType, connectionId }) => ({
33201
+ connectorId,
33202
+ connectionType,
33203
+ connectionId
33204
+ })),
33205
+ missing: connections.missing
33206
+ },
33207
+ schedule: describeSchedule(draft.partialManifest),
33208
+ diff
33209
+ };
33210
+ return { summary, manifest: draft.partialManifest };
33211
+ }
33212
+ function describeSchedule(manifest) {
33213
+ if (!hasTriggerType(manifest))
33214
+ return null;
33215
+ const schedule = getManifestTriggers(manifest).find((trigger) => trigger.type === "schedule");
33216
+ if (!schedule || schedule.type !== "schedule")
33217
+ return null;
33218
+ return getScheduleTriggerDescription({ value: schedule.value, timeFormat: "24h" });
33219
+ }
33220
+ function normaliseManifest(manifest) {
33221
+ const parsed = manifestSchema.safeParse(manifest);
33222
+ return parsed.success ? parsed.data : manifest;
33223
+ }
33224
+ function readManifestName(manifest) {
33225
+ if (!isManifestRecord(manifest))
33226
+ return "(unnamed)";
33227
+ const { name } = manifest;
33228
+ return typeof name === "string" && name.trim() ? name : "(unnamed)";
33229
+ }
33230
+
33231
+ // ../../packages/sdk/dist/esm/drafts/draft-test.js
33232
+ var DEFAULT_POLL_INTERVALS_MS = { first: 2e3, max: 1e4 };
33233
+ var STATUS_REQUEST_GRACE_MS = 1e4;
33234
+ var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set([
33235
+ "pending",
33236
+ "running"
33237
+ ]);
33238
+ async function startDraftTest(draftApi, { draftId, request: request2 }) {
33239
+ const started = await draftApi.test(draftId, request2);
33240
+ return {
33241
+ draftId,
33242
+ testRunId: started.testRunId,
33243
+ trigger: started.trigger,
33244
+ connectorCalls: started.connectorCalls,
33245
+ status: started.status
33246
+ };
33247
+ }
33248
+ async function waitForDraftTest(draftApi, { draftId, testRunId }, { timeoutMs, pollIntervalsMs, requestGraceMs, onPoll, signal }) {
33249
+ const intervals = pollIntervalsMs ?? DEFAULT_POLL_INTERVALS_MS;
33250
+ const deadline = Date.now() + timeoutMs;
33251
+ let intervalMs = intervals.first;
33252
+ const requestTimeout = AbortSignal.timeout(timeoutMs + (requestGraceMs ?? STATUS_REQUEST_GRACE_MS));
33253
+ const requestSignal = AbortSignal.any([signal, requestTimeout]);
33254
+ let last = null;
33255
+ while (true) {
33256
+ try {
33257
+ last = await draftApi.getTestRun(draftId, testRunId, void 0, requestSignal);
33258
+ } catch (error62) {
33259
+ if (signal.aborted || !requestTimeout.aborted)
33260
+ throw error62;
33261
+ return toFinished(last, "timed_out");
33262
+ }
33263
+ if (!OPEN_RUN_STATUSES.has(last.status)) {
33264
+ return toFinished(last, toOutcome(last.status));
33265
+ }
33266
+ onPoll?.(last);
33267
+ const remainingMs = deadline - Date.now();
33268
+ if (remainingMs <= 0) {
33269
+ return toFinished(last, "timed_out");
33270
+ }
33271
+ await delay(Math.min(intervalMs, remainingMs), signal);
33272
+ intervalMs = Math.min(intervalMs * 2, intervals.max);
33273
+ }
33274
+ }
33275
+ function toDraftTestTriggerSelector(value) {
33276
+ if (typeof value === "number")
33277
+ return { index: value };
33278
+ return /^\d+$/.test(value) ? { index: Number(value) } : { event: value };
33279
+ }
33280
+ function toFinished(run, outcome) {
33281
+ return {
33282
+ outcome,
33283
+ status: run?.status ?? "pending",
33284
+ failedStep: run?.failedStep ?? null,
33285
+ 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),
33286
+ result: run?.result ?? null
33287
+ };
33288
+ }
33289
+ function toOutcome(status) {
33290
+ switch (status) {
33291
+ case "completed":
33292
+ return "passed";
33293
+ case "skipped":
33294
+ return "skipped";
33295
+ case "cancelled":
33296
+ return "cancelled";
33297
+ default:
33298
+ return "failed";
33299
+ }
33300
+ }
33301
+ function delay(ms, signal) {
33302
+ return new Promise((resolve, reject) => {
33303
+ if (signal.aborted) {
33304
+ reject(signal.reason);
33305
+ return;
33306
+ }
33307
+ const onAbort = () => {
33308
+ clearTimeout(timer);
33309
+ reject(signal.reason);
33310
+ };
33311
+ const timer = setTimeout(() => {
33312
+ signal.removeEventListener("abort", onAbort);
33313
+ resolve();
33314
+ }, ms);
33315
+ signal.addEventListener("abort", onAbort, { once: true });
33316
+ });
33317
+ }
33318
+
33319
+ // src/lib/auth-environment.ts
33320
+ import { InvalidArgumentError } from "commander";
33321
+ var ENVIRONMENTS = {
33322
+ prod: {
33323
+ name: "prod",
33324
+ resource: "https://automations.todoist.com",
33325
+ apiUrl: DEFAULT_BASE_URL,
33326
+ appUrl: "https://automations.todoist.com",
33327
+ todoistApiUrl: DEFAULT_TODOIST_BASE_URL,
33328
+ authorizationServerOrigin: "https://todoist.com",
33329
+ clientId: "https://automations.todoist.com/api/.well-known/oauth-client-cli"
33330
+ },
33331
+ staging: {
33332
+ name: "staging",
33333
+ resource: "https://automations.staging.todoist.net",
33334
+ apiUrl: "https://automations.staging.todoist.net/api",
33335
+ appUrl: "https://automations.staging.todoist.net",
33336
+ todoistApiUrl: "https://staging.todoist.com/api",
33337
+ authorizationServerOrigin: "https://staging.todoist.com",
33338
+ clientId: "https://automations.staging.todoist.net/api/.well-known/oauth-client-cli"
33339
+ },
33340
+ // The backend serves this document at APP_URL, and a client ID metadata
33341
+ // document is only valid when its own client_id equals the URL it was
33342
+ // fetched from. Local dev therefore needs APP_URL set to the Traefik host
33343
+ // below; on the default http://localhost:24368 the authorization server can
33344
+ // neither reach the document nor match what it contains.
33345
+ dev: {
33346
+ name: "dev",
33347
+ resource: "https://local-agentist.todoist.com",
33348
+ apiUrl: "https://local-agentist.todoist.com",
33349
+ appUrl: "https://local-agentist.todoist.com",
33350
+ todoistApiUrl: "https://local.todoist.com/api",
33351
+ authorizationServerOrigin: "https://local.todoist.com",
33352
+ clientId: "https://local-agentist.todoist.com/.well-known/oauth-client-cli"
33353
+ }
33354
+ };
33355
+ var ENVIRONMENT_NAMES = Object.keys(ENVIRONMENTS);
33356
+ var DEFAULT_ENVIRONMENT = "prod";
33357
+ function authorizeUrl(target) {
33358
+ return `${target.authorizationServerOrigin}/oauth/authorize`;
33359
+ }
33360
+ function tokenUrl(target) {
33361
+ return `${target.authorizationServerOrigin}/oauth/access_token`;
33362
+ }
33363
+ function revokeUrl(target) {
33364
+ return `${target.authorizationServerOrigin}/oauth/revoke_token`;
33365
+ }
33366
+ function parseEnvironmentName(value) {
33367
+ if (!isEnvironmentName(value)) {
33368
+ throw new InvalidArgumentError(`Expected one of: ${ENVIRONMENT_NAMES.join(", ")}.`);
33369
+ }
33370
+ return value;
33371
+ }
33372
+ function isEnvironmentName(value) {
33373
+ return Object.hasOwn(ENVIRONMENTS, value);
33374
+ }
33375
+ var selected = DEFAULT_ENVIRONMENT;
33376
+ function setEnvironment(name) {
33377
+ selected = name;
33378
+ }
33379
+ function getEnvironment() {
33380
+ return ENVIRONMENTS[selected];
33381
+ }
33382
+ function parseEnvironmentOption(value) {
33383
+ const name = parseEnvironmentName(value);
33384
+ setEnvironment(name);
33385
+ return name;
33386
+ }
33387
+ function initialEnvironmentName() {
33388
+ const configured = process.env.TDA_ENV;
33389
+ if (!configured) {
33390
+ setEnvironment(DEFAULT_ENVIRONMENT);
33391
+ return DEFAULT_ENVIRONMENT;
33392
+ }
33393
+ if (!isEnvironmentName(configured)) {
33394
+ throw new Error(
33395
+ `Invalid TDA_ENV '${configured}'. Expected one of: ${ENVIRONMENT_NAMES.join(", ")}.`
33396
+ );
33397
+ }
33398
+ setEnvironment(configured);
33399
+ return configured;
33400
+ }
33401
+
33402
+ // src/lib/auth-records.ts
33403
+ var CONFIG_VERSION = 1;
33404
+ async function getDefaultWorkspaceId() {
33405
+ return getEnvironmentDefault("default_workspace", getEnvironment().name);
33406
+ }
33407
+ async function setDefaultWorkspaceId(workspaceId) {
33408
+ await setEnvironmentDefault("default_workspace", getEnvironment().name, workspaceId);
33409
+ }
33410
+ async function getEnvironmentDefault(key, environment) {
33411
+ return (await read())[key]?.[environment] ?? null;
33412
+ }
33413
+ async function setEnvironmentDefault(key, environment, id) {
33414
+ const config2 = await read();
33415
+ const defaults = { ...config2[key] };
33416
+ if (id === null) delete defaults[environment];
33417
+ else defaults[environment] = id;
33418
+ await write({ ...config2, [key]: defaults });
33419
+ }
33420
+ function configPath() {
33421
+ return getConfigPath(SERVICE_NAME);
33422
+ }
33423
+ async function read() {
33424
+ return readConfig(configPath());
33425
+ }
33426
+ async function write(config2) {
33427
+ await writeConfig(configPath(), { ...config2, config_version: CONFIG_VERSION });
33428
+ }
33429
+ function toRecord(stored) {
33430
+ return {
33431
+ account: {
33432
+ id: stored.id,
33433
+ label: stored.label ?? stored.email,
33434
+ environment: stored.environment,
33435
+ user_id: stored.user_id,
33436
+ email: stored.email,
33437
+ scope: stored.scope
32994
33438
  },
32995
33439
  fallbackToken: stored.api_token,
32996
33440
  fallbackRefreshToken: stored.refresh_token,
@@ -33095,7 +33539,7 @@ function createAutomationsTokenStore(options = {}) {
33095
33539
  }
33096
33540
 
33097
33541
  // src/lib/credentials.ts
33098
- import { CliError, getConfigPath as getConfigPath3, getErrorMessage as getErrorMessage3 } from "@doist/cli-core";
33542
+ import { CliError, getConfigPath as getConfigPath3, getErrorMessage as getErrorMessage4 } from "@doist/cli-core";
33099
33543
  import { refreshAccessToken } from "@doist/cli-core/auth";
33100
33544
 
33101
33545
  // src/lib/auth-scopes.ts
@@ -33185,7 +33629,7 @@ function formatScopesHelp() {
33185
33629
  }
33186
33630
 
33187
33631
  // src/lib/auth-provider.ts
33188
- import { getErrorMessage } from "@doist/cli-core";
33632
+ import { getErrorMessage as getErrorMessage2 } from "@doist/cli-core";
33189
33633
  import { createPkceProvider } from "@doist/cli-core/auth";
33190
33634
  var ERROR_HINTS = ["Try again: tda auth login"];
33191
33635
  function createAutomationsAuthProvider() {
@@ -33225,7 +33669,7 @@ async function fetchTodoistUser(token, todoistApiUrl) {
33225
33669
  });
33226
33670
  } catch (error62) {
33227
33671
  throw new Error(
33228
- `Could not reach Todoist to identify the account: ${getErrorMessage(error62)}`
33672
+ `Could not reach Todoist to identify the account: ${getErrorMessage2(error62)}`
33229
33673
  );
33230
33674
  }
33231
33675
  if (!response.ok) {
@@ -33238,7 +33682,7 @@ async function fetchTodoistUser(token, todoistApiUrl) {
33238
33682
  user = await response.json();
33239
33683
  } catch (error62) {
33240
33684
  throw new Error(
33241
- `Todoist returned an unreadable identity response: ${getErrorMessage(error62)}`
33685
+ `Todoist returned an unreadable identity response: ${getErrorMessage2(error62)}`
33242
33686
  );
33243
33687
  }
33244
33688
  return { id: requireUserId(user.id), email: typeof user.email === "string" ? user.email : "" };
@@ -33250,13 +33694,13 @@ function requireUserId(id) {
33250
33694
  }
33251
33695
 
33252
33696
  // src/lib/errors.ts
33253
- import { getErrorMessage as getErrorMessage2 } from "@doist/cli-core";
33697
+ import { getErrorMessage as getErrorMessage3 } from "@doist/cli-core";
33254
33698
  function getErrorCode(error62) {
33255
33699
  return typeof error62 === "object" && error62 !== null && "code" in error62 ? String(error62.code) : void 0;
33256
33700
  }
33257
33701
  function formatError2(error62) {
33258
33702
  const hints = errorHints(error62);
33259
- return [getErrorMessage2(error62), ...hints.map((hint) => ` - ${hint}`)].join("\n");
33703
+ return [getErrorMessage3(error62), ...hints.map((hint) => ` - ${hint}`)].join("\n");
33260
33704
  }
33261
33705
  function errorHints(error62) {
33262
33706
  if (typeof error62 !== "object" || error62 === null || !("hints" in error62)) return [];
@@ -33336,7 +33780,7 @@ function accessTokenAfterFailedRefresh(error62, snapshot) {
33336
33780
  if (hasTokenLifeLeft(snapshot)) return snapshot.bundle.accessToken;
33337
33781
  throw new CredentialError(
33338
33782
  "REFRESH_FAILED",
33339
- `Could not refresh your access token: ${getErrorMessage3(error62)}`,
33783
+ `Could not refresh your access token: ${getErrorMessage4(error62)}`,
33340
33784
  ["Check your network connection and try again."]
33341
33785
  );
33342
33786
  }
@@ -33364,19 +33808,19 @@ function resolveAfterStoreFailure(error62) {
33364
33808
  if (code !== "AUTH_STORE_READ_FAILED") {
33365
33809
  throw new CredentialError(
33366
33810
  "CREDENTIAL_UNREADABLE",
33367
- `Could not read the stored credential: ${getErrorMessage3(error62)}`,
33811
+ `Could not read the stored credential: ${getErrorMessage4(error62)}`,
33368
33812
  ["Run `tda auth logout` and then `tda auth login` to replace it."]
33369
33813
  );
33370
33814
  }
33371
33815
  const fallback = fromEnvironment();
33372
33816
  if (fallback) {
33373
33817
  process.stderr.write(
33374
- `Warning: could not read the stored credential (${getErrorMessage3(error62)}). Using ${TOKEN_ENV_VAR}.
33818
+ `Warning: could not read the stored credential (${getErrorMessage4(error62)}). Using ${TOKEN_ENV_VAR}.
33375
33819
  `
33376
33820
  );
33377
33821
  return fallback;
33378
33822
  }
33379
- throw new CredentialError("CREDENTIAL_UNREADABLE", getErrorMessage3(error62), [
33823
+ throw new CredentialError("CREDENTIAL_UNREADABLE", getErrorMessage4(error62), [
33380
33824
  `Set ${TOKEN_ENV_VAR} to bypass the credential store.`,
33381
33825
  "Or re-run `tda auth login --credential-store plaintext`."
33382
33826
  ]);
@@ -33477,156 +33921,7 @@ function parseHeaders(values) {
33477
33921
  }
33478
33922
 
33479
33923
  // src/lib/auth-html.ts
33480
- var ICON_SVG = `<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
33481
- <g id="Size=72, Style=Color">
33482
- <g id="AUTOMATIONS CLI">
33483
- <rect x="3" y="3" width="66" height="66" rx="12" fill="#858585"/>
33484
- <g id="Terminal" filter="url(#filter0_iiiiiiii_automations)">
33485
- <rect x="4.83334" y="4.83325" width="62.3333" height="62.3333" rx="11" fill="#60574E"/>
33486
- <rect x="4.83334" y="4.83325" width="62.3333" height="62.3333" rx="11" fill="url(#paint0_radial_automations)" fill-opacity="0.8"/>
33487
- <g id="Automations">
33488
- <g id="Automations Base" filter="url(#filter1_ddii_automations)">
33489
- <rect x="11.7083" y="11.7083" width="22" height="22" rx="4.58333" fill="#FFB466"/>
33490
- <rect x="11.7083" y="11.7083" width="22" height="22" rx="4.58333" fill="url(#paint1_linear_automations)" fill-opacity="0.3" style="mix-blend-mode:color-burn"/>
33491
- </g>
33492
- <g id="Mark" transform="translate(11.7083 11.7083) scale(0.6875)">
33493
- <g transform="translate(11.69 17.23) rotate(15.38) translate(-11.6127 -13.9462)">
33494
- <path d="M19.6079 0.599355C17.3441 -0.321872 14.9739 -0.14792 13.46 0.89331C13.2119 1.0517 12.8569 1.33576 12.5208 1.72732C11.7692 2.60307 11.3623 3.63039 11.2455 4.71375C9.23223 4.72389 7.26359 5.26923 5.91356 6.71772C4.51769 8.21546 4.20143 10.1821 4.50239 12.1398C3.03356 12.6072 1.73247 13.5334 0.797604 15.0344C-0.763486 17.5411 0.0452254 21.226 2.54055 23.9633C3.86696 25.4182 6.02038 27.1152 9.12043 27.7103C9.4387 27.7774 9.7679 27.8383 10.1084 27.8924C10.1631 27.8912 10.2077 27.8829 10.2452 27.8663C10.3679 27.8115 10.4529 27.7164 10.4936 27.5884C10.5386 27.4464 10.4665 27.2329 10.3225 26.8061L10.3223 26.8053C10.295 26.7244 10.2394 26.5328 10.2229 26.4847C10.1092 26.1536 10.1013 25.6968 9.29531 25.5323C6.36484 24.9697 4.22412 22.9285 3.443 21.7079C1.93568 19.5884 1.74985 17.3834 2.5381 16.1175C3.15559 15.1259 3.99949 14.4762 5.00959 14.1271C5.0662 14.2875 5.12651 14.4471 5.18941 14.6047C6.26598 17.3029 8.34278 19.9218 10.7168 21.4594C12.5207 22.6277 14.0688 23.191 15.3656 23.2173C16.7185 23.2446 17.7648 22.679 18.3785 21.7329C19.1495 20.5441 18.9055 19.1005 18.2192 17.8597C17.5219 16.5993 16.2786 15.3443 14.618 14.2688C12.3111 12.7746 9.6482 11.8023 7.16738 11.7667C6.94958 11.7636 6.73278 11.7676 6.5175 11.7794C6.29761 10.2804 6.58767 8.99855 7.41267 8.11328C8.25927 7.2049 9.64226 6.75076 11.3481 6.76069C11.3942 6.99403 11.4511 7.2276 11.5191 7.45994C12.1111 9.48257 13.5227 11.5049 15.5552 13.1574C16.5948 14.0026 17.873 14.7596 19.1105 15.0578C20.3597 15.3589 21.8418 15.2258 22.7273 13.8605C23.4042 12.8168 23.3646 11.5943 22.7683 10.4454C22.1932 9.33738 21.0943 8.26144 19.5493 7.26071C17.7444 6.09178 15.4953 5.21721 13.2899 4.87294C13.3774 4.17741 13.6441 3.56421 14.0766 3.06037C14.1711 2.95366 14.4136 2.7086 14.6268 2.58184C15.5245 1.96452 17.2529 1.76519 18.9922 2.54473C19.0717 2.57587 19.162 2.61474 19.258 2.65802C19.6838 2.84995 19.8972 2.94538 20.0408 2.91486C20.1702 2.88726 20.2715 2.81303 20.3369 2.69806C20.4092 2.57052 20.3835 2.35019 20.3321 1.90979L20.332 1.90897C20.3223 1.82552 20.3146 1.75546 20.3086 1.70584C20.2668 1.36373 20.3503 0.922479 19.6079 0.599355ZM7.09311 13.8459C7.09073 13.8399 7.08762 13.8343 7.08529 13.8283C7.08348 13.8237 7.08223 13.8183 7.0804 13.8137H7.09213C7.10728 13.8138 7.12287 13.8144 7.13806 13.8146C9.13659 13.8433 11.4274 14.6433 13.503 15.9876C14.9676 16.9362 15.9342 17.9639 16.425 18.8509C16.9266 19.7579 16.8492 20.3271 16.6595 20.6196C16.4638 20.9213 16.112 21.1845 15.4076 21.1704C14.6469 21.155 13.4768 20.806 11.8318 19.7406C9.84087 18.4511 8.02486 16.1811 7.09311 13.8459ZM16.8481 11.5685C15.1147 10.1591 13.994 8.51485 13.5196 6.9941C15.2544 7.34223 17.0102 8.05638 18.4342 8.97854C19.8239 9.87862 20.6018 10.7185 20.9497 11.3888C21.2762 12.0181 21.2074 12.4377 21.0073 12.7462C20.7924 13.0776 20.4132 13.2646 19.5913 13.0666C18.7576 12.8657 17.753 12.3042 16.8481 11.5685Z" fill="#57432F" fill-rule="evenodd" clip-rule="evenodd"/>
33495
- </g>
33496
- </g>
33497
- </g>
33498
- <g id="Underscore" filter="url(#filter2_ddii_automations)">
33499
- <rect x="38.0052" y="30.0417" width="17.4167" height="3.66667" rx="1.83333" fill="#FFB466"/>
33500
- <rect x="38.0052" y="30.0417" width="17.4167" height="3.66667" rx="1.83333" fill="url(#paint2_linear_automations)" fill-opacity="0.3" style="mix-blend-mode:color-burn"/>
33501
- </g>
33502
- </g>
33503
- </g>
33504
- </g>
33505
- <defs>
33506
- <filter id="filter0_iiiiiiii_automations" x="4.83334" y="4.83325" width="62.3333" height="62.3333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
33507
- <feFlood flood-opacity="0" result="BackgroundImageFix"/>
33508
- <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
33509
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33510
- <feOffset/>
33511
- <feGaussianBlur stdDeviation="6.63667"/>
33512
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33513
- <feColorMatrix type="matrix" values="0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 1 0"/>
33514
- <feBlend mode="plus-darker" in2="shape" result="effect1_innerShadow_automations"/>
33515
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33516
- <feOffset/>
33517
- <feGaussianBlur stdDeviation="1.30167"/>
33518
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33519
- <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/>
33520
- <feBlend mode="overlay" in2="effect1_innerShadow_automations" result="effect2_innerShadow_automations"/>
33521
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33522
- <feMorphology radius="0.88" operator="dilate" in="SourceAlpha" result="effect3_innerShadow_automations"/>
33523
- <feOffset dx="-0.88" dy="-0.88"/>
33524
- <feGaussianBlur stdDeviation="0.22"/>
33525
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33526
- <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
33527
- <feBlend mode="overlay" in2="effect2_innerShadow_automations" result="effect3_innerShadow_automations"/>
33528
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33529
- <feMorphology radius="0.88" operator="dilate" in="SourceAlpha" result="effect4_innerShadow_automations"/>
33530
- <feOffset dx="-1.68667" dy="-1.68667"/>
33531
- <feGaussianBlur stdDeviation="0.22"/>
33532
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33533
- <feColorMatrix type="matrix" values="0 0 0 0 0.14902 0 0 0 0 0.14902 0 0 0 0 0.14902 0 0 0 1 0"/>
33534
- <feBlend mode="plus-lighter" in2="effect3_innerShadow_automations" result="effect4_innerShadow_automations"/>
33535
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33536
- <feOffset dx="-0.22" dy="-0.22"/>
33537
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33538
- <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.45 0"/>
33539
- <feBlend mode="normal" in2="effect4_innerShadow_automations" result="effect5_innerShadow_automations"/>
33540
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33541
- <feMorphology radius="0.88" operator="dilate" in="SourceAlpha" result="effect6_innerShadow_automations"/>
33542
- <feOffset dx="0.88" dy="0.88"/>
33543
- <feGaussianBlur stdDeviation="0.22"/>
33544
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33545
- <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
33546
- <feBlend mode="overlay" in2="effect5_innerShadow_automations" result="effect6_innerShadow_automations"/>
33547
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33548
- <feMorphology radius="0.88" operator="dilate" in="SourceAlpha" result="effect7_innerShadow_automations"/>
33549
- <feOffset dx="1.68667" dy="1.68667"/>
33550
- <feGaussianBlur stdDeviation="0.22"/>
33551
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33552
- <feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 1 0"/>
33553
- <feBlend mode="plus-lighter" in2="effect6_innerShadow_automations" result="effect7_innerShadow_automations"/>
33554
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33555
- <feOffset dx="0.22" dy="0.22"/>
33556
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33557
- <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.35 0"/>
33558
- <feBlend mode="normal" in2="effect7_innerShadow_automations" result="effect8_innerShadow_automations"/>
33559
- </filter>
33560
- <filter id="filter1_ddii_automations" x="7.12501" y="7.12492" width="31.1667" height="31.1667" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
33561
- <feFlood flood-opacity="0" result="BackgroundImageFix"/>
33562
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33563
- <feOffset/>
33564
- <feGaussianBlur stdDeviation="2.29167"/>
33565
- <feComposite in2="hardAlpha" operator="out"/>
33566
- <feColorMatrix type="matrix" values="0 0 0 0 0.634402 0 0 0 0 0.336572 0 0 0 0 0.028646 0 0 0 0.7 0"/>
33567
- <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_automations"/>
33568
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33569
- <feOffset/>
33570
- <feGaussianBlur stdDeviation="0.916667"/>
33571
- <feComposite in2="hardAlpha" operator="out"/>
33572
- <feColorMatrix type="matrix" values="0 0 0 0 0.635294 0 0 0 0 0.336438 0 0 0 0 0.027451 0 0 0 0.8 0"/>
33573
- <feBlend mode="normal" in2="effect1_dropShadow_automations" result="effect2_dropShadow_automations"/>
33574
- <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_automations" result="shape"/>
33575
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33576
- <feOffset dy="0.458333"/>
33577
- <feGaussianBlur stdDeviation="0.229167"/>
33578
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33579
- <feColorMatrix type="matrix" values="0 0 0 0 0.870968 0 0 0 0 0.752016 0 0 0 0 0.629032 0 0 0 1 0"/>
33580
- <feBlend mode="normal" in2="shape" result="effect3_innerShadow_automations"/>
33581
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33582
- <feOffset dy="-0.458333"/>
33583
- <feGaussianBlur stdDeviation="0.229167"/>
33584
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33585
- <feColorMatrix type="matrix" values="0 0 0 0 0.7728 0 0 0 0 0.465213 0 0 0 0 0.1472 0 0 0 1 0"/>
33586
- <feBlend mode="normal" in2="effect3_innerShadow_automations" result="effect4_innerShadow_automations"/>
33587
- </filter>
33588
- <filter id="filter2_ddii_automations" x="33.4219" y="25.4584" width="26.5833" height="12.8334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
33589
- <feFlood flood-opacity="0" result="BackgroundImageFix"/>
33590
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33591
- <feOffset/>
33592
- <feGaussianBlur stdDeviation="2.29167"/>
33593
- <feComposite in2="hardAlpha" operator="out"/>
33594
- <feColorMatrix type="matrix" values="0 0 0 0 0.634402 0 0 0 0 0.336572 0 0 0 0 0.028646 0 0 0 0.7 0"/>
33595
- <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_automations"/>
33596
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33597
- <feOffset/>
33598
- <feGaussianBlur stdDeviation="0.916667"/>
33599
- <feComposite in2="hardAlpha" operator="out"/>
33600
- <feColorMatrix type="matrix" values="0 0 0 0 0.635294 0 0 0 0 0.336438 0 0 0 0 0.027451 0 0 0 0.8 0"/>
33601
- <feBlend mode="normal" in2="effect1_dropShadow_automations" result="effect2_dropShadow_automations"/>
33602
- <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_automations" result="shape"/>
33603
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33604
- <feOffset dy="0.458333"/>
33605
- <feGaussianBlur stdDeviation="0.229167"/>
33606
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33607
- <feColorMatrix type="matrix" values="0 0 0 0 0.870968 0 0 0 0 0.752016 0 0 0 0 0.629032 0 0 0 1 0"/>
33608
- <feBlend mode="normal" in2="shape" result="effect3_innerShadow_automations"/>
33609
- <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33610
- <feOffset dy="-0.458333"/>
33611
- <feGaussianBlur stdDeviation="0.229167"/>
33612
- <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
33613
- <feColorMatrix type="matrix" values="0 0 0 0 0.7728 0 0 0 0 0.465213 0 0 0 0 0.1472 0 0 0 1 0"/>
33614
- <feBlend mode="normal" in2="effect3_innerShadow_automations" result="effect4_innerShadow_automations"/>
33615
- </filter>
33616
- <radialGradient id="paint0_radial_automations" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(36 24.9084) rotate(90) scale(44.8021 44.9389)">
33617
- <stop stop-opacity="0"/>
33618
- <stop offset="1"/>
33619
- </radialGradient>
33620
- <linearGradient id="paint1_linear_automations" x1="29.3542" y1="14.5728" x2="12.5406" y2="33.7347" gradientUnits="userSpaceOnUse">
33621
- <stop stop-color="white"/>
33622
- <stop offset="1" stop-color="#331A00"/>
33623
- </linearGradient>
33624
- <linearGradient id="paint2_linear_automations" x1="51.0699" y1="30.6131" x2="50.4646" y2="35.1722" gradientUnits="userSpaceOnUse">
33625
- <stop stop-color="white"/>
33626
- <stop offset="1" stop-color="#331A00"/>
33627
- </linearGradient>
33628
- </defs>
33629
- </svg>`;
33924
+ var ICON = `<img src="${AUTOMATIONS_CLI_LOGO_URL}" width="72" height="72" alt="" referrerpolicy="no-referrer">`;
33630
33925
  var PALETTE = `
33631
33926
  :root {
33632
33927
  color-scheme: light dark;
@@ -33711,6 +34006,7 @@ var SHARED_STYLES = `
33711
34006
  position: relative;
33712
34007
  isolation: isolate;
33713
34008
  }
34009
+ .logo img,
33714
34010
  .logo svg {
33715
34011
  display: block;
33716
34012
  width: 100%;
@@ -33868,7 +34164,7 @@ var SUCCESS_HTML = `<!DOCTYPE html>
33868
34164
  <div class="container">
33869
34165
  <header class="header">
33870
34166
  <div class="logo">
33871
- ${ICON_SVG}
34167
+ ${ICON}
33872
34168
  <div class="badge">
33873
34169
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
33874
34170
  <polyline points="20 6 9 17 4 12"/>
@@ -33958,7 +34254,7 @@ function errorHtml(message) {
33958
34254
  <body>
33959
34255
  <div class="container">
33960
34256
  <div class="logo">
33961
- ${ICON_SVG}
34257
+ ${ICON}
33962
34258
  <div class="badge">
33963
34259
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round">
33964
34260
  <line x1="18" y1="6" x2="6" y2="18"/>
@@ -34039,7 +34335,7 @@ function attachAutomationsLoginCommand(auth) {
34039
34335
  }
34040
34336
 
34041
34337
  // src/commands/auth/logout.ts
34042
- import { getErrorMessage as getErrorMessage4 } from "@doist/cli-core";
34338
+ import { getErrorMessage as getErrorMessage5 } from "@doist/cli-core";
34043
34339
  import { attachLogoutCommand } from "@doist/cli-core/auth";
34044
34340
 
34045
34341
  // src/lib/spinner.ts
@@ -34087,7 +34383,7 @@ async function revokeToken(token) {
34087
34383
  });
34088
34384
  return response.ok ? void 0 : `HTTP ${response.status}`;
34089
34385
  } catch (error62) {
34090
- return getErrorMessage4(error62);
34386
+ return getErrorMessage5(error62);
34091
34387
  }
34092
34388
  }
34093
34389
 
@@ -34218,7 +34514,7 @@ function parsePositiveInteger(value) {
34218
34514
  }
34219
34515
 
34220
34516
  // src/lib/command-error.ts
34221
- import { formatJson as formatJson4, formatNdjson as formatNdjson4, getErrorMessage as getErrorMessage5 } from "@doist/cli-core";
34517
+ import { formatJson as formatJson4, formatNdjson as formatNdjson4, getErrorMessage as getErrorMessage6 } from "@doist/cli-core";
34222
34518
 
34223
34519
  // src/lib/billing-errors.ts
34224
34520
  import { CliError as CliError2 } from "@doist/cli-core";
@@ -34297,7 +34593,7 @@ function renderFailure(error62, context = {}) {
34297
34593
  const explained = explainBillingFailure(authExplained);
34298
34594
  const code = getErrorCode(explained);
34299
34595
  return {
34300
- message: getErrorMessage5(explained),
34596
+ message: getErrorMessage6(explained),
34301
34597
  humanMessage: formatError2(explained),
34302
34598
  ...code ? { code } : {}
34303
34599
  };
@@ -36955,7 +37251,7 @@ function registerConnectorCommand(program) {
36955
37251
  }
36956
37252
 
36957
37253
  // src/lib/manifest-input.ts
36958
- import { CliError as CliError3, getErrorMessage as getErrorMessage6 } from "@doist/cli-core";
37254
+ import { CliError as CliError3, getErrorMessage as getErrorMessage7 } from "@doist/cli-core";
36959
37255
  import { Option as Option6 } from "commander";
36960
37256
  import { readFile as readFile2 } from "node:fs/promises";
36961
37257
  import { text } from "node:stream/consumers";
@@ -36971,20 +37267,10 @@ async function readManifestInput(options, input2 = process.stdin) {
36971
37267
  } catch (error62) {
36972
37268
  throw new CliError3(
36973
37269
  "MANIFEST_UNREADABLE",
36974
- `${source.label} is not valid JSON: ${getErrorMessage6(error62)}`
37270
+ `${source.label} is not valid JSON: ${getErrorMessage7(error62)}`
36975
37271
  );
36976
37272
  }
36977
37273
  }
36978
- function parsePartialManifestInput(value) {
36979
- const validation = validatePartialManifest(value);
36980
- if (!validation.isValid) {
36981
- throw new CliError3(
36982
- "MANIFEST_INVALID",
36983
- `Manifest is invalid: ${validation.errors.join("; ")}`
36984
- );
36985
- }
36986
- return validation.data;
36987
- }
36988
37274
  async function readSource(options, input2) {
36989
37275
  if (options.file !== void 0) {
36990
37276
  try {
@@ -36992,7 +37278,7 @@ async function readSource(options, input2) {
36992
37278
  } catch (error62) {
36993
37279
  throw new CliError3(
36994
37280
  "MANIFEST_UNREADABLE",
36995
- `Could not read ${options.file}: ${getErrorMessage6(error62)}`
37281
+ `Could not read ${options.file}: ${getErrorMessage7(error62)}`
36996
37282
  );
36997
37283
  }
36998
37284
  }
@@ -37002,7 +37288,7 @@ async function readSource(options, input2) {
37002
37288
  } catch (error62) {
37003
37289
  throw new CliError3(
37004
37290
  "MANIFEST_UNREADABLE",
37005
- `Could not read standard input: ${getErrorMessage6(error62)}`
37291
+ `Could not read standard input: ${getErrorMessage7(error62)}`
37006
37292
  );
37007
37293
  }
37008
37294
  }
@@ -37010,134 +37296,55 @@ async function readSource(options, input2) {
37010
37296
  }
37011
37297
 
37012
37298
  // src/commands/draft/draft-service.ts
37013
- import { CliError as CliError4, getErrorMessage as getErrorMessage7 } from "@doist/cli-core";
37299
+ import { CliError as CliError4, getErrorMessage as getErrorMessage8 } from "@doist/cli-core";
37014
37300
  import { readFile as readFile3 } from "node:fs/promises";
37015
- import { setTimeout as wait2 } from "node:timers/promises";
37016
-
37017
- // src/lib/manifest-diff.ts
37018
- function stableStringify(value) {
37019
- return JSON.stringify(sortKeys(value));
37020
- }
37021
- function isDeepEqual(left, right) {
37022
- return stableStringify(left) === stableStringify(right);
37023
- }
37024
- function diffManifests(before, after) {
37025
- const previous = asManifest(before);
37026
- const next = asManifest(after);
37027
- const changedProperties = [.../* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => key !== "steps").filter((key) => !isDeepEqual(previous[key], next[key])).sort();
37028
- const previousSteps = stepsById(previous.steps);
37029
- const nextSteps = stepsById(next.steps);
37030
- const added = [...nextSteps.keys()].filter((id) => !previousSteps.has(id));
37031
- const removed = [...previousSteps.keys()].filter((id) => !nextSteps.has(id));
37032
- const changed = [...nextSteps].filter(([id, step]) => previousSteps.has(id) && !isDeepEqual(previousSteps.get(id), step)).map(([id]) => id);
37033
- const sharedBefore = [...previousSteps.keys()].filter((id) => nextSteps.has(id));
37034
- const sharedAfter = [...nextSteps.keys()].filter((id) => previousSteps.has(id));
37035
- const reordered = sharedBefore.some((id, index) => sharedAfter[index] !== id);
37036
- return {
37037
- changedProperties,
37038
- steps: { added, removed, changed, reordered },
37039
- isEmpty: changedProperties.length === 0 && added.length === 0 && removed.length === 0 && changed.length === 0 && !reordered
37040
- };
37041
- }
37042
- function asManifest(value) {
37043
- return isRecord3(value) ? value : {};
37044
- }
37045
- function stepsById(steps) {
37046
- const byId = /* @__PURE__ */ new Map();
37047
- if (!Array.isArray(steps)) return byId;
37048
- steps.forEach((step, index) => {
37049
- const id = isRecord3(step) && typeof step.id === "string" ? step.id : `#${index}`;
37050
- byId.set(id, step);
37051
- });
37052
- return byId;
37301
+ function createDraft2(draftApi, input2) {
37302
+ return withRecoveryHints(draftApi, createDraft(draftApi, input2));
37053
37303
  }
37054
- function sortKeys(value) {
37055
- if (Array.isArray(value)) return value.map(sortKeys);
37056
- if (!isRecord3(value)) return value;
37057
- return Object.fromEntries(
37058
- Object.keys(value).sort().map((key) => [key, sortKeys(value[key])])
37059
- );
37304
+ function updateDraft2(draftApi, input2) {
37305
+ return withRecoveryHints(draftApi, updateDraft(draftApi, input2));
37060
37306
  }
37061
- function isRecord3(value) {
37062
- return typeof value === "object" && value !== null && !Array.isArray(value);
37307
+ function connectDraft2(draftApi, input2) {
37308
+ return withRecoveryHints(draftApi, connectDraft(draftApi, input2));
37063
37309
  }
37064
-
37065
- // src/commands/draft/draft-service.ts
37066
- function resolveDraftApi(api, workspaceId, signal) {
37067
- const createOptions = { signal, translate: false };
37068
- if (workspaceId === null) {
37069
- return {
37070
- scope: null,
37071
- get: (draftId) => api.automationDrafts.get(draftId, { signal }),
37072
- create: (manifest) => api.automationDrafts.create(manifest, createOptions),
37073
- update: (draftId, manifest) => api.automationDrafts.update(draftId, manifest, { signal }),
37074
- createAutomationDraft: (automationId) => api.automations.createDraft(automationId, { signal }),
37075
- upsertConnection: (draftId, body) => api.automationDraftConnections.upsert(draftId, body, { signal }),
37076
- getConnectionStatus: async (draftId) => (await api.automationDraftConnections.getStatus(draftId, { signal })).validation,
37077
- finalize: (draftId) => api.automationDrafts.finalize(draftId, { signal }),
37078
- getAutomation: (automationId) => api.automations.get(automationId, { signal }),
37079
- test: (draftId, request2) => api.automationDrafts.test(draftId, request2, { signal }),
37080
- getTestRun: (draftId, runId, args, requestSignal) => api.automationDrafts.getTestRun(draftId, runId, args, {
37081
- signal: requestSignal ?? signal
37082
- })
37083
- };
37084
- }
37310
+ async function publishDraft2(draftApi, input2) {
37311
+ const result = await withRecoveryHints(draftApi, publishDraft(draftApi, input2));
37312
+ if (result.action === "publish_preview") return result;
37313
+ const { verification } = result;
37314
+ if (verification.status !== "unavailable") return { ...result, verification };
37085
37315
  return {
37086
- scope: workspaceId,
37087
- get: (draftId) => api.workspaceAutomationDrafts.get(workspaceId, draftId, { signal }),
37088
- create: (manifest) => api.workspaceAutomationDrafts.create(workspaceId, manifest, createOptions),
37089
- update: (draftId, manifest) => api.workspaceAutomationDrafts.update(workspaceId, draftId, manifest, { signal }),
37090
- createAutomationDraft: (automationId) => api.workspaceAutomations.createDraft(workspaceId, automationId, { signal }),
37091
- upsertConnection: (draftId, body) => api.workspaceAutomationDraftConnections.upsert(workspaceId, draftId, body, { signal }),
37092
- getConnectionStatus: async (draftId) => (await api.workspaceAutomationDraftConnections.getStatus(workspaceId, draftId, {
37093
- signal
37094
- })).validation,
37095
- finalize: (draftId) => api.workspaceAutomationDrafts.finalize(workspaceId, draftId, { signal }),
37096
- getAutomation: (automationId) => api.workspaceAutomations.get(workspaceId, automationId, { signal }),
37097
- test: (draftId, request2) => api.workspaceAutomationDrafts.test(workspaceId, draftId, request2, { signal }),
37098
- getTestRun: (draftId, runId, args, requestSignal) => api.workspaceAutomationDrafts.getTestRun(workspaceId, draftId, runId, args, {
37099
- signal: requestSignal ?? signal
37100
- })
37316
+ ...result,
37317
+ verification: {
37318
+ ...verification,
37319
+ command: `tda automation view ${result.automation.id} ${scopeFlag(draftApi.workspaceId)}`
37320
+ }
37101
37321
  };
37102
37322
  }
37103
- async function createDraft(draftApi, { manifest, automationId }) {
37104
- const partialManifest = parsePartialManifestInput(manifest);
37105
- if (automationId === void 0) {
37106
- const result = await draftApi.create(partialManifest);
37107
- return { action: "created", target: toTarget(result.draft), ...result };
37108
- }
37109
- const { draft } = await openEditDraft(draftApi, automationId);
37110
- try {
37111
- const result = await draftApi.update(draft.id, partialManifest);
37112
- return { action: "created", target: toTarget(result.draft), ...result };
37113
- } catch (error62) {
37114
- if (!isManifestRejection(error62)) throw error62;
37115
- throw new CliError4(
37116
- "DRAFT_MANIFEST_REJECTED",
37117
- `Opened draft ${draft.id} for automation ${automationId}, but the manifest was rejected: ${getErrorMessage7(error62)}`,
37118
- { hints: draftHints(draftApi.scope, draft.id) }
37119
- );
37120
- }
37121
- }
37122
- function isManifestRejection(error62) {
37123
- return error62 instanceof AutomationsRequestError && (error62.httpStatusCode === 400 || error62.httpStatusCode === 422);
37124
- }
37125
- async function updateDraft(draftApi, { draftId, manifest }) {
37126
- const partialManifest = parsePartialManifestInput(manifest);
37127
- const result = await draftApi.update(draftId, partialManifest);
37128
- return { action: "updated", target: toTarget(result.draft), ...result };
37129
- }
37130
- async function openEditDraft(draftApi, automationId) {
37323
+ async function withRecoveryHints(draftApi, operation) {
37131
37324
  try {
37132
- return await draftApi.createAutomationDraft(automationId);
37325
+ return await operation;
37133
37326
  } catch (error62) {
37134
- const conflict = getOpenDraftConflictError(error62);
37135
- if (!conflict) throw error62;
37136
- throw new CliError4(
37137
- "DRAFT_CONFLICT",
37138
- `Automation ${automationId} already has an open draft ${conflict.draftId}.`,
37139
- { hints: draftHints(draftApi.scope, conflict.draftId) }
37140
- );
37327
+ if (!(error62 instanceof DraftWorkflowError)) throw error62;
37328
+ const scope = draftApi.workspaceId;
37329
+ const draftId = error62.draftId ?? "<draft-id>";
37330
+ switch (error62.code) {
37331
+ case "manifest_invalid":
37332
+ throw new CliError4("MANIFEST_INVALID", error62.message);
37333
+ case "draft_conflict":
37334
+ throw new CliError4("DRAFT_CONFLICT", error62.message, {
37335
+ hints: draftHints(scope, draftId)
37336
+ });
37337
+ case "draft_manifest_rejected":
37338
+ throw new CliError4("DRAFT_MANIFEST_REJECTED", error62.message, {
37339
+ hints: draftHints(scope, draftId)
37340
+ });
37341
+ case "draft_not_ready":
37342
+ throw draftNotReadyError(error62.message, draftId, scope);
37343
+ case "connector_not_required":
37344
+ throw new Error(error62.message);
37345
+ default:
37346
+ throw error62;
37347
+ }
37141
37348
  }
37142
37349
  }
37143
37350
  function draftHints(scope, draftId) {
@@ -37149,175 +37356,7 @@ function draftHints(scope, draftId) {
37149
37356
  function scopeFlag(scope) {
37150
37357
  return scope === null ? "--personal" : `--workspace ${scope}`;
37151
37358
  }
37152
- function toTarget(draft) {
37153
- return draft.automationId === null ? { kind: "new" } : { kind: "automation", automationId: draft.automationId };
37154
- }
37155
- async function connectDraft(draftApi, api, {
37156
- draftId,
37157
- selections,
37158
- auto
37159
- }) {
37160
- const bound = [];
37161
- const required2 = selections.length > 0 ? getRequiredConnectorsFromManifest(
37162
- (await draftApi.get(draftId)).draft.partialManifest
37163
- ) : /* @__PURE__ */ new Map();
37164
- for (const selection of selections) {
37165
- const types = required2.get(selection.connectorId);
37166
- if (!types || selection.connectionType && !types.has(selection.connectionType)) {
37167
- const slot = selection.connectionType ? `${selection.connectorId} (${selection.connectionType})` : selection.connectorId;
37168
- throw new Error(
37169
- `Connector ${slot} is not used by draft ${draftId}. Required: ${describeRequired(required2)}.`
37170
- );
37171
- }
37172
- const connectionTypes = selection.connectionType ? [selection.connectionType] : [...types];
37173
- for (const connectionType of connectionTypes) {
37174
- await draftApi.upsertConnection(draftId, {
37175
- connectorId: selection.connectorId,
37176
- connectionType,
37177
- connectionId: selection.connectionId
37178
- });
37179
- bound.push({ ...selection, connectionType, mode: "explicit" });
37180
- }
37181
- }
37182
- const unresolved = [];
37183
- if (auto) {
37184
- const status = await draftApi.getConnectionStatus(draftId);
37185
- for (const connector of status.connectors) {
37186
- for (const connectionType of ["mcp", "trigger"]) {
37187
- const slot = connector[connectionType];
37188
- if (!slot.required || slot.status !== "missing") continue;
37189
- const { connections } = await api.connections.list({
37190
- connectorId: connector.id,
37191
- capability: connectionType,
37192
- status: "authorized"
37193
- });
37194
- const [only] = connections;
37195
- if (connections.length === 1 && only) {
37196
- await draftApi.upsertConnection(draftId, {
37197
- connectorId: connector.id,
37198
- connectionType,
37199
- connectionId: only.id
37200
- });
37201
- bound.push({
37202
- connectorId: connector.id,
37203
- connectionType,
37204
- connectionId: only.id,
37205
- mode: "auto"
37206
- });
37207
- continue;
37208
- }
37209
- unresolved.push({
37210
- connectorId: connector.id,
37211
- connectionType,
37212
- reason: connections.length === 0 ? "no_connection" : "multiple_connections",
37213
- candidateConnectionIds: connections.map((connection) => connection.id)
37214
- });
37215
- }
37216
- }
37217
- }
37218
- const { isValid, missing } = await draftApi.getConnectionStatus(draftId);
37219
- return { action: "connected", draftId, bound, unresolved, isValid, missing };
37220
- }
37221
- function describeRequired(required2) {
37222
- if (required2.size === 0) return "none";
37223
- return [...required2].map(([connectorId, types]) => `${connectorId} (${[...types].join(", ")})`).join(", ");
37224
- }
37225
- async function publishDraft(draftApi, { draftId, confirmed }) {
37226
- const { summary, manifest } = await summarizeForPublish(draftApi, draftId);
37227
- if (!confirmed) {
37228
- return { action: "publish_preview", summary };
37229
- }
37230
- let published;
37231
- try {
37232
- published = await draftApi.finalize(draftId);
37233
- } catch (error62) {
37234
- const notReady = getDraftNotReadyError(error62);
37235
- if (!notReady) throw error62;
37236
- throw draftNotReadyError(notReady.message, draftId, draftApi.scope);
37237
- }
37238
- const automationId = published.automation.id;
37239
- let automation;
37240
- try {
37241
- automation = await draftApi.getAutomation(automationId);
37242
- } catch (error62) {
37243
- return {
37244
- action: "published",
37245
- summary,
37246
- automation: published.automation,
37247
- verification: {
37248
- status: "unavailable",
37249
- reason: getErrorMessage7(error62),
37250
- command: `tda automation view ${automationId} ${scopeFlag(draftApi.scope)}`
37251
- }
37252
- };
37253
- }
37254
- const expected = normaliseManifest(manifest);
37255
- const actual = normaliseManifest(automation.manifest);
37256
- return {
37257
- action: "published",
37258
- summary,
37259
- automation,
37260
- verification: isDeepEqual(expected, actual) ? { status: "matched" } : { status: "mismatched", mismatch: diffManifests(expected, actual) }
37261
- };
37262
- }
37263
- async function summarizeForPublish(draftApi, draftId) {
37264
- const [detail, connections] = await Promise.all([
37265
- draftApi.get(draftId),
37266
- draftApi.getConnectionStatus(draftId)
37267
- ]);
37268
- const { draft } = detail;
37269
- const target = toTarget(draft);
37270
- const published = target.kind === "automation" ? await draftApi.getAutomation(target.automationId) : null;
37271
- const diff = published ? diffManifests(
37272
- normaliseManifest(published.manifest),
37273
- normaliseManifest(draft.partialManifest)
37274
- ) : null;
37275
- const summary = {
37276
- draftId,
37277
- name: getResourceName(draft.partialManifest),
37278
- scope: draft.workspaceId === null ? { kind: "personal" } : {
37279
- kind: "workspace",
37280
- workspaceId: draft.workspaceId,
37281
- // A new workspace automation publishes team-visible; an
37282
- // edit keeps whatever the automation already has.
37283
- visibility: published?.visibility ?? "workspace"
37284
- },
37285
- target,
37286
- enabled: published?.enabled ?? true,
37287
- validation: { state: detail.state, issues: detail.issues },
37288
- connections: {
37289
- isValid: connections.isValid,
37290
- connected: connections.connected.map(
37291
- ({ connectorId, connectionType, connectionId }) => ({
37292
- connectorId,
37293
- connectionType,
37294
- connectionId
37295
- })
37296
- ),
37297
- missing: connections.missing
37298
- },
37299
- schedule: describeSchedule(draft.partialManifest),
37300
- diff
37301
- };
37302
- return { summary, manifest: draft.partialManifest };
37303
- }
37304
- function describeSchedule(manifest) {
37305
- if (!hasTriggerType(manifest)) return null;
37306
- const schedule = getManifestTriggers(manifest).find((trigger) => trigger.type === "schedule");
37307
- if (!schedule || schedule.type !== "schedule") return null;
37308
- return getScheduleTriggerDescription({ value: schedule.value, timeFormat: "24h" });
37309
- }
37310
- function normaliseManifest(manifest) {
37311
- const parsed = manifestSchema.safeParse(manifest);
37312
- return parsed.success ? parsed.data : manifest;
37313
- }
37314
- var DEFAULT_POLL_INTERVALS_MS = { first: 2e3, max: 1e4 };
37315
- var STATUS_REQUEST_GRACE_MS = 1e4;
37316
- var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set([
37317
- "pending",
37318
- "running"
37319
- ]);
37320
- async function startDraftTest(draftApi, {
37359
+ async function startDraftTest2(draftApi, {
37321
37360
  draftId,
37322
37361
  request: request2,
37323
37362
  wait: waitFor,
@@ -37325,75 +37364,33 @@ async function startDraftTest(draftApi, {
37325
37364
  }) {
37326
37365
  let started;
37327
37366
  try {
37328
- started = await draftApi.test(draftId, request2);
37367
+ started = await startDraftTest(draftApi, { draftId, request: request2 });
37329
37368
  } catch (error62) {
37330
37369
  const refusal = getDraftTestRequestError(error62);
37331
37370
  if (!refusal) throw error62;
37332
- throw toDraftTestCliError(refusal, draftId, draftApi.scope);
37371
+ throw toDraftTestCliError(refusal, draftId, draftApi.workspaceId);
37333
37372
  }
37334
- const base = {
37373
+ const identity = {
37335
37374
  draftId,
37336
37375
  testRunId: started.testRunId,
37337
37376
  trigger: started.trigger,
37338
37377
  connectorCalls: started.connectorCalls,
37339
- command: `tda draft test-run ${draftId} ${started.testRunId} ${scopeFlag(draftApi.scope)}`
37378
+ command: `tda draft test-run ${draftId} ${started.testRunId} ${scopeFlag(draftApi.workspaceId)}`
37340
37379
  };
37341
37380
  if (!waitFor) {
37342
- return { action: "test_started", ...base, status: started.status };
37381
+ return { action: "test_started", ...identity, status: started.status };
37343
37382
  }
37344
- const intervals = waitFor.pollIntervalsMs ?? DEFAULT_POLL_INTERVALS_MS;
37345
- const deadline = Date.now() + waitFor.timeoutMs;
37346
- let intervalMs = intervals.first;
37347
- let last = null;
37348
- while (true) {
37349
- const requestTimeout = AbortSignal.timeout(
37350
- Math.max(deadline - Date.now(), 0) + (waitFor.requestGraceMs ?? STATUS_REQUEST_GRACE_MS)
37351
- );
37352
- try {
37353
- last = await draftApi.getTestRun(
37354
- draftId,
37355
- started.testRunId,
37356
- void 0,
37357
- AbortSignal.any([signal, requestTimeout])
37358
- );
37359
- } catch (error62) {
37360
- if (signal.aborted || !requestTimeout.aborted) throw error62;
37361
- return toFinished(base, last, "timed_out");
37362
- }
37363
- if (!OPEN_RUN_STATUSES.has(last.status)) {
37364
- return toFinished(base, last, toOutcome(last.status));
37365
- }
37366
- const remainingMs = deadline - Date.now();
37367
- if (remainingMs <= 0) {
37368
- return toFinished(base, last, "timed_out");
37369
- }
37370
- await wait2(Math.min(intervalMs, remainingMs), void 0, { signal });
37371
- intervalMs = Math.min(intervalMs * 2, intervals.max);
37372
- }
37373
- }
37374
- function toFinished(base, run, outcome) {
37383
+ const finished = await waitForDraftTest(draftApi, started, { ...waitFor, signal });
37375
37384
  return {
37376
37385
  action: "test_finished",
37377
- ...base,
37378
- outcome,
37379
- status: run?.status ?? "pending",
37380
- failedStep: run?.failedStep ?? null,
37381
- 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),
37382
- result: run?.result ?? null
37386
+ ...identity,
37387
+ outcome: finished.outcome,
37388
+ status: finished.status,
37389
+ failedStep: finished.failedStep,
37390
+ error: finished.error,
37391
+ result: finished.result
37383
37392
  };
37384
37393
  }
37385
- function toOutcome(status) {
37386
- switch (status) {
37387
- case "completed":
37388
- return "passed";
37389
- case "skipped":
37390
- return "skipped";
37391
- case "cancelled":
37392
- return "cancelled";
37393
- default:
37394
- return "failed";
37395
- }
37396
- }
37397
37394
  function draftNotReadyError(message, draftId, scope) {
37398
37395
  const flag = scopeFlag(scope);
37399
37396
  return new CliError4("DRAFT_NOT_READY", message, {
@@ -37419,9 +37416,6 @@ function toDraftTestCliError(refusal, draftId, scope) {
37419
37416
  return new CliError4(refusal.code.toUpperCase(), refusal.message);
37420
37417
  }
37421
37418
  }
37422
- function parseTriggerOption(value) {
37423
- return /^\d+$/.test(value) ? { index: Number(value) } : { event: value };
37424
- }
37425
37419
  async function readTestPayloadFile(path) {
37426
37420
  let content;
37427
37421
  try {
@@ -37429,7 +37423,7 @@ async function readTestPayloadFile(path) {
37429
37423
  } catch (error62) {
37430
37424
  throw new CliError4(
37431
37425
  "PAYLOAD_UNREADABLE",
37432
- `Could not read ${path}: ${getErrorMessage7(error62)}`
37426
+ `Could not read ${path}: ${getErrorMessage8(error62)}`
37433
37427
  );
37434
37428
  }
37435
37429
  try {
@@ -37437,7 +37431,7 @@ async function readTestPayloadFile(path) {
37437
37431
  } catch (error62) {
37438
37432
  throw new CliError4(
37439
37433
  "PAYLOAD_UNREADABLE",
37440
- `${path} is not valid JSON: ${getErrorMessage7(error62)}`
37434
+ `${path} is not valid JSON: ${getErrorMessage8(error62)}`
37441
37435
  );
37442
37436
  }
37443
37437
  }
@@ -37730,8 +37724,8 @@ function registerDraftCommand(program) {
37730
37724
  command,
37731
37725
  async ({ api, signal }) => {
37732
37726
  const manifest = await readManifestInput(options);
37733
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37734
- return createDraft(draftApi, { manifest, automationId: options.automation });
37727
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37728
+ return createDraft2(draftApi, { manifest, automationId: options.automation });
37735
37729
  },
37736
37730
  { human: formatDraftChange },
37737
37731
  { spinner: "Creating draft..." }
@@ -37746,8 +37740,8 @@ function registerDraftCommand(program) {
37746
37740
  command,
37747
37741
  async ({ api, signal }) => {
37748
37742
  const manifest = await readManifestInput(options);
37749
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37750
- return updateDraft(draftApi, { draftId, manifest });
37743
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37744
+ return updateDraft2(draftApi, { draftId, manifest });
37751
37745
  },
37752
37746
  { human: formatDraftChange },
37753
37747
  { spinner: "Updating draft..." }
@@ -37773,8 +37767,8 @@ function registerDraftCommand(program) {
37773
37767
  );
37774
37768
  }
37775
37769
  const selections = options.connection.map(parseConnectionSelectionOption);
37776
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37777
- return connectDraft(draftApi, api, {
37770
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37771
+ return connectDraft2(draftApi, {
37778
37772
  draftId,
37779
37773
  selections,
37780
37774
  auto: options.auto === true
@@ -37790,8 +37784,8 @@ function registerDraftCommand(program) {
37790
37784
  await runApiCommand(
37791
37785
  command,
37792
37786
  async ({ api, signal }) => {
37793
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37794
- return publishDraft(draftApi, { draftId, confirmed: options.yes === true });
37787
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37788
+ return publishDraft2(draftApi, { draftId, confirmed: options.yes === true });
37795
37789
  },
37796
37790
  { human: formatDraftPublish, json: summarizeDraftPublish },
37797
37791
  { spinner: options.yes === true ? "Publishing..." : "Loading draft..." }
@@ -37816,11 +37810,11 @@ function registerDraftCommand(program) {
37816
37810
  throw new Error("--timeout only applies with --wait.");
37817
37811
  }
37818
37812
  const timeoutMs = parseTestTimeout(options.timeout ?? DEFAULT_TEST_WAIT);
37819
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37820
- return startDraftTest(draftApi, {
37813
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37814
+ return startDraftTest2(draftApi, {
37821
37815
  draftId,
37822
37816
  request: {
37823
- ...options.trigger !== void 0 ? { trigger: parseTriggerOption(options.trigger) } : {},
37817
+ ...options.trigger !== void 0 ? { trigger: toDraftTestTriggerSelector(options.trigger) } : {},
37824
37818
  ...options.payloadFile !== void 0 ? { payload: await readTestPayloadFile(options.payloadFile) } : {}
37825
37819
  },
37826
37820
  wait: options.wait === true ? { timeoutMs } : null,
@@ -37841,7 +37835,7 @@ function registerDraftCommand(program) {
37841
37835
  await runApiCommand(
37842
37836
  command,
37843
37837
  async ({ api, signal }) => {
37844
- const draftApi = resolveDraftApi(api, await resolveWorkspaceScope(options), signal);
37838
+ const draftApi = bindDraftApi(api, await resolveWorkspaceScope(options), { signal });
37845
37839
  return draftApi.getTestRun(draftId, runId, {
37846
37840
  includeGeneratedCode: options.full === true,
37847
37841
  includeToolCallPreviews: true