@vzn/vx 0.0.0 → 0.0.14

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 (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -1
  3. package/launcher.mjs +92 -0
  4. package/package.json +44 -3
  5. package/src/bin.ts +25 -0
  6. package/src/cache/cache.ts +1832 -0
  7. package/src/cache/cas-backend.ts +97 -0
  8. package/src/cache/digest.ts +47 -0
  9. package/src/cache/index.ts +28 -0
  10. package/src/cache/inputs.ts +839 -0
  11. package/src/cache/layered-cache.ts +355 -0
  12. package/src/cache/tar.ts +343 -0
  13. package/src/cli/backend.ts +23 -0
  14. package/src/cli/cache.ts +96 -0
  15. package/src/cli/format.ts +15 -0
  16. package/src/cli/help.ts +129 -0
  17. package/src/cli/index.ts +85 -0
  18. package/src/cli/info.ts +80 -0
  19. package/src/cli/lock.ts +135 -0
  20. package/src/cli/mcp-rpc.ts +292 -0
  21. package/src/cli/mcp.ts +92 -0
  22. package/src/cli/migrate-nx.ts +450 -0
  23. package/src/cli/migrate-turbo.ts +364 -0
  24. package/src/cli/migrate.ts +219 -0
  25. package/src/cli/plan-format.ts +148 -0
  26. package/src/cli/run.ts +683 -0
  27. package/src/cli/show.ts +201 -0
  28. package/src/cli/upgrade.ts +103 -0
  29. package/src/cli/watch.ts +287 -0
  30. package/src/config.ts +460 -0
  31. package/src/exec/env.ts +74 -0
  32. package/src/exec/index.ts +13 -0
  33. package/src/exec/runner.ts +470 -0
  34. package/src/exec/sandbox-runtime.ts +578 -0
  35. package/src/graph/dependency-spec.ts +85 -0
  36. package/src/graph/index.ts +27 -0
  37. package/src/graph/scheduler.ts +677 -0
  38. package/src/graph/task-graph.ts +377 -0
  39. package/src/index.ts +222 -0
  40. package/src/orchestrator/colors.ts +63 -0
  41. package/src/orchestrator/devframe-surface.ts +74 -0
  42. package/src/orchestrator/events.ts +277 -0
  43. package/src/orchestrator/execute-task.ts +939 -0
  44. package/src/orchestrator/framed-output.ts +461 -0
  45. package/src/orchestrator/history.ts +148 -0
  46. package/src/orchestrator/index.ts +174 -0
  47. package/src/orchestrator/local-shortcircuit.ts +147 -0
  48. package/src/orchestrator/logger.ts +526 -0
  49. package/src/orchestrator/metrics.ts +1927 -0
  50. package/src/orchestrator/options.ts +197 -0
  51. package/src/orchestrator/plan.ts +129 -0
  52. package/src/orchestrator/plugin-host.ts +185 -0
  53. package/src/orchestrator/plugin.ts +283 -0
  54. package/src/orchestrator/predict.ts +106 -0
  55. package/src/orchestrator/prepare.ts +306 -0
  56. package/src/orchestrator/protocol.ts +156 -0
  57. package/src/orchestrator/remote-prefetch.ts +81 -0
  58. package/src/orchestrator/resources.ts +59 -0
  59. package/src/orchestrator/run-artifacts.ts +111 -0
  60. package/src/orchestrator/run-context.ts +205 -0
  61. package/src/orchestrator/run-report.ts +132 -0
  62. package/src/orchestrator/run-state.ts +145 -0
  63. package/src/orchestrator/run.ts +887 -0
  64. package/src/orchestrator/stable-keys.ts +177 -0
  65. package/src/orchestrator/status-line.ts +287 -0
  66. package/src/orchestrator/summary.ts +315 -0
  67. package/src/orchestrator/tally.ts +69 -0
  68. package/src/orchestrator/task-hash.ts +253 -0
  69. package/src/orchestrator/telemetry-host.ts +80 -0
  70. package/src/orchestrator/telemetry.ts +358 -0
  71. package/src/orchestrator/upstream.ts +121 -0
  72. package/src/orchestrator/verify.ts +164 -0
  73. package/src/orchestrator/wire-render.ts +92 -0
  74. package/src/orchestrator/wire.ts +212 -0
  75. package/src/util/errors.ts +11 -0
  76. package/src/util/hash.ts +25 -0
  77. package/src/util/index.ts +8 -0
  78. package/src/util/paths.ts +16 -0
  79. package/src/util/size.ts +16 -0
  80. package/src/util/ulid.ts +15 -0
  81. package/src/version.ts +10 -0
  82. package/src/workspace/affected.ts +118 -0
  83. package/src/workspace/filter.ts +155 -0
  84. package/src/workspace/fingerprint.ts +41 -0
  85. package/src/workspace/index.ts +27 -0
  86. package/src/workspace/lockfile.ts +118 -0
  87. package/src/workspace/nested-dirs.ts +34 -0
  88. package/src/workspace/package-graph.ts +148 -0
  89. package/src/workspace/project-loader.ts +512 -0
  90. package/src/workspace/workspace.ts +176 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nxt contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,349 @@
1
- Reserved by the vx project. Real release to follow.
1
+ # vx
2
+
3
+ **The fastest way to build a monorepo.**
4
+
5
+ vx runs your task graph, remembers every result, and never does the
6
+ same work twice. Fully cached runs finish in milliseconds — 144 ms
7
+ across 100 packages, 0.62 s across a 1090-package graph of 3,270
8
+ tasks. Measured, reproducible, on hardware you own.
9
+
10
+ One binary. No daemon. No Node. Nothing to babysit.
11
+
12
+ 📖 **[Documentation site →](https://vznjs.github.io/vx/)** — guides,
13
+ architecture, caching, and the full CLI / config reference.
14
+
15
+ ```sh
16
+ # From npm — ships the prebuilt standalone binary (no Bun required):
17
+ npm install -g @vzn/vx # or: pnpm add -g @vzn/vx · bun add -g @vzn/vx
18
+
19
+ # Or the zero-dependency install script (no Node/npm needed):
20
+ curl -fsSL https://raw.githubusercontent.com/vznjs/vx/main/install.sh | sh
21
+ ```
22
+
23
+ ```ts
24
+ // vx.config.ts
25
+ import { defineProject } from '@vzn/vx'
26
+
27
+ export default defineProject({
28
+ tasks: {
29
+ build: {
30
+ exec: { command: 'tsc -b' },
31
+ dependsOn: ['^build'],
32
+ cache: {
33
+ inputs: { files: ['src/**'] },
34
+ outputs: { files: ['dist/**'] },
35
+ },
36
+ },
37
+ test: {
38
+ exec: { command: 'bun test' },
39
+ dependsOn: ['build'],
40
+ cache: { inputs: { files: ['src/**', 'tests/**'] }, outputs: { files: [] } },
41
+ },
42
+ ci: { dependsOn: ['lint', 'test'] }, // umbrella; runs both
43
+ },
44
+ })
45
+ ```
46
+
47
+ ```sh
48
+ vx run build # cwd project + its workspace deps
49
+ vx run test --all # every project that declares `test`
50
+ vx run ci --affected # only what changed since origin/HEAD
51
+ vx watch lint # re-run on file changes
52
+ vx run build --dry # show the plan, don't execute
53
+ ```
54
+
55
+ ## Beyond a task runner — what shipped in the 2026-06 platform arc
56
+
57
+ vx now ships an **open platform**, not just a CLI. Every surface
58
+ below is built into the binary; no external services required.
59
+
60
+ ```sh
61
+ vx mcp # Model Context Protocol server (stdio)
62
+ # — Claude Code / Cursor / Continue.dev talk to vx as a typed tool
63
+
64
+ npm i -g @vzn/vx-cloud # the service CLI — also a standalone binary, no Bun
65
+ vx-cloud serve --ui --open # team backend + embedded dashboard (token-authenticated)
66
+ vx-cloud agent --url https://ci.acme.dev # join a distributed-execution pool (Nx-Agents-style DTE)
67
+ vx-cloud connect https://ci.acme.dev # one-URL analytics + remote cache for every `vx run`
68
+ ```
69
+
70
+ ### Open platform highlights
71
+
72
+ - **Provider-neutral core.** `vx` runs tasks and nothing else — the
73
+ dashboard, remote cache, distribution, and telemetry all arrive via
74
+ PLUGINS declared in `vx.workspace.ts`. `@vzn/vx-cloud` is the
75
+ first-party one; anyone can write another against the same seams
76
+ (backend / cache / telemetry).
77
+ - **MCP for AI agents, twice.** `vx mcp` (stdio, reads your real
78
+ `cache.db`: `getCacheStats`, `getRunHistory`, `explainCacheKey`,
79
+ `whyDidThisRerun`) and `POST /mcp` on any `vx-cloud serve` — local
80
+ or hosted, behind the bearer token.
81
+ - **Provable cache correctness.** `vx run --verify` re-runs each
82
+ cacheable task and byte-compares outputs; `--verify=inputs` runs it
83
+ sandboxed against the declared inputs and names any undeclared read.
84
+ The only runner that PROVES a cache entry safe instead of hoping.
85
+ - **Distributed CI (DTE).** `vx-cloud agent` joins a session-keyed
86
+ pool; a `vx run` fans tasks out across same-commit agents, outputs
87
+ propagate through the shared artifact store, and a standing pool
88
+ multiplexes concurrent runs with fair scheduling.
89
+ - **Remote cache, one URL.** `vx-cloud connect <url>` wires analytics
90
+ ingest AND a Turborepo-wire-compatible artifact store (HMAC signing,
91
+ trusted/untrusted tiers derived from the token). Third-party Turbo
92
+ cache servers work via two env vars.
93
+ - **OTel CI/CD spans.** Declare `otel()` from `@vzn/vx-otel` in
94
+ `vx.workspace.ts` — OTLP/HTTP traces + metrics with ZERO
95
+ OpenTelemetry SDK dependency; runs flow to Grafana / Honeycomb /
96
+ Datadog, including `--verify` hermeticity verdicts as span status.
97
+ - **Dashboard embedded in the `vx-cloud` binary.** Runs, flamegraphs,
98
+ live run cockpit with the task DAG, per-task logs + artifacts,
99
+ cache-key diffs ("why did this re-run?"), flaky-task detection.
100
+ Nothing to build or install; Docker image on ghcr for hosting.
101
+ - **Predictive scheduling.** Opt in with `predictive: true` — the
102
+ scheduler reads run history and starts the longest expected critical
103
+ path first.
104
+
105
+ Each surface lives behind a design doc under `docs/design/`.
106
+
107
+ ## A cache that actually understands your build
108
+
109
+ Every task runner caches. vx caches _correctly_ — and stops work
110
+ others would redo:
111
+
112
+ - **Config is code, and the cache knows it.** `vx.config.ts` is
113
+ evaluated before hashing, so imports, presets, and computed values
114
+ all participate in cache identity. Change a shared preset, and
115
+ exactly the right tasks re-run.
116
+ - **Outputs are owned.** Declared outputs are wiped before every
117
+ execution and every restore. Your tree ends each run bit-identical
118
+ to the cached snapshot — stale files cannot exist.
119
+ - **Hashes come from git.** On a clean tree, deriving every cache key
120
+ costs zero file reads, zero stats, zero database lookups. At
121
+ 15,000 files that's a 3.2× faster warm path.
122
+
123
+ ## Speed is a design discipline
124
+
125
+ Exact bitset graph algorithms for scheduling. One bulk git
126
+ enumeration per run, partitioned by binary search. Restores that
127
+ skip extraction entirely when the tree already matches. In-process
128
+ tar (no subprocess on the hot path). Atomic artifact publishes.
129
+ Single-transaction metadata writes. Every optimization is recorded
130
+ with the invariant that keeps it valid —
131
+ [`docs/optimizations.md`](docs/optimizations.md) is the ledger, and
132
+ [`bench/`](bench/) reproduces the numbers.
133
+
134
+ ## Built for trust
135
+
136
+ - **Provable cache correctness.** `vx run --verify` re-runs each
137
+ cacheable task and byte-compares outputs (determinism);
138
+ `--verify=inputs` sandboxes it against the declared inputs and fails
139
+ loud naming any undeclared read (completeness). No other runner can
140
+ prove a cache entry safe.
141
+ - **Signed artifacts.** HMAC signing on the remote-cache wire; with a
142
+ key configured, unsigned or tampered artifacts are rejected and the
143
+ task simply re-runs. A poisoned cache can't reach your machines.
144
+ - **Cache trust tiers.** The server derives trusted/untrusted from the
145
+ bearer token — a fork-PR artifact can never feed a trusted build.
146
+ - **Corruption can't go live.** Artifacts are validated before they
147
+ enter the store (zstd-bomb and oversize downloads refused); bad
148
+ bytes degrade to a cache miss, never a crash.
149
+ - **Clean exits.** SIGINT/SIGTERM reap every child process — no
150
+ orphaned dev servers in CI.
151
+ - **Readiness you can bound.** Persistent tasks gate downstream work
152
+ on a `readyWhen` signal; `exec.timeout` bounds any task (with
153
+ `--timeout` / workspace defaults), `exec.retries` + `--retry` absorb
154
+ flakes — and retried-then-passed tasks are flagged flaky.
155
+ - **Kernel-level sandboxing**, opt-in per task, that fails the build
156
+ on violation instead of hiding it.
157
+
158
+ ## Reproducible graphs, when you want them
159
+
160
+ Configs are TypeScript — powerful, but a program's output can vary
161
+ with its environment. `vx lock` freezes the fully-resolved task graph
162
+ into a committed `vx-lock.json`, pnpm-style:
163
+
164
+ ```sh
165
+ vx lock # evaluate everything once, write vx-lock.json
166
+ vx lock --check && vx run ci --frozen # CI: audit, then run EXACTLY that graph
167
+ ```
168
+
169
+ | Command | Evaluates configs | Uses lock |
170
+ | ----------------- | ----------------- | --------------------------------------------------------------- |
171
+ | `vx run` | always, live | never — local truth has no asterisks |
172
+ | `vx run --frozen` | never | yes; refuses if absent or a config file changed since locking |
173
+ | `vx lock --check` | full graph | compares — catches env and import drift that byte hashes cannot |
174
+
175
+ Env values read at lock time are frozen by design — cache keys become
176
+ reproducible across machines. Bonus: `--frozen` runs skip config
177
+ evaluation entirely (~120 ms back per 1,000 packages). No other
178
+ runner has an equivalent.
179
+
180
+ ## Everything you need, nothing to configure twice
181
+
182
+ TypeScript config with real imports · task graph with `^task`
183
+ resolution that bridges packages without the task · multi-task runs
184
+ with one shared graph · pnpm-style filters and `--affected` ·
185
+ watch mode · `--dry` / `--graph` plans · persistent dev servers ·
186
+ remote caching via one URL (or two env vars, wire-compatible with
187
+ existing Turbo artifact servers) · retries, timeouts, `--continue`
188
+ modes · per-layer cache control (`--cache=local:r,remote:`) ·
189
+ `vx info`, `--summarize`, `--profile` Chrome traces, `--report` ·
190
+ `vx cache prune` with TTL and size caps · `vx migrate` from
191
+ turbo.json or an Nx graph.
192
+
193
+ ## How it compares
194
+
195
+ | | vx | Turborepo | Nx |
196
+ | ------------------------- | --------------------------------------------------------------------- | ------------------------------ | ------------------- |
197
+ | Fully cached, 100 pkgs¹ | **144 ms** | 279 ms | 583+ ms |
198
+ | Config | TypeScript, evaluated into the cache key | JSON (static) | JSON (static) |
199
+ | Output ownership | **Strict** — wiped before exec AND restore | Additive (stale files survive) | Additive |
200
+ | Clean-tree hashing | **Zero reads** (git index OIDs) | git OIDs | re-hash / daemon |
201
+ | Daemon required for speed | **No** | Optional | Yes |
202
+ | Artifact signing | **Hard-fail** on unsigned | Soft | No |
203
+ | Per-task sandbox | **Yes** — kernel-level, opt-in | No | No |
204
+ | Provable cache safety | **Yes** — `--verify` (determinism) + `--verify=inputs` (completeness) | No | No |
205
+ | MCP server for AI agents | **Yes** (`vx mcp` stdio + `POST /mcp` on the serve) | No | No |
206
+ | Distributed CI execution | **Yes** — OSS, self-hostable (`vx-cloud agent`, Nx-DTE-style) | No | Paid (Nx Cloud DTE) |
207
+ | Dashboard SPA | **Yes** — embedded in `vx-cloud serve --ui`: runs, logs, DAG, flaky | No | Paid |
208
+ | Self-hosted cloud | **Yes** — `vx-cloud` binary or the ghcr Docker image; one stack | Vercel-only | No (proprietary) |
209
+ | Plugin API | **Yes** — backend / cache / telemetry seams | No | Yes (TS-tied) |
210
+ | Predictive scheduling | **Yes** (opt-in: `predictive: true`) | No | No |
211
+ | OTel CI/CD spans | **Yes** — `otel()` plugin, zero OTel-SDK deps | No | Paid |
212
+ | Install | **Single binary** — npm or 1 curl line, no Node/Bun needed | npm + Node | npm + Node |
213
+
214
+ ¹ Wall-clock, direct binaries, same machine and workspace — full
215
+ methodology and more scenarios in
216
+ [`docs/benchmarks.md`](docs/benchmarks.md).
217
+
218
+ ## Switching from another runner
219
+
220
+ Most projects can move in an afternoon. The mapping is mechanical:
221
+
222
+ ```jsonc
223
+ // turbo.json (before)
224
+ {
225
+ "tasks": {
226
+ "build": {
227
+ "dependsOn": ["^build"],
228
+ "inputs": ["src/**"],
229
+ "outputs": ["dist/**"],
230
+ "env": ["NODE_ENV"],
231
+ },
232
+ },
233
+ }
234
+ ```
235
+
236
+ ```ts
237
+ // vx.config.ts (after)
238
+ import { defineProject } from '@vzn/vx'
239
+ export default defineProject({
240
+ tasks: {
241
+ build: {
242
+ // Name the command (Turbo reads package.json scripts). The child
243
+ // env is ISOLATED: a cache-input env var must also be passed
244
+ // through, or the key would vary while the task can't see it.
245
+ exec: { command: 'tsc -b', env: { passThrough: ['NODE_ENV'] } },
246
+ dependsOn: ['^build'],
247
+ cache: {
248
+ inputs: { files: ['src/**'], env: ['NODE_ENV'] },
249
+ outputs: { files: ['dist/**'] },
250
+ },
251
+ },
252
+ },
253
+ })
254
+ ```
255
+
256
+ Differences to know:
257
+
258
+ - vx requires `exec.command` in the config — we don't read `package.json` scripts implicitly.
259
+ - vx requires `cache.inputs.files` when caching is enabled (no default `$TURBO_DEFAULT$`).
260
+ - vx defaults caching **off**; opt in per task by adding the `cache` block.
261
+ - Persistent tasks: `persistent: { readyWhen: 'regex' }` (Turbo uses just `persistent: true`).
262
+ - Remote cache: same wire format. Existing `VERCEL_*` / Turbo-cache-server tokens work via `VX_REMOTE_CACHE_TOKEN`.
263
+
264
+ Side-by-side feature matrix + every known gap: [`docs/comparison.md`](./docs/comparison.md).
265
+
266
+ ## Architecture (one paragraph)
267
+
268
+ `bin.ts → cli/index.ts` dispatches subcommands.
269
+ `orchestrator/run.ts:run()` calls `prepareRun()` which discovers the
270
+ workspace, loads configs, builds the package + task graph, opens the
271
+ cache (local SQLite + optional remote layer), and installs plugins
272
+ from `vx.workspace.ts`. The two-tier scheduler runs the graph in
273
+ topological order with bounded concurrency (confirmed cache hits
274
+ restore ahead of their deps); each task hits the cache (hash → get →
275
+ restore on hit; spawn → save on miss) or short-circuits as a group /
276
+ persistent. Every observation flows through one event bus — the
277
+ terminal renderer subscribes directly, and PLUGINS receive the
278
+ versioned telemetry contract (`TelemetryRecord` / `RunSummaryRecord`)
279
+ — that's how `otel()` and `cloud()` export without core knowing them.
280
+ Core never imports a plugin; the arrow only points plugin → core.
281
+ Every module has a docs page; every interface is a swappable seam.
282
+
283
+ Read [`docs/architecture.md`](./docs/architecture.md) for the module
284
+ map; the design record lives under [`docs/design/`](./docs/design/).
285
+
286
+ ## Documentation
287
+
288
+ Full technical docs live under [`docs/`](./docs/):
289
+
290
+ **Core**
291
+
292
+ - [`docs/architecture.md`](./docs/architecture.md) — module map + data flow
293
+ - [`docs/schema.md`](./docs/schema.md) — every config field
294
+ - [`docs/caching.md`](./docs/caching.md) — cache-key derivation + invalidation table
295
+ - [`docs/execution.md`](./docs/execution.md) — `vx run` lifecycle
296
+ - [`docs/cli.md`](./docs/cli.md) — every flag
297
+ - [`docs/comparison.md`](./docs/comparison.md) — Turbo / Nx / vite-task feature matrix
298
+ - [`docs/modules/`](./docs/modules/) — one reference page per source module
299
+
300
+ **Design + 2026-06 platform arc** (`docs/design/`)
301
+
302
+ - [`architecture-north-star-2026-06.md`](./docs/design/architecture-north-star-2026-06.md) — the unified vision
303
+ - [`architecture-review-2026-06.md`](./docs/design/architecture-review-2026-06.md) — review + applied checklist
304
+ - [`wire-protocol-2026-06.md`](./docs/design/wire-protocol-2026-06.md) — JSON-RPC 2.0 + OTel envelope (shipped)
305
+ - [`distributed-ci-2026-06.md`](./docs/design/distributed-ci-2026-06.md) — original coordinator/worker (superseded by `vx-cloud agent` DTE — `distributed-execution-2026-07.md`)
306
+ - [`vx-cloud-2026-06.md`](./docs/design/vx-cloud-2026-06.md) — original CF cloud (superseded by the standalone `vx-cloud` service)
307
+ - [`extension-protocol-2026-06.md`](./docs/design/extension-protocol-2026-06.md) — subscriber/inspector/driver/plugin (Phase 1 shipped)
308
+ - [`predictive-execution-2026-06.md`](./docs/design/predictive-execution-2026-06.md) — history-aware scheduling (Phase A-B shipped)
309
+ - [`docs/progress/implementation-log-2026-06.md`](./docs/progress/implementation-log-2026-06.md) — phase-by-phase narrative
310
+
311
+ ## Status
312
+
313
+ **Pre-alpha.** The schema is settling; we bump `CACHE_VERSION` rather
314
+ than maintain back-compat. **1,400+ tests (core + packages); CI green
315
+ on every commit**; the project dogfoods itself (`vx run ci`).
316
+ Published on npm: [`@vzn/vx`](https://www.npmjs.com/package/@vzn/vx)
317
+ and `@vzn/vx-cloud`, both as prebuilt standalone binaries.
318
+
319
+ Production readiness for the **core task runner**: the semantics are
320
+ solid. The main operational rough edge is Windows (unsupported).
321
+
322
+ Production readiness for the **platform layer**:
323
+
324
+ | Surface | Maturity | Notes |
325
+ | ---------------------------------------------- | -------------------------------- | ---------------------------------------------------------------- |
326
+ | Core task runner + caching | **production-ready** | dogfooded continuously; 1,150+ core tests, all green |
327
+ | `vx run --verify` (provable cache correctness) | **shippable** | determinism + input-completeness proofs; CI-gate recipe in docs |
328
+ | `vx mcp` + serve `POST /mcp` | **shippable** | live cache.db tools (stdio); serve-hosted MCP behind the bearer |
329
+ | `vx-cloud serve` (ingest + dashboard + /v8) | **shippable** | token-auth, multi-workspace, artifact store, ghcr image |
330
+ | `vx-cloud agent` distributed execution | **shippable for self-hosted CI** | session-keyed DTE, shared pools, fair multi-run scheduling |
331
+ | Plugin API (backend / cache / telemetry) | **shippable** | crash-isolated; `cloud()` + `otel()` are ordinary plugins |
332
+ | Predictive scheduling | **shippable as opt-in** | gated on `predictive: true` + observed data |
333
+ | Dashboard SPA (embedded in `vx-cloud`) | **iterating** | runs/logs/DAG/flaky live; data-first entity redesign in progress |
334
+ | OTel export (`@vzn/vx-otel`) | **shippable** | declare `otel()` in `vx.workspace.ts`; OTLP traces + metrics |
335
+
336
+ ## Development
337
+
338
+ ```sh
339
+ git clone https://github.com/vznjs/vx && cd vx
340
+ bun install
341
+ bun src/bin.ts run ci # format-check + lint + test
342
+ bun src/bin.ts run build # cross-target binaries → dist/
343
+ ```
344
+
345
+ vx is self-hosted: every dev task routes through `bun src/bin.ts run <task>` per the repo's own `vx.config.ts`. No `package.json` scripts; CI invokes vx directly.
346
+
347
+ ## License
348
+
349
+ MIT — see [LICENSE](./LICENSE).
package/launcher.mjs ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ // npm launcher for a vx command — the entry a published package's `bin` points
3
+ // at. It execs the prebuilt standalone binary shipped as a per-platform
4
+ // optionalDependency, so end users get the command WITHOUT installing Bun. ONE
5
+ // launcher serves both `@vzn/vx` and `@vzn/vx-cloud`: everything is derived from
6
+ // this package's OWN name, read from the package.json sitting beside this file.
7
+ //
8
+ // @vzn/vx → platform pkg @vzn/vx-<key>, binary `vx`
9
+ // @vzn/vx-cloud → platform pkg @vzn/vx-cloud-<key>, binary `vx-cloud`
10
+ //
11
+ // Resolution order:
12
+ // 1. the matching <name>-<platform> optionalDependency's binary (the normal
13
+ // path — esbuild/turborepo/biome model, no install-time download);
14
+ // 2. a source fallback: `bun <sourceEntry>` (a source checkout, or a platform
15
+ // with no prebuilt binary if the user happens to have Bun).
16
+ // Anything else is a clear, actionable error.
17
+
18
+ import { spawnSync } from 'node:child_process'
19
+ import { existsSync } from 'node:fs'
20
+ import { createRequire } from 'node:module'
21
+ import { constants as osConstants } from 'node:os'
22
+ import { dirname, join } from 'node:path'
23
+ import { fileURLToPath } from 'node:url'
24
+
25
+ const require = createRequire(import.meta.url)
26
+ const here = dirname(fileURLToPath(import.meta.url))
27
+ const pkg = require('./package.json')
28
+
29
+ // Derive the platform-package prefix + binary basename from this package's name
30
+ // so the same launcher works for @vzn/vx and @vzn/vx-cloud. `base` is the
31
+ // unscoped name (vx / vx-cloud) — the command AND the binary filename inside the
32
+ // platform package. `vxSourceEntry` (a package.json field) is the source-mode
33
+ // entry: `src/bin.ts` for vx, `src/cli/bin.ts` for vx-cloud.
34
+ const name = pkg.name
35
+ const base = name.replace(/^@[^/]+\//, '')
36
+ const sourceEntry = pkg.vxSourceEntry ?? 'src/bin.ts'
37
+
38
+ const SUPPORTED = ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64']
39
+ const key = `${process.platform}-${process.arch}`
40
+ const args = process.argv.slice(2)
41
+
42
+ function platformBinary() {
43
+ if (!SUPPORTED.includes(key)) return undefined
44
+ const platformPkg = `${name}-${key}`
45
+ try {
46
+ // No `exports` restriction on the platform packages, so package.json
47
+ // resolves; the binary sits beside it. Works under npm hoisting + pnpm.
48
+ const manifest = require.resolve(`${platformPkg}/package.json`)
49
+ const bin = join(dirname(manifest), base)
50
+ return existsSync(bin) ? bin : undefined
51
+ } catch {
52
+ return undefined
53
+ }
54
+ }
55
+
56
+ function hasBun() {
57
+ const probe = spawnSync('bun', ['--version'], { stdio: 'ignore' })
58
+ return probe.status === 0
59
+ }
60
+
61
+ function run(cmd, cmdArgs) {
62
+ const res = spawnSync(cmd, cmdArgs, { stdio: 'inherit' })
63
+ if (res.error) {
64
+ process.stderr.write(`${base}: failed to launch (${res.error.message})\n`)
65
+ process.exit(1)
66
+ }
67
+ // Mirror the child's exit; a signal death maps to the POSIX 128+signo code.
68
+ if (res.signal) process.exit(128 + (osConstants.signals[res.signal] ?? 1))
69
+ process.exit(res.status ?? 0)
70
+ }
71
+
72
+ const bin = platformBinary()
73
+ if (bin !== undefined) {
74
+ run(bin, args)
75
+ }
76
+
77
+ // No prebuilt binary for this platform — fall back to the shipped source if Bun
78
+ // is available (a source checkout, or an unsupported platform + Bun installed).
79
+ const source = join(here, sourceEntry)
80
+ if (existsSync(source) && hasBun()) {
81
+ run('bun', [source, ...args])
82
+ }
83
+
84
+ const supported = SUPPORTED.join(', ')
85
+ process.stderr.write(
86
+ `${base}: no prebuilt binary for ${key}.\n` +
87
+ ` Supported platforms: ${supported}.\n` +
88
+ ` If your platform should be supported, reinstall so npm fetches the\n` +
89
+ ` matching ${name}-${key} optionalDependency, or install Bun (>=1.3) to\n` +
90
+ ` run ${base} from source.\n`,
91
+ )
92
+ process.exit(1)
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.0",
4
- "description": "Placeholder to reserve the name and enable npm trusted publishing. Real release to follow.",
5
- "license": "MIT"
3
+ "version": "0.0.14",
4
+ "description": "An open, extensible monorepo task runner.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "import": "./src/index.ts"
10
+ }
11
+ },
12
+ "types": "./src/index.ts",
13
+ "bin": {
14
+ "vx": "./launcher.mjs"
15
+ },
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "optionalDependencies": {
20
+ "@vzn/vx-linux-x64": "0.0.14",
21
+ "@vzn/vx-linux-arm64": "0.0.14",
22
+ "@vzn/vx-darwin-x64": "0.0.14",
23
+ "@vzn/vx-darwin-arm64": "0.0.14"
24
+ },
25
+ "dependencies": {
26
+ "@anthropic-ai/sandbox-runtime": "^0.0.56"
27
+ },
28
+ "files": [
29
+ "src",
30
+ "launcher.mjs",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "repository": "https://github.com/vznjs/vx",
35
+ "homepage": "https://github.com/vznjs/vx#readme",
36
+ "bugs": "https://github.com/vznjs/vx/issues",
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "monorepo",
40
+ "task-runner",
41
+ "build",
42
+ "cache",
43
+ "bun",
44
+ "turborepo",
45
+ "nx"
46
+ ]
6
47
  }
package/src/bin.ts ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bun
2
+ import { run } from './cli/index.js'
3
+ import { UserError } from './util/index.js'
4
+
5
+ // Wrapped in an explicit async main so `bun build --compile` accepts
6
+ // the file. The compile target doesn't allow top-level await.
7
+ async function main(): Promise<void> {
8
+ try {
9
+ process.exit(await run(process.argv.slice(2)))
10
+ } catch (err) {
11
+ // UserError (workspace not found, cycle, config invalid, ...) —
12
+ // print the message only; the stack is noise the user can't act
13
+ // on. Everything else gets the full stack so internal bugs are
14
+ // debuggable.
15
+ if (err instanceof UserError) {
16
+ process.stderr.write(`vx: ${err.message}\n`)
17
+ } else {
18
+ const message = err instanceof Error ? (err.stack ?? err.message) : String(err)
19
+ process.stderr.write(`vx: ${message}\n`)
20
+ }
21
+ process.exit(1)
22
+ }
23
+ }
24
+
25
+ void main()