@askexenow/exe-os 0.9.6 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +668 -37
- package/dist/bin/cli.js +1399 -607
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +795 -155
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +703 -72
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1064 -273
- package/dist/bin/exe-heartbeat.js +676 -45
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +845 -152
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +668 -37
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +731 -91
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +735 -95
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1038 -247
- package/dist/hooks/bug-report-worker.js +902 -172
- package/dist/hooks/commit-complete.js +729 -89
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +851 -158
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +685 -45
- package/dist/hooks/pre-compact.js +729 -89
- package/dist/hooks/pre-tool-use.js +883 -127
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1071 -321
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +732 -92
- package/dist/hooks/session-start.js +1042 -209
- package/dist/hooks/stop.js +691 -51
- package/dist/hooks/subagent-stop.js +685 -45
- package/dist/hooks/summary-worker.js +827 -134
- package/dist/index.js +1026 -234
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +905 -164
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +66 -30
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +109 -73
- package/dist/lib/tmux-routing.js +98 -62
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1807 -472
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +301 -166
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +206 -40
- package/dist/mcp/tools/send-message.js +69 -33
- package/dist/mcp/tools/update-task.js +86 -50
- package/dist/runtime/index.js +731 -91
- package/dist/tui/App.js +864 -125
- package/package.json +3 -2
package/dist/tui/App.js
CHANGED
|
@@ -623,6 +623,18 @@ var init_runtime_table = __esm({
|
|
|
623
623
|
});
|
|
624
624
|
|
|
625
625
|
// src/lib/agent-config.ts
|
|
626
|
+
var agent_config_exports = {};
|
|
627
|
+
__export(agent_config_exports, {
|
|
628
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
629
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
630
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
631
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
632
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
633
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
634
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
635
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
636
|
+
setAgentRuntime: () => setAgentRuntime
|
|
637
|
+
});
|
|
626
638
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
627
639
|
import path3 from "path";
|
|
628
640
|
function loadAgentConfig() {
|
|
@@ -633,6 +645,11 @@ function loadAgentConfig() {
|
|
|
633
645
|
return {};
|
|
634
646
|
}
|
|
635
647
|
}
|
|
648
|
+
function saveAgentConfig(config) {
|
|
649
|
+
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
650
|
+
if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
|
|
651
|
+
writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
652
|
+
}
|
|
636
653
|
function getAgentRuntime(agentId) {
|
|
637
654
|
const config = loadAgentConfig();
|
|
638
655
|
const entry = config[agentId];
|
|
@@ -641,13 +658,47 @@ function getAgentRuntime(agentId) {
|
|
|
641
658
|
if (orgDefault) return orgDefault;
|
|
642
659
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
643
660
|
}
|
|
644
|
-
|
|
661
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
662
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
663
|
+
if (!knownModels) {
|
|
664
|
+
return {
|
|
665
|
+
ok: false,
|
|
666
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
if (!knownModels.includes(model)) {
|
|
670
|
+
return {
|
|
671
|
+
ok: false,
|
|
672
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
const config = loadAgentConfig();
|
|
676
|
+
config[agentId] = { runtime, model };
|
|
677
|
+
saveAgentConfig(config);
|
|
678
|
+
return { ok: true };
|
|
679
|
+
}
|
|
680
|
+
function clearAgentRuntime(agentId) {
|
|
681
|
+
const config = loadAgentConfig();
|
|
682
|
+
delete config[agentId];
|
|
683
|
+
saveAgentConfig(config);
|
|
684
|
+
}
|
|
685
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
645
686
|
var init_agent_config = __esm({
|
|
646
687
|
"src/lib/agent-config.ts"() {
|
|
647
688
|
"use strict";
|
|
648
689
|
init_config();
|
|
649
690
|
init_runtime_table();
|
|
650
691
|
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
692
|
+
KNOWN_RUNTIMES = {
|
|
693
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
694
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
695
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
696
|
+
};
|
|
697
|
+
RUNTIME_LABELS = {
|
|
698
|
+
claude: "Claude Code (Anthropic)",
|
|
699
|
+
codex: "Codex (OpenAI)",
|
|
700
|
+
opencode: "OpenCode (open source)"
|
|
701
|
+
};
|
|
651
702
|
DEFAULT_MODELS = {
|
|
652
703
|
claude: "claude-opus-4",
|
|
653
704
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -852,6 +903,7 @@ __export(employees_exports, {
|
|
|
852
903
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
853
904
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
854
905
|
hasRole: () => hasRole,
|
|
906
|
+
hireEmployee: () => hireEmployee,
|
|
855
907
|
isCoordinatorName: () => isCoordinatorName,
|
|
856
908
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
857
909
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -962,6 +1014,52 @@ function addEmployee(employees, employee) {
|
|
|
962
1014
|
}
|
|
963
1015
|
return [...employees, normalized];
|
|
964
1016
|
}
|
|
1017
|
+
function appendToCoordinatorTeam(employee) {
|
|
1018
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1019
|
+
if (!coordinator) return;
|
|
1020
|
+
const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1021
|
+
if (!existsSync6(idPath)) return;
|
|
1022
|
+
const content = readFileSync6(idPath, "utf-8");
|
|
1023
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1024
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1025
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
1026
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
1027
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
1028
|
+
const entry = `
|
|
1029
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
1030
|
+
`;
|
|
1031
|
+
let updated;
|
|
1032
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
1033
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
1034
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
1035
|
+
} else {
|
|
1036
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
1037
|
+
}
|
|
1038
|
+
writeFileSync4(idPath, updated, "utf-8");
|
|
1039
|
+
}
|
|
1040
|
+
function capitalize(s) {
|
|
1041
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1042
|
+
}
|
|
1043
|
+
async function hireEmployee(employee) {
|
|
1044
|
+
const employees = await loadEmployees();
|
|
1045
|
+
const updated = addEmployee(employees, employee);
|
|
1046
|
+
await saveEmployees(updated);
|
|
1047
|
+
try {
|
|
1048
|
+
appendToCoordinatorTeam(employee);
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
try {
|
|
1052
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
1053
|
+
const config = loadAgentConfig2();
|
|
1054
|
+
const name = employee.name.toLowerCase();
|
|
1055
|
+
if (!config[name] && config["default"]) {
|
|
1056
|
+
config[name] = { ...config["default"] };
|
|
1057
|
+
saveAgentConfig2(config);
|
|
1058
|
+
}
|
|
1059
|
+
} catch {
|
|
1060
|
+
}
|
|
1061
|
+
return updated;
|
|
1062
|
+
}
|
|
965
1063
|
async function normalizeRosterCase(rosterPath) {
|
|
966
1064
|
const employees = await loadEmployees(rosterPath);
|
|
967
1065
|
let changed = false;
|
|
@@ -1032,7 +1130,7 @@ function registerBinSymlinks(name) {
|
|
|
1032
1130
|
}
|
|
1033
1131
|
return { created, skipped, errors };
|
|
1034
1132
|
}
|
|
1035
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
1133
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
1036
1134
|
var init_employees = __esm({
|
|
1037
1135
|
"src/lib/employees.ts"() {
|
|
1038
1136
|
"use strict";
|
|
@@ -1041,16 +1139,602 @@ var init_employees = __esm({
|
|
|
1041
1139
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1042
1140
|
COORDINATOR_ROLE = "COO";
|
|
1043
1141
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1142
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
1143
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
// src/lib/database-adapter.ts
|
|
1148
|
+
import os5 from "os";
|
|
1149
|
+
import path6 from "path";
|
|
1150
|
+
import { createRequire } from "module";
|
|
1151
|
+
import { pathToFileURL } from "url";
|
|
1152
|
+
function quotedIdentifier(identifier) {
|
|
1153
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1154
|
+
}
|
|
1155
|
+
function unqualifiedTableName(name) {
|
|
1156
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1157
|
+
const parts = raw.split(".");
|
|
1158
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1159
|
+
}
|
|
1160
|
+
function stripTrailingSemicolon(sql) {
|
|
1161
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1162
|
+
}
|
|
1163
|
+
function appendClause(sql, clause) {
|
|
1164
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1165
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1166
|
+
if (!returningMatch) {
|
|
1167
|
+
return `${trimmed}${clause}`;
|
|
1168
|
+
}
|
|
1169
|
+
const idx = returningMatch.index;
|
|
1170
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1171
|
+
}
|
|
1172
|
+
function normalizeStatement(stmt) {
|
|
1173
|
+
if (typeof stmt === "string") {
|
|
1174
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1175
|
+
}
|
|
1176
|
+
const sql = stmt.sql;
|
|
1177
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1178
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1179
|
+
}
|
|
1180
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1181
|
+
}
|
|
1182
|
+
function rewriteBooleanLiterals(sql) {
|
|
1183
|
+
let out = sql;
|
|
1184
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1185
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1186
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1187
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1188
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1189
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1190
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1191
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
1194
|
+
}
|
|
1195
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1196
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1197
|
+
return sql;
|
|
1198
|
+
}
|
|
1199
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1200
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1201
|
+
}
|
|
1202
|
+
function rewriteInsertOrReplace(sql) {
|
|
1203
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1204
|
+
if (!match) {
|
|
1205
|
+
return sql;
|
|
1206
|
+
}
|
|
1207
|
+
const rawTable = match[1];
|
|
1208
|
+
const rawColumns = match[2];
|
|
1209
|
+
const remainder = match[3];
|
|
1210
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1211
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1212
|
+
if (!conflictKeys?.length) {
|
|
1213
|
+
return sql;
|
|
1214
|
+
}
|
|
1215
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1216
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1217
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1218
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1219
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1220
|
+
}
|
|
1221
|
+
function rewriteSql(sql) {
|
|
1222
|
+
let out = sql;
|
|
1223
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1224
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1225
|
+
out = rewriteBooleanLiterals(out);
|
|
1226
|
+
out = rewriteInsertOrReplace(out);
|
|
1227
|
+
out = rewriteInsertOrIgnore(out);
|
|
1228
|
+
return stripTrailingSemicolon(out);
|
|
1229
|
+
}
|
|
1230
|
+
function toBoolean(value) {
|
|
1231
|
+
if (value === null || value === void 0) return value;
|
|
1232
|
+
if (typeof value === "boolean") return value;
|
|
1233
|
+
if (typeof value === "number") return value !== 0;
|
|
1234
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1235
|
+
if (typeof value === "string") {
|
|
1236
|
+
const normalized = value.trim().toLowerCase();
|
|
1237
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1238
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1239
|
+
}
|
|
1240
|
+
return Boolean(value);
|
|
1241
|
+
}
|
|
1242
|
+
function countQuestionMarks(sql, end) {
|
|
1243
|
+
let count = 0;
|
|
1244
|
+
let inSingle = false;
|
|
1245
|
+
let inDouble = false;
|
|
1246
|
+
let inLineComment = false;
|
|
1247
|
+
let inBlockComment = false;
|
|
1248
|
+
for (let i = 0; i < end; i++) {
|
|
1249
|
+
const ch = sql[i];
|
|
1250
|
+
const next = sql[i + 1];
|
|
1251
|
+
if (inLineComment) {
|
|
1252
|
+
if (ch === "\n") inLineComment = false;
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (inBlockComment) {
|
|
1256
|
+
if (ch === "*" && next === "/") {
|
|
1257
|
+
inBlockComment = false;
|
|
1258
|
+
i += 1;
|
|
1259
|
+
}
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1263
|
+
inLineComment = true;
|
|
1264
|
+
i += 1;
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1268
|
+
inBlockComment = true;
|
|
1269
|
+
i += 1;
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1273
|
+
inSingle = !inSingle;
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1277
|
+
inDouble = !inDouble;
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1281
|
+
count += 1;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return count;
|
|
1285
|
+
}
|
|
1286
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1287
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1288
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1289
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1290
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1291
|
+
const matchText = match[0];
|
|
1292
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1293
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return indexes;
|
|
1297
|
+
}
|
|
1298
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1299
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1300
|
+
if (!match) return;
|
|
1301
|
+
const rawTable = match[1];
|
|
1302
|
+
const rawColumns = match[2];
|
|
1303
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1304
|
+
if (!boolColumns?.size) return;
|
|
1305
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1306
|
+
for (const [index, column] of columns.entries()) {
|
|
1307
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1308
|
+
args[index] = toBoolean(args[index]);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1313
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1314
|
+
if (!match) return;
|
|
1315
|
+
const rawTable = match[1];
|
|
1316
|
+
const setClause = match[2];
|
|
1317
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1318
|
+
if (!boolColumns?.size) return;
|
|
1319
|
+
const assignments = setClause.split(",");
|
|
1320
|
+
let placeholderIndex = 0;
|
|
1321
|
+
for (const assignment of assignments) {
|
|
1322
|
+
if (!assignment.includes("?")) continue;
|
|
1323
|
+
placeholderIndex += 1;
|
|
1324
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1325
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1326
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function coerceBooleanArgs(sql, args) {
|
|
1331
|
+
const nextArgs = [...args];
|
|
1332
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1333
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1334
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1335
|
+
for (const index of placeholderIndexes) {
|
|
1336
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1337
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return nextArgs;
|
|
1341
|
+
}
|
|
1342
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1343
|
+
let out = "";
|
|
1344
|
+
let placeholder = 0;
|
|
1345
|
+
let inSingle = false;
|
|
1346
|
+
let inDouble = false;
|
|
1347
|
+
let inLineComment = false;
|
|
1348
|
+
let inBlockComment = false;
|
|
1349
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1350
|
+
const ch = sql[i];
|
|
1351
|
+
const next = sql[i + 1];
|
|
1352
|
+
if (inLineComment) {
|
|
1353
|
+
out += ch;
|
|
1354
|
+
if (ch === "\n") inLineComment = false;
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
if (inBlockComment) {
|
|
1358
|
+
out += ch;
|
|
1359
|
+
if (ch === "*" && next === "/") {
|
|
1360
|
+
out += next;
|
|
1361
|
+
inBlockComment = false;
|
|
1362
|
+
i += 1;
|
|
1363
|
+
}
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1367
|
+
out += ch + next;
|
|
1368
|
+
inLineComment = true;
|
|
1369
|
+
i += 1;
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1373
|
+
out += ch + next;
|
|
1374
|
+
inBlockComment = true;
|
|
1375
|
+
i += 1;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1379
|
+
inSingle = !inSingle;
|
|
1380
|
+
out += ch;
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1384
|
+
inDouble = !inDouble;
|
|
1385
|
+
out += ch;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1389
|
+
placeholder += 1;
|
|
1390
|
+
out += `$${placeholder}`;
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
out += ch;
|
|
1394
|
+
}
|
|
1395
|
+
return out;
|
|
1396
|
+
}
|
|
1397
|
+
function translateStatementForPostgres(stmt) {
|
|
1398
|
+
const normalized = normalizeStatement(stmt);
|
|
1399
|
+
if (normalized.kind === "named") {
|
|
1400
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1401
|
+
}
|
|
1402
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1403
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1404
|
+
return {
|
|
1405
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1406
|
+
args: coercedArgs
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
function shouldBypassPostgres(stmt) {
|
|
1410
|
+
const normalized = normalizeStatement(stmt);
|
|
1411
|
+
if (normalized.kind === "named") {
|
|
1412
|
+
return true;
|
|
1413
|
+
}
|
|
1414
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1415
|
+
}
|
|
1416
|
+
function shouldFallbackOnError(error) {
|
|
1417
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1418
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1419
|
+
}
|
|
1420
|
+
function isReadQuery(sql) {
|
|
1421
|
+
const trimmed = sql.trimStart();
|
|
1422
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1423
|
+
}
|
|
1424
|
+
function buildRow(row, columns) {
|
|
1425
|
+
const values = columns.map((column) => row[column]);
|
|
1426
|
+
return Object.assign(values, row);
|
|
1427
|
+
}
|
|
1428
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1429
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1430
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1431
|
+
return {
|
|
1432
|
+
columns,
|
|
1433
|
+
columnTypes: columns.map(() => ""),
|
|
1434
|
+
rows: resultRows,
|
|
1435
|
+
rowsAffected,
|
|
1436
|
+
lastInsertRowid: void 0,
|
|
1437
|
+
toJSON() {
|
|
1438
|
+
return {
|
|
1439
|
+
columns,
|
|
1440
|
+
columnTypes: columns.map(() => ""),
|
|
1441
|
+
rows,
|
|
1442
|
+
rowsAffected,
|
|
1443
|
+
lastInsertRowid: void 0
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
async function loadPrismaClient() {
|
|
1449
|
+
if (!prismaClientPromise) {
|
|
1450
|
+
prismaClientPromise = (async () => {
|
|
1451
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1452
|
+
if (explicitPath) {
|
|
1453
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1454
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1455
|
+
if (!PrismaClient2) {
|
|
1456
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1457
|
+
}
|
|
1458
|
+
return new PrismaClient2();
|
|
1459
|
+
}
|
|
1460
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
|
|
1461
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1462
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1463
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1464
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1465
|
+
if (!PrismaClient) {
|
|
1466
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1467
|
+
}
|
|
1468
|
+
return new PrismaClient();
|
|
1469
|
+
})();
|
|
1470
|
+
}
|
|
1471
|
+
return prismaClientPromise;
|
|
1472
|
+
}
|
|
1473
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1474
|
+
if (!compatibilityBootstrapPromise) {
|
|
1475
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1476
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1477
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1478
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1479
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1480
|
+
relation
|
|
1481
|
+
);
|
|
1482
|
+
if (!rows[0]?.regclass) {
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
await prisma.$executeRawUnsafe(
|
|
1486
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
})();
|
|
1490
|
+
}
|
|
1491
|
+
return compatibilityBootstrapPromise;
|
|
1492
|
+
}
|
|
1493
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1494
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1495
|
+
if (isReadQuery(translated.sql)) {
|
|
1496
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1497
|
+
translated.sql,
|
|
1498
|
+
...translated.args
|
|
1499
|
+
);
|
|
1500
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1501
|
+
}
|
|
1502
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1503
|
+
return buildResultSet([], rowsAffected);
|
|
1504
|
+
}
|
|
1505
|
+
function splitSqlStatements(sql) {
|
|
1506
|
+
const parts = [];
|
|
1507
|
+
let current = "";
|
|
1508
|
+
let inSingle = false;
|
|
1509
|
+
let inDouble = false;
|
|
1510
|
+
let inLineComment = false;
|
|
1511
|
+
let inBlockComment = false;
|
|
1512
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1513
|
+
const ch = sql[i];
|
|
1514
|
+
const next = sql[i + 1];
|
|
1515
|
+
if (inLineComment) {
|
|
1516
|
+
current += ch;
|
|
1517
|
+
if (ch === "\n") inLineComment = false;
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
if (inBlockComment) {
|
|
1521
|
+
current += ch;
|
|
1522
|
+
if (ch === "*" && next === "/") {
|
|
1523
|
+
current += next;
|
|
1524
|
+
inBlockComment = false;
|
|
1525
|
+
i += 1;
|
|
1526
|
+
}
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1530
|
+
current += ch + next;
|
|
1531
|
+
inLineComment = true;
|
|
1532
|
+
i += 1;
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1536
|
+
current += ch + next;
|
|
1537
|
+
inBlockComment = true;
|
|
1538
|
+
i += 1;
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1542
|
+
inSingle = !inSingle;
|
|
1543
|
+
current += ch;
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1547
|
+
inDouble = !inDouble;
|
|
1548
|
+
current += ch;
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1552
|
+
if (current.trim()) {
|
|
1553
|
+
parts.push(current.trim());
|
|
1554
|
+
}
|
|
1555
|
+
current = "";
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
current += ch;
|
|
1559
|
+
}
|
|
1560
|
+
if (current.trim()) {
|
|
1561
|
+
parts.push(current.trim());
|
|
1562
|
+
}
|
|
1563
|
+
return parts;
|
|
1564
|
+
}
|
|
1565
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1566
|
+
const prisma = await loadPrismaClient();
|
|
1567
|
+
await ensureCompatibilityViews(prisma);
|
|
1568
|
+
let closed = false;
|
|
1569
|
+
let adapter;
|
|
1570
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1571
|
+
if (!fallbackClient) {
|
|
1572
|
+
if (error) throw error;
|
|
1573
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1574
|
+
}
|
|
1575
|
+
if (error) {
|
|
1576
|
+
process.stderr.write(
|
|
1577
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1578
|
+
`
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
return fallbackClient.execute(stmt);
|
|
1582
|
+
};
|
|
1583
|
+
adapter = {
|
|
1584
|
+
async execute(stmt) {
|
|
1585
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1586
|
+
return fallbackExecute(stmt);
|
|
1587
|
+
}
|
|
1588
|
+
try {
|
|
1589
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
if (shouldFallbackOnError(error)) {
|
|
1592
|
+
return fallbackExecute(stmt, error);
|
|
1593
|
+
}
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1597
|
+
async batch(stmts, mode) {
|
|
1598
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1599
|
+
if (!fallbackClient) {
|
|
1600
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1601
|
+
}
|
|
1602
|
+
return fallbackClient.batch(stmts, mode);
|
|
1603
|
+
}
|
|
1604
|
+
try {
|
|
1605
|
+
if (prisma.$transaction) {
|
|
1606
|
+
return await prisma.$transaction(async (tx) => {
|
|
1607
|
+
const results2 = [];
|
|
1608
|
+
for (const stmt of stmts) {
|
|
1609
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1610
|
+
}
|
|
1611
|
+
return results2;
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
const results = [];
|
|
1615
|
+
for (const stmt of stmts) {
|
|
1616
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1617
|
+
}
|
|
1618
|
+
return results;
|
|
1619
|
+
} catch (error) {
|
|
1620
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1621
|
+
process.stderr.write(
|
|
1622
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1623
|
+
`
|
|
1624
|
+
);
|
|
1625
|
+
return fallbackClient.batch(stmts, mode);
|
|
1626
|
+
}
|
|
1627
|
+
throw error;
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1630
|
+
async migrate(stmts) {
|
|
1631
|
+
if (fallbackClient) {
|
|
1632
|
+
return fallbackClient.migrate(stmts);
|
|
1633
|
+
}
|
|
1634
|
+
return adapter.batch(stmts, "deferred");
|
|
1635
|
+
},
|
|
1636
|
+
async transaction(mode) {
|
|
1637
|
+
if (!fallbackClient) {
|
|
1638
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1639
|
+
}
|
|
1640
|
+
return fallbackClient.transaction(mode);
|
|
1641
|
+
},
|
|
1642
|
+
async executeMultiple(sql) {
|
|
1643
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1644
|
+
return fallbackClient.executeMultiple(sql);
|
|
1645
|
+
}
|
|
1646
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1647
|
+
await adapter.execute(statement);
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1650
|
+
async sync() {
|
|
1651
|
+
if (fallbackClient) {
|
|
1652
|
+
return fallbackClient.sync();
|
|
1653
|
+
}
|
|
1654
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1655
|
+
},
|
|
1656
|
+
close() {
|
|
1657
|
+
closed = true;
|
|
1658
|
+
prismaClientPromise = null;
|
|
1659
|
+
compatibilityBootstrapPromise = null;
|
|
1660
|
+
void prisma.$disconnect?.();
|
|
1661
|
+
},
|
|
1662
|
+
get closed() {
|
|
1663
|
+
return closed;
|
|
1664
|
+
},
|
|
1665
|
+
get protocol() {
|
|
1666
|
+
return "prisma-postgres";
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
return adapter;
|
|
1670
|
+
}
|
|
1671
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1672
|
+
var init_database_adapter = __esm({
|
|
1673
|
+
"src/lib/database-adapter.ts"() {
|
|
1674
|
+
"use strict";
|
|
1675
|
+
VIEW_MAPPINGS = [
|
|
1676
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1677
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1678
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1679
|
+
{ view: "entities", source: "memory.entities" },
|
|
1680
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1681
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1682
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1683
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1684
|
+
{ view: "messages", source: "memory.messages" },
|
|
1685
|
+
{ view: "users", source: "wiki.users" },
|
|
1686
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1687
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1688
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1689
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1690
|
+
];
|
|
1691
|
+
UPSERT_KEYS = {
|
|
1692
|
+
memories: ["id"],
|
|
1693
|
+
tasks: ["id"],
|
|
1694
|
+
behaviors: ["id"],
|
|
1695
|
+
entities: ["id"],
|
|
1696
|
+
relationships: ["id"],
|
|
1697
|
+
entity_aliases: ["alias"],
|
|
1698
|
+
notifications: ["id"],
|
|
1699
|
+
messages: ["id"],
|
|
1700
|
+
users: ["id"],
|
|
1701
|
+
workspaces: ["id"],
|
|
1702
|
+
workspace_users: ["id"],
|
|
1703
|
+
documents: ["id"],
|
|
1704
|
+
chats: ["id"]
|
|
1705
|
+
};
|
|
1706
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1707
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1708
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1709
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1710
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1711
|
+
};
|
|
1712
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1713
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1714
|
+
);
|
|
1715
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1716
|
+
/\bPRAGMA\b/i,
|
|
1717
|
+
/\bsqlite_master\b/i,
|
|
1718
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1719
|
+
/\bMATCH\b/i,
|
|
1720
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1721
|
+
/\bjson_extract\s*\(/i,
|
|
1722
|
+
/\bjulianday\s*\(/i,
|
|
1723
|
+
/\bstrftime\s*\(/i,
|
|
1724
|
+
/\blast_insert_rowid\s*\(/i
|
|
1725
|
+
];
|
|
1726
|
+
prismaClientPromise = null;
|
|
1727
|
+
compatibilityBootstrapPromise = null;
|
|
1044
1728
|
}
|
|
1045
1729
|
});
|
|
1046
1730
|
|
|
1047
1731
|
// src/lib/exe-daemon-client.ts
|
|
1048
1732
|
import net from "net";
|
|
1049
|
-
import
|
|
1733
|
+
import os6 from "os";
|
|
1050
1734
|
import { spawn } from "child_process";
|
|
1051
1735
|
import { randomUUID } from "crypto";
|
|
1052
1736
|
import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
1053
|
-
import
|
|
1737
|
+
import path7 from "path";
|
|
1054
1738
|
import { fileURLToPath } from "url";
|
|
1055
1739
|
function handleData(chunk) {
|
|
1056
1740
|
_buffer += chunk.toString();
|
|
@@ -1101,17 +1785,17 @@ function cleanupStaleFiles() {
|
|
|
1101
1785
|
}
|
|
1102
1786
|
}
|
|
1103
1787
|
function findPackageRoot() {
|
|
1104
|
-
let dir =
|
|
1105
|
-
const { root } =
|
|
1788
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
1789
|
+
const { root } = path7.parse(dir);
|
|
1106
1790
|
while (dir !== root) {
|
|
1107
|
-
if (existsSync7(
|
|
1108
|
-
dir =
|
|
1791
|
+
if (existsSync7(path7.join(dir, "package.json"))) return dir;
|
|
1792
|
+
dir = path7.dirname(dir);
|
|
1109
1793
|
}
|
|
1110
1794
|
return null;
|
|
1111
1795
|
}
|
|
1112
1796
|
function spawnDaemon() {
|
|
1113
|
-
const freeGB =
|
|
1114
|
-
const totalGB =
|
|
1797
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1798
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
1115
1799
|
if (totalGB <= 8) {
|
|
1116
1800
|
process.stderr.write(
|
|
1117
1801
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -1131,7 +1815,7 @@ function spawnDaemon() {
|
|
|
1131
1815
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1132
1816
|
return;
|
|
1133
1817
|
}
|
|
1134
|
-
const daemonPath =
|
|
1818
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1135
1819
|
if (!existsSync7(daemonPath)) {
|
|
1136
1820
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1137
1821
|
`);
|
|
@@ -1140,7 +1824,7 @@ function spawnDaemon() {
|
|
|
1140
1824
|
const resolvedPath = daemonPath;
|
|
1141
1825
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1142
1826
|
`);
|
|
1143
|
-
const logPath =
|
|
1827
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
1144
1828
|
let stderrFd = "ignore";
|
|
1145
1829
|
try {
|
|
1146
1830
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1287,9 +1971,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1287
1971
|
"src/lib/exe-daemon-client.ts"() {
|
|
1288
1972
|
"use strict";
|
|
1289
1973
|
init_config();
|
|
1290
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1291
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1292
|
-
SPAWN_LOCK_PATH =
|
|
1974
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
1975
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
1976
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1293
1977
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1294
1978
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1295
1979
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1371,7 +2055,7 @@ __export(db_daemon_client_exports, {
|
|
|
1371
2055
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1372
2056
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1373
2057
|
});
|
|
1374
|
-
function
|
|
2058
|
+
function normalizeStatement2(stmt) {
|
|
1375
2059
|
if (typeof stmt === "string") {
|
|
1376
2060
|
return { sql: stmt, args: [] };
|
|
1377
2061
|
}
|
|
@@ -1395,7 +2079,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1395
2079
|
if (!_useDaemon || !isClientConnected()) {
|
|
1396
2080
|
return fallbackClient.execute(stmt);
|
|
1397
2081
|
}
|
|
1398
|
-
const { sql, args } =
|
|
2082
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1399
2083
|
const response = await sendDaemonRequest({
|
|
1400
2084
|
type: "db-execute",
|
|
1401
2085
|
sql,
|
|
@@ -1420,7 +2104,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1420
2104
|
if (!_useDaemon || !isClientConnected()) {
|
|
1421
2105
|
return fallbackClient.batch(stmts, mode);
|
|
1422
2106
|
}
|
|
1423
|
-
const statements = stmts.map(
|
|
2107
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1424
2108
|
const response = await sendDaemonRequest({
|
|
1425
2109
|
type: "db-batch",
|
|
1426
2110
|
statements,
|
|
@@ -1515,6 +2199,18 @@ __export(database_exports, {
|
|
|
1515
2199
|
});
|
|
1516
2200
|
import { createClient } from "@libsql/client";
|
|
1517
2201
|
async function initDatabase(config) {
|
|
2202
|
+
if (_walCheckpointTimer) {
|
|
2203
|
+
clearInterval(_walCheckpointTimer);
|
|
2204
|
+
_walCheckpointTimer = null;
|
|
2205
|
+
}
|
|
2206
|
+
if (_daemonClient) {
|
|
2207
|
+
_daemonClient.close();
|
|
2208
|
+
_daemonClient = null;
|
|
2209
|
+
}
|
|
2210
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2211
|
+
_adapterClient.close();
|
|
2212
|
+
}
|
|
2213
|
+
_adapterClient = null;
|
|
1518
2214
|
if (_client) {
|
|
1519
2215
|
_client.close();
|
|
1520
2216
|
_client = null;
|
|
@@ -1528,6 +2224,7 @@ async function initDatabase(config) {
|
|
|
1528
2224
|
}
|
|
1529
2225
|
_client = createClient(opts);
|
|
1530
2226
|
_resilientClient = wrapWithRetry(_client);
|
|
2227
|
+
_adapterClient = _resilientClient;
|
|
1531
2228
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1532
2229
|
});
|
|
1533
2230
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1538,14 +2235,20 @@ async function initDatabase(config) {
|
|
|
1538
2235
|
});
|
|
1539
2236
|
}, 3e4);
|
|
1540
2237
|
_walCheckpointTimer.unref();
|
|
2238
|
+
if (process.env.DATABASE_URL) {
|
|
2239
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2240
|
+
}
|
|
1541
2241
|
}
|
|
1542
2242
|
function isInitialized() {
|
|
1543
|
-
return _client !== null;
|
|
2243
|
+
return _adapterClient !== null || _client !== null;
|
|
1544
2244
|
}
|
|
1545
2245
|
function getClient() {
|
|
1546
|
-
if (!
|
|
2246
|
+
if (!_adapterClient) {
|
|
1547
2247
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1548
2248
|
}
|
|
2249
|
+
if (process.env.DATABASE_URL) {
|
|
2250
|
+
return _adapterClient;
|
|
2251
|
+
}
|
|
1549
2252
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1550
2253
|
return _resilientClient;
|
|
1551
2254
|
}
|
|
@@ -1555,6 +2258,7 @@ function getClient() {
|
|
|
1555
2258
|
return _resilientClient;
|
|
1556
2259
|
}
|
|
1557
2260
|
async function initDaemonClient() {
|
|
2261
|
+
if (process.env.DATABASE_URL) return;
|
|
1558
2262
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1559
2263
|
if (!_resilientClient) return;
|
|
1560
2264
|
try {
|
|
@@ -2499,26 +3203,36 @@ async function ensureSchema() {
|
|
|
2499
3203
|
}
|
|
2500
3204
|
}
|
|
2501
3205
|
async function disposeDatabase() {
|
|
3206
|
+
if (_walCheckpointTimer) {
|
|
3207
|
+
clearInterval(_walCheckpointTimer);
|
|
3208
|
+
_walCheckpointTimer = null;
|
|
3209
|
+
}
|
|
2502
3210
|
if (_daemonClient) {
|
|
2503
3211
|
_daemonClient.close();
|
|
2504
3212
|
_daemonClient = null;
|
|
2505
3213
|
}
|
|
3214
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
3215
|
+
_adapterClient.close();
|
|
3216
|
+
}
|
|
3217
|
+
_adapterClient = null;
|
|
2506
3218
|
if (_client) {
|
|
2507
3219
|
_client.close();
|
|
2508
3220
|
_client = null;
|
|
2509
3221
|
_resilientClient = null;
|
|
2510
3222
|
}
|
|
2511
3223
|
}
|
|
2512
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
3224
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2513
3225
|
var init_database = __esm({
|
|
2514
3226
|
"src/lib/database.ts"() {
|
|
2515
3227
|
"use strict";
|
|
2516
3228
|
init_db_retry();
|
|
2517
3229
|
init_employees();
|
|
3230
|
+
init_database_adapter();
|
|
2518
3231
|
_client = null;
|
|
2519
3232
|
_resilientClient = null;
|
|
2520
3233
|
_walCheckpointTimer = null;
|
|
2521
3234
|
_daemonClient = null;
|
|
3235
|
+
_adapterClient = null;
|
|
2522
3236
|
initTurso = initDatabase;
|
|
2523
3237
|
disposeTurso = disposeDatabase;
|
|
2524
3238
|
}
|
|
@@ -2543,7 +3257,7 @@ __export(license_exports, {
|
|
|
2543
3257
|
});
|
|
2544
3258
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2545
3259
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2546
|
-
import
|
|
3260
|
+
import path8 from "path";
|
|
2547
3261
|
import { jwtVerify, importSPKI } from "jose";
|
|
2548
3262
|
async function fetchRetry(url, init) {
|
|
2549
3263
|
try {
|
|
@@ -2554,7 +3268,7 @@ async function fetchRetry(url, init) {
|
|
|
2554
3268
|
}
|
|
2555
3269
|
}
|
|
2556
3270
|
function loadDeviceId() {
|
|
2557
|
-
const deviceJsonPath =
|
|
3271
|
+
const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
|
|
2558
3272
|
try {
|
|
2559
3273
|
if (existsSync8(deviceJsonPath)) {
|
|
2560
3274
|
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
@@ -2719,7 +3433,7 @@ async function checkLicense() {
|
|
|
2719
3433
|
let key = loadLicense();
|
|
2720
3434
|
if (!key) {
|
|
2721
3435
|
try {
|
|
2722
|
-
const configPath =
|
|
3436
|
+
const configPath = path8.join(EXE_AI_DIR, "config.json");
|
|
2723
3437
|
if (existsSync8(configPath)) {
|
|
2724
3438
|
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
2725
3439
|
const cloud = raw.cloud;
|
|
@@ -2880,9 +3594,9 @@ var init_license = __esm({
|
|
|
2880
3594
|
"src/lib/license.ts"() {
|
|
2881
3595
|
"use strict";
|
|
2882
3596
|
init_config();
|
|
2883
|
-
LICENSE_PATH =
|
|
2884
|
-
CACHE_PATH =
|
|
2885
|
-
DEVICE_ID_PATH =
|
|
3597
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
3598
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
3599
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2886
3600
|
API_BASE = "https://askexe.com/cloud";
|
|
2887
3601
|
RETRY_DELAY_MS = 500;
|
|
2888
3602
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -2913,7 +3627,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2913
3627
|
|
|
2914
3628
|
// src/lib/plan-limits.ts
|
|
2915
3629
|
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2916
|
-
import
|
|
3630
|
+
import path9 from "path";
|
|
2917
3631
|
function getLicenseSync() {
|
|
2918
3632
|
try {
|
|
2919
3633
|
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
@@ -2985,14 +3699,14 @@ var init_plan_limits = __esm({
|
|
|
2985
3699
|
this.name = "PlanLimitError";
|
|
2986
3700
|
}
|
|
2987
3701
|
};
|
|
2988
|
-
CACHE_PATH2 =
|
|
3702
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2989
3703
|
}
|
|
2990
3704
|
});
|
|
2991
3705
|
|
|
2992
3706
|
// src/lib/notifications.ts
|
|
2993
3707
|
import crypto from "crypto";
|
|
2994
|
-
import
|
|
2995
|
-
import
|
|
3708
|
+
import path10 from "path";
|
|
3709
|
+
import os7 from "os";
|
|
2996
3710
|
import {
|
|
2997
3711
|
readFileSync as readFileSync10,
|
|
2998
3712
|
readdirSync,
|
|
@@ -3148,8 +3862,8 @@ __export(tasks_crud_exports, {
|
|
|
3148
3862
|
writeCheckpoint: () => writeCheckpoint
|
|
3149
3863
|
});
|
|
3150
3864
|
import crypto3 from "crypto";
|
|
3151
|
-
import
|
|
3152
|
-
import
|
|
3865
|
+
import path11 from "path";
|
|
3866
|
+
import os8 from "os";
|
|
3153
3867
|
import { execSync as execSync5 } from "child_process";
|
|
3154
3868
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3155
3869
|
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
@@ -3327,8 +4041,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3327
4041
|
}
|
|
3328
4042
|
if (input.baseDir) {
|
|
3329
4043
|
try {
|
|
3330
|
-
await mkdir3(
|
|
3331
|
-
await mkdir3(
|
|
4044
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4045
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3332
4046
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3333
4047
|
await ensureGitignoreExe(input.baseDir);
|
|
3334
4048
|
} catch {
|
|
@@ -3364,9 +4078,9 @@ ${laneWarning}` : laneWarning;
|
|
|
3364
4078
|
});
|
|
3365
4079
|
if (input.baseDir) {
|
|
3366
4080
|
try {
|
|
3367
|
-
const EXE_OS_DIR =
|
|
3368
|
-
const mdPath =
|
|
3369
|
-
const mdDir =
|
|
4081
|
+
const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
|
|
4082
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
4083
|
+
const mdDir = path11.dirname(mdPath);
|
|
3370
4084
|
if (!existsSync11(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3371
4085
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3372
4086
|
const mdContent = `# ${input.title}
|
|
@@ -3667,7 +4381,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3667
4381
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3668
4382
|
}
|
|
3669
4383
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3670
|
-
const archPath =
|
|
4384
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3671
4385
|
try {
|
|
3672
4386
|
if (existsSync11(archPath)) return;
|
|
3673
4387
|
const template = [
|
|
@@ -3702,7 +4416,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3702
4416
|
}
|
|
3703
4417
|
}
|
|
3704
4418
|
async function ensureGitignoreExe(baseDir) {
|
|
3705
|
-
const gitignorePath =
|
|
4419
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
3706
4420
|
try {
|
|
3707
4421
|
if (existsSync11(gitignorePath)) {
|
|
3708
4422
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -3736,13 +4450,13 @@ var init_tasks_crud = __esm({
|
|
|
3736
4450
|
});
|
|
3737
4451
|
|
|
3738
4452
|
// src/lib/tasks-review.ts
|
|
3739
|
-
import
|
|
4453
|
+
import path12 from "path";
|
|
3740
4454
|
import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3741
4455
|
async function countPendingReviews(sessionScope) {
|
|
3742
4456
|
const client = getClient();
|
|
3743
4457
|
if (sessionScope) {
|
|
3744
4458
|
const result2 = await client.execute({
|
|
3745
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
4459
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3746
4460
|
args: [sessionScope]
|
|
3747
4461
|
});
|
|
3748
4462
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3918,11 +4632,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3918
4632
|
);
|
|
3919
4633
|
}
|
|
3920
4634
|
try {
|
|
3921
|
-
const cacheDir =
|
|
4635
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3922
4636
|
if (existsSync12(cacheDir)) {
|
|
3923
4637
|
for (const f of readdirSync2(cacheDir)) {
|
|
3924
4638
|
if (f.startsWith("review-notified-")) {
|
|
3925
|
-
unlinkSync4(
|
|
4639
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
3926
4640
|
}
|
|
3927
4641
|
}
|
|
3928
4642
|
}
|
|
@@ -3943,7 +4657,7 @@ var init_tasks_review = __esm({
|
|
|
3943
4657
|
});
|
|
3944
4658
|
|
|
3945
4659
|
// src/lib/tasks-chain.ts
|
|
3946
|
-
import
|
|
4660
|
+
import path13 from "path";
|
|
3947
4661
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3948
4662
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3949
4663
|
const client = getClient();
|
|
@@ -3960,7 +4674,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3960
4674
|
});
|
|
3961
4675
|
for (const ur of unblockedRows.rows) {
|
|
3962
4676
|
try {
|
|
3963
|
-
const ubFile =
|
|
4677
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3964
4678
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3965
4679
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3966
4680
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4029,7 +4743,7 @@ var init_tasks_chain = __esm({
|
|
|
4029
4743
|
|
|
4030
4744
|
// src/lib/project-name.ts
|
|
4031
4745
|
import { execSync as execSync6 } from "child_process";
|
|
4032
|
-
import
|
|
4746
|
+
import path14 from "path";
|
|
4033
4747
|
function getProjectName(cwd2) {
|
|
4034
4748
|
const dir = cwd2 ?? process.cwd();
|
|
4035
4749
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4042,7 +4756,7 @@ function getProjectName(cwd2) {
|
|
|
4042
4756
|
timeout: 2e3,
|
|
4043
4757
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4044
4758
|
}).trim();
|
|
4045
|
-
repoRoot =
|
|
4759
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
4046
4760
|
} catch {
|
|
4047
4761
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4048
4762
|
cwd: dir,
|
|
@@ -4051,11 +4765,11 @@ function getProjectName(cwd2) {
|
|
|
4051
4765
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4052
4766
|
}).trim();
|
|
4053
4767
|
}
|
|
4054
|
-
_cached2 =
|
|
4768
|
+
_cached2 = path14.basename(repoRoot);
|
|
4055
4769
|
_cachedCwd = dir;
|
|
4056
4770
|
return _cached2;
|
|
4057
4771
|
} catch {
|
|
4058
|
-
_cached2 =
|
|
4772
|
+
_cached2 = path14.basename(dir);
|
|
4059
4773
|
_cachedCwd = dir;
|
|
4060
4774
|
return _cached2;
|
|
4061
4775
|
}
|
|
@@ -4528,7 +5242,7 @@ __export(tasks_exports, {
|
|
|
4528
5242
|
updateTaskStatus: () => updateTaskStatus,
|
|
4529
5243
|
writeCheckpoint: () => writeCheckpoint
|
|
4530
5244
|
});
|
|
4531
|
-
import
|
|
5245
|
+
import path15 from "path";
|
|
4532
5246
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4533
5247
|
async function createTask(input) {
|
|
4534
5248
|
const result = await createTaskCore(input);
|
|
@@ -4548,8 +5262,8 @@ async function updateTask(input) {
|
|
|
4548
5262
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4549
5263
|
try {
|
|
4550
5264
|
const agent = String(row.assigned_to);
|
|
4551
|
-
const cacheDir =
|
|
4552
|
-
const cachePath =
|
|
5265
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
5266
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
4553
5267
|
if (input.status === "in_progress") {
|
|
4554
5268
|
mkdirSync5(cacheDir, { recursive: true });
|
|
4555
5269
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -5020,12 +5734,12 @@ __export(tmux_routing_exports, {
|
|
|
5020
5734
|
});
|
|
5021
5735
|
import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
|
|
5022
5736
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5023
|
-
import
|
|
5024
|
-
import
|
|
5737
|
+
import path16 from "path";
|
|
5738
|
+
import os9 from "os";
|
|
5025
5739
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5026
5740
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5027
5741
|
function spawnLockPath(sessionName) {
|
|
5028
|
-
return
|
|
5742
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5029
5743
|
}
|
|
5030
5744
|
function isProcessAlive(pid) {
|
|
5031
5745
|
try {
|
|
@@ -5062,8 +5776,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5062
5776
|
function resolveBehaviorsExporterScript() {
|
|
5063
5777
|
try {
|
|
5064
5778
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5065
|
-
const scriptPath =
|
|
5066
|
-
|
|
5779
|
+
const scriptPath = path16.join(
|
|
5780
|
+
path16.dirname(thisFile),
|
|
5067
5781
|
"..",
|
|
5068
5782
|
"bin",
|
|
5069
5783
|
"exe-export-behaviors.js"
|
|
@@ -5138,7 +5852,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5138
5852
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5139
5853
|
}
|
|
5140
5854
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5141
|
-
const filePath =
|
|
5855
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5142
5856
|
writeFileSync7(filePath, JSON.stringify({
|
|
5143
5857
|
parentExe: rootExe,
|
|
5144
5858
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -5147,7 +5861,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5147
5861
|
}
|
|
5148
5862
|
function getParentExe(sessionKey) {
|
|
5149
5863
|
try {
|
|
5150
|
-
const data = JSON.parse(readFileSync12(
|
|
5864
|
+
const data = JSON.parse(readFileSync12(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5151
5865
|
return data.parentExe || null;
|
|
5152
5866
|
} catch {
|
|
5153
5867
|
return null;
|
|
@@ -5156,7 +5870,7 @@ function getParentExe(sessionKey) {
|
|
|
5156
5870
|
function getDispatchedBy(sessionKey) {
|
|
5157
5871
|
try {
|
|
5158
5872
|
const data = JSON.parse(readFileSync12(
|
|
5159
|
-
|
|
5873
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5160
5874
|
"utf8"
|
|
5161
5875
|
));
|
|
5162
5876
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5167,15 +5881,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
5167
5881
|
function resolveExeSession() {
|
|
5168
5882
|
const mySession = getMySession();
|
|
5169
5883
|
if (!mySession) return null;
|
|
5884
|
+
const fromSessionName = extractRootExe(mySession);
|
|
5170
5885
|
try {
|
|
5171
5886
|
const key = getSessionKey();
|
|
5172
5887
|
const parentExe = getParentExe(key);
|
|
5173
5888
|
if (parentExe) {
|
|
5174
|
-
|
|
5889
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
5890
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
5891
|
+
process.stderr.write(
|
|
5892
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
5893
|
+
`
|
|
5894
|
+
);
|
|
5895
|
+
return fromSessionName;
|
|
5896
|
+
}
|
|
5897
|
+
return fromCache;
|
|
5175
5898
|
}
|
|
5176
5899
|
} catch {
|
|
5177
5900
|
}
|
|
5178
|
-
return
|
|
5901
|
+
return fromSessionName ?? mySession;
|
|
5179
5902
|
}
|
|
5180
5903
|
function isEmployeeAlive(sessionName) {
|
|
5181
5904
|
return getTransport().isAlive(sessionName);
|
|
@@ -5333,7 +6056,7 @@ function sendIntercom(targetSession) {
|
|
|
5333
6056
|
try {
|
|
5334
6057
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5335
6058
|
const agent = baseAgentName(rawAgent);
|
|
5336
|
-
const markerPath =
|
|
6059
|
+
const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5337
6060
|
if (existsSync13(markerPath)) {
|
|
5338
6061
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
5339
6062
|
return "debounced";
|
|
@@ -5343,7 +6066,7 @@ function sendIntercom(targetSession) {
|
|
|
5343
6066
|
try {
|
|
5344
6067
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5345
6068
|
const agent = baseAgentName(rawAgent);
|
|
5346
|
-
const taskDir =
|
|
6069
|
+
const taskDir = path16.join(process.cwd(), "exe", agent);
|
|
5347
6070
|
if (existsSync13(taskDir)) {
|
|
5348
6071
|
const files = readdirSync3(taskDir).filter(
|
|
5349
6072
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -5477,8 +6200,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5477
6200
|
const transport = getTransport();
|
|
5478
6201
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5479
6202
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5480
|
-
const logDir =
|
|
5481
|
-
const logFile =
|
|
6203
|
+
const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
|
|
6204
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5482
6205
|
if (!existsSync13(logDir)) {
|
|
5483
6206
|
mkdirSync6(logDir, { recursive: true });
|
|
5484
6207
|
}
|
|
@@ -5486,14 +6209,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5486
6209
|
let cleanupSuffix = "";
|
|
5487
6210
|
try {
|
|
5488
6211
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5489
|
-
const cleanupScript =
|
|
6212
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5490
6213
|
if (existsSync13(cleanupScript)) {
|
|
5491
6214
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5492
6215
|
}
|
|
5493
6216
|
} catch {
|
|
5494
6217
|
}
|
|
5495
6218
|
try {
|
|
5496
|
-
const claudeJsonPath =
|
|
6219
|
+
const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
|
|
5497
6220
|
let claudeJson = {};
|
|
5498
6221
|
try {
|
|
5499
6222
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -5508,10 +6231,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5508
6231
|
} catch {
|
|
5509
6232
|
}
|
|
5510
6233
|
try {
|
|
5511
|
-
const settingsDir =
|
|
6234
|
+
const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
|
|
5512
6235
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5513
|
-
const projSettingsDir =
|
|
5514
|
-
const settingsPath =
|
|
6236
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
6237
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
5515
6238
|
let settings = {};
|
|
5516
6239
|
try {
|
|
5517
6240
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -5558,8 +6281,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5558
6281
|
let behaviorsFlag = "";
|
|
5559
6282
|
let legacyFallbackWarned = false;
|
|
5560
6283
|
if (!useExeAgent && !useBinSymlink) {
|
|
5561
|
-
const identityPath =
|
|
5562
|
-
|
|
6284
|
+
const identityPath = path16.join(
|
|
6285
|
+
os9.homedir(),
|
|
5563
6286
|
".exe-os",
|
|
5564
6287
|
"identity",
|
|
5565
6288
|
`${employeeName}.md`
|
|
@@ -5574,7 +6297,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5574
6297
|
}
|
|
5575
6298
|
const behaviorsFile = exportBehaviorsSync(
|
|
5576
6299
|
employeeName,
|
|
5577
|
-
|
|
6300
|
+
path16.basename(spawnCwd),
|
|
5578
6301
|
sessionName
|
|
5579
6302
|
);
|
|
5580
6303
|
if (behaviorsFile) {
|
|
@@ -5589,9 +6312,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5589
6312
|
}
|
|
5590
6313
|
let sessionContextFlag = "";
|
|
5591
6314
|
try {
|
|
5592
|
-
const ctxDir =
|
|
6315
|
+
const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5593
6316
|
mkdirSync6(ctxDir, { recursive: true });
|
|
5594
|
-
const ctxFile =
|
|
6317
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5595
6318
|
const ctxContent = [
|
|
5596
6319
|
`## Session Context`,
|
|
5597
6320
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5675,7 +6398,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5675
6398
|
transport.pipeLog(sessionName, logFile);
|
|
5676
6399
|
try {
|
|
5677
6400
|
const mySession = getMySession();
|
|
5678
|
-
const dispatchInfo =
|
|
6401
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5679
6402
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5680
6403
|
dispatchedBy: mySession,
|
|
5681
6404
|
rootExe: exeSession,
|
|
@@ -5750,15 +6473,15 @@ var init_tmux_routing = __esm({
|
|
|
5750
6473
|
init_intercom_queue();
|
|
5751
6474
|
init_plan_limits();
|
|
5752
6475
|
init_employees();
|
|
5753
|
-
SPAWN_LOCK_DIR =
|
|
5754
|
-
SESSION_CACHE =
|
|
6476
|
+
SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
6477
|
+
SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5755
6478
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5756
6479
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5757
6480
|
VERIFY_PANE_LINES = 200;
|
|
5758
6481
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5759
6482
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5760
|
-
INTERCOM_LOG2 =
|
|
5761
|
-
DEBOUNCE_FILE =
|
|
6483
|
+
INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
6484
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5762
6485
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5763
6486
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5764
6487
|
}
|
|
@@ -7644,10 +8367,10 @@ var init_hooks = __esm({
|
|
|
7644
8367
|
});
|
|
7645
8368
|
|
|
7646
8369
|
// src/runtime/safety-checks.ts
|
|
7647
|
-
import
|
|
7648
|
-
import
|
|
8370
|
+
import path17 from "path";
|
|
8371
|
+
import os10 from "os";
|
|
7649
8372
|
function checkPathSafety(filePath) {
|
|
7650
|
-
const resolved =
|
|
8373
|
+
const resolved = path17.resolve(filePath);
|
|
7651
8374
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
7652
8375
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
7653
8376
|
if (matches) {
|
|
@@ -7657,7 +8380,7 @@ function checkPathSafety(filePath) {
|
|
|
7657
8380
|
return { safe: true, bypassImmune: true };
|
|
7658
8381
|
}
|
|
7659
8382
|
function checkReadPathSafety(filePath) {
|
|
7660
|
-
const resolved =
|
|
8383
|
+
const resolved = path17.resolve(filePath);
|
|
7661
8384
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
7662
8385
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
7663
8386
|
);
|
|
@@ -7672,7 +8395,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
7672
8395
|
var init_safety_checks = __esm({
|
|
7673
8396
|
"src/runtime/safety-checks.ts"() {
|
|
7674
8397
|
"use strict";
|
|
7675
|
-
HOME =
|
|
8398
|
+
HOME = os10.homedir();
|
|
7676
8399
|
BYPASS_IMMUNE_PATTERNS = [
|
|
7677
8400
|
{
|
|
7678
8401
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -7683,11 +8406,11 @@ var init_safety_checks = __esm({
|
|
|
7683
8406
|
reason: "Git config can set hooks and command execution"
|
|
7684
8407
|
},
|
|
7685
8408
|
{
|
|
7686
|
-
pattern: (p) => p.startsWith(
|
|
8409
|
+
pattern: (p) => p.startsWith(path17.join(HOME, ".claude")),
|
|
7687
8410
|
reason: "Claude configuration files are protected"
|
|
7688
8411
|
},
|
|
7689
8412
|
{
|
|
7690
|
-
pattern: (p) => p.startsWith(
|
|
8413
|
+
pattern: (p) => p.startsWith(path17.join(HOME, ".exe-os")),
|
|
7691
8414
|
reason: "exe-os configuration files are protected"
|
|
7692
8415
|
},
|
|
7693
8416
|
{
|
|
@@ -7704,7 +8427,7 @@ var init_safety_checks = __esm({
|
|
|
7704
8427
|
},
|
|
7705
8428
|
{
|
|
7706
8429
|
pattern: (p) => {
|
|
7707
|
-
const name =
|
|
8430
|
+
const name = path17.basename(p);
|
|
7708
8431
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
7709
8432
|
},
|
|
7710
8433
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -7731,7 +8454,7 @@ __export(file_read_exports, {
|
|
|
7731
8454
|
FileReadTool: () => FileReadTool
|
|
7732
8455
|
});
|
|
7733
8456
|
import fs3 from "fs/promises";
|
|
7734
|
-
import
|
|
8457
|
+
import path18 from "path";
|
|
7735
8458
|
import { z } from "zod";
|
|
7736
8459
|
function isBinary(buf) {
|
|
7737
8460
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -7767,7 +8490,7 @@ var init_file_read = __esm({
|
|
|
7767
8490
|
return { behavior: "allow" };
|
|
7768
8491
|
},
|
|
7769
8492
|
async call(input, context) {
|
|
7770
|
-
const filePath =
|
|
8493
|
+
const filePath = path18.isAbsolute(input.file_path) ? input.file_path : path18.resolve(context.cwd, input.file_path);
|
|
7771
8494
|
let stat;
|
|
7772
8495
|
try {
|
|
7773
8496
|
stat = await fs3.stat(filePath);
|
|
@@ -7807,7 +8530,7 @@ __export(glob_exports, {
|
|
|
7807
8530
|
GlobTool: () => GlobTool
|
|
7808
8531
|
});
|
|
7809
8532
|
import fs4 from "fs/promises";
|
|
7810
|
-
import
|
|
8533
|
+
import path19 from "path";
|
|
7811
8534
|
import { z as z2 } from "zod";
|
|
7812
8535
|
async function walkDir(dir, maxDepth = 10) {
|
|
7813
8536
|
const results = [];
|
|
@@ -7823,7 +8546,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
7823
8546
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
7824
8547
|
continue;
|
|
7825
8548
|
}
|
|
7826
|
-
const fullPath =
|
|
8549
|
+
const fullPath = path19.join(current, entry.name);
|
|
7827
8550
|
if (entry.isDirectory()) {
|
|
7828
8551
|
await walk(fullPath, depth + 1);
|
|
7829
8552
|
} else {
|
|
@@ -7857,11 +8580,11 @@ var init_glob = __esm({
|
|
|
7857
8580
|
inputSchema: inputSchema2,
|
|
7858
8581
|
isReadOnly: true,
|
|
7859
8582
|
async call(input, context) {
|
|
7860
|
-
const baseDir = input.path ?
|
|
8583
|
+
const baseDir = input.path ? path19.isAbsolute(input.path) ? input.path : path19.resolve(context.cwd, input.path) : context.cwd;
|
|
7861
8584
|
try {
|
|
7862
8585
|
const entries = await walkDir(baseDir);
|
|
7863
8586
|
const matched = entries.filter(
|
|
7864
|
-
(e) => simpleGlobMatch(
|
|
8587
|
+
(e) => simpleGlobMatch(path19.relative(baseDir, e.path), input.pattern)
|
|
7865
8588
|
);
|
|
7866
8589
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
7867
8590
|
if (matched.length === 0) {
|
|
@@ -7887,7 +8610,7 @@ __export(grep_exports, {
|
|
|
7887
8610
|
});
|
|
7888
8611
|
import { spawn as spawn2 } from "child_process";
|
|
7889
8612
|
import fs5 from "fs/promises";
|
|
7890
|
-
import
|
|
8613
|
+
import path20 from "path";
|
|
7891
8614
|
import { z as z3 } from "zod";
|
|
7892
8615
|
function runRipgrep(input, searchPath, context) {
|
|
7893
8616
|
return new Promise((resolve, reject) => {
|
|
@@ -7941,7 +8664,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
7941
8664
|
}
|
|
7942
8665
|
for (const entry of entries) {
|
|
7943
8666
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
7944
|
-
const fullPath =
|
|
8667
|
+
const fullPath = path20.join(dir, entry.name);
|
|
7945
8668
|
if (entry.isDirectory()) {
|
|
7946
8669
|
await walk(fullPath);
|
|
7947
8670
|
} else {
|
|
@@ -7987,7 +8710,7 @@ var init_grep = __esm({
|
|
|
7987
8710
|
inputSchema: inputSchema3,
|
|
7988
8711
|
isReadOnly: true,
|
|
7989
8712
|
async call(input, context) {
|
|
7990
|
-
const searchPath = input.path ?
|
|
8713
|
+
const searchPath = input.path ? path20.isAbsolute(input.path) ? input.path : path20.resolve(context.cwd, input.path) : context.cwd;
|
|
7991
8714
|
try {
|
|
7992
8715
|
const result = await runRipgrep(input, searchPath, context);
|
|
7993
8716
|
return result;
|
|
@@ -8012,7 +8735,7 @@ __export(file_write_exports, {
|
|
|
8012
8735
|
FileWriteTool: () => FileWriteTool
|
|
8013
8736
|
});
|
|
8014
8737
|
import fs6 from "fs/promises";
|
|
8015
|
-
import
|
|
8738
|
+
import path21 from "path";
|
|
8016
8739
|
import { z as z4 } from "zod";
|
|
8017
8740
|
var inputSchema4, FileWriteTool;
|
|
8018
8741
|
var init_file_write = __esm({
|
|
@@ -8040,8 +8763,8 @@ var init_file_write = __esm({
|
|
|
8040
8763
|
return { behavior: "allow" };
|
|
8041
8764
|
},
|
|
8042
8765
|
async call(input, context) {
|
|
8043
|
-
const filePath =
|
|
8044
|
-
const dir =
|
|
8766
|
+
const filePath = path21.isAbsolute(input.file_path) ? input.file_path : path21.resolve(context.cwd, input.file_path);
|
|
8767
|
+
const dir = path21.dirname(filePath);
|
|
8045
8768
|
await fs6.mkdir(dir, { recursive: true });
|
|
8046
8769
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
8047
8770
|
return {
|
|
@@ -8059,7 +8782,7 @@ __export(file_edit_exports, {
|
|
|
8059
8782
|
FileEditTool: () => FileEditTool
|
|
8060
8783
|
});
|
|
8061
8784
|
import fs7 from "fs/promises";
|
|
8062
|
-
import
|
|
8785
|
+
import path22 from "path";
|
|
8063
8786
|
import { z as z5 } from "zod";
|
|
8064
8787
|
function countOccurrences(haystack, needle) {
|
|
8065
8788
|
let count = 0;
|
|
@@ -8100,7 +8823,7 @@ var init_file_edit = __esm({
|
|
|
8100
8823
|
return { behavior: "allow" };
|
|
8101
8824
|
},
|
|
8102
8825
|
async call(input, context) {
|
|
8103
|
-
const filePath =
|
|
8826
|
+
const filePath = path22.isAbsolute(input.file_path) ? input.file_path : path22.resolve(context.cwd, input.file_path);
|
|
8104
8827
|
let content;
|
|
8105
8828
|
try {
|
|
8106
8829
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -8766,13 +9489,13 @@ __export(keychain_exports, {
|
|
|
8766
9489
|
});
|
|
8767
9490
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
8768
9491
|
import { existsSync as existsSync14 } from "fs";
|
|
8769
|
-
import
|
|
8770
|
-
import
|
|
9492
|
+
import path25 from "path";
|
|
9493
|
+
import os11 from "os";
|
|
8771
9494
|
function getKeyDir() {
|
|
8772
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
9495
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os11.homedir(), ".exe-os");
|
|
8773
9496
|
}
|
|
8774
9497
|
function getKeyPath() {
|
|
8775
|
-
return
|
|
9498
|
+
return path25.join(getKeyDir(), "master.key");
|
|
8776
9499
|
}
|
|
8777
9500
|
async function tryKeytar() {
|
|
8778
9501
|
try {
|
|
@@ -8795,7 +9518,7 @@ async function getMasterKey() {
|
|
|
8795
9518
|
const keyPath = getKeyPath();
|
|
8796
9519
|
if (!existsSync14(keyPath)) {
|
|
8797
9520
|
process.stderr.write(
|
|
8798
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
9521
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
8799
9522
|
`
|
|
8800
9523
|
);
|
|
8801
9524
|
return null;
|
|
@@ -9136,8 +9859,8 @@ __export(wiki_client_exports, {
|
|
|
9136
9859
|
listDocuments: () => listDocuments,
|
|
9137
9860
|
listWorkspaces: () => listWorkspaces
|
|
9138
9861
|
});
|
|
9139
|
-
async function wikiFetch(config,
|
|
9140
|
-
const url = `${config.baseUrl}/api/v1${
|
|
9862
|
+
async function wikiFetch(config, path27, method = "GET", body) {
|
|
9863
|
+
const url = `${config.baseUrl}/api/v1${path27}`;
|
|
9141
9864
|
const headers = {
|
|
9142
9865
|
Authorization: `Bearer ${config.apiKey}`,
|
|
9143
9866
|
"Content-Type": "application/json"
|
|
@@ -9170,7 +9893,7 @@ async function wikiFetch(config, path26, method = "GET", body) {
|
|
|
9170
9893
|
}
|
|
9171
9894
|
}
|
|
9172
9895
|
if (!response.ok) {
|
|
9173
|
-
throw new Error(`Wiki API ${method} ${
|
|
9896
|
+
throw new Error(`Wiki API ${method} ${path27}: ${response.status} ${response.statusText}`);
|
|
9174
9897
|
}
|
|
9175
9898
|
return response.json();
|
|
9176
9899
|
} finally {
|
|
@@ -9291,7 +10014,7 @@ __export(shard_manager_exports, {
|
|
|
9291
10014
|
listShards: () => listShards,
|
|
9292
10015
|
shardExists: () => shardExists
|
|
9293
10016
|
});
|
|
9294
|
-
import
|
|
10017
|
+
import path26 from "path";
|
|
9295
10018
|
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
9296
10019
|
import { createClient as createClient2 } from "@libsql/client";
|
|
9297
10020
|
function initShardManager(encryptionKey) {
|
|
@@ -9317,7 +10040,7 @@ function getShardClient(projectName) {
|
|
|
9317
10040
|
}
|
|
9318
10041
|
const cached = _shards.get(safeName);
|
|
9319
10042
|
if (cached) return cached;
|
|
9320
|
-
const dbPath =
|
|
10043
|
+
const dbPath = path26.join(SHARDS_DIR, `${safeName}.db`);
|
|
9321
10044
|
const client = createClient2({
|
|
9322
10045
|
url: `file:${dbPath}`,
|
|
9323
10046
|
encryptionKey: _encryptionKey
|
|
@@ -9327,7 +10050,7 @@ function getShardClient(projectName) {
|
|
|
9327
10050
|
}
|
|
9328
10051
|
function shardExists(projectName) {
|
|
9329
10052
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
9330
|
-
return existsSync15(
|
|
10053
|
+
return existsSync15(path26.join(SHARDS_DIR, `${safeName}.db`));
|
|
9331
10054
|
}
|
|
9332
10055
|
function listShards() {
|
|
9333
10056
|
if (!existsSync15(SHARDS_DIR)) return [];
|
|
@@ -9404,7 +10127,23 @@ async function ensureShardSchema(client) {
|
|
|
9404
10127
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
9405
10128
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
9406
10129
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
9407
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
10130
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
10131
|
+
// Metadata enrichment columns (must match database.ts)
|
|
10132
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
10133
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
10134
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
10135
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
10136
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
10137
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
10138
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
10139
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
10140
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
10141
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
10142
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
10143
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
10144
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
10145
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
10146
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
9408
10147
|
]) {
|
|
9409
10148
|
try {
|
|
9410
10149
|
await client.execute(col);
|
|
@@ -9516,7 +10255,7 @@ var init_shard_manager = __esm({
|
|
|
9516
10255
|
"src/lib/shard-manager.ts"() {
|
|
9517
10256
|
"use strict";
|
|
9518
10257
|
init_config();
|
|
9519
|
-
SHARDS_DIR =
|
|
10258
|
+
SHARDS_DIR = path26.join(EXE_AI_DIR, "shards");
|
|
9520
10259
|
_shards = /* @__PURE__ */ new Map();
|
|
9521
10260
|
_encryptionKey = null;
|
|
9522
10261
|
_shardingEnabled = false;
|
|
@@ -14088,8 +14827,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
14088
14827
|
}
|
|
14089
14828
|
|
|
14090
14829
|
// src/tui/ink/components/ErrorOverview.js
|
|
14091
|
-
var cleanupPath = (
|
|
14092
|
-
return
|
|
14830
|
+
var cleanupPath = (path27) => {
|
|
14831
|
+
return path27?.replace(`file://${cwd()}/`, "");
|
|
14093
14832
|
};
|
|
14094
14833
|
var stackUtils = new StackUtils({
|
|
14095
14834
|
cwd: cwd(),
|
|
@@ -16107,7 +16846,7 @@ function Footer() {
|
|
|
16107
16846
|
// src/tui/views/CommandCenter.tsx
|
|
16108
16847
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
16109
16848
|
import TextInput from "ink-text-input";
|
|
16110
|
-
import
|
|
16849
|
+
import path23 from "path";
|
|
16111
16850
|
import { homedir } from "os";
|
|
16112
16851
|
|
|
16113
16852
|
// src/tui/components/StatusDot.tsx
|
|
@@ -17108,7 +17847,7 @@ function CommandCenterView({
|
|
|
17108
17847
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
17109
17848
|
projectName: p.projectName,
|
|
17110
17849
|
exeSession: p.exeSession,
|
|
17111
|
-
projectDir:
|
|
17850
|
+
projectDir: path23.join(homedir(), p.projectName),
|
|
17112
17851
|
employeeCount: p.employees.length,
|
|
17113
17852
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
17114
17853
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -17457,7 +18196,7 @@ function ChatMessageRow({ msg }) {
|
|
|
17457
18196
|
|
|
17458
18197
|
// src/tui/views/Sessions.tsx
|
|
17459
18198
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
17460
|
-
import
|
|
18199
|
+
import path24 from "path";
|
|
17461
18200
|
import { homedir as homedir2 } from "os";
|
|
17462
18201
|
|
|
17463
18202
|
// src/tui/components/TmuxPane.tsx
|
|
@@ -17761,7 +18500,7 @@ function SessionsView({
|
|
|
17761
18500
|
if (demo) {
|
|
17762
18501
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
17763
18502
|
...p,
|
|
17764
|
-
projectDir:
|
|
18503
|
+
projectDir: path24.join(homedir2(), p.projectName),
|
|
17765
18504
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
17766
18505
|
})));
|
|
17767
18506
|
return;
|
|
@@ -19882,8 +20621,8 @@ function SettingsView({ onBack }) {
|
|
|
19882
20621
|
let version = "unknown";
|
|
19883
20622
|
try {
|
|
19884
20623
|
const { readFileSync: readFileSync13 } = await import("fs");
|
|
19885
|
-
const { createRequire } = await import("module");
|
|
19886
|
-
const require2 =
|
|
20624
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
20625
|
+
const require2 = createRequire2(import.meta.url);
|
|
19887
20626
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
19888
20627
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
19889
20628
|
version = pkg.version;
|