@askexenow/exe-os 0.9.8 → 0.9.9

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.
Files changed (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -70,9 +70,47 @@ var init_db_retry = __esm({
70
70
  }
71
71
  });
72
72
 
73
+ // src/lib/secure-files.ts
74
+ import { chmodSync, existsSync, mkdirSync } from "fs";
75
+ import { chmod, mkdir } from "fs/promises";
76
+ async function ensurePrivateDir(dirPath) {
77
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
78
+ try {
79
+ await chmod(dirPath, PRIVATE_DIR_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ function ensurePrivateDirSync(dirPath) {
84
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
85
+ try {
86
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
87
+ } catch {
88
+ }
89
+ }
90
+ async function enforcePrivateFile(filePath) {
91
+ try {
92
+ await chmod(filePath, PRIVATE_FILE_MODE);
93
+ } catch {
94
+ }
95
+ }
96
+ function enforcePrivateFileSync(filePath) {
97
+ try {
98
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
99
+ } catch {
100
+ }
101
+ }
102
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
103
+ var init_secure_files = __esm({
104
+ "src/lib/secure-files.ts"() {
105
+ "use strict";
106
+ PRIVATE_DIR_MODE = 448;
107
+ PRIVATE_FILE_MODE = 384;
108
+ }
109
+ });
110
+
73
111
  // src/lib/config.ts
74
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
75
- import { readFileSync, existsSync, renameSync } from "fs";
112
+ import { readFile, writeFile } from "fs/promises";
113
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
76
114
  import path from "path";
77
115
  import os from "os";
78
116
  function resolveDataDir() {
@@ -80,7 +118,7 @@ function resolveDataDir() {
80
118
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
81
119
  const newDir = path.join(os.homedir(), ".exe-os");
82
120
  const legacyDir = path.join(os.homedir(), ".exe-mem");
83
- if (!existsSync(newDir) && existsSync(legacyDir)) {
121
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
84
122
  try {
85
123
  renameSync(legacyDir, newDir);
86
124
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -143,9 +181,9 @@ function normalizeAutoUpdate(raw) {
143
181
  }
144
182
  async function loadConfig() {
145
183
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
146
- await mkdir(dir, { recursive: true });
184
+ await ensurePrivateDir(dir);
147
185
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
186
+ if (!existsSync2(configPath)) {
149
187
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
188
  }
151
189
  const raw = await readFile(configPath, "utf-8");
@@ -158,6 +196,7 @@ async function loadConfig() {
158
196
  `);
159
197
  try {
160
198
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
199
+ await enforcePrivateFile(configPath);
161
200
  } catch {
162
201
  }
163
202
  }
@@ -177,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
177
216
  var init_config = __esm({
178
217
  "src/lib/config.ts"() {
179
218
  "use strict";
219
+ init_secure_files();
180
220
  EXE_AI_DIR = resolveDataDir();
181
221
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
182
222
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -255,7 +295,7 @@ var init_config = __esm({
255
295
 
256
296
  // src/lib/employees.ts
257
297
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
258
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
298
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
259
299
  import { execSync } from "child_process";
260
300
  import path2 from "path";
261
301
  import os2 from "os";
@@ -272,7 +312,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
272
312
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
273
313
  }
274
314
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
275
- if (!existsSync2(employeesPath)) return [];
315
+ if (!existsSync3(employeesPath)) return [];
276
316
  try {
277
317
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
278
318
  } catch {
@@ -1217,6 +1257,7 @@ async function ensureSchema() {
1217
1257
  project TEXT NOT NULL,
1218
1258
  summary TEXT NOT NULL,
1219
1259
  task_file TEXT,
1260
+ session_scope TEXT,
1220
1261
  read INTEGER NOT NULL DEFAULT 0,
1221
1262
  created_at TEXT NOT NULL
1222
1263
  );
@@ -1225,7 +1266,7 @@ async function ensureSchema() {
1225
1266
  ON notifications(read);
1226
1267
 
1227
1268
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1228
- ON notifications(agent_id);
1269
+ ON notifications(agent_id, session_scope);
1229
1270
 
1230
1271
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1231
1272
  ON notifications(task_file);
@@ -1263,6 +1304,7 @@ async function ensureSchema() {
1263
1304
  target_agent TEXT NOT NULL,
1264
1305
  target_project TEXT,
1265
1306
  target_device TEXT NOT NULL DEFAULT 'local',
1307
+ session_scope TEXT,
1266
1308
  content TEXT NOT NULL,
1267
1309
  priority TEXT DEFAULT 'normal',
1268
1310
  status TEXT DEFAULT 'pending',
@@ -1276,10 +1318,31 @@ async function ensureSchema() {
1276
1318
  );
1277
1319
 
1278
1320
  CREATE INDEX IF NOT EXISTS idx_messages_target
1279
- ON messages(target_agent, status);
1321
+ ON messages(target_agent, session_scope, status);
1280
1322
 
1281
1323
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1282
- ON messages(target_agent, from_agent, server_seq);
1324
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1325
+ `);
1326
+ try {
1327
+ await client.execute({
1328
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1329
+ args: []
1330
+ });
1331
+ } catch {
1332
+ }
1333
+ try {
1334
+ await client.execute({
1335
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1336
+ args: []
1337
+ });
1338
+ } catch {
1339
+ }
1340
+ await client.executeMultiple(`
1341
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1342
+ ON notifications(agent_id, session_scope, read, created_at);
1343
+
1344
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1345
+ ON messages(target_agent, session_scope, status, created_at);
1283
1346
  `);
1284
1347
  try {
1285
1348
  await client.execute({
@@ -1863,6 +1926,13 @@ async function ensureSchema() {
1863
1926
  } catch {
1864
1927
  }
1865
1928
  }
1929
+ try {
1930
+ await client.execute({
1931
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
1932
+ args: []
1933
+ });
1934
+ } catch {
1935
+ }
1866
1936
  }
1867
1937
  var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1868
1938
  var init_database = __esm({
@@ -1885,6 +1955,7 @@ var shard_manager_exports = {};
1885
1955
  __export(shard_manager_exports, {
1886
1956
  disposeShards: () => disposeShards,
1887
1957
  ensureShardSchema: () => ensureShardSchema,
1958
+ getOpenShardCount: () => getOpenShardCount,
1888
1959
  getReadyShardClient: () => getReadyShardClient,
1889
1960
  getShardClient: () => getShardClient,
1890
1961
  getShardsDir: () => getShardsDir,
@@ -1894,14 +1965,17 @@ __export(shard_manager_exports, {
1894
1965
  shardExists: () => shardExists
1895
1966
  });
1896
1967
  import path5 from "path";
1897
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1968
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1898
1969
  import { createClient as createClient2 } from "@libsql/client";
1899
1970
  function initShardManager(encryptionKey) {
1900
1971
  _encryptionKey = encryptionKey;
1901
- if (!existsSync4(SHARDS_DIR)) {
1902
- mkdirSync(SHARDS_DIR, { recursive: true });
1972
+ if (!existsSync5(SHARDS_DIR)) {
1973
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1903
1974
  }
1904
1975
  _shardingEnabled = true;
1976
+ if (_evictionTimer) clearInterval(_evictionTimer);
1977
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
1978
+ _evictionTimer.unref();
1905
1979
  }
1906
1980
  function isShardingEnabled() {
1907
1981
  return _shardingEnabled;
@@ -1918,21 +1992,28 @@ function getShardClient(projectName) {
1918
1992
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1919
1993
  }
1920
1994
  const cached = _shards.get(safeName);
1921
- if (cached) return cached;
1995
+ if (cached) {
1996
+ _shardLastAccess.set(safeName, Date.now());
1997
+ return cached;
1998
+ }
1999
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2000
+ evictLRU();
2001
+ }
1922
2002
  const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1923
2003
  const client = createClient2({
1924
2004
  url: `file:${dbPath}`,
1925
2005
  encryptionKey: _encryptionKey
1926
2006
  });
1927
2007
  _shards.set(safeName, client);
2008
+ _shardLastAccess.set(safeName, Date.now());
1928
2009
  return client;
1929
2010
  }
1930
2011
  function shardExists(projectName) {
1931
2012
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1932
- return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
2013
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1933
2014
  }
1934
2015
  function listShards() {
1935
- if (!existsSync4(SHARDS_DIR)) return [];
2016
+ if (!existsSync5(SHARDS_DIR)) return [];
1936
2017
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1937
2018
  }
1938
2019
  async function ensureShardSchema(client) {
@@ -1984,6 +2065,8 @@ async function ensureShardSchema(client) {
1984
2065
  for (const col of [
1985
2066
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
1986
2067
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2068
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2069
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
1987
2070
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
1988
2071
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
1989
2072
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -2121,21 +2204,69 @@ async function getReadyShardClient(projectName) {
2121
2204
  await ensureShardSchema(client);
2122
2205
  return client;
2123
2206
  }
2207
+ function evictLRU() {
2208
+ let oldest = null;
2209
+ let oldestTime = Infinity;
2210
+ for (const [name, time] of _shardLastAccess) {
2211
+ if (time < oldestTime) {
2212
+ oldestTime = time;
2213
+ oldest = name;
2214
+ }
2215
+ }
2216
+ if (oldest) {
2217
+ const client = _shards.get(oldest);
2218
+ if (client) {
2219
+ client.close();
2220
+ }
2221
+ _shards.delete(oldest);
2222
+ _shardLastAccess.delete(oldest);
2223
+ }
2224
+ }
2225
+ function evictIdleShards() {
2226
+ const now = Date.now();
2227
+ const toEvict = [];
2228
+ for (const [name, lastAccess] of _shardLastAccess) {
2229
+ if (now - lastAccess > SHARD_IDLE_MS) {
2230
+ toEvict.push(name);
2231
+ }
2232
+ }
2233
+ for (const name of toEvict) {
2234
+ const client = _shards.get(name);
2235
+ if (client) {
2236
+ client.close();
2237
+ }
2238
+ _shards.delete(name);
2239
+ _shardLastAccess.delete(name);
2240
+ }
2241
+ }
2242
+ function getOpenShardCount() {
2243
+ return _shards.size;
2244
+ }
2124
2245
  function disposeShards() {
2246
+ if (_evictionTimer) {
2247
+ clearInterval(_evictionTimer);
2248
+ _evictionTimer = null;
2249
+ }
2125
2250
  for (const [, client] of _shards) {
2126
2251
  client.close();
2127
2252
  }
2128
2253
  _shards.clear();
2254
+ _shardLastAccess.clear();
2129
2255
  _shardingEnabled = false;
2130
2256
  _encryptionKey = null;
2131
2257
  }
2132
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2258
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
2133
2259
  var init_shard_manager = __esm({
2134
2260
  "src/lib/shard-manager.ts"() {
2135
2261
  "use strict";
2136
2262
  init_config();
2137
2263
  SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2264
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2265
+ MAX_OPEN_SHARDS = 10;
2266
+ EVICTION_INTERVAL_MS = 60 * 1e3;
2138
2267
  _shards = /* @__PURE__ */ new Map();
2268
+ _shardLastAccess = /* @__PURE__ */ new Map();
2269
+ _evictionTimer = null;
2139
2270
  _encryptionKey = null;
2140
2271
  _shardingEnabled = false;
2141
2272
  }
@@ -2328,13 +2459,50 @@ ${p.content}`).join("\n\n");
2328
2459
  }
2329
2460
  });
2330
2461
 
2462
+ // src/lib/daemon-auth.ts
2463
+ import crypto from "crypto";
2464
+ import path6 from "path";
2465
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2466
+ function normalizeToken(token) {
2467
+ if (!token) return null;
2468
+ const trimmed = token.trim();
2469
+ return trimmed.length > 0 ? trimmed : null;
2470
+ }
2471
+ function readDaemonToken() {
2472
+ try {
2473
+ if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
2474
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
2475
+ } catch {
2476
+ return null;
2477
+ }
2478
+ }
2479
+ function ensureDaemonToken(seed) {
2480
+ const existing = readDaemonToken();
2481
+ if (existing) return existing;
2482
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
2483
+ ensurePrivateDirSync(EXE_AI_DIR);
2484
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
2485
+ `, "utf8");
2486
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
2487
+ return token;
2488
+ }
2489
+ var DAEMON_TOKEN_PATH;
2490
+ var init_daemon_auth = __esm({
2491
+ "src/lib/daemon-auth.ts"() {
2492
+ "use strict";
2493
+ init_config();
2494
+ init_secure_files();
2495
+ DAEMON_TOKEN_PATH = path6.join(EXE_AI_DIR, "exed.token");
2496
+ }
2497
+ });
2498
+
2331
2499
  // src/lib/exe-daemon-client.ts
2332
2500
  import net from "net";
2333
2501
  import os5 from "os";
2334
2502
  import { spawn } from "child_process";
2335
2503
  import { randomUUID as randomUUID2 } from "crypto";
2336
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
2337
- import path6 from "path";
2504
+ import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2505
+ import path7 from "path";
2338
2506
  import { fileURLToPath } from "url";
2339
2507
  function handleData(chunk) {
2340
2508
  _buffer += chunk.toString();
@@ -2362,9 +2530,9 @@ function handleData(chunk) {
2362
2530
  }
2363
2531
  }
2364
2532
  function cleanupStaleFiles() {
2365
- if (existsSync5(PID_PATH)) {
2533
+ if (existsSync7(PID_PATH)) {
2366
2534
  try {
2367
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2535
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
2368
2536
  if (pid > 0) {
2369
2537
  try {
2370
2538
  process.kill(pid, 0);
@@ -2385,11 +2553,11 @@ function cleanupStaleFiles() {
2385
2553
  }
2386
2554
  }
2387
2555
  function findPackageRoot() {
2388
- let dir = path6.dirname(fileURLToPath(import.meta.url));
2389
- const { root } = path6.parse(dir);
2556
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
2557
+ const { root } = path7.parse(dir);
2390
2558
  while (dir !== root) {
2391
- if (existsSync5(path6.join(dir, "package.json"))) return dir;
2392
- dir = path6.dirname(dir);
2559
+ if (existsSync7(path7.join(dir, "package.json"))) return dir;
2560
+ dir = path7.dirname(dir);
2393
2561
  }
2394
2562
  return null;
2395
2563
  }
@@ -2415,16 +2583,17 @@ function spawnDaemon() {
2415
2583
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
2416
2584
  return;
2417
2585
  }
2418
- const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2419
- if (!existsSync5(daemonPath)) {
2586
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2587
+ if (!existsSync7(daemonPath)) {
2420
2588
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2421
2589
  `);
2422
2590
  return;
2423
2591
  }
2424
2592
  const resolvedPath = daemonPath;
2593
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
2425
2594
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2426
2595
  `);
2427
- const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
2596
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
2428
2597
  let stderrFd = "ignore";
2429
2598
  try {
2430
2599
  stderrFd = openSync(logPath, "a");
@@ -2442,7 +2611,8 @@ function spawnDaemon() {
2442
2611
  TMUX_PANE: void 0,
2443
2612
  // Prevents resolveExeSession() from scoping to one session
2444
2613
  EXE_DAEMON_SOCK: SOCKET_PATH,
2445
- EXE_DAEMON_PID: PID_PATH
2614
+ EXE_DAEMON_PID: PID_PATH,
2615
+ [DAEMON_TOKEN_ENV]: daemonToken
2446
2616
  }
2447
2617
  });
2448
2618
  child.unref();
@@ -2552,13 +2722,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
2552
2722
  return;
2553
2723
  }
2554
2724
  const id = randomUUID2();
2725
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
2555
2726
  const timer = setTimeout(() => {
2556
2727
  _pending.delete(id);
2557
2728
  resolve({ error: "Request timeout" });
2558
2729
  }, timeoutMs);
2559
2730
  _pending.set(id, { resolve, timer });
2560
2731
  try {
2561
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
2732
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
2562
2733
  } catch {
2563
2734
  clearTimeout(timer);
2564
2735
  _pending.delete(id);
@@ -2587,9 +2758,9 @@ function killAndRespawnDaemon() {
2587
2758
  }
2588
2759
  try {
2589
2760
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
2590
- if (existsSync5(PID_PATH)) {
2761
+ if (existsSync7(PID_PATH)) {
2591
2762
  try {
2592
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2763
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
2593
2764
  if (pid > 0) {
2594
2765
  try {
2595
2766
  process.kill(pid, "SIGKILL");
@@ -2693,17 +2864,19 @@ async function embedViaClient(text, priority = "high") {
2693
2864
  );
2694
2865
  return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
2695
2866
  }
2696
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2867
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2697
2868
  var init_exe_daemon_client = __esm({
2698
2869
  "src/lib/exe-daemon-client.ts"() {
2699
2870
  "use strict";
2700
2871
  init_config();
2701
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
2702
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
2703
- SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
2872
+ init_daemon_auth();
2873
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
2874
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
2875
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
2704
2876
  SPAWN_LOCK_STALE_MS = 3e4;
2705
2877
  CONNECT_TIMEOUT_MS = 15e3;
2706
2878
  REQUEST_TIMEOUT_MS = 3e4;
2879
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
2707
2880
  _socket = null;
2708
2881
  _connected = false;
2709
2882
  _buffer = "";
@@ -2724,7 +2897,7 @@ init_database();
2724
2897
 
2725
2898
  // src/lib/keychain.ts
2726
2899
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2727
- import { existsSync as existsSync3 } from "fs";
2900
+ import { existsSync as existsSync4 } from "fs";
2728
2901
  import path4 from "path";
2729
2902
  import os4 from "os";
2730
2903
  var SERVICE = "exe-mem";
@@ -2754,7 +2927,7 @@ async function getMasterKey() {
2754
2927
  }
2755
2928
  }
2756
2929
  const keyPath = getKeyPath();
2757
- if (!existsSync3(keyPath)) {
2930
+ if (!existsSync4(keyPath)) {
2758
2931
  process.stderr.write(
2759
2932
  `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2760
2933
  `
@@ -2930,32 +3103,32 @@ function isMainModule(importMetaUrl) {
2930
3103
  }
2931
3104
 
2932
3105
  // src/bin/backfill-vectors.ts
2933
- import { existsSync as existsSync7, unlinkSync as unlinkSync4 } from "fs";
2934
- import path8 from "path";
3106
+ import { existsSync as existsSync9, unlinkSync as unlinkSync4 } from "fs";
3107
+ import path9 from "path";
2935
3108
 
2936
3109
  // src/lib/worker-gate.ts
2937
3110
  init_config();
2938
- import { readdirSync as readdirSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync3, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
2939
- import path7 from "path";
2940
- var WORKER_PID_DIR = path7.join(EXE_AI_DIR, "worker-pids");
3111
+ import { readdirSync as readdirSync2, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
3112
+ import path8 from "path";
3113
+ var WORKER_PID_DIR = path8.join(EXE_AI_DIR, "worker-pids");
2941
3114
  function registerWorkerPid(pid) {
2942
3115
  try {
2943
- mkdirSync2(WORKER_PID_DIR, { recursive: true });
2944
- writeFileSync2(path7.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
3116
+ mkdirSync3(WORKER_PID_DIR, { recursive: true });
3117
+ writeFileSync3(path8.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
2945
3118
  } catch {
2946
3119
  }
2947
3120
  }
2948
3121
  function cleanupWorkerPid() {
2949
3122
  try {
2950
- unlinkSync3(path7.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
3123
+ unlinkSync3(path8.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
2951
3124
  } catch {
2952
3125
  }
2953
3126
  }
2954
- var BACKFILL_LOCK = path7.join(WORKER_PID_DIR, "backfill.lock");
3127
+ var BACKFILL_LOCK = path8.join(WORKER_PID_DIR, "backfill.lock");
2955
3128
  function tryAcquireBackfillLock() {
2956
3129
  try {
2957
- mkdirSync2(WORKER_PID_DIR, { recursive: true });
2958
- if (existsSync6(BACKFILL_LOCK)) {
3130
+ mkdirSync3(WORKER_PID_DIR, { recursive: true });
3131
+ if (existsSync8(BACKFILL_LOCK)) {
2959
3132
  try {
2960
3133
  const pid = parseInt(
2961
3134
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -2971,7 +3144,7 @@ function tryAcquireBackfillLock() {
2971
3144
  } catch {
2972
3145
  }
2973
3146
  }
2974
- writeFileSync2(BACKFILL_LOCK, String(process.pid));
3147
+ writeFileSync3(BACKFILL_LOCK, String(process.pid));
2975
3148
  return true;
2976
3149
  } catch {
2977
3150
  return true;
@@ -2986,7 +3159,7 @@ function releaseBackfillLock() {
2986
3159
 
2987
3160
  // src/bin/backfill-vectors.ts
2988
3161
  var BATCH_SIZE = 100;
2989
- var BACKFILL_FLAG = path8.join(EXE_AI_DIR, "session-cache", "needs-backfill");
3162
+ var BACKFILL_FLAG = path9.join(EXE_AI_DIR, "session-cache", "needs-backfill");
2990
3163
  async function backfillVectors() {
2991
3164
  if (!tryAcquireBackfillLock()) {
2992
3165
  process.stderr.write("[backfill] Another backfill is already running \u2014 exiting\n");
@@ -3064,7 +3237,7 @@ async function backfillVectors() {
3064
3237
  return { processed: totalProcessed, failed: totalFailed, remaining: remainingCount };
3065
3238
  }
3066
3239
  function isBackfillNeeded() {
3067
- return existsSync7(BACKFILL_FLAG);
3240
+ return existsSync9(BACKFILL_FLAG);
3068
3241
  }
3069
3242
  if (isMainModule(import.meta.url)) {
3070
3243
  backfillVectors().then((result) => {