@just-every/code 0.2.47 → 0.2.49

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/bin/coder.js CHANGED
@@ -3,6 +3,9 @@
3
3
 
4
4
  import path from "path";
5
5
  import { fileURLToPath } from "url";
6
+ import { platform as nodePlatform, arch as nodeArch } from "os";
7
+ import { execSync } from "child_process";
8
+ import { get as httpsGet } from "https";
6
9
 
7
10
  // __dirname equivalent in ESM
8
11
  const __filename = fileURLToPath(import.meta.url);
@@ -76,12 +79,196 @@ if (!targetTriple) {
76
79
 
77
80
  // Prefer new 'code-*' binary names; fall back to legacy 'coder-*' if missing.
78
81
  let binaryPath = path.join(__dirname, "..", "bin", `code-${targetTriple}`);
79
- if (!existsSync(binaryPath)) {
80
- binaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
82
+ let legacyBinaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
83
+
84
+ // --- Bootstrap helper (runs if the binary is missing, e.g. Bun blocked postinstall) ---
85
+ import { existsSync, chmodSync, statSync, openSync, readSync, closeSync, mkdirSync, copyFileSync, readFileSync, unlinkSync } from "fs";
86
+
87
+ const validateBinary = (p) => {
88
+ try {
89
+ const st = statSync(p);
90
+ if (!st.isFile() || st.size === 0) {
91
+ return { ok: false, reason: "empty or not a regular file" };
92
+ }
93
+ const fd = openSync(p, "r");
94
+ try {
95
+ const buf = Buffer.alloc(4);
96
+ const n = readSync(fd, buf, 0, 4, 0);
97
+ if (n < 2) return { ok: false, reason: "too short" };
98
+ if (platform === "win32") {
99
+ if (!(buf[0] === 0x4d && buf[1] === 0x5a)) return { ok: false, reason: "invalid PE header (missing MZ)" };
100
+ } else if (platform === "linux" || platform === "android") {
101
+ if (!(buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)) return { ok: false, reason: "invalid ELF header" };
102
+ } else if (platform === "darwin") {
103
+ const isMachO = (buf[0] === 0xcf && buf[1] === 0xfa && buf[2] === 0xed && buf[3] === 0xfe) ||
104
+ (buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe);
105
+ if (!isMachO) return { ok: false, reason: "invalid Mach-O header" };
106
+ }
107
+ } finally {
108
+ closeSync(fd);
109
+ }
110
+ return { ok: true };
111
+ } catch (e) {
112
+ return { ok: false, reason: e.message };
113
+ }
114
+ };
115
+
116
+ const getCacheDir = (version) => {
117
+ const plt = nodePlatform();
118
+ const home = process.env.HOME || process.env.USERPROFILE || "";
119
+ let base = "";
120
+ if (plt === "win32") {
121
+ base = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
122
+ } else if (plt === "darwin") {
123
+ base = path.join(home, "Library", "Caches");
124
+ } else {
125
+ base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
126
+ }
127
+ const dir = path.join(base, "just-every", "code", version);
128
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
129
+ return dir;
130
+ };
131
+
132
+ const getCachedBinaryPath = (version) => {
133
+ const isWin = nodePlatform() === "win32";
134
+ const ext = isWin ? ".exe" : "";
135
+ const cacheDir = getCacheDir(version);
136
+ return path.join(cacheDir, `code-${targetTriple}${ext}`);
137
+ };
138
+
139
+ const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
140
+ const req = httpsGet(url, (res) => {
141
+ const status = res.statusCode || 0;
142
+ if (status >= 300 && status < 400 && res.headers.location) {
143
+ // follow one redirect recursively
144
+ return resolve(httpsDownload(res.headers.location, dest));
145
+ }
146
+ if (status !== 200) {
147
+ return reject(new Error(`HTTP ${status}`));
148
+ }
149
+ const out = require("fs").createWriteStream(dest);
150
+ res.pipe(out);
151
+ out.on("finish", () => out.close(resolve));
152
+ out.on("error", (e) => {
153
+ try { unlinkSync(dest); } catch {}
154
+ reject(e);
155
+ });
156
+ });
157
+ req.on("error", (e) => {
158
+ try { unlinkSync(dest); } catch {}
159
+ reject(e);
160
+ });
161
+ req.setTimeout(120000, () => {
162
+ req.destroy(new Error("download timed out"));
163
+ });
164
+ });
165
+
166
+ const tryBootstrapBinary = () => {
167
+ try {
168
+ // 1) Read our published version
169
+ const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
170
+ const version = pkg.version;
171
+
172
+ const binDir = path.join(__dirname, "..", "bin");
173
+ if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
174
+
175
+ // 2) Fast path: user cache
176
+ const cachePath = getCachedBinaryPath(version);
177
+ if (existsSync(cachePath)) {
178
+ const v = validateBinary(cachePath);
179
+ if (v.ok) {
180
+ copyFileSync(cachePath, binaryPath);
181
+ if (platform !== "win32") chmodSync(binaryPath, 0o755);
182
+ return existsSync(binaryPath);
183
+ }
184
+ }
185
+
186
+ // 3) Try platform package (if present)
187
+ try {
188
+ const req = (await import("module")).createRequire(import.meta.url);
189
+ const name = (() => {
190
+ if (platform === "win32") return "@just-every/code-win32-x64"; // may be unpublished; falls through
191
+ const plt = nodePlatform();
192
+ const cpu = nodeArch();
193
+ if (plt === "darwin" && cpu === "arm64") return "@just-every/code-darwin-arm64";
194
+ if (plt === "darwin" && cpu === "x64") return "@just-every/code-darwin-x64";
195
+ if (plt === "linux" && cpu === "x64") return "@just-every/code-linux-x64-musl";
196
+ if (plt === "linux" && cpu === "arm64") return "@just-every/code-linux-arm64-musl";
197
+ return null;
198
+ })();
199
+ if (name) {
200
+ try {
201
+ const pkgJson = req.resolve(`${name}/package.json`);
202
+ const pkgDir = path.dirname(pkgJson);
203
+ const src = path.join(pkgDir, "bin", `code-${targetTriple}${platform === "win32" ? ".exe" : ""}`);
204
+ if (existsSync(src)) {
205
+ copyFileSync(src, binaryPath);
206
+ if (platform !== "win32") chmodSync(binaryPath, 0o755);
207
+ // refresh cache
208
+ try { copyFileSync(binaryPath, cachePath); } catch {}
209
+ return existsSync(binaryPath);
210
+ }
211
+ } catch { /* ignore and fall back */ }
212
+ }
213
+ } catch { /* ignore */ }
214
+
215
+ // 4) Download from GitHub release
216
+ const isWin = platform === "win32";
217
+ const archiveName = isWin
218
+ ? `code-${targetTriple}.zip`
219
+ : (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return `code-${targetTriple}.zst`; } catch { return `code-${targetTriple}.tar.gz`; } })();
220
+ const url = `https://github.com/just-every/code/releases/download/v${version}/${archiveName}`;
221
+ const tmp = path.join(binDir, `.${archiveName}.part`);
222
+ return httpsDownload(url, tmp)
223
+ .then(() => {
224
+ if (isWin) {
225
+ try {
226
+ const ps = `powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmp}' -DestinationPath '${binDir}' -Force"`;
227
+ execSync(ps, { stdio: "ignore" });
228
+ } catch (e) {
229
+ throw new Error(`failed to unzip: ${e.message}`);
230
+ } finally { try { unlinkSync(tmp); } catch {} }
231
+ } else {
232
+ if (archiveName.endsWith(".zst")) {
233
+ try { execSync(`zstd -d '${tmp}' -o '${binaryPath}'`, { stdio: 'ignore', shell: true }); }
234
+ catch (e) { try { unlinkSync(tmp); } catch {}; throw new Error(`failed to decompress zst: ${e.message}`); }
235
+ try { unlinkSync(tmp); } catch {}
236
+ } else {
237
+ try { execSync(`tar -xzf '${tmp}' -C '${binDir}'`, { stdio: 'ignore', shell: true }); }
238
+ catch (e) { try { unlinkSync(tmp); } catch {}; throw new Error(`failed to extract tar.gz: ${e.message}`); }
239
+ try { unlinkSync(tmp); } catch {}
240
+ }
241
+ }
242
+ const v = validateBinary(binaryPath);
243
+ if (!v.ok) throw new Error(`invalid binary (${v.reason})`);
244
+ if (platform !== "win32") chmodSync(binaryPath, 0o755);
245
+ try { copyFileSync(binaryPath, cachePath); } catch {}
246
+ return true;
247
+ })
248
+ .catch((_e) => false);
249
+ } catch {
250
+ return false;
251
+ }
252
+ };
253
+
254
+ // If missing, attempt to bootstrap into place (helps when Bun blocks postinstall)
255
+ if (!existsSync(binaryPath) && !existsSync(legacyBinaryPath)) {
256
+ const ok = await tryBootstrapBinary();
257
+ if (!ok) {
258
+ // retry legacy name in case archive provided coder-*
259
+ if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
260
+ binaryPath = legacyBinaryPath;
261
+ }
262
+ }
263
+ }
264
+
265
+ // Fall back to legacy name if primary is still missing
266
+ if (!existsSync(binaryPath) && existsSync(legacyBinaryPath)) {
267
+ binaryPath = legacyBinaryPath;
81
268
  }
