@vzn/vx 0.0.631 → 0.0.632
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/README.md +7 -8
- package/package.json +7 -5
- package/schemas/cache.json +24 -0
- package/schemas/error.json +23 -0
- package/schemas/info.json +147 -0
- package/schemas/last.json +188 -0
- package/schemas/plan.json +88 -0
- package/schemas/show.json +124 -0
- package/schemas/summary.json +133 -0
- package/schemas/why.json +168 -0
- package/skills/vx/SKILL.md +85 -0
- package/src/bin.ts +9 -0
- package/src/cache/archive.ts +153 -51
- package/src/cache/cache.ts +45 -19
- package/src/cache/layer.ts +2 -1
- package/src/cache/schema.ts +2 -1
- package/src/cache/tar-stream.ts +204 -66
- package/src/cache/zstd.ts +1 -1
- package/src/cli/cache.ts +14 -14
- package/src/cli/foreign-flags.ts +1 -1
- package/src/cli/help.ts +30 -5
- package/src/cli/index.ts +23 -11
- package/src/cli/info.ts +38 -9
- package/src/cli/init.ts +97 -19
- package/src/cli/last.ts +54 -3
- package/src/cli/plan-format.ts +23 -1
- package/src/cli/plugin-commands.ts +3 -0
- package/src/cli/plugin-templates.ts +1 -1
- package/src/cli/run.ts +40 -21
- package/src/cli/show.ts +10 -7
- package/src/cli/upgrade.ts +1 -1
- package/src/cli/watch.ts +7 -2
- package/src/cli/why.ts +14 -40
- package/src/exec/runner.ts +27 -10
- package/src/graph/scheduler.ts +36 -0
- package/src/graph/task-graph.ts +1 -1
- package/src/index.ts +22 -5
- package/src/orchestrator/admission.ts +29 -1
- package/src/orchestrator/affected-tasks.ts +87 -18
- package/src/orchestrator/doctor.ts +14 -1
- package/src/orchestrator/execute-task.ts +149 -64
- package/src/orchestrator/failure-mode.ts +5 -3
- package/src/orchestrator/framed-output.ts +17 -3
- package/src/orchestrator/index.ts +11 -0
- package/src/orchestrator/logger.ts +57 -6
- package/src/orchestrator/metrics.ts +65 -0
- package/src/orchestrator/options.ts +12 -0
- package/src/orchestrator/output-log.ts +14 -1
- package/src/orchestrator/path-links.ts +79 -0
- package/src/orchestrator/plan.ts +20 -3
- package/src/orchestrator/plugin.ts +7 -0
- package/src/orchestrator/prepare.ts +14 -4
- package/src/orchestrator/run-artifacts.ts +18 -2
- package/src/orchestrator/run-failures.ts +192 -0
- package/src/orchestrator/run-records.ts +10 -8
- package/src/orchestrator/run.ts +87 -22
- package/src/orchestrator/terminal-signals.ts +16 -0
- package/src/orchestrator/upstream.ts +65 -10
- package/src/util/errors.ts +55 -1
- package/src/util/index.ts +3 -0
- package/src/util/secret-mask.ts +13 -1
- package/src/workspace/workspace.ts +1 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "vx run --summarize",
|
|
4
|
+
"description": "Every task's outcome in one run: <cacheDir>/runs/<runId>.json, or the --summarize=<path> file.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"runId",
|
|
9
|
+
"ok",
|
|
10
|
+
"exitCode",
|
|
11
|
+
"startedAt",
|
|
12
|
+
"endedAt",
|
|
13
|
+
"totalMs",
|
|
14
|
+
"savedMs",
|
|
15
|
+
"tasks",
|
|
16
|
+
"aborted",
|
|
17
|
+
"summary"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"runId": { "type": "string" },
|
|
21
|
+
"ok": { "type": "boolean", "description": "the run's verdict: exitCode is 0" },
|
|
22
|
+
"exitCode": { "type": "integer" },
|
|
23
|
+
"startedAt": { "type": "string", "description": "ISO 8601" },
|
|
24
|
+
"endedAt": { "type": "string", "description": "ISO 8601" },
|
|
25
|
+
"totalMs": { "type": "number" },
|
|
26
|
+
"savedMs": {
|
|
27
|
+
"type": "number",
|
|
28
|
+
"description": "what the cache hits skipped: the sum of their storedDurationMs"
|
|
29
|
+
},
|
|
30
|
+
"tasks": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"description": "every counted task: not a group, not aborted",
|
|
33
|
+
"items": { "$ref": "#/$defs/task" }
|
|
34
|
+
},
|
|
35
|
+
"aborted": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"description": "tasks a shutdown signal killed; in no bucket and no total",
|
|
38
|
+
"items": { "$ref": "#/$defs/task" }
|
|
39
|
+
},
|
|
40
|
+
"summary": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"required": [
|
|
44
|
+
"successful",
|
|
45
|
+
"failed",
|
|
46
|
+
"skipped",
|
|
47
|
+
"cachedLocal",
|
|
48
|
+
"restoredLocal",
|
|
49
|
+
"restoredRemote",
|
|
50
|
+
"upToDate",
|
|
51
|
+
"cachedRemote",
|
|
52
|
+
"aborted",
|
|
53
|
+
"total"
|
|
54
|
+
],
|
|
55
|
+
"properties": {
|
|
56
|
+
"successful": { "type": "integer" },
|
|
57
|
+
"failed": { "type": "integer" },
|
|
58
|
+
"skipped": { "type": "integer" },
|
|
59
|
+
"cachedLocal": { "type": "integer" },
|
|
60
|
+
"restoredLocal": { "type": "integer" },
|
|
61
|
+
"restoredRemote": { "type": "integer" },
|
|
62
|
+
"upToDate": { "type": "integer" },
|
|
63
|
+
"cachedRemote": { "type": "integer" },
|
|
64
|
+
"aborted": { "type": "integer" },
|
|
65
|
+
"total": { "type": "integer" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"$defs": {
|
|
70
|
+
"task": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"required": ["id", "project", "task", "status", "exitCode", "durationMs", "hash"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"id": { "type": "string" },
|
|
76
|
+
"project": { "type": "string" },
|
|
77
|
+
"task": { "type": "string" },
|
|
78
|
+
"status": {
|
|
79
|
+
"enum": ["success", "cache-hit", "cache-hit-remote", "failed", "skipped", "aborted"]
|
|
80
|
+
},
|
|
81
|
+
"exitCode": { "type": "integer" },
|
|
82
|
+
"durationMs": { "type": "number" },
|
|
83
|
+
"hash": { "type": ["string", "null"] },
|
|
84
|
+
"restored": {
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"description": "on a hit only: true when outputs were restored, false when they were already up to date"
|
|
87
|
+
},
|
|
88
|
+
"noCache": {
|
|
89
|
+
"const": true,
|
|
90
|
+
"description": "the task declares no cache block: it could never hit"
|
|
91
|
+
},
|
|
92
|
+
"flaky": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"description": "this key both passed and failed on record, or the task needed a retry",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["passes", "failures", "attempts"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"passes": { "type": "integer" },
|
|
99
|
+
"failures": { "type": "integer" },
|
|
100
|
+
"attempts": { "type": "integer" }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"cpuMs": { "type": "number" },
|
|
104
|
+
"peakRssBytes": { "type": "integer" },
|
|
105
|
+
"storedDurationMs": {
|
|
106
|
+
"type": "number",
|
|
107
|
+
"description": "a hit: how long the producing execution ran, what the hit skipped"
|
|
108
|
+
},
|
|
109
|
+
"storedCpuMs": {
|
|
110
|
+
"type": "number",
|
|
111
|
+
"description": "a hit: what the producing execution used"
|
|
112
|
+
},
|
|
113
|
+
"storedPeakRssBytes": { "type": "integer" },
|
|
114
|
+
"admissionHeldMs": {
|
|
115
|
+
"type": "number",
|
|
116
|
+
"description": "an admit policy held it with a worker free"
|
|
117
|
+
},
|
|
118
|
+
"blockedBy": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"description": "a skipped task: the failed task at the root of what blocked it"
|
|
121
|
+
},
|
|
122
|
+
"timedOut": { "const": true },
|
|
123
|
+
"sandboxViolations": { "type": "integer" },
|
|
124
|
+
"notReady": { "enum": ["timeout", "exited", "spawn"] },
|
|
125
|
+
"wallclockStartNs": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"description": "decimal nanoseconds from the run's start"
|
|
128
|
+
},
|
|
129
|
+
"wallclockEndNs": { "type": "string" }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
package/schemas/why.json
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "vx why --format json",
|
|
4
|
+
"description": "A task's run against its previous run. On history older than run ids, `why` and `diff` are null and `explanation` carries the latest cache entry.",
|
|
5
|
+
"oneOf": [
|
|
6
|
+
{
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": ["taskId", "runId", "why", "diff", "roots"],
|
|
10
|
+
"properties": {
|
|
11
|
+
"taskId": { "type": "string" },
|
|
12
|
+
"runId": { "type": "string" },
|
|
13
|
+
"why": { "$ref": "#/$defs/why" },
|
|
14
|
+
"diff": { "$ref": "#/$defs/diff" },
|
|
15
|
+
"roots": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"description": "The tasks under moved upstreams whose own key components moved in this run, each with the chain from the asked task down to it.",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"additionalProperties": false,
|
|
21
|
+
"required": ["chain", "entries"],
|
|
22
|
+
"properties": {
|
|
23
|
+
"chain": { "type": "array", "items": { "type": "string" } },
|
|
24
|
+
"entries": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["kind", "name", "change", "before", "after"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"kind": { "type": "string" },
|
|
32
|
+
"name": { "type": "string" },
|
|
33
|
+
"change": { "enum": ["added", "removed", "changed"] },
|
|
34
|
+
"before": { "type": ["string", "null"] },
|
|
35
|
+
"after": { "type": ["string", "null"] }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"required": ["taskId", "why", "diff", "explanation"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"taskId": { "type": "string" },
|
|
50
|
+
"why": { "type": "null" },
|
|
51
|
+
"diff": { "type": "null" },
|
|
52
|
+
"explanation": { "$ref": "#/$defs/explanation" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"$defs": {
|
|
57
|
+
"why": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["runId", "taskId", "found", "thisRun", "previousRun", "hashChanged", "note"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"runId": { "type": "string" },
|
|
63
|
+
"taskId": { "type": "string" },
|
|
64
|
+
"found": { "const": true },
|
|
65
|
+
"thisRun": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"additionalProperties": false,
|
|
68
|
+
"required": ["hash", "status", "cacheHit", "restored", "cached", "startedAt"],
|
|
69
|
+
"properties": {
|
|
70
|
+
"hash": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "empty for a task that never ran (skipped)"
|
|
73
|
+
},
|
|
74
|
+
"status": { "type": "string" },
|
|
75
|
+
"cacheHit": { "type": ["boolean", "null"] },
|
|
76
|
+
"restored": {
|
|
77
|
+
"type": ["boolean", "null"],
|
|
78
|
+
"description": "on a hit: true when outputs were restored, false when up-to-date"
|
|
79
|
+
},
|
|
80
|
+
"cached": { "type": ["boolean", "null"] },
|
|
81
|
+
"startedAt": { "type": "integer", "description": "epoch ms" }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"previousRun": {
|
|
85
|
+
"type": ["object", "null"],
|
|
86
|
+
"description": "null on the task's first recorded run",
|
|
87
|
+
"additionalProperties": false,
|
|
88
|
+
"required": ["hash", "status", "cacheHit", "restored", "startedAt"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"hash": { "type": "string" },
|
|
91
|
+
"status": { "type": "string" },
|
|
92
|
+
"cacheHit": { "type": ["boolean", "null"] },
|
|
93
|
+
"restored": {
|
|
94
|
+
"type": ["boolean", "null"],
|
|
95
|
+
"description": "on a hit: true when outputs were restored, false when up-to-date"
|
|
96
|
+
},
|
|
97
|
+
"startedAt": { "type": "integer" }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"hashChanged": {
|
|
101
|
+
"type": ["boolean", "null"],
|
|
102
|
+
"description": "null when either run has no key"
|
|
103
|
+
},
|
|
104
|
+
"note": { "type": "string" }
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"diff": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"additionalProperties": false,
|
|
110
|
+
"required": [
|
|
111
|
+
"runId",
|
|
112
|
+
"taskId",
|
|
113
|
+
"found",
|
|
114
|
+
"previousRunId",
|
|
115
|
+
"entries",
|
|
116
|
+
"unchangedCount",
|
|
117
|
+
"note"
|
|
118
|
+
],
|
|
119
|
+
"properties": {
|
|
120
|
+
"runId": { "type": "string" },
|
|
121
|
+
"taskId": { "type": "string" },
|
|
122
|
+
"found": { "type": "boolean" },
|
|
123
|
+
"previousRunId": { "type": ["string", "null"] },
|
|
124
|
+
"entries": {
|
|
125
|
+
"type": "array",
|
|
126
|
+
"items": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"required": ["kind", "name", "change", "before", "after"],
|
|
130
|
+
"properties": {
|
|
131
|
+
"kind": { "type": "string", "description": "files, env, runtime, upstream, …" },
|
|
132
|
+
"name": { "type": "string" },
|
|
133
|
+
"change": { "enum": ["added", "removed", "changed"] },
|
|
134
|
+
"before": { "type": ["string", "null"] },
|
|
135
|
+
"after": { "type": ["string", "null"] }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"unchangedCount": { "type": "integer" },
|
|
140
|
+
"note": { "type": "string" }
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"explanation": {
|
|
144
|
+
"type": "object",
|
|
145
|
+
"additionalProperties": false,
|
|
146
|
+
"required": ["taskId", "project", "task", "latestEntry", "note"],
|
|
147
|
+
"properties": {
|
|
148
|
+
"taskId": { "type": "string" },
|
|
149
|
+
"project": { "type": "string" },
|
|
150
|
+
"task": { "type": "string" },
|
|
151
|
+
"latestEntry": {
|
|
152
|
+
"type": ["object", "null"],
|
|
153
|
+
"additionalProperties": false,
|
|
154
|
+
"required": ["hash", "command", "exitCode", "durationMs", "sizeBytes", "createdAt"],
|
|
155
|
+
"properties": {
|
|
156
|
+
"hash": { "type": "string" },
|
|
157
|
+
"command": { "type": "string" },
|
|
158
|
+
"exitCode": { "type": "integer" },
|
|
159
|
+
"durationMs": { "type": "number" },
|
|
160
|
+
"sizeBytes": { "type": "integer" },
|
|
161
|
+
"createdAt": { "type": "integer" }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"note": { "type": "string" }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vx
|
|
3
|
+
description: Run, inspect and debug tasks in a vx monorepo (vx.config.ts, vx.workspace.ts). Use to build or test projects, find what a change affects, read why a task failed or re-ran, and query the cache as JSON.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# vx
|
|
7
|
+
|
|
8
|
+
vx is the workspace's task runner and build cache. Run tasks through it, not
|
|
9
|
+
through package scripts, so results are cached and ordered by their
|
|
10
|
+
dependencies.
|
|
11
|
+
|
|
12
|
+
## Run
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
vx run build --all # a task in every project that declares it
|
|
16
|
+
vx run test --affected=origin/main # only what the diff since origin/main reaches
|
|
17
|
+
vx run app#build # one project's task (and what it depends on)
|
|
18
|
+
vx run build --all --format json # the result as JSON on stdout; logs go to stderr
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The exit code is the verdict: `0` every task passed or hit the cache, `1` a
|
|
22
|
+
task failed or was skipped, or the command was refused. `--format json`
|
|
23
|
+
prints `schemas/summary.json` (`runId`, `ok`, `exitCode`, a row per task).
|
|
24
|
+
Without a TTY a missing task name never prompts: vx exits 1 and lists the
|
|
25
|
+
tasks it found.
|
|
26
|
+
|
|
27
|
+
## Look before running
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
vx show --format json # projects, their tasks and dependencies
|
|
31
|
+
vx run test --affected=origin/main --dry=json # what would run, cache hit or miss, and why
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Under `--affected`, each kept task in `--dry=json` carries `affected`:
|
|
35
|
+
`{ kind, file?, project?, via? }`, where `kind` is `input` (a changed file
|
|
36
|
+
is one of its inputs), `project`, `package`, `named` or `selected`, and
|
|
37
|
+
`via` is the `dependsOn` chain that carried the change.
|
|
38
|
+
|
|
39
|
+
## When a task fails
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
vx last --failed --format json # the last failed run; a failed row has output and locations
|
|
43
|
+
vx last --log app#build # one task's output: a failure's, or its cached log
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
A failed row's `output` is the task's output as plain text (secrets masked)
|
|
47
|
+
and `locations` the files it names as `{ file, line?, col? }`. Read those
|
|
48
|
+
files, fix, and re-run the same command: cached tasks that passed replay from the cache.
|
|
49
|
+
|
|
50
|
+
## When a task re-ran unexpectedly
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
vx why app#build --format json # which input changed, down to the root cause
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Workspace health
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
vx info --format json # vx and Bun versions, projects, config errors, plugins, cache
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## MCP
|
|
63
|
+
|
|
64
|
+
When `@vzn/vx-mcp` is a plugin in `vx.workspace.ts`, `vx mcp` serves the
|
|
65
|
+
same facts and runs tasks. Adoption does not add it: install
|
|
66
|
+
`@vzn/vx-mcp` and put `mcp()` (from `'@vzn/vx-mcp'`) in `plugins`. Its tools: `listTasks`, `getCacheStats`,
|
|
67
|
+
`getRunHistory`, `explainCacheKey`, `whyDidThisRerun`, `getFailures`, `getTaskLog`, `getConfig`,
|
|
68
|
+
`getWorkspaceInfo`, `runTasks`, `planTasks`. `runTasks` answers what
|
|
69
|
+
`vx run <tasks> --format json` prints, `planTasks` what
|
|
70
|
+
`vx run <tasks> --dry=json` prints; the others only read.
|
|
71
|
+
|
|
72
|
+
## Rules
|
|
73
|
+
|
|
74
|
+
- Never delete or edit the cache by hand; vx repairs it itself.
|
|
75
|
+
- A sandboxed task that fails on a denied path: grant that one path in
|
|
76
|
+
`exec.sandbox.allow`. Never remove `sandbox` or widen it further.
|
|
77
|
+
- A cached task declares its inputs (`cache.inputs.files`); if a change
|
|
78
|
+
does not re-run a task, the file is not one of its inputs. Fix the
|
|
79
|
+
config, do not force.
|
|
80
|
+
- Every JSON shape is a JSON Schema in `node_modules/@vzn/vx/schemas/`.
|
|
81
|
+
- Docs as markdown: https://vznjs.github.io/vx/llms.txt (its index).
|
|
82
|
+
When that host is unreachable, the same pages are
|
|
83
|
+
`https://raw.githubusercontent.com/vznjs/vx/main/packages/vx-docs/src/content/docs/<page>.md`:
|
|
84
|
+
`quickstart`, `guides/migrate`, `guides/configure`, `guides/agents`,
|
|
85
|
+
`guides/troubleshooting`, `guides/ci`, `guides/plugins`.
|
package/src/bin.ts
CHANGED
|
@@ -32,12 +32,15 @@ async function main(): Promise<void> {
|
|
|
32
32
|
} catch (err) {
|
|
33
33
|
const {
|
|
34
34
|
configErrorFrame,
|
|
35
|
+
errorCode,
|
|
36
|
+
errorDocument,
|
|
35
37
|
fsRefusalHint,
|
|
36
38
|
isFsRefusal,
|
|
37
39
|
isOutOfFds,
|
|
38
40
|
isUserError,
|
|
39
41
|
maskedLine,
|
|
40
42
|
OUT_OF_FDS_HINT,
|
|
43
|
+
wantsJson,
|
|
41
44
|
} = await import('./util/index.js')
|
|
42
45
|
// A plugin's failure quotes what it was told (a remote's reply, a
|
|
43
46
|
// header), and a config's own throw is its author's text: a secret in
|
|
@@ -64,6 +67,12 @@ async function main(): Promise<void> {
|
|
|
64
67
|
const message = err instanceof Error ? (err.stack ?? err.message) : String(err)
|
|
65
68
|
say(`vx: ${message}\n`)
|
|
66
69
|
}
|
|
70
|
+
// A verb asked for JSON answers JSON on a refusal too: an agent reading
|
|
71
|
+
// stdout got nothing to parse, and only prose on stderr to branch on.
|
|
72
|
+
if (wantsJson(argv)) {
|
|
73
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
74
|
+
process.stdout.write(errorDocument(errorCode(err), maskedLine(message)))
|
|
75
|
+
}
|
|
67
76
|
// Same reason as the success path above: a large stderr is truncated by
|
|
68
77
|
// `process.exit` too, and an error message cut in half is the one a
|
|
69
78
|
// reader most needs whole.
|