@vzn/vx 0.0.638 → 0.0.639
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/docs/caching.md +44 -15
- package/docs/cli.md +168 -345
- package/docs/execution.md +18 -43
- package/docs/features.md +7 -10
- package/docs/patterns.md +5 -7
- package/docs/schema.md +47 -49
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/skills/vx/SKILL.md +6 -4
- package/src/cache/cache.ts +40 -10
- package/src/cache/git-inputs.ts +6 -0
- package/src/cache/index.ts +2 -2
- package/src/cache/layer.ts +25 -11
- package/src/cache/layered-cache.ts +108 -315
- package/src/cache/plugin-cache.ts +2 -10
- package/src/cache/remote-lane.ts +383 -0
- package/src/cache/run-history.ts +24 -8
- package/src/cache/run-store.ts +4 -4
- package/src/cache/schema.ts +6 -4
- package/src/cache/scoped-cache.ts +7 -0
- package/src/cli/completions.ts +2 -2
- package/src/cli/foreign-flags.ts +10 -22
- package/src/cli/help.ts +6 -31
- package/src/cli/info.ts +2 -1
- package/src/cli/last.ts +6 -3
- package/src/cli/plan-format.ts +5 -58
- package/src/cli/plugin-commands.ts +2 -2
- package/src/cli/run.ts +23 -185
- package/src/cli/show.ts +6 -0
- package/src/cli/upgrade.ts +123 -39
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch.ts +29 -14
- package/src/cli/why.ts +4 -4
- package/src/config.ts +0 -6
- package/src/exec/executor.ts +1 -1
- package/src/exec/sandbox-runtime.ts +1 -1
- package/src/exec/sandbox-violations.ts +3 -1
- package/src/graph/index.ts +9 -1
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +4 -16
- package/src/graph/task-graph.ts +87 -17
- package/src/graph/task-status.ts +95 -0
- package/src/{orchestrator → history}/failure-mode.ts +1 -1
- package/src/{orchestrator → history}/history.ts +1 -1
- package/src/history/index.ts +48 -0
- package/src/{orchestrator → history}/metrics.ts +7 -20
- package/src/{orchestrator → history}/miss-reason.ts +1 -1
- package/src/index.ts +10 -28
- package/src/orchestrator/doctor.ts +1 -1
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +21 -386
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +1 -73
- package/src/orchestrator/miss-save.ts +33 -3
- package/src/orchestrator/options.ts +1 -36
- package/src/orchestrator/output-host.ts +4 -3
- package/src/orchestrator/persistent-task.ts +321 -0
- package/src/orchestrator/persistent.ts +130 -3
- package/src/orchestrator/placement.ts +3 -3
- package/src/orchestrator/plan.ts +18 -5
- package/src/orchestrator/plugin-host.ts +67 -47
- package/src/orchestrator/plugin.ts +6 -6
- package/src/orchestrator/prepare.ts +28 -29
- package/src/orchestrator/remote-prefetch.ts +14 -1
- package/src/orchestrator/run-artifacts.ts +5 -177
- package/src/orchestrator/run-context.ts +55 -0
- package/src/orchestrator/run-failures.ts +15 -3
- package/src/orchestrator/run-records.ts +35 -3
- package/src/orchestrator/run-report.ts +81 -179
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +260 -601
- package/src/orchestrator/sandbox-fix.ts +0 -16
- package/src/orchestrator/signals.ts +14 -6
- package/src/orchestrator/stable-keys.ts +107 -3
- package/src/orchestrator/steps.ts +50 -0
- package/src/orchestrator/task-hash.ts +3 -16
- package/src/orchestrator/task-log-buffer.ts +1 -1
- package/src/orchestrator/task-process.ts +94 -0
- package/src/orchestrator/telemetry-host.ts +1 -1
- package/src/orchestrator/telemetry.ts +5 -82
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +39 -33
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +158 -20
- package/src/{orchestrator → report}/status-line.ts +84 -7
- package/src/{orchestrator → report}/summary.ts +126 -35
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +1 -22
- package/src/util/colors.ts +44 -4
- package/src/util/git-ignored.ts +143 -0
- package/src/util/index.ts +19 -4
- package/src/util/report.ts +13 -5
- package/src/util/settle.ts +14 -39
- package/src/util/tail.ts +1 -1
- package/src/util/ui.ts +17 -6
- package/schemas/summary.json +0 -133
- package/src/cache/chained-cache.ts +0 -280
- package/src/orchestrator/tally.ts +0 -112
- /package/src/{orchestrator → report}/path-links.ts +0 -0
- /package/src/{orchestrator → report}/plain-output.ts +0 -0
- /package/src/{orchestrator → report}/terminal-signals.ts +0 -0
package/src/cache/cache.ts
CHANGED
|
@@ -96,6 +96,7 @@ import {
|
|
|
96
96
|
type PruneResult,
|
|
97
97
|
type RunRecord,
|
|
98
98
|
type SaveArgs,
|
|
99
|
+
type TaskInputComponent,
|
|
99
100
|
type TaskInputRow,
|
|
100
101
|
WORKSPACE_OUTPUT_PREFIX,
|
|
101
102
|
} from './layer.js'
|
|
@@ -309,17 +310,19 @@ function outOfFdsAtOpen(dbFile: string, err: unknown): UserError | undefined {
|
|
|
309
310
|
// v38: blob_sizes moved to the store, and the store's task_outcomes
|
|
310
311
|
// holds the flaky record, so worktrees share both. The cache KEY is
|
|
311
312
|
// unchanged.
|
|
312
|
-
|
|
313
|
+
// v39: invocations drops `tags` (`--tag` is gone). The cache KEY is
|
|
314
|
+
// unchanged.
|
|
315
|
+
// v40: runs.remote_error — each remote read or write of a task's key that
|
|
316
|
+
// failed, for `vx last`. The cache KEY is unchanged.
|
|
317
|
+
export const SCHEMA_VERSION = 'v40'
|
|
313
318
|
|
|
314
319
|
/**
|
|
315
320
|
* An entry's key components as its one `entry_inputs` row (v34): JSON
|
|
316
321
|
* `[kind, name, hash]` triples in (kind, name) code-unit order, the first
|
|
317
322
|
* of a repeated (kind, name) kept, as the per-component primary key did.
|
|
318
323
|
*/
|
|
319
|
-
function encodeEntryInputs(
|
|
320
|
-
|
|
321
|
-
): string {
|
|
322
|
-
const byId = new Map<string, Pick<TaskInputRow, 'kind' | 'name' | 'hash'>>()
|
|
324
|
+
function encodeEntryInputs(components: readonly TaskInputComponent[]): string {
|
|
325
|
+
const byId = new Map<string, TaskInputComponent>()
|
|
323
326
|
for (const c of components) {
|
|
324
327
|
const id = `${c.kind}\0${c.name}`
|
|
325
328
|
if (!byId.has(id)) byId.set(id, c)
|
|
@@ -788,6 +791,23 @@ export interface UploadTally {
|
|
|
788
791
|
failed: number
|
|
789
792
|
}
|
|
790
793
|
|
|
794
|
+
/** One task's traffic with the remotes this run, by its key. */
|
|
795
|
+
export interface RemoteKeyTraffic {
|
|
796
|
+
/** Its uploads queued or running. */
|
|
797
|
+
uploading: number
|
|
798
|
+
/** An upload of it landed on a remote. */
|
|
799
|
+
landed: boolean
|
|
800
|
+
/** Each distinct remote read or write of it that failed: `upload: <message>`. */
|
|
801
|
+
failures: string[]
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/** The run's remote traffic per key: what the footer's remote part counts, task by task. */
|
|
805
|
+
export interface RemoteTraffic {
|
|
806
|
+
/** Uploads queued or running across every remote. */
|
|
807
|
+
inFlight: number
|
|
808
|
+
readonly keys: Map<string, RemoteKeyTraffic>
|
|
809
|
+
}
|
|
810
|
+
|
|
791
811
|
export class Cache implements CacheLayer {
|
|
792
812
|
/** This IS the local layer — there is nothing slower behind it. */
|
|
793
813
|
readonly hasRemote = false
|
|
@@ -797,6 +817,8 @@ export class Cache implements CacheLayer {
|
|
|
797
817
|
* so the run can report them without a method on the layer seam.
|
|
798
818
|
*/
|
|
799
819
|
readonly uploads: UploadTally = { count: 0, bytes: 0, ms: 0, failed: 0 }
|
|
820
|
+
/** The same remotes' traffic per key, filled by the same `LayeredCache`. */
|
|
821
|
+
readonly traffic: RemoteTraffic = { inFlight: 0, keys: new Map() }
|
|
800
822
|
|
|
801
823
|
private readonly db: Database
|
|
802
824
|
private readonly insertEntry: ReturnType<Database['prepare']>
|
|
@@ -1758,9 +1780,14 @@ export class Cache implements CacheLayer {
|
|
|
1758
1780
|
|
|
1759
1781
|
private async hasEntry(hash: string): Promise<'local' | 'remote' | null> {
|
|
1760
1782
|
if (!this.read) return null
|
|
1783
|
+
return this.storedHere(hash) ? 'local' : null
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/** The index row and its artifact are both here, whatever the run may read. */
|
|
1787
|
+
private storedHere(hash: string): boolean {
|
|
1761
1788
|
const row = this.entryExists.get(hash) as { inline_at: number | null } | null
|
|
1762
|
-
if (row === null) return
|
|
1763
|
-
return row.inline_at !== null || existsSync(this.tarPath(hash))
|
|
1789
|
+
if (row === null) return false
|
|
1790
|
+
return row.inline_at !== null || existsSync(this.tarPath(hash))
|
|
1764
1791
|
}
|
|
1765
1792
|
|
|
1766
1793
|
// Local cache has no slower layer to warm from — prefetch is a no-op.
|
|
@@ -2054,8 +2081,6 @@ export class Cache implements CacheLayer {
|
|
|
2054
2081
|
entry: Omit<CacheEntry, 'hash' | 'storedAt' | 'outputFiles' | 'exitCode'>
|
|
2055
2082
|
projectDir: string
|
|
2056
2083
|
outputFiles: string[]
|
|
2057
|
-
/** See `CacheLayer.save`. */
|
|
2058
|
-
skipLocalWrite?: boolean
|
|
2059
2084
|
workspaceOutputFiles?: string[]
|
|
2060
2085
|
workspaceRoot?: string
|
|
2061
2086
|
inputComponents?: readonly TaskInputRow[]
|
|
@@ -2080,7 +2105,9 @@ export class Cache implements CacheLayer {
|
|
|
2080
2105
|
// still uploads to remote — it calls `packArtifact` itself to get
|
|
2081
2106
|
// the bytes, since there's no local artifact to read off disk.
|
|
2082
2107
|
if (!this.write) return
|
|
2083
|
-
|
|
2108
|
+
// A cache that reads saves only on a miss. One that does not (`--force`)
|
|
2109
|
+
// re-executes a key it may hold: that entry is kept as it is.
|
|
2110
|
+
if (!this.read && this.guard(() => this.storedHere(args.hash))) return
|
|
2084
2111
|
// The cache directory is the constructor's (`mkdirSync`, with a
|
|
2085
2112
|
// refusal recorded for `assertWritable`); re-creating it here and
|
|
2086
2113
|
// again in `writeArtifactAndIndex` cost every save two thread-pool
|
|
@@ -2631,6 +2658,9 @@ export class Cache implements CacheLayer {
|
|
|
2631
2658
|
recordRunBundle(bundle: { runs: readonly RunRecord[]; invocation: InvocationRecord }): void {
|
|
2632
2659
|
return this.guard(() => this.history.recordRunBundle(bundle))
|
|
2633
2660
|
}
|
|
2661
|
+
recordRemoteErrors(runId: string, errors: readonly (readonly [string, string])[]): void {
|
|
2662
|
+
return this.guard(() => this.history.recordRemoteErrors(runId, errors))
|
|
2663
|
+
}
|
|
2634
2664
|
stats(opts: CacheStatsOptions = {}): CacheStats {
|
|
2635
2665
|
return this.guard(() => this.statsOf(opts))
|
|
2636
2666
|
}
|
package/src/cache/git-inputs.ts
CHANGED
|
@@ -289,6 +289,12 @@ export class GitFilesCache extends Map<string, readonly string[]> {
|
|
|
289
289
|
* a cache no enumeration filled; its OIDs are then re-checked by content.
|
|
290
290
|
*/
|
|
291
291
|
enumeratedAtMs: number | undefined
|
|
292
|
+
/**
|
|
293
|
+
* The declared outputs (root-relative globs) git ignores whole, asked
|
|
294
|
+
* once per run beside the enumeration: no key reads them, so a reader
|
|
295
|
+
* beside their producer keys up front (`dependsOnSiblingOutputs`).
|
|
296
|
+
*/
|
|
297
|
+
ignoredOutputs: ReadonlySet<string> = new Set()
|
|
292
298
|
|
|
293
299
|
get undecodableNames(): ReadonlySet<string> {
|
|
294
300
|
return this.undecodable
|
package/src/cache/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export {
|
|
|
21
21
|
KEYED_RUNS_SQL,
|
|
22
22
|
parseCachePolicy,
|
|
23
23
|
type RunRecord,
|
|
24
|
+
type TaskInputComponent,
|
|
24
25
|
type TaskInputRow,
|
|
25
26
|
WORKSPACE_OUTPUT_PREFIX,
|
|
26
27
|
type OutputDirRow,
|
|
@@ -69,14 +70,13 @@ export {
|
|
|
69
70
|
type BlobSizeMemo,
|
|
70
71
|
} from './git-inputs.js'
|
|
71
72
|
export { LayeredCache, type RemoteCacheLayer } from './layered-cache.js'
|
|
73
|
+
export type { ResolvedRemote } from './remote-lane.js'
|
|
72
74
|
export { ScopedCache } from './scoped-cache.js'
|
|
73
75
|
export {
|
|
74
76
|
absentPluginCache,
|
|
75
77
|
pluginCache,
|
|
76
78
|
PLUGIN_ENTRY_MAX_BYTES,
|
|
77
79
|
type PluginCache,
|
|
78
|
-
type PluginCacheRemote,
|
|
79
80
|
} from './plugin-cache.js'
|
|
80
81
|
export { packRunOutputs, putRunEntry, runEntryName, writesRunStore } from './run-store.js'
|
|
81
|
-
export { ChainedCache } from './chained-cache.js'
|
|
82
82
|
export { branchSource } from './run-history.js'
|
package/src/cache/layer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The cache layer CONTRACT and its records — what every cache implementation
|
|
2
|
-
// honours (`Cache`, `LayeredCache`,
|
|
2
|
+
// honours (`Cache`, `LayeredCache`, a plugin's remote layer) and
|
|
3
3
|
// the shapes that cross it: the key input, entries, run-history rows, output
|
|
4
4
|
// fingerprints, stats, prune options. No implementation lives here; the one
|
|
5
5
|
// reference to `Cache` below is a type (the local handle a layer may wrap).
|
|
@@ -208,6 +208,8 @@ export interface RunRecord {
|
|
|
208
208
|
timedOut?: true
|
|
209
209
|
sandboxViolations?: number
|
|
210
210
|
notReady?: 'timeout' | 'exited' | 'spawn'
|
|
211
|
+
/** Each remote read or write of its key that failed, `upload: <message>`, `; `-joined (v40). */
|
|
212
|
+
remoteError?: string
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
/**
|
|
@@ -247,8 +249,18 @@ export interface InvocationRecord {
|
|
|
247
249
|
os: string | null
|
|
248
250
|
arch: string | null
|
|
249
251
|
vxVersion: string
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* One cache-key component as `key()` folded it, the `captureInto` row.
|
|
256
|
+
* Persisted on a miss (`TaskInputRow`) so a later run can diff its inputs
|
|
257
|
+
* against this one (the Tier-3 "why did this re-run?" moat); a hit saves
|
|
258
|
+
* nothing, so the warm path is free.
|
|
259
|
+
*/
|
|
260
|
+
export interface TaskInputComponent {
|
|
261
|
+
kind: string
|
|
262
|
+
name: string
|
|
263
|
+
hash: string
|
|
252
264
|
}
|
|
253
265
|
|
|
254
266
|
/**
|
|
@@ -556,6 +568,13 @@ export interface CacheLayer {
|
|
|
556
568
|
* no round-trip. Only meaningful with `hasRemote`.
|
|
557
569
|
*/
|
|
558
570
|
markRemoteAbsent?(hashes: Iterable<string>): void
|
|
571
|
+
/**
|
|
572
|
+
* The run's up-front keys, for each remote the run writes but does not
|
|
573
|
+
* read (`--force`): one batch probe in the background, and an upload of
|
|
574
|
+
* a key the remote already holds is skipped. Only meaningful with
|
|
575
|
+
* `hasRemote`.
|
|
576
|
+
*/
|
|
577
|
+
probeBeforeUpload?(hashes: Promise<readonly string[]>): void
|
|
559
578
|
/**
|
|
560
579
|
* Resolve once every background write-through upload settles. The
|
|
561
580
|
* orchestrator awaits it before `close()` — an upload reading layer state
|
|
@@ -563,6 +582,8 @@ export interface CacheLayer {
|
|
|
563
582
|
* uploads synchronously (or not at all) omits it.
|
|
564
583
|
*/
|
|
565
584
|
drainUploads?(): Promise<void>
|
|
585
|
+
/** The run was stopped: start no more uploads, and let `drainUploads` resolve now. */
|
|
586
|
+
stopUploads?(): void
|
|
566
587
|
key(input: CacheKeyInput): Promise<string>
|
|
567
588
|
get(hash: string, ctx?: CacheGetContext): Promise<CacheEntry | null>
|
|
568
589
|
/**
|
|
@@ -580,7 +601,7 @@ export interface CacheLayer {
|
|
|
580
601
|
* artifact read, no remote download, no local ingest, no accessed_at
|
|
581
602
|
* bump (on the remote side the layer decides the call: an HTTP HEAD, or
|
|
582
603
|
* REAPI's `getActionResult` + `findMissingBlobs`). Planning
|
|
583
|
-
* (`--dry`
|
|
604
|
+
* (`--dry`) predicts hits with this instead of `get` so a
|
|
584
605
|
* dry run can't pull N artifacts over the network. Remote errors
|
|
585
606
|
* degrade to `null` — an existence probe never fails anything.
|
|
586
607
|
*/
|
|
@@ -670,13 +691,6 @@ export interface CacheLayer {
|
|
|
670
691
|
entry: Omit<CacheEntry, 'hash' | 'storedAt' | 'outputFiles' | 'exitCode'>
|
|
671
692
|
projectDir: string
|
|
672
693
|
outputFiles: string[]
|
|
673
|
-
/**
|
|
674
|
-
* Set only by ChainedCache when an EARLIER layer in the chain already
|
|
675
|
-
* wrote this artifact to the same local handle: the local pack + write
|
|
676
|
-
* is skipped and only the layer's remote side acts. A layer with no
|
|
677
|
-
* remote side treats it as a full no-op.
|
|
678
|
-
*/
|
|
679
|
-
skipLocalWrite?: boolean
|
|
680
694
|
/**
|
|
681
695
|
* Resolved `outputs.workspaceFiles` (absolute paths) + the root
|
|
682
696
|
* they're relative to. Packed under `workspace-outputs/<rel>`.
|