@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
package/dist/bin/git-sweep.js
CHANGED
|
@@ -6586,6 +6586,15 @@ function readMachineId() {
|
|
|
6586
6586
|
return "";
|
|
6587
6587
|
}
|
|
6588
6588
|
}
|
|
6589
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6590
|
+
const crypto7 = __require("crypto");
|
|
6591
|
+
const iv = crypto7.randomBytes(12);
|
|
6592
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6593
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6594
|
+
encrypted += cipher.final("base64");
|
|
6595
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6596
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6597
|
+
}
|
|
6589
6598
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6590
6599
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6591
6600
|
try {
|
|
@@ -6604,6 +6613,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6604
6613
|
return null;
|
|
6605
6614
|
}
|
|
6606
6615
|
}
|
|
6616
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6617
|
+
const dir = getKeyDir();
|
|
6618
|
+
await mkdir4(dir, { recursive: true });
|
|
6619
|
+
const keyPath = getKeyPath();
|
|
6620
|
+
const machineKey = deriveMachineKey();
|
|
6621
|
+
if (machineKey) {
|
|
6622
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6623
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6624
|
+
await chmod2(keyPath, 384);
|
|
6625
|
+
return "encrypted";
|
|
6626
|
+
}
|
|
6627
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6628
|
+
await chmod2(keyPath, 384);
|
|
6629
|
+
return "plaintext";
|
|
6630
|
+
}
|
|
6607
6631
|
async function getMasterKey() {
|
|
6608
6632
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6609
6633
|
if (nativeValue) {
|
|
@@ -6655,6 +6679,20 @@ async function getMasterKey() {
|
|
|
6655
6679
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6656
6680
|
if (migrated) {
|
|
6657
6681
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6682
|
+
try {
|
|
6683
|
+
await unlink(keyPath);
|
|
6684
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6685
|
+
} catch {
|
|
6686
|
+
}
|
|
6687
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6688
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6689
|
+
if (fallback === "encrypted") {
|
|
6690
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6691
|
+
} else {
|
|
6692
|
+
process.stderr.write(
|
|
6693
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6694
|
+
);
|
|
6695
|
+
}
|
|
6658
6696
|
}
|
|
6659
6697
|
return key;
|
|
6660
6698
|
} catch (err) {
|
|
@@ -6872,6 +6910,7 @@ var init_memory_write_governor = __esm({
|
|
|
6872
6910
|
// src/lib/shard-manager.ts
|
|
6873
6911
|
var shard_manager_exports = {};
|
|
6874
6912
|
__export(shard_manager_exports, {
|
|
6913
|
+
auditShardHealth: () => auditShardHealth,
|
|
6875
6914
|
disposeShards: () => disposeShards,
|
|
6876
6915
|
ensureShardSchema: () => ensureShardSchema,
|
|
6877
6916
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -6938,6 +6977,70 @@ function listShards() {
|
|
|
6938
6977
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6939
6978
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6940
6979
|
}
|
|
6980
|
+
async function auditShardHealth(options = {}) {
|
|
6981
|
+
if (!_encryptionKey) {
|
|
6982
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
6983
|
+
}
|
|
6984
|
+
const repair = options.repair === true;
|
|
6985
|
+
const dryRun2 = options.dryRun === true;
|
|
6986
|
+
const names = listShards();
|
|
6987
|
+
const shards = [];
|
|
6988
|
+
for (const name of names) {
|
|
6989
|
+
const dbPath = path19.join(SHARDS_DIR, `${name}.db`);
|
|
6990
|
+
const stat = statSync2(dbPath);
|
|
6991
|
+
const item = {
|
|
6992
|
+
name,
|
|
6993
|
+
path: dbPath,
|
|
6994
|
+
ok: false,
|
|
6995
|
+
unreadable: false,
|
|
6996
|
+
error: null,
|
|
6997
|
+
size: stat.size,
|
|
6998
|
+
mtime: stat.mtime.toISOString(),
|
|
6999
|
+
memoryCount: null
|
|
7000
|
+
};
|
|
7001
|
+
const client = createClient2({
|
|
7002
|
+
url: `file:${dbPath}`,
|
|
7003
|
+
encryptionKey: _encryptionKey
|
|
7004
|
+
});
|
|
7005
|
+
try {
|
|
7006
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7007
|
+
const hasMemories = await client.execute(
|
|
7008
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7009
|
+
);
|
|
7010
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7011
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7012
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7013
|
+
}
|
|
7014
|
+
item.ok = true;
|
|
7015
|
+
} catch (err) {
|
|
7016
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7017
|
+
item.error = message;
|
|
7018
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7019
|
+
if (item.unreadable && repair && !dryRun2) {
|
|
7020
|
+
client.close();
|
|
7021
|
+
_shards.delete(name);
|
|
7022
|
+
_shardLastAccess.delete(name);
|
|
7023
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7024
|
+
const archivedPath = path19.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7025
|
+
renameSync4(dbPath, archivedPath);
|
|
7026
|
+
item.archivedPath = archivedPath;
|
|
7027
|
+
}
|
|
7028
|
+
} finally {
|
|
7029
|
+
try {
|
|
7030
|
+
client.close();
|
|
7031
|
+
} catch {
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
shards.push(item);
|
|
7035
|
+
}
|
|
7036
|
+
return {
|
|
7037
|
+
total: shards.length,
|
|
7038
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7039
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7040
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7041
|
+
shards
|
|
7042
|
+
};
|
|
7043
|
+
}
|
|
6941
7044
|
async function ensureShardSchema(client) {
|
|
6942
7045
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
6943
7046
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2589,6 +2589,7 @@ var init_database = __esm({
|
|
|
2589
2589
|
// src/lib/shard-manager.ts
|
|
2590
2590
|
var shard_manager_exports = {};
|
|
2591
2591
|
__export(shard_manager_exports, {
|
|
2592
|
+
auditShardHealth: () => auditShardHealth,
|
|
2592
2593
|
disposeShards: () => disposeShards,
|
|
2593
2594
|
ensureShardSchema: () => ensureShardSchema,
|
|
2594
2595
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2655,6 +2656,70 @@ function listShards() {
|
|
|
2655
2656
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2656
2657
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2657
2658
|
}
|
|
2659
|
+
async function auditShardHealth(options = {}) {
|
|
2660
|
+
if (!_encryptionKey) {
|
|
2661
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2662
|
+
}
|
|
2663
|
+
const repair = options.repair === true;
|
|
2664
|
+
const dryRun = options.dryRun === true;
|
|
2665
|
+
const names = listShards();
|
|
2666
|
+
const shards = [];
|
|
2667
|
+
for (const name of names) {
|
|
2668
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2669
|
+
const stat = statSync2(dbPath);
|
|
2670
|
+
const item = {
|
|
2671
|
+
name,
|
|
2672
|
+
path: dbPath,
|
|
2673
|
+
ok: false,
|
|
2674
|
+
unreadable: false,
|
|
2675
|
+
error: null,
|
|
2676
|
+
size: stat.size,
|
|
2677
|
+
mtime: stat.mtime.toISOString(),
|
|
2678
|
+
memoryCount: null
|
|
2679
|
+
};
|
|
2680
|
+
const client = createClient2({
|
|
2681
|
+
url: `file:${dbPath}`,
|
|
2682
|
+
encryptionKey: _encryptionKey
|
|
2683
|
+
});
|
|
2684
|
+
try {
|
|
2685
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2686
|
+
const hasMemories = await client.execute(
|
|
2687
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2688
|
+
);
|
|
2689
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2690
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2691
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2692
|
+
}
|
|
2693
|
+
item.ok = true;
|
|
2694
|
+
} catch (err) {
|
|
2695
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2696
|
+
item.error = message;
|
|
2697
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2698
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2699
|
+
client.close();
|
|
2700
|
+
_shards.delete(name);
|
|
2701
|
+
_shardLastAccess.delete(name);
|
|
2702
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2703
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2704
|
+
renameSync3(dbPath, archivedPath);
|
|
2705
|
+
item.archivedPath = archivedPath;
|
|
2706
|
+
}
|
|
2707
|
+
} finally {
|
|
2708
|
+
try {
|
|
2709
|
+
client.close();
|
|
2710
|
+
} catch {
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
shards.push(item);
|
|
2714
|
+
}
|
|
2715
|
+
return {
|
|
2716
|
+
total: shards.length,
|
|
2717
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2718
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2719
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2720
|
+
shards
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2658
2723
|
async function ensureShardSchema(client) {
|
|
2659
2724
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2660
2725
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3294,6 +3359,15 @@ function readMachineId() {
|
|
|
3294
3359
|
return "";
|
|
3295
3360
|
}
|
|
3296
3361
|
}
|
|
3362
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3363
|
+
const crypto3 = __require("crypto");
|
|
3364
|
+
const iv = crypto3.randomBytes(12);
|
|
3365
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3366
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3367
|
+
encrypted += cipher.final("base64");
|
|
3368
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3369
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3370
|
+
}
|
|
3297
3371
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3298
3372
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3299
3373
|
try {
|
|
@@ -3312,6 +3386,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3312
3386
|
return null;
|
|
3313
3387
|
}
|
|
3314
3388
|
}
|
|
3389
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3390
|
+
const dir = getKeyDir();
|
|
3391
|
+
await mkdir3(dir, { recursive: true });
|
|
3392
|
+
const keyPath = getKeyPath();
|
|
3393
|
+
const machineKey = deriveMachineKey();
|
|
3394
|
+
if (machineKey) {
|
|
3395
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3396
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3397
|
+
await chmod2(keyPath, 384);
|
|
3398
|
+
return "encrypted";
|
|
3399
|
+
}
|
|
3400
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3401
|
+
await chmod2(keyPath, 384);
|
|
3402
|
+
return "plaintext";
|
|
3403
|
+
}
|
|
3315
3404
|
async function getMasterKey() {
|
|
3316
3405
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3317
3406
|
if (nativeValue) {
|
|
@@ -3363,6 +3452,20 @@ async function getMasterKey() {
|
|
|
3363
3452
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3364
3453
|
if (migrated) {
|
|
3365
3454
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3455
|
+
try {
|
|
3456
|
+
await unlink(keyPath);
|
|
3457
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3458
|
+
} catch {
|
|
3459
|
+
}
|
|
3460
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3461
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3462
|
+
if (fallback === "encrypted") {
|
|
3463
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3464
|
+
} else {
|
|
3465
|
+
process.stderr.write(
|
|
3466
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3366
3469
|
}
|
|
3367
3470
|
return key;
|
|
3368
3471
|
} catch (err) {
|
package/dist/bin/graph-export.js
CHANGED
|
@@ -2897,6 +2897,15 @@ function readMachineId() {
|
|
|
2897
2897
|
return "";
|
|
2898
2898
|
}
|
|
2899
2899
|
}
|
|
2900
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2901
|
+
const crypto2 = __require("crypto");
|
|
2902
|
+
const iv = crypto2.randomBytes(12);
|
|
2903
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2904
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2905
|
+
encrypted += cipher.final("base64");
|
|
2906
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2907
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2908
|
+
}
|
|
2900
2909
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2901
2910
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2902
2911
|
try {
|
|
@@ -2915,6 +2924,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2915
2924
|
return null;
|
|
2916
2925
|
}
|
|
2917
2926
|
}
|
|
2927
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2928
|
+
const dir = getKeyDir();
|
|
2929
|
+
await mkdir3(dir, { recursive: true });
|
|
2930
|
+
const keyPath = getKeyPath();
|
|
2931
|
+
const machineKey = deriveMachineKey();
|
|
2932
|
+
if (machineKey) {
|
|
2933
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2934
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2935
|
+
await chmod2(keyPath, 384);
|
|
2936
|
+
return "encrypted";
|
|
2937
|
+
}
|
|
2938
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2939
|
+
await chmod2(keyPath, 384);
|
|
2940
|
+
return "plaintext";
|
|
2941
|
+
}
|
|
2918
2942
|
async function getMasterKey() {
|
|
2919
2943
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2920
2944
|
if (nativeValue) {
|
|
@@ -2966,6 +2990,20 @@ async function getMasterKey() {
|
|
|
2966
2990
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2967
2991
|
if (migrated) {
|
|
2968
2992
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2993
|
+
try {
|
|
2994
|
+
await unlink(keyPath);
|
|
2995
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2996
|
+
} catch {
|
|
2997
|
+
}
|
|
2998
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2999
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3000
|
+
if (fallback === "encrypted") {
|
|
3001
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3002
|
+
} else {
|
|
3003
|
+
process.stderr.write(
|
|
3004
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
2969
3007
|
}
|
|
2970
3008
|
return key;
|
|
2971
3009
|
} catch (err) {
|
|
@@ -3238,6 +3276,7 @@ var init_memory_write_governor = __esm({
|
|
|
3238
3276
|
// src/lib/shard-manager.ts
|
|
3239
3277
|
var shard_manager_exports = {};
|
|
3240
3278
|
__export(shard_manager_exports, {
|
|
3279
|
+
auditShardHealth: () => auditShardHealth,
|
|
3241
3280
|
disposeShards: () => disposeShards,
|
|
3242
3281
|
ensureShardSchema: () => ensureShardSchema,
|
|
3243
3282
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3304,6 +3343,70 @@ function listShards() {
|
|
|
3304
3343
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3305
3344
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3306
3345
|
}
|
|
3346
|
+
async function auditShardHealth(options = {}) {
|
|
3347
|
+
if (!_encryptionKey) {
|
|
3348
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3349
|
+
}
|
|
3350
|
+
const repair = options.repair === true;
|
|
3351
|
+
const dryRun = options.dryRun === true;
|
|
3352
|
+
const names = listShards();
|
|
3353
|
+
const shards = [];
|
|
3354
|
+
for (const name of names) {
|
|
3355
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3356
|
+
const stat = statSync2(dbPath);
|
|
3357
|
+
const item = {
|
|
3358
|
+
name,
|
|
3359
|
+
path: dbPath,
|
|
3360
|
+
ok: false,
|
|
3361
|
+
unreadable: false,
|
|
3362
|
+
error: null,
|
|
3363
|
+
size: stat.size,
|
|
3364
|
+
mtime: stat.mtime.toISOString(),
|
|
3365
|
+
memoryCount: null
|
|
3366
|
+
};
|
|
3367
|
+
const client = createClient2({
|
|
3368
|
+
url: `file:${dbPath}`,
|
|
3369
|
+
encryptionKey: _encryptionKey
|
|
3370
|
+
});
|
|
3371
|
+
try {
|
|
3372
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3373
|
+
const hasMemories = await client.execute(
|
|
3374
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3375
|
+
);
|
|
3376
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3377
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3378
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3379
|
+
}
|
|
3380
|
+
item.ok = true;
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3383
|
+
item.error = message;
|
|
3384
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3385
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3386
|
+
client.close();
|
|
3387
|
+
_shards.delete(name);
|
|
3388
|
+
_shardLastAccess.delete(name);
|
|
3389
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3390
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3391
|
+
renameSync3(dbPath, archivedPath);
|
|
3392
|
+
item.archivedPath = archivedPath;
|
|
3393
|
+
}
|
|
3394
|
+
} finally {
|
|
3395
|
+
try {
|
|
3396
|
+
client.close();
|
|
3397
|
+
} catch {
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
shards.push(item);
|
|
3401
|
+
}
|
|
3402
|
+
return {
|
|
3403
|
+
total: shards.length,
|
|
3404
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3405
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3406
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3407
|
+
shards
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3307
3410
|
async function ensureShardSchema(client) {
|
|
3308
3411
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3309
3412
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2996,6 +2996,15 @@ function readMachineId() {
|
|
|
2996
2996
|
return "";
|
|
2997
2997
|
}
|
|
2998
2998
|
}
|
|
2999
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3000
|
+
const crypto8 = __require("crypto");
|
|
3001
|
+
const iv = crypto8.randomBytes(12);
|
|
3002
|
+
const cipher = crypto8.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3003
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3004
|
+
encrypted += cipher.final("base64");
|
|
3005
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3006
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3007
|
+
}
|
|
2999
3008
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3000
3009
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3001
3010
|
try {
|
|
@@ -3014,6 +3023,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3014
3023
|
return null;
|
|
3015
3024
|
}
|
|
3016
3025
|
}
|
|
3026
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3027
|
+
const dir = getKeyDir();
|
|
3028
|
+
await mkdir3(dir, { recursive: true });
|
|
3029
|
+
const keyPath = getKeyPath();
|
|
3030
|
+
const machineKey = deriveMachineKey();
|
|
3031
|
+
if (machineKey) {
|
|
3032
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3033
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3034
|
+
await chmod2(keyPath, 384);
|
|
3035
|
+
return "encrypted";
|
|
3036
|
+
}
|
|
3037
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3038
|
+
await chmod2(keyPath, 384);
|
|
3039
|
+
return "plaintext";
|
|
3040
|
+
}
|
|
3017
3041
|
async function getMasterKey() {
|
|
3018
3042
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3019
3043
|
if (nativeValue) {
|
|
@@ -3065,6 +3089,20 @@ async function getMasterKey() {
|
|
|
3065
3089
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3066
3090
|
if (migrated) {
|
|
3067
3091
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3092
|
+
try {
|
|
3093
|
+
await unlink(keyPath);
|
|
3094
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3095
|
+
} catch {
|
|
3096
|
+
}
|
|
3097
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3098
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3099
|
+
if (fallback === "encrypted") {
|
|
3100
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3101
|
+
} else {
|
|
3102
|
+
process.stderr.write(
|
|
3103
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3104
|
+
);
|
|
3105
|
+
}
|
|
3068
3106
|
}
|
|
3069
3107
|
return key;
|
|
3070
3108
|
} catch (err) {
|
|
@@ -3337,6 +3375,7 @@ var init_memory_write_governor = __esm({
|
|
|
3337
3375
|
// src/lib/shard-manager.ts
|
|
3338
3376
|
var shard_manager_exports = {};
|
|
3339
3377
|
__export(shard_manager_exports, {
|
|
3378
|
+
auditShardHealth: () => auditShardHealth,
|
|
3340
3379
|
disposeShards: () => disposeShards,
|
|
3341
3380
|
ensureShardSchema: () => ensureShardSchema,
|
|
3342
3381
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3403,6 +3442,70 @@ function listShards() {
|
|
|
3403
3442
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3404
3443
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3405
3444
|
}
|
|
3445
|
+
async function auditShardHealth(options = {}) {
|
|
3446
|
+
if (!_encryptionKey) {
|
|
3447
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3448
|
+
}
|
|
3449
|
+
const repair = options.repair === true;
|
|
3450
|
+
const dryRun = options.dryRun === true;
|
|
3451
|
+
const names = listShards();
|
|
3452
|
+
const shards = [];
|
|
3453
|
+
for (const name of names) {
|
|
3454
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3455
|
+
const stat = statSync2(dbPath);
|
|
3456
|
+
const item = {
|
|
3457
|
+
name,
|
|
3458
|
+
path: dbPath,
|
|
3459
|
+
ok: false,
|
|
3460
|
+
unreadable: false,
|
|
3461
|
+
error: null,
|
|
3462
|
+
size: stat.size,
|
|
3463
|
+
mtime: stat.mtime.toISOString(),
|
|
3464
|
+
memoryCount: null
|
|
3465
|
+
};
|
|
3466
|
+
const client = createClient2({
|
|
3467
|
+
url: `file:${dbPath}`,
|
|
3468
|
+
encryptionKey: _encryptionKey
|
|
3469
|
+
});
|
|
3470
|
+
try {
|
|
3471
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3472
|
+
const hasMemories = await client.execute(
|
|
3473
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3474
|
+
);
|
|
3475
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3476
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3477
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3478
|
+
}
|
|
3479
|
+
item.ok = true;
|
|
3480
|
+
} catch (err) {
|
|
3481
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3482
|
+
item.error = message;
|
|
3483
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3484
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3485
|
+
client.close();
|
|
3486
|
+
_shards.delete(name);
|
|
3487
|
+
_shardLastAccess.delete(name);
|
|
3488
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3489
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3490
|
+
renameSync3(dbPath, archivedPath);
|
|
3491
|
+
item.archivedPath = archivedPath;
|
|
3492
|
+
}
|
|
3493
|
+
} finally {
|
|
3494
|
+
try {
|
|
3495
|
+
client.close();
|
|
3496
|
+
} catch {
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
shards.push(item);
|
|
3500
|
+
}
|
|
3501
|
+
return {
|
|
3502
|
+
total: shards.length,
|
|
3503
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3504
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3505
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3506
|
+
shards
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3406
3509
|
async function ensureShardSchema(client) {
|
|
3407
3510
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3408
3511
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/scan-tasks.js
CHANGED
|
@@ -6657,6 +6657,15 @@ function readMachineId() {
|
|
|
6657
6657
|
return "";
|
|
6658
6658
|
}
|
|
6659
6659
|
}
|
|
6660
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6661
|
+
const crypto7 = __require("crypto");
|
|
6662
|
+
const iv = crypto7.randomBytes(12);
|
|
6663
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6664
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6665
|
+
encrypted += cipher.final("base64");
|
|
6666
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6667
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6668
|
+
}
|
|
6660
6669
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6661
6670
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6662
6671
|
try {
|
|
@@ -6675,6 +6684,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6675
6684
|
return null;
|
|
6676
6685
|
}
|
|
6677
6686
|
}
|
|
6687
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6688
|
+
const dir = getKeyDir();
|
|
6689
|
+
await mkdir4(dir, { recursive: true });
|
|
6690
|
+
const keyPath = getKeyPath();
|
|
6691
|
+
const machineKey = deriveMachineKey();
|
|
6692
|
+
if (machineKey) {
|
|
6693
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6694
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6695
|
+
await chmod2(keyPath, 384);
|
|
6696
|
+
return "encrypted";
|
|
6697
|
+
}
|
|
6698
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6699
|
+
await chmod2(keyPath, 384);
|
|
6700
|
+
return "plaintext";
|
|
6701
|
+
}
|
|
6678
6702
|
async function getMasterKey() {
|
|
6679
6703
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6680
6704
|
if (nativeValue) {
|
|
@@ -6726,6 +6750,20 @@ async function getMasterKey() {
|
|
|
6726
6750
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6727
6751
|
if (migrated) {
|
|
6728
6752
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6753
|
+
try {
|
|
6754
|
+
await unlink(keyPath);
|
|
6755
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6756
|
+
} catch {
|
|
6757
|
+
}
|
|
6758
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6759
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6760
|
+
if (fallback === "encrypted") {
|
|
6761
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6762
|
+
} else {
|
|
6763
|
+
process.stderr.write(
|
|
6764
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6729
6767
|
}
|
|
6730
6768
|
return key;
|
|
6731
6769
|
} catch (err) {
|
|
@@ -6943,6 +6981,7 @@ var init_memory_write_governor = __esm({
|
|
|
6943
6981
|
// src/lib/shard-manager.ts
|
|
6944
6982
|
var shard_manager_exports = {};
|
|
6945
6983
|
__export(shard_manager_exports, {
|
|
6984
|
+
auditShardHealth: () => auditShardHealth,
|
|
6946
6985
|
disposeShards: () => disposeShards,
|
|
6947
6986
|
ensureShardSchema: () => ensureShardSchema,
|
|
6948
6987
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -7009,6 +7048,70 @@ function listShards() {
|
|
|
7009
7048
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
7010
7049
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
7011
7050
|
}
|
|
7051
|
+
async function auditShardHealth(options = {}) {
|
|
7052
|
+
if (!_encryptionKey) {
|
|
7053
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7054
|
+
}
|
|
7055
|
+
const repair = options.repair === true;
|
|
7056
|
+
const dryRun = options.dryRun === true;
|
|
7057
|
+
const names = listShards();
|
|
7058
|
+
const shards = [];
|
|
7059
|
+
for (const name of names) {
|
|
7060
|
+
const dbPath = path19.join(SHARDS_DIR, `${name}.db`);
|
|
7061
|
+
const stat = statSync2(dbPath);
|
|
7062
|
+
const item = {
|
|
7063
|
+
name,
|
|
7064
|
+
path: dbPath,
|
|
7065
|
+
ok: false,
|
|
7066
|
+
unreadable: false,
|
|
7067
|
+
error: null,
|
|
7068
|
+
size: stat.size,
|
|
7069
|
+
mtime: stat.mtime.toISOString(),
|
|
7070
|
+
memoryCount: null
|
|
7071
|
+
};
|
|
7072
|
+
const client = createClient2({
|
|
7073
|
+
url: `file:${dbPath}`,
|
|
7074
|
+
encryptionKey: _encryptionKey
|
|
7075
|
+
});
|
|
7076
|
+
try {
|
|
7077
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7078
|
+
const hasMemories = await client.execute(
|
|
7079
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7080
|
+
);
|
|
7081
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7082
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7083
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7084
|
+
}
|
|
7085
|
+
item.ok = true;
|
|
7086
|
+
} catch (err) {
|
|
7087
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7088
|
+
item.error = message;
|
|
7089
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7090
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7091
|
+
client.close();
|
|
7092
|
+
_shards.delete(name);
|
|
7093
|
+
_shardLastAccess.delete(name);
|
|
7094
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7095
|
+
const archivedPath = path19.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7096
|
+
renameSync4(dbPath, archivedPath);
|
|
7097
|
+
item.archivedPath = archivedPath;
|
|
7098
|
+
}
|
|
7099
|
+
} finally {
|
|
7100
|
+
try {
|
|
7101
|
+
client.close();
|
|
7102
|
+
} catch {
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
shards.push(item);
|
|
7106
|
+
}
|
|
7107
|
+
return {
|
|
7108
|
+
total: shards.length,
|
|
7109
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7110
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7111
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7112
|
+
shards
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7012
7115
|
async function ensureShardSchema(client) {
|
|
7013
7116
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
7014
7117
|
await client.execute("PRAGMA busy_timeout = 30000");
|