@vzn/vx 0.0.636 → 0.0.637

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.
Files changed (141) hide show
  1. package/README.md +39 -11
  2. package/docs/caching.md +213 -207
  3. package/docs/cli.md +699 -655
  4. package/docs/execution.md +67 -75
  5. package/docs/features.md +44 -40
  6. package/docs/patterns.md +26 -25
  7. package/docs/schema.md +376 -198
  8. package/docs/security.md +76 -39
  9. package/launcher.sh +43 -0
  10. package/package.json +5 -5
  11. package/schemas/cache.json +41 -19
  12. package/schemas/info.json +23 -11
  13. package/schemas/last.json +48 -2
  14. package/schemas/lock.json +1 -1
  15. package/schemas/plan.json +2 -2
  16. package/schemas/show.json +5 -3
  17. package/schemas/why.json +15 -1
  18. package/skills/vx/SKILL.md +16 -12
  19. package/src/bin.ts +17 -2
  20. package/src/cache/archive.ts +11 -0
  21. package/src/cache/cache.ts +105 -53
  22. package/src/cache/git-inputs.ts +277 -63
  23. package/src/cache/index.ts +7 -1
  24. package/src/cache/inputs.ts +125 -35
  25. package/src/cache/key-fold.ts +8 -7
  26. package/src/cache/layer.ts +8 -17
  27. package/src/cache/layered-cache.ts +16 -102
  28. package/src/cache/policy.ts +25 -29
  29. package/src/cache/run-history.ts +36 -14
  30. package/src/cache/schema.ts +21 -23
  31. package/src/cache/scoped-cache.ts +224 -0
  32. package/src/cli/cache.ts +150 -28
  33. package/src/cli/completions.ts +2 -2
  34. package/src/cli/core-alias.ts +1 -1
  35. package/src/cli/foreign-flags.ts +13 -12
  36. package/src/cli/help.ts +94 -63
  37. package/src/cli/index.ts +42 -13
  38. package/src/cli/info.ts +38 -23
  39. package/src/cli/init.ts +115 -406
  40. package/src/cli/last.ts +145 -4
  41. package/src/cli/lock.ts +333 -111
  42. package/src/cli/plan-format.ts +2 -2
  43. package/src/cli/plugin-commands.ts +5 -9
  44. package/src/cli/plugin-templates.ts +10 -10
  45. package/src/cli/run.ts +103 -149
  46. package/src/cli/select.ts +33 -39
  47. package/src/cli/show.ts +17 -19
  48. package/src/cli/task-verb.ts +2 -9
  49. package/src/cli/upgrade.ts +402 -145
  50. package/src/cli/watch-cycle.ts +9 -2
  51. package/src/cli/watch-filter.ts +33 -60
  52. package/src/cli/watch-fs.ts +29 -16
  53. package/src/cli/watch-judge.ts +12 -3
  54. package/src/cli/watch-set.ts +16 -42
  55. package/src/cli/watch.ts +188 -215
  56. package/src/cli/why.ts +28 -17
  57. package/src/cli/workspace-config.ts +31 -68
  58. package/src/config.ts +169 -70
  59. package/src/exec/env.ts +1 -31
  60. package/src/exec/executor.ts +1 -5
  61. package/src/exec/kill-tree.ts +2 -1
  62. package/src/exec/sandbox-runtime.ts +12 -1
  63. package/src/exec/sandbox-violations.ts +9 -1
  64. package/src/graph/dependency-spec.ts +1 -1
  65. package/src/graph/index.ts +1 -0
  66. package/src/graph/priorities.ts +1 -1
  67. package/src/graph/scheduler.ts +19 -7
  68. package/src/graph/task-graph.ts +44 -20
  69. package/src/handoff.ts +169 -0
  70. package/src/index.ts +24 -57
  71. package/src/orchestrator/affected-tasks.ts +7 -5
  72. package/src/orchestrator/command-windows.ts +44 -0
  73. package/src/orchestrator/deferred-outputs.ts +12 -10
  74. package/src/orchestrator/doctor.ts +98 -110
  75. package/src/orchestrator/download-policy.ts +3 -3
  76. package/src/orchestrator/events.ts +19 -3
  77. package/src/orchestrator/excluded-keys.ts +0 -2
  78. package/src/orchestrator/execute-task.ts +158 -63
  79. package/src/orchestrator/fingerprint-watch.ts +18 -15
  80. package/src/orchestrator/forecast.ts +10 -2
  81. package/src/orchestrator/framed-output.ts +8 -3
  82. package/src/orchestrator/graph-stage.ts +5 -5
  83. package/src/orchestrator/history.ts +70 -30
  84. package/src/orchestrator/hit-restore.ts +15 -3
  85. package/src/orchestrator/index.ts +18 -4
  86. package/src/orchestrator/local-shortcircuit.ts +2 -1
  87. package/src/orchestrator/lockfile-claim.ts +79 -20
  88. package/src/orchestrator/logger.ts +71 -39
  89. package/src/orchestrator/metrics.ts +71 -6
  90. package/src/orchestrator/miss-save.ts +3 -0
  91. package/src/orchestrator/options.ts +42 -29
  92. package/src/orchestrator/output-host.ts +72 -0
  93. package/src/orchestrator/plan.ts +6 -8
  94. package/src/orchestrator/plugin-host.ts +87 -63
  95. package/src/orchestrator/plugin.ts +94 -60
  96. package/src/orchestrator/prepare.ts +113 -72
  97. package/src/orchestrator/projects.ts +102 -99
  98. package/src/orchestrator/remote-prefetch.ts +2 -1
  99. package/src/orchestrator/run-context.ts +25 -6
  100. package/src/orchestrator/run-failures.ts +17 -8
  101. package/src/orchestrator/run-lock.ts +1 -1
  102. package/src/orchestrator/run-report.ts +9 -9
  103. package/src/orchestrator/run-settings.ts +48 -0
  104. package/src/orchestrator/run.ts +121 -95
  105. package/src/orchestrator/sandbox-fix.ts +39 -20
  106. package/src/orchestrator/sandbox-request.ts +84 -13
  107. package/src/orchestrator/signals.ts +1 -1
  108. package/src/orchestrator/stable-keys.ts +13 -11
  109. package/src/orchestrator/status-line.ts +48 -6
  110. package/src/orchestrator/summary.ts +46 -62
  111. package/src/orchestrator/task-hash.ts +12 -83
  112. package/src/orchestrator/telemetry.ts +3 -3
  113. package/src/settings.ts +417 -0
  114. package/src/util/errors.ts +1 -1
  115. package/src/util/essential-env.ts +31 -0
  116. package/src/util/hide-env.ts +107 -0
  117. package/src/util/index.ts +8 -2
  118. package/src/util/paths.ts +10 -0
  119. package/src/util/settle.ts +49 -30
  120. package/src/util/size.ts +23 -4
  121. package/src/util/timing.ts +3 -2
  122. package/src/util/ulid.ts +1 -1
  123. package/src/util/verbs.ts +0 -15
  124. package/src/workspace/affected.ts +77 -222
  125. package/src/workspace/config-eval.ts +15 -20
  126. package/src/workspace/config-imports.ts +78 -3
  127. package/src/workspace/config-schema.ts +209 -268
  128. package/src/workspace/dependency-rules.ts +57 -0
  129. package/src/workspace/filter.ts +2 -2
  130. package/src/workspace/fingerprint.ts +11 -121
  131. package/src/workspace/index.ts +20 -27
  132. package/src/workspace/load-reads.ts +4 -4
  133. package/src/workspace/lockfile.ts +96 -140
  134. package/src/workspace/migration.ts +242 -146
  135. package/src/workspace/package-graph.ts +30 -216
  136. package/src/workspace/project-loader.ts +80 -285
  137. package/src/workspace/repo-id.ts +2 -1
  138. package/src/workspace/workspace.ts +362 -831
  139. package/src/cache/config-evals.ts +0 -130
  140. package/src/workspace/config-cache.ts +0 -661
  141. package/src/workspace/migrate-scripts.ts +0 -1121
