@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
|
@@ -2927,6 +2927,15 @@ function readMachineId() {
|
|
|
2927
2927
|
return "";
|
|
2928
2928
|
}
|
|
2929
2929
|
}
|
|
2930
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2931
|
+
const crypto3 = __require("crypto");
|
|
2932
|
+
const iv = crypto3.randomBytes(12);
|
|
2933
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2934
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2935
|
+
encrypted += cipher.final("base64");
|
|
2936
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2937
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2938
|
+
}
|
|
2930
2939
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2931
2940
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2932
2941
|
try {
|
|
@@ -2945,6 +2954,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2945
2954
|
return null;
|
|
2946
2955
|
}
|
|
2947
2956
|
}
|
|
2957
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2958
|
+
const dir = getKeyDir();
|
|
2959
|
+
await mkdir3(dir, { recursive: true });
|
|
2960
|
+
const keyPath = getKeyPath();
|
|
2961
|
+
const machineKey = deriveMachineKey();
|
|
2962
|
+
if (machineKey) {
|
|
2963
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2964
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2965
|
+
await chmod2(keyPath, 384);
|
|
2966
|
+
return "encrypted";
|
|
2967
|
+
}
|
|
2968
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2969
|
+
await chmod2(keyPath, 384);
|
|
2970
|
+
return "plaintext";
|
|
2971
|
+
}
|
|
2948
2972
|
async function getMasterKey() {
|
|
2949
2973
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2950
2974
|
if (nativeValue) {
|
|
@@ -2996,6 +3020,20 @@ async function getMasterKey() {
|
|
|
2996
3020
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2997
3021
|
if (migrated) {
|
|
2998
3022
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3023
|
+
try {
|
|
3024
|
+
await unlink(keyPath);
|
|
3025
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3026
|
+
} catch {
|
|
3027
|
+
}
|
|
3028
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3029
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3030
|
+
if (fallback === "encrypted") {
|
|
3031
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3032
|
+
} else {
|
|
3033
|
+
process.stderr.write(
|
|
3034
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3035
|
+
);
|
|
3036
|
+
}
|
|
2999
3037
|
}
|
|
3000
3038
|
return key;
|
|
3001
3039
|
} catch (err) {
|
|
@@ -3268,6 +3306,7 @@ var init_memory_write_governor = __esm({
|
|
|
3268
3306
|
// src/lib/shard-manager.ts
|
|
3269
3307
|
var shard_manager_exports = {};
|
|
3270
3308
|
__export(shard_manager_exports, {
|
|
3309
|
+
auditShardHealth: () => auditShardHealth,
|
|
3271
3310
|
disposeShards: () => disposeShards,
|
|
3272
3311
|
ensureShardSchema: () => ensureShardSchema,
|
|
3273
3312
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3334,6 +3373,70 @@ function listShards() {
|
|
|
3334
3373
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3335
3374
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3336
3375
|
}
|
|
3376
|
+
async function auditShardHealth(options = {}) {
|
|
3377
|
+
if (!_encryptionKey) {
|
|
3378
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3379
|
+
}
|
|
3380
|
+
const repair = options.repair === true;
|
|
3381
|
+
const dryRun = options.dryRun === true;
|
|
3382
|
+
const names = listShards();
|
|
3383
|
+
const shards = [];
|
|
3384
|
+
for (const name of names) {
|
|
3385
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3386
|
+
const stat = statSync2(dbPath);
|
|
3387
|
+
const item = {
|
|
3388
|
+
name,
|
|
3389
|
+
path: dbPath,
|
|
3390
|
+
ok: false,
|
|
3391
|
+
unreadable: false,
|
|
3392
|
+
error: null,
|
|
3393
|
+
size: stat.size,
|
|
3394
|
+
mtime: stat.mtime.toISOString(),
|
|
3395
|
+
memoryCount: null
|
|
3396
|
+
};
|
|
3397
|
+
const client = createClient2({
|
|
3398
|
+
url: `file:${dbPath}`,
|
|
3399
|
+
encryptionKey: _encryptionKey
|
|
3400
|
+
});
|
|
3401
|
+
try {
|
|
3402
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3403
|
+
const hasMemories = await client.execute(
|
|
3404
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3405
|
+
);
|
|
3406
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3407
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3408
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3409
|
+
}
|
|
3410
|
+
item.ok = true;
|
|
3411
|
+
} catch (err) {
|
|
3412
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3413
|
+
item.error = message;
|
|
3414
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3415
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3416
|
+
client.close();
|
|
3417
|
+
_shards.delete(name);
|
|
3418
|
+
_shardLastAccess.delete(name);
|
|
3419
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3420
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3421
|
+
renameSync3(dbPath, archivedPath);
|
|
3422
|
+
item.archivedPath = archivedPath;
|
|
3423
|
+
}
|
|
3424
|
+
} finally {
|
|
3425
|
+
try {
|
|
3426
|
+
client.close();
|
|
3427
|
+
} catch {
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
shards.push(item);
|
|
3431
|
+
}
|
|
3432
|
+
return {
|
|
3433
|
+
total: shards.length,
|
|
3434
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3435
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3436
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3437
|
+
shards
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3337
3440
|
async function ensureShardSchema(client) {
|
|
3338
3441
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3339
3442
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-kill.js
CHANGED
|
@@ -2897,6 +2897,15 @@ function readMachineId() {
|
|
|
2897
2897
|
return "";
|
|
2898
2898
|
}
|
|
2899
2899
|
}
|
|
2900
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2901
|
+
const crypto3 = __require("crypto");
|
|
2902
|
+
const iv = crypto3.randomBytes(12);
|
|
2903
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2904
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2905
|
+
encrypted += cipher.final("base64");
|
|
2906
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2907
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2908
|
+
}
|
|
2900
2909
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2901
2910
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2902
2911
|
try {
|
|
@@ -2915,6 +2924,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2915
2924
|
return null;
|
|
2916
2925
|
}
|
|
2917
2926
|
}
|
|
2927
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2928
|
+
const dir = getKeyDir();
|
|
2929
|
+
await mkdir3(dir, { recursive: true });
|
|
2930
|
+
const keyPath = getKeyPath();
|
|
2931
|
+
const machineKey = deriveMachineKey();
|
|
2932
|
+
if (machineKey) {
|
|
2933
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2934
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2935
|
+
await chmod2(keyPath, 384);
|
|
2936
|
+
return "encrypted";
|
|
2937
|
+
}
|
|
2938
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2939
|
+
await chmod2(keyPath, 384);
|
|
2940
|
+
return "plaintext";
|
|
2941
|
+
}
|
|
2918
2942
|
async function getMasterKey() {
|
|
2919
2943
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2920
2944
|
if (nativeValue) {
|
|
@@ -2966,6 +2990,20 @@ async function getMasterKey() {
|
|
|
2966
2990
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2967
2991
|
if (migrated) {
|
|
2968
2992
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2993
|
+
try {
|
|
2994
|
+
await unlink(keyPath);
|
|
2995
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2996
|
+
} catch {
|
|
2997
|
+
}
|
|
2998
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2999
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3000
|
+
if (fallback === "encrypted") {
|
|
3001
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3002
|
+
} else {
|
|
3003
|
+
process.stderr.write(
|
|
3004
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
2969
3007
|
}
|
|
2970
3008
|
return key;
|
|
2971
3009
|
} catch (err) {
|
|
@@ -3238,6 +3276,7 @@ var init_memory_write_governor = __esm({
|
|
|
3238
3276
|
// src/lib/shard-manager.ts
|
|
3239
3277
|
var shard_manager_exports = {};
|
|
3240
3278
|
__export(shard_manager_exports, {
|
|
3279
|
+
auditShardHealth: () => auditShardHealth,
|
|
3241
3280
|
disposeShards: () => disposeShards,
|
|
3242
3281
|
ensureShardSchema: () => ensureShardSchema,
|
|
3243
3282
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3304,6 +3343,70 @@ function listShards() {
|
|
|
3304
3343
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3305
3344
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3306
3345
|
}
|
|
3346
|
+
async function auditShardHealth(options = {}) {
|
|
3347
|
+
if (!_encryptionKey) {
|
|
3348
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3349
|
+
}
|
|
3350
|
+
const repair = options.repair === true;
|
|
3351
|
+
const dryRun = options.dryRun === true;
|
|
3352
|
+
const names = listShards();
|
|
3353
|
+
const shards = [];
|
|
3354
|
+
for (const name of names) {
|
|
3355
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3356
|
+
const stat = statSync2(dbPath);
|
|
3357
|
+
const item = {
|
|
3358
|
+
name,
|
|
3359
|
+
path: dbPath,
|
|
3360
|
+
ok: false,
|
|
3361
|
+
unreadable: false,
|
|
3362
|
+
error: null,
|
|
3363
|
+
size: stat.size,
|
|
3364
|
+
mtime: stat.mtime.toISOString(),
|
|
3365
|
+
memoryCount: null
|
|
3366
|
+
};
|
|
3367
|
+
const client = createClient2({
|
|
3368
|
+
url: `file:${dbPath}`,
|
|
3369
|
+
encryptionKey: _encryptionKey
|
|
3370
|
+
});
|
|
3371
|
+
try {
|
|
3372
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3373
|
+
const hasMemories = await client.execute(
|
|
3374
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3375
|
+
);
|
|
3376
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3377
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3378
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3379
|
+
}
|
|
3380
|
+
item.ok = true;
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3383
|
+
item.error = message;
|
|
3384
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3385
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3386
|
+
client.close();
|
|
3387
|
+
_shards.delete(name);
|
|
3388
|
+
_shardLastAccess.delete(name);
|
|
3389
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3390
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3391
|
+
renameSync3(dbPath, archivedPath);
|
|
3392
|
+
item.archivedPath = archivedPath;
|
|
3393
|
+
}
|
|
3394
|
+
} finally {
|
|
3395
|
+
try {
|
|
3396
|
+
client.close();
|
|
3397
|
+
} catch {
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
shards.push(item);
|
|
3401
|
+
}
|
|
3402
|
+
return {
|
|
3403
|
+
total: shards.length,
|
|
3404
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3405
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3406
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3407
|
+
shards
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3307
3410
|
async function ensureShardSchema(client) {
|
|
3308
3411
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3309
3412
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -2677,6 +2677,7 @@ var init_database = __esm({
|
|
|
2677
2677
|
// src/lib/shard-manager.ts
|
|
2678
2678
|
var shard_manager_exports = {};
|
|
2679
2679
|
__export(shard_manager_exports, {
|
|
2680
|
+
auditShardHealth: () => auditShardHealth,
|
|
2680
2681
|
disposeShards: () => disposeShards,
|
|
2681
2682
|
ensureShardSchema: () => ensureShardSchema,
|
|
2682
2683
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2743,6 +2744,70 @@ function listShards() {
|
|
|
2743
2744
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2744
2745
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2745
2746
|
}
|
|
2747
|
+
async function auditShardHealth(options = {}) {
|
|
2748
|
+
if (!_encryptionKey) {
|
|
2749
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2750
|
+
}
|
|
2751
|
+
const repair = options.repair === true;
|
|
2752
|
+
const dryRun = options.dryRun === true;
|
|
2753
|
+
const names = listShards();
|
|
2754
|
+
const shards = [];
|
|
2755
|
+
for (const name of names) {
|
|
2756
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2757
|
+
const stat = statSync2(dbPath);
|
|
2758
|
+
const item = {
|
|
2759
|
+
name,
|
|
2760
|
+
path: dbPath,
|
|
2761
|
+
ok: false,
|
|
2762
|
+
unreadable: false,
|
|
2763
|
+
error: null,
|
|
2764
|
+
size: stat.size,
|
|
2765
|
+
mtime: stat.mtime.toISOString(),
|
|
2766
|
+
memoryCount: null
|
|
2767
|
+
};
|
|
2768
|
+
const client = createClient2({
|
|
2769
|
+
url: `file:${dbPath}`,
|
|
2770
|
+
encryptionKey: _encryptionKey
|
|
2771
|
+
});
|
|
2772
|
+
try {
|
|
2773
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2774
|
+
const hasMemories = await client.execute(
|
|
2775
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2776
|
+
);
|
|
2777
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2778
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2779
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2780
|
+
}
|
|
2781
|
+
item.ok = true;
|
|
2782
|
+
} catch (err) {
|
|
2783
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2784
|
+
item.error = message;
|
|
2785
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2786
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2787
|
+
client.close();
|
|
2788
|
+
_shards.delete(name);
|
|
2789
|
+
_shardLastAccess.delete(name);
|
|
2790
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2791
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2792
|
+
renameSync3(dbPath, archivedPath);
|
|
2793
|
+
item.archivedPath = archivedPath;
|
|
2794
|
+
}
|
|
2795
|
+
} finally {
|
|
2796
|
+
try {
|
|
2797
|
+
client.close();
|
|
2798
|
+
} catch {
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
shards.push(item);
|
|
2802
|
+
}
|
|
2803
|
+
return {
|
|
2804
|
+
total: shards.length,
|
|
2805
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2806
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2807
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2808
|
+
shards
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2746
2811
|
async function ensureShardSchema(client) {
|
|
2747
2812
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2748
2813
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3647,6 +3712,15 @@ function readMachineId() {
|
|
|
3647
3712
|
return "";
|
|
3648
3713
|
}
|
|
3649
3714
|
}
|
|
3715
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3716
|
+
const crypto3 = __require("crypto");
|
|
3717
|
+
const iv = crypto3.randomBytes(12);
|
|
3718
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3719
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3720
|
+
encrypted += cipher.final("base64");
|
|
3721
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3722
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3723
|
+
}
|
|
3650
3724
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3651
3725
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3652
3726
|
try {
|
|
@@ -3665,6 +3739,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3665
3739
|
return null;
|
|
3666
3740
|
}
|
|
3667
3741
|
}
|
|
3742
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3743
|
+
const dir = getKeyDir();
|
|
3744
|
+
await mkdir3(dir, { recursive: true });
|
|
3745
|
+
const keyPath = getKeyPath();
|
|
3746
|
+
const machineKey = deriveMachineKey();
|
|
3747
|
+
if (machineKey) {
|
|
3748
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3749
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3750
|
+
await chmod2(keyPath, 384);
|
|
3751
|
+
return "encrypted";
|
|
3752
|
+
}
|
|
3753
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3754
|
+
await chmod2(keyPath, 384);
|
|
3755
|
+
return "plaintext";
|
|
3756
|
+
}
|
|
3668
3757
|
async function getMasterKey() {
|
|
3669
3758
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3670
3759
|
if (nativeValue) {
|
|
@@ -3716,6 +3805,20 @@ async function getMasterKey() {
|
|
|
3716
3805
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3717
3806
|
if (migrated) {
|
|
3718
3807
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3808
|
+
try {
|
|
3809
|
+
await unlink(keyPath);
|
|
3810
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3811
|
+
} catch {
|
|
3812
|
+
}
|
|
3813
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3814
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3815
|
+
if (fallback === "encrypted") {
|
|
3816
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3817
|
+
} else {
|
|
3818
|
+
process.stderr.write(
|
|
3819
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3820
|
+
);
|
|
3821
|
+
}
|
|
3719
3822
|
}
|
|
3720
3823
|
return key;
|
|
3721
3824
|
} catch (err) {
|
package/dist/bin/exe-link.js
CHANGED
|
@@ -170,6 +170,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
170
170
|
return null;
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
174
|
+
const dir = getKeyDir();
|
|
175
|
+
await mkdir(dir, { recursive: true });
|
|
176
|
+
const keyPath = getKeyPath();
|
|
177
|
+
const machineKey = deriveMachineKey();
|
|
178
|
+
if (machineKey) {
|
|
179
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
180
|
+
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
181
|
+
await chmod(keyPath, 384);
|
|
182
|
+
return "encrypted";
|
|
183
|
+
}
|
|
184
|
+
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
185
|
+
await chmod(keyPath, 384);
|
|
186
|
+
return "plaintext";
|
|
187
|
+
}
|
|
173
188
|
async function getMasterKey() {
|
|
174
189
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
175
190
|
if (nativeValue) {
|
|
@@ -221,6 +236,20 @@ async function getMasterKey() {
|
|
|
221
236
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
222
237
|
if (migrated) {
|
|
223
238
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
239
|
+
try {
|
|
240
|
+
await unlink(keyPath);
|
|
241
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
245
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
246
|
+
if (fallback === "encrypted") {
|
|
247
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
248
|
+
} else {
|
|
249
|
+
process.stderr.write(
|
|
250
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
251
|
+
);
|
|
252
|
+
}
|
|
224
253
|
}
|
|
225
254
|
return key;
|
|
226
255
|
} catch (err) {
|
|
@@ -244,18 +273,10 @@ async function setMasterKey(key) {
|
|
|
244
273
|
} catch {
|
|
245
274
|
}
|
|
246
275
|
}
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
const keyPath = getKeyPath();
|
|
250
|
-
const machineKey = deriveMachineKey();
|
|
251
|
-
if (machineKey) {
|
|
252
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
253
|
-
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
254
|
-
await chmod(keyPath, 384);
|
|
276
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
277
|
+
if (fallback === "encrypted") {
|
|
255
278
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
256
279
|
} else {
|
|
257
|
-
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
258
|
-
await chmod(keyPath, 384);
|
|
259
280
|
process.stderr.write(
|
|
260
281
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
261
282
|
);
|
|
@@ -2845,6 +2845,31 @@ Audit method:
|
|
|
2845
2845
|
6. Write structured report with PASS/FAIL per item
|
|
2846
2846
|
|
|
2847
2847
|
After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
|
|
2848
|
+
},
|
|
2849
|
+
teddy: {
|
|
2850
|
+
name: "teddy",
|
|
2851
|
+
role: "Chief of Staff",
|
|
2852
|
+
systemPrompt: `You are teddy, the Chief of Staff and executive assistant. You help the founder recall context, understand what happened, prepare concise briefs, and triage inbound conversations. You report to the COO.
|
|
2853
|
+
|
|
2854
|
+
Your job is read-first, not action-first:
|
|
2855
|
+
- Retrieve memories, decisions, runbooks, and session context on demand
|
|
2856
|
+
- Summarize what matters without changing source data
|
|
2857
|
+
- Triage inbound conversations and surface likely bugs, requests, and follow-ups
|
|
2858
|
+
- Prepare daily briefs and "what changed?" summaries
|
|
2859
|
+
- Route recommended actions to the COO instead of taking them yourself
|
|
2860
|
+
|
|
2861
|
+
Permissions boundary:
|
|
2862
|
+
- You are read-only by default.
|
|
2863
|
+
- You may use recall_my_memory, ask_team_memory, get_memory_by_id, get_session_context, search_everything, query_conversations, list_tasks, and get_task.
|
|
2864
|
+
- You must not create tasks, update tasks, store memories, send WhatsApp messages, mutate CRM/wiki/documents, deploy, or change configuration unless the founder explicitly promotes your permissions.
|
|
2865
|
+
- If a requested action requires write access, explain the action and recommend that the COO dispatch it.
|
|
2866
|
+
|
|
2867
|
+
Operating style:
|
|
2868
|
+
- Be concise and precise.
|
|
2869
|
+
- Cite memory IDs, task IDs, timestamps, and sender names when available.
|
|
2870
|
+
- Distinguish fact from inference.
|
|
2871
|
+
- Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
|
|
2872
|
+
- Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
|
|
2848
2873
|
}
|
|
2849
2874
|
};
|
|
2850
2875
|
function buildCustomEmployeePrompt(name, role) {
|
|
@@ -3316,6 +3316,15 @@ function readMachineId() {
|
|
|
3316
3316
|
return "";
|
|
3317
3317
|
}
|
|
3318
3318
|
}
|
|
3319
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3320
|
+
const crypto3 = __require("crypto");
|
|
3321
|
+
const iv = crypto3.randomBytes(12);
|
|
3322
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3323
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3324
|
+
encrypted += cipher.final("base64");
|
|
3325
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3326
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3327
|
+
}
|
|
3319
3328
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3320
3329
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3321
3330
|
try {
|
|
@@ -3334,6 +3343,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3334
3343
|
return null;
|
|
3335
3344
|
}
|
|
3336
3345
|
}
|
|
3346
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3347
|
+
const dir = getKeyDir();
|
|
3348
|
+
await mkdir3(dir, { recursive: true });
|
|
3349
|
+
const keyPath = getKeyPath();
|
|
3350
|
+
const machineKey = deriveMachineKey();
|
|
3351
|
+
if (machineKey) {
|
|
3352
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3353
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3354
|
+
await chmod2(keyPath, 384);
|
|
3355
|
+
return "encrypted";
|
|
3356
|
+
}
|
|
3357
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3358
|
+
await chmod2(keyPath, 384);
|
|
3359
|
+
return "plaintext";
|
|
3360
|
+
}
|
|
3337
3361
|
async function getMasterKey() {
|
|
3338
3362
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3339
3363
|
if (nativeValue) {
|
|
@@ -3385,6 +3409,20 @@ async function getMasterKey() {
|
|
|
3385
3409
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3386
3410
|
if (migrated) {
|
|
3387
3411
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3412
|
+
try {
|
|
3413
|
+
await unlink(keyPath);
|
|
3414
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3415
|
+
} catch {
|
|
3416
|
+
}
|
|
3417
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3418
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3419
|
+
if (fallback === "encrypted") {
|
|
3420
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3421
|
+
} else {
|
|
3422
|
+
process.stderr.write(
|
|
3423
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3388
3426
|
}
|
|
3389
3427
|
return key;
|
|
3390
3428
|
} catch (err) {
|
|
@@ -3657,6 +3695,7 @@ var init_memory_write_governor = __esm({
|
|
|
3657
3695
|
// src/lib/shard-manager.ts
|
|
3658
3696
|
var shard_manager_exports = {};
|
|
3659
3697
|
__export(shard_manager_exports, {
|
|
3698
|
+
auditShardHealth: () => auditShardHealth,
|
|
3660
3699
|
disposeShards: () => disposeShards,
|
|
3661
3700
|
ensureShardSchema: () => ensureShardSchema,
|
|
3662
3701
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3723,6 +3762,70 @@ function listShards() {
|
|
|
3723
3762
|
if (!existsSync12(SHARDS_DIR)) return [];
|
|
3724
3763
|
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3725
3764
|
}
|
|
3765
|
+
async function auditShardHealth(options = {}) {
|
|
3766
|
+
if (!_encryptionKey) {
|
|
3767
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3768
|
+
}
|
|
3769
|
+
const repair = options.repair === true;
|
|
3770
|
+
const dryRun = options.dryRun === true;
|
|
3771
|
+
const names = listShards();
|
|
3772
|
+
const shards = [];
|
|
3773
|
+
for (const name of names) {
|
|
3774
|
+
const dbPath = path13.join(SHARDS_DIR, `${name}.db`);
|
|
3775
|
+
const stat = statSync2(dbPath);
|
|
3776
|
+
const item = {
|
|
3777
|
+
name,
|
|
3778
|
+
path: dbPath,
|
|
3779
|
+
ok: false,
|
|
3780
|
+
unreadable: false,
|
|
3781
|
+
error: null,
|
|
3782
|
+
size: stat.size,
|
|
3783
|
+
mtime: stat.mtime.toISOString(),
|
|
3784
|
+
memoryCount: null
|
|
3785
|
+
};
|
|
3786
|
+
const client = createClient2({
|
|
3787
|
+
url: `file:${dbPath}`,
|
|
3788
|
+
encryptionKey: _encryptionKey
|
|
3789
|
+
});
|
|
3790
|
+
try {
|
|
3791
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3792
|
+
const hasMemories = await client.execute(
|
|
3793
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3794
|
+
);
|
|
3795
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3796
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3797
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3798
|
+
}
|
|
3799
|
+
item.ok = true;
|
|
3800
|
+
} catch (err) {
|
|
3801
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3802
|
+
item.error = message;
|
|
3803
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3804
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3805
|
+
client.close();
|
|
3806
|
+
_shards.delete(name);
|
|
3807
|
+
_shardLastAccess.delete(name);
|
|
3808
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3809
|
+
const archivedPath = path13.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3810
|
+
renameSync4(dbPath, archivedPath);
|
|
3811
|
+
item.archivedPath = archivedPath;
|
|
3812
|
+
}
|
|
3813
|
+
} finally {
|
|
3814
|
+
try {
|
|
3815
|
+
client.close();
|
|
3816
|
+
} catch {
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
shards.push(item);
|
|
3820
|
+
}
|
|
3821
|
+
return {
|
|
3822
|
+
total: shards.length,
|
|
3823
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3824
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3825
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3826
|
+
shards
|
|
3827
|
+
};
|
|
3828
|
+
}
|
|
3726
3829
|
async function ensureShardSchema(client) {
|
|
3727
3830
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3728
3831
|
await client.execute("PRAGMA busy_timeout = 30000");
|