@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
|
@@ -417,7 +417,7 @@ function registerBinSymlinks(name) {
|
|
|
417
417
|
}
|
|
418
418
|
return { created, skipped, errors };
|
|
419
419
|
}
|
|
420
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
420
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
421
421
|
var init_employees = __esm({
|
|
422
422
|
"src/lib/employees.ts"() {
|
|
423
423
|
"use strict";
|
|
@@ -425,16 +425,601 @@ var init_employees = __esm({
|
|
|
425
425
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
426
426
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
427
427
|
COORDINATOR_ROLE = "COO";
|
|
428
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// src/lib/database-adapter.ts
|
|
433
|
+
import os3 from "os";
|
|
434
|
+
import path3 from "path";
|
|
435
|
+
import { createRequire } from "module";
|
|
436
|
+
import { pathToFileURL } from "url";
|
|
437
|
+
function quotedIdentifier(identifier) {
|
|
438
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
439
|
+
}
|
|
440
|
+
function unqualifiedTableName(name) {
|
|
441
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
442
|
+
const parts = raw.split(".");
|
|
443
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
444
|
+
}
|
|
445
|
+
function stripTrailingSemicolon(sql) {
|
|
446
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
447
|
+
}
|
|
448
|
+
function appendClause(sql, clause) {
|
|
449
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
450
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
451
|
+
if (!returningMatch) {
|
|
452
|
+
return `${trimmed}${clause}`;
|
|
453
|
+
}
|
|
454
|
+
const idx = returningMatch.index;
|
|
455
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
456
|
+
}
|
|
457
|
+
function normalizeStatement(stmt) {
|
|
458
|
+
if (typeof stmt === "string") {
|
|
459
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
460
|
+
}
|
|
461
|
+
const sql = stmt.sql;
|
|
462
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
463
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
464
|
+
}
|
|
465
|
+
return { kind: "named", sql, args: stmt.args };
|
|
466
|
+
}
|
|
467
|
+
function rewriteBooleanLiterals(sql) {
|
|
468
|
+
let out = sql;
|
|
469
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
470
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
471
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
472
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
473
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
474
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
475
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
476
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
function rewriteInsertOrIgnore(sql) {
|
|
481
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
482
|
+
return sql;
|
|
483
|
+
}
|
|
484
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
485
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
486
|
+
}
|
|
487
|
+
function rewriteInsertOrReplace(sql) {
|
|
488
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
489
|
+
if (!match) {
|
|
490
|
+
return sql;
|
|
491
|
+
}
|
|
492
|
+
const rawTable = match[1];
|
|
493
|
+
const rawColumns = match[2];
|
|
494
|
+
const remainder = match[3];
|
|
495
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
496
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
497
|
+
if (!conflictKeys?.length) {
|
|
498
|
+
return sql;
|
|
499
|
+
}
|
|
500
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
501
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
502
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
503
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
504
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
505
|
+
}
|
|
506
|
+
function rewriteSql(sql) {
|
|
507
|
+
let out = sql;
|
|
508
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
509
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
510
|
+
out = rewriteBooleanLiterals(out);
|
|
511
|
+
out = rewriteInsertOrReplace(out);
|
|
512
|
+
out = rewriteInsertOrIgnore(out);
|
|
513
|
+
return stripTrailingSemicolon(out);
|
|
514
|
+
}
|
|
515
|
+
function toBoolean(value) {
|
|
516
|
+
if (value === null || value === void 0) return value;
|
|
517
|
+
if (typeof value === "boolean") return value;
|
|
518
|
+
if (typeof value === "number") return value !== 0;
|
|
519
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
520
|
+
if (typeof value === "string") {
|
|
521
|
+
const normalized = value.trim().toLowerCase();
|
|
522
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
523
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
524
|
+
}
|
|
525
|
+
return Boolean(value);
|
|
526
|
+
}
|
|
527
|
+
function countQuestionMarks(sql, end) {
|
|
528
|
+
let count = 0;
|
|
529
|
+
let inSingle = false;
|
|
530
|
+
let inDouble = false;
|
|
531
|
+
let inLineComment = false;
|
|
532
|
+
let inBlockComment = false;
|
|
533
|
+
for (let i = 0; i < end; i++) {
|
|
534
|
+
const ch = sql[i];
|
|
535
|
+
const next = sql[i + 1];
|
|
536
|
+
if (inLineComment) {
|
|
537
|
+
if (ch === "\n") inLineComment = false;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (inBlockComment) {
|
|
541
|
+
if (ch === "*" && next === "/") {
|
|
542
|
+
inBlockComment = false;
|
|
543
|
+
i += 1;
|
|
544
|
+
}
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
548
|
+
inLineComment = true;
|
|
549
|
+
i += 1;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
553
|
+
inBlockComment = true;
|
|
554
|
+
i += 1;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
558
|
+
inSingle = !inSingle;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
562
|
+
inDouble = !inDouble;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
566
|
+
count += 1;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return count;
|
|
570
|
+
}
|
|
571
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
572
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
573
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
574
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
575
|
+
for (const match of sql.matchAll(pattern)) {
|
|
576
|
+
const matchText = match[0];
|
|
577
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
578
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return indexes;
|
|
582
|
+
}
|
|
583
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
584
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
585
|
+
if (!match) return;
|
|
586
|
+
const rawTable = match[1];
|
|
587
|
+
const rawColumns = match[2];
|
|
588
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
589
|
+
if (!boolColumns?.size) return;
|
|
590
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
591
|
+
for (const [index, column] of columns.entries()) {
|
|
592
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
593
|
+
args[index] = toBoolean(args[index]);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
598
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
599
|
+
if (!match) return;
|
|
600
|
+
const rawTable = match[1];
|
|
601
|
+
const setClause = match[2];
|
|
602
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
603
|
+
if (!boolColumns?.size) return;
|
|
604
|
+
const assignments = setClause.split(",");
|
|
605
|
+
let placeholderIndex = 0;
|
|
606
|
+
for (const assignment of assignments) {
|
|
607
|
+
if (!assignment.includes("?")) continue;
|
|
608
|
+
placeholderIndex += 1;
|
|
609
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
610
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
611
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function coerceBooleanArgs(sql, args) {
|
|
616
|
+
const nextArgs = [...args];
|
|
617
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
618
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
619
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
620
|
+
for (const index of placeholderIndexes) {
|
|
621
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
622
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return nextArgs;
|
|
626
|
+
}
|
|
627
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
628
|
+
let out = "";
|
|
629
|
+
let placeholder = 0;
|
|
630
|
+
let inSingle = false;
|
|
631
|
+
let inDouble = false;
|
|
632
|
+
let inLineComment = false;
|
|
633
|
+
let inBlockComment = false;
|
|
634
|
+
for (let i = 0; i < sql.length; i++) {
|
|
635
|
+
const ch = sql[i];
|
|
636
|
+
const next = sql[i + 1];
|
|
637
|
+
if (inLineComment) {
|
|
638
|
+
out += ch;
|
|
639
|
+
if (ch === "\n") inLineComment = false;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
if (inBlockComment) {
|
|
643
|
+
out += ch;
|
|
644
|
+
if (ch === "*" && next === "/") {
|
|
645
|
+
out += next;
|
|
646
|
+
inBlockComment = false;
|
|
647
|
+
i += 1;
|
|
648
|
+
}
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
652
|
+
out += ch + next;
|
|
653
|
+
inLineComment = true;
|
|
654
|
+
i += 1;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
658
|
+
out += ch + next;
|
|
659
|
+
inBlockComment = true;
|
|
660
|
+
i += 1;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
664
|
+
inSingle = !inSingle;
|
|
665
|
+
out += ch;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
669
|
+
inDouble = !inDouble;
|
|
670
|
+
out += ch;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
674
|
+
placeholder += 1;
|
|
675
|
+
out += `$${placeholder}`;
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
out += ch;
|
|
679
|
+
}
|
|
680
|
+
return out;
|
|
681
|
+
}
|
|
682
|
+
function translateStatementForPostgres(stmt) {
|
|
683
|
+
const normalized = normalizeStatement(stmt);
|
|
684
|
+
if (normalized.kind === "named") {
|
|
685
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
686
|
+
}
|
|
687
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
688
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
689
|
+
return {
|
|
690
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
691
|
+
args: coercedArgs
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function shouldBypassPostgres(stmt) {
|
|
695
|
+
const normalized = normalizeStatement(stmt);
|
|
696
|
+
if (normalized.kind === "named") {
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
700
|
+
}
|
|
701
|
+
function shouldFallbackOnError(error) {
|
|
702
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
703
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
704
|
+
}
|
|
705
|
+
function isReadQuery(sql) {
|
|
706
|
+
const trimmed = sql.trimStart();
|
|
707
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
708
|
+
}
|
|
709
|
+
function buildRow(row, columns) {
|
|
710
|
+
const values = columns.map((column) => row[column]);
|
|
711
|
+
return Object.assign(values, row);
|
|
712
|
+
}
|
|
713
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
714
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
715
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
716
|
+
return {
|
|
717
|
+
columns,
|
|
718
|
+
columnTypes: columns.map(() => ""),
|
|
719
|
+
rows: resultRows,
|
|
720
|
+
rowsAffected,
|
|
721
|
+
lastInsertRowid: void 0,
|
|
722
|
+
toJSON() {
|
|
723
|
+
return {
|
|
724
|
+
columns,
|
|
725
|
+
columnTypes: columns.map(() => ""),
|
|
726
|
+
rows,
|
|
727
|
+
rowsAffected,
|
|
728
|
+
lastInsertRowid: void 0
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
async function loadPrismaClient() {
|
|
734
|
+
if (!prismaClientPromise) {
|
|
735
|
+
prismaClientPromise = (async () => {
|
|
736
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
737
|
+
if (explicitPath) {
|
|
738
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
739
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
740
|
+
if (!PrismaClient2) {
|
|
741
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
742
|
+
}
|
|
743
|
+
return new PrismaClient2();
|
|
744
|
+
}
|
|
745
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
746
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
747
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
748
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
749
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
750
|
+
if (!PrismaClient) {
|
|
751
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
752
|
+
}
|
|
753
|
+
return new PrismaClient();
|
|
754
|
+
})();
|
|
755
|
+
}
|
|
756
|
+
return prismaClientPromise;
|
|
757
|
+
}
|
|
758
|
+
async function ensureCompatibilityViews(prisma) {
|
|
759
|
+
if (!compatibilityBootstrapPromise) {
|
|
760
|
+
compatibilityBootstrapPromise = (async () => {
|
|
761
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
762
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
763
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
764
|
+
"SELECT to_regclass($1) AS regclass",
|
|
765
|
+
relation
|
|
766
|
+
);
|
|
767
|
+
if (!rows[0]?.regclass) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
await prisma.$executeRawUnsafe(
|
|
771
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
})();
|
|
775
|
+
}
|
|
776
|
+
return compatibilityBootstrapPromise;
|
|
777
|
+
}
|
|
778
|
+
async function executeOnPrisma(executor, stmt) {
|
|
779
|
+
const translated = translateStatementForPostgres(stmt);
|
|
780
|
+
if (isReadQuery(translated.sql)) {
|
|
781
|
+
const rows = await executor.$queryRawUnsafe(
|
|
782
|
+
translated.sql,
|
|
783
|
+
...translated.args
|
|
784
|
+
);
|
|
785
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
786
|
+
}
|
|
787
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
788
|
+
return buildResultSet([], rowsAffected);
|
|
789
|
+
}
|
|
790
|
+
function splitSqlStatements(sql) {
|
|
791
|
+
const parts = [];
|
|
792
|
+
let current = "";
|
|
793
|
+
let inSingle = false;
|
|
794
|
+
let inDouble = false;
|
|
795
|
+
let inLineComment = false;
|
|
796
|
+
let inBlockComment = false;
|
|
797
|
+
for (let i = 0; i < sql.length; i++) {
|
|
798
|
+
const ch = sql[i];
|
|
799
|
+
const next = sql[i + 1];
|
|
800
|
+
if (inLineComment) {
|
|
801
|
+
current += ch;
|
|
802
|
+
if (ch === "\n") inLineComment = false;
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (inBlockComment) {
|
|
806
|
+
current += ch;
|
|
807
|
+
if (ch === "*" && next === "/") {
|
|
808
|
+
current += next;
|
|
809
|
+
inBlockComment = false;
|
|
810
|
+
i += 1;
|
|
811
|
+
}
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
815
|
+
current += ch + next;
|
|
816
|
+
inLineComment = true;
|
|
817
|
+
i += 1;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
821
|
+
current += ch + next;
|
|
822
|
+
inBlockComment = true;
|
|
823
|
+
i += 1;
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
827
|
+
inSingle = !inSingle;
|
|
828
|
+
current += ch;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
832
|
+
inDouble = !inDouble;
|
|
833
|
+
current += ch;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
837
|
+
if (current.trim()) {
|
|
838
|
+
parts.push(current.trim());
|
|
839
|
+
}
|
|
840
|
+
current = "";
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
current += ch;
|
|
844
|
+
}
|
|
845
|
+
if (current.trim()) {
|
|
846
|
+
parts.push(current.trim());
|
|
847
|
+
}
|
|
848
|
+
return parts;
|
|
849
|
+
}
|
|
850
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
851
|
+
const prisma = await loadPrismaClient();
|
|
852
|
+
await ensureCompatibilityViews(prisma);
|
|
853
|
+
let closed = false;
|
|
854
|
+
let adapter;
|
|
855
|
+
const fallbackExecute = async (stmt, error) => {
|
|
856
|
+
if (!fallbackClient) {
|
|
857
|
+
if (error) throw error;
|
|
858
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
859
|
+
}
|
|
860
|
+
if (error) {
|
|
861
|
+
process.stderr.write(
|
|
862
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
863
|
+
`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
return fallbackClient.execute(stmt);
|
|
867
|
+
};
|
|
868
|
+
adapter = {
|
|
869
|
+
async execute(stmt) {
|
|
870
|
+
if (shouldBypassPostgres(stmt)) {
|
|
871
|
+
return fallbackExecute(stmt);
|
|
872
|
+
}
|
|
873
|
+
try {
|
|
874
|
+
return await executeOnPrisma(prisma, stmt);
|
|
875
|
+
} catch (error) {
|
|
876
|
+
if (shouldFallbackOnError(error)) {
|
|
877
|
+
return fallbackExecute(stmt, error);
|
|
878
|
+
}
|
|
879
|
+
throw error;
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
async batch(stmts, mode) {
|
|
883
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
884
|
+
if (!fallbackClient) {
|
|
885
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
886
|
+
}
|
|
887
|
+
return fallbackClient.batch(stmts, mode);
|
|
888
|
+
}
|
|
889
|
+
try {
|
|
890
|
+
if (prisma.$transaction) {
|
|
891
|
+
return await prisma.$transaction(async (tx) => {
|
|
892
|
+
const results2 = [];
|
|
893
|
+
for (const stmt of stmts) {
|
|
894
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
895
|
+
}
|
|
896
|
+
return results2;
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
const results = [];
|
|
900
|
+
for (const stmt of stmts) {
|
|
901
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
902
|
+
}
|
|
903
|
+
return results;
|
|
904
|
+
} catch (error) {
|
|
905
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
906
|
+
process.stderr.write(
|
|
907
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
908
|
+
`
|
|
909
|
+
);
|
|
910
|
+
return fallbackClient.batch(stmts, mode);
|
|
911
|
+
}
|
|
912
|
+
throw error;
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
async migrate(stmts) {
|
|
916
|
+
if (fallbackClient) {
|
|
917
|
+
return fallbackClient.migrate(stmts);
|
|
918
|
+
}
|
|
919
|
+
return adapter.batch(stmts, "deferred");
|
|
920
|
+
},
|
|
921
|
+
async transaction(mode) {
|
|
922
|
+
if (!fallbackClient) {
|
|
923
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
924
|
+
}
|
|
925
|
+
return fallbackClient.transaction(mode);
|
|
926
|
+
},
|
|
927
|
+
async executeMultiple(sql) {
|
|
928
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
929
|
+
return fallbackClient.executeMultiple(sql);
|
|
930
|
+
}
|
|
931
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
932
|
+
await adapter.execute(statement);
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
async sync() {
|
|
936
|
+
if (fallbackClient) {
|
|
937
|
+
return fallbackClient.sync();
|
|
938
|
+
}
|
|
939
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
940
|
+
},
|
|
941
|
+
close() {
|
|
942
|
+
closed = true;
|
|
943
|
+
prismaClientPromise = null;
|
|
944
|
+
compatibilityBootstrapPromise = null;
|
|
945
|
+
void prisma.$disconnect?.();
|
|
946
|
+
},
|
|
947
|
+
get closed() {
|
|
948
|
+
return closed;
|
|
949
|
+
},
|
|
950
|
+
get protocol() {
|
|
951
|
+
return "prisma-postgres";
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
return adapter;
|
|
955
|
+
}
|
|
956
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
957
|
+
var init_database_adapter = __esm({
|
|
958
|
+
"src/lib/database-adapter.ts"() {
|
|
959
|
+
"use strict";
|
|
960
|
+
VIEW_MAPPINGS = [
|
|
961
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
962
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
963
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
964
|
+
{ view: "entities", source: "memory.entities" },
|
|
965
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
966
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
967
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
968
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
969
|
+
{ view: "messages", source: "memory.messages" },
|
|
970
|
+
{ view: "users", source: "wiki.users" },
|
|
971
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
972
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
973
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
974
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
975
|
+
];
|
|
976
|
+
UPSERT_KEYS = {
|
|
977
|
+
memories: ["id"],
|
|
978
|
+
tasks: ["id"],
|
|
979
|
+
behaviors: ["id"],
|
|
980
|
+
entities: ["id"],
|
|
981
|
+
relationships: ["id"],
|
|
982
|
+
entity_aliases: ["alias"],
|
|
983
|
+
notifications: ["id"],
|
|
984
|
+
messages: ["id"],
|
|
985
|
+
users: ["id"],
|
|
986
|
+
workspaces: ["id"],
|
|
987
|
+
workspace_users: ["id"],
|
|
988
|
+
documents: ["id"],
|
|
989
|
+
chats: ["id"]
|
|
990
|
+
};
|
|
991
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
992
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
993
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
994
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
995
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
996
|
+
};
|
|
997
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
998
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
999
|
+
);
|
|
1000
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1001
|
+
/\bPRAGMA\b/i,
|
|
1002
|
+
/\bsqlite_master\b/i,
|
|
1003
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1004
|
+
/\bMATCH\b/i,
|
|
1005
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1006
|
+
/\bjson_extract\s*\(/i,
|
|
1007
|
+
/\bjulianday\s*\(/i,
|
|
1008
|
+
/\bstrftime\s*\(/i,
|
|
1009
|
+
/\blast_insert_rowid\s*\(/i
|
|
1010
|
+
];
|
|
1011
|
+
prismaClientPromise = null;
|
|
1012
|
+
compatibilityBootstrapPromise = null;
|
|
428
1013
|
}
|
|
429
1014
|
});
|
|
430
1015
|
|
|
431
1016
|
// src/lib/exe-daemon-client.ts
|
|
432
1017
|
import net from "net";
|
|
433
|
-
import
|
|
1018
|
+
import os4 from "os";
|
|
434
1019
|
import { spawn } from "child_process";
|
|
435
1020
|
import { randomUUID } from "crypto";
|
|
436
1021
|
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
437
|
-
import
|
|
1022
|
+
import path4 from "path";
|
|
438
1023
|
import { fileURLToPath } from "url";
|
|
439
1024
|
function handleData(chunk) {
|
|
440
1025
|
_buffer += chunk.toString();
|
|
@@ -485,17 +1070,17 @@ function cleanupStaleFiles() {
|
|
|
485
1070
|
}
|
|
486
1071
|
}
|
|
487
1072
|
function findPackageRoot() {
|
|
488
|
-
let dir =
|
|
489
|
-
const { root } =
|
|
1073
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1074
|
+
const { root } = path4.parse(dir);
|
|
490
1075
|
while (dir !== root) {
|
|
491
|
-
if (existsSync3(
|
|
492
|
-
dir =
|
|
1076
|
+
if (existsSync3(path4.join(dir, "package.json"))) return dir;
|
|
1077
|
+
dir = path4.dirname(dir);
|
|
493
1078
|
}
|
|
494
1079
|
return null;
|
|
495
1080
|
}
|
|
496
1081
|
function spawnDaemon() {
|
|
497
|
-
const freeGB =
|
|
498
|
-
const totalGB =
|
|
1082
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
1083
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
499
1084
|
if (totalGB <= 8) {
|
|
500
1085
|
process.stderr.write(
|
|
501
1086
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -515,7 +1100,7 @@ function spawnDaemon() {
|
|
|
515
1100
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
516
1101
|
return;
|
|
517
1102
|
}
|
|
518
|
-
const daemonPath =
|
|
1103
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
519
1104
|
if (!existsSync3(daemonPath)) {
|
|
520
1105
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
521
1106
|
`);
|
|
@@ -524,7 +1109,7 @@ function spawnDaemon() {
|
|
|
524
1109
|
const resolvedPath = daemonPath;
|
|
525
1110
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
526
1111
|
`);
|
|
527
|
-
const logPath =
|
|
1112
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
528
1113
|
let stderrFd = "ignore";
|
|
529
1114
|
try {
|
|
530
1115
|
stderrFd = openSync(logPath, "a");
|
|
@@ -675,74 +1260,123 @@ async function pingDaemon() {
|
|
|
675
1260
|
return null;
|
|
676
1261
|
}
|
|
677
1262
|
function killAndRespawnDaemon() {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1263
|
+
if (!acquireSpawnLock()) {
|
|
1264
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1265
|
+
if (_socket) {
|
|
1266
|
+
_socket.destroy();
|
|
1267
|
+
_socket = null;
|
|
1268
|
+
}
|
|
1269
|
+
_connected = false;
|
|
1270
|
+
_buffer = "";
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
try {
|
|
1274
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1275
|
+
if (existsSync3(PID_PATH)) {
|
|
1276
|
+
try {
|
|
1277
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
1278
|
+
if (pid > 0) {
|
|
1279
|
+
try {
|
|
1280
|
+
process.kill(pid, "SIGKILL");
|
|
1281
|
+
} catch {
|
|
1282
|
+
}
|
|
686
1283
|
}
|
|
1284
|
+
} catch {
|
|
687
1285
|
}
|
|
1286
|
+
}
|
|
1287
|
+
if (_socket) {
|
|
1288
|
+
_socket.destroy();
|
|
1289
|
+
_socket = null;
|
|
1290
|
+
}
|
|
1291
|
+
_connected = false;
|
|
1292
|
+
_buffer = "";
|
|
1293
|
+
try {
|
|
1294
|
+
unlinkSync2(PID_PATH);
|
|
688
1295
|
} catch {
|
|
689
1296
|
}
|
|
1297
|
+
try {
|
|
1298
|
+
unlinkSync2(SOCKET_PATH);
|
|
1299
|
+
} catch {
|
|
1300
|
+
}
|
|
1301
|
+
spawnDaemon();
|
|
1302
|
+
} finally {
|
|
1303
|
+
releaseSpawnLock();
|
|
690
1304
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
_socket = null;
|
|
694
|
-
}
|
|
695
|
-
_connected = false;
|
|
696
|
-
_buffer = "";
|
|
1305
|
+
}
|
|
1306
|
+
function isDaemonTooYoung() {
|
|
697
1307
|
try {
|
|
698
|
-
|
|
1308
|
+
const stat = statSync(PID_PATH);
|
|
1309
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
699
1310
|
} catch {
|
|
1311
|
+
return false;
|
|
700
1312
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1313
|
+
}
|
|
1314
|
+
async function retryThenRestart(doRequest, label) {
|
|
1315
|
+
const result = await doRequest();
|
|
1316
|
+
if (!result.error) {
|
|
1317
|
+
_consecutiveFailures = 0;
|
|
1318
|
+
return result;
|
|
704
1319
|
}
|
|
705
|
-
|
|
1320
|
+
_consecutiveFailures++;
|
|
1321
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1322
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1323
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1324
|
+
`);
|
|
1325
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1326
|
+
if (!_connected) {
|
|
1327
|
+
if (!await connectToSocket()) continue;
|
|
1328
|
+
}
|
|
1329
|
+
const retry = await doRequest();
|
|
1330
|
+
if (!retry.error) {
|
|
1331
|
+
_consecutiveFailures = 0;
|
|
1332
|
+
return retry;
|
|
1333
|
+
}
|
|
1334
|
+
_consecutiveFailures++;
|
|
1335
|
+
}
|
|
1336
|
+
if (isDaemonTooYoung()) {
|
|
1337
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1338
|
+
`);
|
|
1339
|
+
return { error: result.error };
|
|
1340
|
+
}
|
|
1341
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1342
|
+
`);
|
|
1343
|
+
killAndRespawnDaemon();
|
|
1344
|
+
const start = Date.now();
|
|
1345
|
+
let delay2 = 200;
|
|
1346
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1347
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1348
|
+
if (await connectToSocket()) break;
|
|
1349
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1350
|
+
}
|
|
1351
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1352
|
+
const final = await doRequest();
|
|
1353
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1354
|
+
return final;
|
|
706
1355
|
}
|
|
707
1356
|
async function embedViaClient(text, priority = "high") {
|
|
708
1357
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
709
1358
|
_requestCount++;
|
|
710
1359
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
711
1360
|
const health = await pingDaemon();
|
|
712
|
-
if (!health) {
|
|
1361
|
+
if (!health && !isDaemonTooYoung()) {
|
|
713
1362
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
714
1363
|
`);
|
|
715
1364
|
killAndRespawnDaemon();
|
|
716
1365
|
const start = Date.now();
|
|
717
|
-
let
|
|
1366
|
+
let d = 200;
|
|
718
1367
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
719
|
-
await new Promise((r) => setTimeout(r,
|
|
1368
|
+
await new Promise((r) => setTimeout(r, d));
|
|
720
1369
|
if (await connectToSocket()) break;
|
|
721
|
-
|
|
1370
|
+
d = Math.min(d * 2, 3e3);
|
|
722
1371
|
}
|
|
723
1372
|
if (!_connected) return null;
|
|
724
1373
|
}
|
|
725
1374
|
}
|
|
726
|
-
const result = await
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
killAndRespawnDaemon();
|
|
732
|
-
const start = Date.now();
|
|
733
|
-
let delay2 = 200;
|
|
734
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
735
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
736
|
-
if (await connectToSocket()) break;
|
|
737
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
738
|
-
}
|
|
739
|
-
if (!_connected) return null;
|
|
740
|
-
const retry = await sendRequest([text], priority);
|
|
741
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
742
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
743
|
-
`);
|
|
744
|
-
}
|
|
745
|
-
return null;
|
|
1375
|
+
const result = await retryThenRestart(
|
|
1376
|
+
() => sendRequest([text], priority),
|
|
1377
|
+
"Embed"
|
|
1378
|
+
);
|
|
1379
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
746
1380
|
}
|
|
747
1381
|
function disconnectClient() {
|
|
748
1382
|
if (_socket) {
|
|
@@ -760,14 +1394,14 @@ function disconnectClient() {
|
|
|
760
1394
|
function isClientConnected() {
|
|
761
1395
|
return _connected;
|
|
762
1396
|
}
|
|
763
|
-
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;
|
|
1397
|
+
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;
|
|
764
1398
|
var init_exe_daemon_client = __esm({
|
|
765
1399
|
"src/lib/exe-daemon-client.ts"() {
|
|
766
1400
|
"use strict";
|
|
767
1401
|
init_config();
|
|
768
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
769
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
770
|
-
SPAWN_LOCK_PATH =
|
|
1402
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
1403
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
1404
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
771
1405
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
772
1406
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
773
1407
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -775,7 +1409,11 @@ var init_exe_daemon_client = __esm({
|
|
|
775
1409
|
_connected = false;
|
|
776
1410
|
_buffer = "";
|
|
777
1411
|
_requestCount = 0;
|
|
1412
|
+
_consecutiveFailures = 0;
|
|
778
1413
|
HEALTH_CHECK_INTERVAL = 100;
|
|
1414
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1415
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1416
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
779
1417
|
_pending = /* @__PURE__ */ new Map();
|
|
780
1418
|
MAX_BUFFER = 1e7;
|
|
781
1419
|
}
|
|
@@ -851,7 +1489,7 @@ __export(db_daemon_client_exports, {
|
|
|
851
1489
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
852
1490
|
initDaemonDbClient: () => initDaemonDbClient
|
|
853
1491
|
});
|
|
854
|
-
function
|
|
1492
|
+
function normalizeStatement2(stmt) {
|
|
855
1493
|
if (typeof stmt === "string") {
|
|
856
1494
|
return { sql: stmt, args: [] };
|
|
857
1495
|
}
|
|
@@ -875,7 +1513,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
875
1513
|
if (!_useDaemon || !isClientConnected()) {
|
|
876
1514
|
return fallbackClient.execute(stmt);
|
|
877
1515
|
}
|
|
878
|
-
const { sql, args } =
|
|
1516
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
879
1517
|
const response = await sendDaemonRequest({
|
|
880
1518
|
type: "db-execute",
|
|
881
1519
|
sql,
|
|
@@ -900,7 +1538,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
900
1538
|
if (!_useDaemon || !isClientConnected()) {
|
|
901
1539
|
return fallbackClient.batch(stmts, mode);
|
|
902
1540
|
}
|
|
903
|
-
const statements = stmts.map(
|
|
1541
|
+
const statements = stmts.map(normalizeStatement2);
|
|
904
1542
|
const response = await sendDaemonRequest({
|
|
905
1543
|
type: "db-batch",
|
|
906
1544
|
statements,
|
|
@@ -995,6 +1633,18 @@ __export(database_exports, {
|
|
|
995
1633
|
});
|
|
996
1634
|
import { createClient } from "@libsql/client";
|
|
997
1635
|
async function initDatabase(config) {
|
|
1636
|
+
if (_walCheckpointTimer) {
|
|
1637
|
+
clearInterval(_walCheckpointTimer);
|
|
1638
|
+
_walCheckpointTimer = null;
|
|
1639
|
+
}
|
|
1640
|
+
if (_daemonClient) {
|
|
1641
|
+
_daemonClient.close();
|
|
1642
|
+
_daemonClient = null;
|
|
1643
|
+
}
|
|
1644
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1645
|
+
_adapterClient.close();
|
|
1646
|
+
}
|
|
1647
|
+
_adapterClient = null;
|
|
998
1648
|
if (_client) {
|
|
999
1649
|
_client.close();
|
|
1000
1650
|
_client = null;
|
|
@@ -1008,6 +1658,7 @@ async function initDatabase(config) {
|
|
|
1008
1658
|
}
|
|
1009
1659
|
_client = createClient(opts);
|
|
1010
1660
|
_resilientClient = wrapWithRetry(_client);
|
|
1661
|
+
_adapterClient = _resilientClient;
|
|
1011
1662
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1012
1663
|
});
|
|
1013
1664
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1018,14 +1669,20 @@ async function initDatabase(config) {
|
|
|
1018
1669
|
});
|
|
1019
1670
|
}, 3e4);
|
|
1020
1671
|
_walCheckpointTimer.unref();
|
|
1672
|
+
if (process.env.DATABASE_URL) {
|
|
1673
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1674
|
+
}
|
|
1021
1675
|
}
|
|
1022
1676
|
function isInitialized() {
|
|
1023
|
-
return _client !== null;
|
|
1677
|
+
return _adapterClient !== null || _client !== null;
|
|
1024
1678
|
}
|
|
1025
1679
|
function getClient() {
|
|
1026
|
-
if (!
|
|
1680
|
+
if (!_adapterClient) {
|
|
1027
1681
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1028
1682
|
}
|
|
1683
|
+
if (process.env.DATABASE_URL) {
|
|
1684
|
+
return _adapterClient;
|
|
1685
|
+
}
|
|
1029
1686
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1030
1687
|
return _resilientClient;
|
|
1031
1688
|
}
|
|
@@ -1035,6 +1692,7 @@ function getClient() {
|
|
|
1035
1692
|
return _resilientClient;
|
|
1036
1693
|
}
|
|
1037
1694
|
async function initDaemonClient() {
|
|
1695
|
+
if (process.env.DATABASE_URL) return;
|
|
1038
1696
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1039
1697
|
if (!_resilientClient) return;
|
|
1040
1698
|
try {
|
|
@@ -1979,26 +2637,36 @@ async function ensureSchema() {
|
|
|
1979
2637
|
}
|
|
1980
2638
|
}
|
|
1981
2639
|
async function disposeDatabase() {
|
|
2640
|
+
if (_walCheckpointTimer) {
|
|
2641
|
+
clearInterval(_walCheckpointTimer);
|
|
2642
|
+
_walCheckpointTimer = null;
|
|
2643
|
+
}
|
|
1982
2644
|
if (_daemonClient) {
|
|
1983
2645
|
_daemonClient.close();
|
|
1984
2646
|
_daemonClient = null;
|
|
1985
2647
|
}
|
|
2648
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2649
|
+
_adapterClient.close();
|
|
2650
|
+
}
|
|
2651
|
+
_adapterClient = null;
|
|
1986
2652
|
if (_client) {
|
|
1987
2653
|
_client.close();
|
|
1988
2654
|
_client = null;
|
|
1989
2655
|
_resilientClient = null;
|
|
1990
2656
|
}
|
|
1991
2657
|
}
|
|
1992
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2658
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1993
2659
|
var init_database = __esm({
|
|
1994
2660
|
"src/lib/database.ts"() {
|
|
1995
2661
|
"use strict";
|
|
1996
2662
|
init_db_retry();
|
|
1997
2663
|
init_employees();
|
|
2664
|
+
init_database_adapter();
|
|
1998
2665
|
_client = null;
|
|
1999
2666
|
_resilientClient = null;
|
|
2000
2667
|
_walCheckpointTimer = null;
|
|
2001
2668
|
_daemonClient = null;
|
|
2669
|
+
_adapterClient = null;
|
|
2002
2670
|
initTurso = initDatabase;
|
|
2003
2671
|
disposeTurso = disposeDatabase;
|
|
2004
2672
|
}
|
|
@@ -2015,13 +2683,13 @@ __export(keychain_exports, {
|
|
|
2015
2683
|
});
|
|
2016
2684
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2017
2685
|
import { existsSync as existsSync4 } from "fs";
|
|
2018
|
-
import
|
|
2019
|
-
import
|
|
2686
|
+
import path5 from "path";
|
|
2687
|
+
import os5 from "os";
|
|
2020
2688
|
function getKeyDir() {
|
|
2021
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2689
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os5.homedir(), ".exe-os");
|
|
2022
2690
|
}
|
|
2023
2691
|
function getKeyPath() {
|
|
2024
|
-
return
|
|
2692
|
+
return path5.join(getKeyDir(), "master.key");
|
|
2025
2693
|
}
|
|
2026
2694
|
async function tryKeytar() {
|
|
2027
2695
|
try {
|
|
@@ -2044,7 +2712,7 @@ async function getMasterKey() {
|
|
|
2044
2712
|
const keyPath = getKeyPath();
|
|
2045
2713
|
if (!existsSync4(keyPath)) {
|
|
2046
2714
|
process.stderr.write(
|
|
2047
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
2715
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2048
2716
|
`
|
|
2049
2717
|
);
|
|
2050
2718
|
return null;
|
|
@@ -2195,7 +2863,7 @@ __export(shard_manager_exports, {
|
|
|
2195
2863
|
listShards: () => listShards,
|
|
2196
2864
|
shardExists: () => shardExists
|
|
2197
2865
|
});
|
|
2198
|
-
import
|
|
2866
|
+
import path6 from "path";
|
|
2199
2867
|
import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
|
|
2200
2868
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2201
2869
|
function initShardManager(encryptionKey) {
|
|
@@ -2221,7 +2889,7 @@ function getShardClient(projectName) {
|
|
|
2221
2889
|
}
|
|
2222
2890
|
const cached = _shards.get(safeName);
|
|
2223
2891
|
if (cached) return cached;
|
|
2224
|
-
const dbPath =
|
|
2892
|
+
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
2225
2893
|
const client = createClient2({
|
|
2226
2894
|
url: `file:${dbPath}`,
|
|
2227
2895
|
encryptionKey: _encryptionKey
|
|
@@ -2231,7 +2899,7 @@ function getShardClient(projectName) {
|
|
|
2231
2899
|
}
|
|
2232
2900
|
function shardExists(projectName) {
|
|
2233
2901
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2234
|
-
return existsSync5(
|
|
2902
|
+
return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
2235
2903
|
}
|
|
2236
2904
|
function listShards() {
|
|
2237
2905
|
if (!existsSync5(SHARDS_DIR)) return [];
|
|
@@ -2308,7 +2976,23 @@ async function ensureShardSchema(client) {
|
|
|
2308
2976
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2309
2977
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2310
2978
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2311
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2979
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2980
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2981
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2982
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2983
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2984
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2985
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2986
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2987
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2988
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2989
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2990
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2991
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2992
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2993
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2994
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2995
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2312
2996
|
]) {
|
|
2313
2997
|
try {
|
|
2314
2998
|
await client.execute(col);
|
|
@@ -2420,7 +3104,7 @@ var init_shard_manager = __esm({
|
|
|
2420
3104
|
"src/lib/shard-manager.ts"() {
|
|
2421
3105
|
"use strict";
|
|
2422
3106
|
init_config();
|
|
2423
|
-
SHARDS_DIR =
|
|
3107
|
+
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
2424
3108
|
_shards = /* @__PURE__ */ new Map();
|
|
2425
3109
|
_encryptionKey = null;
|
|
2426
3110
|
_shardingEnabled = false;
|
|
@@ -2616,8 +3300,8 @@ ${p.content}`).join("\n\n");
|
|
|
2616
3300
|
|
|
2617
3301
|
// src/lib/notifications.ts
|
|
2618
3302
|
import crypto from "crypto";
|
|
2619
|
-
import
|
|
2620
|
-
import
|
|
3303
|
+
import path7 from "path";
|
|
3304
|
+
import os6 from "os";
|
|
2621
3305
|
import {
|
|
2622
3306
|
readFileSync as readFileSync4,
|
|
2623
3307
|
readdirSync as readdirSync2,
|
|
@@ -2657,13 +3341,13 @@ var init_notifications = __esm({
|
|
|
2657
3341
|
});
|
|
2658
3342
|
|
|
2659
3343
|
// src/lib/session-registry.ts
|
|
2660
|
-
import
|
|
2661
|
-
import
|
|
3344
|
+
import path8 from "path";
|
|
3345
|
+
import os7 from "os";
|
|
2662
3346
|
var REGISTRY_PATH;
|
|
2663
3347
|
var init_session_registry = __esm({
|
|
2664
3348
|
"src/lib/session-registry.ts"() {
|
|
2665
3349
|
"use strict";
|
|
2666
|
-
REGISTRY_PATH =
|
|
3350
|
+
REGISTRY_PATH = path8.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
2667
3351
|
}
|
|
2668
3352
|
});
|
|
2669
3353
|
|
|
@@ -2899,14 +3583,14 @@ var init_runtime_table = __esm({
|
|
|
2899
3583
|
|
|
2900
3584
|
// src/lib/agent-config.ts
|
|
2901
3585
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
2902
|
-
import
|
|
3586
|
+
import path9 from "path";
|
|
2903
3587
|
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
2904
3588
|
var init_agent_config = __esm({
|
|
2905
3589
|
"src/lib/agent-config.ts"() {
|
|
2906
3590
|
"use strict";
|
|
2907
3591
|
init_config();
|
|
2908
3592
|
init_runtime_table();
|
|
2909
|
-
AGENT_CONFIG_PATH =
|
|
3593
|
+
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
2910
3594
|
DEFAULT_MODELS = {
|
|
2911
3595
|
claude: "claude-opus-4",
|
|
2912
3596
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -2917,15 +3601,15 @@ var init_agent_config = __esm({
|
|
|
2917
3601
|
|
|
2918
3602
|
// src/lib/intercom-queue.ts
|
|
2919
3603
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
2920
|
-
import
|
|
2921
|
-
import
|
|
3604
|
+
import path10 from "path";
|
|
3605
|
+
import os8 from "os";
|
|
2922
3606
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
2923
3607
|
var init_intercom_queue = __esm({
|
|
2924
3608
|
"src/lib/intercom-queue.ts"() {
|
|
2925
3609
|
"use strict";
|
|
2926
|
-
QUEUE_PATH =
|
|
3610
|
+
QUEUE_PATH = path10.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
2927
3611
|
TTL_MS = 60 * 60 * 1e3;
|
|
2928
|
-
INTERCOM_LOG =
|
|
3612
|
+
INTERCOM_LOG = path10.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
2929
3613
|
}
|
|
2930
3614
|
});
|
|
2931
3615
|
|
|
@@ -2948,7 +3632,7 @@ __export(license_exports, {
|
|
|
2948
3632
|
});
|
|
2949
3633
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
2950
3634
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2951
|
-
import
|
|
3635
|
+
import path11 from "path";
|
|
2952
3636
|
import { jwtVerify, importSPKI } from "jose";
|
|
2953
3637
|
async function fetchRetry(url, init) {
|
|
2954
3638
|
try {
|
|
@@ -2959,7 +3643,7 @@ async function fetchRetry(url, init) {
|
|
|
2959
3643
|
}
|
|
2960
3644
|
}
|
|
2961
3645
|
function loadDeviceId() {
|
|
2962
|
-
const deviceJsonPath =
|
|
3646
|
+
const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
|
|
2963
3647
|
try {
|
|
2964
3648
|
if (existsSync9(deviceJsonPath)) {
|
|
2965
3649
|
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
@@ -3124,7 +3808,7 @@ async function checkLicense() {
|
|
|
3124
3808
|
let key = loadLicense();
|
|
3125
3809
|
if (!key) {
|
|
3126
3810
|
try {
|
|
3127
|
-
const configPath =
|
|
3811
|
+
const configPath = path11.join(EXE_AI_DIR, "config.json");
|
|
3128
3812
|
if (existsSync9(configPath)) {
|
|
3129
3813
|
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3130
3814
|
const cloud = raw.cloud;
|
|
@@ -3285,9 +3969,9 @@ var init_license = __esm({
|
|
|
3285
3969
|
"src/lib/license.ts"() {
|
|
3286
3970
|
"use strict";
|
|
3287
3971
|
init_config();
|
|
3288
|
-
LICENSE_PATH =
|
|
3289
|
-
CACHE_PATH =
|
|
3290
|
-
DEVICE_ID_PATH =
|
|
3972
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
3973
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
3974
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
3291
3975
|
API_BASE = "https://askexe.com/cloud";
|
|
3292
3976
|
RETRY_DELAY_MS = 500;
|
|
3293
3977
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -3328,7 +4012,7 @@ __export(plan_limits_exports, {
|
|
|
3328
4012
|
getLicenseSync: () => getLicenseSync
|
|
3329
4013
|
});
|
|
3330
4014
|
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3331
|
-
import
|
|
4015
|
+
import path12 from "path";
|
|
3332
4016
|
function getLicenseSync() {
|
|
3333
4017
|
try {
|
|
3334
4018
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -3437,14 +4121,14 @@ var init_plan_limits = __esm({
|
|
|
3437
4121
|
this.name = "PlanLimitError";
|
|
3438
4122
|
}
|
|
3439
4123
|
};
|
|
3440
|
-
CACHE_PATH2 =
|
|
4124
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
3441
4125
|
}
|
|
3442
4126
|
});
|
|
3443
4127
|
|
|
3444
4128
|
// src/lib/tmux-routing.ts
|
|
3445
4129
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
3446
|
-
import
|
|
3447
|
-
import
|
|
4130
|
+
import path13 from "path";
|
|
4131
|
+
import os9 from "os";
|
|
3448
4132
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3449
4133
|
function getMySession() {
|
|
3450
4134
|
return getTransport().getMySession();
|
|
@@ -3457,7 +4141,7 @@ function extractRootExe(name) {
|
|
|
3457
4141
|
}
|
|
3458
4142
|
function getParentExe(sessionKey) {
|
|
3459
4143
|
try {
|
|
3460
|
-
const data = JSON.parse(readFileSync9(
|
|
4144
|
+
const data = JSON.parse(readFileSync9(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3461
4145
|
return data.parentExe || null;
|
|
3462
4146
|
} catch {
|
|
3463
4147
|
return null;
|
|
@@ -3500,10 +4184,10 @@ var init_tmux_routing = __esm({
|
|
|
3500
4184
|
init_intercom_queue();
|
|
3501
4185
|
init_plan_limits();
|
|
3502
4186
|
init_employees();
|
|
3503
|
-
SPAWN_LOCK_DIR =
|
|
3504
|
-
SESSION_CACHE =
|
|
3505
|
-
INTERCOM_LOG2 =
|
|
3506
|
-
DEBOUNCE_FILE =
|
|
4187
|
+
SPAWN_LOCK_DIR = path13.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
4188
|
+
SESSION_CACHE = path13.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4189
|
+
INTERCOM_LOG2 = path13.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
4190
|
+
DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3507
4191
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
3508
4192
|
}
|
|
3509
4193
|
});
|
|
@@ -3570,8 +4254,8 @@ async function embedDirect(text) {
|
|
|
3570
4254
|
const llamaCpp = await import("node-llama-cpp");
|
|
3571
4255
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3572
4256
|
const { existsSync: existsSync16 } = await import("fs");
|
|
3573
|
-
const
|
|
3574
|
-
const modelPath =
|
|
4257
|
+
const path18 = await import("path");
|
|
4258
|
+
const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3575
4259
|
if (!existsSync16(modelPath)) {
|
|
3576
4260
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
3577
4261
|
}
|
|
@@ -3611,12 +4295,12 @@ __export(worker_gate_exports, {
|
|
|
3611
4295
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
3612
4296
|
});
|
|
3613
4297
|
import { readdirSync as readdirSync4, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6, existsSync as existsSync12 } from "fs";
|
|
3614
|
-
import
|
|
4298
|
+
import path14 from "path";
|
|
3615
4299
|
function tryAcquireWorkerSlot() {
|
|
3616
4300
|
try {
|
|
3617
4301
|
mkdirSync6(WORKER_PID_DIR, { recursive: true });
|
|
3618
4302
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
3619
|
-
const reservationPath =
|
|
4303
|
+
const reservationPath = path14.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
3620
4304
|
writeFileSync6(reservationPath, String(process.pid));
|
|
3621
4305
|
const files = readdirSync4(WORKER_PID_DIR);
|
|
3622
4306
|
let alive = 0;
|
|
@@ -3634,7 +4318,7 @@ function tryAcquireWorkerSlot() {
|
|
|
3634
4318
|
alive++;
|
|
3635
4319
|
} catch {
|
|
3636
4320
|
try {
|
|
3637
|
-
unlinkSync4(
|
|
4321
|
+
unlinkSync4(path14.join(WORKER_PID_DIR, f));
|
|
3638
4322
|
} catch {
|
|
3639
4323
|
}
|
|
3640
4324
|
}
|
|
@@ -3658,13 +4342,13 @@ function tryAcquireWorkerSlot() {
|
|
|
3658
4342
|
function registerWorkerPid(pid) {
|
|
3659
4343
|
try {
|
|
3660
4344
|
mkdirSync6(WORKER_PID_DIR, { recursive: true });
|
|
3661
|
-
writeFileSync6(
|
|
4345
|
+
writeFileSync6(path14.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
3662
4346
|
} catch {
|
|
3663
4347
|
}
|
|
3664
4348
|
}
|
|
3665
4349
|
function cleanupWorkerPid() {
|
|
3666
4350
|
try {
|
|
3667
|
-
unlinkSync4(
|
|
4351
|
+
unlinkSync4(path14.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
3668
4352
|
} catch {
|
|
3669
4353
|
}
|
|
3670
4354
|
}
|
|
@@ -3704,9 +4388,9 @@ var init_worker_gate = __esm({
|
|
|
3704
4388
|
"src/lib/worker-gate.ts"() {
|
|
3705
4389
|
"use strict";
|
|
3706
4390
|
init_config();
|
|
3707
|
-
WORKER_PID_DIR =
|
|
4391
|
+
WORKER_PID_DIR = path14.join(EXE_AI_DIR, "worker-pids");
|
|
3708
4392
|
MAX_CONCURRENT_WORKERS = 3;
|
|
3709
|
-
BACKFILL_LOCK =
|
|
4393
|
+
BACKFILL_LOCK = path14.join(WORKER_PID_DIR, "backfill.lock");
|
|
3710
4394
|
}
|
|
3711
4395
|
});
|
|
3712
4396
|
|
|
@@ -3809,7 +4493,7 @@ __export(crdt_sync_exports, {
|
|
|
3809
4493
|
});
|
|
3810
4494
|
import * as Y from "yjs";
|
|
3811
4495
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync13, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
3812
|
-
import
|
|
4496
|
+
import path15 from "path";
|
|
3813
4497
|
import { homedir } from "os";
|
|
3814
4498
|
function getStatePath() {
|
|
3815
4499
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -3965,7 +4649,7 @@ function persistState() {
|
|
|
3965
4649
|
if (!doc) return;
|
|
3966
4650
|
try {
|
|
3967
4651
|
const sp = getStatePath();
|
|
3968
|
-
const dir =
|
|
4652
|
+
const dir = path15.dirname(sp);
|
|
3969
4653
|
if (!existsSync13(dir)) mkdirSync7(dir, { recursive: true });
|
|
3970
4654
|
const state = Y.encodeStateAsUpdate(doc);
|
|
3971
4655
|
writeFileSync7(sp, Buffer.from(state));
|
|
@@ -4009,7 +4693,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
4009
4693
|
var init_crdt_sync = __esm({
|
|
4010
4694
|
"src/lib/crdt-sync.ts"() {
|
|
4011
4695
|
"use strict";
|
|
4012
|
-
DEFAULT_STATE_PATH =
|
|
4696
|
+
DEFAULT_STATE_PATH = path15.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
4013
4697
|
_statePathOverride = null;
|
|
4014
4698
|
doc = null;
|
|
4015
4699
|
}
|
|
@@ -4045,14 +4729,14 @@ __export(cloud_sync_exports, {
|
|
|
4045
4729
|
});
|
|
4046
4730
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync14, readdirSync as readdirSync5, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
4047
4731
|
import crypto3 from "crypto";
|
|
4048
|
-
import
|
|
4732
|
+
import path16 from "path";
|
|
4049
4733
|
import { homedir as homedir2 } from "os";
|
|
4050
4734
|
function sqlSafe(v) {
|
|
4051
4735
|
return v === void 0 ? null : v;
|
|
4052
4736
|
}
|
|
4053
4737
|
function logError(msg) {
|
|
4054
4738
|
try {
|
|
4055
|
-
const logPath =
|
|
4739
|
+
const logPath = path16.join(homedir2(), ".exe-os", "workers.log");
|
|
4056
4740
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
4057
4741
|
`);
|
|
4058
4742
|
} catch {
|
|
@@ -4447,7 +5131,7 @@ async function cloudSync(config) {
|
|
|
4447
5131
|
try {
|
|
4448
5132
|
const employees = await loadEmployees();
|
|
4449
5133
|
rosterResult.employees = employees.length;
|
|
4450
|
-
const idDir =
|
|
5134
|
+
const idDir = path16.join(EXE_AI_DIR, "identity");
|
|
4451
5135
|
if (existsSync14(idDir)) {
|
|
4452
5136
|
rosterResult.identities = readdirSync5(idDir).filter((f) => f.endsWith(".md")).length;
|
|
4453
5137
|
}
|
|
@@ -4488,9 +5172,9 @@ function consumeRosterDeletions() {
|
|
|
4488
5172
|
}
|
|
4489
5173
|
}
|
|
4490
5174
|
function buildRosterBlob(paths) {
|
|
4491
|
-
const rosterPath = paths?.rosterPath ??
|
|
4492
|
-
const identityDir = paths?.identityDir ??
|
|
4493
|
-
const configPath = paths?.configPath ??
|
|
5175
|
+
const rosterPath = paths?.rosterPath ?? path16.join(EXE_AI_DIR, "exe-employees.json");
|
|
5176
|
+
const identityDir = paths?.identityDir ?? path16.join(EXE_AI_DIR, "identity");
|
|
5177
|
+
const configPath = paths?.configPath ?? path16.join(EXE_AI_DIR, "config.json");
|
|
4494
5178
|
let roster = [];
|
|
4495
5179
|
if (existsSync14(rosterPath)) {
|
|
4496
5180
|
try {
|
|
@@ -4502,7 +5186,7 @@ function buildRosterBlob(paths) {
|
|
|
4502
5186
|
if (existsSync14(identityDir)) {
|
|
4503
5187
|
for (const file of readdirSync5(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
4504
5188
|
try {
|
|
4505
|
-
identities[file] = readFileSync11(
|
|
5189
|
+
identities[file] = readFileSync11(path16.join(identityDir, file), "utf-8");
|
|
4506
5190
|
} catch {
|
|
4507
5191
|
}
|
|
4508
5192
|
}
|
|
@@ -4515,7 +5199,7 @@ function buildRosterBlob(paths) {
|
|
|
4515
5199
|
}
|
|
4516
5200
|
}
|
|
4517
5201
|
let agentConfig;
|
|
4518
|
-
const agentConfigPath =
|
|
5202
|
+
const agentConfigPath = path16.join(EXE_AI_DIR, "agent-config.json");
|
|
4519
5203
|
if (existsSync14(agentConfigPath)) {
|
|
4520
5204
|
try {
|
|
4521
5205
|
agentConfig = JSON.parse(readFileSync11(agentConfigPath, "utf-8"));
|
|
@@ -4594,7 +5278,7 @@ async function cloudPullRoster(config) {
|
|
|
4594
5278
|
}
|
|
4595
5279
|
}
|
|
4596
5280
|
function mergeConfig(remoteConfig, configPath) {
|
|
4597
|
-
const cfgPath = configPath ??
|
|
5281
|
+
const cfgPath = configPath ?? path16.join(EXE_AI_DIR, "config.json");
|
|
4598
5282
|
let local = {};
|
|
4599
5283
|
if (existsSync14(cfgPath)) {
|
|
4600
5284
|
try {
|
|
@@ -4603,14 +5287,14 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
4603
5287
|
}
|
|
4604
5288
|
}
|
|
4605
5289
|
const merged = { ...remoteConfig, ...local };
|
|
4606
|
-
const dir =
|
|
5290
|
+
const dir = path16.dirname(cfgPath);
|
|
4607
5291
|
if (!existsSync14(dir)) mkdirSync8(dir, { recursive: true });
|
|
4608
5292
|
writeFileSync8(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
4609
5293
|
}
|
|
4610
5294
|
async function mergeRosterFromRemote(remote, paths) {
|
|
4611
5295
|
return withRosterLock(async () => {
|
|
4612
5296
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
4613
|
-
const identityDir = paths?.identityDir ??
|
|
5297
|
+
const identityDir = paths?.identityDir ?? path16.join(EXE_AI_DIR, "identity");
|
|
4614
5298
|
const localEmployees = await loadEmployees(rosterPath);
|
|
4615
5299
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
4616
5300
|
let added = 0;
|
|
@@ -4632,7 +5316,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
4632
5316
|
const remoteIdentity = remote.identities[matchedKey];
|
|
4633
5317
|
if (remoteIdentity) {
|
|
4634
5318
|
if (!existsSync14(identityDir)) mkdirSync8(identityDir, { recursive: true });
|
|
4635
|
-
const idPath =
|
|
5319
|
+
const idPath = path16.join(identityDir, `${remoteEmp.name}.md`);
|
|
4636
5320
|
let localIdentity = null;
|
|
4637
5321
|
try {
|
|
4638
5322
|
localIdentity = existsSync14(idPath) ? readFileSync11(idPath, "utf-8") : null;
|
|
@@ -4665,7 +5349,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
4665
5349
|
}
|
|
4666
5350
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
4667
5351
|
try {
|
|
4668
|
-
const agentConfigPath =
|
|
5352
|
+
const agentConfigPath = path16.join(EXE_AI_DIR, "agent-config.json");
|
|
4669
5353
|
let local = {};
|
|
4670
5354
|
if (existsSync14(agentConfigPath)) {
|
|
4671
5355
|
try {
|
|
@@ -5112,9 +5796,9 @@ var init_cloud_sync = __esm({
|
|
|
5112
5796
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
5113
5797
|
FETCH_TIMEOUT_MS = 3e4;
|
|
5114
5798
|
PUSH_BATCH_SIZE = 5e3;
|
|
5115
|
-
ROSTER_LOCK_PATH =
|
|
5799
|
+
ROSTER_LOCK_PATH = path16.join(EXE_AI_DIR, "roster-merge.lock");
|
|
5116
5800
|
LOCK_STALE_MS = 3e4;
|
|
5117
|
-
ROSTER_DELETIONS_PATH =
|
|
5801
|
+
ROSTER_DELETIONS_PATH = path16.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5118
5802
|
}
|
|
5119
5803
|
});
|
|
5120
5804
|
|
|
@@ -5484,7 +6168,7 @@ init_employees();
|
|
|
5484
6168
|
import crypto4 from "crypto";
|
|
5485
6169
|
import { execSync as execSync4 } from "child_process";
|
|
5486
6170
|
import { existsSync as existsSync15, mkdirSync as mkdirSync9, openSync as openSync3, closeSync as closeSync3 } from "fs";
|
|
5487
|
-
import
|
|
6171
|
+
import path17 from "path";
|
|
5488
6172
|
async function main() {
|
|
5489
6173
|
const agentId = process.env.AGENT_ID ?? "default";
|
|
5490
6174
|
const agentRole = process.env.AGENT_ROLE ?? "employee";
|
|
@@ -5619,7 +6303,7 @@ async function main() {
|
|
|
5619
6303
|
}
|
|
5620
6304
|
try {
|
|
5621
6305
|
const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5622
|
-
const flagPath =
|
|
6306
|
+
const flagPath = path17.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
|
|
5623
6307
|
if (existsSync15(flagPath)) {
|
|
5624
6308
|
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
5625
6309
|
if (!tryAcquireWorkerSlot2()) {
|
|
@@ -5628,11 +6312,11 @@ async function main() {
|
|
|
5628
6312
|
const { spawn: spawn2 } = await import("child_process");
|
|
5629
6313
|
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
5630
6314
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
5631
|
-
const backfillPath =
|
|
6315
|
+
const backfillPath = path17.resolve(path17.dirname(thisFile), "backfill-vectors.js");
|
|
5632
6316
|
if (existsSync15(backfillPath)) {
|
|
5633
6317
|
const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5634
|
-
const bLogPath =
|
|
5635
|
-
mkdirSync9(
|
|
6318
|
+
const bLogPath = path17.join(exeDir2, "workers.log");
|
|
6319
|
+
mkdirSync9(path17.dirname(bLogPath), { recursive: true });
|
|
5636
6320
|
const bLogFd = openSync3(bLogPath, "a");
|
|
5637
6321
|
const child = spawn2(process.execPath, [backfillPath], {
|
|
5638
6322
|
detached: true,
|