@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -632,6 +632,118 @@ var init_config = __esm({
|
|
|
632
632
|
}
|
|
633
633
|
});
|
|
634
634
|
|
|
635
|
+
// src/lib/runtime-table.ts
|
|
636
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
637
|
+
var init_runtime_table = __esm({
|
|
638
|
+
"src/lib/runtime-table.ts"() {
|
|
639
|
+
"use strict";
|
|
640
|
+
RUNTIME_TABLE = {
|
|
641
|
+
codex: {
|
|
642
|
+
binary: "codex",
|
|
643
|
+
launchMode: "interactive",
|
|
644
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
645
|
+
inlineFlag: "--no-alt-screen",
|
|
646
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
647
|
+
defaultModel: "gpt-5.4"
|
|
648
|
+
},
|
|
649
|
+
opencode: {
|
|
650
|
+
binary: "opencode",
|
|
651
|
+
launchMode: "exec",
|
|
652
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
653
|
+
inlineFlag: "",
|
|
654
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
655
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
DEFAULT_RUNTIME = "claude";
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// src/lib/agent-config.ts
|
|
663
|
+
var agent_config_exports = {};
|
|
664
|
+
__export(agent_config_exports, {
|
|
665
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
666
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
667
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
668
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
669
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
670
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
671
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
672
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
673
|
+
setAgentRuntime: () => setAgentRuntime
|
|
674
|
+
});
|
|
675
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
676
|
+
import path2 from "path";
|
|
677
|
+
function loadAgentConfig() {
|
|
678
|
+
if (!existsSync2(AGENT_CONFIG_PATH)) return {};
|
|
679
|
+
try {
|
|
680
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
681
|
+
} catch {
|
|
682
|
+
return {};
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function saveAgentConfig(config2) {
|
|
686
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
687
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
688
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
689
|
+
}
|
|
690
|
+
function getAgentRuntime(agentId) {
|
|
691
|
+
const config2 = loadAgentConfig();
|
|
692
|
+
const entry = config2[agentId];
|
|
693
|
+
if (entry) return entry;
|
|
694
|
+
const orgDefault = config2["default"];
|
|
695
|
+
if (orgDefault) return orgDefault;
|
|
696
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
697
|
+
}
|
|
698
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
699
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
700
|
+
if (!knownModels) {
|
|
701
|
+
return {
|
|
702
|
+
ok: false,
|
|
703
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
if (!knownModels.includes(model)) {
|
|
707
|
+
return {
|
|
708
|
+
ok: false,
|
|
709
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
const config2 = loadAgentConfig();
|
|
713
|
+
config2[agentId] = { runtime, model };
|
|
714
|
+
saveAgentConfig(config2);
|
|
715
|
+
return { ok: true };
|
|
716
|
+
}
|
|
717
|
+
function clearAgentRuntime(agentId) {
|
|
718
|
+
const config2 = loadAgentConfig();
|
|
719
|
+
delete config2[agentId];
|
|
720
|
+
saveAgentConfig(config2);
|
|
721
|
+
}
|
|
722
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
723
|
+
var init_agent_config = __esm({
|
|
724
|
+
"src/lib/agent-config.ts"() {
|
|
725
|
+
"use strict";
|
|
726
|
+
init_config();
|
|
727
|
+
init_runtime_table();
|
|
728
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
729
|
+
KNOWN_RUNTIMES = {
|
|
730
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
731
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
732
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
733
|
+
};
|
|
734
|
+
RUNTIME_LABELS = {
|
|
735
|
+
claude: "Claude Code (Anthropic)",
|
|
736
|
+
codex: "Codex (OpenAI)",
|
|
737
|
+
opencode: "OpenCode (open source)"
|
|
738
|
+
};
|
|
739
|
+
DEFAULT_MODELS = {
|
|
740
|
+
claude: "claude-opus-4",
|
|
741
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
742
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
635
747
|
// src/lib/employees.ts
|
|
636
748
|
var employees_exports = {};
|
|
637
749
|
__export(employees_exports, {
|
|
@@ -647,6 +759,7 @@ __export(employees_exports, {
|
|
|
647
759
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
648
760
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
649
761
|
hasRole: () => hasRole,
|
|
762
|
+
hireEmployee: () => hireEmployee,
|
|
650
763
|
isCoordinatorName: () => isCoordinatorName,
|
|
651
764
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
652
765
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -659,9 +772,9 @@ __export(employees_exports, {
|
|
|
659
772
|
validateEmployeeName: () => validateEmployeeName
|
|
660
773
|
});
|
|
661
774
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
662
|
-
import { existsSync as
|
|
775
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
663
776
|
import { execSync } from "child_process";
|
|
664
|
-
import
|
|
777
|
+
import path3 from "path";
|
|
665
778
|
import os2 from "os";
|
|
666
779
|
function normalizeRole(role) {
|
|
667
780
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -698,7 +811,7 @@ function validateEmployeeName(name) {
|
|
|
698
811
|
return { valid: true };
|
|
699
812
|
}
|
|
700
813
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
701
|
-
if (!
|
|
814
|
+
if (!existsSync3(employeesPath)) {
|
|
702
815
|
return [];
|
|
703
816
|
}
|
|
704
817
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -709,13 +822,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
709
822
|
}
|
|
710
823
|
}
|
|
711
824
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
712
|
-
await mkdir2(
|
|
825
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
713
826
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
714
827
|
}
|
|
715
828
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
716
|
-
if (!
|
|
829
|
+
if (!existsSync3(employeesPath)) return [];
|
|
717
830
|
try {
|
|
718
|
-
return JSON.parse(
|
|
831
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
719
832
|
} catch {
|
|
720
833
|
return [];
|
|
721
834
|
}
|
|
@@ -757,6 +870,52 @@ function addEmployee(employees, employee) {
|
|
|
757
870
|
}
|
|
758
871
|
return [...employees, normalized];
|
|
759
872
|
}
|
|
873
|
+
function appendToCoordinatorTeam(employee) {
|
|
874
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
875
|
+
if (!coordinator) return;
|
|
876
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
877
|
+
if (!existsSync3(idPath)) return;
|
|
878
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
879
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
880
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
881
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
882
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
883
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
884
|
+
const entry = `
|
|
885
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
886
|
+
`;
|
|
887
|
+
let updated;
|
|
888
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
889
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
890
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
891
|
+
} else {
|
|
892
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
893
|
+
}
|
|
894
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
895
|
+
}
|
|
896
|
+
function capitalize(s) {
|
|
897
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
898
|
+
}
|
|
899
|
+
async function hireEmployee(employee) {
|
|
900
|
+
const employees = await loadEmployees();
|
|
901
|
+
const updated = addEmployee(employees, employee);
|
|
902
|
+
await saveEmployees(updated);
|
|
903
|
+
try {
|
|
904
|
+
appendToCoordinatorTeam(employee);
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
try {
|
|
908
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
909
|
+
const config2 = loadAgentConfig2();
|
|
910
|
+
const name = employee.name.toLowerCase();
|
|
911
|
+
if (!config2[name] && config2["default"]) {
|
|
912
|
+
config2[name] = { ...config2["default"] };
|
|
913
|
+
saveAgentConfig2(config2);
|
|
914
|
+
}
|
|
915
|
+
} catch {
|
|
916
|
+
}
|
|
917
|
+
return updated;
|
|
918
|
+
}
|
|
760
919
|
async function normalizeRosterCase(rosterPath) {
|
|
761
920
|
const employees = await loadEmployees(rosterPath);
|
|
762
921
|
let changed = false;
|
|
@@ -766,14 +925,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
766
925
|
emp.name = emp.name.toLowerCase();
|
|
767
926
|
changed = true;
|
|
768
927
|
try {
|
|
769
|
-
const identityDir =
|
|
770
|
-
const oldPath =
|
|
771
|
-
const newPath =
|
|
772
|
-
if (
|
|
928
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
929
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
930
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
931
|
+
if (existsSync3(oldPath) && !existsSync3(newPath)) {
|
|
773
932
|
renameSync2(oldPath, newPath);
|
|
774
|
-
} else if (
|
|
775
|
-
const content =
|
|
776
|
-
|
|
933
|
+
} else if (existsSync3(oldPath) && oldPath !== newPath) {
|
|
934
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
935
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
777
936
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
778
937
|
unlinkSync(oldPath);
|
|
779
938
|
}
|
|
@@ -803,7 +962,7 @@ function registerBinSymlinks(name) {
|
|
|
803
962
|
errors.push("Could not find 'exe-os' in PATH");
|
|
804
963
|
return { created, skipped, errors };
|
|
805
964
|
}
|
|
806
|
-
const binDir =
|
|
965
|
+
const binDir = path3.dirname(exeBinPath);
|
|
807
966
|
let target;
|
|
808
967
|
try {
|
|
809
968
|
target = readlinkSync(exeBinPath);
|
|
@@ -813,8 +972,8 @@ function registerBinSymlinks(name) {
|
|
|
813
972
|
}
|
|
814
973
|
for (const suffix of ["", "-opencode"]) {
|
|
815
974
|
const linkName = `${name}${suffix}`;
|
|
816
|
-
const linkPath =
|
|
817
|
-
if (
|
|
975
|
+
const linkPath = path3.join(binDir, linkName);
|
|
976
|
+
if (existsSync3(linkPath)) {
|
|
818
977
|
skipped.push(linkName);
|
|
819
978
|
continue;
|
|
820
979
|
}
|
|
@@ -827,21 +986,619 @@ function registerBinSymlinks(name) {
|
|
|
827
986
|
}
|
|
828
987
|
return { created, skipped, errors };
|
|
829
988
|
}
|
|
830
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
989
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
831
990
|
var init_employees = __esm({
|
|
832
991
|
"src/lib/employees.ts"() {
|
|
833
992
|
"use strict";
|
|
834
993
|
init_config();
|
|
835
|
-
EMPLOYEES_PATH =
|
|
994
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
836
995
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
837
996
|
COORDINATOR_ROLE = "COO";
|
|
838
997
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
998
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
999
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
// src/lib/database-adapter.ts
|
|
1004
|
+
import os3 from "os";
|
|
1005
|
+
import path4 from "path";
|
|
1006
|
+
import { createRequire } from "module";
|
|
1007
|
+
import { pathToFileURL } from "url";
|
|
1008
|
+
function quotedIdentifier(identifier) {
|
|
1009
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1010
|
+
}
|
|
1011
|
+
function unqualifiedTableName(name) {
|
|
1012
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1013
|
+
const parts = raw.split(".");
|
|
1014
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1015
|
+
}
|
|
1016
|
+
function stripTrailingSemicolon(sql) {
|
|
1017
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1018
|
+
}
|
|
1019
|
+
function appendClause(sql, clause) {
|
|
1020
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1021
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1022
|
+
if (!returningMatch) {
|
|
1023
|
+
return `${trimmed}${clause}`;
|
|
1024
|
+
}
|
|
1025
|
+
const idx = returningMatch.index;
|
|
1026
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1027
|
+
}
|
|
1028
|
+
function normalizeStatement(stmt) {
|
|
1029
|
+
if (typeof stmt === "string") {
|
|
1030
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1031
|
+
}
|
|
1032
|
+
const sql = stmt.sql;
|
|
1033
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1034
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1035
|
+
}
|
|
1036
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1037
|
+
}
|
|
1038
|
+
function rewriteBooleanLiterals(sql) {
|
|
1039
|
+
let out = sql;
|
|
1040
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1041
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1042
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1043
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1044
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1045
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1046
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1047
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1048
|
+
}
|
|
1049
|
+
return out;
|
|
1050
|
+
}
|
|
1051
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1052
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1053
|
+
return sql;
|
|
1054
|
+
}
|
|
1055
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1056
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1057
|
+
}
|
|
1058
|
+
function rewriteInsertOrReplace(sql) {
|
|
1059
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1060
|
+
if (!match) {
|
|
1061
|
+
return sql;
|
|
1062
|
+
}
|
|
1063
|
+
const rawTable = match[1];
|
|
1064
|
+
const rawColumns = match[2];
|
|
1065
|
+
const remainder = match[3];
|
|
1066
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1067
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1068
|
+
if (!conflictKeys?.length) {
|
|
1069
|
+
return sql;
|
|
1070
|
+
}
|
|
1071
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1072
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1073
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1074
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1075
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1076
|
+
}
|
|
1077
|
+
function rewriteSql(sql) {
|
|
1078
|
+
let out = sql;
|
|
1079
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1080
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1081
|
+
out = rewriteBooleanLiterals(out);
|
|
1082
|
+
out = rewriteInsertOrReplace(out);
|
|
1083
|
+
out = rewriteInsertOrIgnore(out);
|
|
1084
|
+
return stripTrailingSemicolon(out);
|
|
1085
|
+
}
|
|
1086
|
+
function toBoolean(value) {
|
|
1087
|
+
if (value === null || value === void 0) return value;
|
|
1088
|
+
if (typeof value === "boolean") return value;
|
|
1089
|
+
if (typeof value === "number") return value !== 0;
|
|
1090
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1091
|
+
if (typeof value === "string") {
|
|
1092
|
+
const normalized = value.trim().toLowerCase();
|
|
1093
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1094
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1095
|
+
}
|
|
1096
|
+
return Boolean(value);
|
|
1097
|
+
}
|
|
1098
|
+
function countQuestionMarks(sql, end) {
|
|
1099
|
+
let count = 0;
|
|
1100
|
+
let inSingle = false;
|
|
1101
|
+
let inDouble = false;
|
|
1102
|
+
let inLineComment = false;
|
|
1103
|
+
let inBlockComment = false;
|
|
1104
|
+
for (let i = 0; i < end; i++) {
|
|
1105
|
+
const ch = sql[i];
|
|
1106
|
+
const next = sql[i + 1];
|
|
1107
|
+
if (inLineComment) {
|
|
1108
|
+
if (ch === "\n") inLineComment = false;
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (inBlockComment) {
|
|
1112
|
+
if (ch === "*" && next === "/") {
|
|
1113
|
+
inBlockComment = false;
|
|
1114
|
+
i += 1;
|
|
1115
|
+
}
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1119
|
+
inLineComment = true;
|
|
1120
|
+
i += 1;
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1124
|
+
inBlockComment = true;
|
|
1125
|
+
i += 1;
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1129
|
+
inSingle = !inSingle;
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1133
|
+
inDouble = !inDouble;
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1137
|
+
count += 1;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return count;
|
|
1141
|
+
}
|
|
1142
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1143
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1144
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1145
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1146
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1147
|
+
const matchText = match[0];
|
|
1148
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1149
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return indexes;
|
|
1153
|
+
}
|
|
1154
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1155
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1156
|
+
if (!match) return;
|
|
1157
|
+
const rawTable = match[1];
|
|
1158
|
+
const rawColumns = match[2];
|
|
1159
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1160
|
+
if (!boolColumns?.size) return;
|
|
1161
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1162
|
+
for (const [index, column] of columns.entries()) {
|
|
1163
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1164
|
+
args[index] = toBoolean(args[index]);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1169
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1170
|
+
if (!match) return;
|
|
1171
|
+
const rawTable = match[1];
|
|
1172
|
+
const setClause = match[2];
|
|
1173
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1174
|
+
if (!boolColumns?.size) return;
|
|
1175
|
+
const assignments = setClause.split(",");
|
|
1176
|
+
let placeholderIndex = 0;
|
|
1177
|
+
for (const assignment of assignments) {
|
|
1178
|
+
if (!assignment.includes("?")) continue;
|
|
1179
|
+
placeholderIndex += 1;
|
|
1180
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1181
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1182
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function coerceBooleanArgs(sql, args) {
|
|
1187
|
+
const nextArgs = [...args];
|
|
1188
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1189
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1190
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1191
|
+
for (const index of placeholderIndexes) {
|
|
1192
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1193
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
return nextArgs;
|
|
1197
|
+
}
|
|
1198
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1199
|
+
let out = "";
|
|
1200
|
+
let placeholder = 0;
|
|
1201
|
+
let inSingle = false;
|
|
1202
|
+
let inDouble = false;
|
|
1203
|
+
let inLineComment = false;
|
|
1204
|
+
let inBlockComment = false;
|
|
1205
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1206
|
+
const ch = sql[i];
|
|
1207
|
+
const next = sql[i + 1];
|
|
1208
|
+
if (inLineComment) {
|
|
1209
|
+
out += ch;
|
|
1210
|
+
if (ch === "\n") inLineComment = false;
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
if (inBlockComment) {
|
|
1214
|
+
out += ch;
|
|
1215
|
+
if (ch === "*" && next === "/") {
|
|
1216
|
+
out += next;
|
|
1217
|
+
inBlockComment = false;
|
|
1218
|
+
i += 1;
|
|
1219
|
+
}
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1222
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1223
|
+
out += ch + next;
|
|
1224
|
+
inLineComment = true;
|
|
1225
|
+
i += 1;
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1229
|
+
out += ch + next;
|
|
1230
|
+
inBlockComment = true;
|
|
1231
|
+
i += 1;
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1235
|
+
inSingle = !inSingle;
|
|
1236
|
+
out += ch;
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1240
|
+
inDouble = !inDouble;
|
|
1241
|
+
out += ch;
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1245
|
+
placeholder += 1;
|
|
1246
|
+
out += `$${placeholder}`;
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
out += ch;
|
|
1250
|
+
}
|
|
1251
|
+
return out;
|
|
1252
|
+
}
|
|
1253
|
+
function translateStatementForPostgres(stmt) {
|
|
1254
|
+
const normalized = normalizeStatement(stmt);
|
|
1255
|
+
if (normalized.kind === "named") {
|
|
1256
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1257
|
+
}
|
|
1258
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1259
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1260
|
+
return {
|
|
1261
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1262
|
+
args: coercedArgs
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function shouldBypassPostgres(stmt) {
|
|
1266
|
+
const normalized = normalizeStatement(stmt);
|
|
1267
|
+
if (normalized.kind === "named") {
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1271
|
+
}
|
|
1272
|
+
function shouldFallbackOnError(error) {
|
|
1273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1274
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1275
|
+
}
|
|
1276
|
+
function isReadQuery(sql) {
|
|
1277
|
+
const trimmed = sql.trimStart();
|
|
1278
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1279
|
+
}
|
|
1280
|
+
function buildRow(row, columns) {
|
|
1281
|
+
const values = columns.map((column) => row[column]);
|
|
1282
|
+
return Object.assign(values, row);
|
|
1283
|
+
}
|
|
1284
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1285
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1286
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1287
|
+
return {
|
|
1288
|
+
columns,
|
|
1289
|
+
columnTypes: columns.map(() => ""),
|
|
1290
|
+
rows: resultRows,
|
|
1291
|
+
rowsAffected,
|
|
1292
|
+
lastInsertRowid: void 0,
|
|
1293
|
+
toJSON() {
|
|
1294
|
+
return {
|
|
1295
|
+
columns,
|
|
1296
|
+
columnTypes: columns.map(() => ""),
|
|
1297
|
+
rows,
|
|
1298
|
+
rowsAffected,
|
|
1299
|
+
lastInsertRowid: void 0
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
async function loadPrismaClient() {
|
|
1305
|
+
if (!prismaClientPromise) {
|
|
1306
|
+
prismaClientPromise = (async () => {
|
|
1307
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1308
|
+
if (explicitPath) {
|
|
1309
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1310
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1311
|
+
if (!PrismaClient2) {
|
|
1312
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1313
|
+
}
|
|
1314
|
+
return new PrismaClient2();
|
|
1315
|
+
}
|
|
1316
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1317
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
1318
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1319
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1320
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1321
|
+
if (!PrismaClient) {
|
|
1322
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1323
|
+
}
|
|
1324
|
+
return new PrismaClient();
|
|
1325
|
+
})();
|
|
1326
|
+
}
|
|
1327
|
+
return prismaClientPromise;
|
|
1328
|
+
}
|
|
1329
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1330
|
+
if (!compatibilityBootstrapPromise) {
|
|
1331
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1332
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1333
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1334
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1335
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1336
|
+
relation
|
|
1337
|
+
);
|
|
1338
|
+
if (!rows[0]?.regclass) {
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
await prisma.$executeRawUnsafe(
|
|
1342
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
})();
|
|
1346
|
+
}
|
|
1347
|
+
return compatibilityBootstrapPromise;
|
|
1348
|
+
}
|
|
1349
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1350
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1351
|
+
if (isReadQuery(translated.sql)) {
|
|
1352
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1353
|
+
translated.sql,
|
|
1354
|
+
...translated.args
|
|
1355
|
+
);
|
|
1356
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1357
|
+
}
|
|
1358
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1359
|
+
return buildResultSet([], rowsAffected);
|
|
1360
|
+
}
|
|
1361
|
+
function splitSqlStatements(sql) {
|
|
1362
|
+
const parts = [];
|
|
1363
|
+
let current = "";
|
|
1364
|
+
let inSingle = false;
|
|
1365
|
+
let inDouble = false;
|
|
1366
|
+
let inLineComment = false;
|
|
1367
|
+
let inBlockComment = false;
|
|
1368
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1369
|
+
const ch = sql[i];
|
|
1370
|
+
const next = sql[i + 1];
|
|
1371
|
+
if (inLineComment) {
|
|
1372
|
+
current += ch;
|
|
1373
|
+
if (ch === "\n") inLineComment = false;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
if (inBlockComment) {
|
|
1377
|
+
current += ch;
|
|
1378
|
+
if (ch === "*" && next === "/") {
|
|
1379
|
+
current += next;
|
|
1380
|
+
inBlockComment = false;
|
|
1381
|
+
i += 1;
|
|
1382
|
+
}
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1386
|
+
current += ch + next;
|
|
1387
|
+
inLineComment = true;
|
|
1388
|
+
i += 1;
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1392
|
+
current += ch + next;
|
|
1393
|
+
inBlockComment = true;
|
|
1394
|
+
i += 1;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1398
|
+
inSingle = !inSingle;
|
|
1399
|
+
current += ch;
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1403
|
+
inDouble = !inDouble;
|
|
1404
|
+
current += ch;
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1408
|
+
if (current.trim()) {
|
|
1409
|
+
parts.push(current.trim());
|
|
1410
|
+
}
|
|
1411
|
+
current = "";
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
current += ch;
|
|
1415
|
+
}
|
|
1416
|
+
if (current.trim()) {
|
|
1417
|
+
parts.push(current.trim());
|
|
1418
|
+
}
|
|
1419
|
+
return parts;
|
|
1420
|
+
}
|
|
1421
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1422
|
+
const prisma = await loadPrismaClient();
|
|
1423
|
+
await ensureCompatibilityViews(prisma);
|
|
1424
|
+
let closed = false;
|
|
1425
|
+
let adapter;
|
|
1426
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1427
|
+
if (!fallbackClient) {
|
|
1428
|
+
if (error) throw error;
|
|
1429
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1430
|
+
}
|
|
1431
|
+
if (error) {
|
|
1432
|
+
process.stderr.write(
|
|
1433
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1434
|
+
`
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
return fallbackClient.execute(stmt);
|
|
1438
|
+
};
|
|
1439
|
+
adapter = {
|
|
1440
|
+
async execute(stmt) {
|
|
1441
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1442
|
+
return fallbackExecute(stmt);
|
|
1443
|
+
}
|
|
1444
|
+
try {
|
|
1445
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
if (shouldFallbackOnError(error)) {
|
|
1448
|
+
return fallbackExecute(stmt, error);
|
|
1449
|
+
}
|
|
1450
|
+
throw error;
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
async batch(stmts, mode) {
|
|
1454
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1455
|
+
if (!fallbackClient) {
|
|
1456
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1457
|
+
}
|
|
1458
|
+
return fallbackClient.batch(stmts, mode);
|
|
1459
|
+
}
|
|
1460
|
+
try {
|
|
1461
|
+
if (prisma.$transaction) {
|
|
1462
|
+
return await prisma.$transaction(async (tx) => {
|
|
1463
|
+
const results2 = [];
|
|
1464
|
+
for (const stmt of stmts) {
|
|
1465
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1466
|
+
}
|
|
1467
|
+
return results2;
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
const results = [];
|
|
1471
|
+
for (const stmt of stmts) {
|
|
1472
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1473
|
+
}
|
|
1474
|
+
return results;
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1477
|
+
process.stderr.write(
|
|
1478
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1479
|
+
`
|
|
1480
|
+
);
|
|
1481
|
+
return fallbackClient.batch(stmts, mode);
|
|
1482
|
+
}
|
|
1483
|
+
throw error;
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
async migrate(stmts) {
|
|
1487
|
+
if (fallbackClient) {
|
|
1488
|
+
return fallbackClient.migrate(stmts);
|
|
1489
|
+
}
|
|
1490
|
+
return adapter.batch(stmts, "deferred");
|
|
1491
|
+
},
|
|
1492
|
+
async transaction(mode) {
|
|
1493
|
+
if (!fallbackClient) {
|
|
1494
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1495
|
+
}
|
|
1496
|
+
return fallbackClient.transaction(mode);
|
|
1497
|
+
},
|
|
1498
|
+
async executeMultiple(sql) {
|
|
1499
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1500
|
+
return fallbackClient.executeMultiple(sql);
|
|
1501
|
+
}
|
|
1502
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1503
|
+
await adapter.execute(statement);
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
async sync() {
|
|
1507
|
+
if (fallbackClient) {
|
|
1508
|
+
return fallbackClient.sync();
|
|
1509
|
+
}
|
|
1510
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1511
|
+
},
|
|
1512
|
+
close() {
|
|
1513
|
+
closed = true;
|
|
1514
|
+
prismaClientPromise = null;
|
|
1515
|
+
compatibilityBootstrapPromise = null;
|
|
1516
|
+
void prisma.$disconnect?.();
|
|
1517
|
+
},
|
|
1518
|
+
get closed() {
|
|
1519
|
+
return closed;
|
|
1520
|
+
},
|
|
1521
|
+
get protocol() {
|
|
1522
|
+
return "prisma-postgres";
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
return adapter;
|
|
1526
|
+
}
|
|
1527
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1528
|
+
var init_database_adapter = __esm({
|
|
1529
|
+
"src/lib/database-adapter.ts"() {
|
|
1530
|
+
"use strict";
|
|
1531
|
+
VIEW_MAPPINGS = [
|
|
1532
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1533
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1534
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1535
|
+
{ view: "entities", source: "memory.entities" },
|
|
1536
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1537
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1538
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1539
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1540
|
+
{ view: "messages", source: "memory.messages" },
|
|
1541
|
+
{ view: "users", source: "wiki.users" },
|
|
1542
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1543
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1544
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1545
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1546
|
+
];
|
|
1547
|
+
UPSERT_KEYS = {
|
|
1548
|
+
memories: ["id"],
|
|
1549
|
+
tasks: ["id"],
|
|
1550
|
+
behaviors: ["id"],
|
|
1551
|
+
entities: ["id"],
|
|
1552
|
+
relationships: ["id"],
|
|
1553
|
+
entity_aliases: ["alias"],
|
|
1554
|
+
notifications: ["id"],
|
|
1555
|
+
messages: ["id"],
|
|
1556
|
+
users: ["id"],
|
|
1557
|
+
workspaces: ["id"],
|
|
1558
|
+
workspace_users: ["id"],
|
|
1559
|
+
documents: ["id"],
|
|
1560
|
+
chats: ["id"]
|
|
1561
|
+
};
|
|
1562
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1563
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1564
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1565
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1566
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1567
|
+
};
|
|
1568
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1569
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1570
|
+
);
|
|
1571
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1572
|
+
/\bPRAGMA\b/i,
|
|
1573
|
+
/\bsqlite_master\b/i,
|
|
1574
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1575
|
+
/\bMATCH\b/i,
|
|
1576
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1577
|
+
/\bjson_extract\s*\(/i,
|
|
1578
|
+
/\bjulianday\s*\(/i,
|
|
1579
|
+
/\bstrftime\s*\(/i,
|
|
1580
|
+
/\blast_insert_rowid\s*\(/i
|
|
1581
|
+
];
|
|
1582
|
+
prismaClientPromise = null;
|
|
1583
|
+
compatibilityBootstrapPromise = null;
|
|
839
1584
|
}
|
|
840
1585
|
});
|
|
841
1586
|
|
|
842
1587
|
// src/lib/database.ts
|
|
843
1588
|
import { createClient } from "@libsql/client";
|
|
844
1589
|
async function initDatabase(config2) {
|
|
1590
|
+
if (_walCheckpointTimer) {
|
|
1591
|
+
clearInterval(_walCheckpointTimer);
|
|
1592
|
+
_walCheckpointTimer = null;
|
|
1593
|
+
}
|
|
1594
|
+
if (_daemonClient) {
|
|
1595
|
+
_daemonClient.close();
|
|
1596
|
+
_daemonClient = null;
|
|
1597
|
+
}
|
|
1598
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1599
|
+
_adapterClient.close();
|
|
1600
|
+
}
|
|
1601
|
+
_adapterClient = null;
|
|
845
1602
|
if (_client) {
|
|
846
1603
|
_client.close();
|
|
847
1604
|
_client = null;
|
|
@@ -855,6 +1612,7 @@ async function initDatabase(config2) {
|
|
|
855
1612
|
}
|
|
856
1613
|
_client = createClient(opts);
|
|
857
1614
|
_resilientClient = wrapWithRetry(_client);
|
|
1615
|
+
_adapterClient = _resilientClient;
|
|
858
1616
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
859
1617
|
});
|
|
860
1618
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -865,11 +1623,17 @@ async function initDatabase(config2) {
|
|
|
865
1623
|
});
|
|
866
1624
|
}, 3e4);
|
|
867
1625
|
_walCheckpointTimer.unref();
|
|
1626
|
+
if (process.env.DATABASE_URL) {
|
|
1627
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1628
|
+
}
|
|
868
1629
|
}
|
|
869
1630
|
function getClient() {
|
|
870
|
-
if (!
|
|
1631
|
+
if (!_adapterClient) {
|
|
871
1632
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
872
1633
|
}
|
|
1634
|
+
if (process.env.DATABASE_URL) {
|
|
1635
|
+
return _adapterClient;
|
|
1636
|
+
}
|
|
873
1637
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
874
1638
|
return _resilientClient;
|
|
875
1639
|
}
|
|
@@ -1810,26 +2574,36 @@ async function ensureSchema() {
|
|
|
1810
2574
|
}
|
|
1811
2575
|
}
|
|
1812
2576
|
async function disposeDatabase() {
|
|
2577
|
+
if (_walCheckpointTimer) {
|
|
2578
|
+
clearInterval(_walCheckpointTimer);
|
|
2579
|
+
_walCheckpointTimer = null;
|
|
2580
|
+
}
|
|
1813
2581
|
if (_daemonClient) {
|
|
1814
2582
|
_daemonClient.close();
|
|
1815
2583
|
_daemonClient = null;
|
|
1816
2584
|
}
|
|
2585
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2586
|
+
_adapterClient.close();
|
|
2587
|
+
}
|
|
2588
|
+
_adapterClient = null;
|
|
1817
2589
|
if (_client) {
|
|
1818
2590
|
_client.close();
|
|
1819
2591
|
_client = null;
|
|
1820
2592
|
_resilientClient = null;
|
|
1821
2593
|
}
|
|
1822
2594
|
}
|
|
1823
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2595
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1824
2596
|
var init_database = __esm({
|
|
1825
2597
|
"src/lib/database.ts"() {
|
|
1826
2598
|
"use strict";
|
|
1827
2599
|
init_db_retry();
|
|
1828
2600
|
init_employees();
|
|
2601
|
+
init_database_adapter();
|
|
1829
2602
|
_client = null;
|
|
1830
2603
|
_resilientClient = null;
|
|
1831
2604
|
_walCheckpointTimer = null;
|
|
1832
2605
|
_daemonClient = null;
|
|
2606
|
+
_adapterClient = null;
|
|
1833
2607
|
initTurso = initDatabase;
|
|
1834
2608
|
disposeTurso = disposeDatabase;
|
|
1835
2609
|
}
|
|
@@ -1846,11 +2620,11 @@ var init_memory = __esm({
|
|
|
1846
2620
|
|
|
1847
2621
|
// src/lib/exe-daemon-client.ts
|
|
1848
2622
|
import net from "net";
|
|
1849
|
-
import
|
|
2623
|
+
import os4 from "os";
|
|
1850
2624
|
import { spawn } from "child_process";
|
|
1851
2625
|
import { randomUUID } from "crypto";
|
|
1852
|
-
import { existsSync as
|
|
1853
|
-
import
|
|
2626
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
2627
|
+
import path5 from "path";
|
|
1854
2628
|
import { fileURLToPath } from "url";
|
|
1855
2629
|
function handleData(chunk) {
|
|
1856
2630
|
_buffer += chunk.toString();
|
|
@@ -1878,9 +2652,9 @@ function handleData(chunk) {
|
|
|
1878
2652
|
}
|
|
1879
2653
|
}
|
|
1880
2654
|
function cleanupStaleFiles() {
|
|
1881
|
-
if (
|
|
2655
|
+
if (existsSync4(PID_PATH)) {
|
|
1882
2656
|
try {
|
|
1883
|
-
const pid = parseInt(
|
|
2657
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1884
2658
|
if (pid > 0) {
|
|
1885
2659
|
try {
|
|
1886
2660
|
process.kill(pid, 0);
|
|
@@ -1901,17 +2675,17 @@ function cleanupStaleFiles() {
|
|
|
1901
2675
|
}
|
|
1902
2676
|
}
|
|
1903
2677
|
function findPackageRoot() {
|
|
1904
|
-
let dir =
|
|
1905
|
-
const { root } =
|
|
2678
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2679
|
+
const { root } = path5.parse(dir);
|
|
1906
2680
|
while (dir !== root) {
|
|
1907
|
-
if (
|
|
1908
|
-
dir =
|
|
2681
|
+
if (existsSync4(path5.join(dir, "package.json"))) return dir;
|
|
2682
|
+
dir = path5.dirname(dir);
|
|
1909
2683
|
}
|
|
1910
2684
|
return null;
|
|
1911
2685
|
}
|
|
1912
2686
|
function spawnDaemon() {
|
|
1913
|
-
const freeGB =
|
|
1914
|
-
const totalGB =
|
|
2687
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
2688
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1915
2689
|
if (totalGB <= 8) {
|
|
1916
2690
|
process.stderr.write(
|
|
1917
2691
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -1931,8 +2705,8 @@ function spawnDaemon() {
|
|
|
1931
2705
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1932
2706
|
return;
|
|
1933
2707
|
}
|
|
1934
|
-
const daemonPath =
|
|
1935
|
-
if (!
|
|
2708
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2709
|
+
if (!existsSync4(daemonPath)) {
|
|
1936
2710
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1937
2711
|
`);
|
|
1938
2712
|
return;
|
|
@@ -1940,7 +2714,7 @@ function spawnDaemon() {
|
|
|
1940
2714
|
const resolvedPath = daemonPath;
|
|
1941
2715
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1942
2716
|
`);
|
|
1943
|
-
const logPath =
|
|
2717
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1944
2718
|
let stderrFd = "ignore";
|
|
1945
2719
|
try {
|
|
1946
2720
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2091,74 +2865,123 @@ async function pingDaemon() {
|
|
|
2091
2865
|
return null;
|
|
2092
2866
|
}
|
|
2093
2867
|
function killAndRespawnDaemon() {
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2868
|
+
if (!acquireSpawnLock()) {
|
|
2869
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
2870
|
+
if (_socket) {
|
|
2871
|
+
_socket.destroy();
|
|
2872
|
+
_socket = null;
|
|
2873
|
+
}
|
|
2874
|
+
_connected = false;
|
|
2875
|
+
_buffer = "";
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
try {
|
|
2879
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2880
|
+
if (existsSync4(PID_PATH)) {
|
|
2881
|
+
try {
|
|
2882
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2883
|
+
if (pid > 0) {
|
|
2884
|
+
try {
|
|
2885
|
+
process.kill(pid, "SIGKILL");
|
|
2886
|
+
} catch {
|
|
2887
|
+
}
|
|
2102
2888
|
}
|
|
2889
|
+
} catch {
|
|
2103
2890
|
}
|
|
2891
|
+
}
|
|
2892
|
+
if (_socket) {
|
|
2893
|
+
_socket.destroy();
|
|
2894
|
+
_socket = null;
|
|
2895
|
+
}
|
|
2896
|
+
_connected = false;
|
|
2897
|
+
_buffer = "";
|
|
2898
|
+
try {
|
|
2899
|
+
unlinkSync2(PID_PATH);
|
|
2104
2900
|
} catch {
|
|
2105
2901
|
}
|
|
2902
|
+
try {
|
|
2903
|
+
unlinkSync2(SOCKET_PATH);
|
|
2904
|
+
} catch {
|
|
2905
|
+
}
|
|
2906
|
+
spawnDaemon();
|
|
2907
|
+
} finally {
|
|
2908
|
+
releaseSpawnLock();
|
|
2106
2909
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
_socket = null;
|
|
2110
|
-
}
|
|
2111
|
-
_connected = false;
|
|
2112
|
-
_buffer = "";
|
|
2910
|
+
}
|
|
2911
|
+
function isDaemonTooYoung() {
|
|
2113
2912
|
try {
|
|
2114
|
-
|
|
2913
|
+
const stat = statSync(PID_PATH);
|
|
2914
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
2115
2915
|
} catch {
|
|
2916
|
+
return false;
|
|
2116
2917
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2918
|
+
}
|
|
2919
|
+
async function retryThenRestart(doRequest, label) {
|
|
2920
|
+
const result = await doRequest();
|
|
2921
|
+
if (!result.error) {
|
|
2922
|
+
_consecutiveFailures = 0;
|
|
2923
|
+
return result;
|
|
2924
|
+
}
|
|
2925
|
+
_consecutiveFailures++;
|
|
2926
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
2927
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
2928
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
2929
|
+
`);
|
|
2930
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
2931
|
+
if (!_connected) {
|
|
2932
|
+
if (!await connectToSocket()) continue;
|
|
2933
|
+
}
|
|
2934
|
+
const retry = await doRequest();
|
|
2935
|
+
if (!retry.error) {
|
|
2936
|
+
_consecutiveFailures = 0;
|
|
2937
|
+
return retry;
|
|
2938
|
+
}
|
|
2939
|
+
_consecutiveFailures++;
|
|
2940
|
+
}
|
|
2941
|
+
if (isDaemonTooYoung()) {
|
|
2942
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
2943
|
+
`);
|
|
2944
|
+
return { error: result.error };
|
|
2945
|
+
}
|
|
2946
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
2947
|
+
`);
|
|
2948
|
+
killAndRespawnDaemon();
|
|
2949
|
+
const start = Date.now();
|
|
2950
|
+
let delay2 = 200;
|
|
2951
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2952
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2953
|
+
if (await connectToSocket()) break;
|
|
2954
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2120
2955
|
}
|
|
2121
|
-
|
|
2956
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
2957
|
+
const final = await doRequest();
|
|
2958
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
2959
|
+
return final;
|
|
2122
2960
|
}
|
|
2123
2961
|
async function embedViaClient(text, priority = "high") {
|
|
2124
2962
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
2125
2963
|
_requestCount++;
|
|
2126
2964
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
2127
2965
|
const health = await pingDaemon();
|
|
2128
|
-
if (!health) {
|
|
2966
|
+
if (!health && !isDaemonTooYoung()) {
|
|
2129
2967
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
2130
2968
|
`);
|
|
2131
2969
|
killAndRespawnDaemon();
|
|
2132
2970
|
const start = Date.now();
|
|
2133
|
-
let
|
|
2971
|
+
let d = 200;
|
|
2134
2972
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2135
|
-
await new Promise((r) => setTimeout(r,
|
|
2973
|
+
await new Promise((r) => setTimeout(r, d));
|
|
2136
2974
|
if (await connectToSocket()) break;
|
|
2137
|
-
|
|
2975
|
+
d = Math.min(d * 2, 3e3);
|
|
2138
2976
|
}
|
|
2139
2977
|
if (!_connected) return null;
|
|
2140
2978
|
}
|
|
2141
2979
|
}
|
|
2142
|
-
const result = await
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
killAndRespawnDaemon();
|
|
2148
|
-
const start = Date.now();
|
|
2149
|
-
let delay2 = 200;
|
|
2150
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2151
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
2152
|
-
if (await connectToSocket()) break;
|
|
2153
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2154
|
-
}
|
|
2155
|
-
if (!_connected) return null;
|
|
2156
|
-
const retry = await sendRequest([text], priority);
|
|
2157
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
2158
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
2159
|
-
`);
|
|
2160
|
-
}
|
|
2161
|
-
return null;
|
|
2980
|
+
const result = await retryThenRestart(
|
|
2981
|
+
() => sendRequest([text], priority),
|
|
2982
|
+
"Embed"
|
|
2983
|
+
);
|
|
2984
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2162
2985
|
}
|
|
2163
2986
|
function disconnectClient() {
|
|
2164
2987
|
if (_socket) {
|
|
@@ -2173,14 +2996,14 @@ function disconnectClient() {
|
|
|
2173
2996
|
entry.resolve({ error: "Client disconnected" });
|
|
2174
2997
|
}
|
|
2175
2998
|
}
|
|
2176
|
-
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;
|
|
2999
|
+
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;
|
|
2177
3000
|
var init_exe_daemon_client = __esm({
|
|
2178
3001
|
"src/lib/exe-daemon-client.ts"() {
|
|
2179
3002
|
"use strict";
|
|
2180
3003
|
init_config();
|
|
2181
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2182
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2183
|
-
SPAWN_LOCK_PATH =
|
|
3004
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
3005
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
3006
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2184
3007
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2185
3008
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2186
3009
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2188,7 +3011,11 @@ var init_exe_daemon_client = __esm({
|
|
|
2188
3011
|
_connected = false;
|
|
2189
3012
|
_buffer = "";
|
|
2190
3013
|
_requestCount = 0;
|
|
3014
|
+
_consecutiveFailures = 0;
|
|
2191
3015
|
HEALTH_CHECK_INTERVAL = 100;
|
|
3016
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
3017
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
3018
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
2192
3019
|
_pending = /* @__PURE__ */ new Map();
|
|
2193
3020
|
MAX_BUFFER = 1e7;
|
|
2194
3021
|
}
|
|
@@ -2232,8 +3059,8 @@ async function embedDirect(text) {
|
|
|
2232
3059
|
const llamaCpp = await import("node-llama-cpp");
|
|
2233
3060
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2234
3061
|
const { existsSync: existsSync17 } = await import("fs");
|
|
2235
|
-
const
|
|
2236
|
-
const modelPath =
|
|
3062
|
+
const path22 = await import("path");
|
|
3063
|
+
const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2237
3064
|
if (!existsSync17(modelPath)) {
|
|
2238
3065
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2239
3066
|
}
|
|
@@ -2264,14 +3091,14 @@ var init_embedder = __esm({
|
|
|
2264
3091
|
|
|
2265
3092
|
// src/lib/keychain.ts
|
|
2266
3093
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2267
|
-
import { existsSync as
|
|
2268
|
-
import
|
|
2269
|
-
import
|
|
3094
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3095
|
+
import path6 from "path";
|
|
3096
|
+
import os5 from "os";
|
|
2270
3097
|
function getKeyDir() {
|
|
2271
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3098
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2272
3099
|
}
|
|
2273
3100
|
function getKeyPath() {
|
|
2274
|
-
return
|
|
3101
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2275
3102
|
}
|
|
2276
3103
|
async function tryKeytar() {
|
|
2277
3104
|
try {
|
|
@@ -2292,9 +3119,9 @@ async function getMasterKey() {
|
|
|
2292
3119
|
}
|
|
2293
3120
|
}
|
|
2294
3121
|
const keyPath = getKeyPath();
|
|
2295
|
-
if (!
|
|
3122
|
+
if (!existsSync5(keyPath)) {
|
|
2296
3123
|
process.stderr.write(
|
|
2297
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3124
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2298
3125
|
`
|
|
2299
3126
|
);
|
|
2300
3127
|
return null;
|
|
@@ -2332,13 +3159,13 @@ __export(shard_manager_exports, {
|
|
|
2332
3159
|
listShards: () => listShards,
|
|
2333
3160
|
shardExists: () => shardExists
|
|
2334
3161
|
});
|
|
2335
|
-
import
|
|
2336
|
-
import { existsSync as
|
|
3162
|
+
import path7 from "path";
|
|
3163
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2337
3164
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2338
3165
|
function initShardManager(encryptionKey) {
|
|
2339
3166
|
_encryptionKey = encryptionKey;
|
|
2340
|
-
if (!
|
|
2341
|
-
|
|
3167
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
3168
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2342
3169
|
}
|
|
2343
3170
|
_shardingEnabled = true;
|
|
2344
3171
|
}
|
|
@@ -2358,7 +3185,7 @@ function getShardClient(projectName) {
|
|
|
2358
3185
|
}
|
|
2359
3186
|
const cached = _shards.get(safeName);
|
|
2360
3187
|
if (cached) return cached;
|
|
2361
|
-
const dbPath =
|
|
3188
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2362
3189
|
const client = createClient2({
|
|
2363
3190
|
url: `file:${dbPath}`,
|
|
2364
3191
|
encryptionKey: _encryptionKey
|
|
@@ -2368,10 +3195,10 @@ function getShardClient(projectName) {
|
|
|
2368
3195
|
}
|
|
2369
3196
|
function shardExists(projectName) {
|
|
2370
3197
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2371
|
-
return
|
|
3198
|
+
return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2372
3199
|
}
|
|
2373
3200
|
function listShards() {
|
|
2374
|
-
if (!
|
|
3201
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2375
3202
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2376
3203
|
}
|
|
2377
3204
|
async function ensureShardSchema(client) {
|
|
@@ -2445,7 +3272,23 @@ async function ensureShardSchema(client) {
|
|
|
2445
3272
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2446
3273
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2447
3274
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2448
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3275
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3276
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3277
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3278
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3279
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3280
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3281
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3282
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3283
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3284
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3285
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3286
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3287
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3288
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3289
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3290
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3291
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2449
3292
|
]) {
|
|
2450
3293
|
try {
|
|
2451
3294
|
await client.execute(col);
|
|
@@ -2557,7 +3400,7 @@ var init_shard_manager = __esm({
|
|
|
2557
3400
|
"src/lib/shard-manager.ts"() {
|
|
2558
3401
|
"use strict";
|
|
2559
3402
|
init_config();
|
|
2560
|
-
SHARDS_DIR =
|
|
3403
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
2561
3404
|
_shards = /* @__PURE__ */ new Map();
|
|
2562
3405
|
_encryptionKey = null;
|
|
2563
3406
|
_shardingEnabled = false;
|
|
@@ -3330,8 +4173,8 @@ __export(wiki_client_exports, {
|
|
|
3330
4173
|
listDocuments: () => listDocuments,
|
|
3331
4174
|
listWorkspaces: () => listWorkspaces
|
|
3332
4175
|
});
|
|
3333
|
-
async function wikiFetch(config2,
|
|
3334
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
4176
|
+
async function wikiFetch(config2, path22, method = "GET", body) {
|
|
4177
|
+
const url = `${config2.baseUrl}/api/v1${path22}`;
|
|
3335
4178
|
const headers = {
|
|
3336
4179
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
3337
4180
|
"Content-Type": "application/json"
|
|
@@ -3364,7 +4207,7 @@ async function wikiFetch(config2, path21, method = "GET", body) {
|
|
|
3364
4207
|
}
|
|
3365
4208
|
}
|
|
3366
4209
|
if (!response.ok) {
|
|
3367
|
-
throw new Error(`Wiki API ${method} ${
|
|
4210
|
+
throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
|
|
3368
4211
|
}
|
|
3369
4212
|
return response.json();
|
|
3370
4213
|
} finally {
|
|
@@ -4393,9 +5236,9 @@ __export(license_exports, {
|
|
|
4393
5236
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4394
5237
|
validateLicense: () => validateLicense
|
|
4395
5238
|
});
|
|
4396
|
-
import { readFileSync as
|
|
5239
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
4397
5240
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4398
|
-
import
|
|
5241
|
+
import path8 from "path";
|
|
4399
5242
|
import { jwtVerify, importSPKI } from "jose";
|
|
4400
5243
|
async function fetchRetry(url, init) {
|
|
4401
5244
|
try {
|
|
@@ -4406,37 +5249,37 @@ async function fetchRetry(url, init) {
|
|
|
4406
5249
|
}
|
|
4407
5250
|
}
|
|
4408
5251
|
function loadDeviceId() {
|
|
4409
|
-
const deviceJsonPath =
|
|
5252
|
+
const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
|
|
4410
5253
|
try {
|
|
4411
|
-
if (
|
|
4412
|
-
const data = JSON.parse(
|
|
5254
|
+
if (existsSync7(deviceJsonPath)) {
|
|
5255
|
+
const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
|
|
4413
5256
|
if (data.deviceId) return data.deviceId;
|
|
4414
5257
|
}
|
|
4415
5258
|
} catch {
|
|
4416
5259
|
}
|
|
4417
5260
|
try {
|
|
4418
|
-
if (
|
|
4419
|
-
const id2 =
|
|
5261
|
+
if (existsSync7(DEVICE_ID_PATH)) {
|
|
5262
|
+
const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
|
|
4420
5263
|
if (id2) return id2;
|
|
4421
5264
|
}
|
|
4422
5265
|
} catch {
|
|
4423
5266
|
}
|
|
4424
5267
|
const id = randomUUID3();
|
|
4425
|
-
|
|
4426
|
-
|
|
5268
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
5269
|
+
writeFileSync3(DEVICE_ID_PATH, id, "utf8");
|
|
4427
5270
|
return id;
|
|
4428
5271
|
}
|
|
4429
5272
|
function loadLicense() {
|
|
4430
5273
|
try {
|
|
4431
|
-
if (!
|
|
4432
|
-
return
|
|
5274
|
+
if (!existsSync7(LICENSE_PATH)) return null;
|
|
5275
|
+
return readFileSync5(LICENSE_PATH, "utf8").trim();
|
|
4433
5276
|
} catch {
|
|
4434
5277
|
return null;
|
|
4435
5278
|
}
|
|
4436
5279
|
}
|
|
4437
5280
|
function saveLicense(apiKey) {
|
|
4438
|
-
|
|
4439
|
-
|
|
5281
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
5282
|
+
writeFileSync3(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
4440
5283
|
}
|
|
4441
5284
|
async function verifyLicenseJwt(token) {
|
|
4442
5285
|
try {
|
|
@@ -4462,8 +5305,8 @@ async function verifyLicenseJwt(token) {
|
|
|
4462
5305
|
}
|
|
4463
5306
|
async function getCachedLicense() {
|
|
4464
5307
|
try {
|
|
4465
|
-
if (!
|
|
4466
|
-
const raw = JSON.parse(
|
|
5308
|
+
if (!existsSync7(CACHE_PATH)) return null;
|
|
5309
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
4467
5310
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
4468
5311
|
return await verifyLicenseJwt(raw.token);
|
|
4469
5312
|
} catch {
|
|
@@ -4472,8 +5315,8 @@ async function getCachedLicense() {
|
|
|
4472
5315
|
}
|
|
4473
5316
|
function readCachedToken() {
|
|
4474
5317
|
try {
|
|
4475
|
-
if (!
|
|
4476
|
-
const raw = JSON.parse(
|
|
5318
|
+
if (!existsSync7(CACHE_PATH)) return null;
|
|
5319
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
4477
5320
|
return typeof raw.token === "string" ? raw.token : null;
|
|
4478
5321
|
} catch {
|
|
4479
5322
|
return null;
|
|
@@ -4507,7 +5350,7 @@ function getRawCachedPlan() {
|
|
|
4507
5350
|
}
|
|
4508
5351
|
function cacheResponse(token) {
|
|
4509
5352
|
try {
|
|
4510
|
-
|
|
5353
|
+
writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
4511
5354
|
} catch {
|
|
4512
5355
|
}
|
|
4513
5356
|
}
|
|
@@ -4571,9 +5414,9 @@ async function checkLicense() {
|
|
|
4571
5414
|
let key = loadLicense();
|
|
4572
5415
|
if (!key) {
|
|
4573
5416
|
try {
|
|
4574
|
-
const configPath =
|
|
4575
|
-
if (
|
|
4576
|
-
const raw = JSON.parse(
|
|
5417
|
+
const configPath = path8.join(EXE_AI_DIR, "config.json");
|
|
5418
|
+
if (existsSync7(configPath)) {
|
|
5419
|
+
const raw = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
4577
5420
|
const cloud = raw.cloud;
|
|
4578
5421
|
if (cloud?.apiKey) {
|
|
4579
5422
|
key = cloud.apiKey;
|
|
@@ -4732,9 +5575,9 @@ var init_license = __esm({
|
|
|
4732
5575
|
"src/lib/license.ts"() {
|
|
4733
5576
|
"use strict";
|
|
4734
5577
|
init_config();
|
|
4735
|
-
LICENSE_PATH =
|
|
4736
|
-
CACHE_PATH =
|
|
4737
|
-
DEVICE_ID_PATH =
|
|
5578
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
5579
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
5580
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
4738
5581
|
API_BASE = "https://askexe.com/cloud";
|
|
4739
5582
|
RETRY_DELAY_MS = 500;
|
|
4740
5583
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -4772,7 +5615,7 @@ __export(whatsapp_exports, {
|
|
|
4772
5615
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4773
5616
|
import { homedir } from "os";
|
|
4774
5617
|
import { join } from "path";
|
|
4775
|
-
import { mkdirSync as
|
|
5618
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
4776
5619
|
function calculateBackoff(retryCount) {
|
|
4777
5620
|
const base = Math.min(
|
|
4778
5621
|
INITIAL_BACKOFF_MS * BACKOFF_MULTIPLIER ** retryCount,
|
|
@@ -4802,7 +5645,7 @@ var init_whatsapp = __esm({
|
|
|
4802
5645
|
disconnectedAt = 0;
|
|
4803
5646
|
async connect(config2) {
|
|
4804
5647
|
this.authDir = config2.credentials.authDir ?? AUTH_DIR;
|
|
4805
|
-
|
|
5648
|
+
mkdirSync4(this.authDir, { recursive: true });
|
|
4806
5649
|
const baileys = await import("@whiskeysockets/baileys");
|
|
4807
5650
|
const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, DisconnectReason, makeCacheableSignalKeyStore } = baileys;
|
|
4808
5651
|
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
@@ -5632,16 +6475,16 @@ __export(imessage_exports, {
|
|
|
5632
6475
|
});
|
|
5633
6476
|
import { execFile } from "child_process";
|
|
5634
6477
|
import { promisify } from "util";
|
|
5635
|
-
import
|
|
5636
|
-
import
|
|
6478
|
+
import os6 from "os";
|
|
6479
|
+
import path9 from "path";
|
|
5637
6480
|
var execFileAsync, POLL_INTERVAL_MS, MESSAGES_DB_PATH, IMessageAdapter;
|
|
5638
6481
|
var init_imessage = __esm({
|
|
5639
6482
|
"src/gateway/adapters/imessage.ts"() {
|
|
5640
6483
|
"use strict";
|
|
5641
6484
|
execFileAsync = promisify(execFile);
|
|
5642
6485
|
POLL_INTERVAL_MS = 5e3;
|
|
5643
|
-
MESSAGES_DB_PATH =
|
|
5644
|
-
process.env.HOME ??
|
|
6486
|
+
MESSAGES_DB_PATH = path9.join(
|
|
6487
|
+
process.env.HOME ?? os6.homedir(),
|
|
5645
6488
|
"Library/Messages/chat.db"
|
|
5646
6489
|
);
|
|
5647
6490
|
IMessageAdapter = class {
|
|
@@ -5954,9 +6797,9 @@ __export(webhook_exports, {
|
|
|
5954
6797
|
WebhookAdapter: () => WebhookAdapter
|
|
5955
6798
|
});
|
|
5956
6799
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
5957
|
-
function resolvePath(obj,
|
|
6800
|
+
function resolvePath(obj, path22) {
|
|
5958
6801
|
let current = obj;
|
|
5959
|
-
for (const segment of
|
|
6802
|
+
for (const segment of path22.split(".")) {
|
|
5960
6803
|
if (current == null || typeof current !== "object") return void 0;
|
|
5961
6804
|
current = current[segment];
|
|
5962
6805
|
}
|
|
@@ -6059,13 +6902,13 @@ __export(whatsapp_accounts_exports, {
|
|
|
6059
6902
|
getDefaultAccount: () => getDefaultAccount,
|
|
6060
6903
|
loadAccounts: () => loadAccounts
|
|
6061
6904
|
});
|
|
6062
|
-
import { readFileSync as
|
|
6905
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
6063
6906
|
import { join as join2 } from "path";
|
|
6064
6907
|
import { homedir as homedir2 } from "os";
|
|
6065
6908
|
function loadAccounts() {
|
|
6066
6909
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
6067
6910
|
try {
|
|
6068
|
-
const raw =
|
|
6911
|
+
const raw = readFileSync6(CONFIG_PATH2, "utf8");
|
|
6069
6912
|
const parsed = JSON.parse(raw);
|
|
6070
6913
|
if (!Array.isArray(parsed)) {
|
|
6071
6914
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -6105,13 +6948,13 @@ var init_whatsapp_accounts = __esm({
|
|
|
6105
6948
|
});
|
|
6106
6949
|
|
|
6107
6950
|
// src/lib/session-registry.ts
|
|
6108
|
-
import { readFileSync as
|
|
6109
|
-
import
|
|
6110
|
-
import
|
|
6951
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
|
|
6952
|
+
import path10 from "path";
|
|
6953
|
+
import os7 from "os";
|
|
6111
6954
|
function registerSession(entry) {
|
|
6112
|
-
const dir =
|
|
6113
|
-
if (!
|
|
6114
|
-
|
|
6955
|
+
const dir = path10.dirname(REGISTRY_PATH);
|
|
6956
|
+
if (!existsSync8(dir)) {
|
|
6957
|
+
mkdirSync5(dir, { recursive: true });
|
|
6115
6958
|
}
|
|
6116
6959
|
const sessions = listSessions();
|
|
6117
6960
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -6120,11 +6963,11 @@ function registerSession(entry) {
|
|
|
6120
6963
|
} else {
|
|
6121
6964
|
sessions.push(entry);
|
|
6122
6965
|
}
|
|
6123
|
-
|
|
6966
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
6124
6967
|
}
|
|
6125
6968
|
function listSessions() {
|
|
6126
6969
|
try {
|
|
6127
|
-
const raw =
|
|
6970
|
+
const raw = readFileSync7(REGISTRY_PATH, "utf8");
|
|
6128
6971
|
return JSON.parse(raw);
|
|
6129
6972
|
} catch {
|
|
6130
6973
|
return [];
|
|
@@ -6134,7 +6977,7 @@ var REGISTRY_PATH;
|
|
|
6134
6977
|
var init_session_registry = __esm({
|
|
6135
6978
|
"src/lib/session-registry.ts"() {
|
|
6136
6979
|
"use strict";
|
|
6137
|
-
REGISTRY_PATH =
|
|
6980
|
+
REGISTRY_PATH = path10.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
6138
6981
|
}
|
|
6139
6982
|
});
|
|
6140
6983
|
|
|
@@ -6386,67 +7229,6 @@ var init_provider_table = __esm({
|
|
|
6386
7229
|
}
|
|
6387
7230
|
});
|
|
6388
7231
|
|
|
6389
|
-
// src/lib/runtime-table.ts
|
|
6390
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
6391
|
-
var init_runtime_table = __esm({
|
|
6392
|
-
"src/lib/runtime-table.ts"() {
|
|
6393
|
-
"use strict";
|
|
6394
|
-
RUNTIME_TABLE = {
|
|
6395
|
-
codex: {
|
|
6396
|
-
binary: "codex",
|
|
6397
|
-
launchMode: "interactive",
|
|
6398
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
6399
|
-
inlineFlag: "--no-alt-screen",
|
|
6400
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
6401
|
-
defaultModel: "gpt-5.4"
|
|
6402
|
-
},
|
|
6403
|
-
opencode: {
|
|
6404
|
-
binary: "opencode",
|
|
6405
|
-
launchMode: "exec",
|
|
6406
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
6407
|
-
inlineFlag: "",
|
|
6408
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
6409
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
6410
|
-
}
|
|
6411
|
-
};
|
|
6412
|
-
DEFAULT_RUNTIME = "claude";
|
|
6413
|
-
}
|
|
6414
|
-
});
|
|
6415
|
-
|
|
6416
|
-
// src/lib/agent-config.ts
|
|
6417
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
6418
|
-
import path9 from "path";
|
|
6419
|
-
function loadAgentConfig() {
|
|
6420
|
-
if (!existsSync8(AGENT_CONFIG_PATH)) return {};
|
|
6421
|
-
try {
|
|
6422
|
-
return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf-8"));
|
|
6423
|
-
} catch {
|
|
6424
|
-
return {};
|
|
6425
|
-
}
|
|
6426
|
-
}
|
|
6427
|
-
function getAgentRuntime(agentId) {
|
|
6428
|
-
const config2 = loadAgentConfig();
|
|
6429
|
-
const entry = config2[agentId];
|
|
6430
|
-
if (entry) return entry;
|
|
6431
|
-
const orgDefault = config2["default"];
|
|
6432
|
-
if (orgDefault) return orgDefault;
|
|
6433
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
6434
|
-
}
|
|
6435
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
6436
|
-
var init_agent_config = __esm({
|
|
6437
|
-
"src/lib/agent-config.ts"() {
|
|
6438
|
-
"use strict";
|
|
6439
|
-
init_config();
|
|
6440
|
-
init_runtime_table();
|
|
6441
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
6442
|
-
DEFAULT_MODELS = {
|
|
6443
|
-
claude: "claude-opus-4",
|
|
6444
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
6445
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
6446
|
-
};
|
|
6447
|
-
}
|
|
6448
|
-
});
|
|
6449
|
-
|
|
6450
7232
|
// src/lib/intercom-queue.ts
|
|
6451
7233
|
var intercom_queue_exports = {};
|
|
6452
7234
|
__export(intercom_queue_exports, {
|
|
@@ -6457,10 +7239,10 @@ __export(intercom_queue_exports, {
|
|
|
6457
7239
|
readQueue: () => readQueue
|
|
6458
7240
|
});
|
|
6459
7241
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
6460
|
-
import
|
|
6461
|
-
import
|
|
7242
|
+
import path11 from "path";
|
|
7243
|
+
import os8 from "os";
|
|
6462
7244
|
function ensureDir() {
|
|
6463
|
-
const dir =
|
|
7245
|
+
const dir = path11.dirname(QUEUE_PATH);
|
|
6464
7246
|
if (!existsSync9(dir)) mkdirSync6(dir, { recursive: true });
|
|
6465
7247
|
}
|
|
6466
7248
|
function readQueue() {
|
|
@@ -6566,16 +7348,16 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
6566
7348
|
var init_intercom_queue = __esm({
|
|
6567
7349
|
"src/lib/intercom-queue.ts"() {
|
|
6568
7350
|
"use strict";
|
|
6569
|
-
QUEUE_PATH =
|
|
7351
|
+
QUEUE_PATH = path11.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
6570
7352
|
MAX_RETRIES2 = 5;
|
|
6571
7353
|
TTL_MS = 60 * 60 * 1e3;
|
|
6572
|
-
INTERCOM_LOG =
|
|
7354
|
+
INTERCOM_LOG = path11.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
6573
7355
|
}
|
|
6574
7356
|
});
|
|
6575
7357
|
|
|
6576
7358
|
// src/lib/plan-limits.ts
|
|
6577
7359
|
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
6578
|
-
import
|
|
7360
|
+
import path12 from "path";
|
|
6579
7361
|
function getLicenseSync() {
|
|
6580
7362
|
try {
|
|
6581
7363
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -6647,14 +7429,14 @@ var init_plan_limits = __esm({
|
|
|
6647
7429
|
this.name = "PlanLimitError";
|
|
6648
7430
|
}
|
|
6649
7431
|
};
|
|
6650
|
-
CACHE_PATH2 =
|
|
7432
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
6651
7433
|
}
|
|
6652
7434
|
});
|
|
6653
7435
|
|
|
6654
7436
|
// src/lib/notifications.ts
|
|
6655
7437
|
import crypto3 from "crypto";
|
|
6656
|
-
import
|
|
6657
|
-
import
|
|
7438
|
+
import path13 from "path";
|
|
7439
|
+
import os9 from "os";
|
|
6658
7440
|
import {
|
|
6659
7441
|
readFileSync as readFileSync10,
|
|
6660
7442
|
readdirSync as readdirSync2,
|
|
@@ -6763,8 +7545,8 @@ var init_task_scope = __esm({
|
|
|
6763
7545
|
|
|
6764
7546
|
// src/lib/tasks-crud.ts
|
|
6765
7547
|
import crypto5 from "crypto";
|
|
6766
|
-
import
|
|
6767
|
-
import
|
|
7548
|
+
import path14 from "path";
|
|
7549
|
+
import os10 from "os";
|
|
6768
7550
|
import { execSync as execSync4 } from "child_process";
|
|
6769
7551
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
6770
7552
|
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
@@ -6942,8 +7724,8 @@ ${laneWarning}` : laneWarning;
|
|
|
6942
7724
|
}
|
|
6943
7725
|
if (input.baseDir) {
|
|
6944
7726
|
try {
|
|
6945
|
-
await mkdir4(
|
|
6946
|
-
await mkdir4(
|
|
7727
|
+
await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
7728
|
+
await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
6947
7729
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
6948
7730
|
await ensureGitignoreExe(input.baseDir);
|
|
6949
7731
|
} catch {
|
|
@@ -6979,9 +7761,9 @@ ${laneWarning}` : laneWarning;
|
|
|
6979
7761
|
});
|
|
6980
7762
|
if (input.baseDir) {
|
|
6981
7763
|
try {
|
|
6982
|
-
const EXE_OS_DIR =
|
|
6983
|
-
const mdPath =
|
|
6984
|
-
const mdDir =
|
|
7764
|
+
const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
|
|
7765
|
+
const mdPath = path14.join(EXE_OS_DIR, taskFile);
|
|
7766
|
+
const mdDir = path14.dirname(mdPath);
|
|
6985
7767
|
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
6986
7768
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
6987
7769
|
const mdContent = `# ${input.title}
|
|
@@ -7282,7 +8064,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
7282
8064
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
7283
8065
|
}
|
|
7284
8066
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
7285
|
-
const archPath =
|
|
8067
|
+
const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
7286
8068
|
try {
|
|
7287
8069
|
if (existsSync12(archPath)) return;
|
|
7288
8070
|
const template = [
|
|
@@ -7317,7 +8099,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
7317
8099
|
}
|
|
7318
8100
|
}
|
|
7319
8101
|
async function ensureGitignoreExe(baseDir) {
|
|
7320
|
-
const gitignorePath =
|
|
8102
|
+
const gitignorePath = path14.join(baseDir, ".gitignore");
|
|
7321
8103
|
try {
|
|
7322
8104
|
if (existsSync12(gitignorePath)) {
|
|
7323
8105
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -7351,13 +8133,13 @@ var init_tasks_crud = __esm({
|
|
|
7351
8133
|
});
|
|
7352
8134
|
|
|
7353
8135
|
// src/lib/tasks-review.ts
|
|
7354
|
-
import
|
|
8136
|
+
import path15 from "path";
|
|
7355
8137
|
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
7356
8138
|
async function countPendingReviews(sessionScope) {
|
|
7357
8139
|
const client = getClient();
|
|
7358
8140
|
if (sessionScope) {
|
|
7359
8141
|
const result2 = await client.execute({
|
|
7360
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
8142
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
7361
8143
|
args: [sessionScope]
|
|
7362
8144
|
});
|
|
7363
8145
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -7533,11 +8315,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
7533
8315
|
);
|
|
7534
8316
|
}
|
|
7535
8317
|
try {
|
|
7536
|
-
const cacheDir =
|
|
8318
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
7537
8319
|
if (existsSync13(cacheDir)) {
|
|
7538
8320
|
for (const f of readdirSync3(cacheDir)) {
|
|
7539
8321
|
if (f.startsWith("review-notified-")) {
|
|
7540
|
-
unlinkSync4(
|
|
8322
|
+
unlinkSync4(path15.join(cacheDir, f));
|
|
7541
8323
|
}
|
|
7542
8324
|
}
|
|
7543
8325
|
}
|
|
@@ -7558,7 +8340,7 @@ var init_tasks_review = __esm({
|
|
|
7558
8340
|
});
|
|
7559
8341
|
|
|
7560
8342
|
// src/lib/tasks-chain.ts
|
|
7561
|
-
import
|
|
8343
|
+
import path16 from "path";
|
|
7562
8344
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
7563
8345
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
7564
8346
|
const client = getClient();
|
|
@@ -7575,7 +8357,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
7575
8357
|
});
|
|
7576
8358
|
for (const ur of unblockedRows.rows) {
|
|
7577
8359
|
try {
|
|
7578
|
-
const ubFile =
|
|
8360
|
+
const ubFile = path16.join(baseDir, String(ur.task_file));
|
|
7579
8361
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
7580
8362
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
7581
8363
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -7644,7 +8426,7 @@ var init_tasks_chain = __esm({
|
|
|
7644
8426
|
|
|
7645
8427
|
// src/lib/project-name.ts
|
|
7646
8428
|
import { execSync as execSync5 } from "child_process";
|
|
7647
|
-
import
|
|
8429
|
+
import path17 from "path";
|
|
7648
8430
|
function getProjectName(cwd) {
|
|
7649
8431
|
const dir = cwd ?? process.cwd();
|
|
7650
8432
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -7657,7 +8439,7 @@ function getProjectName(cwd) {
|
|
|
7657
8439
|
timeout: 2e3,
|
|
7658
8440
|
stdio: ["pipe", "pipe", "pipe"]
|
|
7659
8441
|
}).trim();
|
|
7660
|
-
repoRoot =
|
|
8442
|
+
repoRoot = path17.dirname(gitCommonDir);
|
|
7661
8443
|
} catch {
|
|
7662
8444
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
7663
8445
|
cwd: dir,
|
|
@@ -7666,11 +8448,11 @@ function getProjectName(cwd) {
|
|
|
7666
8448
|
stdio: ["pipe", "pipe", "pipe"]
|
|
7667
8449
|
}).trim();
|
|
7668
8450
|
}
|
|
7669
|
-
_cached2 =
|
|
8451
|
+
_cached2 = path17.basename(repoRoot);
|
|
7670
8452
|
_cachedCwd = dir;
|
|
7671
8453
|
return _cached2;
|
|
7672
8454
|
} catch {
|
|
7673
|
-
_cached2 =
|
|
8455
|
+
_cached2 = path17.basename(dir);
|
|
7674
8456
|
_cachedCwd = dir;
|
|
7675
8457
|
return _cached2;
|
|
7676
8458
|
}
|
|
@@ -8143,7 +8925,7 @@ __export(tasks_exports, {
|
|
|
8143
8925
|
updateTaskStatus: () => updateTaskStatus,
|
|
8144
8926
|
writeCheckpoint: () => writeCheckpoint
|
|
8145
8927
|
});
|
|
8146
|
-
import
|
|
8928
|
+
import path18 from "path";
|
|
8147
8929
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
8148
8930
|
async function createTask(input) {
|
|
8149
8931
|
const result = await createTaskCore(input);
|
|
@@ -8163,8 +8945,8 @@ async function updateTask(input) {
|
|
|
8163
8945
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
8164
8946
|
try {
|
|
8165
8947
|
const agent = String(row.assigned_to);
|
|
8166
|
-
const cacheDir =
|
|
8167
|
-
const cachePath =
|
|
8948
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
8949
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
8168
8950
|
if (input.status === "in_progress") {
|
|
8169
8951
|
mkdirSync7(cacheDir, { recursive: true });
|
|
8170
8952
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -8635,12 +9417,12 @@ __export(tmux_routing_exports, {
|
|
|
8635
9417
|
});
|
|
8636
9418
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
8637
9419
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
8638
|
-
import
|
|
8639
|
-
import
|
|
9420
|
+
import path19 from "path";
|
|
9421
|
+
import os11 from "os";
|
|
8640
9422
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8641
9423
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
8642
9424
|
function spawnLockPath(sessionName) {
|
|
8643
|
-
return
|
|
9425
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
8644
9426
|
}
|
|
8645
9427
|
function isProcessAlive(pid) {
|
|
8646
9428
|
try {
|
|
@@ -8677,8 +9459,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
8677
9459
|
function resolveBehaviorsExporterScript() {
|
|
8678
9460
|
try {
|
|
8679
9461
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
8680
|
-
const scriptPath =
|
|
8681
|
-
|
|
9462
|
+
const scriptPath = path19.join(
|
|
9463
|
+
path19.dirname(thisFile),
|
|
8682
9464
|
"..",
|
|
8683
9465
|
"bin",
|
|
8684
9466
|
"exe-export-behaviors.js"
|
|
@@ -8753,7 +9535,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
8753
9535
|
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
8754
9536
|
}
|
|
8755
9537
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
8756
|
-
const filePath =
|
|
9538
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
8757
9539
|
writeFileSync7(filePath, JSON.stringify({
|
|
8758
9540
|
parentExe: rootExe,
|
|
8759
9541
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -8762,7 +9544,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
8762
9544
|
}
|
|
8763
9545
|
function getParentExe(sessionKey) {
|
|
8764
9546
|
try {
|
|
8765
|
-
const data = JSON.parse(readFileSync12(
|
|
9547
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
8766
9548
|
return data.parentExe || null;
|
|
8767
9549
|
} catch {
|
|
8768
9550
|
return null;
|
|
@@ -8771,7 +9553,7 @@ function getParentExe(sessionKey) {
|
|
|
8771
9553
|
function getDispatchedBy(sessionKey) {
|
|
8772
9554
|
try {
|
|
8773
9555
|
const data = JSON.parse(readFileSync12(
|
|
8774
|
-
|
|
9556
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
8775
9557
|
"utf8"
|
|
8776
9558
|
));
|
|
8777
9559
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -8957,7 +9739,7 @@ function sendIntercom(targetSession) {
|
|
|
8957
9739
|
try {
|
|
8958
9740
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8959
9741
|
const agent = baseAgentName(rawAgent);
|
|
8960
|
-
const markerPath =
|
|
9742
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
8961
9743
|
if (existsSync14(markerPath)) {
|
|
8962
9744
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
8963
9745
|
return "debounced";
|
|
@@ -8967,7 +9749,7 @@ function sendIntercom(targetSession) {
|
|
|
8967
9749
|
try {
|
|
8968
9750
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8969
9751
|
const agent = baseAgentName(rawAgent);
|
|
8970
|
-
const taskDir =
|
|
9752
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
8971
9753
|
if (existsSync14(taskDir)) {
|
|
8972
9754
|
const files = readdirSync4(taskDir).filter(
|
|
8973
9755
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -9101,8 +9883,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9101
9883
|
const transport = getTransport();
|
|
9102
9884
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
9103
9885
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
9104
|
-
const logDir =
|
|
9105
|
-
const logFile =
|
|
9886
|
+
const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
|
|
9887
|
+
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
9106
9888
|
if (!existsSync14(logDir)) {
|
|
9107
9889
|
mkdirSync8(logDir, { recursive: true });
|
|
9108
9890
|
}
|
|
@@ -9110,14 +9892,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9110
9892
|
let cleanupSuffix = "";
|
|
9111
9893
|
try {
|
|
9112
9894
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
9113
|
-
const cleanupScript =
|
|
9895
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
9114
9896
|
if (existsSync14(cleanupScript)) {
|
|
9115
9897
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
9116
9898
|
}
|
|
9117
9899
|
} catch {
|
|
9118
9900
|
}
|
|
9119
9901
|
try {
|
|
9120
|
-
const claudeJsonPath =
|
|
9902
|
+
const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
|
|
9121
9903
|
let claudeJson = {};
|
|
9122
9904
|
try {
|
|
9123
9905
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -9132,10 +9914,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9132
9914
|
} catch {
|
|
9133
9915
|
}
|
|
9134
9916
|
try {
|
|
9135
|
-
const settingsDir =
|
|
9917
|
+
const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
|
|
9136
9918
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
9137
|
-
const projSettingsDir =
|
|
9138
|
-
const settingsPath =
|
|
9919
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
9920
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
9139
9921
|
let settings = {};
|
|
9140
9922
|
try {
|
|
9141
9923
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -9182,8 +9964,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9182
9964
|
let behaviorsFlag = "";
|
|
9183
9965
|
let legacyFallbackWarned = false;
|
|
9184
9966
|
if (!useExeAgent && !useBinSymlink) {
|
|
9185
|
-
const identityPath =
|
|
9186
|
-
|
|
9967
|
+
const identityPath = path19.join(
|
|
9968
|
+
os11.homedir(),
|
|
9187
9969
|
".exe-os",
|
|
9188
9970
|
"identity",
|
|
9189
9971
|
`${employeeName}.md`
|
|
@@ -9198,7 +9980,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9198
9980
|
}
|
|
9199
9981
|
const behaviorsFile = exportBehaviorsSync(
|
|
9200
9982
|
employeeName,
|
|
9201
|
-
|
|
9983
|
+
path19.basename(spawnCwd),
|
|
9202
9984
|
sessionName
|
|
9203
9985
|
);
|
|
9204
9986
|
if (behaviorsFile) {
|
|
@@ -9213,9 +9995,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9213
9995
|
}
|
|
9214
9996
|
let sessionContextFlag = "";
|
|
9215
9997
|
try {
|
|
9216
|
-
const ctxDir =
|
|
9998
|
+
const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
9217
9999
|
mkdirSync8(ctxDir, { recursive: true });
|
|
9218
|
-
const ctxFile =
|
|
10000
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
9219
10001
|
const ctxContent = [
|
|
9220
10002
|
`## Session Context`,
|
|
9221
10003
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -9299,7 +10081,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9299
10081
|
transport.pipeLog(sessionName, logFile);
|
|
9300
10082
|
try {
|
|
9301
10083
|
const mySession = getMySession();
|
|
9302
|
-
const dispatchInfo =
|
|
10084
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
9303
10085
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
9304
10086
|
dispatchedBy: mySession,
|
|
9305
10087
|
rootExe: exeSession,
|
|
@@ -9374,15 +10156,15 @@ var init_tmux_routing = __esm({
|
|
|
9374
10156
|
init_intercom_queue();
|
|
9375
10157
|
init_plan_limits();
|
|
9376
10158
|
init_employees();
|
|
9377
|
-
SPAWN_LOCK_DIR =
|
|
9378
|
-
SESSION_CACHE =
|
|
10159
|
+
SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
10160
|
+
SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
9379
10161
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
9380
10162
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
9381
10163
|
VERIFY_PANE_LINES = 200;
|
|
9382
10164
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
9383
10165
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
9384
|
-
INTERCOM_LOG2 =
|
|
9385
|
-
DEBOUNCE_FILE =
|
|
10166
|
+
INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
10167
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
9386
10168
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
9387
10169
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
9388
10170
|
}
|
|
@@ -9642,8 +10424,8 @@ var init_messaging = __esm({
|
|
|
9642
10424
|
// src/automation/trigger-engine.ts
|
|
9643
10425
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
9644
10426
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
9645
|
-
import
|
|
9646
|
-
import
|
|
10427
|
+
import path20 from "path";
|
|
10428
|
+
import os12 from "os";
|
|
9647
10429
|
function substituteTemplate(template, record) {
|
|
9648
10430
|
return template.replace(
|
|
9649
10431
|
/\{\{(\w+(?:\.\w+)*)\}\}/g,
|
|
@@ -9938,7 +10720,7 @@ var TRIGGERS_PATH, GRAPH_API_VERSION;
|
|
|
9938
10720
|
var init_trigger_engine = __esm({
|
|
9939
10721
|
"src/automation/trigger-engine.ts"() {
|
|
9940
10722
|
"use strict";
|
|
9941
|
-
TRIGGERS_PATH =
|
|
10723
|
+
TRIGGERS_PATH = path20.join(os12.homedir(), ".exe-os", "triggers.json");
|
|
9942
10724
|
GRAPH_API_VERSION = "v21.0";
|
|
9943
10725
|
}
|
|
9944
10726
|
});
|
|
@@ -10002,8 +10784,8 @@ var init_crm_webhook = __esm({
|
|
|
10002
10784
|
|
|
10003
10785
|
// src/bin/exe-gateway.ts
|
|
10004
10786
|
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
10005
|
-
import
|
|
10006
|
-
import
|
|
10787
|
+
import path21 from "path";
|
|
10788
|
+
import os13 from "os";
|
|
10007
10789
|
|
|
10008
10790
|
// src/gateway/webhook-server.ts
|
|
10009
10791
|
import {
|
|
@@ -10969,8 +11751,8 @@ var BotRegistry = class {
|
|
|
10969
11751
|
|
|
10970
11752
|
// src/bin/exe-gateway.ts
|
|
10971
11753
|
init_employees();
|
|
10972
|
-
var CONFIG_DIR =
|
|
10973
|
-
var CONFIG_PATH3 =
|
|
11754
|
+
var CONFIG_DIR = path21.join(os13.homedir(), ".exe-os");
|
|
11755
|
+
var CONFIG_PATH3 = path21.join(CONFIG_DIR, "gateway.json");
|
|
10974
11756
|
var DEFAULT_PORT = 3100;
|
|
10975
11757
|
function loadConfig2() {
|
|
10976
11758
|
if (!existsSync16(CONFIG_PATH3)) {
|