@vzn/vx 0.0.598 → 0.0.600
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.600",
|
|
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.600",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.600",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.600",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.600"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/archive.ts
CHANGED
|
@@ -209,6 +209,9 @@ interface PackArgs {
|
|
|
209
209
|
within?: string
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
/** The largest second count ustar's 12-byte mtime field holds: 11 octal digits. */
|
|
213
|
+
const USTAR_MTIME_MAX = 8 ** 11 - 1
|
|
214
|
+
|
|
212
215
|
/** At or below this many outputs, `planArtifact` stats them on the calling thread. */
|
|
213
216
|
const ON_THREAD_STATS = 32
|
|
214
217
|
|
|
@@ -309,10 +312,10 @@ export async function planArtifact(args: PackArgs): Promise<ArtifactPlan> {
|
|
|
309
312
|
size: st.size,
|
|
310
313
|
mode,
|
|
311
314
|
mtimeMs: Math.floor(st.mtimeMs),
|
|
312
|
-
// ustar's octal field holds no sign: an mtime before
|
|
313
|
-
// header unreadable and
|
|
314
|
-
// sidecar above carries the real value.
|
|
315
|
-
mtime: Math.max(0, Math.floor(st.mtimeMs / 1000)),
|
|
315
|
+
// ustar's octal field holds no sign and 11 digits: an mtime before
|
|
316
|
+
// 1970 made the header unreadable, and one past March 2242 failed
|
|
317
|
+
// every save of the task. The sidecar above carries the real value.
|
|
318
|
+
mtime: Math.min(USTAR_MTIME_MAX, Math.max(0, Math.floor(st.mtimeMs / 1000))),
|
|
316
319
|
}
|
|
317
320
|
}),
|
|
318
321
|
)
|
package/src/cache/cache.ts
CHANGED
|
@@ -1570,13 +1570,14 @@ export class Cache implements CacheLayer {
|
|
|
1570
1570
|
} catch (err) {
|
|
1571
1571
|
// A UserError here is the extractor naming the tree's fault (an
|
|
1572
1572
|
// output directory that links out of the project).
|
|
1573
|
-
if (
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1573
|
+
if (err instanceof CorruptArtifactError || err instanceof UserError) throw err
|
|
1574
|
+
// The save and the ingest proved every name safe, so one that reads
|
|
1575
|
+
// unsafe now is damaged or tampered bytes: a long name's pax record
|
|
1576
|
+
// has no header checksum, and the name is judged before the CRC at
|
|
1577
|
+
// the end is read. Thrown as is, it failed the task on every run with
|
|
1578
|
+
// the entry kept (X-115).
|
|
1579
|
+
if (err instanceof ArchiveSecurityError)
|
|
1580
|
+
throw new CorruptArtifactError(hash, err.message, err)
|
|
1580
1581
|
// What is on disk, not what is in the archive: a directory standing
|
|
1581
1582
|
// where the entry holds a file, or a file where it needs a directory.
|
|
1582
1583
|
// The clean removes the FILES the output globs select and the
|
|
@@ -1554,6 +1554,12 @@ function literalReadPaths(
|
|
|
1554
1554
|
* `/**` is stripped before the compile). A task's read grant whose only
|
|
1555
1555
|
* brackets are escaped names one path too, and Linux binds it whole, so
|
|
1556
1556
|
* it gets the same subtree: `out/\[id\]` read `out/[id]` and none of it.
|
|
1557
|
+
*
|
|
1558
|
+
* A `*` or `?` meant as itself (escaped in a grant, or in a baseline's
|
|
1559
|
+
* name) has no spelling: SRT's rewrite of each runs inside a class too,
|
|
1560
|
+
* and a backslash compiles as a literal one, so `a\*.txt` granted
|
|
1561
|
+
* `a\bc.txt` and never `a*.txt`. Left out and said once, as on Linux
|
|
1562
|
+
* (`bindableReads`).
|
|
1557
1563
|
*/
|
|
1558
1564
|
export function seatbeltBrackets(
|
|
1559
1565
|
config: Parameters<SrtModule['SandboxManager']['wrapWithSandbox']>[2],
|
|
@@ -1563,7 +1569,9 @@ export function seatbeltBrackets(
|
|
|
1563
1569
|
if (fs === undefined) return config
|
|
1564
1570
|
const literal = (p: string): string => p.replaceAll('\\[', '[[]').replaceAll('\\]', ']')
|
|
1565
1571
|
const bracketed = new Set(names.filter((n) => /[[\]]/.test(n)))
|
|
1572
|
+
const baseline = new Set(names)
|
|
1566
1573
|
const read = (p: string): string[] => {
|
|
1574
|
+
if (!seatbeltSpells(p, 'read', baseline.has(p))) return []
|
|
1567
1575
|
const name = bracketed.has(p) ? p : /\\[[\]]/.test(p) ? namedPath(p) : undefined
|
|
1568
1576
|
if (name === undefined) return [literal(p)]
|
|
1569
1577
|
const at = name.replaceAll('[', '[[]')
|
|
@@ -1574,7 +1582,7 @@ export function seatbeltBrackets(
|
|
|
1574
1582
|
filesystem: {
|
|
1575
1583
|
...fs,
|
|
1576
1584
|
denyRead: fs.denyRead.map((p) => p.replaceAll('[', '[[]')),
|
|
1577
|
-
allowWrite: fs.allowWrite.map(literal),
|
|
1585
|
+
allowWrite: fs.allowWrite.filter((p) => seatbeltSpells(p, 'write', false)).map(literal),
|
|
1578
1586
|
...(fs.allowRead !== undefined
|
|
1579
1587
|
? { allowRead: [...new Set(fs.allowRead.flatMap(read))] }
|
|
1580
1588
|
: {}),
|
|
@@ -1582,6 +1590,28 @@ export function seatbeltBrackets(
|
|
|
1582
1590
|
}
|
|
1583
1591
|
}
|
|
1584
1592
|
|
|
1593
|
+
/** A `*` or `?` behind an odd run of backslashes: the grant means the character. */
|
|
1594
|
+
const ESCAPED_WILDCARD = /(?:^|[^\\])(?:\\\\)*\\[*?]/
|
|
1595
|
+
|
|
1596
|
+
const warnedUnspellable = new Set<string>()
|
|
1597
|
+
|
|
1598
|
+
/** Whether seatbelt's SRT can take `grant` as written; said once when not. */
|
|
1599
|
+
function seatbeltSpells(grant: string, kind: 'read' | 'write', name: boolean): boolean {
|
|
1600
|
+
const m = name ? /[*?]/.exec(grant) : ESCAPED_WILDCARD.exec(grant)
|
|
1601
|
+
if (m === null) return true
|
|
1602
|
+
const at = name ? m.index : m.index + m[0].length - 2
|
|
1603
|
+
if (!warnedUnspellable.has(`${kind} ${grant}`)) {
|
|
1604
|
+
warnedUnspellable.add(`${kind} ${grant}`)
|
|
1605
|
+
process.stderr.write(
|
|
1606
|
+
`[vx] sandbox: the ${kind} grant ${grant} names a literal ${grant[at + (name ? 0 : 1)]}, ` +
|
|
1607
|
+
`which the macOS sandbox reads as a pattern and cannot spell as a name, so it is not ` +
|
|
1608
|
+
`granted. Rename it, or grant the directory above it: ` +
|
|
1609
|
+
`${grant.slice(0, grant.lastIndexOf(path.sep, at) + 1)}\n`,
|
|
1610
|
+
)
|
|
1611
|
+
}
|
|
1612
|
+
return false
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1585
1615
|
/** SRT's wrap, with the socket lift and the git-config grant this task asked for, or none (L-6, B-41). */
|
|
1586
1616
|
function wrapForTask(
|
|
1587
1617
|
SandboxManager: SrtModule['SandboxManager'],
|
|
@@ -531,7 +531,12 @@ async function executePersistentTask(args: ExecuteArgs): Promise<TaskOutcome> {
|
|
|
531
531
|
return {
|
|
532
532
|
node,
|
|
533
533
|
status: 'failed',
|
|
534
|
-
|
|
534
|
+
// A server that traps the TERM and exits 0 still timed out, as a
|
|
535
|
+
// one-shot task that does so reports 143 (X-116).
|
|
536
|
+
exitCode:
|
|
537
|
+
ready?.reason === 'timeout'
|
|
538
|
+
? (await spawn.child.exited) || signalExitCode('SIGTERM')
|
|
539
|
+
: (ready?.exitCode ?? 1),
|
|
535
540
|
durationMs: spawn.readyMs(),
|
|
536
541
|
...(ready !== undefined ? { notReady: ready.reason } : {}),
|
|
537
542
|
wallclockStartNs,
|