@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
|
@@ -323,7 +323,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
323
323
|
if (!emp) return false;
|
|
324
324
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
325
325
|
}
|
|
326
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
326
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
327
327
|
var init_employees = __esm({
|
|
328
328
|
"src/lib/employees.ts"() {
|
|
329
329
|
"use strict";
|
|
@@ -332,6 +332,7 @@ var init_employees = __esm({
|
|
|
332
332
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
333
333
|
COORDINATOR_ROLE = "COO";
|
|
334
334
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
335
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
335
336
|
}
|
|
336
337
|
});
|
|
337
338
|
|
|
@@ -791,13 +792,597 @@ var init_db_retry = __esm({
|
|
|
791
792
|
}
|
|
792
793
|
});
|
|
793
794
|
|
|
795
|
+
// src/lib/database-adapter.ts
|
|
796
|
+
import os5 from "os";
|
|
797
|
+
import path7 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 ?? path7.join(os5.homedir(), "exe-db");
|
|
1109
|
+
const requireFromExeDb = createRequire(path7.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;
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
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 unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
800
|
-
import
|
|
1385
|
+
import path8 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 = path8.dirname(fileURLToPath(import.meta.url));
|
|
1437
|
+
const { root } = path8.parse(dir);
|
|
853
1438
|
while (dir !== root) {
|
|
854
|
-
if (existsSync6(
|
|
855
|
-
dir =
|
|
1439
|
+
if (existsSync6(path8.join(dir, "package.json"))) return dir;
|
|
1440
|
+
dir = path8.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 = path8.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 = path8.join(path8.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 ?? path8.join(EXE_AI_DIR, "exed.sock");
|
|
1623
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
|
|
1624
|
+
SPAWN_LOCK_PATH = path8.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 readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
2276
2891
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2277
|
-
import
|
|
2892
|
+
import path9 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 = path9.join(EXE_AI_DIR, "license.key");
|
|
2900
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2901
|
+
DEVICE_ID_PATH = path9.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 readFileSync9, existsSync as existsSync8 } from "fs";
|
|
2299
|
-
import
|
|
2914
|
+
import path10 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 = path10.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 path11 from "path";
|
|
2993
|
+
import os7 from "os";
|
|
2379
2994
|
import {
|
|
2380
2995
|
readFileSync as readFileSync10,
|
|
2381
2996
|
readdirSync as readdirSync2,
|
|
@@ -2515,8 +3130,8 @@ var init_state_bus = __esm({
|
|
|
2515
3130
|
|
|
2516
3131
|
// src/lib/tasks-crud.ts
|
|
2517
3132
|
import crypto3 from "crypto";
|
|
2518
|
-
import
|
|
2519
|
-
import
|
|
3133
|
+
import path12 from "path";
|
|
3134
|
+
import os8 from "os";
|
|
2520
3135
|
import { execSync as execSync5 } from "child_process";
|
|
2521
3136
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2522
3137
|
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
@@ -2694,8 +3309,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2694
3309
|
}
|
|
2695
3310
|
if (input2.baseDir) {
|
|
2696
3311
|
try {
|
|
2697
|
-
await mkdir3(
|
|
2698
|
-
await mkdir3(
|
|
3312
|
+
await mkdir3(path12.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
3313
|
+
await mkdir3(path12.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
2699
3314
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
2700
3315
|
await ensureGitignoreExe(input2.baseDir);
|
|
2701
3316
|
} catch {
|
|
@@ -2731,9 +3346,9 @@ ${laneWarning}` : laneWarning;
|
|
|
2731
3346
|
});
|
|
2732
3347
|
if (input2.baseDir) {
|
|
2733
3348
|
try {
|
|
2734
|
-
const EXE_OS_DIR =
|
|
2735
|
-
const mdPath =
|
|
2736
|
-
const mdDir =
|
|
3349
|
+
const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
|
|
3350
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
3351
|
+
const mdDir = path12.dirname(mdPath);
|
|
2737
3352
|
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2738
3353
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
2739
3354
|
const mdContent = `# ${input2.title}
|
|
@@ -3034,7 +3649,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3034
3649
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3035
3650
|
}
|
|
3036
3651
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3037
|
-
const archPath =
|
|
3652
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3038
3653
|
try {
|
|
3039
3654
|
if (existsSync10(archPath)) return;
|
|
3040
3655
|
const template = [
|
|
@@ -3069,7 +3684,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3069
3684
|
}
|
|
3070
3685
|
}
|
|
3071
3686
|
async function ensureGitignoreExe(baseDir) {
|
|
3072
|
-
const gitignorePath =
|
|
3687
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
3073
3688
|
try {
|
|
3074
3689
|
if (existsSync10(gitignorePath)) {
|
|
3075
3690
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -3103,13 +3718,13 @@ var init_tasks_crud = __esm({
|
|
|
3103
3718
|
});
|
|
3104
3719
|
|
|
3105
3720
|
// src/lib/tasks-review.ts
|
|
3106
|
-
import
|
|
3721
|
+
import path13 from "path";
|
|
3107
3722
|
import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
3108
3723
|
async function countPendingReviews(sessionScope) {
|
|
3109
3724
|
const client = getClient();
|
|
3110
3725
|
if (sessionScope) {
|
|
3111
3726
|
const result2 = await client.execute({
|
|
3112
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
3727
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3113
3728
|
args: [sessionScope]
|
|
3114
3729
|
});
|
|
3115
3730
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3285,11 +3900,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3285
3900
|
);
|
|
3286
3901
|
}
|
|
3287
3902
|
try {
|
|
3288
|
-
const cacheDir =
|
|
3903
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
3289
3904
|
if (existsSync11(cacheDir)) {
|
|
3290
3905
|
for (const f of readdirSync3(cacheDir)) {
|
|
3291
3906
|
if (f.startsWith("review-notified-")) {
|
|
3292
|
-
unlinkSync5(
|
|
3907
|
+
unlinkSync5(path13.join(cacheDir, f));
|
|
3293
3908
|
}
|
|
3294
3909
|
}
|
|
3295
3910
|
}
|
|
@@ -3310,7 +3925,7 @@ var init_tasks_review = __esm({
|
|
|
3310
3925
|
});
|
|
3311
3926
|
|
|
3312
3927
|
// src/lib/tasks-chain.ts
|
|
3313
|
-
import
|
|
3928
|
+
import path14 from "path";
|
|
3314
3929
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3315
3930
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3316
3931
|
const client = getClient();
|
|
@@ -3327,7 +3942,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3327
3942
|
});
|
|
3328
3943
|
for (const ur of unblockedRows.rows) {
|
|
3329
3944
|
try {
|
|
3330
|
-
const ubFile =
|
|
3945
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
3331
3946
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3332
3947
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3333
3948
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3396,7 +4011,7 @@ var init_tasks_chain = __esm({
|
|
|
3396
4011
|
|
|
3397
4012
|
// src/lib/project-name.ts
|
|
3398
4013
|
import { execSync as execSync6 } from "child_process";
|
|
3399
|
-
import
|
|
4014
|
+
import path15 from "path";
|
|
3400
4015
|
function getProjectName(cwd) {
|
|
3401
4016
|
const dir = cwd ?? process.cwd();
|
|
3402
4017
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3409,7 +4024,7 @@ function getProjectName(cwd) {
|
|
|
3409
4024
|
timeout: 2e3,
|
|
3410
4025
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3411
4026
|
}).trim();
|
|
3412
|
-
repoRoot =
|
|
4027
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
3413
4028
|
} catch {
|
|
3414
4029
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3415
4030
|
cwd: dir,
|
|
@@ -3418,11 +4033,11 @@ function getProjectName(cwd) {
|
|
|
3418
4033
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3419
4034
|
}).trim();
|
|
3420
4035
|
}
|
|
3421
|
-
_cached2 =
|
|
4036
|
+
_cached2 = path15.basename(repoRoot);
|
|
3422
4037
|
_cachedCwd = dir;
|
|
3423
4038
|
return _cached2;
|
|
3424
4039
|
} catch {
|
|
3425
|
-
_cached2 =
|
|
4040
|
+
_cached2 = path15.basename(dir);
|
|
3426
4041
|
_cachedCwd = dir;
|
|
3427
4042
|
return _cached2;
|
|
3428
4043
|
}
|
|
@@ -3895,7 +4510,7 @@ __export(tasks_exports, {
|
|
|
3895
4510
|
updateTaskStatus: () => updateTaskStatus,
|
|
3896
4511
|
writeCheckpoint: () => writeCheckpoint
|
|
3897
4512
|
});
|
|
3898
|
-
import
|
|
4513
|
+
import path16 from "path";
|
|
3899
4514
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
3900
4515
|
async function createTask(input2) {
|
|
3901
4516
|
const result = await createTaskCore(input2);
|
|
@@ -3915,8 +4530,8 @@ async function updateTask(input2) {
|
|
|
3915
4530
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
3916
4531
|
try {
|
|
3917
4532
|
const agent = String(row.assigned_to);
|
|
3918
|
-
const cacheDir =
|
|
3919
|
-
const cachePath =
|
|
4533
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
4534
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
3920
4535
|
if (input2.status === "in_progress") {
|
|
3921
4536
|
mkdirSync6(cacheDir, { recursive: true });
|
|
3922
4537
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -4387,12 +5002,12 @@ __export(tmux_routing_exports, {
|
|
|
4387
5002
|
});
|
|
4388
5003
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4389
5004
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
4390
|
-
import
|
|
4391
|
-
import
|
|
5005
|
+
import path17 from "path";
|
|
5006
|
+
import os9 from "os";
|
|
4392
5007
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4393
5008
|
import { unlinkSync as unlinkSync7 } from "fs";
|
|
4394
5009
|
function spawnLockPath(sessionName) {
|
|
4395
|
-
return
|
|
5010
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4396
5011
|
}
|
|
4397
5012
|
function isProcessAlive(pid) {
|
|
4398
5013
|
try {
|
|
@@ -4429,8 +5044,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4429
5044
|
function resolveBehaviorsExporterScript() {
|
|
4430
5045
|
try {
|
|
4431
5046
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4432
|
-
const scriptPath =
|
|
4433
|
-
|
|
5047
|
+
const scriptPath = path17.join(
|
|
5048
|
+
path17.dirname(thisFile),
|
|
4434
5049
|
"..",
|
|
4435
5050
|
"bin",
|
|
4436
5051
|
"exe-export-behaviors.js"
|
|
@@ -4505,7 +5120,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4505
5120
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4506
5121
|
}
|
|
4507
5122
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4508
|
-
const filePath =
|
|
5123
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4509
5124
|
writeFileSync8(filePath, JSON.stringify({
|
|
4510
5125
|
parentExe: rootExe,
|
|
4511
5126
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -4514,7 +5129,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4514
5129
|
}
|
|
4515
5130
|
function getParentExe(sessionKey) {
|
|
4516
5131
|
try {
|
|
4517
|
-
const data = JSON.parse(readFileSync12(
|
|
5132
|
+
const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4518
5133
|
return data.parentExe || null;
|
|
4519
5134
|
} catch {
|
|
4520
5135
|
return null;
|
|
@@ -4523,7 +5138,7 @@ function getParentExe(sessionKey) {
|
|
|
4523
5138
|
function getDispatchedBy(sessionKey) {
|
|
4524
5139
|
try {
|
|
4525
5140
|
const data = JSON.parse(readFileSync12(
|
|
4526
|
-
|
|
5141
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4527
5142
|
"utf8"
|
|
4528
5143
|
));
|
|
4529
5144
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4709,7 +5324,7 @@ function sendIntercom(targetSession) {
|
|
|
4709
5324
|
try {
|
|
4710
5325
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4711
5326
|
const agent = baseAgentName(rawAgent);
|
|
4712
|
-
const markerPath =
|
|
5327
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4713
5328
|
if (existsSync12(markerPath)) {
|
|
4714
5329
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4715
5330
|
return "debounced";
|
|
@@ -4719,7 +5334,7 @@ function sendIntercom(targetSession) {
|
|
|
4719
5334
|
try {
|
|
4720
5335
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4721
5336
|
const agent = baseAgentName(rawAgent);
|
|
4722
|
-
const taskDir =
|
|
5337
|
+
const taskDir = path17.join(process.cwd(), "exe", agent);
|
|
4723
5338
|
if (existsSync12(taskDir)) {
|
|
4724
5339
|
const files = readdirSync4(taskDir).filter(
|
|
4725
5340
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4853,8 +5468,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4853
5468
|
const transport = getTransport();
|
|
4854
5469
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4855
5470
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4856
|
-
const logDir =
|
|
4857
|
-
const logFile =
|
|
5471
|
+
const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5472
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4858
5473
|
if (!existsSync12(logDir)) {
|
|
4859
5474
|
mkdirSync7(logDir, { recursive: true });
|
|
4860
5475
|
}
|
|
@@ -4862,14 +5477,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4862
5477
|
let cleanupSuffix = "";
|
|
4863
5478
|
try {
|
|
4864
5479
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4865
|
-
const cleanupScript =
|
|
5480
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4866
5481
|
if (existsSync12(cleanupScript)) {
|
|
4867
5482
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4868
5483
|
}
|
|
4869
5484
|
} catch {
|
|
4870
5485
|
}
|
|
4871
5486
|
try {
|
|
4872
|
-
const claudeJsonPath =
|
|
5487
|
+
const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
|
|
4873
5488
|
let claudeJson = {};
|
|
4874
5489
|
try {
|
|
4875
5490
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -4884,10 +5499,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4884
5499
|
} catch {
|
|
4885
5500
|
}
|
|
4886
5501
|
try {
|
|
4887
|
-
const settingsDir =
|
|
5502
|
+
const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
|
|
4888
5503
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4889
|
-
const projSettingsDir =
|
|
4890
|
-
const settingsPath =
|
|
5504
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
5505
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
4891
5506
|
let settings = {};
|
|
4892
5507
|
try {
|
|
4893
5508
|
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
@@ -4934,8 +5549,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4934
5549
|
let behaviorsFlag = "";
|
|
4935
5550
|
let legacyFallbackWarned = false;
|
|
4936
5551
|
if (!useExeAgent && !useBinSymlink) {
|
|
4937
|
-
const identityPath =
|
|
4938
|
-
|
|
5552
|
+
const identityPath = path17.join(
|
|
5553
|
+
os9.homedir(),
|
|
4939
5554
|
".exe-os",
|
|
4940
5555
|
"identity",
|
|
4941
5556
|
`${employeeName}.md`
|
|
@@ -4950,7 +5565,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4950
5565
|
}
|
|
4951
5566
|
const behaviorsFile = exportBehaviorsSync(
|
|
4952
5567
|
employeeName,
|
|
4953
|
-
|
|
5568
|
+
path17.basename(spawnCwd),
|
|
4954
5569
|
sessionName
|
|
4955
5570
|
);
|
|
4956
5571
|
if (behaviorsFile) {
|
|
@@ -4965,9 +5580,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4965
5580
|
}
|
|
4966
5581
|
let sessionContextFlag = "";
|
|
4967
5582
|
try {
|
|
4968
|
-
const ctxDir =
|
|
5583
|
+
const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4969
5584
|
mkdirSync7(ctxDir, { recursive: true });
|
|
4970
|
-
const ctxFile =
|
|
5585
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4971
5586
|
const ctxContent = [
|
|
4972
5587
|
`## Session Context`,
|
|
4973
5588
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5051,7 +5666,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5051
5666
|
transport.pipeLog(sessionName, logFile);
|
|
5052
5667
|
try {
|
|
5053
5668
|
const mySession = getMySession();
|
|
5054
|
-
const dispatchInfo =
|
|
5669
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5055
5670
|
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
5056
5671
|
dispatchedBy: mySession,
|
|
5057
5672
|
rootExe: exeSession,
|
|
@@ -5126,15 +5741,15 @@ var init_tmux_routing = __esm({
|
|
|
5126
5741
|
init_intercom_queue();
|
|
5127
5742
|
init_plan_limits();
|
|
5128
5743
|
init_employees();
|
|
5129
|
-
SPAWN_LOCK_DIR =
|
|
5130
|
-
SESSION_CACHE =
|
|
5744
|
+
SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5745
|
+
SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5131
5746
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5132
5747
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5133
5748
|
VERIFY_PANE_LINES = 200;
|
|
5134
5749
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5135
5750
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5136
|
-
INTERCOM_LOG2 =
|
|
5137
|
-
DEBOUNCE_FILE =
|
|
5751
|
+
INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5752
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5138
5753
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5139
5754
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5140
5755
|
}
|
|
@@ -5176,13 +5791,13 @@ var init_memory = __esm({
|
|
|
5176
5791
|
// src/lib/keychain.ts
|
|
5177
5792
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5178
5793
|
import { existsSync as existsSync13 } from "fs";
|
|
5179
|
-
import
|
|
5180
|
-
import
|
|
5794
|
+
import path18 from "path";
|
|
5795
|
+
import os10 from "os";
|
|
5181
5796
|
function getKeyDir() {
|
|
5182
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5797
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os10.homedir(), ".exe-os");
|
|
5183
5798
|
}
|
|
5184
5799
|
function getKeyPath() {
|
|
5185
|
-
return
|
|
5800
|
+
return path18.join(getKeyDir(), "master.key");
|
|
5186
5801
|
}
|
|
5187
5802
|
async function tryKeytar() {
|
|
5188
5803
|
try {
|
|
@@ -5205,7 +5820,7 @@ async function getMasterKey() {
|
|
|
5205
5820
|
const keyPath = getKeyPath();
|
|
5206
5821
|
if (!existsSync13(keyPath)) {
|
|
5207
5822
|
process.stderr.write(
|
|
5208
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
5823
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5209
5824
|
`
|
|
5210
5825
|
);
|
|
5211
5826
|
return null;
|
|
@@ -5243,7 +5858,7 @@ __export(shard_manager_exports, {
|
|
|
5243
5858
|
listShards: () => listShards,
|
|
5244
5859
|
shardExists: () => shardExists
|
|
5245
5860
|
});
|
|
5246
|
-
import
|
|
5861
|
+
import path19 from "path";
|
|
5247
5862
|
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
|
|
5248
5863
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5249
5864
|
function initShardManager(encryptionKey) {
|
|
@@ -5269,7 +5884,7 @@ function getShardClient(projectName) {
|
|
|
5269
5884
|
}
|
|
5270
5885
|
const cached = _shards.get(safeName);
|
|
5271
5886
|
if (cached) return cached;
|
|
5272
|
-
const dbPath =
|
|
5887
|
+
const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
|
|
5273
5888
|
const client = createClient2({
|
|
5274
5889
|
url: `file:${dbPath}`,
|
|
5275
5890
|
encryptionKey: _encryptionKey
|
|
@@ -5279,7 +5894,7 @@ function getShardClient(projectName) {
|
|
|
5279
5894
|
}
|
|
5280
5895
|
function shardExists(projectName) {
|
|
5281
5896
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5282
|
-
return existsSync14(
|
|
5897
|
+
return existsSync14(path19.join(SHARDS_DIR, `${safeName}.db`));
|
|
5283
5898
|
}
|
|
5284
5899
|
function listShards() {
|
|
5285
5900
|
if (!existsSync14(SHARDS_DIR)) return [];
|
|
@@ -5356,7 +5971,23 @@ async function ensureShardSchema(client) {
|
|
|
5356
5971
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
5357
5972
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
5358
5973
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
5359
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
5974
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
5975
|
+
// Metadata enrichment columns (must match database.ts)
|
|
5976
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
5977
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
5978
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
5979
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
5980
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
5981
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
5982
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
5983
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
5984
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
5985
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
5986
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
5987
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
5988
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
5989
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
5990
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
5360
5991
|
]) {
|
|
5361
5992
|
try {
|
|
5362
5993
|
await client.execute(col);
|
|
@@ -5468,7 +6099,7 @@ var init_shard_manager = __esm({
|
|
|
5468
6099
|
"src/lib/shard-manager.ts"() {
|
|
5469
6100
|
"use strict";
|
|
5470
6101
|
init_config();
|
|
5471
|
-
SHARDS_DIR =
|
|
6102
|
+
SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
|
|
5472
6103
|
_shards = /* @__PURE__ */ new Map();
|
|
5473
6104
|
_encryptionKey = null;
|
|
5474
6105
|
_shardingEnabled = false;
|