@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.
- package/dist/bin/backfill-conversations.js +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
package/dist/lib/embedder.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
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
|
|
137
|
+
await ensurePrivateDir(dir);
|
|
100
138
|
const configPath = path.join(dir, "config.json");
|
|
101
|
-
if (!
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
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
|
|
261
|
-
import
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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 (
|
|
378
|
+
if (existsSync4(PID_PATH)) {
|
|
307
379
|
try {
|
|
308
|
-
const pid = parseInt(
|
|
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 =
|
|
330
|
-
const { root } =
|
|
401
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
402
|
+
const { root } = path3.parse(dir);
|
|
331
403
|
while (dir !== root) {
|
|
332
|
-
if (
|
|
333
|
-
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 =
|
|
360
|
-
if (!
|
|
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 =
|
|
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 (
|
|
606
|
+
if (existsSync4(PID_PATH)) {
|
|
532
607
|
try {
|
|
533
|
-
const pid = parseInt(
|
|
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:
|
|
682
|
-
const
|
|
683
|
-
const modelPath =
|
|
684
|
-
if (!
|
|
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
|
|
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 (!
|
|
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
|
|
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";
|
package/dist/lib/employees.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
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
|
|
168
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
143
169
|
import path2 from "path";
|
|
144
170
|
function loadAgentConfig() {
|
|
145
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
407
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
380
408
|
renameSync2(oldPath, newPath);
|
|
381
|
-
} else if (
|
|
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 (
|
|
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
|
|
7
|
-
import
|
|
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
|
|
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 (!
|
|
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 ??
|
|
97
|
-
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
98
|
-
var SPAWN_LOCK_PATH =
|
|
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 (
|
|
190
|
+
if (existsSync4(PID_PATH)) {
|
|
140
191
|
try {
|
|
141
|
-
const pid = parseInt(
|
|
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 =
|
|
163
|
-
const { root } =
|
|
213
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
214
|
+
const { root } = path3.parse(dir);
|
|
164
215
|
while (dir !== root) {
|
|
165
|
-
if (
|
|
166
|
-
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 =
|
|
193
|
-
if (!
|
|
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 =
|
|
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 (
|
|
418
|
+
if (existsSync4(PID_PATH)) {
|
|
365
419
|
try {
|
|
366
|
-
const pid = parseInt(
|
|
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
|
};
|