@askexenow/exe-os 0.9.119 → 0.9.121
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/cli.js +11 -7
- package/dist/bin/exe-boot.js +9 -6
- package/dist/bin/exe-dispatch.js +9 -6
- package/dist/bin/exe-gateway.js +9 -6
- package/dist/bin/exe-session-cleanup.js +9 -6
- package/dist/bin/git-sweep.js +9 -6
- package/dist/bin/intercom-check.js +42 -6
- package/dist/bin/scan-tasks.js +9 -6
- package/dist/gateway/index.js +9 -6
- package/dist/hooks/bug-report-worker.js +9 -6
- package/dist/hooks/codex-stop-task-finalizer.js +9 -6
- package/dist/hooks/commit-complete.js +9 -6
- package/dist/hooks/ingest.js +9 -6
- package/dist/hooks/pre-compact.js +9 -6
- package/dist/hooks/prompt-submit.js +69 -7
- package/dist/hooks/session-end.js +9 -6
- package/dist/index.js +42 -6
- package/dist/lib/consolidation.js +1 -1
- package/dist/lib/exe-daemon.js +45 -8
- package/dist/lib/skill-learning.js +9 -6
- package/dist/lib/tasks.js +9 -6
- package/dist/lib/tmux-routing.js +9 -6
- package/dist/mcp/register-tools.js +485 -324
- package/dist/mcp/server.js +485 -324
- package/dist/mcp/tools/create-task.js +9 -6
- package/dist/mcp/tools/update-task.js +9 -6
- package/dist/runtime/index.js +42 -6
- package/dist/tui/App.js +9 -6
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -9614,7 +9614,7 @@ async function dedupCluster(client, cluster, embedFn) {
|
|
|
9614
9614
|
const vectors = [];
|
|
9615
9615
|
for (const mem of cluster.memories) {
|
|
9616
9616
|
try {
|
|
9617
|
-
const v = await embedFn(mem.raw_text
|
|
9617
|
+
const v = await embedFn(mem.raw_text);
|
|
9618
9618
|
vectors.push({ id: mem.id, vector: v });
|
|
9619
9619
|
} catch {
|
|
9620
9620
|
}
|
|
@@ -9722,7 +9722,15 @@ var init_consolidation = __esm({
|
|
|
9722
9722
|
});
|
|
9723
9723
|
|
|
9724
9724
|
// src/lib/session-registry.ts
|
|
9725
|
+
var session_registry_exports = {};
|
|
9726
|
+
__export(session_registry_exports, {
|
|
9727
|
+
listSessions: () => listSessions,
|
|
9728
|
+
pruneStaleSessions: () => pruneStaleSessions,
|
|
9729
|
+
refreshSessionProject: () => refreshSessionProject,
|
|
9730
|
+
registerSession: () => registerSession
|
|
9731
|
+
});
|
|
9725
9732
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14 } from "fs";
|
|
9733
|
+
import { execSync as execSync8 } from "child_process";
|
|
9726
9734
|
import path17 from "path";
|
|
9727
9735
|
import os7 from "os";
|
|
9728
9736
|
function registerSession(entry) {
|
|
@@ -9739,6 +9747,16 @@ function registerSession(entry) {
|
|
|
9739
9747
|
}
|
|
9740
9748
|
writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
9741
9749
|
}
|
|
9750
|
+
function refreshSessionProject(windowName, projectDir) {
|
|
9751
|
+
const sessions = listSessions();
|
|
9752
|
+
const entry = sessions.find((s) => s.windowName === windowName);
|
|
9753
|
+
if (!entry || entry.projectDir === projectDir) return;
|
|
9754
|
+
entry.projectDir = projectDir;
|
|
9755
|
+
try {
|
|
9756
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
9757
|
+
} catch {
|
|
9758
|
+
}
|
|
9759
|
+
}
|
|
9742
9760
|
function listSessions() {
|
|
9743
9761
|
try {
|
|
9744
9762
|
const raw = readFileSync10(REGISTRY_PATH, "utf8");
|
|
@@ -9747,6 +9765,25 @@ function listSessions() {
|
|
|
9747
9765
|
return [];
|
|
9748
9766
|
}
|
|
9749
9767
|
}
|
|
9768
|
+
function pruneStaleSessions() {
|
|
9769
|
+
const sessions = listSessions();
|
|
9770
|
+
if (sessions.length === 0) return 0;
|
|
9771
|
+
let liveSessions = [];
|
|
9772
|
+
try {
|
|
9773
|
+
liveSessions = execSync8("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
9774
|
+
encoding: "utf8"
|
|
9775
|
+
}).trim().split("\n").filter(Boolean);
|
|
9776
|
+
} catch {
|
|
9777
|
+
return 0;
|
|
9778
|
+
}
|
|
9779
|
+
const liveSet = new Set(liveSessions);
|
|
9780
|
+
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
9781
|
+
const pruned = sessions.length - alive.length;
|
|
9782
|
+
if (pruned > 0) {
|
|
9783
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
9784
|
+
}
|
|
9785
|
+
return pruned;
|
|
9786
|
+
}
|
|
9750
9787
|
var REGISTRY_PATH;
|
|
9751
9788
|
var init_session_registry = __esm({
|
|
9752
9789
|
"src/lib/session-registry.ts"() {
|
|
@@ -9873,14 +9910,14 @@ var init_transport = __esm({
|
|
|
9873
9910
|
});
|
|
9874
9911
|
|
|
9875
9912
|
// src/lib/cc-agent-support.ts
|
|
9876
|
-
import { execSync as
|
|
9913
|
+
import { execSync as execSync9 } from "child_process";
|
|
9877
9914
|
function _resetCcAgentSupportCache() {
|
|
9878
9915
|
_cachedSupport = null;
|
|
9879
9916
|
}
|
|
9880
9917
|
function claudeSupportsAgentFlag() {
|
|
9881
9918
|
if (_cachedSupport !== null) return _cachedSupport;
|
|
9882
9919
|
try {
|
|
9883
|
-
const helpOutput =
|
|
9920
|
+
const helpOutput = execSync9("claude --help 2>&1", {
|
|
9884
9921
|
encoding: "utf-8",
|
|
9885
9922
|
timeout: 5e3
|
|
9886
9923
|
});
|
|
@@ -10844,7 +10881,7 @@ __export(tmux_routing_exports, {
|
|
|
10844
10881
|
spawnEmployee: () => spawnEmployee,
|
|
10845
10882
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
10846
10883
|
});
|
|
10847
|
-
import { execFileSync as execFileSync2, execSync as
|
|
10884
|
+
import { execFileSync as execFileSync2, execSync as execSync10 } from "child_process";
|
|
10848
10885
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, existsSync as existsSync18, appendFileSync, readdirSync as readdirSync5 } from "fs";
|
|
10849
10886
|
import path21 from "path";
|
|
10850
10887
|
import os10 from "os";
|
|
@@ -11681,7 +11718,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11681
11718
|
let booted = false;
|
|
11682
11719
|
for (let i = 0; i < 30; i++) {
|
|
11683
11720
|
try {
|
|
11684
|
-
|
|
11721
|
+
execSync10("sleep 0.5");
|
|
11685
11722
|
} catch {
|
|
11686
11723
|
}
|
|
11687
11724
|
try {
|
|
@@ -12120,7 +12157,7 @@ __export(tasks_crud_exports, {
|
|
|
12120
12157
|
import crypto7 from "crypto";
|
|
12121
12158
|
import path23 from "path";
|
|
12122
12159
|
import os12 from "os";
|
|
12123
|
-
import { execSync as
|
|
12160
|
+
import { execSync as execSync11 } from "child_process";
|
|
12124
12161
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
12125
12162
|
import { existsSync as existsSync20, readFileSync as readFileSync14 } from "fs";
|
|
12126
12163
|
async function writeCheckpoint(input) {
|
|
@@ -12484,14 +12521,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
12484
12521
|
if (!identifier || identifier === "unknown") return true;
|
|
12485
12522
|
try {
|
|
12486
12523
|
if (identifier.startsWith("%")) {
|
|
12487
|
-
const output =
|
|
12524
|
+
const output = execSync11("tmux list-panes -a -F '#{pane_id}'", {
|
|
12488
12525
|
timeout: 2e3,
|
|
12489
12526
|
encoding: "utf8",
|
|
12490
12527
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12491
12528
|
});
|
|
12492
12529
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
12493
12530
|
} else {
|
|
12494
|
-
|
|
12531
|
+
execSync11(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
12495
12532
|
timeout: 2e3,
|
|
12496
12533
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12497
12534
|
});
|
|
@@ -12500,7 +12537,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
12500
12537
|
} catch {
|
|
12501
12538
|
if (identifier.startsWith("%")) return true;
|
|
12502
12539
|
try {
|
|
12503
|
-
|
|
12540
|
+
execSync11("tmux list-sessions", {
|
|
12504
12541
|
timeout: 2e3,
|
|
12505
12542
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12506
12543
|
});
|
|
@@ -12515,12 +12552,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
12515
12552
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
12516
12553
|
try {
|
|
12517
12554
|
const since = new Date(taskCreatedAt).toISOString();
|
|
12518
|
-
const branch =
|
|
12555
|
+
const branch = execSync11(
|
|
12519
12556
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
12520
12557
|
{ encoding: "utf8", timeout: 3e3 }
|
|
12521
12558
|
).trim();
|
|
12522
12559
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
12523
|
-
const commitCount =
|
|
12560
|
+
const commitCount = execSync11(
|
|
12524
12561
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
12525
12562
|
{ encoding: "utf8", timeout: 5e3 }
|
|
12526
12563
|
).trim();
|
|
@@ -12931,15 +12968,18 @@ var init_tasks_notify = __esm({
|
|
|
12931
12968
|
import crypto8 from "crypto";
|
|
12932
12969
|
async function storeBehavior(opts) {
|
|
12933
12970
|
try {
|
|
12934
|
-
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
12971
|
+
const { loadEmployeesSync: loadEmployeesSync2, baseAgentName: baseAgentName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
12935
12972
|
const roster = loadEmployeesSync2();
|
|
12936
|
-
if (roster.length > 0
|
|
12937
|
-
|
|
12938
|
-
|
|
12973
|
+
if (roster.length > 0) {
|
|
12974
|
+
const base = baseAgentName2(opts.agentId, roster);
|
|
12975
|
+
if (!roster.some((e) => e.name === opts.agentId || e.name === base)) {
|
|
12976
|
+
process.stderr.write(
|
|
12977
|
+
`[behaviors] WARNING: Agent "${opts.agentId}" not found in roster. Storing behavior anyway \u2014 may be orphaned.
|
|
12978
|
+
`
|
|
12979
|
+
);
|
|
12939
12980
|
}
|
|
12940
12981
|
}
|
|
12941
|
-
} catch
|
|
12942
|
-
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
12982
|
+
} catch {
|
|
12943
12983
|
}
|
|
12944
12984
|
const client = getClient();
|
|
12945
12985
|
const id = crypto8.randomUUID();
|
|
@@ -14527,6 +14567,275 @@ var init_worker_gate = __esm({
|
|
|
14527
14567
|
}
|
|
14528
14568
|
});
|
|
14529
14569
|
|
|
14570
|
+
// src/lib/tmux-status.ts
|
|
14571
|
+
var tmux_status_exports = {};
|
|
14572
|
+
__export(tmux_status_exports, {
|
|
14573
|
+
capturePaneLines: () => capturePaneLines,
|
|
14574
|
+
formatStatusAll: () => formatStatusAll,
|
|
14575
|
+
formatStatusDeep: () => formatStatusDeep,
|
|
14576
|
+
formatTeamCondensed: () => formatTeamCondensed,
|
|
14577
|
+
getEmployeeStatuses: () => getEmployeeStatuses,
|
|
14578
|
+
getPaneCwd: () => getPaneCwd,
|
|
14579
|
+
inTmux: () => inTmux,
|
|
14580
|
+
listTmuxSessions: () => listTmuxSessions,
|
|
14581
|
+
listTmuxWindows: () => listTmuxWindows,
|
|
14582
|
+
parseActivity: () => parseActivity,
|
|
14583
|
+
parseContextPercentage: () => parseContextPercentage
|
|
14584
|
+
});
|
|
14585
|
+
import { execSync as execSync13 } from "child_process";
|
|
14586
|
+
function inTmux() {
|
|
14587
|
+
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
14588
|
+
const term = process.env.TERM ?? "";
|
|
14589
|
+
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
14590
|
+
try {
|
|
14591
|
+
execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
14592
|
+
encoding: "utf8",
|
|
14593
|
+
timeout: 2e3
|
|
14594
|
+
});
|
|
14595
|
+
return true;
|
|
14596
|
+
} catch {
|
|
14597
|
+
}
|
|
14598
|
+
try {
|
|
14599
|
+
let pid = process.ppid;
|
|
14600
|
+
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
14601
|
+
const comm = execSync13(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
14602
|
+
encoding: "utf8",
|
|
14603
|
+
timeout: 1e3
|
|
14604
|
+
}).trim();
|
|
14605
|
+
if (/tmux/.test(comm)) return true;
|
|
14606
|
+
const ppid = execSync13(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
14607
|
+
encoding: "utf8",
|
|
14608
|
+
timeout: 1e3
|
|
14609
|
+
}).trim();
|
|
14610
|
+
pid = parseInt(ppid, 10);
|
|
14611
|
+
if (isNaN(pid)) break;
|
|
14612
|
+
}
|
|
14613
|
+
} catch {
|
|
14614
|
+
}
|
|
14615
|
+
return false;
|
|
14616
|
+
}
|
|
14617
|
+
function listTmuxSessions() {
|
|
14618
|
+
try {
|
|
14619
|
+
const out = execSync13("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
14620
|
+
encoding: "utf8",
|
|
14621
|
+
timeout: 3e3
|
|
14622
|
+
});
|
|
14623
|
+
return out.trim().split("\n").filter(Boolean);
|
|
14624
|
+
} catch {
|
|
14625
|
+
return [];
|
|
14626
|
+
}
|
|
14627
|
+
}
|
|
14628
|
+
function capturePaneLines(windowName, lines = 10) {
|
|
14629
|
+
try {
|
|
14630
|
+
const out = execSync13(
|
|
14631
|
+
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
14632
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14633
|
+
);
|
|
14634
|
+
return out.split("\n").filter((l) => l.trim());
|
|
14635
|
+
} catch {
|
|
14636
|
+
return [];
|
|
14637
|
+
}
|
|
14638
|
+
}
|
|
14639
|
+
function getPaneCwd(windowName) {
|
|
14640
|
+
try {
|
|
14641
|
+
const out = execSync13(
|
|
14642
|
+
`tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
|
|
14643
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14644
|
+
);
|
|
14645
|
+
return out.trim() || void 0;
|
|
14646
|
+
} catch {
|
|
14647
|
+
return void 0;
|
|
14648
|
+
}
|
|
14649
|
+
}
|
|
14650
|
+
function projectFromPath(dir) {
|
|
14651
|
+
try {
|
|
14652
|
+
const root = execSync13("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
|
|
14653
|
+
encoding: "utf8",
|
|
14654
|
+
timeout: 3e3
|
|
14655
|
+
}).trim();
|
|
14656
|
+
return root.split("/").pop() ?? dir.split("/").pop() ?? "unknown";
|
|
14657
|
+
} catch {
|
|
14658
|
+
return dir.split("/").pop() ?? "unknown";
|
|
14659
|
+
}
|
|
14660
|
+
}
|
|
14661
|
+
function parseActivity(lines) {
|
|
14662
|
+
const joined = lines.join("\n");
|
|
14663
|
+
if (/npm\s+test|vitest|jest|pytest|cargo\s+test/i.test(joined)) {
|
|
14664
|
+
const passMatch = joined.match(/(\d+)\s*passed/i);
|
|
14665
|
+
if (passMatch) return `Running tests \u2014 ${passMatch[1]} passed`;
|
|
14666
|
+
return "Running tests...";
|
|
14667
|
+
}
|
|
14668
|
+
if (/Edit\(|Edit\s*\{/.test(joined)) {
|
|
14669
|
+
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
14670
|
+
if (fileMatch) return `Editing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
14671
|
+
return "Editing code...";
|
|
14672
|
+
}
|
|
14673
|
+
if (/Write\(|Write\s*\{/.test(joined)) {
|
|
14674
|
+
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
14675
|
+
if (fileMatch) return `Writing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
14676
|
+
return "Writing file...";
|
|
14677
|
+
}
|
|
14678
|
+
if (/Bash\(|Bash\s*\{/.test(joined)) {
|
|
14679
|
+
const cmdMatch = joined.match(/command['":\s]+([^\n'"}{]{1,60})/);
|
|
14680
|
+
if (cmdMatch) return `Running: ${cmdMatch[1].trim().slice(0, 50)}`;
|
|
14681
|
+
return "Running command...";
|
|
14682
|
+
}
|
|
14683
|
+
if (/git\s+commit/.test(joined)) return "Committing...";
|
|
14684
|
+
if (/MANDATORY|START WORKING NOW/.test(joined)) return "Starting new task...";
|
|
14685
|
+
if (/All tasks complete|No open tasks/.test(joined)) return "Idle \u2014 queue empty";
|
|
14686
|
+
if (/store_memory|recall_my_memory/.test(joined)) return "Accessing memory...";
|
|
14687
|
+
if (/Agent\(/.test(joined)) return "Spawning sub-agent...";
|
|
14688
|
+
if (/Read\(|Read\s*\{/.test(joined)) return "Reading files...";
|
|
14689
|
+
const lastLine = lines.filter((l) => l.trim()).pop();
|
|
14690
|
+
if (lastLine && lastLine.trim().length > 5) {
|
|
14691
|
+
return lastLine.trim().slice(0, 60);
|
|
14692
|
+
}
|
|
14693
|
+
return "Working...";
|
|
14694
|
+
}
|
|
14695
|
+
function parseContextPercentage(sessionName) {
|
|
14696
|
+
const lines = capturePaneLines(sessionName, 3);
|
|
14697
|
+
for (const line of lines) {
|
|
14698
|
+
const match = line.match(/[█░]+\s*(\d+)%/);
|
|
14699
|
+
if (match) {
|
|
14700
|
+
return parseInt(match[1], 10);
|
|
14701
|
+
}
|
|
14702
|
+
}
|
|
14703
|
+
return null;
|
|
14704
|
+
}
|
|
14705
|
+
function getEmployeeStatuses(employeeNames) {
|
|
14706
|
+
if (!inTmux() || process.env.VITEST) {
|
|
14707
|
+
return employeeNames.map((name) => ({
|
|
14708
|
+
name,
|
|
14709
|
+
hasWindow: false,
|
|
14710
|
+
icon: "\u26AA"
|
|
14711
|
+
}));
|
|
14712
|
+
}
|
|
14713
|
+
const sessions = new Set(listTmuxSessions());
|
|
14714
|
+
return employeeNames.map((name) => {
|
|
14715
|
+
const sessionName = [...sessions].find((s) => s.startsWith(`${name}-`));
|
|
14716
|
+
if (!sessionName) {
|
|
14717
|
+
return { name, hasWindow: false, icon: "\u26AA" };
|
|
14718
|
+
}
|
|
14719
|
+
let paneAlive = true;
|
|
14720
|
+
try {
|
|
14721
|
+
const paneStatus = execSync13(
|
|
14722
|
+
`tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
|
|
14723
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14724
|
+
).trim();
|
|
14725
|
+
if (paneStatus === "1") paneAlive = false;
|
|
14726
|
+
} catch {
|
|
14727
|
+
paneAlive = false;
|
|
14728
|
+
}
|
|
14729
|
+
if (!paneAlive) {
|
|
14730
|
+
return { name, hasWindow: false, icon: "\u26AA" };
|
|
14731
|
+
}
|
|
14732
|
+
const rawLines = capturePaneLines(sessionName, 10);
|
|
14733
|
+
const cwd = getPaneCwd(sessionName);
|
|
14734
|
+
const project = cwd ? projectFromPath(cwd) : void 0;
|
|
14735
|
+
const activity = rawLines.length > 0 ? parseActivity(rawLines) : "Session active";
|
|
14736
|
+
return {
|
|
14737
|
+
name,
|
|
14738
|
+
hasWindow: true,
|
|
14739
|
+
project,
|
|
14740
|
+
activity,
|
|
14741
|
+
rawLines,
|
|
14742
|
+
icon: "\u{1F7E2}"
|
|
14743
|
+
};
|
|
14744
|
+
});
|
|
14745
|
+
}
|
|
14746
|
+
function formatTeamCondensed(statuses) {
|
|
14747
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
14748
|
+
for (const s of statuses) {
|
|
14749
|
+
const proj = s.project ?? "(no session)";
|
|
14750
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
14751
|
+
byProject.get(proj).push(s);
|
|
14752
|
+
}
|
|
14753
|
+
const lines = [];
|
|
14754
|
+
for (const [proj, emps] of byProject) {
|
|
14755
|
+
const parts = emps.map((e) => {
|
|
14756
|
+
if (!e.hasWindow) return `${e.name} offline`;
|
|
14757
|
+
return `${e.name} ${e.activity?.toLowerCase().slice(0, 40) ?? "active"}`;
|
|
14758
|
+
});
|
|
14759
|
+
lines.push(` ${proj}: ${parts.join(", ")}`);
|
|
14760
|
+
}
|
|
14761
|
+
return lines;
|
|
14762
|
+
}
|
|
14763
|
+
function formatStatusAll(statuses, tasks, roles) {
|
|
14764
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
14765
|
+
for (const s of statuses) {
|
|
14766
|
+
const proj = s.project ?? "(no session)";
|
|
14767
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
14768
|
+
byProject.get(proj).push(s);
|
|
14769
|
+
}
|
|
14770
|
+
const lines = [];
|
|
14771
|
+
lines.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
14772
|
+
lines.push("\u2502 REAL-TIME STATUS \u2502");
|
|
14773
|
+
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
14774
|
+
lines.push("");
|
|
14775
|
+
for (const [proj, emps] of byProject) {
|
|
14776
|
+
lines.push(`\u250C\u2500 ${proj} ${"\u2500".repeat(Math.max(0, 66 - proj.length))}\u2510`);
|
|
14777
|
+
if (emps.every((e) => !e.hasWindow)) {
|
|
14778
|
+
lines.push("\u2502 No active employees" + " ".repeat(49) + "\u2502");
|
|
14779
|
+
} else {
|
|
14780
|
+
for (let i = 0; i < emps.length; i++) {
|
|
14781
|
+
const e = emps[i];
|
|
14782
|
+
const role = roles.get(e.name) ?? "";
|
|
14783
|
+
const roleSuffix = role ? ` (${role})` : "";
|
|
14784
|
+
const nameCol = `${e.name}${roleSuffix}`.padEnd(14);
|
|
14785
|
+
const task = tasks.get(e.name);
|
|
14786
|
+
if (!e.hasWindow) {
|
|
14787
|
+
const statusLine = `\u2502 ${nameCol} \u2502 \u26AA No tmux session`;
|
|
14788
|
+
lines.push(statusLine + " ".repeat(Math.max(0, 70 - statusLine.length)) + "\u2502");
|
|
14789
|
+
} else {
|
|
14790
|
+
const actLine = `\u2502 ${nameCol} \u2502 ${e.icon} ${(e.activity ?? "Active").slice(0, 45)}`;
|
|
14791
|
+
lines.push(actLine + " ".repeat(Math.max(0, 70 - actLine.length)) + "\u2502");
|
|
14792
|
+
if (task) {
|
|
14793
|
+
const taskLine = `\u2502${" ".repeat(16)}\u2502 Task: "${task.title.slice(0, 40)}" [${task.priority.toUpperCase()}]`;
|
|
14794
|
+
lines.push(taskLine + " ".repeat(Math.max(0, 70 - taskLine.length)) + "\u2502");
|
|
14795
|
+
}
|
|
14796
|
+
}
|
|
14797
|
+
if (i < emps.length - 1) {
|
|
14798
|
+
lines.push("\u251C" + "\u2500".repeat(16) + "\u253C" + "\u2500".repeat(52) + "\u2524");
|
|
14799
|
+
}
|
|
14800
|
+
}
|
|
14801
|
+
}
|
|
14802
|
+
lines.push("\u2514" + "\u2500".repeat(16) + "\u2534" + "\u2500".repeat(52) + "\u2518");
|
|
14803
|
+
lines.push("");
|
|
14804
|
+
}
|
|
14805
|
+
return lines.join("\n");
|
|
14806
|
+
}
|
|
14807
|
+
function formatStatusDeep(status2, task, role) {
|
|
14808
|
+
const lines = [];
|
|
14809
|
+
const header = `${status2.name} (${role}) \u2014 ${status2.project ?? "unknown project"}`;
|
|
14810
|
+
lines.push(`\u250C\u2500 ${header} ${"\u2500".repeat(Math.max(0, 66 - header.length))}\u2510`);
|
|
14811
|
+
if (task) {
|
|
14812
|
+
const taskLine = `\u2502 Task: "${task.title.slice(0, 50)}" [${task.priority.toUpperCase()}]`;
|
|
14813
|
+
lines.push(taskLine + " ".repeat(Math.max(0, 70 - taskLine.length)) + "\u2502");
|
|
14814
|
+
lines.push("\u251C" + "\u2500".repeat(69) + "\u2524");
|
|
14815
|
+
}
|
|
14816
|
+
if (!status2.hasWindow) {
|
|
14817
|
+
lines.push("\u2502 No tmux session" + " ".repeat(53) + "\u2502");
|
|
14818
|
+
} else if (status2.rawLines && status2.rawLines.length > 0) {
|
|
14819
|
+
lines.push("\u2502 Recent activity:" + " ".repeat(52) + "\u2502");
|
|
14820
|
+
for (const line of status2.rawLines.slice(-10)) {
|
|
14821
|
+
const trimmed = line.slice(0, 65);
|
|
14822
|
+
const padded = `\u2502 ${trimmed}`;
|
|
14823
|
+
lines.push(padded + " ".repeat(Math.max(0, 70 - padded.length)) + "\u2502");
|
|
14824
|
+
}
|
|
14825
|
+
} else {
|
|
14826
|
+
lines.push("\u2502 Session active \u2014 no recent output" + " ".repeat(35) + "\u2502");
|
|
14827
|
+
}
|
|
14828
|
+
lines.push("\u2514" + "\u2500".repeat(69) + "\u2518");
|
|
14829
|
+
return lines.join("\n");
|
|
14830
|
+
}
|
|
14831
|
+
var listTmuxWindows;
|
|
14832
|
+
var init_tmux_status = __esm({
|
|
14833
|
+
"src/lib/tmux-status.ts"() {
|
|
14834
|
+
"use strict";
|
|
14835
|
+
listTmuxWindows = listTmuxSessions;
|
|
14836
|
+
}
|
|
14837
|
+
});
|
|
14838
|
+
|
|
14530
14839
|
// src/lib/key-backup-status.ts
|
|
14531
14840
|
var key_backup_status_exports = {};
|
|
14532
14841
|
__export(key_backup_status_exports, {
|
|
@@ -14905,307 +15214,81 @@ function readAllBehaviors() {
|
|
|
14905
15214
|
priority: entry.get("priority"),
|
|
14906
15215
|
created_at: entry.get("created_at"),
|
|
14907
15216
|
updated_at: entry.get("updated_at")
|
|
14908
|
-
});
|
|
14909
|
-
});
|
|
14910
|
-
return records;
|
|
14911
|
-
}
|
|
14912
|
-
function onUpdate(callback) {
|
|
14913
|
-
const d = initCrdtDoc();
|
|
14914
|
-
const handler = (update) => callback(update);
|
|
14915
|
-
d.on("update", handler);
|
|
14916
|
-
return () => d.off("update", handler);
|
|
14917
|
-
}
|
|
14918
|
-
function persistState() {
|
|
14919
|
-
if (!doc) return;
|
|
14920
|
-
try {
|
|
14921
|
-
const sp = getStatePath();
|
|
14922
|
-
const dir = path36.dirname(sp);
|
|
14923
|
-
if (!existsSync32(dir)) mkdirSync16(dir, { recursive: true });
|
|
14924
|
-
const state = Y.encodeStateAsUpdate(doc);
|
|
14925
|
-
writeFileSync17(sp, Buffer.from(state));
|
|
14926
|
-
} catch {
|
|
14927
|
-
}
|
|
14928
|
-
}
|
|
14929
|
-
function isCrdtSyncEnabled() {
|
|
14930
|
-
return process.env.EXE_CRDT_SYNC !== "0";
|
|
14931
|
-
}
|
|
14932
|
-
async function rebuildFromDb() {
|
|
14933
|
-
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
14934
|
-
const client = getClient2();
|
|
14935
|
-
const result3 = await client.execute(
|
|
14936
|
-
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
14937
|
-
);
|
|
14938
|
-
const memories = result3.rows.map((row) => ({
|
|
14939
|
-
id: String(row.id),
|
|
14940
|
-
agent_id: row.agent_id,
|
|
14941
|
-
agent_role: row.agent_role,
|
|
14942
|
-
session_id: row.session_id,
|
|
14943
|
-
timestamp: row.timestamp,
|
|
14944
|
-
tool_name: row.tool_name,
|
|
14945
|
-
project_name: row.project_name,
|
|
14946
|
-
has_error: row.has_error,
|
|
14947
|
-
raw_text: row.raw_text,
|
|
14948
|
-
version: row.version,
|
|
14949
|
-
author_device_id: row.author_device_id,
|
|
14950
|
-
scope: row.scope
|
|
14951
|
-
}));
|
|
14952
|
-
const count = importExistingMemories(memories);
|
|
14953
|
-
persistState();
|
|
14954
|
-
return count;
|
|
14955
|
-
}
|
|
14956
|
-
function destroyCrdtDoc() {
|
|
14957
|
-
if (doc) {
|
|
14958
|
-
doc.destroy();
|
|
14959
|
-
doc = null;
|
|
14960
|
-
}
|
|
14961
|
-
}
|
|
14962
|
-
var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
14963
|
-
var init_crdt_sync = __esm({
|
|
14964
|
-
"src/lib/crdt-sync.ts"() {
|
|
14965
|
-
"use strict";
|
|
14966
|
-
DEFAULT_STATE_PATH = path36.join(homedir7(), ".exe-os", "crdt-state.bin");
|
|
14967
|
-
_statePathOverride = null;
|
|
14968
|
-
doc = null;
|
|
14969
|
-
}
|
|
14970
|
-
});
|
|
14971
|
-
|
|
14972
|
-
// src/lib/pg-ssl.ts
|
|
14973
|
-
var pg_ssl_exports = {};
|
|
14974
|
-
__export(pg_ssl_exports, {
|
|
14975
|
-
pgSslConfig: () => pgSslConfig
|
|
14976
|
-
});
|
|
14977
|
-
function pgSslConfig() {
|
|
14978
|
-
if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
|
|
14979
|
-
return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
|
|
14980
|
-
}
|
|
14981
|
-
var init_pg_ssl = __esm({
|
|
14982
|
-
"src/lib/pg-ssl.ts"() {
|
|
14983
|
-
"use strict";
|
|
14984
|
-
}
|
|
14985
|
-
});
|
|
14986
|
-
|
|
14987
|
-
// src/lib/tmux-status.ts
|
|
14988
|
-
import { execSync as execSync13 } from "child_process";
|
|
14989
|
-
function inTmux() {
|
|
14990
|
-
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
14991
|
-
const term = process.env.TERM ?? "";
|
|
14992
|
-
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
14993
|
-
try {
|
|
14994
|
-
execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
14995
|
-
encoding: "utf8",
|
|
14996
|
-
timeout: 2e3
|
|
14997
|
-
});
|
|
14998
|
-
return true;
|
|
14999
|
-
} catch {
|
|
15000
|
-
}
|
|
15001
|
-
try {
|
|
15002
|
-
let pid = process.ppid;
|
|
15003
|
-
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
15004
|
-
const comm = execSync13(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
15005
|
-
encoding: "utf8",
|
|
15006
|
-
timeout: 1e3
|
|
15007
|
-
}).trim();
|
|
15008
|
-
if (/tmux/.test(comm)) return true;
|
|
15009
|
-
const ppid = execSync13(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
15010
|
-
encoding: "utf8",
|
|
15011
|
-
timeout: 1e3
|
|
15012
|
-
}).trim();
|
|
15013
|
-
pid = parseInt(ppid, 10);
|
|
15014
|
-
if (isNaN(pid)) break;
|
|
15015
|
-
}
|
|
15016
|
-
} catch {
|
|
15017
|
-
}
|
|
15018
|
-
return false;
|
|
15019
|
-
}
|
|
15020
|
-
function listTmuxSessions() {
|
|
15021
|
-
try {
|
|
15022
|
-
const out = execSync13("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
15023
|
-
encoding: "utf8",
|
|
15024
|
-
timeout: 3e3
|
|
15025
|
-
});
|
|
15026
|
-
return out.trim().split("\n").filter(Boolean);
|
|
15027
|
-
} catch {
|
|
15028
|
-
return [];
|
|
15029
|
-
}
|
|
15030
|
-
}
|
|
15031
|
-
function capturePaneLines(windowName, lines = 10) {
|
|
15032
|
-
try {
|
|
15033
|
-
const out = execSync13(
|
|
15034
|
-
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
15035
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
15036
|
-
);
|
|
15037
|
-
return out.split("\n").filter((l) => l.trim());
|
|
15038
|
-
} catch {
|
|
15039
|
-
return [];
|
|
15040
|
-
}
|
|
15217
|
+
});
|
|
15218
|
+
});
|
|
15219
|
+
return records;
|
|
15041
15220
|
}
|
|
15042
|
-
function
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
);
|
|
15048
|
-
return out.trim() || void 0;
|
|
15049
|
-
} catch {
|
|
15050
|
-
return void 0;
|
|
15051
|
-
}
|
|
15221
|
+
function onUpdate(callback) {
|
|
15222
|
+
const d = initCrdtDoc();
|
|
15223
|
+
const handler = (update) => callback(update);
|
|
15224
|
+
d.on("update", handler);
|
|
15225
|
+
return () => d.off("update", handler);
|
|
15052
15226
|
}
|
|
15053
|
-
function
|
|
15227
|
+
function persistState() {
|
|
15228
|
+
if (!doc) return;
|
|
15054
15229
|
try {
|
|
15055
|
-
const
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15230
|
+
const sp = getStatePath();
|
|
15231
|
+
const dir = path36.dirname(sp);
|
|
15232
|
+
if (!existsSync32(dir)) mkdirSync16(dir, { recursive: true });
|
|
15233
|
+
const state = Y.encodeStateAsUpdate(doc);
|
|
15234
|
+
writeFileSync17(sp, Buffer.from(state));
|
|
15060
15235
|
} catch {
|
|
15061
|
-
return dir.split("/").pop() ?? "unknown";
|
|
15062
15236
|
}
|
|
15063
15237
|
}
|
|
15064
|
-
function
|
|
15065
|
-
|
|
15066
|
-
if (/npm\s+test|vitest|jest|pytest|cargo\s+test/i.test(joined)) {
|
|
15067
|
-
const passMatch = joined.match(/(\d+)\s*passed/i);
|
|
15068
|
-
if (passMatch) return `Running tests \u2014 ${passMatch[1]} passed`;
|
|
15069
|
-
return "Running tests...";
|
|
15070
|
-
}
|
|
15071
|
-
if (/Edit\(|Edit\s*\{/.test(joined)) {
|
|
15072
|
-
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
15073
|
-
if (fileMatch) return `Editing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
15074
|
-
return "Editing code...";
|
|
15075
|
-
}
|
|
15076
|
-
if (/Write\(|Write\s*\{/.test(joined)) {
|
|
15077
|
-
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
15078
|
-
if (fileMatch) return `Writing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
15079
|
-
return "Writing file...";
|
|
15080
|
-
}
|
|
15081
|
-
if (/Bash\(|Bash\s*\{/.test(joined)) {
|
|
15082
|
-
const cmdMatch = joined.match(/command['":\s]+([^\n'"}{]{1,60})/);
|
|
15083
|
-
if (cmdMatch) return `Running: ${cmdMatch[1].trim().slice(0, 50)}`;
|
|
15084
|
-
return "Running command...";
|
|
15085
|
-
}
|
|
15086
|
-
if (/git\s+commit/.test(joined)) return "Committing...";
|
|
15087
|
-
if (/MANDATORY|START WORKING NOW/.test(joined)) return "Starting new task...";
|
|
15088
|
-
if (/All tasks complete|No open tasks/.test(joined)) return "Idle \u2014 queue empty";
|
|
15089
|
-
if (/store_memory|recall_my_memory/.test(joined)) return "Accessing memory...";
|
|
15090
|
-
if (/Agent\(/.test(joined)) return "Spawning sub-agent...";
|
|
15091
|
-
if (/Read\(|Read\s*\{/.test(joined)) return "Reading files...";
|
|
15092
|
-
const lastLine = lines.filter((l) => l.trim()).pop();
|
|
15093
|
-
if (lastLine && lastLine.trim().length > 5) {
|
|
15094
|
-
return lastLine.trim().slice(0, 60);
|
|
15095
|
-
}
|
|
15096
|
-
return "Working...";
|
|
15238
|
+
function isCrdtSyncEnabled() {
|
|
15239
|
+
return process.env.EXE_CRDT_SYNC !== "0";
|
|
15097
15240
|
}
|
|
15098
|
-
function
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
}
|
|
15122
|
-
if (!paneAlive) {
|
|
15123
|
-
return { name, hasWindow: false, icon: "\u26AA" };
|
|
15124
|
-
}
|
|
15125
|
-
const rawLines = capturePaneLines(sessionName, 10);
|
|
15126
|
-
const cwd = getPaneCwd(sessionName);
|
|
15127
|
-
const project = cwd ? projectFromPath(cwd) : void 0;
|
|
15128
|
-
const activity = rawLines.length > 0 ? parseActivity(rawLines) : "Session active";
|
|
15129
|
-
return {
|
|
15130
|
-
name,
|
|
15131
|
-
hasWindow: true,
|
|
15132
|
-
project,
|
|
15133
|
-
activity,
|
|
15134
|
-
rawLines,
|
|
15135
|
-
icon: "\u{1F7E2}"
|
|
15136
|
-
};
|
|
15137
|
-
});
|
|
15241
|
+
async function rebuildFromDb() {
|
|
15242
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
15243
|
+
const client = getClient2();
|
|
15244
|
+
const result3 = await client.execute(
|
|
15245
|
+
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
15246
|
+
);
|
|
15247
|
+
const memories = result3.rows.map((row) => ({
|
|
15248
|
+
id: String(row.id),
|
|
15249
|
+
agent_id: row.agent_id,
|
|
15250
|
+
agent_role: row.agent_role,
|
|
15251
|
+
session_id: row.session_id,
|
|
15252
|
+
timestamp: row.timestamp,
|
|
15253
|
+
tool_name: row.tool_name,
|
|
15254
|
+
project_name: row.project_name,
|
|
15255
|
+
has_error: row.has_error,
|
|
15256
|
+
raw_text: row.raw_text,
|
|
15257
|
+
version: row.version,
|
|
15258
|
+
author_device_id: row.author_device_id,
|
|
15259
|
+
scope: row.scope
|
|
15260
|
+
}));
|
|
15261
|
+
const count = importExistingMemories(memories);
|
|
15262
|
+
persistState();
|
|
15263
|
+
return count;
|
|
15138
15264
|
}
|
|
15139
|
-
function
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
15144
|
-
byProject.get(proj).push(s);
|
|
15145
|
-
}
|
|
15146
|
-
const lines = [];
|
|
15147
|
-
lines.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
15148
|
-
lines.push("\u2502 REAL-TIME STATUS \u2502");
|
|
15149
|
-
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
15150
|
-
lines.push("");
|
|
15151
|
-
for (const [proj, emps] of byProject) {
|
|
15152
|
-
lines.push(`\u250C\u2500 ${proj} ${"\u2500".repeat(Math.max(0, 66 - proj.length))}\u2510`);
|
|
15153
|
-
if (emps.every((e) => !e.hasWindow)) {
|
|
15154
|
-
lines.push("\u2502 No active employees" + " ".repeat(49) + "\u2502");
|
|
15155
|
-
} else {
|
|
15156
|
-
for (let i = 0; i < emps.length; i++) {
|
|
15157
|
-
const e = emps[i];
|
|
15158
|
-
const role = roles.get(e.name) ?? "";
|
|
15159
|
-
const roleSuffix = role ? ` (${role})` : "";
|
|
15160
|
-
const nameCol = `${e.name}${roleSuffix}`.padEnd(14);
|
|
15161
|
-
const task = tasks.get(e.name);
|
|
15162
|
-
if (!e.hasWindow) {
|
|
15163
|
-
const statusLine = `\u2502 ${nameCol} \u2502 \u26AA No tmux session`;
|
|
15164
|
-
lines.push(statusLine + " ".repeat(Math.max(0, 70 - statusLine.length)) + "\u2502");
|
|
15165
|
-
} else {
|
|
15166
|
-
const actLine = `\u2502 ${nameCol} \u2502 ${e.icon} ${(e.activity ?? "Active").slice(0, 45)}`;
|
|
15167
|
-
lines.push(actLine + " ".repeat(Math.max(0, 70 - actLine.length)) + "\u2502");
|
|
15168
|
-
if (task) {
|
|
15169
|
-
const taskLine = `\u2502${" ".repeat(16)}\u2502 Task: "${task.title.slice(0, 40)}" [${task.priority.toUpperCase()}]`;
|
|
15170
|
-
lines.push(taskLine + " ".repeat(Math.max(0, 70 - taskLine.length)) + "\u2502");
|
|
15171
|
-
}
|
|
15172
|
-
}
|
|
15173
|
-
if (i < emps.length - 1) {
|
|
15174
|
-
lines.push("\u251C" + "\u2500".repeat(16) + "\u253C" + "\u2500".repeat(52) + "\u2524");
|
|
15175
|
-
}
|
|
15176
|
-
}
|
|
15177
|
-
}
|
|
15178
|
-
lines.push("\u2514" + "\u2500".repeat(16) + "\u2534" + "\u2500".repeat(52) + "\u2518");
|
|
15179
|
-
lines.push("");
|
|
15265
|
+
function destroyCrdtDoc() {
|
|
15266
|
+
if (doc) {
|
|
15267
|
+
doc.destroy();
|
|
15268
|
+
doc = null;
|
|
15180
15269
|
}
|
|
15181
|
-
return lines.join("\n");
|
|
15182
15270
|
}
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
lines.push("\u251C" + "\u2500".repeat(69) + "\u2524");
|
|
15191
|
-
}
|
|
15192
|
-
if (!status2.hasWindow) {
|
|
15193
|
-
lines.push("\u2502 No tmux session" + " ".repeat(53) + "\u2502");
|
|
15194
|
-
} else if (status2.rawLines && status2.rawLines.length > 0) {
|
|
15195
|
-
lines.push("\u2502 Recent activity:" + " ".repeat(52) + "\u2502");
|
|
15196
|
-
for (const line of status2.rawLines.slice(-10)) {
|
|
15197
|
-
const trimmed = line.slice(0, 65);
|
|
15198
|
-
const padded = `\u2502 ${trimmed}`;
|
|
15199
|
-
lines.push(padded + " ".repeat(Math.max(0, 70 - padded.length)) + "\u2502");
|
|
15200
|
-
}
|
|
15201
|
-
} else {
|
|
15202
|
-
lines.push("\u2502 Session active \u2014 no recent output" + " ".repeat(35) + "\u2502");
|
|
15271
|
+
var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
15272
|
+
var init_crdt_sync = __esm({
|
|
15273
|
+
"src/lib/crdt-sync.ts"() {
|
|
15274
|
+
"use strict";
|
|
15275
|
+
DEFAULT_STATE_PATH = path36.join(homedir7(), ".exe-os", "crdt-state.bin");
|
|
15276
|
+
_statePathOverride = null;
|
|
15277
|
+
doc = null;
|
|
15203
15278
|
}
|
|
15204
|
-
|
|
15205
|
-
|
|
15279
|
+
});
|
|
15280
|
+
|
|
15281
|
+
// src/lib/pg-ssl.ts
|
|
15282
|
+
var pg_ssl_exports = {};
|
|
15283
|
+
__export(pg_ssl_exports, {
|
|
15284
|
+
pgSslConfig: () => pgSslConfig
|
|
15285
|
+
});
|
|
15286
|
+
function pgSslConfig() {
|
|
15287
|
+
if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
|
|
15288
|
+
return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
|
|
15206
15289
|
}
|
|
15207
|
-
var
|
|
15208
|
-
"src/lib/
|
|
15290
|
+
var init_pg_ssl = __esm({
|
|
15291
|
+
"src/lib/pg-ssl.ts"() {
|
|
15209
15292
|
"use strict";
|
|
15210
15293
|
}
|
|
15211
15294
|
});
|
|
@@ -17483,6 +17566,23 @@ function registerStoreBehavior(server2) {
|
|
|
17483
17566
|
} else {
|
|
17484
17567
|
resolvedProject = getProjectName();
|
|
17485
17568
|
}
|
|
17569
|
+
try {
|
|
17570
|
+
const { loadEmployeesSync: loadEmployeesSync2, baseAgentName: baseAgentName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
17571
|
+
const roster = loadEmployeesSync2();
|
|
17572
|
+
if (roster.length > 0) {
|
|
17573
|
+
const base = baseAgentName2(resolvedAgent, roster);
|
|
17574
|
+
if (!roster.some((e) => e.name === resolvedAgent || e.name === base)) {
|
|
17575
|
+
return {
|
|
17576
|
+
content: [{
|
|
17577
|
+
type: "text",
|
|
17578
|
+
text: `Error: Agent "${resolvedAgent}" not found in roster. Cannot store behavior for non-existent agent. Available agents: ${roster.map((e) => e.name).join(", ")}`
|
|
17579
|
+
}],
|
|
17580
|
+
isError: true
|
|
17581
|
+
};
|
|
17582
|
+
}
|
|
17583
|
+
}
|
|
17584
|
+
} catch {
|
|
17585
|
+
}
|
|
17486
17586
|
const id = await storeBehavior({
|
|
17487
17587
|
agentId: resolvedAgent,
|
|
17488
17588
|
content,
|
|
@@ -19981,7 +20081,8 @@ function summarizeChunk(chunk, filePath) {
|
|
|
19981
20081
|
}
|
|
19982
20082
|
}
|
|
19983
20083
|
function isChunkable(filePath) {
|
|
19984
|
-
|
|
20084
|
+
const lang = languageForFile(filePath);
|
|
20085
|
+
return lang === "typescript" || lang === "javascript";
|
|
19985
20086
|
}
|
|
19986
20087
|
|
|
19987
20088
|
// src/lib/graph-rag.ts
|
|
@@ -21231,7 +21332,7 @@ import { z as z55 } from "zod";
|
|
|
21231
21332
|
init_tmux_routing();
|
|
21232
21333
|
init_task_scope();
|
|
21233
21334
|
init_employees();
|
|
21234
|
-
import { execSync as
|
|
21335
|
+
import { execSync as execSync12 } from "child_process";
|
|
21235
21336
|
import { existsSync as existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
|
|
21236
21337
|
import { homedir as homedir6 } from "os";
|
|
21237
21338
|
import { join as join4 } from "path";
|
|
@@ -21366,12 +21467,50 @@ function countAliveWorkers() {
|
|
|
21366
21467
|
}
|
|
21367
21468
|
return { alive, stale, reservations };
|
|
21368
21469
|
}
|
|
21470
|
+
async function getAgentSessionLoad() {
|
|
21471
|
+
const sessions = [];
|
|
21472
|
+
try {
|
|
21473
|
+
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
21474
|
+
const { listTmuxSessions: listTmuxSessions2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
21475
|
+
const { isCoordinatorName: isCoordinatorName2, baseAgentName: baseAgentName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
21476
|
+
const { isExeSession: isExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
21477
|
+
const registered = listSessions2();
|
|
21478
|
+
const liveTmux = new Set(listTmuxSessions2());
|
|
21479
|
+
const liveAgentSessions = registered.filter(
|
|
21480
|
+
(s) => liveTmux.has(s.windowName) && !isCoordinatorName2(s.agentId) && !isExeSession2(s.windowName)
|
|
21481
|
+
);
|
|
21482
|
+
if (liveAgentSessions.length === 0) {
|
|
21483
|
+
return { sessions: [], totalLive: 0, busySessions: 0, idleSessions: 0 };
|
|
21484
|
+
}
|
|
21485
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
21486
|
+
const client = getClient2();
|
|
21487
|
+
for (const s of liveAgentSessions) {
|
|
21488
|
+
const agent = baseAgentName2(s.agentId);
|
|
21489
|
+
const result3 = await client.execute({
|
|
21490
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
21491
|
+
WHERE assigned_to = ? AND status IN ('open', 'in_progress')`,
|
|
21492
|
+
args: [agent]
|
|
21493
|
+
});
|
|
21494
|
+
const taskCount = Number(result3.rows[0]?.cnt ?? 0);
|
|
21495
|
+
sessions.push({
|
|
21496
|
+
agentId: s.agentId,
|
|
21497
|
+
sessionName: s.windowName,
|
|
21498
|
+
taskCount,
|
|
21499
|
+
idle: taskCount === 0
|
|
21500
|
+
});
|
|
21501
|
+
}
|
|
21502
|
+
} catch {
|
|
21503
|
+
}
|
|
21504
|
+
const busySessions = sessions.filter((s) => !s.idle).length;
|
|
21505
|
+
const idleSessions = sessions.filter((s) => s.idle).length;
|
|
21506
|
+
return { sessions, totalLive: sessions.length, busySessions, idleSessions };
|
|
21507
|
+
}
|
|
21369
21508
|
function registerGetWorkerGate(server2) {
|
|
21370
21509
|
server2.registerTool(
|
|
21371
21510
|
"get_worker_gate",
|
|
21372
21511
|
{
|
|
21373
21512
|
title: "Get Worker Gate",
|
|
21374
|
-
description: "Check worker concurrency gate
|
|
21513
|
+
description: "Check worker concurrency gate and agent session utilization. Reports embed/summary worker slots AND live agent sessions with task load. Idle sessions (alive tmux, zero tasks) are excluded from the effective session count \u2014 they should not block new dispatches.",
|
|
21375
21514
|
inputSchema: {
|
|
21376
21515
|
_placeholder: z56.string().optional().describe("No input required")
|
|
21377
21516
|
}
|
|
@@ -21380,7 +21519,7 @@ function registerGetWorkerGate(server2) {
|
|
|
21380
21519
|
try {
|
|
21381
21520
|
const { alive, stale, reservations } = countAliveWorkers();
|
|
21382
21521
|
const totalActive = alive + reservations;
|
|
21383
|
-
const
|
|
21522
|
+
const canSpawnWorker = totalActive < MAX_CONCURRENT_WORKERS;
|
|
21384
21523
|
const lines = [];
|
|
21385
21524
|
lines.push("## Worker Gate Status\n");
|
|
21386
21525
|
lines.push("| Metric | Value |");
|
|
@@ -21389,7 +21528,29 @@ function registerGetWorkerGate(server2) {
|
|
|
21389
21528
|
lines.push(`| Pending reservations | ${reservations} |`);
|
|
21390
21529
|
lines.push(`| Stale PID files | ${stale} |`);
|
|
21391
21530
|
lines.push(`| Max concurrent | ${MAX_CONCURRENT_WORKERS} |`);
|
|
21392
|
-
lines.push(`| Can spawn new worker | ${
|
|
21531
|
+
lines.push(`| Can spawn new worker | ${canSpawnWorker ? "yes" : "no"} |`);
|
|
21532
|
+
const load = await getAgentSessionLoad();
|
|
21533
|
+
if (load.totalLive > 0) {
|
|
21534
|
+
lines.push("");
|
|
21535
|
+
lines.push("## Agent Session Load\n");
|
|
21536
|
+
lines.push("| Session | Agent | Tasks | Status |");
|
|
21537
|
+
lines.push("|---------|-------|-------|--------|");
|
|
21538
|
+
for (const s of load.sessions) {
|
|
21539
|
+
const status2 = s.idle ? "idle (no tasks)" : `busy (${s.taskCount} task${s.taskCount === 1 ? "" : "s"})`;
|
|
21540
|
+
lines.push(`| ${s.sessionName} | ${s.agentId} | ${s.taskCount} | ${status2} |`);
|
|
21541
|
+
}
|
|
21542
|
+
lines.push("");
|
|
21543
|
+
lines.push(`| Total live sessions | ${load.totalLive} |`);
|
|
21544
|
+
lines.push(`| Busy (with tasks) | ${load.busySessions} |`);
|
|
21545
|
+
lines.push(`| Idle (no tasks) | ${load.idleSessions} |`);
|
|
21546
|
+
lines.push("");
|
|
21547
|
+
lines.push(
|
|
21548
|
+
`**Effective session load: ${load.busySessions}** (idle sessions excluded). ${load.idleSessions > 0 ? `${load.idleSessions} idle session${load.idleSessions === 1 ? "" : "s"} can be reused or killed before spawning new agents.` : "All sessions are actively working."}`
|
|
21549
|
+
);
|
|
21550
|
+
} else {
|
|
21551
|
+
lines.push("");
|
|
21552
|
+
lines.push("No live agent sessions.");
|
|
21553
|
+
}
|
|
21393
21554
|
return {
|
|
21394
21555
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
21395
21556
|
};
|
|
@@ -25706,7 +25867,7 @@ function isScheduledTrigger(trigger) {
|
|
|
25706
25867
|
init_database();
|
|
25707
25868
|
init_store();
|
|
25708
25869
|
import crypto16 from "crypto";
|
|
25709
|
-
import { execSync as
|
|
25870
|
+
import { execSync as execSync14 } from "child_process";
|
|
25710
25871
|
var CRON_FIELD = /^[\d*/,\-]+$/;
|
|
25711
25872
|
function isValidCron(cron) {
|
|
25712
25873
|
const fields = cron.trim().split(/\s+/);
|
|
@@ -25832,7 +25993,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
25832
25993
|
const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
|
|
25833
25994
|
const escapedPrompt = prompt.replace(/"/g, '\\"');
|
|
25834
25995
|
const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
|
|
25835
|
-
|
|
25996
|
+
execSync14(
|
|
25836
25997
|
`(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
|
|
25837
25998
|
{ timeout: 5e3, stdio: "ignore" }
|
|
25838
25999
|
);
|
|
@@ -29514,7 +29675,7 @@ if (isMainModule(import.meta.url)) {
|
|
|
29514
29675
|
// src/bin/exe-healthcheck.ts
|
|
29515
29676
|
import { existsSync as existsSync40, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "fs";
|
|
29516
29677
|
import path52 from "path";
|
|
29517
|
-
import { execSync as
|
|
29678
|
+
import { execSync as execSync15 } from "child_process";
|
|
29518
29679
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
29519
29680
|
init_config();
|
|
29520
29681
|
function findPackageRoot2() {
|
|
@@ -29612,7 +29773,7 @@ function checkTaskSystem(pkgRoot) {
|
|
|
29612
29773
|
return results;
|
|
29613
29774
|
}
|
|
29614
29775
|
try {
|
|
29615
|
-
|
|
29776
|
+
execSync15(`node "${scannerPath}" /tmp/nonexistent-healthcheck-test --format=json 2>/dev/null`, {
|
|
29616
29777
|
timeout: 1e4,
|
|
29617
29778
|
encoding: "utf-8"
|
|
29618
29779
|
});
|
|
@@ -29635,7 +29796,7 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
29635
29796
|
return results;
|
|
29636
29797
|
}
|
|
29637
29798
|
try {
|
|
29638
|
-
|
|
29799
|
+
execSync15(`node --check "${workerPath}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
29639
29800
|
results.push({ name: "workers/ingest-worker", pass: true, detail: "ingest-worker.js parses OK" });
|
|
29640
29801
|
} catch (err) {
|
|
29641
29802
|
results.push({
|
|
@@ -29651,7 +29812,7 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
29651
29812
|
const hooksFailed = [];
|
|
29652
29813
|
for (const hook of hookFiles) {
|
|
29653
29814
|
try {
|
|
29654
|
-
|
|
29815
|
+
execSync15(`node --check "${path52.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
29655
29816
|
hooksPassed++;
|
|
29656
29817
|
} catch {
|
|
29657
29818
|
hooksFailed.push(hook);
|
|
@@ -29693,7 +29854,7 @@ function checkMcpTransport() {
|
|
|
29693
29854
|
if (daemonAlive && existsSync40(tokenPath)) {
|
|
29694
29855
|
try {
|
|
29695
29856
|
const token = readFileSync33(tokenPath, "utf8").trim();
|
|
29696
|
-
const response =
|
|
29857
|
+
const response = execSync15(
|
|
29697
29858
|
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
29698
29859
|
{ encoding: "utf8", timeout: 3e3 }
|
|
29699
29860
|
);
|
|
@@ -29744,7 +29905,7 @@ function checkClaudeCodeInstall() {
|
|
|
29744
29905
|
});
|
|
29745
29906
|
}
|
|
29746
29907
|
try {
|
|
29747
|
-
const claudePath =
|
|
29908
|
+
const claudePath = execSync15("which claude 2>/dev/null || true", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
29748
29909
|
if (!claudePath) {
|
|
29749
29910
|
results.push({
|
|
29750
29911
|
name: "cc/cli-binary",
|
|
@@ -29754,7 +29915,7 @@ function checkClaudeCodeInstall() {
|
|
|
29754
29915
|
} else {
|
|
29755
29916
|
let resolved = claudePath;
|
|
29756
29917
|
try {
|
|
29757
|
-
resolved =
|
|
29918
|
+
resolved = execSync15(`readlink -f "${claudePath}" 2>/dev/null || readlink "${claudePath}" 2>/dev/null || echo "${claudePath}"`, {
|
|
29758
29919
|
encoding: "utf8",
|
|
29759
29920
|
timeout: 5e3
|
|
29760
29921
|
}).trim();
|
|
@@ -29838,7 +29999,7 @@ function checkClaudeCodeInstall() {
|
|
|
29838
29999
|
});
|
|
29839
30000
|
}
|
|
29840
30001
|
try {
|
|
29841
|
-
const ccVersion =
|
|
30002
|
+
const ccVersion = execSync15("claude --version 2>/dev/null || echo unknown", {
|
|
29842
30003
|
encoding: "utf8",
|
|
29843
30004
|
timeout: 5e3
|
|
29844
30005
|
}).trim();
|
|
@@ -29886,7 +30047,7 @@ if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-healt
|
|
|
29886
30047
|
}
|
|
29887
30048
|
|
|
29888
30049
|
// src/lib/update-check.ts
|
|
29889
|
-
import { execSync as
|
|
30050
|
+
import { execSync as execSync16 } from "child_process";
|
|
29890
30051
|
import { readFileSync as readFileSync34 } from "fs";
|
|
29891
30052
|
import path53 from "path";
|
|
29892
30053
|
function getLocalVersion(packageRoot) {
|
|
@@ -29896,7 +30057,7 @@ function getLocalVersion(packageRoot) {
|
|
|
29896
30057
|
}
|
|
29897
30058
|
function getRemoteVersion() {
|
|
29898
30059
|
try {
|
|
29899
|
-
const output =
|
|
30060
|
+
const output = execSync16("npm view @askexenow/exe-os version", {
|
|
29900
30061
|
encoding: "utf-8",
|
|
29901
30062
|
timeout: 15e3,
|
|
29902
30063
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -31384,7 +31545,7 @@ var ToolCapabilityIndex = class {
|
|
|
31384
31545
|
let vector = null;
|
|
31385
31546
|
if (embedFn) {
|
|
31386
31547
|
try {
|
|
31387
|
-
vector = await embedFn(capabilityDoc
|
|
31548
|
+
vector = await embedFn(capabilityDoc);
|
|
31388
31549
|
} catch {
|
|
31389
31550
|
}
|
|
31390
31551
|
}
|