82
269
 
83
270
  // Check if binary exists and try to fix permissions if needed
84
- import { existsSync, chmodSync, statSync, openSync, readSync, closeSync } from "fs";
271
+ // fs imports are above; keep for readability if tree-shaken by bundlers
85
272
  import { spawnSync } from "child_process";
86
273
  if (existsSync(binaryPath)) {
87
274
  try {
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@just-every/code",
3
- "version": "0.2.47",
3
+ "version": "0.2.49",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
6
6
  "bin": {
7
- "code": "bin/coder.js",
8
7
  "coder": "bin/coder.js"
9
8
  },
10
9
  "type": "module",
@@ -35,10 +34,10 @@
35
34
  "prettier": "^3.3.3"
36
35
  },
37
36
  "optionalDependencies": {
38
- "@just-every/code-darwin-arm64": "0.2.47",
39
- "@just-every/code-darwin-x64": "0.2.47",
40
- "@just-every/code-linux-x64-musl": "0.2.47",
41
- "@just-every/code-linux-arm64-musl": "0.2.47",
42
- "@just-every/code-win32-x64": "0.2.47"
37
+ "@just-every/code-darwin-arm64": "0.2.49",
38
+ "@just-every/code-darwin-x64": "0.2.49",
39
+ "@just-every/code-linux-x64-musl": "0.2.49",
40
+ "@just-every/code-linux-arm64-musl": "0.2.49",
41
+ "@just-every/code-win32-x64": "0.2.49"
43
42
  }
44
43
  }
