@vzn/vx 0.0.645 → 0.0.648
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 -50
- package/launcher.cjs +9 -2
- package/launcher.sh +2 -2
- package/package.json +5 -5
- package/schemas/cache.json +5 -5
- package/schemas/error.json +1 -1
- package/schemas/info.json +16 -2
- package/schemas/last.json +127 -5
- package/schemas/plan.json +28 -2
- package/schemas/show.json +10 -1
- package/skills/vx/SKILL.md +19 -3
- package/src/bin.ts +54 -13
- package/src/cache/archive.ts +324 -23
- package/src/cache/cache.ts +330 -65
- package/src/cache/git-inputs.ts +274 -97
- package/src/cache/index.ts +7 -2
- package/src/cache/inputs.ts +515 -83
- package/src/cache/key-fold.ts +15 -6
- package/src/cache/layer.ts +74 -11
- package/src/cache/layered-cache.ts +154 -59
- package/src/cache/linked-inputs.ts +1 -1
- package/src/cache/output-index.ts +44 -14
- package/src/cache/plugin-cache.ts +49 -23
- package/src/cache/remote-hits.ts +72 -0
- package/src/cache/remote-lane.ts +100 -15
- package/src/cache/run-history.ts +77 -23
- package/src/cache/run-store.ts +11 -10
- package/src/cache/schema.ts +42 -6
- package/src/cache/tar-stream.ts +5 -0
- package/src/cache/umask.ts +1 -2
- package/src/cli/cache.ts +58 -27
- package/src/cli/completions.ts +12 -5
- package/src/cli/core-alias.ts +5 -2
- package/src/cli/docs.ts +42 -16
- package/src/cli/help.ts +62 -25
- package/src/cli/index.ts +79 -45
- package/src/cli/info.ts +12 -6
- package/src/cli/init.ts +222 -34
- package/src/cli/last.ts +443 -47
- package/src/cli/lock.ts +197 -52
- package/src/cli/plan-format.ts +91 -49
- package/src/cli/plugin-commands.ts +160 -11
- package/src/cli/plugin-templates.ts +13 -13
- package/src/cli/registry.ts +209 -0
- package/src/cli/run.ts +111 -41
- package/src/cli/select.ts +148 -49
- package/src/cli/show.ts +59 -16
- package/src/cli/task-verb.ts +16 -6
- package/src/cli/upgrade.ts +155 -54
- package/src/cli/watch-cycle.ts +10 -5
- package/src/cli/watch-filter.ts +71 -3
- package/src/cli/watch-fs.ts +22 -6
- package/src/cli/watch-judge.ts +48 -6
- package/src/cli/watch.ts +163 -42
- package/src/cli/why.ts +211 -73
- package/src/cli/workspace-config.ts +54 -15
- package/src/config.ts +13 -1
- package/src/exec/env.ts +7 -2
- package/src/exec/executor.ts +262 -15
- package/src/exec/index.ts +8 -2
- package/src/exec/kill-tree.ts +15 -0
- package/src/exec/local-executor.ts +1 -1
- package/src/exec/proc-sample.ts +70 -6
- package/src/exec/runner.ts +256 -53
- package/src/exec/sandbox-binds.ts +7 -3
- package/src/exec/sandbox-paths.ts +1 -1
- package/src/exec/sandbox-runtime.ts +222 -28
- package/src/exec/sandbox-violations.ts +52 -20
- package/src/graph/critical-path.ts +79 -0
- package/src/graph/default-build.ts +19 -0
- package/src/graph/definition.ts +38 -0
- package/src/graph/folds.ts +38 -5
- package/src/graph/index.ts +4 -1
- package/src/graph/priorities.ts +74 -8
- package/src/graph/scheduler.ts +326 -65
- package/src/graph/task-graph.ts +228 -35
- package/src/handoff.ts +97 -9
- package/src/history/failure-mode.ts +6 -3
- package/src/history/history.ts +268 -50
- package/src/history/index.ts +2 -0
- package/src/history/metrics.ts +142 -45
- package/src/index.ts +42 -10
- package/src/orchestrator/admission.ts +10 -6
- package/src/orchestrator/affected-tasks.ts +11 -6
- package/src/orchestrator/deferred-outputs.ts +2 -0
- package/src/orchestrator/doctor.ts +134 -42
- package/src/orchestrator/execute-args.ts +6 -0
- package/src/orchestrator/execute-task.ts +155 -42
- package/src/orchestrator/fingerprint-watch.ts +1 -1
- package/src/orchestrator/graph-memo.ts +140 -0
- package/src/orchestrator/graph-stage.ts +18 -1
- package/src/orchestrator/held-servers.ts +86 -0
- package/src/orchestrator/hit-restore.ts +227 -122
- package/src/orchestrator/index.ts +20 -3
- package/src/orchestrator/local-floor.ts +198 -0
- package/src/orchestrator/local-shortcircuit.ts +51 -8
- package/src/orchestrator/lockfile-claim.ts +96 -16
- package/src/orchestrator/miss-save.ts +52 -12
- package/src/orchestrator/options.ts +8 -6
- package/src/orchestrator/output-host.ts +43 -14
- package/src/orchestrator/output-log.ts +90 -10
- package/src/orchestrator/persistent-task.ts +97 -28
- package/src/orchestrator/persistent.ts +16 -4
- package/src/orchestrator/placement.ts +257 -14
- package/src/orchestrator/plan.ts +64 -7
- package/src/orchestrator/plugin-host.ts +230 -40
- package/src/orchestrator/plugin.ts +150 -37
- package/src/orchestrator/prepare.ts +178 -32
- package/src/orchestrator/projects.ts +65 -40
- package/src/orchestrator/remote-prefetch.ts +119 -25
- package/src/orchestrator/run-artifacts.ts +95 -32
- package/src/orchestrator/run-context.ts +39 -41
- package/src/orchestrator/run-failures.ts +65 -23
- package/src/orchestrator/run-id.ts +6 -3
- package/src/orchestrator/run-keys.ts +300 -0
- package/src/orchestrator/run-live.ts +294 -0
- package/src/orchestrator/run-lock.ts +13 -7
- package/src/orchestrator/run-records.ts +35 -23
- package/src/orchestrator/run-report.ts +3 -3
- package/src/orchestrator/run-steps.ts +58 -22
- package/src/orchestrator/run-stop.ts +22 -5
- package/src/orchestrator/run.ts +409 -79
- package/src/orchestrator/sandbox-fix.ts +7 -5
- package/src/orchestrator/sandbox-request.ts +78 -22
- package/src/orchestrator/shell-verdict.ts +5 -5
- package/src/orchestrator/signals.ts +26 -1
- package/src/orchestrator/stable-keys.ts +92 -8
- package/src/orchestrator/steps.ts +2 -2
- package/src/orchestrator/task-artifacts.ts +206 -0
- package/src/orchestrator/task-hash.ts +89 -14
- package/src/orchestrator/task-log-buffer.ts +72 -36
- package/src/orchestrator/task-process.ts +22 -0
- package/src/orchestrator/telemetry-host.ts +24 -3
- package/src/orchestrator/telemetry.ts +113 -38
- package/src/orchestrator/upstream.ts +15 -0
- package/src/report/events.ts +32 -13
- package/src/report/forecast.ts +36 -35
- package/src/report/framed-output.ts +173 -46
- package/src/report/index.ts +1 -0
- package/src/report/logger.ts +217 -186
- package/src/report/path-links.ts +12 -4
- package/src/report/plain-output.ts +13 -5
- package/src/report/status-line.ts +94 -46
- package/src/report/summary.ts +174 -38
- package/src/report/terminal-signals.ts +13 -2
- package/src/settings.ts +14 -6
- package/src/util/bun-version.ts +4 -1
- package/src/util/colors.ts +20 -3
- package/src/util/config-frame.ts +41 -4
- package/src/{graph → util}/dependency-spec.ts +22 -13
- package/src/util/edit-distance.ts +13 -1
- package/src/util/env-flag.ts +9 -0
- package/src/util/errors.ts +59 -3
- package/src/util/essential-env.ts +57 -0
- package/src/util/git-ignored.ts +1 -1
- package/src/util/hash.ts +35 -0
- package/src/util/hide-env.ts +45 -5
- package/src/util/index.ts +22 -5
- package/src/util/num.ts +8 -0
- package/src/util/paths.ts +121 -21
- package/src/util/report.ts +6 -1
- package/src/util/secret-mask.ts +62 -1
- package/src/util/secrets.ts +7 -2
- package/src/util/size.ts +11 -3
- package/src/util/tail.ts +6 -1
- package/src/util/timing.ts +13 -1
- package/src/util/ui.ts +65 -4
- package/src/util/which.ts +1 -1
- package/src/util/write-file.ts +67 -0
- package/src/workspace/affected.ts +710 -107
- package/src/workspace/bracket-literal.ts +84 -0
- package/src/workspace/canonical-dir.ts +37 -0
- package/src/workspace/config-bytecode.ts +282 -0
- package/src/workspace/config-eval.ts +29 -8
- package/src/workspace/config-imports.ts +199 -58
- package/src/workspace/config-schema.ts +242 -39
- package/src/workspace/filter.ts +37 -8
- package/src/workspace/index.ts +11 -2
- package/src/workspace/json-data.ts +3 -4
- package/src/workspace/lockfile.ts +57 -8
- package/src/workspace/migration.ts +72 -49
- package/src/workspace/package-graph.ts +5 -4
- package/src/workspace/project-loader.ts +314 -47
- package/src/workspace/repo-id.ts +1 -1
- package/src/workspace/workspace.ts +176 -33
- package/src/cli/foreign-flags.ts +0 -727
package/src/report/path-links.ts
CHANGED
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
import { statSync } from 'node:fs'
|
|
8
8
|
import path from 'node:path'
|
|
9
|
+
import { pathToFileURL } from 'node:url'
|
|
9
10
|
|
|
10
11
|
// `src/a.ts`, `./a.ts:12`, `/abs/a.ts:12:5`, tsc's `src/a.ts(12,5)`. Not
|
|
11
12
|
// after a word, dot, slash or `@`, so a URL's path or a scoped name's tail
|
|
12
|
-
// never starts a match.
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// never starts a match. Colour codes (`\x1b[4m`) are invisible: a path one
|
|
14
|
+
// starts is linked (tasks run with FORCE_COLOR), one it splits from its
|
|
15
|
+
// directory is not. Groups: file, then line and column of either form.
|
|
16
|
+
const SGR = String.raw`\x1b\[[\d;]*m`
|
|
17
|
+
const PATH = new RegExp(
|
|
18
|
+
String.raw`(?<!\x1b\[[\d;]*)(?:(?<![\w./@+-])|(?<=${SGR}))(?<(?:${SGR})+)` +
|
|
19
|
+
String.raw`((?:\/|\.{1,2}\/)?(?:[\w@.+-]+\/)*[\w@+-][\w@.+-]*\.[A-Za-z][A-Za-z0-9]{0,9})(?::(\d+)(?::(\d+))?|\((\d+),(\d+)\))?`,
|
|
20
|
+
'g',
|
|
21
|
+
)
|
|
15
22
|
|
|
16
23
|
/** A file a task's output names; `file` absolute. */
|
|
17
24
|
export interface OutputLocation {
|
|
@@ -29,7 +36,8 @@ export function linkPaths(text: string, dir: string, isFile: (abs: string) => bo
|
|
|
29
36
|
return text.replace(PATH, (match: string, file: string) => {
|
|
30
37
|
const abs = path.resolve(dir, file)
|
|
31
38
|
if (!isFile(abs)) return match
|
|
32
|
-
|
|
39
|
+
// Not `encodeURI`: it keeps a `#` or `?`, which the URL reads as its fragment or query.
|
|
40
|
+
return `\x1b]8;;${pathToFileURL(abs).href}\x1b\\${match}\x1b]8;;\x1b\\`
|
|
33
41
|
})
|
|
34
42
|
}
|
|
35
43
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { TaskNode } from '../graph/index.js'
|
|
2
2
|
import type { Logger } from './logger.js'
|
|
3
3
|
|
|
4
|
-
// A prefix that may yet complete as a CSI or
|
|
5
|
-
// at a chunk's end is held for
|
|
6
|
-
// remainder (`1m`) reached the log
|
|
4
|
+
// A prefix that may yet complete as a CSI, OSC or charset sequence
|
|
5
|
+
// (`tput sgr0` writes `\x1b(B`). An escape cut at a chunk's end is held for
|
|
6
|
+
// the next chunk: stripped alone, its remainder (`1m`, `B`) reached the log
|
|
7
|
+
// as text.
|
|
7
8
|
// eslint-disable-next-line no-control-regex -- ESC is the point
|
|
8
|
-
const PARTIAL_ESCAPE = /^\x1b(?:\[[0-?]*[ -/]*|\][^\x07\x1b]*\x1b
|
|
9
|
+
const PARTIAL_ESCAPE = /^\x1b(?:\[[0-?]*[ -/]*|\][^\x07\x1b]*\x1b?|[ -/]+)?$/
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* `sink` with terminal escapes stripped from task output. Tasks run with
|
|
@@ -34,12 +35,19 @@ export function plainOutput(sink: Logger): Logger {
|
|
|
34
35
|
if (text !== '') write(node, text)
|
|
35
36
|
}
|
|
36
37
|
// Delegated one by one, never spread: an embedder's logger may be a class
|
|
37
|
-
// whose methods live on its prototype and read `this`.
|
|
38
|
+
// whose methods live on its prototype and read `this`. Every hook the sink
|
|
39
|
+
// has: a dropped `warn` put warnings on stdout, a dropped `runDone` left
|
|
40
|
+
// the live region under the footer.
|
|
38
41
|
return {
|
|
39
42
|
status: (line) => sink.status(line),
|
|
43
|
+
...(sink.warn && { warn: (line) => sink.warn!(line) }),
|
|
40
44
|
...(sink.runStart && { runStart: (info) => sink.runStart!(info) }),
|
|
41
45
|
...(sink.taskStart && { taskStart: (node) => sink.taskStart!(node) }),
|
|
42
46
|
...(sink.runEnd && { runEnd: () => sink.runEnd!() }),
|
|
47
|
+
...(sink.stepStart && { stepStart: (id) => sink.stepStart!(id) }),
|
|
48
|
+
...(sink.stepEnd && { stepEnd: (id, info) => sink.stepEnd!(id, info) }),
|
|
49
|
+
...(sink.runStop && { runStop: () => sink.runStop!() }),
|
|
50
|
+
...(sink.runDone && { runDone: () => sink.runDone!() }),
|
|
43
51
|
taskStdout(node, chunk) {
|
|
44
52
|
const text = strip(`${node.id}\0out`, chunk)
|
|
45
53
|
if (text !== '' || chunk === '') sink.taskStdout(node, text)
|
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
// then redraws, so the display can never interleave with task output.
|
|
7
7
|
|
|
8
8
|
import { dumbTerminal, paint, type ColorSupport } from '../util/index.js'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
formatListHeader,
|
|
11
|
+
formatTaskRow,
|
|
12
|
+
paintIdParts,
|
|
13
|
+
pathNote,
|
|
14
|
+
placeNote,
|
|
15
|
+
} from './framed-output.js'
|
|
10
16
|
import type { BlockedTask } from './forecast.js'
|
|
11
17
|
|
|
12
18
|
const NO_COLOR: ColorSupport = { enabled: false }
|
|
@@ -32,6 +38,9 @@ export interface StatusStream {
|
|
|
32
38
|
rows?: number
|
|
33
39
|
}
|
|
34
40
|
|
|
41
|
+
/** A region's lines, or what builds them when it is drawn. */
|
|
42
|
+
export type Region = readonly string[] | (() => readonly string[])
|
|
43
|
+
|
|
35
44
|
export interface OutputWriter {
|
|
36
45
|
/** True only when the stream is a TTY and the run isn't CI. */
|
|
37
46
|
readonly enabled: boolean
|
|
@@ -49,9 +58,11 @@ export interface OutputWriter {
|
|
|
49
58
|
setStatus(line: string, opts?: { force?: boolean }): void
|
|
50
59
|
/**
|
|
51
60
|
* Replace the multi-line status region. Same throttle contract as
|
|
52
|
-
* `setStatus`; the two share one display slot.
|
|
61
|
+
* `setStatus`; the two share one display slot. A function is called
|
|
62
|
+
* when the region is next drawn, once: a set the throttle coalesces
|
|
63
|
+
* builds nothing.
|
|
53
64
|
*/
|
|
54
|
-
setRegion(lines:
|
|
65
|
+
setRegion(lines: Region, opts?: { force?: boolean }): void
|
|
55
66
|
/** Hand over what `coalesce` holds and write straight through from now on. */
|
|
56
67
|
settle(): void
|
|
57
68
|
/** Permanently remove the status display; later set* is a no-op. */
|
|
@@ -107,7 +118,7 @@ export function createOutputWriter(
|
|
|
107
118
|
stream.write(text)
|
|
108
119
|
}
|
|
109
120
|
|
|
110
|
-
let current:
|
|
121
|
+
let current: Region | null = null
|
|
111
122
|
let shown = false
|
|
112
123
|
// PHYSICAL rows the shown region occupies, not logical lines — see
|
|
113
124
|
// `regionRows`.
|
|
@@ -193,13 +204,18 @@ export function createOutputWriter(
|
|
|
193
204
|
// only repeat these bytes.
|
|
194
205
|
cancelTrailing()
|
|
195
206
|
const erase = shown ? eraseSeq() : CLEAR
|
|
207
|
+
// Built here, not at the set: a warm run's task events set the
|
|
208
|
+
// region hundreds of times inside one floor, and each build was a
|
|
209
|
+
// forecast pass and a summary render no frame showed (200 hits on a
|
|
210
|
+
// terminal: run graph 62 → 9 ms). Kept built: a write redraws it.
|
|
211
|
+
if (typeof current === 'function') current = current()
|
|
196
212
|
const lines = fitted(current!)
|
|
197
213
|
stream.write(erase + lines.join('\n'))
|
|
198
214
|
shown = true
|
|
199
215
|
shownRows = regionRows(lines)
|
|
200
216
|
lastDraw = now()
|
|
201
217
|
}
|
|
202
|
-
const set = (lines:
|
|
218
|
+
const set = (lines: Region, o: { force?: boolean }): void => {
|
|
203
219
|
if (!enabled || dead) return
|
|
204
220
|
current = lines
|
|
205
221
|
if (!atLineStart || holds > 0) return
|
|
@@ -306,18 +322,18 @@ export function createOutputWriter(
|
|
|
306
322
|
}
|
|
307
323
|
}
|
|
308
324
|
|
|
309
|
-
const IDLE = '#6b7280'
|
|
310
|
-
|
|
311
325
|
export interface WorkerSlot {
|
|
312
326
|
id: string
|
|
313
327
|
startedMs: number
|
|
328
|
+
/** Its executor holds it for a machine to begin it (`TaskExecutor.running` is `null`). */
|
|
329
|
+
waiting?: boolean
|
|
314
330
|
}
|
|
315
331
|
|
|
316
332
|
export interface StatusRegionState {
|
|
317
|
-
/** A running task's unfinished dependents (`blocks 14` on its row); 0 says nothing. */
|
|
318
|
-
blocks?: ReadonlyMap<string, number>
|
|
319
333
|
/** The running task at the head of the longest unfinished chain. */
|
|
320
334
|
critical?: string
|
|
335
|
+
/** Where a running task runs, when not here (`TaskExecutor.running`); its row shows it. */
|
|
336
|
+
where?: (taskId: string) => string | null | undefined
|
|
321
337
|
/** A running task's usual duration from history; its row shows it. */
|
|
322
338
|
expected?: ReadonlyMap<string, number>
|
|
323
339
|
/**
|
|
@@ -327,17 +343,23 @@ export interface StatusRegionState {
|
|
|
327
343
|
* the honest end of their life.
|
|
328
344
|
*/
|
|
329
345
|
pinnedPersistent: readonly string[]
|
|
330
|
-
/**
|
|
346
|
+
/** The running tasks by slot, null for a free one; a task past the slots follows them. */
|
|
331
347
|
slots: readonly (WorkerSlot | null)[]
|
|
332
|
-
/** The
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
|
|
348
|
+
/** The list's height: one row per worker, whatever runs, so it never jumps. */
|
|
349
|
+
size?: number
|
|
350
|
+
/**
|
|
351
|
+
* The tasks the scheduler has picked for the next free workers, each
|
|
352
|
+
* nested under the running task it waits on, in the rows no running task
|
|
353
|
+
* takes; nothing further ahead.
|
|
354
|
+
*/
|
|
355
|
+
next?: readonly BlockedTask[]
|
|
356
|
+
/** Each running task's count of the tasks it holds back: its `blocked` cell. */
|
|
357
|
+
blockedBy?: ReadonlyMap<string, number>
|
|
336
358
|
/** The run was stopped: running rows say `aborting`, idle rows leave. */
|
|
337
359
|
stopping?: boolean
|
|
338
360
|
/**
|
|
339
361
|
* vx's own steps still running, shown below the worker rows: no worker
|
|
340
|
-
* slot or `--concurrency` bounds them (
|
|
362
|
+
* slot or `--concurrency` bounds them (2026-10-15).
|
|
341
363
|
*/
|
|
342
364
|
steps?: readonly WorkerSlot[]
|
|
343
365
|
/** Clock value used for per-slot elapsed (injectable for tests). */
|
|
@@ -392,7 +414,7 @@ const INTERNAL = 'internal'
|
|
|
392
414
|
/**
|
|
393
415
|
* The list row of one of vx's own steps (`vx:uploads`), on the task grid
|
|
394
416
|
* with `internal` in the cache column: a step is no task and caches
|
|
395
|
-
* nothing (
|
|
417
|
+
* nothing (2026-10-16: `4.60s success internal vx:keep-run`).
|
|
396
418
|
*/
|
|
397
419
|
export function formatStepLine(
|
|
398
420
|
id: string,
|
|
@@ -441,31 +463,39 @@ export function formatStatusRegion(
|
|
|
441
463
|
'',
|
|
442
464
|
paintPinnedId(id, colors),
|
|
443
465
|
colors,
|
|
466
|
+
// A blank `blocked` cell, so the id sits under the header's `task`.
|
|
467
|
+
'',
|
|
444
468
|
),
|
|
445
469
|
)
|
|
446
470
|
}
|
|
447
471
|
|
|
448
|
-
//
|
|
449
|
-
// the ticking time IS the motion), then `running`,
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
)
|
|
465
|
-
continue
|
|
472
|
+
// Running rows: NO glyph — the live elapsed time leads (no spinner;
|
|
473
|
+
// the ticking time IS the motion), then `running`, the usual duration,
|
|
474
|
+
// how many tasks wait on it, and the full id (never truncated — name is
|
|
475
|
+
// the last column). The rows no task runs in hold the tasks planned
|
|
476
|
+
// next, each nested under what it waits on; the rest stay blank, so the
|
|
477
|
+
// list keeps its height.
|
|
478
|
+
const running = s.slots.filter((x): x is WorkerSlot => x !== null && x.waiting !== true)
|
|
479
|
+
// Placed by an executor (DTE), the list is what runs: blocked tasks are
|
|
480
|
+
// the tasks line's count alone, and its size is no machine's.
|
|
481
|
+
const placed = s.where !== undefined
|
|
482
|
+
const free = Math.max(0, (s.size ?? 0) - running.length)
|
|
483
|
+
const nested = new Map<string, BlockedTask[]>()
|
|
484
|
+
if (!placed && s.stopping !== true) {
|
|
485
|
+
const ids = new Set(running.map((x) => x.id))
|
|
486
|
+
let n = 0
|
|
487
|
+
for (const b of s.next ?? []) {
|
|
488
|
+
if (n === free) break
|
|
489
|
+
if (!ids.has(b.by)) continue
|
|
490
|
+
let list = nested.get(b.by)
|
|
491
|
+
if (list === undefined) nested.set(b.by, (list = []))
|
|
492
|
+
list.push(b)
|
|
493
|
+
n++
|
|
466
494
|
}
|
|
467
|
-
|
|
468
|
-
|
|
495
|
+
}
|
|
496
|
+
let rows = 0
|
|
497
|
+
for (const slot of running) {
|
|
498
|
+
rows++
|
|
469
499
|
if (s.stopping === true) {
|
|
470
500
|
lines.push(
|
|
471
501
|
formatTaskRow(
|
|
@@ -477,14 +507,12 @@ export function formatStatusRegion(
|
|
|
477
507
|
'',
|
|
478
508
|
paintPinnedId(slot.id, colors),
|
|
479
509
|
colors,
|
|
510
|
+
'',
|
|
480
511
|
),
|
|
481
512
|
)
|
|
482
513
|
continue
|
|
483
514
|
}
|
|
484
|
-
const
|
|
485
|
-
const notes: string[] = []
|
|
486
|
-
if (s.critical === slot.id && busy > 1) notes.push(paint(WARN, 'critical path', colors))
|
|
487
|
-
if (waiting > 0) notes.push(dim(`blocks ${waiting}`))
|
|
515
|
+
const at = s.where?.(slot.id)
|
|
488
516
|
const usual = s.expected?.get(slot.id)
|
|
489
517
|
lines.push(
|
|
490
518
|
formatTaskRow(
|
|
@@ -496,12 +524,29 @@ export function formatStatusRegion(
|
|
|
496
524
|
'',
|
|
497
525
|
paintPinnedId(slot.id, colors),
|
|
498
526
|
colors,
|
|
499
|
-
|
|
527
|
+
String(s.blockedBy?.get(slot.id) ?? 0),
|
|
528
|
+
) +
|
|
529
|
+
(s.critical === slot.id ? pathNote(colors) : '') +
|
|
530
|
+
(at != null ? placeNote(at, colors) : ''),
|
|
500
531
|
)
|
|
532
|
+
for (const b of nested.get(slot.id) ?? []) {
|
|
533
|
+
rows++
|
|
534
|
+
lines.push(
|
|
535
|
+
formatTaskRow(
|
|
536
|
+
' ',
|
|
537
|
+
null,
|
|
538
|
+
'blocked',
|
|
539
|
+
'',
|
|
540
|
+
'',
|
|
541
|
+
'',
|
|
542
|
+
`${dim('\u2514')} ${paintPinnedId(b.id, colors)}`,
|
|
543
|
+
colors,
|
|
544
|
+
'',
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
}
|
|
501
548
|
}
|
|
502
|
-
if (s.
|
|
503
|
-
lines.push(dim(`\u2026 +${s.overflow} more running`))
|
|
504
|
-
}
|
|
549
|
+
if (!placed) for (; rows < (s.size ?? 0); rows++) lines.push('')
|
|
505
550
|
for (const step of s.steps ?? []) {
|
|
506
551
|
lines.push(
|
|
507
552
|
formatTaskRow(
|
|
@@ -513,9 +558,11 @@ export function formatStatusRegion(
|
|
|
513
558
|
'',
|
|
514
559
|
paintPinnedId(step.id, colors),
|
|
515
560
|
colors,
|
|
561
|
+
'',
|
|
516
562
|
),
|
|
517
563
|
)
|
|
518
564
|
}
|
|
565
|
+
if (lines.length > 1) lines.splice(1, 0, formatListHeader(placed, colors, true))
|
|
519
566
|
// The live summary section — identical shape to the final printout,
|
|
520
567
|
// so the region visually BECOMES the summary when the run ends.
|
|
521
568
|
lines.push(...s.summaryLines)
|
|
@@ -524,11 +571,12 @@ export function formatStatusRegion(
|
|
|
524
571
|
|
|
525
572
|
/**
|
|
526
573
|
* A running task's usual duration, fixed while the elapsed time counts up
|
|
527
|
-
* toward it (
|
|
574
|
+
* toward it (2026-10-14): `~5.2s`, `~42s`, `~2m 05s`. Fits the cache
|
|
528
575
|
* column below 100 minutes.
|
|
529
576
|
*/
|
|
530
577
|
function formatExpected(ms: number): string {
|
|
531
|
-
|
|
578
|
+
// The tier by the rounded value: 999.6 ms printed `~1000ms` (item 1034's class).
|
|
579
|
+
if (Math.round(ms) < 1000) return `~${Math.round(ms)}ms`
|
|
532
580
|
if (ms < 9950) return `~${(ms / 1000).toFixed(1)}s`
|
|
533
581
|
const s = Math.round(ms / 1000)
|
|
534
582
|
if (s < 60) return `~${s}s`
|