@vzn/vx 0.0.650 → 0.0.651
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 +5 -20
- package/package.json +5 -5
- package/schemas/last.json +15 -0
- package/src/bin.ts +9 -31
- package/src/cache/cache.ts +202 -25
- package/src/cache/file-hashes.ts +3 -0
- package/src/cache/git-inputs.ts +188 -39
- package/src/cache/index.ts +1 -0
- package/src/cache/inputs.ts +3 -0
- package/src/cache/layer.ts +11 -0
- package/src/cache/layered-cache.ts +4 -0
- package/src/cache/linked-inputs.ts +3 -0
- package/src/cache/lockfile-memo.ts +3 -2
- package/src/cache/output-index.ts +5 -2
- package/src/cache/remote-lane.ts +2 -1
- package/src/cache/run-history.ts +11 -1
- package/src/cache/schema.ts +11 -1
- package/src/cli/cache.ts +26 -16
- package/src/cli/completions.ts +4 -2
- package/src/cli/docs.ts +10 -11
- package/src/cli/help.ts +12 -23
- package/src/cli/index.ts +4 -9
- package/src/cli/info.ts +15 -7
- package/src/cli/init.ts +23 -15
- package/src/cli/last.ts +213 -203
- package/src/cli/lock.ts +55 -5
- package/src/cli/plan-format.ts +100 -118
- package/src/cli/plugin-commands.ts +8 -2
- package/src/cli/plugin-templates.ts +2 -2
- package/src/cli/run.ts +46 -27
- package/src/cli/select.ts +43 -5
- package/src/cli/show.ts +38 -42
- package/src/cli/task-verb.ts +22 -0
- package/src/cli/upgrade.ts +3 -1
- package/src/cli/watch-fs.ts +6 -1
- package/src/cli/watch-judge.ts +2 -1
- package/src/cli/watch.ts +83 -44
- package/src/cli/why.ts +123 -89
- package/src/cli/workspace-config.ts +2 -1
- package/src/exec/executor.ts +2 -1
- package/src/exec/kill-tree.ts +58 -5
- package/src/exec/local-executor.ts +1 -1
- package/src/exec/pipes.ts +152 -0
- package/src/exec/runner.ts +199 -182
- package/src/exec/sandbox-binds.ts +1 -1
- package/src/exec/sandbox-runtime.ts +11 -2
- package/src/exec/spawn-off.ts +154 -0
- package/src/exec/warm-shell.ts +146 -0
- package/src/graph/critical-path.ts +40 -28
- package/src/graph/priorities.ts +98 -44
- package/src/graph/scheduler.ts +14 -10
- package/src/graph/task-graph.ts +27 -1
- package/src/handoff.ts +10 -7
- package/src/history/history.ts +5 -0
- package/src/history/metrics.ts +9 -1
- package/src/index.ts +3 -1
- package/src/orchestrator/doctor.ts +1 -1
- package/src/orchestrator/execute-task.ts +16 -2
- package/src/orchestrator/graph-memo.ts +50 -1
- package/src/orchestrator/graph-stage.ts +6 -0
- package/src/orchestrator/index.ts +3 -0
- package/src/orchestrator/live-usage.ts +16 -11
- package/src/orchestrator/locked-digests.ts +63 -0
- package/src/orchestrator/lockfile-claim.ts +108 -29
- package/src/orchestrator/miss-save.ts +31 -3
- package/src/orchestrator/persistent-task.ts +3 -1
- package/src/orchestrator/plan.ts +5 -1
- package/src/orchestrator/plugin-host.ts +61 -0
- package/src/orchestrator/plugin.ts +47 -2
- package/src/orchestrator/prepare.ts +16 -1
- package/src/orchestrator/projects.ts +4 -1
- package/src/orchestrator/run-context.ts +29 -10
- package/src/orchestrator/run-failures.ts +1 -1
- package/src/orchestrator/run-records.ts +10 -0
- package/src/orchestrator/run.ts +53 -22
- package/src/orchestrator/sandbox-request.ts +2 -2
- package/src/orchestrator/signals.ts +8 -5
- package/src/orchestrator/task-hash.ts +17 -3
- package/src/report/events.ts +3 -11
- package/src/report/forecast.ts +10 -32
- package/src/report/framed-output.ts +272 -173
- package/src/report/index.ts +8 -3
- package/src/report/logger.ts +73 -38
- package/src/report/plain-output.ts +1 -1
- package/src/report/status-line.ts +49 -357
- package/src/report/summary.ts +100 -131
- package/src/ui/ansi.ts +48 -0
- package/src/ui/components.ts +506 -0
- package/src/ui/index.ts +79 -0
- package/src/ui/kit.ts +37 -0
- package/src/ui/live-region.ts +363 -0
- package/src/{util/refusal-notice.ts → ui/refusal.ts} +7 -5
- package/src/ui/terminal.ts +51 -0
- package/src/ui/theme.ts +224 -0
- package/src/ui/version-line.ts +28 -0
- package/src/util/config-frame.ts +65 -10
- package/src/util/edit-distance.ts +6 -1
- package/src/util/index.ts +2 -15
- package/src/util/paths.ts +20 -2
- package/src/util/secret-mask.ts +39 -3
- package/src/util/settle.ts +31 -0
- package/src/util/timing.ts +4 -16
- package/src/workspace/affected.ts +89 -30
- package/src/workspace/config-schema.ts +4 -2
- package/src/workspace/dependencies-lock.ts +191 -0
- package/src/workspace/filter.ts +28 -0
- package/src/workspace/index.ts +9 -0
- package/src/workspace/lockfile.ts +159 -6
- package/src/workspace/migration.ts +4 -3
- package/src/workspace/workspace.ts +3 -0
- package/src/util/colors.ts +0 -133
- package/src/util/report.ts +0 -44
- package/src/util/ui.ts +0 -258
- /package/src/{report/path-links.ts → ui/links.ts} +0 -0
- /package/src/{report → ui}/terminal-signals.ts +0 -0
package/src/util/ui.ts
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
// vx's output components (org/devex/log-design.md). A verb or a plugin
|
|
2
|
-
// composes these instead of writing raw lines: each component owns its
|
|
3
|
-
// color, indent and spacing, so every surface shares one look. Colors
|
|
4
|
-
// are decided at print time, per stream.
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
detectColors,
|
|
8
|
-
ERROR,
|
|
9
|
-
identityColor,
|
|
10
|
-
paint,
|
|
11
|
-
SUCCESS,
|
|
12
|
-
WARN,
|
|
13
|
-
type ColorSupport,
|
|
14
|
-
} from './colors.js'
|
|
15
|
-
import { gradientRule } from './report.js'
|
|
16
|
-
|
|
17
|
-
export type Tone = 'ok' | 'warn' | 'error' | 'info'
|
|
18
|
-
|
|
19
|
-
const INFO = '#06b6d4'
|
|
20
|
-
const HUE: Record<Tone, string> = { ok: SUCCESS, warn: WARN, error: ERROR, info: INFO }
|
|
21
|
-
const SYMBOL: Record<Tone, string> = { ok: '✓', warn: '!', error: '✗', info: '›' }
|
|
22
|
-
const INDENT = ' '
|
|
23
|
-
const LABEL_MIN = 8
|
|
24
|
-
|
|
25
|
-
type Style = Tone | 'dim' | 'bold' | 'id'
|
|
26
|
-
|
|
27
|
-
/** Styled text inside a component: a plain string, a token, or a run of them. */
|
|
28
|
-
export type Inline = string | { readonly style: Style; readonly text: string } | readonly Inline[]
|
|
29
|
-
|
|
30
|
-
/** A laid-out piece of output; `render` and `print` space blocks apart. */
|
|
31
|
-
export interface Block {
|
|
32
|
-
readonly lines: (colors: ColorSupport) => string[]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** A `project` or `project#task` id, each half in its name's hue, the `#` dim. */
|
|
36
|
-
export const id = (text: string): Inline => ({ style: 'id', text })
|
|
37
|
-
/** A path, glob or file: bold. */
|
|
38
|
-
export const path = (text: string): Inline => ({ style: 'bold', text })
|
|
39
|
-
/** A command to type: bold. */
|
|
40
|
-
export const cmd = (text: string): Inline => ({ style: 'bold', text })
|
|
41
|
-
/** A secondary fact. */
|
|
42
|
-
export const dim = (text: string): Inline => ({ style: 'dim', text })
|
|
43
|
-
/** Where a task runs (`small#1`): its name's hue in every row and block; `local` plain. */
|
|
44
|
-
export const place = (text: string): Inline => (text === 'local' ? text : id(text))
|
|
45
|
-
/** Text in a tone's color. */
|
|
46
|
-
export const tone = (t: Tone, text: string): Inline => ({ style: t, text })
|
|
47
|
-
|
|
48
|
-
// oxlint-disable-next-line no-control-regex
|
|
49
|
-
const CONTROL = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* `s` with every control byte but a tab and a line break as `\xNN`: a
|
|
53
|
-
* notice quotes paths and names, anyone's bytes, and one holding `ESC ]0;`
|
|
54
|
-
* retitled the terminal. The colors are the components' own, added after.
|
|
55
|
-
*/
|
|
56
|
-
const plainText = (s: string): string =>
|
|
57
|
-
s.replace(CONTROL, (ch) => `\\x${ch.charCodeAt(0).toString(16).padStart(2, '0')}`)
|
|
58
|
-
|
|
59
|
-
/** One inline as text, colored for `c`. */
|
|
60
|
-
export function inline(x: Inline, c: ColorSupport): string {
|
|
61
|
-
if (typeof x === 'string') return plainText(x)
|
|
62
|
-
if (Array.isArray(x)) return (x as readonly Inline[]).map((p) => inline(p, c)).join('')
|
|
63
|
-
const { style } = x as { style: Style; text: string }
|
|
64
|
-
const text = plainText((x as { text: string }).text)
|
|
65
|
-
switch (style) {
|
|
66
|
-
case 'dim':
|
|
67
|
-
return paint('', text, c, { dim: true })
|
|
68
|
-
case 'bold':
|
|
69
|
-
return paint('', text, c, { bold: true })
|
|
70
|
-
case 'id': {
|
|
71
|
-
const hash = text.lastIndexOf('#')
|
|
72
|
-
if (hash < 0) return paint(identityColor(text), text, c)
|
|
73
|
-
const task = text.slice(hash + 1)
|
|
74
|
-
return (
|
|
75
|
-
paint(identityColor(text.slice(0, hash)), text.slice(0, hash), c) +
|
|
76
|
-
paint('', '#', c, { dim: true }) +
|
|
77
|
-
paint(identityColor(task), task, c)
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
default:
|
|
81
|
-
return paint(HUE[style], text, c)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const block = (lines: (c: ColorSupport) => string[]): Block => ({ lines })
|
|
86
|
-
const lines = (x: Inline, c: ColorSupport): string[] => inline(x, c).split('\n')
|
|
87
|
-
const visible = (s: string): number => Bun.stringWidth(s)
|
|
88
|
-
|
|
89
|
-
/** `✗ vx lock lock not written`: the tone's symbol, the bold verb, the headline. */
|
|
90
|
-
export function status(t: Tone, verb: string, headline: Inline): Block {
|
|
91
|
-
return block((c) => [
|
|
92
|
-
`${paint(HUE[t], SYMBOL[t], c)} ${paint('', verb, c, { bold: true })} ${inline(headline, c)}`,
|
|
93
|
-
])
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* A line vx says mid-run (`[vx] cache save failed: …`, `vx watch: …`), its
|
|
98
|
-
* words as they always read and its tag in the tone's hue; a line with no
|
|
99
|
-
* tag is painted whole. One string, no newline: the run's own streams write it.
|
|
100
|
-
*/
|
|
101
|
-
export function tagged(t: Tone, line: string, c: ColorSupport): string {
|
|
102
|
-
const body = line.replace(/\n+$/, '')
|
|
103
|
-
const tag = /^(?:\[vx\]|vx(?: [a-z][\w-]*){0,2}:|[\w@/.-]+: )/.exec(body)?.[0].trimEnd()
|
|
104
|
-
return tag === undefined
|
|
105
|
-
? paint(HUE[t], body, c)
|
|
106
|
-
: `${paint(HUE[t], tag, c, { bold: true })}${body.slice(tag.length)}`
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** `tagged` on a stream (stderr unless told), colored for it. */
|
|
110
|
-
export function say(t: Tone, line: string, stream: 'out' | 'err' = 'err'): void {
|
|
111
|
-
const s = stream === 'out' ? process.stdout : process.stderr
|
|
112
|
-
s.write(`${tagged(t, line, detectColors(s))}\n`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* A file or excerpt shown whole (`vx init --dry`, `vx docs`): its title rule
|
|
117
|
-
* bold, its body behind a dim gutter. Uncolored it is the bare text, so a
|
|
118
|
-
* pipe gets the bytes it would copy.
|
|
119
|
-
*/
|
|
120
|
-
export function excerpt(title: string, body: string): Block {
|
|
121
|
-
return block((c) => {
|
|
122
|
-
if (!c.enabled) return [`── ${title} ──`, ...body.split('\n')]
|
|
123
|
-
const gutter = paint(INFO, '│', c, { dim: true })
|
|
124
|
-
return [
|
|
125
|
-
paint(INFO, `── ${title} ──`, c, { bold: true }),
|
|
126
|
-
...body
|
|
127
|
-
.split('\n')
|
|
128
|
-
.map((l, i, all) => (i === all.length - 1 && l === '' ? l : `${gutter} ${l}`)),
|
|
129
|
-
]
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Indented short lines: what is wrong, or what happened. */
|
|
134
|
-
export function text(...items: Inline[]): Block {
|
|
135
|
-
return block((c) =>
|
|
136
|
-
items.flatMap((i) =>
|
|
137
|
-
lines(typeof i === 'string' ? prose(i) : i, c).map((l) => `${INDENT}${l}`),
|
|
138
|
-
),
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Plain words dim, each `command` in them bold: no line of vx reads plain white. */
|
|
143
|
-
function prose(text: string): Inline {
|
|
144
|
-
return text.split(/(`[^`\n]+`)/).map((p, i) => (i % 2 === 1 ? cmd(p) : p === '' ? p : dim(p)))
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** An indented dim line: why it matters. */
|
|
148
|
-
export function why(line: Inline): Block {
|
|
149
|
-
return block((c) => lines(line, c).map((l) => `${INDENT}${paint('', l, c, { dim: true })}`))
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** A label and its values; values after the first sit under it. `null` is a blank line. */
|
|
153
|
-
export type FieldRow = readonly [label: string, ...values: Inline[]] | null
|
|
154
|
-
|
|
155
|
-
/** A dim label column with the values aligned beside it; `next` and `fix` are bold. */
|
|
156
|
-
export function fields(rows: readonly FieldRow[]): Block {
|
|
157
|
-
return block((c) => {
|
|
158
|
-
const width = Math.max(LABEL_MIN, ...rows.map((r) => (r === null ? 0 : r[0].length)))
|
|
159
|
-
const out: string[] = []
|
|
160
|
-
for (const row of rows) {
|
|
161
|
-
if (row === null) {
|
|
162
|
-
out.push('')
|
|
163
|
-
continue
|
|
164
|
-
}
|
|
165
|
-
const [label, ...values] = row
|
|
166
|
-
const strong = label === 'next' || label === 'fix'
|
|
167
|
-
values
|
|
168
|
-
.flatMap((v) => lines(v, c))
|
|
169
|
-
.forEach((v, i) => {
|
|
170
|
-
const head =
|
|
171
|
-
i === 0 ? paint('', label.padEnd(width), c, { dim: true }) : ' '.repeat(width)
|
|
172
|
-
const value = strong ? paint('', v, c, { bold: true }) : v
|
|
173
|
-
out.push(`${INDENT}${head} ${value}`.trimEnd())
|
|
174
|
-
})
|
|
175
|
-
}
|
|
176
|
-
return out
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** `fix <action>`: what to do about it. */
|
|
181
|
-
export function fix(action: Inline): Block {
|
|
182
|
-
return labelled('fix', action)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/** `next <command>`: the command to type next. */
|
|
186
|
-
export function next(command: Inline): Block {
|
|
187
|
-
return labelled('next', command)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function labelled(label: string, value: Inline): Block {
|
|
191
|
-
return block((c) => [
|
|
192
|
-
`${INDENT}${paint('', label, c, { dim: true })} ${paint('', inline(value, c), c, { bold: true })}`,
|
|
193
|
-
])
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** A report: a blank line, the brand rule naming `mark`, then its fields. */
|
|
197
|
-
export function report(mark: string, rows: readonly FieldRow[]): Block {
|
|
198
|
-
return block((c) => ['', gradientRule(c, mark), ...fields(rows).lines(c)])
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** Aligned columns under a dim header. */
|
|
202
|
-
export function table(columns: readonly string[], rows: readonly (readonly Inline[])[]): Block {
|
|
203
|
-
return block((c) => {
|
|
204
|
-
const cells = rows.map((r) => r.map((x) => inline(x, c)))
|
|
205
|
-
const widths = columns.map((h, i) =>
|
|
206
|
-
Math.max(h.length, ...cells.map((r) => visible(r[i] ?? ''))),
|
|
207
|
-
)
|
|
208
|
-
const pad = (s: string, i: number): string =>
|
|
209
|
-
i === columns.length - 1 ? s : s + ' '.repeat(widths[i]! - visible(s))
|
|
210
|
-
return [
|
|
211
|
-
`${INDENT}${paint('', columns.map((h, i) => pad(h, i)).join(' '), c, { dim: true })}`.trimEnd(),
|
|
212
|
-
...cells.map((r) => `${INDENT}${r.map(pad).join(' ')}`.trimEnd()),
|
|
213
|
-
]
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** A source frame (`> 1 | …` and its caret), indented. */
|
|
218
|
-
export function code(frame: string): Block {
|
|
219
|
-
return block(() => frame.split('\n').map((l) => (l === '' ? '' : `${INDENT}${l}`)))
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export interface NoticeParts {
|
|
223
|
-
tone: Tone
|
|
224
|
-
verb: string
|
|
225
|
-
headline: Inline
|
|
226
|
-
/** What is wrong, one fact a line. */
|
|
227
|
-
what?: readonly Inline[]
|
|
228
|
-
why?: Inline
|
|
229
|
-
/** A source frame, below the facts. */
|
|
230
|
-
code?: string
|
|
231
|
-
fix?: Inline
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** A refusal or a warning: status, then what and why, the frame, and the fix, a blank line apart. */
|
|
235
|
-
export function notice(p: NoticeParts): Block {
|
|
236
|
-
return block((c) => {
|
|
237
|
-
const parts: Block[] = [status(p.tone, p.verb, p.headline)]
|
|
238
|
-
const body = [
|
|
239
|
-
...(p.what !== undefined ? [text(...p.what)] : []),
|
|
240
|
-
...(p.why !== undefined ? [why(p.why)] : []),
|
|
241
|
-
]
|
|
242
|
-
if (body.length > 0) parts.push(block((cc) => body.flatMap((b) => b.lines(cc))))
|
|
243
|
-
if (p.code !== undefined) parts.push(code(p.code))
|
|
244
|
-
if (p.fix !== undefined) parts.push(fix(p.fix))
|
|
245
|
-
return parts.flatMap((b, i) => (i === 0 ? b.lines(c) : ['', ...b.lines(c)]))
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** The blocks as text, a blank line apart, ending in a newline. */
|
|
250
|
-
export function render(blocks: readonly Block[], colors: ColorSupport): string {
|
|
251
|
-
return `${blocks.map((b) => b.lines(colors).join('\n')).join('\n\n')}\n`
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** Write the blocks to stdout (`out`) or stderr (`err`), colored for that stream. */
|
|
255
|
-
export function print(stream: 'out' | 'err', ...blocks: Block[]): void {
|
|
256
|
-
const s = stream === 'out' ? process.stdout : process.stderr
|
|
257
|
-
s.write(render(blocks, detectColors(s)))
|
|
258
|
-
}
|
|
File without changes
|
|
File without changes
|