@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
|
@@ -454,7 +454,7 @@ function isMultiInstance(agentName, employees) {
|
|
|
454
454
|
if (!emp) return false;
|
|
455
455
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
456
456
|
}
|
|
457
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
457
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
458
458
|
var init_employees = __esm({
|
|
459
459
|
"src/lib/employees.ts"() {
|
|
460
460
|
"use strict";
|
|
@@ -463,16 +463,601 @@ var init_employees = __esm({
|
|
|
463
463
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
464
464
|
COORDINATOR_ROLE = "COO";
|
|
465
465
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
466
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// src/lib/database-adapter.ts
|
|
471
|
+
import os3 from "os";
|
|
472
|
+
import path4 from "path";
|
|
473
|
+
import { createRequire } from "module";
|
|
474
|
+
import { pathToFileURL } from "url";
|
|
475
|
+
function quotedIdentifier(identifier) {
|
|
476
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
477
|
+
}
|
|
478
|
+
function unqualifiedTableName(name) {
|
|
479
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
480
|
+
const parts = raw.split(".");
|
|
481
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
482
|
+
}
|
|
483
|
+
function stripTrailingSemicolon(sql) {
|
|
484
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
485
|
+
}
|
|
486
|
+
function appendClause(sql, clause) {
|
|
487
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
488
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
489
|
+
if (!returningMatch) {
|
|
490
|
+
return `${trimmed}${clause}`;
|
|
491
|
+
}
|
|
492
|
+
const idx = returningMatch.index;
|
|
493
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
494
|
+
}
|
|
495
|
+
function normalizeStatement(stmt) {
|
|
496
|
+
if (typeof stmt === "string") {
|
|
497
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
498
|
+
}
|
|
499
|
+
const sql = stmt.sql;
|
|
500
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
501
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
502
|
+
}
|
|
503
|
+
return { kind: "named", sql, args: stmt.args };
|
|
504
|
+
}
|
|
505
|
+
function rewriteBooleanLiterals(sql) {
|
|
506
|
+
let out = sql;
|
|
507
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
508
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
509
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
510
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
511
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
512
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
513
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
514
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
515
|
+
}
|
|
516
|
+
return out;
|
|
517
|
+
}
|
|
518
|
+
function rewriteInsertOrIgnore(sql) {
|
|
519
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
520
|
+
return sql;
|
|
521
|
+
}
|
|
522
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
523
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
524
|
+
}
|
|
525
|
+
function rewriteInsertOrReplace(sql) {
|
|
526
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
527
|
+
if (!match) {
|
|
528
|
+
return sql;
|
|
529
|
+
}
|
|
530
|
+
const rawTable = match[1];
|
|
531
|
+
const rawColumns = match[2];
|
|
532
|
+
const remainder = match[3];
|
|
533
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
534
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
535
|
+
if (!conflictKeys?.length) {
|
|
536
|
+
return sql;
|
|
537
|
+
}
|
|
538
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
539
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
540
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
541
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
542
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
543
|
+
}
|
|
544
|
+
function rewriteSql(sql) {
|
|
545
|
+
let out = sql;
|
|
546
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
547
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
548
|
+
out = rewriteBooleanLiterals(out);
|
|
549
|
+
out = rewriteInsertOrReplace(out);
|
|
550
|
+
out = rewriteInsertOrIgnore(out);
|
|
551
|
+
return stripTrailingSemicolon(out);
|
|
552
|
+
}
|
|
553
|
+
function toBoolean(value) {
|
|
554
|
+
if (value === null || value === void 0) return value;
|
|
555
|
+
if (typeof value === "boolean") return value;
|
|
556
|
+
if (typeof value === "number") return value !== 0;
|
|
557
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
558
|
+
if (typeof value === "string") {
|
|
559
|
+
const normalized = value.trim().toLowerCase();
|
|
560
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
561
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
562
|
+
}
|
|
563
|
+
return Boolean(value);
|
|
564
|
+
}
|
|
565
|
+
function countQuestionMarks(sql, end) {
|
|
566
|
+
let count = 0;
|
|
567
|
+
let inSingle = false;
|
|
568
|
+
let inDouble = false;
|
|
569
|
+
let inLineComment = false;
|
|
570
|
+
let inBlockComment = false;
|
|
571
|
+
for (let i = 0; i < end; i++) {
|
|
572
|
+
const ch = sql[i];
|
|
573
|
+
const next = sql[i + 1];
|
|
574
|
+
if (inLineComment) {
|
|
575
|
+
if (ch === "\n") inLineComment = false;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (inBlockComment) {
|
|
579
|
+
if (ch === "*" && next === "/") {
|
|
580
|
+
inBlockComment = false;
|
|
581
|
+
i += 1;
|
|
582
|
+
}
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
586
|
+
inLineComment = true;
|
|
587
|
+
i += 1;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
591
|
+
inBlockComment = true;
|
|
592
|
+
i += 1;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
596
|
+
inSingle = !inSingle;
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
600
|
+
inDouble = !inDouble;
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
604
|
+
count += 1;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return count;
|
|
608
|
+
}
|
|
609
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
610
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
611
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
612
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
613
|
+
for (const match of sql.matchAll(pattern)) {
|
|
614
|
+
const matchText = match[0];
|
|
615
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
616
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return indexes;
|
|
620
|
+
}
|
|
621
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
622
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
623
|
+
if (!match) return;
|
|
624
|
+
const rawTable = match[1];
|
|
625
|
+
const rawColumns = match[2];
|
|
626
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
627
|
+
if (!boolColumns?.size) return;
|
|
628
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
629
|
+
for (const [index, column] of columns.entries()) {
|
|
630
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
631
|
+
args[index] = toBoolean(args[index]);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
636
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
637
|
+
if (!match) return;
|
|
638
|
+
const rawTable = match[1];
|
|
639
|
+
const setClause = match[2];
|
|
640
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
641
|
+
if (!boolColumns?.size) return;
|
|
642
|
+
const assignments = setClause.split(",");
|
|
643
|
+
let placeholderIndex = 0;
|
|
644
|
+
for (const assignment of assignments) {
|
|
645
|
+
if (!assignment.includes("?")) continue;
|
|
646
|
+
placeholderIndex += 1;
|
|
647
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
648
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
649
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function coerceBooleanArgs(sql, args) {
|
|
654
|
+
const nextArgs = [...args];
|
|
655
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
656
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
657
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
658
|
+
for (const index of placeholderIndexes) {
|
|
659
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
660
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return nextArgs;
|
|
664
|
+
}
|
|
665
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
666
|
+
let out = "";
|
|
667
|
+
let placeholder = 0;
|
|
668
|
+
let inSingle = false;
|
|
669
|
+
let inDouble = false;
|
|
670
|
+
let inLineComment = false;
|
|
671
|
+
let inBlockComment = false;
|
|
672
|
+
for (let i = 0; i < sql.length; i++) {
|
|
673
|
+
const ch = sql[i];
|
|
674
|
+
const next = sql[i + 1];
|
|
675
|
+
if (inLineComment) {
|
|
676
|
+
out += ch;
|
|
677
|
+
if (ch === "\n") inLineComment = false;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (inBlockComment) {
|
|
681
|
+
out += ch;
|
|
682
|
+
if (ch === "*" && next === "/") {
|
|
683
|
+
out += next;
|
|
684
|
+
inBlockComment = false;
|
|
685
|
+
i += 1;
|
|
686
|
+
}
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
690
|
+
out += ch + next;
|
|
691
|
+
inLineComment = true;
|
|
692
|
+
i += 1;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
696
|
+
out += ch + next;
|
|
697
|
+
inBlockComment = true;
|
|
698
|
+
i += 1;
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
702
|
+
inSingle = !inSingle;
|
|
703
|
+
out += ch;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
707
|
+
inDouble = !inDouble;
|
|
708
|
+
out += ch;
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
712
|
+
placeholder += 1;
|
|
713
|
+
out += `$${placeholder}`;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
out += ch;
|
|
717
|
+
}
|
|
718
|
+
return out;
|
|
719
|
+
}
|
|
720
|
+
function translateStatementForPostgres(stmt) {
|
|
721
|
+
const normalized = normalizeStatement(stmt);
|
|
722
|
+
if (normalized.kind === "named") {
|
|
723
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
724
|
+
}
|
|
725
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
726
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
727
|
+
return {
|
|
728
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
729
|
+
args: coercedArgs
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function shouldBypassPostgres(stmt) {
|
|
733
|
+
const normalized = normalizeStatement(stmt);
|
|
734
|
+
if (normalized.kind === "named") {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
738
|
+
}
|
|
739
|
+
function shouldFallbackOnError(error) {
|
|
740
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
741
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
742
|
+
}
|
|
743
|
+
function isReadQuery(sql) {
|
|
744
|
+
const trimmed = sql.trimStart();
|
|
745
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
746
|
+
}
|
|
747
|
+
function buildRow(row, columns) {
|
|
748
|
+
const values = columns.map((column) => row[column]);
|
|
749
|
+
return Object.assign(values, row);
|
|
750
|
+
}
|
|
751
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
752
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
753
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
754
|
+
return {
|
|
755
|
+
columns,
|
|
756
|
+
columnTypes: columns.map(() => ""),
|
|
757
|
+
rows: resultRows,
|
|
758
|
+
rowsAffected,
|
|
759
|
+
lastInsertRowid: void 0,
|
|
760
|
+
toJSON() {
|
|
761
|
+
return {
|
|
762
|
+
columns,
|
|
763
|
+
columnTypes: columns.map(() => ""),
|
|
764
|
+
rows,
|
|
765
|
+
rowsAffected,
|
|
766
|
+
lastInsertRowid: void 0
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
async function loadPrismaClient() {
|
|
772
|
+
if (!prismaClientPromise) {
|
|
773
|
+
prismaClientPromise = (async () => {
|
|
774
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
775
|
+
if (explicitPath) {
|
|
776
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
777
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
778
|
+
if (!PrismaClient2) {
|
|
779
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
780
|
+
}
|
|
781
|
+
return new PrismaClient2();
|
|
782
|
+
}
|
|
783
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
784
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
785
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
786
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
787
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
788
|
+
if (!PrismaClient) {
|
|
789
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
790
|
+
}
|
|
791
|
+
return new PrismaClient();
|
|
792
|
+
})();
|
|
793
|
+
}
|
|
794
|
+
return prismaClientPromise;
|
|
795
|
+
}
|
|
796
|
+
async function ensureCompatibilityViews(prisma) {
|
|
797
|
+
if (!compatibilityBootstrapPromise) {
|
|
798
|
+
compatibilityBootstrapPromise = (async () => {
|
|
799
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
800
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
801
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
802
|
+
"SELECT to_regclass($1) AS regclass",
|
|
803
|
+
relation
|
|
804
|
+
);
|
|
805
|
+
if (!rows[0]?.regclass) {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
await prisma.$executeRawUnsafe(
|
|
809
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
})();
|
|
813
|
+
}
|
|
814
|
+
return compatibilityBootstrapPromise;
|
|
815
|
+
}
|
|
816
|
+
async function executeOnPrisma(executor, stmt) {
|
|
817
|
+
const translated = translateStatementForPostgres(stmt);
|
|
818
|
+
if (isReadQuery(translated.sql)) {
|
|
819
|
+
const rows = await executor.$queryRawUnsafe(
|
|
820
|
+
translated.sql,
|
|
821
|
+
...translated.args
|
|
822
|
+
);
|
|
823
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
824
|
+
}
|
|
825
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
826
|
+
return buildResultSet([], rowsAffected);
|
|
827
|
+
}
|
|
828
|
+
function splitSqlStatements(sql) {
|
|
829
|
+
const parts = [];
|
|
830
|
+
let current = "";
|
|
831
|
+
let inSingle = false;
|
|
832
|
+
let inDouble = false;
|
|
833
|
+
let inLineComment = false;
|
|
834
|
+
let inBlockComment = false;
|
|
835
|
+
for (let i = 0; i < sql.length; i++) {
|
|
836
|
+
const ch = sql[i];
|
|
837
|
+
const next = sql[i + 1];
|
|
838
|
+
if (inLineComment) {
|
|
839
|
+
current += ch;
|
|
840
|
+
if (ch === "\n") inLineComment = false;
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
if (inBlockComment) {
|
|
844
|
+
current += ch;
|
|
845
|
+
if (ch === "*" && next === "/") {
|
|
846
|
+
current += next;
|
|
847
|
+
inBlockComment = false;
|
|
848
|
+
i += 1;
|
|
849
|
+
}
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
853
|
+
current += ch + next;
|
|
854
|
+
inLineComment = true;
|
|
855
|
+
i += 1;
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
859
|
+
current += ch + next;
|
|
860
|
+
inBlockComment = true;
|
|
861
|
+
i += 1;
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
865
|
+
inSingle = !inSingle;
|
|
866
|
+
current += ch;
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
870
|
+
inDouble = !inDouble;
|
|
871
|
+
current += ch;
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
875
|
+
if (current.trim()) {
|
|
876
|
+
parts.push(current.trim());
|
|
877
|
+
}
|
|
878
|
+
current = "";
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
current += ch;
|
|
882
|
+
}
|
|
883
|
+
if (current.trim()) {
|
|
884
|
+
parts.push(current.trim());
|
|
885
|
+
}
|
|
886
|
+
return parts;
|
|
887
|
+
}
|
|
888
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
889
|
+
const prisma = await loadPrismaClient();
|
|
890
|
+
await ensureCompatibilityViews(prisma);
|
|
891
|
+
let closed = false;
|
|
892
|
+
let adapter;
|
|
893
|
+
const fallbackExecute = async (stmt, error) => {
|
|
894
|
+
if (!fallbackClient) {
|
|
895
|
+
if (error) throw error;
|
|
896
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
897
|
+
}
|
|
898
|
+
if (error) {
|
|
899
|
+
process.stderr.write(
|
|
900
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
901
|
+
`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
return fallbackClient.execute(stmt);
|
|
905
|
+
};
|
|
906
|
+
adapter = {
|
|
907
|
+
async execute(stmt) {
|
|
908
|
+
if (shouldBypassPostgres(stmt)) {
|
|
909
|
+
return fallbackExecute(stmt);
|
|
910
|
+
}
|
|
911
|
+
try {
|
|
912
|
+
return await executeOnPrisma(prisma, stmt);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
if (shouldFallbackOnError(error)) {
|
|
915
|
+
return fallbackExecute(stmt, error);
|
|
916
|
+
}
|
|
917
|
+
throw error;
|
|
918
|
+
}
|
|
919
|
+
},
|
|
920
|
+
async batch(stmts, mode) {
|
|
921
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
922
|
+
if (!fallbackClient) {
|
|
923
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
924
|
+
}
|
|
925
|
+
return fallbackClient.batch(stmts, mode);
|
|
926
|
+
}
|
|
927
|
+
try {
|
|
928
|
+
if (prisma.$transaction) {
|
|
929
|
+
return await prisma.$transaction(async (tx) => {
|
|
930
|
+
const results2 = [];
|
|
931
|
+
for (const stmt of stmts) {
|
|
932
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
933
|
+
}
|
|
934
|
+
return results2;
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
const results = [];
|
|
938
|
+
for (const stmt of stmts) {
|
|
939
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
940
|
+
}
|
|
941
|
+
return results;
|
|
942
|
+
} catch (error) {
|
|
943
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
944
|
+
process.stderr.write(
|
|
945
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
946
|
+
`
|
|
947
|
+
);
|
|
948
|
+
return fallbackClient.batch(stmts, mode);
|
|
949
|
+
}
|
|
950
|
+
throw error;
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
async migrate(stmts) {
|
|
954
|
+
if (fallbackClient) {
|
|
955
|
+
return fallbackClient.migrate(stmts);
|
|
956
|
+
}
|
|
957
|
+
return adapter.batch(stmts, "deferred");
|
|
958
|
+
},
|
|
959
|
+
async transaction(mode) {
|
|
960
|
+
if (!fallbackClient) {
|
|
961
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
962
|
+
}
|
|
963
|
+
return fallbackClient.transaction(mode);
|
|
964
|
+
},
|
|
965
|
+
async executeMultiple(sql) {
|
|
966
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
967
|
+
return fallbackClient.executeMultiple(sql);
|
|
968
|
+
}
|
|
969
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
970
|
+
await adapter.execute(statement);
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
async sync() {
|
|
974
|
+
if (fallbackClient) {
|
|
975
|
+
return fallbackClient.sync();
|
|
976
|
+
}
|
|
977
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
978
|
+
},
|
|
979
|
+
close() {
|
|
980
|
+
closed = true;
|
|
981
|
+
prismaClientPromise = null;
|
|
982
|
+
compatibilityBootstrapPromise = null;
|
|
983
|
+
void prisma.$disconnect?.();
|
|
984
|
+
},
|
|
985
|
+
get closed() {
|
|
986
|
+
return closed;
|
|
987
|
+
},
|
|
988
|
+
get protocol() {
|
|
989
|
+
return "prisma-postgres";
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
return adapter;
|
|
993
|
+
}
|
|
994
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
995
|
+
var init_database_adapter = __esm({
|
|
996
|
+
"src/lib/database-adapter.ts"() {
|
|
997
|
+
"use strict";
|
|
998
|
+
VIEW_MAPPINGS = [
|
|
999
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1000
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1001
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1002
|
+
{ view: "entities", source: "memory.entities" },
|
|
1003
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1004
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1005
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1006
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1007
|
+
{ view: "messages", source: "memory.messages" },
|
|
1008
|
+
{ view: "users", source: "wiki.users" },
|
|
1009
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1010
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1011
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1012
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1013
|
+
];
|
|
1014
|
+
UPSERT_KEYS = {
|
|
1015
|
+
memories: ["id"],
|
|
1016
|
+
tasks: ["id"],
|
|
1017
|
+
behaviors: ["id"],
|
|
1018
|
+
entities: ["id"],
|
|
1019
|
+
relationships: ["id"],
|
|
1020
|
+
entity_aliases: ["alias"],
|
|
1021
|
+
notifications: ["id"],
|
|
1022
|
+
messages: ["id"],
|
|
1023
|
+
users: ["id"],
|
|
1024
|
+
workspaces: ["id"],
|
|
1025
|
+
workspace_users: ["id"],
|
|
1026
|
+
documents: ["id"],
|
|
1027
|
+
chats: ["id"]
|
|
1028
|
+
};
|
|
1029
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1030
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1031
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1032
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1033
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1034
|
+
};
|
|
1035
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1036
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1037
|
+
);
|
|
1038
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1039
|
+
/\bPRAGMA\b/i,
|
|
1040
|
+
/\bsqlite_master\b/i,
|
|
1041
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1042
|
+
/\bMATCH\b/i,
|
|
1043
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1044
|
+
/\bjson_extract\s*\(/i,
|
|
1045
|
+
/\bjulianday\s*\(/i,
|
|
1046
|
+
/\bstrftime\s*\(/i,
|
|
1047
|
+
/\blast_insert_rowid\s*\(/i
|
|
1048
|
+
];
|
|
1049
|
+
prismaClientPromise = null;
|
|
1050
|
+
compatibilityBootstrapPromise = null;
|
|
466
1051
|
}
|
|
467
1052
|
});
|
|
468
1053
|
|
|
469
1054
|
// src/lib/exe-daemon-client.ts
|
|
470
1055
|
import net from "net";
|
|
471
|
-
import
|
|
1056
|
+
import os4 from "os";
|
|
472
1057
|
import { spawn } from "child_process";
|
|
473
1058
|
import { randomUUID } from "crypto";
|
|
474
1059
|
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
475
|
-
import
|
|
1060
|
+
import path5 from "path";
|
|
476
1061
|
import { fileURLToPath } from "url";
|
|
477
1062
|
function handleData(chunk) {
|
|
478
1063
|
_buffer += chunk.toString();
|
|
@@ -523,17 +1108,17 @@ function cleanupStaleFiles() {
|
|
|
523
1108
|
}
|
|
524
1109
|
}
|
|
525
1110
|
function findPackageRoot() {
|
|
526
|
-
let dir =
|
|
527
|
-
const { root } =
|
|
1111
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1112
|
+
const { root } = path5.parse(dir);
|
|
528
1113
|
while (dir !== root) {
|
|
529
|
-
if (existsSync3(
|
|
530
|
-
dir =
|
|
1114
|
+
if (existsSync3(path5.join(dir, "package.json"))) return dir;
|
|
1115
|
+
dir = path5.dirname(dir);
|
|
531
1116
|
}
|
|
532
1117
|
return null;
|
|
533
1118
|
}
|
|
534
1119
|
function spawnDaemon() {
|
|
535
|
-
const freeGB =
|
|
536
|
-
const totalGB =
|
|
1120
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
1121
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
537
1122
|
if (totalGB <= 8) {
|
|
538
1123
|
process.stderr.write(
|
|
539
1124
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -553,7 +1138,7 @@ function spawnDaemon() {
|
|
|
553
1138
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
554
1139
|
return;
|
|
555
1140
|
}
|
|
556
|
-
const daemonPath =
|
|
1141
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
557
1142
|
if (!existsSync3(daemonPath)) {
|
|
558
1143
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
559
1144
|
`);
|
|
@@ -562,7 +1147,7 @@ function spawnDaemon() {
|
|
|
562
1147
|
const resolvedPath = daemonPath;
|
|
563
1148
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
564
1149
|
`);
|
|
565
|
-
const logPath =
|
|
1150
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
566
1151
|
let stderrFd = "ignore";
|
|
567
1152
|
try {
|
|
568
1153
|
stderrFd = openSync(logPath, "a");
|
|
@@ -713,74 +1298,123 @@ async function pingDaemon() {
|
|
|
713
1298
|
return null;
|
|
714
1299
|
}
|
|
715
1300
|
function killAndRespawnDaemon() {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1301
|
+
if (!acquireSpawnLock()) {
|
|
1302
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1303
|
+
if (_socket) {
|
|
1304
|
+
_socket.destroy();
|
|
1305
|
+
_socket = null;
|
|
1306
|
+
}
|
|
1307
|
+
_connected = false;
|
|
1308
|
+
_buffer = "";
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
try {
|
|
1312
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1313
|
+
if (existsSync3(PID_PATH)) {
|
|
1314
|
+
try {
|
|
1315
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
1316
|
+
if (pid > 0) {
|
|
1317
|
+
try {
|
|
1318
|
+
process.kill(pid, "SIGKILL");
|
|
1319
|
+
} catch {
|
|
1320
|
+
}
|
|
724
1321
|
}
|
|
1322
|
+
} catch {
|
|
725
1323
|
}
|
|
1324
|
+
}
|
|
1325
|
+
if (_socket) {
|
|
1326
|
+
_socket.destroy();
|
|
1327
|
+
_socket = null;
|
|
1328
|
+
}
|
|
1329
|
+
_connected = false;
|
|
1330
|
+
_buffer = "";
|
|
1331
|
+
try {
|
|
1332
|
+
unlinkSync2(PID_PATH);
|
|
726
1333
|
} catch {
|
|
727
1334
|
}
|
|
1335
|
+
try {
|
|
1336
|
+
unlinkSync2(SOCKET_PATH);
|
|
1337
|
+
} catch {
|
|
1338
|
+
}
|
|
1339
|
+
spawnDaemon();
|
|
1340
|
+
} finally {
|
|
1341
|
+
releaseSpawnLock();
|
|
728
1342
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
_socket = null;
|
|
732
|
-
}
|
|
733
|
-
_connected = false;
|
|
734
|
-
_buffer = "";
|
|
1343
|
+
}
|
|
1344
|
+
function isDaemonTooYoung() {
|
|
735
1345
|
try {
|
|
736
|
-
|
|
1346
|
+
const stat = statSync(PID_PATH);
|
|
1347
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
737
1348
|
} catch {
|
|
1349
|
+
return false;
|
|
738
1350
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1351
|
+
}
|
|
1352
|
+
async function retryThenRestart(doRequest, label) {
|
|
1353
|
+
const result = await doRequest();
|
|
1354
|
+
if (!result.error) {
|
|
1355
|
+
_consecutiveFailures = 0;
|
|
1356
|
+
return result;
|
|
742
1357
|
}
|
|
743
|
-
|
|
1358
|
+
_consecutiveFailures++;
|
|
1359
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1360
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1361
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1362
|
+
`);
|
|
1363
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1364
|
+
if (!_connected) {
|
|
1365
|
+
if (!await connectToSocket()) continue;
|
|
1366
|
+
}
|
|
1367
|
+
const retry = await doRequest();
|
|
1368
|
+
if (!retry.error) {
|
|
1369
|
+
_consecutiveFailures = 0;
|
|
1370
|
+
return retry;
|
|
1371
|
+
}
|
|
1372
|
+
_consecutiveFailures++;
|
|
1373
|
+
}
|
|
1374
|
+
if (isDaemonTooYoung()) {
|
|
1375
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1376
|
+
`);
|
|
1377
|
+
return { error: result.error };
|
|
1378
|
+
}
|
|
1379
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1380
|
+
`);
|
|
1381
|
+
killAndRespawnDaemon();
|
|
1382
|
+
const start = Date.now();
|
|
1383
|
+
let delay2 = 200;
|
|
1384
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1385
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1386
|
+
if (await connectToSocket()) break;
|
|
1387
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1388
|
+
}
|
|
1389
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1390
|
+
const final = await doRequest();
|
|
1391
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1392
|
+
return final;
|
|
744
1393
|
}
|
|
745
1394
|
async function embedViaClient(text, priority = "high") {
|
|
746
1395
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
747
1396
|
_requestCount++;
|
|
748
1397
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
749
1398
|
const health = await pingDaemon();
|
|
750
|
-
if (!health) {
|
|
1399
|
+
if (!health && !isDaemonTooYoung()) {
|
|
751
1400
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
752
1401
|
`);
|
|
753
1402
|
killAndRespawnDaemon();
|
|
754
1403
|
const start = Date.now();
|
|
755
|
-
let
|
|
1404
|
+
let d = 200;
|
|
756
1405
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
757
|
-
await new Promise((r) => setTimeout(r,
|
|
1406
|
+
await new Promise((r) => setTimeout(r, d));
|
|
758
1407
|
if (await connectToSocket()) break;
|
|
759
|
-
|
|
1408
|
+
d = Math.min(d * 2, 3e3);
|
|
760
1409
|
}
|
|
761
1410
|
if (!_connected) return null;
|
|
762
1411
|
}
|
|
763
1412
|
}
|
|
764
|
-
const result = await
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
killAndRespawnDaemon();
|
|
770
|
-
const start = Date.now();
|
|
771
|
-
let delay2 = 200;
|
|
772
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
773
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
774
|
-
if (await connectToSocket()) break;
|
|
775
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
776
|
-
}
|
|
777
|
-
if (!_connected) return null;
|
|
778
|
-
const retry = await sendRequest([text], priority);
|
|
779
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
780
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
781
|
-
`);
|
|
782
|
-
}
|
|
783
|
-
return null;
|
|
1413
|
+
const result = await retryThenRestart(
|
|
1414
|
+
() => sendRequest([text], priority),
|
|
1415
|
+
"Embed"
|
|
1416
|
+
);
|
|
1417
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
784
1418
|
}
|
|
785
1419
|
function disconnectClient() {
|
|
786
1420
|
if (_socket) {
|
|
@@ -798,14 +1432,14 @@ function disconnectClient() {
|
|
|
798
1432
|
function isClientConnected() {
|
|
799
1433
|
return _connected;
|
|
800
1434
|
}
|
|
801
|
-
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;
|
|
1435
|
+
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;
|
|
802
1436
|
var init_exe_daemon_client = __esm({
|
|
803
1437
|
"src/lib/exe-daemon-client.ts"() {
|
|
804
1438
|
"use strict";
|
|
805
1439
|
init_config();
|
|
806
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
807
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
808
|
-
SPAWN_LOCK_PATH =
|
|
1440
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1441
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1442
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
809
1443
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
810
1444
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
811
1445
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -813,7 +1447,11 @@ var init_exe_daemon_client = __esm({
|
|
|
813
1447
|
_connected = false;
|
|
814
1448
|
_buffer = "";
|
|
815
1449
|
_requestCount = 0;
|
|
1450
|
+
_consecutiveFailures = 0;
|
|
816
1451
|
HEALTH_CHECK_INTERVAL = 100;
|
|
1452
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1453
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1454
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
817
1455
|
_pending = /* @__PURE__ */ new Map();
|
|
818
1456
|
MAX_BUFFER = 1e7;
|
|
819
1457
|
}
|
|
@@ -889,7 +1527,7 @@ __export(db_daemon_client_exports, {
|
|
|
889
1527
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
890
1528
|
initDaemonDbClient: () => initDaemonDbClient
|
|
891
1529
|
});
|
|
892
|
-
function
|
|
1530
|
+
function normalizeStatement2(stmt) {
|
|
893
1531
|
if (typeof stmt === "string") {
|
|
894
1532
|
return { sql: stmt, args: [] };
|
|
895
1533
|
}
|
|
@@ -913,7 +1551,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
913
1551
|
if (!_useDaemon || !isClientConnected()) {
|
|
914
1552
|
return fallbackClient.execute(stmt);
|
|
915
1553
|
}
|
|
916
|
-
const { sql, args } =
|
|
1554
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
917
1555
|
const response = await sendDaemonRequest({
|
|
918
1556
|
type: "db-execute",
|
|
919
1557
|
sql,
|
|
@@ -938,7 +1576,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
938
1576
|
if (!_useDaemon || !isClientConnected()) {
|
|
939
1577
|
return fallbackClient.batch(stmts, mode);
|
|
940
1578
|
}
|
|
941
|
-
const statements = stmts.map(
|
|
1579
|
+
const statements = stmts.map(normalizeStatement2);
|
|
942
1580
|
const response = await sendDaemonRequest({
|
|
943
1581
|
type: "db-batch",
|
|
944
1582
|
statements,
|
|
@@ -1033,6 +1671,18 @@ __export(database_exports, {
|
|
|
1033
1671
|
});
|
|
1034
1672
|
import { createClient } from "@libsql/client";
|
|
1035
1673
|
async function initDatabase(config) {
|
|
1674
|
+
if (_walCheckpointTimer) {
|
|
1675
|
+
clearInterval(_walCheckpointTimer);
|
|
1676
|
+
_walCheckpointTimer = null;
|
|
1677
|
+
}
|
|
1678
|
+
if (_daemonClient) {
|
|
1679
|
+
_daemonClient.close();
|
|
1680
|
+
_daemonClient = null;
|
|
1681
|
+
}
|
|
1682
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1683
|
+
_adapterClient.close();
|
|
1684
|
+
}
|
|
1685
|
+
_adapterClient = null;
|
|
1036
1686
|
if (_client) {
|
|
1037
1687
|
_client.close();
|
|
1038
1688
|
_client = null;
|
|
@@ -1046,6 +1696,7 @@ async function initDatabase(config) {
|
|
|
1046
1696
|
}
|
|
1047
1697
|
_client = createClient(opts);
|
|
1048
1698
|
_resilientClient = wrapWithRetry(_client);
|
|
1699
|
+
_adapterClient = _resilientClient;
|
|
1049
1700
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1050
1701
|
});
|
|
1051
1702
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1056,14 +1707,20 @@ async function initDatabase(config) {
|
|
|
1056
1707
|
});
|
|
1057
1708
|
}, 3e4);
|
|
1058
1709
|
_walCheckpointTimer.unref();
|
|
1710
|
+
if (process.env.DATABASE_URL) {
|
|
1711
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1712
|
+
}
|
|
1059
1713
|
}
|
|
1060
1714
|
function isInitialized() {
|
|
1061
|
-
return _client !== null;
|
|
1715
|
+
return _adapterClient !== null || _client !== null;
|
|
1062
1716
|
}
|
|
1063
1717
|
function getClient() {
|
|
1064
|
-
if (!
|
|
1718
|
+
if (!_adapterClient) {
|
|
1065
1719
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1066
1720
|
}
|
|
1721
|
+
if (process.env.DATABASE_URL) {
|
|
1722
|
+
return _adapterClient;
|
|
1723
|
+
}
|
|
1067
1724
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1068
1725
|
return _resilientClient;
|
|
1069
1726
|
}
|
|
@@ -1073,6 +1730,7 @@ function getClient() {
|
|
|
1073
1730
|
return _resilientClient;
|
|
1074
1731
|
}
|
|
1075
1732
|
async function initDaemonClient() {
|
|
1733
|
+
if (process.env.DATABASE_URL) return;
|
|
1076
1734
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1077
1735
|
if (!_resilientClient) return;
|
|
1078
1736
|
try {
|
|
@@ -2017,26 +2675,36 @@ async function ensureSchema() {
|
|
|
2017
2675
|
}
|
|
2018
2676
|
}
|
|
2019
2677
|
async function disposeDatabase() {
|
|
2678
|
+
if (_walCheckpointTimer) {
|
|
2679
|
+
clearInterval(_walCheckpointTimer);
|
|
2680
|
+
_walCheckpointTimer = null;
|
|
2681
|
+
}
|
|
2020
2682
|
if (_daemonClient) {
|
|
2021
2683
|
_daemonClient.close();
|
|
2022
2684
|
_daemonClient = null;
|
|
2023
2685
|
}
|
|
2686
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2687
|
+
_adapterClient.close();
|
|
2688
|
+
}
|
|
2689
|
+
_adapterClient = null;
|
|
2024
2690
|
if (_client) {
|
|
2025
2691
|
_client.close();
|
|
2026
2692
|
_client = null;
|
|
2027
2693
|
_resilientClient = null;
|
|
2028
2694
|
}
|
|
2029
2695
|
}
|
|
2030
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2696
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2031
2697
|
var init_database = __esm({
|
|
2032
2698
|
"src/lib/database.ts"() {
|
|
2033
2699
|
"use strict";
|
|
2034
2700
|
init_db_retry();
|
|
2035
2701
|
init_employees();
|
|
2702
|
+
init_database_adapter();
|
|
2036
2703
|
_client = null;
|
|
2037
2704
|
_resilientClient = null;
|
|
2038
2705
|
_walCheckpointTimer = null;
|
|
2039
2706
|
_daemonClient = null;
|
|
2707
|
+
_adapterClient = null;
|
|
2040
2708
|
initTurso = initDatabase;
|
|
2041
2709
|
disposeTurso = disposeDatabase;
|
|
2042
2710
|
}
|
|
@@ -2110,7 +2778,7 @@ __export(shard_manager_exports, {
|
|
|
2110
2778
|
listShards: () => listShards,
|
|
2111
2779
|
shardExists: () => shardExists
|
|
2112
2780
|
});
|
|
2113
|
-
import
|
|
2781
|
+
import path7 from "path";
|
|
2114
2782
|
import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
|
|
2115
2783
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2116
2784
|
function initShardManager(encryptionKey) {
|
|
@@ -2136,7 +2804,7 @@ function getShardClient(projectName) {
|
|
|
2136
2804
|
}
|
|
2137
2805
|
const cached = _shards.get(safeName);
|
|
2138
2806
|
if (cached) return cached;
|
|
2139
|
-
const dbPath =
|
|
2807
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2140
2808
|
const client = createClient2({
|
|
2141
2809
|
url: `file:${dbPath}`,
|
|
2142
2810
|
encryptionKey: _encryptionKey
|
|
@@ -2146,7 +2814,7 @@ function getShardClient(projectName) {
|
|
|
2146
2814
|
}
|
|
2147
2815
|
function shardExists(projectName) {
|
|
2148
2816
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2149
|
-
return existsSync5(
|
|
2817
|
+
return existsSync5(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2150
2818
|
}
|
|
2151
2819
|
function listShards() {
|
|
2152
2820
|
if (!existsSync5(SHARDS_DIR)) return [];
|
|
@@ -2223,7 +2891,23 @@ async function ensureShardSchema(client) {
|
|
|
2223
2891
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2224
2892
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2225
2893
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2226
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2894
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2895
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2896
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2897
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2898
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2899
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2900
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2901
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2902
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2903
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2904
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2905
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2906
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2907
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2908
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2909
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2910
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2227
2911
|
]) {
|
|
2228
2912
|
try {
|
|
2229
2913
|
await client.execute(col);
|
|
@@ -2335,7 +3019,7 @@ var init_shard_manager = __esm({
|
|
|
2335
3019
|
"src/lib/shard-manager.ts"() {
|
|
2336
3020
|
"use strict";
|
|
2337
3021
|
init_config();
|
|
2338
|
-
SHARDS_DIR =
|
|
3022
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
2339
3023
|
_shards = /* @__PURE__ */ new Map();
|
|
2340
3024
|
_encryptionKey = null;
|
|
2341
3025
|
_shardingEnabled = false;
|
|
@@ -2531,8 +3215,8 @@ ${p.content}`).join("\n\n");
|
|
|
2531
3215
|
|
|
2532
3216
|
// src/lib/notifications.ts
|
|
2533
3217
|
import crypto2 from "crypto";
|
|
2534
|
-
import
|
|
2535
|
-
import
|
|
3218
|
+
import path8 from "path";
|
|
3219
|
+
import os6 from "os";
|
|
2536
3220
|
import {
|
|
2537
3221
|
readFileSync as readFileSync4,
|
|
2538
3222
|
readdirSync as readdirSync2,
|
|
@@ -2584,7 +3268,7 @@ var init_notifications = __esm({
|
|
|
2584
3268
|
// src/lib/license.ts
|
|
2585
3269
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
2586
3270
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2587
|
-
import
|
|
3271
|
+
import path9 from "path";
|
|
2588
3272
|
import { jwtVerify, importSPKI } from "jose";
|
|
2589
3273
|
async function fetchRetry(url, init) {
|
|
2590
3274
|
try {
|
|
@@ -2595,7 +3279,7 @@ async function fetchRetry(url, init) {
|
|
|
2595
3279
|
}
|
|
2596
3280
|
}
|
|
2597
3281
|
function loadDeviceId() {
|
|
2598
|
-
const deviceJsonPath =
|
|
3282
|
+
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
2599
3283
|
try {
|
|
2600
3284
|
if (existsSync7(deviceJsonPath)) {
|
|
2601
3285
|
const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
|
|
@@ -2760,7 +3444,7 @@ async function checkLicense() {
|
|
|
2760
3444
|
let key = loadLicense();
|
|
2761
3445
|
if (!key) {
|
|
2762
3446
|
try {
|
|
2763
|
-
const configPath =
|
|
3447
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
2764
3448
|
if (existsSync7(configPath)) {
|
|
2765
3449
|
const raw = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
2766
3450
|
const cloud = raw.cloud;
|
|
@@ -2783,9 +3467,9 @@ var init_license = __esm({
|
|
|
2783
3467
|
"src/lib/license.ts"() {
|
|
2784
3468
|
"use strict";
|
|
2785
3469
|
init_config();
|
|
2786
|
-
LICENSE_PATH =
|
|
2787
|
-
CACHE_PATH =
|
|
2788
|
-
DEVICE_ID_PATH =
|
|
3470
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3471
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3472
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2789
3473
|
API_BASE = "https://askexe.com/cloud";
|
|
2790
3474
|
RETRY_DELAY_MS = 500;
|
|
2791
3475
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -2815,7 +3499,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2815
3499
|
|
|
2816
3500
|
// src/lib/plan-limits.ts
|
|
2817
3501
|
import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
|
|
2818
|
-
import
|
|
3502
|
+
import path10 from "path";
|
|
2819
3503
|
function getLicenseSync() {
|
|
2820
3504
|
try {
|
|
2821
3505
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
@@ -2906,7 +3590,7 @@ var init_plan_limits = __esm({
|
|
|
2906
3590
|
this.name = "PlanLimitError";
|
|
2907
3591
|
}
|
|
2908
3592
|
};
|
|
2909
|
-
CACHE_PATH2 =
|
|
3593
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2910
3594
|
}
|
|
2911
3595
|
});
|
|
2912
3596
|
|
|
@@ -2948,8 +3632,8 @@ async function embedDirect(text) {
|
|
|
2948
3632
|
const llamaCpp = await import("node-llama-cpp");
|
|
2949
3633
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2950
3634
|
const { existsSync: existsSync16 } = await import("fs");
|
|
2951
|
-
const
|
|
2952
|
-
const modelPath =
|
|
3635
|
+
const path21 = await import("path");
|
|
3636
|
+
const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2953
3637
|
if (!existsSync16(modelPath)) {
|
|
2954
3638
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2955
3639
|
}
|
|
@@ -2980,10 +3664,10 @@ var init_embedder = __esm({
|
|
|
2980
3664
|
|
|
2981
3665
|
// src/lib/session-registry.ts
|
|
2982
3666
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
|
|
2983
|
-
import
|
|
2984
|
-
import
|
|
3667
|
+
import path11 from "path";
|
|
3668
|
+
import os7 from "os";
|
|
2985
3669
|
function registerSession(entry) {
|
|
2986
|
-
const dir =
|
|
3670
|
+
const dir = path11.dirname(REGISTRY_PATH);
|
|
2987
3671
|
if (!existsSync9(dir)) {
|
|
2988
3672
|
mkdirSync3(dir, { recursive: true });
|
|
2989
3673
|
}
|
|
@@ -3008,7 +3692,7 @@ var REGISTRY_PATH;
|
|
|
3008
3692
|
var init_session_registry = __esm({
|
|
3009
3693
|
"src/lib/session-registry.ts"() {
|
|
3010
3694
|
"use strict";
|
|
3011
|
-
REGISTRY_PATH =
|
|
3695
|
+
REGISTRY_PATH = path11.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
3012
3696
|
}
|
|
3013
3697
|
});
|
|
3014
3698
|
|
|
@@ -3266,7 +3950,7 @@ var init_runtime_table = __esm({
|
|
|
3266
3950
|
|
|
3267
3951
|
// src/lib/agent-config.ts
|
|
3268
3952
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
3269
|
-
import
|
|
3953
|
+
import path12 from "path";
|
|
3270
3954
|
function loadAgentConfig() {
|
|
3271
3955
|
if (!existsSync10(AGENT_CONFIG_PATH)) return {};
|
|
3272
3956
|
try {
|
|
@@ -3289,7 +3973,7 @@ var init_agent_config = __esm({
|
|
|
3289
3973
|
"use strict";
|
|
3290
3974
|
init_config();
|
|
3291
3975
|
init_runtime_table();
|
|
3292
|
-
AGENT_CONFIG_PATH =
|
|
3976
|
+
AGENT_CONFIG_PATH = path12.join(EXE_AI_DIR, "agent-config.json");
|
|
3293
3977
|
DEFAULT_MODELS = {
|
|
3294
3978
|
claude: "claude-opus-4",
|
|
3295
3979
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -3308,10 +3992,10 @@ __export(intercom_queue_exports, {
|
|
|
3308
3992
|
readQueue: () => readQueue
|
|
3309
3993
|
});
|
|
3310
3994
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
|
|
3311
|
-
import
|
|
3312
|
-
import
|
|
3995
|
+
import path13 from "path";
|
|
3996
|
+
import os8 from "os";
|
|
3313
3997
|
function ensureDir() {
|
|
3314
|
-
const dir =
|
|
3998
|
+
const dir = path13.dirname(QUEUE_PATH);
|
|
3315
3999
|
if (!existsSync11(dir)) mkdirSync5(dir, { recursive: true });
|
|
3316
4000
|
}
|
|
3317
4001
|
function readQueue() {
|
|
@@ -3417,10 +4101,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3417
4101
|
var init_intercom_queue = __esm({
|
|
3418
4102
|
"src/lib/intercom-queue.ts"() {
|
|
3419
4103
|
"use strict";
|
|
3420
|
-
QUEUE_PATH =
|
|
4104
|
+
QUEUE_PATH = path13.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
3421
4105
|
MAX_RETRIES2 = 5;
|
|
3422
4106
|
TTL_MS = 60 * 60 * 1e3;
|
|
3423
|
-
INTERCOM_LOG =
|
|
4107
|
+
INTERCOM_LOG = path13.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
3424
4108
|
}
|
|
3425
4109
|
});
|
|
3426
4110
|
|
|
@@ -3769,12 +4453,12 @@ __export(tmux_routing_exports, {
|
|
|
3769
4453
|
});
|
|
3770
4454
|
import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
|
|
3771
4455
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
3772
|
-
import
|
|
3773
|
-
import
|
|
4456
|
+
import path14 from "path";
|
|
4457
|
+
import os9 from "os";
|
|
3774
4458
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3775
4459
|
import { unlinkSync as unlinkSync4 } from "fs";
|
|
3776
4460
|
function spawnLockPath(sessionName) {
|
|
3777
|
-
return
|
|
4461
|
+
return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3778
4462
|
}
|
|
3779
4463
|
function isProcessAlive(pid) {
|
|
3780
4464
|
try {
|
|
@@ -3811,8 +4495,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
3811
4495
|
function resolveBehaviorsExporterScript() {
|
|
3812
4496
|
try {
|
|
3813
4497
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
3814
|
-
const scriptPath =
|
|
3815
|
-
|
|
4498
|
+
const scriptPath = path14.join(
|
|
4499
|
+
path14.dirname(thisFile),
|
|
3816
4500
|
"..",
|
|
3817
4501
|
"bin",
|
|
3818
4502
|
"exe-export-behaviors.js"
|
|
@@ -3887,7 +4571,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3887
4571
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3888
4572
|
}
|
|
3889
4573
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3890
|
-
const filePath =
|
|
4574
|
+
const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3891
4575
|
writeFileSync6(filePath, JSON.stringify({
|
|
3892
4576
|
parentExe: rootExe,
|
|
3893
4577
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -3896,7 +4580,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3896
4580
|
}
|
|
3897
4581
|
function getParentExe(sessionKey) {
|
|
3898
4582
|
try {
|
|
3899
|
-
const data = JSON.parse(readFileSync10(
|
|
4583
|
+
const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3900
4584
|
return data.parentExe || null;
|
|
3901
4585
|
} catch {
|
|
3902
4586
|
return null;
|
|
@@ -3905,7 +4589,7 @@ function getParentExe(sessionKey) {
|
|
|
3905
4589
|
function getDispatchedBy(sessionKey) {
|
|
3906
4590
|
try {
|
|
3907
4591
|
const data = JSON.parse(readFileSync10(
|
|
3908
|
-
|
|
4592
|
+
path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
3909
4593
|
"utf8"
|
|
3910
4594
|
));
|
|
3911
4595
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4091,7 +4775,7 @@ function sendIntercom(targetSession) {
|
|
|
4091
4775
|
try {
|
|
4092
4776
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4093
4777
|
const agent = baseAgentName(rawAgent);
|
|
4094
|
-
const markerPath =
|
|
4778
|
+
const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4095
4779
|
if (existsSync12(markerPath)) {
|
|
4096
4780
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4097
4781
|
return "debounced";
|
|
@@ -4101,7 +4785,7 @@ function sendIntercom(targetSession) {
|
|
|
4101
4785
|
try {
|
|
4102
4786
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4103
4787
|
const agent = baseAgentName(rawAgent);
|
|
4104
|
-
const taskDir =
|
|
4788
|
+
const taskDir = path14.join(process.cwd(), "exe", agent);
|
|
4105
4789
|
if (existsSync12(taskDir)) {
|
|
4106
4790
|
const files = readdirSync3(taskDir).filter(
|
|
4107
4791
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4235,8 +4919,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4235
4919
|
const transport = getTransport();
|
|
4236
4920
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4237
4921
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4238
|
-
const logDir =
|
|
4239
|
-
const logFile =
|
|
4922
|
+
const logDir = path14.join(os9.homedir(), ".exe-os", "session-logs");
|
|
4923
|
+
const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4240
4924
|
if (!existsSync12(logDir)) {
|
|
4241
4925
|
mkdirSync6(logDir, { recursive: true });
|
|
4242
4926
|
}
|
|
@@ -4244,14 +4928,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4244
4928
|
let cleanupSuffix = "";
|
|
4245
4929
|
try {
|
|
4246
4930
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4247
|
-
const cleanupScript =
|
|
4931
|
+
const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4248
4932
|
if (existsSync12(cleanupScript)) {
|
|
4249
4933
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4250
4934
|
}
|
|
4251
4935
|
} catch {
|
|
4252
4936
|
}
|
|
4253
4937
|
try {
|
|
4254
|
-
const claudeJsonPath =
|
|
4938
|
+
const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
|
|
4255
4939
|
let claudeJson = {};
|
|
4256
4940
|
try {
|
|
4257
4941
|
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
@@ -4266,10 +4950,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4266
4950
|
} catch {
|
|
4267
4951
|
}
|
|
4268
4952
|
try {
|
|
4269
|
-
const settingsDir =
|
|
4953
|
+
const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
|
|
4270
4954
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4271
|
-
const projSettingsDir =
|
|
4272
|
-
const settingsPath =
|
|
4955
|
+
const projSettingsDir = path14.join(settingsDir, normalizedKey);
|
|
4956
|
+
const settingsPath = path14.join(projSettingsDir, "settings.json");
|
|
4273
4957
|
let settings = {};
|
|
4274
4958
|
try {
|
|
4275
4959
|
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
@@ -4316,8 +5000,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4316
5000
|
let behaviorsFlag = "";
|
|
4317
5001
|
let legacyFallbackWarned = false;
|
|
4318
5002
|
if (!useExeAgent && !useBinSymlink) {
|
|
4319
|
-
const identityPath =
|
|
4320
|
-
|
|
5003
|
+
const identityPath = path14.join(
|
|
5004
|
+
os9.homedir(),
|
|
4321
5005
|
".exe-os",
|
|
4322
5006
|
"identity",
|
|
4323
5007
|
`${employeeName}.md`
|
|
@@ -4332,7 +5016,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4332
5016
|
}
|
|
4333
5017
|
const behaviorsFile = exportBehaviorsSync(
|
|
4334
5018
|
employeeName,
|
|
4335
|
-
|
|
5019
|
+
path14.basename(spawnCwd),
|
|
4336
5020
|
sessionName
|
|
4337
5021
|
);
|
|
4338
5022
|
if (behaviorsFile) {
|
|
@@ -4347,9 +5031,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4347
5031
|
}
|
|
4348
5032
|
let sessionContextFlag = "";
|
|
4349
5033
|
try {
|
|
4350
|
-
const ctxDir =
|
|
5034
|
+
const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4351
5035
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4352
|
-
const ctxFile =
|
|
5036
|
+
const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4353
5037
|
const ctxContent = [
|
|
4354
5038
|
`## Session Context`,
|
|
4355
5039
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -4433,7 +5117,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4433
5117
|
transport.pipeLog(sessionName, logFile);
|
|
4434
5118
|
try {
|
|
4435
5119
|
const mySession = getMySession();
|
|
4436
|
-
const dispatchInfo =
|
|
5120
|
+
const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4437
5121
|
writeFileSync6(dispatchInfo, JSON.stringify({
|
|
4438
5122
|
dispatchedBy: mySession,
|
|
4439
5123
|
rootExe: exeSession,
|
|
@@ -4508,15 +5192,15 @@ var init_tmux_routing = __esm({
|
|
|
4508
5192
|
init_intercom_queue();
|
|
4509
5193
|
init_plan_limits();
|
|
4510
5194
|
init_employees();
|
|
4511
|
-
SPAWN_LOCK_DIR =
|
|
4512
|
-
SESSION_CACHE =
|
|
5195
|
+
SPAWN_LOCK_DIR = path14.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5196
|
+
SESSION_CACHE = path14.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4513
5197
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4514
5198
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4515
5199
|
VERIFY_PANE_LINES = 200;
|
|
4516
5200
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4517
5201
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
4518
|
-
INTERCOM_LOG2 =
|
|
4519
|
-
DEBOUNCE_FILE =
|
|
5202
|
+
INTERCOM_LOG2 = path14.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5203
|
+
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4520
5204
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4521
5205
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
4522
5206
|
}
|
|
@@ -4548,8 +5232,8 @@ var init_task_scope = __esm({
|
|
|
4548
5232
|
|
|
4549
5233
|
// src/lib/tasks-crud.ts
|
|
4550
5234
|
import crypto4 from "crypto";
|
|
4551
|
-
import
|
|
4552
|
-
import
|
|
5235
|
+
import path15 from "path";
|
|
5236
|
+
import os10 from "os";
|
|
4553
5237
|
import { execSync as execSync6 } from "child_process";
|
|
4554
5238
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4555
5239
|
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
@@ -4727,8 +5411,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4727
5411
|
}
|
|
4728
5412
|
if (input2.baseDir) {
|
|
4729
5413
|
try {
|
|
4730
|
-
await mkdir4(
|
|
4731
|
-
await mkdir4(
|
|
5414
|
+
await mkdir4(path15.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
5415
|
+
await mkdir4(path15.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
4732
5416
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
4733
5417
|
await ensureGitignoreExe(input2.baseDir);
|
|
4734
5418
|
} catch {
|
|
@@ -4764,9 +5448,9 @@ ${laneWarning}` : laneWarning;
|
|
|
4764
5448
|
});
|
|
4765
5449
|
if (input2.baseDir) {
|
|
4766
5450
|
try {
|
|
4767
|
-
const EXE_OS_DIR =
|
|
4768
|
-
const mdPath =
|
|
4769
|
-
const mdDir =
|
|
5451
|
+
const EXE_OS_DIR = path15.join(os10.homedir(), ".exe-os");
|
|
5452
|
+
const mdPath = path15.join(EXE_OS_DIR, taskFile);
|
|
5453
|
+
const mdDir = path15.dirname(mdPath);
|
|
4770
5454
|
if (!existsSync13(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4771
5455
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
4772
5456
|
const mdContent = `# ${input2.title}
|
|
@@ -5067,7 +5751,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5067
5751
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5068
5752
|
}
|
|
5069
5753
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5070
|
-
const archPath =
|
|
5754
|
+
const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5071
5755
|
try {
|
|
5072
5756
|
if (existsSync13(archPath)) return;
|
|
5073
5757
|
const template = [
|
|
@@ -5102,7 +5786,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5102
5786
|
}
|
|
5103
5787
|
}
|
|
5104
5788
|
async function ensureGitignoreExe(baseDir) {
|
|
5105
|
-
const gitignorePath =
|
|
5789
|
+
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
5106
5790
|
try {
|
|
5107
5791
|
if (existsSync13(gitignorePath)) {
|
|
5108
5792
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -5136,13 +5820,13 @@ var init_tasks_crud = __esm({
|
|
|
5136
5820
|
});
|
|
5137
5821
|
|
|
5138
5822
|
// src/lib/tasks-review.ts
|
|
5139
|
-
import
|
|
5823
|
+
import path16 from "path";
|
|
5140
5824
|
import { existsSync as existsSync14, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
5141
5825
|
async function countPendingReviews(sessionScope) {
|
|
5142
5826
|
const client = getClient();
|
|
5143
5827
|
if (sessionScope) {
|
|
5144
5828
|
const result2 = await client.execute({
|
|
5145
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
5829
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
5146
5830
|
args: [sessionScope]
|
|
5147
5831
|
});
|
|
5148
5832
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -5318,11 +6002,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5318
6002
|
);
|
|
5319
6003
|
}
|
|
5320
6004
|
try {
|
|
5321
|
-
const cacheDir =
|
|
6005
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5322
6006
|
if (existsSync14(cacheDir)) {
|
|
5323
6007
|
for (const f of readdirSync4(cacheDir)) {
|
|
5324
6008
|
if (f.startsWith("review-notified-")) {
|
|
5325
|
-
unlinkSync5(
|
|
6009
|
+
unlinkSync5(path16.join(cacheDir, f));
|
|
5326
6010
|
}
|
|
5327
6011
|
}
|
|
5328
6012
|
}
|
|
@@ -5343,7 +6027,7 @@ var init_tasks_review = __esm({
|
|
|
5343
6027
|
});
|
|
5344
6028
|
|
|
5345
6029
|
// src/lib/tasks-chain.ts
|
|
5346
|
-
import
|
|
6030
|
+
import path17 from "path";
|
|
5347
6031
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5348
6032
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5349
6033
|
const client = getClient();
|
|
@@ -5360,7 +6044,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5360
6044
|
});
|
|
5361
6045
|
for (const ur of unblockedRows.rows) {
|
|
5362
6046
|
try {
|
|
5363
|
-
const ubFile =
|
|
6047
|
+
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
5364
6048
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5365
6049
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5366
6050
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5886,7 +6570,7 @@ __export(tasks_exports, {
|
|
|
5886
6570
|
updateTaskStatus: () => updateTaskStatus,
|
|
5887
6571
|
writeCheckpoint: () => writeCheckpoint
|
|
5888
6572
|
});
|
|
5889
|
-
import
|
|
6573
|
+
import path18 from "path";
|
|
5890
6574
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
5891
6575
|
async function createTask(input2) {
|
|
5892
6576
|
const result = await createTaskCore(input2);
|
|
@@ -5906,8 +6590,8 @@ async function updateTask(input2) {
|
|
|
5906
6590
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
5907
6591
|
try {
|
|
5908
6592
|
const agent = String(row.assigned_to);
|
|
5909
|
-
const cacheDir =
|
|
5910
|
-
const cachePath =
|
|
6593
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
6594
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
5911
6595
|
if (input2.status === "in_progress") {
|
|
5912
6596
|
mkdirSync7(cacheDir, { recursive: true });
|
|
5913
6597
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -6078,12 +6762,12 @@ __export(worker_gate_exports, {
|
|
|
6078
6762
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
6079
6763
|
});
|
|
6080
6764
|
import { readdirSync as readdirSync5, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
|
|
6081
|
-
import
|
|
6765
|
+
import path19 from "path";
|
|
6082
6766
|
function tryAcquireWorkerSlot() {
|
|
6083
6767
|
try {
|
|
6084
6768
|
mkdirSync8(WORKER_PID_DIR, { recursive: true });
|
|
6085
6769
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
6086
|
-
const reservationPath =
|
|
6770
|
+
const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
6087
6771
|
writeFileSync8(reservationPath, String(process.pid));
|
|
6088
6772
|
const files = readdirSync5(WORKER_PID_DIR);
|
|
6089
6773
|
let alive = 0;
|
|
@@ -6101,7 +6785,7 @@ function tryAcquireWorkerSlot() {
|
|
|
6101
6785
|
alive++;
|
|
6102
6786
|
} catch {
|
|
6103
6787
|
try {
|
|
6104
|
-
unlinkSync7(
|
|
6788
|
+
unlinkSync7(path19.join(WORKER_PID_DIR, f));
|
|
6105
6789
|
} catch {
|
|
6106
6790
|
}
|
|
6107
6791
|
}
|
|
@@ -6125,13 +6809,13 @@ function tryAcquireWorkerSlot() {
|
|
|
6125
6809
|
function registerWorkerPid(pid) {
|
|
6126
6810
|
try {
|
|
6127
6811
|
mkdirSync8(WORKER_PID_DIR, { recursive: true });
|
|
6128
|
-
writeFileSync8(
|
|
6812
|
+
writeFileSync8(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
6129
6813
|
} catch {
|
|
6130
6814
|
}
|
|
6131
6815
|
}
|
|
6132
6816
|
function cleanupWorkerPid() {
|
|
6133
6817
|
try {
|
|
6134
|
-
unlinkSync7(
|
|
6818
|
+
unlinkSync7(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
6135
6819
|
} catch {
|
|
6136
6820
|
}
|
|
6137
6821
|
}
|
|
@@ -6171,9 +6855,9 @@ var init_worker_gate = __esm({
|
|
|
6171
6855
|
"src/lib/worker-gate.ts"() {
|
|
6172
6856
|
"use strict";
|
|
6173
6857
|
init_config();
|
|
6174
|
-
WORKER_PID_DIR =
|
|
6858
|
+
WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
|
|
6175
6859
|
MAX_CONCURRENT_WORKERS = 3;
|
|
6176
|
-
BACKFILL_LOCK =
|
|
6860
|
+
BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
|
|
6177
6861
|
}
|
|
6178
6862
|
});
|
|
6179
6863
|
|
|
@@ -6181,7 +6865,7 @@ var init_worker_gate = __esm({
|
|
|
6181
6865
|
import crypto7 from "crypto";
|
|
6182
6866
|
import { execSync as execSync7 } from "child_process";
|
|
6183
6867
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
6184
|
-
import
|
|
6868
|
+
import path20 from "path";
|
|
6185
6869
|
|
|
6186
6870
|
// src/lib/error-detector.ts
|
|
6187
6871
|
init_mcp_prefix();
|
|
@@ -6292,15 +6976,15 @@ import { createHash } from "crypto";
|
|
|
6292
6976
|
// src/lib/keychain.ts
|
|
6293
6977
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
6294
6978
|
import { existsSync as existsSync4 } from "fs";
|
|
6295
|
-
import
|
|
6296
|
-
import
|
|
6979
|
+
import path6 from "path";
|
|
6980
|
+
import os5 from "os";
|
|
6297
6981
|
var SERVICE = "exe-mem";
|
|
6298
6982
|
var ACCOUNT = "master-key";
|
|
6299
6983
|
function getKeyDir() {
|
|
6300
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
6984
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
6301
6985
|
}
|
|
6302
6986
|
function getKeyPath() {
|
|
6303
|
-
return
|
|
6987
|
+
return path6.join(getKeyDir(), "master.key");
|
|
6304
6988
|
}
|
|
6305
6989
|
async function tryKeytar() {
|
|
6306
6990
|
try {
|
|
@@ -6323,7 +7007,7 @@ async function getMasterKey() {
|
|
|
6323
7007
|
const keyPath = getKeyPath();
|
|
6324
7008
|
if (!existsSync4(keyPath)) {
|
|
6325
7009
|
process.stderr.write(
|
|
6326
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
7010
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6327
7011
|
`
|
|
6328
7012
|
);
|
|
6329
7013
|
return null;
|
|
@@ -6796,9 +7480,9 @@ function extractBash(input2, response) {
|
|
|
6796
7480
|
}
|
|
6797
7481
|
function extractGrep(input2, response) {
|
|
6798
7482
|
const pattern = String(input2.pattern ?? "");
|
|
6799
|
-
const
|
|
7483
|
+
const path21 = input2.path ? String(input2.path) : "";
|
|
6800
7484
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
6801
|
-
return `Searched for "${pattern}"${
|
|
7485
|
+
return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
|
|
6802
7486
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
6803
7487
|
}
|
|
6804
7488
|
function extractGlob(input2, response) {
|
|
@@ -6941,7 +7625,7 @@ process.stdin.on("end", async () => {
|
|
|
6941
7625
|
try {
|
|
6942
7626
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6943
7627
|
const agentId2 = process.env.AGENT_ID;
|
|
6944
|
-
const cachePath =
|
|
7628
|
+
const cachePath = path20.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
|
|
6945
7629
|
const { readFileSync: rf } = await import("fs");
|
|
6946
7630
|
const cached = JSON.parse(rf(cachePath, "utf8"));
|
|
6947
7631
|
taskId = cached.taskId ?? null;
|
|
@@ -6980,7 +7664,7 @@ process.stdin.on("end", async () => {
|
|
|
6980
7664
|
if (needsBackfill) {
|
|
6981
7665
|
try {
|
|
6982
7666
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6983
|
-
const flagPath =
|
|
7667
|
+
const flagPath = path20.join(exeDir, "session-cache", "needs-backfill");
|
|
6984
7668
|
writeFileSync9(flagPath, "1");
|
|
6985
7669
|
} catch (err) {
|
|
6986
7670
|
process.stderr.write(`[ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -7092,8 +7776,8 @@ process.stdin.on("end", async () => {
|
|
|
7092
7776
|
}
|
|
7093
7777
|
const cwd = data.cwd ?? process.cwd();
|
|
7094
7778
|
try {
|
|
7095
|
-
mkdirSync9(
|
|
7096
|
-
mkdirSync9(
|
|
7779
|
+
mkdirSync9(path20.join(cwd, "exe/output"), { recursive: true });
|
|
7780
|
+
mkdirSync9(path20.join(cwd, "exe/research"), { recursive: true });
|
|
7097
7781
|
const { ensureGitignoreExe: ensureGitignoreExe2 } = await Promise.resolve().then(() => (init_tasks(), tasks_exports));
|
|
7098
7782
|
await ensureGitignoreExe2(cwd);
|
|
7099
7783
|
} catch (err) {
|