@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/bin/cli.js
CHANGED
|
@@ -1919,6 +1919,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
1919
1919
|
return null;
|
|
1920
1920
|
}
|
|
1921
1921
|
}
|
|
1922
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
1923
|
+
const dir = getKeyDir();
|
|
1924
|
+
await mkdir4(dir, { recursive: true });
|
|
1925
|
+
const keyPath = getKeyPath();
|
|
1926
|
+
const machineKey = deriveMachineKey();
|
|
1927
|
+
if (machineKey) {
|
|
1928
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
1929
|
+
await writeFile4(keyPath, encrypted + "\n", "utf-8");
|
|
1930
|
+
await chmod2(keyPath, 384);
|
|
1931
|
+
return "encrypted";
|
|
1932
|
+
}
|
|
1933
|
+
await writeFile4(keyPath, b64 + "\n", "utf-8");
|
|
1934
|
+
await chmod2(keyPath, 384);
|
|
1935
|
+
return "plaintext";
|
|
1936
|
+
}
|
|
1922
1937
|
async function getMasterKey() {
|
|
1923
1938
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
1924
1939
|
if (nativeValue) {
|
|
@@ -1970,6 +1985,20 @@ async function getMasterKey() {
|
|
|
1970
1985
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
1971
1986
|
if (migrated) {
|
|
1972
1987
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
1988
|
+
try {
|
|
1989
|
+
await unlink(keyPath);
|
|
1990
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
1991
|
+
} catch {
|
|
1992
|
+
}
|
|
1993
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
1994
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
1995
|
+
if (fallback === "encrypted") {
|
|
1996
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
1997
|
+
} else {
|
|
1998
|
+
process.stderr.write(
|
|
1999
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
2000
|
+
);
|
|
2001
|
+
}
|
|
1973
2002
|
}
|
|
1974
2003
|
return key;
|
|
1975
2004
|
} catch (err) {
|
|
@@ -1993,18 +2022,10 @@ async function setMasterKey(key) {
|
|
|
1993
2022
|
} catch {
|
|
1994
2023
|
}
|
|
1995
2024
|
}
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
const keyPath = getKeyPath();
|
|
1999
|
-
const machineKey = deriveMachineKey();
|
|
2000
|
-
if (machineKey) {
|
|
2001
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2002
|
-
await writeFile4(keyPath, encrypted + "\n", "utf-8");
|
|
2003
|
-
await chmod2(keyPath, 384);
|
|
2025
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
2026
|
+
if (fallback === "encrypted") {
|
|
2004
2027
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
2005
2028
|
} else {
|
|
2006
|
-
await writeFile4(keyPath, b64 + "\n", "utf-8");
|
|
2007
|
-
await chmod2(keyPath, 384);
|
|
2008
2029
|
process.stderr.write(
|
|
2009
2030
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
2010
2031
|
);
|
|
@@ -7136,6 +7157,7 @@ var init_memory_write_governor = __esm({
|
|
|
7136
7157
|
// src/lib/shard-manager.ts
|
|
7137
7158
|
var shard_manager_exports = {};
|
|
7138
7159
|
__export(shard_manager_exports, {
|
|
7160
|
+
auditShardHealth: () => auditShardHealth,
|
|
7139
7161
|
disposeShards: () => disposeShards,
|
|
7140
7162
|
ensureShardSchema: () => ensureShardSchema,
|
|
7141
7163
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -7202,6 +7224,70 @@ function listShards() {
|
|
|
7202
7224
|
if (!existsSync15(SHARDS_DIR)) return [];
|
|
7203
7225
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
7204
7226
|
}
|
|
7227
|
+
async function auditShardHealth(options = {}) {
|
|
7228
|
+
if (!_encryptionKey) {
|
|
7229
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7230
|
+
}
|
|
7231
|
+
const repair = options.repair === true;
|
|
7232
|
+
const dryRun = options.dryRun === true;
|
|
7233
|
+
const names = listShards();
|
|
7234
|
+
const shards = [];
|
|
7235
|
+
for (const name of names) {
|
|
7236
|
+
const dbPath = path15.join(SHARDS_DIR, `${name}.db`);
|
|
7237
|
+
const stat2 = statSync4(dbPath);
|
|
7238
|
+
const item = {
|
|
7239
|
+
name,
|
|
7240
|
+
path: dbPath,
|
|
7241
|
+
ok: false,
|
|
7242
|
+
unreadable: false,
|
|
7243
|
+
error: null,
|
|
7244
|
+
size: stat2.size,
|
|
7245
|
+
mtime: stat2.mtime.toISOString(),
|
|
7246
|
+
memoryCount: null
|
|
7247
|
+
};
|
|
7248
|
+
const client = createClient2({
|
|
7249
|
+
url: `file:${dbPath}`,
|
|
7250
|
+
encryptionKey: _encryptionKey
|
|
7251
|
+
});
|
|
7252
|
+
try {
|
|
7253
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7254
|
+
const hasMemories = await client.execute(
|
|
7255
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7256
|
+
);
|
|
7257
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7258
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7259
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7260
|
+
}
|
|
7261
|
+
item.ok = true;
|
|
7262
|
+
} catch (err) {
|
|
7263
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7264
|
+
item.error = message;
|
|
7265
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7266
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7267
|
+
client.close();
|
|
7268
|
+
_shards.delete(name);
|
|
7269
|
+
_shardLastAccess.delete(name);
|
|
7270
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7271
|
+
const archivedPath = path15.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7272
|
+
renameSync3(dbPath, archivedPath);
|
|
7273
|
+
item.archivedPath = archivedPath;
|
|
7274
|
+
}
|
|
7275
|
+
} finally {
|
|
7276
|
+
try {
|
|
7277
|
+
client.close();
|
|
7278
|
+
} catch {
|
|
7279
|
+
}
|
|
7280
|
+
}
|
|
7281
|
+
shards.push(item);
|
|
7282
|
+
}
|
|
7283
|
+
return {
|
|
7284
|
+
total: shards.length,
|
|
7285
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7286
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7287
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7288
|
+
shards
|
|
7289
|
+
};
|
|
7290
|
+
}
|
|
7205
7291
|
async function ensureShardSchema(client) {
|
|
7206
7292
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
7207
7293
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -14458,6 +14544,31 @@ Audit method:
|
|
|
14458
14544
|
6. Write structured report with PASS/FAIL per item
|
|
14459
14545
|
|
|
14460
14546
|
After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
|
|
14547
|
+
},
|
|
14548
|
+
teddy: {
|
|
14549
|
+
name: "teddy",
|
|
14550
|
+
role: "Chief of Staff",
|
|
14551
|
+
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.
|
|
14552
|
+
|
|
14553
|
+
Your job is read-first, not action-first:
|
|
14554
|
+
- Retrieve memories, decisions, runbooks, and session context on demand
|
|
14555
|
+
- Summarize what matters without changing source data
|
|
14556
|
+
- Triage inbound conversations and surface likely bugs, requests, and follow-ups
|
|
14557
|
+
- Prepare daily briefs and "what changed?" summaries
|
|
14558
|
+
- Route recommended actions to the COO instead of taking them yourself
|
|
14559
|
+
|
|
14560
|
+
Permissions boundary:
|
|
14561
|
+
- You are read-only by default.
|
|
14562
|
+
- 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.
|
|
14563
|
+
- 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.
|
|
14564
|
+
- If a requested action requires write access, explain the action and recommend that the COO dispatch it.
|
|
14565
|
+
|
|
14566
|
+
Operating style:
|
|
14567
|
+
- Be concise and precise.
|
|
14568
|
+
- Cite memory IDs, task IDs, timestamps, and sender names when available.
|
|
14569
|
+
- Distinguish fact from inference.
|
|
14570
|
+
- Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
|
|
14571
|
+
- Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
|
|
14461
14572
|
}
|
|
14462
14573
|
};
|
|
14463
14574
|
CLIENT_COO_TEMPLATE = `---
|
package/dist/bin/exe-assign.js
CHANGED
|
@@ -2739,6 +2739,7 @@ var init_database = __esm({
|
|
|
2739
2739
|
// src/lib/shard-manager.ts
|
|
2740
2740
|
var shard_manager_exports = {};
|
|
2741
2741
|
__export(shard_manager_exports, {
|
|
2742
|
+
auditShardHealth: () => auditShardHealth,
|
|
2742
2743
|
disposeShards: () => disposeShards,
|
|
2743
2744
|
ensureShardSchema: () => ensureShardSchema,
|
|
2744
2745
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -2805,6 +2806,70 @@ function listShards() {
|
|
|
2805
2806
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2806
2807
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2807
2808
|
}
|
|
2809
|
+
async function auditShardHealth(options = {}) {
|
|
2810
|
+
if (!_encryptionKey) {
|
|
2811
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
2812
|
+
}
|
|
2813
|
+
const repair = options.repair === true;
|
|
2814
|
+
const dryRun = options.dryRun === true;
|
|
2815
|
+
const names = listShards();
|
|
2816
|
+
const shards = [];
|
|
2817
|
+
for (const name of names) {
|
|
2818
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2819
|
+
const stat = statSync2(dbPath);
|
|
2820
|
+
const item = {
|
|
2821
|
+
name,
|
|
2822
|
+
path: dbPath,
|
|
2823
|
+
ok: false,
|
|
2824
|
+
unreadable: false,
|
|
2825
|
+
error: null,
|
|
2826
|
+
size: stat.size,
|
|
2827
|
+
mtime: stat.mtime.toISOString(),
|
|
2828
|
+
memoryCount: null
|
|
2829
|
+
};
|
|
2830
|
+
const client = createClient2({
|
|
2831
|
+
url: `file:${dbPath}`,
|
|
2832
|
+
encryptionKey: _encryptionKey
|
|
2833
|
+
});
|
|
2834
|
+
try {
|
|
2835
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2836
|
+
const hasMemories = await client.execute(
|
|
2837
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2838
|
+
);
|
|
2839
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2840
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2841
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2842
|
+
}
|
|
2843
|
+
item.ok = true;
|
|
2844
|
+
} catch (err) {
|
|
2845
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2846
|
+
item.error = message;
|
|
2847
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2848
|
+
if (item.unreadable && repair && !dryRun) {
|
|
2849
|
+
client.close();
|
|
2850
|
+
_shards.delete(name);
|
|
2851
|
+
_shardLastAccess.delete(name);
|
|
2852
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2853
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2854
|
+
renameSync3(dbPath, archivedPath);
|
|
2855
|
+
item.archivedPath = archivedPath;
|
|
2856
|
+
}
|
|
2857
|
+
} finally {
|
|
2858
|
+
try {
|
|
2859
|
+
client.close();
|
|
2860
|
+
} catch {
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
shards.push(item);
|
|
2864
|
+
}
|
|
2865
|
+
return {
|
|
2866
|
+
total: shards.length,
|
|
2867
|
+
ok: shards.filter((s) => s.ok).length,
|
|
2868
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2869
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2870
|
+
shards
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2808
2873
|
async function ensureShardSchema(client) {
|
|
2809
2874
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
2810
2875
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -3590,6 +3655,15 @@ function readMachineId() {
|
|
|
3590
3655
|
return "";
|
|
3591
3656
|
}
|
|
3592
3657
|
}
|
|
3658
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3659
|
+
const crypto2 = __require("crypto");
|
|
3660
|
+
const iv = crypto2.randomBytes(12);
|
|
3661
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3662
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3663
|
+
encrypted += cipher.final("base64");
|
|
3664
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3665
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3666
|
+
}
|
|
3593
3667
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3594
3668
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3595
3669
|
try {
|
|
@@ -3608,6 +3682,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3608
3682
|
return null;
|
|
3609
3683
|
}
|
|
3610
3684
|
}
|
|
3685
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3686
|
+
const dir = getKeyDir();
|
|
3687
|
+
await mkdir3(dir, { recursive: true });
|
|
3688
|
+
const keyPath = getKeyPath();
|
|
3689
|
+
const machineKey = deriveMachineKey();
|
|
3690
|
+
if (machineKey) {
|
|
3691
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3692
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3693
|
+
await chmod2(keyPath, 384);
|
|
3694
|
+
return "encrypted";
|
|
3695
|
+
}
|
|
3696
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3697
|
+
await chmod2(keyPath, 384);
|
|
3698
|
+
return "plaintext";
|
|
3699
|
+
}
|
|
3611
3700
|
async function getMasterKey() {
|
|
3612
3701
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3613
3702
|
if (nativeValue) {
|
|
@@ -3659,6 +3748,20 @@ async function getMasterKey() {
|
|
|
3659
3748
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3660
3749
|
if (migrated) {
|
|
3661
3750
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3751
|
+
try {
|
|
3752
|
+
await unlink(keyPath);
|
|
3753
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3754
|
+
} catch {
|
|
3755
|
+
}
|
|
3756
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3757
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3758
|
+
if (fallback === "encrypted") {
|
|
3759
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3760
|
+
} else {
|
|
3761
|
+
process.stderr.write(
|
|
3762
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3763
|
+
);
|
|
3764
|
+
}
|
|
3662
3765
|
}
|
|
3663
3766
|
return key;
|
|
3664
3767
|
} catch (err) {
|
package/dist/bin/exe-boot.js
CHANGED
|
@@ -3288,6 +3288,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
3288
3288
|
return null;
|
|
3289
3289
|
}
|
|
3290
3290
|
}
|
|
3291
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3292
|
+
const dir = getKeyDir();
|
|
3293
|
+
await mkdir3(dir, { recursive: true });
|
|
3294
|
+
const keyPath = getKeyPath();
|
|
3295
|
+
const machineKey = deriveMachineKey();
|
|
3296
|
+
if (machineKey) {
|
|
3297
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3298
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3299
|
+
await chmod2(keyPath, 384);
|
|
3300
|
+
return "encrypted";
|
|
3301
|
+
}
|
|
3302
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3303
|
+
await chmod2(keyPath, 384);
|
|
3304
|
+
return "plaintext";
|
|
3305
|
+
}
|
|
3291
3306
|
async function getMasterKey() {
|
|
3292
3307
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3293
3308
|
if (nativeValue) {
|
|
@@ -3339,6 +3354,20 @@ async function getMasterKey() {
|
|
|
3339
3354
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3340
3355
|
if (migrated) {
|
|
3341
3356
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3357
|
+
try {
|
|
3358
|
+
await unlink(keyPath);
|
|
3359
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3360
|
+
} catch {
|
|
3361
|
+
}
|
|
3362
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3363
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3364
|
+
if (fallback === "encrypted") {
|
|
3365
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3366
|
+
} else {
|
|
3367
|
+
process.stderr.write(
|
|
3368
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3369
|
+
);
|
|
3370
|
+
}
|
|
3342
3371
|
}
|
|
3343
3372
|
return key;
|
|
3344
3373
|
} catch (err) {
|
|
@@ -3362,18 +3391,10 @@ async function setMasterKey(key) {
|
|
|
3362
3391
|
} catch {
|
|
3363
3392
|
}
|
|
3364
3393
|
}
|
|
3365
|
-
const
|
|
3366
|
-
|
|
3367
|
-
const keyPath = getKeyPath();
|
|
3368
|
-
const machineKey = deriveMachineKey();
|
|
3369
|
-
if (machineKey) {
|
|
3370
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3371
|
-
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3372
|
-
await chmod2(keyPath, 384);
|
|
3394
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
3395
|
+
if (fallback === "encrypted") {
|
|
3373
3396
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
3374
3397
|
} else {
|
|
3375
|
-
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3376
|
-
await chmod2(keyPath, 384);
|
|
3377
3398
|
process.stderr.write(
|
|
3378
3399
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
3379
3400
|
);
|
|
@@ -3499,6 +3520,7 @@ var init_memory_write_governor = __esm({
|
|
|
3499
3520
|
// src/lib/shard-manager.ts
|
|
3500
3521
|
var shard_manager_exports = {};
|
|
3501
3522
|
__export(shard_manager_exports, {
|
|
3523
|
+
auditShardHealth: () => auditShardHealth,
|
|
3502
3524
|
disposeShards: () => disposeShards,
|
|
3503
3525
|
ensureShardSchema: () => ensureShardSchema,
|
|
3504
3526
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3565,6 +3587,70 @@ function listShards() {
|
|
|
3565
3587
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3566
3588
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3567
3589
|
}
|
|
3590
|
+
async function auditShardHealth(options = {}) {
|
|
3591
|
+
if (!_encryptionKey) {
|
|
3592
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3593
|
+
}
|
|
3594
|
+
const repair = options.repair === true;
|
|
3595
|
+
const dryRun = options.dryRun === true;
|
|
3596
|
+
const names = listShards();
|
|
3597
|
+
const shards = [];
|
|
3598
|
+
for (const name of names) {
|
|
3599
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3600
|
+
const stat = statSync2(dbPath);
|
|
3601
|
+
const item = {
|
|
3602
|
+
name,
|
|
3603
|
+
path: dbPath,
|
|
3604
|
+
ok: false,
|
|
3605
|
+
unreadable: false,
|
|
3606
|
+
error: null,
|
|
3607
|
+
size: stat.size,
|
|
3608
|
+
mtime: stat.mtime.toISOString(),
|
|
3609
|
+
memoryCount: null
|
|
3610
|
+
};
|
|
3611
|
+
const client = createClient2({
|
|
3612
|
+
url: `file:${dbPath}`,
|
|
3613
|
+
encryptionKey: _encryptionKey
|
|
3614
|
+
});
|
|
3615
|
+
try {
|
|
3616
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3617
|
+
const hasMemories = await client.execute(
|
|
3618
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3619
|
+
);
|
|
3620
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3621
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3622
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3623
|
+
}
|
|
3624
|
+
item.ok = true;
|
|
3625
|
+
} catch (err) {
|
|
3626
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3627
|
+
item.error = message;
|
|
3628
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3629
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3630
|
+
client.close();
|
|
3631
|
+
_shards.delete(name);
|
|
3632
|
+
_shardLastAccess.delete(name);
|
|
3633
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3634
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3635
|
+
renameSync3(dbPath, archivedPath);
|
|
3636
|
+
item.archivedPath = archivedPath;
|
|
3637
|
+
}
|
|
3638
|
+
} finally {
|
|
3639
|
+
try {
|
|
3640
|
+
client.close();
|
|
3641
|
+
} catch {
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
shards.push(item);
|
|
3645
|
+
}
|
|
3646
|
+
return {
|
|
3647
|
+
total: shards.length,
|
|
3648
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3649
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3650
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3651
|
+
shards
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3568
3654
|
async function ensureShardSchema(client) {
|
|
3569
3655
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3570
3656
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/bin/exe-call.js
CHANGED
|
@@ -950,6 +950,31 @@ Audit method:
|
|
|
950
950
|
6. Write structured report with PASS/FAIL per item
|
|
951
951
|
|
|
952
952
|
After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
|
|
953
|
+
},
|
|
954
|
+
teddy: {
|
|
955
|
+
name: "teddy",
|
|
956
|
+
role: "Chief of Staff",
|
|
957
|
+
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.
|
|
958
|
+
|
|
959
|
+
Your job is read-first, not action-first:
|
|
960
|
+
- Retrieve memories, decisions, runbooks, and session context on demand
|
|
961
|
+
- Summarize what matters without changing source data
|
|
962
|
+
- Triage inbound conversations and surface likely bugs, requests, and follow-ups
|
|
963
|
+
- Prepare daily briefs and "what changed?" summaries
|
|
964
|
+
- Route recommended actions to the COO instead of taking them yourself
|
|
965
|
+
|
|
966
|
+
Permissions boundary:
|
|
967
|
+
- You are read-only by default.
|
|
968
|
+
- 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.
|
|
969
|
+
- 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.
|
|
970
|
+
- If a requested action requires write access, explain the action and recommend that the COO dispatch it.
|
|
971
|
+
|
|
972
|
+
Operating style:
|
|
973
|
+
- Be concise and precise.
|
|
974
|
+
- Cite memory IDs, task IDs, timestamps, and sender names when available.
|
|
975
|
+
- Distinguish fact from inference.
|
|
976
|
+
- Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
|
|
977
|
+
- Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
|
|
953
978
|
}
|
|
954
979
|
};
|
|
955
980
|
CLIENT_COO_TEMPLATE = `---
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -138,6 +138,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
138
138
|
return null;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
142
|
+
const dir = getKeyDir();
|
|
143
|
+
await mkdir(dir, { recursive: true });
|
|
144
|
+
const keyPath = getKeyPath();
|
|
145
|
+
const machineKey = deriveMachineKey();
|
|
146
|
+
if (machineKey) {
|
|
147
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
148
|
+
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
149
|
+
await chmod(keyPath, 384);
|
|
150
|
+
return "encrypted";
|
|
151
|
+
}
|
|
152
|
+
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
153
|
+
await chmod(keyPath, 384);
|
|
154
|
+
return "plaintext";
|
|
155
|
+
}
|
|
141
156
|
async function getMasterKey() {
|
|
142
157
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
143
158
|
if (nativeValue) {
|
|
@@ -189,6 +204,20 @@ async function getMasterKey() {
|
|
|
189
204
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
190
205
|
if (migrated) {
|
|
191
206
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
207
|
+
try {
|
|
208
|
+
await unlink(keyPath);
|
|
209
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
213
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
214
|
+
if (fallback === "encrypted") {
|
|
215
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
216
|
+
} else {
|
|
217
|
+
process.stderr.write(
|
|
218
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
219
|
+
);
|
|
220
|
+
}
|
|
192
221
|
}
|
|
193
222
|
return key;
|
|
194
223
|
} catch (err) {
|
|
@@ -212,18 +241,10 @@ async function setMasterKey(key) {
|
|
|
212
241
|
} catch {
|
|
213
242
|
}
|
|
214
243
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
const keyPath = getKeyPath();
|
|
218
|
-
const machineKey = deriveMachineKey();
|
|
219
|
-
if (machineKey) {
|
|
220
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
221
|
-
await writeFile(keyPath, encrypted + "\n", "utf-8");
|
|
222
|
-
await chmod(keyPath, 384);
|
|
244
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
245
|
+
if (fallback === "encrypted") {
|
|
223
246
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
224
247
|
} else {
|
|
225
|
-
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
226
|
-
await chmod(keyPath, 384);
|
|
227
248
|
process.stderr.write(
|
|
228
249
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
229
250
|
);
|
package/dist/bin/exe-dispatch.js
CHANGED
|
@@ -6636,6 +6636,15 @@ function readMachineId() {
|
|
|
6636
6636
|
return "";
|
|
6637
6637
|
}
|
|
6638
6638
|
}
|
|
6639
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6640
|
+
const crypto7 = __require("crypto");
|
|
6641
|
+
const iv = crypto7.randomBytes(12);
|
|
6642
|
+
const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6643
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6644
|
+
encrypted += cipher.final("base64");
|
|
6645
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6646
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6647
|
+
}
|
|
6639
6648
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6640
6649
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6641
6650
|
try {
|
|
@@ -6654,6 +6663,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6654
6663
|
return null;
|
|
6655
6664
|
}
|
|
6656
6665
|
}
|
|
6666
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6667
|
+
const dir = getKeyDir();
|
|
6668
|
+
await mkdir4(dir, { recursive: true });
|
|
6669
|
+
const keyPath = getKeyPath();
|
|
6670
|
+
const machineKey = deriveMachineKey();
|
|
6671
|
+
if (machineKey) {
|
|
6672
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6673
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6674
|
+
await chmod2(keyPath, 384);
|
|
6675
|
+
return "encrypted";
|
|
6676
|
+
}
|
|
6677
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6678
|
+
await chmod2(keyPath, 384);
|
|
6679
|
+
return "plaintext";
|
|
6680
|
+
}
|
|
6657
6681
|
async function getMasterKey() {
|
|
6658
6682
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6659
6683
|
if (nativeValue) {
|
|
@@ -6705,6 +6729,20 @@ async function getMasterKey() {
|
|
|
6705
6729
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6706
6730
|
if (migrated) {
|
|
6707
6731
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
6732
|
+
try {
|
|
6733
|
+
await unlink(keyPath);
|
|
6734
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
6735
|
+
} catch {
|
|
6736
|
+
}
|
|
6737
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
6738
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
6739
|
+
if (fallback === "encrypted") {
|
|
6740
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
6741
|
+
} else {
|
|
6742
|
+
process.stderr.write(
|
|
6743
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
6744
|
+
);
|
|
6745
|
+
}
|
|
6708
6746
|
}
|
|
6709
6747
|
return key;
|
|
6710
6748
|
} catch (err) {
|
|
@@ -6922,6 +6960,7 @@ var init_memory_write_governor = __esm({
|
|
|
6922
6960
|
// src/lib/shard-manager.ts
|
|
6923
6961
|
var shard_manager_exports = {};
|
|
6924
6962
|
__export(shard_manager_exports, {
|
|
6963
|
+
auditShardHealth: () => auditShardHealth,
|
|
6925
6964
|
disposeShards: () => disposeShards,
|
|
6926
6965
|
ensureShardSchema: () => ensureShardSchema,
|
|
6927
6966
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -6988,6 +7027,70 @@ function listShards() {
|
|
|
6988
7027
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6989
7028
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6990
7029
|
}
|
|
7030
|
+
async function auditShardHealth(options = {}) {
|
|
7031
|
+
if (!_encryptionKey) {
|
|
7032
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7033
|
+
}
|
|
7034
|
+
const repair = options.repair === true;
|
|
7035
|
+
const dryRun = options.dryRun === true;
|
|
7036
|
+
const names = listShards();
|
|
7037
|
+
const shards = [];
|
|
7038
|
+
for (const name of names) {
|
|
7039
|
+
const dbPath = path19.join(SHARDS_DIR, `${name}.db`);
|
|
7040
|
+
const stat = statSync2(dbPath);
|
|
7041
|
+
const item = {
|
|
7042
|
+
name,
|
|
7043
|
+
path: dbPath,
|
|
7044
|
+
ok: false,
|
|
7045
|
+
unreadable: false,
|
|
7046
|
+
error: null,
|
|
7047
|
+
size: stat.size,
|
|
7048
|
+
mtime: stat.mtime.toISOString(),
|
|
7049
|
+
memoryCount: null
|
|
7050
|
+
};
|
|
7051
|
+
const client = createClient2({
|
|
7052
|
+
url: `file:${dbPath}`,
|
|
7053
|
+
encryptionKey: _encryptionKey
|
|
7054
|
+
});
|
|
7055
|
+
try {
|
|
7056
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7057
|
+
const hasMemories = await client.execute(
|
|
7058
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7059
|
+
);
|
|
7060
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7061
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7062
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7063
|
+
}
|
|
7064
|
+
item.ok = true;
|
|
7065
|
+
} catch (err) {
|
|
7066
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7067
|
+
item.error = message;
|
|
7068
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7069
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7070
|
+
client.close();
|
|
7071
|
+
_shards.delete(name);
|
|
7072
|
+
_shardLastAccess.delete(name);
|
|
7073
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7074
|
+
const archivedPath = path19.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7075
|
+
renameSync4(dbPath, archivedPath);
|
|
7076
|
+
item.archivedPath = archivedPath;
|
|
7077
|
+
}
|
|
7078
|
+
} finally {
|
|
7079
|
+
try {
|
|
7080
|
+
client.close();
|
|
7081
|
+
} catch {
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
shards.push(item);
|
|
7085
|
+
}
|
|
7086
|
+
return {
|
|
7087
|
+
total: shards.length,
|
|
7088
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7089
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7090
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7091
|
+
shards
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
6991
7094
|
async function ensureShardSchema(client) {
|
|
6992
7095
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
6993
7096
|
await client.execute("PRAGMA busy_timeout = 30000");
|