@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
|
@@ -19,9 +19,47 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
21
|
|
|
22
|
+
// src/lib/secure-files.ts
|
|
23
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
24
|
+
import { chmod, mkdir } from "fs/promises";
|
|
25
|
+
async function ensurePrivateDir(dirPath) {
|
|
26
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
27
|
+
try {
|
|
28
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function ensurePrivateDirSync(dirPath) {
|
|
33
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
34
|
+
try {
|
|
35
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function enforcePrivateFile(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function enforcePrivateFileSync(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
52
|
+
var init_secure_files = __esm({
|
|
53
|
+
"src/lib/secure-files.ts"() {
|
|
54
|
+
"use strict";
|
|
55
|
+
PRIVATE_DIR_MODE = 448;
|
|
56
|
+
PRIVATE_FILE_MODE = 384;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
22
60
|
// src/lib/config.ts
|
|
23
|
-
import { readFile, writeFile
|
|
24
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
61
|
+
import { readFile, writeFile } from "fs/promises";
|
|
62
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
25
63
|
import path from "path";
|
|
26
64
|
import os from "os";
|
|
27
65
|
function resolveDataDir() {
|
|
@@ -29,7 +67,7 @@ function resolveDataDir() {
|
|
|
29
67
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
30
68
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
31
69
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
32
|
-
if (!
|
|
70
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
33
71
|
try {
|
|
34
72
|
renameSync(legacyDir, newDir);
|
|
35
73
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -92,9 +130,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
92
130
|
}
|
|
93
131
|
async function loadConfig() {
|
|
94
132
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
95
|
-
await
|
|
133
|
+
await ensurePrivateDir(dir);
|
|
96
134
|
const configPath = path.join(dir, "config.json");
|
|
97
|
-
if (!
|
|
135
|
+
if (!existsSync2(configPath)) {
|
|
98
136
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
99
137
|
}
|
|
100
138
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -107,6 +145,7 @@ async function loadConfig() {
|
|
|
107
145
|
`);
|
|
108
146
|
try {
|
|
109
147
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
148
|
+
await enforcePrivateFile(configPath);
|
|
110
149
|
} catch {
|
|
111
150
|
}
|
|
112
151
|
}
|
|
@@ -126,6 +165,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
126
165
|
var init_config = __esm({
|
|
127
166
|
"src/lib/config.ts"() {
|
|
128
167
|
"use strict";
|
|
168
|
+
init_secure_files();
|
|
129
169
|
EXE_AI_DIR = resolveDataDir();
|
|
130
170
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
131
171
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -272,7 +312,7 @@ var init_session_key = __esm({
|
|
|
272
312
|
|
|
273
313
|
// src/lib/employees.ts
|
|
274
314
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
275
|
-
import { existsSync as
|
|
315
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
276
316
|
import { execSync as execSync2 } from "child_process";
|
|
277
317
|
import path2 from "path";
|
|
278
318
|
import os2 from "os";
|
|
@@ -296,7 +336,7 @@ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
|
296
336
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
297
337
|
}
|
|
298
338
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
299
|
-
if (!
|
|
339
|
+
if (!existsSync3(employeesPath)) return [];
|
|
300
340
|
try {
|
|
301
341
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
302
342
|
} catch {
|
|
@@ -492,7 +532,7 @@ var init_runtime_table = __esm({
|
|
|
492
532
|
});
|
|
493
533
|
|
|
494
534
|
// src/lib/agent-config.ts
|
|
495
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as
|
|
535
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
496
536
|
import path5 from "path";
|
|
497
537
|
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
498
538
|
var init_agent_config = __esm({
|
|
@@ -500,6 +540,7 @@ var init_agent_config = __esm({
|
|
|
500
540
|
"use strict";
|
|
501
541
|
init_config();
|
|
502
542
|
init_runtime_table();
|
|
543
|
+
init_secure_files();
|
|
503
544
|
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
504
545
|
DEFAULT_MODELS = {
|
|
505
546
|
claude: "claude-opus-4",
|
|
@@ -510,7 +551,7 @@ var init_agent_config = __esm({
|
|
|
510
551
|
});
|
|
511
552
|
|
|
512
553
|
// src/lib/intercom-queue.ts
|
|
513
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as
|
|
554
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
514
555
|
import path6 from "path";
|
|
515
556
|
import os4 from "os";
|
|
516
557
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
@@ -1162,13 +1203,50 @@ var init_database_adapter = __esm({
|
|
|
1162
1203
|
}
|
|
1163
1204
|
});
|
|
1164
1205
|
|
|
1206
|
+
// src/lib/daemon-auth.ts
|
|
1207
|
+
import crypto from "crypto";
|
|
1208
|
+
import path8 from "path";
|
|
1209
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1210
|
+
function normalizeToken(token) {
|
|
1211
|
+
if (!token) return null;
|
|
1212
|
+
const trimmed = token.trim();
|
|
1213
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1214
|
+
}
|
|
1215
|
+
function readDaemonToken() {
|
|
1216
|
+
try {
|
|
1217
|
+
if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
|
|
1218
|
+
return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
|
|
1219
|
+
} catch {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function ensureDaemonToken(seed) {
|
|
1224
|
+
const existing = readDaemonToken();
|
|
1225
|
+
if (existing) return existing;
|
|
1226
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1227
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1228
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
1229
|
+
`, "utf8");
|
|
1230
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1231
|
+
return token;
|
|
1232
|
+
}
|
|
1233
|
+
var DAEMON_TOKEN_PATH;
|
|
1234
|
+
var init_daemon_auth = __esm({
|
|
1235
|
+
"src/lib/daemon-auth.ts"() {
|
|
1236
|
+
"use strict";
|
|
1237
|
+
init_config();
|
|
1238
|
+
init_secure_files();
|
|
1239
|
+
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1165
1243
|
// src/lib/exe-daemon-client.ts
|
|
1166
1244
|
import net from "net";
|
|
1167
1245
|
import os6 from "os";
|
|
1168
1246
|
import { spawn } from "child_process";
|
|
1169
1247
|
import { randomUUID } from "crypto";
|
|
1170
|
-
import { existsSync as
|
|
1171
|
-
import
|
|
1248
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
1249
|
+
import path9 from "path";
|
|
1172
1250
|
import { fileURLToPath } from "url";
|
|
1173
1251
|
function handleData(chunk) {
|
|
1174
1252
|
_buffer += chunk.toString();
|
|
@@ -1196,9 +1274,9 @@ function handleData(chunk) {
|
|
|
1196
1274
|
}
|
|
1197
1275
|
}
|
|
1198
1276
|
function cleanupStaleFiles() {
|
|
1199
|
-
if (
|
|
1277
|
+
if (existsSync7(PID_PATH)) {
|
|
1200
1278
|
try {
|
|
1201
|
-
const pid = parseInt(
|
|
1279
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
1202
1280
|
if (pid > 0) {
|
|
1203
1281
|
try {
|
|
1204
1282
|
process.kill(pid, 0);
|
|
@@ -1219,11 +1297,11 @@ function cleanupStaleFiles() {
|
|
|
1219
1297
|
}
|
|
1220
1298
|
}
|
|
1221
1299
|
function findPackageRoot() {
|
|
1222
|
-
let dir =
|
|
1223
|
-
const { root } =
|
|
1300
|
+
let dir = path9.dirname(fileURLToPath(import.meta.url));
|
|
1301
|
+
const { root } = path9.parse(dir);
|
|
1224
1302
|
while (dir !== root) {
|
|
1225
|
-
if (
|
|
1226
|
-
dir =
|
|
1303
|
+
if (existsSync7(path9.join(dir, "package.json"))) return dir;
|
|
1304
|
+
dir = path9.dirname(dir);
|
|
1227
1305
|
}
|
|
1228
1306
|
return null;
|
|
1229
1307
|
}
|
|
@@ -1249,16 +1327,17 @@ function spawnDaemon() {
|
|
|
1249
1327
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1250
1328
|
return;
|
|
1251
1329
|
}
|
|
1252
|
-
const daemonPath =
|
|
1253
|
-
if (!
|
|
1330
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1331
|
+
if (!existsSync7(daemonPath)) {
|
|
1254
1332
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1255
1333
|
`);
|
|
1256
1334
|
return;
|
|
1257
1335
|
}
|
|
1258
1336
|
const resolvedPath = daemonPath;
|
|
1337
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1259
1338
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1260
1339
|
`);
|
|
1261
|
-
const logPath =
|
|
1340
|
+
const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
|
|
1262
1341
|
let stderrFd = "ignore";
|
|
1263
1342
|
try {
|
|
1264
1343
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1276,7 +1355,8 @@ function spawnDaemon() {
|
|
|
1276
1355
|
TMUX_PANE: void 0,
|
|
1277
1356
|
// Prevents resolveExeSession() from scoping to one session
|
|
1278
1357
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1279
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1358
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1359
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1280
1360
|
}
|
|
1281
1361
|
});
|
|
1282
1362
|
child.unref();
|
|
@@ -1383,13 +1463,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1383
1463
|
return;
|
|
1384
1464
|
}
|
|
1385
1465
|
const id = randomUUID();
|
|
1466
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1386
1467
|
const timer = setTimeout(() => {
|
|
1387
1468
|
_pending.delete(id);
|
|
1388
1469
|
resolve({ error: "Request timeout" });
|
|
1389
1470
|
}, timeoutMs);
|
|
1390
1471
|
_pending.set(id, { resolve, timer });
|
|
1391
1472
|
try {
|
|
1392
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1473
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1393
1474
|
} catch {
|
|
1394
1475
|
clearTimeout(timer);
|
|
1395
1476
|
_pending.delete(id);
|
|
@@ -1400,17 +1481,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1400
1481
|
function isClientConnected() {
|
|
1401
1482
|
return _connected;
|
|
1402
1483
|
}
|
|
1403
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1484
|
+
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;
|
|
1404
1485
|
var init_exe_daemon_client = __esm({
|
|
1405
1486
|
"src/lib/exe-daemon-client.ts"() {
|
|
1406
1487
|
"use strict";
|
|
1407
1488
|
init_config();
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1489
|
+
init_daemon_auth();
|
|
1490
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
|
|
1491
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
|
|
1492
|
+
SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1411
1493
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1412
1494
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1413
1495
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1496
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1414
1497
|
_socket = null;
|
|
1415
1498
|
_connected = false;
|
|
1416
1499
|
_buffer = "";
|
|
@@ -1989,6 +2072,7 @@ async function ensureSchema() {
|
|
|
1989
2072
|
project TEXT NOT NULL,
|
|
1990
2073
|
summary TEXT NOT NULL,
|
|
1991
2074
|
task_file TEXT,
|
|
2075
|
+
session_scope TEXT,
|
|
1992
2076
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1993
2077
|
created_at TEXT NOT NULL
|
|
1994
2078
|
);
|
|
@@ -1997,7 +2081,7 @@ async function ensureSchema() {
|
|
|
1997
2081
|
ON notifications(read);
|
|
1998
2082
|
|
|
1999
2083
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2000
|
-
ON notifications(agent_id);
|
|
2084
|
+
ON notifications(agent_id, session_scope);
|
|
2001
2085
|
|
|
2002
2086
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2003
2087
|
ON notifications(task_file);
|
|
@@ -2035,6 +2119,7 @@ async function ensureSchema() {
|
|
|
2035
2119
|
target_agent TEXT NOT NULL,
|
|
2036
2120
|
target_project TEXT,
|
|
2037
2121
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2122
|
+
session_scope TEXT,
|
|
2038
2123
|
content TEXT NOT NULL,
|
|
2039
2124
|
priority TEXT DEFAULT 'normal',
|
|
2040
2125
|
status TEXT DEFAULT 'pending',
|
|
@@ -2048,10 +2133,31 @@ async function ensureSchema() {
|
|
|
2048
2133
|
);
|
|
2049
2134
|
|
|
2050
2135
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2051
|
-
ON messages(target_agent, status);
|
|
2136
|
+
ON messages(target_agent, session_scope, status);
|
|
2052
2137
|
|
|
2053
2138
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2054
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2139
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2140
|
+
`);
|
|
2141
|
+
try {
|
|
2142
|
+
await client.execute({
|
|
2143
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2144
|
+
args: []
|
|
2145
|
+
});
|
|
2146
|
+
} catch {
|
|
2147
|
+
}
|
|
2148
|
+
try {
|
|
2149
|
+
await client.execute({
|
|
2150
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2151
|
+
args: []
|
|
2152
|
+
});
|
|
2153
|
+
} catch {
|
|
2154
|
+
}
|
|
2155
|
+
await client.executeMultiple(`
|
|
2156
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2157
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2158
|
+
|
|
2159
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2160
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2055
2161
|
`);
|
|
2056
2162
|
try {
|
|
2057
2163
|
await client.execute({
|
|
@@ -2635,6 +2741,13 @@ async function ensureSchema() {
|
|
|
2635
2741
|
} catch {
|
|
2636
2742
|
}
|
|
2637
2743
|
}
|
|
2744
|
+
try {
|
|
2745
|
+
await client.execute({
|
|
2746
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2747
|
+
args: []
|
|
2748
|
+
});
|
|
2749
|
+
} catch {
|
|
2750
|
+
}
|
|
2638
2751
|
}
|
|
2639
2752
|
async function disposeDatabase() {
|
|
2640
2753
|
if (_walCheckpointTimer) {
|
|
@@ -2673,24 +2786,27 @@ var init_database = __esm({
|
|
|
2673
2786
|
});
|
|
2674
2787
|
|
|
2675
2788
|
// src/lib/license.ts
|
|
2676
|
-
import { readFileSync as
|
|
2789
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2677
2790
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2678
|
-
import
|
|
2791
|
+
import { createRequire as createRequire2 } from "module";
|
|
2792
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2793
|
+
import os7 from "os";
|
|
2794
|
+
import path10 from "path";
|
|
2679
2795
|
import { jwtVerify, importSPKI } from "jose";
|
|
2680
2796
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
2681
2797
|
var init_license = __esm({
|
|
2682
2798
|
"src/lib/license.ts"() {
|
|
2683
2799
|
"use strict";
|
|
2684
2800
|
init_config();
|
|
2685
|
-
LICENSE_PATH =
|
|
2686
|
-
CACHE_PATH =
|
|
2687
|
-
DEVICE_ID_PATH =
|
|
2801
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
2802
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2803
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
2688
2804
|
}
|
|
2689
2805
|
});
|
|
2690
2806
|
|
|
2691
2807
|
// src/lib/plan-limits.ts
|
|
2692
|
-
import { readFileSync as
|
|
2693
|
-
import
|
|
2808
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2809
|
+
import path11 from "path";
|
|
2694
2810
|
var CACHE_PATH2;
|
|
2695
2811
|
var init_plan_limits = __esm({
|
|
2696
2812
|
"src/lib/plan-limits.ts"() {
|
|
@@ -2699,14 +2815,14 @@ var init_plan_limits = __esm({
|
|
|
2699
2815
|
init_employees();
|
|
2700
2816
|
init_license();
|
|
2701
2817
|
init_config();
|
|
2702
|
-
CACHE_PATH2 =
|
|
2818
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
2703
2819
|
}
|
|
2704
2820
|
});
|
|
2705
2821
|
|
|
2706
2822
|
// src/lib/tmux-routing.ts
|
|
2707
|
-
import { readFileSync as
|
|
2708
|
-
import
|
|
2709
|
-
import
|
|
2823
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync, readdirSync as readdirSync2 } from "fs";
|
|
2824
|
+
import path12 from "path";
|
|
2825
|
+
import os8 from "os";
|
|
2710
2826
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2711
2827
|
function getMySession() {
|
|
2712
2828
|
return getTransport().getMySession();
|
|
@@ -2719,7 +2835,7 @@ function extractRootExe(name) {
|
|
|
2719
2835
|
}
|
|
2720
2836
|
function getParentExe(sessionKey) {
|
|
2721
2837
|
try {
|
|
2722
|
-
const data = JSON.parse(
|
|
2838
|
+
const data = JSON.parse(readFileSync10(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2723
2839
|
return data.parentExe || null;
|
|
2724
2840
|
} catch {
|
|
2725
2841
|
return null;
|
|
@@ -2762,10 +2878,10 @@ var init_tmux_routing = __esm({
|
|
|
2762
2878
|
init_intercom_queue();
|
|
2763
2879
|
init_plan_limits();
|
|
2764
2880
|
init_employees();
|
|
2765
|
-
SPAWN_LOCK_DIR =
|
|
2766
|
-
SESSION_CACHE =
|
|
2767
|
-
INTERCOM_LOG2 =
|
|
2768
|
-
DEBOUNCE_FILE =
|
|
2881
|
+
SPAWN_LOCK_DIR = path12.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
2882
|
+
SESSION_CACHE = path12.join(os8.homedir(), ".exe-os", "session-cache");
|
|
2883
|
+
INTERCOM_LOG2 = path12.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
2884
|
+
DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2769
2885
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2770
2886
|
}
|
|
2771
2887
|
});
|
|
@@ -2805,14 +2921,14 @@ var init_memory = __esm({
|
|
|
2805
2921
|
|
|
2806
2922
|
// src/lib/keychain.ts
|
|
2807
2923
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2808
|
-
import { existsSync as
|
|
2809
|
-
import
|
|
2810
|
-
import
|
|
2924
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2925
|
+
import path13 from "path";
|
|
2926
|
+
import os9 from "os";
|
|
2811
2927
|
function getKeyDir() {
|
|
2812
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2928
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path13.join(os9.homedir(), ".exe-os");
|
|
2813
2929
|
}
|
|
2814
2930
|
function getKeyPath() {
|
|
2815
|
-
return
|
|
2931
|
+
return path13.join(getKeyDir(), "master.key");
|
|
2816
2932
|
}
|
|
2817
2933
|
async function tryKeytar() {
|
|
2818
2934
|
try {
|
|
@@ -2833,9 +2949,9 @@ async function getMasterKey() {
|
|
|
2833
2949
|
}
|
|
2834
2950
|
}
|
|
2835
2951
|
const keyPath = getKeyPath();
|
|
2836
|
-
if (!
|
|
2952
|
+
if (!existsSync11(keyPath)) {
|
|
2837
2953
|
process.stderr.write(
|
|
2838
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
2954
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2839
2955
|
`
|
|
2840
2956
|
);
|
|
2841
2957
|
return null;
|
|
@@ -2920,6 +3036,7 @@ var shard_manager_exports = {};
|
|
|
2920
3036
|
__export(shard_manager_exports, {
|
|
2921
3037
|
disposeShards: () => disposeShards,
|
|
2922
3038
|
ensureShardSchema: () => ensureShardSchema,
|
|
3039
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2923
3040
|
getReadyShardClient: () => getReadyShardClient,
|
|
2924
3041
|
getShardClient: () => getShardClient,
|
|
2925
3042
|
getShardsDir: () => getShardsDir,
|
|
@@ -2928,15 +3045,18 @@ __export(shard_manager_exports, {
|
|
|
2928
3045
|
listShards: () => listShards,
|
|
2929
3046
|
shardExists: () => shardExists
|
|
2930
3047
|
});
|
|
2931
|
-
import
|
|
2932
|
-
import { existsSync as
|
|
3048
|
+
import path14 from "path";
|
|
3049
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
|
|
2933
3050
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2934
3051
|
function initShardManager(encryptionKey) {
|
|
2935
3052
|
_encryptionKey = encryptionKey;
|
|
2936
|
-
if (!
|
|
3053
|
+
if (!existsSync12(SHARDS_DIR)) {
|
|
2937
3054
|
mkdirSync6(SHARDS_DIR, { recursive: true });
|
|
2938
3055
|
}
|
|
2939
3056
|
_shardingEnabled = true;
|
|
3057
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3058
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3059
|
+
_evictionTimer.unref();
|
|
2940
3060
|
}
|
|
2941
3061
|
function isShardingEnabled() {
|
|
2942
3062
|
return _shardingEnabled;
|
|
@@ -2953,21 +3073,28 @@ function getShardClient(projectName) {
|
|
|
2953
3073
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2954
3074
|
}
|
|
2955
3075
|
const cached = _shards.get(safeName);
|
|
2956
|
-
if (cached)
|
|
2957
|
-
|
|
3076
|
+
if (cached) {
|
|
3077
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3078
|
+
return cached;
|
|
3079
|
+
}
|
|
3080
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3081
|
+
evictLRU();
|
|
3082
|
+
}
|
|
3083
|
+
const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
|
|
2958
3084
|
const client = createClient2({
|
|
2959
3085
|
url: `file:${dbPath}`,
|
|
2960
3086
|
encryptionKey: _encryptionKey
|
|
2961
3087
|
});
|
|
2962
3088
|
_shards.set(safeName, client);
|
|
3089
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2963
3090
|
return client;
|
|
2964
3091
|
}
|
|
2965
3092
|
function shardExists(projectName) {
|
|
2966
3093
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2967
|
-
return
|
|
3094
|
+
return existsSync12(path14.join(SHARDS_DIR, `${safeName}.db`));
|
|
2968
3095
|
}
|
|
2969
3096
|
function listShards() {
|
|
2970
|
-
if (!
|
|
3097
|
+
if (!existsSync12(SHARDS_DIR)) return [];
|
|
2971
3098
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2972
3099
|
}
|
|
2973
3100
|
async function ensureShardSchema(client) {
|
|
@@ -3019,6 +3146,8 @@ async function ensureShardSchema(client) {
|
|
|
3019
3146
|
for (const col of [
|
|
3020
3147
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
3021
3148
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3149
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3150
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
3022
3151
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
3023
3152
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
3024
3153
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -3156,21 +3285,69 @@ async function getReadyShardClient(projectName) {
|
|
|
3156
3285
|
await ensureShardSchema(client);
|
|
3157
3286
|
return client;
|
|
3158
3287
|
}
|
|
3288
|
+
function evictLRU() {
|
|
3289
|
+
let oldest = null;
|
|
3290
|
+
let oldestTime = Infinity;
|
|
3291
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3292
|
+
if (time < oldestTime) {
|
|
3293
|
+
oldestTime = time;
|
|
3294
|
+
oldest = name;
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
if (oldest) {
|
|
3298
|
+
const client = _shards.get(oldest);
|
|
3299
|
+
if (client) {
|
|
3300
|
+
client.close();
|
|
3301
|
+
}
|
|
3302
|
+
_shards.delete(oldest);
|
|
3303
|
+
_shardLastAccess.delete(oldest);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
function evictIdleShards() {
|
|
3307
|
+
const now = Date.now();
|
|
3308
|
+
const toEvict = [];
|
|
3309
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3310
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3311
|
+
toEvict.push(name);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
for (const name of toEvict) {
|
|
3315
|
+
const client = _shards.get(name);
|
|
3316
|
+
if (client) {
|
|
3317
|
+
client.close();
|
|
3318
|
+
}
|
|
3319
|
+
_shards.delete(name);
|
|
3320
|
+
_shardLastAccess.delete(name);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
function getOpenShardCount() {
|
|
3324
|
+
return _shards.size;
|
|
3325
|
+
}
|
|
3159
3326
|
function disposeShards() {
|
|
3327
|
+
if (_evictionTimer) {
|
|
3328
|
+
clearInterval(_evictionTimer);
|
|
3329
|
+
_evictionTimer = null;
|
|
3330
|
+
}
|
|
3160
3331
|
for (const [, client] of _shards) {
|
|
3161
3332
|
client.close();
|
|
3162
3333
|
}
|
|
3163
3334
|
_shards.clear();
|
|
3335
|
+
_shardLastAccess.clear();
|
|
3164
3336
|
_shardingEnabled = false;
|
|
3165
3337
|
_encryptionKey = null;
|
|
3166
3338
|
}
|
|
3167
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3339
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3168
3340
|
var init_shard_manager = __esm({
|
|
3169
3341
|
"src/lib/shard-manager.ts"() {
|
|
3170
3342
|
"use strict";
|
|
3171
3343
|
init_config();
|
|
3172
|
-
SHARDS_DIR =
|
|
3344
|
+
SHARDS_DIR = path14.join(EXE_AI_DIR, "shards");
|
|
3345
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3346
|
+
MAX_OPEN_SHARDS = 10;
|
|
3347
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3173
3348
|
_shards = /* @__PURE__ */ new Map();
|
|
3349
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3350
|
+
_evictionTimer = null;
|
|
3174
3351
|
_encryptionKey = null;
|
|
3175
3352
|
_shardingEnabled = false;
|
|
3176
3353
|
}
|
|
@@ -3937,7 +4114,7 @@ var init_store = __esm({
|
|
|
3937
4114
|
init_config();
|
|
3938
4115
|
init_session_key();
|
|
3939
4116
|
init_employees();
|
|
3940
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
4117
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
3941
4118
|
import { execSync as execSync3 } from "child_process";
|
|
3942
4119
|
import path3 from "path";
|
|
3943
4120
|
var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
|