@vzn/vx 0.0.159 → 0.0.160
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.160",
|
|
4
4
|
"description": "A fast, correct task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.160",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.160",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.160",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.160"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -619,6 +619,8 @@ export interface SandboxedRunArgs {
|
|
|
619
619
|
onStderr?: (chunk: string) => void
|
|
620
620
|
/** See `RunOptions.liveChildren` — same contract for sandboxed spawns. */
|
|
621
621
|
liveChildren?: Set<ReturnType<typeof Bun.spawn>>
|
|
622
|
+
/** `ExecuteRequest.signal`: once aborted, the tracer retry spawns nothing. */
|
|
623
|
+
signal?: AbortSignal
|
|
622
624
|
/** See `RunOptions.timeoutMs` — SIGTERM the child after this many ms. */
|
|
623
625
|
timeoutMs?: number
|
|
624
626
|
/** See `CaptureConfig` — which streams are retained on the result. */
|
|
@@ -1226,12 +1228,14 @@ const TRACER_RETRY_LINE =
|
|
|
1226
1228
|
* strace runs detached (`-DD`), and a tracer that dies leaves the command
|
|
1227
1229
|
* running untraced — but the trace then stops short, and a denial after it
|
|
1228
1230
|
* goes unreported. So an attempt whose stderr carries strace's own word is
|
|
1229
|
-
* run once more, whatever its exit: the sandbox kept its writes to what it
|
|
1231
|
+
* run once more, whatever its exit, unless the run is stopping: the sandbox kept its writes to what it
|
|
1230
1232
|
* declared, so a second run redoes, not doubles, it.
|
|
1231
1233
|
*/
|
|
1232
1234
|
export async function runSandboxed(args: SandboxedRunArgs): Promise<SandboxedRunResult> {
|
|
1233
1235
|
const { tracerFailed, ...first } = await runSandboxedOnce(args)
|
|
1234
|
-
|
|
1236
|
+
// A stopping run has killed the children it holds; a retry would be one
|
|
1237
|
+
// spawned after that kill.
|
|
1238
|
+
if (!tracerFailed || args.signal?.aborted === true) return first
|
|
1235
1239
|
args.onStderr?.(TRACER_RETRY_LINE)
|
|
1236
1240
|
const { tracerFailed: _again, ...second } = await runSandboxedOnce(args)
|
|
1237
1241
|
return {
|
|
@@ -15,9 +15,16 @@ import type { Database } from 'bun:sqlite'
|
|
|
15
15
|
import { EXECUTED_RUNS_SQL } from '../cache/index.js'
|
|
16
16
|
import { failureModeOf, mixedOutcomeKeysSql } from './failure-mode.js'
|
|
17
17
|
import type { FailureMode } from './failure-mode.js'
|
|
18
|
+
import { isPassStatus, TASK_STATUSES } from './telemetry.js'
|
|
18
19
|
|
|
19
20
|
const DEFAULT_RECENT = 50
|
|
20
21
|
|
|
22
|
+
// A hit is recorded as `cache-hit` / `cache-hit-remote` and replays a
|
|
23
|
+
// success; the list is derived so a new status cannot miss it.
|
|
24
|
+
const PASS_STATUSES = `(${TASK_STATUSES.filter(isPassStatus)
|
|
25
|
+
.map((s) => `'${s}'`)
|
|
26
|
+
.join(', ')})`
|
|
27
|
+
|
|
21
28
|
/** Per (project#task) — last RECENT runs collapsed into a summary. */
|
|
22
29
|
export interface TaskHistory {
|
|
23
30
|
/** Total runs in the recent window. */
|
|
@@ -108,7 +115,7 @@ export class LocalHistoryProvider implements HistoryProvider {
|
|
|
108
115
|
r.project,
|
|
109
116
|
r.task,
|
|
110
117
|
COUNT(*) AS total,
|
|
111
|
-
SUM(CASE WHEN r.status
|
|
118
|
+
SUM(CASE WHEN r.status IN ${PASS_STATUSES} THEN 1 ELSE 0 END) AS successes,
|
|
112
119
|
SUM(CASE WHEN r.cache_hit = 1 THEN 1 ELSE 0 END) AS hits,
|
|
113
120
|
SUM(CASE WHEN r.status = 'failed' THEN 1 ELSE 0 END) AS failures,
|
|
114
121
|
SUM(CASE WHEN r.attempts > 1 THEN 1 ELSE 0 END) AS retried,
|
|
@@ -159,21 +166,21 @@ export class LocalHistoryProvider implements HistoryProvider {
|
|
|
159
166
|
// nondeterminism); a green history never pays for it.
|
|
160
167
|
const mixed = this.mixedOutcomeKeys(
|
|
161
168
|
floor,
|
|
162
|
-
rows.filter((r) =>
|
|
169
|
+
rows.filter((r) => r.failures > 0 && r.retried === 0),
|
|
163
170
|
)
|
|
164
171
|
|
|
165
172
|
for (const row of rows) {
|
|
166
173
|
const key = `${row.project}#${row.task}`
|
|
167
|
-
const total = row.total
|
|
168
|
-
const counts = { total, failures: row.failures
|
|
174
|
+
const total = row.total
|
|
175
|
+
const counts = { total, failures: row.failures, retried: row.retried }
|
|
169
176
|
const durations = row.ds === null ? undefined : row.ds.split(',').map(Number)
|
|
170
177
|
if (durations !== undefined) durations.sort((a, b) => a - b)
|
|
171
178
|
out.set(key, {
|
|
172
179
|
runs: total,
|
|
173
180
|
p50DurationMs: durations ? pickPercentile(durations, 0.5) : undefined,
|
|
174
181
|
p99DurationMs: durations ? pickPercentile(durations, 0.99) : undefined,
|
|
175
|
-
successRate:
|
|
176
|
-
hitRate:
|
|
182
|
+
successRate: row.successes / total,
|
|
183
|
+
hitRate: row.hits / total,
|
|
177
184
|
failureMode: failureModeOf(counts, () => mixed.get(key) ?? 0),
|
|
178
185
|
...(row.rss !== null ? { maxPeakRssBytes: row.rss } : {}),
|
|
179
186
|
...(row.cpu !== null ? { maxCpuParallelism: row.cpu } : {}),
|
|
@@ -207,8 +214,8 @@ export class LocalHistoryProvider implements HistoryProvider {
|
|
|
207
214
|
}
|
|
208
215
|
}
|
|
209
216
|
|
|
217
|
+
// A group holds at least one row, so every SUM is a number and `sorted` is
|
|
218
|
+
// never empty; q < 1 keeps the index inside it.
|
|
210
219
|
function pickPercentile(sorted: number[], q: number): number {
|
|
211
|
-
|
|
212
|
-
const idx = Math.min(sorted.length - 1, Math.floor(q * sorted.length))
|
|
213
|
-
return sorted[idx]!
|
|
220
|
+
return sorted[Math.floor(q * sorted.length)]!
|
|
214
221
|
}
|
|
@@ -260,12 +260,37 @@ export function formatSummarySection(
|
|
|
260
260
|
)
|
|
261
261
|
}
|
|
262
262
|
lines.push('', row('info', join(info)), row('time', `${formatDuration(totalMs)}${spread}`))
|
|
263
|
+
// The run in one line, last, where the eye lands: what a Turbo user
|
|
264
|
+
// reads first in its own summary (tasks, cached, time), in vx's words.
|
|
265
|
+
if (stats.total > 0) lines.push(row('result', resultLine(stats, hits, totalMs, colors)))
|
|
263
266
|
} else {
|
|
264
267
|
lines.push('', row('time', `${formatDuration(totalMs)}${spread}`))
|
|
265
268
|
}
|
|
266
269
|
return lines
|
|
267
270
|
}
|
|
268
271
|
|
|
272
|
+
/**
|
|
273
|
+
* `42 tasks · 38 cached (90%) · 3.2s`, `2 tasks · all cached · 23ms`,
|
|
274
|
+
* with the failures first when there are any. Cached is every hit (up to
|
|
275
|
+
* date, restored locally or remotely) over every counted task.
|
|
276
|
+
*/
|
|
277
|
+
function resultLine(
|
|
278
|
+
stats: SummaryStats,
|
|
279
|
+
hits: number,
|
|
280
|
+
totalMs: number,
|
|
281
|
+
colors: ColorSupport,
|
|
282
|
+
): string {
|
|
283
|
+
const parts = [`${stats.total} task${stats.total === 1 ? '' : 's'}`]
|
|
284
|
+
if (stats.failed > 0) parts.push(paint(ERROR, `${stats.failed} failed`, colors, { bold: true }))
|
|
285
|
+
parts.push(
|
|
286
|
+
hits === stats.total
|
|
287
|
+
? paint(SUCCESS, 'all cached', colors, { bold: true })
|
|
288
|
+
: `${hits} cached (${Math.floor((hits / stats.total) * 100)}%)`,
|
|
289
|
+
)
|
|
290
|
+
parts.push(formatDuration(totalMs))
|
|
291
|
+
return parts.join(` ${paint('', '\u00b7', colors, { dim: true })} `)
|
|
292
|
+
}
|
|
293
|
+
|
|
269
294
|
// Local byte formatter — the orchestrator can't import cli/format.ts
|
|
270
295
|
// (module boundary), and the footer only needs one compact form.
|
|
271
296
|
export function formatRunSummary(
|