@vzn/vx 0.0.433 → 0.0.435
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 +13 -6
- package/src/exec/sandbox-runtime.ts +32 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.435",
|
|
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.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.435",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.435",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.435",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.435"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/show.ts
CHANGED
|
@@ -10,7 +10,7 @@ import path from 'node:path'
|
|
|
10
10
|
import type { ProjectConfig, TaskConfig } from '../config.js'
|
|
11
11
|
import { declaredTask } from '../graph/index.js'
|
|
12
12
|
import { flagHint, seeHelp } from './help.js'
|
|
13
|
-
import { nearMatches, relPosix, secretMask, UserError } from '../util/index.js'
|
|
13
|
+
import { listed, nearMatches, relPosix, secretMask, UserError } from '../util/index.js'
|
|
14
14
|
import { discoverCliProjects, loadCliProjects } from './workspace-config.js'
|
|
15
15
|
import {
|
|
16
16
|
findWorkspaceRoot,
|
|
@@ -94,7 +94,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
94
94
|
if (projectName !== undefined && (taskName !== undefined || byName.has(projectName))) {
|
|
95
95
|
if (!byName.has(projectName)) {
|
|
96
96
|
throw new UserError(
|
|
97
|
-
`vx show: unknown project: "${projectName}"${suggest(projectName, [...byName.keys()])}`,
|
|
97
|
+
`vx show: unknown project: "${projectName}"${suggest(projectName, [...byName.keys()], '', 'projects')}`,
|
|
98
98
|
)
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -121,7 +121,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
121
121
|
// `nx show projects` lists them; here a bare `vx show` does.
|
|
122
122
|
const nx = projectName === 'projects' ? ' (`nx show projects` is `vx show` here)' : ''
|
|
123
123
|
throw new UserError(
|
|
124
|
-
`vx show: unknown project or task: "${projectName}"${nx}${suggest(projectName!, [...byName.keys(), ...names])}`,
|
|
124
|
+
`vx show: unknown project or task: "${projectName}"${nx}${suggest(projectName!, [...byName.keys(), ...names], '', 'projects and tasks')}`,
|
|
125
125
|
)
|
|
126
126
|
}
|
|
127
127
|
process.stdout.write(renderTaskAcross(root, declaring, projectName!, parsed.format))
|
|
@@ -143,7 +143,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
143
143
|
const task = declaredTask(config, taskName)
|
|
144
144
|
if (task === undefined) {
|
|
145
145
|
throw new UserError(
|
|
146
|
-
`vx show: unknown task: "${meta.name}#${taskName}"${suggest(taskName, Object.keys(config?.tasks ?? {}), `${meta.name}
|
|
146
|
+
`vx show: unknown task: "${meta.name}#${taskName}"${suggest(taskName, Object.keys(config?.tasks ?? {}), `${meta.name}#`, 'its tasks')}`,
|
|
147
147
|
)
|
|
148
148
|
}
|
|
149
149
|
process.stdout.write(renderTask(meta.name, dir, taskName, task, parsed.format))
|
|
@@ -154,14 +154,21 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
154
154
|
* Near misses by edit distance, plus partial names in either direction.
|
|
155
155
|
* `prefix` makes each a spec the user can paste (`app#build`, not `build`).
|
|
156
156
|
*/
|
|
157
|
-
function suggest(
|
|
157
|
+
function suggest(
|
|
158
|
+
query: string,
|
|
159
|
+
candidates: readonly string[],
|
|
160
|
+
prefix = '',
|
|
161
|
+
label = 'names',
|
|
162
|
+
): string {
|
|
158
163
|
const q = query.toLowerCase()
|
|
159
164
|
const near = new Set(nearMatches(query, candidates))
|
|
160
165
|
for (const c of candidates) {
|
|
161
166
|
const n = c.toLowerCase()
|
|
162
167
|
if (n.includes(q) || q.includes(n)) near.add(c)
|
|
163
168
|
}
|
|
164
|
-
|
|
169
|
+
if (near.size > 0) return ` — did you mean ${[...near].map((n) => prefix + n).join(', ')}?`
|
|
170
|
+
// Nothing near named nothing to pick from (M-57): say what exists.
|
|
171
|
+
return candidates.length === 0 ? '' : `; ${label}: ${listed(candidates.map((c) => prefix + c))}`
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
function projectDir(root: string, meta: ProjectMeta): string {
|
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
lstatSync,
|
|
33
33
|
existsSync,
|
|
34
34
|
mkdirSync,
|
|
35
|
-
mkdtempSync,
|
|
36
35
|
openSync,
|
|
37
36
|
readFileSync,
|
|
38
37
|
readlinkSync,
|
|
@@ -538,19 +537,42 @@ function scanCommandAgain(): string {
|
|
|
538
537
|
}
|
|
539
538
|
}
|
|
540
539
|
/**
|
|
541
|
-
* An empty executable only this
|
|
542
|
-
* sandbox on every wrap, so it lives in
|
|
543
|
-
* shared `/tmp/claude`.
|
|
540
|
+
* An empty executable only this user can write: SRT runs it outside the
|
|
541
|
+
* sandbox on every wrap, so it lives in this user's own 0700 directory under
|
|
542
|
+
* the OS temp dir, never the shared `/tmp/claude`. One directory per user,
|
|
543
|
+
* not per process: a per-process one outlived every SIGKILLed run, and the
|
|
544
|
+
* exit hook that removed it fired under a test's emitted `exit` with the
|
|
545
|
+
* process going on (B-93). Anything else at the name (another owner, group
|
|
546
|
+
* or other bits, a link, a non-empty file) is refused, and the caller falls
|
|
547
|
+
* back to `true`.
|
|
544
548
|
*/
|
|
545
549
|
function noScanCommand(): string {
|
|
546
550
|
if (noScan !== undefined && existsSync(noScan)) return noScan
|
|
547
|
-
const
|
|
551
|
+
const uid = process.getuid!()
|
|
552
|
+
const dir = path.join(os.tmpdir(), `vx-noscan-${uid}`)
|
|
553
|
+
try {
|
|
554
|
+
mkdirSync(dir, { mode: 0o700 })
|
|
555
|
+
} catch (err) {
|
|
556
|
+
if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err
|
|
557
|
+
}
|
|
558
|
+
const d = lstatSync(dir)
|
|
559
|
+
// A link reads mode 0777 here, and a file at the name fails the write
|
|
560
|
+
// below (ENOTDIR).
|
|
561
|
+
if (d.uid !== uid || (d.mode & 0o077) !== 0) {
|
|
562
|
+
throw new Error(`${dir} is not this user's own directory`)
|
|
563
|
+
}
|
|
548
564
|
const file = path.join(dir, 'rg')
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
565
|
+
try {
|
|
566
|
+
writeFileSync(file, '', { mode: 0o700, flag: 'wx' })
|
|
567
|
+
} catch (err) {
|
|
568
|
+
if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err
|
|
569
|
+
}
|
|
570
|
+
const f = lstatSync(file)
|
|
571
|
+
// SRT's dependency check (`Bun.which`) refuses what is not an executable
|
|
572
|
+
// file, which would make the sandbox unavailable rather than fall back. A
|
|
573
|
+
// directory's size is never 0.
|
|
574
|
+
if (f.size !== 0 || (f.mode & 0o100) === 0) {
|
|
575
|
+
throw new Error(`${file} is not an empty executable of this user's`)
|
|
554
576
|
}
|
|
555
577
|
noScan = file
|
|
556
578
|
return file
|