@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
|
@@ -779,7 +779,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
779
779
|
if (!emp) return false;
|
|
780
780
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
781
781
|
}
|
|
782
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
782
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
783
783
|
var init_employees = __esm({
|
|
784
784
|
"src/lib/employees.ts"() {
|
|
785
785
|
"use strict";
|
|
@@ -788,16 +788,601 @@ var init_employees = __esm({
|
|
|
788
788
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
789
789
|
COORDINATOR_ROLE = "COO";
|
|
790
790
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
791
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
// src/lib/database-adapter.ts
|
|
796
|
+
import os5 from "os";
|
|
797
|
+
import path6 from "path";
|
|
798
|
+
import { createRequire } from "module";
|
|
799
|
+
import { pathToFileURL } from "url";
|
|
800
|
+
function quotedIdentifier(identifier) {
|
|
801
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
802
|
+
}
|
|
803
|
+
function unqualifiedTableName(name) {
|
|
804
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
805
|
+
const parts = raw.split(".");
|
|
806
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
807
|
+
}
|
|
808
|
+
function stripTrailingSemicolon(sql) {
|
|
809
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
810
|
+
}
|
|
811
|
+
function appendClause(sql, clause) {
|
|
812
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
813
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
814
|
+
if (!returningMatch) {
|
|
815
|
+
return `${trimmed}${clause}`;
|
|
816
|
+
}
|
|
817
|
+
const idx = returningMatch.index;
|
|
818
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
819
|
+
}
|
|
820
|
+
function normalizeStatement(stmt) {
|
|
821
|
+
if (typeof stmt === "string") {
|
|
822
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
823
|
+
}
|
|
824
|
+
const sql = stmt.sql;
|
|
825
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
826
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
827
|
+
}
|
|
828
|
+
return { kind: "named", sql, args: stmt.args };
|
|
829
|
+
}
|
|
830
|
+
function rewriteBooleanLiterals(sql) {
|
|
831
|
+
let out = sql;
|
|
832
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
833
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
834
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
835
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
836
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
837
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
838
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
839
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
840
|
+
}
|
|
841
|
+
return out;
|
|
842
|
+
}
|
|
843
|
+
function rewriteInsertOrIgnore(sql) {
|
|
844
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
845
|
+
return sql;
|
|
846
|
+
}
|
|
847
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
848
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
849
|
+
}
|
|
850
|
+
function rewriteInsertOrReplace(sql) {
|
|
851
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
852
|
+
if (!match) {
|
|
853
|
+
return sql;
|
|
854
|
+
}
|
|
855
|
+
const rawTable = match[1];
|
|
856
|
+
const rawColumns = match[2];
|
|
857
|
+
const remainder = match[3];
|
|
858
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
859
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
860
|
+
if (!conflictKeys?.length) {
|
|
861
|
+
return sql;
|
|
862
|
+
}
|
|
863
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
864
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
865
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
866
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
867
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
868
|
+
}
|
|
869
|
+
function rewriteSql(sql) {
|
|
870
|
+
let out = sql;
|
|
871
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
872
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
873
|
+
out = rewriteBooleanLiterals(out);
|
|
874
|
+
out = rewriteInsertOrReplace(out);
|
|
875
|
+
out = rewriteInsertOrIgnore(out);
|
|
876
|
+
return stripTrailingSemicolon(out);
|
|
877
|
+
}
|
|
878
|
+
function toBoolean(value) {
|
|
879
|
+
if (value === null || value === void 0) return value;
|
|
880
|
+
if (typeof value === "boolean") return value;
|
|
881
|
+
if (typeof value === "number") return value !== 0;
|
|
882
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
883
|
+
if (typeof value === "string") {
|
|
884
|
+
const normalized = value.trim().toLowerCase();
|
|
885
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
886
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
887
|
+
}
|
|
888
|
+
return Boolean(value);
|
|
889
|
+
}
|
|
890
|
+
function countQuestionMarks(sql, end) {
|
|
891
|
+
let count = 0;
|
|
892
|
+
let inSingle = false;
|
|
893
|
+
let inDouble = false;
|
|
894
|
+
let inLineComment = false;
|
|
895
|
+
let inBlockComment = false;
|
|
896
|
+
for (let i = 0; i < end; i++) {
|
|
897
|
+
const ch = sql[i];
|
|
898
|
+
const next = sql[i + 1];
|
|
899
|
+
if (inLineComment) {
|
|
900
|
+
if (ch === "\n") inLineComment = false;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (inBlockComment) {
|
|
904
|
+
if (ch === "*" && next === "/") {
|
|
905
|
+
inBlockComment = false;
|
|
906
|
+
i += 1;
|
|
907
|
+
}
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
911
|
+
inLineComment = true;
|
|
912
|
+
i += 1;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
916
|
+
inBlockComment = true;
|
|
917
|
+
i += 1;
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
921
|
+
inSingle = !inSingle;
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
925
|
+
inDouble = !inDouble;
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
929
|
+
count += 1;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return count;
|
|
933
|
+
}
|
|
934
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
935
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
936
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
937
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
938
|
+
for (const match of sql.matchAll(pattern)) {
|
|
939
|
+
const matchText = match[0];
|
|
940
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
941
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return indexes;
|
|
945
|
+
}
|
|
946
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
947
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
948
|
+
if (!match) return;
|
|
949
|
+
const rawTable = match[1];
|
|
950
|
+
const rawColumns = match[2];
|
|
951
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
952
|
+
if (!boolColumns?.size) return;
|
|
953
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
954
|
+
for (const [index, column] of columns.entries()) {
|
|
955
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
956
|
+
args[index] = toBoolean(args[index]);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
961
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
962
|
+
if (!match) return;
|
|
963
|
+
const rawTable = match[1];
|
|
964
|
+
const setClause = match[2];
|
|
965
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
966
|
+
if (!boolColumns?.size) return;
|
|
967
|
+
const assignments = setClause.split(",");
|
|
968
|
+
let placeholderIndex = 0;
|
|
969
|
+
for (const assignment of assignments) {
|
|
970
|
+
if (!assignment.includes("?")) continue;
|
|
971
|
+
placeholderIndex += 1;
|
|
972
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
973
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
974
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function coerceBooleanArgs(sql, args) {
|
|
979
|
+
const nextArgs = [...args];
|
|
980
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
981
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
982
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
983
|
+
for (const index of placeholderIndexes) {
|
|
984
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
985
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return nextArgs;
|
|
989
|
+
}
|
|
990
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
991
|
+
let out = "";
|
|
992
|
+
let placeholder = 0;
|
|
993
|
+
let inSingle = false;
|
|
994
|
+
let inDouble = false;
|
|
995
|
+
let inLineComment = false;
|
|
996
|
+
let inBlockComment = false;
|
|
997
|
+
for (let i = 0; i < sql.length; i++) {
|
|
998
|
+
const ch = sql[i];
|
|
999
|
+
const next = sql[i + 1];
|
|
1000
|
+
if (inLineComment) {
|
|
1001
|
+
out += ch;
|
|
1002
|
+
if (ch === "\n") inLineComment = false;
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
if (inBlockComment) {
|
|
1006
|
+
out += ch;
|
|
1007
|
+
if (ch === "*" && next === "/") {
|
|
1008
|
+
out += next;
|
|
1009
|
+
inBlockComment = false;
|
|
1010
|
+
i += 1;
|
|
1011
|
+
}
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1015
|
+
out += ch + next;
|
|
1016
|
+
inLineComment = true;
|
|
1017
|
+
i += 1;
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1021
|
+
out += ch + next;
|
|
1022
|
+
inBlockComment = true;
|
|
1023
|
+
i += 1;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1027
|
+
inSingle = !inSingle;
|
|
1028
|
+
out += ch;
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1032
|
+
inDouble = !inDouble;
|
|
1033
|
+
out += ch;
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1037
|
+
placeholder += 1;
|
|
1038
|
+
out += `$${placeholder}`;
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
out += ch;
|
|
1042
|
+
}
|
|
1043
|
+
return out;
|
|
1044
|
+
}
|
|
1045
|
+
function translateStatementForPostgres(stmt) {
|
|
1046
|
+
const normalized = normalizeStatement(stmt);
|
|
1047
|
+
if (normalized.kind === "named") {
|
|
1048
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1049
|
+
}
|
|
1050
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1051
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1052
|
+
return {
|
|
1053
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1054
|
+
args: coercedArgs
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
function shouldBypassPostgres(stmt) {
|
|
1058
|
+
const normalized = normalizeStatement(stmt);
|
|
1059
|
+
if (normalized.kind === "named") {
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1063
|
+
}
|
|
1064
|
+
function shouldFallbackOnError(error) {
|
|
1065
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1066
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1067
|
+
}
|
|
1068
|
+
function isReadQuery(sql) {
|
|
1069
|
+
const trimmed = sql.trimStart();
|
|
1070
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1071
|
+
}
|
|
1072
|
+
function buildRow(row, columns) {
|
|
1073
|
+
const values = columns.map((column) => row[column]);
|
|
1074
|
+
return Object.assign(values, row);
|
|
1075
|
+
}
|
|
1076
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1077
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1078
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1079
|
+
return {
|
|
1080
|
+
columns,
|
|
1081
|
+
columnTypes: columns.map(() => ""),
|
|
1082
|
+
rows: resultRows,
|
|
1083
|
+
rowsAffected,
|
|
1084
|
+
lastInsertRowid: void 0,
|
|
1085
|
+
toJSON() {
|
|
1086
|
+
return {
|
|
1087
|
+
columns,
|
|
1088
|
+
columnTypes: columns.map(() => ""),
|
|
1089
|
+
rows,
|
|
1090
|
+
rowsAffected,
|
|
1091
|
+
lastInsertRowid: void 0
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
async function loadPrismaClient() {
|
|
1097
|
+
if (!prismaClientPromise) {
|
|
1098
|
+
prismaClientPromise = (async () => {
|
|
1099
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1100
|
+
if (explicitPath) {
|
|
1101
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1102
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1103
|
+
if (!PrismaClient2) {
|
|
1104
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1105
|
+
}
|
|
1106
|
+
return new PrismaClient2();
|
|
1107
|
+
}
|
|
1108
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
|
|
1109
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1110
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1111
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1112
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1113
|
+
if (!PrismaClient) {
|
|
1114
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1115
|
+
}
|
|
1116
|
+
return new PrismaClient();
|
|
1117
|
+
})();
|
|
1118
|
+
}
|
|
1119
|
+
return prismaClientPromise;
|
|
1120
|
+
}
|
|
1121
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1122
|
+
if (!compatibilityBootstrapPromise) {
|
|
1123
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1124
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1125
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1126
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1127
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1128
|
+
relation
|
|
1129
|
+
);
|
|
1130
|
+
if (!rows[0]?.regclass) {
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
await prisma.$executeRawUnsafe(
|
|
1134
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
})();
|
|
1138
|
+
}
|
|
1139
|
+
return compatibilityBootstrapPromise;
|
|
1140
|
+
}
|
|
1141
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1142
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1143
|
+
if (isReadQuery(translated.sql)) {
|
|
1144
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1145
|
+
translated.sql,
|
|
1146
|
+
...translated.args
|
|
1147
|
+
);
|
|
1148
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1149
|
+
}
|
|
1150
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1151
|
+
return buildResultSet([], rowsAffected);
|
|
1152
|
+
}
|
|
1153
|
+
function splitSqlStatements(sql) {
|
|
1154
|
+
const parts = [];
|
|
1155
|
+
let current = "";
|
|
1156
|
+
let inSingle = false;
|
|
1157
|
+
let inDouble = false;
|
|
1158
|
+
let inLineComment = false;
|
|
1159
|
+
let inBlockComment = false;
|
|
1160
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1161
|
+
const ch = sql[i];
|
|
1162
|
+
const next = sql[i + 1];
|
|
1163
|
+
if (inLineComment) {
|
|
1164
|
+
current += ch;
|
|
1165
|
+
if (ch === "\n") inLineComment = false;
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (inBlockComment) {
|
|
1169
|
+
current += ch;
|
|
1170
|
+
if (ch === "*" && next === "/") {
|
|
1171
|
+
current += next;
|
|
1172
|
+
inBlockComment = false;
|
|
1173
|
+
i += 1;
|
|
1174
|
+
}
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1178
|
+
current += ch + next;
|
|
1179
|
+
inLineComment = true;
|
|
1180
|
+
i += 1;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1184
|
+
current += ch + next;
|
|
1185
|
+
inBlockComment = true;
|
|
1186
|
+
i += 1;
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1190
|
+
inSingle = !inSingle;
|
|
1191
|
+
current += ch;
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1195
|
+
inDouble = !inDouble;
|
|
1196
|
+
current += ch;
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1200
|
+
if (current.trim()) {
|
|
1201
|
+
parts.push(current.trim());
|
|
1202
|
+
}
|
|
1203
|
+
current = "";
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
current += ch;
|
|
1207
|
+
}
|
|
1208
|
+
if (current.trim()) {
|
|
1209
|
+
parts.push(current.trim());
|
|
1210
|
+
}
|
|
1211
|
+
return parts;
|
|
1212
|
+
}
|
|
1213
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1214
|
+
const prisma = await loadPrismaClient();
|
|
1215
|
+
await ensureCompatibilityViews(prisma);
|
|
1216
|
+
let closed = false;
|
|
1217
|
+
let adapter;
|
|
1218
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1219
|
+
if (!fallbackClient) {
|
|
1220
|
+
if (error) throw error;
|
|
1221
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1222
|
+
}
|
|
1223
|
+
if (error) {
|
|
1224
|
+
process.stderr.write(
|
|
1225
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1226
|
+
`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
return fallbackClient.execute(stmt);
|
|
1230
|
+
};
|
|
1231
|
+
adapter = {
|
|
1232
|
+
async execute(stmt) {
|
|
1233
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1234
|
+
return fallbackExecute(stmt);
|
|
1235
|
+
}
|
|
1236
|
+
try {
|
|
1237
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
if (shouldFallbackOnError(error)) {
|
|
1240
|
+
return fallbackExecute(stmt, error);
|
|
1241
|
+
}
|
|
1242
|
+
throw error;
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
async batch(stmts, mode) {
|
|
1246
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1247
|
+
if (!fallbackClient) {
|
|
1248
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1249
|
+
}
|
|
1250
|
+
return fallbackClient.batch(stmts, mode);
|
|
1251
|
+
}
|
|
1252
|
+
try {
|
|
1253
|
+
if (prisma.$transaction) {
|
|
1254
|
+
return await prisma.$transaction(async (tx) => {
|
|
1255
|
+
const results2 = [];
|
|
1256
|
+
for (const stmt of stmts) {
|
|
1257
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1258
|
+
}
|
|
1259
|
+
return results2;
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
const results = [];
|
|
1263
|
+
for (const stmt of stmts) {
|
|
1264
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1265
|
+
}
|
|
1266
|
+
return results;
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1269
|
+
process.stderr.write(
|
|
1270
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1271
|
+
`
|
|
1272
|
+
);
|
|
1273
|
+
return fallbackClient.batch(stmts, mode);
|
|
1274
|
+
}
|
|
1275
|
+
throw error;
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
async migrate(stmts) {
|
|
1279
|
+
if (fallbackClient) {
|
|
1280
|
+
return fallbackClient.migrate(stmts);
|
|
1281
|
+
}
|
|
1282
|
+
return adapter.batch(stmts, "deferred");
|
|
1283
|
+
},
|
|
1284
|
+
async transaction(mode) {
|
|
1285
|
+
if (!fallbackClient) {
|
|
1286
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1287
|
+
}
|
|
1288
|
+
return fallbackClient.transaction(mode);
|
|
1289
|
+
},
|
|
1290
|
+
async executeMultiple(sql) {
|
|
1291
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1292
|
+
return fallbackClient.executeMultiple(sql);
|
|
1293
|
+
}
|
|
1294
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1295
|
+
await adapter.execute(statement);
|
|
1296
|
+
}
|
|
1297
|
+
},
|
|
1298
|
+
async sync() {
|
|
1299
|
+
if (fallbackClient) {
|
|
1300
|
+
return fallbackClient.sync();
|
|
1301
|
+
}
|
|
1302
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1303
|
+
},
|
|
1304
|
+
close() {
|
|
1305
|
+
closed = true;
|
|
1306
|
+
prismaClientPromise = null;
|
|
1307
|
+
compatibilityBootstrapPromise = null;
|
|
1308
|
+
void prisma.$disconnect?.();
|
|
1309
|
+
},
|
|
1310
|
+
get closed() {
|
|
1311
|
+
return closed;
|
|
1312
|
+
},
|
|
1313
|
+
get protocol() {
|
|
1314
|
+
return "prisma-postgres";
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
return adapter;
|
|
1318
|
+
}
|
|
1319
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1320
|
+
var init_database_adapter = __esm({
|
|
1321
|
+
"src/lib/database-adapter.ts"() {
|
|
1322
|
+
"use strict";
|
|
1323
|
+
VIEW_MAPPINGS = [
|
|
1324
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1325
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1326
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1327
|
+
{ view: "entities", source: "memory.entities" },
|
|
1328
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1329
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1330
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1331
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1332
|
+
{ view: "messages", source: "memory.messages" },
|
|
1333
|
+
{ view: "users", source: "wiki.users" },
|
|
1334
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1335
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1336
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1337
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1338
|
+
];
|
|
1339
|
+
UPSERT_KEYS = {
|
|
1340
|
+
memories: ["id"],
|
|
1341
|
+
tasks: ["id"],
|
|
1342
|
+
behaviors: ["id"],
|
|
1343
|
+
entities: ["id"],
|
|
1344
|
+
relationships: ["id"],
|
|
1345
|
+
entity_aliases: ["alias"],
|
|
1346
|
+
notifications: ["id"],
|
|
1347
|
+
messages: ["id"],
|
|
1348
|
+
users: ["id"],
|
|
1349
|
+
workspaces: ["id"],
|
|
1350
|
+
workspace_users: ["id"],
|
|
1351
|
+
documents: ["id"],
|
|
1352
|
+
chats: ["id"]
|
|
1353
|
+
};
|
|
1354
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1355
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1356
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1357
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1358
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1359
|
+
};
|
|
1360
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1361
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1362
|
+
);
|
|
1363
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1364
|
+
/\bPRAGMA\b/i,
|
|
1365
|
+
/\bsqlite_master\b/i,
|
|
1366
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1367
|
+
/\bMATCH\b/i,
|
|
1368
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1369
|
+
/\bjson_extract\s*\(/i,
|
|
1370
|
+
/\bjulianday\s*\(/i,
|
|
1371
|
+
/\bstrftime\s*\(/i,
|
|
1372
|
+
/\blast_insert_rowid\s*\(/i
|
|
1373
|
+
];
|
|
1374
|
+
prismaClientPromise = null;
|
|
1375
|
+
compatibilityBootstrapPromise = null;
|
|
791
1376
|
}
|
|
792
1377
|
});
|
|
793
1378
|
|
|
794
1379
|
// src/lib/exe-daemon-client.ts
|
|
795
1380
|
import net from "net";
|
|
796
|
-
import
|
|
1381
|
+
import os6 from "os";
|
|
797
1382
|
import { spawn } from "child_process";
|
|
798
1383
|
import { randomUUID } from "crypto";
|
|
799
1384
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
800
|
-
import
|
|
1385
|
+
import path7 from "path";
|
|
801
1386
|
import { fileURLToPath } from "url";
|
|
802
1387
|
function handleData(chunk) {
|
|
803
1388
|
_buffer += chunk.toString();
|
|
@@ -848,17 +1433,17 @@ function cleanupStaleFiles() {
|
|
|
848
1433
|
}
|
|
849
1434
|
}
|
|
850
1435
|
function findPackageRoot() {
|
|
851
|
-
let dir =
|
|
852
|
-
const { root } =
|
|
1436
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
1437
|
+
const { root } = path7.parse(dir);
|
|
853
1438
|
while (dir !== root) {
|
|
854
|
-
if (existsSync6(
|
|
855
|
-
dir =
|
|
1439
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
1440
|
+
dir = path7.dirname(dir);
|
|
856
1441
|
}
|
|
857
1442
|
return null;
|
|
858
1443
|
}
|
|
859
1444
|
function spawnDaemon() {
|
|
860
|
-
const freeGB =
|
|
861
|
-
const totalGB =
|
|
1445
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1446
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
862
1447
|
if (totalGB <= 8) {
|
|
863
1448
|
process.stderr.write(
|
|
864
1449
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -878,7 +1463,7 @@ function spawnDaemon() {
|
|
|
878
1463
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
879
1464
|
return;
|
|
880
1465
|
}
|
|
881
|
-
const daemonPath =
|
|
1466
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
882
1467
|
if (!existsSync6(daemonPath)) {
|
|
883
1468
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
884
1469
|
`);
|
|
@@ -887,7 +1472,7 @@ function spawnDaemon() {
|
|
|
887
1472
|
const resolvedPath = daemonPath;
|
|
888
1473
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
889
1474
|
`);
|
|
890
|
-
const logPath =
|
|
1475
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
891
1476
|
let stderrFd = "ignore";
|
|
892
1477
|
try {
|
|
893
1478
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1034,9 +1619,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1034
1619
|
"src/lib/exe-daemon-client.ts"() {
|
|
1035
1620
|
"use strict";
|
|
1036
1621
|
init_config();
|
|
1037
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1038
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1039
|
-
SPAWN_LOCK_PATH =
|
|
1622
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
1623
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
1624
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1040
1625
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1041
1626
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1042
1627
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1118,7 +1703,7 @@ __export(db_daemon_client_exports, {
|
|
|
1118
1703
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1119
1704
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1120
1705
|
});
|
|
1121
|
-
function
|
|
1706
|
+
function normalizeStatement2(stmt) {
|
|
1122
1707
|
if (typeof stmt === "string") {
|
|
1123
1708
|
return { sql: stmt, args: [] };
|
|
1124
1709
|
}
|
|
@@ -1142,7 +1727,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1142
1727
|
if (!_useDaemon || !isClientConnected()) {
|
|
1143
1728
|
return fallbackClient.execute(stmt);
|
|
1144
1729
|
}
|
|
1145
|
-
const { sql, args } =
|
|
1730
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1146
1731
|
const response = await sendDaemonRequest({
|
|
1147
1732
|
type: "db-execute",
|
|
1148
1733
|
sql,
|
|
@@ -1167,7 +1752,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1167
1752
|
if (!_useDaemon || !isClientConnected()) {
|
|
1168
1753
|
return fallbackClient.batch(stmts, mode);
|
|
1169
1754
|
}
|
|
1170
|
-
const statements = stmts.map(
|
|
1755
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1171
1756
|
const response = await sendDaemonRequest({
|
|
1172
1757
|
type: "db-batch",
|
|
1173
1758
|
statements,
|
|
@@ -1262,6 +1847,18 @@ __export(database_exports, {
|
|
|
1262
1847
|
});
|
|
1263
1848
|
import { createClient } from "@libsql/client";
|
|
1264
1849
|
async function initDatabase(config) {
|
|
1850
|
+
if (_walCheckpointTimer) {
|
|
1851
|
+
clearInterval(_walCheckpointTimer);
|
|
1852
|
+
_walCheckpointTimer = null;
|
|
1853
|
+
}
|
|
1854
|
+
if (_daemonClient) {
|
|
1855
|
+
_daemonClient.close();
|
|
1856
|
+
_daemonClient = null;
|
|
1857
|
+
}
|
|
1858
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1859
|
+
_adapterClient.close();
|
|
1860
|
+
}
|
|
1861
|
+
_adapterClient = null;
|
|
1265
1862
|
if (_client) {
|
|
1266
1863
|
_client.close();
|
|
1267
1864
|
_client = null;
|
|
@@ -1275,6 +1872,7 @@ async function initDatabase(config) {
|
|
|
1275
1872
|
}
|
|
1276
1873
|
_client = createClient(opts);
|
|
1277
1874
|
_resilientClient = wrapWithRetry(_client);
|
|
1875
|
+
_adapterClient = _resilientClient;
|
|
1278
1876
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1279
1877
|
});
|
|
1280
1878
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1285,14 +1883,20 @@ async function initDatabase(config) {
|
|
|
1285
1883
|
});
|
|
1286
1884
|
}, 3e4);
|
|
1287
1885
|
_walCheckpointTimer.unref();
|
|
1886
|
+
if (process.env.DATABASE_URL) {
|
|
1887
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1888
|
+
}
|
|
1288
1889
|
}
|
|
1289
1890
|
function isInitialized() {
|
|
1290
|
-
return _client !== null;
|
|
1891
|
+
return _adapterClient !== null || _client !== null;
|
|
1291
1892
|
}
|
|
1292
1893
|
function getClient() {
|
|
1293
|
-
if (!
|
|
1894
|
+
if (!_adapterClient) {
|
|
1294
1895
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1295
1896
|
}
|
|
1897
|
+
if (process.env.DATABASE_URL) {
|
|
1898
|
+
return _adapterClient;
|
|
1899
|
+
}
|
|
1296
1900
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1297
1901
|
return _resilientClient;
|
|
1298
1902
|
}
|
|
@@ -1302,6 +1906,7 @@ function getClient() {
|
|
|
1302
1906
|
return _resilientClient;
|
|
1303
1907
|
}
|
|
1304
1908
|
async function initDaemonClient() {
|
|
1909
|
+
if (process.env.DATABASE_URL) return;
|
|
1305
1910
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1306
1911
|
if (!_resilientClient) return;
|
|
1307
1912
|
try {
|
|
@@ -2246,26 +2851,36 @@ async function ensureSchema() {
|
|
|
2246
2851
|
}
|
|
2247
2852
|
}
|
|
2248
2853
|
async function disposeDatabase() {
|
|
2854
|
+
if (_walCheckpointTimer) {
|
|
2855
|
+
clearInterval(_walCheckpointTimer);
|
|
2856
|
+
_walCheckpointTimer = null;
|
|
2857
|
+
}
|
|
2249
2858
|
if (_daemonClient) {
|
|
2250
2859
|
_daemonClient.close();
|
|
2251
2860
|
_daemonClient = null;
|
|
2252
2861
|
}
|
|
2862
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2863
|
+
_adapterClient.close();
|
|
2864
|
+
}
|
|
2865
|
+
_adapterClient = null;
|
|
2253
2866
|
if (_client) {
|
|
2254
2867
|
_client.close();
|
|
2255
2868
|
_client = null;
|
|
2256
2869
|
_resilientClient = null;
|
|
2257
2870
|
}
|
|
2258
2871
|
}
|
|
2259
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2872
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2260
2873
|
var init_database = __esm({
|
|
2261
2874
|
"src/lib/database.ts"() {
|
|
2262
2875
|
"use strict";
|
|
2263
2876
|
init_db_retry();
|
|
2264
2877
|
init_employees();
|
|
2878
|
+
init_database_adapter();
|
|
2265
2879
|
_client = null;
|
|
2266
2880
|
_resilientClient = null;
|
|
2267
2881
|
_walCheckpointTimer = null;
|
|
2268
2882
|
_daemonClient = null;
|
|
2883
|
+
_adapterClient = null;
|
|
2269
2884
|
initTurso = initDatabase;
|
|
2270
2885
|
disposeTurso = disposeDatabase;
|
|
2271
2886
|
}
|
|
@@ -2274,16 +2889,16 @@ var init_database = __esm({
|
|
|
2274
2889
|
// src/lib/license.ts
|
|
2275
2890
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2276
2891
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2277
|
-
import
|
|
2892
|
+
import path8 from "path";
|
|
2278
2893
|
import { jwtVerify, importSPKI } from "jose";
|
|
2279
2894
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2280
2895
|
var init_license = __esm({
|
|
2281
2896
|
"src/lib/license.ts"() {
|
|
2282
2897
|
"use strict";
|
|
2283
2898
|
init_config();
|
|
2284
|
-
LICENSE_PATH =
|
|
2285
|
-
CACHE_PATH =
|
|
2286
|
-
DEVICE_ID_PATH =
|
|
2899
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2900
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2901
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2287
2902
|
PLAN_LIMITS = {
|
|
2288
2903
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2289
2904
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2296,7 +2911,7 @@ var init_license = __esm({
|
|
|
2296
2911
|
|
|
2297
2912
|
// src/lib/plan-limits.ts
|
|
2298
2913
|
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2299
|
-
import
|
|
2914
|
+
import path9 from "path";
|
|
2300
2915
|
function getLicenseSync() {
|
|
2301
2916
|
try {
|
|
2302
2917
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
@@ -2368,14 +2983,14 @@ var init_plan_limits = __esm({
|
|
|
2368
2983
|
this.name = "PlanLimitError";
|
|
2369
2984
|
}
|
|
2370
2985
|
};
|
|
2371
|
-
CACHE_PATH2 =
|
|
2986
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2372
2987
|
}
|
|
2373
2988
|
});
|
|
2374
2989
|
|
|
2375
2990
|
// src/lib/notifications.ts
|
|
2376
2991
|
import crypto from "crypto";
|
|
2377
|
-
import
|
|
2378
|
-
import
|
|
2992
|
+
import path10 from "path";
|
|
2993
|
+
import os7 from "os";
|
|
2379
2994
|
import {
|
|
2380
2995
|
readFileSync as readFileSync9,
|
|
2381
2996
|
readdirSync,
|
|
@@ -2531,8 +3146,8 @@ __export(tasks_crud_exports, {
|
|
|
2531
3146
|
writeCheckpoint: () => writeCheckpoint
|
|
2532
3147
|
});
|
|
2533
3148
|
import crypto3 from "crypto";
|
|
2534
|
-
import
|
|
2535
|
-
import
|
|
3149
|
+
import path11 from "path";
|
|
3150
|
+
import os8 from "os";
|
|
2536
3151
|
import { execSync as execSync4 } from "child_process";
|
|
2537
3152
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2538
3153
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
@@ -2710,8 +3325,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2710
3325
|
}
|
|
2711
3326
|
if (input.baseDir) {
|
|
2712
3327
|
try {
|
|
2713
|
-
await mkdir3(
|
|
2714
|
-
await mkdir3(
|
|
3328
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3329
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2715
3330
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2716
3331
|
await ensureGitignoreExe(input.baseDir);
|
|
2717
3332
|
} catch {
|
|
@@ -2747,9 +3362,9 @@ ${laneWarning}` : laneWarning;
|
|
|
2747
3362
|
});
|
|
2748
3363
|
if (input.baseDir) {
|
|
2749
3364
|
try {
|
|
2750
|
-
const EXE_OS_DIR =
|
|
2751
|
-
const mdPath =
|
|
2752
|
-
const mdDir =
|
|
3365
|
+
const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
|
|
3366
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
3367
|
+
const mdDir = path11.dirname(mdPath);
|
|
2753
3368
|
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2754
3369
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2755
3370
|
const mdContent = `# ${input.title}
|
|
@@ -3050,7 +3665,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3050
3665
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3051
3666
|
}
|
|
3052
3667
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3053
|
-
const archPath =
|
|
3668
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3054
3669
|
try {
|
|
3055
3670
|
if (existsSync10(archPath)) return;
|
|
3056
3671
|
const template = [
|
|
@@ -3085,7 +3700,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3085
3700
|
}
|
|
3086
3701
|
}
|
|
3087
3702
|
async function ensureGitignoreExe(baseDir) {
|
|
3088
|
-
const gitignorePath =
|
|
3703
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
3089
3704
|
try {
|
|
3090
3705
|
if (existsSync10(gitignorePath)) {
|
|
3091
3706
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -3119,13 +3734,13 @@ var init_tasks_crud = __esm({
|
|
|
3119
3734
|
});
|
|
3120
3735
|
|
|
3121
3736
|
// src/lib/tasks-review.ts
|
|
3122
|
-
import
|
|
3737
|
+
import path12 from "path";
|
|
3123
3738
|
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3124
3739
|
async function countPendingReviews(sessionScope) {
|
|
3125
3740
|
const client = getClient();
|
|
3126
3741
|
if (sessionScope) {
|
|
3127
3742
|
const result2 = await client.execute({
|
|
3128
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
3743
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3129
3744
|
args: [sessionScope]
|
|
3130
3745
|
});
|
|
3131
3746
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3301,11 +3916,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3301
3916
|
);
|
|
3302
3917
|
}
|
|
3303
3918
|
try {
|
|
3304
|
-
const cacheDir =
|
|
3919
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3305
3920
|
if (existsSync11(cacheDir)) {
|
|
3306
3921
|
for (const f of readdirSync2(cacheDir)) {
|
|
3307
3922
|
if (f.startsWith("review-notified-")) {
|
|
3308
|
-
unlinkSync4(
|
|
3923
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
3309
3924
|
}
|
|
3310
3925
|
}
|
|
3311
3926
|
}
|
|
@@ -3326,7 +3941,7 @@ var init_tasks_review = __esm({
|
|
|
3326
3941
|
});
|
|
3327
3942
|
|
|
3328
3943
|
// src/lib/tasks-chain.ts
|
|
3329
|
-
import
|
|
3944
|
+
import path13 from "path";
|
|
3330
3945
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3331
3946
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3332
3947
|
const client = getClient();
|
|
@@ -3343,7 +3958,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3343
3958
|
});
|
|
3344
3959
|
for (const ur of unblockedRows.rows) {
|
|
3345
3960
|
try {
|
|
3346
|
-
const ubFile =
|
|
3961
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3347
3962
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3348
3963
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3349
3964
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3412,7 +4027,7 @@ var init_tasks_chain = __esm({
|
|
|
3412
4027
|
|
|
3413
4028
|
// src/lib/project-name.ts
|
|
3414
4029
|
import { execSync as execSync5 } from "child_process";
|
|
3415
|
-
import
|
|
4030
|
+
import path14 from "path";
|
|
3416
4031
|
function getProjectName(cwd) {
|
|
3417
4032
|
const dir = cwd ?? process.cwd();
|
|
3418
4033
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3425,7 +4040,7 @@ function getProjectName(cwd) {
|
|
|
3425
4040
|
timeout: 2e3,
|
|
3426
4041
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3427
4042
|
}).trim();
|
|
3428
|
-
repoRoot =
|
|
4043
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
3429
4044
|
} catch {
|
|
3430
4045
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
3431
4046
|
cwd: dir,
|
|
@@ -3434,11 +4049,11 @@ function getProjectName(cwd) {
|
|
|
3434
4049
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3435
4050
|
}).trim();
|
|
3436
4051
|
}
|
|
3437
|
-
_cached2 =
|
|
4052
|
+
_cached2 = path14.basename(repoRoot);
|
|
3438
4053
|
_cachedCwd = dir;
|
|
3439
4054
|
return _cached2;
|
|
3440
4055
|
} catch {
|
|
3441
|
-
_cached2 =
|
|
4056
|
+
_cached2 = path14.basename(dir);
|
|
3442
4057
|
_cachedCwd = dir;
|
|
3443
4058
|
return _cached2;
|
|
3444
4059
|
}
|
|
@@ -3911,7 +4526,7 @@ __export(tasks_exports, {
|
|
|
3911
4526
|
updateTaskStatus: () => updateTaskStatus,
|
|
3912
4527
|
writeCheckpoint: () => writeCheckpoint
|
|
3913
4528
|
});
|
|
3914
|
-
import
|
|
4529
|
+
import path15 from "path";
|
|
3915
4530
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3916
4531
|
async function createTask(input) {
|
|
3917
4532
|
const result = await createTaskCore(input);
|
|
@@ -3931,8 +4546,8 @@ async function updateTask(input) {
|
|
|
3931
4546
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3932
4547
|
try {
|
|
3933
4548
|
const agent = String(row.assigned_to);
|
|
3934
|
-
const cacheDir =
|
|
3935
|
-
const cachePath =
|
|
4549
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
4550
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3936
4551
|
if (input.status === "in_progress") {
|
|
3937
4552
|
mkdirSync5(cacheDir, { recursive: true });
|
|
3938
4553
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -4403,12 +5018,12 @@ __export(tmux_routing_exports, {
|
|
|
4403
5018
|
});
|
|
4404
5019
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
4405
5020
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
4406
|
-
import
|
|
4407
|
-
import
|
|
5021
|
+
import path16 from "path";
|
|
5022
|
+
import os9 from "os";
|
|
4408
5023
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4409
5024
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4410
5025
|
function spawnLockPath(sessionName) {
|
|
4411
|
-
return
|
|
5026
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4412
5027
|
}
|
|
4413
5028
|
function isProcessAlive(pid) {
|
|
4414
5029
|
try {
|
|
@@ -4445,8 +5060,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4445
5060
|
function resolveBehaviorsExporterScript() {
|
|
4446
5061
|
try {
|
|
4447
5062
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4448
|
-
const scriptPath =
|
|
4449
|
-
|
|
5063
|
+
const scriptPath = path16.join(
|
|
5064
|
+
path16.dirname(thisFile),
|
|
4450
5065
|
"..",
|
|
4451
5066
|
"bin",
|
|
4452
5067
|
"exe-export-behaviors.js"
|
|
@@ -4521,7 +5136,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4521
5136
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4522
5137
|
}
|
|
4523
5138
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4524
|
-
const filePath =
|
|
5139
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4525
5140
|
writeFileSync7(filePath, JSON.stringify({
|
|
4526
5141
|
parentExe: rootExe,
|
|
4527
5142
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -4530,7 +5145,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4530
5145
|
}
|
|
4531
5146
|
function getParentExe(sessionKey) {
|
|
4532
5147
|
try {
|
|
4533
|
-
const data = JSON.parse(readFileSync11(
|
|
5148
|
+
const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4534
5149
|
return data.parentExe || null;
|
|
4535
5150
|
} catch {
|
|
4536
5151
|
return null;
|
|
@@ -4539,7 +5154,7 @@ function getParentExe(sessionKey) {
|
|
|
4539
5154
|
function getDispatchedBy(sessionKey) {
|
|
4540
5155
|
try {
|
|
4541
5156
|
const data = JSON.parse(readFileSync11(
|
|
4542
|
-
|
|
5157
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4543
5158
|
"utf8"
|
|
4544
5159
|
));
|
|
4545
5160
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4725,7 +5340,7 @@ function sendIntercom(targetSession) {
|
|
|
4725
5340
|
try {
|
|
4726
5341
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4727
5342
|
const agent = baseAgentName(rawAgent);
|
|
4728
|
-
const markerPath =
|
|
5343
|
+
const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4729
5344
|
if (existsSync12(markerPath)) {
|
|
4730
5345
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4731
5346
|
return "debounced";
|
|
@@ -4735,7 +5350,7 @@ function sendIntercom(targetSession) {
|
|
|
4735
5350
|
try {
|
|
4736
5351
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4737
5352
|
const agent = baseAgentName(rawAgent);
|
|
4738
|
-
const taskDir =
|
|
5353
|
+
const taskDir = path16.join(process.cwd(), "exe", agent);
|
|
4739
5354
|
if (existsSync12(taskDir)) {
|
|
4740
5355
|
const files = readdirSync3(taskDir).filter(
|
|
4741
5356
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4869,8 +5484,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4869
5484
|
const transport = getTransport();
|
|
4870
5485
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4871
5486
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4872
|
-
const logDir =
|
|
4873
|
-
const logFile =
|
|
5487
|
+
const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5488
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4874
5489
|
if (!existsSync12(logDir)) {
|
|
4875
5490
|
mkdirSync6(logDir, { recursive: true });
|
|
4876
5491
|
}
|
|
@@ -4878,14 +5493,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4878
5493
|
let cleanupSuffix = "";
|
|
4879
5494
|
try {
|
|
4880
5495
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4881
|
-
const cleanupScript =
|
|
5496
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4882
5497
|
if (existsSync12(cleanupScript)) {
|
|
4883
5498
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4884
5499
|
}
|
|
4885
5500
|
} catch {
|
|
4886
5501
|
}
|
|
4887
5502
|
try {
|
|
4888
|
-
const claudeJsonPath =
|
|
5503
|
+
const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
|
|
4889
5504
|
let claudeJson = {};
|
|
4890
5505
|
try {
|
|
4891
5506
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -4900,10 +5515,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4900
5515
|
} catch {
|
|
4901
5516
|
}
|
|
4902
5517
|
try {
|
|
4903
|
-
const settingsDir =
|
|
5518
|
+
const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
|
|
4904
5519
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4905
|
-
const projSettingsDir =
|
|
4906
|
-
const settingsPath =
|
|
5520
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
5521
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4907
5522
|
let settings = {};
|
|
4908
5523
|
try {
|
|
4909
5524
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -4950,8 +5565,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4950
5565
|
let behaviorsFlag = "";
|
|
4951
5566
|
let legacyFallbackWarned = false;
|
|
4952
5567
|
if (!useExeAgent && !useBinSymlink) {
|
|
4953
|
-
const identityPath =
|
|
4954
|
-
|
|
5568
|
+
const identityPath = path16.join(
|
|
5569
|
+
os9.homedir(),
|
|
4955
5570
|
".exe-os",
|
|
4956
5571
|
"identity",
|
|
4957
5572
|
`${employeeName}.md`
|
|
@@ -4966,7 +5581,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4966
5581
|
}
|
|
4967
5582
|
const behaviorsFile = exportBehaviorsSync(
|
|
4968
5583
|
employeeName,
|
|
4969
|
-
|
|
5584
|
+
path16.basename(spawnCwd),
|
|
4970
5585
|
sessionName
|
|
4971
5586
|
);
|
|
4972
5587
|
if (behaviorsFile) {
|
|
@@ -4981,9 +5596,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4981
5596
|
}
|
|
4982
5597
|
let sessionContextFlag = "";
|
|
4983
5598
|
try {
|
|
4984
|
-
const ctxDir =
|
|
5599
|
+
const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4985
5600
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4986
|
-
const ctxFile =
|
|
5601
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4987
5602
|
const ctxContent = [
|
|
4988
5603
|
`## Session Context`,
|
|
4989
5604
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5067,7 +5682,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5067
5682
|
transport.pipeLog(sessionName, logFile);
|
|
5068
5683
|
try {
|
|
5069
5684
|
const mySession = getMySession();
|
|
5070
|
-
const dispatchInfo =
|
|
5685
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5071
5686
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5072
5687
|
dispatchedBy: mySession,
|
|
5073
5688
|
rootExe: exeSession,
|
|
@@ -5142,15 +5757,15 @@ var init_tmux_routing = __esm({
|
|
|
5142
5757
|
init_intercom_queue();
|
|
5143
5758
|
init_plan_limits();
|
|
5144
5759
|
init_employees();
|
|
5145
|
-
SPAWN_LOCK_DIR =
|
|
5146
|
-
SESSION_CACHE =
|
|
5760
|
+
SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5761
|
+
SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5147
5762
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5148
5763
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5149
5764
|
VERIFY_PANE_LINES = 200;
|
|
5150
5765
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5151
5766
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5152
|
-
INTERCOM_LOG2 =
|
|
5153
|
-
DEBOUNCE_FILE =
|
|
5767
|
+
INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5768
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5154
5769
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5155
5770
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5156
5771
|
}
|
|
@@ -5192,13 +5807,13 @@ var init_memory = __esm({
|
|
|
5192
5807
|
// src/lib/keychain.ts
|
|
5193
5808
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5194
5809
|
import { existsSync as existsSync13 } from "fs";
|
|
5195
|
-
import
|
|
5196
|
-
import
|
|
5810
|
+
import path17 from "path";
|
|
5811
|
+
import os10 from "os";
|
|
5197
5812
|
function getKeyDir() {
|
|
5198
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5813
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os10.homedir(), ".exe-os");
|
|
5199
5814
|
}
|
|
5200
5815
|
function getKeyPath() {
|
|
5201
|
-
return
|
|
5816
|
+
return path17.join(getKeyDir(), "master.key");
|
|
5202
5817
|
}
|
|
5203
5818
|
async function tryKeytar() {
|
|
5204
5819
|
try {
|
|
@@ -5221,7 +5836,7 @@ async function getMasterKey() {
|
|
|
5221
5836
|
const keyPath = getKeyPath();
|
|
5222
5837
|
if (!existsSync13(keyPath)) {
|
|
5223
5838
|
process.stderr.write(
|
|
5224
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
5839
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5225
5840
|
`
|
|
5226
5841
|
);
|
|
5227
5842
|
return null;
|
|
@@ -5259,7 +5874,7 @@ __export(shard_manager_exports, {
|
|
|
5259
5874
|
listShards: () => listShards,
|
|
5260
5875
|
shardExists: () => shardExists
|
|
5261
5876
|
});
|
|
5262
|
-
import
|
|
5877
|
+
import path18 from "path";
|
|
5263
5878
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
5264
5879
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5265
5880
|
function initShardManager(encryptionKey) {
|
|
@@ -5285,7 +5900,7 @@ function getShardClient(projectName) {
|
|
|
5285
5900
|
}
|
|
5286
5901
|
const cached = _shards.get(safeName);
|
|
5287
5902
|
if (cached) return cached;
|
|
5288
|
-
const dbPath =
|
|
5903
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
5289
5904
|
const client = createClient2({
|
|
5290
5905
|
url: `file:${dbPath}`,
|
|
5291
5906
|
encryptionKey: _encryptionKey
|
|
@@ -5295,7 +5910,7 @@ function getShardClient(projectName) {
|
|
|
5295
5910
|
}
|
|
5296
5911
|
function shardExists(projectName) {
|
|
5297
5912
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5298
|
-
return existsSync14(
|
|
5913
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
5299
5914
|
}
|
|
5300
5915
|
function listShards() {
|
|
5301
5916
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
@@ -5372,7 +5987,23 @@ async function ensureShardSchema(client) {
|
|
|
5372
5987
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
5373
5988
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
5374
5989
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
5375
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
5990
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
5991
|
+
// Metadata enrichment columns (must match database.ts)
|
|
5992
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
5993
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
5994
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
5995
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
5996
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
5997
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
5998
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
5999
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
6000
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
6001
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
6002
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
6003
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
6004
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
6005
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
6006
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
5376
6007
|
]) {
|
|
5377
6008
|
try {
|
|
5378
6009
|
await client.execute(col);
|
|
@@ -5484,7 +6115,7 @@ var init_shard_manager = __esm({
|
|
|
5484
6115
|
"src/lib/shard-manager.ts"() {
|
|
5485
6116
|
"use strict";
|
|
5486
6117
|
init_config();
|
|
5487
|
-
SHARDS_DIR =
|
|
6118
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5488
6119
|
_shards = /* @__PURE__ */ new Map();
|
|
5489
6120
|
_encryptionKey = null;
|
|
5490
6121
|
_shardingEnabled = false;
|