@just-every/code 0.2.119 → 0.2.121

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
@@ -20,11 +20,7 @@ const { platform, arch } = process;
20
20
  const isWSL = () => {
21
21
  if (platform !== "linux") return false;
22
22
  try {
23
- const os = require("os");
24
- const rel = os.release().toLowerCase();
25
- if (rel.includes("microsoft")) return true;
26
- const fs = require("fs");
27
- const txt = fs.readFileSync("/proc/version", "utf8").toLowerCase();
23
+ const txt = readFileSync("/proc/version", "utf8").toLowerCase();
28
24
  return txt.includes("microsoft");
29
25
  } catch {
30
26
  return false;
@@ -82,7 +78,7 @@ let binaryPath = path.join(__dirname, "..", "bin", `code-${targetTriple}`);
82
78
  let legacyBinaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
83
79
 
84
80
  // --- 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";
81
+ import { existsSync, chmodSync, statSync, openSync, readSync, closeSync, mkdirSync, copyFileSync, readFileSync, unlinkSync, createWriteStream } from "fs";
86
82
 
87
83
  const validateBinary = (p) => {
88
84
  try {
@@ -146,7 +142,7 @@ const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
146
142
  if (status !== 200) {
147
143
  return reject(new Error(`HTTP ${status}`));
148
144
  }
149
- const out = require("fs").createWriteStream(dest);
145
+ const out = createWriteStream(dest);
150
146
  res.pipe(out);
151
147
  out.on("finish", () => out.close(resolve));
152
148
  out.on("error", (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@just-every/code",
3
- "version": "0.2.119",
3
+ "version": "0.2.121",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
6
6
  "bin": {
@@ -14,9 +14,12 @@
14
14
  "bin/coder.js",
15
15
  "bin/codex.js",
16
16
  "postinstall.js",
17
+ "scripts/preinstall.js",
18
+ "scripts/windows-cleanup.ps1",
17
19
  "dist"
18
20
  ],
19
21
  "scripts": {
22
+ "preinstall": "node scripts/preinstall.js",
20
23
  "postinstall": "node postinstall.js",
21
24
  "prepublishOnly": "node -e \"const fs=require('fs'),path=require('path'); const repoGit=path.join(__dirname,'..','.git'); const inCi=process.env.GITHUB_ACTIONS==='true'||process.env.CI==='true'; if(fs.existsSync(repoGit) && !inCi){ console.error('Refusing to publish from codex-cli. Use codex-cli/scripts/stage_release.sh to stage a release.'); process.exit(1);} else { console.log(inCi ? 'CI publish detected.' : 'Publishing staged package...'); }\""
22
25
  },
@@ -32,10 +35,10 @@
32
35
  "prettier": "^3.3.3"
33
36
  },
34
37
  "optionalDependencies": {
35
- "@just-every/code-darwin-arm64": "0.2.119",
36
- "@just-every/code-darwin-x64": "0.2.119",
37
- "@just-every/code-linux-x64-musl": "0.2.119",
38
- "@just-every/code-linux-arm64-musl": "0.2.119",
39
- "@just-every/code-win32-x64": "0.2.119"
38
+ "@just-every/code-darwin-arm64": "0.2.121",
39
+ "@just-every/code-darwin-x64": "0.2.121",
40
+ "@just-every/code-linux-x64-musl": "0.2.121",
41
+ "@just-every/code-linux-arm64-musl": "0.2.121",
42
+ "@just-every/code-win32-x64": "0.2.121"
40
43
  }
41
44
  }
package/postinstall.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // Non-functional change to trigger release workflow
3
3
 
4
- import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync } from 'fs';
4
+ import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
5
5
  import { join, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { get } from 'https';
8
- import { platform, arch } from 'os';
8
+ import { platform, arch, tmpdir } from 'os';
9
9
  import { execSync } from 'child_process';
10
10
  import { createRequire } from 'module';
11
11
 
@@ -54,6 +54,56 @@ function getCachedBinaryPath(version, targetTriple, isWindows) {
54
54
  return join(cacheDir, `code-${targetTriple}${ext}`);
55
55
  }
56
56
 
57
+ function isWSL() {
58
+ if (platform() !== 'linux') return false;
59
+ try {
60
+ const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
61
+ return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
62
+ } catch { return false; }
63
+ }
64
+
65
+ function isPathOnWindowsFs(p) {
66
+ try {
67
+ const mounts = readFileSync('/proc/mounts', 'utf8').split(/\n/).filter(Boolean);
68
+ let best = { mount: '/', type: 'unknown', len: 1 };
69
+ for (const line of mounts) {
70
+ const parts = line.split(' ');
71
+ if (parts.length < 3) continue;
72
+ const mnt = parts[1];
73
+ const typ = parts[2];
74
+ if (p.startsWith(mnt) && mnt.length > best.len) best = { mount: mnt, type: typ, len: mnt.length };
75
+ }
76
+ return best.type === 'drvfs' || best.type === 'cifs';
77
+ } catch { return false; }
78
+ }
79
+
80
+ async function writeCacheAtomic(srcPath, cachePath) {
81
+ try {
82
+ if (existsSync(cachePath)) {
83
+ const ok = validateDownloadedBinary(cachePath).ok;
84
+ if (ok) return;
85
+ }
86
+ } catch {}
87
+ const dir = dirname(cachePath);
88
+ if (!existsSync(dir)) { try { mkdirSync(dir, { recursive: true }); } catch {} }
89
+ const tmp = cachePath + '.tmp-' + Math.random().toString(36).slice(2, 8);
90
+ copyFileSync(srcPath, tmp);
91
+ try { const fd = openSync(tmp, 'r'); try { fsyncSync(fd); } finally { closeSync(fd); } } catch {}
92
+ // Retry with exponential backoff up to ~1.6s total
93
+ const delays = [100, 200, 400, 800, 1200, 1600];
94
+ for (let i = 0; i < delays.length; i++) {
95
+ try {
96
+ if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
97
+ renameSync(tmp, cachePath);
98
+ return;
99
+ } catch {
100
+ await new Promise(r => setTimeout(r, delays[i]));
101
+ }
102
+ }
103
+ if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
104
+ renameSync(tmp, cachePath);
105
+ }
106
+
57
107
  async function downloadBinary(url, dest, maxRedirects = 5, maxRetries = 3) {
58
108
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
59
109
 
@@ -238,8 +288,11 @@ async function main() {
238
288
  if (existsSync(cachePath)) {
239
289
  const valid = validateDownloadedBinary(cachePath);
240
290
  if (valid.ok) {
241
- if (!isWindows) {
242
- // Mirror into local bin for Unix-like platforms
291
+ // Avoid mirroring into node_modules on Windows or WSL-on-NTFS.
292
+ const wsl = isWSL();
293
+ const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
294
+ const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
295
+ if (mirrorToLocal) {
243
296
  copyFileSync(cachePath, localPath);
244
297
  try { chmodSync(localPath, 0o755); } catch {}
245
298
  }
@@ -281,10 +334,13 @@ async function main() {
281
334
  if (!existsSync(src)) {
282
335
  throw new Error(`platform package missing binary: ${platformPkg.name}`);
283
336
  }
284
- // Populate cache first (canonical location)
285
- copyFileSync(src, cachePath);
286
- if (!isWindows) {
287
- // Mirror into local bin for Unix-like platforms only
337
+ // Populate cache first (canonical location) atomically
338
+ await writeCacheAtomic(src, cachePath);
339
+ // Mirror into local bin only on Unix-like filesystems (not Windows/WSL-on-NTFS)
340
+ const wsl = isWSL();
341
+ const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
342
+ const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
343
+ if (mirrorToLocal) {
288
344
  copyFileSync(cachePath, localPath);
289
345
  try { chmodSync(localPath, 0o755); } catch {}
290
346
  }
@@ -299,6 +355,15 @@ async function main() {
299
355
  // - Windows: .zip
300
356
  // - macOS/Linux: prefer .zst if `zstd` CLI is available; otherwise use .tar.gz
301
357
  const isWin = isWindows;
358
+ const isWSL = (() => {
359
+ if (platform() !== 'linux') return false;
360
+ try {
361
+ const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
362
+ return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
363
+ } catch { return false; }
364
+ })();
365
+ const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
366
+ const mirrorToLocal = !(isWin || (isWSL && isPathOnWindowsFs(binDirReal)));
302
367
  let useZst = false;
303
368
  if (!isWin) {
304
369
  try {
@@ -313,24 +378,57 @@ async function main() {
313
378
 
314
379
  console.log(`Downloading ${archiveName}...`);
315
380
  try {
316
- const tmpPath = join(binDir, `.${archiveName}.part`);
381
+ const needsIsolation = isWin || (!isWin && !mirrorToLocal); // Windows or WSL-on-NTFS
382
+ let safeTempDir = needsIsolation ? join(tmpdir(), 'just-every', 'code', version) : binDir;
383
+ // Ensure staging dir exists; if tmp fails (permissions/space), fall back to user cache.
384
+ if (needsIsolation) {
385
+ try {
386
+ if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
387
+ } catch {
388
+ try {
389
+ safeTempDir = getCacheDir(version);
390
+ if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
391
+ } catch {}
392
+ }
393
+ }
394
+ const tmpPath = join(needsIsolation ? safeTempDir : binDir, `.${archiveName}.part`);
317
395
  await downloadBinary(downloadUrl, tmpPath);
318
396
 
319
397
  if (isWin) {
320
- // Unzip the single-file archive using PowerShell (built-in)
398
+ // Unzip to a temp directory, then move into the per-user cache.
399
+ const unzipDest = safeTempDir;
321
400
  try {
322
- const psCmd = `powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmpPath}' -DestinationPath '${binDir}' -Force"`;
323
- execSync(psCmd, { stdio: 'ignore' });
401
+ const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
402
+ const psFull = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
403
+ const psCmd = `Expand-Archive -Path '${tmpPath}' -DestinationPath '${unzipDest}' -Force`;
404
+ let ok = false;
405
+ // Attempt full-path powershell.exe
406
+ try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
407
+ // Fallback to powershell in PATH
408
+ if (!ok) { try { execSync(`powershell -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
409
+ // Fallback to pwsh (PowerShell 7)
410
+ if (!ok) { try { execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
411
+ // Final fallback: bsdtar can extract .zip
412
+ if (!ok) { execSync(`tar -xf "${tmpPath}" -C "${unzipDest}"`, { stdio: 'ignore', shell: true }); }
324
413
  } catch (e) {
325
414
  throw new Error(`failed to unzip archive: ${e.message}`);
326
415
  } finally {
327
416
  try { unlinkSync(tmpPath); } catch {}
328
417
  }
418
+ // Move the extracted file from temp to cache; do not leave a copy in node_modules
419
+ try {
420
+ const extractedPath = join(unzipDest, binaryName);
421
+ await writeCacheAtomic(extractedPath, cachePath);
422
+ try { unlinkSync(extractedPath); } catch {}
423
+ } catch (e) {
424
+ throw new Error(`failed to move binary to cache: ${e.message}`);
425
+ }
329
426
  } else {
330
427
  if (useZst) {
331
428
  // Decompress .zst via system zstd
332
429
  try {
333
- execSync(`zstd -d '${tmpPath}' -o '${localPath}'`, { stdio: 'ignore', shell: true });
430
+ const outPath = mirrorToLocal ? localPath : join(safeTempDir, binaryName);
431
+ execSync(`zstd -d '${tmpPath}' -o '${outPath}'`, { stdio: 'ignore', shell: true });
334
432
  } catch (e) {
335
433
  try { unlinkSync(tmpPath); } catch {}
336
434
  throw new Error(`failed to decompress .zst (need zstd CLI): ${e.message}`);
@@ -339,44 +437,42 @@ async function main() {
339
437
  } else {
340
438
  // Extract .tar.gz using system tar
341
439
  try {
342
- execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, { stdio: 'ignore', shell: true });
440
+ const dest = mirrorToLocal ? binDir : safeTempDir;
441
+ execSync(`tar -xzf '${tmpPath}' -C '${dest}'`, { stdio: 'ignore', shell: true });
343
442
  } catch (e) {
344
443
  try { unlinkSync(tmpPath); } catch {}
345
444
  throw new Error(`failed to extract .tar.gz: ${e.message}`);
346
445
  }
347
446
  try { unlinkSync(tmpPath); } catch {}
348
447
  }
448
+ if (!mirrorToLocal) {
449
+ try {
450
+ const extractedPath = join(safeTempDir, binaryName);
451
+ await writeCacheAtomic(extractedPath, cachePath);
452
+ try { unlinkSync(extractedPath); } catch {}
453
+ } catch (e) {
454
+ throw new Error(`failed to move binary to cache: ${e.message}`);
455
+ }
456
+ }
349
457
  }
350
458
 
351
459
  // Validate header to avoid corrupt binaries causing spawn EFTYPE/ENOEXEC
352
- // On Windows, archive extraction writes to binDir; move the result to cache
353
- // and remove the copy from node_modules to avoid future locks. On Unix,
354
- // we keep a copy in binDir and also ensure cache is populated.
355
- if (isWindows) {
356
- try {
357
- // Ensure the extracted file is at localPath; then move it to cachePath
358
- copyFileSync(localPath, cachePath);
359
- try { unlinkSync(localPath); } catch {}
360
- } catch (e) {
361
- throw new Error(`failed to move binary to cache: ${e.message}`);
362
- }
363
- }
364
460
 
365
- const valid = validateDownloadedBinary(isWindows ? cachePath : localPath);
461
+ const valid = validateDownloadedBinary(isWin ? cachePath : (mirrorToLocal ? localPath : cachePath));
366
462
  if (!valid.ok) {
367
- try { isWindows ? unlinkSync(cachePath) : unlinkSync(localPath); } catch {}
463
+ try { (isWin || !mirrorToLocal) ? unlinkSync(cachePath) : unlinkSync(localPath); } catch {}
368
464
  throw new Error(`invalid binary (${valid.reason})`);
369
465
  }
370
466
 
371
467
  // Make executable on Unix-like systems
372
- if (!isWindows) {
468
+ if (!isWin && mirrorToLocal) {
373
469
  chmodSync(localPath, 0o755);
374
470
  }
375
471
 
376
- console.log(`✓ Installed ${binaryName}${isWindows ? ' (cached)' : ''}`);
472
+ console.log(`✓ Installed ${binaryName}${(isWin || !mirrorToLocal) ? ' (cached)' : ''}`);
377
473
  // Ensure persistent cache holds the binary (already true for Windows path)
378
- if (!isWindows) {
379
- try { copyFileSync(localPath, cachePath); } catch {}
474
+ if (!isWin && mirrorToLocal) {
475
+ try { await writeCacheAtomic(localPath, cachePath); } catch {}
380
476
  }
381
477
  } catch (error) {
382
478
  console.error(`✗ Failed to install ${binaryName}: ${error.message}`);
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ // Windows-friendly preinstall: proactively free file locks from prior installs
3
+ // so npm/yarn/pnpm can stage the new package. No-ops on non-Windows.
4
+
5
+ import { platform } from 'os';
6
+ import { execSync } from 'child_process';
7
+ import { existsSync, readdirSync, rmSync, readFileSync, statSync } from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ function isWSL() {
12
+ if (platform() !== 'linux') return false;
13
+ try {
14
+ const rel = readFileSync('/proc/version', 'utf8').toLowerCase();
15
+ return rel.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
16
+ } catch { return false; }
17
+ }
18
+
19
+ const isWin = platform() === 'win32';
20
+ const wsl = isWSL();
21
+ const isWinLike = isWin || wsl;
22
+
23
+ // Scope: only run for global installs, unless explicitly forced. Allow opt-out.
24
+ const isGlobal = process.env.npm_config_global === 'true';
25
+ const force = process.env.CODE_FORCE_PREINSTALL === '1';
26
+ const skip = process.env.CODE_SKIP_PREINSTALL === '1';
27
+ if (!isWinLike || skip || (!isGlobal && !force)) process.exit(0);
28
+
29
+ function tryExec(cmd, opts = {}) {
30
+ try { execSync(cmd, { stdio: ['ignore', 'ignore', 'ignore'], shell: true, ...opts }); } catch { /* ignore */ }
31
+ }
32
+
33
+ // 1) Stop our native binary if it is holding locks. Avoid killing unrelated tools.
34
+ // Only available on native Windows; skip entirely on WSL to avoid noise.
35
+ if (isWin) {
36
+ tryExec('taskkill /IM code-x86_64-pc-windows-msvc.exe /F');
37
+ }
38
+
39
+ // 2) Remove stale staging dirs from previous failed installs under the global
40
+ // @just-every scope, which npm will reuse (e.g., .code-XXXXX). Remove only
41
+ // old entries and never the current staging or live package.
42
+ try {
43
+ let scopeDir = '';
44
+ try {
45
+ const root = execSync('npm root -g', { stdio: ['ignore', 'pipe', 'ignore'], shell: true }).toString().trim();
46
+ scopeDir = path.join(root, '@just-every');
47
+ } catch {
48
+ // Fall back to guessing from this script location: <staging>\..\..\
49
+ const here = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
50
+ scopeDir = path.resolve(here, '..');
51
+ }
52
+ if (existsSync(scopeDir)) {
53
+ const now = Date.now();
54
+ const maxAgeMs = 2 * 60 * 60 * 1000; // 2 hours
55
+ const currentDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
56
+ for (const name of readdirSync(scopeDir)) {
57
+ if (!name.startsWith('.code-')) continue;
58
+ const p = path.join(scopeDir, name);
59
+ if (path.resolve(p) === currentDir) continue; // never remove our current dir
60
+ try {
61
+ const st = statSync(p);
62
+ const age = now - st.mtimeMs;
63
+ if (age > maxAgeMs) rmSync(p, { recursive: true, force: true });
64
+ } catch { /* ignore */ }
65
+ }
66
+ }
67
+ } catch { /* ignore */ }
68
+
69
+ process.exit(0);
@@ -0,0 +1,32 @@
1
+ <#
2
+ Helper to recover from EBUSY/EPERM during global npm upgrades on Windows.
3
+ Closes running processes and removes stale package folders.
4
+
5
+ Usage (PowerShell):
6
+ Set-ExecutionPolicy -Scope Process Bypass -Force
7
+ ./codex-cli/scripts/windows-cleanup.ps1
8
+ #>
9
+
10
+ $ErrorActionPreference = 'SilentlyContinue'
11
+
12
+ Write-Host "Stopping running Code/Coder processes..."
13
+ taskkill /IM code-x86_64-pc-windows-msvc.exe /F 2>$null | Out-Null
14
+ taskkill /IM code.exe /F 2>$null | Out-Null
15
+ taskkill /IM coder.exe /F 2>$null | Out-Null
16
+
17
+ Write-Host "Removing old global package (if present)..."
18
+ $npmRoot = (& npm root -g).Trim()
19
+ $pkgPath = Join-Path $npmRoot "@just-every\code"
20
+ if (Test-Path $pkgPath) {
21
+ try { Remove-Item -LiteralPath $pkgPath -Recurse -Force -ErrorAction Stop } catch {}
22
+ }
23
+
24
+ Write-Host "Removing temp staging directories (if present)..."
25
+ Get-ChildItem -LiteralPath (Join-Path $npmRoot "@just-every") -Force -ErrorAction SilentlyContinue |
26
+ Where-Object { $_.Name -like '.code-*' } |
27
+ ForEach-Object {
28
+ try { Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction Stop } catch {}
29
+ }
30
+
31
+ Write-Host "Cleanup complete. You can now run: npm install -g @just-every/code@latest"
32
+