@askexenow/exe-os 0.9.53 → 0.9.55
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 +103 -0
- package/dist/bin/backfill-responses.js +103 -0
- package/dist/bin/backfill-vectors.js +103 -0
- package/dist/bin/cleanup-stale-review-tasks.js +103 -0
- package/dist/bin/cli.js +121 -10
- package/dist/bin/exe-assign.js +103 -0
- package/dist/bin/exe-boot.js +96 -10
- package/dist/bin/exe-call.js +25 -0
- package/dist/bin/exe-cloud.js +31 -10
- package/dist/bin/exe-dispatch.js +103 -0
- package/dist/bin/exe-doctor.js +152 -3
- package/dist/bin/exe-export-behaviors.js +103 -0
- package/dist/bin/exe-forget.js +103 -0
- package/dist/bin/exe-gateway.js +103 -0
- package/dist/bin/exe-heartbeat.js +103 -0
- package/dist/bin/exe-kill.js +103 -0
- package/dist/bin/exe-launch-agent.js +103 -0
- package/dist/bin/exe-link.js +31 -10
- package/dist/bin/exe-new-employee.js +25 -0
- package/dist/bin/exe-pending-messages.js +103 -0
- package/dist/bin/exe-pending-notifications.js +103 -0
- package/dist/bin/exe-pending-reviews.js +103 -0
- package/dist/bin/exe-rename.js +103 -0
- package/dist/bin/exe-review.js +103 -0
- package/dist/bin/exe-search.js +103 -0
- package/dist/bin/exe-session-cleanup.js +103 -0
- package/dist/bin/exe-start-codex.js +103 -0
- package/dist/bin/exe-start-opencode.js +103 -0
- package/dist/bin/exe-status.js +103 -0
- package/dist/bin/exe-team.js +103 -0
- package/dist/bin/git-sweep.js +103 -0
- package/dist/bin/graph-backfill.js +103 -0
- package/dist/bin/graph-export.js +103 -0
- package/dist/bin/intercom-check.js +103 -0
- package/dist/bin/scan-tasks.js +103 -0
- package/dist/bin/setup.js +56 -10
- package/dist/bin/shard-migrate.js +103 -0
- package/dist/gateway/index.js +103 -0
- package/dist/hooks/bug-report-worker.js +103 -0
- package/dist/hooks/codex-stop-task-finalizer.js +103 -0
- package/dist/hooks/commit-complete.js +103 -0
- package/dist/hooks/error-recall.js +103 -0
- package/dist/hooks/ingest.js +103 -0
- package/dist/hooks/instructions-loaded.js +103 -0
- package/dist/hooks/notification.js +103 -0
- package/dist/hooks/post-compact.js +103 -0
- package/dist/hooks/post-tool-combined.js +103 -0
- package/dist/hooks/pre-compact.js +103 -0
- package/dist/hooks/pre-tool-use.js +103 -0
- package/dist/hooks/prompt-submit.js +103 -0
- package/dist/hooks/session-end.js +103 -0
- package/dist/hooks/session-start.js +103 -0
- package/dist/hooks/stop.js +103 -0
- package/dist/hooks/subagent-stop.js +103 -0
- package/dist/hooks/summary-worker.js +96 -10
- package/dist/index.js +103 -0
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/employee-templates.js +25 -0
- package/dist/lib/exe-daemon.js +165 -14
- package/dist/lib/hybrid-search.js +103 -0
- package/dist/lib/keychain.js +31 -10
- package/dist/lib/schedules.js +103 -0
- package/dist/lib/store.js +103 -0
- package/dist/mcp/server.js +164 -13
- package/dist/runtime/index.js +103 -0
- package/dist/tui/App.js +96 -10
- package/package.json +1 -1
|
@@ -3382,6 +3382,15 @@ function readMachineId() {
|
|
|
3382
3382
|
return "";
|
|
3383
3383
|
}
|
|
3384
3384
|
}
|
|
3385
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3386
|
+
const crypto3 = __require("crypto");
|
|
3387
|
+
const iv = crypto3.randomBytes(12);
|
|
3388
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3389
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3390
|
+
encrypted += cipher.final("base64");
|
|
3391
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3392
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3393
|
+
}
|
|
3385
3394
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3386
3395
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3387
3396
|
try {
|
|
@@ -3400,6 +3409,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3400
3409
|
return null;
|
|
3401
3410
|
}
|
|
3402
3411
|
}
|
|
3412
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3413
|
+
const dir = getKeyDir();
|
|
3414
|
+
await mkdir3(dir, { recursive: true });
|
|
3415
|
+
const keyPath = getKeyPath();
|
|
3416
|
+
const machineKey = deriveMachineKey();
|
|
3417
|
+
if (machineKey) {
|
|
3418
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3419
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3420
|
+
await chmod2(keyPath, 384);
|
|
3421
|
+
return "encrypted";
|
|
3422
|
+
}
|
|
3423
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3424
|
+
await chmod2(keyPath, 384);
|
|
3425
|
+
return "plaintext";
|
|
3426
|
+
}
|
|
3403
3427
|
async function getMasterKey() {
|
|
3404
3428
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3405
3429
|
if (nativeValue) {
|
|
@@ -3451,6 +3475,20 @@ async function getMasterKey() {
|
|
|
3451
3475
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3452
3476
|
if (migrated) {
|
|
3453
3477
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3478
|
+
try {
|
|
3479
|
+
await unlink(keyPath);
|
|
3480
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3481
|
+
} catch {
|
|
3482
|
+
}
|
|
3483
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3484
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3485
|
+
if (fallback === "encrypted") {
|
|
3486
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3487
|
+
} else {
|
|
3488
|
+
process.stderr.write(
|
|
3489
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3490
|
+
);
|
|
3491
|
+
}
|
|
3454
3492
|
}
|
|
3455
3493
|
return key;
|
|
3456
3494
|
} catch (err) {
|
|
@@ -3723,6 +3761,7 @@ var init_memory_write_governor = __esm({
|
|
|
3723
3761
|
// src/lib/shard-manager.ts
|
|
3724
3762
|
var shard_manager_exports = {};
|
|
3725
3763
|
__export(shard_manager_exports, {
|
|
3764
|
+
auditShardHealth: () => auditShardHealth,
|
|
3726
3765
|
disposeShards: () => disposeShards,
|
|
3727
3766
|
ensureShardSchema: () => ensureShardSchema,
|
|
3728
3767
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3789,6 +3828,70 @@ function listShards() {
|
|
|
3789
3828
|
if (!existsSync13(SHARDS_DIR)) return [];
|
|
3790
3829
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3791
3830
|
}
|
|
3831
|
+
async function auditShardHealth(options = {}) {
|
|
3832
|
+
if (!_encryptionKey) {
|
|
3833
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3834
|
+
}
|
|
3835
|
+
const repair = options.repair === true;
|
|
3836
|
+
const dryRun = options.dryRun === true;
|
|
3837
|
+
const names = listShards();
|
|
3838
|
+
const shards = [];
|
|
3839
|
+
for (const name of names) {
|
|
3840
|
+
const dbPath = path14.join(SHARDS_DIR, `${name}.db`);
|
|
3841
|
+
const stat = statSync2(dbPath);
|
|
3842
|
+
const item = {
|
|
3843
|
+
name,
|
|
3844
|
+
path: dbPath,
|
|
3845
|
+
ok: false,
|
|
3846
|
+
unreadable: false,
|
|
3847
|
+
error: null,
|
|
3848
|
+
size: stat.size,
|
|
3849
|
+
mtime: stat.mtime.toISOString(),
|
|
3850
|
+
memoryCount: null
|
|
3851
|
+
};
|
|
3852
|
+
const client = createClient2({
|
|
3853
|
+
url: `file:${dbPath}`,
|
|
3854
|
+
encryptionKey: _encryptionKey
|
|
3855
|
+
});
|
|
3856
|
+
try {
|
|
3857
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3858
|
+
const hasMemories = await client.execute(
|
|
3859
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3860
|
+
);
|
|
3861
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3862
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3863
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3864
|
+
}
|
|
3865
|
+
item.ok = true;
|
|
3866
|
+
} catch (err) {
|
|
3867
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3868
|
+
item.error = message;
|
|
3869
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3870
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3871
|
+
client.close();
|
|
3872
|
+
_shards.delete(name);
|
|
3873
|
+
_shardLastAccess.delete(name);
|
|
3874
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3875
|
+
const archivedPath = path14.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3876
|
+
renameSync4(dbPath, archivedPath);
|
|
3877
|
+
item.archivedPath = archivedPath;
|
|
3878
|
+
}
|
|
3879
|
+
} finally {
|
|
3880
|
+
try {
|
|
3881
|
+
client.close();
|
|
3882
|
+
} catch {
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
shards.push(item);
|
|
3886
|
+
}
|
|
3887
|
+
return {
|
|
3888
|
+
total: shards.length,
|
|
3889
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3890
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3891
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3892
|
+
shards
|
|
3893
|
+
};
|
|
3894
|
+
}
|
|
3792
3895
|
async function ensureShardSchema(client) {
|
|
3793
3896
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3794
3897
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3476,6 +3476,15 @@ function readMachineId() {
|
|
|
3476
3476
|
return "";
|
|
3477
3477
|
}
|
|
3478
3478
|
}
|
|
3479
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3480
|
+
const crypto3 = __require("crypto");
|
|
3481
|
+
const iv = crypto3.randomBytes(12);
|
|
3482
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3483
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3484
|
+
encrypted += cipher.final("base64");
|
|
3485
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3486
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3487
|
+
}
|
|
3479
3488
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3480
3489
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3481
3490
|
try {
|
|
@@ -3494,6 +3503,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3494
3503
|
return null;
|
|
3495
3504
|
}
|
|
3496
3505
|
}
|
|
3506
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3507
|
+
const dir = getKeyDir();
|
|
3508
|
+
await mkdir3(dir, { recursive: true });
|
|
3509
|
+
const keyPath = getKeyPath();
|
|
3510
|
+
const machineKey = deriveMachineKey();
|
|
3511
|
+
if (machineKey) {
|
|
3512
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3513
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3514
|
+
await chmod2(keyPath, 384);
|
|
3515
|
+
return "encrypted";
|
|
3516
|
+
}
|
|
3517
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3518
|
+
await chmod2(keyPath, 384);
|
|
3519
|
+
return "plaintext";
|
|
3520
|
+
}
|
|
3497
3521
|
async function getMasterKey() {
|
|
3498
3522
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3499
3523
|
if (nativeValue) {
|
|
@@ -3545,6 +3569,20 @@ async function getMasterKey() {
|
|
|
3545
3569
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3546
3570
|
if (migrated) {
|
|
3547
3571
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3572
|
+
try {
|
|
3573
|
+
await unlink(keyPath);
|
|
3574
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3575
|
+
} catch {
|
|
3576
|
+
}
|
|
3577
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3578
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3579
|
+
if (fallback === "encrypted") {
|
|
3580
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3581
|
+
} else {
|
|
3582
|
+
process.stderr.write(
|
|
3583
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3548
3586
|
}
|
|
3549
3587
|
return key;
|
|
3550
3588
|
} catch (err) {
|
|
@@ -3762,6 +3800,7 @@ var init_memory_write_governor = __esm({
|
|
|
3762
3800
|
// src/lib/shard-manager.ts
|
|
3763
3801
|
var shard_manager_exports = {};
|
|
3764
3802
|
__export(shard_manager_exports, {
|
|
3803
|
+
auditShardHealth: () => auditShardHealth,
|
|
3765
3804
|
disposeShards: () => disposeShards,
|
|
3766
3805
|
ensureShardSchema: () => ensureShardSchema,
|
|
3767
3806
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3828,6 +3867,70 @@ function listShards() {
|
|
|
3828
3867
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
3829
3868
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3830
3869
|
}
|
|
3870
|
+
async function auditShardHealth(options = {}) {
|
|
3871
|
+
if (!_encryptionKey) {
|
|
3872
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3873
|
+
}
|
|
3874
|
+
const repair = options.repair === true;
|
|
3875
|
+
const dryRun = options.dryRun === true;
|
|
3876
|
+
const names = listShards();
|
|
3877
|
+
const shards = [];
|
|
3878
|
+
for (const name of names) {
|
|
3879
|
+
const dbPath = path15.join(SHARDS_DIR, `${name}.db`);
|
|
3880
|
+
const stat = statSync2(dbPath);
|
|
3881
|
+
const item = {
|
|
3882
|
+
name,
|
|
3883
|
+
path: dbPath,
|
|
3884
|
+
ok: false,
|
|
3885
|
+
unreadable: false,
|
|
3886
|
+
error: null,
|
|
3887
|
+
size: stat.size,
|
|
3888
|
+
mtime: stat.mtime.toISOString(),
|
|
3889
|
+
memoryCount: null
|
|
3890
|
+
};
|
|
3891
|
+
const client = createClient2({
|
|
3892
|
+
url: `file:${dbPath}`,
|
|
3893
|
+
encryptionKey: _encryptionKey
|
|
3894
|
+
});
|
|
3895
|
+
try {
|
|
3896
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3897
|
+
const hasMemories = await client.execute(
|
|
3898
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3899
|
+
);
|
|
3900
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3901
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3902
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3903
|
+
}
|
|
3904
|
+
item.ok = true;
|
|
3905
|
+
} catch (err) {
|
|
3906
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3907
|
+
item.error = message;
|
|
3908
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3909
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3910
|
+
client.close();
|
|
3911
|
+
_shards.delete(name);
|
|
3912
|
+
_shardLastAccess.delete(name);
|
|
3913
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3914
|
+
const archivedPath = path15.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3915
|
+
renameSync4(dbPath, archivedPath);
|
|
3916
|
+
item.archivedPath = archivedPath;
|
|
3917
|
+
}
|
|
3918
|
+
} finally {
|
|
3919
|
+
try {
|
|
3920
|
+
client.close();
|
|
3921
|
+
} catch {
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
shards.push(item);
|
|
3925
|
+
}
|
|
3926
|
+
return {
|
|
3927
|
+
total: shards.length,
|
|
3928
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3929
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3930
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3931
|
+
shards
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3831
3934
|
async function ensureShardSchema(client) {
|
|
3832
3935
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3833
3936
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-rename.js
CHANGED
|
@@ -3007,6 +3007,15 @@ function readMachineId() {
|
|
|
3007
3007
|
return "";
|
|
3008
3008
|
}
|
|
3009
3009
|
}
|
|
3010
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3011
|
+
const crypto2 = __require("crypto");
|
|
3012
|
+
const iv = crypto2.randomBytes(12);
|
|
3013
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3014
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3015
|
+
encrypted += cipher.final("base64");
|
|
3016
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3017
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3018
|
+
}
|
|
3010
3019
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3011
3020
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3012
3021
|
try {
|
|
@@ -3025,6 +3034,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3025
3034
|
return null;
|
|
3026
3035
|
}
|
|
3027
3036
|
}
|
|
3037
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3038
|
+
const dir = getKeyDir();
|
|
3039
|
+
await mkdir3(dir, { recursive: true });
|
|
3040
|
+
const keyPath = getKeyPath();
|
|
3041
|
+
const machineKey = deriveMachineKey();
|
|
3042
|
+
if (machineKey) {
|
|
3043
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3044
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3045
|
+
await chmod2(keyPath, 384);
|
|
3046
|
+
return "encrypted";
|
|
3047
|
+
}
|
|
3048
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3049
|
+
await chmod2(keyPath, 384);
|
|
3050
|
+
return "plaintext";
|
|
3051
|
+
}
|
|
3028
3052
|
async function getMasterKey() {
|
|
3029
3053
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3030
3054
|
if (nativeValue) {
|
|
@@ -3076,6 +3100,20 @@ async function getMasterKey() {
|
|
|
3076
3100
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3077
3101
|
if (migrated) {
|
|
3078
3102
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3103
|
+
try {
|
|
3104
|
+
await unlink(keyPath);
|
|
3105
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3106
|
+
} catch {
|
|
3107
|
+
}
|
|
3108
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3109
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3110
|
+
if (fallback === "encrypted") {
|
|
3111
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3112
|
+
} else {
|
|
3113
|
+
process.stderr.write(
|
|
3114
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3079
3117
|
}
|
|
3080
3118
|
return key;
|
|
3081
3119
|
} catch (err) {
|
|
@@ -3348,6 +3386,7 @@ var init_memory_write_governor = __esm({
|
|
|
3348
3386
|
// src/lib/shard-manager.ts
|
|
3349
3387
|
var shard_manager_exports = {};
|
|
3350
3388
|
__export(shard_manager_exports, {
|
|
3389
|
+
auditShardHealth: () => auditShardHealth,
|
|
3351
3390
|
disposeShards: () => disposeShards,
|
|
3352
3391
|
ensureShardSchema: () => ensureShardSchema,
|
|
3353
3392
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3414,6 +3453,70 @@ function listShards() {
|
|
|
3414
3453
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3415
3454
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3416
3455
|
}
|
|
3456
|
+
async function auditShardHealth(options = {}) {
|
|
3457
|
+
if (!_encryptionKey) {
|
|
3458
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3459
|
+
}
|
|
3460
|
+
const repair = options.repair === true;
|
|
3461
|
+
const dryRun = options.dryRun === true;
|
|
3462
|
+
const names = listShards();
|
|
3463
|
+
const shards = [];
|
|
3464
|
+
for (const name of names) {
|
|
3465
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3466
|
+
const stat = statSync2(dbPath);
|
|
3467
|
+
const item = {
|
|
3468
|
+
name,
|
|
3469
|
+
path: dbPath,
|
|
3470
|
+
ok: false,
|
|
3471
|
+
unreadable: false,
|
|
3472
|
+
error: null,
|
|
3473
|
+
size: stat.size,
|
|
3474
|
+
mtime: stat.mtime.toISOString(),
|
|
3475
|
+
memoryCount: null
|
|
3476
|
+
};
|
|
3477
|
+
const client = createClient2({
|
|
3478
|
+
url: `file:${dbPath}`,
|
|
3479
|
+
encryptionKey: _encryptionKey
|
|
3480
|
+
});
|
|
3481
|
+
try {
|
|
3482
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3483
|
+
const hasMemories = await client.execute(
|
|
3484
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3485
|
+
);
|
|
3486
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3487
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3488
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3489
|
+
}
|
|
3490
|
+
item.ok = true;
|
|
3491
|
+
} catch (err) {
|
|
3492
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3493
|
+
item.error = message;
|
|
3494
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3495
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3496
|
+
client.close();
|
|
3497
|
+
_shards.delete(name);
|
|
3498
|
+
_shardLastAccess.delete(name);
|
|
3499
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3500
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3501
|
+
renameSync3(dbPath, archivedPath);
|
|
3502
|
+
item.archivedPath = archivedPath;
|
|
3503
|
+
}
|
|
3504
|
+
} finally {
|
|
3505
|
+
try {
|
|
3506
|
+
client.close();
|
|
3507
|
+
} catch {
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
shards.push(item);
|
|
3511
|
+
}
|
|
3512
|
+
return {
|
|
3513
|
+
total: shards.length,
|
|
3514
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3515
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3516
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3517
|
+
shards
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3417
3520
|
async function ensureShardSchema(client) {
|
|
3418
3521
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3419
3522
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-review.js
CHANGED
|
@@ -2911,6 +2911,15 @@ function readMachineId() {
|
|
|
2911
2911
|
return "";
|
|
2912
2912
|
}
|
|
2913
2913
|
}
|
|
2914
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2915
|
+
const crypto2 = __require("crypto");
|
|
2916
|
+
const iv = crypto2.randomBytes(12);
|
|
2917
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2918
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2919
|
+
encrypted += cipher.final("base64");
|
|
2920
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2921
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2922
|
+
}
|
|
2914
2923
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2915
2924
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2916
2925
|
try {
|
|
@@ -2929,6 +2938,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2929
2938
|
return null;
|
|
2930
2939
|
}
|
|
2931
2940
|
}
|
|
2941
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2942
|
+
const dir = getKeyDir();
|
|
2943
|
+
await mkdir3(dir, { recursive: true });
|
|
2944
|
+
const keyPath = getKeyPath();
|
|
2945
|
+
const machineKey = deriveMachineKey();
|
|
2946
|
+
if (machineKey) {
|
|
2947
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2948
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2949
|
+
await chmod2(keyPath, 384);
|
|
2950
|
+
return "encrypted";
|
|
2951
|
+
}
|
|
2952
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2953
|
+
await chmod2(keyPath, 384);
|
|
2954
|
+
return "plaintext";
|
|
2955
|
+
}
|
|
2932
2956
|
async function getMasterKey() {
|
|
2933
2957
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2934
2958
|
if (nativeValue) {
|
|
@@ -2980,6 +3004,20 @@ async function getMasterKey() {
|
|
|
2980
3004
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2981
3005
|
if (migrated) {
|
|
2982
3006
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3007
|
+
try {
|
|
3008
|
+
await unlink(keyPath);
|
|
3009
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3010
|
+
} catch {
|
|
3011
|
+
}
|
|
3012
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3013
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3014
|
+
if (fallback === "encrypted") {
|
|
3015
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3016
|
+
} else {
|
|
3017
|
+
process.stderr.write(
|
|
3018
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
2983
3021
|
}
|
|
2984
3022
|
return key;
|
|
2985
3023
|
} catch (err) {
|
|
@@ -3252,6 +3290,7 @@ var init_memory_write_governor = __esm({
|
|
|
3252
3290
|
// src/lib/shard-manager.ts
|
|
3253
3291
|
var shard_manager_exports = {};
|
|
3254
3292
|
__export(shard_manager_exports, {
|
|
3293
|
+
auditShardHealth: () => auditShardHealth,
|
|
3255
3294
|
disposeShards: () => disposeShards,
|
|
3256
3295
|
ensureShardSchema: () => ensureShardSchema,
|
|
3257
3296
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3318,6 +3357,70 @@ function listShards() {
|
|
|
3318
3357
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3319
3358
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3320
3359
|
}
|
|
3360
|
+
async function auditShardHealth(options = {}) {
|
|
3361
|
+
if (!_encryptionKey) {
|
|
3362
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3363
|
+
}
|
|
3364
|
+
const repair = options.repair === true;
|
|
3365
|
+
const dryRun = options.dryRun === true;
|
|
3366
|
+
const names = listShards();
|
|
3367
|
+
const shards = [];
|
|
3368
|
+
for (const name of names) {
|
|
3369
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3370
|
+
const stat = statSync2(dbPath);
|
|
3371
|
+
const item = {
|
|
3372
|
+
name,
|
|
3373
|
+
path: dbPath,
|
|
3374
|
+
ok: false,
|
|
3375
|
+
unreadable: false,
|
|
3376
|
+
error: null,
|
|
3377
|
+
size: stat.size,
|
|
3378
|
+
mtime: stat.mtime.toISOString(),
|
|
3379
|
+
memoryCount: null
|
|
3380
|
+
};
|
|
3381
|
+
const client = createClient2({
|
|
3382
|
+
url: `file:${dbPath}`,
|
|
3383
|
+
encryptionKey: _encryptionKey
|
|
3384
|
+
});
|
|
3385
|
+
try {
|
|
3386
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3387
|
+
const hasMemories = await client.execute(
|
|
3388
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3389
|
+
);
|
|
3390
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3391
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3392
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3393
|
+
}
|
|
3394
|
+
item.ok = true;
|
|
3395
|
+
} catch (err) {
|
|
3396
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3397
|
+
item.error = message;
|
|
3398
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3399
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3400
|
+
client.close();
|
|
3401
|
+
_shards.delete(name);
|
|
3402
|
+
_shardLastAccess.delete(name);
|
|
3403
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3404
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3405
|
+
renameSync3(dbPath, archivedPath);
|
|
3406
|
+
item.archivedPath = archivedPath;
|
|
3407
|
+
}
|
|
3408
|
+
} finally {
|
|
3409
|
+
try {
|
|
3410
|
+
client.close();
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
shards.push(item);
|
|
3415
|
+
}
|
|
3416
|
+
return {
|
|
3417
|
+
total: shards.length,
|
|
3418
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3419
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3420
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3421
|
+
shards
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3321
3424
|
async function ensureShardSchema(client) {
|
|
3322
3425
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3323
3426
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-search.js
CHANGED
|
@@ -2886,6 +2886,15 @@ function readMachineId() {
|
|
|
2886
2886
|
return "";
|
|
2887
2887
|
}
|
|
2888
2888
|
}
|
|
2889
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2890
|
+
const crypto3 = __require("crypto");
|
|
2891
|
+
const iv = crypto3.randomBytes(12);
|
|
2892
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2893
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2894
|
+
encrypted += cipher.final("base64");
|
|
2895
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2896
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2897
|
+
}
|
|
2889
2898
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2890
2899
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2891
2900
|
try {
|
|
@@ -2904,6 +2913,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2904
2913
|
return null;
|
|
2905
2914
|
}
|
|
2906
2915
|
}
|
|
2916
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2917
|
+
const dir = getKeyDir();
|
|
2918
|
+
await mkdir3(dir, { recursive: true });
|
|
2919
|
+
const keyPath = getKeyPath();
|
|
2920
|
+
const machineKey = deriveMachineKey();
|
|
2921
|
+
if (machineKey) {
|
|
2922
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2923
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2924
|
+
await chmod2(keyPath, 384);
|
|
2925
|
+
return "encrypted";
|
|
2926
|
+
}
|
|
2927
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2928
|
+
await chmod2(keyPath, 384);
|
|
2929
|
+
return "plaintext";
|
|
2930
|
+
}
|
|
2907
2931
|
async function getMasterKey() {
|
|
2908
2932
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2909
2933
|
if (nativeValue) {
|
|
@@ -2955,6 +2979,20 @@ async function getMasterKey() {
|
|
|
2955
2979
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2956
2980
|
if (migrated) {
|
|
2957
2981
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2982
|
+
try {
|
|
2983
|
+
await unlink(keyPath);
|
|
2984
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2985
|
+
} catch {
|
|
2986
|
+
}
|
|
2987
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2988
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
2989
|
+
if (fallback === "encrypted") {
|
|
2990
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
2991
|
+
} else {
|
|
2992
|
+
process.stderr.write(
|
|
2993
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2958
2996
|
}
|
|
2959
2997
|
return key;
|
|
2960
2998
|
} catch (err) {
|
|
@@ -3227,6 +3265,7 @@ var init_memory_write_governor = __esm({
|
|
|
3227
3265
|
// src/lib/shard-manager.ts
|
|
3228
3266
|
var shard_manager_exports = {};
|
|
3229
3267
|
__export(shard_manager_exports, {
|
|
3268
|
+
auditShardHealth: () => auditShardHealth,
|
|
3230
3269
|
disposeShards: () => disposeShards,
|
|
3231
3270
|
ensureShardSchema: () => ensureShardSchema,
|
|
3232
3271
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3293,6 +3332,70 @@ function listShards() {
|
|
|
3293
3332
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3294
3333
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3295
3334
|
}
|
|
3335
|
+
async function auditShardHealth(options = {}) {
|
|
3336
|
+
if (!_encryptionKey) {
|
|
3337
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3338
|
+
}
|
|
3339
|
+
const repair = options.repair === true;
|
|
3340
|
+
const dryRun = options.dryRun === true;
|
|
3341
|
+
const names = listShards();
|
|
3342
|
+
const shards = [];
|
|
3343
|
+
for (const name of names) {
|
|
3344
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3345
|
+
const stat = statSync2(dbPath);
|
|
3346
|
+
const item = {
|
|
3347
|
+
name,
|
|
3348
|
+
path: dbPath,
|
|
3349
|
+
ok: false,
|
|
3350
|
+
unreadable: false,
|
|
3351
|
+
error: null,
|
|
3352
|
+
size: stat.size,
|
|
3353
|
+
mtime: stat.mtime.toISOString(),
|
|
3354
|
+
memoryCount: null
|
|
3355
|
+
};
|
|
3356
|
+
const client = createClient2({
|
|
3357
|
+
url: `file:${dbPath}`,
|
|
3358
|
+
encryptionKey: _encryptionKey
|
|
3359
|
+
});
|
|
3360
|
+
try {
|
|
3361
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3362
|
+
const hasMemories = await client.execute(
|
|
3363
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3364
|
+
);
|
|
3365
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3366
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3367
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3368
|
+
}
|
|
3369
|
+
item.ok = true;
|
|
3370
|
+
} catch (err) {
|
|
3371
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3372
|
+
item.error = message;
|
|
3373
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3374
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3375
|
+
client.close();
|
|
3376
|
+
_shards.delete(name);
|
|
3377
|
+
_shardLastAccess.delete(name);
|
|
3378
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3379
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3380
|
+
renameSync3(dbPath, archivedPath);
|
|
3381
|
+
item.archivedPath = archivedPath;
|
|
3382
|
+
}
|
|
3383
|
+
} finally {
|
|
3384
|
+
try {
|
|
3385
|
+
client.close();
|
|
3386
|
+
} catch {
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
shards.push(item);
|
|
3390
|
+
}
|
|
3391
|
+
return {
|
|
3392
|
+
total: shards.length,
|
|
3393
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3394
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3395
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3396
|
+
shards
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3296
3399
|
async function ensureShardSchema(client) {
|
|
3297
3400
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3298
3401
|
await client.execute("PRAGMA busy_timeout = 30000");
|