@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/lib/exe-daemon.js
CHANGED
|
@@ -985,6 +985,7 @@ __export(employees_exports, {
|
|
|
985
985
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
986
986
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
987
987
|
hasRole: () => hasRole,
|
|
988
|
+
hireEmployee: () => hireEmployee,
|
|
988
989
|
isCoordinatorName: () => isCoordinatorName,
|
|
989
990
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
990
991
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -1095,6 +1096,52 @@ function addEmployee(employees, employee) {
|
|
|
1095
1096
|
}
|
|
1096
1097
|
return [...employees, normalized];
|
|
1097
1098
|
}
|
|
1099
|
+
function appendToCoordinatorTeam(employee) {
|
|
1100
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1101
|
+
if (!coordinator) return;
|
|
1102
|
+
const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1103
|
+
if (!existsSync5(idPath)) return;
|
|
1104
|
+
const content = readFileSync5(idPath, "utf-8");
|
|
1105
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1106
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1107
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
1108
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
1109
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
1110
|
+
const entry = `
|
|
1111
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
1112
|
+
`;
|
|
1113
|
+
let updated;
|
|
1114
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
1115
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
1116
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
1117
|
+
} else {
|
|
1118
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
1119
|
+
}
|
|
1120
|
+
writeFileSync4(idPath, updated, "utf-8");
|
|
1121
|
+
}
|
|
1122
|
+
function capitalize(s) {
|
|
1123
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1124
|
+
}
|
|
1125
|
+
async function hireEmployee(employee) {
|
|
1126
|
+
const employees = await loadEmployees();
|
|
1127
|
+
const updated = addEmployee(employees, employee);
|
|
1128
|
+
await saveEmployees(updated);
|
|
1129
|
+
try {
|
|
1130
|
+
appendToCoordinatorTeam(employee);
|
|
1131
|
+
} catch {
|
|
1132
|
+
}
|
|
1133
|
+
try {
|
|
1134
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
1135
|
+
const config = loadAgentConfig2();
|
|
1136
|
+
const name = employee.name.toLowerCase();
|
|
1137
|
+
if (!config[name] && config["default"]) {
|
|
1138
|
+
config[name] = { ...config["default"] };
|
|
1139
|
+
saveAgentConfig2(config);
|
|
1140
|
+
}
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
return updated;
|
|
1144
|
+
}
|
|
1098
1145
|
async function normalizeRosterCase(rosterPath) {
|
|
1099
1146
|
const employees = await loadEmployees(rosterPath);
|
|
1100
1147
|
let changed = false;
|
|
@@ -1165,7 +1212,7 @@ function registerBinSymlinks(name) {
|
|
|
1165
1212
|
}
|
|
1166
1213
|
return { created, skipped, errors };
|
|
1167
1214
|
}
|
|
1168
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
1215
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
1169
1216
|
var init_employees = __esm({
|
|
1170
1217
|
"src/lib/employees.ts"() {
|
|
1171
1218
|
"use strict";
|
|
@@ -1174,16 +1221,602 @@ var init_employees = __esm({
|
|
|
1174
1221
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1175
1222
|
COORDINATOR_ROLE = "COO";
|
|
1176
1223
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1224
|
+
IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
1225
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
// src/lib/database-adapter.ts
|
|
1230
|
+
import os5 from "os";
|
|
1231
|
+
import path6 from "path";
|
|
1232
|
+
import { createRequire } from "module";
|
|
1233
|
+
import { pathToFileURL } from "url";
|
|
1234
|
+
function quotedIdentifier(identifier) {
|
|
1235
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1236
|
+
}
|
|
1237
|
+
function unqualifiedTableName(name) {
|
|
1238
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1239
|
+
const parts = raw.split(".");
|
|
1240
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1241
|
+
}
|
|
1242
|
+
function stripTrailingSemicolon(sql) {
|
|
1243
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1244
|
+
}
|
|
1245
|
+
function appendClause(sql, clause) {
|
|
1246
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1247
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1248
|
+
if (!returningMatch) {
|
|
1249
|
+
return `${trimmed}${clause}`;
|
|
1250
|
+
}
|
|
1251
|
+
const idx = returningMatch.index;
|
|
1252
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1253
|
+
}
|
|
1254
|
+
function normalizeStatement(stmt) {
|
|
1255
|
+
if (typeof stmt === "string") {
|
|
1256
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1257
|
+
}
|
|
1258
|
+
const sql = stmt.sql;
|
|
1259
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1260
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1261
|
+
}
|
|
1262
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1263
|
+
}
|
|
1264
|
+
function rewriteBooleanLiterals(sql) {
|
|
1265
|
+
let out = sql;
|
|
1266
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1267
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1268
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1269
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1270
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1271
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1272
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1273
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1274
|
+
}
|
|
1275
|
+
return out;
|
|
1276
|
+
}
|
|
1277
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1278
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1279
|
+
return sql;
|
|
1280
|
+
}
|
|
1281
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1282
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1283
|
+
}
|
|
1284
|
+
function rewriteInsertOrReplace(sql) {
|
|
1285
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1286
|
+
if (!match) {
|
|
1287
|
+
return sql;
|
|
1288
|
+
}
|
|
1289
|
+
const rawTable = match[1];
|
|
1290
|
+
const rawColumns = match[2];
|
|
1291
|
+
const remainder = match[3];
|
|
1292
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1293
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1294
|
+
if (!conflictKeys?.length) {
|
|
1295
|
+
return sql;
|
|
1296
|
+
}
|
|
1297
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1298
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1299
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1300
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1301
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1302
|
+
}
|
|
1303
|
+
function rewriteSql(sql) {
|
|
1304
|
+
let out = sql;
|
|
1305
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1306
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1307
|
+
out = rewriteBooleanLiterals(out);
|
|
1308
|
+
out = rewriteInsertOrReplace(out);
|
|
1309
|
+
out = rewriteInsertOrIgnore(out);
|
|
1310
|
+
return stripTrailingSemicolon(out);
|
|
1311
|
+
}
|
|
1312
|
+
function toBoolean(value) {
|
|
1313
|
+
if (value === null || value === void 0) return value;
|
|
1314
|
+
if (typeof value === "boolean") return value;
|
|
1315
|
+
if (typeof value === "number") return value !== 0;
|
|
1316
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1317
|
+
if (typeof value === "string") {
|
|
1318
|
+
const normalized = value.trim().toLowerCase();
|
|
1319
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1320
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1321
|
+
}
|
|
1322
|
+
return Boolean(value);
|
|
1323
|
+
}
|
|
1324
|
+
function countQuestionMarks(sql, end) {
|
|
1325
|
+
let count = 0;
|
|
1326
|
+
let inSingle = false;
|
|
1327
|
+
let inDouble = false;
|
|
1328
|
+
let inLineComment = false;
|
|
1329
|
+
let inBlockComment = false;
|
|
1330
|
+
for (let i = 0; i < end; i++) {
|
|
1331
|
+
const ch = sql[i];
|
|
1332
|
+
const next = sql[i + 1];
|
|
1333
|
+
if (inLineComment) {
|
|
1334
|
+
if (ch === "\n") inLineComment = false;
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
if (inBlockComment) {
|
|
1338
|
+
if (ch === "*" && next === "/") {
|
|
1339
|
+
inBlockComment = false;
|
|
1340
|
+
i += 1;
|
|
1341
|
+
}
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1345
|
+
inLineComment = true;
|
|
1346
|
+
i += 1;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1350
|
+
inBlockComment = true;
|
|
1351
|
+
i += 1;
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1355
|
+
inSingle = !inSingle;
|
|
1356
|
+
continue;
|
|
1357
|
+
}
|
|
1358
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1359
|
+
inDouble = !inDouble;
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1363
|
+
count += 1;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return count;
|
|
1367
|
+
}
|
|
1368
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1369
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1370
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1371
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1372
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1373
|
+
const matchText = match[0];
|
|
1374
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1375
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return indexes;
|
|
1379
|
+
}
|
|
1380
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1381
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1382
|
+
if (!match) return;
|
|
1383
|
+
const rawTable = match[1];
|
|
1384
|
+
const rawColumns = match[2];
|
|
1385
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1386
|
+
if (!boolColumns?.size) return;
|
|
1387
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1388
|
+
for (const [index, column] of columns.entries()) {
|
|
1389
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1390
|
+
args[index] = toBoolean(args[index]);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1395
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1396
|
+
if (!match) return;
|
|
1397
|
+
const rawTable = match[1];
|
|
1398
|
+
const setClause = match[2];
|
|
1399
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1400
|
+
if (!boolColumns?.size) return;
|
|
1401
|
+
const assignments = setClause.split(",");
|
|
1402
|
+
let placeholderIndex = 0;
|
|
1403
|
+
for (const assignment of assignments) {
|
|
1404
|
+
if (!assignment.includes("?")) continue;
|
|
1405
|
+
placeholderIndex += 1;
|
|
1406
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1407
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1408
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function coerceBooleanArgs(sql, args) {
|
|
1413
|
+
const nextArgs = [...args];
|
|
1414
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1415
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1416
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1417
|
+
for (const index of placeholderIndexes) {
|
|
1418
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1419
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
return nextArgs;
|
|
1423
|
+
}
|
|
1424
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1425
|
+
let out = "";
|
|
1426
|
+
let placeholder = 0;
|
|
1427
|
+
let inSingle = false;
|
|
1428
|
+
let inDouble = false;
|
|
1429
|
+
let inLineComment = false;
|
|
1430
|
+
let inBlockComment = false;
|
|
1431
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1432
|
+
const ch = sql[i];
|
|
1433
|
+
const next = sql[i + 1];
|
|
1434
|
+
if (inLineComment) {
|
|
1435
|
+
out += ch;
|
|
1436
|
+
if (ch === "\n") inLineComment = false;
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
if (inBlockComment) {
|
|
1440
|
+
out += ch;
|
|
1441
|
+
if (ch === "*" && next === "/") {
|
|
1442
|
+
out += next;
|
|
1443
|
+
inBlockComment = false;
|
|
1444
|
+
i += 1;
|
|
1445
|
+
}
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1449
|
+
out += ch + next;
|
|
1450
|
+
inLineComment = true;
|
|
1451
|
+
i += 1;
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1455
|
+
out += ch + next;
|
|
1456
|
+
inBlockComment = true;
|
|
1457
|
+
i += 1;
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1461
|
+
inSingle = !inSingle;
|
|
1462
|
+
out += ch;
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1466
|
+
inDouble = !inDouble;
|
|
1467
|
+
out += ch;
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1471
|
+
placeholder += 1;
|
|
1472
|
+
out += `$${placeholder}`;
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
out += ch;
|
|
1476
|
+
}
|
|
1477
|
+
return out;
|
|
1478
|
+
}
|
|
1479
|
+
function translateStatementForPostgres(stmt) {
|
|
1480
|
+
const normalized = normalizeStatement(stmt);
|
|
1481
|
+
if (normalized.kind === "named") {
|
|
1482
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1483
|
+
}
|
|
1484
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1485
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1486
|
+
return {
|
|
1487
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1488
|
+
args: coercedArgs
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function shouldBypassPostgres(stmt) {
|
|
1492
|
+
const normalized = normalizeStatement(stmt);
|
|
1493
|
+
if (normalized.kind === "named") {
|
|
1494
|
+
return true;
|
|
1495
|
+
}
|
|
1496
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1497
|
+
}
|
|
1498
|
+
function shouldFallbackOnError(error) {
|
|
1499
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1500
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1501
|
+
}
|
|
1502
|
+
function isReadQuery(sql) {
|
|
1503
|
+
const trimmed = sql.trimStart();
|
|
1504
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1505
|
+
}
|
|
1506
|
+
function buildRow(row, columns) {
|
|
1507
|
+
const values = columns.map((column) => row[column]);
|
|
1508
|
+
return Object.assign(values, row);
|
|
1509
|
+
}
|
|
1510
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1511
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1512
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1513
|
+
return {
|
|
1514
|
+
columns,
|
|
1515
|
+
columnTypes: columns.map(() => ""),
|
|
1516
|
+
rows: resultRows,
|
|
1517
|
+
rowsAffected,
|
|
1518
|
+
lastInsertRowid: void 0,
|
|
1519
|
+
toJSON() {
|
|
1520
|
+
return {
|
|
1521
|
+
columns,
|
|
1522
|
+
columnTypes: columns.map(() => ""),
|
|
1523
|
+
rows,
|
|
1524
|
+
rowsAffected,
|
|
1525
|
+
lastInsertRowid: void 0
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
async function loadPrismaClient() {
|
|
1531
|
+
if (!prismaClientPromise) {
|
|
1532
|
+
prismaClientPromise = (async () => {
|
|
1533
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1534
|
+
if (explicitPath) {
|
|
1535
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1536
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1537
|
+
if (!PrismaClient2) {
|
|
1538
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1539
|
+
}
|
|
1540
|
+
return new PrismaClient2();
|
|
1541
|
+
}
|
|
1542
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
|
|
1543
|
+
const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1544
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1545
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1546
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1547
|
+
if (!PrismaClient) {
|
|
1548
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1549
|
+
}
|
|
1550
|
+
return new PrismaClient();
|
|
1551
|
+
})();
|
|
1552
|
+
}
|
|
1553
|
+
return prismaClientPromise;
|
|
1554
|
+
}
|
|
1555
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1556
|
+
if (!compatibilityBootstrapPromise) {
|
|
1557
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1558
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1559
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1560
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1561
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1562
|
+
relation
|
|
1563
|
+
);
|
|
1564
|
+
if (!rows[0]?.regclass) {
|
|
1565
|
+
continue;
|
|
1566
|
+
}
|
|
1567
|
+
await prisma.$executeRawUnsafe(
|
|
1568
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
})();
|
|
1572
|
+
}
|
|
1573
|
+
return compatibilityBootstrapPromise;
|
|
1574
|
+
}
|
|
1575
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1576
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1577
|
+
if (isReadQuery(translated.sql)) {
|
|
1578
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1579
|
+
translated.sql,
|
|
1580
|
+
...translated.args
|
|
1581
|
+
);
|
|
1582
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1583
|
+
}
|
|
1584
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1585
|
+
return buildResultSet([], rowsAffected);
|
|
1586
|
+
}
|
|
1587
|
+
function splitSqlStatements(sql) {
|
|
1588
|
+
const parts = [];
|
|
1589
|
+
let current = "";
|
|
1590
|
+
let inSingle = false;
|
|
1591
|
+
let inDouble = false;
|
|
1592
|
+
let inLineComment = false;
|
|
1593
|
+
let inBlockComment = false;
|
|
1594
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1595
|
+
const ch = sql[i];
|
|
1596
|
+
const next = sql[i + 1];
|
|
1597
|
+
if (inLineComment) {
|
|
1598
|
+
current += ch;
|
|
1599
|
+
if (ch === "\n") inLineComment = false;
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (inBlockComment) {
|
|
1603
|
+
current += ch;
|
|
1604
|
+
if (ch === "*" && next === "/") {
|
|
1605
|
+
current += next;
|
|
1606
|
+
inBlockComment = false;
|
|
1607
|
+
i += 1;
|
|
1608
|
+
}
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1612
|
+
current += ch + next;
|
|
1613
|
+
inLineComment = true;
|
|
1614
|
+
i += 1;
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1618
|
+
current += ch + next;
|
|
1619
|
+
inBlockComment = true;
|
|
1620
|
+
i += 1;
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1624
|
+
inSingle = !inSingle;
|
|
1625
|
+
current += ch;
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1629
|
+
inDouble = !inDouble;
|
|
1630
|
+
current += ch;
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1633
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1634
|
+
if (current.trim()) {
|
|
1635
|
+
parts.push(current.trim());
|
|
1636
|
+
}
|
|
1637
|
+
current = "";
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
current += ch;
|
|
1641
|
+
}
|
|
1642
|
+
if (current.trim()) {
|
|
1643
|
+
parts.push(current.trim());
|
|
1644
|
+
}
|
|
1645
|
+
return parts;
|
|
1646
|
+
}
|
|
1647
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1648
|
+
const prisma = await loadPrismaClient();
|
|
1649
|
+
await ensureCompatibilityViews(prisma);
|
|
1650
|
+
let closed = false;
|
|
1651
|
+
let adapter;
|
|
1652
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1653
|
+
if (!fallbackClient) {
|
|
1654
|
+
if (error) throw error;
|
|
1655
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1656
|
+
}
|
|
1657
|
+
if (error) {
|
|
1658
|
+
process.stderr.write(
|
|
1659
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1660
|
+
`
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
return fallbackClient.execute(stmt);
|
|
1664
|
+
};
|
|
1665
|
+
adapter = {
|
|
1666
|
+
async execute(stmt) {
|
|
1667
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1668
|
+
return fallbackExecute(stmt);
|
|
1669
|
+
}
|
|
1670
|
+
try {
|
|
1671
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
if (shouldFallbackOnError(error)) {
|
|
1674
|
+
return fallbackExecute(stmt, error);
|
|
1675
|
+
}
|
|
1676
|
+
throw error;
|
|
1677
|
+
}
|
|
1678
|
+
},
|
|
1679
|
+
async batch(stmts, mode) {
|
|
1680
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1681
|
+
if (!fallbackClient) {
|
|
1682
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1683
|
+
}
|
|
1684
|
+
return fallbackClient.batch(stmts, mode);
|
|
1685
|
+
}
|
|
1686
|
+
try {
|
|
1687
|
+
if (prisma.$transaction) {
|
|
1688
|
+
return await prisma.$transaction(async (tx) => {
|
|
1689
|
+
const results2 = [];
|
|
1690
|
+
for (const stmt of stmts) {
|
|
1691
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1692
|
+
}
|
|
1693
|
+
return results2;
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
const results = [];
|
|
1697
|
+
for (const stmt of stmts) {
|
|
1698
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1699
|
+
}
|
|
1700
|
+
return results;
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1703
|
+
process.stderr.write(
|
|
1704
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1705
|
+
`
|
|
1706
|
+
);
|
|
1707
|
+
return fallbackClient.batch(stmts, mode);
|
|
1708
|
+
}
|
|
1709
|
+
throw error;
|
|
1710
|
+
}
|
|
1711
|
+
},
|
|
1712
|
+
async migrate(stmts) {
|
|
1713
|
+
if (fallbackClient) {
|
|
1714
|
+
return fallbackClient.migrate(stmts);
|
|
1715
|
+
}
|
|
1716
|
+
return adapter.batch(stmts, "deferred");
|
|
1717
|
+
},
|
|
1718
|
+
async transaction(mode) {
|
|
1719
|
+
if (!fallbackClient) {
|
|
1720
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1721
|
+
}
|
|
1722
|
+
return fallbackClient.transaction(mode);
|
|
1723
|
+
},
|
|
1724
|
+
async executeMultiple(sql) {
|
|
1725
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1726
|
+
return fallbackClient.executeMultiple(sql);
|
|
1727
|
+
}
|
|
1728
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1729
|
+
await adapter.execute(statement);
|
|
1730
|
+
}
|
|
1731
|
+
},
|
|
1732
|
+
async sync() {
|
|
1733
|
+
if (fallbackClient) {
|
|
1734
|
+
return fallbackClient.sync();
|
|
1735
|
+
}
|
|
1736
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1737
|
+
},
|
|
1738
|
+
close() {
|
|
1739
|
+
closed = true;
|
|
1740
|
+
prismaClientPromise = null;
|
|
1741
|
+
compatibilityBootstrapPromise = null;
|
|
1742
|
+
void prisma.$disconnect?.();
|
|
1743
|
+
},
|
|
1744
|
+
get closed() {
|
|
1745
|
+
return closed;
|
|
1746
|
+
},
|
|
1747
|
+
get protocol() {
|
|
1748
|
+
return "prisma-postgres";
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
return adapter;
|
|
1752
|
+
}
|
|
1753
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1754
|
+
var init_database_adapter = __esm({
|
|
1755
|
+
"src/lib/database-adapter.ts"() {
|
|
1756
|
+
"use strict";
|
|
1757
|
+
VIEW_MAPPINGS = [
|
|
1758
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1759
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1760
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1761
|
+
{ view: "entities", source: "memory.entities" },
|
|
1762
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1763
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1764
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1765
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1766
|
+
{ view: "messages", source: "memory.messages" },
|
|
1767
|
+
{ view: "users", source: "wiki.users" },
|
|
1768
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1769
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1770
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1771
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1772
|
+
];
|
|
1773
|
+
UPSERT_KEYS = {
|
|
1774
|
+
memories: ["id"],
|
|
1775
|
+
tasks: ["id"],
|
|
1776
|
+
behaviors: ["id"],
|
|
1777
|
+
entities: ["id"],
|
|
1778
|
+
relationships: ["id"],
|
|
1779
|
+
entity_aliases: ["alias"],
|
|
1780
|
+
notifications: ["id"],
|
|
1781
|
+
messages: ["id"],
|
|
1782
|
+
users: ["id"],
|
|
1783
|
+
workspaces: ["id"],
|
|
1784
|
+
workspace_users: ["id"],
|
|
1785
|
+
documents: ["id"],
|
|
1786
|
+
chats: ["id"]
|
|
1787
|
+
};
|
|
1788
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1789
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1790
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1791
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1792
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1793
|
+
};
|
|
1794
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1795
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1796
|
+
);
|
|
1797
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1798
|
+
/\bPRAGMA\b/i,
|
|
1799
|
+
/\bsqlite_master\b/i,
|
|
1800
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1801
|
+
/\bMATCH\b/i,
|
|
1802
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1803
|
+
/\bjson_extract\s*\(/i,
|
|
1804
|
+
/\bjulianday\s*\(/i,
|
|
1805
|
+
/\bstrftime\s*\(/i,
|
|
1806
|
+
/\blast_insert_rowid\s*\(/i
|
|
1807
|
+
];
|
|
1808
|
+
prismaClientPromise = null;
|
|
1809
|
+
compatibilityBootstrapPromise = null;
|
|
1177
1810
|
}
|
|
1178
1811
|
});
|
|
1179
1812
|
|
|
1180
1813
|
// src/lib/exe-daemon-client.ts
|
|
1181
1814
|
import net from "net";
|
|
1182
|
-
import
|
|
1815
|
+
import os6 from "os";
|
|
1183
1816
|
import { spawn } from "child_process";
|
|
1184
1817
|
import { randomUUID } from "crypto";
|
|
1185
1818
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
1186
|
-
import
|
|
1819
|
+
import path7 from "path";
|
|
1187
1820
|
import { fileURLToPath } from "url";
|
|
1188
1821
|
function handleData(chunk) {
|
|
1189
1822
|
_buffer += chunk.toString();
|
|
@@ -1234,17 +1867,17 @@ function cleanupStaleFiles() {
|
|
|
1234
1867
|
}
|
|
1235
1868
|
}
|
|
1236
1869
|
function findPackageRoot() {
|
|
1237
|
-
let dir =
|
|
1238
|
-
const { root } =
|
|
1870
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
1871
|
+
const { root } = path7.parse(dir);
|
|
1239
1872
|
while (dir !== root) {
|
|
1240
|
-
if (existsSync6(
|
|
1241
|
-
dir =
|
|
1873
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
1874
|
+
dir = path7.dirname(dir);
|
|
1242
1875
|
}
|
|
1243
1876
|
return null;
|
|
1244
1877
|
}
|
|
1245
1878
|
function spawnDaemon() {
|
|
1246
|
-
const freeGB =
|
|
1247
|
-
const totalGB =
|
|
1879
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1880
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
1248
1881
|
if (totalGB <= 8) {
|
|
1249
1882
|
process.stderr.write(
|
|
1250
1883
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -1264,7 +1897,7 @@ function spawnDaemon() {
|
|
|
1264
1897
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1265
1898
|
return;
|
|
1266
1899
|
}
|
|
1267
|
-
const daemonPath =
|
|
1900
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1268
1901
|
if (!existsSync6(daemonPath)) {
|
|
1269
1902
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1270
1903
|
`);
|
|
@@ -1273,7 +1906,7 @@ function spawnDaemon() {
|
|
|
1273
1906
|
const resolvedPath = daemonPath;
|
|
1274
1907
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1275
1908
|
`);
|
|
1276
|
-
const logPath =
|
|
1909
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
1277
1910
|
let stderrFd = "ignore";
|
|
1278
1911
|
try {
|
|
1279
1912
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1424,74 +2057,123 @@ async function pingDaemon() {
|
|
|
1424
2057
|
return null;
|
|
1425
2058
|
}
|
|
1426
2059
|
function killAndRespawnDaemon() {
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
2060
|
+
if (!acquireSpawnLock()) {
|
|
2061
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
2062
|
+
if (_socket) {
|
|
2063
|
+
_socket.destroy();
|
|
2064
|
+
_socket = null;
|
|
2065
|
+
}
|
|
2066
|
+
_connected = false;
|
|
2067
|
+
_buffer = "";
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
try {
|
|
2071
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2072
|
+
if (existsSync6(PID_PATH)) {
|
|
2073
|
+
try {
|
|
2074
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
2075
|
+
if (pid > 0) {
|
|
2076
|
+
try {
|
|
2077
|
+
process.kill(pid, "SIGKILL");
|
|
2078
|
+
} catch {
|
|
2079
|
+
}
|
|
1435
2080
|
}
|
|
2081
|
+
} catch {
|
|
1436
2082
|
}
|
|
2083
|
+
}
|
|
2084
|
+
if (_socket) {
|
|
2085
|
+
_socket.destroy();
|
|
2086
|
+
_socket = null;
|
|
2087
|
+
}
|
|
2088
|
+
_connected = false;
|
|
2089
|
+
_buffer = "";
|
|
2090
|
+
try {
|
|
2091
|
+
unlinkSync2(PID_PATH);
|
|
1437
2092
|
} catch {
|
|
1438
2093
|
}
|
|
2094
|
+
try {
|
|
2095
|
+
unlinkSync2(SOCKET_PATH);
|
|
2096
|
+
} catch {
|
|
2097
|
+
}
|
|
2098
|
+
spawnDaemon();
|
|
2099
|
+
} finally {
|
|
2100
|
+
releaseSpawnLock();
|
|
1439
2101
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
_socket = null;
|
|
1443
|
-
}
|
|
1444
|
-
_connected = false;
|
|
1445
|
-
_buffer = "";
|
|
2102
|
+
}
|
|
2103
|
+
function isDaemonTooYoung() {
|
|
1446
2104
|
try {
|
|
1447
|
-
|
|
2105
|
+
const stat = statSync(PID_PATH);
|
|
2106
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
1448
2107
|
} catch {
|
|
2108
|
+
return false;
|
|
1449
2109
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
2110
|
+
}
|
|
2111
|
+
async function retryThenRestart(doRequest, label) {
|
|
2112
|
+
const result = await doRequest();
|
|
2113
|
+
if (!result.error) {
|
|
2114
|
+
_consecutiveFailures = 0;
|
|
2115
|
+
return result;
|
|
2116
|
+
}
|
|
2117
|
+
_consecutiveFailures++;
|
|
2118
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
2119
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
2120
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
2121
|
+
`);
|
|
2122
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
2123
|
+
if (!_connected) {
|
|
2124
|
+
if (!await connectToSocket()) continue;
|
|
2125
|
+
}
|
|
2126
|
+
const retry = await doRequest();
|
|
2127
|
+
if (!retry.error) {
|
|
2128
|
+
_consecutiveFailures = 0;
|
|
2129
|
+
return retry;
|
|
2130
|
+
}
|
|
2131
|
+
_consecutiveFailures++;
|
|
2132
|
+
}
|
|
2133
|
+
if (isDaemonTooYoung()) {
|
|
2134
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
2135
|
+
`);
|
|
2136
|
+
return { error: result.error };
|
|
2137
|
+
}
|
|
2138
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
2139
|
+
`);
|
|
2140
|
+
killAndRespawnDaemon();
|
|
2141
|
+
const start = Date.now();
|
|
2142
|
+
let delay2 = 200;
|
|
2143
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2144
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2145
|
+
if (await connectToSocket()) break;
|
|
2146
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1453
2147
|
}
|
|
1454
|
-
|
|
2148
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
2149
|
+
const final = await doRequest();
|
|
2150
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
2151
|
+
return final;
|
|
1455
2152
|
}
|
|
1456
2153
|
async function embedViaClient(text, priority = "high") {
|
|
1457
2154
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1458
2155
|
_requestCount++;
|
|
1459
2156
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1460
2157
|
const health = await pingDaemon();
|
|
1461
|
-
if (!health) {
|
|
2158
|
+
if (!health && !isDaemonTooYoung()) {
|
|
1462
2159
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1463
2160
|
`);
|
|
1464
2161
|
killAndRespawnDaemon();
|
|
1465
2162
|
const start = Date.now();
|
|
1466
|
-
let
|
|
2163
|
+
let d = 200;
|
|
1467
2164
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1468
|
-
await new Promise((r) => setTimeout(r,
|
|
2165
|
+
await new Promise((r) => setTimeout(r, d));
|
|
1469
2166
|
if (await connectToSocket()) break;
|
|
1470
|
-
|
|
2167
|
+
d = Math.min(d * 2, 3e3);
|
|
1471
2168
|
}
|
|
1472
2169
|
if (!_connected) return null;
|
|
1473
2170
|
}
|
|
1474
2171
|
}
|
|
1475
|
-
const result = await
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
killAndRespawnDaemon();
|
|
1481
|
-
const start = Date.now();
|
|
1482
|
-
let delay2 = 200;
|
|
1483
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1484
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
1485
|
-
if (await connectToSocket()) break;
|
|
1486
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1487
|
-
}
|
|
1488
|
-
if (!_connected) return null;
|
|
1489
|
-
const retry = await sendRequest([text], priority);
|
|
1490
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
1491
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
1492
|
-
`);
|
|
1493
|
-
}
|
|
1494
|
-
return null;
|
|
2172
|
+
const result = await retryThenRestart(
|
|
2173
|
+
() => sendRequest([text], priority),
|
|
2174
|
+
"Embed"
|
|
2175
|
+
);
|
|
2176
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
1495
2177
|
}
|
|
1496
2178
|
function disconnectClient() {
|
|
1497
2179
|
if (_socket) {
|
|
@@ -1509,14 +2191,14 @@ function disconnectClient() {
|
|
|
1509
2191
|
function isClientConnected() {
|
|
1510
2192
|
return _connected;
|
|
1511
2193
|
}
|
|
1512
|
-
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;
|
|
2194
|
+
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;
|
|
1513
2195
|
var init_exe_daemon_client = __esm({
|
|
1514
2196
|
"src/lib/exe-daemon-client.ts"() {
|
|
1515
2197
|
"use strict";
|
|
1516
2198
|
init_config();
|
|
1517
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1518
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1519
|
-
SPAWN_LOCK_PATH =
|
|
2199
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
2200
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
2201
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1520
2202
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1521
2203
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1522
2204
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1524,7 +2206,11 @@ var init_exe_daemon_client = __esm({
|
|
|
1524
2206
|
_connected = false;
|
|
1525
2207
|
_buffer = "";
|
|
1526
2208
|
_requestCount = 0;
|
|
2209
|
+
_consecutiveFailures = 0;
|
|
1527
2210
|
HEALTH_CHECK_INTERVAL = 100;
|
|
2211
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
2212
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
2213
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
1528
2214
|
_pending = /* @__PURE__ */ new Map();
|
|
1529
2215
|
MAX_BUFFER = 1e7;
|
|
1530
2216
|
}
|
|
@@ -1536,7 +2222,7 @@ __export(db_daemon_client_exports, {
|
|
|
1536
2222
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1537
2223
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1538
2224
|
});
|
|
1539
|
-
function
|
|
2225
|
+
function normalizeStatement2(stmt) {
|
|
1540
2226
|
if (typeof stmt === "string") {
|
|
1541
2227
|
return { sql: stmt, args: [] };
|
|
1542
2228
|
}
|
|
@@ -1560,7 +2246,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1560
2246
|
if (!_useDaemon || !isClientConnected()) {
|
|
1561
2247
|
return fallbackClient.execute(stmt);
|
|
1562
2248
|
}
|
|
1563
|
-
const { sql, args } =
|
|
2249
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1564
2250
|
const response = await sendDaemonRequest({
|
|
1565
2251
|
type: "db-execute",
|
|
1566
2252
|
sql,
|
|
@@ -1585,7 +2271,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1585
2271
|
if (!_useDaemon || !isClientConnected()) {
|
|
1586
2272
|
return fallbackClient.batch(stmts, mode);
|
|
1587
2273
|
}
|
|
1588
|
-
const statements = stmts.map(
|
|
2274
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1589
2275
|
const response = await sendDaemonRequest({
|
|
1590
2276
|
type: "db-batch",
|
|
1591
2277
|
statements,
|
|
@@ -1680,6 +2366,18 @@ __export(database_exports, {
|
|
|
1680
2366
|
});
|
|
1681
2367
|
import { createClient } from "@libsql/client";
|
|
1682
2368
|
async function initDatabase(config) {
|
|
2369
|
+
if (_walCheckpointTimer) {
|
|
2370
|
+
clearInterval(_walCheckpointTimer);
|
|
2371
|
+
_walCheckpointTimer = null;
|
|
2372
|
+
}
|
|
2373
|
+
if (_daemonClient) {
|
|
2374
|
+
_daemonClient.close();
|
|
2375
|
+
_daemonClient = null;
|
|
2376
|
+
}
|
|
2377
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2378
|
+
_adapterClient.close();
|
|
2379
|
+
}
|
|
2380
|
+
_adapterClient = null;
|
|
1683
2381
|
if (_client) {
|
|
1684
2382
|
_client.close();
|
|
1685
2383
|
_client = null;
|
|
@@ -1693,6 +2391,7 @@ async function initDatabase(config) {
|
|
|
1693
2391
|
}
|
|
1694
2392
|
_client = createClient(opts);
|
|
1695
2393
|
_resilientClient = wrapWithRetry(_client);
|
|
2394
|
+
_adapterClient = _resilientClient;
|
|
1696
2395
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1697
2396
|
});
|
|
1698
2397
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1703,14 +2402,20 @@ async function initDatabase(config) {
|
|
|
1703
2402
|
});
|
|
1704
2403
|
}, 3e4);
|
|
1705
2404
|
_walCheckpointTimer.unref();
|
|
2405
|
+
if (process.env.DATABASE_URL) {
|
|
2406
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2407
|
+
}
|
|
1706
2408
|
}
|
|
1707
2409
|
function isInitialized() {
|
|
1708
|
-
return _client !== null;
|
|
2410
|
+
return _adapterClient !== null || _client !== null;
|
|
1709
2411
|
}
|
|
1710
2412
|
function getClient() {
|
|
1711
|
-
if (!
|
|
2413
|
+
if (!_adapterClient) {
|
|
1712
2414
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1713
2415
|
}
|
|
2416
|
+
if (process.env.DATABASE_URL) {
|
|
2417
|
+
return _adapterClient;
|
|
2418
|
+
}
|
|
1714
2419
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1715
2420
|
return _resilientClient;
|
|
1716
2421
|
}
|
|
@@ -1720,6 +2425,7 @@ function getClient() {
|
|
|
1720
2425
|
return _resilientClient;
|
|
1721
2426
|
}
|
|
1722
2427
|
async function initDaemonClient() {
|
|
2428
|
+
if (process.env.DATABASE_URL) return;
|
|
1723
2429
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1724
2430
|
if (!_resilientClient) return;
|
|
1725
2431
|
try {
|
|
@@ -2664,26 +3370,36 @@ async function ensureSchema() {
|
|
|
2664
3370
|
}
|
|
2665
3371
|
}
|
|
2666
3372
|
async function disposeDatabase() {
|
|
3373
|
+
if (_walCheckpointTimer) {
|
|
3374
|
+
clearInterval(_walCheckpointTimer);
|
|
3375
|
+
_walCheckpointTimer = null;
|
|
3376
|
+
}
|
|
2667
3377
|
if (_daemonClient) {
|
|
2668
3378
|
_daemonClient.close();
|
|
2669
3379
|
_daemonClient = null;
|
|
2670
3380
|
}
|
|
3381
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
3382
|
+
_adapterClient.close();
|
|
3383
|
+
}
|
|
3384
|
+
_adapterClient = null;
|
|
2671
3385
|
if (_client) {
|
|
2672
3386
|
_client.close();
|
|
2673
3387
|
_client = null;
|
|
2674
3388
|
_resilientClient = null;
|
|
2675
3389
|
}
|
|
2676
3390
|
}
|
|
2677
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
3391
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2678
3392
|
var init_database = __esm({
|
|
2679
3393
|
"src/lib/database.ts"() {
|
|
2680
3394
|
"use strict";
|
|
2681
3395
|
init_db_retry();
|
|
2682
3396
|
init_employees();
|
|
3397
|
+
init_database_adapter();
|
|
2683
3398
|
_client = null;
|
|
2684
3399
|
_resilientClient = null;
|
|
2685
3400
|
_walCheckpointTimer = null;
|
|
2686
3401
|
_daemonClient = null;
|
|
3402
|
+
_adapterClient = null;
|
|
2687
3403
|
initTurso = initDatabase;
|
|
2688
3404
|
disposeTurso = disposeDatabase;
|
|
2689
3405
|
}
|
|
@@ -2692,16 +3408,16 @@ var init_database = __esm({
|
|
|
2692
3408
|
// src/lib/license.ts
|
|
2693
3409
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2694
3410
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2695
|
-
import
|
|
3411
|
+
import path8 from "path";
|
|
2696
3412
|
import { jwtVerify, importSPKI } from "jose";
|
|
2697
3413
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2698
3414
|
var init_license = __esm({
|
|
2699
3415
|
"src/lib/license.ts"() {
|
|
2700
3416
|
"use strict";
|
|
2701
3417
|
init_config();
|
|
2702
|
-
LICENSE_PATH =
|
|
2703
|
-
CACHE_PATH =
|
|
2704
|
-
DEVICE_ID_PATH =
|
|
3418
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
3419
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
3420
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2705
3421
|
PLAN_LIMITS = {
|
|
2706
3422
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2707
3423
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2714,7 +3430,7 @@ var init_license = __esm({
|
|
|
2714
3430
|
|
|
2715
3431
|
// src/lib/plan-limits.ts
|
|
2716
3432
|
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2717
|
-
import
|
|
3433
|
+
import path9 from "path";
|
|
2718
3434
|
function getLicenseSync() {
|
|
2719
3435
|
try {
|
|
2720
3436
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
@@ -2786,14 +3502,14 @@ var init_plan_limits = __esm({
|
|
|
2786
3502
|
this.name = "PlanLimitError";
|
|
2787
3503
|
}
|
|
2788
3504
|
};
|
|
2789
|
-
CACHE_PATH2 =
|
|
3505
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2790
3506
|
}
|
|
2791
3507
|
});
|
|
2792
3508
|
|
|
2793
3509
|
// src/lib/notifications.ts
|
|
2794
3510
|
import crypto from "crypto";
|
|
2795
|
-
import
|
|
2796
|
-
import
|
|
3511
|
+
import path10 from "path";
|
|
3512
|
+
import os7 from "os";
|
|
2797
3513
|
import {
|
|
2798
3514
|
readFileSync as readFileSync9,
|
|
2799
3515
|
readdirSync,
|
|
@@ -3027,8 +3743,8 @@ var init_state_bus = __esm({
|
|
|
3027
3743
|
|
|
3028
3744
|
// src/lib/tasks-crud.ts
|
|
3029
3745
|
import crypto3 from "crypto";
|
|
3030
|
-
import
|
|
3031
|
-
import
|
|
3746
|
+
import path11 from "path";
|
|
3747
|
+
import os8 from "os";
|
|
3032
3748
|
import { execSync as execSync5 } from "child_process";
|
|
3033
3749
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3034
3750
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
@@ -3206,8 +3922,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3206
3922
|
}
|
|
3207
3923
|
if (input.baseDir) {
|
|
3208
3924
|
try {
|
|
3209
|
-
await mkdir3(
|
|
3210
|
-
await mkdir3(
|
|
3925
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3926
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3211
3927
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3212
3928
|
await ensureGitignoreExe(input.baseDir);
|
|
3213
3929
|
} catch {
|
|
@@ -3243,9 +3959,9 @@ ${laneWarning}` : laneWarning;
|
|
|
3243
3959
|
});
|
|
3244
3960
|
if (input.baseDir) {
|
|
3245
3961
|
try {
|
|
3246
|
-
const EXE_OS_DIR =
|
|
3247
|
-
const mdPath =
|
|
3248
|
-
const mdDir =
|
|
3962
|
+
const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
|
|
3963
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
3964
|
+
const mdDir = path11.dirname(mdPath);
|
|
3249
3965
|
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3250
3966
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3251
3967
|
const mdContent = `# ${input.title}
|
|
@@ -3546,7 +4262,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3546
4262
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3547
4263
|
}
|
|
3548
4264
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3549
|
-
const archPath =
|
|
4265
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3550
4266
|
try {
|
|
3551
4267
|
if (existsSync10(archPath)) return;
|
|
3552
4268
|
const template = [
|
|
@@ -3581,7 +4297,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3581
4297
|
}
|
|
3582
4298
|
}
|
|
3583
4299
|
async function ensureGitignoreExe(baseDir) {
|
|
3584
|
-
const gitignorePath =
|
|
4300
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
3585
4301
|
try {
|
|
3586
4302
|
if (existsSync10(gitignorePath)) {
|
|
3587
4303
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -3627,7 +4343,7 @@ __export(tasks_review_exports, {
|
|
|
3627
4343
|
isStale: () => isStale,
|
|
3628
4344
|
listPendingReviews: () => listPendingReviews
|
|
3629
4345
|
});
|
|
3630
|
-
import
|
|
4346
|
+
import path12 from "path";
|
|
3631
4347
|
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3632
4348
|
function formatAge(isoTimestamp) {
|
|
3633
4349
|
if (!isoTimestamp) return "";
|
|
@@ -3648,7 +4364,7 @@ async function countPendingReviews(sessionScope) {
|
|
|
3648
4364
|
const client = getClient();
|
|
3649
4365
|
if (sessionScope) {
|
|
3650
4366
|
const result2 = await client.execute({
|
|
3651
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
4367
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
3652
4368
|
args: [sessionScope]
|
|
3653
4369
|
});
|
|
3654
4370
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3913,11 +4629,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3913
4629
|
);
|
|
3914
4630
|
}
|
|
3915
4631
|
try {
|
|
3916
|
-
const cacheDir =
|
|
4632
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3917
4633
|
if (existsSync11(cacheDir)) {
|
|
3918
4634
|
for (const f of readdirSync2(cacheDir)) {
|
|
3919
4635
|
if (f.startsWith("review-notified-")) {
|
|
3920
|
-
unlinkSync4(
|
|
4636
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
3921
4637
|
}
|
|
3922
4638
|
}
|
|
3923
4639
|
}
|
|
@@ -3938,7 +4654,7 @@ var init_tasks_review = __esm({
|
|
|
3938
4654
|
});
|
|
3939
4655
|
|
|
3940
4656
|
// src/lib/tasks-chain.ts
|
|
3941
|
-
import
|
|
4657
|
+
import path13 from "path";
|
|
3942
4658
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3943
4659
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3944
4660
|
const client = getClient();
|
|
@@ -3955,7 +4671,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3955
4671
|
});
|
|
3956
4672
|
for (const ur of unblockedRows.rows) {
|
|
3957
4673
|
try {
|
|
3958
|
-
const ubFile =
|
|
4674
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3959
4675
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3960
4676
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3961
4677
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4024,7 +4740,7 @@ var init_tasks_chain = __esm({
|
|
|
4024
4740
|
|
|
4025
4741
|
// src/lib/project-name.ts
|
|
4026
4742
|
import { execSync as execSync6 } from "child_process";
|
|
4027
|
-
import
|
|
4743
|
+
import path14 from "path";
|
|
4028
4744
|
function getProjectName(cwd) {
|
|
4029
4745
|
const dir = cwd ?? process.cwd();
|
|
4030
4746
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4037,7 +4753,7 @@ function getProjectName(cwd) {
|
|
|
4037
4753
|
timeout: 2e3,
|
|
4038
4754
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4039
4755
|
}).trim();
|
|
4040
|
-
repoRoot =
|
|
4756
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
4041
4757
|
} catch {
|
|
4042
4758
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4043
4759
|
cwd: dir,
|
|
@@ -4046,11 +4762,11 @@ function getProjectName(cwd) {
|
|
|
4046
4762
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4047
4763
|
}).trim();
|
|
4048
4764
|
}
|
|
4049
|
-
_cached2 =
|
|
4765
|
+
_cached2 = path14.basename(repoRoot);
|
|
4050
4766
|
_cachedCwd = dir;
|
|
4051
4767
|
return _cached2;
|
|
4052
4768
|
} catch {
|
|
4053
|
-
_cached2 =
|
|
4769
|
+
_cached2 = path14.basename(dir);
|
|
4054
4770
|
_cachedCwd = dir;
|
|
4055
4771
|
return _cached2;
|
|
4056
4772
|
}
|
|
@@ -4523,7 +5239,7 @@ __export(tasks_exports, {
|
|
|
4523
5239
|
updateTaskStatus: () => updateTaskStatus,
|
|
4524
5240
|
writeCheckpoint: () => writeCheckpoint
|
|
4525
5241
|
});
|
|
4526
|
-
import
|
|
5242
|
+
import path15 from "path";
|
|
4527
5243
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4528
5244
|
async function createTask(input) {
|
|
4529
5245
|
const result = await createTaskCore(input);
|
|
@@ -4543,8 +5259,8 @@ async function updateTask(input) {
|
|
|
4543
5259
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4544
5260
|
try {
|
|
4545
5261
|
const agent = String(row.assigned_to);
|
|
4546
|
-
const cacheDir =
|
|
4547
|
-
const cachePath =
|
|
5262
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
5263
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
4548
5264
|
if (input.status === "in_progress") {
|
|
4549
5265
|
mkdirSync5(cacheDir, { recursive: true });
|
|
4550
5266
|
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -5015,12 +5731,12 @@ __export(tmux_routing_exports, {
|
|
|
5015
5731
|
});
|
|
5016
5732
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5017
5733
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5018
|
-
import
|
|
5019
|
-
import
|
|
5734
|
+
import path16 from "path";
|
|
5735
|
+
import os9 from "os";
|
|
5020
5736
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5021
5737
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5022
5738
|
function spawnLockPath(sessionName) {
|
|
5023
|
-
return
|
|
5739
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5024
5740
|
}
|
|
5025
5741
|
function isProcessAlive(pid) {
|
|
5026
5742
|
try {
|
|
@@ -5057,8 +5773,8 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5057
5773
|
function resolveBehaviorsExporterScript() {
|
|
5058
5774
|
try {
|
|
5059
5775
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5060
|
-
const scriptPath =
|
|
5061
|
-
|
|
5776
|
+
const scriptPath = path16.join(
|
|
5777
|
+
path16.dirname(thisFile),
|
|
5062
5778
|
"..",
|
|
5063
5779
|
"bin",
|
|
5064
5780
|
"exe-export-behaviors.js"
|
|
@@ -5133,7 +5849,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5133
5849
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5134
5850
|
}
|
|
5135
5851
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5136
|
-
const filePath =
|
|
5852
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5137
5853
|
writeFileSync7(filePath, JSON.stringify({
|
|
5138
5854
|
parentExe: rootExe,
|
|
5139
5855
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -5142,7 +5858,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5142
5858
|
}
|
|
5143
5859
|
function getParentExe(sessionKey) {
|
|
5144
5860
|
try {
|
|
5145
|
-
const data = JSON.parse(readFileSync11(
|
|
5861
|
+
const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5146
5862
|
return data.parentExe || null;
|
|
5147
5863
|
} catch {
|
|
5148
5864
|
return null;
|
|
@@ -5151,7 +5867,7 @@ function getParentExe(sessionKey) {
|
|
|
5151
5867
|
function getDispatchedBy(sessionKey) {
|
|
5152
5868
|
try {
|
|
5153
5869
|
const data = JSON.parse(readFileSync11(
|
|
5154
|
-
|
|
5870
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5155
5871
|
"utf8"
|
|
5156
5872
|
));
|
|
5157
5873
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5337,7 +6053,7 @@ function sendIntercom(targetSession) {
|
|
|
5337
6053
|
try {
|
|
5338
6054
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5339
6055
|
const agent = baseAgentName(rawAgent);
|
|
5340
|
-
const markerPath =
|
|
6056
|
+
const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5341
6057
|
if (existsSync12(markerPath)) {
|
|
5342
6058
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
5343
6059
|
return "debounced";
|
|
@@ -5347,7 +6063,7 @@ function sendIntercom(targetSession) {
|
|
|
5347
6063
|
try {
|
|
5348
6064
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5349
6065
|
const agent = baseAgentName(rawAgent);
|
|
5350
|
-
const taskDir =
|
|
6066
|
+
const taskDir = path16.join(process.cwd(), "exe", agent);
|
|
5351
6067
|
if (existsSync12(taskDir)) {
|
|
5352
6068
|
const files = readdirSync3(taskDir).filter(
|
|
5353
6069
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -5481,8 +6197,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5481
6197
|
const transport = getTransport();
|
|
5482
6198
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5483
6199
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5484
|
-
const logDir =
|
|
5485
|
-
const logFile =
|
|
6200
|
+
const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
|
|
6201
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5486
6202
|
if (!existsSync12(logDir)) {
|
|
5487
6203
|
mkdirSync6(logDir, { recursive: true });
|
|
5488
6204
|
}
|
|
@@ -5490,14 +6206,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5490
6206
|
let cleanupSuffix = "";
|
|
5491
6207
|
try {
|
|
5492
6208
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5493
|
-
const cleanupScript =
|
|
6209
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5494
6210
|
if (existsSync12(cleanupScript)) {
|
|
5495
6211
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5496
6212
|
}
|
|
5497
6213
|
} catch {
|
|
5498
6214
|
}
|
|
5499
6215
|
try {
|
|
5500
|
-
const claudeJsonPath =
|
|
6216
|
+
const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
|
|
5501
6217
|
let claudeJson = {};
|
|
5502
6218
|
try {
|
|
5503
6219
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -5512,10 +6228,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5512
6228
|
} catch {
|
|
5513
6229
|
}
|
|
5514
6230
|
try {
|
|
5515
|
-
const settingsDir =
|
|
6231
|
+
const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
|
|
5516
6232
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5517
|
-
const projSettingsDir =
|
|
5518
|
-
const settingsPath =
|
|
6233
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
6234
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
5519
6235
|
let settings = {};
|
|
5520
6236
|
try {
|
|
5521
6237
|
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
@@ -5562,8 +6278,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5562
6278
|
let behaviorsFlag = "";
|
|
5563
6279
|
let legacyFallbackWarned = false;
|
|
5564
6280
|
if (!useExeAgent && !useBinSymlink) {
|
|
5565
|
-
const identityPath =
|
|
5566
|
-
|
|
6281
|
+
const identityPath = path16.join(
|
|
6282
|
+
os9.homedir(),
|
|
5567
6283
|
".exe-os",
|
|
5568
6284
|
"identity",
|
|
5569
6285
|
`${employeeName}.md`
|
|
@@ -5578,7 +6294,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5578
6294
|
}
|
|
5579
6295
|
const behaviorsFile = exportBehaviorsSync(
|
|
5580
6296
|
employeeName,
|
|
5581
|
-
|
|
6297
|
+
path16.basename(spawnCwd),
|
|
5582
6298
|
sessionName
|
|
5583
6299
|
);
|
|
5584
6300
|
if (behaviorsFile) {
|
|
@@ -5593,9 +6309,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5593
6309
|
}
|
|
5594
6310
|
let sessionContextFlag = "";
|
|
5595
6311
|
try {
|
|
5596
|
-
const ctxDir =
|
|
6312
|
+
const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5597
6313
|
mkdirSync6(ctxDir, { recursive: true });
|
|
5598
|
-
const ctxFile =
|
|
6314
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5599
6315
|
const ctxContent = [
|
|
5600
6316
|
`## Session Context`,
|
|
5601
6317
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -5679,7 +6395,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5679
6395
|
transport.pipeLog(sessionName, logFile);
|
|
5680
6396
|
try {
|
|
5681
6397
|
const mySession = getMySession();
|
|
5682
|
-
const dispatchInfo =
|
|
6398
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5683
6399
|
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5684
6400
|
dispatchedBy: mySession,
|
|
5685
6401
|
rootExe: exeSession,
|
|
@@ -5754,15 +6470,15 @@ var init_tmux_routing = __esm({
|
|
|
5754
6470
|
init_intercom_queue();
|
|
5755
6471
|
init_plan_limits();
|
|
5756
6472
|
init_employees();
|
|
5757
|
-
SPAWN_LOCK_DIR =
|
|
5758
|
-
SESSION_CACHE =
|
|
6473
|
+
SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
6474
|
+
SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
|
|
5759
6475
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5760
6476
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5761
6477
|
VERIFY_PANE_LINES = 200;
|
|
5762
6478
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5763
6479
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5764
|
-
INTERCOM_LOG2 =
|
|
5765
|
-
DEBOUNCE_FILE =
|
|
6480
|
+
INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
6481
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5766
6482
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5767
6483
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5768
6484
|
}
|
|
@@ -6047,8 +6763,8 @@ __export(agent_signals_exports, {
|
|
|
6047
6763
|
hasUnreadInbox: () => hasUnreadInbox
|
|
6048
6764
|
});
|
|
6049
6765
|
import { readFileSync as readFileSync12, existsSync as existsSync13 } from "fs";
|
|
6050
|
-
import
|
|
6051
|
-
import
|
|
6766
|
+
import os10 from "os";
|
|
6767
|
+
import path17 from "path";
|
|
6052
6768
|
async function hasOpenTasks(client, agentId) {
|
|
6053
6769
|
try {
|
|
6054
6770
|
const scope = sessionScopeFilter(null);
|
|
@@ -6090,7 +6806,7 @@ async function hasUnreadInbox(client, agentId) {
|
|
|
6090
6806
|
return CONSERVATIVE_ON_ERROR;
|
|
6091
6807
|
}
|
|
6092
6808
|
}
|
|
6093
|
-
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog =
|
|
6809
|
+
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path17.join(os10.homedir(), ".exe-os", "intercom.log")) {
|
|
6094
6810
|
if (!existsSync13(intercomLog)) return false;
|
|
6095
6811
|
try {
|
|
6096
6812
|
const raw = readFileSync12(intercomLog, "utf8");
|
|
@@ -6420,7 +7136,7 @@ function createIdleNudgeRealDeps(getClient2) {
|
|
|
6420
7136
|
const doScope = sessionScopeFilter(null);
|
|
6421
7137
|
const result = await client.execute({
|
|
6422
7138
|
sql: `SELECT id, title, priority FROM tasks
|
|
6423
|
-
WHERE assigned_to = ? AND status IN ('open', 'in_progress'
|
|
7139
|
+
WHERE assigned_to = ? AND status IN ('open', 'in_progress')${doScope.sql}
|
|
6424
7140
|
ORDER BY CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 ELSE 2 END
|
|
6425
7141
|
LIMIT 1`,
|
|
6426
7142
|
args: [agentId, ...doScope.args]
|
|
@@ -6721,13 +7437,13 @@ __export(keychain_exports, {
|
|
|
6721
7437
|
});
|
|
6722
7438
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
6723
7439
|
import { existsSync as existsSync15 } from "fs";
|
|
6724
|
-
import
|
|
6725
|
-
import
|
|
7440
|
+
import path18 from "path";
|
|
7441
|
+
import os11 from "os";
|
|
6726
7442
|
function getKeyDir() {
|
|
6727
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
7443
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os11.homedir(), ".exe-os");
|
|
6728
7444
|
}
|
|
6729
7445
|
function getKeyPath() {
|
|
6730
|
-
return
|
|
7446
|
+
return path18.join(getKeyDir(), "master.key");
|
|
6731
7447
|
}
|
|
6732
7448
|
async function tryKeytar() {
|
|
6733
7449
|
try {
|
|
@@ -6750,7 +7466,7 @@ async function getMasterKey() {
|
|
|
6750
7466
|
const keyPath = getKeyPath();
|
|
6751
7467
|
if (!existsSync15(keyPath)) {
|
|
6752
7468
|
process.stderr.write(
|
|
6753
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
7469
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6754
7470
|
`
|
|
6755
7471
|
);
|
|
6756
7472
|
return null;
|
|
@@ -6846,7 +7562,7 @@ __export(shard_manager_exports, {
|
|
|
6846
7562
|
listShards: () => listShards,
|
|
6847
7563
|
shardExists: () => shardExists
|
|
6848
7564
|
});
|
|
6849
|
-
import
|
|
7565
|
+
import path19 from "path";
|
|
6850
7566
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
6851
7567
|
import { createClient as createClient2 } from "@libsql/client";
|
|
6852
7568
|
function initShardManager(encryptionKey) {
|
|
@@ -6872,7 +7588,7 @@ function getShardClient(projectName) {
|
|
|
6872
7588
|
}
|
|
6873
7589
|
const cached = _shards.get(safeName);
|
|
6874
7590
|
if (cached) return cached;
|
|
6875
|
-
const dbPath =
|
|
7591
|
+
const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
|
|
6876
7592
|
const client = createClient2({
|
|
6877
7593
|
url: `file:${dbPath}`,
|
|
6878
7594
|
encryptionKey: _encryptionKey
|
|
@@ -6882,7 +7598,7 @@ function getShardClient(projectName) {
|
|
|
6882
7598
|
}
|
|
6883
7599
|
function shardExists(projectName) {
|
|
6884
7600
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6885
|
-
return existsSync16(
|
|
7601
|
+
return existsSync16(path19.join(SHARDS_DIR, `${safeName}.db`));
|
|
6886
7602
|
}
|
|
6887
7603
|
function listShards() {
|
|
6888
7604
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
@@ -6959,7 +7675,23 @@ async function ensureShardSchema(client) {
|
|
|
6959
7675
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
6960
7676
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
6961
7677
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
6962
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
7678
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
7679
|
+
// Metadata enrichment columns (must match database.ts)
|
|
7680
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
7681
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
7682
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
7683
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
7684
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
7685
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
7686
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
7687
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
7688
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
7689
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
7690
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
7691
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
7692
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
7693
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
7694
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
6963
7695
|
]) {
|
|
6964
7696
|
try {
|
|
6965
7697
|
await client.execute(col);
|
|
@@ -7071,7 +7803,7 @@ var init_shard_manager = __esm({
|
|
|
7071
7803
|
"src/lib/shard-manager.ts"() {
|
|
7072
7804
|
"use strict";
|
|
7073
7805
|
init_config();
|
|
7074
|
-
SHARDS_DIR =
|
|
7806
|
+
SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
|
|
7075
7807
|
_shards = /* @__PURE__ */ new Map();
|
|
7076
7808
|
_encryptionKey = null;
|
|
7077
7809
|
_shardingEnabled = false;
|
|
@@ -8445,8 +9177,8 @@ async function embedDirect(text) {
|
|
|
8445
9177
|
const llamaCpp = await import("node-llama-cpp");
|
|
8446
9178
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8447
9179
|
const { existsSync: existsSync19 } = await import("fs");
|
|
8448
|
-
const
|
|
8449
|
-
const modelPath =
|
|
9180
|
+
const path24 = await import("path");
|
|
9181
|
+
const modelPath = path24.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
8450
9182
|
if (!existsSync19(modelPath)) {
|
|
8451
9183
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
8452
9184
|
}
|
|
@@ -9169,8 +9901,8 @@ __export(wiki_sync_exports, {
|
|
|
9169
9901
|
listWorkspaces: () => listWorkspaces,
|
|
9170
9902
|
syncMemories: () => syncMemories
|
|
9171
9903
|
});
|
|
9172
|
-
async function wikiRequest(config,
|
|
9173
|
-
const url = `${config.wikiUrl}/api/v1${
|
|
9904
|
+
async function wikiRequest(config, path24, method = "GET", body) {
|
|
9905
|
+
const url = `${config.wikiUrl}/api/v1${path24}`;
|
|
9174
9906
|
const headers = {
|
|
9175
9907
|
"Authorization": `Bearer ${config.wikiApiKey}`,
|
|
9176
9908
|
"Content-Type": "application/json"
|
|
@@ -9182,7 +9914,7 @@ async function wikiRequest(config, path23, method = "GET", body) {
|
|
|
9182
9914
|
signal: AbortSignal.timeout(3e4)
|
|
9183
9915
|
});
|
|
9184
9916
|
if (!response.ok) {
|
|
9185
|
-
throw new Error(`Wiki API ${method} ${
|
|
9917
|
+
throw new Error(`Wiki API ${method} ${path24}: ${response.status} ${response.statusText}`);
|
|
9186
9918
|
}
|
|
9187
9919
|
return response.json();
|
|
9188
9920
|
}
|
|
@@ -9294,8 +10026,8 @@ __export(token_spend_exports, {
|
|
|
9294
10026
|
import { readdir } from "fs/promises";
|
|
9295
10027
|
import { createReadStream } from "fs";
|
|
9296
10028
|
import { createInterface } from "readline";
|
|
9297
|
-
import
|
|
9298
|
-
import
|
|
10029
|
+
import path20 from "path";
|
|
10030
|
+
import os12 from "os";
|
|
9299
10031
|
function getPricing(model) {
|
|
9300
10032
|
if (MODEL_PRICING[model]) return MODEL_PRICING[model];
|
|
9301
10033
|
const stripped = model.replace(/-\d{8}$/, "");
|
|
@@ -9318,18 +10050,18 @@ async function getAgentSpend(period = "7d") {
|
|
|
9318
10050
|
for (const row of result.rows) {
|
|
9319
10051
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
9320
10052
|
}
|
|
9321
|
-
const claudeDir =
|
|
10053
|
+
const claudeDir = path20.join(os12.homedir(), ".claude", "projects");
|
|
9322
10054
|
let projectDirs = [];
|
|
9323
10055
|
try {
|
|
9324
10056
|
const entries = await readdir(claudeDir);
|
|
9325
|
-
projectDirs = entries.map((e) =>
|
|
10057
|
+
projectDirs = entries.map((e) => path20.join(claudeDir, e));
|
|
9326
10058
|
} catch {
|
|
9327
10059
|
return [];
|
|
9328
10060
|
}
|
|
9329
10061
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
9330
10062
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
9331
10063
|
for (const dir of projectDirs) {
|
|
9332
|
-
const jsonlPath =
|
|
10064
|
+
const jsonlPath = path20.join(dir, `${sessionUuid}.jsonl`);
|
|
9333
10065
|
try {
|
|
9334
10066
|
const usage = await extractSessionUsage(jsonlPath);
|
|
9335
10067
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -9451,9 +10183,9 @@ __export(update_check_exports, {
|
|
|
9451
10183
|
});
|
|
9452
10184
|
import { execSync as execSync11 } from "child_process";
|
|
9453
10185
|
import { readFileSync as readFileSync14 } from "fs";
|
|
9454
|
-
import
|
|
10186
|
+
import path21 from "path";
|
|
9455
10187
|
function getLocalVersion(packageRoot) {
|
|
9456
|
-
const pkgPath =
|
|
10188
|
+
const pkgPath = path21.join(packageRoot, "package.json");
|
|
9457
10189
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
9458
10190
|
return pkg.version;
|
|
9459
10191
|
}
|
|
@@ -9525,9 +10257,9 @@ __export(device_registry_exports, {
|
|
|
9525
10257
|
setFriendlyName: () => setFriendlyName
|
|
9526
10258
|
});
|
|
9527
10259
|
import crypto8 from "crypto";
|
|
9528
|
-
import
|
|
10260
|
+
import os13 from "os";
|
|
9529
10261
|
import { readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, existsSync as existsSync17 } from "fs";
|
|
9530
|
-
import
|
|
10262
|
+
import path22 from "path";
|
|
9531
10263
|
function getDeviceInfo() {
|
|
9532
10264
|
if (existsSync17(DEVICE_JSON_PATH)) {
|
|
9533
10265
|
try {
|
|
@@ -9539,13 +10271,13 @@ function getDeviceInfo() {
|
|
|
9539
10271
|
} catch {
|
|
9540
10272
|
}
|
|
9541
10273
|
}
|
|
9542
|
-
const hostname =
|
|
10274
|
+
const hostname = os13.hostname();
|
|
9543
10275
|
const info = {
|
|
9544
10276
|
deviceId: crypto8.randomUUID(),
|
|
9545
10277
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
9546
10278
|
hostname
|
|
9547
10279
|
};
|
|
9548
|
-
mkdirSync8(
|
|
10280
|
+
mkdirSync8(path22.dirname(DEVICE_JSON_PATH), { recursive: true });
|
|
9549
10281
|
writeFileSync9(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
9550
10282
|
return info;
|
|
9551
10283
|
}
|
|
@@ -9586,7 +10318,7 @@ var init_device_registry = __esm({
|
|
|
9586
10318
|
"src/lib/device-registry.ts"() {
|
|
9587
10319
|
"use strict";
|
|
9588
10320
|
init_config();
|
|
9589
|
-
DEVICE_JSON_PATH =
|
|
10321
|
+
DEVICE_JSON_PATH = path22.join(EXE_AI_DIR, "device.json");
|
|
9590
10322
|
}
|
|
9591
10323
|
});
|
|
9592
10324
|
|
|
@@ -10051,10 +10783,10 @@ init_daemon_protocol();
|
|
|
10051
10783
|
init_daemon_orchestration();
|
|
10052
10784
|
import net2 from "net";
|
|
10053
10785
|
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync7, mkdirSync as mkdirSync9, existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
10054
|
-
import
|
|
10786
|
+
import path23 from "path";
|
|
10055
10787
|
import { getLlama } from "node-llama-cpp";
|
|
10056
|
-
var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
10057
|
-
var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
10788
|
+
var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path23.join(EXE_AI_DIR, "exed.sock");
|
|
10789
|
+
var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path23.join(EXE_AI_DIR, "exed.pid");
|
|
10058
10790
|
var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
|
|
10059
10791
|
var IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
10060
10792
|
var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
|
|
@@ -10080,7 +10812,7 @@ function enqueue(queue, entry) {
|
|
|
10080
10812
|
queue.push(entry);
|
|
10081
10813
|
}
|
|
10082
10814
|
async function loadModel() {
|
|
10083
|
-
const modelPath =
|
|
10815
|
+
const modelPath = path23.join(MODELS_DIR, MODEL_FILE);
|
|
10084
10816
|
if (!existsSync18(modelPath)) {
|
|
10085
10817
|
process.stderr.write(`[exed] FATAL: model not found at ${modelPath}
|
|
10086
10818
|
`);
|
|
@@ -10250,9 +10982,9 @@ async function handleDbBatch(socket, requestId, statements, mode) {
|
|
|
10250
10982
|
}
|
|
10251
10983
|
}
|
|
10252
10984
|
function startServer() {
|
|
10253
|
-
mkdirSync9(
|
|
10985
|
+
mkdirSync9(path23.dirname(SOCKET_PATH2), { recursive: true });
|
|
10254
10986
|
for (const oldFile of ["embed.sock", "embed.pid"]) {
|
|
10255
|
-
const oldPath =
|
|
10987
|
+
const oldPath = path23.join(path23.dirname(SOCKET_PATH2), oldFile);
|
|
10256
10988
|
try {
|
|
10257
10989
|
if (oldFile.endsWith(".pid")) {
|
|
10258
10990
|
const pid = parseInt(readFileSync16(oldPath, "utf8").trim(), 10);
|
|
@@ -10637,7 +11369,7 @@ function startWikiSync() {
|
|
|
10637
11369
|
});
|
|
10638
11370
|
}
|
|
10639
11371
|
var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
|
|
10640
|
-
var AGENT_STATS_PATH =
|
|
11372
|
+
var AGENT_STATS_PATH = path23.join(EXE_AI_DIR, "agent-stats.json");
|
|
10641
11373
|
async function writeAgentStats() {
|
|
10642
11374
|
if (!await ensureStoreForPolling()) return;
|
|
10643
11375
|
try {
|
|
@@ -10767,11 +11499,11 @@ function startIntercomQueueDrain() {
|
|
|
10767
11499
|
const hasInProgressTask = (session) => {
|
|
10768
11500
|
try {
|
|
10769
11501
|
const { baseAgentName: ban } = (init_employees(), __toCommonJS(employees_exports));
|
|
10770
|
-
const
|
|
11502
|
+
const path24 = __require("path");
|
|
10771
11503
|
const { existsSync: existsSync19 } = __require("fs");
|
|
10772
|
-
const
|
|
11504
|
+
const os14 = __require("os");
|
|
10773
11505
|
const agent = ban(session.split("-")[0] ?? session);
|
|
10774
|
-
const markerPath =
|
|
11506
|
+
const markerPath = path24.join(os14.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
|
|
10775
11507
|
return existsSync19(markerPath);
|
|
10776
11508
|
} catch {
|
|
10777
11509
|
return false;
|