@askexenow/exe-os 0.9.7 → 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 +953 -105
  2. package/dist/bin/backfill-responses.js +952 -104
  3. package/dist/bin/backfill-vectors.js +956 -108
  4. package/dist/bin/cleanup-stale-review-tasks.js +802 -58
  5. package/dist/bin/cli.js +2292 -1070
  6. package/dist/bin/exe-agent-config.js +157 -101
  7. package/dist/bin/exe-agent.js +55 -29
  8. package/dist/bin/exe-assign.js +940 -92
  9. package/dist/bin/exe-boot.js +1424 -442
  10. package/dist/bin/exe-call.js +240 -141
  11. package/dist/bin/exe-cloud.js +198 -70
  12. package/dist/bin/exe-dispatch.js +951 -192
  13. package/dist/bin/exe-doctor.js +791 -51
  14. package/dist/bin/exe-export-behaviors.js +790 -42
  15. package/dist/bin/exe-forget.js +771 -31
  16. package/dist/bin/exe-gateway.js +1592 -521
  17. package/dist/bin/exe-heartbeat.js +850 -109
  18. package/dist/bin/exe-kill.js +783 -35
  19. package/dist/bin/exe-launch-agent.js +1030 -107
  20. package/dist/bin/exe-link.js +916 -110
  21. package/dist/bin/exe-new-employee.js +526 -217
  22. package/dist/bin/exe-pending-messages.js +1046 -62
  23. package/dist/bin/exe-pending-notifications.js +1318 -111
  24. package/dist/bin/exe-pending-reviews.js +1040 -72
  25. package/dist/bin/exe-rename.js +772 -59
  26. package/dist/bin/exe-review.js +772 -32
  27. package/dist/bin/exe-search.js +982 -128
  28. package/dist/bin/exe-session-cleanup.js +1180 -306
  29. package/dist/bin/exe-settings.js +185 -105
  30. package/dist/bin/exe-start-codex.js +886 -132
  31. package/dist/bin/exe-start-opencode.js +873 -119
  32. package/dist/bin/exe-status.js +803 -59
  33. package/dist/bin/exe-team.js +772 -32
  34. package/dist/bin/git-sweep.js +1046 -223
  35. package/dist/bin/graph-backfill.js +779 -31
  36. package/dist/bin/graph-export.js +785 -37
  37. package/dist/bin/install.js +632 -200
  38. package/dist/bin/scan-tasks.js +1055 -232
  39. package/dist/bin/setup.js +1419 -320
  40. package/dist/bin/shard-migrate.js +783 -35
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +782 -34
  43. package/dist/gateway/index.js +1444 -449
  44. package/dist/hooks/bug-report-worker.js +1141 -269
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +1044 -221
  47. package/dist/hooks/error-recall.js +989 -135
  48. package/dist/hooks/exe-heartbeat-hook.js +99 -75
  49. package/dist/hooks/ingest-worker.js +4176 -3226
  50. package/dist/hooks/ingest.js +920 -168
  51. package/dist/hooks/instructions-loaded.js +874 -70
  52. package/dist/hooks/notification.js +860 -56
  53. package/dist/hooks/post-compact.js +881 -73
  54. package/dist/hooks/pre-compact.js +1050 -227
  55. package/dist/hooks/pre-tool-use.js +1084 -159
  56. package/dist/hooks/prompt-ingest-worker.js +1089 -164
  57. package/dist/hooks/prompt-submit.js +1469 -515
  58. package/dist/hooks/response-ingest-worker.js +1104 -179
  59. package/dist/hooks/session-end.js +1085 -251
  60. package/dist/hooks/session-start.js +1241 -231
  61. package/dist/hooks/stop.js +935 -109
  62. package/dist/hooks/subagent-stop.js +881 -73
  63. package/dist/hooks/summary-worker.js +1323 -307
  64. package/dist/index.js +1449 -452
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +909 -115
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +42 -9
  69. package/dist/lib/database.js +739 -33
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +2359 -0
  72. package/dist/lib/device-registry.js +760 -47
  73. package/dist/lib/embedder.js +201 -73
  74. package/dist/lib/employee-templates.js +30 -4
  75. package/dist/lib/employees.js +290 -86
  76. package/dist/lib/exe-daemon-client.js +187 -83
  77. package/dist/lib/exe-daemon.js +1696 -616
  78. package/dist/lib/hybrid-search.js +982 -128
  79. package/dist/lib/identity.js +43 -13
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +167 -80
  82. package/dist/lib/reminders.js +35 -5
  83. package/dist/lib/schedules.js +772 -32
  84. package/dist/lib/skill-learning.js +54 -7
  85. package/dist/lib/store.js +779 -31
  86. package/dist/lib/task-router.js +94 -73
  87. package/dist/lib/tasks.js +298 -225
  88. package/dist/lib/tmux-routing.js +246 -172
  89. package/dist/lib/token-spend.js +52 -14
  90. package/dist/mcp/server.js +2893 -850
  91. package/dist/mcp/tools/complete-reminder.js +35 -5
  92. package/dist/mcp/tools/create-reminder.js +35 -5
  93. package/dist/mcp/tools/create-task.js +507 -323
  94. package/dist/mcp/tools/deactivate-behavior.js +40 -10
  95. package/dist/mcp/tools/list-reminders.js +35 -5
  96. package/dist/mcp/tools/list-tasks.js +277 -104
  97. package/dist/mcp/tools/send-message.js +129 -56
  98. package/dist/mcp/tools/update-task.js +1864 -188
  99. package/dist/runtime/index.js +1083 -259
  100. package/dist/tui/App.js +1501 -434
  101. package/package.json +3 -2
