@vzn/vx 0.0.631 → 0.0.633

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +21 -18
  2. package/docs/caching.md +2247 -0
  3. package/docs/cli.md +3214 -0
  4. package/docs/execution.md +708 -0
  5. package/docs/features.md +172 -0
  6. package/docs/patterns.md +219 -0
  7. package/docs/schema.md +2137 -0
  8. package/docs/security.md +209 -0
  9. package/package.json +14 -5
  10. package/schemas/cache.json +24 -0
  11. package/schemas/docs.json +28 -0
  12. package/schemas/error.json +23 -0
  13. package/schemas/info.json +147 -0
  14. package/schemas/init.json +57 -0
  15. package/schemas/last.json +188 -0
  16. package/schemas/lock.json +21 -0
  17. package/schemas/plan.json +88 -0
  18. package/schemas/show.json +124 -0
  19. package/schemas/summary.json +133 -0
  20. package/schemas/why.json +168 -0
  21. package/skills/vx/SKILL.md +89 -0
  22. package/src/bin.ts +9 -0
  23. package/src/cache/archive.ts +153 -51
  24. package/src/cache/cache.ts +542 -137
  25. package/src/cache/chained-cache.ts +9 -0
  26. package/src/cache/inputs.ts +5 -0
  27. package/src/cache/layer.ts +10 -5
  28. package/src/cache/layered-cache.ts +4 -0
  29. package/src/cache/schema.ts +44 -17
  30. package/src/cache/tar-stream.ts +204 -66
  31. package/src/cache/zstd.ts +1 -1
  32. package/src/cli/cache.ts +14 -14
  33. package/src/cli/docs-corpus.ts +21 -0
  34. package/src/cli/docs-text.d.ts +5 -0
  35. package/src/cli/docs.ts +167 -0
  36. package/src/cli/foreign-flags.ts +1 -1
  37. package/src/cli/help.ts +42 -7
  38. package/src/cli/index.ts +25 -11
  39. package/src/cli/info.ts +38 -9
  40. package/src/cli/init.ts +131 -24
  41. package/src/cli/last.ts +54 -3
  42. package/src/cli/lock.ts +43 -15
  43. package/src/cli/plan-format.ts +23 -1
  44. package/src/cli/plugin-commands.ts +3 -0
  45. package/src/cli/plugin-templates.ts +1 -1
  46. package/src/cli/run.ts +40 -21
  47. package/src/cli/show.ts +10 -7
  48. package/src/cli/upgrade.ts +19 -7
  49. package/src/cli/watch.ts +7 -2
  50. package/src/cli/why.ts +14 -40
  51. package/src/exec/runner.ts +27 -10
  52. package/src/graph/scheduler.ts +36 -0
  53. package/src/graph/task-graph.ts +4 -4
  54. package/src/index.ts +22 -5
  55. package/src/orchestrator/admission.ts +29 -1
  56. package/src/orchestrator/affected-tasks.ts +87 -18
  57. package/src/orchestrator/doctor.ts +14 -1
  58. package/src/orchestrator/execute-task.ts +149 -64
  59. package/src/orchestrator/failure-mode.ts +5 -3
  60. package/src/orchestrator/framed-output.ts +17 -3
  61. package/src/orchestrator/index.ts +11 -0
  62. package/src/orchestrator/logger.ts +57 -6
  63. package/src/orchestrator/metrics.ts +76 -4
  64. package/src/orchestrator/miss-reason.ts +3 -5
  65. package/src/orchestrator/miss-save.ts +6 -5
  66. package/src/orchestrator/options.ts +12 -0
  67. package/src/orchestrator/output-log.ts +14 -1
  68. package/src/orchestrator/path-links.ts +79 -0
  69. package/src/orchestrator/plan.ts +20 -3
  70. package/src/orchestrator/plugin.ts +7 -0
  71. package/src/orchestrator/prepare.ts +14 -4
  72. package/src/orchestrator/projects.ts +2 -2
  73. package/src/orchestrator/run-artifacts.ts +18 -2
  74. package/src/orchestrator/run-failures.ts +192 -0
  75. package/src/orchestrator/run-records.ts +10 -8
  76. package/src/orchestrator/run.ts +87 -22
  77. package/src/orchestrator/task-hash.ts +4 -4
  78. package/src/orchestrator/terminal-signals.ts +16 -0
  79. package/src/orchestrator/upstream.ts +65 -10
  80. package/src/util/errors.ts +55 -1
  81. package/src/util/index.ts +3 -0
  82. package/src/util/secret-mask.ts +13 -1
  83. package/src/util/verbs.ts +1 -0
  84. package/src/workspace/config-schema.ts +10 -8
  85. package/src/workspace/migration.ts +30 -3
  86. package/src/workspace/workspace.ts +1 -0
