@vzn/vx 0.0.466 → 0.0.467

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.466",
3
+ "version": "0.0.467",
4
4
  "description": "The fastest task runner and build cache for JavaScript monorepos.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,10 +21,10 @@
21
21
  "node": ">=18"
22
22
  },
23
23
  "optionalDependencies": {
24
- "@vzn/vx-linux-x64": "0.0.466",
25
- "@vzn/vx-linux-arm64": "0.0.466",
26
- "@vzn/vx-darwin-x64": "0.0.466",
27
- "@vzn/vx-darwin-arm64": "0.0.466"
24
+ "@vzn/vx-linux-x64": "0.0.467",
25
+ "@vzn/vx-linux-arm64": "0.0.467",
26
+ "@vzn/vx-darwin-x64": "0.0.467",
27
+ "@vzn/vx-darwin-arm64": "0.0.467"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/last.ts CHANGED
@@ -208,6 +208,10 @@ export async function lastCmd(args: readonly string[]): Promise<number> {
208
208
  noteSchemaReset(cache, warnToStderr)
209
209
  try {
210
210
  const db = cache.dbHandle()
211
+ // Nothing recorded at all is its own answer: `--failed` said "no
212
+ // recorded run failed" and a run id pointed at a `--list` that lists
213
+ // nothing, while `--list --failed` past green runs said there were none.
214
+ const noRuns = (): boolean => listInvocations(db, { limit: 1 }).length === 0
211
215
 
212
216
  if (parsed.list !== undefined) {
213
217
  const invocations =
@@ -219,7 +223,9 @@ export async function lastCmd(args: readonly string[]): Promise<number> {
219
223
  return 0
220
224
  }
221
225
  if (invocations.length === 0) {
222
- process.stdout.write('no recorded runs\n')
226
+ process.stdout.write(
227
+ parsed.failed === true && !noRuns() ? 'no recorded run failed\n' : 'no recorded runs\n',
228
+ )
223
229
  return 0
224
230
  }
225
231
  for (const inv of invocations) {
@@ -241,11 +247,13 @@ export async function lastCmd(args: readonly string[]): Promise<number> {
241
247
  : (listInvocations(db, { limit: 1 })[0] ?? null)
242
248
  if (inv === null || inv === undefined) {
243
249
  throw new UserError(
244
- parsed.runId !== undefined
245
- ? `vx last: no recorded run ${parsed.runId} (vx last --list shows recent runs)`
246
- : parsed.failed === true
247
- ? 'vx last: no recorded run failed'
248
- : 'vx last: no recorded runs yet — run something first',
250
+ noRuns()
251
+ ? 'vx last: no recorded runs yet — run something first'
252
+ : parsed.runId !== undefined
253
+ ? `vx last: no recorded run ${parsed.runId} (vx last --list shows recent runs)`
254
+ : parsed.failed === true
255
+ ? 'vx last: no recorded run failed'
256
+ : 'vx last: no recorded runs yet — run something first',
249
257
  )
250
258
  }
251
259
  const detail = getRun(db, inv.runId)
@@ -114,8 +114,34 @@ export interface CollectInfoOptions {
114
114
  export async function collectInfo(cwd: string, opts: CollectInfoOptions = {}): Promise<InfoFacts> {
115
115
  const warn = opts.warn ?? warnToStderr
116
116
  const reads: LoadReads = new Map()
117
+ // Both first: outside a workspace, or under a workspace config that does
118
+ // not load, the doctor refuses at once, with no probe (~180 ms) ahead.
117
119
  const root = await findWorkspaceRoot(cwd, reads)
118
- const { workspaceConfig, plugins } = await loadWorkspacePlugins(root, warn)
120
+ const workspace = await loadWorkspacePlugins(root, warn)
121
+ // The sandbox probe asks nothing of the workspace, so it runs under the
122
+ // loads below instead of after them. Awaited on every path: it resets
123
+ // the runtime it brought up, whose sockets would hold the process open.
124
+ const probing = sandboxProbe()
125
+ // Rejects only if the reset does; awaited below, never unhandled.
126
+ probing.catch(() => {})
127
+ try {
128
+ return await collectWorkspaceInfo(cwd, root, reads, workspace, opts, probing)
129
+ } catch (err) {
130
+ // The workspace's refusal is the error worth reporting.
131
+ await probing.catch(() => {})
132
+ throw err
133
+ }
134
+ }
135
+
136
+ async function collectWorkspaceInfo(
137
+ cwd: string,
138
+ root: string,
139
+ reads: LoadReads,
140
+ { workspaceConfig, plugins }: Awaited<ReturnType<typeof loadWorkspacePlugins>>,
141
+ opts: CollectInfoOptions,
142
+ probing: Promise<Omit<InfoFacts['sandbox'], 'declared'>>,
143
+ ): Promise<InfoFacts> {
144
+ const warn = opts.warn ?? warnToStderr
119
145
  const cacheDir =
120
146
  opts.cacheDir === undefined
121
147
  ? resolveCacheDir(root, workspaceConfig)
@@ -173,7 +199,10 @@ export async function collectInfo(cwd: string, opts: CollectInfoOptions = {}): P
173
199
  }
174
200
 
175
201
  const lockPresent = await Bun.file(lockfilePath(root)).exists()
176
- const sandbox = await sandboxFact(sandboxed)
202
+ // Asked while the probe runs: each is a synchronous spawn.
203
+ const git = gitVersion()
204
+ const statusCache = gitStatusCache(root)
205
+ const sandbox = { ...(await probing), declared: sandboxed }
177
206
  return {
178
207
  vx: VERSION,
179
208
  // An unsupported Bun does not stop a run, it makes the run's ANSWERS
@@ -183,12 +212,12 @@ export async function collectInfo(cwd: string, opts: CollectInfoOptions = {}): P
183
212
  // have, which is a property 19 tests hold on purpose.
184
213
  bun: Bun.version,
185
214
  bunSupported: !isUnsupportedBun(Bun.version),
186
- git: gitVersion(),
215
+ git,
187
216
  // The one `git status` walk per run is the warm path's critical path on
188
217
  // a large tree (~55 ms at 1000 projects, measured 2026-09-02). git's
189
218
  // own caches make it near-free after the first run, and they are OFF by
190
219
  // default — say so, since nothing else in a run would.
191
- gitStatusCache: gitStatusCache(root),
220
+ gitStatusCache: statusCache,
192
221
  workspaceRoot: root,
193
222
  projects: metas.length,
194
223
  tasks: taskCount,
@@ -224,24 +253,19 @@ export async function collectInfo(cwd: string, opts: CollectInfoOptions = {}): P
224
253
  }
225
254
 
226
255
  /**
227
- * The runtime probe's verdict — one sandboxed `true` (memoized) — and the
228
- * count of loaded tasks that would meet it. The probe initializes the
229
- * Linux runtime, whose proxy sockets would keep a standalone process
230
- * alive; reset after asking, since the doctor runs nothing.
256
+ * The runtime probe's verdict — one sandboxed `true` (memoized). The probe
257
+ * initializes the Linux runtime, whose proxy sockets would keep a
258
+ * standalone process alive; reset after asking, since the doctor runs
259
+ * nothing.
231
260
  */
232
- async function sandboxFact(declared: number): Promise<InfoFacts['sandbox']> {
261
+ async function sandboxProbe(): Promise<Omit<InfoFacts['sandbox'], 'declared'>> {
233
262
  try {
234
263
  const verdict = await probeSandbox()
235
264
  const untraced = verdict.available ? await untracedReason() : null
236
- return {
237
- available: verdict.available,
238
- reason: stableSandboxReason(verdict.reason),
239
- declared,
240
- untraced,
241
- }
265
+ return { available: verdict.available, reason: stableSandboxReason(verdict.reason), untraced }
242
266
  } catch (err) {
243
267
  const message = err instanceof Error ? err.message : String(err)
244
- return { available: false, reason: stableSandboxReason(message), declared, untraced: null }
268
+ return { available: false, reason: stableSandboxReason(message), untraced: null }
245
269
  } finally {
246
270
  await resetSandbox()
247
271
  }