@askexenow/exe-os 0.9.7 → 0.9.9
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 +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
|
@@ -19,9 +19,47 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
21
|
|
|
22
|
+
// src/lib/secure-files.ts
|
|
23
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
24
|
+
import { chmod, mkdir } from "fs/promises";
|
|
25
|
+
async function ensurePrivateDir(dirPath) {
|
|
26
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
27
|
+
try {
|
|
28
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function ensurePrivateDirSync(dirPath) {
|
|
33
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
34
|
+
try {
|
|
35
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function enforcePrivateFile(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function enforcePrivateFileSync(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
52
|
+
var init_secure_files = __esm({
|
|
53
|
+
"src/lib/secure-files.ts"() {
|
|
54
|
+
"use strict";
|
|
55
|
+
PRIVATE_DIR_MODE = 448;
|
|
56
|
+
PRIVATE_FILE_MODE = 384;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
22
60
|
// src/lib/config.ts
|
|
23
|
-
import { readFile, writeFile
|
|
24
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
61
|
+
import { readFile, writeFile } from "fs/promises";
|
|
62
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
25
63
|
import path from "path";
|
|
26
64
|
import os from "os";
|
|
27
65
|
function resolveDataDir() {
|
|
@@ -29,7 +67,7 @@ function resolveDataDir() {
|
|
|
29
67
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
30
68
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
31
69
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
32
|
-
if (!
|
|
70
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
33
71
|
try {
|
|
34
72
|
renameSync(legacyDir, newDir);
|
|
35
73
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -92,9 +130,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
92
130
|
}
|
|
93
131
|
async function loadConfig() {
|
|
94
132
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
95
|
-
await
|
|
133
|
+
await ensurePrivateDir(dir);
|
|
96
134
|
const configPath = path.join(dir, "config.json");
|
|
97
|
-
if (!
|
|
135
|
+
if (!existsSync2(configPath)) {
|
|
98
136
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
99
137
|
}
|
|
100
138
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -107,6 +145,7 @@ async function loadConfig() {
|
|
|
107
145
|
`);
|
|
108
146
|
try {
|
|
109
147
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
148
|
+
await enforcePrivateFile(configPath);
|
|
110
149
|
} catch {
|
|
111
150
|
}
|
|
112
151
|
}
|
|
@@ -126,6 +165,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
126
165
|
var init_config = __esm({
|
|
127
166
|
"src/lib/config.ts"() {
|
|
128
167
|
"use strict";
|
|
168
|
+
init_secure_files();
|
|
129
169
|
EXE_AI_DIR = resolveDataDir();
|
|
130
170
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
131
171
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -272,7 +312,7 @@ var init_session_key = __esm({
|
|
|
272
312
|
|
|
273
313
|
// src/lib/employees.ts
|
|
274
314
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
275
|
-
import { existsSync as
|
|
315
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
276
316
|
import { execSync as execSync2 } from "child_process";
|
|
277
317
|
import path2 from "path";
|
|
278
318
|
import os2 from "os";
|
|
@@ -289,7 +329,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
289
329
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
290
330
|
}
|
|
291
331
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
292
|
-
if (!
|
|
332
|
+
if (!existsSync3(employeesPath)) return [];
|
|
293
333
|
try {
|
|
294
334
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
295
335
|
} catch {
|
|
@@ -299,7 +339,7 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
299
339
|
function getEmployee(employees, name) {
|
|
300
340
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
301
341
|
}
|
|
302
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
342
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
303
343
|
var init_employees = __esm({
|
|
304
344
|
"src/lib/employees.ts"() {
|
|
305
345
|
"use strict";
|
|
@@ -307,6 +347,7 @@ var init_employees = __esm({
|
|
|
307
347
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
308
348
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
309
349
|
COORDINATOR_ROLE = "COO";
|
|
350
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
310
351
|
}
|
|
311
352
|
});
|
|
312
353
|
|
|
@@ -484,7 +525,7 @@ var init_runtime_table = __esm({
|
|
|
484
525
|
});
|
|
485
526
|
|
|
486
527
|
// src/lib/agent-config.ts
|
|
487
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as
|
|
528
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
488
529
|
import path5 from "path";
|
|
489
530
|
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
490
531
|
var init_agent_config = __esm({
|
|
@@ -492,6 +533,7 @@ var init_agent_config = __esm({
|
|
|
492
533
|
"use strict";
|
|
493
534
|
init_config();
|
|
494
535
|
init_runtime_table();
|
|
536
|
+
init_secure_files();
|
|
495
537
|
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
496
538
|
DEFAULT_MODELS = {
|
|
497
539
|
claude: "claude-opus-4",
|
|
@@ -502,7 +544,7 @@ var init_agent_config = __esm({
|
|
|
502
544
|
});
|
|
503
545
|
|
|
504
546
|
// src/lib/intercom-queue.ts
|
|
505
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as
|
|
547
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
506
548
|
import path6 from "path";
|
|
507
549
|
import os4 from "os";
|
|
508
550
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
@@ -570,13 +612,634 @@ var init_db_retry = __esm({
|
|
|
570
612
|
}
|
|
571
613
|
});
|
|
572
614
|
|
|
615
|
+
// src/lib/database-adapter.ts
|
|
616
|
+
import os5 from "os";
|
|
617
|
+
import path7 from "path";
|
|
618
|
+
import { createRequire } from "module";
|
|
619
|
+
import { pathToFileURL } from "url";
|
|
620
|
+
function quotedIdentifier(identifier) {
|
|
621
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
622
|
+
}
|
|
623
|
+
function unqualifiedTableName(name) {
|
|
624
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
625
|
+
const parts = raw.split(".");
|
|
626
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
627
|
+
}
|
|
628
|
+
function stripTrailingSemicolon(sql) {
|
|
629
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
630
|
+
}
|
|
631
|
+
function appendClause(sql, clause) {
|
|
632
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
633
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
634
|
+
if (!returningMatch) {
|
|
635
|
+
return `${trimmed}${clause}`;
|
|
636
|
+
}
|
|
637
|
+
const idx = returningMatch.index;
|
|
638
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
639
|
+
}
|
|
640
|
+
function normalizeStatement(stmt) {
|
|
641
|
+
if (typeof stmt === "string") {
|
|
642
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
643
|
+
}
|
|
644
|
+
const sql = stmt.sql;
|
|
645
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
646
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
647
|
+
}
|
|
648
|
+
return { kind: "named", sql, args: stmt.args };
|
|
649
|
+
}
|
|
650
|
+
function rewriteBooleanLiterals(sql) {
|
|
651
|
+
let out = sql;
|
|
652
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
653
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
654
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
655
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
656
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
657
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
658
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
659
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
660
|
+
}
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
function rewriteInsertOrIgnore(sql) {
|
|
664
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
665
|
+
return sql;
|
|
666
|
+
}
|
|
667
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
668
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
669
|
+
}
|
|
670
|
+
function rewriteInsertOrReplace(sql) {
|
|
671
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
672
|
+
if (!match) {
|
|
673
|
+
return sql;
|
|
674
|
+
}
|
|
675
|
+
const rawTable = match[1];
|
|
676
|
+
const rawColumns = match[2];
|
|
677
|
+
const remainder = match[3];
|
|
678
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
679
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
680
|
+
if (!conflictKeys?.length) {
|
|
681
|
+
return sql;
|
|
682
|
+
}
|
|
683
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
684
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
685
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
686
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
687
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
688
|
+
}
|
|
689
|
+
function rewriteSql(sql) {
|
|
690
|
+
let out = sql;
|
|
691
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
692
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
693
|
+
out = rewriteBooleanLiterals(out);
|
|
694
|
+
out = rewriteInsertOrReplace(out);
|
|
695
|
+
out = rewriteInsertOrIgnore(out);
|
|
696
|
+
return stripTrailingSemicolon(out);
|
|
697
|
+
}
|
|
698
|
+
function toBoolean(value) {
|
|
699
|
+
if (value === null || value === void 0) return value;
|
|
700
|
+
if (typeof value === "boolean") return value;
|
|
701
|
+
if (typeof value === "number") return value !== 0;
|
|
702
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
703
|
+
if (typeof value === "string") {
|
|
704
|
+
const normalized = value.trim().toLowerCase();
|
|
705
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
706
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
707
|
+
}
|
|
708
|
+
return Boolean(value);
|
|
709
|
+
}
|
|
710
|
+
function countQuestionMarks(sql, end) {
|
|
711
|
+
let count = 0;
|
|
712
|
+
let inSingle = false;
|
|
713
|
+
let inDouble = false;
|
|
714
|
+
let inLineComment = false;
|
|
715
|
+
let inBlockComment = false;
|
|
716
|
+
for (let i = 0; i < end; i++) {
|
|
717
|
+
const ch = sql[i];
|
|
718
|
+
const next = sql[i + 1];
|
|
719
|
+
if (inLineComment) {
|
|
720
|
+
if (ch === "\n") inLineComment = false;
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
if (inBlockComment) {
|
|
724
|
+
if (ch === "*" && next === "/") {
|
|
725
|
+
inBlockComment = false;
|
|
726
|
+
i += 1;
|
|
727
|
+
}
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
731
|
+
inLineComment = true;
|
|
732
|
+
i += 1;
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
736
|
+
inBlockComment = true;
|
|
737
|
+
i += 1;
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
741
|
+
inSingle = !inSingle;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
745
|
+
inDouble = !inDouble;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
749
|
+
count += 1;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return count;
|
|
753
|
+
}
|
|
754
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
755
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
756
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
757
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
758
|
+
for (const match of sql.matchAll(pattern)) {
|
|
759
|
+
const matchText = match[0];
|
|
760
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
761
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return indexes;
|
|
765
|
+
}
|
|
766
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
767
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
768
|
+
if (!match) return;
|
|
769
|
+
const rawTable = match[1];
|
|
770
|
+
const rawColumns = match[2];
|
|
771
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
772
|
+
if (!boolColumns?.size) return;
|
|
773
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
774
|
+
for (const [index, column] of columns.entries()) {
|
|
775
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
776
|
+
args[index] = toBoolean(args[index]);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
781
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
782
|
+
if (!match) return;
|
|
783
|
+
const rawTable = match[1];
|
|
784
|
+
const setClause = match[2];
|
|
785
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
786
|
+
if (!boolColumns?.size) return;
|
|
787
|
+
const assignments = setClause.split(",");
|
|
788
|
+
let placeholderIndex = 0;
|
|
789
|
+
for (const assignment of assignments) {
|
|
790
|
+
if (!assignment.includes("?")) continue;
|
|
791
|
+
placeholderIndex += 1;
|
|
792
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
793
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
794
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function coerceBooleanArgs(sql, args) {
|
|
799
|
+
const nextArgs = [...args];
|
|
800
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
801
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
802
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
803
|
+
for (const index of placeholderIndexes) {
|
|
804
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
805
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return nextArgs;
|
|
809
|
+
}
|
|
810
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
811
|
+
let out = "";
|
|
812
|
+
let placeholder = 0;
|
|
813
|
+
let inSingle = false;
|
|
814
|
+
let inDouble = false;
|
|
815
|
+
let inLineComment = false;
|
|
816
|
+
let inBlockComment = false;
|
|
817
|
+
for (let i = 0; i < sql.length; i++) {
|
|
818
|
+
const ch = sql[i];
|
|
819
|
+
const next = sql[i + 1];
|
|
820
|
+
if (inLineComment) {
|
|
821
|
+
out += ch;
|
|
822
|
+
if (ch === "\n") inLineComment = false;
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
if (inBlockComment) {
|
|
826
|
+
out += ch;
|
|
827
|
+
if (ch === "*" && next === "/") {
|
|
828
|
+
out += next;
|
|
829
|
+
inBlockComment = false;
|
|
830
|
+
i += 1;
|
|
831
|
+
}
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
835
|
+
out += ch + next;
|
|
836
|
+
inLineComment = true;
|
|
837
|
+
i += 1;
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
841
|
+
out += ch + next;
|
|
842
|
+
inBlockComment = true;
|
|
843
|
+
i += 1;
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
847
|
+
inSingle = !inSingle;
|
|
848
|
+
out += ch;
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
852
|
+
inDouble = !inDouble;
|
|
853
|
+
out += ch;
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
857
|
+
placeholder += 1;
|
|
858
|
+
out += `$${placeholder}`;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
out += ch;
|
|
862
|
+
}
|
|
863
|
+
return out;
|
|
864
|
+
}
|
|
865
|
+
function translateStatementForPostgres(stmt) {
|
|
866
|
+
const normalized = normalizeStatement(stmt);
|
|
867
|
+
if (normalized.kind === "named") {
|
|
868
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
869
|
+
}
|
|
870
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
871
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
872
|
+
return {
|
|
873
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
874
|
+
args: coercedArgs
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
function shouldBypassPostgres(stmt) {
|
|
878
|
+
const normalized = normalizeStatement(stmt);
|
|
879
|
+
if (normalized.kind === "named") {
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
883
|
+
}
|
|
884
|
+
function shouldFallbackOnError(error) {
|
|
885
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
886
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
887
|
+
}
|
|
888
|
+
function isReadQuery(sql) {
|
|
889
|
+
const trimmed = sql.trimStart();
|
|
890
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
891
|
+
}
|
|
892
|
+
function buildRow(row, columns) {
|
|
893
|
+
const values = columns.map((column) => row[column]);
|
|
894
|
+
return Object.assign(values, row);
|
|
895
|
+
}
|
|
896
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
897
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
898
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
899
|
+
return {
|
|
900
|
+
columns,
|
|
901
|
+
columnTypes: columns.map(() => ""),
|
|
902
|
+
rows: resultRows,
|
|
903
|
+
rowsAffected,
|
|
904
|
+
lastInsertRowid: void 0,
|
|
905
|
+
toJSON() {
|
|
906
|
+
return {
|
|
907
|
+
columns,
|
|
908
|
+
columnTypes: columns.map(() => ""),
|
|
909
|
+
rows,
|
|
910
|
+
rowsAffected,
|
|
911
|
+
lastInsertRowid: void 0
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
async function loadPrismaClient() {
|
|
917
|
+
if (!prismaClientPromise) {
|
|
918
|
+
prismaClientPromise = (async () => {
|
|
919
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
920
|
+
if (explicitPath) {
|
|
921
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
922
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
923
|
+
if (!PrismaClient2) {
|
|
924
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
925
|
+
}
|
|
926
|
+
return new PrismaClient2();
|
|
927
|
+
}
|
|
928
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
|
|
929
|
+
const requireFromExeDb = createRequire(path7.join(exeDbRoot, "package.json"));
|
|
930
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
931
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
932
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
933
|
+
if (!PrismaClient) {
|
|
934
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
935
|
+
}
|
|
936
|
+
return new PrismaClient();
|
|
937
|
+
})();
|
|
938
|
+
}
|
|
939
|
+
return prismaClientPromise;
|
|
940
|
+
}
|
|
941
|
+
async function ensureCompatibilityViews(prisma) {
|
|
942
|
+
if (!compatibilityBootstrapPromise) {
|
|
943
|
+
compatibilityBootstrapPromise = (async () => {
|
|
944
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
945
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
946
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
947
|
+
"SELECT to_regclass($1) AS regclass",
|
|
948
|
+
relation
|
|
949
|
+
);
|
|
950
|
+
if (!rows[0]?.regclass) {
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
await prisma.$executeRawUnsafe(
|
|
954
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
})();
|
|
958
|
+
}
|
|
959
|
+
return compatibilityBootstrapPromise;
|
|
960
|
+
}
|
|
961
|
+
async function executeOnPrisma(executor, stmt) {
|
|
962
|
+
const translated = translateStatementForPostgres(stmt);
|
|
963
|
+
if (isReadQuery(translated.sql)) {
|
|
964
|
+
const rows = await executor.$queryRawUnsafe(
|
|
965
|
+
translated.sql,
|
|
966
|
+
...translated.args
|
|
967
|
+
);
|
|
968
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
969
|
+
}
|
|
970
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
971
|
+
return buildResultSet([], rowsAffected);
|
|
972
|
+
}
|
|
973
|
+
function splitSqlStatements(sql) {
|
|
974
|
+
const parts = [];
|
|
975
|
+
let current = "";
|
|
976
|
+
let inSingle = false;
|
|
977
|
+
let inDouble = false;
|
|
978
|
+
let inLineComment = false;
|
|
979
|
+
let inBlockComment = false;
|
|
980
|
+
for (let i = 0; i < sql.length; i++) {
|
|
981
|
+
const ch = sql[i];
|
|
982
|
+
const next = sql[i + 1];
|
|
983
|
+
if (inLineComment) {
|
|
984
|
+
current += ch;
|
|
985
|
+
if (ch === "\n") inLineComment = false;
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
if (inBlockComment) {
|
|
989
|
+
current += ch;
|
|
990
|
+
if (ch === "*" && next === "/") {
|
|
991
|
+
current += next;
|
|
992
|
+
inBlockComment = false;
|
|
993
|
+
i += 1;
|
|
994
|
+
}
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
998
|
+
current += ch + next;
|
|
999
|
+
inLineComment = true;
|
|
1000
|
+
i += 1;
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1004
|
+
current += ch + next;
|
|
1005
|
+
inBlockComment = true;
|
|
1006
|
+
i += 1;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1010
|
+
inSingle = !inSingle;
|
|
1011
|
+
current += ch;
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1015
|
+
inDouble = !inDouble;
|
|
1016
|
+
current += ch;
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1020
|
+
if (current.trim()) {
|
|
1021
|
+
parts.push(current.trim());
|
|
1022
|
+
}
|
|
1023
|
+
current = "";
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
current += ch;
|
|
1027
|
+
}
|
|
1028
|
+
if (current.trim()) {
|
|
1029
|
+
parts.push(current.trim());
|
|
1030
|
+
}
|
|
1031
|
+
return parts;
|
|
1032
|
+
}
|
|
1033
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1034
|
+
const prisma = await loadPrismaClient();
|
|
1035
|
+
await ensureCompatibilityViews(prisma);
|
|
1036
|
+
let closed = false;
|
|
1037
|
+
let adapter;
|
|
1038
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1039
|
+
if (!fallbackClient) {
|
|
1040
|
+
if (error) throw error;
|
|
1041
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1042
|
+
}
|
|
1043
|
+
if (error) {
|
|
1044
|
+
process.stderr.write(
|
|
1045
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1046
|
+
`
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
return fallbackClient.execute(stmt);
|
|
1050
|
+
};
|
|
1051
|
+
adapter = {
|
|
1052
|
+
async execute(stmt) {
|
|
1053
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1054
|
+
return fallbackExecute(stmt);
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
if (shouldFallbackOnError(error)) {
|
|
1060
|
+
return fallbackExecute(stmt, error);
|
|
1061
|
+
}
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
async batch(stmts, mode) {
|
|
1066
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1067
|
+
if (!fallbackClient) {
|
|
1068
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1069
|
+
}
|
|
1070
|
+
return fallbackClient.batch(stmts, mode);
|
|
1071
|
+
}
|
|
1072
|
+
try {
|
|
1073
|
+
if (prisma.$transaction) {
|
|
1074
|
+
return await prisma.$transaction(async (tx) => {
|
|
1075
|
+
const results2 = [];
|
|
1076
|
+
for (const stmt of stmts) {
|
|
1077
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1078
|
+
}
|
|
1079
|
+
return results2;
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
const results = [];
|
|
1083
|
+
for (const stmt of stmts) {
|
|
1084
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1085
|
+
}
|
|
1086
|
+
return results;
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1089
|
+
process.stderr.write(
|
|
1090
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1091
|
+
`
|
|
1092
|
+
);
|
|
1093
|
+
return fallbackClient.batch(stmts, mode);
|
|
1094
|
+
}
|
|
1095
|
+
throw error;
|
|
1096
|
+
}
|
|
1097
|
+
},
|
|
1098
|
+
async migrate(stmts) {
|
|
1099
|
+
if (fallbackClient) {
|
|
1100
|
+
return fallbackClient.migrate(stmts);
|
|
1101
|
+
}
|
|
1102
|
+
return adapter.batch(stmts, "deferred");
|
|
1103
|
+
},
|
|
1104
|
+
async transaction(mode) {
|
|
1105
|
+
if (!fallbackClient) {
|
|
1106
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1107
|
+
}
|
|
1108
|
+
return fallbackClient.transaction(mode);
|
|
1109
|
+
},
|
|
1110
|
+
async executeMultiple(sql) {
|
|
1111
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1112
|
+
return fallbackClient.executeMultiple(sql);
|
|
1113
|
+
}
|
|
1114
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1115
|
+
await adapter.execute(statement);
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
async sync() {
|
|
1119
|
+
if (fallbackClient) {
|
|
1120
|
+
return fallbackClient.sync();
|
|
1121
|
+
}
|
|
1122
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1123
|
+
},
|
|
1124
|
+
close() {
|
|
1125
|
+
closed = true;
|
|
1126
|
+
prismaClientPromise = null;
|
|
1127
|
+
compatibilityBootstrapPromise = null;
|
|
1128
|
+
void prisma.$disconnect?.();
|
|
1129
|
+
},
|
|
1130
|
+
get closed() {
|
|
1131
|
+
return closed;
|
|
1132
|
+
},
|
|
1133
|
+
get protocol() {
|
|
1134
|
+
return "prisma-postgres";
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
return adapter;
|
|
1138
|
+
}
|
|
1139
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1140
|
+
var init_database_adapter = __esm({
|
|
1141
|
+
"src/lib/database-adapter.ts"() {
|
|
1142
|
+
"use strict";
|
|
1143
|
+
VIEW_MAPPINGS = [
|
|
1144
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1145
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1146
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1147
|
+
{ view: "entities", source: "memory.entities" },
|
|
1148
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1149
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1150
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1151
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1152
|
+
{ view: "messages", source: "memory.messages" },
|
|
1153
|
+
{ view: "users", source: "wiki.users" },
|
|
1154
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1155
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1156
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1157
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1158
|
+
];
|
|
1159
|
+
UPSERT_KEYS = {
|
|
1160
|
+
memories: ["id"],
|
|
1161
|
+
tasks: ["id"],
|
|
1162
|
+
behaviors: ["id"],
|
|
1163
|
+
entities: ["id"],
|
|
1164
|
+
relationships: ["id"],
|
|
1165
|
+
entity_aliases: ["alias"],
|
|
1166
|
+
notifications: ["id"],
|
|
1167
|
+
messages: ["id"],
|
|
1168
|
+
users: ["id"],
|
|
1169
|
+
workspaces: ["id"],
|
|
1170
|
+
workspace_users: ["id"],
|
|
1171
|
+
documents: ["id"],
|
|
1172
|
+
chats: ["id"]
|
|
1173
|
+
};
|
|
1174
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1175
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1176
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1177
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1178
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1179
|
+
};
|
|
1180
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1181
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1182
|
+
);
|
|
1183
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1184
|
+
/\bPRAGMA\b/i,
|
|
1185
|
+
/\bsqlite_master\b/i,
|
|
1186
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1187
|
+
/\bMATCH\b/i,
|
|
1188
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1189
|
+
/\bjson_extract\s*\(/i,
|
|
1190
|
+
/\bjulianday\s*\(/i,
|
|
1191
|
+
/\bstrftime\s*\(/i,
|
|
1192
|
+
/\blast_insert_rowid\s*\(/i
|
|
1193
|
+
];
|
|
1194
|
+
prismaClientPromise = null;
|
|
1195
|
+
compatibilityBootstrapPromise = null;
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// src/lib/daemon-auth.ts
|
|
1200
|
+
import crypto from "crypto";
|
|
1201
|
+
import path8 from "path";
|
|
1202
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1203
|
+
function normalizeToken(token) {
|
|
1204
|
+
if (!token) return null;
|
|
1205
|
+
const trimmed = token.trim();
|
|
1206
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1207
|
+
}
|
|
1208
|
+
function readDaemonToken() {
|
|
1209
|
+
try {
|
|
1210
|
+
if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
|
|
1211
|
+
return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
|
|
1212
|
+
} catch {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function ensureDaemonToken(seed) {
|
|
1217
|
+
const existing = readDaemonToken();
|
|
1218
|
+
if (existing) return existing;
|
|
1219
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1220
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1221
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
1222
|
+
`, "utf8");
|
|
1223
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1224
|
+
return token;
|
|
1225
|
+
}
|
|
1226
|
+
var DAEMON_TOKEN_PATH;
|
|
1227
|
+
var init_daemon_auth = __esm({
|
|
1228
|
+
"src/lib/daemon-auth.ts"() {
|
|
1229
|
+
"use strict";
|
|
1230
|
+
init_config();
|
|
1231
|
+
init_secure_files();
|
|
1232
|
+
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
|
|
573
1236
|
// src/lib/exe-daemon-client.ts
|
|
574
1237
|
import net from "net";
|
|
575
|
-
import
|
|
1238
|
+
import os6 from "os";
|
|
576
1239
|
import { spawn } from "child_process";
|
|
577
1240
|
import { randomUUID } from "crypto";
|
|
578
|
-
import { existsSync as
|
|
579
|
-
import
|
|
1241
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
1242
|
+
import path9 from "path";
|
|
580
1243
|
import { fileURLToPath } from "url";
|
|
581
1244
|
function handleData(chunk) {
|
|
582
1245
|
_buffer += chunk.toString();
|
|
@@ -604,9 +1267,9 @@ function handleData(chunk) {
|
|
|
604
1267
|
}
|
|
605
1268
|
}
|
|
606
1269
|
function cleanupStaleFiles() {
|
|
607
|
-
if (
|
|
1270
|
+
if (existsSync7(PID_PATH)) {
|
|
608
1271
|
try {
|
|
609
|
-
const pid = parseInt(
|
|
1272
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
610
1273
|
if (pid > 0) {
|
|
611
1274
|
try {
|
|
612
1275
|
process.kill(pid, 0);
|
|
@@ -627,17 +1290,17 @@ function cleanupStaleFiles() {
|
|
|
627
1290
|
}
|
|
628
1291
|
}
|
|
629
1292
|
function findPackageRoot() {
|
|
630
|
-
let dir =
|
|
631
|
-
const { root } =
|
|
1293
|
+
let dir = path9.dirname(fileURLToPath(import.meta.url));
|
|
1294
|
+
const { root } = path9.parse(dir);
|
|
632
1295
|
while (dir !== root) {
|
|
633
|
-
if (
|
|
634
|
-
dir =
|
|
1296
|
+
if (existsSync7(path9.join(dir, "package.json"))) return dir;
|
|
1297
|
+
dir = path9.dirname(dir);
|
|
635
1298
|
}
|
|
636
1299
|
return null;
|
|
637
1300
|
}
|
|
638
1301
|
function spawnDaemon() {
|
|
639
|
-
const freeGB =
|
|
640
|
-
const totalGB =
|
|
1302
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1303
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
641
1304
|
if (totalGB <= 8) {
|
|
642
1305
|
process.stderr.write(
|
|
643
1306
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -657,16 +1320,17 @@ function spawnDaemon() {
|
|
|
657
1320
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
658
1321
|
return;
|
|
659
1322
|
}
|
|
660
|
-
const daemonPath =
|
|
661
|
-
if (!
|
|
1323
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1324
|
+
if (!existsSync7(daemonPath)) {
|
|
662
1325
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
663
1326
|
`);
|
|
664
1327
|
return;
|
|
665
1328
|
}
|
|
666
1329
|
const resolvedPath = daemonPath;
|
|
1330
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
667
1331
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
668
1332
|
`);
|
|
669
|
-
const logPath =
|
|
1333
|
+
const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
|
|
670
1334
|
let stderrFd = "ignore";
|
|
671
1335
|
try {
|
|
672
1336
|
stderrFd = openSync(logPath, "a");
|
|
@@ -684,7 +1348,8 @@ function spawnDaemon() {
|
|
|
684
1348
|
TMUX_PANE: void 0,
|
|
685
1349
|
// Prevents resolveExeSession() from scoping to one session
|
|
686
1350
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
687
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1351
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1352
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
688
1353
|
}
|
|
689
1354
|
});
|
|
690
1355
|
child.unref();
|
|
@@ -791,13 +1456,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
791
1456
|
return;
|
|
792
1457
|
}
|
|
793
1458
|
const id = randomUUID();
|
|
1459
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
794
1460
|
const timer = setTimeout(() => {
|
|
795
1461
|
_pending.delete(id);
|
|
796
1462
|
resolve({ error: "Request timeout" });
|
|
797
1463
|
}, timeoutMs);
|
|
798
1464
|
_pending.set(id, { resolve, timer });
|
|
799
1465
|
try {
|
|
800
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1466
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
801
1467
|
} catch {
|
|
802
1468
|
clearTimeout(timer);
|
|
803
1469
|
_pending.delete(id);
|
|
@@ -808,17 +1474,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
808
1474
|
function isClientConnected() {
|
|
809
1475
|
return _connected;
|
|
810
1476
|
}
|
|
811
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1477
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
812
1478
|
var init_exe_daemon_client = __esm({
|
|
813
1479
|
"src/lib/exe-daemon-client.ts"() {
|
|
814
1480
|
"use strict";
|
|
815
1481
|
init_config();
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1482
|
+
init_daemon_auth();
|
|
1483
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
|
|
1484
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
|
|
1485
|
+
SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
819
1486
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
820
1487
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
821
1488
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1489
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
822
1490
|
_socket = null;
|
|
823
1491
|
_connected = false;
|
|
824
1492
|
_buffer = "";
|
|
@@ -897,7 +1565,7 @@ __export(db_daemon_client_exports, {
|
|
|
897
1565
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
898
1566
|
initDaemonDbClient: () => initDaemonDbClient
|
|
899
1567
|
});
|
|
900
|
-
function
|
|
1568
|
+
function normalizeStatement2(stmt) {
|
|
901
1569
|
if (typeof stmt === "string") {
|
|
902
1570
|
return { sql: stmt, args: [] };
|
|
903
1571
|
}
|
|
@@ -921,7 +1589,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
921
1589
|
if (!_useDaemon || !isClientConnected()) {
|
|
922
1590
|
return fallbackClient.execute(stmt);
|
|
923
1591
|
}
|
|
924
|
-
const { sql, args } =
|
|
1592
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
925
1593
|
const response = await sendDaemonRequest({
|
|
926
1594
|
type: "db-execute",
|
|
927
1595
|
sql,
|
|
@@ -946,7 +1614,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
946
1614
|
if (!_useDaemon || !isClientConnected()) {
|
|
947
1615
|
return fallbackClient.batch(stmts, mode);
|
|
948
1616
|
}
|
|
949
|
-
const statements = stmts.map(
|
|
1617
|
+
const statements = stmts.map(normalizeStatement2);
|
|
950
1618
|
const response = await sendDaemonRequest({
|
|
951
1619
|
type: "db-batch",
|
|
952
1620
|
statements,
|
|
@@ -1041,6 +1709,18 @@ __export(database_exports, {
|
|
|
1041
1709
|
});
|
|
1042
1710
|
import { createClient } from "@libsql/client";
|
|
1043
1711
|
async function initDatabase(config) {
|
|
1712
|
+
if (_walCheckpointTimer) {
|
|
1713
|
+
clearInterval(_walCheckpointTimer);
|
|
1714
|
+
_walCheckpointTimer = null;
|
|
1715
|
+
}
|
|
1716
|
+
if (_daemonClient) {
|
|
1717
|
+
_daemonClient.close();
|
|
1718
|
+
_daemonClient = null;
|
|
1719
|
+
}
|
|
1720
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1721
|
+
_adapterClient.close();
|
|
1722
|
+
}
|
|
1723
|
+
_adapterClient = null;
|
|
1044
1724
|
if (_client) {
|
|
1045
1725
|
_client.close();
|
|
1046
1726
|
_client = null;
|
|
@@ -1054,6 +1734,7 @@ async function initDatabase(config) {
|
|
|
1054
1734
|
}
|
|
1055
1735
|
_client = createClient(opts);
|
|
1056
1736
|
_resilientClient = wrapWithRetry(_client);
|
|
1737
|
+
_adapterClient = _resilientClient;
|
|
1057
1738
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1058
1739
|
});
|
|
1059
1740
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1064,14 +1745,20 @@ async function initDatabase(config) {
|
|
|
1064
1745
|
});
|
|
1065
1746
|
}, 3e4);
|
|
1066
1747
|
_walCheckpointTimer.unref();
|
|
1748
|
+
if (process.env.DATABASE_URL) {
|
|
1749
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1750
|
+
}
|
|
1067
1751
|
}
|
|
1068
1752
|
function isInitialized() {
|
|
1069
|
-
return _client !== null;
|
|
1753
|
+
return _adapterClient !== null || _client !== null;
|
|
1070
1754
|
}
|
|
1071
1755
|
function getClient() {
|
|
1072
|
-
if (!
|
|
1756
|
+
if (!_adapterClient) {
|
|
1073
1757
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1074
1758
|
}
|
|
1759
|
+
if (process.env.DATABASE_URL) {
|
|
1760
|
+
return _adapterClient;
|
|
1761
|
+
}
|
|
1075
1762
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1076
1763
|
return _resilientClient;
|
|
1077
1764
|
}
|
|
@@ -1081,6 +1768,7 @@ function getClient() {
|
|
|
1081
1768
|
return _resilientClient;
|
|
1082
1769
|
}
|
|
1083
1770
|
async function initDaemonClient() {
|
|
1771
|
+
if (process.env.DATABASE_URL) return;
|
|
1084
1772
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1085
1773
|
if (!_resilientClient) return;
|
|
1086
1774
|
try {
|
|
@@ -1377,6 +2065,7 @@ async function ensureSchema() {
|
|
|
1377
2065
|
project TEXT NOT NULL,
|
|
1378
2066
|
summary TEXT NOT NULL,
|
|
1379
2067
|
task_file TEXT,
|
|
2068
|
+
session_scope TEXT,
|
|
1380
2069
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1381
2070
|
created_at TEXT NOT NULL
|
|
1382
2071
|
);
|
|
@@ -1385,7 +2074,7 @@ async function ensureSchema() {
|
|
|
1385
2074
|
ON notifications(read);
|
|
1386
2075
|
|
|
1387
2076
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1388
|
-
ON notifications(agent_id);
|
|
2077
|
+
ON notifications(agent_id, session_scope);
|
|
1389
2078
|
|
|
1390
2079
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1391
2080
|
ON notifications(task_file);
|
|
@@ -1423,6 +2112,7 @@ async function ensureSchema() {
|
|
|
1423
2112
|
target_agent TEXT NOT NULL,
|
|
1424
2113
|
target_project TEXT,
|
|
1425
2114
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2115
|
+
session_scope TEXT,
|
|
1426
2116
|
content TEXT NOT NULL,
|
|
1427
2117
|
priority TEXT DEFAULT 'normal',
|
|
1428
2118
|
status TEXT DEFAULT 'pending',
|
|
@@ -1436,10 +2126,31 @@ async function ensureSchema() {
|
|
|
1436
2126
|
);
|
|
1437
2127
|
|
|
1438
2128
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1439
|
-
ON messages(target_agent, status);
|
|
2129
|
+
ON messages(target_agent, session_scope, status);
|
|
1440
2130
|
|
|
1441
2131
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1442
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2132
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2133
|
+
`);
|
|
2134
|
+
try {
|
|
2135
|
+
await client.execute({
|
|
2136
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2137
|
+
args: []
|
|
2138
|
+
});
|
|
2139
|
+
} catch {
|
|
2140
|
+
}
|
|
2141
|
+
try {
|
|
2142
|
+
await client.execute({
|
|
2143
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2144
|
+
args: []
|
|
2145
|
+
});
|
|
2146
|
+
} catch {
|
|
2147
|
+
}
|
|
2148
|
+
await client.executeMultiple(`
|
|
2149
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2150
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2151
|
+
|
|
2152
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2153
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1443
2154
|
`);
|
|
1444
2155
|
try {
|
|
1445
2156
|
await client.execute({
|
|
@@ -2023,52 +2734,72 @@ async function ensureSchema() {
|
|
|
2023
2734
|
} catch {
|
|
2024
2735
|
}
|
|
2025
2736
|
}
|
|
2737
|
+
try {
|
|
2738
|
+
await client.execute({
|
|
2739
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2740
|
+
args: []
|
|
2741
|
+
});
|
|
2742
|
+
} catch {
|
|
2743
|
+
}
|
|
2026
2744
|
}
|
|
2027
2745
|
async function disposeDatabase() {
|
|
2746
|
+
if (_walCheckpointTimer) {
|
|
2747
|
+
clearInterval(_walCheckpointTimer);
|
|
2748
|
+
_walCheckpointTimer = null;
|
|
2749
|
+
}
|
|
2028
2750
|
if (_daemonClient) {
|
|
2029
2751
|
_daemonClient.close();
|
|
2030
2752
|
_daemonClient = null;
|
|
2031
2753
|
}
|
|
2754
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2755
|
+
_adapterClient.close();
|
|
2756
|
+
}
|
|
2757
|
+
_adapterClient = null;
|
|
2032
2758
|
if (_client) {
|
|
2033
2759
|
_client.close();
|
|
2034
2760
|
_client = null;
|
|
2035
2761
|
_resilientClient = null;
|
|
2036
2762
|
}
|
|
2037
2763
|
}
|
|
2038
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2764
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2039
2765
|
var init_database = __esm({
|
|
2040
2766
|
"src/lib/database.ts"() {
|
|
2041
2767
|
"use strict";
|
|
2042
2768
|
init_db_retry();
|
|
2043
2769
|
init_employees();
|
|
2770
|
+
init_database_adapter();
|
|
2044
2771
|
_client = null;
|
|
2045
2772
|
_resilientClient = null;
|
|
2046
2773
|
_walCheckpointTimer = null;
|
|
2047
2774
|
_daemonClient = null;
|
|
2775
|
+
_adapterClient = null;
|
|
2048
2776
|
initTurso = initDatabase;
|
|
2049
2777
|
disposeTurso = disposeDatabase;
|
|
2050
2778
|
}
|
|
2051
2779
|
});
|
|
2052
2780
|
|
|
2053
2781
|
// src/lib/license.ts
|
|
2054
|
-
import { readFileSync as
|
|
2782
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2055
2783
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2056
|
-
import
|
|
2784
|
+
import { createRequire as createRequire2 } from "module";
|
|
2785
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2786
|
+
import os7 from "os";
|
|
2787
|
+
import path10 from "path";
|
|
2057
2788
|
import { jwtVerify, importSPKI } from "jose";
|
|
2058
2789
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
2059
2790
|
var init_license = __esm({
|
|
2060
2791
|
"src/lib/license.ts"() {
|
|
2061
2792
|
"use strict";
|
|
2062
2793
|
init_config();
|
|
2063
|
-
LICENSE_PATH =
|
|
2064
|
-
CACHE_PATH =
|
|
2065
|
-
DEVICE_ID_PATH =
|
|
2794
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
2795
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2796
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
2066
2797
|
}
|
|
2067
2798
|
});
|
|
2068
2799
|
|
|
2069
2800
|
// src/lib/plan-limits.ts
|
|
2070
|
-
import { readFileSync as
|
|
2071
|
-
import
|
|
2801
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2802
|
+
import path11 from "path";
|
|
2072
2803
|
var CACHE_PATH2;
|
|
2073
2804
|
var init_plan_limits = __esm({
|
|
2074
2805
|
"src/lib/plan-limits.ts"() {
|
|
@@ -2077,14 +2808,14 @@ var init_plan_limits = __esm({
|
|
|
2077
2808
|
init_employees();
|
|
2078
2809
|
init_license();
|
|
2079
2810
|
init_config();
|
|
2080
|
-
CACHE_PATH2 =
|
|
2811
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
2081
2812
|
}
|
|
2082
2813
|
});
|
|
2083
2814
|
|
|
2084
2815
|
// src/lib/tmux-routing.ts
|
|
2085
|
-
import { readFileSync as
|
|
2086
|
-
import
|
|
2087
|
-
import
|
|
2816
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync, readdirSync as readdirSync2 } from "fs";
|
|
2817
|
+
import path12 from "path";
|
|
2818
|
+
import os8 from "os";
|
|
2088
2819
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2089
2820
|
function getMySession() {
|
|
2090
2821
|
return getTransport().getMySession();
|
|
@@ -2097,7 +2828,7 @@ function extractRootExe(name) {
|
|
|
2097
2828
|
}
|
|
2098
2829
|
function getParentExe(sessionKey) {
|
|
2099
2830
|
try {
|
|
2100
|
-
const data = JSON.parse(
|
|
2831
|
+
const data = JSON.parse(readFileSync10(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2101
2832
|
return data.parentExe || null;
|
|
2102
2833
|
} catch {
|
|
2103
2834
|
return null;
|
|
@@ -2140,10 +2871,10 @@ var init_tmux_routing = __esm({
|
|
|
2140
2871
|
init_intercom_queue();
|
|
2141
2872
|
init_plan_limits();
|
|
2142
2873
|
init_employees();
|
|
2143
|
-
SPAWN_LOCK_DIR =
|
|
2144
|
-
SESSION_CACHE =
|
|
2145
|
-
INTERCOM_LOG2 =
|
|
2146
|
-
DEBOUNCE_FILE =
|
|
2874
|
+
SPAWN_LOCK_DIR = path12.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
2875
|
+
SESSION_CACHE = path12.join(os8.homedir(), ".exe-os", "session-cache");
|
|
2876
|
+
INTERCOM_LOG2 = path12.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
2877
|
+
DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2147
2878
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2148
2879
|
}
|
|
2149
2880
|
});
|
|
@@ -2183,14 +2914,14 @@ var init_memory = __esm({
|
|
|
2183
2914
|
|
|
2184
2915
|
// src/lib/keychain.ts
|
|
2185
2916
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2186
|
-
import { existsSync as
|
|
2187
|
-
import
|
|
2188
|
-
import
|
|
2917
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2918
|
+
import path13 from "path";
|
|
2919
|
+
import os9 from "os";
|
|
2189
2920
|
function getKeyDir() {
|
|
2190
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2921
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path13.join(os9.homedir(), ".exe-os");
|
|
2191
2922
|
}
|
|
2192
2923
|
function getKeyPath() {
|
|
2193
|
-
return
|
|
2924
|
+
return path13.join(getKeyDir(), "master.key");
|
|
2194
2925
|
}
|
|
2195
2926
|
async function tryKeytar() {
|
|
2196
2927
|
try {
|
|
@@ -2211,9 +2942,9 @@ async function getMasterKey() {
|
|
|
2211
2942
|
}
|
|
2212
2943
|
}
|
|
2213
2944
|
const keyPath = getKeyPath();
|
|
2214
|
-
if (!
|
|
2945
|
+
if (!existsSync11(keyPath)) {
|
|
2215
2946
|
process.stderr.write(
|
|
2216
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
2947
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2217
2948
|
`
|
|
2218
2949
|
);
|
|
2219
2950
|
return null;
|
|
@@ -2298,6 +3029,7 @@ var shard_manager_exports = {};
|
|
|
2298
3029
|
__export(shard_manager_exports, {
|
|
2299
3030
|
disposeShards: () => disposeShards,
|
|
2300
3031
|
ensureShardSchema: () => ensureShardSchema,
|
|
3032
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2301
3033
|
getReadyShardClient: () => getReadyShardClient,
|
|
2302
3034
|
getShardClient: () => getShardClient,
|
|
2303
3035
|
getShardsDir: () => getShardsDir,
|
|
@@ -2306,15 +3038,18 @@ __export(shard_manager_exports, {
|
|
|
2306
3038
|
listShards: () => listShards,
|
|
2307
3039
|
shardExists: () => shardExists
|
|
2308
3040
|
});
|
|
2309
|
-
import
|
|
2310
|
-
import { existsSync as
|
|
3041
|
+
import path14 from "path";
|
|
3042
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
|
|
2311
3043
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2312
3044
|
function initShardManager(encryptionKey) {
|
|
2313
3045
|
_encryptionKey = encryptionKey;
|
|
2314
|
-
if (!
|
|
3046
|
+
if (!existsSync12(SHARDS_DIR)) {
|
|
2315
3047
|
mkdirSync6(SHARDS_DIR, { recursive: true });
|
|
2316
3048
|
}
|
|
2317
3049
|
_shardingEnabled = true;
|
|
3050
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3051
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3052
|
+
_evictionTimer.unref();
|
|
2318
3053
|
}
|
|
2319
3054
|
function isShardingEnabled() {
|
|
2320
3055
|
return _shardingEnabled;
|
|
@@ -2331,21 +3066,28 @@ function getShardClient(projectName) {
|
|
|
2331
3066
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2332
3067
|
}
|
|
2333
3068
|
const cached = _shards.get(safeName);
|
|
2334
|
-
if (cached)
|
|
2335
|
-
|
|
3069
|
+
if (cached) {
|
|
3070
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3071
|
+
return cached;
|
|
3072
|
+
}
|
|
3073
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3074
|
+
evictLRU();
|
|
3075
|
+
}
|
|
3076
|
+
const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
|
|
2336
3077
|
const client = createClient2({
|
|
2337
3078
|
url: `file:${dbPath}`,
|
|
2338
3079
|
encryptionKey: _encryptionKey
|
|
2339
3080
|
});
|
|
2340
3081
|
_shards.set(safeName, client);
|
|
3082
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2341
3083
|
return client;
|
|
2342
3084
|
}
|
|
2343
3085
|
function shardExists(projectName) {
|
|
2344
3086
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2345
|
-
return
|
|
3087
|
+
return existsSync12(path14.join(SHARDS_DIR, `${safeName}.db`));
|
|
2346
3088
|
}
|
|
2347
3089
|
function listShards() {
|
|
2348
|
-
if (!
|
|
3090
|
+
if (!existsSync12(SHARDS_DIR)) return [];
|
|
2349
3091
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2350
3092
|
}
|
|
2351
3093
|
async function ensureShardSchema(client) {
|
|
@@ -2397,6 +3139,8 @@ async function ensureShardSchema(client) {
|
|
|
2397
3139
|
for (const col of [
|
|
2398
3140
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2399
3141
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3142
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3143
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2400
3144
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2401
3145
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2402
3146
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2419,7 +3163,23 @@ async function ensureShardSchema(client) {
|
|
|
2419
3163
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2420
3164
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2421
3165
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2422
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3166
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3167
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3168
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3169
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3170
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3171
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3172
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3173
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3174
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3175
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3176
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3177
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3178
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3179
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3180
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3181
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3182
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2423
3183
|
]) {
|
|
2424
3184
|
try {
|
|
2425
3185
|
await client.execute(col);
|
|
@@ -2518,21 +3278,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2518
3278
|
await ensureShardSchema(client);
|
|
2519
3279
|
return client;
|
|
2520
3280
|
}
|
|
3281
|
+
function evictLRU() {
|
|
3282
|
+
let oldest = null;
|
|
3283
|
+
let oldestTime = Infinity;
|
|
3284
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3285
|
+
if (time < oldestTime) {
|
|
3286
|
+
oldestTime = time;
|
|
3287
|
+
oldest = name;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
if (oldest) {
|
|
3291
|
+
const client = _shards.get(oldest);
|
|
3292
|
+
if (client) {
|
|
3293
|
+
client.close();
|
|
3294
|
+
}
|
|
3295
|
+
_shards.delete(oldest);
|
|
3296
|
+
_shardLastAccess.delete(oldest);
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
function evictIdleShards() {
|
|
3300
|
+
const now = Date.now();
|
|
3301
|
+
const toEvict = [];
|
|
3302
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3303
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3304
|
+
toEvict.push(name);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
for (const name of toEvict) {
|
|
3308
|
+
const client = _shards.get(name);
|
|
3309
|
+
if (client) {
|
|
3310
|
+
client.close();
|
|
3311
|
+
}
|
|
3312
|
+
_shards.delete(name);
|
|
3313
|
+
_shardLastAccess.delete(name);
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
function getOpenShardCount() {
|
|
3317
|
+
return _shards.size;
|
|
3318
|
+
}
|
|
2521
3319
|
function disposeShards() {
|
|
3320
|
+
if (_evictionTimer) {
|
|
3321
|
+
clearInterval(_evictionTimer);
|
|
3322
|
+
_evictionTimer = null;
|
|
3323
|
+
}
|
|
2522
3324
|
for (const [, client] of _shards) {
|
|
2523
3325
|
client.close();
|
|
2524
3326
|
}
|
|
2525
3327
|
_shards.clear();
|
|
3328
|
+
_shardLastAccess.clear();
|
|
2526
3329
|
_shardingEnabled = false;
|
|
2527
3330
|
_encryptionKey = null;
|
|
2528
3331
|
}
|
|
2529
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3332
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2530
3333
|
var init_shard_manager = __esm({
|
|
2531
3334
|
"src/lib/shard-manager.ts"() {
|
|
2532
3335
|
"use strict";
|
|
2533
3336
|
init_config();
|
|
2534
|
-
SHARDS_DIR =
|
|
3337
|
+
SHARDS_DIR = path14.join(EXE_AI_DIR, "shards");
|
|
3338
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3339
|
+
MAX_OPEN_SHARDS = 10;
|
|
3340
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2535
3341
|
_shards = /* @__PURE__ */ new Map();
|
|
3342
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3343
|
+
_evictionTimer = null;
|
|
2536
3344
|
_encryptionKey = null;
|
|
2537
3345
|
_shardingEnabled = false;
|
|
2538
3346
|
}
|
|
@@ -3299,7 +4107,7 @@ var init_store = __esm({
|
|
|
3299
4107
|
init_config();
|
|
3300
4108
|
init_session_key();
|
|
3301
4109
|
init_employees();
|
|
3302
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
4110
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
3303
4111
|
import { execSync as execSync3 } from "child_process";
|
|
3304
4112
|
import path3 from "path";
|
|
3305
4113
|
var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
|