@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
|
@@ -263,6 +263,118 @@ var init_config = __esm({
|
|
|
263
263
|
}
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
+
// src/lib/runtime-table.ts
|
|
267
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
268
|
+
var init_runtime_table = __esm({
|
|
269
|
+
"src/lib/runtime-table.ts"() {
|
|
270
|
+
"use strict";
|
|
271
|
+
RUNTIME_TABLE = {
|
|
272
|
+
codex: {
|
|
273
|
+
binary: "codex",
|
|
274
|
+
launchMode: "interactive",
|
|
275
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
276
|
+
inlineFlag: "--no-alt-screen",
|
|
277
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
278
|
+
defaultModel: "gpt-5.4"
|
|
279
|
+
},
|
|
280
|
+
opencode: {
|
|
281
|
+
binary: "opencode",
|
|
282
|
+
launchMode: "exec",
|
|
283
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
284
|
+
inlineFlag: "",
|
|
285
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
286
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
DEFAULT_RUNTIME = "claude";
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// src/lib/agent-config.ts
|
|
294
|
+
var agent_config_exports = {};
|
|
295
|
+
__export(agent_config_exports, {
|
|
296
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
297
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
298
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
299
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
300
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
301
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
302
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
303
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
304
|
+
setAgentRuntime: () => setAgentRuntime
|
|
305
|
+
});
|
|
306
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
307
|
+
import path2 from "path";
|
|
308
|
+
function loadAgentConfig() {
|
|
309
|
+
if (!existsSync2(AGENT_CONFIG_PATH)) return {};
|
|
310
|
+
try {
|
|
311
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
312
|
+
} catch {
|
|
313
|
+
return {};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function saveAgentConfig(config) {
|
|
317
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
318
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
319
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
320
|
+
}
|
|
321
|
+
function getAgentRuntime(agentId) {
|
|
322
|
+
const config = loadAgentConfig();
|
|
323
|
+
const entry = config[agentId];
|
|
324
|
+
if (entry) return entry;
|
|
325
|
+
const orgDefault = config["default"];
|
|
326
|
+
if (orgDefault) return orgDefault;
|
|
327
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
328
|
+
}
|
|
329
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
330
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
331
|
+
if (!knownModels) {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (!knownModels.includes(model)) {
|
|
338
|
+
return {
|
|
339
|
+
ok: false,
|
|
340
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const config = loadAgentConfig();
|
|
344
|
+
config[agentId] = { runtime, model };
|
|
345
|
+
saveAgentConfig(config);
|
|
346
|
+
return { ok: true };
|
|
347
|
+
}
|
|
348
|
+
function clearAgentRuntime(agentId) {
|
|
349
|
+
const config = loadAgentConfig();
|
|
350
|
+
delete config[agentId];
|
|
351
|
+
saveAgentConfig(config);
|
|
352
|
+
}
|
|
353
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
354
|
+
var init_agent_config = __esm({
|
|
355
|
+
"src/lib/agent-config.ts"() {
|
|
356
|
+
"use strict";
|
|
357
|
+
init_config();
|
|
358
|
+
init_runtime_table();
|
|
359
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
360
|
+
KNOWN_RUNTIMES = {
|
|
361
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
362
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
363
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
364
|
+
};
|
|
365
|
+
RUNTIME_LABELS = {
|
|
366
|
+
claude: "Claude Code (Anthropic)",
|
|
367
|
+
codex: "Codex (OpenAI)",
|
|
368
|
+
opencode: "OpenCode (open source)"
|
|
369
|
+
};
|
|
370
|
+
DEFAULT_MODELS = {
|
|
371
|
+
claude: "claude-opus-4",
|
|
372
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
373
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
266
378
|
// src/lib/employees.ts
|
|
267
379
|
var employees_exports = {};
|
|
268
380
|
__export(employees_exports, {
|
|
@@ -278,6 +390,7 @@ __export(employees_exports, {
|
|
|
278
390
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
279
391
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
280
392
|
hasRole: () => hasRole,
|
|
393
|
+
hireEmployee: () => hireEmployee,
|
|
281
394
|
isCoordinatorName: () => isCoordinatorName,
|
|
282
395
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
283
396
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -290,9 +403,9 @@ __export(employees_exports, {
|
|
|
290
403
|
validateEmployeeName: () => validateEmployeeName
|
|
291
404
|
});
|
|
292
405
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
293
|
-
import { existsSync as
|
|
406
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
294
407
|
import { execSync } from "child_process";
|
|
295
|
-
import
|
|
408
|
+
import path3 from "path";
|
|
296
409
|
import os2 from "os";
|
|
297
410
|
function normalizeRole(role) {
|
|
298
411
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -329,7 +442,7 @@ function validateEmployeeName(name) {
|
|
|
329
442
|
return { valid: true };
|
|
330
443
|
}
|
|
331
444
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
332
|
-
if (!
|
|
445
|
+
if (!existsSync3(employeesPath)) {
|
|
333
446
|
return [];
|
|
334
447
|
}
|
|
335
448
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -340,13 +453,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
340
453
|
}
|
|
341
454
|
}
|
|
342
455
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
343
|
-
await mkdir2(
|
|
456
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
344
457
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
345
458
|
}
|
|
346
459
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
347
|
-
if (!
|
|
460
|
+
if (!existsSync3(employeesPath)) return [];
|
|
348
461
|
try {
|
|
349
|
-
return JSON.parse(
|
|
462
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
350
463
|
} catch {
|
|
351
464
|
return [];
|
|
352
465
|
}
|
|
@@ -388,6 +501,52 @@ function addEmployee(employees, employee) {
|
|
|
388
501
|
}
|
|
389
502
|
return [...employees, normalized];
|
|
390
503
|
}
|
|
504
|
+
function appendToCoordinatorTeam(employee) {
|
|
505
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
506
|
+
if (!coordinator) return;
|
|
507
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
508
|
+
if (!existsSync3(idPath)) return;
|
|
509
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
510
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
511
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
512
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
513
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
514
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
515
|
+
const entry = `
|
|
516
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
517
|
+
`;
|
|
518
|
+
let updated;
|
|
519
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
520
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
521
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
522
|
+
} else {
|
|
523
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
524
|
+
}
|
|
525
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
526
|
+
}
|
|
527
|
+
function capitalize(s) {
|
|
528
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
529
|
+
}
|
|
530
|
+
async function hireEmployee(employee) {
|
|
531
|
+
const employees = await loadEmployees();
|
|
532
|
+
const updated = addEmployee(employees, employee);
|
|
533
|
+
await saveEmployees(updated);
|
|
534
|
+
try {
|
|
535
|
+
appendToCoordinatorTeam(employee);
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
540
|
+
const config = loadAgentConfig2();
|
|
541
|
+
const name = employee.name.toLowerCase();
|
|
542
|
+
if (!config[name] && config["default"]) {
|
|
543
|
+
config[name] = { ...config["default"] };
|
|
544
|
+
saveAgentConfig2(config);
|
|
545
|
+
}
|
|
546
|
+
} catch {
|
|
547
|
+
}
|
|
548
|
+
return updated;
|
|
549
|
+
}
|
|
391
550
|
async function normalizeRosterCase(rosterPath) {
|
|
392
551
|
const employees = await loadEmployees(rosterPath);
|
|
393
552
|
let changed = false;
|
|
@@ -397,14 +556,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
397
556
|
emp.name = emp.name.toLowerCase();
|
|
398
557
|
changed = true;
|
|
399
558
|
try {
|
|
400
|
-
const identityDir =
|
|
401
|
-
const oldPath =
|
|
402
|
-
const newPath =
|
|
403
|
-
if (
|
|
559
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
560
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
561
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
562
|
+
if (existsSync3(oldPath) && !existsSync3(newPath)) {
|
|
404
563
|
renameSync2(oldPath, newPath);
|
|
405
|
-
} else if (
|
|
406
|
-
const content =
|
|
407
|
-
|
|
564
|
+
} else if (existsSync3(oldPath) && oldPath !== newPath) {
|
|
565
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
566
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
408
567
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
409
568
|
unlinkSync(oldPath);
|
|
410
569
|
}
|
|
@@ -434,7 +593,7 @@ function registerBinSymlinks(name) {
|
|
|
434
593
|
errors.push("Could not find 'exe-os' in PATH");
|
|
435
594
|
return { created, skipped, errors };
|
|
436
595
|
}
|
|
437
|
-
const binDir =
|
|
596
|
+
const binDir = path3.dirname(exeBinPath);
|
|
438
597
|
let target;
|
|
439
598
|
try {
|
|
440
599
|
target = readlinkSync(exeBinPath);
|
|
@@ -444,8 +603,8 @@ function registerBinSymlinks(name) {
|
|
|
444
603
|
}
|
|
445
604
|
for (const suffix of ["", "-opencode"]) {
|
|
446
605
|
const linkName = `${name}${suffix}`;
|
|
447
|
-
const linkPath =
|
|
448
|
-
if (
|
|
606
|
+
const linkPath = path3.join(binDir, linkName);
|
|
607
|
+
if (existsSync3(linkPath)) {
|
|
449
608
|
skipped.push(linkName);
|
|
450
609
|
continue;
|
|
451
610
|
}
|
|
@@ -458,21 +617,619 @@ function registerBinSymlinks(name) {
|
|
|
458
617
|
}
|
|
459
618
|
return { created, skipped, errors };
|
|
460
619
|
}
|
|
461
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
620
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
462
621
|
var init_employees = __esm({
|
|
463
622
|
"src/lib/employees.ts"() {
|
|
464
623
|
"use strict";
|
|
465
624
|
init_config();
|
|
466
|
-
EMPLOYEES_PATH =
|
|
625
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
467
626
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
468
627
|
COORDINATOR_ROLE = "COO";
|
|
469
628
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
629
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
630
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// src/lib/database-adapter.ts
|
|
635
|
+
import os3 from "os";
|
|
636
|
+
import path4 from "path";
|
|
637
|
+
import { createRequire } from "module";
|
|
638
|
+
import { pathToFileURL } from "url";
|
|
639
|
+
function quotedIdentifier(identifier) {
|
|
640
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
641
|
+
}
|
|
642
|
+
function unqualifiedTableName(name) {
|
|
643
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
644
|
+
const parts = raw.split(".");
|
|
645
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
646
|
+
}
|
|
647
|
+
function stripTrailingSemicolon(sql) {
|
|
648
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
649
|
+
}
|
|
650
|
+
function appendClause(sql, clause) {
|
|
651
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
652
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
653
|
+
if (!returningMatch) {
|
|
654
|
+
return `${trimmed}${clause}`;
|
|
655
|
+
}
|
|
656
|
+
const idx = returningMatch.index;
|
|
657
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
658
|
+
}
|
|
659
|
+
function normalizeStatement(stmt) {
|
|
660
|
+
if (typeof stmt === "string") {
|
|
661
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
662
|
+
}
|
|
663
|
+
const sql = stmt.sql;
|
|
664
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
665
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
666
|
+
}
|
|
667
|
+
return { kind: "named", sql, args: stmt.args };
|
|
668
|
+
}
|
|
669
|
+
function rewriteBooleanLiterals(sql) {
|
|
670
|
+
let out = sql;
|
|
671
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
672
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
673
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
674
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
675
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
676
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
677
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
678
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
679
|
+
}
|
|
680
|
+
return out;
|
|
681
|
+
}
|
|
682
|
+
function rewriteInsertOrIgnore(sql) {
|
|
683
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
684
|
+
return sql;
|
|
685
|
+
}
|
|
686
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
687
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
688
|
+
}
|
|
689
|
+
function rewriteInsertOrReplace(sql) {
|
|
690
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
691
|
+
if (!match) {
|
|
692
|
+
return sql;
|
|
693
|
+
}
|
|
694
|
+
const rawTable = match[1];
|
|
695
|
+
const rawColumns = match[2];
|
|
696
|
+
const remainder = match[3];
|
|
697
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
698
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
699
|
+
if (!conflictKeys?.length) {
|
|
700
|
+
return sql;
|
|
701
|
+
}
|
|
702
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
703
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
704
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
705
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
706
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
707
|
+
}
|
|
708
|
+
function rewriteSql(sql) {
|
|
709
|
+
let out = sql;
|
|
710
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
711
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
712
|
+
out = rewriteBooleanLiterals(out);
|
|
713
|
+
out = rewriteInsertOrReplace(out);
|
|
714
|
+
out = rewriteInsertOrIgnore(out);
|
|
715
|
+
return stripTrailingSemicolon(out);
|
|
716
|
+
}
|
|
717
|
+
function toBoolean(value) {
|
|
718
|
+
if (value === null || value === void 0) return value;
|
|
719
|
+
if (typeof value === "boolean") return value;
|
|
720
|
+
if (typeof value === "number") return value !== 0;
|
|
721
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
722
|
+
if (typeof value === "string") {
|
|
723
|
+
const normalized = value.trim().toLowerCase();
|
|
724
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
725
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
726
|
+
}
|
|
727
|
+
return Boolean(value);
|
|
728
|
+
}
|
|
729
|
+
function countQuestionMarks(sql, end) {
|
|
730
|
+
let count = 0;
|
|
731
|
+
let inSingle = false;
|
|
732
|
+
let inDouble = false;
|
|
733
|
+
let inLineComment = false;
|
|
734
|
+
let inBlockComment = false;
|
|
735
|
+
for (let i = 0; i < end; i++) {
|
|
736
|
+
const ch = sql[i];
|
|
737
|
+
const next = sql[i + 1];
|
|
738
|
+
if (inLineComment) {
|
|
739
|
+
if (ch === "\n") inLineComment = false;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (inBlockComment) {
|
|
743
|
+
if (ch === "*" && next === "/") {
|
|
744
|
+
inBlockComment = false;
|
|
745
|
+
i += 1;
|
|
746
|
+
}
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
750
|
+
inLineComment = true;
|
|
751
|
+
i += 1;
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
755
|
+
inBlockComment = true;
|
|
756
|
+
i += 1;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
760
|
+
inSingle = !inSingle;
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
764
|
+
inDouble = !inDouble;
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
768
|
+
count += 1;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return count;
|
|
772
|
+
}
|
|
773
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
774
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
775
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
776
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
777
|
+
for (const match of sql.matchAll(pattern)) {
|
|
778
|
+
const matchText = match[0];
|
|
779
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
780
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return indexes;
|
|
784
|
+
}
|
|
785
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
786
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
787
|
+
if (!match) return;
|
|
788
|
+
const rawTable = match[1];
|
|
789
|
+
const rawColumns = match[2];
|
|
790
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
791
|
+
if (!boolColumns?.size) return;
|
|
792
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
793
|
+
for (const [index, column] of columns.entries()) {
|
|
794
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
795
|
+
args[index] = toBoolean(args[index]);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
800
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
801
|
+
if (!match) return;
|
|
802
|
+
const rawTable = match[1];
|
|
803
|
+
const setClause = match[2];
|
|
804
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
805
|
+
if (!boolColumns?.size) return;
|
|
806
|
+
const assignments = setClause.split(",");
|
|
807
|
+
let placeholderIndex = 0;
|
|
808
|
+
for (const assignment of assignments) {
|
|
809
|
+
if (!assignment.includes("?")) continue;
|
|
810
|
+
placeholderIndex += 1;
|
|
811
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
812
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
813
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function coerceBooleanArgs(sql, args) {
|
|
818
|
+
const nextArgs = [...args];
|
|
819
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
820
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
821
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
822
|
+
for (const index of placeholderIndexes) {
|
|
823
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
824
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return nextArgs;
|
|
828
|
+
}
|
|
829
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
830
|
+
let out = "";
|
|
831
|
+
let placeholder = 0;
|
|
832
|
+
let inSingle = false;
|
|
833
|
+
let inDouble = false;
|
|
834
|
+
let inLineComment = false;
|
|
835
|
+
let inBlockComment = false;
|
|
836
|
+
for (let i = 0; i < sql.length; i++) {
|
|
837
|
+
const ch = sql[i];
|
|
838
|
+
const next = sql[i + 1];
|
|
839
|
+
if (inLineComment) {
|
|
840
|
+
out += ch;
|
|
841
|
+
if (ch === "\n") inLineComment = false;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (inBlockComment) {
|
|
845
|
+
out += ch;
|
|
846
|
+
if (ch === "*" && next === "/") {
|
|
847
|
+
out += next;
|
|
848
|
+
inBlockComment = false;
|
|
849
|
+
i += 1;
|
|
850
|
+
}
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
854
|
+
out += ch + next;
|
|
855
|
+
inLineComment = true;
|
|
856
|
+
i += 1;
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
860
|
+
out += ch + next;
|
|
861
|
+
inBlockComment = true;
|
|
862
|
+
i += 1;
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
866
|
+
inSingle = !inSingle;
|
|
867
|
+
out += ch;
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
871
|
+
inDouble = !inDouble;
|
|
872
|
+
out += ch;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
876
|
+
placeholder += 1;
|
|
877
|
+
out += `$${placeholder}`;
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
out += ch;
|
|
881
|
+
}
|
|
882
|
+
return out;
|
|
883
|
+
}
|
|
884
|
+
function translateStatementForPostgres(stmt) {
|
|
885
|
+
const normalized = normalizeStatement(stmt);
|
|
886
|
+
if (normalized.kind === "named") {
|
|
887
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
888
|
+
}
|
|
889
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
890
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
891
|
+
return {
|
|
892
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
893
|
+
args: coercedArgs
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function shouldBypassPostgres(stmt) {
|
|
897
|
+
const normalized = normalizeStatement(stmt);
|
|
898
|
+
if (normalized.kind === "named") {
|
|
899
|
+
return true;
|
|
900
|
+
}
|
|
901
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
902
|
+
}
|
|
903
|
+
function shouldFallbackOnError(error) {
|
|
904
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
905
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
906
|
+
}
|
|
907
|
+
function isReadQuery(sql) {
|
|
908
|
+
const trimmed = sql.trimStart();
|
|
909
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
910
|
+
}
|
|
911
|
+
function buildRow(row, columns) {
|
|
912
|
+
const values = columns.map((column) => row[column]);
|
|
913
|
+
return Object.assign(values, row);
|
|
914
|
+
}
|
|
915
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
916
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
917
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
918
|
+
return {
|
|
919
|
+
columns,
|
|
920
|
+
columnTypes: columns.map(() => ""),
|
|
921
|
+
rows: resultRows,
|
|
922
|
+
rowsAffected,
|
|
923
|
+
lastInsertRowid: void 0,
|
|
924
|
+
toJSON() {
|
|
925
|
+
return {
|
|
926
|
+
columns,
|
|
927
|
+
columnTypes: columns.map(() => ""),
|
|
928
|
+
rows,
|
|
929
|
+
rowsAffected,
|
|
930
|
+
lastInsertRowid: void 0
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
async function loadPrismaClient() {
|
|
936
|
+
if (!prismaClientPromise) {
|
|
937
|
+
prismaClientPromise = (async () => {
|
|
938
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
939
|
+
if (explicitPath) {
|
|
940
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
941
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
942
|
+
if (!PrismaClient2) {
|
|
943
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
944
|
+
}
|
|
945
|
+
return new PrismaClient2();
|
|
946
|
+
}
|
|
947
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
948
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
949
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
950
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
951
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
952
|
+
if (!PrismaClient) {
|
|
953
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
954
|
+
}
|
|
955
|
+
return new PrismaClient();
|
|
956
|
+
})();
|
|
957
|
+
}
|
|
958
|
+
return prismaClientPromise;
|
|
959
|
+
}
|
|
960
|
+
async function ensureCompatibilityViews(prisma) {
|
|
961
|
+
if (!compatibilityBootstrapPromise) {
|
|
962
|
+
compatibilityBootstrapPromise = (async () => {
|
|
963
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
964
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
965
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
966
|
+
"SELECT to_regclass($1) AS regclass",
|
|
967
|
+
relation
|
|
968
|
+
);
|
|
969
|
+
if (!rows[0]?.regclass) {
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
await prisma.$executeRawUnsafe(
|
|
973
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
})();
|
|
977
|
+
}
|
|
978
|
+
return compatibilityBootstrapPromise;
|
|
979
|
+
}
|
|
980
|
+
async function executeOnPrisma(executor, stmt) {
|
|
981
|
+
const translated = translateStatementForPostgres(stmt);
|
|
982
|
+
if (isReadQuery(translated.sql)) {
|
|
983
|
+
const rows = await executor.$queryRawUnsafe(
|
|
984
|
+
translated.sql,
|
|
985
|
+
...translated.args
|
|
986
|
+
);
|
|
987
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
988
|
+
}
|
|
989
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
990
|
+
return buildResultSet([], rowsAffected);
|
|
991
|
+
}
|
|
992
|
+
function splitSqlStatements(sql) {
|
|
993
|
+
const parts = [];
|
|
994
|
+
let current = "";
|
|
995
|
+
let inSingle = false;
|
|
996
|
+
let inDouble = false;
|
|
997
|
+
let inLineComment = false;
|
|
998
|
+
let inBlockComment = false;
|
|
999
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1000
|
+
const ch = sql[i];
|
|
1001
|
+
const next = sql[i + 1];
|
|
1002
|
+
if (inLineComment) {
|
|
1003
|
+
current += ch;
|
|
1004
|
+
if (ch === "\n") inLineComment = false;
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (inBlockComment) {
|
|
1008
|
+
current += ch;
|
|
1009
|
+
if (ch === "*" && next === "/") {
|
|
1010
|
+
current += next;
|
|
1011
|
+
inBlockComment = false;
|
|
1012
|
+
i += 1;
|
|
1013
|
+
}
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1017
|
+
current += ch + next;
|
|
1018
|
+
inLineComment = true;
|
|
1019
|
+
i += 1;
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1023
|
+
current += ch + next;
|
|
1024
|
+
inBlockComment = true;
|
|
1025
|
+
i += 1;
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1029
|
+
inSingle = !inSingle;
|
|
1030
|
+
current += ch;
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1034
|
+
inDouble = !inDouble;
|
|
1035
|
+
current += ch;
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1039
|
+
if (current.trim()) {
|
|
1040
|
+
parts.push(current.trim());
|
|
1041
|
+
}
|
|
1042
|
+
current = "";
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
current += ch;
|
|
1046
|
+
}
|
|
1047
|
+
if (current.trim()) {
|
|
1048
|
+
parts.push(current.trim());
|
|
1049
|
+
}
|
|
1050
|
+
return parts;
|
|
1051
|
+
}
|
|
1052
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1053
|
+
const prisma = await loadPrismaClient();
|
|
1054
|
+
await ensureCompatibilityViews(prisma);
|
|
1055
|
+
let closed = false;
|
|
1056
|
+
let adapter;
|
|
1057
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1058
|
+
if (!fallbackClient) {
|
|
1059
|
+
if (error) throw error;
|
|
1060
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1061
|
+
}
|
|
1062
|
+
if (error) {
|
|
1063
|
+
process.stderr.write(
|
|
1064
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1065
|
+
`
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
return fallbackClient.execute(stmt);
|
|
1069
|
+
};
|
|
1070
|
+
adapter = {
|
|
1071
|
+
async execute(stmt) {
|
|
1072
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1073
|
+
return fallbackExecute(stmt);
|
|
1074
|
+
}
|
|
1075
|
+
try {
|
|
1076
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (shouldFallbackOnError(error)) {
|
|
1079
|
+
return fallbackExecute(stmt, error);
|
|
1080
|
+
}
|
|
1081
|
+
throw error;
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
async batch(stmts, mode) {
|
|
1085
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1086
|
+
if (!fallbackClient) {
|
|
1087
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1088
|
+
}
|
|
1089
|
+
return fallbackClient.batch(stmts, mode);
|
|
1090
|
+
}
|
|
1091
|
+
try {
|
|
1092
|
+
if (prisma.$transaction) {
|
|
1093
|
+
return await prisma.$transaction(async (tx) => {
|
|
1094
|
+
const results2 = [];
|
|
1095
|
+
for (const stmt of stmts) {
|
|
1096
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1097
|
+
}
|
|
1098
|
+
return results2;
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const results = [];
|
|
1102
|
+
for (const stmt of stmts) {
|
|
1103
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1104
|
+
}
|
|
1105
|
+
return results;
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1108
|
+
process.stderr.write(
|
|
1109
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1110
|
+
`
|
|
1111
|
+
);
|
|
1112
|
+
return fallbackClient.batch(stmts, mode);
|
|
1113
|
+
}
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
async migrate(stmts) {
|
|
1118
|
+
if (fallbackClient) {
|
|
1119
|
+
return fallbackClient.migrate(stmts);
|
|
1120
|
+
}
|
|
1121
|
+
return adapter.batch(stmts, "deferred");
|
|
1122
|
+
},
|
|
1123
|
+
async transaction(mode) {
|
|
1124
|
+
if (!fallbackClient) {
|
|
1125
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1126
|
+
}
|
|
1127
|
+
return fallbackClient.transaction(mode);
|
|
1128
|
+
},
|
|
1129
|
+
async executeMultiple(sql) {
|
|
1130
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1131
|
+
return fallbackClient.executeMultiple(sql);
|
|
1132
|
+
}
|
|
1133
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1134
|
+
await adapter.execute(statement);
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
async sync() {
|
|
1138
|
+
if (fallbackClient) {
|
|
1139
|
+
return fallbackClient.sync();
|
|
1140
|
+
}
|
|
1141
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1142
|
+
},
|
|
1143
|
+
close() {
|
|
1144
|
+
closed = true;
|
|
1145
|
+
prismaClientPromise = null;
|
|
1146
|
+
compatibilityBootstrapPromise = null;
|
|
1147
|
+
void prisma.$disconnect?.();
|
|
1148
|
+
},
|
|
1149
|
+
get closed() {
|
|
1150
|
+
return closed;
|
|
1151
|
+
},
|
|
1152
|
+
get protocol() {
|
|
1153
|
+
return "prisma-postgres";
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
return adapter;
|
|
1157
|
+
}
|
|
1158
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1159
|
+
var init_database_adapter = __esm({
|
|
1160
|
+
"src/lib/database-adapter.ts"() {
|
|
1161
|
+
"use strict";
|
|
1162
|
+
VIEW_MAPPINGS = [
|
|
1163
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1164
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1165
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1166
|
+
{ view: "entities", source: "memory.entities" },
|
|
1167
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1168
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1169
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1170
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1171
|
+
{ view: "messages", source: "memory.messages" },
|
|
1172
|
+
{ view: "users", source: "wiki.users" },
|
|
1173
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1174
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1175
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1176
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1177
|
+
];
|
|
1178
|
+
UPSERT_KEYS = {
|
|
1179
|
+
memories: ["id"],
|
|
1180
|
+
tasks: ["id"],
|
|
1181
|
+
behaviors: ["id"],
|
|
1182
|
+
entities: ["id"],
|
|
1183
|
+
relationships: ["id"],
|
|
1184
|
+
entity_aliases: ["alias"],
|
|
1185
|
+
notifications: ["id"],
|
|
1186
|
+
messages: ["id"],
|
|
1187
|
+
users: ["id"],
|
|
1188
|
+
workspaces: ["id"],
|
|
1189
|
+
workspace_users: ["id"],
|
|
1190
|
+
documents: ["id"],
|
|
1191
|
+
chats: ["id"]
|
|
1192
|
+
};
|
|
1193
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1194
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1195
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1196
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1197
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1198
|
+
};
|
|
1199
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1200
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1201
|
+
);
|
|
1202
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1203
|
+
/\bPRAGMA\b/i,
|
|
1204
|
+
/\bsqlite_master\b/i,
|
|
1205
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1206
|
+
/\bMATCH\b/i,
|
|
1207
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1208
|
+
/\bjson_extract\s*\(/i,
|
|
1209
|
+
/\bjulianday\s*\(/i,
|
|
1210
|
+
/\bstrftime\s*\(/i,
|
|
1211
|
+
/\blast_insert_rowid\s*\(/i
|
|
1212
|
+
];
|
|
1213
|
+
prismaClientPromise = null;
|
|
1214
|
+
compatibilityBootstrapPromise = null;
|
|
470
1215
|
}
|
|
471
1216
|
});
|
|
472
1217
|
|
|
473
1218
|
// src/lib/database.ts
|
|
474
1219
|
import { createClient } from "@libsql/client";
|
|
475
1220
|
async function initDatabase(config) {
|
|
1221
|
+
if (_walCheckpointTimer) {
|
|
1222
|
+
clearInterval(_walCheckpointTimer);
|
|
1223
|
+
_walCheckpointTimer = null;
|
|
1224
|
+
}
|
|
1225
|
+
if (_daemonClient) {
|
|
1226
|
+
_daemonClient.close();
|
|
1227
|
+
_daemonClient = null;
|
|
1228
|
+
}
|
|
1229
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1230
|
+
_adapterClient.close();
|
|
1231
|
+
}
|
|
1232
|
+
_adapterClient = null;
|
|
476
1233
|
if (_client) {
|
|
477
1234
|
_client.close();
|
|
478
1235
|
_client = null;
|
|
@@ -486,6 +1243,7 @@ async function initDatabase(config) {
|
|
|
486
1243
|
}
|
|
487
1244
|
_client = createClient(opts);
|
|
488
1245
|
_resilientClient = wrapWithRetry(_client);
|
|
1246
|
+
_adapterClient = _resilientClient;
|
|
489
1247
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
490
1248
|
});
|
|
491
1249
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -496,11 +1254,17 @@ async function initDatabase(config) {
|
|
|
496
1254
|
});
|
|
497
1255
|
}, 3e4);
|
|
498
1256
|
_walCheckpointTimer.unref();
|
|
1257
|
+
if (process.env.DATABASE_URL) {
|
|
1258
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1259
|
+
}
|
|
499
1260
|
}
|
|
500
1261
|
function getClient() {
|
|
501
|
-
if (!
|
|
1262
|
+
if (!_adapterClient) {
|
|
502
1263
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
503
1264
|
}
|
|
1265
|
+
if (process.env.DATABASE_URL) {
|
|
1266
|
+
return _adapterClient;
|
|
1267
|
+
}
|
|
504
1268
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
505
1269
|
return _resilientClient;
|
|
506
1270
|
}
|
|
@@ -1440,16 +2204,18 @@ async function ensureSchema() {
|
|
|
1440
2204
|
}
|
|
1441
2205
|
}
|
|
1442
2206
|
}
|
|
1443
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso;
|
|
2207
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1444
2208
|
var init_database = __esm({
|
|
1445
2209
|
"src/lib/database.ts"() {
|
|
1446
2210
|
"use strict";
|
|
1447
2211
|
init_db_retry();
|
|
1448
2212
|
init_employees();
|
|
2213
|
+
init_database_adapter();
|
|
1449
2214
|
_client = null;
|
|
1450
2215
|
_resilientClient = null;
|
|
1451
2216
|
_walCheckpointTimer = null;
|
|
1452
2217
|
_daemonClient = null;
|
|
2218
|
+
_adapterClient = null;
|
|
1453
2219
|
initTurso = initDatabase;
|
|
1454
2220
|
}
|
|
1455
2221
|
});
|
|
@@ -1522,13 +2288,13 @@ __export(shard_manager_exports, {
|
|
|
1522
2288
|
listShards: () => listShards,
|
|
1523
2289
|
shardExists: () => shardExists
|
|
1524
2290
|
});
|
|
1525
|
-
import
|
|
1526
|
-
import { existsSync as
|
|
2291
|
+
import path6 from "path";
|
|
2292
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1527
2293
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1528
2294
|
function initShardManager(encryptionKey) {
|
|
1529
2295
|
_encryptionKey = encryptionKey;
|
|
1530
|
-
if (!
|
|
1531
|
-
|
|
2296
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
2297
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1532
2298
|
}
|
|
1533
2299
|
_shardingEnabled = true;
|
|
1534
2300
|
}
|
|
@@ -1548,7 +2314,7 @@ function getShardClient(projectName) {
|
|
|
1548
2314
|
}
|
|
1549
2315
|
const cached = _shards.get(safeName);
|
|
1550
2316
|
if (cached) return cached;
|
|
1551
|
-
const dbPath =
|
|
2317
|
+
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
1552
2318
|
const client = createClient2({
|
|
1553
2319
|
url: `file:${dbPath}`,
|
|
1554
2320
|
encryptionKey: _encryptionKey
|
|
@@ -1558,10 +2324,10 @@ function getShardClient(projectName) {
|
|
|
1558
2324
|
}
|
|
1559
2325
|
function shardExists(projectName) {
|
|
1560
2326
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1561
|
-
return
|
|
2327
|
+
return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
1562
2328
|
}
|
|
1563
2329
|
function listShards() {
|
|
1564
|
-
if (!
|
|
2330
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1565
2331
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1566
2332
|
}
|
|
1567
2333
|
async function ensureShardSchema(client) {
|
|
@@ -1635,7 +2401,23 @@ async function ensureShardSchema(client) {
|
|
|
1635
2401
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1636
2402
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1637
2403
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1638
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2404
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2405
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2406
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2407
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2408
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2409
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2410
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2411
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2412
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2413
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2414
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2415
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2416
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2417
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2418
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2419
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2420
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1639
2421
|
]) {
|
|
1640
2422
|
try {
|
|
1641
2423
|
await client.execute(col);
|
|
@@ -1747,7 +2529,7 @@ var init_shard_manager = __esm({
|
|
|
1747
2529
|
"src/lib/shard-manager.ts"() {
|
|
1748
2530
|
"use strict";
|
|
1749
2531
|
init_config();
|
|
1750
|
-
SHARDS_DIR =
|
|
2532
|
+
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
1751
2533
|
_shards = /* @__PURE__ */ new Map();
|
|
1752
2534
|
_encryptionKey = null;
|
|
1753
2535
|
_shardingEnabled = false;
|
|
@@ -1943,13 +2725,13 @@ ${p.content}`).join("\n\n");
|
|
|
1943
2725
|
|
|
1944
2726
|
// src/lib/notifications.ts
|
|
1945
2727
|
import crypto from "crypto";
|
|
1946
|
-
import
|
|
1947
|
-
import
|
|
2728
|
+
import path7 from "path";
|
|
2729
|
+
import os5 from "os";
|
|
1948
2730
|
import {
|
|
1949
|
-
readFileSync as
|
|
2731
|
+
readFileSync as readFileSync4,
|
|
1950
2732
|
readdirSync as readdirSync2,
|
|
1951
2733
|
unlinkSync as unlinkSync2,
|
|
1952
|
-
existsSync as
|
|
2734
|
+
existsSync as existsSync6,
|
|
1953
2735
|
rmdirSync
|
|
1954
2736
|
} from "fs";
|
|
1955
2737
|
async function writeNotification(notification) {
|
|
@@ -1994,13 +2776,13 @@ var init_notifications = __esm({
|
|
|
1994
2776
|
});
|
|
1995
2777
|
|
|
1996
2778
|
// src/lib/session-registry.ts
|
|
1997
|
-
import { readFileSync as
|
|
1998
|
-
import
|
|
1999
|
-
import
|
|
2779
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "fs";
|
|
2780
|
+
import path8 from "path";
|
|
2781
|
+
import os6 from "os";
|
|
2000
2782
|
function registerSession(entry) {
|
|
2001
|
-
const dir =
|
|
2002
|
-
if (!
|
|
2003
|
-
|
|
2783
|
+
const dir = path8.dirname(REGISTRY_PATH);
|
|
2784
|
+
if (!existsSync7(dir)) {
|
|
2785
|
+
mkdirSync3(dir, { recursive: true });
|
|
2004
2786
|
}
|
|
2005
2787
|
const sessions = listSessions();
|
|
2006
2788
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -2009,11 +2791,11 @@ function registerSession(entry) {
|
|
|
2009
2791
|
} else {
|
|
2010
2792
|
sessions.push(entry);
|
|
2011
2793
|
}
|
|
2012
|
-
|
|
2794
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
2013
2795
|
}
|
|
2014
2796
|
function listSessions() {
|
|
2015
2797
|
try {
|
|
2016
|
-
const raw =
|
|
2798
|
+
const raw = readFileSync5(REGISTRY_PATH, "utf8");
|
|
2017
2799
|
return JSON.parse(raw);
|
|
2018
2800
|
} catch {
|
|
2019
2801
|
return [];
|
|
@@ -2023,7 +2805,7 @@ var REGISTRY_PATH;
|
|
|
2023
2805
|
var init_session_registry = __esm({
|
|
2024
2806
|
"src/lib/session-registry.ts"() {
|
|
2025
2807
|
"use strict";
|
|
2026
|
-
REGISTRY_PATH =
|
|
2808
|
+
REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
2027
2809
|
}
|
|
2028
2810
|
});
|
|
2029
2811
|
|
|
@@ -2275,67 +3057,6 @@ var init_provider_table = __esm({
|
|
|
2275
3057
|
}
|
|
2276
3058
|
});
|
|
2277
3059
|
|
|
2278
|
-
// src/lib/runtime-table.ts
|
|
2279
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
2280
|
-
var init_runtime_table = __esm({
|
|
2281
|
-
"src/lib/runtime-table.ts"() {
|
|
2282
|
-
"use strict";
|
|
2283
|
-
RUNTIME_TABLE = {
|
|
2284
|
-
codex: {
|
|
2285
|
-
binary: "codex",
|
|
2286
|
-
launchMode: "interactive",
|
|
2287
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
2288
|
-
inlineFlag: "--no-alt-screen",
|
|
2289
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
2290
|
-
defaultModel: "gpt-5.4"
|
|
2291
|
-
},
|
|
2292
|
-
opencode: {
|
|
2293
|
-
binary: "opencode",
|
|
2294
|
-
launchMode: "exec",
|
|
2295
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
2296
|
-
inlineFlag: "",
|
|
2297
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
2298
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
2299
|
-
}
|
|
2300
|
-
};
|
|
2301
|
-
DEFAULT_RUNTIME = "claude";
|
|
2302
|
-
}
|
|
2303
|
-
});
|
|
2304
|
-
|
|
2305
|
-
// src/lib/agent-config.ts
|
|
2306
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2307
|
-
import path7 from "path";
|
|
2308
|
-
function loadAgentConfig() {
|
|
2309
|
-
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
2310
|
-
try {
|
|
2311
|
-
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
2312
|
-
} catch {
|
|
2313
|
-
return {};
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
function getAgentRuntime(agentId) {
|
|
2317
|
-
const config = loadAgentConfig();
|
|
2318
|
-
const entry = config[agentId];
|
|
2319
|
-
if (entry) return entry;
|
|
2320
|
-
const orgDefault = config["default"];
|
|
2321
|
-
if (orgDefault) return orgDefault;
|
|
2322
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
2323
|
-
}
|
|
2324
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
2325
|
-
var init_agent_config = __esm({
|
|
2326
|
-
"src/lib/agent-config.ts"() {
|
|
2327
|
-
"use strict";
|
|
2328
|
-
init_config();
|
|
2329
|
-
init_runtime_table();
|
|
2330
|
-
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2331
|
-
DEFAULT_MODELS = {
|
|
2332
|
-
claude: "claude-opus-4",
|
|
2333
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
2334
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
2335
|
-
};
|
|
2336
|
-
}
|
|
2337
|
-
});
|
|
2338
|
-
|
|
2339
3060
|
// src/lib/intercom-queue.ts
|
|
2340
3061
|
var intercom_queue_exports = {};
|
|
2341
3062
|
__export(intercom_queue_exports, {
|
|
@@ -2346,10 +3067,10 @@ __export(intercom_queue_exports, {
|
|
|
2346
3067
|
readQueue: () => readQueue
|
|
2347
3068
|
});
|
|
2348
3069
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2349
|
-
import
|
|
2350
|
-
import
|
|
3070
|
+
import path9 from "path";
|
|
3071
|
+
import os7 from "os";
|
|
2351
3072
|
function ensureDir() {
|
|
2352
|
-
const dir =
|
|
3073
|
+
const dir = path9.dirname(QUEUE_PATH);
|
|
2353
3074
|
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
2354
3075
|
}
|
|
2355
3076
|
function readQueue() {
|
|
@@ -2455,26 +3176,26 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
2455
3176
|
var init_intercom_queue = __esm({
|
|
2456
3177
|
"src/lib/intercom-queue.ts"() {
|
|
2457
3178
|
"use strict";
|
|
2458
|
-
QUEUE_PATH =
|
|
3179
|
+
QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
2459
3180
|
MAX_RETRIES2 = 5;
|
|
2460
3181
|
TTL_MS = 60 * 60 * 1e3;
|
|
2461
|
-
INTERCOM_LOG =
|
|
3182
|
+
INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
2462
3183
|
}
|
|
2463
3184
|
});
|
|
2464
3185
|
|
|
2465
3186
|
// src/lib/license.ts
|
|
2466
3187
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
2467
3188
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2468
|
-
import
|
|
3189
|
+
import path10 from "path";
|
|
2469
3190
|
import { jwtVerify, importSPKI } from "jose";
|
|
2470
3191
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2471
3192
|
var init_license = __esm({
|
|
2472
3193
|
"src/lib/license.ts"() {
|
|
2473
3194
|
"use strict";
|
|
2474
3195
|
init_config();
|
|
2475
|
-
LICENSE_PATH =
|
|
2476
|
-
CACHE_PATH =
|
|
2477
|
-
DEVICE_ID_PATH =
|
|
3196
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3197
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3198
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
2478
3199
|
PLAN_LIMITS = {
|
|
2479
3200
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2480
3201
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2487,7 +3208,7 @@ var init_license = __esm({
|
|
|
2487
3208
|
|
|
2488
3209
|
// src/lib/plan-limits.ts
|
|
2489
3210
|
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
2490
|
-
import
|
|
3211
|
+
import path11 from "path";
|
|
2491
3212
|
function getLicenseSync() {
|
|
2492
3213
|
try {
|
|
2493
3214
|
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
@@ -2559,7 +3280,7 @@ var init_plan_limits = __esm({
|
|
|
2559
3280
|
this.name = "PlanLimitError";
|
|
2560
3281
|
}
|
|
2561
3282
|
};
|
|
2562
|
-
CACHE_PATH2 =
|
|
3283
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
2563
3284
|
}
|
|
2564
3285
|
});
|
|
2565
3286
|
|
|
@@ -2908,12 +3629,12 @@ __export(tmux_routing_exports, {
|
|
|
2908
3629
|
});
|
|
2909
3630
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
2910
3631
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
2911
|
-
import
|
|
2912
|
-
import
|
|
3632
|
+
import path12 from "path";
|
|
3633
|
+
import os8 from "os";
|
|
2913
3634
|
import { fileURLToPath } from "url";
|
|
2914
3635
|
import { unlinkSync as unlinkSync3 } from "fs";
|
|
2915
3636
|
function spawnLockPath(sessionName) {
|
|
2916
|
-
return
|
|
3637
|
+
return path12.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
2917
3638
|
}
|
|
2918
3639
|
function isProcessAlive(pid) {
|
|
2919
3640
|
try {
|
|
@@ -2950,8 +3671,8 @@ function releaseSpawnLock(sessionName) {
|
|
|
2950
3671
|
function resolveBehaviorsExporterScript() {
|
|
2951
3672
|
try {
|
|
2952
3673
|
const thisFile = fileURLToPath(import.meta.url);
|
|
2953
|
-
const scriptPath =
|
|
2954
|
-
|
|
3674
|
+
const scriptPath = path12.join(
|
|
3675
|
+
path12.dirname(thisFile),
|
|
2955
3676
|
"..",
|
|
2956
3677
|
"bin",
|
|
2957
3678
|
"exe-export-behaviors.js"
|
|
@@ -3026,7 +3747,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3026
3747
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3027
3748
|
}
|
|
3028
3749
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3029
|
-
const filePath =
|
|
3750
|
+
const filePath = path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3030
3751
|
writeFileSync6(filePath, JSON.stringify({
|
|
3031
3752
|
parentExe: rootExe,
|
|
3032
3753
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -3035,7 +3756,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3035
3756
|
}
|
|
3036
3757
|
function getParentExe(sessionKey) {
|
|
3037
3758
|
try {
|
|
3038
|
-
const data = JSON.parse(readFileSync9(
|
|
3759
|
+
const data = JSON.parse(readFileSync9(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3039
3760
|
return data.parentExe || null;
|
|
3040
3761
|
} catch {
|
|
3041
3762
|
return null;
|
|
@@ -3044,7 +3765,7 @@ function getParentExe(sessionKey) {
|
|
|
3044
3765
|
function getDispatchedBy(sessionKey) {
|
|
3045
3766
|
try {
|
|
3046
3767
|
const data = JSON.parse(readFileSync9(
|
|
3047
|
-
|
|
3768
|
+
path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
3048
3769
|
"utf8"
|
|
3049
3770
|
));
|
|
3050
3771
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -3230,7 +3951,7 @@ function sendIntercom(targetSession) {
|
|
|
3230
3951
|
try {
|
|
3231
3952
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
3232
3953
|
const agent = baseAgentName(rawAgent);
|
|
3233
|
-
const markerPath =
|
|
3954
|
+
const markerPath = path12.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
3234
3955
|
if (existsSync11(markerPath)) {
|
|
3235
3956
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
3236
3957
|
return "debounced";
|
|
@@ -3240,7 +3961,7 @@ function sendIntercom(targetSession) {
|
|
|
3240
3961
|
try {
|
|
3241
3962
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
3242
3963
|
const agent = baseAgentName(rawAgent);
|
|
3243
|
-
const taskDir =
|
|
3964
|
+
const taskDir = path12.join(process.cwd(), "exe", agent);
|
|
3244
3965
|
if (existsSync11(taskDir)) {
|
|
3245
3966
|
const files = readdirSync3(taskDir).filter(
|
|
3246
3967
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
@@ -3374,8 +4095,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3374
4095
|
const transport = getTransport();
|
|
3375
4096
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
3376
4097
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
3377
|
-
const logDir =
|
|
3378
|
-
const logFile =
|
|
4098
|
+
const logDir = path12.join(os8.homedir(), ".exe-os", "session-logs");
|
|
4099
|
+
const logFile = path12.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
3379
4100
|
if (!existsSync11(logDir)) {
|
|
3380
4101
|
mkdirSync6(logDir, { recursive: true });
|
|
3381
4102
|
}
|
|
@@ -3383,14 +4104,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3383
4104
|
let cleanupSuffix = "";
|
|
3384
4105
|
try {
|
|
3385
4106
|
const thisFile = fileURLToPath(import.meta.url);
|
|
3386
|
-
const cleanupScript =
|
|
4107
|
+
const cleanupScript = path12.join(path12.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
3387
4108
|
if (existsSync11(cleanupScript)) {
|
|
3388
4109
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
3389
4110
|
}
|
|
3390
4111
|
} catch {
|
|
3391
4112
|
}
|
|
3392
4113
|
try {
|
|
3393
|
-
const claudeJsonPath =
|
|
4114
|
+
const claudeJsonPath = path12.join(os8.homedir(), ".claude.json");
|
|
3394
4115
|
let claudeJson = {};
|
|
3395
4116
|
try {
|
|
3396
4117
|
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
@@ -3405,10 +4126,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3405
4126
|
} catch {
|
|
3406
4127
|
}
|
|
3407
4128
|
try {
|
|
3408
|
-
const settingsDir =
|
|
4129
|
+
const settingsDir = path12.join(os8.homedir(), ".claude", "projects");
|
|
3409
4130
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
3410
|
-
const projSettingsDir =
|
|
3411
|
-
const settingsPath =
|
|
4131
|
+
const projSettingsDir = path12.join(settingsDir, normalizedKey);
|
|
4132
|
+
const settingsPath = path12.join(projSettingsDir, "settings.json");
|
|
3412
4133
|
let settings = {};
|
|
3413
4134
|
try {
|
|
3414
4135
|
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
@@ -3455,8 +4176,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3455
4176
|
let behaviorsFlag = "";
|
|
3456
4177
|
let legacyFallbackWarned = false;
|
|
3457
4178
|
if (!useExeAgent && !useBinSymlink) {
|
|
3458
|
-
const identityPath =
|
|
3459
|
-
|
|
4179
|
+
const identityPath = path12.join(
|
|
4180
|
+
os8.homedir(),
|
|
3460
4181
|
".exe-os",
|
|
3461
4182
|
"identity",
|
|
3462
4183
|
`${employeeName}.md`
|
|
@@ -3471,7 +4192,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3471
4192
|
}
|
|
3472
4193
|
const behaviorsFile = exportBehaviorsSync(
|
|
3473
4194
|
employeeName,
|
|
3474
|
-
|
|
4195
|
+
path12.basename(spawnCwd),
|
|
3475
4196
|
sessionName
|
|
3476
4197
|
);
|
|
3477
4198
|
if (behaviorsFile) {
|
|
@@ -3486,9 +4207,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3486
4207
|
}
|
|
3487
4208
|
let sessionContextFlag = "";
|
|
3488
4209
|
try {
|
|
3489
|
-
const ctxDir =
|
|
4210
|
+
const ctxDir = path12.join(os8.homedir(), ".exe-os", "session-cache");
|
|
3490
4211
|
mkdirSync6(ctxDir, { recursive: true });
|
|
3491
|
-
const ctxFile =
|
|
4212
|
+
const ctxFile = path12.join(ctxDir, `session-context-${sessionName}.md`);
|
|
3492
4213
|
const ctxContent = [
|
|
3493
4214
|
`## Session Context`,
|
|
3494
4215
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -3572,7 +4293,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3572
4293
|
transport.pipeLog(sessionName, logFile);
|
|
3573
4294
|
try {
|
|
3574
4295
|
const mySession = getMySession();
|
|
3575
|
-
const dispatchInfo =
|
|
4296
|
+
const dispatchInfo = path12.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
3576
4297
|
writeFileSync6(dispatchInfo, JSON.stringify({
|
|
3577
4298
|
dispatchedBy: mySession,
|
|
3578
4299
|
rootExe: exeSession,
|
|
@@ -3647,15 +4368,15 @@ var init_tmux_routing = __esm({
|
|
|
3647
4368
|
init_intercom_queue();
|
|
3648
4369
|
init_plan_limits();
|
|
3649
4370
|
init_employees();
|
|
3650
|
-
SPAWN_LOCK_DIR =
|
|
3651
|
-
SESSION_CACHE =
|
|
4371
|
+
SPAWN_LOCK_DIR = path12.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
4372
|
+
SESSION_CACHE = path12.join(os8.homedir(), ".exe-os", "session-cache");
|
|
3652
4373
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
3653
4374
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
3654
4375
|
VERIFY_PANE_LINES = 200;
|
|
3655
4376
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
3656
4377
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
3657
|
-
INTERCOM_LOG2 =
|
|
3658
|
-
DEBOUNCE_FILE =
|
|
4378
|
+
INTERCOM_LOG2 = path12.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
4379
|
+
DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3659
4380
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
3660
4381
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
3661
4382
|
}
|
|
@@ -3687,8 +4408,8 @@ var init_task_scope = __esm({
|
|
|
3687
4408
|
|
|
3688
4409
|
// src/lib/tasks-crud.ts
|
|
3689
4410
|
import crypto3 from "crypto";
|
|
3690
|
-
import
|
|
3691
|
-
import
|
|
4411
|
+
import path13 from "path";
|
|
4412
|
+
import os9 from "os";
|
|
3692
4413
|
import { execSync as execSync5 } from "child_process";
|
|
3693
4414
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3694
4415
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
@@ -3866,8 +4587,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3866
4587
|
}
|
|
3867
4588
|
if (input.baseDir) {
|
|
3868
4589
|
try {
|
|
3869
|
-
await mkdir4(
|
|
3870
|
-
await mkdir4(
|
|
4590
|
+
await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4591
|
+
await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3871
4592
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3872
4593
|
await ensureGitignoreExe(input.baseDir);
|
|
3873
4594
|
} catch {
|
|
@@ -3903,9 +4624,9 @@ ${laneWarning}` : laneWarning;
|
|
|
3903
4624
|
});
|
|
3904
4625
|
if (input.baseDir) {
|
|
3905
4626
|
try {
|
|
3906
|
-
const EXE_OS_DIR =
|
|
3907
|
-
const mdPath =
|
|
3908
|
-
const mdDir =
|
|
4627
|
+
const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
|
|
4628
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
4629
|
+
const mdDir = path13.dirname(mdPath);
|
|
3909
4630
|
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
3910
4631
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3911
4632
|
const mdContent = `# ${input.title}
|
|
@@ -4206,7 +4927,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4206
4927
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4207
4928
|
}
|
|
4208
4929
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4209
|
-
const archPath =
|
|
4930
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4210
4931
|
try {
|
|
4211
4932
|
if (existsSync12(archPath)) return;
|
|
4212
4933
|
const template = [
|
|
@@ -4241,7 +4962,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4241
4962
|
}
|
|
4242
4963
|
}
|
|
4243
4964
|
async function ensureGitignoreExe(baseDir) {
|
|
4244
|
-
const gitignorePath =
|
|
4965
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
4245
4966
|
try {
|
|
4246
4967
|
if (existsSync12(gitignorePath)) {
|
|
4247
4968
|
const content = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -4275,13 +4996,13 @@ var init_tasks_crud = __esm({
|
|
|
4275
4996
|
});
|
|
4276
4997
|
|
|
4277
4998
|
// src/lib/tasks-review.ts
|
|
4278
|
-
import
|
|
4999
|
+
import path14 from "path";
|
|
4279
5000
|
import { existsSync as existsSync13, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
4280
5001
|
async function countPendingReviews(sessionScope) {
|
|
4281
5002
|
const client = getClient();
|
|
4282
5003
|
if (sessionScope) {
|
|
4283
5004
|
const result2 = await client.execute({
|
|
4284
|
-
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND
|
|
5005
|
+
sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
|
|
4285
5006
|
args: [sessionScope]
|
|
4286
5007
|
});
|
|
4287
5008
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -4457,11 +5178,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4457
5178
|
);
|
|
4458
5179
|
}
|
|
4459
5180
|
try {
|
|
4460
|
-
const cacheDir =
|
|
5181
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4461
5182
|
if (existsSync13(cacheDir)) {
|
|
4462
5183
|
for (const f of readdirSync4(cacheDir)) {
|
|
4463
5184
|
if (f.startsWith("review-notified-")) {
|
|
4464
|
-
unlinkSync4(
|
|
5185
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4465
5186
|
}
|
|
4466
5187
|
}
|
|
4467
5188
|
}
|
|
@@ -4482,7 +5203,7 @@ var init_tasks_review = __esm({
|
|
|
4482
5203
|
});
|
|
4483
5204
|
|
|
4484
5205
|
// src/lib/tasks-chain.ts
|
|
4485
|
-
import
|
|
5206
|
+
import path15 from "path";
|
|
4486
5207
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
4487
5208
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4488
5209
|
const client = getClient();
|
|
@@ -4499,7 +5220,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4499
5220
|
});
|
|
4500
5221
|
for (const ur of unblockedRows.rows) {
|
|
4501
5222
|
try {
|
|
4502
|
-
const ubFile =
|
|
5223
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4503
5224
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
4504
5225
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4505
5226
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4568,7 +5289,7 @@ var init_tasks_chain = __esm({
|
|
|
4568
5289
|
|
|
4569
5290
|
// src/lib/project-name.ts
|
|
4570
5291
|
import { execSync as execSync6 } from "child_process";
|
|
4571
|
-
import
|
|
5292
|
+
import path16 from "path";
|
|
4572
5293
|
function getProjectName(cwd) {
|
|
4573
5294
|
const dir = cwd ?? process.cwd();
|
|
4574
5295
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4581,7 +5302,7 @@ function getProjectName(cwd) {
|
|
|
4581
5302
|
timeout: 2e3,
|
|
4582
5303
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4583
5304
|
}).trim();
|
|
4584
|
-
repoRoot =
|
|
5305
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4585
5306
|
} catch {
|
|
4586
5307
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4587
5308
|
cwd: dir,
|
|
@@ -4590,11 +5311,11 @@ function getProjectName(cwd) {
|
|
|
4590
5311
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4591
5312
|
}).trim();
|
|
4592
5313
|
}
|
|
4593
|
-
_cached2 =
|
|
5314
|
+
_cached2 = path16.basename(repoRoot);
|
|
4594
5315
|
_cachedCwd = dir;
|
|
4595
5316
|
return _cached2;
|
|
4596
5317
|
} catch {
|
|
4597
|
-
_cached2 =
|
|
5318
|
+
_cached2 = path16.basename(dir);
|
|
4598
5319
|
_cachedCwd = dir;
|
|
4599
5320
|
return _cached2;
|
|
4600
5321
|
}
|
|
@@ -5067,7 +5788,7 @@ __export(tasks_exports, {
|
|
|
5067
5788
|
updateTaskStatus: () => updateTaskStatus,
|
|
5068
5789
|
writeCheckpoint: () => writeCheckpoint
|
|
5069
5790
|
});
|
|
5070
|
-
import
|
|
5791
|
+
import path17 from "path";
|
|
5071
5792
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
5072
5793
|
async function createTask(input) {
|
|
5073
5794
|
const result = await createTaskCore(input);
|
|
@@ -5087,8 +5808,8 @@ async function updateTask(input) {
|
|
|
5087
5808
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5088
5809
|
try {
|
|
5089
5810
|
const agent = String(row.assigned_to);
|
|
5090
|
-
const cacheDir =
|
|
5091
|
-
const cachePath =
|
|
5811
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
5812
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
5092
5813
|
if (input.status === "in_progress") {
|
|
5093
5814
|
mkdirSync7(cacheDir, { recursive: true });
|
|
5094
5815
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
@@ -5254,16 +5975,16 @@ init_database();
|
|
|
5254
5975
|
|
|
5255
5976
|
// src/lib/keychain.ts
|
|
5256
5977
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
5257
|
-
import { existsSync as
|
|
5258
|
-
import
|
|
5259
|
-
import
|
|
5978
|
+
import { existsSync as existsSync4 } from "fs";
|
|
5979
|
+
import path5 from "path";
|
|
5980
|
+
import os4 from "os";
|
|
5260
5981
|
var SERVICE = "exe-mem";
|
|
5261
5982
|
var ACCOUNT = "master-key";
|
|
5262
5983
|
function getKeyDir() {
|
|
5263
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5984
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
|
|
5264
5985
|
}
|
|
5265
5986
|
function getKeyPath() {
|
|
5266
|
-
return
|
|
5987
|
+
return path5.join(getKeyDir(), "master.key");
|
|
5267
5988
|
}
|
|
5268
5989
|
async function tryKeytar() {
|
|
5269
5990
|
try {
|
|
@@ -5284,9 +6005,9 @@ async function getMasterKey() {
|
|
|
5284
6005
|
}
|
|
5285
6006
|
}
|
|
5286
6007
|
const keyPath = getKeyPath();
|
|
5287
|
-
if (!
|
|
6008
|
+
if (!existsSync4(keyPath)) {
|
|
5288
6009
|
process.stderr.write(
|
|
5289
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
6010
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5290
6011
|
`
|
|
5291
6012
|
);
|
|
5292
6013
|
return null;
|