@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.
- package/LICENSE +21 -0
- package/README.md +349 -1
- package/launcher.mjs +92 -0
- package/package.json +44 -3
- package/src/bin.ts +25 -0
- package/src/cache/cache.ts +1832 -0
- package/src/cache/cas-backend.ts +97 -0
- package/src/cache/digest.ts +47 -0
- package/src/cache/index.ts +28 -0
- package/src/cache/inputs.ts +839 -0
- package/src/cache/layered-cache.ts +355 -0
- package/src/cache/tar.ts +343 -0
- package/src/cli/backend.ts +23 -0
- package/src/cli/cache.ts +96 -0
- package/src/cli/format.ts +15 -0
- package/src/cli/help.ts +129 -0
- package/src/cli/index.ts +85 -0
- package/src/cli/info.ts +80 -0
- package/src/cli/lock.ts +135 -0
- package/src/cli/mcp-rpc.ts +292 -0
- package/src/cli/mcp.ts +92 -0
- package/src/cli/migrate-nx.ts +450 -0
- package/src/cli/migrate-turbo.ts +364 -0
- package/src/cli/migrate.ts +219 -0
- package/src/cli/plan-format.ts +148 -0
- package/src/cli/run.ts +683 -0
- package/src/cli/show.ts +201 -0
- package/src/cli/upgrade.ts +103 -0
- package/src/cli/watch.ts +287 -0
- package/src/config.ts +460 -0
- package/src/exec/env.ts +74 -0
- package/src/exec/index.ts +13 -0
- package/src/exec/runner.ts +470 -0
- package/src/exec/sandbox-runtime.ts +578 -0
- package/src/graph/dependency-spec.ts +85 -0
- package/src/graph/index.ts +27 -0
- package/src/graph/scheduler.ts +677 -0
- package/src/graph/task-graph.ts +377 -0
- package/src/index.ts +222 -0
- package/src/orchestrator/colors.ts +63 -0
- package/src/orchestrator/devframe-surface.ts +74 -0
- package/src/orchestrator/events.ts +277 -0
- package/src/orchestrator/execute-task.ts +939 -0
- package/src/orchestrator/framed-output.ts +461 -0
- package/src/orchestrator/history.ts +148 -0
- package/src/orchestrator/index.ts +174 -0
- package/src/orchestrator/local-shortcircuit.ts +147 -0
- package/src/orchestrator/logger.ts +526 -0
- package/src/orchestrator/metrics.ts +1927 -0
- package/src/orchestrator/options.ts +197 -0
- package/src/orchestrator/plan.ts +129 -0
- package/src/orchestrator/plugin-host.ts +185 -0
- package/src/orchestrator/plugin.ts +283 -0
- package/src/orchestrator/predict.ts +106 -0
- package/src/orchestrator/prepare.ts +306 -0
- package/src/orchestrator/protocol.ts +156 -0
- package/src/orchestrator/remote-prefetch.ts +81 -0
- package/src/orchestrator/resources.ts +59 -0
- package/src/orchestrator/run-artifacts.ts +111 -0
- package/src/orchestrator/run-context.ts +205 -0
- package/src/orchestrator/run-report.ts +132 -0
- package/src/orchestrator/run-state.ts +145 -0
- package/src/orchestrator/run.ts +887 -0
- package/src/orchestrator/stable-keys.ts +177 -0
- package/src/orchestrator/status-line.ts +287 -0
- package/src/orchestrator/summary.ts +315 -0
- package/src/orchestrator/tally.ts +69 -0
- package/src/orchestrator/task-hash.ts +253 -0
- package/src/orchestrator/telemetry-host.ts +80 -0
- package/src/orchestrator/telemetry.ts +358 -0
- package/src/orchestrator/upstream.ts +121 -0
- package/src/orchestrator/verify.ts +164 -0
- package/src/orchestrator/wire-render.ts +92 -0
- package/src/orchestrator/wire.ts +212 -0
- package/src/util/errors.ts +11 -0
- package/src/util/hash.ts +25 -0
- package/src/util/index.ts +8 -0
- package/src/util/paths.ts +16 -0
- package/src/util/size.ts +16 -0
- package/src/util/ulid.ts +15 -0
- package/src/version.ts +10 -0
- package/src/workspace/affected.ts +118 -0
- package/src/workspace/filter.ts +155 -0
- package/src/workspace/fingerprint.ts +41 -0
- package/src/workspace/index.ts +27 -0
- package/src/workspace/lockfile.ts +118 -0
- package/src/workspace/nested-dirs.ts +34 -0
- package/src/workspace/package-graph.ts +148 -0
- package/src/workspace/project-loader.ts +512 -0
- package/src/workspace/workspace.ts +176 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// CASBackend — pluggable content-addressed storage beneath the cache.
|
|
2
|
+
//
|
|
3
|
+
// Today's Cache + LayeredCache both bundle "where do the bytes live"
|
|
4
|
+
// with "how do we look up entries metadata + key derivation."
|
|
5
|
+
// CASBackend separates the former so a future R2/S3 backend
|
|
6
|
+
// or REAPI CAS bridge can drop in without touching the
|
|
7
|
+
// orchestrator or the SQL entries index.
|
|
8
|
+
//
|
|
9
|
+
// Two reference implementations in this file:
|
|
10
|
+
// - MemoryCASBackend — testing + in-memory builds. Holds raw bytes.
|
|
11
|
+
// - FsCASBackend — writes bytes to a directory; mirrors what
|
|
12
|
+
// Cache.save does today. Bun-native using Bun.file
|
|
13
|
+
// for the read path so big artifacts stream
|
|
14
|
+
// without slurping into memory.
|
|
15
|
+
//
|
|
16
|
+
// Cache.ts has NOT yet been rewired to use a CASBackend — that's a
|
|
17
|
+
// follow-up (Phase 1b). This file ships the abstraction + the two
|
|
18
|
+
// reference impls so downstream work (R2, otel-bridge, distributed-ci)
|
|
19
|
+
// can rely on the type. Byte-identical behaviour today.
|
|
20
|
+
|
|
21
|
+
import { rm } from 'node:fs/promises'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
import type { Digest } from './digest.js'
|
|
24
|
+
|
|
25
|
+
export interface CASBackend {
|
|
26
|
+
/** Write bytes under `digest`. Idempotent — putting the same digest twice is a no-op. */
|
|
27
|
+
put(digest: Digest, bytes: Uint8Array): Promise<void>
|
|
28
|
+
/** Read bytes under `digest`, or null if absent. */
|
|
29
|
+
get(digest: Digest): Promise<Uint8Array | null>
|
|
30
|
+
/** Cheap existence probe — no bytes round-tripped if possible. */
|
|
31
|
+
has(digest: Digest): Promise<boolean>
|
|
32
|
+
/** Drop one entry (eviction). No-op if absent. */
|
|
33
|
+
remove(digest: Digest): Promise<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** In-memory backend, useful for tests and ephemeral runs. */
|
|
37
|
+
export class MemoryCASBackend implements CASBackend {
|
|
38
|
+
private readonly store = new Map<string, Uint8Array>()
|
|
39
|
+
|
|
40
|
+
async put(digest: Digest, bytes: Uint8Array): Promise<void> {
|
|
41
|
+
if (bytes.byteLength !== digest.sizeBytes) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`MemoryCASBackend.put: sizeBytes mismatch (digest=${digest.sizeBytes}, actual=${bytes.byteLength})`,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
this.store.set(digest.hash, bytes)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async get(digest: Digest): Promise<Uint8Array | null> {
|
|
50
|
+
return this.store.get(digest.hash) ?? null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async has(digest: Digest): Promise<boolean> {
|
|
54
|
+
return this.store.has(digest.hash)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async remove(digest: Digest): Promise<void> {
|
|
58
|
+
this.store.delete(digest.hash)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Test-only: how many entries are held. */
|
|
62
|
+
size(): number {
|
|
63
|
+
return this.store.size
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Filesystem-backed backend writing `<rootDir>/<hash>.tar.zst` files. */
|
|
68
|
+
export class FsCASBackend implements CASBackend {
|
|
69
|
+
constructor(private readonly rootDir: string) {}
|
|
70
|
+
|
|
71
|
+
private pathFor(digest: Digest): string {
|
|
72
|
+
return path.join(this.rootDir, `${digest.hash}.tar.zst`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async put(digest: Digest, bytes: Uint8Array): Promise<void> {
|
|
76
|
+
if (bytes.byteLength !== digest.sizeBytes) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`FsCASBackend.put: sizeBytes mismatch (digest=${digest.sizeBytes}, actual=${bytes.byteLength})`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
await Bun.write(this.pathFor(digest), bytes)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async get(digest: Digest): Promise<Uint8Array | null> {
|
|
85
|
+
const file = Bun.file(this.pathFor(digest))
|
|
86
|
+
if (!(await file.exists())) return null
|
|
87
|
+
return new Uint8Array(await file.arrayBuffer())
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async has(digest: Digest): Promise<boolean> {
|
|
91
|
+
return Bun.file(this.pathFor(digest)).exists()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async remove(digest: Digest): Promise<void> {
|
|
95
|
+
await rm(this.pathFor(digest), { force: true })
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Digest — the explicit content-address used by every cache layer.
|
|
2
|
+
//
|
|
3
|
+
// Lifted as a first-class type (architecture-review-2026-06.md §4.3)
|
|
4
|
+
// so storage backends become pluggable beneath CacheLayer: local FS,
|
|
5
|
+
// R2, S3, or a future REAPI CAS bridge all speak (hash, sizeBytes).
|
|
6
|
+
//
|
|
7
|
+
// Two practical wins from making this explicit:
|
|
8
|
+
// 1. sizeBytes is the truncation check at transport boundaries (HTTP
|
|
9
|
+
// Content-Length, R2 ETag-by-size). Free correctness, free corrupt-
|
|
10
|
+
// artifact-early-fail.
|
|
11
|
+
// 2. CASBackend.has(digest) can answer with just the metadata, no
|
|
12
|
+
// bytes round-trip — what existence probes want.
|
|
13
|
+
//
|
|
14
|
+
// Why sizeBytes is `number` and not `bigint`: Bun.file().size returns
|
|
15
|
+
// number; JS Number safely represents integers to 2^53-1 (~9 PB); no
|
|
16
|
+
// real cache artifact gets there. Bigint would create the same
|
|
17
|
+
// JSON.stringify-throws nuisance we already navigated for ns timestamps.
|
|
18
|
+
|
|
19
|
+
export interface Digest {
|
|
20
|
+
readonly hash: string
|
|
21
|
+
readonly sizeBytes: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function makeDigest(hash: string, sizeBytes: number): Digest {
|
|
25
|
+
if (!/^[0-9a-f]+$/i.test(hash)) {
|
|
26
|
+
throw new Error(`invalid digest hash: ${JSON.stringify(hash)}`)
|
|
27
|
+
}
|
|
28
|
+
if (!Number.isInteger(sizeBytes) || sizeBytes < 0) {
|
|
29
|
+
throw new Error(`invalid digest sizeBytes: ${sizeBytes}`)
|
|
30
|
+
}
|
|
31
|
+
return { hash, sizeBytes }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function digestEqual(a: Digest, b: Digest): boolean {
|
|
35
|
+
return a.hash === b.hash && a.sizeBytes === b.sizeBytes
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Stable wire format for logging / RPC: `hash/sizeBytes`. */
|
|
39
|
+
export function digestString(d: Digest): string {
|
|
40
|
+
return `${d.hash}/${d.sizeBytes}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function parseDigest(s: string): Digest {
|
|
44
|
+
const slash = s.indexOf('/')
|
|
45
|
+
if (slash === -1) throw new Error(`invalid digest string: ${JSON.stringify(s)}`)
|
|
46
|
+
return makeDigest(s.slice(0, slash), Number(s.slice(slash + 1)))
|
|
47
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Module contract. Cross-module imports must come through here; see
|
|
2
|
+
// docs/design/module-isolation-2026-06.md and tests/module-boundaries.test.ts.
|
|
3
|
+
// tar.ts is internal — artifact pack/extract is an implementation detail.
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
Cache,
|
|
7
|
+
type CacheEntry,
|
|
8
|
+
type CacheLayer,
|
|
9
|
+
type CachePolicy,
|
|
10
|
+
FULL_CACHE_POLICY,
|
|
11
|
+
type InvocationRecord,
|
|
12
|
+
parseCachePolicy,
|
|
13
|
+
type RunRecord,
|
|
14
|
+
type TaskInputRow,
|
|
15
|
+
WORKSPACE_OUTPUT_PREFIX,
|
|
16
|
+
} from './cache.js'
|
|
17
|
+
export { type CASBackend, FsCASBackend, MemoryCASBackend } from './cas-backend.js'
|
|
18
|
+
export { type Digest, digestEqual, digestString, makeDigest, parseDigest } from './digest.js'
|
|
19
|
+
export {
|
|
20
|
+
cleanOutputs,
|
|
21
|
+
cleanWorkspaceOutputs,
|
|
22
|
+
GitFilesCache,
|
|
23
|
+
populateGitFilesCache,
|
|
24
|
+
resolveInputs,
|
|
25
|
+
resolveOutputs,
|
|
26
|
+
resolveWorkspaceOutputs,
|
|
27
|
+
} from './inputs.js'
|
|
28
|
+
export { LayeredCache, type RemoteCacheLayer } from './layered-cache.js'
|