@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/db.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");
|
|
@@ -99,13 +125,50 @@ var init_config = __esm({
|
|
|
99
125
|
}
|
|
100
126
|
});
|
|
101
127
|
|
|
128
|
+
// src/lib/daemon-auth.ts
|
|
129
|
+
import crypto from "crypto";
|
|
130
|
+
import path4 from "path";
|
|
131
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
132
|
+
function normalizeToken(token) {
|
|
133
|
+
if (!token) return null;
|
|
134
|
+
const trimmed = token.trim();
|
|
135
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
136
|
+
}
|
|
137
|
+
function readDaemonToken() {
|
|
138
|
+
try {
|
|
139
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
140
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function ensureDaemonToken(seed) {
|
|
146
|
+
const existing = readDaemonToken();
|
|
147
|
+
if (existing) return existing;
|
|
148
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
149
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
150
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
151
|
+
`, "utf8");
|
|
152
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
153
|
+
return token;
|
|
154
|
+
}
|
|
155
|
+
var DAEMON_TOKEN_PATH;
|
|
156
|
+
var init_daemon_auth = __esm({
|
|
157
|
+
"src/lib/daemon-auth.ts"() {
|
|
158
|
+
"use strict";
|
|
159
|
+
init_config();
|
|
160
|
+
init_secure_files();
|
|
161
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
102
165
|
// src/lib/exe-daemon-client.ts
|
|
103
166
|
import net from "net";
|
|
104
167
|
import os4 from "os";
|
|
105
168
|
import { spawn } from "child_process";
|
|
106
169
|
import { randomUUID } from "crypto";
|
|
107
|
-
import { existsSync as
|
|
108
|
-
import
|
|
170
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
171
|
+
import path5 from "path";
|
|
109
172
|
import { fileURLToPath } from "url";
|
|
110
173
|
function handleData(chunk) {
|
|
111
174
|
_buffer += chunk.toString();
|
|
@@ -133,9 +196,9 @@ function handleData(chunk) {
|
|
|
133
196
|
}
|
|
134
197
|
}
|
|
135
198
|
function cleanupStaleFiles() {
|
|
136
|
-
if (
|
|
199
|
+
if (existsSync5(PID_PATH)) {
|
|
137
200
|
try {
|
|
138
|
-
const pid = parseInt(
|
|
201
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
139
202
|
if (pid > 0) {
|
|
140
203
|
try {
|
|
141
204
|
process.kill(pid, 0);
|
|
@@ -156,11 +219,11 @@ function cleanupStaleFiles() {
|
|
|
156
219
|
}
|
|
157
220
|
}
|
|
158
221
|
function findPackageRoot() {
|
|
159
|
-
let dir =
|
|
160
|
-
const { root } =
|
|
222
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
223
|
+
const { root } = path5.parse(dir);
|
|
161
224
|
while (dir !== root) {
|
|
162
|
-
if (
|
|
163
|
-
dir =
|
|
225
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
226
|
+
dir = path5.dirname(dir);
|
|
164
227
|
}
|
|
165
228
|
return null;
|
|
166
229
|
}
|
|
@@ -186,16 +249,17 @@ function spawnDaemon() {
|
|
|
186
249
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
187
250
|
return;
|
|
188
251
|
}
|
|
189
|
-
const daemonPath =
|
|
190
|
-
if (!
|
|
252
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
253
|
+
if (!existsSync5(daemonPath)) {
|
|
191
254
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
192
255
|
`);
|
|
193
256
|
return;
|
|
194
257
|
}
|
|
195
258
|
const resolvedPath = daemonPath;
|
|
259
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
196
260
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
197
261
|
`);
|
|
198
|
-
const logPath =
|
|
262
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
199
263
|
let stderrFd = "ignore";
|
|
200
264
|
try {
|
|
201
265
|
stderrFd = openSync(logPath, "a");
|
|
@@ -213,7 +277,8 @@ function spawnDaemon() {
|
|
|
213
277
|
TMUX_PANE: void 0,
|
|
214
278
|
// Prevents resolveExeSession() from scoping to one session
|
|
215
279
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
216
|
-
EXE_DAEMON_PID: PID_PATH
|
|
280
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
281
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
217
282
|
}
|
|
218
283
|
});
|
|
219
284
|
child.unref();
|
|
@@ -320,13 +385,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
320
385
|
return;
|
|
321
386
|
}
|
|
322
387
|
const id = randomUUID();
|
|
388
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
323
389
|
const timer = setTimeout(() => {
|
|
324
390
|
_pending.delete(id);
|
|
325
391
|
resolve({ error: "Request timeout" });
|
|
326
392
|
}, timeoutMs);
|
|
327
393
|
_pending.set(id, { resolve, timer });
|
|
328
394
|
try {
|
|
329
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
395
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
330
396
|
} catch {
|
|
331
397
|
clearTimeout(timer);
|
|
332
398
|
_pending.delete(id);
|
|
@@ -337,17 +403,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
337
403
|
function isClientConnected() {
|
|
338
404
|
return _connected;
|
|
339
405
|
}
|
|
340
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
406
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
341
407
|
var init_exe_daemon_client = __esm({
|
|
342
408
|
"src/lib/exe-daemon-client.ts"() {
|
|
343
409
|
"use strict";
|
|
344
410
|
init_config();
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
411
|
+
init_daemon_auth();
|
|
412
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
413
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
414
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
348
415
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
349
416
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
350
417
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
418
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
351
419
|
_socket = null;
|
|
352
420
|
_connected = false;
|
|
353
421
|
_buffer = "";
|
|
@@ -610,7 +678,7 @@ function wrapWithRetry(client) {
|
|
|
610
678
|
// src/lib/employees.ts
|
|
611
679
|
init_config();
|
|
612
680
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
613
|
-
import { existsSync as
|
|
681
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
614
682
|
import { execSync } from "child_process";
|
|
615
683
|
import path2 from "path";
|
|
616
684
|
import os2 from "os";
|
|
@@ -630,7 +698,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
630
698
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
631
699
|
}
|
|
632
700
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
633
|
-
if (!
|
|
701
|
+
if (!existsSync3(employeesPath)) return [];
|
|
634
702
|
try {
|
|
635
703
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
636
704
|
} catch {
|
|
@@ -1581,6 +1649,7 @@ async function ensureSchema() {
|
|
|
1581
1649
|
project TEXT NOT NULL,
|
|
1582
1650
|
summary TEXT NOT NULL,
|
|
1583
1651
|
task_file TEXT,
|
|
1652
|
+
session_scope TEXT,
|
|
1584
1653
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1585
1654
|
created_at TEXT NOT NULL
|
|
1586
1655
|
);
|
|
@@ -1589,7 +1658,7 @@ async function ensureSchema() {
|
|
|
1589
1658
|
ON notifications(read);
|
|
1590
1659
|
|
|
1591
1660
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1592
|
-
ON notifications(agent_id);
|
|
1661
|
+
ON notifications(agent_id, session_scope);
|
|
1593
1662
|
|
|
1594
1663
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1595
1664
|
ON notifications(task_file);
|
|
@@ -1627,6 +1696,7 @@ async function ensureSchema() {
|
|
|
1627
1696
|
target_agent TEXT NOT NULL,
|
|
1628
1697
|
target_project TEXT,
|
|
1629
1698
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1699
|
+
session_scope TEXT,
|
|
1630
1700
|
content TEXT NOT NULL,
|
|
1631
1701
|
priority TEXT DEFAULT 'normal',
|
|
1632
1702
|
status TEXT DEFAULT 'pending',
|
|
@@ -1640,10 +1710,31 @@ async function ensureSchema() {
|
|
|
1640
1710
|
);
|
|
1641
1711
|
|
|
1642
1712
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1643
|
-
ON messages(target_agent, status);
|
|
1713
|
+
ON messages(target_agent, session_scope, status);
|
|
1644
1714
|
|
|
1645
1715
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1646
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1716
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1717
|
+
`);
|
|
1718
|
+
try {
|
|
1719
|
+
await client.execute({
|
|
1720
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1721
|
+
args: []
|
|
1722
|
+
});
|
|
1723
|
+
} catch {
|
|
1724
|
+
}
|
|
1725
|
+
try {
|
|
1726
|
+
await client.execute({
|
|
1727
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1728
|
+
args: []
|
|
1729
|
+
});
|
|
1730
|
+
} catch {
|
|
1731
|
+
}
|
|
1732
|
+
await client.executeMultiple(`
|
|
1733
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1734
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1735
|
+
|
|
1736
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1737
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1647
1738
|
`);
|
|
1648
1739
|
try {
|
|
1649
1740
|
await client.execute({
|
|
@@ -2227,6 +2318,13 @@ async function ensureSchema() {
|
|
|
2227
2318
|
} catch {
|
|
2228
2319
|
}
|
|
2229
2320
|
}
|
|
2321
|
+
try {
|
|
2322
|
+
await client.execute({
|
|
2323
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2324
|
+
args: []
|
|
2325
|
+
});
|
|
2326
|
+
} catch {
|
|
2327
|
+
}
|
|
2230
2328
|
}
|
|
2231
2329
|
var disposeTurso = disposeDatabase;
|
|
2232
2330
|
async function disposeDatabase() {
|
|
@@ -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");
|
|
@@ -156,7 +182,7 @@ var init_db_retry = __esm({
|
|
|
156
182
|
|
|
157
183
|
// src/lib/employees.ts
|
|
158
184
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
159
|
-
import { existsSync as
|
|
185
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
160
186
|
import { execSync } from "child_process";
|
|
161
187
|
import path2 from "path";
|
|
162
188
|
import os2 from "os";
|
|
@@ -173,7 +199,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
173
199
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
174
200
|
}
|
|
175
201
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
176
|
-
if (!
|
|
202
|
+
if (!existsSync3(employeesPath)) return [];
|
|
177
203
|
try {
|
|
178
204
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
179
205
|
} catch {
|
|
@@ -776,13 +802,50 @@ var init_database_adapter = __esm({
|
|
|
776
802
|
}
|
|
777
803
|
});
|
|
778
804
|
|
|
805
|
+
// src/lib/daemon-auth.ts
|
|
806
|
+
import crypto from "crypto";
|
|
807
|
+
import path4 from "path";
|
|
808
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
809
|
+
function normalizeToken(token) {
|
|
810
|
+
if (!token) return null;
|
|
811
|
+
const trimmed = token.trim();
|
|
812
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
813
|
+
}
|
|
814
|
+
function readDaemonToken() {
|
|
815
|
+
try {
|
|
816
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
817
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
818
|
+
} catch {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
function ensureDaemonToken(seed) {
|
|
823
|
+
const existing = readDaemonToken();
|
|
824
|
+
if (existing) return existing;
|
|
825
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
826
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
827
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
828
|
+
`, "utf8");
|
|
829
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
830
|
+
return token;
|
|
831
|
+
}
|
|
832
|
+
var DAEMON_TOKEN_PATH;
|
|
833
|
+
var init_daemon_auth = __esm({
|
|
834
|
+
"src/lib/daemon-auth.ts"() {
|
|
835
|
+
"use strict";
|
|
836
|
+
init_config();
|
|
837
|
+
init_secure_files();
|
|
838
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
779
842
|
// src/lib/exe-daemon-client.ts
|
|
780
843
|
import net from "net";
|
|
781
844
|
import os4 from "os";
|
|
782
845
|
import { spawn } from "child_process";
|
|
783
846
|
import { randomUUID } from "crypto";
|
|
784
|
-
import { existsSync as
|
|
785
|
-
import
|
|
847
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
848
|
+
import path5 from "path";
|
|
786
849
|
import { fileURLToPath } from "url";
|
|
787
850
|
function handleData(chunk) {
|
|
788
851
|
_buffer += chunk.toString();
|
|
@@ -810,9 +873,9 @@ function handleData(chunk) {
|
|
|
810
873
|
}
|
|
811
874
|
}
|
|
812
875
|
function cleanupStaleFiles() {
|
|
813
|
-
if (
|
|
876
|
+
if (existsSync5(PID_PATH)) {
|
|
814
877
|
try {
|
|
815
|
-
const pid = parseInt(
|
|
878
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
816
879
|
if (pid > 0) {
|
|
817
880
|
try {
|
|
818
881
|
process.kill(pid, 0);
|
|
@@ -833,11 +896,11 @@ function cleanupStaleFiles() {
|
|
|
833
896
|
}
|
|
834
897
|
}
|
|
835
898
|
function findPackageRoot() {
|
|
836
|
-
let dir =
|
|
837
|
-
const { root } =
|
|
899
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
900
|
+
const { root } = path5.parse(dir);
|
|
838
901
|
while (dir !== root) {
|
|
839
|
-
if (
|
|
840
|
-
dir =
|
|
902
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
903
|
+
dir = path5.dirname(dir);
|
|
841
904
|
}
|
|
842
905
|
return null;
|
|
843
906
|
}
|
|
@@ -863,16 +926,17 @@ function spawnDaemon() {
|
|
|
863
926
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
864
927
|
return;
|
|
865
928
|
}
|
|
866
|
-
const daemonPath =
|
|
867
|
-
if (!
|
|
929
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
930
|
+
if (!existsSync5(daemonPath)) {
|
|
868
931
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
869
932
|
`);
|
|
870
933
|
return;
|
|
871
934
|
}
|
|
872
935
|
const resolvedPath = daemonPath;
|
|
936
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
873
937
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
874
938
|
`);
|
|
875
|
-
const logPath =
|
|
939
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
876
940
|
let stderrFd = "ignore";
|
|
877
941
|
try {
|
|
878
942
|
stderrFd = openSync(logPath, "a");
|
|
@@ -890,7 +954,8 @@ function spawnDaemon() {
|
|
|
890
954
|
TMUX_PANE: void 0,
|
|
891
955
|
// Prevents resolveExeSession() from scoping to one session
|
|
892
956
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
893
|
-
EXE_DAEMON_PID: PID_PATH
|
|
957
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
958
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
894
959
|
}
|
|
895
960
|
});
|
|
896
961
|
child.unref();
|
|
@@ -997,13 +1062,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
997
1062
|
return;
|
|
998
1063
|
}
|
|
999
1064
|
const id = randomUUID();
|
|
1065
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1000
1066
|
const timer = setTimeout(() => {
|
|
1001
1067
|
_pending.delete(id);
|
|
1002
1068
|
resolve({ error: "Request timeout" });
|
|
1003
1069
|
}, timeoutMs);
|
|
1004
1070
|
_pending.set(id, { resolve, timer });
|
|
1005
1071
|
try {
|
|
1006
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1072
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1007
1073
|
} catch {
|
|
1008
1074
|
clearTimeout(timer);
|
|
1009
1075
|
_pending.delete(id);
|
|
@@ -1014,17 +1080,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1014
1080
|
function isClientConnected() {
|
|
1015
1081
|
return _connected;
|
|
1016
1082
|
}
|
|
1017
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1083
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1018
1084
|
var init_exe_daemon_client = __esm({
|
|
1019
1085
|
"src/lib/exe-daemon-client.ts"() {
|
|
1020
1086
|
"use strict";
|
|
1021
1087
|
init_config();
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1088
|
+
init_daemon_auth();
|
|
1089
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1090
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1091
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1025
1092
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1026
1093
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1027
1094
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1095
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1028
1096
|
_socket = null;
|
|
1029
1097
|
_connected = false;
|
|
1030
1098
|
_buffer = "";
|
|
@@ -1603,6 +1671,7 @@ async function ensureSchema() {
|
|
|
1603
1671
|
project TEXT NOT NULL,
|
|
1604
1672
|
summary TEXT NOT NULL,
|
|
1605
1673
|
task_file TEXT,
|
|
1674
|
+
session_scope TEXT,
|
|
1606
1675
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1607
1676
|
created_at TEXT NOT NULL
|
|
1608
1677
|
);
|
|
@@ -1611,7 +1680,7 @@ async function ensureSchema() {
|
|
|
1611
1680
|
ON notifications(read);
|
|
1612
1681
|
|
|
1613
1682
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1614
|
-
ON notifications(agent_id);
|
|
1683
|
+
ON notifications(agent_id, session_scope);
|
|
1615
1684
|
|
|
1616
1685
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1617
1686
|
ON notifications(task_file);
|
|
@@ -1649,6 +1718,7 @@ async function ensureSchema() {
|
|
|
1649
1718
|
target_agent TEXT NOT NULL,
|
|
1650
1719
|
target_project TEXT,
|
|
1651
1720
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1721
|
+
session_scope TEXT,
|
|
1652
1722
|
content TEXT NOT NULL,
|
|
1653
1723
|
priority TEXT DEFAULT 'normal',
|
|
1654
1724
|
status TEXT DEFAULT 'pending',
|
|
@@ -1662,10 +1732,31 @@ async function ensureSchema() {
|
|
|
1662
1732
|
);
|
|
1663
1733
|
|
|
1664
1734
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1665
|
-
ON messages(target_agent, status);
|
|
1735
|
+
ON messages(target_agent, session_scope, status);
|
|
1666
1736
|
|
|
1667
1737
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1668
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1738
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1739
|
+
`);
|
|
1740
|
+
try {
|
|
1741
|
+
await client.execute({
|
|
1742
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1743
|
+
args: []
|
|
1744
|
+
});
|
|
1745
|
+
} catch {
|
|
1746
|
+
}
|
|
1747
|
+
try {
|
|
1748
|
+
await client.execute({
|
|
1749
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1750
|
+
args: []
|
|
1751
|
+
});
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
await client.executeMultiple(`
|
|
1755
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1756
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1757
|
+
|
|
1758
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1759
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1669
1760
|
`);
|
|
1670
1761
|
try {
|
|
1671
1762
|
await client.execute({
|
|
@@ -2249,6 +2340,13 @@ async function ensureSchema() {
|
|
|
2249
2340
|
} catch {
|
|
2250
2341
|
}
|
|
2251
2342
|
}
|
|
2343
|
+
try {
|
|
2344
|
+
await client.execute({
|
|
2345
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2346
|
+
args: []
|
|
2347
|
+
});
|
|
2348
|
+
} catch {
|
|
2349
|
+
}
|
|
2252
2350
|
}
|
|
2253
2351
|
async function disposeDatabase() {
|
|
2254
2352
|
if (_walCheckpointTimer) {
|
|
@@ -2288,15 +2386,15 @@ var init_database = __esm({
|
|
|
2288
2386
|
|
|
2289
2387
|
// src/lib/device-registry.ts
|
|
2290
2388
|
init_config();
|
|
2291
|
-
import
|
|
2389
|
+
import crypto2 from "crypto";
|
|
2292
2390
|
import os5 from "os";
|
|
2293
|
-
import { readFileSync as
|
|
2294
|
-
import
|
|
2295
|
-
var DEVICE_JSON_PATH =
|
|
2391
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
|
|
2392
|
+
import path6 from "path";
|
|
2393
|
+
var DEVICE_JSON_PATH = path6.join(EXE_AI_DIR, "device.json");
|
|
2296
2394
|
function getDeviceInfo() {
|
|
2297
|
-
if (
|
|
2395
|
+
if (existsSync6(DEVICE_JSON_PATH)) {
|
|
2298
2396
|
try {
|
|
2299
|
-
const raw =
|
|
2397
|
+
const raw = readFileSync5(DEVICE_JSON_PATH, "utf8");
|
|
2300
2398
|
const data = JSON.parse(raw);
|
|
2301
2399
|
if (data.deviceId && data.friendlyName && data.hostname) {
|
|
2302
2400
|
return data;
|
|
@@ -2306,18 +2404,18 @@ function getDeviceInfo() {
|
|
|
2306
2404
|
}
|
|
2307
2405
|
const hostname = os5.hostname();
|
|
2308
2406
|
const info = {
|
|
2309
|
-
deviceId:
|
|
2407
|
+
deviceId: crypto2.randomUUID(),
|
|
2310
2408
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
2311
2409
|
hostname
|
|
2312
2410
|
};
|
|
2313
|
-
|
|
2314
|
-
|
|
2411
|
+
mkdirSync2(path6.dirname(DEVICE_JSON_PATH), { recursive: true });
|
|
2412
|
+
writeFileSync3(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
2315
2413
|
return info;
|
|
2316
2414
|
}
|
|
2317
2415
|
function setFriendlyName(name) {
|
|
2318
2416
|
const info = getDeviceInfo();
|
|
2319
2417
|
info.friendlyName = name;
|
|
2320
|
-
|
|
2418
|
+
writeFileSync3(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
2321
2419
|
}
|
|
2322
2420
|
async function resolveTargetDevice(targetAgent, targetProject) {
|
|
2323
2421
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|