@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/gateway/index.js
CHANGED
|
@@ -631,6 +631,118 @@ var init_config = __esm({
|
|
|
631
631
|
}
|
|
632
632
|
});
|
|
633
633
|
|
|
634
|
+
// src/lib/runtime-table.ts
|
|
635
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
636
|
+
var init_runtime_table = __esm({
|
|
637
|
+
"src/lib/runtime-table.ts"() {
|
|
638
|
+
"use strict";
|
|
639
|
+
RUNTIME_TABLE = {
|
|
640
|
+
codex: {
|
|
641
|
+
binary: "codex",
|
|
642
|
+
launchMode: "interactive",
|
|
643
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
644
|
+
inlineFlag: "--no-alt-screen",
|
|
645
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
646
|
+
defaultModel: "gpt-5.4"
|
|
647
|
+
},
|
|
648
|
+
opencode: {
|
|
649
|
+
binary: "opencode",
|
|
650
|
+
launchMode: "exec",
|
|
651
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
652
|
+
inlineFlag: "",
|
|
653
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
654
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
DEFAULT_RUNTIME = "claude";
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// src/lib/agent-config.ts
|
|
662
|
+
var agent_config_exports = {};
|
|
663
|
+
__export(agent_config_exports, {
|
|
664
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
665
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
666
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
667
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
668
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
669
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
670
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
671
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
672
|
+
setAgentRuntime: () => setAgentRuntime
|
|
673
|
+
});
|
|
674
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
675
|
+
import path2 from "path";
|
|
676
|
+
function loadAgentConfig() {
|
|
677
|
+
if (!existsSync2(AGENT_CONFIG_PATH)) return {};
|
|
678
|
+
try {
|
|
679
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
680
|
+
} catch {
|
|
681
|
+
return {};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function saveAgentConfig(config2) {
|
|
685
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
686
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
687
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
688
|
+
}
|
|
689
|
+
function getAgentRuntime(agentId) {
|
|
690
|
+
const config2 = loadAgentConfig();
|
|
691
|
+
const entry = config2[agentId];
|
|
692
|
+
if (entry) return entry;
|
|
693
|
+
const orgDefault = config2["default"];
|
|
694
|
+
if (orgDefault) return orgDefault;
|
|
695
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
696
|
+
}
|
|
697
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
698
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
699
|
+
if (!knownModels) {
|
|
700
|
+
return {
|
|
701
|
+
ok: false,
|
|
702
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
if (!knownModels.includes(model)) {
|
|
706
|
+
return {
|
|
707
|
+
ok: false,
|
|
708
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
const config2 = loadAgentConfig();
|
|
712
|
+
config2[agentId] = { runtime, model };
|
|
713
|
+
saveAgentConfig(config2);
|
|
714
|
+
return { ok: true };
|
|
715
|
+
}
|
|
716
|
+
function clearAgentRuntime(agentId) {
|
|
717
|
+
const config2 = loadAgentConfig();
|
|
718
|
+
delete config2[agentId];
|
|
719
|
+
saveAgentConfig(config2);
|
|
720
|
+
}
|
|
721
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
722
|
+
var init_agent_config = __esm({
|
|
723
|
+
"src/lib/agent-config.ts"() {
|
|
724
|
+
"use strict";
|
|
725
|
+
init_config();
|
|
726
|
+
init_runtime_table();
|
|
727
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
728
|
+
KNOWN_RUNTIMES = {
|
|
729
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
730
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
731
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
732
|
+
};
|
|
733
|
+
RUNTIME_LABELS = {
|
|
734
|
+
claude: "Claude Code (Anthropic)",
|
|
735
|
+
codex: "Codex (OpenAI)",
|
|
736
|
+
opencode: "OpenCode (open source)"
|
|
737
|
+
};
|
|
738
|
+
DEFAULT_MODELS = {
|
|
739
|
+
claude: "claude-opus-4",
|
|
740
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
741
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
|
|
634
746
|
// src/lib/employees.ts
|
|
635
747
|
var employees_exports = {};
|
|
636
748
|
__export(employees_exports, {
|
|
@@ -646,6 +758,7 @@ __export(employees_exports, {
|
|
|
646
758
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
647
759
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
648
760
|
hasRole: () => hasRole,
|
|
761
|
+
hireEmployee: () => hireEmployee,
|
|
649
762
|
isCoordinatorName: () => isCoordinatorName,
|
|
650
763
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
651
764
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -658,9 +771,9 @@ __export(employees_exports, {
|
|
|
658
771
|
validateEmployeeName: () => validateEmployeeName
|
|
659
772
|
});
|
|
660
773
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
661
|
-
import { existsSync as
|
|
774
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
662
775
|
import { execSync } from "child_process";
|
|
663
|
-
import
|
|
776
|
+
import path3 from "path";
|
|
664
777
|
import os2 from "os";
|
|
665
778
|
function normalizeRole(role) {
|
|
666
779
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -697,7 +810,7 @@ function validateEmployeeName(name) {
|
|
|
697
810
|
return { valid: true };
|
|
698
811
|
}
|
|
699
812
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
700
|
-
if (!
|
|
813
|
+
if (!existsSync3(employeesPath)) {
|
|
701
814
|
return [];
|
|
702
815
|
}
|
|
703
816
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -708,13 +821,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
708
821
|
}
|
|
709
822
|
}
|
|
710
823
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
711
|
-
await mkdir2(
|
|
824
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
712
825
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
713
826
|
}
|
|
714
827
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
715
|
-
if (!
|
|
828
|
+
if (!existsSync3(employeesPath)) return [];
|
|
716
829
|
try {
|
|
717
|
-
return JSON.parse(
|
|
830
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
718
831
|
} catch {
|
|
719
832
|
return [];
|
|
720
833
|
}
|
|
@@ -756,6 +869,52 @@ function addEmployee(employees, employee) {
|
|
|
756
869
|
}
|
|
757
870
|
return [...employees, normalized];
|
|
758
871
|
}
|
|
872
|
+
function appendToCoordinatorTeam(employee) {
|
|
873
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
874
|
+
if (!coordinator) return;
|
|
875
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
876
|
+
if (!existsSync3(idPath)) return;
|
|
877
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
878
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
879
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
880
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
881
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
882
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
883
|
+
const entry = `
|
|
884
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
885
|
+
`;
|
|
886
|
+
let updated;
|
|
887
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
888
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
889
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
890
|
+
} else {
|
|
891
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
892
|
+
}
|
|
893
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
894
|
+
}
|
|
895
|
+
function capitalize(s) {
|
|
896
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
897
|
+
}
|
|
898
|
+
async function hireEmployee(employee) {
|
|
899
|
+
const employees = await loadEmployees();
|
|
900
|
+
const updated = addEmployee(employees, employee);
|
|
901
|
+
await saveEmployees(updated);
|
|
902
|
+
try {
|
|
903
|
+
appendToCoordinatorTeam(employee);
|
|
904
|
+
} catch {
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
908
|
+
const config2 = loadAgentConfig2();
|
|
909
|
+
const name = employee.name.toLowerCase();
|
|
910
|
+
if (!config2[name] && config2["default"]) {
|
|
911
|
+
config2[name] = { ...config2["default"] };
|
|
912
|
+
saveAgentConfig2(config2);
|
|
913
|
+
}
|
|
914
|
+
} catch {
|
|
915
|
+
}
|
|
916
|
+
return updated;
|
|
917
|
+
}
|
|
759
918
|
async function normalizeRosterCase(rosterPath) {
|
|
760
919
|
const employees = await loadEmployees(rosterPath);
|
|
761
920
|
let changed = false;
|
|
@@ -765,14 +924,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
765
924
|
emp.name = emp.name.toLowerCase();
|
|
766
925
|
changed = true;
|
|
767
926
|
try {
|
|
768
|
-
const identityDir =
|
|
769
|
-
const oldPath =
|
|
770
|
-
const newPath =
|
|
771
|
-
if (
|
|
927
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
928
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
929
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
930
|
+
if (existsSync3(oldPath) && !existsSync3(newPath)) {
|
|
772
931
|
renameSync2(oldPath, newPath);
|
|
773
|
-
} else if (
|
|
774
|
-
const content =
|
|
775
|
-
|
|
932
|
+
} else if (existsSync3(oldPath) && oldPath !== newPath) {
|
|
933
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
934
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
776
935
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
777
936
|
unlinkSync(oldPath);
|
|
778
937
|
}
|
|
@@ -802,7 +961,7 @@ function registerBinSymlinks(name) {
|
|
|
802
961
|
errors.push("Could not find 'exe-os' in PATH");
|
|
803
962
|
return { created, skipped, errors };
|
|
804
963
|
}
|
|
805
|
-
const binDir =
|
|
964
|
+
const binDir = path3.dirname(exeBinPath);
|
|
806
965
|
let target;
|
|
807
966
|
try {
|
|
808
967
|
target = readlinkSync(exeBinPath);
|
|
@@ -812,8 +971,8 @@ function registerBinSymlinks(name) {
|
|
|
812
971
|
}
|
|
813
972
|
for (const suffix of ["", "-opencode"]) {
|
|
814
973
|
const linkName = `${name}${suffix}`;
|
|
815
|
-
const linkPath =
|
|
816
|
-
if (
|
|
974
|
+
const linkPath = path3.join(binDir, linkName);
|
|
975
|
+
if (existsSync3(linkPath)) {
|
|
817
976
|
skipped.push(linkName);
|
|
818
977
|
continue;
|
|
819
978
|
}
|
|
@@ -826,21 +985,619 @@ function registerBinSymlinks(name) {
|
|
|
826
985
|
}
|
|
827
986
|
return { created, skipped, errors };
|
|
828
987
|
}
|
|
829
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
988
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
830
989
|
var init_employees = __esm({
|
|
831
990
|
"src/lib/employees.ts"() {
|
|
832
991
|
"use strict";
|
|
833
992
|
init_config();
|
|
834
|
-
EMPLOYEES_PATH =
|
|
993
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
835
994
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
836
995
|
COORDINATOR_ROLE = "COO";
|
|
837
996
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
997
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
998
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
// src/lib/database-adapter.ts
|
|
1003
|
+
import os3 from "os";
|
|
1004
|
+
import path4 from "path";
|
|
1005
|
+
import { createRequire } from "module";
|
|
1006
|
+
import { pathToFileURL } from "url";
|
|
1007
|
+
function quotedIdentifier(identifier) {
|
|
1008
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1009
|
+
}
|
|
1010
|
+
function unqualifiedTableName(name) {
|
|
1011
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1012
|
+
const parts = raw.split(".");
|
|
1013
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1014
|
+
}
|
|
1015
|
+
function stripTrailingSemicolon(sql) {
|
|
1016
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1017
|
+
}
|
|
1018
|
+
function appendClause(sql, clause) {
|
|
1019
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1020
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1021
|
+
if (!returningMatch) {
|
|
1022
|
+
return `${trimmed}${clause}`;
|
|
1023
|
+
}
|
|
1024
|
+
const idx = returningMatch.index;
|
|
1025
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1026
|
+
}
|
|
1027
|
+
function normalizeStatement(stmt) {
|
|
1028
|
+
if (typeof stmt === "string") {
|
|
1029
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1030
|
+
}
|
|
1031
|
+
const sql = stmt.sql;
|
|
1032
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1033
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1034
|
+
}
|
|
1035
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1036
|
+
}
|
|
1037
|
+
function rewriteBooleanLiterals(sql) {
|
|
1038
|
+
let out = sql;
|
|
1039
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1040
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1041
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1042
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1043
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1044
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1045
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1046
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1047
|
+
}
|
|
1048
|
+
return out;
|
|
1049
|
+
}
|
|
1050
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1051
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1052
|
+
return sql;
|
|
1053
|
+
}
|
|
1054
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1055
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1056
|
+
}
|
|
1057
|
+
function rewriteInsertOrReplace(sql) {
|
|
1058
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1059
|
+
if (!match) {
|
|
1060
|
+
return sql;
|
|
1061
|
+
}
|
|
1062
|
+
const rawTable = match[1];
|
|
1063
|
+
const rawColumns = match[2];
|
|
1064
|
+
const remainder = match[3];
|
|
1065
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1066
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1067
|
+
if (!conflictKeys?.length) {
|
|
1068
|
+
return sql;
|
|
1069
|
+
}
|
|
1070
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1071
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1072
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1073
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1074
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1075
|
+
}
|
|
1076
|
+
function rewriteSql(sql) {
|
|
1077
|
+
let out = sql;
|
|
1078
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1079
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1080
|
+
out = rewriteBooleanLiterals(out);
|
|
1081
|
+
out = rewriteInsertOrReplace(out);
|
|
1082
|
+
out = rewriteInsertOrIgnore(out);
|
|
1083
|
+
return stripTrailingSemicolon(out);
|
|
1084
|
+
}
|
|
1085
|
+
function toBoolean(value) {
|
|
1086
|
+
if (value === null || value === void 0) return value;
|
|
1087
|
+
if (typeof value === "boolean") return value;
|
|
1088
|
+
if (typeof value === "number") return value !== 0;
|
|
1089
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1090
|
+
if (typeof value === "string") {
|
|
1091
|
+
const normalized = value.trim().toLowerCase();
|
|
1092
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1093
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1094
|
+
}
|
|
1095
|
+
return Boolean(value);
|
|
1096
|
+
}
|
|
1097
|
+
function countQuestionMarks(sql, end) {
|
|
1098
|
+
let count = 0;
|
|
1099
|
+
let inSingle = false;
|
|
1100
|
+
let inDouble = false;
|
|
1101
|
+
let inLineComment = false;
|
|
1102
|
+
let inBlockComment = false;
|
|
1103
|
+
for (let i = 0; i < end; i++) {
|
|
1104
|
+
const ch = sql[i];
|
|
1105
|
+
const next = sql[i + 1];
|
|
1106
|
+
if (inLineComment) {
|
|
1107
|
+
if (ch === "\n") inLineComment = false;
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (inBlockComment) {
|
|
1111
|
+
if (ch === "*" && next === "/") {
|
|
1112
|
+
inBlockComment = false;
|
|
1113
|
+
i += 1;
|
|
1114
|
+
}
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1118
|
+
inLineComment = true;
|
|
1119
|
+
i += 1;
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1123
|
+
inBlockComment = true;
|
|
1124
|
+
i += 1;
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1128
|
+
inSingle = !inSingle;
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1132
|
+
inDouble = !inDouble;
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1136
|
+
count += 1;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return count;
|
|
1140
|
+
}
|
|
1141
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1142
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1143
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1144
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1145
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1146
|
+
const matchText = match[0];
|
|
1147
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1148
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return indexes;
|
|
1152
|
+
}
|
|
1153
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1154
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1155
|
+
if (!match) return;
|
|
1156
|
+
const rawTable = match[1];
|
|
1157
|
+
const rawColumns = match[2];
|
|
1158
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1159
|
+
if (!boolColumns?.size) return;
|
|
1160
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1161
|
+
for (const [index, column] of columns.entries()) {
|
|
1162
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1163
|
+
args[index] = toBoolean(args[index]);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1168
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1169
|
+
if (!match) return;
|
|
1170
|
+
const rawTable = match[1];
|
|
1171
|
+
const setClause = match[2];
|
|
1172
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1173
|
+
if (!boolColumns?.size) return;
|
|
1174
|
+
const assignments = setClause.split(",");
|
|
1175
|
+
let placeholderIndex = 0;
|
|
1176
|
+
for (const assignment of assignments) {
|
|
1177
|
+
if (!assignment.includes("?")) continue;
|
|
1178
|
+
placeholderIndex += 1;
|
|
1179
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1180
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1181
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function coerceBooleanArgs(sql, args) {
|
|
1186
|
+
const nextArgs = [...args];
|
|
1187
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1188
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1189
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1190
|
+
for (const index of placeholderIndexes) {
|
|
1191
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1192
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return nextArgs;
|
|
1196
|
+
}
|
|
1197
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1198
|
+
let out = "";
|
|
1199
|
+
let placeholder = 0;
|
|
1200
|
+
let inSingle = false;
|
|
1201
|
+
let inDouble = false;
|
|
1202
|
+
let inLineComment = false;
|
|
1203
|
+
let inBlockComment = false;
|
|
1204
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1205
|
+
const ch = sql[i];
|
|
1206
|
+
const next = sql[i + 1];
|
|
1207
|
+
if (inLineComment) {
|
|
1208
|
+
out += ch;
|
|
1209
|
+
if (ch === "\n") inLineComment = false;
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (inBlockComment) {
|
|
1213
|
+
out += ch;
|
|
1214
|
+
if (ch === "*" && next === "/") {
|
|
1215
|
+
out += next;
|
|
1216
|
+
inBlockComment = false;
|
|
1217
|
+
i += 1;
|
|
1218
|
+
}
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1222
|
+
out += ch + next;
|
|
1223
|
+
inLineComment = true;
|
|
1224
|
+
i += 1;
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1228
|
+
out += ch + next;
|
|
1229
|
+
inBlockComment = true;
|
|
1230
|
+
i += 1;
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1234
|
+
inSingle = !inSingle;
|
|
1235
|
+
out += ch;
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1239
|
+
inDouble = !inDouble;
|
|
1240
|
+
out += ch;
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1244
|
+
placeholder += 1;
|
|
1245
|
+
out += `$${placeholder}`;
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
out += ch;
|
|
1249
|
+
}
|
|
1250
|
+
return out;
|
|
1251
|
+
}
|
|
1252
|
+
function translateStatementForPostgres(stmt) {
|
|
1253
|
+
const normalized = normalizeStatement(stmt);
|
|
1254
|
+
if (normalized.kind === "named") {
|
|
1255
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1256
|
+
}
|
|
1257
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1258
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1259
|
+
return {
|
|
1260
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1261
|
+
args: coercedArgs
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
function shouldBypassPostgres(stmt) {
|
|
1265
|
+
const normalized = normalizeStatement(stmt);
|
|
1266
|
+
if (normalized.kind === "named") {
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1270
|
+
}
|
|
1271
|
+
function shouldFallbackOnError(error) {
|
|
1272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1273
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1274
|
+
}
|
|
1275
|
+
function isReadQuery(sql) {
|
|
1276
|
+
const trimmed = sql.trimStart();
|
|
1277
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1278
|
+
}
|
|
1279
|
+
function buildRow(row, columns) {
|
|
1280
|
+
const values = columns.map((column) => row[column]);
|
|
1281
|
+
return Object.assign(values, row);
|
|
1282
|
+
}
|
|
1283
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1284
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1285
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1286
|
+
return {
|
|
1287
|
+
columns,
|
|
1288
|
+
columnTypes: columns.map(() => ""),
|
|
1289
|
+
rows: resultRows,
|
|
1290
|
+
rowsAffected,
|
|
1291
|
+
lastInsertRowid: void 0,
|
|
1292
|
+
toJSON() {
|
|
1293
|
+
return {
|
|
1294
|
+
columns,
|
|
1295
|
+
columnTypes: columns.map(() => ""),
|
|
1296
|
+
rows,
|
|
1297
|
+
rowsAffected,
|
|
1298
|
+
lastInsertRowid: void 0
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
async function loadPrismaClient() {
|
|
1304
|
+
if (!prismaClientPromise) {
|
|
1305
|
+
prismaClientPromise = (async () => {
|
|
1306
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1307
|
+
if (explicitPath) {
|
|
1308
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1309
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1310
|
+
if (!PrismaClient2) {
|
|
1311
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1312
|
+
}
|
|
1313
|
+
return new PrismaClient2();
|
|
1314
|
+
}
|
|
1315
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1316
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
1317
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1318
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1319
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1320
|
+
if (!PrismaClient) {
|
|
1321
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1322
|
+
}
|
|
1323
|
+
return new PrismaClient();
|
|
1324
|
+
})();
|
|
1325
|
+
}
|
|
1326
|
+
return prismaClientPromise;
|
|
1327
|
+
}
|
|
1328
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1329
|
+
if (!compatibilityBootstrapPromise) {
|
|
1330
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1331
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1332
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1333
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1334
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1335
|
+
relation
|
|
1336
|
+
);
|
|
1337
|
+
if (!rows[0]?.regclass) {
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
await prisma.$executeRawUnsafe(
|
|
1341
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
})();
|
|
1345
|
+
}
|
|
1346
|
+
return compatibilityBootstrapPromise;
|
|
1347
|
+
}
|
|
1348
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1349
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1350
|
+
if (isReadQuery(translated.sql)) {
|
|
1351
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1352
|
+
translated.sql,
|
|
1353
|
+
...translated.args
|
|
1354
|
+
);
|
|
1355
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1356
|
+
}
|
|
1357
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1358
|
+
return buildResultSet([], rowsAffected);
|
|
1359
|
+
}
|
|
1360
|
+
function splitSqlStatements(sql) {
|
|
1361
|
+
const parts = [];
|
|
1362
|
+
let current = "";
|
|
1363
|
+
let inSingle = false;
|
|
1364
|
+
let inDouble = false;
|
|
1365
|
+
let inLineComment = false;
|
|
1366
|
+
let inBlockComment = false;
|
|
1367
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1368
|
+
const ch = sql[i];
|
|
1369
|
+
const next = sql[i + 1];
|
|
1370
|
+
if (inLineComment) {
|
|
1371
|
+
current += ch;
|
|
1372
|
+
if (ch === "\n") inLineComment = false;
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
if (inBlockComment) {
|
|
1376
|
+
current += ch;
|
|
1377
|
+
if (ch === "*" && next === "/") {
|
|
1378
|
+
current += next;
|
|
1379
|
+
inBlockComment = false;
|
|
1380
|
+
i += 1;
|
|
1381
|
+
}
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1385
|
+
current += ch + next;
|
|
1386
|
+
inLineComment = true;
|
|
1387
|
+
i += 1;
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1391
|
+
current += ch + next;
|
|
1392
|
+
inBlockComment = true;
|
|
1393
|
+
i += 1;
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1397
|
+
inSingle = !inSingle;
|
|
1398
|
+
current += ch;
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1402
|
+
inDouble = !inDouble;
|
|
1403
|
+
current += ch;
|
|
1404
|
+
continue;
|
|
1405
|
+
}
|
|
1406
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1407
|
+
if (current.trim()) {
|
|
1408
|
+
parts.push(current.trim());
|
|
1409
|
+
}
|
|
1410
|
+
current = "";
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
current += ch;
|
|
1414
|
+
}
|
|
1415
|
+
if (current.trim()) {
|
|
1416
|
+
parts.push(current.trim());
|
|
1417
|
+
}
|
|
1418
|
+
return parts;
|
|
1419
|
+
}
|
|
1420
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1421
|
+
const prisma = await loadPrismaClient();
|
|
1422
|
+
await ensureCompatibilityViews(prisma);
|
|
1423
|
+
let closed = false;
|
|
1424
|
+
let adapter;
|
|
1425
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1426
|
+
if (!fallbackClient) {
|
|
1427
|
+
if (error) throw error;
|
|
1428
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1429
|
+
}
|
|
1430
|
+
if (error) {
|
|
1431
|
+
process.stderr.write(
|
|
1432
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1433
|
+
`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
return fallbackClient.execute(stmt);
|
|
1437
|
+
};
|
|
1438
|
+
adapter = {
|
|
1439
|
+
async execute(stmt) {
|
|
1440
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1441
|
+
return fallbackExecute(stmt);
|
|
1442
|
+
}
|
|
1443
|
+
try {
|
|
1444
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
if (shouldFallbackOnError(error)) {
|
|
1447
|
+
return fallbackExecute(stmt, error);
|
|
1448
|
+
}
|
|
1449
|
+
throw error;
|
|
1450
|
+
}
|
|
1451
|
+
},
|
|
1452
|
+
async batch(stmts, mode) {
|
|
1453
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1454
|
+
if (!fallbackClient) {
|
|
1455
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1456
|
+
}
|
|
1457
|
+
return fallbackClient.batch(stmts, mode);
|
|
1458
|
+
}
|
|
1459
|
+
try {
|
|
1460
|
+
if (prisma.$transaction) {
|
|
1461
|
+
return await prisma.$transaction(async (tx) => {
|
|
1462
|
+
const results2 = [];
|
|
1463
|
+
for (const stmt of stmts) {
|
|
1464
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1465
|
+
}
|
|
1466
|
+
return results2;
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
const results = [];
|
|
1470
|
+
for (const stmt of stmts) {
|
|
1471
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1472
|
+
}
|
|
1473
|
+
return results;
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1476
|
+
process.stderr.write(
|
|
1477
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1478
|
+
`
|
|
1479
|
+
);
|
|
1480
|
+
return fallbackClient.batch(stmts, mode);
|
|
1481
|
+
}
|
|
1482
|
+
throw error;
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
async migrate(stmts) {
|
|
1486
|
+
if (fallbackClient) {
|
|
1487
|
+
return fallbackClient.migrate(stmts);
|
|
1488
|
+
}
|
|
1489
|
+
return adapter.batch(stmts, "deferred");
|
|
1490
|
+
},
|
|
1491
|
+
async transaction(mode) {
|
|
1492
|
+
if (!fallbackClient) {
|
|
1493
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1494
|
+
}
|
|
1495
|
+
return fallbackClient.transaction(mode);
|
|
1496
|
+
},
|
|
1497
|
+
async executeMultiple(sql) {
|
|
1498
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1499
|
+
return fallbackClient.executeMultiple(sql);
|
|
1500
|
+
}
|
|
1501
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1502
|
+
await adapter.execute(statement);
|
|
1503
|
+
}
|
|
1504
|
+
},
|
|
1505
|
+
async sync() {
|
|
1506
|
+
if (fallbackClient) {
|
|
1507
|
+
return fallbackClient.sync();
|
|
1508
|
+
}
|
|
1509
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1510
|
+
},
|
|
1511
|
+
close() {
|
|
1512
|
+
closed = true;
|
|
1513
|
+
prismaClientPromise = null;
|
|
1514
|
+
compatibilityBootstrapPromise = null;
|
|
1515
|
+
void prisma.$disconnect?.();
|
|
1516
|
+
},
|
|
1517
|
+
get closed() {
|
|
1518
|
+
return closed;
|
|
1519
|
+
},
|
|
1520
|
+
get protocol() {
|
|
1521
|
+
return "prisma-postgres";
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
return adapter;
|
|
1525
|
+
}
|
|
1526
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1527
|
+
var init_database_adapter = __esm({
|
|
1528
|
+
"src/lib/database-adapter.ts"() {
|
|
1529
|
+
"use strict";
|
|
1530
|
+
VIEW_MAPPINGS = [
|
|
1531
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1532
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1533
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1534
|
+
{ view: "entities", source: "memory.entities" },
|
|
1535
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1536
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1537
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1538
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1539
|
+
{ view: "messages", source: "memory.messages" },
|
|
1540
|
+
{ view: "users", source: "wiki.users" },
|
|
1541
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1542
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1543
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1544
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1545
|
+
];
|
|
1546
|
+
UPSERT_KEYS = {
|
|
1547
|
+
memories: ["id"],
|
|
1548
|
+
tasks: ["id"],
|
|
1549
|
+
behaviors: ["id"],
|
|
1550
|
+
entities: ["id"],
|
|
1551
|
+
relationships: ["id"],
|
|
1552
|
+
entity_aliases: ["alias"],
|
|
1553
|
+
notifications: ["id"],
|
|
1554
|
+
messages: ["id"],
|
|
1555
|
+
users: ["id"],
|
|
1556
|
+
workspaces: ["id"],
|
|
1557
|
+
workspace_users: ["id"],
|
|
1558
|
+
documents: ["id"],
|
|
1559
|
+
chats: ["id"]
|
|
1560
|
+
};
|
|
1561
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1562
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1563
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1564
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1565
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1566
|
+
};
|
|
1567
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1568
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1569
|
+
);
|
|
1570
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1571
|
+
/\bPRAGMA\b/i,
|
|
1572
|
+
/\bsqlite_master\b/i,
|
|
1573
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1574
|
+
/\bMATCH\b/i,
|
|
1575
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1576
|
+
/\bjson_extract\s*\(/i,
|
|
1577
|
+
/\bjulianday\s*\(/i,
|
|
1578
|
+
/\bstrftime\s*\(/i,
|
|
1579
|
+
/\blast_insert_rowid\s*\(/i
|
|
1580
|
+
];
|
|
1581
|
+
prismaClientPromise = null;
|
|
1582
|
+
compatibilityBootstrapPromise = null;
|
|
838
1583
|
}
|
|
839
1584
|
});
|
|
840
1585
|
|
|
841
1586
|
// src/lib/database.ts
|
|
842
1587
|
import { createClient } from "@libsql/client";
|
|
843
1588
|
async function initDatabase(config2) {
|
|
1589
|
+
if (_walCheckpointTimer) {
|
|
1590
|
+
clearInterval(_walCheckpointTimer);
|
|
1591
|
+
_walCheckpointTimer = null;
|
|
1592
|
+
}
|
|
1593
|
+
if (_daemonClient) {
|
|
1594
|
+
_daemonClient.close();
|
|
1595
|
+
_daemonClient = null;
|
|
1596
|
+
}
|
|
1597
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1598
|
+
_adapterClient.close();
|
|
1599
|
+
}
|
|
1600
|
+
_adapterClient = null;
|
|
844
1601
|
if (_client) {
|
|
845
1602
|
_client.close();
|
|
846
1603
|
_client = null;
|
|
@@ -854,6 +1611,7 @@ async function initDatabase(config2) {
|
|
|
854
1611
|
}
|
|
855
1612
|
_client = createClient(opts);
|
|
856
1613
|
_resilientClient = wrapWithRetry(_client);
|
|
1614
|
+
_adapterClient = _resilientClient;
|
|
857
1615
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
858
1616
|
});
|
|
859
1617
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -864,11 +1622,17 @@ async function initDatabase(config2) {
|
|
|
864
1622
|
});
|
|
865
1623
|
}, 3e4);
|
|
866
1624
|
_walCheckpointTimer.unref();
|
|
1625
|
+
if (process.env.DATABASE_URL) {
|
|
1626
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1627
|
+
}
|
|
867
1628
|
}
|
|
868
1629
|
function getClient() {
|
|
869
|
-
if (!
|
|
1630
|
+
if (!_adapterClient) {
|
|
870
1631
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
871
1632
|
}
|
|
1633
|
+
if (process.env.DATABASE_URL) {
|
|
1634
|
+
return _adapterClient;
|
|
1635
|
+
}
|
|
872
1636
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
873
1637
|
return _resilientClient;
|
|
874
1638
|
}
|
|
@@ -1809,26 +2573,36 @@ async function ensureSchema() {
|
|
|
1809
2573
|
}
|
|
1810
2574
|
}
|
|
1811
2575
|
async function disposeDatabase() {
|
|
2576
|
+
if (_walCheckpointTimer) {
|
|
2577
|
+
clearInterval(_walCheckpointTimer);
|
|
2578
|
+
_walCheckpointTimer = null;
|
|
2579
|
+
}
|
|
1812
2580
|
if (_daemonClient) {
|
|
1813
2581
|
_daemonClient.close();
|
|
1814
2582
|
_daemonClient = null;
|
|
1815
2583
|
}
|
|
2584
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2585
|
+
_adapterClient.close();
|
|
2586
|
+
}
|
|
2587
|
+
_adapterClient = null;
|
|
1816
2588
|
if (_client) {
|
|
1817
2589
|
_client.close();
|
|
1818
2590
|
_client = null;
|
|
1819
2591
|
_resilientClient = null;
|
|
1820
2592
|
}
|
|
1821
2593
|
}
|
|
1822
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2594
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1823
2595
|
var init_database = __esm({
|
|
1824
2596
|
"src/lib/database.ts"() {
|
|
1825
2597
|
"use strict";
|
|
1826
2598
|
init_db_retry();
|
|
1827
2599
|
init_employees();
|
|
2600
|
+
init_database_adapter();
|
|
1828
2601
|
_client = null;
|
|
1829
2602
|
_resilientClient = null;
|
|
1830
2603
|
_walCheckpointTimer = null;
|
|
1831
2604
|
_daemonClient = null;
|
|
2605
|
+
_adapterClient = null;
|
|
1832
2606
|
initTurso = initDatabase;
|
|
1833
2607
|
disposeTurso = disposeDatabase;
|
|
1834
2608
|
}
|
|
@@ -1845,11 +2619,11 @@ var init_memory = __esm({
|
|
|
1845
2619
|
|
|
1846
2620
|
// src/lib/exe-daemon-client.ts
|
|
1847
2621
|
import net from "net";
|
|
1848
|
-
import
|
|
2622
|
+
import os4 from "os";
|
|
1849
2623
|
import { spawn } from "child_process";
|
|
1850
2624
|
import { randomUUID } from "crypto";
|
|
1851
|
-
import { existsSync as
|
|
1852
|
-
import
|
|
2625
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
2626
|
+
import path5 from "path";
|
|
1853
2627
|
import { fileURLToPath } from "url";
|
|
1854
2628
|
function handleData(chunk) {
|
|
1855
2629
|
_buffer += chunk.toString();
|
|
@@ -1877,9 +2651,9 @@ function handleData(chunk) {
|
|
|
1877
2651
|
}
|
|
1878
2652
|
}
|
|
1879
2653
|
function cleanupStaleFiles() {
|
|
1880
|
-
if (
|
|
2654
|
+
if (existsSync4(PID_PATH)) {
|
|
1881
2655
|
try {
|
|
1882
|
-
const pid = parseInt(
|
|
2656
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1883
2657
|
if (pid > 0) {
|
|
1884
2658
|
try {
|
|
1885
2659
|
process.kill(pid, 0);
|
|
@@ -1900,17 +2674,17 @@ function cleanupStaleFiles() {
|
|
|
1900
2674
|
}
|
|
1901
2675
|
}
|
|
1902
2676
|
function findPackageRoot() {
|
|
1903
|
-
let dir =
|
|
1904
|
-
const { root } =
|
|
2677
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2678
|
+
const { root } = path5.parse(dir);
|
|
1905
2679
|
while (dir !== root) {
|
|
1906
|
-
if (
|
|
1907
|
-
dir =
|
|
2680
|
+
if (existsSync4(path5.join(dir, "package.json"))) return dir;
|
|
2681
|
+
dir = path5.dirname(dir);
|
|
1908
2682
|
}
|
|
1909
2683
|
return null;
|
|
1910
2684
|
}
|
|
1911
2685
|
function spawnDaemon() {
|
|
1912
|
-
const freeGB =
|
|
1913
|
-
const totalGB =
|
|
2686
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
2687
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1914
2688
|
if (totalGB <= 8) {
|
|
1915
2689
|
process.stderr.write(
|
|
1916
2690
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -1930,8 +2704,8 @@ function spawnDaemon() {
|
|
|
1930
2704
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1931
2705
|
return;
|
|
1932
2706
|
}
|
|
1933
|
-
const daemonPath =
|
|
1934
|
-
if (!
|
|
2707
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2708
|
+
if (!existsSync4(daemonPath)) {
|
|
1935
2709
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1936
2710
|
`);
|
|
1937
2711
|
return;
|
|
@@ -1939,7 +2713,7 @@ function spawnDaemon() {
|
|
|
1939
2713
|
const resolvedPath = daemonPath;
|
|
1940
2714
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1941
2715
|
`);
|
|
1942
|
-
const logPath =
|
|
2716
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1943
2717
|
let stderrFd = "ignore";
|
|
1944
2718
|
try {
|
|
1945
2719
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2090,74 +2864,123 @@ async function pingDaemon() {
|
|
|
2090
2864
|
return null;
|
|
2091
2865
|
}
|
|
2092
2866
|
function killAndRespawnDaemon() {
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2867
|
+
if (!acquireSpawnLock()) {
|
|
2868
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
2869
|
+
if (_socket) {
|
|
2870
|
+
_socket.destroy();
|
|
2871
|
+
_socket = null;
|
|
2872
|
+
}
|
|
2873
|
+
_connected = false;
|
|
2874
|
+
_buffer = "";
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
try {
|
|
2878
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2879
|
+
if (existsSync4(PID_PATH)) {
|
|
2880
|
+
try {
|
|
2881
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2882
|
+
if (pid > 0) {
|
|
2883
|
+
try {
|
|
2884
|
+
process.kill(pid, "SIGKILL");
|
|
2885
|
+
} catch {
|
|
2886
|
+
}
|
|
2101
2887
|
}
|
|
2888
|
+
} catch {
|
|
2102
2889
|
}
|
|
2890
|
+
}
|
|
2891
|
+
if (_socket) {
|
|
2892
|
+
_socket.destroy();
|
|
2893
|
+
_socket = null;
|
|
2894
|
+
}
|
|
2895
|
+
_connected = false;
|
|
2896
|
+
_buffer = "";
|
|
2897
|
+
try {
|
|
2898
|
+
unlinkSync2(PID_PATH);
|
|
2103
2899
|
} catch {
|
|
2104
2900
|
}
|
|
2901
|
+
try {
|
|
2902
|
+
unlinkSync2(SOCKET_PATH);
|
|
2903
|
+
} catch {
|
|
2904
|
+
}
|
|
2905
|
+
spawnDaemon();
|
|
2906
|
+
} finally {
|
|
2907
|
+
releaseSpawnLock();
|
|
2105
2908
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
_socket = null;
|
|
2109
|
-
}
|
|
2110
|
-
_connected = false;
|
|
2111
|
-
_buffer = "";
|
|
2909
|
+
}
|
|
2910
|
+
function isDaemonTooYoung() {
|
|
2112
2911
|
try {
|
|
2113
|
-
|
|
2912
|
+
const stat = statSync(PID_PATH);
|
|
2913
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
2114
2914
|
} catch {
|
|
2915
|
+
return false;
|
|
2115
2916
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2917
|
+
}
|
|
2918
|
+
async function retryThenRestart(doRequest, label) {
|
|
2919
|
+
const result = await doRequest();
|
|
2920
|
+
if (!result.error) {
|
|
2921
|
+
_consecutiveFailures = 0;
|
|
2922
|
+
return result;
|
|
2923
|
+
}
|
|
2924
|
+
_consecutiveFailures++;
|
|
2925
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
2926
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
2927
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
2928
|
+
`);
|
|
2929
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
2930
|
+
if (!_connected) {
|
|
2931
|
+
if (!await connectToSocket()) continue;
|
|
2932
|
+
}
|
|
2933
|
+
const retry = await doRequest();
|
|
2934
|
+
if (!retry.error) {
|
|
2935
|
+
_consecutiveFailures = 0;
|
|
2936
|
+
return retry;
|
|
2937
|
+
}
|
|
2938
|
+
_consecutiveFailures++;
|
|
2939
|
+
}
|
|
2940
|
+
if (isDaemonTooYoung()) {
|
|
2941
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
2942
|
+
`);
|
|
2943
|
+
return { error: result.error };
|
|
2944
|
+
}
|
|
2945
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
2946
|
+
`);
|
|
2947
|
+
killAndRespawnDaemon();
|
|
2948
|
+
const start = Date.now();
|
|
2949
|
+
let delay2 = 200;
|
|
2950
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2951
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2952
|
+
if (await connectToSocket()) break;
|
|
2953
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2119
2954
|
}
|
|
2120
|
-
|
|
2955
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
2956
|
+
const final = await doRequest();
|
|
2957
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
2958
|
+
return final;
|
|
2121
2959
|
}
|
|
2122
2960
|
async function embedViaClient(text, priority = "high") {
|
|
2123
2961
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
2124
2962
|
_requestCount++;
|
|
2125
2963
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
2126
2964
|
const health = await pingDaemon();
|
|
2127
|
-
if (!health) {
|
|
2965
|
+
if (!health && !isDaemonTooYoung()) {
|
|
2128
2966
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
2129
2967
|
`);
|
|
2130
2968
|
killAndRespawnDaemon();
|
|
2131
2969
|
const start = Date.now();
|
|
2132
|
-
let
|
|
2970
|
+
let d = 200;
|
|
2133
2971
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2134
|
-
await new Promise((r) => setTimeout(r,
|
|
2972
|
+
await new Promise((r) => setTimeout(r, d));
|
|
2135
2973
|
if (await connectToSocket()) break;
|
|
2136
|
-
|
|
2974
|
+
d = Math.min(d * 2, 3e3);
|
|
2137
2975
|
}
|
|
2138
2976
|
if (!_connected) return null;
|
|
2139
2977
|
}
|
|
2140
2978
|
}
|
|
2141
|
-
const result = await
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
killAndRespawnDaemon();
|
|
2147
|
-
const start = Date.now();
|
|
2148
|
-
let delay2 = 200;
|
|
2149
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2150
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
2151
|
-
if (await connectToSocket()) break;
|
|
2152
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2153
|
-
}
|
|
2154
|
-
if (!_connected) return null;
|
|
2155
|
-
const retry = await sendRequest([text], priority);
|
|
2156
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
2157
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
2158
|
-
`);
|
|
2159
|
-
}
|
|
2160
|
-
return null;
|
|
2979
|
+
const result = await retryThenRestart(
|
|
2980
|
+
() => sendRequest([text], priority),
|
|
2981
|
+
"Embed"
|
|
2982
|
+
);
|
|
2983
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2161
2984
|
}
|
|
2162
2985
|
function disconnectClient() {
|
|
2163
2986
|
if (_socket) {
|
|
@@ -2172,14 +2995,14 @@ function disconnectClient() {
|
|
|
2172
2995
|
entry.resolve({ error: "Client disconnected" });
|
|
2173
2996
|
}
|
|
2174
2997
|
}
|
|
2175
|
-
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;
|
|
2998
|
+
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;
|
|
2176
2999
|
var init_exe_daemon_client = __esm({
|
|
2177
3000
|
"src/lib/exe-daemon-client.ts"() {
|
|
2178
3001
|
"use strict";
|
|
2179
3002
|
init_config();
|
|
2180
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2181
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2182
|
-
SPAWN_LOCK_PATH =
|
|
3003
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
3004
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
3005
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2183
3006
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2184
3007
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2185
3008
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2187,7 +3010,11 @@ var init_exe_daemon_client = __esm({
|
|
|
2187
3010
|
_connected = false;
|
|
2188
3011
|
_buffer = "";
|
|
2189
3012
|
_requestCount = 0;
|
|
3013
|
+
_consecutiveFailures = 0;
|
|
2190
3014
|
HEALTH_CHECK_INTERVAL = 100;
|
|
3015
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
3016
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
3017
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
2191
3018
|
_pending = /* @__PURE__ */ new Map();
|
|
2192
3019
|
MAX_BUFFER = 1e7;
|
|
2193
3020
|
}
|
|
@@ -2231,8 +3058,8 @@ async function embedDirect(text) {
|
|
|
2231
3058
|
const llamaCpp = await import("node-llama-cpp");
|
|
2232
3059
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2233
3060
|
const { existsSync: existsSync16 } = await import("fs");
|
|
2234
|
-
const
|
|
2235
|
-
const modelPath =
|
|
3061
|
+
const path21 = await import("path");
|
|
3062
|
+
const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2236
3063
|
if (!existsSync16(modelPath)) {
|
|
2237
3064
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2238
3065
|
}
|
|
@@ -2263,14 +3090,14 @@ var init_embedder = __esm({
|
|
|
2263
3090
|
|
|
2264
3091
|
// src/lib/keychain.ts
|
|
2265
3092
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2266
|
-
import { existsSync as
|
|
2267
|
-
import
|
|
2268
|
-
import
|
|
3093
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3094
|
+
import path6 from "path";
|
|
3095
|
+
import os5 from "os";
|
|
2269
3096
|
function getKeyDir() {
|
|
2270
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3097
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2271
3098
|
}
|
|
2272
3099
|
function getKeyPath() {
|
|
2273
|
-
return
|
|
3100
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2274
3101
|
}
|
|
2275
3102
|
async function tryKeytar() {
|
|
2276
3103
|
try {
|
|
@@ -2291,9 +3118,9 @@ async function getMasterKey() {
|
|
|
2291
3118
|
}
|
|
2292
3119
|
}
|
|
2293
3120
|
const keyPath = getKeyPath();
|
|
2294
|
-
if (!
|
|
3121
|
+
if (!existsSync5(keyPath)) {
|
|
2295
3122
|
process.stderr.write(
|
|
2296
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3123
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2297
3124
|
`
|
|
2298
3125
|
);
|
|
2299
3126
|
return null;
|
|
@@ -2331,13 +3158,13 @@ __export(shard_manager_exports, {
|
|
|
2331
3158
|
listShards: () => listShards,
|
|
2332
3159
|
shardExists: () => shardExists
|
|
2333
3160
|
});
|
|
2334
|
-
import
|
|
2335
|
-
import { existsSync as
|
|
3161
|
+
import path7 from "path";
|
|
3162
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2336
3163
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2337
3164
|
function initShardManager(encryptionKey) {
|
|
2338
3165
|
_encryptionKey = encryptionKey;
|
|
2339
|
-
if (!
|
|
2340
|
-
|
|
3166
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
3167
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2341
3168
|
}
|
|
2342
3169
|
_shardingEnabled = true;
|
|
2343
3170
|
}
|
|
@@ -2357,7 +3184,7 @@ function getShardClient(projectName) {
|
|
|
2357
3184
|
}
|
|
2358
3185
|
const cached = _shards.get(safeName);
|
|
2359
3186
|
if (cached) return cached;
|
|
2360
|
-
const dbPath =
|
|
3187
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2361
3188
|
const client = createClient2({
|
|
2362
3189
|
url: `file:${dbPath}`,
|
|
2363
3190
|
encryptionKey: _encryptionKey
|
|
@@ -2367,10 +3194,10 @@ function getShardClient(projectName) {
|
|
|
2367
3194
|
}
|
|
2368
3195
|
function shardExists(projectName) {
|
|
2369
3196
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2370
|
-
return
|
|
3197
|
+
return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2371
3198
|
}
|
|
2372
3199
|
function listShards() {
|
|
2373
|
-
if (!
|
|
3200
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2374
3201
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2375
3202
|
}
|
|
2376
3203
|
async function ensureShardSchema(client) {
|
|
@@ -2444,7 +3271,23 @@ async function ensureShardSchema(client) {
|
|
|
2444
3271
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2445
3272
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2446
3273
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2447
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3274
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3275
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3276
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3277
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3278
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3279
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3280
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3281
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3282
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3283
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3284
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3285
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3286
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3287
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3288
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3289
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3290
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2448
3291
|
]) {
|
|
2449
3292
|
try {
|
|
2450
3293
|
await client.execute(col);
|
|
@@ -2556,7 +3399,7 @@ var init_shard_manager = __esm({
|
|
|
2556
3399
|
"src/lib/shard-manager.ts"() {
|
|
2557
3400
|
"use strict";
|
|
2558
3401
|
init_config();
|
|
2559
|
-
SHARDS_DIR =
|
|
3402
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
2560
3403
|
_shards = /* @__PURE__ */ new Map();
|
|
2561
3404
|
_encryptionKey = null;
|
|
2562
3405
|
_shardingEnabled = false;
|
|
@@ -3329,8 +4172,8 @@ __export(wiki_client_exports, {
|
|
|
3329
4172
|
listDocuments: () => listDocuments,
|
|
3330
4173
|
listWorkspaces: () => listWorkspaces
|
|
3331
4174
|
});
|
|
3332
|
-
async function wikiFetch(config2,
|
|
3333
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
4175
|
+
async function wikiFetch(config2, path21, method = "GET", body) {
|
|
4176
|
+
const url = `${config2.baseUrl}/api/v1${path21}`;
|
|
3334
4177
|
const headers = {
|
|
3335
4178
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
3336
4179
|
"Content-Type": "application/json"
|
|
@@ -3363,7 +4206,7 @@ async function wikiFetch(config2, path20, method = "GET", body) {
|
|
|
3363
4206
|
}
|
|
3364
4207
|
}
|
|
3365
4208
|
if (!response.ok) {
|
|
3366
|
-
throw new Error(`Wiki API ${method} ${
|
|
4209
|
+
throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
|
|
3367
4210
|
}
|
|
3368
4211
|
return response.json();
|
|
3369
4212
|
} finally {
|
|
@@ -4384,13 +5227,13 @@ __export(whatsapp_accounts_exports, {
|
|
|
4384
5227
|
getDefaultAccount: () => getDefaultAccount,
|
|
4385
5228
|
loadAccounts: () => loadAccounts
|
|
4386
5229
|
});
|
|
4387
|
-
import { readFileSync as
|
|
5230
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
4388
5231
|
import { join as join2 } from "path";
|
|
4389
5232
|
import { homedir as homedir2 } from "os";
|
|
4390
5233
|
function loadAccounts() {
|
|
4391
5234
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
4392
5235
|
try {
|
|
4393
|
-
const raw =
|
|
5236
|
+
const raw = readFileSync5(CONFIG_PATH2, "utf8");
|
|
4394
5237
|
const parsed = JSON.parse(raw);
|
|
4395
5238
|
if (!Array.isArray(parsed)) {
|
|
4396
5239
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -4430,13 +5273,13 @@ var init_whatsapp_accounts = __esm({
|
|
|
4430
5273
|
});
|
|
4431
5274
|
|
|
4432
5275
|
// src/lib/session-registry.ts
|
|
4433
|
-
import { readFileSync as
|
|
4434
|
-
import
|
|
4435
|
-
import
|
|
5276
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync7 } from "fs";
|
|
5277
|
+
import path9 from "path";
|
|
5278
|
+
import os7 from "os";
|
|
4436
5279
|
function registerSession(entry) {
|
|
4437
|
-
const dir =
|
|
4438
|
-
if (!
|
|
4439
|
-
|
|
5280
|
+
const dir = path9.dirname(REGISTRY_PATH);
|
|
5281
|
+
if (!existsSync7(dir)) {
|
|
5282
|
+
mkdirSync4(dir, { recursive: true });
|
|
4440
5283
|
}
|
|
4441
5284
|
const sessions = listSessions();
|
|
4442
5285
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -4445,11 +5288,11 @@ function registerSession(entry) {
|
|
|
4445
5288
|
} else {
|
|
4446
5289
|
sessions.push(entry);
|
|
4447
5290
|
}
|
|
4448
|
-
|
|
5291
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
4449
5292
|
}
|
|
4450
5293
|
function listSessions() {
|
|
4451
5294
|
try {
|
|
4452
|
-
const raw =
|
|
5295
|
+
const raw = readFileSync6(REGISTRY_PATH, "utf8");
|
|
4453
5296
|
return JSON.parse(raw);
|
|
4454
5297
|
} catch {
|
|
4455
5298
|
return [];
|
|
@@ -4459,7 +5302,7 @@ var REGISTRY_PATH;
|
|
|
4459
5302
|
var init_session_registry = __esm({
|
|
4460
5303
|
"src/lib/session-registry.ts"() {
|
|
4461
5304
|
"use strict";
|
|
4462
|
-
REGISTRY_PATH =
|
|
5305
|
+
REGISTRY_PATH = path9.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
4463
5306
|
}
|
|
4464
5307
|
});
|
|
4465
5308
|
|
|
@@ -4711,67 +5554,6 @@ var init_provider_table = __esm({
|
|
|
4711
5554
|
}
|
|
4712
5555
|
});
|
|
4713
5556
|
|
|
4714
|
-
// src/lib/runtime-table.ts
|
|
4715
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
4716
|
-
var init_runtime_table = __esm({
|
|
4717
|
-
"src/lib/runtime-table.ts"() {
|
|
4718
|
-
"use strict";
|
|
4719
|
-
RUNTIME_TABLE = {
|
|
4720
|
-
codex: {
|
|
4721
|
-
binary: "codex",
|
|
4722
|
-
launchMode: "interactive",
|
|
4723
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
4724
|
-
inlineFlag: "--no-alt-screen",
|
|
4725
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
4726
|
-
defaultModel: "gpt-5.4"
|
|
4727
|
-
},
|
|
4728
|
-
opencode: {
|
|
4729
|
-
binary: "opencode",
|
|
4730
|
-
launchMode: "exec",
|
|
4731
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
4732
|
-
inlineFlag: "",
|
|
4733
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
4734
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
4735
|
-
}
|
|
4736
|
-
};
|
|
4737
|
-
DEFAULT_RUNTIME = "claude";
|
|
4738
|
-
}
|
|
4739
|
-
});
|
|
4740
|
-
|
|
4741
|
-
// src/lib/agent-config.ts
|
|
4742
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
4743
|
-
import path8 from "path";
|
|
4744
|
-
function loadAgentConfig() {
|
|
4745
|
-
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
4746
|
-
try {
|
|
4747
|
-
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
4748
|
-
} catch {
|
|
4749
|
-
return {};
|
|
4750
|
-
}
|
|
4751
|
-
}
|
|
4752
|
-
function getAgentRuntime(agentId) {
|
|
4753
|
-
const config2 = loadAgentConfig();
|
|
4754
|
-
const entry = config2[agentId];
|
|
4755
|
-
if (entry) return entry;
|
|
4756
|
-
const orgDefault = config2["default"];
|
|
4757
|
-
if (orgDefault) return orgDefault;
|
|
4758
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
4759
|
-
}
|
|
4760
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
4761
|
-
var init_agent_config = __esm({
|
|
4762
|
-
"src/lib/agent-config.ts"() {
|
|
4763
|
-
"use strict";
|
|
4764
|
-
init_config();
|
|
4765
|
-
init_runtime_table();
|
|
4766
|
-
AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
4767
|
-
DEFAULT_MODELS = {
|
|
4768
|
-
claude: "claude-opus-4",
|
|
4769
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
4770
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
4771
|
-
};
|
|
4772
|
-
}
|
|
4773
|
-
});
|
|
4774
|
-
|
|
4775
5557
|
// src/lib/intercom-queue.ts
|
|
4776
5558
|
var intercom_queue_exports = {};
|
|
4777
5559
|
__export(intercom_queue_exports, {
|
|
@@ -4782,10 +5564,10 @@ __export(intercom_queue_exports, {
|
|
|
4782
5564
|
readQueue: () => readQueue
|
|
4783
5565
|
});
|
|
4784
5566
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
4785
|
-
import
|
|
4786
|
-
import
|
|
5567
|
+
import path10 from "path";
|
|
5568
|
+
import os8 from "os";
|
|
4787
5569
|
function ensureDir() {
|
|
4788
|
-
const dir =
|
|
5570
|
+
const dir = path10.dirname(QUEUE_PATH);
|
|
4789
5571
|
if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
|
|
4790
5572
|
}
|
|
4791
5573
|
function readQueue() {
|
|
@@ -4891,26 +5673,26 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
4891
5673
|
var init_intercom_queue = __esm({
|
|
4892
5674
|
"src/lib/intercom-queue.ts"() {
|
|
4893
5675
|
"use strict";
|
|
4894
|
-
QUEUE_PATH =
|
|
5676
|
+
QUEUE_PATH = path10.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
4895
5677
|
MAX_RETRIES2 = 5;
|
|
4896
5678
|
TTL_MS = 60 * 60 * 1e3;
|
|
4897
|
-
INTERCOM_LOG =
|
|
5679
|
+
INTERCOM_LOG = path10.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
4898
5680
|
}
|
|
4899
5681
|
});
|
|
4900
5682
|
|
|
4901
5683
|
// src/lib/license.ts
|
|
4902
5684
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
4903
5685
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
4904
|
-
import
|
|
5686
|
+
import path11 from "path";
|
|
4905
5687
|
import { jwtVerify, importSPKI } from "jose";
|
|
4906
5688
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
4907
5689
|
var init_license = __esm({
|
|
4908
5690
|
"src/lib/license.ts"() {
|
|
4909
5691
|
"use strict";
|
|
4910
5692
|
init_config();
|
|
4911
|
-
LICENSE_PATH =
|
|
4912
|
-
CACHE_PATH =
|
|
4913
|
-
DEVICE_ID_PATH =
|
|
5693
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
5694
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
5695
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4914
5696
|
PLAN_LIMITS = {
|
|
4915
5697
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
4916
5698
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -4923,7 +5705,7 @@ var init_license = __esm({
|
|
|
4923
5705
|
|
|
4924
5706
|
// src/lib/plan-limits.ts
|
|
4925
5707
|
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
4926
|
-
import
|
|
5708
|
+
import path12 from "path";
|
|
4927
5709
|
function getLicenseSync() {
|
|
4928
5710
|
try {
|
|
4929
5711
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -4995,14 +5777,14 @@ var init_plan_limits = __esm({
|
|
|
4995
5777
|
this.name = "PlanLimitError";
|
|
4996
5778
|
}
|
|
4997
5779
|
};
|
|
4998
|
-
CACHE_PATH2 =
|
|
5780
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
4999
5781
|
}
|
|
5000
5782
|
});
|
|
5001
5783
|
|
|
5002
5784
|
// src/lib/notifications.ts
|
|
5003
5785
|
import crypto3 from "crypto";
|
|
5004
|
-
import
|
|
5005
|
-
import
|
|
5786
|
+
import path13 from "path";
|
|
5787
|
+
import os9 from "os";
|
|
5006
5788
|
import {
|
|
5007
5789
|
readFileSync as readFileSync10,
|
|
5008
5790
|
readdirSync as readdirSync2,
|
|
@@ -5111,8 +5893,8 @@ var init_task_scope = __esm({
|
|
|
5111
5893
|
|
|
5112
5894
|
// src/lib/tasks-crud.ts
|
|
5113
5895
|
import crypto5 from "crypto";
|
|
5114
|
-
import
|
|
5115
|
-
import
|
|
5896
|
+
import path14 from "path";
|
|
5897
|
+
import os10 from "os";
|
|
5116
5898
|
import { execSync as execSync4 } from "child_process";
|
|
5117
5899
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5118
5900
|
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
@@ -5290,8 +6072,8 @@ ${laneWarning}` : laneWarning;
|
|
|
5290
6072
|
}
|
|
5291
6073
|
if (input.baseDir) {
|
|
5292
6074
|
try {
|
|
5293
|
-
await mkdir4(
|
|
5294
|
-
await mkdir4(
|
|
6075
|
+
await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
6076
|
+
await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
5295
6077
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
5296
6078
|
await ensureGitignoreExe(input.baseDir);
|
|
5297
6079
|
} catch {
|
|
@@ -5327,9 +6109,9 @@ ${laneWarning}` : laneWarning;
|
|
|
5327
6109
|
});
|
|
5328
6110
|
if (input.baseDir) {
|
|
5329
6111
|
try {
|
|
5330
|
-
const EXE_OS_DIR =
|
|
5331
|
-
const mdPath =
|
|
5332
|
-
const mdDir =
|
|
6112
|
+
const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
|
|
6113
|
+
const mdPath = path14.join(EXE_OS_DIR, taskFile);
|
|
6114
|
+
const mdDir = path14.dirname(mdPath);
|
|
5333
6115
|
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
5334
6116
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
5335
6117
|
const mdContent = `# ${input.title}
|
|
@@ -5630,7 +6412,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5630
6412
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5631
6413
|
}
|
|
5632
6414
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5633
|
-
const archPath =
|
|
6415
|
+
const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5634
6416
|
try {
|
|
5635
6417
|
if (existsSync12(archPath)) return;
|
|
5636
6418
|
const template = [
|
|
@@ -5665,7 +6447,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5665
6447
|
}
|
|
5666
6448
|
}
|
|
5667
6449
|
async function ensureGitignoreExe(baseDir) {
|
|
5668
|
-
const gitignorePath =
|
|
6450
|
+
const gitignorePath = path14.join(baseDir, ".gitignore");
|
|
5669
6451
|
try {
|
|
5670
6452
|
if (existsSync12(gitignorePath)) {
|
|
5671
6453
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -5699,13 +6481,13 @@ var init_tasks_crud = __esm({
|
|
|
5699
6481
|
});
|
|
5700
6482
|
|
|
5701
6483
|
// src/lib/tasks-review.ts
|
|
5702
|
-
import
|
|
6484
|
+
import path15 from "path";
|
|
5703
6485
|
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5704
6486
|
async function countPendingReviews(sessionScope) {
|
|
5705
6487
|
const client = getClient();
|
|
5706
6488
|
if (sessionScope) {
|
|
5707
6489
|
const result2 = await client.execute({
|
|
5708
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
6490
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
5709
6491
|
args: [sessionScope]
|
|
5710
6492
|
});
|
|
5711
6493
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -5881,11 +6663,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5881
6663
|
);
|
|
5882
6664
|
}
|
|
5883
6665
|
try {
|
|
5884
|
-
const cacheDir =
|
|
6666
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
5885
6667
|
if (existsSync13(cacheDir)) {
|
|
5886
6668
|
for (const f of readdirSync3(cacheDir)) {
|
|
5887
6669
|
if (f.startsWith("review-notified-")) {
|
|
5888
|
-
unlinkSync4(
|
|
6670
|
+
unlinkSync4(path15.join(cacheDir, f));
|
|
5889
6671
|
}
|
|
5890
6672
|
}
|
|
5891
6673
|
}
|
|
@@ -5906,7 +6688,7 @@ var init_tasks_review = __esm({
|
|
|
5906
6688
|
});
|
|
5907
6689
|
|
|
5908
6690
|
// src/lib/tasks-chain.ts
|
|
5909
|
-
import
|
|
6691
|
+
import path16 from "path";
|
|
5910
6692
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5911
6693
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5912
6694
|
const client = getClient();
|
|
@@ -5923,7 +6705,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5923
6705
|
});
|
|
5924
6706
|
for (const ur of unblockedRows.rows) {
|
|
5925
6707
|
try {
|
|
5926
|
-
const ubFile =
|
|
6708
|
+
const ubFile = path16.join(baseDir, String(ur.task_file));
|
|
5927
6709
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5928
6710
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5929
6711
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5992,7 +6774,7 @@ var init_tasks_chain = __esm({
|
|
|
5992
6774
|
|
|
5993
6775
|
// src/lib/project-name.ts
|
|
5994
6776
|
import { execSync as execSync5 } from "child_process";
|
|
5995
|
-
import
|
|
6777
|
+
import path17 from "path";
|
|
5996
6778
|
function getProjectName(cwd) {
|
|
5997
6779
|
const dir = cwd ?? process.cwd();
|
|
5998
6780
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -6005,7 +6787,7 @@ function getProjectName(cwd) {
|
|
|
6005
6787
|
timeout: 2e3,
|
|
6006
6788
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6007
6789
|
}).trim();
|
|
6008
|
-
repoRoot =
|
|
6790
|
+
repoRoot = path17.dirname(gitCommonDir);
|
|
6009
6791
|
} catch {
|
|
6010
6792
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
6011
6793
|
cwd: dir,
|
|
@@ -6014,11 +6796,11 @@ function getProjectName(cwd) {
|
|
|
6014
6796
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6015
6797
|
}).trim();
|
|
6016
6798
|
}
|
|
6017
|
-
_cached2 =
|
|
6799
|
+
_cached2 = path17.basename(repoRoot);
|
|
6018
6800
|
_cachedCwd = dir;
|
|
6019
6801
|
return _cached2;
|
|
6020
6802
|
} catch {
|
|
6021
|
-
_cached2 =
|
|
6803
|
+
_cached2 = path17.basename(dir);
|
|
6022
6804
|
_cachedCwd = dir;
|
|
6023
6805
|
return _cached2;
|
|
6024
6806
|
}
|
|
@@ -6491,7 +7273,7 @@ __export(tasks_exports, {
|
|
|
6491
7273
|
updateTaskStatus: () => updateTaskStatus,
|
|
6492
7274
|
writeCheckpoint: () => writeCheckpoint
|
|
6493
7275
|
});
|
|
6494
|
-
import
|
|
7276
|
+
import path18 from "path";
|
|
6495
7277
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
6496
7278
|
async function createTask(input) {
|
|
6497
7279
|
const result = await createTaskCore(input);
|
|
@@ -6511,8 +7293,8 @@ async function updateTask(input) {
|
|
|
6511
7293
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
6512
7294
|
try {
|
|
6513
7295
|
const agent = String(row.assigned_to);
|
|
6514
|
-
const cacheDir =
|
|
6515
|
-
const cachePath =
|
|
7296
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
7297
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
6516
7298
|
if (input.status === "in_progress") {
|
|
6517
7299
|
mkdirSync7(cacheDir, { recursive: true });
|
|
6518
7300
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -6983,12 +7765,12 @@ __export(tmux_routing_exports, {
|
|
|
6983
7765
|
});
|
|
6984
7766
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6985
7767
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
6986
|
-
import
|
|
6987
|
-
import
|
|
7768
|
+
import path19 from "path";
|
|
7769
|
+
import os11 from "os";
|
|
6988
7770
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6989
7771
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6990
7772
|
function spawnLockPath(sessionName) {
|
|
6991
|
-
return
|
|
7773
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6992
7774
|
}
|
|
6993
7775
|
function isProcessAlive(pid) {
|
|
6994
7776
|
try {
|
|
@@ -7025,8 +7807,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
7025
7807
|
function resolveBehaviorsExporterScript() {
|
|
7026
7808
|
try {
|
|
7027
7809
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7028
|
-
const scriptPath =
|
|
7029
|
-
|
|
7810
|
+
const scriptPath = path19.join(
|
|
7811
|
+
path19.dirname(thisFile),
|
|
7030
7812
|
"..",
|
|
7031
7813
|
"bin",
|
|
7032
7814
|
"exe-export-behaviors.js"
|
|
@@ -7101,7 +7883,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
7101
7883
|
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
7102
7884
|
}
|
|
7103
7885
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
7104
|
-
const filePath =
|
|
7886
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
7105
7887
|
writeFileSync7(filePath, JSON.stringify({
|
|
7106
7888
|
parentExe: rootExe,
|
|
7107
7889
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -7110,7 +7892,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
7110
7892
|
}
|
|
7111
7893
|
function getParentExe(sessionKey) {
|
|
7112
7894
|
try {
|
|
7113
|
-
const data = JSON.parse(readFileSync12(
|
|
7895
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
7114
7896
|
return data.parentExe || null;
|
|
7115
7897
|
} catch {
|
|
7116
7898
|
return null;
|
|
@@ -7119,7 +7901,7 @@ function getParentExe(sessionKey) {
|
|
|
7119
7901
|
function getDispatchedBy(sessionKey) {
|
|
7120
7902
|
try {
|
|
7121
7903
|
const data = JSON.parse(readFileSync12(
|
|
7122
|
-
|
|
7904
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
7123
7905
|
"utf8"
|
|
7124
7906
|
));
|
|
7125
7907
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -7305,7 +8087,7 @@ function sendIntercom(targetSession) {
|
|
|
7305
8087
|
try {
|
|
7306
8088
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7307
8089
|
const agent = baseAgentName(rawAgent);
|
|
7308
|
-
const markerPath =
|
|
8090
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
7309
8091
|
if (existsSync14(markerPath)) {
|
|
7310
8092
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
7311
8093
|
return "debounced";
|
|
@@ -7315,7 +8097,7 @@ function sendIntercom(targetSession) {
|
|
|
7315
8097
|
try {
|
|
7316
8098
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7317
8099
|
const agent = baseAgentName(rawAgent);
|
|
7318
|
-
const taskDir =
|
|
8100
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
7319
8101
|
if (existsSync14(taskDir)) {
|
|
7320
8102
|
const files = readdirSync4(taskDir).filter(
|
|
7321
8103
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -7449,8 +8231,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7449
8231
|
const transport = getTransport();
|
|
7450
8232
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7451
8233
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7452
|
-
const logDir =
|
|
7453
|
-
const logFile =
|
|
8234
|
+
const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
|
|
8235
|
+
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7454
8236
|
if (!existsSync14(logDir)) {
|
|
7455
8237
|
mkdirSync8(logDir, { recursive: true });
|
|
7456
8238
|
}
|
|
@@ -7458,14 +8240,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7458
8240
|
let cleanupSuffix = "";
|
|
7459
8241
|
try {
|
|
7460
8242
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7461
|
-
const cleanupScript =
|
|
8243
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7462
8244
|
if (existsSync14(cleanupScript)) {
|
|
7463
8245
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7464
8246
|
}
|
|
7465
8247
|
} catch {
|
|
7466
8248
|
}
|
|
7467
8249
|
try {
|
|
7468
|
-
const claudeJsonPath =
|
|
8250
|
+
const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
|
|
7469
8251
|
let claudeJson = {};
|
|
7470
8252
|
try {
|
|
7471
8253
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -7480,10 +8262,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7480
8262
|
} catch {
|
|
7481
8263
|
}
|
|
7482
8264
|
try {
|
|
7483
|
-
const settingsDir =
|
|
8265
|
+
const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
|
|
7484
8266
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7485
|
-
const projSettingsDir =
|
|
7486
|
-
const settingsPath =
|
|
8267
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
8268
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
7487
8269
|
let settings = {};
|
|
7488
8270
|
try {
|
|
7489
8271
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -7530,8 +8312,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7530
8312
|
let behaviorsFlag = "";
|
|
7531
8313
|
let legacyFallbackWarned = false;
|
|
7532
8314
|
if (!useExeAgent && !useBinSymlink) {
|
|
7533
|
-
const identityPath =
|
|
7534
|
-
|
|
8315
|
+
const identityPath = path19.join(
|
|
8316
|
+
os11.homedir(),
|
|
7535
8317
|
".exe-os",
|
|
7536
8318
|
"identity",
|
|
7537
8319
|
`${employeeName}.md`
|
|
@@ -7546,7 +8328,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7546
8328
|
}
|
|
7547
8329
|
const behaviorsFile = exportBehaviorsSync(
|
|
7548
8330
|
employeeName,
|
|
7549
|
-
|
|
8331
|
+
path19.basename(spawnCwd),
|
|
7550
8332
|
sessionName
|
|
7551
8333
|
);
|
|
7552
8334
|
if (behaviorsFile) {
|
|
@@ -7561,9 +8343,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7561
8343
|
}
|
|
7562
8344
|
let sessionContextFlag = "";
|
|
7563
8345
|
try {
|
|
7564
|
-
const ctxDir =
|
|
8346
|
+
const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7565
8347
|
mkdirSync8(ctxDir, { recursive: true });
|
|
7566
|
-
const ctxFile =
|
|
8348
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7567
8349
|
const ctxContent = [
|
|
7568
8350
|
`## Session Context`,
|
|
7569
8351
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -7647,7 +8429,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7647
8429
|
transport.pipeLog(sessionName, logFile);
|
|
7648
8430
|
try {
|
|
7649
8431
|
const mySession = getMySession();
|
|
7650
|
-
const dispatchInfo =
|
|
8432
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7651
8433
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
7652
8434
|
dispatchedBy: mySession,
|
|
7653
8435
|
rootExe: exeSession,
|
|
@@ -7722,15 +8504,15 @@ var init_tmux_routing = __esm({
|
|
|
7722
8504
|
init_intercom_queue();
|
|
7723
8505
|
init_plan_limits();
|
|
7724
8506
|
init_employees();
|
|
7725
|
-
SPAWN_LOCK_DIR =
|
|
7726
|
-
SESSION_CACHE =
|
|
8507
|
+
SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
8508
|
+
SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7727
8509
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7728
8510
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7729
8511
|
VERIFY_PANE_LINES = 200;
|
|
7730
8512
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7731
8513
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7732
|
-
INTERCOM_LOG2 =
|
|
7733
|
-
DEBOUNCE_FILE =
|
|
8514
|
+
INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
8515
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7734
8516
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7735
8517
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7736
8518
|
}
|
|
@@ -9586,7 +10368,7 @@ var OllamaProvider = class {
|
|
|
9586
10368
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
9587
10369
|
import { homedir } from "os";
|
|
9588
10370
|
import { join } from "path";
|
|
9589
|
-
import { mkdirSync as
|
|
10371
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
9590
10372
|
var INITIAL_BACKOFF_MS = 1e3;
|
|
9591
10373
|
var MAX_BACKOFF_MS = 3e5;
|
|
9592
10374
|
var BACKOFF_MULTIPLIER = 2;
|
|
@@ -9612,7 +10394,7 @@ var WhatsAppAdapter = class {
|
|
|
9612
10394
|
disconnectedAt = 0;
|
|
9613
10395
|
async connect(config2) {
|
|
9614
10396
|
this.authDir = config2.credentials.authDir ?? AUTH_DIR;
|
|
9615
|
-
|
|
10397
|
+
mkdirSync3(this.authDir, { recursive: true });
|
|
9616
10398
|
const baileys = await import("@whiskeysockets/baileys");
|
|
9617
10399
|
const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, DisconnectReason, makeCacheableSignalKeyStore } = baileys;
|
|
9618
10400
|
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
@@ -10914,12 +11696,12 @@ var SlackAdapter = class {
|
|
|
10914
11696
|
// src/gateway/adapters/imessage.ts
|
|
10915
11697
|
import { execFile } from "child_process";
|
|
10916
11698
|
import { promisify } from "util";
|
|
10917
|
-
import
|
|
10918
|
-
import
|
|
11699
|
+
import os6 from "os";
|
|
11700
|
+
import path8 from "path";
|
|
10919
11701
|
var execFileAsync = promisify(execFile);
|
|
10920
11702
|
var POLL_INTERVAL_MS = 5e3;
|
|
10921
|
-
var MESSAGES_DB_PATH =
|
|
10922
|
-
process.env.HOME ??
|
|
11703
|
+
var MESSAGES_DB_PATH = path8.join(
|
|
11704
|
+
process.env.HOME ?? os6.homedir(),
|
|
10923
11705
|
"Library/Messages/chat.db"
|
|
10924
11706
|
);
|
|
10925
11707
|
var IMessageAdapter = class {
|
|
@@ -11764,9 +12546,9 @@ async function ensureCRMContact(info) {
|
|
|
11764
12546
|
// src/automation/trigger-engine.ts
|
|
11765
12547
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
11766
12548
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
11767
|
-
import
|
|
11768
|
-
import
|
|
11769
|
-
var TRIGGERS_PATH =
|
|
12549
|
+
import path20 from "path";
|
|
12550
|
+
import os12 from "os";
|
|
12551
|
+
var TRIGGERS_PATH = path20.join(os12.homedir(), ".exe-os", "triggers.json");
|
|
11770
12552
|
var GRAPH_API_VERSION = "v21.0";
|
|
11771
12553
|
function substituteTemplate(template, record) {
|
|
11772
12554
|
return template.replace(
|