@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
|
@@ -388,7 +388,7 @@ function isMultiInstance(agentName2, employees) {
|
|
|
388
388
|
if (!emp) return false;
|
|
389
389
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
390
390
|
}
|
|
391
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
391
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
392
392
|
var init_employees = __esm({
|
|
393
393
|
"src/lib/employees.ts"() {
|
|
394
394
|
"use strict";
|
|
@@ -397,16 +397,601 @@ var init_employees = __esm({
|
|
|
397
397
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
398
398
|
COORDINATOR_ROLE = "COO";
|
|
399
399
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
400
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// src/lib/database-adapter.ts
|
|
405
|
+
import os3 from "os";
|
|
406
|
+
import path3 from "path";
|
|
407
|
+
import { createRequire } from "module";
|
|
408
|
+
import { pathToFileURL } from "url";
|
|
409
|
+
function quotedIdentifier(identifier) {
|
|
410
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
411
|
+
}
|
|
412
|
+
function unqualifiedTableName(name) {
|
|
413
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
414
|
+
const parts = raw.split(".");
|
|
415
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
416
|
+
}
|
|
417
|
+
function stripTrailingSemicolon(sql) {
|
|
418
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
419
|
+
}
|
|
420
|
+
function appendClause(sql, clause) {
|
|
421
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
422
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
423
|
+
if (!returningMatch) {
|
|
424
|
+
return `${trimmed}${clause}`;
|
|
425
|
+
}
|
|
426
|
+
const idx = returningMatch.index;
|
|
427
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
428
|
+
}
|
|
429
|
+
function normalizeStatement(stmt) {
|
|
430
|
+
if (typeof stmt === "string") {
|
|
431
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
432
|
+
}
|
|
433
|
+
const sql = stmt.sql;
|
|
434
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
435
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
436
|
+
}
|
|
437
|
+
return { kind: "named", sql, args: stmt.args };
|
|
438
|
+
}
|
|
439
|
+
function rewriteBooleanLiterals(sql) {
|
|
440
|
+
let out = sql;
|
|
441
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
442
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
443
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
444
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
445
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
446
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
447
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
448
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
449
|
+
}
|
|
450
|
+
return out;
|
|
451
|
+
}
|
|
452
|
+
function rewriteInsertOrIgnore(sql) {
|
|
453
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
454
|
+
return sql;
|
|
455
|
+
}
|
|
456
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
457
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
458
|
+
}
|
|
459
|
+
function rewriteInsertOrReplace(sql) {
|
|
460
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
461
|
+
if (!match) {
|
|
462
|
+
return sql;
|
|
463
|
+
}
|
|
464
|
+
const rawTable = match[1];
|
|
465
|
+
const rawColumns = match[2];
|
|
466
|
+
const remainder = match[3];
|
|
467
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
468
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
469
|
+
if (!conflictKeys?.length) {
|
|
470
|
+
return sql;
|
|
471
|
+
}
|
|
472
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
473
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
474
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
475
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
476
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
477
|
+
}
|
|
478
|
+
function rewriteSql(sql) {
|
|
479
|
+
let out = sql;
|
|
480
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
481
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
482
|
+
out = rewriteBooleanLiterals(out);
|
|
483
|
+
out = rewriteInsertOrReplace(out);
|
|
484
|
+
out = rewriteInsertOrIgnore(out);
|
|
485
|
+
return stripTrailingSemicolon(out);
|
|
486
|
+
}
|
|
487
|
+
function toBoolean(value) {
|
|
488
|
+
if (value === null || value === void 0) return value;
|
|
489
|
+
if (typeof value === "boolean") return value;
|
|
490
|
+
if (typeof value === "number") return value !== 0;
|
|
491
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
492
|
+
if (typeof value === "string") {
|
|
493
|
+
const normalized = value.trim().toLowerCase();
|
|
494
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
495
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
496
|
+
}
|
|
497
|
+
return Boolean(value);
|
|
498
|
+
}
|
|
499
|
+
function countQuestionMarks(sql, end) {
|
|
500
|
+
let count = 0;
|
|
501
|
+
let inSingle = false;
|
|
502
|
+
let inDouble = false;
|
|
503
|
+
let inLineComment = false;
|
|
504
|
+
let inBlockComment = false;
|
|
505
|
+
for (let i = 0; i < end; i++) {
|
|
506
|
+
const ch = sql[i];
|
|
507
|
+
const next = sql[i + 1];
|
|
508
|
+
if (inLineComment) {
|
|
509
|
+
if (ch === "\n") inLineComment = false;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (inBlockComment) {
|
|
513
|
+
if (ch === "*" && next === "/") {
|
|
514
|
+
inBlockComment = false;
|
|
515
|
+
i += 1;
|
|
516
|
+
}
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
520
|
+
inLineComment = true;
|
|
521
|
+
i += 1;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
525
|
+
inBlockComment = true;
|
|
526
|
+
i += 1;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
530
|
+
inSingle = !inSingle;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
534
|
+
inDouble = !inDouble;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
538
|
+
count += 1;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return count;
|
|
542
|
+
}
|
|
543
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
544
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
545
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
546
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
547
|
+
for (const match of sql.matchAll(pattern)) {
|
|
548
|
+
const matchText = match[0];
|
|
549
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
550
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return indexes;
|
|
554
|
+
}
|
|
555
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
556
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
557
|
+
if (!match) return;
|
|
558
|
+
const rawTable = match[1];
|
|
559
|
+
const rawColumns = match[2];
|
|
560
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
561
|
+
if (!boolColumns?.size) return;
|
|
562
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
563
|
+
for (const [index, column] of columns.entries()) {
|
|
564
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
565
|
+
args[index] = toBoolean(args[index]);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
570
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
571
|
+
if (!match) return;
|
|
572
|
+
const rawTable = match[1];
|
|
573
|
+
const setClause = match[2];
|
|
574
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
575
|
+
if (!boolColumns?.size) return;
|
|
576
|
+
const assignments = setClause.split(",");
|
|
577
|
+
let placeholderIndex = 0;
|
|
578
|
+
for (const assignment of assignments) {
|
|
579
|
+
if (!assignment.includes("?")) continue;
|
|
580
|
+
placeholderIndex += 1;
|
|
581
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
582
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
583
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function coerceBooleanArgs(sql, args) {
|
|
588
|
+
const nextArgs = [...args];
|
|
589
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
590
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
591
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
592
|
+
for (const index of placeholderIndexes) {
|
|
593
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
594
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return nextArgs;
|
|
598
|
+
}
|
|
599
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
600
|
+
let out = "";
|
|
601
|
+
let placeholder = 0;
|
|
602
|
+
let inSingle = false;
|
|
603
|
+
let inDouble = false;
|
|
604
|
+
let inLineComment = false;
|
|
605
|
+
let inBlockComment = false;
|
|
606
|
+
for (let i = 0; i < sql.length; i++) {
|
|
607
|
+
const ch = sql[i];
|
|
608
|
+
const next = sql[i + 1];
|
|
609
|
+
if (inLineComment) {
|
|
610
|
+
out += ch;
|
|
611
|
+
if (ch === "\n") inLineComment = false;
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (inBlockComment) {
|
|
615
|
+
out += ch;
|
|
616
|
+
if (ch === "*" && next === "/") {
|
|
617
|
+
out += next;
|
|
618
|
+
inBlockComment = false;
|
|
619
|
+
i += 1;
|
|
620
|
+
}
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
624
|
+
out += ch + next;
|
|
625
|
+
inLineComment = true;
|
|
626
|
+
i += 1;
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
630
|
+
out += ch + next;
|
|
631
|
+
inBlockComment = true;
|
|
632
|
+
i += 1;
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
636
|
+
inSingle = !inSingle;
|
|
637
|
+
out += ch;
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
641
|
+
inDouble = !inDouble;
|
|
642
|
+
out += ch;
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
646
|
+
placeholder += 1;
|
|
647
|
+
out += `$${placeholder}`;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
out += ch;
|
|
651
|
+
}
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
function translateStatementForPostgres(stmt) {
|
|
655
|
+
const normalized = normalizeStatement(stmt);
|
|
656
|
+
if (normalized.kind === "named") {
|
|
657
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
658
|
+
}
|
|
659
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
660
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
661
|
+
return {
|
|
662
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
663
|
+
args: coercedArgs
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function shouldBypassPostgres(stmt) {
|
|
667
|
+
const normalized = normalizeStatement(stmt);
|
|
668
|
+
if (normalized.kind === "named") {
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
672
|
+
}
|
|
673
|
+
function shouldFallbackOnError(error) {
|
|
674
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
675
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
676
|
+
}
|
|
677
|
+
function isReadQuery(sql) {
|
|
678
|
+
const trimmed = sql.trimStart();
|
|
679
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
680
|
+
}
|
|
681
|
+
function buildRow(row, columns) {
|
|
682
|
+
const values = columns.map((column) => row[column]);
|
|
683
|
+
return Object.assign(values, row);
|
|
684
|
+
}
|
|
685
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
686
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
687
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
688
|
+
return {
|
|
689
|
+
columns,
|
|
690
|
+
columnTypes: columns.map(() => ""),
|
|
691
|
+
rows: resultRows,
|
|
692
|
+
rowsAffected,
|
|
693
|
+
lastInsertRowid: void 0,
|
|
694
|
+
toJSON() {
|
|
695
|
+
return {
|
|
696
|
+
columns,
|
|
697
|
+
columnTypes: columns.map(() => ""),
|
|
698
|
+
rows,
|
|
699
|
+
rowsAffected,
|
|
700
|
+
lastInsertRowid: void 0
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async function loadPrismaClient() {
|
|
706
|
+
if (!prismaClientPromise) {
|
|
707
|
+
prismaClientPromise = (async () => {
|
|
708
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
709
|
+
if (explicitPath) {
|
|
710
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
711
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
712
|
+
if (!PrismaClient2) {
|
|
713
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
714
|
+
}
|
|
715
|
+
return new PrismaClient2();
|
|
716
|
+
}
|
|
717
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
718
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
719
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
720
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
721
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
722
|
+
if (!PrismaClient) {
|
|
723
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
724
|
+
}
|
|
725
|
+
return new PrismaClient();
|
|
726
|
+
})();
|
|
727
|
+
}
|
|
728
|
+
return prismaClientPromise;
|
|
729
|
+
}
|
|
730
|
+
async function ensureCompatibilityViews(prisma) {
|
|
731
|
+
if (!compatibilityBootstrapPromise) {
|
|
732
|
+
compatibilityBootstrapPromise = (async () => {
|
|
733
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
734
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
735
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
736
|
+
"SELECT to_regclass($1) AS regclass",
|
|
737
|
+
relation
|
|
738
|
+
);
|
|
739
|
+
if (!rows[0]?.regclass) {
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
await prisma.$executeRawUnsafe(
|
|
743
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
})();
|
|
747
|
+
}
|
|
748
|
+
return compatibilityBootstrapPromise;
|
|
749
|
+
}
|
|
750
|
+
async function executeOnPrisma(executor, stmt) {
|
|
751
|
+
const translated = translateStatementForPostgres(stmt);
|
|
752
|
+
if (isReadQuery(translated.sql)) {
|
|
753
|
+
const rows = await executor.$queryRawUnsafe(
|
|
754
|
+
translated.sql,
|
|
755
|
+
...translated.args
|
|
756
|
+
);
|
|
757
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
758
|
+
}
|
|
759
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
760
|
+
return buildResultSet([], rowsAffected);
|
|
761
|
+
}
|
|
762
|
+
function splitSqlStatements(sql) {
|
|
763
|
+
const parts = [];
|
|
764
|
+
let current = "";
|
|
765
|
+
let inSingle = false;
|
|
766
|
+
let inDouble = false;
|
|
767
|
+
let inLineComment = false;
|
|
768
|
+
let inBlockComment = false;
|
|
769
|
+
for (let i = 0; i < sql.length; i++) {
|
|
770
|
+
const ch = sql[i];
|
|
771
|
+
const next = sql[i + 1];
|
|
772
|
+
if (inLineComment) {
|
|
773
|
+
current += ch;
|
|
774
|
+
if (ch === "\n") inLineComment = false;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (inBlockComment) {
|
|
778
|
+
current += ch;
|
|
779
|
+
if (ch === "*" && next === "/") {
|
|
780
|
+
current += next;
|
|
781
|
+
inBlockComment = false;
|
|
782
|
+
i += 1;
|
|
783
|
+
}
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
787
|
+
current += ch + next;
|
|
788
|
+
inLineComment = true;
|
|
789
|
+
i += 1;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
793
|
+
current += ch + next;
|
|
794
|
+
inBlockComment = true;
|
|
795
|
+
i += 1;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
799
|
+
inSingle = !inSingle;
|
|
800
|
+
current += ch;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
804
|
+
inDouble = !inDouble;
|
|
805
|
+
current += ch;
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
809
|
+
if (current.trim()) {
|
|
810
|
+
parts.push(current.trim());
|
|
811
|
+
}
|
|
812
|
+
current = "";
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
current += ch;
|
|
816
|
+
}
|
|
817
|
+
if (current.trim()) {
|
|
818
|
+
parts.push(current.trim());
|
|
819
|
+
}
|
|
820
|
+
return parts;
|
|
821
|
+
}
|
|
822
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
823
|
+
const prisma = await loadPrismaClient();
|
|
824
|
+
await ensureCompatibilityViews(prisma);
|
|
825
|
+
let closed = false;
|
|
826
|
+
let adapter;
|
|
827
|
+
const fallbackExecute = async (stmt, error) => {
|
|
828
|
+
if (!fallbackClient) {
|
|
829
|
+
if (error) throw error;
|
|
830
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
831
|
+
}
|
|
832
|
+
if (error) {
|
|
833
|
+
process.stderr.write(
|
|
834
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
835
|
+
`
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
return fallbackClient.execute(stmt);
|
|
839
|
+
};
|
|
840
|
+
adapter = {
|
|
841
|
+
async execute(stmt) {
|
|
842
|
+
if (shouldBypassPostgres(stmt)) {
|
|
843
|
+
return fallbackExecute(stmt);
|
|
844
|
+
}
|
|
845
|
+
try {
|
|
846
|
+
return await executeOnPrisma(prisma, stmt);
|
|
847
|
+
} catch (error) {
|
|
848
|
+
if (shouldFallbackOnError(error)) {
|
|
849
|
+
return fallbackExecute(stmt, error);
|
|
850
|
+
}
|
|
851
|
+
throw error;
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
async batch(stmts, mode) {
|
|
855
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
856
|
+
if (!fallbackClient) {
|
|
857
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
858
|
+
}
|
|
859
|
+
return fallbackClient.batch(stmts, mode);
|
|
860
|
+
}
|
|
861
|
+
try {
|
|
862
|
+
if (prisma.$transaction) {
|
|
863
|
+
return await prisma.$transaction(async (tx) => {
|
|
864
|
+
const results2 = [];
|
|
865
|
+
for (const stmt of stmts) {
|
|
866
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
867
|
+
}
|
|
868
|
+
return results2;
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
const results = [];
|
|
872
|
+
for (const stmt of stmts) {
|
|
873
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
874
|
+
}
|
|
875
|
+
return results;
|
|
876
|
+
} catch (error) {
|
|
877
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
878
|
+
process.stderr.write(
|
|
879
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
880
|
+
`
|
|
881
|
+
);
|
|
882
|
+
return fallbackClient.batch(stmts, mode);
|
|
883
|
+
}
|
|
884
|
+
throw error;
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
async migrate(stmts) {
|
|
888
|
+
if (fallbackClient) {
|
|
889
|
+
return fallbackClient.migrate(stmts);
|
|
890
|
+
}
|
|
891
|
+
return adapter.batch(stmts, "deferred");
|
|
892
|
+
},
|
|
893
|
+
async transaction(mode) {
|
|
894
|
+
if (!fallbackClient) {
|
|
895
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
896
|
+
}
|
|
897
|
+
return fallbackClient.transaction(mode);
|
|
898
|
+
},
|
|
899
|
+
async executeMultiple(sql) {
|
|
900
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
901
|
+
return fallbackClient.executeMultiple(sql);
|
|
902
|
+
}
|
|
903
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
904
|
+
await adapter.execute(statement);
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
async sync() {
|
|
908
|
+
if (fallbackClient) {
|
|
909
|
+
return fallbackClient.sync();
|
|
910
|
+
}
|
|
911
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
912
|
+
},
|
|
913
|
+
close() {
|
|
914
|
+
closed = true;
|
|
915
|
+
prismaClientPromise = null;
|
|
916
|
+
compatibilityBootstrapPromise = null;
|
|
917
|
+
void prisma.$disconnect?.();
|
|
918
|
+
},
|
|
919
|
+
get closed() {
|
|
920
|
+
return closed;
|
|
921
|
+
},
|
|
922
|
+
get protocol() {
|
|
923
|
+
return "prisma-postgres";
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
return adapter;
|
|
927
|
+
}
|
|
928
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
929
|
+
var init_database_adapter = __esm({
|
|
930
|
+
"src/lib/database-adapter.ts"() {
|
|
931
|
+
"use strict";
|
|
932
|
+
VIEW_MAPPINGS = [
|
|
933
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
934
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
935
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
936
|
+
{ view: "entities", source: "memory.entities" },
|
|
937
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
938
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
939
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
940
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
941
|
+
{ view: "messages", source: "memory.messages" },
|
|
942
|
+
{ view: "users", source: "wiki.users" },
|
|
943
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
944
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
945
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
946
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
947
|
+
];
|
|
948
|
+
UPSERT_KEYS = {
|
|
949
|
+
memories: ["id"],
|
|
950
|
+
tasks: ["id"],
|
|
951
|
+
behaviors: ["id"],
|
|
952
|
+
entities: ["id"],
|
|
953
|
+
relationships: ["id"],
|
|
954
|
+
entity_aliases: ["alias"],
|
|
955
|
+
notifications: ["id"],
|
|
956
|
+
messages: ["id"],
|
|
957
|
+
users: ["id"],
|
|
958
|
+
workspaces: ["id"],
|
|
959
|
+
workspace_users: ["id"],
|
|
960
|
+
documents: ["id"],
|
|
961
|
+
chats: ["id"]
|
|
962
|
+
};
|
|
963
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
964
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
965
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
966
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
967
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
968
|
+
};
|
|
969
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
970
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
971
|
+
);
|
|
972
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
973
|
+
/\bPRAGMA\b/i,
|
|
974
|
+
/\bsqlite_master\b/i,
|
|
975
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
976
|
+
/\bMATCH\b/i,
|
|
977
|
+
/\bvector_distance_cos\s*\(/i,
|
|
978
|
+
/\bjson_extract\s*\(/i,
|
|
979
|
+
/\bjulianday\s*\(/i,
|
|
980
|
+
/\bstrftime\s*\(/i,
|
|
981
|
+
/\blast_insert_rowid\s*\(/i
|
|
982
|
+
];
|
|
983
|
+
prismaClientPromise = null;
|
|
984
|
+
compatibilityBootstrapPromise = null;
|
|
400
985
|
}
|
|
401
986
|
});
|
|
402
987
|
|
|
403
988
|
// src/lib/exe-daemon-client.ts
|
|
404
989
|
import net from "net";
|
|
405
|
-
import
|
|
990
|
+
import os4 from "os";
|
|
406
991
|
import { spawn } from "child_process";
|
|
407
992
|
import { randomUUID } from "crypto";
|
|
408
993
|
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
409
|
-
import
|
|
994
|
+
import path4 from "path";
|
|
410
995
|
import { fileURLToPath } from "url";
|
|
411
996
|
function handleData(chunk) {
|
|
412
997
|
_buffer += chunk.toString();
|
|
@@ -457,17 +1042,17 @@ function cleanupStaleFiles() {
|
|
|
457
1042
|
}
|
|
458
1043
|
}
|
|
459
1044
|
function findPackageRoot() {
|
|
460
|
-
let dir =
|
|
461
|
-
const { root } =
|
|
1045
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1046
|
+
const { root } = path4.parse(dir);
|
|
462
1047
|
while (dir !== root) {
|
|
463
|
-
if (existsSync3(
|
|
464
|
-
dir =
|
|
1048
|
+
if (existsSync3(path4.join(dir, "package.json"))) return dir;
|
|
1049
|
+
dir = path4.dirname(dir);
|
|
465
1050
|
}
|
|
466
1051
|
return null;
|
|
467
1052
|
}
|
|
468
1053
|
function spawnDaemon() {
|
|
469
|
-
const freeGB =
|
|
470
|
-
const totalGB =
|
|
1054
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
1055
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
471
1056
|
if (totalGB <= 8) {
|
|
472
1057
|
process.stderr.write(
|
|
473
1058
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -487,7 +1072,7 @@ function spawnDaemon() {
|
|
|
487
1072
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
488
1073
|
return;
|
|
489
1074
|
}
|
|
490
|
-
const daemonPath =
|
|
1075
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
491
1076
|
if (!existsSync3(daemonPath)) {
|
|
492
1077
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
493
1078
|
`);
|
|
@@ -496,7 +1081,7 @@ function spawnDaemon() {
|
|
|
496
1081
|
const resolvedPath = daemonPath;
|
|
497
1082
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
498
1083
|
`);
|
|
499
|
-
const logPath =
|
|
1084
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
500
1085
|
let stderrFd = "ignore";
|
|
501
1086
|
try {
|
|
502
1087
|
stderrFd = openSync(logPath, "a");
|
|
@@ -647,74 +1232,123 @@ async function pingDaemon() {
|
|
|
647
1232
|
return null;
|
|
648
1233
|
}
|
|
649
1234
|
function killAndRespawnDaemon() {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1235
|
+
if (!acquireSpawnLock()) {
|
|
1236
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1237
|
+
if (_socket) {
|
|
1238
|
+
_socket.destroy();
|
|
1239
|
+
_socket = null;
|
|
1240
|
+
}
|
|
1241
|
+
_connected = false;
|
|
1242
|
+
_buffer = "";
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1247
|
+
if (existsSync3(PID_PATH)) {
|
|
1248
|
+
try {
|
|
1249
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
1250
|
+
if (pid > 0) {
|
|
1251
|
+
try {
|
|
1252
|
+
process.kill(pid, "SIGKILL");
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
658
1255
|
}
|
|
1256
|
+
} catch {
|
|
659
1257
|
}
|
|
1258
|
+
}
|
|
1259
|
+
if (_socket) {
|
|
1260
|
+
_socket.destroy();
|
|
1261
|
+
_socket = null;
|
|
1262
|
+
}
|
|
1263
|
+
_connected = false;
|
|
1264
|
+
_buffer = "";
|
|
1265
|
+
try {
|
|
1266
|
+
unlinkSync2(PID_PATH);
|
|
660
1267
|
} catch {
|
|
661
1268
|
}
|
|
1269
|
+
try {
|
|
1270
|
+
unlinkSync2(SOCKET_PATH);
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
spawnDaemon();
|
|
1274
|
+
} finally {
|
|
1275
|
+
releaseSpawnLock();
|
|
662
1276
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
_socket = null;
|
|
666
|
-
}
|
|
667
|
-
_connected = false;
|
|
668
|
-
_buffer = "";
|
|
1277
|
+
}
|
|
1278
|
+
function isDaemonTooYoung() {
|
|
669
1279
|
try {
|
|
670
|
-
|
|
1280
|
+
const stat = statSync(PID_PATH);
|
|
1281
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
671
1282
|
} catch {
|
|
1283
|
+
return false;
|
|
672
1284
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1285
|
+
}
|
|
1286
|
+
async function retryThenRestart(doRequest, label) {
|
|
1287
|
+
const result = await doRequest();
|
|
1288
|
+
if (!result.error) {
|
|
1289
|
+
_consecutiveFailures = 0;
|
|
1290
|
+
return result;
|
|
676
1291
|
}
|
|
677
|
-
|
|
1292
|
+
_consecutiveFailures++;
|
|
1293
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1294
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1295
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1296
|
+
`);
|
|
1297
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1298
|
+
if (!_connected) {
|
|
1299
|
+
if (!await connectToSocket()) continue;
|
|
1300
|
+
}
|
|
1301
|
+
const retry = await doRequest();
|
|
1302
|
+
if (!retry.error) {
|
|
1303
|
+
_consecutiveFailures = 0;
|
|
1304
|
+
return retry;
|
|
1305
|
+
}
|
|
1306
|
+
_consecutiveFailures++;
|
|
1307
|
+
}
|
|
1308
|
+
if (isDaemonTooYoung()) {
|
|
1309
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1310
|
+
`);
|
|
1311
|
+
return { error: result.error };
|
|
1312
|
+
}
|
|
1313
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1314
|
+
`);
|
|
1315
|
+
killAndRespawnDaemon();
|
|
1316
|
+
const start = Date.now();
|
|
1317
|
+
let delay2 = 200;
|
|
1318
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1319
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1320
|
+
if (await connectToSocket()) break;
|
|
1321
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1322
|
+
}
|
|
1323
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1324
|
+
const final = await doRequest();
|
|
1325
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1326
|
+
return final;
|
|
678
1327
|
}
|
|
679
1328
|
async function embedViaClient(text, priority = "high") {
|
|
680
1329
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
681
1330
|
_requestCount++;
|
|
682
1331
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
683
1332
|
const health = await pingDaemon();
|
|
684
|
-
if (!health) {
|
|
1333
|
+
if (!health && !isDaemonTooYoung()) {
|
|
685
1334
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
686
1335
|
`);
|
|
687
1336
|
killAndRespawnDaemon();
|
|
688
1337
|
const start = Date.now();
|
|
689
|
-
let
|
|
1338
|
+
let d = 200;
|
|
690
1339
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
691
|
-
await new Promise((r) => setTimeout(r,
|
|
1340
|
+
await new Promise((r) => setTimeout(r, d));
|
|
692
1341
|
if (await connectToSocket()) break;
|
|
693
|
-
|
|
1342
|
+
d = Math.min(d * 2, 3e3);
|
|
694
1343
|
}
|
|
695
1344
|
if (!_connected) return null;
|
|
696
1345
|
}
|
|
697
1346
|
}
|
|
698
|
-
const result = await
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
killAndRespawnDaemon();
|
|
704
|
-
const start = Date.now();
|
|
705
|
-
let delay2 = 200;
|
|
706
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
707
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
708
|
-
if (await connectToSocket()) break;
|
|
709
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
710
|
-
}
|
|
711
|
-
if (!_connected) return null;
|
|
712
|
-
const retry = await sendRequest([text], priority);
|
|
713
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
714
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
715
|
-
`);
|
|
716
|
-
}
|
|
717
|
-
return null;
|
|
1347
|
+
const result = await retryThenRestart(
|
|
1348
|
+
() => sendRequest([text], priority),
|
|
1349
|
+
"Embed"
|
|
1350
|
+
);
|
|
1351
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
718
1352
|
}
|
|
719
1353
|
function disconnectClient() {
|
|
720
1354
|
if (_socket) {
|
|
@@ -732,14 +1366,14 @@ function disconnectClient() {
|
|
|
732
1366
|
function isClientConnected() {
|
|
733
1367
|
return _connected;
|
|
734
1368
|
}
|
|
735
|
-
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;
|
|
1369
|
+
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;
|
|
736
1370
|
var init_exe_daemon_client = __esm({
|
|
737
1371
|
"src/lib/exe-daemon-client.ts"() {
|
|
738
1372
|
"use strict";
|
|
739
1373
|
init_config();
|
|
740
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
741
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
742
|
-
SPAWN_LOCK_PATH =
|
|
1374
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
1375
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
1376
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
743
1377
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
744
1378
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
745
1379
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -747,7 +1381,11 @@ var init_exe_daemon_client = __esm({
|
|
|
747
1381
|
_connected = false;
|
|
748
1382
|
_buffer = "";
|
|
749
1383
|
_requestCount = 0;
|
|
1384
|
+
_consecutiveFailures = 0;
|
|
750
1385
|
HEALTH_CHECK_INTERVAL = 100;
|
|
1386
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1387
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1388
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
751
1389
|
_pending = /* @__PURE__ */ new Map();
|
|
752
1390
|
MAX_BUFFER = 1e7;
|
|
753
1391
|
}
|
|
@@ -823,7 +1461,7 @@ __export(db_daemon_client_exports, {
|
|
|
823
1461
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
824
1462
|
initDaemonDbClient: () => initDaemonDbClient
|
|
825
1463
|
});
|
|
826
|
-
function
|
|
1464
|
+
function normalizeStatement2(stmt) {
|
|
827
1465
|
if (typeof stmt === "string") {
|
|
828
1466
|
return { sql: stmt, args: [] };
|
|
829
1467
|
}
|
|
@@ -847,7 +1485,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
847
1485
|
if (!_useDaemon || !isClientConnected()) {
|
|
848
1486
|
return fallbackClient.execute(stmt);
|
|
849
1487
|
}
|
|
850
|
-
const { sql, args } =
|
|
1488
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
851
1489
|
const response = await sendDaemonRequest({
|
|
852
1490
|
type: "db-execute",
|
|
853
1491
|
sql,
|
|
@@ -872,7 +1510,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
872
1510
|
if (!_useDaemon || !isClientConnected()) {
|
|
873
1511
|
return fallbackClient.batch(stmts, mode);
|
|
874
1512
|
}
|
|
875
|
-
const statements = stmts.map(
|
|
1513
|
+
const statements = stmts.map(normalizeStatement2);
|
|
876
1514
|
const response = await sendDaemonRequest({
|
|
877
1515
|
type: "db-batch",
|
|
878
1516
|
statements,
|
|
@@ -967,6 +1605,18 @@ __export(database_exports, {
|
|
|
967
1605
|
});
|
|
968
1606
|
import { createClient } from "@libsql/client";
|
|
969
1607
|
async function initDatabase(config) {
|
|
1608
|
+
if (_walCheckpointTimer) {
|
|
1609
|
+
clearInterval(_walCheckpointTimer);
|
|
1610
|
+
_walCheckpointTimer = null;
|
|
1611
|
+
}
|
|
1612
|
+
if (_daemonClient) {
|
|
1613
|
+
_daemonClient.close();
|
|
1614
|
+
_daemonClient = null;
|
|
1615
|
+
}
|
|
1616
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1617
|
+
_adapterClient.close();
|
|
1618
|
+
}
|
|
1619
|
+
_adapterClient = null;
|
|
970
1620
|
if (_client) {
|
|
971
1621
|
_client.close();
|
|
972
1622
|
_client = null;
|
|
@@ -980,6 +1630,7 @@ async function initDatabase(config) {
|
|
|
980
1630
|
}
|
|
981
1631
|
_client = createClient(opts);
|
|
982
1632
|
_resilientClient = wrapWithRetry(_client);
|
|
1633
|
+
_adapterClient = _resilientClient;
|
|
983
1634
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
984
1635
|
});
|
|
985
1636
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -990,14 +1641,20 @@ async function initDatabase(config) {
|
|
|
990
1641
|
});
|
|
991
1642
|
}, 3e4);
|
|
992
1643
|
_walCheckpointTimer.unref();
|
|
1644
|
+
if (process.env.DATABASE_URL) {
|
|
1645
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1646
|
+
}
|
|
993
1647
|
}
|
|
994
1648
|
function isInitialized() {
|
|
995
|
-
return _client !== null;
|
|
1649
|
+
return _adapterClient !== null || _client !== null;
|
|
996
1650
|
}
|
|
997
1651
|
function getClient() {
|
|
998
|
-
if (!
|
|
1652
|
+
if (!_adapterClient) {
|
|
999
1653
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1000
1654
|
}
|
|
1655
|
+
if (process.env.DATABASE_URL) {
|
|
1656
|
+
return _adapterClient;
|
|
1657
|
+
}
|
|
1001
1658
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1002
1659
|
return _resilientClient;
|
|
1003
1660
|
}
|
|
@@ -1007,6 +1664,7 @@ function getClient() {
|
|
|
1007
1664
|
return _resilientClient;
|
|
1008
1665
|
}
|
|
1009
1666
|
async function initDaemonClient() {
|
|
1667
|
+
if (process.env.DATABASE_URL) return;
|
|
1010
1668
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1011
1669
|
if (!_resilientClient) return;
|
|
1012
1670
|
try {
|
|
@@ -1951,26 +2609,36 @@ async function ensureSchema() {
|
|
|
1951
2609
|
}
|
|
1952
2610
|
}
|
|
1953
2611
|
async function disposeDatabase() {
|
|
2612
|
+
if (_walCheckpointTimer) {
|
|
2613
|
+
clearInterval(_walCheckpointTimer);
|
|
2614
|
+
_walCheckpointTimer = null;
|
|
2615
|
+
}
|
|
1954
2616
|
if (_daemonClient) {
|
|
1955
2617
|
_daemonClient.close();
|
|
1956
2618
|
_daemonClient = null;
|
|
1957
2619
|
}
|
|
2620
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2621
|
+
_adapterClient.close();
|
|
2622
|
+
}
|
|
2623
|
+
_adapterClient = null;
|
|
1958
2624
|
if (_client) {
|
|
1959
2625
|
_client.close();
|
|
1960
2626
|
_client = null;
|
|
1961
2627
|
_resilientClient = null;
|
|
1962
2628
|
}
|
|
1963
2629
|
}
|
|
1964
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2630
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1965
2631
|
var init_database = __esm({
|
|
1966
2632
|
"src/lib/database.ts"() {
|
|
1967
2633
|
"use strict";
|
|
1968
2634
|
init_db_retry();
|
|
1969
2635
|
init_employees();
|
|
2636
|
+
init_database_adapter();
|
|
1970
2637
|
_client = null;
|
|
1971
2638
|
_resilientClient = null;
|
|
1972
2639
|
_walCheckpointTimer = null;
|
|
1973
2640
|
_daemonClient = null;
|
|
2641
|
+
_adapterClient = null;
|
|
1974
2642
|
initTurso = initDatabase;
|
|
1975
2643
|
disposeTurso = disposeDatabase;
|
|
1976
2644
|
}
|
|
@@ -1979,13 +2647,13 @@ var init_database = __esm({
|
|
|
1979
2647
|
// src/lib/keychain.ts
|
|
1980
2648
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1981
2649
|
import { existsSync as existsSync4 } from "fs";
|
|
1982
|
-
import
|
|
1983
|
-
import
|
|
2650
|
+
import path5 from "path";
|
|
2651
|
+
import os5 from "os";
|
|
1984
2652
|
function getKeyDir() {
|
|
1985
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2653
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os5.homedir(), ".exe-os");
|
|
1986
2654
|
}
|
|
1987
2655
|
function getKeyPath() {
|
|
1988
|
-
return
|
|
2656
|
+
return path5.join(getKeyDir(), "master.key");
|
|
1989
2657
|
}
|
|
1990
2658
|
async function tryKeytar() {
|
|
1991
2659
|
try {
|
|
@@ -2008,7 +2676,7 @@ async function getMasterKey() {
|
|
|
2008
2676
|
const keyPath = getKeyPath();
|
|
2009
2677
|
if (!existsSync4(keyPath)) {
|
|
2010
2678
|
process.stderr.write(
|
|
2011
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
2679
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2012
2680
|
`
|
|
2013
2681
|
);
|
|
2014
2682
|
return null;
|
|
@@ -2101,7 +2769,7 @@ __export(shard_manager_exports, {
|
|
|
2101
2769
|
listShards: () => listShards,
|
|
2102
2770
|
shardExists: () => shardExists
|
|
2103
2771
|
});
|
|
2104
|
-
import
|
|
2772
|
+
import path6 from "path";
|
|
2105
2773
|
import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
|
|
2106
2774
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2107
2775
|
function initShardManager(encryptionKey) {
|
|
@@ -2127,7 +2795,7 @@ function getShardClient(projectName) {
|
|
|
2127
2795
|
}
|
|
2128
2796
|
const cached = _shards.get(safeName);
|
|
2129
2797
|
if (cached) return cached;
|
|
2130
|
-
const dbPath =
|
|
2798
|
+
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
2131
2799
|
const client = createClient2({
|
|
2132
2800
|
url: `file:${dbPath}`,
|
|
2133
2801
|
encryptionKey: _encryptionKey
|
|
@@ -2137,7 +2805,7 @@ function getShardClient(projectName) {
|
|
|
2137
2805
|
}
|
|
2138
2806
|
function shardExists(projectName) {
|
|
2139
2807
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2140
|
-
return existsSync5(
|
|
2808
|
+
return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
2141
2809
|
}
|
|
2142
2810
|
function listShards() {
|
|
2143
2811
|
if (!existsSync5(SHARDS_DIR)) return [];
|
|
@@ -2214,7 +2882,23 @@ async function ensureShardSchema(client) {
|
|
|
2214
2882
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2215
2883
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2216
2884
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2217
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2885
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2886
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2887
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2888
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2889
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2890
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2891
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2892
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2893
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2894
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2895
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2896
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2897
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2898
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2899
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2900
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2901
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2218
2902
|
]) {
|
|
2219
2903
|
try {
|
|
2220
2904
|
await client.execute(col);
|
|
@@ -2326,7 +3010,7 @@ var init_shard_manager = __esm({
|
|
|
2326
3010
|
"src/lib/shard-manager.ts"() {
|
|
2327
3011
|
"use strict";
|
|
2328
3012
|
init_config();
|
|
2329
|
-
SHARDS_DIR =
|
|
3013
|
+
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
2330
3014
|
_shards = /* @__PURE__ */ new Map();
|
|
2331
3015
|
_encryptionKey = null;
|
|
2332
3016
|
_shardingEnabled = false;
|
|
@@ -3092,10 +3776,10 @@ var init_store = __esm({
|
|
|
3092
3776
|
|
|
3093
3777
|
// src/lib/session-registry.ts
|
|
3094
3778
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
|
|
3095
|
-
import
|
|
3096
|
-
import
|
|
3779
|
+
import path7 from "path";
|
|
3780
|
+
import os6 from "os";
|
|
3097
3781
|
function registerSession(entry) {
|
|
3098
|
-
const dir =
|
|
3782
|
+
const dir = path7.dirname(REGISTRY_PATH);
|
|
3099
3783
|
if (!existsSync6(dir)) {
|
|
3100
3784
|
mkdirSync2(dir, { recursive: true });
|
|
3101
3785
|
}
|
|
@@ -3120,7 +3804,7 @@ var REGISTRY_PATH;
|
|
|
3120
3804
|
var init_session_registry = __esm({
|
|
3121
3805
|
"src/lib/session-registry.ts"() {
|
|
3122
3806
|
"use strict";
|
|
3123
|
-
REGISTRY_PATH =
|
|
3807
|
+
REGISTRY_PATH = path7.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
3124
3808
|
}
|
|
3125
3809
|
});
|
|
3126
3810
|
|
|
@@ -3401,7 +4085,7 @@ var init_runtime_table = __esm({
|
|
|
3401
4085
|
|
|
3402
4086
|
// src/lib/agent-config.ts
|
|
3403
4087
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
3404
|
-
import
|
|
4088
|
+
import path8 from "path";
|
|
3405
4089
|
function loadAgentConfig() {
|
|
3406
4090
|
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
3407
4091
|
try {
|
|
@@ -3424,7 +4108,7 @@ var init_agent_config = __esm({
|
|
|
3424
4108
|
"use strict";
|
|
3425
4109
|
init_config();
|
|
3426
4110
|
init_runtime_table();
|
|
3427
|
-
AGENT_CONFIG_PATH =
|
|
4111
|
+
AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
3428
4112
|
DEFAULT_MODELS = {
|
|
3429
4113
|
claude: "claude-opus-4",
|
|
3430
4114
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -3443,10 +4127,10 @@ __export(intercom_queue_exports, {
|
|
|
3443
4127
|
readQueue: () => readQueue
|
|
3444
4128
|
});
|
|
3445
4129
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
3446
|
-
import
|
|
3447
|
-
import
|
|
4130
|
+
import path9 from "path";
|
|
4131
|
+
import os7 from "os";
|
|
3448
4132
|
function ensureDir() {
|
|
3449
|
-
const dir =
|
|
4133
|
+
const dir = path9.dirname(QUEUE_PATH);
|
|
3450
4134
|
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
3451
4135
|
}
|
|
3452
4136
|
function readQueue() {
|
|
@@ -3552,26 +4236,26 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3552
4236
|
var init_intercom_queue = __esm({
|
|
3553
4237
|
"src/lib/intercom-queue.ts"() {
|
|
3554
4238
|
"use strict";
|
|
3555
|
-
QUEUE_PATH =
|
|
4239
|
+
QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
3556
4240
|
MAX_RETRIES2 = 5;
|
|
3557
4241
|
TTL_MS = 60 * 60 * 1e3;
|
|
3558
|
-
INTERCOM_LOG =
|
|
4242
|
+
INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3559
4243
|
}
|
|
3560
4244
|
});
|
|
3561
4245
|
|
|
3562
4246
|
// src/lib/license.ts
|
|
3563
4247
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
3564
4248
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3565
|
-
import
|
|
4249
|
+
import path10 from "path";
|
|
3566
4250
|
import { jwtVerify, importSPKI } from "jose";
|
|
3567
4251
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
3568
4252
|
var init_license = __esm({
|
|
3569
4253
|
"src/lib/license.ts"() {
|
|
3570
4254
|
"use strict";
|
|
3571
4255
|
init_config();
|
|
3572
|
-
LICENSE_PATH =
|
|
3573
|
-
CACHE_PATH =
|
|
3574
|
-
DEVICE_ID_PATH =
|
|
4256
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
4257
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
4258
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3575
4259
|
PLAN_LIMITS = {
|
|
3576
4260
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
3577
4261
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -3584,7 +4268,7 @@ var init_license = __esm({
|
|
|
3584
4268
|
|
|
3585
4269
|
// src/lib/plan-limits.ts
|
|
3586
4270
|
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3587
|
-
import
|
|
4271
|
+
import path11 from "path";
|
|
3588
4272
|
function getLicenseSync() {
|
|
3589
4273
|
try {
|
|
3590
4274
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -3656,14 +4340,14 @@ var init_plan_limits = __esm({
|
|
|
3656
4340
|
this.name = "PlanLimitError";
|
|
3657
4341
|
}
|
|
3658
4342
|
};
|
|
3659
|
-
CACHE_PATH2 =
|
|
4343
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
3660
4344
|
}
|
|
3661
4345
|
});
|
|
3662
4346
|
|
|
3663
4347
|
// src/lib/notifications.ts
|
|
3664
4348
|
import crypto from "crypto";
|
|
3665
|
-
import
|
|
3666
|
-
import
|
|
4349
|
+
import path12 from "path";
|
|
4350
|
+
import os8 from "os";
|
|
3667
4351
|
import {
|
|
3668
4352
|
readFileSync as readFileSync9,
|
|
3669
4353
|
readdirSync as readdirSync2,
|
|
@@ -3764,8 +4448,8 @@ __export(tasks_crud_exports, {
|
|
|
3764
4448
|
writeCheckpoint: () => writeCheckpoint
|
|
3765
4449
|
});
|
|
3766
4450
|
import crypto3 from "crypto";
|
|
3767
|
-
import
|
|
3768
|
-
import
|
|
4451
|
+
import path13 from "path";
|
|
4452
|
+
import os9 from "os";
|
|
3769
4453
|
import { execSync as execSync4 } from "child_process";
|
|
3770
4454
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3771
4455
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
@@ -3943,8 +4627,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3943
4627
|
}
|
|
3944
4628
|
if (input.baseDir) {
|
|
3945
4629
|
try {
|
|
3946
|
-
await mkdir4(
|
|
3947
|
-
await mkdir4(
|
|
4630
|
+
await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4631
|
+
await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3948
4632
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3949
4633
|
await ensureGitignoreExe(input.baseDir);
|
|
3950
4634
|
} catch {
|
|
@@ -3980,9 +4664,9 @@ ${laneWarning}` : laneWarning;
|
|
|
3980
4664
|
});
|
|
3981
4665
|
if (input.baseDir) {
|
|
3982
4666
|
try {
|
|
3983
|
-
const EXE_OS_DIR =
|
|
3984
|
-
const mdPath =
|
|
3985
|
-
const mdDir =
|
|
4667
|
+
const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
|
|
4668
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
4669
|
+
const mdDir = path13.dirname(mdPath);
|
|
3986
4670
|
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
3987
4671
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3988
4672
|
const mdContent = `# ${input.title}
|
|
@@ -4283,7 +4967,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4283
4967
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4284
4968
|
}
|
|
4285
4969
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4286
|
-
const archPath =
|
|
4970
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4287
4971
|
try {
|
|
4288
4972
|
if (existsSync12(archPath)) return;
|
|
4289
4973
|
const template = [
|
|
@@ -4318,7 +5002,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4318
5002
|
}
|
|
4319
5003
|
}
|
|
4320
5004
|
async function ensureGitignoreExe(baseDir) {
|
|
4321
|
-
const gitignorePath =
|
|
5005
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
4322
5006
|
try {
|
|
4323
5007
|
if (existsSync12(gitignorePath)) {
|
|
4324
5008
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -4364,7 +5048,7 @@ __export(tasks_review_exports, {
|
|
|
4364
5048
|
isStale: () => isStale,
|
|
4365
5049
|
listPendingReviews: () => listPendingReviews
|
|
4366
5050
|
});
|
|
4367
|
-
import
|
|
5051
|
+
import path14 from "path";
|
|
4368
5052
|
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
4369
5053
|
function formatAge(isoTimestamp) {
|
|
4370
5054
|
if (!isoTimestamp) return "";
|
|
@@ -4385,7 +5069,7 @@ async function countPendingReviews(sessionScope) {
|
|
|
4385
5069
|
const client = getClient();
|
|
4386
5070
|
if (sessionScope) {
|
|
4387
5071
|
const result2 = await client.execute({
|
|
4388
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
5072
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
4389
5073
|
args: [sessionScope]
|
|
4390
5074
|
});
|
|
4391
5075
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -4650,11 +5334,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4650
5334
|
);
|
|
4651
5335
|
}
|
|
4652
5336
|
try {
|
|
4653
|
-
const cacheDir =
|
|
5337
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4654
5338
|
if (existsSync13(cacheDir)) {
|
|
4655
5339
|
for (const f of readdirSync3(cacheDir)) {
|
|
4656
5340
|
if (f.startsWith("review-notified-")) {
|
|
4657
|
-
unlinkSync4(
|
|
5341
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4658
5342
|
}
|
|
4659
5343
|
}
|
|
4660
5344
|
}
|
|
@@ -4675,7 +5359,7 @@ var init_tasks_review = __esm({
|
|
|
4675
5359
|
});
|
|
4676
5360
|
|
|
4677
5361
|
// src/lib/tasks-chain.ts
|
|
4678
|
-
import
|
|
5362
|
+
import path15 from "path";
|
|
4679
5363
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
4680
5364
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4681
5365
|
const client = getClient();
|
|
@@ -4692,7 +5376,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4692
5376
|
});
|
|
4693
5377
|
for (const ur of unblockedRows.rows) {
|
|
4694
5378
|
try {
|
|
4695
|
-
const ubFile =
|
|
5379
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4696
5380
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
4697
5381
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4698
5382
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4761,7 +5445,7 @@ var init_tasks_chain = __esm({
|
|
|
4761
5445
|
|
|
4762
5446
|
// src/lib/project-name.ts
|
|
4763
5447
|
import { execSync as execSync5 } from "child_process";
|
|
4764
|
-
import
|
|
5448
|
+
import path16 from "path";
|
|
4765
5449
|
function getProjectName(cwd) {
|
|
4766
5450
|
const dir = cwd ?? process.cwd();
|
|
4767
5451
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4774,7 +5458,7 @@ function getProjectName(cwd) {
|
|
|
4774
5458
|
timeout: 2e3,
|
|
4775
5459
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4776
5460
|
}).trim();
|
|
4777
|
-
repoRoot =
|
|
5461
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4778
5462
|
} catch {
|
|
4779
5463
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
4780
5464
|
cwd: dir,
|
|
@@ -4783,11 +5467,11 @@ function getProjectName(cwd) {
|
|
|
4783
5467
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4784
5468
|
}).trim();
|
|
4785
5469
|
}
|
|
4786
|
-
_cached2 =
|
|
5470
|
+
_cached2 = path16.basename(repoRoot);
|
|
4787
5471
|
_cachedCwd = dir;
|
|
4788
5472
|
return _cached2;
|
|
4789
5473
|
} catch {
|
|
4790
|
-
_cached2 =
|
|
5474
|
+
_cached2 = path16.basename(dir);
|
|
4791
5475
|
_cachedCwd = dir;
|
|
4792
5476
|
return _cached2;
|
|
4793
5477
|
}
|
|
@@ -5260,7 +5944,7 @@ __export(tasks_exports, {
|
|
|
5260
5944
|
updateTaskStatus: () => updateTaskStatus,
|
|
5261
5945
|
writeCheckpoint: () => writeCheckpoint
|
|
5262
5946
|
});
|
|
5263
|
-
import
|
|
5947
|
+
import path17 from "path";
|
|
5264
5948
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5265
5949
|
async function createTask(input) {
|
|
5266
5950
|
const result = await createTaskCore(input);
|
|
@@ -5280,8 +5964,8 @@ async function updateTask(input) {
|
|
|
5280
5964
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5281
5965
|
try {
|
|
5282
5966
|
const agent = String(row.assigned_to);
|
|
5283
|
-
const cacheDir =
|
|
5284
|
-
const cachePath =
|
|
5967
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
5968
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
5285
5969
|
if (input.status === "in_progress") {
|
|
5286
5970
|
mkdirSync6(cacheDir, { recursive: true });
|
|
5287
5971
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -5752,12 +6436,12 @@ __export(tmux_routing_exports, {
|
|
|
5752
6436
|
});
|
|
5753
6437
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
5754
6438
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
5755
|
-
import
|
|
5756
|
-
import
|
|
6439
|
+
import path18 from "path";
|
|
6440
|
+
import os10 from "os";
|
|
5757
6441
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5758
6442
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5759
6443
|
function spawnLockPath(sessionName) {
|
|
5760
|
-
return
|
|
6444
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5761
6445
|
}
|
|
5762
6446
|
function isProcessAlive(pid) {
|
|
5763
6447
|
try {
|
|
@@ -5794,8 +6478,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5794
6478
|
function resolveBehaviorsExporterScript() {
|
|
5795
6479
|
try {
|
|
5796
6480
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5797
|
-
const scriptPath =
|
|
5798
|
-
|
|
6481
|
+
const scriptPath = path18.join(
|
|
6482
|
+
path18.dirname(thisFile),
|
|
5799
6483
|
"..",
|
|
5800
6484
|
"bin",
|
|
5801
6485
|
"exe-export-behaviors.js"
|
|
@@ -5870,7 +6554,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5870
6554
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5871
6555
|
}
|
|
5872
6556
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5873
|
-
const filePath =
|
|
6557
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5874
6558
|
writeFileSync7(filePath, JSON.stringify({
|
|
5875
6559
|
parentExe: rootExe,
|
|
5876
6560
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -5879,7 +6563,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5879
6563
|
}
|
|
5880
6564
|
function getParentExe(sessionKey) {
|
|
5881
6565
|
try {
|
|
5882
|
-
const data = JSON.parse(readFileSync11(
|
|
6566
|
+
const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5883
6567
|
return data.parentExe || null;
|
|
5884
6568
|
} catch {
|
|
5885
6569
|
return null;
|
|
@@ -5888,7 +6572,7 @@ function getParentExe(sessionKey) {
|
|
|
5888
6572
|
function getDispatchedBy(sessionKey) {
|
|
5889
6573
|
try {
|
|
5890
6574
|
const data = JSON.parse(readFileSync11(
|
|
5891
|
-
|
|
6575
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5892
6576
|
"utf8"
|
|
5893
6577
|
));
|
|
5894
6578
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5899,15 +6583,24 @@ function getDispatchedBy(sessionKey) {
|
|
|
5899
6583
|
function resolveExeSession() {
|
|
5900
6584
|
const mySession = getMySession();
|
|
5901
6585
|
if (!mySession) return null;
|
|
6586
|
+
const fromSessionName = extractRootExe(mySession);
|
|
5902
6587
|
try {
|
|
5903
6588
|
const key = getSessionKey();
|
|
5904
6589
|
const parentExe = getParentExe(key);
|
|
5905
6590
|
if (parentExe) {
|
|
5906
|
-
|
|
6591
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
6592
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
6593
|
+
process.stderr.write(
|
|
6594
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
6595
|
+
`
|
|
6596
|
+
);
|
|
6597
|
+
return fromSessionName;
|
|
6598
|
+
}
|
|
6599
|
+
return fromCache;
|
|
5907
6600
|
}
|
|
5908
6601
|
} catch {
|
|
5909
6602
|
}
|
|
5910
|
-
return
|
|
6603
|
+
return fromSessionName ?? mySession;
|
|
5911
6604
|
}
|
|
5912
6605
|
function isEmployeeAlive(sessionName) {
|
|
5913
6606
|
return getTransport().isAlive(sessionName);
|
|
@@ -6065,7 +6758,7 @@ function sendIntercom(targetSession) {
|
|
|
6065
6758
|
try {
|
|
6066
6759
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6067
6760
|
const agent = baseAgentName(rawAgent);
|
|
6068
|
-
const markerPath =
|
|
6761
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6069
6762
|
if (existsSync14(markerPath)) {
|
|
6070
6763
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6071
6764
|
return "debounced";
|
|
@@ -6075,7 +6768,7 @@ function sendIntercom(targetSession) {
|
|
|
6075
6768
|
try {
|
|
6076
6769
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6077
6770
|
const agent = baseAgentName(rawAgent);
|
|
6078
|
-
const taskDir =
|
|
6771
|
+
const taskDir = path18.join(process.cwd(), "exe", agent);
|
|
6079
6772
|
if (existsSync14(taskDir)) {
|
|
6080
6773
|
const files = readdirSync4(taskDir).filter(
|
|
6081
6774
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -6209,8 +6902,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6209
6902
|
const transport = getTransport();
|
|
6210
6903
|
const sessionName = employeeSessionName(employeeName, exeSession2, opts?.instance);
|
|
6211
6904
|
const instanceLabel2 = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6212
|
-
const logDir =
|
|
6213
|
-
const logFile =
|
|
6905
|
+
const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
|
|
6906
|
+
const logFile = path18.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
|
|
6214
6907
|
if (!existsSync14(logDir)) {
|
|
6215
6908
|
mkdirSync7(logDir, { recursive: true });
|
|
6216
6909
|
}
|
|
@@ -6218,14 +6911,14 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6218
6911
|
let cleanupSuffix = "";
|
|
6219
6912
|
try {
|
|
6220
6913
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6221
|
-
const cleanupScript =
|
|
6914
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6222
6915
|
if (existsSync14(cleanupScript)) {
|
|
6223
6916
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession2}"`;
|
|
6224
6917
|
}
|
|
6225
6918
|
} catch {
|
|
6226
6919
|
}
|
|
6227
6920
|
try {
|
|
6228
|
-
const claudeJsonPath =
|
|
6921
|
+
const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
|
|
6229
6922
|
let claudeJson = {};
|
|
6230
6923
|
try {
|
|
6231
6924
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -6240,10 +6933,10 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6240
6933
|
} catch {
|
|
6241
6934
|
}
|
|
6242
6935
|
try {
|
|
6243
|
-
const settingsDir =
|
|
6936
|
+
const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
|
|
6244
6937
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6245
|
-
const projSettingsDir =
|
|
6246
|
-
const settingsPath =
|
|
6938
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
6939
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
6247
6940
|
let settings = {};
|
|
6248
6941
|
try {
|
|
6249
6942
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -6290,8 +6983,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6290
6983
|
let behaviorsFlag = "";
|
|
6291
6984
|
let legacyFallbackWarned = false;
|
|
6292
6985
|
if (!useExeAgent && !useBinSymlink) {
|
|
6293
|
-
const identityPath =
|
|
6294
|
-
|
|
6986
|
+
const identityPath = path18.join(
|
|
6987
|
+
os10.homedir(),
|
|
6295
6988
|
".exe-os",
|
|
6296
6989
|
"identity",
|
|
6297
6990
|
`${employeeName}.md`
|
|
@@ -6306,7 +6999,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6306
6999
|
}
|
|
6307
7000
|
const behaviorsFile = exportBehaviorsSync(
|
|
6308
7001
|
employeeName,
|
|
6309
|
-
|
|
7002
|
+
path18.basename(spawnCwd),
|
|
6310
7003
|
sessionName
|
|
6311
7004
|
);
|
|
6312
7005
|
if (behaviorsFile) {
|
|
@@ -6321,9 +7014,9 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6321
7014
|
}
|
|
6322
7015
|
let sessionContextFlag = "";
|
|
6323
7016
|
try {
|
|
6324
|
-
const ctxDir =
|
|
7017
|
+
const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6325
7018
|
mkdirSync7(ctxDir, { recursive: true });
|
|
6326
|
-
const ctxFile =
|
|
7019
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6327
7020
|
const ctxContent = [
|
|
6328
7021
|
`## Session Context`,
|
|
6329
7022
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -6407,7 +7100,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6407
7100
|
transport.pipeLog(sessionName, logFile);
|
|
6408
7101
|
try {
|
|
6409
7102
|
const mySession = getMySession();
|
|
6410
|
-
const dispatchInfo =
|
|
7103
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6411
7104
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
6412
7105
|
dispatchedBy: mySession,
|
|
6413
7106
|
rootExe: exeSession2,
|
|
@@ -6482,15 +7175,15 @@ var init_tmux_routing = __esm({
|
|
|
6482
7175
|
init_intercom_queue();
|
|
6483
7176
|
init_plan_limits();
|
|
6484
7177
|
init_employees();
|
|
6485
|
-
SPAWN_LOCK_DIR =
|
|
6486
|
-
SESSION_CACHE =
|
|
7178
|
+
SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
7179
|
+
SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6487
7180
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6488
7181
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6489
7182
|
VERIFY_PANE_LINES = 200;
|
|
6490
7183
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6491
7184
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
6492
|
-
INTERCOM_LOG2 =
|
|
6493
|
-
DEBOUNCE_FILE =
|
|
7185
|
+
INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
7186
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6494
7187
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6495
7188
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
6496
7189
|
}
|
|
@@ -6558,8 +7251,8 @@ async function embedDirect(text) {
|
|
|
6558
7251
|
const llamaCpp = await import("node-llama-cpp");
|
|
6559
7252
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6560
7253
|
const { existsSync: existsSync16 } = await import("fs");
|
|
6561
|
-
const
|
|
6562
|
-
const modelPath =
|
|
7254
|
+
const path20 = await import("path");
|
|
7255
|
+
const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
6563
7256
|
if (!existsSync16(modelPath)) {
|
|
6564
7257
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
6565
7258
|
}
|
|
@@ -6819,7 +7512,7 @@ __export(worktree_exports, {
|
|
|
6819
7512
|
});
|
|
6820
7513
|
import { execSync as execSync8 } from "child_process";
|
|
6821
7514
|
import { existsSync as existsSync15, readFileSync as readFileSync12, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, realpathSync } from "fs";
|
|
6822
|
-
import
|
|
7515
|
+
import path19 from "path";
|
|
6823
7516
|
function getGitRoot(dir) {
|
|
6824
7517
|
try {
|
|
6825
7518
|
const root = execSync8("git rev-parse --show-toplevel", {
|
|
@@ -6839,14 +7532,14 @@ function getMainRepoRoot(dir) {
|
|
|
6839
7532
|
"git rev-parse --path-format=absolute --git-common-dir",
|
|
6840
7533
|
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
6841
7534
|
).trim();
|
|
6842
|
-
return realpath(
|
|
7535
|
+
return realpath(path19.dirname(commonDir));
|
|
6843
7536
|
} catch {
|
|
6844
7537
|
return null;
|
|
6845
7538
|
}
|
|
6846
7539
|
}
|
|
6847
7540
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
6848
7541
|
const label = instanceLabel(employeeName, instance);
|
|
6849
|
-
return
|
|
7542
|
+
return path19.join(repoRoot, ".worktrees", label);
|
|
6850
7543
|
}
|
|
6851
7544
|
function worktreeBranch(employeeName, instance) {
|
|
6852
7545
|
return `${instanceLabel(employeeName, instance)}-work`;
|
|
@@ -6859,10 +7552,10 @@ function ensureWorktree(projectDir, employeeName, instance) {
|
|
|
6859
7552
|
if (!repoRoot) return null;
|
|
6860
7553
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
6861
7554
|
const branch = worktreeBranch(employeeName, instance);
|
|
6862
|
-
if (existsSync15(
|
|
7555
|
+
if (existsSync15(path19.join(wtPath, ".git"))) {
|
|
6863
7556
|
return wtPath;
|
|
6864
7557
|
}
|
|
6865
|
-
const worktreesDir =
|
|
7558
|
+
const worktreesDir = path19.join(repoRoot, ".worktrees");
|
|
6866
7559
|
mkdirSync8(worktreesDir, { recursive: true });
|
|
6867
7560
|
ensureGitignoreEntry(repoRoot, "/.worktrees/");
|
|
6868
7561
|
try {
|
|
@@ -6996,7 +7689,7 @@ function realpath(p) {
|
|
|
6996
7689
|
}
|
|
6997
7690
|
function ensureGitignoreEntry(repoRoot, entry) {
|
|
6998
7691
|
try {
|
|
6999
|
-
const gitignorePath =
|
|
7692
|
+
const gitignorePath = path19.join(repoRoot, ".gitignore");
|
|
7000
7693
|
if (existsSync15(gitignorePath)) {
|
|
7001
7694
|
const content = readFileSync12(gitignorePath, "utf-8");
|
|
7002
7695
|
if (content.includes(entry)) return;
|