@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
|
@@ -326,7 +326,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
326
326
|
if (!emp) return false;
|
|
327
327
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
328
328
|
}
|
|
329
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
329
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
330
330
|
var init_employees = __esm({
|
|
331
331
|
"src/lib/employees.ts"() {
|
|
332
332
|
"use strict";
|
|
@@ -335,6 +335,7 @@ var init_employees = __esm({
|
|
|
335
335
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
336
336
|
COORDINATOR_ROLE = "COO";
|
|
337
337
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
338
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
338
339
|
}
|
|
339
340
|
});
|
|
340
341
|
|
|
@@ -794,13 +795,597 @@ var init_db_retry = __esm({
|
|
|
794
795
|
}
|
|
795
796
|
});
|
|
796
797
|
|
|
798
|
+
// src/lib/database-adapter.ts
|
|
799
|
+
import os5 from "os";
|
|
800
|
+
import path7 from "path";
|
|
801
|
+
import { createRequire } from "module";
|
|
802
|
+
import { pathToFileURL } from "url";
|
|
803
|
+
function quotedIdentifier(identifier) {
|
|
804
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
805
|
+
}
|
|
806
|
+
function unqualifiedTableName(name) {
|
|
807
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
808
|
+
const parts = raw.split(".");
|
|
809
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
810
|
+
}
|
|
811
|
+
function stripTrailingSemicolon(sql) {
|
|
812
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
813
|
+
}
|
|
814
|
+
function appendClause(sql, clause) {
|
|
815
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
816
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
817
|
+
if (!returningMatch) {
|
|
818
|
+
return `${trimmed}${clause}`;
|
|
819
|
+
}
|
|
820
|
+
const idx = returningMatch.index;
|
|
821
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
822
|
+
}
|
|
823
|
+
function normalizeStatement(stmt) {
|
|
824
|
+
if (typeof stmt === "string") {
|
|
825
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
826
|
+
}
|
|
827
|
+
const sql = stmt.sql;
|
|
828
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
829
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
830
|
+
}
|
|
831
|
+
return { kind: "named", sql, args: stmt.args };
|
|
832
|
+
}
|
|
833
|
+
function rewriteBooleanLiterals(sql) {
|
|
834
|
+
let out = sql;
|
|
835
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
836
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
837
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
838
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
839
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
840
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
841
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
842
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
843
|
+
}
|
|
844
|
+
return out;
|
|
845
|
+
}
|
|
846
|
+
function rewriteInsertOrIgnore(sql) {
|
|
847
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
848
|
+
return sql;
|
|
849
|
+
}
|
|
850
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
851
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
852
|
+
}
|
|
853
|
+
function rewriteInsertOrReplace(sql) {
|
|
854
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
855
|
+
if (!match) {
|
|
856
|
+
return sql;
|
|
857
|
+
}
|
|
858
|
+
const rawTable = match[1];
|
|
859
|
+
const rawColumns = match[2];
|
|
860
|
+
const remainder = match[3];
|
|
861
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
862
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
863
|
+
if (!conflictKeys?.length) {
|
|
864
|
+
return sql;
|
|
865
|
+
}
|
|
866
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
867
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
868
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
869
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
870
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
871
|
+
}
|
|
872
|
+
function rewriteSql(sql) {
|
|
873
|
+
let out = sql;
|
|
874
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
875
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
876
|
+
out = rewriteBooleanLiterals(out);
|
|
877
|
+
out = rewriteInsertOrReplace(out);
|
|
878
|
+
out = rewriteInsertOrIgnore(out);
|
|
879
|
+
return stripTrailingSemicolon(out);
|
|
880
|
+
}
|
|
881
|
+
function toBoolean(value) {
|
|
882
|
+
if (value === null || value === void 0) return value;
|
|
883
|
+
if (typeof value === "boolean") return value;
|
|
884
|
+
if (typeof value === "number") return value !== 0;
|
|
885
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
886
|
+
if (typeof value === "string") {
|
|
887
|
+
const normalized = value.trim().toLowerCase();
|
|
888
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
889
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
890
|
+
}
|
|
891
|
+
return Boolean(value);
|
|
892
|
+
}
|
|
893
|
+
function countQuestionMarks(sql, end) {
|
|
894
|
+
let count = 0;
|
|
895
|
+
let inSingle = false;
|
|
896
|
+
let inDouble = false;
|
|
897
|
+
let inLineComment = false;
|
|
898
|
+
let inBlockComment = false;
|
|
899
|
+
for (let i = 0; i < end; i++) {
|
|
900
|
+
const ch = sql[i];
|
|
901
|
+
const next = sql[i + 1];
|
|
902
|
+
if (inLineComment) {
|
|
903
|
+
if (ch === "\n") inLineComment = false;
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (inBlockComment) {
|
|
907
|
+
if (ch === "*" && next === "/") {
|
|
908
|
+
inBlockComment = false;
|
|
909
|
+
i += 1;
|
|
910
|
+
}
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
914
|
+
inLineComment = true;
|
|
915
|
+
i += 1;
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
919
|
+
inBlockComment = true;
|
|
920
|
+
i += 1;
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
924
|
+
inSingle = !inSingle;
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
928
|
+
inDouble = !inDouble;
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
932
|
+
count += 1;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return count;
|
|
936
|
+
}
|
|
937
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
938
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
939
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
940
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
941
|
+
for (const match of sql.matchAll(pattern)) {
|
|
942
|
+
const matchText = match[0];
|
|
943
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
944
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return indexes;
|
|
948
|
+
}
|
|
949
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
950
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
951
|
+
if (!match) return;
|
|
952
|
+
const rawTable = match[1];
|
|
953
|
+
const rawColumns = match[2];
|
|
954
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
955
|
+
if (!boolColumns?.size) return;
|
|
956
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
957
|
+
for (const [index, column] of columns.entries()) {
|
|
958
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
959
|
+
args[index] = toBoolean(args[index]);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
964
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
965
|
+
if (!match) return;
|
|
966
|
+
const rawTable = match[1];
|
|
967
|
+
const setClause = match[2];
|
|
968
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
969
|
+
if (!boolColumns?.size) return;
|
|
970
|
+
const assignments = setClause.split(",");
|
|
971
|
+
let placeholderIndex = 0;
|
|
972
|
+
for (const assignment of assignments) {
|
|
973
|
+
if (!assignment.includes("?")) continue;
|
|
974
|
+
placeholderIndex += 1;
|
|
975
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
976
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
977
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function coerceBooleanArgs(sql, args) {
|
|
982
|
+
const nextArgs = [...args];
|
|
983
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
984
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
985
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
986
|
+
for (const index of placeholderIndexes) {
|
|
987
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
988
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return nextArgs;
|
|
992
|
+
}
|
|
993
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
994
|
+
let out = "";
|
|
995
|
+
let placeholder = 0;
|
|
996
|
+
let inSingle = false;
|
|
997
|
+
let inDouble = false;
|
|
998
|
+
let inLineComment = false;
|
|
999
|
+
let inBlockComment = false;
|
|
1000
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1001
|
+
const ch = sql[i];
|
|
1002
|
+
const next = sql[i + 1];
|
|
1003
|
+
if (inLineComment) {
|
|
1004
|
+
out += ch;
|
|
1005
|
+
if (ch === "\n") inLineComment = false;
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
if (inBlockComment) {
|
|
1009
|
+
out += ch;
|
|
1010
|
+
if (ch === "*" && next === "/") {
|
|
1011
|
+
out += next;
|
|
1012
|
+
inBlockComment = false;
|
|
1013
|
+
i += 1;
|
|
1014
|
+
}
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1018
|
+
out += ch + next;
|
|
1019
|
+
inLineComment = true;
|
|
1020
|
+
i += 1;
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1024
|
+
out += ch + next;
|
|
1025
|
+
inBlockComment = true;
|
|
1026
|
+
i += 1;
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1030
|
+
inSingle = !inSingle;
|
|
1031
|
+
out += ch;
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1035
|
+
inDouble = !inDouble;
|
|
1036
|
+
out += ch;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1040
|
+
placeholder += 1;
|
|
1041
|
+
out += `$${placeholder}`;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
out += ch;
|
|
1045
|
+
}
|
|
1046
|
+
return out;
|
|
1047
|
+
}
|
|
1048
|
+
function translateStatementForPostgres(stmt) {
|
|
1049
|
+
const normalized = normalizeStatement(stmt);
|
|
1050
|
+
if (normalized.kind === "named") {
|
|
1051
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1052
|
+
}
|
|
1053
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1054
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1055
|
+
return {
|
|
1056
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1057
|
+
args: coercedArgs
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
function shouldBypassPostgres(stmt) {
|
|
1061
|
+
const normalized = normalizeStatement(stmt);
|
|
1062
|
+
if (normalized.kind === "named") {
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1066
|
+
}
|
|
1067
|
+
function shouldFallbackOnError(error) {
|
|
1068
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1069
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1070
|
+
}
|
|
1071
|
+
function isReadQuery(sql) {
|
|
1072
|
+
const trimmed = sql.trimStart();
|
|
1073
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1074
|
+
}
|
|
1075
|
+
function buildRow(row, columns) {
|
|
1076
|
+
const values = columns.map((column) => row[column]);
|
|
1077
|
+
return Object.assign(values, row);
|
|
1078
|
+
}
|
|
1079
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1080
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1081
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1082
|
+
return {
|
|
1083
|
+
columns,
|
|
1084
|
+
columnTypes: columns.map(() => ""),
|
|
1085
|
+
rows: resultRows,
|
|
1086
|
+
rowsAffected,
|
|
1087
|
+
lastInsertRowid: void 0,
|
|
1088
|
+
toJSON() {
|
|
1089
|
+
return {
|
|
1090
|
+
columns,
|
|
1091
|
+
columnTypes: columns.map(() => ""),
|
|
1092
|
+
rows,
|
|
1093
|
+
rowsAffected,
|
|
1094
|
+
lastInsertRowid: void 0
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
async function loadPrismaClient() {
|
|
1100
|
+
if (!prismaClientPromise) {
|
|
1101
|
+
prismaClientPromise = (async () => {
|
|
1102
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1103
|
+
if (explicitPath) {
|
|
1104
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1105
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1106
|
+
if (!PrismaClient2) {
|
|
1107
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1108
|
+
}
|
|
1109
|
+
return new PrismaClient2();
|
|
1110
|
+
}
|
|
1111
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
|
|
1112
|
+
const requireFromExeDb = createRequire(path7.join(exeDbRoot, "package.json"));
|
|
1113
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1114
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1115
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1116
|
+
if (!PrismaClient) {
|
|
1117
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1118
|
+
}
|
|
1119
|
+
return new PrismaClient();
|
|
1120
|
+
})();
|
|
1121
|
+
}
|
|
1122
|
+
return prismaClientPromise;
|
|
1123
|
+
}
|
|
1124
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1125
|
+
if (!compatibilityBootstrapPromise) {
|
|
1126
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1127
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1128
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1129
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1130
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1131
|
+
relation
|
|
1132
|
+
);
|
|
1133
|
+
if (!rows[0]?.regclass) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
await prisma.$executeRawUnsafe(
|
|
1137
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
})();
|
|
1141
|
+
}
|
|
1142
|
+
return compatibilityBootstrapPromise;
|
|
1143
|
+
}
|
|
1144
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1145
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1146
|
+
if (isReadQuery(translated.sql)) {
|
|
1147
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1148
|
+
translated.sql,
|
|
1149
|
+
...translated.args
|
|
1150
|
+
);
|
|
1151
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1152
|
+
}
|
|
1153
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1154
|
+
return buildResultSet([], rowsAffected);
|
|
1155
|
+
}
|
|
1156
|
+
function splitSqlStatements(sql) {
|
|
1157
|
+
const parts = [];
|
|
1158
|
+
let current = "";
|
|
1159
|
+
let inSingle = false;
|
|
1160
|
+
let inDouble = false;
|
|
1161
|
+
let inLineComment = false;
|
|
1162
|
+
let inBlockComment = false;
|
|
1163
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1164
|
+
const ch = sql[i];
|
|
1165
|
+
const next = sql[i + 1];
|
|
1166
|
+
if (inLineComment) {
|
|
1167
|
+
current += ch;
|
|
1168
|
+
if (ch === "\n") inLineComment = false;
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (inBlockComment) {
|
|
1172
|
+
current += ch;
|
|
1173
|
+
if (ch === "*" && next === "/") {
|
|
1174
|
+
current += next;
|
|
1175
|
+
inBlockComment = false;
|
|
1176
|
+
i += 1;
|
|
1177
|
+
}
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1181
|
+
current += ch + next;
|
|
1182
|
+
inLineComment = true;
|
|
1183
|
+
i += 1;
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1187
|
+
current += ch + next;
|
|
1188
|
+
inBlockComment = true;
|
|
1189
|
+
i += 1;
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1193
|
+
inSingle = !inSingle;
|
|
1194
|
+
current += ch;
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1198
|
+
inDouble = !inDouble;
|
|
1199
|
+
current += ch;
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1203
|
+
if (current.trim()) {
|
|
1204
|
+
parts.push(current.trim());
|
|
1205
|
+
}
|
|
1206
|
+
current = "";
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
current += ch;
|
|
1210
|
+
}
|
|
1211
|
+
if (current.trim()) {
|
|
1212
|
+
parts.push(current.trim());
|
|
1213
|
+
}
|
|
1214
|
+
return parts;
|
|
1215
|
+
}
|
|
1216
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1217
|
+
const prisma = await loadPrismaClient();
|
|
1218
|
+
await ensureCompatibilityViews(prisma);
|
|
1219
|
+
let closed = false;
|
|
1220
|
+
let adapter;
|
|
1221
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1222
|
+
if (!fallbackClient) {
|
|
1223
|
+
if (error) throw error;
|
|
1224
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1225
|
+
}
|
|
1226
|
+
if (error) {
|
|
1227
|
+
process.stderr.write(
|
|
1228
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1229
|
+
`
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
return fallbackClient.execute(stmt);
|
|
1233
|
+
};
|
|
1234
|
+
adapter = {
|
|
1235
|
+
async execute(stmt) {
|
|
1236
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1237
|
+
return fallbackExecute(stmt);
|
|
1238
|
+
}
|
|
1239
|
+
try {
|
|
1240
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
if (shouldFallbackOnError(error)) {
|
|
1243
|
+
return fallbackExecute(stmt, error);
|
|
1244
|
+
}
|
|
1245
|
+
throw error;
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
async batch(stmts, mode) {
|
|
1249
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1250
|
+
if (!fallbackClient) {
|
|
1251
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1252
|
+
}
|
|
1253
|
+
return fallbackClient.batch(stmts, mode);
|
|
1254
|
+
}
|
|
1255
|
+
try {
|
|
1256
|
+
if (prisma.$transaction) {
|
|
1257
|
+
return await prisma.$transaction(async (tx) => {
|
|
1258
|
+
const results2 = [];
|
|
1259
|
+
for (const stmt of stmts) {
|
|
1260
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1261
|
+
}
|
|
1262
|
+
return results2;
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
const results = [];
|
|
1266
|
+
for (const stmt of stmts) {
|
|
1267
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1268
|
+
}
|
|
1269
|
+
return results;
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1272
|
+
process.stderr.write(
|
|
1273
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1274
|
+
`
|
|
1275
|
+
);
|
|
1276
|
+
return fallbackClient.batch(stmts, mode);
|
|
1277
|
+
}
|
|
1278
|
+
throw error;
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
async migrate(stmts) {
|
|
1282
|
+
if (fallbackClient) {
|
|
1283
|
+
return fallbackClient.migrate(stmts);
|
|
1284
|
+
}
|
|
1285
|
+
return adapter.batch(stmts, "deferred");
|
|
1286
|
+
},
|
|
1287
|
+
async transaction(mode) {
|
|
1288
|
+
if (!fallbackClient) {
|
|
1289
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1290
|
+
}
|
|
1291
|
+
return fallbackClient.transaction(mode);
|
|
1292
|
+
},
|
|
1293
|
+
async executeMultiple(sql) {
|
|
1294
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1295
|
+
return fallbackClient.executeMultiple(sql);
|
|
1296
|
+
}
|
|
1297
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1298
|
+
await adapter.execute(statement);
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
async sync() {
|
|
1302
|
+
if (fallbackClient) {
|
|
1303
|
+
return fallbackClient.sync();
|
|
1304
|
+
}
|
|
1305
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1306
|
+
},
|
|
1307
|
+
close() {
|
|
1308
|
+
closed = true;
|
|
1309
|
+
prismaClientPromise = null;
|
|
1310
|
+
compatibilityBootstrapPromise = null;
|
|
1311
|
+
void prisma.$disconnect?.();
|
|
1312
|
+
},
|
|
1313
|
+
get closed() {
|
|
1314
|
+
return closed;
|
|
1315
|
+
},
|
|
1316
|
+
get protocol() {
|
|
1317
|
+
return "prisma-postgres";
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
return adapter;
|
|
1321
|
+
}
|
|
1322
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1323
|
+
var init_database_adapter = __esm({
|
|
1324
|
+
"src/lib/database-adapter.ts"() {
|
|
1325
|
+
"use strict";
|
|
1326
|
+
VIEW_MAPPINGS = [
|
|
1327
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1328
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1329
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1330
|
+
{ view: "entities", source: "memory.entities" },
|
|
1331
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1332
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1333
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1334
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1335
|
+
{ view: "messages", source: "memory.messages" },
|
|
1336
|
+
{ view: "users", source: "wiki.users" },
|
|
1337
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1338
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1339
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1340
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1341
|
+
];
|
|
1342
|
+
UPSERT_KEYS = {
|
|
1343
|
+
memories: ["id"],
|
|
1344
|
+
tasks: ["id"],
|
|
1345
|
+
behaviors: ["id"],
|
|
1346
|
+
entities: ["id"],
|
|
1347
|
+
relationships: ["id"],
|
|
1348
|
+
entity_aliases: ["alias"],
|
|
1349
|
+
notifications: ["id"],
|
|
1350
|
+
messages: ["id"],
|
|
1351
|
+
users: ["id"],
|
|
1352
|
+
workspaces: ["id"],
|
|
1353
|
+
workspace_users: ["id"],
|
|
1354
|
+
documents: ["id"],
|
|
1355
|
+
chats: ["id"]
|
|
1356
|
+
};
|
|
1357
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1358
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1359
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1360
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1361
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1362
|
+
};
|
|
1363
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1364
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1365
|
+
);
|
|
1366
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1367
|
+
/\bPRAGMA\b/i,
|
|
1368
|
+
/\bsqlite_master\b/i,
|
|
1369
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1370
|
+
/\bMATCH\b/i,
|
|
1371
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1372
|
+
/\bjson_extract\s*\(/i,
|
|
1373
|
+
/\bjulianday\s*\(/i,
|
|
1374
|
+
/\bstrftime\s*\(/i,
|
|
1375
|
+
/\blast_insert_rowid\s*\(/i
|
|
1376
|
+
];
|
|
1377
|
+
prismaClientPromise = null;
|
|
1378
|
+
compatibilityBootstrapPromise = null;
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
|
|
797
1382
|
// src/lib/exe-daemon-client.ts
|
|
798
1383
|
import net from "net";
|
|
799
|
-
import
|
|
1384
|
+
import os6 from "os";
|
|
800
1385
|
import { spawn } from "child_process";
|
|
801
1386
|
import { randomUUID } from "crypto";
|
|
802
1387
|
import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
803
|
-
import
|
|
1388
|
+
import path8 from "path";
|
|
804
1389
|
import { fileURLToPath } from "url";
|
|
805
1390
|
function handleData(chunk) {
|
|
806
1391
|
_buffer += chunk.toString();
|
|
@@ -851,17 +1436,17 @@ function cleanupStaleFiles() {
|
|
|
851
1436
|
}
|
|
852
1437
|
}
|
|
853
1438
|
function findPackageRoot() {
|
|
854
|
-
let dir =
|
|
855
|
-
const { root } =
|
|
1439
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
1440
|
+
const { root } = path8.parse(dir);
|
|
856
1441
|
while (dir !== root) {
|
|
857
|
-
if (existsSync6(
|
|
858
|
-
dir =
|
|
1442
|
+
if (existsSync6(path8.join(dir, "package.json"))) return dir;
|
|
1443
|
+
dir = path8.dirname(dir);
|
|
859
1444
|
}
|
|
860
1445
|
return null;
|
|
861
1446
|
}
|
|
862
1447
|
function spawnDaemon() {
|
|
863
|
-
const freeGB =
|
|
864
|
-
const totalGB =
|
|
1448
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1449
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
865
1450
|
if (totalGB <= 8) {
|
|
866
1451
|
process.stderr.write(
|
|
867
1452
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -881,7 +1466,7 @@ function spawnDaemon() {
|
|
|
881
1466
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
882
1467
|
return;
|
|
883
1468
|
}
|
|
884
|
-
const daemonPath =
|
|
1469
|
+
const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
885
1470
|
if (!existsSync6(daemonPath)) {
|
|
886
1471
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
887
1472
|
`);
|
|
@@ -890,7 +1475,7 @@ function spawnDaemon() {
|
|
|
890
1475
|
const resolvedPath = daemonPath;
|
|
891
1476
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
892
1477
|
`);
|
|
893
|
-
const logPath =
|
|
1478
|
+
const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
|
|
894
1479
|
let stderrFd = "ignore";
|
|
895
1480
|
try {
|
|
896
1481
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1037,9 +1622,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1037
1622
|
"src/lib/exe-daemon-client.ts"() {
|
|
1038
1623
|
"use strict";
|
|
1039
1624
|
init_config();
|
|
1040
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1041
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1042
|
-
SPAWN_LOCK_PATH =
|
|
1625
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
|
|
1626
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
|
|
1627
|
+
SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1043
1628
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1044
1629
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1045
1630
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1121,7 +1706,7 @@ __export(db_daemon_client_exports, {
|
|
|
1121
1706
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1122
1707
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1123
1708
|
});
|
|
1124
|
-
function
|
|
1709
|
+
function normalizeStatement2(stmt) {
|
|
1125
1710
|
if (typeof stmt === "string") {
|
|
1126
1711
|
return { sql: stmt, args: [] };
|
|
1127
1712
|
}
|
|
@@ -1145,7 +1730,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1145
1730
|
if (!_useDaemon || !isClientConnected()) {
|
|
1146
1731
|
return fallbackClient.execute(stmt);
|
|
1147
1732
|
}
|
|
1148
|
-
const { sql, args } =
|
|
1733
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1149
1734
|
const response = await sendDaemonRequest({
|
|
1150
1735
|
type: "db-execute",
|
|
1151
1736
|
sql,
|
|
@@ -1170,7 +1755,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1170
1755
|
if (!_useDaemon || !isClientConnected()) {
|
|
1171
1756
|
return fallbackClient.batch(stmts, mode);
|
|
1172
1757
|
}
|
|
1173
|
-
const statements = stmts.map(
|
|
1758
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1174
1759
|
const response = await sendDaemonRequest({
|
|
1175
1760
|
type: "db-batch",
|
|
1176
1761
|
statements,
|
|
@@ -1265,6 +1850,18 @@ __export(database_exports, {
|
|
|
1265
1850
|
});
|
|
1266
1851
|
import { createClient } from "@libsql/client";
|
|
1267
1852
|
async function initDatabase(config) {
|
|
1853
|
+
if (_walCheckpointTimer) {
|
|
1854
|
+
clearInterval(_walCheckpointTimer);
|
|
1855
|
+
_walCheckpointTimer = null;
|
|
1856
|
+
}
|
|
1857
|
+
if (_daemonClient) {
|
|
1858
|
+
_daemonClient.close();
|
|
1859
|
+
_daemonClient = null;
|
|
1860
|
+
}
|
|
1861
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1862
|
+
_adapterClient.close();
|
|
1863
|
+
}
|
|
1864
|
+
_adapterClient = null;
|
|
1268
1865
|
if (_client) {
|
|
1269
1866
|
_client.close();
|
|
1270
1867
|
_client = null;
|
|
@@ -1278,6 +1875,7 @@ async function initDatabase(config) {
|
|
|
1278
1875
|
}
|
|
1279
1876
|
_client = createClient(opts);
|
|
1280
1877
|
_resilientClient = wrapWithRetry(_client);
|
|
1878
|
+
_adapterClient = _resilientClient;
|
|
1281
1879
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1282
1880
|
});
|
|
1283
1881
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1288,14 +1886,20 @@ async function initDatabase(config) {
|
|
|
1288
1886
|
});
|
|
1289
1887
|
}, 3e4);
|
|
1290
1888
|
_walCheckpointTimer.unref();
|
|
1889
|
+
if (process.env.DATABASE_URL) {
|
|
1890
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1891
|
+
}
|
|
1291
1892
|
}
|
|
1292
1893
|
function isInitialized() {
|
|
1293
|
-
return _client !== null;
|
|
1894
|
+
return _adapterClient !== null || _client !== null;
|
|
1294
1895
|
}
|
|
1295
1896
|
function getClient() {
|
|
1296
|
-
if (!
|
|
1897
|
+
if (!_adapterClient) {
|
|
1297
1898
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1298
1899
|
}
|
|
1900
|
+
if (process.env.DATABASE_URL) {
|
|
1901
|
+
return _adapterClient;
|
|
1902
|
+
}
|
|
1299
1903
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1300
1904
|
return _resilientClient;
|
|
1301
1905
|
}
|
|
@@ -1305,6 +1909,7 @@ function getClient() {
|
|
|
1305
1909
|
return _resilientClient;
|
|
1306
1910
|
}
|
|
1307
1911
|
async function initDaemonClient() {
|
|
1912
|
+
if (process.env.DATABASE_URL) return;
|
|
1308
1913
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1309
1914
|
if (!_resilientClient) return;
|
|
1310
1915
|
try {
|
|
@@ -2249,26 +2854,36 @@ async function ensureSchema() {
|
|
|
2249
2854
|
}
|
|
2250
2855
|
}
|
|
2251
2856
|
async function disposeDatabase() {
|
|
2857
|
+
if (_walCheckpointTimer) {
|
|
2858
|
+
clearInterval(_walCheckpointTimer);
|
|
2859
|
+
_walCheckpointTimer = null;
|
|
2860
|
+
}
|
|
2252
2861
|
if (_daemonClient) {
|
|
2253
2862
|
_daemonClient.close();
|
|
2254
2863
|
_daemonClient = null;
|
|
2255
2864
|
}
|
|
2865
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2866
|
+
_adapterClient.close();
|
|
2867
|
+
}
|
|
2868
|
+
_adapterClient = null;
|
|
2256
2869
|
if (_client) {
|
|
2257
2870
|
_client.close();
|
|
2258
2871
|
_client = null;
|
|
2259
2872
|
_resilientClient = null;
|
|
2260
2873
|
}
|
|
2261
2874
|
}
|
|
2262
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2875
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2263
2876
|
var init_database = __esm({
|
|
2264
2877
|
"src/lib/database.ts"() {
|
|
2265
2878
|
"use strict";
|
|
2266
2879
|
init_db_retry();
|
|
2267
2880
|
init_employees();
|
|
2881
|
+
init_database_adapter();
|
|
2268
2882
|
_client = null;
|
|
2269
2883
|
_resilientClient = null;
|
|
2270
2884
|
_walCheckpointTimer = null;
|
|
2271
2885
|
_daemonClient = null;
|
|
2886
|
+
_adapterClient = null;
|
|
2272
2887
|
initTurso = initDatabase;
|
|
2273
2888
|
disposeTurso = disposeDatabase;
|
|
2274
2889
|
}
|
|
@@ -2277,16 +2892,16 @@ var init_database = __esm({
|
|
|
2277
2892
|
// src/lib/license.ts
|
|
2278
2893
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
2279
2894
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2280
|
-
import
|
|
2895
|
+
import path9 from "path";
|
|
2281
2896
|
import { jwtVerify, importSPKI } from "jose";
|
|
2282
2897
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2283
2898
|
var init_license = __esm({
|
|
2284
2899
|
"src/lib/license.ts"() {
|
|
2285
2900
|
"use strict";
|
|
2286
2901
|
init_config();
|
|
2287
|
-
LICENSE_PATH =
|
|
2288
|
-
CACHE_PATH =
|
|
2289
|
-
DEVICE_ID_PATH =
|
|
2902
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
2903
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2904
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2290
2905
|
PLAN_LIMITS = {
|
|
2291
2906
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2292
2907
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2299,7 +2914,7 @@ var init_license = __esm({
|
|
|
2299
2914
|
|
|
2300
2915
|
// src/lib/plan-limits.ts
|
|
2301
2916
|
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
2302
|
-
import
|
|
2917
|
+
import path10 from "path";
|
|
2303
2918
|
function getLicenseSync() {
|
|
2304
2919
|
try {
|
|
2305
2920
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
@@ -2371,7 +2986,7 @@ var init_plan_limits = __esm({
|
|
|
2371
2986
|
this.name = "PlanLimitError";
|
|
2372
2987
|
}
|
|
2373
2988
|
};
|
|
2374
|
-
CACHE_PATH2 =
|
|
2989
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2375
2990
|
}
|
|
2376
2991
|
});
|
|
2377
2992
|
|
|
@@ -2388,8 +3003,8 @@ __export(notifications_exports, {
|
|
|
2388
3003
|
writeNotification: () => writeNotification
|
|
2389
3004
|
});
|
|
2390
3005
|
import crypto from "crypto";
|
|
2391
|
-
import
|
|
2392
|
-
import
|
|
3006
|
+
import path11 from "path";
|
|
3007
|
+
import os7 from "os";
|
|
2393
3008
|
import {
|
|
2394
3009
|
readFileSync as readFileSync10,
|
|
2395
3010
|
readdirSync as readdirSync2,
|
|
@@ -2530,8 +3145,8 @@ function formatNotifications(notifications) {
|
|
|
2530
3145
|
return lines.join("\n");
|
|
2531
3146
|
}
|
|
2532
3147
|
async function migrateJsonNotifications() {
|
|
2533
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
2534
|
-
const notifDir =
|
|
3148
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path11.join(os7.homedir(), ".exe-os");
|
|
3149
|
+
const notifDir = path11.join(base, "notifications");
|
|
2535
3150
|
if (!existsSync9(notifDir)) return 0;
|
|
2536
3151
|
let migrated = 0;
|
|
2537
3152
|
try {
|
|
@@ -2540,7 +3155,7 @@ async function migrateJsonNotifications() {
|
|
|
2540
3155
|
const client = getClient();
|
|
2541
3156
|
for (const file of files) {
|
|
2542
3157
|
try {
|
|
2543
|
-
const filePath =
|
|
3158
|
+
const filePath = path11.join(notifDir, file);
|
|
2544
3159
|
const data = JSON.parse(readFileSync10(filePath, "utf8"));
|
|
2545
3160
|
await client.execute({
|
|
2546
3161
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
@@ -2717,8 +3332,8 @@ __export(tasks_crud_exports, {
|
|
|
2717
3332
|
writeCheckpoint: () => writeCheckpoint
|
|
2718
3333
|
});
|
|
2719
3334
|
import crypto3 from "crypto";
|
|
2720
|
-
import
|
|
2721
|
-
import
|
|
3335
|
+
import path12 from "path";
|
|
3336
|
+
import os8 from "os";
|
|
2722
3337
|
import { execSync as execSync5 } from "child_process";
|
|
2723
3338
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2724
3339
|
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
@@ -2896,8 +3511,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2896
3511
|
}
|
|
2897
3512
|
if (input2.baseDir) {
|
|
2898
3513
|
try {
|
|
2899
|
-
await mkdir3(
|
|
2900
|
-
await mkdir3(
|
|
3514
|
+
await mkdir3(path12.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
3515
|
+
await mkdir3(path12.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
2901
3516
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
2902
3517
|
await ensureGitignoreExe(input2.baseDir);
|
|
2903
3518
|
} catch {
|
|
@@ -2933,9 +3548,9 @@ ${laneWarning}` : laneWarning;
|
|
|
2933
3548
|
});
|
|
2934
3549
|
if (input2.baseDir) {
|
|
2935
3550
|
try {
|
|
2936
|
-
const EXE_OS_DIR =
|
|
2937
|
-
const mdPath =
|
|
2938
|
-
const mdDir =
|
|
3551
|
+
const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
|
|
3552
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
3553
|
+
const mdDir = path12.dirname(mdPath);
|
|
2939
3554
|
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2940
3555
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
2941
3556
|
const mdContent = `# ${input2.title}
|
|
@@ -3236,7 +3851,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3236
3851
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3237
3852
|
}
|
|
3238
3853
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3239
|
-
const archPath =
|
|
3854
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3240
3855
|
try {
|
|
3241
3856
|
if (existsSync10(archPath)) return;
|
|
3242
3857
|
const template = [
|
|
@@ -3271,7 +3886,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3271
3886
|
}
|
|
3272
3887
|
}
|
|
3273
3888
|
async function ensureGitignoreExe(baseDir) {
|
|
3274
|
-
const gitignorePath =
|
|
3889
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
3275
3890
|
try {
|
|
3276
3891
|
if (existsSync10(gitignorePath)) {
|
|
3277
3892
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -3305,13 +3920,13 @@ var init_tasks_crud = __esm({
|
|
|
3305
3920
|
});
|
|
3306
3921
|
|
|
3307
3922
|
// src/lib/tasks-review.ts
|
|
3308
|
-
import
|
|
3923
|
+
import path13 from "path";
|
|
3309
3924
|
import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
3310
3925
|
async function countPendingReviews(sessionScope) {
|
|
3311
3926
|
const client = getClient();
|
|
3312
3927
|
if (sessionScope) {
|
|
3313
3928
|
const result2 = await client.execute({
|
|
3314
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
3929
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3315
3930
|
args: [sessionScope]
|
|
3316
3931
|
});
|
|
3317
3932
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3487,11 +4102,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3487
4102
|
);
|
|
3488
4103
|
}
|
|
3489
4104
|
try {
|
|
3490
|
-
const cacheDir =
|
|
4105
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
3491
4106
|
if (existsSync11(cacheDir)) {
|
|
3492
4107
|
for (const f of readdirSync3(cacheDir)) {
|
|
3493
4108
|
if (f.startsWith("review-notified-")) {
|
|
3494
|
-
unlinkSync5(
|
|
4109
|
+
unlinkSync5(path13.join(cacheDir, f));
|
|
3495
4110
|
}
|
|
3496
4111
|
}
|
|
3497
4112
|
}
|
|
@@ -3512,7 +4127,7 @@ var init_tasks_review = __esm({
|
|
|
3512
4127
|
});
|
|
3513
4128
|
|
|
3514
4129
|
// src/lib/tasks-chain.ts
|
|
3515
|
-
import
|
|
4130
|
+
import path14 from "path";
|
|
3516
4131
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3517
4132
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3518
4133
|
const client = getClient();
|
|
@@ -3529,7 +4144,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3529
4144
|
});
|
|
3530
4145
|
for (const ur of unblockedRows.rows) {
|
|
3531
4146
|
try {
|
|
3532
|
-
const ubFile =
|
|
4147
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
3533
4148
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3534
4149
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3535
4150
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3598,7 +4213,7 @@ var init_tasks_chain = __esm({
|
|
|
3598
4213
|
|
|
3599
4214
|
// src/lib/project-name.ts
|
|
3600
4215
|
import { execSync as execSync6 } from "child_process";
|
|
3601
|
-
import
|
|
4216
|
+
import path15 from "path";
|
|
3602
4217
|
function getProjectName(cwd) {
|
|
3603
4218
|
const dir = cwd ?? process.cwd();
|
|
3604
4219
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3611,7 +4226,7 @@ function getProjectName(cwd) {
|
|
|
3611
4226
|
timeout: 2e3,
|
|
3612
4227
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3613
4228
|
}).trim();
|
|
3614
|
-
repoRoot =
|
|
4229
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
3615
4230
|
} catch {
|
|
3616
4231
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3617
4232
|
cwd: dir,
|
|
@@ -3620,11 +4235,11 @@ function getProjectName(cwd) {
|
|
|
3620
4235
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3621
4236
|
}).trim();
|
|
3622
4237
|
}
|
|
3623
|
-
_cached2 =
|
|
4238
|
+
_cached2 = path15.basename(repoRoot);
|
|
3624
4239
|
_cachedCwd = dir;
|
|
3625
4240
|
return _cached2;
|
|
3626
4241
|
} catch {
|
|
3627
|
-
_cached2 =
|
|
4242
|
+
_cached2 = path15.basename(dir);
|
|
3628
4243
|
_cachedCwd = dir;
|
|
3629
4244
|
return _cached2;
|
|
3630
4245
|
}
|
|
@@ -4097,7 +4712,7 @@ __export(tasks_exports, {
|
|
|
4097
4712
|
updateTaskStatus: () => updateTaskStatus,
|
|
4098
4713
|
writeCheckpoint: () => writeCheckpoint
|
|
4099
4714
|
});
|
|
4100
|
-
import
|
|
4715
|
+
import path16 from "path";
|
|
4101
4716
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
4102
4717
|
async function createTask(input2) {
|
|
4103
4718
|
const result = await createTaskCore(input2);
|
|
@@ -4117,8 +4732,8 @@ async function updateTask(input2) {
|
|
|
4117
4732
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
4118
4733
|
try {
|
|
4119
4734
|
const agent = String(row.assigned_to);
|
|
4120
|
-
const cacheDir =
|
|
4121
|
-
const cachePath =
|
|
4735
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
4736
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
4122
4737
|
if (input2.status === "in_progress") {
|
|
4123
4738
|
mkdirSync6(cacheDir, { recursive: true });
|
|
4124
4739
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -4589,12 +5204,12 @@ __export(tmux_routing_exports, {
|
|
|
4589
5204
|
});
|
|
4590
5205
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4591
5206
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
4592
|
-
import
|
|
4593
|
-
import
|
|
5207
|
+
import path17 from "path";
|
|
5208
|
+
import os9 from "os";
|
|
4594
5209
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4595
5210
|
import { unlinkSync as unlinkSync7 } from "fs";
|
|
4596
5211
|
function spawnLockPath(sessionName) {
|
|
4597
|
-
return
|
|
5212
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4598
5213
|
}
|
|
4599
5214
|
function isProcessAlive(pid) {
|
|
4600
5215
|
try {
|
|
@@ -4631,8 +5246,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4631
5246
|
function resolveBehaviorsExporterScript() {
|
|
4632
5247
|
try {
|
|
4633
5248
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4634
|
-
const scriptPath =
|
|
4635
|
-
|
|
5249
|
+
const scriptPath = path17.join(
|
|
5250
|
+
path17.dirname(thisFile),
|
|
4636
5251
|
"..",
|
|
4637
5252
|
"bin",
|
|
4638
5253
|
"exe-export-behaviors.js"
|
|
@@ -4707,7 +5322,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4707
5322
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4708
5323
|
}
|
|
4709
5324
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4710
|
-
const filePath =
|
|
5325
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4711
5326
|
writeFileSync8(filePath, JSON.stringify({
|
|
4712
5327
|
parentExe: rootExe,
|
|
4713
5328
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -4716,7 +5331,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4716
5331
|
}
|
|
4717
5332
|
function getParentExe(sessionKey) {
|
|
4718
5333
|
try {
|
|
4719
|
-
const data = JSON.parse(readFileSync12(
|
|
5334
|
+
const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4720
5335
|
return data.parentExe || null;
|
|
4721
5336
|
} catch {
|
|
4722
5337
|
return null;
|
|
@@ -4725,7 +5340,7 @@ function getParentExe(sessionKey) {
|
|
|
4725
5340
|
function getDispatchedBy(sessionKey) {
|
|
4726
5341
|
try {
|
|
4727
5342
|
const data = JSON.parse(readFileSync12(
|
|
4728
|
-
|
|
5343
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4729
5344
|
"utf8"
|
|
4730
5345
|
));
|
|
4731
5346
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4911,7 +5526,7 @@ function sendIntercom(targetSession) {
|
|
|
4911
5526
|
try {
|
|
4912
5527
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4913
5528
|
const agent = baseAgentName(rawAgent);
|
|
4914
|
-
const markerPath =
|
|
5529
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4915
5530
|
if (existsSync12(markerPath)) {
|
|
4916
5531
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4917
5532
|
return "debounced";
|
|
@@ -4921,7 +5536,7 @@ function sendIntercom(targetSession) {
|
|
|
4921
5536
|
try {
|
|
4922
5537
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4923
5538
|
const agent = baseAgentName(rawAgent);
|
|
4924
|
-
const taskDir =
|
|
5539
|
+
const taskDir = path17.join(process.cwd(), "exe", agent);
|
|
4925
5540
|
if (existsSync12(taskDir)) {
|
|
4926
5541
|
const files = readdirSync4(taskDir).filter(
|
|
4927
5542
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -5055,8 +5670,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5055
5670
|
const transport = getTransport();
|
|
5056
5671
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5057
5672
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5058
|
-
const logDir =
|
|
5059
|
-
const logFile =
|
|
5673
|
+
const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5674
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5060
5675
|
if (!existsSync12(logDir)) {
|
|
5061
5676
|
mkdirSync7(logDir, { recursive: true });
|
|
5062
5677
|
}
|
|
@@ -5064,14 +5679,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5064
5679
|
let cleanupSuffix = "";
|
|
5065
5680
|
try {
|
|
5066
5681
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5067
|
-
const cleanupScript =
|
|
5682
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5068
5683
|
if (existsSync12(cleanupScript)) {
|
|
5069
5684
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5070
5685
|
}
|
|
5071
5686
|
} catch {
|
|
5072
5687
|
}
|
|
5073
5688
|
try {
|
|
5074
|
-
const claudeJsonPath =
|
|
5689
|
+
const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
|
|
5075
5690
|
let claudeJson = {};
|
|
5076
5691
|
try {
|
|
5077
5692
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -5086,10 +5701,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5086
5701
|
} catch {
|
|
5087
5702
|
}
|
|
5088
5703
|
try {
|
|
5089
|
-
const settingsDir =
|
|
5704
|
+
const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
|
|
5090
5705
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5091
|
-
const projSettingsDir =
|
|
5092
|
-
const settingsPath =
|
|
5706
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
5707
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
5093
5708
|
let settings = {};
|
|
5094
5709
|
try {
|
|
5095
5710
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -5136,8 +5751,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5136
5751
|
let behaviorsFlag = "";
|
|
5137
5752
|
let legacyFallbackWarned = false;
|
|
5138
5753
|
if (!useExeAgent && !useBinSymlink) {
|
|
5139
|
-
const identityPath =
|
|
5140
|
-
|
|
5754
|
+
const identityPath = path17.join(
|
|
5755
|
+
os9.homedir(),
|
|
5141
5756
|
".exe-os",
|
|
5142
5757
|
"identity",
|
|
5143
5758
|
`${employeeName}.md`
|
|
@@ -5152,7 +5767,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5152
5767
|
}
|
|
5153
5768
|
const behaviorsFile = exportBehaviorsSync(
|
|
5154
5769
|
employeeName,
|
|
5155
|
-
|
|
5770
|
+
path17.basename(spawnCwd),
|
|
5156
5771
|
sessionName
|
|
5157
5772
|
);
|
|
5158
5773
|
if (behaviorsFile) {
|
|
@@ -5167,9 +5782,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5167
5782
|
}
|
|
5168
5783
|
let sessionContextFlag = "";
|
|
5169
5784
|
try {
|
|
5170
|
-
const ctxDir =
|
|
5785
|
+
const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5171
5786
|
mkdirSync7(ctxDir, { recursive: true });
|
|
5172
|
-
const ctxFile =
|
|
5787
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5173
5788
|
const ctxContent = [
|
|
5174
5789
|
`## Session Context`,
|
|
5175
5790
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5253,7 +5868,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5253
5868
|
transport.pipeLog(sessionName, logFile);
|
|
5254
5869
|
try {
|
|
5255
5870
|
const mySession = getMySession();
|
|
5256
|
-
const dispatchInfo =
|
|
5871
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5257
5872
|
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
5258
5873
|
dispatchedBy: mySession,
|
|
5259
5874
|
rootExe: exeSession,
|
|
@@ -5328,15 +5943,15 @@ var init_tmux_routing = __esm({
|
|
|
5328
5943
|
init_intercom_queue();
|
|
5329
5944
|
init_plan_limits();
|
|
5330
5945
|
init_employees();
|
|
5331
|
-
SPAWN_LOCK_DIR =
|
|
5332
|
-
SESSION_CACHE =
|
|
5946
|
+
SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5947
|
+
SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5333
5948
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5334
5949
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5335
5950
|
VERIFY_PANE_LINES = 200;
|
|
5336
5951
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5337
5952
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5338
|
-
INTERCOM_LOG2 =
|
|
5339
|
-
DEBOUNCE_FILE =
|
|
5953
|
+
INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5954
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5340
5955
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5341
5956
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5342
5957
|
}
|
|
@@ -5378,13 +5993,13 @@ var init_memory = __esm({
|
|
|
5378
5993
|
// src/lib/keychain.ts
|
|
5379
5994
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5380
5995
|
import { existsSync as existsSync13 } from "fs";
|
|
5381
|
-
import
|
|
5382
|
-
import
|
|
5996
|
+
import path18 from "path";
|
|
5997
|
+
import os10 from "os";
|
|
5383
5998
|
function getKeyDir() {
|
|
5384
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5999
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os10.homedir(), ".exe-os");
|
|
5385
6000
|
}
|
|
5386
6001
|
function getKeyPath() {
|
|
5387
|
-
return
|
|
6002
|
+
return path18.join(getKeyDir(), "master.key");
|
|
5388
6003
|
}
|
|
5389
6004
|
async function tryKeytar() {
|
|
5390
6005
|
try {
|
|
@@ -5407,7 +6022,7 @@ async function getMasterKey() {
|
|
|
5407
6022
|
const keyPath = getKeyPath();
|
|
5408
6023
|
if (!existsSync13(keyPath)) {
|
|
5409
6024
|
process.stderr.write(
|
|
5410
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
6025
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5411
6026
|
`
|
|
5412
6027
|
);
|
|
5413
6028
|
return null;
|
|
@@ -5445,7 +6060,7 @@ __export(shard_manager_exports, {
|
|
|
5445
6060
|
listShards: () => listShards,
|
|
5446
6061
|
shardExists: () => shardExists
|
|
5447
6062
|
});
|
|
5448
|
-
import
|
|
6063
|
+
import path19 from "path";
|
|
5449
6064
|
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
|
|
5450
6065
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5451
6066
|
function initShardManager(encryptionKey) {
|
|
@@ -5471,7 +6086,7 @@ function getShardClient(projectName) {
|
|
|
5471
6086
|
}
|
|
5472
6087
|
const cached = _shards.get(safeName);
|
|
5473
6088
|
if (cached) return cached;
|
|
5474
|
-
const dbPath =
|
|
6089
|
+
const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
|
|
5475
6090
|
const client = createClient2({
|
|
5476
6091
|
url: `file:${dbPath}`,
|
|
5477
6092
|
encryptionKey: _encryptionKey
|
|
@@ -5481,7 +6096,7 @@ function getShardClient(projectName) {
|
|
|
5481
6096
|
}
|
|
5482
6097
|
function shardExists(projectName) {
|
|
5483
6098
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5484
|
-
return existsSync14(
|
|
6099
|
+
return existsSync14(path19.join(SHARDS_DIR, `${safeName}.db`));
|
|
5485
6100
|
}
|
|
5486
6101
|
function listShards() {
|
|
5487
6102
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
@@ -5558,7 +6173,23 @@ async function ensureShardSchema(client) {
|
|
|
5558
6173
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
5559
6174
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
5560
6175
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
5561
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
6176
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
6177
|
+
// Metadata enrichment columns (must match database.ts)
|
|
6178
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
6179
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
6180
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
6181
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
6182
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
6183
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
6184
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
6185
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
6186
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
6187
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
6188
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
6189
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
6190
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
6191
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
6192
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
5562
6193
|
]) {
|
|
5563
6194
|
try {
|
|
5564
6195
|
await client.execute(col);
|
|
@@ -5670,7 +6301,7 @@ var init_shard_manager = __esm({
|
|
|
5670
6301
|
"src/lib/shard-manager.ts"() {
|
|
5671
6302
|
"use strict";
|
|
5672
6303
|
init_config();
|
|
5673
|
-
SHARDS_DIR =
|
|
6304
|
+
SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
|
|
5674
6305
|
_shards = /* @__PURE__ */ new Map();
|
|
5675
6306
|
_encryptionKey = null;
|
|
5676
6307
|
_shardingEnabled = false;
|