@@ -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.631",
3
+ "version": "0.0.633",
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.631",
25
- "@vzn/vx-linux-arm64": "0.0.631",
26
- "@vzn/vx-darwin-x64": "0.0.631",
27
- "@vzn/vx-darwin-arm64": "0.0.631"
24
+ "@vzn/vx-linux-x64": "0.0.633",
25
+ "@vzn/vx-linux-arm64": "0.0.633",
26
+ "@vzn/vx-darwin-x64": "0.0.633",
27
+ "@vzn/vx-darwin-arm64": "0.0.633"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -33,6 +33,15 @@
33
33
  "index.ts",
34
34
  "src",
35
35
  "config",
36
+ "schemas",
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",
36
45
  "launcher.cjs",
37
46
  "README.md",
38
47
  "LICENSE"
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "vx cache prune --format json",
4
+ "description": "What the policy evicted and the orphan sweep reaped; with dryRun, what they would.",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["dryRun", "evicted", "bytesFreed", "orphans", "orphanBytes"],
8
+ "properties": {
9
+ "dryRun": { "type": "boolean" },
10
+ "evicted": {
11
+ "type": "integer",
12
+ "description": "index entries evicted by the age / size policy"
13
+ },
14
+ "bytesFreed": {
15
+ "type": "integer",
16
+ "description": "bytes those entries occupied, per the index"
17
+ },
18
+ "orphans": {
19
+ "type": "integer",
20
+ "description": "artifacts and temp files with no index row, reaped"
21
+ },
22
+ "orphanBytes": { "type": "integer" }
23
+ }
24
+ }
@@ -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
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "a refusal under --format json",
4
+ "description": "What a verb asked for JSON prints on stdout when it refuses (exit 1). stderr carries the same refusal in prose.",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["ok", "error"],
8
+ "properties": {
9
+ "ok": { "const": false },
10
+ "error": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["code", "message"],
14
+ "properties": {
15
+ "code": {
16
+ "type": "string",
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
+ },
19
+ "message": { "type": "string", "description": "The refusal in prose; may change." }
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,147 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "vx info --format json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": [
7
+ "vx",
8
+ "bun",
9
+ "bunSupported",
10
+ "git",
11
+ "gitStatusCache",
12
+ "workspaceRoot",
13
+ "projects",
14
+ "tasks",
15
+ "configErrors",
16
+ "plugins",
17
+ "workers",
18
+ "memory",
19
+ "cacheDir",
20
+ "cacheStore",
21
+ "cacheVersion",
22
+ "schemaVersion",
23
+ "cacheEntries",
24
+ "cacheBytes",
25
+ "cacheRetention",
26
+ "orphans",
27
+ "runs24h",
28
+ "hits24h",
29
+ "restored24h",
30
+ "flakyTasks",
31
+ "lockfile",
32
+ "sandbox"
33
+ ],
34
+ "properties": {
35
+ "vx": { "type": "string" },
36
+ "bun": { "type": "string" },
37
+ "bunSupported": { "type": "boolean" },
38
+ "git": { "type": ["string", "null"] },
39
+ "gitStatusCache": {
40
+ "type": ["object", "null"],
41
+ "additionalProperties": false,
42
+ "required": ["fsmonitor", "untrackedCache"],
43
+ "properties": { "fsmonitor": { "type": "boolean" }, "untrackedCache": { "type": "boolean" } }
44
+ },
45
+ "workspaceRoot": { "type": "string" },
46
+ "projects": { "type": "integer" },
47
+ "tasks": { "type": "integer" },
48
+ "configErrors": {
49
+ "type": "array",
50
+ "items": {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": ["path", "message"],
54
+ "properties": { "path": { "type": "string" }, "message": { "type": "string" } }
55
+ }
56
+ },
57
+ "plugins": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "object",
61
+ "additionalProperties": false,
62
+ "required": ["name", "seams"],
63
+ "properties": {
64
+ "name": { "type": "string" },
65
+ "seams": { "type": "array", "items": { "type": "string" } }
66
+ }
67
+ }
68
+ },
69
+ "workers": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": ["count", "source", "cores", "cpuQuota"],
73
+ "properties": {
74
+ "count": { "type": "integer" },
75
+ "source": { "enum": ["workspace", "cgroup", "cores"] },
76
+ "cores": { "type": "integer" },
77
+ "cpuQuota": { "type": ["number", "null"] }
78
+ }
79
+ },
80
+ "memory": {
81
+ "type": "object",
82
+ "additionalProperties": false,
83
+ "required": ["usableBytes", "totalBytes", "cgroupLimitBytes"],
84
+ "properties": {
85
+ "usableBytes": { "type": "integer" },
86
+ "totalBytes": { "type": "integer" },
87
+ "cgroupLimitBytes": { "type": ["integer", "null"] }
88
+ }
89
+ },
90
+ "cacheDir": { "type": "string" },
91
+ "cacheStore": { "type": ["string", "null"] },
92
+ "cacheVersion": { "type": "string" },
93
+ "schemaVersion": { "type": "string" },
94
+ "cacheEntries": { "type": "integer" },
95
+ "cacheBytes": { "type": "integer" },
96
+ "cacheRetention": {
97
+ "type": ["object", "null"],
98
+ "description": "the workspace's cacheRetention, maxSize also in bytes; null when none is declared",
99
+ "additionalProperties": false,
100
+ "properties": {
101
+ "olderThan": { "type": "string" },
102
+ "maxSize": { "type": "string" },
103
+ "maxBytes": { "type": "integer" }
104
+ }
105
+ },
106
+ "orphans": {
107
+ "type": "object",
108
+ "additionalProperties": false,
109
+ "required": ["artifacts", "bytes"],
110
+ "properties": { "artifacts": { "type": "integer" }, "bytes": { "type": "integer" } }
111
+ },
112
+ "runs24h": { "type": "integer" },
113
+ "hits24h": { "type": "integer" },
114
+ "restored24h": {
115
+ "type": "integer",
116
+ "description": "of hits24h, the hits that restored outputs; the rest were up to date"
117
+ },
118
+ "flakyTasks": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "object",
122
+ "additionalProperties": false,
123
+ "required": ["taskId", "project", "task", "keys", "passes", "failures"],
124
+ "properties": {
125
+ "taskId": { "type": "string" },
126
+ "project": { "type": "string" },
127
+ "task": { "type": "string" },
128
+ "keys": { "type": "integer" },
129
+ "passes": { "type": "integer" },
130
+ "failures": { "type": "integer" }
131
+ }
132
+ }
133
+ },
134
+ "lockfile": { "type": "boolean" },
135
+ "sandbox": {
136
+ "type": "object",
137
+ "additionalProperties": false,
138
+ "required": ["available", "reason", "declared", "untraced"],
139
+ "properties": {
140
+ "available": { "type": "boolean" },
141
+ "reason": { "type": "string" },
142
+ "declared": { "type": "integer" },
143
+ "untraced": { "type": ["string", "null"] }
144
+ }
145
+ }
146
+ }
147
+ }
@@ -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
+ }