@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
|
@@ -2930,6 +2930,15 @@ function readMachineId() {
|
|
|
2930
2930
|
return "";
|
|
2931
2931
|
}
|
|
2932
2932
|
}
|
|
2933
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2934
|
+
const crypto8 = __require("crypto");
|
|
2935
|
+
const iv = crypto8.randomBytes(12);
|
|
2936
|
+
const cipher = crypto8.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2937
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2938
|
+
encrypted += cipher.final("base64");
|
|
2939
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2940
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2941
|
+
}
|
|
2933
2942
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2934
2943
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2935
2944
|
try {
|
|
@@ -2948,6 +2957,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2948
2957
|
return null;
|
|
2949
2958
|
}
|
|
2950
2959
|
}
|
|
2960
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2961
|
+
const dir = getKeyDir();
|
|
2962
|
+
await mkdir3(dir, { recursive: true });
|
|
2963
|
+
const keyPath = getKeyPath();
|
|
2964
|
+
const machineKey = deriveMachineKey();
|
|
2965
|
+
if (machineKey) {
|
|
2966
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2967
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2968
|
+
await chmod2(keyPath, 384);
|
|
2969
|
+
return "encrypted";
|
|
2970
|
+
}
|
|
2971
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2972
|
+
await chmod2(keyPath, 384);
|
|
2973
|
+
return "plaintext";
|
|
2974
|
+
}
|
|
2951
2975
|
async function getMasterKey() {
|
|
2952
2976
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2953
2977
|
if (nativeValue) {
|
|
@@ -2999,6 +3023,20 @@ async function getMasterKey() {
|
|
|
2999
3023
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3000
3024
|
if (migrated) {
|
|
3001
3025
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3026
|
+
try {
|
|
3027
|
+
await unlink(keyPath);
|
|
3028
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3029
|
+
} catch {
|
|
3030
|
+
}
|
|
3031
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3032
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3033
|
+
if (fallback === "encrypted") {
|
|
3034
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3035
|
+
} else {
|
|
3036
|
+
process.stderr.write(
|
|
3037
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3038
|
+
);
|
|
3039
|
+
}
|
|
3002
3040
|
}
|
|
3003
3041
|
return key;
|
|
3004
3042
|
} catch (err) {
|
|
@@ -3271,6 +3309,7 @@ var init_memory_write_governor = __esm({
|
|
|
3271
3309
|
// src/lib/shard-manager.ts
|
|
3272
3310
|
var shard_manager_exports = {};
|
|
3273
3311
|
__export(shard_manager_exports, {
|
|
3312
|
+
auditShardHealth: () => auditShardHealth,
|
|
3274
3313
|
disposeShards: () => disposeShards,
|
|
3275
3314
|
ensureShardSchema: () => ensureShardSchema,
|
|
3276
3315
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3337,6 +3376,70 @@ function listShards() {
|
|
|
3337
3376
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3338
3377
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3339
3378
|
}
|
|
3379
|
+
async function auditShardHealth(options = {}) {
|
|
3380
|
+
if (!_encryptionKey) {
|
|
3381
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3382
|
+
}
|
|
3383
|
+
const repair = options.repair === true;
|
|
3384
|
+
const dryRun = options.dryRun === true;
|
|
3385
|
+
const names = listShards();
|
|
3386
|
+
const shards = [];
|
|
3387
|
+
for (const name of names) {
|
|
3388
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3389
|
+
const stat = statSync2(dbPath);
|
|
3390
|
+
const item = {
|
|
3391
|
+
name,
|
|
3392
|
+
path: dbPath,
|
|
3393
|
+
ok: false,
|
|
3394
|
+
unreadable: false,
|
|
3395
|
+
error: null,
|
|
3396
|
+
size: stat.size,
|
|
3397
|
+
mtime: stat.mtime.toISOString(),
|
|
3398
|
+
memoryCount: null
|
|
3399
|
+
};
|
|
3400
|
+
const client = createClient2({
|
|
3401
|
+
url: `file:${dbPath}`,
|
|
3402
|
+
encryptionKey: _encryptionKey
|
|
3403
|
+
});
|
|
3404
|
+
try {
|
|
3405
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3406
|
+
const hasMemories = await client.execute(
|
|
3407
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3408
|
+
);
|
|
3409
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3410
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3411
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3412
|
+
}
|
|
3413
|
+
item.ok = true;
|
|
3414
|
+
} catch (err) {
|
|
3415
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3416
|
+
item.error = message;
|
|
3417
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3418
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3419
|
+
client.close();
|
|
3420
|
+
_shards.delete(name);
|
|
3421
|
+
_shardLastAccess.delete(name);
|
|
3422
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3423
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3424
|
+
renameSync3(dbPath, archivedPath);
|
|
3425
|
+
item.archivedPath = archivedPath;
|
|
3426
|
+
}
|
|
3427
|
+
} finally {
|
|
3428
|
+
try {
|
|
3429
|
+
client.close();
|
|
3430
|
+
} catch {
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
shards.push(item);
|
|
3434
|
+
}
|
|
3435
|
+
return {
|
|
3436
|
+
total: shards.length,
|
|
3437
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3438
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3439
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3440
|
+
shards
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3340
3443
|
async function ensureShardSchema(client) {
|
|
3341
3444
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3342
3445
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2666,6 +2666,7 @@ var init_database = __esm({
|
|
|
2666
2666
|
// src/lib/shard-manager.ts
|
|
2667
2667
|
var shard_manager_exports = {};
|
|
2668
2668
|
__export(shard_manager_exports, {
|
|
2669
|
+
auditShardHealth: () => auditShardHealth,
|
|
2669
2670
|
disposeShards: () => disposeShards,
|
|
2670
2671
|
ensureShardSchema: () => ensureShardSchema,
|
|
2671
2672
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2732,6 +2733,70 @@ function listShards() {
|
|
|
2732
2733
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2733
2734
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2734
2735
|
}
|
|
2736
|
+
async function auditShardHealth(options = {}) {
|
|
2737
|
+
if (!_encryptionKey) {
|
|
2738
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2739
|
+
}
|
|
2740
|
+
const repair = options.repair === true;
|
|
2741
|
+
const dryRun = options.dryRun === true;
|
|
2742
|
+
const names = listShards();
|
|
2743
|
+
const shards = [];
|
|
2744
|
+
for (const name of names) {
|
|
2745
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2746
|
+
const stat = statSync2(dbPath);
|
|
2747
|
+
const item = {
|
|
2748
|
+
name,
|
|
2749
|
+
path: dbPath,
|
|
2750
|
+
ok: false,
|
|
2751
|
+
unreadable: false,
|
|
2752
|
+
error: null,
|
|
2753
|
+
size: stat.size,
|
|
2754
|
+
mtime: stat.mtime.toISOString(),
|
|
2755
|
+
memoryCount: null
|
|
2756
|
+
};
|
|
2757
|
+
const client = createClient2({
|
|
2758
|
+
url: `file:${dbPath}`,
|
|
2759
|
+
encryptionKey: _encryptionKey
|
|
2760
|
+
});
|
|
2761
|
+
try {
|
|
2762
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2763
|
+
const hasMemories = await client.execute(
|
|
2764
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2765
|
+
);
|
|
2766
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2767
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2768
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2769
|
+
}
|
|
2770
|
+
item.ok = true;
|
|
2771
|
+
} catch (err) {
|
|
2772
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2773
|
+
item.error = message;
|
|
2774
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2775
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2776
|
+
client.close();
|
|
2777
|
+
_shards.delete(name);
|
|
2778
|
+
_shardLastAccess.delete(name);
|
|
2779
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2780
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2781
|
+
renameSync3(dbPath, archivedPath);
|
|
2782
|
+
item.archivedPath = archivedPath;
|
|
2783
|
+
}
|
|
2784
|
+
} finally {
|
|
2785
|
+
try {
|
|
2786
|
+
client.close();
|
|
2787
|
+
} catch {
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
shards.push(item);
|
|
2791
|
+
}
|
|
2792
|
+
return {
|
|
2793
|
+
total: shards.length,
|
|
2794
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2795
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2796
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2797
|
+
shards
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2735
2800
|
async function ensureShardSchema(client) {
|
|
2736
2801
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2737
2802
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -4171,6 +4236,15 @@ function readMachineId() {
|
|
|
4171
4236
|
return "";
|
|
4172
4237
|
}
|
|
4173
4238
|
}
|
|
4239
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
4240
|
+
const crypto3 = __require("crypto");
|
|
4241
|
+
const iv = crypto3.randomBytes(12);
|
|
4242
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
4243
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
4244
|
+
encrypted += cipher.final("base64");
|
|
4245
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
4246
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
4247
|
+
}
|
|
4174
4248
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
4175
4249
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
4176
4250
|
try {
|
|
@@ -4189,6 +4263,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
4189
4263
|
return null;
|
|
4190
4264
|
}
|
|
4191
4265
|
}
|
|
4266
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
4267
|
+
const dir = getKeyDir();
|
|
4268
|
+
await mkdir3(dir, { recursive: true });
|
|
4269
|
+
const keyPath = getKeyPath();
|
|
4270
|
+
const machineKey = deriveMachineKey();
|
|
4271
|
+
if (machineKey) {
|
|
4272
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
4273
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
4274
|
+
await chmod2(keyPath, 384);
|
|
4275
|
+
return "encrypted";
|
|
4276
|
+
}
|
|
4277
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
4278
|
+
await chmod2(keyPath, 384);
|
|
4279
|
+
return "plaintext";
|
|
4280
|
+
}
|
|
4192
4281
|
async function getMasterKey() {
|
|
4193
4282
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
4194
4283
|
if (nativeValue) {
|
|
@@ -4240,6 +4329,20 @@ async function getMasterKey() {
|
|
|
4240
4329
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
4241
4330
|
if (migrated) {
|
|
4242
4331
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
4332
|
+
try {
|
|
4333
|
+
await unlink(keyPath);
|
|
4334
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
4335
|
+
} catch {
|
|
4336
|
+
}
|
|
4337
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
4338
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
4339
|
+
if (fallback === "encrypted") {
|
|
4340
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
4341
|
+
} else {
|
|
4342
|
+
process.stderr.write(
|
|
4343
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4243
4346
|
}
|
|
4244
4347
|
return key;
|
|
4245
4348
|
} catch (err) {
|
|
@@ -2666,6 +2666,7 @@ var init_database = __esm({
|
|
|
2666
2666
|
// src/lib/shard-manager.ts
|
|
2667
2667
|
var shard_manager_exports = {};
|
|
2668
2668
|
__export(shard_manager_exports, {
|
|
2669
|
+
auditShardHealth: () => auditShardHealth,
|
|
2669
2670
|
disposeShards: () => disposeShards,
|
|
2670
2671
|
ensureShardSchema: () => ensureShardSchema,
|
|
2671
2672
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2732,6 +2733,70 @@ function listShards() {
|
|
|
2732
2733
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2733
2734
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2734
2735
|
}
|
|
2736
|
+
async function auditShardHealth(options = {}) {
|
|
2737
|
+
if (!_encryptionKey) {
|
|
2738
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2739
|
+
}
|
|
2740
|
+
const repair = options.repair === true;
|
|
2741
|
+
const dryRun = options.dryRun === true;
|
|
2742
|
+
const names = listShards();
|
|
2743
|
+
const shards = [];
|
|
2744
|
+
for (const name of names) {
|
|
2745
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2746
|
+
const stat = statSync2(dbPath);
|
|
2747
|
+
const item = {
|
|
2748
|
+
name,
|
|
2749
|
+
path: dbPath,
|
|
2750
|
+
ok: false,
|
|
2751
|
+
unreadable: false,
|
|
2752
|
+
error: null,
|
|
2753
|
+
size: stat.size,
|
|
2754
|
+
mtime: stat.mtime.toISOString(),
|
|
2755
|
+
memoryCount: null
|
|
2756
|
+
};
|
|
2757
|
+
const client = createClient2({
|
|
2758
|
+
url: `file:${dbPath}`,
|
|
2759
|
+
encryptionKey: _encryptionKey
|
|
2760
|
+
});
|
|
2761
|
+
try {
|
|
2762
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2763
|
+
const hasMemories = await client.execute(
|
|
2764
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2765
|
+
);
|
|
2766
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2767
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2768
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2769
|
+
}
|
|
2770
|
+
item.ok = true;
|
|
2771
|
+
} catch (err) {
|
|
2772
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2773
|
+
item.error = message;
|
|
2774
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2775
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2776
|
+
client.close();
|
|
2777
|
+
_shards.delete(name);
|
|
2778
|
+
_shardLastAccess.delete(name);
|
|
2779
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2780
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2781
|
+
renameSync3(dbPath, archivedPath);
|
|
2782
|
+
item.archivedPath = archivedPath;
|
|
2783
|
+
}
|
|
2784
|
+
} finally {
|
|
2785
|
+
try {
|
|
2786
|
+
client.close();
|
|
2787
|
+
} catch {
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
shards.push(item);
|
|
2791
|
+
}
|
|
2792
|
+
return {
|
|
2793
|
+
total: shards.length,
|
|
2794
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2795
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2796
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2797
|
+
shards
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2735
2800
|
async function ensureShardSchema(client) {
|
|
2736
2801
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2737
2802
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -4036,6 +4101,15 @@ function readMachineId() {
|
|
|
4036
4101
|
return "";
|
|
4037
4102
|
}
|
|
4038
4103
|
}
|
|
4104
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
4105
|
+
const crypto3 = __require("crypto");
|
|
4106
|
+
const iv = crypto3.randomBytes(12);
|
|
4107
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
4108
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
4109
|
+
encrypted += cipher.final("base64");
|
|
4110
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
4111
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
4112
|
+
}
|
|
4039
4113
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
4040
4114
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
4041
4115
|
try {
|
|
@@ -4054,6 +4128,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
4054
4128
|
return null;
|
|
4055
4129
|
}
|
|
4056
4130
|
}
|
|
4131
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
4132
|
+
const dir = getKeyDir();
|
|
4133
|
+
await mkdir3(dir, { recursive: true });
|
|
4134
|
+
const keyPath = getKeyPath();
|
|
4135
|
+
const machineKey = deriveMachineKey();
|
|
4136
|
+
if (machineKey) {
|
|
4137
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
4138
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
4139
|
+
await chmod2(keyPath, 384);
|
|
4140
|
+
return "encrypted";
|
|
4141
|
+
}
|
|
4142
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
4143
|
+
await chmod2(keyPath, 384);
|
|
4144
|
+
return "plaintext";
|
|
4145
|
+
}
|
|
4057
4146
|
async function getMasterKey() {
|
|
4058
4147
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
4059
4148
|
if (nativeValue) {
|
|
@@ -4105,6 +4194,20 @@ async function getMasterKey() {
|
|
|
4105
4194
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
4106
4195
|
if (migrated) {
|
|
4107
4196
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
4197
|
+
try {
|
|
4198
|
+
await unlink(keyPath);
|
|
4199
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
4200
|
+
} catch {
|
|
4201
|
+
}
|
|
4202
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
4203
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
4204
|
+
if (fallback === "encrypted") {
|
|
4205
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
4206
|
+
} else {
|
|
4207
|
+
process.stderr.write(
|
|
4208
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
4209
|
+
);
|
|
4210
|
+
}
|
|
4108
4211
|
}
|
|
4109
4212
|
return key;
|
|
4110
4213
|
} catch (err) {
|
package/dist/bin/exe-status.js
CHANGED
|
@@ -2919,6 +2919,15 @@ function readMachineId() {
|
|
|
2919
2919
|
return "";
|
|
2920
2920
|
}
|
|
2921
2921
|
}
|
|
2922
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2923
|
+
const crypto2 = __require("crypto");
|
|
2924
|
+
const iv = crypto2.randomBytes(12);
|
|
2925
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2926
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2927
|
+
encrypted += cipher.final("base64");
|
|
2928
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2929
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2930
|
+
}
|
|
2922
2931
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2923
2932
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2924
2933
|
try {
|
|
@@ -2937,6 +2946,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2937
2946
|
return null;
|
|
2938
2947
|
}
|
|
2939
2948
|
}
|
|
2949
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2950
|
+
const dir = getKeyDir();
|
|
2951
|
+
await mkdir3(dir, { recursive: true });
|
|
2952
|
+
const keyPath = getKeyPath();
|
|
2953
|
+
const machineKey = deriveMachineKey();
|
|
2954
|
+
if (machineKey) {
|
|
2955
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2956
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2957
|
+
await chmod2(keyPath, 384);
|
|
2958
|
+
return "encrypted";
|
|
2959
|
+
}
|
|
2960
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2961
|
+
await chmod2(keyPath, 384);
|
|
2962
|
+
return "plaintext";
|
|
2963
|
+
}
|
|
2940
2964
|
async function getMasterKey() {
|
|
2941
2965
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2942
2966
|
if (nativeValue) {
|
|
@@ -2988,6 +3012,20 @@ async function getMasterKey() {
|
|
|
2988
3012
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2989
3013
|
if (migrated) {
|
|
2990
3014
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3015
|
+
try {
|
|
3016
|
+
await unlink(keyPath);
|
|
3017
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3018
|
+
} catch {
|
|
3019
|
+
}
|
|
3020
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3021
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3022
|
+
if (fallback === "encrypted") {
|
|
3023
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3024
|
+
} else {
|
|
3025
|
+
process.stderr.write(
|
|
3026
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
2991
3029
|
}
|
|
2992
3030
|
return key;
|
|
2993
3031
|
} catch (err) {
|
|
@@ -3260,6 +3298,7 @@ var init_memory_write_governor = __esm({
|
|
|
3260
3298
|
// src/lib/shard-manager.ts
|
|
3261
3299
|
var shard_manager_exports = {};
|
|
3262
3300
|
__export(shard_manager_exports, {
|
|
3301
|
+
auditShardHealth: () => auditShardHealth,
|
|
3263
3302
|
disposeShards: () => disposeShards,
|
|
3264
3303
|
ensureShardSchema: () => ensureShardSchema,
|
|
3265
3304
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3326,6 +3365,70 @@ function listShards() {
|
|
|
3326
3365
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3327
3366
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3328
3367
|
}
|
|
3368
|
+
async function auditShardHealth(options = {}) {
|
|
3369
|
+
if (!_encryptionKey) {
|
|
3370
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3371
|
+
}
|
|
3372
|
+
const repair = options.repair === true;
|
|
3373
|
+
const dryRun = options.dryRun === true;
|
|
3374
|
+
const names = listShards();
|
|
3375
|
+
const shards = [];
|
|
3376
|
+
for (const name of names) {
|
|
3377
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3378
|
+
const stat = statSync2(dbPath);
|
|
3379
|
+
const item = {
|
|
3380
|
+
name,
|
|
3381
|
+
path: dbPath,
|
|
3382
|
+
ok: false,
|
|
3383
|
+
unreadable: false,
|
|
3384
|
+
error: null,
|
|
3385
|
+
size: stat.size,
|
|
3386
|
+
mtime: stat.mtime.toISOString(),
|
|
3387
|
+
memoryCount: null
|
|
3388
|
+
};
|
|
3389
|
+
const client = createClient2({
|
|
3390
|
+
url: `file:${dbPath}`,
|
|
3391
|
+
encryptionKey: _encryptionKey
|
|
3392
|
+
});
|
|
3393
|
+
try {
|
|
3394
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3395
|
+
const hasMemories = await client.execute(
|
|
3396
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3397
|
+
);
|
|
3398
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3399
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3400
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3401
|
+
}
|
|
3402
|
+
item.ok = true;
|
|
3403
|
+
} catch (err) {
|
|
3404
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3405
|
+
item.error = message;
|
|
3406
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3407
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3408
|
+
client.close();
|
|
3409
|
+
_shards.delete(name);
|
|
3410
|
+
_shardLastAccess.delete(name);
|
|
3411
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3412
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3413
|
+
renameSync3(dbPath, archivedPath);
|
|
3414
|
+
item.archivedPath = archivedPath;
|
|
3415
|
+
}
|
|
3416
|
+
} finally {
|
|
3417
|
+
try {
|
|
3418
|
+
client.close();
|
|
3419
|
+
} catch {
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
shards.push(item);
|
|
3423
|
+
}
|
|
3424
|
+
return {
|
|
3425
|
+
total: shards.length,
|
|
3426
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3427
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3428
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3429
|
+
shards
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3329
3432
|
async function ensureShardSchema(client) {
|
|
3330
3433
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3331
3434
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-team.js
CHANGED
|
@@ -2908,6 +2908,15 @@ function readMachineId() {
|
|
|
2908
2908
|
return "";
|
|
2909
2909
|
}
|
|
2910
2910
|
}
|
|
2911
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2912
|
+
const crypto2 = __require("crypto");
|
|
2913
|
+
const iv = crypto2.randomBytes(12);
|
|
2914
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2915
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2916
|
+
encrypted += cipher.final("base64");
|
|
2917
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2918
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2919
|
+
}
|
|
2911
2920
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2912
2921
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2913
2922
|
try {
|
|
@@ -2926,6 +2935,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2926
2935
|
return null;
|
|
2927
2936
|
}
|
|
2928
2937
|
}
|
|
2938
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2939
|
+
const dir = getKeyDir();
|
|
2940
|
+
await mkdir3(dir, { recursive: true });
|
|
2941
|
+
const keyPath = getKeyPath();
|
|
2942
|
+
const machineKey = deriveMachineKey();
|
|
2943
|
+
if (machineKey) {
|
|
2944
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2945
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2946
|
+
await chmod2(keyPath, 384);
|
|
2947
|
+
return "encrypted";
|
|
2948
|
+
}
|
|
2949
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2950
|
+
await chmod2(keyPath, 384);
|
|
2951
|
+
return "plaintext";
|
|
2952
|
+
}
|
|
2929
2953
|
async function getMasterKey() {
|
|
2930
2954
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2931
2955
|
if (nativeValue) {
|
|
@@ -2977,6 +3001,20 @@ async function getMasterKey() {
|
|
|
2977
3001
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2978
3002
|
if (migrated) {
|
|
2979
3003
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3004
|
+
try {
|
|
3005
|
+
await unlink(keyPath);
|
|
3006
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3007
|
+
} catch {
|
|
3008
|
+
}
|
|
3009
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3010
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3011
|
+
if (fallback === "encrypted") {
|
|
3012
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3013
|
+
} else {
|
|
3014
|
+
process.stderr.write(
|
|
3015
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3016
|
+
);
|
|
3017
|
+
}
|
|
2980
3018
|
}
|
|
2981
3019
|
return key;
|
|
2982
3020
|
} catch (err) {
|
|
@@ -3249,6 +3287,7 @@ var init_memory_write_governor = __esm({
|
|
|
3249
3287
|
// src/lib/shard-manager.ts
|
|
3250
3288
|
var shard_manager_exports = {};
|
|
3251
3289
|
__export(shard_manager_exports, {
|
|
3290
|
+
auditShardHealth: () => auditShardHealth,
|
|
3252
3291
|
disposeShards: () => disposeShards,
|
|
3253
3292
|
ensureShardSchema: () => ensureShardSchema,
|
|
3254
3293
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3315,6 +3354,70 @@ function listShards() {
|
|
|
3315
3354
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3316
3355
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3317
3356
|
}
|
|
3357
|
+
async function auditShardHealth(options = {}) {
|
|
3358
|
+
if (!_encryptionKey) {
|
|
3359
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3360
|
+
}
|
|
3361
|
+
const repair = options.repair === true;
|
|
3362
|
+
const dryRun = options.dryRun === true;
|
|
3363
|
+
const names = listShards();
|
|
3364
|
+
const shards = [];
|
|
3365
|
+
for (const name of names) {
|
|
3366
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3367
|
+
const stat = statSync2(dbPath);
|
|
3368
|
+
const item = {
|
|
3369
|
+
name,
|
|
3370
|
+
path: dbPath,
|
|
3371
|
+
ok: false,
|
|
3372
|
+
unreadable: false,
|
|
3373
|
+
error: null,
|
|
3374
|
+
size: stat.size,
|
|
3375
|
+
mtime: stat.mtime.toISOString(),
|
|
3376
|
+
memoryCount: null
|
|
3377
|
+
};
|
|
3378
|
+
const client = createClient2({
|
|
3379
|
+
url: `file:${dbPath}`,
|
|
3380
|
+
encryptionKey: _encryptionKey
|
|
3381
|
+
});
|
|
3382
|
+
try {
|
|
3383
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3384
|
+
const hasMemories = await client.execute(
|
|
3385
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3386
|
+
);
|
|
3387
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3388
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3389
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3390
|
+
}
|
|
3391
|
+
item.ok = true;
|
|
3392
|
+
} catch (err) {
|
|
3393
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3394
|
+
item.error = message;
|
|
3395
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3396
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3397
|
+
client.close();
|
|
3398
|
+
_shards.delete(name);
|
|
3399
|
+
_shardLastAccess.delete(name);
|
|
3400
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3401
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3402
|
+
renameSync3(dbPath, archivedPath);
|
|
3403
|
+
item.archivedPath = archivedPath;
|
|
3404
|
+
}
|
|
3405
|
+
} finally {
|
|
3406
|
+
try {
|
|
3407
|
+
client.close();
|
|
3408
|
+
} catch {
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
shards.push(item);
|
|
3412
|
+
}
|
|
3413
|
+
return {
|
|
3414
|
+
total: shards.length,
|
|
3415
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3416
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3417
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3418
|
+
shards
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3318
3421
|
async function ensureShardSchema(client) {
|
|
3319
3422
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3320
3423
|
await client.execute("PRAGMA busy_timeout = 30000");
|