@vzn/vx 0.0.454 → 0.0.456
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/cli/completions.ts +12 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/index.ts +18 -2
- package/src/cli/upgrade.ts +4 -2
- package/src/orchestrator/execute-task.ts +17 -1
- package/src/orchestrator/metrics.ts +4 -3
- package/src/workspace/config-schema.ts +16 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.456",
|
|
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.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.456",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.456",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.456",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.456"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/completions.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// project names are not (a completion that evaluates configs on every
|
|
6
6
|
// Tab is the wrong price).
|
|
7
7
|
|
|
8
|
-
import { acceptedFlags, CORE_VERBS, seeHelp } from './help.js'
|
|
8
|
+
import { acceptedFlags, CORE_VERBS, flagHint, seeHelp } from './help.js'
|
|
9
|
+
import { nearest } from '../util/index.js'
|
|
9
10
|
|
|
10
11
|
export type CompletionShell = 'bash' | 'zsh' | 'fish'
|
|
11
12
|
const SHELLS: readonly CompletionShell[] = ['bash', 'zsh', 'fish']
|
|
@@ -95,8 +96,17 @@ export async function completionsCmd(
|
|
|
95
96
|
): Promise<number> {
|
|
96
97
|
const shell = args[0]
|
|
97
98
|
if (args.length !== 1 || !(SHELLS as readonly string[]).includes(shell ?? '')) {
|
|
99
|
+
const flag = args.find((a) => a.startsWith('-'))
|
|
100
|
+
if (flag !== undefined) {
|
|
101
|
+
process.stderr.write(
|
|
102
|
+
`vx completions: unknown flag: ${flag}${flagHint('completions', flag)}${seeHelp('completions')}\n`,
|
|
103
|
+
)
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
const one = shell !== undefined && args.length === 1
|
|
107
|
+
const best = one ? nearest(shell, SHELLS) : undefined
|
|
98
108
|
process.stderr.write(
|
|
99
|
-
`vx completions: expected one shell — bash, zsh or fish${
|
|
109
|
+
`vx completions: expected one shell — bash, zsh or fish${one ? ` (got ${shell})` : ''}${best === undefined ? '' : `. Did you mean ${best}?`}${seeHelp('completions')}\n`,
|
|
100
110
|
)
|
|
101
111
|
return 1
|
|
102
112
|
}
|
package/src/cli/help.ts
CHANGED
|
@@ -216,7 +216,8 @@ export function refusedWord(arg: string): string {
|
|
|
216
216
|
export function flagHint(verb: string, arg: string): string {
|
|
217
217
|
if (!arg.startsWith('-')) return ''
|
|
218
218
|
const name = arg.replace(/=.*$/, '')
|
|
219
|
-
|
|
219
|
+
// Every core verb answers `--help`, which no usage line spells.
|
|
220
|
+
const flags = ['--help', ...acceptedFlags(verb)]
|
|
220
221
|
// `--json` is gh's and Nx's spelling; a verb with `--format` reads it
|
|
221
222
|
// there, and no edit budget reaches `--format json` from `--json`.
|
|
222
223
|
if (name === '--json' && flags.includes('--format')) return ' (did you mean --format json?)'
|
package/src/cli/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// handler lives in a sibling `<name>.ts`; tests import its parsers there.
|
|
3
3
|
|
|
4
4
|
import { VERSION } from '../version.js'
|
|
5
|
-
import { CORE_VERBS, printHelp } from './help.js'
|
|
5
|
+
import { CORE_VERBS, flagHint, printHelp, refusedWord, seeHelp } from './help.js'
|
|
6
6
|
import { FOREIGN_VERBS } from './foreign-flags.js'
|
|
7
7
|
import { isUserError, MOVED_VERBS, nearest, UserError } from '../util/index.js'
|
|
8
8
|
|
|
@@ -49,6 +49,12 @@ export async function run(argv: readonly string[]): Promise<number> {
|
|
|
49
49
|
process.stderr.write(`${pointer}\n`)
|
|
50
50
|
return 1
|
|
51
51
|
}
|
|
52
|
+
if (unknown && verb.startsWith('-')) {
|
|
53
|
+
process.stderr.write(
|
|
54
|
+
`vx help: unknown flag: ${verb}${flagHint('help', verb)} (see \`vx help\`)\n`,
|
|
55
|
+
)
|
|
56
|
+
return 1
|
|
57
|
+
}
|
|
52
58
|
if (unknown) {
|
|
53
59
|
const declared = resolved === null ? [] : resolved.declaredVerbs
|
|
54
60
|
process.stderr.write(
|
|
@@ -61,9 +67,19 @@ export async function run(argv: readonly string[]): Promise<number> {
|
|
|
61
67
|
return 0
|
|
62
68
|
}
|
|
63
69
|
case '--version':
|
|
64
|
-
case 'version':
|
|
70
|
+
case 'version': {
|
|
71
|
+
// `vx version --hlp` printed the version and exited 0: a word it
|
|
72
|
+
// takes no more of is refused, as every other verb refuses one.
|
|
73
|
+
const extra = rest[0]
|
|
74
|
+
if (extra !== undefined) {
|
|
75
|
+
process.stderr.write(
|
|
76
|
+
`vx version: ${refusedWord(extra)}: ${extra}${flagHint('version', extra)}${seeHelp('version')}\n`,
|
|
77
|
+
)
|
|
78
|
+
return 1
|
|
79
|
+
}
|
|
65
80
|
process.stdout.write(`vx ${VERSION}\n`)
|
|
66
81
|
return 0
|
|
82
|
+
}
|
|
67
83
|
case 'run':
|
|
68
84
|
return await (await import('./run.js')).runCmd(rest)
|
|
69
85
|
case 'watch':
|
package/src/cli/upgrade.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// deno upgrade — "update" is what package managers do to indexes.
|
|
14
14
|
|
|
15
15
|
import { chmod, chown, link, rename, rm, stat } from 'node:fs/promises'
|
|
16
|
-
import { seeHelp } from './help.js'
|
|
16
|
+
import { flagHint, seeHelp } from './help.js'
|
|
17
17
|
import { UserError } from '../util/index.js'
|
|
18
18
|
import { VERSION } from '../version.js'
|
|
19
19
|
|
|
@@ -275,7 +275,9 @@ export async function replaceBinary(
|
|
|
275
275
|
export async function upgradeCmd(args: readonly string[]): Promise<number> {
|
|
276
276
|
const unknown = args.find((a) => a.startsWith('-'))
|
|
277
277
|
if (unknown !== undefined) {
|
|
278
|
-
process.stderr.write(
|
|
278
|
+
process.stderr.write(
|
|
279
|
+
`vx upgrade: unknown flag: ${unknown}${flagHint('upgrade', unknown)}${seeHelp('upgrade')}\n`,
|
|
280
|
+
)
|
|
279
281
|
return 1
|
|
280
282
|
}
|
|
281
283
|
// One release is installed; a second tag was dropped without a word and
|
|
@@ -36,7 +36,16 @@ import {
|
|
|
36
36
|
sandboxReads,
|
|
37
37
|
} from '../exec/index.js'
|
|
38
38
|
import { isGroupTask, RestoreDemoted, type TaskNode, type TaskOutcome } from '../graph/index.js'
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
killGraceMs,
|
|
41
|
+
MASKED,
|
|
42
|
+
maskedEmitter,
|
|
43
|
+
printable,
|
|
44
|
+
relPosix,
|
|
45
|
+
secretMask,
|
|
46
|
+
secretNamed,
|
|
47
|
+
span,
|
|
48
|
+
} from '../util/index.js'
|
|
40
49
|
import { forwardedSignal, SIGNAL_SHUTDOWN_GRACE_MS } from './signals.js'
|
|
41
50
|
import { executorLabel, nameExecutorFailure } from './plugin-host.js'
|
|
42
51
|
import {
|
|
@@ -778,6 +787,13 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
|
|
|
778
787
|
...(args.hashCache !== undefined ? { hashCache: args.hashCache } : {}),
|
|
779
788
|
})
|
|
780
789
|
: undefined
|
|
790
|
+
// A name only `exec.env.secret` makes secret is not one the name rule
|
|
791
|
+
// sees later: its row carries the mark, so `vx why` hides its hash (M-63).
|
|
792
|
+
const named = node.config.exec?.env?.secret
|
|
793
|
+
if (named !== undefined)
|
|
794
|
+
for (const c of captured)
|
|
795
|
+
if (c.kind === 'env' && named.includes(c.name) && !secretNamed(c.name))
|
|
796
|
+
c.hash = MASKED + c.hash
|
|
781
797
|
const inputs: TaskInputs | undefined = described?.inputs
|
|
782
798
|
// A declared input set that resolves to NOTHING is the quiet stale hit:
|
|
783
799
|
// the key stops moving with this project's source and every later run
|
|
@@ -557,11 +557,12 @@ export function diffKeyComponents(
|
|
|
557
557
|
const old = new Map(before.map((c) => [id(c), c]))
|
|
558
558
|
const entries: InputDiffEntry[] = []
|
|
559
559
|
let unchangedCount = 0
|
|
560
|
-
// A secret
|
|
560
|
+
// A secret env input's hash is its value, unsalted: a short one is
|
|
561
561
|
// recoverable from a log that printed `vx why` (M-62). The diff says it
|
|
562
|
-
// changed and never shows the hash.
|
|
562
|
+
// changed and never shows the hash. Secret by name, or by the mark a
|
|
563
|
+
// task's `exec.env.secret` put on its row (M-63).
|
|
563
564
|
const shown = (c: TaskInputComponent): string =>
|
|
564
|
-
c.kind === 'env' && secretNamed(c.name) ? MASKED : c.hash
|
|
565
|
+
c.kind === 'env' && (secretNamed(c.name) || c.hash.startsWith(MASKED)) ? MASKED : c.hash
|
|
565
566
|
const keys = new Set<string>([...cur.keys(), ...old.keys()])
|
|
566
567
|
for (const key of keys) {
|
|
567
568
|
const a = cur.get(key)
|
|
@@ -536,7 +536,7 @@ export function validateProjectConfig(config: ProjectConfig, configPath: string)
|
|
|
536
536
|
if (typeof g !== 'string' || g.length === 0) {
|
|
537
537
|
throw new UserError(`${where}.cache.outputs.files must be an array of non-empty strings`)
|
|
538
538
|
}
|
|
539
|
-
if (
|
|
539
|
+
if (isAbsoluteGlob(g)) {
|
|
540
540
|
throw new UserError(
|
|
541
541
|
`${where}.cache.outputs.files: absolute paths are not allowed (got "${g}") — ` +
|
|
542
542
|
`outputs must be project-relative globs`,
|
|
@@ -574,7 +574,7 @@ export function validateProjectConfig(config: ProjectConfig, configPath: string)
|
|
|
574
574
|
if (typeof g !== 'string' || g.length === 0) {
|
|
575
575
|
throw new UserError(`${where}.cache.inputs.files must be an array of non-empty strings`)
|
|
576
576
|
}
|
|
577
|
-
if (g.startsWith('
|
|
577
|
+
if (!g.startsWith('!/') && isAbsoluteGlob(g)) {
|
|
578
578
|
throw new UserError(
|
|
579
579
|
`${where}.cache.inputs.files: absolute paths are not allowed (got "${g}") — ` +
|
|
580
580
|
`inputs must be project-relative globs`,
|
|
@@ -1025,7 +1025,19 @@ function namesDirItself(glob: string): boolean {
|
|
|
1025
1025
|
*/
|
|
1026
1026
|
function hasParentSegment(glob: string): boolean {
|
|
1027
1027
|
const g = glob.startsWith('!') ? glob.slice(1) : glob
|
|
1028
|
-
|
|
1028
|
+
// A brace alternative is a segment too: `{../shared,src}/**` splits on `/`
|
|
1029
|
+
// into `{..`, so it passed, and its `..` arm matched nothing — an input
|
|
1030
|
+
// that silently left the key (M-64).
|
|
1031
|
+
return g.split(/[/{},]/).some((seg) => seg === '..')
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* True when a glob is absolute, or a brace alternative in it is:
|
|
1036
|
+
* `{/etc,dist}/*` matched nothing outside and said so nowhere (M-64).
|
|
1037
|
+
*/
|
|
1038
|
+
function isAbsoluteGlob(glob: string): boolean {
|
|
1039
|
+
const g = glob.startsWith('!') ? glob.slice(1) : glob
|
|
1040
|
+
return g.startsWith('/') || (g.includes('{') && /[{,]\//.test(g))
|
|
1029
1041
|
}
|
|
1030
1042
|
|
|
1031
1043
|
/**
|
|
@@ -1203,7 +1215,7 @@ function validateWorkspaceGlobs(v: unknown, where: string, negation: boolean): v
|
|
|
1203
1215
|
if (typeof g !== 'string' || g.length === 0) {
|
|
1204
1216
|
throw new UserError(`${where} must be an array of non-empty strings`)
|
|
1205
1217
|
}
|
|
1206
|
-
if (
|
|
1218
|
+
if (isAbsoluteGlob(g)) {
|
|
1207
1219
|
throw new UserError(
|
|
1208
1220
|
`${where}: absolute paths are not allowed (got "${g}") — ` +
|
|
1209
1221
|
`entries are workspace-root-relative globs`,
|