@vzn/vx 0.0.21 → 0.0.22
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 +21 -18
- package/package.json +6 -6
- package/src/bin.ts +52 -3
- package/src/cache/archive.ts +170 -21
- package/src/cache/cache.ts +667 -288
- package/src/cache/chained-cache.ts +14 -2
- package/src/cache/config-evals.ts +29 -6
- package/src/cache/file-hashes.ts +63 -35
- package/src/cache/git-inputs.ts +398 -69
- package/src/cache/index.ts +8 -1
- package/src/cache/inputs.ts +403 -102
- package/src/cache/key-fold.ts +232 -0
- package/src/cache/layer.ts +81 -11
- package/src/cache/layered-cache.ts +199 -32
- package/src/cache/output-index.ts +109 -10
- package/src/cache/run-history.ts +8 -3
- package/src/cache/zstd.ts +53 -30
- package/src/cli/cache.ts +51 -21
- package/src/cli/format.ts +4 -15
- package/src/cli/help.ts +13 -3
- package/src/cli/index.ts +35 -9
- package/src/cli/info.ts +49 -7
- package/src/cli/init.ts +17 -5
- package/src/cli/last.ts +96 -21
- package/src/cli/lock.ts +34 -17
- package/src/cli/plan-format.ts +4 -4
- package/src/cli/plugin-commands.ts +20 -5
- package/src/cli/run.ts +55 -12
- package/src/cli/select.ts +23 -13
- package/src/cli/show.ts +9 -4
- package/src/cli/upgrade.ts +134 -14
- package/src/cli/watch.ts +611 -102
- package/src/cli/why.ts +8 -16
- package/src/cli/workspace-config.ts +21 -9
- package/src/config.ts +46 -9
- package/src/exec/executor.ts +51 -5
- package/src/exec/index.ts +10 -0
- package/src/exec/kill-tree.ts +281 -0
- package/src/exec/local-executor.ts +1 -1
- package/src/exec/runner.ts +327 -82
- package/src/exec/sandbox-binds.ts +34 -5
- package/src/exec/sandbox-paths.ts +22 -2
- package/src/exec/sandbox-runtime.ts +627 -102
- package/src/exec/sandbox-violations.ts +36 -23
- package/src/graph/dependency-spec.ts +14 -14
- package/src/graph/index.ts +12 -2
- package/src/graph/priorities.ts +130 -0
- package/src/graph/scheduler.ts +182 -134
- package/src/graph/task-graph.ts +552 -188
- package/src/index.ts +16 -2
- package/src/orchestrator/admission.ts +41 -17
- package/src/orchestrator/colors.ts +8 -3
- package/src/orchestrator/deferred-outputs.ts +22 -7
- package/src/orchestrator/doctor.ts +122 -14
- package/src/orchestrator/events.ts +71 -2
- package/src/orchestrator/excluded-keys.ts +155 -0
- package/src/orchestrator/execute-task.ts +331 -37
- package/src/orchestrator/failure-recap.ts +121 -0
- package/src/orchestrator/fingerprint-watch.ts +89 -0
- package/src/orchestrator/framed-output.ts +81 -14
- package/src/orchestrator/history.ts +6 -7
- package/src/orchestrator/hit-restore.ts +82 -18
- package/src/orchestrator/index.ts +7 -1
- package/src/orchestrator/keyed-projects.ts +89 -0
- package/src/orchestrator/local-shortcircuit.ts +83 -23
- package/src/orchestrator/lockfile-claim.ts +152 -32
- package/src/orchestrator/logger.ts +116 -20
- package/src/orchestrator/metrics.ts +156 -60
- package/src/orchestrator/miss-save.ts +106 -43
- package/src/orchestrator/options.ts +30 -4
- package/src/orchestrator/persistent.ts +41 -21
- package/src/orchestrator/placement.ts +55 -25
- package/src/orchestrator/plan.ts +24 -11
- package/src/orchestrator/plugin-host.ts +17 -23
- package/src/orchestrator/plugin.ts +31 -8
- package/src/orchestrator/prepare.ts +99 -13
- package/src/orchestrator/projects.ts +19 -8
- package/src/orchestrator/remote-prefetch.ts +21 -1
- package/src/orchestrator/run-artifacts.ts +9 -0
- package/src/orchestrator/run-context.ts +19 -4
- package/src/orchestrator/run-lock.ts +349 -0
- package/src/orchestrator/run-records.ts +6 -23
- package/src/orchestrator/run-report.ts +4 -2
- package/src/orchestrator/run.ts +264 -57
- package/src/orchestrator/sandbox-request.ts +388 -62
- package/src/orchestrator/save-lane.ts +4 -7
- package/src/orchestrator/shell-verdict.ts +117 -0
- package/src/orchestrator/signals.ts +86 -30
- package/src/orchestrator/stable-keys.ts +202 -10
- package/src/orchestrator/status-line.ts +33 -1
- package/src/orchestrator/summary.ts +49 -2
- package/src/orchestrator/task-hash.ts +87 -7
- package/src/orchestrator/task-log-buffer.ts +7 -7
- package/src/orchestrator/telemetry-host.ts +16 -7
- package/src/orchestrator/telemetry.ts +69 -28
- package/src/orchestrator/upstream.ts +91 -45
- package/src/util/bun-version.ts +43 -0
- package/src/util/errors.ts +86 -0
- package/src/util/hash.ts +10 -1
- package/src/util/index.ts +32 -3
- package/src/util/paths.ts +147 -5
- package/src/util/procfs.ts +20 -0
- package/src/util/settle.ts +4 -2
- package/src/util/size.ts +33 -0
- package/src/util/task-id.ts +20 -0
- package/src/util/timing.ts +11 -1
- package/src/util/which.ts +48 -0
- package/src/workspace/affected.ts +272 -64
- package/src/workspace/config-cache.ts +262 -32
- package/src/workspace/config-eval.ts +49 -12
- package/src/workspace/config-imports.ts +126 -5
- package/src/workspace/config-schema.ts +237 -41
- package/src/workspace/filter.ts +33 -18
- package/src/workspace/fingerprint.ts +29 -11
- package/src/workspace/index.ts +7 -0
- package/src/workspace/json-data.ts +90 -0
- package/src/workspace/load-reads.ts +32 -0
- package/src/workspace/lockfile.ts +33 -11
- package/src/workspace/migrate-scripts.ts +60 -7
- package/src/workspace/migration.ts +40 -1
- package/src/workspace/package-graph.ts +154 -16
- package/src/workspace/project-loader.ts +227 -77
- package/src/workspace/workspace.ts +173 -23
package/src/cli/upgrade.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// `vx upgrade [tag]` — self-update the compiled binary in place.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Asks the GitHub release API for the asset of this os/arch and the
|
|
3
|
+
// SHA-256 digest it publishes, downloads the asset, verifies the digest,
|
|
4
|
+
// writes next to the current executable, atomic rename over it. The
|
|
5
|
+
// digest is what makes a cut transfer (a proxy that drops a connection,
|
|
6
|
+
// a disk that fills) or a swapped asset a refusal instead of a binary
|
|
7
|
+
// that does not start: the rename is the last step, and a mismatch
|
|
8
|
+
// replaces nothing.
|
|
4
9
|
// Named `upgrade` (not `update`) per CLI convention: bun upgrade,
|
|
5
10
|
// deno upgrade — "update" is what package managers do to indexes.
|
|
6
11
|
|
|
@@ -35,6 +40,25 @@ function isCompiledBinary(): boolean {
|
|
|
35
40
|
)
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* The package directory that owns `execPath` when it sits under a
|
|
45
|
+
* `node_modules` — an npm install's platform binary, which the launcher
|
|
46
|
+
* runs as `…/node_modules/@vzn/vx-<os>-<arch>/vx`. That file is npm's:
|
|
47
|
+
* a rename over it upgrades this command until the next `npm install`
|
|
48
|
+
* (or a lockfile-pinned CI checkout) puts the version npm knows back,
|
|
49
|
+
* and `npm ls` never agrees with `vx --version` in between. Such an
|
|
50
|
+
* install updates through npm, and `vx upgrade` says so. Null for a
|
|
51
|
+
* binary installed by hand from a release.
|
|
52
|
+
*/
|
|
53
|
+
export function npmOwnedBinary(execPath: string): string | null {
|
|
54
|
+
const parts = execPath.split(/[\\/]/)
|
|
55
|
+
const at = parts.lastIndexOf('node_modules')
|
|
56
|
+
if (at === -1) return null
|
|
57
|
+
const scoped = parts[at + 1]?.startsWith('@') === true
|
|
58
|
+
const pkg = parts.slice(at + 1, at + (scoped ? 3 : 2)).join('/')
|
|
59
|
+
return pkg.length > 0 ? pkg : 'node_modules'
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
function assetName(): string {
|
|
39
63
|
const os = process.platform === 'darwin' ? 'darwin' : process.platform
|
|
40
64
|
const arch = process.arch === 'x64' || process.arch === 'arm64' ? process.arch : null
|
|
@@ -44,14 +68,102 @@ function assetName(): string {
|
|
|
44
68
|
return `vx-${os}-${arch}`
|
|
45
69
|
}
|
|
46
70
|
|
|
71
|
+
/** What the release API says about the asset this platform installs. */
|
|
72
|
+
export interface ReleaseAsset {
|
|
73
|
+
url: string
|
|
74
|
+
/** Lower-case hex SHA-256 of the asset's bytes. */
|
|
75
|
+
sha256: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Pick this platform's asset out of a release document and read the
|
|
80
|
+
* digest the API publishes for it (`sha256:<hex>` on every asset a
|
|
81
|
+
* release built through `release.yml` carries). A release without the
|
|
82
|
+
* asset, or an asset without a digest, is refused here: an upgrade that
|
|
83
|
+
* cannot be verified is not attempted.
|
|
84
|
+
*/
|
|
85
|
+
export function releaseAsset(release: unknown, name: string): ReleaseAsset {
|
|
86
|
+
const tag =
|
|
87
|
+
typeof release === 'object' &&
|
|
88
|
+
release !== null &&
|
|
89
|
+
typeof (release as { tag_name?: unknown }).tag_name === 'string'
|
|
90
|
+
? (release as { tag_name: string }).tag_name
|
|
91
|
+
: '(unknown)'
|
|
92
|
+
const assets =
|
|
93
|
+
typeof release === 'object' &&
|
|
94
|
+
release !== null &&
|
|
95
|
+
Array.isArray((release as { assets?: unknown }).assets)
|
|
96
|
+
? (release as { assets: unknown[] }).assets
|
|
97
|
+
: []
|
|
98
|
+
const asset = assets.find(
|
|
99
|
+
(a): a is { name: string; browser_download_url?: unknown; digest?: unknown } =>
|
|
100
|
+
typeof a === 'object' && a !== null && (a as { name?: unknown }).name === name,
|
|
101
|
+
)
|
|
102
|
+
if (asset === undefined) {
|
|
103
|
+
throw new UserError(`vx upgrade: release ${tag} has no asset ${name} for this platform`)
|
|
104
|
+
}
|
|
105
|
+
const url = asset.browser_download_url
|
|
106
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
107
|
+
throw new UserError(`vx upgrade: release ${tag} names no download for ${name}`)
|
|
108
|
+
}
|
|
109
|
+
const digest = asset.digest
|
|
110
|
+
const m = typeof digest === 'string' ? /^sha256:([0-9a-f]{64})$/i.exec(digest) : null
|
|
111
|
+
if (m === null) {
|
|
112
|
+
throw new UserError(
|
|
113
|
+
`vx upgrade: release ${tag} publishes no SHA-256 digest for ${name} — nothing to verify the download against, so it is not attempted`,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
return { url, sha256: m[1]!.toLowerCase() }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `fetch` on a box that cannot reach the host rejects with Bun's own
|
|
121
|
+
* `TypeError` ("Unable to connect. Is the computer able to access the
|
|
122
|
+
* url?", "Was there a typo in the url or port?"), which the CLI printed
|
|
123
|
+
* as an internal error with a stack (a network namespace without a
|
|
124
|
+
* route, 2026-09-16). One line, the host and the reason.
|
|
125
|
+
*/
|
|
126
|
+
async function fetchOrRefuse(url: string, init: RequestInit, what: string): Promise<Response> {
|
|
127
|
+
try {
|
|
128
|
+
return await fetch(url, init)
|
|
129
|
+
} catch (err) {
|
|
130
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
131
|
+
throw new UserError(
|
|
132
|
+
`vx upgrade: could not reach ${new URL(url).host} to ${what} (${message}) — check the network or the proxy and re-run`,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The release document for `latest` or a tag, from the GitHub API. */
|
|
138
|
+
async function fetchRelease(tag: string | undefined): Promise<unknown> {
|
|
139
|
+
const url =
|
|
140
|
+
tag === undefined
|
|
141
|
+
? `https://api.github.com/repos/${REPO}/releases/latest`
|
|
142
|
+
: `https://api.github.com/repos/${REPO}/releases/tags/${encodeURIComponent(tag)}`
|
|
143
|
+
const res = await fetchOrRefuse(
|
|
144
|
+
url,
|
|
145
|
+
{
|
|
146
|
+
redirect: 'follow',
|
|
147
|
+
headers: { Accept: 'application/vnd.github+json', 'User-Agent': `vx/${VERSION}` },
|
|
148
|
+
},
|
|
149
|
+
'read the release',
|
|
150
|
+
)
|
|
151
|
+
if (!res.ok) {
|
|
152
|
+
throw new UserError(
|
|
153
|
+
`vx upgrade: could not read the release (${res.status}) — ${url}${res.status === 404 && tag !== undefined ? ` (is ${tag} a release tag?)` : ''}`,
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
return (await res.json()) as unknown
|
|
157
|
+
}
|
|
158
|
+
|
|
47
159
|
/**
|
|
48
|
-
* Download `url
|
|
49
|
-
*
|
|
50
|
-
* file); the CLI wires it to the
|
|
51
|
-
* process.execPath.
|
|
160
|
+
* Download `url`, verify its SHA-256 against `sha256`, and atomically
|
|
161
|
+
* replace `dest` with it. Exported for tests (which stub `fetch` and
|
|
162
|
+
* point `dest` at a tmp file); the CLI wires it to the release asset
|
|
163
|
+
* and process.execPath.
|
|
52
164
|
*/
|
|
53
|
-
export async function replaceBinary(dest: string, url: string): Promise<void> {
|
|
54
|
-
const res = await
|
|
165
|
+
export async function replaceBinary(dest: string, url: string, sha256: string): Promise<void> {
|
|
166
|
+
const res = await fetchOrRefuse(url, { redirect: 'follow' }, 'download the release asset')
|
|
55
167
|
if (!res.ok) {
|
|
56
168
|
throw new UserError(`vx upgrade: download failed (${res.status}) — ${url}`)
|
|
57
169
|
}
|
|
@@ -59,6 +171,12 @@ export async function replaceBinary(dest: string, url: string): Promise<void> {
|
|
|
59
171
|
if (bytes.byteLength === 0) {
|
|
60
172
|
throw new UserError(`vx upgrade: empty download — ${url}`)
|
|
61
173
|
}
|
|
174
|
+
const got = new Bun.CryptoHasher('sha256').update(bytes).digest('hex')
|
|
175
|
+
if (got !== sha256.toLowerCase()) {
|
|
176
|
+
throw new UserError(
|
|
177
|
+
`vx upgrade: the download did not match the release's SHA-256 (expected ${sha256}, got ${got}) — nothing replaced; try again, and if it repeats the asset is not the one the release published`,
|
|
178
|
+
)
|
|
179
|
+
}
|
|
62
180
|
const tmp = `${dest}.upgrade-${process.pid}`
|
|
63
181
|
try {
|
|
64
182
|
await Bun.write(tmp, bytes)
|
|
@@ -88,14 +206,16 @@ export async function upgradeCmd(args: readonly string[]): Promise<number> {
|
|
|
88
206
|
'(npm installs update with: npm install -g @vzn/vx@latest)',
|
|
89
207
|
)
|
|
90
208
|
}
|
|
91
|
-
const asset = assetName()
|
|
92
|
-
const url =
|
|
93
|
-
tag === undefined
|
|
94
|
-
? `https://github.com/${REPO}/releases/latest/download/${asset}`
|
|
95
|
-
: `https://github.com/${REPO}/releases/download/${tag}/${asset}`
|
|
96
209
|
const dest = process.execPath
|
|
210
|
+
const owner = npmOwnedBinary(dest)
|
|
211
|
+
if (owner !== null) {
|
|
212
|
+
throw new UserError(
|
|
213
|
+
`vx upgrade: this vx was installed by npm (${dest} belongs to ${owner}) — npm owns that file, and the next npm install would put the version it knows back. Update with: npm install -g @vzn/vx@latest`,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
const asset = releaseAsset(await fetchRelease(tag), assetName())
|
|
97
217
|
process.stdout.write(`vx upgrade: ${VERSION} → ${tag ?? 'latest'} (${dest})\n`)
|
|
98
|
-
await replaceBinary(dest, url)
|
|
218
|
+
await replaceBinary(dest, asset.url, asset.sha256)
|
|
99
219
|
// Report the replaced binary's own version — the new build speaks
|
|
100
220
|
// for itself rather than this process guessing.
|
|
101
221
|
const proc = Bun.spawnSync({ cmd: [dest, '--version'], stdout: 'pipe', stderr: 'pipe' })
|