@chikhamx/voidx 2.1.0 → 2.1.1

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.
@@ -61,7 +61,8 @@ function resolvePythonDir(env) {
61
61
 
62
62
  function resolveVenvDir(env) {
63
63
  if (env.VOIDX_NPM_VENV) return path.resolve(env.VOIDX_NPM_VENV);
64
- return path.join(resolveDataHome(env), "voidx", "npm-venv");
64
+ // Use the same venv directory as install.sh — single environment, no duplicates
65
+ return path.join(resolveDataHome(env), "voidx", "venv");
65
66
  }
66
67
 
67
68
  function resolveVenvPython(venvDir) {
@@ -87,6 +88,77 @@ function resolveBundledPython(pythonDir, platform) {
87
88
  return path.join(installDir, "bin", "python3");
88
89
  }
89
90
 
91
+ // ── Legacy cleanup ──────────────────────────────────────────────────────────
92
+ // Remove voidx installed via system Python (pip/pipx) from v1.x era,
93
+ // and old npm-venv directory from v2.x early releases.
94
+ function cleanupLegacy(env) {
95
+ const isWin = process.platform === "win32";
96
+
97
+ // pip
98
+ for (const cmd of isWin ? ["pip"] : ["pip3", "pip"]) {
99
+ try {
100
+ const result = spawnSync(cmd, ["show", "voidx"], {
101
+ encoding: "utf8",
102
+ windowsHide: true,
103
+ });
104
+ if (!result.error && result.status === 0 && (result.stdout || "").includes("Name: voidx")) {
105
+ const versionMatch = (result.stdout || "").match(/Version:\s*(\S+)/);
106
+ const version = versionMatch ? versionMatch[1] : "unknown";
107
+ console.error(` ⚠️ Found pip-installed voidx ${version} (${cmd}), uninstalling…`);
108
+ const uninstallResult = spawnSync(cmd, ["uninstall", "voidx", "-y"], {
109
+ encoding: "utf8",
110
+ windowsHide: true,
111
+ stdio: "inherit",
112
+ });
113
+ if (uninstallResult.status === 0) {
114
+ console.error(" ✅ Uninstalled pip-installed voidx");
115
+ } else {
116
+ console.error(` ⚠️ Failed to uninstall voidx via ${cmd}. Run manually: ${cmd} uninstall voidx`);
117
+ }
118
+ }
119
+ } catch (err) {
120
+ // spawnSync sets result.error for ENOENT (handled by status check above);
121
+ // this catch only handles truly unexpected synchronous throws.
122
+ console.error(` ⚠️ Failed to check ${cmd}: ${err.message}`);
123
+ }
124
+ }
125
+
126
+ // pipx
127
+ try {
128
+ const result = spawnSync("pipx", ["list"], { encoding: "utf8", windowsHide: true });
129
+ if (!result.error && result.status === 0 && /^voidx\b/m.test(result.stdout || "")) {
130
+ console.error(" ⚠️ Found pipx-installed voidx, uninstalling…");
131
+ const uninstallResult = spawnSync("pipx", ["uninstall", "voidx"], {
132
+ encoding: "utf8",
133
+ windowsHide: true,
134
+ stdio: "inherit",
135
+ });
136
+ if (uninstallResult.status === 0) {
137
+ console.error(" ✅ Uninstalled pipx-installed voidx");
138
+ } else {
139
+ console.error(" ⚠️ Failed to uninstall voidx via pipx. Run manually: pipx uninstall voidx");
140
+ }
141
+ }
142
+ } catch (err) {
143
+ // spawnSync sets result.error for ENOENT (handled by status check above);
144
+ // this catch only handles truly unexpected synchronous throws.
145
+ console.error(` ⚠️ Failed to check pipx: ${err.message}`);
146
+ }
147
+
148
+ // Old npm-venv directory
149
+ const dataHome = resolveDataHome(env);
150
+ const oldNpmVenv = path.join(dataHome, "voidx", "npm-venv");
151
+ if (existsSync(oldNpmVenv)) {
152
+ console.error(" ⚠️ Found old npm-venv directory, removing…");
153
+ try {
154
+ rmSync(oldNpmVenv, { recursive: true, force: true });
155
+ console.error(" ✅ Removed old npm-venv directory");
156
+ } catch (err) {
157
+ console.error(` Failed to remove old npm-venv: ${err.message}`);
158
+ }
159
+ }
160
+ }
161
+
90
162
  // ── Download with retry ────────────────────────────────────────────────────
91
163
  const MAX_DOWNLOAD_RETRIES = 3;
92
164
 
@@ -239,8 +311,14 @@ async function main() {
239
311
  const venvPython = resolveVenvPython(venvDir);
240
312
  const executable = resolveVoidxExecutable(venvDir);
241
313
  const packageSpec = env.VOIDX_NPM_PACKAGE_SPEC || `voidx==${pkg.version}`;
242
- const markerPath = path.join(venvDir, ".voidx-npm-version");
243
- const marker = `${pkg.version}\n${packageSpec}\n${PBS_TAG}\n${PBS_CPYTHON}\n`;
314
+ const markerPath = path.join(venvDir, ".voidx-install-version");
315
+ const marker = `${pkg.version}\n${PBS_TAG}\n${PBS_CPYTHON}\n`;
316
+
317
+ // ── Legacy cleanup ────────────────────────────────────────────────────────
318
+ // Remove voidx installed via system Python (pip/pipx) from v1.x era.
319
+ // Also remove old npm-venv directory from v2.x early releases.
320
+ // Runs before marker check so legacy cleanup happens even when already installed.
321
+ cleanupLegacy(env);
244
322
 
245
323
  // Already set up and up-to-date?
246
324
  if (existsSync(executable) && readMarker(markerPath) === marker) {
package/bin/voidx.js CHANGED
@@ -11,6 +11,7 @@ const pkg = require("../package.json");
11
11
  // ── Configuration ──────────────────────────────────────────────────────────
12
12
 
13
13
  const PBS_TAG = "20260602";
14
+ const PBS_CPYTHON = "3.12.13";
14
15
  const PBS_PYTHON_MAJOR = "3.12";
15
16
 
16
17
  // ── Main ───────────────────────────────────────────────────────────────────
@@ -56,7 +57,7 @@ function selectPython(env) {
56
57
  return candidate;
57
58
  }
58
59
 
59
- // 2. Bundled Python (preferred)
60
+ // 2. Bundled Python only — voidx runs in its own isolated environment
60
61
  const bundledBin = resolveBundledPythonBin(env);
61
62
  if (bundledBin && fs.existsSync(bundledBin)) {
62
63
  const candidate = { command: bundledBin, args: [], label: "bundled" };
@@ -66,41 +67,34 @@ function selectPython(env) {
66
67
  }
67
68
  }
68
69
 
69
- // 3. System Python fallback (for upgrades from v1.x where bundled Python was not used)
70
- const candidates = [
71
- { command: "python3", args: [], label: "python3" },
72
- { command: "python", args: [], label: "python" },
73
- { command: "python3.13", args: [], label: "python3.13" },
74
- { command: "python3.12", args: [], label: "python3.12" },
75
- { command: "python3.11", args: [], label: "python3.11" },
76
- ];
77
- if (process.platform === "win32") {
78
- candidates.push({ command: "py", args: ["-3"], label: "py -3" });
70
+ // 3. Bundled Python not found try to bootstrap it (postinstall may have failed)
71
+ console.error("\n⚙️ Bundled Python not found, running setup…\n");
72
+ const postinstallScript = path.join(path.dirname(__filename), "postinstall.js");
73
+ if (fs.existsSync(postinstallScript)) {
74
+ const result = spawnSync(process.execPath, [postinstallScript], {
75
+ stdio: "inherit",
76
+ windowsHide: true,
77
+ env: { ...env },
78
+ });
79
+ if (result.status !== 0) {
80
+ console.error(" Setup failed. Try reinstalling:");
81
+ console.error(" npm install -g @chikhamx/voidx");
82
+ }
79
83
  }
80
84
 
81
- const oldVersions = [];
82
- for (const candidate of candidates) {
85
+ // Retry after bootstrap
86
+ if (bundledBin && fs.existsSync(bundledBin)) {
87
+ const candidate = { command: bundledBin, args: [], label: "bundled" };
83
88
  const probe = probePython(candidate);
84
- if (!probe.ok) {
85
- continue;
86
- }
87
- if (isCompatible(probe.version)) {
89
+ if (probe.ok && isCompatible(probe.version)) {
88
90
  return candidate;
89
91
  }
90
- oldVersions.push(`${probe.versionText} at ${candidate.label}`);
91
92
  }
92
93
 
93
- if (oldVersions.length > 0) {
94
- throw new Error(
95
- `voidx requires Python 3.11+. Found ${oldVersions.join(", ")}.\n` +
96
- "Install Python 3.11+ or reinstall the npm package to get the bundled runtime:\n" +
97
- " npm install -g @chikhamx/voidx"
98
- );
99
- }
100
94
  throw new Error(
101
- "voidx requires Python 3.11+. No Python found.\n" +
102
- "Install Python 3.11+ or reinstall the npm package to get the bundled runtime:\n" +
103
- " npm install -g @chikhamx/voidx"
95
+ "voidx bundled Python not found. Reinstall to set up the isolated runtime:\n" +
96
+ " npm install -g @chikhamx/voidx\n" +
97
+ " or: curl -fsSL https://raw.githubusercontent.com/chikhamx/voidx/master/scripts/install.sh | bash"
104
98
  );
105
99
  }
106
100
 
@@ -162,14 +156,15 @@ function resolveBundledPythonBin(env) {
162
156
  const pythonDir = resolvePythonDir(env);
163
157
  return process.platform === "win32"
164
158
  ? path.join(pythonDir, "python", "python.exe")
165
- : path.join(pythonDir, "python", "bin", `python${PBS_PYTHON_MAJOR}`);
159
+ : path.join(pythonDir, "python", "bin", "python3");
166
160
  }
167
161
 
168
162
  function resolveVenvDir(env) {
169
163
  if (env.VOIDX_NPM_VENV) {
170
164
  return path.resolve(env.VOIDX_NPM_VENV);
171
165
  }
172
- return path.join(resolveDataHome(env), "voidx", "npm-venv");
166
+ // Use the same venv directory as install.sh — single environment, no duplicates
167
+ return path.join(resolveDataHome(env), "voidx", "venv");
173
168
  }
174
169
 
175
170
  function resolveVenvPython(venvDir) {
@@ -195,12 +190,10 @@ function ensureVenv(python, venvDir, env) {
195
190
  return;
196
191
  }
197
192
 
198
- const markerPath = path.join(venvDir, ".voidx-npm-version");
199
- const packageSpec = env.VOIDX_NPM_PACKAGE_SPEC || `voidx==${pkg.version}`;
200
- // Marker must match postinstall.js format (includes PBS_TAG + PBS_CPYTHON)
201
- const marker = `${pkg.version}\n${packageSpec}\n${PBS_TAG}\n3.12.13\n`;
193
+ const markerPath = path.join(venvDir, ".voidx-install-version");
194
+ const marker = `${pkg.version}\n${PBS_TAG}\n${PBS_CPYTHON}\n`;
202
195
  if (fs.existsSync(executable) && readFile(markerPath) === marker) {
203
- debug(env, `Using cached npm-managed environment at ${venvDir}`);
196
+ debug(env, `Using cached environment at ${venvDir}`);
204
197
  return;
205
198
  }
206
199
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chikhamx/voidx",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "npm launcher for voidx, a terminal AI coding agent.",
5
5
  "bin": {
6
6
  "voidx": "bin/voidx.js"