@just-every/code 0.2.48 → 0.2.50
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 -6
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 = async () => {
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@just-every/code",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.50",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
|
|
6
6
|
"bin": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"prettier": "^3.3.3"
|
|
35
35
|
},
|
|
36
36
|
"optionalDependencies": {
|
|
37
|
-
"@just-every/code-darwin-arm64": "0.2.
|
|
38
|
-
"@just-every/code-darwin-x64": "0.2.
|
|
39
|
-
"@just-every/code-linux-x64-musl": "0.2.
|
|
40
|
-
"@just-every/code-linux-arm64-musl": "0.2.
|
|
41
|
-
"@just-every/code-win32-x64": "0.2.
|
|
37
|
+
"@just-every/code-darwin-arm64": "0.2.50",
|
|
38
|
+
"@just-every/code-darwin-x64": "0.2.50",
|
|
39
|
+
"@just-every/code-linux-x64-musl": "0.2.50",
|
|
40
|
+
"@just-every/code-linux-arm64-musl": "0.2.50",
|
|
41
|
+
"@just-every/code-win32-x64": "0.2.50"
|
|
42
42
|
}
|
|
43
43
|
}
|