@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
|
@@ -3319,6 +3319,15 @@ function readMachineId() {
|
|
|
3319
3319
|
return "";
|
|
3320
3320
|
}
|
|
3321
3321
|
}
|
|
3322
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3323
|
+
const crypto2 = __require("crypto");
|
|
3324
|
+
const iv = crypto2.randomBytes(12);
|
|
3325
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3326
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3327
|
+
encrypted += cipher.final("base64");
|
|
3328
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3329
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3330
|
+
}
|
|
3322
3331
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3323
3332
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3324
3333
|
try {
|
|
@@ -3337,6 +3346,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3337
3346
|
return null;
|
|
3338
3347
|
}
|
|
3339
3348
|
}
|
|
3349
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3350
|
+
const dir = getKeyDir();
|
|
3351
|
+
await mkdir3(dir, { recursive: true });
|
|
3352
|
+
const keyPath = getKeyPath();
|
|
3353
|
+
const machineKey = deriveMachineKey();
|
|
3354
|
+
if (machineKey) {
|
|
3355
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3356
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3357
|
+
await chmod2(keyPath, 384);
|
|
3358
|
+
return "encrypted";
|
|
3359
|
+
}
|
|
3360
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3361
|
+
await chmod2(keyPath, 384);
|
|
3362
|
+
return "plaintext";
|
|
3363
|
+
}
|
|
3340
3364
|
async function getMasterKey() {
|
|
3341
3365
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3342
3366
|
if (nativeValue) {
|
|
@@ -3388,6 +3412,20 @@ async function getMasterKey() {
|
|
|
3388
3412
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3389
3413
|
if (migrated) {
|
|
3390
3414
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3415
|
+
try {
|
|
3416
|
+
await unlink(keyPath);
|
|
3417
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3418
|
+
} catch {
|
|
3419
|
+
}
|
|
3420
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3421
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3422
|
+
if (fallback === "encrypted") {
|
|
3423
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3424
|
+
} else {
|
|
3425
|
+
process.stderr.write(
|
|
3426
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3391
3429
|
}
|
|
3392
3430
|
return key;
|
|
3393
3431
|
} catch (err) {
|
|
@@ -3660,6 +3698,7 @@ var init_memory_write_governor = __esm({
|
|
|
3660
3698
|
// src/lib/shard-manager.ts
|
|
3661
3699
|
var shard_manager_exports = {};
|
|
3662
3700
|
__export(shard_manager_exports, {
|
|
3701
|
+
auditShardHealth: () => auditShardHealth,
|
|
3663
3702
|
disposeShards: () => disposeShards,
|
|
3664
3703
|
ensureShardSchema: () => ensureShardSchema,
|
|
3665
3704
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3726,6 +3765,70 @@ function listShards() {
|
|
|
3726
3765
|
if (!existsSync12(SHARDS_DIR)) return [];
|
|
3727
3766
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3728
3767
|
}
|
|
3768
|
+
async function auditShardHealth(options = {}) {
|
|
3769
|
+
if (!_encryptionKey) {
|
|
3770
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3771
|
+
}
|
|
3772
|
+
const repair = options.repair === true;
|
|
3773
|
+
const dryRun = options.dryRun === true;
|
|
3774
|
+
const names = listShards();
|
|
3775
|
+
const shards = [];
|
|
3776
|
+
for (const name of names) {
|
|
3777
|
+
const dbPath = path14.join(SHARDS_DIR, `${name}.db`);
|
|
3778
|
+
const stat = statSync2(dbPath);
|
|
3779
|
+
const item = {
|
|
3780
|
+
name,
|
|
3781
|
+
path: dbPath,
|
|
3782
|
+
ok: false,
|
|
3783
|
+
unreadable: false,
|
|
3784
|
+
error: null,
|
|
3785
|
+
size: stat.size,
|
|
3786
|
+
mtime: stat.mtime.toISOString(),
|
|
3787
|
+
memoryCount: null
|
|
3788
|
+
};
|
|
3789
|
+
const client = createClient2({
|
|
3790
|
+
url: `file:${dbPath}`,
|
|
3791
|
+
encryptionKey: _encryptionKey
|
|
3792
|
+
});
|
|
3793
|
+
try {
|
|
3794
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3795
|
+
const hasMemories = await client.execute(
|
|
3796
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3797
|
+
);
|
|
3798
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3799
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3800
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3801
|
+
}
|
|
3802
|
+
item.ok = true;
|
|
3803
|
+
} catch (err) {
|
|
3804
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3805
|
+
item.error = message;
|
|
3806
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3807
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3808
|
+
client.close();
|
|
3809
|
+
_shards.delete(name);
|
|
3810
|
+
_shardLastAccess.delete(name);
|
|
3811
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3812
|
+
const archivedPath = path14.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3813
|
+
renameSync4(dbPath, archivedPath);
|
|
3814
|
+
item.archivedPath = archivedPath;
|
|
3815
|
+
}
|
|
3816
|
+
} finally {
|
|
3817
|
+
try {
|
|
3818
|
+
client.close();
|
|
3819
|
+
} catch {
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
shards.push(item);
|
|
3823
|
+
}
|
|
3824
|
+
return {
|
|
3825
|
+
total: shards.length,
|
|
3826
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3827
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3828
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3829
|
+
shards
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3729
3832
|
async function ensureShardSchema(client) {
|
|
3730
3833
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3731
3834
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3063,6 +3063,15 @@ function readMachineId() {
|
|
|
3063
3063
|
return "";
|
|
3064
3064
|
}
|
|
3065
3065
|
}
|
|
3066
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3067
|
+
const crypto4 = __require("crypto");
|
|
3068
|
+
const iv = crypto4.randomBytes(12);
|
|
3069
|
+
const cipher = crypto4.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3070
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3071
|
+
encrypted += cipher.final("base64");
|
|
3072
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3073
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3074
|
+
}
|
|
3066
3075
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3067
3076
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3068
3077
|
try {
|
|
@@ -3081,6 +3090,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3081
3090
|
return null;
|
|
3082
3091
|
}
|
|
3083
3092
|
}
|
|
3093
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3094
|
+
const dir = getKeyDir();
|
|
3095
|
+
await mkdir3(dir, { recursive: true });
|
|
3096
|
+
const keyPath = getKeyPath();
|
|
3097
|
+
const machineKey = deriveMachineKey();
|
|
3098
|
+
if (machineKey) {
|
|
3099
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3100
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3101
|
+
await chmod2(keyPath, 384);
|
|
3102
|
+
return "encrypted";
|
|
3103
|
+
}
|
|
3104
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3105
|
+
await chmod2(keyPath, 384);
|
|
3106
|
+
return "plaintext";
|
|
3107
|
+
}
|
|
3084
3108
|
async function getMasterKey() {
|
|
3085
3109
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3086
3110
|
if (nativeValue) {
|
|
@@ -3132,6 +3156,20 @@ async function getMasterKey() {
|
|
|
3132
3156
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3133
3157
|
if (migrated) {
|
|
3134
3158
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3159
|
+
try {
|
|
3160
|
+
await unlink(keyPath);
|
|
3161
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3162
|
+
} catch {
|
|
3163
|
+
}
|
|
3164
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3165
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3166
|
+
if (fallback === "encrypted") {
|
|
3167
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3168
|
+
} else {
|
|
3169
|
+
process.stderr.write(
|
|
3170
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3171
|
+
);
|
|
3172
|
+
}
|
|
3135
3173
|
}
|
|
3136
3174
|
return key;
|
|
3137
3175
|
} catch (err) {
|
|
@@ -3404,6 +3442,7 @@ var init_memory_write_governor = __esm({
|
|
|
3404
3442
|
// src/lib/shard-manager.ts
|
|
3405
3443
|
var shard_manager_exports = {};
|
|
3406
3444
|
__export(shard_manager_exports, {
|
|
3445
|
+
auditShardHealth: () => auditShardHealth,
|
|
3407
3446
|
disposeShards: () => disposeShards,
|
|
3408
3447
|
ensureShardSchema: () => ensureShardSchema,
|
|
3409
3448
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3470,6 +3509,70 @@ function listShards() {
|
|
|
3470
3509
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3471
3510
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3472
3511
|
}
|
|
3512
|
+
async function auditShardHealth(options = {}) {
|
|
3513
|
+
if (!_encryptionKey) {
|
|
3514
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3515
|
+
}
|
|
3516
|
+
const repair = options.repair === true;
|
|
3517
|
+
const dryRun = options.dryRun === true;
|
|
3518
|
+
const names = listShards();
|
|
3519
|
+
const shards = [];
|
|
3520
|
+
for (const name of names) {
|
|
3521
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3522
|
+
const stat = statSync2(dbPath);
|
|
3523
|
+
const item = {
|
|
3524
|
+
name,
|
|
3525
|
+
path: dbPath,
|
|
3526
|
+
ok: false,
|
|
3527
|
+
unreadable: false,
|
|
3528
|
+
error: null,
|
|
3529
|
+
size: stat.size,
|
|
3530
|
+
mtime: stat.mtime.toISOString(),
|
|
3531
|
+
memoryCount: null
|
|
3532
|
+
};
|
|
3533
|
+
const client = createClient2({
|
|
3534
|
+
url: `file:${dbPath}`,
|
|
3535
|
+
encryptionKey: _encryptionKey
|
|
3536
|
+
});
|
|
3537
|
+
try {
|
|
3538
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3539
|
+
const hasMemories = await client.execute(
|
|
3540
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3541
|
+
);
|
|
3542
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3543
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3544
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3545
|
+
}
|
|
3546
|
+
item.ok = true;
|
|
3547
|
+
} catch (err) {
|
|
3548
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3549
|
+
item.error = message;
|
|
3550
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3551
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3552
|
+
client.close();
|
|
3553
|
+
_shards.delete(name);
|
|
3554
|
+
_shardLastAccess.delete(name);
|
|
3555
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3556
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3557
|
+
renameSync3(dbPath, archivedPath);
|
|
3558
|
+
item.archivedPath = archivedPath;
|
|
3559
|
+
}
|
|
3560
|
+
} finally {
|
|
3561
|
+
try {
|
|
3562
|
+
client.close();
|
|
3563
|
+
} catch {
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
shards.push(item);
|
|
3567
|
+
}
|
|
3568
|
+
return {
|
|
3569
|
+
total: shards.length,
|
|
3570
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3571
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3572
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3573
|
+
shards
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3473
3576
|
async function ensureShardSchema(client) {
|
|
3474
3577
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3475
3578
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -6635,6 +6635,15 @@ function readMachineId() {
|
|
|
6635
6635
|
return "";
|
|
6636
6636
|
}
|
|
6637
6637
|
}
|
|
6638
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6639
|
+
const crypto7 = __require("crypto");
|
|
6640
|
+
const iv = crypto7.randomBytes(12);
|
|
6641
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6642
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6643
|
+
encrypted += cipher.final("base64");
|
|
6644
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6645
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6646
|
+
}
|
|
6638
6647
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6639
6648
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6640
6649
|
try {
|
|
@@ -6653,6 +6662,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6653
6662
|
return null;
|
|
6654
6663
|
}
|
|
6655
6664
|
}
|
|
6665
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6666
|
+
const dir = getKeyDir();
|
|
6667
|
+
await mkdir4(dir, { recursive: true });
|
|
6668
|
+
const keyPath = getKeyPath();
|
|
6669
|
+
const machineKey = deriveMachineKey();
|
|
6670
|
+
if (machineKey) {
|
|
6671
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6672
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6673
|
+
await chmod2(keyPath, 384);
|
|
6674
|
+
return "encrypted";
|
|
6675
|
+
}
|
|
6676
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6677
|
+
await chmod2(keyPath, 384);
|
|
6678
|
+
return "plaintext";
|
|
6679
|
+
}
|
|
6656
6680
|
async function getMasterKey() {
|
|
6657
6681
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6658
6682
|
if (nativeValue) {
|
|
@@ -6704,6 +6728,20 @@ async function getMasterKey() {
|
|
|
6704
6728
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6705
6729
|
if (migrated) {
|
|
6706
6730
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6731
|
+
try {
|
|
6732
|
+
await unlink(keyPath);
|
|
6733
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6734
|
+
} catch {
|
|
6735
|
+
}
|
|
6736
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6737
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6738
|
+
if (fallback === "encrypted") {
|
|
6739
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6740
|
+
} else {
|
|
6741
|
+
process.stderr.write(
|
|
6742
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6743
|
+
);
|
|
6744
|
+
}
|
|
6707
6745
|
}
|
|
6708
6746
|
return key;
|
|
6709
6747
|
} catch (err) {
|
|
@@ -6921,6 +6959,7 @@ var init_memory_write_governor = __esm({
|
|
|
6921
6959
|
// src/lib/shard-manager.ts
|
|
6922
6960
|
var shard_manager_exports = {};
|
|
6923
6961
|
__export(shard_manager_exports, {
|
|
6962
|
+
auditShardHealth: () => auditShardHealth,
|
|
6924
6963
|
disposeShards: () => disposeShards,
|
|
6925
6964
|
ensureShardSchema: () => ensureShardSchema,
|
|
6926
6965
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -6987,6 +7026,70 @@ function listShards() {
|
|
|
6987
7026
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6988
7027
|
return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6989
7028
|
}
|
|
7029
|
+
async function auditShardHealth(options = {}) {
|
|
7030
|
+
if (!_encryptionKey) {
|
|
7031
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7032
|
+
}
|
|
7033
|
+
const repair = options.repair === true;
|
|
7034
|
+
const dryRun = options.dryRun === true;
|
|
7035
|
+
const names = listShards();
|
|
7036
|
+
const shards = [];
|
|
7037
|
+
for (const name of names) {
|
|
7038
|
+
const dbPath = path20.join(SHARDS_DIR, `${name}.db`);
|
|
7039
|
+
const stat = statSync2(dbPath);
|
|
7040
|
+
const item = {
|
|
7041
|
+
name,
|
|
7042
|
+
path: dbPath,
|
|
7043
|
+
ok: false,
|
|
7044
|
+
unreadable: false,
|
|
7045
|
+
error: null,
|
|
7046
|
+
size: stat.size,
|
|
7047
|
+
mtime: stat.mtime.toISOString(),
|
|
7048
|
+
memoryCount: null
|
|
7049
|
+
};
|
|
7050
|
+
const client = createClient2({
|
|
7051
|
+
url: `file:${dbPath}`,
|
|
7052
|
+
encryptionKey: _encryptionKey
|
|
7053
|
+
});
|
|
7054
|
+
try {
|
|
7055
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7056
|
+
const hasMemories = await client.execute(
|
|
7057
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7058
|
+
);
|
|
7059
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7060
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7061
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7062
|
+
}
|
|
7063
|
+
item.ok = true;
|
|
7064
|
+
} catch (err) {
|
|
7065
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7066
|
+
item.error = message;
|
|
7067
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7068
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7069
|
+
client.close();
|
|
7070
|
+
_shards.delete(name);
|
|
7071
|
+
_shardLastAccess.delete(name);
|
|
7072
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7073
|
+
const archivedPath = path20.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7074
|
+
renameSync4(dbPath, archivedPath);
|
|
7075
|
+
item.archivedPath = archivedPath;
|
|
7076
|
+
}
|
|
7077
|
+
} finally {
|
|
7078
|
+
try {
|
|
7079
|
+
client.close();
|
|
7080
|
+
} catch {
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
shards.push(item);
|
|
7084
|
+
}
|
|
7085
|
+
return {
|
|
7086
|
+
total: shards.length,
|
|
7087
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7088
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7089
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7090
|
+
shards
|
|
7091
|
+
};
|
|
7092
|
+
}
|
|
6990
7093
|
async function ensureShardSchema(client) {
|
|
6991
7094
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
6992
7095
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3790,6 +3790,15 @@ function readMachineId() {
|
|
|
3790
3790
|
return "";
|
|
3791
3791
|
}
|
|
3792
3792
|
}
|
|
3793
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3794
|
+
const crypto2 = __require("crypto");
|
|
3795
|
+
const iv = crypto2.randomBytes(12);
|
|
3796
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3797
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3798
|
+
encrypted += cipher.final("base64");
|
|
3799
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3800
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3801
|
+
}
|
|
3793
3802
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3794
3803
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3795
3804
|
try {
|
|
@@ -3808,6 +3817,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3808
3817
|
return null;
|
|
3809
3818
|
}
|
|
3810
3819
|
}
|
|
3820
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3821
|
+
const dir = getKeyDir();
|
|
3822
|
+
await mkdir3(dir, { recursive: true });
|
|
3823
|
+
const keyPath = getKeyPath();
|
|
3824
|
+
const machineKey = deriveMachineKey();
|
|
3825
|
+
if (machineKey) {
|
|
3826
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3827
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3828
|
+
await chmod2(keyPath, 384);
|
|
3829
|
+
return "encrypted";
|
|
3830
|
+
}
|
|
3831
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3832
|
+
await chmod2(keyPath, 384);
|
|
3833
|
+
return "plaintext";
|
|
3834
|
+
}
|
|
3811
3835
|
async function getMasterKey() {
|
|
3812
3836
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3813
3837
|
if (nativeValue) {
|
|
@@ -3859,6 +3883,20 @@ async function getMasterKey() {
|
|
|
3859
3883
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3860
3884
|
if (migrated) {
|
|
3861
3885
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3886
|
+
try {
|
|
3887
|
+
await unlink(keyPath);
|
|
3888
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3889
|
+
} catch {
|
|
3890
|
+
}
|
|
3891
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3892
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3893
|
+
if (fallback === "encrypted") {
|
|
3894
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3895
|
+
} else {
|
|
3896
|
+
process.stderr.write(
|
|
3897
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3898
|
+
);
|
|
3899
|
+
}
|
|
3862
3900
|
}
|
|
3863
3901
|
return key;
|
|
3864
3902
|
} catch (err) {
|
|
@@ -4131,6 +4169,7 @@ var init_memory_write_governor = __esm({
|
|
|
4131
4169
|
// src/lib/shard-manager.ts
|
|
4132
4170
|
var shard_manager_exports = {};
|
|
4133
4171
|
__export(shard_manager_exports, {
|
|
4172
|
+
auditShardHealth: () => auditShardHealth,
|
|
4134
4173
|
disposeShards: () => disposeShards,
|
|
4135
4174
|
ensureShardSchema: () => ensureShardSchema,
|
|
4136
4175
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -4197,6 +4236,70 @@ function listShards() {
|
|
|
4197
4236
|
if (!existsSync13(SHARDS_DIR)) return [];
|
|
4198
4237
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4199
4238
|
}
|
|
4239
|
+
async function auditShardHealth(options = {}) {
|
|
4240
|
+
if (!_encryptionKey) {
|
|
4241
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
4242
|
+
}
|
|
4243
|
+
const repair = options.repair === true;
|
|
4244
|
+
const dryRun = options.dryRun === true;
|
|
4245
|
+
const names = listShards();
|
|
4246
|
+
const shards = [];
|
|
4247
|
+
for (const name of names) {
|
|
4248
|
+
const dbPath = path15.join(SHARDS_DIR, `${name}.db`);
|
|
4249
|
+
const stat = statSync3(dbPath);
|
|
4250
|
+
const item = {
|
|
4251
|
+
name,
|
|
4252
|
+
path: dbPath,
|
|
4253
|
+
ok: false,
|
|
4254
|
+
unreadable: false,
|
|
4255
|
+
error: null,
|
|
4256
|
+
size: stat.size,
|
|
4257
|
+
mtime: stat.mtime.toISOString(),
|
|
4258
|
+
memoryCount: null
|
|
4259
|
+
};
|
|
4260
|
+
const client = createClient2({
|
|
4261
|
+
url: `file:${dbPath}`,
|
|
4262
|
+
encryptionKey: _encryptionKey
|
|
4263
|
+
});
|
|
4264
|
+
try {
|
|
4265
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
4266
|
+
const hasMemories = await client.execute(
|
|
4267
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
4268
|
+
);
|
|
4269
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
4270
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
4271
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
4272
|
+
}
|
|
4273
|
+
item.ok = true;
|
|
4274
|
+
} catch (err) {
|
|
4275
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4276
|
+
item.error = message;
|
|
4277
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
4278
|
+
if (item.unreadable && repair && !dryRun) {
|
|
4279
|
+
client.close();
|
|
4280
|
+
_shards.delete(name);
|
|
4281
|
+
_shardLastAccess.delete(name);
|
|
4282
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4283
|
+
const archivedPath = path15.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4284
|
+
renameSync4(dbPath, archivedPath);
|
|
4285
|
+
item.archivedPath = archivedPath;
|
|
4286
|
+
}
|
|
4287
|
+
} finally {
|
|
4288
|
+
try {
|
|
4289
|
+
client.close();
|
|
4290
|
+
} catch {
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
shards.push(item);
|
|
4294
|
+
}
|
|
4295
|
+
return {
|
|
4296
|
+
total: shards.length,
|
|
4297
|
+
ok: shards.filter((s) => s.ok).length,
|
|
4298
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
4299
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
4300
|
+
shards
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4200
4303
|
async function ensureShardSchema(client) {
|
|
4201
4304
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
4202
4305
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3231,6 +3231,15 @@ function readMachineId() {
|
|
|
3231
3231
|
return "";
|
|
3232
3232
|
}
|
|
3233
3233
|
}
|
|
3234
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3235
|
+
const crypto9 = __require("crypto");
|
|
3236
|
+
const iv = crypto9.randomBytes(12);
|
|
3237
|
+
const cipher = crypto9.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3238
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3239
|
+
encrypted += cipher.final("base64");
|
|
3240
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3241
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3242
|
+
}
|
|
3234
3243
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3235
3244
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3236
3245
|
try {
|
|
@@ -3249,6 +3258,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3249
3258
|
return null;
|
|
3250
3259
|
}
|
|
3251
3260
|
}
|
|
3261
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3262
|
+
const dir = getKeyDir();
|
|
3263
|
+
await mkdir3(dir, { recursive: true });
|
|
3264
|
+
const keyPath = getKeyPath();
|
|
3265
|
+
const machineKey = deriveMachineKey();
|
|
3266
|
+
if (machineKey) {
|
|
3267
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3268
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3269
|
+
await chmod2(keyPath, 384);
|
|
3270
|
+
return "encrypted";
|
|
3271
|
+
}
|
|
3272
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3273
|
+
await chmod2(keyPath, 384);
|
|
3274
|
+
return "plaintext";
|
|
3275
|
+
}
|
|
3252
3276
|
async function getMasterKey() {
|
|
3253
3277
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3254
3278
|
if (nativeValue) {
|
|
@@ -3300,6 +3324,20 @@ async function getMasterKey() {
|
|
|
3300
3324
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3301
3325
|
if (migrated) {
|
|
3302
3326
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3327
|
+
try {
|
|
3328
|
+
await unlink(keyPath);
|
|
3329
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3330
|
+
} catch {
|
|
3331
|
+
}
|
|
3332
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3333
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3334
|
+
if (fallback === "encrypted") {
|
|
3335
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3336
|
+
} else {
|
|
3337
|
+
process.stderr.write(
|
|
3338
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3303
3341
|
}
|
|
3304
3342
|
return key;
|
|
3305
3343
|
} catch (err) {
|
|
@@ -3572,6 +3610,7 @@ var init_memory_write_governor = __esm({
|
|
|
3572
3610
|
// src/lib/shard-manager.ts
|
|
3573
3611
|
var shard_manager_exports = {};
|
|
3574
3612
|
__export(shard_manager_exports, {
|
|
3613
|
+
auditShardHealth: () => auditShardHealth,
|
|
3575
3614
|
disposeShards: () => disposeShards,
|
|
3576
3615
|
ensureShardSchema: () => ensureShardSchema,
|
|
3577
3616
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3638,6 +3677,70 @@ function listShards() {
|
|
|
3638
3677
|
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3639
3678
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3640
3679
|
}
|
|
3680
|
+
async function auditShardHealth(options = {}) {
|
|
3681
|
+
if (!_encryptionKey) {
|
|
3682
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3683
|
+
}
|
|
3684
|
+
const repair = options.repair === true;
|
|
3685
|
+
const dryRun = options.dryRun === true;
|
|
3686
|
+
const names = listShards();
|
|
3687
|
+
const shards = [];
|
|
3688
|
+
for (const name of names) {
|
|
3689
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3690
|
+
const stat = statSync2(dbPath);
|
|
3691
|
+
const item = {
|
|
3692
|
+
name,
|
|
3693
|
+
path: dbPath,
|
|
3694
|
+
ok: false,
|
|
3695
|
+
unreadable: false,
|
|
3696
|
+
error: null,
|
|
3697
|
+
size: stat.size,
|
|
3698
|
+
mtime: stat.mtime.toISOString(),
|
|
3699
|
+
memoryCount: null
|
|
3700
|
+
};
|
|
3701
|
+
const client = createClient2({
|
|
3702
|
+
url: `file:${dbPath}`,
|
|
3703
|
+
encryptionKey: _encryptionKey
|
|
3704
|
+
});
|
|
3705
|
+
try {
|
|
3706
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3707
|
+
const hasMemories = await client.execute(
|
|
3708
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3709
|
+
);
|
|
3710
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3711
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3712
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3713
|
+
}
|
|
3714
|
+
item.ok = true;
|
|
3715
|
+
} catch (err) {
|
|
3716
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3717
|
+
item.error = message;
|
|
3718
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3719
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3720
|
+
client.close();
|
|
3721
|
+
_shards.delete(name);
|
|
3722
|
+
_shardLastAccess.delete(name);
|
|
3723
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3724
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3725
|
+
renameSync3(dbPath, archivedPath);
|
|
3726
|
+
item.archivedPath = archivedPath;
|
|
3727
|
+
}
|
|
3728
|
+
} finally {
|
|
3729
|
+
try {
|
|
3730
|
+
client.close();
|
|
3731
|
+
} catch {
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
shards.push(item);
|
|
3735
|
+
}
|
|
3736
|
+
return {
|
|
3737
|
+
total: shards.length,
|
|
3738
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3739
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3740
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3741
|
+
shards
|
|
3742
|
+
};
|
|
3743
|
+
}
|
|
3641
3744
|
async function ensureShardSchema(client) {
|
|
3642
3745
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3643
3746
|
await client.execute("PRAGMA busy_timeout = 30000");
|