@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/bin/scan-tasks.js
CHANGED
|
@@ -792,7 +792,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
792
792
|
if (!emp) return false;
|
|
793
793
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
794
794
|
}
|
|
795
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
795
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
796
796
|
var init_employees = __esm({
|
|
797
797
|
"src/lib/employees.ts"() {
|
|
798
798
|
"use strict";
|
|
@@ -801,16 +801,601 @@ var init_employees = __esm({
|
|
|
801
801
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
802
802
|
COORDINATOR_ROLE = "COO";
|
|
803
803
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
804
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// src/lib/database-adapter.ts
|
|
809
|
+
import os5 from "os";
|
|
810
|
+
import path6 from "path";
|
|
811
|
+
import { createRequire } from "module";
|
|
812
|
+
import { pathToFileURL } from "url";
|
|
813
|
+
function quotedIdentifier(identifier) {
|
|
814
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
815
|
+
}
|
|
816
|
+
function unqualifiedTableName(name) {
|
|
817
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
818
|
+
const parts = raw.split(".");
|
|
819
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
820
|
+
}
|
|
821
|
+
function stripTrailingSemicolon(sql) {
|
|
822
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
823
|
+
}
|
|
824
|
+
function appendClause(sql, clause) {
|
|
825
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
826
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
827
|
+
if (!returningMatch) {
|
|
828
|
+
return `${trimmed}${clause}`;
|
|
829
|
+
}
|
|
830
|
+
const idx = returningMatch.index;
|
|
831
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
832
|
+
}
|
|
833
|
+
function normalizeStatement(stmt) {
|
|
834
|
+
if (typeof stmt === "string") {
|
|
835
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
836
|
+
}
|
|
837
|
+
const sql = stmt.sql;
|
|
838
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
839
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
840
|
+
}
|
|
841
|
+
return { kind: "named", sql, args: stmt.args };
|
|
842
|
+
}
|
|
843
|
+
function rewriteBooleanLiterals(sql) {
|
|
844
|
+
let out = sql;
|
|
845
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
846
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
847
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
848
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
849
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
850
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
851
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
852
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
853
|
+
}
|
|
854
|
+
return out;
|
|
855
|
+
}
|
|
856
|
+
function rewriteInsertOrIgnore(sql) {
|
|
857
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
858
|
+
return sql;
|
|
859
|
+
}
|
|
860
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
861
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
862
|
+
}
|
|
863
|
+
function rewriteInsertOrReplace(sql) {
|
|
864
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
865
|
+
if (!match) {
|
|
866
|
+
return sql;
|
|
867
|
+
}
|
|
868
|
+
const rawTable = match[1];
|
|
869
|
+
const rawColumns = match[2];
|
|
870
|
+
const remainder = match[3];
|
|
871
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
872
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
873
|
+
if (!conflictKeys?.length) {
|
|
874
|
+
return sql;
|
|
875
|
+
}
|
|
876
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
877
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
878
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
879
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
880
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
881
|
+
}
|
|
882
|
+
function rewriteSql(sql) {
|
|
883
|
+
let out = sql;
|
|
884
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
885
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
886
|
+
out = rewriteBooleanLiterals(out);
|
|
887
|
+
out = rewriteInsertOrReplace(out);
|
|
888
|
+
out = rewriteInsertOrIgnore(out);
|
|
889
|
+
return stripTrailingSemicolon(out);
|
|
890
|
+
}
|
|
891
|
+
function toBoolean(value) {
|
|
892
|
+
if (value === null || value === void 0) return value;
|
|
893
|
+
if (typeof value === "boolean") return value;
|
|
894
|
+
if (typeof value === "number") return value !== 0;
|
|
895
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
896
|
+
if (typeof value === "string") {
|
|
897
|
+
const normalized = value.trim().toLowerCase();
|
|
898
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
899
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
900
|
+
}
|
|
901
|
+
return Boolean(value);
|
|
902
|
+
}
|
|
903
|
+
function countQuestionMarks(sql, end) {
|
|
904
|
+
let count = 0;
|
|
905
|
+
let inSingle = false;
|
|
906
|
+
let inDouble = false;
|
|
907
|
+
let inLineComment = false;
|
|
908
|
+
let inBlockComment = false;
|
|
909
|
+
for (let i = 0; i < end; i++) {
|
|
910
|
+
const ch = sql[i];
|
|
911
|
+
const next = sql[i + 1];
|
|
912
|
+
if (inLineComment) {
|
|
913
|
+
if (ch === "\n") inLineComment = false;
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (inBlockComment) {
|
|
917
|
+
if (ch === "*" && next === "/") {
|
|
918
|
+
inBlockComment = false;
|
|
919
|
+
i += 1;
|
|
920
|
+
}
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
924
|
+
inLineComment = true;
|
|
925
|
+
i += 1;
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
929
|
+
inBlockComment = true;
|
|
930
|
+
i += 1;
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
934
|
+
inSingle = !inSingle;
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
938
|
+
inDouble = !inDouble;
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
942
|
+
count += 1;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return count;
|
|
946
|
+
}
|
|
947
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
948
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
949
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
950
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
951
|
+
for (const match of sql.matchAll(pattern)) {
|
|
952
|
+
const matchText = match[0];
|
|
953
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
954
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return indexes;
|
|
958
|
+
}
|
|
959
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
960
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
961
|
+
if (!match) return;
|
|
962
|
+
const rawTable = match[1];
|
|
963
|
+
const rawColumns = match[2];
|
|
964
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
965
|
+
if (!boolColumns?.size) return;
|
|
966
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
967
|
+
for (const [index, column] of columns.entries()) {
|
|
968
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
969
|
+
args[index] = toBoolean(args[index]);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
974
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
975
|
+
if (!match) return;
|
|
976
|
+
const rawTable = match[1];
|
|
977
|
+
const setClause = match[2];
|
|
978
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
979
|
+
if (!boolColumns?.size) return;
|
|
980
|
+
const assignments = setClause.split(",");
|
|
981
|
+
let placeholderIndex = 0;
|
|
982
|
+
for (const assignment of assignments) {
|
|
983
|
+
if (!assignment.includes("?")) continue;
|
|
984
|
+
placeholderIndex += 1;
|
|
985
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
986
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
987
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
function coerceBooleanArgs(sql, args) {
|
|
992
|
+
const nextArgs = [...args];
|
|
993
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
994
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
995
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
996
|
+
for (const index of placeholderIndexes) {
|
|
997
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
998
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return nextArgs;
|
|
1002
|
+
}
|
|
1003
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1004
|
+
let out = "";
|
|
1005
|
+
let placeholder = 0;
|
|
1006
|
+
let inSingle = false;
|
|
1007
|
+
let inDouble = false;
|
|
1008
|
+
let inLineComment = false;
|
|
1009
|
+
let inBlockComment = false;
|
|
1010
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1011
|
+
const ch = sql[i];
|
|
1012
|
+
const next = sql[i + 1];
|
|
1013
|
+
if (inLineComment) {
|
|
1014
|
+
out += ch;
|
|
1015
|
+
if (ch === "\n") inLineComment = false;
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
if (inBlockComment) {
|
|
1019
|
+
out += ch;
|
|
1020
|
+
if (ch === "*" && next === "/") {
|
|
1021
|
+
out += next;
|
|
1022
|
+
inBlockComment = false;
|
|
1023
|
+
i += 1;
|
|
1024
|
+
}
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1028
|
+
out += ch + next;
|
|
1029
|
+
inLineComment = true;
|
|
1030
|
+
i += 1;
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1034
|
+
out += ch + next;
|
|
1035
|
+
inBlockComment = true;
|
|
1036
|
+
i += 1;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1040
|
+
inSingle = !inSingle;
|
|
1041
|
+
out += ch;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1045
|
+
inDouble = !inDouble;
|
|
1046
|
+
out += ch;
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1050
|
+
placeholder += 1;
|
|
1051
|
+
out += `$${placeholder}`;
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
out += ch;
|
|
1055
|
+
}
|
|
1056
|
+
return out;
|
|
1057
|
+
}
|
|
1058
|
+
function translateStatementForPostgres(stmt) {
|
|
1059
|
+
const normalized = normalizeStatement(stmt);
|
|
1060
|
+
if (normalized.kind === "named") {
|
|
1061
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1062
|
+
}
|
|
1063
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1064
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1065
|
+
return {
|
|
1066
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1067
|
+
args: coercedArgs
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function shouldBypassPostgres(stmt) {
|
|
1071
|
+
const normalized = normalizeStatement(stmt);
|
|
1072
|
+
if (normalized.kind === "named") {
|
|
1073
|
+
return true;
|
|
1074
|
+
}
|
|
1075
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1076
|
+
}
|
|
1077
|
+
function shouldFallbackOnError(error) {
|
|
1078
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1079
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1080
|
+
}
|
|
1081
|
+
function isReadQuery(sql) {
|
|
1082
|
+
const trimmed = sql.trimStart();
|
|
1083
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1084
|
+
}
|
|
1085
|
+
function buildRow(row, columns) {
|
|
1086
|
+
const values = columns.map((column) => row[column]);
|
|
1087
|
+
return Object.assign(values, row);
|
|
1088
|
+
}
|
|
1089
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1090
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1091
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1092
|
+
return {
|
|
1093
|
+
columns,
|
|
1094
|
+
columnTypes: columns.map(() => ""),
|
|
1095
|
+
rows: resultRows,
|
|
1096
|
+
rowsAffected,
|
|
1097
|
+
lastInsertRowid: void 0,
|
|
1098
|
+
toJSON() {
|
|
1099
|
+
return {
|
|
1100
|
+
columns,
|
|
1101
|
+
columnTypes: columns.map(() => ""),
|
|
1102
|
+
rows,
|
|
1103
|
+
rowsAffected,
|
|
1104
|
+
lastInsertRowid: void 0
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
async function loadPrismaClient() {
|
|
1110
|
+
if (!prismaClientPromise) {
|
|
1111
|
+
prismaClientPromise = (async () => {
|
|
1112
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1113
|
+
if (explicitPath) {
|
|
1114
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1115
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1116
|
+
if (!PrismaClient2) {
|
|
1117
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1118
|
+
}
|
|
1119
|
+
return new PrismaClient2();
|
|
1120
|
+
}
|
|
1121
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
|
|
1122
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1123
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1124
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1125
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1126
|
+
if (!PrismaClient) {
|
|
1127
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1128
|
+
}
|
|
1129
|
+
return new PrismaClient();
|
|
1130
|
+
})();
|
|
1131
|
+
}
|
|
1132
|
+
return prismaClientPromise;
|
|
1133
|
+
}
|
|
1134
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1135
|
+
if (!compatibilityBootstrapPromise) {
|
|
1136
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1137
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1138
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1139
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1140
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1141
|
+
relation
|
|
1142
|
+
);
|
|
1143
|
+
if (!rows[0]?.regclass) {
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
await prisma.$executeRawUnsafe(
|
|
1147
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
})();
|
|
1151
|
+
}
|
|
1152
|
+
return compatibilityBootstrapPromise;
|
|
1153
|
+
}
|
|
1154
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1155
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1156
|
+
if (isReadQuery(translated.sql)) {
|
|
1157
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1158
|
+
translated.sql,
|
|
1159
|
+
...translated.args
|
|
1160
|
+
);
|
|
1161
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1162
|
+
}
|
|
1163
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1164
|
+
return buildResultSet([], rowsAffected);
|
|
1165
|
+
}
|
|
1166
|
+
function splitSqlStatements(sql) {
|
|
1167
|
+
const parts = [];
|
|
1168
|
+
let current = "";
|
|
1169
|
+
let inSingle = false;
|
|
1170
|
+
let inDouble = false;
|
|
1171
|
+
let inLineComment = false;
|
|
1172
|
+
let inBlockComment = false;
|
|
1173
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1174
|
+
const ch = sql[i];
|
|
1175
|
+
const next = sql[i + 1];
|
|
1176
|
+
if (inLineComment) {
|
|
1177
|
+
current += ch;
|
|
1178
|
+
if (ch === "\n") inLineComment = false;
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
if (inBlockComment) {
|
|
1182
|
+
current += ch;
|
|
1183
|
+
if (ch === "*" && next === "/") {
|
|
1184
|
+
current += next;
|
|
1185
|
+
inBlockComment = false;
|
|
1186
|
+
i += 1;
|
|
1187
|
+
}
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1191
|
+
current += ch + next;
|
|
1192
|
+
inLineComment = true;
|
|
1193
|
+
i += 1;
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1197
|
+
current += ch + next;
|
|
1198
|
+
inBlockComment = true;
|
|
1199
|
+
i += 1;
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1203
|
+
inSingle = !inSingle;
|
|
1204
|
+
current += ch;
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1208
|
+
inDouble = !inDouble;
|
|
1209
|
+
current += ch;
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1213
|
+
if (current.trim()) {
|
|
1214
|
+
parts.push(current.trim());
|
|
1215
|
+
}
|
|
1216
|
+
current = "";
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
current += ch;
|
|
1220
|
+
}
|
|
1221
|
+
if (current.trim()) {
|
|
1222
|
+
parts.push(current.trim());
|
|
1223
|
+
}
|
|
1224
|
+
return parts;
|
|
1225
|
+
}
|
|
1226
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1227
|
+
const prisma = await loadPrismaClient();
|
|
1228
|
+
await ensureCompatibilityViews(prisma);
|
|
1229
|
+
let closed = false;
|
|
1230
|
+
let adapter;
|
|
1231
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1232
|
+
if (!fallbackClient) {
|
|
1233
|
+
if (error) throw error;
|
|
1234
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1235
|
+
}
|
|
1236
|
+
if (error) {
|
|
1237
|
+
process.stderr.write(
|
|
1238
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1239
|
+
`
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
return fallbackClient.execute(stmt);
|
|
1243
|
+
};
|
|
1244
|
+
adapter = {
|
|
1245
|
+
async execute(stmt) {
|
|
1246
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1247
|
+
return fallbackExecute(stmt);
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
if (shouldFallbackOnError(error)) {
|
|
1253
|
+
return fallbackExecute(stmt, error);
|
|
1254
|
+
}
|
|
1255
|
+
throw error;
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
async batch(stmts, mode) {
|
|
1259
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1260
|
+
if (!fallbackClient) {
|
|
1261
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1262
|
+
}
|
|
1263
|
+
return fallbackClient.batch(stmts, mode);
|
|
1264
|
+
}
|
|
1265
|
+
try {
|
|
1266
|
+
if (prisma.$transaction) {
|
|
1267
|
+
return await prisma.$transaction(async (tx) => {
|
|
1268
|
+
const results2 = [];
|
|
1269
|
+
for (const stmt of stmts) {
|
|
1270
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1271
|
+
}
|
|
1272
|
+
return results2;
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
const results = [];
|
|
1276
|
+
for (const stmt of stmts) {
|
|
1277
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1278
|
+
}
|
|
1279
|
+
return results;
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1282
|
+
process.stderr.write(
|
|
1283
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1284
|
+
`
|
|
1285
|
+
);
|
|
1286
|
+
return fallbackClient.batch(stmts, mode);
|
|
1287
|
+
}
|
|
1288
|
+
throw error;
|
|
1289
|
+
}
|
|
1290
|
+
},
|
|
1291
|
+
async migrate(stmts) {
|
|
1292
|
+
if (fallbackClient) {
|
|
1293
|
+
return fallbackClient.migrate(stmts);
|
|
1294
|
+
}
|
|
1295
|
+
return adapter.batch(stmts, "deferred");
|
|
1296
|
+
},
|
|
1297
|
+
async transaction(mode) {
|
|
1298
|
+
if (!fallbackClient) {
|
|
1299
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1300
|
+
}
|
|
1301
|
+
return fallbackClient.transaction(mode);
|
|
1302
|
+
},
|
|
1303
|
+
async executeMultiple(sql) {
|
|
1304
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1305
|
+
return fallbackClient.executeMultiple(sql);
|
|
1306
|
+
}
|
|
1307
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1308
|
+
await adapter.execute(statement);
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
async sync() {
|
|
1312
|
+
if (fallbackClient) {
|
|
1313
|
+
return fallbackClient.sync();
|
|
1314
|
+
}
|
|
1315
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1316
|
+
},
|
|
1317
|
+
close() {
|
|
1318
|
+
closed = true;
|
|
1319
|
+
prismaClientPromise = null;
|
|
1320
|
+
compatibilityBootstrapPromise = null;
|
|
1321
|
+
void prisma.$disconnect?.();
|
|
1322
|
+
},
|
|
1323
|
+
get closed() {
|
|
1324
|
+
return closed;
|
|
1325
|
+
},
|
|
1326
|
+
get protocol() {
|
|
1327
|
+
return "prisma-postgres";
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
return adapter;
|
|
1331
|
+
}
|
|
1332
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1333
|
+
var init_database_adapter = __esm({
|
|
1334
|
+
"src/lib/database-adapter.ts"() {
|
|
1335
|
+
"use strict";
|
|
1336
|
+
VIEW_MAPPINGS = [
|
|
1337
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1338
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1339
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1340
|
+
{ view: "entities", source: "memory.entities" },
|
|
1341
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1342
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1343
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1344
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1345
|
+
{ view: "messages", source: "memory.messages" },
|
|
1346
|
+
{ view: "users", source: "wiki.users" },
|
|
1347
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1348
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1349
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1350
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1351
|
+
];
|
|
1352
|
+
UPSERT_KEYS = {
|
|
1353
|
+
memories: ["id"],
|
|
1354
|
+
tasks: ["id"],
|
|
1355
|
+
behaviors: ["id"],
|
|
1356
|
+
entities: ["id"],
|
|
1357
|
+
relationships: ["id"],
|
|
1358
|
+
entity_aliases: ["alias"],
|
|
1359
|
+
notifications: ["id"],
|
|
1360
|
+
messages: ["id"],
|
|
1361
|
+
users: ["id"],
|
|
1362
|
+
workspaces: ["id"],
|
|
1363
|
+
workspace_users: ["id"],
|
|
1364
|
+
documents: ["id"],
|
|
1365
|
+
chats: ["id"]
|
|
1366
|
+
};
|
|
1367
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1368
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1369
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1370
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1371
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1372
|
+
};
|
|
1373
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1374
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1375
|
+
);
|
|
1376
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1377
|
+
/\bPRAGMA\b/i,
|
|
1378
|
+
/\bsqlite_master\b/i,
|
|
1379
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1380
|
+
/\bMATCH\b/i,
|
|
1381
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1382
|
+
/\bjson_extract\s*\(/i,
|
|
1383
|
+
/\bjulianday\s*\(/i,
|
|
1384
|
+
/\bstrftime\s*\(/i,
|
|
1385
|
+
/\blast_insert_rowid\s*\(/i
|
|
1386
|
+
];
|
|
1387
|
+
prismaClientPromise = null;
|
|
1388
|
+
compatibilityBootstrapPromise = null;
|
|
804
1389
|
}
|
|
805
1390
|
});
|
|
806
1391
|
|
|
807
1392
|
// src/lib/exe-daemon-client.ts
|
|
808
1393
|
import net from "net";
|
|
809
|
-
import
|
|
1394
|
+
import os6 from "os";
|
|
810
1395
|
import { spawn } from "child_process";
|
|
811
1396
|
import { randomUUID } from "crypto";
|
|
812
1397
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
813
|
-
import
|
|
1398
|
+
import path7 from "path";
|
|
814
1399
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
815
1400
|
function handleData(chunk) {
|
|
816
1401
|
_buffer += chunk.toString();
|
|
@@ -861,17 +1446,17 @@ function cleanupStaleFiles() {
|
|
|
861
1446
|
}
|
|
862
1447
|
}
|
|
863
1448
|
function findPackageRoot() {
|
|
864
|
-
let dir =
|
|
865
|
-
const { root } =
|
|
1449
|
+
let dir = path7.dirname(fileURLToPath2(import.meta.url));
|
|
1450
|
+
const { root } = path7.parse(dir);
|
|
866
1451
|
while (dir !== root) {
|
|
867
|
-
if (existsSync6(
|
|
868
|
-
dir =
|
|
1452
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
1453
|
+
dir = path7.dirname(dir);
|
|
869
1454
|
}
|
|
870
1455
|
return null;
|
|
871
1456
|
}
|
|
872
1457
|
function spawnDaemon() {
|
|
873
|
-
const freeGB =
|
|
874
|
-
const totalGB =
|
|
1458
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1459
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
875
1460
|
if (totalGB <= 8) {
|
|
876
1461
|
process.stderr.write(
|
|
877
1462
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -891,7 +1476,7 @@ function spawnDaemon() {
|
|
|
891
1476
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
892
1477
|
return;
|
|
893
1478
|
}
|
|
894
|
-
const daemonPath =
|
|
1479
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
895
1480
|
if (!existsSync6(daemonPath)) {
|
|
896
1481
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
897
1482
|
`);
|
|
@@ -900,7 +1485,7 @@ function spawnDaemon() {
|
|
|
900
1485
|
const resolvedPath = daemonPath;
|
|
901
1486
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
902
1487
|
`);
|
|
903
|
-
const logPath =
|
|
1488
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
904
1489
|
let stderrFd = "ignore";
|
|
905
1490
|
try {
|
|
906
1491
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1047,9 +1632,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1047
1632
|
"src/lib/exe-daemon-client.ts"() {
|
|
1048
1633
|
"use strict";
|
|
1049
1634
|
init_config();
|
|
1050
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1051
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1052
|
-
SPAWN_LOCK_PATH =
|
|
1635
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
1636
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
1637
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1053
1638
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1054
1639
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1055
1640
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1131,7 +1716,7 @@ __export(db_daemon_client_exports, {
|
|
|
1131
1716
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1132
1717
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1133
1718
|
});
|
|
1134
|
-
function
|
|
1719
|
+
function normalizeStatement2(stmt) {
|
|
1135
1720
|
if (typeof stmt === "string") {
|
|
1136
1721
|
return { sql: stmt, args: [] };
|
|
1137
1722
|
}
|
|
@@ -1155,7 +1740,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1155
1740
|
if (!_useDaemon || !isClientConnected()) {
|
|
1156
1741
|
return fallbackClient.execute(stmt);
|
|
1157
1742
|
}
|
|
1158
|
-
const { sql, args } =
|
|
1743
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1159
1744
|
const response = await sendDaemonRequest({
|
|
1160
1745
|
type: "db-execute",
|
|
1161
1746
|
sql,
|
|
@@ -1180,7 +1765,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1180
1765
|
if (!_useDaemon || !isClientConnected()) {
|
|
1181
1766
|
return fallbackClient.batch(stmts, mode);
|
|
1182
1767
|
}
|
|
1183
|
-
const statements = stmts.map(
|
|
1768
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1184
1769
|
const response = await sendDaemonRequest({
|
|
1185
1770
|
type: "db-batch",
|
|
1186
1771
|
statements,
|
|
@@ -1275,6 +1860,18 @@ __export(database_exports, {
|
|
|
1275
1860
|
});
|
|
1276
1861
|
import { createClient } from "@libsql/client";
|
|
1277
1862
|
async function initDatabase(config) {
|
|
1863
|
+
if (_walCheckpointTimer) {
|
|
1864
|
+
clearInterval(_walCheckpointTimer);
|
|
1865
|
+
_walCheckpointTimer = null;
|
|
1866
|
+
}
|
|
1867
|
+
if (_daemonClient) {
|
|
1868
|
+
_daemonClient.close();
|
|
1869
|
+
_daemonClient = null;
|
|
1870
|
+
}
|
|
1871
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1872
|
+
_adapterClient.close();
|
|
1873
|
+
}
|
|
1874
|
+
_adapterClient = null;
|
|
1278
1875
|
if (_client) {
|
|
1279
1876
|
_client.close();
|
|
1280
1877
|
_client = null;
|
|
@@ -1288,6 +1885,7 @@ async function initDatabase(config) {
|
|
|
1288
1885
|
}
|
|
1289
1886
|
_client = createClient(opts);
|
|
1290
1887
|
_resilientClient = wrapWithRetry(_client);
|
|
1888
|
+
_adapterClient = _resilientClient;
|
|
1291
1889
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1292
1890
|
});
|
|
1293
1891
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1298,14 +1896,20 @@ async function initDatabase(config) {
|
|
|
1298
1896
|
});
|
|
1299
1897
|
}, 3e4);
|
|
1300
1898
|
_walCheckpointTimer.unref();
|
|
1899
|
+
if (process.env.DATABASE_URL) {
|
|
1900
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1901
|
+
}
|
|
1301
1902
|
}
|
|
1302
1903
|
function isInitialized() {
|
|
1303
|
-
return _client !== null;
|
|
1904
|
+
return _adapterClient !== null || _client !== null;
|
|
1304
1905
|
}
|
|
1305
1906
|
function getClient() {
|
|
1306
|
-
if (!
|
|
1907
|
+
if (!_adapterClient) {
|
|
1307
1908
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1308
1909
|
}
|
|
1910
|
+
if (process.env.DATABASE_URL) {
|
|
1911
|
+
return _adapterClient;
|
|
1912
|
+
}
|
|
1309
1913
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1310
1914
|
return _resilientClient;
|
|
1311
1915
|
}
|
|
@@ -1315,6 +1919,7 @@ function getClient() {
|
|
|
1315
1919
|
return _resilientClient;
|
|
1316
1920
|
}
|
|
1317
1921
|
async function initDaemonClient() {
|
|
1922
|
+
if (process.env.DATABASE_URL) return;
|
|
1318
1923
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1319
1924
|
if (!_resilientClient) return;
|
|
1320
1925
|
try {
|
|
@@ -2259,26 +2864,36 @@ async function ensureSchema() {
|
|
|
2259
2864
|
}
|
|
2260
2865
|
}
|
|
2261
2866
|
async function disposeDatabase() {
|
|
2867
|
+
if (_walCheckpointTimer) {
|
|
2868
|
+
clearInterval(_walCheckpointTimer);
|
|
2869
|
+
_walCheckpointTimer = null;
|
|
2870
|
+
}
|
|
2262
2871
|
if (_daemonClient) {
|
|
2263
2872
|
_daemonClient.close();
|
|
2264
2873
|
_daemonClient = null;
|
|
2265
2874
|
}
|
|
2875
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2876
|
+
_adapterClient.close();
|
|
2877
|
+
}
|
|
2878
|
+
_adapterClient = null;
|
|
2266
2879
|
if (_client) {
|
|
2267
2880
|
_client.close();
|
|
2268
2881
|
_client = null;
|
|
2269
2882
|
_resilientClient = null;
|
|
2270
2883
|
}
|
|
2271
2884
|
}
|
|
2272
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2885
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2273
2886
|
var init_database = __esm({
|
|
2274
2887
|
"src/lib/database.ts"() {
|
|
2275
2888
|
"use strict";
|
|
2276
2889
|
init_db_retry();
|
|
2277
2890
|
init_employees();
|
|
2891
|
+
init_database_adapter();
|
|
2278
2892
|
_client = null;
|
|
2279
2893
|
_resilientClient = null;
|
|
2280
2894
|
_walCheckpointTimer = null;
|
|
2281
2895
|
_daemonClient = null;
|
|
2896
|
+
_adapterClient = null;
|
|
2282
2897
|
initTurso = initDatabase;
|
|
2283
2898
|
disposeTurso = disposeDatabase;
|
|
2284
2899
|
}
|
|
@@ -2287,16 +2902,16 @@ var init_database = __esm({
|
|
|
2287
2902
|
// src/lib/license.ts
|
|
2288
2903
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2289
2904
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2290
|
-
import
|
|
2905
|
+
import path8 from "path";
|
|
2291
2906
|
import { jwtVerify, importSPKI } from "jose";
|
|
2292
2907
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2293
2908
|
var init_license = __esm({
|
|
2294
2909
|
"src/lib/license.ts"() {
|
|
2295
2910
|
"use strict";
|
|
2296
2911
|
init_config();
|
|
2297
|
-
LICENSE_PATH =
|
|
2298
|
-
CACHE_PATH =
|
|
2299
|
-
DEVICE_ID_PATH =
|
|
2912
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2913
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2914
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2300
2915
|
PLAN_LIMITS = {
|
|
2301
2916
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2302
2917
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2309,7 +2924,7 @@ var init_license = __esm({
|
|
|
2309
2924
|
|
|
2310
2925
|
// src/lib/plan-limits.ts
|
|
2311
2926
|
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2312
|
-
import
|
|
2927
|
+
import path9 from "path";
|
|
2313
2928
|
function getLicenseSync() {
|
|
2314
2929
|
try {
|
|
2315
2930
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
@@ -2381,14 +2996,14 @@ var init_plan_limits = __esm({
|
|
|
2381
2996
|
this.name = "PlanLimitError";
|
|
2382
2997
|
}
|
|
2383
2998
|
};
|
|
2384
|
-
CACHE_PATH2 =
|
|
2999
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2385
3000
|
}
|
|
2386
3001
|
});
|
|
2387
3002
|
|
|
2388
3003
|
// src/lib/notifications.ts
|
|
2389
3004
|
import crypto from "crypto";
|
|
2390
|
-
import
|
|
2391
|
-
import
|
|
3005
|
+
import path10 from "path";
|
|
3006
|
+
import os7 from "os";
|
|
2392
3007
|
import {
|
|
2393
3008
|
readFileSync as readFileSync9,
|
|
2394
3009
|
readdirSync,
|
|
@@ -2528,8 +3143,8 @@ var init_state_bus = __esm({
|
|
|
2528
3143
|
|
|
2529
3144
|
// src/lib/tasks-crud.ts
|
|
2530
3145
|
import crypto3 from "crypto";
|
|
2531
|
-
import
|
|
2532
|
-
import
|
|
3146
|
+
import path11 from "path";
|
|
3147
|
+
import os8 from "os";
|
|
2533
3148
|
import { execSync as execSync4 } from "child_process";
|
|
2534
3149
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2535
3150
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
@@ -2707,8 +3322,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2707
3322
|
}
|
|
2708
3323
|
if (input.baseDir) {
|
|
2709
3324
|
try {
|
|
2710
|
-
await mkdir3(
|
|
2711
|
-
await mkdir3(
|
|
3325
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3326
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2712
3327
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2713
3328
|
await ensureGitignoreExe(input.baseDir);
|
|
2714
3329
|
} catch {
|
|
@@ -2744,9 +3359,9 @@ ${laneWarning}` : laneWarning;
|
|
|
2744
3359
|
});
|
|
2745
3360
|
if (input.baseDir) {
|
|
2746
3361
|
try {
|
|
2747
|
-
const EXE_OS_DIR =
|
|
2748
|
-
const mdPath =
|
|
2749
|
-
const mdDir =
|
|
3362
|
+
const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
|
|
3363
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
3364
|
+
const mdDir = path11.dirname(mdPath);
|
|
2750
3365
|
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2751
3366
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2752
3367
|
const mdContent = `# ${input.title}
|
|
@@ -3047,7 +3662,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3047
3662
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3048
3663
|
}
|
|
3049
3664
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3050
|
-
const archPath =
|
|
3665
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3051
3666
|
try {
|
|
3052
3667
|
if (existsSync10(archPath)) return;
|
|
3053
3668
|
const template = [
|
|
@@ -3082,7 +3697,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3082
3697
|
}
|
|
3083
3698
|
}
|
|
3084
3699
|
async function ensureGitignoreExe(baseDir) {
|
|
3085
|
-
const gitignorePath =
|
|
3700
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
3086
3701
|
try {
|
|
3087
3702
|
if (existsSync10(gitignorePath)) {
|
|
3088
3703
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -3116,13 +3731,13 @@ var init_tasks_crud = __esm({
|
|
|
3116
3731
|
});
|
|
3117
3732
|
|
|
3118
3733
|
// src/lib/tasks-review.ts
|
|
3119
|
-
import
|
|
3734
|
+
import path12 from "path";
|
|
3120
3735
|
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3121
3736
|
async function countPendingReviews(sessionScope) {
|
|
3122
3737
|
const client = getClient();
|
|
3123
3738
|
if (sessionScope) {
|
|
3124
3739
|
const result2 = await client.execute({
|
|
3125
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
3740
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3126
3741
|
args: [sessionScope]
|
|
3127
3742
|
});
|
|
3128
3743
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3298,11 +3913,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3298
3913
|
);
|
|
3299
3914
|
}
|
|
3300
3915
|
try {
|
|
3301
|
-
const cacheDir =
|
|
3916
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3302
3917
|
if (existsSync11(cacheDir)) {
|
|
3303
3918
|
for (const f of readdirSync2(cacheDir)) {
|
|
3304
3919
|
if (f.startsWith("review-notified-")) {
|
|
3305
|
-
unlinkSync4(
|
|
3920
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
3306
3921
|
}
|
|
3307
3922
|
}
|
|
3308
3923
|
}
|
|
@@ -3323,7 +3938,7 @@ var init_tasks_review = __esm({
|
|
|
3323
3938
|
});
|
|
3324
3939
|
|
|
3325
3940
|
// src/lib/tasks-chain.ts
|
|
3326
|
-
import
|
|
3941
|
+
import path13 from "path";
|
|
3327
3942
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3328
3943
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3329
3944
|
const client = getClient();
|
|
@@ -3340,7 +3955,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3340
3955
|
});
|
|
3341
3956
|
for (const ur of unblockedRows.rows) {
|
|
3342
3957
|
try {
|
|
3343
|
-
const ubFile =
|
|
3958
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3344
3959
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3345
3960
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3346
3961
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3414,7 +4029,7 @@ __export(project_name_exports, {
|
|
|
3414
4029
|
getProjectName: () => getProjectName
|
|
3415
4030
|
});
|
|
3416
4031
|
import { execSync as execSync5 } from "child_process";
|
|
3417
|
-
import
|
|
4032
|
+
import path14 from "path";
|
|
3418
4033
|
function getProjectName(cwd) {
|
|
3419
4034
|
const dir = cwd ?? process.cwd();
|
|
3420
4035
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3427,7 +4042,7 @@ function getProjectName(cwd) {
|
|
|
3427
4042
|
timeout: 2e3,
|
|
3428
4043
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3429
4044
|
}).trim();
|
|
3430
|
-
repoRoot =
|
|
4045
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
3431
4046
|
} catch {
|
|
3432
4047
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
3433
4048
|
cwd: dir,
|
|
@@ -3436,11 +4051,11 @@ function getProjectName(cwd) {
|
|
|
3436
4051
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3437
4052
|
}).trim();
|
|
3438
4053
|
}
|
|
3439
|
-
_cached2 =
|
|
4054
|
+
_cached2 = path14.basename(repoRoot);
|
|
3440
4055
|
_cachedCwd = dir;
|
|
3441
4056
|
return _cached2;
|
|
3442
4057
|
} catch {
|
|
3443
|
-
_cached2 =
|
|
4058
|
+
_cached2 = path14.basename(dir);
|
|
3444
4059
|
_cachedCwd = dir;
|
|
3445
4060
|
return _cached2;
|
|
3446
4061
|
}
|
|
@@ -3917,7 +4532,7 @@ __export(tasks_exports, {
|
|
|
3917
4532
|
updateTaskStatus: () => updateTaskStatus,
|
|
3918
4533
|
writeCheckpoint: () => writeCheckpoint
|
|
3919
4534
|
});
|
|
3920
|
-
import
|
|
4535
|
+
import path15 from "path";
|
|
3921
4536
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3922
4537
|
async function createTask(input) {
|
|
3923
4538
|
const result = await createTaskCore(input);
|
|
@@ -3937,8 +4552,8 @@ async function updateTask(input) {
|
|
|
3937
4552
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3938
4553
|
try {
|
|
3939
4554
|
const agent = String(row.assigned_to);
|
|
3940
|
-
const cacheDir =
|
|
3941
|
-
const cachePath =
|
|
4555
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
4556
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3942
4557
|
if (input.status === "in_progress") {
|
|
3943
4558
|
mkdirSync5(cacheDir, { recursive: true });
|
|
3944
4559
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -4409,12 +5024,12 @@ __export(tmux_routing_exports, {
|
|
|
4409
5024
|
});
|
|
4410
5025
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
4411
5026
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
4412
|
-
import
|
|
4413
|
-
import
|
|
5027
|
+
import path16 from "path";
|
|
5028
|
+
import os9 from "os";
|
|
4414
5029
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4415
5030
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4416
5031
|
function spawnLockPath(sessionName) {
|
|
4417
|
-
return
|
|
5032
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4418
5033
|
}
|
|
4419
5034
|
function isProcessAlive(pid) {
|
|
4420
5035
|
try {
|
|
@@ -4451,8 +5066,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4451
5066
|
function resolveBehaviorsExporterScript() {
|
|
4452
5067
|
try {
|
|
4453
5068
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
4454
|
-
const scriptPath =
|
|
4455
|
-
|
|
5069
|
+
const scriptPath = path16.join(
|
|
5070
|
+
path16.dirname(thisFile),
|
|
4456
5071
|
"..",
|
|
4457
5072
|
"bin",
|
|
4458
5073
|
"exe-export-behaviors.js"
|
|
@@ -4527,7 +5142,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4527
5142
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4528
5143
|
}
|
|
4529
5144
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4530
|
-
const filePath =
|
|
5145
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4531
5146
|
writeFileSync7(filePath, JSON.stringify({
|
|
4532
5147
|
parentExe: rootExe,
|
|
4533
5148
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -4536,7 +5151,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4536
5151
|
}
|
|
4537
5152
|
function getParentExe(sessionKey) {
|
|
4538
5153
|
try {
|
|
4539
|
-
const data = JSON.parse(readFileSync11(
|
|
5154
|
+
const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4540
5155
|
return data.parentExe || null;
|
|
4541
5156
|
} catch {
|
|
4542
5157
|
return null;
|
|
@@ -4545,7 +5160,7 @@ function getParentExe(sessionKey) {
|
|
|
4545
5160
|
function getDispatchedBy(sessionKey) {
|
|
4546
5161
|
try {
|
|
4547
5162
|
const data = JSON.parse(readFileSync11(
|
|
4548
|
-
|
|
5163
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4549
5164
|
"utf8"
|
|
4550
5165
|
));
|
|
4551
5166
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4556,15 +5171,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
4556
5171
|
function resolveExeSession() {
|
|
4557
5172
|
const mySession = getMySession();
|
|
4558
5173
|
if (!mySession) return null;
|
|
5174
|
+
const fromSessionName = extractRootExe(mySession);
|
|
4559
5175
|
try {
|
|
4560
5176
|
const key = getSessionKey();
|
|
4561
5177
|
const parentExe = getParentExe(key);
|
|
4562
5178
|
if (parentExe) {
|
|
4563
|
-
|
|
5179
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
5180
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
5181
|
+
process.stderr.write(
|
|
5182
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
5183
|
+
`
|
|
5184
|
+
);
|
|
5185
|
+
return fromSessionName;
|
|
5186
|
+
}
|
|
5187
|
+
return fromCache;
|
|
4564
5188
|
}
|
|
4565
5189
|
} catch {
|
|
4566
5190
|
}
|
|
4567
|
-
return
|
|
5191
|
+
return fromSessionName ?? mySession;
|
|
4568
5192
|
}
|
|
4569
5193
|
function isEmployeeAlive(sessionName) {
|
|
4570
5194
|
return getTransport().isAlive(sessionName);
|
|
@@ -4722,7 +5346,7 @@ function sendIntercom(targetSession) {
|
|
|
4722
5346
|
try {
|
|
4723
5347
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4724
5348
|
const agent = baseAgentName(rawAgent);
|
|
4725
|
-
const markerPath =
|
|
5349
|
+
const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4726
5350
|
if (existsSync12(markerPath)) {
|
|
4727
5351
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4728
5352
|
return "debounced";
|
|
@@ -4732,7 +5356,7 @@ function sendIntercom(targetSession) {
|
|
|
4732
5356
|
try {
|
|
4733
5357
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4734
5358
|
const agent = baseAgentName(rawAgent);
|
|
4735
|
-
const taskDir =
|
|
5359
|
+
const taskDir = path16.join(process.cwd(), "exe", agent);
|
|
4736
5360
|
if (existsSync12(taskDir)) {
|
|
4737
5361
|
const files = readdirSync3(taskDir).filter(
|
|
4738
5362
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4866,8 +5490,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4866
5490
|
const transport = getTransport();
|
|
4867
5491
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4868
5492
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4869
|
-
const logDir =
|
|
4870
|
-
const logFile =
|
|
5493
|
+
const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5494
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4871
5495
|
if (!existsSync12(logDir)) {
|
|
4872
5496
|
mkdirSync6(logDir, { recursive: true });
|
|
4873
5497
|
}
|
|
@@ -4875,14 +5499,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4875
5499
|
let cleanupSuffix = "";
|
|
4876
5500
|
try {
|
|
4877
5501
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
4878
|
-
const cleanupScript =
|
|
5502
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4879
5503
|
if (existsSync12(cleanupScript)) {
|
|
4880
5504
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4881
5505
|
}
|
|
4882
5506
|
} catch {
|
|
4883
5507
|
}
|
|
4884
5508
|
try {
|
|
4885
|
-
const claudeJsonPath =
|
|
5509
|
+
const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
|
|
4886
5510
|
let claudeJson = {};
|
|
4887
5511
|
try {
|
|
4888
5512
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -4897,10 +5521,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4897
5521
|
} catch {
|
|
4898
5522
|
}
|
|
4899
5523
|
try {
|
|
4900
|
-
const settingsDir =
|
|
5524
|
+
const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
|
|
4901
5525
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4902
|
-
const projSettingsDir =
|
|
4903
|
-
const settingsPath =
|
|
5526
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
5527
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4904
5528
|
let settings = {};
|
|
4905
5529
|
try {
|
|
4906
5530
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -4947,8 +5571,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4947
5571
|
let behaviorsFlag = "";
|
|
4948
5572
|
let legacyFallbackWarned = false;
|
|
4949
5573
|
if (!useExeAgent && !useBinSymlink) {
|
|
4950
|
-
const identityPath =
|
|
4951
|
-
|
|
5574
|
+
const identityPath = path16.join(
|
|
5575
|
+
os9.homedir(),
|
|
4952
5576
|
".exe-os",
|
|
4953
5577
|
"identity",
|
|
4954
5578
|
`${employeeName}.md`
|
|
@@ -4963,7 +5587,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4963
5587
|
}
|
|
4964
5588
|
const behaviorsFile = exportBehaviorsSync(
|
|
4965
5589
|
employeeName,
|
|
4966
|
-
|
|
5590
|
+
path16.basename(spawnCwd),
|
|
4967
5591
|
sessionName
|
|
4968
5592
|
);
|
|
4969
5593
|
if (behaviorsFile) {
|
|
@@ -4978,9 +5602,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4978
5602
|
}
|
|
4979
5603
|
let sessionContextFlag = "";
|
|
4980
5604
|
try {
|
|
4981
|
-
const ctxDir =
|
|
5605
|
+
const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4982
5606
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4983
|
-
const ctxFile =
|
|
5607
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4984
5608
|
const ctxContent = [
|
|
4985
5609
|
`## Session Context`,
|
|
4986
5610
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5064,7 +5688,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5064
5688
|
transport.pipeLog(sessionName, logFile);
|
|
5065
5689
|
try {
|
|
5066
5690
|
const mySession = getMySession();
|
|
5067
|
-
const dispatchInfo =
|
|
5691
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5068
5692
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5069
5693
|
dispatchedBy: mySession,
|
|
5070
5694
|
rootExe: exeSession,
|
|
@@ -5139,15 +5763,15 @@ var init_tmux_routing = __esm({
|
|
|
5139
5763
|
init_intercom_queue();
|
|
5140
5764
|
init_plan_limits();
|
|
5141
5765
|
init_employees();
|
|
5142
|
-
SPAWN_LOCK_DIR =
|
|
5143
|
-
SESSION_CACHE =
|
|
5766
|
+
SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5767
|
+
SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5144
5768
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5145
5769
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5146
5770
|
VERIFY_PANE_LINES = 200;
|
|
5147
5771
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5148
5772
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5149
|
-
INTERCOM_LOG2 =
|
|
5150
|
-
DEBOUNCE_FILE =
|
|
5773
|
+
INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5774
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5151
5775
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5152
5776
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5153
5777
|
}
|
|
@@ -5189,13 +5813,13 @@ var init_memory = __esm({
|
|
|
5189
5813
|
// src/lib/keychain.ts
|
|
5190
5814
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5191
5815
|
import { existsSync as existsSync13 } from "fs";
|
|
5192
|
-
import
|
|
5193
|
-
import
|
|
5816
|
+
import path17 from "path";
|
|
5817
|
+
import os10 from "os";
|
|
5194
5818
|
function getKeyDir() {
|
|
5195
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5819
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os10.homedir(), ".exe-os");
|
|
5196
5820
|
}
|
|
5197
5821
|
function getKeyPath() {
|
|
5198
|
-
return
|
|
5822
|
+
return path17.join(getKeyDir(), "master.key");
|
|
5199
5823
|
}
|
|
5200
5824
|
async function tryKeytar() {
|
|
5201
5825
|
try {
|
|
@@ -5218,7 +5842,7 @@ async function getMasterKey() {
|
|
|
5218
5842
|
const keyPath = getKeyPath();
|
|
5219
5843
|
if (!existsSync13(keyPath)) {
|
|
5220
5844
|
process.stderr.write(
|
|
5221
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
5845
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5222
5846
|
`
|
|
5223
5847
|
);
|
|
5224
5848
|
return null;
|
|
@@ -5256,7 +5880,7 @@ __export(shard_manager_exports, {
|
|
|
5256
5880
|
listShards: () => listShards,
|
|
5257
5881
|
shardExists: () => shardExists
|
|
5258
5882
|
});
|
|
5259
|
-
import
|
|
5883
|
+
import path18 from "path";
|
|
5260
5884
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
5261
5885
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5262
5886
|
function initShardManager(encryptionKey) {
|
|
@@ -5282,7 +5906,7 @@ function getShardClient(projectName) {
|
|
|
5282
5906
|
}
|
|
5283
5907
|
const cached = _shards.get(safeName);
|
|
5284
5908
|
if (cached) return cached;
|
|
5285
|
-
const dbPath =
|
|
5909
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
5286
5910
|
const client = createClient2({
|
|
5287
5911
|
url: `file:${dbPath}`,
|
|
5288
5912
|
encryptionKey: _encryptionKey
|
|
@@ -5292,7 +5916,7 @@ function getShardClient(projectName) {
|
|
|
5292
5916
|
}
|
|
5293
5917
|
function shardExists(projectName) {
|
|
5294
5918
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5295
|
-
return existsSync14(
|
|
5919
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
5296
5920
|
}
|
|
5297
5921
|
function listShards() {
|
|
5298
5922
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
@@ -5369,7 +5993,23 @@ async function ensureShardSchema(client) {
|
|
|
5369
5993
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
5370
5994
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
5371
5995
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
5372
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
5996
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
5997
|
+
// Metadata enrichment columns (must match database.ts)
|
|
5998
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
5999
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
6000
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
6001
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
6002
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
6003
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
6004
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
6005
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
6006
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
6007
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
6008
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
6009
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
6010
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
6011
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
6012
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
5373
6013
|
]) {
|
|
5374
6014
|
try {
|
|
5375
6015
|
await client.execute(col);
|
|
@@ -5481,7 +6121,7 @@ var init_shard_manager = __esm({
|
|
|
5481
6121
|
"src/lib/shard-manager.ts"() {
|
|
5482
6122
|
"use strict";
|
|
5483
6123
|
init_config();
|
|
5484
|
-
SHARDS_DIR =
|
|
6124
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5485
6125
|
_shards = /* @__PURE__ */ new Map();
|
|
5486
6126
|
_encryptionKey = null;
|
|
5487
6127
|
_shardingEnabled = false;
|
|
@@ -6247,8 +6887,8 @@ var init_store = __esm({
|
|
|
6247
6887
|
|
|
6248
6888
|
// src/bin/scan-tasks.ts
|
|
6249
6889
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
6250
|
-
import
|
|
6251
|
-
import
|
|
6890
|
+
import path19 from "path";
|
|
6891
|
+
import os11 from "os";
|
|
6252
6892
|
|
|
6253
6893
|
// src/lib/is-main.ts
|
|
6254
6894
|
import { realpathSync } from "fs";
|
|
@@ -6268,13 +6908,13 @@ function isMainModule(importMetaUrl) {
|
|
|
6268
6908
|
// src/bin/scan-tasks.ts
|
|
6269
6909
|
init_session_key();
|
|
6270
6910
|
init_task_scope();
|
|
6271
|
-
function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir =
|
|
6911
|
+
function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir = os11.homedir()) {
|
|
6272
6912
|
if (runtime === "codex") {
|
|
6273
6913
|
return null;
|
|
6274
6914
|
}
|
|
6275
6915
|
try {
|
|
6276
6916
|
if (runtime === "opencode") {
|
|
6277
|
-
const opencodeJson =
|
|
6917
|
+
const opencodeJson = path19.join(homeDir, ".config", "opencode", "opencode.json");
|
|
6278
6918
|
if (!existsSync15(opencodeJson)) {
|
|
6279
6919
|
return "\u26A0\uFE0F MCP config missing (~/.config/opencode/opencode.json not found) \u2014 exe-os task tools may be unavailable. Run `exe-os opencode`.\n";
|
|
6280
6920
|
}
|
|
@@ -6284,7 +6924,7 @@ function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir = os10.homed
|
|
|
6284
6924
|
}
|
|
6285
6925
|
return null;
|
|
6286
6926
|
}
|
|
6287
|
-
const claudeJson =
|
|
6927
|
+
const claudeJson = path19.join(homeDir, ".claude.json");
|
|
6288
6928
|
if (!existsSync15(claudeJson)) {
|
|
6289
6929
|
return "\u26A0\uFE0F MCP config missing (~/.claude.json not found) \u2014 close_task won't work. Run /exe-setup\n";
|
|
6290
6930
|
}
|
|
@@ -6453,7 +7093,7 @@ async function main() {
|
|
|
6453
7093
|
if (out) tmuxSession = out;
|
|
6454
7094
|
} catch {
|
|
6455
7095
|
}
|
|
6456
|
-
const result = await scanFromDb(agentId, tmuxSession);
|
|
7096
|
+
const result = await scanFromDb(agentId, tmuxSession, null);
|
|
6457
7097
|
switch (format) {
|
|
6458
7098
|
case "json":
|
|
6459
7099
|
console.log(formatJson(result));
|