@askexenow/exe-os 0.9.6 → 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 +668 -37
- package/dist/bin/cli.js +1399 -607
- 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 +795 -155
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +703 -72
- 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 +1064 -273
- package/dist/bin/exe-heartbeat.js +676 -45
- 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 +845 -152
- 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 +668 -37
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +731 -91
- 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 +735 -95
- 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 +1038 -247
- package/dist/hooks/bug-report-worker.js +902 -172
- package/dist/hooks/commit-complete.js +729 -89
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +851 -158
- 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 +685 -45
- package/dist/hooks/pre-compact.js +729 -89
- package/dist/hooks/pre-tool-use.js +883 -127
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1071 -321
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +732 -92
- package/dist/hooks/session-start.js +1042 -209
- package/dist/hooks/stop.js +691 -51
- package/dist/hooks/subagent-stop.js +685 -45
- package/dist/hooks/summary-worker.js +827 -134
- package/dist/index.js +1026 -234
- 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 +905 -164
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +66 -30
- 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 +109 -73
- package/dist/lib/tmux-routing.js +98 -62
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1807 -472
- 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 +301 -166
- 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 +206 -40
- package/dist/mcp/tools/send-message.js +69 -33
- package/dist/mcp/tools/update-task.js +86 -50
- package/dist/runtime/index.js +731 -91
- package/dist/tui/App.js +864 -125
- 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 };
|
|
560
611
|
}
|
|
561
|
-
|
|
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);
|
|
621
|
+
}
|
|
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,29 +1162,615 @@ 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
|
}
|
|
973
|
-
try {
|
|
974
|
-
symlinkSync(target, linkPath);
|
|
975
|
-
created.push(linkName);
|
|
976
|
-
} catch (err) {
|
|
977
|
-
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1170
|
+
try {
|
|
1171
|
+
symlinkSync(target, linkPath);
|
|
1172
|
+
created.push(linkName);
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return { created, skipped, errors };
|
|
1178
|
+
}
|
|
1179
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
1180
|
+
var init_employees = __esm({
|
|
1181
|
+
"src/lib/employees.ts"() {
|
|
1182
|
+
"use strict";
|
|
1183
|
+
init_config();
|
|
1184
|
+
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
1185
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1186
|
+
COORDINATOR_ROLE = "COO";
|
|
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;
|
|
978
1603
|
}
|
|
1604
|
+
current += ch;
|
|
979
1605
|
}
|
|
980
|
-
|
|
1606
|
+
if (current.trim()) {
|
|
1607
|
+
parts.push(current.trim());
|
|
1608
|
+
}
|
|
1609
|
+
return parts;
|
|
981
1610
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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"() {
|
|
985
1720
|
"use strict";
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
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;
|
|
@@ -6772,15 +7506,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
6772
7506
|
function resolveExeSession() {
|
|
6773
7507
|
const mySession = getMySession();
|
|
6774
7508
|
if (!mySession) return null;
|
|
7509
|
+
const fromSessionName = extractRootExe(mySession);
|
|
6775
7510
|
try {
|
|
6776
7511
|
const key = getSessionKey();
|
|
6777
7512
|
const parentExe = getParentExe(key);
|
|
6778
7513
|
if (parentExe) {
|
|
6779
|
-
|
|
7514
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
7515
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
7516
|
+
process.stderr.write(
|
|
7517
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
7518
|
+
`
|
|
7519
|
+
);
|
|
7520
|
+
return fromSessionName;
|
|
7521
|
+
}
|
|
7522
|
+
return fromCache;
|
|
6780
7523
|
}
|
|
6781
7524
|
} catch {
|
|
6782
7525
|
}
|
|
6783
|
-
return
|
|
7526
|
+
return fromSessionName ?? mySession;
|
|
6784
7527
|
}
|
|
6785
7528
|
function isEmployeeAlive(sessionName) {
|
|
6786
7529
|
return getTransport().isAlive(sessionName);
|
|
@@ -6938,7 +7681,7 @@ function sendIntercom(targetSession) {
|
|
|
6938
7681
|
try {
|
|
6939
7682
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6940
7683
|
const agent = baseAgentName(rawAgent);
|
|
6941
|
-
const markerPath =
|
|
7684
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6942
7685
|
if (existsSync14(markerPath)) {
|
|
6943
7686
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6944
7687
|
return "debounced";
|
|
@@ -6948,7 +7691,7 @@ function sendIntercom(targetSession) {
|
|
|
6948
7691
|
try {
|
|
6949
7692
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6950
7693
|
const agent = baseAgentName(rawAgent);
|
|
6951
|
-
const taskDir =
|
|
7694
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
6952
7695
|
if (existsSync14(taskDir)) {
|
|
6953
7696
|
const files = readdirSync5(taskDir).filter(
|
|
6954
7697
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -7082,8 +7825,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7082
7825
|
const transport = getTransport();
|
|
7083
7826
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7084
7827
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7085
|
-
const logDir =
|
|
7086
|
-
const logFile =
|
|
7828
|
+
const logDir = path19.join(os9.homedir(), ".exe-os", "session-logs");
|
|
7829
|
+
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7087
7830
|
if (!existsSync14(logDir)) {
|
|
7088
7831
|
mkdirSync7(logDir, { recursive: true });
|
|
7089
7832
|
}
|
|
@@ -7091,14 +7834,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7091
7834
|
let cleanupSuffix = "";
|
|
7092
7835
|
try {
|
|
7093
7836
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7094
|
-
const cleanupScript =
|
|
7837
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7095
7838
|
if (existsSync14(cleanupScript)) {
|
|
7096
7839
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7097
7840
|
}
|
|
7098
7841
|
} catch {
|
|
7099
7842
|
}
|
|
7100
7843
|
try {
|
|
7101
|
-
const claudeJsonPath =
|
|
7844
|
+
const claudeJsonPath = path19.join(os9.homedir(), ".claude.json");
|
|
7102
7845
|
let claudeJson = {};
|
|
7103
7846
|
try {
|
|
7104
7847
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -7113,10 +7856,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7113
7856
|
} catch {
|
|
7114
7857
|
}
|
|
7115
7858
|
try {
|
|
7116
|
-
const settingsDir =
|
|
7859
|
+
const settingsDir = path19.join(os9.homedir(), ".claude", "projects");
|
|
7117
7860
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7118
|
-
const projSettingsDir =
|
|
7119
|
-
const settingsPath =
|
|
7861
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
7862
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
7120
7863
|
let settings = {};
|
|
7121
7864
|
try {
|
|
7122
7865
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -7163,8 +7906,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7163
7906
|
let behaviorsFlag = "";
|
|
7164
7907
|
let legacyFallbackWarned = false;
|
|
7165
7908
|
if (!useExeAgent && !useBinSymlink) {
|
|
7166
|
-
const identityPath2 =
|
|
7167
|
-
|
|
7909
|
+
const identityPath2 = path19.join(
|
|
7910
|
+
os9.homedir(),
|
|
7168
7911
|
".exe-os",
|
|
7169
7912
|
"identity",
|
|
7170
7913
|
`${employeeName}.md`
|
|
@@ -7179,7 +7922,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7179
7922
|
}
|
|
7180
7923
|
const behaviorsFile = exportBehaviorsSync(
|
|
7181
7924
|
employeeName,
|
|
7182
|
-
|
|
7925
|
+
path19.basename(spawnCwd),
|
|
7183
7926
|
sessionName
|
|
7184
7927
|
);
|
|
7185
7928
|
if (behaviorsFile) {
|
|
@@ -7194,9 +7937,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7194
7937
|
}
|
|
7195
7938
|
let sessionContextFlag = "";
|
|
7196
7939
|
try {
|
|
7197
|
-
const ctxDir =
|
|
7940
|
+
const ctxDir = path19.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7198
7941
|
mkdirSync7(ctxDir, { recursive: true });
|
|
7199
|
-
const ctxFile =
|
|
7942
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7200
7943
|
const ctxContent = [
|
|
7201
7944
|
`## Session Context`,
|
|
7202
7945
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -7280,7 +8023,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7280
8023
|
transport.pipeLog(sessionName, logFile);
|
|
7281
8024
|
try {
|
|
7282
8025
|
const mySession = getMySession();
|
|
7283
|
-
const dispatchInfo =
|
|
8026
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7284
8027
|
writeFileSync9(dispatchInfo, JSON.stringify({
|
|
7285
8028
|
dispatchedBy: mySession,
|
|
7286
8029
|
rootExe: exeSession,
|
|
@@ -7355,15 +8098,15 @@ var init_tmux_routing = __esm({
|
|
|
7355
8098
|
init_intercom_queue();
|
|
7356
8099
|
init_plan_limits();
|
|
7357
8100
|
init_employees();
|
|
7358
|
-
SPAWN_LOCK_DIR =
|
|
7359
|
-
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");
|
|
7360
8103
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7361
8104
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7362
8105
|
VERIFY_PANE_LINES = 200;
|
|
7363
8106
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7364
8107
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7365
|
-
INTERCOM_LOG2 =
|
|
7366
|
-
DEBOUNCE_FILE =
|
|
8108
|
+
INTERCOM_LOG2 = path19.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
8109
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7367
8110
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7368
8111
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7369
8112
|
}
|
|
@@ -7411,8 +8154,8 @@ __export(tasks_crud_exports, {
|
|
|
7411
8154
|
writeCheckpoint: () => writeCheckpoint
|
|
7412
8155
|
});
|
|
7413
8156
|
import crypto6 from "crypto";
|
|
7414
|
-
import
|
|
7415
|
-
import
|
|
8157
|
+
import path20 from "path";
|
|
8158
|
+
import os10 from "os";
|
|
7416
8159
|
import { execSync as execSync8 } from "child_process";
|
|
7417
8160
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
7418
8161
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
@@ -7590,8 +8333,8 @@ ${laneWarning}` : laneWarning;
|
|
|
7590
8333
|
}
|
|
7591
8334
|
if (input.baseDir) {
|
|
7592
8335
|
try {
|
|
7593
|
-
await mkdir4(
|
|
7594
|
-
await mkdir4(
|
|
8336
|
+
await mkdir4(path20.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
8337
|
+
await mkdir4(path20.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
7595
8338
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
7596
8339
|
await ensureGitignoreExe(input.baseDir);
|
|
7597
8340
|
} catch {
|
|
@@ -7627,9 +8370,9 @@ ${laneWarning}` : laneWarning;
|
|
|
7627
8370
|
});
|
|
7628
8371
|
if (input.baseDir) {
|
|
7629
8372
|
try {
|
|
7630
|
-
const EXE_OS_DIR =
|
|
7631
|
-
const mdPath =
|
|
7632
|
-
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);
|
|
7633
8376
|
if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
7634
8377
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
7635
8378
|
const mdContent = `# ${input.title}
|
|
@@ -7930,7 +8673,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
7930
8673
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
7931
8674
|
}
|
|
7932
8675
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
7933
|
-
const archPath =
|
|
8676
|
+
const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
7934
8677
|
try {
|
|
7935
8678
|
if (existsSync15(archPath)) return;
|
|
7936
8679
|
const template = [
|
|
@@ -7965,7 +8708,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
7965
8708
|
}
|
|
7966
8709
|
}
|
|
7967
8710
|
async function ensureGitignoreExe(baseDir) {
|
|
7968
|
-
const gitignorePath =
|
|
8711
|
+
const gitignorePath = path20.join(baseDir, ".gitignore");
|
|
7969
8712
|
try {
|
|
7970
8713
|
if (existsSync15(gitignorePath)) {
|
|
7971
8714
|
const content = readFileSync13(gitignorePath, "utf-8");
|
|
@@ -7999,13 +8742,13 @@ var init_tasks_crud = __esm({
|
|
|
7999
8742
|
});
|
|
8000
8743
|
|
|
8001
8744
|
// src/lib/tasks-review.ts
|
|
8002
|
-
import
|
|
8745
|
+
import path21 from "path";
|
|
8003
8746
|
import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
8004
8747
|
async function countPendingReviews(sessionScope) {
|
|
8005
8748
|
const client = getClient();
|
|
8006
8749
|
if (sessionScope) {
|
|
8007
8750
|
const result2 = await client.execute({
|
|
8008
|
-
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 = ?",
|
|
8009
8752
|
args: [sessionScope]
|
|
8010
8753
|
});
|
|
8011
8754
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -8181,11 +8924,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
8181
8924
|
);
|
|
8182
8925
|
}
|
|
8183
8926
|
try {
|
|
8184
|
-
const cacheDir =
|
|
8927
|
+
const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
|
|
8185
8928
|
if (existsSync16(cacheDir)) {
|
|
8186
8929
|
for (const f of readdirSync6(cacheDir)) {
|
|
8187
8930
|
if (f.startsWith("review-notified-")) {
|
|
8188
|
-
unlinkSync6(
|
|
8931
|
+
unlinkSync6(path21.join(cacheDir, f));
|
|
8189
8932
|
}
|
|
8190
8933
|
}
|
|
8191
8934
|
}
|
|
@@ -8206,7 +8949,7 @@ var init_tasks_review = __esm({
|
|
|
8206
8949
|
});
|
|
8207
8950
|
|
|
8208
8951
|
// src/lib/tasks-chain.ts
|
|
8209
|
-
import
|
|
8952
|
+
import path22 from "path";
|
|
8210
8953
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
8211
8954
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
8212
8955
|
const client = getClient();
|
|
@@ -8223,7 +8966,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
8223
8966
|
});
|
|
8224
8967
|
for (const ur of unblockedRows.rows) {
|
|
8225
8968
|
try {
|
|
8226
|
-
const ubFile =
|
|
8969
|
+
const ubFile = path22.join(baseDir, String(ur.task_file));
|
|
8227
8970
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
8228
8971
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
8229
8972
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -8777,7 +9520,7 @@ __export(tasks_exports, {
|
|
|
8777
9520
|
updateTaskStatus: () => updateTaskStatus,
|
|
8778
9521
|
writeCheckpoint: () => writeCheckpoint
|
|
8779
9522
|
});
|
|
8780
|
-
import
|
|
9523
|
+
import path23 from "path";
|
|
8781
9524
|
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
8782
9525
|
async function createTask(input) {
|
|
8783
9526
|
const result = await createTaskCore(input);
|
|
@@ -8797,8 +9540,8 @@ async function updateTask(input) {
|
|
|
8797
9540
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
8798
9541
|
try {
|
|
8799
9542
|
const agent = String(row.assigned_to);
|
|
8800
|
-
const cacheDir =
|
|
8801
|
-
const cachePath =
|
|
9543
|
+
const cacheDir = path23.join(EXE_AI_DIR, "session-cache");
|
|
9544
|
+
const cachePath = path23.join(cacheDir, `current-task-${agent}.json`);
|
|
8802
9545
|
if (input.status === "in_progress") {
|
|
8803
9546
|
mkdirSync8(cacheDir, { recursive: true });
|
|
8804
9547
|
writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -8969,15 +9712,15 @@ __export(identity_exports, {
|
|
|
8969
9712
|
});
|
|
8970
9713
|
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
8971
9714
|
import { readdirSync as readdirSync7 } from "fs";
|
|
8972
|
-
import
|
|
9715
|
+
import path24 from "path";
|
|
8973
9716
|
import { createHash as createHash2 } from "crypto";
|
|
8974
9717
|
function ensureDir2() {
|
|
8975
|
-
if (!existsSync17(
|
|
8976
|
-
mkdirSync9(
|
|
9718
|
+
if (!existsSync17(IDENTITY_DIR2)) {
|
|
9719
|
+
mkdirSync9(IDENTITY_DIR2, { recursive: true });
|
|
8977
9720
|
}
|
|
8978
9721
|
}
|
|
8979
9722
|
function identityPath(agentId) {
|
|
8980
|
-
return
|
|
9723
|
+
return path24.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
8981
9724
|
}
|
|
8982
9725
|
function parseFrontmatter(raw) {
|
|
8983
9726
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -9050,7 +9793,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
9050
9793
|
}
|
|
9051
9794
|
function listIdentities() {
|
|
9052
9795
|
ensureDir2();
|
|
9053
|
-
const files = readdirSync7(
|
|
9796
|
+
const files = readdirSync7(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
9054
9797
|
const results = [];
|
|
9055
9798
|
for (const file of files) {
|
|
9056
9799
|
const agentId = file.replace(".md", "");
|
|
@@ -9083,13 +9826,13 @@ ${teamLines.join("\n")}`);
|
|
|
9083
9826
|
}
|
|
9084
9827
|
return parts.join("\n\n");
|
|
9085
9828
|
}
|
|
9086
|
-
var
|
|
9829
|
+
var IDENTITY_DIR2;
|
|
9087
9830
|
var init_identity = __esm({
|
|
9088
9831
|
"src/lib/identity.ts"() {
|
|
9089
9832
|
"use strict";
|
|
9090
9833
|
init_config();
|
|
9091
9834
|
init_database();
|
|
9092
|
-
|
|
9835
|
+
IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
|
|
9093
9836
|
}
|
|
9094
9837
|
});
|
|
9095
9838
|
|
|
@@ -10202,8 +10945,8 @@ __export(wiki_client_exports, {
|
|
|
10202
10945
|
listDocuments: () => listDocuments,
|
|
10203
10946
|
listWorkspaces: () => listWorkspaces
|
|
10204
10947
|
});
|
|
10205
|
-
async function wikiFetch(config2,
|
|
10206
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
10948
|
+
async function wikiFetch(config2, path41, method = "GET", body) {
|
|
10949
|
+
const url = `${config2.baseUrl}/api/v1${path41}`;
|
|
10207
10950
|
const headers = {
|
|
10208
10951
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
10209
10952
|
"Content-Type": "application/json"
|
|
@@ -10236,7 +10979,7 @@ async function wikiFetch(config2, path40, method = "GET", body) {
|
|
|
10236
10979
|
}
|
|
10237
10980
|
}
|
|
10238
10981
|
if (!response.ok) {
|
|
10239
|
-
throw new Error(`Wiki API ${method} ${
|
|
10982
|
+
throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
|
|
10240
10983
|
}
|
|
10241
10984
|
return response.json();
|
|
10242
10985
|
} finally {
|
|
@@ -10346,12 +11089,12 @@ __export(worker_gate_exports, {
|
|
|
10346
11089
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
10347
11090
|
});
|
|
10348
11091
|
import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
|
|
10349
|
-
import
|
|
11092
|
+
import path34 from "path";
|
|
10350
11093
|
function tryAcquireWorkerSlot() {
|
|
10351
11094
|
try {
|
|
10352
11095
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
10353
11096
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
10354
|
-
const reservationPath =
|
|
11097
|
+
const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
10355
11098
|
writeFileSync17(reservationPath, String(process.pid));
|
|
10356
11099
|
const files = readdirSync10(WORKER_PID_DIR);
|
|
10357
11100
|
let alive = 0;
|
|
@@ -10369,7 +11112,7 @@ function tryAcquireWorkerSlot() {
|
|
|
10369
11112
|
alive++;
|
|
10370
11113
|
} catch {
|
|
10371
11114
|
try {
|
|
10372
|
-
unlinkSync8(
|
|
11115
|
+
unlinkSync8(path34.join(WORKER_PID_DIR, f));
|
|
10373
11116
|
} catch {
|
|
10374
11117
|
}
|
|
10375
11118
|
}
|
|
@@ -10393,13 +11136,13 @@ function tryAcquireWorkerSlot() {
|
|
|
10393
11136
|
function registerWorkerPid(pid) {
|
|
10394
11137
|
try {
|
|
10395
11138
|
mkdirSync14(WORKER_PID_DIR, { recursive: true });
|
|
10396
|
-
writeFileSync17(
|
|
11139
|
+
writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
10397
11140
|
} catch {
|
|
10398
11141
|
}
|
|
10399
11142
|
}
|
|
10400
11143
|
function cleanupWorkerPid() {
|
|
10401
11144
|
try {
|
|
10402
|
-
unlinkSync8(
|
|
11145
|
+
unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
10403
11146
|
} catch {
|
|
10404
11147
|
}
|
|
10405
11148
|
}
|
|
@@ -10439,9 +11182,9 @@ var init_worker_gate = __esm({
|
|
|
10439
11182
|
"src/lib/worker-gate.ts"() {
|
|
10440
11183
|
"use strict";
|
|
10441
11184
|
init_config();
|
|
10442
|
-
WORKER_PID_DIR =
|
|
11185
|
+
WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
|
|
10443
11186
|
MAX_CONCURRENT_WORKERS = 3;
|
|
10444
|
-
BACKFILL_LOCK =
|
|
11187
|
+
BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
|
|
10445
11188
|
}
|
|
10446
11189
|
});
|
|
10447
11190
|
|
|
@@ -10465,7 +11208,7 @@ __export(crdt_sync_exports, {
|
|
|
10465
11208
|
});
|
|
10466
11209
|
import * as Y from "yjs";
|
|
10467
11210
|
import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
|
|
10468
|
-
import
|
|
11211
|
+
import path37 from "path";
|
|
10469
11212
|
import { homedir as homedir5 } from "os";
|
|
10470
11213
|
function getStatePath() {
|
|
10471
11214
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -10621,7 +11364,7 @@ function persistState() {
|
|
|
10621
11364
|
if (!doc) return;
|
|
10622
11365
|
try {
|
|
10623
11366
|
const sp = getStatePath();
|
|
10624
|
-
const dir =
|
|
11367
|
+
const dir = path37.dirname(sp);
|
|
10625
11368
|
if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
|
|
10626
11369
|
const state = Y.encodeStateAsUpdate(doc);
|
|
10627
11370
|
writeFileSync18(sp, Buffer.from(state));
|
|
@@ -10665,7 +11408,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
10665
11408
|
var init_crdt_sync = __esm({
|
|
10666
11409
|
"src/lib/crdt-sync.ts"() {
|
|
10667
11410
|
"use strict";
|
|
10668
|
-
DEFAULT_STATE_PATH =
|
|
11411
|
+
DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
|
|
10669
11412
|
_statePathOverride = null;
|
|
10670
11413
|
doc = null;
|
|
10671
11414
|
}
|
|
@@ -10677,9 +11420,9 @@ init_store();
|
|
|
10677
11420
|
init_database();
|
|
10678
11421
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10679
11422
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10680
|
-
import { spawn as
|
|
11423
|
+
import { spawn as spawn4 } from "child_process";
|
|
10681
11424
|
import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
|
|
10682
|
-
import
|
|
11425
|
+
import path40 from "path";
|
|
10683
11426
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10684
11427
|
|
|
10685
11428
|
// src/mcp/tools/recall-my-memory.ts
|
|
@@ -10987,8 +11730,8 @@ init_active_agent();
|
|
|
10987
11730
|
init_plan_limits();
|
|
10988
11731
|
import { z as z4 } from "zod";
|
|
10989
11732
|
import crypto2 from "crypto";
|
|
10990
|
-
import { writeFileSync as
|
|
10991
|
-
import
|
|
11733
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
11734
|
+
import path14 from "path";
|
|
10992
11735
|
function registerStoreMemory(server2) {
|
|
10993
11736
|
server2.registerTool(
|
|
10994
11737
|
"store_memory",
|
|
@@ -11056,8 +11799,8 @@ function registerStoreMemory(server2) {
|
|
|
11056
11799
|
if (needsBackfill) {
|
|
11057
11800
|
try {
|
|
11058
11801
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11059
|
-
const flagPath =
|
|
11060
|
-
|
|
11802
|
+
const flagPath = path14.join(exeDir, "session-cache", "needs-backfill");
|
|
11803
|
+
writeFileSync5(flagPath, "1");
|
|
11061
11804
|
} catch {
|
|
11062
11805
|
}
|
|
11063
11806
|
}
|
|
@@ -11081,8 +11824,8 @@ init_active_agent();
|
|
|
11081
11824
|
init_employees();
|
|
11082
11825
|
import { z as z5 } from "zod";
|
|
11083
11826
|
import crypto3 from "crypto";
|
|
11084
|
-
import { writeFileSync as
|
|
11085
|
-
import
|
|
11827
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
11828
|
+
import path15 from "path";
|
|
11086
11829
|
function registerCommitMemory(server2) {
|
|
11087
11830
|
server2.registerTool(
|
|
11088
11831
|
"commit_to_long_term_memory",
|
|
@@ -11172,8 +11915,8 @@ function registerCommitMemory(server2) {
|
|
|
11172
11915
|
if (needsBackfill) {
|
|
11173
11916
|
try {
|
|
11174
11917
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11175
|
-
const flagPath =
|
|
11176
|
-
|
|
11918
|
+
const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
|
|
11919
|
+
writeFileSync6(flagPath, "1");
|
|
11177
11920
|
} catch {
|
|
11178
11921
|
}
|
|
11179
11922
|
}
|
|
@@ -11439,13 +12182,14 @@ Warning: ${task.warning}`;
|
|
|
11439
12182
|
// src/mcp/tools/list-tasks.ts
|
|
11440
12183
|
init_tasks();
|
|
11441
12184
|
init_project_name();
|
|
12185
|
+
init_active_agent();
|
|
11442
12186
|
import { z as z8 } from "zod";
|
|
11443
12187
|
function registerListTasks(server2) {
|
|
11444
12188
|
server2.registerTool(
|
|
11445
12189
|
"list_tasks",
|
|
11446
12190
|
{
|
|
11447
12191
|
title: "List Tasks",
|
|
11448
|
-
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.",
|
|
11449
12193
|
inputSchema: {
|
|
11450
12194
|
assigned_to: z8.string().optional().describe("Filter by employee name"),
|
|
11451
12195
|
status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
|
|
@@ -11455,7 +12199,16 @@ function registerListTasks(server2) {
|
|
|
11455
12199
|
},
|
|
11456
12200
|
async ({ assigned_to, status, project_name, priority }) => {
|
|
11457
12201
|
try {
|
|
11458
|
-
|
|
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
|
+
}
|
|
11459
12212
|
const tasks = await listTasks({
|
|
11460
12213
|
assignedTo: assigned_to,
|
|
11461
12214
|
status,
|
|
@@ -13210,9 +13963,9 @@ import { z as z29 } from "zod";
|
|
|
13210
13963
|
// src/automation/trigger-engine.ts
|
|
13211
13964
|
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
|
|
13212
13965
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
13213
|
-
import
|
|
13214
|
-
import
|
|
13215
|
-
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");
|
|
13216
13969
|
function loadTriggers(project) {
|
|
13217
13970
|
if (!existsSync18(TRIGGERS_PATH)) return [];
|
|
13218
13971
|
try {
|
|
@@ -13228,7 +13981,7 @@ function loadTriggers(project) {
|
|
|
13228
13981
|
}
|
|
13229
13982
|
}
|
|
13230
13983
|
function saveTriggers(triggers) {
|
|
13231
|
-
const dir =
|
|
13984
|
+
const dir = path25.dirname(TRIGGERS_PATH);
|
|
13232
13985
|
if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
|
|
13233
13986
|
writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
|
|
13234
13987
|
}
|
|
@@ -13515,23 +14268,23 @@ import { z as z31 } from "zod";
|
|
|
13515
14268
|
|
|
13516
14269
|
// src/automation/starter-packs/index.ts
|
|
13517
14270
|
import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
|
|
13518
|
-
import
|
|
14271
|
+
import path26 from "path";
|
|
13519
14272
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13520
|
-
var __dirname =
|
|
14273
|
+
var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
|
|
13521
14274
|
function listPacks() {
|
|
13522
|
-
const packsDir =
|
|
14275
|
+
const packsDir = path26.join(__dirname, ".");
|
|
13523
14276
|
if (!existsSync19(packsDir)) return [];
|
|
13524
14277
|
return readdirSync8(packsDir, { withFileTypes: true }).filter(
|
|
13525
|
-
(d) => d.isDirectory() && existsSync19(
|
|
14278
|
+
(d) => d.isDirectory() && existsSync19(path26.join(packsDir, d.name, "custom-objects.json"))
|
|
13526
14279
|
).map((d) => d.name);
|
|
13527
14280
|
}
|
|
13528
14281
|
function loadPack(industry) {
|
|
13529
|
-
const packDir =
|
|
13530
|
-
const objectsPath =
|
|
13531
|
-
const triggersPath =
|
|
13532
|
-
const wikiDir =
|
|
13533
|
-
const manifestPath =
|
|
13534
|
-
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");
|
|
13535
14288
|
if (!existsSync19(objectsPath)) return null;
|
|
13536
14289
|
let customObjects = [];
|
|
13537
14290
|
try {
|
|
@@ -13555,7 +14308,7 @@ function loadPack(industry) {
|
|
|
13555
14308
|
if (existsSync19(wikiDir)) {
|
|
13556
14309
|
const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
|
|
13557
14310
|
for (const file of files) {
|
|
13558
|
-
const content = readFileSync17(
|
|
14311
|
+
const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
|
|
13559
14312
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
13560
14313
|
wikiSeeds.push({
|
|
13561
14314
|
filename: file,
|
|
@@ -13633,7 +14386,7 @@ function applyPack(industry, project) {
|
|
|
13633
14386
|
// src/lib/client-coo.ts
|
|
13634
14387
|
init_config();
|
|
13635
14388
|
import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
13636
|
-
import
|
|
14389
|
+
import path27 from "path";
|
|
13637
14390
|
|
|
13638
14391
|
// src/lib/employee-templates.ts
|
|
13639
14392
|
init_global_procedures();
|
|
@@ -13771,10 +14524,10 @@ var ClientCOOClobberError = class extends Error {
|
|
|
13771
14524
|
var COO_ROLE = "Chief Operating Officer";
|
|
13772
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.";
|
|
13773
14526
|
async function provisionClientCOO(vars, opts = {}) {
|
|
13774
|
-
const identityDir = opts.identityDir ??
|
|
14527
|
+
const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
|
|
13775
14528
|
const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
|
|
13776
14529
|
const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
|
|
13777
|
-
const identityPath2 =
|
|
14530
|
+
const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
|
|
13778
14531
|
if (existsSync20(identityPath2)) {
|
|
13779
14532
|
throw new ClientCOOClobberError(vars.agent_name, identityPath2);
|
|
13780
14533
|
}
|
|
@@ -14672,7 +15425,7 @@ function registerUpdateWikiPage(server2) {
|
|
|
14672
15425
|
import { z as z37 } from "zod";
|
|
14673
15426
|
import { execFile } from "child_process";
|
|
14674
15427
|
import { promisify } from "util";
|
|
14675
|
-
import
|
|
15428
|
+
import path28 from "path";
|
|
14676
15429
|
import { existsSync as existsSync21 } from "fs";
|
|
14677
15430
|
|
|
14678
15431
|
// src/lib/hostinger-api.ts
|
|
@@ -14721,9 +15474,9 @@ var HostingerApiClient = class {
|
|
|
14721
15474
|
}
|
|
14722
15475
|
this.lastRequestTime = Date.now();
|
|
14723
15476
|
}
|
|
14724
|
-
async request(method,
|
|
15477
|
+
async request(method, path41, body) {
|
|
14725
15478
|
await this.rateLimit();
|
|
14726
|
-
const url = `${this.baseUrl}${
|
|
15479
|
+
const url = `${this.baseUrl}${path41}`;
|
|
14727
15480
|
const headers = {
|
|
14728
15481
|
Authorization: `Bearer ${this.apiKey}`,
|
|
14729
15482
|
"Content-Type": "application/json",
|
|
@@ -14796,8 +15549,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
14796
15549
|
}
|
|
14797
15550
|
return envelope.result;
|
|
14798
15551
|
}
|
|
14799
|
-
function buildUrl(zoneId,
|
|
14800
|
-
const normalizedPath =
|
|
15552
|
+
function buildUrl(zoneId, path41 = "/dns_records", query) {
|
|
15553
|
+
const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
|
|
14801
15554
|
const url = new URL(
|
|
14802
15555
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
14803
15556
|
);
|
|
@@ -15010,12 +15763,12 @@ async function waitForReady(client, vpsId) {
|
|
|
15010
15763
|
}
|
|
15011
15764
|
async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
15012
15765
|
const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
15013
|
-
const playbookDir =
|
|
15014
|
-
const playbookPath =
|
|
15015
|
-
const inventoryPath =
|
|
15016
|
-
const clientVarsPath =
|
|
15017
|
-
const varsDir =
|
|
15018
|
-
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))) {
|
|
15019
15772
|
throw new Error(`Invalid client name for vars path: ${clientName}`);
|
|
15020
15773
|
}
|
|
15021
15774
|
const args = [
|
|
@@ -15105,7 +15858,7 @@ function buildInventoryRecord(params) {
|
|
|
15105
15858
|
// src/mcp/tools/export-orchestration.ts
|
|
15106
15859
|
init_active_agent();
|
|
15107
15860
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
|
|
15108
|
-
import
|
|
15861
|
+
import path30 from "path";
|
|
15109
15862
|
import { z as z38 } from "zod";
|
|
15110
15863
|
|
|
15111
15864
|
// src/lib/orchestration-package.ts
|
|
@@ -15114,8 +15867,8 @@ init_identity();
|
|
|
15114
15867
|
init_platform_procedures();
|
|
15115
15868
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15116
15869
|
import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
|
|
15117
|
-
import
|
|
15118
|
-
import
|
|
15870
|
+
import os12 from "os";
|
|
15871
|
+
import path29 from "path";
|
|
15119
15872
|
var PACKAGE_VERSION = "1.0";
|
|
15120
15873
|
var ROSTER_FILENAME = "exe-employees.json";
|
|
15121
15874
|
var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
|
|
@@ -15181,10 +15934,10 @@ function validateProcedureEntry(value, index) {
|
|
|
15181
15934
|
};
|
|
15182
15935
|
}
|
|
15183
15936
|
function getRosterPath() {
|
|
15184
|
-
return
|
|
15937
|
+
return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
|
|
15185
15938
|
}
|
|
15186
15939
|
function getBackupPath() {
|
|
15187
|
-
return
|
|
15940
|
+
return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
|
|
15188
15941
|
}
|
|
15189
15942
|
function readRosterFile() {
|
|
15190
15943
|
const rosterPath = getRosterPath();
|
|
@@ -15201,7 +15954,7 @@ function writeRosterFile(roster) {
|
|
|
15201
15954
|
throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
|
|
15202
15955
|
}
|
|
15203
15956
|
const rosterPath = getRosterPath();
|
|
15204
|
-
mkdirSync12(
|
|
15957
|
+
mkdirSync12(path29.dirname(rosterPath), { recursive: true });
|
|
15205
15958
|
if (existsSync22(rosterPath)) {
|
|
15206
15959
|
const currentRoster = readRosterFile();
|
|
15207
15960
|
if (roster.length < currentRoster.length) {
|
|
@@ -15473,7 +16226,7 @@ function registerExportOrchestration(server2) {
|
|
|
15473
16226
|
try {
|
|
15474
16227
|
await initStore();
|
|
15475
16228
|
const pkg = await exportOrchestration(getActiveAgent().agentId);
|
|
15476
|
-
mkdirSync13(
|
|
16229
|
+
mkdirSync13(path30.dirname(output_path), { recursive: true });
|
|
15477
16230
|
writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
|
|
15478
16231
|
`, "utf-8");
|
|
15479
16232
|
return {
|
|
@@ -15674,17 +16427,17 @@ function registerQueryConversations(server2) {
|
|
|
15674
16427
|
// src/mcp/tools/load-skill.ts
|
|
15675
16428
|
import { z as z41 } from "zod";
|
|
15676
16429
|
import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
|
|
15677
|
-
import
|
|
16430
|
+
import path31 from "path";
|
|
15678
16431
|
import { homedir as homedir2 } from "os";
|
|
15679
|
-
var SKILLS_DIR =
|
|
16432
|
+
var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
|
|
15680
16433
|
function listAvailableSkills() {
|
|
15681
16434
|
try {
|
|
15682
16435
|
const entries = readdirSync9(SKILLS_DIR);
|
|
15683
16436
|
return entries.filter((entry) => {
|
|
15684
16437
|
try {
|
|
15685
|
-
const entryPath =
|
|
16438
|
+
const entryPath = path31.join(SKILLS_DIR, entry);
|
|
15686
16439
|
if (!statSync3(entryPath).isDirectory()) return false;
|
|
15687
|
-
const skillFile =
|
|
16440
|
+
const skillFile = path31.join(entryPath, "SKILL.md");
|
|
15688
16441
|
statSync3(skillFile);
|
|
15689
16442
|
return true;
|
|
15690
16443
|
} catch {
|
|
@@ -15727,8 +16480,8 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
15727
16480
|
}]
|
|
15728
16481
|
};
|
|
15729
16482
|
}
|
|
15730
|
-
const sanitized =
|
|
15731
|
-
const skillFile =
|
|
16483
|
+
const sanitized = path31.basename(skill_name);
|
|
16484
|
+
const skillFile = path31.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
15732
16485
|
try {
|
|
15733
16486
|
const content = readFileSync20(skillFile, "utf-8");
|
|
15734
16487
|
return {
|
|
@@ -16369,8 +17122,8 @@ init_database();
|
|
|
16369
17122
|
import { readdir } from "fs/promises";
|
|
16370
17123
|
import { createReadStream } from "fs";
|
|
16371
17124
|
import { createInterface } from "readline";
|
|
16372
|
-
import
|
|
16373
|
-
import
|
|
17125
|
+
import path32 from "path";
|
|
17126
|
+
import os13 from "os";
|
|
16374
17127
|
var MODEL_PRICING = {
|
|
16375
17128
|
// Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
|
|
16376
17129
|
"claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
|
|
@@ -16418,18 +17171,18 @@ async function getAgentSpend(period = "7d") {
|
|
|
16418
17171
|
for (const row of result.rows) {
|
|
16419
17172
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
16420
17173
|
}
|
|
16421
|
-
const claudeDir =
|
|
17174
|
+
const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
|
|
16422
17175
|
let projectDirs = [];
|
|
16423
17176
|
try {
|
|
16424
17177
|
const entries = await readdir(claudeDir);
|
|
16425
|
-
projectDirs = entries.map((e) =>
|
|
17178
|
+
projectDirs = entries.map((e) => path32.join(claudeDir, e));
|
|
16426
17179
|
} catch {
|
|
16427
17180
|
return [];
|
|
16428
17181
|
}
|
|
16429
17182
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
16430
17183
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
16431
17184
|
for (const dir of projectDirs) {
|
|
16432
|
-
const jsonlPath =
|
|
17185
|
+
const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
|
|
16433
17186
|
try {
|
|
16434
17187
|
const usage = await extractSessionUsage(jsonlPath);
|
|
16435
17188
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -17007,12 +17760,12 @@ function registerExportGraph(server2) {
|
|
|
17007
17760
|
}
|
|
17008
17761
|
const html = await exportGraphHTML(client);
|
|
17009
17762
|
const fs = await import("fs");
|
|
17010
|
-
const
|
|
17011
|
-
const
|
|
17012
|
-
const outDir =
|
|
17763
|
+
const path41 = await import("path");
|
|
17764
|
+
const os15 = await import("os");
|
|
17765
|
+
const outDir = path41.join(os15.homedir(), ".exe-os", "exports");
|
|
17013
17766
|
fs.mkdirSync(outDir, { recursive: true });
|
|
17014
17767
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
17015
|
-
const filePath =
|
|
17768
|
+
const filePath = path41.join(outDir, `graph-${timestamp}.html`);
|
|
17016
17769
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
17017
17770
|
return {
|
|
17018
17771
|
content: [
|
|
@@ -17234,9 +17987,9 @@ function registerListAgentSessions(server2) {
|
|
|
17234
17987
|
// src/mcp/tools/get-daemon-health.ts
|
|
17235
17988
|
import { z as z56 } from "zod";
|
|
17236
17989
|
import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
|
|
17237
|
-
import
|
|
17990
|
+
import path33 from "path";
|
|
17238
17991
|
import { homedir as homedir3 } from "os";
|
|
17239
|
-
var PID_PATH2 =
|
|
17992
|
+
var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
|
|
17240
17993
|
function formatUptime(seconds) {
|
|
17241
17994
|
const h = Math.floor(seconds / 3600);
|
|
17242
17995
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -17426,8 +18179,8 @@ init_worker_gate();
|
|
|
17426
18179
|
init_config();
|
|
17427
18180
|
import { z as z58 } from "zod";
|
|
17428
18181
|
import { readdirSync as readdirSync11, existsSync as existsSync26 } from "fs";
|
|
17429
|
-
import
|
|
17430
|
-
var WORKER_PID_DIR2 =
|
|
18182
|
+
import path35 from "path";
|
|
18183
|
+
var WORKER_PID_DIR2 = path35.join(EXE_AI_DIR, "worker-pids");
|
|
17431
18184
|
function countAliveWorkers() {
|
|
17432
18185
|
let alive = 0;
|
|
17433
18186
|
let stale = 0;
|
|
@@ -17503,7 +18256,7 @@ import { z as z59 } from "zod";
|
|
|
17503
18256
|
// src/bin/exe-doctor.ts
|
|
17504
18257
|
init_store();
|
|
17505
18258
|
init_database();
|
|
17506
|
-
import
|
|
18259
|
+
import os14 from "os";
|
|
17507
18260
|
|
|
17508
18261
|
// src/lib/is-main.ts
|
|
17509
18262
|
import { realpathSync } from "fs";
|
|
@@ -17523,7 +18276,7 @@ function isMainModule(importMetaUrl) {
|
|
|
17523
18276
|
// src/bin/exe-doctor.ts
|
|
17524
18277
|
import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
|
|
17525
18278
|
import { spawn as spawn2 } from "child_process";
|
|
17526
|
-
import
|
|
18279
|
+
import path36 from "path";
|
|
17527
18280
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
17528
18281
|
|
|
17529
18282
|
// src/lib/conflict-detector.ts
|
|
@@ -17926,7 +18679,7 @@ async function auditOrphanedProjects(client) {
|
|
|
17926
18679
|
for (const row of result.rows) {
|
|
17927
18680
|
const name = row.project_name;
|
|
17928
18681
|
const count = Number(row.cnt);
|
|
17929
|
-
const exists = existsSync27(
|
|
18682
|
+
const exists = existsSync27(path36.join(home, name)) || existsSync27(path36.join(home, "..", name)) || existsSync27(path36.join(process.cwd(), "..", name));
|
|
17930
18683
|
if (!exists) {
|
|
17931
18684
|
orphans.push({ project_name: name, count });
|
|
17932
18685
|
}
|
|
@@ -17934,7 +18687,7 @@ async function auditOrphanedProjects(client) {
|
|
|
17934
18687
|
return orphans;
|
|
17935
18688
|
}
|
|
17936
18689
|
function auditHookHealth() {
|
|
17937
|
-
const logPath =
|
|
18690
|
+
const logPath = path36.join(
|
|
17938
18691
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
17939
18692
|
".exe-os",
|
|
17940
18693
|
"logs",
|
|
@@ -18020,7 +18773,7 @@ function formatReport(report, flags) {
|
|
|
18020
18773
|
}
|
|
18021
18774
|
lines.push("");
|
|
18022
18775
|
}
|
|
18023
|
-
const totalMemGB =
|
|
18776
|
+
const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
|
|
18024
18777
|
const isLowMemSystem = totalMemGB <= 8;
|
|
18025
18778
|
if (isLowMemSystem && report.nullVectors > 0) {
|
|
18026
18779
|
lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
|
|
@@ -18142,7 +18895,7 @@ async function fixNullVectors() {
|
|
|
18142
18895
|
}
|
|
18143
18896
|
}
|
|
18144
18897
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
18145
|
-
const backfillPath =
|
|
18898
|
+
const backfillPath = path36.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
18146
18899
|
return new Promise((resolve, reject) => {
|
|
18147
18900
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
18148
18901
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -18342,7 +19095,7 @@ import { z as z60 } from "zod";
|
|
|
18342
19095
|
init_database();
|
|
18343
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";
|
|
18344
19097
|
import crypto16 from "crypto";
|
|
18345
|
-
import
|
|
19098
|
+
import path38 from "path";
|
|
18346
19099
|
import { homedir as homedir6 } from "os";
|
|
18347
19100
|
|
|
18348
19101
|
// src/lib/crypto.ts
|
|
@@ -18416,7 +19169,7 @@ function sqlSafe(v) {
|
|
|
18416
19169
|
}
|
|
18417
19170
|
function logError(msg) {
|
|
18418
19171
|
try {
|
|
18419
|
-
const logPath =
|
|
19172
|
+
const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
|
|
18420
19173
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
18421
19174
|
`);
|
|
18422
19175
|
} catch {
|
|
@@ -18425,7 +19178,7 @@ function logError(msg) {
|
|
|
18425
19178
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
18426
19179
|
var FETCH_TIMEOUT_MS4 = 3e4;
|
|
18427
19180
|
var PUSH_BATCH_SIZE = 5e3;
|
|
18428
|
-
var ROSTER_LOCK_PATH =
|
|
19181
|
+
var ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
|
|
18429
19182
|
var LOCK_STALE_MS = 3e4;
|
|
18430
19183
|
async function withRosterLock(fn) {
|
|
18431
19184
|
try {
|
|
@@ -18816,7 +19569,7 @@ async function cloudSync(config2) {
|
|
|
18816
19569
|
try {
|
|
18817
19570
|
const employees = await loadEmployees();
|
|
18818
19571
|
rosterResult.employees = employees.length;
|
|
18819
|
-
const idDir =
|
|
19572
|
+
const idDir = path38.join(EXE_AI_DIR, "identity");
|
|
18820
19573
|
if (existsSync29(idDir)) {
|
|
18821
19574
|
rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
|
|
18822
19575
|
}
|
|
@@ -18835,7 +19588,7 @@ async function cloudSync(config2) {
|
|
|
18835
19588
|
roster: rosterResult
|
|
18836
19589
|
};
|
|
18837
19590
|
}
|
|
18838
|
-
var ROSTER_DELETIONS_PATH =
|
|
19591
|
+
var ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
|
|
18839
19592
|
function consumeRosterDeletions() {
|
|
18840
19593
|
try {
|
|
18841
19594
|
if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
|
|
@@ -18847,9 +19600,9 @@ function consumeRosterDeletions() {
|
|
|
18847
19600
|
}
|
|
18848
19601
|
}
|
|
18849
19602
|
function buildRosterBlob(paths) {
|
|
18850
|
-
const rosterPath = paths?.rosterPath ??
|
|
18851
|
-
const identityDir = paths?.identityDir ??
|
|
18852
|
-
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");
|
|
18853
19606
|
let roster = [];
|
|
18854
19607
|
if (existsSync29(rosterPath)) {
|
|
18855
19608
|
try {
|
|
@@ -18861,7 +19614,7 @@ function buildRosterBlob(paths) {
|
|
|
18861
19614
|
if (existsSync29(identityDir)) {
|
|
18862
19615
|
for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
18863
19616
|
try {
|
|
18864
|
-
identities[file] = readFileSync25(
|
|
19617
|
+
identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
|
|
18865
19618
|
} catch {
|
|
18866
19619
|
}
|
|
18867
19620
|
}
|
|
@@ -18874,7 +19627,7 @@ function buildRosterBlob(paths) {
|
|
|
18874
19627
|
}
|
|
18875
19628
|
}
|
|
18876
19629
|
let agentConfig;
|
|
18877
|
-
const agentConfigPath =
|
|
19630
|
+
const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
|
|
18878
19631
|
if (existsSync29(agentConfigPath)) {
|
|
18879
19632
|
try {
|
|
18880
19633
|
agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
|
|
@@ -18953,7 +19706,7 @@ async function cloudPullRoster(config2) {
|
|
|
18953
19706
|
}
|
|
18954
19707
|
}
|
|
18955
19708
|
function mergeConfig(remoteConfig, configPath) {
|
|
18956
|
-
const cfgPath = configPath ??
|
|
19709
|
+
const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
|
|
18957
19710
|
let local = {};
|
|
18958
19711
|
if (existsSync29(cfgPath)) {
|
|
18959
19712
|
try {
|
|
@@ -18962,14 +19715,14 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
18962
19715
|
}
|
|
18963
19716
|
}
|
|
18964
19717
|
const merged = { ...remoteConfig, ...local };
|
|
18965
|
-
const dir =
|
|
19718
|
+
const dir = path38.dirname(cfgPath);
|
|
18966
19719
|
if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
|
|
18967
19720
|
writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
18968
19721
|
}
|
|
18969
19722
|
async function mergeRosterFromRemote(remote, paths) {
|
|
18970
19723
|
return withRosterLock(async () => {
|
|
18971
19724
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
18972
|
-
const identityDir = paths?.identityDir ??
|
|
19725
|
+
const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
|
|
18973
19726
|
const localEmployees = await loadEmployees(rosterPath);
|
|
18974
19727
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
18975
19728
|
let added = 0;
|
|
@@ -18991,7 +19744,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
18991
19744
|
const remoteIdentity = remote.identities[matchedKey];
|
|
18992
19745
|
if (remoteIdentity) {
|
|
18993
19746
|
if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
|
|
18994
|
-
const idPath =
|
|
19747
|
+
const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
|
|
18995
19748
|
let localIdentity = null;
|
|
18996
19749
|
try {
|
|
18997
19750
|
localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
|
|
@@ -19024,7 +19777,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
19024
19777
|
}
|
|
19025
19778
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
19026
19779
|
try {
|
|
19027
|
-
const agentConfigPath =
|
|
19780
|
+
const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
|
|
19028
19781
|
let local = {};
|
|
19029
19782
|
if (existsSync29(agentConfigPath)) {
|
|
19030
19783
|
try {
|
|
@@ -19753,15 +20506,515 @@ function registerGetLicenseStatus(server2) {
|
|
|
19753
20506
|
);
|
|
19754
20507
|
}
|
|
19755
20508
|
|
|
19756
|
-
// src/mcp/tools/
|
|
20509
|
+
// src/mcp/tools/backup-vps.ts
|
|
20510
|
+
init_keychain();
|
|
19757
20511
|
import { z as z64 } from "zod";
|
|
19758
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
|
+
|
|
19759
21012
|
// src/lib/people.ts
|
|
19760
21013
|
init_config();
|
|
19761
21014
|
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
19762
21015
|
import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
|
|
19763
|
-
import
|
|
19764
|
-
var PEOPLE_PATH =
|
|
21016
|
+
import path39 from "path";
|
|
21017
|
+
var PEOPLE_PATH = path39.join(EXE_AI_DIR, "people.json");
|
|
19765
21018
|
async function loadPeople() {
|
|
19766
21019
|
if (!existsSync30(PEOPLE_PATH)) return [];
|
|
19767
21020
|
try {
|
|
@@ -19772,7 +21025,7 @@ async function loadPeople() {
|
|
|
19772
21025
|
}
|
|
19773
21026
|
}
|
|
19774
21027
|
async function savePeople(people) {
|
|
19775
|
-
await mkdir5(
|
|
21028
|
+
await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
|
|
19776
21029
|
await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
|
|
19777
21030
|
}
|
|
19778
21031
|
async function addPerson(person) {
|
|
@@ -19802,10 +21055,10 @@ function registerAddPerson(server2) {
|
|
|
19802
21055
|
title: "Add Person",
|
|
19803
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.",
|
|
19804
21057
|
inputSchema: {
|
|
19805
|
-
name:
|
|
19806
|
-
role:
|
|
19807
|
-
relationship:
|
|
19808
|
-
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")
|
|
19809
21062
|
}
|
|
19810
21063
|
},
|
|
19811
21064
|
async ({ name, role, relationship, notes }) => {
|
|
@@ -19851,7 +21104,7 @@ function registerGetPerson(server2) {
|
|
|
19851
21104
|
title: "Get Person",
|
|
19852
21105
|
description: "Look up a specific person by name from the people roster.",
|
|
19853
21106
|
inputSchema: {
|
|
19854
|
-
name:
|
|
21107
|
+
name: z65.string().describe("Person's name to look up")
|
|
19855
21108
|
}
|
|
19856
21109
|
},
|
|
19857
21110
|
async ({ name }) => {
|
|
@@ -19879,7 +21132,7 @@ init_active_agent();
|
|
|
19879
21132
|
init_agent_config();
|
|
19880
21133
|
init_runtime_table();
|
|
19881
21134
|
init_employees();
|
|
19882
|
-
import { z as
|
|
21135
|
+
import { z as z66 } from "zod";
|
|
19883
21136
|
function registerSetAgentConfig(server2) {
|
|
19884
21137
|
server2.registerTool(
|
|
19885
21138
|
"set_agent_config",
|
|
@@ -19887,14 +21140,22 @@ function registerSetAgentConfig(server2) {
|
|
|
19887
21140
|
title: "Set Agent Config",
|
|
19888
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.",
|
|
19889
21142
|
inputSchema: {
|
|
19890
|
-
agent_id:
|
|
19891
|
-
runtime:
|
|
19892
|
-
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)")
|
|
19893
21146
|
}
|
|
19894
21147
|
},
|
|
19895
21148
|
async ({ agent_id, runtime, model }) => {
|
|
19896
|
-
const { agentRole } = getActiveAgent();
|
|
19897
|
-
|
|
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) {
|
|
19898
21159
|
return {
|
|
19899
21160
|
content: [
|
|
19900
21161
|
{
|
|
@@ -19979,6 +21240,72 @@ function registerSetAgentConfig(server2) {
|
|
|
19979
21240
|
);
|
|
19980
21241
|
}
|
|
19981
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
|
+
|
|
19982
21309
|
// src/lib/telemetry.ts
|
|
19983
21310
|
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
19984
21311
|
var initialized = false;
|
|
@@ -20106,6 +21433,7 @@ registerGetAutoWakeStatus(server);
|
|
|
20106
21433
|
registerGetWorkerGate(server);
|
|
20107
21434
|
registerRunMemoryAudit(server);
|
|
20108
21435
|
registerCloudSync(server);
|
|
21436
|
+
registerBackupVps(server);
|
|
20109
21437
|
registerGetMemoryCardinality(server);
|
|
20110
21438
|
registerRunConsolidation(server);
|
|
20111
21439
|
registerGetLicenseStatus(server);
|
|
@@ -20113,12 +21441,19 @@ registerAddPerson(server);
|
|
|
20113
21441
|
registerListPeople(server);
|
|
20114
21442
|
registerGetPerson(server);
|
|
20115
21443
|
registerSetAgentConfig(server);
|
|
21444
|
+
registerListEmployees(server);
|
|
20116
21445
|
try {
|
|
20117
21446
|
await initStore();
|
|
20118
21447
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
20119
21448
|
const transport = new StdioServerTransport();
|
|
20120
21449
|
await server.connect(transport);
|
|
20121
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
|
+
});
|
|
20122
21457
|
void (async () => {
|
|
20123
21458
|
try {
|
|
20124
21459
|
const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
@@ -20175,20 +21510,20 @@ try {
|
|
|
20175
21510
|
`
|
|
20176
21511
|
);
|
|
20177
21512
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
20178
|
-
const backfillPath =
|
|
20179
|
-
|
|
21513
|
+
const backfillPath = path40.resolve(
|
|
21514
|
+
path40.dirname(thisFile),
|
|
20180
21515
|
"../bin/backfill-vectors.js"
|
|
20181
21516
|
);
|
|
20182
21517
|
if (existsSync31(backfillPath)) {
|
|
20183
21518
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
20184
|
-
const logPath =
|
|
20185
|
-
mkdirSync17(
|
|
21519
|
+
const logPath = path40.join(exeDir, "workers.log");
|
|
21520
|
+
mkdirSync17(path40.dirname(logPath), { recursive: true });
|
|
20186
21521
|
let logFd = "ignore";
|
|
20187
21522
|
try {
|
|
20188
21523
|
logFd = openSync3(logPath, "a");
|
|
20189
21524
|
} catch {
|
|
20190
21525
|
}
|
|
20191
|
-
const child =
|
|
21526
|
+
const child = spawn4(process.execPath, [backfillPath], {
|
|
20192
21527
|
detached: true,
|
|
20193
21528
|
stdio: ["ignore", "ignore", logFd]
|
|
20194
21529
|
});
|