@chikhamx/voidx 1.0.0 → 1.0.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.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // Lightweight environment check that runs after npm install.
5
+ // Detects Python 3.11+ and prints a hint if missing. Never blocks installation.
6
+
7
+ const { spawnSync } = require("child_process");
8
+
9
+ function main() {
10
+ const probe = detectPython();
11
+ if (probe.ok) {
12
+ console.error(`\n✅ voidx: Python ${probe.versionText} found at "${probe.path}"\n`);
13
+ return;
14
+ }
15
+
16
+ console.error("\n⚠️ voidx requires Python 3.11+, but no compatible Python was found.\n");
17
+ console.error(hint());
18
+ console.error(`\nOnce Python 3.11+ is installed, voidx will bootstrap the rest on first run.\n`);
19
+ }
20
+
21
+ function detectPython() {
22
+ const candidates = [
23
+ "python3", "python",
24
+ "python3.13", "python3.12", "python3.11",
25
+ ];
26
+ if (process.platform === "win32") {
27
+ candidates.push("py");
28
+ }
29
+
30
+ for (const cmd of candidates) {
31
+ const args = process.platform === "win32" && cmd === "py" ? ["-3.11"] : [];
32
+ const result = spawnSync(cmd, [...args, "-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"], {
33
+ encoding: "utf8",
34
+ windowsHide: true,
35
+ });
36
+ if (result.error || result.status !== 0) continue;
37
+ const versionText = (result.stdout || "").trim();
38
+ const match = /^(\d+)\.(\d+)\.\d+/.exec(versionText);
39
+ if (!match) continue;
40
+ const major = Number.parseInt(match[1], 10);
41
+ const minor = Number.parseInt(match[2], 10);
42
+ if (major > 3 || (major === 3 && minor >= 11)) {
43
+ return { ok: true, versionText, path: cmd };
44
+ }
45
+ }
46
+ return { ok: false };
47
+ }
48
+
49
+ function hint() {
50
+ if (process.platform === "darwin") {
51
+ return " Install: brew install python@3.12";
52
+ }
53
+ if (process.platform === "linux") {
54
+ return " Install via your package manager, e.g.:\n" +
55
+ " sudo apt install python3.12 (Debian/Ubuntu)\n" +
56
+ " sudo dnf install python3.12 (Fedora)";
57
+ }
58
+ if (process.platform === "win32") {
59
+ return " Install: https://python.org/downloads";
60
+ }
61
+ return " Install Python 3.11+ from https://python.org/downloads";
62
+ }
63
+
64
+ main();
package/bin/voidx.js CHANGED
@@ -49,6 +49,9 @@ function selectPython(env) {
49
49
  const candidates = [
50
50
  { command: "python3", args: [], label: "python3" },
51
51
  { command: "python", args: [], label: "python" },
52
+ { command: "python3.13", args: [], label: "python3.13" },
53
+ { command: "python3.12", args: [], label: "python3.12" },
54
+ { command: "python3.11", args: [], label: "python3.11" },
52
55
  ];
53
56
  if (process.platform === "win32") {
54
57
  candidates.push({ command: "py", args: ["-3.11"], label: "py -3.11" });
@@ -66,14 +69,33 @@ function selectPython(env) {
66
69
  oldVersions.push(`${probe.versionText} at ${candidate.label}`);
67
70
  }
68
71
 
72
+ const hint = pythonHint();
69
73
  if (oldVersions.length > 0) {
70
- throw new Error(`voidx requires Python 3.11+. Found ${oldVersions.join(", ")}.`);
74
+ throw new Error(
75
+ `voidx requires Python 3.11+. Found ${oldVersions.join(", ")}.\n${hint}`
76
+ );
71
77
  }
72
78
  throw new Error(
73
- "voidx npm launcher requires Python 3.11+. Install Python or set VOIDX_PYTHON."
79
+ `voidx npm launcher requires Python 3.11+. ${hint}`
74
80
  );
75
81
  }
76
82
 
83
+ function pythonHint() {
84
+ if (process.platform === "darwin") {
85
+ return "Install Python 3.11+ via: brew install python@3.12\n" +
86
+ "Or point to an existing install: VOIDX_PYTHON=/path/to/python3 voidx";
87
+ }
88
+ if (process.platform === "linux") {
89
+ return "Install Python 3.11+ via your package manager (apt/dnf).\n" +
90
+ "Or point to an existing install: VOIDX_PYTHON=/path/to/python3 voidx";
91
+ }
92
+ if (process.platform === "win32") {
93
+ return "Install Python 3.11+ from https://python.org/downloads\n" +
94
+ "Or: VOIDX_PYTHON=C:\\Python312\\python.exe voidx";
95
+ }
96
+ return "Install Python 3.11+ or set VOIDX_PYTHON.";
97
+ }
98
+
77
99
  function probePython(candidate) {
78
100
  const code = [
79
101
  "import sys",
@@ -128,39 +150,63 @@ function ensureVenv(python, venvDir, env) {
128
150
 
129
151
  fs.mkdirSync(path.dirname(venvDir), { recursive: true });
130
152
  const venvPython = resolveVenvPython(venvDir);
131
- if (!fs.existsSync(venvPython)) {
132
- debug(env, `Creating npm-managed Python environment at ${venvDir}`);
133
- runChecked(
153
+
154
+ const isFresh = !fs.existsSync(venvPython);
155
+ if (isFresh) {
156
+ console.error(
157
+ "\n⚙️ Setting up voidx environment (this only happens once)...\n"
158
+ );
159
+ const venvResult = spawnSync(
134
160
  python.command,
135
161
  [...python.args, "-m", "venv", venvDir],
136
- "Failed to create the npm-managed Python environment.",
137
- env
162
+ { encoding: "utf8", stdio: "inherit", windowsHide: true }
138
163
  );
164
+ if (venvResult.error) {
165
+ throw new Error(
166
+ `Failed to create the Python virtual environment: ${venvResult.error.message}`
167
+ );
168
+ }
169
+ if (venvResult.status !== 0) {
170
+ throw new Error(
171
+ "Failed to create the Python virtual environment. See errors above."
172
+ );
173
+ }
139
174
  }
140
175
 
141
- debug(env, `Installing ${packageSpec} into ${venvDir}`);
142
- runChecked(
143
- venvPython,
144
- ["-m", "pip", "install", "--upgrade", packageSpec],
145
- `Failed to install ${packageSpec} into the npm-managed Python environment.`,
146
- env
147
- );
148
- fs.writeFileSync(markerPath, marker);
149
- }
150
-
151
- function runChecked(command, args, errorMessage, env) {
152
- const result = spawnSync(command, args, {
153
- encoding: "utf8",
154
- stdio: env.VOIDX_NPM_DEBUG === "1" ? "inherit" : "pipe",
155
- windowsHide: true,
156
- });
157
- if (result.error) {
158
- throw new Error(`${errorMessage} ${result.error.message}`);
159
- }
160
- if (result.status !== 0) {
161
- const stderr = result.stderr ? result.stderr.trim() : "";
162
- throw new Error(stderr ? `${errorMessage} ${stderr}` : errorMessage);
176
+ if (!fs.existsSync(executable) || readFile(markerPath) !== marker) {
177
+ console.error(
178
+ `\n📦 Downloading ${packageSpec} and dependencies… ` +
179
+ "(1–2 minutes on first run)\n"
180
+ );
181
+ const pipEnv = Object.assign({}, env, {
182
+ PIP_NO_INPUT: "1",
183
+ PIP_DISABLE_PIP_VERSION_CHECK: "1",
184
+ PYTHON_KEYRING_BACKEND: "keyring.backends.null.Keyring",
185
+ });
186
+ const result = spawnSync(
187
+ venvPython,
188
+ [
189
+ "-m",
190
+ "pip",
191
+ "install",
192
+ "--upgrade",
193
+ "--progress-bar",
194
+ "on",
195
+ packageSpec,
196
+ ],
197
+ { encoding: "utf8", stdio: "inherit", windowsHide: true, env: pipEnv }
198
+ );
199
+ if (result.error) {
200
+ throw new Error(
201
+ `Failed to install ${packageSpec}: ${result.error.message}`
202
+ );
203
+ }
204
+ if (result.status !== 0) {
205
+ throw new Error(`Failed to install ${packageSpec}. See errors above.`);
206
+ }
163
207
  }
208
+
209
+ fs.writeFileSync(markerPath, marker);
164
210
  }
165
211
 
166
212
  function resolveVenvDir(env) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chikhamx/voidx",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "npm launcher for voidx, a terminal AI coding agent.",
5
5
  "bin": {
6
6
  "voidx": "bin/voidx.js"
@@ -18,6 +18,7 @@
18
18
  "cli"
19
19
  ],
20
20
  "scripts": {
21
- "check": "node --check bin/voidx.js"
21
+ "postinstall": "node bin/postinstall.js",
22
+ "check": "node --check bin/voidx.js && node --check bin/postinstall.js"
22
23
  }
23
24
  }