@askexenow/exe-os 0.9.6 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +668 -37
- package/dist/bin/cli.js +1399 -607
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +795 -155
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +703 -72
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1064 -273
- package/dist/bin/exe-heartbeat.js +676 -45
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +845 -152
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +668 -37
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +731 -91
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +735 -95
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1038 -247
- package/dist/hooks/bug-report-worker.js +902 -172
- package/dist/hooks/commit-complete.js +729 -89
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +851 -158
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +685 -45
- package/dist/hooks/pre-compact.js +729 -89
- package/dist/hooks/pre-tool-use.js +883 -127
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1071 -321
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +732 -92
- package/dist/hooks/session-start.js +1042 -209
- package/dist/hooks/stop.js +691 -51
- package/dist/hooks/subagent-stop.js +685 -45
- package/dist/hooks/summary-worker.js +827 -134
- package/dist/index.js +1026 -234
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +905 -164
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +66 -30
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +109 -73
- package/dist/lib/tmux-routing.js +98 -62
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1807 -472
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +301 -166
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +206 -40
- package/dist/mcp/tools/send-message.js +69 -33
- package/dist/mcp/tools/update-task.js +86 -50
- package/dist/runtime/index.js +731 -91
- package/dist/tui/App.js +864 -125
- package/package.json +3 -2
package/dist/bin/exe-dispatch.js
CHANGED
|
@@ -780,7 +780,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
780
780
|
if (!emp) return false;
|
|
781
781
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
782
782
|
}
|
|
783
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
783
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
784
784
|
var init_employees = __esm({
|
|
785
785
|
"src/lib/employees.ts"() {
|
|
786
786
|
"use strict";
|
|
@@ -789,12 +789,609 @@ var init_employees = __esm({
|
|
|
789
789
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
790
790
|
COORDINATOR_ROLE = "COO";
|
|
791
791
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
792
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// src/lib/database-adapter.ts
|
|
797
|
+
import os5 from "os";
|
|
798
|
+
import path6 from "path";
|
|
799
|
+
import { createRequire } from "module";
|
|
800
|
+
import { pathToFileURL } from "url";
|
|
801
|
+
function quotedIdentifier(identifier) {
|
|
802
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
803
|
+
}
|
|
804
|
+
function unqualifiedTableName(name) {
|
|
805
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
806
|
+
const parts = raw.split(".");
|
|
807
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
808
|
+
}
|
|
809
|
+
function stripTrailingSemicolon(sql) {
|
|
810
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
811
|
+
}
|
|
812
|
+
function appendClause(sql, clause) {
|
|
813
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
814
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
815
|
+
if (!returningMatch) {
|
|
816
|
+
return `${trimmed}${clause}`;
|
|
817
|
+
}
|
|
818
|
+
const idx = returningMatch.index;
|
|
819
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
820
|
+
}
|
|
821
|
+
function normalizeStatement(stmt) {
|
|
822
|
+
if (typeof stmt === "string") {
|
|
823
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
824
|
+
}
|
|
825
|
+
const sql = stmt.sql;
|
|
826
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
827
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
828
|
+
}
|
|
829
|
+
return { kind: "named", sql, args: stmt.args };
|
|
830
|
+
}
|
|
831
|
+
function rewriteBooleanLiterals(sql) {
|
|
832
|
+
let out = sql;
|
|
833
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
834
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
835
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
836
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
837
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
838
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
839
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
840
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
841
|
+
}
|
|
842
|
+
return out;
|
|
843
|
+
}
|
|
844
|
+
function rewriteInsertOrIgnore(sql) {
|
|
845
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
846
|
+
return sql;
|
|
847
|
+
}
|
|
848
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
849
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
850
|
+
}
|
|
851
|
+
function rewriteInsertOrReplace(sql) {
|
|
852
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
853
|
+
if (!match) {
|
|
854
|
+
return sql;
|
|
855
|
+
}
|
|
856
|
+
const rawTable = match[1];
|
|
857
|
+
const rawColumns = match[2];
|
|
858
|
+
const remainder = match[3];
|
|
859
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
860
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
861
|
+
if (!conflictKeys?.length) {
|
|
862
|
+
return sql;
|
|
863
|
+
}
|
|
864
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
865
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
866
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
867
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
868
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
869
|
+
}
|
|
870
|
+
function rewriteSql(sql) {
|
|
871
|
+
let out = sql;
|
|
872
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
873
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
874
|
+
out = rewriteBooleanLiterals(out);
|
|
875
|
+
out = rewriteInsertOrReplace(out);
|
|
876
|
+
out = rewriteInsertOrIgnore(out);
|
|
877
|
+
return stripTrailingSemicolon(out);
|
|
878
|
+
}
|
|
879
|
+
function toBoolean(value) {
|
|
880
|
+
if (value === null || value === void 0) return value;
|
|
881
|
+
if (typeof value === "boolean") return value;
|
|
882
|
+
if (typeof value === "number") return value !== 0;
|
|
883
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
884
|
+
if (typeof value === "string") {
|
|
885
|
+
const normalized = value.trim().toLowerCase();
|
|
886
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
887
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
888
|
+
}
|
|
889
|
+
return Boolean(value);
|
|
890
|
+
}
|
|
891
|
+
function countQuestionMarks(sql, end) {
|
|
892
|
+
let count = 0;
|
|
893
|
+
let inSingle = false;
|
|
894
|
+
let inDouble = false;
|
|
895
|
+
let inLineComment = false;
|
|
896
|
+
let inBlockComment = false;
|
|
897
|
+
for (let i = 0; i < end; i++) {
|
|
898
|
+
const ch = sql[i];
|
|
899
|
+
const next = sql[i + 1];
|
|
900
|
+
if (inLineComment) {
|
|
901
|
+
if (ch === "\n") inLineComment = false;
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (inBlockComment) {
|
|
905
|
+
if (ch === "*" && next === "/") {
|
|
906
|
+
inBlockComment = false;
|
|
907
|
+
i += 1;
|
|
908
|
+
}
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
912
|
+
inLineComment = true;
|
|
913
|
+
i += 1;
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
917
|
+
inBlockComment = true;
|
|
918
|
+
i += 1;
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
922
|
+
inSingle = !inSingle;
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
926
|
+
inDouble = !inDouble;
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
930
|
+
count += 1;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return count;
|
|
934
|
+
}
|
|
935
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
936
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
937
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
938
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
939
|
+
for (const match of sql.matchAll(pattern)) {
|
|
940
|
+
const matchText = match[0];
|
|
941
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
942
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return indexes;
|
|
946
|
+
}
|
|
947
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
948
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
949
|
+
if (!match) return;
|
|
950
|
+
const rawTable = match[1];
|
|
951
|
+
const rawColumns = match[2];
|
|
952
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
953
|
+
if (!boolColumns?.size) return;
|
|
954
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
955
|
+
for (const [index, column] of columns.entries()) {
|
|
956
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
957
|
+
args[index] = toBoolean(args[index]);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
962
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
963
|
+
if (!match) return;
|
|
964
|
+
const rawTable = match[1];
|
|
965
|
+
const setClause = match[2];
|
|
966
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
967
|
+
if (!boolColumns?.size) return;
|
|
968
|
+
const assignments = setClause.split(",");
|
|
969
|
+
let placeholderIndex = 0;
|
|
970
|
+
for (const assignment of assignments) {
|
|
971
|
+
if (!assignment.includes("?")) continue;
|
|
972
|
+
placeholderIndex += 1;
|
|
973
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
974
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
975
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function coerceBooleanArgs(sql, args) {
|
|
980
|
+
const nextArgs = [...args];
|
|
981
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
982
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
983
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
984
|
+
for (const index of placeholderIndexes) {
|
|
985
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
986
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return nextArgs;
|
|
990
|
+
}
|
|
991
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
992
|
+
let out = "";
|
|
993
|
+
let placeholder = 0;
|
|
994
|
+
let inSingle = false;
|
|
995
|
+
let inDouble = false;
|
|
996
|
+
let inLineComment = false;
|
|
997
|
+
let inBlockComment = false;
|
|
998
|
+
for (let i = 0; i < sql.length; i++) {
|
|
999
|
+
const ch = sql[i];
|
|
1000
|
+
const next = sql[i + 1];
|
|
1001
|
+
if (inLineComment) {
|
|
1002
|
+
out += ch;
|
|
1003
|
+
if (ch === "\n") inLineComment = false;
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
if (inBlockComment) {
|
|
1007
|
+
out += ch;
|
|
1008
|
+
if (ch === "*" && next === "/") {
|
|
1009
|
+
out += next;
|
|
1010
|
+
inBlockComment = false;
|
|
1011
|
+
i += 1;
|
|
1012
|
+
}
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1016
|
+
out += ch + next;
|
|
1017
|
+
inLineComment = true;
|
|
1018
|
+
i += 1;
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1022
|
+
out += ch + next;
|
|
1023
|
+
inBlockComment = true;
|
|
1024
|
+
i += 1;
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1028
|
+
inSingle = !inSingle;
|
|
1029
|
+
out += ch;
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1033
|
+
inDouble = !inDouble;
|
|
1034
|
+
out += ch;
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1038
|
+
placeholder += 1;
|
|
1039
|
+
out += `$${placeholder}`;
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
out += ch;
|
|
1043
|
+
}
|
|
1044
|
+
return out;
|
|
1045
|
+
}
|
|
1046
|
+
function translateStatementForPostgres(stmt) {
|
|
1047
|
+
const normalized = normalizeStatement(stmt);
|
|
1048
|
+
if (normalized.kind === "named") {
|
|
1049
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1050
|
+
}
|
|
1051
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1052
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1053
|
+
return {
|
|
1054
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1055
|
+
args: coercedArgs
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function shouldBypassPostgres(stmt) {
|
|
1059
|
+
const normalized = normalizeStatement(stmt);
|
|
1060
|
+
if (normalized.kind === "named") {
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1064
|
+
}
|
|
1065
|
+
function shouldFallbackOnError(error) {
|
|
1066
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1067
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1068
|
+
}
|
|
1069
|
+
function isReadQuery(sql) {
|
|
1070
|
+
const trimmed = sql.trimStart();
|
|
1071
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1072
|
+
}
|
|
1073
|
+
function buildRow(row, columns) {
|
|
1074
|
+
const values = columns.map((column) => row[column]);
|
|
1075
|
+
return Object.assign(values, row);
|
|
1076
|
+
}
|
|
1077
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1078
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1079
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1080
|
+
return {
|
|
1081
|
+
columns,
|
|
1082
|
+
columnTypes: columns.map(() => ""),
|
|
1083
|
+
rows: resultRows,
|
|
1084
|
+
rowsAffected,
|
|
1085
|
+
lastInsertRowid: void 0,
|
|
1086
|
+
toJSON() {
|
|
1087
|
+
return {
|
|
1088
|
+
columns,
|
|
1089
|
+
columnTypes: columns.map(() => ""),
|
|
1090
|
+
rows,
|
|
1091
|
+
rowsAffected,
|
|
1092
|
+
lastInsertRowid: void 0
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
async function loadPrismaClient() {
|
|
1098
|
+
if (!prismaClientPromise) {
|
|
1099
|
+
prismaClientPromise = (async () => {
|
|
1100
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1101
|
+
if (explicitPath) {
|
|
1102
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1103
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1104
|
+
if (!PrismaClient2) {
|
|
1105
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1106
|
+
}
|
|
1107
|
+
return new PrismaClient2();
|
|
1108
|
+
}
|
|
1109
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
|
|
1110
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1111
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1112
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1113
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1114
|
+
if (!PrismaClient) {
|
|
1115
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1116
|
+
}
|
|
1117
|
+
return new PrismaClient();
|
|
1118
|
+
})();
|
|
1119
|
+
}
|
|
1120
|
+
return prismaClientPromise;
|
|
1121
|
+
}
|
|
1122
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1123
|
+
if (!compatibilityBootstrapPromise) {
|
|
1124
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1125
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1126
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1127
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1128
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1129
|
+
relation
|
|
1130
|
+
);
|
|
1131
|
+
if (!rows[0]?.regclass) {
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
await prisma.$executeRawUnsafe(
|
|
1135
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
})();
|
|
1139
|
+
}
|
|
1140
|
+
return compatibilityBootstrapPromise;
|
|
1141
|
+
}
|
|
1142
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1143
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1144
|
+
if (isReadQuery(translated.sql)) {
|
|
1145
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1146
|
+
translated.sql,
|
|
1147
|
+
...translated.args
|
|
1148
|
+
);
|
|
1149
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1150
|
+
}
|
|
1151
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1152
|
+
return buildResultSet([], rowsAffected);
|
|
1153
|
+
}
|
|
1154
|
+
function splitSqlStatements(sql) {
|
|
1155
|
+
const parts = [];
|
|
1156
|
+
let current = "";
|
|
1157
|
+
let inSingle = false;
|
|
1158
|
+
let inDouble = false;
|
|
1159
|
+
let inLineComment = false;
|
|
1160
|
+
let inBlockComment = false;
|
|
1161
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1162
|
+
const ch = sql[i];
|
|
1163
|
+
const next = sql[i + 1];
|
|
1164
|
+
if (inLineComment) {
|
|
1165
|
+
current += ch;
|
|
1166
|
+
if (ch === "\n") inLineComment = false;
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
if (inBlockComment) {
|
|
1170
|
+
current += ch;
|
|
1171
|
+
if (ch === "*" && next === "/") {
|
|
1172
|
+
current += next;
|
|
1173
|
+
inBlockComment = false;
|
|
1174
|
+
i += 1;
|
|
1175
|
+
}
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1179
|
+
current += ch + next;
|
|
1180
|
+
inLineComment = true;
|
|
1181
|
+
i += 1;
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1185
|
+
current += ch + next;
|
|
1186
|
+
inBlockComment = true;
|
|
1187
|
+
i += 1;
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1191
|
+
inSingle = !inSingle;
|
|
1192
|
+
current += ch;
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1196
|
+
inDouble = !inDouble;
|
|
1197
|
+
current += ch;
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1201
|
+
if (current.trim()) {
|
|
1202
|
+
parts.push(current.trim());
|
|
1203
|
+
}
|
|
1204
|
+
current = "";
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
current += ch;
|
|
1208
|
+
}
|
|
1209
|
+
if (current.trim()) {
|
|
1210
|
+
parts.push(current.trim());
|
|
1211
|
+
}
|
|
1212
|
+
return parts;
|
|
1213
|
+
}
|
|
1214
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1215
|
+
const prisma = await loadPrismaClient();
|
|
1216
|
+
await ensureCompatibilityViews(prisma);
|
|
1217
|
+
let closed = false;
|
|
1218
|
+
let adapter;
|
|
1219
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1220
|
+
if (!fallbackClient) {
|
|
1221
|
+
if (error) throw error;
|
|
1222
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1223
|
+
}
|
|
1224
|
+
if (error) {
|
|
1225
|
+
process.stderr.write(
|
|
1226
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1227
|
+
`
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
return fallbackClient.execute(stmt);
|
|
1231
|
+
};
|
|
1232
|
+
adapter = {
|
|
1233
|
+
async execute(stmt) {
|
|
1234
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1235
|
+
return fallbackExecute(stmt);
|
|
1236
|
+
}
|
|
1237
|
+
try {
|
|
1238
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
if (shouldFallbackOnError(error)) {
|
|
1241
|
+
return fallbackExecute(stmt, error);
|
|
1242
|
+
}
|
|
1243
|
+
throw error;
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
async batch(stmts, mode) {
|
|
1247
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1248
|
+
if (!fallbackClient) {
|
|
1249
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1250
|
+
}
|
|
1251
|
+
return fallbackClient.batch(stmts, mode);
|
|
1252
|
+
}
|
|
1253
|
+
try {
|
|
1254
|
+
if (prisma.$transaction) {
|
|
1255
|
+
return await prisma.$transaction(async (tx) => {
|
|
1256
|
+
const results2 = [];
|
|
1257
|
+
for (const stmt of stmts) {
|
|
1258
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1259
|
+
}
|
|
1260
|
+
return results2;
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
const results = [];
|
|
1264
|
+
for (const stmt of stmts) {
|
|
1265
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1266
|
+
}
|
|
1267
|
+
return results;
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1270
|
+
process.stderr.write(
|
|
1271
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1272
|
+
`
|
|
1273
|
+
);
|
|
1274
|
+
return fallbackClient.batch(stmts, mode);
|
|
1275
|
+
}
|
|
1276
|
+
throw error;
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
async migrate(stmts) {
|
|
1280
|
+
if (fallbackClient) {
|
|
1281
|
+
return fallbackClient.migrate(stmts);
|
|
1282
|
+
}
|
|
1283
|
+
return adapter.batch(stmts, "deferred");
|
|
1284
|
+
},
|
|
1285
|
+
async transaction(mode) {
|
|
1286
|
+
if (!fallbackClient) {
|
|
1287
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1288
|
+
}
|
|
1289
|
+
return fallbackClient.transaction(mode);
|
|
1290
|
+
},
|
|
1291
|
+
async executeMultiple(sql) {
|
|
1292
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1293
|
+
return fallbackClient.executeMultiple(sql);
|
|
1294
|
+
}
|
|
1295
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1296
|
+
await adapter.execute(statement);
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
async sync() {
|
|
1300
|
+
if (fallbackClient) {
|
|
1301
|
+
return fallbackClient.sync();
|
|
1302
|
+
}
|
|
1303
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1304
|
+
},
|
|
1305
|
+
close() {
|
|
1306
|
+
closed = true;
|
|
1307
|
+
prismaClientPromise = null;
|
|
1308
|
+
compatibilityBootstrapPromise = null;
|
|
1309
|
+
void prisma.$disconnect?.();
|
|
1310
|
+
},
|
|
1311
|
+
get closed() {
|
|
1312
|
+
return closed;
|
|
1313
|
+
},
|
|
1314
|
+
get protocol() {
|
|
1315
|
+
return "prisma-postgres";
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
return adapter;
|
|
1319
|
+
}
|
|
1320
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1321
|
+
var init_database_adapter = __esm({
|
|
1322
|
+
"src/lib/database-adapter.ts"() {
|
|
1323
|
+
"use strict";
|
|
1324
|
+
VIEW_MAPPINGS = [
|
|
1325
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1326
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1327
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1328
|
+
{ view: "entities", source: "memory.entities" },
|
|
1329
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1330
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1331
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1332
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1333
|
+
{ view: "messages", source: "memory.messages" },
|
|
1334
|
+
{ view: "users", source: "wiki.users" },
|
|
1335
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1336
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1337
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1338
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1339
|
+
];
|
|
1340
|
+
UPSERT_KEYS = {
|
|
1341
|
+
memories: ["id"],
|
|
1342
|
+
tasks: ["id"],
|
|
1343
|
+
behaviors: ["id"],
|
|
1344
|
+
entities: ["id"],
|
|
1345
|
+
relationships: ["id"],
|
|
1346
|
+
entity_aliases: ["alias"],
|
|
1347
|
+
notifications: ["id"],
|
|
1348
|
+
messages: ["id"],
|
|
1349
|
+
users: ["id"],
|
|
1350
|
+
workspaces: ["id"],
|
|
1351
|
+
workspace_users: ["id"],
|
|
1352
|
+
documents: ["id"],
|
|
1353
|
+
chats: ["id"]
|
|
1354
|
+
};
|
|
1355
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1356
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1357
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1358
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1359
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1360
|
+
};
|
|
1361
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1362
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1363
|
+
);
|
|
1364
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1365
|
+
/\bPRAGMA\b/i,
|
|
1366
|
+
/\bsqlite_master\b/i,
|
|
1367
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1368
|
+
/\bMATCH\b/i,
|
|
1369
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1370
|
+
/\bjson_extract\s*\(/i,
|
|
1371
|
+
/\bjulianday\s*\(/i,
|
|
1372
|
+
/\bstrftime\s*\(/i,
|
|
1373
|
+
/\blast_insert_rowid\s*\(/i
|
|
1374
|
+
];
|
|
1375
|
+
prismaClientPromise = null;
|
|
1376
|
+
compatibilityBootstrapPromise = null;
|
|
792
1377
|
}
|
|
793
1378
|
});
|
|
794
1379
|
|
|
795
1380
|
// src/lib/database.ts
|
|
796
1381
|
import { createClient } from "@libsql/client";
|
|
797
1382
|
async function initDatabase(config) {
|
|
1383
|
+
if (_walCheckpointTimer) {
|
|
1384
|
+
clearInterval(_walCheckpointTimer);
|
|
1385
|
+
_walCheckpointTimer = null;
|
|
1386
|
+
}
|
|
1387
|
+
if (_daemonClient) {
|
|
1388
|
+
_daemonClient.close();
|
|
1389
|
+
_daemonClient = null;
|
|
1390
|
+
}
|
|
1391
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1392
|
+
_adapterClient.close();
|
|
1393
|
+
}
|
|
1394
|
+
_adapterClient = null;
|
|
798
1395
|
if (_client) {
|
|
799
1396
|
_client.close();
|
|
800
1397
|
_client = null;
|
|
@@ -808,6 +1405,7 @@ async function initDatabase(config) {
|
|
|
808
1405
|
}
|
|
809
1406
|
_client = createClient(opts);
|
|
810
1407
|
_resilientClient = wrapWithRetry(_client);
|
|
1408
|
+
_adapterClient = _resilientClient;
|
|
811
1409
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
812
1410
|
});
|
|
813
1411
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -818,11 +1416,17 @@ async function initDatabase(config) {
|
|
|
818
1416
|
});
|
|
819
1417
|
}, 3e4);
|
|
820
1418
|
_walCheckpointTimer.unref();
|
|
1419
|
+
if (process.env.DATABASE_URL) {
|
|
1420
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1421
|
+
}
|
|
821
1422
|
}
|
|
822
1423
|
function getClient() {
|
|
823
|
-
if (!
|
|
1424
|
+
if (!_adapterClient) {
|
|
824
1425
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
825
1426
|
}
|
|
1427
|
+
if (process.env.DATABASE_URL) {
|
|
1428
|
+
return _adapterClient;
|
|
1429
|
+
}
|
|
826
1430
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
827
1431
|
return _resilientClient;
|
|
828
1432
|
}
|
|
@@ -1762,16 +2366,18 @@ async function ensureSchema() {
|
|
|
1762
2366
|
}
|
|
1763
2367
|
}
|
|
1764
2368
|
}
|
|
1765
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso;
|
|
2369
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1766
2370
|
var init_database = __esm({
|
|
1767
2371
|
"src/lib/database.ts"() {
|
|
1768
2372
|
"use strict";
|
|
1769
2373
|
init_db_retry();
|
|
1770
2374
|
init_employees();
|
|
2375
|
+
init_database_adapter();
|
|
1771
2376
|
_client = null;
|
|
1772
2377
|
_resilientClient = null;
|
|
1773
2378
|
_walCheckpointTimer = null;
|
|
1774
2379
|
_daemonClient = null;
|
|
2380
|
+
_adapterClient = null;
|
|
1775
2381
|
initTurso = initDatabase;
|
|
1776
2382
|
}
|
|
1777
2383
|
});
|
|
@@ -1779,16 +2385,16 @@ var init_database = __esm({
|
|
|
1779
2385
|
// src/lib/license.ts
|
|
1780
2386
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
1781
2387
|
import { randomUUID } from "crypto";
|
|
1782
|
-
import
|
|
2388
|
+
import path7 from "path";
|
|
1783
2389
|
import { jwtVerify, importSPKI } from "jose";
|
|
1784
2390
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
1785
2391
|
var init_license = __esm({
|
|
1786
2392
|
"src/lib/license.ts"() {
|
|
1787
2393
|
"use strict";
|
|
1788
2394
|
init_config();
|
|
1789
|
-
LICENSE_PATH =
|
|
1790
|
-
CACHE_PATH =
|
|
1791
|
-
DEVICE_ID_PATH =
|
|
2395
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2396
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2397
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
1792
2398
|
PLAN_LIMITS = {
|
|
1793
2399
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
1794
2400
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -1801,7 +2407,7 @@ var init_license = __esm({
|
|
|
1801
2407
|
|
|
1802
2408
|
// src/lib/plan-limits.ts
|
|
1803
2409
|
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
1804
|
-
import
|
|
2410
|
+
import path8 from "path";
|
|
1805
2411
|
function getLicenseSync() {
|
|
1806
2412
|
try {
|
|
1807
2413
|
if (!existsSync7(CACHE_PATH2)) return freeLicense();
|
|
@@ -1873,14 +2479,14 @@ var init_plan_limits = __esm({
|
|
|
1873
2479
|
this.name = "PlanLimitError";
|
|
1874
2480
|
}
|
|
1875
2481
|
};
|
|
1876
|
-
CACHE_PATH2 =
|
|
2482
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
1877
2483
|
}
|
|
1878
2484
|
});
|
|
1879
2485
|
|
|
1880
2486
|
// src/lib/notifications.ts
|
|
1881
2487
|
import crypto from "crypto";
|
|
1882
|
-
import
|
|
1883
|
-
import
|
|
2488
|
+
import path9 from "path";
|
|
2489
|
+
import os6 from "os";
|
|
1884
2490
|
import {
|
|
1885
2491
|
readFileSync as readFileSync8,
|
|
1886
2492
|
readdirSync,
|
|
@@ -2044,8 +2650,8 @@ var init_state_bus = __esm({
|
|
|
2044
2650
|
|
|
2045
2651
|
// src/lib/tasks-crud.ts
|
|
2046
2652
|
import crypto3 from "crypto";
|
|
2047
|
-
import
|
|
2048
|
-
import
|
|
2653
|
+
import path10 from "path";
|
|
2654
|
+
import os7 from "os";
|
|
2049
2655
|
import { execSync as execSync4 } from "child_process";
|
|
2050
2656
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2051
2657
|
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
@@ -2223,8 +2829,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2223
2829
|
}
|
|
2224
2830
|
if (input.baseDir) {
|
|
2225
2831
|
try {
|
|
2226
|
-
await mkdir3(
|
|
2227
|
-
await mkdir3(
|
|
2832
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2833
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2228
2834
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2229
2835
|
await ensureGitignoreExe(input.baseDir);
|
|
2230
2836
|
} catch {
|
|
@@ -2260,9 +2866,9 @@ ${laneWarning}` : laneWarning;
|
|
|
2260
2866
|
});
|
|
2261
2867
|
if (input.baseDir) {
|
|
2262
2868
|
try {
|
|
2263
|
-
const EXE_OS_DIR =
|
|
2264
|
-
const mdPath =
|
|
2265
|
-
const mdDir =
|
|
2869
|
+
const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
|
|
2870
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2871
|
+
const mdDir = path10.dirname(mdPath);
|
|
2266
2872
|
if (!existsSync9(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2267
2873
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2268
2874
|
const mdContent = `# ${input.title}
|
|
@@ -2563,7 +3169,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2563
3169
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2564
3170
|
}
|
|
2565
3171
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2566
|
-
const archPath =
|
|
3172
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2567
3173
|
try {
|
|
2568
3174
|
if (existsSync9(archPath)) return;
|
|
2569
3175
|
const template = [
|
|
@@ -2598,7 +3204,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2598
3204
|
}
|
|
2599
3205
|
}
|
|
2600
3206
|
async function ensureGitignoreExe(baseDir) {
|
|
2601
|
-
const gitignorePath =
|
|
3207
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2602
3208
|
try {
|
|
2603
3209
|
if (existsSync9(gitignorePath)) {
|
|
2604
3210
|
const content = readFileSync9(gitignorePath, "utf-8");
|
|
@@ -2632,13 +3238,13 @@ var init_tasks_crud = __esm({
|
|
|
2632
3238
|
});
|
|
2633
3239
|
|
|
2634
3240
|
// src/lib/tasks-review.ts
|
|
2635
|
-
import
|
|
3241
|
+
import path11 from "path";
|
|
2636
3242
|
import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2637
3243
|
async function countPendingReviews(sessionScope) {
|
|
2638
3244
|
const client = getClient();
|
|
2639
3245
|
if (sessionScope) {
|
|
2640
3246
|
const result3 = await client.execute({
|
|
2641
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
3247
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
2642
3248
|
args: [sessionScope]
|
|
2643
3249
|
});
|
|
2644
3250
|
return Number(result3.rows[0]?.cnt) || 0;
|
|
@@ -2814,11 +3420,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2814
3420
|
);
|
|
2815
3421
|
}
|
|
2816
3422
|
try {
|
|
2817
|
-
const cacheDir =
|
|
3423
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
2818
3424
|
if (existsSync10(cacheDir)) {
|
|
2819
3425
|
for (const f of readdirSync2(cacheDir)) {
|
|
2820
3426
|
if (f.startsWith("review-notified-")) {
|
|
2821
|
-
unlinkSync3(
|
|
3427
|
+
unlinkSync3(path11.join(cacheDir, f));
|
|
2822
3428
|
}
|
|
2823
3429
|
}
|
|
2824
3430
|
}
|
|
@@ -2839,7 +3445,7 @@ var init_tasks_review = __esm({
|
|
|
2839
3445
|
});
|
|
2840
3446
|
|
|
2841
3447
|
// src/lib/tasks-chain.ts
|
|
2842
|
-
import
|
|
3448
|
+
import path12 from "path";
|
|
2843
3449
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2844
3450
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
2845
3451
|
const client = getClient();
|
|
@@ -2856,7 +3462,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
2856
3462
|
});
|
|
2857
3463
|
for (const ur of unblockedRows.rows) {
|
|
2858
3464
|
try {
|
|
2859
|
-
const ubFile =
|
|
3465
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
2860
3466
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
2861
3467
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
2862
3468
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -2925,7 +3531,7 @@ var init_tasks_chain = __esm({
|
|
|
2925
3531
|
|
|
2926
3532
|
// src/lib/project-name.ts
|
|
2927
3533
|
import { execSync as execSync5 } from "child_process";
|
|
2928
|
-
import
|
|
3534
|
+
import path13 from "path";
|
|
2929
3535
|
function getProjectName(cwd) {
|
|
2930
3536
|
const dir = cwd ?? process.cwd();
|
|
2931
3537
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -2938,7 +3544,7 @@ function getProjectName(cwd) {
|
|
|
2938
3544
|
timeout: 2e3,
|
|
2939
3545
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2940
3546
|
}).trim();
|
|
2941
|
-
repoRoot =
|
|
3547
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
2942
3548
|
} catch {
|
|
2943
3549
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
2944
3550
|
cwd: dir,
|
|
@@ -2947,11 +3553,11 @@ function getProjectName(cwd) {
|
|
|
2947
3553
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2948
3554
|
}).trim();
|
|
2949
3555
|
}
|
|
2950
|
-
_cached2 =
|
|
3556
|
+
_cached2 = path13.basename(repoRoot);
|
|
2951
3557
|
_cachedCwd = dir;
|
|
2952
3558
|
return _cached2;
|
|
2953
3559
|
} catch {
|
|
2954
|
-
_cached2 =
|
|
3560
|
+
_cached2 = path13.basename(dir);
|
|
2955
3561
|
_cachedCwd = dir;
|
|
2956
3562
|
return _cached2;
|
|
2957
3563
|
}
|
|
@@ -3424,7 +4030,7 @@ __export(tasks_exports, {
|
|
|
3424
4030
|
updateTaskStatus: () => updateTaskStatus,
|
|
3425
4031
|
writeCheckpoint: () => writeCheckpoint
|
|
3426
4032
|
});
|
|
3427
|
-
import
|
|
4033
|
+
import path14 from "path";
|
|
3428
4034
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
3429
4035
|
async function createTask(input) {
|
|
3430
4036
|
const result2 = await createTaskCore(input);
|
|
@@ -3444,8 +4050,8 @@ async function updateTask(input) {
|
|
|
3444
4050
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3445
4051
|
try {
|
|
3446
4052
|
const agent = String(row.assigned_to);
|
|
3447
|
-
const cacheDir =
|
|
3448
|
-
const cachePath =
|
|
4053
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4054
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3449
4055
|
if (input.status === "in_progress") {
|
|
3450
4056
|
mkdirSync5(cacheDir, { recursive: true });
|
|
3451
4057
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -3916,12 +4522,12 @@ __export(tmux_routing_exports, {
|
|
|
3916
4522
|
});
|
|
3917
4523
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
3918
4524
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
3919
|
-
import
|
|
3920
|
-
import
|
|
4525
|
+
import path15 from "path";
|
|
4526
|
+
import os8 from "os";
|
|
3921
4527
|
import { fileURLToPath } from "url";
|
|
3922
4528
|
import { unlinkSync as unlinkSync5 } from "fs";
|
|
3923
4529
|
function spawnLockPath(sessionName) {
|
|
3924
|
-
return
|
|
4530
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3925
4531
|
}
|
|
3926
4532
|
function isProcessAlive(pid) {
|
|
3927
4533
|
try {
|
|
@@ -3958,8 +4564,8 @@ function releaseSpawnLock(sessionName) {
|
|
|
3958
4564
|
function resolveBehaviorsExporterScript() {
|
|
3959
4565
|
try {
|
|
3960
4566
|
const thisFile = fileURLToPath(import.meta.url);
|
|
3961
|
-
const scriptPath =
|
|
3962
|
-
|
|
4567
|
+
const scriptPath = path15.join(
|
|
4568
|
+
path15.dirname(thisFile),
|
|
3963
4569
|
"..",
|
|
3964
4570
|
"bin",
|
|
3965
4571
|
"exe-export-behaviors.js"
|
|
@@ -4034,7 +4640,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4034
4640
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4035
4641
|
}
|
|
4036
4642
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4037
|
-
const filePath =
|
|
4643
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4038
4644
|
writeFileSync7(filePath, JSON.stringify({
|
|
4039
4645
|
parentExe: rootExe,
|
|
4040
4646
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -4043,7 +4649,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4043
4649
|
}
|
|
4044
4650
|
function getParentExe(sessionKey) {
|
|
4045
4651
|
try {
|
|
4046
|
-
const data = JSON.parse(readFileSync10(
|
|
4652
|
+
const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4047
4653
|
return data.parentExe || null;
|
|
4048
4654
|
} catch {
|
|
4049
4655
|
return null;
|
|
@@ -4052,7 +4658,7 @@ function getParentExe(sessionKey) {
|
|
|
4052
4658
|
function getDispatchedBy(sessionKey) {
|
|
4053
4659
|
try {
|
|
4054
4660
|
const data = JSON.parse(readFileSync10(
|
|
4055
|
-
|
|
4661
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4056
4662
|
"utf8"
|
|
4057
4663
|
));
|
|
4058
4664
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4063,15 +4669,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
4063
4669
|
function resolveExeSession() {
|
|
4064
4670
|
const mySession = getMySession();
|
|
4065
4671
|
if (!mySession) return null;
|
|
4672
|
+
const fromSessionName = extractRootExe(mySession);
|
|
4066
4673
|
try {
|
|
4067
4674
|
const key = getSessionKey();
|
|
4068
4675
|
const parentExe = getParentExe(key);
|
|
4069
4676
|
if (parentExe) {
|
|
4070
|
-
|
|
4677
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
4678
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
4679
|
+
process.stderr.write(
|
|
4680
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
4681
|
+
`
|
|
4682
|
+
);
|
|
4683
|
+
return fromSessionName;
|
|
4684
|
+
}
|
|
4685
|
+
return fromCache;
|
|
4071
4686
|
}
|
|
4072
4687
|
} catch {
|
|
4073
4688
|
}
|
|
4074
|
-
return
|
|
4689
|
+
return fromSessionName ?? mySession;
|
|
4075
4690
|
}
|
|
4076
4691
|
function isEmployeeAlive(sessionName) {
|
|
4077
4692
|
return getTransport().isAlive(sessionName);
|
|
@@ -4229,7 +4844,7 @@ function sendIntercom(targetSession) {
|
|
|
4229
4844
|
try {
|
|
4230
4845
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4231
4846
|
const agent = baseAgentName(rawAgent);
|
|
4232
|
-
const markerPath =
|
|
4847
|
+
const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4233
4848
|
if (existsSync11(markerPath)) {
|
|
4234
4849
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4235
4850
|
return "debounced";
|
|
@@ -4239,7 +4854,7 @@ function sendIntercom(targetSession) {
|
|
|
4239
4854
|
try {
|
|
4240
4855
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4241
4856
|
const agent = baseAgentName(rawAgent);
|
|
4242
|
-
const taskDir =
|
|
4857
|
+
const taskDir = path15.join(process.cwd(), "exe", agent);
|
|
4243
4858
|
if (existsSync11(taskDir)) {
|
|
4244
4859
|
const files = readdirSync3(taskDir).filter(
|
|
4245
4860
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4373,8 +4988,8 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4373
4988
|
const transport = getTransport();
|
|
4374
4989
|
const sessionName = employeeSessionName(employeeName2, exeSession2, opts?.instance);
|
|
4375
4990
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName2}${opts.instance}` : employeeName2;
|
|
4376
|
-
const logDir =
|
|
4377
|
-
const logFile =
|
|
4991
|
+
const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
|
|
4992
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4378
4993
|
if (!existsSync11(logDir)) {
|
|
4379
4994
|
mkdirSync6(logDir, { recursive: true });
|
|
4380
4995
|
}
|
|
@@ -4382,14 +4997,14 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4382
4997
|
let cleanupSuffix = "";
|
|
4383
4998
|
try {
|
|
4384
4999
|
const thisFile = fileURLToPath(import.meta.url);
|
|
4385
|
-
const cleanupScript =
|
|
5000
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4386
5001
|
if (existsSync11(cleanupScript)) {
|
|
4387
5002
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName2}" "${exeSession2}"`;
|
|
4388
5003
|
}
|
|
4389
5004
|
} catch {
|
|
4390
5005
|
}
|
|
4391
5006
|
try {
|
|
4392
|
-
const claudeJsonPath =
|
|
5007
|
+
const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
|
|
4393
5008
|
let claudeJson = {};
|
|
4394
5009
|
try {
|
|
4395
5010
|
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
@@ -4404,10 +5019,10 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4404
5019
|
} catch {
|
|
4405
5020
|
}
|
|
4406
5021
|
try {
|
|
4407
|
-
const settingsDir =
|
|
5022
|
+
const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
|
|
4408
5023
|
const normalizedKey = (opts?.cwd ?? projectDir2).replace(/\//g, "-").replace(/^-/, "");
|
|
4409
|
-
const projSettingsDir =
|
|
4410
|
-
const settingsPath =
|
|
5024
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
5025
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4411
5026
|
let settings = {};
|
|
4412
5027
|
try {
|
|
4413
5028
|
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
@@ -4454,8 +5069,8 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4454
5069
|
let behaviorsFlag = "";
|
|
4455
5070
|
let legacyFallbackWarned = false;
|
|
4456
5071
|
if (!useExeAgent && !useBinSymlink) {
|
|
4457
|
-
const identityPath =
|
|
4458
|
-
|
|
5072
|
+
const identityPath = path15.join(
|
|
5073
|
+
os8.homedir(),
|
|
4459
5074
|
".exe-os",
|
|
4460
5075
|
"identity",
|
|
4461
5076
|
`${employeeName2}.md`
|
|
@@ -4470,7 +5085,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4470
5085
|
}
|
|
4471
5086
|
const behaviorsFile = exportBehaviorsSync(
|
|
4472
5087
|
employeeName2,
|
|
4473
|
-
|
|
5088
|
+
path15.basename(spawnCwd),
|
|
4474
5089
|
sessionName
|
|
4475
5090
|
);
|
|
4476
5091
|
if (behaviorsFile) {
|
|
@@ -4485,9 +5100,9 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4485
5100
|
}
|
|
4486
5101
|
let sessionContextFlag = "";
|
|
4487
5102
|
try {
|
|
4488
|
-
const ctxDir =
|
|
5103
|
+
const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4489
5104
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4490
|
-
const ctxFile =
|
|
5105
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4491
5106
|
const ctxContent = [
|
|
4492
5107
|
`## Session Context`,
|
|
4493
5108
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -4571,7 +5186,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4571
5186
|
transport.pipeLog(sessionName, logFile);
|
|
4572
5187
|
try {
|
|
4573
5188
|
const mySession = getMySession();
|
|
4574
|
-
const dispatchInfo =
|
|
5189
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4575
5190
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4576
5191
|
dispatchedBy: mySession,
|
|
4577
5192
|
rootExe: exeSession2,
|
|
@@ -4646,15 +5261,15 @@ var init_tmux_routing = __esm({
|
|
|
4646
5261
|
init_intercom_queue();
|
|
4647
5262
|
init_plan_limits();
|
|
4648
5263
|
init_employees();
|
|
4649
|
-
SPAWN_LOCK_DIR =
|
|
4650
|
-
SESSION_CACHE =
|
|
5264
|
+
SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
5265
|
+
SESSION_CACHE = path15.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4651
5266
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4652
5267
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4653
5268
|
VERIFY_PANE_LINES = 200;
|
|
4654
5269
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4655
5270
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
4656
|
-
INTERCOM_LOG2 =
|
|
4657
|
-
DEBOUNCE_FILE =
|
|
5271
|
+
INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
5272
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4658
5273
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4659
5274
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
4660
5275
|
}
|
|
@@ -4673,7 +5288,7 @@ __export(shard_manager_exports, {
|
|
|
4673
5288
|
listShards: () => listShards,
|
|
4674
5289
|
shardExists: () => shardExists
|
|
4675
5290
|
});
|
|
4676
|
-
import
|
|
5291
|
+
import path17 from "path";
|
|
4677
5292
|
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
4678
5293
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4679
5294
|
function initShardManager(encryptionKey) {
|
|
@@ -4699,7 +5314,7 @@ function getShardClient(projectName) {
|
|
|
4699
5314
|
}
|
|
4700
5315
|
const cached = _shards.get(safeName);
|
|
4701
5316
|
if (cached) return cached;
|
|
4702
|
-
const dbPath =
|
|
5317
|
+
const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
|
|
4703
5318
|
const client = createClient2({
|
|
4704
5319
|
url: `file:${dbPath}`,
|
|
4705
5320
|
encryptionKey: _encryptionKey
|
|
@@ -4709,7 +5324,7 @@ function getShardClient(projectName) {
|
|
|
4709
5324
|
}
|
|
4710
5325
|
function shardExists(projectName) {
|
|
4711
5326
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4712
|
-
return existsSync13(
|
|
5327
|
+
return existsSync13(path17.join(SHARDS_DIR, `${safeName}.db`));
|
|
4713
5328
|
}
|
|
4714
5329
|
function listShards() {
|
|
4715
5330
|
if (!existsSync13(SHARDS_DIR)) return [];
|
|
@@ -4786,7 +5401,23 @@ async function ensureShardSchema(client) {
|
|
|
4786
5401
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
4787
5402
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
4788
5403
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
4789
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
5404
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
5405
|
+
// Metadata enrichment columns (must match database.ts)
|
|
5406
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
5407
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
5408
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
5409
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
5410
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
5411
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
5412
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
5413
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
5414
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
5415
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
5416
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
5417
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
5418
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
5419
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
5420
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
4790
5421
|
]) {
|
|
4791
5422
|
try {
|
|
4792
5423
|
await client.execute(col);
|
|
@@ -4898,7 +5529,7 @@ var init_shard_manager = __esm({
|
|
|
4898
5529
|
"src/lib/shard-manager.ts"() {
|
|
4899
5530
|
"use strict";
|
|
4900
5531
|
init_config();
|
|
4901
|
-
SHARDS_DIR =
|
|
5532
|
+
SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
|
|
4902
5533
|
_shards = /* @__PURE__ */ new Map();
|
|
4903
5534
|
_encryptionKey = null;
|
|
4904
5535
|
_shardingEnabled = false;
|
|
@@ -5103,15 +5734,15 @@ init_database();
|
|
|
5103
5734
|
// src/lib/keychain.ts
|
|
5104
5735
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5105
5736
|
import { existsSync as existsSync12 } from "fs";
|
|
5106
|
-
import
|
|
5107
|
-
import
|
|
5737
|
+
import path16 from "path";
|
|
5738
|
+
import os9 from "os";
|
|
5108
5739
|
var SERVICE = "exe-mem";
|
|
5109
5740
|
var ACCOUNT = "master-key";
|
|
5110
5741
|
function getKeyDir() {
|
|
5111
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5742
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os9.homedir(), ".exe-os");
|
|
5112
5743
|
}
|
|
5113
5744
|
function getKeyPath() {
|
|
5114
|
-
return
|
|
5745
|
+
return path16.join(getKeyDir(), "master.key");
|
|
5115
5746
|
}
|
|
5116
5747
|
async function tryKeytar() {
|
|
5117
5748
|
try {
|
|
@@ -5134,7 +5765,7 @@ async function getMasterKey() {
|
|
|
5134
5765
|
const keyPath = getKeyPath();
|
|
5135
5766
|
if (!existsSync12(keyPath)) {
|
|
5136
5767
|
process.stderr.write(
|
|
5137
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
5768
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5138
5769
|
`
|
|
5139
5770
|
);
|
|
5140
5771
|
return null;
|