@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
|
@@ -6844,6 +6844,15 @@ function readMachineId() {
|
|
|
6844
6844
|
return "";
|
|
6845
6845
|
}
|
|
6846
6846
|
}
|
|
6847
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6848
|
+
const crypto7 = __require("crypto");
|
|
6849
|
+
const iv = crypto7.randomBytes(12);
|
|
6850
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6851
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6852
|
+
encrypted += cipher.final("base64");
|
|
6853
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6854
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6855
|
+
}
|
|
6847
6856
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6848
6857
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6849
6858
|
try {
|
|
@@ -6862,6 +6871,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6862
6871
|
return null;
|
|
6863
6872
|
}
|
|
6864
6873
|
}
|
|
6874
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6875
|
+
const dir = getKeyDir();
|
|
6876
|
+
await mkdir4(dir, { recursive: true });
|
|
6877
|
+
const keyPath = getKeyPath();
|
|
6878
|
+
const machineKey = deriveMachineKey();
|
|
6879
|
+
if (machineKey) {
|
|
6880
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6881
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6882
|
+
await chmod2(keyPath, 384);
|
|
6883
|
+
return "encrypted";
|
|
6884
|
+
}
|
|
6885
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6886
|
+
await chmod2(keyPath, 384);
|
|
6887
|
+
return "plaintext";
|
|
6888
|
+
}
|
|
6865
6889
|
async function getMasterKey() {
|
|
6866
6890
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6867
6891
|
if (nativeValue) {
|
|
@@ -6913,6 +6937,20 @@ async function getMasterKey() {
|
|
|
6913
6937
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6914
6938
|
if (migrated) {
|
|
6915
6939
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6940
|
+
try {
|
|
6941
|
+
await unlink(keyPath);
|
|
6942
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6943
|
+
} catch {
|
|
6944
|
+
}
|
|
6945
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6946
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6947
|
+
if (fallback === "encrypted") {
|
|
6948
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6949
|
+
} else {
|
|
6950
|
+
process.stderr.write(
|
|
6951
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6952
|
+
);
|
|
6953
|
+
}
|
|
6916
6954
|
}
|
|
6917
6955
|
return key;
|
|
6918
6956
|
} catch (err) {
|
|
@@ -7130,6 +7168,7 @@ var init_memory_write_governor = __esm({
|
|
|
7130
7168
|
// src/lib/shard-manager.ts
|
|
7131
7169
|
var shard_manager_exports = {};
|
|
7132
7170
|
__export(shard_manager_exports, {
|
|
7171
|
+
auditShardHealth: () => auditShardHealth,
|
|
7133
7172
|
disposeShards: () => disposeShards,
|
|
7134
7173
|
ensureShardSchema: () => ensureShardSchema,
|
|
7135
7174
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -7196,6 +7235,70 @@ function listShards() {
|
|
|
7196
7235
|
if (!existsSync17(SHARDS_DIR)) return [];
|
|
7197
7236
|
return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
7198
7237
|
}
|
|
7238
|
+
async function auditShardHealth(options = {}) {
|
|
7239
|
+
if (!_encryptionKey) {
|
|
7240
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7241
|
+
}
|
|
7242
|
+
const repair = options.repair === true;
|
|
7243
|
+
const dryRun = options.dryRun === true;
|
|
7244
|
+
const names = listShards();
|
|
7245
|
+
const shards = [];
|
|
7246
|
+
for (const name of names) {
|
|
7247
|
+
const dbPath = path21.join(SHARDS_DIR, `${name}.db`);
|
|
7248
|
+
const stat = statSync2(dbPath);
|
|
7249
|
+
const item = {
|
|
7250
|
+
name,
|
|
7251
|
+
path: dbPath,
|
|
7252
|
+
ok: false,
|
|
7253
|
+
unreadable: false,
|
|
7254
|
+
error: null,
|
|
7255
|
+
size: stat.size,
|
|
7256
|
+
mtime: stat.mtime.toISOString(),
|
|
7257
|
+
memoryCount: null
|
|
7258
|
+
};
|
|
7259
|
+
const client = createClient2({
|
|
7260
|
+
url: `file:${dbPath}`,
|
|
7261
|
+
encryptionKey: _encryptionKey
|
|
7262
|
+
});
|
|
7263
|
+
try {
|
|
7264
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7265
|
+
const hasMemories = await client.execute(
|
|
7266
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7267
|
+
);
|
|
7268
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7269
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7270
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7271
|
+
}
|
|
7272
|
+
item.ok = true;
|
|
7273
|
+
} catch (err) {
|
|
7274
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7275
|
+
item.error = message;
|
|
7276
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7277
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7278
|
+
client.close();
|
|
7279
|
+
_shards.delete(name);
|
|
7280
|
+
_shardLastAccess.delete(name);
|
|
7281
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7282
|
+
const archivedPath = path21.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7283
|
+
renameSync4(dbPath, archivedPath);
|
|
7284
|
+
item.archivedPath = archivedPath;
|
|
7285
|
+
}
|
|
7286
|
+
} finally {
|
|
7287
|
+
try {
|
|
7288
|
+
client.close();
|
|
7289
|
+
} catch {
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
shards.push(item);
|
|
7293
|
+
}
|
|
7294
|
+
return {
|
|
7295
|
+
total: shards.length,
|
|
7296
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7297
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7298
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7299
|
+
shards
|
|
7300
|
+
};
|
|
7301
|
+
}
|
|
7199
7302
|
async function ensureShardSchema(client) {
|
|
7200
7303
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
7201
7304
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3297,6 +3297,15 @@ function readMachineId() {
|
|
|
3297
3297
|
return "";
|
|
3298
3298
|
}
|
|
3299
3299
|
}
|
|
3300
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3301
|
+
const crypto3 = __require("crypto");
|
|
3302
|
+
const iv = crypto3.randomBytes(12);
|
|
3303
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3304
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3305
|
+
encrypted += cipher.final("base64");
|
|
3306
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3307
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3308
|
+
}
|
|
3300
3309
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3301
3310
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3302
3311
|
try {
|
|
@@ -3315,6 +3324,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3315
3324
|
return null;
|
|
3316
3325
|
}
|
|
3317
3326
|
}
|
|
3327
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3328
|
+
const dir = getKeyDir();
|
|
3329
|
+
await mkdir3(dir, { recursive: true });
|
|
3330
|
+
const keyPath = getKeyPath();
|
|
3331
|
+
const machineKey = deriveMachineKey();
|
|
3332
|
+
if (machineKey) {
|
|
3333
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3334
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3335
|
+
await chmod2(keyPath, 384);
|
|
3336
|
+
return "encrypted";
|
|
3337
|
+
}
|
|
3338
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3339
|
+
await chmod2(keyPath, 384);
|
|
3340
|
+
return "plaintext";
|
|
3341
|
+
}
|
|
3318
3342
|
async function getMasterKey() {
|
|
3319
3343
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3320
3344
|
if (nativeValue) {
|
|
@@ -3366,6 +3390,20 @@ async function getMasterKey() {
|
|
|
3366
3390
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3367
3391
|
if (migrated) {
|
|
3368
3392
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3393
|
+
try {
|
|
3394
|
+
await unlink(keyPath);
|
|
3395
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3396
|
+
} catch {
|
|
3397
|
+
}
|
|
3398
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3399
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3400
|
+
if (fallback === "encrypted") {
|
|
3401
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3402
|
+
} else {
|
|
3403
|
+
process.stderr.write(
|
|
3404
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3405
|
+
);
|
|
3406
|
+
}
|
|
3369
3407
|
}
|
|
3370
3408
|
return key;
|
|
3371
3409
|
} catch (err) {
|
|
@@ -3638,6 +3676,7 @@ var init_memory_write_governor = __esm({
|
|
|
3638
3676
|
// src/lib/shard-manager.ts
|
|
3639
3677
|
var shard_manager_exports = {};
|
|
3640
3678
|
__export(shard_manager_exports, {
|
|
3679
|
+
auditShardHealth: () => auditShardHealth,
|
|
3641
3680
|
disposeShards: () => disposeShards,
|
|
3642
3681
|
ensureShardSchema: () => ensureShardSchema,
|
|
3643
3682
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3704,6 +3743,70 @@ function listShards() {
|
|
|
3704
3743
|
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3705
3744
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3706
3745
|
}
|
|
3746
|
+
async function auditShardHealth(options = {}) {
|
|
3747
|
+
if (!_encryptionKey) {
|
|
3748
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3749
|
+
}
|
|
3750
|
+
const repair = options.repair === true;
|
|
3751
|
+
const dryRun = options.dryRun === true;
|
|
3752
|
+
const names = listShards();
|
|
3753
|
+
const shards = [];
|
|
3754
|
+
for (const name of names) {
|
|
3755
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3756
|
+
const stat = statSync2(dbPath);
|
|
3757
|
+
const item = {
|
|
3758
|
+
name,
|
|
3759
|
+
path: dbPath,
|
|
3760
|
+
ok: false,
|
|
3761
|
+
unreadable: false,
|
|
3762
|
+
error: null,
|
|
3763
|
+
size: stat.size,
|
|
3764
|
+
mtime: stat.mtime.toISOString(),
|
|
3765
|
+
memoryCount: null
|
|
3766
|
+
};
|
|
3767
|
+
const client = createClient2({
|
|
3768
|
+
url: `file:${dbPath}`,
|
|
3769
|
+
encryptionKey: _encryptionKey
|
|
3770
|
+
});
|
|
3771
|
+
try {
|
|
3772
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3773
|
+
const hasMemories = await client.execute(
|
|
3774
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3775
|
+
);
|
|
3776
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3777
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3778
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3779
|
+
}
|
|
3780
|
+
item.ok = true;
|
|
3781
|
+
} catch (err) {
|
|
3782
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3783
|
+
item.error = message;
|
|
3784
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3785
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3786
|
+
client.close();
|
|
3787
|
+
_shards.delete(name);
|
|
3788
|
+
_shardLastAccess.delete(name);
|
|
3789
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3790
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3791
|
+
renameSync3(dbPath, archivedPath);
|
|
3792
|
+
item.archivedPath = archivedPath;
|
|
3793
|
+
}
|
|
3794
|
+
} finally {
|
|
3795
|
+
try {
|
|
3796
|
+
client.close();
|
|
3797
|
+
} catch {
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
shards.push(item);
|
|
3801
|
+
}
|
|
3802
|
+
return {
|
|
3803
|
+
total: shards.length,
|
|
3804
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3805
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3806
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3807
|
+
shards
|
|
3808
|
+
};
|
|
3809
|
+
}
|
|
3707
3810
|
async function ensureShardSchema(client) {
|
|
3708
3811
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3709
3812
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/hooks/stop.js
CHANGED
|
@@ -3345,6 +3345,15 @@ function readMachineId() {
|
|
|
3345
3345
|
return "";
|
|
3346
3346
|
}
|
|
3347
3347
|
}
|
|
3348
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3349
|
+
const crypto2 = __require("crypto");
|
|
3350
|
+
const iv = crypto2.randomBytes(12);
|
|
3351
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3352
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3353
|
+
encrypted += cipher.final("base64");
|
|
3354
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3355
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3356
|
+
}
|
|
3348
3357
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3349
3358
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3350
3359
|
try {
|
|
@@ -3363,6 +3372,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3363
3372
|
return null;
|
|
3364
3373
|
}
|
|
3365
3374
|
}
|
|
3375
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3376
|
+
const dir = getKeyDir();
|
|
3377
|
+
await mkdir3(dir, { recursive: true });
|
|
3378
|
+
const keyPath = getKeyPath();
|
|
3379
|
+
const machineKey = deriveMachineKey();
|
|
3380
|
+
if (machineKey) {
|
|
3381
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3382
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3383
|
+
await chmod2(keyPath, 384);
|
|
3384
|
+
return "encrypted";
|
|
3385
|
+
}
|
|
3386
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3387
|
+
await chmod2(keyPath, 384);
|
|
3388
|
+
return "plaintext";
|
|
3389
|
+
}
|
|
3366
3390
|
async function getMasterKey() {
|
|
3367
3391
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3368
3392
|
if (nativeValue) {
|
|
@@ -3414,6 +3438,20 @@ async function getMasterKey() {
|
|
|
3414
3438
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3415
3439
|
if (migrated) {
|
|
3416
3440
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3441
|
+
try {
|
|
3442
|
+
await unlink(keyPath);
|
|
3443
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3444
|
+
} catch {
|
|
3445
|
+
}
|
|
3446
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3447
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3448
|
+
if (fallback === "encrypted") {
|
|
3449
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3450
|
+
} else {
|
|
3451
|
+
process.stderr.write(
|
|
3452
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3417
3455
|
}
|
|
3418
3456
|
return key;
|
|
3419
3457
|
} catch (err) {
|
|
@@ -3686,6 +3724,7 @@ var init_memory_write_governor = __esm({
|
|
|
3686
3724
|
// src/lib/shard-manager.ts
|
|
3687
3725
|
var shard_manager_exports = {};
|
|
3688
3726
|
__export(shard_manager_exports, {
|
|
3727
|
+
auditShardHealth: () => auditShardHealth,
|
|
3689
3728
|
disposeShards: () => disposeShards,
|
|
3690
3729
|
ensureShardSchema: () => ensureShardSchema,
|
|
3691
3730
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3752,6 +3791,70 @@ function listShards() {
|
|
|
3752
3791
|
if (!existsSync12(SHARDS_DIR)) return [];
|
|
3753
3792
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3754
3793
|
}
|
|
3794
|
+
async function auditShardHealth(options = {}) {
|
|
3795
|
+
if (!_encryptionKey) {
|
|
3796
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3797
|
+
}
|
|
3798
|
+
const repair = options.repair === true;
|
|
3799
|
+
const dryRun = options.dryRun === true;
|
|
3800
|
+
const names = listShards();
|
|
3801
|
+
const shards = [];
|
|
3802
|
+
for (const name of names) {
|
|
3803
|
+
const dbPath = path14.join(SHARDS_DIR, `${name}.db`);
|
|
3804
|
+
const stat = statSync2(dbPath);
|
|
3805
|
+
const item = {
|
|
3806
|
+
name,
|
|
3807
|
+
path: dbPath,
|
|
3808
|
+
ok: false,
|
|
3809
|
+
unreadable: false,
|
|
3810
|
+
error: null,
|
|
3811
|
+
size: stat.size,
|
|
3812
|
+
mtime: stat.mtime.toISOString(),
|
|
3813
|
+
memoryCount: null
|
|
3814
|
+
};
|
|
3815
|
+
const client = createClient2({
|
|
3816
|
+
url: `file:${dbPath}`,
|
|
3817
|
+
encryptionKey: _encryptionKey
|
|
3818
|
+
});
|
|
3819
|
+
try {
|
|
3820
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3821
|
+
const hasMemories = await client.execute(
|
|
3822
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3823
|
+
);
|
|
3824
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3825
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3826
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3827
|
+
}
|
|
3828
|
+
item.ok = true;
|
|
3829
|
+
} catch (err) {
|
|
3830
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3831
|
+
item.error = message;
|
|
3832
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3833
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3834
|
+
client.close();
|
|
3835
|
+
_shards.delete(name);
|
|
3836
|
+
_shardLastAccess.delete(name);
|
|
3837
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3838
|
+
const archivedPath = path14.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3839
|
+
renameSync4(dbPath, archivedPath);
|
|
3840
|
+
item.archivedPath = archivedPath;
|
|
3841
|
+
}
|
|
3842
|
+
} finally {
|
|
3843
|
+
try {
|
|
3844
|
+
client.close();
|
|
3845
|
+
} catch {
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
shards.push(item);
|
|
3849
|
+
}
|
|
3850
|
+
return {
|
|
3851
|
+
total: shards.length,
|
|
3852
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3853
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3854
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3855
|
+
shards
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3755
3858
|
async function ensureShardSchema(client) {
|
|
3756
3859
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3757
3860
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3326,6 +3326,15 @@ function readMachineId() {
|
|
|
3326
3326
|
return "";
|
|
3327
3327
|
}
|
|
3328
3328
|
}
|
|
3329
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3330
|
+
const crypto2 = __require("crypto");
|
|
3331
|
+
const iv = crypto2.randomBytes(12);
|
|
3332
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3333
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3334
|
+
encrypted += cipher.final("base64");
|
|
3335
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3336
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3337
|
+
}
|
|
3329
3338
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3330
3339
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3331
3340
|
try {
|
|
@@ -3344,6 +3353,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3344
3353
|
return null;
|
|
3345
3354
|
}
|
|
3346
3355
|
}
|
|
3356
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3357
|
+
const dir = getKeyDir();
|
|
3358
|
+
await mkdir3(dir, { recursive: true });
|
|
3359
|
+
const keyPath = getKeyPath();
|
|
3360
|
+
const machineKey = deriveMachineKey();
|
|
3361
|
+
if (machineKey) {
|
|
3362
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3363
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3364
|
+
await chmod2(keyPath, 384);
|
|
3365
|
+
return "encrypted";
|
|
3366
|
+
}
|
|
3367
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3368
|
+
await chmod2(keyPath, 384);
|
|
3369
|
+
return "plaintext";
|
|
3370
|
+
}
|
|
3347
3371
|
async function getMasterKey() {
|
|
3348
3372
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3349
3373
|
if (nativeValue) {
|
|
@@ -3395,6 +3419,20 @@ async function getMasterKey() {
|
|
|
3395
3419
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3396
3420
|
if (migrated) {
|
|
3397
3421
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3422
|
+
try {
|
|
3423
|
+
await unlink(keyPath);
|
|
3424
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3425
|
+
} catch {
|
|
3426
|
+
}
|
|
3427
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3428
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3429
|
+
if (fallback === "encrypted") {
|
|
3430
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3431
|
+
} else {
|
|
3432
|
+
process.stderr.write(
|
|
3433
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3434
|
+
);
|
|
3435
|
+
}
|
|
3398
3436
|
}
|
|
3399
3437
|
return key;
|
|
3400
3438
|
} catch (err) {
|
|
@@ -3667,6 +3705,7 @@ var init_memory_write_governor = __esm({
|
|
|
3667
3705
|
// src/lib/shard-manager.ts
|
|
3668
3706
|
var shard_manager_exports = {};
|
|
3669
3707
|
__export(shard_manager_exports, {
|
|
3708
|
+
auditShardHealth: () => auditShardHealth,
|
|
3670
3709
|
disposeShards: () => disposeShards,
|
|
3671
3710
|
ensureShardSchema: () => ensureShardSchema,
|
|
3672
3711
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3733,6 +3772,70 @@ function listShards() {
|
|
|
3733
3772
|
if (!existsSync12(SHARDS_DIR)) return [];
|
|
3734
3773
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3735
3774
|
}
|
|
3775
|
+
async function auditShardHealth(options = {}) {
|
|
3776
|
+
if (!_encryptionKey) {
|
|
3777
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3778
|
+
}
|
|
3779
|
+
const repair = options.repair === true;
|
|
3780
|
+
const dryRun = options.dryRun === true;
|
|
3781
|
+
const names = listShards();
|
|
3782
|
+
const shards = [];
|
|
3783
|
+
for (const name of names) {
|
|
3784
|
+
const dbPath = path14.join(SHARDS_DIR, `${name}.db`);
|
|
3785
|
+
const stat = statSync2(dbPath);
|
|
3786
|
+
const item = {
|
|
3787
|
+
name,
|
|
3788
|
+
path: dbPath,
|
|
3789
|
+
ok: false,
|
|
3790
|
+
unreadable: false,
|
|
3791
|
+
error: null,
|
|
3792
|
+
size: stat.size,
|
|
3793
|
+
mtime: stat.mtime.toISOString(),
|
|
3794
|
+
memoryCount: null
|
|
3795
|
+
};
|
|
3796
|
+
const client = createClient2({
|
|
3797
|
+
url: `file:${dbPath}`,
|
|
3798
|
+
encryptionKey: _encryptionKey
|
|
3799
|
+
});
|
|
3800
|
+
try {
|
|
3801
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3802
|
+
const hasMemories = await client.execute(
|
|
3803
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3804
|
+
);
|
|
3805
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3806
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3807
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3808
|
+
}
|
|
3809
|
+
item.ok = true;
|
|
3810
|
+
} catch (err) {
|
|
3811
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3812
|
+
item.error = message;
|
|
3813
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3814
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3815
|
+
client.close();
|
|
3816
|
+
_shards.delete(name);
|
|
3817
|
+
_shardLastAccess.delete(name);
|
|
3818
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3819
|
+
const archivedPath = path14.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3820
|
+
renameSync4(dbPath, archivedPath);
|
|
3821
|
+
item.archivedPath = archivedPath;
|
|
3822
|
+
}
|
|
3823
|
+
} finally {
|
|
3824
|
+
try {
|
|
3825
|
+
client.close();
|
|
3826
|
+
} catch {
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
shards.push(item);
|
|
3830
|
+
}
|
|
3831
|
+
return {
|
|
3832
|
+
total: shards.length,
|
|
3833
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3834
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3835
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3836
|
+
shards
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3736
3839
|
async function ensureShardSchema(client) {
|
|
3737
3840
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3738
3841
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3017,6 +3017,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3017
3017
|
return null;
|
|
3018
3018
|
}
|
|
3019
3019
|
}
|
|
3020
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3021
|
+
const dir = getKeyDir();
|
|
3022
|
+
await mkdir3(dir, { recursive: true });
|
|
3023
|
+
const keyPath = getKeyPath();
|
|
3024
|
+
const machineKey = deriveMachineKey();
|
|
3025
|
+
if (machineKey) {
|
|
3026
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3027
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3028
|
+
await chmod2(keyPath, 384);
|
|
3029
|
+
return "encrypted";
|
|
3030
|
+
}
|
|
3031
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3032
|
+
await chmod2(keyPath, 384);
|
|
3033
|
+
return "plaintext";
|
|
3034
|
+
}
|
|
3020
3035
|
async function getMasterKey() {
|
|
3021
3036
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3022
3037
|
if (nativeValue) {
|
|
@@ -3068,6 +3083,20 @@ async function getMasterKey() {
|
|
|
3068
3083
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3069
3084
|
if (migrated) {
|
|
3070
3085
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3086
|
+
try {
|
|
3087
|
+
await unlink(keyPath);
|
|
3088
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3089
|
+
} catch {
|
|
3090
|
+
}
|
|
3091
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3092
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3093
|
+
if (fallback === "encrypted") {
|
|
3094
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3095
|
+
} else {
|
|
3096
|
+
process.stderr.write(
|
|
3097
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3071
3100
|
}
|
|
3072
3101
|
return key;
|
|
3073
3102
|
} catch (err) {
|
|
@@ -3091,18 +3120,10 @@ async function setMasterKey(key) {
|
|
|
3091
3120
|
} catch {
|
|
3092
3121
|
}
|
|
3093
3122
|
}
|
|
3094
|
-
const
|
|
3095
|
-
|
|
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);
|
|
3123
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
3124
|
+
if (fallback === "encrypted") {
|
|
3102
3125
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
3103
3126
|
} else {
|
|
3104
|
-
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3105
|
-
await chmod2(keyPath, 384);
|
|
3106
3127
|
process.stderr.write(
|
|
3107
3128
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
3108
3129
|
);
|
|
@@ -3220,6 +3241,7 @@ var init_state_bus = __esm({
|
|
|
3220
3241
|
// src/lib/shard-manager.ts
|
|
3221
3242
|
var shard_manager_exports = {};
|
|
3222
3243
|
__export(shard_manager_exports, {
|
|
3244
|
+
auditShardHealth: () => auditShardHealth,
|
|
3223
3245
|
disposeShards: () => disposeShards,
|
|
3224
3246
|
ensureShardSchema: () => ensureShardSchema,
|
|
3225
3247
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3286,6 +3308,70 @@ function listShards() {
|
|
|
3286
3308
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3287
3309
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3288
3310
|
}
|
|
3311
|
+
async function auditShardHealth(options = {}) {
|
|
3312
|
+
if (!_encryptionKey) {
|
|
3313
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3314
|
+
}
|
|
3315
|
+
const repair = options.repair === true;
|
|
3316
|
+
const dryRun = options.dryRun === true;
|
|
3317
|
+
const names = listShards();
|
|
3318
|
+
const shards = [];
|
|
3319
|
+
for (const name of names) {
|
|
3320
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3321
|
+
const stat = statSync2(dbPath);
|
|
3322
|
+
const item = {
|
|
3323
|
+
name,
|
|
3324
|
+
path: dbPath,
|
|
3325
|
+
ok: false,
|
|
3326
|
+
unreadable: false,
|
|
3327
|
+
error: null,
|
|
3328
|
+
size: stat.size,
|
|
3329
|
+
mtime: stat.mtime.toISOString(),
|
|
3330
|
+
memoryCount: null
|
|
3331
|
+
};
|
|
3332
|
+
const client = createClient2({
|
|
3333
|
+
url: `file:${dbPath}`,
|
|
3334
|
+
encryptionKey: _encryptionKey
|
|
3335
|
+
});
|
|
3336
|
+
try {
|
|
3337
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3338
|
+
const hasMemories = await client.execute(
|
|
3339
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3340
|
+
);
|
|
3341
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3342
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3343
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3344
|
+
}
|
|
3345
|
+
item.ok = true;
|
|
3346
|
+
} catch (err) {
|
|
3347
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3348
|
+
item.error = message;
|
|
3349
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3350
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3351
|
+
client.close();
|
|
3352
|
+
_shards.delete(name);
|
|
3353
|
+
_shardLastAccess.delete(name);
|
|
3354
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3355
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3356
|
+
renameSync3(dbPath, archivedPath);
|
|
3357
|
+
item.archivedPath = archivedPath;
|
|
3358
|
+
}
|
|
3359
|
+
} finally {
|
|
3360
|
+
try {
|
|
3361
|
+
client.close();
|
|
3362
|
+
} catch {
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
shards.push(item);
|
|
3366
|
+
}
|
|
3367
|
+
return {
|
|
3368
|
+
total: shards.length,
|
|
3369
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3370
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3371
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3372
|
+
shards
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3289
3375
|
async function ensureShardSchema(client) {
|
|
3290
3376
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3291
3377
|
await client.execute("PRAGMA busy_timeout = 30000");
|