@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/bin/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;
|
|
@@ -4731,7 +5346,7 @@ function sendIntercom(targetSession) {
|
|
|
4731
5346
|
try {
|
|
4732
5347
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4733
5348
|
const agent = baseAgentName(rawAgent);
|
|
4734
|
-
const markerPath =
|
|
5349
|
+
const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4735
5350
|
if (existsSync12(markerPath)) {
|
|
4736
5351
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4737
5352
|
return "debounced";
|
|
@@ -4741,7 +5356,7 @@ function sendIntercom(targetSession) {
|
|
|
4741
5356
|
try {
|
|
4742
5357
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4743
5358
|
const agent = baseAgentName(rawAgent);
|
|
4744
|
-
const taskDir =
|
|
5359
|
+
const taskDir = path16.join(process.cwd(), "exe", agent);
|
|
4745
5360
|
if (existsSync12(taskDir)) {
|
|
4746
5361
|
const files = readdirSync3(taskDir).filter(
|
|
4747
5362
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4875,8 +5490,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4875
5490
|
const transport = getTransport();
|
|
4876
5491
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4877
5492
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4878
|
-
const logDir =
|
|
4879
|
-
const logFile =
|
|
5493
|
+
const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5494
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4880
5495
|
if (!existsSync12(logDir)) {
|
|
4881
5496
|
mkdirSync6(logDir, { recursive: true });
|
|
4882
5497
|
}
|
|
@@ -4884,14 +5499,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4884
5499
|
let cleanupSuffix = "";
|
|
4885
5500
|
try {
|
|
4886
5501
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
4887
|
-
const cleanupScript =
|
|
5502
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4888
5503
|
if (existsSync12(cleanupScript)) {
|
|
4889
5504
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4890
5505
|
}
|
|
4891
5506
|
} catch {
|
|
4892
5507
|
}
|
|
4893
5508
|
try {
|
|
4894
|
-
const claudeJsonPath =
|
|
5509
|
+
const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
|
|
4895
5510
|
let claudeJson = {};
|
|
4896
5511
|
try {
|
|
4897
5512
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -4906,10 +5521,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4906
5521
|
} catch {
|
|
4907
5522
|
}
|
|
4908
5523
|
try {
|
|
4909
|
-
const settingsDir =
|
|
5524
|
+
const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
|
|
4910
5525
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4911
|
-
const projSettingsDir =
|
|
4912
|
-
const settingsPath =
|
|
5526
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
5527
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4913
5528
|
let settings = {};
|
|
4914
5529
|
try {
|
|
4915
5530
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -4956,8 +5571,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4956
5571
|
let behaviorsFlag = "";
|
|
4957
5572
|
let legacyFallbackWarned = false;
|
|
4958
5573
|
if (!useExeAgent && !useBinSymlink) {
|
|
4959
|
-
const identityPath =
|
|
4960
|
-
|
|
5574
|
+
const identityPath = path16.join(
|
|
5575
|
+
os9.homedir(),
|
|
4961
5576
|
".exe-os",
|
|
4962
5577
|
"identity",
|
|
4963
5578
|
`${employeeName}.md`
|
|
@@ -4972,7 +5587,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4972
5587
|
}
|
|
4973
5588
|
const behaviorsFile = exportBehaviorsSync(
|
|
4974
5589
|
employeeName,
|
|
4975
|
-
|
|
5590
|
+
path16.basename(spawnCwd),
|
|
4976
5591
|
sessionName
|
|
4977
5592
|
);
|
|
4978
5593
|
if (behaviorsFile) {
|
|
@@ -4987,9 +5602,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4987
5602
|
}
|
|
4988
5603
|
let sessionContextFlag = "";
|
|
4989
5604
|
try {
|
|
4990
|
-
const ctxDir =
|
|
5605
|
+
const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4991
5606
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4992
|
-
const ctxFile =
|
|
5607
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4993
5608
|
const ctxContent = [
|
|
4994
5609
|
`## Session Context`,
|
|
4995
5610
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5073,7 +5688,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5073
5688
|
transport.pipeLog(sessionName, logFile);
|
|
5074
5689
|
try {
|
|
5075
5690
|
const mySession = getMySession();
|
|
5076
|
-
const dispatchInfo =
|
|
5691
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5077
5692
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5078
5693
|
dispatchedBy: mySession,
|
|
5079
5694
|
rootExe: exeSession,
|
|
@@ -5148,15 +5763,15 @@ var init_tmux_routing = __esm({
|
|
|
5148
5763
|
init_intercom_queue();
|
|
5149
5764
|
init_plan_limits();
|
|
5150
5765
|
init_employees();
|
|
5151
|
-
SPAWN_LOCK_DIR =
|
|
5152
|
-
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");
|
|
5153
5768
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5154
5769
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5155
5770
|
VERIFY_PANE_LINES = 200;
|
|
5156
5771
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5157
5772
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5158
|
-
INTERCOM_LOG2 =
|
|
5159
|
-
DEBOUNCE_FILE =
|
|
5773
|
+
INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5774
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5160
5775
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5161
5776
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5162
5777
|
}
|
|
@@ -5198,13 +5813,13 @@ var init_memory = __esm({
|
|
|
5198
5813
|
// src/lib/keychain.ts
|
|
5199
5814
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5200
5815
|
import { existsSync as existsSync13 } from "fs";
|
|
5201
|
-
import
|
|
5202
|
-
import
|
|
5816
|
+
import path17 from "path";
|
|
5817
|
+
import os10 from "os";
|
|
5203
5818
|
function getKeyDir() {
|
|
5204
|
-
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");
|
|
5205
5820
|
}
|
|
5206
5821
|
function getKeyPath() {
|
|
5207
|
-
return
|
|
5822
|
+
return path17.join(getKeyDir(), "master.key");
|
|
5208
5823
|
}
|
|
5209
5824
|
async function tryKeytar() {
|
|
5210
5825
|
try {
|
|
@@ -5227,7 +5842,7 @@ async function getMasterKey() {
|
|
|
5227
5842
|
const keyPath = getKeyPath();
|
|
5228
5843
|
if (!existsSync13(keyPath)) {
|
|
5229
5844
|
process.stderr.write(
|
|
5230
|
-
`[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"})
|
|
5231
5846
|
`
|
|
5232
5847
|
);
|
|
5233
5848
|
return null;
|
|
@@ -5265,7 +5880,7 @@ __export(shard_manager_exports, {
|
|
|
5265
5880
|
listShards: () => listShards,
|
|
5266
5881
|
shardExists: () => shardExists
|
|
5267
5882
|
});
|
|
5268
|
-
import
|
|
5883
|
+
import path18 from "path";
|
|
5269
5884
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
5270
5885
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5271
5886
|
function initShardManager(encryptionKey) {
|
|
@@ -5291,7 +5906,7 @@ function getShardClient(projectName) {
|
|
|
5291
5906
|
}
|
|
5292
5907
|
const cached = _shards.get(safeName);
|
|
5293
5908
|
if (cached) return cached;
|
|
5294
|
-
const dbPath =
|
|
5909
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
5295
5910
|
const client = createClient2({
|
|
5296
5911
|
url: `file:${dbPath}`,
|
|
5297
5912
|
encryptionKey: _encryptionKey
|
|
@@ -5301,7 +5916,7 @@ function getShardClient(projectName) {
|
|
|
5301
5916
|
}
|
|
5302
5917
|
function shardExists(projectName) {
|
|
5303
5918
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5304
|
-
return existsSync14(
|
|
5919
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
5305
5920
|
}
|
|
5306
5921
|
function listShards() {
|
|
5307
5922
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
@@ -5378,7 +5993,23 @@ async function ensureShardSchema(client) {
|
|
|
5378
5993
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
5379
5994
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
5380
5995
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
5381
|
-
"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"
|
|
5382
6013
|
]) {
|
|
5383
6014
|
try {
|
|
5384
6015
|
await client.execute(col);
|
|
@@ -5490,7 +6121,7 @@ var init_shard_manager = __esm({
|
|
|
5490
6121
|
"src/lib/shard-manager.ts"() {
|
|
5491
6122
|
"use strict";
|
|
5492
6123
|
init_config();
|
|
5493
|
-
SHARDS_DIR =
|
|
6124
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5494
6125
|
_shards = /* @__PURE__ */ new Map();
|
|
5495
6126
|
_encryptionKey = null;
|
|
5496
6127
|
_shardingEnabled = false;
|
|
@@ -6256,8 +6887,8 @@ var init_store = __esm({
|
|
|
6256
6887
|
|
|
6257
6888
|
// src/bin/scan-tasks.ts
|
|
6258
6889
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
6259
|
-
import
|
|
6260
|
-
import
|
|
6890
|
+
import path19 from "path";
|
|
6891
|
+
import os11 from "os";
|
|
6261
6892
|
|
|
6262
6893
|
// src/lib/is-main.ts
|
|
6263
6894
|
import { realpathSync } from "fs";
|
|
@@ -6277,13 +6908,13 @@ function isMainModule(importMetaUrl) {
|
|
|
6277
6908
|
// src/bin/scan-tasks.ts
|
|
6278
6909
|
init_session_key();
|
|
6279
6910
|
init_task_scope();
|
|
6280
|
-
function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir =
|
|
6911
|
+
function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir = os11.homedir()) {
|
|
6281
6912
|
if (runtime === "codex") {
|
|
6282
6913
|
return null;
|
|
6283
6914
|
}
|
|
6284
6915
|
try {
|
|
6285
6916
|
if (runtime === "opencode") {
|
|
6286
|
-
const opencodeJson =
|
|
6917
|
+
const opencodeJson = path19.join(homeDir, ".config", "opencode", "opencode.json");
|
|
6287
6918
|
if (!existsSync15(opencodeJson)) {
|
|
6288
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";
|
|
6289
6920
|
}
|
|
@@ -6293,7 +6924,7 @@ function getMcpHealthWarning(runtime = getSessionRuntime(), homeDir = os10.homed
|
|
|
6293
6924
|
}
|
|
6294
6925
|
return null;
|
|
6295
6926
|
}
|
|
6296
|
-
const claudeJson =
|
|
6927
|
+
const claudeJson = path19.join(homeDir, ".claude.json");
|
|
6297
6928
|
if (!existsSync15(claudeJson)) {
|
|
6298
6929
|
return "\u26A0\uFE0F MCP config missing (~/.claude.json not found) \u2014 close_task won't work. Run /exe-setup\n";
|
|
6299
6930
|
}
|
|
@@ -6462,7 +7093,7 @@ async function main() {
|
|
|
6462
7093
|
if (out) tmuxSession = out;
|
|
6463
7094
|
} catch {
|
|
6464
7095
|
}
|
|
6465
|
-
const result = await scanFromDb(agentId, tmuxSession);
|
|
7096
|
+
const result = await scanFromDb(agentId, tmuxSession, null);
|
|
6466
7097
|
switch (format) {
|
|
6467
7098
|
case "json":
|
|
6468
7099
|
console.log(formatJson(result));
|