@vzn/vx 0.0.636 → 0.0.637
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 +39 -11
- package/docs/caching.md +213 -207
- package/docs/cli.md +699 -655
- package/docs/execution.md +67 -75
- package/docs/features.md +44 -40
- package/docs/patterns.md +26 -25
- package/docs/schema.md +376 -198
- package/docs/security.md +76 -39
- package/launcher.sh +43 -0
- package/package.json +5 -5
- package/schemas/cache.json +41 -19
- package/schemas/info.json +23 -11
- package/schemas/last.json +48 -2
- package/schemas/lock.json +1 -1
- package/schemas/plan.json +2 -2
- package/schemas/show.json +5 -3
- package/schemas/why.json +15 -1
- package/skills/vx/SKILL.md +16 -12
- package/src/bin.ts +17 -2
- package/src/cache/archive.ts +11 -0
- package/src/cache/cache.ts +105 -53
- package/src/cache/git-inputs.ts +277 -63
- package/src/cache/index.ts +7 -1
- package/src/cache/inputs.ts +125 -35
- package/src/cache/key-fold.ts +8 -7
- package/src/cache/layer.ts +8 -17
- package/src/cache/layered-cache.ts +16 -102
- package/src/cache/policy.ts +25 -29
- package/src/cache/run-history.ts +36 -14
- package/src/cache/schema.ts +21 -23
- package/src/cache/scoped-cache.ts +224 -0
- package/src/cli/cache.ts +150 -28
- package/src/cli/completions.ts +2 -2
- package/src/cli/core-alias.ts +1 -1
- package/src/cli/foreign-flags.ts +13 -12
- package/src/cli/help.ts +94 -63
- package/src/cli/index.ts +42 -13
- package/src/cli/info.ts +38 -23
- package/src/cli/init.ts +115 -406
- package/src/cli/last.ts +145 -4
- package/src/cli/lock.ts +333 -111
- package/src/cli/plan-format.ts +2 -2
- package/src/cli/plugin-commands.ts +5 -9
- package/src/cli/plugin-templates.ts +10 -10
- package/src/cli/run.ts +103 -149
- package/src/cli/select.ts +33 -39
- package/src/cli/show.ts +17 -19
- package/src/cli/task-verb.ts +2 -9
- package/src/cli/upgrade.ts +402 -145
- package/src/cli/watch-cycle.ts +9 -2
- package/src/cli/watch-filter.ts +33 -60
- package/src/cli/watch-fs.ts +29 -16
- package/src/cli/watch-judge.ts +12 -3
- package/src/cli/watch-set.ts +16 -42
- package/src/cli/watch.ts +188 -215
- package/src/cli/why.ts +28 -17
- package/src/cli/workspace-config.ts +31 -68
- package/src/config.ts +169 -70
- package/src/exec/env.ts +1 -31
- package/src/exec/executor.ts +1 -5
- package/src/exec/kill-tree.ts +2 -1
- package/src/exec/sandbox-runtime.ts +12 -1
- package/src/exec/sandbox-violations.ts +9 -1
- package/src/graph/dependency-spec.ts +1 -1
- package/src/graph/index.ts +1 -0
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +19 -7
- package/src/graph/task-graph.ts +44 -20
- package/src/handoff.ts +169 -0
- package/src/index.ts +24 -57
- package/src/orchestrator/affected-tasks.ts +7 -5
- package/src/orchestrator/command-windows.ts +44 -0
- package/src/orchestrator/deferred-outputs.ts +12 -10
- package/src/orchestrator/doctor.ts +98 -110
- package/src/orchestrator/download-policy.ts +3 -3
- package/src/orchestrator/events.ts +19 -3
- package/src/orchestrator/excluded-keys.ts +0 -2
- package/src/orchestrator/execute-task.ts +158 -63
- package/src/orchestrator/fingerprint-watch.ts +18 -15
- package/src/orchestrator/forecast.ts +10 -2
- package/src/orchestrator/framed-output.ts +8 -3
- package/src/orchestrator/graph-stage.ts +5 -5
- package/src/orchestrator/history.ts +70 -30
- package/src/orchestrator/hit-restore.ts +15 -3
- package/src/orchestrator/index.ts +18 -4
- package/src/orchestrator/local-shortcircuit.ts +2 -1
- package/src/orchestrator/lockfile-claim.ts +79 -20
- package/src/orchestrator/logger.ts +71 -39
- package/src/orchestrator/metrics.ts +71 -6
- package/src/orchestrator/miss-save.ts +3 -0
- package/src/orchestrator/options.ts +42 -29
- package/src/orchestrator/output-host.ts +72 -0
- package/src/orchestrator/plan.ts +6 -8
- package/src/orchestrator/plugin-host.ts +87 -63
- package/src/orchestrator/plugin.ts +94 -60
- package/src/orchestrator/prepare.ts +113 -72
- package/src/orchestrator/projects.ts +102 -99
- package/src/orchestrator/remote-prefetch.ts +2 -1
- package/src/orchestrator/run-context.ts +25 -6
- package/src/orchestrator/run-failures.ts +17 -8
- package/src/orchestrator/run-lock.ts +1 -1
- package/src/orchestrator/run-report.ts +9 -9
- package/src/orchestrator/run-settings.ts +48 -0
- package/src/orchestrator/run.ts +121 -95
- package/src/orchestrator/sandbox-fix.ts +39 -20
- package/src/orchestrator/sandbox-request.ts +84 -13
- package/src/orchestrator/signals.ts +1 -1
- package/src/orchestrator/stable-keys.ts +13 -11
- package/src/orchestrator/status-line.ts +48 -6
- package/src/orchestrator/summary.ts +46 -62
- package/src/orchestrator/task-hash.ts +12 -83
- package/src/orchestrator/telemetry.ts +3 -3
- package/src/settings.ts +417 -0
- package/src/util/errors.ts +1 -1
- package/src/util/essential-env.ts +31 -0
- package/src/util/hide-env.ts +107 -0
- package/src/util/index.ts +8 -2
- package/src/util/paths.ts +10 -0
- package/src/util/settle.ts +49 -30
- package/src/util/size.ts +23 -4
- package/src/util/timing.ts +3 -2
- package/src/util/ulid.ts +1 -1
- package/src/util/verbs.ts +0 -15
- package/src/workspace/affected.ts +77 -222
- package/src/workspace/config-eval.ts +15 -20
- package/src/workspace/config-imports.ts +78 -3
- package/src/workspace/config-schema.ts +209 -268
- package/src/workspace/dependency-rules.ts +57 -0
- package/src/workspace/filter.ts +2 -2
- package/src/workspace/fingerprint.ts +11 -121
- package/src/workspace/index.ts +20 -27
- package/src/workspace/load-reads.ts +4 -4
- package/src/workspace/lockfile.ts +96 -140
- package/src/workspace/migration.ts +242 -146
- package/src/workspace/package-graph.ts +30 -216
- package/src/workspace/project-loader.ts +80 -285
- package/src/workspace/repo-id.ts +2 -1
- package/src/workspace/workspace.ts +362 -831
- package/src/cache/config-evals.ts +0 -130
- package/src/workspace/config-cache.ts +0 -661
- package/src/workspace/migrate-scripts.ts +0 -1121
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ESSENTIAL_ENV } from './essential-env.js'
|
|
2
|
+
|
|
3
|
+
const PR_SET_DUMPABLE = 4
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hide vx's environment from a same-user task. A task gets only declared
|
|
7
|
+
* variables, but vx itself holds the rest. Not a boundary (`exec.sandbox`
|
|
8
|
+
* is): it hides the environment, not files. On every verb (owner,
|
|
9
|
+
* 2026-10-13).
|
|
10
|
+
*
|
|
11
|
+
* Linux: make vx non-dumpable, so its `/proc/<pid>/environ` (and ptrace)
|
|
12
|
+
* close to the task. A child is dumpable again after `exec`, so tasks are
|
|
13
|
+
* unaffected. Loading `bun:ffi` costs ~0.8 ms. `VX_DEBUG_DUMPABLE` keeps vx
|
|
14
|
+
* observable: strace cannot read a non-dumpable tracee's memory, nor a
|
|
15
|
+
* debugger `/proc/<pid>/fd`.
|
|
16
|
+
*
|
|
17
|
+
* macOS: `ps eww` reads the env strings the kernel copied above the
|
|
18
|
+
* stack, and no flag closes that to the same user, so vx zeroes the
|
|
19
|
+
* values there (owner, 2026-10-13: "Do the override then on macOS").
|
|
20
|
+
* Bun copied the env at startup, so `process.env`, and every task built
|
|
21
|
+
* from it, keeps them.
|
|
22
|
+
*/
|
|
23
|
+
export async function hideEnvFromTasks(): Promise<boolean> {
|
|
24
|
+
if (process.platform === 'darwin') return wipeStartupEnv()
|
|
25
|
+
if (process.platform !== 'linux' || process.env['VX_DEBUG_DUMPABLE']) return false
|
|
26
|
+
try {
|
|
27
|
+
const { dlopen, FFIType } = await import('bun:ffi')
|
|
28
|
+
const libc = dlopen('libc.so.6', {
|
|
29
|
+
prctl: {
|
|
30
|
+
args: [FFIType.i32, FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u64],
|
|
31
|
+
returns: FFIType.i32,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
const ok = libc.symbols.prctl(PR_SET_DUMPABLE, 0n, 0n, 0n, 0n) === 0
|
|
35
|
+
libc.close()
|
|
36
|
+
return ok
|
|
37
|
+
} catch {
|
|
38
|
+
// No glibc (musl names it otherwise): the env stays as readable as
|
|
39
|
+
// any process's, as it was.
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Zero the value of every startup variable a task does not see by
|
|
46
|
+
* default (`ESSENTIAL_ENV` stays: every task gets it anyway). The
|
|
47
|
+
* original `envp` array follows `argv`'s NULL on the stack and is never
|
|
48
|
+
* moved, unlike `environ`, which `setenv` may reallocate. libc's
|
|
49
|
+
* `getenv` reads that block, so it answers `''` for a wiped name; vx
|
|
50
|
+
* reads `process.env`.
|
|
51
|
+
*/
|
|
52
|
+
async function wipeStartupEnv(): Promise<boolean> {
|
|
53
|
+
try {
|
|
54
|
+
const { dlopen, FFIType, read, toArrayBuffer, CString } = await import('bun:ffi')
|
|
55
|
+
const sys = dlopen('/usr/lib/libSystem.B.dylib', {
|
|
56
|
+
_NSGetArgv: { args: [], returns: FFIType.ptr },
|
|
57
|
+
_NSGetArgc: { args: [], returns: FFIType.ptr },
|
|
58
|
+
})
|
|
59
|
+
const argv = read.ptr(sys.symbols._NSGetArgv()!, 0)
|
|
60
|
+
const argc = read.i32(sys.symbols._NSGetArgc()!, 0)
|
|
61
|
+
sys.close()
|
|
62
|
+
const keep = new Set(ESSENTIAL_ENV)
|
|
63
|
+
for (let i = argc + 1; ; i++) {
|
|
64
|
+
const p = read.ptr(argv, i * 8)
|
|
65
|
+
if (!p) break
|
|
66
|
+
const entry = new CString(p as never).toString()
|
|
67
|
+
const eq = entry.indexOf('=')
|
|
68
|
+
if (eq < 0 || keep.has(entry.slice(0, eq))) continue
|
|
69
|
+
new Uint8Array(toArrayBuffer(p as never, 0, Buffer.byteLength(entry))).fill(0, eq + 1)
|
|
70
|
+
}
|
|
71
|
+
return true
|
|
72
|
+
} catch {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The env for a helper vx keeps running beside its tasks (the group
|
|
79
|
+
* guard, a port bridge): PATH alone. Without an `env`, a spawn hands the
|
|
80
|
+
* child vx's startup env, and that child, dumpable after its `exec`,
|
|
81
|
+
* would show a task what `hideEnvFromTasks` hid.
|
|
82
|
+
*/
|
|
83
|
+
export function helperEnv(): Record<string, string> {
|
|
84
|
+
return { PATH: process.env['PATH'] ?? '' }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The env for a `git` vx runs: what git reads (`GIT_*`, the locale,
|
|
89
|
+
* `XDG_CONFIG_HOME`, the essentials: `HOME`, `PATH`) and nothing else.
|
|
90
|
+
* A git child is readable after its `exec` like any helper, and `vx
|
|
91
|
+
* watch` runs git while its tasks run.
|
|
92
|
+
*/
|
|
93
|
+
export function gitEnv(): Record<string, string> {
|
|
94
|
+
const env: Record<string, string> = {}
|
|
95
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
96
|
+
if (value === undefined) continue
|
|
97
|
+
if (
|
|
98
|
+
name.startsWith('GIT_') ||
|
|
99
|
+
name.startsWith('LC_') ||
|
|
100
|
+
name === 'XDG_CONFIG_HOME' ||
|
|
101
|
+
name === 'LANGUAGE' ||
|
|
102
|
+
ESSENTIAL_ENV.includes(name)
|
|
103
|
+
)
|
|
104
|
+
env[name] = value
|
|
105
|
+
}
|
|
106
|
+
return env
|
|
107
|
+
}
|
package/src/util/index.ts
CHANGED
|
@@ -45,6 +45,8 @@ export {
|
|
|
45
45
|
outputMatcher,
|
|
46
46
|
printable,
|
|
47
47
|
relPosix,
|
|
48
|
+
DOT_VX_GITATTRIBUTES,
|
|
49
|
+
DOT_VX_GITIGNORE,
|
|
48
50
|
splitNegations,
|
|
49
51
|
staticPrefix,
|
|
50
52
|
stripTrailingSlash,
|
|
@@ -58,10 +60,12 @@ export {
|
|
|
58
60
|
claimExitForSignal,
|
|
59
61
|
exitClaimedBySignal,
|
|
60
62
|
killGraceMs,
|
|
63
|
+
setSettleBounds,
|
|
64
|
+
type SettleBound,
|
|
61
65
|
settleWithin,
|
|
62
66
|
teardownTimeoutMs,
|
|
63
67
|
} from './settle.js'
|
|
64
|
-
export { formatBytes, parseDuration, parseSize } from './size.js'
|
|
68
|
+
export { formatBytes, parseDuration, parseSize, treeBytes } from './size.js'
|
|
65
69
|
export {
|
|
66
70
|
cgroupCpuQuota,
|
|
67
71
|
cgroupMemoryLimitBytes,
|
|
@@ -73,11 +77,13 @@ export { appendTail, createTail, resetTail, tailText, type Tail } from './tail.j
|
|
|
73
77
|
export { ulid } from './ulid.js'
|
|
74
78
|
export { splitTaskId } from './task-id.js'
|
|
75
79
|
export { editDistance, listed, nearMatches, nearest } from './edit-distance.js'
|
|
76
|
-
export { CORE_VERBS
|
|
80
|
+
export { CORE_VERBS } from './verbs.js'
|
|
77
81
|
export { isUnsupportedBun, MIN_BUN, unsupportedBunMessage } from './bun-version.js'
|
|
78
82
|
export { executablePath, shellArgv, taskShell } from './which.js'
|
|
79
83
|
export { procfsIsOwn } from './procfs.js'
|
|
80
84
|
export { hangupIgnored } from './hangup.js'
|
|
85
|
+
export { ESSENTIAL_ENV } from './essential-env.js'
|
|
86
|
+
export { gitEnv, helperEnv, hideEnvFromTasks } from './hide-env.js'
|
|
81
87
|
export { realPath, realpathOf } from './real-path.js'
|
|
82
88
|
export {
|
|
83
89
|
maskedCommand,
|
package/src/util/paths.ts
CHANGED
|
@@ -11,6 +11,16 @@ export function toPosix(p: string): string {
|
|
|
11
11
|
return IS_POSIX ? p : p.split(path.sep).join('/')
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* `.vx/.gitignore`: git ignores the machine-local `.vx/cache` and nothing
|
|
16
|
+
* else in `.vx`, so the workspace config and the lock beside it are
|
|
17
|
+
* committed. `vx init` and the cache write it when missing.
|
|
18
|
+
*/
|
|
19
|
+
export const DOT_VX_GITIGNORE = '/cache\n'
|
|
20
|
+
|
|
21
|
+
/** `.vx/.gitattributes`' text: GitHub collapses the machine lock in a diff. */
|
|
22
|
+
export const DOT_VX_GITATTRIBUTES = 'projects.lock.json linguist-generated=true\n'
|
|
23
|
+
|
|
14
24
|
export function relPosix(from: string, to: string): string {
|
|
15
25
|
return toPosix(path.relative(from, to))
|
|
16
26
|
}
|
package/src/util/settle.ts
CHANGED
|
@@ -2,49 +2,68 @@
|
|
|
2
2
|
// wrote; it must not hold the run's exit hostage.
|
|
3
3
|
|
|
4
4
|
import { MAX_TIMEOUT_MS } from './num.js'
|
|
5
|
+
import { parseDuration } from './size.js'
|
|
5
6
|
|
|
6
7
|
/** Default upper bound on one end-of-run flush/teardown await. */
|
|
7
8
|
const DEFAULT_TIMEOUT_MS = 3000
|
|
8
9
|
|
|
10
|
+
/** A bound the run resolved from its flag or `.vx/workspace`. */
|
|
11
|
+
export interface SettleBound {
|
|
12
|
+
readonly ms: number
|
|
13
|
+
readonly from: 'flag' | 'workspace'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let teardownSet: SettleBound | undefined
|
|
17
|
+
let killGraceSet: SettleBound | undefined
|
|
18
|
+
|
|
9
19
|
/**
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
20
|
+
* The run's `run.teardownTimeout` and `run.killGrace` from its flag or
|
|
21
|
+
* workspace (the orchestrator resolves them; util sits below settings).
|
|
22
|
+
* The variable, read per call, sits between the two, so a test drives the
|
|
23
|
+
* deadline instead of waiting it out.
|
|
12
24
|
*/
|
|
13
|
-
export function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
export function setSettleBounds(b: { teardown?: SettleBound; killGrace?: SettleBound }): void {
|
|
26
|
+
teardownSet = b.teardown
|
|
27
|
+
killGraceSet = b.killGrace
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A flag's bound, else the variable's (a positive duration inside the
|
|
32
|
+
* timer's range: the CLI refuses any other before a run, and an embedder's
|
|
33
|
+
* falls back), else the workspace's.
|
|
34
|
+
*/
|
|
35
|
+
function bound(name: string, set: SettleBound | undefined, zero = false): number | undefined {
|
|
36
|
+
if (set?.from === 'flag') return set.ms
|
|
37
|
+
const raw = process.env[name]
|
|
38
|
+
if (raw !== undefined && raw !== '') {
|
|
39
|
+
const n = parseDuration(raw)
|
|
40
|
+
if (n !== null && (n > 0 || zero) && n <= MAX_TIMEOUT_MS) return n
|
|
26
41
|
}
|
|
27
|
-
return
|
|
42
|
+
return set?.ms
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
/**
|
|
31
|
-
* The
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
46
|
+
* The bound on one end-of-run flush or teardown (`run.teardownTimeout`).
|
|
47
|
+
* There is no "no limit": the deadline exists so a plugin's flush cannot
|
|
48
|
+
* hold the run's exit hostage, so a timer past the platform's range (which
|
|
49
|
+
* would fire at once and drop every buffered record) is no value.
|
|
50
|
+
*/
|
|
51
|
+
export function teardownTimeoutMs(): number {
|
|
52
|
+
return bound('VX_RUN_TEARDOWN_TIMEOUT', teardownSet, true) ?? DEFAULT_TIMEOUT_MS
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The SIGTERM→SIGKILL grace a run grants a child that ignores SIGTERM
|
|
57
|
+
* (`run.killGrace`): the one-shot timeout escalation (`exec/runner.ts`),
|
|
58
|
+
* the signal teardown (`orchestrator/signals.ts`) and the end-of-run
|
|
59
|
+
* persistent shutdown (`orchestrator/persistent.ts`) share it. Two seconds
|
|
35
60
|
* is generous for a real server's cleanup and is what every run waits when
|
|
36
|
-
* a child wedges
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* a third of the suite's wall time for a claim a 200 ms grace proves too.
|
|
61
|
+
* a child wedges; the tests that prove the escalation used to wait the
|
|
62
|
+
* full two seconds each, a third of the suite's wall time for a claim a
|
|
63
|
+
* 200 ms grace proves too.
|
|
40
64
|
*/
|
|
41
65
|
export function killGraceMs(defaultMs: number): number {
|
|
42
|
-
|
|
43
|
-
if (raw !== undefined && /^[0-9]+$/.test(raw)) {
|
|
44
|
-
const n = Number(raw)
|
|
45
|
-
if (n > 0 && n <= MAX_TIMEOUT_MS) return n
|
|
46
|
-
}
|
|
47
|
-
return defaultMs
|
|
66
|
+
return bound('VX_RUN_KILL_GRACE', killGraceSet) ?? defaultMs
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
/**
|
package/src/util/size.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { lstatSync, readdirSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
1
3
|
import { parseDecimalInt } from './num.js'
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -20,25 +22,34 @@ export function parseSize(input: string): number | null {
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
|
-
* Parse `<n><unit>` where unit is s/m/h/d, case-insensitively (`parseSize`
|
|
25
|
+
* Parse `<n><unit>` where unit is ms/s/m/h/d, case-insensitively (`parseSize`
|
|
24
26
|
* has always been case-insensitive; this matched only lowercase, so `30D`
|
|
25
27
|
* was rejected while `1GB` and `1gb` both worked).
|
|
26
28
|
*/
|
|
27
29
|
export function parseDuration(input: string): number | null {
|
|
28
|
-
const m = input.match(/^(\d+)([smhd])$/i)
|
|
30
|
+
const m = input.match(/^(\d+)(ms|[smhd])$/i)
|
|
29
31
|
if (!m) return null
|
|
30
32
|
const n = parseDecimalInt(m[1]!)
|
|
31
33
|
if (n === null) return null
|
|
32
34
|
// Lowercase before the switch: with the /i flag an uppercase `M` would
|
|
33
35
|
// otherwise fall through to the days branch.
|
|
34
36
|
const unit = m[2]!.toLowerCase()
|
|
35
|
-
const mult =
|
|
37
|
+
const mult =
|
|
38
|
+
unit === 'ms'
|
|
39
|
+
? 1
|
|
40
|
+
: unit === 's'
|
|
41
|
+
? 1000
|
|
42
|
+
: unit === 'm'
|
|
43
|
+
? 60_000
|
|
44
|
+
: unit === 'h'
|
|
45
|
+
? 3_600_000
|
|
46
|
+
: 86_400_000
|
|
36
47
|
return n * mult
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* Human-readable byte size with KB/MB/GB/TB/PB suffixes and one
|
|
41
|
-
* decimal of precision below 10 of any unit. Used by `vx info`, `vx cache
|
|
52
|
+
* decimal of precision below 10 of any unit. Used by `vx info`, `vx cache clean` and the
|
|
42
53
|
* run's `cacheRetention` line.
|
|
43
54
|
*/
|
|
44
55
|
export function formatBytes(n: number): string {
|
|
@@ -51,3 +62,11 @@ export function formatBytes(n: number): string {
|
|
|
51
62
|
}
|
|
52
63
|
return `${v.toFixed(0)} PB`
|
|
53
64
|
}
|
|
65
|
+
|
|
66
|
+
/** Bytes under `p` on disk, links not followed; 0 when it is missing. */
|
|
67
|
+
export function treeBytes(p: string): number {
|
|
68
|
+
const st = lstatSync(p, { throwIfNoEntry: false })
|
|
69
|
+
if (st === undefined) return 0
|
|
70
|
+
if (!st.isDirectory()) return st.size
|
|
71
|
+
return readdirSync(p).reduce((n, e) => n + treeBytes(path.join(p, e)), 0)
|
|
72
|
+
}
|
package/src/util/timing.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Stage timing for the run path, printed to stderr when `
|
|
1
|
+
// Stage timing for the run path, printed to stderr when `VX_DEBUG_TIMING` is
|
|
2
|
+
// true (`debug.timing`; util reads it itself, below src/settings.ts).
|
|
2
3
|
//
|
|
3
4
|
// The CPU profiler attributes a tight loop's cost unreliably and hides
|
|
4
5
|
// where an `await` waited; a stage table answers "where did the warm run
|
|
@@ -8,7 +9,7 @@
|
|
|
8
9
|
// prints (a push per stage, ~15 a run): a telemetry sink draws them as
|
|
9
10
|
// spans (`stageTimes`). The per-call `span`s stay off unless enabled.
|
|
10
11
|
|
|
11
|
-
const enabled = process.env.
|
|
12
|
+
const enabled = process.env.VX_DEBUG_TIMING === 'true' || process.env.VX_DEBUG_TIMING === '1'
|
|
12
13
|
let t0 = Bun.nanoseconds()
|
|
13
14
|
// The wall clock at t0, read then: `performance.timeOrigin` plus the
|
|
14
15
|
// monotonic clock drifted 34 ms from `Date.now()` on macOS CI, so stages
|
package/src/util/ulid.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Run-id generator. Thin wrapper over `Bun.randomUUIDv7()` so the
|
|
2
2
|
// `runId` column on `runs` carries a timestamp-ordered identifier —
|
|
3
|
-
//
|
|
3
|
+
// time-range queries can range-scan without an index on the
|
|
4
4
|
// time column, and the IDs lexicographically sort by creation time
|
|
5
5
|
// for grouping.
|
|
6
6
|
//
|
package/src/util/verbs.ts
CHANGED
|
@@ -19,18 +19,3 @@ export const CORE_VERBS = [
|
|
|
19
19
|
'help',
|
|
20
20
|
'version',
|
|
21
21
|
] as const
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Verbs core owned once and a package owns now. Not core verbs: a plugin
|
|
25
|
-
* may declare them, and the dispatcher prints the
|
|
26
|
-
* pointer only when no declared plugin claimed the name.
|
|
27
|
-
*/
|
|
28
|
-
export const MOVED_VERBS: Readonly<Record<string, string>> = {
|
|
29
|
-
migrate:
|
|
30
|
-
'vx migrate moved to @vzn/vx-migrate: run `bunx @vzn/vx-migrate` (turbo.json or an Nx ' +
|
|
31
|
-
'project graph → vx.config.ts); `vx init` reads package.json scripts',
|
|
32
|
-
prune:
|
|
33
|
-
'vx prune comes from @vzn/vx-lockfile: declare pnpm(), bun(), npm() or yarn() in ' +
|
|
34
|
-
'vx.workspace.ts',
|
|
35
|
-
stats: 'vx stats was removed (H-19): run `vx info`, which prints the same report',
|
|
36
|
-
}
|