@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
|
@@ -1033,8 +1033,8 @@ function findPackageRoot() {
|
|
|
1033
1033
|
function getAvailableMemoryGB() {
|
|
1034
1034
|
if (process.platform === "darwin") {
|
|
1035
1035
|
try {
|
|
1036
|
-
const { execSync:
|
|
1037
|
-
const vmstat =
|
|
1036
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1037
|
+
const vmstat = execSync3("vm_stat", { encoding: "utf8" });
|
|
1038
1038
|
const pageSize = 16384;
|
|
1039
1039
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1040
1040
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -3290,6 +3290,7 @@ import { createHash } from "crypto";
|
|
|
3290
3290
|
// src/lib/keychain.ts
|
|
3291
3291
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3292
3292
|
import { existsSync as existsSync6 } from "fs";
|
|
3293
|
+
import { execSync as execSync2 } from "child_process";
|
|
3293
3294
|
import path6 from "path";
|
|
3294
3295
|
import os5 from "os";
|
|
3295
3296
|
var SERVICE = "exe-mem";
|
|
@@ -3300,6 +3301,59 @@ function getKeyDir() {
|
|
|
3300
3301
|
function getKeyPath() {
|
|
3301
3302
|
return path6.join(getKeyDir(), "master.key");
|
|
3302
3303
|
}
|
|
3304
|
+
function macKeychainGet() {
|
|
3305
|
+
if (process.platform !== "darwin") return null;
|
|
3306
|
+
try {
|
|
3307
|
+
return execSync2(
|
|
3308
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3309
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3310
|
+
).trim();
|
|
3311
|
+
} catch {
|
|
3312
|
+
return null;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
function macKeychainSet(value) {
|
|
3316
|
+
if (process.platform !== "darwin") return false;
|
|
3317
|
+
try {
|
|
3318
|
+
try {
|
|
3319
|
+
execSync2(
|
|
3320
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3321
|
+
{ timeout: 5e3 }
|
|
3322
|
+
);
|
|
3323
|
+
} catch {
|
|
3324
|
+
}
|
|
3325
|
+
execSync2(
|
|
3326
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3327
|
+
{ timeout: 5e3 }
|
|
3328
|
+
);
|
|
3329
|
+
return true;
|
|
3330
|
+
} catch {
|
|
3331
|
+
return false;
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
function linuxSecretGet() {
|
|
3335
|
+
if (process.platform !== "linux") return null;
|
|
3336
|
+
try {
|
|
3337
|
+
return execSync2(
|
|
3338
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3339
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3340
|
+
).trim();
|
|
3341
|
+
} catch {
|
|
3342
|
+
return null;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
function linuxSecretSet(value) {
|
|
3346
|
+
if (process.platform !== "linux") return false;
|
|
3347
|
+
try {
|
|
3348
|
+
execSync2(
|
|
3349
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
3350
|
+
{ timeout: 5e3 }
|
|
3351
|
+
);
|
|
3352
|
+
return true;
|
|
3353
|
+
} catch {
|
|
3354
|
+
return false;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3303
3357
|
async function tryKeytar() {
|
|
3304
3358
|
try {
|
|
3305
3359
|
return await import("keytar");
|
|
@@ -3307,13 +3361,64 @@ async function tryKeytar() {
|
|
|
3307
3361
|
return null;
|
|
3308
3362
|
}
|
|
3309
3363
|
}
|
|
3364
|
+
var ENCRYPTED_PREFIX = "enc:";
|
|
3365
|
+
function deriveMachineKey() {
|
|
3366
|
+
try {
|
|
3367
|
+
const crypto3 = __require("crypto");
|
|
3368
|
+
const material = [
|
|
3369
|
+
os5.hostname(),
|
|
3370
|
+
os5.userInfo().username,
|
|
3371
|
+
os5.arch(),
|
|
3372
|
+
os5.platform(),
|
|
3373
|
+
// Machine ID on Linux (stable across reboots)
|
|
3374
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3375
|
+
].join("|");
|
|
3376
|
+
return crypto3.createHash("sha256").update(material).digest();
|
|
3377
|
+
} catch {
|
|
3378
|
+
return null;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
function readMachineId() {
|
|
3382
|
+
try {
|
|
3383
|
+
const { readFileSync: readFileSync5 } = __require("fs");
|
|
3384
|
+
return readFileSync5("/etc/machine-id", "utf-8").trim();
|
|
3385
|
+
} catch {
|
|
3386
|
+
return "";
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3390
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3391
|
+
try {
|
|
3392
|
+
const crypto3 = __require("crypto");
|
|
3393
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3394
|
+
if (parts.length !== 3) return null;
|
|
3395
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3396
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3397
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3398
|
+
const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3399
|
+
decipher.setAuthTag(authTag);
|
|
3400
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3401
|
+
decrypted += decipher.final("utf-8");
|
|
3402
|
+
return decrypted;
|
|
3403
|
+
} catch {
|
|
3404
|
+
return null;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3310
3407
|
async function getMasterKey() {
|
|
3408
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3409
|
+
if (nativeValue) {
|
|
3410
|
+
return Buffer.from(nativeValue, "base64");
|
|
3411
|
+
}
|
|
3311
3412
|
const keytar = await tryKeytar();
|
|
3312
3413
|
if (keytar) {
|
|
3313
3414
|
try {
|
|
3314
|
-
const
|
|
3315
|
-
if (
|
|
3316
|
-
|
|
3415
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3416
|
+
if (keytarValue) {
|
|
3417
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
3418
|
+
if (migrated) {
|
|
3419
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3420
|
+
}
|
|
3421
|
+
return Buffer.from(keytarValue, "base64");
|
|
3317
3422
|
}
|
|
3318
3423
|
} catch {
|
|
3319
3424
|
}
|
|
@@ -3327,8 +3432,31 @@ async function getMasterKey() {
|
|
|
3327
3432
|
return null;
|
|
3328
3433
|
}
|
|
3329
3434
|
try {
|
|
3330
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
3331
|
-
|
|
3435
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3436
|
+
let b64Value;
|
|
3437
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3438
|
+
const machineKey = deriveMachineKey();
|
|
3439
|
+
if (!machineKey) {
|
|
3440
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3441
|
+
return null;
|
|
3442
|
+
}
|
|
3443
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3444
|
+
if (!decrypted) {
|
|
3445
|
+
process.stderr.write(
|
|
3446
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
3447
|
+
);
|
|
3448
|
+
return null;
|
|
3449
|
+
}
|
|
3450
|
+
b64Value = decrypted;
|
|
3451
|
+
} else {
|
|
3452
|
+
b64Value = content;
|
|
3453
|
+
}
|
|
3454
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3455
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3456
|
+
if (migrated) {
|
|
3457
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3458
|
+
}
|
|
3459
|
+
return key;
|
|
3332
3460
|
} catch (err) {
|
|
3333
3461
|
process.stderr.write(
|
|
3334
3462
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1033,8 +1033,8 @@ function findPackageRoot() {
|
|
|
1033
1033
|
function getAvailableMemoryGB() {
|
|
1034
1034
|
if (process.platform === "darwin") {
|
|
1035
1035
|
try {
|
|
1036
|
-
const { execSync:
|
|
1037
|
-
const vmstat =
|
|
1036
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1037
|
+
const vmstat = execSync3("vm_stat", { encoding: "utf8" });
|
|
1038
1038
|
const pageSize = 16384;
|
|
1039
1039
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1040
1040
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -3289,6 +3289,7 @@ import { createHash } from "crypto";
|
|
|
3289
3289
|
// src/lib/keychain.ts
|
|
3290
3290
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3291
3291
|
import { existsSync as existsSync6 } from "fs";
|
|
3292
|
+
import { execSync as execSync2 } from "child_process";
|
|
3292
3293
|
import path6 from "path";
|
|
3293
3294
|
import os5 from "os";
|
|
3294
3295
|
var SERVICE = "exe-mem";
|
|
@@ -3299,6 +3300,59 @@ function getKeyDir() {
|
|
|
3299
3300
|
function getKeyPath() {
|
|
3300
3301
|
return path6.join(getKeyDir(), "master.key");
|
|
3301
3302
|
}
|
|
3303
|
+
function macKeychainGet() {
|
|
3304
|
+
if (process.platform !== "darwin") return null;
|
|
3305
|
+
try {
|
|
3306
|
+
return execSync2(
|
|
3307
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3308
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3309
|
+
).trim();
|
|
3310
|
+
} catch {
|
|
3311
|
+
return null;
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
function macKeychainSet(value) {
|
|
3315
|
+
if (process.platform !== "darwin") return false;
|
|
3316
|
+
try {
|
|
3317
|
+
try {
|
|
3318
|
+
execSync2(
|
|
3319
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3320
|
+
{ timeout: 5e3 }
|
|
3321
|
+
);
|
|
3322
|
+
} catch {
|
|
3323
|
+
}
|
|
3324
|
+
execSync2(
|
|
3325
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3326
|
+
{ timeout: 5e3 }
|
|
3327
|
+
);
|
|
3328
|
+
return true;
|
|
3329
|
+
} catch {
|
|
3330
|
+
return false;
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
function linuxSecretGet() {
|
|
3334
|
+
if (process.platform !== "linux") return null;
|
|
3335
|
+
try {
|
|
3336
|
+
return execSync2(
|
|
3337
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3338
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3339
|
+
).trim();
|
|
3340
|
+
} catch {
|
|
3341
|
+
return null;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
function linuxSecretSet(value) {
|
|
3345
|
+
if (process.platform !== "linux") return false;
|
|
3346
|
+
try {
|
|
3347
|
+
execSync2(
|
|
3348
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
3349
|
+
{ timeout: 5e3 }
|
|
3350
|
+
);
|
|
3351
|
+
return true;
|
|
3352
|
+
} catch {
|
|
3353
|
+
return false;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3302
3356
|
async function tryKeytar() {
|
|
3303
3357
|
try {
|
|
3304
3358
|
return await import("keytar");
|
|
@@ -3306,13 +3360,64 @@ async function tryKeytar() {
|
|
|
3306
3360
|
return null;
|
|
3307
3361
|
}
|
|
3308
3362
|
}
|
|
3363
|
+
var ENCRYPTED_PREFIX = "enc:";
|
|
3364
|
+
function deriveMachineKey() {
|
|
3365
|
+
try {
|
|
3366
|
+
const crypto3 = __require("crypto");
|
|
3367
|
+
const material = [
|
|
3368
|
+
os5.hostname(),
|
|
3369
|
+
os5.userInfo().username,
|
|
3370
|
+
os5.arch(),
|
|
3371
|
+
os5.platform(),
|
|
3372
|
+
// Machine ID on Linux (stable across reboots)
|
|
3373
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3374
|
+
].join("|");
|
|
3375
|
+
return crypto3.createHash("sha256").update(material).digest();
|
|
3376
|
+
} catch {
|
|
3377
|
+
return null;
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
function readMachineId() {
|
|
3381
|
+
try {
|
|
3382
|
+
const { readFileSync: readFileSync5 } = __require("fs");
|
|
3383
|
+
return readFileSync5("/etc/machine-id", "utf-8").trim();
|
|
3384
|
+
} catch {
|
|
3385
|
+
return "";
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3389
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3390
|
+
try {
|
|
3391
|
+
const crypto3 = __require("crypto");
|
|
3392
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3393
|
+
if (parts.length !== 3) return null;
|
|
3394
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3395
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3396
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3397
|
+
const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3398
|
+
decipher.setAuthTag(authTag);
|
|
3399
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3400
|
+
decrypted += decipher.final("utf-8");
|
|
3401
|
+
return decrypted;
|
|
3402
|
+
} catch {
|
|
3403
|
+
return null;
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3309
3406
|
async function getMasterKey() {
|
|
3407
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3408
|
+
if (nativeValue) {
|
|
3409
|
+
return Buffer.from(nativeValue, "base64");
|
|
3410
|
+
}
|
|
3310
3411
|
const keytar = await tryKeytar();
|
|
3311
3412
|
if (keytar) {
|
|
3312
3413
|
try {
|
|
3313
|
-
const
|
|
3314
|
-
if (
|
|
3315
|
-
|
|
3414
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3415
|
+
if (keytarValue) {
|
|
3416
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
3417
|
+
if (migrated) {
|
|
3418
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3419
|
+
}
|
|
3420
|
+
return Buffer.from(keytarValue, "base64");
|
|
3316
3421
|
}
|
|
3317
3422
|
} catch {
|
|
3318
3423
|
}
|
|
@@ -3326,8 +3431,31 @@ async function getMasterKey() {
|
|
|
3326
3431
|
return null;
|
|
3327
3432
|
}
|
|
3328
3433
|
try {
|
|
3329
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
3330
|
-
|
|
3434
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3435
|
+
let b64Value;
|
|
3436
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3437
|
+
const machineKey = deriveMachineKey();
|
|
3438
|
+
if (!machineKey) {
|
|
3439
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3440
|
+
return null;
|
|
3441
|
+
}
|
|
3442
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3443
|
+
if (!decrypted) {
|
|
3444
|
+
process.stderr.write(
|
|
3445
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
3446
|
+
);
|
|
3447
|
+
return null;
|
|
3448
|
+
}
|
|
3449
|
+
b64Value = decrypted;
|
|
3450
|
+
} else {
|
|
3451
|
+
b64Value = content;
|
|
3452
|
+
}
|
|
3453
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3454
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3455
|
+
if (migrated) {
|
|
3456
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3457
|
+
}
|
|
3458
|
+
return key;
|
|
3331
3459
|
} catch (err) {
|
|
3332
3460
|
process.stderr.write(
|
|
3333
3461
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -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;
|
|
@@ -3277,6 +3277,7 @@ import { createHash } from "crypto";
|
|
|
3277
3277
|
// src/lib/keychain.ts
|
|
3278
3278
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3279
3279
|
import { existsSync as existsSync6 } from "fs";
|
|
3280
|
+
import { execSync as execSync2 } from "child_process";
|
|
3280
3281
|
import path6 from "path";
|
|
3281
3282
|
import os5 from "os";
|
|
3282
3283
|
var SERVICE = "exe-mem";
|
|
@@ -3287,6 +3288,59 @@ function getKeyDir() {
|
|
|
3287
3288
|
function getKeyPath() {
|
|
3288
3289
|
return path6.join(getKeyDir(), "master.key");
|
|
3289
3290
|
}
|
|
3291
|
+
function macKeychainGet() {
|
|
3292
|
+
if (process.platform !== "darwin") return null;
|
|
3293
|
+
try {
|
|
3294
|
+
return execSync2(
|
|
3295
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3296
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3297
|
+
).trim();
|
|
3298
|
+
} catch {
|
|
3299
|
+
return null;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
function macKeychainSet(value) {
|
|
3303
|
+
if (process.platform !== "darwin") return false;
|
|
3304
|
+
try {
|
|
3305
|
+
try {
|
|
3306
|
+
execSync2(
|
|
3307
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3308
|
+
{ timeout: 5e3 }
|
|
3309
|
+
);
|
|
3310
|
+
} catch {
|
|
3311
|
+
}
|
|
3312
|
+
execSync2(
|
|
3313
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3314
|
+
{ timeout: 5e3 }
|
|
3315
|
+
);
|
|
3316
|
+
return true;
|
|
3317
|
+
} catch {
|
|
3318
|
+
return false;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
function linuxSecretGet() {
|
|
3322
|
+
if (process.platform !== "linux") return null;
|
|
3323
|
+
try {
|
|
3324
|
+
return execSync2(
|
|
3325
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3326
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3327
|
+
).trim();
|
|
3328
|
+
} catch {
|
|
3329
|
+
return null;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
function linuxSecretSet(value) {
|
|
3333
|
+
if (process.platform !== "linux") return false;
|
|
3334
|
+
try {
|
|
3335
|
+
execSync2(
|
|
3336
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
3337
|
+
{ timeout: 5e3 }
|
|
3338
|
+
);
|
|
3339
|
+
return true;
|
|
3340
|
+
} catch {
|
|
3341
|
+
return false;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3290
3344
|
async function tryKeytar() {
|
|
3291
3345
|
try {
|
|
3292
3346
|
return await import("keytar");
|
|
@@ -3294,13 +3348,64 @@ async function tryKeytar() {
|
|
|
3294
3348
|
return null;
|
|
3295
3349
|
}
|
|
3296
3350
|
}
|
|
3351
|
+
var ENCRYPTED_PREFIX = "enc:";
|
|
3352
|
+
function deriveMachineKey() {
|
|
3353
|
+
try {
|
|
3354
|
+
const crypto2 = __require("crypto");
|
|
3355
|
+
const material = [
|
|
3356
|
+
os5.hostname(),
|
|
3357
|
+
os5.userInfo().username,
|
|
3358
|
+
os5.arch(),
|
|
3359
|
+
os5.platform(),
|
|
3360
|
+
// Machine ID on Linux (stable across reboots)
|
|
3361
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3362
|
+
].join("|");
|
|
3363
|
+
return crypto2.createHash("sha256").update(material).digest();
|
|
3364
|
+
} catch {
|
|
3365
|
+
return null;
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
function readMachineId() {
|
|
3369
|
+
try {
|
|
3370
|
+
const { readFileSync: readFileSync5 } = __require("fs");
|
|
3371
|
+
return readFileSync5("/etc/machine-id", "utf-8").trim();
|
|
3372
|
+
} catch {
|
|
3373
|
+
return "";
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3377
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3378
|
+
try {
|
|
3379
|
+
const crypto2 = __require("crypto");
|
|
3380
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3381
|
+
if (parts.length !== 3) return null;
|
|
3382
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3383
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3384
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3385
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3386
|
+
decipher.setAuthTag(authTag);
|
|
3387
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3388
|
+
decrypted += decipher.final("utf-8");
|
|
3389
|
+
return decrypted;
|
|
3390
|
+
} catch {
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3297
3394
|
async function getMasterKey() {
|
|
3395
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3396
|
+
if (nativeValue) {
|
|
3397
|
+
return Buffer.from(nativeValue, "base64");
|
|
3398
|
+
}
|
|
3298
3399
|
const keytar = await tryKeytar();
|
|
3299
3400
|
if (keytar) {
|
|
3300
3401
|
try {
|
|
3301
|
-
const
|
|
3302
|
-
if (
|
|
3303
|
-
|
|
3402
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3403
|
+
if (keytarValue) {
|
|
3404
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
3405
|
+
if (migrated) {
|
|
3406
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3407
|
+
}
|
|
3408
|
+
return Buffer.from(keytarValue, "base64");
|
|
3304
3409
|
}
|
|
3305
3410
|
} catch {
|
|
3306
3411
|
}
|
|
@@ -3314,8 +3419,31 @@ async function getMasterKey() {
|
|
|
3314
3419
|
return null;
|
|
3315
3420
|
}
|
|
3316
3421
|
try {
|
|
3317
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
3318
|
-
|
|
3422
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3423
|
+
let b64Value;
|
|
3424
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3425
|
+
const machineKey = deriveMachineKey();
|
|
3426
|
+
if (!machineKey) {
|
|
3427
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3428
|
+
return null;
|
|
3429
|
+
}
|
|
3430
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3431
|
+
if (!decrypted) {
|
|
3432
|
+
process.stderr.write(
|
|
3433
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
3434
|
+
);
|
|
3435
|
+
return null;
|
|
3436
|
+
}
|
|
3437
|
+
b64Value = decrypted;
|
|
3438
|
+
} else {
|
|
3439
|
+
b64Value = content;
|
|
3440
|
+
}
|
|
3441
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3442
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3443
|
+
if (migrated) {
|
|
3444
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3445
|
+
}
|
|
3446
|
+
return key;
|
|
3319
3447
|
} catch (err) {
|
|
3320
3448
|
process.stderr.write(
|
|
3321
3449
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|