@askexenow/exe-os 0.9.53 → 0.9.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +103 -0
- package/dist/bin/backfill-responses.js +103 -0
- package/dist/bin/backfill-vectors.js +103 -0
- package/dist/bin/cleanup-stale-review-tasks.js +103 -0
- package/dist/bin/cli.js +121 -10
- package/dist/bin/exe-assign.js +103 -0
- package/dist/bin/exe-boot.js +96 -10
- package/dist/bin/exe-call.js +25 -0
- package/dist/bin/exe-cloud.js +31 -10
- package/dist/bin/exe-dispatch.js +103 -0
- package/dist/bin/exe-doctor.js +152 -3
- package/dist/bin/exe-export-behaviors.js +103 -0
- package/dist/bin/exe-forget.js +103 -0
- package/dist/bin/exe-gateway.js +103 -0
- package/dist/bin/exe-heartbeat.js +103 -0
- package/dist/bin/exe-kill.js +103 -0
- package/dist/bin/exe-launch-agent.js +103 -0
- package/dist/bin/exe-link.js +31 -10
- package/dist/bin/exe-new-employee.js +25 -0
- package/dist/bin/exe-pending-messages.js +103 -0
- package/dist/bin/exe-pending-notifications.js +103 -0
- package/dist/bin/exe-pending-reviews.js +103 -0
- package/dist/bin/exe-rename.js +103 -0
- package/dist/bin/exe-review.js +103 -0
- package/dist/bin/exe-search.js +103 -0
- package/dist/bin/exe-session-cleanup.js +103 -0
- package/dist/bin/exe-start-codex.js +103 -0
- package/dist/bin/exe-start-opencode.js +103 -0
- package/dist/bin/exe-status.js +103 -0
- package/dist/bin/exe-team.js +103 -0
- package/dist/bin/git-sweep.js +103 -0
- package/dist/bin/graph-backfill.js +103 -0
- package/dist/bin/graph-export.js +103 -0
- package/dist/bin/intercom-check.js +103 -0
- package/dist/bin/scan-tasks.js +103 -0
- package/dist/bin/setup.js +56 -10
- package/dist/bin/shard-migrate.js +103 -0
- package/dist/gateway/index.js +103 -0
- package/dist/hooks/bug-report-worker.js +103 -0
- package/dist/hooks/codex-stop-task-finalizer.js +103 -0
- package/dist/hooks/commit-complete.js +103 -0
- package/dist/hooks/error-recall.js +103 -0
- package/dist/hooks/ingest.js +103 -0
- package/dist/hooks/instructions-loaded.js +103 -0
- package/dist/hooks/notification.js +103 -0
- package/dist/hooks/post-compact.js +103 -0
- package/dist/hooks/post-tool-combined.js +103 -0
- package/dist/hooks/pre-compact.js +103 -0
- package/dist/hooks/pre-tool-use.js +103 -0
- package/dist/hooks/prompt-submit.js +103 -0
- package/dist/hooks/session-end.js +103 -0
- package/dist/hooks/session-start.js +103 -0
- package/dist/hooks/stop.js +103 -0
- package/dist/hooks/subagent-stop.js +103 -0
- package/dist/hooks/summary-worker.js +96 -10
- package/dist/index.js +103 -0
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/employee-templates.js +25 -0
- package/dist/lib/exe-daemon.js +165 -14
- package/dist/lib/hybrid-search.js +103 -0
- package/dist/lib/keychain.js +31 -10
- package/dist/lib/schedules.js +103 -0
- package/dist/lib/store.js +103 -0
- package/dist/mcp/server.js +164 -13
- package/dist/runtime/index.js +103 -0
- package/dist/tui/App.js +96 -10
- package/package.json +1 -1
package/dist/lib/keychain.js
CHANGED
|
@@ -155,6 +155,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
155
155
|
return null;
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
159
|
+
const dir = getKeyDir();
|
|
160
|
+
await mkdir(dir, { recursive: true });
|
|
161
|
+
const keyPath = getKeyPath();
|
|
162
|
+
const machineKey = deriveMachineKey();
|
|
163
|
+
if (machineKey) {
|
|
164
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
165
|
+
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
166
|
+
await chmod(keyPath, 384);
|
|
167
|
+
return "encrypted";
|
|
168
|
+
}
|
|
169
|
+
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
170
|
+
await chmod(keyPath, 384);
|
|
171
|
+
return "plaintext";
|
|
172
|
+
}
|
|
158
173
|
async function getMasterKey() {
|
|
159
174
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
160
175
|
if (nativeValue) {
|
|
@@ -206,6 +221,20 @@ async function getMasterKey() {
|
|
|
206
221
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
207
222
|
if (migrated) {
|
|
208
223
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
224
|
+
try {
|
|
225
|
+
await unlink(keyPath);
|
|
226
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
230
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
231
|
+
if (fallback === "encrypted") {
|
|
232
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
233
|
+
} else {
|
|
234
|
+
process.stderr.write(
|
|
235
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
236
|
+
);
|
|
237
|
+
}
|
|
209
238
|
}
|
|
210
239
|
return key;
|
|
211
240
|
} catch (err) {
|
|
@@ -229,18 +258,10 @@ async function setMasterKey(key) {
|
|
|
229
258
|
} catch {
|
|
230
259
|
}
|
|
231
260
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
const keyPath = getKeyPath();
|
|
235
|
-
const machineKey = deriveMachineKey();
|
|
236
|
-
if (machineKey) {
|
|
237
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
238
|
-
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
239
|
-
await chmod(keyPath, 384);
|
|
261
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
262
|
+
if (fallback === "encrypted") {
|
|
240
263
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
241
264
|
} else {
|
|
242
|
-
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
243
|
-
await chmod(keyPath, 384);
|
|
244
265
|
process.stderr.write(
|
|
245
266
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
246
267
|
);
|
package/dist/lib/schedules.js
CHANGED
|
@@ -2588,6 +2588,7 @@ var init_database = __esm({
|
|
|
2588
2588
|
// src/lib/shard-manager.ts
|
|
2589
2589
|
var shard_manager_exports = {};
|
|
2590
2590
|
__export(shard_manager_exports, {
|
|
2591
|
+
auditShardHealth: () => auditShardHealth,
|
|
2591
2592
|
disposeShards: () => disposeShards,
|
|
2592
2593
|
ensureShardSchema: () => ensureShardSchema,
|
|
2593
2594
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2654,6 +2655,70 @@ function listShards() {
|
|
|
2654
2655
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2655
2656
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2656
2657
|
}
|
|
2658
|
+
async function auditShardHealth(options = {}) {
|
|
2659
|
+
if (!_encryptionKey) {
|
|
2660
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2661
|
+
}
|
|
2662
|
+
const repair = options.repair === true;
|
|
2663
|
+
const dryRun = options.dryRun === true;
|
|
2664
|
+
const names = listShards();
|
|
2665
|
+
const shards = [];
|
|
2666
|
+
for (const name of names) {
|
|
2667
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2668
|
+
const stat = statSync2(dbPath);
|
|
2669
|
+
const item = {
|
|
2670
|
+
name,
|
|
2671
|
+
path: dbPath,
|
|
2672
|
+
ok: false,
|
|
2673
|
+
unreadable: false,
|
|
2674
|
+
error: null,
|
|
2675
|
+
size: stat.size,
|
|
2676
|
+
mtime: stat.mtime.toISOString(),
|
|
2677
|
+
memoryCount: null
|
|
2678
|
+
};
|
|
2679
|
+
const client = createClient2({
|
|
2680
|
+
url: `file:${dbPath}`,
|
|
2681
|
+
encryptionKey: _encryptionKey
|
|
2682
|
+
});
|
|
2683
|
+
try {
|
|
2684
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2685
|
+
const hasMemories = await client.execute(
|
|
2686
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2687
|
+
);
|
|
2688
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2689
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2690
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2691
|
+
}
|
|
2692
|
+
item.ok = true;
|
|
2693
|
+
} catch (err) {
|
|
2694
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2695
|
+
item.error = message;
|
|
2696
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2697
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2698
|
+
client.close();
|
|
2699
|
+
_shards.delete(name);
|
|
2700
|
+
_shardLastAccess.delete(name);
|
|
2701
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2702
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2703
|
+
renameSync3(dbPath, archivedPath);
|
|
2704
|
+
item.archivedPath = archivedPath;
|
|
2705
|
+
}
|
|
2706
|
+
} finally {
|
|
2707
|
+
try {
|
|
2708
|
+
client.close();
|
|
2709
|
+
} catch {
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
shards.push(item);
|
|
2713
|
+
}
|
|
2714
|
+
return {
|
|
2715
|
+
total: shards.length,
|
|
2716
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2717
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2718
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2719
|
+
shards
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2657
2722
|
async function ensureShardSchema(client) {
|
|
2658
2723
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2659
2724
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3298,6 +3363,15 @@ function readMachineId() {
|
|
|
3298
3363
|
return "";
|
|
3299
3364
|
}
|
|
3300
3365
|
}
|
|
3366
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3367
|
+
const crypto3 = __require("crypto");
|
|
3368
|
+
const iv = crypto3.randomBytes(12);
|
|
3369
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3370
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3371
|
+
encrypted += cipher.final("base64");
|
|
3372
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3373
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3374
|
+
}
|
|
3301
3375
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3302
3376
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3303
3377
|
try {
|
|
@@ -3316,6 +3390,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3316
3390
|
return null;
|
|
3317
3391
|
}
|
|
3318
3392
|
}
|
|
3393
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3394
|
+
const dir = getKeyDir();
|
|
3395
|
+
await mkdir3(dir, { recursive: true });
|
|
3396
|
+
const keyPath = getKeyPath();
|
|
3397
|
+
const machineKey = deriveMachineKey();
|
|
3398
|
+
if (machineKey) {
|
|
3399
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3400
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3401
|
+
await chmod2(keyPath, 384);
|
|
3402
|
+
return "encrypted";
|
|
3403
|
+
}
|
|
3404
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3405
|
+
await chmod2(keyPath, 384);
|
|
3406
|
+
return "plaintext";
|
|
3407
|
+
}
|
|
3319
3408
|
async function getMasterKey() {
|
|
3320
3409
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3321
3410
|
if (nativeValue) {
|
|
@@ -3367,6 +3456,20 @@ async function getMasterKey() {
|
|
|
3367
3456
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3368
3457
|
if (migrated) {
|
|
3369
3458
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3459
|
+
try {
|
|
3460
|
+
await unlink(keyPath);
|
|
3461
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3462
|
+
} catch {
|
|
3463
|
+
}
|
|
3464
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3465
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3466
|
+
if (fallback === "encrypted") {
|
|
3467
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3468
|
+
} else {
|
|
3469
|
+
process.stderr.write(
|
|
3470
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3471
|
+
);
|
|
3472
|
+
}
|
|
3370
3473
|
}
|
|
3371
3474
|
return key;
|
|
3372
3475
|
} catch (err) {
|
package/dist/lib/store.js
CHANGED
|
@@ -2588,6 +2588,7 @@ var init_database = __esm({
|
|
|
2588
2588
|
// src/lib/shard-manager.ts
|
|
2589
2589
|
var shard_manager_exports = {};
|
|
2590
2590
|
__export(shard_manager_exports, {
|
|
2591
|
+
auditShardHealth: () => auditShardHealth,
|
|
2591
2592
|
disposeShards: () => disposeShards,
|
|
2592
2593
|
ensureShardSchema: () => ensureShardSchema,
|
|
2593
2594
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2654,6 +2655,70 @@ function listShards() {
|
|
|
2654
2655
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2655
2656
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2656
2657
|
}
|
|
2658
|
+
async function auditShardHealth(options = {}) {
|
|
2659
|
+
if (!_encryptionKey) {
|
|
2660
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2661
|
+
}
|
|
2662
|
+
const repair = options.repair === true;
|
|
2663
|
+
const dryRun = options.dryRun === true;
|
|
2664
|
+
const names = listShards();
|
|
2665
|
+
const shards = [];
|
|
2666
|
+
for (const name of names) {
|
|
2667
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2668
|
+
const stat = statSync2(dbPath);
|
|
2669
|
+
const item = {
|
|
2670
|
+
name,
|
|
2671
|
+
path: dbPath,
|
|
2672
|
+
ok: false,
|
|
2673
|
+
unreadable: false,
|
|
2674
|
+
error: null,
|
|
2675
|
+
size: stat.size,
|
|
2676
|
+
mtime: stat.mtime.toISOString(),
|
|
2677
|
+
memoryCount: null
|
|
2678
|
+
};
|
|
2679
|
+
const client = createClient2({
|
|
2680
|
+
url: `file:${dbPath}`,
|
|
2681
|
+
encryptionKey: _encryptionKey
|
|
2682
|
+
});
|
|
2683
|
+
try {
|
|
2684
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2685
|
+
const hasMemories = await client.execute(
|
|
2686
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2687
|
+
);
|
|
2688
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2689
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2690
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2691
|
+
}
|
|
2692
|
+
item.ok = true;
|
|
2693
|
+
} catch (err) {
|
|
2694
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2695
|
+
item.error = message;
|
|
2696
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2697
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2698
|
+
client.close();
|
|
2699
|
+
_shards.delete(name);
|
|
2700
|
+
_shardLastAccess.delete(name);
|
|
2701
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2702
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2703
|
+
renameSync3(dbPath, archivedPath);
|
|
2704
|
+
item.archivedPath = archivedPath;
|
|
2705
|
+
}
|
|
2706
|
+
} finally {
|
|
2707
|
+
try {
|
|
2708
|
+
client.close();
|
|
2709
|
+
} catch {
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
shards.push(item);
|
|
2713
|
+
}
|
|
2714
|
+
return {
|
|
2715
|
+
total: shards.length,
|
|
2716
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2717
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2718
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2719
|
+
shards
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2657
2722
|
async function ensureShardSchema(client) {
|
|
2658
2723
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2659
2724
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3293,6 +3358,15 @@ function readMachineId() {
|
|
|
3293
3358
|
return "";
|
|
3294
3359
|
}
|
|
3295
3360
|
}
|
|
3361
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3362
|
+
const crypto2 = __require("crypto");
|
|
3363
|
+
const iv = crypto2.randomBytes(12);
|
|
3364
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3365
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3366
|
+
encrypted += cipher.final("base64");
|
|
3367
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3368
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3369
|
+
}
|
|
3296
3370
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3297
3371
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3298
3372
|
try {
|
|
@@ -3311,6 +3385,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3311
3385
|
return null;
|
|
3312
3386
|
}
|
|
3313
3387
|
}
|
|
3388
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3389
|
+
const dir = getKeyDir();
|
|
3390
|
+
await mkdir3(dir, { recursive: true });
|
|
3391
|
+
const keyPath = getKeyPath();
|
|
3392
|
+
const machineKey = deriveMachineKey();
|
|
3393
|
+
if (machineKey) {
|
|
3394
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3395
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3396
|
+
await chmod2(keyPath, 384);
|
|
3397
|
+
return "encrypted";
|
|
3398
|
+
}
|
|
3399
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3400
|
+
await chmod2(keyPath, 384);
|
|
3401
|
+
return "plaintext";
|
|
3402
|
+
}
|
|
3314
3403
|
async function getMasterKey() {
|
|
3315
3404
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3316
3405
|
if (nativeValue) {
|
|
@@ -3362,6 +3451,20 @@ async function getMasterKey() {
|
|
|
3362
3451
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3363
3452
|
if (migrated) {
|
|
3364
3453
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3454
|
+
try {
|
|
3455
|
+
await unlink(keyPath);
|
|
3456
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3457
|
+
} catch {
|
|
3458
|
+
}
|
|
3459
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3460
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3461
|
+
if (fallback === "encrypted") {
|
|
3462
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3463
|
+
} else {
|
|
3464
|
+
process.stderr.write(
|
|
3465
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3365
3468
|
}
|
|
3366
3469
|
return key;
|
|
3367
3470
|
} catch (err) {
|
package/dist/mcp/server.js
CHANGED
|
@@ -3389,6 +3389,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3389
3389
|
return null;
|
|
3390
3390
|
}
|
|
3391
3391
|
}
|
|
3392
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3393
|
+
const dir = getKeyDir();
|
|
3394
|
+
await mkdir3(dir, { recursive: true });
|
|
3395
|
+
const keyPath = getKeyPath();
|
|
3396
|
+
const machineKey = deriveMachineKey();
|
|
3397
|
+
if (machineKey) {
|
|
3398
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3399
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3400
|
+
await chmod2(keyPath, 384);
|
|
3401
|
+
return "encrypted";
|
|
3402
|
+
}
|
|
3403
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3404
|
+
await chmod2(keyPath, 384);
|
|
3405
|
+
return "plaintext";
|
|
3406
|
+
}
|
|
3392
3407
|
async function getMasterKey() {
|
|
3393
3408
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3394
3409
|
if (nativeValue) {
|
|
@@ -3440,6 +3455,20 @@ async function getMasterKey() {
|
|
|
3440
3455
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3441
3456
|
if (migrated) {
|
|
3442
3457
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3458
|
+
try {
|
|
3459
|
+
await unlink(keyPath);
|
|
3460
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3461
|
+
} catch {
|
|
3462
|
+
}
|
|
3463
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3464
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3465
|
+
if (fallback === "encrypted") {
|
|
3466
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3467
|
+
} else {
|
|
3468
|
+
process.stderr.write(
|
|
3469
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3443
3472
|
}
|
|
3444
3473
|
return key;
|
|
3445
3474
|
} catch (err) {
|
|
@@ -3463,18 +3492,10 @@ async function setMasterKey(key) {
|
|
|
3463
3492
|
} catch {
|
|
3464
3493
|
}
|
|
3465
3494
|
}
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
const keyPath = getKeyPath();
|
|
3469
|
-
const machineKey = deriveMachineKey();
|
|
3470
|
-
if (machineKey) {
|
|
3471
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3472
|
-
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3473
|
-
await chmod2(keyPath, 384);
|
|
3495
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
3496
|
+
if (fallback === "encrypted") {
|
|
3474
3497
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
3475
3498
|
} else {
|
|
3476
|
-
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3477
|
-
await chmod2(keyPath, 384);
|
|
3478
3499
|
process.stderr.write(
|
|
3479
3500
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
3480
3501
|
);
|
|
@@ -3786,6 +3807,7 @@ var init_memory_write_governor = __esm({
|
|
|
3786
3807
|
// src/lib/shard-manager.ts
|
|
3787
3808
|
var shard_manager_exports = {};
|
|
3788
3809
|
__export(shard_manager_exports, {
|
|
3810
|
+
auditShardHealth: () => auditShardHealth,
|
|
3789
3811
|
disposeShards: () => disposeShards,
|
|
3790
3812
|
ensureShardSchema: () => ensureShardSchema,
|
|
3791
3813
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3852,6 +3874,70 @@ function listShards() {
|
|
|
3852
3874
|
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3853
3875
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3854
3876
|
}
|
|
3877
|
+
async function auditShardHealth(options = {}) {
|
|
3878
|
+
if (!_encryptionKey) {
|
|
3879
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3880
|
+
}
|
|
3881
|
+
const repair = options.repair === true;
|
|
3882
|
+
const dryRun = options.dryRun === true;
|
|
3883
|
+
const names = listShards();
|
|
3884
|
+
const shards = [];
|
|
3885
|
+
for (const name of names) {
|
|
3886
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3887
|
+
const stat = statSync2(dbPath);
|
|
3888
|
+
const item = {
|
|
3889
|
+
name,
|
|
3890
|
+
path: dbPath,
|
|
3891
|
+
ok: false,
|
|
3892
|
+
unreadable: false,
|
|
3893
|
+
error: null,
|
|
3894
|
+
size: stat.size,
|
|
3895
|
+
mtime: stat.mtime.toISOString(),
|
|
3896
|
+
memoryCount: null
|
|
3897
|
+
};
|
|
3898
|
+
const client = createClient2({
|
|
3899
|
+
url: `file:${dbPath}`,
|
|
3900
|
+
encryptionKey: _encryptionKey
|
|
3901
|
+
});
|
|
3902
|
+
try {
|
|
3903
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3904
|
+
const hasMemories = await client.execute(
|
|
3905
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3906
|
+
);
|
|
3907
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3908
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3909
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3910
|
+
}
|
|
3911
|
+
item.ok = true;
|
|
3912
|
+
} catch (err) {
|
|
3913
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3914
|
+
item.error = message;
|
|
3915
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3916
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3917
|
+
client.close();
|
|
3918
|
+
_shards.delete(name);
|
|
3919
|
+
_shardLastAccess.delete(name);
|
|
3920
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3921
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3922
|
+
renameSync3(dbPath, archivedPath);
|
|
3923
|
+
item.archivedPath = archivedPath;
|
|
3924
|
+
}
|
|
3925
|
+
} finally {
|
|
3926
|
+
try {
|
|
3927
|
+
client.close();
|
|
3928
|
+
} catch {
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
shards.push(item);
|
|
3932
|
+
}
|
|
3933
|
+
return {
|
|
3934
|
+
total: shards.length,
|
|
3935
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3936
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3937
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3938
|
+
shards
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3855
3941
|
async function ensureShardSchema(client) {
|
|
3856
3942
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3857
3943
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -18712,15 +18798,31 @@ function auditHookHealth() {
|
|
|
18712
18798
|
const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
|
|
18713
18799
|
return { logExists: true, totalLines, errorsLastHour, topPatterns };
|
|
18714
18800
|
}
|
|
18801
|
+
async function auditShards() {
|
|
18802
|
+
try {
|
|
18803
|
+
const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
18804
|
+
const report = await auditShardHealth2();
|
|
18805
|
+
return {
|
|
18806
|
+
total: report.total,
|
|
18807
|
+
ok: report.ok,
|
|
18808
|
+
unreadable: report.unreadable,
|
|
18809
|
+
archived: report.archived,
|
|
18810
|
+
unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
|
|
18811
|
+
};
|
|
18812
|
+
} catch {
|
|
18813
|
+
return { total: 0, ok: 0, unreadable: 0, archived: 0, unreadableNames: [] };
|
|
18814
|
+
}
|
|
18815
|
+
}
|
|
18715
18816
|
async function runAudit(client, flags) {
|
|
18716
18817
|
const runConflicts = flags.conflicts || process.env.EXE_AUDIT_CONFLICTS === "1";
|
|
18717
|
-
const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects] = await Promise.all([
|
|
18818
|
+
const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, shards] = await Promise.all([
|
|
18718
18819
|
auditStats(client, flags),
|
|
18719
18820
|
auditNullVectors(client, flags),
|
|
18720
18821
|
auditDuplicates(client, flags),
|
|
18721
18822
|
auditBloated(client, flags),
|
|
18722
18823
|
auditFts(client),
|
|
18723
|
-
auditOrphanedProjects(client)
|
|
18824
|
+
auditOrphanedProjects(client),
|
|
18825
|
+
auditShards()
|
|
18724
18826
|
]);
|
|
18725
18827
|
let conflicts;
|
|
18726
18828
|
if (runConflicts) {
|
|
@@ -18740,7 +18842,7 @@ async function runAudit(client, flags) {
|
|
|
18740
18842
|
}
|
|
18741
18843
|
const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
|
|
18742
18844
|
const hookHealth = auditHookHealth();
|
|
18743
|
-
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth };
|
|
18845
|
+
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
|
|
18744
18846
|
}
|
|
18745
18847
|
function indicator(value, warn) {
|
|
18746
18848
|
if (value === 0) return "\u{1F7E2}";
|
|
@@ -18819,6 +18921,15 @@ function formatReport(report, flags) {
|
|
|
18819
18921
|
lines.push(` ${p.count}x: ${p.pattern}`);
|
|
18820
18922
|
}
|
|
18821
18923
|
}
|
|
18924
|
+
const sh = report.shards;
|
|
18925
|
+
if (sh.total > 0) {
|
|
18926
|
+
if (sh.unreadable === 0) {
|
|
18927
|
+
lines.push(`\u{1F7E2} Shards: ${fmtNum(sh.ok)} / ${fmtNum(sh.total)} readable`);
|
|
18928
|
+
} else {
|
|
18929
|
+
const names = sh.unreadableNames.slice(0, 5).join(", ");
|
|
18930
|
+
lines.push(`\u{1F534} Shards: ${fmtNum(sh.unreadable)} unreadable (${names}${sh.unreadableNames.length > 5 ? ", ..." : ""})`);
|
|
18931
|
+
}
|
|
18932
|
+
}
|
|
18822
18933
|
lines.push("");
|
|
18823
18934
|
if (flags.verbose) {
|
|
18824
18935
|
if (report.duplicates.length > 0) {
|
|
@@ -18866,6 +18977,9 @@ function formatReport(report, flags) {
|
|
|
18866
18977
|
if (!report.fts.inSync) {
|
|
18867
18978
|
recs.push("Run --fix to rebuild FTS index");
|
|
18868
18979
|
}
|
|
18980
|
+
if (report.shards.unreadable > 0) {
|
|
18981
|
+
recs.push(`Run --fix to archive ${fmtNum(report.shards.unreadable)} unreadable shard(s); global DB remains authoritative`);
|
|
18982
|
+
}
|
|
18869
18983
|
if (report.orphanedProjects.length > 0) {
|
|
18870
18984
|
const names = report.orphanedProjects.map((o) => `"${o.project_name}"`).join(", ");
|
|
18871
18985
|
recs.push(`Consider /exe-forget for orphaned project${report.orphanedProjects.length > 1 ? "s" : ""} ${names}`);
|
|
@@ -18971,6 +19085,17 @@ async function fixBloated(client, bloated, dryRun) {
|
|
|
18971
19085
|
}
|
|
18972
19086
|
return chunksCreated;
|
|
18973
19087
|
}
|
|
19088
|
+
async function fixShards(dryRun) {
|
|
19089
|
+
const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
19090
|
+
const report = await auditShardHealth2({ repair: true, dryRun });
|
|
19091
|
+
return {
|
|
19092
|
+
total: report.total,
|
|
19093
|
+
ok: report.ok,
|
|
19094
|
+
unreadable: report.unreadable,
|
|
19095
|
+
archived: report.archived,
|
|
19096
|
+
unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
|
|
19097
|
+
};
|
|
19098
|
+
}
|
|
18974
19099
|
function splitAtSentences(text3, maxChunkSize) {
|
|
18975
19100
|
if (text3.length <= maxChunkSize) return [text3];
|
|
18976
19101
|
const chunks = [];
|
|
@@ -19046,6 +19171,11 @@ ${mode} Applying repairs...
|
|
|
19046
19171
|
console.log(" Done.");
|
|
19047
19172
|
}
|
|
19048
19173
|
}
|
|
19174
|
+
if (report.shards.unreadable > 0) {
|
|
19175
|
+
console.log(`${mode} Archiving ${fmtNum(report.shards.unreadable)} unreadable shard(s)...`);
|
|
19176
|
+
const fixed = await fixShards(flags.dryRun);
|
|
19177
|
+
console.log(` ${flags.dryRun ? "Would archive" : "Archived"} ${fmtNum(flags.dryRun ? fixed.unreadable : fixed.archived)} shard(s).`);
|
|
19178
|
+
}
|
|
19049
19179
|
console.log(`
|
|
19050
19180
|
${mode} Complete.`);
|
|
19051
19181
|
}
|
|
@@ -24963,9 +25093,30 @@ var TOOL_CATEGORIES = {
|
|
|
24963
25093
|
registerListGlobalProcedures: "orchestration",
|
|
24964
25094
|
registerDeactivateGlobalProcedure: "orchestration"
|
|
24965
25095
|
};
|
|
25096
|
+
var CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
25097
|
+
"registerRecallMyMemory",
|
|
25098
|
+
"registerAskTeamMemory",
|
|
25099
|
+
"registerGetMemoryById",
|
|
25100
|
+
"registerGetSessionContext",
|
|
25101
|
+
"registerSearchEverything",
|
|
25102
|
+
"registerGetDecision",
|
|
25103
|
+
"registerGetIdentity",
|
|
25104
|
+
"registerListTasks",
|
|
25105
|
+
"registerGetTask",
|
|
25106
|
+
"registerListReminders",
|
|
25107
|
+
"registerQueryConversations",
|
|
25108
|
+
"registerQueryCompanyBrain"
|
|
25109
|
+
]);
|
|
25110
|
+
function isChiefOfStaffRole(role) {
|
|
25111
|
+
const normalized = (role ?? "").trim().toLowerCase();
|
|
25112
|
+
return normalized === "chief of staff" || normalized === "executive assistant";
|
|
25113
|
+
}
|
|
24966
25114
|
function isToolAllowed(registerFnName) {
|
|
24967
25115
|
const role = process.env.AGENT_ROLE;
|
|
24968
25116
|
if (!role) return true;
|
|
25117
|
+
if (isChiefOfStaffRole(role)) {
|
|
25118
|
+
return CHIEF_OF_STAFF_READ_TOOLS.has(registerFnName);
|
|
25119
|
+
}
|
|
24969
25120
|
const category = TOOL_CATEGORIES[registerFnName];
|
|
24970
25121
|
if (!category) return true;
|
|
24971
25122
|
const allowedRoles = TOOL_GATES[category];
|