@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 +190 -3
- package/package.json +6 -7
- package/postinstall.js +38 -7
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
39
|
-
"@just-every/code-darwin-x64": "0.2.
|
|
40
|
-
"@just-every/code-linux-x64-musl": "0.2.
|
|
41
|
-
"@just-every/code-linux-arm64-musl": "0.2.
|
|
42
|
-
"@just-every/code-win32-x64": "0.2.
|
|
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
|
-
//
|
|
412
|
-
//
|
|
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 (
|
|
464
|
-
|
|
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
|
|
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;
|
|
534
|
-
if (globalBin
|
|
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
|