billion-context 0.1.17 → 0.1.19
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/dist/index.js +189 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3234,16 +3234,18 @@ function logMsg(opts, level, msg2) {
|
|
|
3234
3234
|
}
|
|
3235
3235
|
|
|
3236
3236
|
// src/update.ts
|
|
3237
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
3237
|
+
import { readFile, writeFile, mkdir, access, constants, rm } from "fs/promises";
|
|
3238
3238
|
import { execFile } from "child_process";
|
|
3239
3239
|
import path4 from "path";
|
|
3240
|
+
import { fileURLToPath } from "url";
|
|
3240
3241
|
var REGISTRY_BASE = "https://registry.npmjs.org";
|
|
3241
3242
|
var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
|
|
3242
3243
|
var THROTTLE_FILE = path4.join(cacheDir(), ".update-check");
|
|
3243
|
-
var
|
|
3244
|
+
var LOCK_FILE = path4.join(cacheDir(), ".update-lock");
|
|
3245
|
+
var LOCK_STALE_MS = 2 * 60 * 1e3;
|
|
3244
3246
|
var timer;
|
|
3245
3247
|
var inFlight = false;
|
|
3246
|
-
var
|
|
3248
|
+
var firstCheckDone = false;
|
|
3247
3249
|
function parseVersion(v) {
|
|
3248
3250
|
return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
3249
3251
|
}
|
|
@@ -3273,22 +3275,98 @@ async function writeLastCheck(ts) {
|
|
|
3273
3275
|
} catch {
|
|
3274
3276
|
}
|
|
3275
3277
|
}
|
|
3278
|
+
async function findInstallDir(packageName) {
|
|
3279
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
3280
|
+
for (; ; ) {
|
|
3281
|
+
try {
|
|
3282
|
+
const pkg = JSON.parse(await readFile(path4.join(dir, "package.json"), "utf-8"));
|
|
3283
|
+
if (pkg.name === packageName) return dir;
|
|
3284
|
+
} catch {
|
|
3285
|
+
}
|
|
3286
|
+
const parent = path4.dirname(dir);
|
|
3287
|
+
if (parent === dir) return void 0;
|
|
3288
|
+
dir = parent;
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
async function readDiskVersion(installDir) {
|
|
3292
|
+
try {
|
|
3293
|
+
const pkg = JSON.parse(await readFile(path4.join(installDir, "package.json"), "utf-8"));
|
|
3294
|
+
return pkg.version;
|
|
3295
|
+
} catch {
|
|
3296
|
+
return void 0;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
async function tryAcquireLock() {
|
|
3300
|
+
const pid = process.pid;
|
|
3301
|
+
const now = Date.now();
|
|
3302
|
+
async function readLock() {
|
|
3303
|
+
try {
|
|
3304
|
+
const raw = await readFile(LOCK_FILE, "utf-8");
|
|
3305
|
+
const data = JSON.parse(raw);
|
|
3306
|
+
if (typeof data.pid === "number" && typeof data.ts === "number") {
|
|
3307
|
+
return data;
|
|
3308
|
+
}
|
|
3309
|
+
} catch {
|
|
3310
|
+
}
|
|
3311
|
+
return null;
|
|
3312
|
+
}
|
|
3313
|
+
function isAlive(checkPid) {
|
|
3314
|
+
try {
|
|
3315
|
+
process.kill(checkPid, 0);
|
|
3316
|
+
return true;
|
|
3317
|
+
} catch {
|
|
3318
|
+
return false;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
const existing = await readLock();
|
|
3322
|
+
if (existing) {
|
|
3323
|
+
const age = now - existing.ts;
|
|
3324
|
+
const holderAlive = isAlive(existing.pid);
|
|
3325
|
+
if (holderAlive && age < LOCK_STALE_MS) {
|
|
3326
|
+
return null;
|
|
3327
|
+
}
|
|
3328
|
+
log("info", `[update] stealing stale lock (pid=${existing.pid}, age=${Math.round(age / 1e3)}s, alive=${holderAlive})`);
|
|
3329
|
+
}
|
|
3330
|
+
try {
|
|
3331
|
+
await writeFile(LOCK_FILE, JSON.stringify({ pid, ts: now }), { flag: "wx" });
|
|
3332
|
+
} catch {
|
|
3333
|
+
const winner = await readLock();
|
|
3334
|
+
if (winner && winner.pid !== pid) {
|
|
3335
|
+
log("info", `[update] lost lock race to pid=${winner.pid}, skipping update`);
|
|
3336
|
+
return null;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
const confirmed = await readLock();
|
|
3340
|
+
if (!confirmed || confirmed.pid !== pid) {
|
|
3341
|
+
log("info", `[update] lock held by pid=${confirmed?.pid}, skipping update`);
|
|
3342
|
+
return null;
|
|
3343
|
+
}
|
|
3344
|
+
return {
|
|
3345
|
+
release: async () => {
|
|
3346
|
+
const current = await readLock();
|
|
3347
|
+
if (current?.pid === pid) {
|
|
3348
|
+
await rm(LOCK_FILE, { force: true }).catch(() => {
|
|
3349
|
+
});
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3276
3354
|
async function checkForUpdate(opts, force = false) {
|
|
3277
3355
|
if (!opts.autoUpdate && !force) return;
|
|
3278
3356
|
if (inFlight) return;
|
|
3279
3357
|
inFlight = true;
|
|
3280
|
-
const firstInProcess = !notified.has("__first_check_done__");
|
|
3281
3358
|
try {
|
|
3282
3359
|
const now = Date.now();
|
|
3283
3360
|
const lastCheck = await readLastCheck();
|
|
3284
3361
|
const sinceLastSec = lastCheck ? (now - lastCheck) / 1e3 | 0 : -1;
|
|
3285
|
-
if (!force &&
|
|
3362
|
+
if (!force && firstCheckDone && now - lastCheck < CHECK_INTERVAL_MS) {
|
|
3286
3363
|
const retryIn = (CHECK_INTERVAL_MS - (now - lastCheck)) / 1e3 | 0;
|
|
3287
3364
|
log("info", `[update] throttled \u2014 last checked ${sinceLastSec}s ago, retry in ${retryIn}s`);
|
|
3288
3365
|
return;
|
|
3289
3366
|
}
|
|
3290
3367
|
await writeLastCheck(now);
|
|
3291
|
-
|
|
3368
|
+
firstCheckDone = true;
|
|
3369
|
+
log("info", `[update] checking npm registry for ${opts.packageName}${sinceLastSec < 0 ? " (startup check)" : sinceLastSec === 0 ? "" : ` (last check ${sinceLastSec}s ago)`}\u2026`);
|
|
3292
3370
|
const url = `${REGISTRY_BASE}/${opts.packageName}/latest`;
|
|
3293
3371
|
const res = await fetch(url, {
|
|
3294
3372
|
signal: AbortSignal.timeout(5e3),
|
|
@@ -3304,44 +3382,124 @@ async function checkForUpdate(opts, force = false) {
|
|
|
3304
3382
|
log("warn", `[update] registry response had no version, skipping`);
|
|
3305
3383
|
return;
|
|
3306
3384
|
}
|
|
3307
|
-
|
|
3308
|
-
|
|
3385
|
+
const installDir = await findInstallDir(opts.packageName);
|
|
3386
|
+
const diskVersion = installDir ? await readDiskVersion(installDir) : void 0;
|
|
3387
|
+
const currentVersion = diskVersion ?? opts.currentVersion;
|
|
3388
|
+
if (!isNewer(latest, currentVersion)) {
|
|
3389
|
+
log("info", `[update] current=${currentVersion} latest=${latest} (up to date)`);
|
|
3309
3390
|
return;
|
|
3310
3391
|
}
|
|
3311
|
-
|
|
3312
|
-
|
|
3392
|
+
const tarballUrl = data.dist?.tarball;
|
|
3393
|
+
if (!tarballUrl) {
|
|
3394
|
+
log("warn", `[update] registry response for ${latest} had no tarball URL`);
|
|
3313
3395
|
return;
|
|
3314
3396
|
}
|
|
3315
|
-
log("info", `[update] new version found: ${
|
|
3316
|
-
const
|
|
3317
|
-
if (
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
}
|
|
3321
|
-
|
|
3397
|
+
log("info", `[update] new version found: ${currentVersion} \u2192 ${latest}, downloading\u2026`);
|
|
3398
|
+
const lock = await tryAcquireLock();
|
|
3399
|
+
if (!lock) {
|
|
3400
|
+
log("info", `[update] another process is updating, will check next cycle`);
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
try {
|
|
3404
|
+
const result = await installViaTarball(latest, tarballUrl, installDir);
|
|
3405
|
+
if (result.ok) {
|
|
3406
|
+
log("info", `[update] installed ${currentVersion} \u2192 ${latest}. Restart to finish.`);
|
|
3407
|
+
} else {
|
|
3408
|
+
log("warn", `[update] install failed: ${result.error}. Will retry next cycle.`);
|
|
3409
|
+
}
|
|
3410
|
+
} finally {
|
|
3411
|
+
await lock.release();
|
|
3322
3412
|
}
|
|
3323
3413
|
} catch (e) {
|
|
3324
3414
|
log("warn", `[update] check failed: ${String(e)}`);
|
|
3325
3415
|
} finally {
|
|
3326
3416
|
inFlight = false;
|
|
3327
|
-
notified.add("__first_check_done__");
|
|
3328
3417
|
}
|
|
3329
3418
|
}
|
|
3330
|
-
async function
|
|
3331
|
-
if (!
|
|
3419
|
+
async function installViaTarball(version, tarballUrl, installDir) {
|
|
3420
|
+
if (!installDir) {
|
|
3421
|
+
return { ok: false, error: "cannot determine install directory (package.json not found walking up from running binary)" };
|
|
3422
|
+
}
|
|
3423
|
+
try {
|
|
3424
|
+
await access(installDir, constants.W_OK);
|
|
3425
|
+
} catch {
|
|
3426
|
+
return { ok: false, error: `install dir not writable: ${installDir}` };
|
|
3427
|
+
}
|
|
3428
|
+
let tgzBuffer;
|
|
3429
|
+
try {
|
|
3430
|
+
const tgzRes = await fetch(tarballUrl, { signal: AbortSignal.timeout(6e4) });
|
|
3431
|
+
if (!tgzRes.ok) {
|
|
3432
|
+
return { ok: false, error: `tarball download failed: HTTP ${tgzRes.status} ${tgzRes.statusText}` };
|
|
3433
|
+
}
|
|
3434
|
+
tgzBuffer = Buffer.from(await tgzRes.arrayBuffer());
|
|
3435
|
+
} catch (e) {
|
|
3436
|
+
return { ok: false, error: `tarball download failed: ${String(e)}` };
|
|
3437
|
+
}
|
|
3438
|
+
const tmpFile = path4.join(cacheDir(), `.update-${version}.tgz`);
|
|
3332
3439
|
try {
|
|
3333
|
-
|
|
3440
|
+
await mkdir(cacheDir(), { recursive: true });
|
|
3441
|
+
await writeFile(tmpFile, tgzBuffer);
|
|
3442
|
+
} catch (e) {
|
|
3443
|
+
return { ok: false, error: `failed to write temp file ${tmpFile}: ${String(e)}` };
|
|
3444
|
+
}
|
|
3445
|
+
const stagingDir = path4.join(cacheDir(), `.update-staging-${version}`);
|
|
3446
|
+
try {
|
|
3447
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
3448
|
+
await mkdir(stagingDir, { recursive: true });
|
|
3449
|
+
const { code, stderr } = await new Promise((resolve) => {
|
|
3334
3450
|
execFile(
|
|
3335
|
-
"
|
|
3336
|
-
["
|
|
3337
|
-
{ timeout:
|
|
3338
|
-
(err) =>
|
|
3451
|
+
"tar",
|
|
3452
|
+
["xzf", tmpFile, "-C", stagingDir, "--strip-components=1"],
|
|
3453
|
+
{ timeout: 3e4 },
|
|
3454
|
+
(err, _stdout, stderr2) => {
|
|
3455
|
+
if (err) {
|
|
3456
|
+
resolve({ code: 1, stderr: stderr2 || err.message });
|
|
3457
|
+
} else {
|
|
3458
|
+
resolve({ code: 0, stderr: stderr2 || "" });
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3339
3461
|
);
|
|
3340
3462
|
});
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3463
|
+
if (code !== 0) {
|
|
3464
|
+
return { ok: false, error: `extraction failed (tar exit ${code})${stderr.trim() ? `: ${stderr.trim()}` : ""}` };
|
|
3465
|
+
}
|
|
3466
|
+
const stagingVersion = await readDiskVersion(stagingDir);
|
|
3467
|
+
if (stagingVersion !== version) {
|
|
3468
|
+
return { ok: false, error: `staging verification failed: version is ${stagingVersion ?? "missing"}, expected ${version}` };
|
|
3469
|
+
}
|
|
3470
|
+
} catch (e) {
|
|
3471
|
+
return { ok: false, error: `extraction failed: ${String(e)}` };
|
|
3472
|
+
} finally {
|
|
3473
|
+
await rm(tmpFile, { force: true });
|
|
3344
3474
|
}
|
|
3475
|
+
try {
|
|
3476
|
+
const { code, stderr } = await new Promise((resolve) => {
|
|
3477
|
+
execFile(
|
|
3478
|
+
"cp",
|
|
3479
|
+
["-r", stagingDir + "/.", installDir + "/"],
|
|
3480
|
+
{ timeout: 3e4 },
|
|
3481
|
+
(err, _stdout, stderr2) => {
|
|
3482
|
+
if (err) {
|
|
3483
|
+
resolve({ code: 1, stderr: stderr2 || err.message });
|
|
3484
|
+
} else {
|
|
3485
|
+
resolve({ code: 0, stderr: stderr2 || "" });
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
);
|
|
3489
|
+
});
|
|
3490
|
+
if (code !== 0) {
|
|
3491
|
+
return { ok: false, error: `failed to copy to install dir (cp exit ${code})${stderr.trim() ? `: ${stderr.trim()}` : ""}` };
|
|
3492
|
+
}
|
|
3493
|
+
} catch (e) {
|
|
3494
|
+
return { ok: false, error: `failed to copy to install dir: ${String(e)}` };
|
|
3495
|
+
} finally {
|
|
3496
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
3497
|
+
}
|
|
3498
|
+
const newVersion = await readDiskVersion(installDir);
|
|
3499
|
+
if (newVersion !== version) {
|
|
3500
|
+
return { ok: false, error: `post-install verification failed: package.json version is ${newVersion ?? "missing"}, expected ${version}` };
|
|
3501
|
+
}
|
|
3502
|
+
return { ok: true };
|
|
3345
3503
|
}
|
|
3346
3504
|
function startAutoUpdate(opts) {
|
|
3347
3505
|
log("info", `[update] auto-update enabled (checking every ${CHECK_INTERVAL_MS / 1e3 | 0}s)`);
|
|
@@ -3356,11 +3514,11 @@ function startAutoUpdate(opts) {
|
|
|
3356
3514
|
|
|
3357
3515
|
// src/cli.ts
|
|
3358
3516
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3359
|
-
import { fileURLToPath } from "url";
|
|
3517
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3360
3518
|
import path5 from "path";
|
|
3361
3519
|
var VERSION = (() => {
|
|
3362
3520
|
try {
|
|
3363
|
-
const here =
|
|
3521
|
+
const here = fileURLToPath2(import.meta.url);
|
|
3364
3522
|
const pkg = path5.join(path5.dirname(here), "..", "package.json");
|
|
3365
3523
|
return JSON.parse(readFileSync3(pkg, "utf8")).version ?? "dev";
|
|
3366
3524
|
} catch {
|
|
@@ -3369,7 +3527,7 @@ var VERSION = (() => {
|
|
|
3369
3527
|
})();
|
|
3370
3528
|
var PACKAGE_NAME = (() => {
|
|
3371
3529
|
try {
|
|
3372
|
-
const here =
|
|
3530
|
+
const here = fileURLToPath2(import.meta.url);
|
|
3373
3531
|
const pkg = path5.join(path5.dirname(here), "..", "package.json");
|
|
3374
3532
|
return JSON.parse(readFileSync3(pkg, "utf8")).name ?? "billion-context";
|
|
3375
3533
|
} catch {
|