@askexenow/exe-os 0.9.6 → 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 +668 -37
- package/dist/bin/cli.js +1399 -607
- 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 +795 -155
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +703 -72
- 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 +1064 -273
- package/dist/bin/exe-heartbeat.js +676 -45
- 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 +845 -152
- 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 +668 -37
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +731 -91
- 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 +735 -95
- 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 +1038 -247
- package/dist/hooks/bug-report-worker.js +902 -172
- package/dist/hooks/commit-complete.js +729 -89
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +851 -158
- 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 +685 -45
- package/dist/hooks/pre-compact.js +729 -89
- package/dist/hooks/pre-tool-use.js +883 -127
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1071 -321
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +732 -92
- package/dist/hooks/session-start.js +1042 -209
- package/dist/hooks/stop.js +691 -51
- package/dist/hooks/subagent-stop.js +685 -45
- package/dist/hooks/summary-worker.js +827 -134
- package/dist/index.js +1026 -234
- 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 +905 -164
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +66 -30
- 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 +109 -73
- package/dist/lib/tmux-routing.js +98 -62
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1807 -472
- 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 +301 -166
- 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 +206 -40
- package/dist/mcp/tools/send-message.js +69 -33
- package/dist/mcp/tools/update-task.js +86 -50
- package/dist/runtime/index.js +731 -91
- package/dist/tui/App.js +864 -125
- 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;
|
|
@@ -3916,15 +4600,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
3916
4600
|
function resolveExeSession() {
|
|
3917
4601
|
const mySession = getMySession();
|
|
3918
4602
|
if (!mySession) return null;
|
|
4603
|
+
const fromSessionName = extractRootExe(mySession);
|
|
3919
4604
|
try {
|
|
3920
4605
|
const key = getSessionKey();
|
|
3921
4606
|
const parentExe = getParentExe(key);
|
|
3922
4607
|
if (parentExe) {
|
|
3923
|
-
|
|
4608
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
4609
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
4610
|
+
process.stderr.write(
|
|
4611
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
4612
|
+
`
|
|
4613
|
+
);
|
|
4614
|
+
return fromSessionName;
|
|
4615
|
+
}
|
|
4616
|
+
return fromCache;
|
|
3924
4617
|
}
|
|
3925
4618
|
} catch {
|
|
3926
4619
|
}
|
|
3927
|
-
return
|
|
4620
|
+
return fromSessionName ?? mySession;
|
|
3928
4621
|
}
|
|
3929
4622
|
function isEmployeeAlive(sessionName) {
|
|
3930
4623
|
return getTransport().isAlive(sessionName);
|
|
@@ -4082,7 +4775,7 @@ function sendIntercom(targetSession) {
|
|
|
4082
4775
|
try {
|
|
4083
4776
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4084
4777
|
const agent = baseAgentName(rawAgent);
|
|
4085
|
-
const markerPath =
|
|
4778
|
+
const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4086
4779
|
if (existsSync12(markerPath)) {
|
|
4087
4780
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
4088
4781
|
return "debounced";
|
|
@@ -4092,7 +4785,7 @@ function sendIntercom(targetSession) {
|
|
|
4092
4785
|
try {
|
|
4093
4786
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4094
4787
|
const agent = baseAgentName(rawAgent);
|
|
4095
|
-
const taskDir =
|
|
4788
|
+
const taskDir = path14.join(process.cwd(), "exe", agent);
|
|
4096
4789
|
if (existsSync12(taskDir)) {
|
|
4097
4790
|
const files = readdirSync3(taskDir).filter(
|
|
4098
4791
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -4226,8 +4919,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4226
4919
|
const transport = getTransport();
|
|
4227
4920
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4228
4921
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4229
|
-
const logDir =
|
|
4230
|
-
const logFile =
|
|
4922
|
+
const logDir = path14.join(os9.homedir(), ".exe-os", "session-logs");
|
|
4923
|
+
const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4231
4924
|
if (!existsSync12(logDir)) {
|
|
4232
4925
|
mkdirSync6(logDir, { recursive: true });
|
|
4233
4926
|
}
|
|
@@ -4235,14 +4928,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4235
4928
|
let cleanupSuffix = "";
|
|
4236
4929
|
try {
|
|
4237
4930
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4238
|
-
const cleanupScript =
|
|
4931
|
+
const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4239
4932
|
if (existsSync12(cleanupScript)) {
|
|
4240
4933
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4241
4934
|
}
|
|
4242
4935
|
} catch {
|
|
4243
4936
|
}
|
|
4244
4937
|
try {
|
|
4245
|
-
const claudeJsonPath =
|
|
4938
|
+
const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
|
|
4246
4939
|
let claudeJson = {};
|
|
4247
4940
|
try {
|
|
4248
4941
|
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
@@ -4257,10 +4950,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4257
4950
|
} catch {
|
|
4258
4951
|
}
|
|
4259
4952
|
try {
|
|
4260
|
-
const settingsDir =
|
|
4953
|
+
const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
|
|
4261
4954
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4262
|
-
const projSettingsDir =
|
|
4263
|
-
const settingsPath =
|
|
4955
|
+
const projSettingsDir = path14.join(settingsDir, normalizedKey);
|
|
4956
|
+
const settingsPath = path14.join(projSettingsDir, "settings.json");
|
|
4264
4957
|
let settings = {};
|
|
4265
4958
|
try {
|
|
4266
4959
|
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
@@ -4307,8 +5000,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4307
5000
|
let behaviorsFlag = "";
|
|
4308
5001
|
let legacyFallbackWarned = false;
|
|
4309
5002
|
if (!useExeAgent && !useBinSymlink) {
|
|
4310
|
-
const identityPath =
|
|
4311
|
-
|
|
5003
|
+
const identityPath = path14.join(
|
|
5004
|
+
os9.homedir(),
|
|
4312
5005
|
".exe-os",
|
|
4313
5006
|
"identity",
|
|
4314
5007
|
`${employeeName}.md`
|
|
@@ -4323,7 +5016,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4323
5016
|
}
|
|
4324
5017
|
const behaviorsFile = exportBehaviorsSync(
|
|
4325
5018
|
employeeName,
|
|
4326
|
-
|
|
5019
|
+
path14.basename(spawnCwd),
|
|
4327
5020
|
sessionName
|
|
4328
5021
|
);
|
|
4329
5022
|
if (behaviorsFile) {
|
|
@@ -4338,9 +5031,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4338
5031
|
}
|
|
4339
5032
|
let sessionContextFlag = "";
|
|
4340
5033
|
try {
|
|
4341
|
-
const ctxDir =
|
|
5034
|
+
const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4342
5035
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4343
|
-
const ctxFile =
|
|
5036
|
+
const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4344
5037
|
const ctxContent = [
|
|
4345
5038
|
`## Session Context`,
|
|
4346
5039
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -4424,7 +5117,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4424
5117
|
transport.pipeLog(sessionName, logFile);
|
|
4425
5118
|
try {
|
|
4426
5119
|
const mySession = getMySession();
|
|
4427
|
-
const dispatchInfo =
|
|
5120
|
+
const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4428
5121
|
writeFileSync6(dispatchInfo, JSON.stringify({
|
|
4429
5122
|
dispatchedBy: mySession,
|
|
4430
5123
|
rootExe: exeSession,
|
|
@@ -4499,15 +5192,15 @@ var init_tmux_routing = __esm({
|
|
|
4499
5192
|
init_intercom_queue();
|
|
4500
5193
|
init_plan_limits();
|
|
4501
5194
|
init_employees();
|
|
4502
|
-
SPAWN_LOCK_DIR =
|
|
4503
|
-
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");
|
|
4504
5197
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4505
5198
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4506
5199
|
VERIFY_PANE_LINES = 200;
|
|
4507
5200
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4508
5201
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
4509
|
-
INTERCOM_LOG2 =
|
|
4510
|
-
DEBOUNCE_FILE =
|
|
5202
|
+
INTERCOM_LOG2 = path14.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5203
|
+
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4511
5204
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4512
5205
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
4513
5206
|
}
|
|
@@ -4539,8 +5232,8 @@ var init_task_scope = __esm({
|
|
|
4539
5232
|
|
|
4540
5233
|
// src/lib/tasks-crud.ts
|
|
4541
5234
|
import crypto4 from "crypto";
|
|
4542
|
-
import
|
|
4543
|
-
import
|
|
5235
|
+
import path15 from "path";
|
|
5236
|
+
import os10 from "os";
|
|
4544
5237
|
import { execSync as execSync6 } from "child_process";
|
|
4545
5238
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4546
5239
|
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
@@ -4718,8 +5411,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4718
5411
|
}
|
|
4719
5412
|
if (input2.baseDir) {
|
|
4720
5413
|
try {
|
|
4721
|
-
await mkdir4(
|
|
4722
|
-
await mkdir4(
|
|
5414
|
+
await mkdir4(path15.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
5415
|
+
await mkdir4(path15.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
4723
5416
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
4724
5417
|
await ensureGitignoreExe(input2.baseDir);
|
|
4725
5418
|
} catch {
|
|
@@ -4755,9 +5448,9 @@ ${laneWarning}` : laneWarning;
|
|
|
4755
5448
|
});
|
|
4756
5449
|
if (input2.baseDir) {
|
|
4757
5450
|
try {
|
|
4758
|
-
const EXE_OS_DIR =
|
|
4759
|
-
const mdPath =
|
|
4760
|
-
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);
|
|
4761
5454
|
if (!existsSync13(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4762
5455
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
4763
5456
|
const mdContent = `# ${input2.title}
|
|
@@ -5058,7 +5751,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5058
5751
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5059
5752
|
}
|
|
5060
5753
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5061
|
-
const archPath =
|
|
5754
|
+
const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5062
5755
|
try {
|
|
5063
5756
|
if (existsSync13(archPath)) return;
|
|
5064
5757
|
const template = [
|
|
@@ -5093,7 +5786,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5093
5786
|
}
|
|
5094
5787
|
}
|
|
5095
5788
|
async function ensureGitignoreExe(baseDir) {
|
|
5096
|
-
const gitignorePath =
|
|
5789
|
+
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
5097
5790
|
try {
|
|
5098
5791
|
if (existsSync13(gitignorePath)) {
|
|
5099
5792
|
const content = readFileSync11(gitignorePath, "utf-8");
|
|
@@ -5127,13 +5820,13 @@ var init_tasks_crud = __esm({
|
|
|
5127
5820
|
});
|
|
5128
5821
|
|
|
5129
5822
|
// src/lib/tasks-review.ts
|
|
5130
|
-
import
|
|
5823
|
+
import path16 from "path";
|
|
5131
5824
|
import { existsSync as existsSync14, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
5132
5825
|
async function countPendingReviews(sessionScope) {
|
|
5133
5826
|
const client = getClient();
|
|
5134
5827
|
if (sessionScope) {
|
|
5135
5828
|
const result2 = await client.execute({
|
|
5136
|
-
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 = ?",
|
|
5137
5830
|
args: [sessionScope]
|
|
5138
5831
|
});
|
|
5139
5832
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -5309,11 +6002,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5309
6002
|
);
|
|
5310
6003
|
}
|
|
5311
6004
|
try {
|
|
5312
|
-
const cacheDir =
|
|
6005
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5313
6006
|
if (existsSync14(cacheDir)) {
|
|
5314
6007
|
for (const f of readdirSync4(cacheDir)) {
|
|
5315
6008
|
if (f.startsWith("review-notified-")) {
|
|
5316
|
-
unlinkSync5(
|
|
6009
|
+
unlinkSync5(path16.join(cacheDir, f));
|
|
5317
6010
|
}
|
|
5318
6011
|
}
|
|
5319
6012
|
}
|
|
@@ -5334,7 +6027,7 @@ var init_tasks_review = __esm({
|
|
|
5334
6027
|
});
|
|
5335
6028
|
|
|
5336
6029
|
// src/lib/tasks-chain.ts
|
|
5337
|
-
import
|
|
6030
|
+
import path17 from "path";
|
|
5338
6031
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5339
6032
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5340
6033
|
const client = getClient();
|
|
@@ -5351,7 +6044,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5351
6044
|
});
|
|
5352
6045
|
for (const ur of unblockedRows.rows) {
|
|
5353
6046
|
try {
|
|
5354
|
-
const ubFile =
|
|
6047
|
+
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
5355
6048
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5356
6049
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5357
6050
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5877,7 +6570,7 @@ __export(tasks_exports, {
|
|
|
5877
6570
|
updateTaskStatus: () => updateTaskStatus,
|
|
5878
6571
|
writeCheckpoint: () => writeCheckpoint
|
|
5879
6572
|
});
|
|
5880
|
-
import
|
|
6573
|
+
import path18 from "path";
|
|
5881
6574
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
5882
6575
|
async function createTask(input2) {
|
|
5883
6576
|
const result = await createTaskCore(input2);
|
|
@@ -5897,8 +6590,8 @@ async function updateTask(input2) {
|
|
|
5897
6590
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
5898
6591
|
try {
|
|
5899
6592
|
const agent = String(row.assigned_to);
|
|
5900
|
-
const cacheDir =
|
|
5901
|
-
const cachePath =
|
|
6593
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
6594
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
5902
6595
|
if (input2.status === "in_progress") {
|
|
5903
6596
|
mkdirSync7(cacheDir, { recursive: true });
|
|
5904
6597
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -6069,12 +6762,12 @@ __export(worker_gate_exports, {
|
|
|
6069
6762
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
6070
6763
|
});
|
|
6071
6764
|
import { readdirSync as readdirSync5, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
|
|
6072
|
-
import
|
|
6765
|
+
import path19 from "path";
|
|
6073
6766
|
function tryAcquireWorkerSlot() {
|
|
6074
6767
|
try {
|
|
6075
6768
|
mkdirSync8(WORKER_PID_DIR, { recursive: true });
|
|
6076
6769
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
6077
|
-
const reservationPath =
|
|
6770
|
+
const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
6078
6771
|
writeFileSync8(reservationPath, String(process.pid));
|
|
6079
6772
|
const files = readdirSync5(WORKER_PID_DIR);
|
|
6080
6773
|
let alive = 0;
|
|
@@ -6092,7 +6785,7 @@ function tryAcquireWorkerSlot() {
|
|
|
6092
6785
|
alive++;
|
|
6093
6786
|
} catch {
|
|
6094
6787
|
try {
|
|
6095
|
-
unlinkSync7(
|
|
6788
|
+
unlinkSync7(path19.join(WORKER_PID_DIR, f));
|
|
6096
6789
|
} catch {
|
|
6097
6790
|
}
|
|
6098
6791
|
}
|
|
@@ -6116,13 +6809,13 @@ function tryAcquireWorkerSlot() {
|
|
|
6116
6809
|
function registerWorkerPid(pid) {
|
|
6117
6810
|
try {
|
|
6118
6811
|
mkdirSync8(WORKER_PID_DIR, { recursive: true });
|
|
6119
|
-
writeFileSync8(
|
|
6812
|
+
writeFileSync8(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
6120
6813
|
} catch {
|
|
6121
6814
|
}
|
|
6122
6815
|
}
|
|
6123
6816
|
function cleanupWorkerPid() {
|
|
6124
6817
|
try {
|
|
6125
|
-
unlinkSync7(
|
|
6818
|
+
unlinkSync7(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
6126
6819
|
} catch {
|
|
6127
6820
|
}
|
|
6128
6821
|
}
|
|
@@ -6162,9 +6855,9 @@ var init_worker_gate = __esm({
|
|
|
6162
6855
|
"src/lib/worker-gate.ts"() {
|
|
6163
6856
|
"use strict";
|
|
6164
6857
|
init_config();
|
|
6165
|
-
WORKER_PID_DIR =
|
|
6858
|
+
WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
|
|
6166
6859
|
MAX_CONCURRENT_WORKERS = 3;
|
|
6167
|
-
BACKFILL_LOCK =
|
|
6860
|
+
BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
|
|
6168
6861
|
}
|
|
6169
6862
|
});
|
|
6170
6863
|
|
|
@@ -6172,7 +6865,7 @@ var init_worker_gate = __esm({
|
|
|
6172
6865
|
import crypto7 from "crypto";
|
|
6173
6866
|
import { execSync as execSync7 } from "child_process";
|
|
6174
6867
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
6175
|
-
import
|
|
6868
|
+
import path20 from "path";
|
|
6176
6869
|
|
|
6177
6870
|
// src/lib/error-detector.ts
|
|
6178
6871
|
init_mcp_prefix();
|
|
@@ -6283,15 +6976,15 @@ import { createHash } from "crypto";
|
|
|
6283
6976
|
// src/lib/keychain.ts
|
|
6284
6977
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
6285
6978
|
import { existsSync as existsSync4 } from "fs";
|
|
6286
|
-
import
|
|
6287
|
-
import
|
|
6979
|
+
import path6 from "path";
|
|
6980
|
+
import os5 from "os";
|
|
6288
6981
|
var SERVICE = "exe-mem";
|
|
6289
6982
|
var ACCOUNT = "master-key";
|
|
6290
6983
|
function getKeyDir() {
|
|
6291
|
-
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");
|
|
6292
6985
|
}
|
|
6293
6986
|
function getKeyPath() {
|
|
6294
|
-
return
|
|
6987
|
+
return path6.join(getKeyDir(), "master.key");
|
|
6295
6988
|
}
|
|
6296
6989
|
async function tryKeytar() {
|
|
6297
6990
|
try {
|
|
@@ -6314,7 +7007,7 @@ async function getMasterKey() {
|
|
|
6314
7007
|
const keyPath = getKeyPath();
|
|
6315
7008
|
if (!existsSync4(keyPath)) {
|
|
6316
7009
|
process.stderr.write(
|
|
6317
|
-
`[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"})
|
|
6318
7011
|
`
|
|
6319
7012
|
);
|
|
6320
7013
|
return null;
|
|
@@ -6787,9 +7480,9 @@ function extractBash(input2, response) {
|
|
|
6787
7480
|
}
|
|
6788
7481
|
function extractGrep(input2, response) {
|
|
6789
7482
|
const pattern = String(input2.pattern ?? "");
|
|
6790
|
-
const
|
|
7483
|
+
const path21 = input2.path ? String(input2.path) : "";
|
|
6791
7484
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
6792
|
-
return `Searched for "${pattern}"${
|
|
7485
|
+
return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
|
|
6793
7486
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
6794
7487
|
}
|
|
6795
7488
|
function extractGlob(input2, response) {
|
|
@@ -6932,7 +7625,7 @@ process.stdin.on("end", async () => {
|
|
|
6932
7625
|
try {
|
|
6933
7626
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6934
7627
|
const agentId2 = process.env.AGENT_ID;
|
|
6935
|
-
const cachePath =
|
|
7628
|
+
const cachePath = path20.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
|
|
6936
7629
|
const { readFileSync: rf } = await import("fs");
|
|
6937
7630
|
const cached = JSON.parse(rf(cachePath, "utf8"));
|
|
6938
7631
|
taskId = cached.taskId ?? null;
|
|
@@ -6971,7 +7664,7 @@ process.stdin.on("end", async () => {
|
|
|
6971
7664
|
if (needsBackfill) {
|
|
6972
7665
|
try {
|
|
6973
7666
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6974
|
-
const flagPath =
|
|
7667
|
+
const flagPath = path20.join(exeDir, "session-cache", "needs-backfill");
|
|
6975
7668
|
writeFileSync9(flagPath, "1");
|
|
6976
7669
|
} catch (err) {
|
|
6977
7670
|
process.stderr.write(`[ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -7083,8 +7776,8 @@ process.stdin.on("end", async () => {
|
|
|
7083
7776
|
}
|
|
7084
7777
|
const cwd = data.cwd ?? process.cwd();
|
|
7085
7778
|
try {
|
|
7086
|
-
mkdirSync9(
|
|
7087
|
-
mkdirSync9(
|
|
7779
|
+
mkdirSync9(path20.join(cwd, "exe/output"), { recursive: true });
|
|
7780
|
+
mkdirSync9(path20.join(cwd, "exe/research"), { recursive: true });
|
|
7088
7781
|
const { ensureGitignoreExe: ensureGitignoreExe2 } = await Promise.resolve().then(() => (init_tasks(), tasks_exports));
|
|
7089
7782
|
await ensureGitignoreExe2(cwd);
|
|
7090
7783
|
} catch (err) {
|