@sanity/workflow-cli 0.9.0 → 0.11.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 +516 -0
- package/README.md +71 -19
- package/bin/run.js +0 -4
- package/dist/commands/abort.d.ts +4 -6
- package/dist/commands/abort.js +13 -24
- package/dist/commands/definition/delete.d.ts +2 -2
- package/dist/commands/definition/delete.js +14 -27
- package/dist/commands/definition/diff.d.ts +2 -2
- package/dist/commands/definition/diff.js +3 -12
- package/dist/commands/definition/list.d.ts +3 -4
- package/dist/commands/definition/list.js +41 -47
- package/dist/commands/definition/show.d.ts +35 -3
- package/dist/commands/definition/show.js +44 -24
- package/dist/commands/deploy.d.ts +11 -5
- package/dist/commands/deploy.js +54 -57
- package/dist/commands/diagnose.d.ts +22 -4
- package/dist/commands/diagnose.js +60 -56
- package/dist/commands/fire-action.d.ts +2 -4
- package/dist/commands/fire-action.js +39 -62
- package/dist/commands/list.d.ts +18 -10
- package/dist/commands/list.js +77 -69
- package/dist/commands/reset-activity.js +0 -1
- package/dist/commands/set-stage.d.ts +2 -2
- package/dist/commands/set-stage.js +14 -30
- package/dist/commands/show.d.ts +2 -2
- package/dist/commands/show.js +12 -24
- package/dist/commands/start.d.ts +4 -28
- package/dist/commands/start.js +19 -80
- package/dist/commands/tail.d.ts +2 -2
- package/dist/commands/tail.js +34 -36
- package/dist/hooks/finally/telemetry.d.ts +8 -0
- package/dist/hooks/finally/telemetry.js +13 -0
- package/dist/hooks/prerun/telemetry.d.ts +8 -0
- package/dist/hooks/prerun/telemetry.js +9 -0
- package/dist/index.js +0 -3
- package/dist/lib/base-command.d.ts +14 -0
- package/dist/lib/base-command.js +10 -0
- package/dist/lib/client.js +13 -30
- package/dist/lib/context.d.ts +49 -13
- package/dist/lib/context.js +49 -48
- package/dist/lib/definitions.d.ts +10 -0
- package/dist/lib/definitions.js +11 -24
- package/dist/lib/diff.d.ts +5 -2
- package/dist/lib/diff.js +61 -27
- package/dist/lib/env.d.ts +4 -0
- package/dist/lib/env.js +4 -3
- package/dist/lib/fail.d.ts +19 -6
- package/dist/lib/fail.js +26 -26
- package/dist/lib/flags.d.ts +0 -7
- package/dist/lib/flags.js +0 -17
- package/dist/lib/load-config.d.ts +11 -7
- package/dist/lib/load-config.js +40 -20
- package/dist/lib/operation-args.d.ts +14 -23
- package/dist/lib/operation-args.js +6 -38
- package/dist/lib/ops-report.d.ts +18 -10
- package/dist/lib/ops-report.js +20 -16
- package/dist/lib/params.js +0 -8
- package/dist/lib/read-fanout.d.ts +22 -0
- package/dist/lib/read-fanout.js +28 -0
- package/dist/lib/select-deployment.js +0 -21
- package/dist/lib/share-definitions.d.ts +120 -0
- package/dist/lib/share-definitions.js +187 -0
- package/dist/lib/stub.js +0 -7
- package/dist/lib/telemetry-setup.d.ts +70 -0
- package/dist/lib/telemetry-setup.js +99 -0
- package/dist/lib/telemetry.d.ts +131 -0
- package/dist/lib/telemetry.js +94 -0
- package/dist/lib/ui.d.ts +33 -1
- package/dist/lib/ui.js +32 -22
- package/oclif.manifest.json +21 -52
- package/package.json +14 -8
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's telemetry shell. A standalone oclif binary is its own app
|
|
3
|
+
* shell — no host to ride — so this module owns the store, the standard
|
|
4
|
+
* Sanity-intake consent/transport recipe (shared via
|
|
5
|
+
* `@sanity/workflow-engine`'s `createTelemetryIntake`), and the one-time
|
|
6
|
+
* disclosure. The `prerun` hook assembles it once per invocation and the
|
|
7
|
+
* `finally` hook completes the command trace and flushes.
|
|
8
|
+
*
|
|
9
|
+
* Consent recipe: CI / `DO_NOT_TRACK` → denied; not logged in → no store
|
|
10
|
+
* (undetermined, nothing sent); otherwise the account-level status the
|
|
11
|
+
* intake recipe fetches. Consent is managed account-wide by
|
|
12
|
+
* `sanity telemetry enable|disable|status` — the CLI inherits it and
|
|
13
|
+
* ships no consent UX of its own.
|
|
14
|
+
*
|
|
15
|
+
* Provided-implementation caveat: a `telemetry` logger on the workflow
|
|
16
|
+
* config replaces this shell wholesale — the built-in store is never
|
|
17
|
+
* constructed and none of its policy (consent, environment suppression,
|
|
18
|
+
* transport) applies; every event flows to the provided logger
|
|
19
|
+
* unconditionally. Gates live in the store, not at emission sites.
|
|
20
|
+
*
|
|
21
|
+
* Payload policy: no customer strings, ever. The command trace carries
|
|
22
|
+
* the command id, the NAMES of declared flags used, and a success flag —
|
|
23
|
+
* never flag values, arguments, or error text. `Editorial Workflows
|
|
24
|
+
* Definition Shared` is a content-free marker (hash + structural counts); the shared
|
|
25
|
+
* document itself rides the first-party feedback endpoint, never
|
|
26
|
+
* telemetry (see `share-definitions.ts`). `Editorial Workflows Definition
|
|
27
|
+
* Sharing Decided` records the per-invocation consent outcome (default /
|
|
28
|
+
* opt-in / opt-out and whether anything was donated) — also content-free.
|
|
29
|
+
*/
|
|
30
|
+
import { type DefinedTelemetryTrace, type TelemetryStore } from '@sanity/telemetry';
|
|
31
|
+
import { type TelemetryIntakeClient, type WorkflowDefinitionDeployedData, type WorkflowTelemetryLogger } from '@sanity/workflow-engine';
|
|
32
|
+
export interface WorkflowCliCommandData {
|
|
33
|
+
/** The oclif command id — ours, never user free-text. */
|
|
34
|
+
command: string;
|
|
35
|
+
/** Names of DECLARED flags present on the invocation — never values,
|
|
36
|
+
* never undeclared tokens. */
|
|
37
|
+
flags: string[];
|
|
38
|
+
success: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare const WorkflowCliCommandExecuted: DefinedTelemetryTrace<WorkflowCliCommandData, void>;
|
|
41
|
+
/** {@link WorkflowDefinitionDeployedData} minus the deploy status — the marker
|
|
42
|
+
* is projected from the engine's own structural derivation so the two events
|
|
43
|
+
* can never disagree about one definition, and only newly created versions
|
|
44
|
+
* are ever shared, so status carries nothing. */
|
|
45
|
+
export type WorkflowDefinitionSharedData = Omit<WorkflowDefinitionDeployedData, 'status'>;
|
|
46
|
+
export declare const WorkflowDefinitionShared: import("@sanity/telemetry").DefinedTelemetryLog<WorkflowDefinitionSharedData>;
|
|
47
|
+
/**
|
|
48
|
+
* How the donation decision was reached, and what it was. Paired with `shared`
|
|
49
|
+
* (whether it actually reached the endpoint) this keeps every case distinct —
|
|
50
|
+
* an explicit decline vs a run that couldn't ask, and a chosen share vs a
|
|
51
|
+
* failed POST:
|
|
52
|
+
* - `opt-in` / `opt-out` — an explicit `--share-defs` / `--no-share-defs`.
|
|
53
|
+
* - `prompt-accepted` / `prompt-declined` — the first-run consent prompt.
|
|
54
|
+
* - `remembered-share` / `remembered-decline` — a persisted earlier answer.
|
|
55
|
+
* - `unattended` — CI / `DO_NOT_TRACK` / non-TTY: never asked, never shares.
|
|
56
|
+
*/
|
|
57
|
+
export type DefinitionSharingDecision = 'opt-in' | 'opt-out' | 'prompt-accepted' | 'prompt-declined' | 'remembered-share' | 'remembered-decline' | 'unattended';
|
|
58
|
+
export interface WorkflowDefinitionSharingDecidedData {
|
|
59
|
+
decision: DefinitionSharingDecision;
|
|
60
|
+
/** Whether the donation actually reached the feedback endpoint — `false` for
|
|
61
|
+
* any non-sharing decision, and also for a chosen share whose POST failed. */
|
|
62
|
+
shared: boolean;
|
|
63
|
+
/** Newly created definition versions this invocation could have donated. */
|
|
64
|
+
definitionCount: number;
|
|
65
|
+
}
|
|
66
|
+
export declare const WorkflowDefinitionSharingDecided: import("@sanity/telemetry").DefinedTelemetryLog<WorkflowDefinitionSharingDecidedData>;
|
|
67
|
+
/**
|
|
68
|
+
* The shell for one CLI invocation. `builtin` is the Sanity-intake store;
|
|
69
|
+
* `provided` is a config-supplied logger that replaces the shell and its
|
|
70
|
+
* policy wholesale; `off` (the default) emits nothing — no config-derived
|
|
71
|
+
* project, no auth token, or telemetry setup failed.
|
|
72
|
+
*/
|
|
73
|
+
export type CliTelemetry = {
|
|
74
|
+
kind: 'builtin';
|
|
75
|
+
logger: WorkflowTelemetryLogger;
|
|
76
|
+
store: TelemetryStore<unknown>;
|
|
77
|
+
envDenied: boolean;
|
|
78
|
+
/** The environment denial was overridden ({@link createBuiltinTelemetry}):
|
|
79
|
+
* the store sends despite CI / `DO_NOT_TRACK`, subject only to
|
|
80
|
+
* account consent. */
|
|
81
|
+
forceSend: boolean;
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'provided';
|
|
84
|
+
logger: WorkflowTelemetryLogger;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'off';
|
|
87
|
+
logger: WorkflowTelemetryLogger;
|
|
88
|
+
};
|
|
89
|
+
export declare function cliTelemetry(): CliTelemetry;
|
|
90
|
+
/** Install the invocation's shell and, for the built-in store, start the
|
|
91
|
+
* command trace (completed by {@link finishCliTelemetry}). */
|
|
92
|
+
export declare function setCliTelemetry(telemetry: CliTelemetry): void;
|
|
93
|
+
export declare function clearCliTelemetry(): void;
|
|
94
|
+
/** Build the built-in Sanity-intake shell over an authenticated,
|
|
95
|
+
* project-bound client. `forceSend` overrides the environment denial
|
|
96
|
+
* (CI / `DO_NOT_TRACK`) — an explicit `--share-defs` opts the deploy
|
|
97
|
+
* invocation's telemetry in regardless (a command-line arg beats env
|
|
98
|
+
* vars). Account-level consent is unaffected: the intake still fetches
|
|
99
|
+
* `/intake/telemetry-status`, so `sanity telemetry disable` still mutes. */
|
|
100
|
+
export declare function createBuiltinTelemetry(args: {
|
|
101
|
+
client: TelemetryIntakeClient;
|
|
102
|
+
projectId: string;
|
|
103
|
+
env: Record<string, string | undefined>;
|
|
104
|
+
forceSend?: boolean;
|
|
105
|
+
}): Extract<CliTelemetry, {
|
|
106
|
+
kind: 'builtin';
|
|
107
|
+
}>;
|
|
108
|
+
/**
|
|
109
|
+
* Race `work` against a deadline, resolving `undefined` when the deadline
|
|
110
|
+
* wins. The timer is always cleared once the race settles — oclif never
|
|
111
|
+
* force-exits on success, so a pending timer would hold a finished process
|
|
112
|
+
* open. The losing promise's eventual settlement is absorbed by the race.
|
|
113
|
+
*/
|
|
114
|
+
export declare function raceWithDeadline<T>(work: Promise<T>, deadlineMs: number): Promise<T | undefined>;
|
|
115
|
+
/**
|
|
116
|
+
* Complete the command trace and flush the final batch — the `finally`
|
|
117
|
+
* hook's whole job. On the built-in store the trace completes with the
|
|
118
|
+
* payload and the store flushes (deadline-capped); a provided logger gets
|
|
119
|
+
* the same payload through its `log` seam and owns its own delivery.
|
|
120
|
+
*/
|
|
121
|
+
export declare function finishCliTelemetry(data: WorkflowCliCommandData): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* The names of declared flags present in `argv` — long form (`--name`,
|
|
124
|
+
* `--name=value`), negated booleans (`--no-name`), and short chars
|
|
125
|
+
* (`-c`). Matching against the command's declared flags is what keeps
|
|
126
|
+
* values and free-text out of the payload; tokens after the `--`
|
|
127
|
+
* terminator are never inspected.
|
|
128
|
+
*/
|
|
129
|
+
export declare function usedFlagNames(argv: string[], flagDefs: Record<string, {
|
|
130
|
+
char?: string;
|
|
131
|
+
}> | undefined): string[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createBatchedStore, createSessionId, defineEvent, defineTrace, } from '@sanity/telemetry';
|
|
2
|
+
import { createTelemetryIntake, isTelemetryEnvDenied, noopTelemetry, } from '@sanity/workflow-engine';
|
|
3
|
+
export const WorkflowCliCommandExecuted = defineTrace({
|
|
4
|
+
name: 'Editorial Workflows CLI Command Executed',
|
|
5
|
+
version: 1,
|
|
6
|
+
description: 'A workflow CLI command was executed — payload is the command id, the names of declared flags used (never their values), and a success flag',
|
|
7
|
+
});
|
|
8
|
+
export const WorkflowDefinitionShared = defineEvent({
|
|
9
|
+
name: 'Editorial Workflows Definition Shared',
|
|
10
|
+
version: 1,
|
|
11
|
+
description: 'A newly created workflow definition version was donated to Sanity. Content-free marker: the content hash plus the same structural counts Editorial Workflows Definition Deployed carries — the document itself goes to the first-party definition-feedback endpoint, never through telemetry.',
|
|
12
|
+
});
|
|
13
|
+
export const WorkflowDefinitionSharingDecided = defineEvent({
|
|
14
|
+
name: 'Editorial Workflows Definition Sharing Decided',
|
|
15
|
+
version: 1,
|
|
16
|
+
description: 'One per deploy invocation that created new definition versions: how the donation decision was reached and what it was (explicit flag / prompt accept-or-decline / remembered / unattended), whether anything reached the endpoint, and how many versions were eligible. Content-free — lets adoption dashboards separate explicit opt-outs, prompt declines, and unattended runs, and measure the true opt-out rate, without ever carrying the document.',
|
|
17
|
+
});
|
|
18
|
+
const OFF = { kind: 'off', logger: noopTelemetry };
|
|
19
|
+
let current = OFF;
|
|
20
|
+
let activeTrace;
|
|
21
|
+
export function cliTelemetry() {
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
export function setCliTelemetry(telemetry) {
|
|
25
|
+
current = telemetry;
|
|
26
|
+
if (telemetry.kind === 'builtin') {
|
|
27
|
+
activeTrace = telemetry.store.logger.trace(WorkflowCliCommandExecuted);
|
|
28
|
+
activeTrace.start();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function clearCliTelemetry() {
|
|
32
|
+
current = OFF;
|
|
33
|
+
activeTrace = undefined;
|
|
34
|
+
}
|
|
35
|
+
export function createBuiltinTelemetry(args) {
|
|
36
|
+
const { client, projectId, env, forceSend = false } = args;
|
|
37
|
+
const envDenied = isTelemetryEnvDenied(env);
|
|
38
|
+
const store = createBatchedStore(createSessionId(), createTelemetryIntake({ client, projectId, denied: forceSend ? false : envDenied }));
|
|
39
|
+
return { kind: 'builtin', logger: store.logger, store, envDenied, forceSend };
|
|
40
|
+
}
|
|
41
|
+
function traceAsLogEvent(trace) {
|
|
42
|
+
const { name, version, description } = trace;
|
|
43
|
+
return { type: 'log', name, version, description };
|
|
44
|
+
}
|
|
45
|
+
const FLUSH_DEADLINE_MS = 3_000;
|
|
46
|
+
export async function raceWithDeadline(work, deadlineMs) {
|
|
47
|
+
let deadline;
|
|
48
|
+
try {
|
|
49
|
+
return await Promise.race([
|
|
50
|
+
work,
|
|
51
|
+
new Promise((resolve) => {
|
|
52
|
+
deadline = setTimeout(() => resolve(undefined), deadlineMs);
|
|
53
|
+
}),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
clearTimeout(deadline);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export async function finishCliTelemetry(data) {
|
|
61
|
+
if (current.kind === 'provided') {
|
|
62
|
+
current.logger.log(traceAsLogEvent(WorkflowCliCommandExecuted), data);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (current.kind !== 'builtin')
|
|
66
|
+
return;
|
|
67
|
+
if (activeTrace !== undefined) {
|
|
68
|
+
activeTrace.log(data);
|
|
69
|
+
activeTrace.complete();
|
|
70
|
+
activeTrace = undefined;
|
|
71
|
+
}
|
|
72
|
+
const { store } = current;
|
|
73
|
+
try {
|
|
74
|
+
await raceWithDeadline(store.flush(), FLUSH_DEADLINE_MS);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
store.end();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function usedFlagNames(argv, flagDefs) {
|
|
81
|
+
const terminator = argv.indexOf('--');
|
|
82
|
+
const tokens = argv.slice(0, terminator === -1 ? argv.length : terminator);
|
|
83
|
+
return Object.entries(flagDefs ?? {})
|
|
84
|
+
.filter(([name, def]) => tokens.some((token) => matchesFlag({ token, name, char: def.char })))
|
|
85
|
+
.map(([name]) => name)
|
|
86
|
+
.sort();
|
|
87
|
+
}
|
|
88
|
+
function matchesFlag(args) {
|
|
89
|
+
const { token, name, char } = args;
|
|
90
|
+
if (token === `--${name}` || token.startsWith(`--${name}=`) || token === `--no-${name}`) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return char !== undefined && (token === `-${char}` || token.startsWith(`-${char}=`));
|
|
94
|
+
}
|
package/dist/lib/ui.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivityStatus } from '@sanity/workflow-engine';
|
|
1
|
+
import type { ActivityStatus, WorkflowResource } from '@sanity/workflow-engine';
|
|
2
2
|
/**
|
|
3
3
|
* A bold `Title:` section heading. Mirrors the Sanity CLI's `sectionHeader`
|
|
4
4
|
* (name + shape) so it folds into the shared helper when this CLI merges in.
|
|
@@ -15,6 +15,38 @@ export declare function formatKeyValue({ key, value, indent, padTo, }: {
|
|
|
15
15
|
indent?: number;
|
|
16
16
|
padTo?: number;
|
|
17
17
|
}): string;
|
|
18
|
+
/**
|
|
19
|
+
* A borderless column table as printable lines: cyan header row, every
|
|
20
|
+
* column padded to its widest cell, columns joined by three spaces.
|
|
21
|
+
* Mirrors the Sanity CLI's list-table shape (see `projects list`) so it
|
|
22
|
+
* folds into the shared helper when this CLI merges in. Widths count
|
|
23
|
+
* visible characters, so a pre-styled (ANSI) cell still pads correctly.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatTable(headers: string[], rows: string[][]): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Clip a probe-fetched result back to `limit`. The list query builders
|
|
28
|
+
* fetch one row past the display limit; when that probe row comes back,
|
|
29
|
+
* `note` carries the dim "showing the first N" hint to print under the table.
|
|
30
|
+
*/
|
|
31
|
+
export declare function clipToLimit<T>(rows: T[], limit: number): {
|
|
32
|
+
rows: T[];
|
|
33
|
+
note?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The `▸ label` banner printed above a group's output when a run spans
|
|
37
|
+
* several groups (deployments for deploy, resources for fanned-out reads),
|
|
38
|
+
* so each block is attributable. `undefined` for a lone group — it keeps its
|
|
39
|
+
* original unprefixed output. A leading blank line separates every group
|
|
40
|
+
* after the first.
|
|
41
|
+
*/
|
|
42
|
+
export declare function groupBanner({ label, index, total, }: {
|
|
43
|
+
label: string;
|
|
44
|
+
index: number;
|
|
45
|
+
total: number;
|
|
46
|
+
}): string | undefined;
|
|
47
|
+
/** A workflow resource as a display label: a dataset's `project.dataset` id
|
|
48
|
+
* reads naturally bare; other resource types keep their type for clarity. */
|
|
49
|
+
export declare function resourceLabel(resource: WorkflowResource): string;
|
|
18
50
|
/** Status glyph per activity state. log-symbols ship pre-colored (green ✔, red ✖),
|
|
19
51
|
* so those need no extra wrap; the rest carry their own state color. */
|
|
20
52
|
export declare const activityIcon: Record<ActivityStatus, string>;
|
package/dist/lib/ui.js
CHANGED
|
@@ -1,52 +1,62 @@
|
|
|
1
|
-
import { styleText } from 'node:util';
|
|
1
|
+
import { stripVTControlCharacters, styleText } from 'node:util';
|
|
2
2
|
import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
|
|
3
3
|
import { lightFormat } from 'date-fns/lightFormat';
|
|
4
4
|
import { parseISO } from 'date-fns/parseISO';
|
|
5
5
|
import logSymbols from 'log-symbols';
|
|
6
|
-
/**
|
|
7
|
-
* A bold `Title:` section heading. Mirrors the Sanity CLI's `sectionHeader`
|
|
8
|
-
* (name + shape) so it folds into the shared helper when this CLI merges in.
|
|
9
|
-
*/
|
|
10
6
|
export function sectionHeader(title) {
|
|
11
7
|
return styleText('bold', `${title}:`);
|
|
12
8
|
}
|
|
13
|
-
/**
|
|
14
|
-
* A ` key: value` detail row — a dim, padded key then the value. Mirrors
|
|
15
|
-
* the Sanity CLI's `formatKeyValue` so it folds into the shared helper on
|
|
16
|
-
* merge; pass `padTo` (the longest key's length) to align a block of rows.
|
|
17
|
-
*/
|
|
18
9
|
export function formatKeyValue({ key, value, indent = 2, padTo = 0, }) {
|
|
19
10
|
const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1);
|
|
20
11
|
return `${' '.repeat(indent)}${styleText('dim', paddedKey)} ${value}`;
|
|
21
12
|
}
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
export function formatTable(headers, rows) {
|
|
14
|
+
const width = (cell) => stripVTControlCharacters(cell).length;
|
|
15
|
+
const widths = headers.map((header, i) => Math.max(width(header), ...rows.map((row) => width(row[i] ?? ''))));
|
|
16
|
+
const formatRow = (row) => widths
|
|
17
|
+
.map((columnWidth, i) => {
|
|
18
|
+
const cell = row[i] ?? '';
|
|
19
|
+
return cell + ' '.repeat(columnWidth - width(cell));
|
|
20
|
+
})
|
|
21
|
+
.join(' ')
|
|
22
|
+
.trimEnd();
|
|
23
|
+
return [styleText('cyan', formatRow(headers)), ...rows.map(formatRow)];
|
|
24
|
+
}
|
|
25
|
+
export function clipToLimit(rows, limit) {
|
|
26
|
+
if (rows.length <= limit) {
|
|
27
|
+
return { rows };
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
rows: rows.slice(0, limit),
|
|
31
|
+
note: styleText('dim', `showing the first ${limit} — raise --limit to see more`),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function groupBanner({ label, index, total, }) {
|
|
35
|
+
if (total <= 1) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const gap = index > 0 ? '\n' : '';
|
|
39
|
+
return `${gap}${styleText('bold', `▸ ${label}`)}`;
|
|
40
|
+
}
|
|
41
|
+
export function resourceLabel(resource) {
|
|
42
|
+
return resource.type === 'dataset' ? resource.id : `${resource.type}:${resource.id}`;
|
|
43
|
+
}
|
|
24
44
|
export const activityIcon = {
|
|
25
45
|
done: logSymbols.success,
|
|
26
46
|
active: styleText('cyan', '●'),
|
|
27
|
-
pending: styleText('dim', '○'),
|
|
28
47
|
failed: logSymbols.error,
|
|
29
48
|
skipped: styleText('dim', '⊘'),
|
|
30
49
|
};
|
|
31
|
-
/** Parse an engine ISO-8601 timestamp, or `undefined` when it's missing or
|
|
32
|
-
* unparseable. The engine writes valid ISO, but the lake is the only
|
|
33
|
-
* enforcement boundary — a legacy or raw-client-written doc can carry a bad
|
|
34
|
-
* or absent timestamp, and `parseISO`/`lightFormat` throw on those. Callers
|
|
35
|
-
* fall back to the raw value so one bad field never crashes a whole command. */
|
|
36
50
|
function parseTimestamp(iso) {
|
|
37
51
|
if (!iso)
|
|
38
52
|
return undefined;
|
|
39
53
|
const date = parseISO(iso);
|
|
40
54
|
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
41
55
|
}
|
|
42
|
-
/** An engine ISO-8601 timestamp as an absolute `yyyy-MM-dd HH:mm:ss` — the
|
|
43
|
-
* Sanity CLI's audit-log format (see `backups/list`). For detail views. */
|
|
44
56
|
export function formatTimestamp(iso) {
|
|
45
57
|
const date = parseTimestamp(iso);
|
|
46
58
|
return date ? lightFormat(date, 'yyyy-MM-dd HH:mm:ss') : iso;
|
|
47
59
|
}
|
|
48
|
-
/** An engine ISO-8601 timestamp as a relative `… ago` — the Sanity CLI's
|
|
49
|
-
* job-list format (see `datasets/copy`). For scannable overview tables. */
|
|
50
60
|
export function formatAge(iso) {
|
|
51
61
|
const date = parseTimestamp(iso);
|
|
52
62
|
return date ? `${formatDistanceToNow(date)} ago` : iso;
|
package/oclif.manifest.json
CHANGED
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"pluginName": "@sanity/workflow-cli",
|
|
38
38
|
"pluginType": "core",
|
|
39
39
|
"strict": true,
|
|
40
|
-
"enableJsonFlag": false,
|
|
41
40
|
"isESM": true,
|
|
42
41
|
"relativePath": [
|
|
43
42
|
"dist",
|
|
@@ -91,6 +90,12 @@
|
|
|
91
90
|
"hasDynamicHelp": false,
|
|
92
91
|
"multiple": false,
|
|
93
92
|
"type": "option"
|
|
93
|
+
},
|
|
94
|
+
"share-defs": {
|
|
95
|
+
"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.",
|
|
96
|
+
"name": "share-defs",
|
|
97
|
+
"allowNo": true,
|
|
98
|
+
"type": "boolean"
|
|
94
99
|
}
|
|
95
100
|
},
|
|
96
101
|
"hasDynamicHelp": false,
|
|
@@ -100,7 +105,6 @@
|
|
|
100
105
|
"pluginName": "@sanity/workflow-cli",
|
|
101
106
|
"pluginType": "core",
|
|
102
107
|
"strict": true,
|
|
103
|
-
"enableJsonFlag": false,
|
|
104
108
|
"isESM": true,
|
|
105
109
|
"relativePath": [
|
|
106
110
|
"dist",
|
|
@@ -145,7 +149,6 @@
|
|
|
145
149
|
"pluginName": "@sanity/workflow-cli",
|
|
146
150
|
"pluginType": "core",
|
|
147
151
|
"strict": true,
|
|
148
|
-
"enableJsonFlag": false,
|
|
149
152
|
"isESM": true,
|
|
150
153
|
"relativePath": [
|
|
151
154
|
"dist",
|
|
@@ -162,11 +165,10 @@
|
|
|
162
165
|
"required": true
|
|
163
166
|
}
|
|
164
167
|
},
|
|
165
|
-
"description": "Fire an action on an instance
|
|
168
|
+
"description": "Fire an action on an instance to unstick a waiting activity — the write acts as the configured token. Omit --action to list what can be fired.",
|
|
166
169
|
"examples": [
|
|
167
170
|
"<%= config.bin %> fire-action wf-instance.abc123",
|
|
168
171
|
"<%= config.bin %> fire-action wf-instance.abc123 --activity approve --action approve",
|
|
169
|
-
"<%= config.bin %> fire-action wf-instance.abc123 --activity approve --action approve --as user.xyz --as-role editor",
|
|
170
172
|
"<%= config.bin %> fire-action wf-instance.abc123 --activity publish --action publish --param note=shipping"
|
|
171
173
|
],
|
|
172
174
|
"flags": {
|
|
@@ -199,21 +201,6 @@
|
|
|
199
201
|
"multiple": true,
|
|
200
202
|
"type": "option"
|
|
201
203
|
},
|
|
202
|
-
"as": {
|
|
203
|
-
"description": "Attribute the write to this Sanity user id (default: a workflow-cli system actor).",
|
|
204
|
-
"name": "as",
|
|
205
|
-
"hasDynamicHelp": false,
|
|
206
|
-
"multiple": false,
|
|
207
|
-
"type": "option"
|
|
208
|
-
},
|
|
209
|
-
"as-role": {
|
|
210
|
-
"description": "Role to attribute to the --as user (repeatable), so role-gated filters pass.",
|
|
211
|
-
"name": "as-role",
|
|
212
|
-
"default": [],
|
|
213
|
-
"hasDynamicHelp": false,
|
|
214
|
-
"multiple": true,
|
|
215
|
-
"type": "option"
|
|
216
|
-
},
|
|
217
204
|
"json": {
|
|
218
205
|
"description": "Emit structured JSON instead of rendered output.",
|
|
219
206
|
"name": "json",
|
|
@@ -228,7 +215,6 @@
|
|
|
228
215
|
"pluginName": "@sanity/workflow-cli",
|
|
229
216
|
"pluginType": "core",
|
|
230
217
|
"strict": true,
|
|
231
|
-
"enableJsonFlag": false,
|
|
232
218
|
"isESM": true,
|
|
233
219
|
"relativePath": [
|
|
234
220
|
"dist",
|
|
@@ -239,11 +225,12 @@
|
|
|
239
225
|
"list": {
|
|
240
226
|
"aliases": [],
|
|
241
227
|
"args": {},
|
|
242
|
-
"description": "List workflow instances in the configured dataset.",
|
|
228
|
+
"description": "List workflow instances in the configured dataset (in-flight by default).",
|
|
243
229
|
"examples": [
|
|
244
230
|
"<%= config.bin %> list",
|
|
245
|
-
"<%= config.bin %> list --
|
|
231
|
+
"<%= config.bin %> list --include-completed",
|
|
246
232
|
"<%= config.bin %> list --definition productLaunch",
|
|
233
|
+
"<%= config.bin %> list --document dataset:proj:ds:article-1",
|
|
247
234
|
"<%= config.bin %> list --tag prod"
|
|
248
235
|
],
|
|
249
236
|
"flags": {
|
|
@@ -254,9 +241,9 @@
|
|
|
254
241
|
"multiple": false,
|
|
255
242
|
"type": "option"
|
|
256
243
|
},
|
|
257
|
-
"
|
|
258
|
-
"description": "
|
|
259
|
-
"name": "
|
|
244
|
+
"include-completed": {
|
|
245
|
+
"description": "Include completed/aborted instances (default: in-flight only).",
|
|
246
|
+
"name": "include-completed",
|
|
260
247
|
"allowNo": false,
|
|
261
248
|
"type": "boolean"
|
|
262
249
|
},
|
|
@@ -273,6 +260,13 @@
|
|
|
273
260
|
"multiple": false,
|
|
274
261
|
"type": "option"
|
|
275
262
|
},
|
|
263
|
+
"document": {
|
|
264
|
+
"description": "Only instances that reference this document (resource-qualified GDR URI, e.g. \"dataset:proj:ds:article-1\").",
|
|
265
|
+
"name": "document",
|
|
266
|
+
"hasDynamicHelp": false,
|
|
267
|
+
"multiple": false,
|
|
268
|
+
"type": "option"
|
|
269
|
+
},
|
|
276
270
|
"limit": {
|
|
277
271
|
"description": "Maximum rows to return.",
|
|
278
272
|
"name": "limit",
|
|
@@ -289,7 +283,6 @@
|
|
|
289
283
|
"pluginName": "@sanity/workflow-cli",
|
|
290
284
|
"pluginType": "core",
|
|
291
285
|
"strict": true,
|
|
292
|
-
"enableJsonFlag": false,
|
|
293
286
|
"isESM": true,
|
|
294
287
|
"relativePath": [
|
|
295
288
|
"dist",
|
|
@@ -373,7 +366,6 @@
|
|
|
373
366
|
"pluginName": "@sanity/workflow-cli",
|
|
374
367
|
"pluginType": "core",
|
|
375
368
|
"strict": true,
|
|
376
|
-
"enableJsonFlag": false,
|
|
377
369
|
"isESM": true,
|
|
378
370
|
"relativePath": [
|
|
379
371
|
"dist",
|
|
@@ -422,7 +414,6 @@
|
|
|
422
414
|
"pluginName": "@sanity/workflow-cli",
|
|
423
415
|
"pluginType": "core",
|
|
424
416
|
"strict": true,
|
|
425
|
-
"enableJsonFlag": false,
|
|
426
417
|
"isESM": true,
|
|
427
418
|
"relativePath": [
|
|
428
419
|
"dist",
|
|
@@ -443,7 +434,6 @@
|
|
|
443
434
|
"examples": [
|
|
444
435
|
"<%= config.bin %> start productLaunch",
|
|
445
436
|
"<%= config.bin %> start article-review --field subject='{\"id\":\"dataset:proj:ds:article-1\",\"type\":\"article\"}'",
|
|
446
|
-
"<%= config.bin %> start article-review --as user.xyz --as-role editor",
|
|
447
437
|
"<%= config.bin %> start productLaunch --version 2 --tag prod"
|
|
448
438
|
],
|
|
449
439
|
"flags": {
|
|
@@ -469,21 +459,6 @@
|
|
|
469
459
|
"multiple": true,
|
|
470
460
|
"type": "option"
|
|
471
461
|
},
|
|
472
|
-
"as": {
|
|
473
|
-
"description": "Attribute the write to this Sanity user id (default: a workflow-cli system actor).",
|
|
474
|
-
"name": "as",
|
|
475
|
-
"hasDynamicHelp": false,
|
|
476
|
-
"multiple": false,
|
|
477
|
-
"type": "option"
|
|
478
|
-
},
|
|
479
|
-
"as-role": {
|
|
480
|
-
"description": "Role to attribute to the --as user (repeatable), so role-gated filters pass.",
|
|
481
|
-
"name": "as-role",
|
|
482
|
-
"default": [],
|
|
483
|
-
"hasDynamicHelp": false,
|
|
484
|
-
"multiple": true,
|
|
485
|
-
"type": "option"
|
|
486
|
-
},
|
|
487
462
|
"json": {
|
|
488
463
|
"description": "Emit structured JSON instead of rendered output.",
|
|
489
464
|
"name": "json",
|
|
@@ -498,7 +473,6 @@
|
|
|
498
473
|
"pluginName": "@sanity/workflow-cli",
|
|
499
474
|
"pluginType": "core",
|
|
500
475
|
"strict": true,
|
|
501
|
-
"enableJsonFlag": false,
|
|
502
476
|
"isESM": true,
|
|
503
477
|
"relativePath": [
|
|
504
478
|
"dist",
|
|
@@ -535,7 +509,6 @@
|
|
|
535
509
|
"pluginName": "@sanity/workflow-cli",
|
|
536
510
|
"pluginType": "core",
|
|
537
511
|
"strict": true,
|
|
538
|
-
"enableJsonFlag": false,
|
|
539
512
|
"isESM": true,
|
|
540
513
|
"relativePath": [
|
|
541
514
|
"dist",
|
|
@@ -594,7 +567,6 @@
|
|
|
594
567
|
"pluginName": "@sanity/workflow-cli",
|
|
595
568
|
"pluginType": "core",
|
|
596
569
|
"strict": true,
|
|
597
|
-
"enableJsonFlag": false,
|
|
598
570
|
"isESM": true,
|
|
599
571
|
"relativePath": [
|
|
600
572
|
"dist",
|
|
@@ -640,7 +612,6 @@
|
|
|
640
612
|
"pluginName": "@sanity/workflow-cli",
|
|
641
613
|
"pluginType": "core",
|
|
642
614
|
"strict": true,
|
|
643
|
-
"enableJsonFlag": false,
|
|
644
615
|
"isESM": true,
|
|
645
616
|
"relativePath": [
|
|
646
617
|
"dist",
|
|
@@ -688,7 +659,6 @@
|
|
|
688
659
|
"pluginName": "@sanity/workflow-cli",
|
|
689
660
|
"pluginType": "core",
|
|
690
661
|
"strict": true,
|
|
691
|
-
"enableJsonFlag": false,
|
|
692
662
|
"isESM": true,
|
|
693
663
|
"relativePath": [
|
|
694
664
|
"dist",
|
|
@@ -730,7 +700,6 @@
|
|
|
730
700
|
"pluginName": "@sanity/workflow-cli",
|
|
731
701
|
"pluginType": "core",
|
|
732
702
|
"strict": true,
|
|
733
|
-
"enableJsonFlag": false,
|
|
734
703
|
"isESM": true,
|
|
735
704
|
"relativePath": [
|
|
736
705
|
"dist",
|
|
@@ -740,5 +709,5 @@
|
|
|
740
709
|
]
|
|
741
710
|
}
|
|
742
711
|
},
|
|
743
|
-
"version": "0.
|
|
712
|
+
"version": "0.11.0"
|
|
744
713
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Command-line tool for deploying, inspecting, and administering Sanity workflow definitions and instances.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
29
|
"bin/run.js",
|
|
30
|
-
"oclif.manifest.json"
|
|
30
|
+
"oclif.manifest.json",
|
|
31
|
+
"CHANGELOG.md"
|
|
31
32
|
],
|
|
32
33
|
"type": "module",
|
|
33
34
|
"sideEffects": false,
|
|
@@ -41,34 +42,38 @@
|
|
|
41
42
|
"./package.json": "./package.json"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
|
-
"access": "
|
|
45
|
+
"access": "public"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@oclif/core": "^4.11.4",
|
|
48
49
|
"@oclif/plugin-help": "^6.2.50",
|
|
49
50
|
"@sanity/cli-core": "^2.1.1",
|
|
50
51
|
"@sanity/client": "^7.22.1",
|
|
52
|
+
"@sanity/telemetry": "^1.1.0",
|
|
51
53
|
"boxen": "^8.0.1",
|
|
52
|
-
"console-table-printer": "^2.16.0",
|
|
53
54
|
"date-fns": "^4.4.0",
|
|
54
55
|
"diff": "^9.0.0",
|
|
55
56
|
"jiti": "^2.7.0",
|
|
56
57
|
"log-symbols": "^7.0.1",
|
|
57
58
|
"ora": "^9.4.0",
|
|
58
|
-
"@sanity/workflow-engine": "0.
|
|
59
|
+
"@sanity/workflow-engine": "0.16.0"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@types/diff": "^8.0.0",
|
|
62
63
|
"@types/node": "^24.12.4",
|
|
63
64
|
"oclif": "^4.23.16",
|
|
64
65
|
"vitest": "^4.1.8",
|
|
65
|
-
"@sanity/workflow-engine-test": "0.
|
|
66
|
-
"@sanity/workflow-examples": "0.
|
|
66
|
+
"@sanity/workflow-engine-test": "0.11.0",
|
|
67
|
+
"@sanity/workflow-examples": "0.6.0"
|
|
67
68
|
},
|
|
68
69
|
"oclif": {
|
|
69
70
|
"bin": "sanity-workflows",
|
|
70
71
|
"commands": "./dist/commands",
|
|
71
72
|
"dirname": "sanity-workflows",
|
|
73
|
+
"hooks": {
|
|
74
|
+
"finally": "./dist/hooks/finally/telemetry",
|
|
75
|
+
"prerun": "./dist/hooks/prerun/telemetry"
|
|
76
|
+
},
|
|
72
77
|
"plugins": [
|
|
73
78
|
"@oclif/plugin-help"
|
|
74
79
|
],
|
|
@@ -86,9 +91,10 @@
|
|
|
86
91
|
"node": ">=20.12"
|
|
87
92
|
},
|
|
88
93
|
"scripts": {
|
|
89
|
-
"build": "tsc -p tsconfig.build.json",
|
|
94
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.declarations.json",
|
|
90
95
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
91
96
|
"test": "vitest run",
|
|
97
|
+
"test:packaging": "vitest run --config vitest.packaging.config.ts",
|
|
92
98
|
"test:watch": "vitest",
|
|
93
99
|
"dev": "NODE_OPTIONS='--conditions=development' tsx --env-file-if-exists=../../.env bin/dev.js",
|
|
94
100
|
"cli": "NODE_OPTIONS='--conditions=development' tsx --env-file-if-exists=../../.env bin/dev.js"
|