@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
package/dist/bin/exe-boot.js
CHANGED
|
@@ -368,7 +368,7 @@ function registerBinSymlinks(name) {
|
|
|
368
368
|
}
|
|
369
369
|
return { created, skipped, errors };
|
|
370
370
|
}
|
|
371
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
371
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
372
372
|
var init_employees = __esm({
|
|
373
373
|
"src/lib/employees.ts"() {
|
|
374
374
|
"use strict";
|
|
@@ -377,6 +377,7 @@ var init_employees = __esm({
|
|
|
377
377
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
378
378
|
COORDINATOR_ROLE = "COO";
|
|
379
379
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
380
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
380
381
|
}
|
|
381
382
|
});
|
|
382
383
|
|
|
@@ -435,13 +436,597 @@ var init_db_retry = __esm({
|
|
|
435
436
|
}
|
|
436
437
|
});
|
|
437
438
|
|
|
439
|
+
// src/lib/database-adapter.ts
|
|
440
|
+
import os3 from "os";
|
|
441
|
+
import path3 from "path";
|
|
442
|
+
import { createRequire } from "module";
|
|
443
|
+
import { pathToFileURL } from "url";
|
|
444
|
+
function quotedIdentifier(identifier) {
|
|
445
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
446
|
+
}
|
|
447
|
+
function unqualifiedTableName(name) {
|
|
448
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
449
|
+
const parts = raw.split(".");
|
|
450
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
451
|
+
}
|
|
452
|
+
function stripTrailingSemicolon(sql) {
|
|
453
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
454
|
+
}
|
|
455
|
+
function appendClause(sql, clause) {
|
|
456
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
457
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
458
|
+
if (!returningMatch) {
|
|
459
|
+
return `${trimmed}${clause}`;
|
|
460
|
+
}
|
|
461
|
+
const idx = returningMatch.index;
|
|
462
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
463
|
+
}
|
|
464
|
+
function normalizeStatement(stmt) {
|
|
465
|
+
if (typeof stmt === "string") {
|
|
466
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
467
|
+
}
|
|
468
|
+
const sql = stmt.sql;
|
|
469
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
470
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
471
|
+
}
|
|
472
|
+
return { kind: "named", sql, args: stmt.args };
|
|
473
|
+
}
|
|
474
|
+
function rewriteBooleanLiterals(sql) {
|
|
475
|
+
let out = sql;
|
|
476
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
477
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
478
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
479
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
480
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
481
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
482
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
483
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
484
|
+
}
|
|
485
|
+
return out;
|
|
486
|
+
}
|
|
487
|
+
function rewriteInsertOrIgnore(sql) {
|
|
488
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
489
|
+
return sql;
|
|
490
|
+
}
|
|
491
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
492
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
493
|
+
}
|
|
494
|
+
function rewriteInsertOrReplace(sql) {
|
|
495
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
496
|
+
if (!match) {
|
|
497
|
+
return sql;
|
|
498
|
+
}
|
|
499
|
+
const rawTable = match[1];
|
|
500
|
+
const rawColumns = match[2];
|
|
501
|
+
const remainder = match[3];
|
|
502
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
503
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
504
|
+
if (!conflictKeys?.length) {
|
|
505
|
+
return sql;
|
|
506
|
+
}
|
|
507
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
508
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
509
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
510
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
511
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
512
|
+
}
|
|
513
|
+
function rewriteSql(sql) {
|
|
514
|
+
let out = sql;
|
|
515
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
516
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
517
|
+
out = rewriteBooleanLiterals(out);
|
|
518
|
+
out = rewriteInsertOrReplace(out);
|
|
519
|
+
out = rewriteInsertOrIgnore(out);
|
|
520
|
+
return stripTrailingSemicolon(out);
|
|
521
|
+
}
|
|
522
|
+
function toBoolean(value) {
|
|
523
|
+
if (value === null || value === void 0) return value;
|
|
524
|
+
if (typeof value === "boolean") return value;
|
|
525
|
+
if (typeof value === "number") return value !== 0;
|
|
526
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
527
|
+
if (typeof value === "string") {
|
|
528
|
+
const normalized = value.trim().toLowerCase();
|
|
529
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
530
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
531
|
+
}
|
|
532
|
+
return Boolean(value);
|
|
533
|
+
}
|
|
534
|
+
function countQuestionMarks(sql, end) {
|
|
535
|
+
let count = 0;
|
|
536
|
+
let inSingle = false;
|
|
537
|
+
let inDouble = false;
|
|
538
|
+
let inLineComment = false;
|
|
539
|
+
let inBlockComment = false;
|
|
540
|
+
for (let i = 0; i < end; i++) {
|
|
541
|
+
const ch = sql[i];
|
|
542
|
+
const next = sql[i + 1];
|
|
543
|
+
if (inLineComment) {
|
|
544
|
+
if (ch === "\n") inLineComment = false;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (inBlockComment) {
|
|
548
|
+
if (ch === "*" && next === "/") {
|
|
549
|
+
inBlockComment = false;
|
|
550
|
+
i += 1;
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
555
|
+
inLineComment = true;
|
|
556
|
+
i += 1;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
560
|
+
inBlockComment = true;
|
|
561
|
+
i += 1;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
565
|
+
inSingle = !inSingle;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
569
|
+
inDouble = !inDouble;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
573
|
+
count += 1;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return count;
|
|
577
|
+
}
|
|
578
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
579
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
580
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
581
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
582
|
+
for (const match of sql.matchAll(pattern)) {
|
|
583
|
+
const matchText = match[0];
|
|
584
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
585
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return indexes;
|
|
589
|
+
}
|
|
590
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
591
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
592
|
+
if (!match) return;
|
|
593
|
+
const rawTable = match[1];
|
|
594
|
+
const rawColumns = match[2];
|
|
595
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
596
|
+
if (!boolColumns?.size) return;
|
|
597
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
598
|
+
for (const [index, column] of columns.entries()) {
|
|
599
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
600
|
+
args[index] = toBoolean(args[index]);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
605
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
606
|
+
if (!match) return;
|
|
607
|
+
const rawTable = match[1];
|
|
608
|
+
const setClause = match[2];
|
|
609
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
610
|
+
if (!boolColumns?.size) return;
|
|
611
|
+
const assignments = setClause.split(",");
|
|
612
|
+
let placeholderIndex = 0;
|
|
613
|
+
for (const assignment of assignments) {
|
|
614
|
+
if (!assignment.includes("?")) continue;
|
|
615
|
+
placeholderIndex += 1;
|
|
616
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
617
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
618
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function coerceBooleanArgs(sql, args) {
|
|
623
|
+
const nextArgs = [...args];
|
|
624
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
625
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
626
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
627
|
+
for (const index of placeholderIndexes) {
|
|
628
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
629
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return nextArgs;
|
|
633
|
+
}
|
|
634
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
635
|
+
let out = "";
|
|
636
|
+
let placeholder = 0;
|
|
637
|
+
let inSingle = false;
|
|
638
|
+
let inDouble = false;
|
|
639
|
+
let inLineComment = false;
|
|
640
|
+
let inBlockComment = false;
|
|
641
|
+
for (let i = 0; i < sql.length; i++) {
|
|
642
|
+
const ch = sql[i];
|
|
643
|
+
const next = sql[i + 1];
|
|
644
|
+
if (inLineComment) {
|
|
645
|
+
out += ch;
|
|
646
|
+
if (ch === "\n") inLineComment = false;
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (inBlockComment) {
|
|
650
|
+
out += ch;
|
|
651
|
+
if (ch === "*" && next === "/") {
|
|
652
|
+
out += next;
|
|
653
|
+
inBlockComment = false;
|
|
654
|
+
i += 1;
|
|
655
|
+
}
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
659
|
+
out += ch + next;
|
|
660
|
+
inLineComment = true;
|
|
661
|
+
i += 1;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
665
|
+
out += ch + next;
|
|
666
|
+
inBlockComment = true;
|
|
667
|
+
i += 1;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
671
|
+
inSingle = !inSingle;
|
|
672
|
+
out += ch;
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
676
|
+
inDouble = !inDouble;
|
|
677
|
+
out += ch;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
681
|
+
placeholder += 1;
|
|
682
|
+
out += `$${placeholder}`;
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
out += ch;
|
|
686
|
+
}
|
|
687
|
+
return out;
|
|
688
|
+
}
|
|
689
|
+
function translateStatementForPostgres(stmt) {
|
|
690
|
+
const normalized = normalizeStatement(stmt);
|
|
691
|
+
if (normalized.kind === "named") {
|
|
692
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
693
|
+
}
|
|
694
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
695
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
696
|
+
return {
|
|
697
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
698
|
+
args: coercedArgs
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function shouldBypassPostgres(stmt) {
|
|
702
|
+
const normalized = normalizeStatement(stmt);
|
|
703
|
+
if (normalized.kind === "named") {
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
707
|
+
}
|
|
708
|
+
function shouldFallbackOnError(error) {
|
|
709
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
710
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
711
|
+
}
|
|
712
|
+
function isReadQuery(sql) {
|
|
713
|
+
const trimmed = sql.trimStart();
|
|
714
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
715
|
+
}
|
|
716
|
+
function buildRow(row, columns) {
|
|
717
|
+
const values = columns.map((column) => row[column]);
|
|
718
|
+
return Object.assign(values, row);
|
|
719
|
+
}
|
|
720
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
721
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
722
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
723
|
+
return {
|
|
724
|
+
columns,
|
|
725
|
+
columnTypes: columns.map(() => ""),
|
|
726
|
+
rows: resultRows,
|
|
727
|
+
rowsAffected,
|
|
728
|
+
lastInsertRowid: void 0,
|
|
729
|
+
toJSON() {
|
|
730
|
+
return {
|
|
731
|
+
columns,
|
|
732
|
+
columnTypes: columns.map(() => ""),
|
|
733
|
+
rows,
|
|
734
|
+
rowsAffected,
|
|
735
|
+
lastInsertRowid: void 0
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
async function loadPrismaClient() {
|
|
741
|
+
if (!prismaClientPromise) {
|
|
742
|
+
prismaClientPromise = (async () => {
|
|
743
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
744
|
+
if (explicitPath) {
|
|
745
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
746
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
747
|
+
if (!PrismaClient2) {
|
|
748
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
749
|
+
}
|
|
750
|
+
return new PrismaClient2();
|
|
751
|
+
}
|
|
752
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
753
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
754
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
755
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
756
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
757
|
+
if (!PrismaClient) {
|
|
758
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
759
|
+
}
|
|
760
|
+
return new PrismaClient();
|
|
761
|
+
})();
|
|
762
|
+
}
|
|
763
|
+
return prismaClientPromise;
|
|
764
|
+
}
|
|
765
|
+
async function ensureCompatibilityViews(prisma) {
|
|
766
|
+
if (!compatibilityBootstrapPromise) {
|
|
767
|
+
compatibilityBootstrapPromise = (async () => {
|
|
768
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
769
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
770
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
771
|
+
"SELECT to_regclass($1) AS regclass",
|
|
772
|
+
relation
|
|
773
|
+
);
|
|
774
|
+
if (!rows[0]?.regclass) {
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
await prisma.$executeRawUnsafe(
|
|
778
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
})();
|
|
782
|
+
}
|
|
783
|
+
return compatibilityBootstrapPromise;
|
|
784
|
+
}
|
|
785
|
+
async function executeOnPrisma(executor, stmt) {
|
|
786
|
+
const translated = translateStatementForPostgres(stmt);
|
|
787
|
+
if (isReadQuery(translated.sql)) {
|
|
788
|
+
const rows = await executor.$queryRawUnsafe(
|
|
789
|
+
translated.sql,
|
|
790
|
+
...translated.args
|
|
791
|
+
);
|
|
792
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
793
|
+
}
|
|
794
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
795
|
+
return buildResultSet([], rowsAffected);
|
|
796
|
+
}
|
|
797
|
+
function splitSqlStatements(sql) {
|
|
798
|
+
const parts = [];
|
|
799
|
+
let current = "";
|
|
800
|
+
let inSingle = false;
|
|
801
|
+
let inDouble = false;
|
|
802
|
+
let inLineComment = false;
|
|
803
|
+
let inBlockComment = false;
|
|
804
|
+
for (let i = 0; i < sql.length; i++) {
|
|
805
|
+
const ch = sql[i];
|
|
806
|
+
const next = sql[i + 1];
|
|
807
|
+
if (inLineComment) {
|
|
808
|
+
current += ch;
|
|
809
|
+
if (ch === "\n") inLineComment = false;
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (inBlockComment) {
|
|
813
|
+
current += ch;
|
|
814
|
+
if (ch === "*" && next === "/") {
|
|
815
|
+
current += next;
|
|
816
|
+
inBlockComment = false;
|
|
817
|
+
i += 1;
|
|
818
|
+
}
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
822
|
+
current += ch + next;
|
|
823
|
+
inLineComment = true;
|
|
824
|
+
i += 1;
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
828
|
+
current += ch + next;
|
|
829
|
+
inBlockComment = true;
|
|
830
|
+
i += 1;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
834
|
+
inSingle = !inSingle;
|
|
835
|
+
current += ch;
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
839
|
+
inDouble = !inDouble;
|
|
840
|
+
current += ch;
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
844
|
+
if (current.trim()) {
|
|
845
|
+
parts.push(current.trim());
|
|
846
|
+
}
|
|
847
|
+
current = "";
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
current += ch;
|
|
851
|
+
}
|
|
852
|
+
if (current.trim()) {
|
|
853
|
+
parts.push(current.trim());
|
|
854
|
+
}
|
|
855
|
+
return parts;
|
|
856
|
+
}
|
|
857
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
858
|
+
const prisma = await loadPrismaClient();
|
|
859
|
+
await ensureCompatibilityViews(prisma);
|
|
860
|
+
let closed = false;
|
|
861
|
+
let adapter;
|
|
862
|
+
const fallbackExecute = async (stmt, error) => {
|
|
863
|
+
if (!fallbackClient) {
|
|
864
|
+
if (error) throw error;
|
|
865
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
866
|
+
}
|
|
867
|
+
if (error) {
|
|
868
|
+
process.stderr.write(
|
|
869
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
870
|
+
`
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
return fallbackClient.execute(stmt);
|
|
874
|
+
};
|
|
875
|
+
adapter = {
|
|
876
|
+
async execute(stmt) {
|
|
877
|
+
if (shouldBypassPostgres(stmt)) {
|
|
878
|
+
return fallbackExecute(stmt);
|
|
879
|
+
}
|
|
880
|
+
try {
|
|
881
|
+
return await executeOnPrisma(prisma, stmt);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
if (shouldFallbackOnError(error)) {
|
|
884
|
+
return fallbackExecute(stmt, error);
|
|
885
|
+
}
|
|
886
|
+
throw error;
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
async batch(stmts, mode) {
|
|
890
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
891
|
+
if (!fallbackClient) {
|
|
892
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
893
|
+
}
|
|
894
|
+
return fallbackClient.batch(stmts, mode);
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
if (prisma.$transaction) {
|
|
898
|
+
return await prisma.$transaction(async (tx) => {
|
|
899
|
+
const results2 = [];
|
|
900
|
+
for (const stmt of stmts) {
|
|
901
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
902
|
+
}
|
|
903
|
+
return results2;
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
const results = [];
|
|
907
|
+
for (const stmt of stmts) {
|
|
908
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
909
|
+
}
|
|
910
|
+
return results;
|
|
911
|
+
} catch (error) {
|
|
912
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
913
|
+
process.stderr.write(
|
|
914
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
915
|
+
`
|
|
916
|
+
);
|
|
917
|
+
return fallbackClient.batch(stmts, mode);
|
|
918
|
+
}
|
|
919
|
+
throw error;
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
async migrate(stmts) {
|
|
923
|
+
if (fallbackClient) {
|
|
924
|
+
return fallbackClient.migrate(stmts);
|
|
925
|
+
}
|
|
926
|
+
return adapter.batch(stmts, "deferred");
|
|
927
|
+
},
|
|
928
|
+
async transaction(mode) {
|
|
929
|
+
if (!fallbackClient) {
|
|
930
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
931
|
+
}
|
|
932
|
+
return fallbackClient.transaction(mode);
|
|
933
|
+
},
|
|
934
|
+
async executeMultiple(sql) {
|
|
935
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
936
|
+
return fallbackClient.executeMultiple(sql);
|
|
937
|
+
}
|
|
938
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
939
|
+
await adapter.execute(statement);
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
async sync() {
|
|
943
|
+
if (fallbackClient) {
|
|
944
|
+
return fallbackClient.sync();
|
|
945
|
+
}
|
|
946
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
947
|
+
},
|
|
948
|
+
close() {
|
|
949
|
+
closed = true;
|
|
950
|
+
prismaClientPromise = null;
|
|
951
|
+
compatibilityBootstrapPromise = null;
|
|
952
|
+
void prisma.$disconnect?.();
|
|
953
|
+
},
|
|
954
|
+
get closed() {
|
|
955
|
+
return closed;
|
|
956
|
+
},
|
|
957
|
+
get protocol() {
|
|
958
|
+
return "prisma-postgres";
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
return adapter;
|
|
962
|
+
}
|
|
963
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
964
|
+
var init_database_adapter = __esm({
|
|
965
|
+
"src/lib/database-adapter.ts"() {
|
|
966
|
+
"use strict";
|
|
967
|
+
VIEW_MAPPINGS = [
|
|
968
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
969
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
970
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
971
|
+
{ view: "entities", source: "memory.entities" },
|
|
972
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
973
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
974
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
975
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
976
|
+
{ view: "messages", source: "memory.messages" },
|
|
977
|
+
{ view: "users", source: "wiki.users" },
|
|
978
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
979
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
980
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
981
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
982
|
+
];
|
|
983
|
+
UPSERT_KEYS = {
|
|
984
|
+
memories: ["id"],
|
|
985
|
+
tasks: ["id"],
|
|
986
|
+
behaviors: ["id"],
|
|
987
|
+
entities: ["id"],
|
|
988
|
+
relationships: ["id"],
|
|
989
|
+
entity_aliases: ["alias"],
|
|
990
|
+
notifications: ["id"],
|
|
991
|
+
messages: ["id"],
|
|
992
|
+
users: ["id"],
|
|
993
|
+
workspaces: ["id"],
|
|
994
|
+
workspace_users: ["id"],
|
|
995
|
+
documents: ["id"],
|
|
996
|
+
chats: ["id"]
|
|
997
|
+
};
|
|
998
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
999
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1000
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1001
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1002
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1003
|
+
};
|
|
1004
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1005
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1006
|
+
);
|
|
1007
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1008
|
+
/\bPRAGMA\b/i,
|
|
1009
|
+
/\bsqlite_master\b/i,
|
|
1010
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1011
|
+
/\bMATCH\b/i,
|
|
1012
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1013
|
+
/\bjson_extract\s*\(/i,
|
|
1014
|
+
/\bjulianday\s*\(/i,
|
|
1015
|
+
/\bstrftime\s*\(/i,
|
|
1016
|
+
/\blast_insert_rowid\s*\(/i
|
|
1017
|
+
];
|
|
1018
|
+
prismaClientPromise = null;
|
|
1019
|
+
compatibilityBootstrapPromise = null;
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
|
|
438
1023
|
// src/lib/exe-daemon-client.ts
|
|
439
1024
|
import net from "net";
|
|
440
|
-
import
|
|
1025
|
+
import os4 from "os";
|
|
441
1026
|
import { spawn } from "child_process";
|
|
442
1027
|
import { randomUUID } from "crypto";
|
|
443
1028
|
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
444
|
-
import
|
|
1029
|
+
import path4 from "path";
|
|
445
1030
|
import { fileURLToPath } from "url";
|
|
446
1031
|
function handleData(chunk) {
|
|
447
1032
|
_buffer += chunk.toString();
|
|
@@ -492,17 +1077,17 @@ function cleanupStaleFiles() {
|
|
|
492
1077
|
}
|
|
493
1078
|
}
|
|
494
1079
|
function findPackageRoot() {
|
|
495
|
-
let dir =
|
|
496
|
-
const { root } =
|
|
1080
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1081
|
+
const { root } = path4.parse(dir);
|
|
497
1082
|
while (dir !== root) {
|
|
498
|
-
if (existsSync3(
|
|
499
|
-
dir =
|
|
1083
|
+
if (existsSync3(path4.join(dir, "package.json"))) return dir;
|
|
1084
|
+
dir = path4.dirname(dir);
|
|
500
1085
|
}
|
|
501
1086
|
return null;
|
|
502
1087
|
}
|
|
503
1088
|
function spawnDaemon() {
|
|
504
|
-
const freeGB =
|
|
505
|
-
const totalGB =
|
|
1089
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
1090
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
506
1091
|
if (totalGB <= 8) {
|
|
507
1092
|
process.stderr.write(
|
|
508
1093
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -522,7 +1107,7 @@ function spawnDaemon() {
|
|
|
522
1107
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
523
1108
|
return;
|
|
524
1109
|
}
|
|
525
|
-
const daemonPath =
|
|
1110
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
526
1111
|
if (!existsSync3(daemonPath)) {
|
|
527
1112
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
528
1113
|
`);
|
|
@@ -531,7 +1116,7 @@ function spawnDaemon() {
|
|
|
531
1116
|
const resolvedPath = daemonPath;
|
|
532
1117
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
533
1118
|
`);
|
|
534
|
-
const logPath =
|
|
1119
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
535
1120
|
let stderrFd = "ignore";
|
|
536
1121
|
try {
|
|
537
1122
|
stderrFd = openSync(logPath, "a");
|
|
@@ -678,9 +1263,9 @@ var init_exe_daemon_client = __esm({
|
|
|
678
1263
|
"src/lib/exe-daemon-client.ts"() {
|
|
679
1264
|
"use strict";
|
|
680
1265
|
init_config();
|
|
681
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
682
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
683
|
-
SPAWN_LOCK_PATH =
|
|
1266
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
1267
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
1268
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
684
1269
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
685
1270
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
686
1271
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -762,7 +1347,7 @@ __export(db_daemon_client_exports, {
|
|
|
762
1347
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
763
1348
|
initDaemonDbClient: () => initDaemonDbClient
|
|
764
1349
|
});
|
|
765
|
-
function
|
|
1350
|
+
function normalizeStatement2(stmt) {
|
|
766
1351
|
if (typeof stmt === "string") {
|
|
767
1352
|
return { sql: stmt, args: [] };
|
|
768
1353
|
}
|
|
@@ -786,7 +1371,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
786
1371
|
if (!_useDaemon || !isClientConnected()) {
|
|
787
1372
|
return fallbackClient.execute(stmt);
|
|
788
1373
|
}
|
|
789
|
-
const { sql, args } =
|
|
1374
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
790
1375
|
const response = await sendDaemonRequest({
|
|
791
1376
|
type: "db-execute",
|
|
792
1377
|
sql,
|
|
@@ -811,7 +1396,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
811
1396
|
if (!_useDaemon || !isClientConnected()) {
|
|
812
1397
|
return fallbackClient.batch(stmts, mode);
|
|
813
1398
|
}
|
|
814
|
-
const statements = stmts.map(
|
|
1399
|
+
const statements = stmts.map(normalizeStatement2);
|
|
815
1400
|
const response = await sendDaemonRequest({
|
|
816
1401
|
type: "db-batch",
|
|
817
1402
|
statements,
|
|
@@ -906,6 +1491,18 @@ __export(database_exports, {
|
|
|
906
1491
|
});
|
|
907
1492
|
import { createClient } from "@libsql/client";
|
|
908
1493
|
async function initDatabase(config) {
|
|
1494
|
+
if (_walCheckpointTimer) {
|
|
1495
|
+
clearInterval(_walCheckpointTimer);
|
|
1496
|
+
_walCheckpointTimer = null;
|
|
1497
|
+
}
|
|
1498
|
+
if (_daemonClient) {
|
|
1499
|
+
_daemonClient.close();
|
|
1500
|
+
_daemonClient = null;
|
|
1501
|
+
}
|
|
1502
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1503
|
+
_adapterClient.close();
|
|
1504
|
+
}
|
|
1505
|
+
_adapterClient = null;
|
|
909
1506
|
if (_client) {
|
|
910
1507
|
_client.close();
|
|
911
1508
|
_client = null;
|
|
@@ -919,6 +1516,7 @@ async function initDatabase(config) {
|
|
|
919
1516
|
}
|
|
920
1517
|
_client = createClient(opts);
|
|
921
1518
|
_resilientClient = wrapWithRetry(_client);
|
|
1519
|
+
_adapterClient = _resilientClient;
|
|
922
1520
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
923
1521
|
});
|
|
924
1522
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -929,14 +1527,20 @@ async function initDatabase(config) {
|
|
|
929
1527
|
});
|
|
930
1528
|
}, 3e4);
|
|
931
1529
|
_walCheckpointTimer.unref();
|
|
1530
|
+
if (process.env.DATABASE_URL) {
|
|
1531
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1532
|
+
}
|
|
932
1533
|
}
|
|
933
1534
|
function isInitialized() {
|
|
934
|
-
return _client !== null;
|
|
1535
|
+
return _adapterClient !== null || _client !== null;
|
|
935
1536
|
}
|
|
936
1537
|
function getClient() {
|
|
937
|
-
if (!
|
|
1538
|
+
if (!_adapterClient) {
|
|
938
1539
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
939
1540
|
}
|
|
1541
|
+
if (process.env.DATABASE_URL) {
|
|
1542
|
+
return _adapterClient;
|
|
1543
|
+
}
|
|
940
1544
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
941
1545
|
return _resilientClient;
|
|
942
1546
|
}
|
|
@@ -946,6 +1550,7 @@ function getClient() {
|
|
|
946
1550
|
return _resilientClient;
|
|
947
1551
|
}
|
|
948
1552
|
async function initDaemonClient() {
|
|
1553
|
+
if (process.env.DATABASE_URL) return;
|
|
949
1554
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
950
1555
|
if (!_resilientClient) return;
|
|
951
1556
|
try {
|
|
@@ -1890,26 +2495,36 @@ async function ensureSchema() {
|
|
|
1890
2495
|
}
|
|
1891
2496
|
}
|
|
1892
2497
|
async function disposeDatabase() {
|
|
2498
|
+
if (_walCheckpointTimer) {
|
|
2499
|
+
clearInterval(_walCheckpointTimer);
|
|
2500
|
+
_walCheckpointTimer = null;
|
|
2501
|
+
}
|
|
1893
2502
|
if (_daemonClient) {
|
|
1894
2503
|
_daemonClient.close();
|
|
1895
2504
|
_daemonClient = null;
|
|
1896
2505
|
}
|
|
2506
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2507
|
+
_adapterClient.close();
|
|
2508
|
+
}
|
|
2509
|
+
_adapterClient = null;
|
|
1897
2510
|
if (_client) {
|
|
1898
2511
|
_client.close();
|
|
1899
2512
|
_client = null;
|
|
1900
2513
|
_resilientClient = null;
|
|
1901
2514
|
}
|
|
1902
2515
|
}
|
|
1903
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2516
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1904
2517
|
var init_database = __esm({
|
|
1905
2518
|
"src/lib/database.ts"() {
|
|
1906
2519
|
"use strict";
|
|
1907
2520
|
init_db_retry();
|
|
1908
2521
|
init_employees();
|
|
2522
|
+
init_database_adapter();
|
|
1909
2523
|
_client = null;
|
|
1910
2524
|
_resilientClient = null;
|
|
1911
2525
|
_walCheckpointTimer = null;
|
|
1912
2526
|
_daemonClient = null;
|
|
2527
|
+
_adapterClient = null;
|
|
1913
2528
|
initTurso = initDatabase;
|
|
1914
2529
|
disposeTurso = disposeDatabase;
|
|
1915
2530
|
}
|
|
@@ -2120,13 +2735,13 @@ __export(keychain_exports, {
|
|
|
2120
2735
|
});
|
|
2121
2736
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2122
2737
|
import { existsSync as existsSync4 } from "fs";
|
|
2123
|
-
import
|
|
2124
|
-
import
|
|
2738
|
+
import path5 from "path";
|
|
2739
|
+
import os5 from "os";
|
|
2125
2740
|
function getKeyDir() {
|
|
2126
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2741
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os5.homedir(), ".exe-os");
|
|
2127
2742
|
}
|
|
2128
2743
|
function getKeyPath() {
|
|
2129
|
-
return
|
|
2744
|
+
return path5.join(getKeyDir(), "master.key");
|
|
2130
2745
|
}
|
|
2131
2746
|
async function tryKeytar() {
|
|
2132
2747
|
try {
|
|
@@ -2149,7 +2764,7 @@ async function getMasterKey() {
|
|
|
2149
2764
|
const keyPath = getKeyPath();
|
|
2150
2765
|
if (!existsSync4(keyPath)) {
|
|
2151
2766
|
process.stderr.write(
|
|
2152
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
2767
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2153
2768
|
`
|
|
2154
2769
|
);
|
|
2155
2770
|
return null;
|
|
@@ -2300,7 +2915,7 @@ __export(shard_manager_exports, {
|
|
|
2300
2915
|
listShards: () => listShards,
|
|
2301
2916
|
shardExists: () => shardExists
|
|
2302
2917
|
});
|
|
2303
|
-
import
|
|
2918
|
+
import path6 from "path";
|
|
2304
2919
|
import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
|
|
2305
2920
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2306
2921
|
function initShardManager(encryptionKey) {
|
|
@@ -2326,7 +2941,7 @@ function getShardClient(projectName) {
|
|
|
2326
2941
|
}
|
|
2327
2942
|
const cached = _shards.get(safeName);
|
|
2328
2943
|
if (cached) return cached;
|
|
2329
|
-
const dbPath =
|
|
2944
|
+
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
2330
2945
|
const client = createClient2({
|
|
2331
2946
|
url: `file:${dbPath}`,
|
|
2332
2947
|
encryptionKey: _encryptionKey
|
|
@@ -2336,7 +2951,7 @@ function getShardClient(projectName) {
|
|
|
2336
2951
|
}
|
|
2337
2952
|
function shardExists(projectName) {
|
|
2338
2953
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2339
|
-
return existsSync5(
|
|
2954
|
+
return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
2340
2955
|
}
|
|
2341
2956
|
function listShards() {
|
|
2342
2957
|
if (!existsSync5(SHARDS_DIR)) return [];
|
|
@@ -2413,7 +3028,23 @@ async function ensureShardSchema(client) {
|
|
|
2413
3028
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2414
3029
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2415
3030
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2416
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3031
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3032
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3033
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3034
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3035
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3036
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3037
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3038
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3039
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3040
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3041
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3042
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3043
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3044
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3045
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3046
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3047
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2417
3048
|
]) {
|
|
2418
3049
|
try {
|
|
2419
3050
|
await client.execute(col);
|
|
@@ -2525,7 +3156,7 @@ var init_shard_manager = __esm({
|
|
|
2525
3156
|
"src/lib/shard-manager.ts"() {
|
|
2526
3157
|
"use strict";
|
|
2527
3158
|
init_config();
|
|
2528
|
-
SHARDS_DIR =
|
|
3159
|
+
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
2529
3160
|
_shards = /* @__PURE__ */ new Map();
|
|
2530
3161
|
_encryptionKey = null;
|
|
2531
3162
|
_shardingEnabled = false;
|
|
@@ -2633,10 +3264,10 @@ __export(session_registry_exports, {
|
|
|
2633
3264
|
});
|
|
2634
3265
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
|
|
2635
3266
|
import { execSync as execSync2 } from "child_process";
|
|
2636
|
-
import
|
|
2637
|
-
import
|
|
3267
|
+
import path7 from "path";
|
|
3268
|
+
import os6 from "os";
|
|
2638
3269
|
function registerSession(entry) {
|
|
2639
|
-
const dir =
|
|
3270
|
+
const dir = path7.dirname(REGISTRY_PATH);
|
|
2640
3271
|
if (!existsSync6(dir)) {
|
|
2641
3272
|
mkdirSync2(dir, { recursive: true });
|
|
2642
3273
|
}
|
|
@@ -2680,7 +3311,7 @@ var REGISTRY_PATH;
|
|
|
2680
3311
|
var init_session_registry = __esm({
|
|
2681
3312
|
"src/lib/session-registry.ts"() {
|
|
2682
3313
|
"use strict";
|
|
2683
|
-
REGISTRY_PATH =
|
|
3314
|
+
REGISTRY_PATH = path7.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
2684
3315
|
}
|
|
2685
3316
|
});
|
|
2686
3317
|
|
|
@@ -2961,7 +3592,7 @@ var init_runtime_table = __esm({
|
|
|
2961
3592
|
|
|
2962
3593
|
// src/lib/agent-config.ts
|
|
2963
3594
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2964
|
-
import
|
|
3595
|
+
import path8 from "path";
|
|
2965
3596
|
function loadAgentConfig() {
|
|
2966
3597
|
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
2967
3598
|
try {
|
|
@@ -2984,7 +3615,7 @@ var init_agent_config = __esm({
|
|
|
2984
3615
|
"use strict";
|
|
2985
3616
|
init_config();
|
|
2986
3617
|
init_runtime_table();
|
|
2987
|
-
AGENT_CONFIG_PATH =
|
|
3618
|
+
AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
2988
3619
|
DEFAULT_MODELS = {
|
|
2989
3620
|
claude: "claude-opus-4",
|
|
2990
3621
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -3003,10 +3634,10 @@ __export(intercom_queue_exports, {
|
|
|
3003
3634
|
readQueue: () => readQueue
|
|
3004
3635
|
});
|
|
3005
3636
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
3006
|
-
import
|
|
3007
|
-
import
|
|
3637
|
+
import path9 from "path";
|
|
3638
|
+
import os7 from "os";
|
|
3008
3639
|
function ensureDir() {
|
|
3009
|
-
const dir =
|
|
3640
|
+
const dir = path9.dirname(QUEUE_PATH);
|
|
3010
3641
|
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
3011
3642
|
}
|
|
3012
3643
|
function readQueue() {
|
|
@@ -3112,10 +3743,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3112
3743
|
var init_intercom_queue = __esm({
|
|
3113
3744
|
"src/lib/intercom-queue.ts"() {
|
|
3114
3745
|
"use strict";
|
|
3115
|
-
QUEUE_PATH =
|
|
3746
|
+
QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
3116
3747
|
MAX_RETRIES2 = 5;
|
|
3117
3748
|
TTL_MS = 60 * 60 * 1e3;
|
|
3118
|
-
INTERCOM_LOG =
|
|
3749
|
+
INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3119
3750
|
}
|
|
3120
3751
|
});
|
|
3121
3752
|
|
|
@@ -3138,7 +3769,7 @@ __export(license_exports, {
|
|
|
3138
3769
|
});
|
|
3139
3770
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
3140
3771
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3141
|
-
import
|
|
3772
|
+
import path10 from "path";
|
|
3142
3773
|
import { jwtVerify, importSPKI } from "jose";
|
|
3143
3774
|
async function fetchRetry(url, init) {
|
|
3144
3775
|
try {
|
|
@@ -3149,7 +3780,7 @@ async function fetchRetry(url, init) {
|
|
|
3149
3780
|
}
|
|
3150
3781
|
}
|
|
3151
3782
|
function loadDeviceId() {
|
|
3152
|
-
const deviceJsonPath =
|
|
3783
|
+
const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
|
|
3153
3784
|
try {
|
|
3154
3785
|
if (existsSync9(deviceJsonPath)) {
|
|
3155
3786
|
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
@@ -3314,7 +3945,7 @@ async function checkLicense() {
|
|
|
3314
3945
|
let key = loadLicense();
|
|
3315
3946
|
if (!key) {
|
|
3316
3947
|
try {
|
|
3317
|
-
const configPath =
|
|
3948
|
+
const configPath = path10.join(EXE_AI_DIR, "config.json");
|
|
3318
3949
|
if (existsSync9(configPath)) {
|
|
3319
3950
|
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3320
3951
|
const cloud = raw.cloud;
|
|
@@ -3475,9 +4106,9 @@ var init_license = __esm({
|
|
|
3475
4106
|
"src/lib/license.ts"() {
|
|
3476
4107
|
"use strict";
|
|
3477
4108
|
init_config();
|
|
3478
|
-
LICENSE_PATH =
|
|
3479
|
-
CACHE_PATH =
|
|
3480
|
-
DEVICE_ID_PATH =
|
|
4109
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
4110
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
4111
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3481
4112
|
API_BASE = "https://askexe.com/cloud";
|
|
3482
4113
|
RETRY_DELAY_MS = 500;
|
|
3483
4114
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -3508,7 +4139,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
3508
4139
|
|
|
3509
4140
|
// src/lib/plan-limits.ts
|
|
3510
4141
|
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3511
|
-
import
|
|
4142
|
+
import path11 from "path";
|
|
3512
4143
|
function getLicenseSync() {
|
|
3513
4144
|
try {
|
|
3514
4145
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -3580,14 +4211,14 @@ var init_plan_limits = __esm({
|
|
|
3580
4211
|
this.name = "PlanLimitError";
|
|
3581
4212
|
}
|
|
3582
4213
|
};
|
|
3583
|
-
CACHE_PATH2 =
|
|
4214
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
3584
4215
|
}
|
|
3585
4216
|
});
|
|
3586
4217
|
|
|
3587
4218
|
// src/lib/notifications.ts
|
|
3588
4219
|
import crypto from "crypto";
|
|
3589
|
-
import
|
|
3590
|
-
import
|
|
4220
|
+
import path12 from "path";
|
|
4221
|
+
import os8 from "os";
|
|
3591
4222
|
import {
|
|
3592
4223
|
readFileSync as readFileSync9,
|
|
3593
4224
|
readdirSync as readdirSync2,
|
|
@@ -3705,8 +4336,8 @@ async function markDoneTaskNotificationsAsRead() {
|
|
|
3705
4336
|
}
|
|
3706
4337
|
}
|
|
3707
4338
|
async function migrateJsonNotifications() {
|
|
3708
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
3709
|
-
const notifDir =
|
|
4339
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path12.join(os8.homedir(), ".exe-os");
|
|
4340
|
+
const notifDir = path12.join(base, "notifications");
|
|
3710
4341
|
if (!existsSync11(notifDir)) return 0;
|
|
3711
4342
|
let migrated = 0;
|
|
3712
4343
|
try {
|
|
@@ -3715,7 +4346,7 @@ async function migrateJsonNotifications() {
|
|
|
3715
4346
|
const client = getClient();
|
|
3716
4347
|
for (const file of files) {
|
|
3717
4348
|
try {
|
|
3718
|
-
const filePath =
|
|
4349
|
+
const filePath = path12.join(notifDir, file);
|
|
3719
4350
|
const data = JSON.parse(readFileSync9(filePath, "utf8"));
|
|
3720
4351
|
await client.execute({
|
|
3721
4352
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
@@ -3863,8 +4494,8 @@ var init_session_kill_telemetry = __esm({
|
|
|
3863
4494
|
|
|
3864
4495
|
// src/lib/tasks-crud.ts
|
|
3865
4496
|
import crypto3 from "crypto";
|
|
3866
|
-
import
|
|
3867
|
-
import
|
|
4497
|
+
import path13 from "path";
|
|
4498
|
+
import os9 from "os";
|
|
3868
4499
|
import { execSync as execSync5 } from "child_process";
|
|
3869
4500
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3870
4501
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
@@ -4042,8 +4673,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4042
4673
|
}
|
|
4043
4674
|
if (input.baseDir) {
|
|
4044
4675
|
try {
|
|
4045
|
-
await mkdir4(
|
|
4046
|
-
await mkdir4(
|
|
4676
|
+
await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4677
|
+
await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4047
4678
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4048
4679
|
await ensureGitignoreExe(input.baseDir);
|
|
4049
4680
|
} catch {
|
|
@@ -4079,9 +4710,9 @@ ${laneWarning}` : laneWarning;
|
|
|
4079
4710
|
});
|
|
4080
4711
|
if (input.baseDir) {
|
|
4081
4712
|
try {
|
|
4082
|
-
const EXE_OS_DIR =
|
|
4083
|
-
const mdPath =
|
|
4084
|
-
const mdDir =
|
|
4713
|
+
const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
|
|
4714
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
4715
|
+
const mdDir = path13.dirname(mdPath);
|
|
4085
4716
|
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4086
4717
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4087
4718
|
const mdContent = `# ${input.title}
|
|
@@ -4382,7 +5013,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4382
5013
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4383
5014
|
}
|
|
4384
5015
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4385
|
-
const archPath =
|
|
5016
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4386
5017
|
try {
|
|
4387
5018
|
if (existsSync12(archPath)) return;
|
|
4388
5019
|
const template = [
|
|
@@ -4417,7 +5048,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4417
5048
|
}
|
|
4418
5049
|
}
|
|
4419
5050
|
async function ensureGitignoreExe(baseDir) {
|
|
4420
|
-
const gitignorePath =
|
|
5051
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
4421
5052
|
try {
|
|
4422
5053
|
if (existsSync12(gitignorePath)) {
|
|
4423
5054
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -4451,13 +5082,13 @@ var init_tasks_crud = __esm({
|
|
|
4451
5082
|
});
|
|
4452
5083
|
|
|
4453
5084
|
// src/lib/tasks-review.ts
|
|
4454
|
-
import
|
|
5085
|
+
import path14 from "path";
|
|
4455
5086
|
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
4456
5087
|
async function countPendingReviews(sessionScope) {
|
|
4457
5088
|
const client = getClient();
|
|
4458
5089
|
if (sessionScope) {
|
|
4459
5090
|
const result2 = await client.execute({
|
|
4460
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
5091
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
4461
5092
|
args: [sessionScope]
|
|
4462
5093
|
});
|
|
4463
5094
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -4633,11 +5264,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4633
5264
|
);
|
|
4634
5265
|
}
|
|
4635
5266
|
try {
|
|
4636
|
-
const cacheDir =
|
|
5267
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4637
5268
|
if (existsSync13(cacheDir)) {
|
|
4638
5269
|
for (const f of readdirSync3(cacheDir)) {
|
|
4639
5270
|
if (f.startsWith("review-notified-")) {
|
|
4640
|
-
unlinkSync4(
|
|
5271
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4641
5272
|
}
|
|
4642
5273
|
}
|
|
4643
5274
|
}
|
|
@@ -4658,7 +5289,7 @@ var init_tasks_review = __esm({
|
|
|
4658
5289
|
});
|
|
4659
5290
|
|
|
4660
5291
|
// src/lib/tasks-chain.ts
|
|
4661
|
-
import
|
|
5292
|
+
import path15 from "path";
|
|
4662
5293
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
4663
5294
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4664
5295
|
const client = getClient();
|
|
@@ -4675,7 +5306,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4675
5306
|
});
|
|
4676
5307
|
for (const ur of unblockedRows.rows) {
|
|
4677
5308
|
try {
|
|
4678
|
-
const ubFile =
|
|
5309
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4679
5310
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
4680
5311
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4681
5312
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4749,7 +5380,7 @@ __export(project_name_exports, {
|
|
|
4749
5380
|
getProjectName: () => getProjectName
|
|
4750
5381
|
});
|
|
4751
5382
|
import { execSync as execSync6 } from "child_process";
|
|
4752
|
-
import
|
|
5383
|
+
import path16 from "path";
|
|
4753
5384
|
function getProjectName(cwd) {
|
|
4754
5385
|
const dir = cwd ?? process.cwd();
|
|
4755
5386
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4762,7 +5393,7 @@ function getProjectName(cwd) {
|
|
|
4762
5393
|
timeout: 2e3,
|
|
4763
5394
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4764
5395
|
}).trim();
|
|
4765
|
-
repoRoot =
|
|
5396
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4766
5397
|
} catch {
|
|
4767
5398
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4768
5399
|
cwd: dir,
|
|
@@ -4771,11 +5402,11 @@ function getProjectName(cwd) {
|
|
|
4771
5402
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4772
5403
|
}).trim();
|
|
4773
5404
|
}
|
|
4774
|
-
_cached2 =
|
|
5405
|
+
_cached2 = path16.basename(repoRoot);
|
|
4775
5406
|
_cachedCwd = dir;
|
|
4776
5407
|
return _cached2;
|
|
4777
5408
|
} catch {
|
|
4778
|
-
_cached2 =
|
|
5409
|
+
_cached2 = path16.basename(dir);
|
|
4779
5410
|
_cachedCwd = dir;
|
|
4780
5411
|
return _cached2;
|
|
4781
5412
|
}
|
|
@@ -5252,7 +5883,7 @@ __export(tasks_exports, {
|
|
|
5252
5883
|
updateTaskStatus: () => updateTaskStatus,
|
|
5253
5884
|
writeCheckpoint: () => writeCheckpoint
|
|
5254
5885
|
});
|
|
5255
|
-
import
|
|
5886
|
+
import path17 from "path";
|
|
5256
5887
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5257
5888
|
async function createTask(input) {
|
|
5258
5889
|
const result = await createTaskCore(input);
|
|
@@ -5272,8 +5903,8 @@ async function updateTask(input) {
|
|
|
5272
5903
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5273
5904
|
try {
|
|
5274
5905
|
const agent = String(row.assigned_to);
|
|
5275
|
-
const cacheDir =
|
|
5276
|
-
const cachePath =
|
|
5906
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
5907
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
5277
5908
|
if (input.status === "in_progress") {
|
|
5278
5909
|
mkdirSync6(cacheDir, { recursive: true });
|
|
5279
5910
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -5744,12 +6375,12 @@ __export(tmux_routing_exports, {
|
|
|
5744
6375
|
});
|
|
5745
6376
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5746
6377
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
5747
|
-
import
|
|
5748
|
-
import
|
|
6378
|
+
import path18 from "path";
|
|
6379
|
+
import os10 from "os";
|
|
5749
6380
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5750
6381
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5751
6382
|
function spawnLockPath(sessionName) {
|
|
5752
|
-
return
|
|
6383
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5753
6384
|
}
|
|
5754
6385
|
function isProcessAlive(pid) {
|
|
5755
6386
|
try {
|
|
@@ -5786,8 +6417,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5786
6417
|
function resolveBehaviorsExporterScript() {
|
|
5787
6418
|
try {
|
|
5788
6419
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5789
|
-
const scriptPath =
|
|
5790
|
-
|
|
6420
|
+
const scriptPath = path18.join(
|
|
6421
|
+
path18.dirname(thisFile),
|
|
5791
6422
|
"..",
|
|
5792
6423
|
"bin",
|
|
5793
6424
|
"exe-export-behaviors.js"
|
|
@@ -5862,7 +6493,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5862
6493
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5863
6494
|
}
|
|
5864
6495
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5865
|
-
const filePath =
|
|
6496
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5866
6497
|
writeFileSync7(filePath, JSON.stringify({
|
|
5867
6498
|
parentExe: rootExe,
|
|
5868
6499
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -5871,7 +6502,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5871
6502
|
}
|
|
5872
6503
|
function getParentExe(sessionKey) {
|
|
5873
6504
|
try {
|
|
5874
|
-
const data = JSON.parse(readFileSync11(
|
|
6505
|
+
const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5875
6506
|
return data.parentExe || null;
|
|
5876
6507
|
} catch {
|
|
5877
6508
|
return null;
|
|
@@ -5880,7 +6511,7 @@ function getParentExe(sessionKey) {
|
|
|
5880
6511
|
function getDispatchedBy(sessionKey) {
|
|
5881
6512
|
try {
|
|
5882
6513
|
const data = JSON.parse(readFileSync11(
|
|
5883
|
-
|
|
6514
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5884
6515
|
"utf8"
|
|
5885
6516
|
));
|
|
5886
6517
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6066,7 +6697,7 @@ function sendIntercom(targetSession) {
|
|
|
6066
6697
|
try {
|
|
6067
6698
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6068
6699
|
const agent = baseAgentName(rawAgent);
|
|
6069
|
-
const markerPath =
|
|
6700
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6070
6701
|
if (existsSync14(markerPath)) {
|
|
6071
6702
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6072
6703
|
return "debounced";
|
|
@@ -6076,7 +6707,7 @@ function sendIntercom(targetSession) {
|
|
|
6076
6707
|
try {
|
|
6077
6708
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6078
6709
|
const agent = baseAgentName(rawAgent);
|
|
6079
|
-
const taskDir =
|
|
6710
|
+
const taskDir = path18.join(process.cwd(), "exe", agent);
|
|
6080
6711
|
if (existsSync14(taskDir)) {
|
|
6081
6712
|
const files = readdirSync4(taskDir).filter(
|
|
6082
6713
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -6210,8 +6841,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6210
6841
|
const transport = getTransport();
|
|
6211
6842
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6212
6843
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6213
|
-
const logDir =
|
|
6214
|
-
const logFile =
|
|
6844
|
+
const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
|
|
6845
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6215
6846
|
if (!existsSync14(logDir)) {
|
|
6216
6847
|
mkdirSync7(logDir, { recursive: true });
|
|
6217
6848
|
}
|
|
@@ -6219,14 +6850,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6219
6850
|
let cleanupSuffix = "";
|
|
6220
6851
|
try {
|
|
6221
6852
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6222
|
-
const cleanupScript =
|
|
6853
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6223
6854
|
if (existsSync14(cleanupScript)) {
|
|
6224
6855
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6225
6856
|
}
|
|
6226
6857
|
} catch {
|
|
6227
6858
|
}
|
|
6228
6859
|
try {
|
|
6229
|
-
const claudeJsonPath =
|
|
6860
|
+
const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
|
|
6230
6861
|
let claudeJson = {};
|
|
6231
6862
|
try {
|
|
6232
6863
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -6241,10 +6872,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6241
6872
|
} catch {
|
|
6242
6873
|
}
|
|
6243
6874
|
try {
|
|
6244
|
-
const settingsDir =
|
|
6875
|
+
const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
|
|
6245
6876
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6246
|
-
const projSettingsDir =
|
|
6247
|
-
const settingsPath =
|
|
6877
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
6878
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
6248
6879
|
let settings = {};
|
|
6249
6880
|
try {
|
|
6250
6881
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -6291,8 +6922,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6291
6922
|
let behaviorsFlag = "";
|
|
6292
6923
|
let legacyFallbackWarned = false;
|
|
6293
6924
|
if (!useExeAgent && !useBinSymlink) {
|
|
6294
|
-
const identityPath =
|
|
6295
|
-
|
|
6925
|
+
const identityPath = path18.join(
|
|
6926
|
+
os10.homedir(),
|
|
6296
6927
|
".exe-os",
|
|
6297
6928
|
"identity",
|
|
6298
6929
|
`${employeeName}.md`
|
|
@@ -6307,7 +6938,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6307
6938
|
}
|
|
6308
6939
|
const behaviorsFile = exportBehaviorsSync(
|
|
6309
6940
|
employeeName,
|
|
6310
|
-
|
|
6941
|
+
path18.basename(spawnCwd),
|
|
6311
6942
|
sessionName
|
|
6312
6943
|
);
|
|
6313
6944
|
if (behaviorsFile) {
|
|
@@ -6322,9 +6953,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6322
6953
|
}
|
|
6323
6954
|
let sessionContextFlag = "";
|
|
6324
6955
|
try {
|
|
6325
|
-
const ctxDir =
|
|
6956
|
+
const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6326
6957
|
mkdirSync7(ctxDir, { recursive: true });
|
|
6327
|
-
const ctxFile =
|
|
6958
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6328
6959
|
const ctxContent = [
|
|
6329
6960
|
`## Session Context`,
|
|
6330
6961
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -6408,7 +7039,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6408
7039
|
transport.pipeLog(sessionName, logFile);
|
|
6409
7040
|
try {
|
|
6410
7041
|
const mySession = getMySession();
|
|
6411
|
-
const dispatchInfo =
|
|
7042
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6412
7043
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
6413
7044
|
dispatchedBy: mySession,
|
|
6414
7045
|
rootExe: exeSession,
|
|
@@ -6483,15 +7114,15 @@ var init_tmux_routing = __esm({
|
|
|
6483
7114
|
init_intercom_queue();
|
|
6484
7115
|
init_plan_limits();
|
|
6485
7116
|
init_employees();
|
|
6486
|
-
SPAWN_LOCK_DIR =
|
|
6487
|
-
SESSION_CACHE =
|
|
7117
|
+
SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
7118
|
+
SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6488
7119
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6489
7120
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6490
7121
|
VERIFY_PANE_LINES = 200;
|
|
6491
7122
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6492
7123
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
6493
|
-
INTERCOM_LOG2 =
|
|
6494
|
-
DEBOUNCE_FILE =
|
|
7124
|
+
INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
7125
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6495
7126
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6496
7127
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
6497
7128
|
}
|
|
@@ -6549,12 +7180,12 @@ __export(worker_gate_exports, {
|
|
|
6549
7180
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
6550
7181
|
});
|
|
6551
7182
|
import { readdirSync as readdirSync6, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync15 } from "fs";
|
|
6552
|
-
import
|
|
7183
|
+
import path20 from "path";
|
|
6553
7184
|
function tryAcquireWorkerSlot() {
|
|
6554
7185
|
try {
|
|
6555
7186
|
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6556
7187
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
6557
|
-
const reservationPath =
|
|
7188
|
+
const reservationPath = path20.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
6558
7189
|
writeFileSync9(reservationPath, String(process.pid));
|
|
6559
7190
|
const files = readdirSync6(WORKER_PID_DIR);
|
|
6560
7191
|
let alive = 0;
|
|
@@ -6572,7 +7203,7 @@ function tryAcquireWorkerSlot() {
|
|
|
6572
7203
|
alive++;
|
|
6573
7204
|
} catch {
|
|
6574
7205
|
try {
|
|
6575
|
-
unlinkSync8(
|
|
7206
|
+
unlinkSync8(path20.join(WORKER_PID_DIR, f));
|
|
6576
7207
|
} catch {
|
|
6577
7208
|
}
|
|
6578
7209
|
}
|
|
@@ -6596,13 +7227,13 @@ function tryAcquireWorkerSlot() {
|
|
|
6596
7227
|
function registerWorkerPid(pid) {
|
|
6597
7228
|
try {
|
|
6598
7229
|
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6599
|
-
writeFileSync9(
|
|
7230
|
+
writeFileSync9(path20.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
6600
7231
|
} catch {
|
|
6601
7232
|
}
|
|
6602
7233
|
}
|
|
6603
7234
|
function cleanupWorkerPid() {
|
|
6604
7235
|
try {
|
|
6605
|
-
unlinkSync8(
|
|
7236
|
+
unlinkSync8(path20.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
6606
7237
|
} catch {
|
|
6607
7238
|
}
|
|
6608
7239
|
}
|
|
@@ -6642,9 +7273,9 @@ var init_worker_gate = __esm({
|
|
|
6642
7273
|
"src/lib/worker-gate.ts"() {
|
|
6643
7274
|
"use strict";
|
|
6644
7275
|
init_config();
|
|
6645
|
-
WORKER_PID_DIR =
|
|
7276
|
+
WORKER_PID_DIR = path20.join(EXE_AI_DIR, "worker-pids");
|
|
6646
7277
|
MAX_CONCURRENT_WORKERS = 3;
|
|
6647
|
-
BACKFILL_LOCK =
|
|
7278
|
+
BACKFILL_LOCK = path20.join(WORKER_PID_DIR, "backfill.lock");
|
|
6648
7279
|
}
|
|
6649
7280
|
});
|
|
6650
7281
|
|
|
@@ -6747,7 +7378,7 @@ __export(crdt_sync_exports, {
|
|
|
6747
7378
|
});
|
|
6748
7379
|
import * as Y from "yjs";
|
|
6749
7380
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
|
|
6750
|
-
import
|
|
7381
|
+
import path21 from "path";
|
|
6751
7382
|
import { homedir } from "os";
|
|
6752
7383
|
function getStatePath() {
|
|
6753
7384
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -6903,7 +7534,7 @@ function persistState() {
|
|
|
6903
7534
|
if (!doc) return;
|
|
6904
7535
|
try {
|
|
6905
7536
|
const sp = getStatePath();
|
|
6906
|
-
const dir =
|
|
7537
|
+
const dir = path21.dirname(sp);
|
|
6907
7538
|
if (!existsSync16(dir)) mkdirSync10(dir, { recursive: true });
|
|
6908
7539
|
const state = Y.encodeStateAsUpdate(doc);
|
|
6909
7540
|
writeFileSync10(sp, Buffer.from(state));
|
|
@@ -6947,7 +7578,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
6947
7578
|
var init_crdt_sync = __esm({
|
|
6948
7579
|
"src/lib/crdt-sync.ts"() {
|
|
6949
7580
|
"use strict";
|
|
6950
|
-
DEFAULT_STATE_PATH =
|
|
7581
|
+
DEFAULT_STATE_PATH = path21.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
6951
7582
|
_statePathOverride = null;
|
|
6952
7583
|
doc = null;
|
|
6953
7584
|
}
|
|
@@ -6983,14 +7614,14 @@ __export(cloud_sync_exports, {
|
|
|
6983
7614
|
});
|
|
6984
7615
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync17, readdirSync as readdirSync7, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
6985
7616
|
import crypto7 from "crypto";
|
|
6986
|
-
import
|
|
7617
|
+
import path22 from "path";
|
|
6987
7618
|
import { homedir as homedir2 } from "os";
|
|
6988
7619
|
function sqlSafe(v) {
|
|
6989
7620
|
return v === void 0 ? null : v;
|
|
6990
7621
|
}
|
|
6991
7622
|
function logError(msg) {
|
|
6992
7623
|
try {
|
|
6993
|
-
const logPath =
|
|
7624
|
+
const logPath = path22.join(homedir2(), ".exe-os", "workers.log");
|
|
6994
7625
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
6995
7626
|
`);
|
|
6996
7627
|
} catch {
|
|
@@ -7385,7 +8016,7 @@ async function cloudSync(config) {
|
|
|
7385
8016
|
try {
|
|
7386
8017
|
const employees = await loadEmployees();
|
|
7387
8018
|
rosterResult.employees = employees.length;
|
|
7388
|
-
const idDir =
|
|
8019
|
+
const idDir = path22.join(EXE_AI_DIR, "identity");
|
|
7389
8020
|
if (existsSync17(idDir)) {
|
|
7390
8021
|
rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
|
|
7391
8022
|
}
|
|
@@ -7426,9 +8057,9 @@ function consumeRosterDeletions() {
|
|
|
7426
8057
|
}
|
|
7427
8058
|
}
|
|
7428
8059
|
function buildRosterBlob(paths) {
|
|
7429
|
-
const rosterPath = paths?.rosterPath ??
|
|
7430
|
-
const identityDir = paths?.identityDir ??
|
|
7431
|
-
const configPath = paths?.configPath ??
|
|
8060
|
+
const rosterPath = paths?.rosterPath ?? path22.join(EXE_AI_DIR, "exe-employees.json");
|
|
8061
|
+
const identityDir = paths?.identityDir ?? path22.join(EXE_AI_DIR, "identity");
|
|
8062
|
+
const configPath = paths?.configPath ?? path22.join(EXE_AI_DIR, "config.json");
|
|
7432
8063
|
let roster = [];
|
|
7433
8064
|
if (existsSync17(rosterPath)) {
|
|
7434
8065
|
try {
|
|
@@ -7440,7 +8071,7 @@ function buildRosterBlob(paths) {
|
|
|
7440
8071
|
if (existsSync17(identityDir)) {
|
|
7441
8072
|
for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
7442
8073
|
try {
|
|
7443
|
-
identities[file] = readFileSync14(
|
|
8074
|
+
identities[file] = readFileSync14(path22.join(identityDir, file), "utf-8");
|
|
7444
8075
|
} catch {
|
|
7445
8076
|
}
|
|
7446
8077
|
}
|
|
@@ -7453,7 +8084,7 @@ function buildRosterBlob(paths) {
|
|
|
7453
8084
|
}
|
|
7454
8085
|
}
|
|
7455
8086
|
let agentConfig;
|
|
7456
|
-
const agentConfigPath =
|
|
8087
|
+
const agentConfigPath = path22.join(EXE_AI_DIR, "agent-config.json");
|
|
7457
8088
|
if (existsSync17(agentConfigPath)) {
|
|
7458
8089
|
try {
|
|
7459
8090
|
agentConfig = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
|
|
@@ -7532,7 +8163,7 @@ async function cloudPullRoster(config) {
|
|
|
7532
8163
|
}
|
|
7533
8164
|
}
|
|
7534
8165
|
function mergeConfig(remoteConfig, configPath) {
|
|
7535
|
-
const cfgPath = configPath ??
|
|
8166
|
+
const cfgPath = configPath ?? path22.join(EXE_AI_DIR, "config.json");
|
|
7536
8167
|
let local = {};
|
|
7537
8168
|
if (existsSync17(cfgPath)) {
|
|
7538
8169
|
try {
|
|
@@ -7541,14 +8172,14 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
7541
8172
|
}
|
|
7542
8173
|
}
|
|
7543
8174
|
const merged = { ...remoteConfig, ...local };
|
|
7544
|
-
const dir =
|
|
8175
|
+
const dir = path22.dirname(cfgPath);
|
|
7545
8176
|
if (!existsSync17(dir)) mkdirSync11(dir, { recursive: true });
|
|
7546
8177
|
writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
7547
8178
|
}
|
|
7548
8179
|
async function mergeRosterFromRemote(remote, paths) {
|
|
7549
8180
|
return withRosterLock(async () => {
|
|
7550
8181
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
7551
|
-
const identityDir = paths?.identityDir ??
|
|
8182
|
+
const identityDir = paths?.identityDir ?? path22.join(EXE_AI_DIR, "identity");
|
|
7552
8183
|
const localEmployees = await loadEmployees(rosterPath);
|
|
7553
8184
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
7554
8185
|
let added = 0;
|
|
@@ -7570,7 +8201,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
7570
8201
|
const remoteIdentity = remote.identities[matchedKey];
|
|
7571
8202
|
if (remoteIdentity) {
|
|
7572
8203
|
if (!existsSync17(identityDir)) mkdirSync11(identityDir, { recursive: true });
|
|
7573
|
-
const idPath =
|
|
8204
|
+
const idPath = path22.join(identityDir, `${remoteEmp.name}.md`);
|
|
7574
8205
|
let localIdentity = null;
|
|
7575
8206
|
try {
|
|
7576
8207
|
localIdentity = existsSync17(idPath) ? readFileSync14(idPath, "utf-8") : null;
|
|
@@ -7603,7 +8234,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
7603
8234
|
}
|
|
7604
8235
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
7605
8236
|
try {
|
|
7606
|
-
const agentConfigPath =
|
|
8237
|
+
const agentConfigPath = path22.join(EXE_AI_DIR, "agent-config.json");
|
|
7607
8238
|
let local = {};
|
|
7608
8239
|
if (existsSync17(agentConfigPath)) {
|
|
7609
8240
|
try {
|
|
@@ -8050,9 +8681,9 @@ var init_cloud_sync = __esm({
|
|
|
8050
8681
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
8051
8682
|
FETCH_TIMEOUT_MS = 3e4;
|
|
8052
8683
|
PUSH_BATCH_SIZE = 5e3;
|
|
8053
|
-
ROSTER_LOCK_PATH =
|
|
8684
|
+
ROSTER_LOCK_PATH = path22.join(EXE_AI_DIR, "roster-merge.lock");
|
|
8054
8685
|
LOCK_STALE_MS = 3e4;
|
|
8055
|
-
ROSTER_DELETIONS_PATH =
|
|
8686
|
+
ROSTER_DELETIONS_PATH = path22.join(EXE_AI_DIR, "roster-deletions.json");
|
|
8056
8687
|
}
|
|
8057
8688
|
});
|
|
8058
8689
|
|
|
@@ -8234,10 +8865,10 @@ var init_schedules = __esm({
|
|
|
8234
8865
|
|
|
8235
8866
|
// src/bin/exe-boot.ts
|
|
8236
8867
|
init_employees();
|
|
8237
|
-
import
|
|
8868
|
+
import path23 from "path";
|
|
8238
8869
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
8239
8870
|
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
|
|
8240
|
-
import
|
|
8871
|
+
import os11 from "os";
|
|
8241
8872
|
|
|
8242
8873
|
// src/lib/employee-templates.ts
|
|
8243
8874
|
init_global_procedures();
|
|
@@ -8741,11 +9372,11 @@ init_session_key();
|
|
|
8741
9372
|
init_employees();
|
|
8742
9373
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
|
|
8743
9374
|
import { execSync as execSync8 } from "child_process";
|
|
8744
|
-
import
|
|
8745
|
-
var CACHE_DIR =
|
|
9375
|
+
import path19 from "path";
|
|
9376
|
+
var CACHE_DIR = path19.join(EXE_AI_DIR, "session-cache");
|
|
8746
9377
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
8747
9378
|
function getMarkerPath() {
|
|
8748
|
-
return
|
|
9379
|
+
return path19.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
8749
9380
|
}
|
|
8750
9381
|
function writeActiveAgent(agentId, agentRole) {
|
|
8751
9382
|
try {
|
|
@@ -8760,11 +9391,11 @@ function writeActiveAgent(agentId, agentRole) {
|
|
|
8760
9391
|
function cleanupSessionMarkers() {
|
|
8761
9392
|
const key = getSessionKey();
|
|
8762
9393
|
try {
|
|
8763
|
-
unlinkSync7(
|
|
9394
|
+
unlinkSync7(path19.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
8764
9395
|
} catch {
|
|
8765
9396
|
}
|
|
8766
9397
|
try {
|
|
8767
|
-
unlinkSync7(
|
|
9398
|
+
unlinkSync7(path19.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
8768
9399
|
} catch {
|
|
8769
9400
|
}
|
|
8770
9401
|
}
|
|
@@ -8854,7 +9485,7 @@ async function boot(options) {
|
|
|
8854
9485
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
8855
9486
|
for (const dir of employeeDirs) {
|
|
8856
9487
|
const employee = dir.name;
|
|
8857
|
-
const taskDir =
|
|
9488
|
+
const taskDir = path23.join(exeDir, employee);
|
|
8858
9489
|
let files;
|
|
8859
9490
|
try {
|
|
8860
9491
|
files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
|
|
@@ -8865,7 +9496,7 @@ async function boot(options) {
|
|
|
8865
9496
|
const taskFilePath = `exe/${employee}/${file}`;
|
|
8866
9497
|
let content;
|
|
8867
9498
|
try {
|
|
8868
|
-
content = readFs(
|
|
9499
|
+
content = readFs(path23.join(taskDir, file), "utf8");
|
|
8869
9500
|
} catch {
|
|
8870
9501
|
continue;
|
|
8871
9502
|
}
|
|
@@ -8951,12 +9582,12 @@ async function boot(options) {
|
|
|
8951
9582
|
}
|
|
8952
9583
|
try {
|
|
8953
9584
|
for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
|
|
8954
|
-
const reviewDir =
|
|
9585
|
+
const reviewDir = path23.join(process.cwd(), "exe", reviewDirName);
|
|
8955
9586
|
if (existsSync18(reviewDir)) {
|
|
8956
9587
|
for (const f of readdirSync8(reviewDir)) {
|
|
8957
9588
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
8958
9589
|
try {
|
|
8959
|
-
unlinkSync11(
|
|
9590
|
+
unlinkSync11(path23.join(reviewDir, f));
|
|
8960
9591
|
} catch {
|
|
8961
9592
|
}
|
|
8962
9593
|
}
|
|
@@ -9002,7 +9633,7 @@ async function boot(options) {
|
|
|
9002
9633
|
});
|
|
9003
9634
|
const taskFile = String(r.task_file);
|
|
9004
9635
|
try {
|
|
9005
|
-
const filePath =
|
|
9636
|
+
const filePath = path23.join(process.cwd(), taskFile);
|
|
9006
9637
|
if (existsSync18(filePath)) {
|
|
9007
9638
|
let content = readFileSync15(filePath, "utf8");
|
|
9008
9639
|
content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
|
|
@@ -9501,8 +10132,8 @@ async function boot(options) {
|
|
|
9501
10132
|
})()
|
|
9502
10133
|
]);
|
|
9503
10134
|
try {
|
|
9504
|
-
const configPath =
|
|
9505
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
10135
|
+
const configPath = path23.join(
|
|
10136
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path23.join(os11.homedir(), ".exe-os"),
|
|
9506
10137
|
"config.json"
|
|
9507
10138
|
);
|
|
9508
10139
|
if (existsSync18(configPath)) {
|
|
@@ -9512,7 +10143,7 @@ async function boot(options) {
|
|
|
9512
10143
|
} catch {
|
|
9513
10144
|
}
|
|
9514
10145
|
try {
|
|
9515
|
-
const backfillFlagPath =
|
|
10146
|
+
const backfillFlagPath = path23.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
9516
10147
|
const isBackfillNeeded = () => existsSync18(backfillFlagPath);
|
|
9517
10148
|
const coverageResult = await client.execute({
|
|
9518
10149
|
sql: `SELECT COUNT(*) as total,
|
|
@@ -9535,7 +10166,7 @@ async function boot(options) {
|
|
|
9535
10166
|
let daemonRunning = false;
|
|
9536
10167
|
let daemonUptime;
|
|
9537
10168
|
let daemonRequestsServed;
|
|
9538
|
-
const socketPath =
|
|
10169
|
+
const socketPath = path23.join(EXE_AI_DIR, "exed.sock");
|
|
9539
10170
|
if (existsSync18(socketPath)) {
|
|
9540
10171
|
try {
|
|
9541
10172
|
const net2 = await import("net");
|
|
@@ -9578,7 +10209,7 @@ async function boot(options) {
|
|
|
9578
10209
|
}
|
|
9579
10210
|
}
|
|
9580
10211
|
if (!daemonRunning) {
|
|
9581
|
-
const pidPath =
|
|
10212
|
+
const pidPath = path23.join(EXE_AI_DIR, "exed.pid");
|
|
9582
10213
|
if (existsSync18(pidPath)) {
|
|
9583
10214
|
try {
|
|
9584
10215
|
const pid = parseInt(readFileSync15(pidPath, "utf8").trim(), 10);
|
|
@@ -9592,7 +10223,7 @@ async function boot(options) {
|
|
|
9592
10223
|
}
|
|
9593
10224
|
if (nullCount === 0) {
|
|
9594
10225
|
try {
|
|
9595
|
-
const flagPath =
|
|
10226
|
+
const flagPath = path23.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
9596
10227
|
if (existsSync18(flagPath)) {
|
|
9597
10228
|
const { unlinkSync: unlinkSync12 } = await import("fs");
|
|
9598
10229
|
unlinkSync12(flagPath);
|
|
@@ -9619,10 +10250,10 @@ async function boot(options) {
|
|
|
9619
10250
|
const { spawn: spawn2 } = await import("child_process");
|
|
9620
10251
|
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
9621
10252
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
9622
|
-
const backfillPath =
|
|
10253
|
+
const backfillPath = path23.resolve(path23.dirname(thisFile), "backfill-vectors.js");
|
|
9623
10254
|
if (existsSync18(backfillPath)) {
|
|
9624
10255
|
const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
|
|
9625
|
-
const workerLogPath =
|
|
10256
|
+
const workerLogPath = path23.join(EXE_AI_DIR, "workers.log");
|
|
9626
10257
|
let stderrFd = "ignore";
|
|
9627
10258
|
try {
|
|
9628
10259
|
stderrFd = openSync3(workerLogPath, "a");
|
|
@@ -9652,7 +10283,7 @@ async function boot(options) {
|
|
|
9652
10283
|
const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
|
|
9653
10284
|
const missing = [];
|
|
9654
10285
|
for (const bin of criticalBinaries) {
|
|
9655
|
-
const binPath =
|
|
10286
|
+
const binPath = path23.resolve(path23.dirname(thisFile), bin);
|
|
9656
10287
|
if (!existsSync18(binPath)) {
|
|
9657
10288
|
missing.push(`dist/bin/${bin}`);
|
|
9658
10289
|
}
|
|
@@ -9682,7 +10313,7 @@ async function boot(options) {
|
|
|
9682
10313
|
console.log(brief);
|
|
9683
10314
|
return;
|
|
9684
10315
|
}
|
|
9685
|
-
const sessionDir =
|
|
10316
|
+
const sessionDir = path23.join(EXE_AI_DIR, "sessions", coordinatorName);
|
|
9686
10317
|
await mkdir5(sessionDir, { recursive: true });
|
|
9687
10318
|
const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
|
|
9688
10319
|
|
|
@@ -9691,7 +10322,7 @@ async function boot(options) {
|
|
|
9691
10322
|
# Status Brief
|
|
9692
10323
|
|
|
9693
10324
|
${brief}`;
|
|
9694
|
-
await writeFile6(
|
|
10325
|
+
await writeFile6(path23.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
|
|
9695
10326
|
const unread = await readUnreadNotifications();
|
|
9696
10327
|
if (unread.length > 0) {
|
|
9697
10328
|
console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
|
|
@@ -9700,8 +10331,8 @@ ${brief}`;
|
|
|
9700
10331
|
await cleanupOldNotifications();
|
|
9701
10332
|
console.log(brief);
|
|
9702
10333
|
try {
|
|
9703
|
-
const configPath2 =
|
|
9704
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
10334
|
+
const configPath2 = path23.join(
|
|
10335
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path23.join(os11.homedir(), ".exe-os"),
|
|
9705
10336
|
"config.json"
|
|
9706
10337
|
);
|
|
9707
10338
|
if (existsSync18(configPath2)) {
|