@vzn/vx 0.0.71 → 0.0.72

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.71",
3
+ "version": "0.0.72",
4
4
  "description": "An open, extensible monorepo task runner.",
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.71",
21
- "@vzn/vx-linux-arm64": "0.0.71",
22
- "@vzn/vx-darwin-x64": "0.0.71",
23
- "@vzn/vx-darwin-arm64": "0.0.71"
20
+ "@vzn/vx-linux-x64": "0.0.72",
21
+ "@vzn/vx-linux-arm64": "0.0.72",
22
+ "@vzn/vx-darwin-x64": "0.0.72",
23
+ "@vzn/vx-darwin-arm64": "0.0.72"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -178,6 +178,12 @@ export interface ScheduleOptions {
178
178
  execute: (node: TaskNode, upstream: TaskOutcome[]) => Promise<TaskOutcome>
179
179
  onStart?: (node: TaskNode) => void
180
180
  onFinish?: (outcome: TaskOutcome) => void
181
+ /**
182
+ * Where the line naming a rejected `execute` goes, before its failed
183
+ * outcome lands. Absent = stderr. The run gives it the task's own
184
+ * stderr, so its frame and the failure recap carry the reason.
185
+ */
186
+ onError?: (node: TaskNode, line: string) => void
181
187
  /**
182
188
  * What an outcome still owes before its dependents may start — a cache
183
189
  * save that runs off the execution slot (the orchestrator's save lane):
@@ -722,19 +728,15 @@ export async function runGraph(options: ScheduleOptions): Promise<Map<string, Ta
722
728
  })
723
729
  leave()
724
730
  untrack()
725
- finishOne(id, outcome)
726
- // Surface the error live; the outcome itself doesn't
727
- // carry captured stderr (that's the logger's job). A
728
- // UserError is a config/input failure (e.g. a failed
731
+ // A UserError is a config/input failure (e.g. a failed
729
732
  // `cache.inputs.runtime` command), not a vx bug — report it
730
733
  // plainly, never as an "internal error".
734
+ let line: string
731
735
  if (isUserError(err) || isFsRefusal(err)) {
732
736
  const text = isUserError(err) ? message : `${message} — ${fsRefusalHint(err)}`
733
737
  const first = refusedBy.get(text)
734
738
  if (first === undefined) refusedBy.set(text, id)
735
- process.stderr.write(
736
- `[vx] ${id}: ${first === undefined ? text : `as ${first} above`}\n`,
737
- )
739
+ line = `[vx] ${id}: ${first === undefined ? text : `as ${first} above`}\n`
738
740
  } else {
739
741
  const named = err instanceof Error && err.name !== 'Error' ? `${err.name}: ` : ''
740
742
  // A wrapped error's cause is the fact the reader needs (a
@@ -746,8 +748,15 @@ export async function runGraph(options: ScheduleOptions): Promise<Map<string, Ta
746
748
  cause === undefined
747
749
  ? ''
748
750
  : ` (cause: ${(cause as NodeJS.ErrnoException).code ?? cause.name}: ${cause.message})`
749
- process.stderr.write(`[vx] internal error in ${id}: ${named}${message}${because}\n`)
751
+ line = `[vx] internal error in ${id}: ${named}${message}${because}\n`
750
752
  }
753
+ try {
754
+ if (options.onError === undefined) process.stderr.write(line)
755
+ else options.onError(node, line)
756
+ } catch {
757
+ process.stderr.write(line)
758
+ }
759
+ finishOne(id, outcome)
751
760
  tick()
752
761
  },
753
762
  )
@@ -806,6 +806,7 @@ async function runOnBus(
806
806
  log.taskComplete(o.node, o)
807
807
  narrowDemand(o.node.id)
808
808
  },
809
+ onError: (node, line) => log.taskStderr(node, line),
809
810
  execute: (node, upstream) => executeWithDedup(node, keyUpstream(node, upstream)),
810
811
  // A `schedule` plugin's weights; the scheduler keeps its structural
811
812
  // baseline as the tie-break. Empty map → baseline only.