@askexenow/exe-os 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/bin/exe-rename.js
CHANGED
|
@@ -196,7 +196,7 @@ function registerBinSymlinks(name) {
|
|
|
196
196
|
}
|
|
197
197
|
return { created, skipped, errors };
|
|
198
198
|
}
|
|
199
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
199
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
200
200
|
var init_employees = __esm({
|
|
201
201
|
"src/lib/employees.ts"() {
|
|
202
202
|
"use strict";
|
|
@@ -204,6 +204,7 @@ var init_employees = __esm({
|
|
|
204
204
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
205
205
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
206
206
|
COORDINATOR_ROLE = "COO";
|
|
207
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
207
208
|
}
|
|
208
209
|
});
|
|
209
210
|
|
|
@@ -262,13 +263,597 @@ var init_db_retry = __esm({
|
|
|
262
263
|
}
|
|
263
264
|
});
|
|
264
265
|
|
|
266
|
+
// src/lib/database-adapter.ts
|
|
267
|
+
import os3 from "os";
|
|
268
|
+
import path3 from "path";
|
|
269
|
+
import { createRequire } from "module";
|
|
270
|
+
import { pathToFileURL } from "url";
|
|
271
|
+
function quotedIdentifier(identifier) {
|
|
272
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
273
|
+
}
|
|
274
|
+
function unqualifiedTableName(name) {
|
|
275
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
276
|
+
const parts = raw.split(".");
|
|
277
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
278
|
+
}
|
|
279
|
+
function stripTrailingSemicolon(sql) {
|
|
280
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
281
|
+
}
|
|
282
|
+
function appendClause(sql, clause) {
|
|
283
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
284
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
285
|
+
if (!returningMatch) {
|
|
286
|
+
return `${trimmed}${clause}`;
|
|
287
|
+
}
|
|
288
|
+
const idx = returningMatch.index;
|
|
289
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
290
|
+
}
|
|
291
|
+
function normalizeStatement(stmt) {
|
|
292
|
+
if (typeof stmt === "string") {
|
|
293
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
294
|
+
}
|
|
295
|
+
const sql = stmt.sql;
|
|
296
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
297
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
298
|
+
}
|
|
299
|
+
return { kind: "named", sql, args: stmt.args };
|
|
300
|
+
}
|
|
301
|
+
function rewriteBooleanLiterals(sql) {
|
|
302
|
+
let out = sql;
|
|
303
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
304
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
305
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
306
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
307
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
308
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
309
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
310
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
function rewriteInsertOrIgnore(sql) {
|
|
315
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
316
|
+
return sql;
|
|
317
|
+
}
|
|
318
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
319
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
320
|
+
}
|
|
321
|
+
function rewriteInsertOrReplace(sql) {
|
|
322
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
323
|
+
if (!match) {
|
|
324
|
+
return sql;
|
|
325
|
+
}
|
|
326
|
+
const rawTable = match[1];
|
|
327
|
+
const rawColumns = match[2];
|
|
328
|
+
const remainder = match[3];
|
|
329
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
330
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
331
|
+
if (!conflictKeys?.length) {
|
|
332
|
+
return sql;
|
|
333
|
+
}
|
|
334
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
335
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
336
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
337
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
338
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
339
|
+
}
|
|
340
|
+
function rewriteSql(sql) {
|
|
341
|
+
let out = sql;
|
|
342
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
343
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
344
|
+
out = rewriteBooleanLiterals(out);
|
|
345
|
+
out = rewriteInsertOrReplace(out);
|
|
346
|
+
out = rewriteInsertOrIgnore(out);
|
|
347
|
+
return stripTrailingSemicolon(out);
|
|
348
|
+
}
|
|
349
|
+
function toBoolean(value) {
|
|
350
|
+
if (value === null || value === void 0) return value;
|
|
351
|
+
if (typeof value === "boolean") return value;
|
|
352
|
+
if (typeof value === "number") return value !== 0;
|
|
353
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
354
|
+
if (typeof value === "string") {
|
|
355
|
+
const normalized = value.trim().toLowerCase();
|
|
356
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
357
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
358
|
+
}
|
|
359
|
+
return Boolean(value);
|
|
360
|
+
}
|
|
361
|
+
function countQuestionMarks(sql, end) {
|
|
362
|
+
let count = 0;
|
|
363
|
+
let inSingle = false;
|
|
364
|
+
let inDouble = false;
|
|
365
|
+
let inLineComment = false;
|
|
366
|
+
let inBlockComment = false;
|
|
367
|
+
for (let i = 0; i < end; i++) {
|
|
368
|
+
const ch = sql[i];
|
|
369
|
+
const next = sql[i + 1];
|
|
370
|
+
if (inLineComment) {
|
|
371
|
+
if (ch === "\n") inLineComment = false;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (inBlockComment) {
|
|
375
|
+
if (ch === "*" && next === "/") {
|
|
376
|
+
inBlockComment = false;
|
|
377
|
+
i += 1;
|
|
378
|
+
}
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
382
|
+
inLineComment = true;
|
|
383
|
+
i += 1;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
387
|
+
inBlockComment = true;
|
|
388
|
+
i += 1;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
392
|
+
inSingle = !inSingle;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
396
|
+
inDouble = !inDouble;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
400
|
+
count += 1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return count;
|
|
404
|
+
}
|
|
405
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
406
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
407
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
408
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
409
|
+
for (const match of sql.matchAll(pattern)) {
|
|
410
|
+
const matchText = match[0];
|
|
411
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
412
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return indexes;
|
|
416
|
+
}
|
|
417
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
418
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
419
|
+
if (!match) return;
|
|
420
|
+
const rawTable = match[1];
|
|
421
|
+
const rawColumns = match[2];
|
|
422
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
423
|
+
if (!boolColumns?.size) return;
|
|
424
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
425
|
+
for (const [index, column] of columns.entries()) {
|
|
426
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
427
|
+
args[index] = toBoolean(args[index]);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
432
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
433
|
+
if (!match) return;
|
|
434
|
+
const rawTable = match[1];
|
|
435
|
+
const setClause = match[2];
|
|
436
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
437
|
+
if (!boolColumns?.size) return;
|
|
438
|
+
const assignments = setClause.split(",");
|
|
439
|
+
let placeholderIndex = 0;
|
|
440
|
+
for (const assignment of assignments) {
|
|
441
|
+
if (!assignment.includes("?")) continue;
|
|
442
|
+
placeholderIndex += 1;
|
|
443
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
444
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
445
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function coerceBooleanArgs(sql, args) {
|
|
450
|
+
const nextArgs = [...args];
|
|
451
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
452
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
453
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
454
|
+
for (const index of placeholderIndexes) {
|
|
455
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
456
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return nextArgs;
|
|
460
|
+
}
|
|
461
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
462
|
+
let out = "";
|
|
463
|
+
let placeholder = 0;
|
|
464
|
+
let inSingle = false;
|
|
465
|
+
let inDouble = false;
|
|
466
|
+
let inLineComment = false;
|
|
467
|
+
let inBlockComment = false;
|
|
468
|
+
for (let i = 0; i < sql.length; i++) {
|
|
469
|
+
const ch = sql[i];
|
|
470
|
+
const next = sql[i + 1];
|
|
471
|
+
if (inLineComment) {
|
|
472
|
+
out += ch;
|
|
473
|
+
if (ch === "\n") inLineComment = false;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (inBlockComment) {
|
|
477
|
+
out += ch;
|
|
478
|
+
if (ch === "*" && next === "/") {
|
|
479
|
+
out += next;
|
|
480
|
+
inBlockComment = false;
|
|
481
|
+
i += 1;
|
|
482
|
+
}
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
486
|
+
out += ch + next;
|
|
487
|
+
inLineComment = true;
|
|
488
|
+
i += 1;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
492
|
+
out += ch + next;
|
|
493
|
+
inBlockComment = true;
|
|
494
|
+
i += 1;
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
498
|
+
inSingle = !inSingle;
|
|
499
|
+
out += ch;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
503
|
+
inDouble = !inDouble;
|
|
504
|
+
out += ch;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
508
|
+
placeholder += 1;
|
|
509
|
+
out += `$${placeholder}`;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
out += ch;
|
|
513
|
+
}
|
|
514
|
+
return out;
|
|
515
|
+
}
|
|
516
|
+
function translateStatementForPostgres(stmt) {
|
|
517
|
+
const normalized = normalizeStatement(stmt);
|
|
518
|
+
if (normalized.kind === "named") {
|
|
519
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
520
|
+
}
|
|
521
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
522
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
523
|
+
return {
|
|
524
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
525
|
+
args: coercedArgs
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function shouldBypassPostgres(stmt) {
|
|
529
|
+
const normalized = normalizeStatement(stmt);
|
|
530
|
+
if (normalized.kind === "named") {
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
534
|
+
}
|
|
535
|
+
function shouldFallbackOnError(error) {
|
|
536
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
537
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
538
|
+
}
|
|
539
|
+
function isReadQuery(sql) {
|
|
540
|
+
const trimmed = sql.trimStart();
|
|
541
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
542
|
+
}
|
|
543
|
+
function buildRow(row, columns) {
|
|
544
|
+
const values = columns.map((column) => row[column]);
|
|
545
|
+
return Object.assign(values, row);
|
|
546
|
+
}
|
|
547
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
548
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
549
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
550
|
+
return {
|
|
551
|
+
columns,
|
|
552
|
+
columnTypes: columns.map(() => ""),
|
|
553
|
+
rows: resultRows,
|
|
554
|
+
rowsAffected,
|
|
555
|
+
lastInsertRowid: void 0,
|
|
556
|
+
toJSON() {
|
|
557
|
+
return {
|
|
558
|
+
columns,
|
|
559
|
+
columnTypes: columns.map(() => ""),
|
|
560
|
+
rows,
|
|
561
|
+
rowsAffected,
|
|
562
|
+
lastInsertRowid: void 0
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
async function loadPrismaClient() {
|
|
568
|
+
if (!prismaClientPromise) {
|
|
569
|
+
prismaClientPromise = (async () => {
|
|
570
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
571
|
+
if (explicitPath) {
|
|
572
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
573
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
574
|
+
if (!PrismaClient2) {
|
|
575
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
576
|
+
}
|
|
577
|
+
return new PrismaClient2();
|
|
578
|
+
}
|
|
579
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
580
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
581
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
582
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
583
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
584
|
+
if (!PrismaClient) {
|
|
585
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
586
|
+
}
|
|
587
|
+
return new PrismaClient();
|
|
588
|
+
})();
|
|
589
|
+
}
|
|
590
|
+
return prismaClientPromise;
|
|
591
|
+
}
|
|
592
|
+
async function ensureCompatibilityViews(prisma) {
|
|
593
|
+
if (!compatibilityBootstrapPromise) {
|
|
594
|
+
compatibilityBootstrapPromise = (async () => {
|
|
595
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
596
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
597
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
598
|
+
"SELECT to_regclass($1) AS regclass",
|
|
599
|
+
relation
|
|
600
|
+
);
|
|
601
|
+
if (!rows[0]?.regclass) {
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
await prisma.$executeRawUnsafe(
|
|
605
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
})();
|
|
609
|
+
}
|
|
610
|
+
return compatibilityBootstrapPromise;
|
|
611
|
+
}
|
|
612
|
+
async function executeOnPrisma(executor, stmt) {
|
|
613
|
+
const translated = translateStatementForPostgres(stmt);
|
|
614
|
+
if (isReadQuery(translated.sql)) {
|
|
615
|
+
const rows = await executor.$queryRawUnsafe(
|
|
616
|
+
translated.sql,
|
|
617
|
+
...translated.args
|
|
618
|
+
);
|
|
619
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
620
|
+
}
|
|
621
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
622
|
+
return buildResultSet([], rowsAffected);
|
|
623
|
+
}
|
|
624
|
+
function splitSqlStatements(sql) {
|
|
625
|
+
const parts = [];
|
|
626
|
+
let current = "";
|
|
627
|
+
let inSingle = false;
|
|
628
|
+
let inDouble = false;
|
|
629
|
+
let inLineComment = false;
|
|
630
|
+
let inBlockComment = false;
|
|
631
|
+
for (let i = 0; i < sql.length; i++) {
|
|
632
|
+
const ch = sql[i];
|
|
633
|
+
const next = sql[i + 1];
|
|
634
|
+
if (inLineComment) {
|
|
635
|
+
current += ch;
|
|
636
|
+
if (ch === "\n") inLineComment = false;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (inBlockComment) {
|
|
640
|
+
current += ch;
|
|
641
|
+
if (ch === "*" && next === "/") {
|
|
642
|
+
current += next;
|
|
643
|
+
inBlockComment = false;
|
|
644
|
+
i += 1;
|
|
645
|
+
}
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
649
|
+
current += ch + next;
|
|
650
|
+
inLineComment = true;
|
|
651
|
+
i += 1;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
655
|
+
current += ch + next;
|
|
656
|
+
inBlockComment = true;
|
|
657
|
+
i += 1;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
661
|
+
inSingle = !inSingle;
|
|
662
|
+
current += ch;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
666
|
+
inDouble = !inDouble;
|
|
667
|
+
current += ch;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
671
|
+
if (current.trim()) {
|
|
672
|
+
parts.push(current.trim());
|
|
673
|
+
}
|
|
674
|
+
current = "";
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
current += ch;
|
|
678
|
+
}
|
|
679
|
+
if (current.trim()) {
|
|
680
|
+
parts.push(current.trim());
|
|
681
|
+
}
|
|
682
|
+
return parts;
|
|
683
|
+
}
|
|
684
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
685
|
+
const prisma = await loadPrismaClient();
|
|
686
|
+
await ensureCompatibilityViews(prisma);
|
|
687
|
+
let closed = false;
|
|
688
|
+
let adapter;
|
|
689
|
+
const fallbackExecute = async (stmt, error) => {
|
|
690
|
+
if (!fallbackClient) {
|
|
691
|
+
if (error) throw error;
|
|
692
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
693
|
+
}
|
|
694
|
+
if (error) {
|
|
695
|
+
process.stderr.write(
|
|
696
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
697
|
+
`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
return fallbackClient.execute(stmt);
|
|
701
|
+
};
|
|
702
|
+
adapter = {
|
|
703
|
+
async execute(stmt) {
|
|
704
|
+
if (shouldBypassPostgres(stmt)) {
|
|
705
|
+
return fallbackExecute(stmt);
|
|
706
|
+
}
|
|
707
|
+
try {
|
|
708
|
+
return await executeOnPrisma(prisma, stmt);
|
|
709
|
+
} catch (error) {
|
|
710
|
+
if (shouldFallbackOnError(error)) {
|
|
711
|
+
return fallbackExecute(stmt, error);
|
|
712
|
+
}
|
|
713
|
+
throw error;
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
async batch(stmts, mode) {
|
|
717
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
718
|
+
if (!fallbackClient) {
|
|
719
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
720
|
+
}
|
|
721
|
+
return fallbackClient.batch(stmts, mode);
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
if (prisma.$transaction) {
|
|
725
|
+
return await prisma.$transaction(async (tx) => {
|
|
726
|
+
const results2 = [];
|
|
727
|
+
for (const stmt of stmts) {
|
|
728
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
729
|
+
}
|
|
730
|
+
return results2;
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
const results = [];
|
|
734
|
+
for (const stmt of stmts) {
|
|
735
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
736
|
+
}
|
|
737
|
+
return results;
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
740
|
+
process.stderr.write(
|
|
741
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
742
|
+
`
|
|
743
|
+
);
|
|
744
|
+
return fallbackClient.batch(stmts, mode);
|
|
745
|
+
}
|
|
746
|
+
throw error;
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
async migrate(stmts) {
|
|
750
|
+
if (fallbackClient) {
|
|
751
|
+
return fallbackClient.migrate(stmts);
|
|
752
|
+
}
|
|
753
|
+
return adapter.batch(stmts, "deferred");
|
|
754
|
+
},
|
|
755
|
+
async transaction(mode) {
|
|
756
|
+
if (!fallbackClient) {
|
|
757
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
758
|
+
}
|
|
759
|
+
return fallbackClient.transaction(mode);
|
|
760
|
+
},
|
|
761
|
+
async executeMultiple(sql) {
|
|
762
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
763
|
+
return fallbackClient.executeMultiple(sql);
|
|
764
|
+
}
|
|
765
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
766
|
+
await adapter.execute(statement);
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
async sync() {
|
|
770
|
+
if (fallbackClient) {
|
|
771
|
+
return fallbackClient.sync();
|
|
772
|
+
}
|
|
773
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
774
|
+
},
|
|
775
|
+
close() {
|
|
776
|
+
closed = true;
|
|
777
|
+
prismaClientPromise = null;
|
|
778
|
+
compatibilityBootstrapPromise = null;
|
|
779
|
+
void prisma.$disconnect?.();
|
|
780
|
+
},
|
|
781
|
+
get closed() {
|
|
782
|
+
return closed;
|
|
783
|
+
},
|
|
784
|
+
get protocol() {
|
|
785
|
+
return "prisma-postgres";
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
return adapter;
|
|
789
|
+
}
|
|
790
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
791
|
+
var init_database_adapter = __esm({
|
|
792
|
+
"src/lib/database-adapter.ts"() {
|
|
793
|
+
"use strict";
|
|
794
|
+
VIEW_MAPPINGS = [
|
|
795
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
796
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
797
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
798
|
+
{ view: "entities", source: "memory.entities" },
|
|
799
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
800
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
801
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
802
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
803
|
+
{ view: "messages", source: "memory.messages" },
|
|
804
|
+
{ view: "users", source: "wiki.users" },
|
|
805
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
806
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
807
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
808
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
809
|
+
];
|
|
810
|
+
UPSERT_KEYS = {
|
|
811
|
+
memories: ["id"],
|
|
812
|
+
tasks: ["id"],
|
|
813
|
+
behaviors: ["id"],
|
|
814
|
+
entities: ["id"],
|
|
815
|
+
relationships: ["id"],
|
|
816
|
+
entity_aliases: ["alias"],
|
|
817
|
+
notifications: ["id"],
|
|
818
|
+
messages: ["id"],
|
|
819
|
+
users: ["id"],
|
|
820
|
+
workspaces: ["id"],
|
|
821
|
+
workspace_users: ["id"],
|
|
822
|
+
documents: ["id"],
|
|
823
|
+
chats: ["id"]
|
|
824
|
+
};
|
|
825
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
826
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
827
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
828
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
829
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
830
|
+
};
|
|
831
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
832
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
833
|
+
);
|
|
834
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
835
|
+
/\bPRAGMA\b/i,
|
|
836
|
+
/\bsqlite_master\b/i,
|
|
837
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
838
|
+
/\bMATCH\b/i,
|
|
839
|
+
/\bvector_distance_cos\s*\(/i,
|
|
840
|
+
/\bjson_extract\s*\(/i,
|
|
841
|
+
/\bjulianday\s*\(/i,
|
|
842
|
+
/\bstrftime\s*\(/i,
|
|
843
|
+
/\blast_insert_rowid\s*\(/i
|
|
844
|
+
];
|
|
845
|
+
prismaClientPromise = null;
|
|
846
|
+
compatibilityBootstrapPromise = null;
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
265
850
|
// src/lib/exe-daemon-client.ts
|
|
266
851
|
import net from "net";
|
|
267
|
-
import
|
|
852
|
+
import os4 from "os";
|
|
268
853
|
import { spawn } from "child_process";
|
|
269
854
|
import { randomUUID } from "crypto";
|
|
270
855
|
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
271
|
-
import
|
|
856
|
+
import path4 from "path";
|
|
272
857
|
import { fileURLToPath } from "url";
|
|
273
858
|
function handleData(chunk) {
|
|
274
859
|
_buffer += chunk.toString();
|
|
@@ -319,17 +904,17 @@ function cleanupStaleFiles() {
|
|
|
319
904
|
}
|
|
320
905
|
}
|
|
321
906
|
function findPackageRoot() {
|
|
322
|
-
let dir =
|
|
323
|
-
const { root } =
|
|
907
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
908
|
+
const { root } = path4.parse(dir);
|
|
324
909
|
while (dir !== root) {
|
|
325
|
-
if (existsSync3(
|
|
326
|
-
dir =
|
|
910
|
+
if (existsSync3(path4.join(dir, "package.json"))) return dir;
|
|
911
|
+
dir = path4.dirname(dir);
|
|
327
912
|
}
|
|
328
913
|
return null;
|
|
329
914
|
}
|
|
330
915
|
function spawnDaemon() {
|
|
331
|
-
const freeGB =
|
|
332
|
-
const totalGB =
|
|
916
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
917
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
333
918
|
if (totalGB <= 8) {
|
|
334
919
|
process.stderr.write(
|
|
335
920
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -349,7 +934,7 @@ function spawnDaemon() {
|
|
|
349
934
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
350
935
|
return;
|
|
351
936
|
}
|
|
352
|
-
const daemonPath =
|
|
937
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
353
938
|
if (!existsSync3(daemonPath)) {
|
|
354
939
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
355
940
|
`);
|
|
@@ -358,7 +943,7 @@ function spawnDaemon() {
|
|
|
358
943
|
const resolvedPath = daemonPath;
|
|
359
944
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
360
945
|
`);
|
|
361
|
-
const logPath =
|
|
946
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
362
947
|
let stderrFd = "ignore";
|
|
363
948
|
try {
|
|
364
949
|
stderrFd = openSync(logPath, "a");
|
|
@@ -505,9 +1090,9 @@ var init_exe_daemon_client = __esm({
|
|
|
505
1090
|
"src/lib/exe-daemon-client.ts"() {
|
|
506
1091
|
"use strict";
|
|
507
1092
|
init_config();
|
|
508
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
509
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
510
|
-
SPAWN_LOCK_PATH =
|
|
1093
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
1094
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
1095
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
511
1096
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
512
1097
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
513
1098
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -589,7 +1174,7 @@ __export(db_daemon_client_exports, {
|
|
|
589
1174
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
590
1175
|
initDaemonDbClient: () => initDaemonDbClient
|
|
591
1176
|
});
|
|
592
|
-
function
|
|
1177
|
+
function normalizeStatement2(stmt) {
|
|
593
1178
|
if (typeof stmt === "string") {
|
|
594
1179
|
return { sql: stmt, args: [] };
|
|
595
1180
|
}
|
|
@@ -613,7 +1198,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
613
1198
|
if (!_useDaemon || !isClientConnected()) {
|
|
614
1199
|
return fallbackClient.execute(stmt);
|
|
615
1200
|
}
|
|
616
|
-
const { sql, args } =
|
|
1201
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
617
1202
|
const response = await sendDaemonRequest({
|
|
618
1203
|
type: "db-execute",
|
|
619
1204
|
sql,
|
|
@@ -638,7 +1223,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
638
1223
|
if (!_useDaemon || !isClientConnected()) {
|
|
639
1224
|
return fallbackClient.batch(stmts, mode);
|
|
640
1225
|
}
|
|
641
|
-
const statements = stmts.map(
|
|
1226
|
+
const statements = stmts.map(normalizeStatement2);
|
|
642
1227
|
const response = await sendDaemonRequest({
|
|
643
1228
|
type: "db-batch",
|
|
644
1229
|
statements,
|
|
@@ -733,6 +1318,18 @@ __export(database_exports, {
|
|
|
733
1318
|
});
|
|
734
1319
|
import { createClient } from "@libsql/client";
|
|
735
1320
|
async function initDatabase(config) {
|
|
1321
|
+
if (_walCheckpointTimer) {
|
|
1322
|
+
clearInterval(_walCheckpointTimer);
|
|
1323
|
+
_walCheckpointTimer = null;
|
|
1324
|
+
}
|
|
1325
|
+
if (_daemonClient) {
|
|
1326
|
+
_daemonClient.close();
|
|
1327
|
+
_daemonClient = null;
|
|
1328
|
+
}
|
|
1329
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1330
|
+
_adapterClient.close();
|
|
1331
|
+
}
|
|
1332
|
+
_adapterClient = null;
|
|
736
1333
|
if (_client) {
|
|
737
1334
|
_client.close();
|
|
738
1335
|
_client = null;
|
|
@@ -746,6 +1343,7 @@ async function initDatabase(config) {
|
|
|
746
1343
|
}
|
|
747
1344
|
_client = createClient(opts);
|
|
748
1345
|
_resilientClient = wrapWithRetry(_client);
|
|
1346
|
+
_adapterClient = _resilientClient;
|
|
749
1347
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
750
1348
|
});
|
|
751
1349
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -756,14 +1354,20 @@ async function initDatabase(config) {
|
|
|
756
1354
|
});
|
|
757
1355
|
}, 3e4);
|
|
758
1356
|
_walCheckpointTimer.unref();
|
|
1357
|
+
if (process.env.DATABASE_URL) {
|
|
1358
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1359
|
+
}
|
|
759
1360
|
}
|
|
760
1361
|
function isInitialized() {
|
|
761
|
-
return _client !== null;
|
|
1362
|
+
return _adapterClient !== null || _client !== null;
|
|
762
1363
|
}
|
|
763
1364
|
function getClient() {
|
|
764
|
-
if (!
|
|
1365
|
+
if (!_adapterClient) {
|
|
765
1366
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
766
1367
|
}
|
|
1368
|
+
if (process.env.DATABASE_URL) {
|
|
1369
|
+
return _adapterClient;
|
|
1370
|
+
}
|
|
767
1371
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
768
1372
|
return _resilientClient;
|
|
769
1373
|
}
|
|
@@ -773,6 +1377,7 @@ function getClient() {
|
|
|
773
1377
|
return _resilientClient;
|
|
774
1378
|
}
|
|
775
1379
|
async function initDaemonClient() {
|
|
1380
|
+
if (process.env.DATABASE_URL) return;
|
|
776
1381
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
777
1382
|
if (!_resilientClient) return;
|
|
778
1383
|
try {
|
|
@@ -1717,26 +2322,36 @@ async function ensureSchema() {
|
|
|
1717
2322
|
}
|
|
1718
2323
|
}
|
|
1719
2324
|
async function disposeDatabase() {
|
|
2325
|
+
if (_walCheckpointTimer) {
|
|
2326
|
+
clearInterval(_walCheckpointTimer);
|
|
2327
|
+
_walCheckpointTimer = null;
|
|
2328
|
+
}
|
|
1720
2329
|
if (_daemonClient) {
|
|
1721
2330
|
_daemonClient.close();
|
|
1722
2331
|
_daemonClient = null;
|
|
1723
2332
|
}
|
|
2333
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2334
|
+
_adapterClient.close();
|
|
2335
|
+
}
|
|
2336
|
+
_adapterClient = null;
|
|
1724
2337
|
if (_client) {
|
|
1725
2338
|
_client.close();
|
|
1726
2339
|
_client = null;
|
|
1727
2340
|
_resilientClient = null;
|
|
1728
2341
|
}
|
|
1729
2342
|
}
|
|
1730
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2343
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1731
2344
|
var init_database = __esm({
|
|
1732
2345
|
"src/lib/database.ts"() {
|
|
1733
2346
|
"use strict";
|
|
1734
2347
|
init_db_retry();
|
|
1735
2348
|
init_employees();
|
|
2349
|
+
init_database_adapter();
|
|
1736
2350
|
_client = null;
|
|
1737
2351
|
_resilientClient = null;
|
|
1738
2352
|
_walCheckpointTimer = null;
|
|
1739
2353
|
_daemonClient = null;
|
|
2354
|
+
_adapterClient = null;
|
|
1740
2355
|
initTurso = initDatabase;
|
|
1741
2356
|
disposeTurso = disposeDatabase;
|
|
1742
2357
|
}
|
|
@@ -1746,7 +2361,7 @@ var init_database = __esm({
|
|
|
1746
2361
|
init_employees();
|
|
1747
2362
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, unlinkSync as unlinkSync3, existsSync as existsSync4 } from "fs";
|
|
1748
2363
|
import { execSync as execSync2 } from "child_process";
|
|
1749
|
-
import
|
|
2364
|
+
import path5 from "path";
|
|
1750
2365
|
import { homedir } from "os";
|
|
1751
2366
|
|
|
1752
2367
|
// src/lib/global-procedures.ts
|
|
@@ -1891,9 +2506,9 @@ function isMainModule(importMetaUrl) {
|
|
|
1891
2506
|
|
|
1892
2507
|
// src/bin/exe-rename.ts
|
|
1893
2508
|
async function renameEmployee(oldName, newName, opts = {}) {
|
|
1894
|
-
const rosterPath = opts.rosterPath ??
|
|
1895
|
-
const identityDir = opts.identityDir ??
|
|
1896
|
-
const agentsDir = opts.agentsDir ??
|
|
2509
|
+
const rosterPath = opts.rosterPath ?? path5.join(homedir(), ".exe-os", "exe-employees.json");
|
|
2510
|
+
const identityDir = opts.identityDir ?? path5.join(homedir(), ".exe-os", "identity");
|
|
2511
|
+
const agentsDir = opts.agentsDir ?? path5.join(homedir(), ".claude", "agents");
|
|
1897
2512
|
const validation = validateEmployeeName(newName);
|
|
1898
2513
|
if (!validation.valid) {
|
|
1899
2514
|
return { success: false, error: validation.error };
|
|
@@ -1925,8 +2540,8 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
1925
2540
|
writeFileSync2(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1926
2541
|
}
|
|
1927
2542
|
});
|
|
1928
|
-
const oldIdentityPath =
|
|
1929
|
-
const newIdentityPath =
|
|
2543
|
+
const oldIdentityPath = path5.join(identityDir, `${rosterOldName}.md`);
|
|
2544
|
+
const newIdentityPath = path5.join(identityDir, `${newName}.md`);
|
|
1930
2545
|
if (existsSync4(oldIdentityPath)) {
|
|
1931
2546
|
const content = readFileSync4(oldIdentityPath, "utf-8");
|
|
1932
2547
|
const updatedContent = content.replace(
|
|
@@ -1945,8 +2560,8 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
1945
2560
|
}
|
|
1946
2561
|
});
|
|
1947
2562
|
}
|
|
1948
|
-
const oldAgentPath =
|
|
1949
|
-
const newAgentPath =
|
|
2563
|
+
const oldAgentPath = path5.join(agentsDir, `${rosterOldName}.md`);
|
|
2564
|
+
const newAgentPath = path5.join(agentsDir, `${newName}.md`);
|
|
1950
2565
|
if (existsSync4(oldAgentPath)) {
|
|
1951
2566
|
const agentContent = readFileSync4(oldAgentPath, "utf-8");
|
|
1952
2567
|
renameSync3(oldAgentPath, newAgentPath);
|
|
@@ -2033,9 +2648,9 @@ function removeOldSymlinks(name) {
|
|
|
2033
2648
|
try {
|
|
2034
2649
|
const exeBinPath = findExeBin2();
|
|
2035
2650
|
if (!exeBinPath) return;
|
|
2036
|
-
const binDir =
|
|
2651
|
+
const binDir = path5.dirname(exeBinPath);
|
|
2037
2652
|
for (const suffix of ["", "-opencode"]) {
|
|
2038
|
-
const linkPath =
|
|
2653
|
+
const linkPath = path5.join(binDir, `${name}${suffix}`);
|
|
2039
2654
|
if (existsSync4(linkPath)) {
|
|
2040
2655
|
try {
|
|
2041
2656
|
unlinkSync3(linkPath);
|