@askexenow/exe-os 0.9.296 → 0.9.298
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/deploy/compose/.env.customer.example +6 -1
- package/deploy/compose/.env.example +6 -1
- package/deploy/compose/docker-compose.yml +24 -5
- package/deploy/compose/generate-env.ts +20 -2
- package/deploy/compose/setup.sh +10 -1
- package/deploy/stack-manifests/v0.9.json +1 -1
- package/dist/active-agent-3JXJQ2GZ.js +27 -0
- package/dist/active-agent-XK54VI3S.js +28 -0
- package/dist/agentic-ontology-BPJOT2H2.js +25 -0
- package/dist/backfill-metadata-OTLLYTX4.js +600 -0
- package/dist/behaviors-BGEUHF7D.js +46 -0
- package/dist/bin/agentic-ontology-backfill.js +5 -5
- package/dist/bin/agentic-reflection-backfill.js +6 -6
- package/dist/bin/agentic-semantic-label.js +5 -5
- package/dist/bin/backfill-conversations.js +4 -4
- package/dist/bin/backfill-responses.js +4 -4
- package/dist/bin/backfill-vectors.js +5 -5
- package/dist/bin/bulk-sync-postgres.js +7 -7
- package/dist/bin/cc-doctor.js +5 -4
- package/dist/bin/cleanup-stale-review-tasks.js +9 -9
- package/dist/bin/cli.js +15 -15
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +13 -13
- package/dist/bin/exe-assign.js +5 -5
- package/dist/bin/exe-boot.js +32 -17
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +7 -7
- package/dist/bin/exe-dispatch.js +9 -9
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +7 -7
- package/dist/bin/exe-forget.js +6 -6
- package/dist/bin/exe-gateway.js +7 -7
- package/dist/bin/exe-healthcheck.js +5 -4
- package/dist/bin/exe-heartbeat.js +9 -9
- package/dist/bin/exe-kill.js +12 -12
- package/dist/bin/exe-launch-agent.js +25 -16
- package/dist/bin/exe-new-employee.js +6 -6
- package/dist/bin/exe-pending-messages.js +10 -10
- package/dist/bin/exe-pending-notifications.js +9 -9
- package/dist/bin/exe-pending-reviews.js +9 -9
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +11 -11
- package/dist/bin/exe-search.js +5 -5
- package/dist/bin/exe-session-cleanup.js +14 -14
- package/dist/bin/exe-settings.js +8 -8
- package/dist/bin/exe-start-codex.js +11 -11
- package/dist/bin/exe-start-opencode.js +8 -8
- package/dist/bin/exe-status.js +10 -10
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/git-sweep.js +10 -10
- package/dist/bin/graph-backfill.js +4 -4
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +7 -7
- package/dist/bin/install-launchd.js +10 -124
- package/dist/bin/install.js +21 -12
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/scan-tasks.js +9 -9
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -4
- package/dist/bin/stack-update.js +27 -3
- package/dist/capability-cards-KR7BOS2H.js +89 -0
- package/dist/capacity-monitor-CQFYTU45.js +51 -0
- package/dist/catchup-brief-HNT2CYJQ.js +175 -0
- package/dist/cc-binary-detect-4B25R3TO.js +15 -0
- package/dist/chunk-2DQIQXHX.js +204 -0
- package/dist/chunk-2DWYR2LJ.js +395 -0
- package/dist/chunk-2MSLUPJA.js +70 -0
- package/dist/chunk-2P5KBHJH.js +362 -0
- package/dist/chunk-2QGG6GG3.js +4388 -0
- package/dist/chunk-2XR6OX3M.js +181 -0
- package/dist/chunk-3REXR56D.js +98 -0
- package/dist/chunk-4FEZF2HJ.js +184 -0
- package/dist/chunk-54PQ35JV.js +244 -0
- package/dist/chunk-5ALR7GCX.js +33 -0
- package/dist/chunk-5GJ5MHC3.js +1094 -0
- package/dist/chunk-5IIJUSVD.js +240 -0
- package/dist/chunk-5XM7CTLK.js +199 -0
- package/dist/chunk-6GQMQJE2.js +331 -0
- package/dist/chunk-7C4K7HSF.js +262 -0
- package/dist/chunk-AAFTYJ5R.js +185 -0
- package/dist/chunk-ANIZN35L.js +284 -0
- package/dist/chunk-AOXVDJ7W.js +150 -0
- package/dist/chunk-AR3HN5SA.js +76 -0
- package/dist/chunk-BFFM7FF4.js +214 -0
- package/dist/chunk-CVJYNRYF.js +50 -0
- package/dist/chunk-CXEY5CE2.js +81 -0
- package/dist/chunk-CY4IRJ2I.js +381 -0
- package/dist/chunk-DBNTPP7Q.js +1079 -0
- package/dist/chunk-DTLYO26W.js +402 -0
- package/dist/chunk-EESPF5VS.js +1350 -0
- package/dist/chunk-EN4D3BCA.js +2135 -0
- package/dist/chunk-EYDBWKYI.js +304 -0
- package/dist/chunk-FCSS3YHS.js +348 -0
- package/dist/chunk-G4VSF4DA.js +630 -0
- package/dist/chunk-GEF6EHCW.js +197 -0
- package/dist/chunk-GXDV35ZN.js +735 -0
- package/dist/chunk-HJWT6TFU.js +97 -0
- package/dist/chunk-HQD3IPZA.js +14597 -0
- package/dist/chunk-I3IW6AAX.js +3342 -0
- package/dist/chunk-I4JYP7CK.js +535 -0
- package/dist/chunk-J2VWNIYC.js +167 -0
- package/dist/chunk-JK5OLEM6.js +227 -0
- package/dist/chunk-JVMP77TZ.js +1186 -0
- package/dist/chunk-KJ5DAVIP.js +122 -0
- package/dist/chunk-KPP2AYJV.js +333 -0
- package/dist/chunk-L2WWAOQF.js +455 -0
- package/dist/chunk-L3STXXCO.js +731 -0
- package/dist/chunk-LMX434DW.js +38 -0
- package/dist/chunk-LP4NNUZN.js +58 -0
- package/dist/chunk-MN7Q23PF.js +157 -0
- package/dist/chunk-O4QLYWMC.js +668 -0
- package/dist/chunk-OP5W524C.js +85 -0
- package/dist/chunk-OQ336FHE.js +836 -0
- package/dist/chunk-P7WAPK6X.js +54 -0
- package/dist/chunk-PLM4JJHE.js +128 -0
- package/dist/chunk-POTMDICN.js +1352 -0
- package/dist/chunk-POZYABJT.js +567 -0
- package/dist/chunk-PQG2IQNN.js +280 -0
- package/dist/chunk-PUGKVPCC.js +210 -0
- package/dist/chunk-QE6AI57N.js +1158 -0
- package/dist/chunk-QNCZDDVW.js +448 -0
- package/dist/chunk-QZIYE2QV.js +456 -0
- package/dist/chunk-RXOHALES.js +345 -0
- package/dist/chunk-SGUSCL3H.js +129 -0
- package/dist/chunk-SWOGQD57.js +85 -0
- package/dist/chunk-SZLIO4SL.js +128 -0
- package/dist/chunk-T2USDSC2.js +221 -0
- package/dist/chunk-UKE7MZVO.js +382 -0
- package/dist/chunk-VSRJPOZD.js +290 -0
- package/dist/chunk-WCQ3Y5LB.js +2162 -0
- package/dist/chunk-WHBT6M3P.js +30 -0
- package/dist/chunk-WR6ANODF.js +57 -0
- package/dist/chunk-X7ADWEZ7.js +171 -0
- package/dist/chunk-Y4QZXMCD.js +123 -0
- package/dist/chunk-Y4WA4CUW.js +377 -0
- package/dist/chunk-YUOUHUAJ.js +127 -0
- package/dist/chunk-YXC3OWWW.js +128 -0
- package/dist/co-activation-EN7HT734.js +74 -0
- package/dist/co-occurrence-TRPHUCSC.js +95 -0
- package/dist/core-memory-TVMGLIJ7.js +110 -0
- package/dist/crdt-sync-WOMERQIS.js +33 -0
- package/dist/crm-webhook-Q5Z74FDJ.js +10 -0
- package/dist/cto-delegation-gate-NEMNMLXB.js +280 -0
- package/dist/daemon-orchestration-RYDUCL24.js +143 -0
- package/dist/db-backup-Z3E3LHZK.js +43 -0
- package/dist/doc-graph-extractor-TSCKGSB5.js +133 -0
- package/dist/dreaming-42LRG6AO.js +34 -0
- package/dist/exe-drift-SJ6OTCQV.js +70 -0
- package/dist/exe-export-SRDDONTU.js +77 -0
- package/dist/exe-import-PWHXRFLN.js +80 -0
- package/dist/exe-key-GH6ITI5C.js +673 -0
- package/dist/exe-snapshot-RVLH5PJZ.js +338 -0
- package/dist/fast-db-init-CM4XPET2.js +7 -0
- package/dist/gateway/index.js +14 -14
- package/dist/git-staleness-7EFCUGT2.js +112 -0
- package/dist/git-task-sweep-6DR24G3Y.js +42 -0
- package/dist/global-procedures-NXLOLU7Y.js +22 -0
- package/dist/graph-auto-extract-5F3PLBCK.js +183 -0
- package/dist/hooks/bug-report-worker.js +11 -11
- package/dist/hooks/codex-stop-task-finalizer.js +11 -11
- package/dist/hooks/commit-complete.js +11 -11
- package/dist/hooks/error-recall.js +10 -7
- package/dist/hooks/exe-heartbeat-hook.js +3 -3
- package/dist/hooks/ingest.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +19 -19
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +10 -10
- package/dist/hooks/post-tool-combined.js +5 -5
- package/dist/hooks/pre-compact.js +11 -11
- package/dist/hooks/pre-tool-use.js +14 -14
- package/dist/hooks/prompt-submit.js +26 -22
- package/dist/hooks/session-end.js +15 -15
- package/dist/hooks/session-start.js +12 -12
- package/dist/hooks/stop.js +14 -14
- package/dist/hooks/subagent-stop.js +10 -10
- package/dist/hooks/summary-worker.js +14 -14
- package/dist/index.js +19 -19
- package/dist/installer-5WBAZKSI.js +344 -0
- package/dist/installer-ENK2QYTC.js +298 -0
- package/dist/installer-TUO7KCRN.js +40 -0
- package/dist/lib/cloud-sync.js +7 -7
- package/dist/lib/consolidation.js +5 -5
- package/dist/lib/database.js +4 -2
- package/dist/lib/db-read-worker-client.js +121 -0
- package/dist/lib/db-read-worker.js +73 -0
- package/dist/lib/db.js +4 -2
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +110 -87
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/messaging.js +9 -9
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +5 -5
- package/dist/lib/session-registry.js +4 -4
- package/dist/lib/skill-learning.js +6 -6
- package/dist/lib/store.js +4 -4
- package/dist/lib/task-router.js +3 -3
- package/dist/lib/tasks.js +10 -10
- package/dist/lib/tmux-routing.js +8 -8
- package/dist/lib/token-spend.js +3 -3
- package/dist/mcp/register-tools.js +56 -55
- package/dist/mcp/server.js +58 -57
- package/dist/mcp/tools/complete-reminder.js +4 -4
- package/dist/mcp/tools/create-reminder.js +4 -4
- package/dist/mcp/tools/create-task.js +12 -12
- package/dist/mcp/tools/deactivate-behavior.js +7 -7
- package/dist/mcp/tools/list-reminders.js +4 -4
- package/dist/mcp/tools/list-tasks.js +12 -12
- package/dist/mcp/tools/send-message.js +11 -11
- package/dist/mcp/tools/update-task.js +11 -11
- package/dist/mcp-http-config-FA6LWCTD.js +31 -0
- package/dist/memory-cards-4QRWCJ54.js +180 -0
- package/dist/memory-graph-extractor-HCS4XTQK.js +22 -0
- package/dist/memory-poisoning-defense-7I4LY4VL.js +224 -0
- package/dist/memory-reflection-ZSSO6RFR.js +244 -0
- package/dist/notifications-VQJCTOMC.js +47 -0
- package/dist/orchestration-events-UFRQWTRS.js +27 -0
- package/dist/orchestrator-ZSSTRMDC.js +35 -0
- package/dist/pipeline-router-U7WWUVKV.js +15 -0
- package/dist/plan-limits-KVFTHSQU.js +28 -0
- package/dist/project-boot-KOGUVIQ4.js +299 -0
- package/dist/projection-worker-OMUSXRHO.js +1084 -0
- package/dist/prospective-memory-PC72YWLY.js +232 -0
- package/dist/reranker-E3MOR2MC.js +19 -0
- package/dist/retrieval-health-OEHPZH65.js +12 -0
- package/dist/review-polling-4GWZSSSQ.js +126 -0
- package/dist/runtime/index.js +11 -11
- package/dist/session-events-A3L5KUHB.js +38 -0
- package/dist/session-kill-telemetry-UFJY57N2.js +31 -0
- package/dist/session-scope-5MT6U7CR.js +88 -0
- package/dist/setup-wizard-NZE2I7M5.js +12 -0
- package/dist/skill-refinement-Q6USTB35.js +159 -0
- package/dist/steward-gate-XOYAMKNY.js +15 -0
- package/dist/task-enforcement-MQ4TKLAN.js +506 -0
- package/dist/task-scope-AWFGZWPM.js +37 -0
- package/dist/tasks-crud-LMPAYXKS.js +79 -0
- package/dist/tasks-notify-6DZOY3MH.js +40 -0
- package/dist/tasks-review-TMBMXNUJ.js +49 -0
- package/dist/telemetry-upload-C2PKRA26.js +741 -0
- package/dist/token-budget-HOOQPFKR.js +86 -0
- package/dist/tool-telemetry-EJVBIKOS.js +17 -0
- package/dist/tui/App.js +16 -16
- package/dist/tui-data-6HIDQFN7.js +260 -0
- package/dist/wiki-acl-DOBVII5N.js +111 -0
- package/dist/worker-gate-ZH5PK25Z.js +21 -0
- package/dist/workflow-engine-SGU6WUWF.js +28 -0
- package/dist/worktree-NRQVJ2M4.js +28 -0
- package/dist/worktree-sweep-FBEHEPZO.js +21 -0
- package/package.json +1 -1
- package/release-notes.json +88 -88
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import {
|
|
2
|
+
employeeSessionName,
|
|
3
|
+
isEmployeeAlive,
|
|
4
|
+
resolveExeSession,
|
|
5
|
+
sendIntercom,
|
|
6
|
+
strictSessionScopeFilter
|
|
7
|
+
} from "./chunk-2QGG6GG3.js";
|
|
8
|
+
import {
|
|
9
|
+
parseMessage,
|
|
10
|
+
serializeMessage
|
|
11
|
+
} from "./chunk-4JERP7NT.js";
|
|
12
|
+
import {
|
|
13
|
+
recordOrchestrationEventBestEffort
|
|
14
|
+
} from "./chunk-G4VSF4DA.js";
|
|
15
|
+
import {
|
|
16
|
+
getClient
|
|
17
|
+
} from "./chunk-I3IW6AAX.js";
|
|
18
|
+
|
|
19
|
+
// src/lib/messaging.ts
|
|
20
|
+
import crypto from "crypto";
|
|
21
|
+
function generateUlid() {
|
|
22
|
+
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
23
|
+
const random = crypto.randomBytes(10).toString("hex").slice(0, 16);
|
|
24
|
+
return (timestamp + random).toUpperCase();
|
|
25
|
+
}
|
|
26
|
+
function rowToMessage(row) {
|
|
27
|
+
return {
|
|
28
|
+
id: row.id,
|
|
29
|
+
fromAgent: row.from_agent,
|
|
30
|
+
fromDevice: row.from_device,
|
|
31
|
+
targetAgent: row.target_agent,
|
|
32
|
+
targetProject: row.target_project ?? null,
|
|
33
|
+
targetDevice: row.target_device,
|
|
34
|
+
sessionScope: row.session_scope ?? null,
|
|
35
|
+
content: row.content,
|
|
36
|
+
priority: row.priority ?? "normal",
|
|
37
|
+
status: row.status ?? "pending",
|
|
38
|
+
serverSeq: row.server_seq != null ? Number(row.server_seq) : null,
|
|
39
|
+
retryCount: Number(row.retry_count ?? 0),
|
|
40
|
+
createdAt: row.created_at,
|
|
41
|
+
deliveredAt: row.delivered_at ?? null,
|
|
42
|
+
processedAt: row.processed_at ?? null,
|
|
43
|
+
failedAt: row.failed_at ?? null,
|
|
44
|
+
failureReason: row.failure_reason ?? null
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var MAX_RETRIES = 10;
|
|
48
|
+
async function countUnreadMessages(targetAgent, sessionScope) {
|
|
49
|
+
const client = getClient();
|
|
50
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
51
|
+
const result = await client.execute({
|
|
52
|
+
sql: `SELECT COUNT(*) AS count FROM messages
|
|
53
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
54
|
+
args: [targetAgent, ...scope.args]
|
|
55
|
+
});
|
|
56
|
+
return Number(result.rows[0]?.count ?? 0);
|
|
57
|
+
}
|
|
58
|
+
async function sendMessage(input) {
|
|
59
|
+
const client = getClient();
|
|
60
|
+
const id = generateUlid();
|
|
61
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
62
|
+
const targetDevice = input.targetDevice ?? "local";
|
|
63
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
64
|
+
await client.execute({
|
|
65
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, founder_id, created_at)
|
|
66
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,
|
|
67
|
+
args: [
|
|
68
|
+
id,
|
|
69
|
+
input.fromAgent,
|
|
70
|
+
input.targetAgent,
|
|
71
|
+
input.targetProject ?? null,
|
|
72
|
+
targetDevice,
|
|
73
|
+
sessionScope,
|
|
74
|
+
input.content,
|
|
75
|
+
input.priority ?? "normal",
|
|
76
|
+
input.founderId ?? null,
|
|
77
|
+
now
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
recordOrchestrationEventBestEffort({
|
|
81
|
+
eventType: "message.sent",
|
|
82
|
+
source: "messaging.sendMessage",
|
|
83
|
+
agentId: input.fromAgent,
|
|
84
|
+
payload: { targetAgent: input.targetAgent, priority: input.priority ?? "normal", targetDevice },
|
|
85
|
+
sessionScope: sessionScope ?? null
|
|
86
|
+
});
|
|
87
|
+
try {
|
|
88
|
+
if (targetDevice !== "local") {
|
|
89
|
+
await deliverCrossMachineMessage(id, targetDevice);
|
|
90
|
+
} else {
|
|
91
|
+
await deliverLocalMessage(id);
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
96
|
+
const result = await client.execute({
|
|
97
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
98
|
+
args: [id, ...sentScope.args]
|
|
99
|
+
});
|
|
100
|
+
return rowToMessage(result.rows[0]);
|
|
101
|
+
}
|
|
102
|
+
var _wsClientSend = null;
|
|
103
|
+
function setWsClientSend(fn) {
|
|
104
|
+
_wsClientSend = fn;
|
|
105
|
+
}
|
|
106
|
+
async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
107
|
+
const client = getClient();
|
|
108
|
+
const result = await client.execute({
|
|
109
|
+
sql: "SELECT * FROM messages WHERE id = ?",
|
|
110
|
+
args: [messageId]
|
|
111
|
+
});
|
|
112
|
+
if (result.rows.length === 0) return false;
|
|
113
|
+
const msg = rowToMessage(result.rows[0]);
|
|
114
|
+
if (msg.status !== "pending") return false;
|
|
115
|
+
if (!_wsClientSend) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const payload = JSON.stringify({
|
|
119
|
+
id: msg.id,
|
|
120
|
+
fromAgent: msg.fromAgent,
|
|
121
|
+
targetAgent: msg.targetAgent,
|
|
122
|
+
targetProject: msg.targetProject,
|
|
123
|
+
sessionScope: msg.sessionScope,
|
|
124
|
+
content: msg.content,
|
|
125
|
+
priority: msg.priority,
|
|
126
|
+
createdAt: msg.createdAt
|
|
127
|
+
});
|
|
128
|
+
const sent = _wsClientSend(targetDevice, payload);
|
|
129
|
+
if (sent) {
|
|
130
|
+
await client.execute({
|
|
131
|
+
sql: "UPDATE messages SET status = 'synced' WHERE id = ?",
|
|
132
|
+
args: [messageId]
|
|
133
|
+
});
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
async function deliverLocalMessage(messageId) {
|
|
139
|
+
const client = getClient();
|
|
140
|
+
const result = await client.execute({
|
|
141
|
+
sql: "SELECT * FROM messages WHERE id = ?",
|
|
142
|
+
args: [messageId]
|
|
143
|
+
});
|
|
144
|
+
if (result.rows.length === 0) return false;
|
|
145
|
+
const msg = rowToMessage(result.rows[0]);
|
|
146
|
+
if (msg.status !== "pending") return false;
|
|
147
|
+
const targetAgent = msg.targetAgent;
|
|
148
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
149
|
+
try {
|
|
150
|
+
const exeSession = resolveExeSession();
|
|
151
|
+
if (!exeSession) {
|
|
152
|
+
throw new Error("No coordinator session found");
|
|
153
|
+
}
|
|
154
|
+
process.stderr.write(`[messaging] ENTER deliverLocal: target=${targetAgent} exeSession=${exeSession}
|
|
155
|
+
`);
|
|
156
|
+
const { isCoordinatorName: isCoord } = await import("./lib/employees.js");
|
|
157
|
+
const isCOO = isCoord(targetAgent);
|
|
158
|
+
process.stderr.write(`[messaging] COO check: targetAgent="${targetAgent}" isCOO=${isCOO}
|
|
159
|
+
`);
|
|
160
|
+
const sessionName = isCOO ? exeSession : employeeSessionName(targetAgent, exeSession);
|
|
161
|
+
process.stderr.write(`[messaging] delivery attempt: target=${targetAgent} session=${sessionName} isCOO=${isCOO}
|
|
162
|
+
`);
|
|
163
|
+
if (!isCOO && !isEmployeeAlive(sessionName)) {
|
|
164
|
+
throw new Error("Session not running \u2014 message stays queued");
|
|
165
|
+
}
|
|
166
|
+
const unreadCount = await countUnreadMessages(targetAgent, msg.sessionScope);
|
|
167
|
+
if (unreadCount <= 0) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const intercomResult = sendIntercom(sessionName, { force: true, reason: "message" });
|
|
171
|
+
process.stderr.write(`[messaging] intercom result for ${sessionName}: ${intercomResult}
|
|
172
|
+
`);
|
|
173
|
+
await client.execute({
|
|
174
|
+
sql: "UPDATE messages SET status = 'delivered', delivered_at = ? WHERE id = ?",
|
|
175
|
+
args: [now, messageId]
|
|
176
|
+
});
|
|
177
|
+
recordOrchestrationEventBestEffort({
|
|
178
|
+
eventType: "message.delivered",
|
|
179
|
+
source: "messaging.deliverLocalMessage",
|
|
180
|
+
agentId: msg.targetAgent,
|
|
181
|
+
payload: { fromAgent: msg.fromAgent, priority: msg.priority }
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
} catch (err) {
|
|
185
|
+
process.stderr.write(
|
|
186
|
+
`[messaging] delivery failed for message ${messageId}: ${err instanceof Error ? err.message : String(err)}
|
|
187
|
+
`
|
|
188
|
+
);
|
|
189
|
+
const newRetryCount = msg.retryCount + 1;
|
|
190
|
+
if (newRetryCount >= MAX_RETRIES) {
|
|
191
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
192
|
+
} else {
|
|
193
|
+
await client.execute({
|
|
194
|
+
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
195
|
+
args: [newRetryCount, messageId]
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
202
|
+
const client = getClient();
|
|
203
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
204
|
+
const result = await client.execute({
|
|
205
|
+
sql: `SELECT * FROM messages
|
|
206
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
207
|
+
ORDER BY id`,
|
|
208
|
+
args: [targetAgent, ...scope.args]
|
|
209
|
+
});
|
|
210
|
+
return result.rows.map((row) => rowToMessage(row));
|
|
211
|
+
}
|
|
212
|
+
async function markRead(messageId, sessionScope) {
|
|
213
|
+
const client = getClient();
|
|
214
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
215
|
+
await client.execute({
|
|
216
|
+
sql: `UPDATE messages SET status = 'read'
|
|
217
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
218
|
+
args: [messageId, ...scope.args]
|
|
219
|
+
});
|
|
220
|
+
recordOrchestrationEventBestEffort({
|
|
221
|
+
eventType: "message.read",
|
|
222
|
+
source: "messaging.markRead",
|
|
223
|
+
payload: { messageId }
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
227
|
+
const client = getClient();
|
|
228
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
229
|
+
await client.execute({
|
|
230
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
231
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
232
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function markProcessed(messageId, sessionScope) {
|
|
236
|
+
const client = getClient();
|
|
237
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
238
|
+
await client.execute({
|
|
239
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
240
|
+
WHERE id = ?${scope.sql}`,
|
|
241
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
245
|
+
const client = getClient();
|
|
246
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
247
|
+
const result = await client.execute({
|
|
248
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
249
|
+
args: [messageId, ...scope.args]
|
|
250
|
+
});
|
|
251
|
+
return result.rows[0]?.status ?? null;
|
|
252
|
+
}
|
|
253
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
254
|
+
const client = getClient();
|
|
255
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
256
|
+
const result = await client.execute({
|
|
257
|
+
sql: `SELECT * FROM messages
|
|
258
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
259
|
+
ORDER BY id`,
|
|
260
|
+
args: [targetAgent, ...scope.args]
|
|
261
|
+
});
|
|
262
|
+
return result.rows.map((row) => rowToMessage(row));
|
|
263
|
+
}
|
|
264
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
265
|
+
const client = getClient();
|
|
266
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
267
|
+
const result = await client.execute({
|
|
268
|
+
sql: `SELECT * FROM messages
|
|
269
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
270
|
+
ORDER BY id`,
|
|
271
|
+
args: [targetAgent, ...scope.args]
|
|
272
|
+
});
|
|
273
|
+
return result.rows.map((row) => rowToMessage(row));
|
|
274
|
+
}
|
|
275
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
276
|
+
const client = getClient();
|
|
277
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
278
|
+
await client.execute({
|
|
279
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
280
|
+
WHERE id = ?${scope.sql}`,
|
|
281
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function getFailedMessages(sessionScope) {
|
|
285
|
+
const client = getClient();
|
|
286
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
287
|
+
const result = await client.execute({
|
|
288
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
289
|
+
args: [...scope.args]
|
|
290
|
+
});
|
|
291
|
+
return result.rows.map((row) => rowToMessage(row));
|
|
292
|
+
}
|
|
293
|
+
async function retryPendingMessages(sessionScope) {
|
|
294
|
+
const client = getClient();
|
|
295
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
296
|
+
const result = await client.execute({
|
|
297
|
+
sql: `SELECT * FROM messages
|
|
298
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
299
|
+
ORDER BY id`,
|
|
300
|
+
args: [MAX_RETRIES, ...scope.args]
|
|
301
|
+
});
|
|
302
|
+
let delivered = 0;
|
|
303
|
+
for (const row of result.rows) {
|
|
304
|
+
const msg = rowToMessage(row);
|
|
305
|
+
try {
|
|
306
|
+
const success = await deliverLocalMessage(msg.id);
|
|
307
|
+
if (success) delivered++;
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return delivered;
|
|
312
|
+
}
|
|
313
|
+
async function sendTypedMessage(envelope, targetAgent, opts) {
|
|
314
|
+
return sendMessage({
|
|
315
|
+
fromAgent: envelope.from,
|
|
316
|
+
targetAgent,
|
|
317
|
+
targetProject: opts?.targetProject,
|
|
318
|
+
targetDevice: opts?.targetDevice,
|
|
319
|
+
content: serializeMessage(envelope),
|
|
320
|
+
priority: opts?.priority,
|
|
321
|
+
sessionScope: opts?.sessionScope
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function parseIncomingMessage(content) {
|
|
325
|
+
return parseMessage(content);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export {
|
|
329
|
+
countUnreadMessages,
|
|
330
|
+
sendMessage,
|
|
331
|
+
setWsClientSend,
|
|
332
|
+
deliverLocalMessage,
|
|
333
|
+
getPendingMessages,
|
|
334
|
+
markRead,
|
|
335
|
+
markAcknowledged,
|
|
336
|
+
markProcessed,
|
|
337
|
+
getMessageStatus,
|
|
338
|
+
getUnacknowledgedMessages,
|
|
339
|
+
getReadMessages,
|
|
340
|
+
markFailed,
|
|
341
|
+
getFailedMessages,
|
|
342
|
+
retryPendingMessages,
|
|
343
|
+
sendTypedMessage,
|
|
344
|
+
parseIncomingMessage
|
|
345
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EMPLOYEES_PATH,
|
|
3
|
+
getClient,
|
|
4
|
+
isInitialized,
|
|
5
|
+
loadEmployees
|
|
6
|
+
} from "./chunk-I3IW6AAX.js";
|
|
7
|
+
import {
|
|
8
|
+
PLAN_LIMITS,
|
|
9
|
+
checkLicense,
|
|
10
|
+
isFeatureAllowed
|
|
11
|
+
} from "./chunk-YTKVJJSU.js";
|
|
12
|
+
import {
|
|
13
|
+
EXE_AI_DIR
|
|
14
|
+
} from "./chunk-R36FAN53.js";
|
|
15
|
+
|
|
16
|
+
// src/lib/plan-limits.ts
|
|
17
|
+
import { readFileSync, existsSync } from "fs";
|
|
18
|
+
import path from "path";
|
|
19
|
+
var PlanLimitError = class extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "PlanLimitError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var CACHE_PATH = path.join(EXE_AI_DIR, "license-cache.json");
|
|
26
|
+
function getLicenseSync() {
|
|
27
|
+
try {
|
|
28
|
+
if (!existsSync(CACHE_PATH)) return freeLicense();
|
|
29
|
+
const raw = JSON.parse(readFileSync(CACHE_PATH, "utf8"));
|
|
30
|
+
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
31
|
+
const parts = raw.token.split(".");
|
|
32
|
+
if (parts.length !== 3) return freeLicense();
|
|
33
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
34
|
+
const plan = payload.plan ?? "free";
|
|
35
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
36
|
+
return {
|
|
37
|
+
valid: true,
|
|
38
|
+
plan,
|
|
39
|
+
email: payload.sub ?? "",
|
|
40
|
+
expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
|
|
41
|
+
deviceLimit: limits.devices,
|
|
42
|
+
employeeLimit: limits.employees,
|
|
43
|
+
memoryLimit: limits.memories
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return freeLicense();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function freeLicense() {
|
|
50
|
+
const limits = PLAN_LIMITS.free;
|
|
51
|
+
return {
|
|
52
|
+
valid: true,
|
|
53
|
+
plan: "free",
|
|
54
|
+
email: "",
|
|
55
|
+
expiresAt: null,
|
|
56
|
+
deviceLimit: limits.devices,
|
|
57
|
+
employeeLimit: limits.employees,
|
|
58
|
+
memoryLimit: limits.memories
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function countActiveMemories() {
|
|
62
|
+
if (!isInitialized()) return 0;
|
|
63
|
+
const client = getClient();
|
|
64
|
+
const result = await client.execute(
|
|
65
|
+
"SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL"
|
|
66
|
+
);
|
|
67
|
+
const row = result.rows[0];
|
|
68
|
+
return Number(row?.cnt ?? 0);
|
|
69
|
+
}
|
|
70
|
+
async function assertMemoryLimit() {
|
|
71
|
+
const license = await checkLicense();
|
|
72
|
+
if (license.memoryLimit < 0) return;
|
|
73
|
+
const count = await countActiveMemories();
|
|
74
|
+
if (count >= license.memoryLimit) {
|
|
75
|
+
throw new PlanLimitError(
|
|
76
|
+
`Memory limit reached: ${count}/${license.memoryLimit} active memories on the ${license.plan} plan. Upgrade at https://askexe.com to store more.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function assertEmployeeLimit(license, rosterPath) {
|
|
81
|
+
const lic = license ?? await checkLicense();
|
|
82
|
+
if (lic.employeeLimit < 0) return;
|
|
83
|
+
const employees = await loadEmployees(rosterPath ?? EMPLOYEES_PATH);
|
|
84
|
+
if (employees.length >= lic.employeeLimit) {
|
|
85
|
+
throw new PlanLimitError(
|
|
86
|
+
`Employee limit reached: ${employees.length}/${lic.employeeLimit} employees on the ${lic.plan} plan. Upgrade at https://askexe.com to add more.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function assertEmployeeLimitSync(rosterPath) {
|
|
91
|
+
const license = getLicenseSync();
|
|
92
|
+
if (license.employeeLimit < 0) return;
|
|
93
|
+
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
94
|
+
let count = 0;
|
|
95
|
+
try {
|
|
96
|
+
if (existsSync(filePath)) {
|
|
97
|
+
const raw = readFileSync(filePath, "utf8");
|
|
98
|
+
const employees = JSON.parse(raw);
|
|
99
|
+
count = Array.isArray(employees) ? employees.length : 0;
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
throw new PlanLimitError(
|
|
103
|
+
`Cannot verify employee count: roster unreadable at ${filePath}. Refusing to proceed. Check file permissions or upgrade plan.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (count >= license.employeeLimit) {
|
|
107
|
+
throw new PlanLimitError(
|
|
108
|
+
`Employee limit reached: ${count}/${license.employeeLimit} employees on the ${license.plan} plan. Upgrade at https://askexe.com to add more.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function assertFeature(feature) {
|
|
113
|
+
const license = await checkLicense();
|
|
114
|
+
if (!isFeatureAllowed(license, feature)) {
|
|
115
|
+
throw new PlanLimitError(
|
|
116
|
+
`Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
PlanLimitError,
|
|
123
|
+
getLicenseSync,
|
|
124
|
+
countActiveMemories,
|
|
125
|
+
assertMemoryLimit,
|
|
126
|
+
assertEmployeeLimit,
|
|
127
|
+
assertEmployeeLimitSync,
|
|
128
|
+
assertFeature
|
|
129
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PLATFORM_PROCEDURES,
|
|
3
|
+
PLATFORM_PROCEDURE_TITLES
|
|
4
|
+
} from "./chunk-VUH67PT4.js";
|
|
5
|
+
import {
|
|
6
|
+
getClient
|
|
7
|
+
} from "./chunk-I3IW6AAX.js";
|
|
8
|
+
|
|
9
|
+
// src/lib/global-procedures.ts
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
var _customerCache = "";
|
|
12
|
+
var _cacheLoaded = false;
|
|
13
|
+
var _platformCache = PLATFORM_PROCEDURES.map((p) => `### ${p.title}
|
|
14
|
+
${p.content}`).join("\n\n");
|
|
15
|
+
async function loadGlobalProcedures() {
|
|
16
|
+
const client = getClient();
|
|
17
|
+
const result = await client.execute({
|
|
18
|
+
sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
19
|
+
args: []
|
|
20
|
+
});
|
|
21
|
+
const allRows = result.rows;
|
|
22
|
+
const customerOnly = allRows.filter((p) => {
|
|
23
|
+
if (PLATFORM_PROCEDURE_TITLES.has(p.title)) return false;
|
|
24
|
+
const title = (p.title ?? "").trim();
|
|
25
|
+
const content = (p.content ?? "").trim();
|
|
26
|
+
if (!title || title === "(untitled)") return false;
|
|
27
|
+
if (!content) return false;
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
const values = customerOnly.filter((p) => p.kind === "value");
|
|
31
|
+
const procedures = customerOnly.filter((p) => p.kind !== "value");
|
|
32
|
+
const parts = [];
|
|
33
|
+
if (values.length > 0) {
|
|
34
|
+
parts.push("### Company Values\n" + values.map((v) => `- **${v.title}:** ${v.content}`).join("\n"));
|
|
35
|
+
}
|
|
36
|
+
if (procedures.length > 0) {
|
|
37
|
+
parts.push(procedures.map((p) => `### ${p.title}
|
|
38
|
+
${p.content}`).join("\n\n"));
|
|
39
|
+
}
|
|
40
|
+
_customerCache = parts.join("\n\n");
|
|
41
|
+
_cacheLoaded = true;
|
|
42
|
+
return customerOnly;
|
|
43
|
+
}
|
|
44
|
+
function getGlobalProceduresBlock() {
|
|
45
|
+
const sections = [];
|
|
46
|
+
if (_platformCache) sections.push(_platformCache);
|
|
47
|
+
if (_cacheLoaded && _customerCache) sections.push(_customerCache);
|
|
48
|
+
if (sections.length === 0) return "";
|
|
49
|
+
return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
|
|
50
|
+
|
|
51
|
+
${sections.join("\n\n")}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
async function storeGlobalProcedure(input) {
|
|
55
|
+
if (!input.title?.trim() || !input.content?.trim()) {
|
|
56
|
+
throw new Error("storeGlobalProcedure requires a non-empty title and content");
|
|
57
|
+
}
|
|
58
|
+
const id = randomUUID();
|
|
59
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60
|
+
const client = getClient();
|
|
61
|
+
await client.execute({
|
|
62
|
+
sql: `INSERT INTO company_procedures (id, title, content, priority, domain, kind, active, created_at, updated_at)
|
|
63
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
64
|
+
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, input.kind ?? "procedure", now, now]
|
|
65
|
+
});
|
|
66
|
+
await loadGlobalProcedures();
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
async function deactivateGlobalProcedure(id) {
|
|
70
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
71
|
+
const client = getClient();
|
|
72
|
+
const result = await client.execute({
|
|
73
|
+
sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
74
|
+
args: [now, id]
|
|
75
|
+
});
|
|
76
|
+
await loadGlobalProcedures();
|
|
77
|
+
return result.rowsAffected > 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
loadGlobalProcedures,
|
|
82
|
+
getGlobalProceduresBlock,
|
|
83
|
+
storeGlobalProcedure,
|
|
84
|
+
deactivateGlobalProcedure
|
|
85
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadEmployeesSync
|
|
3
|
+
} from "./chunk-I3IW6AAX.js";
|
|
4
|
+
|
|
5
|
+
// src/adapters/mcp-http-config.ts
|
|
6
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
7
|
+
import { randomBytes } from "crypto";
|
|
8
|
+
import { execFileSync } from "child_process";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import os from "os";
|
|
11
|
+
var DEFAULT_MCP_HTTP_PORT = "48739";
|
|
12
|
+
function resolveDefaultAgentId() {
|
|
13
|
+
if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
|
|
14
|
+
try {
|
|
15
|
+
const coo = loadEmployeesSync().find((e) => e.role === "COO");
|
|
16
|
+
if (coo) return coo.name;
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
return "exe";
|
|
20
|
+
}
|
|
21
|
+
var DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
|
|
22
|
+
var DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
|
|
23
|
+
function sanitizePort(raw) {
|
|
24
|
+
const v = raw?.trim();
|
|
25
|
+
if (!v) return null;
|
|
26
|
+
if (!/^\d+$/.test(v)) return null;
|
|
27
|
+
const port = Number(v);
|
|
28
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
function configMcpPort(homeDir = os.homedir()) {
|
|
32
|
+
try {
|
|
33
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(homeDir, ".exe-os");
|
|
34
|
+
const configPath = path.join(dir, "config.json");
|
|
35
|
+
if (!existsSync(configPath)) return null;
|
|
36
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
37
|
+
const candidate = cfg.mcpPort ?? cfg.fixedMcpPort;
|
|
38
|
+
if (candidate == null) return null;
|
|
39
|
+
return sanitizePort(String(candidate));
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function mcpHttpPort(homeDir = os.homedir()) {
|
|
45
|
+
return sanitizePort(process.env.EXE_MCP_PORT) ?? configMcpPort(homeDir) ?? DEFAULT_MCP_HTTP_PORT;
|
|
46
|
+
}
|
|
47
|
+
function mcpHttpUrl(homeDir = os.homedir()) {
|
|
48
|
+
return `http://127.0.0.1:${mcpHttpPort(homeDir)}/mcp`;
|
|
49
|
+
}
|
|
50
|
+
function persistMcpPortToConfig(port = DEFAULT_MCP_HTTP_PORT, homeDir = os.homedir()) {
|
|
51
|
+
try {
|
|
52
|
+
if (!sanitizePort(port)) return;
|
|
53
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(homeDir, ".exe-os");
|
|
54
|
+
const configPath = path.join(dir, "config.json");
|
|
55
|
+
if (!existsSync(configPath)) return;
|
|
56
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
57
|
+
if (cfg.mcpPort === port) return;
|
|
58
|
+
cfg.mcpPort = port;
|
|
59
|
+
writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
60
|
+
`, "utf-8");
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readOrCreateDaemonToken(homeDir = os.homedir()) {
|
|
65
|
+
const exeDir = path.join(homeDir, ".exe-os");
|
|
66
|
+
const tokenPath = path.join(exeDir, "exed.token");
|
|
67
|
+
if (existsSync(tokenPath)) {
|
|
68
|
+
try {
|
|
69
|
+
const token2 = readFileSync(tokenPath, "utf-8").trim();
|
|
70
|
+
if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const token = randomBytes(32).toString("hex");
|
|
75
|
+
mkdirSync(exeDir, { recursive: true });
|
|
76
|
+
writeFileSync(tokenPath, `${token}
|
|
77
|
+
`, "utf-8");
|
|
78
|
+
try {
|
|
79
|
+
chmodSync(tokenPath, 384);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
return token;
|
|
83
|
+
}
|
|
84
|
+
function buildMcpHttpHeaders(homeDir = os.homedir(), opts = {}) {
|
|
85
|
+
const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
|
|
86
|
+
const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
87
|
+
let sessionName = process.env.EXE_SESSION_NAME ?? "";
|
|
88
|
+
if (!sessionName && !opts.useShellPlaceholders) {
|
|
89
|
+
try {
|
|
90
|
+
const sock = process.env.EXE_TMUX_SOCKET;
|
|
91
|
+
const tmuxArgs = sock ? ["-L", sock, "display-message", "-p", "#{session_name}"] : ["display-message", "-p", "#{session_name}"];
|
|
92
|
+
const tmuxSession = execFileSync("tmux", tmuxArgs, {
|
|
93
|
+
encoding: "utf8",
|
|
94
|
+
timeout: 2e3
|
|
95
|
+
}).trim();
|
|
96
|
+
if (tmuxSession) sessionName = tmuxSession;
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const headers = {
|
|
101
|
+
Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
|
|
102
|
+
"X-Agent-Id": agentId,
|
|
103
|
+
"X-Agent-Role": agentRole
|
|
104
|
+
};
|
|
105
|
+
if (sessionName && !opts.useShellPlaceholders) headers["X-Exe-Session"] = sessionName;
|
|
106
|
+
return headers;
|
|
107
|
+
}
|
|
108
|
+
function buildClaudeHttpMcpEntry(homeDir = os.homedir()) {
|
|
109
|
+
const entry = {
|
|
110
|
+
type: "http",
|
|
111
|
+
url: mcpHttpUrl(),
|
|
112
|
+
headers: buildMcpHttpHeaders(homeDir, { useShellPlaceholders: true })
|
|
113
|
+
};
|
|
114
|
+
delete entry.headers["X-Exe-Session"];
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
DEFAULT_MCP_HTTP_PORT,
|
|
120
|
+
DEFAULT_MCP_HTTP_AGENT_ID,
|
|
121
|
+
DEFAULT_MCP_HTTP_AGENT_ROLE,
|
|
122
|
+
mcpHttpPort,
|
|
123
|
+
mcpHttpUrl,
|
|
124
|
+
persistMcpPortToConfig,
|
|
125
|
+
readOrCreateDaemonToken,
|
|
126
|
+
buildMcpHttpHeaders,
|
|
127
|
+
buildClaudeHttpMcpEntry
|
|
128
|
+
};
|