@sanity/workflow-cli 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @sanity/workflow-cli
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1c6d4c0: Name the target deployment and resource on `deploy` progress and result lines. The spinner and completion line now read `Deploying N definition(s) to <name> (<tag>) → <resource>…` / `Processed N definition(s) · <name> (<tag>) → <resource>` (and the dry-run equivalents), so a single-deployment run — the common case, which prints no per-batch banner — always shows where the deploy landed.
8
+ - fb42001: The instance-keyed commands — `diagnose`, `fire-action`, `set-stage`, `abort`, `reset-activity` — now locate an instance by fanning out across every configured resource, the same way `show` and the other read commands already do. Passing an instance id is enough: on a config that spans several resources they no longer prompt for (or require) a `--deployment` / `--tag` selector — those stay as optional narrowers. As an optimization, an id carrying the engine's default `<tag>.` mint prefix probes that tag's resource first and skips the fan-out on a hit. `reset-activity` also gains the `--deployment` narrower its siblings already had.
9
+ - 84ff807: The read commands — `list`, `show`, `definition list`, `definition show` — now take `--json`, emitting structured output on stdout for parity with the verb commands. `show` and `definition show` print the fetched document as stored; `list` and `definition list` print their table's row view (each row annotated with the `resource` it came from) under a `truncated` flag. A JSON read spanning several resources is all-or-nothing: an unreadable resource fails the whole command rather than emitting a partial listing a script would mistake for the complete result.
10
+
11
+ ### Patch Changes
12
+
13
+ - a8c460c: Colour the whole terminal-status row in the instance header — aborted rows render bold yellow, completed rows bold green — so a finished instance stands out from the neutral detail rows instead of only its timestamp carrying colour.
14
+ - Updated dependencies [88ba4ba]
15
+ - @sanity/workflow-engine@0.22.0
16
+
3
17
  ## 0.21.0
4
18
 
5
19
  ### Minor Changes
@@ -26,10 +26,26 @@ export default class DefinitionList extends WorkflowCommand {
26
26
  static description: string;
27
27
  static examples: string[];
28
28
  static flags: {
29
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
30
  limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
30
31
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
31
32
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
32
33
  };
33
34
  run(): Promise<void>;
34
35
  }
36
+ /** Map a queried definition to its display row. Drops the stamp pair (gate
37
+ * plumbing, not view data) and `_id` — `tag.name.vN`, which just repeats
38
+ * name + tag + version, and no command takes an `_id` anyway (they take
39
+ * name + --tag/--version); `name vN` is this CLI's identifier format (the
40
+ * `definition show` title). */
41
+ export declare function definitionRow(r: DefinitionListRow): {
42
+ name: string;
43
+ version: number;
44
+ title: string;
45
+ tag: string;
46
+ stageCount: number;
47
+ inFlightCount: number;
48
+ totalInstances: number;
49
+ _createdAt: string;
50
+ };
35
51
  export {};
