@withandeo/cli 0.11.0 → 0.12.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.
Files changed (51) hide show
  1. package/README.md +18 -0
  2. package/dist/api.d.ts +6 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +9 -2
  5. package/dist/api.js.map +1 -1
  6. package/dist/artifact-limits.d.ts +8 -0
  7. package/dist/artifact-limits.d.ts.map +1 -0
  8. package/dist/artifact-limits.js +10 -0
  9. package/dist/artifact-limits.js.map +1 -0
  10. package/dist/artifact.d.ts.map +1 -1
  11. package/dist/artifact.js +28 -11
  12. package/dist/artifact.js.map +1 -1
  13. package/dist/contracts.d.ts +3 -0
  14. package/dist/contracts.d.ts.map +1 -1
  15. package/dist/files.d.ts.map +1 -1
  16. package/dist/files.js +16 -1
  17. package/dist/files.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/main.d.ts.map +1 -1
  23. package/dist/main.js +100 -6
  24. package/dist/main.js.map +1 -1
  25. package/dist/workflow-config.d.ts +9 -0
  26. package/dist/workflow-config.d.ts.map +1 -0
  27. package/dist/workflow-config.js +76 -0
  28. package/dist/workflow-config.js.map +1 -0
  29. package/dist/workflow-contract.d.ts +34 -0
  30. package/dist/workflow-contract.d.ts.map +1 -0
  31. package/dist/workflow-contract.js +96 -0
  32. package/dist/workflow-contract.js.map +1 -0
  33. package/dist/workflow-coordinator.d.ts +4 -0
  34. package/dist/workflow-coordinator.d.ts.map +1 -0
  35. package/dist/workflow-coordinator.js +49 -0
  36. package/dist/workflow-coordinator.js.map +1 -0
  37. package/dist/workflow-dev-host.d.ts +2 -0
  38. package/dist/workflow-dev-host.d.ts.map +1 -0
  39. package/dist/workflow-dev-host.js +34 -0
  40. package/dist/workflow-dev-host.js.map +1 -0
  41. package/dist/workflow-dev.d.ts +5 -0
  42. package/dist/workflow-dev.d.ts.map +1 -0
  43. package/dist/workflow-dev.js +112 -0
  44. package/dist/workflow-dev.js.map +1 -0
  45. package/dist/workflow-package.d.ts +5 -0
  46. package/dist/workflow-package.d.ts.map +1 -0
  47. package/dist/workflow-package.js +116 -0
  48. package/dist/workflow-package.js.map +1 -0
  49. package/package.json +8 -1
  50. package/skill/tender-accounts/SKILL.md +9 -0
  51. package/templates/shopify-customer-account/apps/gateway/README.md +2 -0
package/dist/main.js CHANGED
@@ -7,6 +7,7 @@ import { createHash } from "node:crypto";
7
7
  import { AgentApiClient } from "./api.js";
8
8
  import { CLI_AUTH_SCHEMA, DEFAULT_AUTH_PROFILE, createDefaultAuthStore, validateAuthProfile, validateNamedAuthProfile, } from "./auth-store.js";
9
9
  import { labelLocalArtifact, verifyArtifact } from "./artifact.js";
10
+ import { packageAppWorkflows } from "./workflow-package.js";
10
11
  import { BUILD_ENVELOPE_FILENAME, CLI_CONFIG_SCHEMA, CLI_LINK_SCHEMA, } from "./contracts.js";
11
12
  import { CliApiError, CliError } from "./errors.js";
12
13
  import { loadProjectContext, normalizeApiUrl, pathExists, readBoundedFile, readOptionalToken, requiredProjectId, resolveArtifactDirectory, safeArtifactPath, writeProjectLink, } from "./files.js";
@@ -90,6 +91,8 @@ export async function runCli(argv, overrides = {}) {
90
91
  }
91
92
  }
