@askexenow/exe-os 0.9.30 → 0.9.32
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/bin/backfill-conversations.js +135 -7
- package/dist/bin/backfill-responses.js +135 -7
- package/dist/bin/backfill-vectors.js +135 -7
- package/dist/bin/cleanup-stale-review-tasks.js +139 -11
- package/dist/bin/cli.js +812 -486
- package/dist/bin/exe-assign.js +135 -7
- package/dist/bin/exe-boot.js +422 -113
- package/dist/bin/exe-cloud.js +160 -9
- package/dist/bin/exe-dispatch.js +136 -8
- package/dist/bin/exe-doctor.js +255 -13
- package/dist/bin/exe-export-behaviors.js +136 -8
- package/dist/bin/exe-forget.js +136 -8
- package/dist/bin/exe-gateway.js +171 -24
- package/dist/bin/exe-heartbeat.js +141 -13
- package/dist/bin/exe-kill.js +140 -12
- package/dist/bin/exe-launch-agent.js +143 -15
- package/dist/bin/exe-link.js +357 -48
- package/dist/bin/exe-pending-messages.js +136 -8
- package/dist/bin/exe-pending-notifications.js +136 -8
- package/dist/bin/exe-pending-reviews.js +138 -10
- package/dist/bin/exe-review.js +136 -8
- package/dist/bin/exe-search.js +155 -20
- package/dist/bin/exe-session-cleanup.js +166 -38
- package/dist/bin/exe-start-codex.js +142 -14
- package/dist/bin/exe-start-opencode.js +140 -12
- package/dist/bin/exe-status.js +148 -20
- package/dist/bin/exe-team.js +136 -8
- package/dist/bin/git-sweep.js +138 -10
- package/dist/bin/graph-backfill.js +135 -7
- package/dist/bin/graph-export.js +136 -8
- package/dist/bin/intercom-check.js +153 -25
- package/dist/bin/scan-tasks.js +138 -10
- package/dist/bin/setup.js +447 -121
- package/dist/bin/shard-migrate.js +135 -7
- package/dist/gateway/index.js +151 -23
- package/dist/hooks/bug-report-worker.js +151 -23
- package/dist/hooks/codex-stop-task-finalizer.js +145 -17
- package/dist/hooks/commit-complete.js +138 -10
- package/dist/hooks/error-recall.js +159 -24
- package/dist/hooks/ingest.js +142 -14
- package/dist/hooks/instructions-loaded.js +136 -8
- package/dist/hooks/notification.js +136 -8
- package/dist/hooks/post-compact.js +136 -8
- package/dist/hooks/post-tool-combined.js +159 -24
- package/dist/hooks/pre-compact.js +136 -8
- package/dist/hooks/pre-tool-use.js +144 -16
- package/dist/hooks/prompt-submit.js +195 -55
- package/dist/hooks/session-end.js +141 -13
- package/dist/hooks/session-start.js +165 -30
- package/dist/hooks/stop.js +136 -8
- package/dist/hooks/subagent-stop.js +136 -8
- package/dist/hooks/summary-worker.js +374 -65
- package/dist/index.js +136 -8
- package/dist/lib/cloud-sync.js +355 -46
- package/dist/lib/consolidation.js +1 -0
- package/dist/lib/exe-daemon.js +469 -127
- package/dist/lib/hybrid-search.js +155 -20
- package/dist/lib/keychain.js +191 -7
- package/dist/lib/schedules.js +138 -10
- package/dist/lib/store.js +135 -7
- package/dist/mcp/server.js +706 -213
- package/dist/runtime/index.js +136 -8
- package/dist/tui/App.js +208 -31
- package/package.json +1 -1
package/dist/bin/exe-doctor.js
CHANGED
|
@@ -1029,8 +1029,8 @@ function findPackageRoot() {
|
|
|
1029
1029
|
function getAvailableMemoryGB() {
|
|
1030
1030
|
if (process.platform === "darwin") {
|
|
1031
1031
|
try {
|
|
1032
|
-
const { execSync:
|
|
1033
|
-
const vmstat =
|
|
1032
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1033
|
+
const vmstat = execSync3("vm_stat", { encoding: "utf8" });
|
|
1034
1034
|
const pageSize = 16384;
|
|
1035
1035
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1036
1036
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -3243,6 +3243,109 @@ var init_worker_gate = __esm({
|
|
|
3243
3243
|
}
|
|
3244
3244
|
});
|
|
3245
3245
|
|
|
3246
|
+
// src/lib/db-backup.ts
|
|
3247
|
+
var db_backup_exports = {};
|
|
3248
|
+
__export(db_backup_exports, {
|
|
3249
|
+
createBackup: () => createBackup,
|
|
3250
|
+
findActiveDb: () => findActiveDb,
|
|
3251
|
+
getBackupDir: () => getBackupDir,
|
|
3252
|
+
getLatestBackup: () => getLatestBackup,
|
|
3253
|
+
hasBackupToday: () => hasBackupToday,
|
|
3254
|
+
listBackups: () => listBackups,
|
|
3255
|
+
rotateBackups: () => rotateBackups
|
|
3256
|
+
});
|
|
3257
|
+
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync3, unlinkSync as unlinkSync4, statSync as statSync2 } from "fs";
|
|
3258
|
+
import path9 from "path";
|
|
3259
|
+
function findActiveDb() {
|
|
3260
|
+
for (const name of DB_NAMES) {
|
|
3261
|
+
const p = path9.join(EXE_AI_DIR, name);
|
|
3262
|
+
if (existsSync9(p)) return p;
|
|
3263
|
+
}
|
|
3264
|
+
return null;
|
|
3265
|
+
}
|
|
3266
|
+
function createBackup(reason = "manual") {
|
|
3267
|
+
const dbPath = findActiveDb();
|
|
3268
|
+
if (!dbPath) return null;
|
|
3269
|
+
mkdirSync4(BACKUP_DIR, { recursive: true });
|
|
3270
|
+
const dbName = path9.basename(dbPath, ".db");
|
|
3271
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
3272
|
+
const backupName = `${dbName}-${reason}-${timestamp}.db`;
|
|
3273
|
+
const backupPath = path9.join(BACKUP_DIR, backupName);
|
|
3274
|
+
copyFileSync(dbPath, backupPath);
|
|
3275
|
+
const walPath = dbPath + "-wal";
|
|
3276
|
+
if (existsSync9(walPath)) {
|
|
3277
|
+
try {
|
|
3278
|
+
copyFileSync(walPath, backupPath + "-wal");
|
|
3279
|
+
} catch {
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
const shmPath = dbPath + "-shm";
|
|
3283
|
+
if (existsSync9(shmPath)) {
|
|
3284
|
+
try {
|
|
3285
|
+
copyFileSync(shmPath, backupPath + "-shm");
|
|
3286
|
+
} catch {
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
return backupPath;
|
|
3290
|
+
}
|
|
3291
|
+
function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
|
|
3292
|
+
if (!existsSync9(BACKUP_DIR)) return 0;
|
|
3293
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
|
|
3294
|
+
let deleted = 0;
|
|
3295
|
+
try {
|
|
3296
|
+
const files = readdirSync3(BACKUP_DIR);
|
|
3297
|
+
for (const file of files) {
|
|
3298
|
+
if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
|
|
3299
|
+
const filePath = path9.join(BACKUP_DIR, file);
|
|
3300
|
+
try {
|
|
3301
|
+
const stat = statSync2(filePath);
|
|
3302
|
+
if (stat.mtimeMs < cutoff) {
|
|
3303
|
+
unlinkSync4(filePath);
|
|
3304
|
+
deleted++;
|
|
3305
|
+
}
|
|
3306
|
+
} catch {
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
} catch {
|
|
3310
|
+
}
|
|
3311
|
+
return deleted;
|
|
3312
|
+
}
|
|
3313
|
+
function listBackups() {
|
|
3314
|
+
if (!existsSync9(BACKUP_DIR)) return [];
|
|
3315
|
+
try {
|
|
3316
|
+
const files = readdirSync3(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
|
|
3317
|
+
return files.map((name) => {
|
|
3318
|
+
const p = path9.join(BACKUP_DIR, name);
|
|
3319
|
+
const stat = statSync2(p);
|
|
3320
|
+
return { path: p, name, size: stat.size, date: stat.mtime };
|
|
3321
|
+
}).sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
3322
|
+
} catch {
|
|
3323
|
+
return [];
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
function hasBackupToday(reason) {
|
|
3327
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3328
|
+
const backups = listBackups();
|
|
3329
|
+
return backups.some((b) => b.name.includes(reason) && b.name.includes(today.replace(/-/g, "-")));
|
|
3330
|
+
}
|
|
3331
|
+
function getLatestBackup() {
|
|
3332
|
+
const backups = listBackups();
|
|
3333
|
+
return backups.length > 0 ? backups[0].path : null;
|
|
3334
|
+
}
|
|
3335
|
+
function getBackupDir() {
|
|
3336
|
+
return BACKUP_DIR;
|
|
3337
|
+
}
|
|
3338
|
+
var BACKUP_DIR, DEFAULT_KEEP_DAYS, DB_NAMES;
|
|
3339
|
+
var init_db_backup = __esm({
|
|
3340
|
+
"src/lib/db-backup.ts"() {
|
|
3341
|
+
"use strict";
|
|
3342
|
+
init_config();
|
|
3343
|
+
BACKUP_DIR = path9.join(EXE_AI_DIR, "backups");
|
|
3344
|
+
DEFAULT_KEEP_DAYS = 3;
|
|
3345
|
+
DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
|
|
3346
|
+
}
|
|
3347
|
+
});
|
|
3348
|
+
|
|
3246
3349
|
// src/bin/exe-doctor.ts
|
|
3247
3350
|
import os6 from "os";
|
|
3248
3351
|
|
|
@@ -3254,6 +3357,7 @@ import { createHash } from "crypto";
|
|
|
3254
3357
|
// src/lib/keychain.ts
|
|
3255
3358
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3256
3359
|
import { existsSync as existsSync6 } from "fs";
|
|
3360
|
+
import { execSync as execSync2 } from "child_process";
|
|
3257
3361
|
import path6 from "path";
|
|
3258
3362
|
import os5 from "os";
|
|
3259
3363
|
var SERVICE = "exe-mem";
|
|
@@ -3264,6 +3368,59 @@ function getKeyDir() {
|
|
|
3264
3368
|
function getKeyPath() {
|
|
3265
3369
|
return path6.join(getKeyDir(), "master.key");
|
|
3266
3370
|
}
|
|
3371
|
+
function macKeychainGet() {
|
|
3372
|
+
if (process.platform !== "darwin") return null;
|
|
3373
|
+
try {
|
|
3374
|
+
return execSync2(
|
|
3375
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3376
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3377
|
+
).trim();
|
|
3378
|
+
} catch {
|
|
3379
|
+
return null;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
function macKeychainSet(value) {
|
|
3383
|
+
if (process.platform !== "darwin") return false;
|
|
3384
|
+
try {
|
|
3385
|
+
try {
|
|
3386
|
+
execSync2(
|
|
3387
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3388
|
+
{ timeout: 5e3 }
|
|
3389
|
+
);
|
|
3390
|
+
} catch {
|
|
3391
|
+
}
|
|
3392
|
+
execSync2(
|
|
3393
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3394
|
+
{ timeout: 5e3 }
|
|
3395
|
+
);
|
|
3396
|
+
return true;
|
|
3397
|
+
} catch {
|
|
3398
|
+
return false;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
function linuxSecretGet() {
|
|
3402
|
+
if (process.platform !== "linux") return null;
|
|
3403
|
+
try {
|
|
3404
|
+
return execSync2(
|
|
3405
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3406
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3407
|
+
).trim();
|
|
3408
|
+
} catch {
|
|
3409
|
+
return null;
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
function linuxSecretSet(value) {
|
|
3413
|
+
if (process.platform !== "linux") return false;
|
|
3414
|
+
try {
|
|
3415
|
+
execSync2(
|
|
3416
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
3417
|
+
{ timeout: 5e3 }
|
|
3418
|
+
);
|
|
3419
|
+
return true;
|
|
3420
|
+
} catch {
|
|
3421
|
+
return false;
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3267
3424
|
async function tryKeytar() {
|
|
3268
3425
|
try {
|
|
3269
3426
|
return await import("keytar");
|
|
@@ -3271,13 +3428,64 @@ async function tryKeytar() {
|
|
|
3271
3428
|
return null;
|
|
3272
3429
|
}
|
|
3273
3430
|
}
|
|
3431
|
+
var ENCRYPTED_PREFIX = "enc:";
|
|
3432
|
+
function deriveMachineKey() {
|
|
3433
|
+
try {
|
|
3434
|
+
const crypto2 = __require("crypto");
|
|
3435
|
+
const material = [
|
|
3436
|
+
os5.hostname(),
|
|
3437
|
+
os5.userInfo().username,
|
|
3438
|
+
os5.arch(),
|
|
3439
|
+
os5.platform(),
|
|
3440
|
+
// Machine ID on Linux (stable across reboots)
|
|
3441
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3442
|
+
].join("|");
|
|
3443
|
+
return crypto2.createHash("sha256").update(material).digest();
|
|
3444
|
+
} catch {
|
|
3445
|
+
return null;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
function readMachineId() {
|
|
3449
|
+
try {
|
|
3450
|
+
const { readFileSync: readFileSync6 } = __require("fs");
|
|
3451
|
+
return readFileSync6("/etc/machine-id", "utf-8").trim();
|
|
3452
|
+
} catch {
|
|
3453
|
+
return "";
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3457
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3458
|
+
try {
|
|
3459
|
+
const crypto2 = __require("crypto");
|
|
3460
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3461
|
+
if (parts.length !== 3) return null;
|
|
3462
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3463
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3464
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3465
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3466
|
+
decipher.setAuthTag(authTag);
|
|
3467
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3468
|
+
decrypted += decipher.final("utf-8");
|
|
3469
|
+
return decrypted;
|
|
3470
|
+
} catch {
|
|
3471
|
+
return null;
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3274
3474
|
async function getMasterKey() {
|
|
3475
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3476
|
+
if (nativeValue) {
|
|
3477
|
+
return Buffer.from(nativeValue, "base64");
|
|
3478
|
+
}
|
|
3275
3479
|
const keytar = await tryKeytar();
|
|
3276
3480
|
if (keytar) {
|
|
3277
3481
|
try {
|
|
3278
|
-
const
|
|
3279
|
-
if (
|
|
3280
|
-
|
|
3482
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3483
|
+
if (keytarValue) {
|
|
3484
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
3485
|
+
if (migrated) {
|
|
3486
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3487
|
+
}
|
|
3488
|
+
return Buffer.from(keytarValue, "base64");
|
|
3281
3489
|
}
|
|
3282
3490
|
} catch {
|
|
3283
3491
|
}
|
|
@@ -3291,8 +3499,31 @@ async function getMasterKey() {
|
|
|
3291
3499
|
return null;
|
|
3292
3500
|
}
|
|
3293
3501
|
try {
|
|
3294
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
3295
|
-
|
|
3502
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3503
|
+
let b64Value;
|
|
3504
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3505
|
+
const machineKey = deriveMachineKey();
|
|
3506
|
+
if (!machineKey) {
|
|
3507
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3508
|
+
return null;
|
|
3509
|
+
}
|
|
3510
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3511
|
+
if (!decrypted) {
|
|
3512
|
+
process.stderr.write(
|
|
3513
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
3514
|
+
);
|
|
3515
|
+
return null;
|
|
3516
|
+
}
|
|
3517
|
+
b64Value = decrypted;
|
|
3518
|
+
} else {
|
|
3519
|
+
b64Value = content;
|
|
3520
|
+
}
|
|
3521
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3522
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3523
|
+
if (migrated) {
|
|
3524
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3525
|
+
}
|
|
3526
|
+
return key;
|
|
3296
3527
|
} catch (err) {
|
|
3297
3528
|
process.stderr.write(
|
|
3298
3529
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3458,9 +3689,9 @@ function isMainModule(importMetaUrl) {
|
|
|
3458
3689
|
}
|
|
3459
3690
|
|
|
3460
3691
|
// src/bin/exe-doctor.ts
|
|
3461
|
-
import { existsSync as
|
|
3692
|
+
import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
|
|
3462
3693
|
import { spawn as spawn2 } from "child_process";
|
|
3463
|
-
import
|
|
3694
|
+
import path10 from "path";
|
|
3464
3695
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3465
3696
|
|
|
3466
3697
|
// src/lib/conflict-detector.ts
|
|
@@ -3864,7 +4095,7 @@ async function auditOrphanedProjects(client) {
|
|
|
3864
4095
|
for (const row of result.rows) {
|
|
3865
4096
|
const name = row.project_name;
|
|
3866
4097
|
const count = Number(row.cnt);
|
|
3867
|
-
const exists =
|
|
4098
|
+
const exists = existsSync10(path10.join(home, name)) || existsSync10(path10.join(home, "..", name)) || existsSync10(path10.join(process.cwd(), "..", name));
|
|
3868
4099
|
if (!exists) {
|
|
3869
4100
|
orphans.push({ project_name: name, count });
|
|
3870
4101
|
}
|
|
@@ -3872,13 +4103,13 @@ async function auditOrphanedProjects(client) {
|
|
|
3872
4103
|
return orphans;
|
|
3873
4104
|
}
|
|
3874
4105
|
function auditHookHealth() {
|
|
3875
|
-
const logPath =
|
|
4106
|
+
const logPath = path10.join(
|
|
3876
4107
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
3877
4108
|
".exe-os",
|
|
3878
4109
|
"logs",
|
|
3879
4110
|
"hooks.log"
|
|
3880
4111
|
);
|
|
3881
|
-
if (!
|
|
4112
|
+
if (!existsSync10(logPath)) {
|
|
3882
4113
|
return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
|
|
3883
4114
|
}
|
|
3884
4115
|
let content;
|
|
@@ -4096,7 +4327,7 @@ async function fixNullVectors() {
|
|
|
4096
4327
|
}
|
|
4097
4328
|
}
|
|
4098
4329
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
4099
|
-
const backfillPath =
|
|
4330
|
+
const backfillPath = path10.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
4100
4331
|
return new Promise((resolve, reject) => {
|
|
4101
4332
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
4102
4333
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -4208,6 +4439,17 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
4208
4439
|
console.log(`
|
|
4209
4440
|
${mode} Applying repairs...
|
|
4210
4441
|
`);
|
|
4442
|
+
if (!flags.dryRun) {
|
|
4443
|
+
try {
|
|
4444
|
+
const { createBackup: createBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
|
|
4445
|
+
const backupPath = createBackup2("pre-fix");
|
|
4446
|
+
if (backupPath) {
|
|
4447
|
+
console.log(` Backup created: ${backupPath.split("/").pop()}`);
|
|
4448
|
+
}
|
|
4449
|
+
} catch {
|
|
4450
|
+
console.log(" Warning: backup failed \u2014 proceeding with fix anyway");
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4211
4453
|
if (report.nullVectors > 0) {
|
|
4212
4454
|
console.log(`${mode} Backfilling ${fmtNum(report.nullVectors)} null vectors...`);
|
|
4213
4455
|
if (!flags.dryRun) {
|
|
@@ -1040,8 +1040,8 @@ function findPackageRoot() {
|
|
|
1040
1040
|
function getAvailableMemoryGB() {
|
|
1041
1041
|
if (process.platform === "darwin") {
|
|
1042
1042
|
try {
|
|
1043
|
-
const { execSync:
|
|
1044
|
-
const vmstat =
|
|
1043
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1044
|
+
const vmstat = execSync3("vm_stat", { encoding: "utf8" });
|
|
1045
1045
|
const pageSize = 16384;
|
|
1046
1046
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1047
1047
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -2780,6 +2780,7 @@ var init_database = __esm({
|
|
|
2780
2780
|
// src/lib/keychain.ts
|
|
2781
2781
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2782
2782
|
import { existsSync as existsSync6 } from "fs";
|
|
2783
|
+
import { execSync as execSync2 } from "child_process";
|
|
2783
2784
|
import path6 from "path";
|
|
2784
2785
|
import os5 from "os";
|
|
2785
2786
|
function getKeyDir() {
|
|
@@ -2788,6 +2789,59 @@ function getKeyDir() {
|
|
|
2788
2789
|
function getKeyPath() {
|
|
2789
2790
|
return path6.join(getKeyDir(), "master.key");
|
|
2790
2791
|
}
|
|
2792
|
+
function macKeychainGet() {
|
|
2793
|
+
if (process.platform !== "darwin") return null;
|
|
2794
|
+
try {
|
|
2795
|
+
return execSync2(
|
|
2796
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
2797
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
2798
|
+
).trim();
|
|
2799
|
+
} catch {
|
|
2800
|
+
return null;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
function macKeychainSet(value) {
|
|
2804
|
+
if (process.platform !== "darwin") return false;
|
|
2805
|
+
try {
|
|
2806
|
+
try {
|
|
2807
|
+
execSync2(
|
|
2808
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
2809
|
+
{ timeout: 5e3 }
|
|
2810
|
+
);
|
|
2811
|
+
} catch {
|
|
2812
|
+
}
|
|
2813
|
+
execSync2(
|
|
2814
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
2815
|
+
{ timeout: 5e3 }
|
|
2816
|
+
);
|
|
2817
|
+
return true;
|
|
2818
|
+
} catch {
|
|
2819
|
+
return false;
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
function linuxSecretGet() {
|
|
2823
|
+
if (process.platform !== "linux") return null;
|
|
2824
|
+
try {
|
|
2825
|
+
return execSync2(
|
|
2826
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
2827
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
2828
|
+
).trim();
|
|
2829
|
+
} catch {
|
|
2830
|
+
return null;
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
function linuxSecretSet(value) {
|
|
2834
|
+
if (process.platform !== "linux") return false;
|
|
2835
|
+
try {
|
|
2836
|
+
execSync2(
|
|
2837
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
2838
|
+
{ timeout: 5e3 }
|
|
2839
|
+
);
|
|
2840
|
+
return true;
|
|
2841
|
+
} catch {
|
|
2842
|
+
return false;
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2791
2845
|
async function tryKeytar() {
|
|
2792
2846
|
try {
|
|
2793
2847
|
return await import("keytar");
|
|
@@ -2795,13 +2849,63 @@ async function tryKeytar() {
|
|
|
2795
2849
|
return null;
|
|
2796
2850
|
}
|
|
2797
2851
|
}
|
|
2852
|
+
function deriveMachineKey() {
|
|
2853
|
+
try {
|
|
2854
|
+
const crypto3 = __require("crypto");
|
|
2855
|
+
const material = [
|
|
2856
|
+
os5.hostname(),
|
|
2857
|
+
os5.userInfo().username,
|
|
2858
|
+
os5.arch(),
|
|
2859
|
+
os5.platform(),
|
|
2860
|
+
// Machine ID on Linux (stable across reboots)
|
|
2861
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
2862
|
+
].join("|");
|
|
2863
|
+
return crypto3.createHash("sha256").update(material).digest();
|
|
2864
|
+
} catch {
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
function readMachineId() {
|
|
2869
|
+
try {
|
|
2870
|
+
const { readFileSync: readFileSync5 } = __require("fs");
|
|
2871
|
+
return readFileSync5("/etc/machine-id", "utf-8").trim();
|
|
2872
|
+
} catch {
|
|
2873
|
+
return "";
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2877
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2878
|
+
try {
|
|
2879
|
+
const crypto3 = __require("crypto");
|
|
2880
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
2881
|
+
if (parts.length !== 3) return null;
|
|
2882
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
2883
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
2884
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
2885
|
+
const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
2886
|
+
decipher.setAuthTag(authTag);
|
|
2887
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
2888
|
+
decrypted += decipher.final("utf-8");
|
|
2889
|
+
return decrypted;
|
|
2890
|
+
} catch {
|
|
2891
|
+
return null;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2798
2894
|
async function getMasterKey() {
|
|
2895
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2896
|
+
if (nativeValue) {
|
|
2897
|
+
return Buffer.from(nativeValue, "base64");
|
|
2898
|
+
}
|
|
2799
2899
|
const keytar = await tryKeytar();
|
|
2800
2900
|
if (keytar) {
|
|
2801
2901
|
try {
|
|
2802
|
-
const
|
|
2803
|
-
if (
|
|
2804
|
-
|
|
2902
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
2903
|
+
if (keytarValue) {
|
|
2904
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
2905
|
+
if (migrated) {
|
|
2906
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
2907
|
+
}
|
|
2908
|
+
return Buffer.from(keytarValue, "base64");
|
|
2805
2909
|
}
|
|
2806
2910
|
} catch {
|
|
2807
2911
|
}
|
|
@@ -2815,8 +2919,31 @@ async function getMasterKey() {
|
|
|
2815
2919
|
return null;
|
|
2816
2920
|
}
|
|
2817
2921
|
try {
|
|
2818
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
2819
|
-
|
|
2922
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
2923
|
+
let b64Value;
|
|
2924
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2925
|
+
const machineKey = deriveMachineKey();
|
|
2926
|
+
if (!machineKey) {
|
|
2927
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
2928
|
+
return null;
|
|
2929
|
+
}
|
|
2930
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
2931
|
+
if (!decrypted) {
|
|
2932
|
+
process.stderr.write(
|
|
2933
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
2934
|
+
);
|
|
2935
|
+
return null;
|
|
2936
|
+
}
|
|
2937
|
+
b64Value = decrypted;
|
|
2938
|
+
} else {
|
|
2939
|
+
b64Value = content;
|
|
2940
|
+
}
|
|
2941
|
+
const key = Buffer.from(b64Value, "base64");
|
|
2942
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2943
|
+
if (migrated) {
|
|
2944
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2945
|
+
}
|
|
2946
|
+
return key;
|
|
2820
2947
|
} catch (err) {
|
|
2821
2948
|
process.stderr.write(
|
|
2822
2949
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -2825,12 +2952,13 @@ async function getMasterKey() {
|
|
|
2825
2952
|
return null;
|
|
2826
2953
|
}
|
|
2827
2954
|
}
|
|
2828
|
-
var SERVICE, ACCOUNT;
|
|
2955
|
+
var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
|
|
2829
2956
|
var init_keychain = __esm({
|
|
2830
2957
|
"src/lib/keychain.ts"() {
|
|
2831
2958
|
"use strict";
|
|
2832
2959
|
SERVICE = "exe-mem";
|
|
2833
2960
|
ACCOUNT = "master-key";
|
|
2961
|
+
ENCRYPTED_PREFIX = "enc:";
|
|
2834
2962
|
}
|
|
2835
2963
|
});
|
|
2836
2964
|
|