@vzn/vx 0.0.277 → 0.0.279
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 +5 -5
- package/src/bin.ts +17 -16
- package/src/cli/init.ts +44 -2
- package/src/cli/show.ts +5 -0
- package/src/cli/why.ts +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.279",
|
|
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.279",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.279",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.279",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.279"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/bin.ts
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun --no-env-file --no-install
|
|
2
|
-
import { registerCoreAlias, run } from './cli/index.js'
|
|
3
|
-
import {
|
|
4
|
-
fsRefusalHint,
|
|
5
|
-
isFsRefusal,
|
|
6
|
-
isOutOfFds,
|
|
7
|
-
isUserError,
|
|
8
|
-
OUT_OF_FDS_HINT,
|
|
9
|
-
} from './util/index.js'
|
|
10
|
-
|
|
11
|
-
// Every `import … from '@vzn/vx'` this process evaluates — a plugin
|
|
12
|
-
// package, a workspace or project config — resolves to THIS core, not to
|
|
13
|
-
// a second copy from node_modules (core-alias.ts says why and what it
|
|
14
|
-
// costs). Registered before any verb runs; the façade loads on first use.
|
|
15
|
-
registerCoreAlias(() => import('./index.js') as Promise<Record<string, unknown>>)
|
|
16
|
-
|
|
17
2
|
// Wrapped in an explicit async main so `bun build --compile` accepts
|
|
18
3
|
// the file. The compile target doesn't allow top-level await.
|
|
19
4
|
let settled = false
|
|
20
5
|
|
|
21
6
|
async function main(): Promise<void> {
|
|
7
|
+
const argv = process.argv.slice(2)
|
|
22
8
|
try {
|
|
23
|
-
|
|
9
|
+
// `vx --version` needs one leaf module; the dispatcher and the util
|
|
10
|
+
// barrel it pulled in were 20 ms of its 28 (2026-10-01).
|
|
11
|
+
if (argv.length === 1 && (argv[0] === '--version' || argv[0] === 'version')) {
|
|
12
|
+
process.stdout.write(`vx ${(await import('./version.js')).VERSION}\n`)
|
|
13
|
+
settled = true
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
const { registerCoreAlias, run } = await import('./cli/index.js')
|
|
17
|
+
// Every `import … from '@vzn/vx'` this process evaluates — a plugin
|
|
18
|
+
// package, a workspace or project config — resolves to THIS core, not to
|
|
19
|
+
// a second copy from node_modules (core-alias.ts says why and what it
|
|
20
|
+
// costs). Registered before any verb runs; the façade loads on first use.
|
|
21
|
+
registerCoreAlias(() => import('./index.js') as Promise<Record<string, unknown>>)
|
|
22
|
+
const code = await run(argv)
|
|
24
23
|
// NOTHING calls `process.exit` here, and that is the fix rather than a
|
|
25
24
|
// simplification. Bun drops what a pipe has not yet taken when
|
|
26
25
|
// `process.exit` follows a large write: 300 KB written then exit
|
|
@@ -34,6 +33,8 @@ async function main(): Promise<void> {
|
|
|
34
33
|
// several hundred spawns of this binary would show at once.
|
|
35
34
|
process.exitCode = code
|
|
36
35
|
} catch (err) {
|
|
36
|
+
const { fsRefusalHint, isFsRefusal, isOutOfFds, isUserError, OUT_OF_FDS_HINT } =
|
|
37
|
+
await import('./util/index.js')
|
|
37
38
|
// UserError (workspace not found, cycle, config invalid, ...) —
|
|
38
39
|
// print the message only; the stack is noise the user can't act
|
|
39
40
|
// on. Everything else gets the full stack so internal bugs are
|
package/src/cli/init.ts
CHANGED
|
@@ -180,7 +180,9 @@ async function adopt(
|
|
|
180
180
|
: `wrote ${name}.`
|
|
181
181
|
const cacheLine =
|
|
182
182
|
remote === undefined
|
|
183
|
-
? ''
|
|
183
|
+
? runner === 'nx' && usesNxCloud(root)
|
|
184
|
+
? 'nx.json connects Nx Cloud, whose cache vx cannot share: runs cache on this machine (nxCache() serves a self-hosted Nx cache).\n'
|
|
185
|
+
: ''
|
|
184
186
|
: kept && !readFileSync(path.join(root, existing), 'utf8').includes(`${cache}(`)
|
|
185
187
|
? `${remote}: add ${cache}() from @vzn/vx-migrate to its plugins and vx shares that remote cache.\n`
|
|
186
188
|
: kept
|
|
@@ -195,6 +197,31 @@ async function adopt(
|
|
|
195
197
|
|
|
196
198
|
const REMOTE_CACHE = { turbo: 'turboCache', nx: 'nxCache' } as const
|
|
197
199
|
|
|
200
|
+
/**
|
|
201
|
+
* nx.json names an Nx Cloud workspace (`nxCloudId`, `nxCloudAccessToken`,
|
|
202
|
+
* or the legacy `nx-cloud` runner). Its wire is Nx's own, so a user who
|
|
203
|
+
* expected their remote cache under vx heard nothing and ran cold.
|
|
204
|
+
*/
|
|
205
|
+
function usesNxCloud(root: string): boolean {
|
|
206
|
+
let json: {
|
|
207
|
+
nxCloudId?: unknown
|
|
208
|
+
nxCloudAccessToken?: unknown
|
|
209
|
+
tasksRunnerOptions?: { default?: { runner?: unknown } }
|
|
210
|
+
} | null
|
|
211
|
+
try {
|
|
212
|
+
json = Bun.JSONC.parse(readFileSync(path.join(root, 'nx.json'), 'utf8')) as typeof json
|
|
213
|
+
} catch {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
const set = (v: unknown) => typeof v === 'string' && v !== ''
|
|
217
|
+
const runner = json?.tasksRunnerOptions?.default?.runner
|
|
218
|
+
return (
|
|
219
|
+
set(json?.nxCloudId) ||
|
|
220
|
+
set(json?.nxCloudAccessToken) ||
|
|
221
|
+
(typeof runner === 'string' && /(^|\/)nx-cloud$/.test(runner))
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
198
225
|
/** The variable each runner's self-hosted remote cache is set by, as its plugin reads it. */
|
|
199
226
|
const REMOTE_CACHE_ENV = { turbo: 'TURBO_TOKEN', nx: 'NX_SELF_HOSTED_REMOTE_CACHE_SERVER' } as const
|
|
200
227
|
|
|
@@ -202,7 +229,7 @@ const CI_FILES = ['.gitlab-ci.yml', '.circleci/config.yml']
|
|
|
202
229
|
|
|
203
230
|
/**
|
|
204
231
|
* Where the repo shows a remote cache its runner uses, or undefined: turbo.json's
|
|
205
|
-
* enabled `remoteCache`, or a CI file that sets the runner's cache variable (a
|
|
232
|
+
* enabled `remoteCache`, a `turbo link`ed `.turbo/config.json`, or a CI file that sets the runner's cache variable (a
|
|
206
233
|
* local repo holds no token; CI does).
|
|
207
234
|
*/
|
|
208
235
|
function remoteCacheSignal(root: string, runner: 'turbo' | 'nx'): string | undefined {
|
|
@@ -222,6 +249,21 @@ function remoteCacheSignal(root: string, runner: 'turbo' | 'nx'): string | undef
|
|
|
222
249
|
return `${f} names a remoteCache`
|
|
223
250
|
}
|
|
224
251
|
}
|
|
252
|
+
// `turbo link` writes the team here (gitignored, on the machine that
|
|
253
|
+
// linked); turboCache() reads it, so a linked repo has a remote cache.
|
|
254
|
+
let linked: unknown
|
|
255
|
+
try {
|
|
256
|
+
linked = JSON.parse(readFileSync(path.join(root, '.turbo', 'config.json'), 'utf8'))
|
|
257
|
+
} catch {}
|
|
258
|
+
if (
|
|
259
|
+
typeof linked === 'object' &&
|
|
260
|
+
linked !== null &&
|
|
261
|
+
Object.entries(linked).some(
|
|
262
|
+
([k, v]) => /^(teamid|teamslug|token)$/i.test(k) && typeof v === 'string' && v !== '',
|
|
263
|
+
)
|
|
264
|
+
) {
|
|
265
|
+
return '.turbo/config.json links a team (turbo link)'
|
|
266
|
+
}
|
|
225
267
|
}
|
|
226
268
|
const variable = REMOTE_CACHE_ENV[runner]
|
|
227
269
|
// A line that sets it (`TURBO_TOKEN: …`, `TURBO_TOKEN=…`), not one that
|
package/src/cli/show.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// <task>`: one task. `<task>`: that task in every project declaring it.
|
|
7
7
|
// Deliberately NOT the lock — vx-lock.json is already the frozen JSON.
|
|
8
8
|
|
|
9
|
+
import path from 'node:path'
|
|
9
10
|
import type { ProjectConfig, TaskConfig } from '../config.js'
|
|
10
11
|
import { declaredTask } from '../graph/index.js'
|
|
11
12
|
import { flagHint, seeHelp } from './help.js'
|
|
@@ -65,6 +66,10 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
65
66
|
const metas = await discoverCliProjects(await loadWorkspace(root, reads))
|
|
66
67
|
const byName = new Map(metas.map((m) => [m.name, m]))
|
|
67
68
|
|
|
69
|
+
// `//#task` is Turbo's root package's task: the root project here (D-39).
|
|
70
|
+
const rootMeta = metas.find((m) => path.resolve(m.dir) === path.resolve(root))
|
|
71
|
+
if (parsed.target?.startsWith('//#') && rootMeta !== undefined)
|
|
72
|
+
parsed.target = `${rootMeta.name}#${parsed.target.slice(3)}`
|
|
68
73
|
const hashAt = parsed.target?.indexOf('#') ?? -1
|
|
69
74
|
const projectName =
|
|
70
75
|
parsed.target === undefined
|
package/src/cli/why.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// names the exact cache-key components that differ. Read-only over cache.db —
|
|
6
6
|
// no config evaluation, no re-hash.
|
|
7
7
|
|
|
8
|
+
import path from 'node:path'
|
|
8
9
|
import { Cache, noteSchemaReset } from '../cache/index.js'
|
|
9
10
|
import { flagHint, seeHelp } from './help.js'
|
|
10
11
|
import { splitTaskId } from '../graph/index.js'
|
|
@@ -159,6 +160,16 @@ function resolveTarget(cache: Cache, target: string): string {
|
|
|
159
160
|
|
|
160
161
|
const fmtWhen = (ms: number): string => new Date(ms).toISOString()
|
|
161
162
|
|
|
163
|
+
/** `//#task`, Turbo's root package, as the root project's id: its package.json name (D-39). */
|
|
164
|
+
async function rootSpelled(root: string, target: string): Promise<string> {
|
|
165
|
+
if (!target.startsWith('//#')) return target
|
|
166
|
+
const name = await Bun.file(path.join(root, 'package.json'))
|
|
167
|
+
.json()
|
|
168
|
+
.then((j: { name?: unknown }) => j.name)
|
|
169
|
+
.catch(() => undefined)
|
|
170
|
+
return typeof name === 'string' && name !== '' ? `${name}#${target.slice(3)}` : target
|
|
171
|
+
}
|
|
172
|
+
|
|
162
173
|
export async function whyCmd(args: readonly string[]): Promise<number> {
|
|
163
174
|
const parsed = parseWhyArgs(args)
|
|
164
175
|
if (parsed.error !== undefined) throw new UserError(`vx why: ${parsed.error}`)
|
|
@@ -171,7 +182,7 @@ export async function whyCmd(args: readonly string[]): Promise<number> {
|
|
|
171
182
|
noteSchemaReset(cache, warnToStderr)
|
|
172
183
|
try {
|
|
173
184
|
const db = cache.dbHandle()
|
|
174
|
-
const taskId = resolveTarget(cache, parsed.target)
|
|
185
|
+
const taskId = resolveTarget(cache, await rootSpelled(root, parsed.target))
|
|
175
186
|
const runId =
|
|
176
187
|
parsed.runId !== undefined
|
|
177
188
|
? (resolveRunId(db, parsed.runId, 'vx why') ?? parsed.runId)
|