@askexenow/exe-os 0.9.8 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1411 -953
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +913 -543
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +418 -262
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +793 -485
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +566 -357
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +530 -319
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +547 -336
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +649 -417
- package/dist/hooks/bug-report-worker.js +486 -316
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +528 -317
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3442 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +534 -323
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +614 -382
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +569 -347
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +664 -431
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +1049 -680
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +422 -357
- package/dist/lib/tmux-routing.js +314 -248
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1408 -672
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +448 -371
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1983 -315
- package/dist/runtime/index.js +567 -355
- package/dist/tui/App.js +887 -531
- package/package.json +4 -4
package/dist/bin/cli.js
CHANGED
|
@@ -26,6 +26,44 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
26
26
|
};
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
|
|
29
|
+
// src/lib/secure-files.ts
|
|
30
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
31
|
+
import { chmod, mkdir } from "fs/promises";
|
|
32
|
+
async function ensurePrivateDir(dirPath) {
|
|
33
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
34
|
+
try {
|
|
35
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function ensurePrivateDirSync(dirPath) {
|
|
40
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
41
|
+
try {
|
|
42
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function enforcePrivateFile(filePath) {
|
|
47
|
+
try {
|
|
48
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function enforcePrivateFileSync(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
59
|
+
var init_secure_files = __esm({
|
|
60
|
+
"src/lib/secure-files.ts"() {
|
|
61
|
+
"use strict";
|
|
62
|
+
PRIVATE_DIR_MODE = 448;
|
|
63
|
+
PRIVATE_FILE_MODE = 384;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
29
67
|
// src/lib/config.ts
|
|
30
68
|
var config_exports = {};
|
|
31
69
|
__export(config_exports, {
|
|
@@ -42,8 +80,8 @@ __export(config_exports, {
|
|
|
42
80
|
migrateConfig: () => migrateConfig,
|
|
43
81
|
saveConfig: () => saveConfig
|
|
44
82
|
});
|
|
45
|
-
import { readFile, writeFile
|
|
46
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
83
|
+
import { readFile, writeFile } from "fs/promises";
|
|
84
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
47
85
|
import path from "path";
|
|
48
86
|
import os from "os";
|
|
49
87
|
function resolveDataDir() {
|
|
@@ -51,7 +89,7 @@ function resolveDataDir() {
|
|
|
51
89
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
52
90
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
53
91
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
54
|
-
if (!
|
|
92
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
55
93
|
try {
|
|
56
94
|
renameSync(legacyDir, newDir);
|
|
57
95
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -114,9 +152,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
114
152
|
}
|
|
115
153
|
async function loadConfig() {
|
|
116
154
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
117
|
-
await
|
|
155
|
+
await ensurePrivateDir(dir);
|
|
118
156
|
const configPath = path.join(dir, "config.json");
|
|
119
|
-
if (!
|
|
157
|
+
if (!existsSync2(configPath)) {
|
|
120
158
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
121
159
|
}
|
|
122
160
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -129,6 +167,7 @@ async function loadConfig() {
|
|
|
129
167
|
`);
|
|
130
168
|
try {
|
|
131
169
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
170
|
+
await enforcePrivateFile(configPath);
|
|
132
171
|
} catch {
|
|
133
172
|
}
|
|
134
173
|
}
|
|
@@ -147,7 +186,7 @@ async function loadConfig() {
|
|
|
147
186
|
function loadConfigSync() {
|
|
148
187
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
149
188
|
const configPath = path.join(dir, "config.json");
|
|
150
|
-
if (!
|
|
189
|
+
if (!existsSync2(configPath)) {
|
|
151
190
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
152
191
|
}
|
|
153
192
|
try {
|
|
@@ -165,12 +204,10 @@ function loadConfigSync() {
|
|
|
165
204
|
}
|
|
166
205
|
async function saveConfig(config) {
|
|
167
206
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
168
|
-
await
|
|
207
|
+
await ensurePrivateDir(dir);
|
|
169
208
|
const configPath = path.join(dir, "config.json");
|
|
170
209
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
171
|
-
|
|
172
|
-
await chmod(configPath, 384);
|
|
173
|
-
}
|
|
210
|
+
await enforcePrivateFile(configPath);
|
|
174
211
|
}
|
|
175
212
|
async function loadConfigFrom(configPath) {
|
|
176
213
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -190,6 +227,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
190
227
|
var init_config = __esm({
|
|
191
228
|
"src/lib/config.ts"() {
|
|
192
229
|
"use strict";
|
|
230
|
+
init_secure_files();
|
|
193
231
|
EXE_AI_DIR = resolveDataDir();
|
|
194
232
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
195
233
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -306,10 +344,10 @@ __export(agent_config_exports, {
|
|
|
306
344
|
saveAgentConfig: () => saveAgentConfig,
|
|
307
345
|
setAgentRuntime: () => setAgentRuntime
|
|
308
346
|
});
|
|
309
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as
|
|
347
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
310
348
|
import path2 from "path";
|
|
311
349
|
function loadAgentConfig() {
|
|
312
|
-
if (!
|
|
350
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
313
351
|
try {
|
|
314
352
|
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
315
353
|
} catch {
|
|
@@ -318,8 +356,9 @@ function loadAgentConfig() {
|
|
|
318
356
|
}
|
|
319
357
|
function saveAgentConfig(config) {
|
|
320
358
|
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
321
|
-
|
|
359
|
+
ensurePrivateDirSync(dir);
|
|
322
360
|
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
361
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
323
362
|
}
|
|
324
363
|
function getAgentRuntime(agentId) {
|
|
325
364
|
const config = loadAgentConfig();
|
|
@@ -359,6 +398,7 @@ var init_agent_config = __esm({
|
|
|
359
398
|
"use strict";
|
|
360
399
|
init_config();
|
|
361
400
|
init_runtime_table();
|
|
401
|
+
init_secure_files();
|
|
362
402
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
363
403
|
KNOWN_RUNTIMES = {
|
|
364
404
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -406,7 +446,7 @@ __export(employees_exports, {
|
|
|
406
446
|
validateEmployeeName: () => validateEmployeeName
|
|
407
447
|
});
|
|
408
448
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
409
|
-
import { existsSync as
|
|
449
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
410
450
|
import { execSync } from "child_process";
|
|
411
451
|
import path3 from "path";
|
|
412
452
|
import os2 from "os";
|
|
@@ -445,7 +485,7 @@ function validateEmployeeName(name) {
|
|
|
445
485
|
return { valid: true };
|
|
446
486
|
}
|
|
447
487
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
448
|
-
if (!
|
|
488
|
+
if (!existsSync4(employeesPath)) {
|
|
449
489
|
return [];
|
|
450
490
|
}
|
|
451
491
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -460,7 +500,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
460
500
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
461
501
|
}
|
|
462
502
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
463
|
-
if (!
|
|
503
|
+
if (!existsSync4(employeesPath)) return [];
|
|
464
504
|
try {
|
|
465
505
|
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
466
506
|
} catch {
|
|
@@ -508,7 +548,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
508
548
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
509
549
|
if (!coordinator) return;
|
|
510
550
|
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
511
|
-
if (!
|
|
551
|
+
if (!existsSync4(idPath)) return;
|
|
512
552
|
const content = readFileSync3(idPath, "utf-8");
|
|
513
553
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
514
554
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -562,9 +602,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
562
602
|
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
563
603
|
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
564
604
|
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
565
|
-
if (
|
|
605
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
566
606
|
renameSync2(oldPath, newPath);
|
|
567
|
-
} else if (
|
|
607
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
568
608
|
const content = readFileSync3(oldPath, "utf-8");
|
|
569
609
|
writeFileSync2(newPath, content, "utf-8");
|
|
570
610
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -607,7 +647,7 @@ function registerBinSymlinks(name) {
|
|
|
607
647
|
for (const suffix of ["", "-opencode"]) {
|
|
608
648
|
const linkName = `${name}${suffix}`;
|
|
609
649
|
const linkPath = path3.join(binDir, linkName);
|
|
610
|
-
if (
|
|
650
|
+
if (existsSync4(linkPath)) {
|
|
611
651
|
skipped.push(linkName);
|
|
612
652
|
continue;
|
|
613
653
|
}
|
|
@@ -638,7 +678,7 @@ var init_employees = __esm({
|
|
|
638
678
|
import os3 from "os";
|
|
639
679
|
import path4 from "path";
|
|
640
680
|
import {
|
|
641
|
-
existsSync as
|
|
681
|
+
existsSync as existsSync5,
|
|
642
682
|
lstatSync,
|
|
643
683
|
mkdirSync as mkdirSync2,
|
|
644
684
|
readlinkSync as readlinkSync2,
|
|
@@ -657,7 +697,7 @@ function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
|
657
697
|
const target = identitySourcePath(homeDir, agentId);
|
|
658
698
|
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
659
699
|
mkdirSync2(claudeAgentsDir(homeDir), { recursive: true });
|
|
660
|
-
if (
|
|
700
|
+
if (existsSync5(link)) {
|
|
661
701
|
let stat2;
|
|
662
702
|
try {
|
|
663
703
|
stat2 = lstatSync(link);
|
|
@@ -731,12 +771,12 @@ __export(preferences_exports, {
|
|
|
731
771
|
loadPreferences: () => loadPreferences,
|
|
732
772
|
savePreferences: () => savePreferences
|
|
733
773
|
});
|
|
734
|
-
import { existsSync as
|
|
774
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
735
775
|
import path5 from "path";
|
|
736
776
|
import os4 from "os";
|
|
737
777
|
function loadPreferences(homeDir = os4.homedir()) {
|
|
738
778
|
const configPath = path5.join(homeDir, ".exe-os", "config.json");
|
|
739
|
-
if (!
|
|
779
|
+
if (!existsSync6(configPath)) return {};
|
|
740
780
|
try {
|
|
741
781
|
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
742
782
|
return config.preferences ?? {};
|
|
@@ -747,9 +787,9 @@ function loadPreferences(homeDir = os4.homedir()) {
|
|
|
747
787
|
function savePreferences(prefs, homeDir = os4.homedir()) {
|
|
748
788
|
const configDir = path5.join(homeDir, ".exe-os");
|
|
749
789
|
const configPath = path5.join(configDir, "config.json");
|
|
750
|
-
|
|
790
|
+
ensurePrivateDirSync(configDir);
|
|
751
791
|
let config = {};
|
|
752
|
-
if (
|
|
792
|
+
if (existsSync6(configPath)) {
|
|
753
793
|
try {
|
|
754
794
|
config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
755
795
|
} catch {
|
|
@@ -758,10 +798,12 @@ function savePreferences(prefs, homeDir = os4.homedir()) {
|
|
|
758
798
|
}
|
|
759
799
|
config.preferences = prefs;
|
|
760
800
|
writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
801
|
+
enforcePrivateFileSync(configPath);
|
|
761
802
|
}
|
|
762
803
|
var init_preferences = __esm({
|
|
763
804
|
"src/lib/preferences.ts"() {
|
|
764
805
|
"use strict";
|
|
806
|
+
init_secure_files();
|
|
765
807
|
}
|
|
766
808
|
});
|
|
767
809
|
|
|
@@ -779,7 +821,7 @@ __export(installer_exports, {
|
|
|
779
821
|
setupTmux: () => setupTmux
|
|
780
822
|
});
|
|
781
823
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
782
|
-
import { existsSync as
|
|
824
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
783
825
|
import path6 from "path";
|
|
784
826
|
import os5 from "os";
|
|
785
827
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -790,7 +832,7 @@ function resolvePackageRoot() {
|
|
|
790
832
|
const root = path6.parse(dir).root;
|
|
791
833
|
while (dir !== root) {
|
|
792
834
|
const pkgPath = path6.join(dir, "package.json");
|
|
793
|
-
if (
|
|
835
|
+
if (existsSync7(pkgPath)) {
|
|
794
836
|
try {
|
|
795
837
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
796
838
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
@@ -806,7 +848,7 @@ async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
|
|
|
806
848
|
let skipped = 0;
|
|
807
849
|
const skillsBase = path6.join(homeDir, ".claude", "skills");
|
|
808
850
|
const exeDir = path6.join(packageRoot, "src", "commands", "exe");
|
|
809
|
-
if (
|
|
851
|
+
if (existsSync7(exeDir)) {
|
|
810
852
|
const entries = await readdir(exeDir);
|
|
811
853
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
812
854
|
for (const file of mdFiles) {
|
|
@@ -821,7 +863,7 @@ async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
|
|
|
821
863
|
}
|
|
822
864
|
}
|
|
823
865
|
const topLevelSrc = path6.join(packageRoot, "src", "commands", "exe.md");
|
|
824
|
-
if (
|
|
866
|
+
if (existsSync7(topLevelSrc)) {
|
|
825
867
|
const destDir = path6.join(skillsBase, "exe");
|
|
826
868
|
await mkdir3(destDir, { recursive: true });
|
|
827
869
|
const destPath = path6.join(destDir, "SKILL.md");
|
|
@@ -847,7 +889,7 @@ name: ${skillName}
|
|
|
847
889
|
`);
|
|
848
890
|
}
|
|
849
891
|
}
|
|
850
|
-
if (
|
|
892
|
+
if (existsSync7(destPath)) {
|
|
851
893
|
const existing = await readFile3(destPath, "utf-8");
|
|
852
894
|
if (existing === content) return false;
|
|
853
895
|
}
|
|
@@ -857,7 +899,7 @@ name: ${skillName}
|
|
|
857
899
|
async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
858
900
|
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
859
901
|
let claudeJson = {};
|
|
860
|
-
if (
|
|
902
|
+
if (existsSync7(claudeJsonPath)) {
|
|
861
903
|
try {
|
|
862
904
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
863
905
|
} catch {
|
|
@@ -874,21 +916,21 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
874
916
|
env: {}
|
|
875
917
|
};
|
|
876
918
|
const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
877
|
-
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
878
919
|
const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
|
|
879
|
-
|
|
880
|
-
|
|
920
|
+
if (claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
921
|
+
delete claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
922
|
+
}
|
|
923
|
+
if (memMatches && !claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
881
924
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
882
925
|
return false;
|
|
883
926
|
}
|
|
884
927
|
claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
|
|
885
|
-
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
886
928
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
887
929
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
888
930
|
return true;
|
|
889
931
|
}
|
|
890
932
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
891
|
-
if (!
|
|
933
|
+
if (!existsSync7(settingsPath)) return;
|
|
892
934
|
try {
|
|
893
935
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
894
936
|
const servers = settings.mcpServers;
|
|
@@ -915,7 +957,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
915
957
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
916
958
|
await mkdir3(logsDir, { recursive: true });
|
|
917
959
|
let settings = {};
|
|
918
|
-
if (
|
|
960
|
+
if (existsSync7(settingsPath)) {
|
|
919
961
|
try {
|
|
920
962
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
921
963
|
} catch {
|
|
@@ -1183,7 +1225,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1183
1225
|
}
|
|
1184
1226
|
async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
1185
1227
|
const rosterPath = path6.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1186
|
-
if (!
|
|
1228
|
+
if (!existsSync7(rosterPath)) return 0;
|
|
1187
1229
|
let employees;
|
|
1188
1230
|
try {
|
|
1189
1231
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -1204,7 +1246,7 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
1204
1246
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1205
1247
|
let totalRemoved = 0;
|
|
1206
1248
|
for (const rcPath of rcFiles) {
|
|
1207
|
-
if (!
|
|
1249
|
+
if (!existsSync7(rcPath)) continue;
|
|
1208
1250
|
let content;
|
|
1209
1251
|
try {
|
|
1210
1252
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -1337,13 +1379,13 @@ async function installStatusLine(packageRoot, homeDir = os5.homedir()) {
|
|
|
1337
1379
|
const claudeDir = path6.join(homeDir, ".claude");
|
|
1338
1380
|
await mkdir3(claudeDir, { recursive: true });
|
|
1339
1381
|
const assetPath = path6.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1340
|
-
if (!
|
|
1382
|
+
if (!existsSync7(assetPath)) return "asset-missing";
|
|
1341
1383
|
const destScript = path6.join(claudeDir, "statusline-command.sh");
|
|
1342
1384
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1343
1385
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1344
1386
|
const settingsPath = path6.join(claudeDir, "settings.json");
|
|
1345
1387
|
let settings = {};
|
|
1346
|
-
if (
|
|
1388
|
+
if (existsSync7(settingsPath)) {
|
|
1347
1389
|
try {
|
|
1348
1390
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1349
1391
|
} catch {
|
|
@@ -1382,7 +1424,7 @@ async function runInstaller(homeDir) {
|
|
|
1382
1424
|
);
|
|
1383
1425
|
const resolvedHome = homeDir ?? os5.homedir();
|
|
1384
1426
|
const exeWorkspace = path6.join(resolvedHome, "exe");
|
|
1385
|
-
if (!
|
|
1427
|
+
if (!existsSync7(exeWorkspace)) {
|
|
1386
1428
|
try {
|
|
1387
1429
|
await mkdir3(path6.join(exeWorkspace, "content"), { recursive: true });
|
|
1388
1430
|
await mkdir3(path6.join(exeWorkspace, "operations"), { recursive: true });
|
|
@@ -1429,17 +1471,17 @@ function setupTmux(home) {
|
|
|
1429
1471
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1430
1472
|
const pkgRoot = resolvePackageRoot();
|
|
1431
1473
|
const assetPath = path6.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1432
|
-
if (!
|
|
1474
|
+
if (!existsSync7(assetPath)) {
|
|
1433
1475
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1434
1476
|
`);
|
|
1435
1477
|
return;
|
|
1436
1478
|
}
|
|
1437
|
-
|
|
1479
|
+
mkdirSync3(exeDir, { recursive: true });
|
|
1438
1480
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1439
|
-
if (
|
|
1481
|
+
if (existsSync7(userTmuxConf)) {
|
|
1440
1482
|
const existing = readFileSync5(userTmuxConf, "utf8");
|
|
1441
1483
|
if (!existing.includes(sourceLine)) {
|
|
1442
|
-
if (!
|
|
1484
|
+
if (!existsSync7(backupPath)) {
|
|
1443
1485
|
copyFileSync(userTmuxConf, backupPath);
|
|
1444
1486
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1445
1487
|
`);
|
|
@@ -1462,7 +1504,7 @@ function setupGhostty(home) {
|
|
|
1462
1504
|
const homeDir = home ?? os5.homedir();
|
|
1463
1505
|
const xdgConfig = path6.join(homeDir, ".config", "ghostty");
|
|
1464
1506
|
const macConfig = path6.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1465
|
-
const ghosttyInstalled =
|
|
1507
|
+
const ghosttyInstalled = existsSync7(xdgConfig) || existsSync7(macConfig) || (() => {
|
|
1466
1508
|
try {
|
|
1467
1509
|
execSync2("which ghostty 2>/dev/null");
|
|
1468
1510
|
return true;
|
|
@@ -1475,28 +1517,28 @@ function setupGhostty(home) {
|
|
|
1475
1517
|
}
|
|
1476
1518
|
const pkgRoot = resolvePackageRoot();
|
|
1477
1519
|
const assetPath = path6.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1478
|
-
if (!
|
|
1520
|
+
if (!existsSync7(assetPath)) {
|
|
1479
1521
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1480
1522
|
return;
|
|
1481
1523
|
}
|
|
1482
1524
|
const configDir = xdgConfig;
|
|
1483
1525
|
const configPath = path6.join(configDir, "config");
|
|
1484
1526
|
const backupPath = path6.join(configDir, "config.backup");
|
|
1485
|
-
|
|
1527
|
+
mkdirSync3(configDir, { recursive: true });
|
|
1486
1528
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1487
1529
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1488
1530
|
const assetContent = readFileSync5(assetPath, "utf8").trim();
|
|
1489
1531
|
const markedSection = `${START_MARKER}
|
|
1490
1532
|
${assetContent}
|
|
1491
1533
|
${END_MARKER}`;
|
|
1492
|
-
if (
|
|
1534
|
+
if (existsSync7(configPath)) {
|
|
1493
1535
|
const existing = readFileSync5(configPath, "utf8");
|
|
1494
1536
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1495
1537
|
const before = existing.slice(0, existing.indexOf(START_MARKER));
|
|
1496
1538
|
const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
|
|
1497
1539
|
writeFileSync4(configPath, `${before}${markedSection}${after}`);
|
|
1498
1540
|
} else if (existing.includes("Exe OS")) {
|
|
1499
|
-
if (!
|
|
1541
|
+
if (!existsSync7(backupPath)) {
|
|
1500
1542
|
copyFileSync(configPath, backupPath);
|
|
1501
1543
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1502
1544
|
`);
|
|
@@ -1504,7 +1546,7 @@ ${END_MARKER}`;
|
|
|
1504
1546
|
writeFileSync4(configPath, `${markedSection}
|
|
1505
1547
|
`);
|
|
1506
1548
|
} else {
|
|
1507
|
-
if (!
|
|
1549
|
+
if (!existsSync7(backupPath)) {
|
|
1508
1550
|
copyFileSync(configPath, backupPath);
|
|
1509
1551
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1510
1552
|
`);
|
|
@@ -1564,7 +1606,7 @@ __export(keychain_exports, {
|
|
|
1564
1606
|
setMasterKey: () => setMasterKey
|
|
1565
1607
|
});
|
|
1566
1608
|
import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
1567
|
-
import { existsSync as
|
|
1609
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1568
1610
|
import path7 from "path";
|
|
1569
1611
|
import os6 from "os";
|
|
1570
1612
|
function getKeyDir() {
|
|
@@ -1592,7 +1634,7 @@ async function getMasterKey() {
|
|
|
1592
1634
|
}
|
|
1593
1635
|
}
|
|
1594
1636
|
const keyPath = getKeyPath();
|
|
1595
|
-
if (!
|
|
1637
|
+
if (!existsSync8(keyPath)) {
|
|
1596
1638
|
process.stderr.write(
|
|
1597
1639
|
`[keychain] Key not found at ${keyPath} (HOME=${os6.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
1598
1640
|
`
|
|
@@ -1635,7 +1677,7 @@ async function deleteMasterKey() {
|
|
|
1635
1677
|
}
|
|
1636
1678
|
}
|
|
1637
1679
|
const keyPath = getKeyPath();
|
|
1638
|
-
if (
|
|
1680
|
+
if (existsSync8(keyPath)) {
|
|
1639
1681
|
await unlink(keyPath);
|
|
1640
1682
|
}
|
|
1641
1683
|
}
|
|
@@ -2363,13 +2405,50 @@ var init_database_adapter = __esm({
|
|
|
2363
2405
|
}
|
|
2364
2406
|
});
|
|
2365
2407
|
|
|
2408
|
+
// src/lib/daemon-auth.ts
|
|
2409
|
+
import crypto2 from "crypto";
|
|
2410
|
+
import path9 from "path";
|
|
2411
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
2412
|
+
function normalizeToken(token) {
|
|
2413
|
+
if (!token) return null;
|
|
2414
|
+
const trimmed = token.trim();
|
|
2415
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2416
|
+
}
|
|
2417
|
+
function readDaemonToken() {
|
|
2418
|
+
try {
|
|
2419
|
+
if (!existsSync9(DAEMON_TOKEN_PATH)) return null;
|
|
2420
|
+
return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
|
|
2421
|
+
} catch {
|
|
2422
|
+
return null;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
function ensureDaemonToken(seed) {
|
|
2426
|
+
const existing = readDaemonToken();
|
|
2427
|
+
if (existing) return existing;
|
|
2428
|
+
const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
|
|
2429
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2430
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
2431
|
+
`, "utf8");
|
|
2432
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2433
|
+
return token;
|
|
2434
|
+
}
|
|
2435
|
+
var DAEMON_TOKEN_PATH;
|
|
2436
|
+
var init_daemon_auth = __esm({
|
|
2437
|
+
"src/lib/daemon-auth.ts"() {
|
|
2438
|
+
"use strict";
|
|
2439
|
+
init_config();
|
|
2440
|
+
init_secure_files();
|
|
2441
|
+
DAEMON_TOKEN_PATH = path9.join(EXE_AI_DIR, "exed.token");
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
|
|
2366
2445
|
// src/lib/exe-daemon-client.ts
|
|
2367
2446
|
import net from "net";
|
|
2368
2447
|
import os8 from "os";
|
|
2369
2448
|
import { spawn } from "child_process";
|
|
2370
2449
|
import { randomUUID } from "crypto";
|
|
2371
|
-
import { existsSync as
|
|
2372
|
-
import
|
|
2450
|
+
import { existsSync as existsSync10, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
2451
|
+
import path10 from "path";
|
|
2373
2452
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2374
2453
|
function handleData(chunk) {
|
|
2375
2454
|
_buffer += chunk.toString();
|
|
@@ -2397,9 +2476,9 @@ function handleData(chunk) {
|
|
|
2397
2476
|
}
|
|
2398
2477
|
}
|
|
2399
2478
|
function cleanupStaleFiles() {
|
|
2400
|
-
if (
|
|
2479
|
+
if (existsSync10(PID_PATH)) {
|
|
2401
2480
|
try {
|
|
2402
|
-
const pid = parseInt(
|
|
2481
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
2403
2482
|
if (pid > 0) {
|
|
2404
2483
|
try {
|
|
2405
2484
|
process.kill(pid, 0);
|
|
@@ -2420,11 +2499,11 @@ function cleanupStaleFiles() {
|
|
|
2420
2499
|
}
|
|
2421
2500
|
}
|
|
2422
2501
|
function findPackageRoot() {
|
|
2423
|
-
let dir =
|
|
2424
|
-
const { root } =
|
|
2502
|
+
let dir = path10.dirname(fileURLToPath3(import.meta.url));
|
|
2503
|
+
const { root } = path10.parse(dir);
|
|
2425
2504
|
while (dir !== root) {
|
|
2426
|
-
if (
|
|
2427
|
-
dir =
|
|
2505
|
+
if (existsSync10(path10.join(dir, "package.json"))) return dir;
|
|
2506
|
+
dir = path10.dirname(dir);
|
|
2428
2507
|
}
|
|
2429
2508
|
return null;
|
|
2430
2509
|
}
|
|
@@ -2450,16 +2529,17 @@ function spawnDaemon() {
|
|
|
2450
2529
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2451
2530
|
return;
|
|
2452
2531
|
}
|
|
2453
|
-
const daemonPath =
|
|
2454
|
-
if (!
|
|
2532
|
+
const daemonPath = path10.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2533
|
+
if (!existsSync10(daemonPath)) {
|
|
2455
2534
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2456
2535
|
`);
|
|
2457
2536
|
return;
|
|
2458
2537
|
}
|
|
2459
2538
|
const resolvedPath = daemonPath;
|
|
2539
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2460
2540
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2461
2541
|
`);
|
|
2462
|
-
const logPath =
|
|
2542
|
+
const logPath = path10.join(path10.dirname(SOCKET_PATH), "exed.log");
|
|
2463
2543
|
let stderrFd = "ignore";
|
|
2464
2544
|
try {
|
|
2465
2545
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2477,7 +2557,8 @@ function spawnDaemon() {
|
|
|
2477
2557
|
TMUX_PANE: void 0,
|
|
2478
2558
|
// Prevents resolveExeSession() from scoping to one session
|
|
2479
2559
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2480
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2560
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2561
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2481
2562
|
}
|
|
2482
2563
|
});
|
|
2483
2564
|
child.unref();
|
|
@@ -2587,13 +2668,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2587
2668
|
return;
|
|
2588
2669
|
}
|
|
2589
2670
|
const id = randomUUID();
|
|
2671
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2590
2672
|
const timer = setTimeout(() => {
|
|
2591
2673
|
_pending.delete(id);
|
|
2592
2674
|
resolve({ error: "Request timeout" });
|
|
2593
2675
|
}, timeoutMs);
|
|
2594
2676
|
_pending.set(id, { resolve, timer });
|
|
2595
2677
|
try {
|
|
2596
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2678
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2597
2679
|
} catch {
|
|
2598
2680
|
clearTimeout(timer);
|
|
2599
2681
|
_pending.delete(id);
|
|
@@ -2622,9 +2704,9 @@ function killAndRespawnDaemon() {
|
|
|
2622
2704
|
}
|
|
2623
2705
|
try {
|
|
2624
2706
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2625
|
-
if (
|
|
2707
|
+
if (existsSync10(PID_PATH)) {
|
|
2626
2708
|
try {
|
|
2627
|
-
const pid = parseInt(
|
|
2709
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
2628
2710
|
if (pid > 0) {
|
|
2629
2711
|
try {
|
|
2630
2712
|
process.kill(pid, "SIGKILL");
|
|
@@ -2744,17 +2826,19 @@ function disconnectClient() {
|
|
|
2744
2826
|
function isClientConnected() {
|
|
2745
2827
|
return _connected;
|
|
2746
2828
|
}
|
|
2747
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
2829
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
2748
2830
|
var init_exe_daemon_client = __esm({
|
|
2749
2831
|
"src/lib/exe-daemon-client.ts"() {
|
|
2750
2832
|
"use strict";
|
|
2751
2833
|
init_config();
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2834
|
+
init_daemon_auth();
|
|
2835
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path10.join(EXE_AI_DIR, "exed.sock");
|
|
2836
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path10.join(EXE_AI_DIR, "exed.pid");
|
|
2837
|
+
SPAWN_LOCK_PATH = path10.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2755
2838
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2756
2839
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2757
2840
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2841
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2758
2842
|
_socket = null;
|
|
2759
2843
|
_connected = false;
|
|
2760
2844
|
_buffer = "";
|
|
@@ -3339,6 +3423,7 @@ async function ensureSchema() {
|
|
|
3339
3423
|
project TEXT NOT NULL,
|
|
3340
3424
|
summary TEXT NOT NULL,
|
|
3341
3425
|
task_file TEXT,
|
|
3426
|
+
session_scope TEXT,
|
|
3342
3427
|
read INTEGER NOT NULL DEFAULT 0,
|
|
3343
3428
|
created_at TEXT NOT NULL
|
|
3344
3429
|
);
|
|
@@ -3347,7 +3432,7 @@ async function ensureSchema() {
|
|
|
3347
3432
|
ON notifications(read);
|
|
3348
3433
|
|
|
3349
3434
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
3350
|
-
ON notifications(agent_id);
|
|
3435
|
+
ON notifications(agent_id, session_scope);
|
|
3351
3436
|
|
|
3352
3437
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
3353
3438
|
ON notifications(task_file);
|
|
@@ -3385,6 +3470,7 @@ async function ensureSchema() {
|
|
|
3385
3470
|
target_agent TEXT NOT NULL,
|
|
3386
3471
|
target_project TEXT,
|
|
3387
3472
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
3473
|
+
session_scope TEXT,
|
|
3388
3474
|
content TEXT NOT NULL,
|
|
3389
3475
|
priority TEXT DEFAULT 'normal',
|
|
3390
3476
|
status TEXT DEFAULT 'pending',
|
|
@@ -3398,10 +3484,31 @@ async function ensureSchema() {
|
|
|
3398
3484
|
);
|
|
3399
3485
|
|
|
3400
3486
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
3401
|
-
ON messages(target_agent, status);
|
|
3487
|
+
ON messages(target_agent, session_scope, status);
|
|
3402
3488
|
|
|
3403
3489
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
3404
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
3490
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
3491
|
+
`);
|
|
3492
|
+
try {
|
|
3493
|
+
await client.execute({
|
|
3494
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
3495
|
+
args: []
|
|
3496
|
+
});
|
|
3497
|
+
} catch {
|
|
3498
|
+
}
|
|
3499
|
+
try {
|
|
3500
|
+
await client.execute({
|
|
3501
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
3502
|
+
args: []
|
|
3503
|
+
});
|
|
3504
|
+
} catch {
|
|
3505
|
+
}
|
|
3506
|
+
await client.executeMultiple(`
|
|
3507
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
3508
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
3509
|
+
|
|
3510
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
3511
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
3405
3512
|
`);
|
|
3406
3513
|
try {
|
|
3407
3514
|
await client.execute({
|
|
@@ -3985,6 +4092,13 @@ async function ensureSchema() {
|
|
|
3985
4092
|
} catch {
|
|
3986
4093
|
}
|
|
3987
4094
|
}
|
|
4095
|
+
try {
|
|
4096
|
+
await client.execute({
|
|
4097
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
4098
|
+
args: []
|
|
4099
|
+
});
|
|
4100
|
+
} catch {
|
|
4101
|
+
}
|
|
3988
4102
|
}
|
|
3989
4103
|
async function disposeDatabase() {
|
|
3990
4104
|
if (_walCheckpointTimer) {
|
|
@@ -4030,13 +4144,13 @@ __export(crypto_exports, {
|
|
|
4030
4144
|
initSyncCrypto: () => initSyncCrypto,
|
|
4031
4145
|
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
4032
4146
|
});
|
|
4033
|
-
import
|
|
4147
|
+
import crypto3 from "crypto";
|
|
4034
4148
|
function initSyncCrypto(masterKey) {
|
|
4035
4149
|
if (masterKey.length !== 32) {
|
|
4036
4150
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
4037
4151
|
}
|
|
4038
4152
|
_syncKey = Buffer.from(
|
|
4039
|
-
|
|
4153
|
+
crypto3.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
4040
4154
|
);
|
|
4041
4155
|
}
|
|
4042
4156
|
function isSyncCryptoInitialized() {
|
|
@@ -4050,8 +4164,8 @@ function requireSyncKey() {
|
|
|
4050
4164
|
}
|
|
4051
4165
|
function encryptSyncBlob(data) {
|
|
4052
4166
|
const key = requireSyncKey();
|
|
4053
|
-
const iv =
|
|
4054
|
-
const cipher =
|
|
4167
|
+
const iv = crypto3.randomBytes(IV_LENGTH);
|
|
4168
|
+
const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
|
|
4055
4169
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
4056
4170
|
const tag = cipher.getAuthTag();
|
|
4057
4171
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -4065,7 +4179,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
4065
4179
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
4066
4180
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
4067
4181
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
4068
|
-
const decipher =
|
|
4182
|
+
const decipher = crypto3.createDecipheriv(ALGORITHM, key, iv);
|
|
4069
4183
|
decipher.setAuthTag(tag);
|
|
4070
4184
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
4071
4185
|
}
|
|
@@ -4118,9 +4232,12 @@ __export(license_exports, {
|
|
|
4118
4232
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4119
4233
|
validateLicense: () => validateLicense
|
|
4120
4234
|
});
|
|
4121
|
-
import { readFileSync as
|
|
4235
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
|
|
4122
4236
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4123
|
-
import
|
|
4237
|
+
import { createRequire as createRequire2 } from "module";
|
|
4238
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4239
|
+
import os9 from "os";
|
|
4240
|
+
import path11 from "path";
|
|
4124
4241
|
import { jwtVerify, importSPKI } from "jose";
|
|
4125
4242
|
async function fetchRetry(url, init) {
|
|
4126
4243
|
try {
|
|
@@ -4131,37 +4248,37 @@ async function fetchRetry(url, init) {
|
|
|
4131
4248
|
}
|
|
4132
4249
|
}
|
|
4133
4250
|
function loadDeviceId() {
|
|
4134
|
-
const deviceJsonPath =
|
|
4251
|
+
const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
|
|
4135
4252
|
try {
|
|
4136
|
-
if (
|
|
4137
|
-
const data = JSON.parse(
|
|
4253
|
+
if (existsSync11(deviceJsonPath)) {
|
|
4254
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
4138
4255
|
if (data.deviceId) return data.deviceId;
|
|
4139
4256
|
}
|
|
4140
4257
|
} catch {
|
|
4141
4258
|
}
|
|
4142
4259
|
try {
|
|
4143
|
-
if (
|
|
4144
|
-
const id2 =
|
|
4260
|
+
if (existsSync11(DEVICE_ID_PATH)) {
|
|
4261
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
4145
4262
|
if (id2) return id2;
|
|
4146
4263
|
}
|
|
4147
4264
|
} catch {
|
|
4148
4265
|
}
|
|
4149
4266
|
const id = randomUUID2();
|
|
4150
|
-
|
|
4151
|
-
|
|
4267
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
4268
|
+
writeFileSync6(DEVICE_ID_PATH, id, "utf8");
|
|
4152
4269
|
return id;
|
|
4153
4270
|
}
|
|
4154
4271
|
function loadLicense() {
|
|
4155
4272
|
try {
|
|
4156
|
-
if (!
|
|
4157
|
-
return
|
|
4273
|
+
if (!existsSync11(LICENSE_PATH)) return null;
|
|
4274
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
4158
4275
|
} catch {
|
|
4159
4276
|
return null;
|
|
4160
4277
|
}
|
|
4161
4278
|
}
|
|
4162
4279
|
function saveLicense(apiKey) {
|
|
4163
|
-
|
|
4164
|
-
|
|
4280
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
4281
|
+
writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
4165
4282
|
}
|
|
4166
4283
|
async function verifyLicenseJwt(token) {
|
|
4167
4284
|
try {
|
|
@@ -4187,8 +4304,8 @@ async function verifyLicenseJwt(token) {
|
|
|
4187
4304
|
}
|
|
4188
4305
|
async function getCachedLicense() {
|
|
4189
4306
|
try {
|
|
4190
|
-
if (!
|
|
4191
|
-
const raw = JSON.parse(
|
|
4307
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
4308
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
4192
4309
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
4193
4310
|
return await verifyLicenseJwt(raw.token);
|
|
4194
4311
|
} catch {
|
|
@@ -4197,8 +4314,8 @@ async function getCachedLicense() {
|
|
|
4197
4314
|
}
|
|
4198
4315
|
function readCachedToken() {
|
|
4199
4316
|
try {
|
|
4200
|
-
if (!
|
|
4201
|
-
const raw = JSON.parse(
|
|
4317
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
4318
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
4202
4319
|
return typeof raw.token === "string" ? raw.token : null;
|
|
4203
4320
|
} catch {
|
|
4204
4321
|
return null;
|
|
@@ -4232,56 +4349,130 @@ function getRawCachedPlan() {
|
|
|
4232
4349
|
}
|
|
4233
4350
|
function cacheResponse(token) {
|
|
4234
4351
|
try {
|
|
4235
|
-
|
|
4352
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
4236
4353
|
} catch {
|
|
4237
4354
|
}
|
|
4238
4355
|
}
|
|
4239
|
-
|
|
4240
|
-
|
|
4356
|
+
function loadPrismaForLicense() {
|
|
4357
|
+
if (_prismaFailed) return null;
|
|
4358
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
4359
|
+
if (!dbUrl) {
|
|
4360
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os9.homedir(), "exe-db");
|
|
4361
|
+
if (!existsSync11(path11.join(exeDbRoot, "package.json"))) {
|
|
4362
|
+
_prismaFailed = true;
|
|
4363
|
+
return null;
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
if (!_prismaPromise) {
|
|
4367
|
+
_prismaPromise = (async () => {
|
|
4368
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
4369
|
+
if (explicitPath) {
|
|
4370
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
4371
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
4372
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
4373
|
+
return new Ctor2();
|
|
4374
|
+
}
|
|
4375
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os9.homedir(), "exe-db");
|
|
4376
|
+
const req = createRequire2(path11.join(exeDbRoot, "package.json"));
|
|
4377
|
+
const entry = req.resolve("@prisma/client");
|
|
4378
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
4379
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4380
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
4381
|
+
return new Ctor();
|
|
4382
|
+
})().catch((err) => {
|
|
4383
|
+
_prismaFailed = true;
|
|
4384
|
+
_prismaPromise = null;
|
|
4385
|
+
throw err;
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
return _prismaPromise;
|
|
4389
|
+
}
|
|
4390
|
+
async function validateViaPostgres(apiKey) {
|
|
4391
|
+
const loader = loadPrismaForLicense();
|
|
4392
|
+
if (!loader) return null;
|
|
4393
|
+
try {
|
|
4394
|
+
const prisma = await loader;
|
|
4395
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
4396
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
4397
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
4398
|
+
apiKey
|
|
4399
|
+
);
|
|
4400
|
+
if (!rows || rows.length === 0) return null;
|
|
4401
|
+
const row = rows[0];
|
|
4402
|
+
if (row.status !== "active") return null;
|
|
4403
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
4404
|
+
const plan = row.plan;
|
|
4405
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
4406
|
+
return {
|
|
4407
|
+
valid: true,
|
|
4408
|
+
plan,
|
|
4409
|
+
email: row.email,
|
|
4410
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
4411
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
4412
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
4413
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
4414
|
+
};
|
|
4415
|
+
} catch {
|
|
4416
|
+
return null;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
4241
4420
|
try {
|
|
4242
4421
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
4243
4422
|
method: "POST",
|
|
4244
4423
|
headers: { "Content-Type": "application/json" },
|
|
4245
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
4424
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
4246
4425
|
signal: AbortSignal.timeout(1e4)
|
|
4247
4426
|
});
|
|
4248
|
-
if (res.ok)
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4427
|
+
if (!res.ok) return null;
|
|
4428
|
+
const data = await res.json();
|
|
4429
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
4430
|
+
if (!data.valid) return null;
|
|
4431
|
+
if (data.token) {
|
|
4432
|
+
cacheResponse(data.token);
|
|
4433
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
4434
|
+
if (verified) return verified;
|
|
4435
|
+
}
|
|
4436
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
4437
|
+
return {
|
|
4438
|
+
valid: data.valid,
|
|
4439
|
+
plan: data.plan,
|
|
4440
|
+
email: data.email,
|
|
4441
|
+
expiresAt: data.expiresAt,
|
|
4442
|
+
deviceLimit: limits.devices,
|
|
4443
|
+
employeeLimit: limits.employees,
|
|
4444
|
+
memoryLimit: limits.memories
|
|
4445
|
+
};
|
|
4446
|
+
} catch {
|
|
4447
|
+
return null;
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
async function validateLicense(apiKey, deviceId) {
|
|
4451
|
+
const did = deviceId ?? loadDeviceId();
|
|
4452
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
4453
|
+
if (pgResult) {
|
|
4454
|
+
try {
|
|
4455
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
4456
|
+
} catch {
|
|
4457
|
+
}
|
|
4458
|
+
return pgResult;
|
|
4459
|
+
}
|
|
4460
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
4461
|
+
if (cfResult) return cfResult;
|
|
4462
|
+
const cached = await getCachedLicense();
|
|
4463
|
+
if (cached) return cached;
|
|
4464
|
+
try {
|
|
4465
|
+
if (existsSync11(CACHE_PATH)) {
|
|
4466
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
4467
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
4468
|
+
return raw.pgLicense;
|
|
4469
|
+
}
|
|
4272
4470
|
}
|
|
4273
|
-
const cached = await getCachedLicense();
|
|
4274
|
-
if (cached) return cached;
|
|
4275
|
-
const raw = getRawCachedPlan();
|
|
4276
|
-
if (raw) return raw;
|
|
4277
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
4278
4471
|
} catch {
|
|
4279
|
-
const cached = await getCachedLicense();
|
|
4280
|
-
if (cached) return cached;
|
|
4281
|
-
const rawFallback = getRawCachedPlan();
|
|
4282
|
-
if (rawFallback) return rawFallback;
|
|
4283
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
4284
4472
|
}
|
|
4473
|
+
const rawFallback = getRawCachedPlan();
|
|
4474
|
+
if (rawFallback) return rawFallback;
|
|
4475
|
+
return { ...FREE_LICENSE, valid: false };
|
|
4285
4476
|
}
|
|
4286
4477
|
function getCacheAgeMs() {
|
|
4287
4478
|
try {
|
|
@@ -4296,9 +4487,9 @@ async function checkLicense() {
|
|
|
4296
4487
|
let key = loadLicense();
|
|
4297
4488
|
if (!key) {
|
|
4298
4489
|
try {
|
|
4299
|
-
const configPath =
|
|
4300
|
-
if (
|
|
4301
|
-
const raw = JSON.parse(
|
|
4490
|
+
const configPath = path11.join(EXE_AI_DIR, "config.json");
|
|
4491
|
+
if (existsSync11(configPath)) {
|
|
4492
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
4302
4493
|
const cloud = raw.cloud;
|
|
4303
4494
|
if (cloud?.apiKey) {
|
|
4304
4495
|
key = cloud.apiKey;
|
|
@@ -4452,14 +4643,14 @@ function stopLicenseRevalidation() {
|
|
|
4452
4643
|
_revalTimer = null;
|
|
4453
4644
|
}
|
|
4454
4645
|
}
|
|
4455
|
-
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;
|
|
4646
|
+
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;
|
|
4456
4647
|
var init_license = __esm({
|
|
4457
4648
|
"src/lib/license.ts"() {
|
|
4458
4649
|
"use strict";
|
|
4459
4650
|
init_config();
|
|
4460
|
-
LICENSE_PATH =
|
|
4461
|
-
CACHE_PATH =
|
|
4462
|
-
DEVICE_ID_PATH =
|
|
4651
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
4652
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4653
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4463
4654
|
API_BASE = "https://askexe.com/cloud";
|
|
4464
4655
|
RETRY_DELAY_MS = 500;
|
|
4465
4656
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -4483,6 +4674,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
4483
4674
|
employeeLimit: 1,
|
|
4484
4675
|
memoryLimit: 5e3
|
|
4485
4676
|
};
|
|
4677
|
+
_prismaPromise = null;
|
|
4678
|
+
_prismaFailed = false;
|
|
4486
4679
|
CACHE_MAX_AGE_MS = 36e5;
|
|
4487
4680
|
_revalTimer = null;
|
|
4488
4681
|
}
|
|
@@ -4507,8 +4700,8 @@ __export(crdt_sync_exports, {
|
|
|
4507
4700
|
rebuildFromDb: () => rebuildFromDb
|
|
4508
4701
|
});
|
|
4509
4702
|
import * as Y from "yjs";
|
|
4510
|
-
import { readFileSync as
|
|
4511
|
-
import
|
|
4703
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
4704
|
+
import path12 from "path";
|
|
4512
4705
|
import { homedir } from "os";
|
|
4513
4706
|
function getStatePath() {
|
|
4514
4707
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -4520,9 +4713,9 @@ function initCrdtDoc() {
|
|
|
4520
4713
|
if (doc) return doc;
|
|
4521
4714
|
doc = new Y.Doc();
|
|
4522
4715
|
const sp = getStatePath();
|
|
4523
|
-
if (
|
|
4716
|
+
if (existsSync12(sp)) {
|
|
4524
4717
|
try {
|
|
4525
|
-
const state =
|
|
4718
|
+
const state = readFileSync9(sp);
|
|
4526
4719
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
4527
4720
|
} catch {
|
|
4528
4721
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -4664,10 +4857,10 @@ function persistState() {
|
|
|
4664
4857
|
if (!doc) return;
|
|
4665
4858
|
try {
|
|
4666
4859
|
const sp = getStatePath();
|
|
4667
|
-
const dir =
|
|
4668
|
-
if (!
|
|
4860
|
+
const dir = path12.dirname(sp);
|
|
4861
|
+
if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
|
|
4669
4862
|
const state = Y.encodeStateAsUpdate(doc);
|
|
4670
|
-
|
|
4863
|
+
writeFileSync7(sp, Buffer.from(state));
|
|
4671
4864
|
} catch {
|
|
4672
4865
|
}
|
|
4673
4866
|
}
|
|
@@ -4708,7 +4901,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
4708
4901
|
var init_crdt_sync = __esm({
|
|
4709
4902
|
"src/lib/crdt-sync.ts"() {
|
|
4710
4903
|
"use strict";
|
|
4711
|
-
DEFAULT_STATE_PATH =
|
|
4904
|
+
DEFAULT_STATE_PATH = path12.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
4712
4905
|
_statePathOverride = null;
|
|
4713
4906
|
doc = null;
|
|
4714
4907
|
}
|
|
@@ -4740,39 +4933,107 @@ __export(cloud_sync_exports, {
|
|
|
4740
4933
|
cloudSync: () => cloudSync,
|
|
4741
4934
|
mergeConfig: () => mergeConfig,
|
|
4742
4935
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4936
|
+
pushToPostgres: () => pushToPostgres,
|
|
4743
4937
|
recordRosterDeletion: () => recordRosterDeletion
|
|
4744
4938
|
});
|
|
4745
|
-
import { readFileSync as
|
|
4746
|
-
import
|
|
4747
|
-
import
|
|
4939
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync13, readdirSync, mkdirSync as mkdirSync6, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
4940
|
+
import crypto4 from "crypto";
|
|
4941
|
+
import path13 from "path";
|
|
4748
4942
|
import { homedir as homedir2 } from "os";
|
|
4749
4943
|
function sqlSafe(v) {
|
|
4750
4944
|
return v === void 0 ? null : v;
|
|
4751
4945
|
}
|
|
4752
4946
|
function logError(msg) {
|
|
4753
4947
|
try {
|
|
4754
|
-
const logPath =
|
|
4948
|
+
const logPath = path13.join(homedir2(), ".exe-os", "workers.log");
|
|
4755
4949
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
4756
4950
|
`);
|
|
4757
4951
|
} catch {
|
|
4758
4952
|
}
|
|
4759
4953
|
}
|
|
4954
|
+
function loadPgClient() {
|
|
4955
|
+
if (_pgFailed) return null;
|
|
4956
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
4957
|
+
const configPath = path13.join(EXE_AI_DIR, "config.json");
|
|
4958
|
+
let cloudPostgresUrl;
|
|
4959
|
+
try {
|
|
4960
|
+
if (existsSync13(configPath)) {
|
|
4961
|
+
const cfg = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
4962
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
4963
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
4964
|
+
_pgFailed = true;
|
|
4965
|
+
return null;
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
} catch {
|
|
4969
|
+
}
|
|
4970
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
4971
|
+
if (!url) {
|
|
4972
|
+
_pgFailed = true;
|
|
4973
|
+
return null;
|
|
4974
|
+
}
|
|
4975
|
+
if (!_pgPromise) {
|
|
4976
|
+
_pgPromise = (async () => {
|
|
4977
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
4978
|
+
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
4979
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(homedir2(), "exe-db");
|
|
4980
|
+
const req = createRequire3(path13.join(exeDbRoot, "package.json"));
|
|
4981
|
+
const entry = req.resolve("@prisma/client");
|
|
4982
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
4983
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4984
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
4985
|
+
return new Ctor();
|
|
4986
|
+
})().catch(() => {
|
|
4987
|
+
_pgFailed = true;
|
|
4988
|
+
_pgPromise = null;
|
|
4989
|
+
throw new Error("pg_unavailable");
|
|
4990
|
+
});
|
|
4991
|
+
}
|
|
4992
|
+
return _pgPromise;
|
|
4993
|
+
}
|
|
4994
|
+
async function pushToPostgres(records) {
|
|
4995
|
+
const loader = loadPgClient();
|
|
4996
|
+
if (!loader) return 0;
|
|
4997
|
+
let prisma;
|
|
4998
|
+
try {
|
|
4999
|
+
prisma = await loader;
|
|
5000
|
+
} catch {
|
|
5001
|
+
return 0;
|
|
5002
|
+
}
|
|
5003
|
+
let inserted = 0;
|
|
5004
|
+
for (const rec of records) {
|
|
5005
|
+
try {
|
|
5006
|
+
await prisma.$executeRawUnsafe(
|
|
5007
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
5008
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
5009
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
5010
|
+
String(rec.id ?? ""),
|
|
5011
|
+
JSON.stringify(rec),
|
|
5012
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
5013
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
5014
|
+
);
|
|
5015
|
+
inserted++;
|
|
5016
|
+
} catch {
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
return inserted;
|
|
5020
|
+
}
|
|
4760
5021
|
async function withRosterLock(fn) {
|
|
4761
5022
|
try {
|
|
4762
5023
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
4763
5024
|
closeSync2(fd);
|
|
4764
|
-
|
|
5025
|
+
writeFileSync8(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4765
5026
|
} catch (err) {
|
|
4766
5027
|
if (err.code === "EEXIST") {
|
|
4767
5028
|
try {
|
|
4768
|
-
const ts = parseInt(
|
|
5029
|
+
const ts = parseInt(readFileSync10(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
4769
5030
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
4770
5031
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
4771
5032
|
}
|
|
4772
5033
|
unlinkSync4(ROSTER_LOCK_PATH);
|
|
4773
5034
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
4774
5035
|
closeSync2(fd);
|
|
4775
|
-
|
|
5036
|
+
writeFileSync8(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4776
5037
|
} catch (retryErr) {
|
|
4777
5038
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
4778
5039
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -5042,6 +5303,10 @@ async function cloudSync(config) {
|
|
|
5042
5303
|
const maxVersion = Number(records[records.length - 1].version);
|
|
5043
5304
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
5044
5305
|
if (!pushOk) break;
|
|
5306
|
+
try {
|
|
5307
|
+
await pushToPostgres(records);
|
|
5308
|
+
} catch {
|
|
5309
|
+
}
|
|
5045
5310
|
await client.execute({
|
|
5046
5311
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
5047
5312
|
args: [String(maxVersion)]
|
|
@@ -5146,8 +5411,8 @@ async function cloudSync(config) {
|
|
|
5146
5411
|
try {
|
|
5147
5412
|
const employees = await loadEmployees();
|
|
5148
5413
|
rosterResult.employees = employees.length;
|
|
5149
|
-
const idDir =
|
|
5150
|
-
if (
|
|
5414
|
+
const idDir = path13.join(EXE_AI_DIR, "identity");
|
|
5415
|
+
if (existsSync13(idDir)) {
|
|
5151
5416
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
5152
5417
|
}
|
|
5153
5418
|
} catch {
|
|
@@ -5168,62 +5433,62 @@ async function cloudSync(config) {
|
|
|
5168
5433
|
function recordRosterDeletion(name) {
|
|
5169
5434
|
let deletions = [];
|
|
5170
5435
|
try {
|
|
5171
|
-
if (
|
|
5172
|
-
deletions = JSON.parse(
|
|
5436
|
+
if (existsSync13(ROSTER_DELETIONS_PATH)) {
|
|
5437
|
+
deletions = JSON.parse(readFileSync10(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5173
5438
|
}
|
|
5174
5439
|
} catch {
|
|
5175
5440
|
}
|
|
5176
5441
|
if (!deletions.includes(name)) deletions.push(name);
|
|
5177
|
-
|
|
5442
|
+
writeFileSync8(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
5178
5443
|
}
|
|
5179
5444
|
function consumeRosterDeletions() {
|
|
5180
5445
|
try {
|
|
5181
|
-
if (!
|
|
5182
|
-
const deletions = JSON.parse(
|
|
5183
|
-
|
|
5446
|
+
if (!existsSync13(ROSTER_DELETIONS_PATH)) return [];
|
|
5447
|
+
const deletions = JSON.parse(readFileSync10(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5448
|
+
writeFileSync8(ROSTER_DELETIONS_PATH, "[]");
|
|
5184
5449
|
return deletions;
|
|
5185
5450
|
} catch {
|
|
5186
5451
|
return [];
|
|
5187
5452
|
}
|
|
5188
5453
|
}
|
|
5189
5454
|
function buildRosterBlob(paths) {
|
|
5190
|
-
const rosterPath = paths?.rosterPath ??
|
|
5191
|
-
const identityDir = paths?.identityDir ??
|
|
5192
|
-
const configPath = paths?.configPath ??
|
|
5455
|
+
const rosterPath = paths?.rosterPath ?? path13.join(EXE_AI_DIR, "exe-employees.json");
|
|
5456
|
+
const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
|
|
5457
|
+
const configPath = paths?.configPath ?? path13.join(EXE_AI_DIR, "config.json");
|
|
5193
5458
|
let roster = [];
|
|
5194
|
-
if (
|
|
5459
|
+
if (existsSync13(rosterPath)) {
|
|
5195
5460
|
try {
|
|
5196
|
-
roster = JSON.parse(
|
|
5461
|
+
roster = JSON.parse(readFileSync10(rosterPath, "utf-8"));
|
|
5197
5462
|
} catch {
|
|
5198
5463
|
}
|
|
5199
5464
|
}
|
|
5200
5465
|
const identities = {};
|
|
5201
|
-
if (
|
|
5466
|
+
if (existsSync13(identityDir)) {
|
|
5202
5467
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
5203
5468
|
try {
|
|
5204
|
-
identities[file] =
|
|
5469
|
+
identities[file] = readFileSync10(path13.join(identityDir, file), "utf-8");
|
|
5205
5470
|
} catch {
|
|
5206
5471
|
}
|
|
5207
5472
|
}
|
|
5208
5473
|
}
|
|
5209
5474
|
let config;
|
|
5210
|
-
if (
|
|
5475
|
+
if (existsSync13(configPath)) {
|
|
5211
5476
|
try {
|
|
5212
|
-
config = JSON.parse(
|
|
5477
|
+
config = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
5213
5478
|
} catch {
|
|
5214
5479
|
}
|
|
5215
5480
|
}
|
|
5216
5481
|
let agentConfig;
|
|
5217
|
-
const agentConfigPath =
|
|
5218
|
-
if (
|
|
5482
|
+
const agentConfigPath = path13.join(EXE_AI_DIR, "agent-config.json");
|
|
5483
|
+
if (existsSync13(agentConfigPath)) {
|
|
5219
5484
|
try {
|
|
5220
|
-
agentConfig = JSON.parse(
|
|
5485
|
+
agentConfig = JSON.parse(readFileSync10(agentConfigPath, "utf-8"));
|
|
5221
5486
|
} catch {
|
|
5222
5487
|
}
|
|
5223
5488
|
}
|
|
5224
5489
|
const deletedNames = consumeRosterDeletions();
|
|
5225
5490
|
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
5226
|
-
const hash =
|
|
5491
|
+
const hash = crypto4.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5227
5492
|
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
5228
5493
|
}
|
|
5229
5494
|
async function cloudPushRoster(config) {
|
|
@@ -5293,23 +5558,24 @@ async function cloudPullRoster(config) {
|
|
|
5293
5558
|
}
|
|
5294
5559
|
}
|
|
5295
5560
|
function mergeConfig(remoteConfig, configPath) {
|
|
5296
|
-
const cfgPath = configPath ??
|
|
5561
|
+
const cfgPath = configPath ?? path13.join(EXE_AI_DIR, "config.json");
|
|
5297
5562
|
let local = {};
|
|
5298
|
-
if (
|
|
5563
|
+
if (existsSync13(cfgPath)) {
|
|
5299
5564
|
try {
|
|
5300
|
-
local = JSON.parse(
|
|
5565
|
+
local = JSON.parse(readFileSync10(cfgPath, "utf-8"));
|
|
5301
5566
|
} catch {
|
|
5302
5567
|
}
|
|
5303
5568
|
}
|
|
5304
5569
|
const merged = { ...remoteConfig, ...local };
|
|
5305
|
-
const dir =
|
|
5306
|
-
|
|
5307
|
-
|
|
5570
|
+
const dir = path13.dirname(cfgPath);
|
|
5571
|
+
ensurePrivateDirSync(dir);
|
|
5572
|
+
writeFileSync8(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
5573
|
+
enforcePrivateFileSync(cfgPath);
|
|
5308
5574
|
}
|
|
5309
5575
|
async function mergeRosterFromRemote(remote, paths) {
|
|
5310
5576
|
return withRosterLock(async () => {
|
|
5311
5577
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
5312
|
-
const identityDir = paths?.identityDir ??
|
|
5578
|
+
const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
|
|
5313
5579
|
const localEmployees = await loadEmployees(rosterPath);
|
|
5314
5580
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
5315
5581
|
let added = 0;
|
|
@@ -5330,15 +5596,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
5330
5596
|
) ?? lookupKey;
|
|
5331
5597
|
const remoteIdentity = remote.identities[matchedKey];
|
|
5332
5598
|
if (remoteIdentity) {
|
|
5333
|
-
if (!
|
|
5334
|
-
const idPath =
|
|
5599
|
+
if (!existsSync13(identityDir)) mkdirSync6(identityDir, { recursive: true });
|
|
5600
|
+
const idPath = path13.join(identityDir, `${remoteEmp.name}.md`);
|
|
5335
5601
|
let localIdentity = null;
|
|
5336
5602
|
try {
|
|
5337
|
-
localIdentity =
|
|
5603
|
+
localIdentity = existsSync13(idPath) ? readFileSync10(idPath, "utf-8") : null;
|
|
5338
5604
|
} catch {
|
|
5339
5605
|
}
|
|
5340
5606
|
if (localIdentity !== remoteIdentity) {
|
|
5341
|
-
|
|
5607
|
+
writeFileSync8(idPath, remoteIdentity, "utf-8");
|
|
5342
5608
|
identitiesUpdated++;
|
|
5343
5609
|
}
|
|
5344
5610
|
}
|
|
@@ -5364,16 +5630,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
5364
5630
|
}
|
|
5365
5631
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
5366
5632
|
try {
|
|
5367
|
-
const agentConfigPath =
|
|
5633
|
+
const agentConfigPath = path13.join(EXE_AI_DIR, "agent-config.json");
|
|
5368
5634
|
let local = {};
|
|
5369
|
-
if (
|
|
5635
|
+
if (existsSync13(agentConfigPath)) {
|
|
5370
5636
|
try {
|
|
5371
|
-
local = JSON.parse(
|
|
5637
|
+
local = JSON.parse(readFileSync10(agentConfigPath, "utf-8"));
|
|
5372
5638
|
} catch {
|
|
5373
5639
|
}
|
|
5374
5640
|
}
|
|
5375
5641
|
const merged = { ...remote.agentConfig, ...local };
|
|
5376
|
-
|
|
5642
|
+
ensurePrivateDirSync(path13.dirname(agentConfigPath));
|
|
5643
|
+
writeFileSync8(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
5644
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
5377
5645
|
} catch {
|
|
5378
5646
|
}
|
|
5379
5647
|
}
|
|
@@ -5797,7 +6065,7 @@ async function cloudPullDocuments(config) {
|
|
|
5797
6065
|
}
|
|
5798
6066
|
return { pulled };
|
|
5799
6067
|
}
|
|
5800
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
6068
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
5801
6069
|
var init_cloud_sync = __esm({
|
|
5802
6070
|
"src/lib/cloud-sync.ts"() {
|
|
5803
6071
|
"use strict";
|
|
@@ -5808,12 +6076,15 @@ var init_cloud_sync = __esm({
|
|
|
5808
6076
|
init_config();
|
|
5809
6077
|
init_crdt_sync();
|
|
5810
6078
|
init_employees();
|
|
6079
|
+
init_secure_files();
|
|
5811
6080
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
5812
6081
|
FETCH_TIMEOUT_MS = 3e4;
|
|
5813
6082
|
PUSH_BATCH_SIZE = 5e3;
|
|
5814
|
-
ROSTER_LOCK_PATH =
|
|
6083
|
+
ROSTER_LOCK_PATH = path13.join(EXE_AI_DIR, "roster-merge.lock");
|
|
5815
6084
|
LOCK_STALE_MS = 3e4;
|
|
5816
|
-
|
|
6085
|
+
_pgPromise = null;
|
|
6086
|
+
_pgFailed = false;
|
|
6087
|
+
ROSTER_DELETIONS_PATH = path13.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5817
6088
|
}
|
|
5818
6089
|
});
|
|
5819
6090
|
|
|
@@ -6111,6 +6382,7 @@ var shard_manager_exports = {};
|
|
|
6111
6382
|
__export(shard_manager_exports, {
|
|
6112
6383
|
disposeShards: () => disposeShards,
|
|
6113
6384
|
ensureShardSchema: () => ensureShardSchema,
|
|
6385
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
6114
6386
|
getReadyShardClient: () => getReadyShardClient,
|
|
6115
6387
|
getShardClient: () => getShardClient,
|
|
6116
6388
|
getShardsDir: () => getShardsDir,
|
|
@@ -6119,15 +6391,18 @@ __export(shard_manager_exports, {
|
|
|
6119
6391
|
listShards: () => listShards,
|
|
6120
6392
|
shardExists: () => shardExists
|
|
6121
6393
|
});
|
|
6122
|
-
import
|
|
6123
|
-
import { existsSync as
|
|
6394
|
+
import path14 from "path";
|
|
6395
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync2 } from "fs";
|
|
6124
6396
|
import { createClient as createClient2 } from "@libsql/client";
|
|
6125
6397
|
function initShardManager(encryptionKey) {
|
|
6126
6398
|
_encryptionKey = encryptionKey;
|
|
6127
|
-
if (!
|
|
6128
|
-
|
|
6399
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
6400
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
6129
6401
|
}
|
|
6130
6402
|
_shardingEnabled = true;
|
|
6403
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
6404
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
6405
|
+
_evictionTimer.unref();
|
|
6131
6406
|
}
|
|
6132
6407
|
function isShardingEnabled() {
|
|
6133
6408
|
return _shardingEnabled;
|
|
@@ -6144,21 +6419,28 @@ function getShardClient(projectName) {
|
|
|
6144
6419
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
6145
6420
|
}
|
|
6146
6421
|
const cached = _shards.get(safeName);
|
|
6147
|
-
if (cached)
|
|
6148
|
-
|
|
6422
|
+
if (cached) {
|
|
6423
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6424
|
+
return cached;
|
|
6425
|
+
}
|
|
6426
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
6427
|
+
evictLRU();
|
|
6428
|
+
}
|
|
6429
|
+
const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
|
|
6149
6430
|
const client = createClient2({
|
|
6150
6431
|
url: `file:${dbPath}`,
|
|
6151
6432
|
encryptionKey: _encryptionKey
|
|
6152
6433
|
});
|
|
6153
6434
|
_shards.set(safeName, client);
|
|
6435
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6154
6436
|
return client;
|
|
6155
6437
|
}
|
|
6156
6438
|
function shardExists(projectName) {
|
|
6157
6439
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6158
|
-
return
|
|
6440
|
+
return existsSync14(path14.join(SHARDS_DIR, `${safeName}.db`));
|
|
6159
6441
|
}
|
|
6160
6442
|
function listShards() {
|
|
6161
|
-
if (!
|
|
6443
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
6162
6444
|
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6163
6445
|
}
|
|
6164
6446
|
async function ensureShardSchema(client) {
|
|
@@ -6210,6 +6492,8 @@ async function ensureShardSchema(client) {
|
|
|
6210
6492
|
for (const col of [
|
|
6211
6493
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
6212
6494
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
6495
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
6496
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
6213
6497
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
6214
6498
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
6215
6499
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -6347,21 +6631,69 @@ async function getReadyShardClient(projectName) {
|
|
|
6347
6631
|
await ensureShardSchema(client);
|
|
6348
6632
|
return client;
|
|
6349
6633
|
}
|
|
6634
|
+
function evictLRU() {
|
|
6635
|
+
let oldest = null;
|
|
6636
|
+
let oldestTime = Infinity;
|
|
6637
|
+
for (const [name, time] of _shardLastAccess) {
|
|
6638
|
+
if (time < oldestTime) {
|
|
6639
|
+
oldestTime = time;
|
|
6640
|
+
oldest = name;
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
if (oldest) {
|
|
6644
|
+
const client = _shards.get(oldest);
|
|
6645
|
+
if (client) {
|
|
6646
|
+
client.close();
|
|
6647
|
+
}
|
|
6648
|
+
_shards.delete(oldest);
|
|
6649
|
+
_shardLastAccess.delete(oldest);
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
function evictIdleShards() {
|
|
6653
|
+
const now = Date.now();
|
|
6654
|
+
const toEvict = [];
|
|
6655
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
6656
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
6657
|
+
toEvict.push(name);
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
for (const name of toEvict) {
|
|
6661
|
+
const client = _shards.get(name);
|
|
6662
|
+
if (client) {
|
|
6663
|
+
client.close();
|
|
6664
|
+
}
|
|
6665
|
+
_shards.delete(name);
|
|
6666
|
+
_shardLastAccess.delete(name);
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
function getOpenShardCount() {
|
|
6670
|
+
return _shards.size;
|
|
6671
|
+
}
|
|
6350
6672
|
function disposeShards() {
|
|
6673
|
+
if (_evictionTimer) {
|
|
6674
|
+
clearInterval(_evictionTimer);
|
|
6675
|
+
_evictionTimer = null;
|
|
6676
|
+
}
|
|
6351
6677
|
for (const [, client] of _shards) {
|
|
6352
6678
|
client.close();
|
|
6353
6679
|
}
|
|
6354
6680
|
_shards.clear();
|
|
6681
|
+
_shardLastAccess.clear();
|
|
6355
6682
|
_shardingEnabled = false;
|
|
6356
6683
|
_encryptionKey = null;
|
|
6357
6684
|
}
|
|
6358
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
6685
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
6359
6686
|
var init_shard_manager = __esm({
|
|
6360
6687
|
"src/lib/shard-manager.ts"() {
|
|
6361
6688
|
"use strict";
|
|
6362
6689
|
init_config();
|
|
6363
|
-
SHARDS_DIR =
|
|
6690
|
+
SHARDS_DIR = path14.join(EXE_AI_DIR, "shards");
|
|
6691
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
6692
|
+
MAX_OPEN_SHARDS = 10;
|
|
6693
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
6364
6694
|
_shards = /* @__PURE__ */ new Map();
|
|
6695
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
6696
|
+
_evictionTimer = null;
|
|
6365
6697
|
_encryptionKey = null;
|
|
6366
6698
|
_shardingEnabled = false;
|
|
6367
6699
|
}
|
|
@@ -7129,15 +7461,15 @@ var backfill_conversations_exports = {};
|
|
|
7129
7461
|
__export(backfill_conversations_exports, {
|
|
7130
7462
|
backfillConversations: () => backfillConversations
|
|
7131
7463
|
});
|
|
7132
|
-
import
|
|
7464
|
+
import crypto5 from "crypto";
|
|
7133
7465
|
import { createReadStream } from "fs";
|
|
7134
7466
|
import { readdir as readdir2, stat } from "fs/promises";
|
|
7135
|
-
import
|
|
7467
|
+
import path15 from "path";
|
|
7136
7468
|
import { createInterface as createInterface2 } from "readline";
|
|
7137
7469
|
import { homedir as homedir3 } from "os";
|
|
7138
7470
|
import { parseArgs } from "util";
|
|
7139
7471
|
async function findJsonlFiles(sinceDate, projectFilter) {
|
|
7140
|
-
const projectsDir =
|
|
7472
|
+
const projectsDir = path15.join(homedir3(), ".claude", "projects");
|
|
7141
7473
|
const files = [];
|
|
7142
7474
|
async function walk(dir, depth = 0) {
|
|
7143
7475
|
if (depth > MAX_WALK_DEPTH) return;
|
|
@@ -7148,7 +7480,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
7148
7480
|
return;
|
|
7149
7481
|
}
|
|
7150
7482
|
for (const entry of entries) {
|
|
7151
|
-
const full =
|
|
7483
|
+
const full = path15.join(dir, entry.name);
|
|
7152
7484
|
if (entry.isDirectory()) {
|
|
7153
7485
|
if (entry.name === "subagents" || entry.name === "tool-results") continue;
|
|
7154
7486
|
await walk(full, depth + 1);
|
|
@@ -7173,7 +7505,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
7173
7505
|
if (!entry.isDirectory()) continue;
|
|
7174
7506
|
const decoded = decodeProjectDir(entry.name);
|
|
7175
7507
|
if (decoded.toLowerCase().includes(projectFilter.toLowerCase())) {
|
|
7176
|
-
await walk(
|
|
7508
|
+
await walk(path15.join(projectsDir, entry.name));
|
|
7177
7509
|
}
|
|
7178
7510
|
}
|
|
7179
7511
|
} else {
|
|
@@ -7190,14 +7522,14 @@ function decodeProjectDir(dirName) {
|
|
|
7190
7522
|
return dirName;
|
|
7191
7523
|
}
|
|
7192
7524
|
function projectNameFromPath(filePath) {
|
|
7193
|
-
const projectsDir =
|
|
7194
|
-
const relative =
|
|
7195
|
-
const projectDir = relative.split(
|
|
7525
|
+
const projectsDir = path15.join(homedir3(), ".claude", "projects");
|
|
7526
|
+
const relative = path15.relative(projectsDir, filePath);
|
|
7527
|
+
const projectDir = relative.split(path15.sep)[0] ?? "unknown";
|
|
7196
7528
|
return decodeProjectDir(projectDir);
|
|
7197
7529
|
}
|
|
7198
7530
|
async function parseConversation(filePath) {
|
|
7199
7531
|
const conv = {
|
|
7200
|
-
sessionId:
|
|
7532
|
+
sessionId: path15.basename(filePath, ".jsonl"),
|
|
7201
7533
|
projectName: projectNameFromPath(filePath),
|
|
7202
7534
|
cwd: void 0,
|
|
7203
7535
|
startTime: void 0,
|
|
@@ -7261,7 +7593,7 @@ async function parseConversation(filePath) {
|
|
|
7261
7593
|
}
|
|
7262
7594
|
}
|
|
7263
7595
|
if (conv.cwd) {
|
|
7264
|
-
conv.projectName =
|
|
7596
|
+
conv.projectName = path15.basename(conv.cwd);
|
|
7265
7597
|
const worktreeMatch = conv.cwd.match(/\.worktrees\/([^/]+)/);
|
|
7266
7598
|
if (worktreeMatch?.[1]) {
|
|
7267
7599
|
conv.agentId = worktreeMatch[1];
|
|
@@ -7434,7 +7766,7 @@ async function backfillConversations(options) {
|
|
|
7434
7766
|
}
|
|
7435
7767
|
}
|
|
7436
7768
|
await writeMemory({
|
|
7437
|
-
id:
|
|
7769
|
+
id: crypto5.randomUUID(),
|
|
7438
7770
|
agent_id: conv.agentId,
|
|
7439
7771
|
agent_role: isCoordinatorName(conv.agentId) ? "COO" : "specialist",
|
|
7440
7772
|
session_id: conv.sessionId,
|
|
@@ -7913,9 +8245,9 @@ Unclassified: ${unclassified}
|
|
|
7913
8245
|
}
|
|
7914
8246
|
async function exportBatches(options) {
|
|
7915
8247
|
const fs8 = await import("fs");
|
|
7916
|
-
const
|
|
8248
|
+
const path46 = await import("path");
|
|
7917
8249
|
const client = getClient();
|
|
7918
|
-
const outDir =
|
|
8250
|
+
const outDir = path46.join(process.cwd(), "exe/output/classifications/input");
|
|
7919
8251
|
fs8.mkdirSync(outDir, { recursive: true });
|
|
7920
8252
|
const countResult = await client.execute({
|
|
7921
8253
|
sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
|
|
@@ -7939,7 +8271,7 @@ async function exportBatches(options) {
|
|
|
7939
8271
|
const text = String(row.text || "").replace(/\n/g, " ");
|
|
7940
8272
|
return JSON.stringify({ id: row.id, text });
|
|
7941
8273
|
});
|
|
7942
|
-
const batchFile =
|
|
8274
|
+
const batchFile = path46.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
|
|
7943
8275
|
fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
|
|
7944
8276
|
exported += batch.rows.length;
|
|
7945
8277
|
offset += options.batchSize;
|
|
@@ -7955,7 +8287,7 @@ async function exportBatches(options) {
|
|
|
7955
8287
|
}
|
|
7956
8288
|
async function importClassifications(importDir) {
|
|
7957
8289
|
const fs8 = await import("fs");
|
|
7958
|
-
const
|
|
8290
|
+
const path46 = await import("path");
|
|
7959
8291
|
const client = getClient();
|
|
7960
8292
|
const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
7961
8293
|
process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
|
|
@@ -7963,7 +8295,7 @@ async function importClassifications(importDir) {
|
|
|
7963
8295
|
let imported = 0;
|
|
7964
8296
|
let invalid = 0;
|
|
7965
8297
|
for (const file of files) {
|
|
7966
|
-
const lines = fs8.readFileSync(
|
|
8298
|
+
const lines = fs8.readFileSync(path46.join(importDir, file), "utf-8").split("\n").filter(Boolean);
|
|
7967
8299
|
for (const line of lines) {
|
|
7968
8300
|
try {
|
|
7969
8301
|
const rec = JSON.parse(line);
|
|
@@ -8104,17 +8436,17 @@ __export(identity_exports, {
|
|
|
8104
8436
|
listIdentities: () => listIdentities,
|
|
8105
8437
|
updateIdentity: () => updateIdentity
|
|
8106
8438
|
});
|
|
8107
|
-
import { existsSync as
|
|
8439
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
8108
8440
|
import { readdirSync as readdirSync3 } from "fs";
|
|
8109
|
-
import
|
|
8441
|
+
import path16 from "path";
|
|
8110
8442
|
import { createHash as createHash2 } from "crypto";
|
|
8111
8443
|
function ensureDir() {
|
|
8112
|
-
if (!
|
|
8113
|
-
|
|
8444
|
+
if (!existsSync15(IDENTITY_DIR2)) {
|
|
8445
|
+
mkdirSync8(IDENTITY_DIR2, { recursive: true });
|
|
8114
8446
|
}
|
|
8115
8447
|
}
|
|
8116
8448
|
function identityPath(agentId) {
|
|
8117
|
-
return
|
|
8449
|
+
return path16.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
8118
8450
|
}
|
|
8119
8451
|
function parseFrontmatter(raw) {
|
|
8120
8452
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -8155,8 +8487,8 @@ function contentHash(content) {
|
|
|
8155
8487
|
}
|
|
8156
8488
|
function getIdentity(agentId) {
|
|
8157
8489
|
const filePath = identityPath(agentId);
|
|
8158
|
-
if (!
|
|
8159
|
-
const raw =
|
|
8490
|
+
if (!existsSync15(filePath)) return null;
|
|
8491
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
8160
8492
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
8161
8493
|
return {
|
|
8162
8494
|
agentId,
|
|
@@ -8170,7 +8502,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
8170
8502
|
ensureDir();
|
|
8171
8503
|
const filePath = identityPath(agentId);
|
|
8172
8504
|
const hash = contentHash(content);
|
|
8173
|
-
|
|
8505
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
8174
8506
|
try {
|
|
8175
8507
|
const client = getClient();
|
|
8176
8508
|
await client.execute({
|
|
@@ -8226,15 +8558,15 @@ var init_identity = __esm({
|
|
|
8226
8558
|
"use strict";
|
|
8227
8559
|
init_config();
|
|
8228
8560
|
init_database();
|
|
8229
|
-
IDENTITY_DIR2 =
|
|
8561
|
+
IDENTITY_DIR2 = path16.join(EXE_AI_DIR, "identity");
|
|
8230
8562
|
}
|
|
8231
8563
|
});
|
|
8232
8564
|
|
|
8233
8565
|
// src/lib/orchestration-package.ts
|
|
8234
8566
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8235
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
8236
|
-
import
|
|
8237
|
-
import
|
|
8567
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
8568
|
+
import os10 from "os";
|
|
8569
|
+
import path17 from "path";
|
|
8238
8570
|
function ensureObject(value, label) {
|
|
8239
8571
|
if (value == null || Array.isArray(value) || typeof value !== "object") {
|
|
8240
8572
|
throw new Error(`${label} must be an object`);
|
|
@@ -8293,15 +8625,15 @@ function validateProcedureEntry(value, index) {
|
|
|
8293
8625
|
};
|
|
8294
8626
|
}
|
|
8295
8627
|
function getRosterPath() {
|
|
8296
|
-
return
|
|
8628
|
+
return path17.join(os10.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
|
|
8297
8629
|
}
|
|
8298
8630
|
function getBackupPath() {
|
|
8299
|
-
return
|
|
8631
|
+
return path17.join(os10.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
|
|
8300
8632
|
}
|
|
8301
8633
|
function readRosterFile() {
|
|
8302
8634
|
const rosterPath = getRosterPath();
|
|
8303
|
-
if (!
|
|
8304
|
-
const raw =
|
|
8635
|
+
if (!existsSync16(rosterPath)) return [];
|
|
8636
|
+
const raw = readFileSync12(rosterPath, "utf-8");
|
|
8305
8637
|
const parsed = JSON.parse(raw);
|
|
8306
8638
|
if (!Array.isArray(parsed)) {
|
|
8307
8639
|
throw new Error("Roster file must contain a JSON array");
|
|
@@ -8313,8 +8645,8 @@ function writeRosterFile(roster) {
|
|
|
8313
8645
|
throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
|
|
8314
8646
|
}
|
|
8315
8647
|
const rosterPath = getRosterPath();
|
|
8316
|
-
|
|
8317
|
-
if (
|
|
8648
|
+
mkdirSync9(path17.dirname(rosterPath), { recursive: true });
|
|
8649
|
+
if (existsSync16(rosterPath)) {
|
|
8318
8650
|
const currentRoster = readRosterFile();
|
|
8319
8651
|
if (roster.length < currentRoster.length) {
|
|
8320
8652
|
throw new Error(
|
|
@@ -8323,7 +8655,7 @@ function writeRosterFile(roster) {
|
|
|
8323
8655
|
}
|
|
8324
8656
|
copyFileSync2(rosterPath, getBackupPath());
|
|
8325
8657
|
}
|
|
8326
|
-
|
|
8658
|
+
writeFileSync10(rosterPath, `${JSON.stringify(roster, null, 2)}
|
|
8327
8659
|
`, "utf-8");
|
|
8328
8660
|
}
|
|
8329
8661
|
function buildImportedRosterEntries(roster, timestamp) {
|
|
@@ -8590,8 +8922,8 @@ var exe_export_exports = {};
|
|
|
8590
8922
|
__export(exe_export_exports, {
|
|
8591
8923
|
runExeExport: () => runExeExport
|
|
8592
8924
|
});
|
|
8593
|
-
import { mkdirSync as
|
|
8594
|
-
import
|
|
8925
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
8926
|
+
import path18 from "path";
|
|
8595
8927
|
function printUsage() {
|
|
8596
8928
|
process.stdout.write("Usage: exe-os export --output <path>\n");
|
|
8597
8929
|
}
|
|
@@ -8612,8 +8944,8 @@ async function runExeExport(argv = process.argv.slice(2)) {
|
|
|
8612
8944
|
await initStore();
|
|
8613
8945
|
try {
|
|
8614
8946
|
const pkg = await exportOrchestration("cli");
|
|
8615
|
-
|
|
8616
|
-
|
|
8947
|
+
mkdirSync10(path18.dirname(outputPath), { recursive: true });
|
|
8948
|
+
writeFileSync11(outputPath, `${JSON.stringify(pkg, null, 2)}
|
|
8617
8949
|
`, "utf-8");
|
|
8618
8950
|
process.stdout.write(
|
|
8619
8951
|
`Exported ${pkg.roster.length} roster entries, ${Object.keys(pkg.identities).length} identities, ${pkg.behaviors.length} behaviors, ${pkg.procedures.length} procedures to ${outputPath}
|
|
@@ -8649,7 +8981,7 @@ var exe_import_exports = {};
|
|
|
8649
8981
|
__export(exe_import_exports, {
|
|
8650
8982
|
runExeImport: () => runExeImport
|
|
8651
8983
|
});
|
|
8652
|
-
import { readFileSync as
|
|
8984
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
8653
8985
|
function printUsage2() {
|
|
8654
8986
|
process.stdout.write("Usage: exe-os import --from <path> [--merge]\n");
|
|
8655
8987
|
}
|
|
@@ -8672,7 +9004,7 @@ async function runExeImport(argv = process.argv.slice(2)) {
|
|
|
8672
9004
|
if (parsed == null) return;
|
|
8673
9005
|
await initStore();
|
|
8674
9006
|
try {
|
|
8675
|
-
const raw =
|
|
9007
|
+
const raw = readFileSync13(parsed.packagePath, "utf-8");
|
|
8676
9008
|
const pkg = validatePackage(JSON.parse(raw));
|
|
8677
9009
|
const result = await importOrchestration(pkg, parsed.strategy);
|
|
8678
9010
|
process.stdout.write(
|
|
@@ -8712,14 +9044,14 @@ __export(session_registry_exports, {
|
|
|
8712
9044
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
8713
9045
|
registerSession: () => registerSession
|
|
8714
9046
|
});
|
|
8715
|
-
import { readFileSync as
|
|
9047
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, mkdirSync as mkdirSync11, existsSync as existsSync17 } from "fs";
|
|
8716
9048
|
import { execSync as execSync3 } from "child_process";
|
|
8717
|
-
import
|
|
8718
|
-
import
|
|
9049
|
+
import path19 from "path";
|
|
9050
|
+
import os11 from "os";
|
|
8719
9051
|
function registerSession(entry) {
|
|
8720
|
-
const dir =
|
|
8721
|
-
if (!
|
|
8722
|
-
|
|
9052
|
+
const dir = path19.dirname(REGISTRY_PATH);
|
|
9053
|
+
if (!existsSync17(dir)) {
|
|
9054
|
+
mkdirSync11(dir, { recursive: true });
|
|
8723
9055
|
}
|
|
8724
9056
|
const sessions = listSessions();
|
|
8725
9057
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -8728,11 +9060,11 @@ function registerSession(entry) {
|
|
|
8728
9060
|
} else {
|
|
8729
9061
|
sessions.push(entry);
|
|
8730
9062
|
}
|
|
8731
|
-
|
|
9063
|
+
writeFileSync12(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
8732
9064
|
}
|
|
8733
9065
|
function listSessions() {
|
|
8734
9066
|
try {
|
|
8735
|
-
const raw =
|
|
9067
|
+
const raw = readFileSync14(REGISTRY_PATH, "utf8");
|
|
8736
9068
|
return JSON.parse(raw);
|
|
8737
9069
|
} catch {
|
|
8738
9070
|
return [];
|
|
@@ -8753,7 +9085,7 @@ function pruneStaleSessions() {
|
|
|
8753
9085
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
8754
9086
|
const pruned = sessions.length - alive.length;
|
|
8755
9087
|
if (pruned > 0) {
|
|
8756
|
-
|
|
9088
|
+
writeFileSync12(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
8757
9089
|
}
|
|
8758
9090
|
return pruned;
|
|
8759
9091
|
}
|
|
@@ -8761,7 +9093,7 @@ var REGISTRY_PATH;
|
|
|
8761
9093
|
var init_session_registry = __esm({
|
|
8762
9094
|
"src/lib/session-registry.ts"() {
|
|
8763
9095
|
"use strict";
|
|
8764
|
-
REGISTRY_PATH =
|
|
9096
|
+
REGISTRY_PATH = path19.join(os11.homedir(), ".exe-os", "session-registry.json");
|
|
8765
9097
|
}
|
|
8766
9098
|
});
|
|
8767
9099
|
|
|
@@ -9007,17 +9339,17 @@ __export(intercom_queue_exports, {
|
|
|
9007
9339
|
queueIntercom: () => queueIntercom,
|
|
9008
9340
|
readQueue: () => readQueue
|
|
9009
9341
|
});
|
|
9010
|
-
import { readFileSync as
|
|
9011
|
-
import
|
|
9012
|
-
import
|
|
9342
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, renameSync as renameSync3, existsSync as existsSync18, mkdirSync as mkdirSync12 } from "fs";
|
|
9343
|
+
import path20 from "path";
|
|
9344
|
+
import os12 from "os";
|
|
9013
9345
|
function ensureDir2() {
|
|
9014
|
-
const dir =
|
|
9015
|
-
if (!
|
|
9346
|
+
const dir = path20.dirname(QUEUE_PATH);
|
|
9347
|
+
if (!existsSync18(dir)) mkdirSync12(dir, { recursive: true });
|
|
9016
9348
|
}
|
|
9017
9349
|
function readQueue() {
|
|
9018
9350
|
try {
|
|
9019
|
-
if (!
|
|
9020
|
-
return JSON.parse(
|
|
9351
|
+
if (!existsSync18(QUEUE_PATH)) return [];
|
|
9352
|
+
return JSON.parse(readFileSync15(QUEUE_PATH, "utf8"));
|
|
9021
9353
|
} catch {
|
|
9022
9354
|
return [];
|
|
9023
9355
|
}
|
|
@@ -9025,7 +9357,7 @@ function readQueue() {
|
|
|
9025
9357
|
function writeQueue(queue) {
|
|
9026
9358
|
ensureDir2();
|
|
9027
9359
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
9028
|
-
|
|
9360
|
+
writeFileSync13(tmp, JSON.stringify(queue, null, 2));
|
|
9029
9361
|
renameSync3(tmp, QUEUE_PATH);
|
|
9030
9362
|
}
|
|
9031
9363
|
function queueIntercom(targetSession, reason) {
|
|
@@ -9117,20 +9449,20 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
9117
9449
|
var init_intercom_queue = __esm({
|
|
9118
9450
|
"src/lib/intercom-queue.ts"() {
|
|
9119
9451
|
"use strict";
|
|
9120
|
-
QUEUE_PATH =
|
|
9452
|
+
QUEUE_PATH = path20.join(os12.homedir(), ".exe-os", "intercom-queue.json");
|
|
9121
9453
|
MAX_RETRIES2 = 5;
|
|
9122
9454
|
TTL_MS = 60 * 60 * 1e3;
|
|
9123
|
-
INTERCOM_LOG =
|
|
9455
|
+
INTERCOM_LOG = path20.join(os12.homedir(), ".exe-os", "intercom.log");
|
|
9124
9456
|
}
|
|
9125
9457
|
});
|
|
9126
9458
|
|
|
9127
9459
|
// src/lib/plan-limits.ts
|
|
9128
|
-
import { readFileSync as
|
|
9129
|
-
import
|
|
9460
|
+
import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
|
|
9461
|
+
import path21 from "path";
|
|
9130
9462
|
function getLicenseSync() {
|
|
9131
9463
|
try {
|
|
9132
|
-
if (!
|
|
9133
|
-
const raw = JSON.parse(
|
|
9464
|
+
if (!existsSync19(CACHE_PATH2)) return freeLicense();
|
|
9465
|
+
const raw = JSON.parse(readFileSync16(CACHE_PATH2, "utf8"));
|
|
9134
9466
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
9135
9467
|
const parts = raw.token.split(".");
|
|
9136
9468
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -9168,8 +9500,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
9168
9500
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
9169
9501
|
let count = 0;
|
|
9170
9502
|
try {
|
|
9171
|
-
if (
|
|
9172
|
-
const raw =
|
|
9503
|
+
if (existsSync19(filePath)) {
|
|
9504
|
+
const raw = readFileSync16(filePath, "utf8");
|
|
9173
9505
|
const employees = JSON.parse(raw);
|
|
9174
9506
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
9175
9507
|
}
|
|
@@ -9198,29 +9530,69 @@ var init_plan_limits = __esm({
|
|
|
9198
9530
|
this.name = "PlanLimitError";
|
|
9199
9531
|
}
|
|
9200
9532
|
};
|
|
9201
|
-
CACHE_PATH2 =
|
|
9533
|
+
CACHE_PATH2 = path21.join(EXE_AI_DIR, "license-cache.json");
|
|
9534
|
+
}
|
|
9535
|
+
});
|
|
9536
|
+
|
|
9537
|
+
// src/lib/task-scope.ts
|
|
9538
|
+
var task_scope_exports = {};
|
|
9539
|
+
__export(task_scope_exports, {
|
|
9540
|
+
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
9541
|
+
sessionScopeFilter: () => sessionScopeFilter,
|
|
9542
|
+
strictSessionScopeFilter: () => strictSessionScopeFilter
|
|
9543
|
+
});
|
|
9544
|
+
function getCurrentSessionScope() {
|
|
9545
|
+
try {
|
|
9546
|
+
return resolveExeSession();
|
|
9547
|
+
} catch {
|
|
9548
|
+
return null;
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
9552
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
9553
|
+
if (!scope) return { sql: "", args: [] };
|
|
9554
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
9555
|
+
return {
|
|
9556
|
+
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
9557
|
+
args: [scope]
|
|
9558
|
+
};
|
|
9559
|
+
}
|
|
9560
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
9561
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
9562
|
+
if (!scope) return { sql: "", args: [] };
|
|
9563
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
9564
|
+
return {
|
|
9565
|
+
sql: ` AND ${col} = ?`,
|
|
9566
|
+
args: [scope]
|
|
9567
|
+
};
|
|
9568
|
+
}
|
|
9569
|
+
var init_task_scope = __esm({
|
|
9570
|
+
"src/lib/task-scope.ts"() {
|
|
9571
|
+
"use strict";
|
|
9572
|
+
init_tmux_routing();
|
|
9202
9573
|
}
|
|
9203
9574
|
});
|
|
9204
9575
|
|
|
9205
9576
|
// src/lib/notifications.ts
|
|
9206
|
-
import
|
|
9207
|
-
import
|
|
9208
|
-
import
|
|
9577
|
+
import crypto6 from "crypto";
|
|
9578
|
+
import path22 from "path";
|
|
9579
|
+
import os13 from "os";
|
|
9209
9580
|
import {
|
|
9210
|
-
readFileSync as
|
|
9581
|
+
readFileSync as readFileSync17,
|
|
9211
9582
|
readdirSync as readdirSync4,
|
|
9212
9583
|
unlinkSync as unlinkSync5,
|
|
9213
|
-
existsSync as
|
|
9584
|
+
existsSync as existsSync20,
|
|
9214
9585
|
rmdirSync
|
|
9215
9586
|
} from "fs";
|
|
9216
9587
|
async function writeNotification(notification) {
|
|
9217
9588
|
try {
|
|
9218
9589
|
const client = getClient();
|
|
9219
|
-
const id =
|
|
9590
|
+
const id = crypto6.randomUUID();
|
|
9220
9591
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9592
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
9221
9593
|
await client.execute({
|
|
9222
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
9223
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
9594
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
9595
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
9224
9596
|
args: [
|
|
9225
9597
|
id,
|
|
9226
9598
|
notification.agentId,
|
|
@@ -9229,6 +9601,7 @@ async function writeNotification(notification) {
|
|
|
9229
9601
|
notification.project,
|
|
9230
9602
|
notification.summary,
|
|
9231
9603
|
notification.taskFile ?? null,
|
|
9604
|
+
sessionScope,
|
|
9232
9605
|
now
|
|
9233
9606
|
]
|
|
9234
9607
|
});
|
|
@@ -9237,12 +9610,14 @@ async function writeNotification(notification) {
|
|
|
9237
9610
|
`);
|
|
9238
9611
|
}
|
|
9239
9612
|
}
|
|
9240
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
9613
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
9241
9614
|
try {
|
|
9242
9615
|
const client = getClient();
|
|
9616
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9243
9617
|
await client.execute({
|
|
9244
|
-
sql:
|
|
9245
|
-
|
|
9618
|
+
sql: `UPDATE notifications SET read = 1
|
|
9619
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
9620
|
+
args: [taskFile, ...scope.args]
|
|
9246
9621
|
});
|
|
9247
9622
|
} catch {
|
|
9248
9623
|
}
|
|
@@ -9251,11 +9626,12 @@ var init_notifications = __esm({
|
|
|
9251
9626
|
"src/lib/notifications.ts"() {
|
|
9252
9627
|
"use strict";
|
|
9253
9628
|
init_database();
|
|
9629
|
+
init_task_scope();
|
|
9254
9630
|
}
|
|
9255
9631
|
});
|
|
9256
9632
|
|
|
9257
9633
|
// src/lib/session-kill-telemetry.ts
|
|
9258
|
-
import
|
|
9634
|
+
import crypto7 from "crypto";
|
|
9259
9635
|
async function recordSessionKill(input) {
|
|
9260
9636
|
try {
|
|
9261
9637
|
const client = getClient();
|
|
@@ -9265,7 +9641,7 @@ async function recordSessionKill(input) {
|
|
|
9265
9641
|
ticks_idle, estimated_tokens_saved)
|
|
9266
9642
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
9267
9643
|
args: [
|
|
9268
|
-
|
|
9644
|
+
crypto7.randomUUID(),
|
|
9269
9645
|
input.sessionName,
|
|
9270
9646
|
input.agentId,
|
|
9271
9647
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9288,32 +9664,107 @@ var init_session_kill_telemetry = __esm({
|
|
|
9288
9664
|
}
|
|
9289
9665
|
});
|
|
9290
9666
|
|
|
9291
|
-
// src/lib/
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
function getCurrentSessionScope() {
|
|
9667
|
+
// src/lib/project-name.ts
|
|
9668
|
+
import { execSync as execSync6 } from "child_process";
|
|
9669
|
+
import path23 from "path";
|
|
9670
|
+
function getProjectName(cwd2) {
|
|
9671
|
+
const dir = cwd2 ?? process.cwd();
|
|
9672
|
+
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
9298
9673
|
try {
|
|
9299
|
-
|
|
9674
|
+
let repoRoot;
|
|
9675
|
+
try {
|
|
9676
|
+
const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
|
|
9677
|
+
cwd: dir,
|
|
9678
|
+
encoding: "utf8",
|
|
9679
|
+
timeout: 2e3,
|
|
9680
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9681
|
+
}).trim();
|
|
9682
|
+
repoRoot = path23.dirname(gitCommonDir);
|
|
9683
|
+
} catch {
|
|
9684
|
+
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
9685
|
+
cwd: dir,
|
|
9686
|
+
encoding: "utf8",
|
|
9687
|
+
timeout: 2e3,
|
|
9688
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9689
|
+
}).trim();
|
|
9690
|
+
}
|
|
9691
|
+
_cached2 = path23.basename(repoRoot);
|
|
9692
|
+
_cachedCwd = dir;
|
|
9693
|
+
return _cached2;
|
|
9300
9694
|
} catch {
|
|
9301
|
-
|
|
9695
|
+
_cached2 = path23.basename(dir);
|
|
9696
|
+
_cachedCwd = dir;
|
|
9697
|
+
return _cached2;
|
|
9302
9698
|
}
|
|
9303
9699
|
}
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9700
|
+
var _cached2, _cachedCwd;
|
|
9701
|
+
var init_project_name = __esm({
|
|
9702
|
+
"src/lib/project-name.ts"() {
|
|
9703
|
+
"use strict";
|
|
9704
|
+
_cached2 = null;
|
|
9705
|
+
_cachedCwd = null;
|
|
9706
|
+
}
|
|
9707
|
+
});
|
|
9708
|
+
|
|
9709
|
+
// src/lib/session-scope.ts
|
|
9710
|
+
var session_scope_exports = {};
|
|
9711
|
+
__export(session_scope_exports, {
|
|
9712
|
+
assertSessionScope: () => assertSessionScope,
|
|
9713
|
+
findSessionForProject: () => findSessionForProject,
|
|
9714
|
+
getSessionProject: () => getSessionProject
|
|
9715
|
+
});
|
|
9716
|
+
function getSessionProject(sessionName) {
|
|
9717
|
+
const sessions = listSessions();
|
|
9718
|
+
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
9719
|
+
if (!entry) return null;
|
|
9720
|
+
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
9721
|
+
return parts[parts.length - 1] ?? null;
|
|
9312
9722
|
}
|
|
9313
|
-
|
|
9314
|
-
|
|
9723
|
+
function findSessionForProject(projectName) {
|
|
9724
|
+
const sessions = listSessions();
|
|
9725
|
+
for (const s of sessions) {
|
|
9726
|
+
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
9727
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
9728
|
+
}
|
|
9729
|
+
return null;
|
|
9730
|
+
}
|
|
9731
|
+
function assertSessionScope(actionType, targetProject) {
|
|
9732
|
+
try {
|
|
9733
|
+
const currentProject = getProjectName();
|
|
9734
|
+
const exeSession = resolveExeSession();
|
|
9735
|
+
if (!exeSession) {
|
|
9736
|
+
return { allowed: true, reason: "no_session" };
|
|
9737
|
+
}
|
|
9738
|
+
if (currentProject === targetProject) {
|
|
9739
|
+
return {
|
|
9740
|
+
allowed: true,
|
|
9741
|
+
reason: "same_session",
|
|
9742
|
+
currentProject,
|
|
9743
|
+
targetProject
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
process.stderr.write(
|
|
9747
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
9748
|
+
`
|
|
9749
|
+
);
|
|
9750
|
+
return {
|
|
9751
|
+
allowed: false,
|
|
9752
|
+
reason: "cross_session_denied",
|
|
9753
|
+
currentProject,
|
|
9754
|
+
targetProject,
|
|
9755
|
+
targetSession: findSessionForProject(targetProject)?.windowName
|
|
9756
|
+
};
|
|
9757
|
+
} catch {
|
|
9758
|
+
return { allowed: true, reason: "no_session" };
|
|
9759
|
+
}
|
|
9760
|
+
}
|
|
9761
|
+
var init_session_scope = __esm({
|
|
9762
|
+
"src/lib/session-scope.ts"() {
|
|
9315
9763
|
"use strict";
|
|
9764
|
+
init_session_registry();
|
|
9765
|
+
init_project_name();
|
|
9316
9766
|
init_tmux_routing();
|
|
9767
|
+
init_employees();
|
|
9317
9768
|
}
|
|
9318
9769
|
});
|
|
9319
9770
|
|
|
@@ -9334,12 +9785,12 @@ __export(tasks_crud_exports, {
|
|
|
9334
9785
|
updateTaskStatus: () => updateTaskStatus,
|
|
9335
9786
|
writeCheckpoint: () => writeCheckpoint
|
|
9336
9787
|
});
|
|
9337
|
-
import
|
|
9338
|
-
import
|
|
9339
|
-
import
|
|
9340
|
-
import { execSync as
|
|
9788
|
+
import crypto8 from "crypto";
|
|
9789
|
+
import path24 from "path";
|
|
9790
|
+
import os14 from "os";
|
|
9791
|
+
import { execSync as execSync7 } from "child_process";
|
|
9341
9792
|
import { mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
9342
|
-
import { existsSync as
|
|
9793
|
+
import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
|
|
9343
9794
|
async function writeCheckpoint(input) {
|
|
9344
9795
|
const client = getClient();
|
|
9345
9796
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -9455,13 +9906,28 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
9455
9906
|
}
|
|
9456
9907
|
async function createTaskCore(input) {
|
|
9457
9908
|
const client = getClient();
|
|
9458
|
-
const id =
|
|
9909
|
+
const id = crypto8.randomUUID();
|
|
9459
9910
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9460
9911
|
const slug = slugify(input.title);
|
|
9461
9912
|
let earlySessionScope = null;
|
|
9913
|
+
let scopeMismatchWarning;
|
|
9462
9914
|
try {
|
|
9463
9915
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
9464
|
-
|
|
9916
|
+
const resolved = resolveExeSession2();
|
|
9917
|
+
if (resolved && input.projectName) {
|
|
9918
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
9919
|
+
const sessionProject = getSessionProject2(resolved);
|
|
9920
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
9921
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
9922
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
9923
|
+
`);
|
|
9924
|
+
earlySessionScope = null;
|
|
9925
|
+
} else {
|
|
9926
|
+
earlySessionScope = resolved;
|
|
9927
|
+
}
|
|
9928
|
+
} else {
|
|
9929
|
+
earlySessionScope = resolved;
|
|
9930
|
+
}
|
|
9465
9931
|
} catch {
|
|
9466
9932
|
}
|
|
9467
9933
|
const scope = earlySessionScope ?? "default";
|
|
@@ -9512,10 +9978,14 @@ async function createTaskCore(input) {
|
|
|
9512
9978
|
${laneWarning}` : laneWarning;
|
|
9513
9979
|
}
|
|
9514
9980
|
}
|
|
9981
|
+
if (scopeMismatchWarning) {
|
|
9982
|
+
warning = warning ? `${warning}
|
|
9983
|
+
${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
9984
|
+
}
|
|
9515
9985
|
if (input.baseDir) {
|
|
9516
9986
|
try {
|
|
9517
|
-
await mkdir5(
|
|
9518
|
-
await mkdir5(
|
|
9987
|
+
await mkdir5(path24.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
9988
|
+
await mkdir5(path24.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
9519
9989
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
9520
9990
|
await ensureGitignoreExe(input.baseDir);
|
|
9521
9991
|
} catch {
|
|
@@ -9551,13 +10021,19 @@ ${laneWarning}` : laneWarning;
|
|
|
9551
10021
|
});
|
|
9552
10022
|
if (input.baseDir) {
|
|
9553
10023
|
try {
|
|
9554
|
-
const EXE_OS_DIR =
|
|
9555
|
-
const mdPath =
|
|
9556
|
-
const mdDir =
|
|
9557
|
-
if (!
|
|
10024
|
+
const EXE_OS_DIR = path24.join(os14.homedir(), ".exe-os");
|
|
10025
|
+
const mdPath = path24.join(EXE_OS_DIR, taskFile);
|
|
10026
|
+
const mdDir = path24.dirname(mdPath);
|
|
10027
|
+
if (!existsSync21(mdDir)) await mkdir5(mdDir, { recursive: true });
|
|
9558
10028
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
9559
10029
|
const mdContent = `# ${input.title}
|
|
9560
10030
|
|
|
10031
|
+
## MANDATORY: When done
|
|
10032
|
+
|
|
10033
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
10034
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
10035
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
10036
|
+
|
|
9561
10037
|
**ID:** ${id}
|
|
9562
10038
|
**Status:** ${initialStatus}
|
|
9563
10039
|
**Priority:** ${input.priority}
|
|
@@ -9571,12 +10047,6 @@ ${laneWarning}` : laneWarning;
|
|
|
9571
10047
|
## Context
|
|
9572
10048
|
|
|
9573
10049
|
${input.context}
|
|
9574
|
-
|
|
9575
|
-
## MANDATORY: When done
|
|
9576
|
-
|
|
9577
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
9578
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
9579
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
9580
10050
|
`;
|
|
9581
10051
|
await writeFile5(mdPath, mdContent, "utf-8");
|
|
9582
10052
|
} catch (err) {
|
|
@@ -9658,14 +10128,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
9658
10128
|
if (!identifier || identifier === "unknown") return true;
|
|
9659
10129
|
try {
|
|
9660
10130
|
if (identifier.startsWith("%")) {
|
|
9661
|
-
const output =
|
|
10131
|
+
const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
|
|
9662
10132
|
timeout: 2e3,
|
|
9663
10133
|
encoding: "utf8",
|
|
9664
10134
|
stdio: ["pipe", "pipe", "pipe"]
|
|
9665
10135
|
});
|
|
9666
10136
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
9667
10137
|
} else {
|
|
9668
|
-
|
|
10138
|
+
execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
9669
10139
|
timeout: 2e3,
|
|
9670
10140
|
stdio: ["pipe", "pipe", "pipe"]
|
|
9671
10141
|
});
|
|
@@ -9674,7 +10144,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
9674
10144
|
} catch {
|
|
9675
10145
|
if (identifier.startsWith("%")) return true;
|
|
9676
10146
|
try {
|
|
9677
|
-
|
|
10147
|
+
execSync7("tmux list-sessions", {
|
|
9678
10148
|
timeout: 2e3,
|
|
9679
10149
|
stdio: ["pipe", "pipe", "pipe"]
|
|
9680
10150
|
});
|
|
@@ -9689,12 +10159,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
9689
10159
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
9690
10160
|
try {
|
|
9691
10161
|
const since = new Date(taskCreatedAt).toISOString();
|
|
9692
|
-
const branch =
|
|
10162
|
+
const branch = execSync7(
|
|
9693
10163
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
9694
10164
|
{ encoding: "utf8", timeout: 3e3 }
|
|
9695
10165
|
).trim();
|
|
9696
10166
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
9697
|
-
const commitCount =
|
|
10167
|
+
const commitCount = execSync7(
|
|
9698
10168
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
9699
10169
|
{ encoding: "utf8", timeout: 5e3 }
|
|
9700
10170
|
).trim();
|
|
@@ -9825,7 +10295,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
9825
10295
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
9826
10296
|
} catch {
|
|
9827
10297
|
}
|
|
9828
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
10298
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
9829
10299
|
try {
|
|
9830
10300
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
9831
10301
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -9854,9 +10324,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
9854
10324
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
9855
10325
|
}
|
|
9856
10326
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
9857
|
-
const archPath =
|
|
10327
|
+
const archPath = path24.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
9858
10328
|
try {
|
|
9859
|
-
if (
|
|
10329
|
+
if (existsSync21(archPath)) return;
|
|
9860
10330
|
const template = [
|
|
9861
10331
|
`# ${projectName} \u2014 System Architecture`,
|
|
9862
10332
|
"",
|
|
@@ -9889,10 +10359,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
9889
10359
|
}
|
|
9890
10360
|
}
|
|
9891
10361
|
async function ensureGitignoreExe(baseDir) {
|
|
9892
|
-
const gitignorePath =
|
|
10362
|
+
const gitignorePath = path24.join(baseDir, ".gitignore");
|
|
9893
10363
|
try {
|
|
9894
|
-
if (
|
|
9895
|
-
const content =
|
|
10364
|
+
if (existsSync21(gitignorePath)) {
|
|
10365
|
+
const content = readFileSync18(gitignorePath, "utf-8");
|
|
9896
10366
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
9897
10367
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
9898
10368
|
} else {
|
|
@@ -9923,58 +10393,42 @@ var init_tasks_crud = __esm({
|
|
|
9923
10393
|
});
|
|
9924
10394
|
|
|
9925
10395
|
// src/lib/tasks-review.ts
|
|
9926
|
-
import
|
|
9927
|
-
import { existsSync as
|
|
10396
|
+
import path25 from "path";
|
|
10397
|
+
import { existsSync as existsSync22, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
9928
10398
|
async function countPendingReviews(sessionScope) {
|
|
9929
10399
|
const client = getClient();
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
args: [sessionScope]
|
|
9934
|
-
});
|
|
9935
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
9936
|
-
}
|
|
10400
|
+
const scope = strictSessionScopeFilter(
|
|
10401
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10402
|
+
);
|
|
9937
10403
|
const result = await client.execute({
|
|
9938
|
-
sql:
|
|
9939
|
-
|
|
10404
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
10405
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
10406
|
+
args: [...scope.args]
|
|
9940
10407
|
});
|
|
9941
10408
|
return Number(result.rows[0]?.cnt) || 0;
|
|
9942
10409
|
}
|
|
9943
10410
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
9944
10411
|
const client = getClient();
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
9949
|
-
AND session_scope = ?`,
|
|
9950
|
-
args: [sinceIso, sessionScope]
|
|
9951
|
-
});
|
|
9952
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
9953
|
-
}
|
|
10412
|
+
const scope = strictSessionScopeFilter(
|
|
10413
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10414
|
+
);
|
|
9954
10415
|
const result = await client.execute({
|
|
9955
10416
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9956
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
9957
|
-
args: [sinceIso]
|
|
10417
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
10418
|
+
args: [sinceIso, ...scope.args]
|
|
9958
10419
|
});
|
|
9959
10420
|
return Number(result.rows[0]?.cnt) || 0;
|
|
9960
10421
|
}
|
|
9961
10422
|
async function listPendingReviews(limit, sessionScope) {
|
|
9962
10423
|
const client = getClient();
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
WHERE status = 'needs_review'
|
|
9967
|
-
AND session_scope = ?
|
|
9968
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
9969
|
-
args: [sessionScope, limit]
|
|
9970
|
-
});
|
|
9971
|
-
return result2.rows;
|
|
9972
|
-
}
|
|
10424
|
+
const scope = strictSessionScopeFilter(
|
|
10425
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10426
|
+
);
|
|
9973
10427
|
const result = await client.execute({
|
|
9974
10428
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
9975
|
-
WHERE status = 'needs_review'
|
|
10429
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
9976
10430
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
9977
|
-
args: [limit]
|
|
10431
|
+
args: [...scope.args, limit]
|
|
9978
10432
|
});
|
|
9979
10433
|
return result.rows;
|
|
9980
10434
|
}
|
|
@@ -9986,7 +10440,7 @@ async function cleanupOrphanedReviews() {
|
|
|
9986
10440
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
9987
10441
|
AND assigned_by = 'system'
|
|
9988
10442
|
AND title LIKE 'Review:%'
|
|
9989
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
10443
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
9990
10444
|
args: [now]
|
|
9991
10445
|
});
|
|
9992
10446
|
const r1b = await client.execute({
|
|
@@ -10105,11 +10559,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
10105
10559
|
);
|
|
10106
10560
|
}
|
|
10107
10561
|
try {
|
|
10108
|
-
const cacheDir =
|
|
10109
|
-
if (
|
|
10562
|
+
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
10563
|
+
if (existsSync22(cacheDir)) {
|
|
10110
10564
|
for (const f of readdirSync5(cacheDir)) {
|
|
10111
10565
|
if (f.startsWith("review-notified-")) {
|
|
10112
|
-
unlinkSync6(
|
|
10566
|
+
unlinkSync6(path25.join(cacheDir, f));
|
|
10113
10567
|
}
|
|
10114
10568
|
}
|
|
10115
10569
|
}
|
|
@@ -10126,11 +10580,12 @@ var init_tasks_review = __esm({
|
|
|
10126
10580
|
init_tmux_routing();
|
|
10127
10581
|
init_session_key();
|
|
10128
10582
|
init_state_bus();
|
|
10583
|
+
init_task_scope();
|
|
10129
10584
|
}
|
|
10130
10585
|
});
|
|
10131
10586
|
|
|
10132
10587
|
// src/lib/tasks-chain.ts
|
|
10133
|
-
import
|
|
10588
|
+
import path26 from "path";
|
|
10134
10589
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
10135
10590
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
10136
10591
|
const client = getClient();
|
|
@@ -10147,7 +10602,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
10147
10602
|
});
|
|
10148
10603
|
for (const ur of unblockedRows.rows) {
|
|
10149
10604
|
try {
|
|
10150
|
-
const ubFile =
|
|
10605
|
+
const ubFile = path26.join(baseDir, String(ur.task_file));
|
|
10151
10606
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
10152
10607
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
10153
10608
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -10182,7 +10637,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
10182
10637
|
const scScope = sessionScopeFilter();
|
|
10183
10638
|
const remaining = await client.execute({
|
|
10184
10639
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
10185
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
10640
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
10186
10641
|
args: [parentTaskId, ...scScope.args]
|
|
10187
10642
|
});
|
|
10188
10643
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -10214,110 +10669,6 @@ var init_tasks_chain = __esm({
|
|
|
10214
10669
|
}
|
|
10215
10670
|
});
|
|
10216
10671
|
|
|
10217
|
-
// src/lib/project-name.ts
|
|
10218
|
-
import { execSync as execSync7 } from "child_process";
|
|
10219
|
-
import path25 from "path";
|
|
10220
|
-
function getProjectName(cwd2) {
|
|
10221
|
-
const dir = cwd2 ?? process.cwd();
|
|
10222
|
-
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
10223
|
-
try {
|
|
10224
|
-
let repoRoot;
|
|
10225
|
-
try {
|
|
10226
|
-
const gitCommonDir = execSync7("git rev-parse --path-format=absolute --git-common-dir", {
|
|
10227
|
-
cwd: dir,
|
|
10228
|
-
encoding: "utf8",
|
|
10229
|
-
timeout: 2e3,
|
|
10230
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10231
|
-
}).trim();
|
|
10232
|
-
repoRoot = path25.dirname(gitCommonDir);
|
|
10233
|
-
} catch {
|
|
10234
|
-
repoRoot = execSync7("git rev-parse --show-toplevel", {
|
|
10235
|
-
cwd: dir,
|
|
10236
|
-
encoding: "utf8",
|
|
10237
|
-
timeout: 2e3,
|
|
10238
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10239
|
-
}).trim();
|
|
10240
|
-
}
|
|
10241
|
-
_cached2 = path25.basename(repoRoot);
|
|
10242
|
-
_cachedCwd = dir;
|
|
10243
|
-
return _cached2;
|
|
10244
|
-
} catch {
|
|
10245
|
-
_cached2 = path25.basename(dir);
|
|
10246
|
-
_cachedCwd = dir;
|
|
10247
|
-
return _cached2;
|
|
10248
|
-
}
|
|
10249
|
-
}
|
|
10250
|
-
var _cached2, _cachedCwd;
|
|
10251
|
-
var init_project_name = __esm({
|
|
10252
|
-
"src/lib/project-name.ts"() {
|
|
10253
|
-
"use strict";
|
|
10254
|
-
_cached2 = null;
|
|
10255
|
-
_cachedCwd = null;
|
|
10256
|
-
}
|
|
10257
|
-
});
|
|
10258
|
-
|
|
10259
|
-
// src/lib/session-scope.ts
|
|
10260
|
-
var session_scope_exports = {};
|
|
10261
|
-
__export(session_scope_exports, {
|
|
10262
|
-
assertSessionScope: () => assertSessionScope,
|
|
10263
|
-
findSessionForProject: () => findSessionForProject,
|
|
10264
|
-
getSessionProject: () => getSessionProject
|
|
10265
|
-
});
|
|
10266
|
-
function getSessionProject(sessionName) {
|
|
10267
|
-
const sessions = listSessions();
|
|
10268
|
-
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
10269
|
-
if (!entry) return null;
|
|
10270
|
-
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
10271
|
-
return parts[parts.length - 1] ?? null;
|
|
10272
|
-
}
|
|
10273
|
-
function findSessionForProject(projectName) {
|
|
10274
|
-
const sessions = listSessions();
|
|
10275
|
-
for (const s of sessions) {
|
|
10276
|
-
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
10277
|
-
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
10278
|
-
}
|
|
10279
|
-
return null;
|
|
10280
|
-
}
|
|
10281
|
-
function assertSessionScope(actionType, targetProject) {
|
|
10282
|
-
try {
|
|
10283
|
-
const currentProject = getProjectName();
|
|
10284
|
-
const exeSession = resolveExeSession();
|
|
10285
|
-
if (!exeSession) {
|
|
10286
|
-
return { allowed: true, reason: "no_session" };
|
|
10287
|
-
}
|
|
10288
|
-
if (currentProject === targetProject) {
|
|
10289
|
-
return {
|
|
10290
|
-
allowed: true,
|
|
10291
|
-
reason: "same_session",
|
|
10292
|
-
currentProject,
|
|
10293
|
-
targetProject
|
|
10294
|
-
};
|
|
10295
|
-
}
|
|
10296
|
-
process.stderr.write(
|
|
10297
|
-
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
10298
|
-
`
|
|
10299
|
-
);
|
|
10300
|
-
return {
|
|
10301
|
-
allowed: false,
|
|
10302
|
-
reason: "cross_session_denied",
|
|
10303
|
-
currentProject,
|
|
10304
|
-
targetProject,
|
|
10305
|
-
targetSession: findSessionForProject(targetProject)?.windowName
|
|
10306
|
-
};
|
|
10307
|
-
} catch {
|
|
10308
|
-
return { allowed: true, reason: "no_session" };
|
|
10309
|
-
}
|
|
10310
|
-
}
|
|
10311
|
-
var init_session_scope = __esm({
|
|
10312
|
-
"src/lib/session-scope.ts"() {
|
|
10313
|
-
"use strict";
|
|
10314
|
-
init_session_registry();
|
|
10315
|
-
init_project_name();
|
|
10316
|
-
init_tmux_routing();
|
|
10317
|
-
init_employees();
|
|
10318
|
-
}
|
|
10319
|
-
});
|
|
10320
|
-
|
|
10321
10672
|
// src/lib/tasks-notify.ts
|
|
10322
10673
|
async function dispatchTaskToEmployee(input) {
|
|
10323
10674
|
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
@@ -10385,10 +10736,10 @@ var init_tasks_notify = __esm({
|
|
|
10385
10736
|
});
|
|
10386
10737
|
|
|
10387
10738
|
// src/lib/behaviors.ts
|
|
10388
|
-
import
|
|
10739
|
+
import crypto9 from "crypto";
|
|
10389
10740
|
async function storeBehavior(opts) {
|
|
10390
10741
|
const client = getClient();
|
|
10391
|
-
const id =
|
|
10742
|
+
const id = crypto9.randomUUID();
|
|
10392
10743
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10393
10744
|
await client.execute({
|
|
10394
10745
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -10417,7 +10768,7 @@ __export(skill_learning_exports, {
|
|
|
10417
10768
|
storeTrajectory: () => storeTrajectory,
|
|
10418
10769
|
sweepTrajectories: () => sweepTrajectories
|
|
10419
10770
|
});
|
|
10420
|
-
import
|
|
10771
|
+
import crypto10 from "crypto";
|
|
10421
10772
|
async function extractTrajectory(taskId, agentId) {
|
|
10422
10773
|
const client = getClient();
|
|
10423
10774
|
const result = await client.execute({
|
|
@@ -10446,11 +10797,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
10446
10797
|
return signature;
|
|
10447
10798
|
}
|
|
10448
10799
|
function hashSignature(signature) {
|
|
10449
|
-
return
|
|
10800
|
+
return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
10450
10801
|
}
|
|
10451
10802
|
async function storeTrajectory(opts) {
|
|
10452
10803
|
const client = getClient();
|
|
10453
|
-
const id =
|
|
10804
|
+
const id = crypto10.randomUUID();
|
|
10454
10805
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10455
10806
|
const signatureHash = hashSignature(opts.signature);
|
|
10456
10807
|
await client.execute({
|
|
@@ -10715,8 +11066,8 @@ __export(tasks_exports, {
|
|
|
10715
11066
|
updateTaskStatus: () => updateTaskStatus,
|
|
10716
11067
|
writeCheckpoint: () => writeCheckpoint
|
|
10717
11068
|
});
|
|
10718
|
-
import
|
|
10719
|
-
import { writeFileSync as
|
|
11069
|
+
import path27 from "path";
|
|
11070
|
+
import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
10720
11071
|
async function createTask(input) {
|
|
10721
11072
|
const result = await createTaskCore(input);
|
|
10722
11073
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -10735,12 +11086,12 @@ async function updateTask(input) {
|
|
|
10735
11086
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
10736
11087
|
try {
|
|
10737
11088
|
const agent = String(row.assigned_to);
|
|
10738
|
-
const cacheDir =
|
|
10739
|
-
const cachePath =
|
|
11089
|
+
const cacheDir = path27.join(EXE_AI_DIR, "session-cache");
|
|
11090
|
+
const cachePath = path27.join(cacheDir, `current-task-${agent}.json`);
|
|
10740
11091
|
if (input.status === "in_progress") {
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
11092
|
+
mkdirSync13(cacheDir, { recursive: true });
|
|
11093
|
+
writeFileSync14(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
11094
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
10744
11095
|
try {
|
|
10745
11096
|
unlinkSync7(cachePath);
|
|
10746
11097
|
} catch {
|
|
@@ -10748,10 +11099,10 @@ async function updateTask(input) {
|
|
|
10748
11099
|
}
|
|
10749
11100
|
} catch {
|
|
10750
11101
|
}
|
|
10751
|
-
if (input.status === "done") {
|
|
11102
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10752
11103
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
10753
11104
|
}
|
|
10754
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
11105
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
10755
11106
|
try {
|
|
10756
11107
|
const client = getClient();
|
|
10757
11108
|
const taskTitle = String(row.title);
|
|
@@ -10767,7 +11118,7 @@ async function updateTask(input) {
|
|
|
10767
11118
|
if (!isCoordinatorName(assignedAgent)) {
|
|
10768
11119
|
try {
|
|
10769
11120
|
const draftClient = getClient();
|
|
10770
|
-
if (input.status === "done") {
|
|
11121
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10771
11122
|
await draftClient.execute({
|
|
10772
11123
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
10773
11124
|
args: [assignedAgent]
|
|
@@ -10784,7 +11135,7 @@ async function updateTask(input) {
|
|
|
10784
11135
|
try {
|
|
10785
11136
|
const client = getClient();
|
|
10786
11137
|
const cascaded = await client.execute({
|
|
10787
|
-
sql: `UPDATE tasks SET status = '
|
|
11138
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
10788
11139
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
10789
11140
|
args: [now, taskId]
|
|
10790
11141
|
});
|
|
@@ -10797,14 +11148,14 @@ async function updateTask(input) {
|
|
|
10797
11148
|
} catch {
|
|
10798
11149
|
}
|
|
10799
11150
|
}
|
|
10800
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
11151
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
10801
11152
|
if (isTerminal) {
|
|
10802
11153
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
10803
11154
|
if (!isCoordinator) {
|
|
10804
11155
|
notifyTaskDone();
|
|
10805
11156
|
}
|
|
10806
11157
|
await markTaskNotificationsRead(taskFile);
|
|
10807
|
-
if (input.status === "done") {
|
|
11158
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10808
11159
|
try {
|
|
10809
11160
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
10810
11161
|
} catch {
|
|
@@ -10824,7 +11175,7 @@ async function updateTask(input) {
|
|
|
10824
11175
|
}
|
|
10825
11176
|
}
|
|
10826
11177
|
}
|
|
10827
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
11178
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
10828
11179
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
10829
11180
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
10830
11181
|
taskId,
|
|
@@ -11196,6 +11547,7 @@ __export(tmux_routing_exports, {
|
|
|
11196
11547
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
11197
11548
|
isExeSession: () => isExeSession,
|
|
11198
11549
|
isSessionBusy: () => isSessionBusy,
|
|
11550
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
11199
11551
|
notifyParentExe: () => notifyParentExe,
|
|
11200
11552
|
parseParentExe: () => parseParentExe,
|
|
11201
11553
|
registerParentExe: () => registerParentExe,
|
|
@@ -11206,13 +11558,13 @@ __export(tmux_routing_exports, {
|
|
|
11206
11558
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
11207
11559
|
});
|
|
11208
11560
|
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
11209
|
-
import { readFileSync as
|
|
11210
|
-
import
|
|
11211
|
-
import
|
|
11561
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14, existsSync as existsSync23, appendFileSync as appendFileSync2, readdirSync as readdirSync6 } from "fs";
|
|
11562
|
+
import path28 from "path";
|
|
11563
|
+
import os15 from "os";
|
|
11212
11564
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11213
11565
|
import { unlinkSync as unlinkSync8 } from "fs";
|
|
11214
11566
|
function spawnLockPath(sessionName) {
|
|
11215
|
-
return
|
|
11567
|
+
return path28.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
11216
11568
|
}
|
|
11217
11569
|
function isProcessAlive(pid) {
|
|
11218
11570
|
try {
|
|
@@ -11223,13 +11575,13 @@ function isProcessAlive(pid) {
|
|
|
11223
11575
|
}
|
|
11224
11576
|
}
|
|
11225
11577
|
function acquireSpawnLock2(sessionName) {
|
|
11226
|
-
if (!
|
|
11227
|
-
|
|
11578
|
+
if (!existsSync23(SPAWN_LOCK_DIR)) {
|
|
11579
|
+
mkdirSync14(SPAWN_LOCK_DIR, { recursive: true });
|
|
11228
11580
|
}
|
|
11229
11581
|
const lockFile = spawnLockPath(sessionName);
|
|
11230
|
-
if (
|
|
11582
|
+
if (existsSync23(lockFile)) {
|
|
11231
11583
|
try {
|
|
11232
|
-
const lock = JSON.parse(
|
|
11584
|
+
const lock = JSON.parse(readFileSync19(lockFile, "utf8"));
|
|
11233
11585
|
const age = Date.now() - lock.timestamp;
|
|
11234
11586
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
11235
11587
|
return false;
|
|
@@ -11237,7 +11589,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
11237
11589
|
} catch {
|
|
11238
11590
|
}
|
|
11239
11591
|
}
|
|
11240
|
-
|
|
11592
|
+
writeFileSync15(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
11241
11593
|
return true;
|
|
11242
11594
|
}
|
|
11243
11595
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -11249,13 +11601,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
11249
11601
|
function resolveBehaviorsExporterScript() {
|
|
11250
11602
|
try {
|
|
11251
11603
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
11252
|
-
const scriptPath =
|
|
11253
|
-
|
|
11604
|
+
const scriptPath = path28.join(
|
|
11605
|
+
path28.dirname(thisFile),
|
|
11254
11606
|
"..",
|
|
11255
11607
|
"bin",
|
|
11256
11608
|
"exe-export-behaviors.js"
|
|
11257
11609
|
);
|
|
11258
|
-
return
|
|
11610
|
+
return existsSync23(scriptPath) ? scriptPath : null;
|
|
11259
11611
|
} catch {
|
|
11260
11612
|
return null;
|
|
11261
11613
|
}
|
|
@@ -11321,12 +11673,12 @@ function extractRootExe(name) {
|
|
|
11321
11673
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
11322
11674
|
}
|
|
11323
11675
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
11324
|
-
if (!
|
|
11325
|
-
|
|
11676
|
+
if (!existsSync23(SESSION_CACHE)) {
|
|
11677
|
+
mkdirSync14(SESSION_CACHE, { recursive: true });
|
|
11326
11678
|
}
|
|
11327
11679
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
11328
|
-
const filePath =
|
|
11329
|
-
|
|
11680
|
+
const filePath = path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
11681
|
+
writeFileSync15(filePath, JSON.stringify({
|
|
11330
11682
|
parentExe: rootExe,
|
|
11331
11683
|
dispatchedBy: dispatchedBy || rootExe,
|
|
11332
11684
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11334,7 +11686,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
11334
11686
|
}
|
|
11335
11687
|
function getParentExe(sessionKey) {
|
|
11336
11688
|
try {
|
|
11337
|
-
const data = JSON.parse(
|
|
11689
|
+
const data = JSON.parse(readFileSync19(path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
11338
11690
|
return data.parentExe || null;
|
|
11339
11691
|
} catch {
|
|
11340
11692
|
return null;
|
|
@@ -11342,8 +11694,8 @@ function getParentExe(sessionKey) {
|
|
|
11342
11694
|
}
|
|
11343
11695
|
function getDispatchedBy(sessionKey) {
|
|
11344
11696
|
try {
|
|
11345
|
-
const data = JSON.parse(
|
|
11346
|
-
|
|
11697
|
+
const data = JSON.parse(readFileSync19(
|
|
11698
|
+
path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
11347
11699
|
"utf8"
|
|
11348
11700
|
));
|
|
11349
11701
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -11413,8 +11765,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
11413
11765
|
}
|
|
11414
11766
|
function readDebounceState() {
|
|
11415
11767
|
try {
|
|
11416
|
-
if (!
|
|
11417
|
-
const raw = JSON.parse(
|
|
11768
|
+
if (!existsSync23(DEBOUNCE_FILE)) return {};
|
|
11769
|
+
const raw = JSON.parse(readFileSync19(DEBOUNCE_FILE, "utf8"));
|
|
11418
11770
|
const state = {};
|
|
11419
11771
|
for (const [key, val] of Object.entries(raw)) {
|
|
11420
11772
|
if (typeof val === "number") {
|
|
@@ -11430,8 +11782,8 @@ function readDebounceState() {
|
|
|
11430
11782
|
}
|
|
11431
11783
|
function writeDebounceState(state) {
|
|
11432
11784
|
try {
|
|
11433
|
-
if (!
|
|
11434
|
-
|
|
11785
|
+
if (!existsSync23(SESSION_CACHE)) mkdirSync14(SESSION_CACHE, { recursive: true });
|
|
11786
|
+
writeFileSync15(DEBOUNCE_FILE, JSON.stringify(state));
|
|
11435
11787
|
} catch {
|
|
11436
11788
|
}
|
|
11437
11789
|
}
|
|
@@ -11529,8 +11881,8 @@ function sendIntercom(targetSession) {
|
|
|
11529
11881
|
try {
|
|
11530
11882
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
11531
11883
|
const agent = baseAgentName(rawAgent);
|
|
11532
|
-
const markerPath =
|
|
11533
|
-
if (
|
|
11884
|
+
const markerPath = path28.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
11885
|
+
if (existsSync23(markerPath)) {
|
|
11534
11886
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
11535
11887
|
return "debounced";
|
|
11536
11888
|
}
|
|
@@ -11539,8 +11891,8 @@ function sendIntercom(targetSession) {
|
|
|
11539
11891
|
try {
|
|
11540
11892
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
11541
11893
|
const agent = baseAgentName(rawAgent);
|
|
11542
|
-
const taskDir =
|
|
11543
|
-
if (
|
|
11894
|
+
const taskDir = path28.join(process.cwd(), "exe", agent);
|
|
11895
|
+
if (existsSync23(taskDir)) {
|
|
11544
11896
|
const files = readdirSync6(taskDir).filter(
|
|
11545
11897
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
11546
11898
|
);
|
|
@@ -11600,6 +11952,21 @@ function notifyParentExe(sessionKey) {
|
|
|
11600
11952
|
}
|
|
11601
11953
|
return true;
|
|
11602
11954
|
}
|
|
11955
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
11956
|
+
const transport = getTransport();
|
|
11957
|
+
try {
|
|
11958
|
+
const sessions = transport.listSessions();
|
|
11959
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
11960
|
+
execSync8(
|
|
11961
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
11962
|
+
{ timeout: 3e3 }
|
|
11963
|
+
);
|
|
11964
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
11965
|
+
return true;
|
|
11966
|
+
} catch {
|
|
11967
|
+
return false;
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11603
11970
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
11604
11971
|
if (isCoordinatorName(employeeName)) {
|
|
11605
11972
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -11673,26 +12040,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11673
12040
|
const transport = getTransport();
|
|
11674
12041
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
11675
12042
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
11676
|
-
const logDir =
|
|
11677
|
-
const logFile =
|
|
11678
|
-
if (!
|
|
11679
|
-
|
|
12043
|
+
const logDir = path28.join(os15.homedir(), ".exe-os", "session-logs");
|
|
12044
|
+
const logFile = path28.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
12045
|
+
if (!existsSync23(logDir)) {
|
|
12046
|
+
mkdirSync14(logDir, { recursive: true });
|
|
11680
12047
|
}
|
|
11681
12048
|
transport.kill(sessionName);
|
|
11682
12049
|
let cleanupSuffix = "";
|
|
11683
12050
|
try {
|
|
11684
12051
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
11685
|
-
const cleanupScript =
|
|
11686
|
-
if (
|
|
12052
|
+
const cleanupScript = path28.join(path28.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
12053
|
+
if (existsSync23(cleanupScript)) {
|
|
11687
12054
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
11688
12055
|
}
|
|
11689
12056
|
} catch {
|
|
11690
12057
|
}
|
|
11691
12058
|
try {
|
|
11692
|
-
const claudeJsonPath =
|
|
12059
|
+
const claudeJsonPath = path28.join(os15.homedir(), ".claude.json");
|
|
11693
12060
|
let claudeJson = {};
|
|
11694
12061
|
try {
|
|
11695
|
-
claudeJson = JSON.parse(
|
|
12062
|
+
claudeJson = JSON.parse(readFileSync19(claudeJsonPath, "utf8"));
|
|
11696
12063
|
} catch {
|
|
11697
12064
|
}
|
|
11698
12065
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -11700,17 +12067,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11700
12067
|
const trustDir = opts?.cwd ?? projectDir;
|
|
11701
12068
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
11702
12069
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
11703
|
-
|
|
12070
|
+
writeFileSync15(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
11704
12071
|
} catch {
|
|
11705
12072
|
}
|
|
11706
12073
|
try {
|
|
11707
|
-
const settingsDir =
|
|
12074
|
+
const settingsDir = path28.join(os15.homedir(), ".claude", "projects");
|
|
11708
12075
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
11709
|
-
const projSettingsDir =
|
|
11710
|
-
const settingsPath =
|
|
12076
|
+
const projSettingsDir = path28.join(settingsDir, normalizedKey);
|
|
12077
|
+
const settingsPath = path28.join(projSettingsDir, "settings.json");
|
|
11711
12078
|
let settings = {};
|
|
11712
12079
|
try {
|
|
11713
|
-
settings = JSON.parse(
|
|
12080
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
11714
12081
|
} catch {
|
|
11715
12082
|
}
|
|
11716
12083
|
const perms = settings.permissions ?? {};
|
|
@@ -11738,8 +12105,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11738
12105
|
if (changed) {
|
|
11739
12106
|
perms.allow = allow;
|
|
11740
12107
|
settings.permissions = perms;
|
|
11741
|
-
|
|
11742
|
-
|
|
12108
|
+
mkdirSync14(projSettingsDir, { recursive: true });
|
|
12109
|
+
writeFileSync15(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
11743
12110
|
}
|
|
11744
12111
|
} catch {
|
|
11745
12112
|
}
|
|
@@ -11754,8 +12121,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11754
12121
|
let behaviorsFlag = "";
|
|
11755
12122
|
let legacyFallbackWarned = false;
|
|
11756
12123
|
if (!useExeAgent && !useBinSymlink) {
|
|
11757
|
-
const identityPath2 =
|
|
11758
|
-
|
|
12124
|
+
const identityPath2 = path28.join(
|
|
12125
|
+
os15.homedir(),
|
|
11759
12126
|
".exe-os",
|
|
11760
12127
|
"identity",
|
|
11761
12128
|
`${employeeName}.md`
|
|
@@ -11764,13 +12131,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11764
12131
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
11765
12132
|
if (hasAgentFlag) {
|
|
11766
12133
|
identityFlag = ` --agent ${employeeName}`;
|
|
11767
|
-
} else if (
|
|
12134
|
+
} else if (existsSync23(identityPath2)) {
|
|
11768
12135
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
11769
12136
|
legacyFallbackWarned = true;
|
|
11770
12137
|
}
|
|
11771
12138
|
const behaviorsFile = exportBehaviorsSync(
|
|
11772
12139
|
employeeName,
|
|
11773
|
-
|
|
12140
|
+
path28.basename(spawnCwd),
|
|
11774
12141
|
sessionName
|
|
11775
12142
|
);
|
|
11776
12143
|
if (behaviorsFile) {
|
|
@@ -11785,16 +12152,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11785
12152
|
}
|
|
11786
12153
|
let sessionContextFlag = "";
|
|
11787
12154
|
try {
|
|
11788
|
-
const ctxDir =
|
|
11789
|
-
|
|
11790
|
-
const ctxFile =
|
|
12155
|
+
const ctxDir = path28.join(os15.homedir(), ".exe-os", "session-cache");
|
|
12156
|
+
mkdirSync14(ctxDir, { recursive: true });
|
|
12157
|
+
const ctxFile = path28.join(ctxDir, `session-context-${sessionName}.md`);
|
|
11791
12158
|
const ctxContent = [
|
|
11792
12159
|
`## Session Context`,
|
|
11793
12160
|
`You are running in tmux session: ${sessionName}.`,
|
|
11794
12161
|
`Your parent coordinator session is ${exeSession}.`,
|
|
11795
12162
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
11796
12163
|
].join("\n");
|
|
11797
|
-
|
|
12164
|
+
writeFileSync15(ctxFile, ctxContent);
|
|
11798
12165
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
11799
12166
|
} catch {
|
|
11800
12167
|
}
|
|
@@ -11871,8 +12238,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11871
12238
|
transport.pipeLog(sessionName, logFile);
|
|
11872
12239
|
try {
|
|
11873
12240
|
const mySession = getMySession();
|
|
11874
|
-
const dispatchInfo =
|
|
11875
|
-
|
|
12241
|
+
const dispatchInfo = path28.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
12242
|
+
writeFileSync15(dispatchInfo, JSON.stringify({
|
|
11876
12243
|
dispatchedBy: mySession,
|
|
11877
12244
|
rootExe: exeSession,
|
|
11878
12245
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -11946,15 +12313,15 @@ var init_tmux_routing = __esm({
|
|
|
11946
12313
|
init_intercom_queue();
|
|
11947
12314
|
init_plan_limits();
|
|
11948
12315
|
init_employees();
|
|
11949
|
-
SPAWN_LOCK_DIR =
|
|
11950
|
-
SESSION_CACHE =
|
|
12316
|
+
SPAWN_LOCK_DIR = path28.join(os15.homedir(), ".exe-os", "spawn-locks");
|
|
12317
|
+
SESSION_CACHE = path28.join(os15.homedir(), ".exe-os", "session-cache");
|
|
11951
12318
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
11952
12319
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
11953
12320
|
VERIFY_PANE_LINES = 200;
|
|
11954
12321
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
11955
12322
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
11956
|
-
INTERCOM_LOG2 =
|
|
11957
|
-
DEBOUNCE_FILE =
|
|
12323
|
+
INTERCOM_LOG2 = path28.join(os15.homedir(), ".exe-os", "intercom.log");
|
|
12324
|
+
DEBOUNCE_FILE = path28.join(SESSION_CACHE, "intercom-debounce.json");
|
|
11958
12325
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
11959
12326
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
11960
12327
|
}
|
|
@@ -11977,10 +12344,10 @@ __export(messaging_exports, {
|
|
|
11977
12344
|
sendMessage: () => sendMessage,
|
|
11978
12345
|
setWsClientSend: () => setWsClientSend
|
|
11979
12346
|
});
|
|
11980
|
-
import
|
|
12347
|
+
import crypto11 from "crypto";
|
|
11981
12348
|
function generateUlid() {
|
|
11982
12349
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
11983
|
-
const random =
|
|
12350
|
+
const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
|
|
11984
12351
|
return (timestamp + random).toUpperCase();
|
|
11985
12352
|
}
|
|
11986
12353
|
function rowToMessage(row) {
|
|
@@ -11991,6 +12358,7 @@ function rowToMessage(row) {
|
|
|
11991
12358
|
targetAgent: row.target_agent,
|
|
11992
12359
|
targetProject: row.target_project ?? null,
|
|
11993
12360
|
targetDevice: row.target_device,
|
|
12361
|
+
sessionScope: row.session_scope ?? null,
|
|
11994
12362
|
content: row.content,
|
|
11995
12363
|
priority: row.priority ?? "normal",
|
|
11996
12364
|
status: row.status ?? "pending",
|
|
@@ -12008,15 +12376,17 @@ async function sendMessage(input) {
|
|
|
12008
12376
|
const id = generateUlid();
|
|
12009
12377
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12010
12378
|
const targetDevice = input.targetDevice ?? "local";
|
|
12379
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
12011
12380
|
await client.execute({
|
|
12012
|
-
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
|
|
12013
|
-
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
12381
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
12382
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
12014
12383
|
args: [
|
|
12015
12384
|
id,
|
|
12016
12385
|
input.fromAgent,
|
|
12017
12386
|
input.targetAgent,
|
|
12018
12387
|
input.targetProject ?? null,
|
|
12019
12388
|
targetDevice,
|
|
12389
|
+
sessionScope,
|
|
12020
12390
|
input.content,
|
|
12021
12391
|
input.priority ?? "normal",
|
|
12022
12392
|
now
|
|
@@ -12030,9 +12400,10 @@ async function sendMessage(input) {
|
|
|
12030
12400
|
}
|
|
12031
12401
|
} catch {
|
|
12032
12402
|
}
|
|
12403
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
12033
12404
|
const result = await client.execute({
|
|
12034
|
-
sql:
|
|
12035
|
-
args: [id]
|
|
12405
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
12406
|
+
args: [id, ...sentScope.args]
|
|
12036
12407
|
});
|
|
12037
12408
|
return rowToMessage(result.rows[0]);
|
|
12038
12409
|
}
|
|
@@ -12056,6 +12427,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
12056
12427
|
fromAgent: msg.fromAgent,
|
|
12057
12428
|
targetAgent: msg.targetAgent,
|
|
12058
12429
|
targetProject: msg.targetProject,
|
|
12430
|
+
sessionScope: msg.sessionScope,
|
|
12059
12431
|
content: msg.content,
|
|
12060
12432
|
priority: msg.priority,
|
|
12061
12433
|
createdAt: msg.createdAt
|
|
@@ -12099,7 +12471,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
12099
12471
|
} catch {
|
|
12100
12472
|
const newRetryCount = msg.retryCount + 1;
|
|
12101
12473
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
12102
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
12474
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
12103
12475
|
} else {
|
|
12104
12476
|
await client.execute({
|
|
12105
12477
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -12109,85 +12481,101 @@ async function deliverLocalMessage(messageId) {
|
|
|
12109
12481
|
return false;
|
|
12110
12482
|
}
|
|
12111
12483
|
}
|
|
12112
|
-
async function getPendingMessages(targetAgent) {
|
|
12484
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
12113
12485
|
const client = getClient();
|
|
12486
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12114
12487
|
const result = await client.execute({
|
|
12115
12488
|
sql: `SELECT * FROM messages
|
|
12116
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
12489
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
12117
12490
|
ORDER BY id`,
|
|
12118
|
-
args: [targetAgent]
|
|
12491
|
+
args: [targetAgent, ...scope.args]
|
|
12119
12492
|
});
|
|
12120
12493
|
return result.rows.map((row) => rowToMessage(row));
|
|
12121
12494
|
}
|
|
12122
|
-
async function markRead(messageId) {
|
|
12495
|
+
async function markRead(messageId, sessionScope) {
|
|
12123
12496
|
const client = getClient();
|
|
12497
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12124
12498
|
await client.execute({
|
|
12125
|
-
sql:
|
|
12126
|
-
|
|
12499
|
+
sql: `UPDATE messages SET status = 'read'
|
|
12500
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
12501
|
+
args: [messageId, ...scope.args]
|
|
12127
12502
|
});
|
|
12128
12503
|
}
|
|
12129
|
-
async function markAcknowledged(messageId) {
|
|
12504
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
12130
12505
|
const client = getClient();
|
|
12506
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12131
12507
|
await client.execute({
|
|
12132
|
-
sql:
|
|
12133
|
-
|
|
12508
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
12509
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
12510
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
12134
12511
|
});
|
|
12135
12512
|
}
|
|
12136
|
-
async function markProcessed(messageId) {
|
|
12513
|
+
async function markProcessed(messageId, sessionScope) {
|
|
12137
12514
|
const client = getClient();
|
|
12515
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12138
12516
|
await client.execute({
|
|
12139
|
-
sql:
|
|
12140
|
-
|
|
12517
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
12518
|
+
WHERE id = ?${scope.sql}`,
|
|
12519
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
12141
12520
|
});
|
|
12142
12521
|
}
|
|
12143
|
-
async function getMessageStatus(messageId) {
|
|
12522
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
12144
12523
|
const client = getClient();
|
|
12524
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12145
12525
|
const result = await client.execute({
|
|
12146
|
-
sql:
|
|
12147
|
-
args: [messageId]
|
|
12526
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
12527
|
+
args: [messageId, ...scope.args]
|
|
12148
12528
|
});
|
|
12149
12529
|
return result.rows[0]?.status ?? null;
|
|
12150
12530
|
}
|
|
12151
|
-
async function getUnacknowledgedMessages(targetAgent) {
|
|
12531
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
12152
12532
|
const client = getClient();
|
|
12533
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12153
12534
|
const result = await client.execute({
|
|
12154
12535
|
sql: `SELECT * FROM messages
|
|
12155
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
|
|
12536
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
12156
12537
|
ORDER BY id`,
|
|
12157
|
-
args: [targetAgent]
|
|
12538
|
+
args: [targetAgent, ...scope.args]
|
|
12158
12539
|
});
|
|
12159
12540
|
return result.rows.map((row) => rowToMessage(row));
|
|
12160
12541
|
}
|
|
12161
|
-
async function getReadMessages(targetAgent) {
|
|
12542
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
12162
12543
|
const client = getClient();
|
|
12544
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12163
12545
|
const result = await client.execute({
|
|
12164
|
-
sql:
|
|
12165
|
-
|
|
12546
|
+
sql: `SELECT * FROM messages
|
|
12547
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
12548
|
+
ORDER BY id`,
|
|
12549
|
+
args: [targetAgent, ...scope.args]
|
|
12166
12550
|
});
|
|
12167
12551
|
return result.rows.map((row) => rowToMessage(row));
|
|
12168
12552
|
}
|
|
12169
|
-
async function markFailed(messageId, reason) {
|
|
12553
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
12170
12554
|
const client = getClient();
|
|
12555
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12171
12556
|
await client.execute({
|
|
12172
|
-
sql:
|
|
12173
|
-
|
|
12557
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
12558
|
+
WHERE id = ?${scope.sql}`,
|
|
12559
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
12174
12560
|
});
|
|
12175
12561
|
}
|
|
12176
|
-
async function getFailedMessages() {
|
|
12562
|
+
async function getFailedMessages(sessionScope) {
|
|
12177
12563
|
const client = getClient();
|
|
12564
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12178
12565
|
const result = await client.execute({
|
|
12179
|
-
sql:
|
|
12180
|
-
args: []
|
|
12566
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
12567
|
+
args: [...scope.args]
|
|
12181
12568
|
});
|
|
12182
12569
|
return result.rows.map((row) => rowToMessage(row));
|
|
12183
12570
|
}
|
|
12184
|
-
async function retryPendingMessages() {
|
|
12571
|
+
async function retryPendingMessages(sessionScope) {
|
|
12185
12572
|
const client = getClient();
|
|
12573
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12186
12574
|
const result = await client.execute({
|
|
12187
12575
|
sql: `SELECT * FROM messages
|
|
12188
|
-
WHERE status = 'pending' AND retry_count <
|
|
12576
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
12189
12577
|
ORDER BY id`,
|
|
12190
|
-
args: [MAX_RETRIES3]
|
|
12578
|
+
args: [MAX_RETRIES3, ...scope.args]
|
|
12191
12579
|
});
|
|
12192
12580
|
let delivered = 0;
|
|
12193
12581
|
for (const row of result.rows) {
|
|
@@ -12206,6 +12594,7 @@ var init_messaging = __esm({
|
|
|
12206
12594
|
"use strict";
|
|
12207
12595
|
init_database();
|
|
12208
12596
|
init_tmux_routing();
|
|
12597
|
+
init_task_scope();
|
|
12209
12598
|
MAX_RETRIES3 = 10;
|
|
12210
12599
|
_wsClientSend = null;
|
|
12211
12600
|
}
|
|
@@ -12221,9 +12610,9 @@ __export(active_agent_exports, {
|
|
|
12221
12610
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
12222
12611
|
writeActiveAgent: () => writeActiveAgent
|
|
12223
12612
|
});
|
|
12224
|
-
import { readFileSync as
|
|
12613
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9, readdirSync as readdirSync7 } from "fs";
|
|
12225
12614
|
import { execSync as execSync9 } from "child_process";
|
|
12226
|
-
import
|
|
12615
|
+
import path29 from "path";
|
|
12227
12616
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
12228
12617
|
if (candidate === baseName) return true;
|
|
12229
12618
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -12267,12 +12656,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
12267
12656
|
return null;
|
|
12268
12657
|
}
|
|
12269
12658
|
function getMarkerPath() {
|
|
12270
|
-
return
|
|
12659
|
+
return path29.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
12271
12660
|
}
|
|
12272
12661
|
function writeActiveAgent(agentId, agentRole) {
|
|
12273
12662
|
try {
|
|
12274
|
-
|
|
12275
|
-
|
|
12663
|
+
mkdirSync15(CACHE_DIR, { recursive: true });
|
|
12664
|
+
writeFileSync16(
|
|
12276
12665
|
getMarkerPath(),
|
|
12277
12666
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
12278
12667
|
);
|
|
@@ -12288,7 +12677,7 @@ function clearActiveAgent() {
|
|
|
12288
12677
|
function getActiveAgent() {
|
|
12289
12678
|
try {
|
|
12290
12679
|
const markerPath = getMarkerPath();
|
|
12291
|
-
const raw =
|
|
12680
|
+
const raw = readFileSync20(markerPath, "utf8");
|
|
12292
12681
|
const data = JSON.parse(raw);
|
|
12293
12682
|
if (data.agentId) {
|
|
12294
12683
|
if (data.startedAt) {
|
|
@@ -12336,14 +12725,14 @@ function getAllActiveAgents() {
|
|
|
12336
12725
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
12337
12726
|
if (key === "undefined") continue;
|
|
12338
12727
|
try {
|
|
12339
|
-
const raw =
|
|
12728
|
+
const raw = readFileSync20(path29.join(CACHE_DIR, file), "utf8");
|
|
12340
12729
|
const data = JSON.parse(raw);
|
|
12341
12730
|
if (!data.agentId) continue;
|
|
12342
12731
|
if (data.startedAt) {
|
|
12343
12732
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
12344
12733
|
if (age > STALE_MS) {
|
|
12345
12734
|
try {
|
|
12346
|
-
unlinkSync9(
|
|
12735
|
+
unlinkSync9(path29.join(CACHE_DIR, file));
|
|
12347
12736
|
} catch {
|
|
12348
12737
|
}
|
|
12349
12738
|
continue;
|
|
@@ -12366,11 +12755,11 @@ function getAllActiveAgents() {
|
|
|
12366
12755
|
function cleanupSessionMarkers() {
|
|
12367
12756
|
const key = getSessionKey();
|
|
12368
12757
|
try {
|
|
12369
|
-
unlinkSync9(
|
|
12758
|
+
unlinkSync9(path29.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
12370
12759
|
} catch {
|
|
12371
12760
|
}
|
|
12372
12761
|
try {
|
|
12373
|
-
unlinkSync9(
|
|
12762
|
+
unlinkSync9(path29.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
12374
12763
|
} catch {
|
|
12375
12764
|
}
|
|
12376
12765
|
}
|
|
@@ -12381,7 +12770,7 @@ var init_active_agent = __esm({
|
|
|
12381
12770
|
init_config();
|
|
12382
12771
|
init_session_key();
|
|
12383
12772
|
init_employees();
|
|
12384
|
-
CACHE_DIR =
|
|
12773
|
+
CACHE_DIR = path29.join(EXE_AI_DIR, "session-cache");
|
|
12385
12774
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
12386
12775
|
}
|
|
12387
12776
|
});
|
|
@@ -13009,14 +13398,14 @@ __export(exe_rename_exports, {
|
|
|
13009
13398
|
main: () => main2,
|
|
13010
13399
|
renameEmployee: () => renameEmployee
|
|
13011
13400
|
});
|
|
13012
|
-
import { readFileSync as
|
|
13401
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as renameSync4, unlinkSync as unlinkSync10, existsSync as existsSync24 } from "fs";
|
|
13013
13402
|
import { execSync as execSync10 } from "child_process";
|
|
13014
|
-
import
|
|
13403
|
+
import path30 from "path";
|
|
13015
13404
|
import { homedir as homedir4 } from "os";
|
|
13016
13405
|
async function renameEmployee(oldName, newName, opts = {}) {
|
|
13017
|
-
const rosterPath = opts.rosterPath ??
|
|
13018
|
-
const identityDir = opts.identityDir ??
|
|
13019
|
-
const agentsDir = opts.agentsDir ??
|
|
13406
|
+
const rosterPath = opts.rosterPath ?? path30.join(homedir4(), ".exe-os", "exe-employees.json");
|
|
13407
|
+
const identityDir = opts.identityDir ?? path30.join(homedir4(), ".exe-os", "identity");
|
|
13408
|
+
const agentsDir = opts.agentsDir ?? path30.join(homedir4(), ".claude", "agents");
|
|
13020
13409
|
const validation = validateEmployeeName(newName);
|
|
13021
13410
|
if (!validation.valid) {
|
|
13022
13411
|
return { success: false, error: validation.error };
|
|
@@ -13045,40 +13434,40 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
13045
13434
|
undo: () => {
|
|
13046
13435
|
employee.name = originalName;
|
|
13047
13436
|
employee.systemPrompt = originalPrompt;
|
|
13048
|
-
|
|
13437
|
+
writeFileSync17(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
13049
13438
|
}
|
|
13050
13439
|
});
|
|
13051
|
-
const oldIdentityPath =
|
|
13052
|
-
const newIdentityPath =
|
|
13053
|
-
if (
|
|
13054
|
-
const content =
|
|
13440
|
+
const oldIdentityPath = path30.join(identityDir, `${rosterOldName}.md`);
|
|
13441
|
+
const newIdentityPath = path30.join(identityDir, `${newName}.md`);
|
|
13442
|
+
if (existsSync24(oldIdentityPath)) {
|
|
13443
|
+
const content = readFileSync21(oldIdentityPath, "utf-8");
|
|
13055
13444
|
const updatedContent = content.replace(
|
|
13056
13445
|
/^(agent_id:\s*)\S+/m,
|
|
13057
13446
|
`$1${newName}`
|
|
13058
13447
|
);
|
|
13059
13448
|
renameSync4(oldIdentityPath, newIdentityPath);
|
|
13060
|
-
|
|
13449
|
+
writeFileSync17(newIdentityPath, updatedContent, "utf-8");
|
|
13061
13450
|
rollbackStack.push({
|
|
13062
13451
|
description: "restore identity file",
|
|
13063
13452
|
undo: () => {
|
|
13064
|
-
if (
|
|
13065
|
-
|
|
13453
|
+
if (existsSync24(newIdentityPath)) {
|
|
13454
|
+
writeFileSync17(newIdentityPath, content, "utf-8");
|
|
13066
13455
|
renameSync4(newIdentityPath, oldIdentityPath);
|
|
13067
13456
|
}
|
|
13068
13457
|
}
|
|
13069
13458
|
});
|
|
13070
13459
|
}
|
|
13071
|
-
const oldAgentPath =
|
|
13072
|
-
const newAgentPath =
|
|
13073
|
-
if (
|
|
13074
|
-
const agentContent =
|
|
13460
|
+
const oldAgentPath = path30.join(agentsDir, `${rosterOldName}.md`);
|
|
13461
|
+
const newAgentPath = path30.join(agentsDir, `${newName}.md`);
|
|
13462
|
+
if (existsSync24(oldAgentPath)) {
|
|
13463
|
+
const agentContent = readFileSync21(oldAgentPath, "utf-8");
|
|
13075
13464
|
renameSync4(oldAgentPath, newAgentPath);
|
|
13076
13465
|
rollbackStack.push({
|
|
13077
13466
|
description: "restore agent file",
|
|
13078
13467
|
undo: () => {
|
|
13079
|
-
if (
|
|
13468
|
+
if (existsSync24(newAgentPath)) {
|
|
13080
13469
|
renameSync4(newAgentPath, oldAgentPath);
|
|
13081
|
-
|
|
13470
|
+
writeFileSync17(oldAgentPath, agentContent, "utf-8");
|
|
13082
13471
|
}
|
|
13083
13472
|
}
|
|
13084
13473
|
});
|
|
@@ -13156,10 +13545,10 @@ function removeOldSymlinks(name) {
|
|
|
13156
13545
|
try {
|
|
13157
13546
|
const exeBinPath = findExeBin2();
|
|
13158
13547
|
if (!exeBinPath) return;
|
|
13159
|
-
const binDir =
|
|
13548
|
+
const binDir = path30.dirname(exeBinPath);
|
|
13160
13549
|
for (const suffix of ["", "-opencode"]) {
|
|
13161
|
-
const linkPath =
|
|
13162
|
-
if (
|
|
13550
|
+
const linkPath = path30.join(binDir, `${name}${suffix}`);
|
|
13551
|
+
if (existsSync24(linkPath)) {
|
|
13163
13552
|
try {
|
|
13164
13553
|
unlinkSync10(linkPath);
|
|
13165
13554
|
} catch {
|
|
@@ -13204,16 +13593,16 @@ var init_exe_rename = __esm({
|
|
|
13204
13593
|
});
|
|
13205
13594
|
|
|
13206
13595
|
// src/lib/model-downloader.ts
|
|
13207
|
-
import { createWriteStream, createReadStream as createReadStream2, existsSync as
|
|
13596
|
+
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync25, unlinkSync as unlinkSync11, renameSync as renameSync5 } from "fs";
|
|
13208
13597
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
13209
13598
|
import { createHash as createHash3 } from "crypto";
|
|
13210
|
-
import
|
|
13599
|
+
import path31 from "path";
|
|
13211
13600
|
async function downloadModel(opts) {
|
|
13212
13601
|
const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
|
|
13213
|
-
const destPath =
|
|
13602
|
+
const destPath = path31.join(destDir, LOCAL_FILENAME);
|
|
13214
13603
|
const tmpPath = destPath + ".tmp";
|
|
13215
13604
|
await mkdir6(destDir, { recursive: true });
|
|
13216
|
-
if (
|
|
13605
|
+
if (existsSync25(destPath)) {
|
|
13217
13606
|
const hash = await fileHash(destPath);
|
|
13218
13607
|
if (hash === EXPECTED_SHA256) {
|
|
13219
13608
|
return destPath;
|
|
@@ -13225,7 +13614,7 @@ async function downloadModel(opts) {
|
|
|
13225
13614
|
let downloaded = 0;
|
|
13226
13615
|
for (let attempt = 1; attempt <= MAX_RETRIES4; attempt++) {
|
|
13227
13616
|
try {
|
|
13228
|
-
if (
|
|
13617
|
+
if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
|
|
13229
13618
|
const response = await fetchFn(GGUF_URL, {
|
|
13230
13619
|
redirect: "follow",
|
|
13231
13620
|
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
@@ -13270,7 +13659,7 @@ async function downloadModel(opts) {
|
|
|
13270
13659
|
process.stderr.write(`
|
|
13271
13660
|
Download attempt ${attempt} failed, retrying...
|
|
13272
13661
|
`);
|
|
13273
|
-
if (
|
|
13662
|
+
if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
|
|
13274
13663
|
}
|
|
13275
13664
|
}
|
|
13276
13665
|
}
|
|
@@ -13333,10 +13722,10 @@ async function disposeEmbedder() {
|
|
|
13333
13722
|
async function embedDirect(text) {
|
|
13334
13723
|
const llamaCpp = await import("node-llama-cpp");
|
|
13335
13724
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
13336
|
-
const { existsSync:
|
|
13337
|
-
const
|
|
13338
|
-
const modelPath =
|
|
13339
|
-
if (!
|
|
13725
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
13726
|
+
const path46 = await import("path");
|
|
13727
|
+
const modelPath = path46.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
13728
|
+
if (!existsSync32(modelPath)) {
|
|
13340
13729
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
13341
13730
|
}
|
|
13342
13731
|
const llama = await llamaCpp.getLlama();
|
|
@@ -13911,36 +14300,36 @@ __export(session_wrappers_exports, {
|
|
|
13911
14300
|
generateSessionWrappers: () => generateSessionWrappers
|
|
13912
14301
|
});
|
|
13913
14302
|
import {
|
|
13914
|
-
existsSync as
|
|
13915
|
-
readFileSync as
|
|
13916
|
-
writeFileSync as
|
|
13917
|
-
mkdirSync as
|
|
13918
|
-
chmodSync,
|
|
14303
|
+
existsSync as existsSync26,
|
|
14304
|
+
readFileSync as readFileSync22,
|
|
14305
|
+
writeFileSync as writeFileSync18,
|
|
14306
|
+
mkdirSync as mkdirSync16,
|
|
14307
|
+
chmodSync as chmodSync2,
|
|
13919
14308
|
readdirSync as readdirSync8,
|
|
13920
14309
|
unlinkSync as unlinkSync12
|
|
13921
14310
|
} from "fs";
|
|
13922
|
-
import
|
|
14311
|
+
import path32 from "path";
|
|
13923
14312
|
import { homedir as homedir5 } from "os";
|
|
13924
14313
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
13925
14314
|
const home = homeDir ?? homedir5();
|
|
13926
|
-
const binDir =
|
|
13927
|
-
const rosterPath =
|
|
13928
|
-
|
|
13929
|
-
const exeStartDst =
|
|
14315
|
+
const binDir = path32.join(home, ".exe-os", "bin");
|
|
14316
|
+
const rosterPath = path32.join(home, ".exe-os", "exe-employees.json");
|
|
14317
|
+
mkdirSync16(binDir, { recursive: true });
|
|
14318
|
+
const exeStartDst = path32.join(binDir, "exe-start");
|
|
13930
14319
|
const candidates = [
|
|
13931
|
-
|
|
13932
|
-
|
|
14320
|
+
path32.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
14321
|
+
path32.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
13933
14322
|
];
|
|
13934
14323
|
for (const src of candidates) {
|
|
13935
|
-
if (
|
|
13936
|
-
|
|
13937
|
-
|
|
14324
|
+
if (existsSync26(src)) {
|
|
14325
|
+
writeFileSync18(exeStartDst, readFileSync22(src));
|
|
14326
|
+
chmodSync2(exeStartDst, 493);
|
|
13938
14327
|
break;
|
|
13939
14328
|
}
|
|
13940
14329
|
}
|
|
13941
14330
|
let employees = [];
|
|
13942
14331
|
try {
|
|
13943
|
-
employees = JSON.parse(
|
|
14332
|
+
employees = JSON.parse(readFileSync22(rosterPath, "utf8"));
|
|
13944
14333
|
} catch {
|
|
13945
14334
|
return { created: 0, pathConfigured: false };
|
|
13946
14335
|
}
|
|
@@ -13950,9 +14339,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
13950
14339
|
try {
|
|
13951
14340
|
for (const f of readdirSync8(binDir)) {
|
|
13952
14341
|
if (f === "exe-start") continue;
|
|
13953
|
-
const fPath =
|
|
14342
|
+
const fPath = path32.join(binDir, f);
|
|
13954
14343
|
try {
|
|
13955
|
-
const content =
|
|
14344
|
+
const content = readFileSync22(fPath, "utf8");
|
|
13956
14345
|
if (content.includes("exe-start")) {
|
|
13957
14346
|
unlinkSync12(fPath);
|
|
13958
14347
|
}
|
|
@@ -13967,31 +14356,31 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
13967
14356
|
`;
|
|
13968
14357
|
for (const emp of employees) {
|
|
13969
14358
|
for (let n = 1; n <= MAX_N; n++) {
|
|
13970
|
-
const wrapperPath =
|
|
13971
|
-
|
|
13972
|
-
|
|
14359
|
+
const wrapperPath = path32.join(binDir, `${emp.name}${n}`);
|
|
14360
|
+
writeFileSync18(wrapperPath, wrapperContent);
|
|
14361
|
+
chmodSync2(wrapperPath, 493);
|
|
13973
14362
|
created++;
|
|
13974
14363
|
}
|
|
13975
14364
|
}
|
|
13976
14365
|
const codexLauncherCandidates = [
|
|
13977
|
-
|
|
13978
|
-
|
|
14366
|
+
path32.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
14367
|
+
path32.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
13979
14368
|
];
|
|
13980
14369
|
let codexLauncher = null;
|
|
13981
14370
|
for (const c of codexLauncherCandidates) {
|
|
13982
|
-
if (
|
|
14371
|
+
if (existsSync26(c)) {
|
|
13983
14372
|
codexLauncher = c;
|
|
13984
14373
|
break;
|
|
13985
14374
|
}
|
|
13986
14375
|
}
|
|
13987
14376
|
if (codexLauncher) {
|
|
13988
14377
|
for (const emp of employees) {
|
|
13989
|
-
const wrapperPath =
|
|
14378
|
+
const wrapperPath = path32.join(binDir, `${emp.name}-codex`);
|
|
13990
14379
|
const content = `#!/bin/bash
|
|
13991
14380
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
13992
14381
|
`;
|
|
13993
|
-
|
|
13994
|
-
|
|
14382
|
+
writeFileSync18(wrapperPath, content);
|
|
14383
|
+
chmodSync2(wrapperPath, 493);
|
|
13995
14384
|
created++;
|
|
13996
14385
|
}
|
|
13997
14386
|
}
|
|
@@ -14009,24 +14398,24 @@ export PATH="${binDir}:$PATH"
|
|
|
14009
14398
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
14010
14399
|
const profilePaths = [];
|
|
14011
14400
|
if (shell.includes("zsh")) {
|
|
14012
|
-
profilePaths.push(
|
|
14401
|
+
profilePaths.push(path32.join(home, ".zshrc"));
|
|
14013
14402
|
} else if (shell.includes("bash")) {
|
|
14014
|
-
profilePaths.push(
|
|
14015
|
-
profilePaths.push(
|
|
14403
|
+
profilePaths.push(path32.join(home, ".bashrc"));
|
|
14404
|
+
profilePaths.push(path32.join(home, ".bash_profile"));
|
|
14016
14405
|
} else {
|
|
14017
|
-
profilePaths.push(
|
|
14406
|
+
profilePaths.push(path32.join(home, ".profile"));
|
|
14018
14407
|
}
|
|
14019
14408
|
for (const profilePath of profilePaths) {
|
|
14020
14409
|
try {
|
|
14021
14410
|
let content = "";
|
|
14022
14411
|
try {
|
|
14023
|
-
content =
|
|
14412
|
+
content = readFileSync22(profilePath, "utf8");
|
|
14024
14413
|
} catch {
|
|
14025
14414
|
}
|
|
14026
14415
|
if (content.includes(".exe-os/bin")) {
|
|
14027
14416
|
return false;
|
|
14028
14417
|
}
|
|
14029
|
-
|
|
14418
|
+
writeFileSync18(profilePath, content + exportLine);
|
|
14030
14419
|
return true;
|
|
14031
14420
|
} catch {
|
|
14032
14421
|
continue;
|
|
@@ -14048,37 +14437,37 @@ __export(setup_wizard_exports, {
|
|
|
14048
14437
|
runSetupWizard: () => runSetupWizard,
|
|
14049
14438
|
validateModel: () => validateModel
|
|
14050
14439
|
});
|
|
14051
|
-
import
|
|
14052
|
-
import { existsSync as
|
|
14053
|
-
import
|
|
14054
|
-
import
|
|
14440
|
+
import crypto12 from "crypto";
|
|
14441
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync19, unlinkSync as unlinkSync13 } from "fs";
|
|
14442
|
+
import os16 from "os";
|
|
14443
|
+
import path33 from "path";
|
|
14055
14444
|
import { createInterface as createInterface3 } from "readline";
|
|
14056
14445
|
function findPackageRoot2() {
|
|
14057
|
-
let dir =
|
|
14058
|
-
const root =
|
|
14446
|
+
let dir = path33.dirname(new URL(import.meta.url).pathname);
|
|
14447
|
+
const root = path33.parse(dir).root;
|
|
14059
14448
|
while (dir !== root) {
|
|
14060
|
-
const pkgPath =
|
|
14061
|
-
if (
|
|
14449
|
+
const pkgPath = path33.join(dir, "package.json");
|
|
14450
|
+
if (existsSync27(pkgPath)) {
|
|
14062
14451
|
try {
|
|
14063
|
-
const pkg = JSON.parse(
|
|
14452
|
+
const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
|
|
14064
14453
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
14065
14454
|
} catch {
|
|
14066
14455
|
}
|
|
14067
14456
|
}
|
|
14068
|
-
dir =
|
|
14457
|
+
dir = path33.dirname(dir);
|
|
14069
14458
|
}
|
|
14070
14459
|
return null;
|
|
14071
14460
|
}
|
|
14072
14461
|
function loadSetupState() {
|
|
14073
14462
|
try {
|
|
14074
|
-
return JSON.parse(
|
|
14463
|
+
return JSON.parse(readFileSync23(SETUP_STATE_PATH, "utf8"));
|
|
14075
14464
|
} catch {
|
|
14076
14465
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
14077
14466
|
}
|
|
14078
14467
|
}
|
|
14079
14468
|
function saveSetupState(state) {
|
|
14080
|
-
|
|
14081
|
-
|
|
14469
|
+
mkdirSync17(path33.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
14470
|
+
writeFileSync19(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
14082
14471
|
}
|
|
14083
14472
|
function clearSetupState() {
|
|
14084
14473
|
try {
|
|
@@ -14097,10 +14486,10 @@ function ask2(rl, prompt) {
|
|
|
14097
14486
|
});
|
|
14098
14487
|
}
|
|
14099
14488
|
function getAvailableMemoryGB() {
|
|
14100
|
-
return
|
|
14489
|
+
return os16.freemem() / (1024 * 1024 * 1024);
|
|
14101
14490
|
}
|
|
14102
14491
|
function getTotalMemoryGB() {
|
|
14103
|
-
return
|
|
14492
|
+
return os16.totalmem() / (1024 * 1024 * 1024);
|
|
14104
14493
|
}
|
|
14105
14494
|
function isLowMemory() {
|
|
14106
14495
|
return getAvailableMemoryGB() < 2;
|
|
@@ -14111,8 +14500,8 @@ async function validateModel(log) {
|
|
|
14111
14500
|
if (totalGB <= 8 || isLowMemory()) {
|
|
14112
14501
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
14113
14502
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
14114
|
-
const modelPath =
|
|
14115
|
-
if (
|
|
14503
|
+
const modelPath = path33.join(MODELS_DIR, LOCAL_FILENAME);
|
|
14504
|
+
if (existsSync27(modelPath)) {
|
|
14116
14505
|
const { statSync: statSync2 } = await import("fs");
|
|
14117
14506
|
const size = statSync2(modelPath).size;
|
|
14118
14507
|
if (size > 300 * 1e6) {
|
|
@@ -14203,7 +14592,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14203
14592
|
if (state.completedSteps.length > 0) {
|
|
14204
14593
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
14205
14594
|
}
|
|
14206
|
-
if (
|
|
14595
|
+
if (existsSync27(LEGACY_LANCE_PATH)) {
|
|
14207
14596
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
14208
14597
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
14209
14598
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -14215,7 +14604,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14215
14604
|
log("Encryption key already exists \u2014 skipping generation.");
|
|
14216
14605
|
} else {
|
|
14217
14606
|
log("Generating 256-bit encryption key...");
|
|
14218
|
-
const key =
|
|
14607
|
+
const key = crypto12.randomBytes(32);
|
|
14219
14608
|
await setMasterKey(key);
|
|
14220
14609
|
log("Encryption key generated and stored securely.");
|
|
14221
14610
|
}
|
|
@@ -14367,19 +14756,19 @@ async function runSetupWizard(opts = {}) {
|
|
|
14367
14756
|
await saveConfig(config);
|
|
14368
14757
|
log("");
|
|
14369
14758
|
try {
|
|
14370
|
-
const claudeJsonPath =
|
|
14759
|
+
const claudeJsonPath = path33.join(os16.homedir(), ".claude.json");
|
|
14371
14760
|
let claudeJson = {};
|
|
14372
14761
|
try {
|
|
14373
|
-
claudeJson = JSON.parse(
|
|
14762
|
+
claudeJson = JSON.parse(readFileSync23(claudeJsonPath, "utf8"));
|
|
14374
14763
|
} catch {
|
|
14375
14764
|
}
|
|
14376
14765
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
14377
14766
|
const projects = claudeJson.projects;
|
|
14378
|
-
for (const dir of [process.cwd(),
|
|
14767
|
+
for (const dir of [process.cwd(), os16.homedir()]) {
|
|
14379
14768
|
if (!projects[dir]) projects[dir] = {};
|
|
14380
14769
|
projects[dir].hasTrustDialogAccepted = true;
|
|
14381
14770
|
}
|
|
14382
|
-
|
|
14771
|
+
writeFileSync19(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
14383
14772
|
} catch {
|
|
14384
14773
|
}
|
|
14385
14774
|
state.completedSteps.push(5);
|
|
@@ -14393,7 +14782,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14393
14782
|
const prefs = { ...existingPrefs };
|
|
14394
14783
|
log("=== Config Defaults ===");
|
|
14395
14784
|
log("");
|
|
14396
|
-
const ghosttyDetected =
|
|
14785
|
+
const ghosttyDetected = existsSync27(path33.join(os16.homedir(), ".config", "ghostty")) || existsSync27(path33.join(os16.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
14397
14786
|
if (ghosttyDetected) {
|
|
14398
14787
|
const ghosttyAnswer = await ask2(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
14399
14788
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -14440,7 +14829,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14440
14829
|
let missingIdentities = [];
|
|
14441
14830
|
for (const emp of roster) {
|
|
14442
14831
|
const idPath = identityPath2(emp.name);
|
|
14443
|
-
if (!
|
|
14832
|
+
if (!existsSync27(idPath)) {
|
|
14444
14833
|
missingIdentities.push(emp.name);
|
|
14445
14834
|
}
|
|
14446
14835
|
}
|
|
@@ -14472,7 +14861,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14472
14861
|
}
|
|
14473
14862
|
missingIdentities = [];
|
|
14474
14863
|
for (const emp of roster) {
|
|
14475
|
-
if (!
|
|
14864
|
+
if (!existsSync27(identityPath2(emp.name))) {
|
|
14476
14865
|
missingIdentities.push(emp.name);
|
|
14477
14866
|
}
|
|
14478
14867
|
}
|
|
@@ -14537,9 +14926,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14537
14926
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
14538
14927
|
if (cooIdentityContent) {
|
|
14539
14928
|
const cooIdPath = identityPath2(cooName);
|
|
14540
|
-
|
|
14929
|
+
mkdirSync17(path33.dirname(cooIdPath), { recursive: true });
|
|
14541
14930
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
14542
|
-
|
|
14931
|
+
writeFileSync19(cooIdPath, replaced, "utf-8");
|
|
14543
14932
|
}
|
|
14544
14933
|
registerBinSymlinks2(cooName);
|
|
14545
14934
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -14633,9 +15022,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14633
15022
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
14634
15023
|
if (ctoIdentityContent) {
|
|
14635
15024
|
const ctoIdPath = identityPath2(ctoName);
|
|
14636
|
-
|
|
15025
|
+
mkdirSync17(path33.dirname(ctoIdPath), { recursive: true });
|
|
14637
15026
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
14638
|
-
|
|
15027
|
+
writeFileSync19(ctoIdPath, replaced, "utf-8");
|
|
14639
15028
|
}
|
|
14640
15029
|
registerBinSymlinks2(ctoName);
|
|
14641
15030
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -14656,9 +15045,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14656
15045
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
14657
15046
|
if (cmoIdentityContent) {
|
|
14658
15047
|
const cmoIdPath = identityPath2(cmoName);
|
|
14659
|
-
|
|
15048
|
+
mkdirSync17(path33.dirname(cmoIdPath), { recursive: true });
|
|
14660
15049
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
14661
|
-
|
|
15050
|
+
writeFileSync19(cmoIdPath, replaced, "utf-8");
|
|
14662
15051
|
}
|
|
14663
15052
|
registerBinSymlinks2(cmoName);
|
|
14664
15053
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -14680,7 +15069,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14680
15069
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
14681
15070
|
}
|
|
14682
15071
|
if (wrapResult.pathConfigured) {
|
|
14683
|
-
const binDir =
|
|
15072
|
+
const binDir = path33.join(os16.homedir(), ".exe-os", "bin");
|
|
14684
15073
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
14685
15074
|
pathJustConfigured = true;
|
|
14686
15075
|
}
|
|
@@ -14723,7 +15112,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14723
15112
|
const pkgRoot2 = findPackageRoot2();
|
|
14724
15113
|
if (pkgRoot2) {
|
|
14725
15114
|
try {
|
|
14726
|
-
version = JSON.parse(
|
|
15115
|
+
version = JSON.parse(readFileSync23(path33.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
14727
15116
|
} catch {
|
|
14728
15117
|
}
|
|
14729
15118
|
}
|
|
@@ -14757,17 +15146,17 @@ var init_setup_wizard = __esm({
|
|
|
14757
15146
|
init_config();
|
|
14758
15147
|
init_keychain();
|
|
14759
15148
|
init_model_downloader();
|
|
14760
|
-
SETUP_STATE_PATH =
|
|
15149
|
+
SETUP_STATE_PATH = path33.join(os16.homedir(), ".exe-os", "setup-state.json");
|
|
14761
15150
|
}
|
|
14762
15151
|
});
|
|
14763
15152
|
|
|
14764
15153
|
// src/lib/update-check.ts
|
|
14765
15154
|
import { execSync as execSync11 } from "child_process";
|
|
14766
|
-
import { readFileSync as
|
|
14767
|
-
import
|
|
15155
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
15156
|
+
import path34 from "path";
|
|
14768
15157
|
function getLocalVersion(packageRoot) {
|
|
14769
|
-
const pkgPath =
|
|
14770
|
-
const pkg = JSON.parse(
|
|
15158
|
+
const pkgPath = path34.join(packageRoot, "package.json");
|
|
15159
|
+
const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
|
|
14771
15160
|
return pkg.version;
|
|
14772
15161
|
}
|
|
14773
15162
|
function getRemoteVersion() {
|
|
@@ -19291,8 +19680,8 @@ var init_ErrorOverview = __esm({
|
|
|
19291
19680
|
"use strict";
|
|
19292
19681
|
init_Box();
|
|
19293
19682
|
init_Text();
|
|
19294
|
-
cleanupPath = (
|
|
19295
|
-
return
|
|
19683
|
+
cleanupPath = (path46) => {
|
|
19684
|
+
return path46?.replace(`file://${cwd()}/`, "");
|
|
19296
19685
|
};
|
|
19297
19686
|
stackUtils = new StackUtils({
|
|
19298
19687
|
cwd: cwd(),
|
|
@@ -21700,11 +22089,11 @@ function Footer() {
|
|
|
21700
22089
|
} catch {
|
|
21701
22090
|
}
|
|
21702
22091
|
try {
|
|
21703
|
-
const { existsSync:
|
|
22092
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
21704
22093
|
const { join } = await import("path");
|
|
21705
22094
|
const home = process.env.HOME ?? "";
|
|
21706
22095
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
21707
|
-
setDaemon(
|
|
22096
|
+
setDaemon(existsSync32(pidPath) ? "running" : "stopped");
|
|
21708
22097
|
} catch {
|
|
21709
22098
|
setDaemon("unknown");
|
|
21710
22099
|
}
|
|
@@ -23746,10 +24135,10 @@ var init_hooks = __esm({
|
|
|
23746
24135
|
});
|
|
23747
24136
|
|
|
23748
24137
|
// src/runtime/safety-checks.ts
|
|
23749
|
-
import
|
|
23750
|
-
import
|
|
24138
|
+
import path35 from "path";
|
|
24139
|
+
import os17 from "os";
|
|
23751
24140
|
function checkPathSafety(filePath) {
|
|
23752
|
-
const resolved =
|
|
24141
|
+
const resolved = path35.resolve(filePath);
|
|
23753
24142
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
23754
24143
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
23755
24144
|
if (matches) {
|
|
@@ -23759,7 +24148,7 @@ function checkPathSafety(filePath) {
|
|
|
23759
24148
|
return { safe: true, bypassImmune: true };
|
|
23760
24149
|
}
|
|
23761
24150
|
function checkReadPathSafety(filePath) {
|
|
23762
|
-
const resolved =
|
|
24151
|
+
const resolved = path35.resolve(filePath);
|
|
23763
24152
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
23764
24153
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
23765
24154
|
);
|
|
@@ -23774,7 +24163,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
23774
24163
|
var init_safety_checks = __esm({
|
|
23775
24164
|
"src/runtime/safety-checks.ts"() {
|
|
23776
24165
|
"use strict";
|
|
23777
|
-
HOME =
|
|
24166
|
+
HOME = os17.homedir();
|
|
23778
24167
|
BYPASS_IMMUNE_PATTERNS = [
|
|
23779
24168
|
{
|
|
23780
24169
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -23785,11 +24174,11 @@ var init_safety_checks = __esm({
|
|
|
23785
24174
|
reason: "Git config can set hooks and command execution"
|
|
23786
24175
|
},
|
|
23787
24176
|
{
|
|
23788
|
-
pattern: (p) => p.startsWith(
|
|
24177
|
+
pattern: (p) => p.startsWith(path35.join(HOME, ".claude")),
|
|
23789
24178
|
reason: "Claude configuration files are protected"
|
|
23790
24179
|
},
|
|
23791
24180
|
{
|
|
23792
|
-
pattern: (p) => p.startsWith(
|
|
24181
|
+
pattern: (p) => p.startsWith(path35.join(HOME, ".exe-os")),
|
|
23793
24182
|
reason: "exe-os configuration files are protected"
|
|
23794
24183
|
},
|
|
23795
24184
|
{
|
|
@@ -23806,7 +24195,7 @@ var init_safety_checks = __esm({
|
|
|
23806
24195
|
},
|
|
23807
24196
|
{
|
|
23808
24197
|
pattern: (p) => {
|
|
23809
|
-
const name =
|
|
24198
|
+
const name = path35.basename(p);
|
|
23810
24199
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
23811
24200
|
},
|
|
23812
24201
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -23833,7 +24222,7 @@ __export(file_read_exports, {
|
|
|
23833
24222
|
FileReadTool: () => FileReadTool
|
|
23834
24223
|
});
|
|
23835
24224
|
import fs3 from "fs/promises";
|
|
23836
|
-
import
|
|
24225
|
+
import path36 from "path";
|
|
23837
24226
|
import { z } from "zod";
|
|
23838
24227
|
function isBinary(buf) {
|
|
23839
24228
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -23869,7 +24258,7 @@ var init_file_read = __esm({
|
|
|
23869
24258
|
return { behavior: "allow" };
|
|
23870
24259
|
},
|
|
23871
24260
|
async call(input, context) {
|
|
23872
|
-
const filePath =
|
|
24261
|
+
const filePath = path36.isAbsolute(input.file_path) ? input.file_path : path36.resolve(context.cwd, input.file_path);
|
|
23873
24262
|
let stat2;
|
|
23874
24263
|
try {
|
|
23875
24264
|
stat2 = await fs3.stat(filePath);
|
|
@@ -23909,7 +24298,7 @@ __export(glob_exports, {
|
|
|
23909
24298
|
GlobTool: () => GlobTool
|
|
23910
24299
|
});
|
|
23911
24300
|
import fs4 from "fs/promises";
|
|
23912
|
-
import
|
|
24301
|
+
import path37 from "path";
|
|
23913
24302
|
import { z as z2 } from "zod";
|
|
23914
24303
|
async function walkDir(dir, maxDepth = 10) {
|
|
23915
24304
|
const results = [];
|
|
@@ -23925,7 +24314,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
23925
24314
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
23926
24315
|
continue;
|
|
23927
24316
|
}
|
|
23928
|
-
const fullPath =
|
|
24317
|
+
const fullPath = path37.join(current, entry.name);
|
|
23929
24318
|
if (entry.isDirectory()) {
|
|
23930
24319
|
await walk(fullPath, depth + 1);
|
|
23931
24320
|
} else {
|
|
@@ -23959,11 +24348,11 @@ var init_glob = __esm({
|
|
|
23959
24348
|
inputSchema: inputSchema2,
|
|
23960
24349
|
isReadOnly: true,
|
|
23961
24350
|
async call(input, context) {
|
|
23962
|
-
const baseDir = input.path ?
|
|
24351
|
+
const baseDir = input.path ? path37.isAbsolute(input.path) ? input.path : path37.resolve(context.cwd, input.path) : context.cwd;
|
|
23963
24352
|
try {
|
|
23964
24353
|
const entries = await walkDir(baseDir);
|
|
23965
24354
|
const matched = entries.filter(
|
|
23966
|
-
(e) => simpleGlobMatch(
|
|
24355
|
+
(e) => simpleGlobMatch(path37.relative(baseDir, e.path), input.pattern)
|
|
23967
24356
|
);
|
|
23968
24357
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
23969
24358
|
if (matched.length === 0) {
|
|
@@ -23989,7 +24378,7 @@ __export(grep_exports, {
|
|
|
23989
24378
|
});
|
|
23990
24379
|
import { spawn as spawn2 } from "child_process";
|
|
23991
24380
|
import fs5 from "fs/promises";
|
|
23992
|
-
import
|
|
24381
|
+
import path38 from "path";
|
|
23993
24382
|
import { z as z3 } from "zod";
|
|
23994
24383
|
function runRipgrep(input, searchPath, context) {
|
|
23995
24384
|
return new Promise((resolve, reject) => {
|
|
@@ -24043,7 +24432,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
24043
24432
|
}
|
|
24044
24433
|
for (const entry of entries) {
|
|
24045
24434
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
24046
|
-
const fullPath =
|
|
24435
|
+
const fullPath = path38.join(dir, entry.name);
|
|
24047
24436
|
if (entry.isDirectory()) {
|
|
24048
24437
|
await walk(fullPath);
|
|
24049
24438
|
} else {
|
|
@@ -24089,7 +24478,7 @@ var init_grep = __esm({
|
|
|
24089
24478
|
inputSchema: inputSchema3,
|
|
24090
24479
|
isReadOnly: true,
|
|
24091
24480
|
async call(input, context) {
|
|
24092
|
-
const searchPath = input.path ?
|
|
24481
|
+
const searchPath = input.path ? path38.isAbsolute(input.path) ? input.path : path38.resolve(context.cwd, input.path) : context.cwd;
|
|
24093
24482
|
try {
|
|
24094
24483
|
const result = await runRipgrep(input, searchPath, context);
|
|
24095
24484
|
return result;
|
|
@@ -24114,7 +24503,7 @@ __export(file_write_exports, {
|
|
|
24114
24503
|
FileWriteTool: () => FileWriteTool
|
|
24115
24504
|
});
|
|
24116
24505
|
import fs6 from "fs/promises";
|
|
24117
|
-
import
|
|
24506
|
+
import path39 from "path";
|
|
24118
24507
|
import { z as z4 } from "zod";
|
|
24119
24508
|
var inputSchema4, FileWriteTool;
|
|
24120
24509
|
var init_file_write = __esm({
|
|
@@ -24142,8 +24531,8 @@ var init_file_write = __esm({
|
|
|
24142
24531
|
return { behavior: "allow" };
|
|
24143
24532
|
},
|
|
24144
24533
|
async call(input, context) {
|
|
24145
|
-
const filePath =
|
|
24146
|
-
const dir =
|
|
24534
|
+
const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
|
|
24535
|
+
const dir = path39.dirname(filePath);
|
|
24147
24536
|
await fs6.mkdir(dir, { recursive: true });
|
|
24148
24537
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
24149
24538
|
return {
|
|
@@ -24161,7 +24550,7 @@ __export(file_edit_exports, {
|
|
|
24161
24550
|
FileEditTool: () => FileEditTool
|
|
24162
24551
|
});
|
|
24163
24552
|
import fs7 from "fs/promises";
|
|
24164
|
-
import
|
|
24553
|
+
import path40 from "path";
|
|
24165
24554
|
import { z as z5 } from "zod";
|
|
24166
24555
|
function countOccurrences(haystack, needle) {
|
|
24167
24556
|
let count = 0;
|
|
@@ -24202,7 +24591,7 @@ var init_file_edit = __esm({
|
|
|
24202
24591
|
return { behavior: "allow" };
|
|
24203
24592
|
},
|
|
24204
24593
|
async call(input, context) {
|
|
24205
|
-
const filePath =
|
|
24594
|
+
const filePath = path40.isAbsolute(input.file_path) ? input.file_path : path40.resolve(context.cwd, input.file_path);
|
|
24206
24595
|
let content;
|
|
24207
24596
|
try {
|
|
24208
24597
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -24444,7 +24833,7 @@ var init_bash = __esm({
|
|
|
24444
24833
|
// src/tui/views/CommandCenter.tsx
|
|
24445
24834
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
24446
24835
|
import TextInput from "ink-text-input";
|
|
24447
|
-
import
|
|
24836
|
+
import path41 from "path";
|
|
24448
24837
|
import { homedir as homedir6 } from "os";
|
|
24449
24838
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
24450
24839
|
function CommandCenterView({
|
|
@@ -24479,15 +24868,15 @@ function CommandCenterView({
|
|
|
24479
24868
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
24480
24869
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
24481
24870
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
24482
|
-
const { readFileSync:
|
|
24871
|
+
const { readFileSync: readFileSync28, existsSync: existsSync32 } = await import("fs");
|
|
24483
24872
|
const { join } = await import("path");
|
|
24484
24873
|
const { homedir: homedir8 } = await import("os");
|
|
24485
24874
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
24486
24875
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
24487
24876
|
let providerConfigs = {};
|
|
24488
|
-
if (
|
|
24877
|
+
if (existsSync32(configPath)) {
|
|
24489
24878
|
try {
|
|
24490
|
-
const raw = JSON.parse(
|
|
24879
|
+
const raw = JSON.parse(readFileSync28(configPath, "utf8"));
|
|
24491
24880
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
24492
24881
|
if (raw.providers && typeof raw.providers === "object") {
|
|
24493
24882
|
providerConfigs = raw.providers;
|
|
@@ -24548,7 +24937,7 @@ function CommandCenterView({
|
|
|
24548
24937
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
24549
24938
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
24550
24939
|
for (const f of agentFiles) {
|
|
24551
|
-
const data = JSON.parse(
|
|
24940
|
+
const data = JSON.parse(readFileSync28(join(markerDir, f), "utf8"));
|
|
24552
24941
|
if (data.agentRole) {
|
|
24553
24942
|
agentRole = data.agentRole;
|
|
24554
24943
|
break;
|
|
@@ -24693,7 +25082,7 @@ function CommandCenterView({
|
|
|
24693
25082
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
24694
25083
|
projectName: p.projectName,
|
|
24695
25084
|
exeSession: p.exeSession,
|
|
24696
|
-
projectDir:
|
|
25085
|
+
projectDir: path41.join(homedir6(), p.projectName),
|
|
24697
25086
|
employeeCount: p.employees.length,
|
|
24698
25087
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
24699
25088
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -24731,7 +25120,7 @@ function CommandCenterView({
|
|
|
24731
25120
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
24732
25121
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
24733
25122
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
24734
|
-
const { existsSync:
|
|
25123
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
24735
25124
|
const { join } = await import("path");
|
|
24736
25125
|
const client = getClient2();
|
|
24737
25126
|
if (!client) {
|
|
@@ -24802,7 +25191,7 @@ function CommandCenterView({
|
|
|
24802
25191
|
}
|
|
24803
25192
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
24804
25193
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
24805
|
-
const hasGit = projectDir ?
|
|
25194
|
+
const hasGit = projectDir ? existsSync32(join(projectDir, ".git")) : false;
|
|
24806
25195
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
24807
25196
|
projectList.push({
|
|
24808
25197
|
projectName: name,
|
|
@@ -24827,7 +25216,7 @@ function CommandCenterView({
|
|
|
24827
25216
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
24828
25217
|
try {
|
|
24829
25218
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
24830
|
-
setHealth((h) => ({ ...h, daemon:
|
|
25219
|
+
setHealth((h) => ({ ...h, daemon: existsSync32(pidPath) ? "running" : "stopped" }));
|
|
24831
25220
|
} catch {
|
|
24832
25221
|
}
|
|
24833
25222
|
const activityResult = await client.execute(
|
|
@@ -25697,7 +26086,7 @@ var init_useOrchestrator = __esm({
|
|
|
25697
26086
|
|
|
25698
26087
|
// src/tui/views/Sessions.tsx
|
|
25699
26088
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
25700
|
-
import
|
|
26089
|
+
import path42 from "path";
|
|
25701
26090
|
import { homedir as homedir7 } from "os";
|
|
25702
26091
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
25703
26092
|
function isCoordinatorEntry(entry) {
|
|
@@ -25735,7 +26124,7 @@ function SessionsView({
|
|
|
25735
26124
|
if (demo) {
|
|
25736
26125
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
25737
26126
|
...p,
|
|
25738
|
-
projectDir:
|
|
26127
|
+
projectDir: path42.join(homedir7(), p.projectName),
|
|
25739
26128
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
25740
26129
|
})));
|
|
25741
26130
|
return;
|
|
@@ -26145,6 +26534,133 @@ var init_Sessions = __esm({
|
|
|
26145
26534
|
}
|
|
26146
26535
|
});
|
|
26147
26536
|
|
|
26537
|
+
// src/lib/tui-data.ts
|
|
26538
|
+
var tui_data_exports = {};
|
|
26539
|
+
__export(tui_data_exports, {
|
|
26540
|
+
loadMemoryDashboard: () => loadMemoryDashboard,
|
|
26541
|
+
loadTaskList: () => loadTaskList,
|
|
26542
|
+
loadTeamMetrics: () => loadTeamMetrics,
|
|
26543
|
+
searchWikiMemoryRows: () => searchWikiMemoryRows
|
|
26544
|
+
});
|
|
26545
|
+
async function loadMemoryDashboard(limit) {
|
|
26546
|
+
const client = getClient();
|
|
26547
|
+
const [countResult, recentResult, agentResult] = await Promise.all([
|
|
26548
|
+
client.execute("SELECT COUNT(*) as cnt FROM memories"),
|
|
26549
|
+
client.execute({
|
|
26550
|
+
sql: "SELECT agent_id, tool_name, project_name, raw_text, timestamp FROM memories ORDER BY timestamp DESC LIMIT ?",
|
|
26551
|
+
args: [limit]
|
|
26552
|
+
}),
|
|
26553
|
+
client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id ORDER BY cnt DESC")
|
|
26554
|
+
]);
|
|
26555
|
+
return {
|
|
26556
|
+
total: Number(countResult.rows[0]?.cnt ?? 0),
|
|
26557
|
+
recent: recentResult.rows.map((row) => ({
|
|
26558
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
26559
|
+
toolName: String(row.tool_name ?? ""),
|
|
26560
|
+
projectName: String(row.project_name ?? ""),
|
|
26561
|
+
rawText: String(row.raw_text ?? ""),
|
|
26562
|
+
timestamp: String(row.timestamp ?? "")
|
|
26563
|
+
})),
|
|
26564
|
+
byAgent: agentResult.rows.map((row) => ({
|
|
26565
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
26566
|
+
count: Number(row.cnt ?? 0)
|
|
26567
|
+
}))
|
|
26568
|
+
};
|
|
26569
|
+
}
|
|
26570
|
+
async function loadTeamMetrics(employeeNames) {
|
|
26571
|
+
const client = getClient();
|
|
26572
|
+
const memoryCounts = /* @__PURE__ */ new Map();
|
|
26573
|
+
const projectsByEmployee = /* @__PURE__ */ new Map();
|
|
26574
|
+
const currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
26575
|
+
const scope = sessionScopeFilter();
|
|
26576
|
+
const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
|
|
26577
|
+
for (const row of memResult.rows) {
|
|
26578
|
+
memoryCounts.set(String(row.agent_id), Number(row.cnt));
|
|
26579
|
+
}
|
|
26580
|
+
for (const employeeName of employeeNames) {
|
|
26581
|
+
const [projectResult, taskResult] = await Promise.all([
|
|
26582
|
+
client.execute({
|
|
26583
|
+
sql: `SELECT DISTINCT project_name,
|
|
26584
|
+
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
26585
|
+
FROM tasks
|
|
26586
|
+
WHERE assigned_to = ? AND status IN ('open','in_progress','done')${scope.sql}
|
|
26587
|
+
GROUP BY project_name
|
|
26588
|
+
ORDER BY urgency ASC
|
|
26589
|
+
LIMIT 5`,
|
|
26590
|
+
args: [employeeName, ...scope.args]
|
|
26591
|
+
}),
|
|
26592
|
+
client.execute({
|
|
26593
|
+
sql: `SELECT title FROM tasks
|
|
26594
|
+
WHERE assigned_to = ? AND status = 'in_progress'${scope.sql}
|
|
26595
|
+
ORDER BY updated_at DESC
|
|
26596
|
+
LIMIT 1`,
|
|
26597
|
+
args: [employeeName, ...scope.args]
|
|
26598
|
+
})
|
|
26599
|
+
]);
|
|
26600
|
+
const projects = projectResult.rows.map((row) => {
|
|
26601
|
+
const urgency = Number(row.urgency);
|
|
26602
|
+
return {
|
|
26603
|
+
name: String(row.project_name),
|
|
26604
|
+
status: urgency === 1 ? "active" : urgency === 2 ? "has_tasks" : "idle"
|
|
26605
|
+
};
|
|
26606
|
+
});
|
|
26607
|
+
if (projects.length > 0) projectsByEmployee.set(employeeName, projects);
|
|
26608
|
+
if (taskResult.rows.length > 0) {
|
|
26609
|
+
currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
|
|
26610
|
+
}
|
|
26611
|
+
}
|
|
26612
|
+
return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
|
|
26613
|
+
}
|
|
26614
|
+
async function loadTaskList() {
|
|
26615
|
+
const client = getClient();
|
|
26616
|
+
const scope = sessionScopeFilter();
|
|
26617
|
+
const result = await client.execute({
|
|
26618
|
+
sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
|
|
26619
|
+
FROM tasks
|
|
26620
|
+
WHERE 1=1${scope.sql}
|
|
26621
|
+
ORDER BY
|
|
26622
|
+
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,
|
|
26623
|
+
CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
|
|
26624
|
+
created_at DESC`,
|
|
26625
|
+
args: [...scope.args]
|
|
26626
|
+
});
|
|
26627
|
+
return result.rows.map((row) => ({
|
|
26628
|
+
id: String(row.id ?? ""),
|
|
26629
|
+
title: String(row.title ?? ""),
|
|
26630
|
+
priority: String(row.priority ?? "p2").toUpperCase(),
|
|
26631
|
+
assignedTo: String(row.assigned_to ?? ""),
|
|
26632
|
+
assignedBy: String(row.assigned_by ?? ""),
|
|
26633
|
+
status: String(row.status ?? "open"),
|
|
26634
|
+
projectName: String(row.project_name ?? ""),
|
|
26635
|
+
createdAt: String(row.created_at ?? ""),
|
|
26636
|
+
result: String(row.result ?? "")
|
|
26637
|
+
}));
|
|
26638
|
+
}
|
|
26639
|
+
async function searchWikiMemoryRows(query) {
|
|
26640
|
+
const client = getClient();
|
|
26641
|
+
const result = await client.execute({
|
|
26642
|
+
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
26643
|
+
FROM memories
|
|
26644
|
+
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
26645
|
+
ORDER BY timestamp DESC LIMIT 20`,
|
|
26646
|
+
args: [`%${query}%`]
|
|
26647
|
+
});
|
|
26648
|
+
return result.rows.map((row) => ({
|
|
26649
|
+
id: String(row.id),
|
|
26650
|
+
agentId: String(row.agent_id),
|
|
26651
|
+
rawText: String(row.raw_text),
|
|
26652
|
+
timestamp: String(row.timestamp),
|
|
26653
|
+
projectName: String(row.project_name ?? "")
|
|
26654
|
+
}));
|
|
26655
|
+
}
|
|
26656
|
+
var init_tui_data = __esm({
|
|
26657
|
+
"src/lib/tui-data.ts"() {
|
|
26658
|
+
"use strict";
|
|
26659
|
+
init_database();
|
|
26660
|
+
init_task_scope();
|
|
26661
|
+
}
|
|
26662
|
+
});
|
|
26663
|
+
|
|
26148
26664
|
// src/tui/views/Tasks.tsx
|
|
26149
26665
|
import React20, { useState as useState10, useEffect as useEffect12, useMemo as useMemo5 } from "react";
|
|
26150
26666
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -26264,37 +26780,22 @@ function TasksView({ onBack }) {
|
|
|
26264
26780
|
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
26265
26781
|
return withTrace2("tui.tasks.loadTasks", async () => {
|
|
26266
26782
|
try {
|
|
26267
|
-
const {
|
|
26268
|
-
const
|
|
26269
|
-
|
|
26270
|
-
|
|
26271
|
-
|
|
26272
|
-
|
|
26273
|
-
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26280
|
-
})
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
id: String(r.id ?? ""),
|
|
26284
|
-
priority: String(r.priority ?? "p2").toUpperCase(),
|
|
26285
|
-
title: String(r.title ?? ""),
|
|
26286
|
-
assignee: String(r.assigned_to ?? ""),
|
|
26287
|
-
assignedBy: String(r.assigned_by ?? ""),
|
|
26288
|
-
status: String(r.status ?? "open"),
|
|
26289
|
-
project: String(r.project_name ?? ""),
|
|
26290
|
-
createdAt: String(r.created_at ?? ""),
|
|
26291
|
-
result: String(r.result ?? "")
|
|
26292
|
-
}))
|
|
26293
|
-
);
|
|
26294
|
-
setDbError(null);
|
|
26295
|
-
} else {
|
|
26296
|
-
setDbError("Database client not initialized. Run exe-os setup.");
|
|
26297
|
-
}
|
|
26783
|
+
const { loadTaskList: loadTaskList2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
26784
|
+
const tasks = await loadTaskList2();
|
|
26785
|
+
setAllTasks(
|
|
26786
|
+
tasks.map((task) => ({
|
|
26787
|
+
id: task.id,
|
|
26788
|
+
priority: task.priority,
|
|
26789
|
+
title: task.title,
|
|
26790
|
+
assignee: task.assignedTo,
|
|
26791
|
+
assignedBy: task.assignedBy,
|
|
26792
|
+
status: task.status,
|
|
26793
|
+
project: task.projectName,
|
|
26794
|
+
createdAt: task.createdAt,
|
|
26795
|
+
result: task.result
|
|
26796
|
+
}))
|
|
26797
|
+
);
|
|
26798
|
+
setDbError(null);
|
|
26298
26799
|
} catch (err) {
|
|
26299
26800
|
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
26300
26801
|
} finally {
|
|
@@ -26423,7 +26924,6 @@ var init_Tasks = __esm({
|
|
|
26423
26924
|
await init_ink2();
|
|
26424
26925
|
init_DemoContext();
|
|
26425
26926
|
init_demo_data();
|
|
26426
|
-
init_task_scope();
|
|
26427
26927
|
STATUS_FILTERS = ["all", "open", "in_progress", "done", "blocked", "needs_review"];
|
|
26428
26928
|
PRIORITY_COLORS = {
|
|
26429
26929
|
P0: "red",
|
|
@@ -26839,12 +27339,12 @@ async function loadGatewayConfig() {
|
|
|
26839
27339
|
state.running = false;
|
|
26840
27340
|
}
|
|
26841
27341
|
try {
|
|
26842
|
-
const { existsSync:
|
|
27342
|
+
const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
|
|
26843
27343
|
const { join } = await import("path");
|
|
26844
27344
|
const home = process.env.HOME ?? "";
|
|
26845
27345
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
26846
|
-
if (
|
|
26847
|
-
const raw = JSON.parse(
|
|
27346
|
+
if (existsSync32(configPath)) {
|
|
27347
|
+
const raw = JSON.parse(readFileSync28(configPath, "utf8"));
|
|
26848
27348
|
state.port = raw.port ?? 3100;
|
|
26849
27349
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
26850
27350
|
if (raw.adapters) {
|
|
@@ -27414,41 +27914,16 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
27414
27914
|
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
27415
27915
|
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
27416
27916
|
try {
|
|
27417
|
-
const {
|
|
27418
|
-
const
|
|
27419
|
-
|
|
27420
|
-
|
|
27421
|
-
|
|
27422
|
-
|
|
27423
|
-
|
|
27424
|
-
|
|
27425
|
-
|
|
27426
|
-
|
|
27427
|
-
sql: `SELECT DISTINCT project_name,
|
|
27428
|
-
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
27429
|
-
FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')${tmScope.sql}
|
|
27430
|
-
GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
|
|
27431
|
-
args: [emp.name, ...tmScope.args]
|
|
27432
|
-
});
|
|
27433
|
-
const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
|
|
27434
|
-
const urgency = Number(r.urgency);
|
|
27435
|
-
let pStatus = "idle";
|
|
27436
|
-
if (urgency === 1) pStatus = "active";
|
|
27437
|
-
else if (urgency === 2) pStatus = "has_tasks";
|
|
27438
|
-
return { name: String(r.project_name), status: pStatus };
|
|
27439
|
-
});
|
|
27440
|
-
if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
|
|
27441
|
-
}
|
|
27442
|
-
for (const emp of roster) {
|
|
27443
|
-
const taskResult = await client.execute({
|
|
27444
|
-
sql: `SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress'${tmScope.sql} ORDER BY updated_at DESC LIMIT 1`,
|
|
27445
|
-
args: [emp.name, ...tmScope.args]
|
|
27446
|
-
});
|
|
27447
|
-
if (taskResult.rows.length > 0) {
|
|
27448
|
-
currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
|
|
27449
|
-
}
|
|
27450
|
-
}
|
|
27451
|
-
}
|
|
27917
|
+
const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
27918
|
+
const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
|
|
27919
|
+
memoryCounts = teamMetrics.memoryCounts;
|
|
27920
|
+
projectsByEmployee = new Map(
|
|
27921
|
+
Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
|
|
27922
|
+
name,
|
|
27923
|
+
projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
|
|
27924
|
+
])
|
|
27925
|
+
);
|
|
27926
|
+
currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
|
|
27452
27927
|
} catch {
|
|
27453
27928
|
}
|
|
27454
27929
|
const teamData = roster.map((emp) => {
|
|
@@ -27467,12 +27942,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
27467
27942
|
setMembers(teamData);
|
|
27468
27943
|
setDbError(null);
|
|
27469
27944
|
try {
|
|
27470
|
-
const { existsSync:
|
|
27945
|
+
const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
|
|
27471
27946
|
const { join } = await import("path");
|
|
27472
27947
|
const home = process.env.HOME ?? "";
|
|
27473
27948
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
27474
|
-
if (
|
|
27475
|
-
const raw = JSON.parse(
|
|
27949
|
+
if (existsSync32(gatewayConfig)) {
|
|
27950
|
+
const raw = JSON.parse(readFileSync28(gatewayConfig, "utf8"));
|
|
27476
27951
|
if (raw.agents && raw.agents.length > 0) {
|
|
27477
27952
|
setExternals(raw.agents.map((a) => ({
|
|
27478
27953
|
name: a.name,
|
|
@@ -27639,7 +28114,6 @@ var init_Team = __esm({
|
|
|
27639
28114
|
init_demo_data();
|
|
27640
28115
|
init_useOrchestrator();
|
|
27641
28116
|
init_agent_status();
|
|
27642
|
-
init_task_scope();
|
|
27643
28117
|
DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
|
|
27644
28118
|
}
|
|
27645
28119
|
});
|
|
@@ -27653,8 +28127,8 @@ __export(wiki_client_exports, {
|
|
|
27653
28127
|
listDocuments: () => listDocuments,
|
|
27654
28128
|
listWorkspaces: () => listWorkspaces
|
|
27655
28129
|
});
|
|
27656
|
-
async function wikiFetch(config,
|
|
27657
|
-
const url = `${config.baseUrl}/api/v1${
|
|
28130
|
+
async function wikiFetch(config, path46, method = "GET", body) {
|
|
28131
|
+
const url = `${config.baseUrl}/api/v1${path46}`;
|
|
27658
28132
|
const headers = {
|
|
27659
28133
|
Authorization: `Bearer ${config.apiKey}`,
|
|
27660
28134
|
"Content-Type": "application/json"
|
|
@@ -27687,7 +28161,7 @@ async function wikiFetch(config, path45, method = "GET", body) {
|
|
|
27687
28161
|
}
|
|
27688
28162
|
}
|
|
27689
28163
|
if (!response.ok) {
|
|
27690
|
-
throw new Error(`Wiki API ${method} ${
|
|
28164
|
+
throw new Error(`Wiki API ${method} ${path46}: ${response.status} ${response.statusText}`);
|
|
27691
28165
|
}
|
|
27692
28166
|
return response.json();
|
|
27693
28167
|
} finally {
|
|
@@ -27933,24 +28407,8 @@ function WikiView({ onBack }) {
|
|
|
27933
28407
|
}
|
|
27934
28408
|
setSearchLoading(true);
|
|
27935
28409
|
try {
|
|
27936
|
-
const {
|
|
27937
|
-
|
|
27938
|
-
const result = await client.execute({
|
|
27939
|
-
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
27940
|
-
FROM memories
|
|
27941
|
-
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
27942
|
-
ORDER BY timestamp DESC LIMIT 20`,
|
|
27943
|
-
args: [`%${query}%`]
|
|
27944
|
-
});
|
|
27945
|
-
setSearchResults(
|
|
27946
|
-
result.rows.map((r) => ({
|
|
27947
|
-
id: String(r.id),
|
|
27948
|
-
agentId: String(r.agent_id),
|
|
27949
|
-
rawText: String(r.raw_text),
|
|
27950
|
-
timestamp: String(r.timestamp),
|
|
27951
|
-
projectName: String(r.project_name ?? "")
|
|
27952
|
-
}))
|
|
27953
|
-
);
|
|
28410
|
+
const { searchWikiMemoryRows: searchWikiMemoryRows2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
28411
|
+
setSearchResults(await searchWikiMemoryRows2(query));
|
|
27954
28412
|
} catch {
|
|
27955
28413
|
setSearchResults([]);
|
|
27956
28414
|
} finally {
|
|
@@ -28297,12 +28755,12 @@ function SettingsView({ onBack }) {
|
|
|
28297
28755
|
}
|
|
28298
28756
|
setProviders(providerList);
|
|
28299
28757
|
try {
|
|
28300
|
-
const { existsSync:
|
|
28758
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
28301
28759
|
const { join } = await import("path");
|
|
28302
28760
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
28303
28761
|
const cfg = await loadConfig2();
|
|
28304
28762
|
const home = process.env.HOME ?? "";
|
|
28305
|
-
const hasKey =
|
|
28763
|
+
const hasKey = existsSync32(join(home, ".exe-os", "master.key"));
|
|
28306
28764
|
if (cfg.cloud) {
|
|
28307
28765
|
setCloud({
|
|
28308
28766
|
configured: true,
|
|
@@ -28315,22 +28773,22 @@ function SettingsView({ onBack }) {
|
|
|
28315
28773
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
28316
28774
|
let daemon = "unknown";
|
|
28317
28775
|
try {
|
|
28318
|
-
daemon =
|
|
28776
|
+
daemon = existsSync32(pidPath) ? "running" : "stopped";
|
|
28319
28777
|
} catch {
|
|
28320
28778
|
}
|
|
28321
28779
|
let version = "unknown";
|
|
28322
28780
|
try {
|
|
28323
|
-
const { readFileSync:
|
|
28324
|
-
const { createRequire:
|
|
28325
|
-
const require2 =
|
|
28781
|
+
const { readFileSync: readFileSync28 } = await import("fs");
|
|
28782
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
28783
|
+
const require2 = createRequire3(import.meta.url);
|
|
28326
28784
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
28327
|
-
const pkg = JSON.parse(
|
|
28785
|
+
const pkg = JSON.parse(readFileSync28(pkgPath, "utf8"));
|
|
28328
28786
|
version = pkg.version;
|
|
28329
28787
|
} catch {
|
|
28330
28788
|
try {
|
|
28331
|
-
const { readFileSync:
|
|
28789
|
+
const { readFileSync: readFileSync28 } = await import("fs");
|
|
28332
28790
|
const { join: joinPath } = await import("path");
|
|
28333
|
-
const pkg = JSON.parse(
|
|
28791
|
+
const pkg = JSON.parse(readFileSync28(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
28334
28792
|
version = pkg.version;
|
|
28335
28793
|
} catch {
|
|
28336
28794
|
}
|
|
@@ -29131,15 +29589,15 @@ __export(installer_exports2, {
|
|
|
29131
29589
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
29132
29590
|
});
|
|
29133
29591
|
import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
29134
|
-
import { existsSync as
|
|
29135
|
-
import
|
|
29136
|
-
import
|
|
29137
|
-
async function registerOpenCodeMcp(packageRoot, homeDir =
|
|
29138
|
-
const configDir =
|
|
29139
|
-
const configPath =
|
|
29592
|
+
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
|
|
29593
|
+
import path43 from "path";
|
|
29594
|
+
import os18 from "os";
|
|
29595
|
+
async function registerOpenCodeMcp(packageRoot, homeDir = os18.homedir()) {
|
|
29596
|
+
const configDir = path43.join(homeDir, ".config", "opencode");
|
|
29597
|
+
const configPath = path43.join(configDir, "opencode.json");
|
|
29140
29598
|
await mkdir7(configDir, { recursive: true });
|
|
29141
29599
|
let config = {};
|
|
29142
|
-
if (
|
|
29600
|
+
if (existsSync29(configPath)) {
|
|
29143
29601
|
try {
|
|
29144
29602
|
config = JSON.parse(await readFile6(configPath, "utf-8"));
|
|
29145
29603
|
} catch {
|
|
@@ -29151,7 +29609,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
|
|
|
29151
29609
|
}
|
|
29152
29610
|
const newEntry = {
|
|
29153
29611
|
type: "local",
|
|
29154
|
-
command: ["node",
|
|
29612
|
+
command: ["node", path43.join(packageRoot, "dist", "mcp", "server.js")],
|
|
29155
29613
|
enabled: true
|
|
29156
29614
|
};
|
|
29157
29615
|
const current = config.mcp["exe-os"];
|
|
@@ -29165,15 +29623,15 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
|
|
|
29165
29623
|
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
29166
29624
|
return true;
|
|
29167
29625
|
}
|
|
29168
|
-
async function installOpenCodePlugin(packageRoot, homeDir =
|
|
29169
|
-
const pluginDir =
|
|
29170
|
-
const pluginPath =
|
|
29626
|
+
async function installOpenCodePlugin(packageRoot, homeDir = os18.homedir()) {
|
|
29627
|
+
const pluginDir = path43.join(homeDir, ".config", "opencode", "plugins");
|
|
29628
|
+
const pluginPath = path43.join(pluginDir, "exe-os.mjs");
|
|
29171
29629
|
await mkdir7(pluginDir, { recursive: true });
|
|
29172
29630
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
29173
29631
|
/__PACKAGE_ROOT__/g,
|
|
29174
29632
|
packageRoot.replace(/\\/g, "\\\\")
|
|
29175
29633
|
);
|
|
29176
|
-
if (
|
|
29634
|
+
if (existsSync29(pluginPath)) {
|
|
29177
29635
|
const existing = await readFile6(pluginPath, "utf-8");
|
|
29178
29636
|
if (existing === pluginContent) {
|
|
29179
29637
|
return false;
|
|
@@ -29182,17 +29640,17 @@ async function installOpenCodePlugin(packageRoot, homeDir = os17.homedir()) {
|
|
|
29182
29640
|
await writeFile7(pluginPath, pluginContent);
|
|
29183
29641
|
return true;
|
|
29184
29642
|
}
|
|
29185
|
-
function verifyOpenCodeHooks(homeDir =
|
|
29186
|
-
const configPath =
|
|
29187
|
-
const pluginPath =
|
|
29188
|
-
if (!
|
|
29643
|
+
function verifyOpenCodeHooks(homeDir = os18.homedir()) {
|
|
29644
|
+
const configPath = path43.join(homeDir, ".config", "opencode", "opencode.json");
|
|
29645
|
+
const pluginPath = path43.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
29646
|
+
if (!existsSync29(configPath)) return false;
|
|
29189
29647
|
try {
|
|
29190
|
-
const config = JSON.parse(
|
|
29648
|
+
const config = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
29191
29649
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
29192
29650
|
} catch {
|
|
29193
29651
|
return false;
|
|
29194
29652
|
}
|
|
29195
|
-
if (!
|
|
29653
|
+
if (!existsSync29(pluginPath)) return false;
|
|
29196
29654
|
return true;
|
|
29197
29655
|
}
|
|
29198
29656
|
async function runOpenCodeInstaller(homeDir) {
|
|
@@ -29225,19 +29683,19 @@ __export(installer_exports3, {
|
|
|
29225
29683
|
verifyCodexHooks: () => verifyCodexHooks
|
|
29226
29684
|
});
|
|
29227
29685
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
29228
|
-
import { existsSync as
|
|
29229
|
-
import
|
|
29230
|
-
import
|
|
29231
|
-
async function mergeCodexHooks(packageRoot, homeDir =
|
|
29232
|
-
const codexDir =
|
|
29233
|
-
const hooksPath =
|
|
29234
|
-
const logsDir =
|
|
29235
|
-
const hookLogPath =
|
|
29686
|
+
import { existsSync as existsSync30 } from "fs";
|
|
29687
|
+
import path44 from "path";
|
|
29688
|
+
import os19 from "os";
|
|
29689
|
+
async function mergeCodexHooks(packageRoot, homeDir = os19.homedir()) {
|
|
29690
|
+
const codexDir = path44.join(homeDir, ".codex");
|
|
29691
|
+
const hooksPath = path44.join(codexDir, "hooks.json");
|
|
29692
|
+
const logsDir = path44.join(homeDir, ".exe-os", "logs");
|
|
29693
|
+
const hookLogPath = path44.join(logsDir, "hooks.log");
|
|
29236
29694
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
29237
29695
|
await mkdir8(codexDir, { recursive: true });
|
|
29238
29696
|
await mkdir8(logsDir, { recursive: true });
|
|
29239
29697
|
let hooksJson = {};
|
|
29240
|
-
if (
|
|
29698
|
+
if (existsSync30(hooksPath)) {
|
|
29241
29699
|
try {
|
|
29242
29700
|
hooksJson = JSON.parse(await readFile7(hooksPath, "utf-8"));
|
|
29243
29701
|
} catch {
|
|
@@ -29254,7 +29712,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29254
29712
|
hooks: [
|
|
29255
29713
|
{
|
|
29256
29714
|
type: "command",
|
|
29257
|
-
command: `node "${
|
|
29715
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
29258
29716
|
timeout: 30,
|
|
29259
29717
|
statusMessage: "exe-os: loading memory brief"
|
|
29260
29718
|
}
|
|
@@ -29269,11 +29727,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29269
29727
|
hooks: [
|
|
29270
29728
|
{
|
|
29271
29729
|
type: "command",
|
|
29272
|
-
command: `node "${
|
|
29730
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
29273
29731
|
},
|
|
29274
29732
|
{
|
|
29275
29733
|
type: "command",
|
|
29276
|
-
command: `node "${
|
|
29734
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
29277
29735
|
}
|
|
29278
29736
|
]
|
|
29279
29737
|
},
|
|
@@ -29285,11 +29743,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29285
29743
|
hooks: [
|
|
29286
29744
|
{
|
|
29287
29745
|
type: "command",
|
|
29288
|
-
command: `node "${
|
|
29746
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
29289
29747
|
},
|
|
29290
29748
|
{
|
|
29291
29749
|
type: "command",
|
|
29292
|
-
command: `node "${
|
|
29750
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
29293
29751
|
timeout: 5
|
|
29294
29752
|
}
|
|
29295
29753
|
]
|
|
@@ -29302,7 +29760,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29302
29760
|
hooks: [
|
|
29303
29761
|
{
|
|
29304
29762
|
type: "command",
|
|
29305
|
-
command: `node "${
|
|
29763
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
29306
29764
|
}
|
|
29307
29765
|
]
|
|
29308
29766
|
},
|
|
@@ -29315,7 +29773,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29315
29773
|
hooks: [
|
|
29316
29774
|
{
|
|
29317
29775
|
type: "command",
|
|
29318
|
-
command: `node "${
|
|
29776
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
29319
29777
|
}
|
|
29320
29778
|
]
|
|
29321
29779
|
},
|
|
@@ -29346,9 +29804,9 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29346
29804
|
await writeFile8(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
29347
29805
|
return { added, skipped };
|
|
29348
29806
|
}
|
|
29349
|
-
function verifyCodexHooks(homeDir =
|
|
29350
|
-
const hooksPath =
|
|
29351
|
-
if (!
|
|
29807
|
+
function verifyCodexHooks(homeDir = os19.homedir()) {
|
|
29808
|
+
const hooksPath = path44.join(homeDir, ".codex", "hooks.json");
|
|
29809
|
+
if (!existsSync30(hooksPath)) return false;
|
|
29352
29810
|
try {
|
|
29353
29811
|
const hooksJson = JSON.parse(
|
|
29354
29812
|
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
@@ -29368,14 +29826,14 @@ function verifyCodexHooks(homeDir = os18.homedir()) {
|
|
|
29368
29826
|
return false;
|
|
29369
29827
|
}
|
|
29370
29828
|
}
|
|
29371
|
-
async function installCodexStatusLine(homeDir =
|
|
29829
|
+
async function installCodexStatusLine(homeDir = os19.homedir()) {
|
|
29372
29830
|
const prefs = loadPreferences(homeDir);
|
|
29373
29831
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
29374
|
-
const codexDir =
|
|
29375
|
-
const configPath =
|
|
29832
|
+
const codexDir = path44.join(homeDir, ".codex");
|
|
29833
|
+
const configPath = path44.join(codexDir, "config.toml");
|
|
29376
29834
|
await mkdir8(codexDir, { recursive: true });
|
|
29377
29835
|
let content = "";
|
|
29378
|
-
if (
|
|
29836
|
+
if (existsSync30(configPath)) {
|
|
29379
29837
|
content = await readFile7(configPath, "utf-8");
|
|
29380
29838
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
29381
29839
|
return "already-configured";
|
|
@@ -29423,14 +29881,14 @@ var init_installer3 = __esm({
|
|
|
29423
29881
|
});
|
|
29424
29882
|
|
|
29425
29883
|
// src/bin/cli.ts
|
|
29426
|
-
import { existsSync as
|
|
29427
|
-
import
|
|
29428
|
-
import
|
|
29884
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync9, rmSync } from "fs";
|
|
29885
|
+
import path45 from "path";
|
|
29886
|
+
import os20 from "os";
|
|
29429
29887
|
var args = process.argv.slice(2);
|
|
29430
29888
|
if (args.includes("--version") || args.includes("-v")) {
|
|
29431
29889
|
try {
|
|
29432
|
-
const pkgPath =
|
|
29433
|
-
const pkg = JSON.parse(
|
|
29890
|
+
const pkgPath = path45.join(path45.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
29891
|
+
const pkg = JSON.parse(readFileSync27(pkgPath, "utf8"));
|
|
29434
29892
|
console.log(pkg.version);
|
|
29435
29893
|
} catch {
|
|
29436
29894
|
console.log("unknown");
|
|
@@ -29594,11 +30052,11 @@ ID: ${result.id}`);
|
|
|
29594
30052
|
});
|
|
29595
30053
|
await init_App2().then(() => App_exports);
|
|
29596
30054
|
} else {
|
|
29597
|
-
const claudeDir =
|
|
29598
|
-
const settingsPath =
|
|
29599
|
-
const hasClaudeCode =
|
|
30055
|
+
const claudeDir = path45.join(os20.homedir(), ".claude");
|
|
30056
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30057
|
+
const hasClaudeCode = existsSync31(settingsPath) && (() => {
|
|
29600
30058
|
try {
|
|
29601
|
-
const raw =
|
|
30059
|
+
const raw = readFileSync27(settingsPath, "utf8");
|
|
29602
30060
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
29603
30061
|
} catch {
|
|
29604
30062
|
return false;
|
|
@@ -29608,9 +30066,9 @@ ID: ${result.id}`);
|
|
|
29608
30066
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
29609
30067
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
29610
30068
|
try {
|
|
29611
|
-
const rosterPath =
|
|
29612
|
-
if (
|
|
29613
|
-
const roster = JSON.parse(
|
|
30069
|
+
const rosterPath = path45.join(os20.homedir(), ".exe-os", "exe-employees.json");
|
|
30070
|
+
if (existsSync31(rosterPath)) {
|
|
30071
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29614
30072
|
const coo = roster.find((e) => e.role === "COO");
|
|
29615
30073
|
if (coo) cooName = coo.name;
|
|
29616
30074
|
}
|
|
@@ -29674,14 +30132,14 @@ async function runCodexInstall() {
|
|
|
29674
30132
|
}
|
|
29675
30133
|
}
|
|
29676
30134
|
async function runClaudeCheck() {
|
|
29677
|
-
const claudeDir =
|
|
29678
|
-
const settingsPath =
|
|
29679
|
-
const claudeJsonPath =
|
|
30135
|
+
const claudeDir = path45.join(os20.homedir(), ".claude");
|
|
30136
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30137
|
+
const claudeJsonPath = path45.join(os20.homedir(), ".claude.json");
|
|
29680
30138
|
let ok = true;
|
|
29681
|
-
if (
|
|
30139
|
+
if (existsSync31(settingsPath)) {
|
|
29682
30140
|
let settings;
|
|
29683
30141
|
try {
|
|
29684
|
-
settings = JSON.parse(
|
|
30142
|
+
settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
|
|
29685
30143
|
} catch {
|
|
29686
30144
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
29687
30145
|
ok = false;
|
|
@@ -29707,10 +30165,10 @@ async function runClaudeCheck() {
|
|
|
29707
30165
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
29708
30166
|
ok = false;
|
|
29709
30167
|
}
|
|
29710
|
-
if (
|
|
30168
|
+
if (existsSync31(claudeJsonPath)) {
|
|
29711
30169
|
let claudeJson;
|
|
29712
30170
|
try {
|
|
29713
|
-
claudeJson = JSON.parse(
|
|
30171
|
+
claudeJson = JSON.parse(readFileSync27(claudeJsonPath, "utf8"));
|
|
29714
30172
|
} catch {
|
|
29715
30173
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
29716
30174
|
ok = false;
|
|
@@ -29729,8 +30187,8 @@ async function runClaudeCheck() {
|
|
|
29729
30187
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
29730
30188
|
ok = false;
|
|
29731
30189
|
}
|
|
29732
|
-
const skillsDir =
|
|
29733
|
-
if (
|
|
30190
|
+
const skillsDir = path45.join(claudeDir, "skills");
|
|
30191
|
+
if (existsSync31(skillsDir)) {
|
|
29734
30192
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
29735
30193
|
} else {
|
|
29736
30194
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -29746,17 +30204,17 @@ async function runClaudeCheck() {
|
|
|
29746
30204
|
async function runClaudeUninstall(flags = []) {
|
|
29747
30205
|
const dryRun = flags.includes("--dry-run");
|
|
29748
30206
|
const purge = flags.includes("--purge");
|
|
29749
|
-
const homeDir =
|
|
29750
|
-
const claudeDir =
|
|
29751
|
-
const settingsPath =
|
|
29752
|
-
const claudeJsonPath =
|
|
29753
|
-
const exeOsDir =
|
|
30207
|
+
const homeDir = os20.homedir();
|
|
30208
|
+
const claudeDir = path45.join(homeDir, ".claude");
|
|
30209
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30210
|
+
const claudeJsonPath = path45.join(homeDir, ".claude.json");
|
|
30211
|
+
const exeOsDir = path45.join(homeDir, ".exe-os");
|
|
29754
30212
|
let removed = 0;
|
|
29755
30213
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
29756
30214
|
let settings = {};
|
|
29757
|
-
if (
|
|
30215
|
+
if (existsSync31(settingsPath)) {
|
|
29758
30216
|
try {
|
|
29759
|
-
settings = JSON.parse(
|
|
30217
|
+
settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
|
|
29760
30218
|
} catch {
|
|
29761
30219
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
29762
30220
|
if (purge) {
|
|
@@ -29794,15 +30252,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29794
30252
|
permCount = before - settings.permissions.allow.length;
|
|
29795
30253
|
}
|
|
29796
30254
|
if (!dryRun) {
|
|
29797
|
-
|
|
30255
|
+
writeFileSync20(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
29798
30256
|
}
|
|
29799
30257
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
29800
30258
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
29801
30259
|
removed++;
|
|
29802
30260
|
}
|
|
29803
30261
|
}
|
|
29804
|
-
if (
|
|
29805
|
-
const raw =
|
|
30262
|
+
if (existsSync31(claudeJsonPath)) {
|
|
30263
|
+
const raw = readFileSync27(claudeJsonPath, "utf8");
|
|
29806
30264
|
if (raw.length > 1e6) {
|
|
29807
30265
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
29808
30266
|
} else {
|
|
@@ -29823,7 +30281,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29823
30281
|
}
|
|
29824
30282
|
if (removedMcp) {
|
|
29825
30283
|
if (!dryRun) {
|
|
29826
|
-
|
|
30284
|
+
writeFileSync20(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
29827
30285
|
}
|
|
29828
30286
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
29829
30287
|
removed++;
|
|
@@ -29831,14 +30289,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29831
30289
|
}
|
|
29832
30290
|
}
|
|
29833
30291
|
}
|
|
29834
|
-
const skillsDir =
|
|
29835
|
-
if (
|
|
30292
|
+
const skillsDir = path45.join(claudeDir, "skills");
|
|
30293
|
+
if (existsSync31(skillsDir)) {
|
|
29836
30294
|
let skillCount = 0;
|
|
29837
30295
|
try {
|
|
29838
30296
|
const entries = readdirSync9(skillsDir);
|
|
29839
30297
|
for (const entry of entries) {
|
|
29840
30298
|
if (entry.startsWith("exe")) {
|
|
29841
|
-
const fullPath =
|
|
30299
|
+
const fullPath = path45.join(skillsDir, entry);
|
|
29842
30300
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
29843
30301
|
skillCount++;
|
|
29844
30302
|
}
|
|
@@ -29850,30 +30308,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29850
30308
|
removed++;
|
|
29851
30309
|
}
|
|
29852
30310
|
}
|
|
29853
|
-
const claudeMdPath =
|
|
29854
|
-
if (
|
|
29855
|
-
const content =
|
|
30311
|
+
const claudeMdPath = path45.join(claudeDir, "CLAUDE.md");
|
|
30312
|
+
if (existsSync31(claudeMdPath)) {
|
|
30313
|
+
const content = readFileSync27(claudeMdPath, "utf8");
|
|
29856
30314
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
29857
30315
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
29858
30316
|
const startIdx = content.indexOf(startMarker);
|
|
29859
30317
|
const endIdx = content.indexOf(endMarker);
|
|
29860
30318
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
29861
30319
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
29862
|
-
if (!dryRun)
|
|
30320
|
+
if (!dryRun) writeFileSync20(claudeMdPath, cleaned);
|
|
29863
30321
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
29864
30322
|
removed++;
|
|
29865
30323
|
}
|
|
29866
30324
|
}
|
|
29867
|
-
const agentsDir =
|
|
29868
|
-
if (
|
|
30325
|
+
const agentsDir = path45.join(claudeDir, "agents");
|
|
30326
|
+
if (existsSync31(agentsDir)) {
|
|
29869
30327
|
let agentCount = 0;
|
|
29870
30328
|
try {
|
|
29871
30329
|
const entries = readdirSync9(agentsDir).filter((f) => f.endsWith(".md"));
|
|
29872
30330
|
let knownNames = /* @__PURE__ */ new Set();
|
|
29873
|
-
const rosterPath =
|
|
29874
|
-
if (
|
|
30331
|
+
const rosterPath = path45.join(exeOsDir, "exe-employees.json");
|
|
30332
|
+
if (existsSync31(rosterPath)) {
|
|
29875
30333
|
try {
|
|
29876
|
-
const roster = JSON.parse(
|
|
30334
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29877
30335
|
knownNames = new Set(roster.map((e) => e.name));
|
|
29878
30336
|
} catch {
|
|
29879
30337
|
}
|
|
@@ -29881,7 +30339,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29881
30339
|
for (const entry of entries) {
|
|
29882
30340
|
const name = entry.replace(/\.md$/, "");
|
|
29883
30341
|
if (knownNames.has(name)) {
|
|
29884
|
-
if (!dryRun) rmSync(
|
|
30342
|
+
if (!dryRun) rmSync(path45.join(agentsDir, entry), { force: true });
|
|
29885
30343
|
agentCount++;
|
|
29886
30344
|
}
|
|
29887
30345
|
}
|
|
@@ -29892,16 +30350,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29892
30350
|
removed++;
|
|
29893
30351
|
}
|
|
29894
30352
|
}
|
|
29895
|
-
const projectsDir =
|
|
29896
|
-
if (
|
|
30353
|
+
const projectsDir = path45.join(claudeDir, "projects");
|
|
30354
|
+
if (existsSync31(projectsDir)) {
|
|
29897
30355
|
let projectCount = 0;
|
|
29898
30356
|
try {
|
|
29899
30357
|
const projects = readdirSync9(projectsDir);
|
|
29900
30358
|
for (const proj of projects) {
|
|
29901
|
-
const projSettings =
|
|
29902
|
-
if (!
|
|
30359
|
+
const projSettings = path45.join(projectsDir, proj, "settings.json");
|
|
30360
|
+
if (!existsSync31(projSettings)) continue;
|
|
29903
30361
|
try {
|
|
29904
|
-
const pSettings = JSON.parse(
|
|
30362
|
+
const pSettings = JSON.parse(readFileSync27(projSettings, "utf8"));
|
|
29905
30363
|
let changed = false;
|
|
29906
30364
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
29907
30365
|
const before = pSettings.permissions.allow.length;
|
|
@@ -29911,7 +30369,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29911
30369
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
29912
30370
|
}
|
|
29913
30371
|
if (changed && !dryRun) {
|
|
29914
|
-
|
|
30372
|
+
writeFileSync20(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
29915
30373
|
}
|
|
29916
30374
|
if (changed) projectCount++;
|
|
29917
30375
|
} catch {
|
|
@@ -29935,18 +30393,18 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29935
30393
|
};
|
|
29936
30394
|
const exeBinPath = findExeBin3();
|
|
29937
30395
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
29938
|
-
const binDir =
|
|
30396
|
+
const binDir = path45.dirname(exeBinPath);
|
|
29939
30397
|
let symlinkCount = 0;
|
|
29940
|
-
const rosterPath =
|
|
29941
|
-
if (
|
|
29942
|
-
const roster = JSON.parse(
|
|
30398
|
+
const rosterPath = path45.join(exeOsDir, "exe-employees.json");
|
|
30399
|
+
if (existsSync31(rosterPath)) {
|
|
30400
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29943
30401
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
29944
30402
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
29945
30403
|
for (const emp of roster) {
|
|
29946
30404
|
if (emp.name === coordinatorName) continue;
|
|
29947
30405
|
for (const suffix of ["", "-opencode"]) {
|
|
29948
|
-
const linkPath =
|
|
29949
|
-
if (
|
|
30406
|
+
const linkPath = path45.join(binDir, `${emp.name}${suffix}`);
|
|
30407
|
+
if (existsSync31(linkPath)) {
|
|
29950
30408
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
29951
30409
|
symlinkCount++;
|
|
29952
30410
|
}
|
|
@@ -29959,7 +30417,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29959
30417
|
}
|
|
29960
30418
|
} catch {
|
|
29961
30419
|
}
|
|
29962
|
-
if (purge &&
|
|
30420
|
+
if (purge && existsSync31(exeOsDir)) {
|
|
29963
30421
|
if (!dryRun) {
|
|
29964
30422
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
29965
30423
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -29984,7 +30442,7 @@ async function checkForUpdateOnBoot() {
|
|
|
29984
30442
|
const config = await loadConfig2();
|
|
29985
30443
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
29986
30444
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
29987
|
-
const packageRoot =
|
|
30445
|
+
const packageRoot = path45.resolve(
|
|
29988
30446
|
new URL("../..", import.meta.url).pathname
|
|
29989
30447
|
);
|
|
29990
30448
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -30044,7 +30502,7 @@ async function runActivate(key) {
|
|
|
30044
30502
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
30045
30503
|
if (idTemplate) {
|
|
30046
30504
|
const idPath = identityPath2(name);
|
|
30047
|
-
const dir =
|
|
30505
|
+
const dir = path45.dirname(idPath);
|
|
30048
30506
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
30049
30507
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
30050
30508
|
}
|