@vzn/vx 0.0.95 → 0.0.96

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.95",
3
+ "version": "0.0.96",
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.95",
21
- "@vzn/vx-linux-arm64": "0.0.95",
22
- "@vzn/vx-darwin-x64": "0.0.95",
23
- "@vzn/vx-darwin-arm64": "0.0.95"
20
+ "@vzn/vx-linux-x64": "0.0.96",
21
+ "@vzn/vx-linux-arm64": "0.0.96",
22
+ "@vzn/vx-darwin-x64": "0.0.96",
23
+ "@vzn/vx-darwin-arm64": "0.0.96"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -174,8 +174,10 @@ export async function* tarEntries(stream: ReadableStream<Uint8Array>): AsyncGene
174
174
  const size = pendingSize ?? octal(h, 124, 12)
175
175
  const mtimeMs = octal(h, 136, 12) * 1000
176
176
  const padded = Math.ceil(size / BLOCK) * BLOCK
177
- const magic = field(h, 257, 6)
178
- const prefix = magic.startsWith('ustar') ? field(h, 345, 155) : ''
177
+ // POSIX `ustar\0` only: old GNU's `ustar ` keeps atime and ctime at
178
+ // 345, and `-G` fills them, so a prefix read there named every entry
179
+ // `<atime>…/<name>`.
180
+ const prefix = field(h, 257, 6) === 'ustar' ? field(h, 345, 155) : ''
179
181
  const rawName = field(h, 0, 100)
180
182
  let name = pendingPath ?? (prefix ? `${prefix}/${rawName}` : rawName)
181
183
  pendingPath = undefined
package/src/cli/show.ts CHANGED
@@ -83,7 +83,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
83
83
  const bareTask = projectName !== undefined && taskName === undefined && !byName.has(projectName)
84
84
  const scope = projectName === undefined || bareTask ? 'all' : [projectName]
85
85
 
86
- const projects = await loadCliProjects(root, metas, scope)
86
+ const projects = await loadCliProjects(root, metas, scope, { noCreate: true })
87
87
 
