@askexenow/exe-os 0.9.8 → 0.9.10
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 +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1411 -953
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +913 -543
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +418 -262
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +793 -485
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +566 -357
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +530 -319
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +547 -336
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +649 -417
- package/dist/hooks/bug-report-worker.js +486 -316
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +528 -317
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3442 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +534 -323
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +614 -382
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +569 -347
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +664 -431
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +1049 -680
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +422 -357
- package/dist/lib/tmux-routing.js +314 -248
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1408 -672
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +448 -371
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1983 -315
- package/dist/runtime/index.js +567 -355
- package/dist/tui/App.js +887 -531
- package/package.json +4 -4
package/dist/tui/App.js
CHANGED
|
@@ -355,6 +355,44 @@ var init_provider_table = __esm({
|
|
|
355
355
|
}
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
+
// src/lib/secure-files.ts
|
|
359
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
360
|
+
import { chmod, mkdir } from "fs/promises";
|
|
361
|
+
async function ensurePrivateDir(dirPath) {
|
|
362
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
363
|
+
try {
|
|
364
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function ensurePrivateDirSync(dirPath) {
|
|
369
|
+
mkdirSync2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
370
|
+
try {
|
|
371
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async function enforcePrivateFile(filePath) {
|
|
376
|
+
try {
|
|
377
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function enforcePrivateFileSync(filePath) {
|
|
382
|
+
try {
|
|
383
|
+
if (existsSync3(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
384
|
+
} catch {
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
388
|
+
var init_secure_files = __esm({
|
|
389
|
+
"src/lib/secure-files.ts"() {
|
|
390
|
+
"use strict";
|
|
391
|
+
PRIVATE_DIR_MODE = 448;
|
|
392
|
+
PRIVATE_FILE_MODE = 384;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
358
396
|
// src/lib/config.ts
|
|
359
397
|
var config_exports = {};
|
|
360
398
|
__export(config_exports, {
|
|
@@ -371,8 +409,8 @@ __export(config_exports, {
|
|
|
371
409
|
migrateConfig: () => migrateConfig,
|
|
372
410
|
saveConfig: () => saveConfig
|
|
373
411
|
});
|
|
374
|
-
import { readFile, writeFile
|
|
375
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
412
|
+
import { readFile, writeFile } from "fs/promises";
|
|
413
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, renameSync } from "fs";
|
|
376
414
|
import path2 from "path";
|
|
377
415
|
import os2 from "os";
|
|
378
416
|
function resolveDataDir() {
|
|
@@ -380,7 +418,7 @@ function resolveDataDir() {
|
|
|
380
418
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
381
419
|
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
382
420
|
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
383
|
-
if (!
|
|
421
|
+
if (!existsSync4(newDir) && existsSync4(legacyDir)) {
|
|
384
422
|
try {
|
|
385
423
|
renameSync(legacyDir, newDir);
|
|
386
424
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -443,9 +481,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
443
481
|
}
|
|
444
482
|
async function loadConfig() {
|
|
445
483
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
446
|
-
await
|
|
484
|
+
await ensurePrivateDir(dir);
|
|
447
485
|
const configPath = path2.join(dir, "config.json");
|
|
448
|
-
if (!
|
|
486
|
+
if (!existsSync4(configPath)) {
|
|
449
487
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
450
488
|
}
|
|
451
489
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -458,6 +496,7 @@ async function loadConfig() {
|
|
|
458
496
|
`);
|
|
459
497
|
try {
|
|
460
498
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
499
|
+
await enforcePrivateFile(configPath);
|
|
461
500
|
} catch {
|
|
462
501
|
}
|
|
463
502
|
}
|
|
@@ -476,7 +515,7 @@ async function loadConfig() {
|
|
|
476
515
|
function loadConfigSync() {
|
|
477
516
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
478
517
|
const configPath = path2.join(dir, "config.json");
|
|
479
|
-
if (!
|
|
518
|
+
if (!existsSync4(configPath)) {
|
|
480
519
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
481
520
|
}
|
|
482
521
|
try {
|
|
@@ -494,12 +533,10 @@ function loadConfigSync() {
|
|
|
494
533
|
}
|
|
495
534
|
async function saveConfig(config) {
|
|
496
535
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
497
|
-
await
|
|
536
|
+
await ensurePrivateDir(dir);
|
|
498
537
|
const configPath = path2.join(dir, "config.json");
|
|
499
538
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
500
|
-
|
|
501
|
-
await chmod(configPath, 384);
|
|
502
|
-
}
|
|
539
|
+
await enforcePrivateFile(configPath);
|
|
503
540
|
}
|
|
504
541
|
async function loadConfigFrom(configPath) {
|
|
505
542
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -519,6 +556,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
519
556
|
var init_config = __esm({
|
|
520
557
|
"src/lib/config.ts"() {
|
|
521
558
|
"use strict";
|
|
559
|
+
init_secure_files();
|
|
522
560
|
EXE_AI_DIR = resolveDataDir();
|
|
523
561
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
524
562
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -635,10 +673,10 @@ __export(agent_config_exports, {
|
|
|
635
673
|
saveAgentConfig: () => saveAgentConfig,
|
|
636
674
|
setAgentRuntime: () => setAgentRuntime
|
|
637
675
|
});
|
|
638
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as
|
|
676
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
|
|
639
677
|
import path3 from "path";
|
|
640
678
|
function loadAgentConfig() {
|
|
641
|
-
if (!
|
|
679
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
642
680
|
try {
|
|
643
681
|
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
644
682
|
} catch {
|
|
@@ -647,8 +685,9 @@ function loadAgentConfig() {
|
|
|
647
685
|
}
|
|
648
686
|
function saveAgentConfig(config) {
|
|
649
687
|
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
650
|
-
|
|
688
|
+
ensurePrivateDirSync(dir);
|
|
651
689
|
writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
690
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
652
691
|
}
|
|
653
692
|
function getAgentRuntime(agentId) {
|
|
654
693
|
const config = loadAgentConfig();
|
|
@@ -688,6 +727,7 @@ var init_agent_config = __esm({
|
|
|
688
727
|
"use strict";
|
|
689
728
|
init_config();
|
|
690
729
|
init_runtime_table();
|
|
730
|
+
init_secure_files();
|
|
691
731
|
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
692
732
|
KNOWN_RUNTIMES = {
|
|
693
733
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -716,16 +756,16 @@ __export(intercom_queue_exports, {
|
|
|
716
756
|
queueIntercom: () => queueIntercom,
|
|
717
757
|
readQueue: () => readQueue
|
|
718
758
|
});
|
|
719
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as
|
|
759
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
720
760
|
import path4 from "path";
|
|
721
761
|
import os3 from "os";
|
|
722
762
|
function ensureDir() {
|
|
723
763
|
const dir = path4.dirname(QUEUE_PATH);
|
|
724
|
-
if (!
|
|
764
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
725
765
|
}
|
|
726
766
|
function readQueue() {
|
|
727
767
|
try {
|
|
728
|
-
if (!
|
|
768
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
729
769
|
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
730
770
|
} catch {
|
|
731
771
|
return [];
|
|
@@ -916,7 +956,7 @@ __export(employees_exports, {
|
|
|
916
956
|
validateEmployeeName: () => validateEmployeeName
|
|
917
957
|
});
|
|
918
958
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
919
|
-
import { existsSync as
|
|
959
|
+
import { existsSync as existsSync7, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
920
960
|
import { execSync as execSync4 } from "child_process";
|
|
921
961
|
import path5 from "path";
|
|
922
962
|
import os4 from "os";
|
|
@@ -955,7 +995,7 @@ function validateEmployeeName(name) {
|
|
|
955
995
|
return { valid: true };
|
|
956
996
|
}
|
|
957
997
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
958
|
-
if (!
|
|
998
|
+
if (!existsSync7(employeesPath)) {
|
|
959
999
|
return [];
|
|
960
1000
|
}
|
|
961
1001
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -970,7 +1010,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
970
1010
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
971
1011
|
}
|
|
972
1012
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
973
|
-
if (!
|
|
1013
|
+
if (!existsSync7(employeesPath)) return [];
|
|
974
1014
|
try {
|
|
975
1015
|
return JSON.parse(readFileSync6(employeesPath, "utf-8"));
|
|
976
1016
|
} catch {
|
|
@@ -1018,7 +1058,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
1018
1058
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1019
1059
|
if (!coordinator) return;
|
|
1020
1060
|
const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1021
|
-
if (!
|
|
1061
|
+
if (!existsSync7(idPath)) return;
|
|
1022
1062
|
const content = readFileSync6(idPath, "utf-8");
|
|
1023
1063
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1024
1064
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -1072,9 +1112,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
1072
1112
|
const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
|
|
1073
1113
|
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
1074
1114
|
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
1075
|
-
if (
|
|
1115
|
+
if (existsSync7(oldPath) && !existsSync7(newPath)) {
|
|
1076
1116
|
renameSync3(oldPath, newPath);
|
|
1077
|
-
} else if (
|
|
1117
|
+
} else if (existsSync7(oldPath) && oldPath !== newPath) {
|
|
1078
1118
|
const content = readFileSync6(oldPath, "utf-8");
|
|
1079
1119
|
writeFileSync4(newPath, content, "utf-8");
|
|
1080
1120
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -1117,7 +1157,7 @@ function registerBinSymlinks(name) {
|
|
|
1117
1157
|
for (const suffix of ["", "-opencode"]) {
|
|
1118
1158
|
const linkName = `${name}${suffix}`;
|
|
1119
1159
|
const linkPath = path5.join(binDir, linkName);
|
|
1120
|
-
if (
|
|
1160
|
+
if (existsSync7(linkPath)) {
|
|
1121
1161
|
skipped.push(linkName);
|
|
1122
1162
|
continue;
|
|
1123
1163
|
}
|
|
@@ -1728,13 +1768,50 @@ var init_database_adapter = __esm({
|
|
|
1728
1768
|
}
|
|
1729
1769
|
});
|
|
1730
1770
|
|
|
1771
|
+
// src/lib/daemon-auth.ts
|
|
1772
|
+
import crypto from "crypto";
|
|
1773
|
+
import path7 from "path";
|
|
1774
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
1775
|
+
function normalizeToken(token) {
|
|
1776
|
+
if (!token) return null;
|
|
1777
|
+
const trimmed = token.trim();
|
|
1778
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1779
|
+
}
|
|
1780
|
+
function readDaemonToken() {
|
|
1781
|
+
try {
|
|
1782
|
+
if (!existsSync8(DAEMON_TOKEN_PATH)) return null;
|
|
1783
|
+
return normalizeToken(readFileSync7(DAEMON_TOKEN_PATH, "utf8"));
|
|
1784
|
+
} catch {
|
|
1785
|
+
return null;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
function ensureDaemonToken(seed) {
|
|
1789
|
+
const existing = readDaemonToken();
|
|
1790
|
+
if (existing) return existing;
|
|
1791
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1792
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1793
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
1794
|
+
`, "utf8");
|
|
1795
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1796
|
+
return token;
|
|
1797
|
+
}
|
|
1798
|
+
var DAEMON_TOKEN_PATH;
|
|
1799
|
+
var init_daemon_auth = __esm({
|
|
1800
|
+
"src/lib/daemon-auth.ts"() {
|
|
1801
|
+
"use strict";
|
|
1802
|
+
init_config();
|
|
1803
|
+
init_secure_files();
|
|
1804
|
+
DAEMON_TOKEN_PATH = path7.join(EXE_AI_DIR, "exed.token");
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
|
|
1731
1808
|
// src/lib/exe-daemon-client.ts
|
|
1732
1809
|
import net from "net";
|
|
1733
1810
|
import os6 from "os";
|
|
1734
1811
|
import { spawn } from "child_process";
|
|
1735
1812
|
import { randomUUID } from "crypto";
|
|
1736
|
-
import { existsSync as
|
|
1737
|
-
import
|
|
1813
|
+
import { existsSync as existsSync9, unlinkSync as unlinkSync2, readFileSync as readFileSync8, openSync, closeSync, statSync } from "fs";
|
|
1814
|
+
import path8 from "path";
|
|
1738
1815
|
import { fileURLToPath } from "url";
|
|
1739
1816
|
function handleData(chunk) {
|
|
1740
1817
|
_buffer += chunk.toString();
|
|
@@ -1762,9 +1839,9 @@ function handleData(chunk) {
|
|
|
1762
1839
|
}
|
|
1763
1840
|
}
|
|
1764
1841
|
function cleanupStaleFiles() {
|
|
1765
|
-
if (
|
|
1842
|
+
if (existsSync9(PID_PATH)) {
|
|
1766
1843
|
try {
|
|
1767
|
-
const pid = parseInt(
|
|
1844
|
+
const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
|
|
1768
1845
|
if (pid > 0) {
|
|
1769
1846
|
try {
|
|
1770
1847
|
process.kill(pid, 0);
|
|
@@ -1785,11 +1862,11 @@ function cleanupStaleFiles() {
|
|
|
1785
1862
|
}
|
|
1786
1863
|
}
|
|
1787
1864
|
function findPackageRoot() {
|
|
1788
|
-
let dir =
|
|
1789
|
-
const { root } =
|
|
1865
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
1866
|
+
const { root } = path8.parse(dir);
|
|
1790
1867
|
while (dir !== root) {
|
|
1791
|
-
if (
|
|
1792
|
-
dir =
|
|
1868
|
+
if (existsSync9(path8.join(dir, "package.json"))) return dir;
|
|
1869
|
+
dir = path8.dirname(dir);
|
|
1793
1870
|
}
|
|
1794
1871
|
return null;
|
|
1795
1872
|
}
|
|
@@ -1815,16 +1892,17 @@ function spawnDaemon() {
|
|
|
1815
1892
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1816
1893
|
return;
|
|
1817
1894
|
}
|
|
1818
|
-
const daemonPath =
|
|
1819
|
-
if (!
|
|
1895
|
+
const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1896
|
+
if (!existsSync9(daemonPath)) {
|
|
1820
1897
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1821
1898
|
`);
|
|
1822
1899
|
return;
|
|
1823
1900
|
}
|
|
1824
1901
|
const resolvedPath = daemonPath;
|
|
1902
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1825
1903
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1826
1904
|
`);
|
|
1827
|
-
const logPath =
|
|
1905
|
+
const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
|
|
1828
1906
|
let stderrFd = "ignore";
|
|
1829
1907
|
try {
|
|
1830
1908
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1842,7 +1920,8 @@ function spawnDaemon() {
|
|
|
1842
1920
|
TMUX_PANE: void 0,
|
|
1843
1921
|
// Prevents resolveExeSession() from scoping to one session
|
|
1844
1922
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1845
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1923
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1924
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1846
1925
|
}
|
|
1847
1926
|
});
|
|
1848
1927
|
child.unref();
|
|
@@ -1949,13 +2028,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1949
2028
|
return;
|
|
1950
2029
|
}
|
|
1951
2030
|
const id = randomUUID();
|
|
2031
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1952
2032
|
const timer = setTimeout(() => {
|
|
1953
2033
|
_pending.delete(id);
|
|
1954
2034
|
resolve({ error: "Request timeout" });
|
|
1955
2035
|
}, timeoutMs);
|
|
1956
2036
|
_pending.set(id, { resolve, timer });
|
|
1957
2037
|
try {
|
|
1958
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2038
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1959
2039
|
} catch {
|
|
1960
2040
|
clearTimeout(timer);
|
|
1961
2041
|
_pending.delete(id);
|
|
@@ -1966,17 +2046,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1966
2046
|
function isClientConnected() {
|
|
1967
2047
|
return _connected;
|
|
1968
2048
|
}
|
|
1969
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
2049
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1970
2050
|
var init_exe_daemon_client = __esm({
|
|
1971
2051
|
"src/lib/exe-daemon-client.ts"() {
|
|
1972
2052
|
"use strict";
|
|
1973
2053
|
init_config();
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2054
|
+
init_daemon_auth();
|
|
2055
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
|
|
2056
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
|
|
2057
|
+
SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1977
2058
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1978
2059
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1979
2060
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2061
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1980
2062
|
_socket = null;
|
|
1981
2063
|
_connected = false;
|
|
1982
2064
|
_buffer = "";
|
|
@@ -2555,6 +2637,7 @@ async function ensureSchema() {
|
|
|
2555
2637
|
project TEXT NOT NULL,
|
|
2556
2638
|
summary TEXT NOT NULL,
|
|
2557
2639
|
task_file TEXT,
|
|
2640
|
+
session_scope TEXT,
|
|
2558
2641
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2559
2642
|
created_at TEXT NOT NULL
|
|
2560
2643
|
);
|
|
@@ -2563,7 +2646,7 @@ async function ensureSchema() {
|
|
|
2563
2646
|
ON notifications(read);
|
|
2564
2647
|
|
|
2565
2648
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2566
|
-
ON notifications(agent_id);
|
|
2649
|
+
ON notifications(agent_id, session_scope);
|
|
2567
2650
|
|
|
2568
2651
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2569
2652
|
ON notifications(task_file);
|
|
@@ -2601,6 +2684,7 @@ async function ensureSchema() {
|
|
|
2601
2684
|
target_agent TEXT NOT NULL,
|
|
2602
2685
|
target_project TEXT,
|
|
2603
2686
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2687
|
+
session_scope TEXT,
|
|
2604
2688
|
content TEXT NOT NULL,
|
|
2605
2689
|
priority TEXT DEFAULT 'normal',
|
|
2606
2690
|
status TEXT DEFAULT 'pending',
|
|
@@ -2614,10 +2698,31 @@ async function ensureSchema() {
|
|
|
2614
2698
|
);
|
|
2615
2699
|
|
|
2616
2700
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2617
|
-
ON messages(target_agent, status);
|
|
2701
|
+
ON messages(target_agent, session_scope, status);
|
|
2618
2702
|
|
|
2619
2703
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2620
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2704
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2705
|
+
`);
|
|
2706
|
+
try {
|
|
2707
|
+
await client.execute({
|
|
2708
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2709
|
+
args: []
|
|
2710
|
+
});
|
|
2711
|
+
} catch {
|
|
2712
|
+
}
|
|
2713
|
+
try {
|
|
2714
|
+
await client.execute({
|
|
2715
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2716
|
+
args: []
|
|
2717
|
+
});
|
|
2718
|
+
} catch {
|
|
2719
|
+
}
|
|
2720
|
+
await client.executeMultiple(`
|
|
2721
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2722
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2723
|
+
|
|
2724
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2725
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2621
2726
|
`);
|
|
2622
2727
|
try {
|
|
2623
2728
|
await client.execute({
|
|
@@ -3201,6 +3306,13 @@ async function ensureSchema() {
|
|
|
3201
3306
|
} catch {
|
|
3202
3307
|
}
|
|
3203
3308
|
}
|
|
3309
|
+
try {
|
|
3310
|
+
await client.execute({
|
|
3311
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
3312
|
+
args: []
|
|
3313
|
+
});
|
|
3314
|
+
} catch {
|
|
3315
|
+
}
|
|
3204
3316
|
}
|
|
3205
3317
|
async function disposeDatabase() {
|
|
3206
3318
|
if (_walCheckpointTimer) {
|
|
@@ -3255,9 +3367,12 @@ __export(license_exports, {
|
|
|
3255
3367
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
3256
3368
|
validateLicense: () => validateLicense
|
|
3257
3369
|
});
|
|
3258
|
-
import { readFileSync as
|
|
3370
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
3259
3371
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3260
|
-
import
|
|
3372
|
+
import { createRequire as createRequire2 } from "module";
|
|
3373
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3374
|
+
import os7 from "os";
|
|
3375
|
+
import path9 from "path";
|
|
3261
3376
|
import { jwtVerify, importSPKI } from "jose";
|
|
3262
3377
|
async function fetchRetry(url, init) {
|
|
3263
3378
|
try {
|
|
@@ -3268,37 +3383,37 @@ async function fetchRetry(url, init) {
|
|
|
3268
3383
|
}
|
|
3269
3384
|
}
|
|
3270
3385
|
function loadDeviceId() {
|
|
3271
|
-
const deviceJsonPath =
|
|
3386
|
+
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
3272
3387
|
try {
|
|
3273
|
-
if (
|
|
3274
|
-
const data = JSON.parse(
|
|
3388
|
+
if (existsSync10(deviceJsonPath)) {
|
|
3389
|
+
const data = JSON.parse(readFileSync9(deviceJsonPath, "utf8"));
|
|
3275
3390
|
if (data.deviceId) return data.deviceId;
|
|
3276
3391
|
}
|
|
3277
3392
|
} catch {
|
|
3278
3393
|
}
|
|
3279
3394
|
try {
|
|
3280
|
-
if (
|
|
3281
|
-
const id2 =
|
|
3395
|
+
if (existsSync10(DEVICE_ID_PATH)) {
|
|
3396
|
+
const id2 = readFileSync9(DEVICE_ID_PATH, "utf8").trim();
|
|
3282
3397
|
if (id2) return id2;
|
|
3283
3398
|
}
|
|
3284
3399
|
} catch {
|
|
3285
3400
|
}
|
|
3286
3401
|
const id = randomUUID2();
|
|
3287
3402
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
3288
|
-
|
|
3403
|
+
writeFileSync6(DEVICE_ID_PATH, id, "utf8");
|
|
3289
3404
|
return id;
|
|
3290
3405
|
}
|
|
3291
3406
|
function loadLicense() {
|
|
3292
3407
|
try {
|
|
3293
|
-
if (!
|
|
3294
|
-
return
|
|
3408
|
+
if (!existsSync10(LICENSE_PATH)) return null;
|
|
3409
|
+
return readFileSync9(LICENSE_PATH, "utf8").trim();
|
|
3295
3410
|
} catch {
|
|
3296
3411
|
return null;
|
|
3297
3412
|
}
|
|
3298
3413
|
}
|
|
3299
3414
|
function saveLicense(apiKey) {
|
|
3300
3415
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
3301
|
-
|
|
3416
|
+
writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3302
3417
|
}
|
|
3303
3418
|
async function verifyLicenseJwt(token) {
|
|
3304
3419
|
try {
|
|
@@ -3324,8 +3439,8 @@ async function verifyLicenseJwt(token) {
|
|
|
3324
3439
|
}
|
|
3325
3440
|
async function getCachedLicense() {
|
|
3326
3441
|
try {
|
|
3327
|
-
if (!
|
|
3328
|
-
const raw = JSON.parse(
|
|
3442
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3443
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH, "utf8"));
|
|
3329
3444
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3330
3445
|
return await verifyLicenseJwt(raw.token);
|
|
3331
3446
|
} catch {
|
|
@@ -3334,8 +3449,8 @@ async function getCachedLicense() {
|
|
|
3334
3449
|
}
|
|
3335
3450
|
function readCachedToken() {
|
|
3336
3451
|
try {
|
|
3337
|
-
if (!
|
|
3338
|
-
const raw = JSON.parse(
|
|
3452
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3453
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH, "utf8"));
|
|
3339
3454
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3340
3455
|
} catch {
|
|
3341
3456
|
return null;
|
|
@@ -3369,56 +3484,130 @@ function getRawCachedPlan() {
|
|
|
3369
3484
|
}
|
|
3370
3485
|
function cacheResponse(token) {
|
|
3371
3486
|
try {
|
|
3372
|
-
|
|
3487
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
3373
3488
|
} catch {
|
|
3374
3489
|
}
|
|
3375
3490
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3491
|
+
function loadPrismaForLicense() {
|
|
3492
|
+
if (_prismaFailed) return null;
|
|
3493
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
3494
|
+
if (!dbUrl) {
|
|
3495
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os7.homedir(), "exe-db");
|
|
3496
|
+
if (!existsSync10(path9.join(exeDbRoot, "package.json"))) {
|
|
3497
|
+
_prismaFailed = true;
|
|
3498
|
+
return null;
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
if (!_prismaPromise) {
|
|
3502
|
+
_prismaPromise = (async () => {
|
|
3503
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
3504
|
+
if (explicitPath) {
|
|
3505
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
3506
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
3507
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
3508
|
+
return new Ctor2();
|
|
3509
|
+
}
|
|
3510
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os7.homedir(), "exe-db");
|
|
3511
|
+
const req = createRequire2(path9.join(exeDbRoot, "package.json"));
|
|
3512
|
+
const entry = req.resolve("@prisma/client");
|
|
3513
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
3514
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3515
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
3516
|
+
return new Ctor();
|
|
3517
|
+
})().catch((err) => {
|
|
3518
|
+
_prismaFailed = true;
|
|
3519
|
+
_prismaPromise = null;
|
|
3520
|
+
throw err;
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
return _prismaPromise;
|
|
3524
|
+
}
|
|
3525
|
+
async function validateViaPostgres(apiKey) {
|
|
3526
|
+
const loader = loadPrismaForLicense();
|
|
3527
|
+
if (!loader) return null;
|
|
3528
|
+
try {
|
|
3529
|
+
const prisma = await loader;
|
|
3530
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
3531
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
3532
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
3533
|
+
apiKey
|
|
3534
|
+
);
|
|
3535
|
+
if (!rows || rows.length === 0) return null;
|
|
3536
|
+
const row = rows[0];
|
|
3537
|
+
if (row.status !== "active") return null;
|
|
3538
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
3539
|
+
const plan = row.plan;
|
|
3540
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
3541
|
+
return {
|
|
3542
|
+
valid: true,
|
|
3543
|
+
plan,
|
|
3544
|
+
email: row.email,
|
|
3545
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
3546
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
3547
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
3548
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
3549
|
+
};
|
|
3550
|
+
} catch {
|
|
3551
|
+
return null;
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
3378
3555
|
try {
|
|
3379
3556
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3380
3557
|
method: "POST",
|
|
3381
3558
|
headers: { "Content-Type": "application/json" },
|
|
3382
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
3559
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
3383
3560
|
signal: AbortSignal.timeout(1e4)
|
|
3384
3561
|
});
|
|
3385
|
-
if (res.ok)
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3562
|
+
if (!res.ok) return null;
|
|
3563
|
+
const data = await res.json();
|
|
3564
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
3565
|
+
if (!data.valid) return null;
|
|
3566
|
+
if (data.token) {
|
|
3567
|
+
cacheResponse(data.token);
|
|
3568
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
3569
|
+
if (verified) return verified;
|
|
3570
|
+
}
|
|
3571
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3572
|
+
return {
|
|
3573
|
+
valid: data.valid,
|
|
3574
|
+
plan: data.plan,
|
|
3575
|
+
email: data.email,
|
|
3576
|
+
expiresAt: data.expiresAt,
|
|
3577
|
+
deviceLimit: limits.devices,
|
|
3578
|
+
employeeLimit: limits.employees,
|
|
3579
|
+
memoryLimit: limits.memories
|
|
3580
|
+
};
|
|
3581
|
+
} catch {
|
|
3582
|
+
return null;
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
async function validateLicense(apiKey, deviceId) {
|
|
3586
|
+
const did = deviceId ?? loadDeviceId();
|
|
3587
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
3588
|
+
if (pgResult) {
|
|
3589
|
+
try {
|
|
3590
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
3591
|
+
} catch {
|
|
3592
|
+
}
|
|
3593
|
+
return pgResult;
|
|
3594
|
+
}
|
|
3595
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
3596
|
+
if (cfResult) return cfResult;
|
|
3597
|
+
const cached = await getCachedLicense();
|
|
3598
|
+
if (cached) return cached;
|
|
3599
|
+
try {
|
|
3600
|
+
if (existsSync10(CACHE_PATH)) {
|
|
3601
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH, "utf8"));
|
|
3602
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
3603
|
+
return raw.pgLicense;
|
|
3604
|
+
}
|
|
3409
3605
|
}
|
|
3410
|
-
const cached = await getCachedLicense();
|
|
3411
|
-
if (cached) return cached;
|
|
3412
|
-
const raw = getRawCachedPlan();
|
|
3413
|
-
if (raw) return raw;
|
|
3414
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
3415
3606
|
} catch {
|
|
3416
|
-
const cached = await getCachedLicense();
|
|
3417
|
-
if (cached) return cached;
|
|
3418
|
-
const rawFallback = getRawCachedPlan();
|
|
3419
|
-
if (rawFallback) return rawFallback;
|
|
3420
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
3421
3607
|
}
|
|
3608
|
+
const rawFallback = getRawCachedPlan();
|
|
3609
|
+
if (rawFallback) return rawFallback;
|
|
3610
|
+
return { ...FREE_LICENSE, valid: false };
|
|
3422
3611
|
}
|
|
3423
3612
|
function getCacheAgeMs() {
|
|
3424
3613
|
try {
|
|
@@ -3433,9 +3622,9 @@ async function checkLicense() {
|
|
|
3433
3622
|
let key = loadLicense();
|
|
3434
3623
|
if (!key) {
|
|
3435
3624
|
try {
|
|
3436
|
-
const configPath =
|
|
3437
|
-
if (
|
|
3438
|
-
const raw = JSON.parse(
|
|
3625
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
3626
|
+
if (existsSync10(configPath)) {
|
|
3627
|
+
const raw = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
3439
3628
|
const cloud = raw.cloud;
|
|
3440
3629
|
if (cloud?.apiKey) {
|
|
3441
3630
|
key = cloud.apiKey;
|
|
@@ -3589,14 +3778,14 @@ function stopLicenseRevalidation() {
|
|
|
3589
3778
|
_revalTimer = null;
|
|
3590
3779
|
}
|
|
3591
3780
|
}
|
|
3592
|
-
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
3781
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
|
|
3593
3782
|
var init_license = __esm({
|
|
3594
3783
|
"src/lib/license.ts"() {
|
|
3595
3784
|
"use strict";
|
|
3596
3785
|
init_config();
|
|
3597
|
-
LICENSE_PATH =
|
|
3598
|
-
CACHE_PATH =
|
|
3599
|
-
DEVICE_ID_PATH =
|
|
3786
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3787
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3788
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
3600
3789
|
API_BASE = "https://askexe.com/cloud";
|
|
3601
3790
|
RETRY_DELAY_MS = 500;
|
|
3602
3791
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -3620,18 +3809,20 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
3620
3809
|
employeeLimit: 1,
|
|
3621
3810
|
memoryLimit: 5e3
|
|
3622
3811
|
};
|
|
3812
|
+
_prismaPromise = null;
|
|
3813
|
+
_prismaFailed = false;
|
|
3623
3814
|
CACHE_MAX_AGE_MS = 36e5;
|
|
3624
3815
|
_revalTimer = null;
|
|
3625
3816
|
}
|
|
3626
3817
|
});
|
|
3627
3818
|
|
|
3628
3819
|
// src/lib/plan-limits.ts
|
|
3629
|
-
import { readFileSync as
|
|
3630
|
-
import
|
|
3820
|
+
import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
3821
|
+
import path10 from "path";
|
|
3631
3822
|
function getLicenseSync() {
|
|
3632
3823
|
try {
|
|
3633
|
-
if (!
|
|
3634
|
-
const raw = JSON.parse(
|
|
3824
|
+
if (!existsSync11(CACHE_PATH2)) return freeLicense();
|
|
3825
|
+
const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
|
|
3635
3826
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3636
3827
|
const parts = raw.token.split(".");
|
|
3637
3828
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3669,8 +3860,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3669
3860
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
3670
3861
|
let count = 0;
|
|
3671
3862
|
try {
|
|
3672
|
-
if (
|
|
3673
|
-
const raw =
|
|
3863
|
+
if (existsSync11(filePath)) {
|
|
3864
|
+
const raw = readFileSync10(filePath, "utf8");
|
|
3674
3865
|
const employees = JSON.parse(raw);
|
|
3675
3866
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3676
3867
|
}
|
|
@@ -3699,29 +3890,30 @@ var init_plan_limits = __esm({
|
|
|
3699
3890
|
this.name = "PlanLimitError";
|
|
3700
3891
|
}
|
|
3701
3892
|
};
|
|
3702
|
-
CACHE_PATH2 =
|
|
3893
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3703
3894
|
}
|
|
3704
3895
|
});
|
|
3705
3896
|
|
|
3706
3897
|
// src/lib/notifications.ts
|
|
3707
|
-
import
|
|
3708
|
-
import
|
|
3709
|
-
import
|
|
3898
|
+
import crypto2 from "crypto";
|
|
3899
|
+
import path11 from "path";
|
|
3900
|
+
import os8 from "os";
|
|
3710
3901
|
import {
|
|
3711
|
-
readFileSync as
|
|
3902
|
+
readFileSync as readFileSync11,
|
|
3712
3903
|
readdirSync,
|
|
3713
3904
|
unlinkSync as unlinkSync3,
|
|
3714
|
-
existsSync as
|
|
3905
|
+
existsSync as existsSync12,
|
|
3715
3906
|
rmdirSync
|
|
3716
3907
|
} from "fs";
|
|
3717
3908
|
async function writeNotification(notification) {
|
|
3718
3909
|
try {
|
|
3719
3910
|
const client = getClient();
|
|
3720
|
-
const id =
|
|
3911
|
+
const id = crypto2.randomUUID();
|
|
3721
3912
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3913
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
3722
3914
|
await client.execute({
|
|
3723
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3724
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3915
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
3916
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3725
3917
|
args: [
|
|
3726
3918
|
id,
|
|
3727
3919
|
notification.agentId,
|
|
@@ -3730,6 +3922,7 @@ async function writeNotification(notification) {
|
|
|
3730
3922
|
notification.project,
|
|
3731
3923
|
notification.summary,
|
|
3732
3924
|
notification.taskFile ?? null,
|
|
3925
|
+
sessionScope,
|
|
3733
3926
|
now
|
|
3734
3927
|
]
|
|
3735
3928
|
});
|
|
@@ -3738,12 +3931,14 @@ async function writeNotification(notification) {
|
|
|
3738
3931
|
`);
|
|
3739
3932
|
}
|
|
3740
3933
|
}
|
|
3741
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
3934
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
3742
3935
|
try {
|
|
3743
3936
|
const client = getClient();
|
|
3937
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3744
3938
|
await client.execute({
|
|
3745
|
-
sql:
|
|
3746
|
-
|
|
3939
|
+
sql: `UPDATE notifications SET read = 1
|
|
3940
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
3941
|
+
args: [taskFile, ...scope.args]
|
|
3747
3942
|
});
|
|
3748
3943
|
} catch {
|
|
3749
3944
|
}
|
|
@@ -3752,11 +3947,12 @@ var init_notifications = __esm({
|
|
|
3752
3947
|
"src/lib/notifications.ts"() {
|
|
3753
3948
|
"use strict";
|
|
3754
3949
|
init_database();
|
|
3950
|
+
init_task_scope();
|
|
3755
3951
|
}
|
|
3756
3952
|
});
|
|
3757
3953
|
|
|
3758
3954
|
// src/lib/session-kill-telemetry.ts
|
|
3759
|
-
import
|
|
3955
|
+
import crypto3 from "crypto";
|
|
3760
3956
|
async function recordSessionKill(input) {
|
|
3761
3957
|
try {
|
|
3762
3958
|
const client = getClient();
|
|
@@ -3766,7 +3962,7 @@ async function recordSessionKill(input) {
|
|
|
3766
3962
|
ticks_idle, estimated_tokens_saved)
|
|
3767
3963
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3768
3964
|
args: [
|
|
3769
|
-
|
|
3965
|
+
crypto3.randomUUID(),
|
|
3770
3966
|
input.sessionName,
|
|
3771
3967
|
input.agentId,
|
|
3772
3968
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3844,6 +4040,110 @@ var init_state_bus = __esm({
|
|
|
3844
4040
|
}
|
|
3845
4041
|
});
|
|
3846
4042
|
|
|
4043
|
+
// src/lib/project-name.ts
|
|
4044
|
+
import { execSync as execSync5 } from "child_process";
|
|
4045
|
+
import path12 from "path";
|
|
4046
|
+
function getProjectName(cwd2) {
|
|
4047
|
+
const dir = cwd2 ?? process.cwd();
|
|
4048
|
+
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
4049
|
+
try {
|
|
4050
|
+
let repoRoot;
|
|
4051
|
+
try {
|
|
4052
|
+
const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
|
|
4053
|
+
cwd: dir,
|
|
4054
|
+
encoding: "utf8",
|
|
4055
|
+
timeout: 2e3,
|
|
4056
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4057
|
+
}).trim();
|
|
4058
|
+
repoRoot = path12.dirname(gitCommonDir);
|
|
4059
|
+
} catch {
|
|
4060
|
+
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
4061
|
+
cwd: dir,
|
|
4062
|
+
encoding: "utf8",
|
|
4063
|
+
timeout: 2e3,
|
|
4064
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4065
|
+
}).trim();
|
|
4066
|
+
}
|
|
4067
|
+
_cached2 = path12.basename(repoRoot);
|
|
4068
|
+
_cachedCwd = dir;
|
|
4069
|
+
return _cached2;
|
|
4070
|
+
} catch {
|
|
4071
|
+
_cached2 = path12.basename(dir);
|
|
4072
|
+
_cachedCwd = dir;
|
|
4073
|
+
return _cached2;
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
var _cached2, _cachedCwd;
|
|
4077
|
+
var init_project_name = __esm({
|
|
4078
|
+
"src/lib/project-name.ts"() {
|
|
4079
|
+
"use strict";
|
|
4080
|
+
_cached2 = null;
|
|
4081
|
+
_cachedCwd = null;
|
|
4082
|
+
}
|
|
4083
|
+
});
|
|
4084
|
+
|
|
4085
|
+
// src/lib/session-scope.ts
|
|
4086
|
+
var session_scope_exports = {};
|
|
4087
|
+
__export(session_scope_exports, {
|
|
4088
|
+
assertSessionScope: () => assertSessionScope,
|
|
4089
|
+
findSessionForProject: () => findSessionForProject,
|
|
4090
|
+
getSessionProject: () => getSessionProject
|
|
4091
|
+
});
|
|
4092
|
+
function getSessionProject(sessionName) {
|
|
4093
|
+
const sessions = listSessions();
|
|
4094
|
+
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
4095
|
+
if (!entry) return null;
|
|
4096
|
+
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
4097
|
+
return parts[parts.length - 1] ?? null;
|
|
4098
|
+
}
|
|
4099
|
+
function findSessionForProject(projectName) {
|
|
4100
|
+
const sessions = listSessions();
|
|
4101
|
+
for (const s of sessions) {
|
|
4102
|
+
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
4103
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
4104
|
+
}
|
|
4105
|
+
return null;
|
|
4106
|
+
}
|
|
4107
|
+
function assertSessionScope(actionType, targetProject) {
|
|
4108
|
+
try {
|
|
4109
|
+
const currentProject = getProjectName();
|
|
4110
|
+
const exeSession = resolveExeSession();
|
|
4111
|
+
if (!exeSession) {
|
|
4112
|
+
return { allowed: true, reason: "no_session" };
|
|
4113
|
+
}
|
|
4114
|
+
if (currentProject === targetProject) {
|
|
4115
|
+
return {
|
|
4116
|
+
allowed: true,
|
|
4117
|
+
reason: "same_session",
|
|
4118
|
+
currentProject,
|
|
4119
|
+
targetProject
|
|
4120
|
+
};
|
|
4121
|
+
}
|
|
4122
|
+
process.stderr.write(
|
|
4123
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
4124
|
+
`
|
|
4125
|
+
);
|
|
4126
|
+
return {
|
|
4127
|
+
allowed: false,
|
|
4128
|
+
reason: "cross_session_denied",
|
|
4129
|
+
currentProject,
|
|
4130
|
+
targetProject,
|
|
4131
|
+
targetSession: findSessionForProject(targetProject)?.windowName
|
|
4132
|
+
};
|
|
4133
|
+
} catch {
|
|
4134
|
+
return { allowed: true, reason: "no_session" };
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
var init_session_scope = __esm({
|
|
4138
|
+
"src/lib/session-scope.ts"() {
|
|
4139
|
+
"use strict";
|
|
4140
|
+
init_session_registry();
|
|
4141
|
+
init_project_name();
|
|
4142
|
+
init_tmux_routing();
|
|
4143
|
+
init_employees();
|
|
4144
|
+
}
|
|
4145
|
+
});
|
|
4146
|
+
|
|
3847
4147
|
// src/lib/tasks-crud.ts
|
|
3848
4148
|
var tasks_crud_exports = {};
|
|
3849
4149
|
__export(tasks_crud_exports, {
|
|
@@ -3861,12 +4161,12 @@ __export(tasks_crud_exports, {
|
|
|
3861
4161
|
updateTaskStatus: () => updateTaskStatus,
|
|
3862
4162
|
writeCheckpoint: () => writeCheckpoint
|
|
3863
4163
|
});
|
|
3864
|
-
import
|
|
3865
|
-
import
|
|
3866
|
-
import
|
|
3867
|
-
import { execSync as
|
|
4164
|
+
import crypto4 from "crypto";
|
|
4165
|
+
import path13 from "path";
|
|
4166
|
+
import os9 from "os";
|
|
4167
|
+
import { execSync as execSync6 } from "child_process";
|
|
3868
4168
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3869
|
-
import { existsSync as
|
|
4169
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
3870
4170
|
async function writeCheckpoint(input) {
|
|
3871
4171
|
const client = getClient();
|
|
3872
4172
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3982,13 +4282,28 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3982
4282
|
}
|
|
3983
4283
|
async function createTaskCore(input) {
|
|
3984
4284
|
const client = getClient();
|
|
3985
|
-
const id =
|
|
4285
|
+
const id = crypto4.randomUUID();
|
|
3986
4286
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3987
4287
|
const slug = slugify(input.title);
|
|
3988
4288
|
let earlySessionScope = null;
|
|
4289
|
+
let scopeMismatchWarning;
|
|
3989
4290
|
try {
|
|
3990
4291
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3991
|
-
|
|
4292
|
+
const resolved = resolveExeSession2();
|
|
4293
|
+
if (resolved && input.projectName) {
|
|
4294
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
4295
|
+
const sessionProject = getSessionProject2(resolved);
|
|
4296
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
4297
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
4298
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
4299
|
+
`);
|
|
4300
|
+
earlySessionScope = null;
|
|
4301
|
+
} else {
|
|
4302
|
+
earlySessionScope = resolved;
|
|
4303
|
+
}
|
|
4304
|
+
} else {
|
|
4305
|
+
earlySessionScope = resolved;
|
|
4306
|
+
}
|
|
3992
4307
|
} catch {
|
|
3993
4308
|
}
|
|
3994
4309
|
const scope = earlySessionScope ?? "default";
|
|
@@ -4039,10 +4354,14 @@ async function createTaskCore(input) {
|
|
|
4039
4354
|
${laneWarning}` : laneWarning;
|
|
4040
4355
|
}
|
|
4041
4356
|
}
|
|
4357
|
+
if (scopeMismatchWarning) {
|
|
4358
|
+
warning = warning ? `${warning}
|
|
4359
|
+
${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
4360
|
+
}
|
|
4042
4361
|
if (input.baseDir) {
|
|
4043
4362
|
try {
|
|
4044
|
-
await mkdir3(
|
|
4045
|
-
await mkdir3(
|
|
4363
|
+
await mkdir3(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4364
|
+
await mkdir3(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4046
4365
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4047
4366
|
await ensureGitignoreExe(input.baseDir);
|
|
4048
4367
|
} catch {
|
|
@@ -4078,13 +4397,19 @@ ${laneWarning}` : laneWarning;
|
|
|
4078
4397
|
});
|
|
4079
4398
|
if (input.baseDir) {
|
|
4080
4399
|
try {
|
|
4081
|
-
const EXE_OS_DIR =
|
|
4082
|
-
const mdPath =
|
|
4083
|
-
const mdDir =
|
|
4084
|
-
if (!
|
|
4400
|
+
const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
|
|
4401
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
4402
|
+
const mdDir = path13.dirname(mdPath);
|
|
4403
|
+
if (!existsSync13(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
4085
4404
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4086
4405
|
const mdContent = `# ${input.title}
|
|
4087
4406
|
|
|
4407
|
+
## MANDATORY: When done
|
|
4408
|
+
|
|
4409
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4410
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4411
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4412
|
+
|
|
4088
4413
|
**ID:** ${id}
|
|
4089
4414
|
**Status:** ${initialStatus}
|
|
4090
4415
|
**Priority:** ${input.priority}
|
|
@@ -4098,12 +4423,6 @@ ${laneWarning}` : laneWarning;
|
|
|
4098
4423
|
## Context
|
|
4099
4424
|
|
|
4100
4425
|
${input.context}
|
|
4101
|
-
|
|
4102
|
-
## MANDATORY: When done
|
|
4103
|
-
|
|
4104
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
4105
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4106
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4107
4426
|
`;
|
|
4108
4427
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
4109
4428
|
} catch (err) {
|
|
@@ -4185,14 +4504,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
4185
4504
|
if (!identifier || identifier === "unknown") return true;
|
|
4186
4505
|
try {
|
|
4187
4506
|
if (identifier.startsWith("%")) {
|
|
4188
|
-
const output =
|
|
4507
|
+
const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
|
|
4189
4508
|
timeout: 2e3,
|
|
4190
4509
|
encoding: "utf8",
|
|
4191
4510
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4192
4511
|
});
|
|
4193
4512
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
4194
4513
|
} else {
|
|
4195
|
-
|
|
4514
|
+
execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
4196
4515
|
timeout: 2e3,
|
|
4197
4516
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4198
4517
|
});
|
|
@@ -4201,7 +4520,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
4201
4520
|
} catch {
|
|
4202
4521
|
if (identifier.startsWith("%")) return true;
|
|
4203
4522
|
try {
|
|
4204
|
-
|
|
4523
|
+
execSync6("tmux list-sessions", {
|
|
4205
4524
|
timeout: 2e3,
|
|
4206
4525
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4207
4526
|
});
|
|
@@ -4216,12 +4535,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
4216
4535
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
4217
4536
|
try {
|
|
4218
4537
|
const since = new Date(taskCreatedAt).toISOString();
|
|
4219
|
-
const branch =
|
|
4538
|
+
const branch = execSync6(
|
|
4220
4539
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
4221
4540
|
{ encoding: "utf8", timeout: 3e3 }
|
|
4222
4541
|
).trim();
|
|
4223
4542
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
4224
|
-
const commitCount =
|
|
4543
|
+
const commitCount = execSync6(
|
|
4225
4544
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
4226
4545
|
{ encoding: "utf8", timeout: 5e3 }
|
|
4227
4546
|
).trim();
|
|
@@ -4352,7 +4671,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4352
4671
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4353
4672
|
} catch {
|
|
4354
4673
|
}
|
|
4355
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
4674
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4356
4675
|
try {
|
|
4357
4676
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4358
4677
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -4381,9 +4700,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4381
4700
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4382
4701
|
}
|
|
4383
4702
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4384
|
-
const archPath =
|
|
4703
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4385
4704
|
try {
|
|
4386
|
-
if (
|
|
4705
|
+
if (existsSync13(archPath)) return;
|
|
4387
4706
|
const template = [
|
|
4388
4707
|
`# ${projectName} \u2014 System Architecture`,
|
|
4389
4708
|
"",
|
|
@@ -4416,10 +4735,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4416
4735
|
}
|
|
4417
4736
|
}
|
|
4418
4737
|
async function ensureGitignoreExe(baseDir) {
|
|
4419
|
-
const gitignorePath =
|
|
4738
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
4420
4739
|
try {
|
|
4421
|
-
if (
|
|
4422
|
-
const content =
|
|
4740
|
+
if (existsSync13(gitignorePath)) {
|
|
4741
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
4423
4742
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4424
4743
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4425
4744
|
} else {
|
|
@@ -4450,58 +4769,42 @@ var init_tasks_crud = __esm({
|
|
|
4450
4769
|
});
|
|
4451
4770
|
|
|
4452
4771
|
// src/lib/tasks-review.ts
|
|
4453
|
-
import
|
|
4454
|
-
import { existsSync as
|
|
4772
|
+
import path14 from "path";
|
|
4773
|
+
import { existsSync as existsSync14, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
4455
4774
|
async function countPendingReviews(sessionScope) {
|
|
4456
4775
|
const client = getClient();
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
args: [sessionScope]
|
|
4461
|
-
});
|
|
4462
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
4463
|
-
}
|
|
4776
|
+
const scope = strictSessionScopeFilter(
|
|
4777
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4778
|
+
);
|
|
4464
4779
|
const result = await client.execute({
|
|
4465
|
-
sql:
|
|
4466
|
-
|
|
4780
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4781
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
4782
|
+
args: [...scope.args]
|
|
4467
4783
|
});
|
|
4468
4784
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4469
4785
|
}
|
|
4470
4786
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
4471
4787
|
const client = getClient();
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
4476
|
-
AND session_scope = ?`,
|
|
4477
|
-
args: [sinceIso, sessionScope]
|
|
4478
|
-
});
|
|
4479
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
4480
|
-
}
|
|
4788
|
+
const scope = strictSessionScopeFilter(
|
|
4789
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4790
|
+
);
|
|
4481
4791
|
const result = await client.execute({
|
|
4482
4792
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4483
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
4484
|
-
args: [sinceIso]
|
|
4793
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
4794
|
+
args: [sinceIso, ...scope.args]
|
|
4485
4795
|
});
|
|
4486
4796
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4487
4797
|
}
|
|
4488
4798
|
async function listPendingReviews(limit, sessionScope) {
|
|
4489
4799
|
const client = getClient();
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
WHERE status = 'needs_review'
|
|
4494
|
-
AND session_scope = ?
|
|
4495
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
4496
|
-
args: [sessionScope, limit]
|
|
4497
|
-
});
|
|
4498
|
-
return result2.rows;
|
|
4499
|
-
}
|
|
4800
|
+
const scope = strictSessionScopeFilter(
|
|
4801
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4802
|
+
);
|
|
4500
4803
|
const result = await client.execute({
|
|
4501
4804
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
4502
|
-
WHERE status = 'needs_review'
|
|
4805
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
4503
4806
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
4504
|
-
args: [limit]
|
|
4807
|
+
args: [...scope.args, limit]
|
|
4505
4808
|
});
|
|
4506
4809
|
return result.rows;
|
|
4507
4810
|
}
|
|
@@ -4513,7 +4816,7 @@ async function cleanupOrphanedReviews() {
|
|
|
4513
4816
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
4514
4817
|
AND assigned_by = 'system'
|
|
4515
4818
|
AND title LIKE 'Review:%'
|
|
4516
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
4819
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
4517
4820
|
args: [now]
|
|
4518
4821
|
});
|
|
4519
4822
|
const r1b = await client.execute({
|
|
@@ -4632,11 +4935,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4632
4935
|
);
|
|
4633
4936
|
}
|
|
4634
4937
|
try {
|
|
4635
|
-
const cacheDir =
|
|
4636
|
-
if (
|
|
4938
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4939
|
+
if (existsSync14(cacheDir)) {
|
|
4637
4940
|
for (const f of readdirSync2(cacheDir)) {
|
|
4638
4941
|
if (f.startsWith("review-notified-")) {
|
|
4639
|
-
unlinkSync4(
|
|
4942
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4640
4943
|
}
|
|
4641
4944
|
}
|
|
4642
4945
|
}
|
|
@@ -4653,11 +4956,12 @@ var init_tasks_review = __esm({
|
|
|
4653
4956
|
init_tmux_routing();
|
|
4654
4957
|
init_session_key();
|
|
4655
4958
|
init_state_bus();
|
|
4959
|
+
init_task_scope();
|
|
4656
4960
|
}
|
|
4657
4961
|
});
|
|
4658
4962
|
|
|
4659
4963
|
// src/lib/tasks-chain.ts
|
|
4660
|
-
import
|
|
4964
|
+
import path15 from "path";
|
|
4661
4965
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
4662
4966
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4663
4967
|
const client = getClient();
|
|
@@ -4674,7 +4978,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4674
4978
|
});
|
|
4675
4979
|
for (const ur of unblockedRows.rows) {
|
|
4676
4980
|
try {
|
|
4677
|
-
const ubFile =
|
|
4981
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4678
4982
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
4679
4983
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4680
4984
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4709,7 +5013,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
4709
5013
|
const scScope = sessionScopeFilter();
|
|
4710
5014
|
const remaining = await client.execute({
|
|
4711
5015
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4712
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
5016
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
4713
5017
|
args: [parentTaskId, ...scScope.args]
|
|
4714
5018
|
});
|
|
4715
5019
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -4741,110 +5045,6 @@ var init_tasks_chain = __esm({
|
|
|
4741
5045
|
}
|
|
4742
5046
|
});
|
|
4743
5047
|
|
|
4744
|
-
// src/lib/project-name.ts
|
|
4745
|
-
import { execSync as execSync6 } from "child_process";
|
|
4746
|
-
import path14 from "path";
|
|
4747
|
-
function getProjectName(cwd2) {
|
|
4748
|
-
const dir = cwd2 ?? process.cwd();
|
|
4749
|
-
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
4750
|
-
try {
|
|
4751
|
-
let repoRoot;
|
|
4752
|
-
try {
|
|
4753
|
-
const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
|
|
4754
|
-
cwd: dir,
|
|
4755
|
-
encoding: "utf8",
|
|
4756
|
-
timeout: 2e3,
|
|
4757
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4758
|
-
}).trim();
|
|
4759
|
-
repoRoot = path14.dirname(gitCommonDir);
|
|
4760
|
-
} catch {
|
|
4761
|
-
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4762
|
-
cwd: dir,
|
|
4763
|
-
encoding: "utf8",
|
|
4764
|
-
timeout: 2e3,
|
|
4765
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4766
|
-
}).trim();
|
|
4767
|
-
}
|
|
4768
|
-
_cached2 = path14.basename(repoRoot);
|
|
4769
|
-
_cachedCwd = dir;
|
|
4770
|
-
return _cached2;
|
|
4771
|
-
} catch {
|
|
4772
|
-
_cached2 = path14.basename(dir);
|
|
4773
|
-
_cachedCwd = dir;
|
|
4774
|
-
return _cached2;
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
var _cached2, _cachedCwd;
|
|
4778
|
-
var init_project_name = __esm({
|
|
4779
|
-
"src/lib/project-name.ts"() {
|
|
4780
|
-
"use strict";
|
|
4781
|
-
_cached2 = null;
|
|
4782
|
-
_cachedCwd = null;
|
|
4783
|
-
}
|
|
4784
|
-
});
|
|
4785
|
-
|
|
4786
|
-
// src/lib/session-scope.ts
|
|
4787
|
-
var session_scope_exports = {};
|
|
4788
|
-
__export(session_scope_exports, {
|
|
4789
|
-
assertSessionScope: () => assertSessionScope,
|
|
4790
|
-
findSessionForProject: () => findSessionForProject,
|
|
4791
|
-
getSessionProject: () => getSessionProject
|
|
4792
|
-
});
|
|
4793
|
-
function getSessionProject(sessionName) {
|
|
4794
|
-
const sessions = listSessions();
|
|
4795
|
-
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
4796
|
-
if (!entry) return null;
|
|
4797
|
-
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
4798
|
-
return parts[parts.length - 1] ?? null;
|
|
4799
|
-
}
|
|
4800
|
-
function findSessionForProject(projectName) {
|
|
4801
|
-
const sessions = listSessions();
|
|
4802
|
-
for (const s of sessions) {
|
|
4803
|
-
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
4804
|
-
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
4805
|
-
}
|
|
4806
|
-
return null;
|
|
4807
|
-
}
|
|
4808
|
-
function assertSessionScope(actionType, targetProject) {
|
|
4809
|
-
try {
|
|
4810
|
-
const currentProject = getProjectName();
|
|
4811
|
-
const exeSession = resolveExeSession();
|
|
4812
|
-
if (!exeSession) {
|
|
4813
|
-
return { allowed: true, reason: "no_session" };
|
|
4814
|
-
}
|
|
4815
|
-
if (currentProject === targetProject) {
|
|
4816
|
-
return {
|
|
4817
|
-
allowed: true,
|
|
4818
|
-
reason: "same_session",
|
|
4819
|
-
currentProject,
|
|
4820
|
-
targetProject
|
|
4821
|
-
};
|
|
4822
|
-
}
|
|
4823
|
-
process.stderr.write(
|
|
4824
|
-
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
4825
|
-
`
|
|
4826
|
-
);
|
|
4827
|
-
return {
|
|
4828
|
-
allowed: false,
|
|
4829
|
-
reason: "cross_session_denied",
|
|
4830
|
-
currentProject,
|
|
4831
|
-
targetProject,
|
|
4832
|
-
targetSession: findSessionForProject(targetProject)?.windowName
|
|
4833
|
-
};
|
|
4834
|
-
} catch {
|
|
4835
|
-
return { allowed: true, reason: "no_session" };
|
|
4836
|
-
}
|
|
4837
|
-
}
|
|
4838
|
-
var init_session_scope = __esm({
|
|
4839
|
-
"src/lib/session-scope.ts"() {
|
|
4840
|
-
"use strict";
|
|
4841
|
-
init_session_registry();
|
|
4842
|
-
init_project_name();
|
|
4843
|
-
init_tmux_routing();
|
|
4844
|
-
init_employees();
|
|
4845
|
-
}
|
|
4846
|
-
});
|
|
4847
|
-
|
|
4848
5048
|
// src/lib/tasks-notify.ts
|
|
4849
5049
|
async function dispatchTaskToEmployee(input) {
|
|
4850
5050
|
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
@@ -4912,10 +5112,10 @@ var init_tasks_notify = __esm({
|
|
|
4912
5112
|
});
|
|
4913
5113
|
|
|
4914
5114
|
// src/lib/behaviors.ts
|
|
4915
|
-
import
|
|
5115
|
+
import crypto5 from "crypto";
|
|
4916
5116
|
async function storeBehavior(opts) {
|
|
4917
5117
|
const client = getClient();
|
|
4918
|
-
const id =
|
|
5118
|
+
const id = crypto5.randomUUID();
|
|
4919
5119
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4920
5120
|
await client.execute({
|
|
4921
5121
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -4944,7 +5144,7 @@ __export(skill_learning_exports, {
|
|
|
4944
5144
|
storeTrajectory: () => storeTrajectory,
|
|
4945
5145
|
sweepTrajectories: () => sweepTrajectories
|
|
4946
5146
|
});
|
|
4947
|
-
import
|
|
5147
|
+
import crypto6 from "crypto";
|
|
4948
5148
|
async function extractTrajectory(taskId, agentId) {
|
|
4949
5149
|
const client = getClient();
|
|
4950
5150
|
const result = await client.execute({
|
|
@@ -4973,11 +5173,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
4973
5173
|
return signature;
|
|
4974
5174
|
}
|
|
4975
5175
|
function hashSignature(signature) {
|
|
4976
|
-
return
|
|
5176
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
4977
5177
|
}
|
|
4978
5178
|
async function storeTrajectory(opts) {
|
|
4979
5179
|
const client = getClient();
|
|
4980
|
-
const id =
|
|
5180
|
+
const id = crypto6.randomUUID();
|
|
4981
5181
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4982
5182
|
const signatureHash = hashSignature(opts.signature);
|
|
4983
5183
|
await client.execute({
|
|
@@ -5242,8 +5442,8 @@ __export(tasks_exports, {
|
|
|
5242
5442
|
updateTaskStatus: () => updateTaskStatus,
|
|
5243
5443
|
writeCheckpoint: () => writeCheckpoint
|
|
5244
5444
|
});
|
|
5245
|
-
import
|
|
5246
|
-
import { writeFileSync as
|
|
5445
|
+
import path16 from "path";
|
|
5446
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
5247
5447
|
async function createTask(input) {
|
|
5248
5448
|
const result = await createTaskCore(input);
|
|
5249
5449
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5262,12 +5462,12 @@ async function updateTask(input) {
|
|
|
5262
5462
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5263
5463
|
try {
|
|
5264
5464
|
const agent = String(row.assigned_to);
|
|
5265
|
-
const cacheDir =
|
|
5266
|
-
const cachePath =
|
|
5465
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5466
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
5267
5467
|
if (input.status === "in_progress") {
|
|
5268
5468
|
mkdirSync5(cacheDir, { recursive: true });
|
|
5269
|
-
|
|
5270
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5469
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5470
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5271
5471
|
try {
|
|
5272
5472
|
unlinkSync5(cachePath);
|
|
5273
5473
|
} catch {
|
|
@@ -5275,10 +5475,10 @@ async function updateTask(input) {
|
|
|
5275
5475
|
}
|
|
5276
5476
|
} catch {
|
|
5277
5477
|
}
|
|
5278
|
-
if (input.status === "done") {
|
|
5478
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5279
5479
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5280
5480
|
}
|
|
5281
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5481
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5282
5482
|
try {
|
|
5283
5483
|
const client = getClient();
|
|
5284
5484
|
const taskTitle = String(row.title);
|
|
@@ -5294,7 +5494,7 @@ async function updateTask(input) {
|
|
|
5294
5494
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5295
5495
|
try {
|
|
5296
5496
|
const draftClient = getClient();
|
|
5297
|
-
if (input.status === "done") {
|
|
5497
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5298
5498
|
await draftClient.execute({
|
|
5299
5499
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
5300
5500
|
args: [assignedAgent]
|
|
@@ -5311,7 +5511,7 @@ async function updateTask(input) {
|
|
|
5311
5511
|
try {
|
|
5312
5512
|
const client = getClient();
|
|
5313
5513
|
const cascaded = await client.execute({
|
|
5314
|
-
sql: `UPDATE tasks SET status = '
|
|
5514
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
5315
5515
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
5316
5516
|
args: [now, taskId]
|
|
5317
5517
|
});
|
|
@@ -5324,14 +5524,14 @@ async function updateTask(input) {
|
|
|
5324
5524
|
} catch {
|
|
5325
5525
|
}
|
|
5326
5526
|
}
|
|
5327
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
5527
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
5328
5528
|
if (isTerminal) {
|
|
5329
5529
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
5330
5530
|
if (!isCoordinator) {
|
|
5331
5531
|
notifyTaskDone();
|
|
5332
5532
|
}
|
|
5333
5533
|
await markTaskNotificationsRead(taskFile);
|
|
5334
|
-
if (input.status === "done") {
|
|
5534
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5335
5535
|
try {
|
|
5336
5536
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5337
5537
|
} catch {
|
|
@@ -5351,7 +5551,7 @@ async function updateTask(input) {
|
|
|
5351
5551
|
}
|
|
5352
5552
|
}
|
|
5353
5553
|
}
|
|
5354
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5554
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5355
5555
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
5356
5556
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
5357
5557
|
taskId,
|
|
@@ -5723,6 +5923,7 @@ __export(tmux_routing_exports, {
|
|
|
5723
5923
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
5724
5924
|
isExeSession: () => isExeSession,
|
|
5725
5925
|
isSessionBusy: () => isSessionBusy,
|
|
5926
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
5726
5927
|
notifyParentExe: () => notifyParentExe,
|
|
5727
5928
|
parseParentExe: () => parseParentExe,
|
|
5728
5929
|
registerParentExe: () => registerParentExe,
|
|
@@ -5733,13 +5934,13 @@ __export(tmux_routing_exports, {
|
|
|
5733
5934
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5734
5935
|
});
|
|
5735
5936
|
import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
|
|
5736
|
-
import { readFileSync as
|
|
5737
|
-
import
|
|
5738
|
-
import
|
|
5937
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5938
|
+
import path17 from "path";
|
|
5939
|
+
import os10 from "os";
|
|
5739
5940
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5740
5941
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5741
5942
|
function spawnLockPath(sessionName) {
|
|
5742
|
-
return
|
|
5943
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5743
5944
|
}
|
|
5744
5945
|
function isProcessAlive(pid) {
|
|
5745
5946
|
try {
|
|
@@ -5750,13 +5951,13 @@ function isProcessAlive(pid) {
|
|
|
5750
5951
|
}
|
|
5751
5952
|
}
|
|
5752
5953
|
function acquireSpawnLock2(sessionName) {
|
|
5753
|
-
if (!
|
|
5954
|
+
if (!existsSync15(SPAWN_LOCK_DIR)) {
|
|
5754
5955
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
5755
5956
|
}
|
|
5756
5957
|
const lockFile = spawnLockPath(sessionName);
|
|
5757
|
-
if (
|
|
5958
|
+
if (existsSync15(lockFile)) {
|
|
5758
5959
|
try {
|
|
5759
|
-
const lock = JSON.parse(
|
|
5960
|
+
const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
|
|
5760
5961
|
const age = Date.now() - lock.timestamp;
|
|
5761
5962
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5762
5963
|
return false;
|
|
@@ -5764,7 +5965,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5764
5965
|
} catch {
|
|
5765
5966
|
}
|
|
5766
5967
|
}
|
|
5767
|
-
|
|
5968
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5768
5969
|
return true;
|
|
5769
5970
|
}
|
|
5770
5971
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5776,13 +5977,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5776
5977
|
function resolveBehaviorsExporterScript() {
|
|
5777
5978
|
try {
|
|
5778
5979
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5779
|
-
const scriptPath =
|
|
5780
|
-
|
|
5980
|
+
const scriptPath = path17.join(
|
|
5981
|
+
path17.dirname(thisFile),
|
|
5781
5982
|
"..",
|
|
5782
5983
|
"bin",
|
|
5783
5984
|
"exe-export-behaviors.js"
|
|
5784
5985
|
);
|
|
5785
|
-
return
|
|
5986
|
+
return existsSync15(scriptPath) ? scriptPath : null;
|
|
5786
5987
|
} catch {
|
|
5787
5988
|
return null;
|
|
5788
5989
|
}
|
|
@@ -5848,12 +6049,12 @@ function extractRootExe(name) {
|
|
|
5848
6049
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5849
6050
|
}
|
|
5850
6051
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5851
|
-
if (!
|
|
6052
|
+
if (!existsSync15(SESSION_CACHE)) {
|
|
5852
6053
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5853
6054
|
}
|
|
5854
6055
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5855
|
-
const filePath =
|
|
5856
|
-
|
|
6056
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6057
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5857
6058
|
parentExe: rootExe,
|
|
5858
6059
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5859
6060
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5861,7 +6062,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5861
6062
|
}
|
|
5862
6063
|
function getParentExe(sessionKey) {
|
|
5863
6064
|
try {
|
|
5864
|
-
const data = JSON.parse(
|
|
6065
|
+
const data = JSON.parse(readFileSync13(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5865
6066
|
return data.parentExe || null;
|
|
5866
6067
|
} catch {
|
|
5867
6068
|
return null;
|
|
@@ -5869,8 +6070,8 @@ function getParentExe(sessionKey) {
|
|
|
5869
6070
|
}
|
|
5870
6071
|
function getDispatchedBy(sessionKey) {
|
|
5871
6072
|
try {
|
|
5872
|
-
const data = JSON.parse(
|
|
5873
|
-
|
|
6073
|
+
const data = JSON.parse(readFileSync13(
|
|
6074
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5874
6075
|
"utf8"
|
|
5875
6076
|
));
|
|
5876
6077
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5940,8 +6141,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5940
6141
|
}
|
|
5941
6142
|
function readDebounceState() {
|
|
5942
6143
|
try {
|
|
5943
|
-
if (!
|
|
5944
|
-
const raw = JSON.parse(
|
|
6144
|
+
if (!existsSync15(DEBOUNCE_FILE)) return {};
|
|
6145
|
+
const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
|
|
5945
6146
|
const state = {};
|
|
5946
6147
|
for (const [key, val] of Object.entries(raw)) {
|
|
5947
6148
|
if (typeof val === "number") {
|
|
@@ -5957,8 +6158,8 @@ function readDebounceState() {
|
|
|
5957
6158
|
}
|
|
5958
6159
|
function writeDebounceState(state) {
|
|
5959
6160
|
try {
|
|
5960
|
-
if (!
|
|
5961
|
-
|
|
6161
|
+
if (!existsSync15(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
6162
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5962
6163
|
} catch {
|
|
5963
6164
|
}
|
|
5964
6165
|
}
|
|
@@ -6056,8 +6257,8 @@ function sendIntercom(targetSession) {
|
|
|
6056
6257
|
try {
|
|
6057
6258
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6058
6259
|
const agent = baseAgentName(rawAgent);
|
|
6059
|
-
const markerPath =
|
|
6060
|
-
if (
|
|
6260
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6261
|
+
if (existsSync15(markerPath)) {
|
|
6061
6262
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6062
6263
|
return "debounced";
|
|
6063
6264
|
}
|
|
@@ -6066,8 +6267,8 @@ function sendIntercom(targetSession) {
|
|
|
6066
6267
|
try {
|
|
6067
6268
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6068
6269
|
const agent = baseAgentName(rawAgent);
|
|
6069
|
-
const taskDir =
|
|
6070
|
-
if (
|
|
6270
|
+
const taskDir = path17.join(process.cwd(), "exe", agent);
|
|
6271
|
+
if (existsSync15(taskDir)) {
|
|
6071
6272
|
const files = readdirSync3(taskDir).filter(
|
|
6072
6273
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
6073
6274
|
);
|
|
@@ -6127,6 +6328,21 @@ function notifyParentExe(sessionKey) {
|
|
|
6127
6328
|
}
|
|
6128
6329
|
return true;
|
|
6129
6330
|
}
|
|
6331
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
6332
|
+
const transport = getTransport();
|
|
6333
|
+
try {
|
|
6334
|
+
const sessions = transport.listSessions();
|
|
6335
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
6336
|
+
execSync7(
|
|
6337
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6338
|
+
{ timeout: 3e3 }
|
|
6339
|
+
);
|
|
6340
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
6341
|
+
return true;
|
|
6342
|
+
} catch {
|
|
6343
|
+
return false;
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6130
6346
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
6131
6347
|
if (isCoordinatorName(employeeName)) {
|
|
6132
6348
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6200,26 +6416,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6200
6416
|
const transport = getTransport();
|
|
6201
6417
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6202
6418
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6203
|
-
const logDir =
|
|
6204
|
-
const logFile =
|
|
6205
|
-
if (!
|
|
6419
|
+
const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
|
|
6420
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6421
|
+
if (!existsSync15(logDir)) {
|
|
6206
6422
|
mkdirSync6(logDir, { recursive: true });
|
|
6207
6423
|
}
|
|
6208
6424
|
transport.kill(sessionName);
|
|
6209
6425
|
let cleanupSuffix = "";
|
|
6210
6426
|
try {
|
|
6211
6427
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6212
|
-
const cleanupScript =
|
|
6213
|
-
if (
|
|
6428
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6429
|
+
if (existsSync15(cleanupScript)) {
|
|
6214
6430
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6215
6431
|
}
|
|
6216
6432
|
} catch {
|
|
6217
6433
|
}
|
|
6218
6434
|
try {
|
|
6219
|
-
const claudeJsonPath =
|
|
6435
|
+
const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
|
|
6220
6436
|
let claudeJson = {};
|
|
6221
6437
|
try {
|
|
6222
|
-
claudeJson = JSON.parse(
|
|
6438
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
6223
6439
|
} catch {
|
|
6224
6440
|
}
|
|
6225
6441
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6227,17 +6443,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6227
6443
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6228
6444
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6229
6445
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6230
|
-
|
|
6446
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6231
6447
|
} catch {
|
|
6232
6448
|
}
|
|
6233
6449
|
try {
|
|
6234
|
-
const settingsDir =
|
|
6450
|
+
const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
|
|
6235
6451
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6236
|
-
const projSettingsDir =
|
|
6237
|
-
const settingsPath =
|
|
6452
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6453
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
6238
6454
|
let settings = {};
|
|
6239
6455
|
try {
|
|
6240
|
-
settings = JSON.parse(
|
|
6456
|
+
settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
|
|
6241
6457
|
} catch {
|
|
6242
6458
|
}
|
|
6243
6459
|
const perms = settings.permissions ?? {};
|
|
@@ -6266,7 +6482,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6266
6482
|
perms.allow = allow;
|
|
6267
6483
|
settings.permissions = perms;
|
|
6268
6484
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
6269
|
-
|
|
6485
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6270
6486
|
}
|
|
6271
6487
|
} catch {
|
|
6272
6488
|
}
|
|
@@ -6281,8 +6497,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6281
6497
|
let behaviorsFlag = "";
|
|
6282
6498
|
let legacyFallbackWarned = false;
|
|
6283
6499
|
if (!useExeAgent && !useBinSymlink) {
|
|
6284
|
-
const identityPath =
|
|
6285
|
-
|
|
6500
|
+
const identityPath = path17.join(
|
|
6501
|
+
os10.homedir(),
|
|
6286
6502
|
".exe-os",
|
|
6287
6503
|
"identity",
|
|
6288
6504
|
`${employeeName}.md`
|
|
@@ -6291,13 +6507,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6291
6507
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6292
6508
|
if (hasAgentFlag) {
|
|
6293
6509
|
identityFlag = ` --agent ${employeeName}`;
|
|
6294
|
-
} else if (
|
|
6510
|
+
} else if (existsSync15(identityPath)) {
|
|
6295
6511
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6296
6512
|
legacyFallbackWarned = true;
|
|
6297
6513
|
}
|
|
6298
6514
|
const behaviorsFile = exportBehaviorsSync(
|
|
6299
6515
|
employeeName,
|
|
6300
|
-
|
|
6516
|
+
path17.basename(spawnCwd),
|
|
6301
6517
|
sessionName
|
|
6302
6518
|
);
|
|
6303
6519
|
if (behaviorsFile) {
|
|
@@ -6312,16 +6528,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6312
6528
|
}
|
|
6313
6529
|
let sessionContextFlag = "";
|
|
6314
6530
|
try {
|
|
6315
|
-
const ctxDir =
|
|
6531
|
+
const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6316
6532
|
mkdirSync6(ctxDir, { recursive: true });
|
|
6317
|
-
const ctxFile =
|
|
6533
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6318
6534
|
const ctxContent = [
|
|
6319
6535
|
`## Session Context`,
|
|
6320
6536
|
`You are running in tmux session: ${sessionName}.`,
|
|
6321
6537
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6322
6538
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6323
6539
|
].join("\n");
|
|
6324
|
-
|
|
6540
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
6325
6541
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6326
6542
|
} catch {
|
|
6327
6543
|
}
|
|
@@ -6398,8 +6614,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6398
6614
|
transport.pipeLog(sessionName, logFile);
|
|
6399
6615
|
try {
|
|
6400
6616
|
const mySession = getMySession();
|
|
6401
|
-
const dispatchInfo =
|
|
6402
|
-
|
|
6617
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6618
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
6403
6619
|
dispatchedBy: mySession,
|
|
6404
6620
|
rootExe: exeSession,
|
|
6405
6621
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -6473,15 +6689,15 @@ var init_tmux_routing = __esm({
|
|
|
6473
6689
|
init_intercom_queue();
|
|
6474
6690
|
init_plan_limits();
|
|
6475
6691
|
init_employees();
|
|
6476
|
-
SPAWN_LOCK_DIR =
|
|
6477
|
-
SESSION_CACHE =
|
|
6692
|
+
SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6693
|
+
SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6478
6694
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6479
6695
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6480
6696
|
VERIFY_PANE_LINES = 200;
|
|
6481
6697
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6482
6698
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
6483
|
-
INTERCOM_LOG2 =
|
|
6484
|
-
DEBOUNCE_FILE =
|
|
6699
|
+
INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6700
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6485
6701
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6486
6702
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
6487
6703
|
}
|
|
@@ -6491,7 +6707,8 @@ var init_tmux_routing = __esm({
|
|
|
6491
6707
|
var task_scope_exports = {};
|
|
6492
6708
|
__export(task_scope_exports, {
|
|
6493
6709
|
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
6494
|
-
sessionScopeFilter: () => sessionScopeFilter
|
|
6710
|
+
sessionScopeFilter: () => sessionScopeFilter,
|
|
6711
|
+
strictSessionScopeFilter: () => strictSessionScopeFilter
|
|
6495
6712
|
});
|
|
6496
6713
|
function getCurrentSessionScope() {
|
|
6497
6714
|
try {
|
|
@@ -6509,6 +6726,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
6509
6726
|
args: [scope]
|
|
6510
6727
|
};
|
|
6511
6728
|
}
|
|
6729
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
6730
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
6731
|
+
if (!scope) return { sql: "", args: [] };
|
|
6732
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
6733
|
+
return {
|
|
6734
|
+
sql: ` AND ${col} = ?`,
|
|
6735
|
+
args: [scope]
|
|
6736
|
+
};
|
|
6737
|
+
}
|
|
6512
6738
|
var init_task_scope = __esm({
|
|
6513
6739
|
"src/lib/task-scope.ts"() {
|
|
6514
6740
|
"use strict";
|
|
@@ -8367,10 +8593,10 @@ var init_hooks = __esm({
|
|
|
8367
8593
|
});
|
|
8368
8594
|
|
|
8369
8595
|
// src/runtime/safety-checks.ts
|
|
8370
|
-
import
|
|
8371
|
-
import
|
|
8596
|
+
import path18 from "path";
|
|
8597
|
+
import os11 from "os";
|
|
8372
8598
|
function checkPathSafety(filePath) {
|
|
8373
|
-
const resolved =
|
|
8599
|
+
const resolved = path18.resolve(filePath);
|
|
8374
8600
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
8375
8601
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
8376
8602
|
if (matches) {
|
|
@@ -8380,7 +8606,7 @@ function checkPathSafety(filePath) {
|
|
|
8380
8606
|
return { safe: true, bypassImmune: true };
|
|
8381
8607
|
}
|
|
8382
8608
|
function checkReadPathSafety(filePath) {
|
|
8383
|
-
const resolved =
|
|
8609
|
+
const resolved = path18.resolve(filePath);
|
|
8384
8610
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
8385
8611
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
8386
8612
|
);
|
|
@@ -8395,7 +8621,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
8395
8621
|
var init_safety_checks = __esm({
|
|
8396
8622
|
"src/runtime/safety-checks.ts"() {
|
|
8397
8623
|
"use strict";
|
|
8398
|
-
HOME =
|
|
8624
|
+
HOME = os11.homedir();
|
|
8399
8625
|
BYPASS_IMMUNE_PATTERNS = [
|
|
8400
8626
|
{
|
|
8401
8627
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -8406,11 +8632,11 @@ var init_safety_checks = __esm({
|
|
|
8406
8632
|
reason: "Git config can set hooks and command execution"
|
|
8407
8633
|
},
|
|
8408
8634
|
{
|
|
8409
|
-
pattern: (p) => p.startsWith(
|
|
8635
|
+
pattern: (p) => p.startsWith(path18.join(HOME, ".claude")),
|
|
8410
8636
|
reason: "Claude configuration files are protected"
|
|
8411
8637
|
},
|
|
8412
8638
|
{
|
|
8413
|
-
pattern: (p) => p.startsWith(
|
|
8639
|
+
pattern: (p) => p.startsWith(path18.join(HOME, ".exe-os")),
|
|
8414
8640
|
reason: "exe-os configuration files are protected"
|
|
8415
8641
|
},
|
|
8416
8642
|
{
|
|
@@ -8427,7 +8653,7 @@ var init_safety_checks = __esm({
|
|
|
8427
8653
|
},
|
|
8428
8654
|
{
|
|
8429
8655
|
pattern: (p) => {
|
|
8430
|
-
const name =
|
|
8656
|
+
const name = path18.basename(p);
|
|
8431
8657
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
8432
8658
|
},
|
|
8433
8659
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -8454,7 +8680,7 @@ __export(file_read_exports, {
|
|
|
8454
8680
|
FileReadTool: () => FileReadTool
|
|
8455
8681
|
});
|
|
8456
8682
|
import fs3 from "fs/promises";
|
|
8457
|
-
import
|
|
8683
|
+
import path19 from "path";
|
|
8458
8684
|
import { z } from "zod";
|
|
8459
8685
|
function isBinary(buf) {
|
|
8460
8686
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -8490,7 +8716,7 @@ var init_file_read = __esm({
|
|
|
8490
8716
|
return { behavior: "allow" };
|
|
8491
8717
|
},
|
|
8492
8718
|
async call(input, context) {
|
|
8493
|
-
const filePath =
|
|
8719
|
+
const filePath = path19.isAbsolute(input.file_path) ? input.file_path : path19.resolve(context.cwd, input.file_path);
|
|
8494
8720
|
let stat;
|
|
8495
8721
|
try {
|
|
8496
8722
|
stat = await fs3.stat(filePath);
|
|
@@ -8530,7 +8756,7 @@ __export(glob_exports, {
|
|
|
8530
8756
|
GlobTool: () => GlobTool
|
|
8531
8757
|
});
|
|
8532
8758
|
import fs4 from "fs/promises";
|
|
8533
|
-
import
|
|
8759
|
+
import path20 from "path";
|
|
8534
8760
|
import { z as z2 } from "zod";
|
|
8535
8761
|
async function walkDir(dir, maxDepth = 10) {
|
|
8536
8762
|
const results = [];
|
|
@@ -8546,7 +8772,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
8546
8772
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
8547
8773
|
continue;
|
|
8548
8774
|
}
|
|
8549
|
-
const fullPath =
|
|
8775
|
+
const fullPath = path20.join(current, entry.name);
|
|
8550
8776
|
if (entry.isDirectory()) {
|
|
8551
8777
|
await walk(fullPath, depth + 1);
|
|
8552
8778
|
} else {
|
|
@@ -8580,11 +8806,11 @@ var init_glob = __esm({
|
|
|
8580
8806
|
inputSchema: inputSchema2,
|
|
8581
8807
|
isReadOnly: true,
|
|
8582
8808
|
async call(input, context) {
|
|
8583
|
-
const baseDir = input.path ?
|
|
8809
|
+
const baseDir = input.path ? path20.isAbsolute(input.path) ? input.path : path20.resolve(context.cwd, input.path) : context.cwd;
|
|
8584
8810
|
try {
|
|
8585
8811
|
const entries = await walkDir(baseDir);
|
|
8586
8812
|
const matched = entries.filter(
|
|
8587
|
-
(e) => simpleGlobMatch(
|
|
8813
|
+
(e) => simpleGlobMatch(path20.relative(baseDir, e.path), input.pattern)
|
|
8588
8814
|
);
|
|
8589
8815
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
8590
8816
|
if (matched.length === 0) {
|
|
@@ -8610,7 +8836,7 @@ __export(grep_exports, {
|
|
|
8610
8836
|
});
|
|
8611
8837
|
import { spawn as spawn2 } from "child_process";
|
|
8612
8838
|
import fs5 from "fs/promises";
|
|
8613
|
-
import
|
|
8839
|
+
import path21 from "path";
|
|
8614
8840
|
import { z as z3 } from "zod";
|
|
8615
8841
|
function runRipgrep(input, searchPath, context) {
|
|
8616
8842
|
return new Promise((resolve, reject) => {
|
|
@@ -8664,7 +8890,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
8664
8890
|
}
|
|
8665
8891
|
for (const entry of entries) {
|
|
8666
8892
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
8667
|
-
const fullPath =
|
|
8893
|
+
const fullPath = path21.join(dir, entry.name);
|
|
8668
8894
|
if (entry.isDirectory()) {
|
|
8669
8895
|
await walk(fullPath);
|
|
8670
8896
|
} else {
|
|
@@ -8710,7 +8936,7 @@ var init_grep = __esm({
|
|
|
8710
8936
|
inputSchema: inputSchema3,
|
|
8711
8937
|
isReadOnly: true,
|
|
8712
8938
|
async call(input, context) {
|
|
8713
|
-
const searchPath = input.path ?
|
|
8939
|
+
const searchPath = input.path ? path21.isAbsolute(input.path) ? input.path : path21.resolve(context.cwd, input.path) : context.cwd;
|
|
8714
8940
|
try {
|
|
8715
8941
|
const result = await runRipgrep(input, searchPath, context);
|
|
8716
8942
|
return result;
|
|
@@ -8735,7 +8961,7 @@ __export(file_write_exports, {
|
|
|
8735
8961
|
FileWriteTool: () => FileWriteTool
|
|
8736
8962
|
});
|
|
8737
8963
|
import fs6 from "fs/promises";
|
|
8738
|
-
import
|
|
8964
|
+
import path22 from "path";
|
|
8739
8965
|
import { z as z4 } from "zod";
|
|
8740
8966
|
var inputSchema4, FileWriteTool;
|
|
8741
8967
|
var init_file_write = __esm({
|
|
@@ -8763,8 +8989,8 @@ var init_file_write = __esm({
|
|
|
8763
8989
|
return { behavior: "allow" };
|
|
8764
8990
|
},
|
|
8765
8991
|
async call(input, context) {
|
|
8766
|
-
const filePath =
|
|
8767
|
-
const dir =
|
|
8992
|
+
const filePath = path22.isAbsolute(input.file_path) ? input.file_path : path22.resolve(context.cwd, input.file_path);
|
|
8993
|
+
const dir = path22.dirname(filePath);
|
|
8768
8994
|
await fs6.mkdir(dir, { recursive: true });
|
|
8769
8995
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
8770
8996
|
return {
|
|
@@ -8782,7 +9008,7 @@ __export(file_edit_exports, {
|
|
|
8782
9008
|
FileEditTool: () => FileEditTool
|
|
8783
9009
|
});
|
|
8784
9010
|
import fs7 from "fs/promises";
|
|
8785
|
-
import
|
|
9011
|
+
import path23 from "path";
|
|
8786
9012
|
import { z as z5 } from "zod";
|
|
8787
9013
|
function countOccurrences(haystack, needle) {
|
|
8788
9014
|
let count = 0;
|
|
@@ -8823,7 +9049,7 @@ var init_file_edit = __esm({
|
|
|
8823
9049
|
return { behavior: "allow" };
|
|
8824
9050
|
},
|
|
8825
9051
|
async call(input, context) {
|
|
8826
|
-
const filePath =
|
|
9052
|
+
const filePath = path23.isAbsolute(input.file_path) ? input.file_path : path23.resolve(context.cwd, input.file_path);
|
|
8827
9053
|
let content;
|
|
8828
9054
|
try {
|
|
8829
9055
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -9430,6 +9656,133 @@ ${task.context}`,
|
|
|
9430
9656
|
}
|
|
9431
9657
|
});
|
|
9432
9658
|
|
|
9659
|
+
// src/lib/tui-data.ts
|
|
9660
|
+
var tui_data_exports = {};
|
|
9661
|
+
__export(tui_data_exports, {
|
|
9662
|
+
loadMemoryDashboard: () => loadMemoryDashboard,
|
|
9663
|
+
loadTaskList: () => loadTaskList,
|
|
9664
|
+
loadTeamMetrics: () => loadTeamMetrics,
|
|
9665
|
+
searchWikiMemoryRows: () => searchWikiMemoryRows
|
|
9666
|
+
});
|
|
9667
|
+
async function loadMemoryDashboard(limit) {
|
|
9668
|
+
const client = getClient();
|
|
9669
|
+
const [countResult, recentResult, agentResult] = await Promise.all([
|
|
9670
|
+
client.execute("SELECT COUNT(*) as cnt FROM memories"),
|
|
9671
|
+
client.execute({
|
|
9672
|
+
sql: "SELECT agent_id, tool_name, project_name, raw_text, timestamp FROM memories ORDER BY timestamp DESC LIMIT ?",
|
|
9673
|
+
args: [limit]
|
|
9674
|
+
}),
|
|
9675
|
+
client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id ORDER BY cnt DESC")
|
|
9676
|
+
]);
|
|
9677
|
+
return {
|
|
9678
|
+
total: Number(countResult.rows[0]?.cnt ?? 0),
|
|
9679
|
+
recent: recentResult.rows.map((row) => ({
|
|
9680
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
9681
|
+
toolName: String(row.tool_name ?? ""),
|
|
9682
|
+
projectName: String(row.project_name ?? ""),
|
|
9683
|
+
rawText: String(row.raw_text ?? ""),
|
|
9684
|
+
timestamp: String(row.timestamp ?? "")
|
|
9685
|
+
})),
|
|
9686
|
+
byAgent: agentResult.rows.map((row) => ({
|
|
9687
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
9688
|
+
count: Number(row.cnt ?? 0)
|
|
9689
|
+
}))
|
|
9690
|
+
};
|
|
9691
|
+
}
|
|
9692
|
+
async function loadTeamMetrics(employeeNames) {
|
|
9693
|
+
const client = getClient();
|
|
9694
|
+
const memoryCounts = /* @__PURE__ */ new Map();
|
|
9695
|
+
const projectsByEmployee = /* @__PURE__ */ new Map();
|
|
9696
|
+
const currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
9697
|
+
const scope = sessionScopeFilter();
|
|
9698
|
+
const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
|
|
9699
|
+
for (const row of memResult.rows) {
|
|
9700
|
+
memoryCounts.set(String(row.agent_id), Number(row.cnt));
|
|
9701
|
+
}
|
|
9702
|
+
for (const employeeName of employeeNames) {
|
|
9703
|
+
const [projectResult, taskResult] = await Promise.all([
|
|
9704
|
+
client.execute({
|
|
9705
|
+
sql: `SELECT DISTINCT project_name,
|
|
9706
|
+
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
9707
|
+
FROM tasks
|
|
9708
|
+
WHERE assigned_to = ? AND status IN ('open','in_progress','done')${scope.sql}
|
|
9709
|
+
GROUP BY project_name
|
|
9710
|
+
ORDER BY urgency ASC
|
|
9711
|
+
LIMIT 5`,
|
|
9712
|
+
args: [employeeName, ...scope.args]
|
|
9713
|
+
}),
|
|
9714
|
+
client.execute({
|
|
9715
|
+
sql: `SELECT title FROM tasks
|
|
9716
|
+
WHERE assigned_to = ? AND status = 'in_progress'${scope.sql}
|
|
9717
|
+
ORDER BY updated_at DESC
|
|
9718
|
+
LIMIT 1`,
|
|
9719
|
+
args: [employeeName, ...scope.args]
|
|
9720
|
+
})
|
|
9721
|
+
]);
|
|
9722
|
+
const projects = projectResult.rows.map((row) => {
|
|
9723
|
+
const urgency = Number(row.urgency);
|
|
9724
|
+
return {
|
|
9725
|
+
name: String(row.project_name),
|
|
9726
|
+
status: urgency === 1 ? "active" : urgency === 2 ? "has_tasks" : "idle"
|
|
9727
|
+
};
|
|
9728
|
+
});
|
|
9729
|
+
if (projects.length > 0) projectsByEmployee.set(employeeName, projects);
|
|
9730
|
+
if (taskResult.rows.length > 0) {
|
|
9731
|
+
currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
|
|
9732
|
+
}
|
|
9733
|
+
}
|
|
9734
|
+
return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
|
|
9735
|
+
}
|
|
9736
|
+
async function loadTaskList() {
|
|
9737
|
+
const client = getClient();
|
|
9738
|
+
const scope = sessionScopeFilter();
|
|
9739
|
+
const result = await client.execute({
|
|
9740
|
+
sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
|
|
9741
|
+
FROM tasks
|
|
9742
|
+
WHERE 1=1${scope.sql}
|
|
9743
|
+
ORDER BY
|
|
9744
|
+
CASE status WHEN 'in_progress' THEN 0 WHEN 'open' THEN 1 WHEN 'blocked' THEN 2 WHEN 'needs_review' THEN 3 WHEN 'done' THEN 4 ELSE 5 END,
|
|
9745
|
+
CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
|
|
9746
|
+
created_at DESC`,
|
|
9747
|
+
args: [...scope.args]
|
|
9748
|
+
});
|
|
9749
|
+
return result.rows.map((row) => ({
|
|
9750
|
+
id: String(row.id ?? ""),
|
|
9751
|
+
title: String(row.title ?? ""),
|
|
9752
|
+
priority: String(row.priority ?? "p2").toUpperCase(),
|
|
9753
|
+
assignedTo: String(row.assigned_to ?? ""),
|
|
9754
|
+
assignedBy: String(row.assigned_by ?? ""),
|
|
9755
|
+
status: String(row.status ?? "open"),
|
|
9756
|
+
projectName: String(row.project_name ?? ""),
|
|
9757
|
+
createdAt: String(row.created_at ?? ""),
|
|
9758
|
+
result: String(row.result ?? "")
|
|
9759
|
+
}));
|
|
9760
|
+
}
|
|
9761
|
+
async function searchWikiMemoryRows(query) {
|
|
9762
|
+
const client = getClient();
|
|
9763
|
+
const result = await client.execute({
|
|
9764
|
+
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
9765
|
+
FROM memories
|
|
9766
|
+
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
9767
|
+
ORDER BY timestamp DESC LIMIT 20`,
|
|
9768
|
+
args: [`%${query}%`]
|
|
9769
|
+
});
|
|
9770
|
+
return result.rows.map((row) => ({
|
|
9771
|
+
id: String(row.id),
|
|
9772
|
+
agentId: String(row.agent_id),
|
|
9773
|
+
rawText: String(row.raw_text),
|
|
9774
|
+
timestamp: String(row.timestamp),
|
|
9775
|
+
projectName: String(row.project_name ?? "")
|
|
9776
|
+
}));
|
|
9777
|
+
}
|
|
9778
|
+
var init_tui_data = __esm({
|
|
9779
|
+
"src/lib/tui-data.ts"() {
|
|
9780
|
+
"use strict";
|
|
9781
|
+
init_database();
|
|
9782
|
+
init_task_scope();
|
|
9783
|
+
}
|
|
9784
|
+
});
|
|
9785
|
+
|
|
9433
9786
|
// src/lib/message-queue.ts
|
|
9434
9787
|
var DEFAULT_MAX_SIZE, DEFAULT_TTL_MS, MessageQueue;
|
|
9435
9788
|
var init_message_queue = __esm({
|
|
@@ -9488,14 +9841,14 @@ __export(keychain_exports, {
|
|
|
9488
9841
|
setMasterKey: () => setMasterKey
|
|
9489
9842
|
});
|
|
9490
9843
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
9491
|
-
import { existsSync as
|
|
9492
|
-
import
|
|
9493
|
-
import
|
|
9844
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9845
|
+
import path26 from "path";
|
|
9846
|
+
import os12 from "os";
|
|
9494
9847
|
function getKeyDir() {
|
|
9495
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
9848
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path26.join(os12.homedir(), ".exe-os");
|
|
9496
9849
|
}
|
|
9497
9850
|
function getKeyPath() {
|
|
9498
|
-
return
|
|
9851
|
+
return path26.join(getKeyDir(), "master.key");
|
|
9499
9852
|
}
|
|
9500
9853
|
async function tryKeytar() {
|
|
9501
9854
|
try {
|
|
@@ -9516,9 +9869,9 @@ async function getMasterKey() {
|
|
|
9516
9869
|
}
|
|
9517
9870
|
}
|
|
9518
9871
|
const keyPath = getKeyPath();
|
|
9519
|
-
if (!
|
|
9872
|
+
if (!existsSync16(keyPath)) {
|
|
9520
9873
|
process.stderr.write(
|
|
9521
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
9874
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os12.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
9522
9875
|
`
|
|
9523
9876
|
);
|
|
9524
9877
|
return null;
|
|
@@ -9559,7 +9912,7 @@ async function deleteMasterKey() {
|
|
|
9559
9912
|
}
|
|
9560
9913
|
}
|
|
9561
9914
|
const keyPath = getKeyPath();
|
|
9562
|
-
if (
|
|
9915
|
+
if (existsSync16(keyPath)) {
|
|
9563
9916
|
await unlink(keyPath);
|
|
9564
9917
|
}
|
|
9565
9918
|
}
|
|
@@ -9608,16 +9961,16 @@ __export(ws_auth_exports, {
|
|
|
9608
9961
|
deriveWsAuthToken: () => deriveWsAuthToken,
|
|
9609
9962
|
hashAuthToken: () => hashAuthToken
|
|
9610
9963
|
});
|
|
9611
|
-
import
|
|
9964
|
+
import crypto7 from "crypto";
|
|
9612
9965
|
function deriveWsAuthToken(masterKey) {
|
|
9613
|
-
return Buffer.from(
|
|
9966
|
+
return Buffer.from(crypto7.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
|
|
9614
9967
|
}
|
|
9615
9968
|
function deriveOrgId(masterKey) {
|
|
9616
|
-
const raw = Buffer.from(
|
|
9617
|
-
return
|
|
9969
|
+
const raw = Buffer.from(crypto7.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
|
|
9970
|
+
return crypto7.createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
9618
9971
|
}
|
|
9619
9972
|
function hashAuthToken(token) {
|
|
9620
|
-
return
|
|
9973
|
+
return crypto7.createHash("sha256").update(token).digest("hex");
|
|
9621
9974
|
}
|
|
9622
9975
|
var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
|
|
9623
9976
|
var init_ws_auth = __esm({
|
|
@@ -9859,8 +10212,8 @@ __export(wiki_client_exports, {
|
|
|
9859
10212
|
listDocuments: () => listDocuments,
|
|
9860
10213
|
listWorkspaces: () => listWorkspaces
|
|
9861
10214
|
});
|
|
9862
|
-
async function wikiFetch(config,
|
|
9863
|
-
const url = `${config.baseUrl}/api/v1${
|
|
10215
|
+
async function wikiFetch(config, path28, method = "GET", body) {
|
|
10216
|
+
const url = `${config.baseUrl}/api/v1${path28}`;
|
|
9864
10217
|
const headers = {
|
|
9865
10218
|
Authorization: `Bearer ${config.apiKey}`,
|
|
9866
10219
|
"Content-Type": "application/json"
|
|
@@ -9893,7 +10246,7 @@ async function wikiFetch(config, path27, method = "GET", body) {
|
|
|
9893
10246
|
}
|
|
9894
10247
|
}
|
|
9895
10248
|
if (!response.ok) {
|
|
9896
|
-
throw new Error(`Wiki API ${method} ${
|
|
10249
|
+
throw new Error(`Wiki API ${method} ${path28}: ${response.status} ${response.statusText}`);
|
|
9897
10250
|
}
|
|
9898
10251
|
return response.json();
|
|
9899
10252
|
} finally {
|
|
@@ -10006,6 +10359,7 @@ var shard_manager_exports = {};
|
|
|
10006
10359
|
__export(shard_manager_exports, {
|
|
10007
10360
|
disposeShards: () => disposeShards,
|
|
10008
10361
|
ensureShardSchema: () => ensureShardSchema,
|
|
10362
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
10009
10363
|
getReadyShardClient: () => getReadyShardClient,
|
|
10010
10364
|
getShardClient: () => getShardClient,
|
|
10011
10365
|
getShardsDir: () => getShardsDir,
|
|
@@ -10014,15 +10368,18 @@ __export(shard_manager_exports, {
|
|
|
10014
10368
|
listShards: () => listShards,
|
|
10015
10369
|
shardExists: () => shardExists
|
|
10016
10370
|
});
|
|
10017
|
-
import
|
|
10018
|
-
import { existsSync as
|
|
10371
|
+
import path27 from "path";
|
|
10372
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
10019
10373
|
import { createClient as createClient2 } from "@libsql/client";
|
|
10020
10374
|
function initShardManager(encryptionKey) {
|
|
10021
10375
|
_encryptionKey = encryptionKey;
|
|
10022
|
-
if (!
|
|
10376
|
+
if (!existsSync17(SHARDS_DIR)) {
|
|
10023
10377
|
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
10024
10378
|
}
|
|
10025
10379
|
_shardingEnabled = true;
|
|
10380
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
10381
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
10382
|
+
_evictionTimer.unref();
|
|
10026
10383
|
}
|
|
10027
10384
|
function isShardingEnabled() {
|
|
10028
10385
|
return _shardingEnabled;
|
|
@@ -10039,21 +10396,28 @@ function getShardClient(projectName) {
|
|
|
10039
10396
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
10040
10397
|
}
|
|
10041
10398
|
const cached = _shards.get(safeName);
|
|
10042
|
-
if (cached)
|
|
10043
|
-
|
|
10399
|
+
if (cached) {
|
|
10400
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
10401
|
+
return cached;
|
|
10402
|
+
}
|
|
10403
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
10404
|
+
evictLRU();
|
|
10405
|
+
}
|
|
10406
|
+
const dbPath = path27.join(SHARDS_DIR, `${safeName}.db`);
|
|
10044
10407
|
const client = createClient2({
|
|
10045
10408
|
url: `file:${dbPath}`,
|
|
10046
10409
|
encryptionKey: _encryptionKey
|
|
10047
10410
|
});
|
|
10048
10411
|
_shards.set(safeName, client);
|
|
10412
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
10049
10413
|
return client;
|
|
10050
10414
|
}
|
|
10051
10415
|
function shardExists(projectName) {
|
|
10052
10416
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
10053
|
-
return
|
|
10417
|
+
return existsSync17(path27.join(SHARDS_DIR, `${safeName}.db`));
|
|
10054
10418
|
}
|
|
10055
10419
|
function listShards() {
|
|
10056
|
-
if (!
|
|
10420
|
+
if (!existsSync17(SHARDS_DIR)) return [];
|
|
10057
10421
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
10058
10422
|
}
|
|
10059
10423
|
async function ensureShardSchema(client) {
|
|
@@ -10105,6 +10469,8 @@ async function ensureShardSchema(client) {
|
|
|
10105
10469
|
for (const col of [
|
|
10106
10470
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
10107
10471
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
10472
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
10473
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
10108
10474
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
10109
10475
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
10110
10476
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -10242,21 +10608,69 @@ async function getReadyShardClient(projectName) {
|
|
|
10242
10608
|
await ensureShardSchema(client);
|
|
10243
10609
|
return client;
|
|
10244
10610
|
}
|
|
10611
|
+
function evictLRU() {
|
|
10612
|
+
let oldest = null;
|
|
10613
|
+
let oldestTime = Infinity;
|
|
10614
|
+
for (const [name, time] of _shardLastAccess) {
|
|
10615
|
+
if (time < oldestTime) {
|
|
10616
|
+
oldestTime = time;
|
|
10617
|
+
oldest = name;
|
|
10618
|
+
}
|
|
10619
|
+
}
|
|
10620
|
+
if (oldest) {
|
|
10621
|
+
const client = _shards.get(oldest);
|
|
10622
|
+
if (client) {
|
|
10623
|
+
client.close();
|
|
10624
|
+
}
|
|
10625
|
+
_shards.delete(oldest);
|
|
10626
|
+
_shardLastAccess.delete(oldest);
|
|
10627
|
+
}
|
|
10628
|
+
}
|
|
10629
|
+
function evictIdleShards() {
|
|
10630
|
+
const now = Date.now();
|
|
10631
|
+
const toEvict = [];
|
|
10632
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
10633
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
10634
|
+
toEvict.push(name);
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
for (const name of toEvict) {
|
|
10638
|
+
const client = _shards.get(name);
|
|
10639
|
+
if (client) {
|
|
10640
|
+
client.close();
|
|
10641
|
+
}
|
|
10642
|
+
_shards.delete(name);
|
|
10643
|
+
_shardLastAccess.delete(name);
|
|
10644
|
+
}
|
|
10645
|
+
}
|
|
10646
|
+
function getOpenShardCount() {
|
|
10647
|
+
return _shards.size;
|
|
10648
|
+
}
|
|
10245
10649
|
function disposeShards() {
|
|
10650
|
+
if (_evictionTimer) {
|
|
10651
|
+
clearInterval(_evictionTimer);
|
|
10652
|
+
_evictionTimer = null;
|
|
10653
|
+
}
|
|
10246
10654
|
for (const [, client] of _shards) {
|
|
10247
10655
|
client.close();
|
|
10248
10656
|
}
|
|
10249
10657
|
_shards.clear();
|
|
10658
|
+
_shardLastAccess.clear();
|
|
10250
10659
|
_shardingEnabled = false;
|
|
10251
10660
|
_encryptionKey = null;
|
|
10252
10661
|
}
|
|
10253
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
10662
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
10254
10663
|
var init_shard_manager = __esm({
|
|
10255
10664
|
"src/lib/shard-manager.ts"() {
|
|
10256
10665
|
"use strict";
|
|
10257
10666
|
init_config();
|
|
10258
|
-
SHARDS_DIR =
|
|
10667
|
+
SHARDS_DIR = path27.join(EXE_AI_DIR, "shards");
|
|
10668
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
10669
|
+
MAX_OPEN_SHARDS = 10;
|
|
10670
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
10259
10671
|
_shards = /* @__PURE__ */ new Map();
|
|
10672
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
10673
|
+
_evictionTimer = null;
|
|
10260
10674
|
_encryptionKey = null;
|
|
10261
10675
|
_shardingEnabled = false;
|
|
10262
10676
|
}
|
|
@@ -14827,8 +15241,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
14827
15241
|
}
|
|
14828
15242
|
|
|
14829
15243
|
// src/tui/ink/components/ErrorOverview.js
|
|
14830
|
-
var cleanupPath = (
|
|
14831
|
-
return
|
|
15244
|
+
var cleanupPath = (path28) => {
|
|
15245
|
+
return path28?.replace(`file://${cwd()}/`, "");
|
|
14832
15246
|
};
|
|
14833
15247
|
var stackUtils = new StackUtils({
|
|
14834
15248
|
cwd: cwd(),
|
|
@@ -16760,11 +17174,11 @@ function Footer() {
|
|
|
16760
17174
|
} catch {
|
|
16761
17175
|
}
|
|
16762
17176
|
try {
|
|
16763
|
-
const { existsSync:
|
|
17177
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
16764
17178
|
const { join } = await import("path");
|
|
16765
17179
|
const home = process.env.HOME ?? "";
|
|
16766
17180
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
16767
|
-
setDaemon(
|
|
17181
|
+
setDaemon(existsSync18(pidPath) ? "running" : "stopped");
|
|
16768
17182
|
} catch {
|
|
16769
17183
|
setDaemon("unknown");
|
|
16770
17184
|
}
|
|
@@ -16846,7 +17260,7 @@ function Footer() {
|
|
|
16846
17260
|
// src/tui/views/CommandCenter.tsx
|
|
16847
17261
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
16848
17262
|
import TextInput from "ink-text-input";
|
|
16849
|
-
import
|
|
17263
|
+
import path24 from "path";
|
|
16850
17264
|
import { homedir } from "os";
|
|
16851
17265
|
|
|
16852
17266
|
// src/tui/components/StatusDot.tsx
|
|
@@ -17633,15 +18047,15 @@ function CommandCenterView({
|
|
|
17633
18047
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
17634
18048
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
17635
18049
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
17636
|
-
const { readFileSync:
|
|
18050
|
+
const { readFileSync: readFileSync14, existsSync: existsSync18 } = await import("fs");
|
|
17637
18051
|
const { join } = await import("path");
|
|
17638
18052
|
const { homedir: homedir3 } = await import("os");
|
|
17639
18053
|
const configPath = join(homedir3(), ".exe-os", "config.json");
|
|
17640
18054
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
17641
18055
|
let providerConfigs = {};
|
|
17642
|
-
if (
|
|
18056
|
+
if (existsSync18(configPath)) {
|
|
17643
18057
|
try {
|
|
17644
|
-
const raw = JSON.parse(
|
|
18058
|
+
const raw = JSON.parse(readFileSync14(configPath, "utf8"));
|
|
17645
18059
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
17646
18060
|
if (raw.providers && typeof raw.providers === "object") {
|
|
17647
18061
|
providerConfigs = raw.providers;
|
|
@@ -17702,7 +18116,7 @@ function CommandCenterView({
|
|
|
17702
18116
|
const markerDir = join(homedir3(), ".exe-os", "session-cache");
|
|
17703
18117
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
17704
18118
|
for (const f of agentFiles) {
|
|
17705
|
-
const data = JSON.parse(
|
|
18119
|
+
const data = JSON.parse(readFileSync14(join(markerDir, f), "utf8"));
|
|
17706
18120
|
if (data.agentRole) {
|
|
17707
18121
|
agentRole = data.agentRole;
|
|
17708
18122
|
break;
|
|
@@ -17847,7 +18261,7 @@ function CommandCenterView({
|
|
|
17847
18261
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
17848
18262
|
projectName: p.projectName,
|
|
17849
18263
|
exeSession: p.exeSession,
|
|
17850
|
-
projectDir:
|
|
18264
|
+
projectDir: path24.join(homedir(), p.projectName),
|
|
17851
18265
|
employeeCount: p.employees.length,
|
|
17852
18266
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
17853
18267
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -17885,7 +18299,7 @@ function CommandCenterView({
|
|
|
17885
18299
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
17886
18300
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
17887
18301
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
17888
|
-
const { existsSync:
|
|
18302
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
17889
18303
|
const { join } = await import("path");
|
|
17890
18304
|
const client = getClient2();
|
|
17891
18305
|
if (!client) {
|
|
@@ -17956,7 +18370,7 @@ function CommandCenterView({
|
|
|
17956
18370
|
}
|
|
17957
18371
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
17958
18372
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
17959
|
-
const hasGit = projectDir ?
|
|
18373
|
+
const hasGit = projectDir ? existsSync18(join(projectDir, ".git")) : false;
|
|
17960
18374
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
17961
18375
|
projectList.push({
|
|
17962
18376
|
projectName: name,
|
|
@@ -17981,7 +18395,7 @@ function CommandCenterView({
|
|
|
17981
18395
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
17982
18396
|
try {
|
|
17983
18397
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
17984
|
-
setHealth((h) => ({ ...h, daemon:
|
|
18398
|
+
setHealth((h) => ({ ...h, daemon: existsSync18(pidPath) ? "running" : "stopped" }));
|
|
17985
18399
|
} catch {
|
|
17986
18400
|
}
|
|
17987
18401
|
const activityResult = await client.execute(
|
|
@@ -18196,7 +18610,7 @@ function ChatMessageRow({ msg }) {
|
|
|
18196
18610
|
|
|
18197
18611
|
// src/tui/views/Sessions.tsx
|
|
18198
18612
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
18199
|
-
import
|
|
18613
|
+
import path25 from "path";
|
|
18200
18614
|
import { homedir as homedir2 } from "os";
|
|
18201
18615
|
|
|
18202
18616
|
// src/tui/components/TmuxPane.tsx
|
|
@@ -18500,7 +18914,7 @@ function SessionsView({
|
|
|
18500
18914
|
if (demo) {
|
|
18501
18915
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
18502
18916
|
...p,
|
|
18503
|
-
projectDir:
|
|
18917
|
+
projectDir: path25.join(homedir2(), p.projectName),
|
|
18504
18918
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
18505
18919
|
})));
|
|
18506
18920
|
return;
|
|
@@ -18896,7 +19310,6 @@ function SessionsView({
|
|
|
18896
19310
|
|
|
18897
19311
|
// src/tui/views/Tasks.tsx
|
|
18898
19312
|
import React20, { useState as useState10, useEffect as useEffect12, useMemo as useMemo5 } from "react";
|
|
18899
|
-
init_task_scope();
|
|
18900
19313
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
18901
19314
|
var STATUS_FILTERS = ["all", "open", "in_progress", "done", "blocked", "needs_review"];
|
|
18902
19315
|
var PRIORITY_COLORS = {
|
|
@@ -19029,37 +19442,22 @@ function TasksView({ onBack }) {
|
|
|
19029
19442
|
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
19030
19443
|
return withTrace2("tui.tasks.loadTasks", async () => {
|
|
19031
19444
|
try {
|
|
19032
|
-
const {
|
|
19033
|
-
const
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
})
|
|
19046
|
-
|
|
19047
|
-
|
|
19048
|
-
id: String(r.id ?? ""),
|
|
19049
|
-
priority: String(r.priority ?? "p2").toUpperCase(),
|
|
19050
|
-
title: String(r.title ?? ""),
|
|
19051
|
-
assignee: String(r.assigned_to ?? ""),
|
|
19052
|
-
assignedBy: String(r.assigned_by ?? ""),
|
|
19053
|
-
status: String(r.status ?? "open"),
|
|
19054
|
-
project: String(r.project_name ?? ""),
|
|
19055
|
-
createdAt: String(r.created_at ?? ""),
|
|
19056
|
-
result: String(r.result ?? "")
|
|
19057
|
-
}))
|
|
19058
|
-
);
|
|
19059
|
-
setDbError(null);
|
|
19060
|
-
} else {
|
|
19061
|
-
setDbError("Database client not initialized. Run exe-os setup.");
|
|
19062
|
-
}
|
|
19445
|
+
const { loadTaskList: loadTaskList2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
19446
|
+
const tasks = await loadTaskList2();
|
|
19447
|
+
setAllTasks(
|
|
19448
|
+
tasks.map((task) => ({
|
|
19449
|
+
id: task.id,
|
|
19450
|
+
priority: task.priority,
|
|
19451
|
+
title: task.title,
|
|
19452
|
+
assignee: task.assignedTo,
|
|
19453
|
+
assignedBy: task.assignedBy,
|
|
19454
|
+
status: task.status,
|
|
19455
|
+
project: task.projectName,
|
|
19456
|
+
createdAt: task.createdAt,
|
|
19457
|
+
result: task.result
|
|
19458
|
+
}))
|
|
19459
|
+
);
|
|
19460
|
+
setDbError(null);
|
|
19063
19461
|
} catch (err) {
|
|
19064
19462
|
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
19065
19463
|
} finally {
|
|
@@ -19397,12 +19795,12 @@ async function loadGatewayConfig() {
|
|
|
19397
19795
|
state.running = false;
|
|
19398
19796
|
}
|
|
19399
19797
|
try {
|
|
19400
|
-
const { existsSync:
|
|
19798
|
+
const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
|
|
19401
19799
|
const { join } = await import("path");
|
|
19402
19800
|
const home = process.env.HOME ?? "";
|
|
19403
19801
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
19404
|
-
if (
|
|
19405
|
-
const raw = JSON.parse(
|
|
19802
|
+
if (existsSync18(configPath)) {
|
|
19803
|
+
const raw = JSON.parse(readFileSync14(configPath, "utf8"));
|
|
19406
19804
|
state.port = raw.port ?? 3100;
|
|
19407
19805
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
19408
19806
|
if (raw.adapters) {
|
|
@@ -19802,7 +20200,6 @@ function getAgentStatus(agentId) {
|
|
|
19802
20200
|
}
|
|
19803
20201
|
|
|
19804
20202
|
// src/tui/views/Team.tsx
|
|
19805
|
-
init_task_scope();
|
|
19806
20203
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
19807
20204
|
var DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
|
|
19808
20205
|
function roleBadgeColor(role) {
|
|
@@ -19875,41 +20272,16 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
19875
20272
|
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
19876
20273
|
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
19877
20274
|
try {
|
|
19878
|
-
const {
|
|
19879
|
-
const
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
sql: `SELECT DISTINCT project_name,
|
|
19889
|
-
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
19890
|
-
FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')${tmScope.sql}
|
|
19891
|
-
GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
|
|
19892
|
-
args: [emp.name, ...tmScope.args]
|
|
19893
|
-
});
|
|
19894
|
-
const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
|
|
19895
|
-
const urgency = Number(r.urgency);
|
|
19896
|
-
let pStatus = "idle";
|
|
19897
|
-
if (urgency === 1) pStatus = "active";
|
|
19898
|
-
else if (urgency === 2) pStatus = "has_tasks";
|
|
19899
|
-
return { name: String(r.project_name), status: pStatus };
|
|
19900
|
-
});
|
|
19901
|
-
if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
|
|
19902
|
-
}
|
|
19903
|
-
for (const emp of roster) {
|
|
19904
|
-
const taskResult = await client.execute({
|
|
19905
|
-
sql: `SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress'${tmScope.sql} ORDER BY updated_at DESC LIMIT 1`,
|
|
19906
|
-
args: [emp.name, ...tmScope.args]
|
|
19907
|
-
});
|
|
19908
|
-
if (taskResult.rows.length > 0) {
|
|
19909
|
-
currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
|
|
19910
|
-
}
|
|
19911
|
-
}
|
|
19912
|
-
}
|
|
20275
|
+
const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
20276
|
+
const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
|
|
20277
|
+
memoryCounts = teamMetrics.memoryCounts;
|
|
20278
|
+
projectsByEmployee = new Map(
|
|
20279
|
+
Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
|
|
20280
|
+
name,
|
|
20281
|
+
projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
|
|
20282
|
+
])
|
|
20283
|
+
);
|
|
20284
|
+
currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
|
|
19913
20285
|
} catch {
|
|
19914
20286
|
}
|
|
19915
20287
|
const teamData = roster.map((emp) => {
|
|
@@ -19928,12 +20300,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
19928
20300
|
setMembers(teamData);
|
|
19929
20301
|
setDbError(null);
|
|
19930
20302
|
try {
|
|
19931
|
-
const { existsSync:
|
|
20303
|
+
const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
|
|
19932
20304
|
const { join } = await import("path");
|
|
19933
20305
|
const home = process.env.HOME ?? "";
|
|
19934
20306
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
19935
|
-
if (
|
|
19936
|
-
const raw = JSON.parse(
|
|
20307
|
+
if (existsSync18(gatewayConfig)) {
|
|
20308
|
+
const raw = JSON.parse(readFileSync14(gatewayConfig, "utf8"));
|
|
19937
20309
|
if (raw.agents && raw.agents.length > 0) {
|
|
19938
20310
|
setExternals(raw.agents.map((a) => ({
|
|
19939
20311
|
name: a.name,
|
|
@@ -20247,24 +20619,8 @@ function WikiView({ onBack }) {
|
|
|
20247
20619
|
}
|
|
20248
20620
|
setSearchLoading(true);
|
|
20249
20621
|
try {
|
|
20250
|
-
const {
|
|
20251
|
-
|
|
20252
|
-
const result = await client.execute({
|
|
20253
|
-
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
20254
|
-
FROM memories
|
|
20255
|
-
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
20256
|
-
ORDER BY timestamp DESC LIMIT 20`,
|
|
20257
|
-
args: [`%${query}%`]
|
|
20258
|
-
});
|
|
20259
|
-
setSearchResults(
|
|
20260
|
-
result.rows.map((r) => ({
|
|
20261
|
-
id: String(r.id),
|
|
20262
|
-
agentId: String(r.agent_id),
|
|
20263
|
-
rawText: String(r.raw_text),
|
|
20264
|
-
timestamp: String(r.timestamp),
|
|
20265
|
-
projectName: String(r.project_name ?? "")
|
|
20266
|
-
}))
|
|
20267
|
-
);
|
|
20622
|
+
const { searchWikiMemoryRows: searchWikiMemoryRows2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
20623
|
+
setSearchResults(await searchWikiMemoryRows2(query));
|
|
20268
20624
|
} catch {
|
|
20269
20625
|
setSearchResults([]);
|
|
20270
20626
|
} finally {
|
|
@@ -20597,12 +20953,12 @@ function SettingsView({ onBack }) {
|
|
|
20597
20953
|
}
|
|
20598
20954
|
setProviders(providerList);
|
|
20599
20955
|
try {
|
|
20600
|
-
const { existsSync:
|
|
20956
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
20601
20957
|
const { join } = await import("path");
|
|
20602
20958
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
20603
20959
|
const cfg = await loadConfig2();
|
|
20604
20960
|
const home = process.env.HOME ?? "";
|
|
20605
|
-
const hasKey =
|
|
20961
|
+
const hasKey = existsSync18(join(home, ".exe-os", "master.key"));
|
|
20606
20962
|
if (cfg.cloud) {
|
|
20607
20963
|
setCloud({
|
|
20608
20964
|
configured: true,
|
|
@@ -20615,22 +20971,22 @@ function SettingsView({ onBack }) {
|
|
|
20615
20971
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
20616
20972
|
let daemon = "unknown";
|
|
20617
20973
|
try {
|
|
20618
|
-
daemon =
|
|
20974
|
+
daemon = existsSync18(pidPath) ? "running" : "stopped";
|
|
20619
20975
|
} catch {
|
|
20620
20976
|
}
|
|
20621
20977
|
let version = "unknown";
|
|
20622
20978
|
try {
|
|
20623
|
-
const { readFileSync:
|
|
20624
|
-
const { createRequire:
|
|
20625
|
-
const require2 =
|
|
20979
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
20980
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
20981
|
+
const require2 = createRequire3(import.meta.url);
|
|
20626
20982
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
20627
|
-
const pkg = JSON.parse(
|
|
20983
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
20628
20984
|
version = pkg.version;
|
|
20629
20985
|
} catch {
|
|
20630
20986
|
try {
|
|
20631
|
-
const { readFileSync:
|
|
20987
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
20632
20988
|
const { join: joinPath } = await import("path");
|
|
20633
|
-
const pkg = JSON.parse(
|
|
20989
|
+
const pkg = JSON.parse(readFileSync14(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
20634
20990
|
version = pkg.version;
|
|
20635
20991
|
} catch {
|
|
20636
20992
|
}
|