92
93
  async function execute(topic, options, cwd, runtime) {
94
+ if (topic.startsWith("workflows "))
95
+ return { value: await workflowCommand(topic.slice(10), cwd, options, runtime) };
93
96
  switch (topic) {
94
97
  case "init":
95
98
  return { value: await initializeStarter({
@@ -200,6 +203,52 @@ async function execute(topic, options, cwd, runtime) {
200
203
  throw new CliError("command_unknown", `Unknown command: ${topic}.`, "tender-accounts --help");
201
204
  }
202
205
  }
206
+ async function workflowCommand(action, cwd, options, runtime) {
207
+ const context = await optionalContext(cwd);
208
+ const { client, projectId } = await sourceProjectAccess(cwd, context?.link ?? null, options, runtime, false);
209
+ const query = Object.fromEntries(["environment", "limit", "offset", "cursor"].flatMap(key => options.flags.has(key) ? [[key, options.flags.get(key)]] : []));
210
+ if (action === "list")
211
+ return client.appWorkflows(projectId, [], { query });
212
+ if (action === "preview-services")
213
+ return client.appWorkflows(projectId, ["preview-services"]);
214
+ if (action === "preview-service-set")
215
+ return client.appWorkflows(projectId, ["preview-services"], { method: "PUT", body: {
216
+ targetProjectId: requiredFlag(options, "target-project"), previewAppId: requiredFlag(options, "preview") === "none" ? null : requiredFlag(options, "preview"),
217
+ } });
218
+ const scope = requiredFlag(options, "scope");
219
+ if (action === "runs")
220
+ return client.appWorkflows(projectId, [scope, "runs"], { query });
221
+ const id = requiredFlag(options, "run");
222
+ if (action === "acknowledge") {
223
+ if (options.flags.get("confirm") !== id)
224
+ throw new CliError("confirmation_required", "Use --confirm RUN_ID after inspecting history. Acknowledgment does not resend or confirm event delivery.");
225
+ return client.appWorkflows(projectId, [scope, "runs", id, "acknowledge"], { method: "POST", body: { confirm: true, controlId: requiredFlag(options, "control") } });
226
+ }
227
+ if (action === "status")
228
+ return client.appWorkflows(projectId, [scope, "runs", id]);
229
+ if (["history", "diagnostics", "export"].includes(action))
230
+ return client.appWorkflows(projectId, [scope, "runs", id, action], { query });
231
+ let payload = {};
232
+ if (options.flags.has("input-file")) {
233
+ try {
234
+ payload = JSON.parse(new TextDecoder("utf8", { fatal: true }).decode(await readBoundedFile(resolve(cwd, requiredFlag(options, "input-file")), 64 * 1024)));
235
+ }
236
+ catch {
237
+ throw new CliError("workflow_input_invalid", "The input file must contain valid JSON of at most 64 KiB. Do not put API credentials in workflow input.");
238
+ }
239
+ }
240
+ if (action === "start")
241
+ return client.appWorkflows(projectId, [scope, "runs"], { method: "POST", body: { id, params: payload } });
242
+ if (action === "event")
243
+ return client.appWorkflows(projectId, [scope, "runs", id, "sendEvent"], { method: "POST", body: { type: requiredFlag(options, "type"), payload } });
244
+ if (["pause", "resume", "terminate", "restart"].includes(action)) {
245
+ if (["terminate", "restart"].includes(action) && options.flags.get("confirm") !== id) {
246
+ throw new CliError("confirmation_required", "Confirm the exact run with --confirm RUN_ID. Restart can repeat business operations.");
247
+ }
248
+ return client.appWorkflows(projectId, [scope, "runs", id, action], { method: "POST", body: { confirm: true } });
249
+ }
250
+ throw new CliError("command_unknown", "Unknown workflow command.", "andeo workflows --help");
251
+ }
203
252
  async function sourceStatus(cwd, options, runtime) {
204
253
  const context = await optionalContext(cwd);
205
254
  const { client, projectId } = await sourceProjectAccess(cwd, context?.link ?? null, options, runtime, false);
@@ -1432,6 +1481,20 @@ async function doctor(cwd, options, runtime) {
1432
1481
  }
1433
1482
  async function runDev(cwd, options, runtime) {
1434
1483
  const context = await loadProjectContext(cwd);
1484
+ const { readWorkflowConfig } = await import("./workflow-config.js");
1485
+ if ((await readWorkflowConfig(context.root, runtime.environment.WRANGLER_ENV))?.declarations.length) {
1486
+ await buildProject(cwd, options, runtime);
1487
+ const { startWorkflowDev } = await import("./workflow-dev.js");
1488
+ const local = await startWorkflowDev(context.root, resolveArtifactDirectory(context));
1489
+ runtime.stderr.write("Andeo workflow development: http://127.0.0.1:8787 (local execution, not hosted authorization). Restart after rebuilding changes.\n");
1490
+ try {
1491
+ await new Promise(resolve => { const remove = runtime.onInterrupt(() => { remove(); resolve(); }); });
1492
+ }
1493
+ finally {
1494
+ await local.dispose();
1495
+ }
1496
+ return { value: { status: "stopped", workflowExecution: "local" } };
1497
+ }
1435
1498
  if (!context.config.commands.dev) {
1436
1499
  throw new CliError("dev_command_missing", "This app does not declare commands.dev in tender-accounts.json.");
1437
1500
  }
@@ -1460,6 +1523,7 @@ async function buildProject(cwd, options, runtime) {
1460
1523
  const source = await sourceState(context.root, runtime);
1461
1524
  await runBuildStage(cwd, options, runtime, "check", { TENDER_SOURCE_REVISION: source.revision });
1462
1525
  await runBuildStage(cwd, options, runtime, "package", { TENDER_SOURCE_REVISION: source.revision });
1526
+ await packageAppWorkflows(context.root, resolveArtifactDirectory(context), runtime.environment.WRANGLER_ENV);
1463
1527
  let artifact = await verifyArtifact(resolveArtifactDirectory(context));
1464
1528
  artifact = await labelLocalArtifact(artifact, { gitRevision: source.revision, dirty: source.dirty });
1465
1529
  return buildSummary(artifact, source.dirty);
@@ -1529,6 +1593,7 @@ async function preview(cwd, options, runtime, uploadOnly = false) {
1529
1593
  previewUrl: session.url,
1530
1594
  previewGrantId: session.grantId,
1531
1595
  previewExpiresAt: session.expiresAt,
1596
+ sourceVerification: session.sourceVerification,
1532
1597
  from: session.from,
1533
1598
  entry: session.entry,
1534
1599
  selection: session.selection,
@@ -1574,8 +1639,10 @@ async function previewOpen(cwd, options, runtime) {
1574
1639
  }
1575
1640
  async function deliveryRetry(cwd, options, runtime) {
1576
1641
  const context = await loadProjectContext(cwd);
1642
+ if (options.booleans.has("reauthorize") && !context.link)
1643
+ throw new CliError("project_not_linked", "Link the exact app before reauthorizing its preview.", "andeo link --json");
1577
1644
  const { client } = await authenticatedClient(cwd, context.link, options, runtime);
1578
- return await client.retryDelivery(requiredDeliveryId(options));
1645
+ return await client.retryDelivery(requiredDeliveryId(options), options.booleans.has("reauthorize") ? context.link.projectId : undefined);
1579
1646
  }
1580
1647
  async function authenticatedClient(cwd, link, options, runtime) {
1581
1648
  let apiUrl = selectedApiUrl(options, link, runtime.environment, runtime.defaultApiUrl);
@@ -1902,7 +1969,7 @@ function commandTopic(argv) {
1902
1969
  const topic = `preview ${argv[1]}`;
1903
1970
  return argv.includes("--help") || argv.includes("-h") ? { topic: "help", values: [topic] } : { topic, values: argv.slice(2) };
1904
1971
  }
1905
- if (parent === "auth" || parent === "delivery" || parent === "source" || parent === "skill" || parent === "config" || parent === "production") {
1972
+ if (parent === "auth" || parent === "delivery" || parent === "source" || parent === "skill" || parent === "config" || parent === "production" || parent === "workflows") {
1906
1973
  const child = argv[1];
1907
1974
  if (!child || child.startsWith("--"))
1908
1975
  return { topic: "help", values: [parent] };
@@ -1932,7 +1999,7 @@ function parseOptions(values) {
1932
1999
  const booleans = new Set();
1933
2000
  const positionals = [];
1934
2001
  const booleanNames = new Set([
1935
- "json", "token-stdin", "dry-run", "no-wait", "force",
2002
+ "json", "token-stdin", "dry-run", "no-wait", "force", "reauthorize",
1936
2003
  "device", "wait", "no-open", "local", "all", "production",
1937
2004
  ]);
1938
2005
  for (let index = 0; index < values.length; index += 1) {
@@ -1965,6 +2032,21 @@ function assertAllowedOptions(topic, options) {
1965
2032
  const common = ["cwd", "json"];
1966
2033
  const authentication = ["api-url", "token-stdin", "profile"];
1967
2034
  const allowed = {
2035
+ "workflows list": [...common, ...authentication, "project", "environment", "limit", "offset"],
2036
+ "workflows runs": [...common, ...authentication, "project", "scope", "limit", "offset"],
2037
+ "workflows status": [...common, ...authentication, "project", "scope", "run"],
2038
+ "workflows history": [...common, ...authentication, "project", "scope", "run", "cursor"],
2039
+ "workflows diagnostics": [...common, ...authentication, "project", "scope", "run", "limit", "offset"],
2040
+ "workflows export": [...common, ...authentication, "project", "scope", "run", "limit", "offset"],
2041
+ "workflows start": [...common, ...authentication, "project", "scope", "run", "input-file"],
2042
+ "workflows event": [...common, ...authentication, "project", "scope", "run", "input-file", "type"],
2043
+ "workflows pause": [...common, ...authentication, "project", "scope", "run"],
2044
+ "workflows resume": [...common, ...authentication, "project", "scope", "run"],
2045
+ "workflows restart": [...common, ...authentication, "project", "scope", "run", "confirm"],
2046
+ "workflows acknowledge": [...common, ...authentication, "project", "scope", "run", "control", "confirm"],
2047
+ "workflows terminate": [...common, ...authentication, "project", "scope", "run", "confirm"],
2048
+ "workflows preview-services": [...common, ...authentication, "project"],
2049
+ "workflows preview-service-set": [...common, ...authentication, "project", "target-project", "preview"],
1968
2050
  init: [...common, "name", "slug", "directory", "template", "dry-run"],
1969
2051
  "config example": common,
1970
2052
  "skill print": common,
@@ -2045,7 +2127,7 @@ function assertAllowedOptions(topic, options) {
2045
2127
  "sampling-rate",
2046
2128
  ],
2047
2129
  "delivery status": [...common, ...authentication, "delivery"],
2048
- "delivery retry": [...common, ...authentication, "delivery"],
2130
+ "delivery retry": [...common, ...authentication, "delivery", "reauthorize"],
2049
2131
  };
2050
2132
  const accepted = new Set(allowed[topic] ?? common);
2051
2133
  const provided = [...options.flags.keys(), ...options.booleans];
@@ -2146,6 +2228,18 @@ function writeOutput(value, options, runtime) {
2146
2228
  runtime.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
2147
2229
  }
2148
2230
  function helpText(topic) {
2231
+ if (topic === "workflows" || topic?.startsWith("workflows "))
2232
+ return `Usage: andeo workflows <list|runs|status|start|event|pause|resume|terminate|restart|acknowledge|history|diagnostics|export|preview-services|preview-service-set> [options]
2233
+
2234
+ All commands support --project PROJECT_ID and --json. List scopes with workflows list [--environment preview|production].
2235
+ Runs: --scope SCOPE_ID. Inspect/control one run: --scope SCOPE_ID --run APP_LOCAL_ID.
2236
+ Start: --input-file params.json (optional). Event: --type NAME [--input-file event.json].
2237
+ Restart and terminate require --confirm APP_LOCAL_ID. Mutations are never automatically retried.
2238
+ Acknowledge an uncertain event with --control CONTROL_ID --confirm APP_LOCAL_ID after inspection; this never resends or confirms delivery.
2239
+ History resumes with --cursor N; lists/diagnostics/export support --limit 1..100 and --offset N.
2240
+ Choose background preview routing with preview-service-set --target-project PROJECT_ID --preview APP_ID|none.
2241
+ Only non-secret JSON belongs in workflow params/events. Keep credentials in the connected service's native secrets.
2242
+ Account workflow entitlement is required for new starts. Run status is authoritative; list results are a timestamped projection.`;
2149
2243
  const topics = {
2150
2244
  init: "Usage: andeo init --name NAME [--slug SLUG] [--directory PATH] [--template shopify-customer-account] [--dry-run] [--json]\n\nCreate a new merchant-owned Shopify gateway and React portal in an empty directory.\n\nExamples:\n andeo init --name \"Acme account\" --directory ./acme-account\n andeo init --name \"Acme account\" --directory ./acme-account --dry-run --json",
2151
2245
  auth: "Usage: andeo auth <login|create|activate|deactivate|status|list|delete|logout> [options]",
@@ -2194,7 +2288,7 @@ function helpText(topic) {
2194
2288
  "delivery status": "Usage: andeo delivery status --delivery dly_... [--json]\n\nRead one developer preview delivery. Do not pass a dwf_ production workflow ID.",
2195
2289
  "preview entries": "Usage: andeo preview entries [--json]\n\nList eligible published entry apps and the selected default for this linked app.",
2196
2290
  "preview open": "Usage: andeo preview open --from dly_...|sbl_... [--through PROJECT_ID] [--return-path /account] [--json]\n\nOpen the exact deployed preview without rebuilding or uploading. Private apps open through their published connections. Opening again renews access.",
2197
- "delivery retry": "Usage: andeo delivery retry --delivery dly_... [--json]\n\nRetry one requested or failed developer preview idempotently. A dwf_ production workflow is resumed by a merchant administrator from Activity.",
2291
+ "delivery retry": "Usage: andeo delivery retry --delivery dly_... [--reauthorize] [--json]\n\nRetry one requested or failed developer preview idempotently. --reauthorize explicitly authorizes a legacy preview for the same owner and linked project using the current CLI login; it cannot replace another login's recorded grant or publish production. Normal credential refresh needs no flag. A dwf_ production workflow is resumed by a merchant administrator from Activity.",
2198
2292
  skill: "Usage: andeo skill <print|install> [options]",
2199
2293
  "skill print": "Usage: andeo skill print [--json]",
2200
2294
  "skill install": "Usage: andeo skill install [--agent codex|claude] [--directory PATH] [--force] [--json]\n\nInstall globally for the current user by default: ~/.agents/skills for Codex or ~/.claude/skills for Claude. --directory is an explicit custom or repository-local override.",
@@ -2203,7 +2297,7 @@ function helpText(topic) {
2203
2297
  };
2204
2298
  if (topic && topics[topic])
2205
2299
  return topics[topic];
2206
- return `Andeo merchant developer CLI ${CLI_VERSION}\n\nCommands:\n init Create a new Shopify account starter\n auth login Create or refresh the default login\n auth create Create or re-authorize a named login\n auth activate Bind a named login to a directory tree\n auth deactivate Remove an exact directory binding\n auth status Validate one login or scoped credential\n auth list List local login profiles and bindings\n auth delete Revoke one named login\n auth logout Revoke the default or every login\n link Link this checkout to one permitted project\n doctor Check developer readiness\n dev Run the project-owned local dev command\n check Run project validation\n build Package and verify a portable artifact\n upload Retain a build for administrator publication\n preview Build and deploy an exact preview\n preview entries List eligible connected entry apps\n preview open Reopen an exact deployed preview\n source status Inspect Andeo-managed Git and recent builds\n source branches List exact managed branches and default policy\n source compare Compare two managed branches\n source connect Connect this linked project to managed Git\n source pull Clone or fast-forward the exact connected repository\n source token Issue a short-lived repository credential\n source push Push the exact commit without persisting a token\n source promote Fast-forward the default after successful preview checks\n source history Run guarded, archived source-history migration\n tail Stream logs for one exact linked project\n delivery status Read preview workflow state\n delivery retry Retry a failed preview workflow\n skill install Install the Andeo agent skill\n config example Print the project configuration contract\n\nThere is intentionally no production publish command.\nRun andeo <command> --help for examples.`;
2300
+ return `Andeo merchant developer CLI ${CLI_VERSION}\n\nCommands:\n init Create a new Shopify account starter\n auth login Create or refresh the default login\n auth create Create or re-authorize a named login\n auth activate Bind a named login to a directory tree\n auth deactivate Remove an exact directory binding\n auth status Validate one login or scoped credential\n auth list List local login profiles and bindings\n auth delete Revoke one named login\n auth logout Revoke the default or every login\n link Link this checkout to one permitted project\n doctor Check developer readiness\n dev Run the project-owned local dev command\n check Run project validation\n build Package and verify a portable artifact\n upload Retain a build for administrator publication\n preview Build and deploy an exact preview\n preview entries List eligible connected entry apps\n preview open Reopen an exact deployed preview\n source status Inspect Andeo-managed Git and recent builds\n source branches List exact managed branches and default policy\n source compare Compare two managed branches\n source connect Connect this linked project to managed Git\n source pull Clone or fast-forward the exact connected repository\n source token Issue a short-lived repository credential\n source push Push the exact commit without persisting a token\n source promote Fast-forward the default after successful preview checks\n source history Run guarded, archived source-history migration\n workflows Inspect and control app-defined background runs\n tail Stream logs for one exact linked project\n delivery status Read preview workflow state\n delivery retry Retry a failed preview workflow\n skill install Install the Andeo agent skill\n config example Print the project configuration contract\n\nThere is intentionally no production publish command.\nRun andeo <command> --help for examples.`;
2207
2301
  }
2208
2302
  function normalizeError(error) {
2209
2303
  if (error instanceof CliApiError) {