88
88
  if (parsed.target === undefined) {
89
89
  process.stdout.write(renderList(root, metas, projects, parsed.format))
package/src/cli/watch.ts CHANGED
@@ -16,6 +16,7 @@ import fs from 'node:fs'
16
16
  import path from 'node:path'
17
17
  import { xxh3 } from '../util/index.js'
18
18
  import { parseRunArgs, resolveRunOptions, type RunArgs } from './run.js'
19
+ import { seeHelp } from './help.js'
19
20
  import {
20
21
  fingerprintClaims,
21
22
  forwardedSignal,
@@ -96,7 +97,9 @@ export async function watchCmd(args: readonly string[]): Promise<number> {
96
97
  return 1
97
98
  }
98
99
  if (parsed.tasks.length === 0) {
99
- process.stderr.write(`vx watch: missing task name\n`)
100
+ process.stderr.write(
101
+ `vx watch: missing task name (vx watch <task>, e.g. vx watch build)${seeHelp('watch')}\n`,
102
+ )
100
103
  return 1
101
104
  }
102
105
 
@@ -88,6 +88,12 @@ export interface CliLoadOptions {
88
88
  cacheDir?: string
89
89
  /** `--frozen`: configs come from `vx-lock.json`, not evaluation. */
90
90
  frozen?: boolean
91
+ /**
92
+ * A reading verb (`vx show`) makes nothing on disk (item 900): with no
93
+ * cache directory yet, it evaluates without the cache rather than
94
+ * create one.
95
+ */
96
+ noCreate?: boolean
91
97
  }
92
98
 
93
99
  /**
@@ -113,8 +119,11 @@ export async function loadCliProjects(
113
119
  // glob could select live what the run then treats otherwise.
114
120
  const lock = opts.frozen === true ? await readLockfile(workspaceRoot) : null
115
121
  if (opts.frozen === true && lock === null) throw new UserError(FROZEN_WITHOUT_LOCK)
116
- const cache = new Cache(cacheDir, { read: true, write: true }, workspaceRoot)
117
- noteSchemaReset(cache, warnToStderr)
122
+ const cache =
123
+ opts.noCreate === true && !existsSync(cacheDir)
124
+ ? null
125
+ : new Cache(cacheDir, { read: true, write: true }, workspaceRoot)
126
+ if (cache !== null) noteSchemaReset(cache, warnToStderr)
118
127
  try {
119
128
  const loaded = await loadProjects({
120
129
  workspaceRoot,
@@ -125,15 +134,18 @@ export async function loadCliProjects(
125
134
  seeds: scope,
126
135
  closure: false,
127
136
  lock,
128
- evalCache: {
129
- store: cache,
130
- workspaceRoot,
131
- workspaceFingerprint: await computeWorkspaceFingerprint(workspaceRoot),
132
- },
137
+ evalCache:
138
+ cache === null
139
+ ? undefined
140
+ : {
141
+ store: cache,
142
+ workspaceRoot,
143
+ workspaceFingerprint: await computeWorkspaceFingerprint(workspaceRoot),
144
+ },
133
145
  warn: warnToStderr,
134
146
  })
135
147
  return loaded.projects
136
148
  } finally {
137
- cache.close()
149
+ cache?.close()
138
150
  }
139
151
  }
@@ -678,15 +678,19 @@ export function resolveSandboxConfig(
678
678
  r.weakerNetworkIsolation = cfg.weakerNetworkIsolation
679
679
  }
680
680
  if (cfg.ignore !== undefined) {
681
- // Relative patterns anchor at the project dir; `~` ones are taken as
682
- // written. A pattern is not a path, but its literal head is: both
681
+ // Relative patterns anchor at the project dir, `~` ones at the home
682
+ // directory, as a grant's are (a `~` pattern kept as written matched no
683
+ // recorded path). A pattern is not a path, but its literal head is: both
683
684
  // producers record where a denial LANDS, so the head is canonicalized
684
685
  // like every other side of the policy. Anchored at a project reached
685
686
  // through a link (macOS's `/var`), no pattern matched and the denial it
686
687
  // named failed the task (B-2).
687
688
  const anchor = (pat: string): string => {
688
- if (pat.startsWith('~')) return pat
689
- const abs = path.isAbsolute(pat) ? pat : path.join(projectDir, pat)
689
+ const abs = pat.startsWith('~')
690
+ ? path.join(os.homedir(), pat.slice(1))
691
+ : path.isAbsolute(pat)
692
+ ? pat
693
+ : path.join(projectDir, pat)
690
694
  const wild = abs.search(BUN_GLOB_WILDCARDS)
691
695
  const head = wild === -1 ? abs : abs.slice(0, abs.lastIndexOf(path.sep, wild)) || path.sep
692
696
  return toRealPath(head) + abs.slice(head.length)
@@ -906,10 +910,11 @@ function ownGroupCommand(
906
910
  // the tracer attached, so one it inherited (the watcher, SRT's network
907
911
  // bridges) that exits first sent the command on untraced, and under
908
912
  // `--seccomp-bpf` its `execve` failed ENOSYS. A fresh fork has none.
909
- // An async list starts with SIGINT and SIGQUIT ignored, and a shell
910
- // cannot trap what it was started ignoring: SRT's bash may put them
911
- // back before the `exec` (dash may not), so a task's `trap … INT` hears
912
- // vx's cancellation as it does untraced.
913
+ // POSIX lets an async list start with SIGINT and SIGQUIT ignored, and a
914
+ // shell cannot trap what it was started ignoring: bash 5.2 ignores them
915
+ // for a backgrounded simple command (the first cut here, whose task's
916
+ // `trap … INT` never fired) but not for a brace group, so the `trap -`
917
+ // is for a bash that does; SRT's bash may put them back (dash may not).
913
918
  const tracer = [
914
919
  executablePath('strace'),
915
920
  '-DD',