@@ -8,6 +8,44 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/secure-files.ts
12
+ import { chmodSync, existsSync, mkdirSync } from "fs";
13
+ import { chmod, mkdir } from "fs/promises";
14
+ async function ensurePrivateDir(dirPath) {
15
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
16
+ try {
17
+ await chmod(dirPath, PRIVATE_DIR_MODE);
18
+ } catch {
19
+ }
20
+ }
21
+ function ensurePrivateDirSync(dirPath) {
22
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
23
+ try {
24
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
25
+ } catch {
26
+ }
27
+ }
28
+ async function enforcePrivateFile(filePath) {
29
+ try {
30
+ await chmod(filePath, PRIVATE_FILE_MODE);
31
+ } catch {
32
+ }
33
+ }
34
+ function enforcePrivateFileSync(filePath) {
35
+ try {
36
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
37
+ } catch {
38
+ }
39
+ }
40
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
41
+ var init_secure_files = __esm({
42
+ "src/lib/secure-files.ts"() {
43
+ "use strict";
44
+ PRIVATE_DIR_MODE = 448;
45
+ PRIVATE_FILE_MODE = 384;
46
+ }
47
+ });
48
+
11
49
  // src/lib/config.ts
12
50
  var config_exports = {};
13
51
  __export(config_exports, {
@@ -24,8 +62,8 @@ __export(config_exports, {
24
62
  migrateConfig: () => migrateConfig,
25
63
  saveConfig: () => saveConfig
26
64
  });
27
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
28
- import { readFileSync, existsSync, renameSync } from "fs";
65
+ import { readFile, writeFile } from "fs/promises";
66
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
29
67
  import path from "path";
30
68
  import os from "os";
31
69
  function resolveDataDir() {
@@ -33,7 +71,7 @@ function resolveDataDir() {
33
71
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
34
72
  const newDir = path.join(os.homedir(), ".exe-os");
35
73
  const legacyDir = path.join(os.homedir(), ".exe-mem");
36
- if (!existsSync(newDir) && existsSync(legacyDir)) {
74
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
37
75
  try {
38
76
  renameSync(legacyDir, newDir);
39
77
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -96,9 +134,9 @@ function normalizeAutoUpdate(raw) {
96
134
  }
97
135
  async function loadConfig() {
98
136
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
99
- await mkdir(dir, { recursive: true });
137
+ await ensurePrivateDir(dir);
100
138
  const configPath = path.join(dir, "config.json");
101
- if (!existsSync(configPath)) {
139
+ if (!existsSync2(configPath)) {
102
140
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
103
141
  }
104
142
  const raw = await readFile(configPath, "utf-8");
@@ -111,6 +149,7 @@ async function loadConfig() {
111
149
  `);
112
150
  try {
113
151
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
152
+ await enforcePrivateFile(configPath);
114
153
  } catch {
115
154
  }
116
155
  }
@@ -129,7 +168,7 @@ async function loadConfig() {
129
168
  function loadConfigSync() {
130
169
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
131
170
  const configPath = path.join(dir, "config.json");
132
- if (!existsSync(configPath)) {
171
+ if (!existsSync2(configPath)) {
133
172
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
134
173
  }
135
174
  try {
@@ -147,12 +186,10 @@ function loadConfigSync() {
147
186
  }
148
187
  async function saveConfig(config) {
149
188
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
150
- await mkdir(dir, { recursive: true });
189
+ await ensurePrivateDir(dir);
151
190
  const configPath = path.join(dir, "config.json");
152
191
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
- if (config.cloud?.apiKey) {
154
- await chmod(configPath, 384);
155
- }
192
+ await enforcePrivateFile(configPath);
156
193
  }
157
194
  async function loadConfigFrom(configPath) {
158
195
  const raw = await readFile(configPath, "utf-8");
@@ -172,6 +209,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
172
209
  var init_config = __esm({
173
210
  "src/lib/config.ts"() {
174
211
  "use strict";
212
+ init_secure_files();
175
213
  EXE_AI_DIR = resolveDataDir();
176
214
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
177
215
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -257,20 +295,58 @@ import net from "net";
257
295
  import os2 from "os";
258
296
  import { spawn } from "child_process";
259
297
  import { randomUUID } from "crypto";
260
- import { existsSync as existsSync2, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
261
- import path2 from "path";
298
+ import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
299
+ import path3 from "path";
262
300
  import { fileURLToPath } from "url";
263
- var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path2.join(EXE_AI_DIR, "exed.sock");
264
- var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path2.join(EXE_AI_DIR, "exed.pid");
265
- var SPAWN_LOCK_PATH = path2.join(EXE_AI_DIR, "exed-spawn.lock");
301
+
302
+ // src/lib/daemon-auth.ts
303
+ init_config();
304
+ init_secure_files();
305
+ import crypto from "crypto";
306
+ import path2 from "path";
307
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
308
+ var DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
309
+ function normalizeToken(token) {
310
+ if (!token) return null;
311
+ const trimmed = token.trim();
312
+ return trimmed.length > 0 ? trimmed : null;
313
+ }
314
+ function readDaemonToken() {
315
+ try {
316
+ if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
317
+ return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
318
+ } catch {
319
+ return null;
320
+ }
321
+ }
322
+ function ensureDaemonToken(seed) {
323
+ const existing = readDaemonToken();
324
+ if (existing) return existing;
325
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
326
+ ensurePrivateDirSync(EXE_AI_DIR);
327
+ writeFileSync(DAEMON_TOKEN_PATH, `${token}
328
+ `, "utf8");
329
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
330
+ return token;
331
+ }
332
+
333
+ // src/lib/exe-daemon-client.ts
334
+ var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
335
+ var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
336
+ var SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
266
337
  var SPAWN_LOCK_STALE_MS = 3e4;
267
338
  var CONNECT_TIMEOUT_MS = 15e3;
268
339
  var REQUEST_TIMEOUT_MS = 3e4;
340
+ var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
269
341
  var _socket = null;
270
342
  var _connected = false;
271
343
  var _buffer = "";
272
344
  var _requestCount = 0;
345
+ var _consecutiveFailures = 0;
273
346
  var HEALTH_CHECK_INTERVAL = 100;
347
+ var MAX_RETRIES_BEFORE_RESTART = 3;
348
+ var RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
349
+ var MIN_DAEMON_AGE_MS = 3e4;
274
350
  var _pending = /* @__PURE__ */ new Map();
275
351
  var MAX_BUFFER = 1e7;
276
352
  function handleData(chunk) {
@@ -299,9 +375,9 @@ function handleData(chunk) {
299
375
  }
300
376
  }
301
377
  function cleanupStaleFiles() {
302
- if (existsSync2(PID_PATH)) {
378
+ if (existsSync4(PID_PATH)) {
303
379
  try {
304
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
380
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
305
381
  if (pid > 0) {
306
382
  try {
307
383
  process.kill(pid, 0);
@@ -322,11 +398,11 @@ function cleanupStaleFiles() {
322
398
  }
323
399
  }
324
400
  function findPackageRoot() {
325
- let dir = path2.dirname(fileURLToPath(import.meta.url));
326
- const { root } = path2.parse(dir);
401
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
402
+ const { root } = path3.parse(dir);
327
403
  while (dir !== root) {
328
- if (existsSync2(path2.join(dir, "package.json"))) return dir;
329
- dir = path2.dirname(dir);
404
+ if (existsSync4(path3.join(dir, "package.json"))) return dir;
405
+ dir = path3.dirname(dir);
330
406
  }
331
407
  return null;
332
408
  }
@@ -352,16 +428,17 @@ function spawnDaemon() {
352
428
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
353
429
  return;
354
430
  }
355
- const daemonPath = path2.join(pkgRoot, "dist", "lib", "exe-daemon.js");
356
- if (!existsSync2(daemonPath)) {
431
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
432
+ if (!existsSync4(daemonPath)) {
357
433
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
358
434
  `);
359
435
  return;
360
436
  }
361
437
  const resolvedPath = daemonPath;
438
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
362
439
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
363
440
  `);
364
- const logPath = path2.join(path2.dirname(SOCKET_PATH), "exed.log");
441
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
365
442
  let stderrFd = "ignore";
366
443
  try {
367
444
  stderrFd = openSync(logPath, "a");
@@ -379,7 +456,8 @@ function spawnDaemon() {
379
456
  TMUX_PANE: void 0,
380
457
  // Prevents resolveExeSession() from scoping to one session
381
458
  EXE_DAEMON_SOCK: SOCKET_PATH,
382
- EXE_DAEMON_PID: PID_PATH
459
+ EXE_DAEMON_PID: PID_PATH,
460
+ [DAEMON_TOKEN_ENV]: daemonToken
383
461
  }
384
462
  });
385
463
  child.unref();
@@ -489,13 +567,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
489
567
  return;
490
568
  }
491
569
  const id = randomUUID();
570
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
492
571
  const timer = setTimeout(() => {
493
572
  _pending.delete(id);
494
573
  resolve({ error: "Request timeout" });
495
574
  }, timeoutMs);
496
575
  _pending.set(id, { resolve, timer });
497
576
  try {
498
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
577
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
499
578
  } catch {
500
579
  clearTimeout(timer);
501
580
  _pending.delete(id);
@@ -512,74 +591,123 @@ async function pingDaemon() {
512
591
  return null;
513
592
  }
514
593
  function killAndRespawnDaemon() {
515
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
516
- if (existsSync2(PID_PATH)) {
517
- try {
518
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
519
- if (pid > 0) {
520
- try {
521
- process.kill(pid, "SIGKILL");
522
- } catch {
594
+ if (!acquireSpawnLock()) {
595
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
596
+ if (_socket) {
597
+ _socket.destroy();
598
+ _socket = null;
599
+ }
600
+ _connected = false;
601
+ _buffer = "";
602
+ return;
603
+ }
604
+ try {
605
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
606
+ if (existsSync4(PID_PATH)) {
607
+ try {
608
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
609
+ if (pid > 0) {
610
+ try {
611
+ process.kill(pid, "SIGKILL");
612
+ } catch {
613
+ }
523
614
  }
615
+ } catch {
524
616
  }
617
+ }
618
+ if (_socket) {
619
+ _socket.destroy();
620
+ _socket = null;
621
+ }
622
+ _connected = false;
623
+ _buffer = "";
624
+ try {
625
+ unlinkSync(PID_PATH);
525
626
  } catch {
526
627
  }
628
+ try {
629
+ unlinkSync(SOCKET_PATH);
630
+ } catch {
631
+ }
632
+ spawnDaemon();
633
+ } finally {
634
+ releaseSpawnLock();
527
635
  }
528
- if (_socket) {
529
- _socket.destroy();
530
- _socket = null;
531
- }
532
- _connected = false;
533
- _buffer = "";
636
+ }
637
+ function isDaemonTooYoung() {
534
638
  try {
535
- unlinkSync(PID_PATH);
639
+ const stat = statSync(PID_PATH);
640
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
536
641
  } catch {
642
+ return false;
537
643
  }
538
- try {
539
- unlinkSync(SOCKET_PATH);
540
- } catch {
644
+ }
645
+ async function retryThenRestart(doRequest, label) {
646
+ const result = await doRequest();
647
+ if (!result.error) {
648
+ _consecutiveFailures = 0;
649
+ return result;
650
+ }
651
+ _consecutiveFailures++;
652
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
653
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
654
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
655
+ `);
656
+ await new Promise((r) => setTimeout(r, delayMs));
657
+ if (!_connected) {
658
+ if (!await connectToSocket()) continue;
659
+ }
660
+ const retry = await doRequest();
661
+ if (!retry.error) {
662
+ _consecutiveFailures = 0;
663
+ return retry;
664
+ }
665
+ _consecutiveFailures++;
666
+ }
667
+ if (isDaemonTooYoung()) {
668
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
669
+ `);
670
+ return { error: result.error };
671
+ }
672
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
673
+ `);
674
+ killAndRespawnDaemon();
675
+ const start = Date.now();
676
+ let delay = 200;
677
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
678
+ await new Promise((r) => setTimeout(r, delay));
679
+ if (await connectToSocket()) break;
680
+ delay = Math.min(delay * 2, 3e3);
541
681
  }
542
- spawnDaemon();
682
+ if (!_connected) return { error: "Daemon restart failed" };
683
+ const final = await doRequest();
684
+ if (!final.error) _consecutiveFailures = 0;
685
+ return final;
543
686
  }
544
687
  async function embedViaClient(text, priority = "high") {
545
688
  if (!_connected && !await connectEmbedDaemon()) return null;
546
689
  _requestCount++;
547
690
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
548
691
  const health = await pingDaemon();
549
- if (!health) {
692
+ if (!health && !isDaemonTooYoung()) {
550
693
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
551
694
  `);
552
695
  killAndRespawnDaemon();
553
696
  const start = Date.now();
554
- let delay = 200;
697
+ let d = 200;
555
698
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
556
- await new Promise((r) => setTimeout(r, delay));
699
+ await new Promise((r) => setTimeout(r, d));
557
700
  if (await connectToSocket()) break;
558
- delay = Math.min(delay * 2, 3e3);
701
+ d = Math.min(d * 2, 3e3);
559
702
  }
560
703
  if (!_connected) return null;
561
704
  }
562
705
  }
563
- const result = await sendRequest([text], priority);
564
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
565
- if (result.error) {
566
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
567
- `);
568
- killAndRespawnDaemon();
569
- const start = Date.now();
570
- let delay = 200;
571
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
572
- await new Promise((r) => setTimeout(r, delay));
573
- if (await connectToSocket()) break;
574
- delay = Math.min(delay * 2, 3e3);
575
- }
576
- if (!_connected) return null;
577
- const retry = await sendRequest([text], priority);
578
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
579
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
580
- `);
581
- }
582
- return null;
706
+ const result = await retryThenRestart(
707
+ () => sendRequest([text], priority),
708
+ "Embed"
709
+ );
710
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
583
711
  }
584
712
  function disconnectClient() {
585
713
  if (_socket) {
@@ -625,10 +753,10 @@ async function disposeEmbedder() {
625
753
  async function embedDirect(text) {
626
754
  const llamaCpp = await import("node-llama-cpp");
627
755
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
628
- const { existsSync: existsSync3 } = await import("fs");
629
- const path3 = await import("path");
630
- const modelPath = path3.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
631
- if (!existsSync3(modelPath)) {
756
+ const { existsSync: existsSync5 } = await import("fs");
757
+ const path4 = await import("path");
758
+ const modelPath = path4.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
759
+ if (!existsSync5(modelPath)) {
632
760
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
633
761
  }
634
762
  const llama = await llamaCpp.getLlama();
@@ -3,9 +3,18 @@ var __esm = (fn, res) => function __init() {
3
3
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
4
  };
5
5
 
6
+ // src/lib/secure-files.ts
7
+ import { chmodSync, existsSync, mkdirSync } from "fs";
8
+ import { chmod, mkdir } from "fs/promises";
9
+ var init_secure_files = __esm({
10
+ "src/lib/secure-files.ts"() {
11
+ "use strict";
12
+ }
13
+ });
14
+
6
15
  // src/lib/config.ts
7
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
8
- import { readFileSync, existsSync, renameSync } from "fs";
16
+ import { readFile, writeFile } from "fs/promises";
17
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
18
  import path from "path";
10
19
  import os from "os";
11
20
  function resolveDataDir() {
@@ -13,7 +22,7 @@ function resolveDataDir() {
13
22
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
23
  const newDir = path.join(os.homedir(), ".exe-os");
15
24
  const legacyDir = path.join(os.homedir(), ".exe-mem");
16
- if (!existsSync(newDir) && existsSync(legacyDir)) {
25
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
26
  try {
18
27
  renameSync(legacyDir, newDir);
19
28
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -28,6 +37,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
28
37
  var init_config = __esm({
29
38
  "src/lib/config.ts"() {
30
39
  "use strict";
40
+ init_secure_files();
31
41
  EXE_AI_DIR = resolveDataDir();
32
42
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
33
43
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -103,11 +113,27 @@ import { createClient } from "@libsql/client";
103
113
  // src/lib/employees.ts
104
114
  init_config();
105
115
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
106
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
116
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
107
117
  import { execSync } from "child_process";
108
118
  import path2 from "path";
109
119
  import os2 from "os";
110
120
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
121
+ var IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
122
+
123
+ // src/lib/database-adapter.ts
124
+ import os3 from "os";
125
+ import path3 from "path";
126
+ import { createRequire } from "module";
127
+ import { pathToFileURL } from "url";
128
+ var BOOLEAN_COLUMNS_BY_TABLE = {
129
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
130
+ behaviors: /* @__PURE__ */ new Set(["active"]),
131
+ notifications: /* @__PURE__ */ new Set(["read"]),
132
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
133
+ };
134
+ var BOOLEAN_COLUMN_NAMES = new Set(
135
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
136
+ );
111
137
 
112
138
  // src/lib/platform-procedures.ts
113
139
  var PLATFORM_PROCEDURES = [