@@ -3,8 +3,8 @@ import { WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, assertReadableModel,
3
3
  import logSymbols from 'log-symbols';
4
4
  import { WorkflowCommand } from "../../../lib/base-command.js";
5
5
  import { resolveReadTargets } from "../../../lib/context.js";
6
- import { tagFlags } from "../../../lib/flags.js";
7
- import { runReadAcrossTargets } from "../../../lib/read-fanout.js";
6
+ import { jsonFlags, tagFlags } from "../../../lib/flags.js";
7
+ import { logJsonListing, runReadAcrossTargets } from "../../../lib/read-fanout.js";
8
8
  import { logClippedTable } from "../../../lib/ui.js";
9
9
  export function buildDefinitionListQuery(flags) {
10
10
  const params = { limit: flags.limit + 1 };
@@ -45,6 +45,7 @@ export default class DefinitionList extends WorkflowCommand {
45
45
  static examples = [
46
46
  '<%= config.bin %> definition list',
47
47
  '<%= config.bin %> definition list --tag prod',
48
+ '<%= config.bin %> definition list --json',
48
49
  ];
49
50
  static flags = {
50
51
  ...tagFlags,
@@ -55,35 +56,48 @@ export default class DefinitionList extends WorkflowCommand {
55
56
  name: Flags.string({
56
57
  description: 'Filter to a single workflow definition name (e.g. product-launch).',
57
58
  }),
59
+ ...jsonFlags,
58
60
  };
59
61
  async run() {
60
62
  const { flags } = await this.parse(DefinitionList);
61
63
  const targets = await resolveReadTargets(flags);
62
64
  const { groq, params } = buildDefinitionListQuery(flags);
65
+ if (flags.json) {
66
+ await logJsonListing({
67
+ targets,
68
+ key: 'definitions',
69
+ limit: flags.limit,
70
+ log: (line) => this.log(line),
71
+ fetch: ({ client }) => fetchDefinitionRows({ client, groq, params, limit: flags.limit }),
72
+ toRow: definitionRow,
73
+ });
74
+ return;
75
+ }
63
76
  const failures = await runReadAcrossTargets({
64
77
  targets,
65
78
  log: (line) => this.log(line),
66
79
  run: async ({ client }) => {
67
- const fetched = (await client.fetch(groq, params, {
68
- tag: 'definition.list',
69
- })).map(assertReadableModel);
70
- if (!fetched.length) {
80
+ const fetched = await fetchDefinitionRows({ client, groq, params, limit: flags.limit });
81
+ if (!fetched.rows.length) {
71
82
  this.log(`${logSymbols.info} no definitions found`);
72
83
  return;
73
84
  }
74
85
  logClippedTable({
75
- rows: fetched,
86
+ rows: fetched.rows,
76
87
  limit: flags.limit,
77
88
  headers: ['workflow', 'title', 'tag', 'stages', 'in flight', 'instances', 'created'],
78
- toCells: (r) => [
79
- `${r.name} v${r.version}`,
80
- r.title,
81
- r.tag,
82
- String(r.stageCount),
83
- String(r.inFlightCount),
84
- String(r.totalInstances),
85
- r._createdAt,
86
- ],
89
+ toCells: (r) => {
90
+ const row = definitionRow(r);
91
+ return [
92
+ `${row.name} v${row.version}`,
93
+ row.title,
94
+ row.tag,
95
+ String(row.stageCount),
96
+ String(row.inFlightCount),
97
+ String(row.totalInstances),
98
+ row._createdAt,
99
+ ];
100
+ },
87
101
  log: (line) => this.log(line),
88
102
  });
89
103
  },
@@ -93,3 +107,19 @@ export default class DefinitionList extends WorkflowCommand {
93
107
  }
94
108
  }
95
109
  }
110
+ export function definitionRow(r) {
111
+ return {
112
+ name: r.name,
113
+ version: r.version,
114
+ title: r.title,
115
+ tag: r.tag,
116
+ stageCount: r.stageCount,
117
+ inFlightCount: r.inFlightCount,
118
+ totalInstances: r.totalInstances,
119
+ _createdAt: r._createdAt,
120
+ };
121
+ }
122
+ async function fetchDefinitionRows({ client, groq, params, limit, }) {
123
+ const rows = await client.fetch(groq, params, { tag: 'definition.list' });
124
+ return { rows: rows.map(assertReadableModel), hasMore: rows.length > limit };
125
+ }
@@ -8,6 +8,7 @@ export default class DefinitionShow extends WorkflowCommand {
8
8
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
9
9
  };
10
10
  static flags: {
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  version: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  };
@@ -6,7 +6,7 @@ import { WorkflowCommand } from "../../../lib/base-command.js";
6
6
  import { resolveReadTargets, soleHitOrFail } from "../../../lib/context.js";
7
7
  import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../../lib/definitions.js";
8
8
  import { fail } from "../../../lib/fail.js";
9
- import { tagFlags } from "../../../lib/flags.js";
9
+ import { jsonFlags, tagFlags } from "../../../lib/flags.js";
10
10
  import { formatKeyValue, resourceLabel, sectionHeader } from "../../../lib/ui.js";
11
11
  export default class DefinitionShow extends WorkflowCommand {
12
12
  static aliases = ['definition:show'];
@@ -17,6 +17,7 @@ export default class DefinitionShow extends WorkflowCommand {
17
17
  static flags = {
18
18
  ...tagFlags,
19
19
  version: Flags.integer({ description: 'Specific version (default: latest).' }),
20
+ ...jsonFlags,
20
21
  };
21
22
  async run() {
22
23
  const { args, flags } = await this.parse(DefinitionShow);
@@ -32,6 +33,10 @@ export default class DefinitionShow extends WorkflowCommand {
32
33
  exit: 1,
33
34
  });
34
35
  }
36
+ if (flags.json) {
37
+ this.log(JSON.stringify(def, null, 2));
38
+ return;
39
+ }
35
40
  this.log(definitionHeader(def, def.tag));
36
41
  this.log('');
37
42
  for (const line of describeDefinition(def)) {
@@ -77,6 +77,24 @@ export declare function deploymentBanner({ deployment, index, total, }: {
77
77
  index: number;
78
78
  total: number;
79
79
  }): string | undefined;
80
+ /** The deploy target as one label — the deployment's friendly name/tag
81
+ * ({@link deploymentLabel}) and the resource it writes to
82
+ * ({@link resourceLabel}) — so every progress and result line names where a
83
+ * deploy landed, including the common single-deployment run that prints no
84
+ * banner. */
85
+ export declare function targetLabel(deployment: WorkflowDeployment): string;
86
+ /** The deploy spinner's progress line — names the full target it writes to. */
87
+ export declare function deployStartLine(count: number, where: string): string;
88
+ /** The dry-run spinner's progress line — a diff reads the resource's deployed
89
+ * state, so it names the resource it compares against, not the deployment. */
90
+ export declare function dryRunStartLine(count: number, resource: string): string;
91
+ /** A completed deploy/dry-run result line: the past-tense verb, the count, and
92
+ * the full target, so a finished run always shows where it landed. */
93
+ export declare function resultLine({ verb, count, where, }: {
94
+ verb: 'Processed' | 'Diffed';
95
+ count: number;
96
+ where: string;
97
+ }): string;
80
98
  /** One line per deployed definition, symbol-tagged by its result status,
81
99
  * followed by any advisory effect-output lint warnings indented beneath it. */
82
100
  export declare function deployResultLines(results: DeployDefinitionResult[]): string[];
@@ -13,7 +13,7 @@ import { loadWorkflowConfig } from "../../lib/load-config.js";
13
13
  import { deploymentLabel, deploymentToTarget, selectDeployments, } from "../../lib/select-deployment.js";
14
14
  import { shareDefinitionsAfterDeploy } from "../../lib/share-definitions.js";
15
15
  import { cliTelemetry } from "../../lib/telemetry.js";
16
- import { groupBanner } from "../../lib/ui.js";
16
+ import { groupBanner, resourceLabel } from "../../lib/ui.js";
17
17
  export default class Deploy extends WorkflowCommand {
18
18
  static aliases = ['deploy'];
19
19
  static description = 'Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.';
@@ -110,14 +110,16 @@ export default class Deploy extends WorkflowCommand {
110
110
  }
111
111
  async processDeployment({ deployment, defs, dryRun, client, }) {
112
112
  const target = deploymentToTarget(deployment);
113
+ const where = targetLabel(deployment);
114
+ const resource = resourceLabel(deployment.workflowResource);
113
115
  if (dryRun) {
114
- await this.runDryRun({ client, defs, target });
116
+ await this.runDryRun({ client, defs, target, where, resource });
115
117
  return undefined;
116
118
  }
117
- return this.executeDeploy({ client, defs, target });
119
+ return this.executeDeploy({ client, defs, target, where });
118
120
  }
119
- async executeDeploy({ client, defs, target, }) {
120
- const spinner = ora(`Deploying ${defs.length} definition(s)…`).start();
121
+ async executeDeploy({ client, defs, target, where, }) {
122
+ const spinner = ora(deployStartLine(defs.length, where)).start();
121
123
  try {
122
124
  const deployed = await workflow.deployDefinitions({
123
125
  ...target,
@@ -125,7 +127,7 @@ export default class Deploy extends WorkflowCommand {
125
127
  definitions: defs,
126
128
  telemetry: cliTelemetry().logger,
127
129
  });
128
- spinner.succeed(`Processed ${deployed.results.length} definition(s)`);
130
+ spinner.succeed(resultLine({ verb: 'Processed', count: deployed.results.length, where }));
129
131
  for (const line of deployResultLines(deployed.results))
130
132
  this.log(line);
131
133
  return deployed;
@@ -135,11 +137,11 @@ export default class Deploy extends WorkflowCommand {
135
137
  throw error;
136
138
  }
137
139
  }
138
- async runDryRun({ client, defs, target, }) {
139
- const spinner = ora(`Diffing ${defs.length} definition(s) against dataset…`).start();
140
+ async runDryRun({ client, defs, target, where, resource, }) {
141
+ const spinner = ora(dryRunStartLine(defs.length, resource)).start();
140
142
  try {
141
143
  const entries = await computeDiffEntries({ client, defs, target });
142
- spinner.succeed(`Diffed ${defs.length} definition(s)`);
144
+ spinner.succeed(resultLine({ verb: 'Diffed', count: defs.length, where }));
143
145
  for (const line of diffReport(entries))
144
146
  this.log(line);
145
147
  }
@@ -194,6 +196,18 @@ export function deploySummary({ total, failures, }) {
194
196
  export function deploymentBanner({ deployment, index, total, }) {
195
197
  return groupBanner({ label: deploymentLabel(deployment), index, total });
196
198
  }
199
+ export function targetLabel(deployment) {
200
+ return `${deploymentLabel(deployment)} → ${resourceLabel(deployment.workflowResource)}`;
201
+ }
202
+ export function deployStartLine(count, where) {
203
+ return `Deploying ${count} definition(s) to ${where}…`;
204
+ }
205
+ export function dryRunStartLine(count, resource) {
206
+ return `Diffing ${count} definition(s) against ${resource}…`;
207
+ }
208
+ export function resultLine({ verb, count, where, }) {
209
+ return `${verb} ${count} definition(s) · ${where}`;
210
+ }
197
211
  export function deployResultLines(results) {
198
212
  const symbols = {
199
213
  created: logSymbols.success,
@@ -51,6 +51,7 @@ export default class List extends WorkflowCommand {
51
51
  static description: string;
52
52
  static examples: string[];
53
53
  static flags: {
54
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
54
55
  'include-completed': import("@oclif/core/interfaces").BooleanFlag<boolean>;
55
56
  failed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
56
57
  definition: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -4,8 +4,8 @@ import logSymbols from 'log-symbols';
4
4
  import { WorkflowCommand } from "../../lib/base-command.js";
5
5
  import { resolveReadTargets } from "../../lib/context.js";
6
6
  import { failOnThrow } from "../../lib/fail.js";
7
- import { tagFlags } from "../../lib/flags.js";
8
- import { runReadAcrossTargets } from "../../lib/read-fanout.js";
7
+ import { jsonFlags, tagFlags } from "../../lib/flags.js";
8
+ import { logJsonListing, runReadAcrossTargets } from "../../lib/read-fanout.js";
9
9
  import { formatAge, logClippedTable } from "../../lib/ui.js";
10
10
  const DOCUMENT_LIST_PROJECTION = `{
11
11
  _type,
@@ -85,6 +85,7 @@ export default class List extends WorkflowCommand {
85
85
  '<%= config.bin %> list --definition productLaunch',
86
86
  '<%= config.bin %> list --document dataset:proj:ds:article-1',
87
87
  '<%= config.bin %> list --tag prod',
88
+ '<%= config.bin %> list --json',
88
89
  ];
89
90
  static flags = {
90
91
  ...tagFlags,
@@ -107,12 +108,24 @@ export default class List extends WorkflowCommand {
107
108
  description: 'Maximum rows to return.',
108
109
  default: 50,
109
110
  }),
111
+ ...jsonFlags,
110
112
  };
111
113
  async run() {
112
114
  const { flags } = await this.parse(List);
113
115
  const targets = await resolveReadTargets(flags);
114
116
  const { groq, params } = failOnThrow('Invalid --document:', () => buildListQuery(flags));
115
117
  const document = flags.document;
118
+ if (flags.json) {
119
+ await logJsonListing({
120
+ targets,
121
+ key: 'instances',
122
+ limit: flags.limit,
123
+ log: (line) => this.log(line),
124
+ fetch: ({ client }) => fetchRows({ client, groq, params, document, limit: flags.limit }),
125
+ toRow: instanceRow,
126
+ });
127
+ return;
128
+ }
116
129
  const failures = await runReadAcrossTargets({
117
130
  targets,
118
131
  log: (line) => this.log(line),
@@ -11,6 +11,7 @@ export default class ResetActivity extends WorkflowCommand {
11
11
  };
12
12
  static flags: {
13
13
  skip: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
16
  };
16
17
  run(): Promise<void>;
@@ -3,7 +3,7 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { workflow } from '@sanity/workflow-engine';
4
4
  import { WorkflowCommand } from "../../lib/base-command.js";
5
5
  import { resolveInstanceContext } from "../../lib/context.js";
6
- import { tagFlags } from "../../lib/flags.js";
6
+ import { instanceFlags } from "../../lib/flags.js";
7
7
  import { buildOperationArgs } from "../../lib/operation-args.js";
8
8
  import { cascadeTail, runWriteVerb, } from "../../lib/ops-report.js";
9
9
  export default class ResetActivity extends WorkflowCommand {
@@ -18,7 +18,7 @@ export default class ResetActivity extends WorkflowCommand {
18
18
  activity: Args.string({ required: true, description: 'Activity name within the current stage.' }),
19
19
  };
20
20
  static flags = {
21
- ...tagFlags,
21
+ ...instanceFlags,
22
22
  skip: Flags.boolean({
23
23
  default: false,
24
24
  description: 'Bypass the activity (mark it skipped) instead of re-running it (back to active).',
@@ -8,6 +8,7 @@ export default class Show extends WorkflowCommand {
8
8
  instanceId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
9
9
  };
10
10
  static flags: {
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  include: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
13
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  };
@@ -4,7 +4,7 @@ import { abortReason, displayTitle, terminalState, } from '@sanity/workflow-engi
4
4
  import logSymbols from 'log-symbols';
5
5
  import { WorkflowCommand } from "../../lib/base-command.js";
6
6
  import { findInstance, resolveReadTargets } from "../../lib/context.js";
7
- import { tagFlags } from "../../lib/flags.js";
7
+ import { jsonFlags, tagFlags } from "../../lib/flags.js";
8
8
  import { formatKeyValue, formatTimestamp, sectionHeader, activityIcon } from "../../lib/ui.js";
9
9
  export default class Show extends WorkflowCommand {
10
10
  static aliases = ['show'];
@@ -12,6 +12,7 @@ export default class Show extends WorkflowCommand {
12
12
  static examples = [
13
13
  '<%= config.bin %> show wf-instance.abc123',
14
14
  '<%= config.bin %> show wf-instance.abc123 --include history',
15
+ '<%= config.bin %> show wf-instance.abc123 --json',
15
16
  ];
16
17
  static args = {
17
18
  instanceId: Args.string({
@@ -22,11 +23,12 @@ export default class Show extends WorkflowCommand {
22
23
  static flags = {
23
24
  ...tagFlags,
24
25
  include: Flags.string({
25
- description: 'Optional sections to include in output.',
26
+ description: 'Optional sections to include in rendered output (--json always carries the full document).',
26
27
  options: ['history'],
27
28
  multiple: true,
28
29
  default: [],
29
30
  }),
31
+ ...jsonFlags,
30
32
  };
31
33
  async run() {
32
34
  const { args, flags } = await this.parse(Show);
@@ -36,6 +38,10 @@ export default class Show extends WorkflowCommand {
36
38
  this.error(`${logSymbols.error} no instance with id "${args.instanceId}"`, { exit: 1 });
37
39
  }
38
40
  const { instance } = hit;
41
+ if (flags.json) {
42
+ this.log(JSON.stringify(instance, null, 2));
43
+ return;
44
+ }
39
45
  this.log(instanceHeader(instance));
40
46
  this.log('');
41
47
  for (const line of describeInstance(instance, {
@@ -55,14 +61,16 @@ export function instanceHeader(instance) {
55
61
  if (terminalState(instance) === 'aborted' && instance.abortedAt !== undefined) {
56
62
  const reason = abortReason(instance);
57
63
  const tail = reason ? ` — ${reason}` : '';
58
- const value = styleText('yellow', `${formatTimestamp(instance.abortedAt)}${tail}`);
59
- rows.push(formatKeyValue({ key: 'Aborted', value, padTo: HEADER_PAD }));
64
+ const value = `${formatTimestamp(instance.abortedAt)}${tail}`;
65
+ rows.push(formatKeyValue({ key: 'Aborted', value, padTo: HEADER_PAD, emphasis: 'yellow' }));
66
+ }
67
+ else if (instance.completedAt) {
68
+ const value = formatTimestamp(instance.completedAt);
69
+ rows.push(formatKeyValue({ key: 'Completed', value, padTo: HEADER_PAD, emphasis: 'green' }));
60
70
  }
61
71
  else {
62
- const completed = instance.completedAt
63
- ? styleText('green', formatTimestamp(instance.completedAt))
64
- : styleText('dim', '—');
65
- rows.push(formatKeyValue({ key: 'Completed', value: completed, padTo: HEADER_PAD }));
72
+ const value = styleText('dim', '—');
73
+ rows.push(formatKeyValue({ key: 'Completed', value, padTo: HEADER_PAD }));
66
74
  }
67
75
  rows.push(formatKeyValue({ key: 'Tag', value: instance.tag, padTo: HEADER_PAD }));
68
76
  return [title, ...rows].join('\n');
@@ -1,7 +1,6 @@
1
1
  import type { SanityClient } from '@sanity/client';
2
2
  import { type WorkflowConfig, type WorkflowDeployment, type WorkflowInstance, type WorkflowResource } from '@sanity/workflow-engine';
3
3
  import type { EngineScope } from './operation-args.ts';
4
- import { type ChooseDeploymentName } from './select-deployment.ts';
5
4
  export interface DeploymentContext {
6
5
  deployment: WorkflowDeployment;
7
6
  client: SanityClient;
@@ -51,20 +50,18 @@ export declare function dedupeResources(resources: WorkflowResource[]): Workflow
51
50
  * tag (tags may repeat across deployments), or — untagged — every distinct
52
51
  * one the config's deployments mention ({@link dedupeResources}). */
53
52
  export declare function resolveReadResources(config: WorkflowConfig, tag: string | undefined): WorkflowResource[];
54
- /** The single resource an instance-keyed command reads from when it isn't
55
- * disambiguated interactively: the sole resource, the `--tag`-narrowed one, or
56
- * a `fail` asking for `--deployment`/`--tag` when the config spans several. The
57
- * interactive picker lives in {@link resolveInstanceResource}; listings fan out
58
- * via {@link resolveReadResources} instead. */
59
- export declare function resolveReadResource(config: WorkflowConfig, tag: string | undefined): WorkflowResource;
60
53
  /**
61
- * The resolution an instance-id-targeted command shares, read or write: an
62
- * authenticated client for the resource, plus the instance itself — so the
63
- * command derives its partition (`tag`) from the instance, never from the
64
- * config's declared deployments. An instance id is globally unique and carries
65
- * its own `tag`, so a command that names one acts on that instance regardless
66
- * of which tags the config happens to deploy; the config only locates the
67
- * resource (via {@link resolveInstanceResource}, no tag filter).
54
+ * The resolution an instance-id-targeted command shares, read or write: locate
55
+ * the instance by the same cross-resource fan-out the read/list commands use
56
+ * ({@link findInstance}), then hand back an authenticated client for the
57
+ * resource that held it plus a scope whose `tag` comes from the instance
58
+ * itself never from the config's declared deployments. An instance id is
59
+ * globally unique and carries its own `tag`, so naming one is enough to act on
60
+ * it: the config only locates candidate resources, and the fan-out's
61
+ * {@link soleHitOrFail} rejects the collision case where several datasets hold
62
+ * the id (the `<tag>.` fast-path may skip that full-set check — safe, ids are
63
+ * collision-free). `--deployment` / `--tag` stay optional narrowers, not
64
+ * required disambiguators.
68
65
  *
69
66
  * Contrast {@link resolveContext}: the deploy/diff/delete path IS scoped to a
70
67
  * declared deployment because it acts on the authored definition set (or a
@@ -74,29 +71,6 @@ export declare function resolveInstanceContext(flags: {
74
71
  deployment?: string | undefined;
75
72
  tag?: string | undefined;
76
73
  }, instanceId: string): Promise<InstanceContext>;
77
- /**
78
- * Which resource an instance-keyed command reads from: the one `--deployment`
79
- * names (resolved by its unique deployment name); otherwise, when the config
80
- * spans several resources, an interactive terminal is prompted to pick a
81
- * deployment ({@link canPromptOnStderr}) — its resource is the read source, the
82
- * interactive counterpart to `--deployment`. A run that can't prompt falls to
83
- * the sole or `--tag`-narrowed resource, or the ambiguity error
84
- * ({@link resolveReadResource}). The instance id is globally unique, so this
85
- * only locates the resource to look in — never the instance's `tag` partition,
86
- * which the caller takes from the loaded instance. The shared path behind all
87
- * four instance-keyed commands.
88
- */
89
- export declare function resolveInstanceResource(config: WorkflowConfig, { deployment, tag, interactive, chooseDeployment, }: {
90
- deployment?: string | undefined;
91
- tag?: string | undefined;
92
- interactive?: boolean | undefined;
93
- chooseDeployment?: ChooseDeploymentName | undefined;
94
- }): Promise<WorkflowResource>;
95
- /** Fetch an instance by id, exiting cleanly when the resource has no such
96
- * document — the diagnostic an operator sees on a mistyped id. Split out from
97
- * {@link resolveInstanceContext} so the not-found path is unit-testable
98
- * without a live config/token/client. */
99
- export declare function loadInstanceOrFail(client: Pick<SanityClient, 'getDocument'>, instanceId: string): Promise<WorkflowInstance>;
100
74
  /** A read target that turned out to hold the instance being looked up. */
101
75
  export interface InstanceHit extends ReadTarget {
102
76
  instance: WorkflowInstance;
@@ -2,19 +2,20 @@ import { assertReadableModel, resourceGdr, } from '@sanity/workflow-engine';
2
2
  import { clientFor, resolveTokenOrFail } from "./client.js";
3
3
  import { fail } from "./fail.js";
4
4
  import { loadWorkflowConfig } from "./load-config.js";
5
- import { canPromptOnStderr } from "./prompt.js";
6
- import { availableDeployments, deploymentsForTag, selectDeployment, } from "./select-deployment.js";
5
+ import { deploymentsForTag, selectDeployment } from "./select-deployment.js";
7
6
  import { resourceLabel } from "./ui.js";
8
7
  export async function resolveContext(flags) {
9
8
  const config = await loadWorkflowConfig();
10
9
  const deployment = await selectDeployment(config, { name: flags.deployment, tag: flags.tag });
11
10
  return { deployment, client: clientFor(deployment.workflowResource, await resolveTokenOrFail()) };
12
11
  }
12
+ function targetsFor(resources, token) {
13
+ return resources.map((resource) => ({ resource, client: clientFor(resource, token) }));
14
+ }
13
15
  export async function resolveReadTargets(flags) {
14
16
  const config = await loadWorkflowConfig();
15
17
  const resources = resolveReadResources(config, flags.tag);
16
- const token = await resolveTokenOrFail();
17
- return resources.map((resource) => ({ resource, client: clientFor(resource, token) }));
18
+ return targetsFor(resources, await resolveTokenOrFail());
18
19
  }
19
20
  export function dedupeResources(resources) {
20
21
  return [...new Map(resources.map((resource) => [resourceGdr(resource), resource])).values()];
@@ -29,50 +30,53 @@ export function resolveReadResources(config, tag) {
29
30
  }
30
31
  return resources;
31
32
  }
32
- export function resolveReadResource(config, tag) {
33
- const resources = resolveReadResources(config, tag);
34
- const [sole] = resources;
35
- if (resources.length === 1 && sole !== undefined) {
36
- return sole;
33
+ const LOCATE_TAG = 'instance.load';
34
+ export async function resolveInstanceContext(flags, instanceId) {
35
+ const config = await loadWorkflowConfig();
36
+ const token = await resolveTokenOrFail();
37
+ const hit = await locateInstance({ config, flags, instanceId, token });
38
+ if (hit === undefined) {
39
+ fail(`Workflow instance ${instanceId} not found`);
37
40
  }
38
- if (tag === undefined) {
39
- fail('Config spans multiple resources — pass --deployment or --tag to choose one.', availableDeployments(config));
41
+ return { client: hit.client, scope: { tag: hit.instance.tag, workflowResource: hit.resource } };
42
+ }
43
+ async function locateInstance({ config, flags, instanceId, token, }) {
44
+ const resources = await instanceCandidateResources(config, flags);
45
+ const fanOut = () => findInstance({ targets: targetsFor(resources, token), instanceId, requestTag: LOCATE_TAG });
46
+ const explicit = flags.deployment !== undefined || flags.tag !== undefined;
47
+ const hinted = explicit || resources.length <= 1
48
+ ? []
49
+ : hintedResources({ config, instanceId, candidates: resources });
50
+ if (hinted.length === 0 || hinted.length === resources.length) {
51
+ return fanOut();
40
52
  }
41
- const carriers = config.deployments.filter((d) => d.tag === tag);
42
- fail(`Tag "${tag}" spans multiple resources — pass --deployment to choose one.`, availableDeployments(config, carriers));
53
+ const hit = await hintedProbe(targetsFor(hinted, token), instanceId);
54
+ return hit ?? fanOut();
43
55
  }
44
- export async function resolveInstanceContext(flags, instanceId) {
45
- const config = await loadWorkflowConfig();
46
- const workflowResource = await resolveInstanceResource(config, flags);
47
- const client = clientFor(workflowResource, await resolveTokenOrFail());
48
- const instance = await loadInstanceOrFail(client, instanceId);
49
- return { client, scope: { tag: instance.tag, workflowResource } };
56
+ async function hintedProbe(targets, instanceId) {
57
+ try {
58
+ return await findInstance({ targets, instanceId, requestTag: LOCATE_TAG });
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
50
63
  }
51
- export async function resolveInstanceResource(config, { deployment, tag, interactive, chooseDeployment, }) {
64
+ async function instanceCandidateResources(config, { deployment, tag }) {
52
65
  if (deployment !== undefined) {
53
- return (await selectDeployment(config, { name: deployment, tag: undefined })).workflowResource;
54
- }
55
- if (tag === undefined &&
56
- (interactive ?? canPromptOnStderr()) &&
57
- resolveReadResources(config, undefined).length > 1) {
58
- const chosen = await selectDeployment(config, {
59
- name: undefined,
60
- tag: undefined,
61
- interactive: true,
62
- chooseName: chooseDeployment,
63
- });
64
- return chosen.workflowResource;
66
+ return [(await selectDeployment(config, { name: deployment, tag: undefined })).workflowResource];
65
67
  }
66
- return resolveReadResource(config, tag);
68
+ return resolveReadResources(config, tag);
67
69
  }
68
- export async function loadInstanceOrFail(client, instanceId) {
69
- const instance = await client.getDocument(instanceId, {
70
- tag: 'instance.load',
71
- });
72
- if (!instance) {
73
- fail(`Workflow instance ${instanceId} not found`);
70
+ function hintedResources({ config, instanceId, candidates, }) {
71
+ const dot = instanceId.indexOf('.');
72
+ if (dot <= 0) {
73
+ return [];
74
74
  }
75
- return assertReadableModel(instance);
75
+ const prefix = instanceId.slice(0, dot);
76
+ const taggedGdrs = new Set(config.deployments
77
+ .filter((deployment) => deployment.tag === prefix)
78
+ .map((deployment) => resourceGdr(deployment.workflowResource)));
79
+ return candidates.filter((resource) => taggedGdrs.has(resourceGdr(resource)));
76
80
  }
77
81
  export function soleHitOrFail(hits, subject) {
78
82
  const [sole, ...rest] = hits;
@@ -7,13 +7,13 @@
7
7
  export declare const tagFlags: {
8
8
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
- /** The selectors an instance-keyed command (`diagnose`, `abort`, `set-stage`,
11
- * `fire-action`) takes. An instance id is globally unique and carries its own
12
- * `tag`, so these only pick WHICH resource to read it from — never the
13
- * instance's partition, which always comes from the loaded instance.
14
- * `--deployment` names one deployment and reads from the resource it targets;
15
- * `--tag` stays the optional narrower {@link tagFlags} describes. Mutually
16
- * exclusive; a sole-resource config needs neither. */
10
+ /** The optional narrowers an instance-keyed command (`diagnose`, `abort`,
11
+ * `set-stage`, `fire-action`, `reset-activity`) takes. The command locates the
12
+ * instance by fanning out across every configured resource, so neither is ever
13
+ * required — an instance id is globally unique and carries its own `tag`. They
14
+ * only narrow WHERE to look: `--deployment` to the resource one deployment
15
+ * targets, `--tag` to the resources under that tag. The instance's partition
16
+ * always comes from the loaded instance, never from these. Mutually exclusive. */
17
17
  export declare const instanceFlags: {
18
18
  deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
19
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -28,6 +28,11 @@ export declare const deploymentFlags: {
28
28
  deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
29
29
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
30
30
  };
31
+ /** Structured stdout instead of the rendered view. One payload convention:
32
+ * verb commands emit their operation envelope (`instanceId` carries the doc
33
+ * `_id`); read commands emit document-shaped data — `show` commands print
34
+ * the fetched document as stored, list commands print the table's row view
35
+ * (plus each row's `resource`) under a `truncated` flag. */
31
36
  export declare const jsonFlags: {
32
37
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
33
38
  };
package/dist/lib/flags.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Flags } from '@oclif/core';
2
- const tagAsFilterDescription = 'Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.';
2
+ const tagAsFilterDescription = 'Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.';
3
3
  export const tagFlags = {
4
4
  tag: Flags.string({ description: tagAsFilterDescription }),
5
5
  };
6
6
  export const instanceFlags = {
7
7
  deployment: Flags.string({
8
- description: 'Deployment name — read the instance from the resource that deployment targets; the tag partition still comes from the loaded instance.',
8
+ description: 'Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.',
9
9
  exclusive: ['tag'],
10
10
  }),
11
11
  tag: Flags.string({ description: tagAsFilterDescription, exclusive: ['deployment'] }),
@@ -6,6 +6,29 @@ export interface ReadFailure {
6
6
  resource: WorkflowResource;
7
7
  message: string;
8
8
  }
9
+ /**
10
+ * The `--json` tail of a list command — the structured counterpart of
11
+ * {@link runReadAcrossTargets} + the clipped table: fetch every target's
12
+ * rows, {@link clipToLimit} each target's page, map rows through the
13
+ * command's row view (`toRow`, the structured analogue of the table's
14
+ * `toCells`), annotate each with the resource it came from (the analogue of
15
+ * the banner), and log one `{<key>: rows, truncated}` payload.
16
+ * All-or-nothing, unlike the human path's per-resource tolerance: a script
17
+ * would mistake a listing that silently omitted an unreadable resource for
18
+ * the complete result, so any failure rejects the whole read.
19
+ */
20
+ export declare function logJsonListing<T, R>({ targets, fetch, toRow, key, limit, log, }: {
21
+ targets: ReadTarget[];
22
+ fetch: (target: ReadTarget) => Promise<{
23
+ rows: T[];
24
+ hasMore: boolean;
25
+ }>;
26
+ toRow: (row: T) => R;
27
+ /** The payload's row key — names what the rows are (`instances`, `definitions`). */
28
+ key: string;
29
+ limit: number;
30
+ log: (line: string) => void;
31
+ }): Promise<void>;
9
32
  /**
10
33
  * Run a read over every target, grouping output per resource: a banner when
11
34
  * the read spans several, then whatever `run` logs for that target. A throw
@@ -1,6 +1,22 @@
1
1
  import logSymbols from 'log-symbols';
2
2
  import { failureDetail } from "./fail.js";
3
- import { groupBanner, resourceLabel } from "./ui.js";
3
+ import { clipToLimit, groupBanner, resourceLabel } from "./ui.js";
4
+ export async function logJsonListing({ targets, fetch, toRow, key, limit, log, }) {
5
+ const fetched = await Promise.all(targets.map(async (target) => {
6
+ const { rows, hasMore } = await fetch(target);
7
+ return {
8
+ rows: clipToLimit(rows, limit).rows.map((row) => ({
9
+ ...toRow(row),
10
+ resource: target.resource,
11
+ })),
12
+ hasMore: hasMore || rows.length > limit,
13
+ };
14
+ }));
15
+ log(JSON.stringify({
16
+ [key]: fetched.flatMap((result) => result.rows),
17
+ truncated: fetched.some((result) => result.hasMore),
18
+ }, null, 2));
19
+ }
4
20
  export async function runReadAcrossTargets({ targets, log, run, }) {
5
21
  const failures = [];
6
22
  for (const [index, target] of targets.entries()) {
@@ -48,10 +48,6 @@ export declare function deploymentsForTag(config: WorkflowConfig, tag: string):
48
48
  * explicit opt-in — never a default.
49
49
  */
50
50
  export declare function selectDeployments(config: WorkflowConfig, { name, tag, allTags, interactive, chooseName }: DeploymentSelectionOptions): Promise<WorkflowDeployment[]>;
51
- /** The "Available deployments: name (tag), …" hint every ambiguity failure
52
- * prints — one vocabulary across the write and instance-keyed paths, so a
53
- * reader always sees both the `--deployment` names and their `--tag`s. */
54
- export declare function availableDeployments(config: WorkflowConfig, deployments?: WorkflowDeployment[]): string;
55
51
  /**
56
52
  * Project a deployment into the engine's {@link DeployTarget} — the shape the
57
53
  * deploy/diff verbs (`deployDefinitions`, `computeDiffEntries`, `diffEntry`)
@@ -66,7 +66,7 @@ async function chooseDeploymentName(deployments) {
66
66
  choices: deployments.map(({ name, tag }) => ({ name, value: name, description: `tag: ${tag}` })),
67
67
  });
68
68
  }
69
- export function availableDeployments(config, deployments = config.deployments) {
69
+ function availableDeployments(config, deployments = config.deployments) {
70
70
  return `Available deployments: ${deployments.map(deploymentLabel).join(', ')}`;
71
71
  }
72
72
  export function deploymentToTarget(deployment) {
package/dist/lib/ui.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { styleText } from 'node:util';
1
2
  import type { ActivityStatus, WorkflowResource } from '@sanity/workflow-engine';
2
3
  /**
3
4
  * A bold `Title:` section heading. Mirrors the Sanity CLI's `sectionHeader`
@@ -8,12 +9,17 @@ export declare function sectionHeader(title: string): string;
8
9
  * A ` key: value` detail row — a dim, padded key then the value. Mirrors
9
10
  * the Sanity CLI's `formatKeyValue` so it folds into the shared helper on
10
11
  * merge; pass `padTo` (the longest key's length) to align a block of rows.
12
+ *
13
+ * Pass `emphasis` (a {@link styleText} colour) to render the whole row — key
14
+ * and value together — bold in that colour instead of the dim-key default, so
15
+ * a standout row (a terminal instance status) pops from the neutral rows.
11
16
  */
12
- export declare function formatKeyValue({ key, value, indent, padTo, }: {
17
+ export declare function formatKeyValue({ key, value, indent, padTo, emphasis, }: {
13
18
  key: string;
14
19
  value: string;
15
20
  indent?: number;
16
21
  padTo?: number;
22
+ emphasis?: Parameters<typeof styleText>[0];
17
23
  }): string;
18
24
  /**
19
25
  * A borderless column table as printable lines: cyan header row, every
package/dist/lib/ui.js CHANGED
@@ -6,9 +6,13 @@ import logSymbols from 'log-symbols';
6
6
  export function sectionHeader(title) {
7
7
  return styleText('bold', `${title}:`);
8
8
  }
9
- export function formatKeyValue({ key, value, indent = 2, padTo = 0, }) {
9
+ export function formatKeyValue({ key, value, indent = 2, padTo = 0, emphasis, }) {
10
10
  const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1);
11
- return `${' '.repeat(indent)}${styleText('dim', paddedKey)} ${value}`;
11
+ const pad = ' '.repeat(indent);
12
+ if (emphasis) {
13
+ return `${pad}${styleText('bold', styleText(emphasis, `${paddedKey} ${value}`))}`;
14
+ }
15
+ return `${pad}${styleText('dim', paddedKey)} ${value}`;
12
16
  }
13
17
  export function formatTable(headers, rows) {
14
18
  const width = (cell) => stripVTControlCharacters(cell).length;
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "flags": {
20
20
  "deployment": {
21
- "description": "Deployment name — read the instance from the resource that deployment targets; the tag partition still comes from the loaded instance.",
21
+ "description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
22
22
  "exclusive": [
23
23
  "tag"
24
24
  ],
@@ -28,7 +28,7 @@
28
28
  "type": "option"
29
29
  },
30
30
  "tag": {
31
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
31
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
32
32
  "exclusive": [
33
33
  "deployment"
34
34
  ],
@@ -165,7 +165,7 @@
165
165
  ],
166
166
  "flags": {
167
167
  "deployment": {
168
- "description": "Deployment name — read the instance from the resource that deployment targets; the tag partition still comes from the loaded instance.",
168
+ "description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
169
169
  "exclusive": [
170
170
  "tag"
171
171
  ],
@@ -175,7 +175,7 @@
175
175
  "type": "option"
176
176
  },
177
177
  "tag": {
178
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
178
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
179
179
  "exclusive": [
180
180
  "deployment"
181
181
  ],
@@ -225,7 +225,7 @@
225
225
  ],
226
226
  "flags": {
227
227
  "deployment": {
228
- "description": "Deployment name — read the instance from the resource that deployment targets; the tag partition still comes from the loaded instance.",
228
+ "description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
229
229
  "exclusive": [
230
230
  "tag"
231
231
  ],
@@ -235,7 +235,7 @@
235
235
  "type": "option"
236
236
  },
237
237
  "tag": {
238
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
238
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
239
239
  "exclusive": [
240
240
  "deployment"
241
241
  ],
@@ -299,11 +299,12 @@
299
299
  "<%= config.bin %> list --include-completed",
300
300
  "<%= config.bin %> list --definition productLaunch",
301
301
  "<%= config.bin %> list --document dataset:proj:ds:article-1",
302
- "<%= config.bin %> list --tag prod"
302
+ "<%= config.bin %> list --tag prod",
303
+ "<%= config.bin %> list --json"
303
304
  ],
304
305
  "flags": {
305
306
  "tag": {
306
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
307
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
307
308
  "name": "tag",
308
309
  "hasDynamicHelp": false,
309
310
  "multiple": false,
@@ -342,6 +343,12 @@
342
343
  "hasDynamicHelp": false,
343
344
  "multiple": false,
344
345
  "type": "option"
346
+ },
347
+ "json": {
348
+ "description": "Emit structured JSON instead of rendered output.",
349
+ "name": "json",
350
+ "allowNo": false,
351
+ "type": "boolean"
345
352
  }
346
353
  },
347
354
  "hasDynamicHelp": false,
@@ -429,8 +436,21 @@
429
436
  "<%= config.bin %> reset-activity wf-instance.abc123 legal-review --skip"
430
437
  ],
431
438
  "flags": {
439
+ "deployment": {
440
+ "description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
441
+ "exclusive": [
442
+ "tag"
443
+ ],
444
+ "name": "deployment",
445
+ "hasDynamicHelp": false,
446
+ "multiple": false,
447
+ "type": "option"
448
+ },
432
449
  "tag": {
433
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
450
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
451
+ "exclusive": [
452
+ "deployment"
453
+ ],
434
454
  "name": "tag",
435
455
  "hasDynamicHelp": false,
436
456
  "multiple": false,
@@ -476,7 +496,7 @@
476
496
  ],
477
497
  "flags": {
478
498
  "deployment": {
479
- "description": "Deployment name — read the instance from the resource that deployment targets; the tag partition still comes from the loaded instance.",
499
+ "description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
480
500
  "exclusive": [
481
501
  "tag"
482
502
  ],
@@ -486,7 +506,7 @@
486
506
  "type": "option"
487
507
  },
488
508
  "tag": {
489
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
509
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
490
510
  "exclusive": [
491
511
  "deployment"
492
512
  ],
@@ -540,18 +560,19 @@
540
560
  "description": "Show the state, activities, and effects of a workflow instance.",
541
561
  "examples": [
542
562
  "<%= config.bin %> show wf-instance.abc123",
543
- "<%= config.bin %> show wf-instance.abc123 --include history"
563
+ "<%= config.bin %> show wf-instance.abc123 --include history",
564
+ "<%= config.bin %> show wf-instance.abc123 --json"
544
565
  ],
545
566
  "flags": {
546
567
  "tag": {
547
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
568
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
548
569
  "name": "tag",
549
570
  "hasDynamicHelp": false,
550
571
  "multiple": false,
551
572
  "type": "option"
552
573
  },
553
574
  "include": {
554
- "description": "Optional sections to include in output.",
575
+ "description": "Optional sections to include in rendered output (--json always carries the full document).",
555
576
  "name": "include",
556
577
  "default": [],
557
578
  "hasDynamicHelp": false,
@@ -560,6 +581,12 @@
560
581
  "history"
561
582
  ],
562
583
  "type": "option"
584
+ },
585
+ "json": {
586
+ "description": "Emit structured JSON instead of rendered output.",
587
+ "name": "json",
588
+ "allowNo": false,
589
+ "type": "boolean"
563
590
  }
564
591
  },
565
592
  "hasDynamicHelp": false,
@@ -677,7 +704,7 @@
677
704
  ],
678
705
  "flags": {
679
706
  "tag": {
680
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
707
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
681
708
  "name": "tag",
682
709
  "hasDynamicHelp": false,
683
710
  "multiple": false,
@@ -843,11 +870,12 @@
843
870
  "description": "List deployed workflow definitions.",
844
871
  "examples": [
845
872
  "<%= config.bin %> definition list",
846
- "<%= config.bin %> definition list --tag prod"
873
+ "<%= config.bin %> definition list --tag prod",
874
+ "<%= config.bin %> definition list --json"
847
875
  ],
848
876
  "flags": {
849
877
  "tag": {
850
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
878
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
851
879
  "name": "tag",
852
880
  "hasDynamicHelp": false,
853
881
  "multiple": false,
@@ -867,6 +895,12 @@
867
895
  "hasDynamicHelp": false,
868
896
  "multiple": false,
869
897
  "type": "option"
898
+ },
899
+ "json": {
900
+ "description": "Emit structured JSON instead of rendered output.",
901
+ "name": "json",
902
+ "allowNo": false,
903
+ "type": "boolean"
870
904
  }
871
905
  },
872
906
  "hasDynamicHelp": false,
@@ -899,7 +933,7 @@
899
933
  "description": "Show a deployed workflow definition.",
900
934
  "flags": {
901
935
  "tag": {
902
- "description": "Workflow environment tag (e.g. prod, test) — an optional query filter, and the resource disambiguator when the config spans several.",
936
+ "description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
903
937
  "name": "tag",
904
938
  "hasDynamicHelp": false,
905
939
  "multiple": false,
@@ -911,6 +945,12 @@
911
945
  "hasDynamicHelp": false,
912
946
  "multiple": false,
913
947
  "type": "option"
948
+ },
949
+ "json": {
950
+ "description": "Emit structured JSON instead of rendered output.",
951
+ "name": "json",
952
+ "allowNo": false,
953
+ "type": "boolean"
914
954
  }
915
955
  },
916
956
  "hasDynamicHelp": false,
@@ -930,5 +970,5 @@
930
970
  ]
931
971
  }
932
972
  },
933
- "version": "0.21.0"
973
+ "version": "0.22.0"
934
974
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/workflow-cli",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Command-line tool for deploying, inspecting, and administering Sanity workflow definitions and instances.",
5
5
  "keywords": [
6
6
  "cli",
@@ -62,12 +62,12 @@
62
62
  "@types/node": "^24.12.4",
63
63
  "oclif": "^4.23.16",
64
64
  "vitest": "^4.1.8",
65
- "@sanity/workflow-engine": "0.21.0",
66
- "@sanity/workflow-engine-test": "0.21.0",
67
- "@sanity/workflow-examples": "0.10.0"
65
+ "@sanity/workflow-engine": "0.22.0",
66
+ "@sanity/workflow-engine-test": "0.22.0",
67
+ "@sanity/workflow-examples": "0.10.1"
68
68
  },
69
69
  "peerDependencies": {
70
- "@sanity/workflow-engine": "0.21.0"
70
+ "@sanity/workflow-engine": "0.22.0"
71
71
  },
72
72
  "oclif": {
73
73
  "bin": "sanity-workflows",