@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
|
@@ -6651,6 +6651,15 @@ function readMachineId() {
|
|
|
6651
6651
|
return "";
|
|
6652
6652
|
}
|
|
6653
6653
|
}
|
|
6654
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6655
|
+
const crypto7 = __require("crypto");
|
|
6656
|
+
const iv = crypto7.randomBytes(12);
|
|
6657
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6658
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6659
|
+
encrypted += cipher.final("base64");
|
|
6660
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6661
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6662
|
+
}
|
|
6654
6663
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6655
6664
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6656
6665
|
try {
|
|
@@ -6669,6 +6678,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6669
6678
|
return null;
|
|
6670
6679
|
}
|
|
6671
6680
|
}
|
|
6681
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6682
|
+
const dir = getKeyDir();
|
|
6683
|
+
await mkdir4(dir, { recursive: true });
|
|
6684
|
+
const keyPath = getKeyPath();
|
|
6685
|
+
const machineKey = deriveMachineKey();
|
|
6686
|
+
if (machineKey) {
|
|
6687
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6688
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6689
|
+
await chmod2(keyPath, 384);
|
|
6690
|
+
return "encrypted";
|
|
6691
|
+
}
|
|
6692
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6693
|
+
await chmod2(keyPath, 384);
|
|
6694
|
+
return "plaintext";
|
|
6695
|
+
}
|
|
6672
6696
|
async function getMasterKey() {
|
|
6673
6697
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6674
6698
|
if (nativeValue) {
|
|
@@ -6720,6 +6744,20 @@ async function getMasterKey() {
|
|
|
6720
6744
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6721
6745
|
if (migrated) {
|
|
6722
6746
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6747
|
+
try {
|
|
6748
|
+
await unlink(keyPath);
|
|
6749
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6750
|
+
} catch {
|
|
6751
|
+
}
|
|
6752
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6753
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6754
|
+
if (fallback === "encrypted") {
|
|
6755
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6756
|
+
} else {
|
|
6757
|
+
process.stderr.write(
|
|
6758
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6759
|
+
);
|
|
6760
|
+
}
|
|
6723
6761
|
}
|
|
6724
6762
|
return key;
|
|
6725
6763
|
} catch (err) {
|
|
@@ -6937,6 +6975,7 @@ var init_memory_write_governor = __esm({
|
|
|
6937
6975
|
// src/lib/shard-manager.ts
|
|
6938
6976
|
var shard_manager_exports = {};
|
|
6939
6977
|
__export(shard_manager_exports, {
|
|
6978
|
+
auditShardHealth: () => auditShardHealth,
|
|
6940
6979
|
disposeShards: () => disposeShards,
|
|
6941
6980
|
ensureShardSchema: () => ensureShardSchema,
|
|
6942
6981
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -7003,6 +7042,70 @@ function listShards() {
|
|
|
7003
7042
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
7004
7043
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
7005
7044
|
}
|
|
7045
|
+
async function auditShardHealth(options = {}) {
|
|
7046
|
+
if (!_encryptionKey) {
|
|
7047
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7048
|
+
}
|
|
7049
|
+
const repair = options.repair === true;
|
|
7050
|
+
const dryRun = options.dryRun === true;
|
|
7051
|
+
const names = listShards();
|
|
7052
|
+
const shards = [];
|
|
7053
|
+
for (const name of names) {
|
|
7054
|
+
const dbPath = path19.join(SHARDS_DIR, `${name}.db`);
|
|
7055
|
+
const stat = statSync2(dbPath);
|
|
7056
|
+
const item = {
|
|
7057
|
+
name,
|
|
7058
|
+
path: dbPath,
|
|
7059
|
+
ok: false,
|
|
7060
|
+
unreadable: false,
|
|
7061
|
+
error: null,
|
|
7062
|
+
size: stat.size,
|
|
7063
|
+
mtime: stat.mtime.toISOString(),
|
|
7064
|
+
memoryCount: null
|
|
7065
|
+
};
|
|
7066
|
+
const client = createClient2({
|
|
7067
|
+
url: `file:${dbPath}`,
|
|
7068
|
+
encryptionKey: _encryptionKey
|
|
7069
|
+
});
|
|
7070
|
+
try {
|
|
7071
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7072
|
+
const hasMemories = await client.execute(
|
|
7073
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7074
|
+
);
|
|
7075
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7076
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7077
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7078
|
+
}
|
|
7079
|
+
item.ok = true;
|
|
7080
|
+
} catch (err) {
|
|
7081
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7082
|
+
item.error = message;
|
|
7083
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7084
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7085
|
+
client.close();
|
|
7086
|
+
_shards.delete(name);
|
|
7087
|
+
_shardLastAccess.delete(name);
|
|
7088
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7089
|
+
const archivedPath = path19.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7090
|
+
renameSync4(dbPath, archivedPath);
|
|
7091
|
+
item.archivedPath = archivedPath;
|
|
7092
|
+
}
|
|
7093
|
+
} finally {
|
|
7094
|
+
try {
|
|
7095
|
+
client.close();
|
|
7096
|
+
} catch {
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
shards.push(item);
|
|
7100
|
+
}
|
|
7101
|
+
return {
|
|
7102
|
+
total: shards.length,
|
|
7103
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7104
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7105
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7106
|
+
shards
|
|
7107
|
+
};
|
|
7108
|
+
}
|
|
7006
7109
|
async function ensureShardSchema(client) {
|
|
7007
7110
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
7008
7111
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2888,6 +2888,15 @@ function readMachineId() {
|
|
|
2888
2888
|
return "";
|
|
2889
2889
|
}
|
|
2890
2890
|
}
|
|
2891
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2892
|
+
const crypto4 = __require("crypto");
|
|
2893
|
+
const iv = crypto4.randomBytes(12);
|
|
2894
|
+
const cipher = crypto4.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2895
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2896
|
+
encrypted += cipher.final("base64");
|
|
2897
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2898
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2899
|
+
}
|
|
2891
2900
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2892
2901
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2893
2902
|
try {
|
|
@@ -2906,6 +2915,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2906
2915
|
return null;
|
|
2907
2916
|
}
|
|
2908
2917
|
}
|
|
2918
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2919
|
+
const dir = getKeyDir();
|
|
2920
|
+
await mkdir3(dir, { recursive: true });
|
|
2921
|
+
const keyPath = getKeyPath();
|
|
2922
|
+
const machineKey = deriveMachineKey();
|
|
2923
|
+
if (machineKey) {
|
|
2924
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2925
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2926
|
+
await chmod2(keyPath, 384);
|
|
2927
|
+
return "encrypted";
|
|
2928
|
+
}
|
|
2929
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2930
|
+
await chmod2(keyPath, 384);
|
|
2931
|
+
return "plaintext";
|
|
2932
|
+
}
|
|
2909
2933
|
async function getMasterKey() {
|
|
2910
2934
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2911
2935
|
if (nativeValue) {
|
|
@@ -2957,6 +2981,20 @@ async function getMasterKey() {
|
|
|
2957
2981
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2958
2982
|
if (migrated) {
|
|
2959
2983
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2984
|
+
try {
|
|
2985
|
+
await unlink(keyPath);
|
|
2986
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2987
|
+
} catch {
|
|
2988
|
+
}
|
|
2989
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2990
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
2991
|
+
if (fallback === "encrypted") {
|
|
2992
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
2993
|
+
} else {
|
|
2994
|
+
process.stderr.write(
|
|
2995
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2960
2998
|
}
|
|
2961
2999
|
return key;
|
|
2962
3000
|
} catch (err) {
|
|
@@ -3229,6 +3267,7 @@ var init_memory_write_governor = __esm({
|
|
|
3229
3267
|
// src/lib/shard-manager.ts
|
|
3230
3268
|
var shard_manager_exports = {};
|
|
3231
3269
|
__export(shard_manager_exports, {
|
|
3270
|
+
auditShardHealth: () => auditShardHealth,
|
|
3232
3271
|
disposeShards: () => disposeShards,
|
|
3233
3272
|
ensureShardSchema: () => ensureShardSchema,
|
|
3234
3273
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3295,6 +3334,70 @@ function listShards() {
|
|
|
3295
3334
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3296
3335
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3297
3336
|
}
|
|
3337
|
+
async function auditShardHealth(options = {}) {
|
|
3338
|
+
if (!_encryptionKey) {
|
|
3339
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3340
|
+
}
|
|
3341
|
+
const repair = options.repair === true;
|
|
3342
|
+
const dryRun = options.dryRun === true;
|
|
3343
|
+
const names = listShards();
|
|
3344
|
+
const shards = [];
|
|
3345
|
+
for (const name of names) {
|
|
3346
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3347
|
+
const stat = statSync2(dbPath);
|
|
3348
|
+
const item = {
|
|
3349
|
+
name,
|
|
3350
|
+
path: dbPath,
|
|
3351
|
+
ok: false,
|
|
3352
|
+
unreadable: false,
|
|
3353
|
+
error: null,
|
|
3354
|
+
size: stat.size,
|
|
3355
|
+
mtime: stat.mtime.toISOString(),
|
|
3356
|
+
memoryCount: null
|
|
3357
|
+
};
|
|
3358
|
+
const client = createClient2({
|
|
3359
|
+
url: `file:${dbPath}`,
|
|
3360
|
+
encryptionKey: _encryptionKey
|
|
3361
|
+
});
|
|
3362
|
+
try {
|
|
3363
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3364
|
+
const hasMemories = await client.execute(
|
|
3365
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3366
|
+
);
|
|
3367
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3368
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3369
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3370
|
+
}
|
|
3371
|
+
item.ok = true;
|
|
3372
|
+
} catch (err) {
|
|
3373
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3374
|
+
item.error = message;
|
|
3375
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3376
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3377
|
+
client.close();
|
|
3378
|
+
_shards.delete(name);
|
|
3379
|
+
_shardLastAccess.delete(name);
|
|
3380
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3381
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3382
|
+
renameSync3(dbPath, archivedPath);
|
|
3383
|
+
item.archivedPath = archivedPath;
|
|
3384
|
+
}
|
|
3385
|
+
} finally {
|
|
3386
|
+
try {
|
|
3387
|
+
client.close();
|
|
3388
|
+
} catch {
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
shards.push(item);
|
|
3392
|
+
}
|
|
3393
|
+
return {
|
|
3394
|
+
total: shards.length,
|
|
3395
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3396
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3397
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3398
|
+
shards
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3298
3401
|
async function ensureShardSchema(client) {
|
|
3299
3402
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3300
3403
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/hooks/ingest.js
CHANGED
|
@@ -3065,6 +3065,15 @@ function readMachineId() {
|
|
|
3065
3065
|
return "";
|
|
3066
3066
|
}
|
|
3067
3067
|
}
|
|
3068
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3069
|
+
const crypto3 = __require("crypto");
|
|
3070
|
+
const iv = crypto3.randomBytes(12);
|
|
3071
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3072
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3073
|
+
encrypted += cipher.final("base64");
|
|
3074
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3075
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3076
|
+
}
|
|
3068
3077
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3069
3078
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3070
3079
|
try {
|
|
@@ -3083,6 +3092,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3083
3092
|
return null;
|
|
3084
3093
|
}
|
|
3085
3094
|
}
|
|
3095
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3096
|
+
const dir = getKeyDir();
|
|
3097
|
+
await mkdir3(dir, { recursive: true });
|
|
3098
|
+
const keyPath = getKeyPath();
|
|
3099
|
+
const machineKey = deriveMachineKey();
|
|
3100
|
+
if (machineKey) {
|
|
3101
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3102
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3103
|
+
await chmod2(keyPath, 384);
|
|
3104
|
+
return "encrypted";
|
|
3105
|
+
}
|
|
3106
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3107
|
+
await chmod2(keyPath, 384);
|
|
3108
|
+
return "plaintext";
|
|
3109
|
+
}
|
|
3086
3110
|
async function getMasterKey() {
|
|
3087
3111
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3088
3112
|
if (nativeValue) {
|
|
@@ -3134,6 +3158,20 @@ async function getMasterKey() {
|
|
|
3134
3158
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3135
3159
|
if (migrated) {
|
|
3136
3160
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3161
|
+
try {
|
|
3162
|
+
await unlink(keyPath);
|
|
3163
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3164
|
+
} catch {
|
|
3165
|
+
}
|
|
3166
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3167
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3168
|
+
if (fallback === "encrypted") {
|
|
3169
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3170
|
+
} else {
|
|
3171
|
+
process.stderr.write(
|
|
3172
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3137
3175
|
}
|
|
3138
3176
|
return key;
|
|
3139
3177
|
} catch (err) {
|
|
@@ -3406,6 +3444,7 @@ var init_memory_write_governor = __esm({
|
|
|
3406
3444
|
// src/lib/shard-manager.ts
|
|
3407
3445
|
var shard_manager_exports = {};
|
|
3408
3446
|
__export(shard_manager_exports, {
|
|
3447
|
+
auditShardHealth: () => auditShardHealth,
|
|
3409
3448
|
disposeShards: () => disposeShards,
|
|
3410
3449
|
ensureShardSchema: () => ensureShardSchema,
|
|
3411
3450
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3472,6 +3511,70 @@ function listShards() {
|
|
|
3472
3511
|
if (!existsSync10(SHARDS_DIR)) return [];
|
|
3473
3512
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3474
3513
|
}
|
|
3514
|
+
async function auditShardHealth(options = {}) {
|
|
3515
|
+
if (!_encryptionKey) {
|
|
3516
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3517
|
+
}
|
|
3518
|
+
const repair = options.repair === true;
|
|
3519
|
+
const dryRun = options.dryRun === true;
|
|
3520
|
+
const names = listShards();
|
|
3521
|
+
const shards = [];
|
|
3522
|
+
for (const name of names) {
|
|
3523
|
+
const dbPath = path11.join(SHARDS_DIR, `${name}.db`);
|
|
3524
|
+
const stat = statSync2(dbPath);
|
|
3525
|
+
const item = {
|
|
3526
|
+
name,
|
|
3527
|
+
path: dbPath,
|
|
3528
|
+
ok: false,
|
|
3529
|
+
unreadable: false,
|
|
3530
|
+
error: null,
|
|
3531
|
+
size: stat.size,
|
|
3532
|
+
mtime: stat.mtime.toISOString(),
|
|
3533
|
+
memoryCount: null
|
|
3534
|
+
};
|
|
3535
|
+
const client = createClient2({
|
|
3536
|
+
url: `file:${dbPath}`,
|
|
3537
|
+
encryptionKey: _encryptionKey
|
|
3538
|
+
});
|
|
3539
|
+
try {
|
|
3540
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3541
|
+
const hasMemories = await client.execute(
|
|
3542
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3543
|
+
);
|
|
3544
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3545
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3546
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3547
|
+
}
|
|
3548
|
+
item.ok = true;
|
|
3549
|
+
} catch (err) {
|
|
3550
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3551
|
+
item.error = message;
|
|
3552
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3553
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3554
|
+
client.close();
|
|
3555
|
+
_shards.delete(name);
|
|
3556
|
+
_shardLastAccess.delete(name);
|
|
3557
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3558
|
+
const archivedPath = path11.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3559
|
+
renameSync3(dbPath, archivedPath);
|
|
3560
|
+
item.archivedPath = archivedPath;
|
|
3561
|
+
}
|
|
3562
|
+
} finally {
|
|
3563
|
+
try {
|
|
3564
|
+
client.close();
|
|
3565
|
+
} catch {
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
shards.push(item);
|
|
3569
|
+
}
|
|
3570
|
+
return {
|
|
3571
|
+
total: shards.length,
|
|
3572
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3573
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3574
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3575
|
+
shards
|
|
3576
|
+
};
|
|
3577
|
+
}
|
|
3475
3578
|
async function ensureShardSchema(client) {
|
|
3476
3579
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3477
3580
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2899,6 +2899,15 @@ function readMachineId() {
|
|
|
2899
2899
|
return "";
|
|
2900
2900
|
}
|
|
2901
2901
|
}
|
|
2902
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2903
|
+
const crypto2 = __require("crypto");
|
|
2904
|
+
const iv = crypto2.randomBytes(12);
|
|
2905
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2906
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2907
|
+
encrypted += cipher.final("base64");
|
|
2908
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2909
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2910
|
+
}
|
|
2902
2911
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2903
2912
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2904
2913
|
try {
|
|
@@ -2917,6 +2926,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2917
2926
|
return null;
|
|
2918
2927
|
}
|
|
2919
2928
|
}
|
|
2929
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2930
|
+
const dir = getKeyDir();
|
|
2931
|
+
await mkdir3(dir, { recursive: true });
|
|
2932
|
+
const keyPath = getKeyPath();
|
|
2933
|
+
const machineKey = deriveMachineKey();
|
|
2934
|
+
if (machineKey) {
|
|
2935
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2936
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2937
|
+
await chmod2(keyPath, 384);
|
|
2938
|
+
return "encrypted";
|
|
2939
|
+
}
|
|
2940
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2941
|
+
await chmod2(keyPath, 384);
|
|
2942
|
+
return "plaintext";
|
|
2943
|
+
}
|
|
2920
2944
|
async function getMasterKey() {
|
|
2921
2945
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2922
2946
|
if (nativeValue) {
|
|
@@ -2968,6 +2992,20 @@ async function getMasterKey() {
|
|
|
2968
2992
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2969
2993
|
if (migrated) {
|
|
2970
2994
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2995
|
+
try {
|
|
2996
|
+
await unlink(keyPath);
|
|
2997
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2998
|
+
} catch {
|
|
2999
|
+
}
|
|
3000
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3001
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3002
|
+
if (fallback === "encrypted") {
|
|
3003
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3004
|
+
} else {
|
|
3005
|
+
process.stderr.write(
|
|
3006
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3007
|
+
);
|
|
3008
|
+
}
|
|
2971
3009
|
}
|
|
2972
3010
|
return key;
|
|
2973
3011
|
} catch (err) {
|
|
@@ -3240,6 +3278,7 @@ var init_memory_write_governor = __esm({
|
|
|
3240
3278
|
// src/lib/shard-manager.ts
|
|
3241
3279
|
var shard_manager_exports = {};
|
|
3242
3280
|
__export(shard_manager_exports, {
|
|
3281
|
+
auditShardHealth: () => auditShardHealth,
|
|
3243
3282
|
disposeShards: () => disposeShards,
|
|
3244
3283
|
ensureShardSchema: () => ensureShardSchema,
|
|
3245
3284
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3306,6 +3345,70 @@ function listShards() {
|
|
|
3306
3345
|
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3307
3346
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3308
3347
|
}
|
|
3348
|
+
async function auditShardHealth(options = {}) {
|
|
3349
|
+
if (!_encryptionKey) {
|
|
3350
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3351
|
+
}
|
|
3352
|
+
const repair = options.repair === true;
|
|
3353
|
+
const dryRun = options.dryRun === true;
|
|
3354
|
+
const names = listShards();
|
|
3355
|
+
const shards = [];
|
|
3356
|
+
for (const name of names) {
|
|
3357
|
+
const dbPath = path9.join(SHARDS_DIR, `${name}.db`);
|
|
3358
|
+
const stat = statSync2(dbPath);
|
|
3359
|
+
const item = {
|
|
3360
|
+
name,
|
|
3361
|
+
path: dbPath,
|
|
3362
|
+
ok: false,
|
|
3363
|
+
unreadable: false,
|
|
3364
|
+
error: null,
|
|
3365
|
+
size: stat.size,
|
|
3366
|
+
mtime: stat.mtime.toISOString(),
|
|
3367
|
+
memoryCount: null
|
|
3368
|
+
};
|
|
3369
|
+
const client = createClient2({
|
|
3370
|
+
url: `file:${dbPath}`,
|
|
3371
|
+
encryptionKey: _encryptionKey
|
|
3372
|
+
});
|
|
3373
|
+
try {
|
|
3374
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3375
|
+
const hasMemories = await client.execute(
|
|
3376
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3377
|
+
);
|
|
3378
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3379
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3380
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3381
|
+
}
|
|
3382
|
+
item.ok = true;
|
|
3383
|
+
} catch (err) {
|
|
3384
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3385
|
+
item.error = message;
|
|
3386
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3387
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3388
|
+
client.close();
|
|
3389
|
+
_shards.delete(name);
|
|
3390
|
+
_shardLastAccess.delete(name);
|
|
3391
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3392
|
+
const archivedPath = path9.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3393
|
+
renameSync3(dbPath, archivedPath);
|
|
3394
|
+
item.archivedPath = archivedPath;
|
|
3395
|
+
}
|
|
3396
|
+
} finally {
|
|
3397
|
+
try {
|
|
3398
|
+
client.close();
|
|
3399
|
+
} catch {
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
shards.push(item);
|
|
3403
|
+
}
|
|
3404
|
+
return {
|
|
3405
|
+
total: shards.length,
|
|
3406
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3407
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3408
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3409
|
+
shards
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3309
3412
|
async function ensureShardSchema(client) {
|
|
3310
3413
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3311
3414
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2899,6 +2899,15 @@ function readMachineId() {
|
|
|
2899
2899
|
return "";
|
|
2900
2900
|
}
|
|
2901
2901
|
}
|
|
2902
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2903
|
+
const crypto2 = __require("crypto");
|
|
2904
|
+
const iv = crypto2.randomBytes(12);
|
|
2905
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2906
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2907
|
+
encrypted += cipher.final("base64");
|
|
2908
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2909
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2910
|
+
}
|
|
2902
2911
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2903
2912
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2904
2913
|
try {
|
|
@@ -2917,6 +2926,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2917
2926
|
return null;
|
|
2918
2927
|
}
|
|
2919
2928
|
}
|
|
2929
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2930
|
+
const dir = getKeyDir();
|
|
2931
|
+
await mkdir3(dir, { recursive: true });
|
|
2932
|
+
const keyPath = getKeyPath();
|
|
2933
|
+
const machineKey = deriveMachineKey();
|
|
2934
|
+
if (machineKey) {
|
|
2935
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2936
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2937
|
+
await chmod2(keyPath, 384);
|
|
2938
|
+
return "encrypted";
|
|
2939
|
+
}
|
|
2940
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2941
|
+
await chmod2(keyPath, 384);
|
|
2942
|
+
return "plaintext";
|
|
2943
|
+
}
|
|
2920
2944
|
async function getMasterKey() {
|
|
2921
2945
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2922
2946
|
if (nativeValue) {
|
|
@@ -2968,6 +2992,20 @@ async function getMasterKey() {
|
|
|
2968
2992
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2969
2993
|
if (migrated) {
|
|
2970
2994
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2995
|
+
try {
|
|
2996
|
+
await unlink(keyPath);
|
|
2997
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2998
|
+
} catch {
|
|
2999
|
+
}
|
|
3000
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3001
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3002
|
+
if (fallback === "encrypted") {
|
|
3003
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3004
|
+
} else {
|
|
3005
|
+
process.stderr.write(
|
|
3006
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3007
|
+
);
|
|
3008
|
+
}
|
|
2971
3009
|
}
|
|
2972
3010
|
return key;
|
|
2973
3011
|
} catch (err) {
|
|
@@ -3240,6 +3278,7 @@ var init_memory_write_governor = __esm({
|
|
|
3240
3278
|
// src/lib/shard-manager.ts
|
|
3241
3279
|
var shard_manager_exports = {};
|
|
3242
3280
|
__export(shard_manager_exports, {
|
|
3281
|
+
auditShardHealth: () => auditShardHealth,
|
|
3243
3282
|
disposeShards: () => disposeShards,
|
|
3244
3283
|
ensureShardSchema: () => ensureShardSchema,
|
|
3245
3284
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3306,6 +3345,70 @@ function listShards() {
|
|
|
3306
3345
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3307
3346
|
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3308
3347
|
}
|
|
3348
|
+
async function auditShardHealth(options = {}) {
|
|
3349
|
+
if (!_encryptionKey) {
|
|
3350
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3351
|
+
}
|
|
3352
|
+
const repair = options.repair === true;
|
|
3353
|
+
const dryRun = options.dryRun === true;
|
|
3354
|
+
const names = listShards();
|
|
3355
|
+
const shards = [];
|
|
3356
|
+
for (const name of names) {
|
|
3357
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3358
|
+
const stat = statSync2(dbPath);
|
|
3359
|
+
const item = {
|
|
3360
|
+
name,
|
|
3361
|
+
path: dbPath,
|
|
3362
|
+
ok: false,
|
|
3363
|
+
unreadable: false,
|
|
3364
|
+
error: null,
|
|
3365
|
+
size: stat.size,
|
|
3366
|
+
mtime: stat.mtime.toISOString(),
|
|
3367
|
+
memoryCount: null
|
|
3368
|
+
};
|
|
3369
|
+
const client = createClient2({
|
|
3370
|
+
url: `file:${dbPath}`,
|
|
3371
|
+
encryptionKey: _encryptionKey
|
|
3372
|
+
});
|
|
3373
|
+
try {
|
|
3374
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3375
|
+
const hasMemories = await client.execute(
|
|
3376
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3377
|
+
);
|
|
3378
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3379
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3380
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3381
|
+
}
|
|
3382
|
+
item.ok = true;
|
|
3383
|
+
} catch (err) {
|
|
3384
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3385
|
+
item.error = message;
|
|
3386
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3387
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3388
|
+
client.close();
|
|
3389
|
+
_shards.delete(name);
|
|
3390
|
+
_shardLastAccess.delete(name);
|
|
3391
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3392
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3393
|
+
renameSync3(dbPath, archivedPath);
|
|
3394
|
+
item.archivedPath = archivedPath;
|
|
3395
|
+
}
|
|
3396
|
+
} finally {
|
|
3397
|
+
try {
|
|
3398
|
+
client.close();
|
|
3399
|
+
} catch {
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
shards.push(item);
|
|
3403
|
+
}
|
|
3404
|
+
return {
|
|
3405
|
+
total: shards.length,
|
|
3406
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3407
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3408
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3409
|
+
shards
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3309
3412
|
async function ensureShardSchema(client) {
|
|
3310
3413
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3311
3414
|
await client.execute("PRAGMA busy_timeout = 30000");
|