@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/bin/exe-link.js
CHANGED
|
@@ -567,7 +567,7 @@ function registerBinSymlinks(name) {
|
|
|
567
567
|
}
|
|
568
568
|
return { created, skipped, errors };
|
|
569
569
|
}
|
|
570
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
570
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
571
571
|
var init_employees = __esm({
|
|
572
572
|
"src/lib/employees.ts"() {
|
|
573
573
|
"use strict";
|
|
@@ -575,16 +575,601 @@ var init_employees = __esm({
|
|
|
575
575
|
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
576
576
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
577
577
|
COORDINATOR_ROLE = "COO";
|
|
578
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// src/lib/database-adapter.ts
|
|
583
|
+
import os4 from "os";
|
|
584
|
+
import path4 from "path";
|
|
585
|
+
import { createRequire } from "module";
|
|
586
|
+
import { pathToFileURL } from "url";
|
|
587
|
+
function quotedIdentifier(identifier) {
|
|
588
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
589
|
+
}
|
|
590
|
+
function unqualifiedTableName(name) {
|
|
591
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
592
|
+
const parts = raw.split(".");
|
|
593
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
594
|
+
}
|
|
595
|
+
function stripTrailingSemicolon(sql) {
|
|
596
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
597
|
+
}
|
|
598
|
+
function appendClause(sql, clause) {
|
|
599
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
600
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
601
|
+
if (!returningMatch) {
|
|
602
|
+
return `${trimmed}${clause}`;
|
|
603
|
+
}
|
|
604
|
+
const idx = returningMatch.index;
|
|
605
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
606
|
+
}
|
|
607
|
+
function normalizeStatement(stmt) {
|
|
608
|
+
if (typeof stmt === "string") {
|
|
609
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
610
|
+
}
|
|
611
|
+
const sql = stmt.sql;
|
|
612
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
613
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
614
|
+
}
|
|
615
|
+
return { kind: "named", sql, args: stmt.args };
|
|
616
|
+
}
|
|
617
|
+
function rewriteBooleanLiterals(sql) {
|
|
618
|
+
let out = sql;
|
|
619
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
620
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
621
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
622
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
623
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
624
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
625
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
626
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
627
|
+
}
|
|
628
|
+
return out;
|
|
629
|
+
}
|
|
630
|
+
function rewriteInsertOrIgnore(sql) {
|
|
631
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
632
|
+
return sql;
|
|
633
|
+
}
|
|
634
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
635
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
636
|
+
}
|
|
637
|
+
function rewriteInsertOrReplace(sql) {
|
|
638
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
639
|
+
if (!match) {
|
|
640
|
+
return sql;
|
|
641
|
+
}
|
|
642
|
+
const rawTable = match[1];
|
|
643
|
+
const rawColumns = match[2];
|
|
644
|
+
const remainder = match[3];
|
|
645
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
646
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
647
|
+
if (!conflictKeys?.length) {
|
|
648
|
+
return sql;
|
|
649
|
+
}
|
|
650
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
651
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
652
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
653
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
654
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
655
|
+
}
|
|
656
|
+
function rewriteSql(sql) {
|
|
657
|
+
let out = sql;
|
|
658
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
659
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
660
|
+
out = rewriteBooleanLiterals(out);
|
|
661
|
+
out = rewriteInsertOrReplace(out);
|
|
662
|
+
out = rewriteInsertOrIgnore(out);
|
|
663
|
+
return stripTrailingSemicolon(out);
|
|
664
|
+
}
|
|
665
|
+
function toBoolean(value) {
|
|
666
|
+
if (value === null || value === void 0) return value;
|
|
667
|
+
if (typeof value === "boolean") return value;
|
|
668
|
+
if (typeof value === "number") return value !== 0;
|
|
669
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
670
|
+
if (typeof value === "string") {
|
|
671
|
+
const normalized = value.trim().toLowerCase();
|
|
672
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
673
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
674
|
+
}
|
|
675
|
+
return Boolean(value);
|
|
676
|
+
}
|
|
677
|
+
function countQuestionMarks(sql, end) {
|
|
678
|
+
let count = 0;
|
|
679
|
+
let inSingle = false;
|
|
680
|
+
let inDouble = false;
|
|
681
|
+
let inLineComment = false;
|
|
682
|
+
let inBlockComment = false;
|
|
683
|
+
for (let i = 0; i < end; i++) {
|
|
684
|
+
const ch = sql[i];
|
|
685
|
+
const next = sql[i + 1];
|
|
686
|
+
if (inLineComment) {
|
|
687
|
+
if (ch === "\n") inLineComment = false;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (inBlockComment) {
|
|
691
|
+
if (ch === "*" && next === "/") {
|
|
692
|
+
inBlockComment = false;
|
|
693
|
+
i += 1;
|
|
694
|
+
}
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
698
|
+
inLineComment = true;
|
|
699
|
+
i += 1;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
703
|
+
inBlockComment = true;
|
|
704
|
+
i += 1;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
708
|
+
inSingle = !inSingle;
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
712
|
+
inDouble = !inDouble;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
716
|
+
count += 1;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return count;
|
|
720
|
+
}
|
|
721
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
722
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
723
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
724
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
725
|
+
for (const match of sql.matchAll(pattern)) {
|
|
726
|
+
const matchText = match[0];
|
|
727
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
728
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return indexes;
|
|
732
|
+
}
|
|
733
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
734
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
735
|
+
if (!match) return;
|
|
736
|
+
const rawTable = match[1];
|
|
737
|
+
const rawColumns = match[2];
|
|
738
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
739
|
+
if (!boolColumns?.size) return;
|
|
740
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
741
|
+
for (const [index, column] of columns.entries()) {
|
|
742
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
743
|
+
args[index] = toBoolean(args[index]);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
748
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
749
|
+
if (!match) return;
|
|
750
|
+
const rawTable = match[1];
|
|
751
|
+
const setClause = match[2];
|
|
752
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
753
|
+
if (!boolColumns?.size) return;
|
|
754
|
+
const assignments = setClause.split(",");
|
|
755
|
+
let placeholderIndex = 0;
|
|
756
|
+
for (const assignment of assignments) {
|
|
757
|
+
if (!assignment.includes("?")) continue;
|
|
758
|
+
placeholderIndex += 1;
|
|
759
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
760
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
761
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function coerceBooleanArgs(sql, args) {
|
|
766
|
+
const nextArgs = [...args];
|
|
767
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
768
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
769
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
770
|
+
for (const index of placeholderIndexes) {
|
|
771
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
772
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return nextArgs;
|
|
776
|
+
}
|
|
777
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
778
|
+
let out = "";
|
|
779
|
+
let placeholder = 0;
|
|
780
|
+
let inSingle = false;
|
|
781
|
+
let inDouble = false;
|
|
782
|
+
let inLineComment = false;
|
|
783
|
+
let inBlockComment = false;
|
|
784
|
+
for (let i = 0; i < sql.length; i++) {
|
|
785
|
+
const ch = sql[i];
|
|
786
|
+
const next = sql[i + 1];
|
|
787
|
+
if (inLineComment) {
|
|
788
|
+
out += ch;
|
|
789
|
+
if (ch === "\n") inLineComment = false;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (inBlockComment) {
|
|
793
|
+
out += ch;
|
|
794
|
+
if (ch === "*" && next === "/") {
|
|
795
|
+
out += next;
|
|
796
|
+
inBlockComment = false;
|
|
797
|
+
i += 1;
|
|
798
|
+
}
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
802
|
+
out += ch + next;
|
|
803
|
+
inLineComment = true;
|
|
804
|
+
i += 1;
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
808
|
+
out += ch + next;
|
|
809
|
+
inBlockComment = true;
|
|
810
|
+
i += 1;
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
814
|
+
inSingle = !inSingle;
|
|
815
|
+
out += ch;
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
819
|
+
inDouble = !inDouble;
|
|
820
|
+
out += ch;
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
824
|
+
placeholder += 1;
|
|
825
|
+
out += `$${placeholder}`;
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
out += ch;
|
|
829
|
+
}
|
|
830
|
+
return out;
|
|
831
|
+
}
|
|
832
|
+
function translateStatementForPostgres(stmt) {
|
|
833
|
+
const normalized = normalizeStatement(stmt);
|
|
834
|
+
if (normalized.kind === "named") {
|
|
835
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
836
|
+
}
|
|
837
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
838
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
839
|
+
return {
|
|
840
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
841
|
+
args: coercedArgs
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function shouldBypassPostgres(stmt) {
|
|
845
|
+
const normalized = normalizeStatement(stmt);
|
|
846
|
+
if (normalized.kind === "named") {
|
|
847
|
+
return true;
|
|
848
|
+
}
|
|
849
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
850
|
+
}
|
|
851
|
+
function shouldFallbackOnError(error) {
|
|
852
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
853
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
854
|
+
}
|
|
855
|
+
function isReadQuery(sql) {
|
|
856
|
+
const trimmed = sql.trimStart();
|
|
857
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
858
|
+
}
|
|
859
|
+
function buildRow(row, columns) {
|
|
860
|
+
const values = columns.map((column) => row[column]);
|
|
861
|
+
return Object.assign(values, row);
|
|
862
|
+
}
|
|
863
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
864
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
865
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
866
|
+
return {
|
|
867
|
+
columns,
|
|
868
|
+
columnTypes: columns.map(() => ""),
|
|
869
|
+
rows: resultRows,
|
|
870
|
+
rowsAffected,
|
|
871
|
+
lastInsertRowid: void 0,
|
|
872
|
+
toJSON() {
|
|
873
|
+
return {
|
|
874
|
+
columns,
|
|
875
|
+
columnTypes: columns.map(() => ""),
|
|
876
|
+
rows,
|
|
877
|
+
rowsAffected,
|
|
878
|
+
lastInsertRowid: void 0
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
async function loadPrismaClient() {
|
|
884
|
+
if (!prismaClientPromise) {
|
|
885
|
+
prismaClientPromise = (async () => {
|
|
886
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
887
|
+
if (explicitPath) {
|
|
888
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
889
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
890
|
+
if (!PrismaClient2) {
|
|
891
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
892
|
+
}
|
|
893
|
+
return new PrismaClient2();
|
|
894
|
+
}
|
|
895
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os4.homedir(), "exe-db");
|
|
896
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
897
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
898
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
899
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
900
|
+
if (!PrismaClient) {
|
|
901
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
902
|
+
}
|
|
903
|
+
return new PrismaClient();
|
|
904
|
+
})();
|
|
905
|
+
}
|
|
906
|
+
return prismaClientPromise;
|
|
907
|
+
}
|
|
908
|
+
async function ensureCompatibilityViews(prisma) {
|
|
909
|
+
if (!compatibilityBootstrapPromise) {
|
|
910
|
+
compatibilityBootstrapPromise = (async () => {
|
|
911
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
912
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
913
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
914
|
+
"SELECT to_regclass($1) AS regclass",
|
|
915
|
+
relation
|
|
916
|
+
);
|
|
917
|
+
if (!rows[0]?.regclass) {
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
await prisma.$executeRawUnsafe(
|
|
921
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
})();
|
|
925
|
+
}
|
|
926
|
+
return compatibilityBootstrapPromise;
|
|
927
|
+
}
|
|
928
|
+
async function executeOnPrisma(executor, stmt) {
|
|
929
|
+
const translated = translateStatementForPostgres(stmt);
|
|
930
|
+
if (isReadQuery(translated.sql)) {
|
|
931
|
+
const rows = await executor.$queryRawUnsafe(
|
|
932
|
+
translated.sql,
|
|
933
|
+
...translated.args
|
|
934
|
+
);
|
|
935
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
936
|
+
}
|
|
937
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
938
|
+
return buildResultSet([], rowsAffected);
|
|
939
|
+
}
|
|
940
|
+
function splitSqlStatements(sql) {
|
|
941
|
+
const parts = [];
|
|
942
|
+
let current = "";
|
|
943
|
+
let inSingle = false;
|
|
944
|
+
let inDouble = false;
|
|
945
|
+
let inLineComment = false;
|
|
946
|
+
let inBlockComment = false;
|
|
947
|
+
for (let i = 0; i < sql.length; i++) {
|
|
948
|
+
const ch = sql[i];
|
|
949
|
+
const next = sql[i + 1];
|
|
950
|
+
if (inLineComment) {
|
|
951
|
+
current += ch;
|
|
952
|
+
if (ch === "\n") inLineComment = false;
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
if (inBlockComment) {
|
|
956
|
+
current += ch;
|
|
957
|
+
if (ch === "*" && next === "/") {
|
|
958
|
+
current += next;
|
|
959
|
+
inBlockComment = false;
|
|
960
|
+
i += 1;
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
965
|
+
current += ch + next;
|
|
966
|
+
inLineComment = true;
|
|
967
|
+
i += 1;
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
971
|
+
current += ch + next;
|
|
972
|
+
inBlockComment = true;
|
|
973
|
+
i += 1;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
977
|
+
inSingle = !inSingle;
|
|
978
|
+
current += ch;
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
982
|
+
inDouble = !inDouble;
|
|
983
|
+
current += ch;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
987
|
+
if (current.trim()) {
|
|
988
|
+
parts.push(current.trim());
|
|
989
|
+
}
|
|
990
|
+
current = "";
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
current += ch;
|
|
994
|
+
}
|
|
995
|
+
if (current.trim()) {
|
|
996
|
+
parts.push(current.trim());
|
|
997
|
+
}
|
|
998
|
+
return parts;
|
|
999
|
+
}
|
|
1000
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1001
|
+
const prisma = await loadPrismaClient();
|
|
1002
|
+
await ensureCompatibilityViews(prisma);
|
|
1003
|
+
let closed = false;
|
|
1004
|
+
let adapter;
|
|
1005
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1006
|
+
if (!fallbackClient) {
|
|
1007
|
+
if (error) throw error;
|
|
1008
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1009
|
+
}
|
|
1010
|
+
if (error) {
|
|
1011
|
+
process.stderr.write(
|
|
1012
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1013
|
+
`
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
return fallbackClient.execute(stmt);
|
|
1017
|
+
};
|
|
1018
|
+
adapter = {
|
|
1019
|
+
async execute(stmt) {
|
|
1020
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1021
|
+
return fallbackExecute(stmt);
|
|
1022
|
+
}
|
|
1023
|
+
try {
|
|
1024
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
if (shouldFallbackOnError(error)) {
|
|
1027
|
+
return fallbackExecute(stmt, error);
|
|
1028
|
+
}
|
|
1029
|
+
throw error;
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
async batch(stmts, mode) {
|
|
1033
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1034
|
+
if (!fallbackClient) {
|
|
1035
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1036
|
+
}
|
|
1037
|
+
return fallbackClient.batch(stmts, mode);
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
if (prisma.$transaction) {
|
|
1041
|
+
return await prisma.$transaction(async (tx) => {
|
|
1042
|
+
const results2 = [];
|
|
1043
|
+
for (const stmt of stmts) {
|
|
1044
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1045
|
+
}
|
|
1046
|
+
return results2;
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
const results = [];
|
|
1050
|
+
for (const stmt of stmts) {
|
|
1051
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1052
|
+
}
|
|
1053
|
+
return results;
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1056
|
+
process.stderr.write(
|
|
1057
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1058
|
+
`
|
|
1059
|
+
);
|
|
1060
|
+
return fallbackClient.batch(stmts, mode);
|
|
1061
|
+
}
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
async migrate(stmts) {
|
|
1066
|
+
if (fallbackClient) {
|
|
1067
|
+
return fallbackClient.migrate(stmts);
|
|
1068
|
+
}
|
|
1069
|
+
return adapter.batch(stmts, "deferred");
|
|
1070
|
+
},
|
|
1071
|
+
async transaction(mode) {
|
|
1072
|
+
if (!fallbackClient) {
|
|
1073
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1074
|
+
}
|
|
1075
|
+
return fallbackClient.transaction(mode);
|
|
1076
|
+
},
|
|
1077
|
+
async executeMultiple(sql) {
|
|
1078
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1079
|
+
return fallbackClient.executeMultiple(sql);
|
|
1080
|
+
}
|
|
1081
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1082
|
+
await adapter.execute(statement);
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
async sync() {
|
|
1086
|
+
if (fallbackClient) {
|
|
1087
|
+
return fallbackClient.sync();
|
|
1088
|
+
}
|
|
1089
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1090
|
+
},
|
|
1091
|
+
close() {
|
|
1092
|
+
closed = true;
|
|
1093
|
+
prismaClientPromise = null;
|
|
1094
|
+
compatibilityBootstrapPromise = null;
|
|
1095
|
+
void prisma.$disconnect?.();
|
|
1096
|
+
},
|
|
1097
|
+
get closed() {
|
|
1098
|
+
return closed;
|
|
1099
|
+
},
|
|
1100
|
+
get protocol() {
|
|
1101
|
+
return "prisma-postgres";
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
return adapter;
|
|
1105
|
+
}
|
|
1106
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1107
|
+
var init_database_adapter = __esm({
|
|
1108
|
+
"src/lib/database-adapter.ts"() {
|
|
1109
|
+
"use strict";
|
|
1110
|
+
VIEW_MAPPINGS = [
|
|
1111
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1112
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1113
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1114
|
+
{ view: "entities", source: "memory.entities" },
|
|
1115
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1116
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1117
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1118
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1119
|
+
{ view: "messages", source: "memory.messages" },
|
|
1120
|
+
{ view: "users", source: "wiki.users" },
|
|
1121
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1122
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1123
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1124
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1125
|
+
];
|
|
1126
|
+
UPSERT_KEYS = {
|
|
1127
|
+
memories: ["id"],
|
|
1128
|
+
tasks: ["id"],
|
|
1129
|
+
behaviors: ["id"],
|
|
1130
|
+
entities: ["id"],
|
|
1131
|
+
relationships: ["id"],
|
|
1132
|
+
entity_aliases: ["alias"],
|
|
1133
|
+
notifications: ["id"],
|
|
1134
|
+
messages: ["id"],
|
|
1135
|
+
users: ["id"],
|
|
1136
|
+
workspaces: ["id"],
|
|
1137
|
+
workspace_users: ["id"],
|
|
1138
|
+
documents: ["id"],
|
|
1139
|
+
chats: ["id"]
|
|
1140
|
+
};
|
|
1141
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1142
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1143
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1144
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1145
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1146
|
+
};
|
|
1147
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1148
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1149
|
+
);
|
|
1150
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1151
|
+
/\bPRAGMA\b/i,
|
|
1152
|
+
/\bsqlite_master\b/i,
|
|
1153
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1154
|
+
/\bMATCH\b/i,
|
|
1155
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1156
|
+
/\bjson_extract\s*\(/i,
|
|
1157
|
+
/\bjulianday\s*\(/i,
|
|
1158
|
+
/\bstrftime\s*\(/i,
|
|
1159
|
+
/\blast_insert_rowid\s*\(/i
|
|
1160
|
+
];
|
|
1161
|
+
prismaClientPromise = null;
|
|
1162
|
+
compatibilityBootstrapPromise = null;
|
|
578
1163
|
}
|
|
579
1164
|
});
|
|
580
1165
|
|
|
581
1166
|
// src/lib/exe-daemon-client.ts
|
|
582
1167
|
import net from "net";
|
|
583
|
-
import
|
|
1168
|
+
import os5 from "os";
|
|
584
1169
|
import { spawn } from "child_process";
|
|
585
1170
|
import { randomUUID } from "crypto";
|
|
586
1171
|
import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
587
|
-
import
|
|
1172
|
+
import path5 from "path";
|
|
588
1173
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
589
1174
|
function handleData(chunk) {
|
|
590
1175
|
_buffer += chunk.toString();
|
|
@@ -635,17 +1220,17 @@ function cleanupStaleFiles() {
|
|
|
635
1220
|
}
|
|
636
1221
|
}
|
|
637
1222
|
function findPackageRoot() {
|
|
638
|
-
let dir =
|
|
639
|
-
const { root } =
|
|
1223
|
+
let dir = path5.dirname(fileURLToPath2(import.meta.url));
|
|
1224
|
+
const { root } = path5.parse(dir);
|
|
640
1225
|
while (dir !== root) {
|
|
641
|
-
if (existsSync4(
|
|
642
|
-
dir =
|
|
1226
|
+
if (existsSync4(path5.join(dir, "package.json"))) return dir;
|
|
1227
|
+
dir = path5.dirname(dir);
|
|
643
1228
|
}
|
|
644
1229
|
return null;
|
|
645
1230
|
}
|
|
646
1231
|
function spawnDaemon() {
|
|
647
|
-
const freeGB =
|
|
648
|
-
const totalGB =
|
|
1232
|
+
const freeGB = os5.freemem() / (1024 * 1024 * 1024);
|
|
1233
|
+
const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
|
|
649
1234
|
if (totalGB <= 8) {
|
|
650
1235
|
process.stderr.write(
|
|
651
1236
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -665,7 +1250,7 @@ function spawnDaemon() {
|
|
|
665
1250
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
666
1251
|
return;
|
|
667
1252
|
}
|
|
668
|
-
const daemonPath =
|
|
1253
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
669
1254
|
if (!existsSync4(daemonPath)) {
|
|
670
1255
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
671
1256
|
`);
|
|
@@ -674,7 +1259,7 @@ function spawnDaemon() {
|
|
|
674
1259
|
const resolvedPath = daemonPath;
|
|
675
1260
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
676
1261
|
`);
|
|
677
|
-
const logPath =
|
|
1262
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
678
1263
|
let stderrFd = "ignore";
|
|
679
1264
|
try {
|
|
680
1265
|
stderrFd = openSync(logPath, "a");
|
|
@@ -821,9 +1406,9 @@ var init_exe_daemon_client = __esm({
|
|
|
821
1406
|
"src/lib/exe-daemon-client.ts"() {
|
|
822
1407
|
"use strict";
|
|
823
1408
|
init_config();
|
|
824
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
825
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
826
|
-
SPAWN_LOCK_PATH =
|
|
1409
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1410
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1411
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
827
1412
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
828
1413
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
829
1414
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -905,7 +1490,7 @@ __export(db_daemon_client_exports, {
|
|
|
905
1490
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
906
1491
|
initDaemonDbClient: () => initDaemonDbClient
|
|
907
1492
|
});
|
|
908
|
-
function
|
|
1493
|
+
function normalizeStatement2(stmt) {
|
|
909
1494
|
if (typeof stmt === "string") {
|
|
910
1495
|
return { sql: stmt, args: [] };
|
|
911
1496
|
}
|
|
@@ -929,7 +1514,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
929
1514
|
if (!_useDaemon || !isClientConnected()) {
|
|
930
1515
|
return fallbackClient.execute(stmt);
|
|
931
1516
|
}
|
|
932
|
-
const { sql, args } =
|
|
1517
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
933
1518
|
const response = await sendDaemonRequest({
|
|
934
1519
|
type: "db-execute",
|
|
935
1520
|
sql,
|
|
@@ -954,7 +1539,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
954
1539
|
if (!_useDaemon || !isClientConnected()) {
|
|
955
1540
|
return fallbackClient.batch(stmts, mode);
|
|
956
1541
|
}
|
|
957
|
-
const statements = stmts.map(
|
|
1542
|
+
const statements = stmts.map(normalizeStatement2);
|
|
958
1543
|
const response = await sendDaemonRequest({
|
|
959
1544
|
type: "db-batch",
|
|
960
1545
|
statements,
|
|
@@ -1049,6 +1634,18 @@ __export(database_exports, {
|
|
|
1049
1634
|
});
|
|
1050
1635
|
import { createClient } from "@libsql/client";
|
|
1051
1636
|
async function initDatabase(config) {
|
|
1637
|
+
if (_walCheckpointTimer) {
|
|
1638
|
+
clearInterval(_walCheckpointTimer);
|
|
1639
|
+
_walCheckpointTimer = null;
|
|
1640
|
+
}
|
|
1641
|
+
if (_daemonClient) {
|
|
1642
|
+
_daemonClient.close();
|
|
1643
|
+
_daemonClient = null;
|
|
1644
|
+
}
|
|
1645
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1646
|
+
_adapterClient.close();
|
|
1647
|
+
}
|
|
1648
|
+
_adapterClient = null;
|
|
1052
1649
|
if (_client) {
|
|
1053
1650
|
_client.close();
|
|
1054
1651
|
_client = null;
|
|
@@ -1062,6 +1659,7 @@ async function initDatabase(config) {
|
|
|
1062
1659
|
}
|
|
1063
1660
|
_client = createClient(opts);
|
|
1064
1661
|
_resilientClient = wrapWithRetry(_client);
|
|
1662
|
+
_adapterClient = _resilientClient;
|
|
1065
1663
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1066
1664
|
});
|
|
1067
1665
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1072,14 +1670,20 @@ async function initDatabase(config) {
|
|
|
1072
1670
|
});
|
|
1073
1671
|
}, 3e4);
|
|
1074
1672
|
_walCheckpointTimer.unref();
|
|
1673
|
+
if (process.env.DATABASE_URL) {
|
|
1674
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1675
|
+
}
|
|
1075
1676
|
}
|
|
1076
1677
|
function isInitialized() {
|
|
1077
|
-
return _client !== null;
|
|
1678
|
+
return _adapterClient !== null || _client !== null;
|
|
1078
1679
|
}
|
|
1079
1680
|
function getClient() {
|
|
1080
|
-
if (!
|
|
1681
|
+
if (!_adapterClient) {
|
|
1081
1682
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1082
1683
|
}
|
|
1684
|
+
if (process.env.DATABASE_URL) {
|
|
1685
|
+
return _adapterClient;
|
|
1686
|
+
}
|
|
1083
1687
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1084
1688
|
return _resilientClient;
|
|
1085
1689
|
}
|
|
@@ -1089,6 +1693,7 @@ function getClient() {
|
|
|
1089
1693
|
return _resilientClient;
|
|
1090
1694
|
}
|
|
1091
1695
|
async function initDaemonClient() {
|
|
1696
|
+
if (process.env.DATABASE_URL) return;
|
|
1092
1697
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1093
1698
|
if (!_resilientClient) return;
|
|
1094
1699
|
try {
|
|
@@ -2033,26 +2638,36 @@ async function ensureSchema() {
|
|
|
2033
2638
|
}
|
|
2034
2639
|
}
|
|
2035
2640
|
async function disposeDatabase() {
|
|
2641
|
+
if (_walCheckpointTimer) {
|
|
2642
|
+
clearInterval(_walCheckpointTimer);
|
|
2643
|
+
_walCheckpointTimer = null;
|
|
2644
|
+
}
|
|
2036
2645
|
if (_daemonClient) {
|
|
2037
2646
|
_daemonClient.close();
|
|
2038
2647
|
_daemonClient = null;
|
|
2039
2648
|
}
|
|
2649
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2650
|
+
_adapterClient.close();
|
|
2651
|
+
}
|
|
2652
|
+
_adapterClient = null;
|
|
2040
2653
|
if (_client) {
|
|
2041
2654
|
_client.close();
|
|
2042
2655
|
_client = null;
|
|
2043
2656
|
_resilientClient = null;
|
|
2044
2657
|
}
|
|
2045
2658
|
}
|
|
2046
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2659
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2047
2660
|
var init_database = __esm({
|
|
2048
2661
|
"src/lib/database.ts"() {
|
|
2049
2662
|
"use strict";
|
|
2050
2663
|
init_db_retry();
|
|
2051
2664
|
init_employees();
|
|
2665
|
+
init_database_adapter();
|
|
2052
2666
|
_client = null;
|
|
2053
2667
|
_resilientClient = null;
|
|
2054
2668
|
_walCheckpointTimer = null;
|
|
2055
2669
|
_daemonClient = null;
|
|
2670
|
+
_adapterClient = null;
|
|
2056
2671
|
initTurso = initDatabase;
|
|
2057
2672
|
disposeTurso = disposeDatabase;
|
|
2058
2673
|
}
|
|
@@ -2081,10 +2696,10 @@ var init_compress = __esm({
|
|
|
2081
2696
|
// src/lib/license.ts
|
|
2082
2697
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync } from "fs";
|
|
2083
2698
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2084
|
-
import
|
|
2699
|
+
import path6 from "path";
|
|
2085
2700
|
import { jwtVerify, importSPKI } from "jose";
|
|
2086
2701
|
function loadDeviceId() {
|
|
2087
|
-
const deviceJsonPath =
|
|
2702
|
+
const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
|
|
2088
2703
|
try {
|
|
2089
2704
|
if (existsSync5(deviceJsonPath)) {
|
|
2090
2705
|
const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
|
|
@@ -2109,9 +2724,9 @@ var init_license = __esm({
|
|
|
2109
2724
|
"src/lib/license.ts"() {
|
|
2110
2725
|
"use strict";
|
|
2111
2726
|
init_config();
|
|
2112
|
-
LICENSE_PATH =
|
|
2113
|
-
CACHE_PATH =
|
|
2114
|
-
DEVICE_ID_PATH =
|
|
2727
|
+
LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
|
|
2728
|
+
CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
|
|
2729
|
+
DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
|
|
2115
2730
|
}
|
|
2116
2731
|
});
|
|
2117
2732
|
|
|
@@ -2135,7 +2750,7 @@ __export(crdt_sync_exports, {
|
|
|
2135
2750
|
});
|
|
2136
2751
|
import * as Y from "yjs";
|
|
2137
2752
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2138
|
-
import
|
|
2753
|
+
import path7 from "path";
|
|
2139
2754
|
import { homedir } from "os";
|
|
2140
2755
|
function getStatePath() {
|
|
2141
2756
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -2291,7 +2906,7 @@ function persistState() {
|
|
|
2291
2906
|
if (!doc) return;
|
|
2292
2907
|
try {
|
|
2293
2908
|
const sp = getStatePath();
|
|
2294
|
-
const dir =
|
|
2909
|
+
const dir = path7.dirname(sp);
|
|
2295
2910
|
if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
|
|
2296
2911
|
const state = Y.encodeStateAsUpdate(doc);
|
|
2297
2912
|
writeFileSync3(sp, Buffer.from(state));
|
|
@@ -2335,7 +2950,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
2335
2950
|
var init_crdt_sync = __esm({
|
|
2336
2951
|
"src/lib/crdt-sync.ts"() {
|
|
2337
2952
|
"use strict";
|
|
2338
|
-
DEFAULT_STATE_PATH =
|
|
2953
|
+
DEFAULT_STATE_PATH = path7.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
2339
2954
|
_statePathOverride = null;
|
|
2340
2955
|
doc = null;
|
|
2341
2956
|
}
|
|
@@ -2371,14 +2986,14 @@ __export(cloud_sync_exports, {
|
|
|
2371
2986
|
});
|
|
2372
2987
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
2373
2988
|
import crypto2 from "crypto";
|
|
2374
|
-
import
|
|
2989
|
+
import path8 from "path";
|
|
2375
2990
|
import { homedir as homedir2 } from "os";
|
|
2376
2991
|
function sqlSafe(v) {
|
|
2377
2992
|
return v === void 0 ? null : v;
|
|
2378
2993
|
}
|
|
2379
2994
|
function logError(msg) {
|
|
2380
2995
|
try {
|
|
2381
|
-
const logPath =
|
|
2996
|
+
const logPath = path8.join(homedir2(), ".exe-os", "workers.log");
|
|
2382
2997
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
2383
2998
|
`);
|
|
2384
2999
|
} catch {
|
|
@@ -2773,7 +3388,7 @@ async function cloudSync(config) {
|
|
|
2773
3388
|
try {
|
|
2774
3389
|
const employees = await loadEmployees();
|
|
2775
3390
|
rosterResult.employees = employees.length;
|
|
2776
|
-
const idDir =
|
|
3391
|
+
const idDir = path8.join(EXE_AI_DIR, "identity");
|
|
2777
3392
|
if (existsSync7(idDir)) {
|
|
2778
3393
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
2779
3394
|
}
|
|
@@ -2814,9 +3429,9 @@ function consumeRosterDeletions() {
|
|
|
2814
3429
|
}
|
|
2815
3430
|
}
|
|
2816
3431
|
function buildRosterBlob(paths) {
|
|
2817
|
-
const rosterPath = paths?.rosterPath ??
|
|
2818
|
-
const identityDir = paths?.identityDir ??
|
|
2819
|
-
const configPath = paths?.configPath ??
|
|
3432
|
+
const rosterPath = paths?.rosterPath ?? path8.join(EXE_AI_DIR, "exe-employees.json");
|
|
3433
|
+
const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
|
|
3434
|
+
const configPath = paths?.configPath ?? path8.join(EXE_AI_DIR, "config.json");
|
|
2820
3435
|
let roster = [];
|
|
2821
3436
|
if (existsSync7(rosterPath)) {
|
|
2822
3437
|
try {
|
|
@@ -2828,7 +3443,7 @@ function buildRosterBlob(paths) {
|
|
|
2828
3443
|
if (existsSync7(identityDir)) {
|
|
2829
3444
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
2830
3445
|
try {
|
|
2831
|
-
identities[file] = readFileSync6(
|
|
3446
|
+
identities[file] = readFileSync6(path8.join(identityDir, file), "utf-8");
|
|
2832
3447
|
} catch {
|
|
2833
3448
|
}
|
|
2834
3449
|
}
|
|
@@ -2841,7 +3456,7 @@ function buildRosterBlob(paths) {
|
|
|
2841
3456
|
}
|
|
2842
3457
|
}
|
|
2843
3458
|
let agentConfig;
|
|
2844
|
-
const agentConfigPath =
|
|
3459
|
+
const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
2845
3460
|
if (existsSync7(agentConfigPath)) {
|
|
2846
3461
|
try {
|
|
2847
3462
|
agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
|
|
@@ -2920,7 +3535,7 @@ async function cloudPullRoster(config) {
|
|
|
2920
3535
|
}
|
|
2921
3536
|
}
|
|
2922
3537
|
function mergeConfig(remoteConfig, configPath) {
|
|
2923
|
-
const cfgPath = configPath ??
|
|
3538
|
+
const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
|
|
2924
3539
|
let local = {};
|
|
2925
3540
|
if (existsSync7(cfgPath)) {
|
|
2926
3541
|
try {
|
|
@@ -2929,14 +3544,14 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
2929
3544
|
}
|
|
2930
3545
|
}
|
|
2931
3546
|
const merged = { ...remoteConfig, ...local };
|
|
2932
|
-
const dir =
|
|
3547
|
+
const dir = path8.dirname(cfgPath);
|
|
2933
3548
|
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
2934
3549
|
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
2935
3550
|
}
|
|
2936
3551
|
async function mergeRosterFromRemote(remote, paths) {
|
|
2937
3552
|
return withRosterLock(async () => {
|
|
2938
3553
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2939
|
-
const identityDir = paths?.identityDir ??
|
|
3554
|
+
const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
|
|
2940
3555
|
const localEmployees = await loadEmployees(rosterPath);
|
|
2941
3556
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
2942
3557
|
let added = 0;
|
|
@@ -2958,7 +3573,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2958
3573
|
const remoteIdentity = remote.identities[matchedKey];
|
|
2959
3574
|
if (remoteIdentity) {
|
|
2960
3575
|
if (!existsSync7(identityDir)) mkdirSync3(identityDir, { recursive: true });
|
|
2961
|
-
const idPath =
|
|
3576
|
+
const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
|
|
2962
3577
|
let localIdentity = null;
|
|
2963
3578
|
try {
|
|
2964
3579
|
localIdentity = existsSync7(idPath) ? readFileSync6(idPath, "utf-8") : null;
|
|
@@ -2991,7 +3606,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2991
3606
|
}
|
|
2992
3607
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
2993
3608
|
try {
|
|
2994
|
-
const agentConfigPath =
|
|
3609
|
+
const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
2995
3610
|
let local = {};
|
|
2996
3611
|
if (existsSync7(agentConfigPath)) {
|
|
2997
3612
|
try {
|
|
@@ -3438,9 +4053,9 @@ var init_cloud_sync = __esm({
|
|
|
3438
4053
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
3439
4054
|
FETCH_TIMEOUT_MS = 3e4;
|
|
3440
4055
|
PUSH_BATCH_SIZE = 5e3;
|
|
3441
|
-
ROSTER_LOCK_PATH =
|
|
4056
|
+
ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
|
|
3442
4057
|
LOCK_STALE_MS = 3e4;
|
|
3443
|
-
ROSTER_DELETIONS_PATH =
|
|
4058
|
+
ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
|
|
3444
4059
|
}
|
|
3445
4060
|
});
|
|
3446
4061
|
|