@askexenow/exe-os 0.8.85 → 0.8.86
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/cleanup-stale-review-tasks.js +57 -19
- package/dist/bin/cli.js +507 -337
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-boot.js +344 -346
- package/dist/bin/exe-dispatch.js +375 -250
- package/dist/bin/exe-forget.js +5 -1
- package/dist/bin/exe-gateway.js +260 -135
- package/dist/bin/exe-healthcheck.js +133 -1
- package/dist/bin/exe-heartbeat.js +72 -31
- package/dist/bin/exe-link.js +25 -2
- package/dist/bin/exe-new-employee.js +22 -0
- package/dist/bin/exe-pending-messages.js +55 -17
- package/dist/bin/exe-pending-reviews.js +57 -19
- package/dist/bin/exe-search.js +6 -2
- package/dist/bin/exe-session-cleanup.js +260 -135
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +57 -19
- package/dist/bin/git-sweep.js +391 -266
- package/dist/bin/install.js +22 -0
- package/dist/bin/scan-tasks.js +394 -269
- package/dist/bin/setup.js +47 -2
- package/dist/gateway/index.js +257 -132
- package/dist/hooks/bug-report-worker.js +242 -117
- package/dist/hooks/commit-complete.js +389 -264
- package/dist/hooks/error-recall.js +6 -2
- package/dist/hooks/ingest-worker.js +314 -193
- package/dist/hooks/post-compact.js +84 -46
- package/dist/hooks/pre-compact.js +272 -147
- package/dist/hooks/pre-tool-use.js +104 -66
- package/dist/hooks/prompt-submit.js +126 -66
- package/dist/hooks/session-end.js +277 -152
- package/dist/hooks/session-start.js +70 -28
- package/dist/hooks/stop.js +90 -52
- package/dist/hooks/subagent-stop.js +84 -46
- package/dist/hooks/summary-worker.js +175 -114
- package/dist/index.js +296 -171
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +25 -2
- package/dist/lib/exe-daemon.js +338 -213
- package/dist/lib/hybrid-search.js +7 -2
- package/dist/lib/messaging.js +95 -39
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/tasks.js +242 -117
- package/dist/lib/tmux-routing.js +314 -189
- package/dist/mcp/server.js +573 -274
- package/dist/mcp/tools/create-task.js +260 -135
- package/dist/mcp/tools/list-tasks.js +68 -30
- package/dist/mcp/tools/send-message.js +100 -44
- package/dist/mcp/tools/update-task.js +123 -67
- package/dist/runtime/index.js +276 -151
- package/dist/tui/App.js +479 -354
- package/package.json +1 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -644,6 +644,57 @@ var init_provider_table = __esm({
|
|
|
644
644
|
}
|
|
645
645
|
});
|
|
646
646
|
|
|
647
|
+
// src/lib/runtime-table.ts
|
|
648
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
649
|
+
var init_runtime_table = __esm({
|
|
650
|
+
"src/lib/runtime-table.ts"() {
|
|
651
|
+
"use strict";
|
|
652
|
+
RUNTIME_TABLE = {
|
|
653
|
+
codex: {
|
|
654
|
+
binary: "codex",
|
|
655
|
+
launchMode: "exec",
|
|
656
|
+
autoApproveFlag: "--full-auto",
|
|
657
|
+
inlineFlag: "--no-alt-screen",
|
|
658
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
659
|
+
defaultModel: "gpt-5.4"
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
DEFAULT_RUNTIME = "claude";
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
// src/lib/agent-config.ts
|
|
667
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
668
|
+
import path3 from "path";
|
|
669
|
+
function loadAgentConfig() {
|
|
670
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
671
|
+
try {
|
|
672
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
673
|
+
} catch {
|
|
674
|
+
return {};
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function getAgentRuntime(agentId) {
|
|
678
|
+
const config = loadAgentConfig();
|
|
679
|
+
const entry = config[agentId];
|
|
680
|
+
if (entry) return entry;
|
|
681
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
682
|
+
}
|
|
683
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
684
|
+
var init_agent_config = __esm({
|
|
685
|
+
"src/lib/agent-config.ts"() {
|
|
686
|
+
"use strict";
|
|
687
|
+
init_config();
|
|
688
|
+
init_runtime_table();
|
|
689
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
690
|
+
DEFAULT_MODELS = {
|
|
691
|
+
claude: "claude-opus-4",
|
|
692
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
693
|
+
opencode: "minimax-m2.7"
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
|
|
647
698
|
// src/lib/intercom-queue.ts
|
|
648
699
|
var intercom_queue_exports = {};
|
|
649
700
|
__export(intercom_queue_exports, {
|
|
@@ -652,17 +703,17 @@ __export(intercom_queue_exports, {
|
|
|
652
703
|
queueIntercom: () => queueIntercom,
|
|
653
704
|
readQueue: () => readQueue
|
|
654
705
|
});
|
|
655
|
-
import { readFileSync as
|
|
656
|
-
import
|
|
706
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
707
|
+
import path4 from "path";
|
|
657
708
|
import os3 from "os";
|
|
658
709
|
function ensureDir() {
|
|
659
|
-
const dir =
|
|
660
|
-
if (!
|
|
710
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
711
|
+
if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
|
|
661
712
|
}
|
|
662
713
|
function readQueue() {
|
|
663
714
|
try {
|
|
664
|
-
if (!
|
|
665
|
-
return JSON.parse(
|
|
715
|
+
if (!existsSync4(QUEUE_PATH)) return [];
|
|
716
|
+
return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
|
|
666
717
|
} catch {
|
|
667
718
|
return [];
|
|
668
719
|
}
|
|
@@ -670,7 +721,7 @@ function readQueue() {
|
|
|
670
721
|
function writeQueue(queue) {
|
|
671
722
|
ensureDir();
|
|
672
723
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
673
|
-
|
|
724
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
674
725
|
renameSync2(tmp, QUEUE_PATH);
|
|
675
726
|
}
|
|
676
727
|
function queueIntercom(targetSession, reason) {
|
|
@@ -753,10 +804,10 @@ var QUEUE_PATH, MAX_RETRIES, TTL_MS, INTERCOM_LOG;
|
|
|
753
804
|
var init_intercom_queue = __esm({
|
|
754
805
|
"src/lib/intercom-queue.ts"() {
|
|
755
806
|
"use strict";
|
|
756
|
-
QUEUE_PATH =
|
|
807
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
757
808
|
MAX_RETRIES = 5;
|
|
758
809
|
TTL_MS = 60 * 60 * 1e3;
|
|
759
|
-
INTERCOM_LOG =
|
|
810
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
760
811
|
}
|
|
761
812
|
});
|
|
762
813
|
|
|
@@ -842,9 +893,9 @@ __export(employees_exports, {
|
|
|
842
893
|
validateEmployeeName: () => validateEmployeeName
|
|
843
894
|
});
|
|
844
895
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
845
|
-
import { existsSync as
|
|
896
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
846
897
|
import { execSync as execSync4 } from "child_process";
|
|
847
|
-
import
|
|
898
|
+
import path5 from "path";
|
|
848
899
|
import os4 from "os";
|
|
849
900
|
function normalizeRole(role) {
|
|
850
901
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -881,7 +932,7 @@ function validateEmployeeName(name) {
|
|
|
881
932
|
return { valid: true };
|
|
882
933
|
}
|
|
883
934
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
884
|
-
if (!
|
|
935
|
+
if (!existsSync5(employeesPath)) {
|
|
885
936
|
return [];
|
|
886
937
|
}
|
|
887
938
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -892,13 +943,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
892
943
|
}
|
|
893
944
|
}
|
|
894
945
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
895
|
-
await mkdir2(
|
|
946
|
+
await mkdir2(path5.dirname(employeesPath), { recursive: true });
|
|
896
947
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
897
948
|
}
|
|
898
949
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
899
|
-
if (!
|
|
950
|
+
if (!existsSync5(employeesPath)) return [];
|
|
900
951
|
try {
|
|
901
|
-
return JSON.parse(
|
|
952
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
902
953
|
} catch {
|
|
903
954
|
return [];
|
|
904
955
|
}
|
|
@@ -949,14 +1000,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
949
1000
|
emp.name = emp.name.toLowerCase();
|
|
950
1001
|
changed = true;
|
|
951
1002
|
try {
|
|
952
|
-
const identityDir =
|
|
953
|
-
const oldPath =
|
|
954
|
-
const newPath =
|
|
955
|
-
if (
|
|
1003
|
+
const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
|
|
1004
|
+
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
1005
|
+
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
1006
|
+
if (existsSync5(oldPath) && !existsSync5(newPath)) {
|
|
956
1007
|
renameSync3(oldPath, newPath);
|
|
957
|
-
} else if (
|
|
958
|
-
const content =
|
|
959
|
-
|
|
1008
|
+
} else if (existsSync5(oldPath) && oldPath !== newPath) {
|
|
1009
|
+
const content = readFileSync5(oldPath, "utf-8");
|
|
1010
|
+
writeFileSync4(newPath, content, "utf-8");
|
|
960
1011
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
961
1012
|
unlinkSync(oldPath);
|
|
962
1013
|
}
|
|
@@ -986,7 +1037,7 @@ function registerBinSymlinks(name) {
|
|
|
986
1037
|
errors.push("Could not find 'exe-os' in PATH");
|
|
987
1038
|
return { created, skipped, errors };
|
|
988
1039
|
}
|
|
989
|
-
const binDir =
|
|
1040
|
+
const binDir = path5.dirname(exeBinPath);
|
|
990
1041
|
let target;
|
|
991
1042
|
try {
|
|
992
1043
|
target = readlinkSync(exeBinPath);
|
|
@@ -996,8 +1047,8 @@ function registerBinSymlinks(name) {
|
|
|
996
1047
|
}
|
|
997
1048
|
for (const suffix of ["", "-opencode"]) {
|
|
998
1049
|
const linkName = `${name}${suffix}`;
|
|
999
|
-
const linkPath =
|
|
1000
|
-
if (
|
|
1050
|
+
const linkPath = path5.join(binDir, linkName);
|
|
1051
|
+
if (existsSync5(linkPath)) {
|
|
1001
1052
|
skipped.push(linkName);
|
|
1002
1053
|
continue;
|
|
1003
1054
|
}
|
|
@@ -1015,7 +1066,7 @@ var init_employees = __esm({
|
|
|
1015
1066
|
"src/lib/employees.ts"() {
|
|
1016
1067
|
"use strict";
|
|
1017
1068
|
init_config();
|
|
1018
|
-
EMPLOYEES_PATH =
|
|
1069
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
1019
1070
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1020
1071
|
COORDINATOR_ROLE = "COO";
|
|
1021
1072
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
@@ -1026,8 +1077,8 @@ var init_employees = __esm({
|
|
|
1026
1077
|
import net from "net";
|
|
1027
1078
|
import { spawn } from "child_process";
|
|
1028
1079
|
import { randomUUID } from "crypto";
|
|
1029
|
-
import { existsSync as
|
|
1030
|
-
import
|
|
1080
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
1081
|
+
import path6 from "path";
|
|
1031
1082
|
import { fileURLToPath } from "url";
|
|
1032
1083
|
function handleData(chunk) {
|
|
1033
1084
|
_buffer += chunk.toString();
|
|
@@ -1055,9 +1106,9 @@ function handleData(chunk) {
|
|
|
1055
1106
|
}
|
|
1056
1107
|
}
|
|
1057
1108
|
function cleanupStaleFiles() {
|
|
1058
|
-
if (
|
|
1109
|
+
if (existsSync6(PID_PATH)) {
|
|
1059
1110
|
try {
|
|
1060
|
-
const pid = parseInt(
|
|
1111
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
1061
1112
|
if (pid > 0) {
|
|
1062
1113
|
try {
|
|
1063
1114
|
process.kill(pid, 0);
|
|
@@ -1078,11 +1129,11 @@ function cleanupStaleFiles() {
|
|
|
1078
1129
|
}
|
|
1079
1130
|
}
|
|
1080
1131
|
function findPackageRoot() {
|
|
1081
|
-
let dir =
|
|
1082
|
-
const { root } =
|
|
1132
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1133
|
+
const { root } = path6.parse(dir);
|
|
1083
1134
|
while (dir !== root) {
|
|
1084
|
-
if (
|
|
1085
|
-
dir =
|
|
1135
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1136
|
+
dir = path6.dirname(dir);
|
|
1086
1137
|
}
|
|
1087
1138
|
return null;
|
|
1088
1139
|
}
|
|
@@ -1092,8 +1143,8 @@ function spawnDaemon() {
|
|
|
1092
1143
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1093
1144
|
return;
|
|
1094
1145
|
}
|
|
1095
|
-
const daemonPath =
|
|
1096
|
-
if (!
|
|
1146
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1147
|
+
if (!existsSync6(daemonPath)) {
|
|
1097
1148
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1098
1149
|
`);
|
|
1099
1150
|
return;
|
|
@@ -1101,7 +1152,7 @@ function spawnDaemon() {
|
|
|
1101
1152
|
const resolvedPath = daemonPath;
|
|
1102
1153
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1103
1154
|
`);
|
|
1104
|
-
const logPath =
|
|
1155
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1105
1156
|
let stderrFd = "ignore";
|
|
1106
1157
|
try {
|
|
1107
1158
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1251,9 +1302,9 @@ async function pingDaemon() {
|
|
|
1251
1302
|
}
|
|
1252
1303
|
function killAndRespawnDaemon() {
|
|
1253
1304
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1254
|
-
if (
|
|
1305
|
+
if (existsSync6(PID_PATH)) {
|
|
1255
1306
|
try {
|
|
1256
|
-
const pid = parseInt(
|
|
1307
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
1257
1308
|
if (pid > 0) {
|
|
1258
1309
|
try {
|
|
1259
1310
|
process.kill(pid, "SIGKILL");
|
|
@@ -1340,9 +1391,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1340
1391
|
"src/lib/exe-daemon-client.ts"() {
|
|
1341
1392
|
"use strict";
|
|
1342
1393
|
init_config();
|
|
1343
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1344
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1345
|
-
SPAWN_LOCK_PATH =
|
|
1394
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1395
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1396
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1346
1397
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1347
1398
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1348
1399
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2505,18 +2556,18 @@ var init_database = __esm({
|
|
|
2505
2556
|
});
|
|
2506
2557
|
|
|
2507
2558
|
// src/lib/license.ts
|
|
2508
|
-
import { readFileSync as
|
|
2559
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2509
2560
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2510
|
-
import
|
|
2561
|
+
import path7 from "path";
|
|
2511
2562
|
import { jwtVerify, importSPKI } from "jose";
|
|
2512
2563
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2513
2564
|
var init_license = __esm({
|
|
2514
2565
|
"src/lib/license.ts"() {
|
|
2515
2566
|
"use strict";
|
|
2516
2567
|
init_config();
|
|
2517
|
-
LICENSE_PATH =
|
|
2518
|
-
CACHE_PATH =
|
|
2519
|
-
DEVICE_ID_PATH =
|
|
2568
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2569
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2570
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2520
2571
|
PLAN_LIMITS = {
|
|
2521
2572
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2522
2573
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2528,12 +2579,12 @@ var init_license = __esm({
|
|
|
2528
2579
|
});
|
|
2529
2580
|
|
|
2530
2581
|
// src/lib/plan-limits.ts
|
|
2531
|
-
import { readFileSync as
|
|
2532
|
-
import
|
|
2582
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2583
|
+
import path8 from "path";
|
|
2533
2584
|
function getLicenseSync() {
|
|
2534
2585
|
try {
|
|
2535
|
-
if (!
|
|
2536
|
-
const raw = JSON.parse(
|
|
2586
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2587
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
2537
2588
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2538
2589
|
const parts = raw.token.split(".");
|
|
2539
2590
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2571,8 +2622,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2571
2622
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2572
2623
|
let count = 0;
|
|
2573
2624
|
try {
|
|
2574
|
-
if (
|
|
2575
|
-
const raw =
|
|
2625
|
+
if (existsSync8(filePath)) {
|
|
2626
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
2576
2627
|
const employees = JSON.parse(raw);
|
|
2577
2628
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2578
2629
|
}
|
|
@@ -2601,19 +2652,19 @@ var init_plan_limits = __esm({
|
|
|
2601
2652
|
this.name = "PlanLimitError";
|
|
2602
2653
|
}
|
|
2603
2654
|
};
|
|
2604
|
-
CACHE_PATH2 =
|
|
2655
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2605
2656
|
}
|
|
2606
2657
|
});
|
|
2607
2658
|
|
|
2608
2659
|
// src/lib/notifications.ts
|
|
2609
2660
|
import crypto from "crypto";
|
|
2610
|
-
import
|
|
2661
|
+
import path9 from "path";
|
|
2611
2662
|
import os5 from "os";
|
|
2612
2663
|
import {
|
|
2613
|
-
readFileSync as
|
|
2664
|
+
readFileSync as readFileSync9,
|
|
2614
2665
|
readdirSync,
|
|
2615
2666
|
unlinkSync as unlinkSync3,
|
|
2616
|
-
existsSync as
|
|
2667
|
+
existsSync as existsSync9,
|
|
2617
2668
|
rmdirSync
|
|
2618
2669
|
} from "fs";
|
|
2619
2670
|
async function writeNotification(notification) {
|
|
@@ -2847,11 +2898,11 @@ var init_state_bus = __esm({
|
|
|
2847
2898
|
|
|
2848
2899
|
// src/lib/tasks-crud.ts
|
|
2849
2900
|
import crypto3 from "crypto";
|
|
2850
|
-
import
|
|
2901
|
+
import path10 from "path";
|
|
2851
2902
|
import os6 from "os";
|
|
2852
2903
|
import { execSync as execSync5 } from "child_process";
|
|
2853
2904
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2854
|
-
import { existsSync as
|
|
2905
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2855
2906
|
async function writeCheckpoint(input) {
|
|
2856
2907
|
const client = getClient();
|
|
2857
2908
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3026,8 +3077,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3026
3077
|
}
|
|
3027
3078
|
if (input.baseDir) {
|
|
3028
3079
|
try {
|
|
3029
|
-
await mkdir3(
|
|
3030
|
-
await mkdir3(
|
|
3080
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3081
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3031
3082
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3032
3083
|
await ensureGitignoreExe(input.baseDir);
|
|
3033
3084
|
} catch {
|
|
@@ -3063,10 +3114,10 @@ ${laneWarning}` : laneWarning;
|
|
|
3063
3114
|
});
|
|
3064
3115
|
if (input.baseDir) {
|
|
3065
3116
|
try {
|
|
3066
|
-
const EXE_OS_DIR =
|
|
3067
|
-
const mdPath =
|
|
3068
|
-
const mdDir =
|
|
3069
|
-
if (!
|
|
3117
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
3118
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
3119
|
+
const mdDir = path10.dirname(mdPath);
|
|
3120
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3070
3121
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3071
3122
|
const mdContent = `# ${input.title}
|
|
3072
3123
|
|
|
@@ -3091,7 +3142,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
3091
3142
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3092
3143
|
`;
|
|
3093
3144
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3094
|
-
} catch {
|
|
3145
|
+
} catch (err) {
|
|
3146
|
+
process.stderr.write(
|
|
3147
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3148
|
+
`
|
|
3149
|
+
);
|
|
3095
3150
|
}
|
|
3096
3151
|
}
|
|
3097
3152
|
return {
|
|
@@ -3351,9 +3406,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3351
3406
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3352
3407
|
}
|
|
3353
3408
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3354
|
-
const archPath =
|
|
3409
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3355
3410
|
try {
|
|
3356
|
-
if (
|
|
3411
|
+
if (existsSync10(archPath)) return;
|
|
3357
3412
|
const template = [
|
|
3358
3413
|
`# ${projectName} \u2014 System Architecture`,
|
|
3359
3414
|
"",
|
|
@@ -3386,10 +3441,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3386
3441
|
}
|
|
3387
3442
|
}
|
|
3388
3443
|
async function ensureGitignoreExe(baseDir) {
|
|
3389
|
-
const gitignorePath =
|
|
3444
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
3390
3445
|
try {
|
|
3391
|
-
if (
|
|
3392
|
-
const content =
|
|
3446
|
+
if (existsSync10(gitignorePath)) {
|
|
3447
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
3393
3448
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3394
3449
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3395
3450
|
} else {
|
|
@@ -3430,8 +3485,8 @@ __export(tasks_review_exports, {
|
|
|
3430
3485
|
getReviewChecklist: () => getReviewChecklist,
|
|
3431
3486
|
listPendingReviews: () => listPendingReviews
|
|
3432
3487
|
});
|
|
3433
|
-
import
|
|
3434
|
-
import { existsSync as
|
|
3488
|
+
import path11 from "path";
|
|
3489
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3435
3490
|
async function countPendingReviews(sessionScope) {
|
|
3436
3491
|
const client = getClient();
|
|
3437
3492
|
if (sessionScope) {
|
|
@@ -3701,11 +3756,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3701
3756
|
);
|
|
3702
3757
|
}
|
|
3703
3758
|
try {
|
|
3704
|
-
const cacheDir =
|
|
3705
|
-
if (
|
|
3759
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3760
|
+
if (existsSync11(cacheDir)) {
|
|
3706
3761
|
for (const f of readdirSync2(cacheDir)) {
|
|
3707
3762
|
if (f.startsWith("review-notified-")) {
|
|
3708
|
-
unlinkSync4(
|
|
3763
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3709
3764
|
}
|
|
3710
3765
|
}
|
|
3711
3766
|
}
|
|
@@ -3726,7 +3781,7 @@ var init_tasks_review = __esm({
|
|
|
3726
3781
|
});
|
|
3727
3782
|
|
|
3728
3783
|
// src/lib/tasks-chain.ts
|
|
3729
|
-
import
|
|
3784
|
+
import path12 from "path";
|
|
3730
3785
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3731
3786
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3732
3787
|
const client = getClient();
|
|
@@ -3743,7 +3798,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3743
3798
|
});
|
|
3744
3799
|
for (const ur of unblockedRows.rows) {
|
|
3745
3800
|
try {
|
|
3746
|
-
const ubFile =
|
|
3801
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3747
3802
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3748
3803
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3749
3804
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3812,7 +3867,7 @@ var init_tasks_chain = __esm({
|
|
|
3812
3867
|
|
|
3813
3868
|
// src/lib/project-name.ts
|
|
3814
3869
|
import { execSync as execSync6 } from "child_process";
|
|
3815
|
-
import
|
|
3870
|
+
import path13 from "path";
|
|
3816
3871
|
function getProjectName(cwd) {
|
|
3817
3872
|
const dir = cwd ?? process.cwd();
|
|
3818
3873
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3825,7 +3880,7 @@ function getProjectName(cwd) {
|
|
|
3825
3880
|
timeout: 2e3,
|
|
3826
3881
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3827
3882
|
}).trim();
|
|
3828
|
-
repoRoot =
|
|
3883
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3829
3884
|
} catch {
|
|
3830
3885
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3831
3886
|
cwd: dir,
|
|
@@ -3834,11 +3889,11 @@ function getProjectName(cwd) {
|
|
|
3834
3889
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3835
3890
|
}).trim();
|
|
3836
3891
|
}
|
|
3837
|
-
_cached2 =
|
|
3892
|
+
_cached2 = path13.basename(repoRoot);
|
|
3838
3893
|
_cachedCwd = dir;
|
|
3839
3894
|
return _cached2;
|
|
3840
3895
|
} catch {
|
|
3841
|
-
_cached2 =
|
|
3896
|
+
_cached2 = path13.basename(dir);
|
|
3842
3897
|
_cachedCwd = dir;
|
|
3843
3898
|
return _cached2;
|
|
3844
3899
|
}
|
|
@@ -4311,8 +4366,8 @@ __export(tasks_exports, {
|
|
|
4311
4366
|
updateTaskStatus: () => updateTaskStatus,
|
|
4312
4367
|
writeCheckpoint: () => writeCheckpoint
|
|
4313
4368
|
});
|
|
4314
|
-
import
|
|
4315
|
-
import { writeFileSync as
|
|
4369
|
+
import path14 from "path";
|
|
4370
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4316
4371
|
async function createTask(input) {
|
|
4317
4372
|
const result = await createTaskCore(input);
|
|
4318
4373
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4331,11 +4386,11 @@ async function updateTask(input) {
|
|
|
4331
4386
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4332
4387
|
try {
|
|
4333
4388
|
const agent = String(row.assigned_to);
|
|
4334
|
-
const cacheDir =
|
|
4335
|
-
const cachePath =
|
|
4389
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4390
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
4336
4391
|
if (input.status === "in_progress") {
|
|
4337
|
-
|
|
4338
|
-
|
|
4392
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
4393
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4339
4394
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
4340
4395
|
try {
|
|
4341
4396
|
unlinkSync5(cachePath);
|
|
@@ -4802,13 +4857,13 @@ __export(tmux_routing_exports, {
|
|
|
4802
4857
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4803
4858
|
});
|
|
4804
4859
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4805
|
-
import { readFileSync as
|
|
4806
|
-
import
|
|
4860
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4861
|
+
import path15 from "path";
|
|
4807
4862
|
import os7 from "os";
|
|
4808
4863
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4809
4864
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4810
4865
|
function spawnLockPath(sessionName) {
|
|
4811
|
-
return
|
|
4866
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4812
4867
|
}
|
|
4813
4868
|
function isProcessAlive(pid) {
|
|
4814
4869
|
try {
|
|
@@ -4819,13 +4874,13 @@ function isProcessAlive(pid) {
|
|
|
4819
4874
|
}
|
|
4820
4875
|
}
|
|
4821
4876
|
function acquireSpawnLock2(sessionName) {
|
|
4822
|
-
if (!
|
|
4823
|
-
|
|
4877
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4878
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4824
4879
|
}
|
|
4825
4880
|
const lockFile = spawnLockPath(sessionName);
|
|
4826
|
-
if (
|
|
4881
|
+
if (existsSync12(lockFile)) {
|
|
4827
4882
|
try {
|
|
4828
|
-
const lock = JSON.parse(
|
|
4883
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4829
4884
|
const age = Date.now() - lock.timestamp;
|
|
4830
4885
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4831
4886
|
return false;
|
|
@@ -4833,7 +4888,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4833
4888
|
} catch {
|
|
4834
4889
|
}
|
|
4835
4890
|
}
|
|
4836
|
-
|
|
4891
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4837
4892
|
return true;
|
|
4838
4893
|
}
|
|
4839
4894
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4845,13 +4900,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4845
4900
|
function resolveBehaviorsExporterScript() {
|
|
4846
4901
|
try {
|
|
4847
4902
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4848
|
-
const scriptPath =
|
|
4849
|
-
|
|
4903
|
+
const scriptPath = path15.join(
|
|
4904
|
+
path15.dirname(thisFile),
|
|
4850
4905
|
"..",
|
|
4851
4906
|
"bin",
|
|
4852
4907
|
"exe-export-behaviors.js"
|
|
4853
4908
|
);
|
|
4854
|
-
return
|
|
4909
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4855
4910
|
} catch {
|
|
4856
4911
|
return null;
|
|
4857
4912
|
}
|
|
@@ -4917,12 +4972,12 @@ function extractRootExe(name) {
|
|
|
4917
4972
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4918
4973
|
}
|
|
4919
4974
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4920
|
-
if (!
|
|
4921
|
-
|
|
4975
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4976
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4922
4977
|
}
|
|
4923
4978
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4924
|
-
const filePath =
|
|
4925
|
-
|
|
4979
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4980
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4926
4981
|
parentExe: rootExe,
|
|
4927
4982
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4928
4983
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4930,7 +4985,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4930
4985
|
}
|
|
4931
4986
|
function getParentExe(sessionKey) {
|
|
4932
4987
|
try {
|
|
4933
|
-
const data = JSON.parse(
|
|
4988
|
+
const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4934
4989
|
return data.parentExe || null;
|
|
4935
4990
|
} catch {
|
|
4936
4991
|
return null;
|
|
@@ -4938,8 +4993,8 @@ function getParentExe(sessionKey) {
|
|
|
4938
4993
|
}
|
|
4939
4994
|
function getDispatchedBy(sessionKey) {
|
|
4940
4995
|
try {
|
|
4941
|
-
const data = JSON.parse(
|
|
4942
|
-
|
|
4996
|
+
const data = JSON.parse(readFileSync11(
|
|
4997
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4943
4998
|
"utf8"
|
|
4944
4999
|
));
|
|
4945
5000
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5000,32 +5055,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5000
5055
|
}
|
|
5001
5056
|
function readDebounceState() {
|
|
5002
5057
|
try {
|
|
5003
|
-
if (!
|
|
5004
|
-
|
|
5058
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
5059
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
5060
|
+
const state = {};
|
|
5061
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5062
|
+
if (typeof val === "number") {
|
|
5063
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5064
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5065
|
+
state[key] = val;
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
return state;
|
|
5005
5069
|
} catch {
|
|
5006
5070
|
return {};
|
|
5007
5071
|
}
|
|
5008
5072
|
}
|
|
5009
5073
|
function writeDebounceState(state) {
|
|
5010
5074
|
try {
|
|
5011
|
-
if (!
|
|
5012
|
-
|
|
5075
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5076
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5013
5077
|
} catch {
|
|
5014
5078
|
}
|
|
5015
5079
|
}
|
|
5016
5080
|
function isDebounced(targetSession) {
|
|
5017
5081
|
const state = readDebounceState();
|
|
5018
|
-
const
|
|
5019
|
-
|
|
5082
|
+
const entry = state[targetSession];
|
|
5083
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5084
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5085
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5086
|
+
state[targetSession].pending++;
|
|
5087
|
+
writeDebounceState(state);
|
|
5088
|
+
return true;
|
|
5089
|
+
}
|
|
5090
|
+
return false;
|
|
5020
5091
|
}
|
|
5021
5092
|
function recordDebounce(targetSession) {
|
|
5022
5093
|
const state = readDebounceState();
|
|
5023
|
-
state[targetSession]
|
|
5094
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5095
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
5024
5096
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
5025
5097
|
for (const key of Object.keys(state)) {
|
|
5026
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5098
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
5027
5099
|
}
|
|
5028
5100
|
writeDebounceState(state);
|
|
5101
|
+
return batched;
|
|
5029
5102
|
}
|
|
5030
5103
|
function logIntercom(msg) {
|
|
5031
5104
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -5070,7 +5143,7 @@ function sendIntercom(targetSession) {
|
|
|
5070
5143
|
return "skipped_exe";
|
|
5071
5144
|
}
|
|
5072
5145
|
if (isDebounced(targetSession)) {
|
|
5073
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5146
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
5074
5147
|
return "debounced";
|
|
5075
5148
|
}
|
|
5076
5149
|
try {
|
|
@@ -5082,14 +5155,14 @@ function sendIntercom(targetSession) {
|
|
|
5082
5155
|
const sessionState = getSessionState(targetSession);
|
|
5083
5156
|
if (sessionState === "no_claude") {
|
|
5084
5157
|
queueIntercom(targetSession, "claude not running in session");
|
|
5085
|
-
recordDebounce(targetSession);
|
|
5086
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5158
|
+
const batched2 = recordDebounce(targetSession);
|
|
5159
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5087
5160
|
return "queued";
|
|
5088
5161
|
}
|
|
5089
5162
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
5090
5163
|
queueIntercom(targetSession, "session busy at send time");
|
|
5091
|
-
recordDebounce(targetSession);
|
|
5092
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5164
|
+
const batched2 = recordDebounce(targetSession);
|
|
5165
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5093
5166
|
return "queued";
|
|
5094
5167
|
}
|
|
5095
5168
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -5097,8 +5170,8 @@ function sendIntercom(targetSession) {
|
|
|
5097
5170
|
transport.sendKeys(targetSession, "q");
|
|
5098
5171
|
}
|
|
5099
5172
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
5100
|
-
recordDebounce(targetSession);
|
|
5101
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5173
|
+
const batched = recordDebounce(targetSession);
|
|
5174
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
5102
5175
|
return "delivered";
|
|
5103
5176
|
} catch {
|
|
5104
5177
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -5200,26 +5273,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5200
5273
|
const transport = getTransport();
|
|
5201
5274
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5202
5275
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5203
|
-
const logDir =
|
|
5204
|
-
const logFile =
|
|
5205
|
-
if (!
|
|
5206
|
-
|
|
5276
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
5277
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5278
|
+
if (!existsSync12(logDir)) {
|
|
5279
|
+
mkdirSync6(logDir, { recursive: true });
|
|
5207
5280
|
}
|
|
5208
5281
|
transport.kill(sessionName);
|
|
5209
5282
|
let cleanupSuffix = "";
|
|
5210
5283
|
try {
|
|
5211
5284
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5212
|
-
const cleanupScript =
|
|
5213
|
-
if (
|
|
5285
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5286
|
+
if (existsSync12(cleanupScript)) {
|
|
5214
5287
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5215
5288
|
}
|
|
5216
5289
|
} catch {
|
|
5217
5290
|
}
|
|
5218
5291
|
try {
|
|
5219
|
-
const claudeJsonPath =
|
|
5292
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
5220
5293
|
let claudeJson = {};
|
|
5221
5294
|
try {
|
|
5222
|
-
claudeJson = JSON.parse(
|
|
5295
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
5223
5296
|
} catch {
|
|
5224
5297
|
}
|
|
5225
5298
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5227,17 +5300,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5227
5300
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5228
5301
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5229
5302
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5230
|
-
|
|
5303
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5231
5304
|
} catch {
|
|
5232
5305
|
}
|
|
5233
5306
|
try {
|
|
5234
|
-
const settingsDir =
|
|
5307
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
5235
5308
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5236
|
-
const projSettingsDir =
|
|
5237
|
-
const settingsPath =
|
|
5309
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
5310
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
5238
5311
|
let settings = {};
|
|
5239
5312
|
try {
|
|
5240
|
-
settings = JSON.parse(
|
|
5313
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
5241
5314
|
} catch {
|
|
5242
5315
|
}
|
|
5243
5316
|
const perms = settings.permissions ?? {};
|
|
@@ -5265,20 +5338,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5265
5338
|
if (changed) {
|
|
5266
5339
|
perms.allow = allow;
|
|
5267
5340
|
settings.permissions = perms;
|
|
5268
|
-
|
|
5269
|
-
|
|
5341
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
5342
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5270
5343
|
}
|
|
5271
5344
|
} catch {
|
|
5272
5345
|
}
|
|
5273
5346
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
5274
5347
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
5275
|
-
const
|
|
5348
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
5349
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
5350
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
5351
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
5276
5352
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
5277
5353
|
let identityFlag = "";
|
|
5278
5354
|
let behaviorsFlag = "";
|
|
5279
5355
|
let legacyFallbackWarned = false;
|
|
5280
5356
|
if (!useExeAgent && !useBinSymlink) {
|
|
5281
|
-
const identityPath =
|
|
5357
|
+
const identityPath = path15.join(
|
|
5282
5358
|
os7.homedir(),
|
|
5283
5359
|
".exe-os",
|
|
5284
5360
|
"identity",
|
|
@@ -5288,13 +5364,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5288
5364
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5289
5365
|
if (hasAgentFlag) {
|
|
5290
5366
|
identityFlag = ` --agent ${employeeName}`;
|
|
5291
|
-
} else if (
|
|
5367
|
+
} else if (existsSync12(identityPath)) {
|
|
5292
5368
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5293
5369
|
legacyFallbackWarned = true;
|
|
5294
5370
|
}
|
|
5295
5371
|
const behaviorsFile = exportBehaviorsSync(
|
|
5296
5372
|
employeeName,
|
|
5297
|
-
|
|
5373
|
+
path15.basename(spawnCwd),
|
|
5298
5374
|
sessionName
|
|
5299
5375
|
);
|
|
5300
5376
|
if (behaviorsFile) {
|
|
@@ -5309,16 +5385,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5309
5385
|
}
|
|
5310
5386
|
let sessionContextFlag = "";
|
|
5311
5387
|
try {
|
|
5312
|
-
const ctxDir =
|
|
5313
|
-
|
|
5314
|
-
const ctxFile =
|
|
5388
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
5389
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
5390
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5315
5391
|
const ctxContent = [
|
|
5316
5392
|
`## Session Context`,
|
|
5317
5393
|
`You are running in tmux session: ${sessionName}.`,
|
|
5318
5394
|
`Your parent coordinator session is ${exeSession}.`,
|
|
5319
5395
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
5320
5396
|
].join("\n");
|
|
5321
|
-
|
|
5397
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
5322
5398
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5323
5399
|
} catch {
|
|
5324
5400
|
}
|
|
@@ -5332,9 +5408,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5332
5408
|
}
|
|
5333
5409
|
}
|
|
5334
5410
|
}
|
|
5411
|
+
if (useCodex) {
|
|
5412
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
5413
|
+
if (codexCfg?.apiKeyEnv) {
|
|
5414
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
5415
|
+
if (keyVal) {
|
|
5416
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
5420
|
+
}
|
|
5421
|
+
if (useOpencode) {
|
|
5422
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
5423
|
+
if (ocCfg?.apiKeyEnv) {
|
|
5424
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
5425
|
+
if (keyVal) {
|
|
5426
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5430
|
+
}
|
|
5431
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
5432
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
5433
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
5434
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5335
5437
|
let spawnCommand;
|
|
5336
5438
|
if (useExeAgent) {
|
|
5337
5439
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
5440
|
+
} else if (useCodex) {
|
|
5441
|
+
process.stderr.write(
|
|
5442
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
5443
|
+
`
|
|
5444
|
+
);
|
|
5445
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
5446
|
+
} else if (useOpencode) {
|
|
5447
|
+
const binName = `${employeeName}-opencode`;
|
|
5448
|
+
process.stderr.write(
|
|
5449
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
5450
|
+
`
|
|
5451
|
+
);
|
|
5452
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
5338
5453
|
} else if (useBinSymlink) {
|
|
5339
5454
|
const binName = `${employeeName}-${ccProvider}`;
|
|
5340
5455
|
process.stderr.write(
|
|
@@ -5356,11 +5471,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5356
5471
|
transport.pipeLog(sessionName, logFile);
|
|
5357
5472
|
try {
|
|
5358
5473
|
const mySession = getMySession();
|
|
5359
|
-
const dispatchInfo =
|
|
5360
|
-
|
|
5474
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5475
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5361
5476
|
dispatchedBy: mySession,
|
|
5362
5477
|
rootExe: exeSession,
|
|
5363
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
5478
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
5479
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
5480
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
5364
5481
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5365
5482
|
}));
|
|
5366
5483
|
} catch {
|
|
@@ -5378,6 +5495,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5378
5495
|
booted = true;
|
|
5379
5496
|
break;
|
|
5380
5497
|
}
|
|
5498
|
+
} else if (useCodex) {
|
|
5499
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
5500
|
+
booted = true;
|
|
5501
|
+
break;
|
|
5502
|
+
}
|
|
5381
5503
|
} else {
|
|
5382
5504
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
5383
5505
|
booted = true;
|
|
@@ -5389,9 +5511,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5389
5511
|
}
|
|
5390
5512
|
if (!booted) {
|
|
5391
5513
|
releaseSpawnLock2(sessionName);
|
|
5392
|
-
|
|
5514
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5515
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
5393
5516
|
}
|
|
5394
|
-
if (!useExeAgent) {
|
|
5517
|
+
if (!useExeAgent && !useCodex) {
|
|
5395
5518
|
try {
|
|
5396
5519
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
5397
5520
|
} catch {
|
|
@@ -5418,17 +5541,19 @@ var init_tmux_routing = __esm({
|
|
|
5418
5541
|
init_cc_agent_support();
|
|
5419
5542
|
init_mcp_prefix();
|
|
5420
5543
|
init_provider_table();
|
|
5544
|
+
init_agent_config();
|
|
5545
|
+
init_runtime_table();
|
|
5421
5546
|
init_intercom_queue();
|
|
5422
5547
|
init_plan_limits();
|
|
5423
5548
|
init_employees();
|
|
5424
|
-
SPAWN_LOCK_DIR =
|
|
5425
|
-
SESSION_CACHE =
|
|
5549
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
5550
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
5426
5551
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5427
5552
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5428
5553
|
VERIFY_PANE_LINES = 200;
|
|
5429
5554
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5430
|
-
INTERCOM_LOG2 =
|
|
5431
|
-
DEBOUNCE_FILE =
|
|
5555
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
5556
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5432
5557
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5433
5558
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
5434
5559
|
}
|
|
@@ -5712,9 +5837,9 @@ __export(agent_signals_exports, {
|
|
|
5712
5837
|
hasOpenTasks: () => hasOpenTasks,
|
|
5713
5838
|
hasUnreadInbox: () => hasUnreadInbox
|
|
5714
5839
|
});
|
|
5715
|
-
import { readFileSync as
|
|
5840
|
+
import { readFileSync as readFileSync12, existsSync as existsSync13 } from "fs";
|
|
5716
5841
|
import os8 from "os";
|
|
5717
|
-
import
|
|
5842
|
+
import path16 from "path";
|
|
5718
5843
|
async function hasOpenTasks(client, agentId) {
|
|
5719
5844
|
try {
|
|
5720
5845
|
const scope = sessionScopeFilter(null);
|
|
@@ -5756,10 +5881,10 @@ async function hasUnreadInbox(client, agentId) {
|
|
|
5756
5881
|
return CONSERVATIVE_ON_ERROR;
|
|
5757
5882
|
}
|
|
5758
5883
|
}
|
|
5759
|
-
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog =
|
|
5760
|
-
if (!
|
|
5884
|
+
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os8.homedir(), ".exe-os", "intercom.log")) {
|
|
5885
|
+
if (!existsSync13(intercomLog)) return false;
|
|
5761
5886
|
try {
|
|
5762
|
-
const raw =
|
|
5887
|
+
const raw = readFileSync12(intercomLog, "utf8");
|
|
5763
5888
|
const lines = raw.split("\n");
|
|
5764
5889
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5765
5890
|
const line = lines[i];
|
|
@@ -5832,7 +5957,7 @@ __export(daemon_orchestration_exports, {
|
|
|
5832
5957
|
shouldNudgeEmployee: () => shouldNudgeEmployee
|
|
5833
5958
|
});
|
|
5834
5959
|
import { execSync as execSync9 } from "child_process";
|
|
5835
|
-
import { existsSync as
|
|
5960
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
5836
5961
|
import { homedir } from "os";
|
|
5837
5962
|
import { join } from "path";
|
|
5838
5963
|
function shouldNudgeEmployee(sessionState, hasOpenTasks2, lastNudgeMs, nowMs, dedupMs) {
|
|
@@ -6023,8 +6148,8 @@ async function pollReviewNudge(deps, state) {
|
|
|
6023
6148
|
function loadNudgeState() {
|
|
6024
6149
|
const state = { lastNudge: /* @__PURE__ */ new Map() };
|
|
6025
6150
|
try {
|
|
6026
|
-
if (!
|
|
6027
|
-
const raw = JSON.parse(
|
|
6151
|
+
if (!existsSync14(NUDGE_STATE_PATH)) return state;
|
|
6152
|
+
const raw = JSON.parse(readFileSync13(NUDGE_STATE_PATH, "utf8"));
|
|
6028
6153
|
if (Array.isArray(raw)) {
|
|
6029
6154
|
for (const [key, val] of raw) {
|
|
6030
6155
|
if (key && typeof val?.at === "number" && typeof val?.count === "number") {
|
|
@@ -6038,7 +6163,7 @@ function loadNudgeState() {
|
|
|
6038
6163
|
}
|
|
6039
6164
|
function saveNudgeState(state) {
|
|
6040
6165
|
const entries = Array.from(state.lastNudge.entries());
|
|
6041
|
-
|
|
6166
|
+
writeFileSync8(NUDGE_STATE_PATH, JSON.stringify(entries), "utf8");
|
|
6042
6167
|
}
|
|
6043
6168
|
function createReviewNudgeRealDeps(getClient2) {
|
|
6044
6169
|
return {
|
|
@@ -6380,14 +6505,14 @@ __export(keychain_exports, {
|
|
|
6380
6505
|
setMasterKey: () => setMasterKey
|
|
6381
6506
|
});
|
|
6382
6507
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
6383
|
-
import { existsSync as
|
|
6384
|
-
import
|
|
6508
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6509
|
+
import path17 from "path";
|
|
6385
6510
|
import os9 from "os";
|
|
6386
6511
|
function getKeyDir() {
|
|
6387
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
6512
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os9.homedir(), ".exe-os");
|
|
6388
6513
|
}
|
|
6389
6514
|
function getKeyPath() {
|
|
6390
|
-
return
|
|
6515
|
+
return path17.join(getKeyDir(), "master.key");
|
|
6391
6516
|
}
|
|
6392
6517
|
async function tryKeytar() {
|
|
6393
6518
|
try {
|
|
@@ -6408,7 +6533,7 @@ async function getMasterKey() {
|
|
|
6408
6533
|
}
|
|
6409
6534
|
}
|
|
6410
6535
|
const keyPath = getKeyPath();
|
|
6411
|
-
if (!
|
|
6536
|
+
if (!existsSync15(keyPath)) {
|
|
6412
6537
|
process.stderr.write(
|
|
6413
6538
|
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6414
6539
|
`
|
|
@@ -6451,7 +6576,7 @@ async function deleteMasterKey() {
|
|
|
6451
6576
|
}
|
|
6452
6577
|
}
|
|
6453
6578
|
const keyPath = getKeyPath();
|
|
6454
|
-
if (
|
|
6579
|
+
if (existsSync15(keyPath)) {
|
|
6455
6580
|
await unlink(keyPath);
|
|
6456
6581
|
}
|
|
6457
6582
|
}
|
|
@@ -6506,13 +6631,13 @@ __export(shard_manager_exports, {
|
|
|
6506
6631
|
listShards: () => listShards,
|
|
6507
6632
|
shardExists: () => shardExists
|
|
6508
6633
|
});
|
|
6509
|
-
import
|
|
6510
|
-
import { existsSync as
|
|
6634
|
+
import path18 from "path";
|
|
6635
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
6511
6636
|
import { createClient as createClient2 } from "@libsql/client";
|
|
6512
6637
|
function initShardManager(encryptionKey) {
|
|
6513
6638
|
_encryptionKey = encryptionKey;
|
|
6514
|
-
if (!
|
|
6515
|
-
|
|
6639
|
+
if (!existsSync16(SHARDS_DIR)) {
|
|
6640
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
6516
6641
|
}
|
|
6517
6642
|
_shardingEnabled = true;
|
|
6518
6643
|
}
|
|
@@ -6532,7 +6657,7 @@ function getShardClient(projectName) {
|
|
|
6532
6657
|
}
|
|
6533
6658
|
const cached = _shards.get(safeName);
|
|
6534
6659
|
if (cached) return cached;
|
|
6535
|
-
const dbPath =
|
|
6660
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
6536
6661
|
const client = createClient2({
|
|
6537
6662
|
url: `file:${dbPath}`,
|
|
6538
6663
|
encryptionKey: _encryptionKey
|
|
@@ -6542,10 +6667,10 @@ function getShardClient(projectName) {
|
|
|
6542
6667
|
}
|
|
6543
6668
|
function shardExists(projectName) {
|
|
6544
6669
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6545
|
-
return
|
|
6670
|
+
return existsSync16(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
6546
6671
|
}
|
|
6547
6672
|
function listShards() {
|
|
6548
|
-
if (!
|
|
6673
|
+
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6549
6674
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6550
6675
|
}
|
|
6551
6676
|
async function ensureShardSchema(client) {
|
|
@@ -6731,7 +6856,7 @@ var init_shard_manager = __esm({
|
|
|
6731
6856
|
"src/lib/shard-manager.ts"() {
|
|
6732
6857
|
"use strict";
|
|
6733
6858
|
init_config();
|
|
6734
|
-
SHARDS_DIR =
|
|
6859
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
6735
6860
|
_shards = /* @__PURE__ */ new Map();
|
|
6736
6861
|
_encryptionKey = null;
|
|
6737
6862
|
_shardingEnabled = false;
|
|
@@ -8096,10 +8221,10 @@ async function disposeEmbedder() {
|
|
|
8096
8221
|
async function embedDirect(text) {
|
|
8097
8222
|
const llamaCpp = await import("node-llama-cpp");
|
|
8098
8223
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8099
|
-
const { existsSync:
|
|
8100
|
-
const
|
|
8101
|
-
const modelPath =
|
|
8102
|
-
if (!
|
|
8224
|
+
const { existsSync: existsSync19 } = await import("fs");
|
|
8225
|
+
const path23 = await import("path");
|
|
8226
|
+
const modelPath = path23.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
8227
|
+
if (!existsSync19(modelPath)) {
|
|
8103
8228
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
8104
8229
|
}
|
|
8105
8230
|
const llama = await llamaCpp.getLlama();
|
|
@@ -8821,8 +8946,8 @@ __export(wiki_sync_exports, {
|
|
|
8821
8946
|
listWorkspaces: () => listWorkspaces,
|
|
8822
8947
|
syncMemories: () => syncMemories
|
|
8823
8948
|
});
|
|
8824
|
-
async function wikiRequest(config,
|
|
8825
|
-
const url = `${config.wikiUrl}/api/v1${
|
|
8949
|
+
async function wikiRequest(config, path23, method = "GET", body) {
|
|
8950
|
+
const url = `${config.wikiUrl}/api/v1${path23}`;
|
|
8826
8951
|
const headers = {
|
|
8827
8952
|
"Authorization": `Bearer ${config.wikiApiKey}`,
|
|
8828
8953
|
"Content-Type": "application/json"
|
|
@@ -8834,7 +8959,7 @@ async function wikiRequest(config, path22, method = "GET", body) {
|
|
|
8834
8959
|
signal: AbortSignal.timeout(3e4)
|
|
8835
8960
|
});
|
|
8836
8961
|
if (!response.ok) {
|
|
8837
|
-
throw new Error(`Wiki API ${method} ${
|
|
8962
|
+
throw new Error(`Wiki API ${method} ${path23}: ${response.status} ${response.statusText}`);
|
|
8838
8963
|
}
|
|
8839
8964
|
return response.json();
|
|
8840
8965
|
}
|
|
@@ -8946,7 +9071,7 @@ __export(token_spend_exports, {
|
|
|
8946
9071
|
import { readdir } from "fs/promises";
|
|
8947
9072
|
import { createReadStream } from "fs";
|
|
8948
9073
|
import { createInterface } from "readline";
|
|
8949
|
-
import
|
|
9074
|
+
import path19 from "path";
|
|
8950
9075
|
import os10 from "os";
|
|
8951
9076
|
function getPricing(model) {
|
|
8952
9077
|
if (MODEL_PRICING[model]) return MODEL_PRICING[model];
|
|
@@ -8970,18 +9095,18 @@ async function getAgentSpend(period = "7d") {
|
|
|
8970
9095
|
for (const row of result.rows) {
|
|
8971
9096
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
8972
9097
|
}
|
|
8973
|
-
const claudeDir =
|
|
9098
|
+
const claudeDir = path19.join(os10.homedir(), ".claude", "projects");
|
|
8974
9099
|
let projectDirs = [];
|
|
8975
9100
|
try {
|
|
8976
9101
|
const entries = await readdir(claudeDir);
|
|
8977
|
-
projectDirs = entries.map((e) =>
|
|
9102
|
+
projectDirs = entries.map((e) => path19.join(claudeDir, e));
|
|
8978
9103
|
} catch {
|
|
8979
9104
|
return [];
|
|
8980
9105
|
}
|
|
8981
9106
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
8982
9107
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
8983
9108
|
for (const dir of projectDirs) {
|
|
8984
|
-
const jsonlPath =
|
|
9109
|
+
const jsonlPath = path19.join(dir, `${sessionUuid}.jsonl`);
|
|
8985
9110
|
try {
|
|
8986
9111
|
const usage = await extractSessionUsage(jsonlPath);
|
|
8987
9112
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -9102,11 +9227,11 @@ __export(update_check_exports, {
|
|
|
9102
9227
|
getRemoteVersion: () => getRemoteVersion
|
|
9103
9228
|
});
|
|
9104
9229
|
import { execSync as execSync11 } from "child_process";
|
|
9105
|
-
import { readFileSync as
|
|
9106
|
-
import
|
|
9230
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
9231
|
+
import path20 from "path";
|
|
9107
9232
|
function getLocalVersion(packageRoot) {
|
|
9108
|
-
const pkgPath =
|
|
9109
|
-
const pkg = JSON.parse(
|
|
9233
|
+
const pkgPath = path20.join(packageRoot, "package.json");
|
|
9234
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
9110
9235
|
return pkg.version;
|
|
9111
9236
|
}
|
|
9112
9237
|
function getRemoteVersion() {
|
|
@@ -9178,12 +9303,12 @@ __export(device_registry_exports, {
|
|
|
9178
9303
|
});
|
|
9179
9304
|
import crypto8 from "crypto";
|
|
9180
9305
|
import os11 from "os";
|
|
9181
|
-
import { readFileSync as
|
|
9182
|
-
import
|
|
9306
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, existsSync as existsSync17 } from "fs";
|
|
9307
|
+
import path21 from "path";
|
|
9183
9308
|
function getDeviceInfo() {
|
|
9184
|
-
if (
|
|
9309
|
+
if (existsSync17(DEVICE_JSON_PATH)) {
|
|
9185
9310
|
try {
|
|
9186
|
-
const raw =
|
|
9311
|
+
const raw = readFileSync15(DEVICE_JSON_PATH, "utf8");
|
|
9187
9312
|
const data = JSON.parse(raw);
|
|
9188
9313
|
if (data.deviceId && data.friendlyName && data.hostname) {
|
|
9189
9314
|
return data;
|
|
@@ -9197,14 +9322,14 @@ function getDeviceInfo() {
|
|
|
9197
9322
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
9198
9323
|
hostname
|
|
9199
9324
|
};
|
|
9200
|
-
|
|
9201
|
-
|
|
9325
|
+
mkdirSync8(path21.dirname(DEVICE_JSON_PATH), { recursive: true });
|
|
9326
|
+
writeFileSync9(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
9202
9327
|
return info;
|
|
9203
9328
|
}
|
|
9204
9329
|
function setFriendlyName(name) {
|
|
9205
9330
|
const info = getDeviceInfo();
|
|
9206
9331
|
info.friendlyName = name;
|
|
9207
|
-
|
|
9332
|
+
writeFileSync9(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
9208
9333
|
}
|
|
9209
9334
|
async function resolveTargetDevice(targetAgent, targetProject) {
|
|
9210
9335
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
@@ -9238,7 +9363,7 @@ var init_device_registry = __esm({
|
|
|
9238
9363
|
"src/lib/device-registry.ts"() {
|
|
9239
9364
|
"use strict";
|
|
9240
9365
|
init_config();
|
|
9241
|
-
DEVICE_JSON_PATH =
|
|
9366
|
+
DEVICE_JSON_PATH = path21.join(EXE_AI_DIR, "device.json");
|
|
9242
9367
|
}
|
|
9243
9368
|
});
|
|
9244
9369
|
|
|
@@ -9702,11 +9827,11 @@ init_memory();
|
|
|
9702
9827
|
init_daemon_protocol();
|
|
9703
9828
|
init_daemon_orchestration();
|
|
9704
9829
|
import net2 from "net";
|
|
9705
|
-
import { writeFileSync as
|
|
9706
|
-
import
|
|
9830
|
+
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync7, mkdirSync as mkdirSync9, existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
9831
|
+
import path22 from "path";
|
|
9707
9832
|
import { getLlama } from "node-llama-cpp";
|
|
9708
|
-
var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
9709
|
-
var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
9833
|
+
var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path22.join(EXE_AI_DIR, "exed.sock");
|
|
9834
|
+
var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path22.join(EXE_AI_DIR, "exed.pid");
|
|
9710
9835
|
var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
|
|
9711
9836
|
var IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
9712
9837
|
var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
|
|
@@ -9732,8 +9857,8 @@ function enqueue(queue, entry) {
|
|
|
9732
9857
|
queue.push(entry);
|
|
9733
9858
|
}
|
|
9734
9859
|
async function loadModel() {
|
|
9735
|
-
const modelPath =
|
|
9736
|
-
if (!
|
|
9860
|
+
const modelPath = path22.join(MODELS_DIR, MODEL_FILE);
|
|
9861
|
+
if (!existsSync18(modelPath)) {
|
|
9737
9862
|
process.stderr.write(`[exed] FATAL: model not found at ${modelPath}
|
|
9738
9863
|
`);
|
|
9739
9864
|
process.exit(1);
|
|
@@ -9902,12 +10027,12 @@ async function handleDbBatch(socket, requestId, statements, mode) {
|
|
|
9902
10027
|
}
|
|
9903
10028
|
}
|
|
9904
10029
|
function startServer() {
|
|
9905
|
-
|
|
10030
|
+
mkdirSync9(path22.dirname(SOCKET_PATH2), { recursive: true });
|
|
9906
10031
|
for (const oldFile of ["embed.sock", "embed.pid"]) {
|
|
9907
|
-
const oldPath =
|
|
10032
|
+
const oldPath = path22.join(path22.dirname(SOCKET_PATH2), oldFile);
|
|
9908
10033
|
try {
|
|
9909
10034
|
if (oldFile.endsWith(".pid")) {
|
|
9910
|
-
const pid = parseInt(
|
|
10035
|
+
const pid = parseInt(readFileSync16(oldPath, "utf8").trim(), 10);
|
|
9911
10036
|
if (pid > 0) try {
|
|
9912
10037
|
process.kill(pid, "SIGKILL");
|
|
9913
10038
|
} catch {
|
|
@@ -9996,7 +10121,7 @@ function startServer() {
|
|
|
9996
10121
|
server.listen(SOCKET_PATH2, () => {
|
|
9997
10122
|
process.stderr.write(`[exed] Listening on ${SOCKET_PATH2}
|
|
9998
10123
|
`);
|
|
9999
|
-
|
|
10124
|
+
writeFileSync10(PID_PATH2, String(process.pid));
|
|
10000
10125
|
checkIdle();
|
|
10001
10126
|
});
|
|
10002
10127
|
}
|
|
@@ -10289,7 +10414,7 @@ function startWikiSync() {
|
|
|
10289
10414
|
});
|
|
10290
10415
|
}
|
|
10291
10416
|
var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
|
|
10292
|
-
var AGENT_STATS_PATH =
|
|
10417
|
+
var AGENT_STATS_PATH = path22.join(EXE_AI_DIR, "agent-stats.json");
|
|
10293
10418
|
async function writeAgentStats() {
|
|
10294
10419
|
if (!await ensureStoreForPolling()) return;
|
|
10295
10420
|
try {
|
|
@@ -10347,7 +10472,7 @@ async function writeAgentStats() {
|
|
|
10347
10472
|
pid: process.pid
|
|
10348
10473
|
}
|
|
10349
10474
|
};
|
|
10350
|
-
|
|
10475
|
+
writeFileSync10(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
|
|
10351
10476
|
} catch (err) {
|
|
10352
10477
|
process.stderr.write(`[exed] Agent stats error: ${err instanceof Error ? err.message : String(err)}
|
|
10353
10478
|
`);
|
|
@@ -10494,8 +10619,8 @@ process.on("SIGINT", () => void shutdown());
|
|
|
10494
10619
|
process.on("SIGTERM", () => void shutdown());
|
|
10495
10620
|
function checkExistingDaemon() {
|
|
10496
10621
|
try {
|
|
10497
|
-
if (!
|
|
10498
|
-
const pid = parseInt(
|
|
10622
|
+
if (!existsSync18(PID_PATH2)) return false;
|
|
10623
|
+
const pid = parseInt(readFileSync16(PID_PATH2, "utf8").trim(), 10);
|
|
10499
10624
|
if (!pid || isNaN(pid)) return false;
|
|
10500
10625
|
process.kill(pid, 0);
|
|
10501
10626
|
process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
|