@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
package/dist/bin/setup.js CHANGED
@@ -15,6 +15,44 @@ var __export = (target, all) => {
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
17
 
18
+ // src/lib/secure-files.ts
19
+ import { chmodSync, existsSync, mkdirSync } from "fs";
20
+ import { chmod, mkdir } from "fs/promises";
21
+ async function ensurePrivateDir(dirPath) {
22
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
23
+ try {
24
+ await chmod(dirPath, PRIVATE_DIR_MODE);
25
+ } catch {
26
+ }
27
+ }
28
+ function ensurePrivateDirSync(dirPath) {
29
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
30
+ try {
31
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
32
+ } catch {
33
+ }
34
+ }
35
+ async function enforcePrivateFile(filePath) {
36
+ try {
37
+ await chmod(filePath, PRIVATE_FILE_MODE);
38
+ } catch {
39
+ }
40
+ }
41
+ function enforcePrivateFileSync(filePath) {
42
+ try {
43
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
44
+ } catch {
45
+ }
46
+ }
47
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
48
+ var init_secure_files = __esm({
49
+ "src/lib/secure-files.ts"() {
50
+ "use strict";
51
+ PRIVATE_DIR_MODE = 448;
52
+ PRIVATE_FILE_MODE = 384;
53
+ }
54
+ });
55
+
18
56
  // src/lib/config.ts
19
57
  var config_exports = {};
20
58
  __export(config_exports, {
@@ -31,8 +69,8 @@ __export(config_exports, {
31
69
  migrateConfig: () => migrateConfig,
32
70
  saveConfig: () => saveConfig
33
71
  });
34
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
35
- import { readFileSync, existsSync, renameSync } from "fs";
72
+ import { readFile, writeFile } from "fs/promises";
73
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
36
74
  import path from "path";
37
75
  import os from "os";
38
76
  function resolveDataDir() {
@@ -40,7 +78,7 @@ function resolveDataDir() {
40
78
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
41
79
  const newDir = path.join(os.homedir(), ".exe-os");
42
80
  const legacyDir = path.join(os.homedir(), ".exe-mem");
43
- if (!existsSync(newDir) && existsSync(legacyDir)) {
81
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
44
82
  try {
45
83
  renameSync(legacyDir, newDir);
46
84
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -103,9 +141,9 @@ function normalizeAutoUpdate(raw) {
103
141
  }
104
142
  async function loadConfig() {
105
143
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
106
- await mkdir(dir, { recursive: true });
144
+ await ensurePrivateDir(dir);
107
145
  const configPath = path.join(dir, "config.json");
108
- if (!existsSync(configPath)) {
146
+ if (!existsSync2(configPath)) {
109
147
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
110
148
  }
111
149
  const raw = await readFile(configPath, "utf-8");
@@ -118,6 +156,7 @@ async function loadConfig() {
118
156
  `);
119
157
  try {
120
158
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
159
+ await enforcePrivateFile(configPath);
121
160
  } catch {
122
161
  }
123
162
  }
@@ -136,7 +175,7 @@ async function loadConfig() {
136
175
  function loadConfigSync() {
137
176
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
138
177
  const configPath = path.join(dir, "config.json");
139
- if (!existsSync(configPath)) {
178
+ if (!existsSync2(configPath)) {
140
179
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
141
180
  }
142
181
  try {
@@ -154,12 +193,10 @@ function loadConfigSync() {
154
193
  }
155
194
  async function saveConfig(config) {
156
195
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
157
- await mkdir(dir, { recursive: true });
196
+ await ensurePrivateDir(dir);
158
197
  const configPath = path.join(dir, "config.json");
159
198
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
160
- if (config.cloud?.apiKey) {
161
- await chmod(configPath, 384);
162
- }
199
+ await enforcePrivateFile(configPath);
163
200
  }
164
201
  async function loadConfigFrom(configPath) {
165
202
  const raw = await readFile(configPath, "utf-8");
@@ -179,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
179
216
  var init_config = __esm({
180
217
  "src/lib/config.ts"() {
181
218
  "use strict";
219
+ init_secure_files();
182
220
  EXE_AI_DIR = resolveDataDir();
183
221
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
184
222
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -265,7 +303,7 @@ __export(keychain_exports, {
265
303
  setMasterKey: () => setMasterKey
266
304
  });
267
305
  import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
268
- import { existsSync as existsSync2 } from "fs";
306
+ import { existsSync as existsSync3 } from "fs";
269
307
  import path2 from "path";
270
308
  import os2 from "os";
271
309
  function getKeyDir() {
@@ -293,7 +331,7 @@ async function getMasterKey() {
293
331
  }
294
332
  }
295
333
  const keyPath = getKeyPath();
296
- if (!existsSync2(keyPath)) {
334
+ if (!existsSync3(keyPath)) {
297
335
  process.stderr.write(
298
336
  `[keychain] Key not found at ${keyPath} (HOME=${os2.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
299
337
  `
@@ -336,7 +374,7 @@ async function deleteMasterKey() {
336
374
  }
337
375
  }
338
376
  const keyPath = getKeyPath();
339
- if (existsSync2(keyPath)) {
377
+ if (existsSync3(keyPath)) {
340
378
  await unlink(keyPath);
341
379
  }
342
380
  }
@@ -387,13 +425,50 @@ var init_memory = __esm({
387
425
  }
388
426
  });
389
427
 
428
+ // src/lib/daemon-auth.ts
429
+ import crypto from "crypto";
430
+ import path4 from "path";
431
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync } from "fs";
432
+ function normalizeToken(token) {
433
+ if (!token) return null;
434
+ const trimmed = token.trim();
435
+ return trimmed.length > 0 ? trimmed : null;
436
+ }
437
+ function readDaemonToken() {
438
+ try {
439
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
440
+ return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
441
+ } catch {
442
+ return null;
443
+ }
444
+ }
445
+ function ensureDaemonToken(seed) {
446
+ const existing = readDaemonToken();
447
+ if (existing) return existing;
448
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
449
+ ensurePrivateDirSync(EXE_AI_DIR);
450
+ writeFileSync(DAEMON_TOKEN_PATH, `${token}
451
+ `, "utf8");
452
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
453
+ return token;
454
+ }
455
+ var DAEMON_TOKEN_PATH;
456
+ var init_daemon_auth = __esm({
457
+ "src/lib/daemon-auth.ts"() {
458
+ "use strict";
459
+ init_config();
460
+ init_secure_files();
461
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
462
+ }
463
+ });
464
+
390
465
  // src/lib/exe-daemon-client.ts
391
466
  import net from "net";
392
467
  import os3 from "os";
393
468
  import { spawn } from "child_process";
394
469
  import { randomUUID } from "crypto";
395
- import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
396
- import path4 from "path";
470
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
471
+ import path5 from "path";
397
472
  import { fileURLToPath } from "url";
398
473
  function handleData(chunk) {
399
474
  _buffer += chunk.toString();
@@ -421,9 +496,9 @@ function handleData(chunk) {
421
496
  }
422
497
  }
423
498
  function cleanupStaleFiles() {
424
- if (existsSync4(PID_PATH)) {
499
+ if (existsSync6(PID_PATH)) {
425
500
  try {
426
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
501
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
427
502
  if (pid > 0) {
428
503
  try {
429
504
  process.kill(pid, 0);
@@ -444,11 +519,11 @@ function cleanupStaleFiles() {
444
519
  }
445
520
  }
446
521
  function findPackageRoot() {
447
- let dir = path4.dirname(fileURLToPath(import.meta.url));
448
- const { root } = path4.parse(dir);
522
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
523
+ const { root } = path5.parse(dir);
449
524
  while (dir !== root) {
450
- if (existsSync4(path4.join(dir, "package.json"))) return dir;
451
- dir = path4.dirname(dir);
525
+ if (existsSync6(path5.join(dir, "package.json"))) return dir;
526
+ dir = path5.dirname(dir);
452
527
  }
453
528
  return null;
454
529
  }
@@ -474,16 +549,17 @@ function spawnDaemon() {
474
549
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
475
550
  return;
476
551
  }
477
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
478
- if (!existsSync4(daemonPath)) {
552
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
553
+ if (!existsSync6(daemonPath)) {
479
554
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
480
555
  `);
481
556
  return;
482
557
  }
483
558
  const resolvedPath = daemonPath;
559
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
484
560
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
485
561
  `);
486
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
562
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
487
563
  let stderrFd = "ignore";
488
564
  try {
489
565
  stderrFd = openSync(logPath, "a");
@@ -501,7 +577,8 @@ function spawnDaemon() {
501
577
  TMUX_PANE: void 0,
502
578
  // Prevents resolveExeSession() from scoping to one session
503
579
  EXE_DAEMON_SOCK: SOCKET_PATH,
504
- EXE_DAEMON_PID: PID_PATH
580
+ EXE_DAEMON_PID: PID_PATH,
581
+ [DAEMON_TOKEN_ENV]: daemonToken
505
582
  }
506
583
  });
507
584
  child.unref();
@@ -611,13 +688,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
611
688
  return;
612
689
  }
613
690
  const id = randomUUID();
691
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
614
692
  const timer = setTimeout(() => {
615
693
  _pending.delete(id);
616
694
  resolve({ error: "Request timeout" });
617
695
  }, timeoutMs);
618
696
  _pending.set(id, { resolve, timer });
619
697
  try {
620
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
698
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
621
699
  } catch {
622
700
  clearTimeout(timer);
623
701
  _pending.delete(id);
@@ -634,74 +712,123 @@ async function pingDaemon() {
634
712
  return null;
635
713
  }
636
714
  function killAndRespawnDaemon() {
637
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
638
- if (existsSync4(PID_PATH)) {
639
- try {
640
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
641
- if (pid > 0) {
642
- try {
643
- process.kill(pid, "SIGKILL");
644
- } catch {
715
+ if (!acquireSpawnLock()) {
716
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
717
+ if (_socket) {
718
+ _socket.destroy();
719
+ _socket = null;
720
+ }
721
+ _connected = false;
722
+ _buffer = "";
723
+ return;
724
+ }
725
+ try {
726
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
727
+ if (existsSync6(PID_PATH)) {
728
+ try {
729
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
730
+ if (pid > 0) {
731
+ try {
732
+ process.kill(pid, "SIGKILL");
733
+ } catch {
734
+ }
645
735
  }
736
+ } catch {
646
737
  }
738
+ }
739
+ if (_socket) {
740
+ _socket.destroy();
741
+ _socket = null;
742
+ }
743
+ _connected = false;
744
+ _buffer = "";
745
+ try {
746
+ unlinkSync2(PID_PATH);
647
747
  } catch {
648
748
  }
749
+ try {
750
+ unlinkSync2(SOCKET_PATH);
751
+ } catch {
752
+ }
753
+ spawnDaemon();
754
+ } finally {
755
+ releaseSpawnLock();
649
756
  }
650
- if (_socket) {
651
- _socket.destroy();
652
- _socket = null;
653
- }
654
- _connected = false;
655
- _buffer = "";
757
+ }
758
+ function isDaemonTooYoung() {
656
759
  try {
657
- unlinkSync2(PID_PATH);
760
+ const stat = statSync(PID_PATH);
761
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
658
762
  } catch {
763
+ return false;
659
764
  }
660
- try {
661
- unlinkSync2(SOCKET_PATH);
662
- } catch {
765
+ }
766
+ async function retryThenRestart(doRequest, label) {
767
+ const result = await doRequest();
768
+ if (!result.error) {
769
+ _consecutiveFailures = 0;
770
+ return result;
771
+ }
772
+ _consecutiveFailures++;
773
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
774
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
775
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
776
+ `);
777
+ await new Promise((r) => setTimeout(r, delayMs));
778
+ if (!_connected) {
779
+ if (!await connectToSocket()) continue;
780
+ }
781
+ const retry = await doRequest();
782
+ if (!retry.error) {
783
+ _consecutiveFailures = 0;
784
+ return retry;
785
+ }
786
+ _consecutiveFailures++;
787
+ }
788
+ if (isDaemonTooYoung()) {
789
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
790
+ `);
791
+ return { error: result.error };
663
792
  }
664
- spawnDaemon();
793
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
794
+ `);
795
+ killAndRespawnDaemon();
796
+ const start = Date.now();
797
+ let delay2 = 200;
798
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
799
+ await new Promise((r) => setTimeout(r, delay2));
800
+ if (await connectToSocket()) break;
801
+ delay2 = Math.min(delay2 * 2, 3e3);
802
+ }
803
+ if (!_connected) return { error: "Daemon restart failed" };
804
+ const final = await doRequest();
805
+ if (!final.error) _consecutiveFailures = 0;
806
+ return final;
665
807
  }
666
808
  async function embedViaClient(text, priority = "high") {
667
809
  if (!_connected && !await connectEmbedDaemon()) return null;
668
810
  _requestCount++;
669
811
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
670
812
  const health = await pingDaemon();
671
- if (!health) {
813
+ if (!health && !isDaemonTooYoung()) {
672
814
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
673
815
  `);
674
816
  killAndRespawnDaemon();
675
817
  const start = Date.now();
676
- let delay2 = 200;
818
+ let d = 200;
677
819
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
678
- await new Promise((r) => setTimeout(r, delay2));
820
+ await new Promise((r) => setTimeout(r, d));
679
821
  if (await connectToSocket()) break;
680
- delay2 = Math.min(delay2 * 2, 3e3);
822
+ d = Math.min(d * 2, 3e3);
681
823
  }
682
824
  if (!_connected) return null;
683
825
  }
684
826
  }
685
- const result = await sendRequest([text], priority);
686
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
687
- if (result.error) {
688
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
689
- `);
690
- killAndRespawnDaemon();
691
- const start = Date.now();
692
- let delay2 = 200;
693
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
694
- await new Promise((r) => setTimeout(r, delay2));
695
- if (await connectToSocket()) break;
696
- delay2 = Math.min(delay2 * 2, 3e3);
697
- }
698
- if (!_connected) return null;
699
- const retry = await sendRequest([text], priority);
700
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
701
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
702
- `);
703
- }
704
- return null;
827
+ const result = await retryThenRestart(
828
+ () => sendRequest([text], priority),
829
+ "Embed"
830
+ );
831
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
705
832
  }
706
833
  function disconnectClient() {
707
834
  if (_socket) {
@@ -719,22 +846,28 @@ function disconnectClient() {
719
846
  function isClientConnected() {
720
847
  return _connected;
721
848
  }
722
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
849
+ 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;
723
850
  var init_exe_daemon_client = __esm({
724
851
  "src/lib/exe-daemon-client.ts"() {
725
852
  "use strict";
726
853
  init_config();
727
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
728
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
729
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
854
+ init_daemon_auth();
855
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
856
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
857
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
730
858
  SPAWN_LOCK_STALE_MS = 3e4;
731
859
  CONNECT_TIMEOUT_MS = 15e3;
732
860
  REQUEST_TIMEOUT_MS = 3e4;
861
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
733
862
  _socket = null;
734
863
  _connected = false;
735
864
  _buffer = "";
736
865
  _requestCount = 0;
866
+ _consecutiveFailures = 0;
737
867
  HEALTH_CHECK_INTERVAL = 100;
868
+ MAX_RETRIES_BEFORE_RESTART = 3;
869
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
870
+ MIN_DAEMON_AGE_MS = 3e4;
738
871
  _pending = /* @__PURE__ */ new Map();
739
872
  MAX_BUFFER = 1e7;
740
873
  }
@@ -777,10 +910,10 @@ async function disposeEmbedder() {
777
910
  async function embedDirect(text) {
778
911
  const llamaCpp = await import("node-llama-cpp");
779
912
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
780
- const { existsSync: existsSync13 } = await import("fs");
781
- const path13 = await import("path");
782
- const modelPath = path13.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
783
- if (!existsSync13(modelPath)) {
913
+ const { existsSync: existsSync16 } = await import("fs");
914
+ const path16 = await import("path");
915
+ const modelPath = path16.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
916
+ if (!existsSync16(modelPath)) {
784
917
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
785
918
  }
786
919
  const llama = await llamaCpp.getLlama();
@@ -825,9 +958,12 @@ __export(license_exports, {
825
958
  stopLicenseRevalidation: () => stopLicenseRevalidation,
826
959
  validateLicense: () => validateLicense
827
960
  });
828
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync5, mkdirSync } from "fs";
961
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
829
962
  import { randomUUID as randomUUID2 } from "crypto";
830
- import path5 from "path";
963
+ import { createRequire } from "module";
964
+ import { pathToFileURL } from "url";
965
+ import os4 from "os";
966
+ import path6 from "path";
831
967
  import { jwtVerify, importSPKI } from "jose";
832
968
  async function fetchRetry(url, init) {
833
969
  try {
@@ -838,37 +974,37 @@ async function fetchRetry(url, init) {
838
974
  }
839
975
  }
840
976
  function loadDeviceId() {
841
- const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
977
+ const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
842
978
  try {
843
- if (existsSync5(deviceJsonPath)) {
844
- const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
979
+ if (existsSync7(deviceJsonPath)) {
980
+ const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
845
981
  if (data.deviceId) return data.deviceId;
846
982
  }
847
983
  } catch {
848
984
  }
849
985
  try {
850
- if (existsSync5(DEVICE_ID_PATH)) {
851
- const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
986
+ if (existsSync7(DEVICE_ID_PATH)) {
987
+ const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
852
988
  if (id2) return id2;
853
989
  }
854
990
  } catch {
855
991
  }
856
992
  const id = randomUUID2();
857
- mkdirSync(EXE_AI_DIR, { recursive: true });
858
- writeFileSync(DEVICE_ID_PATH, id, "utf8");
993
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
994
+ writeFileSync2(DEVICE_ID_PATH, id, "utf8");
859
995
  return id;
860
996
  }
861
997
  function loadLicense() {
862
998
  try {
863
- if (!existsSync5(LICENSE_PATH)) return null;
864
- return readFileSync3(LICENSE_PATH, "utf8").trim();
999
+ if (!existsSync7(LICENSE_PATH)) return null;
1000
+ return readFileSync4(LICENSE_PATH, "utf8").trim();
865
1001
  } catch {
866
1002
  return null;
867
1003
  }
868
1004
  }
869
1005
  function saveLicense(apiKey) {
870
- mkdirSync(EXE_AI_DIR, { recursive: true });
871
- writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
1006
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
1007
+ writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
872
1008
  }
873
1009
  async function verifyLicenseJwt(token) {
874
1010
  try {
@@ -894,8 +1030,8 @@ async function verifyLicenseJwt(token) {
894
1030
  }
895
1031
  async function getCachedLicense() {
896
1032
  try {
897
- if (!existsSync5(CACHE_PATH)) return null;
898
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1033
+ if (!existsSync7(CACHE_PATH)) return null;
1034
+ const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
899
1035
  if (!raw.token || typeof raw.token !== "string") return null;
900
1036
  return await verifyLicenseJwt(raw.token);
901
1037
  } catch {
@@ -904,8 +1040,8 @@ async function getCachedLicense() {
904
1040
  }
905
1041
  function readCachedToken() {
906
1042
  try {
907
- if (!existsSync5(CACHE_PATH)) return null;
908
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1043
+ if (!existsSync7(CACHE_PATH)) return null;
1044
+ const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
909
1045
  return typeof raw.token === "string" ? raw.token : null;
910
1046
  } catch {
911
1047
  return null;
@@ -939,56 +1075,130 @@ function getRawCachedPlan() {
939
1075
  }
940
1076
  function cacheResponse(token) {
941
1077
  try {
942
- writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
1078
+ writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
943
1079
  } catch {
944
1080
  }
945
1081
  }
946
- async function validateLicense(apiKey, deviceId) {
947
- const did = deviceId ?? loadDeviceId();
1082
+ function loadPrismaForLicense() {
1083
+ if (_prismaFailed) return null;
1084
+ const dbUrl = process.env.DATABASE_URL;
1085
+ if (!dbUrl) {
1086
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
1087
+ if (!existsSync7(path6.join(exeDbRoot, "package.json"))) {
1088
+ _prismaFailed = true;
1089
+ return null;
1090
+ }
1091
+ }
1092
+ if (!_prismaPromise) {
1093
+ _prismaPromise = (async () => {
1094
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1095
+ if (explicitPath) {
1096
+ const mod2 = await import(pathToFileURL(explicitPath).href);
1097
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
1098
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
1099
+ return new Ctor2();
1100
+ }
1101
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
1102
+ const req = createRequire(path6.join(exeDbRoot, "package.json"));
1103
+ const entry = req.resolve("@prisma/client");
1104
+ const mod = await import(pathToFileURL(entry).href);
1105
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
1106
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
1107
+ return new Ctor();
1108
+ })().catch((err) => {
1109
+ _prismaFailed = true;
1110
+ _prismaPromise = null;
1111
+ throw err;
1112
+ });
1113
+ }
1114
+ return _prismaPromise;
1115
+ }
1116
+ async function validateViaPostgres(apiKey) {
1117
+ const loader = loadPrismaForLicense();
1118
+ if (!loader) return null;
1119
+ try {
1120
+ const prisma = await loader;
1121
+ const rows = await prisma.$queryRawUnsafe(
1122
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
1123
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
1124
+ apiKey
1125
+ );
1126
+ if (!rows || rows.length === 0) return null;
1127
+ const row = rows[0];
1128
+ if (row.status !== "active") return null;
1129
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
1130
+ const plan = row.plan;
1131
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1132
+ return {
1133
+ valid: true,
1134
+ plan,
1135
+ email: row.email,
1136
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
1137
+ deviceLimit: row.device_limit ?? limits.devices,
1138
+ employeeLimit: row.employee_limit ?? limits.employees,
1139
+ memoryLimit: row.memory_limit ?? limits.memories
1140
+ };
1141
+ } catch {
1142
+ return null;
1143
+ }
1144
+ }
1145
+ async function validateViaCFWorker(apiKey, deviceId) {
948
1146
  try {
949
1147
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
950
1148
  method: "POST",
951
1149
  headers: { "Content-Type": "application/json" },
952
- body: JSON.stringify({ apiKey, deviceId: did }),
1150
+ body: JSON.stringify({ apiKey, deviceId }),
953
1151
  signal: AbortSignal.timeout(1e4)
954
1152
  });
955
- if (res.ok) {
956
- const data = await res.json();
957
- if (data.error === "device_limit_exceeded") {
958
- const cached2 = await getCachedLicense();
959
- if (cached2) return cached2;
960
- const raw2 = getRawCachedPlan();
961
- if (raw2) return { ...raw2, valid: false };
962
- return { ...FREE_LICENSE, valid: false, plan: "free" };
963
- }
964
- if (data.token) {
965
- cacheResponse(data.token);
966
- const verified = await verifyLicenseJwt(data.token);
967
- if (verified) return verified;
1153
+ if (!res.ok) return null;
1154
+ const data = await res.json();
1155
+ if (data.error === "device_limit_exceeded") return null;
1156
+ if (!data.valid) return null;
1157
+ if (data.token) {
1158
+ cacheResponse(data.token);
1159
+ const verified = await verifyLicenseJwt(data.token);
1160
+ if (verified) return verified;
1161
+ }
1162
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
1163
+ return {
1164
+ valid: data.valid,
1165
+ plan: data.plan,
1166
+ email: data.email,
1167
+ expiresAt: data.expiresAt,
1168
+ deviceLimit: limits.devices,
1169
+ employeeLimit: limits.employees,
1170
+ memoryLimit: limits.memories
1171
+ };
1172
+ } catch {
1173
+ return null;
1174
+ }
1175
+ }
1176
+ async function validateLicense(apiKey, deviceId) {
1177
+ const did = deviceId ?? loadDeviceId();
1178
+ const pgResult = await validateViaPostgres(apiKey);
1179
+ if (pgResult) {
1180
+ try {
1181
+ writeFileSync2(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
1182
+ } catch {
1183
+ }
1184
+ return pgResult;
1185
+ }
1186
+ const cfResult = await validateViaCFWorker(apiKey, did);
1187
+ if (cfResult) return cfResult;
1188
+ const cached = await getCachedLicense();
1189
+ if (cached) return cached;
1190
+ try {
1191
+ if (existsSync7(CACHE_PATH)) {
1192
+ const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
1193
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
1194
+ return raw.pgLicense;
968
1195
  }
969
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
970
- return {
971
- valid: data.valid,
972
- plan: data.plan,
973
- email: data.email,
974
- expiresAt: data.expiresAt,
975
- deviceLimit: limits.devices,
976
- employeeLimit: limits.employees,
977
- memoryLimit: limits.memories
978
- };
979
1196
  }
980
- const cached = await getCachedLicense();
981
- if (cached) return cached;
982
- const raw = getRawCachedPlan();
983
- if (raw) return raw;
984
- return { ...FREE_LICENSE, valid: false, plan: "free" };
985
1197
  } catch {
986
- const cached = await getCachedLicense();
987
- if (cached) return cached;
988
- const rawFallback = getRawCachedPlan();
989
- if (rawFallback) return rawFallback;
990
- return { ...FREE_LICENSE, valid: false, error: "offline" };
991
1198
  }
1199
+ const rawFallback = getRawCachedPlan();
1200
+ if (rawFallback) return rawFallback;
1201
+ return { ...FREE_LICENSE, valid: false };
992
1202
  }
993
1203
  function getCacheAgeMs() {
994
1204
  try {
@@ -1003,9 +1213,9 @@ async function checkLicense() {
1003
1213
  let key = loadLicense();
1004
1214
  if (!key) {
1005
1215
  try {
1006
- const configPath = path5.join(EXE_AI_DIR, "config.json");
1007
- if (existsSync5(configPath)) {
1008
- const raw = JSON.parse(readFileSync3(configPath, "utf8"));
1216
+ const configPath = path6.join(EXE_AI_DIR, "config.json");
1217
+ if (existsSync7(configPath)) {
1218
+ const raw = JSON.parse(readFileSync4(configPath, "utf8"));
1009
1219
  const cloud = raw.cloud;
1010
1220
  if (cloud?.apiKey) {
1011
1221
  key = cloud.apiKey;
@@ -1159,14 +1369,14 @@ function stopLicenseRevalidation() {
1159
1369
  _revalTimer = null;
1160
1370
  }
1161
1371
  }
1162
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
1372
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
1163
1373
  var init_license = __esm({
1164
1374
  "src/lib/license.ts"() {
1165
1375
  "use strict";
1166
1376
  init_config();
1167
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
1168
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
1169
- DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
1377
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1378
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1379
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
1170
1380
  API_BASE = "https://askexe.com/cloud";
1171
1381
  RETRY_DELAY_MS = 500;
1172
1382
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -1190,6 +1400,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1190
1400
  employeeLimit: 1,
1191
1401
  memoryLimit: 5e3
1192
1402
  };
1403
+ _prismaPromise = null;
1404
+ _prismaFailed = false;
1193
1405
  CACHE_MAX_AGE_MS = 36e5;
1194
1406
  _revalTimer = null;
1195
1407
  }
@@ -1203,13 +1415,13 @@ __export(crypto_exports, {
1203
1415
  initSyncCrypto: () => initSyncCrypto,
1204
1416
  isSyncCryptoInitialized: () => isSyncCryptoInitialized
1205
1417
  });
1206
- import crypto from "crypto";
1418
+ import crypto2 from "crypto";
1207
1419
  function initSyncCrypto(masterKey) {
1208
1420
  if (masterKey.length !== 32) {
1209
1421
  throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
1210
1422
  }
1211
1423
  _syncKey = Buffer.from(
1212
- crypto.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
1424
+ crypto2.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
1213
1425
  );
1214
1426
  }
1215
1427
  function isSyncCryptoInitialized() {
@@ -1223,8 +1435,8 @@ function requireSyncKey() {
1223
1435
  }
1224
1436
  function encryptSyncBlob(data) {
1225
1437
  const key = requireSyncKey();
1226
- const iv = crypto.randomBytes(IV_LENGTH);
1227
- const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
1438
+ const iv = crypto2.randomBytes(IV_LENGTH);
1439
+ const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
1228
1440
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
1229
1441
  const tag = cipher.getAuthTag();
1230
1442
  return Buffer.concat([iv, encrypted, tag]).toString("base64");
@@ -1238,7 +1450,7 @@ function decryptSyncBlob(ciphertext) {
1238
1450
  const iv = combined.subarray(0, IV_LENGTH);
1239
1451
  const tag = combined.subarray(combined.length - TAG_LENGTH);
1240
1452
  const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
1241
- const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
1453
+ const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
1242
1454
  decipher.setAuthTag(tag);
1243
1455
  return Buffer.concat([decipher.update(encrypted), decipher.final()]);
1244
1456
  }
@@ -1309,6 +1521,120 @@ var init_db_retry = __esm({
1309
1521
  }
1310
1522
  });
1311
1523
 
1524
+ // src/lib/runtime-table.ts
1525
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
1526
+ var init_runtime_table = __esm({
1527
+ "src/lib/runtime-table.ts"() {
1528
+ "use strict";
1529
+ RUNTIME_TABLE = {
1530
+ codex: {
1531
+ binary: "codex",
1532
+ launchMode: "interactive",
1533
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
1534
+ inlineFlag: "--no-alt-screen",
1535
+ apiKeyEnv: "OPENAI_API_KEY",
1536
+ defaultModel: "gpt-5.4"
1537
+ },
1538
+ opencode: {
1539
+ binary: "opencode",
1540
+ launchMode: "exec",
1541
+ autoApproveFlag: "--dangerously-skip-permissions",
1542
+ inlineFlag: "",
1543
+ apiKeyEnv: "ANTHROPIC_API_KEY",
1544
+ defaultModel: "anthropic/claude-sonnet-4-6"
1545
+ }
1546
+ };
1547
+ DEFAULT_RUNTIME = "claude";
1548
+ }
1549
+ });
1550
+
1551
+ // src/lib/agent-config.ts
1552
+ var agent_config_exports = {};
1553
+ __export(agent_config_exports, {
1554
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
1555
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
1556
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
1557
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
1558
+ clearAgentRuntime: () => clearAgentRuntime,
1559
+ getAgentRuntime: () => getAgentRuntime,
1560
+ loadAgentConfig: () => loadAgentConfig,
1561
+ saveAgentConfig: () => saveAgentConfig,
1562
+ setAgentRuntime: () => setAgentRuntime
1563
+ });
1564
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8 } from "fs";
1565
+ import path7 from "path";
1566
+ function loadAgentConfig() {
1567
+ if (!existsSync8(AGENT_CONFIG_PATH)) return {};
1568
+ try {
1569
+ return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
1570
+ } catch {
1571
+ return {};
1572
+ }
1573
+ }
1574
+ function saveAgentConfig(config) {
1575
+ const dir = path7.dirname(AGENT_CONFIG_PATH);
1576
+ ensurePrivateDirSync(dir);
1577
+ writeFileSync3(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
1578
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
1579
+ }
1580
+ function getAgentRuntime(agentId) {
1581
+ const config = loadAgentConfig();
1582
+ const entry = config[agentId];
1583
+ if (entry) return entry;
1584
+ const orgDefault = config["default"];
1585
+ if (orgDefault) return orgDefault;
1586
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
1587
+ }
1588
+ function setAgentRuntime(agentId, runtime, model) {
1589
+ const knownModels = KNOWN_RUNTIMES[runtime];
1590
+ if (!knownModels) {
1591
+ return {
1592
+ ok: false,
1593
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
1594
+ };
1595
+ }
1596
+ if (!knownModels.includes(model)) {
1597
+ return {
1598
+ ok: false,
1599
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
1600
+ };
1601
+ }
1602
+ const config = loadAgentConfig();
1603
+ config[agentId] = { runtime, model };
1604
+ saveAgentConfig(config);
1605
+ return { ok: true };
1606
+ }
1607
+ function clearAgentRuntime(agentId) {
1608
+ const config = loadAgentConfig();
1609
+ delete config[agentId];
1610
+ saveAgentConfig(config);
1611
+ }
1612
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
1613
+ var init_agent_config = __esm({
1614
+ "src/lib/agent-config.ts"() {
1615
+ "use strict";
1616
+ init_config();
1617
+ init_runtime_table();
1618
+ init_secure_files();
1619
+ AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
1620
+ KNOWN_RUNTIMES = {
1621
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
1622
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
1623
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
1624
+ };
1625
+ RUNTIME_LABELS = {
1626
+ claude: "Claude Code (Anthropic)",
1627
+ codex: "Codex (OpenAI)",
1628
+ opencode: "OpenCode (open source)"
1629
+ };
1630
+ DEFAULT_MODELS = {
1631
+ claude: "claude-opus-4",
1632
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
1633
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
1634
+ };
1635
+ }
1636
+ });
1637
+
1312
1638
  // src/lib/employees.ts
1313
1639
  var employees_exports = {};
1314
1640
  __export(employees_exports, {
@@ -1324,6 +1650,7 @@ __export(employees_exports, {
1324
1650
  getEmployeeByRole: () => getEmployeeByRole,
1325
1651
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
1326
1652
  hasRole: () => hasRole,
1653
+ hireEmployee: () => hireEmployee,
1327
1654
  isCoordinatorName: () => isCoordinatorName,
1328
1655
  isCoordinatorRole: () => isCoordinatorRole,
1329
1656
  isMultiInstance: () => isMultiInstance,
@@ -1336,10 +1663,10 @@ __export(employees_exports, {
1336
1663
  validateEmployeeName: () => validateEmployeeName
1337
1664
  });
1338
1665
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
1339
- import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "fs";
1666
+ import { existsSync as existsSync9, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
1340
1667
  import { execSync } from "child_process";
1341
- import path6 from "path";
1342
- import os4 from "os";
1668
+ import path8 from "path";
1669
+ import os5 from "os";
1343
1670
  function normalizeRole(role) {
1344
1671
  return (role ?? "").trim().toLowerCase();
1345
1672
  }
@@ -1375,7 +1702,7 @@ function validateEmployeeName(name) {
1375
1702
  return { valid: true };
1376
1703
  }
1377
1704
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1378
- if (!existsSync6(employeesPath)) {
1705
+ if (!existsSync9(employeesPath)) {
1379
1706
  return [];
1380
1707
  }
1381
1708
  const raw = await readFile3(employeesPath, "utf-8");
@@ -1386,13 +1713,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1386
1713
  }
1387
1714
  }
1388
1715
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1389
- await mkdir4(path6.dirname(employeesPath), { recursive: true });
1716
+ await mkdir4(path8.dirname(employeesPath), { recursive: true });
1390
1717
  await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1391
1718
  }
1392
1719
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
1393
- if (!existsSync6(employeesPath)) return [];
1720
+ if (!existsSync9(employeesPath)) return [];
1394
1721
  try {
1395
- return JSON.parse(readFileSync4(employeesPath, "utf-8"));
1722
+ return JSON.parse(readFileSync6(employeesPath, "utf-8"));
1396
1723
  } catch {
1397
1724
  return [];
1398
1725
  }
@@ -1434,6 +1761,52 @@ function addEmployee(employees, employee) {
1434
1761
  }
1435
1762
  return [...employees, normalized];
1436
1763
  }
1764
+ function appendToCoordinatorTeam(employee) {
1765
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
1766
+ if (!coordinator) return;
1767
+ const idPath = path8.join(IDENTITY_DIR, `${coordinator.name}.md`);
1768
+ if (!existsSync9(idPath)) return;
1769
+ const content = readFileSync6(idPath, "utf-8");
1770
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
1771
+ const teamMatch = content.match(TEAM_SECTION_RE);
1772
+ if (!teamMatch || teamMatch.index === void 0) return;
1773
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
1774
+ const nextHeading = afterTeam.match(/\n## /);
1775
+ const entry = `
1776
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
1777
+ `;
1778
+ let updated;
1779
+ if (nextHeading && nextHeading.index !== void 0) {
1780
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
1781
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
1782
+ } else {
1783
+ updated = content.trimEnd() + "\n" + entry;
1784
+ }
1785
+ writeFileSync4(idPath, updated, "utf-8");
1786
+ }
1787
+ function capitalize(s) {
1788
+ return s.charAt(0).toUpperCase() + s.slice(1);
1789
+ }
1790
+ async function hireEmployee(employee) {
1791
+ const employees = await loadEmployees();
1792
+ const updated = addEmployee(employees, employee);
1793
+ await saveEmployees(updated);
1794
+ try {
1795
+ appendToCoordinatorTeam(employee);
1796
+ } catch {
1797
+ }
1798
+ try {
1799
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
1800
+ const config = loadAgentConfig2();
1801
+ const name = employee.name.toLowerCase();
1802
+ if (!config[name] && config["default"]) {
1803
+ config[name] = { ...config["default"] };
1804
+ saveAgentConfig2(config);
1805
+ }
1806
+ } catch {
1807
+ }
1808
+ return updated;
1809
+ }
1437
1810
  async function normalizeRosterCase(rosterPath) {
1438
1811
  const employees = await loadEmployees(rosterPath);
1439
1812
  let changed = false;
@@ -1443,14 +1816,14 @@ async function normalizeRosterCase(rosterPath) {
1443
1816
  emp.name = emp.name.toLowerCase();
1444
1817
  changed = true;
1445
1818
  try {
1446
- const identityDir = path6.join(os4.homedir(), ".exe-os", "identity");
1447
- const oldPath = path6.join(identityDir, `${oldName}.md`);
1448
- const newPath = path6.join(identityDir, `${emp.name}.md`);
1449
- if (existsSync6(oldPath) && !existsSync6(newPath)) {
1819
+ const identityDir = path8.join(os5.homedir(), ".exe-os", "identity");
1820
+ const oldPath = path8.join(identityDir, `${oldName}.md`);
1821
+ const newPath = path8.join(identityDir, `${emp.name}.md`);
1822
+ if (existsSync9(oldPath) && !existsSync9(newPath)) {
1450
1823
  renameSync3(oldPath, newPath);
1451
- } else if (existsSync6(oldPath) && oldPath !== newPath) {
1452
- const content = readFileSync4(oldPath, "utf-8");
1453
- writeFileSync2(newPath, content, "utf-8");
1824
+ } else if (existsSync9(oldPath) && oldPath !== newPath) {
1825
+ const content = readFileSync6(oldPath, "utf-8");
1826
+ writeFileSync4(newPath, content, "utf-8");
1454
1827
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
1455
1828
  unlinkSync3(oldPath);
1456
1829
  }
@@ -1480,7 +1853,7 @@ function registerBinSymlinks(name) {
1480
1853
  errors.push("Could not find 'exe-os' in PATH");
1481
1854
  return { created, skipped, errors };
1482
1855
  }
1483
- const binDir = path6.dirname(exeBinPath);
1856
+ const binDir = path8.dirname(exeBinPath);
1484
1857
  let target;
1485
1858
  try {
1486
1859
  target = readlinkSync(exeBinPath);
@@ -1490,8 +1863,8 @@ function registerBinSymlinks(name) {
1490
1863
  }
1491
1864
  for (const suffix of ["", "-opencode"]) {
1492
1865
  const linkName = `${name}${suffix}`;
1493
- const linkPath = path6.join(binDir, linkName);
1494
- if (existsSync6(linkPath)) {
1866
+ const linkPath = path8.join(binDir, linkName);
1867
+ if (existsSync9(linkPath)) {
1495
1868
  skipped.push(linkName);
1496
1869
  continue;
1497
1870
  }
@@ -1504,15 +1877,601 @@ function registerBinSymlinks(name) {
1504
1877
  }
1505
1878
  return { created, skipped, errors };
1506
1879
  }
1507
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
1880
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
1508
1881
  var init_employees = __esm({
1509
1882
  "src/lib/employees.ts"() {
1510
1883
  "use strict";
1511
1884
  init_config();
1512
- EMPLOYEES_PATH = path6.join(EXE_AI_DIR, "exe-employees.json");
1885
+ EMPLOYEES_PATH = path8.join(EXE_AI_DIR, "exe-employees.json");
1513
1886
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
1514
1887
  COORDINATOR_ROLE = "COO";
1515
1888
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
1889
+ IDENTITY_DIR = path8.join(EXE_AI_DIR, "identity");
1890
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
1891
+ }
1892
+ });
1893
+
1894
+ // src/lib/database-adapter.ts
1895
+ import os6 from "os";
1896
+ import path9 from "path";
1897
+ import { createRequire as createRequire2 } from "module";
1898
+ import { pathToFileURL as pathToFileURL2 } from "url";
1899
+ function quotedIdentifier(identifier) {
1900
+ return `"${identifier.replace(/"/g, '""')}"`;
1901
+ }
1902
+ function unqualifiedTableName(name) {
1903
+ const raw = name.trim().replace(/^"|"$/g, "");
1904
+ const parts = raw.split(".");
1905
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
1906
+ }
1907
+ function stripTrailingSemicolon(sql) {
1908
+ return sql.trim().replace(/;+\s*$/u, "");
1909
+ }
1910
+ function appendClause(sql, clause) {
1911
+ const trimmed = stripTrailingSemicolon(sql);
1912
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
1913
+ if (!returningMatch) {
1914
+ return `${trimmed}${clause}`;
1915
+ }
1916
+ const idx = returningMatch.index;
1917
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
1918
+ }
1919
+ function normalizeStatement(stmt) {
1920
+ if (typeof stmt === "string") {
1921
+ return { kind: "positional", sql: stmt, args: [] };
1922
+ }
1923
+ const sql = stmt.sql;
1924
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
1925
+ return { kind: "positional", sql, args: stmt.args ?? [] };
1926
+ }
1927
+ return { kind: "named", sql, args: stmt.args };
1928
+ }
1929
+ function rewriteBooleanLiterals(sql) {
1930
+ let out = sql;
1931
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1932
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
1933
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
1934
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
1935
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
1936
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
1937
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
1938
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
1939
+ }
1940
+ return out;
1941
+ }
1942
+ function rewriteInsertOrIgnore(sql) {
1943
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
1944
+ return sql;
1945
+ }
1946
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
1947
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
1948
+ }
1949
+ function rewriteInsertOrReplace(sql) {
1950
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
1951
+ if (!match) {
1952
+ return sql;
1953
+ }
1954
+ const rawTable = match[1];
1955
+ const rawColumns = match[2];
1956
+ const remainder = match[3];
1957
+ const tableName = unqualifiedTableName(rawTable);
1958
+ const conflictKeys = UPSERT_KEYS[tableName];
1959
+ if (!conflictKeys?.length) {
1960
+ return sql;
1961
+ }
1962
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1963
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
1964
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
1965
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
1966
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
1967
+ }
1968
+ function rewriteSql(sql) {
1969
+ let out = sql;
1970
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
1971
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
1972
+ out = rewriteBooleanLiterals(out);
1973
+ out = rewriteInsertOrReplace(out);
1974
+ out = rewriteInsertOrIgnore(out);
1975
+ return stripTrailingSemicolon(out);
1976
+ }
1977
+ function toBoolean(value) {
1978
+ if (value === null || value === void 0) return value;
1979
+ if (typeof value === "boolean") return value;
1980
+ if (typeof value === "number") return value !== 0;
1981
+ if (typeof value === "bigint") return value !== 0n;
1982
+ if (typeof value === "string") {
1983
+ const normalized = value.trim().toLowerCase();
1984
+ if (normalized === "0" || normalized === "false") return false;
1985
+ if (normalized === "1" || normalized === "true") return true;
1986
+ }
1987
+ return Boolean(value);
1988
+ }
1989
+ function countQuestionMarks(sql, end) {
1990
+ let count = 0;
1991
+ let inSingle = false;
1992
+ let inDouble = false;
1993
+ let inLineComment = false;
1994
+ let inBlockComment = false;
1995
+ for (let i = 0; i < end; i++) {
1996
+ const ch = sql[i];
1997
+ const next = sql[i + 1];
1998
+ if (inLineComment) {
1999
+ if (ch === "\n") inLineComment = false;
2000
+ continue;
2001
+ }
2002
+ if (inBlockComment) {
2003
+ if (ch === "*" && next === "/") {
2004
+ inBlockComment = false;
2005
+ i += 1;
2006
+ }
2007
+ continue;
2008
+ }
2009
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
2010
+ inLineComment = true;
2011
+ i += 1;
2012
+ continue;
2013
+ }
2014
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
2015
+ inBlockComment = true;
2016
+ i += 1;
2017
+ continue;
2018
+ }
2019
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
2020
+ inSingle = !inSingle;
2021
+ continue;
2022
+ }
2023
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
2024
+ inDouble = !inDouble;
2025
+ continue;
2026
+ }
2027
+ if (!inSingle && !inDouble && ch === "?") {
2028
+ count += 1;
2029
+ }
2030
+ }
2031
+ return count;
2032
+ }
2033
+ function findBooleanPlaceholderIndexes(sql) {
2034
+ const indexes = /* @__PURE__ */ new Set();
2035
+ for (const column of BOOLEAN_COLUMN_NAMES) {
2036
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
2037
+ for (const match of sql.matchAll(pattern)) {
2038
+ const matchText = match[0];
2039
+ const qIndex = match.index + matchText.lastIndexOf("?");
2040
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
2041
+ }
2042
+ }
2043
+ return indexes;
2044
+ }
2045
+ function coerceInsertBooleanArgs(sql, args2) {
2046
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
2047
+ if (!match) return;
2048
+ const rawTable = match[1];
2049
+ const rawColumns = match[2];
2050
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
2051
+ if (!boolColumns?.size) return;
2052
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
2053
+ for (const [index, column] of columns.entries()) {
2054
+ if (boolColumns.has(column) && index < args2.length) {
2055
+ args2[index] = toBoolean(args2[index]);
2056
+ }
2057
+ }
2058
+ }
2059
+ function coerceUpdateBooleanArgs(sql, args2) {
2060
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
2061
+ if (!match) return;
2062
+ const rawTable = match[1];
2063
+ const setClause = match[2];
2064
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
2065
+ if (!boolColumns?.size) return;
2066
+ const assignments = setClause.split(",");
2067
+ let placeholderIndex = 0;
2068
+ for (const assignment of assignments) {
2069
+ if (!assignment.includes("?")) continue;
2070
+ placeholderIndex += 1;
2071
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
2072
+ if (colMatch && boolColumns.has(colMatch[1])) {
2073
+ args2[placeholderIndex - 1] = toBoolean(args2[placeholderIndex - 1]);
2074
+ }
2075
+ }
2076
+ }
2077
+ function coerceBooleanArgs(sql, args2) {
2078
+ const nextArgs = [...args2];
2079
+ coerceInsertBooleanArgs(sql, nextArgs);
2080
+ coerceUpdateBooleanArgs(sql, nextArgs);
2081
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
2082
+ for (const index of placeholderIndexes) {
2083
+ if (index > 0 && index <= nextArgs.length) {
2084
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
2085
+ }
2086
+ }
2087
+ return nextArgs;
2088
+ }
2089
+ function convertQuestionMarksToDollarParams(sql) {
2090
+ let out = "";
2091
+ let placeholder = 0;
2092
+ let inSingle = false;
2093
+ let inDouble = false;
2094
+ let inLineComment = false;
2095
+ let inBlockComment = false;
2096
+ for (let i = 0; i < sql.length; i++) {
2097
+ const ch = sql[i];
2098
+ const next = sql[i + 1];
2099
+ if (inLineComment) {
2100
+ out += ch;
2101
+ if (ch === "\n") inLineComment = false;
2102
+ continue;
2103
+ }
2104
+ if (inBlockComment) {
2105
+ out += ch;
2106
+ if (ch === "*" && next === "/") {
2107
+ out += next;
2108
+ inBlockComment = false;
2109
+ i += 1;
2110
+ }
2111
+ continue;
2112
+ }
2113
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
2114
+ out += ch + next;
2115
+ inLineComment = true;
2116
+ i += 1;
2117
+ continue;
2118
+ }
2119
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
2120
+ out += ch + next;
2121
+ inBlockComment = true;
2122
+ i += 1;
2123
+ continue;
2124
+ }
2125
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
2126
+ inSingle = !inSingle;
2127
+ out += ch;
2128
+ continue;
2129
+ }
2130
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
2131
+ inDouble = !inDouble;
2132
+ out += ch;
2133
+ continue;
2134
+ }
2135
+ if (!inSingle && !inDouble && ch === "?") {
2136
+ placeholder += 1;
2137
+ out += `$${placeholder}`;
2138
+ continue;
2139
+ }
2140
+ out += ch;
2141
+ }
2142
+ return out;
2143
+ }
2144
+ function translateStatementForPostgres(stmt) {
2145
+ const normalized = normalizeStatement(stmt);
2146
+ if (normalized.kind === "named") {
2147
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
2148
+ }
2149
+ const rewrittenSql = rewriteSql(normalized.sql);
2150
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
2151
+ return {
2152
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
2153
+ args: coercedArgs
2154
+ };
2155
+ }
2156
+ function shouldBypassPostgres(stmt) {
2157
+ const normalized = normalizeStatement(stmt);
2158
+ if (normalized.kind === "named") {
2159
+ return true;
2160
+ }
2161
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
2162
+ }
2163
+ function shouldFallbackOnError(error) {
2164
+ const message = error instanceof Error ? error.message : String(error);
2165
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
2166
+ }
2167
+ function isReadQuery(sql) {
2168
+ const trimmed = sql.trimStart();
2169
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
2170
+ }
2171
+ function buildRow(row, columns) {
2172
+ const values = columns.map((column) => row[column]);
2173
+ return Object.assign(values, row);
2174
+ }
2175
+ function buildResultSet(rows, rowsAffected = 0) {
2176
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
2177
+ const resultRows = rows.map((row) => buildRow(row, columns));
2178
+ return {
2179
+ columns,
2180
+ columnTypes: columns.map(() => ""),
2181
+ rows: resultRows,
2182
+ rowsAffected,
2183
+ lastInsertRowid: void 0,
2184
+ toJSON() {
2185
+ return {
2186
+ columns,
2187
+ columnTypes: columns.map(() => ""),
2188
+ rows,
2189
+ rowsAffected,
2190
+ lastInsertRowid: void 0
2191
+ };
2192
+ }
2193
+ };
2194
+ }
2195
+ async function loadPrismaClient() {
2196
+ if (!prismaClientPromise) {
2197
+ prismaClientPromise = (async () => {
2198
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
2199
+ if (explicitPath) {
2200
+ const module2 = await import(pathToFileURL2(explicitPath).href);
2201
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
2202
+ if (!PrismaClient2) {
2203
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
2204
+ }
2205
+ return new PrismaClient2();
2206
+ }
2207
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os6.homedir(), "exe-db");
2208
+ const requireFromExeDb = createRequire2(path9.join(exeDbRoot, "package.json"));
2209
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
2210
+ const module = await import(pathToFileURL2(prismaEntry).href);
2211
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
2212
+ if (!PrismaClient) {
2213
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
2214
+ }
2215
+ return new PrismaClient();
2216
+ })();
2217
+ }
2218
+ return prismaClientPromise;
2219
+ }
2220
+ async function ensureCompatibilityViews(prisma) {
2221
+ if (!compatibilityBootstrapPromise) {
2222
+ compatibilityBootstrapPromise = (async () => {
2223
+ for (const mapping of VIEW_MAPPINGS) {
2224
+ const relation = mapping.source.replace(/"/g, "");
2225
+ const rows = await prisma.$queryRawUnsafe(
2226
+ "SELECT to_regclass($1) AS regclass",
2227
+ relation
2228
+ );
2229
+ if (!rows[0]?.regclass) {
2230
+ continue;
2231
+ }
2232
+ await prisma.$executeRawUnsafe(
2233
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
2234
+ );
2235
+ }
2236
+ })();
2237
+ }
2238
+ return compatibilityBootstrapPromise;
2239
+ }
2240
+ async function executeOnPrisma(executor, stmt) {
2241
+ const translated = translateStatementForPostgres(stmt);
2242
+ if (isReadQuery(translated.sql)) {
2243
+ const rows = await executor.$queryRawUnsafe(
2244
+ translated.sql,
2245
+ ...translated.args
2246
+ );
2247
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
2248
+ }
2249
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
2250
+ return buildResultSet([], rowsAffected);
2251
+ }
2252
+ function splitSqlStatements(sql) {
2253
+ const parts = [];
2254
+ let current = "";
2255
+ let inSingle = false;
2256
+ let inDouble = false;
2257
+ let inLineComment = false;
2258
+ let inBlockComment = false;
2259
+ for (let i = 0; i < sql.length; i++) {
2260
+ const ch = sql[i];
2261
+ const next = sql[i + 1];
2262
+ if (inLineComment) {
2263
+ current += ch;
2264
+ if (ch === "\n") inLineComment = false;
2265
+ continue;
2266
+ }
2267
+ if (inBlockComment) {
2268
+ current += ch;
2269
+ if (ch === "*" && next === "/") {
2270
+ current += next;
2271
+ inBlockComment = false;
2272
+ i += 1;
2273
+ }
2274
+ continue;
2275
+ }
2276
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
2277
+ current += ch + next;
2278
+ inLineComment = true;
2279
+ i += 1;
2280
+ continue;
2281
+ }
2282
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
2283
+ current += ch + next;
2284
+ inBlockComment = true;
2285
+ i += 1;
2286
+ continue;
2287
+ }
2288
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
2289
+ inSingle = !inSingle;
2290
+ current += ch;
2291
+ continue;
2292
+ }
2293
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
2294
+ inDouble = !inDouble;
2295
+ current += ch;
2296
+ continue;
2297
+ }
2298
+ if (!inSingle && !inDouble && ch === ";") {
2299
+ if (current.trim()) {
2300
+ parts.push(current.trim());
2301
+ }
2302
+ current = "";
2303
+ continue;
2304
+ }
2305
+ current += ch;
2306
+ }
2307
+ if (current.trim()) {
2308
+ parts.push(current.trim());
2309
+ }
2310
+ return parts;
2311
+ }
2312
+ async function createPrismaDbAdapter(fallbackClient) {
2313
+ const prisma = await loadPrismaClient();
2314
+ await ensureCompatibilityViews(prisma);
2315
+ let closed = false;
2316
+ let adapter;
2317
+ const fallbackExecute = async (stmt, error) => {
2318
+ if (!fallbackClient) {
2319
+ if (error) throw error;
2320
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
2321
+ }
2322
+ if (error) {
2323
+ process.stderr.write(
2324
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
2325
+ `
2326
+ );
2327
+ }
2328
+ return fallbackClient.execute(stmt);
2329
+ };
2330
+ adapter = {
2331
+ async execute(stmt) {
2332
+ if (shouldBypassPostgres(stmt)) {
2333
+ return fallbackExecute(stmt);
2334
+ }
2335
+ try {
2336
+ return await executeOnPrisma(prisma, stmt);
2337
+ } catch (error) {
2338
+ if (shouldFallbackOnError(error)) {
2339
+ return fallbackExecute(stmt, error);
2340
+ }
2341
+ throw error;
2342
+ }
2343
+ },
2344
+ async batch(stmts, mode) {
2345
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
2346
+ if (!fallbackClient) {
2347
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
2348
+ }
2349
+ return fallbackClient.batch(stmts, mode);
2350
+ }
2351
+ try {
2352
+ if (prisma.$transaction) {
2353
+ return await prisma.$transaction(async (tx) => {
2354
+ const results2 = [];
2355
+ for (const stmt of stmts) {
2356
+ results2.push(await executeOnPrisma(tx, stmt));
2357
+ }
2358
+ return results2;
2359
+ });
2360
+ }
2361
+ const results = [];
2362
+ for (const stmt of stmts) {
2363
+ results.push(await executeOnPrisma(prisma, stmt));
2364
+ }
2365
+ return results;
2366
+ } catch (error) {
2367
+ if (fallbackClient && shouldFallbackOnError(error)) {
2368
+ process.stderr.write(
2369
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
2370
+ `
2371
+ );
2372
+ return fallbackClient.batch(stmts, mode);
2373
+ }
2374
+ throw error;
2375
+ }
2376
+ },
2377
+ async migrate(stmts) {
2378
+ if (fallbackClient) {
2379
+ return fallbackClient.migrate(stmts);
2380
+ }
2381
+ return adapter.batch(stmts, "deferred");
2382
+ },
2383
+ async transaction(mode) {
2384
+ if (!fallbackClient) {
2385
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
2386
+ }
2387
+ return fallbackClient.transaction(mode);
2388
+ },
2389
+ async executeMultiple(sql) {
2390
+ if (fallbackClient && shouldBypassPostgres(sql)) {
2391
+ return fallbackClient.executeMultiple(sql);
2392
+ }
2393
+ for (const statement of splitSqlStatements(sql)) {
2394
+ await adapter.execute(statement);
2395
+ }
2396
+ },
2397
+ async sync() {
2398
+ if (fallbackClient) {
2399
+ return fallbackClient.sync();
2400
+ }
2401
+ return { frame_no: 0, frames_synced: 0 };
2402
+ },
2403
+ close() {
2404
+ closed = true;
2405
+ prismaClientPromise = null;
2406
+ compatibilityBootstrapPromise = null;
2407
+ void prisma.$disconnect?.();
2408
+ },
2409
+ get closed() {
2410
+ return closed;
2411
+ },
2412
+ get protocol() {
2413
+ return "prisma-postgres";
2414
+ }
2415
+ };
2416
+ return adapter;
2417
+ }
2418
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
2419
+ var init_database_adapter = __esm({
2420
+ "src/lib/database-adapter.ts"() {
2421
+ "use strict";
2422
+ VIEW_MAPPINGS = [
2423
+ { view: "memories", source: "memory.memory_records" },
2424
+ { view: "tasks", source: "memory.tasks" },
2425
+ { view: "behaviors", source: "memory.behaviors" },
2426
+ { view: "entities", source: "memory.entities" },
2427
+ { view: "relationships", source: "memory.relationships" },
2428
+ { view: "entity_memories", source: "memory.entity_memories" },
2429
+ { view: "entity_aliases", source: "memory.entity_aliases" },
2430
+ { view: "notifications", source: "memory.notifications" },
2431
+ { view: "messages", source: "memory.messages" },
2432
+ { view: "users", source: "wiki.users" },
2433
+ { view: "workspaces", source: "wiki.workspaces" },
2434
+ { view: "workspace_users", source: "wiki.workspace_users" },
2435
+ { view: "documents", source: "wiki.workspace_documents" },
2436
+ { view: "chats", source: "wiki.workspace_chats" }
2437
+ ];
2438
+ UPSERT_KEYS = {
2439
+ memories: ["id"],
2440
+ tasks: ["id"],
2441
+ behaviors: ["id"],
2442
+ entities: ["id"],
2443
+ relationships: ["id"],
2444
+ entity_aliases: ["alias"],
2445
+ notifications: ["id"],
2446
+ messages: ["id"],
2447
+ users: ["id"],
2448
+ workspaces: ["id"],
2449
+ workspace_users: ["id"],
2450
+ documents: ["id"],
2451
+ chats: ["id"]
2452
+ };
2453
+ BOOLEAN_COLUMNS_BY_TABLE = {
2454
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
2455
+ behaviors: /* @__PURE__ */ new Set(["active"]),
2456
+ notifications: /* @__PURE__ */ new Set(["read"]),
2457
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
2458
+ };
2459
+ BOOLEAN_COLUMN_NAMES = new Set(
2460
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
2461
+ );
2462
+ IMMEDIATE_FALLBACK_PATTERNS = [
2463
+ /\bPRAGMA\b/i,
2464
+ /\bsqlite_master\b/i,
2465
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
2466
+ /\bMATCH\b/i,
2467
+ /\bvector_distance_cos\s*\(/i,
2468
+ /\bjson_extract\s*\(/i,
2469
+ /\bjulianday\s*\(/i,
2470
+ /\bstrftime\s*\(/i,
2471
+ /\blast_insert_rowid\s*\(/i
2472
+ ];
2473
+ prismaClientPromise = null;
2474
+ compatibilityBootstrapPromise = null;
1516
2475
  }
1517
2476
  });
1518
2477
 
@@ -1586,7 +2545,7 @@ __export(db_daemon_client_exports, {
1586
2545
  createDaemonDbClient: () => createDaemonDbClient,
1587
2546
  initDaemonDbClient: () => initDaemonDbClient
1588
2547
  });
1589
- function normalizeStatement(stmt) {
2548
+ function normalizeStatement2(stmt) {
1590
2549
  if (typeof stmt === "string") {
1591
2550
  return { sql: stmt, args: [] };
1592
2551
  }
@@ -1610,7 +2569,7 @@ function createDaemonDbClient(fallbackClient) {
1610
2569
  if (!_useDaemon || !isClientConnected()) {
1611
2570
  return fallbackClient.execute(stmt);
1612
2571
  }
1613
- const { sql, args: args2 } = normalizeStatement(stmt);
2572
+ const { sql, args: args2 } = normalizeStatement2(stmt);
1614
2573
  const response = await sendDaemonRequest({
1615
2574
  type: "db-execute",
1616
2575
  sql,
@@ -1635,7 +2594,7 @@ function createDaemonDbClient(fallbackClient) {
1635
2594
  if (!_useDaemon || !isClientConnected()) {
1636
2595
  return fallbackClient.batch(stmts, mode);
1637
2596
  }
1638
- const statements = stmts.map(normalizeStatement);
2597
+ const statements = stmts.map(normalizeStatement2);
1639
2598
  const response = await sendDaemonRequest({
1640
2599
  type: "db-batch",
1641
2600
  statements,
@@ -1730,6 +2689,18 @@ __export(database_exports, {
1730
2689
  });
1731
2690
  import { createClient } from "@libsql/client";
1732
2691
  async function initDatabase(config) {
2692
+ if (_walCheckpointTimer) {
2693
+ clearInterval(_walCheckpointTimer);
2694
+ _walCheckpointTimer = null;
2695
+ }
2696
+ if (_daemonClient) {
2697
+ _daemonClient.close();
2698
+ _daemonClient = null;
2699
+ }
2700
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2701
+ _adapterClient.close();
2702
+ }
2703
+ _adapterClient = null;
1733
2704
  if (_client) {
1734
2705
  _client.close();
1735
2706
  _client = null;
@@ -1743,6 +2714,7 @@ async function initDatabase(config) {
1743
2714
  }
1744
2715
  _client = createClient(opts);
1745
2716
  _resilientClient = wrapWithRetry(_client);
2717
+ _adapterClient = _resilientClient;
1746
2718
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1747
2719
  });
1748
2720
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1753,14 +2725,20 @@ async function initDatabase(config) {
1753
2725
  });
1754
2726
  }, 3e4);
1755
2727
  _walCheckpointTimer.unref();
2728
+ if (process.env.DATABASE_URL) {
2729
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
2730
+ }
1756
2731
  }
1757
2732
  function isInitialized() {
1758
- return _client !== null;
2733
+ return _adapterClient !== null || _client !== null;
1759
2734
  }
1760
2735
  function getClient() {
1761
- if (!_resilientClient) {
2736
+ if (!_adapterClient) {
1762
2737
  throw new Error("Database client not initialized. Call initDatabase() first.");
1763
2738
  }
2739
+ if (process.env.DATABASE_URL) {
2740
+ return _adapterClient;
2741
+ }
1764
2742
  if (process.env.EXE_IS_DAEMON === "1") {
1765
2743
  return _resilientClient;
1766
2744
  }
@@ -1770,6 +2748,7 @@ function getClient() {
1770
2748
  return _resilientClient;
1771
2749
  }
1772
2750
  async function initDaemonClient() {
2751
+ if (process.env.DATABASE_URL) return;
1773
2752
  if (process.env.EXE_IS_DAEMON === "1") return;
1774
2753
  if (!_resilientClient) return;
1775
2754
  try {
@@ -2066,6 +3045,7 @@ async function ensureSchema() {
2066
3045
  project TEXT NOT NULL,
2067
3046
  summary TEXT NOT NULL,
2068
3047
  task_file TEXT,
3048
+ session_scope TEXT,
2069
3049
  read INTEGER NOT NULL DEFAULT 0,
2070
3050
  created_at TEXT NOT NULL
2071
3051
  );
@@ -2074,7 +3054,7 @@ async function ensureSchema() {
2074
3054
  ON notifications(read);
2075
3055
 
2076
3056
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
2077
- ON notifications(agent_id);
3057
+ ON notifications(agent_id, session_scope);
2078
3058
 
2079
3059
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
2080
3060
  ON notifications(task_file);
@@ -2112,6 +3092,7 @@ async function ensureSchema() {
2112
3092
  target_agent TEXT NOT NULL,
2113
3093
  target_project TEXT,
2114
3094
  target_device TEXT NOT NULL DEFAULT 'local',
3095
+ session_scope TEXT,
2115
3096
  content TEXT NOT NULL,
2116
3097
  priority TEXT DEFAULT 'normal',
2117
3098
  status TEXT DEFAULT 'pending',
@@ -2125,10 +3106,31 @@ async function ensureSchema() {
2125
3106
  );
2126
3107
 
2127
3108
  CREATE INDEX IF NOT EXISTS idx_messages_target
2128
- ON messages(target_agent, status);
3109
+ ON messages(target_agent, session_scope, status);
2129
3110
 
2130
3111
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
2131
- ON messages(target_agent, from_agent, server_seq);
3112
+ ON messages(target_agent, session_scope, from_agent, server_seq);
3113
+ `);
3114
+ try {
3115
+ await client.execute({
3116
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
3117
+ args: []
3118
+ });
3119
+ } catch {
3120
+ }
3121
+ try {
3122
+ await client.execute({
3123
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
3124
+ args: []
3125
+ });
3126
+ } catch {
3127
+ }
3128
+ await client.executeMultiple(`
3129
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
3130
+ ON notifications(agent_id, session_scope, read, created_at);
3131
+
3132
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
3133
+ ON messages(target_agent, session_scope, status, created_at);
2132
3134
  `);
2133
3135
  try {
2134
3136
  await client.execute({
@@ -2712,28 +3714,45 @@ async function ensureSchema() {
2712
3714
  } catch {
2713
3715
  }
2714
3716
  }
3717
+ try {
3718
+ await client.execute({
3719
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
3720
+ args: []
3721
+ });
3722
+ } catch {
3723
+ }
2715
3724
  }
2716
3725
  async function disposeDatabase() {
3726
+ if (_walCheckpointTimer) {
3727
+ clearInterval(_walCheckpointTimer);
3728
+ _walCheckpointTimer = null;
3729
+ }
2717
3730
  if (_daemonClient) {
2718
3731
  _daemonClient.close();
2719
3732
  _daemonClient = null;
2720
3733
  }
3734
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3735
+ _adapterClient.close();
3736
+ }
3737
+ _adapterClient = null;
2721
3738
  if (_client) {
2722
3739
  _client.close();
2723
3740
  _client = null;
2724
3741
  _resilientClient = null;
2725
3742
  }
2726
3743
  }
2727
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
3744
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2728
3745
  var init_database = __esm({
2729
3746
  "src/lib/database.ts"() {
2730
3747
  "use strict";
2731
3748
  init_db_retry();
2732
3749
  init_employees();
3750
+ init_database_adapter();
2733
3751
  _client = null;
2734
3752
  _resilientClient = null;
2735
3753
  _walCheckpointTimer = null;
2736
3754
  _daemonClient = null;
3755
+ _adapterClient = null;
2737
3756
  initTurso = initDatabase;
2738
3757
  disposeTurso = disposeDatabase;
2739
3758
  }
@@ -2778,8 +3797,8 @@ __export(crdt_sync_exports, {
2778
3797
  rebuildFromDb: () => rebuildFromDb
2779
3798
  });
2780
3799
  import * as Y from "yjs";
2781
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync4 } from "fs";
2782
- import path7 from "path";
3800
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync3, unlinkSync as unlinkSync4 } from "fs";
3801
+ import path10 from "path";
2783
3802
  import { homedir } from "os";
2784
3803
  function getStatePath() {
2785
3804
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -2791,9 +3810,9 @@ function initCrdtDoc() {
2791
3810
  if (doc) return doc;
2792
3811
  doc = new Y.Doc();
2793
3812
  const sp = getStatePath();
2794
- if (existsSync7(sp)) {
3813
+ if (existsSync10(sp)) {
2795
3814
  try {
2796
- const state = readFileSync5(sp);
3815
+ const state = readFileSync7(sp);
2797
3816
  Y.applyUpdate(doc, new Uint8Array(state));
2798
3817
  } catch {
2799
3818
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -2935,10 +3954,10 @@ function persistState() {
2935
3954
  if (!doc) return;
2936
3955
  try {
2937
3956
  const sp = getStatePath();
2938
- const dir = path7.dirname(sp);
2939
- if (!existsSync7(dir)) mkdirSync2(dir, { recursive: true });
3957
+ const dir = path10.dirname(sp);
3958
+ if (!existsSync10(dir)) mkdirSync3(dir, { recursive: true });
2940
3959
  const state = Y.encodeStateAsUpdate(doc);
2941
- writeFileSync3(sp, Buffer.from(state));
3960
+ writeFileSync5(sp, Buffer.from(state));
2942
3961
  } catch {
2943
3962
  }
2944
3963
  }
@@ -2979,7 +3998,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
2979
3998
  var init_crdt_sync = __esm({
2980
3999
  "src/lib/crdt-sync.ts"() {
2981
4000
  "use strict";
2982
- DEFAULT_STATE_PATH = path7.join(homedir(), ".exe-os", "crdt-state.bin");
4001
+ DEFAULT_STATE_PATH = path10.join(homedir(), ".exe-os", "crdt-state.bin");
2983
4002
  _statePathOverride = null;
2984
4003
  doc = null;
2985
4004
  }
@@ -3011,39 +4030,107 @@ __export(cloud_sync_exports, {
3011
4030
  cloudSync: () => cloudSync,
3012
4031
  mergeConfig: () => mergeConfig,
3013
4032
  mergeRosterFromRemote: () => mergeRosterFromRemote,
4033
+ pushToPostgres: () => pushToPostgres,
3014
4034
  recordRosterDeletion: () => recordRosterDeletion
3015
4035
  });
3016
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
3017
- import crypto2 from "crypto";
3018
- import path8 from "path";
4036
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
4037
+ import crypto3 from "crypto";
4038
+ import path11 from "path";
3019
4039
  import { homedir as homedir2 } from "os";
3020
4040
  function sqlSafe(v) {
3021
4041
  return v === void 0 ? null : v;
3022
4042
  }
3023
4043
  function logError(msg) {
3024
4044
  try {
3025
- const logPath = path8.join(homedir2(), ".exe-os", "workers.log");
4045
+ const logPath = path11.join(homedir2(), ".exe-os", "workers.log");
3026
4046
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
3027
4047
  `);
3028
4048
  } catch {
3029
4049
  }
3030
4050
  }
4051
+ function loadPgClient() {
4052
+ if (_pgFailed) return null;
4053
+ const postgresUrl = process.env.DATABASE_URL;
4054
+ const configPath = path11.join(EXE_AI_DIR, "config.json");
4055
+ let cloudPostgresUrl;
4056
+ try {
4057
+ if (existsSync11(configPath)) {
4058
+ const cfg = JSON.parse(readFileSync8(configPath, "utf8"));
4059
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
4060
+ if (cfg.cloud?.syncToPostgres === false) {
4061
+ _pgFailed = true;
4062
+ return null;
4063
+ }
4064
+ }
4065
+ } catch {
4066
+ }
4067
+ const url = postgresUrl || cloudPostgresUrl;
4068
+ if (!url) {
4069
+ _pgFailed = true;
4070
+ return null;
4071
+ }
4072
+ if (!_pgPromise) {
4073
+ _pgPromise = (async () => {
4074
+ const { createRequire: createRequire3 } = await import("module");
4075
+ const { pathToFileURL: pathToFileURL3 } = await import("url");
4076
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(homedir2(), "exe-db");
4077
+ const req = createRequire3(path11.join(exeDbRoot, "package.json"));
4078
+ const entry = req.resolve("@prisma/client");
4079
+ const mod = await import(pathToFileURL3(entry).href);
4080
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4081
+ if (!Ctor) throw new Error("No PrismaClient");
4082
+ return new Ctor();
4083
+ })().catch(() => {
4084
+ _pgFailed = true;
4085
+ _pgPromise = null;
4086
+ throw new Error("pg_unavailable");
4087
+ });
4088
+ }
4089
+ return _pgPromise;
4090
+ }
4091
+ async function pushToPostgres(records) {
4092
+ const loader = loadPgClient();
4093
+ if (!loader) return 0;
4094
+ let prisma;
4095
+ try {
4096
+ prisma = await loader;
4097
+ } catch {
4098
+ return 0;
4099
+ }
4100
+ let inserted = 0;
4101
+ for (const rec of records) {
4102
+ try {
4103
+ await prisma.$executeRawUnsafe(
4104
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
4105
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
4106
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
4107
+ String(rec.id ?? ""),
4108
+ JSON.stringify(rec),
4109
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
4110
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
4111
+ );
4112
+ inserted++;
4113
+ } catch {
4114
+ }
4115
+ }
4116
+ return inserted;
4117
+ }
3031
4118
  async function withRosterLock(fn) {
3032
4119
  try {
3033
4120
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
3034
4121
  closeSync2(fd);
3035
- writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
4122
+ writeFileSync6(ROSTER_LOCK_PATH, String(Date.now()));
3036
4123
  } catch (err) {
3037
4124
  if (err.code === "EEXIST") {
3038
4125
  try {
3039
- const ts = parseInt(readFileSync6(ROSTER_LOCK_PATH, "utf-8"), 10);
4126
+ const ts = parseInt(readFileSync8(ROSTER_LOCK_PATH, "utf-8"), 10);
3040
4127
  if (Date.now() - ts < LOCK_STALE_MS) {
3041
4128
  throw new Error("Roster merge already in progress \u2014 another sync is running");
3042
4129
  }
3043
4130
  unlinkSync5(ROSTER_LOCK_PATH);
3044
4131
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
3045
4132
  closeSync2(fd);
3046
- writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
4133
+ writeFileSync6(ROSTER_LOCK_PATH, String(Date.now()));
3047
4134
  } catch (retryErr) {
3048
4135
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
3049
4136
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -3313,6 +4400,10 @@ async function cloudSync(config) {
3313
4400
  const maxVersion = Number(records[records.length - 1].version);
3314
4401
  const pushOk = await cloudPush(records, maxVersion, config);
3315
4402
  if (!pushOk) break;
4403
+ try {
4404
+ await pushToPostgres(records);
4405
+ } catch {
4406
+ }
3316
4407
  await client.execute({
3317
4408
  sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
3318
4409
  args: [String(maxVersion)]
@@ -3417,8 +4508,8 @@ async function cloudSync(config) {
3417
4508
  try {
3418
4509
  const employees = await loadEmployees();
3419
4510
  rosterResult.employees = employees.length;
3420
- const idDir = path8.join(EXE_AI_DIR, "identity");
3421
- if (existsSync8(idDir)) {
4511
+ const idDir = path11.join(EXE_AI_DIR, "identity");
4512
+ if (existsSync11(idDir)) {
3422
4513
  rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
3423
4514
  }
3424
4515
  } catch {
@@ -3439,62 +4530,62 @@ async function cloudSync(config) {
3439
4530
  function recordRosterDeletion(name) {
3440
4531
  let deletions = [];
3441
4532
  try {
3442
- if (existsSync8(ROSTER_DELETIONS_PATH)) {
3443
- deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
4533
+ if (existsSync11(ROSTER_DELETIONS_PATH)) {
4534
+ deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
3444
4535
  }
3445
4536
  } catch {
3446
4537
  }
3447
4538
  if (!deletions.includes(name)) deletions.push(name);
3448
- writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
4539
+ writeFileSync6(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
3449
4540
  }
3450
4541
  function consumeRosterDeletions() {
3451
4542
  try {
3452
- if (!existsSync8(ROSTER_DELETIONS_PATH)) return [];
3453
- const deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
3454
- writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
4543
+ if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
4544
+ const deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
4545
+ writeFileSync6(ROSTER_DELETIONS_PATH, "[]");
3455
4546
  return deletions;
3456
4547
  } catch {
3457
4548
  return [];
3458
4549
  }
3459
4550
  }
3460
4551
  function buildRosterBlob(paths) {
3461
- const rosterPath = paths?.rosterPath ?? path8.join(EXE_AI_DIR, "exe-employees.json");
3462
- const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
3463
- const configPath = paths?.configPath ?? path8.join(EXE_AI_DIR, "config.json");
4552
+ const rosterPath = paths?.rosterPath ?? path11.join(EXE_AI_DIR, "exe-employees.json");
4553
+ const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
4554
+ const configPath = paths?.configPath ?? path11.join(EXE_AI_DIR, "config.json");
3464
4555
  let roster = [];
3465
- if (existsSync8(rosterPath)) {
4556
+ if (existsSync11(rosterPath)) {
3466
4557
  try {
3467
- roster = JSON.parse(readFileSync6(rosterPath, "utf-8"));
4558
+ roster = JSON.parse(readFileSync8(rosterPath, "utf-8"));
3468
4559
  } catch {
3469
4560
  }
3470
4561
  }
3471
4562
  const identities = {};
3472
- if (existsSync8(identityDir)) {
4563
+ if (existsSync11(identityDir)) {
3473
4564
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
3474
4565
  try {
3475
- identities[file] = readFileSync6(path8.join(identityDir, file), "utf-8");
4566
+ identities[file] = readFileSync8(path11.join(identityDir, file), "utf-8");
3476
4567
  } catch {
3477
4568
  }
3478
4569
  }
3479
4570
  }
3480
4571
  let config;
3481
- if (existsSync8(configPath)) {
4572
+ if (existsSync11(configPath)) {
3482
4573
  try {
3483
- config = JSON.parse(readFileSync6(configPath, "utf-8"));
4574
+ config = JSON.parse(readFileSync8(configPath, "utf-8"));
3484
4575
  } catch {
3485
4576
  }
3486
4577
  }
3487
4578
  let agentConfig;
3488
- const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
3489
- if (existsSync8(agentConfigPath)) {
4579
+ const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
4580
+ if (existsSync11(agentConfigPath)) {
3490
4581
  try {
3491
- agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
4582
+ agentConfig = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
3492
4583
  } catch {
3493
4584
  }
3494
4585
  }
3495
4586
  const deletedNames = consumeRosterDeletions();
3496
4587
  const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
3497
- const hash = crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
4588
+ const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
3498
4589
  return { roster, identities, config, agentConfig, deletedNames, version: hash };
3499
4590
  }
3500
4591
  async function cloudPushRoster(config) {
@@ -3564,23 +4655,24 @@ async function cloudPullRoster(config) {
3564
4655
  }
3565
4656
  }
3566
4657
  function mergeConfig(remoteConfig, configPath) {
3567
- const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
4658
+ const cfgPath = configPath ?? path11.join(EXE_AI_DIR, "config.json");
3568
4659
  let local = {};
3569
- if (existsSync8(cfgPath)) {
4660
+ if (existsSync11(cfgPath)) {
3570
4661
  try {
3571
- local = JSON.parse(readFileSync6(cfgPath, "utf-8"));
4662
+ local = JSON.parse(readFileSync8(cfgPath, "utf-8"));
3572
4663
  } catch {
3573
4664
  }
3574
4665
  }
3575
4666
  const merged = { ...remoteConfig, ...local };
3576
- const dir = path8.dirname(cfgPath);
3577
- if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
3578
- writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
4667
+ const dir = path11.dirname(cfgPath);
4668
+ ensurePrivateDirSync(dir);
4669
+ writeFileSync6(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
4670
+ enforcePrivateFileSync(cfgPath);
3579
4671
  }
3580
4672
  async function mergeRosterFromRemote(remote, paths) {
3581
4673
  return withRosterLock(async () => {
3582
4674
  const rosterPath = paths?.rosterPath ?? void 0;
3583
- const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
4675
+ const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
3584
4676
  const localEmployees = await loadEmployees(rosterPath);
3585
4677
  const localNames = new Set(localEmployees.map((e) => e.name));
3586
4678
  let added = 0;
@@ -3601,15 +4693,15 @@ async function mergeRosterFromRemote(remote, paths) {
3601
4693
  ) ?? lookupKey;
3602
4694
  const remoteIdentity = remote.identities[matchedKey];
3603
4695
  if (remoteIdentity) {
3604
- if (!existsSync8(identityDir)) mkdirSync3(identityDir, { recursive: true });
3605
- const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
4696
+ if (!existsSync11(identityDir)) mkdirSync4(identityDir, { recursive: true });
4697
+ const idPath = path11.join(identityDir, `${remoteEmp.name}.md`);
3606
4698
  let localIdentity = null;
3607
4699
  try {
3608
- localIdentity = existsSync8(idPath) ? readFileSync6(idPath, "utf-8") : null;
4700
+ localIdentity = existsSync11(idPath) ? readFileSync8(idPath, "utf-8") : null;
3609
4701
  } catch {
3610
4702
  }
3611
4703
  if (localIdentity !== remoteIdentity) {
3612
- writeFileSync4(idPath, remoteIdentity, "utf-8");
4704
+ writeFileSync6(idPath, remoteIdentity, "utf-8");
3613
4705
  identitiesUpdated++;
3614
4706
  }
3615
4707
  }
@@ -3635,16 +4727,18 @@ async function mergeRosterFromRemote(remote, paths) {
3635
4727
  }
3636
4728
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
3637
4729
  try {
3638
- const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
4730
+ const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
3639
4731
  let local = {};
3640
- if (existsSync8(agentConfigPath)) {
4732
+ if (existsSync11(agentConfigPath)) {
3641
4733
  try {
3642
- local = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
4734
+ local = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
3643
4735
  } catch {
3644
4736
  }
3645
4737
  }
3646
4738
  const merged = { ...remote.agentConfig, ...local };
3647
- writeFileSync4(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
4739
+ ensurePrivateDirSync(path11.dirname(agentConfigPath));
4740
+ writeFileSync6(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
4741
+ enforcePrivateFileSync(agentConfigPath);
3648
4742
  } catch {
3649
4743
  }
3650
4744
  }
@@ -4068,7 +5162,7 @@ async function cloudPullDocuments(config) {
4068
5162
  }
4069
5163
  return { pulled };
4070
5164
  }
4071
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
5165
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
4072
5166
  var init_cloud_sync = __esm({
4073
5167
  "src/lib/cloud-sync.ts"() {
4074
5168
  "use strict";
@@ -4079,12 +5173,15 @@ var init_cloud_sync = __esm({
4079
5173
  init_config();
4080
5174
  init_crdt_sync();
4081
5175
  init_employees();
5176
+ init_secure_files();
4082
5177
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
4083
5178
  FETCH_TIMEOUT_MS = 3e4;
4084
5179
  PUSH_BATCH_SIZE = 5e3;
4085
- ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
5180
+ ROSTER_LOCK_PATH = path11.join(EXE_AI_DIR, "roster-merge.lock");
4086
5181
  LOCK_STALE_MS = 3e4;
4087
- ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
5182
+ _pgPromise = null;
5183
+ _pgFailed = false;
5184
+ ROSTER_DELETIONS_PATH = path11.join(EXE_AI_DIR, "roster-deletions.json");
4088
5185
  }
4089
5186
  });
4090
5187
 
@@ -4094,37 +5191,39 @@ __export(preferences_exports, {
4094
5191
  loadPreferences: () => loadPreferences,
4095
5192
  savePreferences: () => savePreferences
4096
5193
  });
4097
- import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
4098
- import path9 from "path";
4099
- import os5 from "os";
4100
- function loadPreferences(homeDir = os5.homedir()) {
4101
- const configPath = path9.join(homeDir, ".exe-os", "config.json");
4102
- if (!existsSync9(configPath)) return {};
5194
+ import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
5195
+ import path12 from "path";
5196
+ import os7 from "os";
5197
+ function loadPreferences(homeDir = os7.homedir()) {
5198
+ const configPath = path12.join(homeDir, ".exe-os", "config.json");
5199
+ if (!existsSync12(configPath)) return {};
4103
5200
  try {
4104
- const config = JSON.parse(readFileSync7(configPath, "utf-8"));
5201
+ const config = JSON.parse(readFileSync9(configPath, "utf-8"));
4105
5202
  return config.preferences ?? {};
4106
5203
  } catch {
4107
5204
  return {};
4108
5205
  }
4109
5206
  }
4110
- function savePreferences(prefs, homeDir = os5.homedir()) {
4111
- const configDir = path9.join(homeDir, ".exe-os");
4112
- const configPath = path9.join(configDir, "config.json");
4113
- mkdirSync4(configDir, { recursive: true });
5207
+ function savePreferences(prefs, homeDir = os7.homedir()) {
5208
+ const configDir = path12.join(homeDir, ".exe-os");
5209
+ const configPath = path12.join(configDir, "config.json");
5210
+ ensurePrivateDirSync(configDir);
4114
5211
  let config = {};
4115
- if (existsSync9(configPath)) {
5212
+ if (existsSync12(configPath)) {
4116
5213
  try {
4117
- config = JSON.parse(readFileSync7(configPath, "utf-8"));
5214
+ config = JSON.parse(readFileSync9(configPath, "utf-8"));
4118
5215
  } catch {
4119
5216
  config = {};
4120
5217
  }
4121
5218
  }
4122
5219
  config.preferences = prefs;
4123
- writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
5220
+ writeFileSync7(configPath, JSON.stringify(config, null, 2) + "\n");
5221
+ enforcePrivateFileSync(configPath);
4124
5222
  }
4125
5223
  var init_preferences = __esm({
4126
5224
  "src/lib/preferences.ts"() {
4127
5225
  "use strict";
5226
+ init_secure_files();
4128
5227
  }
4129
5228
  });
4130
5229
 
@@ -4895,17 +5994,17 @@ __export(identity_exports, {
4895
5994
  listIdentities: () => listIdentities,
4896
5995
  updateIdentity: () => updateIdentity
4897
5996
  });
4898
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
5997
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
4899
5998
  import { readdirSync as readdirSync2 } from "fs";
4900
- import path10 from "path";
5999
+ import path13 from "path";
4901
6000
  import { createHash as createHash2 } from "crypto";
4902
6001
  function ensureDir() {
4903
- if (!existsSync10(IDENTITY_DIR)) {
4904
- mkdirSync5(IDENTITY_DIR, { recursive: true });
6002
+ if (!existsSync13(IDENTITY_DIR2)) {
6003
+ mkdirSync5(IDENTITY_DIR2, { recursive: true });
4905
6004
  }
4906
6005
  }
4907
6006
  function identityPath(agentId) {
4908
- return path10.join(IDENTITY_DIR, `${agentId}.md`);
6007
+ return path13.join(IDENTITY_DIR2, `${agentId}.md`);
4909
6008
  }
4910
6009
  function parseFrontmatter(raw) {
4911
6010
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -4946,8 +6045,8 @@ function contentHash(content) {
4946
6045
  }
4947
6046
  function getIdentity(agentId) {
4948
6047
  const filePath = identityPath(agentId);
4949
- if (!existsSync10(filePath)) return null;
4950
- const raw = readFileSync8(filePath, "utf-8");
6048
+ if (!existsSync13(filePath)) return null;
6049
+ const raw = readFileSync10(filePath, "utf-8");
4951
6050
  const { frontmatter, body } = parseFrontmatter(raw);
4952
6051
  return {
4953
6052
  agentId,
@@ -4961,7 +6060,7 @@ async function updateIdentity(agentId, content, updatedBy) {
4961
6060
  ensureDir();
4962
6061
  const filePath = identityPath(agentId);
4963
6062
  const hash = contentHash(content);
4964
- writeFileSync6(filePath, content, "utf-8");
6063
+ writeFileSync8(filePath, content, "utf-8");
4965
6064
  try {
4966
6065
  const client = getClient();
4967
6066
  await client.execute({
@@ -4978,7 +6077,7 @@ async function updateIdentity(agentId, content, updatedBy) {
4978
6077
  }
4979
6078
  function listIdentities() {
4980
6079
  ensureDir();
4981
- const files = readdirSync2(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
6080
+ const files = readdirSync2(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
4982
6081
  const results = [];
4983
6082
  for (const file of files) {
4984
6083
  const agentId = file.replace(".md", "");
@@ -5011,13 +6110,13 @@ ${teamLines.join("\n")}`);
5011
6110
  }
5012
6111
  return parts.join("\n\n");
5013
6112
  }
5014
- var IDENTITY_DIR;
6113
+ var IDENTITY_DIR2;
5015
6114
  var init_identity = __esm({
5016
6115
  "src/lib/identity.ts"() {
5017
6116
  "use strict";
5018
6117
  init_config();
5019
6118
  init_database();
5020
- IDENTITY_DIR = path10.join(EXE_AI_DIR, "identity");
6119
+ IDENTITY_DIR2 = path13.join(EXE_AI_DIR, "identity");
5021
6120
  }
5022
6121
  });
5023
6122
 
@@ -5568,36 +6667,36 @@ __export(session_wrappers_exports, {
5568
6667
  generateSessionWrappers: () => generateSessionWrappers
5569
6668
  });
5570
6669
  import {
5571
- existsSync as existsSync11,
5572
- readFileSync as readFileSync9,
5573
- writeFileSync as writeFileSync7,
6670
+ existsSync as existsSync14,
6671
+ readFileSync as readFileSync11,
6672
+ writeFileSync as writeFileSync9,
5574
6673
  mkdirSync as mkdirSync6,
5575
- chmodSync,
6674
+ chmodSync as chmodSync2,
5576
6675
  readdirSync as readdirSync3,
5577
6676
  unlinkSync as unlinkSync6
5578
6677
  } from "fs";
5579
- import path11 from "path";
6678
+ import path14 from "path";
5580
6679
  import { homedir as homedir3 } from "os";
5581
6680
  function generateSessionWrappers(packageRoot, homeDir) {
5582
6681
  const home = homeDir ?? homedir3();
5583
- const binDir = path11.join(home, ".exe-os", "bin");
5584
- const rosterPath = path11.join(home, ".exe-os", "exe-employees.json");
6682
+ const binDir = path14.join(home, ".exe-os", "bin");
6683
+ const rosterPath = path14.join(home, ".exe-os", "exe-employees.json");
5585
6684
  mkdirSync6(binDir, { recursive: true });
5586
- const exeStartDst = path11.join(binDir, "exe-start");
6685
+ const exeStartDst = path14.join(binDir, "exe-start");
5587
6686
  const candidates = [
5588
- path11.join(packageRoot, "dist", "bin", "exe-start.sh"),
5589
- path11.join(packageRoot, "src", "bin", "exe-start.sh")
6687
+ path14.join(packageRoot, "dist", "bin", "exe-start.sh"),
6688
+ path14.join(packageRoot, "src", "bin", "exe-start.sh")
5590
6689
  ];
5591
6690
  for (const src of candidates) {
5592
- if (existsSync11(src)) {
5593
- writeFileSync7(exeStartDst, readFileSync9(src));
5594
- chmodSync(exeStartDst, 493);
6691
+ if (existsSync14(src)) {
6692
+ writeFileSync9(exeStartDst, readFileSync11(src));
6693
+ chmodSync2(exeStartDst, 493);
5595
6694
  break;
5596
6695
  }
5597
6696
  }
5598
6697
  let employees = [];
5599
6698
  try {
5600
- employees = JSON.parse(readFileSync9(rosterPath, "utf8"));
6699
+ employees = JSON.parse(readFileSync11(rosterPath, "utf8"));
5601
6700
  } catch {
5602
6701
  return { created: 0, pathConfigured: false };
5603
6702
  }
@@ -5607,9 +6706,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
5607
6706
  try {
5608
6707
  for (const f of readdirSync3(binDir)) {
5609
6708
  if (f === "exe-start") continue;
5610
- const fPath = path11.join(binDir, f);
6709
+ const fPath = path14.join(binDir, f);
5611
6710
  try {
5612
- const content = readFileSync9(fPath, "utf8");
6711
+ const content = readFileSync11(fPath, "utf8");
5613
6712
  if (content.includes("exe-start")) {
5614
6713
  unlinkSync6(fPath);
5615
6714
  }
@@ -5624,31 +6723,31 @@ exec "${exeStartDst}" "$0" "$@"
5624
6723
  `;
5625
6724
  for (const emp of employees) {
5626
6725
  for (let n = 1; n <= MAX_N; n++) {
5627
- const wrapperPath = path11.join(binDir, `${emp.name}${n}`);
5628
- writeFileSync7(wrapperPath, wrapperContent);
5629
- chmodSync(wrapperPath, 493);
6726
+ const wrapperPath = path14.join(binDir, `${emp.name}${n}`);
6727
+ writeFileSync9(wrapperPath, wrapperContent);
6728
+ chmodSync2(wrapperPath, 493);
5630
6729
  created++;
5631
6730
  }
5632
6731
  }
5633
6732
  const codexLauncherCandidates = [
5634
- path11.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
5635
- path11.join(packageRoot, "src", "bin", "exe-start-codex.ts")
6733
+ path14.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
6734
+ path14.join(packageRoot, "src", "bin", "exe-start-codex.ts")
5636
6735
  ];
5637
6736
  let codexLauncher = null;
5638
6737
  for (const c of codexLauncherCandidates) {
5639
- if (existsSync11(c)) {
6738
+ if (existsSync14(c)) {
5640
6739
  codexLauncher = c;
5641
6740
  break;
5642
6741
  }
5643
6742
  }
5644
6743
  if (codexLauncher) {
5645
6744
  for (const emp of employees) {
5646
- const wrapperPath = path11.join(binDir, `${emp.name}-codex`);
6745
+ const wrapperPath = path14.join(binDir, `${emp.name}-codex`);
5647
6746
  const content = `#!/bin/bash
5648
6747
  exec node "${codexLauncher}" --agent ${emp.name} "$@"
5649
6748
  `;
5650
- writeFileSync7(wrapperPath, content);
5651
- chmodSync(wrapperPath, 493);
6749
+ writeFileSync9(wrapperPath, content);
6750
+ chmodSync2(wrapperPath, 493);
5652
6751
  created++;
5653
6752
  }
5654
6753
  }
@@ -5666,24 +6765,24 @@ export PATH="${binDir}:$PATH"
5666
6765
  const shell = process.env.SHELL ?? "/bin/bash";
5667
6766
  const profilePaths = [];
5668
6767
  if (shell.includes("zsh")) {
5669
- profilePaths.push(path11.join(home, ".zshrc"));
6768
+ profilePaths.push(path14.join(home, ".zshrc"));
5670
6769
  } else if (shell.includes("bash")) {
5671
- profilePaths.push(path11.join(home, ".bashrc"));
5672
- profilePaths.push(path11.join(home, ".bash_profile"));
6770
+ profilePaths.push(path14.join(home, ".bashrc"));
6771
+ profilePaths.push(path14.join(home, ".bash_profile"));
5673
6772
  } else {
5674
- profilePaths.push(path11.join(home, ".profile"));
6773
+ profilePaths.push(path14.join(home, ".profile"));
5675
6774
  }
5676
6775
  for (const profilePath of profilePaths) {
5677
6776
  try {
5678
6777
  let content = "";
5679
6778
  try {
5680
- content = readFileSync9(profilePath, "utf8");
6779
+ content = readFileSync11(profilePath, "utf8");
5681
6780
  } catch {
5682
6781
  }
5683
6782
  if (content.includes(".exe-os/bin")) {
5684
6783
  return false;
5685
6784
  }
5686
- writeFileSync7(profilePath, content + exportLine);
6785
+ writeFileSync9(profilePath, content + exportLine);
5687
6786
  return true;
5688
6787
  } catch {
5689
6788
  continue;
@@ -5702,14 +6801,14 @@ var init_session_wrappers = __esm({
5702
6801
  // src/lib/setup-wizard.ts
5703
6802
  init_config();
5704
6803
  init_keychain();
5705
- import crypto3 from "crypto";
5706
- import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7 } from "fs";
5707
- import os6 from "os";
5708
- import path12 from "path";
6804
+ import crypto4 from "crypto";
6805
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync7 } from "fs";
6806
+ import os8 from "os";
6807
+ import path15 from "path";
5709
6808
  import { createInterface } from "readline";
5710
6809
 
5711
6810
  // src/lib/model-downloader.ts
5712
- import { createWriteStream, createReadStream, existsSync as existsSync3, unlinkSync, renameSync as renameSync2 } from "fs";
6811
+ import { createWriteStream, createReadStream, existsSync as existsSync4, unlinkSync, renameSync as renameSync2 } from "fs";
5713
6812
  import { mkdir as mkdir3 } from "fs/promises";
5714
6813
  import { createHash } from "crypto";
5715
6814
  import path3 from "path";
@@ -5722,7 +6821,7 @@ async function downloadModel(opts) {
5722
6821
  const destPath = path3.join(destDir, LOCAL_FILENAME);
5723
6822
  const tmpPath = destPath + ".tmp";
5724
6823
  await mkdir3(destDir, { recursive: true });
5725
- if (existsSync3(destPath)) {
6824
+ if (existsSync4(destPath)) {
5726
6825
  const hash = await fileHash(destPath);
5727
6826
  if (hash === EXPECTED_SHA256) {
5728
6827
  return destPath;
@@ -5734,7 +6833,7 @@ async function downloadModel(opts) {
5734
6833
  let downloaded = 0;
5735
6834
  for (let attempt = 1; attempt <= MAX_RETRIES2; attempt++) {
5736
6835
  try {
5737
- if (existsSync3(tmpPath)) unlinkSync(tmpPath);
6836
+ if (existsSync4(tmpPath)) unlinkSync(tmpPath);
5738
6837
  const response = await fetchFn(GGUF_URL, {
5739
6838
  redirect: "follow",
5740
6839
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
@@ -5779,7 +6878,7 @@ async function downloadModel(opts) {
5779
6878
  process.stderr.write(`
5780
6879
  Download attempt ${attempt} failed, retrying...
5781
6880
  `);
5782
- if (existsSync3(tmpPath)) unlinkSync(tmpPath);
6881
+ if (existsSync4(tmpPath)) unlinkSync(tmpPath);
5783
6882
  }
5784
6883
  }
5785
6884
  }
@@ -5797,32 +6896,32 @@ async function fileHash(filePath) {
5797
6896
 
5798
6897
  // src/lib/setup-wizard.ts
5799
6898
  function findPackageRoot2() {
5800
- let dir = path12.dirname(new URL(import.meta.url).pathname);
5801
- const root = path12.parse(dir).root;
6899
+ let dir = path15.dirname(new URL(import.meta.url).pathname);
6900
+ const root = path15.parse(dir).root;
5802
6901
  while (dir !== root) {
5803
- const pkgPath = path12.join(dir, "package.json");
5804
- if (existsSync12(pkgPath)) {
6902
+ const pkgPath = path15.join(dir, "package.json");
6903
+ if (existsSync15(pkgPath)) {
5805
6904
  try {
5806
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
6905
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
5807
6906
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
5808
6907
  } catch {
5809
6908
  }
5810
6909
  }
5811
- dir = path12.dirname(dir);
6910
+ dir = path15.dirname(dir);
5812
6911
  }
5813
6912
  return null;
5814
6913
  }
5815
- var SETUP_STATE_PATH = path12.join(os6.homedir(), ".exe-os", "setup-state.json");
6914
+ var SETUP_STATE_PATH = path15.join(os8.homedir(), ".exe-os", "setup-state.json");
5816
6915
  function loadSetupState() {
5817
6916
  try {
5818
- return JSON.parse(readFileSync10(SETUP_STATE_PATH, "utf8"));
6917
+ return JSON.parse(readFileSync12(SETUP_STATE_PATH, "utf8"));
5819
6918
  } catch {
5820
6919
  return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
5821
6920
  }
5822
6921
  }
5823
6922
  function saveSetupState(state) {
5824
- mkdirSync7(path12.dirname(SETUP_STATE_PATH), { recursive: true });
5825
- writeFileSync8(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
6923
+ mkdirSync7(path15.dirname(SETUP_STATE_PATH), { recursive: true });
6924
+ writeFileSync10(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
5826
6925
  }
5827
6926
  function clearSetupState() {
5828
6927
  try {
@@ -5841,10 +6940,10 @@ function ask(rl, prompt) {
5841
6940
  });
5842
6941
  }
5843
6942
  function getAvailableMemoryGB() {
5844
- return os6.freemem() / (1024 * 1024 * 1024);
6943
+ return os8.freemem() / (1024 * 1024 * 1024);
5845
6944
  }
5846
6945
  function getTotalMemoryGB() {
5847
- return os6.totalmem() / (1024 * 1024 * 1024);
6946
+ return os8.totalmem() / (1024 * 1024 * 1024);
5848
6947
  }
5849
6948
  function isLowMemory() {
5850
6949
  return getAvailableMemoryGB() < 2;
@@ -5855,8 +6954,8 @@ async function validateModel(log) {
5855
6954
  if (totalGB <= 8 || isLowMemory()) {
5856
6955
  log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
5857
6956
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
5858
- const modelPath = path12.join(MODELS_DIR, LOCAL_FILENAME);
5859
- if (existsSync12(modelPath)) {
6957
+ const modelPath = path15.join(MODELS_DIR, LOCAL_FILENAME);
6958
+ if (existsSync15(modelPath)) {
5860
6959
  const { statSync: statSync2 } = await import("fs");
5861
6960
  const size = statSync2(modelPath).size;
5862
6961
  if (size > 300 * 1e6) {
@@ -5947,7 +7046,7 @@ async function runSetupWizard(opts = {}) {
5947
7046
  if (state.completedSteps.length > 0) {
5948
7047
  log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
5949
7048
  }
5950
- if (existsSync12(LEGACY_LANCE_PATH)) {
7049
+ if (existsSync15(LEGACY_LANCE_PATH)) {
5951
7050
  log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
5952
7051
  log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
5953
7052
  log(" The old directory will not be modified or deleted.");
@@ -5959,7 +7058,7 @@ async function runSetupWizard(opts = {}) {
5959
7058
  log("Encryption key already exists \u2014 skipping generation.");
5960
7059
  } else {
5961
7060
  log("Generating 256-bit encryption key...");
5962
- const key = crypto3.randomBytes(32);
7061
+ const key = crypto4.randomBytes(32);
5963
7062
  await setMasterKey(key);
5964
7063
  log("Encryption key generated and stored securely.");
5965
7064
  }
@@ -6111,19 +7210,19 @@ async function runSetupWizard(opts = {}) {
6111
7210
  await saveConfig(config);
6112
7211
  log("");
6113
7212
  try {
6114
- const claudeJsonPath = path12.join(os6.homedir(), ".claude.json");
7213
+ const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
6115
7214
  let claudeJson = {};
6116
7215
  try {
6117
- claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
7216
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
6118
7217
  } catch {
6119
7218
  }
6120
7219
  if (!claudeJson.projects) claudeJson.projects = {};
6121
7220
  const projects = claudeJson.projects;
6122
- for (const dir of [process.cwd(), os6.homedir()]) {
7221
+ for (const dir of [process.cwd(), os8.homedir()]) {
6123
7222
  if (!projects[dir]) projects[dir] = {};
6124
7223
  projects[dir].hasTrustDialogAccepted = true;
6125
7224
  }
6126
- writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7225
+ writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6127
7226
  } catch {
6128
7227
  }
6129
7228
  state.completedSteps.push(5);
@@ -6137,7 +7236,7 @@ async function runSetupWizard(opts = {}) {
6137
7236
  const prefs = { ...existingPrefs };
6138
7237
  log("=== Config Defaults ===");
6139
7238
  log("");
6140
- const ghosttyDetected = existsSync12(path12.join(os6.homedir(), ".config", "ghostty")) || existsSync12(path12.join(os6.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
7239
+ const ghosttyDetected = existsSync15(path15.join(os8.homedir(), ".config", "ghostty")) || existsSync15(path15.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
6141
7240
  if (ghosttyDetected) {
6142
7241
  const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
6143
7242
  prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
@@ -6184,7 +7283,7 @@ async function runSetupWizard(opts = {}) {
6184
7283
  let missingIdentities = [];
6185
7284
  for (const emp of roster) {
6186
7285
  const idPath = identityPath2(emp.name);
6187
- if (!existsSync12(idPath)) {
7286
+ if (!existsSync15(idPath)) {
6188
7287
  missingIdentities.push(emp.name);
6189
7288
  }
6190
7289
  }
@@ -6216,7 +7315,7 @@ async function runSetupWizard(opts = {}) {
6216
7315
  }
6217
7316
  missingIdentities = [];
6218
7317
  for (const emp of roster) {
6219
- if (!existsSync12(identityPath2(emp.name))) {
7318
+ if (!existsSync15(identityPath2(emp.name))) {
6220
7319
  missingIdentities.push(emp.name);
6221
7320
  }
6222
7321
  }
@@ -6281,9 +7380,9 @@ async function runSetupWizard(opts = {}) {
6281
7380
  const cooIdentityContent = getIdentityTemplate("coo");
6282
7381
  if (cooIdentityContent) {
6283
7382
  const cooIdPath = identityPath2(cooName);
6284
- mkdirSync7(path12.dirname(cooIdPath), { recursive: true });
7383
+ mkdirSync7(path15.dirname(cooIdPath), { recursive: true });
6285
7384
  const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
6286
- writeFileSync8(cooIdPath, replaced, "utf-8");
7385
+ writeFileSync10(cooIdPath, replaced, "utf-8");
6287
7386
  }
6288
7387
  registerBinSymlinks2(cooName);
6289
7388
  createdEmployees.push({ name: cooName, role: "COO" });
@@ -6377,9 +7476,9 @@ async function runSetupWizard(opts = {}) {
6377
7476
  const ctoIdentityContent = getIdentityTemplate("cto");
6378
7477
  if (ctoIdentityContent) {
6379
7478
  const ctoIdPath = identityPath2(ctoName);
6380
- mkdirSync7(path12.dirname(ctoIdPath), { recursive: true });
7479
+ mkdirSync7(path15.dirname(ctoIdPath), { recursive: true });
6381
7480
  const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
6382
- writeFileSync8(ctoIdPath, replaced, "utf-8");
7481
+ writeFileSync10(ctoIdPath, replaced, "utf-8");
6383
7482
  }
6384
7483
  registerBinSymlinks2(ctoName);
6385
7484
  createdEmployees.push({ name: ctoName, role: "CTO" });
@@ -6400,9 +7499,9 @@ async function runSetupWizard(opts = {}) {
6400
7499
  const cmoIdentityContent = getIdentityTemplate("cmo");
6401
7500
  if (cmoIdentityContent) {
6402
7501
  const cmoIdPath = identityPath2(cmoName);
6403
- mkdirSync7(path12.dirname(cmoIdPath), { recursive: true });
7502
+ mkdirSync7(path15.dirname(cmoIdPath), { recursive: true });
6404
7503
  const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
6405
- writeFileSync8(cmoIdPath, replaced, "utf-8");
7504
+ writeFileSync10(cmoIdPath, replaced, "utf-8");
6406
7505
  }
6407
7506
  registerBinSymlinks2(cmoName);
6408
7507
  createdEmployees.push({ name: cmoName, role: "CMO" });
@@ -6424,7 +7523,7 @@ async function runSetupWizard(opts = {}) {
6424
7523
  log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
6425
7524
  }
6426
7525
  if (wrapResult.pathConfigured) {
6427
- const binDir = path12.join(os6.homedir(), ".exe-os", "bin");
7526
+ const binDir = path15.join(os8.homedir(), ".exe-os", "bin");
6428
7527
  process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
6429
7528
  pathJustConfigured = true;
6430
7529
  }
@@ -6467,7 +7566,7 @@ async function runSetupWizard(opts = {}) {
6467
7566
  const pkgRoot2 = findPackageRoot2();
6468
7567
  if (pkgRoot2) {
6469
7568
  try {
6470
- version = JSON.parse(readFileSync10(path12.join(pkgRoot2, "package.json"), "utf-8")).version;
7569
+ version = JSON.parse(readFileSync12(path15.join(pkgRoot2, "package.json"), "utf-8")).version;
6471
7570
  } catch {
6472
7571
  }
6473
7572
  }