@askexenow/exe-os 0.9.8 → 0.9.10
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 +1411 -953
- 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 +913 -543
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +418 -262
- 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 +793 -485
- 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 +566 -357
- 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 +530 -319
- 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 +547 -336
- 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 +649 -417
- package/dist/hooks/bug-report-worker.js +486 -316
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +528 -317
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3442 -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 +534 -323
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +614 -382
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +569 -347
- 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 +664 -431
- 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 +1049 -680
- 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 +422 -357
- package/dist/lib/tmux-routing.js +314 -248
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1408 -672
- 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 +448 -371
- 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 +1983 -315
- package/dist/runtime/index.js +567 -355
- package/dist/tui/App.js +887 -531
- package/package.json +4 -4
package/dist/mcp/server.js
CHANGED
|
@@ -34,6 +34,44 @@ var init_memory = __esm({
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
// src/lib/secure-files.ts
|
|
38
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
39
|
+
import { chmod, mkdir } from "fs/promises";
|
|
40
|
+
async function ensurePrivateDir(dirPath) {
|
|
41
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
42
|
+
try {
|
|
43
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function ensurePrivateDirSync(dirPath) {
|
|
48
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
49
|
+
try {
|
|
50
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function enforcePrivateFile(filePath) {
|
|
55
|
+
try {
|
|
56
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function enforcePrivateFileSync(filePath) {
|
|
61
|
+
try {
|
|
62
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
67
|
+
var init_secure_files = __esm({
|
|
68
|
+
"src/lib/secure-files.ts"() {
|
|
69
|
+
"use strict";
|
|
70
|
+
PRIVATE_DIR_MODE = 448;
|
|
71
|
+
PRIVATE_FILE_MODE = 384;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
37
75
|
// src/lib/config.ts
|
|
38
76
|
var config_exports = {};
|
|
39
77
|
__export(config_exports, {
|
|
@@ -50,8 +88,8 @@ __export(config_exports, {
|
|
|
50
88
|
migrateConfig: () => migrateConfig,
|
|
51
89
|
saveConfig: () => saveConfig
|
|
52
90
|
});
|
|
53
|
-
import { readFile, writeFile
|
|
54
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
91
|
+
import { readFile, writeFile } from "fs/promises";
|
|
92
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
55
93
|
import path from "path";
|
|
56
94
|
import os from "os";
|
|
57
95
|
function resolveDataDir() {
|
|
@@ -59,7 +97,7 @@ function resolveDataDir() {
|
|
|
59
97
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
60
98
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
61
99
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
62
|
-
if (!
|
|
100
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
63
101
|
try {
|
|
64
102
|
renameSync(legacyDir, newDir);
|
|
65
103
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -122,9 +160,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
122
160
|
}
|
|
123
161
|
async function loadConfig() {
|
|
124
162
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
125
|
-
await
|
|
163
|
+
await ensurePrivateDir(dir);
|
|
126
164
|
const configPath = path.join(dir, "config.json");
|
|
127
|
-
if (!
|
|
165
|
+
if (!existsSync2(configPath)) {
|
|
128
166
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
129
167
|
}
|
|
130
168
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -137,6 +175,7 @@ async function loadConfig() {
|
|
|
137
175
|
`);
|
|
138
176
|
try {
|
|
139
177
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
178
|
+
await enforcePrivateFile(configPath);
|
|
140
179
|
} catch {
|
|
141
180
|
}
|
|
142
181
|
}
|
|
@@ -155,7 +194,7 @@ async function loadConfig() {
|
|
|
155
194
|
function loadConfigSync() {
|
|
156
195
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
157
196
|
const configPath = path.join(dir, "config.json");
|
|
158
|
-
if (!
|
|
197
|
+
if (!existsSync2(configPath)) {
|
|
159
198
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
160
199
|
}
|
|
161
200
|
try {
|
|
@@ -173,12 +212,10 @@ function loadConfigSync() {
|
|
|
173
212
|
}
|
|
174
213
|
async function saveConfig(config2) {
|
|
175
214
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
176
|
-
await
|
|
215
|
+
await ensurePrivateDir(dir);
|
|
177
216
|
const configPath = path.join(dir, "config.json");
|
|
178
217
|
await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
|
|
179
|
-
|
|
180
|
-
await chmod(configPath, 384);
|
|
181
|
-
}
|
|
218
|
+
await enforcePrivateFile(configPath);
|
|
182
219
|
}
|
|
183
220
|
async function loadConfigFrom(configPath) {
|
|
184
221
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -198,6 +235,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
198
235
|
var init_config = __esm({
|
|
199
236
|
"src/lib/config.ts"() {
|
|
200
237
|
"use strict";
|
|
238
|
+
init_secure_files();
|
|
201
239
|
EXE_AI_DIR = resolveDataDir();
|
|
202
240
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
203
241
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -274,6 +312,43 @@ var init_config = __esm({
|
|
|
274
312
|
}
|
|
275
313
|
});
|
|
276
314
|
|
|
315
|
+
// src/lib/daemon-auth.ts
|
|
316
|
+
import crypto2 from "crypto";
|
|
317
|
+
import path2 from "path";
|
|
318
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
319
|
+
function normalizeToken(token) {
|
|
320
|
+
if (!token) return null;
|
|
321
|
+
const trimmed = token.trim();
|
|
322
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
323
|
+
}
|
|
324
|
+
function readDaemonToken() {
|
|
325
|
+
try {
|
|
326
|
+
if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
|
|
327
|
+
return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
|
|
328
|
+
} catch {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function ensureDaemonToken(seed) {
|
|
333
|
+
const existing = readDaemonToken();
|
|
334
|
+
if (existing) return existing;
|
|
335
|
+
const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
|
|
336
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
337
|
+
writeFileSync(DAEMON_TOKEN_PATH, `${token}
|
|
338
|
+
`, "utf8");
|
|
339
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
340
|
+
return token;
|
|
341
|
+
}
|
|
342
|
+
var DAEMON_TOKEN_PATH;
|
|
343
|
+
var init_daemon_auth = __esm({
|
|
344
|
+
"src/lib/daemon-auth.ts"() {
|
|
345
|
+
"use strict";
|
|
346
|
+
init_config();
|
|
347
|
+
init_secure_files();
|
|
348
|
+
DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
277
352
|
// src/lib/exe-daemon-client.ts
|
|
278
353
|
var exe_daemon_client_exports = {};
|
|
279
354
|
__export(exe_daemon_client_exports, {
|
|
@@ -283,14 +358,15 @@ __export(exe_daemon_client_exports, {
|
|
|
283
358
|
embedViaClient: () => embedViaClient,
|
|
284
359
|
isClientConnected: () => isClientConnected,
|
|
285
360
|
pingDaemon: () => pingDaemon,
|
|
286
|
-
sendDaemonRequest: () => sendDaemonRequest
|
|
361
|
+
sendDaemonRequest: () => sendDaemonRequest,
|
|
362
|
+
sendIngestRequest: () => sendIngestRequest
|
|
287
363
|
});
|
|
288
364
|
import net from "net";
|
|
289
365
|
import os2 from "os";
|
|
290
366
|
import { spawn } from "child_process";
|
|
291
367
|
import { randomUUID } from "crypto";
|
|
292
|
-
import { existsSync as
|
|
293
|
-
import
|
|
368
|
+
import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
369
|
+
import path3 from "path";
|
|
294
370
|
import { fileURLToPath } from "url";
|
|
295
371
|
function handleData(chunk) {
|
|
296
372
|
_buffer += chunk.toString();
|
|
@@ -318,9 +394,9 @@ function handleData(chunk) {
|
|
|
318
394
|
}
|
|
319
395
|
}
|
|
320
396
|
function cleanupStaleFiles() {
|
|
321
|
-
if (
|
|
397
|
+
if (existsSync4(PID_PATH)) {
|
|
322
398
|
try {
|
|
323
|
-
const pid = parseInt(
|
|
399
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
324
400
|
if (pid > 0) {
|
|
325
401
|
try {
|
|
326
402
|
process.kill(pid, 0);
|
|
@@ -341,11 +417,11 @@ function cleanupStaleFiles() {
|
|
|
341
417
|
}
|
|
342
418
|
}
|
|
343
419
|
function findPackageRoot() {
|
|
344
|
-
let dir =
|
|
345
|
-
const { root } =
|
|
420
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
421
|
+
const { root } = path3.parse(dir);
|
|
346
422
|
while (dir !== root) {
|
|
347
|
-
if (
|
|
348
|
-
dir =
|
|
423
|
+
if (existsSync4(path3.join(dir, "package.json"))) return dir;
|
|
424
|
+
dir = path3.dirname(dir);
|
|
349
425
|
}
|
|
350
426
|
return null;
|
|
351
427
|
}
|
|
@@ -371,16 +447,17 @@ function spawnDaemon() {
|
|
|
371
447
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
372
448
|
return;
|
|
373
449
|
}
|
|
374
|
-
const daemonPath =
|
|
375
|
-
if (!
|
|
450
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
451
|
+
if (!existsSync4(daemonPath)) {
|
|
376
452
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
377
453
|
`);
|
|
378
454
|
return;
|
|
379
455
|
}
|
|
380
456
|
const resolvedPath = daemonPath;
|
|
457
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
381
458
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
382
459
|
`);
|
|
383
|
-
const logPath =
|
|
460
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
384
461
|
let stderrFd = "ignore";
|
|
385
462
|
try {
|
|
386
463
|
stderrFd = openSync(logPath, "a");
|
|
@@ -398,7 +475,8 @@ function spawnDaemon() {
|
|
|
398
475
|
TMUX_PANE: void 0,
|
|
399
476
|
// Prevents resolveExeSession() from scoping to one session
|
|
400
477
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
401
|
-
EXE_DAEMON_PID: PID_PATH
|
|
478
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
479
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
402
480
|
}
|
|
403
481
|
});
|
|
404
482
|
child.unref();
|
|
@@ -508,13 +586,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
508
586
|
return;
|
|
509
587
|
}
|
|
510
588
|
const id = randomUUID();
|
|
589
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
511
590
|
const timer = setTimeout(() => {
|
|
512
591
|
_pending.delete(id);
|
|
513
592
|
resolve({ error: "Request timeout" });
|
|
514
593
|
}, timeoutMs);
|
|
515
594
|
_pending.set(id, { resolve, timer });
|
|
516
595
|
try {
|
|
517
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
596
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
518
597
|
} catch {
|
|
519
598
|
clearTimeout(timer);
|
|
520
599
|
_pending.delete(id);
|
|
@@ -543,9 +622,9 @@ function killAndRespawnDaemon() {
|
|
|
543
622
|
}
|
|
544
623
|
try {
|
|
545
624
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
546
|
-
if (
|
|
625
|
+
if (existsSync4(PID_PATH)) {
|
|
547
626
|
try {
|
|
548
|
-
const pid = parseInt(
|
|
627
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
549
628
|
if (pid > 0) {
|
|
550
629
|
try {
|
|
551
630
|
process.kill(pid, "SIGKILL");
|
|
@@ -674,17 +753,30 @@ function disconnectClient() {
|
|
|
674
753
|
function isClientConnected() {
|
|
675
754
|
return _connected;
|
|
676
755
|
}
|
|
677
|
-
|
|
756
|
+
function sendIngestRequest(payload) {
|
|
757
|
+
if (!_socket || !_connected) return false;
|
|
758
|
+
try {
|
|
759
|
+
const id = randomUUID();
|
|
760
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
761
|
+
_socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
|
|
762
|
+
return true;
|
|
763
|
+
} catch {
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
678
768
|
var init_exe_daemon_client = __esm({
|
|
679
769
|
"src/lib/exe-daemon-client.ts"() {
|
|
680
770
|
"use strict";
|
|
681
771
|
init_config();
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
772
|
+
init_daemon_auth();
|
|
773
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
774
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
775
|
+
SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
685
776
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
686
777
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
687
778
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
779
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
688
780
|
_socket = null;
|
|
689
781
|
_connected = false;
|
|
690
782
|
_buffer = "";
|
|
@@ -736,10 +828,10 @@ async function disposeEmbedder() {
|
|
|
736
828
|
async function embedDirect(text) {
|
|
737
829
|
const llamaCpp = await import("node-llama-cpp");
|
|
738
830
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
739
|
-
const { existsSync:
|
|
740
|
-
const
|
|
741
|
-
const modelPath =
|
|
742
|
-
if (!
|
|
831
|
+
const { existsSync: existsSync34 } = await import("fs");
|
|
832
|
+
const path45 = await import("path");
|
|
833
|
+
const modelPath = path45.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
834
|
+
if (!existsSync34(modelPath)) {
|
|
743
835
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
744
836
|
}
|
|
745
837
|
const llama = await llamaCpp.getLlama();
|
|
@@ -862,20 +954,21 @@ __export(agent_config_exports, {
|
|
|
862
954
|
saveAgentConfig: () => saveAgentConfig,
|
|
863
955
|
setAgentRuntime: () => setAgentRuntime
|
|
864
956
|
});
|
|
865
|
-
import { readFileSync as
|
|
866
|
-
import
|
|
957
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
|
|
958
|
+
import path4 from "path";
|
|
867
959
|
function loadAgentConfig() {
|
|
868
|
-
if (!
|
|
960
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
869
961
|
try {
|
|
870
|
-
return JSON.parse(
|
|
962
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
871
963
|
} catch {
|
|
872
964
|
return {};
|
|
873
965
|
}
|
|
874
966
|
}
|
|
875
967
|
function saveAgentConfig(config2) {
|
|
876
|
-
const dir =
|
|
877
|
-
|
|
878
|
-
|
|
968
|
+
const dir = path4.dirname(AGENT_CONFIG_PATH);
|
|
969
|
+
ensurePrivateDirSync(dir);
|
|
970
|
+
writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
971
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
879
972
|
}
|
|
880
973
|
function getAgentRuntime(agentId) {
|
|
881
974
|
const config2 = loadAgentConfig();
|
|
@@ -915,7 +1008,8 @@ var init_agent_config = __esm({
|
|
|
915
1008
|
"use strict";
|
|
916
1009
|
init_config();
|
|
917
1010
|
init_runtime_table();
|
|
918
|
-
|
|
1011
|
+
init_secure_files();
|
|
1012
|
+
AGENT_CONFIG_PATH = path4.join(EXE_AI_DIR, "agent-config.json");
|
|
919
1013
|
KNOWN_RUNTIMES = {
|
|
920
1014
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
921
1015
|
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
@@ -962,9 +1056,9 @@ __export(employees_exports, {
|
|
|
962
1056
|
validateEmployeeName: () => validateEmployeeName
|
|
963
1057
|
});
|
|
964
1058
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
965
|
-
import { existsSync as
|
|
1059
|
+
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
966
1060
|
import { execSync } from "child_process";
|
|
967
|
-
import
|
|
1061
|
+
import path5 from "path";
|
|
968
1062
|
import os3 from "os";
|
|
969
1063
|
function normalizeRole(role) {
|
|
970
1064
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -1001,7 +1095,7 @@ function validateEmployeeName(name) {
|
|
|
1001
1095
|
return { valid: true };
|
|
1002
1096
|
}
|
|
1003
1097
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
1004
|
-
if (!
|
|
1098
|
+
if (!existsSync6(employeesPath)) {
|
|
1005
1099
|
return [];
|
|
1006
1100
|
}
|
|
1007
1101
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -1012,13 +1106,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
1012
1106
|
}
|
|
1013
1107
|
}
|
|
1014
1108
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
1015
|
-
await mkdir2(
|
|
1109
|
+
await mkdir2(path5.dirname(employeesPath), { recursive: true });
|
|
1016
1110
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1017
1111
|
}
|
|
1018
1112
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
1019
|
-
if (!
|
|
1113
|
+
if (!existsSync6(employeesPath)) return [];
|
|
1020
1114
|
try {
|
|
1021
|
-
return JSON.parse(
|
|
1115
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
1022
1116
|
} catch {
|
|
1023
1117
|
return [];
|
|
1024
1118
|
}
|
|
@@ -1063,9 +1157,9 @@ function addEmployee(employees, employee) {
|
|
|
1063
1157
|
function appendToCoordinatorTeam(employee) {
|
|
1064
1158
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1065
1159
|
if (!coordinator) return;
|
|
1066
|
-
const idPath =
|
|
1067
|
-
if (!
|
|
1068
|
-
const content =
|
|
1160
|
+
const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1161
|
+
if (!existsSync6(idPath)) return;
|
|
1162
|
+
const content = readFileSync5(idPath, "utf-8");
|
|
1069
1163
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1070
1164
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1071
1165
|
if (!teamMatch || teamMatch.index === void 0) return;
|
|
@@ -1081,7 +1175,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
1081
1175
|
} else {
|
|
1082
1176
|
updated = content.trimEnd() + "\n" + entry;
|
|
1083
1177
|
}
|
|
1084
|
-
|
|
1178
|
+
writeFileSync3(idPath, updated, "utf-8");
|
|
1085
1179
|
}
|
|
1086
1180
|
function capitalize(s) {
|
|
1087
1181
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -1115,14 +1209,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
1115
1209
|
emp.name = emp.name.toLowerCase();
|
|
1116
1210
|
changed = true;
|
|
1117
1211
|
try {
|
|
1118
|
-
const identityDir =
|
|
1119
|
-
const oldPath =
|
|
1120
|
-
const newPath =
|
|
1121
|
-
if (
|
|
1212
|
+
const identityDir = path5.join(os3.homedir(), ".exe-os", "identity");
|
|
1213
|
+
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
1214
|
+
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
1215
|
+
if (existsSync6(oldPath) && !existsSync6(newPath)) {
|
|
1122
1216
|
renameSync2(oldPath, newPath);
|
|
1123
|
-
} else if (
|
|
1124
|
-
const content =
|
|
1125
|
-
|
|
1217
|
+
} else if (existsSync6(oldPath) && oldPath !== newPath) {
|
|
1218
|
+
const content = readFileSync5(oldPath, "utf-8");
|
|
1219
|
+
writeFileSync3(newPath, content, "utf-8");
|
|
1126
1220
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
1127
1221
|
unlinkSync2(oldPath);
|
|
1128
1222
|
}
|
|
@@ -1152,7 +1246,7 @@ function registerBinSymlinks(name) {
|
|
|
1152
1246
|
errors.push("Could not find 'exe-os' in PATH");
|
|
1153
1247
|
return { created, skipped, errors };
|
|
1154
1248
|
}
|
|
1155
|
-
const binDir =
|
|
1249
|
+
const binDir = path5.dirname(exeBinPath);
|
|
1156
1250
|
let target;
|
|
1157
1251
|
try {
|
|
1158
1252
|
target = readlinkSync(exeBinPath);
|
|
@@ -1162,8 +1256,8 @@ function registerBinSymlinks(name) {
|
|
|
1162
1256
|
}
|
|
1163
1257
|
for (const suffix of ["", "-opencode"]) {
|
|
1164
1258
|
const linkName = `${name}${suffix}`;
|
|
1165
|
-
const linkPath =
|
|
1166
|
-
if (
|
|
1259
|
+
const linkPath = path5.join(binDir, linkName);
|
|
1260
|
+
if (existsSync6(linkPath)) {
|
|
1167
1261
|
skipped.push(linkName);
|
|
1168
1262
|
continue;
|
|
1169
1263
|
}
|
|
@@ -1181,18 +1275,18 @@ var init_employees = __esm({
|
|
|
1181
1275
|
"src/lib/employees.ts"() {
|
|
1182
1276
|
"use strict";
|
|
1183
1277
|
init_config();
|
|
1184
|
-
EMPLOYEES_PATH =
|
|
1278
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
1185
1279
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1186
1280
|
COORDINATOR_ROLE = "COO";
|
|
1187
1281
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1188
|
-
IDENTITY_DIR =
|
|
1282
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
1189
1283
|
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1190
1284
|
}
|
|
1191
1285
|
});
|
|
1192
1286
|
|
|
1193
1287
|
// src/lib/database-adapter.ts
|
|
1194
1288
|
import os4 from "os";
|
|
1195
|
-
import
|
|
1289
|
+
import path6 from "path";
|
|
1196
1290
|
import { createRequire } from "module";
|
|
1197
1291
|
import { pathToFileURL } from "url";
|
|
1198
1292
|
function quotedIdentifier(identifier) {
|
|
@@ -1503,8 +1597,8 @@ async function loadPrismaClient() {
|
|
|
1503
1597
|
}
|
|
1504
1598
|
return new PrismaClient2();
|
|
1505
1599
|
}
|
|
1506
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
1507
|
-
const requireFromExeDb = createRequire(
|
|
1600
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
|
|
1601
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1508
1602
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1509
1603
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1510
1604
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -2344,6 +2438,7 @@ async function ensureSchema() {
|
|
|
2344
2438
|
project TEXT NOT NULL,
|
|
2345
2439
|
summary TEXT NOT NULL,
|
|
2346
2440
|
task_file TEXT,
|
|
2441
|
+
session_scope TEXT,
|
|
2347
2442
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2348
2443
|
created_at TEXT NOT NULL
|
|
2349
2444
|
);
|
|
@@ -2352,7 +2447,7 @@ async function ensureSchema() {
|
|
|
2352
2447
|
ON notifications(read);
|
|
2353
2448
|
|
|
2354
2449
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2355
|
-
ON notifications(agent_id);
|
|
2450
|
+
ON notifications(agent_id, session_scope);
|
|
2356
2451
|
|
|
2357
2452
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2358
2453
|
ON notifications(task_file);
|
|
@@ -2390,6 +2485,7 @@ async function ensureSchema() {
|
|
|
2390
2485
|
target_agent TEXT NOT NULL,
|
|
2391
2486
|
target_project TEXT,
|
|
2392
2487
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2488
|
+
session_scope TEXT,
|
|
2393
2489
|
content TEXT NOT NULL,
|
|
2394
2490
|
priority TEXT DEFAULT 'normal',
|
|
2395
2491
|
status TEXT DEFAULT 'pending',
|
|
@@ -2403,10 +2499,31 @@ async function ensureSchema() {
|
|
|
2403
2499
|
);
|
|
2404
2500
|
|
|
2405
2501
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2406
|
-
ON messages(target_agent, status);
|
|
2502
|
+
ON messages(target_agent, session_scope, status);
|
|
2407
2503
|
|
|
2408
2504
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2409
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2505
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2506
|
+
`);
|
|
2507
|
+
try {
|
|
2508
|
+
await client.execute({
|
|
2509
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2510
|
+
args: []
|
|
2511
|
+
});
|
|
2512
|
+
} catch {
|
|
2513
|
+
}
|
|
2514
|
+
try {
|
|
2515
|
+
await client.execute({
|
|
2516
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2517
|
+
args: []
|
|
2518
|
+
});
|
|
2519
|
+
} catch {
|
|
2520
|
+
}
|
|
2521
|
+
await client.executeMultiple(`
|
|
2522
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2523
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2524
|
+
|
|
2525
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2526
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2410
2527
|
`);
|
|
2411
2528
|
try {
|
|
2412
2529
|
await client.execute({
|
|
@@ -2990,6 +3107,13 @@ async function ensureSchema() {
|
|
|
2990
3107
|
} catch {
|
|
2991
3108
|
}
|
|
2992
3109
|
}
|
|
3110
|
+
try {
|
|
3111
|
+
await client.execute({
|
|
3112
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
3113
|
+
args: []
|
|
3114
|
+
});
|
|
3115
|
+
} catch {
|
|
3116
|
+
}
|
|
2993
3117
|
}
|
|
2994
3118
|
async function disposeDatabase() {
|
|
2995
3119
|
if (_walCheckpointTimer) {
|
|
@@ -3037,14 +3161,14 @@ __export(keychain_exports, {
|
|
|
3037
3161
|
setMasterKey: () => setMasterKey
|
|
3038
3162
|
});
|
|
3039
3163
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3040
|
-
import { existsSync as
|
|
3041
|
-
import
|
|
3164
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3165
|
+
import path7 from "path";
|
|
3042
3166
|
import os5 from "os";
|
|
3043
3167
|
function getKeyDir() {
|
|
3044
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3168
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3045
3169
|
}
|
|
3046
3170
|
function getKeyPath() {
|
|
3047
|
-
return
|
|
3171
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3048
3172
|
}
|
|
3049
3173
|
async function tryKeytar() {
|
|
3050
3174
|
try {
|
|
@@ -3065,7 +3189,7 @@ async function getMasterKey() {
|
|
|
3065
3189
|
}
|
|
3066
3190
|
}
|
|
3067
3191
|
const keyPath = getKeyPath();
|
|
3068
|
-
if (!
|
|
3192
|
+
if (!existsSync7(keyPath)) {
|
|
3069
3193
|
process.stderr.write(
|
|
3070
3194
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3071
3195
|
`
|
|
@@ -3108,7 +3232,7 @@ async function deleteMasterKey() {
|
|
|
3108
3232
|
}
|
|
3109
3233
|
}
|
|
3110
3234
|
const keyPath = getKeyPath();
|
|
3111
|
-
if (
|
|
3235
|
+
if (existsSync7(keyPath)) {
|
|
3112
3236
|
await unlink(keyPath);
|
|
3113
3237
|
}
|
|
3114
3238
|
}
|
|
@@ -3210,6 +3334,7 @@ var shard_manager_exports = {};
|
|
|
3210
3334
|
__export(shard_manager_exports, {
|
|
3211
3335
|
disposeShards: () => disposeShards,
|
|
3212
3336
|
ensureShardSchema: () => ensureShardSchema,
|
|
3337
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
3213
3338
|
getReadyShardClient: () => getReadyShardClient,
|
|
3214
3339
|
getShardClient: () => getShardClient,
|
|
3215
3340
|
getShardsDir: () => getShardsDir,
|
|
@@ -3218,15 +3343,18 @@ __export(shard_manager_exports, {
|
|
|
3218
3343
|
listShards: () => listShards,
|
|
3219
3344
|
shardExists: () => shardExists
|
|
3220
3345
|
});
|
|
3221
|
-
import
|
|
3222
|
-
import { existsSync as
|
|
3346
|
+
import path8 from "path";
|
|
3347
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
3223
3348
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3224
3349
|
function initShardManager(encryptionKey) {
|
|
3225
3350
|
_encryptionKey = encryptionKey;
|
|
3226
|
-
if (!
|
|
3351
|
+
if (!existsSync8(SHARDS_DIR)) {
|
|
3227
3352
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
3228
3353
|
}
|
|
3229
3354
|
_shardingEnabled = true;
|
|
3355
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3356
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3357
|
+
_evictionTimer.unref();
|
|
3230
3358
|
}
|
|
3231
3359
|
function isShardingEnabled() {
|
|
3232
3360
|
return _shardingEnabled;
|
|
@@ -3243,21 +3371,28 @@ function getShardClient(projectName) {
|
|
|
3243
3371
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
3244
3372
|
}
|
|
3245
3373
|
const cached = _shards.get(safeName);
|
|
3246
|
-
if (cached)
|
|
3247
|
-
|
|
3374
|
+
if (cached) {
|
|
3375
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3376
|
+
return cached;
|
|
3377
|
+
}
|
|
3378
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3379
|
+
evictLRU();
|
|
3380
|
+
}
|
|
3381
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3248
3382
|
const client = createClient2({
|
|
3249
3383
|
url: `file:${dbPath}`,
|
|
3250
3384
|
encryptionKey: _encryptionKey
|
|
3251
3385
|
});
|
|
3252
3386
|
_shards.set(safeName, client);
|
|
3387
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3253
3388
|
return client;
|
|
3254
3389
|
}
|
|
3255
3390
|
function shardExists(projectName) {
|
|
3256
3391
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3257
|
-
return
|
|
3392
|
+
return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3258
3393
|
}
|
|
3259
3394
|
function listShards() {
|
|
3260
|
-
if (!
|
|
3395
|
+
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3261
3396
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3262
3397
|
}
|
|
3263
3398
|
async function ensureShardSchema(client) {
|
|
@@ -3309,6 +3444,8 @@ async function ensureShardSchema(client) {
|
|
|
3309
3444
|
for (const col of [
|
|
3310
3445
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
3311
3446
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3447
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3448
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
3312
3449
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
3313
3450
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
3314
3451
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -3446,21 +3583,69 @@ async function getReadyShardClient(projectName) {
|
|
|
3446
3583
|
await ensureShardSchema(client);
|
|
3447
3584
|
return client;
|
|
3448
3585
|
}
|
|
3586
|
+
function evictLRU() {
|
|
3587
|
+
let oldest = null;
|
|
3588
|
+
let oldestTime = Infinity;
|
|
3589
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3590
|
+
if (time < oldestTime) {
|
|
3591
|
+
oldestTime = time;
|
|
3592
|
+
oldest = name;
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
if (oldest) {
|
|
3596
|
+
const client = _shards.get(oldest);
|
|
3597
|
+
if (client) {
|
|
3598
|
+
client.close();
|
|
3599
|
+
}
|
|
3600
|
+
_shards.delete(oldest);
|
|
3601
|
+
_shardLastAccess.delete(oldest);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
function evictIdleShards() {
|
|
3605
|
+
const now = Date.now();
|
|
3606
|
+
const toEvict = [];
|
|
3607
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3608
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3609
|
+
toEvict.push(name);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
for (const name of toEvict) {
|
|
3613
|
+
const client = _shards.get(name);
|
|
3614
|
+
if (client) {
|
|
3615
|
+
client.close();
|
|
3616
|
+
}
|
|
3617
|
+
_shards.delete(name);
|
|
3618
|
+
_shardLastAccess.delete(name);
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
function getOpenShardCount() {
|
|
3622
|
+
return _shards.size;
|
|
3623
|
+
}
|
|
3449
3624
|
function disposeShards() {
|
|
3625
|
+
if (_evictionTimer) {
|
|
3626
|
+
clearInterval(_evictionTimer);
|
|
3627
|
+
_evictionTimer = null;
|
|
3628
|
+
}
|
|
3450
3629
|
for (const [, client] of _shards) {
|
|
3451
3630
|
client.close();
|
|
3452
3631
|
}
|
|
3453
3632
|
_shards.clear();
|
|
3633
|
+
_shardLastAccess.clear();
|
|
3454
3634
|
_shardingEnabled = false;
|
|
3455
3635
|
_encryptionKey = null;
|
|
3456
3636
|
}
|
|
3457
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3637
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3458
3638
|
var init_shard_manager = __esm({
|
|
3459
3639
|
"src/lib/shard-manager.ts"() {
|
|
3460
3640
|
"use strict";
|
|
3461
3641
|
init_config();
|
|
3462
|
-
SHARDS_DIR =
|
|
3642
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
3643
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3644
|
+
MAX_OPEN_SHARDS = 10;
|
|
3645
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3463
3646
|
_shards = /* @__PURE__ */ new Map();
|
|
3647
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3648
|
+
_evictionTimer = null;
|
|
3464
3649
|
_encryptionKey = null;
|
|
3465
3650
|
_shardingEnabled = false;
|
|
3466
3651
|
}
|
|
@@ -4322,8 +4507,8 @@ __export(reranker_exports, {
|
|
|
4322
4507
|
rerankWithContext: () => rerankWithContext,
|
|
4323
4508
|
rerankWithScores: () => rerankWithScores
|
|
4324
4509
|
});
|
|
4325
|
-
import
|
|
4326
|
-
import { existsSync as
|
|
4510
|
+
import path9 from "path";
|
|
4511
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4327
4512
|
function resetIdleTimer() {
|
|
4328
4513
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
4329
4514
|
_idleTimer = setTimeout(() => {
|
|
@@ -4334,18 +4519,18 @@ function resetIdleTimer() {
|
|
|
4334
4519
|
}
|
|
4335
4520
|
}
|
|
4336
4521
|
function isRerankerAvailable() {
|
|
4337
|
-
return
|
|
4522
|
+
return existsSync9(path9.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
4338
4523
|
}
|
|
4339
4524
|
function getRerankerModelPath() {
|
|
4340
|
-
return
|
|
4525
|
+
return path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
4341
4526
|
}
|
|
4342
4527
|
async function ensureLoaded() {
|
|
4343
4528
|
if (_rerankerContext) {
|
|
4344
4529
|
resetIdleTimer();
|
|
4345
4530
|
return;
|
|
4346
4531
|
}
|
|
4347
|
-
const modelPath =
|
|
4348
|
-
if (!
|
|
4532
|
+
const modelPath = path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
4533
|
+
if (!existsSync9(modelPath)) {
|
|
4349
4534
|
throw new Error(
|
|
4350
4535
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
4351
4536
|
);
|
|
@@ -4448,7 +4633,7 @@ __export(project_name_exports, {
|
|
|
4448
4633
|
getProjectName: () => getProjectName
|
|
4449
4634
|
});
|
|
4450
4635
|
import { execSync as execSync2 } from "child_process";
|
|
4451
|
-
import
|
|
4636
|
+
import path10 from "path";
|
|
4452
4637
|
function getProjectName(cwd) {
|
|
4453
4638
|
const dir = cwd ?? process.cwd();
|
|
4454
4639
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
@@ -4461,7 +4646,7 @@ function getProjectName(cwd) {
|
|
|
4461
4646
|
timeout: 2e3,
|
|
4462
4647
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4463
4648
|
}).trim();
|
|
4464
|
-
repoRoot =
|
|
4649
|
+
repoRoot = path10.dirname(gitCommonDir);
|
|
4465
4650
|
} catch {
|
|
4466
4651
|
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
4467
4652
|
cwd: dir,
|
|
@@ -4470,11 +4655,11 @@ function getProjectName(cwd) {
|
|
|
4470
4655
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4471
4656
|
}).trim();
|
|
4472
4657
|
}
|
|
4473
|
-
_cached =
|
|
4658
|
+
_cached = path10.basename(repoRoot);
|
|
4474
4659
|
_cachedCwd = dir;
|
|
4475
4660
|
return _cached;
|
|
4476
4661
|
} catch {
|
|
4477
|
-
_cached =
|
|
4662
|
+
_cached = path10.basename(dir);
|
|
4478
4663
|
_cachedCwd = dir;
|
|
4479
4664
|
return _cached;
|
|
4480
4665
|
}
|
|
@@ -4498,9 +4683,9 @@ __export(file_grep_exports, {
|
|
|
4498
4683
|
grepProjectFiles: () => grepProjectFiles
|
|
4499
4684
|
});
|
|
4500
4685
|
import { execSync as execSync3 } from "child_process";
|
|
4501
|
-
import { readFileSync as
|
|
4502
|
-
import
|
|
4503
|
-
import
|
|
4686
|
+
import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync10 } from "fs";
|
|
4687
|
+
import path11 from "path";
|
|
4688
|
+
import crypto3 from "crypto";
|
|
4504
4689
|
function hasRipgrep() {
|
|
4505
4690
|
if (_hasRg === null) {
|
|
4506
4691
|
try {
|
|
@@ -4533,13 +4718,13 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
4533
4718
|
const chunkCtx = getChunkContext(hit.filePath, hit.lineNumber);
|
|
4534
4719
|
const prefix = chunkCtx ? `[file: ${hit.filePath}:${hit.lineNumber} in ${chunkCtx}]` : `[file: ${hit.filePath}:${hit.lineNumber}]`;
|
|
4535
4720
|
return {
|
|
4536
|
-
id:
|
|
4721
|
+
id: crypto3.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
|
|
4537
4722
|
agent_id: "project",
|
|
4538
4723
|
agent_role: "file",
|
|
4539
4724
|
session_id: "file-grep",
|
|
4540
4725
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4541
4726
|
tool_name: "file_grep",
|
|
4542
|
-
project_name:
|
|
4727
|
+
project_name: path11.basename(projectRoot),
|
|
4543
4728
|
has_error: false,
|
|
4544
4729
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
4545
4730
|
vector: null,
|
|
@@ -4551,7 +4736,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
4551
4736
|
try {
|
|
4552
4737
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
4553
4738
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
4554
|
-
const source =
|
|
4739
|
+
const source = readFileSync6(filePath, "utf8");
|
|
4555
4740
|
const lines = source.split("\n");
|
|
4556
4741
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
4557
4742
|
const line = lines[i];
|
|
@@ -4613,11 +4798,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
4613
4798
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
4614
4799
|
const hits = [];
|
|
4615
4800
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
4616
|
-
const absPath =
|
|
4801
|
+
const absPath = path11.join(projectRoot, filePath);
|
|
4617
4802
|
try {
|
|
4618
4803
|
const stat = statSync2(absPath);
|
|
4619
4804
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
4620
|
-
const content =
|
|
4805
|
+
const content = readFileSync6(absPath, "utf8");
|
|
4621
4806
|
const lines = content.split("\n");
|
|
4622
4807
|
const matches = content.match(regex);
|
|
4623
4808
|
if (!matches || matches.length === 0) continue;
|
|
@@ -4640,15 +4825,15 @@ function collectFiles(root, patterns) {
|
|
|
4640
4825
|
const files = [];
|
|
4641
4826
|
function walk(dir, relative) {
|
|
4642
4827
|
if (files.length >= MAX_FILES) return;
|
|
4643
|
-
const basename =
|
|
4828
|
+
const basename = path11.basename(dir);
|
|
4644
4829
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
4645
4830
|
try {
|
|
4646
4831
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
4647
4832
|
for (const entry of entries) {
|
|
4648
4833
|
if (files.length >= MAX_FILES) return;
|
|
4649
|
-
const rel =
|
|
4834
|
+
const rel = path11.join(relative, entry.name);
|
|
4650
4835
|
if (entry.isDirectory()) {
|
|
4651
|
-
walk(
|
|
4836
|
+
walk(path11.join(dir, entry.name), rel);
|
|
4652
4837
|
} else if (entry.isFile()) {
|
|
4653
4838
|
for (const pat of patterns) {
|
|
4654
4839
|
if (matchGlob(rel, pat)) {
|
|
@@ -4680,7 +4865,7 @@ function matchGlob(filePath, pattern) {
|
|
|
4680
4865
|
if (slashIdx !== -1) {
|
|
4681
4866
|
const dir = pattern.slice(0, slashIdx);
|
|
4682
4867
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
4683
|
-
const fileDir =
|
|
4868
|
+
const fileDir = path11.dirname(filePath);
|
|
4684
4869
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
4685
4870
|
}
|
|
4686
4871
|
const ext = pattern.replace("*", "");
|
|
@@ -4688,9 +4873,9 @@ function matchGlob(filePath, pattern) {
|
|
|
4688
4873
|
}
|
|
4689
4874
|
function buildSnippet(hit, projectRoot) {
|
|
4690
4875
|
try {
|
|
4691
|
-
const absPath =
|
|
4692
|
-
if (!
|
|
4693
|
-
const lines =
|
|
4876
|
+
const absPath = path11.join(projectRoot, hit.filePath);
|
|
4877
|
+
if (!existsSync10(absPath)) return hit.matchLine;
|
|
4878
|
+
const lines = readFileSync6(absPath, "utf8").split("\n");
|
|
4694
4879
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
4695
4880
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
4696
4881
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -5915,9 +6100,9 @@ __export(active_agent_exports, {
|
|
|
5915
6100
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
5916
6101
|
writeActiveAgent: () => writeActiveAgent
|
|
5917
6102
|
});
|
|
5918
|
-
import { readFileSync as
|
|
6103
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
5919
6104
|
import { execSync as execSync5 } from "child_process";
|
|
5920
|
-
import
|
|
6105
|
+
import path12 from "path";
|
|
5921
6106
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
5922
6107
|
if (candidate === baseName) return true;
|
|
5923
6108
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -5961,12 +6146,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
5961
6146
|
return null;
|
|
5962
6147
|
}
|
|
5963
6148
|
function getMarkerPath() {
|
|
5964
|
-
return
|
|
6149
|
+
return path12.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
5965
6150
|
}
|
|
5966
6151
|
function writeActiveAgent(agentId, agentRole) {
|
|
5967
6152
|
try {
|
|
5968
6153
|
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
5969
|
-
|
|
6154
|
+
writeFileSync4(
|
|
5970
6155
|
getMarkerPath(),
|
|
5971
6156
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
5972
6157
|
);
|
|
@@ -5982,7 +6167,7 @@ function clearActiveAgent() {
|
|
|
5982
6167
|
function getActiveAgent() {
|
|
5983
6168
|
try {
|
|
5984
6169
|
const markerPath = getMarkerPath();
|
|
5985
|
-
const raw =
|
|
6170
|
+
const raw = readFileSync7(markerPath, "utf8");
|
|
5986
6171
|
const data = JSON.parse(raw);
|
|
5987
6172
|
if (data.agentId) {
|
|
5988
6173
|
if (data.startedAt) {
|
|
@@ -6030,14 +6215,14 @@ function getAllActiveAgents() {
|
|
|
6030
6215
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
6031
6216
|
if (key === "undefined") continue;
|
|
6032
6217
|
try {
|
|
6033
|
-
const raw =
|
|
6218
|
+
const raw = readFileSync7(path12.join(CACHE_DIR, file), "utf8");
|
|
6034
6219
|
const data = JSON.parse(raw);
|
|
6035
6220
|
if (!data.agentId) continue;
|
|
6036
6221
|
if (data.startedAt) {
|
|
6037
6222
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
6038
6223
|
if (age > STALE_MS) {
|
|
6039
6224
|
try {
|
|
6040
|
-
unlinkSync3(
|
|
6225
|
+
unlinkSync3(path12.join(CACHE_DIR, file));
|
|
6041
6226
|
} catch {
|
|
6042
6227
|
}
|
|
6043
6228
|
continue;
|
|
@@ -6060,11 +6245,11 @@ function getAllActiveAgents() {
|
|
|
6060
6245
|
function cleanupSessionMarkers() {
|
|
6061
6246
|
const key = getSessionKey();
|
|
6062
6247
|
try {
|
|
6063
|
-
unlinkSync3(
|
|
6248
|
+
unlinkSync3(path12.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
6064
6249
|
} catch {
|
|
6065
6250
|
}
|
|
6066
6251
|
try {
|
|
6067
|
-
unlinkSync3(
|
|
6252
|
+
unlinkSync3(path12.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
6068
6253
|
} catch {
|
|
6069
6254
|
}
|
|
6070
6255
|
}
|
|
@@ -6075,7 +6260,7 @@ var init_active_agent = __esm({
|
|
|
6075
6260
|
init_config();
|
|
6076
6261
|
init_session_key();
|
|
6077
6262
|
init_employees();
|
|
6078
|
-
CACHE_DIR =
|
|
6263
|
+
CACHE_DIR = path12.join(EXE_AI_DIR, "session-cache");
|
|
6079
6264
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
6080
6265
|
}
|
|
6081
6266
|
});
|
|
@@ -6097,9 +6282,12 @@ __export(license_exports, {
|
|
|
6097
6282
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
6098
6283
|
validateLicense: () => validateLicense
|
|
6099
6284
|
});
|
|
6100
|
-
import { readFileSync as
|
|
6285
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
|
|
6101
6286
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6102
|
-
import
|
|
6287
|
+
import { createRequire as createRequire2 } from "module";
|
|
6288
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
6289
|
+
import os6 from "os";
|
|
6290
|
+
import path13 from "path";
|
|
6103
6291
|
import { jwtVerify, importSPKI } from "jose";
|
|
6104
6292
|
async function fetchRetry(url, init) {
|
|
6105
6293
|
try {
|
|
@@ -6110,37 +6298,37 @@ async function fetchRetry(url, init) {
|
|
|
6110
6298
|
}
|
|
6111
6299
|
}
|
|
6112
6300
|
function loadDeviceId() {
|
|
6113
|
-
const deviceJsonPath =
|
|
6301
|
+
const deviceJsonPath = path13.join(EXE_AI_DIR, "device.json");
|
|
6114
6302
|
try {
|
|
6115
|
-
if (
|
|
6116
|
-
const data = JSON.parse(
|
|
6303
|
+
if (existsSync11(deviceJsonPath)) {
|
|
6304
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
6117
6305
|
if (data.deviceId) return data.deviceId;
|
|
6118
6306
|
}
|
|
6119
6307
|
} catch {
|
|
6120
6308
|
}
|
|
6121
6309
|
try {
|
|
6122
|
-
if (
|
|
6123
|
-
const id2 =
|
|
6310
|
+
if (existsSync11(DEVICE_ID_PATH)) {
|
|
6311
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
6124
6312
|
if (id2) return id2;
|
|
6125
6313
|
}
|
|
6126
6314
|
} catch {
|
|
6127
6315
|
}
|
|
6128
6316
|
const id = randomUUID3();
|
|
6129
6317
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
6130
|
-
|
|
6318
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
6131
6319
|
return id;
|
|
6132
6320
|
}
|
|
6133
6321
|
function loadLicense() {
|
|
6134
6322
|
try {
|
|
6135
|
-
if (!
|
|
6136
|
-
return
|
|
6323
|
+
if (!existsSync11(LICENSE_PATH)) return null;
|
|
6324
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
6137
6325
|
} catch {
|
|
6138
6326
|
return null;
|
|
6139
6327
|
}
|
|
6140
6328
|
}
|
|
6141
6329
|
function saveLicense(apiKey) {
|
|
6142
6330
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
6143
|
-
|
|
6331
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
6144
6332
|
}
|
|
6145
6333
|
async function verifyLicenseJwt(token) {
|
|
6146
6334
|
try {
|
|
@@ -6166,8 +6354,8 @@ async function verifyLicenseJwt(token) {
|
|
|
6166
6354
|
}
|
|
6167
6355
|
async function getCachedLicense() {
|
|
6168
6356
|
try {
|
|
6169
|
-
if (!
|
|
6170
|
-
const raw = JSON.parse(
|
|
6357
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
6358
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
6171
6359
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
6172
6360
|
return await verifyLicenseJwt(raw.token);
|
|
6173
6361
|
} catch {
|
|
@@ -6176,8 +6364,8 @@ async function getCachedLicense() {
|
|
|
6176
6364
|
}
|
|
6177
6365
|
function readCachedToken() {
|
|
6178
6366
|
try {
|
|
6179
|
-
if (!
|
|
6180
|
-
const raw = JSON.parse(
|
|
6367
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
6368
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
6181
6369
|
return typeof raw.token === "string" ? raw.token : null;
|
|
6182
6370
|
} catch {
|
|
6183
6371
|
return null;
|
|
@@ -6211,57 +6399,131 @@ function getRawCachedPlan() {
|
|
|
6211
6399
|
}
|
|
6212
6400
|
function cacheResponse(token) {
|
|
6213
6401
|
try {
|
|
6214
|
-
|
|
6402
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
6215
6403
|
} catch {
|
|
6216
6404
|
}
|
|
6217
6405
|
}
|
|
6218
|
-
|
|
6219
|
-
|
|
6406
|
+
function loadPrismaForLicense() {
|
|
6407
|
+
if (_prismaFailed) return null;
|
|
6408
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
6409
|
+
if (!dbUrl) {
|
|
6410
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(os6.homedir(), "exe-db");
|
|
6411
|
+
if (!existsSync11(path13.join(exeDbRoot, "package.json"))) {
|
|
6412
|
+
_prismaFailed = true;
|
|
6413
|
+
return null;
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
if (!_prismaPromise) {
|
|
6417
|
+
_prismaPromise = (async () => {
|
|
6418
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
6419
|
+
if (explicitPath) {
|
|
6420
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
6421
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
6422
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
6423
|
+
return new Ctor2();
|
|
6424
|
+
}
|
|
6425
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(os6.homedir(), "exe-db");
|
|
6426
|
+
const req = createRequire2(path13.join(exeDbRoot, "package.json"));
|
|
6427
|
+
const entry = req.resolve("@prisma/client");
|
|
6428
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
6429
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
6430
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
6431
|
+
return new Ctor();
|
|
6432
|
+
})().catch((err) => {
|
|
6433
|
+
_prismaFailed = true;
|
|
6434
|
+
_prismaPromise = null;
|
|
6435
|
+
throw err;
|
|
6436
|
+
});
|
|
6437
|
+
}
|
|
6438
|
+
return _prismaPromise;
|
|
6439
|
+
}
|
|
6440
|
+
async function validateViaPostgres(apiKey) {
|
|
6441
|
+
const loader = loadPrismaForLicense();
|
|
6442
|
+
if (!loader) return null;
|
|
6443
|
+
try {
|
|
6444
|
+
const prisma = await loader;
|
|
6445
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
6446
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
6447
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
6448
|
+
apiKey
|
|
6449
|
+
);
|
|
6450
|
+
if (!rows || rows.length === 0) return null;
|
|
6451
|
+
const row = rows[0];
|
|
6452
|
+
if (row.status !== "active") return null;
|
|
6453
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
6454
|
+
const plan = row.plan;
|
|
6455
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
6456
|
+
return {
|
|
6457
|
+
valid: true,
|
|
6458
|
+
plan,
|
|
6459
|
+
email: row.email,
|
|
6460
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
6461
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
6462
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
6463
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
6464
|
+
};
|
|
6465
|
+
} catch {
|
|
6466
|
+
return null;
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
6220
6470
|
try {
|
|
6221
6471
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
6222
6472
|
method: "POST",
|
|
6223
6473
|
headers: { "Content-Type": "application/json" },
|
|
6224
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
6474
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
6225
6475
|
signal: AbortSignal.timeout(1e4)
|
|
6226
6476
|
});
|
|
6227
|
-
if (res.ok)
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
}
|
|
6236
|
-
if (data.token) {
|
|
6237
|
-
cacheResponse(data.token);
|
|
6238
|
-
const verified = await verifyLicenseJwt(data.token);
|
|
6239
|
-
if (verified) return verified;
|
|
6240
|
-
}
|
|
6241
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
6242
|
-
return {
|
|
6243
|
-
valid: data.valid,
|
|
6244
|
-
plan: data.plan,
|
|
6245
|
-
email: data.email,
|
|
6246
|
-
expiresAt: data.expiresAt,
|
|
6247
|
-
deviceLimit: limits.devices,
|
|
6248
|
-
employeeLimit: limits.employees,
|
|
6249
|
-
memoryLimit: limits.memories
|
|
6250
|
-
};
|
|
6477
|
+
if (!res.ok) return null;
|
|
6478
|
+
const data = await res.json();
|
|
6479
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
6480
|
+
if (!data.valid) return null;
|
|
6481
|
+
if (data.token) {
|
|
6482
|
+
cacheResponse(data.token);
|
|
6483
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
6484
|
+
if (verified) return verified;
|
|
6251
6485
|
}
|
|
6252
|
-
const
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6486
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
6487
|
+
return {
|
|
6488
|
+
valid: data.valid,
|
|
6489
|
+
plan: data.plan,
|
|
6490
|
+
email: data.email,
|
|
6491
|
+
expiresAt: data.expiresAt,
|
|
6492
|
+
deviceLimit: limits.devices,
|
|
6493
|
+
employeeLimit: limits.employees,
|
|
6494
|
+
memoryLimit: limits.memories
|
|
6495
|
+
};
|
|
6257
6496
|
} catch {
|
|
6258
|
-
|
|
6259
|
-
if (cached) return cached;
|
|
6260
|
-
const rawFallback = getRawCachedPlan();
|
|
6261
|
-
if (rawFallback) return rawFallback;
|
|
6262
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
6497
|
+
return null;
|
|
6263
6498
|
}
|
|
6264
6499
|
}
|
|
6500
|
+
async function validateLicense(apiKey, deviceId) {
|
|
6501
|
+
const did = deviceId ?? loadDeviceId();
|
|
6502
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
6503
|
+
if (pgResult) {
|
|
6504
|
+
try {
|
|
6505
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
6506
|
+
} catch {
|
|
6507
|
+
}
|
|
6508
|
+
return pgResult;
|
|
6509
|
+
}
|
|
6510
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
6511
|
+
if (cfResult) return cfResult;
|
|
6512
|
+
const cached = await getCachedLicense();
|
|
6513
|
+
if (cached) return cached;
|
|
6514
|
+
try {
|
|
6515
|
+
if (existsSync11(CACHE_PATH)) {
|
|
6516
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
6517
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
6518
|
+
return raw.pgLicense;
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
} catch {
|
|
6522
|
+
}
|
|
6523
|
+
const rawFallback = getRawCachedPlan();
|
|
6524
|
+
if (rawFallback) return rawFallback;
|
|
6525
|
+
return { ...FREE_LICENSE, valid: false };
|
|
6526
|
+
}
|
|
6265
6527
|
function getCacheAgeMs() {
|
|
6266
6528
|
try {
|
|
6267
6529
|
const { statSync: statSync4 } = __require("fs");
|
|
@@ -6275,9 +6537,9 @@ async function checkLicense() {
|
|
|
6275
6537
|
let key = loadLicense();
|
|
6276
6538
|
if (!key) {
|
|
6277
6539
|
try {
|
|
6278
|
-
const configPath =
|
|
6279
|
-
if (
|
|
6280
|
-
const raw = JSON.parse(
|
|
6540
|
+
const configPath = path13.join(EXE_AI_DIR, "config.json");
|
|
6541
|
+
if (existsSync11(configPath)) {
|
|
6542
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
6281
6543
|
const cloud = raw.cloud;
|
|
6282
6544
|
if (cloud?.apiKey) {
|
|
6283
6545
|
key = cloud.apiKey;
|
|
@@ -6431,14 +6693,14 @@ function stopLicenseRevalidation() {
|
|
|
6431
6693
|
_revalTimer = null;
|
|
6432
6694
|
}
|
|
6433
6695
|
}
|
|
6434
|
-
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
6696
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
|
|
6435
6697
|
var init_license = __esm({
|
|
6436
6698
|
"src/lib/license.ts"() {
|
|
6437
6699
|
"use strict";
|
|
6438
6700
|
init_config();
|
|
6439
|
-
LICENSE_PATH =
|
|
6440
|
-
CACHE_PATH =
|
|
6441
|
-
DEVICE_ID_PATH =
|
|
6701
|
+
LICENSE_PATH = path13.join(EXE_AI_DIR, "license.key");
|
|
6702
|
+
CACHE_PATH = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
6703
|
+
DEVICE_ID_PATH = path13.join(EXE_AI_DIR, "device-id");
|
|
6442
6704
|
API_BASE = "https://askexe.com/cloud";
|
|
6443
6705
|
RETRY_DELAY_MS = 500;
|
|
6444
6706
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -6462,6 +6724,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
6462
6724
|
employeeLimit: 1,
|
|
6463
6725
|
memoryLimit: 5e3
|
|
6464
6726
|
};
|
|
6727
|
+
_prismaPromise = null;
|
|
6728
|
+
_prismaFailed = false;
|
|
6465
6729
|
CACHE_MAX_AGE_MS = 36e5;
|
|
6466
6730
|
_revalTimer = null;
|
|
6467
6731
|
}
|
|
@@ -6478,12 +6742,12 @@ __export(plan_limits_exports, {
|
|
|
6478
6742
|
countActiveMemories: () => countActiveMemories,
|
|
6479
6743
|
getLicenseSync: () => getLicenseSync
|
|
6480
6744
|
});
|
|
6481
|
-
import { readFileSync as
|
|
6482
|
-
import
|
|
6745
|
+
import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
|
|
6746
|
+
import path14 from "path";
|
|
6483
6747
|
function getLicenseSync() {
|
|
6484
6748
|
try {
|
|
6485
|
-
if (!
|
|
6486
|
-
const raw = JSON.parse(
|
|
6749
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
6750
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
6487
6751
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
6488
6752
|
const parts = raw.token.split(".");
|
|
6489
6753
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -6550,8 +6814,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
6550
6814
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
6551
6815
|
let count = 0;
|
|
6552
6816
|
try {
|
|
6553
|
-
if (
|
|
6554
|
-
const raw =
|
|
6817
|
+
if (existsSync12(filePath)) {
|
|
6818
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
6555
6819
|
const employees = JSON.parse(raw);
|
|
6556
6820
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
6557
6821
|
}
|
|
@@ -6588,69 +6852,17 @@ var init_plan_limits = __esm({
|
|
|
6588
6852
|
this.name = "PlanLimitError";
|
|
6589
6853
|
}
|
|
6590
6854
|
};
|
|
6591
|
-
CACHE_PATH2 =
|
|
6592
|
-
}
|
|
6593
|
-
});
|
|
6594
|
-
|
|
6595
|
-
// src/lib/notifications.ts
|
|
6596
|
-
import crypto4 from "crypto";
|
|
6597
|
-
import path16 from "path";
|
|
6598
|
-
import os6 from "os";
|
|
6599
|
-
import {
|
|
6600
|
-
readFileSync as readFileSync9,
|
|
6601
|
-
readdirSync as readdirSync4,
|
|
6602
|
-
unlinkSync as unlinkSync4,
|
|
6603
|
-
existsSync as existsSync11,
|
|
6604
|
-
rmdirSync
|
|
6605
|
-
} from "fs";
|
|
6606
|
-
async function writeNotification(notification) {
|
|
6607
|
-
try {
|
|
6608
|
-
const client = getClient();
|
|
6609
|
-
const id = crypto4.randomUUID();
|
|
6610
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6611
|
-
await client.execute({
|
|
6612
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
6613
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
6614
|
-
args: [
|
|
6615
|
-
id,
|
|
6616
|
-
notification.agentId,
|
|
6617
|
-
notification.agentRole,
|
|
6618
|
-
notification.event,
|
|
6619
|
-
notification.project,
|
|
6620
|
-
notification.summary,
|
|
6621
|
-
notification.taskFile ?? null,
|
|
6622
|
-
now
|
|
6623
|
-
]
|
|
6624
|
-
});
|
|
6625
|
-
} catch (err) {
|
|
6626
|
-
process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
6627
|
-
`);
|
|
6628
|
-
}
|
|
6629
|
-
}
|
|
6630
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
6631
|
-
try {
|
|
6632
|
-
const client = getClient();
|
|
6633
|
-
await client.execute({
|
|
6634
|
-
sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
|
|
6635
|
-
args: [taskFile]
|
|
6636
|
-
});
|
|
6637
|
-
} catch {
|
|
6638
|
-
}
|
|
6639
|
-
}
|
|
6640
|
-
var init_notifications = __esm({
|
|
6641
|
-
"src/lib/notifications.ts"() {
|
|
6642
|
-
"use strict";
|
|
6643
|
-
init_database();
|
|
6855
|
+
CACHE_PATH2 = path14.join(EXE_AI_DIR, "license-cache.json");
|
|
6644
6856
|
}
|
|
6645
6857
|
});
|
|
6646
6858
|
|
|
6647
6859
|
// src/lib/session-registry.ts
|
|
6648
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
6860
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
|
|
6649
6861
|
import path17 from "path";
|
|
6650
6862
|
import os7 from "os";
|
|
6651
6863
|
function registerSession(entry) {
|
|
6652
6864
|
const dir = path17.dirname(REGISTRY_PATH);
|
|
6653
|
-
if (!
|
|
6865
|
+
if (!existsSync13(dir)) {
|
|
6654
6866
|
mkdirSync5(dir, { recursive: true });
|
|
6655
6867
|
}
|
|
6656
6868
|
const sessions = listSessions();
|
|
@@ -6660,7 +6872,7 @@ function registerSession(entry) {
|
|
|
6660
6872
|
} else {
|
|
6661
6873
|
sessions.push(entry);
|
|
6662
6874
|
}
|
|
6663
|
-
|
|
6875
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
6664
6876
|
}
|
|
6665
6877
|
function listSessions() {
|
|
6666
6878
|
try {
|
|
@@ -6867,16 +7079,16 @@ __export(intercom_queue_exports, {
|
|
|
6867
7079
|
queueIntercom: () => queueIntercom,
|
|
6868
7080
|
readQueue: () => readQueue
|
|
6869
7081
|
});
|
|
6870
|
-
import { readFileSync as readFileSync11, writeFileSync as
|
|
7082
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, renameSync as renameSync3, existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
|
|
6871
7083
|
import path18 from "path";
|
|
6872
7084
|
import os8 from "os";
|
|
6873
7085
|
function ensureDir() {
|
|
6874
7086
|
const dir = path18.dirname(QUEUE_PATH);
|
|
6875
|
-
if (!
|
|
7087
|
+
if (!existsSync14(dir)) mkdirSync6(dir, { recursive: true });
|
|
6876
7088
|
}
|
|
6877
7089
|
function readQueue() {
|
|
6878
7090
|
try {
|
|
6879
|
-
if (!
|
|
7091
|
+
if (!existsSync14(QUEUE_PATH)) return [];
|
|
6880
7092
|
return JSON.parse(readFileSync11(QUEUE_PATH, "utf8"));
|
|
6881
7093
|
} catch {
|
|
6882
7094
|
return [];
|
|
@@ -6885,7 +7097,7 @@ function readQueue() {
|
|
|
6885
7097
|
function writeQueue(queue) {
|
|
6886
7098
|
ensureDir();
|
|
6887
7099
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
6888
|
-
|
|
7100
|
+
writeFileSync9(tmp, JSON.stringify(queue, null, 2));
|
|
6889
7101
|
renameSync3(tmp, QUEUE_PATH);
|
|
6890
7102
|
}
|
|
6891
7103
|
function queueIntercom(targetSession, reason) {
|
|
@@ -6985,7 +7197,7 @@ var init_intercom_queue = __esm({
|
|
|
6985
7197
|
});
|
|
6986
7198
|
|
|
6987
7199
|
// src/lib/session-kill-telemetry.ts
|
|
6988
|
-
import
|
|
7200
|
+
import crypto6 from "crypto";
|
|
6989
7201
|
async function recordSessionKill(input) {
|
|
6990
7202
|
try {
|
|
6991
7203
|
const client = getClient();
|
|
@@ -6995,7 +7207,7 @@ async function recordSessionKill(input) {
|
|
|
6995
7207
|
ticks_idle, estimated_tokens_saved)
|
|
6996
7208
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
6997
7209
|
args: [
|
|
6998
|
-
|
|
7210
|
+
crypto6.randomUUID(),
|
|
6999
7211
|
input.sessionName,
|
|
7000
7212
|
input.agentId,
|
|
7001
7213
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7348,6 +7560,7 @@ __export(tmux_routing_exports, {
|
|
|
7348
7560
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
7349
7561
|
isExeSession: () => isExeSession,
|
|
7350
7562
|
isSessionBusy: () => isSessionBusy,
|
|
7563
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
7351
7564
|
notifyParentExe: () => notifyParentExe,
|
|
7352
7565
|
parseParentExe: () => parseParentExe,
|
|
7353
7566
|
registerParentExe: () => registerParentExe,
|
|
@@ -7358,11 +7571,11 @@ __export(tmux_routing_exports, {
|
|
|
7358
7571
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
7359
7572
|
});
|
|
7360
7573
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
7361
|
-
import { readFileSync as readFileSync12, writeFileSync as
|
|
7574
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync7, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
7362
7575
|
import path19 from "path";
|
|
7363
7576
|
import os9 from "os";
|
|
7364
7577
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7365
|
-
import { unlinkSync as
|
|
7578
|
+
import { unlinkSync as unlinkSync4 } from "fs";
|
|
7366
7579
|
function spawnLockPath(sessionName) {
|
|
7367
7580
|
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
7368
7581
|
}
|
|
@@ -7375,11 +7588,11 @@ function isProcessAlive(pid) {
|
|
|
7375
7588
|
}
|
|
7376
7589
|
}
|
|
7377
7590
|
function acquireSpawnLock2(sessionName) {
|
|
7378
|
-
if (!
|
|
7591
|
+
if (!existsSync15(SPAWN_LOCK_DIR)) {
|
|
7379
7592
|
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
7380
7593
|
}
|
|
7381
7594
|
const lockFile = spawnLockPath(sessionName);
|
|
7382
|
-
if (
|
|
7595
|
+
if (existsSync15(lockFile)) {
|
|
7383
7596
|
try {
|
|
7384
7597
|
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
7385
7598
|
const age = Date.now() - lock.timestamp;
|
|
@@ -7389,12 +7602,12 @@ function acquireSpawnLock2(sessionName) {
|
|
|
7389
7602
|
} catch {
|
|
7390
7603
|
}
|
|
7391
7604
|
}
|
|
7392
|
-
|
|
7605
|
+
writeFileSync10(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
7393
7606
|
return true;
|
|
7394
7607
|
}
|
|
7395
7608
|
function releaseSpawnLock2(sessionName) {
|
|
7396
7609
|
try {
|
|
7397
|
-
|
|
7610
|
+
unlinkSync4(spawnLockPath(sessionName));
|
|
7398
7611
|
} catch {
|
|
7399
7612
|
}
|
|
7400
7613
|
}
|
|
@@ -7407,7 +7620,7 @@ function resolveBehaviorsExporterScript() {
|
|
|
7407
7620
|
"bin",
|
|
7408
7621
|
"exe-export-behaviors.js"
|
|
7409
7622
|
);
|
|
7410
|
-
return
|
|
7623
|
+
return existsSync15(scriptPath) ? scriptPath : null;
|
|
7411
7624
|
} catch {
|
|
7412
7625
|
return null;
|
|
7413
7626
|
}
|
|
@@ -7473,12 +7686,12 @@ function extractRootExe(name) {
|
|
|
7473
7686
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
7474
7687
|
}
|
|
7475
7688
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
7476
|
-
if (!
|
|
7689
|
+
if (!existsSync15(SESSION_CACHE)) {
|
|
7477
7690
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
7478
7691
|
}
|
|
7479
7692
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
7480
7693
|
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
7481
|
-
|
|
7694
|
+
writeFileSync10(filePath, JSON.stringify({
|
|
7482
7695
|
parentExe: rootExe,
|
|
7483
7696
|
dispatchedBy: dispatchedBy || rootExe,
|
|
7484
7697
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7565,7 +7778,7 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
7565
7778
|
}
|
|
7566
7779
|
function readDebounceState() {
|
|
7567
7780
|
try {
|
|
7568
|
-
if (!
|
|
7781
|
+
if (!existsSync15(DEBOUNCE_FILE)) return {};
|
|
7569
7782
|
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
7570
7783
|
const state = {};
|
|
7571
7784
|
for (const [key, val] of Object.entries(raw)) {
|
|
@@ -7582,8 +7795,8 @@ function readDebounceState() {
|
|
|
7582
7795
|
}
|
|
7583
7796
|
function writeDebounceState(state) {
|
|
7584
7797
|
try {
|
|
7585
|
-
if (!
|
|
7586
|
-
|
|
7798
|
+
if (!existsSync15(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
7799
|
+
writeFileSync10(DEBOUNCE_FILE, JSON.stringify(state));
|
|
7587
7800
|
} catch {
|
|
7588
7801
|
}
|
|
7589
7802
|
}
|
|
@@ -7682,7 +7895,7 @@ function sendIntercom(targetSession) {
|
|
|
7682
7895
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7683
7896
|
const agent = baseAgentName(rawAgent);
|
|
7684
7897
|
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
7685
|
-
if (
|
|
7898
|
+
if (existsSync15(markerPath)) {
|
|
7686
7899
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
7687
7900
|
return "debounced";
|
|
7688
7901
|
}
|
|
@@ -7692,8 +7905,8 @@ function sendIntercom(targetSession) {
|
|
|
7692
7905
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7693
7906
|
const agent = baseAgentName(rawAgent);
|
|
7694
7907
|
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
7695
|
-
if (
|
|
7696
|
-
const files =
|
|
7908
|
+
if (existsSync15(taskDir)) {
|
|
7909
|
+
const files = readdirSync4(taskDir).filter(
|
|
7697
7910
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
7698
7911
|
);
|
|
7699
7912
|
if (files.length === 0) {
|
|
@@ -7752,6 +7965,21 @@ function notifyParentExe(sessionKey) {
|
|
|
7752
7965
|
}
|
|
7753
7966
|
return true;
|
|
7754
7967
|
}
|
|
7968
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
7969
|
+
const transport = getTransport();
|
|
7970
|
+
try {
|
|
7971
|
+
const sessions = transport.listSessions();
|
|
7972
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
7973
|
+
execSync7(
|
|
7974
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
7975
|
+
{ timeout: 3e3 }
|
|
7976
|
+
);
|
|
7977
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
7978
|
+
return true;
|
|
7979
|
+
} catch {
|
|
7980
|
+
return false;
|
|
7981
|
+
}
|
|
7982
|
+
}
|
|
7755
7983
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
7756
7984
|
if (isCoordinatorName(employeeName)) {
|
|
7757
7985
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -7827,7 +8055,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7827
8055
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7828
8056
|
const logDir = path19.join(os9.homedir(), ".exe-os", "session-logs");
|
|
7829
8057
|
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7830
|
-
if (!
|
|
8058
|
+
if (!existsSync15(logDir)) {
|
|
7831
8059
|
mkdirSync7(logDir, { recursive: true });
|
|
7832
8060
|
}
|
|
7833
8061
|
transport.kill(sessionName);
|
|
@@ -7835,7 +8063,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7835
8063
|
try {
|
|
7836
8064
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7837
8065
|
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7838
|
-
if (
|
|
8066
|
+
if (existsSync15(cleanupScript)) {
|
|
7839
8067
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7840
8068
|
}
|
|
7841
8069
|
} catch {
|
|
@@ -7852,7 +8080,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7852
8080
|
const trustDir = opts?.cwd ?? projectDir;
|
|
7853
8081
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
7854
8082
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
7855
|
-
|
|
8083
|
+
writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
7856
8084
|
} catch {
|
|
7857
8085
|
}
|
|
7858
8086
|
try {
|
|
@@ -7891,7 +8119,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7891
8119
|
perms.allow = allow;
|
|
7892
8120
|
settings.permissions = perms;
|
|
7893
8121
|
mkdirSync7(projSettingsDir, { recursive: true });
|
|
7894
|
-
|
|
8122
|
+
writeFileSync10(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
7895
8123
|
}
|
|
7896
8124
|
} catch {
|
|
7897
8125
|
}
|
|
@@ -7916,7 +8144,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7916
8144
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
7917
8145
|
if (hasAgentFlag) {
|
|
7918
8146
|
identityFlag = ` --agent ${employeeName}`;
|
|
7919
|
-
} else if (
|
|
8147
|
+
} else if (existsSync15(identityPath2)) {
|
|
7920
8148
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
7921
8149
|
legacyFallbackWarned = true;
|
|
7922
8150
|
}
|
|
@@ -7946,7 +8174,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7946
8174
|
`Your parent coordinator session is ${exeSession}.`,
|
|
7947
8175
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
7948
8176
|
].join("\n");
|
|
7949
|
-
|
|
8177
|
+
writeFileSync10(ctxFile, ctxContent);
|
|
7950
8178
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
7951
8179
|
} catch {
|
|
7952
8180
|
}
|
|
@@ -8024,7 +8252,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8024
8252
|
try {
|
|
8025
8253
|
const mySession = getMySession();
|
|
8026
8254
|
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
8027
|
-
|
|
8255
|
+
writeFileSync10(dispatchInfo, JSON.stringify({
|
|
8028
8256
|
dispatchedBy: mySession,
|
|
8029
8257
|
rootExe: exeSession,
|
|
8030
8258
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -8129,6 +8357,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
8129
8357
|
args: [scope]
|
|
8130
8358
|
};
|
|
8131
8359
|
}
|
|
8360
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
8361
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
8362
|
+
if (!scope) return { sql: "", args: [] };
|
|
8363
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
8364
|
+
return {
|
|
8365
|
+
sql: ` AND ${col} = ?`,
|
|
8366
|
+
args: [scope]
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8132
8369
|
var init_task_scope = __esm({
|
|
8133
8370
|
"src/lib/task-scope.ts"() {
|
|
8134
8371
|
"use strict";
|
|
@@ -8136,6 +8373,125 @@ var init_task_scope = __esm({
|
|
|
8136
8373
|
}
|
|
8137
8374
|
});
|
|
8138
8375
|
|
|
8376
|
+
// src/lib/notifications.ts
|
|
8377
|
+
import crypto7 from "crypto";
|
|
8378
|
+
import path20 from "path";
|
|
8379
|
+
import os10 from "os";
|
|
8380
|
+
import {
|
|
8381
|
+
readFileSync as readFileSync13,
|
|
8382
|
+
readdirSync as readdirSync5,
|
|
8383
|
+
unlinkSync as unlinkSync5,
|
|
8384
|
+
existsSync as existsSync16,
|
|
8385
|
+
rmdirSync
|
|
8386
|
+
} from "fs";
|
|
8387
|
+
async function writeNotification(notification) {
|
|
8388
|
+
try {
|
|
8389
|
+
const client = getClient();
|
|
8390
|
+
const id = crypto7.randomUUID();
|
|
8391
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8392
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
8393
|
+
await client.execute({
|
|
8394
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
8395
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
8396
|
+
args: [
|
|
8397
|
+
id,
|
|
8398
|
+
notification.agentId,
|
|
8399
|
+
notification.agentRole,
|
|
8400
|
+
notification.event,
|
|
8401
|
+
notification.project,
|
|
8402
|
+
notification.summary,
|
|
8403
|
+
notification.taskFile ?? null,
|
|
8404
|
+
sessionScope,
|
|
8405
|
+
now
|
|
8406
|
+
]
|
|
8407
|
+
});
|
|
8408
|
+
} catch (err) {
|
|
8409
|
+
process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
8410
|
+
`);
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
8414
|
+
try {
|
|
8415
|
+
const client = getClient();
|
|
8416
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8417
|
+
await client.execute({
|
|
8418
|
+
sql: `UPDATE notifications SET read = 1
|
|
8419
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
8420
|
+
args: [taskFile, ...scope.args]
|
|
8421
|
+
});
|
|
8422
|
+
} catch {
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
var init_notifications = __esm({
|
|
8426
|
+
"src/lib/notifications.ts"() {
|
|
8427
|
+
"use strict";
|
|
8428
|
+
init_database();
|
|
8429
|
+
init_task_scope();
|
|
8430
|
+
}
|
|
8431
|
+
});
|
|
8432
|
+
|
|
8433
|
+
// src/lib/session-scope.ts
|
|
8434
|
+
var session_scope_exports = {};
|
|
8435
|
+
__export(session_scope_exports, {
|
|
8436
|
+
assertSessionScope: () => assertSessionScope,
|
|
8437
|
+
findSessionForProject: () => findSessionForProject,
|
|
8438
|
+
getSessionProject: () => getSessionProject
|
|
8439
|
+
});
|
|
8440
|
+
function getSessionProject(sessionName) {
|
|
8441
|
+
const sessions = listSessions();
|
|
8442
|
+
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
8443
|
+
if (!entry) return null;
|
|
8444
|
+
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
8445
|
+
return parts[parts.length - 1] ?? null;
|
|
8446
|
+
}
|
|
8447
|
+
function findSessionForProject(projectName) {
|
|
8448
|
+
const sessions = listSessions();
|
|
8449
|
+
for (const s of sessions) {
|
|
8450
|
+
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
8451
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
8452
|
+
}
|
|
8453
|
+
return null;
|
|
8454
|
+
}
|
|
8455
|
+
function assertSessionScope(actionType, targetProject) {
|
|
8456
|
+
try {
|
|
8457
|
+
const currentProject = getProjectName();
|
|
8458
|
+
const exeSession = resolveExeSession();
|
|
8459
|
+
if (!exeSession) {
|
|
8460
|
+
return { allowed: true, reason: "no_session" };
|
|
8461
|
+
}
|
|
8462
|
+
if (currentProject === targetProject) {
|
|
8463
|
+
return {
|
|
8464
|
+
allowed: true,
|
|
8465
|
+
reason: "same_session",
|
|
8466
|
+
currentProject,
|
|
8467
|
+
targetProject
|
|
8468
|
+
};
|
|
8469
|
+
}
|
|
8470
|
+
process.stderr.write(
|
|
8471
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
8472
|
+
`
|
|
8473
|
+
);
|
|
8474
|
+
return {
|
|
8475
|
+
allowed: false,
|
|
8476
|
+
reason: "cross_session_denied",
|
|
8477
|
+
currentProject,
|
|
8478
|
+
targetProject,
|
|
8479
|
+
targetSession: findSessionForProject(targetProject)?.windowName
|
|
8480
|
+
};
|
|
8481
|
+
} catch {
|
|
8482
|
+
return { allowed: true, reason: "no_session" };
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
var init_session_scope = __esm({
|
|
8486
|
+
"src/lib/session-scope.ts"() {
|
|
8487
|
+
"use strict";
|
|
8488
|
+
init_session_registry();
|
|
8489
|
+
init_project_name();
|
|
8490
|
+
init_tmux_routing();
|
|
8491
|
+
init_employees();
|
|
8492
|
+
}
|
|
8493
|
+
});
|
|
8494
|
+
|
|
8139
8495
|
// src/lib/tasks-crud.ts
|
|
8140
8496
|
var tasks_crud_exports = {};
|
|
8141
8497
|
__export(tasks_crud_exports, {
|
|
@@ -8153,12 +8509,12 @@ __export(tasks_crud_exports, {
|
|
|
8153
8509
|
updateTaskStatus: () => updateTaskStatus,
|
|
8154
8510
|
writeCheckpoint: () => writeCheckpoint
|
|
8155
8511
|
});
|
|
8156
|
-
import
|
|
8157
|
-
import
|
|
8158
|
-
import
|
|
8512
|
+
import crypto8 from "crypto";
|
|
8513
|
+
import path21 from "path";
|
|
8514
|
+
import os11 from "os";
|
|
8159
8515
|
import { execSync as execSync8 } from "child_process";
|
|
8160
8516
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
8161
|
-
import { existsSync as
|
|
8517
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
8162
8518
|
async function writeCheckpoint(input) {
|
|
8163
8519
|
const client = getClient();
|
|
8164
8520
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -8274,13 +8630,28 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
8274
8630
|
}
|
|
8275
8631
|
async function createTaskCore(input) {
|
|
8276
8632
|
const client = getClient();
|
|
8277
|
-
const id =
|
|
8633
|
+
const id = crypto8.randomUUID();
|
|
8278
8634
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8279
8635
|
const slug = slugify(input.title);
|
|
8280
8636
|
let earlySessionScope = null;
|
|
8637
|
+
let scopeMismatchWarning;
|
|
8281
8638
|
try {
|
|
8282
8639
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
8283
|
-
|
|
8640
|
+
const resolved = resolveExeSession2();
|
|
8641
|
+
if (resolved && input.projectName) {
|
|
8642
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
8643
|
+
const sessionProject = getSessionProject2(resolved);
|
|
8644
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
8645
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
8646
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
8647
|
+
`);
|
|
8648
|
+
earlySessionScope = null;
|
|
8649
|
+
} else {
|
|
8650
|
+
earlySessionScope = resolved;
|
|
8651
|
+
}
|
|
8652
|
+
} else {
|
|
8653
|
+
earlySessionScope = resolved;
|
|
8654
|
+
}
|
|
8284
8655
|
} catch {
|
|
8285
8656
|
}
|
|
8286
8657
|
const scope = earlySessionScope ?? "default";
|
|
@@ -8331,10 +8702,14 @@ async function createTaskCore(input) {
|
|
|
8331
8702
|
${laneWarning}` : laneWarning;
|
|
8332
8703
|
}
|
|
8333
8704
|
}
|
|
8705
|
+
if (scopeMismatchWarning) {
|
|
8706
|
+
warning = warning ? `${warning}
|
|
8707
|
+
${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
8708
|
+
}
|
|
8334
8709
|
if (input.baseDir) {
|
|
8335
8710
|
try {
|
|
8336
|
-
await mkdir4(
|
|
8337
|
-
await mkdir4(
|
|
8711
|
+
await mkdir4(path21.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
8712
|
+
await mkdir4(path21.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
8338
8713
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
8339
8714
|
await ensureGitignoreExe(input.baseDir);
|
|
8340
8715
|
} catch {
|
|
@@ -8370,13 +8745,19 @@ ${laneWarning}` : laneWarning;
|
|
|
8370
8745
|
});
|
|
8371
8746
|
if (input.baseDir) {
|
|
8372
8747
|
try {
|
|
8373
|
-
const EXE_OS_DIR =
|
|
8374
|
-
const mdPath =
|
|
8375
|
-
const mdDir =
|
|
8376
|
-
if (!
|
|
8748
|
+
const EXE_OS_DIR = path21.join(os11.homedir(), ".exe-os");
|
|
8749
|
+
const mdPath = path21.join(EXE_OS_DIR, taskFile);
|
|
8750
|
+
const mdDir = path21.dirname(mdPath);
|
|
8751
|
+
if (!existsSync17(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
8377
8752
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
8378
8753
|
const mdContent = `# ${input.title}
|
|
8379
8754
|
|
|
8755
|
+
## MANDATORY: When done
|
|
8756
|
+
|
|
8757
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
8758
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
8759
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
8760
|
+
|
|
8380
8761
|
**ID:** ${id}
|
|
8381
8762
|
**Status:** ${initialStatus}
|
|
8382
8763
|
**Priority:** ${input.priority}
|
|
@@ -8390,12 +8771,6 @@ ${laneWarning}` : laneWarning;
|
|
|
8390
8771
|
## Context
|
|
8391
8772
|
|
|
8392
8773
|
${input.context}
|
|
8393
|
-
|
|
8394
|
-
## MANDATORY: When done
|
|
8395
|
-
|
|
8396
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
8397
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
8398
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
8399
8774
|
`;
|
|
8400
8775
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
8401
8776
|
} catch (err) {
|
|
@@ -8644,7 +9019,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
8644
9019
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
8645
9020
|
} catch {
|
|
8646
9021
|
}
|
|
8647
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
9022
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
8648
9023
|
try {
|
|
8649
9024
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
8650
9025
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -8673,9 +9048,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
8673
9048
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
8674
9049
|
}
|
|
8675
9050
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
8676
|
-
const archPath =
|
|
9051
|
+
const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
8677
9052
|
try {
|
|
8678
|
-
if (
|
|
9053
|
+
if (existsSync17(archPath)) return;
|
|
8679
9054
|
const template = [
|
|
8680
9055
|
`# ${projectName} \u2014 System Architecture`,
|
|
8681
9056
|
"",
|
|
@@ -8708,10 +9083,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
8708
9083
|
}
|
|
8709
9084
|
}
|
|
8710
9085
|
async function ensureGitignoreExe(baseDir) {
|
|
8711
|
-
const gitignorePath =
|
|
9086
|
+
const gitignorePath = path21.join(baseDir, ".gitignore");
|
|
8712
9087
|
try {
|
|
8713
|
-
if (
|
|
8714
|
-
const content =
|
|
9088
|
+
if (existsSync17(gitignorePath)) {
|
|
9089
|
+
const content = readFileSync14(gitignorePath, "utf-8");
|
|
8715
9090
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
8716
9091
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
8717
9092
|
} else {
|
|
@@ -8742,58 +9117,42 @@ var init_tasks_crud = __esm({
|
|
|
8742
9117
|
});
|
|
8743
9118
|
|
|
8744
9119
|
// src/lib/tasks-review.ts
|
|
8745
|
-
import
|
|
8746
|
-
import { existsSync as
|
|
9120
|
+
import path22 from "path";
|
|
9121
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
8747
9122
|
async function countPendingReviews(sessionScope) {
|
|
8748
9123
|
const client = getClient();
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
args: [sessionScope]
|
|
8753
|
-
});
|
|
8754
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
8755
|
-
}
|
|
9124
|
+
const scope = strictSessionScopeFilter(
|
|
9125
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9126
|
+
);
|
|
8756
9127
|
const result = await client.execute({
|
|
8757
|
-
sql:
|
|
8758
|
-
|
|
9128
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9129
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
9130
|
+
args: [...scope.args]
|
|
8759
9131
|
});
|
|
8760
9132
|
return Number(result.rows[0]?.cnt) || 0;
|
|
8761
9133
|
}
|
|
8762
9134
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
8763
9135
|
const client = getClient();
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
8768
|
-
AND session_scope = ?`,
|
|
8769
|
-
args: [sinceIso, sessionScope]
|
|
8770
|
-
});
|
|
8771
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
8772
|
-
}
|
|
9136
|
+
const scope = strictSessionScopeFilter(
|
|
9137
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9138
|
+
);
|
|
8773
9139
|
const result = await client.execute({
|
|
8774
9140
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
8775
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
8776
|
-
args: [sinceIso]
|
|
9141
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
9142
|
+
args: [sinceIso, ...scope.args]
|
|
8777
9143
|
});
|
|
8778
9144
|
return Number(result.rows[0]?.cnt) || 0;
|
|
8779
9145
|
}
|
|
8780
9146
|
async function listPendingReviews(limit, sessionScope) {
|
|
8781
9147
|
const client = getClient();
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
WHERE status = 'needs_review'
|
|
8786
|
-
AND session_scope = ?
|
|
8787
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
8788
|
-
args: [sessionScope, limit]
|
|
8789
|
-
});
|
|
8790
|
-
return result2.rows;
|
|
8791
|
-
}
|
|
9148
|
+
const scope = strictSessionScopeFilter(
|
|
9149
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9150
|
+
);
|
|
8792
9151
|
const result = await client.execute({
|
|
8793
9152
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
8794
|
-
WHERE status = 'needs_review'
|
|
9153
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
8795
9154
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
8796
|
-
args: [limit]
|
|
9155
|
+
args: [...scope.args, limit]
|
|
8797
9156
|
});
|
|
8798
9157
|
return result.rows;
|
|
8799
9158
|
}
|
|
@@ -8805,7 +9164,7 @@ async function cleanupOrphanedReviews() {
|
|
|
8805
9164
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
8806
9165
|
AND assigned_by = 'system'
|
|
8807
9166
|
AND title LIKE 'Review:%'
|
|
8808
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
9167
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
8809
9168
|
args: [now]
|
|
8810
9169
|
});
|
|
8811
9170
|
const r1b = await client.execute({
|
|
@@ -8924,11 +9283,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
8924
9283
|
);
|
|
8925
9284
|
}
|
|
8926
9285
|
try {
|
|
8927
|
-
const cacheDir =
|
|
8928
|
-
if (
|
|
9286
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
9287
|
+
if (existsSync18(cacheDir)) {
|
|
8929
9288
|
for (const f of readdirSync6(cacheDir)) {
|
|
8930
9289
|
if (f.startsWith("review-notified-")) {
|
|
8931
|
-
unlinkSync6(
|
|
9290
|
+
unlinkSync6(path22.join(cacheDir, f));
|
|
8932
9291
|
}
|
|
8933
9292
|
}
|
|
8934
9293
|
}
|
|
@@ -8945,11 +9304,12 @@ var init_tasks_review = __esm({
|
|
|
8945
9304
|
init_tmux_routing();
|
|
8946
9305
|
init_session_key();
|
|
8947
9306
|
init_state_bus();
|
|
9307
|
+
init_task_scope();
|
|
8948
9308
|
}
|
|
8949
9309
|
});
|
|
8950
9310
|
|
|
8951
9311
|
// src/lib/tasks-chain.ts
|
|
8952
|
-
import
|
|
9312
|
+
import path23 from "path";
|
|
8953
9313
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
8954
9314
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
8955
9315
|
const client = getClient();
|
|
@@ -8966,7 +9326,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
8966
9326
|
});
|
|
8967
9327
|
for (const ur of unblockedRows.rows) {
|
|
8968
9328
|
try {
|
|
8969
|
-
const ubFile =
|
|
9329
|
+
const ubFile = path23.join(baseDir, String(ur.task_file));
|
|
8970
9330
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
8971
9331
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
8972
9332
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -9001,7 +9361,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
9001
9361
|
const scScope = sessionScopeFilter();
|
|
9002
9362
|
const remaining = await client.execute({
|
|
9003
9363
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9004
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
9364
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
9005
9365
|
args: [parentTaskId, ...scScope.args]
|
|
9006
9366
|
});
|
|
9007
9367
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -9033,68 +9393,6 @@ var init_tasks_chain = __esm({
|
|
|
9033
9393
|
}
|
|
9034
9394
|
});
|
|
9035
9395
|
|
|
9036
|
-
// src/lib/session-scope.ts
|
|
9037
|
-
var session_scope_exports = {};
|
|
9038
|
-
__export(session_scope_exports, {
|
|
9039
|
-
assertSessionScope: () => assertSessionScope,
|
|
9040
|
-
findSessionForProject: () => findSessionForProject,
|
|
9041
|
-
getSessionProject: () => getSessionProject
|
|
9042
|
-
});
|
|
9043
|
-
function getSessionProject(sessionName) {
|
|
9044
|
-
const sessions = listSessions();
|
|
9045
|
-
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
9046
|
-
if (!entry) return null;
|
|
9047
|
-
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
9048
|
-
return parts[parts.length - 1] ?? null;
|
|
9049
|
-
}
|
|
9050
|
-
function findSessionForProject(projectName) {
|
|
9051
|
-
const sessions = listSessions();
|
|
9052
|
-
for (const s of sessions) {
|
|
9053
|
-
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
9054
|
-
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
9055
|
-
}
|
|
9056
|
-
return null;
|
|
9057
|
-
}
|
|
9058
|
-
function assertSessionScope(actionType, targetProject) {
|
|
9059
|
-
try {
|
|
9060
|
-
const currentProject = getProjectName();
|
|
9061
|
-
const exeSession = resolveExeSession();
|
|
9062
|
-
if (!exeSession) {
|
|
9063
|
-
return { allowed: true, reason: "no_session" };
|
|
9064
|
-
}
|
|
9065
|
-
if (currentProject === targetProject) {
|
|
9066
|
-
return {
|
|
9067
|
-
allowed: true,
|
|
9068
|
-
reason: "same_session",
|
|
9069
|
-
currentProject,
|
|
9070
|
-
targetProject
|
|
9071
|
-
};
|
|
9072
|
-
}
|
|
9073
|
-
process.stderr.write(
|
|
9074
|
-
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
9075
|
-
`
|
|
9076
|
-
);
|
|
9077
|
-
return {
|
|
9078
|
-
allowed: false,
|
|
9079
|
-
reason: "cross_session_denied",
|
|
9080
|
-
currentProject,
|
|
9081
|
-
targetProject,
|
|
9082
|
-
targetSession: findSessionForProject(targetProject)?.windowName
|
|
9083
|
-
};
|
|
9084
|
-
} catch {
|
|
9085
|
-
return { allowed: true, reason: "no_session" };
|
|
9086
|
-
}
|
|
9087
|
-
}
|
|
9088
|
-
var init_session_scope = __esm({
|
|
9089
|
-
"src/lib/session-scope.ts"() {
|
|
9090
|
-
"use strict";
|
|
9091
|
-
init_session_registry();
|
|
9092
|
-
init_project_name();
|
|
9093
|
-
init_tmux_routing();
|
|
9094
|
-
init_employees();
|
|
9095
|
-
}
|
|
9096
|
-
});
|
|
9097
|
-
|
|
9098
9396
|
// src/lib/tasks-notify.ts
|
|
9099
9397
|
async function dispatchTaskToEmployee(input) {
|
|
9100
9398
|
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
@@ -9162,10 +9460,10 @@ var init_tasks_notify = __esm({
|
|
|
9162
9460
|
});
|
|
9163
9461
|
|
|
9164
9462
|
// src/lib/behaviors.ts
|
|
9165
|
-
import
|
|
9463
|
+
import crypto9 from "crypto";
|
|
9166
9464
|
async function storeBehavior(opts) {
|
|
9167
9465
|
const client = getClient();
|
|
9168
|
-
const id =
|
|
9466
|
+
const id = crypto9.randomUUID();
|
|
9169
9467
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9170
9468
|
await client.execute({
|
|
9171
9469
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -9222,7 +9520,7 @@ __export(skill_learning_exports, {
|
|
|
9222
9520
|
storeTrajectory: () => storeTrajectory,
|
|
9223
9521
|
sweepTrajectories: () => sweepTrajectories
|
|
9224
9522
|
});
|
|
9225
|
-
import
|
|
9523
|
+
import crypto10 from "crypto";
|
|
9226
9524
|
async function extractTrajectory(taskId, agentId) {
|
|
9227
9525
|
const client = getClient();
|
|
9228
9526
|
const result = await client.execute({
|
|
@@ -9251,11 +9549,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
9251
9549
|
return signature;
|
|
9252
9550
|
}
|
|
9253
9551
|
function hashSignature(signature) {
|
|
9254
|
-
return
|
|
9552
|
+
return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
9255
9553
|
}
|
|
9256
9554
|
async function storeTrajectory(opts) {
|
|
9257
9555
|
const client = getClient();
|
|
9258
|
-
const id =
|
|
9556
|
+
const id = crypto10.randomUUID();
|
|
9259
9557
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9260
9558
|
const signatureHash = hashSignature(opts.signature);
|
|
9261
9559
|
await client.execute({
|
|
@@ -9520,8 +9818,8 @@ __export(tasks_exports, {
|
|
|
9520
9818
|
updateTaskStatus: () => updateTaskStatus,
|
|
9521
9819
|
writeCheckpoint: () => writeCheckpoint
|
|
9522
9820
|
});
|
|
9523
|
-
import
|
|
9524
|
-
import { writeFileSync as
|
|
9821
|
+
import path24 from "path";
|
|
9822
|
+
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
9525
9823
|
async function createTask(input) {
|
|
9526
9824
|
const result = await createTaskCore(input);
|
|
9527
9825
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -9540,12 +9838,12 @@ async function updateTask(input) {
|
|
|
9540
9838
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
9541
9839
|
try {
|
|
9542
9840
|
const agent = String(row.assigned_to);
|
|
9543
|
-
const cacheDir =
|
|
9544
|
-
const cachePath =
|
|
9841
|
+
const cacheDir = path24.join(EXE_AI_DIR, "session-cache");
|
|
9842
|
+
const cachePath = path24.join(cacheDir, `current-task-${agent}.json`);
|
|
9545
9843
|
if (input.status === "in_progress") {
|
|
9546
9844
|
mkdirSync8(cacheDir, { recursive: true });
|
|
9547
|
-
|
|
9548
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
9845
|
+
writeFileSync11(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
9846
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
9549
9847
|
try {
|
|
9550
9848
|
unlinkSync7(cachePath);
|
|
9551
9849
|
} catch {
|
|
@@ -9553,10 +9851,10 @@ async function updateTask(input) {
|
|
|
9553
9851
|
}
|
|
9554
9852
|
} catch {
|
|
9555
9853
|
}
|
|
9556
|
-
if (input.status === "done") {
|
|
9854
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9557
9855
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
9558
9856
|
}
|
|
9559
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
9857
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
9560
9858
|
try {
|
|
9561
9859
|
const client = getClient();
|
|
9562
9860
|
const taskTitle = String(row.title);
|
|
@@ -9572,7 +9870,7 @@ async function updateTask(input) {
|
|
|
9572
9870
|
if (!isCoordinatorName(assignedAgent)) {
|
|
9573
9871
|
try {
|
|
9574
9872
|
const draftClient = getClient();
|
|
9575
|
-
if (input.status === "done") {
|
|
9873
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9576
9874
|
await draftClient.execute({
|
|
9577
9875
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
9578
9876
|
args: [assignedAgent]
|
|
@@ -9589,7 +9887,7 @@ async function updateTask(input) {
|
|
|
9589
9887
|
try {
|
|
9590
9888
|
const client = getClient();
|
|
9591
9889
|
const cascaded = await client.execute({
|
|
9592
|
-
sql: `UPDATE tasks SET status = '
|
|
9890
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
9593
9891
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
9594
9892
|
args: [now, taskId]
|
|
9595
9893
|
});
|
|
@@ -9602,14 +9900,14 @@ async function updateTask(input) {
|
|
|
9602
9900
|
} catch {
|
|
9603
9901
|
}
|
|
9604
9902
|
}
|
|
9605
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
9903
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
9606
9904
|
if (isTerminal) {
|
|
9607
9905
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
9608
9906
|
if (!isCoordinator) {
|
|
9609
9907
|
notifyTaskDone();
|
|
9610
9908
|
}
|
|
9611
9909
|
await markTaskNotificationsRead(taskFile);
|
|
9612
|
-
if (input.status === "done") {
|
|
9910
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9613
9911
|
try {
|
|
9614
9912
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
9615
9913
|
} catch {
|
|
@@ -9629,7 +9927,7 @@ async function updateTask(input) {
|
|
|
9629
9927
|
}
|
|
9630
9928
|
}
|
|
9631
9929
|
}
|
|
9632
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
9930
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
9633
9931
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
9634
9932
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
9635
9933
|
taskId,
|
|
@@ -9710,17 +10008,17 @@ __export(identity_exports, {
|
|
|
9710
10008
|
listIdentities: () => listIdentities,
|
|
9711
10009
|
updateIdentity: () => updateIdentity
|
|
9712
10010
|
});
|
|
9713
|
-
import { existsSync as
|
|
10011
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
9714
10012
|
import { readdirSync as readdirSync7 } from "fs";
|
|
9715
|
-
import
|
|
10013
|
+
import path25 from "path";
|
|
9716
10014
|
import { createHash as createHash2 } from "crypto";
|
|
9717
10015
|
function ensureDir2() {
|
|
9718
|
-
if (!
|
|
10016
|
+
if (!existsSync19(IDENTITY_DIR2)) {
|
|
9719
10017
|
mkdirSync9(IDENTITY_DIR2, { recursive: true });
|
|
9720
10018
|
}
|
|
9721
10019
|
}
|
|
9722
10020
|
function identityPath(agentId) {
|
|
9723
|
-
return
|
|
10021
|
+
return path25.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
9724
10022
|
}
|
|
9725
10023
|
function parseFrontmatter(raw) {
|
|
9726
10024
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -9761,8 +10059,8 @@ function contentHash(content) {
|
|
|
9761
10059
|
}
|
|
9762
10060
|
function getIdentity(agentId) {
|
|
9763
10061
|
const filePath = identityPath(agentId);
|
|
9764
|
-
if (!
|
|
9765
|
-
const raw =
|
|
10062
|
+
if (!existsSync19(filePath)) return null;
|
|
10063
|
+
const raw = readFileSync15(filePath, "utf-8");
|
|
9766
10064
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
9767
10065
|
return {
|
|
9768
10066
|
agentId,
|
|
@@ -9776,7 +10074,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
9776
10074
|
ensureDir2();
|
|
9777
10075
|
const filePath = identityPath(agentId);
|
|
9778
10076
|
const hash = contentHash(content);
|
|
9779
|
-
|
|
10077
|
+
writeFileSync12(filePath, content, "utf-8");
|
|
9780
10078
|
try {
|
|
9781
10079
|
const client = getClient();
|
|
9782
10080
|
await client.execute({
|
|
@@ -9832,7 +10130,7 @@ var init_identity = __esm({
|
|
|
9832
10130
|
"use strict";
|
|
9833
10131
|
init_config();
|
|
9834
10132
|
init_database();
|
|
9835
|
-
IDENTITY_DIR2 =
|
|
10133
|
+
IDENTITY_DIR2 = path25.join(EXE_AI_DIR, "identity");
|
|
9836
10134
|
}
|
|
9837
10135
|
});
|
|
9838
10136
|
|
|
@@ -10378,10 +10676,10 @@ ${PLAN_MODE_COMPAT}
|
|
|
10378
10676
|
});
|
|
10379
10677
|
|
|
10380
10678
|
// src/lib/messaging.ts
|
|
10381
|
-
import
|
|
10679
|
+
import crypto11 from "crypto";
|
|
10382
10680
|
function generateUlid() {
|
|
10383
10681
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
10384
|
-
const random =
|
|
10682
|
+
const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
|
|
10385
10683
|
return (timestamp + random).toUpperCase();
|
|
10386
10684
|
}
|
|
10387
10685
|
function rowToMessage(row) {
|
|
@@ -10392,6 +10690,7 @@ function rowToMessage(row) {
|
|
|
10392
10690
|
targetAgent: row.target_agent,
|
|
10393
10691
|
targetProject: row.target_project ?? null,
|
|
10394
10692
|
targetDevice: row.target_device,
|
|
10693
|
+
sessionScope: row.session_scope ?? null,
|
|
10395
10694
|
content: row.content,
|
|
10396
10695
|
priority: row.priority ?? "normal",
|
|
10397
10696
|
status: row.status ?? "pending",
|
|
@@ -10409,15 +10708,17 @@ async function sendMessage(input) {
|
|
|
10409
10708
|
const id = generateUlid();
|
|
10410
10709
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10411
10710
|
const targetDevice = input.targetDevice ?? "local";
|
|
10711
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
10412
10712
|
await client.execute({
|
|
10413
|
-
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
|
|
10414
|
-
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
10713
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
10714
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
10415
10715
|
args: [
|
|
10416
10716
|
id,
|
|
10417
10717
|
input.fromAgent,
|
|
10418
10718
|
input.targetAgent,
|
|
10419
10719
|
input.targetProject ?? null,
|
|
10420
10720
|
targetDevice,
|
|
10721
|
+
sessionScope,
|
|
10421
10722
|
input.content,
|
|
10422
10723
|
input.priority ?? "normal",
|
|
10423
10724
|
now
|
|
@@ -10431,9 +10732,10 @@ async function sendMessage(input) {
|
|
|
10431
10732
|
}
|
|
10432
10733
|
} catch {
|
|
10433
10734
|
}
|
|
10735
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
10434
10736
|
const result = await client.execute({
|
|
10435
|
-
sql:
|
|
10436
|
-
args: [id]
|
|
10737
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
10738
|
+
args: [id, ...sentScope.args]
|
|
10437
10739
|
});
|
|
10438
10740
|
return rowToMessage(result.rows[0]);
|
|
10439
10741
|
}
|
|
@@ -10454,6 +10756,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
10454
10756
|
fromAgent: msg.fromAgent,
|
|
10455
10757
|
targetAgent: msg.targetAgent,
|
|
10456
10758
|
targetProject: msg.targetProject,
|
|
10759
|
+
sessionScope: msg.sessionScope,
|
|
10457
10760
|
content: msg.content,
|
|
10458
10761
|
priority: msg.priority,
|
|
10459
10762
|
createdAt: msg.createdAt
|
|
@@ -10497,7 +10800,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
10497
10800
|
} catch {
|
|
10498
10801
|
const newRetryCount = msg.retryCount + 1;
|
|
10499
10802
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
10500
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
10803
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
10501
10804
|
} else {
|
|
10502
10805
|
await client.execute({
|
|
10503
10806
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -10507,11 +10810,13 @@ async function deliverLocalMessage(messageId) {
|
|
|
10507
10810
|
return false;
|
|
10508
10811
|
}
|
|
10509
10812
|
}
|
|
10510
|
-
async function markFailed(messageId, reason) {
|
|
10813
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
10511
10814
|
const client = getClient();
|
|
10815
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
10512
10816
|
await client.execute({
|
|
10513
|
-
sql:
|
|
10514
|
-
|
|
10817
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
10818
|
+
WHERE id = ?${scope.sql}`,
|
|
10819
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
10515
10820
|
});
|
|
10516
10821
|
}
|
|
10517
10822
|
var MAX_RETRIES3, _wsClientSend;
|
|
@@ -10520,19 +10825,20 @@ var init_messaging = __esm({
|
|
|
10520
10825
|
"use strict";
|
|
10521
10826
|
init_database();
|
|
10522
10827
|
init_tmux_routing();
|
|
10828
|
+
init_task_scope();
|
|
10523
10829
|
MAX_RETRIES3 = 10;
|
|
10524
10830
|
_wsClientSend = null;
|
|
10525
10831
|
}
|
|
10526
10832
|
});
|
|
10527
10833
|
|
|
10528
10834
|
// src/gateway/whatsapp-accounts.ts
|
|
10529
|
-
import { readFileSync as
|
|
10835
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
10530
10836
|
import { join } from "path";
|
|
10531
10837
|
import { homedir } from "os";
|
|
10532
10838
|
function loadAccounts() {
|
|
10533
10839
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
10534
10840
|
try {
|
|
10535
|
-
const raw =
|
|
10841
|
+
const raw = readFileSync16(CONFIG_PATH2, "utf8");
|
|
10536
10842
|
const parsed = JSON.parse(raw);
|
|
10537
10843
|
if (!Array.isArray(parsed)) {
|
|
10538
10844
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -10945,8 +11251,8 @@ __export(wiki_client_exports, {
|
|
|
10945
11251
|
listDocuments: () => listDocuments,
|
|
10946
11252
|
listWorkspaces: () => listWorkspaces
|
|
10947
11253
|
});
|
|
10948
|
-
async function wikiFetch(config2,
|
|
10949
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
11254
|
+
async function wikiFetch(config2, path45, method = "GET", body) {
|
|
11255
|
+
const url = `${config2.baseUrl}/api/v1${path45}`;
|
|
10950
11256
|
const headers = {
|
|
10951
11257
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
10952
11258
|
"Content-Type": "application/json"
|
|
@@ -10979,7 +11285,7 @@ async function wikiFetch(config2, path41, method = "GET", body) {
|
|
|
10979
11285
|
}
|
|
10980
11286
|
}
|
|
10981
11287
|
if (!response.ok) {
|
|
10982
|
-
throw new Error(`Wiki API ${method} ${
|
|
11288
|
+
throw new Error(`Wiki API ${method} ${path45}: ${response.status} ${response.statusText}`);
|
|
10983
11289
|
}
|
|
10984
11290
|
return response.json();
|
|
10985
11291
|
} finally {
|
|
@@ -11088,14 +11394,14 @@ __export(worker_gate_exports, {
|
|
|
11088
11394
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
11089
11395
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
11090
11396
|
});
|
|
11091
|
-
import { readdirSync as readdirSync10, writeFileSync as
|
|
11092
|
-
import
|
|
11397
|
+
import { readdirSync as readdirSync10, writeFileSync as writeFileSync18, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync27 } from "fs";
|
|
11398
|
+
import path35 from "path";
|
|
11093
11399
|
function tryAcquireWorkerSlot() {
|
|
11094
11400
|
try {
|
|
11095
11401
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11096
11402
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
11097
|
-
const reservationPath =
|
|
11098
|
-
|
|
11403
|
+
const reservationPath = path35.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
11404
|
+
writeFileSync18(reservationPath, String(process.pid));
|
|
11099
11405
|
const files = readdirSync10(WORKER_PID_DIR);
|
|
11100
11406
|
let alive = 0;
|
|
11101
11407
|
for (const f of files) {
|
|
@@ -11112,7 +11418,7 @@ function tryAcquireWorkerSlot() {
|
|
|
11112
11418
|
alive++;
|
|
11113
11419
|
} catch {
|
|
11114
11420
|
try {
|
|
11115
|
-
unlinkSync8(
|
|
11421
|
+
unlinkSync8(path35.join(WORKER_PID_DIR, f));
|
|
11116
11422
|
} catch {
|
|
11117
11423
|
}
|
|
11118
11424
|
}
|
|
@@ -11136,20 +11442,20 @@ function tryAcquireWorkerSlot() {
|
|
|
11136
11442
|
function registerWorkerPid(pid) {
|
|
11137
11443
|
try {
|
|
11138
11444
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11139
|
-
|
|
11445
|
+
writeFileSync18(path35.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
11140
11446
|
} catch {
|
|
11141
11447
|
}
|
|
11142
11448
|
}
|
|
11143
11449
|
function cleanupWorkerPid() {
|
|
11144
11450
|
try {
|
|
11145
|
-
unlinkSync8(
|
|
11451
|
+
unlinkSync8(path35.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
11146
11452
|
} catch {
|
|
11147
11453
|
}
|
|
11148
11454
|
}
|
|
11149
11455
|
function tryAcquireBackfillLock() {
|
|
11150
11456
|
try {
|
|
11151
11457
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11152
|
-
if (
|
|
11458
|
+
if (existsSync27(BACKFILL_LOCK)) {
|
|
11153
11459
|
try {
|
|
11154
11460
|
const pid = parseInt(
|
|
11155
11461
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -11165,7 +11471,7 @@ function tryAcquireBackfillLock() {
|
|
|
11165
11471
|
} catch {
|
|
11166
11472
|
}
|
|
11167
11473
|
}
|
|
11168
|
-
|
|
11474
|
+
writeFileSync18(BACKFILL_LOCK, String(process.pid));
|
|
11169
11475
|
return true;
|
|
11170
11476
|
} catch {
|
|
11171
11477
|
return true;
|
|
@@ -11182,9 +11488,9 @@ var init_worker_gate = __esm({
|
|
|
11182
11488
|
"src/lib/worker-gate.ts"() {
|
|
11183
11489
|
"use strict";
|
|
11184
11490
|
init_config();
|
|
11185
|
-
WORKER_PID_DIR =
|
|
11491
|
+
WORKER_PID_DIR = path35.join(EXE_AI_DIR, "worker-pids");
|
|
11186
11492
|
MAX_CONCURRENT_WORKERS = 3;
|
|
11187
|
-
BACKFILL_LOCK =
|
|
11493
|
+
BACKFILL_LOCK = path35.join(WORKER_PID_DIR, "backfill.lock");
|
|
11188
11494
|
}
|
|
11189
11495
|
});
|
|
11190
11496
|
|
|
@@ -11207,8 +11513,8 @@ __export(crdt_sync_exports, {
|
|
|
11207
11513
|
rebuildFromDb: () => rebuildFromDb
|
|
11208
11514
|
});
|
|
11209
11515
|
import * as Y from "yjs";
|
|
11210
|
-
import { readFileSync as
|
|
11211
|
-
import
|
|
11516
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync30, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
|
|
11517
|
+
import path38 from "path";
|
|
11212
11518
|
import { homedir as homedir5 } from "os";
|
|
11213
11519
|
function getStatePath() {
|
|
11214
11520
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -11220,9 +11526,9 @@ function initCrdtDoc() {
|
|
|
11220
11526
|
if (doc) return doc;
|
|
11221
11527
|
doc = new Y.Doc();
|
|
11222
11528
|
const sp = getStatePath();
|
|
11223
|
-
if (
|
|
11529
|
+
if (existsSync30(sp)) {
|
|
11224
11530
|
try {
|
|
11225
|
-
const state =
|
|
11531
|
+
const state = readFileSync25(sp);
|
|
11226
11532
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
11227
11533
|
} catch {
|
|
11228
11534
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -11364,10 +11670,10 @@ function persistState() {
|
|
|
11364
11670
|
if (!doc) return;
|
|
11365
11671
|
try {
|
|
11366
11672
|
const sp = getStatePath();
|
|
11367
|
-
const dir =
|
|
11368
|
-
if (!
|
|
11673
|
+
const dir = path38.dirname(sp);
|
|
11674
|
+
if (!existsSync30(dir)) mkdirSync15(dir, { recursive: true });
|
|
11369
11675
|
const state = Y.encodeStateAsUpdate(doc);
|
|
11370
|
-
|
|
11676
|
+
writeFileSync19(sp, Buffer.from(state));
|
|
11371
11677
|
} catch {
|
|
11372
11678
|
}
|
|
11373
11679
|
}
|
|
@@ -11408,7 +11714,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
11408
11714
|
var init_crdt_sync = __esm({
|
|
11409
11715
|
"src/lib/crdt-sync.ts"() {
|
|
11410
11716
|
"use strict";
|
|
11411
|
-
DEFAULT_STATE_PATH =
|
|
11717
|
+
DEFAULT_STATE_PATH = path38.join(homedir5(), ".exe-os", "crdt-state.bin");
|
|
11412
11718
|
_statePathOverride = null;
|
|
11413
11719
|
doc = null;
|
|
11414
11720
|
}
|
|
@@ -11421,8 +11727,8 @@ init_database();
|
|
|
11421
11727
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11422
11728
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11423
11729
|
import { spawn as spawn4 } from "child_process";
|
|
11424
|
-
import { existsSync as
|
|
11425
|
-
import
|
|
11730
|
+
import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
|
|
11731
|
+
import path44 from "path";
|
|
11426
11732
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11427
11733
|
|
|
11428
11734
|
// src/mcp/tools/recall-my-memory.ts
|
|
@@ -11729,9 +12035,9 @@ init_store();
|
|
|
11729
12035
|
init_active_agent();
|
|
11730
12036
|
init_plan_limits();
|
|
11731
12037
|
import { z as z4 } from "zod";
|
|
11732
|
-
import
|
|
11733
|
-
import { writeFileSync as
|
|
11734
|
-
import
|
|
12038
|
+
import crypto4 from "crypto";
|
|
12039
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
12040
|
+
import path15 from "path";
|
|
11735
12041
|
function registerStoreMemory(server2) {
|
|
11736
12042
|
server2.registerTool(
|
|
11737
12043
|
"store_memory",
|
|
@@ -11780,7 +12086,7 @@ function registerStoreMemory(server2) {
|
|
|
11780
12086
|
vector = null;
|
|
11781
12087
|
needsBackfill = true;
|
|
11782
12088
|
}
|
|
11783
|
-
const memoryId =
|
|
12089
|
+
const memoryId = crypto4.randomUUID();
|
|
11784
12090
|
await writeMemory({
|
|
11785
12091
|
id: memoryId,
|
|
11786
12092
|
agent_id: agentId,
|
|
@@ -11799,8 +12105,8 @@ function registerStoreMemory(server2) {
|
|
|
11799
12105
|
if (needsBackfill) {
|
|
11800
12106
|
try {
|
|
11801
12107
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11802
|
-
const flagPath =
|
|
11803
|
-
|
|
12108
|
+
const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
|
|
12109
|
+
writeFileSync6(flagPath, "1");
|
|
11804
12110
|
} catch {
|
|
11805
12111
|
}
|
|
11806
12112
|
}
|
|
@@ -11823,9 +12129,9 @@ init_plan_limits();
|
|
|
11823
12129
|
init_active_agent();
|
|
11824
12130
|
init_employees();
|
|
11825
12131
|
import { z as z5 } from "zod";
|
|
11826
|
-
import
|
|
11827
|
-
import { writeFileSync as
|
|
11828
|
-
import
|
|
12132
|
+
import crypto5 from "crypto";
|
|
12133
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
12134
|
+
import path16 from "path";
|
|
11829
12135
|
function registerCommitMemory(server2) {
|
|
11830
12136
|
server2.registerTool(
|
|
11831
12137
|
"commit_to_long_term_memory",
|
|
@@ -11872,7 +12178,7 @@ function registerCommitMemory(server2) {
|
|
|
11872
12178
|
vector = null;
|
|
11873
12179
|
needsBackfill = true;
|
|
11874
12180
|
}
|
|
11875
|
-
const memoryId =
|
|
12181
|
+
const memoryId = crypto5.randomUUID();
|
|
11876
12182
|
await assertMemoryLimit();
|
|
11877
12183
|
const memoryType = canCoordinate(agentId, agentRole) ? "adr" : "raw";
|
|
11878
12184
|
await writeMemory({
|
|
@@ -11915,8 +12221,8 @@ function registerCommitMemory(server2) {
|
|
|
11915
12221
|
if (needsBackfill) {
|
|
11916
12222
|
try {
|
|
11917
12223
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11918
|
-
const flagPath =
|
|
11919
|
-
|
|
12224
|
+
const flagPath = path16.join(exeDir, "session-cache", "needs-backfill");
|
|
12225
|
+
writeFileSync7(flagPath, "1");
|
|
11920
12226
|
} catch {
|
|
11921
12227
|
}
|
|
11922
12228
|
}
|
|
@@ -12106,10 +12412,10 @@ function registerCreateTask(server2) {
|
|
|
12106
12412
|
skipDispatch: true
|
|
12107
12413
|
});
|
|
12108
12414
|
try {
|
|
12109
|
-
const { existsSync:
|
|
12415
|
+
const { existsSync: existsSync34, mkdirSync: mkdirSync18, writeFileSync: writeFileSync21 } = await import("fs");
|
|
12110
12416
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
12111
12417
|
const idPath = identityPath2(assigned_to);
|
|
12112
|
-
if (!
|
|
12418
|
+
if (!existsSync34(idPath)) {
|
|
12113
12419
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
12114
12420
|
const employees = await loadEmployees2();
|
|
12115
12421
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -12118,8 +12424,8 @@ function registerCreateTask(server2) {
|
|
|
12118
12424
|
const template = getTemplateForTitle2(emp.role);
|
|
12119
12425
|
if (template) {
|
|
12120
12426
|
const dir = (await import("path")).dirname(idPath);
|
|
12121
|
-
if (!
|
|
12122
|
-
|
|
12427
|
+
if (!existsSync34(dir)) mkdirSync18(dir, { recursive: true });
|
|
12428
|
+
writeFileSync21(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
12123
12429
|
}
|
|
12124
12430
|
}
|
|
12125
12431
|
}
|
|
@@ -12192,7 +12498,7 @@ function registerListTasks(server2) {
|
|
|
12192
12498
|
description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all' for all projects. When querying your own tasks, project filter is skipped automatically.",
|
|
12193
12499
|
inputSchema: {
|
|
12194
12500
|
assigned_to: z8.string().optional().describe("Filter by employee name"),
|
|
12195
|
-
status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
|
|
12501
|
+
status: z8.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).optional().describe("Filter by status. Default: active tasks only (excludes closed/cancelled)"),
|
|
12196
12502
|
project_name: z8.string().optional().describe("Project name. Defaults to current project. Pass 'all' for all projects."),
|
|
12197
12503
|
priority: z8.enum(["p0", "p1", "p2"]).optional().describe("Filter by priority")
|
|
12198
12504
|
}
|
|
@@ -12263,7 +12569,7 @@ function registerUpdateTask(server2) {
|
|
|
12263
12569
|
description: "Update task status. Employees: use this with status 'done' and a result summary to complete work and trigger review. Accepts UUID, slug (filename), or title substring.",
|
|
12264
12570
|
inputSchema: {
|
|
12265
12571
|
task_id: z9.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
|
|
12266
|
-
status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled"]).describe("New status"),
|
|
12572
|
+
status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).describe("New status"),
|
|
12267
12573
|
result: z9.string().optional().describe("Result summary (include when status=done)")
|
|
12268
12574
|
}
|
|
12269
12575
|
},
|
|
@@ -12353,7 +12659,17 @@ function registerUpdateTask(server2) {
|
|
|
12353
12659
|
}
|
|
12354
12660
|
let text = `Task "${task.title}" marked ${task.status}.
|
|
12355
12661
|
File: ${task.taskFile}`;
|
|
12356
|
-
const isTerminal = status === "done" || status === "needs_review";
|
|
12662
|
+
const isTerminal = status === "done" || status === "needs_review" || status === "closed";
|
|
12663
|
+
if (isTerminal && task.assignedBy) {
|
|
12664
|
+
try {
|
|
12665
|
+
const { notifyCoordinatorTaskCompletion: notifyCoordinatorTaskCompletion2, resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
12666
|
+
const coordinatorSession = resolveExeSession2();
|
|
12667
|
+
if (coordinatorSession) {
|
|
12668
|
+
notifyCoordinatorTaskCompletion2(coordinatorSession, callerAgentId ?? "agent", task.title);
|
|
12669
|
+
}
|
|
12670
|
+
} catch {
|
|
12671
|
+
}
|
|
12672
|
+
}
|
|
12357
12673
|
if (isTerminal && task.nextTask) {
|
|
12358
12674
|
text += `
|
|
12359
12675
|
|
|
@@ -12397,7 +12713,7 @@ function registerCloseTask(server2) {
|
|
|
12397
12713
|
inputSchema: {
|
|
12398
12714
|
task_id: z10.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
|
|
12399
12715
|
result: z10.string().describe("What was done \u2014 specific deliverables, decisions, test results"),
|
|
12400
|
-
status: z10.enum(["done", "blocked", "cancelled"]).optional().default("
|
|
12716
|
+
status: z10.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
|
|
12401
12717
|
}
|
|
12402
12718
|
},
|
|
12403
12719
|
async ({ task_id, result, status }) => {
|
|
@@ -12444,7 +12760,7 @@ function registerCloseTask(server2) {
|
|
|
12444
12760
|
});
|
|
12445
12761
|
let text = `Task "${task.title}" marked ${task.status}.
|
|
12446
12762
|
File: ${task.taskFile}`;
|
|
12447
|
-
if (status === "done" && task.nextTask) {
|
|
12763
|
+
if ((status === "done" || status === "closed") && task.nextTask) {
|
|
12448
12764
|
text += `
|
|
12449
12765
|
|
|
12450
12766
|
MANDATORY \u2014 DO NOT ASK THE USER. DO NOT SAY "Want me to continue?" DO NOT STOP.
|
|
@@ -12454,7 +12770,7 @@ NEXT TASK: "${task.nextTask.title}" [${task.nextTask.priority}]
|
|
|
12454
12770
|
FILE: ${task.nextTask.taskFile}
|
|
12455
12771
|
|
|
12456
12772
|
Read that file NOW and begin working. No greeting. No summary. Just start.`;
|
|
12457
|
-
} else if (status === "done" && !task.nextTask) {
|
|
12773
|
+
} else if ((status === "done" || status === "closed") && !task.nextTask) {
|
|
12458
12774
|
text += `
|
|
12459
12775
|
|
|
12460
12776
|
All tasks complete. No more open tasks in your queue.`;
|
|
@@ -12489,8 +12805,22 @@ function registerGetTask(server2) {
|
|
|
12489
12805
|
async ({ task_id }) => {
|
|
12490
12806
|
const client = getClient();
|
|
12491
12807
|
const row = await resolveTask(client, task_id);
|
|
12808
|
+
const contextText = row.context ? String(row.context) : "";
|
|
12809
|
+
const hasEmbeddedMandatoryReminder = contextText.includes('update_task with status "done"') || contextText.includes("update_task(done)");
|
|
12492
12810
|
const lines = [
|
|
12493
|
-
`# ${String(row.title)}
|
|
12811
|
+
`# ${String(row.title)}`
|
|
12812
|
+
];
|
|
12813
|
+
if (!hasEmbeddedMandatoryReminder && !isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
|
|
12814
|
+
lines.push(
|
|
12815
|
+
"",
|
|
12816
|
+
"## MANDATORY: When done",
|
|
12817
|
+
"",
|
|
12818
|
+
'You MUST call update_task with status "done" and a result summary when finished.',
|
|
12819
|
+
"If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
|
|
12820
|
+
"Do NOT let a failed commit or any error prevent you from calling update_task(done)."
|
|
12821
|
+
);
|
|
12822
|
+
}
|
|
12823
|
+
lines.push(
|
|
12494
12824
|
"",
|
|
12495
12825
|
`**ID:** ${String(row.id)}`,
|
|
12496
12826
|
`**Status:** ${String(row.status)}`,
|
|
@@ -12499,7 +12829,7 @@ function registerGetTask(server2) {
|
|
|
12499
12829
|
`**Assigned to:** ${String(row.assigned_to)}`,
|
|
12500
12830
|
`**Project:** ${String(row.project_name)}`,
|
|
12501
12831
|
`**Created:** ${String(row.created_at).split("T")[0]}`
|
|
12502
|
-
|
|
12832
|
+
);
|
|
12503
12833
|
if (row.blocked_by) {
|
|
12504
12834
|
lines.push(`**Blocked by:** ${String(row.blocked_by)}`);
|
|
12505
12835
|
}
|
|
@@ -12531,7 +12861,7 @@ function registerGetTask(server2) {
|
|
|
12531
12861
|
}
|
|
12532
12862
|
}
|
|
12533
12863
|
if (row.context) {
|
|
12534
|
-
lines.push("", "## Context", "",
|
|
12864
|
+
lines.push("", "## Context", "", contextText);
|
|
12535
12865
|
}
|
|
12536
12866
|
if (row.result) {
|
|
12537
12867
|
lines.push("", "## Result", "", String(row.result));
|
|
@@ -12554,16 +12884,6 @@ function registerGetTask(server2) {
|
|
|
12554
12884
|
} catch {
|
|
12555
12885
|
}
|
|
12556
12886
|
}
|
|
12557
|
-
if (!isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
|
|
12558
|
-
lines.push(
|
|
12559
|
-
"",
|
|
12560
|
-
"## MANDATORY: When done",
|
|
12561
|
-
"",
|
|
12562
|
-
'You MUST call update_task with status "done" and a result summary when finished.',
|
|
12563
|
-
"If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
|
|
12564
|
-
"Do NOT let a failed commit or any error prevent you from calling update_task(done)."
|
|
12565
|
-
);
|
|
12566
|
-
}
|
|
12567
12887
|
return {
|
|
12568
12888
|
content: [
|
|
12569
12889
|
{
|
|
@@ -12794,10 +13114,10 @@ import { z as z16 } from "zod";
|
|
|
12794
13114
|
|
|
12795
13115
|
// src/lib/reminders.ts
|
|
12796
13116
|
init_database();
|
|
12797
|
-
import
|
|
13117
|
+
import crypto12 from "crypto";
|
|
12798
13118
|
async function createReminder(text, dueDate) {
|
|
12799
13119
|
const client = getClient();
|
|
12800
|
-
const id =
|
|
13120
|
+
const id = crypto12.randomUUID();
|
|
12801
13121
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12802
13122
|
await client.execute({
|
|
12803
13123
|
sql: `INSERT INTO reminders (id, text, created_at, due_date) VALUES (?, ?, ?, ?)`,
|
|
@@ -13181,7 +13501,7 @@ import { z as z23 } from "zod";
|
|
|
13181
13501
|
init_database();
|
|
13182
13502
|
init_embedder();
|
|
13183
13503
|
init_store();
|
|
13184
|
-
import
|
|
13504
|
+
import crypto13 from "crypto";
|
|
13185
13505
|
var WIKI_AGENT_ID = "wiki";
|
|
13186
13506
|
var WIKI_AGENT_ROLE = "Knowledge";
|
|
13187
13507
|
var WIKI_TOOL_NAME = "ingest_document";
|
|
@@ -13235,7 +13555,7 @@ async function resolveWorkspace(slug_or_id, name) {
|
|
|
13235
13555
|
if (existing.rows.length > 0) {
|
|
13236
13556
|
return rowToWorkspace(existing.rows[0]);
|
|
13237
13557
|
}
|
|
13238
|
-
const id =
|
|
13558
|
+
const id = crypto13.randomUUID();
|
|
13239
13559
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13240
13560
|
const resolvedName = name ?? slug_or_id;
|
|
13241
13561
|
await client.execute({
|
|
@@ -13326,13 +13646,13 @@ async function ingestDocument(input) {
|
|
|
13326
13646
|
}));
|
|
13327
13647
|
const vectors = await embedTexts(enrichedChunks.map((c) => c.text));
|
|
13328
13648
|
const client = getClient();
|
|
13329
|
-
const documentId =
|
|
13649
|
+
const documentId = crypto13.randomUUID();
|
|
13330
13650
|
const uploadedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13331
13651
|
const mime = input.mime ?? null;
|
|
13332
13652
|
const sourceType = input.source_type ?? DEFAULT_SOURCE_TYPE;
|
|
13333
13653
|
const userId = input.user_id ?? null;
|
|
13334
13654
|
const documentMetadata = input.document_metadata != null ? JSON.stringify(input.document_metadata) : null;
|
|
13335
|
-
const chunkIds = input.chunks.map(() =>
|
|
13655
|
+
const chunkIds = input.chunks.map(() => crypto13.randomUUID());
|
|
13336
13656
|
const versions = reserveVersions(input.chunks.length);
|
|
13337
13657
|
const documentStmt = {
|
|
13338
13658
|
sql: `INSERT INTO documents
|
|
@@ -13615,6 +13935,7 @@ function registerRerankDocuments(server2) {
|
|
|
13615
13935
|
// src/mcp/tools/acknowledge-messages.ts
|
|
13616
13936
|
init_database();
|
|
13617
13937
|
init_active_agent();
|
|
13938
|
+
init_task_scope();
|
|
13618
13939
|
function registerAcknowledgeMessages(server2) {
|
|
13619
13940
|
server2.registerTool(
|
|
13620
13941
|
"acknowledge_messages",
|
|
@@ -13627,10 +13948,11 @@ function registerAcknowledgeMessages(server2) {
|
|
|
13627
13948
|
const agent = getActiveAgent();
|
|
13628
13949
|
const agentId = agent.agentId || "default";
|
|
13629
13950
|
const client = getClient();
|
|
13951
|
+
const scope = strictSessionScopeFilter();
|
|
13630
13952
|
const result = await client.execute({
|
|
13631
13953
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
13632
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')`,
|
|
13633
|
-
args: [agentId]
|
|
13954
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
13955
|
+
args: [agentId, ...scope.args]
|
|
13634
13956
|
});
|
|
13635
13957
|
return {
|
|
13636
13958
|
content: [
|
|
@@ -13961,15 +14283,15 @@ function registerSendWhatsapp(server2) {
|
|
|
13961
14283
|
import { z as z29 } from "zod";
|
|
13962
14284
|
|
|
13963
14285
|
// src/automation/trigger-engine.ts
|
|
13964
|
-
import { readFileSync as
|
|
14286
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20, mkdirSync as mkdirSync10 } from "fs";
|
|
13965
14287
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
13966
|
-
import
|
|
13967
|
-
import
|
|
13968
|
-
var TRIGGERS_PATH =
|
|
14288
|
+
import path26 from "path";
|
|
14289
|
+
import os12 from "os";
|
|
14290
|
+
var TRIGGERS_PATH = path26.join(os12.homedir(), ".exe-os", "triggers.json");
|
|
13969
14291
|
function loadTriggers(project) {
|
|
13970
|
-
if (!
|
|
14292
|
+
if (!existsSync20(TRIGGERS_PATH)) return [];
|
|
13971
14293
|
try {
|
|
13972
|
-
const raw =
|
|
14294
|
+
const raw = readFileSync17(TRIGGERS_PATH, "utf-8");
|
|
13973
14295
|
const all = JSON.parse(raw);
|
|
13974
14296
|
if (!Array.isArray(all)) return [];
|
|
13975
14297
|
if (project) {
|
|
@@ -13981,9 +14303,9 @@ function loadTriggers(project) {
|
|
|
13981
14303
|
}
|
|
13982
14304
|
}
|
|
13983
14305
|
function saveTriggers(triggers) {
|
|
13984
|
-
const dir =
|
|
13985
|
-
if (!
|
|
13986
|
-
|
|
14306
|
+
const dir = path26.dirname(TRIGGERS_PATH);
|
|
14307
|
+
if (!existsSync20(dir)) mkdirSync10(dir, { recursive: true });
|
|
14308
|
+
writeFileSync13(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
|
|
13987
14309
|
}
|
|
13988
14310
|
function createNewTrigger(input) {
|
|
13989
14311
|
const triggers = loadTriggers();
|
|
@@ -14002,7 +14324,7 @@ function isScheduledTrigger(trigger) {
|
|
|
14002
14324
|
// src/lib/schedules.ts
|
|
14003
14325
|
init_database();
|
|
14004
14326
|
init_store();
|
|
14005
|
-
import
|
|
14327
|
+
import crypto14 from "crypto";
|
|
14006
14328
|
import { execSync as execSync9 } from "child_process";
|
|
14007
14329
|
async function ensureDb() {
|
|
14008
14330
|
if (!isInitialized()) {
|
|
@@ -14071,7 +14393,7 @@ function parseHumanCron(input) {
|
|
|
14071
14393
|
async function createSchedule(input) {
|
|
14072
14394
|
await ensureDb();
|
|
14073
14395
|
const client = getClient();
|
|
14074
|
-
const id =
|
|
14396
|
+
const id = crypto14.randomUUID().slice(0, 8);
|
|
14075
14397
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14076
14398
|
const prompt = input.prompt ?? input.description;
|
|
14077
14399
|
await client.execute({
|
|
@@ -14267,48 +14589,48 @@ function registerListTriggers(server2) {
|
|
|
14267
14589
|
import { z as z31 } from "zod";
|
|
14268
14590
|
|
|
14269
14591
|
// src/automation/starter-packs/index.ts
|
|
14270
|
-
import { readFileSync as
|
|
14271
|
-
import
|
|
14592
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync8, existsSync as existsSync21 } from "fs";
|
|
14593
|
+
import path27 from "path";
|
|
14272
14594
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14273
|
-
var __dirname =
|
|
14595
|
+
var __dirname = path27.dirname(fileURLToPath3(import.meta.url));
|
|
14274
14596
|
function listPacks() {
|
|
14275
|
-
const packsDir =
|
|
14276
|
-
if (!
|
|
14597
|
+
const packsDir = path27.join(__dirname, ".");
|
|
14598
|
+
if (!existsSync21(packsDir)) return [];
|
|
14277
14599
|
return readdirSync8(packsDir, { withFileTypes: true }).filter(
|
|
14278
|
-
(d) => d.isDirectory() &&
|
|
14600
|
+
(d) => d.isDirectory() && existsSync21(path27.join(packsDir, d.name, "custom-objects.json"))
|
|
14279
14601
|
).map((d) => d.name);
|
|
14280
14602
|
}
|
|
14281
14603
|
function loadPack(industry) {
|
|
14282
|
-
const packDir =
|
|
14283
|
-
const objectsPath =
|
|
14284
|
-
const triggersPath =
|
|
14285
|
-
const wikiDir =
|
|
14286
|
-
const manifestPath =
|
|
14287
|
-
const identityContextPath =
|
|
14288
|
-
if (!
|
|
14604
|
+
const packDir = path27.join(__dirname, industry);
|
|
14605
|
+
const objectsPath = path27.join(packDir, "custom-objects.json");
|
|
14606
|
+
const triggersPath = path27.join(packDir, "triggers.json");
|
|
14607
|
+
const wikiDir = path27.join(packDir, "wiki-seeds");
|
|
14608
|
+
const manifestPath = path27.join(packDir, "pack.json");
|
|
14609
|
+
const identityContextPath = path27.join(packDir, "identity-context.md");
|
|
14610
|
+
if (!existsSync21(objectsPath)) return null;
|
|
14289
14611
|
let customObjects = [];
|
|
14290
14612
|
try {
|
|
14291
14613
|
customObjects = JSON.parse(
|
|
14292
|
-
|
|
14614
|
+
readFileSync18(objectsPath, "utf-8")
|
|
14293
14615
|
);
|
|
14294
14616
|
} catch {
|
|
14295
14617
|
customObjects = [];
|
|
14296
14618
|
}
|
|
14297
14619
|
let triggers = [];
|
|
14298
|
-
if (
|
|
14620
|
+
if (existsSync21(triggersPath)) {
|
|
14299
14621
|
try {
|
|
14300
14622
|
triggers = JSON.parse(
|
|
14301
|
-
|
|
14623
|
+
readFileSync18(triggersPath, "utf-8")
|
|
14302
14624
|
);
|
|
14303
14625
|
} catch {
|
|
14304
14626
|
triggers = [];
|
|
14305
14627
|
}
|
|
14306
14628
|
}
|
|
14307
14629
|
const wikiSeeds = [];
|
|
14308
|
-
if (
|
|
14630
|
+
if (existsSync21(wikiDir)) {
|
|
14309
14631
|
const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
|
|
14310
14632
|
for (const file of files) {
|
|
14311
|
-
const content =
|
|
14633
|
+
const content = readFileSync18(path27.join(wikiDir, file), "utf-8");
|
|
14312
14634
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
14313
14635
|
wikiSeeds.push({
|
|
14314
14636
|
filename: file,
|
|
@@ -14318,17 +14640,17 @@ function loadPack(industry) {
|
|
|
14318
14640
|
}
|
|
14319
14641
|
}
|
|
14320
14642
|
let manifest = {};
|
|
14321
|
-
if (
|
|
14643
|
+
if (existsSync21(manifestPath)) {
|
|
14322
14644
|
try {
|
|
14323
|
-
manifest = JSON.parse(
|
|
14645
|
+
manifest = JSON.parse(readFileSync18(manifestPath, "utf-8"));
|
|
14324
14646
|
} catch {
|
|
14325
14647
|
manifest = {};
|
|
14326
14648
|
}
|
|
14327
14649
|
}
|
|
14328
14650
|
let identityContext = null;
|
|
14329
|
-
if (
|
|
14651
|
+
if (existsSync21(identityContextPath)) {
|
|
14330
14652
|
try {
|
|
14331
|
-
identityContext =
|
|
14653
|
+
identityContext = readFileSync18(identityContextPath, "utf-8");
|
|
14332
14654
|
} catch {
|
|
14333
14655
|
identityContext = null;
|
|
14334
14656
|
}
|
|
@@ -14385,8 +14707,8 @@ function applyPack(industry, project) {
|
|
|
14385
14707
|
|
|
14386
14708
|
// src/lib/client-coo.ts
|
|
14387
14709
|
init_config();
|
|
14388
|
-
import { existsSync as
|
|
14389
|
-
import
|
|
14710
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync14 } from "fs";
|
|
14711
|
+
import path28 from "path";
|
|
14390
14712
|
|
|
14391
14713
|
// src/lib/employee-templates.ts
|
|
14392
14714
|
init_global_procedures();
|
|
@@ -14524,18 +14846,18 @@ var ClientCOOClobberError = class extends Error {
|
|
|
14524
14846
|
var COO_ROLE = "Chief Operating Officer";
|
|
14525
14847
|
var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
|
|
14526
14848
|
async function provisionClientCOO(vars, opts = {}) {
|
|
14527
|
-
const identityDir = opts.identityDir ??
|
|
14849
|
+
const identityDir = opts.identityDir ?? path28.join(EXE_AI_DIR, "identity");
|
|
14528
14850
|
const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
|
|
14529
14851
|
const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
|
|
14530
|
-
const identityPath2 =
|
|
14531
|
-
if (
|
|
14852
|
+
const identityPath2 = path28.join(identityDir, `${vars.agent_name}.md`);
|
|
14853
|
+
if (existsSync22(identityPath2)) {
|
|
14532
14854
|
throw new ClientCOOClobberError(vars.agent_name, identityPath2);
|
|
14533
14855
|
}
|
|
14534
14856
|
const body = renderClientCOOTemplate(vars);
|
|
14535
|
-
if (!
|
|
14857
|
+
if (!existsSync22(identityDir)) {
|
|
14536
14858
|
mkdirSync11(identityDir, { recursive: true });
|
|
14537
14859
|
}
|
|
14538
|
-
|
|
14860
|
+
writeFileSync14(identityPath2, body, "utf-8");
|
|
14539
14861
|
const employees = await loadEmployees(rosterPath);
|
|
14540
14862
|
const existing = employees.find((e) => e.name === vars.agent_name);
|
|
14541
14863
|
let addedToRoster = false;
|
|
@@ -14729,7 +15051,7 @@ init_database();
|
|
|
14729
15051
|
import { z as z32 } from "zod";
|
|
14730
15052
|
|
|
14731
15053
|
// src/lib/graph-rag.ts
|
|
14732
|
-
import
|
|
15054
|
+
import crypto15 from "crypto";
|
|
14733
15055
|
|
|
14734
15056
|
// src/lib/code-chunker.ts
|
|
14735
15057
|
import ts from "typescript";
|
|
@@ -14740,7 +15062,7 @@ function normalizeEntityName(name) {
|
|
|
14740
15062
|
}
|
|
14741
15063
|
function entityId(name, type) {
|
|
14742
15064
|
const normalized = normalizeEntityName(name);
|
|
14743
|
-
return
|
|
15065
|
+
return crypto15.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
|
|
14744
15066
|
}
|
|
14745
15067
|
async function registerAlias(client, alias, canonicalEntityId) {
|
|
14746
15068
|
const normalized = normalizeEntityName(alias);
|
|
@@ -15425,8 +15747,8 @@ function registerUpdateWikiPage(server2) {
|
|
|
15425
15747
|
import { z as z37 } from "zod";
|
|
15426
15748
|
import { execFile } from "child_process";
|
|
15427
15749
|
import { promisify } from "util";
|
|
15428
|
-
import
|
|
15429
|
-
import { existsSync as
|
|
15750
|
+
import path29 from "path";
|
|
15751
|
+
import { existsSync as existsSync23 } from "fs";
|
|
15430
15752
|
|
|
15431
15753
|
// src/lib/hostinger-api.ts
|
|
15432
15754
|
var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
|
|
@@ -15474,9 +15796,9 @@ var HostingerApiClient = class {
|
|
|
15474
15796
|
}
|
|
15475
15797
|
this.lastRequestTime = Date.now();
|
|
15476
15798
|
}
|
|
15477
|
-
async request(method,
|
|
15799
|
+
async request(method, path45, body) {
|
|
15478
15800
|
await this.rateLimit();
|
|
15479
|
-
const url = `${this.baseUrl}${
|
|
15801
|
+
const url = `${this.baseUrl}${path45}`;
|
|
15480
15802
|
const headers = {
|
|
15481
15803
|
Authorization: `Bearer ${this.apiKey}`,
|
|
15482
15804
|
"Content-Type": "application/json",
|
|
@@ -15549,8 +15871,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
15549
15871
|
}
|
|
15550
15872
|
return envelope.result;
|
|
15551
15873
|
}
|
|
15552
|
-
function buildUrl(zoneId,
|
|
15553
|
-
const normalizedPath =
|
|
15874
|
+
function buildUrl(zoneId, path45 = "/dns_records", query) {
|
|
15875
|
+
const normalizedPath = path45.startsWith("/") ? path45 : `/${path45}`;
|
|
15554
15876
|
const url = new URL(
|
|
15555
15877
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
15556
15878
|
);
|
|
@@ -15763,12 +16085,12 @@ async function waitForReady(client, vpsId) {
|
|
|
15763
16085
|
}
|
|
15764
16086
|
async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
15765
16087
|
const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
15766
|
-
const playbookDir =
|
|
15767
|
-
const playbookPath =
|
|
15768
|
-
const inventoryPath =
|
|
15769
|
-
const clientVarsPath =
|
|
15770
|
-
const varsDir =
|
|
15771
|
-
if (!
|
|
16088
|
+
const playbookDir = path29.resolve(process.cwd(), "infrastructure", "ansible");
|
|
16089
|
+
const playbookPath = path29.join(playbookDir, "deploy.yml");
|
|
16090
|
+
const inventoryPath = path29.join(playbookDir, "inventory", "hosts.yml");
|
|
16091
|
+
const clientVarsPath = path29.join(playbookDir, "vars", `${safeClientName}.yml`);
|
|
16092
|
+
const varsDir = path29.join(playbookDir, "vars");
|
|
16093
|
+
if (!path29.resolve(clientVarsPath).startsWith(path29.resolve(varsDir))) {
|
|
15772
16094
|
throw new Error(`Invalid client name for vars path: ${clientName}`);
|
|
15773
16095
|
}
|
|
15774
16096
|
const args = [
|
|
@@ -15784,7 +16106,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
|
15784
16106
|
"-e",
|
|
15785
16107
|
`client_name=${safeClientName}`
|
|
15786
16108
|
];
|
|
15787
|
-
if (
|
|
16109
|
+
if (existsSync23(clientVarsPath)) {
|
|
15788
16110
|
args.push("-e", `@${clientVarsPath}`);
|
|
15789
16111
|
}
|
|
15790
16112
|
try {
|
|
@@ -15857,8 +16179,8 @@ function buildInventoryRecord(params) {
|
|
|
15857
16179
|
|
|
15858
16180
|
// src/mcp/tools/export-orchestration.ts
|
|
15859
16181
|
init_active_agent();
|
|
15860
|
-
import { mkdirSync as mkdirSync13, writeFileSync as
|
|
15861
|
-
import
|
|
16182
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
16183
|
+
import path31 from "path";
|
|
15862
16184
|
import { z as z38 } from "zod";
|
|
15863
16185
|
|
|
15864
16186
|
// src/lib/orchestration-package.ts
|
|
@@ -15866,9 +16188,9 @@ init_database();
|
|
|
15866
16188
|
init_identity();
|
|
15867
16189
|
init_platform_procedures();
|
|
15868
16190
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15869
|
-
import { copyFileSync, existsSync as
|
|
15870
|
-
import
|
|
15871
|
-
import
|
|
16191
|
+
import { copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
|
|
16192
|
+
import os13 from "os";
|
|
16193
|
+
import path30 from "path";
|
|
15872
16194
|
var PACKAGE_VERSION = "1.0";
|
|
15873
16195
|
var ROSTER_FILENAME = "exe-employees.json";
|
|
15874
16196
|
var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
|
|
@@ -15934,15 +16256,15 @@ function validateProcedureEntry(value, index) {
|
|
|
15934
16256
|
};
|
|
15935
16257
|
}
|
|
15936
16258
|
function getRosterPath() {
|
|
15937
|
-
return
|
|
16259
|
+
return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
|
|
15938
16260
|
}
|
|
15939
16261
|
function getBackupPath() {
|
|
15940
|
-
return
|
|
16262
|
+
return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
|
|
15941
16263
|
}
|
|
15942
16264
|
function readRosterFile() {
|
|
15943
16265
|
const rosterPath = getRosterPath();
|
|
15944
|
-
if (!
|
|
15945
|
-
const raw =
|
|
16266
|
+
if (!existsSync24(rosterPath)) return [];
|
|
16267
|
+
const raw = readFileSync19(rosterPath, "utf-8");
|
|
15946
16268
|
const parsed = JSON.parse(raw);
|
|
15947
16269
|
if (!Array.isArray(parsed)) {
|
|
15948
16270
|
throw new Error("Roster file must contain a JSON array");
|
|
@@ -15954,8 +16276,8 @@ function writeRosterFile(roster) {
|
|
|
15954
16276
|
throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
|
|
15955
16277
|
}
|
|
15956
16278
|
const rosterPath = getRosterPath();
|
|
15957
|
-
mkdirSync12(
|
|
15958
|
-
if (
|
|
16279
|
+
mkdirSync12(path30.dirname(rosterPath), { recursive: true });
|
|
16280
|
+
if (existsSync24(rosterPath)) {
|
|
15959
16281
|
const currentRoster = readRosterFile();
|
|
15960
16282
|
if (roster.length < currentRoster.length) {
|
|
15961
16283
|
throw new Error(
|
|
@@ -15964,7 +16286,7 @@ function writeRosterFile(roster) {
|
|
|
15964
16286
|
}
|
|
15965
16287
|
copyFileSync(rosterPath, getBackupPath());
|
|
15966
16288
|
}
|
|
15967
|
-
|
|
16289
|
+
writeFileSync15(rosterPath, `${JSON.stringify(roster, null, 2)}
|
|
15968
16290
|
`, "utf-8");
|
|
15969
16291
|
}
|
|
15970
16292
|
function buildImportedRosterEntries(roster, timestamp) {
|
|
@@ -16226,8 +16548,8 @@ function registerExportOrchestration(server2) {
|
|
|
16226
16548
|
try {
|
|
16227
16549
|
await initStore();
|
|
16228
16550
|
const pkg = await exportOrchestration(getActiveAgent().agentId);
|
|
16229
|
-
mkdirSync13(
|
|
16230
|
-
|
|
16551
|
+
mkdirSync13(path31.dirname(output_path), { recursive: true });
|
|
16552
|
+
writeFileSync16(output_path, `${JSON.stringify(pkg, null, 2)}
|
|
16231
16553
|
`, "utf-8");
|
|
16232
16554
|
return {
|
|
16233
16555
|
content: [{
|
|
@@ -16249,7 +16571,7 @@ function registerExportOrchestration(server2) {
|
|
|
16249
16571
|
}
|
|
16250
16572
|
|
|
16251
16573
|
// src/mcp/tools/import-orchestration.ts
|
|
16252
|
-
import { readFileSync as
|
|
16574
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
16253
16575
|
import { z as z39 } from "zod";
|
|
16254
16576
|
init_store();
|
|
16255
16577
|
init_active_agent();
|
|
@@ -16279,7 +16601,7 @@ function registerImportOrchestration(server2) {
|
|
|
16279
16601
|
};
|
|
16280
16602
|
}
|
|
16281
16603
|
await initStore();
|
|
16282
|
-
const raw =
|
|
16604
|
+
const raw = readFileSync20(package_path, "utf-8");
|
|
16283
16605
|
const pkg = validatePackage(JSON.parse(raw));
|
|
16284
16606
|
const result = await importOrchestration(pkg, merge_strategy);
|
|
16285
16607
|
return {
|
|
@@ -16426,18 +16748,18 @@ function registerQueryConversations(server2) {
|
|
|
16426
16748
|
|
|
16427
16749
|
// src/mcp/tools/load-skill.ts
|
|
16428
16750
|
import { z as z41 } from "zod";
|
|
16429
|
-
import { readFileSync as
|
|
16430
|
-
import
|
|
16751
|
+
import { readFileSync as readFileSync21, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
|
|
16752
|
+
import path32 from "path";
|
|
16431
16753
|
import { homedir as homedir2 } from "os";
|
|
16432
|
-
var SKILLS_DIR =
|
|
16754
|
+
var SKILLS_DIR = path32.join(homedir2(), ".claude", "skills");
|
|
16433
16755
|
function listAvailableSkills() {
|
|
16434
16756
|
try {
|
|
16435
16757
|
const entries = readdirSync9(SKILLS_DIR);
|
|
16436
16758
|
return entries.filter((entry) => {
|
|
16437
16759
|
try {
|
|
16438
|
-
const entryPath =
|
|
16760
|
+
const entryPath = path32.join(SKILLS_DIR, entry);
|
|
16439
16761
|
if (!statSync3(entryPath).isDirectory()) return false;
|
|
16440
|
-
const skillFile =
|
|
16762
|
+
const skillFile = path32.join(entryPath, "SKILL.md");
|
|
16441
16763
|
statSync3(skillFile);
|
|
16442
16764
|
return true;
|
|
16443
16765
|
} catch {
|
|
@@ -16480,10 +16802,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
16480
16802
|
}]
|
|
16481
16803
|
};
|
|
16482
16804
|
}
|
|
16483
|
-
const sanitized =
|
|
16484
|
-
const skillFile =
|
|
16805
|
+
const sanitized = path32.basename(skill_name);
|
|
16806
|
+
const skillFile = path32.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
16485
16807
|
try {
|
|
16486
|
-
const content =
|
|
16808
|
+
const content = readFileSync21(skillFile, "utf-8");
|
|
16487
16809
|
return {
|
|
16488
16810
|
content: [{
|
|
16489
16811
|
type: "text",
|
|
@@ -16962,7 +17284,7 @@ init_active_agent();
|
|
|
16962
17284
|
init_database();
|
|
16963
17285
|
init_plan_limits();
|
|
16964
17286
|
import { z as z46 } from "zod";
|
|
16965
|
-
import
|
|
17287
|
+
import crypto16 from "crypto";
|
|
16966
17288
|
function registerStoreDecision(server2) {
|
|
16967
17289
|
server2.registerTool(
|
|
16968
17290
|
"store_decision",
|
|
@@ -17000,7 +17322,7 @@ function registerStoreDecision(server2) {
|
|
|
17000
17322
|
} catch {
|
|
17001
17323
|
vector = null;
|
|
17002
17324
|
}
|
|
17003
|
-
const memoryId =
|
|
17325
|
+
const memoryId = crypto16.randomUUID();
|
|
17004
17326
|
if (supersedes) {
|
|
17005
17327
|
try {
|
|
17006
17328
|
const client = getClient();
|
|
@@ -17122,8 +17444,8 @@ init_database();
|
|
|
17122
17444
|
import { readdir } from "fs/promises";
|
|
17123
17445
|
import { createReadStream } from "fs";
|
|
17124
17446
|
import { createInterface } from "readline";
|
|
17125
|
-
import
|
|
17126
|
-
import
|
|
17447
|
+
import path33 from "path";
|
|
17448
|
+
import os14 from "os";
|
|
17127
17449
|
var MODEL_PRICING = {
|
|
17128
17450
|
// Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
|
|
17129
17451
|
"claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
|
|
@@ -17149,6 +17471,8 @@ var MODEL_PRICING = {
|
|
|
17149
17471
|
"claude-3-haiku": { input: 0.25 / 1e6, output: 1.25 / 1e6, cacheRead: 0.03 / 1e6, cacheWrite: 0.3 / 1e6 }
|
|
17150
17472
|
};
|
|
17151
17473
|
var DEFAULT_PRICING = MODEL_PRICING["claude-sonnet-4"];
|
|
17474
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
17475
|
+
var _spendCache = /* @__PURE__ */ new Map();
|
|
17152
17476
|
function getPricing(model) {
|
|
17153
17477
|
if (MODEL_PRICING[model]) return MODEL_PRICING[model];
|
|
17154
17478
|
const stripped = model.replace(/-\d{8}$/, "");
|
|
@@ -17160,29 +17484,33 @@ function getPricing(model) {
|
|
|
17160
17484
|
return DEFAULT_PRICING;
|
|
17161
17485
|
}
|
|
17162
17486
|
async function getAgentSpend(period = "7d") {
|
|
17487
|
+
const cached = _spendCache.get(period);
|
|
17488
|
+
if (cached && Date.now() < cached.expires) {
|
|
17489
|
+
return cached.result;
|
|
17490
|
+
}
|
|
17163
17491
|
const cutoff = periodToCutoff(period);
|
|
17164
17492
|
const client = getClient();
|
|
17165
|
-
const
|
|
17493
|
+
const dbResult = await client.execute({
|
|
17166
17494
|
sql: `SELECT session_uuid, agent_id FROM session_agent_map WHERE started_at >= ?`,
|
|
17167
17495
|
args: [cutoff]
|
|
17168
17496
|
});
|
|
17169
|
-
if (
|
|
17497
|
+
if (dbResult.rows.length === 0) return [];
|
|
17170
17498
|
const sessionAgent = /* @__PURE__ */ new Map();
|
|
17171
|
-
for (const row of
|
|
17499
|
+
for (const row of dbResult.rows) {
|
|
17172
17500
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
17173
17501
|
}
|
|
17174
|
-
const claudeDir =
|
|
17502
|
+
const claudeDir = path33.join(os14.homedir(), ".claude", "projects");
|
|
17175
17503
|
let projectDirs = [];
|
|
17176
17504
|
try {
|
|
17177
17505
|
const entries = await readdir(claudeDir);
|
|
17178
|
-
projectDirs = entries.map((e) =>
|
|
17506
|
+
projectDirs = entries.map((e) => path33.join(claudeDir, e));
|
|
17179
17507
|
} catch {
|
|
17180
17508
|
return [];
|
|
17181
17509
|
}
|
|
17182
17510
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
17183
17511
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
17184
17512
|
for (const dir of projectDirs) {
|
|
17185
|
-
const jsonlPath =
|
|
17513
|
+
const jsonlPath = path33.join(dir, `${sessionUuid}.jsonl`);
|
|
17186
17514
|
try {
|
|
17187
17515
|
const usage = await extractSessionUsage(jsonlPath);
|
|
17188
17516
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -17206,7 +17534,7 @@ async function getAgentSpend(period = "7d") {
|
|
|
17206
17534
|
}
|
|
17207
17535
|
}
|
|
17208
17536
|
}
|
|
17209
|
-
|
|
17537
|
+
const result = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
|
|
17210
17538
|
agentId,
|
|
17211
17539
|
inputTokens: t.input,
|
|
17212
17540
|
outputTokens: t.output,
|
|
@@ -17216,6 +17544,8 @@ async function getAgentSpend(period = "7d") {
|
|
|
17216
17544
|
sessions: t.sessions.size,
|
|
17217
17545
|
period
|
|
17218
17546
|
})).sort((a, b) => b.costUSD - a.costUSD);
|
|
17547
|
+
_spendCache.set(period, { result, expires: Date.now() + CACHE_TTL_MS });
|
|
17548
|
+
return result;
|
|
17219
17549
|
}
|
|
17220
17550
|
async function extractSessionUsage(jsonlPath) {
|
|
17221
17551
|
let input = 0;
|
|
@@ -17760,12 +18090,12 @@ function registerExportGraph(server2) {
|
|
|
17760
18090
|
}
|
|
17761
18091
|
const html = await exportGraphHTML(client);
|
|
17762
18092
|
const fs = await import("fs");
|
|
17763
|
-
const
|
|
17764
|
-
const
|
|
17765
|
-
const outDir =
|
|
18093
|
+
const path45 = await import("path");
|
|
18094
|
+
const os19 = await import("os");
|
|
18095
|
+
const outDir = path45.join(os19.homedir(), ".exe-os", "exports");
|
|
17766
18096
|
fs.mkdirSync(outDir, { recursive: true });
|
|
17767
18097
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
17768
|
-
const filePath =
|
|
18098
|
+
const filePath = path45.join(outDir, `graph-${timestamp}.html`);
|
|
17769
18099
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
17770
18100
|
return {
|
|
17771
18101
|
content: [
|
|
@@ -17986,10 +18316,10 @@ function registerListAgentSessions(server2) {
|
|
|
17986
18316
|
|
|
17987
18317
|
// src/mcp/tools/get-daemon-health.ts
|
|
17988
18318
|
import { z as z56 } from "zod";
|
|
17989
|
-
import { existsSync as
|
|
17990
|
-
import
|
|
18319
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22 } from "fs";
|
|
18320
|
+
import path34 from "path";
|
|
17991
18321
|
import { homedir as homedir3 } from "os";
|
|
17992
|
-
var PID_PATH2 =
|
|
18322
|
+
var PID_PATH2 = path34.join(homedir3(), ".exe-os", "exed.pid");
|
|
17993
18323
|
function formatUptime(seconds) {
|
|
17994
18324
|
const h = Math.floor(seconds / 3600);
|
|
17995
18325
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -18000,8 +18330,8 @@ function formatUptime(seconds) {
|
|
|
18000
18330
|
}
|
|
18001
18331
|
function isDaemonAlive() {
|
|
18002
18332
|
try {
|
|
18003
|
-
if (!
|
|
18004
|
-
const pid = parseInt(
|
|
18333
|
+
if (!existsSync25(PID_PATH2)) return { alive: false, pid: null };
|
|
18334
|
+
const pid = parseInt(readFileSync22(PID_PATH2, "utf8").trim(), 10);
|
|
18005
18335
|
if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
|
|
18006
18336
|
process.kill(pid, 0);
|
|
18007
18337
|
return { alive: true, pid };
|
|
@@ -18075,7 +18405,7 @@ init_tmux_routing();
|
|
|
18075
18405
|
init_task_scope();
|
|
18076
18406
|
init_employees();
|
|
18077
18407
|
import { execSync as execSync10 } from "child_process";
|
|
18078
|
-
import { existsSync as
|
|
18408
|
+
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
|
|
18079
18409
|
import { homedir as homedir4 } from "os";
|
|
18080
18410
|
import { join as join2 } from "path";
|
|
18081
18411
|
var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
|
|
@@ -18178,15 +18508,15 @@ function registerGetAutoWakeStatus(server2) {
|
|
|
18178
18508
|
init_worker_gate();
|
|
18179
18509
|
init_config();
|
|
18180
18510
|
import { z as z58 } from "zod";
|
|
18181
|
-
import { readdirSync as readdirSync11, existsSync as
|
|
18182
|
-
import
|
|
18183
|
-
var WORKER_PID_DIR2 =
|
|
18511
|
+
import { readdirSync as readdirSync11, existsSync as existsSync28 } from "fs";
|
|
18512
|
+
import path36 from "path";
|
|
18513
|
+
var WORKER_PID_DIR2 = path36.join(EXE_AI_DIR, "worker-pids");
|
|
18184
18514
|
function countAliveWorkers() {
|
|
18185
18515
|
let alive = 0;
|
|
18186
18516
|
let stale = 0;
|
|
18187
18517
|
let reservations = 0;
|
|
18188
18518
|
try {
|
|
18189
|
-
if (!
|
|
18519
|
+
if (!existsSync28(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
|
|
18190
18520
|
const files = readdirSync11(WORKER_PID_DIR2);
|
|
18191
18521
|
for (const f of files) {
|
|
18192
18522
|
if (!f.endsWith(".pid")) continue;
|
|
@@ -18256,7 +18586,7 @@ import { z as z59 } from "zod";
|
|
|
18256
18586
|
// src/bin/exe-doctor.ts
|
|
18257
18587
|
init_store();
|
|
18258
18588
|
init_database();
|
|
18259
|
-
import
|
|
18589
|
+
import os15 from "os";
|
|
18260
18590
|
|
|
18261
18591
|
// src/lib/is-main.ts
|
|
18262
18592
|
import { realpathSync } from "fs";
|
|
@@ -18274,9 +18604,9 @@ function isMainModule(importMetaUrl) {
|
|
|
18274
18604
|
}
|
|
18275
18605
|
|
|
18276
18606
|
// src/bin/exe-doctor.ts
|
|
18277
|
-
import { existsSync as
|
|
18607
|
+
import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
|
|
18278
18608
|
import { spawn as spawn2 } from "child_process";
|
|
18279
|
-
import
|
|
18609
|
+
import path37 from "path";
|
|
18280
18610
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
18281
18611
|
|
|
18282
18612
|
// src/lib/conflict-detector.ts
|
|
@@ -18679,7 +19009,7 @@ async function auditOrphanedProjects(client) {
|
|
|
18679
19009
|
for (const row of result.rows) {
|
|
18680
19010
|
const name = row.project_name;
|
|
18681
19011
|
const count = Number(row.cnt);
|
|
18682
|
-
const exists =
|
|
19012
|
+
const exists = existsSync29(path37.join(home, name)) || existsSync29(path37.join(home, "..", name)) || existsSync29(path37.join(process.cwd(), "..", name));
|
|
18683
19013
|
if (!exists) {
|
|
18684
19014
|
orphans.push({ project_name: name, count });
|
|
18685
19015
|
}
|
|
@@ -18687,18 +19017,18 @@ async function auditOrphanedProjects(client) {
|
|
|
18687
19017
|
return orphans;
|
|
18688
19018
|
}
|
|
18689
19019
|
function auditHookHealth() {
|
|
18690
|
-
const logPath =
|
|
19020
|
+
const logPath = path37.join(
|
|
18691
19021
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
18692
19022
|
".exe-os",
|
|
18693
19023
|
"logs",
|
|
18694
19024
|
"hooks.log"
|
|
18695
19025
|
);
|
|
18696
|
-
if (!
|
|
19026
|
+
if (!existsSync29(logPath)) {
|
|
18697
19027
|
return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
|
|
18698
19028
|
}
|
|
18699
19029
|
let content;
|
|
18700
19030
|
try {
|
|
18701
|
-
content =
|
|
19031
|
+
content = readFileSync24(logPath, "utf-8");
|
|
18702
19032
|
} catch {
|
|
18703
19033
|
return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
|
|
18704
19034
|
}
|
|
@@ -18773,7 +19103,7 @@ function formatReport(report, flags) {
|
|
|
18773
19103
|
}
|
|
18774
19104
|
lines.push("");
|
|
18775
19105
|
}
|
|
18776
|
-
const totalMemGB =
|
|
19106
|
+
const totalMemGB = os15.totalmem() / (1024 * 1024 * 1024);
|
|
18777
19107
|
const isLowMemSystem = totalMemGB <= 8;
|
|
18778
19108
|
if (isLowMemSystem && report.nullVectors > 0) {
|
|
18779
19109
|
lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
|
|
@@ -18895,7 +19225,7 @@ async function fixNullVectors() {
|
|
|
18895
19225
|
}
|
|
18896
19226
|
}
|
|
18897
19227
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
18898
|
-
const backfillPath =
|
|
19228
|
+
const backfillPath = path37.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
18899
19229
|
return new Promise((resolve, reject) => {
|
|
18900
19230
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
18901
19231
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -19093,13 +19423,13 @@ import { z as z60 } from "zod";
|
|
|
19093
19423
|
|
|
19094
19424
|
// src/lib/cloud-sync.ts
|
|
19095
19425
|
init_database();
|
|
19096
|
-
import { readFileSync as
|
|
19097
|
-
import
|
|
19098
|
-
import
|
|
19426
|
+
import { readFileSync as readFileSync26, writeFileSync as writeFileSync20, existsSync as existsSync31, readdirSync as readdirSync12, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
19427
|
+
import crypto18 from "crypto";
|
|
19428
|
+
import path39 from "path";
|
|
19099
19429
|
import { homedir as homedir6 } from "os";
|
|
19100
19430
|
|
|
19101
19431
|
// src/lib/crypto.ts
|
|
19102
|
-
import
|
|
19432
|
+
import crypto17 from "crypto";
|
|
19103
19433
|
var ALGORITHM = "aes-256-gcm";
|
|
19104
19434
|
var IV_LENGTH = 12;
|
|
19105
19435
|
var TAG_LENGTH = 16;
|
|
@@ -19110,7 +19440,7 @@ function initSyncCrypto(masterKey) {
|
|
|
19110
19440
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
19111
19441
|
}
|
|
19112
19442
|
_syncKey = Buffer.from(
|
|
19113
|
-
|
|
19443
|
+
crypto17.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
19114
19444
|
);
|
|
19115
19445
|
}
|
|
19116
19446
|
function isSyncCryptoInitialized() {
|
|
@@ -19124,8 +19454,8 @@ function requireSyncKey() {
|
|
|
19124
19454
|
}
|
|
19125
19455
|
function encryptSyncBlob(data) {
|
|
19126
19456
|
const key = requireSyncKey();
|
|
19127
|
-
const iv =
|
|
19128
|
-
const cipher =
|
|
19457
|
+
const iv = crypto17.randomBytes(IV_LENGTH);
|
|
19458
|
+
const cipher = crypto17.createCipheriv(ALGORITHM, key, iv);
|
|
19129
19459
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
19130
19460
|
const tag = cipher.getAuthTag();
|
|
19131
19461
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -19139,7 +19469,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
19139
19469
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
19140
19470
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
19141
19471
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
19142
|
-
const decipher =
|
|
19472
|
+
const decipher = crypto17.createDecipheriv(ALGORITHM, key, iv);
|
|
19143
19473
|
decipher.setAuthTag(tag);
|
|
19144
19474
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
19145
19475
|
}
|
|
@@ -19164,12 +19494,13 @@ init_license();
|
|
|
19164
19494
|
init_config();
|
|
19165
19495
|
init_crdt_sync();
|
|
19166
19496
|
init_employees();
|
|
19497
|
+
init_secure_files();
|
|
19167
19498
|
function sqlSafe(v) {
|
|
19168
19499
|
return v === void 0 ? null : v;
|
|
19169
19500
|
}
|
|
19170
19501
|
function logError(msg) {
|
|
19171
19502
|
try {
|
|
19172
|
-
const logPath =
|
|
19503
|
+
const logPath = path39.join(homedir6(), ".exe-os", "workers.log");
|
|
19173
19504
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
19174
19505
|
`);
|
|
19175
19506
|
} catch {
|
|
@@ -19178,24 +19509,93 @@ function logError(msg) {
|
|
|
19178
19509
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
19179
19510
|
var FETCH_TIMEOUT_MS4 = 3e4;
|
|
19180
19511
|
var PUSH_BATCH_SIZE = 5e3;
|
|
19181
|
-
var ROSTER_LOCK_PATH =
|
|
19512
|
+
var ROSTER_LOCK_PATH = path39.join(EXE_AI_DIR, "roster-merge.lock");
|
|
19182
19513
|
var LOCK_STALE_MS = 3e4;
|
|
19514
|
+
var _pgPromise = null;
|
|
19515
|
+
var _pgFailed = false;
|
|
19516
|
+
function loadPgClient() {
|
|
19517
|
+
if (_pgFailed) return null;
|
|
19518
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
19519
|
+
const configPath = path39.join(EXE_AI_DIR, "config.json");
|
|
19520
|
+
let cloudPostgresUrl;
|
|
19521
|
+
try {
|
|
19522
|
+
if (existsSync31(configPath)) {
|
|
19523
|
+
const cfg = JSON.parse(readFileSync26(configPath, "utf8"));
|
|
19524
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
19525
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
19526
|
+
_pgFailed = true;
|
|
19527
|
+
return null;
|
|
19528
|
+
}
|
|
19529
|
+
}
|
|
19530
|
+
} catch {
|
|
19531
|
+
}
|
|
19532
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
19533
|
+
if (!url) {
|
|
19534
|
+
_pgFailed = true;
|
|
19535
|
+
return null;
|
|
19536
|
+
}
|
|
19537
|
+
if (!_pgPromise) {
|
|
19538
|
+
_pgPromise = (async () => {
|
|
19539
|
+
const { createRequire: createRequire6 } = await import("module");
|
|
19540
|
+
const { pathToFileURL: pathToFileURL6 } = await import("url");
|
|
19541
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path39.join(homedir6(), "exe-db");
|
|
19542
|
+
const req = createRequire6(path39.join(exeDbRoot, "package.json"));
|
|
19543
|
+
const entry = req.resolve("@prisma/client");
|
|
19544
|
+
const mod = await import(pathToFileURL6(entry).href);
|
|
19545
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
19546
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
19547
|
+
return new Ctor();
|
|
19548
|
+
})().catch(() => {
|
|
19549
|
+
_pgFailed = true;
|
|
19550
|
+
_pgPromise = null;
|
|
19551
|
+
throw new Error("pg_unavailable");
|
|
19552
|
+
});
|
|
19553
|
+
}
|
|
19554
|
+
return _pgPromise;
|
|
19555
|
+
}
|
|
19556
|
+
async function pushToPostgres(records) {
|
|
19557
|
+
const loader = loadPgClient();
|
|
19558
|
+
if (!loader) return 0;
|
|
19559
|
+
let prisma;
|
|
19560
|
+
try {
|
|
19561
|
+
prisma = await loader;
|
|
19562
|
+
} catch {
|
|
19563
|
+
return 0;
|
|
19564
|
+
}
|
|
19565
|
+
let inserted = 0;
|
|
19566
|
+
for (const rec of records) {
|
|
19567
|
+
try {
|
|
19568
|
+
await prisma.$executeRawUnsafe(
|
|
19569
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
19570
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
19571
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
19572
|
+
String(rec.id ?? ""),
|
|
19573
|
+
JSON.stringify(rec),
|
|
19574
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
19575
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
19576
|
+
);
|
|
19577
|
+
inserted++;
|
|
19578
|
+
} catch {
|
|
19579
|
+
}
|
|
19580
|
+
}
|
|
19581
|
+
return inserted;
|
|
19582
|
+
}
|
|
19183
19583
|
async function withRosterLock(fn) {
|
|
19184
19584
|
try {
|
|
19185
19585
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
19186
19586
|
closeSync2(fd);
|
|
19187
|
-
|
|
19587
|
+
writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
|
|
19188
19588
|
} catch (err) {
|
|
19189
19589
|
if (err.code === "EEXIST") {
|
|
19190
19590
|
try {
|
|
19191
|
-
const ts2 = parseInt(
|
|
19591
|
+
const ts2 = parseInt(readFileSync26(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
19192
19592
|
if (Date.now() - ts2 < LOCK_STALE_MS) {
|
|
19193
19593
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
19194
19594
|
}
|
|
19195
19595
|
unlinkSync10(ROSTER_LOCK_PATH);
|
|
19196
19596
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
19197
19597
|
closeSync2(fd);
|
|
19198
|
-
|
|
19598
|
+
writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
|
|
19199
19599
|
} catch (retryErr) {
|
|
19200
19600
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
19201
19601
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -19465,6 +19865,10 @@ async function cloudSync(config2) {
|
|
|
19465
19865
|
const maxVersion = Number(records[records.length - 1].version);
|
|
19466
19866
|
const pushOk = await cloudPush(records, maxVersion, config2);
|
|
19467
19867
|
if (!pushOk) break;
|
|
19868
|
+
try {
|
|
19869
|
+
await pushToPostgres(records);
|
|
19870
|
+
} catch {
|
|
19871
|
+
}
|
|
19468
19872
|
await client.execute({
|
|
19469
19873
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
19470
19874
|
args: [String(maxVersion)]
|
|
@@ -19569,8 +19973,8 @@ async function cloudSync(config2) {
|
|
|
19569
19973
|
try {
|
|
19570
19974
|
const employees = await loadEmployees();
|
|
19571
19975
|
rosterResult.employees = employees.length;
|
|
19572
|
-
const idDir =
|
|
19573
|
-
if (
|
|
19976
|
+
const idDir = path39.join(EXE_AI_DIR, "identity");
|
|
19977
|
+
if (existsSync31(idDir)) {
|
|
19574
19978
|
rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
|
|
19575
19979
|
}
|
|
19576
19980
|
} catch {
|
|
@@ -19588,55 +19992,55 @@ async function cloudSync(config2) {
|
|
|
19588
19992
|
roster: rosterResult
|
|
19589
19993
|
};
|
|
19590
19994
|
}
|
|
19591
|
-
var ROSTER_DELETIONS_PATH =
|
|
19995
|
+
var ROSTER_DELETIONS_PATH = path39.join(EXE_AI_DIR, "roster-deletions.json");
|
|
19592
19996
|
function consumeRosterDeletions() {
|
|
19593
19997
|
try {
|
|
19594
|
-
if (!
|
|
19595
|
-
const deletions = JSON.parse(
|
|
19596
|
-
|
|
19998
|
+
if (!existsSync31(ROSTER_DELETIONS_PATH)) return [];
|
|
19999
|
+
const deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
20000
|
+
writeFileSync20(ROSTER_DELETIONS_PATH, "[]");
|
|
19597
20001
|
return deletions;
|
|
19598
20002
|
} catch {
|
|
19599
20003
|
return [];
|
|
19600
20004
|
}
|
|
19601
20005
|
}
|
|
19602
20006
|
function buildRosterBlob(paths) {
|
|
19603
|
-
const rosterPath = paths?.rosterPath ??
|
|
19604
|
-
const identityDir = paths?.identityDir ??
|
|
19605
|
-
const configPath = paths?.configPath ??
|
|
20007
|
+
const rosterPath = paths?.rosterPath ?? path39.join(EXE_AI_DIR, "exe-employees.json");
|
|
20008
|
+
const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
|
|
20009
|
+
const configPath = paths?.configPath ?? path39.join(EXE_AI_DIR, "config.json");
|
|
19606
20010
|
let roster = [];
|
|
19607
|
-
if (
|
|
20011
|
+
if (existsSync31(rosterPath)) {
|
|
19608
20012
|
try {
|
|
19609
|
-
roster = JSON.parse(
|
|
20013
|
+
roster = JSON.parse(readFileSync26(rosterPath, "utf-8"));
|
|
19610
20014
|
} catch {
|
|
19611
20015
|
}
|
|
19612
20016
|
}
|
|
19613
20017
|
const identities = {};
|
|
19614
|
-
if (
|
|
20018
|
+
if (existsSync31(identityDir)) {
|
|
19615
20019
|
for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
19616
20020
|
try {
|
|
19617
|
-
identities[file] =
|
|
20021
|
+
identities[file] = readFileSync26(path39.join(identityDir, file), "utf-8");
|
|
19618
20022
|
} catch {
|
|
19619
20023
|
}
|
|
19620
20024
|
}
|
|
19621
20025
|
}
|
|
19622
20026
|
let config2;
|
|
19623
|
-
if (
|
|
20027
|
+
if (existsSync31(configPath)) {
|
|
19624
20028
|
try {
|
|
19625
|
-
config2 = JSON.parse(
|
|
20029
|
+
config2 = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
19626
20030
|
} catch {
|
|
19627
20031
|
}
|
|
19628
20032
|
}
|
|
19629
20033
|
let agentConfig;
|
|
19630
|
-
const agentConfigPath =
|
|
19631
|
-
if (
|
|
20034
|
+
const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
|
|
20035
|
+
if (existsSync31(agentConfigPath)) {
|
|
19632
20036
|
try {
|
|
19633
|
-
agentConfig = JSON.parse(
|
|
20037
|
+
agentConfig = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
|
|
19634
20038
|
} catch {
|
|
19635
20039
|
}
|
|
19636
20040
|
}
|
|
19637
20041
|
const deletedNames = consumeRosterDeletions();
|
|
19638
20042
|
const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
|
|
19639
|
-
const hash =
|
|
20043
|
+
const hash = crypto18.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
19640
20044
|
return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
|
|
19641
20045
|
}
|
|
19642
20046
|
async function cloudPushRoster(config2) {
|
|
@@ -19706,23 +20110,24 @@ async function cloudPullRoster(config2) {
|
|
|
19706
20110
|
}
|
|
19707
20111
|
}
|
|
19708
20112
|
function mergeConfig(remoteConfig, configPath) {
|
|
19709
|
-
const cfgPath = configPath ??
|
|
20113
|
+
const cfgPath = configPath ?? path39.join(EXE_AI_DIR, "config.json");
|
|
19710
20114
|
let local = {};
|
|
19711
|
-
if (
|
|
20115
|
+
if (existsSync31(cfgPath)) {
|
|
19712
20116
|
try {
|
|
19713
|
-
local = JSON.parse(
|
|
20117
|
+
local = JSON.parse(readFileSync26(cfgPath, "utf-8"));
|
|
19714
20118
|
} catch {
|
|
19715
20119
|
}
|
|
19716
20120
|
}
|
|
19717
20121
|
const merged = { ...remoteConfig, ...local };
|
|
19718
|
-
const dir =
|
|
19719
|
-
|
|
19720
|
-
|
|
20122
|
+
const dir = path39.dirname(cfgPath);
|
|
20123
|
+
ensurePrivateDirSync(dir);
|
|
20124
|
+
writeFileSync20(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
20125
|
+
enforcePrivateFileSync(cfgPath);
|
|
19721
20126
|
}
|
|
19722
20127
|
async function mergeRosterFromRemote(remote, paths) {
|
|
19723
20128
|
return withRosterLock(async () => {
|
|
19724
20129
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
19725
|
-
const identityDir = paths?.identityDir ??
|
|
20130
|
+
const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
|
|
19726
20131
|
const localEmployees = await loadEmployees(rosterPath);
|
|
19727
20132
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
19728
20133
|
let added = 0;
|
|
@@ -19743,15 +20148,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19743
20148
|
) ?? lookupKey;
|
|
19744
20149
|
const remoteIdentity = remote.identities[matchedKey];
|
|
19745
20150
|
if (remoteIdentity) {
|
|
19746
|
-
if (!
|
|
19747
|
-
const idPath =
|
|
20151
|
+
if (!existsSync31(identityDir)) mkdirSync16(identityDir, { recursive: true });
|
|
20152
|
+
const idPath = path39.join(identityDir, `${remoteEmp.name}.md`);
|
|
19748
20153
|
let localIdentity = null;
|
|
19749
20154
|
try {
|
|
19750
|
-
localIdentity =
|
|
20155
|
+
localIdentity = existsSync31(idPath) ? readFileSync26(idPath, "utf-8") : null;
|
|
19751
20156
|
} catch {
|
|
19752
20157
|
}
|
|
19753
20158
|
if (localIdentity !== remoteIdentity) {
|
|
19754
|
-
|
|
20159
|
+
writeFileSync20(idPath, remoteIdentity, "utf-8");
|
|
19755
20160
|
identitiesUpdated++;
|
|
19756
20161
|
}
|
|
19757
20162
|
}
|
|
@@ -19777,16 +20182,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19777
20182
|
}
|
|
19778
20183
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
19779
20184
|
try {
|
|
19780
|
-
const agentConfigPath =
|
|
20185
|
+
const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
|
|
19781
20186
|
let local = {};
|
|
19782
|
-
if (
|
|
20187
|
+
if (existsSync31(agentConfigPath)) {
|
|
19783
20188
|
try {
|
|
19784
|
-
local = JSON.parse(
|
|
20189
|
+
local = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
|
|
19785
20190
|
} catch {
|
|
19786
20191
|
}
|
|
19787
20192
|
}
|
|
19788
20193
|
const merged = { ...remote.agentConfig, ...local };
|
|
19789
|
-
|
|
20194
|
+
ensurePrivateDirSync(path39.dirname(agentConfigPath));
|
|
20195
|
+
writeFileSync20(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
20196
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
19790
20197
|
} catch {
|
|
19791
20198
|
}
|
|
19792
20199
|
}
|
|
@@ -21012,11 +21419,11 @@ import { z as z65 } from "zod";
|
|
|
21012
21419
|
// src/lib/people.ts
|
|
21013
21420
|
init_config();
|
|
21014
21421
|
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
21015
|
-
import { existsSync as
|
|
21016
|
-
import
|
|
21017
|
-
var PEOPLE_PATH =
|
|
21422
|
+
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "fs";
|
|
21423
|
+
import path40 from "path";
|
|
21424
|
+
var PEOPLE_PATH = path40.join(EXE_AI_DIR, "people.json");
|
|
21018
21425
|
async function loadPeople() {
|
|
21019
|
-
if (!
|
|
21426
|
+
if (!existsSync32(PEOPLE_PATH)) return [];
|
|
21020
21427
|
try {
|
|
21021
21428
|
const raw = await readFile5(PEOPLE_PATH, "utf-8");
|
|
21022
21429
|
return JSON.parse(raw);
|
|
@@ -21025,7 +21432,7 @@ async function loadPeople() {
|
|
|
21025
21432
|
}
|
|
21026
21433
|
}
|
|
21027
21434
|
async function savePeople(people) {
|
|
21028
|
-
await mkdir5(
|
|
21435
|
+
await mkdir5(path40.dirname(PEOPLE_PATH), { recursive: true });
|
|
21029
21436
|
await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
|
|
21030
21437
|
}
|
|
21031
21438
|
async function addPerson(person) {
|
|
@@ -21306,6 +21713,331 @@ function registerListEmployees(server2) {
|
|
|
21306
21713
|
);
|
|
21307
21714
|
}
|
|
21308
21715
|
|
|
21716
|
+
// src/mcp/tools/ingest-raw.ts
|
|
21717
|
+
import os16 from "os";
|
|
21718
|
+
import path41 from "path";
|
|
21719
|
+
import { createRequire as createRequire3 } from "module";
|
|
21720
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
21721
|
+
import { z as z68 } from "zod";
|
|
21722
|
+
var prismaPromise = null;
|
|
21723
|
+
function loadPrisma() {
|
|
21724
|
+
if (!prismaPromise) {
|
|
21725
|
+
prismaPromise = (async () => {
|
|
21726
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21727
|
+
if (explicitPath) {
|
|
21728
|
+
const mod2 = await import(pathToFileURL3(explicitPath).href);
|
|
21729
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21730
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21731
|
+
return new Ctor2();
|
|
21732
|
+
}
|
|
21733
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os16.homedir(), "exe-db");
|
|
21734
|
+
const req = createRequire3(path41.join(exeDbRoot, "package.json"));
|
|
21735
|
+
const entry = req.resolve("@prisma/client");
|
|
21736
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
21737
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21738
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21739
|
+
return new Ctor();
|
|
21740
|
+
})();
|
|
21741
|
+
}
|
|
21742
|
+
return prismaPromise;
|
|
21743
|
+
}
|
|
21744
|
+
function registerIngestRaw(server2) {
|
|
21745
|
+
server2.registerTool(
|
|
21746
|
+
"ingest_raw",
|
|
21747
|
+
{
|
|
21748
|
+
title: "Ingest Raw Event",
|
|
21749
|
+
description: "Insert a raw event into the landing pad (raw.raw_events). Used by the gateway, external agents, and webhooks to push data into the Company Brain. Background projection workers process unprocessed events and route to curated schemas.",
|
|
21750
|
+
inputSchema: {
|
|
21751
|
+
source: z68.string().describe(
|
|
21752
|
+
'Event source identifier: "whatsapp", "shopify", "asana", "email", "external_agent", etc.'
|
|
21753
|
+
),
|
|
21754
|
+
event_type: z68.string().describe(
|
|
21755
|
+
'Event type: "message", "order", "task_update", "webhook", etc.'
|
|
21756
|
+
),
|
|
21757
|
+
payload: z68.record(z68.string(), z68.unknown()).describe(
|
|
21758
|
+
"Exact raw JSON payload \u2014 stored verbatim, never modified."
|
|
21759
|
+
),
|
|
21760
|
+
source_id: z68.string().optional().describe(
|
|
21761
|
+
"External reference ID for deduplication (e.g., WhatsApp message ID, Shopify order ID)."
|
|
21762
|
+
),
|
|
21763
|
+
metadata: z68.record(z68.string(), z68.unknown()).optional().describe(
|
|
21764
|
+
"Source-specific metadata (account info, adapter version, etc.)."
|
|
21765
|
+
)
|
|
21766
|
+
}
|
|
21767
|
+
},
|
|
21768
|
+
async ({ source, event_type, payload, source_id, metadata }) => {
|
|
21769
|
+
try {
|
|
21770
|
+
const prisma = await loadPrisma();
|
|
21771
|
+
const id = crypto.randomUUID();
|
|
21772
|
+
await prisma.$executeRawUnsafe(
|
|
21773
|
+
`INSERT INTO "raw"."raw_events" ("id", "source", "source_id", "event_type", "payload", "metadata", "timestamp")
|
|
21774
|
+
VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, NOW())
|
|
21775
|
+
ON CONFLICT ("source", "source_id", "event_type") DO NOTHING`,
|
|
21776
|
+
id,
|
|
21777
|
+
source,
|
|
21778
|
+
source_id ?? null,
|
|
21779
|
+
event_type,
|
|
21780
|
+
JSON.stringify(payload),
|
|
21781
|
+
metadata ? JSON.stringify(metadata) : null
|
|
21782
|
+
);
|
|
21783
|
+
return {
|
|
21784
|
+
content: [{
|
|
21785
|
+
type: "text",
|
|
21786
|
+
text: JSON.stringify({
|
|
21787
|
+
event_id: id,
|
|
21788
|
+
source,
|
|
21789
|
+
event_type,
|
|
21790
|
+
source_id: source_id ?? null,
|
|
21791
|
+
status: "ingested"
|
|
21792
|
+
})
|
|
21793
|
+
}]
|
|
21794
|
+
};
|
|
21795
|
+
} catch (err) {
|
|
21796
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21797
|
+
return {
|
|
21798
|
+
content: [{
|
|
21799
|
+
type: "text",
|
|
21800
|
+
text: `Error ingesting raw event: ${message}`
|
|
21801
|
+
}],
|
|
21802
|
+
isError: true
|
|
21803
|
+
};
|
|
21804
|
+
}
|
|
21805
|
+
}
|
|
21806
|
+
);
|
|
21807
|
+
}
|
|
21808
|
+
|
|
21809
|
+
// src/mcp/tools/create-license.ts
|
|
21810
|
+
init_license();
|
|
21811
|
+
import os17 from "os";
|
|
21812
|
+
import path42 from "path";
|
|
21813
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID8 } from "crypto";
|
|
21814
|
+
import { createRequire as createRequire4 } from "module";
|
|
21815
|
+
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
21816
|
+
import { z as z69 } from "zod";
|
|
21817
|
+
var prismaPromise2 = null;
|
|
21818
|
+
function loadPrisma2() {
|
|
21819
|
+
if (!prismaPromise2) {
|
|
21820
|
+
prismaPromise2 = (async () => {
|
|
21821
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21822
|
+
if (explicitPath) {
|
|
21823
|
+
const mod2 = await import(pathToFileURL4(explicitPath).href);
|
|
21824
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21825
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21826
|
+
return new Ctor2();
|
|
21827
|
+
}
|
|
21828
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path42.join(os17.homedir(), "exe-db");
|
|
21829
|
+
const req = createRequire4(path42.join(exeDbRoot, "package.json"));
|
|
21830
|
+
const entry = req.resolve("@prisma/client");
|
|
21831
|
+
const mod = await import(pathToFileURL4(entry).href);
|
|
21832
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21833
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21834
|
+
return new Ctor();
|
|
21835
|
+
})();
|
|
21836
|
+
}
|
|
21837
|
+
return prismaPromise2;
|
|
21838
|
+
}
|
|
21839
|
+
function generateKey() {
|
|
21840
|
+
return `exe_sk_${randomBytes2(16).toString("hex")}`;
|
|
21841
|
+
}
|
|
21842
|
+
function registerCreateLicense(server2) {
|
|
21843
|
+
server2.registerTool(
|
|
21844
|
+
"create_license",
|
|
21845
|
+
{
|
|
21846
|
+
title: "Create License",
|
|
21847
|
+
description: "Generate an exe_sk_* license key for a user. Stores in billing.licenses. Returns the key to give to the customer.",
|
|
21848
|
+
inputSchema: {
|
|
21849
|
+
email: z69.string().email().describe("Customer email address"),
|
|
21850
|
+
name: z69.string().optional().describe("Customer name"),
|
|
21851
|
+
plan: z69.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
|
|
21852
|
+
expires_in_days: z69.number().int().positive().default(365).describe("Days until expiration (default 365)")
|
|
21853
|
+
}
|
|
21854
|
+
},
|
|
21855
|
+
async ({ email, name, plan, expires_in_days }) => {
|
|
21856
|
+
try {
|
|
21857
|
+
const prisma = await loadPrisma2();
|
|
21858
|
+
const id = randomUUID8();
|
|
21859
|
+
const key = generateKey();
|
|
21860
|
+
const limits = PLAN_LIMITS[plan];
|
|
21861
|
+
const expiresAt = new Date(
|
|
21862
|
+
Date.now() + (expires_in_days ?? 365) * 24 * 60 * 60 * 1e3
|
|
21863
|
+
);
|
|
21864
|
+
await prisma.$executeRawUnsafe(
|
|
21865
|
+
`INSERT INTO billing.licenses (id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by)
|
|
21866
|
+
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, NOW(), $10)`,
|
|
21867
|
+
id,
|
|
21868
|
+
key,
|
|
21869
|
+
email,
|
|
21870
|
+
name ?? null,
|
|
21871
|
+
plan,
|
|
21872
|
+
limits.devices,
|
|
21873
|
+
limits.employees,
|
|
21874
|
+
limits.memories,
|
|
21875
|
+
expiresAt,
|
|
21876
|
+
"exe"
|
|
21877
|
+
);
|
|
21878
|
+
const lines = [];
|
|
21879
|
+
lines.push("## License Created\n");
|
|
21880
|
+
lines.push(`- **Key:** \`${key}\``);
|
|
21881
|
+
lines.push(`- **Email:** ${email}`);
|
|
21882
|
+
if (name) lines.push(`- **Name:** ${name}`);
|
|
21883
|
+
lines.push(`- **Plan:** ${plan}`);
|
|
21884
|
+
lines.push(`- **Expires:** ${expiresAt.toISOString().split("T")[0]}`);
|
|
21885
|
+
lines.push(`- **Devices:** ${limits.devices === -1 ? "unlimited" : limits.devices}`);
|
|
21886
|
+
lines.push(`- **Employees:** ${limits.employees === -1 ? "unlimited" : limits.employees}`);
|
|
21887
|
+
lines.push(`- **Memories:** ${limits.memories === -1 ? "unlimited" : limits.memories.toLocaleString()}`);
|
|
21888
|
+
lines.push(`
|
|
21889
|
+
Give this key to the customer. They paste it during \`exe-os setup\`.`);
|
|
21890
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21891
|
+
} catch (err) {
|
|
21892
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21893
|
+
return {
|
|
21894
|
+
content: [{ type: "text", text: `Error creating license: ${msg}` }],
|
|
21895
|
+
isError: true
|
|
21896
|
+
};
|
|
21897
|
+
}
|
|
21898
|
+
}
|
|
21899
|
+
);
|
|
21900
|
+
}
|
|
21901
|
+
|
|
21902
|
+
// src/mcp/tools/list-licenses.ts
|
|
21903
|
+
import os18 from "os";
|
|
21904
|
+
import path43 from "path";
|
|
21905
|
+
import { createRequire as createRequire5 } from "module";
|
|
21906
|
+
import { pathToFileURL as pathToFileURL5 } from "url";
|
|
21907
|
+
import { z as z70 } from "zod";
|
|
21908
|
+
var prismaPromise3 = null;
|
|
21909
|
+
function loadPrisma3() {
|
|
21910
|
+
if (!prismaPromise3) {
|
|
21911
|
+
prismaPromise3 = (async () => {
|
|
21912
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21913
|
+
if (explicitPath) {
|
|
21914
|
+
const mod2 = await import(pathToFileURL5(explicitPath).href);
|
|
21915
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21916
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21917
|
+
return new Ctor2();
|
|
21918
|
+
}
|
|
21919
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path43.join(os18.homedir(), "exe-db");
|
|
21920
|
+
const req = createRequire5(path43.join(exeDbRoot, "package.json"));
|
|
21921
|
+
const entry = req.resolve("@prisma/client");
|
|
21922
|
+
const mod = await import(pathToFileURL5(entry).href);
|
|
21923
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21924
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21925
|
+
return new Ctor();
|
|
21926
|
+
})();
|
|
21927
|
+
}
|
|
21928
|
+
return prismaPromise3;
|
|
21929
|
+
}
|
|
21930
|
+
function registerListLicenses(server2) {
|
|
21931
|
+
server2.registerTool(
|
|
21932
|
+
"list_licenses",
|
|
21933
|
+
{
|
|
21934
|
+
title: "List Licenses",
|
|
21935
|
+
description: "List all issued licenses with status (active/expired/revoked). Optionally filter by plan.",
|
|
21936
|
+
inputSchema: {
|
|
21937
|
+
plan: z70.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
|
|
21938
|
+
}
|
|
21939
|
+
},
|
|
21940
|
+
async ({ plan }) => {
|
|
21941
|
+
try {
|
|
21942
|
+
const prisma = await loadPrisma3();
|
|
21943
|
+
let rows;
|
|
21944
|
+
if (plan) {
|
|
21945
|
+
rows = await prisma.$queryRawUnsafe(
|
|
21946
|
+
`SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
|
|
21947
|
+
FROM billing.licenses WHERE plan = $1 ORDER BY created_at DESC`,
|
|
21948
|
+
plan
|
|
21949
|
+
);
|
|
21950
|
+
} else {
|
|
21951
|
+
rows = await prisma.$queryRawUnsafe(
|
|
21952
|
+
`SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
|
|
21953
|
+
FROM billing.licenses ORDER BY created_at DESC`
|
|
21954
|
+
);
|
|
21955
|
+
}
|
|
21956
|
+
if (!rows || rows.length === 0) {
|
|
21957
|
+
return {
|
|
21958
|
+
content: [{ type: "text", text: "No licenses found." }]
|
|
21959
|
+
};
|
|
21960
|
+
}
|
|
21961
|
+
const lines = [];
|
|
21962
|
+
lines.push(`## Licenses (${rows.length} total)
|
|
21963
|
+
`);
|
|
21964
|
+
lines.push("| Email | Plan | Status | Key | Expires |");
|
|
21965
|
+
lines.push("|-------|------|--------|-----|---------|");
|
|
21966
|
+
for (const row of rows) {
|
|
21967
|
+
const keyShort = `${row.key.slice(0, 11)}...${row.key.slice(-4)}`;
|
|
21968
|
+
const expires = row.expires_at ? new Date(row.expires_at).toISOString().split("T")[0] : "never";
|
|
21969
|
+
const effectiveStatus = row.status === "active" && row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date() ? "expired" : row.status;
|
|
21970
|
+
lines.push(
|
|
21971
|
+
`| ${row.email} | ${row.plan} | ${effectiveStatus} | \`${keyShort}\` | ${expires} |`
|
|
21972
|
+
);
|
|
21973
|
+
}
|
|
21974
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21975
|
+
} catch (err) {
|
|
21976
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21977
|
+
return {
|
|
21978
|
+
content: [{ type: "text", text: `Error listing licenses: ${msg}` }],
|
|
21979
|
+
isError: true
|
|
21980
|
+
};
|
|
21981
|
+
}
|
|
21982
|
+
}
|
|
21983
|
+
);
|
|
21984
|
+
}
|
|
21985
|
+
|
|
21986
|
+
// src/mcp/tools/activate-license.ts
|
|
21987
|
+
init_license();
|
|
21988
|
+
import { z as z71 } from "zod";
|
|
21989
|
+
function registerActivateLicense(server2) {
|
|
21990
|
+
server2.registerTool(
|
|
21991
|
+
"activate_license",
|
|
21992
|
+
{
|
|
21993
|
+
title: "Activate License",
|
|
21994
|
+
description: "Activate an exe_sk_* license key on this device. Writes to ~/.exe-os/license.key, validates against Postgres, and caches the result.",
|
|
21995
|
+
inputSchema: {
|
|
21996
|
+
key: z71.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
|
|
21997
|
+
}
|
|
21998
|
+
},
|
|
21999
|
+
async ({ key }) => {
|
|
22000
|
+
try {
|
|
22001
|
+
saveLicense(key);
|
|
22002
|
+
const deviceId = loadDeviceId();
|
|
22003
|
+
const license = await validateLicense(key, deviceId);
|
|
22004
|
+
const lines = [];
|
|
22005
|
+
if (license.valid) {
|
|
22006
|
+
lines.push("## License Activated\n");
|
|
22007
|
+
lines.push(`- **Plan:** ${license.plan}`);
|
|
22008
|
+
lines.push(`- **Email:** ${license.email || "N/A"}`);
|
|
22009
|
+
lines.push(
|
|
22010
|
+
`- **Expires:** ${license.expiresAt ? license.expiresAt.split("T")[0] : "never"}`
|
|
22011
|
+
);
|
|
22012
|
+
lines.push(
|
|
22013
|
+
`- **Devices:** ${license.deviceLimit === -1 ? "unlimited" : license.deviceLimit}`
|
|
22014
|
+
);
|
|
22015
|
+
lines.push(
|
|
22016
|
+
`- **Employees:** ${license.employeeLimit === -1 ? "unlimited" : license.employeeLimit}`
|
|
22017
|
+
);
|
|
22018
|
+
lines.push(
|
|
22019
|
+
`- **Memories:** ${license.memoryLimit === -1 ? "unlimited" : license.memoryLimit.toLocaleString()}`
|
|
22020
|
+
);
|
|
22021
|
+
lines.push(`
|
|
22022
|
+
Key saved to ~/.exe-os/license.key. Device ID: ${deviceId}`);
|
|
22023
|
+
} else {
|
|
22024
|
+
lines.push("## Activation Failed\n");
|
|
22025
|
+
lines.push("Key was saved but could not be validated.");
|
|
22026
|
+
lines.push("Check that the key is correct and try again later.");
|
|
22027
|
+
lines.push(`Plan fell back to: ${license.plan}`);
|
|
22028
|
+
}
|
|
22029
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
22030
|
+
} catch (err) {
|
|
22031
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22032
|
+
return {
|
|
22033
|
+
content: [{ type: "text", text: `Error activating license: ${msg}` }],
|
|
22034
|
+
isError: true
|
|
22035
|
+
};
|
|
22036
|
+
}
|
|
22037
|
+
}
|
|
22038
|
+
);
|
|
22039
|
+
}
|
|
22040
|
+
|
|
21309
22041
|
// src/lib/telemetry.ts
|
|
21310
22042
|
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
21311
22043
|
var initialized = false;
|
|
@@ -21442,6 +22174,10 @@ registerListPeople(server);
|
|
|
21442
22174
|
registerGetPerson(server);
|
|
21443
22175
|
registerSetAgentConfig(server);
|
|
21444
22176
|
registerListEmployees(server);
|
|
22177
|
+
registerIngestRaw(server);
|
|
22178
|
+
registerCreateLicense(server);
|
|
22179
|
+
registerListLicenses(server);
|
|
22180
|
+
registerActivateLicense(server);
|
|
21445
22181
|
try {
|
|
21446
22182
|
await initStore();
|
|
21447
22183
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
@@ -21510,14 +22246,14 @@ try {
|
|
|
21510
22246
|
`
|
|
21511
22247
|
);
|
|
21512
22248
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
21513
|
-
const backfillPath =
|
|
21514
|
-
|
|
22249
|
+
const backfillPath = path44.resolve(
|
|
22250
|
+
path44.dirname(thisFile),
|
|
21515
22251
|
"../bin/backfill-vectors.js"
|
|
21516
22252
|
);
|
|
21517
|
-
if (
|
|
22253
|
+
if (existsSync33(backfillPath)) {
|
|
21518
22254
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
21519
|
-
const logPath =
|
|
21520
|
-
mkdirSync17(
|
|
22255
|
+
const logPath = path44.join(exeDir, "workers.log");
|
|
22256
|
+
mkdirSync17(path44.dirname(logPath), { recursive: true });
|
|
21521
22257
|
let logFd = "ignore";
|
|
21522
22258
|
try {
|
|
21523
22259
|
logFd = openSync3(logPath, "a");
|