@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
|
@@ -341,7 +341,7 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
341
341
|
function getEmployee(employees, name) {
|
|
342
342
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
343
343
|
}
|
|
344
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
344
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
345
345
|
var init_employees = __esm({
|
|
346
346
|
"src/lib/employees.ts"() {
|
|
347
347
|
"use strict";
|
|
@@ -349,12 +349,609 @@ var init_employees = __esm({
|
|
|
349
349
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
350
350
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
351
351
|
COORDINATOR_ROLE = "COO";
|
|
352
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// src/lib/database-adapter.ts
|
|
357
|
+
import os3 from "os";
|
|
358
|
+
import path3 from "path";
|
|
359
|
+
import { createRequire } from "module";
|
|
360
|
+
import { pathToFileURL } from "url";
|
|
361
|
+
function quotedIdentifier(identifier) {
|
|
362
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
363
|
+
}
|
|
364
|
+
function unqualifiedTableName(name) {
|
|
365
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
366
|
+
const parts = raw.split(".");
|
|
367
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
368
|
+
}
|
|
369
|
+
function stripTrailingSemicolon(sql) {
|
|
370
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
371
|
+
}
|
|
372
|
+
function appendClause(sql, clause) {
|
|
373
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
374
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
375
|
+
if (!returningMatch) {
|
|
376
|
+
return `${trimmed}${clause}`;
|
|
377
|
+
}
|
|
378
|
+
const idx = returningMatch.index;
|
|
379
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
380
|
+
}
|
|
381
|
+
function normalizeStatement(stmt) {
|
|
382
|
+
if (typeof stmt === "string") {
|
|
383
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
384
|
+
}
|
|
385
|
+
const sql = stmt.sql;
|
|
386
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
387
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
388
|
+
}
|
|
389
|
+
return { kind: "named", sql, args: stmt.args };
|
|
390
|
+
}
|
|
391
|
+
function rewriteBooleanLiterals(sql) {
|
|
392
|
+
let out = sql;
|
|
393
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
394
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
395
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
396
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
397
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
398
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
399
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
400
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
401
|
+
}
|
|
402
|
+
return out;
|
|
403
|
+
}
|
|
404
|
+
function rewriteInsertOrIgnore(sql) {
|
|
405
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
406
|
+
return sql;
|
|
407
|
+
}
|
|
408
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
409
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
410
|
+
}
|
|
411
|
+
function rewriteInsertOrReplace(sql) {
|
|
412
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
413
|
+
if (!match) {
|
|
414
|
+
return sql;
|
|
415
|
+
}
|
|
416
|
+
const rawTable = match[1];
|
|
417
|
+
const rawColumns = match[2];
|
|
418
|
+
const remainder = match[3];
|
|
419
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
420
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
421
|
+
if (!conflictKeys?.length) {
|
|
422
|
+
return sql;
|
|
423
|
+
}
|
|
424
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
425
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
426
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
427
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
428
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
429
|
+
}
|
|
430
|
+
function rewriteSql(sql) {
|
|
431
|
+
let out = sql;
|
|
432
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
433
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
434
|
+
out = rewriteBooleanLiterals(out);
|
|
435
|
+
out = rewriteInsertOrReplace(out);
|
|
436
|
+
out = rewriteInsertOrIgnore(out);
|
|
437
|
+
return stripTrailingSemicolon(out);
|
|
438
|
+
}
|
|
439
|
+
function toBoolean(value) {
|
|
440
|
+
if (value === null || value === void 0) return value;
|
|
441
|
+
if (typeof value === "boolean") return value;
|
|
442
|
+
if (typeof value === "number") return value !== 0;
|
|
443
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
444
|
+
if (typeof value === "string") {
|
|
445
|
+
const normalized = value.trim().toLowerCase();
|
|
446
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
447
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
448
|
+
}
|
|
449
|
+
return Boolean(value);
|
|
450
|
+
}
|
|
451
|
+
function countQuestionMarks(sql, end) {
|
|
452
|
+
let count = 0;
|
|
453
|
+
let inSingle = false;
|
|
454
|
+
let inDouble = false;
|
|
455
|
+
let inLineComment = false;
|
|
456
|
+
let inBlockComment = false;
|
|
457
|
+
for (let i = 0; i < end; i++) {
|
|
458
|
+
const ch = sql[i];
|
|
459
|
+
const next = sql[i + 1];
|
|
460
|
+
if (inLineComment) {
|
|
461
|
+
if (ch === "\n") inLineComment = false;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (inBlockComment) {
|
|
465
|
+
if (ch === "*" && next === "/") {
|
|
466
|
+
inBlockComment = false;
|
|
467
|
+
i += 1;
|
|
468
|
+
}
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
472
|
+
inLineComment = true;
|
|
473
|
+
i += 1;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
477
|
+
inBlockComment = true;
|
|
478
|
+
i += 1;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
482
|
+
inSingle = !inSingle;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
486
|
+
inDouble = !inDouble;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
490
|
+
count += 1;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return count;
|
|
494
|
+
}
|
|
495
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
496
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
497
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
498
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
499
|
+
for (const match of sql.matchAll(pattern)) {
|
|
500
|
+
const matchText = match[0];
|
|
501
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
502
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return indexes;
|
|
506
|
+
}
|
|
507
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
508
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
509
|
+
if (!match) return;
|
|
510
|
+
const rawTable = match[1];
|
|
511
|
+
const rawColumns = match[2];
|
|
512
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
513
|
+
if (!boolColumns?.size) return;
|
|
514
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
515
|
+
for (const [index, column] of columns.entries()) {
|
|
516
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
517
|
+
args[index] = toBoolean(args[index]);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
522
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
523
|
+
if (!match) return;
|
|
524
|
+
const rawTable = match[1];
|
|
525
|
+
const setClause = match[2];
|
|
526
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
527
|
+
if (!boolColumns?.size) return;
|
|
528
|
+
const assignments = setClause.split(",");
|
|
529
|
+
let placeholderIndex = 0;
|
|
530
|
+
for (const assignment of assignments) {
|
|
531
|
+
if (!assignment.includes("?")) continue;
|
|
532
|
+
placeholderIndex += 1;
|
|
533
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
534
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
535
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function coerceBooleanArgs(sql, args) {
|
|
540
|
+
const nextArgs = [...args];
|
|
541
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
542
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
543
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
544
|
+
for (const index of placeholderIndexes) {
|
|
545
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
546
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return nextArgs;
|
|
550
|
+
}
|
|
551
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
552
|
+
let out = "";
|
|
553
|
+
let placeholder = 0;
|
|
554
|
+
let inSingle = false;
|
|
555
|
+
let inDouble = false;
|
|
556
|
+
let inLineComment = false;
|
|
557
|
+
let inBlockComment = false;
|
|
558
|
+
for (let i = 0; i < sql.length; i++) {
|
|
559
|
+
const ch = sql[i];
|
|
560
|
+
const next = sql[i + 1];
|
|
561
|
+
if (inLineComment) {
|
|
562
|
+
out += ch;
|
|
563
|
+
if (ch === "\n") inLineComment = false;
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (inBlockComment) {
|
|
567
|
+
out += ch;
|
|
568
|
+
if (ch === "*" && next === "/") {
|
|
569
|
+
out += next;
|
|
570
|
+
inBlockComment = false;
|
|
571
|
+
i += 1;
|
|
572
|
+
}
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
576
|
+
out += ch + next;
|
|
577
|
+
inLineComment = true;
|
|
578
|
+
i += 1;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
582
|
+
out += ch + next;
|
|
583
|
+
inBlockComment = true;
|
|
584
|
+
i += 1;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
588
|
+
inSingle = !inSingle;
|
|
589
|
+
out += ch;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
593
|
+
inDouble = !inDouble;
|
|
594
|
+
out += ch;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
598
|
+
placeholder += 1;
|
|
599
|
+
out += `$${placeholder}`;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
out += ch;
|
|
603
|
+
}
|
|
604
|
+
return out;
|
|
605
|
+
}
|
|
606
|
+
function translateStatementForPostgres(stmt) {
|
|
607
|
+
const normalized = normalizeStatement(stmt);
|
|
608
|
+
if (normalized.kind === "named") {
|
|
609
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
610
|
+
}
|
|
611
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
612
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
613
|
+
return {
|
|
614
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
615
|
+
args: coercedArgs
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function shouldBypassPostgres(stmt) {
|
|
619
|
+
const normalized = normalizeStatement(stmt);
|
|
620
|
+
if (normalized.kind === "named") {
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
624
|
+
}
|
|
625
|
+
function shouldFallbackOnError(error) {
|
|
626
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
627
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
628
|
+
}
|
|
629
|
+
function isReadQuery(sql) {
|
|
630
|
+
const trimmed = sql.trimStart();
|
|
631
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
632
|
+
}
|
|
633
|
+
function buildRow(row, columns) {
|
|
634
|
+
const values = columns.map((column) => row[column]);
|
|
635
|
+
return Object.assign(values, row);
|
|
636
|
+
}
|
|
637
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
638
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
639
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
640
|
+
return {
|
|
641
|
+
columns,
|
|
642
|
+
columnTypes: columns.map(() => ""),
|
|
643
|
+
rows: resultRows,
|
|
644
|
+
rowsAffected,
|
|
645
|
+
lastInsertRowid: void 0,
|
|
646
|
+
toJSON() {
|
|
647
|
+
return {
|
|
648
|
+
columns,
|
|
649
|
+
columnTypes: columns.map(() => ""),
|
|
650
|
+
rows,
|
|
651
|
+
rowsAffected,
|
|
652
|
+
lastInsertRowid: void 0
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
async function loadPrismaClient() {
|
|
658
|
+
if (!prismaClientPromise) {
|
|
659
|
+
prismaClientPromise = (async () => {
|
|
660
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
661
|
+
if (explicitPath) {
|
|
662
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
663
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
664
|
+
if (!PrismaClient2) {
|
|
665
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
666
|
+
}
|
|
667
|
+
return new PrismaClient2();
|
|
668
|
+
}
|
|
669
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
670
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
671
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
672
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
673
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
674
|
+
if (!PrismaClient) {
|
|
675
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
676
|
+
}
|
|
677
|
+
return new PrismaClient();
|
|
678
|
+
})();
|
|
679
|
+
}
|
|
680
|
+
return prismaClientPromise;
|
|
681
|
+
}
|
|
682
|
+
async function ensureCompatibilityViews(prisma) {
|
|
683
|
+
if (!compatibilityBootstrapPromise) {
|
|
684
|
+
compatibilityBootstrapPromise = (async () => {
|
|
685
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
686
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
687
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
688
|
+
"SELECT to_regclass($1) AS regclass",
|
|
689
|
+
relation
|
|
690
|
+
);
|
|
691
|
+
if (!rows[0]?.regclass) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
await prisma.$executeRawUnsafe(
|
|
695
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
})();
|
|
699
|
+
}
|
|
700
|
+
return compatibilityBootstrapPromise;
|
|
701
|
+
}
|
|
702
|
+
async function executeOnPrisma(executor, stmt) {
|
|
703
|
+
const translated = translateStatementForPostgres(stmt);
|
|
704
|
+
if (isReadQuery(translated.sql)) {
|
|
705
|
+
const rows = await executor.$queryRawUnsafe(
|
|
706
|
+
translated.sql,
|
|
707
|
+
...translated.args
|
|
708
|
+
);
|
|
709
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
710
|
+
}
|
|
711
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
712
|
+
return buildResultSet([], rowsAffected);
|
|
713
|
+
}
|
|
714
|
+
function splitSqlStatements(sql) {
|
|
715
|
+
const parts = [];
|
|
716
|
+
let current = "";
|
|
717
|
+
let inSingle = false;
|
|
718
|
+
let inDouble = false;
|
|
719
|
+
let inLineComment = false;
|
|
720
|
+
let inBlockComment = false;
|
|
721
|
+
for (let i = 0; i < sql.length; i++) {
|
|
722
|
+
const ch = sql[i];
|
|
723
|
+
const next = sql[i + 1];
|
|
724
|
+
if (inLineComment) {
|
|
725
|
+
current += ch;
|
|
726
|
+
if (ch === "\n") inLineComment = false;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (inBlockComment) {
|
|
730
|
+
current += ch;
|
|
731
|
+
if (ch === "*" && next === "/") {
|
|
732
|
+
current += next;
|
|
733
|
+
inBlockComment = false;
|
|
734
|
+
i += 1;
|
|
735
|
+
}
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
739
|
+
current += ch + next;
|
|
740
|
+
inLineComment = true;
|
|
741
|
+
i += 1;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
745
|
+
current += ch + next;
|
|
746
|
+
inBlockComment = true;
|
|
747
|
+
i += 1;
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
751
|
+
inSingle = !inSingle;
|
|
752
|
+
current += ch;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
756
|
+
inDouble = !inDouble;
|
|
757
|
+
current += ch;
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
761
|
+
if (current.trim()) {
|
|
762
|
+
parts.push(current.trim());
|
|
763
|
+
}
|
|
764
|
+
current = "";
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
current += ch;
|
|
768
|
+
}
|
|
769
|
+
if (current.trim()) {
|
|
770
|
+
parts.push(current.trim());
|
|
771
|
+
}
|
|
772
|
+
return parts;
|
|
773
|
+
}
|
|
774
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
775
|
+
const prisma = await loadPrismaClient();
|
|
776
|
+
await ensureCompatibilityViews(prisma);
|
|
777
|
+
let closed = false;
|
|
778
|
+
let adapter;
|
|
779
|
+
const fallbackExecute = async (stmt, error) => {
|
|
780
|
+
if (!fallbackClient) {
|
|
781
|
+
if (error) throw error;
|
|
782
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
783
|
+
}
|
|
784
|
+
if (error) {
|
|
785
|
+
process.stderr.write(
|
|
786
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
787
|
+
`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
return fallbackClient.execute(stmt);
|
|
791
|
+
};
|
|
792
|
+
adapter = {
|
|
793
|
+
async execute(stmt) {
|
|
794
|
+
if (shouldBypassPostgres(stmt)) {
|
|
795
|
+
return fallbackExecute(stmt);
|
|
796
|
+
}
|
|
797
|
+
try {
|
|
798
|
+
return await executeOnPrisma(prisma, stmt);
|
|
799
|
+
} catch (error) {
|
|
800
|
+
if (shouldFallbackOnError(error)) {
|
|
801
|
+
return fallbackExecute(stmt, error);
|
|
802
|
+
}
|
|
803
|
+
throw error;
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
async batch(stmts, mode) {
|
|
807
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
808
|
+
if (!fallbackClient) {
|
|
809
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
810
|
+
}
|
|
811
|
+
return fallbackClient.batch(stmts, mode);
|
|
812
|
+
}
|
|
813
|
+
try {
|
|
814
|
+
if (prisma.$transaction) {
|
|
815
|
+
return await prisma.$transaction(async (tx) => {
|
|
816
|
+
const results2 = [];
|
|
817
|
+
for (const stmt of stmts) {
|
|
818
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
819
|
+
}
|
|
820
|
+
return results2;
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
const results = [];
|
|
824
|
+
for (const stmt of stmts) {
|
|
825
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
826
|
+
}
|
|
827
|
+
return results;
|
|
828
|
+
} catch (error) {
|
|
829
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
830
|
+
process.stderr.write(
|
|
831
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
832
|
+
`
|
|
833
|
+
);
|
|
834
|
+
return fallbackClient.batch(stmts, mode);
|
|
835
|
+
}
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
async migrate(stmts) {
|
|
840
|
+
if (fallbackClient) {
|
|
841
|
+
return fallbackClient.migrate(stmts);
|
|
842
|
+
}
|
|
843
|
+
return adapter.batch(stmts, "deferred");
|
|
844
|
+
},
|
|
845
|
+
async transaction(mode) {
|
|
846
|
+
if (!fallbackClient) {
|
|
847
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
848
|
+
}
|
|
849
|
+
return fallbackClient.transaction(mode);
|
|
850
|
+
},
|
|
851
|
+
async executeMultiple(sql) {
|
|
852
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
853
|
+
return fallbackClient.executeMultiple(sql);
|
|
854
|
+
}
|
|
855
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
856
|
+
await adapter.execute(statement);
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
async sync() {
|
|
860
|
+
if (fallbackClient) {
|
|
861
|
+
return fallbackClient.sync();
|
|
862
|
+
}
|
|
863
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
864
|
+
},
|
|
865
|
+
close() {
|
|
866
|
+
closed = true;
|
|
867
|
+
prismaClientPromise = null;
|
|
868
|
+
compatibilityBootstrapPromise = null;
|
|
869
|
+
void prisma.$disconnect?.();
|
|
870
|
+
},
|
|
871
|
+
get closed() {
|
|
872
|
+
return closed;
|
|
873
|
+
},
|
|
874
|
+
get protocol() {
|
|
875
|
+
return "prisma-postgres";
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
return adapter;
|
|
879
|
+
}
|
|
880
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
881
|
+
var init_database_adapter = __esm({
|
|
882
|
+
"src/lib/database-adapter.ts"() {
|
|
883
|
+
"use strict";
|
|
884
|
+
VIEW_MAPPINGS = [
|
|
885
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
886
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
887
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
888
|
+
{ view: "entities", source: "memory.entities" },
|
|
889
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
890
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
891
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
892
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
893
|
+
{ view: "messages", source: "memory.messages" },
|
|
894
|
+
{ view: "users", source: "wiki.users" },
|
|
895
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
896
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
897
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
898
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
899
|
+
];
|
|
900
|
+
UPSERT_KEYS = {
|
|
901
|
+
memories: ["id"],
|
|
902
|
+
tasks: ["id"],
|
|
903
|
+
behaviors: ["id"],
|
|
904
|
+
entities: ["id"],
|
|
905
|
+
relationships: ["id"],
|
|
906
|
+
entity_aliases: ["alias"],
|
|
907
|
+
notifications: ["id"],
|
|
908
|
+
messages: ["id"],
|
|
909
|
+
users: ["id"],
|
|
910
|
+
workspaces: ["id"],
|
|
911
|
+
workspace_users: ["id"],
|
|
912
|
+
documents: ["id"],
|
|
913
|
+
chats: ["id"]
|
|
914
|
+
};
|
|
915
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
916
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
917
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
918
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
919
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
920
|
+
};
|
|
921
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
922
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
923
|
+
);
|
|
924
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
925
|
+
/\bPRAGMA\b/i,
|
|
926
|
+
/\bsqlite_master\b/i,
|
|
927
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
928
|
+
/\bMATCH\b/i,
|
|
929
|
+
/\bvector_distance_cos\s*\(/i,
|
|
930
|
+
/\bjson_extract\s*\(/i,
|
|
931
|
+
/\bjulianday\s*\(/i,
|
|
932
|
+
/\bstrftime\s*\(/i,
|
|
933
|
+
/\blast_insert_rowid\s*\(/i
|
|
934
|
+
];
|
|
935
|
+
prismaClientPromise = null;
|
|
936
|
+
compatibilityBootstrapPromise = null;
|
|
352
937
|
}
|
|
353
938
|
});
|
|
354
939
|
|
|
355
940
|
// src/lib/database.ts
|
|
356
941
|
import { createClient } from "@libsql/client";
|
|
357
942
|
async function initDatabase(config) {
|
|
943
|
+
if (_walCheckpointTimer) {
|
|
944
|
+
clearInterval(_walCheckpointTimer);
|
|
945
|
+
_walCheckpointTimer = null;
|
|
946
|
+
}
|
|
947
|
+
if (_daemonClient) {
|
|
948
|
+
_daemonClient.close();
|
|
949
|
+
_daemonClient = null;
|
|
950
|
+
}
|
|
951
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
952
|
+
_adapterClient.close();
|
|
953
|
+
}
|
|
954
|
+
_adapterClient = null;
|
|
358
955
|
if (_client) {
|
|
359
956
|
_client.close();
|
|
360
957
|
_client = null;
|
|
@@ -368,6 +965,7 @@ async function initDatabase(config) {
|
|
|
368
965
|
}
|
|
369
966
|
_client = createClient(opts);
|
|
370
967
|
_resilientClient = wrapWithRetry(_client);
|
|
968
|
+
_adapterClient = _resilientClient;
|
|
371
969
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
372
970
|
});
|
|
373
971
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -378,11 +976,17 @@ async function initDatabase(config) {
|
|
|
378
976
|
});
|
|
379
977
|
}, 3e4);
|
|
380
978
|
_walCheckpointTimer.unref();
|
|
979
|
+
if (process.env.DATABASE_URL) {
|
|
980
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
981
|
+
}
|
|
381
982
|
}
|
|
382
983
|
function getClient() {
|
|
383
|
-
if (!
|
|
984
|
+
if (!_adapterClient) {
|
|
384
985
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
385
986
|
}
|
|
987
|
+
if (process.env.DATABASE_URL) {
|
|
988
|
+
return _adapterClient;
|
|
989
|
+
}
|
|
386
990
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
387
991
|
return _resilientClient;
|
|
388
992
|
}
|
|
@@ -1323,26 +1927,36 @@ async function ensureSchema() {
|
|
|
1323
1927
|
}
|
|
1324
1928
|
}
|
|
1325
1929
|
async function disposeDatabase() {
|
|
1930
|
+
if (_walCheckpointTimer) {
|
|
1931
|
+
clearInterval(_walCheckpointTimer);
|
|
1932
|
+
_walCheckpointTimer = null;
|
|
1933
|
+
}
|
|
1326
1934
|
if (_daemonClient) {
|
|
1327
1935
|
_daemonClient.close();
|
|
1328
1936
|
_daemonClient = null;
|
|
1329
1937
|
}
|
|
1938
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1939
|
+
_adapterClient.close();
|
|
1940
|
+
}
|
|
1941
|
+
_adapterClient = null;
|
|
1330
1942
|
if (_client) {
|
|
1331
1943
|
_client.close();
|
|
1332
1944
|
_client = null;
|
|
1333
1945
|
_resilientClient = null;
|
|
1334
1946
|
}
|
|
1335
1947
|
}
|
|
1336
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
1948
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1337
1949
|
var init_database = __esm({
|
|
1338
1950
|
"src/lib/database.ts"() {
|
|
1339
1951
|
"use strict";
|
|
1340
1952
|
init_db_retry();
|
|
1341
1953
|
init_employees();
|
|
1954
|
+
init_database_adapter();
|
|
1342
1955
|
_client = null;
|
|
1343
1956
|
_resilientClient = null;
|
|
1344
1957
|
_walCheckpointTimer = null;
|
|
1345
1958
|
_daemonClient = null;
|
|
1959
|
+
_adapterClient = null;
|
|
1346
1960
|
initTurso = initDatabase;
|
|
1347
1961
|
disposeTurso = disposeDatabase;
|
|
1348
1962
|
}
|
|
@@ -1351,13 +1965,13 @@ var init_database = __esm({
|
|
|
1351
1965
|
// src/lib/keychain.ts
|
|
1352
1966
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1353
1967
|
import { existsSync as existsSync3 } from "fs";
|
|
1354
|
-
import
|
|
1355
|
-
import
|
|
1968
|
+
import path4 from "path";
|
|
1969
|
+
import os4 from "os";
|
|
1356
1970
|
function getKeyDir() {
|
|
1357
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1971
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
|
|
1358
1972
|
}
|
|
1359
1973
|
function getKeyPath() {
|
|
1360
|
-
return
|
|
1974
|
+
return path4.join(getKeyDir(), "master.key");
|
|
1361
1975
|
}
|
|
1362
1976
|
async function tryKeytar() {
|
|
1363
1977
|
try {
|
|
@@ -1380,7 +1994,7 @@ async function getMasterKey() {
|
|
|
1380
1994
|
const keyPath = getKeyPath();
|
|
1381
1995
|
if (!existsSync3(keyPath)) {
|
|
1382
1996
|
process.stderr.write(
|
|
1383
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
1997
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
1384
1998
|
`
|
|
1385
1999
|
);
|
|
1386
2000
|
return null;
|
|
@@ -1473,7 +2087,7 @@ __export(shard_manager_exports, {
|
|
|
1473
2087
|
listShards: () => listShards,
|
|
1474
2088
|
shardExists: () => shardExists
|
|
1475
2089
|
});
|
|
1476
|
-
import
|
|
2090
|
+
import path5 from "path";
|
|
1477
2091
|
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1478
2092
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1479
2093
|
function initShardManager(encryptionKey) {
|
|
@@ -1499,7 +2113,7 @@ function getShardClient(projectName) {
|
|
|
1499
2113
|
}
|
|
1500
2114
|
const cached = _shards.get(safeName);
|
|
1501
2115
|
if (cached) return cached;
|
|
1502
|
-
const dbPath =
|
|
2116
|
+
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1503
2117
|
const client = createClient2({
|
|
1504
2118
|
url: `file:${dbPath}`,
|
|
1505
2119
|
encryptionKey: _encryptionKey
|
|
@@ -1509,7 +2123,7 @@ function getShardClient(projectName) {
|
|
|
1509
2123
|
}
|
|
1510
2124
|
function shardExists(projectName) {
|
|
1511
2125
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1512
|
-
return existsSync4(
|
|
2126
|
+
return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1513
2127
|
}
|
|
1514
2128
|
function listShards() {
|
|
1515
2129
|
if (!existsSync4(SHARDS_DIR)) return [];
|
|
@@ -1586,7 +2200,23 @@ async function ensureShardSchema(client) {
|
|
|
1586
2200
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1587
2201
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1588
2202
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1589
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2203
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2204
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2205
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2206
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2207
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2208
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2209
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2210
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2211
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2212
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2213
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2214
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2215
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2216
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2217
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2218
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2219
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1590
2220
|
]) {
|
|
1591
2221
|
try {
|
|
1592
2222
|
await client.execute(col);
|
|
@@ -1698,7 +2328,7 @@ var init_shard_manager = __esm({
|
|
|
1698
2328
|
"src/lib/shard-manager.ts"() {
|
|
1699
2329
|
"use strict";
|
|
1700
2330
|
init_config();
|
|
1701
|
-
SHARDS_DIR =
|
|
2331
|
+
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
1702
2332
|
_shards = /* @__PURE__ */ new Map();
|
|
1703
2333
|
_encryptionKey = null;
|
|
1704
2334
|
_shardingEnabled = false;
|
|
@@ -2561,7 +3191,7 @@ __export(reranker_exports, {
|
|
|
2561
3191
|
rerankWithContext: () => rerankWithContext,
|
|
2562
3192
|
rerankWithScores: () => rerankWithScores
|
|
2563
3193
|
});
|
|
2564
|
-
import
|
|
3194
|
+
import path6 from "path";
|
|
2565
3195
|
import { existsSync as existsSync5 } from "fs";
|
|
2566
3196
|
function resetIdleTimer() {
|
|
2567
3197
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
@@ -2573,17 +3203,17 @@ function resetIdleTimer() {
|
|
|
2573
3203
|
}
|
|
2574
3204
|
}
|
|
2575
3205
|
function isRerankerAvailable() {
|
|
2576
|
-
return existsSync5(
|
|
3206
|
+
return existsSync5(path6.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
2577
3207
|
}
|
|
2578
3208
|
function getRerankerModelPath() {
|
|
2579
|
-
return
|
|
3209
|
+
return path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
2580
3210
|
}
|
|
2581
3211
|
async function ensureLoaded() {
|
|
2582
3212
|
if (_rerankerContext) {
|
|
2583
3213
|
resetIdleTimer();
|
|
2584
3214
|
return;
|
|
2585
3215
|
}
|
|
2586
|
-
const modelPath =
|
|
3216
|
+
const modelPath = path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
2587
3217
|
if (!existsSync5(modelPath)) {
|
|
2588
3218
|
throw new Error(
|
|
2589
3219
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
@@ -2682,11 +3312,11 @@ var init_reranker = __esm({
|
|
|
2682
3312
|
|
|
2683
3313
|
// src/lib/exe-daemon-client.ts
|
|
2684
3314
|
import net from "net";
|
|
2685
|
-
import
|
|
3315
|
+
import os5 from "os";
|
|
2686
3316
|
import { spawn } from "child_process";
|
|
2687
3317
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2688
3318
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2689
|
-
import
|
|
3319
|
+
import path7 from "path";
|
|
2690
3320
|
import { fileURLToPath } from "url";
|
|
2691
3321
|
function handleData(chunk) {
|
|
2692
3322
|
_buffer += chunk.toString();
|
|
@@ -2737,17 +3367,17 @@ function cleanupStaleFiles() {
|
|
|
2737
3367
|
}
|
|
2738
3368
|
}
|
|
2739
3369
|
function findPackageRoot() {
|
|
2740
|
-
let dir =
|
|
2741
|
-
const { root } =
|
|
3370
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
3371
|
+
const { root } = path7.parse(dir);
|
|
2742
3372
|
while (dir !== root) {
|
|
2743
|
-
if (existsSync6(
|
|
2744
|
-
dir =
|
|
3373
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
3374
|
+
dir = path7.dirname(dir);
|
|
2745
3375
|
}
|
|
2746
3376
|
return null;
|
|
2747
3377
|
}
|
|
2748
3378
|
function spawnDaemon() {
|
|
2749
|
-
const freeGB =
|
|
2750
|
-
const totalGB =
|
|
3379
|
+
const freeGB = os5.freemem() / (1024 * 1024 * 1024);
|
|
3380
|
+
const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
|
|
2751
3381
|
if (totalGB <= 8) {
|
|
2752
3382
|
process.stderr.write(
|
|
2753
3383
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -2767,7 +3397,7 @@ function spawnDaemon() {
|
|
|
2767
3397
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2768
3398
|
return;
|
|
2769
3399
|
}
|
|
2770
|
-
const daemonPath =
|
|
3400
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2771
3401
|
if (!existsSync6(daemonPath)) {
|
|
2772
3402
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2773
3403
|
`);
|
|
@@ -2776,7 +3406,7 @@ function spawnDaemon() {
|
|
|
2776
3406
|
const resolvedPath = daemonPath;
|
|
2777
3407
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2778
3408
|
`);
|
|
2779
|
-
const logPath =
|
|
3409
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
2780
3410
|
let stderrFd = "ignore";
|
|
2781
3411
|
try {
|
|
2782
3412
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2927,74 +3557,123 @@ async function pingDaemon() {
|
|
|
2927
3557
|
return null;
|
|
2928
3558
|
}
|
|
2929
3559
|
function killAndRespawnDaemon() {
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
3560
|
+
if (!acquireSpawnLock()) {
|
|
3561
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
3562
|
+
if (_socket) {
|
|
3563
|
+
_socket.destroy();
|
|
3564
|
+
_socket = null;
|
|
3565
|
+
}
|
|
3566
|
+
_connected = false;
|
|
3567
|
+
_buffer = "";
|
|
3568
|
+
return;
|
|
3569
|
+
}
|
|
3570
|
+
try {
|
|
3571
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
3572
|
+
if (existsSync6(PID_PATH)) {
|
|
3573
|
+
try {
|
|
3574
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
3575
|
+
if (pid > 0) {
|
|
3576
|
+
try {
|
|
3577
|
+
process.kill(pid, "SIGKILL");
|
|
3578
|
+
} catch {
|
|
3579
|
+
}
|
|
2938
3580
|
}
|
|
3581
|
+
} catch {
|
|
2939
3582
|
}
|
|
3583
|
+
}
|
|
3584
|
+
if (_socket) {
|
|
3585
|
+
_socket.destroy();
|
|
3586
|
+
_socket = null;
|
|
3587
|
+
}
|
|
3588
|
+
_connected = false;
|
|
3589
|
+
_buffer = "";
|
|
3590
|
+
try {
|
|
3591
|
+
unlinkSync2(PID_PATH);
|
|
2940
3592
|
} catch {
|
|
2941
3593
|
}
|
|
3594
|
+
try {
|
|
3595
|
+
unlinkSync2(SOCKET_PATH);
|
|
3596
|
+
} catch {
|
|
3597
|
+
}
|
|
3598
|
+
spawnDaemon();
|
|
3599
|
+
} finally {
|
|
3600
|
+
releaseSpawnLock();
|
|
2942
3601
|
}
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
_socket = null;
|
|
2946
|
-
}
|
|
2947
|
-
_connected = false;
|
|
2948
|
-
_buffer = "";
|
|
3602
|
+
}
|
|
3603
|
+
function isDaemonTooYoung() {
|
|
2949
3604
|
try {
|
|
2950
|
-
|
|
3605
|
+
const stat = statSync(PID_PATH);
|
|
3606
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
2951
3607
|
} catch {
|
|
3608
|
+
return false;
|
|
2952
3609
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3610
|
+
}
|
|
3611
|
+
async function retryThenRestart(doRequest, label) {
|
|
3612
|
+
const result = await doRequest();
|
|
3613
|
+
if (!result.error) {
|
|
3614
|
+
_consecutiveFailures = 0;
|
|
3615
|
+
return result;
|
|
3616
|
+
}
|
|
3617
|
+
_consecutiveFailures++;
|
|
3618
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
3619
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
3620
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
3621
|
+
`);
|
|
3622
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
3623
|
+
if (!_connected) {
|
|
3624
|
+
if (!await connectToSocket()) continue;
|
|
3625
|
+
}
|
|
3626
|
+
const retry = await doRequest();
|
|
3627
|
+
if (!retry.error) {
|
|
3628
|
+
_consecutiveFailures = 0;
|
|
3629
|
+
return retry;
|
|
3630
|
+
}
|
|
3631
|
+
_consecutiveFailures++;
|
|
3632
|
+
}
|
|
3633
|
+
if (isDaemonTooYoung()) {
|
|
3634
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
3635
|
+
`);
|
|
3636
|
+
return { error: result.error };
|
|
3637
|
+
}
|
|
3638
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
3639
|
+
`);
|
|
3640
|
+
killAndRespawnDaemon();
|
|
3641
|
+
const start = Date.now();
|
|
3642
|
+
let delay2 = 200;
|
|
3643
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
3644
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
3645
|
+
if (await connectToSocket()) break;
|
|
3646
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2956
3647
|
}
|
|
2957
|
-
|
|
3648
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
3649
|
+
const final = await doRequest();
|
|
3650
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
3651
|
+
return final;
|
|
2958
3652
|
}
|
|
2959
3653
|
async function embedViaClient(text, priority = "high") {
|
|
2960
3654
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
2961
3655
|
_requestCount++;
|
|
2962
3656
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
2963
3657
|
const health = await pingDaemon();
|
|
2964
|
-
if (!health) {
|
|
3658
|
+
if (!health && !isDaemonTooYoung()) {
|
|
2965
3659
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
2966
3660
|
`);
|
|
2967
3661
|
killAndRespawnDaemon();
|
|
2968
3662
|
const start = Date.now();
|
|
2969
|
-
let
|
|
3663
|
+
let d = 200;
|
|
2970
3664
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2971
|
-
await new Promise((r) => setTimeout(r,
|
|
3665
|
+
await new Promise((r) => setTimeout(r, d));
|
|
2972
3666
|
if (await connectToSocket()) break;
|
|
2973
|
-
|
|
3667
|
+
d = Math.min(d * 2, 3e3);
|
|
2974
3668
|
}
|
|
2975
3669
|
if (!_connected) return null;
|
|
2976
3670
|
}
|
|
2977
3671
|
}
|
|
2978
|
-
const result = await
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
killAndRespawnDaemon();
|
|
2984
|
-
const start = Date.now();
|
|
2985
|
-
let delay2 = 200;
|
|
2986
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2987
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
2988
|
-
if (await connectToSocket()) break;
|
|
2989
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2990
|
-
}
|
|
2991
|
-
if (!_connected) return null;
|
|
2992
|
-
const retry = await sendRequest([text], priority);
|
|
2993
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
2994
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
2995
|
-
`);
|
|
2996
|
-
}
|
|
2997
|
-
return null;
|
|
3672
|
+
const result = await retryThenRestart(
|
|
3673
|
+
() => sendRequest([text], priority),
|
|
3674
|
+
"Embed"
|
|
3675
|
+
);
|
|
3676
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2998
3677
|
}
|
|
2999
3678
|
function disconnectClient() {
|
|
3000
3679
|
if (_socket) {
|
|
@@ -3009,14 +3688,14 @@ function disconnectClient() {
|
|
|
3009
3688
|
entry.resolve({ error: "Client disconnected" });
|
|
3010
3689
|
}
|
|
3011
3690
|
}
|
|
3012
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
3691
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
3013
3692
|
var init_exe_daemon_client = __esm({
|
|
3014
3693
|
"src/lib/exe-daemon-client.ts"() {
|
|
3015
3694
|
"use strict";
|
|
3016
3695
|
init_config();
|
|
3017
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
3018
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
3019
|
-
SPAWN_LOCK_PATH =
|
|
3696
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
3697
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
3698
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
3020
3699
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
3021
3700
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
3022
3701
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -3024,7 +3703,11 @@ var init_exe_daemon_client = __esm({
|
|
|
3024
3703
|
_connected = false;
|
|
3025
3704
|
_buffer = "";
|
|
3026
3705
|
_requestCount = 0;
|
|
3706
|
+
_consecutiveFailures = 0;
|
|
3027
3707
|
HEALTH_CHECK_INTERVAL = 100;
|
|
3708
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
3709
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
3710
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
3028
3711
|
_pending = /* @__PURE__ */ new Map();
|
|
3029
3712
|
MAX_BUFFER = 1e7;
|
|
3030
3713
|
}
|
|
@@ -3068,8 +3751,8 @@ async function embedDirect(text) {
|
|
|
3068
3751
|
const llamaCpp = await import("node-llama-cpp");
|
|
3069
3752
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3070
3753
|
const { existsSync: existsSync8 } = await import("fs");
|
|
3071
|
-
const
|
|
3072
|
-
const modelPath =
|
|
3754
|
+
const path11 = await import("path");
|
|
3755
|
+
const modelPath = path11.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3073
3756
|
if (!existsSync8(modelPath)) {
|
|
3074
3757
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
3075
3758
|
}
|
|
@@ -3105,7 +3788,7 @@ __export(project_name_exports, {
|
|
|
3105
3788
|
getProjectName: () => getProjectName
|
|
3106
3789
|
});
|
|
3107
3790
|
import { execSync as execSync2 } from "child_process";
|
|
3108
|
-
import
|
|
3791
|
+
import path8 from "path";
|
|
3109
3792
|
function getProjectName(cwd) {
|
|
3110
3793
|
const dir = cwd ?? process.cwd();
|
|
3111
3794
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
@@ -3118,7 +3801,7 @@ function getProjectName(cwd) {
|
|
|
3118
3801
|
timeout: 2e3,
|
|
3119
3802
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3120
3803
|
}).trim();
|
|
3121
|
-
repoRoot =
|
|
3804
|
+
repoRoot = path8.dirname(gitCommonDir);
|
|
3122
3805
|
} catch {
|
|
3123
3806
|
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
3124
3807
|
cwd: dir,
|
|
@@ -3127,11 +3810,11 @@ function getProjectName(cwd) {
|
|
|
3127
3810
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3128
3811
|
}).trim();
|
|
3129
3812
|
}
|
|
3130
|
-
_cached =
|
|
3813
|
+
_cached = path8.basename(repoRoot);
|
|
3131
3814
|
_cachedCwd = dir;
|
|
3132
3815
|
return _cached;
|
|
3133
3816
|
} catch {
|
|
3134
|
-
_cached =
|
|
3817
|
+
_cached = path8.basename(dir);
|
|
3135
3818
|
_cachedCwd = dir;
|
|
3136
3819
|
return _cached;
|
|
3137
3820
|
}
|
|
@@ -3156,7 +3839,7 @@ __export(file_grep_exports, {
|
|
|
3156
3839
|
});
|
|
3157
3840
|
import { execSync as execSync3 } from "child_process";
|
|
3158
3841
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync7 } from "fs";
|
|
3159
|
-
import
|
|
3842
|
+
import path9 from "path";
|
|
3160
3843
|
import crypto2 from "crypto";
|
|
3161
3844
|
function hasRipgrep() {
|
|
3162
3845
|
if (_hasRg === null) {
|
|
@@ -3196,7 +3879,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
3196
3879
|
session_id: "file-grep",
|
|
3197
3880
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3198
3881
|
tool_name: "file_grep",
|
|
3199
|
-
project_name:
|
|
3882
|
+
project_name: path9.basename(projectRoot),
|
|
3200
3883
|
has_error: false,
|
|
3201
3884
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
3202
3885
|
vector: null,
|
|
@@ -3270,7 +3953,7 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
3270
3953
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
3271
3954
|
const hits = [];
|
|
3272
3955
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
3273
|
-
const absPath =
|
|
3956
|
+
const absPath = path9.join(projectRoot, filePath);
|
|
3274
3957
|
try {
|
|
3275
3958
|
const stat = statSync2(absPath);
|
|
3276
3959
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
@@ -3297,15 +3980,15 @@ function collectFiles(root, patterns) {
|
|
|
3297
3980
|
const files = [];
|
|
3298
3981
|
function walk(dir, relative) {
|
|
3299
3982
|
if (files.length >= MAX_FILES) return;
|
|
3300
|
-
const basename =
|
|
3983
|
+
const basename = path9.basename(dir);
|
|
3301
3984
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
3302
3985
|
try {
|
|
3303
3986
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
3304
3987
|
for (const entry of entries) {
|
|
3305
3988
|
if (files.length >= MAX_FILES) return;
|
|
3306
|
-
const rel =
|
|
3989
|
+
const rel = path9.join(relative, entry.name);
|
|
3307
3990
|
if (entry.isDirectory()) {
|
|
3308
|
-
walk(
|
|
3991
|
+
walk(path9.join(dir, entry.name), rel);
|
|
3309
3992
|
} else if (entry.isFile()) {
|
|
3310
3993
|
for (const pat of patterns) {
|
|
3311
3994
|
if (matchGlob(rel, pat)) {
|
|
@@ -3337,7 +4020,7 @@ function matchGlob(filePath, pattern) {
|
|
|
3337
4020
|
if (slashIdx !== -1) {
|
|
3338
4021
|
const dir = pattern.slice(0, slashIdx);
|
|
3339
4022
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
3340
|
-
const fileDir =
|
|
4023
|
+
const fileDir = path9.dirname(filePath);
|
|
3341
4024
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
3342
4025
|
}
|
|
3343
4026
|
const ext = pattern.replace("*", "");
|
|
@@ -3345,7 +4028,7 @@ function matchGlob(filePath, pattern) {
|
|
|
3345
4028
|
}
|
|
3346
4029
|
function buildSnippet(hit, projectRoot) {
|
|
3347
4030
|
try {
|
|
3348
|
-
const absPath =
|
|
4031
|
+
const absPath = path9.join(projectRoot, hit.filePath);
|
|
3349
4032
|
if (!existsSync7(absPath)) return hit.matchLine;
|
|
3350
4033
|
const lines = readFileSync4(absPath, "utf8").split("\n");
|
|
3351
4034
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
@@ -4593,7 +5276,7 @@ init_database();
|
|
|
4593
5276
|
init_config();
|
|
4594
5277
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
4595
5278
|
import { execSync as execSync5 } from "child_process";
|
|
4596
|
-
import
|
|
5279
|
+
import path10 from "path";
|
|
4597
5280
|
|
|
4598
5281
|
// src/lib/session-key.ts
|
|
4599
5282
|
import { execSync as execSync4 } from "child_process";
|
|
@@ -4659,7 +5342,7 @@ function getSessionKey() {
|
|
|
4659
5342
|
|
|
4660
5343
|
// src/lib/active-agent.ts
|
|
4661
5344
|
init_employees();
|
|
4662
|
-
var CACHE_DIR =
|
|
5345
|
+
var CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
|
|
4663
5346
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4664
5347
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4665
5348
|
if (candidate === baseName) return true;
|
|
@@ -4704,7 +5387,7 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
4704
5387
|
return null;
|
|
4705
5388
|
}
|
|
4706
5389
|
function getMarkerPath() {
|
|
4707
|
-
return
|
|
5390
|
+
return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4708
5391
|
}
|
|
4709
5392
|
function getActiveAgent() {
|
|
4710
5393
|
try {
|
|
@@ -4774,10 +5457,10 @@ process.stdin.on("end", async () => {
|
|
|
4774
5457
|
}
|
|
4775
5458
|
if (process.env.EXE_DEBUG_HOOKS) {
|
|
4776
5459
|
try {
|
|
4777
|
-
const
|
|
5460
|
+
const os6 = await import("os");
|
|
4778
5461
|
const fs = await import("fs");
|
|
4779
5462
|
const nodePath = await import("path");
|
|
4780
|
-
const debugPath = nodePath.default.join(
|
|
5463
|
+
const debugPath = nodePath.default.join(os6.default.homedir(), ".exe-os", "logs", "hook-stdin-error-recall.log");
|
|
4781
5464
|
fs.mkdirSync(nodePath.default.dirname(debugPath), { recursive: true });
|
|
4782
5465
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4783
5466
|
const snippet = input.length > 2e3 ? input.slice(0, 2e3) + "...[truncated]" : input;
|