package/postinstall.js CHANGED
@@ -408,8 +408,12 @@ async function main() {
408
408
  console.warn('⚠ Main code binary not found. You may need to build from source.');
409
409
  }
410
410
 
411
- // With bin name = 'code', handle collisions (e.g., VS Code) and add legacy wrappers
412
- // Only do this for global installs; skip under npx/local to avoid extra work and collisions.
411
+ // Handle collisions (e.g., VS Code) and add wrappers. We no longer publish a
412
+ // `code` bin in package.json. Instead, for global installs we create a `code`
413
+ // wrapper only when there is no conflicting `code` earlier on PATH. This avoids
414
+ // hijacking the VS Code CLI while still giving users a friendly name when safe.
415
+ // For upgrades from older versions that published a `code` bin, we also remove
416
+ // our old shim if a conflict is detected.
413
417
  if (isGlobal && !isNpx) try {
414
418
  const isTTY = process.stdout && process.stdout.isTTY;
415
419
  const isWindows = platform() === 'win32';
@@ -460,8 +464,23 @@ async function main() {
460
464
  } catch (e) {
461
465
  console.log(`⚠ Could not remove Bun shim '${bunShim}': ${e.message}`);
462
466
  }
463
- } else if (existsSync(bunShim)) {
464
- installedCmds.add('code');
467
+ } else if (!other) {
468
+ // No conflict: create a wrapper that forwards to `coder`
469
+ try {
470
+ const wrapperPath = bunShim;
471
+ if (isWindows) {
472
+ const content = `@echo off\r\n"%~dp0coder" %*\r\n`;
473
+ writeFileSync(wrapperPath, content);
474
+ } else {
475
+ const content = `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
476
+ writeFileSync(wrapperPath, content);
477
+ chmodSync(wrapperPath, 0o755);
478
+ }
479
+ console.log("✓ Created 'code' wrapper -> coder (bun)");
480
+ installedCmds.add('code');
481
+ } catch (e) {
482
+ console.log(`⚠ Failed to create 'code' wrapper (bun): ${e.message}`);
483
+ }
465
484
  }
466
485
 
467
486
  // Print summary for Bun
@@ -486,7 +505,7 @@ async function main() {
486
505
 
487
506
  const ourShim = join(globalBin || '', isWindows ? 'code.cmd' : 'code');
488
507
  const candidates = resolveAllOnPath();
489
- const others = candidates.filter(p => p && ourShim && p !== ourShim);
508
+ const others = candidates.filter(p => p && (!ourShim || p !== ourShim));
490
509
  const collision = others.length > 0;
491
510
 
492
511
  const ensureWrapper = (name, args) => {
@@ -530,8 +549,20 @@ async function main() {
530
549
  console.log('Note: could not determine npm global bin; skipping alias creation.');
531
550
  }
532
551
  } else {
533
- // No collision; npm created a 'code' shim for us.
534
- if (globalBin && existsSync(ourShim)) installedCmds.add('code');
552
+ // No collision; ensure a 'code' wrapper exists forwarding to 'coder'
553
+ if (globalBin) {
554
+ try {
555
+ const content = isWindows
556
+ ? `@echo off\r\n"%~dp0coder" %*\r\n`
557
+ : `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
558
+ writeFileSync(ourShim, content);
559
+ if (!isWindows) chmodSync(ourShim, 0o755);
560
+ console.log("✓ Created 'code' wrapper -> coder");
561
+ installedCmds.add('code');
562
+ } catch (e) {
563
+ console.log(`⚠ Failed to create 'code' wrapper: ${e.message}`);
564
+ }
565
+ }
535
566
  }
536
567
 
537
568
  // Print summary for npm/pnpm/yarn