@sanity/workflow-cli 0.32.0 → 0.34.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 +153 -0
- package/README.md +361 -50
- package/dist/commands/workflows/blueprint/generate.d.ts +9 -0
- package/dist/commands/workflows/blueprint/generate.js +28 -0
- package/dist/commands/workflows/diagnose.d.ts +4 -2
- package/dist/commands/workflows/diagnose.js +11 -82
- package/dist/commands/workflows/show.d.ts +3 -5
- package/dist/commands/workflows/show.js +28 -1
- package/dist/hooks/prerun/telemetry.d.ts +6 -0
- package/dist/hooks/prerun/telemetry.js +14 -7
- package/dist/lib/blueprint-emit.d.ts +30 -0
- package/dist/lib/blueprint-emit.js +147 -0
- package/dist/lib/blueprint-needs.d.ts +2 -0
- package/dist/lib/blueprint-needs.js +109 -0
- package/dist/lib/cause-detail.d.ts +9 -0
- package/dist/lib/cause-detail.js +111 -0
- package/dist/lib/load-config.d.ts +20 -0
- package/dist/lib/load-config.js +16 -13
- package/dist/lib/share-definitions.d.ts +2 -2
- package/dist/lib/share-definitions.js +2 -2
- package/dist/lib/telemetry-setup.d.ts +2 -0
- package/dist/lib/telemetry-setup.js +2 -2
- package/dist/lib/telemetry.d.ts +5 -2
- package/dist/lib/telemetry.js +13 -4
- package/dist/standalone-argv.d.ts +2 -2
- package/dist/standalone-argv.js +1 -0
- package/oclif.manifest.json +33 -1
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -3,23 +3,31 @@
|
|
|
3
3
|
Command-line tool for deploying, inspecting, and administering Sanity workflow
|
|
4
4
|
definitions and instances.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
> `sanity login`.
|
|
6
|
+
The package is in early access and is publicly available on npm. Commands that
|
|
7
|
+
access Sanity use the resources in `sanity.workflow.ts` and authenticate with a
|
|
8
|
+
login session or an API token. `deploy --check` validates definitions offline.
|
|
10
9
|
|
|
11
10
|
## Run
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Use Node.js 20.12 or later. Install the CLI and its engine peer from the same
|
|
13
|
+
Workflows release in your project:
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
pnpm --filter @sanity/workflow-cli dev list --include-completed
|
|
20
|
-
pnpm --filter @sanity/workflow-cli dev show wf-instance.abc123
|
|
15
|
+
```sh
|
|
16
|
+
npm install --save-dev @sanity/workflow-cli @sanity/workflow-engine
|
|
17
|
+
npx @sanity/workflow-cli --help
|
|
21
18
|
```
|
|
22
19
|
|
|
20
|
+
After authentication and [configuration](#configuration), run commands through
|
|
21
|
+
the project-local package:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx @sanity/workflow-cli deploy --dry-run
|
|
25
|
+
npx @sanity/workflow-cli list --include-completed
|
|
26
|
+
npx @sanity/workflow-cli show INSTANCE_ID
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Replace `INSTANCE_ID` with the workflow instance ID returned by `list`.
|
|
30
|
+
|
|
23
31
|
The package is an [oclif](https://oclif.io) plugin: every command's canonical
|
|
24
32
|
id nests under the `workflows` topic
|
|
25
33
|
(`sanity-workflows workflows deploy`), so mounting the package into
|
|
@@ -32,9 +40,9 @@ binary's stable surface — its production and development entrypoints
|
|
|
32
40
|
resolves the command. Those short forms are **not** registered as oclif aliases,
|
|
33
41
|
so a host mount does not pollute the host root.
|
|
34
42
|
|
|
35
|
-
Authenticate
|
|
36
|
-
CI, set `SANITY_AUTH_TOKEN` instead.
|
|
37
|
-
directory you run from
|
|
43
|
+
Authenticate with `npx sanity@latest login`; the CLI reads that session token.
|
|
44
|
+
For CI, set `SANITY_AUTH_TOKEN` instead. Create a `sanity.workflow.ts` in the
|
|
45
|
+
directory you run from, as described in [Configuration](#configuration).
|
|
38
46
|
|
|
39
47
|
The token must span **every resource the workflow references**, not just the
|
|
40
48
|
workflow resource. Runtime commands build one client from the deployment
|
|
@@ -52,28 +60,29 @@ the directory you run from. It exports a config built with
|
|
|
52
60
|
environment):
|
|
53
61
|
|
|
54
62
|
```ts
|
|
63
|
+
import type {WorkflowDeploymentInput} from '@sanity/workflow-engine'
|
|
55
64
|
import {defineWorkflowConfig} from '@sanity/workflow-engine/define'
|
|
56
65
|
|
|
57
66
|
import {articleReview, urlDraft} from './src/workflows.ts'
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
],
|
|
73
|
-
definitions: [articleReview, urlDraft], // the batch this deployment ships
|
|
74
|
-
},
|
|
68
|
+
// Export each deployment under its own `name`, then list the exports in the
|
|
69
|
+
// config. `blueprint generate` requires these named exports; nothing else does.
|
|
70
|
+
export const production = {
|
|
71
|
+
expectedMinReaderModel: 4,
|
|
72
|
+
name: 'production', // the deployment's unique identity (lowercase letters, digits, dashes)
|
|
73
|
+
tag: 'prod', // the environment partition the engine's docs are scoped to
|
|
74
|
+
workflowResource: {type: 'dataset', id: 'acme.workflows'}, // where those docs live
|
|
75
|
+
resourceAliases: [
|
|
76
|
+
// binds each `@<handle>:` a definition references to a physical resource
|
|
77
|
+
// in a DIFFERENT dataset from `workflowResource` (same-resource content
|
|
78
|
+
// needs no alias — see below)
|
|
79
|
+
{name: 'content', resource: {type: 'dataset', id: 'acme.content'}},
|
|
80
|
+
{name: 'assets', resource: {type: 'dataset', id: 'acme.assets'}},
|
|
75
81
|
],
|
|
76
|
-
|
|
82
|
+
definitions: [articleReview, urlDraft], // the batch this deployment ships
|
|
83
|
+
} satisfies WorkflowDeploymentInput
|
|
84
|
+
|
|
85
|
+
export default defineWorkflowConfig({deployments: [production]})
|
|
77
86
|
```
|
|
78
87
|
|
|
79
88
|
`name` is the deployment's identity: unique across the config and constrained
|
|
@@ -82,16 +91,20 @@ repeat, as long as no two deployments share both a `workflowResource` and a
|
|
|
82
91
|
`tag` — that pair is the storage partition, and the config rejects the
|
|
83
92
|
collision naming both entries.
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
tag
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
For `deploy`, `start`, `definition diff`, and `definition delete`, select a
|
|
95
|
+
deployment with `--deployment <name>`. With one deployment configured, you can
|
|
96
|
+
omit the selector. `--tag <tag>` selects one deployment when the tag is unique;
|
|
97
|
+
on `deploy`, it targets every deployment carrying the tag. With several
|
|
98
|
+
deployments and no selector, these commands prompt for a deployment in an
|
|
99
|
+
interactive terminal. In a non-interactive shell, they fail asking for
|
|
100
|
+
`--deployment` or `--tag`; `deploy` also suggests `--all-tags`.
|
|
101
|
+
|
|
102
|
+
The instance-targeted commands `abort`, `set-stage`, `reset-activity`,
|
|
103
|
+
`fire-action`, and `diagnose` locate the instance by ID across configured
|
|
104
|
+
resources. They do not prompt for a deployment. Their optional `--deployment`
|
|
105
|
+
and `--tag` flags narrow the resources searched; the operation's tag comes from
|
|
106
|
+
the loaded instance.
|
|
107
|
+
|
|
95
108
|
`--all-tags` deploys every deployment in the config in one run: a failure in one doesn't
|
|
96
109
|
stop the rest — the run continues, prints a summary of what failed, and exits
|
|
97
110
|
non-zero. The client's project + dataset are derived from the deployment's
|
|
@@ -173,6 +186,8 @@ Invoke as `sanity-workflows <command>` (or `sanity workflows <command>` once the
|
|
|
173
186
|
| `set-stage <instance-id> --to <stage>` | Move an instance to a stage, skipping declared transitions. The target stage's enter lifecycle still runs. |
|
|
174
187
|
| `reset-activity <instance-id> <activity>` | Re-run a failed activity, or `--skip` it so a gated transition can fire. |
|
|
175
188
|
| `fire-action <instance-id>` | Fire an action on a waiting activity. Omit `--action` to list what can be fired. |
|
|
189
|
+
| `blueprint generate` | Write the Sanity Blueprints runtime the definitions require next to `sanity.workflow.ts`. |
|
|
190
|
+
| `blueprint generate --check` | Verify that runtime still matches the definitions. Writes nothing; exits non-zero on any difference. |
|
|
176
191
|
| `definition list` | List deployed definitions. |
|
|
177
192
|
| `definition show <name>` | Show a deployed definition (latest version, or `--version`). |
|
|
178
193
|
| `definition diff <name>` | Diff the in-code definition against what is deployed. |
|
|
@@ -180,24 +195,320 @@ Invoke as `sanity-workflows <command>` (or `sanity workflows <command>` once the
|
|
|
180
195
|
| `nuke --deployment <name>` / `nuke --tag <tag>` | Dev reset: delete every engine-owned document for that tag (instances, definitions, guards). Prints a plan, then asks for confirmation. Content documents are never touched. |
|
|
181
196
|
| `nuke --instance <id>` | Dev reset: delete one finished instance and its guards. Refuses in-flight instances (abort first). |
|
|
182
197
|
|
|
198
|
+
## Generating the Blueprints runtime
|
|
199
|
+
|
|
200
|
+
**Experimental, and not ready for production use in this release.** Both
|
|
201
|
+
commands are available so you can try the generated runtime and give feedback;
|
|
202
|
+
their flags and their output may still change. What you would hit today:
|
|
203
|
+
|
|
204
|
+
- The start watcher runs on every create and update of a subject type, not only
|
|
205
|
+
on writes that can actually start a workflow, so it wakes for documents that
|
|
206
|
+
already have an instance or match no definition. The heartbeat is scheduled
|
|
207
|
+
every minute and evaluates every in-flight instance in the tag when it runs;
|
|
208
|
+
how often the platform runs it is the plan limit described below.
|
|
209
|
+
- `sanity-workflows deploy` does not check the generated tree, so drift between
|
|
210
|
+
your definitions and the tree on disk is caught only by
|
|
211
|
+
`blueprint generate --check` in CI.
|
|
212
|
+
- Definitions and functions deploy in two steps, definitions first, until the
|
|
213
|
+
Blueprints service registers the `sanity.workflow` resource.
|
|
214
|
+
- A generated file cannot be taken over: editing one is drift, and the next
|
|
215
|
+
generation rewrites it.
|
|
216
|
+
- A retry policy is not checked against its function's timeout at generation, so
|
|
217
|
+
an effect can be configured to retry for longer than its function may run.
|
|
218
|
+
|
|
219
|
+
All of these are tracked, and the next release is the bar for using this in
|
|
220
|
+
production.
|
|
221
|
+
|
|
222
|
+
A definition that declares an effect, reads `$now`, or starts autonomously needs
|
|
223
|
+
server-side code: something has to run the effect handlers, sample deadlines, and
|
|
224
|
+
start instances from document writes. `blueprint generate` derives that runtime
|
|
225
|
+
from the definitions and writes it next to `sanity.workflow.ts`, so
|
|
226
|
+
`npx sanity blueprints deploy` has something to deploy. A purely interactive
|
|
227
|
+
workflow needs none of it, and the command says so.
|
|
228
|
+
|
|
229
|
+
Five prerequisites:
|
|
230
|
+
|
|
231
|
+
- **Each deployment is exported under its own `name`, and the export is the
|
|
232
|
+
deployment object itself**, as the [Configuration](#configuration) example
|
|
233
|
+
does: every generated module imports its deployment from `./sanity.workflow`
|
|
234
|
+
by that name. The command refuses to write anything until each name is
|
|
235
|
+
exported, and names the export to add. It checks the name only, so make sure
|
|
236
|
+
each export is the deployment your config lists. A typecheck of the generated
|
|
237
|
+
modules catches a wrong export only where a generated function uses it, and a
|
|
238
|
+
deployment that needs no functions emits none.
|
|
239
|
+
- **`sanity.workflow.ts` is importable inside a deployed function.** Every
|
|
240
|
+
generated module imports it, and each function bundles it, so the file runs
|
|
241
|
+
where your shell and your `.env` do not exist. Reading an absent variable
|
|
242
|
+
there yields `undefined`; what fails is a config that **throws** when a
|
|
243
|
+
variable is missing, or requires one to build the deployment, because that
|
|
244
|
+
runs on init before your handler is reached. Keep the deployment's resource
|
|
245
|
+
coordinates literal in the file and read secrets inside your handlers.
|
|
246
|
+
- **The Blueprints packages the generated modules import are installed**,
|
|
247
|
+
alongside the CLI and its peers.
|
|
248
|
+
- **Your definitions are deployed before the functions.** The generated
|
|
249
|
+
`workflows.blueprint.ts` carries the definitions entry commented out, because
|
|
250
|
+
the Blueprints service does not accept the `sanity.workflow` resource type
|
|
251
|
+
yet, so `npx sanity blueprints deploy` does not deploy them. Run
|
|
252
|
+
`npx @sanity/workflow-cli deploy` first: a function that starts or advances an
|
|
253
|
+
instance resolves its definition from the Content Lake, and finds nothing
|
|
254
|
+
until that deploy has run.
|
|
255
|
+
- **A generated heartbeat needs an organization-scoped stack.** The heartbeat is
|
|
256
|
+
a scheduled function, and the platform only accepts one on a stack in
|
|
257
|
+
organization scope. A project-scoped stack refuses it, and with it the whole
|
|
258
|
+
deploy. See
|
|
259
|
+
[Promote a stack to organization scope](https://www.sanity.io/docs/blueprints/promote-stack-to-organization-scope).
|
|
260
|
+
How often it may run is a plan limit rather than something the generator
|
|
261
|
+
chooses — see [the schedule](#the-generated-heartbeats-schedule) below.
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
npm install --save-dev @sanity/blueprints @sanity/functions @sanity/workflow-blueprint
|
|
265
|
+
npx @sanity/workflow-cli blueprint generate
|
|
266
|
+
npx @sanity/workflow-cli deploy
|
|
267
|
+
npx sanity blueprints deploy
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Each generated function's `package.json` copies the specifications your project
|
|
271
|
+
declares for `@sanity/functions`, `@sanity/workflow-blueprint`, and
|
|
272
|
+
`@sanity/workflow-engine`. A package you declare nowhere fails the command
|
|
273
|
+
rather than being stamped with a guessed range.
|
|
274
|
+
|
|
275
|
+
The command prints what the definitions need and why, then every file it wrote:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
$ npx @sanity/workflow-cli blueprint generate
|
|
279
|
+
Runtime needs:
|
|
280
|
+
|
|
281
|
+
▸ prod (prod)
|
|
282
|
+
hosting: this deployment declares function, and each level below inherits it
|
|
283
|
+
→ function workflow(s): article-review
|
|
284
|
+
→ function effect(s): notify-reviewer
|
|
285
|
+
effects: 1 declared — notify-reviewer
|
|
286
|
+
→ drain function wf-prod-drain-effects runs notify-reviewer
|
|
287
|
+
triggered by instance writes matching _type == "sanity.workflow.instance" && tag == "prod" && count(pendingEffects[!defined(claim)]) > 0
|
|
288
|
+
clock: samples $now deadlines; sweeps stale effect claims left by a dead drain
|
|
289
|
+
article-review · working · action-when · $fields.subject.embargoAt <= $now
|
|
290
|
+
→ scheduled function wf-prod-heartbeat on * * * * *
|
|
291
|
+
a scheduled function runs at most as often as your organization's plan allows — every minute on Enterprise, hourly on Growth, daily on Free (https://www.sanity.io/docs/functions/functions-introduction) — and a schedule below that threshold is deployed and never invoked
|
|
292
|
+
autonomous starts: 1 definition(s) — article-review
|
|
293
|
+
→ start watcher wf-prod-start-workflows on acme.workflows matching _type in ["article"]
|
|
294
|
+
|
|
295
|
+
Generated tree:
|
|
296
|
+
created workflows.blueprint.ts
|
|
297
|
+
created functions/wf-prod-drain-effects/index.ts
|
|
298
|
+
created functions/wf-prod-drain-effects/package.json
|
|
299
|
+
created functions/wf-prod-heartbeat/index.ts
|
|
300
|
+
created functions/wf-prod-heartbeat/package.json
|
|
301
|
+
created functions/wf-prod-start-workflows/index.ts
|
|
302
|
+
created functions/wf-prod-start-workflows/package.json
|
|
303
|
+
created effect-handlers/all.ts
|
|
304
|
+
created effect-handlers/notify-reviewer.ts yours now — implement the effect
|
|
305
|
+
created sanity.blueprint.ts the workflow resources spread
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Fill in each `effect-handlers/<effect name>.ts`: they are yours, scaffolded once
|
|
309
|
+
and never overwritten. Everything marked `created`, `updated`, or `unchanged`
|
|
310
|
+
under `workflows.blueprint.ts`, `functions/`, and `effect-handlers/all.ts` is
|
|
311
|
+
regenerated on every run, so edits there do not survive. Your own
|
|
312
|
+
`sanity.blueprint.ts` receives the `workflowResources` import and spread once;
|
|
313
|
+
after that the command leaves it alone. The command never deletes a file. A
|
|
314
|
+
handler for an effect you removed, a scaffold blocked by a path that differs
|
|
315
|
+
only in case, and a blueprint it could not wire are reported for you to resolve.
|
|
316
|
+
|
|
317
|
+
The command covers every deployment in the config, because the emitted
|
|
318
|
+
`workflows.blueprint.ts` declares them all.
|
|
319
|
+
|
|
320
|
+
### Where each workflow runs
|
|
321
|
+
|
|
322
|
+
The report opens with the hosting each `runtime` block resolved to, after every
|
|
323
|
+
level has inherited from the one above it. It matters because a name listed
|
|
324
|
+
under `durableFunction` or `selfHosted` has no emitted function to point at:
|
|
325
|
+
|
|
326
|
+
- `function` — the drain, heartbeat, and start watcher above.
|
|
327
|
+
- `durableFunction` — nothing is emitted yet. The rest of the deployment emits
|
|
328
|
+
normally around it.
|
|
329
|
+
- `selfHosted` — nothing is emitted, and the report ends with what your own
|
|
330
|
+
process must do instead: which deadlines to tick, which effects to drain,
|
|
331
|
+
which parents to re-evaluate when a child settles, and which autonomous
|
|
332
|
+
definitions to start. A self-hosted deadline is never a reason to emit a
|
|
333
|
+
heartbeat, so it is listed there and not under `clock`; an emitted heartbeat
|
|
334
|
+
ticks every in-flight instance in the tag, whatever hosts each workflow.
|
|
335
|
+
|
|
336
|
+
### The generated heartbeat's schedule
|
|
337
|
+
|
|
338
|
+
The heartbeat is emitted on `* * * * *`, one run a minute, and how often the
|
|
339
|
+
platform will actually run it is a plan limit rather than something the
|
|
340
|
+
generator can choose: Sanity documents the threshold as
|
|
341
|
+
[Free daily, Growth hourly, Enterprise minutely](https://www.sanity.io/docs/functions/functions-introduction).
|
|
342
|
+
Below Enterprise the platform accepts the scheduled function and does not run
|
|
343
|
+
it, so a workflow whose deadlines depend on the heartbeat does not advance and
|
|
344
|
+
nothing reports an error. Check your plan's cadence before relying on a `$now`
|
|
345
|
+
deadline in a deployed workflow.
|
|
346
|
+
|
|
347
|
+
### Checking for drift in CI
|
|
348
|
+
|
|
349
|
+
`blueprint generate --check` renders the same runtime in memory and compares it
|
|
350
|
+
with the files on disk. It writes nothing and exits zero when they match:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
$ npx @sanity/workflow-cli blueprint generate --check
|
|
354
|
+
✔ the generated tree matches the definitions
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Otherwise it exits `1` and names every file, so the log alone says what to fix:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
$ npx @sanity/workflow-cli blueprint generate --check
|
|
361
|
+
✖ The generated tree does not match the definitions (1):
|
|
362
|
+
differs effect-handlers/all.ts
|
|
363
|
+
Run `sanity-workflows blueprint generate` to bring it back in line.
|
|
364
|
+
$ echo $?
|
|
365
|
+
1
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
A file reports `missing` when nothing is at its path, `differs` when the file
|
|
369
|
+
there has other contents, and a handler for a declared effect reports `missing`
|
|
370
|
+
when you have not written it yet. Add the command to the job that builds your
|
|
371
|
+
blueprint and a definition change can no longer ship without the runtime it
|
|
372
|
+
needs.
|
|
373
|
+
|
|
183
374
|
## Telemetry
|
|
184
375
|
|
|
185
|
-
The CLI collects usage telemetry through Sanity's standard pipeline
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
the engine's adoption events from the operations it drives. Consent is the
|
|
189
|
-
account-wide status managed by `npx sanity telemetry enable|disable|status`;
|
|
190
|
-
CI and trueish `DO_NOT_TRACK` suppress everything (except a deploy that may share
|
|
376
|
+
The CLI collects usage telemetry through Sanity's standard pipeline. Consent
|
|
377
|
+
is the account-wide status managed by `npx sanity telemetry enable|disable|status`.
|
|
378
|
+
CI and trueish `DO_NOT_TRACK` suppress everything except a deploy that may share
|
|
191
379
|
new definitions, which forces that deploy's telemetry unless `--no-share-defs`
|
|
192
|
-
is passed — see [Definition sharing](#definition-sharing)
|
|
193
|
-
|
|
194
|
-
|
|
380
|
+
is passed — see [Definition sharing](#definition-sharing). A session that isn't
|
|
381
|
+
logged in sends nothing. A one-time notice on stderr discloses collection on
|
|
382
|
+
first use.
|
|
383
|
+
|
|
384
|
+
Payloads never include customer-authored strings: no flag values, argv
|
|
385
|
+
tokens, error text, definition names, stage names, GROQ, or document content.
|
|
386
|
+
The one instance-scoped exception the engine already ships is `instanceId`
|
|
387
|
+
(the instance document `_id`). Effect events also carry the author-chosen
|
|
388
|
+
effect name. Definition sharing sends the definition document to a first-party
|
|
389
|
+
feedback endpoint, not through this pipeline; telemetry only records the
|
|
390
|
+
content-free markers below.
|
|
391
|
+
|
|
392
|
+
Each event includes `context.surface: 'cli'` and its process execution mode
|
|
393
|
+
in `context.environment`. `NODE_ENV=production` reports `production`;
|
|
394
|
+
`development` and `test` report `development`. Unset, empty, and unrecognized
|
|
395
|
+
values default to `production` for installed CLI and MCP runs. The SDK reports
|
|
396
|
+
build mode and defaults to `development` instead.
|
|
397
|
+
|
|
398
|
+
When comparing activity across surfaces, group or filter by `context.surface`
|
|
399
|
+
alongside `context.environment`. Environment does not identify a production
|
|
400
|
+
dataset or deployment; the API host, dataset name, and deployment tag do not
|
|
401
|
+
set it. Existing telemetry consent and opt-out settings still apply.
|
|
195
402
|
|
|
196
403
|
One extension point: supply your own logger as `telemetry` in
|
|
197
404
|
`sanity.workflow.ts` and the built-in pipeline is not constructed at all —
|
|
198
405
|
every event flows to your implementation unconditionally (CI included), and
|
|
199
406
|
consent, suppression, and destination become its business.
|
|
200
407
|
|
|
408
|
+
### Session properties
|
|
409
|
+
|
|
410
|
+
The built-in store attaches these user properties once per invocation (joined
|
|
411
|
+
to events by session id). User identity is not in the payload; the intake
|
|
412
|
+
service resolves the sender from the authenticated session.
|
|
413
|
+
|
|
414
|
+
| Property | What it is |
|
|
415
|
+
| ----------------- | ------------------------------------------------------------------------------------------------ |
|
|
416
|
+
| `surface` | Always `'cli'`. |
|
|
417
|
+
| `machinePlatform` | Node `process.platform` (for example `darwin`, `linux`). |
|
|
418
|
+
| `cpuArchitecture` | Node `process.arch`. |
|
|
419
|
+
| `runtime` | Always `'node'` for this CLI. |
|
|
420
|
+
| `runtimeVersion` | Node `process.version`. |
|
|
421
|
+
| `cliVersion` | The CLI package version, when oclif supplied it. |
|
|
422
|
+
| `projectId` | Project id of the first dataset-backed deployment in `sanity.workflow.ts`. |
|
|
423
|
+
| `dataset` | Dataset of that same deployment. |
|
|
424
|
+
| `orgId` | Organization id for that project, when the lookup succeeds before the command flushes. Optional. |
|
|
425
|
+
|
|
426
|
+
### CLI events
|
|
427
|
+
|
|
428
|
+
These events are defined by this package.
|
|
429
|
+
|
|
430
|
+
#### `Workflows CLI Command Executed` (version 1)
|
|
431
|
+
|
|
432
|
+
One trace per workflows command. The built-in store starts the trace in the
|
|
433
|
+
prerun hook with `groupOrCommand` set to the oclif command id (for example
|
|
434
|
+
`workflows:deploy`, `workflows:definition:list`, `workflows:fire-action`) and
|
|
435
|
+
completes it in the finally hook. A config-supplied logger receives the same
|
|
436
|
+
payload as a log, with no trace. Host commands outside the `workflows` topic
|
|
437
|
+
do not emit.
|
|
438
|
+
|
|
439
|
+
| Field | Type | What is collected |
|
|
440
|
+
| --------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
441
|
+
| `command` | `string` | The oclif command id (`workflows:…`). Never argv free-text. |
|
|
442
|
+
| `flags` | `string[]` | Names of **declared** flags present on the invocation, sorted. Long form, `--no-name`, and short chars. Never values, never undeclared tokens, never tokens after `--`. |
|
|
443
|
+
| `success` | `boolean` | `true` when the command finished without an oclif error. |
|
|
444
|
+
|
|
445
|
+
#### `Workflows Definition Shared` (version 1)
|
|
446
|
+
|
|
447
|
+
One content-free marker per newly created definition version that actually
|
|
448
|
+
reached Sanity's definition-feedback endpoint. Unchanged redeploys, dry runs,
|
|
449
|
+
and `--check` never emit it. The document itself is not in this event.
|
|
450
|
+
|
|
451
|
+
| Field | Type | What is collected |
|
|
452
|
+
| ------------------ | ---------- | ---------------------------------------------------------------------------------------------------- |
|
|
453
|
+
| `contentHash` | `string` | Content fingerprint of the donated definition. |
|
|
454
|
+
| `deployId` | `string` | Random run id shared with that invocation's `Workflows Definition Deployed` events. |
|
|
455
|
+
| `stageCount` | `number` | Declared stages. |
|
|
456
|
+
| `activityCount` | `number` | Activities across those stages. |
|
|
457
|
+
| `actionCount` | `number` | Actions across those activities. |
|
|
458
|
+
| `transitionCount` | `number` | Declared transitions. |
|
|
459
|
+
| `fieldCount` | `number` | Declared field entries at workflow, stage, and activity scope. |
|
|
460
|
+
| `activityKinds` | `string[]` | Distinct effective activity kinds, sorted: `user`, `service`, `script`, `manual`, `receive`. |
|
|
461
|
+
| `fieldKinds` | `string[]` | Distinct declared field kinds, sorted (engine field kinds such as `string`, `subject`, `assignees`). |
|
|
462
|
+
| `guardCount` | `number` | Guards declared on stages. |
|
|
463
|
+
| `effectCount` | `number` | Effects declared on actions. |
|
|
464
|
+
| `subworkflowCount` | `number` | Actions that carry a `spawn` block. |
|
|
465
|
+
| `lifecycle` | `string` | `'standalone'` or `'child'`. |
|
|
466
|
+
|
|
467
|
+
#### `Workflows Definition Sharing Decided` (version 2)
|
|
468
|
+
|
|
469
|
+
One event per `deploy` that created at least one new definition version,
|
|
470
|
+
whether or not anything was donated. Measures opt-out rate without carrying
|
|
471
|
+
the document.
|
|
472
|
+
|
|
473
|
+
| Field | Type | What is collected |
|
|
474
|
+
| ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
475
|
+
| `decision` | `string` | `'opt-in'` (`--share-defs`), `'opt-out'` (`--no-share-defs`), or `'default'` (neither flag). |
|
|
476
|
+
| `shared` | `boolean` | Whether a donation POST reached the endpoint. `false` for any non-sharing decision, and for a chosen share whose POST failed. |
|
|
477
|
+
| `definitionCount` | `number` | Newly created definition versions this invocation could have donated. |
|
|
478
|
+
|
|
479
|
+
### Engine events the CLI records
|
|
480
|
+
|
|
481
|
+
Write commands pass the CLI logger into the engine, so a successful operation
|
|
482
|
+
also records the engine's adoption events on the same session. Read commands
|
|
483
|
+
(`list`, `show`, `diagnose`, `tail`, `definition list` / `show` / `diff`) do
|
|
484
|
+
not emit engine events. `nuke` deletes engine-owned documents outside those
|
|
485
|
+
verbs and does not emit them either.
|
|
486
|
+
|
|
487
|
+
Every instance-scoped engine event includes:
|
|
488
|
+
|
|
489
|
+
| Field | Type | What is collected |
|
|
490
|
+
| ----------------------- | -------- | -------------------------------------------------------------------------------------------------- |
|
|
491
|
+
| `definitionContentHash` | `string` | Content fingerprint of the pinned definition. Omitted when the definition predates fingerprinting. |
|
|
492
|
+
| `instanceId` | `string` | Instance document `_id`. |
|
|
493
|
+
|
|
494
|
+
| Event | Version | When the CLI emits it | Additional payload |
|
|
495
|
+
| ------------------------------- | ------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
496
|
+
| `Workflows Definition Deployed` | 1 | `deploy` for each definition (created or unchanged) | Same structural counts as `Workflows Definition Shared`, plus `status`: `'created'` or `'unchanged'`. |
|
|
497
|
+
| `Workflows Instance Started` | 1 | `start`, and a `fire-action` that spawns a child | `initialFieldCount` (number of caller-supplied initial fields), `viaSpawn` (`true` only for a spawned child), `lifecycle`. |
|
|
498
|
+
| `Workflows Stage Transitioned` | 1 | A committed hop from `fire-action`, `start` cascade, or `set-stage` | `fromStageIndex`, `toStageIndex` (indexes into `stages[]`, never names), `toIsTerminal`, `isRevisit`, `dwellMs` (milliseconds in the exited stage, omitted if that entry is missing), `via`: `'transition'` or `'setStage'`. Unsampled. |
|
|
499
|
+
| `Workflows Action Fired` | 1 | `fire-action` | `activityKind` (optional; derived kind of the fired activity), `hasParams` (whether params were supplied — not the params), `cascaded` (auto-transitions in the post-action cascade). |
|
|
500
|
+
| `Workflows Instance Aborted` | 1 | `abort` | `changed`: `false` if the instance was already terminal. |
|
|
501
|
+
| `Workflows Stage Set` | 1 | `set-stage` | `changed`: `false` if already at the target stage. |
|
|
502
|
+
| `Workflows Activity Reset` | 1 | `reset-activity` | `changed`: `false` if already at the target status, or the instance was terminal. |
|
|
503
|
+
| `Workflows Definition Deleted` | 1 | `definition delete` | `deletedVersionCount`, `cascadeAbortedCount`, `deletedGuardCount` (guards only when the last version goes). |
|
|
504
|
+
| `Workflows Effect Completed` | 1 | `abort` cancelling a pending effect | `effect` (author-chosen name), `status` (`'done'`, `'failed'`, or `'cancelled'`), `origin` (`'action'`), `cascaded`. |
|
|
505
|
+
|
|
506
|
+
The engine also defines `Workflows Field Edited`, `Workflows Instance Ticked`,
|
|
507
|
+
`Workflows Effects Drained`, and `Workflows Effect State Reported`. This CLI
|
|
508
|
+
does not call those verbs, so it does not record those events. Field-edit and
|
|
509
|
+
tick events are sampled at most once per 60 seconds when some other shell
|
|
510
|
+
does emit them.
|
|
511
|
+
|
|
201
512
|
## Definition sharing
|
|
202
513
|
|
|
203
514
|
Separate from telemetry, a `deploy` that creates new definition versions
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WorkflowCommand } from '../../../lib/base-command.ts';
|
|
2
|
+
export default class BlueprintGenerate extends WorkflowCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { WorkflowCommand } from "../../../lib/base-command.js";
|
|
3
|
+
import { checkBlueprint, generateBlueprint } from "../../../lib/blueprint-emit.js";
|
|
4
|
+
import { loadWorkflowConfigModule } from "../../../lib/load-config.js";
|
|
5
|
+
export default class BlueprintGenerate extends WorkflowCommand {
|
|
6
|
+
static description = 'Experimental: generate the Sanity Blueprints runtime the definitions require, next to sanity.workflow.ts. Writes the workflow resources, one function per derived need, the effect-handler registry, and a handler stub per declared effect. Covers every deployment in the config, because the emitted resources module declares them all. These flags and this output may change before the Blueprints backend accepts the sanity.workflow resource.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> workflows blueprint generate',
|
|
9
|
+
'<%= config.bin %> workflows blueprint generate --check',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
check: Flags.boolean({
|
|
13
|
+
description: 'Experimental: verify the tree on disk still matches the definitions; write nothing and exit non-zero on any difference. The CI drift gate.',
|
|
14
|
+
default: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(BlueprintGenerate);
|
|
19
|
+
const root = process.cwd();
|
|
20
|
+
const { config, configFile, exportedNames } = await loadWorkflowConfigModule(root);
|
|
21
|
+
const log = (line) => this.log(line);
|
|
22
|
+
if (flags.check) {
|
|
23
|
+
checkBlueprint({ root, config, configFile, exportedNames, log });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
generateBlueprint({ root, config, configFile, exportedNames, log });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
|
|
1
|
+
import { type Diagnosis, type DiagnoseInput, type MissingDocument, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
|
|
2
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
|
|
@@ -23,10 +23,12 @@ export declare function rawTransitionProjection(evaluation: WorkflowEvaluation):
|
|
|
23
23
|
* {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
|
|
24
24
|
* fix block and the `--json` output stay one computation.
|
|
25
25
|
*/
|
|
26
|
-
export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, }: {
|
|
26
|
+
export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, allMissingDocuments, }: {
|
|
27
27
|
diagnosis: Diagnosis;
|
|
28
28
|
input: DiagnoseInput;
|
|
29
29
|
remediations: SuggestedRemediation[];
|
|
30
|
+
/** Full evaluation evidence, including references outside the blocking subset. */
|
|
31
|
+
allMissingDocuments?: MissingDocument[] | undefined;
|
|
30
32
|
/** Insight summaries per unsatisfied transition name — rendered under the
|
|
31
33
|
* transition line so "stuck" reads as "stuck until X". */
|
|
32
34
|
explanations?: ReadonlyMap<string, string> | undefined;
|