@vzn/vx 0.0.632 → 0.0.634
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 +14 -10
- package/docs/caching.md +2250 -0
- package/docs/cli.md +3283 -0
- package/docs/execution.md +708 -0
- package/docs/features.md +174 -0
- package/docs/patterns.md +219 -0
- package/docs/schema.md +2151 -0
- package/docs/security.md +209 -0
- package/package.json +12 -5
- package/schemas/docs.json +28 -0
- package/schemas/error.json +5 -1
- package/schemas/init.json +57 -0
- package/schemas/lock.json +21 -0
- package/schemas/plan.json +4 -0
- package/skills/vx/SKILL.md +13 -3
- package/src/cache/cache.ts +556 -122
- package/src/cache/chained-cache.ts +9 -0
- package/src/cache/inputs.ts +71 -30
- package/src/cache/layer.ts +8 -4
- package/src/cache/layered-cache.ts +4 -0
- package/src/cache/schema.ts +42 -16
- package/src/cli/docs-corpus.ts +21 -0
- package/src/cli/docs-text.d.ts +5 -0
- package/src/cli/docs.ts +173 -0
- package/src/cli/help.ts +14 -3
- package/src/cli/index.ts +2 -0
- package/src/cli/init.ts +44 -8
- package/src/cli/lock.ts +43 -15
- package/src/cli/plan-format.ts +3 -1
- package/src/cli/run.ts +8 -2
- package/src/cli/upgrade.ts +18 -6
- package/src/config.ts +9 -0
- package/src/graph/priorities.ts +72 -6
- package/src/graph/scheduler.ts +17 -4
- package/src/graph/task-graph.ts +56 -18
- package/src/orchestrator/metrics.ts +11 -4
- package/src/orchestrator/miss-reason.ts +3 -5
- package/src/orchestrator/miss-save.ts +6 -5
- package/src/orchestrator/projects.ts +2 -2
- package/src/orchestrator/remote-prefetch.ts +8 -2
- package/src/orchestrator/run.ts +13 -1
- package/src/orchestrator/task-hash.ts +4 -4
- package/src/util/errors.ts +9 -2
- package/src/util/paths.ts +32 -0
- package/src/util/verbs.ts +1 -0
- package/src/workspace/config-schema.ts +26 -8
- package/src/workspace/lockfile.ts +29 -2
- package/src/workspace/migration.ts +30 -3
package/docs/security.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Security model
|
|
2
|
+
|
|
3
|
+
What vx trusts, what it checks, what the sandbox stops and what it does
|
|
4
|
+
not, and how to report a problem. Report a vulnerability privately
|
|
5
|
+
through GitHub's private vulnerability reporting on
|
|
6
|
+
[vznjs/vx](https://github.com/vznjs/vx/security) (Security → Report a
|
|
7
|
+
vulnerability), never in a public issue.
|
|
8
|
+
|
|
9
|
+
## What vx trusts
|
|
10
|
+
|
|
11
|
+
- **Your configs.** `vx.config.ts` and `vx.workspace.ts` are programs vx
|
|
12
|
+
runs in its own process. A config can do anything you can: vx
|
|
13
|
+
validates what it returns, not what it does. What a config imports
|
|
14
|
+
must already be installed: vx runs Bun with `--no-install`, so an
|
|
15
|
+
import no `node_modules` provides is refused, never fetched from the
|
|
16
|
+
registry (L-22).
|
|
17
|
+
- **Your plugins.** A plugin is code you imported. It runs in vx's
|
|
18
|
+
process with a config's reach; vx checks the shape of what it hands
|
|
19
|
+
back and isolates a telemetry plugin's failures, nothing more.
|
|
20
|
+
- **Your lockfile and your repo.** vx parses a lockfile (and
|
|
21
|
+
`turbo.json` or an Nx graph when migrating) into keys and refuses one
|
|
22
|
+
it cannot read, naming the file. A lockfile that names another
|
|
23
|
+
install is a different key, never a code path.
|
|
24
|
+
- **Whoever can write your remote cache.** A cache key names the
|
|
25
|
+
inputs, not the bytes, so a store that answers a key with bytes of its
|
|
26
|
+
choosing is trusted as a writer, as it is in every action cache. Use a
|
|
27
|
+
store only your builds write to, or `turboCache()`'s signature key,
|
|
28
|
+
which refuses an artifact a keyholder did not sign. A job you do not
|
|
29
|
+
trust (a fork's pull request) reads at most: `--cache=local:rw,remote:r`,
|
|
30
|
+
or the tools' own `TURBO_REMOTE_CACHE_READ_ONLY`, `TURBO_CACHE` and
|
|
31
|
+
`NX_SKIP_REMOTE_CACHE`, which the cache plugins honour (L-34).
|
|
32
|
+
|
|
33
|
+
## Cache poisoning
|
|
34
|
+
|
|
35
|
+
A pull request that can write the cache the default branch reads can
|
|
36
|
+
plant bytes main later replays (CREEP, CVE-2025-36852). The boundary is
|
|
37
|
+
the credential, enforced by the cache server: the default branch's job
|
|
38
|
+
holds a token that writes the trusted keys, and a pull request's job
|
|
39
|
+
holds one that cannot (read-only, or confined by the server to its own
|
|
40
|
+
namespace), or none.
|
|
41
|
+
|
|
42
|
+
`cacheScope` is the client half. `'pr-123'` reads its own keys,
|
|
43
|
+
then the trusted ones, and writes only its own, which `github()` sets on
|
|
44
|
+
Actions (`pr-<n>`, `ref-<name>`; the default branch stays trusted)
|
|
45
|
+
and `VX_CACHE_SCOPE` sets on any CI.
|
|
46
|
+
It keeps honest runs apart and stops a non-hermetic PR build from
|
|
47
|
+
landing where main reads; it is not a boundary, since the PR's own
|
|
48
|
+
config decides it and any run holding a write credential can write any
|
|
49
|
+
key.
|
|
50
|
+
|
|
51
|
+
A boundary the server holds, with bazel-remote and `@vzn/vx-reapi`: reads
|
|
52
|
+
are open, writes need a password, and only main's push job has it.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
htpasswd -cB /etc/bazel-remote/htpasswd ci # the write credential
|
|
56
|
+
bazel-remote --dir /data --max_size 50 --grpc_address 0.0.0.0:9092 \
|
|
57
|
+
--htpasswd_file /etc/bazel-remote/htpasswd --allow_unauthenticated_reads
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// vx.workspace.ts: the header only where the secret is set
|
|
62
|
+
reapi({
|
|
63
|
+
endpoint: 'grpcs://cache.example.com:9092',
|
|
64
|
+
headers: process.env.CACHE_AUTH ? { authorization: `Basic ${process.env.CACHE_AUTH}` } : {},
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# .github/workflows/ci.yml: CACHE_AUTH (base64 of ci:<password>) is a
|
|
70
|
+
# secret of the `cache-write` environment, whose only deployment branch
|
|
71
|
+
# is main, so no other branch's workflow can read it
|
|
72
|
+
jobs:
|
|
73
|
+
build:
|
|
74
|
+
if: github.event_name != 'push' || github.ref != 'refs/heads/main'
|
|
75
|
+
steps:
|
|
76
|
+
- run: vx run build --all
|
|
77
|
+
build-main:
|
|
78
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
79
|
+
environment: cache-write
|
|
80
|
+
env:
|
|
81
|
+
CACHE_AUTH: ${{ secrets.CACHE_AUTH }}
|
|
82
|
+
steps:
|
|
83
|
+
- run: vx run build --all
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A pull request's job has no `CACHE_AUTH`, so the server refuses its
|
|
87
|
+
writes whatever its config says. Never give the write secret to a
|
|
88
|
+
`pull_request_target`, `issue_comment` or `workflow_run` job that checks
|
|
89
|
+
out a pull request's code: it runs with main's ref and secrets, and
|
|
90
|
+
`github()` scopes it `pr-<n>` only on the client side.
|
|
91
|
+
|
|
92
|
+
## What vx checks on bytes it did not write
|
|
93
|
+
|
|
94
|
+
Every artifact that arrives from a remote, and every one read back from
|
|
95
|
+
the local store:
|
|
96
|
+
|
|
97
|
+
- ends in a CRC-32 of its entries: a byte changed in transit or at rest
|
|
98
|
+
makes the hit a miss, never a replay of the damaged bytes;
|
|
99
|
+
- on arrival, records the cache key it was packed under, and is
|
|
100
|
+
refused under another (a local read-back is not re-checked);
|
|
101
|
+
- on arrival, carries only the files the task declares as outputs;
|
|
102
|
+
- lands each file under the task's project or declared workspace path:
|
|
103
|
+
traversal (`..`, `.`, absolute names) is refused before anything
|
|
104
|
+
lands, a refused archive leaves nothing behind, and links and devices
|
|
105
|
+
are never written; a seeded fuzz of hostile archives holds this;
|
|
106
|
+
- is bounded: a decompression bomb, an oversized header or body, and a
|
|
107
|
+
remote answer larger than asked for are refused as they are read.
|
|
108
|
+
|
|
109
|
+
Remote execution (`@vzn/vx-reapi`) holds each blob to its digest, and
|
|
110
|
+
writes only the server's outputs a declared glob names, inside the
|
|
111
|
+
workspace, through no link that leads out of it.
|
|
112
|
+
|
|
113
|
+
## What the sandbox stops
|
|
114
|
+
|
|
115
|
+
A task with `exec.sandbox` runs where only what it declares exists:
|
|
116
|
+
|
|
117
|
+
- reads of the workspace outside the task's project and grants fail;
|
|
118
|
+
- writes outside its `allow.write` grants fail (declared outputs grant
|
|
119
|
+
none);
|
|
120
|
+
- the network is closed except to the domains granted, one union per
|
|
121
|
+
run: a task granted any domain reaches every domain the run grants;
|
|
122
|
+
with none, it reaches no listener on the host's loopback and no unix
|
|
123
|
+
socket outside the workspace (L-46);
|
|
124
|
+
- its temp directory, port-bridge socket and trace log are its own
|
|
125
|
+
(mode 0700), unreachable from another task and another local user;
|
|
126
|
+
- the host's credential stores (`~/.ssh`, `~/.gnupg`, `~/.aws`,
|
|
127
|
+
`~/.npmrc`, `~/.netrc` and the like; the list is in the schema's
|
|
128
|
+
sandbox section) are unreadable unless `allow.read` names one (L-41);
|
|
129
|
+
- vx's own cache directory, wherever `cacheDir` puts it in the
|
|
130
|
+
workspace, is a wall like `.vx`: a broad grant stops at it, and a write
|
|
131
|
+
grant that would bind it is refused (L-24);
|
|
132
|
+
- a symlinked output must resolve inside its project: vx packs outputs
|
|
133
|
+
outside the sandbox, and a link to another project's file would have
|
|
134
|
+
carried that file into the cache (L-23). An output directory linked out
|
|
135
|
+
of the project refuses the task by name (X-88).
|
|
136
|
+
|
|
137
|
+
An undeclared touch of the task's own files fails the task, and a failed
|
|
138
|
+
task is never cached. See [Sandboxing tasks](https://vznjs.github.io/vx/guides/sandboxing/).
|
|
139
|
+
|
|
140
|
+
## What it does not stop
|
|
141
|
+
|
|
142
|
+
- **A task with no `exec.sandbox`.** It runs with your permissions.
|
|
143
|
+
- **Reads outside the workspace root.** `~/.cache`, `/etc` and the like
|
|
144
|
+
stay readable (tools need them) and fold into no key. The credential
|
|
145
|
+
stores under home (`~/.ssh`, `~/.aws`, `~/.npmrc` and the like) are
|
|
146
|
+
not: a task reads one only when its `allow.read` names it (L-41).
|
|
147
|
+
- **Resource use.** CPU, memory and disk are bounded by timeouts and the
|
|
148
|
+
artifact ceiling, not by the sandbox.
|
|
149
|
+
- **A weaker sandbox where the host cannot nest one**:
|
|
150
|
+
`weakerWhenNested` in a container, a task that itself sandboxes on
|
|
151
|
+
macOS. `vx info` says what the host supports.
|
|
152
|
+
|
|
153
|
+
## Agents (`vx mcp`)
|
|
154
|
+
|
|
155
|
+
An agent connected to `vx mcp` can do what `vx run` can do, nothing more.
|
|
156
|
+
Every tool but `runTasks` only reads. `runTasks` runs `vx run --format json`
|
|
157
|
+
as a child process: the agent names tasks and sets `all`, `force`,
|
|
158
|
+
`filter` and `affected`, and nothing else. A task name that starts with
|
|
159
|
+
`-` is refused, a filter goes as one `--filter=` value, and a ref reaches
|
|
160
|
+
git after `--end-of-options`, so no argument becomes a flag. The commands
|
|
161
|
+
that run are the ones your configs declare, from the workspace root, with
|
|
162
|
+
the environment `vx mcp` started with, filtered by each task's `exec.env`
|
|
163
|
+
and held by its `exec.sandbox`. `force` costs a rerun, not a wrong entry:
|
|
164
|
+
the key stays the same. What reaches the agent (summaries, refusals,
|
|
165
|
+
`getFailures` output) is masked as described under Secrets.
|
|
166
|
+
`mcp({ run: false })` takes `runTasks` away, and `mcp({ run: ['test'] })`
|
|
167
|
+
limits it to those task names. An agent that
|
|
168
|
+
can edit `vx.config.ts` can run anything, as you can, because configs are
|
|
169
|
+
code vx trusts.
|
|
170
|
+
|
|
171
|
+
## Secrets
|
|
172
|
+
|
|
173
|
+
The value, of 6 characters or more, of a variable whose name holds
|
|
174
|
+
`TOKEN`, `SECRET`, `KEY`, `PASSWORD`, `PASSWD` or `CREDENTIAL` (not one
|
|
175
|
+
ending `_FILE`, `_PATH` or `_DIR`, nor `GIT_CONFIG_KEY_<n>`), or that a
|
|
176
|
+
task lists in `exec.env.secret`, and the password of a URL
|
|
177
|
+
(`scheme://user:<password>@`) in any variable's value, is printed as `***` wherever vx shows, stores or
|
|
178
|
+
exports it: task output, the stdout a hit replays, commands, an
|
|
179
|
+
executor's error, telemetry,
|
|
180
|
+
`vx show` and `vx mcp`'s `listTasks` (L-26). Values in the run history are digests under a per-store
|
|
181
|
+
salt, and what follows `--` on the command line reaches telemetry as a
|
|
182
|
+
count, not a quote, a secret before it masked (L-44). A remote executor (`@vzn/vx-reapi`) receives a
|
|
183
|
+
task's `exec.env.define` values and the `cache.inputs.env` values its
|
|
184
|
+
local child gets, never `passThrough`; those values sit unmasked in the
|
|
185
|
+
action's Command, which the remote stores in its CAS, as Bazel's
|
|
186
|
+
`--action_env` does, so a secret a remote task needs is trusted to that
|
|
187
|
+
remote. `vx lock` refuses to write a lock holding a secret value: the lock is
|
|
188
|
+
committed (L-42).
|
|
189
|
+
|
|
190
|
+
Masking covers the text vx shows and stores, not a task's outputs: they
|
|
191
|
+
are cached and uploaded as the task wrote them, so a build that bakes a
|
|
192
|
+
secret into a file (a server bundle reading `process.env.API_KEY`) hands
|
|
193
|
+
it to every reader of the remote cache. Keep secrets out of cached
|
|
194
|
+
outputs, or give that task no `cache`. vx does not scan outputs for
|
|
195
|
+
secret values: a name-based match would refuse public `*_KEY` values too.
|
|
196
|
+
|
|
197
|
+
## Releases
|
|
198
|
+
|
|
199
|
+
Release binaries carry a build-provenance attestation
|
|
200
|
+
(`gh attestation verify vx-<target> --repo vznjs/vx`); `vx upgrade`
|
|
201
|
+
checks each download's SHA-256 before it replaces anything and stages it in a fresh directory, never a name another user could plant a link at, which proves
|
|
202
|
+
the bytes arrived intact, not who built them: the digest comes from the
|
|
203
|
+
release that serves the asset, and no signature is checked (owner,
|
|
204
|
+
2026-10-03: no release signing key for now); npm packages
|
|
205
|
+
publish with provenance; every third-party action in CI runs from a
|
|
206
|
+
full commit SHA; and a workflow hands an event's or a dispatcher's value
|
|
207
|
+
to a script through `env`, never pasted in (a release tag is checked as a
|
|
208
|
+
version first); every workflow names its token's permissions, and CI's
|
|
209
|
+
is read-only. A test holds each rule.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.634",
|
|
4
4
|
"description": "The fastest task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.634",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.634",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.634",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.634"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -35,6 +35,13 @@
|
|
|
35
35
|
"config",
|
|
36
36
|
"schemas",
|
|
37
37
|
"skills",
|
|
38
|
+
"docs/cli.md",
|
|
39
|
+
"docs/schema.md",
|
|
40
|
+
"docs/caching.md",
|
|
41
|
+
"docs/execution.md",
|
|
42
|
+
"docs/patterns.md",
|
|
43
|
+
"docs/security.md",
|
|
44
|
+
"docs/features.md",
|
|
38
45
|
"launcher.cjs",
|
|
39
46
|
"README.md",
|
|
40
47
|
"LICENSE"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "vx docs <query> --format json",
|
|
4
|
+
"description": "The reference sections holding every word of the query, best first.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["query", "hits"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"query": { "type": "string", "description": "the query's words, lowercased" },
|
|
10
|
+
"hits": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"required": ["page", "heading", "url", "text"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"page": { "type": "string", "description": "the page: cli, schema, caching …" },
|
|
18
|
+
"heading": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "the section's heading; empty for a page's opening"
|
|
21
|
+
},
|
|
22
|
+
"url": { "type": "string", "description": "the section on the site" },
|
|
23
|
+
"text": { "type": "string", "description": "the section's markdown, heading included" }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/schemas/error.json
CHANGED
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
"type": "string",
|
|
17
17
|
"description": "Stable; branch on it. VX_E_USAGE, VX_E_UNKNOWN_COMMAND, VX_E_UNKNOWN_TASK, VX_E_UNKNOWN_PROJECT, VX_E_NO_HISTORY, VX_E_AMBIGUOUS, VX_E_CONFIG, VX_E_CYCLE, VX_E_NO_WORKSPACE, VX_E_CACHE_VERSION, VX_E_REFUSED, VX_E_FS, VX_E_FDS, VX_E_INTERNAL."
|
|
18
18
|
},
|
|
19
|
-
"message": { "type": "string", "description": "The refusal in prose; may change." }
|
|
19
|
+
"message": { "type": "string", "description": "The refusal in prose; may change." },
|
|
20
|
+
"docs": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "For a core code (VX_E_…): its section of the CLI reference, which says what to do. A plugin's code has none."
|
|
23
|
+
}
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "vx init --dry --format json",
|
|
4
|
+
"description": "What vx init (or vx-migrate) would write, nothing written. In a Turbo or Nx repo the plan is vx-migrate's.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["dry", "source", "files", "kept", "replaced", "todos", "notes", "next"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"dry": { "const": true },
|
|
10
|
+
"source": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "what the plan maps: package.json scripts, turbo.json, nx graph, lerna.json …"
|
|
13
|
+
},
|
|
14
|
+
"files": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"description": "each file the run would write, root-relative, with its whole text",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"required": ["path", "contents"],
|
|
21
|
+
"properties": { "path": { "type": "string" }, "contents": { "type": "string" } }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"kept": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": { "type": "string" },
|
|
27
|
+
"description": "configs already here that the run leaves as they are"
|
|
28
|
+
},
|
|
29
|
+
"replaced": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"items": { "type": "string" },
|
|
32
|
+
"description": "files of another extension the run would delete for the new ones"
|
|
33
|
+
},
|
|
34
|
+
"todos": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"description": "what the mapping could not settle, one reason with the tasks it names",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["reason", "tasks"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"reason": { "type": "string" },
|
|
43
|
+
"tasks": { "type": "array", "items": { "type": "string" }, "description": "project#task" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"notes": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"items": { "type": "string" },
|
|
50
|
+
"description": "the report's notes"
|
|
51
|
+
},
|
|
52
|
+
"next": {
|
|
53
|
+
"type": ["string", "null"],
|
|
54
|
+
"description": "the command to type after the real run; null where the plan names none"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "vx lock --check --format json",
|
|
4
|
+
"description": "The lock audit: exit 0 when upToDate, else 1 with each drift in problems.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["upToDate", "audited", "notAudited", "problems"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"upToDate": { "type": "boolean" },
|
|
10
|
+
"audited": { "type": "integer", "description": "projects with a vx.config, each audited" },
|
|
11
|
+
"notAudited": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"description": "projects without a vx.config: nothing to freeze, never audited"
|
|
14
|
+
},
|
|
15
|
+
"problems": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": { "type": "string" },
|
|
18
|
+
"description": "one line per drift, as the plain audit prints it on stderr; a missing lock is one"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/schemas/plan.json
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["tasks"],
|
|
8
8
|
"properties": {
|
|
9
|
+
"affectedBase": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "under --affected: the ref the diff was taken against (a bare --affected guesses it: origin/HEAD, a trunk branch, else HEAD~1)"
|
|
12
|
+
},
|
|
9
13
|
"tasks": {
|
|
10
14
|
"type": "array",
|
|
11
15
|
"items": {
|
package/skills/vx/SKILL.md
CHANGED
|
@@ -21,6 +21,9 @@ vx run build --all --format json # the result as JSON on stdout; logs go to s
|
|
|
21
21
|
The exit code is the verdict: `0` every task passed or hit the cache, `1` a
|
|
22
22
|
task failed or was skipped, or the command was refused. `--format json`
|
|
23
23
|
prints `schemas/summary.json` (`runId`, `ok`, `exitCode`, a row per task).
|
|
24
|
+
A refusal under `--format json` is one line on stdout,
|
|
25
|
+
`{"ok":false,"error":{"code","message","docs"}}`: branch on `code`;
|
|
26
|
+
`docs` (or `vx docs <code>` offline) says what to do.
|
|
24
27
|
Without a TTY a missing task name never prompts: vx exits 1 and lists the
|
|
25
28
|
tasks it found.
|
|
26
29
|
|
|
@@ -31,7 +34,10 @@ vx show --format json # projects, their tasks and depen
|
|
|
31
34
|
vx run test --affected=origin/main --dry=json # what would run, cache hit or miss, and why
|
|
32
35
|
```
|
|
33
36
|
|
|
34
|
-
Under `--affected`,
|
|
37
|
+
Under `--affected`, `--dry=json` states `affectedBase` (the ref it diffed
|
|
38
|
+
against; name one, `--affected=origin/main`, rather than trust the guess)
|
|
39
|
+
and each task you asked for carries `affected` (a dependency it pulls in
|
|
40
|
+
does not):
|
|
35
41
|
`{ kind, file?, project?, via? }`, where `kind` is `input` (a changed file
|
|
36
42
|
is one of its inputs), `project`, `package`, `named` or `selected`, and
|
|
37
43
|
`via` is the `dependsOn` chain that carried the change.
|
|
@@ -64,10 +70,11 @@ vx info --format json # vx and Bun versions, projects, config errors, plugins,
|
|
|
64
70
|
When `@vzn/vx-mcp` is a plugin in `vx.workspace.ts`, `vx mcp` serves the
|
|
65
71
|
same facts and runs tasks. Adoption does not add it: install
|
|
66
72
|
`@vzn/vx-mcp` and put `mcp()` (from `'@vzn/vx-mcp'`) in `plugins`. Its tools: `listTasks`, `getCacheStats`,
|
|
67
|
-
`getRunHistory`, `explainCacheKey`, `whyDidThisRerun`, `getFailures`, `getTaskLog`, `getConfig`,
|
|
73
|
+
`getRunHistory`, `explainCacheKey`, `whyDidThisRerun`, `getFailures`, `getTaskLog`, `getConfig`, `checkLock`, `pruneCache`, `planInit`, `searchDocs`,
|
|
68
74
|
`getWorkspaceInfo`, `runTasks`, `planTasks`. `runTasks` answers what
|
|
69
75
|
`vx run <tasks> --format json` prints, `planTasks` what
|
|
70
|
-
`vx run <tasks> --dry=json` prints
|
|
76
|
+
`vx run <tasks> --dry=json` prints, `pruneCache` evicts only with
|
|
77
|
+
`dryRun: false`; the others only read.
|
|
71
78
|
|
|
72
79
|
## Rules
|
|
73
80
|
|
|
@@ -78,6 +85,9 @@ same facts and runs tasks. Adoption does not add it: install
|
|
|
78
85
|
does not re-run a task, the file is not one of its inputs. Fix the
|
|
79
86
|
config, do not force.
|
|
80
87
|
- Every JSON shape is a JSON Schema in `node_modules/@vzn/vx/schemas/`.
|
|
88
|
+
- Offline reference: `vx docs <words> --format json` searches the CLI, config
|
|
89
|
+
and cache pages that ship with vx; the sections holding every word print
|
|
90
|
+
whole.
|
|
81
91
|
- Docs as markdown: https://vznjs.github.io/vx/llms.txt (its index).
|
|
82
92
|
When that host is unreachable, the same pages are
|
|
83
93
|
`https://raw.githubusercontent.com/vznjs/vx/main/packages/vx-docs/src/content/docs/<page>.md`:
|