@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/mcp/server.js
CHANGED
|
@@ -531,98 +531,132 @@ async function pingDaemon() {
|
|
|
531
531
|
return null;
|
|
532
532
|
}
|
|
533
533
|
function killAndRespawnDaemon() {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
534
|
+
if (!acquireSpawnLock()) {
|
|
535
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
536
|
+
if (_socket) {
|
|
537
|
+
_socket.destroy();
|
|
538
|
+
_socket = null;
|
|
539
|
+
}
|
|
540
|
+
_connected = false;
|
|
541
|
+
_buffer = "";
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
546
|
+
if (existsSync2(PID_PATH)) {
|
|
547
|
+
try {
|
|
548
|
+
const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
|
|
549
|
+
if (pid > 0) {
|
|
550
|
+
try {
|
|
551
|
+
process.kill(pid, "SIGKILL");
|
|
552
|
+
} catch {
|
|
553
|
+
}
|
|
542
554
|
}
|
|
555
|
+
} catch {
|
|
543
556
|
}
|
|
557
|
+
}
|
|
558
|
+
if (_socket) {
|
|
559
|
+
_socket.destroy();
|
|
560
|
+
_socket = null;
|
|
561
|
+
}
|
|
562
|
+
_connected = false;
|
|
563
|
+
_buffer = "";
|
|
564
|
+
try {
|
|
565
|
+
unlinkSync(PID_PATH);
|
|
544
566
|
} catch {
|
|
545
567
|
}
|
|
568
|
+
try {
|
|
569
|
+
unlinkSync(SOCKET_PATH);
|
|
570
|
+
} catch {
|
|
571
|
+
}
|
|
572
|
+
spawnDaemon();
|
|
573
|
+
} finally {
|
|
574
|
+
releaseSpawnLock();
|
|
546
575
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
_socket = null;
|
|
550
|
-
}
|
|
551
|
-
_connected = false;
|
|
552
|
-
_buffer = "";
|
|
576
|
+
}
|
|
577
|
+
function isDaemonTooYoung() {
|
|
553
578
|
try {
|
|
554
|
-
|
|
579
|
+
const stat = statSync(PID_PATH);
|
|
580
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
555
581
|
} catch {
|
|
582
|
+
return false;
|
|
556
583
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
584
|
+
}
|
|
585
|
+
async function retryThenRestart(doRequest, label) {
|
|
586
|
+
const result = await doRequest();
|
|
587
|
+
if (!result.error) {
|
|
588
|
+
_consecutiveFailures = 0;
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
_consecutiveFailures++;
|
|
592
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
593
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
594
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
595
|
+
`);
|
|
596
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
597
|
+
if (!_connected) {
|
|
598
|
+
if (!await connectToSocket()) continue;
|
|
599
|
+
}
|
|
600
|
+
const retry = await doRequest();
|
|
601
|
+
if (!retry.error) {
|
|
602
|
+
_consecutiveFailures = 0;
|
|
603
|
+
return retry;
|
|
604
|
+
}
|
|
605
|
+
_consecutiveFailures++;
|
|
606
|
+
}
|
|
607
|
+
if (isDaemonTooYoung()) {
|
|
608
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
609
|
+
`);
|
|
610
|
+
return { error: result.error };
|
|
611
|
+
}
|
|
612
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
613
|
+
`);
|
|
614
|
+
killAndRespawnDaemon();
|
|
615
|
+
const start = Date.now();
|
|
616
|
+
let delay2 = 200;
|
|
617
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
618
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
619
|
+
if (await connectToSocket()) break;
|
|
620
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
560
621
|
}
|
|
561
|
-
|
|
622
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
623
|
+
const final = await doRequest();
|
|
624
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
625
|
+
return final;
|
|
562
626
|
}
|
|
563
627
|
async function embedViaClient(text, priority = "high") {
|
|
564
628
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
565
629
|
_requestCount++;
|
|
566
630
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
567
631
|
const health = await pingDaemon();
|
|
568
|
-
if (!health) {
|
|
632
|
+
if (!health && !isDaemonTooYoung()) {
|
|
569
633
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
570
634
|
`);
|
|
571
635
|
killAndRespawnDaemon();
|
|
572
636
|
const start = Date.now();
|
|
573
|
-
let
|
|
637
|
+
let d = 200;
|
|
574
638
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
575
|
-
await new Promise((r) => setTimeout(r,
|
|
639
|
+
await new Promise((r) => setTimeout(r, d));
|
|
576
640
|
if (await connectToSocket()) break;
|
|
577
|
-
|
|
641
|
+
d = Math.min(d * 2, 3e3);
|
|
578
642
|
}
|
|
579
643
|
if (!_connected) return null;
|
|
580
644
|
}
|
|
581
645
|
}
|
|
582
|
-
const result = await
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
killAndRespawnDaemon();
|
|
588
|
-
const start = Date.now();
|
|
589
|
-
let delay2 = 200;
|
|
590
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
591
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
592
|
-
if (await connectToSocket()) break;
|
|
593
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
594
|
-
}
|
|
595
|
-
if (!_connected) return null;
|
|
596
|
-
const retry = await sendRequest([text], priority);
|
|
597
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
598
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
599
|
-
`);
|
|
600
|
-
}
|
|
601
|
-
return null;
|
|
646
|
+
const result = await retryThenRestart(
|
|
647
|
+
() => sendRequest([text], priority),
|
|
648
|
+
"Embed"
|
|
649
|
+
);
|
|
650
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
602
651
|
}
|
|
603
652
|
async function embedBatchViaClient(texts, priority = "high") {
|
|
604
653
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
605
654
|
_requestCount++;
|
|
606
|
-
const result = await
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
killAndRespawnDaemon();
|
|
612
|
-
const start = Date.now();
|
|
613
|
-
let delay2 = 200;
|
|
614
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
615
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
616
|
-
if (await connectToSocket()) break;
|
|
617
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
618
|
-
}
|
|
619
|
-
if (!_connected) return null;
|
|
620
|
-
const retry = await sendRequest(texts, priority);
|
|
621
|
-
if (!retry.error && retry.vectors) return retry.vectors;
|
|
622
|
-
process.stderr.write(`[exed-client] Batch retry also failed: ${retry.error ?? "no vectors"}
|
|
623
|
-
`);
|
|
624
|
-
}
|
|
625
|
-
return null;
|
|
655
|
+
const result = await retryThenRestart(
|
|
656
|
+
() => sendRequest(texts, priority),
|
|
657
|
+
"Batch embed"
|
|
658
|
+
);
|
|
659
|
+
return !result.error && result.vectors ? result.vectors : null;
|
|
626
660
|
}
|
|
627
661
|
function disconnectClient() {
|
|
628
662
|
if (_socket) {
|
|
@@ -640,7 +674,7 @@ function disconnectClient() {
|
|
|
640
674
|
function isClientConnected() {
|
|
641
675
|
return _connected;
|
|
642
676
|
}
|
|
643
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
677
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
644
678
|
var init_exe_daemon_client = __esm({
|
|
645
679
|
"src/lib/exe-daemon-client.ts"() {
|
|
646
680
|
"use strict";
|
|
@@ -655,7 +689,11 @@ var init_exe_daemon_client = __esm({
|
|
|
655
689
|
_connected = false;
|
|
656
690
|
_buffer = "";
|
|
657
691
|
_requestCount = 0;
|
|
692
|
+
_consecutiveFailures = 0;
|
|
658
693
|
HEALTH_CHECK_INTERVAL = 100;
|
|
694
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
695
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
696
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
659
697
|
_pending = /* @__PURE__ */ new Map();
|
|
660
698
|
MAX_BUFFER = 1e7;
|
|
661
699
|
}
|
|
@@ -699,8 +737,8 @@ async function embedDirect(text) {
|
|
|
699
737
|
const llamaCpp = await import("node-llama-cpp");
|
|
700
738
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
701
739
|
const { existsSync: existsSync32 } = await import("fs");
|
|
702
|
-
const
|
|
703
|
-
const modelPath =
|
|
740
|
+
const path41 = await import("path");
|
|
741
|
+
const modelPath = path41.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
704
742
|
if (!existsSync32(modelPath)) {
|
|
705
743
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
706
744
|
}
|
|
@@ -784,6 +822,118 @@ var init_db_retry = __esm({
|
|
|
784
822
|
}
|
|
785
823
|
});
|
|
786
824
|
|
|
825
|
+
// src/lib/runtime-table.ts
|
|
826
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
827
|
+
var init_runtime_table = __esm({
|
|
828
|
+
"src/lib/runtime-table.ts"() {
|
|
829
|
+
"use strict";
|
|
830
|
+
RUNTIME_TABLE = {
|
|
831
|
+
codex: {
|
|
832
|
+
binary: "codex",
|
|
833
|
+
launchMode: "interactive",
|
|
834
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
835
|
+
inlineFlag: "--no-alt-screen",
|
|
836
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
837
|
+
defaultModel: "gpt-5.4"
|
|
838
|
+
},
|
|
839
|
+
opencode: {
|
|
840
|
+
binary: "opencode",
|
|
841
|
+
launchMode: "exec",
|
|
842
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
843
|
+
inlineFlag: "",
|
|
844
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
845
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
DEFAULT_RUNTIME = "claude";
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// src/lib/agent-config.ts
|
|
853
|
+
var agent_config_exports = {};
|
|
854
|
+
__export(agent_config_exports, {
|
|
855
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
856
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
857
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
858
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
859
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
860
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
861
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
862
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
863
|
+
setAgentRuntime: () => setAgentRuntime
|
|
864
|
+
});
|
|
865
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
|
|
866
|
+
import path3 from "path";
|
|
867
|
+
function loadAgentConfig() {
|
|
868
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
869
|
+
try {
|
|
870
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
871
|
+
} catch {
|
|
872
|
+
return {};
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function saveAgentConfig(config2) {
|
|
876
|
+
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
877
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
878
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
879
|
+
}
|
|
880
|
+
function getAgentRuntime(agentId) {
|
|
881
|
+
const config2 = loadAgentConfig();
|
|
882
|
+
const entry = config2[agentId];
|
|
883
|
+
if (entry) return entry;
|
|
884
|
+
const orgDefault = config2["default"];
|
|
885
|
+
if (orgDefault) return orgDefault;
|
|
886
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
887
|
+
}
|
|
888
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
889
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
890
|
+
if (!knownModels) {
|
|
891
|
+
return {
|
|
892
|
+
ok: false,
|
|
893
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
if (!knownModels.includes(model)) {
|
|
897
|
+
return {
|
|
898
|
+
ok: false,
|
|
899
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const config2 = loadAgentConfig();
|
|
903
|
+
config2[agentId] = { runtime, model };
|
|
904
|
+
saveAgentConfig(config2);
|
|
905
|
+
return { ok: true };
|
|
906
|
+
}
|
|
907
|
+
function clearAgentRuntime(agentId) {
|
|
908
|
+
const config2 = loadAgentConfig();
|
|
909
|
+
delete config2[agentId];
|
|
910
|
+
saveAgentConfig(config2);
|
|
911
|
+
}
|
|
912
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
913
|
+
var init_agent_config = __esm({
|
|
914
|
+
"src/lib/agent-config.ts"() {
|
|
915
|
+
"use strict";
|
|
916
|
+
init_config();
|
|
917
|
+
init_runtime_table();
|
|
918
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
919
|
+
KNOWN_RUNTIMES = {
|
|
920
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
921
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
922
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
923
|
+
};
|
|
924
|
+
RUNTIME_LABELS = {
|
|
925
|
+
claude: "Claude Code (Anthropic)",
|
|
926
|
+
codex: "Codex (OpenAI)",
|
|
927
|
+
opencode: "OpenCode (open source)"
|
|
928
|
+
};
|
|
929
|
+
DEFAULT_MODELS = {
|
|
930
|
+
claude: "claude-opus-4",
|
|
931
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
932
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
787
937
|
// src/lib/employees.ts
|
|
788
938
|
var employees_exports = {};
|
|
789
939
|
__export(employees_exports, {
|
|
@@ -799,6 +949,7 @@ __export(employees_exports, {
|
|
|
799
949
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
800
950
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
801
951
|
hasRole: () => hasRole,
|
|
952
|
+
hireEmployee: () => hireEmployee,
|
|
802
953
|
isCoordinatorName: () => isCoordinatorName,
|
|
803
954
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
804
955
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -811,9 +962,9 @@ __export(employees_exports, {
|
|
|
811
962
|
validateEmployeeName: () => validateEmployeeName
|
|
812
963
|
});
|
|
813
964
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
814
|
-
import { existsSync as
|
|
965
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
815
966
|
import { execSync } from "child_process";
|
|
816
|
-
import
|
|
967
|
+
import path4 from "path";
|
|
817
968
|
import os3 from "os";
|
|
818
969
|
function normalizeRole(role) {
|
|
819
970
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -850,7 +1001,7 @@ function validateEmployeeName(name) {
|
|
|
850
1001
|
return { valid: true };
|
|
851
1002
|
}
|
|
852
1003
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
853
|
-
if (!
|
|
1004
|
+
if (!existsSync4(employeesPath)) {
|
|
854
1005
|
return [];
|
|
855
1006
|
}
|
|
856
1007
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -861,13 +1012,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
861
1012
|
}
|
|
862
1013
|
}
|
|
863
1014
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
864
|
-
await mkdir2(
|
|
1015
|
+
await mkdir2(path4.dirname(employeesPath), { recursive: true });
|
|
865
1016
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
866
1017
|
}
|
|
867
1018
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
868
|
-
if (!
|
|
1019
|
+
if (!existsSync4(employeesPath)) return [];
|
|
869
1020
|
try {
|
|
870
|
-
return JSON.parse(
|
|
1021
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
871
1022
|
} catch {
|
|
872
1023
|
return [];
|
|
873
1024
|
}
|
|
@@ -909,6 +1060,52 @@ function addEmployee(employees, employee) {
|
|
|
909
1060
|
}
|
|
910
1061
|
return [...employees, normalized];
|
|
911
1062
|
}
|
|
1063
|
+
function appendToCoordinatorTeam(employee) {
|
|
1064
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1065
|
+
if (!coordinator) return;
|
|
1066
|
+
const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1067
|
+
if (!existsSync4(idPath)) return;
|
|
1068
|
+
const content = readFileSync4(idPath, "utf-8");
|
|
1069
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1070
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1071
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
1072
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
1073
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
1074
|
+
const entry = `
|
|
1075
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
1076
|
+
`;
|
|
1077
|
+
let updated;
|
|
1078
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
1079
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
1080
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
1081
|
+
} else {
|
|
1082
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
1083
|
+
}
|
|
1084
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
1085
|
+
}
|
|
1086
|
+
function capitalize(s) {
|
|
1087
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1088
|
+
}
|
|
1089
|
+
async function hireEmployee(employee) {
|
|
1090
|
+
const employees = await loadEmployees();
|
|
1091
|
+
const updated = addEmployee(employees, employee);
|
|
1092
|
+
await saveEmployees(updated);
|
|
1093
|
+
try {
|
|
1094
|
+
appendToCoordinatorTeam(employee);
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
try {
|
|
1098
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
1099
|
+
const config2 = loadAgentConfig2();
|
|
1100
|
+
const name = employee.name.toLowerCase();
|
|
1101
|
+
if (!config2[name] && config2["default"]) {
|
|
1102
|
+
config2[name] = { ...config2["default"] };
|
|
1103
|
+
saveAgentConfig2(config2);
|
|
1104
|
+
}
|
|
1105
|
+
} catch {
|
|
1106
|
+
}
|
|
1107
|
+
return updated;
|
|
1108
|
+
}
|
|
912
1109
|
async function normalizeRosterCase(rosterPath) {
|
|
913
1110
|
const employees = await loadEmployees(rosterPath);
|
|
914
1111
|
let changed = false;
|
|
@@ -918,14 +1115,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
918
1115
|
emp.name = emp.name.toLowerCase();
|
|
919
1116
|
changed = true;
|
|
920
1117
|
try {
|
|
921
|
-
const identityDir =
|
|
922
|
-
const oldPath =
|
|
923
|
-
const newPath =
|
|
924
|
-
if (
|
|
1118
|
+
const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
|
|
1119
|
+
const oldPath = path4.join(identityDir, `${oldName}.md`);
|
|
1120
|
+
const newPath = path4.join(identityDir, `${emp.name}.md`);
|
|
1121
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
925
1122
|
renameSync2(oldPath, newPath);
|
|
926
|
-
} else if (
|
|
927
|
-
const content =
|
|
928
|
-
|
|
1123
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
1124
|
+
const content = readFileSync4(oldPath, "utf-8");
|
|
1125
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
929
1126
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
930
1127
|
unlinkSync2(oldPath);
|
|
931
1128
|
}
|
|
@@ -955,7 +1152,7 @@ function registerBinSymlinks(name) {
|
|
|
955
1152
|
errors.push("Could not find 'exe-os' in PATH");
|
|
956
1153
|
return { created, skipped, errors };
|
|
957
1154
|
}
|
|
958
|
-
const binDir =
|
|
1155
|
+
const binDir = path4.dirname(exeBinPath);
|
|
959
1156
|
let target;
|
|
960
1157
|
try {
|
|
961
1158
|
target = readlinkSync(exeBinPath);
|
|
@@ -965,8 +1162,8 @@ function registerBinSymlinks(name) {
|
|
|
965
1162
|
}
|
|
966
1163
|
for (const suffix of ["", "-opencode"]) {
|
|
967
1164
|
const linkName = `${name}${suffix}`;
|
|
968
|
-
const linkPath =
|
|
969
|
-
if (
|
|
1165
|
+
const linkPath = path4.join(binDir, linkName);
|
|
1166
|
+
if (existsSync4(linkPath)) {
|
|
970
1167
|
skipped.push(linkName);
|
|
971
1168
|
continue;
|
|
972
1169
|
}
|
|
@@ -979,15 +1176,601 @@ function registerBinSymlinks(name) {
|
|
|
979
1176
|
}
|
|
980
1177
|
return { created, skipped, errors };
|
|
981
1178
|
}
|
|
982
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
1179
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
983
1180
|
var init_employees = __esm({
|
|
984
1181
|
"src/lib/employees.ts"() {
|
|
985
1182
|
"use strict";
|
|
986
1183
|
init_config();
|
|
987
|
-
EMPLOYEES_PATH =
|
|
1184
|
+
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
988
1185
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
989
1186
|
COORDINATOR_ROLE = "COO";
|
|
990
1187
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1188
|
+
IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
|
|
1189
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
// src/lib/database-adapter.ts
|
|
1194
|
+
import os4 from "os";
|
|
1195
|
+
import path5 from "path";
|
|
1196
|
+
import { createRequire } from "module";
|
|
1197
|
+
import { pathToFileURL } from "url";
|
|
1198
|
+
function quotedIdentifier(identifier) {
|
|
1199
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1200
|
+
}
|
|
1201
|
+
function unqualifiedTableName(name) {
|
|
1202
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1203
|
+
const parts = raw.split(".");
|
|
1204
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1205
|
+
}
|
|
1206
|
+
function stripTrailingSemicolon(sql) {
|
|
1207
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1208
|
+
}
|
|
1209
|
+
function appendClause(sql, clause) {
|
|
1210
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1211
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1212
|
+
if (!returningMatch) {
|
|
1213
|
+
return `${trimmed}${clause}`;
|
|
1214
|
+
}
|
|
1215
|
+
const idx = returningMatch.index;
|
|
1216
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1217
|
+
}
|
|
1218
|
+
function normalizeStatement(stmt) {
|
|
1219
|
+
if (typeof stmt === "string") {
|
|
1220
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1221
|
+
}
|
|
1222
|
+
const sql = stmt.sql;
|
|
1223
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1224
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1225
|
+
}
|
|
1226
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1227
|
+
}
|
|
1228
|
+
function rewriteBooleanLiterals(sql) {
|
|
1229
|
+
let out = sql;
|
|
1230
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1231
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1232
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1233
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1234
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1235
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1236
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1237
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1238
|
+
}
|
|
1239
|
+
return out;
|
|
1240
|
+
}
|
|
1241
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1242
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1243
|
+
return sql;
|
|
1244
|
+
}
|
|
1245
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1246
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1247
|
+
}
|
|
1248
|
+
function rewriteInsertOrReplace(sql) {
|
|
1249
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1250
|
+
if (!match) {
|
|
1251
|
+
return sql;
|
|
1252
|
+
}
|
|
1253
|
+
const rawTable = match[1];
|
|
1254
|
+
const rawColumns = match[2];
|
|
1255
|
+
const remainder = match[3];
|
|
1256
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1257
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1258
|
+
if (!conflictKeys?.length) {
|
|
1259
|
+
return sql;
|
|
1260
|
+
}
|
|
1261
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1262
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1263
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1264
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1265
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1266
|
+
}
|
|
1267
|
+
function rewriteSql(sql) {
|
|
1268
|
+
let out = sql;
|
|
1269
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1270
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1271
|
+
out = rewriteBooleanLiterals(out);
|
|
1272
|
+
out = rewriteInsertOrReplace(out);
|
|
1273
|
+
out = rewriteInsertOrIgnore(out);
|
|
1274
|
+
return stripTrailingSemicolon(out);
|
|
1275
|
+
}
|
|
1276
|
+
function toBoolean(value) {
|
|
1277
|
+
if (value === null || value === void 0) return value;
|
|
1278
|
+
if (typeof value === "boolean") return value;
|
|
1279
|
+
if (typeof value === "number") return value !== 0;
|
|
1280
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1281
|
+
if (typeof value === "string") {
|
|
1282
|
+
const normalized = value.trim().toLowerCase();
|
|
1283
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1284
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1285
|
+
}
|
|
1286
|
+
return Boolean(value);
|
|
1287
|
+
}
|
|
1288
|
+
function countQuestionMarks(sql, end) {
|
|
1289
|
+
let count = 0;
|
|
1290
|
+
let inSingle = false;
|
|
1291
|
+
let inDouble = false;
|
|
1292
|
+
let inLineComment = false;
|
|
1293
|
+
let inBlockComment = false;
|
|
1294
|
+
for (let i = 0; i < end; i++) {
|
|
1295
|
+
const ch = sql[i];
|
|
1296
|
+
const next = sql[i + 1];
|
|
1297
|
+
if (inLineComment) {
|
|
1298
|
+
if (ch === "\n") inLineComment = false;
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
if (inBlockComment) {
|
|
1302
|
+
if (ch === "*" && next === "/") {
|
|
1303
|
+
inBlockComment = false;
|
|
1304
|
+
i += 1;
|
|
1305
|
+
}
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1309
|
+
inLineComment = true;
|
|
1310
|
+
i += 1;
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1314
|
+
inBlockComment = true;
|
|
1315
|
+
i += 1;
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1319
|
+
inSingle = !inSingle;
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1323
|
+
inDouble = !inDouble;
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1327
|
+
count += 1;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return count;
|
|
1331
|
+
}
|
|
1332
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1333
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1334
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1335
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1336
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1337
|
+
const matchText = match[0];
|
|
1338
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1339
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return indexes;
|
|
1343
|
+
}
|
|
1344
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1345
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1346
|
+
if (!match) return;
|
|
1347
|
+
const rawTable = match[1];
|
|
1348
|
+
const rawColumns = match[2];
|
|
1349
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1350
|
+
if (!boolColumns?.size) return;
|
|
1351
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1352
|
+
for (const [index, column] of columns.entries()) {
|
|
1353
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1354
|
+
args[index] = toBoolean(args[index]);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1359
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1360
|
+
if (!match) return;
|
|
1361
|
+
const rawTable = match[1];
|
|
1362
|
+
const setClause = match[2];
|
|
1363
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1364
|
+
if (!boolColumns?.size) return;
|
|
1365
|
+
const assignments = setClause.split(",");
|
|
1366
|
+
let placeholderIndex = 0;
|
|
1367
|
+
for (const assignment of assignments) {
|
|
1368
|
+
if (!assignment.includes("?")) continue;
|
|
1369
|
+
placeholderIndex += 1;
|
|
1370
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1371
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1372
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
function coerceBooleanArgs(sql, args) {
|
|
1377
|
+
const nextArgs = [...args];
|
|
1378
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1379
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1380
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1381
|
+
for (const index of placeholderIndexes) {
|
|
1382
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1383
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return nextArgs;
|
|
1387
|
+
}
|
|
1388
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1389
|
+
let out = "";
|
|
1390
|
+
let placeholder = 0;
|
|
1391
|
+
let inSingle = false;
|
|
1392
|
+
let inDouble = false;
|
|
1393
|
+
let inLineComment = false;
|
|
1394
|
+
let inBlockComment = false;
|
|
1395
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1396
|
+
const ch = sql[i];
|
|
1397
|
+
const next = sql[i + 1];
|
|
1398
|
+
if (inLineComment) {
|
|
1399
|
+
out += ch;
|
|
1400
|
+
if (ch === "\n") inLineComment = false;
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
if (inBlockComment) {
|
|
1404
|
+
out += ch;
|
|
1405
|
+
if (ch === "*" && next === "/") {
|
|
1406
|
+
out += next;
|
|
1407
|
+
inBlockComment = false;
|
|
1408
|
+
i += 1;
|
|
1409
|
+
}
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1413
|
+
out += ch + next;
|
|
1414
|
+
inLineComment = true;
|
|
1415
|
+
i += 1;
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1419
|
+
out += ch + next;
|
|
1420
|
+
inBlockComment = true;
|
|
1421
|
+
i += 1;
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1425
|
+
inSingle = !inSingle;
|
|
1426
|
+
out += ch;
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1430
|
+
inDouble = !inDouble;
|
|
1431
|
+
out += ch;
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1435
|
+
placeholder += 1;
|
|
1436
|
+
out += `$${placeholder}`;
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
out += ch;
|
|
1440
|
+
}
|
|
1441
|
+
return out;
|
|
1442
|
+
}
|
|
1443
|
+
function translateStatementForPostgres(stmt) {
|
|
1444
|
+
const normalized = normalizeStatement(stmt);
|
|
1445
|
+
if (normalized.kind === "named") {
|
|
1446
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1447
|
+
}
|
|
1448
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1449
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1450
|
+
return {
|
|
1451
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1452
|
+
args: coercedArgs
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function shouldBypassPostgres(stmt) {
|
|
1456
|
+
const normalized = normalizeStatement(stmt);
|
|
1457
|
+
if (normalized.kind === "named") {
|
|
1458
|
+
return true;
|
|
1459
|
+
}
|
|
1460
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1461
|
+
}
|
|
1462
|
+
function shouldFallbackOnError(error) {
|
|
1463
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1464
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1465
|
+
}
|
|
1466
|
+
function isReadQuery(sql) {
|
|
1467
|
+
const trimmed = sql.trimStart();
|
|
1468
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1469
|
+
}
|
|
1470
|
+
function buildRow(row, columns) {
|
|
1471
|
+
const values = columns.map((column) => row[column]);
|
|
1472
|
+
return Object.assign(values, row);
|
|
1473
|
+
}
|
|
1474
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1475
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1476
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1477
|
+
return {
|
|
1478
|
+
columns,
|
|
1479
|
+
columnTypes: columns.map(() => ""),
|
|
1480
|
+
rows: resultRows,
|
|
1481
|
+
rowsAffected,
|
|
1482
|
+
lastInsertRowid: void 0,
|
|
1483
|
+
toJSON() {
|
|
1484
|
+
return {
|
|
1485
|
+
columns,
|
|
1486
|
+
columnTypes: columns.map(() => ""),
|
|
1487
|
+
rows,
|
|
1488
|
+
rowsAffected,
|
|
1489
|
+
lastInsertRowid: void 0
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
async function loadPrismaClient() {
|
|
1495
|
+
if (!prismaClientPromise) {
|
|
1496
|
+
prismaClientPromise = (async () => {
|
|
1497
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1498
|
+
if (explicitPath) {
|
|
1499
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1500
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1501
|
+
if (!PrismaClient2) {
|
|
1502
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1503
|
+
}
|
|
1504
|
+
return new PrismaClient2();
|
|
1505
|
+
}
|
|
1506
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path5.join(os4.homedir(), "exe-db");
|
|
1507
|
+
const requireFromExeDb = createRequire(path5.join(exeDbRoot, "package.json"));
|
|
1508
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1509
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1510
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1511
|
+
if (!PrismaClient) {
|
|
1512
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1513
|
+
}
|
|
1514
|
+
return new PrismaClient();
|
|
1515
|
+
})();
|
|
1516
|
+
}
|
|
1517
|
+
return prismaClientPromise;
|
|
1518
|
+
}
|
|
1519
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1520
|
+
if (!compatibilityBootstrapPromise) {
|
|
1521
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1522
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1523
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1524
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1525
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1526
|
+
relation
|
|
1527
|
+
);
|
|
1528
|
+
if (!rows[0]?.regclass) {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
await prisma.$executeRawUnsafe(
|
|
1532
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
})();
|
|
1536
|
+
}
|
|
1537
|
+
return compatibilityBootstrapPromise;
|
|
1538
|
+
}
|
|
1539
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1540
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1541
|
+
if (isReadQuery(translated.sql)) {
|
|
1542
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1543
|
+
translated.sql,
|
|
1544
|
+
...translated.args
|
|
1545
|
+
);
|
|
1546
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1547
|
+
}
|
|
1548
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1549
|
+
return buildResultSet([], rowsAffected);
|
|
1550
|
+
}
|
|
1551
|
+
function splitSqlStatements(sql) {
|
|
1552
|
+
const parts = [];
|
|
1553
|
+
let current = "";
|
|
1554
|
+
let inSingle = false;
|
|
1555
|
+
let inDouble = false;
|
|
1556
|
+
let inLineComment = false;
|
|
1557
|
+
let inBlockComment = false;
|
|
1558
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1559
|
+
const ch = sql[i];
|
|
1560
|
+
const next = sql[i + 1];
|
|
1561
|
+
if (inLineComment) {
|
|
1562
|
+
current += ch;
|
|
1563
|
+
if (ch === "\n") inLineComment = false;
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1566
|
+
if (inBlockComment) {
|
|
1567
|
+
current += ch;
|
|
1568
|
+
if (ch === "*" && next === "/") {
|
|
1569
|
+
current += next;
|
|
1570
|
+
inBlockComment = false;
|
|
1571
|
+
i += 1;
|
|
1572
|
+
}
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1576
|
+
current += ch + next;
|
|
1577
|
+
inLineComment = true;
|
|
1578
|
+
i += 1;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1582
|
+
current += ch + next;
|
|
1583
|
+
inBlockComment = true;
|
|
1584
|
+
i += 1;
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1588
|
+
inSingle = !inSingle;
|
|
1589
|
+
current += ch;
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1593
|
+
inDouble = !inDouble;
|
|
1594
|
+
current += ch;
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1598
|
+
if (current.trim()) {
|
|
1599
|
+
parts.push(current.trim());
|
|
1600
|
+
}
|
|
1601
|
+
current = "";
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
current += ch;
|
|
1605
|
+
}
|
|
1606
|
+
if (current.trim()) {
|
|
1607
|
+
parts.push(current.trim());
|
|
1608
|
+
}
|
|
1609
|
+
return parts;
|
|
1610
|
+
}
|
|
1611
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1612
|
+
const prisma = await loadPrismaClient();
|
|
1613
|
+
await ensureCompatibilityViews(prisma);
|
|
1614
|
+
let closed = false;
|
|
1615
|
+
let adapter;
|
|
1616
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1617
|
+
if (!fallbackClient) {
|
|
1618
|
+
if (error) throw error;
|
|
1619
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1620
|
+
}
|
|
1621
|
+
if (error) {
|
|
1622
|
+
process.stderr.write(
|
|
1623
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1624
|
+
`
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
return fallbackClient.execute(stmt);
|
|
1628
|
+
};
|
|
1629
|
+
adapter = {
|
|
1630
|
+
async execute(stmt) {
|
|
1631
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1632
|
+
return fallbackExecute(stmt);
|
|
1633
|
+
}
|
|
1634
|
+
try {
|
|
1635
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
if (shouldFallbackOnError(error)) {
|
|
1638
|
+
return fallbackExecute(stmt, error);
|
|
1639
|
+
}
|
|
1640
|
+
throw error;
|
|
1641
|
+
}
|
|
1642
|
+
},
|
|
1643
|
+
async batch(stmts, mode) {
|
|
1644
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1645
|
+
if (!fallbackClient) {
|
|
1646
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1647
|
+
}
|
|
1648
|
+
return fallbackClient.batch(stmts, mode);
|
|
1649
|
+
}
|
|
1650
|
+
try {
|
|
1651
|
+
if (prisma.$transaction) {
|
|
1652
|
+
return await prisma.$transaction(async (tx) => {
|
|
1653
|
+
const results2 = [];
|
|
1654
|
+
for (const stmt of stmts) {
|
|
1655
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1656
|
+
}
|
|
1657
|
+
return results2;
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
const results = [];
|
|
1661
|
+
for (const stmt of stmts) {
|
|
1662
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1663
|
+
}
|
|
1664
|
+
return results;
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1667
|
+
process.stderr.write(
|
|
1668
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1669
|
+
`
|
|
1670
|
+
);
|
|
1671
|
+
return fallbackClient.batch(stmts, mode);
|
|
1672
|
+
}
|
|
1673
|
+
throw error;
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
async migrate(stmts) {
|
|
1677
|
+
if (fallbackClient) {
|
|
1678
|
+
return fallbackClient.migrate(stmts);
|
|
1679
|
+
}
|
|
1680
|
+
return adapter.batch(stmts, "deferred");
|
|
1681
|
+
},
|
|
1682
|
+
async transaction(mode) {
|
|
1683
|
+
if (!fallbackClient) {
|
|
1684
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1685
|
+
}
|
|
1686
|
+
return fallbackClient.transaction(mode);
|
|
1687
|
+
},
|
|
1688
|
+
async executeMultiple(sql) {
|
|
1689
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1690
|
+
return fallbackClient.executeMultiple(sql);
|
|
1691
|
+
}
|
|
1692
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1693
|
+
await adapter.execute(statement);
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
async sync() {
|
|
1697
|
+
if (fallbackClient) {
|
|
1698
|
+
return fallbackClient.sync();
|
|
1699
|
+
}
|
|
1700
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1701
|
+
},
|
|
1702
|
+
close() {
|
|
1703
|
+
closed = true;
|
|
1704
|
+
prismaClientPromise = null;
|
|
1705
|
+
compatibilityBootstrapPromise = null;
|
|
1706
|
+
void prisma.$disconnect?.();
|
|
1707
|
+
},
|
|
1708
|
+
get closed() {
|
|
1709
|
+
return closed;
|
|
1710
|
+
},
|
|
1711
|
+
get protocol() {
|
|
1712
|
+
return "prisma-postgres";
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
return adapter;
|
|
1716
|
+
}
|
|
1717
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1718
|
+
var init_database_adapter = __esm({
|
|
1719
|
+
"src/lib/database-adapter.ts"() {
|
|
1720
|
+
"use strict";
|
|
1721
|
+
VIEW_MAPPINGS = [
|
|
1722
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1723
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1724
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1725
|
+
{ view: "entities", source: "memory.entities" },
|
|
1726
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1727
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1728
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1729
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1730
|
+
{ view: "messages", source: "memory.messages" },
|
|
1731
|
+
{ view: "users", source: "wiki.users" },
|
|
1732
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1733
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1734
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1735
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1736
|
+
];
|
|
1737
|
+
UPSERT_KEYS = {
|
|
1738
|
+
memories: ["id"],
|
|
1739
|
+
tasks: ["id"],
|
|
1740
|
+
behaviors: ["id"],
|
|
1741
|
+
entities: ["id"],
|
|
1742
|
+
relationships: ["id"],
|
|
1743
|
+
entity_aliases: ["alias"],
|
|
1744
|
+
notifications: ["id"],
|
|
1745
|
+
messages: ["id"],
|
|
1746
|
+
users: ["id"],
|
|
1747
|
+
workspaces: ["id"],
|
|
1748
|
+
workspace_users: ["id"],
|
|
1749
|
+
documents: ["id"],
|
|
1750
|
+
chats: ["id"]
|
|
1751
|
+
};
|
|
1752
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1753
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1754
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1755
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1756
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1757
|
+
};
|
|
1758
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1759
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1760
|
+
);
|
|
1761
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1762
|
+
/\bPRAGMA\b/i,
|
|
1763
|
+
/\bsqlite_master\b/i,
|
|
1764
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1765
|
+
/\bMATCH\b/i,
|
|
1766
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1767
|
+
/\bjson_extract\s*\(/i,
|
|
1768
|
+
/\bjulianday\s*\(/i,
|
|
1769
|
+
/\bstrftime\s*\(/i,
|
|
1770
|
+
/\blast_insert_rowid\s*\(/i
|
|
1771
|
+
];
|
|
1772
|
+
prismaClientPromise = null;
|
|
1773
|
+
compatibilityBootstrapPromise = null;
|
|
991
1774
|
}
|
|
992
1775
|
});
|
|
993
1776
|
|
|
@@ -1061,7 +1844,7 @@ __export(db_daemon_client_exports, {
|
|
|
1061
1844
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1062
1845
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1063
1846
|
});
|
|
1064
|
-
function
|
|
1847
|
+
function normalizeStatement2(stmt) {
|
|
1065
1848
|
if (typeof stmt === "string") {
|
|
1066
1849
|
return { sql: stmt, args: [] };
|
|
1067
1850
|
}
|
|
@@ -1085,7 +1868,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1085
1868
|
if (!_useDaemon || !isClientConnected()) {
|
|
1086
1869
|
return fallbackClient.execute(stmt);
|
|
1087
1870
|
}
|
|
1088
|
-
const { sql, args } =
|
|
1871
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1089
1872
|
const response = await sendDaemonRequest({
|
|
1090
1873
|
type: "db-execute",
|
|
1091
1874
|
sql,
|
|
@@ -1110,7 +1893,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1110
1893
|
if (!_useDaemon || !isClientConnected()) {
|
|
1111
1894
|
return fallbackClient.batch(stmts, mode);
|
|
1112
1895
|
}
|
|
1113
|
-
const statements = stmts.map(
|
|
1896
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1114
1897
|
const response = await sendDaemonRequest({
|
|
1115
1898
|
type: "db-batch",
|
|
1116
1899
|
statements,
|
|
@@ -1205,6 +1988,18 @@ __export(database_exports, {
|
|
|
1205
1988
|
});
|
|
1206
1989
|
import { createClient } from "@libsql/client";
|
|
1207
1990
|
async function initDatabase(config2) {
|
|
1991
|
+
if (_walCheckpointTimer) {
|
|
1992
|
+
clearInterval(_walCheckpointTimer);
|
|
1993
|
+
_walCheckpointTimer = null;
|
|
1994
|
+
}
|
|
1995
|
+
if (_daemonClient) {
|
|
1996
|
+
_daemonClient.close();
|
|
1997
|
+
_daemonClient = null;
|
|
1998
|
+
}
|
|
1999
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2000
|
+
_adapterClient.close();
|
|
2001
|
+
}
|
|
2002
|
+
_adapterClient = null;
|
|
1208
2003
|
if (_client) {
|
|
1209
2004
|
_client.close();
|
|
1210
2005
|
_client = null;
|
|
@@ -1218,6 +2013,7 @@ async function initDatabase(config2) {
|
|
|
1218
2013
|
}
|
|
1219
2014
|
_client = createClient(opts);
|
|
1220
2015
|
_resilientClient = wrapWithRetry(_client);
|
|
2016
|
+
_adapterClient = _resilientClient;
|
|
1221
2017
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1222
2018
|
});
|
|
1223
2019
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1228,14 +2024,20 @@ async function initDatabase(config2) {
|
|
|
1228
2024
|
});
|
|
1229
2025
|
}, 3e4);
|
|
1230
2026
|
_walCheckpointTimer.unref();
|
|
2027
|
+
if (process.env.DATABASE_URL) {
|
|
2028
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2029
|
+
}
|
|
1231
2030
|
}
|
|
1232
2031
|
function isInitialized() {
|
|
1233
|
-
return _client !== null;
|
|
2032
|
+
return _adapterClient !== null || _client !== null;
|
|
1234
2033
|
}
|
|
1235
2034
|
function getClient() {
|
|
1236
|
-
if (!
|
|
2035
|
+
if (!_adapterClient) {
|
|
1237
2036
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1238
2037
|
}
|
|
2038
|
+
if (process.env.DATABASE_URL) {
|
|
2039
|
+
return _adapterClient;
|
|
2040
|
+
}
|
|
1239
2041
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1240
2042
|
return _resilientClient;
|
|
1241
2043
|
}
|
|
@@ -1245,6 +2047,7 @@ function getClient() {
|
|
|
1245
2047
|
return _resilientClient;
|
|
1246
2048
|
}
|
|
1247
2049
|
async function initDaemonClient() {
|
|
2050
|
+
if (process.env.DATABASE_URL) return;
|
|
1248
2051
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1249
2052
|
if (!_resilientClient) return;
|
|
1250
2053
|
try {
|
|
@@ -2189,26 +2992,36 @@ async function ensureSchema() {
|
|
|
2189
2992
|
}
|
|
2190
2993
|
}
|
|
2191
2994
|
async function disposeDatabase() {
|
|
2995
|
+
if (_walCheckpointTimer) {
|
|
2996
|
+
clearInterval(_walCheckpointTimer);
|
|
2997
|
+
_walCheckpointTimer = null;
|
|
2998
|
+
}
|
|
2192
2999
|
if (_daemonClient) {
|
|
2193
3000
|
_daemonClient.close();
|
|
2194
3001
|
_daemonClient = null;
|
|
2195
3002
|
}
|
|
3003
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
3004
|
+
_adapterClient.close();
|
|
3005
|
+
}
|
|
3006
|
+
_adapterClient = null;
|
|
2196
3007
|
if (_client) {
|
|
2197
3008
|
_client.close();
|
|
2198
3009
|
_client = null;
|
|
2199
3010
|
_resilientClient = null;
|
|
2200
3011
|
}
|
|
2201
3012
|
}
|
|
2202
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
3013
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2203
3014
|
var init_database = __esm({
|
|
2204
3015
|
"src/lib/database.ts"() {
|
|
2205
3016
|
"use strict";
|
|
2206
3017
|
init_db_retry();
|
|
2207
3018
|
init_employees();
|
|
3019
|
+
init_database_adapter();
|
|
2208
3020
|
_client = null;
|
|
2209
3021
|
_resilientClient = null;
|
|
2210
3022
|
_walCheckpointTimer = null;
|
|
2211
3023
|
_daemonClient = null;
|
|
3024
|
+
_adapterClient = null;
|
|
2212
3025
|
initTurso = initDatabase;
|
|
2213
3026
|
disposeTurso = disposeDatabase;
|
|
2214
3027
|
}
|
|
@@ -2224,14 +3037,14 @@ __export(keychain_exports, {
|
|
|
2224
3037
|
setMasterKey: () => setMasterKey
|
|
2225
3038
|
});
|
|
2226
3039
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2227
|
-
import { existsSync as
|
|
2228
|
-
import
|
|
2229
|
-
import
|
|
3040
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3041
|
+
import path6 from "path";
|
|
3042
|
+
import os5 from "os";
|
|
2230
3043
|
function getKeyDir() {
|
|
2231
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3044
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2232
3045
|
}
|
|
2233
3046
|
function getKeyPath() {
|
|
2234
|
-
return
|
|
3047
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2235
3048
|
}
|
|
2236
3049
|
async function tryKeytar() {
|
|
2237
3050
|
try {
|
|
@@ -2252,9 +3065,9 @@ async function getMasterKey() {
|
|
|
2252
3065
|
}
|
|
2253
3066
|
}
|
|
2254
3067
|
const keyPath = getKeyPath();
|
|
2255
|
-
if (!
|
|
3068
|
+
if (!existsSync5(keyPath)) {
|
|
2256
3069
|
process.stderr.write(
|
|
2257
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3070
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2258
3071
|
`
|
|
2259
3072
|
);
|
|
2260
3073
|
return null;
|
|
@@ -2295,7 +3108,7 @@ async function deleteMasterKey() {
|
|
|
2295
3108
|
}
|
|
2296
3109
|
}
|
|
2297
3110
|
const keyPath = getKeyPath();
|
|
2298
|
-
if (
|
|
3111
|
+
if (existsSync5(keyPath)) {
|
|
2299
3112
|
await unlink(keyPath);
|
|
2300
3113
|
}
|
|
2301
3114
|
}
|
|
@@ -2405,13 +3218,13 @@ __export(shard_manager_exports, {
|
|
|
2405
3218
|
listShards: () => listShards,
|
|
2406
3219
|
shardExists: () => shardExists
|
|
2407
3220
|
});
|
|
2408
|
-
import
|
|
2409
|
-
import { existsSync as
|
|
3221
|
+
import path7 from "path";
|
|
3222
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2410
3223
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2411
3224
|
function initShardManager(encryptionKey) {
|
|
2412
3225
|
_encryptionKey = encryptionKey;
|
|
2413
|
-
if (!
|
|
2414
|
-
|
|
3226
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
3227
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2415
3228
|
}
|
|
2416
3229
|
_shardingEnabled = true;
|
|
2417
3230
|
}
|
|
@@ -2431,7 +3244,7 @@ function getShardClient(projectName) {
|
|
|
2431
3244
|
}
|
|
2432
3245
|
const cached = _shards.get(safeName);
|
|
2433
3246
|
if (cached) return cached;
|
|
2434
|
-
const dbPath =
|
|
3247
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2435
3248
|
const client = createClient2({
|
|
2436
3249
|
url: `file:${dbPath}`,
|
|
2437
3250
|
encryptionKey: _encryptionKey
|
|
@@ -2441,10 +3254,10 @@ function getShardClient(projectName) {
|
|
|
2441
3254
|
}
|
|
2442
3255
|
function shardExists(projectName) {
|
|
2443
3256
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2444
|
-
return
|
|
3257
|
+
return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2445
3258
|
}
|
|
2446
3259
|
function listShards() {
|
|
2447
|
-
if (!
|
|
3260
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2448
3261
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2449
3262
|
}
|
|
2450
3263
|
async function ensureShardSchema(client) {
|
|
@@ -2518,7 +3331,23 @@ async function ensureShardSchema(client) {
|
|
|
2518
3331
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2519
3332
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2520
3333
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2521
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3334
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3335
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3336
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3337
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3338
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3339
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3340
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3341
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3342
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3343
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3344
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3345
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3346
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3347
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3348
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3349
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3350
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2522
3351
|
]) {
|
|
2523
3352
|
try {
|
|
2524
3353
|
await client.execute(col);
|
|
@@ -2630,7 +3459,7 @@ var init_shard_manager = __esm({
|
|
|
2630
3459
|
"src/lib/shard-manager.ts"() {
|
|
2631
3460
|
"use strict";
|
|
2632
3461
|
init_config();
|
|
2633
|
-
SHARDS_DIR =
|
|
3462
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
2634
3463
|
_shards = /* @__PURE__ */ new Map();
|
|
2635
3464
|
_encryptionKey = null;
|
|
2636
3465
|
_shardingEnabled = false;
|
|
@@ -3493,8 +4322,8 @@ __export(reranker_exports, {
|
|
|
3493
4322
|
rerankWithContext: () => rerankWithContext,
|
|
3494
4323
|
rerankWithScores: () => rerankWithScores
|
|
3495
4324
|
});
|
|
3496
|
-
import
|
|
3497
|
-
import { existsSync as
|
|
4325
|
+
import path8 from "path";
|
|
4326
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3498
4327
|
function resetIdleTimer() {
|
|
3499
4328
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
3500
4329
|
_idleTimer = setTimeout(() => {
|
|
@@ -3505,18 +4334,18 @@ function resetIdleTimer() {
|
|
|
3505
4334
|
}
|
|
3506
4335
|
}
|
|
3507
4336
|
function isRerankerAvailable() {
|
|
3508
|
-
return
|
|
4337
|
+
return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
3509
4338
|
}
|
|
3510
4339
|
function getRerankerModelPath() {
|
|
3511
|
-
return
|
|
4340
|
+
return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3512
4341
|
}
|
|
3513
4342
|
async function ensureLoaded() {
|
|
3514
4343
|
if (_rerankerContext) {
|
|
3515
4344
|
resetIdleTimer();
|
|
3516
4345
|
return;
|
|
3517
4346
|
}
|
|
3518
|
-
const modelPath =
|
|
3519
|
-
if (!
|
|
4347
|
+
const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
4348
|
+
if (!existsSync7(modelPath)) {
|
|
3520
4349
|
throw new Error(
|
|
3521
4350
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3522
4351
|
);
|
|
@@ -3619,7 +4448,7 @@ __export(project_name_exports, {
|
|
|
3619
4448
|
getProjectName: () => getProjectName
|
|
3620
4449
|
});
|
|
3621
4450
|
import { execSync as execSync2 } from "child_process";
|
|
3622
|
-
import
|
|
4451
|
+
import path9 from "path";
|
|
3623
4452
|
function getProjectName(cwd) {
|
|
3624
4453
|
const dir = cwd ?? process.cwd();
|
|
3625
4454
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
@@ -3632,7 +4461,7 @@ function getProjectName(cwd) {
|
|
|
3632
4461
|
timeout: 2e3,
|
|
3633
4462
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3634
4463
|
}).trim();
|
|
3635
|
-
repoRoot =
|
|
4464
|
+
repoRoot = path9.dirname(gitCommonDir);
|
|
3636
4465
|
} catch {
|
|
3637
4466
|
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
3638
4467
|
cwd: dir,
|
|
@@ -3641,11 +4470,11 @@ function getProjectName(cwd) {
|
|
|
3641
4470
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3642
4471
|
}).trim();
|
|
3643
4472
|
}
|
|
3644
|
-
_cached =
|
|
4473
|
+
_cached = path9.basename(repoRoot);
|
|
3645
4474
|
_cachedCwd = dir;
|
|
3646
4475
|
return _cached;
|
|
3647
4476
|
} catch {
|
|
3648
|
-
_cached =
|
|
4477
|
+
_cached = path9.basename(dir);
|
|
3649
4478
|
_cachedCwd = dir;
|
|
3650
4479
|
return _cached;
|
|
3651
4480
|
}
|
|
@@ -3669,8 +4498,8 @@ __export(file_grep_exports, {
|
|
|
3669
4498
|
grepProjectFiles: () => grepProjectFiles
|
|
3670
4499
|
});
|
|
3671
4500
|
import { execSync as execSync3 } from "child_process";
|
|
3672
|
-
import { readFileSync as
|
|
3673
|
-
import
|
|
4501
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync8 } from "fs";
|
|
4502
|
+
import path10 from "path";
|
|
3674
4503
|
import crypto from "crypto";
|
|
3675
4504
|
function hasRipgrep() {
|
|
3676
4505
|
if (_hasRg === null) {
|
|
@@ -3710,7 +4539,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
3710
4539
|
session_id: "file-grep",
|
|
3711
4540
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3712
4541
|
tool_name: "file_grep",
|
|
3713
|
-
project_name:
|
|
4542
|
+
project_name: path10.basename(projectRoot),
|
|
3714
4543
|
has_error: false,
|
|
3715
4544
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
3716
4545
|
vector: null,
|
|
@@ -3722,7 +4551,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
3722
4551
|
try {
|
|
3723
4552
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
3724
4553
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
3725
|
-
const source =
|
|
4554
|
+
const source = readFileSync5(filePath, "utf8");
|
|
3726
4555
|
const lines = source.split("\n");
|
|
3727
4556
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
3728
4557
|
const line = lines[i];
|
|
@@ -3784,11 +4613,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
3784
4613
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
3785
4614
|
const hits = [];
|
|
3786
4615
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
3787
|
-
const absPath =
|
|
4616
|
+
const absPath = path10.join(projectRoot, filePath);
|
|
3788
4617
|
try {
|
|
3789
4618
|
const stat = statSync2(absPath);
|
|
3790
4619
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
3791
|
-
const content =
|
|
4620
|
+
const content = readFileSync5(absPath, "utf8");
|
|
3792
4621
|
const lines = content.split("\n");
|
|
3793
4622
|
const matches = content.match(regex);
|
|
3794
4623
|
if (!matches || matches.length === 0) continue;
|
|
@@ -3811,15 +4640,15 @@ function collectFiles(root, patterns) {
|
|
|
3811
4640
|
const files = [];
|
|
3812
4641
|
function walk(dir, relative) {
|
|
3813
4642
|
if (files.length >= MAX_FILES) return;
|
|
3814
|
-
const basename =
|
|
4643
|
+
const basename = path10.basename(dir);
|
|
3815
4644
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
3816
4645
|
try {
|
|
3817
4646
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
3818
4647
|
for (const entry of entries) {
|
|
3819
4648
|
if (files.length >= MAX_FILES) return;
|
|
3820
|
-
const rel =
|
|
4649
|
+
const rel = path10.join(relative, entry.name);
|
|
3821
4650
|
if (entry.isDirectory()) {
|
|
3822
|
-
walk(
|
|
4651
|
+
walk(path10.join(dir, entry.name), rel);
|
|
3823
4652
|
} else if (entry.isFile()) {
|
|
3824
4653
|
for (const pat of patterns) {
|
|
3825
4654
|
if (matchGlob(rel, pat)) {
|
|
@@ -3851,7 +4680,7 @@ function matchGlob(filePath, pattern) {
|
|
|
3851
4680
|
if (slashIdx !== -1) {
|
|
3852
4681
|
const dir = pattern.slice(0, slashIdx);
|
|
3853
4682
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
3854
|
-
const fileDir =
|
|
4683
|
+
const fileDir = path10.dirname(filePath);
|
|
3855
4684
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
3856
4685
|
}
|
|
3857
4686
|
const ext = pattern.replace("*", "");
|
|
@@ -3859,9 +4688,9 @@ function matchGlob(filePath, pattern) {
|
|
|
3859
4688
|
}
|
|
3860
4689
|
function buildSnippet(hit, projectRoot) {
|
|
3861
4690
|
try {
|
|
3862
|
-
const absPath =
|
|
3863
|
-
if (!
|
|
3864
|
-
const lines =
|
|
4691
|
+
const absPath = path10.join(projectRoot, hit.filePath);
|
|
4692
|
+
if (!existsSync8(absPath)) return hit.matchLine;
|
|
4693
|
+
const lines = readFileSync5(absPath, "utf8").split("\n");
|
|
3865
4694
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
3866
4695
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
3867
4696
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -5086,9 +5915,9 @@ __export(active_agent_exports, {
|
|
|
5086
5915
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
5087
5916
|
writeActiveAgent: () => writeActiveAgent
|
|
5088
5917
|
});
|
|
5089
|
-
import { readFileSync as
|
|
5918
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
5090
5919
|
import { execSync as execSync5 } from "child_process";
|
|
5091
|
-
import
|
|
5920
|
+
import path11 from "path";
|
|
5092
5921
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
5093
5922
|
if (candidate === baseName) return true;
|
|
5094
5923
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -5132,12 +5961,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
5132
5961
|
return null;
|
|
5133
5962
|
}
|
|
5134
5963
|
function getMarkerPath() {
|
|
5135
|
-
return
|
|
5964
|
+
return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
5136
5965
|
}
|
|
5137
5966
|
function writeActiveAgent(agentId, agentRole) {
|
|
5138
5967
|
try {
|
|
5139
|
-
|
|
5140
|
-
|
|
5968
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
5969
|
+
writeFileSync3(
|
|
5141
5970
|
getMarkerPath(),
|
|
5142
5971
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
5143
5972
|
);
|
|
@@ -5153,7 +5982,7 @@ function clearActiveAgent() {
|
|
|
5153
5982
|
function getActiveAgent() {
|
|
5154
5983
|
try {
|
|
5155
5984
|
const markerPath = getMarkerPath();
|
|
5156
|
-
const raw =
|
|
5985
|
+
const raw = readFileSync6(markerPath, "utf8");
|
|
5157
5986
|
const data = JSON.parse(raw);
|
|
5158
5987
|
if (data.agentId) {
|
|
5159
5988
|
if (data.startedAt) {
|
|
@@ -5201,14 +6030,14 @@ function getAllActiveAgents() {
|
|
|
5201
6030
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
5202
6031
|
if (key === "undefined") continue;
|
|
5203
6032
|
try {
|
|
5204
|
-
const raw =
|
|
6033
|
+
const raw = readFileSync6(path11.join(CACHE_DIR, file), "utf8");
|
|
5205
6034
|
const data = JSON.parse(raw);
|
|
5206
6035
|
if (!data.agentId) continue;
|
|
5207
6036
|
if (data.startedAt) {
|
|
5208
6037
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
5209
6038
|
if (age > STALE_MS) {
|
|
5210
6039
|
try {
|
|
5211
|
-
unlinkSync3(
|
|
6040
|
+
unlinkSync3(path11.join(CACHE_DIR, file));
|
|
5212
6041
|
} catch {
|
|
5213
6042
|
}
|
|
5214
6043
|
continue;
|
|
@@ -5231,11 +6060,11 @@ function getAllActiveAgents() {
|
|
|
5231
6060
|
function cleanupSessionMarkers() {
|
|
5232
6061
|
const key = getSessionKey();
|
|
5233
6062
|
try {
|
|
5234
|
-
unlinkSync3(
|
|
6063
|
+
unlinkSync3(path11.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
5235
6064
|
} catch {
|
|
5236
6065
|
}
|
|
5237
6066
|
try {
|
|
5238
|
-
unlinkSync3(
|
|
6067
|
+
unlinkSync3(path11.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
5239
6068
|
} catch {
|
|
5240
6069
|
}
|
|
5241
6070
|
}
|
|
@@ -5246,7 +6075,7 @@ var init_active_agent = __esm({
|
|
|
5246
6075
|
init_config();
|
|
5247
6076
|
init_session_key();
|
|
5248
6077
|
init_employees();
|
|
5249
|
-
CACHE_DIR =
|
|
6078
|
+
CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
|
|
5250
6079
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
5251
6080
|
}
|
|
5252
6081
|
});
|
|
@@ -5268,9 +6097,9 @@ __export(license_exports, {
|
|
|
5268
6097
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
5269
6098
|
validateLicense: () => validateLicense
|
|
5270
6099
|
});
|
|
5271
|
-
import { readFileSync as
|
|
6100
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
5272
6101
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5273
|
-
import
|
|
6102
|
+
import path12 from "path";
|
|
5274
6103
|
import { jwtVerify, importSPKI } from "jose";
|
|
5275
6104
|
async function fetchRetry(url, init) {
|
|
5276
6105
|
try {
|
|
@@ -5281,37 +6110,37 @@ async function fetchRetry(url, init) {
|
|
|
5281
6110
|
}
|
|
5282
6111
|
}
|
|
5283
6112
|
function loadDeviceId() {
|
|
5284
|
-
const deviceJsonPath =
|
|
6113
|
+
const deviceJsonPath = path12.join(EXE_AI_DIR, "device.json");
|
|
5285
6114
|
try {
|
|
5286
|
-
if (
|
|
5287
|
-
const data = JSON.parse(
|
|
6115
|
+
if (existsSync9(deviceJsonPath)) {
|
|
6116
|
+
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
5288
6117
|
if (data.deviceId) return data.deviceId;
|
|
5289
6118
|
}
|
|
5290
6119
|
} catch {
|
|
5291
6120
|
}
|
|
5292
6121
|
try {
|
|
5293
|
-
if (
|
|
5294
|
-
const id2 =
|
|
6122
|
+
if (existsSync9(DEVICE_ID_PATH)) {
|
|
6123
|
+
const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
|
|
5295
6124
|
if (id2) return id2;
|
|
5296
6125
|
}
|
|
5297
6126
|
} catch {
|
|
5298
6127
|
}
|
|
5299
6128
|
const id = randomUUID3();
|
|
5300
|
-
|
|
5301
|
-
|
|
6129
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
6130
|
+
writeFileSync4(DEVICE_ID_PATH, id, "utf8");
|
|
5302
6131
|
return id;
|
|
5303
6132
|
}
|
|
5304
6133
|
function loadLicense() {
|
|
5305
6134
|
try {
|
|
5306
|
-
if (!
|
|
5307
|
-
return
|
|
6135
|
+
if (!existsSync9(LICENSE_PATH)) return null;
|
|
6136
|
+
return readFileSync7(LICENSE_PATH, "utf8").trim();
|
|
5308
6137
|
} catch {
|
|
5309
6138
|
return null;
|
|
5310
6139
|
}
|
|
5311
6140
|
}
|
|
5312
6141
|
function saveLicense(apiKey) {
|
|
5313
|
-
|
|
5314
|
-
|
|
6142
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
6143
|
+
writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
5315
6144
|
}
|
|
5316
6145
|
async function verifyLicenseJwt(token) {
|
|
5317
6146
|
try {
|
|
@@ -5337,8 +6166,8 @@ async function verifyLicenseJwt(token) {
|
|
|
5337
6166
|
}
|
|
5338
6167
|
async function getCachedLicense() {
|
|
5339
6168
|
try {
|
|
5340
|
-
if (!
|
|
5341
|
-
const raw = JSON.parse(
|
|
6169
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
6170
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
5342
6171
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
5343
6172
|
return await verifyLicenseJwt(raw.token);
|
|
5344
6173
|
} catch {
|
|
@@ -5347,8 +6176,8 @@ async function getCachedLicense() {
|
|
|
5347
6176
|
}
|
|
5348
6177
|
function readCachedToken() {
|
|
5349
6178
|
try {
|
|
5350
|
-
if (!
|
|
5351
|
-
const raw = JSON.parse(
|
|
6179
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
6180
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
5352
6181
|
return typeof raw.token === "string" ? raw.token : null;
|
|
5353
6182
|
} catch {
|
|
5354
6183
|
return null;
|
|
@@ -5382,7 +6211,7 @@ function getRawCachedPlan() {
|
|
|
5382
6211
|
}
|
|
5383
6212
|
function cacheResponse(token) {
|
|
5384
6213
|
try {
|
|
5385
|
-
|
|
6214
|
+
writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
5386
6215
|
} catch {
|
|
5387
6216
|
}
|
|
5388
6217
|
}
|
|
@@ -5446,9 +6275,9 @@ async function checkLicense() {
|
|
|
5446
6275
|
let key = loadLicense();
|
|
5447
6276
|
if (!key) {
|
|
5448
6277
|
try {
|
|
5449
|
-
const configPath =
|
|
5450
|
-
if (
|
|
5451
|
-
const raw = JSON.parse(
|
|
6278
|
+
const configPath = path12.join(EXE_AI_DIR, "config.json");
|
|
6279
|
+
if (existsSync9(configPath)) {
|
|
6280
|
+
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
5452
6281
|
const cloud = raw.cloud;
|
|
5453
6282
|
if (cloud?.apiKey) {
|
|
5454
6283
|
key = cloud.apiKey;
|
|
@@ -5607,9 +6436,9 @@ var init_license = __esm({
|
|
|
5607
6436
|
"src/lib/license.ts"() {
|
|
5608
6437
|
"use strict";
|
|
5609
6438
|
init_config();
|
|
5610
|
-
LICENSE_PATH =
|
|
5611
|
-
CACHE_PATH =
|
|
5612
|
-
DEVICE_ID_PATH =
|
|
6439
|
+
LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
|
|
6440
|
+
CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
6441
|
+
DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
|
|
5613
6442
|
API_BASE = "https://askexe.com/cloud";
|
|
5614
6443
|
RETRY_DELAY_MS = 500;
|
|
5615
6444
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -5649,12 +6478,12 @@ __export(plan_limits_exports, {
|
|
|
5649
6478
|
countActiveMemories: () => countActiveMemories,
|
|
5650
6479
|
getLicenseSync: () => getLicenseSync
|
|
5651
6480
|
});
|
|
5652
|
-
import { readFileSync as
|
|
5653
|
-
import
|
|
6481
|
+
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
6482
|
+
import path13 from "path";
|
|
5654
6483
|
function getLicenseSync() {
|
|
5655
6484
|
try {
|
|
5656
|
-
if (!
|
|
5657
|
-
const raw = JSON.parse(
|
|
6485
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
6486
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
5658
6487
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
5659
6488
|
const parts = raw.token.split(".");
|
|
5660
6489
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -5721,8 +6550,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
5721
6550
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
5722
6551
|
let count = 0;
|
|
5723
6552
|
try {
|
|
5724
|
-
if (
|
|
5725
|
-
const raw =
|
|
6553
|
+
if (existsSync10(filePath)) {
|
|
6554
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
5726
6555
|
const employees = JSON.parse(raw);
|
|
5727
6556
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
5728
6557
|
}
|
|
@@ -5759,19 +6588,19 @@ var init_plan_limits = __esm({
|
|
|
5759
6588
|
this.name = "PlanLimitError";
|
|
5760
6589
|
}
|
|
5761
6590
|
};
|
|
5762
|
-
CACHE_PATH2 =
|
|
6591
|
+
CACHE_PATH2 = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
5763
6592
|
}
|
|
5764
6593
|
});
|
|
5765
6594
|
|
|
5766
6595
|
// src/lib/notifications.ts
|
|
5767
6596
|
import crypto4 from "crypto";
|
|
5768
|
-
import
|
|
5769
|
-
import
|
|
6597
|
+
import path16 from "path";
|
|
6598
|
+
import os6 from "os";
|
|
5770
6599
|
import {
|
|
5771
|
-
readFileSync as
|
|
6600
|
+
readFileSync as readFileSync9,
|
|
5772
6601
|
readdirSync as readdirSync4,
|
|
5773
6602
|
unlinkSync as unlinkSync4,
|
|
5774
|
-
existsSync as
|
|
6603
|
+
existsSync as existsSync11,
|
|
5775
6604
|
rmdirSync
|
|
5776
6605
|
} from "fs";
|
|
5777
6606
|
async function writeNotification(notification) {
|
|
@@ -5816,13 +6645,13 @@ var init_notifications = __esm({
|
|
|
5816
6645
|
});
|
|
5817
6646
|
|
|
5818
6647
|
// src/lib/session-registry.ts
|
|
5819
|
-
import { readFileSync as
|
|
5820
|
-
import
|
|
5821
|
-
import
|
|
6648
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync12 } from "fs";
|
|
6649
|
+
import path17 from "path";
|
|
6650
|
+
import os7 from "os";
|
|
5822
6651
|
function registerSession(entry) {
|
|
5823
|
-
const dir =
|
|
5824
|
-
if (!
|
|
5825
|
-
|
|
6652
|
+
const dir = path17.dirname(REGISTRY_PATH);
|
|
6653
|
+
if (!existsSync12(dir)) {
|
|
6654
|
+
mkdirSync5(dir, { recursive: true });
|
|
5826
6655
|
}
|
|
5827
6656
|
const sessions = listSessions();
|
|
5828
6657
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -5831,11 +6660,11 @@ function registerSession(entry) {
|
|
|
5831
6660
|
} else {
|
|
5832
6661
|
sessions.push(entry);
|
|
5833
6662
|
}
|
|
5834
|
-
|
|
6663
|
+
writeFileSync7(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
5835
6664
|
}
|
|
5836
6665
|
function listSessions() {
|
|
5837
6666
|
try {
|
|
5838
|
-
const raw =
|
|
6667
|
+
const raw = readFileSync10(REGISTRY_PATH, "utf8");
|
|
5839
6668
|
return JSON.parse(raw);
|
|
5840
6669
|
} catch {
|
|
5841
6670
|
return [];
|
|
@@ -5845,7 +6674,7 @@ var REGISTRY_PATH;
|
|
|
5845
6674
|
var init_session_registry = __esm({
|
|
5846
6675
|
"src/lib/session-registry.ts"() {
|
|
5847
6676
|
"use strict";
|
|
5848
|
-
REGISTRY_PATH =
|
|
6677
|
+
REGISTRY_PATH = path17.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
5849
6678
|
}
|
|
5850
6679
|
});
|
|
5851
6680
|
|
|
@@ -6029,101 +6858,6 @@ var init_provider_table = __esm({
|
|
|
6029
6858
|
}
|
|
6030
6859
|
});
|
|
6031
6860
|
|
|
6032
|
-
// src/lib/runtime-table.ts
|
|
6033
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
6034
|
-
var init_runtime_table = __esm({
|
|
6035
|
-
"src/lib/runtime-table.ts"() {
|
|
6036
|
-
"use strict";
|
|
6037
|
-
RUNTIME_TABLE = {
|
|
6038
|
-
codex: {
|
|
6039
|
-
binary: "codex",
|
|
6040
|
-
launchMode: "interactive",
|
|
6041
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
6042
|
-
inlineFlag: "--no-alt-screen",
|
|
6043
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
6044
|
-
defaultModel: "gpt-5.4"
|
|
6045
|
-
},
|
|
6046
|
-
opencode: {
|
|
6047
|
-
binary: "opencode",
|
|
6048
|
-
launchMode: "exec",
|
|
6049
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
6050
|
-
inlineFlag: "",
|
|
6051
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
6052
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
6053
|
-
}
|
|
6054
|
-
};
|
|
6055
|
-
DEFAULT_RUNTIME = "claude";
|
|
6056
|
-
}
|
|
6057
|
-
});
|
|
6058
|
-
|
|
6059
|
-
// src/lib/agent-config.ts
|
|
6060
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
|
|
6061
|
-
import path16 from "path";
|
|
6062
|
-
function loadAgentConfig() {
|
|
6063
|
-
if (!existsSync12(AGENT_CONFIG_PATH)) return {};
|
|
6064
|
-
try {
|
|
6065
|
-
return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
|
|
6066
|
-
} catch {
|
|
6067
|
-
return {};
|
|
6068
|
-
}
|
|
6069
|
-
}
|
|
6070
|
-
function saveAgentConfig(config2) {
|
|
6071
|
-
const dir = path16.dirname(AGENT_CONFIG_PATH);
|
|
6072
|
-
if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
|
|
6073
|
-
writeFileSync7(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
6074
|
-
}
|
|
6075
|
-
function getAgentRuntime(agentId) {
|
|
6076
|
-
const config2 = loadAgentConfig();
|
|
6077
|
-
const entry = config2[agentId];
|
|
6078
|
-
if (entry) return entry;
|
|
6079
|
-
const orgDefault = config2["default"];
|
|
6080
|
-
if (orgDefault) return orgDefault;
|
|
6081
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
6082
|
-
}
|
|
6083
|
-
function setAgentRuntime(agentId, runtime, model) {
|
|
6084
|
-
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
6085
|
-
if (!knownModels) {
|
|
6086
|
-
return {
|
|
6087
|
-
ok: false,
|
|
6088
|
-
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
6089
|
-
};
|
|
6090
|
-
}
|
|
6091
|
-
if (!knownModels.includes(model)) {
|
|
6092
|
-
return {
|
|
6093
|
-
ok: false,
|
|
6094
|
-
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
6095
|
-
};
|
|
6096
|
-
}
|
|
6097
|
-
const config2 = loadAgentConfig();
|
|
6098
|
-
config2[agentId] = { runtime, model };
|
|
6099
|
-
saveAgentConfig(config2);
|
|
6100
|
-
return { ok: true };
|
|
6101
|
-
}
|
|
6102
|
-
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
6103
|
-
var init_agent_config = __esm({
|
|
6104
|
-
"src/lib/agent-config.ts"() {
|
|
6105
|
-
"use strict";
|
|
6106
|
-
init_config();
|
|
6107
|
-
init_runtime_table();
|
|
6108
|
-
AGENT_CONFIG_PATH = path16.join(EXE_AI_DIR, "agent-config.json");
|
|
6109
|
-
KNOWN_RUNTIMES = {
|
|
6110
|
-
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
6111
|
-
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
6112
|
-
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
6113
|
-
};
|
|
6114
|
-
RUNTIME_LABELS = {
|
|
6115
|
-
claude: "Claude Code (Anthropic)",
|
|
6116
|
-
codex: "Codex (OpenAI)",
|
|
6117
|
-
opencode: "OpenCode (open source)"
|
|
6118
|
-
};
|
|
6119
|
-
DEFAULT_MODELS = {
|
|
6120
|
-
claude: "claude-opus-4",
|
|
6121
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
6122
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
6123
|
-
};
|
|
6124
|
-
}
|
|
6125
|
-
});
|
|
6126
|
-
|
|
6127
6861
|
// src/lib/intercom-queue.ts
|
|
6128
6862
|
var intercom_queue_exports = {};
|
|
6129
6863
|
__export(intercom_queue_exports, {
|
|
@@ -6134,10 +6868,10 @@ __export(intercom_queue_exports, {
|
|
|
6134
6868
|
readQueue: () => readQueue
|
|
6135
6869
|
});
|
|
6136
6870
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
6137
|
-
import
|
|
6138
|
-
import
|
|
6871
|
+
import path18 from "path";
|
|
6872
|
+
import os8 from "os";
|
|
6139
6873
|
function ensureDir() {
|
|
6140
|
-
const dir =
|
|
6874
|
+
const dir = path18.dirname(QUEUE_PATH);
|
|
6141
6875
|
if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
|
|
6142
6876
|
}
|
|
6143
6877
|
function readQueue() {
|
|
@@ -6243,10 +6977,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
6243
6977
|
var init_intercom_queue = __esm({
|
|
6244
6978
|
"src/lib/intercom-queue.ts"() {
|
|
6245
6979
|
"use strict";
|
|
6246
|
-
QUEUE_PATH =
|
|
6980
|
+
QUEUE_PATH = path18.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
6247
6981
|
MAX_RETRIES2 = 5;
|
|
6248
6982
|
TTL_MS = 60 * 60 * 1e3;
|
|
6249
|
-
INTERCOM_LOG =
|
|
6983
|
+
INTERCOM_LOG = path18.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
6250
6984
|
}
|
|
6251
6985
|
});
|
|
6252
6986
|
|
|
@@ -6625,12 +7359,12 @@ __export(tmux_routing_exports, {
|
|
|
6625
7359
|
});
|
|
6626
7360
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
6627
7361
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync5 } from "fs";
|
|
6628
|
-
import
|
|
6629
|
-
import
|
|
7362
|
+
import path19 from "path";
|
|
7363
|
+
import os9 from "os";
|
|
6630
7364
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6631
7365
|
import { unlinkSync as unlinkSync5 } from "fs";
|
|
6632
7366
|
function spawnLockPath(sessionName) {
|
|
6633
|
-
return
|
|
7367
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6634
7368
|
}
|
|
6635
7369
|
function isProcessAlive(pid) {
|
|
6636
7370
|
try {
|
|
@@ -6667,8 +7401,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6667
7401
|
function resolveBehaviorsExporterScript() {
|
|
6668
7402
|
try {
|
|
6669
7403
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6670
|
-
const scriptPath =
|
|
6671
|
-
|
|
7404
|
+
const scriptPath = path19.join(
|
|
7405
|
+
path19.dirname(thisFile),
|
|
6672
7406
|
"..",
|
|
6673
7407
|
"bin",
|
|
6674
7408
|
"exe-export-behaviors.js"
|
|
@@ -6743,7 +7477,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6743
7477
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6744
7478
|
}
|
|
6745
7479
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6746
|
-
const filePath =
|
|
7480
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6747
7481
|
writeFileSync9(filePath, JSON.stringify({
|
|
6748
7482
|
parentExe: rootExe,
|
|
6749
7483
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -6752,7 +7486,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6752
7486
|
}
|
|
6753
7487
|
function getParentExe(sessionKey) {
|
|
6754
7488
|
try {
|
|
6755
|
-
const data = JSON.parse(readFileSync12(
|
|
7489
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6756
7490
|
return data.parentExe || null;
|
|
6757
7491
|
} catch {
|
|
6758
7492
|
return null;
|
|
@@ -6761,7 +7495,7 @@ function getParentExe(sessionKey) {
|
|
|
6761
7495
|
function getDispatchedBy(sessionKey) {
|
|
6762
7496
|
try {
|
|
6763
7497
|
const data = JSON.parse(readFileSync12(
|
|
6764
|
-
|
|
7498
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6765
7499
|
"utf8"
|
|
6766
7500
|
));
|
|
6767
7501
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6947,7 +7681,7 @@ function sendIntercom(targetSession) {
|
|
|
6947
7681
|
try {
|
|
6948
7682
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6949
7683
|
const agent = baseAgentName(rawAgent);
|
|
6950
|
-
const markerPath =
|
|
7684
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6951
7685
|
if (existsSync14(markerPath)) {
|
|
6952
7686
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6953
7687
|
return "debounced";
|
|
@@ -6957,7 +7691,7 @@ function sendIntercom(targetSession) {
|
|
|
6957
7691
|
try {
|
|
6958
7692
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6959
7693
|
const agent = baseAgentName(rawAgent);
|
|
6960
|
-
const taskDir =
|
|
7694
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
6961
7695
|
if (existsSync14(taskDir)) {
|
|
6962
7696
|
const files = readdirSync5(taskDir).filter(
|
|
6963
7697
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -7091,8 +7825,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7091
7825
|
const transport = getTransport();
|
|
7092
7826
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7093
7827
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7094
|
-
const logDir =
|
|
7095
|
-
const logFile =
|
|
7828
|
+
const logDir = path19.join(os9.homedir(), ".exe-os", "session-logs");
|
|
7829
|
+
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7096
7830
|
if (!existsSync14(logDir)) {
|
|
7097
7831
|
mkdirSync7(logDir, { recursive: true });
|
|
7098
7832
|
}
|
|
@@ -7100,14 +7834,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7100
7834
|
let cleanupSuffix = "";
|
|
7101
7835
|
try {
|
|
7102
7836
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7103
|
-
const cleanupScript =
|
|
7837
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7104
7838
|
if (existsSync14(cleanupScript)) {
|
|
7105
7839
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7106
7840
|
}
|
|
7107
7841
|
} catch {
|
|
7108
7842
|
}
|
|
7109
7843
|
try {
|
|
7110
|
-
const claudeJsonPath =
|
|
7844
|
+
const claudeJsonPath = path19.join(os9.homedir(), ".claude.json");
|
|
7111
7845
|
let claudeJson = {};
|
|
7112
7846
|
try {
|
|
7113
7847
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -7122,10 +7856,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7122
7856
|
} catch {
|
|
7123
7857
|
}
|
|
7124
7858
|
try {
|
|
7125
|
-
const settingsDir =
|
|
7859
|
+
const settingsDir = path19.join(os9.homedir(), ".claude", "projects");
|
|
7126
7860
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7127
|
-
const projSettingsDir =
|
|
7128
|
-
const settingsPath =
|
|
7861
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
7862
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
7129
7863
|
let settings = {};
|
|
7130
7864
|
try {
|
|
7131
7865
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -7172,8 +7906,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7172
7906
|
let behaviorsFlag = "";
|
|
7173
7907
|
let legacyFallbackWarned = false;
|
|
7174
7908
|
if (!useExeAgent && !useBinSymlink) {
|
|
7175
|
-
const identityPath2 =
|
|
7176
|
-
|
|
7909
|
+
const identityPath2 = path19.join(
|
|
7910
|
+
os9.homedir(),
|
|
7177
7911
|
".exe-os",
|
|
7178
7912
|
"identity",
|
|
7179
7913
|
`${employeeName}.md`
|
|
@@ -7188,7 +7922,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7188
7922
|
}
|
|
7189
7923
|
const behaviorsFile = exportBehaviorsSync(
|
|
7190
7924
|
employeeName,
|
|
7191
|
-
|
|
7925
|
+
path19.basename(spawnCwd),
|
|
7192
7926
|
sessionName
|
|
7193
7927
|
);
|
|
7194
7928
|
if (behaviorsFile) {
|
|
@@ -7203,9 +7937,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7203
7937
|
}
|
|
7204
7938
|
let sessionContextFlag = "";
|
|
7205
7939
|
try {
|
|
7206
|
-
const ctxDir =
|
|
7940
|
+
const ctxDir = path19.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7207
7941
|
mkdirSync7(ctxDir, { recursive: true });
|
|
7208
|
-
const ctxFile =
|
|
7942
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7209
7943
|
const ctxContent = [
|
|
7210
7944
|
`## Session Context`,
|
|
7211
7945
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -7289,7 +8023,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7289
8023
|
transport.pipeLog(sessionName, logFile);
|
|
7290
8024
|
try {
|
|
7291
8025
|
const mySession = getMySession();
|
|
7292
|
-
const dispatchInfo =
|
|
8026
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7293
8027
|
writeFileSync9(dispatchInfo, JSON.stringify({
|
|
7294
8028
|
dispatchedBy: mySession,
|
|
7295
8029
|
rootExe: exeSession,
|
|
@@ -7364,15 +8098,15 @@ var init_tmux_routing = __esm({
|
|
|
7364
8098
|
init_intercom_queue();
|
|
7365
8099
|
init_plan_limits();
|
|
7366
8100
|
init_employees();
|
|
7367
|
-
SPAWN_LOCK_DIR =
|
|
7368
|
-
SESSION_CACHE =
|
|
8101
|
+
SPAWN_LOCK_DIR = path19.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
8102
|
+
SESSION_CACHE = path19.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7369
8103
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7370
8104
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7371
8105
|
VERIFY_PANE_LINES = 200;
|
|
7372
8106
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7373
8107
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7374
|
-
INTERCOM_LOG2 =
|
|
7375
|
-
DEBOUNCE_FILE =
|
|
8108
|
+
INTERCOM_LOG2 = path19.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
8109
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7376
8110
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7377
8111
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7378
8112
|
}
|
|
@@ -7420,8 +8154,8 @@ __export(tasks_crud_exports, {
|
|
|
7420
8154
|
writeCheckpoint: () => writeCheckpoint
|
|
7421
8155
|
});
|
|
7422
8156
|
import crypto6 from "crypto";
|
|
7423
|
-
import
|
|
7424
|
-
import
|
|
8157
|
+
import path20 from "path";
|
|
8158
|
+
import os10 from "os";
|
|
7425
8159
|
import { execSync as execSync8 } from "child_process";
|
|
7426
8160
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
7427
8161
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
@@ -7599,8 +8333,8 @@ ${laneWarning}` : laneWarning;
|
|
|
7599
8333
|
}
|
|
7600
8334
|
if (input.baseDir) {
|
|
7601
8335
|
try {
|
|
7602
|
-
await mkdir4(
|
|
7603
|
-
await mkdir4(
|
|
8336
|
+
await mkdir4(path20.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
8337
|
+
await mkdir4(path20.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
7604
8338
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
7605
8339
|
await ensureGitignoreExe(input.baseDir);
|
|
7606
8340
|
} catch {
|
|
@@ -7636,9 +8370,9 @@ ${laneWarning}` : laneWarning;
|
|
|
7636
8370
|
});
|
|
7637
8371
|
if (input.baseDir) {
|
|
7638
8372
|
try {
|
|
7639
|
-
const EXE_OS_DIR =
|
|
7640
|
-
const mdPath =
|
|
7641
|
-
const mdDir =
|
|
8373
|
+
const EXE_OS_DIR = path20.join(os10.homedir(), ".exe-os");
|
|
8374
|
+
const mdPath = path20.join(EXE_OS_DIR, taskFile);
|
|
8375
|
+
const mdDir = path20.dirname(mdPath);
|
|
7642
8376
|
if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
7643
8377
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
7644
8378
|
const mdContent = `# ${input.title}
|
|
@@ -7939,7 +8673,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
7939
8673
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
7940
8674
|
}
|
|
7941
8675
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
7942
|
-
const archPath =
|
|
8676
|
+
const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
7943
8677
|
try {
|
|
7944
8678
|
if (existsSync15(archPath)) return;
|
|
7945
8679
|
const template = [
|
|
@@ -7974,7 +8708,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
7974
8708
|
}
|
|
7975
8709
|
}
|
|
7976
8710
|
async function ensureGitignoreExe(baseDir) {
|
|
7977
|
-
const gitignorePath =
|
|
8711
|
+
const gitignorePath = path20.join(baseDir, ".gitignore");
|
|
7978
8712
|
try {
|
|
7979
8713
|
if (existsSync15(gitignorePath)) {
|
|
7980
8714
|
const content = readFileSync13(gitignorePath, "utf-8");
|
|
@@ -8008,13 +8742,13 @@ var init_tasks_crud = __esm({
|
|
|
8008
8742
|
});
|
|
8009
8743
|
|
|
8010
8744
|
// src/lib/tasks-review.ts
|
|
8011
|
-
import
|
|
8745
|
+
import path21 from "path";
|
|
8012
8746
|
import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
8013
8747
|
async function countPendingReviews(sessionScope) {
|
|
8014
8748
|
const client = getClient();
|
|
8015
8749
|
if (sessionScope) {
|
|
8016
8750
|
const result2 = await client.execute({
|
|
8017
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
8751
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
8018
8752
|
args: [sessionScope]
|
|
8019
8753
|
});
|
|
8020
8754
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -8190,11 +8924,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
8190
8924
|
);
|
|
8191
8925
|
}
|
|
8192
8926
|
try {
|
|
8193
|
-
const cacheDir =
|
|
8927
|
+
const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
|
|
8194
8928
|
if (existsSync16(cacheDir)) {
|
|
8195
8929
|
for (const f of readdirSync6(cacheDir)) {
|
|
8196
8930
|
if (f.startsWith("review-notified-")) {
|
|
8197
|
-
unlinkSync6(
|
|
8931
|
+
unlinkSync6(path21.join(cacheDir, f));
|
|
8198
8932
|
}
|
|
8199
8933
|
}
|
|
8200
8934
|
}
|
|
@@ -8215,7 +8949,7 @@ var init_tasks_review = __esm({
|
|
|
8215
8949
|
});
|
|
8216
8950
|
|
|
8217
8951
|
// src/lib/tasks-chain.ts
|
|
8218
|
-
import
|
|
8952
|
+
import path22 from "path";
|
|
8219
8953
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
8220
8954
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
8221
8955
|
const client = getClient();
|
|
@@ -8232,7 +8966,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
8232
8966
|
});
|
|
8233
8967
|
for (const ur of unblockedRows.rows) {
|
|
8234
8968
|
try {
|
|
8235
|
-
const ubFile =
|
|
8969
|
+
const ubFile = path22.join(baseDir, String(ur.task_file));
|
|
8236
8970
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
8237
8971
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
8238
8972
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -8786,7 +9520,7 @@ __export(tasks_exports, {
|
|
|
8786
9520
|
updateTaskStatus: () => updateTaskStatus,
|
|
8787
9521
|
writeCheckpoint: () => writeCheckpoint
|
|
8788
9522
|
});
|
|
8789
|
-
import
|
|
9523
|
+
import path23 from "path";
|
|
8790
9524
|
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
8791
9525
|
async function createTask(input) {
|
|
8792
9526
|
const result = await createTaskCore(input);
|
|
@@ -8806,8 +9540,8 @@ async function updateTask(input) {
|
|
|
8806
9540
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
8807
9541
|
try {
|
|
8808
9542
|
const agent = String(row.assigned_to);
|
|
8809
|
-
const cacheDir =
|
|
8810
|
-
const cachePath =
|
|
9543
|
+
const cacheDir = path23.join(EXE_AI_DIR, "session-cache");
|
|
9544
|
+
const cachePath = path23.join(cacheDir, `current-task-${agent}.json`);
|
|
8811
9545
|
if (input.status === "in_progress") {
|
|
8812
9546
|
mkdirSync8(cacheDir, { recursive: true });
|
|
8813
9547
|
writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -8978,15 +9712,15 @@ __export(identity_exports, {
|
|
|
8978
9712
|
});
|
|
8979
9713
|
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
8980
9714
|
import { readdirSync as readdirSync7 } from "fs";
|
|
8981
|
-
import
|
|
9715
|
+
import path24 from "path";
|
|
8982
9716
|
import { createHash as createHash2 } from "crypto";
|
|
8983
9717
|
function ensureDir2() {
|
|
8984
|
-
if (!existsSync17(
|
|
8985
|
-
mkdirSync9(
|
|
9718
|
+
if (!existsSync17(IDENTITY_DIR2)) {
|
|
9719
|
+
mkdirSync9(IDENTITY_DIR2, { recursive: true });
|
|
8986
9720
|
}
|
|
8987
9721
|
}
|
|
8988
9722
|
function identityPath(agentId) {
|
|
8989
|
-
return
|
|
9723
|
+
return path24.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
8990
9724
|
}
|
|
8991
9725
|
function parseFrontmatter(raw) {
|
|
8992
9726
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -9059,7 +9793,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
9059
9793
|
}
|
|
9060
9794
|
function listIdentities() {
|
|
9061
9795
|
ensureDir2();
|
|
9062
|
-
const files = readdirSync7(
|
|
9796
|
+
const files = readdirSync7(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
9063
9797
|
const results = [];
|
|
9064
9798
|
for (const file of files) {
|
|
9065
9799
|
const agentId = file.replace(".md", "");
|
|
@@ -9092,13 +9826,13 @@ ${teamLines.join("\n")}`);
|
|
|
9092
9826
|
}
|
|
9093
9827
|
return parts.join("\n\n");
|
|
9094
9828
|
}
|
|
9095
|
-
var
|
|
9829
|
+
var IDENTITY_DIR2;
|
|
9096
9830
|
var init_identity = __esm({
|
|
9097
9831
|
"src/lib/identity.ts"() {
|
|
9098
9832
|
"use strict";
|
|
9099
9833
|
init_config();
|
|
9100
9834
|
init_database();
|
|
9101
|
-
|
|
9835
|
+
IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
|
|
9102
9836
|
}
|
|
9103
9837
|
});
|
|
9104
9838
|
|
|
@@ -10211,8 +10945,8 @@ __export(wiki_client_exports, {
|
|
|
10211
10945
|
listDocuments: () => listDocuments,
|
|
10212
10946
|
listWorkspaces: () => listWorkspaces
|
|
10213
10947
|
});
|
|
10214
|
-
async function wikiFetch(config2,
|
|
10215
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
10948
|
+
async function wikiFetch(config2, path41, method = "GET", body) {
|
|
10949
|
+
const url = `${config2.baseUrl}/api/v1${path41}`;
|
|
10216
10950
|
const headers = {
|
|
10217
10951
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
10218
10952
|
"Content-Type": "application/json"
|
|
@@ -10245,7 +10979,7 @@ async function wikiFetch(config2, path40, method = "GET", body) {
|
|
|
10245
10979
|
}
|
|
10246
10980
|
}
|
|
10247
10981
|
if (!response.ok) {
|
|
10248
|
-
throw new Error(`Wiki API ${method} ${
|
|
10982
|
+
throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
|
|
10249
10983
|
}
|
|
10250
10984
|
return response.json();
|
|
10251
10985
|
} finally {
|
|
@@ -10355,12 +11089,12 @@ __export(worker_gate_exports, {
|
|
|
10355
11089
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
10356
11090
|
});
|
|
10357
11091
|
import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
|
|
10358
|
-
import
|
|
11092
|
+
import path34 from "path";
|
|
10359
11093
|
function tryAcquireWorkerSlot() {
|
|
10360
11094
|
try {
|
|
10361
11095
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
10362
11096
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
10363
|
-
const reservationPath =
|
|
11097
|
+
const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
10364
11098
|
writeFileSync17(reservationPath, String(process.pid));
|
|
10365
11099
|
const files = readdirSync10(WORKER_PID_DIR);
|
|
10366
11100
|
let alive = 0;
|
|
@@ -10378,7 +11112,7 @@ function tryAcquireWorkerSlot() {
|
|
|
10378
11112
|
alive++;
|
|
10379
11113
|
} catch {
|
|
10380
11114
|
try {
|
|
10381
|
-
unlinkSync8(
|
|
11115
|
+
unlinkSync8(path34.join(WORKER_PID_DIR, f));
|
|
10382
11116
|
} catch {
|
|
10383
11117
|
}
|
|
10384
11118
|
}
|
|
@@ -10402,13 +11136,13 @@ function tryAcquireWorkerSlot() {
|
|
|
10402
11136
|
function registerWorkerPid(pid) {
|
|
10403
11137
|
try {
|
|
10404
11138
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
10405
|
-
writeFileSync17(
|
|
11139
|
+
writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
10406
11140
|
} catch {
|
|
10407
11141
|
}
|
|
10408
11142
|
}
|
|
10409
11143
|
function cleanupWorkerPid() {
|
|
10410
11144
|
try {
|
|
10411
|
-
unlinkSync8(
|
|
11145
|
+
unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
10412
11146
|
} catch {
|
|
10413
11147
|
}
|
|
10414
11148
|
}
|
|
@@ -10448,9 +11182,9 @@ var init_worker_gate = __esm({
|
|
|
10448
11182
|
"src/lib/worker-gate.ts"() {
|
|
10449
11183
|
"use strict";
|
|
10450
11184
|
init_config();
|
|
10451
|
-
WORKER_PID_DIR =
|
|
11185
|
+
WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
|
|
10452
11186
|
MAX_CONCURRENT_WORKERS = 3;
|
|
10453
|
-
BACKFILL_LOCK =
|
|
11187
|
+
BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
|
|
10454
11188
|
}
|
|
10455
11189
|
});
|
|
10456
11190
|
|
|
@@ -10474,7 +11208,7 @@ __export(crdt_sync_exports, {
|
|
|
10474
11208
|
});
|
|
10475
11209
|
import * as Y from "yjs";
|
|
10476
11210
|
import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
|
|
10477
|
-
import
|
|
11211
|
+
import path37 from "path";
|
|
10478
11212
|
import { homedir as homedir5 } from "os";
|
|
10479
11213
|
function getStatePath() {
|
|
10480
11214
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -10630,7 +11364,7 @@ function persistState() {
|
|
|
10630
11364
|
if (!doc) return;
|
|
10631
11365
|
try {
|
|
10632
11366
|
const sp = getStatePath();
|
|
10633
|
-
const dir =
|
|
11367
|
+
const dir = path37.dirname(sp);
|
|
10634
11368
|
if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
|
|
10635
11369
|
const state = Y.encodeStateAsUpdate(doc);
|
|
10636
11370
|
writeFileSync18(sp, Buffer.from(state));
|
|
@@ -10674,7 +11408,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
10674
11408
|
var init_crdt_sync = __esm({
|
|
10675
11409
|
"src/lib/crdt-sync.ts"() {
|
|
10676
11410
|
"use strict";
|
|
10677
|
-
DEFAULT_STATE_PATH =
|
|
11411
|
+
DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
|
|
10678
11412
|
_statePathOverride = null;
|
|
10679
11413
|
doc = null;
|
|
10680
11414
|
}
|
|
@@ -10686,9 +11420,9 @@ init_store();
|
|
|
10686
11420
|
init_database();
|
|
10687
11421
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10688
11422
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10689
|
-
import { spawn as
|
|
11423
|
+
import { spawn as spawn4 } from "child_process";
|
|
10690
11424
|
import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
|
|
10691
|
-
import
|
|
11425
|
+
import path40 from "path";
|
|
10692
11426
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10693
11427
|
|
|
10694
11428
|
// src/mcp/tools/recall-my-memory.ts
|
|
@@ -10996,8 +11730,8 @@ init_active_agent();
|
|
|
10996
11730
|
init_plan_limits();
|
|
10997
11731
|
import { z as z4 } from "zod";
|
|
10998
11732
|
import crypto2 from "crypto";
|
|
10999
|
-
import { writeFileSync as
|
|
11000
|
-
import
|
|
11733
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
11734
|
+
import path14 from "path";
|
|
11001
11735
|
function registerStoreMemory(server2) {
|
|
11002
11736
|
server2.registerTool(
|
|
11003
11737
|
"store_memory",
|
|
@@ -11065,8 +11799,8 @@ function registerStoreMemory(server2) {
|
|
|
11065
11799
|
if (needsBackfill) {
|
|
11066
11800
|
try {
|
|
11067
11801
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11068
|
-
const flagPath =
|
|
11069
|
-
|
|
11802
|
+
const flagPath = path14.join(exeDir, "session-cache", "needs-backfill");
|
|
11803
|
+
writeFileSync5(flagPath, "1");
|
|
11070
11804
|
} catch {
|
|
11071
11805
|
}
|
|
11072
11806
|
}
|
|
@@ -11090,8 +11824,8 @@ init_active_agent();
|
|
|
11090
11824
|
init_employees();
|
|
11091
11825
|
import { z as z5 } from "zod";
|
|
11092
11826
|
import crypto3 from "crypto";
|
|
11093
|
-
import { writeFileSync as
|
|
11094
|
-
import
|
|
11827
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
11828
|
+
import path15 from "path";
|
|
11095
11829
|
function registerCommitMemory(server2) {
|
|
11096
11830
|
server2.registerTool(
|
|
11097
11831
|
"commit_to_long_term_memory",
|
|
@@ -11181,8 +11915,8 @@ function registerCommitMemory(server2) {
|
|
|
11181
11915
|
if (needsBackfill) {
|
|
11182
11916
|
try {
|
|
11183
11917
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11184
|
-
const flagPath =
|
|
11185
|
-
|
|
11918
|
+
const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
|
|
11919
|
+
writeFileSync6(flagPath, "1");
|
|
11186
11920
|
} catch {
|
|
11187
11921
|
}
|
|
11188
11922
|
}
|
|
@@ -11448,13 +12182,14 @@ Warning: ${task.warning}`;
|
|
|
11448
12182
|
// src/mcp/tools/list-tasks.ts
|
|
11449
12183
|
init_tasks();
|
|
11450
12184
|
init_project_name();
|
|
12185
|
+
init_active_agent();
|
|
11451
12186
|
import { z as z8 } from "zod";
|
|
11452
12187
|
function registerListTasks(server2) {
|
|
11453
12188
|
server2.registerTool(
|
|
11454
12189
|
"list_tasks",
|
|
11455
12190
|
{
|
|
11456
12191
|
title: "List Tasks",
|
|
11457
|
-
description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all'
|
|
12192
|
+
description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all' for all projects. When querying your own tasks, project filter is skipped automatically.",
|
|
11458
12193
|
inputSchema: {
|
|
11459
12194
|
assigned_to: z8.string().optional().describe("Filter by employee name"),
|
|
11460
12195
|
status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
|
|
@@ -11464,7 +12199,16 @@ function registerListTasks(server2) {
|
|
|
11464
12199
|
},
|
|
11465
12200
|
async ({ assigned_to, status, project_name, priority }) => {
|
|
11466
12201
|
try {
|
|
11467
|
-
|
|
12202
|
+
let resolvedProject;
|
|
12203
|
+
if (project_name === "all") {
|
|
12204
|
+
resolvedProject = void 0;
|
|
12205
|
+
} else if (project_name) {
|
|
12206
|
+
resolvedProject = project_name;
|
|
12207
|
+
} else {
|
|
12208
|
+
const { agentId } = getActiveAgent();
|
|
12209
|
+
const queryingSelf = !assigned_to || assigned_to === agentId;
|
|
12210
|
+
resolvedProject = queryingSelf ? void 0 : getProjectName();
|
|
12211
|
+
}
|
|
11468
12212
|
const tasks = await listTasks({
|
|
11469
12213
|
assignedTo: assigned_to,
|
|
11470
12214
|
status,
|
|
@@ -13219,9 +13963,9 @@ import { z as z29 } from "zod";
|
|
|
13219
13963
|
// src/automation/trigger-engine.ts
|
|
13220
13964
|
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
|
|
13221
13965
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
13222
|
-
import
|
|
13223
|
-
import
|
|
13224
|
-
var TRIGGERS_PATH =
|
|
13966
|
+
import path25 from "path";
|
|
13967
|
+
import os11 from "os";
|
|
13968
|
+
var TRIGGERS_PATH = path25.join(os11.homedir(), ".exe-os", "triggers.json");
|
|
13225
13969
|
function loadTriggers(project) {
|
|
13226
13970
|
if (!existsSync18(TRIGGERS_PATH)) return [];
|
|
13227
13971
|
try {
|
|
@@ -13237,7 +13981,7 @@ function loadTriggers(project) {
|
|
|
13237
13981
|
}
|
|
13238
13982
|
}
|
|
13239
13983
|
function saveTriggers(triggers) {
|
|
13240
|
-
const dir =
|
|
13984
|
+
const dir = path25.dirname(TRIGGERS_PATH);
|
|
13241
13985
|
if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
|
|
13242
13986
|
writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
|
|
13243
13987
|
}
|
|
@@ -13524,23 +14268,23 @@ import { z as z31 } from "zod";
|
|
|
13524
14268
|
|
|
13525
14269
|
// src/automation/starter-packs/index.ts
|
|
13526
14270
|
import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
|
|
13527
|
-
import
|
|
14271
|
+
import path26 from "path";
|
|
13528
14272
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13529
|
-
var __dirname =
|
|
14273
|
+
var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
|
|
13530
14274
|
function listPacks() {
|
|
13531
|
-
const packsDir =
|
|
14275
|
+
const packsDir = path26.join(__dirname, ".");
|
|
13532
14276
|
if (!existsSync19(packsDir)) return [];
|
|
13533
14277
|
return readdirSync8(packsDir, { withFileTypes: true }).filter(
|
|
13534
|
-
(d) => d.isDirectory() && existsSync19(
|
|
14278
|
+
(d) => d.isDirectory() && existsSync19(path26.join(packsDir, d.name, "custom-objects.json"))
|
|
13535
14279
|
).map((d) => d.name);
|
|
13536
14280
|
}
|
|
13537
14281
|
function loadPack(industry) {
|
|
13538
|
-
const packDir =
|
|
13539
|
-
const objectsPath =
|
|
13540
|
-
const triggersPath =
|
|
13541
|
-
const wikiDir =
|
|
13542
|
-
const manifestPath =
|
|
13543
|
-
const identityContextPath =
|
|
14282
|
+
const packDir = path26.join(__dirname, industry);
|
|
14283
|
+
const objectsPath = path26.join(packDir, "custom-objects.json");
|
|
14284
|
+
const triggersPath = path26.join(packDir, "triggers.json");
|
|
14285
|
+
const wikiDir = path26.join(packDir, "wiki-seeds");
|
|
14286
|
+
const manifestPath = path26.join(packDir, "pack.json");
|
|
14287
|
+
const identityContextPath = path26.join(packDir, "identity-context.md");
|
|
13544
14288
|
if (!existsSync19(objectsPath)) return null;
|
|
13545
14289
|
let customObjects = [];
|
|
13546
14290
|
try {
|
|
@@ -13564,7 +14308,7 @@ function loadPack(industry) {
|
|
|
13564
14308
|
if (existsSync19(wikiDir)) {
|
|
13565
14309
|
const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
|
|
13566
14310
|
for (const file of files) {
|
|
13567
|
-
const content = readFileSync17(
|
|
14311
|
+
const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
|
|
13568
14312
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
13569
14313
|
wikiSeeds.push({
|
|
13570
14314
|
filename: file,
|
|
@@ -13642,7 +14386,7 @@ function applyPack(industry, project) {
|
|
|
13642
14386
|
// src/lib/client-coo.ts
|
|
13643
14387
|
init_config();
|
|
13644
14388
|
import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
13645
|
-
import
|
|
14389
|
+
import path27 from "path";
|
|
13646
14390
|
|
|
13647
14391
|
// src/lib/employee-templates.ts
|
|
13648
14392
|
init_global_procedures();
|
|
@@ -13780,10 +14524,10 @@ var ClientCOOClobberError = class extends Error {
|
|
|
13780
14524
|
var COO_ROLE = "Chief Operating Officer";
|
|
13781
14525
|
var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
|
|
13782
14526
|
async function provisionClientCOO(vars, opts = {}) {
|
|
13783
|
-
const identityDir = opts.identityDir ??
|
|
14527
|
+
const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
|
|
13784
14528
|
const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
|
|
13785
14529
|
const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
|
|
13786
|
-
const identityPath2 =
|
|
14530
|
+
const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
|
|
13787
14531
|
if (existsSync20(identityPath2)) {
|
|
13788
14532
|
throw new ClientCOOClobberError(vars.agent_name, identityPath2);
|
|
13789
14533
|
}
|
|
@@ -14681,7 +15425,7 @@ function registerUpdateWikiPage(server2) {
|
|
|
14681
15425
|
import { z as z37 } from "zod";
|
|
14682
15426
|
import { execFile } from "child_process";
|
|
14683
15427
|
import { promisify } from "util";
|
|
14684
|
-
import
|
|
15428
|
+
import path28 from "path";
|
|
14685
15429
|
import { existsSync as existsSync21 } from "fs";
|
|
14686
15430
|
|
|
14687
15431
|
// src/lib/hostinger-api.ts
|
|
@@ -14730,9 +15474,9 @@ var HostingerApiClient = class {
|
|
|
14730
15474
|
}
|
|
14731
15475
|
this.lastRequestTime = Date.now();
|
|
14732
15476
|
}
|
|
14733
|
-
async request(method,
|
|
15477
|
+
async request(method, path41, body) {
|
|
14734
15478
|
await this.rateLimit();
|
|
14735
|
-
const url = `${this.baseUrl}${
|
|
15479
|
+
const url = `${this.baseUrl}${path41}`;
|
|
14736
15480
|
const headers = {
|
|
14737
15481
|
Authorization: `Bearer ${this.apiKey}`,
|
|
14738
15482
|
"Content-Type": "application/json",
|
|
@@ -14805,8 +15549,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
14805
15549
|
}
|
|
14806
15550
|
return envelope.result;
|
|
14807
15551
|
}
|
|
14808
|
-
function buildUrl(zoneId,
|
|
14809
|
-
const normalizedPath =
|
|
15552
|
+
function buildUrl(zoneId, path41 = "/dns_records", query) {
|
|
15553
|
+
const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
|
|
14810
15554
|
const url = new URL(
|
|
14811
15555
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
14812
15556
|
);
|
|
@@ -15019,12 +15763,12 @@ async function waitForReady(client, vpsId) {
|
|
|
15019
15763
|
}
|
|
15020
15764
|
async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
15021
15765
|
const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
15022
|
-
const playbookDir =
|
|
15023
|
-
const playbookPath =
|
|
15024
|
-
const inventoryPath =
|
|
15025
|
-
const clientVarsPath =
|
|
15026
|
-
const varsDir =
|
|
15027
|
-
if (!
|
|
15766
|
+
const playbookDir = path28.resolve(process.cwd(), "infrastructure", "ansible");
|
|
15767
|
+
const playbookPath = path28.join(playbookDir, "deploy.yml");
|
|
15768
|
+
const inventoryPath = path28.join(playbookDir, "inventory", "hosts.yml");
|
|
15769
|
+
const clientVarsPath = path28.join(playbookDir, "vars", `${safeClientName}.yml`);
|
|
15770
|
+
const varsDir = path28.join(playbookDir, "vars");
|
|
15771
|
+
if (!path28.resolve(clientVarsPath).startsWith(path28.resolve(varsDir))) {
|
|
15028
15772
|
throw new Error(`Invalid client name for vars path: ${clientName}`);
|
|
15029
15773
|
}
|
|
15030
15774
|
const args = [
|
|
@@ -15114,7 +15858,7 @@ function buildInventoryRecord(params) {
|
|
|
15114
15858
|
// src/mcp/tools/export-orchestration.ts
|
|
15115
15859
|
init_active_agent();
|
|
15116
15860
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
|
|
15117
|
-
import
|
|
15861
|
+
import path30 from "path";
|
|
15118
15862
|
import { z as z38 } from "zod";
|
|
15119
15863
|
|
|
15120
15864
|
// src/lib/orchestration-package.ts
|
|
@@ -15123,8 +15867,8 @@ init_identity();
|
|
|
15123
15867
|
init_platform_procedures();
|
|
15124
15868
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15125
15869
|
import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
|
|
15126
|
-
import
|
|
15127
|
-
import
|
|
15870
|
+
import os12 from "os";
|
|
15871
|
+
import path29 from "path";
|
|
15128
15872
|
var PACKAGE_VERSION = "1.0";
|
|
15129
15873
|
var ROSTER_FILENAME = "exe-employees.json";
|
|
15130
15874
|
var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
|
|
@@ -15190,10 +15934,10 @@ function validateProcedureEntry(value, index) {
|
|
|
15190
15934
|
};
|
|
15191
15935
|
}
|
|
15192
15936
|
function getRosterPath() {
|
|
15193
|
-
return
|
|
15937
|
+
return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
|
|
15194
15938
|
}
|
|
15195
15939
|
function getBackupPath() {
|
|
15196
|
-
return
|
|
15940
|
+
return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
|
|
15197
15941
|
}
|
|
15198
15942
|
function readRosterFile() {
|
|
15199
15943
|
const rosterPath = getRosterPath();
|
|
@@ -15210,7 +15954,7 @@ function writeRosterFile(roster) {
|
|
|
15210
15954
|
throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
|
|
15211
15955
|
}
|
|
15212
15956
|
const rosterPath = getRosterPath();
|
|
15213
|
-
mkdirSync12(
|
|
15957
|
+
mkdirSync12(path29.dirname(rosterPath), { recursive: true });
|
|
15214
15958
|
if (existsSync22(rosterPath)) {
|
|
15215
15959
|
const currentRoster = readRosterFile();
|
|
15216
15960
|
if (roster.length < currentRoster.length) {
|
|
@@ -15482,7 +16226,7 @@ function registerExportOrchestration(server2) {
|
|
|
15482
16226
|
try {
|
|
15483
16227
|
await initStore();
|
|
15484
16228
|
const pkg = await exportOrchestration(getActiveAgent().agentId);
|
|
15485
|
-
mkdirSync13(
|
|
16229
|
+
mkdirSync13(path30.dirname(output_path), { recursive: true });
|
|
15486
16230
|
writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
|
|
15487
16231
|
`, "utf-8");
|
|
15488
16232
|
return {
|
|
@@ -15683,17 +16427,17 @@ function registerQueryConversations(server2) {
|
|
|
15683
16427
|
// src/mcp/tools/load-skill.ts
|
|
15684
16428
|
import { z as z41 } from "zod";
|
|
15685
16429
|
import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
|
|
15686
|
-
import
|
|
16430
|
+
import path31 from "path";
|
|
15687
16431
|
import { homedir as homedir2 } from "os";
|
|
15688
|
-
var SKILLS_DIR =
|
|
16432
|
+
var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
|
|
15689
16433
|
function listAvailableSkills() {
|
|
15690
16434
|
try {
|
|
15691
16435
|
const entries = readdirSync9(SKILLS_DIR);
|
|
15692
16436
|
return entries.filter((entry) => {
|
|
15693
16437
|
try {
|
|
15694
|
-
const entryPath =
|
|
16438
|
+
const entryPath = path31.join(SKILLS_DIR, entry);
|
|
15695
16439
|
if (!statSync3(entryPath).isDirectory()) return false;
|
|
15696
|
-
const skillFile =
|
|
16440
|
+
const skillFile = path31.join(entryPath, "SKILL.md");
|
|
15697
16441
|
statSync3(skillFile);
|
|
15698
16442
|
return true;
|
|
15699
16443
|
} catch {
|
|
@@ -15736,8 +16480,8 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
15736
16480
|
}]
|
|
15737
16481
|
};
|
|
15738
16482
|
}
|
|
15739
|
-
const sanitized =
|
|
15740
|
-
const skillFile =
|
|
16483
|
+
const sanitized = path31.basename(skill_name);
|
|
16484
|
+
const skillFile = path31.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
15741
16485
|
try {
|
|
15742
16486
|
const content = readFileSync20(skillFile, "utf-8");
|
|
15743
16487
|
return {
|
|
@@ -16378,8 +17122,8 @@ init_database();
|
|
|
16378
17122
|
import { readdir } from "fs/promises";
|
|
16379
17123
|
import { createReadStream } from "fs";
|
|
16380
17124
|
import { createInterface } from "readline";
|
|
16381
|
-
import
|
|
16382
|
-
import
|
|
17125
|
+
import path32 from "path";
|
|
17126
|
+
import os13 from "os";
|
|
16383
17127
|
var MODEL_PRICING = {
|
|
16384
17128
|
// Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
|
|
16385
17129
|
"claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
|
|
@@ -16427,18 +17171,18 @@ async function getAgentSpend(period = "7d") {
|
|
|
16427
17171
|
for (const row of result.rows) {
|
|
16428
17172
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
16429
17173
|
}
|
|
16430
|
-
const claudeDir =
|
|
17174
|
+
const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
|
|
16431
17175
|
let projectDirs = [];
|
|
16432
17176
|
try {
|
|
16433
17177
|
const entries = await readdir(claudeDir);
|
|
16434
|
-
projectDirs = entries.map((e) =>
|
|
17178
|
+
projectDirs = entries.map((e) => path32.join(claudeDir, e));
|
|
16435
17179
|
} catch {
|
|
16436
17180
|
return [];
|
|
16437
17181
|
}
|
|
16438
17182
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
16439
17183
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
16440
17184
|
for (const dir of projectDirs) {
|
|
16441
|
-
const jsonlPath =
|
|
17185
|
+
const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
|
|
16442
17186
|
try {
|
|
16443
17187
|
const usage = await extractSessionUsage(jsonlPath);
|
|
16444
17188
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -17016,12 +17760,12 @@ function registerExportGraph(server2) {
|
|
|
17016
17760
|
}
|
|
17017
17761
|
const html = await exportGraphHTML(client);
|
|
17018
17762
|
const fs = await import("fs");
|
|
17019
|
-
const
|
|
17020
|
-
const
|
|
17021
|
-
const outDir =
|
|
17763
|
+
const path41 = await import("path");
|
|
17764
|
+
const os15 = await import("os");
|
|
17765
|
+
const outDir = path41.join(os15.homedir(), ".exe-os", "exports");
|
|
17022
17766
|
fs.mkdirSync(outDir, { recursive: true });
|
|
17023
17767
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
17024
|
-
const filePath =
|
|
17768
|
+
const filePath = path41.join(outDir, `graph-${timestamp}.html`);
|
|
17025
17769
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
17026
17770
|
return {
|
|
17027
17771
|
content: [
|
|
@@ -17243,9 +17987,9 @@ function registerListAgentSessions(server2) {
|
|
|
17243
17987
|
// src/mcp/tools/get-daemon-health.ts
|
|
17244
17988
|
import { z as z56 } from "zod";
|
|
17245
17989
|
import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
|
|
17246
|
-
import
|
|
17990
|
+
import path33 from "path";
|
|
17247
17991
|
import { homedir as homedir3 } from "os";
|
|
17248
|
-
var PID_PATH2 =
|
|
17992
|
+
var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
|
|
17249
17993
|
function formatUptime(seconds) {
|
|
17250
17994
|
const h = Math.floor(seconds / 3600);
|
|
17251
17995
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -17435,8 +18179,8 @@ init_worker_gate();
|
|
|
17435
18179
|
init_config();
|
|
17436
18180
|
import { z as z58 } from "zod";
|
|
17437
18181
|
import { readdirSync as readdirSync11, existsSync as existsSync26 } from "fs";
|
|
17438
|
-
import
|
|
17439
|
-
var WORKER_PID_DIR2 =
|
|
18182
|
+
import path35 from "path";
|
|
18183
|
+
var WORKER_PID_DIR2 = path35.join(EXE_AI_DIR, "worker-pids");
|
|
17440
18184
|
function countAliveWorkers() {
|
|
17441
18185
|
let alive = 0;
|
|
17442
18186
|
let stale = 0;
|
|
@@ -17512,7 +18256,7 @@ import { z as z59 } from "zod";
|
|
|
17512
18256
|
// src/bin/exe-doctor.ts
|
|
17513
18257
|
init_store();
|
|
17514
18258
|
init_database();
|
|
17515
|
-
import
|
|
18259
|
+
import os14 from "os";
|
|
17516
18260
|
|
|
17517
18261
|
// src/lib/is-main.ts
|
|
17518
18262
|
import { realpathSync } from "fs";
|
|
@@ -17532,7 +18276,7 @@ function isMainModule(importMetaUrl) {
|
|
|
17532
18276
|
// src/bin/exe-doctor.ts
|
|
17533
18277
|
import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
|
|
17534
18278
|
import { spawn as spawn2 } from "child_process";
|
|
17535
|
-
import
|
|
18279
|
+
import path36 from "path";
|
|
17536
18280
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
17537
18281
|
|
|
17538
18282
|
// src/lib/conflict-detector.ts
|
|
@@ -17935,7 +18679,7 @@ async function auditOrphanedProjects(client) {
|
|
|
17935
18679
|
for (const row of result.rows) {
|
|
17936
18680
|
const name = row.project_name;
|
|
17937
18681
|
const count = Number(row.cnt);
|
|
17938
|
-
const exists = existsSync27(
|
|
18682
|
+
const exists = existsSync27(path36.join(home, name)) || existsSync27(path36.join(home, "..", name)) || existsSync27(path36.join(process.cwd(), "..", name));
|
|
17939
18683
|
if (!exists) {
|
|
17940
18684
|
orphans.push({ project_name: name, count });
|
|
17941
18685
|
}
|
|
@@ -17943,7 +18687,7 @@ async function auditOrphanedProjects(client) {
|
|
|
17943
18687
|
return orphans;
|
|
17944
18688
|
}
|
|
17945
18689
|
function auditHookHealth() {
|
|
17946
|
-
const logPath =
|
|
18690
|
+
const logPath = path36.join(
|
|
17947
18691
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
17948
18692
|
".exe-os",
|
|
17949
18693
|
"logs",
|
|
@@ -18029,7 +18773,7 @@ function formatReport(report, flags) {
|
|
|
18029
18773
|
}
|
|
18030
18774
|
lines.push("");
|
|
18031
18775
|
}
|
|
18032
|
-
const totalMemGB =
|
|
18776
|
+
const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
|
|
18033
18777
|
const isLowMemSystem = totalMemGB <= 8;
|
|
18034
18778
|
if (isLowMemSystem && report.nullVectors > 0) {
|
|
18035
18779
|
lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
|
|
@@ -18151,7 +18895,7 @@ async function fixNullVectors() {
|
|
|
18151
18895
|
}
|
|
18152
18896
|
}
|
|
18153
18897
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
18154
|
-
const backfillPath =
|
|
18898
|
+
const backfillPath = path36.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
18155
18899
|
return new Promise((resolve, reject) => {
|
|
18156
18900
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
18157
18901
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -18351,7 +19095,7 @@ import { z as z60 } from "zod";
|
|
|
18351
19095
|
init_database();
|
|
18352
19096
|
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync29, readdirSync as readdirSync12, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
18353
19097
|
import crypto16 from "crypto";
|
|
18354
|
-
import
|
|
19098
|
+
import path38 from "path";
|
|
18355
19099
|
import { homedir as homedir6 } from "os";
|
|
18356
19100
|
|
|
18357
19101
|
// src/lib/crypto.ts
|
|
@@ -18425,7 +19169,7 @@ function sqlSafe(v) {
|
|
|
18425
19169
|
}
|
|
18426
19170
|
function logError(msg) {
|
|
18427
19171
|
try {
|
|
18428
|
-
const logPath =
|
|
19172
|
+
const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
|
|
18429
19173
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
18430
19174
|
`);
|
|
18431
19175
|
} catch {
|
|
@@ -18434,7 +19178,7 @@ function logError(msg) {
|
|
|
18434
19178
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
18435
19179
|
var FETCH_TIMEOUT_MS4 = 3e4;
|
|
18436
19180
|
var PUSH_BATCH_SIZE = 5e3;
|
|
18437
|
-
var ROSTER_LOCK_PATH =
|
|
19181
|
+
var ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
|
|
18438
19182
|
var LOCK_STALE_MS = 3e4;
|
|
18439
19183
|
async function withRosterLock(fn) {
|
|
18440
19184
|
try {
|
|
@@ -18825,7 +19569,7 @@ async function cloudSync(config2) {
|
|
|
18825
19569
|
try {
|
|
18826
19570
|
const employees = await loadEmployees();
|
|
18827
19571
|
rosterResult.employees = employees.length;
|
|
18828
|
-
const idDir =
|
|
19572
|
+
const idDir = path38.join(EXE_AI_DIR, "identity");
|
|
18829
19573
|
if (existsSync29(idDir)) {
|
|
18830
19574
|
rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
|
|
18831
19575
|
}
|
|
@@ -18844,7 +19588,7 @@ async function cloudSync(config2) {
|
|
|
18844
19588
|
roster: rosterResult
|
|
18845
19589
|
};
|
|
18846
19590
|
}
|
|
18847
|
-
var ROSTER_DELETIONS_PATH =
|
|
19591
|
+
var ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
|
|
18848
19592
|
function consumeRosterDeletions() {
|
|
18849
19593
|
try {
|
|
18850
19594
|
if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
|
|
@@ -18856,9 +19600,9 @@ function consumeRosterDeletions() {
|
|
|
18856
19600
|
}
|
|
18857
19601
|
}
|
|
18858
19602
|
function buildRosterBlob(paths) {
|
|
18859
|
-
const rosterPath = paths?.rosterPath ??
|
|
18860
|
-
const identityDir = paths?.identityDir ??
|
|
18861
|
-
const configPath = paths?.configPath ??
|
|
19603
|
+
const rosterPath = paths?.rosterPath ?? path38.join(EXE_AI_DIR, "exe-employees.json");
|
|
19604
|
+
const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
|
|
19605
|
+
const configPath = paths?.configPath ?? path38.join(EXE_AI_DIR, "config.json");
|
|
18862
19606
|
let roster = [];
|
|
18863
19607
|
if (existsSync29(rosterPath)) {
|
|
18864
19608
|
try {
|
|
@@ -18870,7 +19614,7 @@ function buildRosterBlob(paths) {
|
|
|
18870
19614
|
if (existsSync29(identityDir)) {
|
|
18871
19615
|
for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
18872
19616
|
try {
|
|
18873
|
-
identities[file] = readFileSync25(
|
|
19617
|
+
identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
|
|
18874
19618
|
} catch {
|
|
18875
19619
|
}
|
|
18876
19620
|
}
|
|
@@ -18883,7 +19627,7 @@ function buildRosterBlob(paths) {
|
|
|
18883
19627
|
}
|
|
18884
19628
|
}
|
|
18885
19629
|
let agentConfig;
|
|
18886
|
-
const agentConfigPath =
|
|
19630
|
+
const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
|
|
18887
19631
|
if (existsSync29(agentConfigPath)) {
|
|
18888
19632
|
try {
|
|
18889
19633
|
agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
|
|
@@ -18962,7 +19706,7 @@ async function cloudPullRoster(config2) {
|
|
|
18962
19706
|
}
|
|
18963
19707
|
}
|
|
18964
19708
|
function mergeConfig(remoteConfig, configPath) {
|
|
18965
|
-
const cfgPath = configPath ??
|
|
19709
|
+
const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
|
|
18966
19710
|
let local = {};
|
|
18967
19711
|
if (existsSync29(cfgPath)) {
|
|
18968
19712
|
try {
|
|
@@ -18971,14 +19715,14 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
18971
19715
|
}
|
|
18972
19716
|
}
|
|
18973
19717
|
const merged = { ...remoteConfig, ...local };
|
|
18974
|
-
const dir =
|
|
19718
|
+
const dir = path38.dirname(cfgPath);
|
|
18975
19719
|
if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
|
|
18976
19720
|
writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
18977
19721
|
}
|
|
18978
19722
|
async function mergeRosterFromRemote(remote, paths) {
|
|
18979
19723
|
return withRosterLock(async () => {
|
|
18980
19724
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
18981
|
-
const identityDir = paths?.identityDir ??
|
|
19725
|
+
const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
|
|
18982
19726
|
const localEmployees = await loadEmployees(rosterPath);
|
|
18983
19727
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
18984
19728
|
let added = 0;
|
|
@@ -19000,7 +19744,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19000
19744
|
const remoteIdentity = remote.identities[matchedKey];
|
|
19001
19745
|
if (remoteIdentity) {
|
|
19002
19746
|
if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
|
|
19003
|
-
const idPath =
|
|
19747
|
+
const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
|
|
19004
19748
|
let localIdentity = null;
|
|
19005
19749
|
try {
|
|
19006
19750
|
localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
|
|
@@ -19033,7 +19777,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19033
19777
|
}
|
|
19034
19778
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
19035
19779
|
try {
|
|
19036
|
-
const agentConfigPath =
|
|
19780
|
+
const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
|
|
19037
19781
|
let local = {};
|
|
19038
19782
|
if (existsSync29(agentConfigPath)) {
|
|
19039
19783
|
try {
|
|
@@ -19762,15 +20506,515 @@ function registerGetLicenseStatus(server2) {
|
|
|
19762
20506
|
);
|
|
19763
20507
|
}
|
|
19764
20508
|
|
|
19765
|
-
// src/mcp/tools/
|
|
20509
|
+
// src/mcp/tools/backup-vps.ts
|
|
20510
|
+
init_keychain();
|
|
19766
20511
|
import { z as z64 } from "zod";
|
|
19767
20512
|
|
|
20513
|
+
// src/lib/vps-backup.ts
|
|
20514
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
20515
|
+
import { Readable } from "stream";
|
|
20516
|
+
import { spawn as spawn3 } from "child_process";
|
|
20517
|
+
import {
|
|
20518
|
+
DeleteObjectsCommand,
|
|
20519
|
+
GetObjectCommand,
|
|
20520
|
+
ListObjectsV2Command,
|
|
20521
|
+
PutObjectCommand,
|
|
20522
|
+
S3Client
|
|
20523
|
+
} from "@aws-sdk/client-s3";
|
|
20524
|
+
import {
|
|
20525
|
+
constants as brotliConstants,
|
|
20526
|
+
brotliCompressSync as brotliCompressSync2,
|
|
20527
|
+
brotliDecompressSync as brotliDecompressSync2
|
|
20528
|
+
} from "zlib";
|
|
20529
|
+
var ALGORITHM2 = "aes-256-gcm";
|
|
20530
|
+
var IV_BYTES = 12;
|
|
20531
|
+
var TAG_BYTES = 16;
|
|
20532
|
+
var CLIENT_ID_FALLBACK = "default";
|
|
20533
|
+
var BACKUP_EXT = ".pgdump.br.enc";
|
|
20534
|
+
var BACKUP_PREFIX = "backups/";
|
|
20535
|
+
function makeS3Client(opts) {
|
|
20536
|
+
return new S3Client({
|
|
20537
|
+
region: "auto",
|
|
20538
|
+
endpoint: opts.r2Endpoint,
|
|
20539
|
+
credentials: {
|
|
20540
|
+
accessKeyId: opts.r2AccessKeyId,
|
|
20541
|
+
secretAccessKey: opts.r2SecretAccessKey
|
|
20542
|
+
}
|
|
20543
|
+
});
|
|
20544
|
+
}
|
|
20545
|
+
function parsePostgresClientId(databaseUrl) {
|
|
20546
|
+
try {
|
|
20547
|
+
const parsed = new URL(databaseUrl);
|
|
20548
|
+
const candidate = parsed.pathname.split("/").filter(Boolean).pop() ?? CLIENT_ID_FALLBACK;
|
|
20549
|
+
const name = decodeURIComponent(candidate).trim() || CLIENT_ID_FALLBACK;
|
|
20550
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
20551
|
+
} catch {
|
|
20552
|
+
return CLIENT_ID_FALLBACK;
|
|
20553
|
+
}
|
|
20554
|
+
}
|
|
20555
|
+
function makeBackupTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
20556
|
+
return date.toISOString().replace(/[:.]/g, "-");
|
|
20557
|
+
}
|
|
20558
|
+
function makeBackupKey(databaseUrl, date = /* @__PURE__ */ new Date()) {
|
|
20559
|
+
const clientId = parsePostgresClientId(databaseUrl);
|
|
20560
|
+
return `${BACKUP_PREFIX}${clientId}/${makeBackupTimestamp(date)}${BACKUP_EXT}`;
|
|
20561
|
+
}
|
|
20562
|
+
function validateEncryptionKey(encryptionKey) {
|
|
20563
|
+
if (encryptionKey.length !== 32) {
|
|
20564
|
+
throw new Error(`Encryption key must be 32 bytes for AES-256, got ${encryptionKey.length}`);
|
|
20565
|
+
}
|
|
20566
|
+
}
|
|
20567
|
+
function encryptBlob(input, encryptionKey) {
|
|
20568
|
+
validateEncryptionKey(encryptionKey);
|
|
20569
|
+
const iv = randomBytes(IV_BYTES);
|
|
20570
|
+
const cipher = createCipheriv(ALGORITHM2, encryptionKey, iv);
|
|
20571
|
+
const ciphertext = Buffer.concat([cipher.update(input), cipher.final()]);
|
|
20572
|
+
const tag = cipher.getAuthTag();
|
|
20573
|
+
return Buffer.concat([iv, tag, ciphertext]);
|
|
20574
|
+
}
|
|
20575
|
+
function decryptBlob(input, encryptionKey) {
|
|
20576
|
+
validateEncryptionKey(encryptionKey);
|
|
20577
|
+
if (input.length < IV_BYTES + TAG_BYTES) {
|
|
20578
|
+
throw new Error("Encrypted backup blob is too short");
|
|
20579
|
+
}
|
|
20580
|
+
const iv = input.subarray(0, IV_BYTES);
|
|
20581
|
+
const tag = input.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
|
|
20582
|
+
const ciphertext = input.subarray(IV_BYTES + TAG_BYTES);
|
|
20583
|
+
const decipher = createDecipheriv(ALGORITHM2, encryptionKey, iv);
|
|
20584
|
+
decipher.setAuthTag(tag);
|
|
20585
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
20586
|
+
}
|
|
20587
|
+
function compress2(input) {
|
|
20588
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
20589
|
+
return brotliCompressSync2(input, {
|
|
20590
|
+
params: {
|
|
20591
|
+
[brotliConstants.BROTLI_PARAM_QUALITY]: 4
|
|
20592
|
+
}
|
|
20593
|
+
});
|
|
20594
|
+
}
|
|
20595
|
+
function decompress2(input) {
|
|
20596
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
20597
|
+
return brotliDecompressSync2(input);
|
|
20598
|
+
}
|
|
20599
|
+
async function runProcess(command, args, input) {
|
|
20600
|
+
return new Promise((resolve, reject) => {
|
|
20601
|
+
const child = spawn3(command, args, {
|
|
20602
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
20603
|
+
});
|
|
20604
|
+
const stdoutChunks = [];
|
|
20605
|
+
const stderrChunks = [];
|
|
20606
|
+
child.stdout.on("data", (chunk) => {
|
|
20607
|
+
stdoutChunks.push(chunk);
|
|
20608
|
+
});
|
|
20609
|
+
child.stderr.on("data", (chunk) => {
|
|
20610
|
+
stderrChunks.push(chunk);
|
|
20611
|
+
});
|
|
20612
|
+
child.on("error", (error) => {
|
|
20613
|
+
reject(error);
|
|
20614
|
+
});
|
|
20615
|
+
child.on("close", (code) => {
|
|
20616
|
+
if (code !== 0) {
|
|
20617
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
|
|
20618
|
+
reject(new Error(`Command failed: ${command} ${args.join(" ")} (code ${code})${stderr ? `: ${stderr}` : ""}`));
|
|
20619
|
+
return;
|
|
20620
|
+
}
|
|
20621
|
+
resolve(Buffer.concat(stdoutChunks));
|
|
20622
|
+
});
|
|
20623
|
+
if (input) {
|
|
20624
|
+
child.stdin.write(input);
|
|
20625
|
+
child.stdin.end();
|
|
20626
|
+
}
|
|
20627
|
+
});
|
|
20628
|
+
}
|
|
20629
|
+
async function listBackupObjects(client, bucket, prefix) {
|
|
20630
|
+
const objects = [];
|
|
20631
|
+
let token;
|
|
20632
|
+
do {
|
|
20633
|
+
const command = new ListObjectsV2Command({
|
|
20634
|
+
Bucket: bucket,
|
|
20635
|
+
Prefix: prefix,
|
|
20636
|
+
ContinuationToken: token
|
|
20637
|
+
});
|
|
20638
|
+
const response = await client.send(command);
|
|
20639
|
+
const items = response.Contents ?? [];
|
|
20640
|
+
for (const obj of items) {
|
|
20641
|
+
if (!obj.Key || !obj.LastModified) continue;
|
|
20642
|
+
objects.push({
|
|
20643
|
+
key: obj.Key,
|
|
20644
|
+
size: typeof obj.Size === "number" ? obj.Size : Number(obj.Size ?? 0),
|
|
20645
|
+
lastModified: obj.LastModified
|
|
20646
|
+
});
|
|
20647
|
+
}
|
|
20648
|
+
token = response.IsTruncated ? response.NextContinuationToken : void 0;
|
|
20649
|
+
} while (token);
|
|
20650
|
+
return objects;
|
|
20651
|
+
}
|
|
20652
|
+
function selectRetentionKeep(keys) {
|
|
20653
|
+
if (keys.length <= 11) {
|
|
20654
|
+
return new Set(keys.map((k) => k.key));
|
|
20655
|
+
}
|
|
20656
|
+
const newest = [...keys].sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
20657
|
+
const keep = /* @__PURE__ */ new Set();
|
|
20658
|
+
const seenDays = /* @__PURE__ */ new Set();
|
|
20659
|
+
const seenWeeks = /* @__PURE__ */ new Set();
|
|
20660
|
+
for (const backup of newest) {
|
|
20661
|
+
if (keep.size >= 11) break;
|
|
20662
|
+
const day = backup.lastModified.toISOString().slice(0, 10);
|
|
20663
|
+
if (seenDays.size < 7 && !seenDays.has(day)) {
|
|
20664
|
+
seenDays.add(day);
|
|
20665
|
+
keep.add(backup.key);
|
|
20666
|
+
}
|
|
20667
|
+
}
|
|
20668
|
+
const dayCount = seenDays.size;
|
|
20669
|
+
if (dayCount >= 7) {
|
|
20670
|
+
for (const backup of newest) {
|
|
20671
|
+
if (keep.has(backup.key) || keep.size >= 11) {
|
|
20672
|
+
continue;
|
|
20673
|
+
}
|
|
20674
|
+
const week = getWeekBucket(backup.lastModified);
|
|
20675
|
+
if (seenWeeks.size < 4 && !seenWeeks.has(week)) {
|
|
20676
|
+
seenWeeks.add(week);
|
|
20677
|
+
keep.add(backup.key);
|
|
20678
|
+
}
|
|
20679
|
+
if (seenWeeks.size >= 4) {
|
|
20680
|
+
break;
|
|
20681
|
+
}
|
|
20682
|
+
}
|
|
20683
|
+
}
|
|
20684
|
+
return keep;
|
|
20685
|
+
}
|
|
20686
|
+
function getWeekBucket(date) {
|
|
20687
|
+
const start = Date.UTC(date.getUTCFullYear(), 0, 0);
|
|
20688
|
+
const dayOfYear = Math.floor((Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - start) / 864e5);
|
|
20689
|
+
const week = Math.floor(dayOfYear / 7) + 1;
|
|
20690
|
+
return `${date.getUTCFullYear()}-W${String(week).padStart(2, "0")}`;
|
|
20691
|
+
}
|
|
20692
|
+
async function enforceRetentionPolicy(opts, clientId, allObjects) {
|
|
20693
|
+
const s3 = makeS3Client(opts);
|
|
20694
|
+
const prefix = `${BACKUP_PREFIX}${clientId}/`;
|
|
20695
|
+
const relevant = allObjects.filter((o) => o.key.startsWith(prefix));
|
|
20696
|
+
const keep = selectRetentionKeep(relevant);
|
|
20697
|
+
if (keep.size >= relevant.length) {
|
|
20698
|
+
return;
|
|
20699
|
+
}
|
|
20700
|
+
const toDelete = relevant.filter((obj) => !keep.has(obj.key)).map((obj) => ({ Key: obj.key })).filter((entry) => Boolean(entry.Key));
|
|
20701
|
+
const deleteBatches = chunkArray(toDelete, 1e3);
|
|
20702
|
+
for (const batch of deleteBatches) {
|
|
20703
|
+
await s3.send(
|
|
20704
|
+
new DeleteObjectsCommand({
|
|
20705
|
+
Bucket: opts.r2Bucket,
|
|
20706
|
+
Delete: { Objects: batch }
|
|
20707
|
+
})
|
|
20708
|
+
);
|
|
20709
|
+
}
|
|
20710
|
+
}
|
|
20711
|
+
function chunkArray(items, size) {
|
|
20712
|
+
const chunks = [];
|
|
20713
|
+
for (let i = 0; i < items.length; i += size) {
|
|
20714
|
+
chunks.push(items.slice(i, i + size));
|
|
20715
|
+
}
|
|
20716
|
+
return chunks;
|
|
20717
|
+
}
|
|
20718
|
+
async function readStreamFully(input) {
|
|
20719
|
+
const stream = normalizeS3Body(input);
|
|
20720
|
+
const chunks = [];
|
|
20721
|
+
return new Promise((resolve, reject) => {
|
|
20722
|
+
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
20723
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
20724
|
+
stream.on("error", (error) => reject(error));
|
|
20725
|
+
});
|
|
20726
|
+
}
|
|
20727
|
+
function normalizeS3Body(body) {
|
|
20728
|
+
if (body instanceof Readable) return body;
|
|
20729
|
+
if (body && typeof body === "object" && "getReader" in body) {
|
|
20730
|
+
const reader = body.getReader();
|
|
20731
|
+
const readable = new Readable({ read() {
|
|
20732
|
+
} });
|
|
20733
|
+
(async () => {
|
|
20734
|
+
try {
|
|
20735
|
+
while (true) {
|
|
20736
|
+
const { done, value } = await reader.read();
|
|
20737
|
+
if (done) {
|
|
20738
|
+
readable.push(null);
|
|
20739
|
+
break;
|
|
20740
|
+
}
|
|
20741
|
+
readable.push(Buffer.from(value));
|
|
20742
|
+
}
|
|
20743
|
+
} catch (error) {
|
|
20744
|
+
readable.destroy(error);
|
|
20745
|
+
}
|
|
20746
|
+
})();
|
|
20747
|
+
return readable;
|
|
20748
|
+
}
|
|
20749
|
+
throw new Error("Unsupported S3 object body type");
|
|
20750
|
+
}
|
|
20751
|
+
async function createBackup(opts) {
|
|
20752
|
+
validateEncryptionKey(opts.encryptionKey);
|
|
20753
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
20754
|
+
const key = makeBackupKey(opts.databaseUrl, new Date(timestamp));
|
|
20755
|
+
const client = makeS3Client(opts);
|
|
20756
|
+
const dump = await runProcess("pg_dump", ["--format=custom", opts.databaseUrl]);
|
|
20757
|
+
const compressed = compress2(dump);
|
|
20758
|
+
const encrypted = encryptBlob(compressed, opts.encryptionKey);
|
|
20759
|
+
await client.send(
|
|
20760
|
+
new PutObjectCommand({
|
|
20761
|
+
Bucket: opts.r2Bucket,
|
|
20762
|
+
Key: key,
|
|
20763
|
+
Body: Readable.from(encrypted),
|
|
20764
|
+
ContentType: "application/octet-stream",
|
|
20765
|
+
ContentLength: encrypted.length
|
|
20766
|
+
})
|
|
20767
|
+
);
|
|
20768
|
+
const all = await listBackupObjects(client, opts.r2Bucket, BACKUP_PREFIX);
|
|
20769
|
+
const clientId = parsePostgresClientId(opts.databaseUrl);
|
|
20770
|
+
await enforceRetentionPolicy(
|
|
20771
|
+
{
|
|
20772
|
+
r2Bucket: opts.r2Bucket,
|
|
20773
|
+
r2Endpoint: opts.r2Endpoint,
|
|
20774
|
+
r2AccessKeyId: opts.r2AccessKeyId,
|
|
20775
|
+
r2SecretAccessKey: opts.r2SecretAccessKey
|
|
20776
|
+
},
|
|
20777
|
+
clientId,
|
|
20778
|
+
all
|
|
20779
|
+
);
|
|
20780
|
+
return {
|
|
20781
|
+
key,
|
|
20782
|
+
sizeBytes: encrypted.length,
|
|
20783
|
+
timestamp
|
|
20784
|
+
};
|
|
20785
|
+
}
|
|
20786
|
+
async function restoreBackup(opts) {
|
|
20787
|
+
validateEncryptionKey(opts.encryptionKey);
|
|
20788
|
+
const client = makeS3Client(opts);
|
|
20789
|
+
const response = await client.send(
|
|
20790
|
+
new GetObjectCommand({
|
|
20791
|
+
Bucket: opts.r2Bucket,
|
|
20792
|
+
Key: opts.backupKey
|
|
20793
|
+
})
|
|
20794
|
+
);
|
|
20795
|
+
if (!response.Body) {
|
|
20796
|
+
throw new Error(`Backup not found in R2: ${opts.backupKey}`);
|
|
20797
|
+
}
|
|
20798
|
+
const encrypted = await readStreamFully(response.Body);
|
|
20799
|
+
const decompressed = decompress2(decryptBlob(encrypted, opts.encryptionKey));
|
|
20800
|
+
await runProcess("pg_restore", ["--clean", "--if-exists", "-d", opts.databaseUrl], decompressed);
|
|
20801
|
+
}
|
|
20802
|
+
async function listBackups(opts) {
|
|
20803
|
+
const s3 = makeS3Client(opts);
|
|
20804
|
+
const objects = await listBackupObjects(s3, opts.r2Bucket, BACKUP_PREFIX);
|
|
20805
|
+
return objects.filter((obj) => obj.key.endsWith(BACKUP_EXT)).map((obj) => ({
|
|
20806
|
+
key: obj.key,
|
|
20807
|
+
size: obj.size,
|
|
20808
|
+
lastModified: obj.lastModified.toISOString()
|
|
20809
|
+
})).sort((a, b) => b.lastModified.localeCompare(a.lastModified));
|
|
20810
|
+
}
|
|
20811
|
+
async function backupHealth(opts) {
|
|
20812
|
+
const backups = await listBackups(opts);
|
|
20813
|
+
if (backups.length === 0) {
|
|
20814
|
+
return {
|
|
20815
|
+
lastBackup: null,
|
|
20816
|
+
backupCount: 0,
|
|
20817
|
+
totalSizeBytes: 0,
|
|
20818
|
+
oldestBackup: null
|
|
20819
|
+
};
|
|
20820
|
+
}
|
|
20821
|
+
return {
|
|
20822
|
+
lastBackup: backups[0].lastModified,
|
|
20823
|
+
backupCount: backups.length,
|
|
20824
|
+
totalSizeBytes: backups.reduce((acc, item) => acc + item.size, 0),
|
|
20825
|
+
oldestBackup: backups[backups.length - 1].lastModified
|
|
20826
|
+
};
|
|
20827
|
+
}
|
|
20828
|
+
|
|
20829
|
+
// src/mcp/tools/backup-vps.ts
|
|
20830
|
+
function registerBackupVps(server2) {
|
|
20831
|
+
server2.registerTool(
|
|
20832
|
+
"backup_vps",
|
|
20833
|
+
{
|
|
20834
|
+
title: "VPS Backup",
|
|
20835
|
+
description: "Run VPS Postgres backup/restore workflows and check status.",
|
|
20836
|
+
inputSchema: z64.discriminatedUnion("action", [
|
|
20837
|
+
z64.object({
|
|
20838
|
+
action: z64.literal("backup"),
|
|
20839
|
+
databaseUrl: z64.string().min(1).describe("Postgres DATABASE_URL to dump from"),
|
|
20840
|
+
encryptionKeyB64: z64.string().min(1).max(4096).optional().describe(
|
|
20841
|
+
"Base64 AES-256 key (defaults to local master key if omitted)"
|
|
20842
|
+
),
|
|
20843
|
+
r2Bucket: z64.string().min(1).describe("R2 bucket name"),
|
|
20844
|
+
r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
|
|
20845
|
+
r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
|
|
20846
|
+
r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
|
|
20847
|
+
}),
|
|
20848
|
+
z64.object({
|
|
20849
|
+
action: z64.literal("restore"),
|
|
20850
|
+
databaseUrl: z64.string().min(1).describe("Target Postgres DATABASE_URL for restore"),
|
|
20851
|
+
backupKey: z64.string().min(1).describe("R2 object key to restore from"),
|
|
20852
|
+
encryptionKeyB64: z64.string().min(1).max(4096).optional().describe(
|
|
20853
|
+
"Base64 AES-256 key (defaults to local master key if omitted)"
|
|
20854
|
+
),
|
|
20855
|
+
r2Bucket: z64.string().min(1).describe("R2 bucket name"),
|
|
20856
|
+
r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
|
|
20857
|
+
r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
|
|
20858
|
+
r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
|
|
20859
|
+
}),
|
|
20860
|
+
z64.object({
|
|
20861
|
+
action: z64.literal("list"),
|
|
20862
|
+
r2Bucket: z64.string().min(1).describe("R2 bucket name"),
|
|
20863
|
+
r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
|
|
20864
|
+
r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
|
|
20865
|
+
r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
|
|
20866
|
+
}),
|
|
20867
|
+
z64.object({
|
|
20868
|
+
action: z64.literal("health"),
|
|
20869
|
+
r2Bucket: z64.string().min(1).describe("R2 bucket name"),
|
|
20870
|
+
r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
|
|
20871
|
+
r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
|
|
20872
|
+
r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
|
|
20873
|
+
})
|
|
20874
|
+
])
|
|
20875
|
+
},
|
|
20876
|
+
async (input) => {
|
|
20877
|
+
try {
|
|
20878
|
+
switch (input.action) {
|
|
20879
|
+
case "backup": {
|
|
20880
|
+
const encryptionKey = await resolveEncryptionKey(input.encryptionKeyB64);
|
|
20881
|
+
const options = {
|
|
20882
|
+
databaseUrl: input.databaseUrl,
|
|
20883
|
+
encryptionKey,
|
|
20884
|
+
r2Bucket: input.r2Bucket,
|
|
20885
|
+
r2Endpoint: input.r2Endpoint,
|
|
20886
|
+
r2AccessKeyId: input.r2AccessKeyId,
|
|
20887
|
+
r2SecretAccessKey: input.r2SecretAccessKey
|
|
20888
|
+
};
|
|
20889
|
+
const result = await createBackup(options);
|
|
20890
|
+
return {
|
|
20891
|
+
content: [
|
|
20892
|
+
{
|
|
20893
|
+
type: "text",
|
|
20894
|
+
text: `Backup complete: ${result.key}
|
|
20895
|
+
${result.sizeBytes} bytes
|
|
20896
|
+
${result.timestamp}`
|
|
20897
|
+
}
|
|
20898
|
+
]
|
|
20899
|
+
};
|
|
20900
|
+
}
|
|
20901
|
+
case "restore": {
|
|
20902
|
+
const encryptionKey = await resolveEncryptionKey(input.encryptionKeyB64);
|
|
20903
|
+
const options = {
|
|
20904
|
+
databaseUrl: input.databaseUrl,
|
|
20905
|
+
backupKey: input.backupKey,
|
|
20906
|
+
encryptionKey,
|
|
20907
|
+
r2Bucket: input.r2Bucket,
|
|
20908
|
+
r2Endpoint: input.r2Endpoint,
|
|
20909
|
+
r2AccessKeyId: input.r2AccessKeyId,
|
|
20910
|
+
r2SecretAccessKey: input.r2SecretAccessKey
|
|
20911
|
+
};
|
|
20912
|
+
await restoreBackup(options);
|
|
20913
|
+
return {
|
|
20914
|
+
content: [
|
|
20915
|
+
{
|
|
20916
|
+
type: "text",
|
|
20917
|
+
text: `Restore complete from ${input.backupKey}`
|
|
20918
|
+
}
|
|
20919
|
+
]
|
|
20920
|
+
};
|
|
20921
|
+
}
|
|
20922
|
+
case "list": {
|
|
20923
|
+
const options = {
|
|
20924
|
+
r2Bucket: input.r2Bucket,
|
|
20925
|
+
r2Endpoint: input.r2Endpoint,
|
|
20926
|
+
r2AccessKeyId: input.r2AccessKeyId,
|
|
20927
|
+
r2SecretAccessKey: input.r2SecretAccessKey
|
|
20928
|
+
};
|
|
20929
|
+
const result = await listBackups(options);
|
|
20930
|
+
return {
|
|
20931
|
+
content: [
|
|
20932
|
+
{
|
|
20933
|
+
type: "text",
|
|
20934
|
+
text: formatBackups(result)
|
|
20935
|
+
}
|
|
20936
|
+
]
|
|
20937
|
+
};
|
|
20938
|
+
}
|
|
20939
|
+
case "health": {
|
|
20940
|
+
const result = await backupHealth({
|
|
20941
|
+
r2Bucket: input.r2Bucket,
|
|
20942
|
+
r2Endpoint: input.r2Endpoint,
|
|
20943
|
+
r2AccessKeyId: input.r2AccessKeyId,
|
|
20944
|
+
r2SecretAccessKey: input.r2SecretAccessKey
|
|
20945
|
+
});
|
|
20946
|
+
return {
|
|
20947
|
+
content: [
|
|
20948
|
+
{
|
|
20949
|
+
type: "text",
|
|
20950
|
+
text: [
|
|
20951
|
+
`last_backup: ${result.lastBackup ?? "none"}`,
|
|
20952
|
+
`backup_count: ${result.backupCount}`,
|
|
20953
|
+
`total_size_bytes: ${result.totalSizeBytes}`,
|
|
20954
|
+
`oldest_backup: ${result.oldestBackup ?? "none"}`
|
|
20955
|
+
].join("\n")
|
|
20956
|
+
}
|
|
20957
|
+
]
|
|
20958
|
+
};
|
|
20959
|
+
}
|
|
20960
|
+
default:
|
|
20961
|
+
return {
|
|
20962
|
+
content: [
|
|
20963
|
+
{
|
|
20964
|
+
type: "text",
|
|
20965
|
+
text: "Unknown backup action"
|
|
20966
|
+
}
|
|
20967
|
+
],
|
|
20968
|
+
isError: true
|
|
20969
|
+
};
|
|
20970
|
+
}
|
|
20971
|
+
} catch (error) {
|
|
20972
|
+
return {
|
|
20973
|
+
content: [
|
|
20974
|
+
{
|
|
20975
|
+
type: "text",
|
|
20976
|
+
text: error instanceof Error ? error.message : String(error)
|
|
20977
|
+
}
|
|
20978
|
+
],
|
|
20979
|
+
isError: true
|
|
20980
|
+
};
|
|
20981
|
+
}
|
|
20982
|
+
}
|
|
20983
|
+
);
|
|
20984
|
+
}
|
|
20985
|
+
function formatBackups(backups) {
|
|
20986
|
+
if (backups.length === 0) {
|
|
20987
|
+
return "No backups found.";
|
|
20988
|
+
}
|
|
20989
|
+
const lines = [
|
|
20990
|
+
"# VPS backups",
|
|
20991
|
+
"",
|
|
20992
|
+
"| Key | Size | Last Modified |",
|
|
20993
|
+
"| --- | ----: | ------------- |",
|
|
20994
|
+
...backups.map((item) => `| ${item.key} | ${item.size.toLocaleString()} | ${item.lastModified} |`)
|
|
20995
|
+
];
|
|
20996
|
+
return lines.join("\n");
|
|
20997
|
+
}
|
|
20998
|
+
async function resolveEncryptionKey(providedB64) {
|
|
20999
|
+
if (providedB64) {
|
|
21000
|
+
return Buffer.from(providedB64, "base64");
|
|
21001
|
+
}
|
|
21002
|
+
const masterKey = await getMasterKey();
|
|
21003
|
+
if (!masterKey) {
|
|
21004
|
+
throw new Error("No encryption key provided and no local master key is available.");
|
|
21005
|
+
}
|
|
21006
|
+
return masterKey;
|
|
21007
|
+
}
|
|
21008
|
+
|
|
21009
|
+
// src/mcp/tools/people-roster.ts
|
|
21010
|
+
import { z as z65 } from "zod";
|
|
21011
|
+
|
|
19768
21012
|
// src/lib/people.ts
|
|
19769
21013
|
init_config();
|
|
19770
21014
|
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
19771
21015
|
import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
|
|
19772
|
-
import
|
|
19773
|
-
var PEOPLE_PATH =
|
|
21016
|
+
import path39 from "path";
|
|
21017
|
+
var PEOPLE_PATH = path39.join(EXE_AI_DIR, "people.json");
|
|
19774
21018
|
async function loadPeople() {
|
|
19775
21019
|
if (!existsSync30(PEOPLE_PATH)) return [];
|
|
19776
21020
|
try {
|
|
@@ -19781,7 +21025,7 @@ async function loadPeople() {
|
|
|
19781
21025
|
}
|
|
19782
21026
|
}
|
|
19783
21027
|
async function savePeople(people) {
|
|
19784
|
-
await mkdir5(
|
|
21028
|
+
await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
|
|
19785
21029
|
await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
|
|
19786
21030
|
}
|
|
19787
21031
|
async function addPerson(person) {
|
|
@@ -19811,10 +21055,10 @@ function registerAddPerson(server2) {
|
|
|
19811
21055
|
title: "Add Person",
|
|
19812
21056
|
description: "Add or update a key human in the people roster. Used for co-founders, partners, customers \u2014 anyone agents need to know about.",
|
|
19813
21057
|
inputSchema: {
|
|
19814
|
-
name:
|
|
19815
|
-
role:
|
|
19816
|
-
relationship:
|
|
19817
|
-
notes:
|
|
21058
|
+
name: z65.string().describe("Person's name"),
|
|
21059
|
+
role: z65.string().describe("Their role (e.g. co-founder, customer, partner)"),
|
|
21060
|
+
relationship: z65.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
|
|
21061
|
+
notes: z65.string().optional().describe("Additional context about this person")
|
|
19818
21062
|
}
|
|
19819
21063
|
},
|
|
19820
21064
|
async ({ name, role, relationship, notes }) => {
|
|
@@ -19860,7 +21104,7 @@ function registerGetPerson(server2) {
|
|
|
19860
21104
|
title: "Get Person",
|
|
19861
21105
|
description: "Look up a specific person by name from the people roster.",
|
|
19862
21106
|
inputSchema: {
|
|
19863
|
-
name:
|
|
21107
|
+
name: z65.string().describe("Person's name to look up")
|
|
19864
21108
|
}
|
|
19865
21109
|
},
|
|
19866
21110
|
async ({ name }) => {
|
|
@@ -19888,7 +21132,7 @@ init_active_agent();
|
|
|
19888
21132
|
init_agent_config();
|
|
19889
21133
|
init_runtime_table();
|
|
19890
21134
|
init_employees();
|
|
19891
|
-
import { z as
|
|
21135
|
+
import { z as z66 } from "zod";
|
|
19892
21136
|
function registerSetAgentConfig(server2) {
|
|
19893
21137
|
server2.registerTool(
|
|
19894
21138
|
"set_agent_config",
|
|
@@ -19896,14 +21140,22 @@ function registerSetAgentConfig(server2) {
|
|
|
19896
21140
|
title: "Set Agent Config",
|
|
19897
21141
|
description: "Set or view per-agent runtime + model configuration. Controls which runtime (claude, codex, opencode) and model each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents.",
|
|
19898
21142
|
inputSchema: {
|
|
19899
|
-
agent_id:
|
|
19900
|
-
runtime:
|
|
19901
|
-
model:
|
|
21143
|
+
agent_id: z66.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
|
|
21144
|
+
runtime: z66.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
|
|
21145
|
+
model: z66.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)")
|
|
19902
21146
|
}
|
|
19903
21147
|
},
|
|
19904
21148
|
async ({ agent_id, runtime, model }) => {
|
|
19905
|
-
const { agentRole } = getActiveAgent();
|
|
19906
|
-
|
|
21149
|
+
const { agentId, agentRole } = getActiveAgent();
|
|
21150
|
+
const isCoordinator = isCoordinatorRole(agentRole) || (() => {
|
|
21151
|
+
try {
|
|
21152
|
+
const emp = getEmployee(loadEmployeesSync(), agentId);
|
|
21153
|
+
return emp ? isCoordinatorRole(emp.role) : false;
|
|
21154
|
+
} catch {
|
|
21155
|
+
return false;
|
|
21156
|
+
}
|
|
21157
|
+
})();
|
|
21158
|
+
if (!isCoordinator) {
|
|
19907
21159
|
return {
|
|
19908
21160
|
content: [
|
|
19909
21161
|
{
|
|
@@ -19988,6 +21240,72 @@ function registerSetAgentConfig(server2) {
|
|
|
19988
21240
|
);
|
|
19989
21241
|
}
|
|
19990
21242
|
|
|
21243
|
+
// src/mcp/tools/list-employees.ts
|
|
21244
|
+
init_employees();
|
|
21245
|
+
init_agent_config();
|
|
21246
|
+
import { z as z67 } from "zod";
|
|
21247
|
+
function registerListEmployees(server2) {
|
|
21248
|
+
server2.registerTool(
|
|
21249
|
+
"list_employees",
|
|
21250
|
+
{
|
|
21251
|
+
title: "List Employees",
|
|
21252
|
+
description: "List all AI employees in the organization with their role, runtime, and model configuration. Use to see who's on the team and how they're configured.",
|
|
21253
|
+
inputSchema: {
|
|
21254
|
+
role: z67.string().optional().describe("Filter by role (case-insensitive)")
|
|
21255
|
+
}
|
|
21256
|
+
},
|
|
21257
|
+
async ({ role }) => {
|
|
21258
|
+
try {
|
|
21259
|
+
let employees = loadEmployeesSync();
|
|
21260
|
+
if (role) {
|
|
21261
|
+
const lower = role.toLowerCase();
|
|
21262
|
+
employees = employees.filter(
|
|
21263
|
+
(e) => e.role.toLowerCase().includes(lower)
|
|
21264
|
+
);
|
|
21265
|
+
}
|
|
21266
|
+
if (employees.length === 0) {
|
|
21267
|
+
return {
|
|
21268
|
+
content: [
|
|
21269
|
+
{
|
|
21270
|
+
type: "text",
|
|
21271
|
+
text: role ? `No employees found with role matching "${role}".` : "No employees in roster."
|
|
21272
|
+
}
|
|
21273
|
+
]
|
|
21274
|
+
};
|
|
21275
|
+
}
|
|
21276
|
+
const lines = [
|
|
21277
|
+
"Employee Roster",
|
|
21278
|
+
"\u2550".repeat(70),
|
|
21279
|
+
"Name".padEnd(12) + "Role".padEnd(20) + "Runtime".padEnd(16) + "Model",
|
|
21280
|
+
"\u2500".repeat(70)
|
|
21281
|
+
];
|
|
21282
|
+
for (const emp of employees) {
|
|
21283
|
+
const cfg = getAgentRuntime(emp.name);
|
|
21284
|
+
const rtLabel = RUNTIME_LABELS[cfg.runtime] ?? cfg.runtime;
|
|
21285
|
+
lines.push(
|
|
21286
|
+
emp.name.padEnd(12) + emp.role.padEnd(20) + rtLabel.substring(0, 15).padEnd(16) + cfg.model
|
|
21287
|
+
);
|
|
21288
|
+
}
|
|
21289
|
+
lines.push("\u2500".repeat(70));
|
|
21290
|
+
lines.push(`${employees.length} employee(s)`);
|
|
21291
|
+
return {
|
|
21292
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
21293
|
+
};
|
|
21294
|
+
} catch (err) {
|
|
21295
|
+
return {
|
|
21296
|
+
content: [
|
|
21297
|
+
{
|
|
21298
|
+
type: "text",
|
|
21299
|
+
text: `Failed to list employees: ${err instanceof Error ? err.message : String(err)}`
|
|
21300
|
+
}
|
|
21301
|
+
],
|
|
21302
|
+
isError: true
|
|
21303
|
+
};
|
|
21304
|
+
}
|
|
21305
|
+
}
|
|
21306
|
+
);
|
|
21307
|
+
}
|
|
21308
|
+
|
|
19991
21309
|
// src/lib/telemetry.ts
|
|
19992
21310
|
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
19993
21311
|
var initialized = false;
|
|
@@ -20115,6 +21433,7 @@ registerGetAutoWakeStatus(server);
|
|
|
20115
21433
|
registerGetWorkerGate(server);
|
|
20116
21434
|
registerRunMemoryAudit(server);
|
|
20117
21435
|
registerCloudSync(server);
|
|
21436
|
+
registerBackupVps(server);
|
|
20118
21437
|
registerGetMemoryCardinality(server);
|
|
20119
21438
|
registerRunConsolidation(server);
|
|
20120
21439
|
registerGetLicenseStatus(server);
|
|
@@ -20122,12 +21441,19 @@ registerAddPerson(server);
|
|
|
20122
21441
|
registerListPeople(server);
|
|
20123
21442
|
registerGetPerson(server);
|
|
20124
21443
|
registerSetAgentConfig(server);
|
|
21444
|
+
registerListEmployees(server);
|
|
20125
21445
|
try {
|
|
20126
21446
|
await initStore();
|
|
20127
21447
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
20128
21448
|
const transport = new StdioServerTransport();
|
|
20129
21449
|
await server.connect(transport);
|
|
20130
21450
|
process.stderr.write("[exe-os] MCP server connected.\n");
|
|
21451
|
+
void initDaemonClient().catch((err) => {
|
|
21452
|
+
process.stderr.write(
|
|
21453
|
+
`[exe-os] Daemon DB routing unavailable (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
21454
|
+
`
|
|
21455
|
+
);
|
|
21456
|
+
});
|
|
20131
21457
|
void (async () => {
|
|
20132
21458
|
try {
|
|
20133
21459
|
const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
@@ -20184,20 +21510,20 @@ try {
|
|
|
20184
21510
|
`
|
|
20185
21511
|
);
|
|
20186
21512
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
20187
|
-
const backfillPath =
|
|
20188
|
-
|
|
21513
|
+
const backfillPath = path40.resolve(
|
|
21514
|
+
path40.dirname(thisFile),
|
|
20189
21515
|
"../bin/backfill-vectors.js"
|
|
20190
21516
|
);
|
|
20191
21517
|
if (existsSync31(backfillPath)) {
|
|
20192
21518
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
20193
|
-
const logPath =
|
|
20194
|
-
mkdirSync17(
|
|
21519
|
+
const logPath = path40.join(exeDir, "workers.log");
|
|
21520
|
+
mkdirSync17(path40.dirname(logPath), { recursive: true });
|
|
20195
21521
|
let logFd = "ignore";
|
|
20196
21522
|
try {
|
|
20197
21523
|
logFd = openSync3(logPath, "a");
|
|
20198
21524
|
} catch {
|
|
20199
21525
|
}
|
|
20200
|
-
const child =
|
|
21526
|
+
const child = spawn4(process.execPath, [backfillPath], {
|
|
20201
21527
|
detached: true,
|
|
20202
21528
|
stdio: ["ignore", "ignore", logFd]
|
|
20203
21529
|
});
|