@askexenow/exe-os 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
package/dist/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,56 +6399,130 @@ 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
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
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;
|
|
6485
|
+
}
|
|
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
|
+
};
|
|
6496
|
+
} catch {
|
|
6497
|
+
return null;
|
|
6498
|
+
}
|
|
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
|
+
}
|
|
6251
6520
|
}
|
|
6252
|
-
const cached = await getCachedLicense();
|
|
6253
|
-
if (cached) return cached;
|
|
6254
|
-
const raw = getRawCachedPlan();
|
|
6255
|
-
if (raw) return raw;
|
|
6256
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
6257
6521
|
} catch {
|
|
6258
|
-
const cached = await getCachedLicense();
|
|
6259
|
-
if (cached) return cached;
|
|
6260
|
-
const rawFallback = getRawCachedPlan();
|
|
6261
|
-
if (rawFallback) return rawFallback;
|
|
6262
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
6263
6522
|
}
|
|
6523
|
+
const rawFallback = getRawCachedPlan();
|
|
6524
|
+
if (rawFallback) return rawFallback;
|
|
6525
|
+
return { ...FREE_LICENSE, valid: false };
|
|
6264
6526
|
}
|
|
6265
6527
|
function getCacheAgeMs() {
|
|
6266
6528
|
try {
|
|
@@ -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,63 @@ 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
|
+
|
|
8139
8433
|
// src/lib/tasks-crud.ts
|
|
8140
8434
|
var tasks_crud_exports = {};
|
|
8141
8435
|
__export(tasks_crud_exports, {
|
|
@@ -8153,12 +8447,12 @@ __export(tasks_crud_exports, {
|
|
|
8153
8447
|
updateTaskStatus: () => updateTaskStatus,
|
|
8154
8448
|
writeCheckpoint: () => writeCheckpoint
|
|
8155
8449
|
});
|
|
8156
|
-
import
|
|
8157
|
-
import
|
|
8158
|
-
import
|
|
8450
|
+
import crypto8 from "crypto";
|
|
8451
|
+
import path21 from "path";
|
|
8452
|
+
import os11 from "os";
|
|
8159
8453
|
import { execSync as execSync8 } from "child_process";
|
|
8160
8454
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
8161
|
-
import { existsSync as
|
|
8455
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
8162
8456
|
async function writeCheckpoint(input) {
|
|
8163
8457
|
const client = getClient();
|
|
8164
8458
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -8274,7 +8568,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
8274
8568
|
}
|
|
8275
8569
|
async function createTaskCore(input) {
|
|
8276
8570
|
const client = getClient();
|
|
8277
|
-
const id =
|
|
8571
|
+
const id = crypto8.randomUUID();
|
|
8278
8572
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8279
8573
|
const slug = slugify(input.title);
|
|
8280
8574
|
let earlySessionScope = null;
|
|
@@ -8333,8 +8627,8 @@ ${laneWarning}` : laneWarning;
|
|
|
8333
8627
|
}
|
|
8334
8628
|
if (input.baseDir) {
|
|
8335
8629
|
try {
|
|
8336
|
-
await mkdir4(
|
|
8337
|
-
await mkdir4(
|
|
8630
|
+
await mkdir4(path21.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
8631
|
+
await mkdir4(path21.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
8338
8632
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
8339
8633
|
await ensureGitignoreExe(input.baseDir);
|
|
8340
8634
|
} catch {
|
|
@@ -8370,13 +8664,19 @@ ${laneWarning}` : laneWarning;
|
|
|
8370
8664
|
});
|
|
8371
8665
|
if (input.baseDir) {
|
|
8372
8666
|
try {
|
|
8373
|
-
const EXE_OS_DIR =
|
|
8374
|
-
const mdPath =
|
|
8375
|
-
const mdDir =
|
|
8376
|
-
if (!
|
|
8667
|
+
const EXE_OS_DIR = path21.join(os11.homedir(), ".exe-os");
|
|
8668
|
+
const mdPath = path21.join(EXE_OS_DIR, taskFile);
|
|
8669
|
+
const mdDir = path21.dirname(mdPath);
|
|
8670
|
+
if (!existsSync17(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
8377
8671
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
8378
8672
|
const mdContent = `# ${input.title}
|
|
8379
8673
|
|
|
8674
|
+
## MANDATORY: When done
|
|
8675
|
+
|
|
8676
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
8677
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
8678
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
8679
|
+
|
|
8380
8680
|
**ID:** ${id}
|
|
8381
8681
|
**Status:** ${initialStatus}
|
|
8382
8682
|
**Priority:** ${input.priority}
|
|
@@ -8390,12 +8690,6 @@ ${laneWarning}` : laneWarning;
|
|
|
8390
8690
|
## Context
|
|
8391
8691
|
|
|
8392
8692
|
${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
8693
|
`;
|
|
8400
8694
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
8401
8695
|
} catch (err) {
|
|
@@ -8644,7 +8938,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
8644
8938
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
8645
8939
|
} catch {
|
|
8646
8940
|
}
|
|
8647
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
8941
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
8648
8942
|
try {
|
|
8649
8943
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
8650
8944
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -8673,9 +8967,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
8673
8967
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
8674
8968
|
}
|
|
8675
8969
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
8676
|
-
const archPath =
|
|
8970
|
+
const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
8677
8971
|
try {
|
|
8678
|
-
if (
|
|
8972
|
+
if (existsSync17(archPath)) return;
|
|
8679
8973
|
const template = [
|
|
8680
8974
|
`# ${projectName} \u2014 System Architecture`,
|
|
8681
8975
|
"",
|
|
@@ -8708,10 +9002,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
8708
9002
|
}
|
|
8709
9003
|
}
|
|
8710
9004
|
async function ensureGitignoreExe(baseDir) {
|
|
8711
|
-
const gitignorePath =
|
|
9005
|
+
const gitignorePath = path21.join(baseDir, ".gitignore");
|
|
8712
9006
|
try {
|
|
8713
|
-
if (
|
|
8714
|
-
const content =
|
|
9007
|
+
if (existsSync17(gitignorePath)) {
|
|
9008
|
+
const content = readFileSync14(gitignorePath, "utf-8");
|
|
8715
9009
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
8716
9010
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
8717
9011
|
} else {
|
|
@@ -8742,58 +9036,42 @@ var init_tasks_crud = __esm({
|
|
|
8742
9036
|
});
|
|
8743
9037
|
|
|
8744
9038
|
// src/lib/tasks-review.ts
|
|
8745
|
-
import
|
|
8746
|
-
import { existsSync as
|
|
9039
|
+
import path22 from "path";
|
|
9040
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
8747
9041
|
async function countPendingReviews(sessionScope) {
|
|
8748
9042
|
const client = getClient();
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
args: [sessionScope]
|
|
8753
|
-
});
|
|
8754
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
8755
|
-
}
|
|
9043
|
+
const scope = strictSessionScopeFilter(
|
|
9044
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9045
|
+
);
|
|
8756
9046
|
const result = await client.execute({
|
|
8757
|
-
sql:
|
|
8758
|
-
|
|
9047
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9048
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
9049
|
+
args: [...scope.args]
|
|
8759
9050
|
});
|
|
8760
9051
|
return Number(result.rows[0]?.cnt) || 0;
|
|
8761
9052
|
}
|
|
8762
9053
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
8763
9054
|
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
|
-
}
|
|
9055
|
+
const scope = strictSessionScopeFilter(
|
|
9056
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9057
|
+
);
|
|
8773
9058
|
const result = await client.execute({
|
|
8774
9059
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
8775
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
8776
|
-
args: [sinceIso]
|
|
9060
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
9061
|
+
args: [sinceIso, ...scope.args]
|
|
8777
9062
|
});
|
|
8778
9063
|
return Number(result.rows[0]?.cnt) || 0;
|
|
8779
9064
|
}
|
|
8780
9065
|
async function listPendingReviews(limit, sessionScope) {
|
|
8781
9066
|
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
|
-
}
|
|
9067
|
+
const scope = strictSessionScopeFilter(
|
|
9068
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9069
|
+
);
|
|
8792
9070
|
const result = await client.execute({
|
|
8793
9071
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
8794
|
-
WHERE status = 'needs_review'
|
|
9072
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
8795
9073
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
8796
|
-
args: [limit]
|
|
9074
|
+
args: [...scope.args, limit]
|
|
8797
9075
|
});
|
|
8798
9076
|
return result.rows;
|
|
8799
9077
|
}
|
|
@@ -8805,7 +9083,7 @@ async function cleanupOrphanedReviews() {
|
|
|
8805
9083
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
8806
9084
|
AND assigned_by = 'system'
|
|
8807
9085
|
AND title LIKE 'Review:%'
|
|
8808
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
9086
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
8809
9087
|
args: [now]
|
|
8810
9088
|
});
|
|
8811
9089
|
const r1b = await client.execute({
|
|
@@ -8924,11 +9202,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
8924
9202
|
);
|
|
8925
9203
|
}
|
|
8926
9204
|
try {
|
|
8927
|
-
const cacheDir =
|
|
8928
|
-
if (
|
|
9205
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
9206
|
+
if (existsSync18(cacheDir)) {
|
|
8929
9207
|
for (const f of readdirSync6(cacheDir)) {
|
|
8930
9208
|
if (f.startsWith("review-notified-")) {
|
|
8931
|
-
unlinkSync6(
|
|
9209
|
+
unlinkSync6(path22.join(cacheDir, f));
|
|
8932
9210
|
}
|
|
8933
9211
|
}
|
|
8934
9212
|
}
|
|
@@ -8945,11 +9223,12 @@ var init_tasks_review = __esm({
|
|
|
8945
9223
|
init_tmux_routing();
|
|
8946
9224
|
init_session_key();
|
|
8947
9225
|
init_state_bus();
|
|
9226
|
+
init_task_scope();
|
|
8948
9227
|
}
|
|
8949
9228
|
});
|
|
8950
9229
|
|
|
8951
9230
|
// src/lib/tasks-chain.ts
|
|
8952
|
-
import
|
|
9231
|
+
import path23 from "path";
|
|
8953
9232
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
8954
9233
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
8955
9234
|
const client = getClient();
|
|
@@ -8966,7 +9245,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
8966
9245
|
});
|
|
8967
9246
|
for (const ur of unblockedRows.rows) {
|
|
8968
9247
|
try {
|
|
8969
|
-
const ubFile =
|
|
9248
|
+
const ubFile = path23.join(baseDir, String(ur.task_file));
|
|
8970
9249
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
8971
9250
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
8972
9251
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -9001,7 +9280,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
9001
9280
|
const scScope = sessionScopeFilter();
|
|
9002
9281
|
const remaining = await client.execute({
|
|
9003
9282
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9004
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
9283
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
9005
9284
|
args: [parentTaskId, ...scScope.args]
|
|
9006
9285
|
});
|
|
9007
9286
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -9162,10 +9441,10 @@ var init_tasks_notify = __esm({
|
|
|
9162
9441
|
});
|
|
9163
9442
|
|
|
9164
9443
|
// src/lib/behaviors.ts
|
|
9165
|
-
import
|
|
9444
|
+
import crypto9 from "crypto";
|
|
9166
9445
|
async function storeBehavior(opts) {
|
|
9167
9446
|
const client = getClient();
|
|
9168
|
-
const id =
|
|
9447
|
+
const id = crypto9.randomUUID();
|
|
9169
9448
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9170
9449
|
await client.execute({
|
|
9171
9450
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -9222,7 +9501,7 @@ __export(skill_learning_exports, {
|
|
|
9222
9501
|
storeTrajectory: () => storeTrajectory,
|
|
9223
9502
|
sweepTrajectories: () => sweepTrajectories
|
|
9224
9503
|
});
|
|
9225
|
-
import
|
|
9504
|
+
import crypto10 from "crypto";
|
|
9226
9505
|
async function extractTrajectory(taskId, agentId) {
|
|
9227
9506
|
const client = getClient();
|
|
9228
9507
|
const result = await client.execute({
|
|
@@ -9251,11 +9530,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
9251
9530
|
return signature;
|
|
9252
9531
|
}
|
|
9253
9532
|
function hashSignature(signature) {
|
|
9254
|
-
return
|
|
9533
|
+
return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
9255
9534
|
}
|
|
9256
9535
|
async function storeTrajectory(opts) {
|
|
9257
9536
|
const client = getClient();
|
|
9258
|
-
const id =
|
|
9537
|
+
const id = crypto10.randomUUID();
|
|
9259
9538
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9260
9539
|
const signatureHash = hashSignature(opts.signature);
|
|
9261
9540
|
await client.execute({
|
|
@@ -9520,8 +9799,8 @@ __export(tasks_exports, {
|
|
|
9520
9799
|
updateTaskStatus: () => updateTaskStatus,
|
|
9521
9800
|
writeCheckpoint: () => writeCheckpoint
|
|
9522
9801
|
});
|
|
9523
|
-
import
|
|
9524
|
-
import { writeFileSync as
|
|
9802
|
+
import path24 from "path";
|
|
9803
|
+
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
9525
9804
|
async function createTask(input) {
|
|
9526
9805
|
const result = await createTaskCore(input);
|
|
9527
9806
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -9540,12 +9819,12 @@ async function updateTask(input) {
|
|
|
9540
9819
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
9541
9820
|
try {
|
|
9542
9821
|
const agent = String(row.assigned_to);
|
|
9543
|
-
const cacheDir =
|
|
9544
|
-
const cachePath =
|
|
9822
|
+
const cacheDir = path24.join(EXE_AI_DIR, "session-cache");
|
|
9823
|
+
const cachePath = path24.join(cacheDir, `current-task-${agent}.json`);
|
|
9545
9824
|
if (input.status === "in_progress") {
|
|
9546
9825
|
mkdirSync8(cacheDir, { recursive: true });
|
|
9547
|
-
|
|
9548
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
9826
|
+
writeFileSync11(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
9827
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
9549
9828
|
try {
|
|
9550
9829
|
unlinkSync7(cachePath);
|
|
9551
9830
|
} catch {
|
|
@@ -9553,10 +9832,10 @@ async function updateTask(input) {
|
|
|
9553
9832
|
}
|
|
9554
9833
|
} catch {
|
|
9555
9834
|
}
|
|
9556
|
-
if (input.status === "done") {
|
|
9835
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9557
9836
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
9558
9837
|
}
|
|
9559
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
9838
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
9560
9839
|
try {
|
|
9561
9840
|
const client = getClient();
|
|
9562
9841
|
const taskTitle = String(row.title);
|
|
@@ -9572,7 +9851,7 @@ async function updateTask(input) {
|
|
|
9572
9851
|
if (!isCoordinatorName(assignedAgent)) {
|
|
9573
9852
|
try {
|
|
9574
9853
|
const draftClient = getClient();
|
|
9575
|
-
if (input.status === "done") {
|
|
9854
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9576
9855
|
await draftClient.execute({
|
|
9577
9856
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
9578
9857
|
args: [assignedAgent]
|
|
@@ -9589,7 +9868,7 @@ async function updateTask(input) {
|
|
|
9589
9868
|
try {
|
|
9590
9869
|
const client = getClient();
|
|
9591
9870
|
const cascaded = await client.execute({
|
|
9592
|
-
sql: `UPDATE tasks SET status = '
|
|
9871
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
9593
9872
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
9594
9873
|
args: [now, taskId]
|
|
9595
9874
|
});
|
|
@@ -9602,14 +9881,14 @@ async function updateTask(input) {
|
|
|
9602
9881
|
} catch {
|
|
9603
9882
|
}
|
|
9604
9883
|
}
|
|
9605
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
9884
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
9606
9885
|
if (isTerminal) {
|
|
9607
9886
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
9608
9887
|
if (!isCoordinator) {
|
|
9609
9888
|
notifyTaskDone();
|
|
9610
9889
|
}
|
|
9611
9890
|
await markTaskNotificationsRead(taskFile);
|
|
9612
|
-
if (input.status === "done") {
|
|
9891
|
+
if (input.status === "done" || input.status === "closed") {
|
|
9613
9892
|
try {
|
|
9614
9893
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
9615
9894
|
} catch {
|
|
@@ -9629,7 +9908,7 @@ async function updateTask(input) {
|
|
|
9629
9908
|
}
|
|
9630
9909
|
}
|
|
9631
9910
|
}
|
|
9632
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
9911
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
9633
9912
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
9634
9913
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
9635
9914
|
taskId,
|
|
@@ -9710,17 +9989,17 @@ __export(identity_exports, {
|
|
|
9710
9989
|
listIdentities: () => listIdentities,
|
|
9711
9990
|
updateIdentity: () => updateIdentity
|
|
9712
9991
|
});
|
|
9713
|
-
import { existsSync as
|
|
9992
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
9714
9993
|
import { readdirSync as readdirSync7 } from "fs";
|
|
9715
|
-
import
|
|
9994
|
+
import path25 from "path";
|
|
9716
9995
|
import { createHash as createHash2 } from "crypto";
|
|
9717
9996
|
function ensureDir2() {
|
|
9718
|
-
if (!
|
|
9997
|
+
if (!existsSync19(IDENTITY_DIR2)) {
|
|
9719
9998
|
mkdirSync9(IDENTITY_DIR2, { recursive: true });
|
|
9720
9999
|
}
|
|
9721
10000
|
}
|
|
9722
10001
|
function identityPath(agentId) {
|
|
9723
|
-
return
|
|
10002
|
+
return path25.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
9724
10003
|
}
|
|
9725
10004
|
function parseFrontmatter(raw) {
|
|
9726
10005
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -9761,8 +10040,8 @@ function contentHash(content) {
|
|
|
9761
10040
|
}
|
|
9762
10041
|
function getIdentity(agentId) {
|
|
9763
10042
|
const filePath = identityPath(agentId);
|
|
9764
|
-
if (!
|
|
9765
|
-
const raw =
|
|
10043
|
+
if (!existsSync19(filePath)) return null;
|
|
10044
|
+
const raw = readFileSync15(filePath, "utf-8");
|
|
9766
10045
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
9767
10046
|
return {
|
|
9768
10047
|
agentId,
|
|
@@ -9776,7 +10055,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
9776
10055
|
ensureDir2();
|
|
9777
10056
|
const filePath = identityPath(agentId);
|
|
9778
10057
|
const hash = contentHash(content);
|
|
9779
|
-
|
|
10058
|
+
writeFileSync12(filePath, content, "utf-8");
|
|
9780
10059
|
try {
|
|
9781
10060
|
const client = getClient();
|
|
9782
10061
|
await client.execute({
|
|
@@ -9832,7 +10111,7 @@ var init_identity = __esm({
|
|
|
9832
10111
|
"use strict";
|
|
9833
10112
|
init_config();
|
|
9834
10113
|
init_database();
|
|
9835
|
-
IDENTITY_DIR2 =
|
|
10114
|
+
IDENTITY_DIR2 = path25.join(EXE_AI_DIR, "identity");
|
|
9836
10115
|
}
|
|
9837
10116
|
});
|
|
9838
10117
|
|
|
@@ -10378,10 +10657,10 @@ ${PLAN_MODE_COMPAT}
|
|
|
10378
10657
|
});
|
|
10379
10658
|
|
|
10380
10659
|
// src/lib/messaging.ts
|
|
10381
|
-
import
|
|
10660
|
+
import crypto11 from "crypto";
|
|
10382
10661
|
function generateUlid() {
|
|
10383
10662
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
10384
|
-
const random =
|
|
10663
|
+
const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
|
|
10385
10664
|
return (timestamp + random).toUpperCase();
|
|
10386
10665
|
}
|
|
10387
10666
|
function rowToMessage(row) {
|
|
@@ -10392,6 +10671,7 @@ function rowToMessage(row) {
|
|
|
10392
10671
|
targetAgent: row.target_agent,
|
|
10393
10672
|
targetProject: row.target_project ?? null,
|
|
10394
10673
|
targetDevice: row.target_device,
|
|
10674
|
+
sessionScope: row.session_scope ?? null,
|
|
10395
10675
|
content: row.content,
|
|
10396
10676
|
priority: row.priority ?? "normal",
|
|
10397
10677
|
status: row.status ?? "pending",
|
|
@@ -10409,15 +10689,17 @@ async function sendMessage(input) {
|
|
|
10409
10689
|
const id = generateUlid();
|
|
10410
10690
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10411
10691
|
const targetDevice = input.targetDevice ?? "local";
|
|
10692
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
10412
10693
|
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', ?)`,
|
|
10694
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
10695
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
10415
10696
|
args: [
|
|
10416
10697
|
id,
|
|
10417
10698
|
input.fromAgent,
|
|
10418
10699
|
input.targetAgent,
|
|
10419
10700
|
input.targetProject ?? null,
|
|
10420
10701
|
targetDevice,
|
|
10702
|
+
sessionScope,
|
|
10421
10703
|
input.content,
|
|
10422
10704
|
input.priority ?? "normal",
|
|
10423
10705
|
now
|
|
@@ -10431,9 +10713,10 @@ async function sendMessage(input) {
|
|
|
10431
10713
|
}
|
|
10432
10714
|
} catch {
|
|
10433
10715
|
}
|
|
10716
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
10434
10717
|
const result = await client.execute({
|
|
10435
|
-
sql:
|
|
10436
|
-
args: [id]
|
|
10718
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
10719
|
+
args: [id, ...sentScope.args]
|
|
10437
10720
|
});
|
|
10438
10721
|
return rowToMessage(result.rows[0]);
|
|
10439
10722
|
}
|
|
@@ -10454,6 +10737,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
10454
10737
|
fromAgent: msg.fromAgent,
|
|
10455
10738
|
targetAgent: msg.targetAgent,
|
|
10456
10739
|
targetProject: msg.targetProject,
|
|
10740
|
+
sessionScope: msg.sessionScope,
|
|
10457
10741
|
content: msg.content,
|
|
10458
10742
|
priority: msg.priority,
|
|
10459
10743
|
createdAt: msg.createdAt
|
|
@@ -10497,7 +10781,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
10497
10781
|
} catch {
|
|
10498
10782
|
const newRetryCount = msg.retryCount + 1;
|
|
10499
10783
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
10500
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
10784
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
10501
10785
|
} else {
|
|
10502
10786
|
await client.execute({
|
|
10503
10787
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -10507,11 +10791,13 @@ async function deliverLocalMessage(messageId) {
|
|
|
10507
10791
|
return false;
|
|
10508
10792
|
}
|
|
10509
10793
|
}
|
|
10510
|
-
async function markFailed(messageId, reason) {
|
|
10794
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
10511
10795
|
const client = getClient();
|
|
10796
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
10512
10797
|
await client.execute({
|
|
10513
|
-
sql:
|
|
10514
|
-
|
|
10798
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
10799
|
+
WHERE id = ?${scope.sql}`,
|
|
10800
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
10515
10801
|
});
|
|
10516
10802
|
}
|
|
10517
10803
|
var MAX_RETRIES3, _wsClientSend;
|
|
@@ -10520,19 +10806,20 @@ var init_messaging = __esm({
|
|
|
10520
10806
|
"use strict";
|
|
10521
10807
|
init_database();
|
|
10522
10808
|
init_tmux_routing();
|
|
10809
|
+
init_task_scope();
|
|
10523
10810
|
MAX_RETRIES3 = 10;
|
|
10524
10811
|
_wsClientSend = null;
|
|
10525
10812
|
}
|
|
10526
10813
|
});
|
|
10527
10814
|
|
|
10528
10815
|
// src/gateway/whatsapp-accounts.ts
|
|
10529
|
-
import { readFileSync as
|
|
10816
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
10530
10817
|
import { join } from "path";
|
|
10531
10818
|
import { homedir } from "os";
|
|
10532
10819
|
function loadAccounts() {
|
|
10533
10820
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
10534
10821
|
try {
|
|
10535
|
-
const raw =
|
|
10822
|
+
const raw = readFileSync16(CONFIG_PATH2, "utf8");
|
|
10536
10823
|
const parsed = JSON.parse(raw);
|
|
10537
10824
|
if (!Array.isArray(parsed)) {
|
|
10538
10825
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -10945,8 +11232,8 @@ __export(wiki_client_exports, {
|
|
|
10945
11232
|
listDocuments: () => listDocuments,
|
|
10946
11233
|
listWorkspaces: () => listWorkspaces
|
|
10947
11234
|
});
|
|
10948
|
-
async function wikiFetch(config2,
|
|
10949
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
11235
|
+
async function wikiFetch(config2, path45, method = "GET", body) {
|
|
11236
|
+
const url = `${config2.baseUrl}/api/v1${path45}`;
|
|
10950
11237
|
const headers = {
|
|
10951
11238
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
10952
11239
|
"Content-Type": "application/json"
|
|
@@ -10979,7 +11266,7 @@ async function wikiFetch(config2, path41, method = "GET", body) {
|
|
|
10979
11266
|
}
|
|
10980
11267
|
}
|
|
10981
11268
|
if (!response.ok) {
|
|
10982
|
-
throw new Error(`Wiki API ${method} ${
|
|
11269
|
+
throw new Error(`Wiki API ${method} ${path45}: ${response.status} ${response.statusText}`);
|
|
10983
11270
|
}
|
|
10984
11271
|
return response.json();
|
|
10985
11272
|
} finally {
|
|
@@ -11088,14 +11375,14 @@ __export(worker_gate_exports, {
|
|
|
11088
11375
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
11089
11376
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
11090
11377
|
});
|
|
11091
|
-
import { readdirSync as readdirSync10, writeFileSync as
|
|
11092
|
-
import
|
|
11378
|
+
import { readdirSync as readdirSync10, writeFileSync as writeFileSync18, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync27 } from "fs";
|
|
11379
|
+
import path35 from "path";
|
|
11093
11380
|
function tryAcquireWorkerSlot() {
|
|
11094
11381
|
try {
|
|
11095
11382
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11096
11383
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
11097
|
-
const reservationPath =
|
|
11098
|
-
|
|
11384
|
+
const reservationPath = path35.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
11385
|
+
writeFileSync18(reservationPath, String(process.pid));
|
|
11099
11386
|
const files = readdirSync10(WORKER_PID_DIR);
|
|
11100
11387
|
let alive = 0;
|
|
11101
11388
|
for (const f of files) {
|
|
@@ -11112,7 +11399,7 @@ function tryAcquireWorkerSlot() {
|
|
|
11112
11399
|
alive++;
|
|
11113
11400
|
} catch {
|
|
11114
11401
|
try {
|
|
11115
|
-
unlinkSync8(
|
|
11402
|
+
unlinkSync8(path35.join(WORKER_PID_DIR, f));
|
|
11116
11403
|
} catch {
|
|
11117
11404
|
}
|
|
11118
11405
|
}
|
|
@@ -11136,20 +11423,20 @@ function tryAcquireWorkerSlot() {
|
|
|
11136
11423
|
function registerWorkerPid(pid) {
|
|
11137
11424
|
try {
|
|
11138
11425
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11139
|
-
|
|
11426
|
+
writeFileSync18(path35.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
11140
11427
|
} catch {
|
|
11141
11428
|
}
|
|
11142
11429
|
}
|
|
11143
11430
|
function cleanupWorkerPid() {
|
|
11144
11431
|
try {
|
|
11145
|
-
unlinkSync8(
|
|
11432
|
+
unlinkSync8(path35.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
11146
11433
|
} catch {
|
|
11147
11434
|
}
|
|
11148
11435
|
}
|
|
11149
11436
|
function tryAcquireBackfillLock() {
|
|
11150
11437
|
try {
|
|
11151
11438
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
11152
|
-
if (
|
|
11439
|
+
if (existsSync27(BACKFILL_LOCK)) {
|
|
11153
11440
|
try {
|
|
11154
11441
|
const pid = parseInt(
|
|
11155
11442
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -11165,7 +11452,7 @@ function tryAcquireBackfillLock() {
|
|
|
11165
11452
|
} catch {
|
|
11166
11453
|
}
|
|
11167
11454
|
}
|
|
11168
|
-
|
|
11455
|
+
writeFileSync18(BACKFILL_LOCK, String(process.pid));
|
|
11169
11456
|
return true;
|
|
11170
11457
|
} catch {
|
|
11171
11458
|
return true;
|
|
@@ -11182,9 +11469,9 @@ var init_worker_gate = __esm({
|
|
|
11182
11469
|
"src/lib/worker-gate.ts"() {
|
|
11183
11470
|
"use strict";
|
|
11184
11471
|
init_config();
|
|
11185
|
-
WORKER_PID_DIR =
|
|
11472
|
+
WORKER_PID_DIR = path35.join(EXE_AI_DIR, "worker-pids");
|
|
11186
11473
|
MAX_CONCURRENT_WORKERS = 3;
|
|
11187
|
-
BACKFILL_LOCK =
|
|
11474
|
+
BACKFILL_LOCK = path35.join(WORKER_PID_DIR, "backfill.lock");
|
|
11188
11475
|
}
|
|
11189
11476
|
});
|
|
11190
11477
|
|
|
@@ -11207,8 +11494,8 @@ __export(crdt_sync_exports, {
|
|
|
11207
11494
|
rebuildFromDb: () => rebuildFromDb
|
|
11208
11495
|
});
|
|
11209
11496
|
import * as Y from "yjs";
|
|
11210
|
-
import { readFileSync as
|
|
11211
|
-
import
|
|
11497
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync30, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
|
|
11498
|
+
import path38 from "path";
|
|
11212
11499
|
import { homedir as homedir5 } from "os";
|
|
11213
11500
|
function getStatePath() {
|
|
11214
11501
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -11220,9 +11507,9 @@ function initCrdtDoc() {
|
|
|
11220
11507
|
if (doc) return doc;
|
|
11221
11508
|
doc = new Y.Doc();
|
|
11222
11509
|
const sp = getStatePath();
|
|
11223
|
-
if (
|
|
11510
|
+
if (existsSync30(sp)) {
|
|
11224
11511
|
try {
|
|
11225
|
-
const state =
|
|
11512
|
+
const state = readFileSync25(sp);
|
|
11226
11513
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
11227
11514
|
} catch {
|
|
11228
11515
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -11364,10 +11651,10 @@ function persistState() {
|
|
|
11364
11651
|
if (!doc) return;
|
|
11365
11652
|
try {
|
|
11366
11653
|
const sp = getStatePath();
|
|
11367
|
-
const dir =
|
|
11368
|
-
if (!
|
|
11654
|
+
const dir = path38.dirname(sp);
|
|
11655
|
+
if (!existsSync30(dir)) mkdirSync15(dir, { recursive: true });
|
|
11369
11656
|
const state = Y.encodeStateAsUpdate(doc);
|
|
11370
|
-
|
|
11657
|
+
writeFileSync19(sp, Buffer.from(state));
|
|
11371
11658
|
} catch {
|
|
11372
11659
|
}
|
|
11373
11660
|
}
|
|
@@ -11408,7 +11695,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
11408
11695
|
var init_crdt_sync = __esm({
|
|
11409
11696
|
"src/lib/crdt-sync.ts"() {
|
|
11410
11697
|
"use strict";
|
|
11411
|
-
DEFAULT_STATE_PATH =
|
|
11698
|
+
DEFAULT_STATE_PATH = path38.join(homedir5(), ".exe-os", "crdt-state.bin");
|
|
11412
11699
|
_statePathOverride = null;
|
|
11413
11700
|
doc = null;
|
|
11414
11701
|
}
|
|
@@ -11421,8 +11708,8 @@ init_database();
|
|
|
11421
11708
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11422
11709
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11423
11710
|
import { spawn as spawn4 } from "child_process";
|
|
11424
|
-
import { existsSync as
|
|
11425
|
-
import
|
|
11711
|
+
import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
|
|
11712
|
+
import path44 from "path";
|
|
11426
11713
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11427
11714
|
|
|
11428
11715
|
// src/mcp/tools/recall-my-memory.ts
|
|
@@ -11729,9 +12016,9 @@ init_store();
|
|
|
11729
12016
|
init_active_agent();
|
|
11730
12017
|
init_plan_limits();
|
|
11731
12018
|
import { z as z4 } from "zod";
|
|
11732
|
-
import
|
|
11733
|
-
import { writeFileSync as
|
|
11734
|
-
import
|
|
12019
|
+
import crypto4 from "crypto";
|
|
12020
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
12021
|
+
import path15 from "path";
|
|
11735
12022
|
function registerStoreMemory(server2) {
|
|
11736
12023
|
server2.registerTool(
|
|
11737
12024
|
"store_memory",
|
|
@@ -11780,7 +12067,7 @@ function registerStoreMemory(server2) {
|
|
|
11780
12067
|
vector = null;
|
|
11781
12068
|
needsBackfill = true;
|
|
11782
12069
|
}
|
|
11783
|
-
const memoryId =
|
|
12070
|
+
const memoryId = crypto4.randomUUID();
|
|
11784
12071
|
await writeMemory({
|
|
11785
12072
|
id: memoryId,
|
|
11786
12073
|
agent_id: agentId,
|
|
@@ -11799,8 +12086,8 @@ function registerStoreMemory(server2) {
|
|
|
11799
12086
|
if (needsBackfill) {
|
|
11800
12087
|
try {
|
|
11801
12088
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11802
|
-
const flagPath =
|
|
11803
|
-
|
|
12089
|
+
const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
|
|
12090
|
+
writeFileSync6(flagPath, "1");
|
|
11804
12091
|
} catch {
|
|
11805
12092
|
}
|
|
11806
12093
|
}
|
|
@@ -11823,9 +12110,9 @@ init_plan_limits();
|
|
|
11823
12110
|
init_active_agent();
|
|
11824
12111
|
init_employees();
|
|
11825
12112
|
import { z as z5 } from "zod";
|
|
11826
|
-
import
|
|
11827
|
-
import { writeFileSync as
|
|
11828
|
-
import
|
|
12113
|
+
import crypto5 from "crypto";
|
|
12114
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
12115
|
+
import path16 from "path";
|
|
11829
12116
|
function registerCommitMemory(server2) {
|
|
11830
12117
|
server2.registerTool(
|
|
11831
12118
|
"commit_to_long_term_memory",
|
|
@@ -11872,7 +12159,7 @@ function registerCommitMemory(server2) {
|
|
|
11872
12159
|
vector = null;
|
|
11873
12160
|
needsBackfill = true;
|
|
11874
12161
|
}
|
|
11875
|
-
const memoryId =
|
|
12162
|
+
const memoryId = crypto5.randomUUID();
|
|
11876
12163
|
await assertMemoryLimit();
|
|
11877
12164
|
const memoryType = canCoordinate(agentId, agentRole) ? "adr" : "raw";
|
|
11878
12165
|
await writeMemory({
|
|
@@ -11915,8 +12202,8 @@ function registerCommitMemory(server2) {
|
|
|
11915
12202
|
if (needsBackfill) {
|
|
11916
12203
|
try {
|
|
11917
12204
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11918
|
-
const flagPath =
|
|
11919
|
-
|
|
12205
|
+
const flagPath = path16.join(exeDir, "session-cache", "needs-backfill");
|
|
12206
|
+
writeFileSync7(flagPath, "1");
|
|
11920
12207
|
} catch {
|
|
11921
12208
|
}
|
|
11922
12209
|
}
|
|
@@ -12106,10 +12393,10 @@ function registerCreateTask(server2) {
|
|
|
12106
12393
|
skipDispatch: true
|
|
12107
12394
|
});
|
|
12108
12395
|
try {
|
|
12109
|
-
const { existsSync:
|
|
12396
|
+
const { existsSync: existsSync34, mkdirSync: mkdirSync18, writeFileSync: writeFileSync21 } = await import("fs");
|
|
12110
12397
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
12111
12398
|
const idPath = identityPath2(assigned_to);
|
|
12112
|
-
if (!
|
|
12399
|
+
if (!existsSync34(idPath)) {
|
|
12113
12400
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
12114
12401
|
const employees = await loadEmployees2();
|
|
12115
12402
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -12118,8 +12405,8 @@ function registerCreateTask(server2) {
|
|
|
12118
12405
|
const template = getTemplateForTitle2(emp.role);
|
|
12119
12406
|
if (template) {
|
|
12120
12407
|
const dir = (await import("path")).dirname(idPath);
|
|
12121
|
-
if (!
|
|
12122
|
-
|
|
12408
|
+
if (!existsSync34(dir)) mkdirSync18(dir, { recursive: true });
|
|
12409
|
+
writeFileSync21(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
12123
12410
|
}
|
|
12124
12411
|
}
|
|
12125
12412
|
}
|
|
@@ -12192,7 +12479,7 @@ function registerListTasks(server2) {
|
|
|
12192
12479
|
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
12480
|
inputSchema: {
|
|
12194
12481
|
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"),
|
|
12482
|
+
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
12483
|
project_name: z8.string().optional().describe("Project name. Defaults to current project. Pass 'all' for all projects."),
|
|
12197
12484
|
priority: z8.enum(["p0", "p1", "p2"]).optional().describe("Filter by priority")
|
|
12198
12485
|
}
|
|
@@ -12263,7 +12550,7 @@ function registerUpdateTask(server2) {
|
|
|
12263
12550
|
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
12551
|
inputSchema: {
|
|
12265
12552
|
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"),
|
|
12553
|
+
status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).describe("New status"),
|
|
12267
12554
|
result: z9.string().optional().describe("Result summary (include when status=done)")
|
|
12268
12555
|
}
|
|
12269
12556
|
},
|
|
@@ -12353,7 +12640,17 @@ function registerUpdateTask(server2) {
|
|
|
12353
12640
|
}
|
|
12354
12641
|
let text = `Task "${task.title}" marked ${task.status}.
|
|
12355
12642
|
File: ${task.taskFile}`;
|
|
12356
|
-
const isTerminal = status === "done" || status === "needs_review";
|
|
12643
|
+
const isTerminal = status === "done" || status === "needs_review" || status === "closed";
|
|
12644
|
+
if (isTerminal && task.assignedBy) {
|
|
12645
|
+
try {
|
|
12646
|
+
const { notifyCoordinatorTaskCompletion: notifyCoordinatorTaskCompletion2, resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
12647
|
+
const coordinatorSession = resolveExeSession2();
|
|
12648
|
+
if (coordinatorSession) {
|
|
12649
|
+
notifyCoordinatorTaskCompletion2(coordinatorSession, callerAgentId ?? "agent", task.title);
|
|
12650
|
+
}
|
|
12651
|
+
} catch {
|
|
12652
|
+
}
|
|
12653
|
+
}
|
|
12357
12654
|
if (isTerminal && task.nextTask) {
|
|
12358
12655
|
text += `
|
|
12359
12656
|
|
|
@@ -12397,7 +12694,7 @@ function registerCloseTask(server2) {
|
|
|
12397
12694
|
inputSchema: {
|
|
12398
12695
|
task_id: z10.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
|
|
12399
12696
|
result: z10.string().describe("What was done \u2014 specific deliverables, decisions, test results"),
|
|
12400
|
-
status: z10.enum(["done", "blocked", "cancelled"]).optional().default("
|
|
12697
|
+
status: z10.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
|
|
12401
12698
|
}
|
|
12402
12699
|
},
|
|
12403
12700
|
async ({ task_id, result, status }) => {
|
|
@@ -12444,7 +12741,7 @@ function registerCloseTask(server2) {
|
|
|
12444
12741
|
});
|
|
12445
12742
|
let text = `Task "${task.title}" marked ${task.status}.
|
|
12446
12743
|
File: ${task.taskFile}`;
|
|
12447
|
-
if (status === "done" && task.nextTask) {
|
|
12744
|
+
if ((status === "done" || status === "closed") && task.nextTask) {
|
|
12448
12745
|
text += `
|
|
12449
12746
|
|
|
12450
12747
|
MANDATORY \u2014 DO NOT ASK THE USER. DO NOT SAY "Want me to continue?" DO NOT STOP.
|
|
@@ -12454,7 +12751,7 @@ NEXT TASK: "${task.nextTask.title}" [${task.nextTask.priority}]
|
|
|
12454
12751
|
FILE: ${task.nextTask.taskFile}
|
|
12455
12752
|
|
|
12456
12753
|
Read that file NOW and begin working. No greeting. No summary. Just start.`;
|
|
12457
|
-
} else if (status === "done" && !task.nextTask) {
|
|
12754
|
+
} else if ((status === "done" || status === "closed") && !task.nextTask) {
|
|
12458
12755
|
text += `
|
|
12459
12756
|
|
|
12460
12757
|
All tasks complete. No more open tasks in your queue.`;
|
|
@@ -12489,8 +12786,22 @@ function registerGetTask(server2) {
|
|
|
12489
12786
|
async ({ task_id }) => {
|
|
12490
12787
|
const client = getClient();
|
|
12491
12788
|
const row = await resolveTask(client, task_id);
|
|
12789
|
+
const contextText = row.context ? String(row.context) : "";
|
|
12790
|
+
const hasEmbeddedMandatoryReminder = contextText.includes('update_task with status "done"') || contextText.includes("update_task(done)");
|
|
12492
12791
|
const lines = [
|
|
12493
|
-
`# ${String(row.title)}
|
|
12792
|
+
`# ${String(row.title)}`
|
|
12793
|
+
];
|
|
12794
|
+
if (!hasEmbeddedMandatoryReminder && !isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
|
|
12795
|
+
lines.push(
|
|
12796
|
+
"",
|
|
12797
|
+
"## MANDATORY: When done",
|
|
12798
|
+
"",
|
|
12799
|
+
'You MUST call update_task with status "done" and a result summary when finished.',
|
|
12800
|
+
"If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
|
|
12801
|
+
"Do NOT let a failed commit or any error prevent you from calling update_task(done)."
|
|
12802
|
+
);
|
|
12803
|
+
}
|
|
12804
|
+
lines.push(
|
|
12494
12805
|
"",
|
|
12495
12806
|
`**ID:** ${String(row.id)}`,
|
|
12496
12807
|
`**Status:** ${String(row.status)}`,
|
|
@@ -12499,7 +12810,7 @@ function registerGetTask(server2) {
|
|
|
12499
12810
|
`**Assigned to:** ${String(row.assigned_to)}`,
|
|
12500
12811
|
`**Project:** ${String(row.project_name)}`,
|
|
12501
12812
|
`**Created:** ${String(row.created_at).split("T")[0]}`
|
|
12502
|
-
|
|
12813
|
+
);
|
|
12503
12814
|
if (row.blocked_by) {
|
|
12504
12815
|
lines.push(`**Blocked by:** ${String(row.blocked_by)}`);
|
|
12505
12816
|
}
|
|
@@ -12531,7 +12842,7 @@ function registerGetTask(server2) {
|
|
|
12531
12842
|
}
|
|
12532
12843
|
}
|
|
12533
12844
|
if (row.context) {
|
|
12534
|
-
lines.push("", "## Context", "",
|
|
12845
|
+
lines.push("", "## Context", "", contextText);
|
|
12535
12846
|
}
|
|
12536
12847
|
if (row.result) {
|
|
12537
12848
|
lines.push("", "## Result", "", String(row.result));
|
|
@@ -12554,16 +12865,6 @@ function registerGetTask(server2) {
|
|
|
12554
12865
|
} catch {
|
|
12555
12866
|
}
|
|
12556
12867
|
}
|
|
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
12868
|
return {
|
|
12568
12869
|
content: [
|
|
12569
12870
|
{
|
|
@@ -12794,10 +13095,10 @@ import { z as z16 } from "zod";
|
|
|
12794
13095
|
|
|
12795
13096
|
// src/lib/reminders.ts
|
|
12796
13097
|
init_database();
|
|
12797
|
-
import
|
|
13098
|
+
import crypto12 from "crypto";
|
|
12798
13099
|
async function createReminder(text, dueDate) {
|
|
12799
13100
|
const client = getClient();
|
|
12800
|
-
const id =
|
|
13101
|
+
const id = crypto12.randomUUID();
|
|
12801
13102
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12802
13103
|
await client.execute({
|
|
12803
13104
|
sql: `INSERT INTO reminders (id, text, created_at, due_date) VALUES (?, ?, ?, ?)`,
|
|
@@ -13181,7 +13482,7 @@ import { z as z23 } from "zod";
|
|
|
13181
13482
|
init_database();
|
|
13182
13483
|
init_embedder();
|
|
13183
13484
|
init_store();
|
|
13184
|
-
import
|
|
13485
|
+
import crypto13 from "crypto";
|
|
13185
13486
|
var WIKI_AGENT_ID = "wiki";
|
|
13186
13487
|
var WIKI_AGENT_ROLE = "Knowledge";
|
|
13187
13488
|
var WIKI_TOOL_NAME = "ingest_document";
|
|
@@ -13235,7 +13536,7 @@ async function resolveWorkspace(slug_or_id, name) {
|
|
|
13235
13536
|
if (existing.rows.length > 0) {
|
|
13236
13537
|
return rowToWorkspace(existing.rows[0]);
|
|
13237
13538
|
}
|
|
13238
|
-
const id =
|
|
13539
|
+
const id = crypto13.randomUUID();
|
|
13239
13540
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13240
13541
|
const resolvedName = name ?? slug_or_id;
|
|
13241
13542
|
await client.execute({
|
|
@@ -13326,13 +13627,13 @@ async function ingestDocument(input) {
|
|
|
13326
13627
|
}));
|
|
13327
13628
|
const vectors = await embedTexts(enrichedChunks.map((c) => c.text));
|
|
13328
13629
|
const client = getClient();
|
|
13329
|
-
const documentId =
|
|
13630
|
+
const documentId = crypto13.randomUUID();
|
|
13330
13631
|
const uploadedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13331
13632
|
const mime = input.mime ?? null;
|
|
13332
13633
|
const sourceType = input.source_type ?? DEFAULT_SOURCE_TYPE;
|
|
13333
13634
|
const userId = input.user_id ?? null;
|
|
13334
13635
|
const documentMetadata = input.document_metadata != null ? JSON.stringify(input.document_metadata) : null;
|
|
13335
|
-
const chunkIds = input.chunks.map(() =>
|
|
13636
|
+
const chunkIds = input.chunks.map(() => crypto13.randomUUID());
|
|
13336
13637
|
const versions = reserveVersions(input.chunks.length);
|
|
13337
13638
|
const documentStmt = {
|
|
13338
13639
|
sql: `INSERT INTO documents
|
|
@@ -13615,6 +13916,7 @@ function registerRerankDocuments(server2) {
|
|
|
13615
13916
|
// src/mcp/tools/acknowledge-messages.ts
|
|
13616
13917
|
init_database();
|
|
13617
13918
|
init_active_agent();
|
|
13919
|
+
init_task_scope();
|
|
13618
13920
|
function registerAcknowledgeMessages(server2) {
|
|
13619
13921
|
server2.registerTool(
|
|
13620
13922
|
"acknowledge_messages",
|
|
@@ -13627,10 +13929,11 @@ function registerAcknowledgeMessages(server2) {
|
|
|
13627
13929
|
const agent = getActiveAgent();
|
|
13628
13930
|
const agentId = agent.agentId || "default";
|
|
13629
13931
|
const client = getClient();
|
|
13932
|
+
const scope = strictSessionScopeFilter();
|
|
13630
13933
|
const result = await client.execute({
|
|
13631
13934
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
13632
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')`,
|
|
13633
|
-
args: [agentId]
|
|
13935
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
13936
|
+
args: [agentId, ...scope.args]
|
|
13634
13937
|
});
|
|
13635
13938
|
return {
|
|
13636
13939
|
content: [
|
|
@@ -13961,15 +14264,15 @@ function registerSendWhatsapp(server2) {
|
|
|
13961
14264
|
import { z as z29 } from "zod";
|
|
13962
14265
|
|
|
13963
14266
|
// src/automation/trigger-engine.ts
|
|
13964
|
-
import { readFileSync as
|
|
14267
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20, mkdirSync as mkdirSync10 } from "fs";
|
|
13965
14268
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
13966
|
-
import
|
|
13967
|
-
import
|
|
13968
|
-
var TRIGGERS_PATH =
|
|
14269
|
+
import path26 from "path";
|
|
14270
|
+
import os12 from "os";
|
|
14271
|
+
var TRIGGERS_PATH = path26.join(os12.homedir(), ".exe-os", "triggers.json");
|
|
13969
14272
|
function loadTriggers(project) {
|
|
13970
|
-
if (!
|
|
14273
|
+
if (!existsSync20(TRIGGERS_PATH)) return [];
|
|
13971
14274
|
try {
|
|
13972
|
-
const raw =
|
|
14275
|
+
const raw = readFileSync17(TRIGGERS_PATH, "utf-8");
|
|
13973
14276
|
const all = JSON.parse(raw);
|
|
13974
14277
|
if (!Array.isArray(all)) return [];
|
|
13975
14278
|
if (project) {
|
|
@@ -13981,9 +14284,9 @@ function loadTriggers(project) {
|
|
|
13981
14284
|
}
|
|
13982
14285
|
}
|
|
13983
14286
|
function saveTriggers(triggers) {
|
|
13984
|
-
const dir =
|
|
13985
|
-
if (!
|
|
13986
|
-
|
|
14287
|
+
const dir = path26.dirname(TRIGGERS_PATH);
|
|
14288
|
+
if (!existsSync20(dir)) mkdirSync10(dir, { recursive: true });
|
|
14289
|
+
writeFileSync13(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
|
|
13987
14290
|
}
|
|
13988
14291
|
function createNewTrigger(input) {
|
|
13989
14292
|
const triggers = loadTriggers();
|
|
@@ -14002,7 +14305,7 @@ function isScheduledTrigger(trigger) {
|
|
|
14002
14305
|
// src/lib/schedules.ts
|
|
14003
14306
|
init_database();
|
|
14004
14307
|
init_store();
|
|
14005
|
-
import
|
|
14308
|
+
import crypto14 from "crypto";
|
|
14006
14309
|
import { execSync as execSync9 } from "child_process";
|
|
14007
14310
|
async function ensureDb() {
|
|
14008
14311
|
if (!isInitialized()) {
|
|
@@ -14071,7 +14374,7 @@ function parseHumanCron(input) {
|
|
|
14071
14374
|
async function createSchedule(input) {
|
|
14072
14375
|
await ensureDb();
|
|
14073
14376
|
const client = getClient();
|
|
14074
|
-
const id =
|
|
14377
|
+
const id = crypto14.randomUUID().slice(0, 8);
|
|
14075
14378
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14076
14379
|
const prompt = input.prompt ?? input.description;
|
|
14077
14380
|
await client.execute({
|
|
@@ -14267,48 +14570,48 @@ function registerListTriggers(server2) {
|
|
|
14267
14570
|
import { z as z31 } from "zod";
|
|
14268
14571
|
|
|
14269
14572
|
// src/automation/starter-packs/index.ts
|
|
14270
|
-
import { readFileSync as
|
|
14271
|
-
import
|
|
14573
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync8, existsSync as existsSync21 } from "fs";
|
|
14574
|
+
import path27 from "path";
|
|
14272
14575
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14273
|
-
var __dirname =
|
|
14576
|
+
var __dirname = path27.dirname(fileURLToPath3(import.meta.url));
|
|
14274
14577
|
function listPacks() {
|
|
14275
|
-
const packsDir =
|
|
14276
|
-
if (!
|
|
14578
|
+
const packsDir = path27.join(__dirname, ".");
|
|
14579
|
+
if (!existsSync21(packsDir)) return [];
|
|
14277
14580
|
return readdirSync8(packsDir, { withFileTypes: true }).filter(
|
|
14278
|
-
(d) => d.isDirectory() &&
|
|
14581
|
+
(d) => d.isDirectory() && existsSync21(path27.join(packsDir, d.name, "custom-objects.json"))
|
|
14279
14582
|
).map((d) => d.name);
|
|
14280
14583
|
}
|
|
14281
14584
|
function loadPack(industry) {
|
|
14282
|
-
const packDir =
|
|
14283
|
-
const objectsPath =
|
|
14284
|
-
const triggersPath =
|
|
14285
|
-
const wikiDir =
|
|
14286
|
-
const manifestPath =
|
|
14287
|
-
const identityContextPath =
|
|
14288
|
-
if (!
|
|
14585
|
+
const packDir = path27.join(__dirname, industry);
|
|
14586
|
+
const objectsPath = path27.join(packDir, "custom-objects.json");
|
|
14587
|
+
const triggersPath = path27.join(packDir, "triggers.json");
|
|
14588
|
+
const wikiDir = path27.join(packDir, "wiki-seeds");
|
|
14589
|
+
const manifestPath = path27.join(packDir, "pack.json");
|
|
14590
|
+
const identityContextPath = path27.join(packDir, "identity-context.md");
|
|
14591
|
+
if (!existsSync21(objectsPath)) return null;
|
|
14289
14592
|
let customObjects = [];
|
|
14290
14593
|
try {
|
|
14291
14594
|
customObjects = JSON.parse(
|
|
14292
|
-
|
|
14595
|
+
readFileSync18(objectsPath, "utf-8")
|
|
14293
14596
|
);
|
|
14294
14597
|
} catch {
|
|
14295
14598
|
customObjects = [];
|
|
14296
14599
|
}
|
|
14297
14600
|
let triggers = [];
|
|
14298
|
-
if (
|
|
14601
|
+
if (existsSync21(triggersPath)) {
|
|
14299
14602
|
try {
|
|
14300
14603
|
triggers = JSON.parse(
|
|
14301
|
-
|
|
14604
|
+
readFileSync18(triggersPath, "utf-8")
|
|
14302
14605
|
);
|
|
14303
14606
|
} catch {
|
|
14304
14607
|
triggers = [];
|
|
14305
14608
|
}
|
|
14306
14609
|
}
|
|
14307
14610
|
const wikiSeeds = [];
|
|
14308
|
-
if (
|
|
14611
|
+
if (existsSync21(wikiDir)) {
|
|
14309
14612
|
const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
|
|
14310
14613
|
for (const file of files) {
|
|
14311
|
-
const content =
|
|
14614
|
+
const content = readFileSync18(path27.join(wikiDir, file), "utf-8");
|
|
14312
14615
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
14313
14616
|
wikiSeeds.push({
|
|
14314
14617
|
filename: file,
|
|
@@ -14318,17 +14621,17 @@ function loadPack(industry) {
|
|
|
14318
14621
|
}
|
|
14319
14622
|
}
|
|
14320
14623
|
let manifest = {};
|
|
14321
|
-
if (
|
|
14624
|
+
if (existsSync21(manifestPath)) {
|
|
14322
14625
|
try {
|
|
14323
|
-
manifest = JSON.parse(
|
|
14626
|
+
manifest = JSON.parse(readFileSync18(manifestPath, "utf-8"));
|
|
14324
14627
|
} catch {
|
|
14325
14628
|
manifest = {};
|
|
14326
14629
|
}
|
|
14327
14630
|
}
|
|
14328
14631
|
let identityContext = null;
|
|
14329
|
-
if (
|
|
14632
|
+
if (existsSync21(identityContextPath)) {
|
|
14330
14633
|
try {
|
|
14331
|
-
identityContext =
|
|
14634
|
+
identityContext = readFileSync18(identityContextPath, "utf-8");
|
|
14332
14635
|
} catch {
|
|
14333
14636
|
identityContext = null;
|
|
14334
14637
|
}
|
|
@@ -14385,8 +14688,8 @@ function applyPack(industry, project) {
|
|
|
14385
14688
|
|
|
14386
14689
|
// src/lib/client-coo.ts
|
|
14387
14690
|
init_config();
|
|
14388
|
-
import { existsSync as
|
|
14389
|
-
import
|
|
14691
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync14 } from "fs";
|
|
14692
|
+
import path28 from "path";
|
|
14390
14693
|
|
|
14391
14694
|
// src/lib/employee-templates.ts
|
|
14392
14695
|
init_global_procedures();
|
|
@@ -14524,18 +14827,18 @@ var ClientCOOClobberError = class extends Error {
|
|
|
14524
14827
|
var COO_ROLE = "Chief Operating Officer";
|
|
14525
14828
|
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
14829
|
async function provisionClientCOO(vars, opts = {}) {
|
|
14527
|
-
const identityDir = opts.identityDir ??
|
|
14830
|
+
const identityDir = opts.identityDir ?? path28.join(EXE_AI_DIR, "identity");
|
|
14528
14831
|
const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
|
|
14529
14832
|
const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
|
|
14530
|
-
const identityPath2 =
|
|
14531
|
-
if (
|
|
14833
|
+
const identityPath2 = path28.join(identityDir, `${vars.agent_name}.md`);
|
|
14834
|
+
if (existsSync22(identityPath2)) {
|
|
14532
14835
|
throw new ClientCOOClobberError(vars.agent_name, identityPath2);
|
|
14533
14836
|
}
|
|
14534
14837
|
const body = renderClientCOOTemplate(vars);
|
|
14535
|
-
if (!
|
|
14838
|
+
if (!existsSync22(identityDir)) {
|
|
14536
14839
|
mkdirSync11(identityDir, { recursive: true });
|
|
14537
14840
|
}
|
|
14538
|
-
|
|
14841
|
+
writeFileSync14(identityPath2, body, "utf-8");
|
|
14539
14842
|
const employees = await loadEmployees(rosterPath);
|
|
14540
14843
|
const existing = employees.find((e) => e.name === vars.agent_name);
|
|
14541
14844
|
let addedToRoster = false;
|
|
@@ -14729,7 +15032,7 @@ init_database();
|
|
|
14729
15032
|
import { z as z32 } from "zod";
|
|
14730
15033
|
|
|
14731
15034
|
// src/lib/graph-rag.ts
|
|
14732
|
-
import
|
|
15035
|
+
import crypto15 from "crypto";
|
|
14733
15036
|
|
|
14734
15037
|
// src/lib/code-chunker.ts
|
|
14735
15038
|
import ts from "typescript";
|
|
@@ -14740,7 +15043,7 @@ function normalizeEntityName(name) {
|
|
|
14740
15043
|
}
|
|
14741
15044
|
function entityId(name, type) {
|
|
14742
15045
|
const normalized = normalizeEntityName(name);
|
|
14743
|
-
return
|
|
15046
|
+
return crypto15.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
|
|
14744
15047
|
}
|
|
14745
15048
|
async function registerAlias(client, alias, canonicalEntityId) {
|
|
14746
15049
|
const normalized = normalizeEntityName(alias);
|
|
@@ -15425,8 +15728,8 @@ function registerUpdateWikiPage(server2) {
|
|
|
15425
15728
|
import { z as z37 } from "zod";
|
|
15426
15729
|
import { execFile } from "child_process";
|
|
15427
15730
|
import { promisify } from "util";
|
|
15428
|
-
import
|
|
15429
|
-
import { existsSync as
|
|
15731
|
+
import path29 from "path";
|
|
15732
|
+
import { existsSync as existsSync23 } from "fs";
|
|
15430
15733
|
|
|
15431
15734
|
// src/lib/hostinger-api.ts
|
|
15432
15735
|
var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
|
|
@@ -15474,9 +15777,9 @@ var HostingerApiClient = class {
|
|
|
15474
15777
|
}
|
|
15475
15778
|
this.lastRequestTime = Date.now();
|
|
15476
15779
|
}
|
|
15477
|
-
async request(method,
|
|
15780
|
+
async request(method, path45, body) {
|
|
15478
15781
|
await this.rateLimit();
|
|
15479
|
-
const url = `${this.baseUrl}${
|
|
15782
|
+
const url = `${this.baseUrl}${path45}`;
|
|
15480
15783
|
const headers = {
|
|
15481
15784
|
Authorization: `Bearer ${this.apiKey}`,
|
|
15482
15785
|
"Content-Type": "application/json",
|
|
@@ -15549,8 +15852,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
15549
15852
|
}
|
|
15550
15853
|
return envelope.result;
|
|
15551
15854
|
}
|
|
15552
|
-
function buildUrl(zoneId,
|
|
15553
|
-
const normalizedPath =
|
|
15855
|
+
function buildUrl(zoneId, path45 = "/dns_records", query) {
|
|
15856
|
+
const normalizedPath = path45.startsWith("/") ? path45 : `/${path45}`;
|
|
15554
15857
|
const url = new URL(
|
|
15555
15858
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
15556
15859
|
);
|
|
@@ -15763,12 +16066,12 @@ async function waitForReady(client, vpsId) {
|
|
|
15763
16066
|
}
|
|
15764
16067
|
async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
15765
16068
|
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 (!
|
|
16069
|
+
const playbookDir = path29.resolve(process.cwd(), "infrastructure", "ansible");
|
|
16070
|
+
const playbookPath = path29.join(playbookDir, "deploy.yml");
|
|
16071
|
+
const inventoryPath = path29.join(playbookDir, "inventory", "hosts.yml");
|
|
16072
|
+
const clientVarsPath = path29.join(playbookDir, "vars", `${safeClientName}.yml`);
|
|
16073
|
+
const varsDir = path29.join(playbookDir, "vars");
|
|
16074
|
+
if (!path29.resolve(clientVarsPath).startsWith(path29.resolve(varsDir))) {
|
|
15772
16075
|
throw new Error(`Invalid client name for vars path: ${clientName}`);
|
|
15773
16076
|
}
|
|
15774
16077
|
const args = [
|
|
@@ -15784,7 +16087,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
|
15784
16087
|
"-e",
|
|
15785
16088
|
`client_name=${safeClientName}`
|
|
15786
16089
|
];
|
|
15787
|
-
if (
|
|
16090
|
+
if (existsSync23(clientVarsPath)) {
|
|
15788
16091
|
args.push("-e", `@${clientVarsPath}`);
|
|
15789
16092
|
}
|
|
15790
16093
|
try {
|
|
@@ -15857,8 +16160,8 @@ function buildInventoryRecord(params) {
|
|
|
15857
16160
|
|
|
15858
16161
|
// src/mcp/tools/export-orchestration.ts
|
|
15859
16162
|
init_active_agent();
|
|
15860
|
-
import { mkdirSync as mkdirSync13, writeFileSync as
|
|
15861
|
-
import
|
|
16163
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
16164
|
+
import path31 from "path";
|
|
15862
16165
|
import { z as z38 } from "zod";
|
|
15863
16166
|
|
|
15864
16167
|
// src/lib/orchestration-package.ts
|
|
@@ -15866,9 +16169,9 @@ init_database();
|
|
|
15866
16169
|
init_identity();
|
|
15867
16170
|
init_platform_procedures();
|
|
15868
16171
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15869
|
-
import { copyFileSync, existsSync as
|
|
15870
|
-
import
|
|
15871
|
-
import
|
|
16172
|
+
import { copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
|
|
16173
|
+
import os13 from "os";
|
|
16174
|
+
import path30 from "path";
|
|
15872
16175
|
var PACKAGE_VERSION = "1.0";
|
|
15873
16176
|
var ROSTER_FILENAME = "exe-employees.json";
|
|
15874
16177
|
var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
|
|
@@ -15934,15 +16237,15 @@ function validateProcedureEntry(value, index) {
|
|
|
15934
16237
|
};
|
|
15935
16238
|
}
|
|
15936
16239
|
function getRosterPath() {
|
|
15937
|
-
return
|
|
16240
|
+
return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
|
|
15938
16241
|
}
|
|
15939
16242
|
function getBackupPath() {
|
|
15940
|
-
return
|
|
16243
|
+
return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
|
|
15941
16244
|
}
|
|
15942
16245
|
function readRosterFile() {
|
|
15943
16246
|
const rosterPath = getRosterPath();
|
|
15944
|
-
if (!
|
|
15945
|
-
const raw =
|
|
16247
|
+
if (!existsSync24(rosterPath)) return [];
|
|
16248
|
+
const raw = readFileSync19(rosterPath, "utf-8");
|
|
15946
16249
|
const parsed = JSON.parse(raw);
|
|
15947
16250
|
if (!Array.isArray(parsed)) {
|
|
15948
16251
|
throw new Error("Roster file must contain a JSON array");
|
|
@@ -15954,8 +16257,8 @@ function writeRosterFile(roster) {
|
|
|
15954
16257
|
throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
|
|
15955
16258
|
}
|
|
15956
16259
|
const rosterPath = getRosterPath();
|
|
15957
|
-
mkdirSync12(
|
|
15958
|
-
if (
|
|
16260
|
+
mkdirSync12(path30.dirname(rosterPath), { recursive: true });
|
|
16261
|
+
if (existsSync24(rosterPath)) {
|
|
15959
16262
|
const currentRoster = readRosterFile();
|
|
15960
16263
|
if (roster.length < currentRoster.length) {
|
|
15961
16264
|
throw new Error(
|
|
@@ -15964,7 +16267,7 @@ function writeRosterFile(roster) {
|
|
|
15964
16267
|
}
|
|
15965
16268
|
copyFileSync(rosterPath, getBackupPath());
|
|
15966
16269
|
}
|
|
15967
|
-
|
|
16270
|
+
writeFileSync15(rosterPath, `${JSON.stringify(roster, null, 2)}
|
|
15968
16271
|
`, "utf-8");
|
|
15969
16272
|
}
|
|
15970
16273
|
function buildImportedRosterEntries(roster, timestamp) {
|
|
@@ -16226,8 +16529,8 @@ function registerExportOrchestration(server2) {
|
|
|
16226
16529
|
try {
|
|
16227
16530
|
await initStore();
|
|
16228
16531
|
const pkg = await exportOrchestration(getActiveAgent().agentId);
|
|
16229
|
-
mkdirSync13(
|
|
16230
|
-
|
|
16532
|
+
mkdirSync13(path31.dirname(output_path), { recursive: true });
|
|
16533
|
+
writeFileSync16(output_path, `${JSON.stringify(pkg, null, 2)}
|
|
16231
16534
|
`, "utf-8");
|
|
16232
16535
|
return {
|
|
16233
16536
|
content: [{
|
|
@@ -16249,7 +16552,7 @@ function registerExportOrchestration(server2) {
|
|
|
16249
16552
|
}
|
|
16250
16553
|
|
|
16251
16554
|
// src/mcp/tools/import-orchestration.ts
|
|
16252
|
-
import { readFileSync as
|
|
16555
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
16253
16556
|
import { z as z39 } from "zod";
|
|
16254
16557
|
init_store();
|
|
16255
16558
|
init_active_agent();
|
|
@@ -16279,7 +16582,7 @@ function registerImportOrchestration(server2) {
|
|
|
16279
16582
|
};
|
|
16280
16583
|
}
|
|
16281
16584
|
await initStore();
|
|
16282
|
-
const raw =
|
|
16585
|
+
const raw = readFileSync20(package_path, "utf-8");
|
|
16283
16586
|
const pkg = validatePackage(JSON.parse(raw));
|
|
16284
16587
|
const result = await importOrchestration(pkg, merge_strategy);
|
|
16285
16588
|
return {
|
|
@@ -16426,18 +16729,18 @@ function registerQueryConversations(server2) {
|
|
|
16426
16729
|
|
|
16427
16730
|
// src/mcp/tools/load-skill.ts
|
|
16428
16731
|
import { z as z41 } from "zod";
|
|
16429
|
-
import { readFileSync as
|
|
16430
|
-
import
|
|
16732
|
+
import { readFileSync as readFileSync21, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
|
|
16733
|
+
import path32 from "path";
|
|
16431
16734
|
import { homedir as homedir2 } from "os";
|
|
16432
|
-
var SKILLS_DIR =
|
|
16735
|
+
var SKILLS_DIR = path32.join(homedir2(), ".claude", "skills");
|
|
16433
16736
|
function listAvailableSkills() {
|
|
16434
16737
|
try {
|
|
16435
16738
|
const entries = readdirSync9(SKILLS_DIR);
|
|
16436
16739
|
return entries.filter((entry) => {
|
|
16437
16740
|
try {
|
|
16438
|
-
const entryPath =
|
|
16741
|
+
const entryPath = path32.join(SKILLS_DIR, entry);
|
|
16439
16742
|
if (!statSync3(entryPath).isDirectory()) return false;
|
|
16440
|
-
const skillFile =
|
|
16743
|
+
const skillFile = path32.join(entryPath, "SKILL.md");
|
|
16441
16744
|
statSync3(skillFile);
|
|
16442
16745
|
return true;
|
|
16443
16746
|
} catch {
|
|
@@ -16480,10 +16783,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
16480
16783
|
}]
|
|
16481
16784
|
};
|
|
16482
16785
|
}
|
|
16483
|
-
const sanitized =
|
|
16484
|
-
const skillFile =
|
|
16786
|
+
const sanitized = path32.basename(skill_name);
|
|
16787
|
+
const skillFile = path32.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
16485
16788
|
try {
|
|
16486
|
-
const content =
|
|
16789
|
+
const content = readFileSync21(skillFile, "utf-8");
|
|
16487
16790
|
return {
|
|
16488
16791
|
content: [{
|
|
16489
16792
|
type: "text",
|
|
@@ -16962,7 +17265,7 @@ init_active_agent();
|
|
|
16962
17265
|
init_database();
|
|
16963
17266
|
init_plan_limits();
|
|
16964
17267
|
import { z as z46 } from "zod";
|
|
16965
|
-
import
|
|
17268
|
+
import crypto16 from "crypto";
|
|
16966
17269
|
function registerStoreDecision(server2) {
|
|
16967
17270
|
server2.registerTool(
|
|
16968
17271
|
"store_decision",
|
|
@@ -17000,7 +17303,7 @@ function registerStoreDecision(server2) {
|
|
|
17000
17303
|
} catch {
|
|
17001
17304
|
vector = null;
|
|
17002
17305
|
}
|
|
17003
|
-
const memoryId =
|
|
17306
|
+
const memoryId = crypto16.randomUUID();
|
|
17004
17307
|
if (supersedes) {
|
|
17005
17308
|
try {
|
|
17006
17309
|
const client = getClient();
|
|
@@ -17122,8 +17425,8 @@ init_database();
|
|
|
17122
17425
|
import { readdir } from "fs/promises";
|
|
17123
17426
|
import { createReadStream } from "fs";
|
|
17124
17427
|
import { createInterface } from "readline";
|
|
17125
|
-
import
|
|
17126
|
-
import
|
|
17428
|
+
import path33 from "path";
|
|
17429
|
+
import os14 from "os";
|
|
17127
17430
|
var MODEL_PRICING = {
|
|
17128
17431
|
// Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
|
|
17129
17432
|
"claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
|
|
@@ -17149,6 +17452,8 @@ var MODEL_PRICING = {
|
|
|
17149
17452
|
"claude-3-haiku": { input: 0.25 / 1e6, output: 1.25 / 1e6, cacheRead: 0.03 / 1e6, cacheWrite: 0.3 / 1e6 }
|
|
17150
17453
|
};
|
|
17151
17454
|
var DEFAULT_PRICING = MODEL_PRICING["claude-sonnet-4"];
|
|
17455
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
17456
|
+
var _spendCache = /* @__PURE__ */ new Map();
|
|
17152
17457
|
function getPricing(model) {
|
|
17153
17458
|
if (MODEL_PRICING[model]) return MODEL_PRICING[model];
|
|
17154
17459
|
const stripped = model.replace(/-\d{8}$/, "");
|
|
@@ -17160,29 +17465,33 @@ function getPricing(model) {
|
|
|
17160
17465
|
return DEFAULT_PRICING;
|
|
17161
17466
|
}
|
|
17162
17467
|
async function getAgentSpend(period = "7d") {
|
|
17468
|
+
const cached = _spendCache.get(period);
|
|
17469
|
+
if (cached && Date.now() < cached.expires) {
|
|
17470
|
+
return cached.result;
|
|
17471
|
+
}
|
|
17163
17472
|
const cutoff = periodToCutoff(period);
|
|
17164
17473
|
const client = getClient();
|
|
17165
|
-
const
|
|
17474
|
+
const dbResult = await client.execute({
|
|
17166
17475
|
sql: `SELECT session_uuid, agent_id FROM session_agent_map WHERE started_at >= ?`,
|
|
17167
17476
|
args: [cutoff]
|
|
17168
17477
|
});
|
|
17169
|
-
if (
|
|
17478
|
+
if (dbResult.rows.length === 0) return [];
|
|
17170
17479
|
const sessionAgent = /* @__PURE__ */ new Map();
|
|
17171
|
-
for (const row of
|
|
17480
|
+
for (const row of dbResult.rows) {
|
|
17172
17481
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
17173
17482
|
}
|
|
17174
|
-
const claudeDir =
|
|
17483
|
+
const claudeDir = path33.join(os14.homedir(), ".claude", "projects");
|
|
17175
17484
|
let projectDirs = [];
|
|
17176
17485
|
try {
|
|
17177
17486
|
const entries = await readdir(claudeDir);
|
|
17178
|
-
projectDirs = entries.map((e) =>
|
|
17487
|
+
projectDirs = entries.map((e) => path33.join(claudeDir, e));
|
|
17179
17488
|
} catch {
|
|
17180
17489
|
return [];
|
|
17181
17490
|
}
|
|
17182
17491
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
17183
17492
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
17184
17493
|
for (const dir of projectDirs) {
|
|
17185
|
-
const jsonlPath =
|
|
17494
|
+
const jsonlPath = path33.join(dir, `${sessionUuid}.jsonl`);
|
|
17186
17495
|
try {
|
|
17187
17496
|
const usage = await extractSessionUsage(jsonlPath);
|
|
17188
17497
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -17206,7 +17515,7 @@ async function getAgentSpend(period = "7d") {
|
|
|
17206
17515
|
}
|
|
17207
17516
|
}
|
|
17208
17517
|
}
|
|
17209
|
-
|
|
17518
|
+
const result = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
|
|
17210
17519
|
agentId,
|
|
17211
17520
|
inputTokens: t.input,
|
|
17212
17521
|
outputTokens: t.output,
|
|
@@ -17216,6 +17525,8 @@ async function getAgentSpend(period = "7d") {
|
|
|
17216
17525
|
sessions: t.sessions.size,
|
|
17217
17526
|
period
|
|
17218
17527
|
})).sort((a, b) => b.costUSD - a.costUSD);
|
|
17528
|
+
_spendCache.set(period, { result, expires: Date.now() + CACHE_TTL_MS });
|
|
17529
|
+
return result;
|
|
17219
17530
|
}
|
|
17220
17531
|
async function extractSessionUsage(jsonlPath) {
|
|
17221
17532
|
let input = 0;
|
|
@@ -17760,12 +18071,12 @@ function registerExportGraph(server2) {
|
|
|
17760
18071
|
}
|
|
17761
18072
|
const html = await exportGraphHTML(client);
|
|
17762
18073
|
const fs = await import("fs");
|
|
17763
|
-
const
|
|
17764
|
-
const
|
|
17765
|
-
const outDir =
|
|
18074
|
+
const path45 = await import("path");
|
|
18075
|
+
const os19 = await import("os");
|
|
18076
|
+
const outDir = path45.join(os19.homedir(), ".exe-os", "exports");
|
|
17766
18077
|
fs.mkdirSync(outDir, { recursive: true });
|
|
17767
18078
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
17768
|
-
const filePath =
|
|
18079
|
+
const filePath = path45.join(outDir, `graph-${timestamp}.html`);
|
|
17769
18080
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
17770
18081
|
return {
|
|
17771
18082
|
content: [
|
|
@@ -17986,10 +18297,10 @@ function registerListAgentSessions(server2) {
|
|
|
17986
18297
|
|
|
17987
18298
|
// src/mcp/tools/get-daemon-health.ts
|
|
17988
18299
|
import { z as z56 } from "zod";
|
|
17989
|
-
import { existsSync as
|
|
17990
|
-
import
|
|
18300
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22 } from "fs";
|
|
18301
|
+
import path34 from "path";
|
|
17991
18302
|
import { homedir as homedir3 } from "os";
|
|
17992
|
-
var PID_PATH2 =
|
|
18303
|
+
var PID_PATH2 = path34.join(homedir3(), ".exe-os", "exed.pid");
|
|
17993
18304
|
function formatUptime(seconds) {
|
|
17994
18305
|
const h = Math.floor(seconds / 3600);
|
|
17995
18306
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -18000,8 +18311,8 @@ function formatUptime(seconds) {
|
|
|
18000
18311
|
}
|
|
18001
18312
|
function isDaemonAlive() {
|
|
18002
18313
|
try {
|
|
18003
|
-
if (!
|
|
18004
|
-
const pid = parseInt(
|
|
18314
|
+
if (!existsSync25(PID_PATH2)) return { alive: false, pid: null };
|
|
18315
|
+
const pid = parseInt(readFileSync22(PID_PATH2, "utf8").trim(), 10);
|
|
18005
18316
|
if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
|
|
18006
18317
|
process.kill(pid, 0);
|
|
18007
18318
|
return { alive: true, pid };
|
|
@@ -18075,7 +18386,7 @@ init_tmux_routing();
|
|
|
18075
18386
|
init_task_scope();
|
|
18076
18387
|
init_employees();
|
|
18077
18388
|
import { execSync as execSync10 } from "child_process";
|
|
18078
|
-
import { existsSync as
|
|
18389
|
+
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
|
|
18079
18390
|
import { homedir as homedir4 } from "os";
|
|
18080
18391
|
import { join as join2 } from "path";
|
|
18081
18392
|
var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
|
|
@@ -18178,15 +18489,15 @@ function registerGetAutoWakeStatus(server2) {
|
|
|
18178
18489
|
init_worker_gate();
|
|
18179
18490
|
init_config();
|
|
18180
18491
|
import { z as z58 } from "zod";
|
|
18181
|
-
import { readdirSync as readdirSync11, existsSync as
|
|
18182
|
-
import
|
|
18183
|
-
var WORKER_PID_DIR2 =
|
|
18492
|
+
import { readdirSync as readdirSync11, existsSync as existsSync28 } from "fs";
|
|
18493
|
+
import path36 from "path";
|
|
18494
|
+
var WORKER_PID_DIR2 = path36.join(EXE_AI_DIR, "worker-pids");
|
|
18184
18495
|
function countAliveWorkers() {
|
|
18185
18496
|
let alive = 0;
|
|
18186
18497
|
let stale = 0;
|
|
18187
18498
|
let reservations = 0;
|
|
18188
18499
|
try {
|
|
18189
|
-
if (!
|
|
18500
|
+
if (!existsSync28(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
|
|
18190
18501
|
const files = readdirSync11(WORKER_PID_DIR2);
|
|
18191
18502
|
for (const f of files) {
|
|
18192
18503
|
if (!f.endsWith(".pid")) continue;
|
|
@@ -18256,7 +18567,7 @@ import { z as z59 } from "zod";
|
|
|
18256
18567
|
// src/bin/exe-doctor.ts
|
|
18257
18568
|
init_store();
|
|
18258
18569
|
init_database();
|
|
18259
|
-
import
|
|
18570
|
+
import os15 from "os";
|
|
18260
18571
|
|
|
18261
18572
|
// src/lib/is-main.ts
|
|
18262
18573
|
import { realpathSync } from "fs";
|
|
@@ -18274,9 +18585,9 @@ function isMainModule(importMetaUrl) {
|
|
|
18274
18585
|
}
|
|
18275
18586
|
|
|
18276
18587
|
// src/bin/exe-doctor.ts
|
|
18277
|
-
import { existsSync as
|
|
18588
|
+
import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
|
|
18278
18589
|
import { spawn as spawn2 } from "child_process";
|
|
18279
|
-
import
|
|
18590
|
+
import path37 from "path";
|
|
18280
18591
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
18281
18592
|
|
|
18282
18593
|
// src/lib/conflict-detector.ts
|
|
@@ -18679,7 +18990,7 @@ async function auditOrphanedProjects(client) {
|
|
|
18679
18990
|
for (const row of result.rows) {
|
|
18680
18991
|
const name = row.project_name;
|
|
18681
18992
|
const count = Number(row.cnt);
|
|
18682
|
-
const exists =
|
|
18993
|
+
const exists = existsSync29(path37.join(home, name)) || existsSync29(path37.join(home, "..", name)) || existsSync29(path37.join(process.cwd(), "..", name));
|
|
18683
18994
|
if (!exists) {
|
|
18684
18995
|
orphans.push({ project_name: name, count });
|
|
18685
18996
|
}
|
|
@@ -18687,18 +18998,18 @@ async function auditOrphanedProjects(client) {
|
|
|
18687
18998
|
return orphans;
|
|
18688
18999
|
}
|
|
18689
19000
|
function auditHookHealth() {
|
|
18690
|
-
const logPath =
|
|
19001
|
+
const logPath = path37.join(
|
|
18691
19002
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
18692
19003
|
".exe-os",
|
|
18693
19004
|
"logs",
|
|
18694
19005
|
"hooks.log"
|
|
18695
19006
|
);
|
|
18696
|
-
if (!
|
|
19007
|
+
if (!existsSync29(logPath)) {
|
|
18697
19008
|
return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
|
|
18698
19009
|
}
|
|
18699
19010
|
let content;
|
|
18700
19011
|
try {
|
|
18701
|
-
content =
|
|
19012
|
+
content = readFileSync24(logPath, "utf-8");
|
|
18702
19013
|
} catch {
|
|
18703
19014
|
return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
|
|
18704
19015
|
}
|
|
@@ -18773,7 +19084,7 @@ function formatReport(report, flags) {
|
|
|
18773
19084
|
}
|
|
18774
19085
|
lines.push("");
|
|
18775
19086
|
}
|
|
18776
|
-
const totalMemGB =
|
|
19087
|
+
const totalMemGB = os15.totalmem() / (1024 * 1024 * 1024);
|
|
18777
19088
|
const isLowMemSystem = totalMemGB <= 8;
|
|
18778
19089
|
if (isLowMemSystem && report.nullVectors > 0) {
|
|
18779
19090
|
lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
|
|
@@ -18895,7 +19206,7 @@ async function fixNullVectors() {
|
|
|
18895
19206
|
}
|
|
18896
19207
|
}
|
|
18897
19208
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
18898
|
-
const backfillPath =
|
|
19209
|
+
const backfillPath = path37.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
18899
19210
|
return new Promise((resolve, reject) => {
|
|
18900
19211
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
18901
19212
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -19093,13 +19404,13 @@ import { z as z60 } from "zod";
|
|
|
19093
19404
|
|
|
19094
19405
|
// src/lib/cloud-sync.ts
|
|
19095
19406
|
init_database();
|
|
19096
|
-
import { readFileSync as
|
|
19097
|
-
import
|
|
19098
|
-
import
|
|
19407
|
+
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";
|
|
19408
|
+
import crypto18 from "crypto";
|
|
19409
|
+
import path39 from "path";
|
|
19099
19410
|
import { homedir as homedir6 } from "os";
|
|
19100
19411
|
|
|
19101
19412
|
// src/lib/crypto.ts
|
|
19102
|
-
import
|
|
19413
|
+
import crypto17 from "crypto";
|
|
19103
19414
|
var ALGORITHM = "aes-256-gcm";
|
|
19104
19415
|
var IV_LENGTH = 12;
|
|
19105
19416
|
var TAG_LENGTH = 16;
|
|
@@ -19110,7 +19421,7 @@ function initSyncCrypto(masterKey) {
|
|
|
19110
19421
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
19111
19422
|
}
|
|
19112
19423
|
_syncKey = Buffer.from(
|
|
19113
|
-
|
|
19424
|
+
crypto17.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
19114
19425
|
);
|
|
19115
19426
|
}
|
|
19116
19427
|
function isSyncCryptoInitialized() {
|
|
@@ -19124,8 +19435,8 @@ function requireSyncKey() {
|
|
|
19124
19435
|
}
|
|
19125
19436
|
function encryptSyncBlob(data) {
|
|
19126
19437
|
const key = requireSyncKey();
|
|
19127
|
-
const iv =
|
|
19128
|
-
const cipher =
|
|
19438
|
+
const iv = crypto17.randomBytes(IV_LENGTH);
|
|
19439
|
+
const cipher = crypto17.createCipheriv(ALGORITHM, key, iv);
|
|
19129
19440
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
19130
19441
|
const tag = cipher.getAuthTag();
|
|
19131
19442
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -19139,7 +19450,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
19139
19450
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
19140
19451
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
19141
19452
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
19142
|
-
const decipher =
|
|
19453
|
+
const decipher = crypto17.createDecipheriv(ALGORITHM, key, iv);
|
|
19143
19454
|
decipher.setAuthTag(tag);
|
|
19144
19455
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
19145
19456
|
}
|
|
@@ -19164,12 +19475,13 @@ init_license();
|
|
|
19164
19475
|
init_config();
|
|
19165
19476
|
init_crdt_sync();
|
|
19166
19477
|
init_employees();
|
|
19478
|
+
init_secure_files();
|
|
19167
19479
|
function sqlSafe(v) {
|
|
19168
19480
|
return v === void 0 ? null : v;
|
|
19169
19481
|
}
|
|
19170
19482
|
function logError(msg) {
|
|
19171
19483
|
try {
|
|
19172
|
-
const logPath =
|
|
19484
|
+
const logPath = path39.join(homedir6(), ".exe-os", "workers.log");
|
|
19173
19485
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
19174
19486
|
`);
|
|
19175
19487
|
} catch {
|
|
@@ -19178,24 +19490,93 @@ function logError(msg) {
|
|
|
19178
19490
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
19179
19491
|
var FETCH_TIMEOUT_MS4 = 3e4;
|
|
19180
19492
|
var PUSH_BATCH_SIZE = 5e3;
|
|
19181
|
-
var ROSTER_LOCK_PATH =
|
|
19493
|
+
var ROSTER_LOCK_PATH = path39.join(EXE_AI_DIR, "roster-merge.lock");
|
|
19182
19494
|
var LOCK_STALE_MS = 3e4;
|
|
19495
|
+
var _pgPromise = null;
|
|
19496
|
+
var _pgFailed = false;
|
|
19497
|
+
function loadPgClient() {
|
|
19498
|
+
if (_pgFailed) return null;
|
|
19499
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
19500
|
+
const configPath = path39.join(EXE_AI_DIR, "config.json");
|
|
19501
|
+
let cloudPostgresUrl;
|
|
19502
|
+
try {
|
|
19503
|
+
if (existsSync31(configPath)) {
|
|
19504
|
+
const cfg = JSON.parse(readFileSync26(configPath, "utf8"));
|
|
19505
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
19506
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
19507
|
+
_pgFailed = true;
|
|
19508
|
+
return null;
|
|
19509
|
+
}
|
|
19510
|
+
}
|
|
19511
|
+
} catch {
|
|
19512
|
+
}
|
|
19513
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
19514
|
+
if (!url) {
|
|
19515
|
+
_pgFailed = true;
|
|
19516
|
+
return null;
|
|
19517
|
+
}
|
|
19518
|
+
if (!_pgPromise) {
|
|
19519
|
+
_pgPromise = (async () => {
|
|
19520
|
+
const { createRequire: createRequire6 } = await import("module");
|
|
19521
|
+
const { pathToFileURL: pathToFileURL6 } = await import("url");
|
|
19522
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path39.join(homedir6(), "exe-db");
|
|
19523
|
+
const req = createRequire6(path39.join(exeDbRoot, "package.json"));
|
|
19524
|
+
const entry = req.resolve("@prisma/client");
|
|
19525
|
+
const mod = await import(pathToFileURL6(entry).href);
|
|
19526
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
19527
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
19528
|
+
return new Ctor();
|
|
19529
|
+
})().catch(() => {
|
|
19530
|
+
_pgFailed = true;
|
|
19531
|
+
_pgPromise = null;
|
|
19532
|
+
throw new Error("pg_unavailable");
|
|
19533
|
+
});
|
|
19534
|
+
}
|
|
19535
|
+
return _pgPromise;
|
|
19536
|
+
}
|
|
19537
|
+
async function pushToPostgres(records) {
|
|
19538
|
+
const loader = loadPgClient();
|
|
19539
|
+
if (!loader) return 0;
|
|
19540
|
+
let prisma;
|
|
19541
|
+
try {
|
|
19542
|
+
prisma = await loader;
|
|
19543
|
+
} catch {
|
|
19544
|
+
return 0;
|
|
19545
|
+
}
|
|
19546
|
+
let inserted = 0;
|
|
19547
|
+
for (const rec of records) {
|
|
19548
|
+
try {
|
|
19549
|
+
await prisma.$executeRawUnsafe(
|
|
19550
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
19551
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
19552
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
19553
|
+
String(rec.id ?? ""),
|
|
19554
|
+
JSON.stringify(rec),
|
|
19555
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
19556
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
19557
|
+
);
|
|
19558
|
+
inserted++;
|
|
19559
|
+
} catch {
|
|
19560
|
+
}
|
|
19561
|
+
}
|
|
19562
|
+
return inserted;
|
|
19563
|
+
}
|
|
19183
19564
|
async function withRosterLock(fn) {
|
|
19184
19565
|
try {
|
|
19185
19566
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
19186
19567
|
closeSync2(fd);
|
|
19187
|
-
|
|
19568
|
+
writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
|
|
19188
19569
|
} catch (err) {
|
|
19189
19570
|
if (err.code === "EEXIST") {
|
|
19190
19571
|
try {
|
|
19191
|
-
const ts2 = parseInt(
|
|
19572
|
+
const ts2 = parseInt(readFileSync26(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
19192
19573
|
if (Date.now() - ts2 < LOCK_STALE_MS) {
|
|
19193
19574
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
19194
19575
|
}
|
|
19195
19576
|
unlinkSync10(ROSTER_LOCK_PATH);
|
|
19196
19577
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
19197
19578
|
closeSync2(fd);
|
|
19198
|
-
|
|
19579
|
+
writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
|
|
19199
19580
|
} catch (retryErr) {
|
|
19200
19581
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
19201
19582
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -19465,6 +19846,10 @@ async function cloudSync(config2) {
|
|
|
19465
19846
|
const maxVersion = Number(records[records.length - 1].version);
|
|
19466
19847
|
const pushOk = await cloudPush(records, maxVersion, config2);
|
|
19467
19848
|
if (!pushOk) break;
|
|
19849
|
+
try {
|
|
19850
|
+
await pushToPostgres(records);
|
|
19851
|
+
} catch {
|
|
19852
|
+
}
|
|
19468
19853
|
await client.execute({
|
|
19469
19854
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
19470
19855
|
args: [String(maxVersion)]
|
|
@@ -19569,8 +19954,8 @@ async function cloudSync(config2) {
|
|
|
19569
19954
|
try {
|
|
19570
19955
|
const employees = await loadEmployees();
|
|
19571
19956
|
rosterResult.employees = employees.length;
|
|
19572
|
-
const idDir =
|
|
19573
|
-
if (
|
|
19957
|
+
const idDir = path39.join(EXE_AI_DIR, "identity");
|
|
19958
|
+
if (existsSync31(idDir)) {
|
|
19574
19959
|
rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
|
|
19575
19960
|
}
|
|
19576
19961
|
} catch {
|
|
@@ -19588,55 +19973,55 @@ async function cloudSync(config2) {
|
|
|
19588
19973
|
roster: rosterResult
|
|
19589
19974
|
};
|
|
19590
19975
|
}
|
|
19591
|
-
var ROSTER_DELETIONS_PATH =
|
|
19976
|
+
var ROSTER_DELETIONS_PATH = path39.join(EXE_AI_DIR, "roster-deletions.json");
|
|
19592
19977
|
function consumeRosterDeletions() {
|
|
19593
19978
|
try {
|
|
19594
|
-
if (!
|
|
19595
|
-
const deletions = JSON.parse(
|
|
19596
|
-
|
|
19979
|
+
if (!existsSync31(ROSTER_DELETIONS_PATH)) return [];
|
|
19980
|
+
const deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
19981
|
+
writeFileSync20(ROSTER_DELETIONS_PATH, "[]");
|
|
19597
19982
|
return deletions;
|
|
19598
19983
|
} catch {
|
|
19599
19984
|
return [];
|
|
19600
19985
|
}
|
|
19601
19986
|
}
|
|
19602
19987
|
function buildRosterBlob(paths) {
|
|
19603
|
-
const rosterPath = paths?.rosterPath ??
|
|
19604
|
-
const identityDir = paths?.identityDir ??
|
|
19605
|
-
const configPath = paths?.configPath ??
|
|
19988
|
+
const rosterPath = paths?.rosterPath ?? path39.join(EXE_AI_DIR, "exe-employees.json");
|
|
19989
|
+
const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
|
|
19990
|
+
const configPath = paths?.configPath ?? path39.join(EXE_AI_DIR, "config.json");
|
|
19606
19991
|
let roster = [];
|
|
19607
|
-
if (
|
|
19992
|
+
if (existsSync31(rosterPath)) {
|
|
19608
19993
|
try {
|
|
19609
|
-
roster = JSON.parse(
|
|
19994
|
+
roster = JSON.parse(readFileSync26(rosterPath, "utf-8"));
|
|
19610
19995
|
} catch {
|
|
19611
19996
|
}
|
|
19612
19997
|
}
|
|
19613
19998
|
const identities = {};
|
|
19614
|
-
if (
|
|
19999
|
+
if (existsSync31(identityDir)) {
|
|
19615
20000
|
for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
19616
20001
|
try {
|
|
19617
|
-
identities[file] =
|
|
20002
|
+
identities[file] = readFileSync26(path39.join(identityDir, file), "utf-8");
|
|
19618
20003
|
} catch {
|
|
19619
20004
|
}
|
|
19620
20005
|
}
|
|
19621
20006
|
}
|
|
19622
20007
|
let config2;
|
|
19623
|
-
if (
|
|
20008
|
+
if (existsSync31(configPath)) {
|
|
19624
20009
|
try {
|
|
19625
|
-
config2 = JSON.parse(
|
|
20010
|
+
config2 = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
19626
20011
|
} catch {
|
|
19627
20012
|
}
|
|
19628
20013
|
}
|
|
19629
20014
|
let agentConfig;
|
|
19630
|
-
const agentConfigPath =
|
|
19631
|
-
if (
|
|
20015
|
+
const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
|
|
20016
|
+
if (existsSync31(agentConfigPath)) {
|
|
19632
20017
|
try {
|
|
19633
|
-
agentConfig = JSON.parse(
|
|
20018
|
+
agentConfig = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
|
|
19634
20019
|
} catch {
|
|
19635
20020
|
}
|
|
19636
20021
|
}
|
|
19637
20022
|
const deletedNames = consumeRosterDeletions();
|
|
19638
20023
|
const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
|
|
19639
|
-
const hash =
|
|
20024
|
+
const hash = crypto18.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
19640
20025
|
return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
|
|
19641
20026
|
}
|
|
19642
20027
|
async function cloudPushRoster(config2) {
|
|
@@ -19706,23 +20091,24 @@ async function cloudPullRoster(config2) {
|
|
|
19706
20091
|
}
|
|
19707
20092
|
}
|
|
19708
20093
|
function mergeConfig(remoteConfig, configPath) {
|
|
19709
|
-
const cfgPath = configPath ??
|
|
20094
|
+
const cfgPath = configPath ?? path39.join(EXE_AI_DIR, "config.json");
|
|
19710
20095
|
let local = {};
|
|
19711
|
-
if (
|
|
20096
|
+
if (existsSync31(cfgPath)) {
|
|
19712
20097
|
try {
|
|
19713
|
-
local = JSON.parse(
|
|
20098
|
+
local = JSON.parse(readFileSync26(cfgPath, "utf-8"));
|
|
19714
20099
|
} catch {
|
|
19715
20100
|
}
|
|
19716
20101
|
}
|
|
19717
20102
|
const merged = { ...remoteConfig, ...local };
|
|
19718
|
-
const dir =
|
|
19719
|
-
|
|
19720
|
-
|
|
20103
|
+
const dir = path39.dirname(cfgPath);
|
|
20104
|
+
ensurePrivateDirSync(dir);
|
|
20105
|
+
writeFileSync20(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
20106
|
+
enforcePrivateFileSync(cfgPath);
|
|
19721
20107
|
}
|
|
19722
20108
|
async function mergeRosterFromRemote(remote, paths) {
|
|
19723
20109
|
return withRosterLock(async () => {
|
|
19724
20110
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
19725
|
-
const identityDir = paths?.identityDir ??
|
|
20111
|
+
const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
|
|
19726
20112
|
const localEmployees = await loadEmployees(rosterPath);
|
|
19727
20113
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
19728
20114
|
let added = 0;
|
|
@@ -19743,15 +20129,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19743
20129
|
) ?? lookupKey;
|
|
19744
20130
|
const remoteIdentity = remote.identities[matchedKey];
|
|
19745
20131
|
if (remoteIdentity) {
|
|
19746
|
-
if (!
|
|
19747
|
-
const idPath =
|
|
20132
|
+
if (!existsSync31(identityDir)) mkdirSync16(identityDir, { recursive: true });
|
|
20133
|
+
const idPath = path39.join(identityDir, `${remoteEmp.name}.md`);
|
|
19748
20134
|
let localIdentity = null;
|
|
19749
20135
|
try {
|
|
19750
|
-
localIdentity =
|
|
20136
|
+
localIdentity = existsSync31(idPath) ? readFileSync26(idPath, "utf-8") : null;
|
|
19751
20137
|
} catch {
|
|
19752
20138
|
}
|
|
19753
20139
|
if (localIdentity !== remoteIdentity) {
|
|
19754
|
-
|
|
20140
|
+
writeFileSync20(idPath, remoteIdentity, "utf-8");
|
|
19755
20141
|
identitiesUpdated++;
|
|
19756
20142
|
}
|
|
19757
20143
|
}
|
|
@@ -19777,16 +20163,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19777
20163
|
}
|
|
19778
20164
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
19779
20165
|
try {
|
|
19780
|
-
const agentConfigPath =
|
|
20166
|
+
const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
|
|
19781
20167
|
let local = {};
|
|
19782
|
-
if (
|
|
20168
|
+
if (existsSync31(agentConfigPath)) {
|
|
19783
20169
|
try {
|
|
19784
|
-
local = JSON.parse(
|
|
20170
|
+
local = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
|
|
19785
20171
|
} catch {
|
|
19786
20172
|
}
|
|
19787
20173
|
}
|
|
19788
20174
|
const merged = { ...remote.agentConfig, ...local };
|
|
19789
|
-
|
|
20175
|
+
ensurePrivateDirSync(path39.dirname(agentConfigPath));
|
|
20176
|
+
writeFileSync20(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
20177
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
19790
20178
|
} catch {
|
|
19791
20179
|
}
|
|
19792
20180
|
}
|
|
@@ -21012,11 +21400,11 @@ import { z as z65 } from "zod";
|
|
|
21012
21400
|
// src/lib/people.ts
|
|
21013
21401
|
init_config();
|
|
21014
21402
|
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
21015
|
-
import { existsSync as
|
|
21016
|
-
import
|
|
21017
|
-
var PEOPLE_PATH =
|
|
21403
|
+
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "fs";
|
|
21404
|
+
import path40 from "path";
|
|
21405
|
+
var PEOPLE_PATH = path40.join(EXE_AI_DIR, "people.json");
|
|
21018
21406
|
async function loadPeople() {
|
|
21019
|
-
if (!
|
|
21407
|
+
if (!existsSync32(PEOPLE_PATH)) return [];
|
|
21020
21408
|
try {
|
|
21021
21409
|
const raw = await readFile5(PEOPLE_PATH, "utf-8");
|
|
21022
21410
|
return JSON.parse(raw);
|
|
@@ -21025,7 +21413,7 @@ async function loadPeople() {
|
|
|
21025
21413
|
}
|
|
21026
21414
|
}
|
|
21027
21415
|
async function savePeople(people) {
|
|
21028
|
-
await mkdir5(
|
|
21416
|
+
await mkdir5(path40.dirname(PEOPLE_PATH), { recursive: true });
|
|
21029
21417
|
await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
|
|
21030
21418
|
}
|
|
21031
21419
|
async function addPerson(person) {
|
|
@@ -21306,6 +21694,331 @@ function registerListEmployees(server2) {
|
|
|
21306
21694
|
);
|
|
21307
21695
|
}
|
|
21308
21696
|
|
|
21697
|
+
// src/mcp/tools/ingest-raw.ts
|
|
21698
|
+
import os16 from "os";
|
|
21699
|
+
import path41 from "path";
|
|
21700
|
+
import { createRequire as createRequire3 } from "module";
|
|
21701
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
21702
|
+
import { z as z68 } from "zod";
|
|
21703
|
+
var prismaPromise = null;
|
|
21704
|
+
function loadPrisma() {
|
|
21705
|
+
if (!prismaPromise) {
|
|
21706
|
+
prismaPromise = (async () => {
|
|
21707
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21708
|
+
if (explicitPath) {
|
|
21709
|
+
const mod2 = await import(pathToFileURL3(explicitPath).href);
|
|
21710
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21711
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21712
|
+
return new Ctor2();
|
|
21713
|
+
}
|
|
21714
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os16.homedir(), "exe-db");
|
|
21715
|
+
const req = createRequire3(path41.join(exeDbRoot, "package.json"));
|
|
21716
|
+
const entry = req.resolve("@prisma/client");
|
|
21717
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
21718
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21719
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21720
|
+
return new Ctor();
|
|
21721
|
+
})();
|
|
21722
|
+
}
|
|
21723
|
+
return prismaPromise;
|
|
21724
|
+
}
|
|
21725
|
+
function registerIngestRaw(server2) {
|
|
21726
|
+
server2.registerTool(
|
|
21727
|
+
"ingest_raw",
|
|
21728
|
+
{
|
|
21729
|
+
title: "Ingest Raw Event",
|
|
21730
|
+
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.",
|
|
21731
|
+
inputSchema: {
|
|
21732
|
+
source: z68.string().describe(
|
|
21733
|
+
'Event source identifier: "whatsapp", "shopify", "asana", "email", "external_agent", etc.'
|
|
21734
|
+
),
|
|
21735
|
+
event_type: z68.string().describe(
|
|
21736
|
+
'Event type: "message", "order", "task_update", "webhook", etc.'
|
|
21737
|
+
),
|
|
21738
|
+
payload: z68.record(z68.string(), z68.unknown()).describe(
|
|
21739
|
+
"Exact raw JSON payload \u2014 stored verbatim, never modified."
|
|
21740
|
+
),
|
|
21741
|
+
source_id: z68.string().optional().describe(
|
|
21742
|
+
"External reference ID for deduplication (e.g., WhatsApp message ID, Shopify order ID)."
|
|
21743
|
+
),
|
|
21744
|
+
metadata: z68.record(z68.string(), z68.unknown()).optional().describe(
|
|
21745
|
+
"Source-specific metadata (account info, adapter version, etc.)."
|
|
21746
|
+
)
|
|
21747
|
+
}
|
|
21748
|
+
},
|
|
21749
|
+
async ({ source, event_type, payload, source_id, metadata }) => {
|
|
21750
|
+
try {
|
|
21751
|
+
const prisma = await loadPrisma();
|
|
21752
|
+
const id = crypto.randomUUID();
|
|
21753
|
+
await prisma.$executeRawUnsafe(
|
|
21754
|
+
`INSERT INTO "raw"."raw_events" ("id", "source", "source_id", "event_type", "payload", "metadata", "timestamp")
|
|
21755
|
+
VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, NOW())
|
|
21756
|
+
ON CONFLICT ("source", "source_id", "event_type") DO NOTHING`,
|
|
21757
|
+
id,
|
|
21758
|
+
source,
|
|
21759
|
+
source_id ?? null,
|
|
21760
|
+
event_type,
|
|
21761
|
+
JSON.stringify(payload),
|
|
21762
|
+
metadata ? JSON.stringify(metadata) : null
|
|
21763
|
+
);
|
|
21764
|
+
return {
|
|
21765
|
+
content: [{
|
|
21766
|
+
type: "text",
|
|
21767
|
+
text: JSON.stringify({
|
|
21768
|
+
event_id: id,
|
|
21769
|
+
source,
|
|
21770
|
+
event_type,
|
|
21771
|
+
source_id: source_id ?? null,
|
|
21772
|
+
status: "ingested"
|
|
21773
|
+
})
|
|
21774
|
+
}]
|
|
21775
|
+
};
|
|
21776
|
+
} catch (err) {
|
|
21777
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21778
|
+
return {
|
|
21779
|
+
content: [{
|
|
21780
|
+
type: "text",
|
|
21781
|
+
text: `Error ingesting raw event: ${message}`
|
|
21782
|
+
}],
|
|
21783
|
+
isError: true
|
|
21784
|
+
};
|
|
21785
|
+
}
|
|
21786
|
+
}
|
|
21787
|
+
);
|
|
21788
|
+
}
|
|
21789
|
+
|
|
21790
|
+
// src/mcp/tools/create-license.ts
|
|
21791
|
+
init_license();
|
|
21792
|
+
import os17 from "os";
|
|
21793
|
+
import path42 from "path";
|
|
21794
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID8 } from "crypto";
|
|
21795
|
+
import { createRequire as createRequire4 } from "module";
|
|
21796
|
+
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
21797
|
+
import { z as z69 } from "zod";
|
|
21798
|
+
var prismaPromise2 = null;
|
|
21799
|
+
function loadPrisma2() {
|
|
21800
|
+
if (!prismaPromise2) {
|
|
21801
|
+
prismaPromise2 = (async () => {
|
|
21802
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21803
|
+
if (explicitPath) {
|
|
21804
|
+
const mod2 = await import(pathToFileURL4(explicitPath).href);
|
|
21805
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21806
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21807
|
+
return new Ctor2();
|
|
21808
|
+
}
|
|
21809
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path42.join(os17.homedir(), "exe-db");
|
|
21810
|
+
const req = createRequire4(path42.join(exeDbRoot, "package.json"));
|
|
21811
|
+
const entry = req.resolve("@prisma/client");
|
|
21812
|
+
const mod = await import(pathToFileURL4(entry).href);
|
|
21813
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21814
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21815
|
+
return new Ctor();
|
|
21816
|
+
})();
|
|
21817
|
+
}
|
|
21818
|
+
return prismaPromise2;
|
|
21819
|
+
}
|
|
21820
|
+
function generateKey() {
|
|
21821
|
+
return `exe_sk_${randomBytes2(16).toString("hex")}`;
|
|
21822
|
+
}
|
|
21823
|
+
function registerCreateLicense(server2) {
|
|
21824
|
+
server2.registerTool(
|
|
21825
|
+
"create_license",
|
|
21826
|
+
{
|
|
21827
|
+
title: "Create License",
|
|
21828
|
+
description: "Generate an exe_sk_* license key for a user. Stores in billing.licenses. Returns the key to give to the customer.",
|
|
21829
|
+
inputSchema: {
|
|
21830
|
+
email: z69.string().email().describe("Customer email address"),
|
|
21831
|
+
name: z69.string().optional().describe("Customer name"),
|
|
21832
|
+
plan: z69.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
|
|
21833
|
+
expires_in_days: z69.number().int().positive().default(365).describe("Days until expiration (default 365)")
|
|
21834
|
+
}
|
|
21835
|
+
},
|
|
21836
|
+
async ({ email, name, plan, expires_in_days }) => {
|
|
21837
|
+
try {
|
|
21838
|
+
const prisma = await loadPrisma2();
|
|
21839
|
+
const id = randomUUID8();
|
|
21840
|
+
const key = generateKey();
|
|
21841
|
+
const limits = PLAN_LIMITS[plan];
|
|
21842
|
+
const expiresAt = new Date(
|
|
21843
|
+
Date.now() + (expires_in_days ?? 365) * 24 * 60 * 60 * 1e3
|
|
21844
|
+
);
|
|
21845
|
+
await prisma.$executeRawUnsafe(
|
|
21846
|
+
`INSERT INTO billing.licenses (id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by)
|
|
21847
|
+
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, NOW(), $10)`,
|
|
21848
|
+
id,
|
|
21849
|
+
key,
|
|
21850
|
+
email,
|
|
21851
|
+
name ?? null,
|
|
21852
|
+
plan,
|
|
21853
|
+
limits.devices,
|
|
21854
|
+
limits.employees,
|
|
21855
|
+
limits.memories,
|
|
21856
|
+
expiresAt,
|
|
21857
|
+
"exe"
|
|
21858
|
+
);
|
|
21859
|
+
const lines = [];
|
|
21860
|
+
lines.push("## License Created\n");
|
|
21861
|
+
lines.push(`- **Key:** \`${key}\``);
|
|
21862
|
+
lines.push(`- **Email:** ${email}`);
|
|
21863
|
+
if (name) lines.push(`- **Name:** ${name}`);
|
|
21864
|
+
lines.push(`- **Plan:** ${plan}`);
|
|
21865
|
+
lines.push(`- **Expires:** ${expiresAt.toISOString().split("T")[0]}`);
|
|
21866
|
+
lines.push(`- **Devices:** ${limits.devices === -1 ? "unlimited" : limits.devices}`);
|
|
21867
|
+
lines.push(`- **Employees:** ${limits.employees === -1 ? "unlimited" : limits.employees}`);
|
|
21868
|
+
lines.push(`- **Memories:** ${limits.memories === -1 ? "unlimited" : limits.memories.toLocaleString()}`);
|
|
21869
|
+
lines.push(`
|
|
21870
|
+
Give this key to the customer. They paste it during \`exe-os setup\`.`);
|
|
21871
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21872
|
+
} catch (err) {
|
|
21873
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21874
|
+
return {
|
|
21875
|
+
content: [{ type: "text", text: `Error creating license: ${msg}` }],
|
|
21876
|
+
isError: true
|
|
21877
|
+
};
|
|
21878
|
+
}
|
|
21879
|
+
}
|
|
21880
|
+
);
|
|
21881
|
+
}
|
|
21882
|
+
|
|
21883
|
+
// src/mcp/tools/list-licenses.ts
|
|
21884
|
+
import os18 from "os";
|
|
21885
|
+
import path43 from "path";
|
|
21886
|
+
import { createRequire as createRequire5 } from "module";
|
|
21887
|
+
import { pathToFileURL as pathToFileURL5 } from "url";
|
|
21888
|
+
import { z as z70 } from "zod";
|
|
21889
|
+
var prismaPromise3 = null;
|
|
21890
|
+
function loadPrisma3() {
|
|
21891
|
+
if (!prismaPromise3) {
|
|
21892
|
+
prismaPromise3 = (async () => {
|
|
21893
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
21894
|
+
if (explicitPath) {
|
|
21895
|
+
const mod2 = await import(pathToFileURL5(explicitPath).href);
|
|
21896
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
21897
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21898
|
+
return new Ctor2();
|
|
21899
|
+
}
|
|
21900
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path43.join(os18.homedir(), "exe-db");
|
|
21901
|
+
const req = createRequire5(path43.join(exeDbRoot, "package.json"));
|
|
21902
|
+
const entry = req.resolve("@prisma/client");
|
|
21903
|
+
const mod = await import(pathToFileURL5(entry).href);
|
|
21904
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21905
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
21906
|
+
return new Ctor();
|
|
21907
|
+
})();
|
|
21908
|
+
}
|
|
21909
|
+
return prismaPromise3;
|
|
21910
|
+
}
|
|
21911
|
+
function registerListLicenses(server2) {
|
|
21912
|
+
server2.registerTool(
|
|
21913
|
+
"list_licenses",
|
|
21914
|
+
{
|
|
21915
|
+
title: "List Licenses",
|
|
21916
|
+
description: "List all issued licenses with status (active/expired/revoked). Optionally filter by plan.",
|
|
21917
|
+
inputSchema: {
|
|
21918
|
+
plan: z70.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
|
|
21919
|
+
}
|
|
21920
|
+
},
|
|
21921
|
+
async ({ plan }) => {
|
|
21922
|
+
try {
|
|
21923
|
+
const prisma = await loadPrisma3();
|
|
21924
|
+
let rows;
|
|
21925
|
+
if (plan) {
|
|
21926
|
+
rows = await prisma.$queryRawUnsafe(
|
|
21927
|
+
`SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
|
|
21928
|
+
FROM billing.licenses WHERE plan = $1 ORDER BY created_at DESC`,
|
|
21929
|
+
plan
|
|
21930
|
+
);
|
|
21931
|
+
} else {
|
|
21932
|
+
rows = await prisma.$queryRawUnsafe(
|
|
21933
|
+
`SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
|
|
21934
|
+
FROM billing.licenses ORDER BY created_at DESC`
|
|
21935
|
+
);
|
|
21936
|
+
}
|
|
21937
|
+
if (!rows || rows.length === 0) {
|
|
21938
|
+
return {
|
|
21939
|
+
content: [{ type: "text", text: "No licenses found." }]
|
|
21940
|
+
};
|
|
21941
|
+
}
|
|
21942
|
+
const lines = [];
|
|
21943
|
+
lines.push(`## Licenses (${rows.length} total)
|
|
21944
|
+
`);
|
|
21945
|
+
lines.push("| Email | Plan | Status | Key | Expires |");
|
|
21946
|
+
lines.push("|-------|------|--------|-----|---------|");
|
|
21947
|
+
for (const row of rows) {
|
|
21948
|
+
const keyShort = `${row.key.slice(0, 11)}...${row.key.slice(-4)}`;
|
|
21949
|
+
const expires = row.expires_at ? new Date(row.expires_at).toISOString().split("T")[0] : "never";
|
|
21950
|
+
const effectiveStatus = row.status === "active" && row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date() ? "expired" : row.status;
|
|
21951
|
+
lines.push(
|
|
21952
|
+
`| ${row.email} | ${row.plan} | ${effectiveStatus} | \`${keyShort}\` | ${expires} |`
|
|
21953
|
+
);
|
|
21954
|
+
}
|
|
21955
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21956
|
+
} catch (err) {
|
|
21957
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21958
|
+
return {
|
|
21959
|
+
content: [{ type: "text", text: `Error listing licenses: ${msg}` }],
|
|
21960
|
+
isError: true
|
|
21961
|
+
};
|
|
21962
|
+
}
|
|
21963
|
+
}
|
|
21964
|
+
);
|
|
21965
|
+
}
|
|
21966
|
+
|
|
21967
|
+
// src/mcp/tools/activate-license.ts
|
|
21968
|
+
init_license();
|
|
21969
|
+
import { z as z71 } from "zod";
|
|
21970
|
+
function registerActivateLicense(server2) {
|
|
21971
|
+
server2.registerTool(
|
|
21972
|
+
"activate_license",
|
|
21973
|
+
{
|
|
21974
|
+
title: "Activate License",
|
|
21975
|
+
description: "Activate an exe_sk_* license key on this device. Writes to ~/.exe-os/license.key, validates against Postgres, and caches the result.",
|
|
21976
|
+
inputSchema: {
|
|
21977
|
+
key: z71.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
|
|
21978
|
+
}
|
|
21979
|
+
},
|
|
21980
|
+
async ({ key }) => {
|
|
21981
|
+
try {
|
|
21982
|
+
saveLicense(key);
|
|
21983
|
+
const deviceId = loadDeviceId();
|
|
21984
|
+
const license = await validateLicense(key, deviceId);
|
|
21985
|
+
const lines = [];
|
|
21986
|
+
if (license.valid) {
|
|
21987
|
+
lines.push("## License Activated\n");
|
|
21988
|
+
lines.push(`- **Plan:** ${license.plan}`);
|
|
21989
|
+
lines.push(`- **Email:** ${license.email || "N/A"}`);
|
|
21990
|
+
lines.push(
|
|
21991
|
+
`- **Expires:** ${license.expiresAt ? license.expiresAt.split("T")[0] : "never"}`
|
|
21992
|
+
);
|
|
21993
|
+
lines.push(
|
|
21994
|
+
`- **Devices:** ${license.deviceLimit === -1 ? "unlimited" : license.deviceLimit}`
|
|
21995
|
+
);
|
|
21996
|
+
lines.push(
|
|
21997
|
+
`- **Employees:** ${license.employeeLimit === -1 ? "unlimited" : license.employeeLimit}`
|
|
21998
|
+
);
|
|
21999
|
+
lines.push(
|
|
22000
|
+
`- **Memories:** ${license.memoryLimit === -1 ? "unlimited" : license.memoryLimit.toLocaleString()}`
|
|
22001
|
+
);
|
|
22002
|
+
lines.push(`
|
|
22003
|
+
Key saved to ~/.exe-os/license.key. Device ID: ${deviceId}`);
|
|
22004
|
+
} else {
|
|
22005
|
+
lines.push("## Activation Failed\n");
|
|
22006
|
+
lines.push("Key was saved but could not be validated.");
|
|
22007
|
+
lines.push("Check that the key is correct and try again later.");
|
|
22008
|
+
lines.push(`Plan fell back to: ${license.plan}`);
|
|
22009
|
+
}
|
|
22010
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
22011
|
+
} catch (err) {
|
|
22012
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22013
|
+
return {
|
|
22014
|
+
content: [{ type: "text", text: `Error activating license: ${msg}` }],
|
|
22015
|
+
isError: true
|
|
22016
|
+
};
|
|
22017
|
+
}
|
|
22018
|
+
}
|
|
22019
|
+
);
|
|
22020
|
+
}
|
|
22021
|
+
|
|
21309
22022
|
// src/lib/telemetry.ts
|
|
21310
22023
|
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
21311
22024
|
var initialized = false;
|
|
@@ -21442,6 +22155,10 @@ registerListPeople(server);
|
|
|
21442
22155
|
registerGetPerson(server);
|
|
21443
22156
|
registerSetAgentConfig(server);
|
|
21444
22157
|
registerListEmployees(server);
|
|
22158
|
+
registerIngestRaw(server);
|
|
22159
|
+
registerCreateLicense(server);
|
|
22160
|
+
registerListLicenses(server);
|
|
22161
|
+
registerActivateLicense(server);
|
|
21445
22162
|
try {
|
|
21446
22163
|
await initStore();
|
|
21447
22164
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
@@ -21510,14 +22227,14 @@ try {
|
|
|
21510
22227
|
`
|
|
21511
22228
|
);
|
|
21512
22229
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
21513
|
-
const backfillPath =
|
|
21514
|
-
|
|
22230
|
+
const backfillPath = path44.resolve(
|
|
22231
|
+
path44.dirname(thisFile),
|
|
21515
22232
|
"../bin/backfill-vectors.js"
|
|
21516
22233
|
);
|
|
21517
|
-
if (
|
|
22234
|
+
if (existsSync33(backfillPath)) {
|
|
21518
22235
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
21519
|
-
const logPath =
|
|
21520
|
-
mkdirSync17(
|
|
22236
|
+
const logPath = path44.join(exeDir, "workers.log");
|
|
22237
|
+
mkdirSync17(path44.dirname(logPath), { recursive: true });
|
|
21521
22238
|
let logFd = "ignore";
|
|
21522
22239
|
try {
|
|
21523
22240
|
logFd = openSync3(logPath, "a");
|