@askexenow/exe-os 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -8,6 +8,44 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/secure-files.ts
12
+ import { chmodSync, existsSync, mkdirSync } from "fs";
13
+ import { chmod, mkdir } from "fs/promises";
14
+ async function ensurePrivateDir(dirPath) {
15
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
16
+ try {
17
+ await chmod(dirPath, PRIVATE_DIR_MODE);
18
+ } catch {
19
+ }
20
+ }
21
+ function ensurePrivateDirSync(dirPath) {
22
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
23
+ try {
24
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
25
+ } catch {
26
+ }
27
+ }
28
+ async function enforcePrivateFile(filePath) {
29
+ try {
30
+ await chmod(filePath, PRIVATE_FILE_MODE);
31
+ } catch {
32
+ }
33
+ }
34
+ function enforcePrivateFileSync(filePath) {
35
+ try {
36
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
37
+ } catch {
38
+ }
39
+ }
40
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
41
+ var init_secure_files = __esm({
42
+ "src/lib/secure-files.ts"() {
43
+ "use strict";
44
+ PRIVATE_DIR_MODE = 448;
45
+ PRIVATE_FILE_MODE = 384;
46
+ }
47
+ });
48
+
11
49
  // src/lib/config.ts
12
50
  var config_exports = {};
13
51
  __export(config_exports, {
@@ -24,8 +62,8 @@ __export(config_exports, {
24
62
  migrateConfig: () => migrateConfig,
25
63
  saveConfig: () => saveConfig
26
64
  });
27
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
28
- import { readFileSync, existsSync, renameSync } from "fs";
65
+ import { readFile, writeFile } from "fs/promises";
66
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
29
67
  import path from "path";
30
68
  import os from "os";
31
69
  function resolveDataDir() {
@@ -33,7 +71,7 @@ function resolveDataDir() {
33
71
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
34
72
  const newDir = path.join(os.homedir(), ".exe-os");
35
73
  const legacyDir = path.join(os.homedir(), ".exe-mem");
36
- if (!existsSync(newDir) && existsSync(legacyDir)) {
74
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
37
75
  try {
38
76
  renameSync(legacyDir, newDir);
39
77
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -96,9 +134,9 @@ function normalizeAutoUpdate(raw) {
96
134
  }
97
135
  async function loadConfig() {
98
136
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
99
- await mkdir(dir, { recursive: true });
137
+ await ensurePrivateDir(dir);
100
138
  const configPath = path.join(dir, "config.json");
101
- if (!existsSync(configPath)) {
139
+ if (!existsSync2(configPath)) {
102
140
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
103
141
  }
104
142
  const raw = await readFile(configPath, "utf-8");
@@ -111,6 +149,7 @@ async function loadConfig() {
111
149
  `);
112
150
  try {
113
151
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
152
+ await enforcePrivateFile(configPath);
114
153
  } catch {
115
154
  }
116
155
  }
@@ -129,7 +168,7 @@ async function loadConfig() {
129
168
  function loadConfigSync() {
130
169
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
131
170
  const configPath = path.join(dir, "config.json");
132
- if (!existsSync(configPath)) {
171
+ if (!existsSync2(configPath)) {
133
172
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
134
173
  }
135
174
  try {
@@ -147,12 +186,10 @@ function loadConfigSync() {
147
186
  }
148
187
  async function saveConfig(config) {
149
188
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
150
- await mkdir(dir, { recursive: true });
189
+ await ensurePrivateDir(dir);
151
190
  const configPath = path.join(dir, "config.json");
152
191
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
- if (config.cloud?.apiKey) {
154
- await chmod(configPath, 384);
155
- }
192
+ await enforcePrivateFile(configPath);
156
193
  }
157
194
  async function loadConfigFrom(configPath) {
158
195
  const raw = await readFile(configPath, "utf-8");
@@ -172,6 +209,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
172
209
  var init_config = __esm({
173
210
  "src/lib/config.ts"() {
174
211
  "use strict";
212
+ init_secure_files();
175
213
  EXE_AI_DIR = resolveDataDir();
176
214
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
177
215
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -257,15 +295,49 @@ import net from "net";
257
295
  import os2 from "os";
258
296
  import { spawn } from "child_process";
259
297
  import { randomUUID } from "crypto";
260
- import { existsSync as existsSync2, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
261
- import path2 from "path";
298
+ import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
299
+ import path3 from "path";
262
300
  import { fileURLToPath } from "url";
263
- var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path2.join(EXE_AI_DIR, "exed.sock");
264
- var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path2.join(EXE_AI_DIR, "exed.pid");
265
- var SPAWN_LOCK_PATH = path2.join(EXE_AI_DIR, "exed-spawn.lock");
301
+
302
+ // src/lib/daemon-auth.ts
303
+ init_config();
304
+ init_secure_files();
305
+ import crypto from "crypto";
306
+ import path2 from "path";
307
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
308
+ var DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
309
+ function normalizeToken(token) {
310
+ if (!token) return null;
311
+ const trimmed = token.trim();
312
+ return trimmed.length > 0 ? trimmed : null;
313
+ }
314
+ function readDaemonToken() {
315
+ try {
316
+ if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
317
+ return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
318
+ } catch {
319
+ return null;
320
+ }
321
+ }
322
+ function ensureDaemonToken(seed) {
323
+ const existing = readDaemonToken();
324
+ if (existing) return existing;
325
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
326
+ ensurePrivateDirSync(EXE_AI_DIR);
327
+ writeFileSync(DAEMON_TOKEN_PATH, `${token}
328
+ `, "utf8");
329
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
330
+ return token;
331
+ }
332
+
333
+ // src/lib/exe-daemon-client.ts
334
+ var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
335
+ var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
336
+ var SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
266
337
  var SPAWN_LOCK_STALE_MS = 3e4;
267
338
  var CONNECT_TIMEOUT_MS = 15e3;
268
339
  var REQUEST_TIMEOUT_MS = 3e4;
340
+ var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
269
341
  var _socket = null;
270
342
  var _connected = false;
271
343
  var _buffer = "";
@@ -303,9 +375,9 @@ function handleData(chunk) {
303
375
  }
304
376
  }
305
377
  function cleanupStaleFiles() {
306
- if (existsSync2(PID_PATH)) {
378
+ if (existsSync4(PID_PATH)) {
307
379
  try {
308
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
380
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
309
381
  if (pid > 0) {
310
382
  try {
311
383
  process.kill(pid, 0);
@@ -326,11 +398,11 @@ function cleanupStaleFiles() {
326
398
  }
327
399
  }
328
400
  function findPackageRoot() {
329
- let dir = path2.dirname(fileURLToPath(import.meta.url));
330
- const { root } = path2.parse(dir);
401
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
402
+ const { root } = path3.parse(dir);
331
403
  while (dir !== root) {
332
- if (existsSync2(path2.join(dir, "package.json"))) return dir;
333
- dir = path2.dirname(dir);
404
+ if (existsSync4(path3.join(dir, "package.json"))) return dir;
405
+ dir = path3.dirname(dir);
334
406
  }
335
407
  return null;
336
408
  }
@@ -356,16 +428,17 @@ function spawnDaemon() {
356
428
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
357
429
  return;
358
430
  }
359
- const daemonPath = path2.join(pkgRoot, "dist", "lib", "exe-daemon.js");
360
- if (!existsSync2(daemonPath)) {
431
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
432
+ if (!existsSync4(daemonPath)) {
361
433
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
362
434
  `);
363
435
  return;
364
436
  }
365
437
  const resolvedPath = daemonPath;
438
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
366
439
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
367
440
  `);
368
- const logPath = path2.join(path2.dirname(SOCKET_PATH), "exed.log");
441
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
369
442
  let stderrFd = "ignore";
370
443
  try {
371
444
  stderrFd = openSync(logPath, "a");
@@ -383,7 +456,8 @@ function spawnDaemon() {
383
456
  TMUX_PANE: void 0,
384
457
  // Prevents resolveExeSession() from scoping to one session
385
458
  EXE_DAEMON_SOCK: SOCKET_PATH,
386
- EXE_DAEMON_PID: PID_PATH
459
+ EXE_DAEMON_PID: PID_PATH,
460
+ [DAEMON_TOKEN_ENV]: daemonToken
387
461
  }
388
462
  });
389
463
  child.unref();
@@ -493,13 +567,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
493
567
  return;
494
568
  }
495
569
  const id = randomUUID();
570
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
496
571
  const timer = setTimeout(() => {
497
572
  _pending.delete(id);
498
573
  resolve({ error: "Request timeout" });
499
574
  }, timeoutMs);
500
575
  _pending.set(id, { resolve, timer });
501
576
  try {
502
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
577
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
503
578
  } catch {
504
579
  clearTimeout(timer);
505
580
  _pending.delete(id);
@@ -528,9 +603,9 @@ function killAndRespawnDaemon() {
528
603
  }
529
604
  try {
530
605
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
531
- if (existsSync2(PID_PATH)) {
606
+ if (existsSync4(PID_PATH)) {
532
607
  try {
533
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
608
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
534
609
  if (pid > 0) {
535
610
  try {
536
611
  process.kill(pid, "SIGKILL");
@@ -678,10 +753,10 @@ async function disposeEmbedder() {
678
753
  async function embedDirect(text) {
679
754
  const llamaCpp = await import("node-llama-cpp");
680
755
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
681
- const { existsSync: existsSync3 } = await import("fs");
682
- const path3 = await import("path");
683
- const modelPath = path3.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
684
- if (!existsSync3(modelPath)) {
756
+ const { existsSync: existsSync5 } = await import("fs");
757
+ const path4 = await import("path");
758
+ const modelPath = path4.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
759
+ if (!existsSync5(modelPath)) {
685
760
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
686
761
  }
687
762
  const llama = await llamaCpp.getLlama();
@@ -3,9 +3,18 @@ var __esm = (fn, res) => function __init() {
3
3
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
4
  };
5
5
 
6
+ // src/lib/secure-files.ts
7
+ import { chmodSync, existsSync, mkdirSync } from "fs";
8
+ import { chmod, mkdir } from "fs/promises";
9
+ var init_secure_files = __esm({
10
+ "src/lib/secure-files.ts"() {
11
+ "use strict";
12
+ }
13
+ });
14
+
6
15
  // src/lib/config.ts
7
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
8
- import { readFileSync, existsSync, renameSync } from "fs";
16
+ import { readFile, writeFile } from "fs/promises";
17
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
18
  import path from "path";
10
19
  import os from "os";
11
20
  function resolveDataDir() {
@@ -13,7 +22,7 @@ function resolveDataDir() {
13
22
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
23
  const newDir = path.join(os.homedir(), ".exe-os");
15
24
  const legacyDir = path.join(os.homedir(), ".exe-mem");
16
- if (!existsSync(newDir) && existsSync(legacyDir)) {
25
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
26
  try {
18
27
  renameSync(legacyDir, newDir);
19
28
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -28,6 +37,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
28
37
  var init_config = __esm({
29
38
  "src/lib/config.ts"() {
30
39
  "use strict";
40
+ init_secure_files();
31
41
  EXE_AI_DIR = resolveDataDir();
32
42
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
33
43
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -103,7 +113,7 @@ import { createClient } from "@libsql/client";
103
113
  // src/lib/employees.ts
104
114
  init_config();
105
115
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
106
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
116
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
107
117
  import { execSync } from "child_process";
108
118
  import path2 from "path";
109
119
  import os2 from "os";
@@ -8,9 +8,34 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/secure-files.ts
12
+ import { chmodSync, existsSync, mkdirSync } from "fs";
13
+ import { chmod, mkdir } from "fs/promises";
14
+ function ensurePrivateDirSync(dirPath) {
15
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
16
+ try {
17
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
18
+ } catch {
19
+ }
20
+ }
21
+ function enforcePrivateFileSync(filePath) {
22
+ try {
23
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
24
+ } catch {
25
+ }
26
+ }
27
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
28
+ var init_secure_files = __esm({
29
+ "src/lib/secure-files.ts"() {
30
+ "use strict";
31
+ PRIVATE_DIR_MODE = 448;
32
+ PRIVATE_FILE_MODE = 384;
33
+ }
34
+ });
35
+
11
36
  // src/lib/config.ts
12
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
13
- import { readFileSync, existsSync, renameSync } from "fs";
37
+ import { readFile, writeFile } from "fs/promises";
38
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
14
39
  import path from "path";
15
40
  import os from "os";
16
41
  function resolveDataDir() {
@@ -18,7 +43,7 @@ function resolveDataDir() {
18
43
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
19
44
  const newDir = path.join(os.homedir(), ".exe-os");
20
45
  const legacyDir = path.join(os.homedir(), ".exe-mem");
21
- if (!existsSync(newDir) && existsSync(legacyDir)) {
46
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
22
47
  try {
23
48
  renameSync(legacyDir, newDir);
24
49
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -33,6 +58,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
33
58
  var init_config = __esm({
34
59
  "src/lib/config.ts"() {
35
60
  "use strict";
61
+ init_secure_files();
36
62
  EXE_AI_DIR = resolveDataDir();
37
63
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
38
64
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -139,10 +165,10 @@ __export(agent_config_exports, {
139
165
  saveAgentConfig: () => saveAgentConfig,
140
166
  setAgentRuntime: () => setAgentRuntime
141
167
  });
142
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
168
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
143
169
  import path2 from "path";
144
170
  function loadAgentConfig() {
145
- if (!existsSync2(AGENT_CONFIG_PATH)) return {};
171
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
146
172
  try {
147
173
  return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
148
174
  } catch {
@@ -151,8 +177,9 @@ function loadAgentConfig() {
151
177
  }
152
178
  function saveAgentConfig(config) {
153
179
  const dir = path2.dirname(AGENT_CONFIG_PATH);
154
- if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
180
+ ensurePrivateDirSync(dir);
155
181
  writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
182
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
156
183
  }
157
184
  function getAgentRuntime(agentId) {
158
185
  const config = loadAgentConfig();
@@ -192,6 +219,7 @@ var init_agent_config = __esm({
192
219
  "use strict";
193
220
  init_config();
194
221
  init_runtime_table();
222
+ init_secure_files();
195
223
  AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
196
224
  KNOWN_RUNTIMES = {
197
225
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
@@ -214,7 +242,7 @@ var init_agent_config = __esm({
214
242
  // src/lib/employees.ts
215
243
  init_config();
216
244
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
217
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
245
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
218
246
  import { execSync } from "child_process";
219
247
  import path3 from "path";
220
248
  import os2 from "os";
@@ -256,7 +284,7 @@ function validateEmployeeName(name) {
256
284
  return { valid: true };
257
285
  }
258
286
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
259
- if (!existsSync3(employeesPath)) {
287
+ if (!existsSync4(employeesPath)) {
260
288
  return [];
261
289
  }
262
290
  const raw = await readFile2(employeesPath, "utf-8");
@@ -271,7 +299,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
271
299
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
272
300
  }
273
301
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
274
- if (!existsSync3(employeesPath)) return [];
302
+ if (!existsSync4(employeesPath)) return [];
275
303
  try {
276
304
  return JSON.parse(readFileSync3(employeesPath, "utf-8"));
277
305
  } catch {
@@ -322,7 +350,7 @@ function appendToCoordinatorTeam(employee) {
322
350
  const coordinator = getCoordinatorEmployee(loadEmployeesSync());
323
351
  if (!coordinator) return;
324
352
  const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
325
- if (!existsSync3(idPath)) return;
353
+ if (!existsSync4(idPath)) return;
326
354
  const content = readFileSync3(idPath, "utf-8");
327
355
  if (content.includes(`**${capitalize(employee.name)}`)) return;
328
356
  const teamMatch = content.match(TEAM_SECTION_RE);
@@ -376,9 +404,9 @@ async function normalizeRosterCase(rosterPath) {
376
404
  const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
377
405
  const oldPath = path3.join(identityDir, `${oldName}.md`);
378
406
  const newPath = path3.join(identityDir, `${emp.name}.md`);
379
- if (existsSync3(oldPath) && !existsSync3(newPath)) {
407
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
380
408
  renameSync2(oldPath, newPath);
381
- } else if (existsSync3(oldPath) && oldPath !== newPath) {
409
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
382
410
  const content = readFileSync3(oldPath, "utf-8");
383
411
  writeFileSync2(newPath, content, "utf-8");
384
412
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
@@ -421,7 +449,7 @@ function registerBinSymlinks(name) {
421
449
  for (const suffix of ["", "-opencode"]) {
422
450
  const linkName = `${name}${suffix}`;
423
451
  const linkPath = path3.join(binDir, linkName);
424
- if (existsSync3(linkPath)) {
452
+ if (existsSync4(linkPath)) {
425
453
  skipped.push(linkName);
426
454
  continue;
427
455
  }
@@ -3,21 +3,42 @@ import net from "net";
3
3
  import os2 from "os";
4
4
  import { spawn } from "child_process";
5
5
  import { randomUUID } from "crypto";
6
- import { existsSync as existsSync2, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
7
- import path2 from "path";
6
+ import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
7
+ import path3 from "path";
8
8
  import { fileURLToPath } from "url";
9
9
 
10
10
  // src/lib/config.ts
11
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
12
- import { readFileSync, existsSync, renameSync } from "fs";
11
+ import { readFile, writeFile } from "fs/promises";
12
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
13
13
  import path from "path";
14
14
  import os from "os";
15
+
16
+ // src/lib/secure-files.ts
17
+ import { chmodSync, existsSync, mkdirSync } from "fs";
18
+ import { chmod, mkdir } from "fs/promises";
19
+ var PRIVATE_DIR_MODE = 448;
20
+ var PRIVATE_FILE_MODE = 384;
21
+ function ensurePrivateDirSync(dirPath) {
22
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
23
+ try {
24
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
25
+ } catch {
26
+ }
27
+ }
28
+ function enforcePrivateFileSync(filePath) {
29
+ try {
30
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
31
+ } catch {
32
+ }
33
+ }
34
+
35
+ // src/lib/config.ts
15
36
  function resolveDataDir() {
16
37
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
17
38
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
18
39
  const newDir = path.join(os.homedir(), ".exe-os");
19
40
  const legacyDir = path.join(os.homedir(), ".exe-mem");
20
- if (!existsSync(newDir) && existsSync(legacyDir)) {
41
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
21
42
  try {
22
43
  renameSync(legacyDir, newDir);
23
44
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -92,13 +113,43 @@ var DEFAULT_CONFIG = {
92
113
  }
93
114
  };
94
115
 
116
+ // src/lib/daemon-auth.ts
117
+ import crypto from "crypto";
118
+ import path2 from "path";
119
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
120
+ var DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
121
+ function normalizeToken(token) {
122
+ if (!token) return null;
123
+ const trimmed = token.trim();
124
+ return trimmed.length > 0 ? trimmed : null;
125
+ }
126
+ function readDaemonToken() {
127
+ try {
128
+ if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
129
+ return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
130
+ } catch {
131
+ return null;
132
+ }
133
+ }
134
+ function ensureDaemonToken(seed) {
135
+ const existing = readDaemonToken();
136
+ if (existing) return existing;
137
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
138
+ ensurePrivateDirSync(EXE_AI_DIR);
139
+ writeFileSync(DAEMON_TOKEN_PATH, `${token}
140
+ `, "utf8");
141
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
142
+ return token;
143
+ }
144
+
95
145
  // src/lib/exe-daemon-client.ts
96
- var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path2.join(EXE_AI_DIR, "exed.sock");
97
- var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path2.join(EXE_AI_DIR, "exed.pid");
98
- var SPAWN_LOCK_PATH = path2.join(EXE_AI_DIR, "exed-spawn.lock");
146
+ var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
147
+ var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
148
+ var SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
99
149
  var SPAWN_LOCK_STALE_MS = 3e4;
100
150
  var CONNECT_TIMEOUT_MS = 15e3;
101
151
  var REQUEST_TIMEOUT_MS = 3e4;
152
+ var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
102
153
  var _socket = null;
103
154
  var _connected = false;
104
155
  var _buffer = "";
@@ -136,9 +187,9 @@ function handleData(chunk) {
136
187
  }
137
188
  }
138
189
  function cleanupStaleFiles() {
139
- if (existsSync2(PID_PATH)) {
190
+ if (existsSync4(PID_PATH)) {
140
191
  try {
141
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
192
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
142
193
  if (pid > 0) {
143
194
  try {
144
195
  process.kill(pid, 0);
@@ -159,11 +210,11 @@ function cleanupStaleFiles() {
159
210
  }
160
211
  }
161
212
  function findPackageRoot() {
162
- let dir = path2.dirname(fileURLToPath(import.meta.url));
163
- const { root } = path2.parse(dir);
213
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
214
+ const { root } = path3.parse(dir);
164
215
  while (dir !== root) {
165
- if (existsSync2(path2.join(dir, "package.json"))) return dir;
166
- dir = path2.dirname(dir);
216
+ if (existsSync4(path3.join(dir, "package.json"))) return dir;
217
+ dir = path3.dirname(dir);
167
218
  }
168
219
  return null;
169
220
  }
@@ -189,16 +240,17 @@ function spawnDaemon() {
189
240
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
190
241
  return;
191
242
  }
192
- const daemonPath = path2.join(pkgRoot, "dist", "lib", "exe-daemon.js");
193
- if (!existsSync2(daemonPath)) {
243
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
244
+ if (!existsSync4(daemonPath)) {
194
245
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
195
246
  `);
196
247
  return;
197
248
  }
198
249
  const resolvedPath = daemonPath;
250
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
199
251
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
200
252
  `);
201
- const logPath = path2.join(path2.dirname(SOCKET_PATH), "exed.log");
253
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
202
254
  let stderrFd = "ignore";
203
255
  try {
204
256
  stderrFd = openSync(logPath, "a");
@@ -216,7 +268,8 @@ function spawnDaemon() {
216
268
  TMUX_PANE: void 0,
217
269
  // Prevents resolveExeSession() from scoping to one session
218
270
  EXE_DAEMON_SOCK: SOCKET_PATH,
219
- EXE_DAEMON_PID: PID_PATH
271
+ EXE_DAEMON_PID: PID_PATH,
272
+ [DAEMON_TOKEN_ENV]: daemonToken
220
273
  }
221
274
  });
222
275
  child.unref();
@@ -326,13 +379,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
326
379
  return;
327
380
  }
328
381
  const id = randomUUID();
382
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
329
383
  const timer = setTimeout(() => {
330
384
  _pending.delete(id);
331
385
  resolve({ error: "Request timeout" });
332
386
  }, timeoutMs);
333
387
  _pending.set(id, { resolve, timer });
334
388
  try {
335
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
389
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
336
390
  } catch {
337
391
  clearTimeout(timer);
338
392
  _pending.delete(id);
@@ -361,9 +415,9 @@ function killAndRespawnDaemon() {
361
415
  }
362
416
  try {
363
417
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
364
- if (existsSync2(PID_PATH)) {
418
+ if (existsSync4(PID_PATH)) {
365
419
  try {
366
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
420
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
367
421
  if (pid > 0) {
368
422
  try {
369
423
  process.kill(pid, "SIGKILL");
@@ -492,6 +546,17 @@ function disconnectClient() {
492
546
  function isClientConnected() {
493
547
  return _connected;
494
548
  }
549
+ function sendIngestRequest(payload) {
550
+ if (!_socket || !_connected) return false;
551
+ try {
552
+ const id = randomUUID();
553
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
554
+ _socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
555
+ return true;
556
+ } catch {
557
+ return false;
558
+ }
559
+ }
495
560
  export {
496
561
  connectEmbedDaemon,
497
562
  disconnectClient,
@@ -499,5 +564,6 @@ export {
499
564
  embedViaClient,
500
565
  isClientConnected,
501
566
  pingDaemon,
502
- sendDaemonRequest
567
+ sendDaemonRequest,
568
+ sendIngestRequest
503
569
  };