@vzn/vx 0.0.635 → 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.
- package/README.md +47 -19
- package/docs/caching.md +222 -210
- package/docs/cli.md +699 -664
- package/docs/execution.md +67 -75
- package/docs/features.md +44 -40
- package/docs/patterns.md +31 -32
- package/docs/schema.md +418 -202
- package/docs/security.md +76 -39
- package/launcher.sh +43 -0
- package/package.json +5 -5
- package/schemas/cache.json +41 -19
- package/schemas/info.json +23 -11
- package/schemas/last.json +48 -2
- package/schemas/lock.json +1 -1
- package/schemas/plan.json +2 -2
- package/schemas/show.json +13 -4
- package/schemas/why.json +15 -1
- package/skills/vx/SKILL.md +16 -12
- package/src/bin.ts +17 -2
- package/src/cache/archive.ts +11 -0
- package/src/cache/cache.ts +105 -53
- package/src/cache/git-inputs.ts +277 -63
- package/src/cache/index.ts +7 -1
- package/src/cache/inputs.ts +125 -35
- package/src/cache/key-fold.ts +8 -7
- package/src/cache/layer.ts +8 -17
- package/src/cache/layered-cache.ts +16 -102
- package/src/cache/policy.ts +25 -29
- package/src/cache/run-history.ts +36 -14
- package/src/cache/schema.ts +21 -23
- package/src/cache/scoped-cache.ts +224 -0
- package/src/cli/cache.ts +150 -28
- package/src/cli/completions.ts +2 -2
- package/src/cli/core-alias.ts +1 -1
- package/src/cli/foreign-flags.ts +13 -12
- package/src/cli/help.ts +94 -61
- package/src/cli/index.ts +42 -13
- package/src/cli/info.ts +38 -23
- package/src/cli/init.ts +115 -406
- package/src/cli/last.ts +145 -4
- package/src/cli/lock.ts +333 -111
- package/src/cli/plan-format.ts +2 -2
- package/src/cli/plugin-commands.ts +5 -9
- package/src/cli/plugin-templates.ts +10 -10
- package/src/cli/run.ts +123 -144
- package/src/cli/select.ts +33 -39
- package/src/cli/show.ts +17 -19
- package/src/cli/task-verb.ts +2 -9
- package/src/cli/upgrade.ts +402 -145
- package/src/cli/watch-cycle.ts +9 -2
- package/src/cli/watch-filter.ts +33 -60
- package/src/cli/watch-fs.ts +29 -16
- package/src/cli/watch-judge.ts +12 -3
- package/src/cli/watch-set.ts +16 -42
- package/src/cli/watch.ts +188 -215
- package/src/cli/why.ts +28 -17
- package/src/cli/workspace-config.ts +31 -68
- package/src/config.ts +191 -71
- package/src/exec/env.ts +1 -31
- package/src/exec/executor.ts +1 -5
- package/src/exec/kill-tree.ts +2 -1
- package/src/exec/sandbox-runtime.ts +12 -1
- package/src/exec/sandbox-violations.ts +9 -1
- package/src/graph/dependency-spec.ts +1 -1
- package/src/graph/index.ts +1 -0
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +19 -7
- package/src/graph/task-graph.ts +44 -20
- package/src/handoff.ts +169 -0
- package/src/index.ts +25 -57
- package/src/orchestrator/affected-tasks.ts +7 -5
- package/src/orchestrator/command-windows.ts +44 -0
- package/src/orchestrator/deferred-outputs.ts +12 -10
- package/src/orchestrator/doctor.ts +98 -110
- package/src/orchestrator/download-policy.ts +3 -3
- package/src/orchestrator/events.ts +19 -3
- package/src/orchestrator/excluded-keys.ts +0 -2
- package/src/orchestrator/execute-task.ts +192 -70
- package/src/orchestrator/fingerprint-watch.ts +18 -15
- package/src/orchestrator/forecast.ts +10 -2
- package/src/orchestrator/framed-output.ts +8 -3
- package/src/orchestrator/graph-stage.ts +5 -5
- package/src/orchestrator/history.ts +70 -30
- package/src/orchestrator/hit-restore.ts +15 -3
- package/src/orchestrator/index.ts +18 -4
- package/src/orchestrator/local-shortcircuit.ts +2 -1
- package/src/orchestrator/lockfile-claim.ts +79 -20
- package/src/orchestrator/logger.ts +71 -39
- package/src/orchestrator/metrics.ts +71 -6
- package/src/orchestrator/miss-save.ts +3 -0
- package/src/orchestrator/options.ts +43 -28
- package/src/orchestrator/output-host.ts +72 -0
- package/src/orchestrator/plan.ts +6 -8
- package/src/orchestrator/plugin-host.ts +87 -63
- package/src/orchestrator/plugin.ts +94 -60
- package/src/orchestrator/prepare.ts +114 -73
- package/src/orchestrator/projects.ts +102 -99
- package/src/orchestrator/remote-prefetch.ts +2 -1
- package/src/orchestrator/run-context.ts +25 -6
- package/src/orchestrator/run-failures.ts +17 -8
- package/src/orchestrator/run-lock.ts +1 -1
- package/src/orchestrator/run-report.ts +9 -9
- package/src/orchestrator/run-settings.ts +48 -0
- package/src/orchestrator/run.ts +127 -88
- package/src/orchestrator/sandbox-fix.ts +39 -20
- package/src/orchestrator/sandbox-request.ts +84 -13
- package/src/orchestrator/signals.ts +1 -1
- package/src/orchestrator/stable-keys.ts +13 -11
- package/src/orchestrator/status-line.ts +48 -6
- package/src/orchestrator/summary.ts +46 -62
- package/src/orchestrator/task-hash.ts +12 -83
- package/src/orchestrator/telemetry.ts +3 -3
- package/src/settings.ts +417 -0
- package/src/util/errors.ts +1 -1
- package/src/util/essential-env.ts +31 -0
- package/src/util/hide-env.ts +107 -0
- package/src/util/index.ts +8 -2
- package/src/util/paths.ts +10 -0
- package/src/util/settle.ts +49 -30
- package/src/util/size.ts +23 -4
- package/src/util/timing.ts +3 -2
- package/src/util/ulid.ts +1 -1
- package/src/util/verbs.ts +0 -15
- package/src/workspace/affected.ts +77 -222
- package/src/workspace/config-eval.ts +15 -20
- package/src/workspace/config-imports.ts +78 -3
- package/src/workspace/config-schema.ts +251 -241
- package/src/workspace/dependency-rules.ts +57 -0
- package/src/workspace/filter.ts +2 -2
- package/src/workspace/fingerprint.ts +11 -121
- package/src/workspace/index.ts +22 -27
- package/src/workspace/load-reads.ts +4 -4
- package/src/workspace/lockfile.ts +96 -140
- package/src/workspace/migration.ts +242 -146
- package/src/workspace/package-graph.ts +30 -216
- package/src/workspace/project-loader.ts +80 -285
- package/src/workspace/repo-id.ts +2 -1
- package/src/workspace/workspace.ts +362 -831
- package/src/cache/config-evals.ts +0 -130
- package/src/workspace/config-cache.ts +0 -661
- 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 {
|
|
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 `
|
|
23
|
-
* first-run path, 2026-09-20). A typo of `
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
rm: '`vx cache
|
|
38
|
-
delete:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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, ['
|
|
48
|
-
// `
|
|
49
|
-
return best === undefined ? '. The subcommand is
|
|
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 '
|
|
56
|
-
return await
|
|
62
|
+
case 'clean':
|
|
63
|
+
return await cleanCmd(rest)
|
|
57
64
|
case undefined:
|
|
58
|
-
// A bare `vx cache
|
|
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
|
|
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 —
|
|
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 —
|
|
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
|
|
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.
|
|
153
|
-
return {
|
|
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
|
|
176
|
+
async function cleanCmd(args: readonly string[]): Promise<number> {
|
|
159
177
|
const parsed = parsePruneArgs(args)
|
|
160
|
-
if (parsed.error) return refuse('cache
|
|
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
|
|
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
|
|
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
|
+
}
|
package/src/cli/completions.ts
CHANGED
|
@@ -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: ['
|
|
33
|
+
cache: ['clean'],
|
|
34
34
|
completions: [...SHELLS],
|
|
35
35
|
}
|
|
36
36
|
|
package/src/cli/core-alias.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One core per process. A plugin package, 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
|
package/src/cli/foreign-flags.ts
CHANGED
|
@@ -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: '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|