@sanity/workflow-cli 0.11.0 → 0.13.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 (59) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +75 -46
  3. package/dist/commands/{abort.d.ts → editorial-workflows/abort.d.ts} +3 -2
  4. package/dist/commands/{abort.js → editorial-workflows/abort.js} +7 -6
  5. package/dist/commands/{definition → editorial-workflows/definition}/delete.d.ts +3 -2
  6. package/dist/commands/{definition → editorial-workflows/definition}/delete.js +6 -5
  7. package/dist/commands/{definition → editorial-workflows/definition}/diff.d.ts +2 -1
  8. package/dist/commands/{definition → editorial-workflows/definition}/diff.js +7 -6
  9. package/dist/commands/{definition → editorial-workflows/definition}/list.d.ts +5 -1
  10. package/dist/commands/{definition → editorial-workflows/definition}/list.js +27 -24
  11. package/dist/commands/{definition → editorial-workflows/definition}/show.d.ts +3 -2
  12. package/dist/commands/{definition → editorial-workflows/definition}/show.js +9 -8
  13. package/dist/commands/{deploy.d.ts → editorial-workflows/deploy.d.ts} +2 -1
  14. package/dist/commands/{deploy.js → editorial-workflows/deploy.js} +14 -13
  15. package/dist/commands/{diagnose.d.ts → editorial-workflows/diagnose.d.ts} +2 -1
  16. package/dist/commands/{diagnose.js → editorial-workflows/diagnose.js} +7 -6
  17. package/dist/commands/{fire-action.d.ts → editorial-workflows/fire-action.d.ts} +3 -2
  18. package/dist/commands/{fire-action.js → editorial-workflows/fire-action.js} +8 -7
  19. package/dist/commands/{list.d.ts → editorial-workflows/list.d.ts} +4 -1
  20. package/dist/commands/{list.js → editorial-workflows/list.js} +33 -28
  21. package/dist/commands/editorial-workflows/nuke.d.ts +12 -0
  22. package/dist/commands/editorial-workflows/nuke.js +77 -0
  23. package/dist/commands/{reset-activity.d.ts → editorial-workflows/reset-activity.d.ts} +2 -1
  24. package/dist/commands/{reset-activity.js → editorial-workflows/reset-activity.js} +2 -1
  25. package/dist/commands/{set-stage.d.ts → editorial-workflows/set-stage.d.ts} +4 -3
  26. package/dist/commands/{set-stage.js → editorial-workflows/set-stage.js} +6 -5
  27. package/dist/commands/{show.d.ts → editorial-workflows/show.d.ts} +2 -1
  28. package/dist/commands/{show.js → editorial-workflows/show.js} +40 -26
  29. package/dist/commands/{start.d.ts → editorial-workflows/start.d.ts} +20 -4
  30. package/dist/commands/{start.js → editorial-workflows/start.js} +58 -16
  31. package/dist/commands/{tail.d.ts → editorial-workflows/tail.d.ts} +2 -1
  32. package/dist/commands/{tail.js → editorial-workflows/tail.js} +10 -8
  33. package/dist/hooks/finally/telemetry.js +2 -2
  34. package/dist/hooks/prerun/telemetry.d.ts +4 -3
  35. package/dist/lib/base-command.d.ts +4 -6
  36. package/dist/lib/base-command.js +6 -0
  37. package/dist/lib/client.d.ts +8 -0
  38. package/dist/lib/client.js +1 -1
  39. package/dist/lib/context.d.ts +6 -2
  40. package/dist/lib/context.js +9 -7
  41. package/dist/lib/definitions.js +2 -2
  42. package/dist/lib/flags.d.ts +4 -3
  43. package/dist/lib/nuke.d.ts +89 -0
  44. package/dist/lib/nuke.js +111 -0
  45. package/dist/lib/operation-args.d.ts +3 -1
  46. package/dist/lib/ops-report.d.ts +2 -1
  47. package/dist/lib/prompt.d.ts +11 -0
  48. package/dist/lib/prompt.js +4 -0
  49. package/dist/lib/select-deployment.d.ts +12 -7
  50. package/dist/lib/select-deployment.js +26 -1
  51. package/dist/lib/share-definitions.d.ts +23 -28
  52. package/dist/lib/share-definitions.js +30 -40
  53. package/dist/lib/telemetry-setup.d.ts +2 -2
  54. package/dist/lib/telemetry.d.ts +18 -10
  55. package/dist/lib/telemetry.js +5 -2
  56. package/dist/lib/ui.d.ts +12 -0
  57. package/dist/lib/ui.js +9 -0
  58. package/oclif.manifest.json +138 -46
  59. package/package.json +11 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # @sanity/workflow-cli
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 58f8211: Every command's canonical oclif id now nests under the `editorial-workflows` topic, making the package mountable as a plugin of the `sanity` CLI (`sanity editorial-workflows deploy`) without colliding with host commands. The standalone surface is unchanged: the bare forms (`sanity-workflows deploy`, `sanity-workflows definition list`, …) remain as aliases of the nested ids.
8
+ - 00c089d: **BREAKING:** Require every definition deployment and workflow deployment configuration to state an `expectedMinReaderModel` literal that exactly matches the installed engine's writer capability.
9
+
10
+ Deploy and definition-diff paths now reject missing or stale acknowledgements before client access or telemetry, with a structured error and readers-first rollout guidance.
11
+
12
+ - 53661fa: Prompt interactive deploys to select a configured deployment tag when several are available and no selector flag was supplied. Render Ctrl+C prompt cancellations without a stack trace.
13
+
14
+ ### Patch Changes
15
+
16
+ - 82e4a4e: Fix definition sharing to honor its opt-out contract: deploys now share newly created definition versions by default in every environment, including CI, `DO_NOT_TRACK`, and non-TTY runs. The first interactive run shows a mandatory disclosure rather than a consent choice, every other flagless run prints a clear status block with the explicit opt-in and opt-out flags, and only `--no-share-defs` suppresses sharing for that invocation.
17
+ - 9126299: **BREAKING:** Delete persisted stage guard documents when their stage exits, and remove the lifted-record `GUARD_LIFTED_PREDICATE` and `isGuardLifted` exports.
18
+
19
+ Guard deletion now uses the observed document revision so stale exit reconciliation cannot delete a guard reactivated by a newer stage visit. Reactive guard streams contain only active persisted guards.
20
+
21
+ - Updated dependencies [239d9f6]
22
+ - Updated dependencies [3af2ca0]
23
+ - Updated dependencies [7276702]
24
+ - Updated dependencies [58f8211]
25
+ - Updated dependencies [00c089d]
26
+ - Updated dependencies [71cd58e]
27
+ - Updated dependencies [cd03973]
28
+ - Updated dependencies [d26cc1a]
29
+ - Updated dependencies [9126299]
30
+ - Updated dependencies [72018af]
31
+ - Updated dependencies [5cd8ffd]
32
+ - Updated dependencies [b82bda4]
33
+ - Updated dependencies [55a54b3]
34
+ - Updated dependencies [b82bda4]
35
+ - Updated dependencies [4490442]
36
+ - Updated dependencies [1540dce]
37
+ - @sanity/workflow-engine@0.18.0
38
+
39
+ ## 0.12.0
40
+
41
+ ### Minor Changes
42
+
43
+ - e3a7ba2: `start` gains `--instance-id` — for retries: the id is the start's idempotency key, so passing the id of a start that failed partway resumes it instead of creating a duplicate (an already-settled start replays as a no-op). A start whose first auto-advance fails after the instance was created and primed (`StartNotSettledError`) now reports as STARTED with the cause and the retry id — a warn, not the "Start rejected" failure path — and the `--json` payload carries `{instanceId, notSettled: true, detail}`. A start that fails before priming (`StartNotPrimedError`) still fails, with the `--instance-id` retry hint appended so the operator can resume instead of duplicating.
44
+ - a8ace4d: New `nuke --tag <tag>` command — the dev-period big red button that deletes every engine-owned document for a deployment tag: instances, definitions, and guard docs (matched by instance-id prefix, so guards orphaned by earlier partial deletions are swept too) across the deployment's workflow resource and every alias-bound resource. Content documents are never touched. The command always prints a per-dataset dry-run plan, then requires typing back every involved `project.dataset` target; `--force` skips the prompt for scripts/CI (the plan still prints). Unlike `definition delete` (the governed lifecycle path), `nuke` deletes raw docs without reading them, so it works on pre-amendment shapes a newer engine can no longer read — the sanctioned reset while the versioned upgrade framework doesn't exist.
45
+ - 5a1a9fe: Direct reads of engine-owned documents adopt the data-model gate (`assertReadableModel`): the CLI's instance and definition reads (`show`, `tail`, `list`, `definition show`, `definition list`, deploy lookups, the share read-back) and the MCP tools that interpret rows from the ungated `engine.query` escape hatch (`list_workflow_definitions`, `list_workflow_instances`, the definition lookup behind `start_workflow` / `get_workflow_definition`). A document written by a newer engine data model now fails with the governed "upgrade `@sanity/workflow-engine`" error instead of rendering unchecked; list projections carry the stamp pair so projected rows gate too.
46
+
47
+ ### Patch Changes
48
+
49
+ - 092a0d4: **BREAKING:** runtime-supplied refs written into field state are gated on the deployment's declared resource surface.
50
+ - A `doc.ref` / `doc.refs` / `release.ref` value a caller or effect handler supplies — start `initialFields` input, an action's param-sourced op values, `editField` values, effect-completion ops — must target a declared resource: the `workflowResource` itself or a resource the `resourceClients` resolver serves. An off-surface ref throws the new `RefResourceUndeclaredError` and the commit aborts, instead of writing cleanly and failing later when something dereferences it. The check is advisory like every engine check; stored documents are unchanged.
51
+ - Definition-authored refs are exempt: literal `initialValue` seeds, `type: 'query'` texts, spawn `with` projections, and op values that bind no action param were validated (and alias-expanded) at deploy, so they materialize into field state without re-checking.
52
+ - Resource aliases stay a deploy-time abstraction: `deployDefinitions({definitions, resourceAliases?})` expands every `@<alias>:` reference to its physical GDR before fingerprinting, and nothing alias-shaped survives past deploy — the runtime holds no alias map, and an alias-shaped ref supplied to a runtime verb is rejected as malformed like any other non-GDR string.
53
+ - `createBench` takes `serveResources` (dataset siblings served — and thereby declared — straight from the bench's own store) plus a raw `resourceClients` resolver for anything beyond same-store siblings. The engine also exports `sameResource` alongside the existing GDR helpers.
54
+ - The CLI wires no `resourceClients`, so refs supplied at runtime through it (`start --field`, `fire-action` ref params) admit the workflow resource only.
55
+
56
+ - Updated dependencies [f9389e5]
57
+ - Updated dependencies [d0c62ea]
58
+ - Updated dependencies [c3eed2e]
59
+ - Updated dependencies [df4bd80]
60
+ - Updated dependencies [092a0d4]
61
+ - Updated dependencies [5a1a9fe]
62
+ - Updated dependencies [e3a7ba2]
63
+ - Updated dependencies [30fed9e]
64
+ - Updated dependencies [1321ba5]
65
+ - Updated dependencies [e683875]
66
+ - Updated dependencies [a8ace4d]
67
+ - @sanity/workflow-engine@0.17.0
68
+
3
69
  ## 0.11.0
4
70
 
5
71
  ### Minor Changes
package/README.md CHANGED
@@ -20,6 +20,19 @@ pnpm --filter @sanity/workflow-cli dev list --include-completed
20
20
  pnpm --filter @sanity/workflow-cli dev show wf-instance.abc123
21
21
  ```
22
22
 
23
+ The package is an [oclif](https://oclif.io) plugin: every command's canonical
24
+ id nests under the `editorial-workflows` topic
25
+ (`sanity-workflows editorial-workflows deploy`), so mounting the package into
26
+ the `sanity` CLI's plugin list surfaces the same commands as
27
+ `sanity editorial-workflows …` without its canonical ids colliding with the
28
+ host's own commands (the sanity CLI already has a `deploy`). The bare forms
29
+ used throughout this README (`deploy`, `definition list`, …) are aliases of
30
+ the nested ids and are the standalone binary's stable surface. oclif registers
31
+ plugin aliases in a host unconditionally (an id collision resolves by plugin
32
+ priority, host first), so a host mount would also surface these bare aliases
33
+ at its root — trimming or hiding them for the mounted context is part of the
34
+ host-side mount work, not something a host can configure away.
35
+
23
36
  Authenticate once with `sanity login` (the CLI reads that session token); for
24
37
  CI, set `SANITY_AUTH_TOKEN` instead. Then create a `sanity.workflow.ts` in the
25
38
  directory you run from — see [Configuration](#configuration).
@@ -46,6 +59,7 @@ import {articleReview, urlDraft} from './src/workflows.ts'
46
59
  export default defineWorkflowConfig({
47
60
  deployments: [
48
61
  {
62
+ expectedMinReaderModel: 2,
49
63
  name: 'production',
50
64
  tag: 'prod', // the environment partition the engine's docs are scoped to
51
65
  workflowResource: {type: 'dataset', id: 'acme.workflows'}, // where those docs live
@@ -63,22 +77,33 @@ export default defineWorkflowConfig({
63
77
  ```
64
78
 
65
79
  Pick a deployment with `--tag <tag>`; with a single deployment configured you
66
- can omit it, and with several configured a bare `deploy` fails asking for
67
- `--tag` or `--all-tags`. `--all-tags` deploys every deployment in the config in
68
- one run: a failure in one doesn't stop the rest — the run continues, prints a
69
- summary of what failed, and exits non-zero. The client's project + dataset are derived
70
- from the deployment's `workflowResource`, and `deploy` expands each
71
- definition's `@<handle>:` reference to the bound physical resource. An invalid
72
- config fails with a clean, path-prefixed error before the command runs.
80
+ can omit it. With several configured, a bare interactive `deploy` presents a
81
+ keyboard-driven tag selector. In CI or another non-interactive shell, it fails
82
+ asking for `--tag` or `--all-tags` instead of blocking for input. `--all-tags`
83
+ deploys every deployment in the config in one run: a failure in one doesn't
84
+ stop the rest the run continues, prints a summary of what failed, and exits
85
+ non-zero. The client's project + dataset are derived from the deployment's
86
+ `workflowResource`, and `deploy` expands each definition's `@<handle>:`
87
+ reference to the bound physical resource. An invalid config fails with a clean,
88
+ path-prefixed error before the command runs.
73
89
 
74
90
  `resourceAliases` is only for content that lives in a _different_ resource from
75
91
  `workflowResource`. A definition can reference a document in the **same**
76
92
  resource by bare id, no alias needed — bare ids root at `workflowResource` at
77
- runtime. Rule of thumb: **different resource bind an alias and reference it as
78
- `@<handle>:<id>`; same resource use a bare id and omit `resourceAliases`
93
+ runtime. Bindings exist at deploy time only: `deploy` expands each `@<handle>:`
94
+ reference to its bound physical GDR, and nothing alias-shaped survives into the
95
+ deployed definition or the runtime. Rule of thumb: **different resource →
96
+ bind an alias; same resource → use a bare id and omit `resourceAliases`
79
97
  entirely.** So the simplest single-dataset setup is just a `workflowResource`
80
98
  and `definitions`, with no `resourceAliases` at all.
81
99
 
100
+ Refs supplied at runtime (`start --field`, `fire-action` ref params) are gated
101
+ separately: they may target the `workflowResource` itself, and anything else is
102
+ rejected at the write with `RefResourceUndeclaredError`. The CLI wires no
103
+ `resourceClients` resolver, so it has no way to widen that surface — a runtime
104
+ foreign-resource ref goes through an engine consumer that serves the target
105
+ resource, not through the CLI.
106
+
82
107
  | Var | Purpose |
83
108
  | ------------------- | ---------------------------------------------------------------------------------------------------- |
84
109
  | `SANITY_AUTH_TOKEN` | Explicit token for CI / scripted use — wins over the `sanity login` session (editor role for writes) |
@@ -118,25 +143,26 @@ stale compiled output.
118
143
 
119
144
  ## Command status
120
145
 
121
- | Command | Status |
122
- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
123
- | `deploy` | wired — calls `workflow.deployDefinitions` over the selected deployment's definitions |
124
- | `deploy --all-tags` | wired — deploys every deployment in the config, continuing past per-deployment failures |
125
- | `deploy --check` | wired — runs `validateDefinition` over the local batch + a duplicate-name check |
126
- | `deploy --dry-run` | wired — fetches existing docs and renders a coloured JSON diff per change |
127
- | `deploy --only <name>` | wired — filters deploy/check/dry-run to one definition by `name` |
128
- | `start <name>` | wired — calls `workflow.startInstance` (`--field` for input fields) |
129
- | `list` | wired — `client.fetch` over `sanity.workflow.instance` documents (`--definition <name>` to filter) |
130
- | `show <instance-id>` | wired — `client.getDocument` |
131
- | `diagnose <instance-id>` | wired — calls `workflow.diagnose`, classifies why the instance is/isn't progressing |
132
- | `tail <instance-id>` | wired — `client.listen()` over the instance, prints new history entries |
133
- | `abort <instance-id>` | wired — calls `workflow.abortInstance` (hard stop: cancels pending effects, lifts guards) |
134
- | `set-stage <instance-id> --to <stage>` | wired — calls `workflow.setStage` (admin override: skips declared transitions/filters; enter lifecycle + cascade still run) |
135
- | `fire-action <instance-id>` | wired — `workflow.availableActions` lists actions; `workflow.fireAction` fires one |
136
- | `definition list` | wired — `client.fetch` over `sanity.workflow.definition` documents |
137
- | `definition show <name>` | wired — `client.fetch`, latest version unless `--version` |
138
- | `definition diff <name>` | wired — diffs the in-code definition against the deployed latest (`--version` to pin) |
139
- | `definition delete <name>` | wired — calls `workflow.deleteDefinition` (refuses on live instances unless `--cascade`) |
146
+ | Command | Status |
147
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
148
+ | `deploy` | wired — calls `workflow.deployDefinitions` over the selected deployment's definitions |
149
+ | `deploy --all-tags` | wired — deploys every deployment in the config, continuing past per-deployment failures |
150
+ | `deploy --check` | wired — runs `validateDefinition` over the local batch + a duplicate-name check |
151
+ | `deploy --dry-run` | wired — fetches existing docs and renders a coloured JSON diff per change |
152
+ | `deploy --only <name>` | wired — filters deploy/check/dry-run to one definition by `name` |
153
+ | `start <name>` | wired — calls `workflow.startInstance` (`--field` for input fields) |
154
+ | `list` | wired — `client.fetch` over `sanity.workflow.instance` documents (`--definition <name>` to filter) |
155
+ | `show <instance-id>` | wired — `client.getDocument` |
156
+ | `diagnose <instance-id>` | wired — calls `workflow.diagnose`, classifies why the instance is/isn't progressing |
157
+ | `tail <instance-id>` | wired — `client.listen()` over the instance, prints new history entries |
158
+ | `abort <instance-id>` | wired — calls `workflow.abortInstance` (hard stop: cancels pending effects, removes guards) |
159
+ | `set-stage <instance-id> --to <stage>` | wired — calls `workflow.setStage` (admin override: skips declared transitions/filters; enter lifecycle + cascade still run) |
160
+ | `fire-action <instance-id>` | wired — `workflow.availableActions` lists actions; `workflow.fireAction` fires one |
161
+ | `definition list` | wired — `client.fetch` over `sanity.workflow.definition` documents |
162
+ | `definition show <name>` | wired — `client.fetch`, latest version unless `--version` |
163
+ | `definition diff <name>` | wired — diffs the in-code definition against the deployed latest (`--version` to pin) |
164
+ | `definition delete <name>` | wired — calls `workflow.deleteDefinition` (refuses on live instances unless `--cascade`) |
165
+ | `nuke --tag <tag>` | wired — dev-period reset (exists only until the versioned upgrade framework): deletes every engine-owned doc for the tag (instances, definitions, guards across resources); plan + typed confirm |
140
166
 
141
167
  ## Telemetry
142
168
 
@@ -145,8 +171,9 @@ per-command trace (`Editorial Workflows CLI Command Executed` — the command id
145
171
  names of declared flags used, never their values, and a success flag) plus
146
172
  the engine's adoption events from the operations it drives. Consent is the
147
173
  account-wide status managed by `npx sanity telemetry enable|disable|status`;
148
- CI and trueish `DO_NOT_TRACK` suppress everything (except a `deploy --share-defs`,
149
- which forces that deploy's telemetry see [Definition sharing](#definition-sharing)),
174
+ CI and trueish `DO_NOT_TRACK` suppress everything (except a deploy that may share
175
+ new definitions, which forces that deploy's telemetry unless `--no-share-defs`
176
+ is passed — see [Definition sharing](#definition-sharing)),
150
177
  and a session that isn't logged in sends nothing. A one-time notice on stderr
151
178
  discloses collection on first use.
152
179
 
@@ -166,25 +193,27 @@ definition-feedback endpoint, not the telemetry pipeline; telemetry carries
166
193
  only a content-free marker (content hash plus structural counts) per shared
167
194
  definition and a per-invocation decision event.
168
195
 
169
- Sharing is **opt-out**. Pass `--share-defs` to donate without being asked, or
170
- `--no-share-defs` to decline. With neither flag, an interactive terminal is
171
- asked **once** the answer is remembered in your Sanity user config, so the
172
- prompt never repeats before anything is sent; notice always precedes the
173
- send. Every later flagless deploy then prints a one-line reminder of the
174
- current state (sharing, or not) and the flag to change it, so an ongoing
175
- donation is never silent. Unattended runs (CI, `DO_NOT_TRACK`, a non-TTY pipe)
176
- can't prompt: by default they donate nothing (opt in per run with
177
- `--share-defs`). An explicit `--share-defs` also sends that deploy's telemetry
178
- regardless of CI / `DO_NOT_TRACK` a command-line arg beats env vars;
179
- account-level `sanity telemetry disable` still applies. A failed share only
180
- warns, never failing a completed deploy.
196
+ Sharing is **opt-out in every environment**. Pass `--no-share-defs` to decline
197
+ for one invocation. With neither flag, the first interactive deploy prints a
198
+ full notice before sending and remembers that the notice was shown; every later
199
+ flagless deploy prints a status block showing that sharing is on by default and
200
+ showing runnable shell examples for the default, explicit opt-in, and opt-out
201
+ forms; the explicit flags suppress the status block. Unattended runs (CI,
202
+ `DO_NOT_TRACK`, or a non-TTY pipe) also share by default and print that status
203
+ block in their logs.
204
+ A flagless deploy or explicit `--share-defs` forces that deploy's telemetry
205
+ through the environment gate so donated definitions retain their content-free
206
+ markers; account-level `sanity telemetry disable` still applies. A failed share
207
+ only warns, never failing a completed deploy.
181
208
 
182
209
  ## Known gaps
183
210
 
184
- - **No `sanity workflow` plugin wiring yet.** The proposal targets the
185
- Sanity CLI as a plugin host. The package publishes as a standalone
186
- `sanity-workflows` binary today; plugging into `sanity` as an oclif
187
- plugin is a separate piece of work.
211
+ - **Not mounted in the `sanity` CLI yet.** The package side is mount-ready —
212
+ commands nest under the `editorial-workflows` oclif topic (see
213
+ [Run](#run)) — but the `sanity` CLI does not list this package in its
214
+ plugin array, so the commands ship only through the standalone
215
+ `sanity-workflows` binary today. The host-side mount (and what happens to
216
+ the bare aliases inside a host) is a separate piece of work.
188
217
  - **No true bypass.** `set-stage` is the engine's `setStage` admin
189
218
  override: it skips the definition's declared transitions and filters, but
190
219
  the target stage's enter lifecycle and the post-move cascade still run. A
@@ -1,7 +1,8 @@
1
1
  import { type WorkflowInstance } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../lib/base-command.ts';
3
- import { type WriteReport } from '../lib/ops-report.ts';
2
+ import { WorkflowCommand } from '../../lib/base-command.ts';
3
+ import { type WriteReport } from '../../lib/ops-report.ts';
4
4
  export default class Abort extends WorkflowCommand {
5
+ static aliases: string[];
5
6
  static description: string;
6
7
  static examples: string[];
7
8
  static args: {
@@ -1,13 +1,14 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Args, Flags } from '@oclif/core';
3
3
  import { workflow } from '@sanity/workflow-engine';
4
- import { WorkflowCommand } from "../lib/base-command.js";
5
- import { resolveInstanceContext } from "../lib/context.js";
6
- import { tagFlags } from "../lib/flags.js";
7
- import { buildOperationArgs } from "../lib/operation-args.js";
8
- import { runWriteVerb } from "../lib/ops-report.js";
4
+ import { WorkflowCommand } from "../../lib/base-command.js";
5
+ import { resolveInstanceContext } from "../../lib/context.js";
6
+ import { tagFlags } from "../../lib/flags.js";
7
+ import { buildOperationArgs } from "../../lib/operation-args.js";
8
+ import { runWriteVerb } from "../../lib/ops-report.js";
9
9
  export default class Abort extends WorkflowCommand {
10
- static description = 'Abort an in-flight workflow instance — a hard stop: pending effects are cancelled, stage guards lifted, and the instance is marked terminal where it stands.';
10
+ static aliases = ['abort'];
11
+ static description = 'Abort an in-flight workflow instance — a hard stop: pending effects are cancelled, stage guards removed, and the instance is marked terminal where it stands.';
11
12
  static examples = [
12
13
  '<%= config.bin %> abort wf-instance.abc123',
13
14
  "<%= config.bin %> abort wf-instance.abc123 --reason 'superseded by relaunch'",
@@ -1,7 +1,8 @@
1
1
  import { type DeleteDefinitionArgs, type DeleteDefinitionResult } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../../lib/base-command.ts';
3
- import { type EngineScope } from '../../lib/operation-args.ts';
2
+ import { WorkflowCommand } from '../../../lib/base-command.ts';
3
+ import { type EngineScope } from '../../../lib/operation-args.ts';
4
4
  export default class DefinitionDelete extends WorkflowCommand {
5
+ static aliases: string[];
5
6
  static description: string;
6
7
  static examples: string[];
7
8
  static args: {
@@ -1,12 +1,13 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Args, Flags } from '@oclif/core';
3
3
  import { workflow, } from '@sanity/workflow-engine';
4
- import { WorkflowCommand } from "../../lib/base-command.js";
5
- import { resolveContext } from "../../lib/context.js";
6
- import { tagFlags } from "../../lib/flags.js";
7
- import { baseEngineArgs } from "../../lib/operation-args.js";
8
- import { runWriteVerb } from "../../lib/ops-report.js";
4
+ import { WorkflowCommand } from "../../../lib/base-command.js";
5
+ import { resolveContext } from "../../../lib/context.js";
6
+ import { tagFlags } from "../../../lib/flags.js";
7
+ import { baseEngineArgs } from "../../../lib/operation-args.js";
8
+ import { runWriteVerb } from "../../../lib/ops-report.js";
9
9
  export default class DefinitionDelete extends WorkflowCommand {
10
+ static aliases = ['definition:delete'];
10
11
  static description = 'Delete a deployed workflow definition (every version, or one via --version). ' +
11
12
  'Refuses while non-terminal instances exist unless --cascade aborts them first — ' +
12
13
  'instances are aborted in place, never deleted.';
@@ -1,6 +1,7 @@
1
1
  import { type WorkflowDefinition } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../../lib/base-command.ts';
2
+ import { WorkflowCommand } from '../../../lib/base-command.ts';
3
3
  export default class DefinitionDiff extends WorkflowCommand {
4
+ static aliases: string[];
4
5
  static description: string;
5
6
  static examples: string[];
6
7
  static args: {
@@ -1,13 +1,14 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { diffEntry } from '@sanity/workflow-engine';
3
3
  import ora from 'ora';
4
- import { WorkflowCommand } from "../../lib/base-command.js";
5
- import { resolveContext } from "../../lib/context.js";
6
- import { fetchDeployedDefinition, selectDefinitions, validateOrFail } from "../../lib/definitions.js";
7
- import { diffReport } from "../../lib/diff.js";
8
- import { tagFlags } from "../../lib/flags.js";
9
- import { deploymentToTarget } from "../../lib/select-deployment.js";
4
+ import { WorkflowCommand } from "../../../lib/base-command.js";
5
+ import { resolveContext } from "../../../lib/context.js";
6
+ import { fetchDeployedDefinition, selectDefinitions, validateOrFail, } from "../../../lib/definitions.js";
7
+ import { diffReport } from "../../../lib/diff.js";
8
+ import { tagFlags } from "../../../lib/flags.js";
9
+ import { deploymentToTarget } from "../../../lib/select-deployment.js";
10
10
  export default class DefinitionDiff extends WorkflowCommand {
11
+ static aliases = ['definition:diff'];
11
12
  static description = 'Diff an in-code definition against the deployed version (latest by default).';
12
13
  static examples = [
13
14
  '<%= config.bin %> definition diff productLaunch',
@@ -1,10 +1,11 @@
1
- import { WorkflowCommand } from '../../lib/base-command.ts';
1
+ import { WorkflowCommand } from '../../../lib/base-command.ts';
2
2
  interface DefinitionListFlags {
3
3
  tag?: string | undefined;
4
4
  name?: string | undefined;
5
5
  limit: number;
6
6
  }
7
7
  export interface DefinitionListRow {
8
+ _id: string;
8
9
  name: string;
9
10
  version: number;
10
11
  title: string;
@@ -13,12 +14,15 @@ export interface DefinitionListRow {
13
14
  inFlightCount: number;
14
15
  totalInstances: number;
15
16
  _createdAt: string;
17
+ modelVersion?: number | null;
18
+ minReaderModel?: number | null;
16
19
  }
17
20
  export declare function buildDefinitionListQuery(flags: DefinitionListFlags): {
18
21
  groq: string;
19
22
  params: Record<string, unknown>;
20
23
  };
21
24
  export default class DefinitionList extends WorkflowCommand {
25
+ static aliases: string[];
22
26
  static description: string;
23
27
  static examples: string[];
24
28
  static flags: {
@@ -1,11 +1,11 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, inFlightFilter, tagScopeFilter, } from '@sanity/workflow-engine';
2
+ import { WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, assertReadableModel, inFlightFilter, tagScopeFilter, } from '@sanity/workflow-engine';
3
3
  import logSymbols from 'log-symbols';
4
- import { WorkflowCommand } from "../../lib/base-command.js";
5
- import { resolveReadTargets } from "../../lib/context.js";
6
- import { tagFlags } from "../../lib/flags.js";
7
- import { runReadAcrossTargets } from "../../lib/read-fanout.js";
8
- import { clipToLimit, formatTable } from "../../lib/ui.js";
4
+ import { WorkflowCommand } from "../../../lib/base-command.js";
5
+ import { resolveReadTargets } from "../../../lib/context.js";
6
+ import { tagFlags } from "../../../lib/flags.js";
7
+ import { runReadAcrossTargets } from "../../../lib/read-fanout.js";
8
+ import { logClippedTable } from "../../../lib/ui.js";
9
9
  export function buildDefinitionListQuery(flags) {
10
10
  const params = { limit: flags.limit + 1 };
11
11
  const filters = [`_type == "${WORKFLOW_DEFINITION_TYPE}"`];
@@ -25,11 +25,14 @@ export function buildDefinitionListQuery(flags) {
25
25
  }
26
26
  const instanceMatch = instanceFilters.join(' && ');
27
27
  const groq = `*[${filters.join(' && ')}] | order(name asc, version desc) [0...$limit]{
28
+ _id,
28
29
  name,
29
30
  version,
30
31
  title,
31
32
  tag,
32
33
  _createdAt,
34
+ modelVersion,
35
+ minReaderModel,
33
36
  "stageCount": count(stages),
34
37
  "inFlightCount": count(*[${instanceMatch} && ${inFlightFilter()}]),
35
38
  "totalInstances": count(*[${instanceMatch}])
@@ -37,6 +40,7 @@ export function buildDefinitionListQuery(flags) {
37
40
  return { groq, params };
38
41
  }
39
42
  export default class DefinitionList extends WorkflowCommand {
43
+ static aliases = ['definition:list'];
40
44
  static description = 'List deployed workflow definitions.';
41
45
  static examples = [
42
46
  '<%= config.bin %> definition list',
@@ -60,29 +64,28 @@ export default class DefinitionList extends WorkflowCommand {
60
64
  targets,
61
65
  log: (line) => this.log(line),
62
66
  run: async ({ client }) => {
63
- const fetched = await client.fetch(groq, params, {
67
+ const fetched = (await client.fetch(groq, params, {
64
68
  tag: 'definition.list',
65
- });
69
+ })).map(assertReadableModel);
66
70
  if (!fetched.length) {
67
71
  this.log(`${logSymbols.info} no definitions found`);
68
72
  return;
69
73
  }
70
- const { rows, note } = clipToLimit(fetched, flags.limit);
71
- const lines = formatTable(['workflow', 'title', 'tag', 'stages', 'in flight', 'instances', 'created'], rows.map((r) => [
72
- `${r.name} v${r.version}`,
73
- r.title,
74
- r.tag,
75
- String(r.stageCount),
76
- String(r.inFlightCount),
77
- String(r.totalInstances),
78
- r._createdAt,
79
- ]));
80
- for (const line of lines) {
81
- this.log(line);
82
- }
83
- if (note) {
84
- this.log(note);
85
- }
74
+ logClippedTable({
75
+ rows: fetched,
76
+ limit: flags.limit,
77
+ 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
+ ],
87
+ log: (line) => this.log(line),
88
+ });
86
89
  },
87
90
  });
88
91
  if (failures.length > 0) {
@@ -1,7 +1,8 @@
1
1
  import { type DeployedDefinition, type WorkflowDefinition, type WorkflowResource } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../../lib/base-command.ts';
3
- import { type ReadTarget } from '../../lib/context.ts';
2
+ import { WorkflowCommand } from '../../../lib/base-command.ts';
3
+ import { type ReadTarget } from '../../../lib/context.ts';
4
4
  export default class DefinitionShow extends WorkflowCommand {
5
+ static aliases: string[];
5
6
  static description: string;
6
7
  static args: {
7
8
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -1,14 +1,15 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Args, Flags } from '@oclif/core';
3
- import { isTerminalStage, } from '@sanity/workflow-engine';
3
+ import { assertReadableModel, isTerminalStage, } from '@sanity/workflow-engine';
4
4
  import logSymbols from 'log-symbols';
5
- import { WorkflowCommand } from "../../lib/base-command.js";
6
- import { resolveReadTargets, soleHitOrFail } from "../../lib/context.js";
7
- import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../lib/definitions.js";
8
- import { fail } from "../../lib/fail.js";
9
- import { tagFlags } from "../../lib/flags.js";
10
- import { formatKeyValue, resourceLabel, sectionHeader } from "../../lib/ui.js";
5
+ import { WorkflowCommand } from "../../../lib/base-command.js";
6
+ import { resolveReadTargets, soleHitOrFail } from "../../../lib/context.js";
7
+ import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../../lib/definitions.js";
8
+ import { fail } from "../../../lib/fail.js";
9
+ import { tagFlags } from "../../../lib/flags.js";
10
+ import { formatKeyValue, resourceLabel, sectionHeader } from "../../../lib/ui.js";
11
11
  export default class DefinitionShow extends WorkflowCommand {
12
+ static aliases = ['definition:show'];
12
13
  static description = 'Show a deployed workflow definition.';
13
14
  static args = {
14
15
  name: Args.string({ required: true, description: 'Workflow definition name.' }),
@@ -64,7 +65,7 @@ export async function fetchDefinitionHits({ targets, groq, params, }) {
64
65
  const def = await target.client.fetch(groq, params, {
65
66
  tag: 'definition.show',
66
67
  });
67
- return def === null ? undefined : { resource: target.resource, def };
68
+ return def === null ? undefined : { resource: target.resource, def: assertReadableModel(def) };
68
69
  }));
69
70
  return probes.filter((hit) => hit !== undefined);
70
71
  }
@@ -1,5 +1,5 @@
1
1
  import { type DeployDefinitionResult, type WorkflowDefinition, type WorkflowDeployment } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../lib/base-command.ts';
2
+ import { WorkflowCommand } from '../../lib/base-command.ts';
3
3
  /** A deployment paired with the definitions selected + validated for it. */
4
4
  interface DeployBatch {
5
5
  deployment: WorkflowDeployment;
@@ -11,6 +11,7 @@ interface DeployFailure {
11
11
  message: string;
12
12
  }
13
13
  export default class Deploy extends WorkflowCommand {
14
+ static aliases: string[];
14
15
  static description: string;
15
16
  static examples: string[];
16
17
  static flags: {
@@ -3,18 +3,19 @@ import { Flags } from '@oclif/core';
3
3
  import { computeDiffEntries, errorMessage, workflow, } from '@sanity/workflow-engine';
4
4
  import logSymbols from 'log-symbols';
5
5
  import ora from 'ora';
6
- import { WorkflowCommand } from "../lib/base-command.js";
7
- import { clientFor, resolveTokenOrFail } from "../lib/client.js";
8
- import { selectDefinitions, validateOrFail } from "../lib/definitions.js";
9
- import { diffReport } from "../lib/diff.js";
10
- import { fail, isAuthRejection } from "../lib/fail.js";
11
- import { tagFlags } from "../lib/flags.js";
12
- import { loadWorkflowConfig } from "../lib/load-config.js";
13
- import { deploymentToTarget, selectDeployments } from "../lib/select-deployment.js";
14
- import { shareDefinitionsAfterDeploy } from "../lib/share-definitions.js";
15
- import { cliTelemetry } from "../lib/telemetry.js";
16
- import { groupBanner } from "../lib/ui.js";
6
+ import { WorkflowCommand } from "../../lib/base-command.js";
7
+ import { clientFor, resolveTokenOrFail } from "../../lib/client.js";
8
+ import { selectDefinitions, validateOrFail } from "../../lib/definitions.js";
9
+ import { diffReport } from "../../lib/diff.js";
10
+ import { fail, isAuthRejection } from "../../lib/fail.js";
11
+ import { tagFlags } from "../../lib/flags.js";
12
+ import { loadWorkflowConfig } from "../../lib/load-config.js";
13
+ import { deploymentToTarget, selectDeployments } from "../../lib/select-deployment.js";
14
+ import { shareDefinitionsAfterDeploy } from "../../lib/share-definitions.js";
15
+ import { cliTelemetry } from "../../lib/telemetry.js";
16
+ import { groupBanner } from "../../lib/ui.js";
17
17
  export default class Deploy extends WorkflowCommand {
18
+ static aliases = ['deploy'];
18
19
  static description = 'Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.';
19
20
  static examples = [
20
21
  '<%= config.bin %> deploy --tag prod',
@@ -43,14 +44,14 @@ export default class Deploy extends WorkflowCommand {
43
44
  }),
44
45
  'share-defs': Flags.boolean({
45
46
  allowNo: true,
46
- description: 'Share the definition documents newly created by this deploy with Sanity — the full document, verbatim (structure, names, filters, effect configuration, seeded values), plus its deployment coordinates (project and dataset, or resource id); never content documents, instances, or your Sanity auth token. Opt-out: an interactive terminal is asked once (the answer is remembered), while unattended runs (CI / non-TTY / DO_NOT_TRACK) share nothing unless --share-defs is passed. Use --no-share-defs to opt out. An explicit --share-defs also sends telemetry for this deploy regardless of CI / DO_NOT_TRACK.',
47
+ description: 'Share the definition documents newly created by this deploy with Sanity — the full document, verbatim (structure, names, filters, effect configuration, seeded values), plus its deployment coordinates (project and dataset, or resource id); never content documents, instances, or your Sanity auth token. Sharing is the default in every environment, including CI / non-TTY / DO_NOT_TRACK. Use --no-share-defs to opt out.',
47
48
  }),
48
49
  };
49
50
  async run() {
50
51
  const { flags } = await this.parse(Deploy);
51
52
  validateModeFlags(flags.check, flags['dry-run']);
52
53
  const config = await loadWorkflowConfig();
53
- const batches = buildBatches(selectDeployments(config, { tag: flags.tag, allTags: flags['all-tags'] }), flags.only);
54
+ const batches = buildBatches(await selectDeployments(config, { tag: flags.tag, allTags: flags['all-tags'] }), flags.only);
54
55
  const log = (line) => this.log(line);
55
56
  if (flags.check) {
56
57
  await reconcileBatches({
@@ -1,5 +1,5 @@
1
1
  import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
2
- import { WorkflowCommand } from '../lib/base-command.ts';
2
+ import { WorkflowCommand } from '../../lib/base-command.ts';
3
3
  /** The `--json` transitions payload: raw derived state (atom GROQ, negation,
4
4
  * pivotality, solvable requirement) — structure for scripts, never baked
5
5
  * English. Pure so the shape is a testable contract. */
@@ -32,6 +32,7 @@ export declare function renderDiagnosis({ diagnosis, input, remediations, explan
32
32
  explanations?: ReadonlyMap<string, string> | undefined;
33
33
  }): string[];
34
34
  export default class Diagnose extends WorkflowCommand {
35
+ static aliases: string[];
35
36
  static description: string;
36
37
  static examples: string[];
37
38
  static args: {