@vzn/vx 0.0.0 → 0.0.12
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/LICENSE +21 -0
- package/README.md +332 -1
- package/launcher.mjs +84 -0
- package/package.json +44 -3
- package/src/bin.ts +25 -0
- package/src/cache/cache.ts +1832 -0
- package/src/cache/cas-backend.ts +97 -0
- package/src/cache/digest.ts +47 -0
- package/src/cache/index.ts +29 -0
- package/src/cache/inputs.ts +833 -0
- package/src/cache/layered-cache.ts +335 -0
- package/src/cache/remote-cache.ts +251 -0
- package/src/cache/tar.ts +343 -0
- package/src/cli/backend.ts +23 -0
- package/src/cli/cache.ts +101 -0
- package/src/cli/format.ts +15 -0
- package/src/cli/help.ts +123 -0
- package/src/cli/index.ts +85 -0
- package/src/cli/info.ts +83 -0
- package/src/cli/lock.ts +135 -0
- package/src/cli/mcp-rpc.ts +292 -0
- package/src/cli/mcp.ts +92 -0
- package/src/cli/migrate-nx.ts +450 -0
- package/src/cli/migrate-turbo.ts +364 -0
- package/src/cli/migrate.ts +219 -0
- package/src/cli/plan-format.ts +148 -0
- package/src/cli/run.ts +661 -0
- package/src/cli/show.ts +201 -0
- package/src/cli/upgrade.ts +103 -0
- package/src/cli/watch.ts +287 -0
- package/src/config.ts +421 -0
- package/src/exec/env.ts +74 -0
- package/src/exec/index.ts +13 -0
- package/src/exec/runner.ts +470 -0
- package/src/exec/sandbox-runtime.ts +578 -0
- package/src/graph/dependency-spec.ts +69 -0
- package/src/graph/index.ts +18 -0
- package/src/graph/scheduler.ts +482 -0
- package/src/graph/task-graph.ts +325 -0
- package/src/index.ts +197 -0
- package/src/orchestrator/colors.ts +63 -0
- package/src/orchestrator/devframe-surface.ts +74 -0
- package/src/orchestrator/events.ts +277 -0
- package/src/orchestrator/execute-task.ts +883 -0
- package/src/orchestrator/framed-output.ts +461 -0
- package/src/orchestrator/history.ts +148 -0
- package/src/orchestrator/index.ts +163 -0
- package/src/orchestrator/local-shortcircuit.ts +147 -0
- package/src/orchestrator/logger.ts +526 -0
- package/src/orchestrator/metrics.ts +1576 -0
- package/src/orchestrator/options.ts +176 -0
- package/src/orchestrator/plan.ts +129 -0
- package/src/orchestrator/plugin-host.ts +190 -0
- package/src/orchestrator/plugin.ts +283 -0
- package/src/orchestrator/predict.ts +106 -0
- package/src/orchestrator/prepare.ts +297 -0
- package/src/orchestrator/protocol.ts +142 -0
- package/src/orchestrator/remote-cache-setup.ts +60 -0
- package/src/orchestrator/remote-prefetch.ts +81 -0
- package/src/orchestrator/run-artifacts.ts +111 -0
- package/src/orchestrator/run-context.ts +205 -0
- package/src/orchestrator/run-report.ts +132 -0
- package/src/orchestrator/run-state.ts +145 -0
- package/src/orchestrator/run.ts +842 -0
- package/src/orchestrator/stable-keys.ts +177 -0
- package/src/orchestrator/status-line.ts +287 -0
- package/src/orchestrator/summary.ts +291 -0
- package/src/orchestrator/tally.ts +69 -0
- package/src/orchestrator/task-hash.ts +238 -0
- package/src/orchestrator/telemetry-host.ts +80 -0
- package/src/orchestrator/telemetry.ts +350 -0
- package/src/orchestrator/upstream.ts +93 -0
- package/src/orchestrator/verify.ts +141 -0
- package/src/orchestrator/wire-render.ts +92 -0
- package/src/orchestrator/wire.ts +212 -0
- package/src/util/errors.ts +11 -0
- package/src/util/hash.ts +25 -0
- package/src/util/index.ts +7 -0
- package/src/util/paths.ts +16 -0
- package/src/util/ulid.ts +15 -0
- package/src/version.ts +10 -0
- package/src/workspace/affected.ts +118 -0
- package/src/workspace/filter.ts +155 -0
- package/src/workspace/fingerprint.ts +41 -0
- package/src/workspace/index.ts +27 -0
- package/src/workspace/lockfile.ts +118 -0
- package/src/workspace/nested-dirs.ts +34 -0
- package/src/workspace/package-graph.ts +148 -0
- package/src/workspace/project-loader.ts +470 -0
- package/src/workspace/workspace.ts +176 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Markdown run report (`vx run --report=markdown`). A moon-style table —
|
|
2
|
+
// one row per task plus a header line of totals — written verbatim to
|
|
3
|
+
// stdout after a run so CI can append it to a step summary
|
|
4
|
+
// (`vx run ci --report=markdown >> $GITHUB_STEP_SUMMARY`). Pure: the
|
|
5
|
+
// run's finished outcomes in, a markdown string out. No ANSI, no live
|
|
6
|
+
// region — keep it machine-clean and diffable.
|
|
7
|
+
|
|
8
|
+
import type { RunResult } from './protocol.js'
|
|
9
|
+
import type { OutcomeView } from './events.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Outcome buckets for the report header. Mirrors the terminal summary's
|
|
13
|
+
* `tallyOutcomes` partition (success counts hits; `aborted` is no work
|
|
14
|
+
* and excluded), but derives from the serializable `OutcomeView` the CLI
|
|
15
|
+
* holds after a run rather than the in-process `TaskOutcome` — so it
|
|
16
|
+
* needs no graph back-ref and stays self-contained here.
|
|
17
|
+
*/
|
|
18
|
+
interface ReportTally {
|
|
19
|
+
total: number
|
|
20
|
+
successful: number
|
|
21
|
+
failed: number
|
|
22
|
+
skipped: number
|
|
23
|
+
cached: number
|
|
24
|
+
/** Wall-clock ms summed over tasks that actually executed (misses). */
|
|
25
|
+
executedMs: number
|
|
26
|
+
/** Wall-clock ms summed over cache hits — the work the cache skipped. */
|
|
27
|
+
savedMs: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function tally(outcomes: readonly OutcomeView[]): ReportTally {
|
|
31
|
+
const t: ReportTally = {
|
|
32
|
+
total: 0,
|
|
33
|
+
successful: 0,
|
|
34
|
+
failed: 0,
|
|
35
|
+
skipped: 0,
|
|
36
|
+
cached: 0,
|
|
37
|
+
executedMs: 0,
|
|
38
|
+
savedMs: 0,
|
|
39
|
+
}
|
|
40
|
+
for (const o of outcomes) {
|
|
41
|
+
// A child killed by a shutdown signal (Ctrl-C teardown) is no work —
|
|
42
|
+
// excluded from totals and every bucket, like the terminal tally.
|
|
43
|
+
if (o.status === 'aborted') continue
|
|
44
|
+
t.total++
|
|
45
|
+
switch (o.status) {
|
|
46
|
+
case 'success':
|
|
47
|
+
t.successful++
|
|
48
|
+
t.executedMs += o.durationMs
|
|
49
|
+
break
|
|
50
|
+
case 'cache-hit':
|
|
51
|
+
case 'cache-hit-remote':
|
|
52
|
+
t.successful++
|
|
53
|
+
t.cached++
|
|
54
|
+
t.savedMs += o.durationMs
|
|
55
|
+
break
|
|
56
|
+
case 'failed':
|
|
57
|
+
t.failed++
|
|
58
|
+
t.executedMs += o.durationMs
|
|
59
|
+
break
|
|
60
|
+
case 'skipped':
|
|
61
|
+
t.skipped++
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return t
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function statusWord(o: OutcomeView): string {
|
|
69
|
+
switch (o.status) {
|
|
70
|
+
case 'success':
|
|
71
|
+
return 'success'
|
|
72
|
+
case 'cache-hit':
|
|
73
|
+
case 'cache-hit-remote':
|
|
74
|
+
return 'success'
|
|
75
|
+
case 'failed':
|
|
76
|
+
return `failed (exit ${o.exitCode})`
|
|
77
|
+
default:
|
|
78
|
+
return o.status
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function cacheWord(o: OutcomeView): string {
|
|
83
|
+
switch (o.status) {
|
|
84
|
+
case 'cache-hit':
|
|
85
|
+
return o.restored === false ? 'up-to-date' : 'local'
|
|
86
|
+
case 'cache-hit-remote':
|
|
87
|
+
return o.restored === false ? 'up-to-date' : 'remote'
|
|
88
|
+
case 'success':
|
|
89
|
+
case 'failed':
|
|
90
|
+
return 'miss'
|
|
91
|
+
default:
|
|
92
|
+
return '—'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function fmtDuration(ms: number): string {
|
|
97
|
+
if (ms < 1000) return `${Math.round(ms)}ms`
|
|
98
|
+
return `${(ms / 1000).toFixed(2)}s`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Render the finished run as a markdown report. A header line of totals
|
|
103
|
+
* followed by a per-task table. Self-contained — everything is derived
|
|
104
|
+
* from the result's outcomes.
|
|
105
|
+
*/
|
|
106
|
+
export function formatRunReportMarkdown(result: RunResult): string {
|
|
107
|
+
const t = tally(result.outcomes)
|
|
108
|
+
const parts = [
|
|
109
|
+
`**${t.total} task${t.total === 1 ? '' : 's'}**`,
|
|
110
|
+
`${t.successful} success`,
|
|
111
|
+
`${t.failed} failed`,
|
|
112
|
+
`${t.cached} cached`,
|
|
113
|
+
]
|
|
114
|
+
if (t.skipped > 0) parts.push(`${t.skipped} skipped`)
|
|
115
|
+
parts.push(`${fmtDuration(t.executedMs)} total`)
|
|
116
|
+
if (t.savedMs > 0) parts.push(`${fmtDuration(t.savedMs)} saved`)
|
|
117
|
+
|
|
118
|
+
const lines: string[] = []
|
|
119
|
+
lines.push(`## vx run — ${result.ok ? 'passed' : 'failed'}`)
|
|
120
|
+
lines.push('')
|
|
121
|
+
lines.push(parts.join(' · '))
|
|
122
|
+
lines.push('')
|
|
123
|
+
lines.push('| Task | Status | Cache | Duration |')
|
|
124
|
+
lines.push('| --- | --- | --- | --- |')
|
|
125
|
+
for (const o of result.outcomes) {
|
|
126
|
+
if (o.status === 'aborted') continue
|
|
127
|
+
lines.push(
|
|
128
|
+
`| ${o.taskId} | ${statusWord(o)} | ${cacheWord(o)} | ${fmtDuration(o.durationMs)} |`,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
return lines.join('\n') + '\n'
|
|
132
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// The derived run aggregate — the single view-model every surface holds.
|
|
2
|
+
// Raw RunEvents are the source of truth; this reduces them into the same
|
|
3
|
+
// counters/per-task status the terminal summary already renders, so a
|
|
4
|
+
// web/TUI/MCP surface can render reactively from one place instead of
|
|
5
|
+
// re-deriving. A devframe shared-state holds a RunState and re-reduces on
|
|
6
|
+
// each event; the terminal keeps its own inline counters (byte-identical,
|
|
7
|
+
// untouched). See docs/design/event-stream-2026-06.md.
|
|
8
|
+
|
|
9
|
+
import { isGroupTask, type TaskStatus } from '../graph/index.js'
|
|
10
|
+
import type { RunEvent } from './events.js'
|
|
11
|
+
|
|
12
|
+
/** Per-task lifecycle as seen by a surface. */
|
|
13
|
+
export type TaskState = 'running' | TaskStatus
|
|
14
|
+
|
|
15
|
+
export interface TaskRecord {
|
|
16
|
+
id: string
|
|
17
|
+
state: TaskState
|
|
18
|
+
/** Final duration once complete; absent while running. */
|
|
19
|
+
durationMs?: number
|
|
20
|
+
/** True for a persistent task left running after its outcome landed. */
|
|
21
|
+
persistent?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Cache-miss duration spread — the same numbers the final summary shows. */
|
|
25
|
+
export interface Spread {
|
|
26
|
+
maxMs: number
|
|
27
|
+
minMs: number
|
|
28
|
+
sumMs: number
|
|
29
|
+
count: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RunState {
|
|
33
|
+
total: number
|
|
34
|
+
done: number
|
|
35
|
+
failed: number
|
|
36
|
+
succeeded: number
|
|
37
|
+
upToDate: number
|
|
38
|
+
restoredLocal: number
|
|
39
|
+
restoredRemote: number
|
|
40
|
+
skipped: number
|
|
41
|
+
/** Ids currently executing (a worker holds them). */
|
|
42
|
+
running: string[]
|
|
43
|
+
/** Per-task records, keyed by id. Group tasks are excluded (no work). */
|
|
44
|
+
tasks: Record<string, TaskRecord>
|
|
45
|
+
spread: Spread | null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function initRunState(total = 0): RunState {
|
|
49
|
+
return {
|
|
50
|
+
total,
|
|
51
|
+
done: 0,
|
|
52
|
+
failed: 0,
|
|
53
|
+
succeeded: 0,
|
|
54
|
+
upToDate: 0,
|
|
55
|
+
restoredLocal: 0,
|
|
56
|
+
restoredRemote: 0,
|
|
57
|
+
skipped: 0,
|
|
58
|
+
running: [],
|
|
59
|
+
tasks: {},
|
|
60
|
+
spread: null,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fold one event into the aggregate, returning a NEW state (pure — never
|
|
66
|
+
* mutates `prev`, so it's safe to feed a devframe `sharedState.mutate`
|
|
67
|
+
* that diffs old vs new). Mirrors `defaultLogger`'s inline bookkeeping:
|
|
68
|
+
* group tasks (no exec) are ignored, an `aborted` task reverts to pending
|
|
69
|
+
* (freed, never counted), and the spread tracks success+failed only.
|
|
70
|
+
*/
|
|
71
|
+
export function reduce(prev: RunState, event: RunEvent): RunState {
|
|
72
|
+
switch (event.kind) {
|
|
73
|
+
case 'run:start':
|
|
74
|
+
return initRunState(event.info.total)
|
|
75
|
+
|
|
76
|
+
case 'task:start': {
|
|
77
|
+
if (isGroupTask(event.node)) return prev
|
|
78
|
+
const id = event.node.id
|
|
79
|
+
return {
|
|
80
|
+
...prev,
|
|
81
|
+
running: [...prev.running, id],
|
|
82
|
+
tasks: { ...prev.tasks, [id]: { id, state: 'running' } },
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case 'task:complete': {
|
|
87
|
+
if (isGroupTask(event.node)) return prev
|
|
88
|
+
const id = event.node.id
|
|
89
|
+
const { status, durationMs } = event.outcome
|
|
90
|
+
const running = prev.running.filter((r) => r !== id)
|
|
91
|
+
|
|
92
|
+
// Aborted (killed by a shutdown signal): free the slot, count
|
|
93
|
+
// nothing, drop the record — the run is tearing down.
|
|
94
|
+
if (status === 'aborted') {
|
|
95
|
+
const tasks = { ...prev.tasks }
|
|
96
|
+
delete tasks[id]
|
|
97
|
+
return { ...prev, running, tasks }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const persistent = event.node.config.exec?.persistent !== undefined && status === 'success'
|
|
101
|
+
const next: RunState = {
|
|
102
|
+
...prev,
|
|
103
|
+
done: prev.done + 1,
|
|
104
|
+
running,
|
|
105
|
+
tasks: {
|
|
106
|
+
...prev.tasks,
|
|
107
|
+
[id]: { id, state: status, durationMs, ...(persistent ? { persistent: true } : {}) },
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
switch (status) {
|
|
111
|
+
case 'success':
|
|
112
|
+
next.succeeded = prev.succeeded + 1
|
|
113
|
+
break
|
|
114
|
+
case 'cache-hit':
|
|
115
|
+
if (event.outcome.restored === false) next.upToDate = prev.upToDate + 1
|
|
116
|
+
else next.restoredLocal = prev.restoredLocal + 1
|
|
117
|
+
break
|
|
118
|
+
case 'cache-hit-remote':
|
|
119
|
+
if (event.outcome.restored === false) next.upToDate = prev.upToDate + 1
|
|
120
|
+
else next.restoredRemote = prev.restoredRemote + 1
|
|
121
|
+
break
|
|
122
|
+
case 'failed':
|
|
123
|
+
next.failed = prev.failed + 1
|
|
124
|
+
break
|
|
125
|
+
case 'skipped':
|
|
126
|
+
next.skipped = prev.skipped + 1
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
if (status === 'success' || status === 'failed') {
|
|
130
|
+
const s = prev.spread
|
|
131
|
+
next.spread = {
|
|
132
|
+
maxMs: Math.max(s?.maxMs ?? 0, durationMs),
|
|
133
|
+
minMs: Math.min(s?.minMs ?? Infinity, durationMs),
|
|
134
|
+
sumMs: (s?.sumMs ?? 0) + durationMs,
|
|
135
|
+
count: (s?.count ?? 0) + 1,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return next
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// stdout/stderr/status/end carry no aggregate signal.
|
|
142
|
+
default:
|
|
143
|
+
return prev
|
|
144
|
+
}
|
|
145
|
+
}
|