@vzn/vx 0.0.651 → 0.0.653
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 +3 -4
- package/launcher.sh +2 -1
- package/package.json +5 -5
- package/schemas/error.json +1 -1
- package/schemas/show.json +15 -20
- package/skills/vx/SKILL.md +3 -3
- package/src/bin.ts +37 -3
- package/src/cache/archive.ts +197 -32
- package/src/cache/cache.ts +882 -230
- package/src/cache/checkpointer.ts +102 -0
- package/src/cache/file-hashes.ts +2 -1
- package/src/cache/fold-memo.ts +24 -4
- package/src/cache/fs-clock.ts +26 -0
- package/src/cache/git-inputs.ts +188 -22
- package/src/cache/history-stream.ts +151 -0
- package/src/cache/history-writer.ts +76 -0
- package/src/cache/index.ts +12 -1
- package/src/cache/inputs.ts +616 -135
- package/src/cache/key-fold.ts +1 -1
- package/src/cache/layer.ts +29 -16
- package/src/cache/layered-cache.ts +28 -7
- package/src/cache/lockfile-memo.ts +2 -1
- package/src/cache/output-index.ts +106 -44
- package/src/cache/plugin-cache.ts +2 -2
- package/src/cache/policy.ts +6 -2
- package/src/cache/remote-lane.ts +3 -1
- package/src/cache/run-history.ts +94 -25
- package/src/cache/schema.ts +46 -10
- package/src/cli/cache.ts +149 -36
- package/src/cli/completions.ts +2 -2
- package/src/cli/core-alias.ts +9 -2
- package/src/cli/docs.ts +4 -2
- package/src/cli/help.ts +27 -4
- package/src/cli/index.ts +62 -7
- package/src/cli/info.ts +5 -4
- package/src/cli/init.ts +55 -20
- package/src/cli/last.ts +218 -43
- package/src/cli/lock.ts +187 -24
- package/src/cli/plan-format.ts +24 -21
- package/src/cli/plugin-commands.ts +6 -1
- package/src/cli/plugin-templates.ts +36 -45
- package/src/cli/run.ts +68 -34
- package/src/cli/select.ts +160 -64
- package/src/cli/show.ts +70 -28
- package/src/cli/task-verb.ts +12 -7
- package/src/cli/upgrade.ts +210 -76
- package/src/cli/watch-filter.ts +88 -15
- package/src/cli/watch-fs.ts +34 -47
- package/src/cli/watch-judge.ts +73 -9
- package/src/cli/watch-set.ts +10 -8
- package/src/cli/watch.ts +190 -33
- package/src/cli/why.ts +107 -33
- package/src/cli/workspace-config.ts +11 -1
- package/src/config.ts +56 -30
- package/src/exec/env.ts +3 -1
- package/src/exec/executor.ts +116 -16
- package/src/exec/index.ts +1 -0
- package/src/exec/kill-tree.ts +7 -7
- package/src/exec/runner.ts +270 -108
- package/src/exec/sandbox-binds.ts +109 -18
- package/src/exec/sandbox-paths.ts +3 -1
- package/src/exec/sandbox-runtime.ts +446 -64
- package/src/exec/sandbox-violations.ts +250 -46
- package/src/exec/spawn-off.ts +10 -5
- package/src/exec/task-proxy.ts +93 -0
- package/src/exec/warm-shell.ts +29 -7
- package/src/graph/definition.ts +16 -3
- package/src/graph/folds.ts +89 -16
- package/src/graph/index.ts +8 -0
- package/src/graph/priorities.ts +8 -5
- package/src/graph/scheduler.ts +176 -39
- package/src/graph/stand-ins.ts +36 -0
- package/src/graph/task-graph.ts +347 -160
- package/src/graph/task-status.ts +5 -1
- package/src/handoff.ts +45 -10
- package/src/history/failure-mode.ts +39 -6
- package/src/history/history.ts +103 -30
- package/src/history/index.ts +2 -0
- package/src/history/metrics.ts +193 -73
- package/src/index.ts +6 -3
- package/src/orchestrator/admission.ts +14 -10
- package/src/orchestrator/affected-tasks.ts +35 -13
- package/src/orchestrator/command-windows.ts +8 -5
- package/src/orchestrator/deferred-outputs.ts +42 -13
- package/src/orchestrator/doctor.ts +21 -9
- package/src/orchestrator/download-policy.ts +8 -10
- package/src/orchestrator/excluded-keys.ts +15 -3
- package/src/orchestrator/execute-args.ts +9 -2
- package/src/orchestrator/execute-task.ts +612 -138
- package/src/orchestrator/graph-memo.ts +26 -4
- package/src/orchestrator/graph-stage.ts +27 -4
- package/src/orchestrator/held-servers.ts +36 -14
- package/src/orchestrator/hit-restore.ts +141 -93
- package/src/orchestrator/index.ts +4 -1
- package/src/orchestrator/keyed-projects.ts +53 -0
- package/src/orchestrator/local-floor.ts +21 -3
- package/src/orchestrator/local-shortcircuit.ts +2 -2
- package/src/orchestrator/lockfile-claim.ts +30 -4
- package/src/orchestrator/miss-save.ts +60 -61
- package/src/orchestrator/options.ts +13 -0
- package/src/orchestrator/output-host.ts +16 -4
- package/src/orchestrator/output-log.ts +1 -54
- package/src/orchestrator/persistent-task.ts +32 -16
- package/src/orchestrator/persistent.ts +152 -15
- package/src/orchestrator/placement.ts +82 -16
- package/src/orchestrator/plan.ts +39 -17
- package/src/orchestrator/plugin-host.ts +238 -110
- package/src/orchestrator/plugin.ts +59 -27
- package/src/orchestrator/prepare.ts +194 -38
- package/src/orchestrator/projects.ts +89 -10
- package/src/orchestrator/read-only.ts +115 -0
- package/src/orchestrator/remote-prefetch.ts +1 -1
- package/src/orchestrator/run-context.ts +6 -2
- package/src/orchestrator/run-failures.ts +101 -23
- package/src/orchestrator/run-id.ts +13 -2
- package/src/orchestrator/run-keys.ts +87 -28
- package/src/orchestrator/run-live.ts +16 -13
- package/src/orchestrator/run-lock.ts +27 -4
- package/src/orchestrator/run-records.ts +95 -74
- package/src/orchestrator/run-report.ts +3 -2
- package/src/orchestrator/run-stop.ts +17 -9
- package/src/orchestrator/run.ts +531 -122
- package/src/orchestrator/sandbox-fix.ts +40 -12
- package/src/orchestrator/sandbox-request.ts +314 -44
- package/src/orchestrator/save-lane.ts +24 -13
- package/src/orchestrator/shell-verdict.ts +29 -1
- package/src/orchestrator/signals.ts +103 -8
- package/src/orchestrator/stable-keys.ts +34 -27
- package/src/orchestrator/task-artifacts.ts +16 -3
- package/src/orchestrator/task-hash.ts +191 -34
- package/src/orchestrator/telemetry-host.ts +25 -6
- package/src/orchestrator/telemetry.ts +85 -13
- package/src/orchestrator/upstream.ts +3 -1
- package/src/report/events.ts +23 -6
- package/src/report/forecast.ts +103 -37
- package/src/report/framed-output.ts +179 -162
- package/src/report/index.ts +2 -1
- package/src/report/logger.ts +108 -31
- package/src/report/plain-output.ts +15 -9
- package/src/report/status-line.ts +28 -55
- package/src/report/summary.ts +61 -18
- package/src/settings.ts +50 -14
- package/src/ui/components.ts +36 -42
- package/src/ui/index.ts +3 -1
- package/src/ui/live-region.ts +67 -8
- package/src/ui/terminal.ts +12 -0
- package/src/ui/theme.ts +16 -1
- package/src/util/config-frame.ts +142 -6
- package/src/util/edit-distance.ts +17 -1
- package/src/util/errors.ts +23 -7
- package/src/util/foreign-cache-dir.ts +60 -0
- package/src/util/git-ignored.ts +59 -5
- package/src/util/index.ts +15 -1
- package/src/util/paths.ts +51 -7
- package/src/util/plain-text.ts +19 -0
- package/src/util/secret-mask.ts +57 -1
- package/src/util/settle.ts +18 -1
- package/src/util/size.ts +12 -2
- package/src/util/tail.ts +4 -1
- package/src/util/write-file.ts +86 -9
- package/src/workspace/affected.ts +225 -27
- package/src/workspace/bracket-literal.ts +2 -2
- package/src/workspace/config-bytecode.ts +23 -4
- package/src/workspace/config-eval.ts +90 -16
- package/src/workspace/config-imports.ts +96 -41
- package/src/workspace/config-schema.ts +192 -86
- package/src/workspace/dependencies-lock.ts +7 -14
- package/src/workspace/filter.ts +18 -2
- package/src/workspace/index.ts +3 -1
- package/src/workspace/json-data.ts +34 -8
- package/src/workspace/lockfile.ts +86 -37
- package/src/workspace/migration.ts +58 -31
- package/src/workspace/module-state.ts +62 -0
- package/src/workspace/nested-dirs.ts +8 -0
- package/src/workspace/project-loader.ts +276 -32
- package/src/workspace/repo-id.ts +29 -6
- package/src/workspace/workspace.ts +190 -30
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
toRealPath,
|
|
14
14
|
unique,
|
|
15
15
|
} from './sandbox-paths.js'
|
|
16
|
-
import
|
|
16
|
+
import { shellQuote } from './runner.js'
|
|
17
|
+
import type { ResolvedSandboxConfig, SandboxedRunArgs } from './sandbox-runtime.js'
|
|
17
18
|
import * as ui from '../ui/index.js'
|
|
18
19
|
|
|
19
20
|
type SrtModule = typeof import('@anthropic-ai/sandbox-runtime')
|
|
@@ -40,6 +41,48 @@ export function bindableWrites(paths: readonly string[]): string[] {
|
|
|
40
41
|
return unique(paths.filter((p) => !holdsGlobChar(p)).map(bindOf))
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Linux: each write grant spelled through a link in the project, as the
|
|
46
|
+
* bind that puts it where the task names it: `[source, at]`, the grant's
|
|
47
|
+
* own bind and its twin under the link. A file grant binds its directory,
|
|
48
|
+
* so its twin is the link's; a link that is the file itself sits in the
|
|
49
|
+
* bound directory already and needs none. SRT skips a write path that is
|
|
50
|
+
* a sibling link (it reads one as leaving its tree), so vx lays these
|
|
51
|
+
* itself (`withLinkedWrites`).
|
|
52
|
+
*/
|
|
53
|
+
export function linkedWriteBinds(
|
|
54
|
+
linked: ResolvedSandboxConfig['linkedWrites'],
|
|
55
|
+
): Array<readonly [string, string]> {
|
|
56
|
+
if (process.platform !== 'linux' || linked === undefined) return []
|
|
57
|
+
const out: Array<readonly [string, string]> = []
|
|
58
|
+
for (const { link, target } of linked) {
|
|
59
|
+
if (holdsGlobChar(target) || holdsGlobChar(link)) continue
|
|
60
|
+
const source = bindOf(target)
|
|
61
|
+
const at = source === target ? link : path.dirname(link)
|
|
62
|
+
if (at !== source && !out.some(([, a]) => a === at)) out.push([source, at])
|
|
63
|
+
}
|
|
64
|
+
return out
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Linux: SRT's bwrap line with each `linkedWriteBinds` pair bound last,
|
|
69
|
+
* before the `--` that ends bwrap's options: after the masks and the read
|
|
70
|
+
* grants, so none lands over it. The link's directory is then a mount of
|
|
71
|
+
* its target, writable as the grant is.
|
|
72
|
+
*/
|
|
73
|
+
export function withLinkedWrites(
|
|
74
|
+
wrapped: string,
|
|
75
|
+
binds: ReadonlyArray<readonly [string, string]>,
|
|
76
|
+
): string {
|
|
77
|
+
if (binds.length === 0) return wrapped
|
|
78
|
+
for (const m of wrapped.matchAll(/(?:'[^']*'|"[^"]*"|[^\s'"])+/g)) {
|
|
79
|
+
if (m[0] !== '--') continue
|
|
80
|
+
const words = binds.map(([s, a]) => `--bind ${shellQuote(s)} ${shellQuote(a)} `).join('')
|
|
81
|
+
return `${wrapped.slice(0, m.index)}${words}${wrapped.slice(m.index)}`
|
|
82
|
+
}
|
|
83
|
+
return wrapped
|
|
84
|
+
}
|
|
85
|
+
|
|
43
86
|
function bindOf(p: string): string {
|
|
44
87
|
if (!isMountableLiteral(p)) return p
|
|
45
88
|
try {
|
|
@@ -162,7 +205,12 @@ const warnedSymlinkPunch = new Set<string>()
|
|
|
162
205
|
* A grant with no write path under it is returned untouched — the common
|
|
163
206
|
* case costs nothing, not even a readdir.
|
|
164
207
|
*/
|
|
165
|
-
export function punchWritePaths(
|
|
208
|
+
export function punchWritePaths(
|
|
209
|
+
readPath: string,
|
|
210
|
+
writePaths: readonly string[],
|
|
211
|
+
/** Whether a link child's real path may be bound; one it may not is dropped. */
|
|
212
|
+
reachable: (real: string) => boolean = () => true,
|
|
213
|
+
): string[] {
|
|
166
214
|
// Linux only. The shadowing is a property of bwrap MOUNTS; macOS seatbelt
|
|
167
215
|
// evaluates rules by precedence, so a read grant on a directory and a
|
|
168
216
|
// write grant inside it coexist. Punching there costs the directory
|
|
@@ -193,25 +241,57 @@ export function punchWritePaths(readPath: string, writePaths: readonly string[])
|
|
|
193
241
|
// SRT's config carries no `--symlink`, so the only fix is the grant:
|
|
194
242
|
// say which one, loudly.
|
|
195
243
|
try {
|
|
196
|
-
if (lstatSync(child).isSymbolicLink())
|
|
244
|
+
if (lstatSync(child).isSymbolicLink()) {
|
|
245
|
+
// Bound by its target, a link out to a denied place (`node_modules
|
|
246
|
+
// -> ..`, `shared -> ../b`) granted what the whole grant never did:
|
|
247
|
+
// bound unpunched, the link stays a link into the deny (item 1006's
|
|
248
|
+
// glob hit, through the punch).
|
|
249
|
+
if (!reachable(toRealPath(child))) continue
|
|
250
|
+
linked.push(child)
|
|
251
|
+
}
|
|
197
252
|
} catch {
|
|
198
253
|
// vanished between readdir and lstat: nothing to bind either way
|
|
199
254
|
}
|
|
200
|
-
out.push(...punchWritePaths(child, under))
|
|
255
|
+
out.push(...punchWritePaths(child, under, reachable))
|
|
201
256
|
}
|
|
202
257
|
if (linked.length > 0 && !warnedSymlinkPunch.has(readPath)) {
|
|
203
258
|
warnedSymlinkPunch.add(readPath)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
259
|
+
// A link INTO a write grant is that grant under a second name, mounted
|
|
260
|
+
// read-only: the remedy is to grant the name (`linkedWriteBinds` binds
|
|
261
|
+
// it writable), not to move the grant.
|
|
262
|
+
const plain: string[] = []
|
|
263
|
+
for (const l of linked) {
|
|
264
|
+
const w = writePaths.find((w) => atOrUnder(toRealPath(l), w))
|
|
265
|
+
if (w === undefined) {
|
|
266
|
+
plain.push(l)
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
269
|
+
let dir = false
|
|
270
|
+
try {
|
|
271
|
+
dir = statSync(l).isDirectory()
|
|
272
|
+
} catch {
|
|
273
|
+
// dangling: granted as a file
|
|
274
|
+
}
|
|
275
|
+
ui.say(
|
|
276
|
+
'warn',
|
|
277
|
+
`[vx] sandbox: ${l} is a symlink into the write grant ${w}, and the Linux sandbox ` +
|
|
278
|
+
`mounts it read-only, so a write through it is refused. Grant the link itself to ` +
|
|
279
|
+
`write through it: ${l}${dir ? '/' : ''}\n`,
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
if (plain.length > 0) {
|
|
283
|
+
ui.say(
|
|
284
|
+
'warn',
|
|
285
|
+
`[vx] sandbox: a write grant under ${readPath} makes its ${plain.length} symlinked ` +
|
|
286
|
+
`entr${plain.length === 1 ? 'y' : 'ies'} (${plain
|
|
287
|
+
.slice(0, 3)
|
|
288
|
+
.map((l) => path.basename(l))
|
|
289
|
+
.join(', ')}${plain.length > 3 ? ', …' : ''}) plain directories inside the sandbox — ` +
|
|
290
|
+
`a package resolved through one loses its siblings. Move the write grant (${under
|
|
291
|
+
.map((w) => path.relative(readPath, w))
|
|
292
|
+
.join(', ')}) out of it.\n`,
|
|
293
|
+
)
|
|
294
|
+
}
|
|
215
295
|
}
|
|
216
296
|
return out
|
|
217
297
|
}
|
|
@@ -300,9 +380,20 @@ export function buildCustomConfig(
|
|
|
300
380
|
const c = args.config
|
|
301
381
|
const denyRead = unique([...baselines.denyRead])
|
|
302
382
|
const allowWrite = bindableWrites(unique([...c.allowWrite]))
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
)
|
|
383
|
+
// A link a write grant is spelled through is bound by vx, not SRT: a
|
|
384
|
+
// read grant above it is punched around it all the same.
|
|
385
|
+
const punched = [...allowWrite, ...linkedWriteBinds(c.linkedWrites).map(([, at]) => at)]
|
|
386
|
+
const reads = [...baselines.allowRead, ...c.allowRead]
|
|
387
|
+
// Asked only of a link a punch meets: a target under the deny anchor is
|
|
388
|
+
// bound only where some read grant already reaches it.
|
|
389
|
+
let fence: { denied: string[]; granted: string[] } | undefined
|
|
390
|
+
const reachable = (real: string): boolean => {
|
|
391
|
+
fence ??= { denied: denyRead.map(toRealPath), granted: reads.map(toRealPath) }
|
|
392
|
+
return (
|
|
393
|
+
!fence.denied.some((d) => atOrUnder(real, d)) || fence.granted.some((g) => atOrUnder(real, g))
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
const allowRead = unique(reads.flatMap((r) => punchWritePaths(r, punched, reachable)))
|
|
306
397
|
|
|
307
398
|
const custom: Parameters<SrtModule['SandboxManager']['wrapWithSandbox']>[2] = {
|
|
308
399
|
filesystem: {
|
|
@@ -102,6 +102,8 @@ export function sandboxReads(
|
|
|
102
102
|
readonly config: {
|
|
103
103
|
readonly allowRead: readonly string[]
|
|
104
104
|
readonly allowWrite: readonly string[]
|
|
105
|
+
/** Denies the whole host, not only `baseDenyRead`: only a grant reads. */
|
|
106
|
+
readonly hermetic?: boolean
|
|
105
107
|
}
|
|
106
108
|
},
|
|
107
109
|
file: string,
|
|
@@ -109,7 +111,7 @@ export function sandboxReads(
|
|
|
109
111
|
const real = toRealPath(file)
|
|
110
112
|
const under = (g: string): boolean => atOrUnder(real, toRealPath(g))
|
|
111
113
|
return (
|
|
112
|
-
!sandbox.baseDenyRead.some(under) ||
|
|
114
|
+
(sandbox.config.hermetic !== true && !sandbox.baseDenyRead.some(under)) ||
|
|
113
115
|
[...sandbox.baseAllowRead, ...sandbox.config.allowRead, ...sandbox.config.allowWrite].some(
|
|
114
116
|
under,
|
|
115
117
|
)
|