@vzn/vx 0.0.98 → 0.0.100
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/package.json +5 -5
- package/src/cli/show.ts +6 -1
- package/src/cli/upgrade.ts +10 -4
- package/src/exec/sandbox-runtime.ts +18 -2
- package/src/workspace/migration.ts +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.100",
|
|
4
4
|
"description": "A fast, correct task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.100",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.100",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.100",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.100"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/show.ts
CHANGED
|
@@ -70,7 +70,12 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
70
70
|
? parsed.target
|
|
71
71
|
: parsed.target.slice(0, hashAt)
|
|
72
72
|
const taskName = hashAt === -1 ? undefined : parsed.target!.slice(hashAt + 1)
|
|
73
|
-
if (taskName === '')
|
|
73
|
+
if (taskName === '') {
|
|
74
|
+
const list = projectName === '' ? 'vx show' : `vx show ${projectName}`
|
|
75
|
+
throw new UserError(
|
|
76
|
+
`vx show: missing task name after '#' in "${parsed.target}" (${list} lists the tasks)`,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
74
79
|
if (projectName !== undefined && (taskName !== undefined || byName.has(projectName))) {
|
|
75
80
|
if (!byName.has(projectName)) {
|
|
76
81
|
throw new UserError(
|
package/src/cli/upgrade.ts
CHANGED
|
@@ -105,11 +105,15 @@ export function releaseAsset(release: unknown, name: string): ReleaseAsset {
|
|
|
105
105
|
typeof a === 'object' && a !== null && (a as { name?: unknown }).name === name,
|
|
106
106
|
)
|
|
107
107
|
if (asset === undefined) {
|
|
108
|
-
throw new UserError(
|
|
108
|
+
throw new UserError(
|
|
109
|
+
`vx upgrade: release ${tag} has no asset ${name} for this platform — nothing replaced; npm install -g @vzn/vx@latest installs it instead`,
|
|
110
|
+
)
|
|
109
111
|
}
|
|
110
112
|
const url = asset.browser_download_url
|
|
111
113
|
if (typeof url !== 'string' || url.length === 0) {
|
|
112
|
-
throw new UserError(
|
|
114
|
+
throw new UserError(
|
|
115
|
+
`vx upgrade: release ${tag} names no download for ${name} — nothing replaced; npm install -g @vzn/vx@latest installs it instead`,
|
|
116
|
+
)
|
|
113
117
|
}
|
|
114
118
|
const digest = asset.digest
|
|
115
119
|
const m = typeof digest === 'string' ? /^sha256:([0-9a-f]{64})$/i.exec(digest) : null
|
|
@@ -217,13 +221,15 @@ export async function replaceBinary(
|
|
|
217
221
|
): Promise<void> {
|
|
218
222
|
const res = await fetchOrRefuse(url, { redirect: 'follow' }, 'download the release asset')
|
|
219
223
|
if (!res.ok) {
|
|
220
|
-
throw new UserError(
|
|
224
|
+
throw new UserError(
|
|
225
|
+
`vx upgrade: download failed (${res.status}) — ${url} — nothing replaced; re-run`,
|
|
226
|
+
)
|
|
221
227
|
}
|
|
222
228
|
const bytes = new Uint8Array(
|
|
223
229
|
await readOrRefuse(() => res.arrayBuffer(), url, 'download the release asset'),
|
|
224
230
|
)
|
|
225
231
|
if (bytes.byteLength === 0) {
|
|
226
|
-
throw new UserError(`vx upgrade: empty download — ${url}`)
|
|
232
|
+
throw new UserError(`vx upgrade: empty download — ${url} — nothing replaced; re-run`)
|
|
227
233
|
}
|
|
228
234
|
const got = new Bun.CryptoHasher('sha256').update(bytes).digest('hex')
|
|
229
235
|
if (got !== sha256.toLowerCase()) {
|
|
@@ -340,7 +340,18 @@ function unlinkOnExit(file: string): void {
|
|
|
340
340
|
* that names it outright still reaches it.
|
|
341
341
|
*/
|
|
342
342
|
function taskTmpdir(tag: string): string {
|
|
343
|
-
return path.join(
|
|
343
|
+
return path.join(taskTmpRoot(), `vx-task-${process.pid}-${tag}`)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The parent of every task's own temp directory, walled off from every
|
|
348
|
+
* sandboxed task, each granted its own directory inside: SRT binds its
|
|
349
|
+
* whole temp dir writable into every task, so a task listed and read what
|
|
350
|
+
* a concurrent one kept in its TMPDIR, or replaced its port bridge's
|
|
351
|
+
* socket (L-10).
|
|
352
|
+
*/
|
|
353
|
+
function taskTmpRoot(): string {
|
|
354
|
+
return path.join(sandboxTmpdir(), 'vx-tasks')
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
const liveTaskTmpdirs = new Set<string>()
|
|
@@ -799,6 +810,11 @@ export async function wrapSandboxedCommand(
|
|
|
799
810
|
|
|
800
811
|
const baselines = canonicalBaselines(args)
|
|
801
812
|
const customConfig = buildCustomConfig(args, baselines)
|
|
813
|
+
customConfig!.filesystem!.denyRead!.push(toRealPath(taskTmpRoot()))
|
|
814
|
+
customConfig!.filesystem!.allowWrite!.push(toRealPath(tmp))
|
|
815
|
+
// Seatbelt re-allows a read inside a denied region only by name; on Linux
|
|
816
|
+
// a read grant over a write path would remount it read-only.
|
|
817
|
+
if (process.platform === 'darwin') customConfig!.filesystem!.allowRead!.push(toRealPath(tmp))
|
|
802
818
|
// Linux: the ports a list grants are bridged out of the task's network
|
|
803
819
|
// namespace. The task's side of each bridge is a socat in front of the
|
|
804
820
|
// user command, so it goes INTO the sandboxed command; the host side is
|
|
@@ -972,7 +988,7 @@ export function bridgedPorts(c: Pick<ResolvedSandboxConfig, 'localBinding'>): nu
|
|
|
972
988
|
|
|
973
989
|
/** Where a bridge's unix socket lives: the sandbox tmpdir, bound read-write on both sides. */
|
|
974
990
|
export function portBridgeSocket(tag: string, port: number): string {
|
|
975
|
-
return path.join(
|
|
991
|
+
return path.join(taskTmpdir(tag), `vx-port-${tag}-${port}.sock`)
|
|
976
992
|
}
|
|
977
993
|
|
|
978
994
|
/**
|
|
@@ -357,7 +357,13 @@ function renderConfigFile(
|
|
|
357
357
|
if (p.importLines.length > 0) lines.push(...p.importLines)
|
|
358
358
|
lines.push('', 'export default {', ' tasks: {')
|
|
359
359
|
for (const t of p.tasks) {
|
|
360
|
-
|
|
360
|
+
// A reason can quote a manifest's own text (an Nx `env` key), and a
|
|
361
|
+
// line break there ended the comment: the rest was code in the file
|
|
362
|
+
// the next run evaluates (D-21). Each line stays inside the comment.
|
|
363
|
+
for (const todo of t.todos) {
|
|
364
|
+
const [first, ...rest] = todo.split(/\r\n|[\n\r\u2028\u2029]/)
|
|
365
|
+
lines.push(` // TODO(vx-migrate): ${first}`, ...rest.map((l) => ` // ${l}`))
|
|
366
|
+
}
|
|
361
367
|
if (t.task === null) continue // skipped target — the TODO above explains
|
|
362
368
|
// `__proto__: {…}` in a literal SETS the prototype, quoted or not, and
|
|
363
369
|
// the run refused the config (item 1033); a computed key is a property.
|