@vzn/vx 0.0.635 → 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 +47 -19
- package/docs/caching.md +222 -210
- package/docs/cli.md +699 -664
- package/docs/execution.md +67 -75
- package/docs/features.md +44 -40
- package/docs/patterns.md +31 -32
- package/docs/schema.md +418 -202
- 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 +13 -4
- 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 -61
- 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 +123 -144
- 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 +191 -71
- 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 +25 -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 +192 -70
- 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 +43 -28
- 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 +114 -73
- 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 +127 -88
- 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 +251 -241
- 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 +22 -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
package/src/cli/last.ts
CHANGED
|
@@ -9,11 +9,14 @@ import { Cache } from '../cache/index.js'
|
|
|
9
9
|
import { formatBytes } from './format.js'
|
|
10
10
|
import { flagHint, formatValue, seeHelp } from './help.js'
|
|
11
11
|
import {
|
|
12
|
+
currentBranch,
|
|
12
13
|
exitSignal,
|
|
14
|
+
type FailureMode,
|
|
13
15
|
getInvocation,
|
|
14
16
|
getRun,
|
|
15
17
|
type InvocationDetail,
|
|
16
18
|
listInvocations,
|
|
19
|
+
LocalHistoryProvider,
|
|
17
20
|
outcomeWord,
|
|
18
21
|
type RunSummaryRow,
|
|
19
22
|
resolveRunId,
|
|
@@ -21,7 +24,7 @@ import {
|
|
|
21
24
|
shortRunId,
|
|
22
25
|
taskLog,
|
|
23
26
|
} from '../orchestrator/index.js'
|
|
24
|
-
import { formatElapsed, UserError } from '../util/index.js'
|
|
27
|
+
import { formatElapsed, splitTaskId, UserError } from '../util/index.js'
|
|
25
28
|
import { findWorkspaceRoot } from '../workspace/index.js'
|
|
26
29
|
import { cliCacheDir, parseCacheDirFlag } from './workspace-config.js'
|
|
27
30
|
|
|
@@ -33,6 +36,12 @@ interface LastArgs {
|
|
|
33
36
|
format: 'pretty' | 'json'
|
|
34
37
|
/** `--log <task>`: that task's output in the run, not the run's summary. */
|
|
35
38
|
log?: string
|
|
39
|
+
/** `--stats`: per-task numbers over the history window, not a run. */
|
|
40
|
+
stats?: boolean
|
|
41
|
+
/** `--stats`' positional: a task id, or a task name in every project. */
|
|
42
|
+
task?: string
|
|
43
|
+
/** `--branch`: the branch `--stats` forecasts from, as a run's `--branch`. */
|
|
44
|
+
branch?: string
|
|
36
45
|
/** `--cache-dir`: read the history a run with the same flag wrote. */
|
|
37
46
|
cacheDir?: string
|
|
38
47
|
error?: string
|
|
@@ -46,6 +55,8 @@ const RUN_ID_SHAPE = /^[0-9a-f]{8}(?:-|$)/i
|
|
|
46
55
|
|
|
47
56
|
export function parseLastArgs(args: readonly string[]): LastArgs {
|
|
48
57
|
const out: LastArgs = { format: 'pretty' }
|
|
58
|
+
// `vx last app#build --log`: a bare --log takes the positional as its task.
|
|
59
|
+
let bareLog = false
|
|
49
60
|
for (let i = 0; i < args.length; i++) {
|
|
50
61
|
const a = args[i]!
|
|
51
62
|
if (a === '--list' || a.startsWith('--list=')) {
|
|
@@ -76,9 +87,30 @@ export function parseLastArgs(args: readonly string[]): LastArgs {
|
|
|
76
87
|
out.failed = true
|
|
77
88
|
continue
|
|
78
89
|
}
|
|
79
|
-
if (a === '--
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
if (a === '--stats') {
|
|
91
|
+
out.stats = true
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
if (a === '--branch' || a.startsWith('--branch=')) {
|
|
95
|
+
const v = a === '--branch' ? args[++i] : a.slice('--branch='.length)
|
|
96
|
+
if (v === undefined || v === '' || (a === '--branch' && v.startsWith('-'))) {
|
|
97
|
+
return { ...out, error: `--branch requires a branch name${seeHelp('last')}` }
|
|
98
|
+
}
|
|
99
|
+
out.branch = v
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
if (a === '--log') {
|
|
103
|
+
const v = args[i + 1]
|
|
104
|
+
if (v === undefined || v.startsWith('-')) bareLog = true
|
|
105
|
+
else {
|
|
106
|
+
out.log = v
|
|
107
|
+
i++
|
|
108
|
+
}
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
if (a.startsWith('--log=')) {
|
|
112
|
+
const v = a.slice('--log='.length)
|
|
113
|
+
if (v === '') {
|
|
82
114
|
return { ...out, error: `--log requires a task id, like app#build${seeHelp('last')}` }
|
|
83
115
|
}
|
|
84
116
|
out.log = v
|
|
@@ -103,6 +135,27 @@ export function parseLastArgs(args: readonly string[]): LastArgs {
|
|
|
103
135
|
return { ...out, error: `unexpected argument: ${a}${seeHelp('last')}` }
|
|
104
136
|
out.runId = a
|
|
105
137
|
}
|
|
138
|
+
if (bareLog) {
|
|
139
|
+
if (out.runId === undefined) {
|
|
140
|
+
return { ...out, error: `--log requires a task id, like app#build${seeHelp('last')}` }
|
|
141
|
+
}
|
|
142
|
+
out.log = out.runId
|
|
143
|
+
delete out.runId
|
|
144
|
+
}
|
|
145
|
+
if (out.stats === true) {
|
|
146
|
+
if (out.list !== undefined || out.failed === true || out.log !== undefined) {
|
|
147
|
+
return {
|
|
148
|
+
...out,
|
|
149
|
+
error: `--stats does not combine with --list, --failed or --log${seeHelp('last')}`,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (out.runId !== undefined) out.task = out.runId
|
|
153
|
+
delete out.runId
|
|
154
|
+
return out
|
|
155
|
+
}
|
|
156
|
+
if (out.branch !== undefined) {
|
|
157
|
+
return { ...out, error: `--branch combines only with --stats${seeHelp('last')}` }
|
|
158
|
+
}
|
|
106
159
|
// One run's replay or a list of runs, not both: the id was dropped.
|
|
107
160
|
// Beside --list, a word no run id could be is just an extra one.
|
|
108
161
|
if (out.runId !== undefined && out.list !== undefined && !RUN_ID_SHAPE.test(out.runId)) {
|
|
@@ -137,6 +190,88 @@ function failedInvocations(db: Database, limit: number): InvocationDetail[] {
|
|
|
137
190
|
return ids.map((r) => getInvocation(db, r.run_id)).filter((i) => i !== null)
|
|
138
191
|
}
|
|
139
192
|
|
|
193
|
+
/** One task's numbers over the window, every key present (`null` where unknown). */
|
|
194
|
+
export interface TaskStats {
|
|
195
|
+
taskId: string
|
|
196
|
+
runs: number
|
|
197
|
+
p50DurationMs: number | null
|
|
198
|
+
p99DurationMs: number | null
|
|
199
|
+
successRate: number
|
|
200
|
+
hitRate: number
|
|
201
|
+
failureMode: FailureMode
|
|
202
|
+
maxPeakRssBytes: number | null
|
|
203
|
+
maxCpuParallelism: number | null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* `--stats`: what the scheduler and `--dry` learn from history, per task,
|
|
208
|
+
* over the same window (`LocalHistoryProvider`), with the p50 a run on
|
|
209
|
+
* `branch` forecasts.
|
|
210
|
+
*/
|
|
211
|
+
async function printStats(
|
|
212
|
+
db: Database,
|
|
213
|
+
target: string | undefined,
|
|
214
|
+
format: 'pretty' | 'json',
|
|
215
|
+
branch: string | null,
|
|
216
|
+
): Promise<number> {
|
|
217
|
+
const provider = new LocalHistoryProvider(db, undefined, branch)
|
|
218
|
+
const matches = (id: string): boolean =>
|
|
219
|
+
target === undefined || (target.includes('#') ? id === target : splitTaskId(id)[1] === target)
|
|
220
|
+
const stats: TaskStats[] = [...(await provider.loadAll())]
|
|
221
|
+
.filter(([id]) => matches(id))
|
|
222
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
223
|
+
.map(([taskId, h]) => ({
|
|
224
|
+
taskId,
|
|
225
|
+
runs: h.runs,
|
|
226
|
+
p50DurationMs: h.p50DurationMs ?? null,
|
|
227
|
+
p99DurationMs: h.p99DurationMs ?? null,
|
|
228
|
+
successRate: h.successRate,
|
|
229
|
+
hitRate: h.hitRate,
|
|
230
|
+
failureMode: h.failureMode,
|
|
231
|
+
maxPeakRssBytes: h.maxPeakRssBytes ?? null,
|
|
232
|
+
maxCpuParallelism: h.maxCpuParallelism ?? null,
|
|
233
|
+
}))
|
|
234
|
+
if (stats.length === 0 && target !== undefined) {
|
|
235
|
+
throw new UserError(
|
|
236
|
+
`vx last: no recorded run of ${target} in the last ${provider.recent} runs`,
|
|
237
|
+
'VX_E_NO_HISTORY',
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
if (format === 'json') {
|
|
241
|
+
process.stdout.write(`${JSON.stringify({ window: provider.recent, branch, stats })}\n`)
|
|
242
|
+
return 0
|
|
243
|
+
}
|
|
244
|
+
if (stats.length === 0) {
|
|
245
|
+
process.stdout.write('no recorded runs\n')
|
|
246
|
+
return 0
|
|
247
|
+
}
|
|
248
|
+
const pct = (r: number): string => `${Math.round(r * 100)}%`
|
|
249
|
+
const ms = (v: number | null): string => (v === null ? '-' : fmtMs(v))
|
|
250
|
+
const rows = [
|
|
251
|
+
['task', 'runs', 'p50', 'p99', 'pass', 'hit', 'failure mode', 'peak rss', 'cpu'],
|
|
252
|
+
...stats.map((s) => [
|
|
253
|
+
s.taskId,
|
|
254
|
+
String(s.runs),
|
|
255
|
+
ms(s.p50DurationMs),
|
|
256
|
+
ms(s.p99DurationMs),
|
|
257
|
+
pct(s.successRate),
|
|
258
|
+
pct(s.hitRate),
|
|
259
|
+
s.failureMode,
|
|
260
|
+
s.maxPeakRssBytes === null ? '-' : formatBytes(s.maxPeakRssBytes),
|
|
261
|
+
s.maxCpuParallelism === null ? '-' : `${s.maxCpuParallelism.toFixed(1)}×`,
|
|
262
|
+
]),
|
|
263
|
+
]
|
|
264
|
+
const widths = rows[0]!.map((_, c) => Math.max(...rows.map((r) => r[c]!.length)))
|
|
265
|
+
const lines = rows.map((r) =>
|
|
266
|
+
r
|
|
267
|
+
.map((cell, c) => cell.padEnd(widths[c]!))
|
|
268
|
+
.join(' ')
|
|
269
|
+
.trimEnd(),
|
|
270
|
+
)
|
|
271
|
+
process.stdout.write(`${lines.join('\n')}\n`)
|
|
272
|
+
return 0
|
|
273
|
+
}
|
|
274
|
+
|
|
140
275
|
const fmtWhen = (ms: number): string => new Date(ms).toISOString()
|
|
141
276
|
|
|
142
277
|
const fmtMs = (ms: number): string => formatElapsed(ms, true)
|
|
@@ -312,6 +447,11 @@ export async function lastCmd(args: readonly string[]): Promise<number> {
|
|
|
312
447
|
// nothing, while `--list --failed` past green runs said there were none.
|
|
313
448
|
const noRuns = (): boolean => listInvocations(db, { limit: 1 }).length === 0
|
|
314
449
|
|
|
450
|
+
if (parsed.stats === true) {
|
|
451
|
+
const branch = currentBranch(root, undefined, parsed.branch)
|
|
452
|
+
return await printStats(db, parsed.task, parsed.format, branch)
|
|
453
|
+
}
|
|
454
|
+
|
|
315
455
|
if (parsed.log !== undefined) {
|
|
316
456
|
const runId =
|
|
317
457
|
parsed.runId === undefined
|
|
@@ -388,6 +528,7 @@ export async function lastCmd(args: readonly string[]): Promise<number> {
|
|
|
388
528
|
? t
|
|
389
529
|
: {
|
|
390
530
|
...t,
|
|
531
|
+
...(f.inputMoved !== undefined ? { inputMoved: f.inputMoved } : {}),
|
|
391
532
|
output: f.output,
|
|
392
533
|
locations: f.locations,
|
|
393
534
|
...(f.sandboxFix !== undefined ? { sandboxFix: f.sandboxFix } : {}),
|