@askexenow/exe-os 0.9.8 → 0.9.9
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 +1295 -856
- 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 +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- 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 +677 -388
- 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 +440 -250
- 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 +404 -212
- 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 +412 -220
- 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 +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -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 +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- 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 +538 -324
- 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 +935 -587
- 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 +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- 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 +306 -248
- 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 +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
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(),
|
|
@@ -3861,12 +4057,12 @@ __export(tasks_crud_exports, {
|
|
|
3861
4057
|
updateTaskStatus: () => updateTaskStatus,
|
|
3862
4058
|
writeCheckpoint: () => writeCheckpoint
|
|
3863
4059
|
});
|
|
3864
|
-
import
|
|
3865
|
-
import
|
|
3866
|
-
import
|
|
4060
|
+
import crypto4 from "crypto";
|
|
4061
|
+
import path12 from "path";
|
|
4062
|
+
import os9 from "os";
|
|
3867
4063
|
import { execSync as execSync5 } from "child_process";
|
|
3868
4064
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3869
|
-
import { existsSync as
|
|
4065
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
3870
4066
|
async function writeCheckpoint(input) {
|
|
3871
4067
|
const client = getClient();
|
|
3872
4068
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3982,7 +4178,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3982
4178
|
}
|
|
3983
4179
|
async function createTaskCore(input) {
|
|
3984
4180
|
const client = getClient();
|
|
3985
|
-
const id =
|
|
4181
|
+
const id = crypto4.randomUUID();
|
|
3986
4182
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3987
4183
|
const slug = slugify(input.title);
|
|
3988
4184
|
let earlySessionScope = null;
|
|
@@ -4041,8 +4237,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4041
4237
|
}
|
|
4042
4238
|
if (input.baseDir) {
|
|
4043
4239
|
try {
|
|
4044
|
-
await mkdir3(
|
|
4045
|
-
await mkdir3(
|
|
4240
|
+
await mkdir3(path12.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4241
|
+
await mkdir3(path12.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4046
4242
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4047
4243
|
await ensureGitignoreExe(input.baseDir);
|
|
4048
4244
|
} catch {
|
|
@@ -4078,13 +4274,19 @@ ${laneWarning}` : laneWarning;
|
|
|
4078
4274
|
});
|
|
4079
4275
|
if (input.baseDir) {
|
|
4080
4276
|
try {
|
|
4081
|
-
const EXE_OS_DIR =
|
|
4082
|
-
const mdPath =
|
|
4083
|
-
const mdDir =
|
|
4084
|
-
if (!
|
|
4277
|
+
const EXE_OS_DIR = path12.join(os9.homedir(), ".exe-os");
|
|
4278
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
4279
|
+
const mdDir = path12.dirname(mdPath);
|
|
4280
|
+
if (!existsSync13(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
4085
4281
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4086
4282
|
const mdContent = `# ${input.title}
|
|
4087
4283
|
|
|
4284
|
+
## MANDATORY: When done
|
|
4285
|
+
|
|
4286
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4287
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4288
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4289
|
+
|
|
4088
4290
|
**ID:** ${id}
|
|
4089
4291
|
**Status:** ${initialStatus}
|
|
4090
4292
|
**Priority:** ${input.priority}
|
|
@@ -4098,12 +4300,6 @@ ${laneWarning}` : laneWarning;
|
|
|
4098
4300
|
## Context
|
|
4099
4301
|
|
|
4100
4302
|
${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
4303
|
`;
|
|
4108
4304
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
4109
4305
|
} catch (err) {
|
|
@@ -4352,7 +4548,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4352
4548
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4353
4549
|
} catch {
|
|
4354
4550
|
}
|
|
4355
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
4551
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4356
4552
|
try {
|
|
4357
4553
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4358
4554
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -4381,9 +4577,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4381
4577
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4382
4578
|
}
|
|
4383
4579
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4384
|
-
const archPath =
|
|
4580
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4385
4581
|
try {
|
|
4386
|
-
if (
|
|
4582
|
+
if (existsSync13(archPath)) return;
|
|
4387
4583
|
const template = [
|
|
4388
4584
|
`# ${projectName} \u2014 System Architecture`,
|
|
4389
4585
|
"",
|
|
@@ -4416,10 +4612,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4416
4612
|
}
|
|
4417
4613
|
}
|
|
4418
4614
|
async function ensureGitignoreExe(baseDir) {
|
|
4419
|
-
const gitignorePath =
|
|
4615
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
4420
4616
|
try {
|
|
4421
|
-
if (
|
|
4422
|
-
const content =
|
|
4617
|
+
if (existsSync13(gitignorePath)) {
|
|
4618
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
4423
4619
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4424
4620
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4425
4621
|
} else {
|
|
@@ -4450,58 +4646,42 @@ var init_tasks_crud = __esm({
|
|
|
4450
4646
|
});
|
|
4451
4647
|
|
|
4452
4648
|
// src/lib/tasks-review.ts
|
|
4453
|
-
import
|
|
4454
|
-
import { existsSync as
|
|
4649
|
+
import path13 from "path";
|
|
4650
|
+
import { existsSync as existsSync14, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
4455
4651
|
async function countPendingReviews(sessionScope) {
|
|
4456
4652
|
const client = getClient();
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
args: [sessionScope]
|
|
4461
|
-
});
|
|
4462
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
4463
|
-
}
|
|
4653
|
+
const scope = strictSessionScopeFilter(
|
|
4654
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4655
|
+
);
|
|
4464
4656
|
const result = await client.execute({
|
|
4465
|
-
sql:
|
|
4466
|
-
|
|
4657
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4658
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
4659
|
+
args: [...scope.args]
|
|
4467
4660
|
});
|
|
4468
4661
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4469
4662
|
}
|
|
4470
4663
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
4471
4664
|
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
|
-
}
|
|
4665
|
+
const scope = strictSessionScopeFilter(
|
|
4666
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4667
|
+
);
|
|
4481
4668
|
const result = await client.execute({
|
|
4482
4669
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4483
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
4484
|
-
args: [sinceIso]
|
|
4670
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
4671
|
+
args: [sinceIso, ...scope.args]
|
|
4485
4672
|
});
|
|
4486
4673
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4487
4674
|
}
|
|
4488
4675
|
async function listPendingReviews(limit, sessionScope) {
|
|
4489
4676
|
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
|
-
}
|
|
4677
|
+
const scope = strictSessionScopeFilter(
|
|
4678
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4679
|
+
);
|
|
4500
4680
|
const result = await client.execute({
|
|
4501
4681
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
4502
|
-
WHERE status = 'needs_review'
|
|
4682
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
4503
4683
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
4504
|
-
args: [limit]
|
|
4684
|
+
args: [...scope.args, limit]
|
|
4505
4685
|
});
|
|
4506
4686
|
return result.rows;
|
|
4507
4687
|
}
|
|
@@ -4513,7 +4693,7 @@ async function cleanupOrphanedReviews() {
|
|
|
4513
4693
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
4514
4694
|
AND assigned_by = 'system'
|
|
4515
4695
|
AND title LIKE 'Review:%'
|
|
4516
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
4696
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
4517
4697
|
args: [now]
|
|
4518
4698
|
});
|
|
4519
4699
|
const r1b = await client.execute({
|
|
@@ -4632,11 +4812,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4632
4812
|
);
|
|
4633
4813
|
}
|
|
4634
4814
|
try {
|
|
4635
|
-
const cacheDir =
|
|
4636
|
-
if (
|
|
4815
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
4816
|
+
if (existsSync14(cacheDir)) {
|
|
4637
4817
|
for (const f of readdirSync2(cacheDir)) {
|
|
4638
4818
|
if (f.startsWith("review-notified-")) {
|
|
4639
|
-
unlinkSync4(
|
|
4819
|
+
unlinkSync4(path13.join(cacheDir, f));
|
|
4640
4820
|
}
|
|
4641
4821
|
}
|
|
4642
4822
|
}
|
|
@@ -4653,11 +4833,12 @@ var init_tasks_review = __esm({
|
|
|
4653
4833
|
init_tmux_routing();
|
|
4654
4834
|
init_session_key();
|
|
4655
4835
|
init_state_bus();
|
|
4836
|
+
init_task_scope();
|
|
4656
4837
|
}
|
|
4657
4838
|
});
|
|
4658
4839
|
|
|
4659
4840
|
// src/lib/tasks-chain.ts
|
|
4660
|
-
import
|
|
4841
|
+
import path14 from "path";
|
|
4661
4842
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
4662
4843
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4663
4844
|
const client = getClient();
|
|
@@ -4674,7 +4855,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4674
4855
|
});
|
|
4675
4856
|
for (const ur of unblockedRows.rows) {
|
|
4676
4857
|
try {
|
|
4677
|
-
const ubFile =
|
|
4858
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
4678
4859
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
4679
4860
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4680
4861
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4709,7 +4890,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
4709
4890
|
const scScope = sessionScopeFilter();
|
|
4710
4891
|
const remaining = await client.execute({
|
|
4711
4892
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4712
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
4893
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
4713
4894
|
args: [parentTaskId, ...scScope.args]
|
|
4714
4895
|
});
|
|
4715
4896
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -4743,7 +4924,7 @@ var init_tasks_chain = __esm({
|
|
|
4743
4924
|
|
|
4744
4925
|
// src/lib/project-name.ts
|
|
4745
4926
|
import { execSync as execSync6 } from "child_process";
|
|
4746
|
-
import
|
|
4927
|
+
import path15 from "path";
|
|
4747
4928
|
function getProjectName(cwd2) {
|
|
4748
4929
|
const dir = cwd2 ?? process.cwd();
|
|
4749
4930
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4756,7 +4937,7 @@ function getProjectName(cwd2) {
|
|
|
4756
4937
|
timeout: 2e3,
|
|
4757
4938
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4758
4939
|
}).trim();
|
|
4759
|
-
repoRoot =
|
|
4940
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
4760
4941
|
} catch {
|
|
4761
4942
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4762
4943
|
cwd: dir,
|
|
@@ -4765,11 +4946,11 @@ function getProjectName(cwd2) {
|
|
|
4765
4946
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4766
4947
|
}).trim();
|
|
4767
4948
|
}
|
|
4768
|
-
_cached2 =
|
|
4949
|
+
_cached2 = path15.basename(repoRoot);
|
|
4769
4950
|
_cachedCwd = dir;
|
|
4770
4951
|
return _cached2;
|
|
4771
4952
|
} catch {
|
|
4772
|
-
_cached2 =
|
|
4953
|
+
_cached2 = path15.basename(dir);
|
|
4773
4954
|
_cachedCwd = dir;
|
|
4774
4955
|
return _cached2;
|
|
4775
4956
|
}
|
|
@@ -4912,10 +5093,10 @@ var init_tasks_notify = __esm({
|
|
|
4912
5093
|
});
|
|
4913
5094
|
|
|
4914
5095
|
// src/lib/behaviors.ts
|
|
4915
|
-
import
|
|
5096
|
+
import crypto5 from "crypto";
|
|
4916
5097
|
async function storeBehavior(opts) {
|
|
4917
5098
|
const client = getClient();
|
|
4918
|
-
const id =
|
|
5099
|
+
const id = crypto5.randomUUID();
|
|
4919
5100
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4920
5101
|
await client.execute({
|
|
4921
5102
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -4944,7 +5125,7 @@ __export(skill_learning_exports, {
|
|
|
4944
5125
|
storeTrajectory: () => storeTrajectory,
|
|
4945
5126
|
sweepTrajectories: () => sweepTrajectories
|
|
4946
5127
|
});
|
|
4947
|
-
import
|
|
5128
|
+
import crypto6 from "crypto";
|
|
4948
5129
|
async function extractTrajectory(taskId, agentId) {
|
|
4949
5130
|
const client = getClient();
|
|
4950
5131
|
const result = await client.execute({
|
|
@@ -4973,11 +5154,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
4973
5154
|
return signature;
|
|
4974
5155
|
}
|
|
4975
5156
|
function hashSignature(signature) {
|
|
4976
|
-
return
|
|
5157
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
4977
5158
|
}
|
|
4978
5159
|
async function storeTrajectory(opts) {
|
|
4979
5160
|
const client = getClient();
|
|
4980
|
-
const id =
|
|
5161
|
+
const id = crypto6.randomUUID();
|
|
4981
5162
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4982
5163
|
const signatureHash = hashSignature(opts.signature);
|
|
4983
5164
|
await client.execute({
|
|
@@ -5242,8 +5423,8 @@ __export(tasks_exports, {
|
|
|
5242
5423
|
updateTaskStatus: () => updateTaskStatus,
|
|
5243
5424
|
writeCheckpoint: () => writeCheckpoint
|
|
5244
5425
|
});
|
|
5245
|
-
import
|
|
5246
|
-
import { writeFileSync as
|
|
5426
|
+
import path16 from "path";
|
|
5427
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
5247
5428
|
async function createTask(input) {
|
|
5248
5429
|
const result = await createTaskCore(input);
|
|
5249
5430
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5262,12 +5443,12 @@ async function updateTask(input) {
|
|
|
5262
5443
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5263
5444
|
try {
|
|
5264
5445
|
const agent = String(row.assigned_to);
|
|
5265
|
-
const cacheDir =
|
|
5266
|
-
const cachePath =
|
|
5446
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5447
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
5267
5448
|
if (input.status === "in_progress") {
|
|
5268
5449
|
mkdirSync5(cacheDir, { recursive: true });
|
|
5269
|
-
|
|
5270
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5450
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5451
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5271
5452
|
try {
|
|
5272
5453
|
unlinkSync5(cachePath);
|
|
5273
5454
|
} catch {
|
|
@@ -5275,10 +5456,10 @@ async function updateTask(input) {
|
|
|
5275
5456
|
}
|
|
5276
5457
|
} catch {
|
|
5277
5458
|
}
|
|
5278
|
-
if (input.status === "done") {
|
|
5459
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5279
5460
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5280
5461
|
}
|
|
5281
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5462
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5282
5463
|
try {
|
|
5283
5464
|
const client = getClient();
|
|
5284
5465
|
const taskTitle = String(row.title);
|
|
@@ -5294,7 +5475,7 @@ async function updateTask(input) {
|
|
|
5294
5475
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5295
5476
|
try {
|
|
5296
5477
|
const draftClient = getClient();
|
|
5297
|
-
if (input.status === "done") {
|
|
5478
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5298
5479
|
await draftClient.execute({
|
|
5299
5480
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
5300
5481
|
args: [assignedAgent]
|
|
@@ -5311,7 +5492,7 @@ async function updateTask(input) {
|
|
|
5311
5492
|
try {
|
|
5312
5493
|
const client = getClient();
|
|
5313
5494
|
const cascaded = await client.execute({
|
|
5314
|
-
sql: `UPDATE tasks SET status = '
|
|
5495
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
5315
5496
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
5316
5497
|
args: [now, taskId]
|
|
5317
5498
|
});
|
|
@@ -5324,14 +5505,14 @@ async function updateTask(input) {
|
|
|
5324
5505
|
} catch {
|
|
5325
5506
|
}
|
|
5326
5507
|
}
|
|
5327
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
5508
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
5328
5509
|
if (isTerminal) {
|
|
5329
5510
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
5330
5511
|
if (!isCoordinator) {
|
|
5331
5512
|
notifyTaskDone();
|
|
5332
5513
|
}
|
|
5333
5514
|
await markTaskNotificationsRead(taskFile);
|
|
5334
|
-
if (input.status === "done") {
|
|
5515
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5335
5516
|
try {
|
|
5336
5517
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5337
5518
|
} catch {
|
|
@@ -5351,7 +5532,7 @@ async function updateTask(input) {
|
|
|
5351
5532
|
}
|
|
5352
5533
|
}
|
|
5353
5534
|
}
|
|
5354
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5535
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5355
5536
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
5356
5537
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
5357
5538
|
taskId,
|
|
@@ -5723,6 +5904,7 @@ __export(tmux_routing_exports, {
|
|
|
5723
5904
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
5724
5905
|
isExeSession: () => isExeSession,
|
|
5725
5906
|
isSessionBusy: () => isSessionBusy,
|
|
5907
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
5726
5908
|
notifyParentExe: () => notifyParentExe,
|
|
5727
5909
|
parseParentExe: () => parseParentExe,
|
|
5728
5910
|
registerParentExe: () => registerParentExe,
|
|
@@ -5733,13 +5915,13 @@ __export(tmux_routing_exports, {
|
|
|
5733
5915
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5734
5916
|
});
|
|
5735
5917
|
import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
|
|
5736
|
-
import { readFileSync as
|
|
5737
|
-
import
|
|
5738
|
-
import
|
|
5918
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5919
|
+
import path17 from "path";
|
|
5920
|
+
import os10 from "os";
|
|
5739
5921
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5740
5922
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5741
5923
|
function spawnLockPath(sessionName) {
|
|
5742
|
-
return
|
|
5924
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5743
5925
|
}
|
|
5744
5926
|
function isProcessAlive(pid) {
|
|
5745
5927
|
try {
|
|
@@ -5750,13 +5932,13 @@ function isProcessAlive(pid) {
|
|
|
5750
5932
|
}
|
|
5751
5933
|
}
|
|
5752
5934
|
function acquireSpawnLock2(sessionName) {
|
|
5753
|
-
if (!
|
|
5935
|
+
if (!existsSync15(SPAWN_LOCK_DIR)) {
|
|
5754
5936
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
5755
5937
|
}
|
|
5756
5938
|
const lockFile = spawnLockPath(sessionName);
|
|
5757
|
-
if (
|
|
5939
|
+
if (existsSync15(lockFile)) {
|
|
5758
5940
|
try {
|
|
5759
|
-
const lock = JSON.parse(
|
|
5941
|
+
const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
|
|
5760
5942
|
const age = Date.now() - lock.timestamp;
|
|
5761
5943
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5762
5944
|
return false;
|
|
@@ -5764,7 +5946,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5764
5946
|
} catch {
|
|
5765
5947
|
}
|
|
5766
5948
|
}
|
|
5767
|
-
|
|
5949
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5768
5950
|
return true;
|
|
5769
5951
|
}
|
|
5770
5952
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5776,13 +5958,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5776
5958
|
function resolveBehaviorsExporterScript() {
|
|
5777
5959
|
try {
|
|
5778
5960
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5779
|
-
const scriptPath =
|
|
5780
|
-
|
|
5961
|
+
const scriptPath = path17.join(
|
|
5962
|
+
path17.dirname(thisFile),
|
|
5781
5963
|
"..",
|
|
5782
5964
|
"bin",
|
|
5783
5965
|
"exe-export-behaviors.js"
|
|
5784
5966
|
);
|
|
5785
|
-
return
|
|
5967
|
+
return existsSync15(scriptPath) ? scriptPath : null;
|
|
5786
5968
|
} catch {
|
|
5787
5969
|
return null;
|
|
5788
5970
|
}
|
|
@@ -5848,12 +6030,12 @@ function extractRootExe(name) {
|
|
|
5848
6030
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5849
6031
|
}
|
|
5850
6032
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5851
|
-
if (!
|
|
6033
|
+
if (!existsSync15(SESSION_CACHE)) {
|
|
5852
6034
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5853
6035
|
}
|
|
5854
6036
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5855
|
-
const filePath =
|
|
5856
|
-
|
|
6037
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6038
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5857
6039
|
parentExe: rootExe,
|
|
5858
6040
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5859
6041
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5861,7 +6043,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5861
6043
|
}
|
|
5862
6044
|
function getParentExe(sessionKey) {
|
|
5863
6045
|
try {
|
|
5864
|
-
const data = JSON.parse(
|
|
6046
|
+
const data = JSON.parse(readFileSync13(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5865
6047
|
return data.parentExe || null;
|
|
5866
6048
|
} catch {
|
|
5867
6049
|
return null;
|
|
@@ -5869,8 +6051,8 @@ function getParentExe(sessionKey) {
|
|
|
5869
6051
|
}
|
|
5870
6052
|
function getDispatchedBy(sessionKey) {
|
|
5871
6053
|
try {
|
|
5872
|
-
const data = JSON.parse(
|
|
5873
|
-
|
|
6054
|
+
const data = JSON.parse(readFileSync13(
|
|
6055
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5874
6056
|
"utf8"
|
|
5875
6057
|
));
|
|
5876
6058
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5940,8 +6122,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5940
6122
|
}
|
|
5941
6123
|
function readDebounceState() {
|
|
5942
6124
|
try {
|
|
5943
|
-
if (!
|
|
5944
|
-
const raw = JSON.parse(
|
|
6125
|
+
if (!existsSync15(DEBOUNCE_FILE)) return {};
|
|
6126
|
+
const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
|
|
5945
6127
|
const state = {};
|
|
5946
6128
|
for (const [key, val] of Object.entries(raw)) {
|
|
5947
6129
|
if (typeof val === "number") {
|
|
@@ -5957,8 +6139,8 @@ function readDebounceState() {
|
|
|
5957
6139
|
}
|
|
5958
6140
|
function writeDebounceState(state) {
|
|
5959
6141
|
try {
|
|
5960
|
-
if (!
|
|
5961
|
-
|
|
6142
|
+
if (!existsSync15(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
6143
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5962
6144
|
} catch {
|
|
5963
6145
|
}
|
|
5964
6146
|
}
|
|
@@ -6056,8 +6238,8 @@ function sendIntercom(targetSession) {
|
|
|
6056
6238
|
try {
|
|
6057
6239
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6058
6240
|
const agent = baseAgentName(rawAgent);
|
|
6059
|
-
const markerPath =
|
|
6060
|
-
if (
|
|
6241
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6242
|
+
if (existsSync15(markerPath)) {
|
|
6061
6243
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6062
6244
|
return "debounced";
|
|
6063
6245
|
}
|
|
@@ -6066,8 +6248,8 @@ function sendIntercom(targetSession) {
|
|
|
6066
6248
|
try {
|
|
6067
6249
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6068
6250
|
const agent = baseAgentName(rawAgent);
|
|
6069
|
-
const taskDir =
|
|
6070
|
-
if (
|
|
6251
|
+
const taskDir = path17.join(process.cwd(), "exe", agent);
|
|
6252
|
+
if (existsSync15(taskDir)) {
|
|
6071
6253
|
const files = readdirSync3(taskDir).filter(
|
|
6072
6254
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
6073
6255
|
);
|
|
@@ -6127,6 +6309,21 @@ function notifyParentExe(sessionKey) {
|
|
|
6127
6309
|
}
|
|
6128
6310
|
return true;
|
|
6129
6311
|
}
|
|
6312
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
6313
|
+
const transport = getTransport();
|
|
6314
|
+
try {
|
|
6315
|
+
const sessions = transport.listSessions();
|
|
6316
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
6317
|
+
execSync7(
|
|
6318
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6319
|
+
{ timeout: 3e3 }
|
|
6320
|
+
);
|
|
6321
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
6322
|
+
return true;
|
|
6323
|
+
} catch {
|
|
6324
|
+
return false;
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6130
6327
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
6131
6328
|
if (isCoordinatorName(employeeName)) {
|
|
6132
6329
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6200,26 +6397,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6200
6397
|
const transport = getTransport();
|
|
6201
6398
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6202
6399
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6203
|
-
const logDir =
|
|
6204
|
-
const logFile =
|
|
6205
|
-
if (!
|
|
6400
|
+
const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
|
|
6401
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6402
|
+
if (!existsSync15(logDir)) {
|
|
6206
6403
|
mkdirSync6(logDir, { recursive: true });
|
|
6207
6404
|
}
|
|
6208
6405
|
transport.kill(sessionName);
|
|
6209
6406
|
let cleanupSuffix = "";
|
|
6210
6407
|
try {
|
|
6211
6408
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6212
|
-
const cleanupScript =
|
|
6213
|
-
if (
|
|
6409
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6410
|
+
if (existsSync15(cleanupScript)) {
|
|
6214
6411
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6215
6412
|
}
|
|
6216
6413
|
} catch {
|
|
6217
6414
|
}
|
|
6218
6415
|
try {
|
|
6219
|
-
const claudeJsonPath =
|
|
6416
|
+
const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
|
|
6220
6417
|
let claudeJson = {};
|
|
6221
6418
|
try {
|
|
6222
|
-
claudeJson = JSON.parse(
|
|
6419
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
6223
6420
|
} catch {
|
|
6224
6421
|
}
|
|
6225
6422
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6227,17 +6424,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6227
6424
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6228
6425
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6229
6426
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6230
|
-
|
|
6427
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6231
6428
|
} catch {
|
|
6232
6429
|
}
|
|
6233
6430
|
try {
|
|
6234
|
-
const settingsDir =
|
|
6431
|
+
const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
|
|
6235
6432
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6236
|
-
const projSettingsDir =
|
|
6237
|
-
const settingsPath =
|
|
6433
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6434
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
6238
6435
|
let settings = {};
|
|
6239
6436
|
try {
|
|
6240
|
-
settings = JSON.parse(
|
|
6437
|
+
settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
|
|
6241
6438
|
} catch {
|
|
6242
6439
|
}
|
|
6243
6440
|
const perms = settings.permissions ?? {};
|
|
@@ -6266,7 +6463,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6266
6463
|
perms.allow = allow;
|
|
6267
6464
|
settings.permissions = perms;
|
|
6268
6465
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
6269
|
-
|
|
6466
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6270
6467
|
}
|
|
6271
6468
|
} catch {
|
|
6272
6469
|
}
|
|
@@ -6281,8 +6478,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6281
6478
|
let behaviorsFlag = "";
|
|
6282
6479
|
let legacyFallbackWarned = false;
|
|
6283
6480
|
if (!useExeAgent && !useBinSymlink) {
|
|
6284
|
-
const identityPath =
|
|
6285
|
-
|
|
6481
|
+
const identityPath = path17.join(
|
|
6482
|
+
os10.homedir(),
|
|
6286
6483
|
".exe-os",
|
|
6287
6484
|
"identity",
|
|
6288
6485
|
`${employeeName}.md`
|
|
@@ -6291,13 +6488,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6291
6488
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6292
6489
|
if (hasAgentFlag) {
|
|
6293
6490
|
identityFlag = ` --agent ${employeeName}`;
|
|
6294
|
-
} else if (
|
|
6491
|
+
} else if (existsSync15(identityPath)) {
|
|
6295
6492
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6296
6493
|
legacyFallbackWarned = true;
|
|
6297
6494
|
}
|
|
6298
6495
|
const behaviorsFile = exportBehaviorsSync(
|
|
6299
6496
|
employeeName,
|
|
6300
|
-
|
|
6497
|
+
path17.basename(spawnCwd),
|
|
6301
6498
|
sessionName
|
|
6302
6499
|
);
|
|
6303
6500
|
if (behaviorsFile) {
|
|
@@ -6312,16 +6509,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6312
6509
|
}
|
|
6313
6510
|
let sessionContextFlag = "";
|
|
6314
6511
|
try {
|
|
6315
|
-
const ctxDir =
|
|
6512
|
+
const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6316
6513
|
mkdirSync6(ctxDir, { recursive: true });
|
|
6317
|
-
const ctxFile =
|
|
6514
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6318
6515
|
const ctxContent = [
|
|
6319
6516
|
`## Session Context`,
|
|
6320
6517
|
`You are running in tmux session: ${sessionName}.`,
|
|
6321
6518
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6322
6519
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6323
6520
|
].join("\n");
|
|
6324
|
-
|
|
6521
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
6325
6522
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6326
6523
|
} catch {
|
|
6327
6524
|
}
|
|
@@ -6398,8 +6595,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6398
6595
|
transport.pipeLog(sessionName, logFile);
|
|
6399
6596
|
try {
|
|
6400
6597
|
const mySession = getMySession();
|
|
6401
|
-
const dispatchInfo =
|
|
6402
|
-
|
|
6598
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6599
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
6403
6600
|
dispatchedBy: mySession,
|
|
6404
6601
|
rootExe: exeSession,
|
|
6405
6602
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -6473,15 +6670,15 @@ var init_tmux_routing = __esm({
|
|
|
6473
6670
|
init_intercom_queue();
|
|
6474
6671
|
init_plan_limits();
|
|
6475
6672
|
init_employees();
|
|
6476
|
-
SPAWN_LOCK_DIR =
|
|
6477
|
-
SESSION_CACHE =
|
|
6673
|
+
SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6674
|
+
SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6478
6675
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6479
6676
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6480
6677
|
VERIFY_PANE_LINES = 200;
|
|
6481
6678
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6482
6679
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
6483
|
-
INTERCOM_LOG2 =
|
|
6484
|
-
DEBOUNCE_FILE =
|
|
6680
|
+
INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6681
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6485
6682
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6486
6683
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
6487
6684
|
}
|
|
@@ -6491,7 +6688,8 @@ var init_tmux_routing = __esm({
|
|
|
6491
6688
|
var task_scope_exports = {};
|
|
6492
6689
|
__export(task_scope_exports, {
|
|
6493
6690
|
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
6494
|
-
sessionScopeFilter: () => sessionScopeFilter
|
|
6691
|
+
sessionScopeFilter: () => sessionScopeFilter,
|
|
6692
|
+
strictSessionScopeFilter: () => strictSessionScopeFilter
|
|
6495
6693
|
});
|
|
6496
6694
|
function getCurrentSessionScope() {
|
|
6497
6695
|
try {
|
|
@@ -6509,6 +6707,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
6509
6707
|
args: [scope]
|
|
6510
6708
|
};
|
|
6511
6709
|
}
|
|
6710
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
6711
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
6712
|
+
if (!scope) return { sql: "", args: [] };
|
|
6713
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
6714
|
+
return {
|
|
6715
|
+
sql: ` AND ${col} = ?`,
|
|
6716
|
+
args: [scope]
|
|
6717
|
+
};
|
|
6718
|
+
}
|
|
6512
6719
|
var init_task_scope = __esm({
|
|
6513
6720
|
"src/lib/task-scope.ts"() {
|
|
6514
6721
|
"use strict";
|
|
@@ -8367,10 +8574,10 @@ var init_hooks = __esm({
|
|
|
8367
8574
|
});
|
|
8368
8575
|
|
|
8369
8576
|
// src/runtime/safety-checks.ts
|
|
8370
|
-
import
|
|
8371
|
-
import
|
|
8577
|
+
import path18 from "path";
|
|
8578
|
+
import os11 from "os";
|
|
8372
8579
|
function checkPathSafety(filePath) {
|
|
8373
|
-
const resolved =
|
|
8580
|
+
const resolved = path18.resolve(filePath);
|
|
8374
8581
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
8375
8582
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
8376
8583
|
if (matches) {
|
|
@@ -8380,7 +8587,7 @@ function checkPathSafety(filePath) {
|
|
|
8380
8587
|
return { safe: true, bypassImmune: true };
|
|
8381
8588
|
}
|
|
8382
8589
|
function checkReadPathSafety(filePath) {
|
|
8383
|
-
const resolved =
|
|
8590
|
+
const resolved = path18.resolve(filePath);
|
|
8384
8591
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
8385
8592
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
8386
8593
|
);
|
|
@@ -8395,7 +8602,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
8395
8602
|
var init_safety_checks = __esm({
|
|
8396
8603
|
"src/runtime/safety-checks.ts"() {
|
|
8397
8604
|
"use strict";
|
|
8398
|
-
HOME =
|
|
8605
|
+
HOME = os11.homedir();
|
|
8399
8606
|
BYPASS_IMMUNE_PATTERNS = [
|
|
8400
8607
|
{
|
|
8401
8608
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -8406,11 +8613,11 @@ var init_safety_checks = __esm({
|
|
|
8406
8613
|
reason: "Git config can set hooks and command execution"
|
|
8407
8614
|
},
|
|
8408
8615
|
{
|
|
8409
|
-
pattern: (p) => p.startsWith(
|
|
8616
|
+
pattern: (p) => p.startsWith(path18.join(HOME, ".claude")),
|
|
8410
8617
|
reason: "Claude configuration files are protected"
|
|
8411
8618
|
},
|
|
8412
8619
|
{
|
|
8413
|
-
pattern: (p) => p.startsWith(
|
|
8620
|
+
pattern: (p) => p.startsWith(path18.join(HOME, ".exe-os")),
|
|
8414
8621
|
reason: "exe-os configuration files are protected"
|
|
8415
8622
|
},
|
|
8416
8623
|
{
|
|
@@ -8427,7 +8634,7 @@ var init_safety_checks = __esm({
|
|
|
8427
8634
|
},
|
|
8428
8635
|
{
|
|
8429
8636
|
pattern: (p) => {
|
|
8430
|
-
const name =
|
|
8637
|
+
const name = path18.basename(p);
|
|
8431
8638
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
8432
8639
|
},
|
|
8433
8640
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -8454,7 +8661,7 @@ __export(file_read_exports, {
|
|
|
8454
8661
|
FileReadTool: () => FileReadTool
|
|
8455
8662
|
});
|
|
8456
8663
|
import fs3 from "fs/promises";
|
|
8457
|
-
import
|
|
8664
|
+
import path19 from "path";
|
|
8458
8665
|
import { z } from "zod";
|
|
8459
8666
|
function isBinary(buf) {
|
|
8460
8667
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -8490,7 +8697,7 @@ var init_file_read = __esm({
|
|
|
8490
8697
|
return { behavior: "allow" };
|
|
8491
8698
|
},
|
|
8492
8699
|
async call(input, context) {
|
|
8493
|
-
const filePath =
|
|
8700
|
+
const filePath = path19.isAbsolute(input.file_path) ? input.file_path : path19.resolve(context.cwd, input.file_path);
|
|
8494
8701
|
let stat;
|
|
8495
8702
|
try {
|
|
8496
8703
|
stat = await fs3.stat(filePath);
|
|
@@ -8530,7 +8737,7 @@ __export(glob_exports, {
|
|
|
8530
8737
|
GlobTool: () => GlobTool
|
|
8531
8738
|
});
|
|
8532
8739
|
import fs4 from "fs/promises";
|
|
8533
|
-
import
|
|
8740
|
+
import path20 from "path";
|
|
8534
8741
|
import { z as z2 } from "zod";
|
|
8535
8742
|
async function walkDir(dir, maxDepth = 10) {
|
|
8536
8743
|
const results = [];
|
|
@@ -8546,7 +8753,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
8546
8753
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
8547
8754
|
continue;
|
|
8548
8755
|
}
|
|
8549
|
-
const fullPath =
|
|
8756
|
+
const fullPath = path20.join(current, entry.name);
|
|
8550
8757
|
if (entry.isDirectory()) {
|
|
8551
8758
|
await walk(fullPath, depth + 1);
|
|
8552
8759
|
} else {
|
|
@@ -8580,11 +8787,11 @@ var init_glob = __esm({
|
|
|
8580
8787
|
inputSchema: inputSchema2,
|
|
8581
8788
|
isReadOnly: true,
|
|
8582
8789
|
async call(input, context) {
|
|
8583
|
-
const baseDir = input.path ?
|
|
8790
|
+
const baseDir = input.path ? path20.isAbsolute(input.path) ? input.path : path20.resolve(context.cwd, input.path) : context.cwd;
|
|
8584
8791
|
try {
|
|
8585
8792
|
const entries = await walkDir(baseDir);
|
|
8586
8793
|
const matched = entries.filter(
|
|
8587
|
-
(e) => simpleGlobMatch(
|
|
8794
|
+
(e) => simpleGlobMatch(path20.relative(baseDir, e.path), input.pattern)
|
|
8588
8795
|
);
|
|
8589
8796
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
8590
8797
|
if (matched.length === 0) {
|
|
@@ -8610,7 +8817,7 @@ __export(grep_exports, {
|
|
|
8610
8817
|
});
|
|
8611
8818
|
import { spawn as spawn2 } from "child_process";
|
|
8612
8819
|
import fs5 from "fs/promises";
|
|
8613
|
-
import
|
|
8820
|
+
import path21 from "path";
|
|
8614
8821
|
import { z as z3 } from "zod";
|
|
8615
8822
|
function runRipgrep(input, searchPath, context) {
|
|
8616
8823
|
return new Promise((resolve, reject) => {
|
|
@@ -8664,7 +8871,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
8664
8871
|
}
|
|
8665
8872
|
for (const entry of entries) {
|
|
8666
8873
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
8667
|
-
const fullPath =
|
|
8874
|
+
const fullPath = path21.join(dir, entry.name);
|
|
8668
8875
|
if (entry.isDirectory()) {
|
|
8669
8876
|
await walk(fullPath);
|
|
8670
8877
|
} else {
|
|
@@ -8710,7 +8917,7 @@ var init_grep = __esm({
|
|
|
8710
8917
|
inputSchema: inputSchema3,
|
|
8711
8918
|
isReadOnly: true,
|
|
8712
8919
|
async call(input, context) {
|
|
8713
|
-
const searchPath = input.path ?
|
|
8920
|
+
const searchPath = input.path ? path21.isAbsolute(input.path) ? input.path : path21.resolve(context.cwd, input.path) : context.cwd;
|
|
8714
8921
|
try {
|
|
8715
8922
|
const result = await runRipgrep(input, searchPath, context);
|
|
8716
8923
|
return result;
|
|
@@ -8735,7 +8942,7 @@ __export(file_write_exports, {
|
|
|
8735
8942
|
FileWriteTool: () => FileWriteTool
|
|
8736
8943
|
});
|
|
8737
8944
|
import fs6 from "fs/promises";
|
|
8738
|
-
import
|
|
8945
|
+
import path22 from "path";
|
|
8739
8946
|
import { z as z4 } from "zod";
|
|
8740
8947
|
var inputSchema4, FileWriteTool;
|
|
8741
8948
|
var init_file_write = __esm({
|
|
@@ -8763,8 +8970,8 @@ var init_file_write = __esm({
|
|
|
8763
8970
|
return { behavior: "allow" };
|
|
8764
8971
|
},
|
|
8765
8972
|
async call(input, context) {
|
|
8766
|
-
const filePath =
|
|
8767
|
-
const dir =
|
|
8973
|
+
const filePath = path22.isAbsolute(input.file_path) ? input.file_path : path22.resolve(context.cwd, input.file_path);
|
|
8974
|
+
const dir = path22.dirname(filePath);
|
|
8768
8975
|
await fs6.mkdir(dir, { recursive: true });
|
|
8769
8976
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
8770
8977
|
return {
|
|
@@ -8782,7 +8989,7 @@ __export(file_edit_exports, {
|
|
|
8782
8989
|
FileEditTool: () => FileEditTool
|
|
8783
8990
|
});
|
|
8784
8991
|
import fs7 from "fs/promises";
|
|
8785
|
-
import
|
|
8992
|
+
import path23 from "path";
|
|
8786
8993
|
import { z as z5 } from "zod";
|
|
8787
8994
|
function countOccurrences(haystack, needle) {
|
|
8788
8995
|
let count = 0;
|
|
@@ -8823,7 +9030,7 @@ var init_file_edit = __esm({
|
|
|
8823
9030
|
return { behavior: "allow" };
|
|
8824
9031
|
},
|
|
8825
9032
|
async call(input, context) {
|
|
8826
|
-
const filePath =
|
|
9033
|
+
const filePath = path23.isAbsolute(input.file_path) ? input.file_path : path23.resolve(context.cwd, input.file_path);
|
|
8827
9034
|
let content;
|
|
8828
9035
|
try {
|
|
8829
9036
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -9430,6 +9637,133 @@ ${task.context}`,
|
|
|
9430
9637
|
}
|
|
9431
9638
|
});
|
|
9432
9639
|
|
|
9640
|
+
// src/lib/tui-data.ts
|
|
9641
|
+
var tui_data_exports = {};
|
|
9642
|
+
__export(tui_data_exports, {
|
|
9643
|
+
loadMemoryDashboard: () => loadMemoryDashboard,
|
|
9644
|
+
loadTaskList: () => loadTaskList,
|
|
9645
|
+
loadTeamMetrics: () => loadTeamMetrics,
|
|
9646
|
+
searchWikiMemoryRows: () => searchWikiMemoryRows
|
|
9647
|
+
});
|
|
9648
|
+
async function loadMemoryDashboard(limit) {
|
|
9649
|
+
const client = getClient();
|
|
9650
|
+
const [countResult, recentResult, agentResult] = await Promise.all([
|
|
9651
|
+
client.execute("SELECT COUNT(*) as cnt FROM memories"),
|
|
9652
|
+
client.execute({
|
|
9653
|
+
sql: "SELECT agent_id, tool_name, project_name, raw_text, timestamp FROM memories ORDER BY timestamp DESC LIMIT ?",
|
|
9654
|
+
args: [limit]
|
|
9655
|
+
}),
|
|
9656
|
+
client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id ORDER BY cnt DESC")
|
|
9657
|
+
]);
|
|
9658
|
+
return {
|
|
9659
|
+
total: Number(countResult.rows[0]?.cnt ?? 0),
|
|
9660
|
+
recent: recentResult.rows.map((row) => ({
|
|
9661
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
9662
|
+
toolName: String(row.tool_name ?? ""),
|
|
9663
|
+
projectName: String(row.project_name ?? ""),
|
|
9664
|
+
rawText: String(row.raw_text ?? ""),
|
|
9665
|
+
timestamp: String(row.timestamp ?? "")
|
|
9666
|
+
})),
|
|
9667
|
+
byAgent: agentResult.rows.map((row) => ({
|
|
9668
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
9669
|
+
count: Number(row.cnt ?? 0)
|
|
9670
|
+
}))
|
|
9671
|
+
};
|
|
9672
|
+
}
|
|
9673
|
+
async function loadTeamMetrics(employeeNames) {
|
|
9674
|
+
const client = getClient();
|
|
9675
|
+
const memoryCounts = /* @__PURE__ */ new Map();
|
|
9676
|
+
const projectsByEmployee = /* @__PURE__ */ new Map();
|
|
9677
|
+
const currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
9678
|
+
const scope = sessionScopeFilter();
|
|
9679
|
+
const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
|
|
9680
|
+
for (const row of memResult.rows) {
|
|
9681
|
+
memoryCounts.set(String(row.agent_id), Number(row.cnt));
|
|
9682
|
+
}
|
|
9683
|
+
for (const employeeName of employeeNames) {
|
|
9684
|
+
const [projectResult, taskResult] = await Promise.all([
|
|
9685
|
+
client.execute({
|
|
9686
|
+
sql: `SELECT DISTINCT project_name,
|
|
9687
|
+
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
9688
|
+
FROM tasks
|
|
9689
|
+
WHERE assigned_to = ? AND status IN ('open','in_progress','done')${scope.sql}
|
|
9690
|
+
GROUP BY project_name
|
|
9691
|
+
ORDER BY urgency ASC
|
|
9692
|
+
LIMIT 5`,
|
|
9693
|
+
args: [employeeName, ...scope.args]
|
|
9694
|
+
}),
|
|
9695
|
+
client.execute({
|
|
9696
|
+
sql: `SELECT title FROM tasks
|
|
9697
|
+
WHERE assigned_to = ? AND status = 'in_progress'${scope.sql}
|
|
9698
|
+
ORDER BY updated_at DESC
|
|
9699
|
+
LIMIT 1`,
|
|
9700
|
+
args: [employeeName, ...scope.args]
|
|
9701
|
+
})
|
|
9702
|
+
]);
|
|
9703
|
+
const projects = projectResult.rows.map((row) => {
|
|
9704
|
+
const urgency = Number(row.urgency);
|
|
9705
|
+
return {
|
|
9706
|
+
name: String(row.project_name),
|
|
9707
|
+
status: urgency === 1 ? "active" : urgency === 2 ? "has_tasks" : "idle"
|
|
9708
|
+
};
|
|
9709
|
+
});
|
|
9710
|
+
if (projects.length > 0) projectsByEmployee.set(employeeName, projects);
|
|
9711
|
+
if (taskResult.rows.length > 0) {
|
|
9712
|
+
currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
|
|
9713
|
+
}
|
|
9714
|
+
}
|
|
9715
|
+
return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
|
|
9716
|
+
}
|
|
9717
|
+
async function loadTaskList() {
|
|
9718
|
+
const client = getClient();
|
|
9719
|
+
const scope = sessionScopeFilter();
|
|
9720
|
+
const result = await client.execute({
|
|
9721
|
+
sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
|
|
9722
|
+
FROM tasks
|
|
9723
|
+
WHERE 1=1${scope.sql}
|
|
9724
|
+
ORDER BY
|
|
9725
|
+
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,
|
|
9726
|
+
CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
|
|
9727
|
+
created_at DESC`,
|
|
9728
|
+
args: [...scope.args]
|
|
9729
|
+
});
|
|
9730
|
+
return result.rows.map((row) => ({
|
|
9731
|
+
id: String(row.id ?? ""),
|
|
9732
|
+
title: String(row.title ?? ""),
|
|
9733
|
+
priority: String(row.priority ?? "p2").toUpperCase(),
|
|
9734
|
+
assignedTo: String(row.assigned_to ?? ""),
|
|
9735
|
+
assignedBy: String(row.assigned_by ?? ""),
|
|
9736
|
+
status: String(row.status ?? "open"),
|
|
9737
|
+
projectName: String(row.project_name ?? ""),
|
|
9738
|
+
createdAt: String(row.created_at ?? ""),
|
|
9739
|
+
result: String(row.result ?? "")
|
|
9740
|
+
}));
|
|
9741
|
+
}
|
|
9742
|
+
async function searchWikiMemoryRows(query) {
|
|
9743
|
+
const client = getClient();
|
|
9744
|
+
const result = await client.execute({
|
|
9745
|
+
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
9746
|
+
FROM memories
|
|
9747
|
+
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
9748
|
+
ORDER BY timestamp DESC LIMIT 20`,
|
|
9749
|
+
args: [`%${query}%`]
|
|
9750
|
+
});
|
|
9751
|
+
return result.rows.map((row) => ({
|
|
9752
|
+
id: String(row.id),
|
|
9753
|
+
agentId: String(row.agent_id),
|
|
9754
|
+
rawText: String(row.raw_text),
|
|
9755
|
+
timestamp: String(row.timestamp),
|
|
9756
|
+
projectName: String(row.project_name ?? "")
|
|
9757
|
+
}));
|
|
9758
|
+
}
|
|
9759
|
+
var init_tui_data = __esm({
|
|
9760
|
+
"src/lib/tui-data.ts"() {
|
|
9761
|
+
"use strict";
|
|
9762
|
+
init_database();
|
|
9763
|
+
init_task_scope();
|
|
9764
|
+
}
|
|
9765
|
+
});
|
|
9766
|
+
|
|
9433
9767
|
// src/lib/message-queue.ts
|
|
9434
9768
|
var DEFAULT_MAX_SIZE, DEFAULT_TTL_MS, MessageQueue;
|
|
9435
9769
|
var init_message_queue = __esm({
|
|
@@ -9488,14 +9822,14 @@ __export(keychain_exports, {
|
|
|
9488
9822
|
setMasterKey: () => setMasterKey
|
|
9489
9823
|
});
|
|
9490
9824
|
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
|
|
9825
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9826
|
+
import path26 from "path";
|
|
9827
|
+
import os12 from "os";
|
|
9494
9828
|
function getKeyDir() {
|
|
9495
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
9829
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path26.join(os12.homedir(), ".exe-os");
|
|
9496
9830
|
}
|
|
9497
9831
|
function getKeyPath() {
|
|
9498
|
-
return
|
|
9832
|
+
return path26.join(getKeyDir(), "master.key");
|
|
9499
9833
|
}
|
|
9500
9834
|
async function tryKeytar() {
|
|
9501
9835
|
try {
|
|
@@ -9516,9 +9850,9 @@ async function getMasterKey() {
|
|
|
9516
9850
|
}
|
|
9517
9851
|
}
|
|
9518
9852
|
const keyPath = getKeyPath();
|
|
9519
|
-
if (!
|
|
9853
|
+
if (!existsSync16(keyPath)) {
|
|
9520
9854
|
process.stderr.write(
|
|
9521
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
9855
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os12.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
9522
9856
|
`
|
|
9523
9857
|
);
|
|
9524
9858
|
return null;
|
|
@@ -9559,7 +9893,7 @@ async function deleteMasterKey() {
|
|
|
9559
9893
|
}
|
|
9560
9894
|
}
|
|
9561
9895
|
const keyPath = getKeyPath();
|
|
9562
|
-
if (
|
|
9896
|
+
if (existsSync16(keyPath)) {
|
|
9563
9897
|
await unlink(keyPath);
|
|
9564
9898
|
}
|
|
9565
9899
|
}
|
|
@@ -9608,16 +9942,16 @@ __export(ws_auth_exports, {
|
|
|
9608
9942
|
deriveWsAuthToken: () => deriveWsAuthToken,
|
|
9609
9943
|
hashAuthToken: () => hashAuthToken
|
|
9610
9944
|
});
|
|
9611
|
-
import
|
|
9945
|
+
import crypto7 from "crypto";
|
|
9612
9946
|
function deriveWsAuthToken(masterKey) {
|
|
9613
|
-
return Buffer.from(
|
|
9947
|
+
return Buffer.from(crypto7.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
|
|
9614
9948
|
}
|
|
9615
9949
|
function deriveOrgId(masterKey) {
|
|
9616
|
-
const raw = Buffer.from(
|
|
9617
|
-
return
|
|
9950
|
+
const raw = Buffer.from(crypto7.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
|
|
9951
|
+
return crypto7.createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
9618
9952
|
}
|
|
9619
9953
|
function hashAuthToken(token) {
|
|
9620
|
-
return
|
|
9954
|
+
return crypto7.createHash("sha256").update(token).digest("hex");
|
|
9621
9955
|
}
|
|
9622
9956
|
var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
|
|
9623
9957
|
var init_ws_auth = __esm({
|
|
@@ -9859,8 +10193,8 @@ __export(wiki_client_exports, {
|
|
|
9859
10193
|
listDocuments: () => listDocuments,
|
|
9860
10194
|
listWorkspaces: () => listWorkspaces
|
|
9861
10195
|
});
|
|
9862
|
-
async function wikiFetch(config,
|
|
9863
|
-
const url = `${config.baseUrl}/api/v1${
|
|
10196
|
+
async function wikiFetch(config, path28, method = "GET", body) {
|
|
10197
|
+
const url = `${config.baseUrl}/api/v1${path28}`;
|
|
9864
10198
|
const headers = {
|
|
9865
10199
|
Authorization: `Bearer ${config.apiKey}`,
|
|
9866
10200
|
"Content-Type": "application/json"
|
|
@@ -9893,7 +10227,7 @@ async function wikiFetch(config, path27, method = "GET", body) {
|
|
|
9893
10227
|
}
|
|
9894
10228
|
}
|
|
9895
10229
|
if (!response.ok) {
|
|
9896
|
-
throw new Error(`Wiki API ${method} ${
|
|
10230
|
+
throw new Error(`Wiki API ${method} ${path28}: ${response.status} ${response.statusText}`);
|
|
9897
10231
|
}
|
|
9898
10232
|
return response.json();
|
|
9899
10233
|
} finally {
|
|
@@ -10006,6 +10340,7 @@ var shard_manager_exports = {};
|
|
|
10006
10340
|
__export(shard_manager_exports, {
|
|
10007
10341
|
disposeShards: () => disposeShards,
|
|
10008
10342
|
ensureShardSchema: () => ensureShardSchema,
|
|
10343
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
10009
10344
|
getReadyShardClient: () => getReadyShardClient,
|
|
10010
10345
|
getShardClient: () => getShardClient,
|
|
10011
10346
|
getShardsDir: () => getShardsDir,
|
|
@@ -10014,15 +10349,18 @@ __export(shard_manager_exports, {
|
|
|
10014
10349
|
listShards: () => listShards,
|
|
10015
10350
|
shardExists: () => shardExists
|
|
10016
10351
|
});
|
|
10017
|
-
import
|
|
10018
|
-
import { existsSync as
|
|
10352
|
+
import path27 from "path";
|
|
10353
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
10019
10354
|
import { createClient as createClient2 } from "@libsql/client";
|
|
10020
10355
|
function initShardManager(encryptionKey) {
|
|
10021
10356
|
_encryptionKey = encryptionKey;
|
|
10022
|
-
if (!
|
|
10357
|
+
if (!existsSync17(SHARDS_DIR)) {
|
|
10023
10358
|
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
10024
10359
|
}
|
|
10025
10360
|
_shardingEnabled = true;
|
|
10361
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
10362
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
10363
|
+
_evictionTimer.unref();
|
|
10026
10364
|
}
|
|
10027
10365
|
function isShardingEnabled() {
|
|
10028
10366
|
return _shardingEnabled;
|
|
@@ -10039,21 +10377,28 @@ function getShardClient(projectName) {
|
|
|
10039
10377
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
10040
10378
|
}
|
|
10041
10379
|
const cached = _shards.get(safeName);
|
|
10042
|
-
if (cached)
|
|
10043
|
-
|
|
10380
|
+
if (cached) {
|
|
10381
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
10382
|
+
return cached;
|
|
10383
|
+
}
|
|
10384
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
10385
|
+
evictLRU();
|
|
10386
|
+
}
|
|
10387
|
+
const dbPath = path27.join(SHARDS_DIR, `${safeName}.db`);
|
|
10044
10388
|
const client = createClient2({
|
|
10045
10389
|
url: `file:${dbPath}`,
|
|
10046
10390
|
encryptionKey: _encryptionKey
|
|
10047
10391
|
});
|
|
10048
10392
|
_shards.set(safeName, client);
|
|
10393
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
10049
10394
|
return client;
|
|
10050
10395
|
}
|
|
10051
10396
|
function shardExists(projectName) {
|
|
10052
10397
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
10053
|
-
return
|
|
10398
|
+
return existsSync17(path27.join(SHARDS_DIR, `${safeName}.db`));
|
|
10054
10399
|
}
|
|
10055
10400
|
function listShards() {
|
|
10056
|
-
if (!
|
|
10401
|
+
if (!existsSync17(SHARDS_DIR)) return [];
|
|
10057
10402
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
10058
10403
|
}
|
|
10059
10404
|
async function ensureShardSchema(client) {
|
|
@@ -10105,6 +10450,8 @@ async function ensureShardSchema(client) {
|
|
|
10105
10450
|
for (const col of [
|
|
10106
10451
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
10107
10452
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
10453
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
10454
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
10108
10455
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
10109
10456
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
10110
10457
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -10242,21 +10589,69 @@ async function getReadyShardClient(projectName) {
|
|
|
10242
10589
|
await ensureShardSchema(client);
|
|
10243
10590
|
return client;
|
|
10244
10591
|
}
|
|
10592
|
+
function evictLRU() {
|
|
10593
|
+
let oldest = null;
|
|
10594
|
+
let oldestTime = Infinity;
|
|
10595
|
+
for (const [name, time] of _shardLastAccess) {
|
|
10596
|
+
if (time < oldestTime) {
|
|
10597
|
+
oldestTime = time;
|
|
10598
|
+
oldest = name;
|
|
10599
|
+
}
|
|
10600
|
+
}
|
|
10601
|
+
if (oldest) {
|
|
10602
|
+
const client = _shards.get(oldest);
|
|
10603
|
+
if (client) {
|
|
10604
|
+
client.close();
|
|
10605
|
+
}
|
|
10606
|
+
_shards.delete(oldest);
|
|
10607
|
+
_shardLastAccess.delete(oldest);
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
function evictIdleShards() {
|
|
10611
|
+
const now = Date.now();
|
|
10612
|
+
const toEvict = [];
|
|
10613
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
10614
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
10615
|
+
toEvict.push(name);
|
|
10616
|
+
}
|
|
10617
|
+
}
|
|
10618
|
+
for (const name of toEvict) {
|
|
10619
|
+
const client = _shards.get(name);
|
|
10620
|
+
if (client) {
|
|
10621
|
+
client.close();
|
|
10622
|
+
}
|
|
10623
|
+
_shards.delete(name);
|
|
10624
|
+
_shardLastAccess.delete(name);
|
|
10625
|
+
}
|
|
10626
|
+
}
|
|
10627
|
+
function getOpenShardCount() {
|
|
10628
|
+
return _shards.size;
|
|
10629
|
+
}
|
|
10245
10630
|
function disposeShards() {
|
|
10631
|
+
if (_evictionTimer) {
|
|
10632
|
+
clearInterval(_evictionTimer);
|
|
10633
|
+
_evictionTimer = null;
|
|
10634
|
+
}
|
|
10246
10635
|
for (const [, client] of _shards) {
|
|
10247
10636
|
client.close();
|
|
10248
10637
|
}
|
|
10249
10638
|
_shards.clear();
|
|
10639
|
+
_shardLastAccess.clear();
|
|
10250
10640
|
_shardingEnabled = false;
|
|
10251
10641
|
_encryptionKey = null;
|
|
10252
10642
|
}
|
|
10253
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
10643
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
10254
10644
|
var init_shard_manager = __esm({
|
|
10255
10645
|
"src/lib/shard-manager.ts"() {
|
|
10256
10646
|
"use strict";
|
|
10257
10647
|
init_config();
|
|
10258
|
-
SHARDS_DIR =
|
|
10648
|
+
SHARDS_DIR = path27.join(EXE_AI_DIR, "shards");
|
|
10649
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
10650
|
+
MAX_OPEN_SHARDS = 10;
|
|
10651
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
10259
10652
|
_shards = /* @__PURE__ */ new Map();
|
|
10653
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
10654
|
+
_evictionTimer = null;
|
|
10260
10655
|
_encryptionKey = null;
|
|
10261
10656
|
_shardingEnabled = false;
|
|
10262
10657
|
}
|
|
@@ -14827,8 +15222,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
14827
15222
|
}
|
|
14828
15223
|
|
|
14829
15224
|
// src/tui/ink/components/ErrorOverview.js
|
|
14830
|
-
var cleanupPath = (
|
|
14831
|
-
return
|
|
15225
|
+
var cleanupPath = (path28) => {
|
|
15226
|
+
return path28?.replace(`file://${cwd()}/`, "");
|
|
14832
15227
|
};
|
|
14833
15228
|
var stackUtils = new StackUtils({
|
|
14834
15229
|
cwd: cwd(),
|
|
@@ -16760,11 +17155,11 @@ function Footer() {
|
|
|
16760
17155
|
} catch {
|
|
16761
17156
|
}
|
|
16762
17157
|
try {
|
|
16763
|
-
const { existsSync:
|
|
17158
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
16764
17159
|
const { join } = await import("path");
|
|
16765
17160
|
const home = process.env.HOME ?? "";
|
|
16766
17161
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
16767
|
-
setDaemon(
|
|
17162
|
+
setDaemon(existsSync18(pidPath) ? "running" : "stopped");
|
|
16768
17163
|
} catch {
|
|
16769
17164
|
setDaemon("unknown");
|
|
16770
17165
|
}
|
|
@@ -16846,7 +17241,7 @@ function Footer() {
|
|
|
16846
17241
|
// src/tui/views/CommandCenter.tsx
|
|
16847
17242
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
16848
17243
|
import TextInput from "ink-text-input";
|
|
16849
|
-
import
|
|
17244
|
+
import path24 from "path";
|
|
16850
17245
|
import { homedir } from "os";
|
|
16851
17246
|
|
|
16852
17247
|
// src/tui/components/StatusDot.tsx
|
|
@@ -17633,15 +18028,15 @@ function CommandCenterView({
|
|
|
17633
18028
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
17634
18029
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
17635
18030
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
17636
|
-
const { readFileSync:
|
|
18031
|
+
const { readFileSync: readFileSync14, existsSync: existsSync18 } = await import("fs");
|
|
17637
18032
|
const { join } = await import("path");
|
|
17638
18033
|
const { homedir: homedir3 } = await import("os");
|
|
17639
18034
|
const configPath = join(homedir3(), ".exe-os", "config.json");
|
|
17640
18035
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
17641
18036
|
let providerConfigs = {};
|
|
17642
|
-
if (
|
|
18037
|
+
if (existsSync18(configPath)) {
|
|
17643
18038
|
try {
|
|
17644
|
-
const raw = JSON.parse(
|
|
18039
|
+
const raw = JSON.parse(readFileSync14(configPath, "utf8"));
|
|
17645
18040
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
17646
18041
|
if (raw.providers && typeof raw.providers === "object") {
|
|
17647
18042
|
providerConfigs = raw.providers;
|
|
@@ -17702,7 +18097,7 @@ function CommandCenterView({
|
|
|
17702
18097
|
const markerDir = join(homedir3(), ".exe-os", "session-cache");
|
|
17703
18098
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
17704
18099
|
for (const f of agentFiles) {
|
|
17705
|
-
const data = JSON.parse(
|
|
18100
|
+
const data = JSON.parse(readFileSync14(join(markerDir, f), "utf8"));
|
|
17706
18101
|
if (data.agentRole) {
|
|
17707
18102
|
agentRole = data.agentRole;
|
|
17708
18103
|
break;
|
|
@@ -17847,7 +18242,7 @@ function CommandCenterView({
|
|
|
17847
18242
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
17848
18243
|
projectName: p.projectName,
|
|
17849
18244
|
exeSession: p.exeSession,
|
|
17850
|
-
projectDir:
|
|
18245
|
+
projectDir: path24.join(homedir(), p.projectName),
|
|
17851
18246
|
employeeCount: p.employees.length,
|
|
17852
18247
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
17853
18248
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -17885,7 +18280,7 @@ function CommandCenterView({
|
|
|
17885
18280
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
17886
18281
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
17887
18282
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
17888
|
-
const { existsSync:
|
|
18283
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
17889
18284
|
const { join } = await import("path");
|
|
17890
18285
|
const client = getClient2();
|
|
17891
18286
|
if (!client) {
|
|
@@ -17956,7 +18351,7 @@ function CommandCenterView({
|
|
|
17956
18351
|
}
|
|
17957
18352
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
17958
18353
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
17959
|
-
const hasGit = projectDir ?
|
|
18354
|
+
const hasGit = projectDir ? existsSync18(join(projectDir, ".git")) : false;
|
|
17960
18355
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
17961
18356
|
projectList.push({
|
|
17962
18357
|
projectName: name,
|
|
@@ -17981,7 +18376,7 @@ function CommandCenterView({
|
|
|
17981
18376
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
17982
18377
|
try {
|
|
17983
18378
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
17984
|
-
setHealth((h) => ({ ...h, daemon:
|
|
18379
|
+
setHealth((h) => ({ ...h, daemon: existsSync18(pidPath) ? "running" : "stopped" }));
|
|
17985
18380
|
} catch {
|
|
17986
18381
|
}
|
|
17987
18382
|
const activityResult = await client.execute(
|
|
@@ -18196,7 +18591,7 @@ function ChatMessageRow({ msg }) {
|
|
|
18196
18591
|
|
|
18197
18592
|
// src/tui/views/Sessions.tsx
|
|
18198
18593
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
18199
|
-
import
|
|
18594
|
+
import path25 from "path";
|
|
18200
18595
|
import { homedir as homedir2 } from "os";
|
|
18201
18596
|
|
|
18202
18597
|
// src/tui/components/TmuxPane.tsx
|
|
@@ -18500,7 +18895,7 @@ function SessionsView({
|
|
|
18500
18895
|
if (demo) {
|
|
18501
18896
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
18502
18897
|
...p,
|
|
18503
|
-
projectDir:
|
|
18898
|
+
projectDir: path25.join(homedir2(), p.projectName),
|
|
18504
18899
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
18505
18900
|
})));
|
|
18506
18901
|
return;
|
|
@@ -18896,7 +19291,6 @@ function SessionsView({
|
|
|
18896
19291
|
|
|
18897
19292
|
// src/tui/views/Tasks.tsx
|
|
18898
19293
|
import React20, { useState as useState10, useEffect as useEffect12, useMemo as useMemo5 } from "react";
|
|
18899
|
-
init_task_scope();
|
|
18900
19294
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
18901
19295
|
var STATUS_FILTERS = ["all", "open", "in_progress", "done", "blocked", "needs_review"];
|
|
18902
19296
|
var PRIORITY_COLORS = {
|
|
@@ -19029,37 +19423,22 @@ function TasksView({ onBack }) {
|
|
|
19029
19423
|
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
19030
19424
|
return withTrace2("tui.tasks.loadTasks", async () => {
|
|
19031
19425
|
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
|
-
}
|
|
19426
|
+
const { loadTaskList: loadTaskList2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
19427
|
+
const tasks = await loadTaskList2();
|
|
19428
|
+
setAllTasks(
|
|
19429
|
+
tasks.map((task) => ({
|
|
19430
|
+
id: task.id,
|
|
19431
|
+
priority: task.priority,
|
|
19432
|
+
title: task.title,
|
|
19433
|
+
assignee: task.assignedTo,
|
|
19434
|
+
assignedBy: task.assignedBy,
|
|
19435
|
+
status: task.status,
|
|
19436
|
+
project: task.projectName,
|
|
19437
|
+
createdAt: task.createdAt,
|
|
19438
|
+
result: task.result
|
|
19439
|
+
}))
|
|
19440
|
+
);
|
|
19441
|
+
setDbError(null);
|
|
19063
19442
|
} catch (err) {
|
|
19064
19443
|
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
19065
19444
|
} finally {
|
|
@@ -19397,12 +19776,12 @@ async function loadGatewayConfig() {
|
|
|
19397
19776
|
state.running = false;
|
|
19398
19777
|
}
|
|
19399
19778
|
try {
|
|
19400
|
-
const { existsSync:
|
|
19779
|
+
const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
|
|
19401
19780
|
const { join } = await import("path");
|
|
19402
19781
|
const home = process.env.HOME ?? "";
|
|
19403
19782
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
19404
|
-
if (
|
|
19405
|
-
const raw = JSON.parse(
|
|
19783
|
+
if (existsSync18(configPath)) {
|
|
19784
|
+
const raw = JSON.parse(readFileSync14(configPath, "utf8"));
|
|
19406
19785
|
state.port = raw.port ?? 3100;
|
|
19407
19786
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
19408
19787
|
if (raw.adapters) {
|
|
@@ -19802,7 +20181,6 @@ function getAgentStatus(agentId) {
|
|
|
19802
20181
|
}
|
|
19803
20182
|
|
|
19804
20183
|
// src/tui/views/Team.tsx
|
|
19805
|
-
init_task_scope();
|
|
19806
20184
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
19807
20185
|
var DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
|
|
19808
20186
|
function roleBadgeColor(role) {
|
|
@@ -19875,41 +20253,16 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
19875
20253
|
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
19876
20254
|
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
19877
20255
|
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
|
-
}
|
|
20256
|
+
const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
20257
|
+
const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
|
|
20258
|
+
memoryCounts = teamMetrics.memoryCounts;
|
|
20259
|
+
projectsByEmployee = new Map(
|
|
20260
|
+
Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
|
|
20261
|
+
name,
|
|
20262
|
+
projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
|
|
20263
|
+
])
|
|
20264
|
+
);
|
|
20265
|
+
currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
|
|
19913
20266
|
} catch {
|
|
19914
20267
|
}
|
|
19915
20268
|
const teamData = roster.map((emp) => {
|
|
@@ -19928,12 +20281,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
19928
20281
|
setMembers(teamData);
|
|
19929
20282
|
setDbError(null);
|
|
19930
20283
|
try {
|
|
19931
|
-
const { existsSync:
|
|
20284
|
+
const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
|
|
19932
20285
|
const { join } = await import("path");
|
|
19933
20286
|
const home = process.env.HOME ?? "";
|
|
19934
20287
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
19935
|
-
if (
|
|
19936
|
-
const raw = JSON.parse(
|
|
20288
|
+
if (existsSync18(gatewayConfig)) {
|
|
20289
|
+
const raw = JSON.parse(readFileSync14(gatewayConfig, "utf8"));
|
|
19937
20290
|
if (raw.agents && raw.agents.length > 0) {
|
|
19938
20291
|
setExternals(raw.agents.map((a) => ({
|
|
19939
20292
|
name: a.name,
|
|
@@ -20247,24 +20600,8 @@ function WikiView({ onBack }) {
|
|
|
20247
20600
|
}
|
|
20248
20601
|
setSearchLoading(true);
|
|
20249
20602
|
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
|
-
);
|
|
20603
|
+
const { searchWikiMemoryRows: searchWikiMemoryRows2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
20604
|
+
setSearchResults(await searchWikiMemoryRows2(query));
|
|
20268
20605
|
} catch {
|
|
20269
20606
|
setSearchResults([]);
|
|
20270
20607
|
} finally {
|
|
@@ -20597,12 +20934,12 @@ function SettingsView({ onBack }) {
|
|
|
20597
20934
|
}
|
|
20598
20935
|
setProviders(providerList);
|
|
20599
20936
|
try {
|
|
20600
|
-
const { existsSync:
|
|
20937
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
20601
20938
|
const { join } = await import("path");
|
|
20602
20939
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
20603
20940
|
const cfg = await loadConfig2();
|
|
20604
20941
|
const home = process.env.HOME ?? "";
|
|
20605
|
-
const hasKey =
|
|
20942
|
+
const hasKey = existsSync18(join(home, ".exe-os", "master.key"));
|
|
20606
20943
|
if (cfg.cloud) {
|
|
20607
20944
|
setCloud({
|
|
20608
20945
|
configured: true,
|
|
@@ -20615,22 +20952,22 @@ function SettingsView({ onBack }) {
|
|
|
20615
20952
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
20616
20953
|
let daemon = "unknown";
|
|
20617
20954
|
try {
|
|
20618
|
-
daemon =
|
|
20955
|
+
daemon = existsSync18(pidPath) ? "running" : "stopped";
|
|
20619
20956
|
} catch {
|
|
20620
20957
|
}
|
|
20621
20958
|
let version = "unknown";
|
|
20622
20959
|
try {
|
|
20623
|
-
const { readFileSync:
|
|
20624
|
-
const { createRequire:
|
|
20625
|
-
const require2 =
|
|
20960
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
20961
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
20962
|
+
const require2 = createRequire3(import.meta.url);
|
|
20626
20963
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
20627
|
-
const pkg = JSON.parse(
|
|
20964
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
20628
20965
|
version = pkg.version;
|
|
20629
20966
|
} catch {
|
|
20630
20967
|
try {
|
|
20631
|
-
const { readFileSync:
|
|
20968
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
20632
20969
|
const { join: joinPath } = await import("path");
|
|
20633
|
-
const pkg = JSON.parse(
|
|
20970
|
+
const pkg = JSON.parse(readFileSync14(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
20634
20971
|
version = pkg.version;
|
|
20635
20972
|
} catch {
|
|
20636
20973
|
}
|