@askexenow/exe-os 0.8.33 → 0.8.37
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 +341 -349
- package/dist/bin/backfill-responses.js +81 -13
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1737 -1117
- package/dist/bin/exe-assign.js +89 -19
- package/dist/bin/exe-boot.js +951 -101
- package/dist/bin/exe-call.js +61 -2
- package/dist/bin/exe-dispatch.js +61 -13
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +71 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +178 -45
- package/dist/bin/exe-heartbeat.js +79 -14
- package/dist/bin/exe-kill.js +71 -3
- package/dist/bin/exe-launch-agent.js +148 -14
- package/dist/bin/exe-link.js +1437 -0
- package/dist/bin/exe-new-employee.js +98 -13
- package/dist/bin/exe-pending-messages.js +74 -8
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +77 -11
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +73 -5
- package/dist/bin/exe-search.js +88 -14
- package/dist/bin/exe-session-cleanup.js +102 -28
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +80 -5
- package/dist/bin/graph-backfill.js +71 -3
- package/dist/bin/graph-export.js +71 -3
- package/dist/bin/install.js +38 -8
- package/dist/bin/scan-tasks.js +80 -5
- package/dist/bin/setup.js +128 -10
- package/dist/bin/shard-migrate.js +71 -3
- package/dist/bin/wiki-sync.js +71 -3
- package/dist/gateway/index.js +179 -46
- package/dist/hooks/bug-report-worker.js +254 -28
- package/dist/hooks/commit-complete.js +80 -5
- package/dist/hooks/error-recall.js +89 -15
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +185 -51
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +81 -6
- package/dist/hooks/notification.js +81 -6
- package/dist/hooks/post-compact.js +81 -6
- package/dist/hooks/pre-compact.js +81 -6
- package/dist/hooks/pre-tool-use.js +423 -196
- package/dist/hooks/prompt-ingest-worker.js +91 -23
- package/dist/hooks/prompt-submit.js +159 -45
- package/dist/hooks/response-ingest-worker.js +96 -23
- package/dist/hooks/session-end.js +81 -6
- package/dist/hooks/session-start.js +89 -15
- package/dist/hooks/stop.js +81 -6
- package/dist/hooks/subagent-stop.js +81 -6
- package/dist/hooks/summary-worker.js +807 -55
- package/dist/index.js +198 -60
- package/dist/lib/cloud-sync.js +703 -18
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +26 -0
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +207 -74
- package/dist/lib/hybrid-search.js +88 -14
- package/dist/lib/identity-templates.js +51 -0
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +65 -17
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +73 -4
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +95 -28
- package/dist/lib/tmux-routing.js +92 -23
- package/dist/mcp/server.js +800 -74
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +198 -31
- package/dist/mcp/tools/deactivate-behavior.js +4 -4
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +19 -9
- package/dist/mcp/tools/send-message.js +69 -21
- package/dist/mcp/tools/update-task.js +28 -18
- package/dist/runtime/index.js +166 -28
- package/dist/tui/App.js +193 -40
- package/package.json +7 -3
- package/src/commands/exe/afk.md +116 -0
- package/src/commands/exe/rename.md +12 -0
|
@@ -6,12 +6,12 @@ import crypto from "crypto";
|
|
|
6
6
|
|
|
7
7
|
// src/lib/database.ts
|
|
8
8
|
import { createClient } from "@libsql/client";
|
|
9
|
-
var
|
|
9
|
+
var _resilientClient = null;
|
|
10
10
|
function getClient() {
|
|
11
|
-
if (!
|
|
11
|
+
if (!_resilientClient) {
|
|
12
12
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return _resilientClient;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// src/lib/reminders.ts
|
|
@@ -6,12 +6,12 @@ import crypto from "crypto";
|
|
|
6
6
|
|
|
7
7
|
// src/lib/database.ts
|
|
8
8
|
import { createClient } from "@libsql/client";
|
|
9
|
-
var
|
|
9
|
+
var _resilientClient = null;
|
|
10
10
|
function getClient() {
|
|
11
|
-
if (!
|
|
11
|
+
if (!_resilientClient) {
|
|
12
12
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return _resilientClient;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// src/lib/reminders.ts
|
|
@@ -19,19 +19,27 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
21
|
|
|
22
|
+
// src/lib/db-retry.ts
|
|
23
|
+
var init_db_retry = __esm({
|
|
24
|
+
"src/lib/db-retry.ts"() {
|
|
25
|
+
"use strict";
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
22
29
|
// src/lib/database.ts
|
|
23
30
|
import { createClient } from "@libsql/client";
|
|
24
31
|
function getClient() {
|
|
25
|
-
if (!
|
|
32
|
+
if (!_resilientClient) {
|
|
26
33
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
27
34
|
}
|
|
28
|
-
return
|
|
35
|
+
return _resilientClient;
|
|
29
36
|
}
|
|
30
|
-
var
|
|
37
|
+
var _resilientClient;
|
|
31
38
|
var init_database = __esm({
|
|
32
39
|
"src/lib/database.ts"() {
|
|
33
40
|
"use strict";
|
|
34
|
-
|
|
41
|
+
init_db_retry();
|
|
42
|
+
_resilientClient = null;
|
|
35
43
|
}
|
|
36
44
|
});
|
|
37
45
|
|
|
@@ -508,21 +516,139 @@ var init_tasks_crud = __esm({
|
|
|
508
516
|
});
|
|
509
517
|
|
|
510
518
|
// src/lib/employees.ts
|
|
519
|
+
var employees_exports = {};
|
|
520
|
+
__export(employees_exports, {
|
|
521
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
522
|
+
addEmployee: () => addEmployee,
|
|
523
|
+
getEmployee: () => getEmployee,
|
|
524
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
525
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
526
|
+
hasRole: () => hasRole,
|
|
527
|
+
isMultiInstance: () => isMultiInstance,
|
|
528
|
+
loadEmployees: () => loadEmployees,
|
|
529
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
530
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
531
|
+
saveEmployees: () => saveEmployees,
|
|
532
|
+
validateEmployeeName: () => validateEmployeeName
|
|
533
|
+
});
|
|
511
534
|
import { readFile as readFile2, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
512
|
-
import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
|
|
535
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
|
|
513
536
|
import { execSync as execSync2 } from "child_process";
|
|
514
537
|
import path4 from "path";
|
|
515
|
-
|
|
538
|
+
function validateEmployeeName(name) {
|
|
539
|
+
if (!name) {
|
|
540
|
+
return { valid: false, error: "Name is required" };
|
|
541
|
+
}
|
|
542
|
+
if (name.length > 32) {
|
|
543
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
544
|
+
}
|
|
545
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
546
|
+
return {
|
|
547
|
+
valid: false,
|
|
548
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return { valid: true };
|
|
552
|
+
}
|
|
553
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
554
|
+
if (!existsSync4(employeesPath)) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
558
|
+
try {
|
|
559
|
+
return JSON.parse(raw);
|
|
560
|
+
} catch {
|
|
561
|
+
return [];
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
565
|
+
await mkdir3(path4.dirname(employeesPath), { recursive: true });
|
|
566
|
+
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
567
|
+
}
|
|
568
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
569
|
+
if (!existsSync4(employeesPath)) return [];
|
|
570
|
+
try {
|
|
571
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
572
|
+
} catch {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function getEmployee(employees, name) {
|
|
577
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
578
|
+
}
|
|
579
|
+
function getEmployeeByRole(employees, role) {
|
|
580
|
+
const lower = role.toLowerCase();
|
|
581
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
582
|
+
}
|
|
583
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
584
|
+
const lower = role.toLowerCase();
|
|
585
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
586
|
+
}
|
|
587
|
+
function hasRole(agentName, role) {
|
|
588
|
+
const employees = loadEmployeesSync();
|
|
589
|
+
const emp = getEmployee(employees, agentName);
|
|
590
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
591
|
+
}
|
|
592
|
+
function isMultiInstance(agentName, employees) {
|
|
593
|
+
const roster = employees ?? loadEmployeesSync();
|
|
594
|
+
const emp = getEmployee(roster, agentName);
|
|
595
|
+
if (!emp) return false;
|
|
596
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
597
|
+
}
|
|
598
|
+
function addEmployee(employees, employee) {
|
|
599
|
+
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
600
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
601
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
602
|
+
}
|
|
603
|
+
return [...employees, normalized];
|
|
604
|
+
}
|
|
605
|
+
function registerBinSymlinks(name) {
|
|
606
|
+
const created = [];
|
|
607
|
+
const skipped = [];
|
|
608
|
+
const errors = [];
|
|
609
|
+
let exeBinPath;
|
|
610
|
+
try {
|
|
611
|
+
exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
|
|
612
|
+
} catch {
|
|
613
|
+
errors.push("Could not find 'exe' in PATH");
|
|
614
|
+
return { created, skipped, errors };
|
|
615
|
+
}
|
|
616
|
+
const binDir = path4.dirname(exeBinPath);
|
|
617
|
+
let target;
|
|
618
|
+
try {
|
|
619
|
+
target = readlinkSync(exeBinPath);
|
|
620
|
+
} catch {
|
|
621
|
+
errors.push("Could not read 'exe' symlink");
|
|
622
|
+
return { created, skipped, errors };
|
|
623
|
+
}
|
|
624
|
+
for (const suffix of ["", "-opencode"]) {
|
|
625
|
+
const linkName = `${name}${suffix}`;
|
|
626
|
+
const linkPath = path4.join(binDir, linkName);
|
|
627
|
+
if (existsSync4(linkPath)) {
|
|
628
|
+
skipped.push(linkName);
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
symlinkSync(target, linkPath);
|
|
633
|
+
created.push(linkName);
|
|
634
|
+
} catch (err) {
|
|
635
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return { created, skipped, errors };
|
|
639
|
+
}
|
|
640
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
516
641
|
var init_employees = __esm({
|
|
517
642
|
"src/lib/employees.ts"() {
|
|
518
643
|
"use strict";
|
|
519
644
|
init_config();
|
|
520
645
|
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
646
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
521
647
|
}
|
|
522
648
|
});
|
|
523
649
|
|
|
524
650
|
// src/lib/session-registry.ts
|
|
525
|
-
import { readFileSync as
|
|
651
|
+
import { readFileSync as readFileSync5, writeFileSync, mkdirSync, existsSync as existsSync5 } from "fs";
|
|
526
652
|
import path5 from "path";
|
|
527
653
|
import os3 from "os";
|
|
528
654
|
function registerSession(entry) {
|
|
@@ -541,7 +667,7 @@ function registerSession(entry) {
|
|
|
541
667
|
}
|
|
542
668
|
function listSessions() {
|
|
543
669
|
try {
|
|
544
|
-
const raw =
|
|
670
|
+
const raw = readFileSync5(REGISTRY_PATH, "utf8");
|
|
545
671
|
return JSON.parse(raw);
|
|
546
672
|
} catch {
|
|
547
673
|
return [];
|
|
@@ -771,7 +897,7 @@ var init_provider_table = __esm({
|
|
|
771
897
|
});
|
|
772
898
|
|
|
773
899
|
// src/lib/intercom-queue.ts
|
|
774
|
-
import { readFileSync as
|
|
900
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
775
901
|
import path6 from "path";
|
|
776
902
|
import os4 from "os";
|
|
777
903
|
function ensureDir() {
|
|
@@ -781,7 +907,7 @@ function ensureDir() {
|
|
|
781
907
|
function readQueue() {
|
|
782
908
|
try {
|
|
783
909
|
if (!existsSync6(QUEUE_PATH)) return [];
|
|
784
|
-
return JSON.parse(
|
|
910
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
785
911
|
} catch {
|
|
786
912
|
return [];
|
|
787
913
|
}
|
|
@@ -820,7 +946,7 @@ var init_intercom_queue = __esm({
|
|
|
820
946
|
});
|
|
821
947
|
|
|
822
948
|
// src/lib/license.ts
|
|
823
|
-
import { readFileSync as
|
|
949
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
824
950
|
import { randomUUID } from "crypto";
|
|
825
951
|
import path7 from "path";
|
|
826
952
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -843,12 +969,12 @@ var init_license = __esm({
|
|
|
843
969
|
});
|
|
844
970
|
|
|
845
971
|
// src/lib/plan-limits.ts
|
|
846
|
-
import { readFileSync as
|
|
972
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
847
973
|
import path8 from "path";
|
|
848
974
|
function getLicenseSync() {
|
|
849
975
|
try {
|
|
850
976
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
851
|
-
const raw = JSON.parse(
|
|
977
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
852
978
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
853
979
|
const parts = raw.token.split(".");
|
|
854
980
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -887,7 +1013,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
887
1013
|
let count = 0;
|
|
888
1014
|
try {
|
|
889
1015
|
if (existsSync8(filePath)) {
|
|
890
|
-
const raw =
|
|
1016
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
891
1017
|
const employees = JSON.parse(raw);
|
|
892
1018
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
893
1019
|
}
|
|
@@ -922,10 +1048,46 @@ var init_plan_limits = __esm({
|
|
|
922
1048
|
|
|
923
1049
|
// src/lib/tmux-routing.ts
|
|
924
1050
|
import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
|
|
925
|
-
import { readFileSync as
|
|
1051
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync9, appendFileSync } from "fs";
|
|
926
1052
|
import path9 from "path";
|
|
927
1053
|
import os5 from "os";
|
|
928
1054
|
import { fileURLToPath } from "url";
|
|
1055
|
+
import { unlinkSync as unlinkSync2 } from "fs";
|
|
1056
|
+
function spawnLockPath(sessionName) {
|
|
1057
|
+
return path9.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
1058
|
+
}
|
|
1059
|
+
function isProcessAlive(pid) {
|
|
1060
|
+
try {
|
|
1061
|
+
process.kill(pid, 0);
|
|
1062
|
+
return true;
|
|
1063
|
+
} catch {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function acquireSpawnLock(sessionName) {
|
|
1068
|
+
if (!existsSync9(SPAWN_LOCK_DIR)) {
|
|
1069
|
+
mkdirSync4(SPAWN_LOCK_DIR, { recursive: true });
|
|
1070
|
+
}
|
|
1071
|
+
const lockFile = spawnLockPath(sessionName);
|
|
1072
|
+
if (existsSync9(lockFile)) {
|
|
1073
|
+
try {
|
|
1074
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
1075
|
+
const age = Date.now() - lock.timestamp;
|
|
1076
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
} catch {
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
writeFileSync4(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
function releaseSpawnLock(sessionName) {
|
|
1086
|
+
try {
|
|
1087
|
+
unlinkSync2(spawnLockPath(sessionName));
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
929
1091
|
function resolveBehaviorsExporterScript() {
|
|
930
1092
|
try {
|
|
931
1093
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -971,7 +1133,7 @@ function extractRootExe(name) {
|
|
|
971
1133
|
}
|
|
972
1134
|
function getParentExe(sessionKey) {
|
|
973
1135
|
try {
|
|
974
|
-
const data = JSON.parse(
|
|
1136
|
+
const data = JSON.parse(readFileSync9(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
975
1137
|
return data.parentExe || null;
|
|
976
1138
|
} catch {
|
|
977
1139
|
return null;
|
|
@@ -995,17 +1157,17 @@ function isEmployeeAlive(sessionName) {
|
|
|
995
1157
|
}
|
|
996
1158
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
997
1159
|
const base = employeeSessionName(employeeName, exeSession);
|
|
998
|
-
if (!isAlive(base)) return 0;
|
|
1160
|
+
if (!isAlive(base) && acquireSpawnLock(base)) return 0;
|
|
999
1161
|
for (let i = 2; i <= maxInstances; i++) {
|
|
1000
1162
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
1001
|
-
if (!isAlive(candidate)) return i;
|
|
1163
|
+
if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
|
|
1002
1164
|
}
|
|
1003
1165
|
return null;
|
|
1004
1166
|
}
|
|
1005
1167
|
function readDebounceState() {
|
|
1006
1168
|
try {
|
|
1007
1169
|
if (!existsSync9(DEBOUNCE_FILE)) return {};
|
|
1008
|
-
return JSON.parse(
|
|
1170
|
+
return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
1009
1171
|
} catch {
|
|
1010
1172
|
return {};
|
|
1011
1173
|
}
|
|
@@ -1179,7 +1341,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1179
1341
|
const claudeJsonPath = path9.join(os5.homedir(), ".claude.json");
|
|
1180
1342
|
let claudeJson = {};
|
|
1181
1343
|
try {
|
|
1182
|
-
claudeJson = JSON.parse(
|
|
1344
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
1183
1345
|
} catch {
|
|
1184
1346
|
}
|
|
1185
1347
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -1197,7 +1359,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1197
1359
|
const settingsPath = path9.join(projSettingsDir, "settings.json");
|
|
1198
1360
|
let settings = {};
|
|
1199
1361
|
try {
|
|
1200
|
-
settings = JSON.parse(
|
|
1362
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
1201
1363
|
} catch {
|
|
1202
1364
|
}
|
|
1203
1365
|
const perms = settings.permissions ?? {};
|
|
@@ -1310,6 +1472,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1310
1472
|
command: spawnCommand
|
|
1311
1473
|
});
|
|
1312
1474
|
if (spawnResult.error) {
|
|
1475
|
+
releaseSpawnLock(sessionName);
|
|
1313
1476
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
1314
1477
|
}
|
|
1315
1478
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -1347,6 +1510,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1347
1510
|
}
|
|
1348
1511
|
}
|
|
1349
1512
|
if (!booted) {
|
|
1513
|
+
releaseSpawnLock(sessionName);
|
|
1350
1514
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
1351
1515
|
}
|
|
1352
1516
|
if (!useExeAgent) {
|
|
@@ -1363,9 +1527,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1363
1527
|
pid: 0,
|
|
1364
1528
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1365
1529
|
});
|
|
1530
|
+
releaseSpawnLock(sessionName);
|
|
1366
1531
|
return { sessionName };
|
|
1367
1532
|
}
|
|
1368
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1533
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1369
1534
|
var init_tmux_routing = __esm({
|
|
1370
1535
|
"src/lib/tmux-routing.ts"() {
|
|
1371
1536
|
"use strict";
|
|
@@ -1377,6 +1542,7 @@ var init_tmux_routing = __esm({
|
|
|
1377
1542
|
init_provider_table();
|
|
1378
1543
|
init_intercom_queue();
|
|
1379
1544
|
init_plan_limits();
|
|
1545
|
+
SPAWN_LOCK_DIR = path9.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
1380
1546
|
SESSION_CACHE = path9.join(os5.homedir(), ".exe-os", "session-cache");
|
|
1381
1547
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
1382
1548
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
@@ -1389,7 +1555,7 @@ var init_tmux_routing = __esm({
|
|
|
1389
1555
|
|
|
1390
1556
|
// src/lib/tasks-review.ts
|
|
1391
1557
|
import path10 from "path";
|
|
1392
|
-
import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as
|
|
1558
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
1393
1559
|
var init_tasks_review = __esm({
|
|
1394
1560
|
"src/lib/tasks-review.ts"() {
|
|
1395
1561
|
"use strict";
|
|
@@ -1544,7 +1710,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
1544
1710
|
} else {
|
|
1545
1711
|
const projectDir = input.projectDir ?? process.cwd();
|
|
1546
1712
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
1547
|
-
autoInstance: input.assignedTo
|
|
1713
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
1548
1714
|
});
|
|
1549
1715
|
if (result.status === "failed") {
|
|
1550
1716
|
process.stderr.write(
|
|
@@ -1566,12 +1732,13 @@ var init_tasks_notify = __esm({
|
|
|
1566
1732
|
init_session_key();
|
|
1567
1733
|
init_notifications();
|
|
1568
1734
|
init_transport();
|
|
1735
|
+
init_employees();
|
|
1569
1736
|
}
|
|
1570
1737
|
});
|
|
1571
1738
|
|
|
1572
1739
|
// src/lib/tasks.ts
|
|
1573
1740
|
import path13 from "path";
|
|
1574
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as
|
|
1741
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
1575
1742
|
async function createTask(input) {
|
|
1576
1743
|
const result = await createTaskCore(input);
|
|
1577
1744
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -1607,7 +1774,7 @@ import { z } from "zod";
|
|
|
1607
1774
|
|
|
1608
1775
|
// src/adapters/claude/active-agent.ts
|
|
1609
1776
|
init_config();
|
|
1610
|
-
import { readFileSync as
|
|
1777
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
|
|
1611
1778
|
import { execSync as execSync7 } from "child_process";
|
|
1612
1779
|
import path14 from "path";
|
|
1613
1780
|
|
|
@@ -1623,14 +1790,14 @@ function getMarkerPath() {
|
|
|
1623
1790
|
function getActiveAgent() {
|
|
1624
1791
|
try {
|
|
1625
1792
|
const markerPath = getMarkerPath();
|
|
1626
|
-
const raw =
|
|
1793
|
+
const raw = readFileSync10(markerPath, "utf8");
|
|
1627
1794
|
const data = JSON.parse(raw);
|
|
1628
1795
|
if (data.agentId) {
|
|
1629
1796
|
if (data.startedAt) {
|
|
1630
1797
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
1631
1798
|
if (age > STALE_MS) {
|
|
1632
1799
|
try {
|
|
1633
|
-
|
|
1800
|
+
unlinkSync5(markerPath);
|
|
1634
1801
|
} catch {
|
|
1635
1802
|
}
|
|
1636
1803
|
} else {
|
|
@@ -1653,7 +1820,7 @@ function getActiveAgent() {
|
|
|
1653
1820
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
1654
1821
|
{ encoding: "utf8", timeout: 2e3 }
|
|
1655
1822
|
).trim();
|
|
1656
|
-
const empMatch = sessionName.match(/^(
|
|
1823
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
1657
1824
|
if (empMatch && empMatch[1] !== "exe") {
|
|
1658
1825
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
1659
1826
|
}
|
|
@@ -1717,8 +1884,8 @@ function registerCreateTask(server) {
|
|
|
1717
1884
|
try {
|
|
1718
1885
|
const exeSession = resolveExeSession();
|
|
1719
1886
|
if (exeSession) {
|
|
1720
|
-
const
|
|
1721
|
-
const useAutoInstance =
|
|
1887
|
+
const { isMultiInstance: isMultiInstance2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
1888
|
+
const useAutoInstance = isMultiInstance2(assigned_to);
|
|
1722
1889
|
const { loadConfigSync: loadConfigSync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
1723
1890
|
const cfg = loadConfigSync2();
|
|
1724
1891
|
const result = ensureEmployee(assigned_to, exeSession, process.cwd(), {
|
|
@@ -6,12 +6,12 @@ import crypto from "crypto";
|
|
|
6
6
|
|
|
7
7
|
// src/lib/database.ts
|
|
8
8
|
import { createClient } from "@libsql/client";
|
|
9
|
-
var
|
|
9
|
+
var _resilientClient = null;
|
|
10
10
|
function getClient() {
|
|
11
|
-
if (!
|
|
11
|
+
if (!_resilientClient) {
|
|
12
12
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return _resilientClient;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// src/lib/behaviors.ts
|
|
@@ -188,7 +188,7 @@ function getActiveAgent() {
|
|
|
188
188
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
189
189
|
{ encoding: "utf8", timeout: 2e3 }
|
|
190
190
|
).trim();
|
|
191
|
-
const empMatch = sessionName.match(/^(
|
|
191
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
192
192
|
if (empMatch && empMatch[1] !== "exe") {
|
|
193
193
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
194
194
|
}
|
|
@@ -6,12 +6,12 @@ import crypto from "crypto";
|
|
|
6
6
|
|
|
7
7
|
// src/lib/database.ts
|
|
8
8
|
import { createClient } from "@libsql/client";
|
|
9
|
-
var
|
|
9
|
+
var _resilientClient = null;
|
|
10
10
|
function getClient() {
|
|
11
|
-
if (!
|
|
11
|
+
if (!_resilientClient) {
|
|
12
12
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return _resilientClient;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// src/lib/reminders.ts
|
|
@@ -3,19 +3,27 @@ var __esm = (fn, res) => function __init() {
|
|
|
3
3
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
+
// src/lib/db-retry.ts
|
|
7
|
+
var init_db_retry = __esm({
|
|
8
|
+
"src/lib/db-retry.ts"() {
|
|
9
|
+
"use strict";
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
6
13
|
// src/lib/database.ts
|
|
7
14
|
import { createClient } from "@libsql/client";
|
|
8
15
|
function getClient() {
|
|
9
|
-
if (!
|
|
16
|
+
if (!_resilientClient) {
|
|
10
17
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
11
18
|
}
|
|
12
|
-
return
|
|
19
|
+
return _resilientClient;
|
|
13
20
|
}
|
|
14
|
-
var
|
|
21
|
+
var _resilientClient;
|
|
15
22
|
var init_database = __esm({
|
|
16
23
|
"src/lib/database.ts"() {
|
|
17
24
|
"use strict";
|
|
18
|
-
|
|
25
|
+
init_db_retry();
|
|
26
|
+
_resilientClient = null;
|
|
19
27
|
}
|
|
20
28
|
});
|
|
21
29
|
|
|
@@ -194,7 +202,7 @@ var init_tasks_crud = __esm({
|
|
|
194
202
|
|
|
195
203
|
// src/lib/employees.ts
|
|
196
204
|
import { readFile as readFile2, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
197
|
-
import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
|
|
205
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
|
|
198
206
|
import { execSync as execSync2 } from "child_process";
|
|
199
207
|
import path4 from "path";
|
|
200
208
|
var EMPLOYEES_PATH;
|
|
@@ -262,7 +270,7 @@ var init_provider_table = __esm({
|
|
|
262
270
|
});
|
|
263
271
|
|
|
264
272
|
// src/lib/intercom-queue.ts
|
|
265
|
-
import { readFileSync as
|
|
273
|
+
import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync5, mkdirSync } from "fs";
|
|
266
274
|
import path6 from "path";
|
|
267
275
|
import os4 from "os";
|
|
268
276
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
@@ -276,7 +284,7 @@ var init_intercom_queue = __esm({
|
|
|
276
284
|
});
|
|
277
285
|
|
|
278
286
|
// src/lib/license.ts
|
|
279
|
-
import { readFileSync as
|
|
287
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
280
288
|
import { randomUUID } from "crypto";
|
|
281
289
|
import path7 from "path";
|
|
282
290
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -292,7 +300,7 @@ var init_license = __esm({
|
|
|
292
300
|
});
|
|
293
301
|
|
|
294
302
|
// src/lib/plan-limits.ts
|
|
295
|
-
import { readFileSync as
|
|
303
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
296
304
|
import path8 from "path";
|
|
297
305
|
var CACHE_PATH2;
|
|
298
306
|
var init_plan_limits = __esm({
|
|
@@ -310,7 +318,7 @@ var init_plan_limits = __esm({
|
|
|
310
318
|
import path9 from "path";
|
|
311
319
|
import os5 from "os";
|
|
312
320
|
import { fileURLToPath } from "url";
|
|
313
|
-
var SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
321
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
314
322
|
var init_tmux_routing = __esm({
|
|
315
323
|
"src/lib/tmux-routing.ts"() {
|
|
316
324
|
"use strict";
|
|
@@ -322,6 +330,7 @@ var init_tmux_routing = __esm({
|
|
|
322
330
|
init_provider_table();
|
|
323
331
|
init_intercom_queue();
|
|
324
332
|
init_plan_limits();
|
|
333
|
+
SPAWN_LOCK_DIR = path9.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
325
334
|
SESSION_CACHE = path9.join(os5.homedir(), ".exe-os", "session-cache");
|
|
326
335
|
INTERCOM_LOG2 = path9.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
327
336
|
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
@@ -364,6 +373,7 @@ var init_tasks_notify = __esm({
|
|
|
364
373
|
init_session_key();
|
|
365
374
|
init_notifications();
|
|
366
375
|
init_transport();
|
|
376
|
+
init_employees();
|
|
367
377
|
}
|
|
368
378
|
});
|
|
369
379
|
|