@vzn/vx 0.0.356 → 0.0.357

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.356",
3
+ "version": "0.0.357",
4
4
  "description": "A fast, correct 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.356",
25
- "@vzn/vx-linux-arm64": "0.0.356",
26
- "@vzn/vx-darwin-x64": "0.0.356",
27
- "@vzn/vx-darwin-arm64": "0.0.356"
24
+ "@vzn/vx-linux-x64": "0.0.357",
25
+ "@vzn/vx-linux-arm64": "0.0.357",
26
+ "@vzn/vx-darwin-x64": "0.0.357",
27
+ "@vzn/vx-darwin-arm64": "0.0.357"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -235,9 +235,23 @@ export function execWrap(command: string): string {
235
235
  */
236
236
  export function execWord(command: string): string | undefined {
237
237
  if (SHELL_CONTROL.test(command)) return undefined
238
- const first = command.trimStart().split(/\s+/)[0] ?? ''
239
- if (first === '' || first.includes('=') || SHELL_BUILTINS.has(first)) return undefined
240
- return first
238
+ // The word as the shell reads it: quotes group and are removed, so
239
+ // `"./my tool.sh"` names one file (the verdict looked for `"./my`).
240
+ const raw = command.trimStart()
241
+ let first = ''
242
+ let quote = ''
243
+ let i = 0
244
+ for (; i < raw.length; i++) {
245
+ const c = raw[i]!
246
+ if (quote !== '') {
247
+ if (c === quote) quote = ''
248
+ else first += c
249
+ } else if (c === "'" || c === '"') quote = c
250
+ else if (/\s/.test(c)) break
251
+ else first += c
252
+ }
253
+ if (quote !== '' || first === '' || raw.slice(0, i).includes('=')) return undefined
254
+ return SHELL_BUILTINS.has(first) ? undefined : first
241
255
  }
242
256
 
243
257
  /**
@@ -37,7 +37,7 @@ import {
37
37
  } from '../exec/index.js'
38
38
  import { isGroupTask, RestoreDemoted, type TaskNode, type TaskOutcome } from '../graph/index.js'
39
39
  import { killGraceMs, maskedEmitter, printable, relPosix, secretMask, span } from '../util/index.js'
40
- import { SIGNAL_SHUTDOWN_GRACE_MS } from './signals.js'
40
+ import { forwardedSignal, SIGNAL_SHUTDOWN_GRACE_MS } from './signals.js'
41
41
  import { executorLabel, nameExecutorFailure } from './plugin-host.js'
42
42
  import {
43
43
  mayWriteFingerprint,
@@ -354,6 +354,22 @@ async function executePersistentTask(args: ExecuteArgs): Promise<TaskOutcome> {
354
354
  persistentOpts.timeoutMs = effectiveTimeout
355
355
  }
356
356
 
357
+ // A stop that landed during the awaits above (the key, the sandbox's
358
+ // arming, request and wrap) leaves nothing to kill yet: spawned now, the
359
+ // server came up after the teardown and held the run's exit 7 s.
360
+ if (args.stopSignal?.aborted === true) {
361
+ if (bridgeTag !== undefined) releaseBridges(bridgeTag)
362
+ await placeholderSweeper(placeholders)()
363
+ return {
364
+ node,
365
+ status: 'aborted',
366
+ // The signal a spawned server would have been sent (a hang-up forwards SIGTERM).
367
+ exitCode: signalExitCode(forwardedSignal(args.stopSignal.reason)),
368
+ durationMs: 0,
369
+ wallclockStartNs,
370
+ wallclockEndNs: process.hrtime.bigint() - args.runStartHrTimeNs,
371
+ }
372
+ }
357
373
  const spawn = runPersistent(persistentOpts)
358
374
  // The host side of a port bridge lives exactly as long as the server:
359
375
  // released on the child's exit, whether the run tore it down or it died.