@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
|
@@ -64,9 +64,34 @@ var init_db_retry = __esm({
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
// src/lib/secure-files.ts
|
|
68
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
69
|
+
import { chmod, mkdir } from "fs/promises";
|
|
70
|
+
async function ensurePrivateDir(dirPath) {
|
|
71
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
72
|
+
try {
|
|
73
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function enforcePrivateFile(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
84
|
+
var init_secure_files = __esm({
|
|
85
|
+
"src/lib/secure-files.ts"() {
|
|
86
|
+
"use strict";
|
|
87
|
+
PRIVATE_DIR_MODE = 448;
|
|
88
|
+
PRIVATE_FILE_MODE = 384;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
67
92
|
// src/lib/config.ts
|
|
68
|
-
import { readFile, writeFile
|
|
69
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
93
|
+
import { readFile, writeFile } from "fs/promises";
|
|
94
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
70
95
|
import path from "path";
|
|
71
96
|
import os from "os";
|
|
72
97
|
function resolveDataDir() {
|
|
@@ -74,7 +99,7 @@ function resolveDataDir() {
|
|
|
74
99
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
75
100
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
76
101
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
77
|
-
if (!
|
|
102
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
78
103
|
try {
|
|
79
104
|
renameSync(legacyDir, newDir);
|
|
80
105
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -137,9 +162,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
137
162
|
}
|
|
138
163
|
async function loadConfig() {
|
|
139
164
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
140
|
-
await
|
|
165
|
+
await ensurePrivateDir(dir);
|
|
141
166
|
const configPath = path.join(dir, "config.json");
|
|
142
|
-
if (!
|
|
167
|
+
if (!existsSync2(configPath)) {
|
|
143
168
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
144
169
|
}
|
|
145
170
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -152,6 +177,7 @@ async function loadConfig() {
|
|
|
152
177
|
`);
|
|
153
178
|
try {
|
|
154
179
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
180
|
+
await enforcePrivateFile(configPath);
|
|
155
181
|
} catch {
|
|
156
182
|
}
|
|
157
183
|
}
|
|
@@ -171,6 +197,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
171
197
|
var init_config = __esm({
|
|
172
198
|
"src/lib/config.ts"() {
|
|
173
199
|
"use strict";
|
|
200
|
+
init_secure_files();
|
|
174
201
|
EXE_AI_DIR = resolveDataDir();
|
|
175
202
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
176
203
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -249,7 +276,7 @@ var init_config = __esm({
|
|
|
249
276
|
|
|
250
277
|
// src/lib/employees.ts
|
|
251
278
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
252
|
-
import { existsSync as
|
|
279
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
253
280
|
import { execSync } from "child_process";
|
|
254
281
|
import path2 from "path";
|
|
255
282
|
import os2 from "os";
|
|
@@ -266,7 +293,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
266
293
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
267
294
|
}
|
|
268
295
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
269
|
-
if (!
|
|
296
|
+
if (!existsSync3(employeesPath)) return [];
|
|
270
297
|
try {
|
|
271
298
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
272
299
|
} catch {
|
|
@@ -284,7 +311,7 @@ function baseAgentName(name, employees) {
|
|
|
284
311
|
if (getEmployee(roster, base)) return base;
|
|
285
312
|
return name;
|
|
286
313
|
}
|
|
287
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
314
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
288
315
|
var init_employees = __esm({
|
|
289
316
|
"src/lib/employees.ts"() {
|
|
290
317
|
"use strict";
|
|
@@ -292,12 +319,609 @@ var init_employees = __esm({
|
|
|
292
319
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
293
320
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
294
321
|
COORDINATOR_ROLE = "COO";
|
|
322
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// src/lib/database-adapter.ts
|
|
327
|
+
import os3 from "os";
|
|
328
|
+
import path3 from "path";
|
|
329
|
+
import { createRequire } from "module";
|
|
330
|
+
import { pathToFileURL } from "url";
|
|
331
|
+
function quotedIdentifier(identifier) {
|
|
332
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
333
|
+
}
|
|
334
|
+
function unqualifiedTableName(name) {
|
|
335
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
336
|
+
const parts = raw.split(".");
|
|
337
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
338
|
+
}
|
|
339
|
+
function stripTrailingSemicolon(sql) {
|
|
340
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
341
|
+
}
|
|
342
|
+
function appendClause(sql, clause) {
|
|
343
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
344
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
345
|
+
if (!returningMatch) {
|
|
346
|
+
return `${trimmed}${clause}`;
|
|
347
|
+
}
|
|
348
|
+
const idx = returningMatch.index;
|
|
349
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
350
|
+
}
|
|
351
|
+
function normalizeStatement(stmt) {
|
|
352
|
+
if (typeof stmt === "string") {
|
|
353
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
354
|
+
}
|
|
355
|
+
const sql = stmt.sql;
|
|
356
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
357
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
358
|
+
}
|
|
359
|
+
return { kind: "named", sql, args: stmt.args };
|
|
360
|
+
}
|
|
361
|
+
function rewriteBooleanLiterals(sql) {
|
|
362
|
+
let out = sql;
|
|
363
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
364
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
365
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
366
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
367
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
368
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
369
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
370
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
function rewriteInsertOrIgnore(sql) {
|
|
375
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
376
|
+
return sql;
|
|
377
|
+
}
|
|
378
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
379
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
380
|
+
}
|
|
381
|
+
function rewriteInsertOrReplace(sql) {
|
|
382
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
383
|
+
if (!match) {
|
|
384
|
+
return sql;
|
|
385
|
+
}
|
|
386
|
+
const rawTable = match[1];
|
|
387
|
+
const rawColumns = match[2];
|
|
388
|
+
const remainder = match[3];
|
|
389
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
390
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
391
|
+
if (!conflictKeys?.length) {
|
|
392
|
+
return sql;
|
|
393
|
+
}
|
|
394
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
395
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
396
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
397
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
398
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
399
|
+
}
|
|
400
|
+
function rewriteSql(sql) {
|
|
401
|
+
let out = sql;
|
|
402
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
403
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
404
|
+
out = rewriteBooleanLiterals(out);
|
|
405
|
+
out = rewriteInsertOrReplace(out);
|
|
406
|
+
out = rewriteInsertOrIgnore(out);
|
|
407
|
+
return stripTrailingSemicolon(out);
|
|
408
|
+
}
|
|
409
|
+
function toBoolean(value) {
|
|
410
|
+
if (value === null || value === void 0) return value;
|
|
411
|
+
if (typeof value === "boolean") return value;
|
|
412
|
+
if (typeof value === "number") return value !== 0;
|
|
413
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
414
|
+
if (typeof value === "string") {
|
|
415
|
+
const normalized = value.trim().toLowerCase();
|
|
416
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
417
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
418
|
+
}
|
|
419
|
+
return Boolean(value);
|
|
420
|
+
}
|
|
421
|
+
function countQuestionMarks(sql, end) {
|
|
422
|
+
let count = 0;
|
|
423
|
+
let inSingle = false;
|
|
424
|
+
let inDouble = false;
|
|
425
|
+
let inLineComment = false;
|
|
426
|
+
let inBlockComment = false;
|
|
427
|
+
for (let i = 0; i < end; i++) {
|
|
428
|
+
const ch = sql[i];
|
|
429
|
+
const next = sql[i + 1];
|
|
430
|
+
if (inLineComment) {
|
|
431
|
+
if (ch === "\n") inLineComment = false;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (inBlockComment) {
|
|
435
|
+
if (ch === "*" && next === "/") {
|
|
436
|
+
inBlockComment = false;
|
|
437
|
+
i += 1;
|
|
438
|
+
}
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
442
|
+
inLineComment = true;
|
|
443
|
+
i += 1;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
447
|
+
inBlockComment = true;
|
|
448
|
+
i += 1;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
452
|
+
inSingle = !inSingle;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
456
|
+
inDouble = !inDouble;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
460
|
+
count += 1;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return count;
|
|
464
|
+
}
|
|
465
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
466
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
467
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
468
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
469
|
+
for (const match of sql.matchAll(pattern)) {
|
|
470
|
+
const matchText = match[0];
|
|
471
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
472
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return indexes;
|
|
476
|
+
}
|
|
477
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
478
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
479
|
+
if (!match) return;
|
|
480
|
+
const rawTable = match[1];
|
|
481
|
+
const rawColumns = match[2];
|
|
482
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
483
|
+
if (!boolColumns?.size) return;
|
|
484
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
485
|
+
for (const [index, column] of columns.entries()) {
|
|
486
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
487
|
+
args[index] = toBoolean(args[index]);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
492
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
493
|
+
if (!match) return;
|
|
494
|
+
const rawTable = match[1];
|
|
495
|
+
const setClause = match[2];
|
|
496
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
497
|
+
if (!boolColumns?.size) return;
|
|
498
|
+
const assignments = setClause.split(",");
|
|
499
|
+
let placeholderIndex = 0;
|
|
500
|
+
for (const assignment of assignments) {
|
|
501
|
+
if (!assignment.includes("?")) continue;
|
|
502
|
+
placeholderIndex += 1;
|
|
503
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
504
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
505
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function coerceBooleanArgs(sql, args) {
|
|
510
|
+
const nextArgs = [...args];
|
|
511
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
512
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
513
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
514
|
+
for (const index of placeholderIndexes) {
|
|
515
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
516
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return nextArgs;
|
|
520
|
+
}
|
|
521
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
522
|
+
let out = "";
|
|
523
|
+
let placeholder = 0;
|
|
524
|
+
let inSingle = false;
|
|
525
|
+
let inDouble = false;
|
|
526
|
+
let inLineComment = false;
|
|
527
|
+
let inBlockComment = false;
|
|
528
|
+
for (let i = 0; i < sql.length; i++) {
|
|
529
|
+
const ch = sql[i];
|
|
530
|
+
const next = sql[i + 1];
|
|
531
|
+
if (inLineComment) {
|
|
532
|
+
out += ch;
|
|
533
|
+
if (ch === "\n") inLineComment = false;
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
if (inBlockComment) {
|
|
537
|
+
out += ch;
|
|
538
|
+
if (ch === "*" && next === "/") {
|
|
539
|
+
out += next;
|
|
540
|
+
inBlockComment = false;
|
|
541
|
+
i += 1;
|
|
542
|
+
}
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
546
|
+
out += ch + next;
|
|
547
|
+
inLineComment = true;
|
|
548
|
+
i += 1;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
552
|
+
out += ch + next;
|
|
553
|
+
inBlockComment = true;
|
|
554
|
+
i += 1;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
558
|
+
inSingle = !inSingle;
|
|
559
|
+
out += ch;
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
563
|
+
inDouble = !inDouble;
|
|
564
|
+
out += ch;
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
568
|
+
placeholder += 1;
|
|
569
|
+
out += `$${placeholder}`;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
out += ch;
|
|
573
|
+
}
|
|
574
|
+
return out;
|
|
575
|
+
}
|
|
576
|
+
function translateStatementForPostgres(stmt) {
|
|
577
|
+
const normalized = normalizeStatement(stmt);
|
|
578
|
+
if (normalized.kind === "named") {
|
|
579
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
580
|
+
}
|
|
581
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
582
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
583
|
+
return {
|
|
584
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
585
|
+
args: coercedArgs
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function shouldBypassPostgres(stmt) {
|
|
589
|
+
const normalized = normalizeStatement(stmt);
|
|
590
|
+
if (normalized.kind === "named") {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
594
|
+
}
|
|
595
|
+
function shouldFallbackOnError(error) {
|
|
596
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
597
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
598
|
+
}
|
|
599
|
+
function isReadQuery(sql) {
|
|
600
|
+
const trimmed = sql.trimStart();
|
|
601
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
602
|
+
}
|
|
603
|
+
function buildRow(row, columns) {
|
|
604
|
+
const values = columns.map((column) => row[column]);
|
|
605
|
+
return Object.assign(values, row);
|
|
606
|
+
}
|
|
607
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
608
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
609
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
610
|
+
return {
|
|
611
|
+
columns,
|
|
612
|
+
columnTypes: columns.map(() => ""),
|
|
613
|
+
rows: resultRows,
|
|
614
|
+
rowsAffected,
|
|
615
|
+
lastInsertRowid: void 0,
|
|
616
|
+
toJSON() {
|
|
617
|
+
return {
|
|
618
|
+
columns,
|
|
619
|
+
columnTypes: columns.map(() => ""),
|
|
620
|
+
rows,
|
|
621
|
+
rowsAffected,
|
|
622
|
+
lastInsertRowid: void 0
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async function loadPrismaClient() {
|
|
628
|
+
if (!prismaClientPromise) {
|
|
629
|
+
prismaClientPromise = (async () => {
|
|
630
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
631
|
+
if (explicitPath) {
|
|
632
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
633
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
634
|
+
if (!PrismaClient2) {
|
|
635
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
636
|
+
}
|
|
637
|
+
return new PrismaClient2();
|
|
638
|
+
}
|
|
639
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
640
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
641
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
642
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
643
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
644
|
+
if (!PrismaClient) {
|
|
645
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
646
|
+
}
|
|
647
|
+
return new PrismaClient();
|
|
648
|
+
})();
|
|
649
|
+
}
|
|
650
|
+
return prismaClientPromise;
|
|
651
|
+
}
|
|
652
|
+
async function ensureCompatibilityViews(prisma) {
|
|
653
|
+
if (!compatibilityBootstrapPromise) {
|
|
654
|
+
compatibilityBootstrapPromise = (async () => {
|
|
655
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
656
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
657
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
658
|
+
"SELECT to_regclass($1) AS regclass",
|
|
659
|
+
relation
|
|
660
|
+
);
|
|
661
|
+
if (!rows[0]?.regclass) {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
await prisma.$executeRawUnsafe(
|
|
665
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
})();
|
|
669
|
+
}
|
|
670
|
+
return compatibilityBootstrapPromise;
|
|
671
|
+
}
|
|
672
|
+
async function executeOnPrisma(executor, stmt) {
|
|
673
|
+
const translated = translateStatementForPostgres(stmt);
|
|
674
|
+
if (isReadQuery(translated.sql)) {
|
|
675
|
+
const rows = await executor.$queryRawUnsafe(
|
|
676
|
+
translated.sql,
|
|
677
|
+
...translated.args
|
|
678
|
+
);
|
|
679
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
680
|
+
}
|
|
681
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
682
|
+
return buildResultSet([], rowsAffected);
|
|
683
|
+
}
|
|
684
|
+
function splitSqlStatements(sql) {
|
|
685
|
+
const parts = [];
|
|
686
|
+
let current = "";
|
|
687
|
+
let inSingle = false;
|
|
688
|
+
let inDouble = false;
|
|
689
|
+
let inLineComment = false;
|
|
690
|
+
let inBlockComment = false;
|
|
691
|
+
for (let i = 0; i < sql.length; i++) {
|
|
692
|
+
const ch = sql[i];
|
|
693
|
+
const next = sql[i + 1];
|
|
694
|
+
if (inLineComment) {
|
|
695
|
+
current += ch;
|
|
696
|
+
if (ch === "\n") inLineComment = false;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (inBlockComment) {
|
|
700
|
+
current += ch;
|
|
701
|
+
if (ch === "*" && next === "/") {
|
|
702
|
+
current += next;
|
|
703
|
+
inBlockComment = false;
|
|
704
|
+
i += 1;
|
|
705
|
+
}
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
709
|
+
current += ch + next;
|
|
710
|
+
inLineComment = true;
|
|
711
|
+
i += 1;
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
715
|
+
current += ch + next;
|
|
716
|
+
inBlockComment = true;
|
|
717
|
+
i += 1;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
721
|
+
inSingle = !inSingle;
|
|
722
|
+
current += ch;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
726
|
+
inDouble = !inDouble;
|
|
727
|
+
current += ch;
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
731
|
+
if (current.trim()) {
|
|
732
|
+
parts.push(current.trim());
|
|
733
|
+
}
|
|
734
|
+
current = "";
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
current += ch;
|
|
738
|
+
}
|
|
739
|
+
if (current.trim()) {
|
|
740
|
+
parts.push(current.trim());
|
|
741
|
+
}
|
|
742
|
+
return parts;
|
|
743
|
+
}
|
|
744
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
745
|
+
const prisma = await loadPrismaClient();
|
|
746
|
+
await ensureCompatibilityViews(prisma);
|
|
747
|
+
let closed = false;
|
|
748
|
+
let adapter;
|
|
749
|
+
const fallbackExecute = async (stmt, error) => {
|
|
750
|
+
if (!fallbackClient) {
|
|
751
|
+
if (error) throw error;
|
|
752
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
753
|
+
}
|
|
754
|
+
if (error) {
|
|
755
|
+
process.stderr.write(
|
|
756
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
757
|
+
`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return fallbackClient.execute(stmt);
|
|
761
|
+
};
|
|
762
|
+
adapter = {
|
|
763
|
+
async execute(stmt) {
|
|
764
|
+
if (shouldBypassPostgres(stmt)) {
|
|
765
|
+
return fallbackExecute(stmt);
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
return await executeOnPrisma(prisma, stmt);
|
|
769
|
+
} catch (error) {
|
|
770
|
+
if (shouldFallbackOnError(error)) {
|
|
771
|
+
return fallbackExecute(stmt, error);
|
|
772
|
+
}
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
async batch(stmts, mode) {
|
|
777
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
778
|
+
if (!fallbackClient) {
|
|
779
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
780
|
+
}
|
|
781
|
+
return fallbackClient.batch(stmts, mode);
|
|
782
|
+
}
|
|
783
|
+
try {
|
|
784
|
+
if (prisma.$transaction) {
|
|
785
|
+
return await prisma.$transaction(async (tx) => {
|
|
786
|
+
const results2 = [];
|
|
787
|
+
for (const stmt of stmts) {
|
|
788
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
789
|
+
}
|
|
790
|
+
return results2;
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const results = [];
|
|
794
|
+
for (const stmt of stmts) {
|
|
795
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
796
|
+
}
|
|
797
|
+
return results;
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
800
|
+
process.stderr.write(
|
|
801
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
802
|
+
`
|
|
803
|
+
);
|
|
804
|
+
return fallbackClient.batch(stmts, mode);
|
|
805
|
+
}
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
async migrate(stmts) {
|
|
810
|
+
if (fallbackClient) {
|
|
811
|
+
return fallbackClient.migrate(stmts);
|
|
812
|
+
}
|
|
813
|
+
return adapter.batch(stmts, "deferred");
|
|
814
|
+
},
|
|
815
|
+
async transaction(mode) {
|
|
816
|
+
if (!fallbackClient) {
|
|
817
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
818
|
+
}
|
|
819
|
+
return fallbackClient.transaction(mode);
|
|
820
|
+
},
|
|
821
|
+
async executeMultiple(sql) {
|
|
822
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
823
|
+
return fallbackClient.executeMultiple(sql);
|
|
824
|
+
}
|
|
825
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
826
|
+
await adapter.execute(statement);
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
async sync() {
|
|
830
|
+
if (fallbackClient) {
|
|
831
|
+
return fallbackClient.sync();
|
|
832
|
+
}
|
|
833
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
834
|
+
},
|
|
835
|
+
close() {
|
|
836
|
+
closed = true;
|
|
837
|
+
prismaClientPromise = null;
|
|
838
|
+
compatibilityBootstrapPromise = null;
|
|
839
|
+
void prisma.$disconnect?.();
|
|
840
|
+
},
|
|
841
|
+
get closed() {
|
|
842
|
+
return closed;
|
|
843
|
+
},
|
|
844
|
+
get protocol() {
|
|
845
|
+
return "prisma-postgres";
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
return adapter;
|
|
849
|
+
}
|
|
850
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
851
|
+
var init_database_adapter = __esm({
|
|
852
|
+
"src/lib/database-adapter.ts"() {
|
|
853
|
+
"use strict";
|
|
854
|
+
VIEW_MAPPINGS = [
|
|
855
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
856
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
857
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
858
|
+
{ view: "entities", source: "memory.entities" },
|
|
859
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
860
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
861
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
862
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
863
|
+
{ view: "messages", source: "memory.messages" },
|
|
864
|
+
{ view: "users", source: "wiki.users" },
|
|
865
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
866
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
867
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
868
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
869
|
+
];
|
|
870
|
+
UPSERT_KEYS = {
|
|
871
|
+
memories: ["id"],
|
|
872
|
+
tasks: ["id"],
|
|
873
|
+
behaviors: ["id"],
|
|
874
|
+
entities: ["id"],
|
|
875
|
+
relationships: ["id"],
|
|
876
|
+
entity_aliases: ["alias"],
|
|
877
|
+
notifications: ["id"],
|
|
878
|
+
messages: ["id"],
|
|
879
|
+
users: ["id"],
|
|
880
|
+
workspaces: ["id"],
|
|
881
|
+
workspace_users: ["id"],
|
|
882
|
+
documents: ["id"],
|
|
883
|
+
chats: ["id"]
|
|
884
|
+
};
|
|
885
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
886
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
887
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
888
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
889
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
890
|
+
};
|
|
891
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
892
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
893
|
+
);
|
|
894
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
895
|
+
/\bPRAGMA\b/i,
|
|
896
|
+
/\bsqlite_master\b/i,
|
|
897
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
898
|
+
/\bMATCH\b/i,
|
|
899
|
+
/\bvector_distance_cos\s*\(/i,
|
|
900
|
+
/\bjson_extract\s*\(/i,
|
|
901
|
+
/\bjulianday\s*\(/i,
|
|
902
|
+
/\bstrftime\s*\(/i,
|
|
903
|
+
/\blast_insert_rowid\s*\(/i
|
|
904
|
+
];
|
|
905
|
+
prismaClientPromise = null;
|
|
906
|
+
compatibilityBootstrapPromise = null;
|
|
295
907
|
}
|
|
296
908
|
});
|
|
297
909
|
|
|
298
910
|
// src/lib/database.ts
|
|
299
911
|
import { createClient } from "@libsql/client";
|
|
300
912
|
async function initDatabase(config) {
|
|
913
|
+
if (_walCheckpointTimer) {
|
|
914
|
+
clearInterval(_walCheckpointTimer);
|
|
915
|
+
_walCheckpointTimer = null;
|
|
916
|
+
}
|
|
917
|
+
if (_daemonClient) {
|
|
918
|
+
_daemonClient.close();
|
|
919
|
+
_daemonClient = null;
|
|
920
|
+
}
|
|
921
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
922
|
+
_adapterClient.close();
|
|
923
|
+
}
|
|
924
|
+
_adapterClient = null;
|
|
301
925
|
if (_client) {
|
|
302
926
|
_client.close();
|
|
303
927
|
_client = null;
|
|
@@ -311,6 +935,7 @@ async function initDatabase(config) {
|
|
|
311
935
|
}
|
|
312
936
|
_client = createClient(opts);
|
|
313
937
|
_resilientClient = wrapWithRetry(_client);
|
|
938
|
+
_adapterClient = _resilientClient;
|
|
314
939
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
315
940
|
});
|
|
316
941
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -321,11 +946,17 @@ async function initDatabase(config) {
|
|
|
321
946
|
});
|
|
322
947
|
}, 3e4);
|
|
323
948
|
_walCheckpointTimer.unref();
|
|
949
|
+
if (process.env.DATABASE_URL) {
|
|
950
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
951
|
+
}
|
|
324
952
|
}
|
|
325
953
|
function getClient() {
|
|
326
|
-
if (!
|
|
954
|
+
if (!_adapterClient) {
|
|
327
955
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
328
956
|
}
|
|
957
|
+
if (process.env.DATABASE_URL) {
|
|
958
|
+
return _adapterClient;
|
|
959
|
+
}
|
|
329
960
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
330
961
|
return _resilientClient;
|
|
331
962
|
}
|
|
@@ -618,6 +1249,7 @@ async function ensureSchema() {
|
|
|
618
1249
|
project TEXT NOT NULL,
|
|
619
1250
|
summary TEXT NOT NULL,
|
|
620
1251
|
task_file TEXT,
|
|
1252
|
+
session_scope TEXT,
|
|
621
1253
|
read INTEGER NOT NULL DEFAULT 0,
|
|
622
1254
|
created_at TEXT NOT NULL
|
|
623
1255
|
);
|
|
@@ -626,7 +1258,7 @@ async function ensureSchema() {
|
|
|
626
1258
|
ON notifications(read);
|
|
627
1259
|
|
|
628
1260
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
629
|
-
ON notifications(agent_id);
|
|
1261
|
+
ON notifications(agent_id, session_scope);
|
|
630
1262
|
|
|
631
1263
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
632
1264
|
ON notifications(task_file);
|
|
@@ -664,6 +1296,7 @@ async function ensureSchema() {
|
|
|
664
1296
|
target_agent TEXT NOT NULL,
|
|
665
1297
|
target_project TEXT,
|
|
666
1298
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1299
|
+
session_scope TEXT,
|
|
667
1300
|
content TEXT NOT NULL,
|
|
668
1301
|
priority TEXT DEFAULT 'normal',
|
|
669
1302
|
status TEXT DEFAULT 'pending',
|
|
@@ -677,10 +1310,31 @@ async function ensureSchema() {
|
|
|
677
1310
|
);
|
|
678
1311
|
|
|
679
1312
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
680
|
-
ON messages(target_agent, status);
|
|
1313
|
+
ON messages(target_agent, session_scope, status);
|
|
681
1314
|
|
|
682
1315
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
683
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1316
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1317
|
+
`);
|
|
1318
|
+
try {
|
|
1319
|
+
await client.execute({
|
|
1320
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1321
|
+
args: []
|
|
1322
|
+
});
|
|
1323
|
+
} catch {
|
|
1324
|
+
}
|
|
1325
|
+
try {
|
|
1326
|
+
await client.execute({
|
|
1327
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1328
|
+
args: []
|
|
1329
|
+
});
|
|
1330
|
+
} catch {
|
|
1331
|
+
}
|
|
1332
|
+
await client.executeMultiple(`
|
|
1333
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1334
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1335
|
+
|
|
1336
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1337
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
684
1338
|
`);
|
|
685
1339
|
try {
|
|
686
1340
|
await client.execute({
|
|
@@ -1264,28 +1918,45 @@ async function ensureSchema() {
|
|
|
1264
1918
|
} catch {
|
|
1265
1919
|
}
|
|
1266
1920
|
}
|
|
1921
|
+
try {
|
|
1922
|
+
await client.execute({
|
|
1923
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1924
|
+
args: []
|
|
1925
|
+
});
|
|
1926
|
+
} catch {
|
|
1927
|
+
}
|
|
1267
1928
|
}
|
|
1268
1929
|
async function disposeDatabase() {
|
|
1930
|
+
if (_walCheckpointTimer) {
|
|
1931
|
+
clearInterval(_walCheckpointTimer);
|
|
1932
|
+
_walCheckpointTimer = null;
|
|
1933
|
+
}
|
|
1269
1934
|
if (_daemonClient) {
|
|
1270
1935
|
_daemonClient.close();
|
|
1271
1936
|
_daemonClient = null;
|
|
1272
1937
|
}
|
|
1938
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1939
|
+
_adapterClient.close();
|
|
1940
|
+
}
|
|
1941
|
+
_adapterClient = null;
|
|
1273
1942
|
if (_client) {
|
|
1274
1943
|
_client.close();
|
|
1275
1944
|
_client = null;
|
|
1276
1945
|
_resilientClient = null;
|
|
1277
1946
|
}
|
|
1278
1947
|
}
|
|
1279
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
1948
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1280
1949
|
var init_database = __esm({
|
|
1281
1950
|
"src/lib/database.ts"() {
|
|
1282
1951
|
"use strict";
|
|
1283
1952
|
init_db_retry();
|
|
1284
1953
|
init_employees();
|
|
1954
|
+
init_database_adapter();
|
|
1285
1955
|
_client = null;
|
|
1286
1956
|
_resilientClient = null;
|
|
1287
1957
|
_walCheckpointTimer = null;
|
|
1288
1958
|
_daemonClient = null;
|
|
1959
|
+
_adapterClient = null;
|
|
1289
1960
|
initTurso = initDatabase;
|
|
1290
1961
|
disposeTurso = disposeDatabase;
|
|
1291
1962
|
}
|
|
@@ -1296,6 +1967,7 @@ var shard_manager_exports = {};
|
|
|
1296
1967
|
__export(shard_manager_exports, {
|
|
1297
1968
|
disposeShards: () => disposeShards,
|
|
1298
1969
|
ensureShardSchema: () => ensureShardSchema,
|
|
1970
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1299
1971
|
getReadyShardClient: () => getReadyShardClient,
|
|
1300
1972
|
getShardClient: () => getShardClient,
|
|
1301
1973
|
getShardsDir: () => getShardsDir,
|
|
@@ -1304,15 +1976,18 @@ __export(shard_manager_exports, {
|
|
|
1304
1976
|
listShards: () => listShards,
|
|
1305
1977
|
shardExists: () => shardExists
|
|
1306
1978
|
});
|
|
1307
|
-
import
|
|
1308
|
-
import { existsSync as
|
|
1979
|
+
import path5 from "path";
|
|
1980
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1309
1981
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1310
1982
|
function initShardManager(encryptionKey) {
|
|
1311
1983
|
_encryptionKey = encryptionKey;
|
|
1312
|
-
if (!
|
|
1313
|
-
|
|
1984
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
1985
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1314
1986
|
}
|
|
1315
1987
|
_shardingEnabled = true;
|
|
1988
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
1989
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
1990
|
+
_evictionTimer.unref();
|
|
1316
1991
|
}
|
|
1317
1992
|
function isShardingEnabled() {
|
|
1318
1993
|
return _shardingEnabled;
|
|
@@ -1329,21 +2004,28 @@ function getShardClient(projectName) {
|
|
|
1329
2004
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1330
2005
|
}
|
|
1331
2006
|
const cached = _shards.get(safeName);
|
|
1332
|
-
if (cached)
|
|
1333
|
-
|
|
2007
|
+
if (cached) {
|
|
2008
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2009
|
+
return cached;
|
|
2010
|
+
}
|
|
2011
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2012
|
+
evictLRU();
|
|
2013
|
+
}
|
|
2014
|
+
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1334
2015
|
const client = createClient2({
|
|
1335
2016
|
url: `file:${dbPath}`,
|
|
1336
2017
|
encryptionKey: _encryptionKey
|
|
1337
2018
|
});
|
|
1338
2019
|
_shards.set(safeName, client);
|
|
2020
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1339
2021
|
return client;
|
|
1340
2022
|
}
|
|
1341
2023
|
function shardExists(projectName) {
|
|
1342
2024
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1343
|
-
return
|
|
2025
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1344
2026
|
}
|
|
1345
2027
|
function listShards() {
|
|
1346
|
-
if (!
|
|
2028
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1347
2029
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1348
2030
|
}
|
|
1349
2031
|
async function ensureShardSchema(client) {
|
|
@@ -1395,6 +2077,8 @@ async function ensureShardSchema(client) {
|
|
|
1395
2077
|
for (const col of [
|
|
1396
2078
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
1397
2079
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2080
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2081
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
1398
2082
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
1399
2083
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
1400
2084
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -1417,7 +2101,23 @@ async function ensureShardSchema(client) {
|
|
|
1417
2101
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1418
2102
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1419
2103
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1420
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2104
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2105
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2106
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2107
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2108
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2109
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2110
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2111
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2112
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2113
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2114
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2115
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2116
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2117
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2118
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2119
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2120
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1421
2121
|
]) {
|
|
1422
2122
|
try {
|
|
1423
2123
|
await client.execute(col);
|
|
@@ -1516,21 +2216,69 @@ async function getReadyShardClient(projectName) {
|
|
|
1516
2216
|
await ensureShardSchema(client);
|
|
1517
2217
|
return client;
|
|
1518
2218
|
}
|
|
2219
|
+
function evictLRU() {
|
|
2220
|
+
let oldest = null;
|
|
2221
|
+
let oldestTime = Infinity;
|
|
2222
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2223
|
+
if (time < oldestTime) {
|
|
2224
|
+
oldestTime = time;
|
|
2225
|
+
oldest = name;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
if (oldest) {
|
|
2229
|
+
const client = _shards.get(oldest);
|
|
2230
|
+
if (client) {
|
|
2231
|
+
client.close();
|
|
2232
|
+
}
|
|
2233
|
+
_shards.delete(oldest);
|
|
2234
|
+
_shardLastAccess.delete(oldest);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
function evictIdleShards() {
|
|
2238
|
+
const now = Date.now();
|
|
2239
|
+
const toEvict = [];
|
|
2240
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2241
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2242
|
+
toEvict.push(name);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
for (const name of toEvict) {
|
|
2246
|
+
const client = _shards.get(name);
|
|
2247
|
+
if (client) {
|
|
2248
|
+
client.close();
|
|
2249
|
+
}
|
|
2250
|
+
_shards.delete(name);
|
|
2251
|
+
_shardLastAccess.delete(name);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
function getOpenShardCount() {
|
|
2255
|
+
return _shards.size;
|
|
2256
|
+
}
|
|
1519
2257
|
function disposeShards() {
|
|
2258
|
+
if (_evictionTimer) {
|
|
2259
|
+
clearInterval(_evictionTimer);
|
|
2260
|
+
_evictionTimer = null;
|
|
2261
|
+
}
|
|
1520
2262
|
for (const [, client] of _shards) {
|
|
1521
2263
|
client.close();
|
|
1522
2264
|
}
|
|
1523
2265
|
_shards.clear();
|
|
2266
|
+
_shardLastAccess.clear();
|
|
1524
2267
|
_shardingEnabled = false;
|
|
1525
2268
|
_encryptionKey = null;
|
|
1526
2269
|
}
|
|
1527
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2270
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
1528
2271
|
var init_shard_manager = __esm({
|
|
1529
2272
|
"src/lib/shard-manager.ts"() {
|
|
1530
2273
|
"use strict";
|
|
1531
2274
|
init_config();
|
|
1532
|
-
SHARDS_DIR =
|
|
2275
|
+
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
2276
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2277
|
+
MAX_OPEN_SHARDS = 10;
|
|
2278
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
1533
2279
|
_shards = /* @__PURE__ */ new Map();
|
|
2280
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2281
|
+
_evictionTimer = null;
|
|
1534
2282
|
_encryptionKey = null;
|
|
1535
2283
|
_shardingEnabled = false;
|
|
1536
2284
|
}
|
|
@@ -1723,6 +2471,32 @@ ${p.content}`).join("\n\n");
|
|
|
1723
2471
|
}
|
|
1724
2472
|
});
|
|
1725
2473
|
|
|
2474
|
+
// src/lib/runtime-table.ts
|
|
2475
|
+
var RUNTIME_TABLE;
|
|
2476
|
+
var init_runtime_table = __esm({
|
|
2477
|
+
"src/lib/runtime-table.ts"() {
|
|
2478
|
+
"use strict";
|
|
2479
|
+
RUNTIME_TABLE = {
|
|
2480
|
+
codex: {
|
|
2481
|
+
binary: "codex",
|
|
2482
|
+
launchMode: "interactive",
|
|
2483
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
2484
|
+
inlineFlag: "--no-alt-screen",
|
|
2485
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
2486
|
+
defaultModel: "gpt-5.4"
|
|
2487
|
+
},
|
|
2488
|
+
opencode: {
|
|
2489
|
+
binary: "opencode",
|
|
2490
|
+
launchMode: "exec",
|
|
2491
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
2492
|
+
inlineFlag: "",
|
|
2493
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
2494
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
});
|
|
2499
|
+
|
|
1726
2500
|
// src/lib/session-key.ts
|
|
1727
2501
|
import { execSync as execSync2 } from "child_process";
|
|
1728
2502
|
function normalizeCommand(command) {
|
|
@@ -1801,9 +2575,9 @@ __export(active_agent_exports, {
|
|
|
1801
2575
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
1802
2576
|
writeActiveAgent: () => writeActiveAgent
|
|
1803
2577
|
});
|
|
1804
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as
|
|
2578
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
1805
2579
|
import { execSync as execSync3 } from "child_process";
|
|
1806
|
-
import
|
|
2580
|
+
import path7 from "path";
|
|
1807
2581
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
1808
2582
|
if (candidate === baseName) return true;
|
|
1809
2583
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -1847,11 +2621,11 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
1847
2621
|
return null;
|
|
1848
2622
|
}
|
|
1849
2623
|
function getMarkerPath() {
|
|
1850
|
-
return
|
|
2624
|
+
return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
1851
2625
|
}
|
|
1852
2626
|
function writeActiveAgent(agentId, agentRole) {
|
|
1853
2627
|
try {
|
|
1854
|
-
|
|
2628
|
+
mkdirSync4(CACHE_DIR, { recursive: true });
|
|
1855
2629
|
writeFileSync3(
|
|
1856
2630
|
getMarkerPath(),
|
|
1857
2631
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
@@ -1916,14 +2690,14 @@ function getAllActiveAgents() {
|
|
|
1916
2690
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
1917
2691
|
if (key === "undefined") continue;
|
|
1918
2692
|
try {
|
|
1919
|
-
const raw = readFileSync3(
|
|
2693
|
+
const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
|
|
1920
2694
|
const data = JSON.parse(raw);
|
|
1921
2695
|
if (!data.agentId) continue;
|
|
1922
2696
|
if (data.startedAt) {
|
|
1923
2697
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
1924
2698
|
if (age > STALE_MS) {
|
|
1925
2699
|
try {
|
|
1926
|
-
unlinkSync3(
|
|
2700
|
+
unlinkSync3(path7.join(CACHE_DIR, file));
|
|
1927
2701
|
} catch {
|
|
1928
2702
|
}
|
|
1929
2703
|
continue;
|
|
@@ -1946,11 +2720,11 @@ function getAllActiveAgents() {
|
|
|
1946
2720
|
function cleanupSessionMarkers() {
|
|
1947
2721
|
const key = getSessionKey();
|
|
1948
2722
|
try {
|
|
1949
|
-
unlinkSync3(
|
|
2723
|
+
unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
1950
2724
|
} catch {
|
|
1951
2725
|
}
|
|
1952
2726
|
try {
|
|
1953
|
-
unlinkSync3(
|
|
2727
|
+
unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
1954
2728
|
} catch {
|
|
1955
2729
|
}
|
|
1956
2730
|
}
|
|
@@ -1961,18 +2735,18 @@ var init_active_agent = __esm({
|
|
|
1961
2735
|
init_config();
|
|
1962
2736
|
init_session_key();
|
|
1963
2737
|
init_employees();
|
|
1964
|
-
CACHE_DIR =
|
|
2738
|
+
CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
|
|
1965
2739
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
1966
2740
|
}
|
|
1967
2741
|
});
|
|
1968
2742
|
|
|
1969
2743
|
// src/lib/agent-symlinks.ts
|
|
1970
|
-
import
|
|
1971
|
-
import
|
|
2744
|
+
import os6 from "os";
|
|
2745
|
+
import path8 from "path";
|
|
1972
2746
|
import {
|
|
1973
|
-
existsSync as
|
|
2747
|
+
existsSync as existsSync7,
|
|
1974
2748
|
lstatSync,
|
|
1975
|
-
mkdirSync as
|
|
2749
|
+
mkdirSync as mkdirSync5,
|
|
1976
2750
|
readlinkSync as readlinkSync2,
|
|
1977
2751
|
symlinkSync as symlinkSync2
|
|
1978
2752
|
} from "fs";
|
|
@@ -1998,38 +2772,39 @@ var init_mcp_prefix = __esm({
|
|
|
1998
2772
|
});
|
|
1999
2773
|
|
|
2000
2774
|
// src/lib/preferences.ts
|
|
2001
|
-
import { existsSync as
|
|
2002
|
-
import
|
|
2003
|
-
import
|
|
2775
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2776
|
+
import path9 from "path";
|
|
2777
|
+
import os7 from "os";
|
|
2004
2778
|
var init_preferences = __esm({
|
|
2005
2779
|
"src/lib/preferences.ts"() {
|
|
2006
2780
|
"use strict";
|
|
2781
|
+
init_secure_files();
|
|
2007
2782
|
}
|
|
2008
2783
|
});
|
|
2009
2784
|
|
|
2010
2785
|
// src/adapters/claude/installer.ts
|
|
2011
2786
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
|
|
2012
|
-
import { existsSync as
|
|
2013
|
-
import
|
|
2014
|
-
import
|
|
2787
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
2788
|
+
import path10 from "path";
|
|
2789
|
+
import os8 from "os";
|
|
2015
2790
|
import { execSync as execSync4 } from "child_process";
|
|
2016
2791
|
import { fileURLToPath } from "url";
|
|
2017
2792
|
function resolvePackageRoot() {
|
|
2018
2793
|
const thisFile = fileURLToPath(import.meta.url);
|
|
2019
|
-
let dir =
|
|
2020
|
-
const root =
|
|
2794
|
+
let dir = path10.dirname(thisFile);
|
|
2795
|
+
const root = path10.parse(dir).root;
|
|
2021
2796
|
while (dir !== root) {
|
|
2022
|
-
const pkgPath =
|
|
2023
|
-
if (
|
|
2797
|
+
const pkgPath = path10.join(dir, "package.json");
|
|
2798
|
+
if (existsSync9(pkgPath)) {
|
|
2024
2799
|
try {
|
|
2025
2800
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
2026
2801
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
2027
2802
|
} catch {
|
|
2028
2803
|
}
|
|
2029
2804
|
}
|
|
2030
|
-
dir =
|
|
2805
|
+
dir = path10.dirname(dir);
|
|
2031
2806
|
}
|
|
2032
|
-
return
|
|
2807
|
+
return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
|
|
2033
2808
|
}
|
|
2034
2809
|
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
2035
2810
|
var init_installer = __esm({
|
|
@@ -2247,15 +3022,15 @@ __export(installer_exports, {
|
|
|
2247
3022
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
2248
3023
|
});
|
|
2249
3024
|
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
2250
|
-
import { existsSync as
|
|
2251
|
-
import
|
|
2252
|
-
import
|
|
2253
|
-
async function registerOpenCodeMcp(packageRoot, homeDir =
|
|
2254
|
-
const configDir =
|
|
2255
|
-
const configPath =
|
|
3025
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
3026
|
+
import path11 from "path";
|
|
3027
|
+
import os9 from "os";
|
|
3028
|
+
async function registerOpenCodeMcp(packageRoot, homeDir = os9.homedir()) {
|
|
3029
|
+
const configDir = path11.join(homeDir, ".config", "opencode");
|
|
3030
|
+
const configPath = path11.join(configDir, "opencode.json");
|
|
2256
3031
|
await mkdir5(configDir, { recursive: true });
|
|
2257
3032
|
let config = {};
|
|
2258
|
-
if (
|
|
3033
|
+
if (existsSync10(configPath)) {
|
|
2259
3034
|
try {
|
|
2260
3035
|
config = JSON.parse(await readFile5(configPath, "utf-8"));
|
|
2261
3036
|
} catch {
|
|
@@ -2267,7 +3042,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
|
|
|
2267
3042
|
}
|
|
2268
3043
|
const newEntry = {
|
|
2269
3044
|
type: "local",
|
|
2270
|
-
command: ["node",
|
|
3045
|
+
command: ["node", path11.join(packageRoot, "dist", "mcp", "server.js")],
|
|
2271
3046
|
enabled: true
|
|
2272
3047
|
};
|
|
2273
3048
|
const current = config.mcp["exe-os"];
|
|
@@ -2281,15 +3056,15 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
|
|
|
2281
3056
|
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
2282
3057
|
return true;
|
|
2283
3058
|
}
|
|
2284
|
-
async function installOpenCodePlugin(packageRoot, homeDir =
|
|
2285
|
-
const pluginDir =
|
|
2286
|
-
const pluginPath =
|
|
3059
|
+
async function installOpenCodePlugin(packageRoot, homeDir = os9.homedir()) {
|
|
3060
|
+
const pluginDir = path11.join(homeDir, ".config", "opencode", "plugins");
|
|
3061
|
+
const pluginPath = path11.join(pluginDir, "exe-os.mjs");
|
|
2287
3062
|
await mkdir5(pluginDir, { recursive: true });
|
|
2288
3063
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
2289
3064
|
/__PACKAGE_ROOT__/g,
|
|
2290
3065
|
packageRoot.replace(/\\/g, "\\\\")
|
|
2291
3066
|
);
|
|
2292
|
-
if (
|
|
3067
|
+
if (existsSync10(pluginPath)) {
|
|
2293
3068
|
const existing = await readFile5(pluginPath, "utf-8");
|
|
2294
3069
|
if (existing === pluginContent) {
|
|
2295
3070
|
return false;
|
|
@@ -2298,17 +3073,17 @@ async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
|
|
|
2298
3073
|
await writeFile5(pluginPath, pluginContent);
|
|
2299
3074
|
return true;
|
|
2300
3075
|
}
|
|
2301
|
-
function verifyOpenCodeHooks(homeDir =
|
|
2302
|
-
const configPath =
|
|
2303
|
-
const pluginPath =
|
|
2304
|
-
if (!
|
|
3076
|
+
function verifyOpenCodeHooks(homeDir = os9.homedir()) {
|
|
3077
|
+
const configPath = path11.join(homeDir, ".config", "opencode", "opencode.json");
|
|
3078
|
+
const pluginPath = path11.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
3079
|
+
if (!existsSync10(configPath)) return false;
|
|
2305
3080
|
try {
|
|
2306
3081
|
const config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2307
3082
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
2308
3083
|
} catch {
|
|
2309
3084
|
return false;
|
|
2310
3085
|
}
|
|
2311
|
-
if (!
|
|
3086
|
+
if (!existsSync10(pluginPath)) return false;
|
|
2312
3087
|
return true;
|
|
2313
3088
|
}
|
|
2314
3089
|
async function runOpenCodeInstaller(homeDir) {
|
|
@@ -2333,10 +3108,10 @@ var init_installer2 = __esm({
|
|
|
2333
3108
|
});
|
|
2334
3109
|
|
|
2335
3110
|
// src/bin/exe-start-opencode.ts
|
|
2336
|
-
import
|
|
2337
|
-
import
|
|
3111
|
+
import os10 from "os";
|
|
3112
|
+
import path12 from "path";
|
|
2338
3113
|
import {
|
|
2339
|
-
existsSync as
|
|
3114
|
+
existsSync as existsSync11,
|
|
2340
3115
|
readFileSync as readFileSync7,
|
|
2341
3116
|
writeFileSync as writeFileSync6,
|
|
2342
3117
|
mkdirSync as mkdirSync7,
|
|
@@ -2350,16 +3125,16 @@ init_database();
|
|
|
2350
3125
|
|
|
2351
3126
|
// src/lib/keychain.ts
|
|
2352
3127
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2353
|
-
import { existsSync as
|
|
2354
|
-
import
|
|
2355
|
-
import
|
|
3128
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3129
|
+
import path4 from "path";
|
|
3130
|
+
import os4 from "os";
|
|
2356
3131
|
var SERVICE = "exe-mem";
|
|
2357
3132
|
var ACCOUNT = "master-key";
|
|
2358
3133
|
function getKeyDir() {
|
|
2359
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3134
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
|
|
2360
3135
|
}
|
|
2361
3136
|
function getKeyPath() {
|
|
2362
|
-
return
|
|
3137
|
+
return path4.join(getKeyDir(), "master.key");
|
|
2363
3138
|
}
|
|
2364
3139
|
async function tryKeytar() {
|
|
2365
3140
|
try {
|
|
@@ -2380,9 +3155,9 @@ async function getMasterKey() {
|
|
|
2380
3155
|
}
|
|
2381
3156
|
}
|
|
2382
3157
|
const keyPath = getKeyPath();
|
|
2383
|
-
if (!
|
|
3158
|
+
if (!existsSync4(keyPath)) {
|
|
2384
3159
|
process.stderr.write(
|
|
2385
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3160
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2386
3161
|
`
|
|
2387
3162
|
);
|
|
2388
3163
|
return null;
|
|
@@ -2693,11 +3468,11 @@ function vectorToBlob(vector) {
|
|
|
2693
3468
|
}
|
|
2694
3469
|
|
|
2695
3470
|
// src/lib/behaviors-export.ts
|
|
2696
|
-
import
|
|
2697
|
-
import
|
|
3471
|
+
import os5 from "os";
|
|
3472
|
+
import path6 from "path";
|
|
2698
3473
|
import {
|
|
2699
|
-
existsSync as
|
|
2700
|
-
mkdirSync as
|
|
3474
|
+
existsSync as existsSync6,
|
|
3475
|
+
mkdirSync as mkdirSync3,
|
|
2701
3476
|
readdirSync as readdirSync2,
|
|
2702
3477
|
statSync,
|
|
2703
3478
|
unlinkSync as unlinkSync2,
|
|
@@ -2736,15 +3511,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
2736
3511
|
}
|
|
2737
3512
|
|
|
2738
3513
|
// src/lib/behaviors-export.ts
|
|
2739
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
2740
|
-
|
|
3514
|
+
var BEHAVIORS_EXPORT_DIR = path6.join(
|
|
3515
|
+
os5.homedir(),
|
|
2741
3516
|
".exe-os",
|
|
2742
3517
|
"behaviors-export"
|
|
2743
3518
|
);
|
|
2744
3519
|
var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
|
|
2745
3520
|
var EXPORT_BEHAVIOR_LIMIT = 30;
|
|
2746
3521
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
2747
|
-
if (!
|
|
3522
|
+
if (!existsSync6(BEHAVIORS_EXPORT_DIR)) return;
|
|
2748
3523
|
let entries;
|
|
2749
3524
|
try {
|
|
2750
3525
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -2752,7 +3527,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
2752
3527
|
return;
|
|
2753
3528
|
}
|
|
2754
3529
|
for (const entry of entries) {
|
|
2755
|
-
const filePath =
|
|
3530
|
+
const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
2756
3531
|
try {
|
|
2757
3532
|
const stat = statSync(filePath);
|
|
2758
3533
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
@@ -2785,16 +3560,16 @@ function renderBehaviorExport(behaviors) {
|
|
|
2785
3560
|
}
|
|
2786
3561
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
2787
3562
|
if (!sessionKey) {
|
|
2788
|
-
return
|
|
3563
|
+
return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
2789
3564
|
}
|
|
2790
3565
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2791
|
-
return
|
|
3566
|
+
return path6.join(
|
|
2792
3567
|
BEHAVIORS_EXPORT_DIR,
|
|
2793
3568
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
2794
3569
|
);
|
|
2795
3570
|
}
|
|
2796
3571
|
async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
2797
|
-
|
|
3572
|
+
mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
|
|
2798
3573
|
sweepStaleBehaviorExports();
|
|
2799
3574
|
const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
|
|
2800
3575
|
if (behaviors.length === 0) return null;
|
|
@@ -2806,28 +3581,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
2806
3581
|
|
|
2807
3582
|
// src/bin/exe-start-opencode.ts
|
|
2808
3583
|
init_employees();
|
|
2809
|
-
|
|
2810
|
-
// src/lib/runtime-table.ts
|
|
2811
|
-
var RUNTIME_TABLE = {
|
|
2812
|
-
codex: {
|
|
2813
|
-
binary: "codex",
|
|
2814
|
-
launchMode: "interactive",
|
|
2815
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
2816
|
-
inlineFlag: "--no-alt-screen",
|
|
2817
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
2818
|
-
defaultModel: "gpt-5.4"
|
|
2819
|
-
},
|
|
2820
|
-
opencode: {
|
|
2821
|
-
binary: "opencode",
|
|
2822
|
-
launchMode: "exec",
|
|
2823
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
2824
|
-
inlineFlag: "",
|
|
2825
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
2826
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
2827
|
-
}
|
|
2828
|
-
};
|
|
2829
|
-
|
|
2830
|
-
// src/bin/exe-start-opencode.ts
|
|
3584
|
+
init_runtime_table();
|
|
2831
3585
|
var OC = RUNTIME_TABLE.opencode;
|
|
2832
3586
|
var BOOT_INSTRUCTIONS = `
|
|
2833
3587
|
---
|
|
@@ -2838,7 +3592,7 @@ When done with a task: call update_task with status "done".
|
|
|
2838
3592
|
Always call store_memory to persist important findings.
|
|
2839
3593
|
`;
|
|
2840
3594
|
function resolveAgent(argv) {
|
|
2841
|
-
const invokedAs =
|
|
3595
|
+
const invokedAs = path12.basename(argv[1] ?? "");
|
|
2842
3596
|
if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
|
|
2843
3597
|
const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
|
|
2844
3598
|
return { agent: agent2, passthrough: argv.slice(2) };
|
|
@@ -2855,9 +3609,9 @@ function resolveAgent(argv) {
|
|
|
2855
3609
|
return { agent, passthrough };
|
|
2856
3610
|
}
|
|
2857
3611
|
function loadIdentity(agent) {
|
|
2858
|
-
const dir =
|
|
2859
|
-
const exact =
|
|
2860
|
-
if (
|
|
3612
|
+
const dir = path12.join(os10.homedir(), ".exe-os", "identity");
|
|
3613
|
+
const exact = path12.join(dir, `${agent}.md`);
|
|
3614
|
+
if (existsSync11(exact)) {
|
|
2861
3615
|
const content = readFileSync7(exact, "utf-8").trim();
|
|
2862
3616
|
if (content) return content;
|
|
2863
3617
|
}
|
|
@@ -2865,13 +3619,13 @@ function loadIdentity(agent) {
|
|
|
2865
3619
|
const files = readdirSync4(dir);
|
|
2866
3620
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
2867
3621
|
if (match) {
|
|
2868
|
-
const content = readFileSync7(
|
|
3622
|
+
const content = readFileSync7(path12.join(dir, match), "utf-8").trim();
|
|
2869
3623
|
if (content) return content;
|
|
2870
3624
|
}
|
|
2871
3625
|
} catch {
|
|
2872
3626
|
}
|
|
2873
3627
|
try {
|
|
2874
|
-
const rosterPath =
|
|
3628
|
+
const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
|
|
2875
3629
|
const roster = JSON.parse(readFileSync7(rosterPath, "utf8"));
|
|
2876
3630
|
const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
|
|
2877
3631
|
if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
|
|
@@ -2882,17 +3636,17 @@ function loadIdentity(agent) {
|
|
|
2882
3636
|
return null;
|
|
2883
3637
|
}
|
|
2884
3638
|
function writeAgentFile(agent, identity, behaviorsPath) {
|
|
2885
|
-
const agentDir =
|
|
3639
|
+
const agentDir = path12.join(os10.homedir(), ".config", "opencode", "agents");
|
|
2886
3640
|
mkdirSync7(agentDir, { recursive: true });
|
|
2887
3641
|
let content = identity;
|
|
2888
|
-
if (behaviorsPath &&
|
|
3642
|
+
if (behaviorsPath && existsSync11(behaviorsPath)) {
|
|
2889
3643
|
const behaviors = readFileSync7(behaviorsPath, "utf-8").trim();
|
|
2890
3644
|
if (behaviors) {
|
|
2891
3645
|
content += "\n\n" + behaviors;
|
|
2892
3646
|
}
|
|
2893
3647
|
}
|
|
2894
3648
|
content += "\n" + BOOT_INSTRUCTIONS;
|
|
2895
|
-
const outPath =
|
|
3649
|
+
const outPath = path12.join(agentDir, `${agent}.md`);
|
|
2896
3650
|
writeFileSync6(outPath, content, "utf-8");
|
|
2897
3651
|
return outPath;
|
|
2898
3652
|
}
|
|
@@ -2955,7 +3709,7 @@ async function main() {
|
|
|
2955
3709
|
const empRole = (() => {
|
|
2956
3710
|
try {
|
|
2957
3711
|
const emps = readFileSync7(
|
|
2958
|
-
|
|
3712
|
+
path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
|
|
2959
3713
|
"utf-8"
|
|
2960
3714
|
);
|
|
2961
3715
|
const found = JSON.parse(emps).find(
|