balladeer 0.0.4 → 0.0.5

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.
Files changed (2) hide show
  1. package/bin/balladeer.js +35 -10
  2. package/package.json +1 -1
package/bin/balladeer.js CHANGED
@@ -12,7 +12,7 @@
12
12
  const { createHash } = require("node:crypto");
13
13
  const { existsSync, mkdtempSync, writeFileSync, rmSync } = require("node:fs");
14
14
  const { tmpdir } = require("node:os");
15
- const { join } = require("node:path");
15
+ const { join, sep } = require("node:path");
16
16
  const { spawnSync } = require("node:child_process");
17
17
  const readline = require("node:readline");
18
18
 
@@ -82,17 +82,34 @@ async function main() {
82
82
  return;
83
83
  }
84
84
 
85
- // Volta prepends its real-node bin dir to a child tool's PATH, so a bare `npm` spawned from
86
- // here resolves to the RAW npm inside the node image and installs into that image's globals,
87
- // while the `balladeer` on the user's PATH stays Volta's shim serving THIS bootstrapper — a
88
- // split-brain where every run is the installer again. Route the install through Volta's own
89
- // npm shim so its interception updates the package image the shim actually serves.
90
- const voltaNpm = process.env.VOLTA_HOME ? join(process.env.VOLTA_HOME, "bin", "npm") : null;
85
+ // Volta, two traps (both hit live on a Volta machine): (1) it prepends its real-node bin dir
86
+ // to a child tool's PATH, so a bare `npm` here is the RAW npm that installs into the node
87
+ // image's globals — while the user's `balladeer` stays Volta's shim serving THIS bootstrapper
88
+ // (split-brain: every run is the installer again); (2) it stamps _VOLTA_TOOL_RECURSION into a
89
+ // shimmed tool's env, and the shim npm passes straight through to the raw npm when it sees it,
90
+ // so merely routing through $VOLTA_HOME/bin/npm changes nothing. Fix: spawn Volta's npm shim
91
+ // WITH the recursion guard scrubbed, so its global-install interception updates the package
92
+ // image the user's shim actually serves.
93
+ const childEnv = { ...process.env };
94
+ delete childEnv._VOLTA_TOOL_RECURSION;
95
+ // Volta detection cannot rely on env: inside a shimmed tool VOLTA_HOME is absent (the env
96
+ // carries only _VOLTA_TOOL_RECURSION). Our own node binary IS Volta's
97
+ // (<volta>/tools/image/node/<v>/bin/node), so derive the root from execPath, honoring
98
+ // VOLTA_HOME when it is set (custom install locations).
99
+ const voltaMarker = `${sep}.volta${sep}`;
100
+ const voltaRoot =
101
+ process.env.VOLTA_HOME ??
102
+ (process.execPath.includes(voltaMarker)
103
+ ? process.execPath.slice(0, process.execPath.indexOf(voltaMarker) + voltaMarker.length - 1)
104
+ : null);
105
+ const voltaBin = voltaRoot ? join(voltaRoot, "bin") : null;
106
+ const voltaNpm = voltaBin ? join(voltaBin, "npm") : null;
91
107
  const npmCmd = voltaNpm && existsSync(voltaNpm) ? voltaNpm : "npm";
92
108
  // --ignore-scripts: the client has no install hooks, and this keeps a hostile tarball from
93
109
  // running code at install time (the same flag the script installer passes).
94
110
  const r = spawnSync(npmCmd, ["install", "-g", "--ignore-scripts", file], {
95
111
  stdio: "inherit",
112
+ env: childEnv,
96
113
  shell: process.platform === "win32",
97
114
  });
98
115
  try {
@@ -102,9 +119,17 @@ async function main() {
102
119
  fail(`error: npm install -g failed (exit ${r.status ?? "?"}). Fix the npm error above and re-run \`balladeer\`.`, r.status ?? 1);
103
120
  }
104
121
 
105
- // Never report success on npm's exit code alone: prove the world changed. If `balladeer` still
106
- // resolves to this installer (or to nothing), say so honestly instead of claiming victory.
107
- const probe = spawnSync("balladeer", ["--version"], { encoding: "utf8", shell: process.platform === "win32" });
122
+ // Never report success on npm's exit code alone: prove the world changed — AS THE USER'S SHELL
123
+ // SEES IT. Probe the Volta shim by absolute path when Volta is present (this process's PATH has
124
+ // Volta's image dirs prepended, so a bare probe can hit a copy the user's shell never resolves),
125
+ // with the recursion guard scrubbed so the shim re-resolves instead of echoing this bootstrapper.
126
+ const voltaShim = voltaBin ? join(voltaBin, "balladeer") : null;
127
+ const probeCmd = voltaShim && existsSync(voltaShim) ? voltaShim : "balladeer";
128
+ const probe = spawnSync(probeCmd, ["--version"], {
129
+ encoding: "utf8",
130
+ env: childEnv,
131
+ shell: process.platform === "win32",
132
+ });
108
133
  const probedVersion = (probe.stdout ?? "").trim();
109
134
  if (probe.status !== 0 || !/^\d+\.\d+\.\d+/.test(probedVersion)) {
110
135
  fail(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "balladeer",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Installer for the Balladeer client (decision memory for engineering teams and their AI agents): downloads the sha256-verified client from get.balladeer.ai and installs it globally.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://balladeer.ai",