package/src/cli/cache.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { existsSync } from 'node:fs'
2
- import { Cache } from '../cache/index.js'
1
+ import { existsSync, readdirSync, rmSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
+ import path from 'node:path'
4
+ import { Cache, vxCacheEntries } from '../cache/index.js'
3
5
  import { flagHint, formatValue, refuse, refusedWord, seeHelp } from './help.js'
4
- import { errorCode, nearest, parseDuration, parseSize } from '../util/index.js'
6
+ import { errorCode, nearest, parseDuration, parseSize, relPosix, treeBytes } from '../util/index.js'
5
7
  import { acquireRunLock } from '../orchestrator/index.js'
6
8
  import { findWorkspaceRoot } from '../workspace/index.js'
7
9
  import {
@@ -19,8 +21,8 @@ export { parseDuration, parseSize } from '../util/index.js'
19
21
  /**
20
22
  * `vx cache stats` is a habit from the other runners, and vx's answer is a
21
23
  * different VERB rather than a missing one — saying so where the user typed
22
- * it beats sending them to a help screen that lists only `prune` (walked the
23
- * first-run path, 2026-09-20). A typo of `prune` itself gets the same
24
+ * it beats sending them to a help screen that lists only `clean` (walked the
25
+ * first-run path, 2026-09-20). A typo of `clean` itself gets the same
24
26
  * nearest-neighbour hint a task, flag or project name gets.
25
27
  */
26
28
  const CACHE_ELSEWHERE: Record<string, string> = {
@@ -32,34 +34,39 @@ const CACHE_ELSEWHERE: Record<string, string> = {
32
34
  ls: '`vx info` reports the cache entry count and size, and `vx last --list` the recent runs',
33
35
  dir: '`vx info` prints the cache directory this workspace uses',
34
36
  path: '`vx info` prints the cache directory this workspace uses',
35
- clean: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
36
- clear: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
37
- rm: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
38
- delete: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
39
- evict: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
40
- gc: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
41
- purge: '`vx cache prune` is the eviction verb (`--older-than`, `--max-size`)',
37
+ clear:
38
+ '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
39
+ rm: '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
40
+ delete:
41
+ '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
42
+ reset:
43
+ '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
44
+ wipe: '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
45
+ evict: '`vx cache clean` is the eviction verb (`--older-than`, `--max-size`)',
46
+ gc: '`vx cache clean` is the eviction verb (`--older-than`, `--max-size`)',
47
+ purge:
48
+ '`vx cache clean` with no bound clears the whole cache; `--older-than` / `--max-size` trim it',
42
49
  }
43
50
 
44
51
  function cacheSubHint(sub: string): string {
45
52
  const elsewhere = CACHE_ELSEWHERE[sub]
46
53
  if (elsewhere !== undefined) return ` — ${elsewhere}`
47
- const best = nearest(sub, ['prune'])
48
- // `prune` is the one subcommand: name it when nothing is near (M-58).
49
- return best === undefined ? '. The subcommand is prune' : `. Did you mean ${best}?`
54
+ const best = nearest(sub, ['clean'])
55
+ // `clean` is the one subcommand: name it when nothing is near (M-58).
56
+ return best === undefined ? '. The subcommand is clean' : `. Did you mean ${best}?`
50
57
  }
51
58
 
52
59
  export async function cacheCmd(args: readonly string[]): Promise<number> {
53
60
  const [sub, ...rest] = args
54
61
  switch (sub) {
55
- case 'prune':
56
- return await pruneCmd(rest)
62
+ case 'clean':
63
+ return await cleanCmd(rest)
57
64
  case undefined:
58
- // A bare `vx cache prune` refuses too: the hint names a whole command.
65
+ // A bare `vx cache clean` refuses too: the hint names a whole command.
59
66
  return refuse(
60
67
  'cache',
61
68
  args,
62
- `missing subcommand. Try \`vx cache prune --older-than 30d\` or \`vx cache prune --max-size 1G\`${seeHelp('cache')}`,
69
+ `missing subcommand. Try \`vx cache clean --older-than 30d\` to trim the cache, or \`vx cache clean\` to clear it${seeHelp('cache')}`,
63
70
  'VX_E_USAGE',
64
71
  )
65
72
  default:
@@ -79,6 +86,8 @@ interface PruneArgs {
79
86
  format?: 'pretty' | 'json'
80
87
  /** `--cache-dir`: prune the cache a run with the same flag uses. */
81
88
  cacheDir?: string
89
+ /** `--global`: clear every repository's store under `~/.vx`. */
90
+ global?: boolean
82
91
  error?: string
83
92
  }
84
93
 
@@ -104,7 +113,7 @@ export function parsePruneArgs(args: readonly string[]): PruneArgs {
104
113
  // the deliberate way to do it.
105
114
  if (ms === 0) {
106
115
  return {
107
- error: `--older-than 0 would evict every entry — to clear the cache, delete the cache store \`vx info\` names`,
116
+ error: `--older-than 0 would evict every entry — \`vx cache clean\` with no bound clears the whole cache`,
108
117
  }
109
118
  }
110
119
  out.olderThanMs = Date.now() - ms
@@ -118,7 +127,7 @@ export function parsePruneArgs(args: readonly string[]): PruneArgs {
118
127
  return { error: `--max-size must be a size like 500M or 1G (got ${v})${seeHelp('cache')}` }
119
128
  if (bytes === 0) {
120
129
  return {
121
- error: `--max-size 0 would evict every entry — to clear the cache, delete the cache store \`vx info\` names`,
130
+ error: `--max-size 0 would evict every entry — \`vx cache clean\` with no bound clears the whole cache`,
122
131
  }
123
132
  }
124
133
  // A bare number is bytes to `parseSize` (a computed size needs that),
@@ -134,6 +143,8 @@ export function parsePruneArgs(args: readonly string[]): PruneArgs {
134
143
  out.maxBytes = bytes
135
144
  } else if (a === '--dry-run') {
136
145
  out.dryRun = true
146
+ } else if (a === '--global') {
147
+ out.global = true
137
148
  } else if (a === '--format' || a?.startsWith('--format=')) {
138
149
  const v = formatValue(a === '--format' ? args[++i] : a.slice('--format='.length), 'cache')
139
150
  if (typeof v === 'object') return v
@@ -142,31 +153,38 @@ export function parsePruneArgs(args: readonly string[]): PruneArgs {
142
153
  const cd = parseCacheDirFlag(args, i)
143
154
  if (cd === null)
144
155
  return {
145
- error: `${refusedWord(a ?? '')}: ${a}${flagHint('cache prune', a ?? '')}${seeHelp('cache')}`,
156
+ error: `${refusedWord(a ?? '')}: ${a}${flagHint('cache clean', a ?? '')}${seeHelp('cache')}`,
146
157
  }
147
158
  if ('error' in cd) return { error: `${cd.error}${seeHelp('cache')}` }
148
159
  out.cacheDir = cd.cacheDir
149
160
  i = cd.next
150
161
  }
151
162
  }
152
- if (out.olderThanMs === undefined && out.maxBytes === undefined) {
153
- return { error: `needs --older-than <duration> or --max-size <size>${seeHelp('cache')}` }
163
+ if (out.global === true && (out.olderThanMs !== undefined || out.maxBytes !== undefined)) {
164
+ return {
165
+ error: `--global clears every repository's store and takes no --older-than or --max-size${seeHelp('cache')}`,
166
+ }
167
+ }
168
+ if (out.global === true && out.cacheDir !== undefined) {
169
+ return {
170
+ error: `--global clears the stores under ~/.vx and takes no --cache-dir${seeHelp('cache')}`,
171
+ }
154
172
  }
155
173
  return out
156
174
  }
157
175
 
158
- async function pruneCmd(args: readonly string[]): Promise<number> {
176
+ async function cleanCmd(args: readonly string[]): Promise<number> {
159
177
  const parsed = parsePruneArgs(args)
160
- if (parsed.error) return refuse('cache prune', args, parsed.error, 'VX_E_USAGE')
178
+ if (parsed.error) return refuse('cache clean', args, parsed.error, 'VX_E_USAGE')
161
179
  const cwd = process.cwd()
162
180
  let root: string
163
181
  try {
164
182
  root = await findWorkspaceRoot(cwd)
165
183
  } catch (err) {
166
- return refuse('cache prune', args, (err as Error).message, errorCode(err))
184
+ return refuse('cache clean', args, (err as Error).message, errorCode(err))
167
185
  }
168
186
  // Honor `--cache-dir`, `defineWorkspace({ cacheDir: '...' })` and a
169
- // `config` plugin's edit of it — `vx run` and `vx cache prune` must
187
+ // `config` plugin's edit of it — `vx run` and `vx cache clean` must
170
188
  // operate on the same directory or prune silently no-ops against the
171
189
  // wrong path.
172
190
  // A dry run only reads: it never resets the index (item 896).
@@ -181,6 +199,9 @@ async function pruneCmd(args: readonly string[]): Promise<number> {
181
199
  dir = ws.cacheDir
182
200
  storeRoot = await ws.storeRoot
183
201
  }
202
+ if (parsed.olderThanMs === undefined && parsed.maxBytes === undefined) {
203
+ return clear(args, root, dir, storeRoot, parsed)
204
+ }
184
205
  // A workspace that never ran has nothing to prune, and opening the cache
185
206
  // to find that out made it: `.vx/cache` with a database and a
186
207
  // `.gitignore`, where item 900 had held the dry run to making nothing
@@ -241,3 +262,104 @@ function printPruned(
241
262
  `${dry ? 'Would prune' : 'Pruned'} ${result.evicted} entr${result.evicted === 1 ? 'y' : 'ies'} (${formatBytes(result.bytesFreed)}${dry ? '' : ' freed'})${orphans}\n`,
242
263
  )
243
264
  }
265
+
266
+ /** `vx cache clean --format json` with no bound (schemas/cache.json `clear`). */
267
+ export interface ClearResult {
268
+ dryRun: boolean
269
+ /** Workspace-relative inside the workspace, else absolute. */
270
+ cleared: string[]
271
+ bytesFreed: number
272
+ }
273
+
274
+ /**
275
+ * `vx cache clean` with no bound: delete the cache directory (its index,
276
+ * run history and any artifacts kept beside it) and the store its index
277
+ * names (`~/.vx/<id>/cache`, shared by the repository's checkouts), as
278
+ * `nx reset` does. `--global` takes every store under `~/.vx` too. The
279
+ * workspace config, the lock and `.vx/.gitignore` sit outside them all.
280
+ * A directory the user named (`cache.dir`) may be shared (`build/`,
281
+ * `~/.cache`): there only what vx wrote goes (item 968).
282
+ */
283
+ async function clear(
284
+ args: readonly string[],
285
+ root: string,
286
+ dir: string,
287
+ storeRoot: string | null,
288
+ parsed: PruneArgs,
289
+ ): Promise<number> {
290
+ // The store the index recorded wins over today's (a changed HOME), as a
291
+ // run reads it; a directory with no index is not a vx cache to delete.
292
+ const targets: string[] = []
293
+ if (existsSync(path.join(dir, 'cache.db'))) {
294
+ const cache = new Cache(dir, undefined, undefined, undefined, 'preview', storeRoot)
295
+ const store = cache.storeDir
296
+ cache.close()
297
+ targets.push(dir)
298
+ // The index is a file in the checkout (committed, or restored from a CI
299
+ // cache): the store it names is deleted only when it is one.
300
+ if (store !== undefined && isStore(store)) targets.push(store)
301
+ } else if (storeRoot !== null) targets.push(storeRoot)
302
+ if (parsed.global === true) {
303
+ const vxHome = path.join(process.env['HOME'] || homedir(), '.vx')
304
+ const ids = existsSync(vxHome) ? readdirSync(vxHome) : []
305
+ for (const id of ids) targets.push(path.join(vxHome, id, 'cache'))
306
+ }
307
+ // `cache.dir: '.vx'` (or the root) would take the workspace config and
308
+ // the lock with it.
309
+ const dotVx = path.join(root, '.vx')
310
+ const doomed = [...new Set(targets.map((t) => path.resolve(t)))].filter((t) => existsSync(t))
311
+ const unsafe = doomed.find((t) => t === dotVx || !path.relative(t, dotVx).startsWith('..'))
312
+ if (unsafe !== undefined) {
313
+ return refuse(
314
+ 'cache clean',
315
+ args,
316
+ `cache directory ${unsafe} holds the workspace config and lock; set cache.dir to a directory of its own`,
317
+ 'VX_E_CONFIG',
318
+ )
319
+ }
320
+ const dry = parsed.dryRun === true
321
+ // `.vx/cache` and the stores are vx's own; any other directory is the
322
+ // user's, cleared entry by entry and removed only if that empties it.
323
+ const named = path.resolve(dir) === path.join(dotVx, 'cache') ? undefined : path.resolve(dir)
324
+ const entries = (t: string): string[] =>
325
+ t === named ? vxCacheEntries(t).map((e) => path.join(t, e)) : [t]
326
+ const bytes = doomed.reduce((n, t) => n + entries(t).reduce((m, e) => m + treeBytes(e), 0), 0)
327
+ if (!dry && doomed.length > 0) {
328
+ // A clear beside a run deletes what the run is restoring or saving; it
329
+ // waits for the run's lock, as a trim does.
330
+ const release = await acquireRunLock(root, { log: warnToStderr })
331
+ try {
332
+ for (const t of doomed) {
333
+ for (const e of entries(t)) rmSync(e, { recursive: true, force: true })
334
+ if (t === named && readdirSync(t).length === 0) rmSync(t, { recursive: true })
335
+ }
336
+ } finally {
337
+ await release()
338
+ }
339
+ }
340
+ const shown = doomed.map((t) => (path.relative(root, t).startsWith('..') ? t : relPosix(root, t)))
341
+ if (parsed.format === 'json') {
342
+ const result: ClearResult = { dryRun: dry, cleared: shown, bytesFreed: bytes }
343
+ process.stdout.write(`${JSON.stringify(result)}\n`)
344
+ } else if (shown.length === 0) process.stdout.write('Nothing to clear: no cache here\n')
345
+ else {
346
+ const scope =
347
+ parsed.global === true
348
+ ? "every repository's store and this workspace's run history"
349
+ : "this repository's store, shared by its checkouts, and this workspace's run history"
350
+ process.stdout.write(
351
+ `${dry ? 'Would clear' : 'Cleared'} ${scope} (${formatBytes(bytes)}${dry ? '' : ' freed'}):\n${shown.map((t) => ` ${t}\n`).join('')}`,
352
+ )
353
+ }
354
+ return 0
355
+ }
356
+
357
+ /** A store's shape: `<home>/.vx/<id>/cache` holding its `store.db`. */
358
+ function isStore(stored: string): boolean {
359
+ const dir = path.resolve(stored)
360
+ return (
361
+ path.basename(dir) === 'cache' &&
362
+ path.basename(path.dirname(path.dirname(dir))) === '.vx' &&
363
+ existsSync(path.join(dir, 'store.db'))
364
+ )
365
+ }
@@ -12,7 +12,7 @@ import { nearest } from '../util/index.js'
12
12
  export const FLAG_VALUES: Readonly<Record<string, readonly string[]>> = {
13
13
  '--format': ['pretty', 'json'],
14
14
  '--output-logs': ['full', 'errors-only', 'hash-only', 'none'],
15
- '--download': ['all', 'toplevel', 'none'],
15
+ '--cache-download': ['all', 'toplevel', 'none'],
16
16
  }
17
17
 
18
18
  export type CompletionShell = 'bash' | 'zsh' | 'fish'
@@ -30,7 +30,7 @@ export function verbFlags(verb: string): string[] {
30
30
 
31
31
  /** The words that follow `vx <verb>` besides flags: subcommands and fixed choices. */
32
32
  const VERB_WORDS: Readonly<Record<string, readonly string[]>> = {
33
- cache: ['prune'],
33
+ cache: ['clean'],
34
34
  completions: [...SHELLS],
35
35
  }
36
36
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * One core per process. A plugin package, a `vx.workspace.ts`, a
2
+ * One core per process. A plugin package, a `.vx/workspace.ts`, a
3
3
  * `vx.config.ts` all `import … from '@vzn/vx'`, and that specifier
4
4
  * resolves through node_modules — to a SECOND copy of core when the host
5
5
  * is the compiled binary (whose own copy lives under the bunfs root) or
@@ -159,14 +159,15 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
159
159
  value: false,
160
160
  outcome: 'same',
161
161
  // Turbo 2.11's own help: "Equivalent to `--cache=local:r,remote:r`".
162
- vx: "`--no-cache` reads nothing either; Turbo's (reads, no writes) is `--cache local:r,remote:r`",
162
+ vx: "`--no-cache` reads nothing either; Turbo's (reads, no writes) is `--cache-policy local:r,remote:r`",
163
163
  },
164
164
  {
165
165
  runner: 'turbo',
166
166
  names: ['--cache'],
167
167
  value: true,
168
- outcome: 'same',
169
- vx: '`--cache local:rw,remote:r`',
168
+ outcome: 'alias',
169
+ vx: '`--cache-policy`, the same spec (`local:rw,remote:r`)',
170
+ to: (v) => ['--cache-policy', v ?? ''],
170
171
  },
171
172
  {
172
173
  runner: 'turbo',
@@ -256,7 +257,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
256
257
  value: true,
257
258
  shown: '`--experimental-otel-*`',
258
259
  outcome: 'refuse',
259
- vx: 'telemetry is a plugin: `otel()` from @vzn/vx-otel in vx.workspace.ts',
260
+ vx: 'telemetry is a plugin: `otel()` from @vzn/vx-otel in .vx/workspace.ts',
260
261
  },
261
262
  {
262
263
  runner: 'nx',
@@ -297,7 +298,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
297
298
  value: false,
298
299
  bool: true,
299
300
  outcome: 'refuse',
300
- vx: 'use `--cache local:,remote:rw`',
301
+ vx: 'use `--cache-policy local:,remote:rw`',
301
302
  },
302
303
  {
303
304
  runner: 'turbo',
@@ -305,7 +306,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
305
306
  value: false,
306
307
  bool: true,
307
308
  outcome: 'refuse',
308
- vx: 'use `--cache local:rw,remote:r`',
309
+ vx: 'use `--cache-policy local:rw,remote:r`',
309
310
  },
310
311
  {
311
312
  runner: 'turbo',
@@ -396,7 +397,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
396
397
  names: ['--token', '--team', '--api'],
397
398
  value: true,
398
399
  outcome: 'refuse',
399
- vx: 'a remote cache is a plugin: `turboCache()` from @vzn/vx-migrate in vx.workspace.ts reads TURBO_TOKEN / TURBO_TEAM / TURBO_API',
400
+ vx: 'a remote cache is a plugin: `turboCache()` from @vzn/vx-migrate in .vx/workspace.ts reads TURBO_TOKEN / TURBO_TEAM / TURBO_API',
400
401
  },
401
402
  {
402
403
  runner: 'turbo',
@@ -532,8 +533,8 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
532
533
  value: false,
533
534
  bool: true,
534
535
  outcome: 'alias',
535
- vx: '`--cache local:rw,remote:`',
536
- to: () => ['--cache', 'local:rw,remote:'],
536
+ vx: '`--cache-policy local:rw,remote:`',
537
+ to: () => ['--cache-policy', 'local:rw,remote:'],
537
538
  },
538
539
  {
539
540
  runner: 'nx',
@@ -575,7 +576,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
575
576
  names: ['--runner'],
576
577
  value: true,
577
578
  outcome: 'refuse',
578
- vx: 'a remote cache is a plugin: `nxCache()` from @vzn/vx-migrate in vx.workspace.ts',
579
+ vx: 'a remote cache is a plugin: `nxCache()` from @vzn/vx-migrate in .vx/workspace.ts',
579
580
  },
580
581
  {
581
582
  runner: 'nx',
@@ -656,7 +657,7 @@ export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
656
657
 
657
658
  /** Nx's verbs, as its users type them: `vx run` takes their flags. */
658
659
  const TURBO_LOGIN =
659
- 'vx has no login: a remote cache is a plugin, `turboCache()` from @vzn/vx-migrate in vx.workspace.ts, which reads TURBO_TOKEN / TURBO_TEAM / TURBO_API'
660
+ 'vx has no login: a remote cache is a plugin, `turboCache()` from @vzn/vx-migrate in .vx/workspace.ts, which reads TURBO_TOKEN / TURBO_TEAM / TURBO_API'
660
661
 
661
662
  export const FOREIGN_VERBS: Readonly<Record<string, string>> = {
662
663
  'run-many':
@@ -668,7 +669,7 @@ export const FOREIGN_VERBS: Readonly<Record<string, string>> = {
668
669
  query:
669
670
  '`turbo query` has no vx form: `vx run <task> --dry=json` prints the planned graph as JSON',
670
671
  reset:
671
- '`nx reset` is `vx cache prune --older-than <age>` here, or delete the cache store `vx info` names to drop it all',
672
+ '`nx reset` is `vx cache clean` here; `--older-than <age>` trims the cache instead of clearing it',
672
673
  daemon: 'vx has no daemon: there is nothing to start or stop',
673
674
  login: TURBO_LOGIN,
674
675
  logout: TURBO_LOGIN,