@askexenow/exe-os 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
package/dist/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,35 +9664,6 @@ var init_session_kill_telemetry = __esm({
|
|
|
9288
9664
|
}
|
|
9289
9665
|
});
|
|
9290
9666
|
|
|
9291
|
-
// src/lib/task-scope.ts
|
|
9292
|
-
var task_scope_exports = {};
|
|
9293
|
-
__export(task_scope_exports, {
|
|
9294
|
-
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
9295
|
-
sessionScopeFilter: () => sessionScopeFilter
|
|
9296
|
-
});
|
|
9297
|
-
function getCurrentSessionScope() {
|
|
9298
|
-
try {
|
|
9299
|
-
return resolveExeSession();
|
|
9300
|
-
} catch {
|
|
9301
|
-
return null;
|
|
9302
|
-
}
|
|
9303
|
-
}
|
|
9304
|
-
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
9305
|
-
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
9306
|
-
if (!scope) return { sql: "", args: [] };
|
|
9307
|
-
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
9308
|
-
return {
|
|
9309
|
-
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
9310
|
-
args: [scope]
|
|
9311
|
-
};
|
|
9312
|
-
}
|
|
9313
|
-
var init_task_scope = __esm({
|
|
9314
|
-
"src/lib/task-scope.ts"() {
|
|
9315
|
-
"use strict";
|
|
9316
|
-
init_tmux_routing();
|
|
9317
|
-
}
|
|
9318
|
-
});
|
|
9319
|
-
|
|
9320
9667
|
// src/lib/tasks-crud.ts
|
|
9321
9668
|
var tasks_crud_exports = {};
|
|
9322
9669
|
__export(tasks_crud_exports, {
|
|
@@ -9334,12 +9681,12 @@ __export(tasks_crud_exports, {
|
|
|
9334
9681
|
updateTaskStatus: () => updateTaskStatus,
|
|
9335
9682
|
writeCheckpoint: () => writeCheckpoint
|
|
9336
9683
|
});
|
|
9337
|
-
import
|
|
9338
|
-
import
|
|
9339
|
-
import
|
|
9684
|
+
import crypto8 from "crypto";
|
|
9685
|
+
import path23 from "path";
|
|
9686
|
+
import os14 from "os";
|
|
9340
9687
|
import { execSync as execSync6 } from "child_process";
|
|
9341
9688
|
import { mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
9342
|
-
import { existsSync as
|
|
9689
|
+
import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
|
|
9343
9690
|
async function writeCheckpoint(input) {
|
|
9344
9691
|
const client = getClient();
|
|
9345
9692
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -9455,7 +9802,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
9455
9802
|
}
|
|
9456
9803
|
async function createTaskCore(input) {
|
|
9457
9804
|
const client = getClient();
|
|
9458
|
-
const id =
|
|
9805
|
+
const id = crypto8.randomUUID();
|
|
9459
9806
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9460
9807
|
const slug = slugify(input.title);
|
|
9461
9808
|
let earlySessionScope = null;
|
|
@@ -9514,8 +9861,8 @@ ${laneWarning}` : laneWarning;
|
|
|
9514
9861
|
}
|
|
9515
9862
|
if (input.baseDir) {
|
|
9516
9863
|
try {
|
|
9517
|
-
await mkdir5(
|
|
9518
|
-
await mkdir5(
|
|
9864
|
+
await mkdir5(path23.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
9865
|
+
await mkdir5(path23.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
9519
9866
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
9520
9867
|
await ensureGitignoreExe(input.baseDir);
|
|
9521
9868
|
} catch {
|
|
@@ -9551,13 +9898,19 @@ ${laneWarning}` : laneWarning;
|
|
|
9551
9898
|
});
|
|
9552
9899
|
if (input.baseDir) {
|
|
9553
9900
|
try {
|
|
9554
|
-
const EXE_OS_DIR =
|
|
9555
|
-
const mdPath =
|
|
9556
|
-
const mdDir =
|
|
9557
|
-
if (!
|
|
9901
|
+
const EXE_OS_DIR = path23.join(os14.homedir(), ".exe-os");
|
|
9902
|
+
const mdPath = path23.join(EXE_OS_DIR, taskFile);
|
|
9903
|
+
const mdDir = path23.dirname(mdPath);
|
|
9904
|
+
if (!existsSync21(mdDir)) await mkdir5(mdDir, { recursive: true });
|
|
9558
9905
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
9559
9906
|
const mdContent = `# ${input.title}
|
|
9560
9907
|
|
|
9908
|
+
## MANDATORY: When done
|
|
9909
|
+
|
|
9910
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
9911
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
9912
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
9913
|
+
|
|
9561
9914
|
**ID:** ${id}
|
|
9562
9915
|
**Status:** ${initialStatus}
|
|
9563
9916
|
**Priority:** ${input.priority}
|
|
@@ -9571,12 +9924,6 @@ ${laneWarning}` : laneWarning;
|
|
|
9571
9924
|
## Context
|
|
9572
9925
|
|
|
9573
9926
|
${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
9927
|
`;
|
|
9581
9928
|
await writeFile5(mdPath, mdContent, "utf-8");
|
|
9582
9929
|
} catch (err) {
|
|
@@ -9825,7 +10172,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
9825
10172
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
9826
10173
|
} catch {
|
|
9827
10174
|
}
|
|
9828
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
10175
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
9829
10176
|
try {
|
|
9830
10177
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
9831
10178
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -9854,9 +10201,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
9854
10201
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
9855
10202
|
}
|
|
9856
10203
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
9857
|
-
const archPath =
|
|
10204
|
+
const archPath = path23.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
9858
10205
|
try {
|
|
9859
|
-
if (
|
|
10206
|
+
if (existsSync21(archPath)) return;
|
|
9860
10207
|
const template = [
|
|
9861
10208
|
`# ${projectName} \u2014 System Architecture`,
|
|
9862
10209
|
"",
|
|
@@ -9889,10 +10236,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
9889
10236
|
}
|
|
9890
10237
|
}
|
|
9891
10238
|
async function ensureGitignoreExe(baseDir) {
|
|
9892
|
-
const gitignorePath =
|
|
10239
|
+
const gitignorePath = path23.join(baseDir, ".gitignore");
|
|
9893
10240
|
try {
|
|
9894
|
-
if (
|
|
9895
|
-
const content =
|
|
10241
|
+
if (existsSync21(gitignorePath)) {
|
|
10242
|
+
const content = readFileSync18(gitignorePath, "utf-8");
|
|
9896
10243
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
9897
10244
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
9898
10245
|
} else {
|
|
@@ -9923,58 +10270,42 @@ var init_tasks_crud = __esm({
|
|
|
9923
10270
|
});
|
|
9924
10271
|
|
|
9925
10272
|
// src/lib/tasks-review.ts
|
|
9926
|
-
import
|
|
9927
|
-
import { existsSync as
|
|
10273
|
+
import path24 from "path";
|
|
10274
|
+
import { existsSync as existsSync22, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
9928
10275
|
async function countPendingReviews(sessionScope) {
|
|
9929
10276
|
const client = getClient();
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
args: [sessionScope]
|
|
9934
|
-
});
|
|
9935
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
9936
|
-
}
|
|
10277
|
+
const scope = strictSessionScopeFilter(
|
|
10278
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10279
|
+
);
|
|
9937
10280
|
const result = await client.execute({
|
|
9938
|
-
sql:
|
|
9939
|
-
|
|
10281
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
10282
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
10283
|
+
args: [...scope.args]
|
|
9940
10284
|
});
|
|
9941
10285
|
return Number(result.rows[0]?.cnt) || 0;
|
|
9942
10286
|
}
|
|
9943
10287
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
9944
10288
|
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
|
-
}
|
|
10289
|
+
const scope = strictSessionScopeFilter(
|
|
10290
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10291
|
+
);
|
|
9954
10292
|
const result = await client.execute({
|
|
9955
10293
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9956
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
9957
|
-
args: [sinceIso]
|
|
10294
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
10295
|
+
args: [sinceIso, ...scope.args]
|
|
9958
10296
|
});
|
|
9959
10297
|
return Number(result.rows[0]?.cnt) || 0;
|
|
9960
10298
|
}
|
|
9961
10299
|
async function listPendingReviews(limit, sessionScope) {
|
|
9962
10300
|
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
|
-
}
|
|
10301
|
+
const scope = strictSessionScopeFilter(
|
|
10302
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
10303
|
+
);
|
|
9973
10304
|
const result = await client.execute({
|
|
9974
10305
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
9975
|
-
WHERE status = 'needs_review'
|
|
10306
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
9976
10307
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
9977
|
-
args: [limit]
|
|
10308
|
+
args: [...scope.args, limit]
|
|
9978
10309
|
});
|
|
9979
10310
|
return result.rows;
|
|
9980
10311
|
}
|
|
@@ -9986,7 +10317,7 @@ async function cleanupOrphanedReviews() {
|
|
|
9986
10317
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
9987
10318
|
AND assigned_by = 'system'
|
|
9988
10319
|
AND title LIKE 'Review:%'
|
|
9989
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
10320
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
9990
10321
|
args: [now]
|
|
9991
10322
|
});
|
|
9992
10323
|
const r1b = await client.execute({
|
|
@@ -10105,11 +10436,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
10105
10436
|
);
|
|
10106
10437
|
}
|
|
10107
10438
|
try {
|
|
10108
|
-
const cacheDir =
|
|
10109
|
-
if (
|
|
10439
|
+
const cacheDir = path24.join(EXE_AI_DIR, "session-cache");
|
|
10440
|
+
if (existsSync22(cacheDir)) {
|
|
10110
10441
|
for (const f of readdirSync5(cacheDir)) {
|
|
10111
10442
|
if (f.startsWith("review-notified-")) {
|
|
10112
|
-
unlinkSync6(
|
|
10443
|
+
unlinkSync6(path24.join(cacheDir, f));
|
|
10113
10444
|
}
|
|
10114
10445
|
}
|
|
10115
10446
|
}
|
|
@@ -10126,11 +10457,12 @@ var init_tasks_review = __esm({
|
|
|
10126
10457
|
init_tmux_routing();
|
|
10127
10458
|
init_session_key();
|
|
10128
10459
|
init_state_bus();
|
|
10460
|
+
init_task_scope();
|
|
10129
10461
|
}
|
|
10130
10462
|
});
|
|
10131
10463
|
|
|
10132
10464
|
// src/lib/tasks-chain.ts
|
|
10133
|
-
import
|
|
10465
|
+
import path25 from "path";
|
|
10134
10466
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
10135
10467
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
10136
10468
|
const client = getClient();
|
|
@@ -10147,7 +10479,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
10147
10479
|
});
|
|
10148
10480
|
for (const ur of unblockedRows.rows) {
|
|
10149
10481
|
try {
|
|
10150
|
-
const ubFile =
|
|
10482
|
+
const ubFile = path25.join(baseDir, String(ur.task_file));
|
|
10151
10483
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
10152
10484
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
10153
10485
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -10182,7 +10514,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
10182
10514
|
const scScope = sessionScopeFilter();
|
|
10183
10515
|
const remaining = await client.execute({
|
|
10184
10516
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
10185
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
10517
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
10186
10518
|
args: [parentTaskId, ...scScope.args]
|
|
10187
10519
|
});
|
|
10188
10520
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -10216,7 +10548,7 @@ var init_tasks_chain = __esm({
|
|
|
10216
10548
|
|
|
10217
10549
|
// src/lib/project-name.ts
|
|
10218
10550
|
import { execSync as execSync7 } from "child_process";
|
|
10219
|
-
import
|
|
10551
|
+
import path26 from "path";
|
|
10220
10552
|
function getProjectName(cwd2) {
|
|
10221
10553
|
const dir = cwd2 ?? process.cwd();
|
|
10222
10554
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -10229,7 +10561,7 @@ function getProjectName(cwd2) {
|
|
|
10229
10561
|
timeout: 2e3,
|
|
10230
10562
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10231
10563
|
}).trim();
|
|
10232
|
-
repoRoot =
|
|
10564
|
+
repoRoot = path26.dirname(gitCommonDir);
|
|
10233
10565
|
} catch {
|
|
10234
10566
|
repoRoot = execSync7("git rev-parse --show-toplevel", {
|
|
10235
10567
|
cwd: dir,
|
|
@@ -10238,11 +10570,11 @@ function getProjectName(cwd2) {
|
|
|
10238
10570
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10239
10571
|
}).trim();
|
|
10240
10572
|
}
|
|
10241
|
-
_cached2 =
|
|
10573
|
+
_cached2 = path26.basename(repoRoot);
|
|
10242
10574
|
_cachedCwd = dir;
|
|
10243
10575
|
return _cached2;
|
|
10244
10576
|
} catch {
|
|
10245
|
-
_cached2 =
|
|
10577
|
+
_cached2 = path26.basename(dir);
|
|
10246
10578
|
_cachedCwd = dir;
|
|
10247
10579
|
return _cached2;
|
|
10248
10580
|
}
|
|
@@ -10385,10 +10717,10 @@ var init_tasks_notify = __esm({
|
|
|
10385
10717
|
});
|
|
10386
10718
|
|
|
10387
10719
|
// src/lib/behaviors.ts
|
|
10388
|
-
import
|
|
10720
|
+
import crypto9 from "crypto";
|
|
10389
10721
|
async function storeBehavior(opts) {
|
|
10390
10722
|
const client = getClient();
|
|
10391
|
-
const id =
|
|
10723
|
+
const id = crypto9.randomUUID();
|
|
10392
10724
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10393
10725
|
await client.execute({
|
|
10394
10726
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -10417,7 +10749,7 @@ __export(skill_learning_exports, {
|
|
|
10417
10749
|
storeTrajectory: () => storeTrajectory,
|
|
10418
10750
|
sweepTrajectories: () => sweepTrajectories
|
|
10419
10751
|
});
|
|
10420
|
-
import
|
|
10752
|
+
import crypto10 from "crypto";
|
|
10421
10753
|
async function extractTrajectory(taskId, agentId) {
|
|
10422
10754
|
const client = getClient();
|
|
10423
10755
|
const result = await client.execute({
|
|
@@ -10446,11 +10778,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
10446
10778
|
return signature;
|
|
10447
10779
|
}
|
|
10448
10780
|
function hashSignature(signature) {
|
|
10449
|
-
return
|
|
10781
|
+
return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
10450
10782
|
}
|
|
10451
10783
|
async function storeTrajectory(opts) {
|
|
10452
10784
|
const client = getClient();
|
|
10453
|
-
const id =
|
|
10785
|
+
const id = crypto10.randomUUID();
|
|
10454
10786
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10455
10787
|
const signatureHash = hashSignature(opts.signature);
|
|
10456
10788
|
await client.execute({
|
|
@@ -10715,8 +11047,8 @@ __export(tasks_exports, {
|
|
|
10715
11047
|
updateTaskStatus: () => updateTaskStatus,
|
|
10716
11048
|
writeCheckpoint: () => writeCheckpoint
|
|
10717
11049
|
});
|
|
10718
|
-
import
|
|
10719
|
-
import { writeFileSync as
|
|
11050
|
+
import path27 from "path";
|
|
11051
|
+
import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
10720
11052
|
async function createTask(input) {
|
|
10721
11053
|
const result = await createTaskCore(input);
|
|
10722
11054
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -10735,12 +11067,12 @@ async function updateTask(input) {
|
|
|
10735
11067
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
10736
11068
|
try {
|
|
10737
11069
|
const agent = String(row.assigned_to);
|
|
10738
|
-
const cacheDir =
|
|
10739
|
-
const cachePath =
|
|
11070
|
+
const cacheDir = path27.join(EXE_AI_DIR, "session-cache");
|
|
11071
|
+
const cachePath = path27.join(cacheDir, `current-task-${agent}.json`);
|
|
10740
11072
|
if (input.status === "in_progress") {
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
11073
|
+
mkdirSync13(cacheDir, { recursive: true });
|
|
11074
|
+
writeFileSync14(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
11075
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
10744
11076
|
try {
|
|
10745
11077
|
unlinkSync7(cachePath);
|
|
10746
11078
|
} catch {
|
|
@@ -10748,10 +11080,10 @@ async function updateTask(input) {
|
|
|
10748
11080
|
}
|
|
10749
11081
|
} catch {
|
|
10750
11082
|
}
|
|
10751
|
-
if (input.status === "done") {
|
|
11083
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10752
11084
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
10753
11085
|
}
|
|
10754
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
11086
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
10755
11087
|
try {
|
|
10756
11088
|
const client = getClient();
|
|
10757
11089
|
const taskTitle = String(row.title);
|
|
@@ -10767,7 +11099,7 @@ async function updateTask(input) {
|
|
|
10767
11099
|
if (!isCoordinatorName(assignedAgent)) {
|
|
10768
11100
|
try {
|
|
10769
11101
|
const draftClient = getClient();
|
|
10770
|
-
if (input.status === "done") {
|
|
11102
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10771
11103
|
await draftClient.execute({
|
|
10772
11104
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
10773
11105
|
args: [assignedAgent]
|
|
@@ -10784,7 +11116,7 @@ async function updateTask(input) {
|
|
|
10784
11116
|
try {
|
|
10785
11117
|
const client = getClient();
|
|
10786
11118
|
const cascaded = await client.execute({
|
|
10787
|
-
sql: `UPDATE tasks SET status = '
|
|
11119
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
10788
11120
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
10789
11121
|
args: [now, taskId]
|
|
10790
11122
|
});
|
|
@@ -10797,14 +11129,14 @@ async function updateTask(input) {
|
|
|
10797
11129
|
} catch {
|
|
10798
11130
|
}
|
|
10799
11131
|
}
|
|
10800
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
11132
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
10801
11133
|
if (isTerminal) {
|
|
10802
11134
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
10803
11135
|
if (!isCoordinator) {
|
|
10804
11136
|
notifyTaskDone();
|
|
10805
11137
|
}
|
|
10806
11138
|
await markTaskNotificationsRead(taskFile);
|
|
10807
|
-
if (input.status === "done") {
|
|
11139
|
+
if (input.status === "done" || input.status === "closed") {
|
|
10808
11140
|
try {
|
|
10809
11141
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
10810
11142
|
} catch {
|
|
@@ -10824,7 +11156,7 @@ async function updateTask(input) {
|
|
|
10824
11156
|
}
|
|
10825
11157
|
}
|
|
10826
11158
|
}
|
|
10827
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
11159
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
10828
11160
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
10829
11161
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
10830
11162
|
taskId,
|
|
@@ -11196,6 +11528,7 @@ __export(tmux_routing_exports, {
|
|
|
11196
11528
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
11197
11529
|
isExeSession: () => isExeSession,
|
|
11198
11530
|
isSessionBusy: () => isSessionBusy,
|
|
11531
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
11199
11532
|
notifyParentExe: () => notifyParentExe,
|
|
11200
11533
|
parseParentExe: () => parseParentExe,
|
|
11201
11534
|
registerParentExe: () => registerParentExe,
|
|
@@ -11206,13 +11539,13 @@ __export(tmux_routing_exports, {
|
|
|
11206
11539
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
11207
11540
|
});
|
|
11208
11541
|
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
11209
|
-
import { readFileSync as
|
|
11210
|
-
import
|
|
11211
|
-
import
|
|
11542
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14, existsSync as existsSync23, appendFileSync as appendFileSync2, readdirSync as readdirSync6 } from "fs";
|
|
11543
|
+
import path28 from "path";
|
|
11544
|
+
import os15 from "os";
|
|
11212
11545
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11213
11546
|
import { unlinkSync as unlinkSync8 } from "fs";
|
|
11214
11547
|
function spawnLockPath(sessionName) {
|
|
11215
|
-
return
|
|
11548
|
+
return path28.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
11216
11549
|
}
|
|
11217
11550
|
function isProcessAlive(pid) {
|
|
11218
11551
|
try {
|
|
@@ -11223,13 +11556,13 @@ function isProcessAlive(pid) {
|
|
|
11223
11556
|
}
|
|
11224
11557
|
}
|
|
11225
11558
|
function acquireSpawnLock2(sessionName) {
|
|
11226
|
-
if (!
|
|
11227
|
-
|
|
11559
|
+
if (!existsSync23(SPAWN_LOCK_DIR)) {
|
|
11560
|
+
mkdirSync14(SPAWN_LOCK_DIR, { recursive: true });
|
|
11228
11561
|
}
|
|
11229
11562
|
const lockFile = spawnLockPath(sessionName);
|
|
11230
|
-
if (
|
|
11563
|
+
if (existsSync23(lockFile)) {
|
|
11231
11564
|
try {
|
|
11232
|
-
const lock = JSON.parse(
|
|
11565
|
+
const lock = JSON.parse(readFileSync19(lockFile, "utf8"));
|
|
11233
11566
|
const age = Date.now() - lock.timestamp;
|
|
11234
11567
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
11235
11568
|
return false;
|
|
@@ -11237,7 +11570,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
11237
11570
|
} catch {
|
|
11238
11571
|
}
|
|
11239
11572
|
}
|
|
11240
|
-
|
|
11573
|
+
writeFileSync15(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
11241
11574
|
return true;
|
|
11242
11575
|
}
|
|
11243
11576
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -11249,13 +11582,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
11249
11582
|
function resolveBehaviorsExporterScript() {
|
|
11250
11583
|
try {
|
|
11251
11584
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
11252
|
-
const scriptPath =
|
|
11253
|
-
|
|
11585
|
+
const scriptPath = path28.join(
|
|
11586
|
+
path28.dirname(thisFile),
|
|
11254
11587
|
"..",
|
|
11255
11588
|
"bin",
|
|
11256
11589
|
"exe-export-behaviors.js"
|
|
11257
11590
|
);
|
|
11258
|
-
return
|
|
11591
|
+
return existsSync23(scriptPath) ? scriptPath : null;
|
|
11259
11592
|
} catch {
|
|
11260
11593
|
return null;
|
|
11261
11594
|
}
|
|
@@ -11321,12 +11654,12 @@ function extractRootExe(name) {
|
|
|
11321
11654
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
11322
11655
|
}
|
|
11323
11656
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
11324
|
-
if (!
|
|
11325
|
-
|
|
11657
|
+
if (!existsSync23(SESSION_CACHE)) {
|
|
11658
|
+
mkdirSync14(SESSION_CACHE, { recursive: true });
|
|
11326
11659
|
}
|
|
11327
11660
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
11328
|
-
const filePath =
|
|
11329
|
-
|
|
11661
|
+
const filePath = path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
11662
|
+
writeFileSync15(filePath, JSON.stringify({
|
|
11330
11663
|
parentExe: rootExe,
|
|
11331
11664
|
dispatchedBy: dispatchedBy || rootExe,
|
|
11332
11665
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11334,7 +11667,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
11334
11667
|
}
|
|
11335
11668
|
function getParentExe(sessionKey) {
|
|
11336
11669
|
try {
|
|
11337
|
-
const data = JSON.parse(
|
|
11670
|
+
const data = JSON.parse(readFileSync19(path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
11338
11671
|
return data.parentExe || null;
|
|
11339
11672
|
} catch {
|
|
11340
11673
|
return null;
|
|
@@ -11342,8 +11675,8 @@ function getParentExe(sessionKey) {
|
|
|
11342
11675
|
}
|
|
11343
11676
|
function getDispatchedBy(sessionKey) {
|
|
11344
11677
|
try {
|
|
11345
|
-
const data = JSON.parse(
|
|
11346
|
-
|
|
11678
|
+
const data = JSON.parse(readFileSync19(
|
|
11679
|
+
path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
11347
11680
|
"utf8"
|
|
11348
11681
|
));
|
|
11349
11682
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -11413,8 +11746,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
11413
11746
|
}
|
|
11414
11747
|
function readDebounceState() {
|
|
11415
11748
|
try {
|
|
11416
|
-
if (!
|
|
11417
|
-
const raw = JSON.parse(
|
|
11749
|
+
if (!existsSync23(DEBOUNCE_FILE)) return {};
|
|
11750
|
+
const raw = JSON.parse(readFileSync19(DEBOUNCE_FILE, "utf8"));
|
|
11418
11751
|
const state = {};
|
|
11419
11752
|
for (const [key, val] of Object.entries(raw)) {
|
|
11420
11753
|
if (typeof val === "number") {
|
|
@@ -11430,8 +11763,8 @@ function readDebounceState() {
|
|
|
11430
11763
|
}
|
|
11431
11764
|
function writeDebounceState(state) {
|
|
11432
11765
|
try {
|
|
11433
|
-
if (!
|
|
11434
|
-
|
|
11766
|
+
if (!existsSync23(SESSION_CACHE)) mkdirSync14(SESSION_CACHE, { recursive: true });
|
|
11767
|
+
writeFileSync15(DEBOUNCE_FILE, JSON.stringify(state));
|
|
11435
11768
|
} catch {
|
|
11436
11769
|
}
|
|
11437
11770
|
}
|
|
@@ -11529,8 +11862,8 @@ function sendIntercom(targetSession) {
|
|
|
11529
11862
|
try {
|
|
11530
11863
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
11531
11864
|
const agent = baseAgentName(rawAgent);
|
|
11532
|
-
const markerPath =
|
|
11533
|
-
if (
|
|
11865
|
+
const markerPath = path28.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
11866
|
+
if (existsSync23(markerPath)) {
|
|
11534
11867
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
11535
11868
|
return "debounced";
|
|
11536
11869
|
}
|
|
@@ -11539,8 +11872,8 @@ function sendIntercom(targetSession) {
|
|
|
11539
11872
|
try {
|
|
11540
11873
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
11541
11874
|
const agent = baseAgentName(rawAgent);
|
|
11542
|
-
const taskDir =
|
|
11543
|
-
if (
|
|
11875
|
+
const taskDir = path28.join(process.cwd(), "exe", agent);
|
|
11876
|
+
if (existsSync23(taskDir)) {
|
|
11544
11877
|
const files = readdirSync6(taskDir).filter(
|
|
11545
11878
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
11546
11879
|
);
|
|
@@ -11600,6 +11933,21 @@ function notifyParentExe(sessionKey) {
|
|
|
11600
11933
|
}
|
|
11601
11934
|
return true;
|
|
11602
11935
|
}
|
|
11936
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
11937
|
+
const transport = getTransport();
|
|
11938
|
+
try {
|
|
11939
|
+
const sessions = transport.listSessions();
|
|
11940
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
11941
|
+
execSync8(
|
|
11942
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
11943
|
+
{ timeout: 3e3 }
|
|
11944
|
+
);
|
|
11945
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
11946
|
+
return true;
|
|
11947
|
+
} catch {
|
|
11948
|
+
return false;
|
|
11949
|
+
}
|
|
11950
|
+
}
|
|
11603
11951
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
11604
11952
|
if (isCoordinatorName(employeeName)) {
|
|
11605
11953
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -11673,26 +12021,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11673
12021
|
const transport = getTransport();
|
|
11674
12022
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
11675
12023
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
11676
|
-
const logDir =
|
|
11677
|
-
const logFile =
|
|
11678
|
-
if (!
|
|
11679
|
-
|
|
12024
|
+
const logDir = path28.join(os15.homedir(), ".exe-os", "session-logs");
|
|
12025
|
+
const logFile = path28.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
12026
|
+
if (!existsSync23(logDir)) {
|
|
12027
|
+
mkdirSync14(logDir, { recursive: true });
|
|
11680
12028
|
}
|
|
11681
12029
|
transport.kill(sessionName);
|
|
11682
12030
|
let cleanupSuffix = "";
|
|
11683
12031
|
try {
|
|
11684
12032
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
11685
|
-
const cleanupScript =
|
|
11686
|
-
if (
|
|
12033
|
+
const cleanupScript = path28.join(path28.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
12034
|
+
if (existsSync23(cleanupScript)) {
|
|
11687
12035
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
11688
12036
|
}
|
|
11689
12037
|
} catch {
|
|
11690
12038
|
}
|
|
11691
12039
|
try {
|
|
11692
|
-
const claudeJsonPath =
|
|
12040
|
+
const claudeJsonPath = path28.join(os15.homedir(), ".claude.json");
|
|
11693
12041
|
let claudeJson = {};
|
|
11694
12042
|
try {
|
|
11695
|
-
claudeJson = JSON.parse(
|
|
12043
|
+
claudeJson = JSON.parse(readFileSync19(claudeJsonPath, "utf8"));
|
|
11696
12044
|
} catch {
|
|
11697
12045
|
}
|
|
11698
12046
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -11700,17 +12048,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11700
12048
|
const trustDir = opts?.cwd ?? projectDir;
|
|
11701
12049
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
11702
12050
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
11703
|
-
|
|
12051
|
+
writeFileSync15(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
11704
12052
|
} catch {
|
|
11705
12053
|
}
|
|
11706
12054
|
try {
|
|
11707
|
-
const settingsDir =
|
|
12055
|
+
const settingsDir = path28.join(os15.homedir(), ".claude", "projects");
|
|
11708
12056
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
11709
|
-
const projSettingsDir =
|
|
11710
|
-
const settingsPath =
|
|
12057
|
+
const projSettingsDir = path28.join(settingsDir, normalizedKey);
|
|
12058
|
+
const settingsPath = path28.join(projSettingsDir, "settings.json");
|
|
11711
12059
|
let settings = {};
|
|
11712
12060
|
try {
|
|
11713
|
-
settings = JSON.parse(
|
|
12061
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
11714
12062
|
} catch {
|
|
11715
12063
|
}
|
|
11716
12064
|
const perms = settings.permissions ?? {};
|
|
@@ -11738,8 +12086,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11738
12086
|
if (changed) {
|
|
11739
12087
|
perms.allow = allow;
|
|
11740
12088
|
settings.permissions = perms;
|
|
11741
|
-
|
|
11742
|
-
|
|
12089
|
+
mkdirSync14(projSettingsDir, { recursive: true });
|
|
12090
|
+
writeFileSync15(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
11743
12091
|
}
|
|
11744
12092
|
} catch {
|
|
11745
12093
|
}
|
|
@@ -11754,8 +12102,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11754
12102
|
let behaviorsFlag = "";
|
|
11755
12103
|
let legacyFallbackWarned = false;
|
|
11756
12104
|
if (!useExeAgent && !useBinSymlink) {
|
|
11757
|
-
const identityPath2 =
|
|
11758
|
-
|
|
12105
|
+
const identityPath2 = path28.join(
|
|
12106
|
+
os15.homedir(),
|
|
11759
12107
|
".exe-os",
|
|
11760
12108
|
"identity",
|
|
11761
12109
|
`${employeeName}.md`
|
|
@@ -11764,13 +12112,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11764
12112
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
11765
12113
|
if (hasAgentFlag) {
|
|
11766
12114
|
identityFlag = ` --agent ${employeeName}`;
|
|
11767
|
-
} else if (
|
|
12115
|
+
} else if (existsSync23(identityPath2)) {
|
|
11768
12116
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
11769
12117
|
legacyFallbackWarned = true;
|
|
11770
12118
|
}
|
|
11771
12119
|
const behaviorsFile = exportBehaviorsSync(
|
|
11772
12120
|
employeeName,
|
|
11773
|
-
|
|
12121
|
+
path28.basename(spawnCwd),
|
|
11774
12122
|
sessionName
|
|
11775
12123
|
);
|
|
11776
12124
|
if (behaviorsFile) {
|
|
@@ -11785,16 +12133,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11785
12133
|
}
|
|
11786
12134
|
let sessionContextFlag = "";
|
|
11787
12135
|
try {
|
|
11788
|
-
const ctxDir =
|
|
11789
|
-
|
|
11790
|
-
const ctxFile =
|
|
12136
|
+
const ctxDir = path28.join(os15.homedir(), ".exe-os", "session-cache");
|
|
12137
|
+
mkdirSync14(ctxDir, { recursive: true });
|
|
12138
|
+
const ctxFile = path28.join(ctxDir, `session-context-${sessionName}.md`);
|
|
11791
12139
|
const ctxContent = [
|
|
11792
12140
|
`## Session Context`,
|
|
11793
12141
|
`You are running in tmux session: ${sessionName}.`,
|
|
11794
12142
|
`Your parent coordinator session is ${exeSession}.`,
|
|
11795
12143
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
11796
12144
|
].join("\n");
|
|
11797
|
-
|
|
12145
|
+
writeFileSync15(ctxFile, ctxContent);
|
|
11798
12146
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
11799
12147
|
} catch {
|
|
11800
12148
|
}
|
|
@@ -11871,8 +12219,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11871
12219
|
transport.pipeLog(sessionName, logFile);
|
|
11872
12220
|
try {
|
|
11873
12221
|
const mySession = getMySession();
|
|
11874
|
-
const dispatchInfo =
|
|
11875
|
-
|
|
12222
|
+
const dispatchInfo = path28.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
12223
|
+
writeFileSync15(dispatchInfo, JSON.stringify({
|
|
11876
12224
|
dispatchedBy: mySession,
|
|
11877
12225
|
rootExe: exeSession,
|
|
11878
12226
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -11946,15 +12294,15 @@ var init_tmux_routing = __esm({
|
|
|
11946
12294
|
init_intercom_queue();
|
|
11947
12295
|
init_plan_limits();
|
|
11948
12296
|
init_employees();
|
|
11949
|
-
SPAWN_LOCK_DIR =
|
|
11950
|
-
SESSION_CACHE =
|
|
12297
|
+
SPAWN_LOCK_DIR = path28.join(os15.homedir(), ".exe-os", "spawn-locks");
|
|
12298
|
+
SESSION_CACHE = path28.join(os15.homedir(), ".exe-os", "session-cache");
|
|
11951
12299
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
11952
12300
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
11953
12301
|
VERIFY_PANE_LINES = 200;
|
|
11954
12302
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
11955
12303
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
11956
|
-
INTERCOM_LOG2 =
|
|
11957
|
-
DEBOUNCE_FILE =
|
|
12304
|
+
INTERCOM_LOG2 = path28.join(os15.homedir(), ".exe-os", "intercom.log");
|
|
12305
|
+
DEBOUNCE_FILE = path28.join(SESSION_CACHE, "intercom-debounce.json");
|
|
11958
12306
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
11959
12307
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
11960
12308
|
}
|
|
@@ -11977,10 +12325,10 @@ __export(messaging_exports, {
|
|
|
11977
12325
|
sendMessage: () => sendMessage,
|
|
11978
12326
|
setWsClientSend: () => setWsClientSend
|
|
11979
12327
|
});
|
|
11980
|
-
import
|
|
12328
|
+
import crypto11 from "crypto";
|
|
11981
12329
|
function generateUlid() {
|
|
11982
12330
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
11983
|
-
const random =
|
|
12331
|
+
const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
|
|
11984
12332
|
return (timestamp + random).toUpperCase();
|
|
11985
12333
|
}
|
|
11986
12334
|
function rowToMessage(row) {
|
|
@@ -11991,6 +12339,7 @@ function rowToMessage(row) {
|
|
|
11991
12339
|
targetAgent: row.target_agent,
|
|
11992
12340
|
targetProject: row.target_project ?? null,
|
|
11993
12341
|
targetDevice: row.target_device,
|
|
12342
|
+
sessionScope: row.session_scope ?? null,
|
|
11994
12343
|
content: row.content,
|
|
11995
12344
|
priority: row.priority ?? "normal",
|
|
11996
12345
|
status: row.status ?? "pending",
|
|
@@ -12008,15 +12357,17 @@ async function sendMessage(input) {
|
|
|
12008
12357
|
const id = generateUlid();
|
|
12009
12358
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12010
12359
|
const targetDevice = input.targetDevice ?? "local";
|
|
12360
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
12011
12361
|
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', ?)`,
|
|
12362
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
12363
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
12014
12364
|
args: [
|
|
12015
12365
|
id,
|
|
12016
12366
|
input.fromAgent,
|
|
12017
12367
|
input.targetAgent,
|
|
12018
12368
|
input.targetProject ?? null,
|
|
12019
12369
|
targetDevice,
|
|
12370
|
+
sessionScope,
|
|
12020
12371
|
input.content,
|
|
12021
12372
|
input.priority ?? "normal",
|
|
12022
12373
|
now
|
|
@@ -12030,9 +12381,10 @@ async function sendMessage(input) {
|
|
|
12030
12381
|
}
|
|
12031
12382
|
} catch {
|
|
12032
12383
|
}
|
|
12384
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
12033
12385
|
const result = await client.execute({
|
|
12034
|
-
sql:
|
|
12035
|
-
args: [id]
|
|
12386
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
12387
|
+
args: [id, ...sentScope.args]
|
|
12036
12388
|
});
|
|
12037
12389
|
return rowToMessage(result.rows[0]);
|
|
12038
12390
|
}
|
|
@@ -12056,6 +12408,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
12056
12408
|
fromAgent: msg.fromAgent,
|
|
12057
12409
|
targetAgent: msg.targetAgent,
|
|
12058
12410
|
targetProject: msg.targetProject,
|
|
12411
|
+
sessionScope: msg.sessionScope,
|
|
12059
12412
|
content: msg.content,
|
|
12060
12413
|
priority: msg.priority,
|
|
12061
12414
|
createdAt: msg.createdAt
|
|
@@ -12099,7 +12452,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
12099
12452
|
} catch {
|
|
12100
12453
|
const newRetryCount = msg.retryCount + 1;
|
|
12101
12454
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
12102
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
12455
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
12103
12456
|
} else {
|
|
12104
12457
|
await client.execute({
|
|
12105
12458
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -12109,85 +12462,101 @@ async function deliverLocalMessage(messageId) {
|
|
|
12109
12462
|
return false;
|
|
12110
12463
|
}
|
|
12111
12464
|
}
|
|
12112
|
-
async function getPendingMessages(targetAgent) {
|
|
12465
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
12113
12466
|
const client = getClient();
|
|
12467
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12114
12468
|
const result = await client.execute({
|
|
12115
12469
|
sql: `SELECT * FROM messages
|
|
12116
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
12470
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
12117
12471
|
ORDER BY id`,
|
|
12118
|
-
args: [targetAgent]
|
|
12472
|
+
args: [targetAgent, ...scope.args]
|
|
12119
12473
|
});
|
|
12120
12474
|
return result.rows.map((row) => rowToMessage(row));
|
|
12121
12475
|
}
|
|
12122
|
-
async function markRead(messageId) {
|
|
12476
|
+
async function markRead(messageId, sessionScope) {
|
|
12123
12477
|
const client = getClient();
|
|
12478
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12124
12479
|
await client.execute({
|
|
12125
|
-
sql:
|
|
12126
|
-
|
|
12480
|
+
sql: `UPDATE messages SET status = 'read'
|
|
12481
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
12482
|
+
args: [messageId, ...scope.args]
|
|
12127
12483
|
});
|
|
12128
12484
|
}
|
|
12129
|
-
async function markAcknowledged(messageId) {
|
|
12485
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
12130
12486
|
const client = getClient();
|
|
12487
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12131
12488
|
await client.execute({
|
|
12132
|
-
sql:
|
|
12133
|
-
|
|
12489
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
12490
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
12491
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
12134
12492
|
});
|
|
12135
12493
|
}
|
|
12136
|
-
async function markProcessed(messageId) {
|
|
12494
|
+
async function markProcessed(messageId, sessionScope) {
|
|
12137
12495
|
const client = getClient();
|
|
12496
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12138
12497
|
await client.execute({
|
|
12139
|
-
sql:
|
|
12140
|
-
|
|
12498
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
12499
|
+
WHERE id = ?${scope.sql}`,
|
|
12500
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
12141
12501
|
});
|
|
12142
12502
|
}
|
|
12143
|
-
async function getMessageStatus(messageId) {
|
|
12503
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
12144
12504
|
const client = getClient();
|
|
12505
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12145
12506
|
const result = await client.execute({
|
|
12146
|
-
sql:
|
|
12147
|
-
args: [messageId]
|
|
12507
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
12508
|
+
args: [messageId, ...scope.args]
|
|
12148
12509
|
});
|
|
12149
12510
|
return result.rows[0]?.status ?? null;
|
|
12150
12511
|
}
|
|
12151
|
-
async function getUnacknowledgedMessages(targetAgent) {
|
|
12512
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
12152
12513
|
const client = getClient();
|
|
12514
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12153
12515
|
const result = await client.execute({
|
|
12154
12516
|
sql: `SELECT * FROM messages
|
|
12155
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
|
|
12517
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
12156
12518
|
ORDER BY id`,
|
|
12157
|
-
args: [targetAgent]
|
|
12519
|
+
args: [targetAgent, ...scope.args]
|
|
12158
12520
|
});
|
|
12159
12521
|
return result.rows.map((row) => rowToMessage(row));
|
|
12160
12522
|
}
|
|
12161
|
-
async function getReadMessages(targetAgent) {
|
|
12523
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
12162
12524
|
const client = getClient();
|
|
12525
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12163
12526
|
const result = await client.execute({
|
|
12164
|
-
sql:
|
|
12165
|
-
|
|
12527
|
+
sql: `SELECT * FROM messages
|
|
12528
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
12529
|
+
ORDER BY id`,
|
|
12530
|
+
args: [targetAgent, ...scope.args]
|
|
12166
12531
|
});
|
|
12167
12532
|
return result.rows.map((row) => rowToMessage(row));
|
|
12168
12533
|
}
|
|
12169
|
-
async function markFailed(messageId, reason) {
|
|
12534
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
12170
12535
|
const client = getClient();
|
|
12536
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12171
12537
|
await client.execute({
|
|
12172
|
-
sql:
|
|
12173
|
-
|
|
12538
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
12539
|
+
WHERE id = ?${scope.sql}`,
|
|
12540
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
12174
12541
|
});
|
|
12175
12542
|
}
|
|
12176
|
-
async function getFailedMessages() {
|
|
12543
|
+
async function getFailedMessages(sessionScope) {
|
|
12177
12544
|
const client = getClient();
|
|
12545
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12178
12546
|
const result = await client.execute({
|
|
12179
|
-
sql:
|
|
12180
|
-
args: []
|
|
12547
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
12548
|
+
args: [...scope.args]
|
|
12181
12549
|
});
|
|
12182
12550
|
return result.rows.map((row) => rowToMessage(row));
|
|
12183
12551
|
}
|
|
12184
|
-
async function retryPendingMessages() {
|
|
12552
|
+
async function retryPendingMessages(sessionScope) {
|
|
12185
12553
|
const client = getClient();
|
|
12554
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
12186
12555
|
const result = await client.execute({
|
|
12187
12556
|
sql: `SELECT * FROM messages
|
|
12188
|
-
WHERE status = 'pending' AND retry_count <
|
|
12557
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
12189
12558
|
ORDER BY id`,
|
|
12190
|
-
args: [MAX_RETRIES3]
|
|
12559
|
+
args: [MAX_RETRIES3, ...scope.args]
|
|
12191
12560
|
});
|
|
12192
12561
|
let delivered = 0;
|
|
12193
12562
|
for (const row of result.rows) {
|
|
@@ -12206,6 +12575,7 @@ var init_messaging = __esm({
|
|
|
12206
12575
|
"use strict";
|
|
12207
12576
|
init_database();
|
|
12208
12577
|
init_tmux_routing();
|
|
12578
|
+
init_task_scope();
|
|
12209
12579
|
MAX_RETRIES3 = 10;
|
|
12210
12580
|
_wsClientSend = null;
|
|
12211
12581
|
}
|
|
@@ -12221,9 +12591,9 @@ __export(active_agent_exports, {
|
|
|
12221
12591
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
12222
12592
|
writeActiveAgent: () => writeActiveAgent
|
|
12223
12593
|
});
|
|
12224
|
-
import { readFileSync as
|
|
12594
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9, readdirSync as readdirSync7 } from "fs";
|
|
12225
12595
|
import { execSync as execSync9 } from "child_process";
|
|
12226
|
-
import
|
|
12596
|
+
import path29 from "path";
|
|
12227
12597
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
12228
12598
|
if (candidate === baseName) return true;
|
|
12229
12599
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -12267,12 +12637,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
12267
12637
|
return null;
|
|
12268
12638
|
}
|
|
12269
12639
|
function getMarkerPath() {
|
|
12270
|
-
return
|
|
12640
|
+
return path29.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
12271
12641
|
}
|
|
12272
12642
|
function writeActiveAgent(agentId, agentRole) {
|
|
12273
12643
|
try {
|
|
12274
|
-
|
|
12275
|
-
|
|
12644
|
+
mkdirSync15(CACHE_DIR, { recursive: true });
|
|
12645
|
+
writeFileSync16(
|
|
12276
12646
|
getMarkerPath(),
|
|
12277
12647
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
12278
12648
|
);
|
|
@@ -12288,7 +12658,7 @@ function clearActiveAgent() {
|
|
|
12288
12658
|
function getActiveAgent() {
|
|
12289
12659
|
try {
|
|
12290
12660
|
const markerPath = getMarkerPath();
|
|
12291
|
-
const raw =
|
|
12661
|
+
const raw = readFileSync20(markerPath, "utf8");
|
|
12292
12662
|
const data = JSON.parse(raw);
|
|
12293
12663
|
if (data.agentId) {
|
|
12294
12664
|
if (data.startedAt) {
|
|
@@ -12336,14 +12706,14 @@ function getAllActiveAgents() {
|
|
|
12336
12706
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
12337
12707
|
if (key === "undefined") continue;
|
|
12338
12708
|
try {
|
|
12339
|
-
const raw =
|
|
12709
|
+
const raw = readFileSync20(path29.join(CACHE_DIR, file), "utf8");
|
|
12340
12710
|
const data = JSON.parse(raw);
|
|
12341
12711
|
if (!data.agentId) continue;
|
|
12342
12712
|
if (data.startedAt) {
|
|
12343
12713
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
12344
12714
|
if (age > STALE_MS) {
|
|
12345
12715
|
try {
|
|
12346
|
-
unlinkSync9(
|
|
12716
|
+
unlinkSync9(path29.join(CACHE_DIR, file));
|
|
12347
12717
|
} catch {
|
|
12348
12718
|
}
|
|
12349
12719
|
continue;
|
|
@@ -12366,11 +12736,11 @@ function getAllActiveAgents() {
|
|
|
12366
12736
|
function cleanupSessionMarkers() {
|
|
12367
12737
|
const key = getSessionKey();
|
|
12368
12738
|
try {
|
|
12369
|
-
unlinkSync9(
|
|
12739
|
+
unlinkSync9(path29.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
12370
12740
|
} catch {
|
|
12371
12741
|
}
|
|
12372
12742
|
try {
|
|
12373
|
-
unlinkSync9(
|
|
12743
|
+
unlinkSync9(path29.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
12374
12744
|
} catch {
|
|
12375
12745
|
}
|
|
12376
12746
|
}
|
|
@@ -12381,7 +12751,7 @@ var init_active_agent = __esm({
|
|
|
12381
12751
|
init_config();
|
|
12382
12752
|
init_session_key();
|
|
12383
12753
|
init_employees();
|
|
12384
|
-
CACHE_DIR =
|
|
12754
|
+
CACHE_DIR = path29.join(EXE_AI_DIR, "session-cache");
|
|
12385
12755
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
12386
12756
|
}
|
|
12387
12757
|
});
|
|
@@ -13009,14 +13379,14 @@ __export(exe_rename_exports, {
|
|
|
13009
13379
|
main: () => main2,
|
|
13010
13380
|
renameEmployee: () => renameEmployee
|
|
13011
13381
|
});
|
|
13012
|
-
import { readFileSync as
|
|
13382
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as renameSync4, unlinkSync as unlinkSync10, existsSync as existsSync24 } from "fs";
|
|
13013
13383
|
import { execSync as execSync10 } from "child_process";
|
|
13014
|
-
import
|
|
13384
|
+
import path30 from "path";
|
|
13015
13385
|
import { homedir as homedir4 } from "os";
|
|
13016
13386
|
async function renameEmployee(oldName, newName, opts = {}) {
|
|
13017
|
-
const rosterPath = opts.rosterPath ??
|
|
13018
|
-
const identityDir = opts.identityDir ??
|
|
13019
|
-
const agentsDir = opts.agentsDir ??
|
|
13387
|
+
const rosterPath = opts.rosterPath ?? path30.join(homedir4(), ".exe-os", "exe-employees.json");
|
|
13388
|
+
const identityDir = opts.identityDir ?? path30.join(homedir4(), ".exe-os", "identity");
|
|
13389
|
+
const agentsDir = opts.agentsDir ?? path30.join(homedir4(), ".claude", "agents");
|
|
13020
13390
|
const validation = validateEmployeeName(newName);
|
|
13021
13391
|
if (!validation.valid) {
|
|
13022
13392
|
return { success: false, error: validation.error };
|
|
@@ -13045,40 +13415,40 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
13045
13415
|
undo: () => {
|
|
13046
13416
|
employee.name = originalName;
|
|
13047
13417
|
employee.systemPrompt = originalPrompt;
|
|
13048
|
-
|
|
13418
|
+
writeFileSync17(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
13049
13419
|
}
|
|
13050
13420
|
});
|
|
13051
|
-
const oldIdentityPath =
|
|
13052
|
-
const newIdentityPath =
|
|
13053
|
-
if (
|
|
13054
|
-
const content =
|
|
13421
|
+
const oldIdentityPath = path30.join(identityDir, `${rosterOldName}.md`);
|
|
13422
|
+
const newIdentityPath = path30.join(identityDir, `${newName}.md`);
|
|
13423
|
+
if (existsSync24(oldIdentityPath)) {
|
|
13424
|
+
const content = readFileSync21(oldIdentityPath, "utf-8");
|
|
13055
13425
|
const updatedContent = content.replace(
|
|
13056
13426
|
/^(agent_id:\s*)\S+/m,
|
|
13057
13427
|
`$1${newName}`
|
|
13058
13428
|
);
|
|
13059
13429
|
renameSync4(oldIdentityPath, newIdentityPath);
|
|
13060
|
-
|
|
13430
|
+
writeFileSync17(newIdentityPath, updatedContent, "utf-8");
|
|
13061
13431
|
rollbackStack.push({
|
|
13062
13432
|
description: "restore identity file",
|
|
13063
13433
|
undo: () => {
|
|
13064
|
-
if (
|
|
13065
|
-
|
|
13434
|
+
if (existsSync24(newIdentityPath)) {
|
|
13435
|
+
writeFileSync17(newIdentityPath, content, "utf-8");
|
|
13066
13436
|
renameSync4(newIdentityPath, oldIdentityPath);
|
|
13067
13437
|
}
|
|
13068
13438
|
}
|
|
13069
13439
|
});
|
|
13070
13440
|
}
|
|
13071
|
-
const oldAgentPath =
|
|
13072
|
-
const newAgentPath =
|
|
13073
|
-
if (
|
|
13074
|
-
const agentContent =
|
|
13441
|
+
const oldAgentPath = path30.join(agentsDir, `${rosterOldName}.md`);
|
|
13442
|
+
const newAgentPath = path30.join(agentsDir, `${newName}.md`);
|
|
13443
|
+
if (existsSync24(oldAgentPath)) {
|
|
13444
|
+
const agentContent = readFileSync21(oldAgentPath, "utf-8");
|
|
13075
13445
|
renameSync4(oldAgentPath, newAgentPath);
|
|
13076
13446
|
rollbackStack.push({
|
|
13077
13447
|
description: "restore agent file",
|
|
13078
13448
|
undo: () => {
|
|
13079
|
-
if (
|
|
13449
|
+
if (existsSync24(newAgentPath)) {
|
|
13080
13450
|
renameSync4(newAgentPath, oldAgentPath);
|
|
13081
|
-
|
|
13451
|
+
writeFileSync17(oldAgentPath, agentContent, "utf-8");
|
|
13082
13452
|
}
|
|
13083
13453
|
}
|
|
13084
13454
|
});
|
|
@@ -13156,10 +13526,10 @@ function removeOldSymlinks(name) {
|
|
|
13156
13526
|
try {
|
|
13157
13527
|
const exeBinPath = findExeBin2();
|
|
13158
13528
|
if (!exeBinPath) return;
|
|
13159
|
-
const binDir =
|
|
13529
|
+
const binDir = path30.dirname(exeBinPath);
|
|
13160
13530
|
for (const suffix of ["", "-opencode"]) {
|
|
13161
|
-
const linkPath =
|
|
13162
|
-
if (
|
|
13531
|
+
const linkPath = path30.join(binDir, `${name}${suffix}`);
|
|
13532
|
+
if (existsSync24(linkPath)) {
|
|
13163
13533
|
try {
|
|
13164
13534
|
unlinkSync10(linkPath);
|
|
13165
13535
|
} catch {
|
|
@@ -13204,16 +13574,16 @@ var init_exe_rename = __esm({
|
|
|
13204
13574
|
});
|
|
13205
13575
|
|
|
13206
13576
|
// src/lib/model-downloader.ts
|
|
13207
|
-
import { createWriteStream, createReadStream as createReadStream2, existsSync as
|
|
13577
|
+
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync25, unlinkSync as unlinkSync11, renameSync as renameSync5 } from "fs";
|
|
13208
13578
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
13209
13579
|
import { createHash as createHash3 } from "crypto";
|
|
13210
|
-
import
|
|
13580
|
+
import path31 from "path";
|
|
13211
13581
|
async function downloadModel(opts) {
|
|
13212
13582
|
const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
|
|
13213
|
-
const destPath =
|
|
13583
|
+
const destPath = path31.join(destDir, LOCAL_FILENAME);
|
|
13214
13584
|
const tmpPath = destPath + ".tmp";
|
|
13215
13585
|
await mkdir6(destDir, { recursive: true });
|
|
13216
|
-
if (
|
|
13586
|
+
if (existsSync25(destPath)) {
|
|
13217
13587
|
const hash = await fileHash(destPath);
|
|
13218
13588
|
if (hash === EXPECTED_SHA256) {
|
|
13219
13589
|
return destPath;
|
|
@@ -13225,7 +13595,7 @@ async function downloadModel(opts) {
|
|
|
13225
13595
|
let downloaded = 0;
|
|
13226
13596
|
for (let attempt = 1; attempt <= MAX_RETRIES4; attempt++) {
|
|
13227
13597
|
try {
|
|
13228
|
-
if (
|
|
13598
|
+
if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
|
|
13229
13599
|
const response = await fetchFn(GGUF_URL, {
|
|
13230
13600
|
redirect: "follow",
|
|
13231
13601
|
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
@@ -13270,7 +13640,7 @@ async function downloadModel(opts) {
|
|
|
13270
13640
|
process.stderr.write(`
|
|
13271
13641
|
Download attempt ${attempt} failed, retrying...
|
|
13272
13642
|
`);
|
|
13273
|
-
if (
|
|
13643
|
+
if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
|
|
13274
13644
|
}
|
|
13275
13645
|
}
|
|
13276
13646
|
}
|
|
@@ -13333,10 +13703,10 @@ async function disposeEmbedder() {
|
|
|
13333
13703
|
async function embedDirect(text) {
|
|
13334
13704
|
const llamaCpp = await import("node-llama-cpp");
|
|
13335
13705
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
13336
|
-
const { existsSync:
|
|
13337
|
-
const
|
|
13338
|
-
const modelPath =
|
|
13339
|
-
if (!
|
|
13706
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
13707
|
+
const path46 = await import("path");
|
|
13708
|
+
const modelPath = path46.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
13709
|
+
if (!existsSync32(modelPath)) {
|
|
13340
13710
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
13341
13711
|
}
|
|
13342
13712
|
const llama = await llamaCpp.getLlama();
|
|
@@ -13911,36 +14281,36 @@ __export(session_wrappers_exports, {
|
|
|
13911
14281
|
generateSessionWrappers: () => generateSessionWrappers
|
|
13912
14282
|
});
|
|
13913
14283
|
import {
|
|
13914
|
-
existsSync as
|
|
13915
|
-
readFileSync as
|
|
13916
|
-
writeFileSync as
|
|
13917
|
-
mkdirSync as
|
|
13918
|
-
chmodSync,
|
|
14284
|
+
existsSync as existsSync26,
|
|
14285
|
+
readFileSync as readFileSync22,
|
|
14286
|
+
writeFileSync as writeFileSync18,
|
|
14287
|
+
mkdirSync as mkdirSync16,
|
|
14288
|
+
chmodSync as chmodSync2,
|
|
13919
14289
|
readdirSync as readdirSync8,
|
|
13920
14290
|
unlinkSync as unlinkSync12
|
|
13921
14291
|
} from "fs";
|
|
13922
|
-
import
|
|
14292
|
+
import path32 from "path";
|
|
13923
14293
|
import { homedir as homedir5 } from "os";
|
|
13924
14294
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
13925
14295
|
const home = homeDir ?? homedir5();
|
|
13926
|
-
const binDir =
|
|
13927
|
-
const rosterPath =
|
|
13928
|
-
|
|
13929
|
-
const exeStartDst =
|
|
14296
|
+
const binDir = path32.join(home, ".exe-os", "bin");
|
|
14297
|
+
const rosterPath = path32.join(home, ".exe-os", "exe-employees.json");
|
|
14298
|
+
mkdirSync16(binDir, { recursive: true });
|
|
14299
|
+
const exeStartDst = path32.join(binDir, "exe-start");
|
|
13930
14300
|
const candidates = [
|
|
13931
|
-
|
|
13932
|
-
|
|
14301
|
+
path32.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
14302
|
+
path32.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
13933
14303
|
];
|
|
13934
14304
|
for (const src of candidates) {
|
|
13935
|
-
if (
|
|
13936
|
-
|
|
13937
|
-
|
|
14305
|
+
if (existsSync26(src)) {
|
|
14306
|
+
writeFileSync18(exeStartDst, readFileSync22(src));
|
|
14307
|
+
chmodSync2(exeStartDst, 493);
|
|
13938
14308
|
break;
|
|
13939
14309
|
}
|
|
13940
14310
|
}
|
|
13941
14311
|
let employees = [];
|
|
13942
14312
|
try {
|
|
13943
|
-
employees = JSON.parse(
|
|
14313
|
+
employees = JSON.parse(readFileSync22(rosterPath, "utf8"));
|
|
13944
14314
|
} catch {
|
|
13945
14315
|
return { created: 0, pathConfigured: false };
|
|
13946
14316
|
}
|
|
@@ -13950,9 +14320,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
13950
14320
|
try {
|
|
13951
14321
|
for (const f of readdirSync8(binDir)) {
|
|
13952
14322
|
if (f === "exe-start") continue;
|
|
13953
|
-
const fPath =
|
|
14323
|
+
const fPath = path32.join(binDir, f);
|
|
13954
14324
|
try {
|
|
13955
|
-
const content =
|
|
14325
|
+
const content = readFileSync22(fPath, "utf8");
|
|
13956
14326
|
if (content.includes("exe-start")) {
|
|
13957
14327
|
unlinkSync12(fPath);
|
|
13958
14328
|
}
|
|
@@ -13967,31 +14337,31 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
13967
14337
|
`;
|
|
13968
14338
|
for (const emp of employees) {
|
|
13969
14339
|
for (let n = 1; n <= MAX_N; n++) {
|
|
13970
|
-
const wrapperPath =
|
|
13971
|
-
|
|
13972
|
-
|
|
14340
|
+
const wrapperPath = path32.join(binDir, `${emp.name}${n}`);
|
|
14341
|
+
writeFileSync18(wrapperPath, wrapperContent);
|
|
14342
|
+
chmodSync2(wrapperPath, 493);
|
|
13973
14343
|
created++;
|
|
13974
14344
|
}
|
|
13975
14345
|
}
|
|
13976
14346
|
const codexLauncherCandidates = [
|
|
13977
|
-
|
|
13978
|
-
|
|
14347
|
+
path32.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
14348
|
+
path32.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
13979
14349
|
];
|
|
13980
14350
|
let codexLauncher = null;
|
|
13981
14351
|
for (const c of codexLauncherCandidates) {
|
|
13982
|
-
if (
|
|
14352
|
+
if (existsSync26(c)) {
|
|
13983
14353
|
codexLauncher = c;
|
|
13984
14354
|
break;
|
|
13985
14355
|
}
|
|
13986
14356
|
}
|
|
13987
14357
|
if (codexLauncher) {
|
|
13988
14358
|
for (const emp of employees) {
|
|
13989
|
-
const wrapperPath =
|
|
14359
|
+
const wrapperPath = path32.join(binDir, `${emp.name}-codex`);
|
|
13990
14360
|
const content = `#!/bin/bash
|
|
13991
14361
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
13992
14362
|
`;
|
|
13993
|
-
|
|
13994
|
-
|
|
14363
|
+
writeFileSync18(wrapperPath, content);
|
|
14364
|
+
chmodSync2(wrapperPath, 493);
|
|
13995
14365
|
created++;
|
|
13996
14366
|
}
|
|
13997
14367
|
}
|
|
@@ -14009,24 +14379,24 @@ export PATH="${binDir}:$PATH"
|
|
|
14009
14379
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
14010
14380
|
const profilePaths = [];
|
|
14011
14381
|
if (shell.includes("zsh")) {
|
|
14012
|
-
profilePaths.push(
|
|
14382
|
+
profilePaths.push(path32.join(home, ".zshrc"));
|
|
14013
14383
|
} else if (shell.includes("bash")) {
|
|
14014
|
-
profilePaths.push(
|
|
14015
|
-
profilePaths.push(
|
|
14384
|
+
profilePaths.push(path32.join(home, ".bashrc"));
|
|
14385
|
+
profilePaths.push(path32.join(home, ".bash_profile"));
|
|
14016
14386
|
} else {
|
|
14017
|
-
profilePaths.push(
|
|
14387
|
+
profilePaths.push(path32.join(home, ".profile"));
|
|
14018
14388
|
}
|
|
14019
14389
|
for (const profilePath of profilePaths) {
|
|
14020
14390
|
try {
|
|
14021
14391
|
let content = "";
|
|
14022
14392
|
try {
|
|
14023
|
-
content =
|
|
14393
|
+
content = readFileSync22(profilePath, "utf8");
|
|
14024
14394
|
} catch {
|
|
14025
14395
|
}
|
|
14026
14396
|
if (content.includes(".exe-os/bin")) {
|
|
14027
14397
|
return false;
|
|
14028
14398
|
}
|
|
14029
|
-
|
|
14399
|
+
writeFileSync18(profilePath, content + exportLine);
|
|
14030
14400
|
return true;
|
|
14031
14401
|
} catch {
|
|
14032
14402
|
continue;
|
|
@@ -14048,37 +14418,37 @@ __export(setup_wizard_exports, {
|
|
|
14048
14418
|
runSetupWizard: () => runSetupWizard,
|
|
14049
14419
|
validateModel: () => validateModel
|
|
14050
14420
|
});
|
|
14051
|
-
import
|
|
14052
|
-
import { existsSync as
|
|
14053
|
-
import
|
|
14054
|
-
import
|
|
14421
|
+
import crypto12 from "crypto";
|
|
14422
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync19, unlinkSync as unlinkSync13 } from "fs";
|
|
14423
|
+
import os16 from "os";
|
|
14424
|
+
import path33 from "path";
|
|
14055
14425
|
import { createInterface as createInterface3 } from "readline";
|
|
14056
14426
|
function findPackageRoot2() {
|
|
14057
|
-
let dir =
|
|
14058
|
-
const root =
|
|
14427
|
+
let dir = path33.dirname(new URL(import.meta.url).pathname);
|
|
14428
|
+
const root = path33.parse(dir).root;
|
|
14059
14429
|
while (dir !== root) {
|
|
14060
|
-
const pkgPath =
|
|
14061
|
-
if (
|
|
14430
|
+
const pkgPath = path33.join(dir, "package.json");
|
|
14431
|
+
if (existsSync27(pkgPath)) {
|
|
14062
14432
|
try {
|
|
14063
|
-
const pkg = JSON.parse(
|
|
14433
|
+
const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
|
|
14064
14434
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
14065
14435
|
} catch {
|
|
14066
14436
|
}
|
|
14067
14437
|
}
|
|
14068
|
-
dir =
|
|
14438
|
+
dir = path33.dirname(dir);
|
|
14069
14439
|
}
|
|
14070
14440
|
return null;
|
|
14071
14441
|
}
|
|
14072
14442
|
function loadSetupState() {
|
|
14073
14443
|
try {
|
|
14074
|
-
return JSON.parse(
|
|
14444
|
+
return JSON.parse(readFileSync23(SETUP_STATE_PATH, "utf8"));
|
|
14075
14445
|
} catch {
|
|
14076
14446
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
14077
14447
|
}
|
|
14078
14448
|
}
|
|
14079
14449
|
function saveSetupState(state) {
|
|
14080
|
-
|
|
14081
|
-
|
|
14450
|
+
mkdirSync17(path33.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
14451
|
+
writeFileSync19(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
14082
14452
|
}
|
|
14083
14453
|
function clearSetupState() {
|
|
14084
14454
|
try {
|
|
@@ -14097,10 +14467,10 @@ function ask2(rl, prompt) {
|
|
|
14097
14467
|
});
|
|
14098
14468
|
}
|
|
14099
14469
|
function getAvailableMemoryGB() {
|
|
14100
|
-
return
|
|
14470
|
+
return os16.freemem() / (1024 * 1024 * 1024);
|
|
14101
14471
|
}
|
|
14102
14472
|
function getTotalMemoryGB() {
|
|
14103
|
-
return
|
|
14473
|
+
return os16.totalmem() / (1024 * 1024 * 1024);
|
|
14104
14474
|
}
|
|
14105
14475
|
function isLowMemory() {
|
|
14106
14476
|
return getAvailableMemoryGB() < 2;
|
|
@@ -14111,8 +14481,8 @@ async function validateModel(log) {
|
|
|
14111
14481
|
if (totalGB <= 8 || isLowMemory()) {
|
|
14112
14482
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
14113
14483
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
14114
|
-
const modelPath =
|
|
14115
|
-
if (
|
|
14484
|
+
const modelPath = path33.join(MODELS_DIR, LOCAL_FILENAME);
|
|
14485
|
+
if (existsSync27(modelPath)) {
|
|
14116
14486
|
const { statSync: statSync2 } = await import("fs");
|
|
14117
14487
|
const size = statSync2(modelPath).size;
|
|
14118
14488
|
if (size > 300 * 1e6) {
|
|
@@ -14203,7 +14573,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14203
14573
|
if (state.completedSteps.length > 0) {
|
|
14204
14574
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
14205
14575
|
}
|
|
14206
|
-
if (
|
|
14576
|
+
if (existsSync27(LEGACY_LANCE_PATH)) {
|
|
14207
14577
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
14208
14578
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
14209
14579
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -14215,7 +14585,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14215
14585
|
log("Encryption key already exists \u2014 skipping generation.");
|
|
14216
14586
|
} else {
|
|
14217
14587
|
log("Generating 256-bit encryption key...");
|
|
14218
|
-
const key =
|
|
14588
|
+
const key = crypto12.randomBytes(32);
|
|
14219
14589
|
await setMasterKey(key);
|
|
14220
14590
|
log("Encryption key generated and stored securely.");
|
|
14221
14591
|
}
|
|
@@ -14367,19 +14737,19 @@ async function runSetupWizard(opts = {}) {
|
|
|
14367
14737
|
await saveConfig(config);
|
|
14368
14738
|
log("");
|
|
14369
14739
|
try {
|
|
14370
|
-
const claudeJsonPath =
|
|
14740
|
+
const claudeJsonPath = path33.join(os16.homedir(), ".claude.json");
|
|
14371
14741
|
let claudeJson = {};
|
|
14372
14742
|
try {
|
|
14373
|
-
claudeJson = JSON.parse(
|
|
14743
|
+
claudeJson = JSON.parse(readFileSync23(claudeJsonPath, "utf8"));
|
|
14374
14744
|
} catch {
|
|
14375
14745
|
}
|
|
14376
14746
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
14377
14747
|
const projects = claudeJson.projects;
|
|
14378
|
-
for (const dir of [process.cwd(),
|
|
14748
|
+
for (const dir of [process.cwd(), os16.homedir()]) {
|
|
14379
14749
|
if (!projects[dir]) projects[dir] = {};
|
|
14380
14750
|
projects[dir].hasTrustDialogAccepted = true;
|
|
14381
14751
|
}
|
|
14382
|
-
|
|
14752
|
+
writeFileSync19(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
14383
14753
|
} catch {
|
|
14384
14754
|
}
|
|
14385
14755
|
state.completedSteps.push(5);
|
|
@@ -14393,7 +14763,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14393
14763
|
const prefs = { ...existingPrefs };
|
|
14394
14764
|
log("=== Config Defaults ===");
|
|
14395
14765
|
log("");
|
|
14396
|
-
const ghosttyDetected =
|
|
14766
|
+
const ghosttyDetected = existsSync27(path33.join(os16.homedir(), ".config", "ghostty")) || existsSync27(path33.join(os16.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
14397
14767
|
if (ghosttyDetected) {
|
|
14398
14768
|
const ghosttyAnswer = await ask2(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
14399
14769
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -14440,7 +14810,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14440
14810
|
let missingIdentities = [];
|
|
14441
14811
|
for (const emp of roster) {
|
|
14442
14812
|
const idPath = identityPath2(emp.name);
|
|
14443
|
-
if (!
|
|
14813
|
+
if (!existsSync27(idPath)) {
|
|
14444
14814
|
missingIdentities.push(emp.name);
|
|
14445
14815
|
}
|
|
14446
14816
|
}
|
|
@@ -14472,7 +14842,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14472
14842
|
}
|
|
14473
14843
|
missingIdentities = [];
|
|
14474
14844
|
for (const emp of roster) {
|
|
14475
|
-
if (!
|
|
14845
|
+
if (!existsSync27(identityPath2(emp.name))) {
|
|
14476
14846
|
missingIdentities.push(emp.name);
|
|
14477
14847
|
}
|
|
14478
14848
|
}
|
|
@@ -14537,9 +14907,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14537
14907
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
14538
14908
|
if (cooIdentityContent) {
|
|
14539
14909
|
const cooIdPath = identityPath2(cooName);
|
|
14540
|
-
|
|
14910
|
+
mkdirSync17(path33.dirname(cooIdPath), { recursive: true });
|
|
14541
14911
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
14542
|
-
|
|
14912
|
+
writeFileSync19(cooIdPath, replaced, "utf-8");
|
|
14543
14913
|
}
|
|
14544
14914
|
registerBinSymlinks2(cooName);
|
|
14545
14915
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -14633,9 +15003,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14633
15003
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
14634
15004
|
if (ctoIdentityContent) {
|
|
14635
15005
|
const ctoIdPath = identityPath2(ctoName);
|
|
14636
|
-
|
|
15006
|
+
mkdirSync17(path33.dirname(ctoIdPath), { recursive: true });
|
|
14637
15007
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
14638
|
-
|
|
15008
|
+
writeFileSync19(ctoIdPath, replaced, "utf-8");
|
|
14639
15009
|
}
|
|
14640
15010
|
registerBinSymlinks2(ctoName);
|
|
14641
15011
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -14656,9 +15026,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
14656
15026
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
14657
15027
|
if (cmoIdentityContent) {
|
|
14658
15028
|
const cmoIdPath = identityPath2(cmoName);
|
|
14659
|
-
|
|
15029
|
+
mkdirSync17(path33.dirname(cmoIdPath), { recursive: true });
|
|
14660
15030
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
14661
|
-
|
|
15031
|
+
writeFileSync19(cmoIdPath, replaced, "utf-8");
|
|
14662
15032
|
}
|
|
14663
15033
|
registerBinSymlinks2(cmoName);
|
|
14664
15034
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -14680,7 +15050,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14680
15050
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
14681
15051
|
}
|
|
14682
15052
|
if (wrapResult.pathConfigured) {
|
|
14683
|
-
const binDir =
|
|
15053
|
+
const binDir = path33.join(os16.homedir(), ".exe-os", "bin");
|
|
14684
15054
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
14685
15055
|
pathJustConfigured = true;
|
|
14686
15056
|
}
|
|
@@ -14723,7 +15093,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
14723
15093
|
const pkgRoot2 = findPackageRoot2();
|
|
14724
15094
|
if (pkgRoot2) {
|
|
14725
15095
|
try {
|
|
14726
|
-
version = JSON.parse(
|
|
15096
|
+
version = JSON.parse(readFileSync23(path33.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
14727
15097
|
} catch {
|
|
14728
15098
|
}
|
|
14729
15099
|
}
|
|
@@ -14757,17 +15127,17 @@ var init_setup_wizard = __esm({
|
|
|
14757
15127
|
init_config();
|
|
14758
15128
|
init_keychain();
|
|
14759
15129
|
init_model_downloader();
|
|
14760
|
-
SETUP_STATE_PATH =
|
|
15130
|
+
SETUP_STATE_PATH = path33.join(os16.homedir(), ".exe-os", "setup-state.json");
|
|
14761
15131
|
}
|
|
14762
15132
|
});
|
|
14763
15133
|
|
|
14764
15134
|
// src/lib/update-check.ts
|
|
14765
15135
|
import { execSync as execSync11 } from "child_process";
|
|
14766
|
-
import { readFileSync as
|
|
14767
|
-
import
|
|
15136
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
15137
|
+
import path34 from "path";
|
|
14768
15138
|
function getLocalVersion(packageRoot) {
|
|
14769
|
-
const pkgPath =
|
|
14770
|
-
const pkg = JSON.parse(
|
|
15139
|
+
const pkgPath = path34.join(packageRoot, "package.json");
|
|
15140
|
+
const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
|
|
14771
15141
|
return pkg.version;
|
|
14772
15142
|
}
|
|
14773
15143
|
function getRemoteVersion() {
|
|
@@ -19291,8 +19661,8 @@ var init_ErrorOverview = __esm({
|
|
|
19291
19661
|
"use strict";
|
|
19292
19662
|
init_Box();
|
|
19293
19663
|
init_Text();
|
|
19294
|
-
cleanupPath = (
|
|
19295
|
-
return
|
|
19664
|
+
cleanupPath = (path46) => {
|
|
19665
|
+
return path46?.replace(`file://${cwd()}/`, "");
|
|
19296
19666
|
};
|
|
19297
19667
|
stackUtils = new StackUtils({
|
|
19298
19668
|
cwd: cwd(),
|
|
@@ -21700,11 +22070,11 @@ function Footer() {
|
|
|
21700
22070
|
} catch {
|
|
21701
22071
|
}
|
|
21702
22072
|
try {
|
|
21703
|
-
const { existsSync:
|
|
22073
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
21704
22074
|
const { join } = await import("path");
|
|
21705
22075
|
const home = process.env.HOME ?? "";
|
|
21706
22076
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
21707
|
-
setDaemon(
|
|
22077
|
+
setDaemon(existsSync32(pidPath) ? "running" : "stopped");
|
|
21708
22078
|
} catch {
|
|
21709
22079
|
setDaemon("unknown");
|
|
21710
22080
|
}
|
|
@@ -23746,10 +24116,10 @@ var init_hooks = __esm({
|
|
|
23746
24116
|
});
|
|
23747
24117
|
|
|
23748
24118
|
// src/runtime/safety-checks.ts
|
|
23749
|
-
import
|
|
23750
|
-
import
|
|
24119
|
+
import path35 from "path";
|
|
24120
|
+
import os17 from "os";
|
|
23751
24121
|
function checkPathSafety(filePath) {
|
|
23752
|
-
const resolved =
|
|
24122
|
+
const resolved = path35.resolve(filePath);
|
|
23753
24123
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
23754
24124
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
23755
24125
|
if (matches) {
|
|
@@ -23759,7 +24129,7 @@ function checkPathSafety(filePath) {
|
|
|
23759
24129
|
return { safe: true, bypassImmune: true };
|
|
23760
24130
|
}
|
|
23761
24131
|
function checkReadPathSafety(filePath) {
|
|
23762
|
-
const resolved =
|
|
24132
|
+
const resolved = path35.resolve(filePath);
|
|
23763
24133
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
23764
24134
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
23765
24135
|
);
|
|
@@ -23774,7 +24144,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
23774
24144
|
var init_safety_checks = __esm({
|
|
23775
24145
|
"src/runtime/safety-checks.ts"() {
|
|
23776
24146
|
"use strict";
|
|
23777
|
-
HOME =
|
|
24147
|
+
HOME = os17.homedir();
|
|
23778
24148
|
BYPASS_IMMUNE_PATTERNS = [
|
|
23779
24149
|
{
|
|
23780
24150
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -23785,11 +24155,11 @@ var init_safety_checks = __esm({
|
|
|
23785
24155
|
reason: "Git config can set hooks and command execution"
|
|
23786
24156
|
},
|
|
23787
24157
|
{
|
|
23788
|
-
pattern: (p) => p.startsWith(
|
|
24158
|
+
pattern: (p) => p.startsWith(path35.join(HOME, ".claude")),
|
|
23789
24159
|
reason: "Claude configuration files are protected"
|
|
23790
24160
|
},
|
|
23791
24161
|
{
|
|
23792
|
-
pattern: (p) => p.startsWith(
|
|
24162
|
+
pattern: (p) => p.startsWith(path35.join(HOME, ".exe-os")),
|
|
23793
24163
|
reason: "exe-os configuration files are protected"
|
|
23794
24164
|
},
|
|
23795
24165
|
{
|
|
@@ -23806,7 +24176,7 @@ var init_safety_checks = __esm({
|
|
|
23806
24176
|
},
|
|
23807
24177
|
{
|
|
23808
24178
|
pattern: (p) => {
|
|
23809
|
-
const name =
|
|
24179
|
+
const name = path35.basename(p);
|
|
23810
24180
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
23811
24181
|
},
|
|
23812
24182
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -23833,7 +24203,7 @@ __export(file_read_exports, {
|
|
|
23833
24203
|
FileReadTool: () => FileReadTool
|
|
23834
24204
|
});
|
|
23835
24205
|
import fs3 from "fs/promises";
|
|
23836
|
-
import
|
|
24206
|
+
import path36 from "path";
|
|
23837
24207
|
import { z } from "zod";
|
|
23838
24208
|
function isBinary(buf) {
|
|
23839
24209
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -23869,7 +24239,7 @@ var init_file_read = __esm({
|
|
|
23869
24239
|
return { behavior: "allow" };
|
|
23870
24240
|
},
|
|
23871
24241
|
async call(input, context) {
|
|
23872
|
-
const filePath =
|
|
24242
|
+
const filePath = path36.isAbsolute(input.file_path) ? input.file_path : path36.resolve(context.cwd, input.file_path);
|
|
23873
24243
|
let stat2;
|
|
23874
24244
|
try {
|
|
23875
24245
|
stat2 = await fs3.stat(filePath);
|
|
@@ -23909,7 +24279,7 @@ __export(glob_exports, {
|
|
|
23909
24279
|
GlobTool: () => GlobTool
|
|
23910
24280
|
});
|
|
23911
24281
|
import fs4 from "fs/promises";
|
|
23912
|
-
import
|
|
24282
|
+
import path37 from "path";
|
|
23913
24283
|
import { z as z2 } from "zod";
|
|
23914
24284
|
async function walkDir(dir, maxDepth = 10) {
|
|
23915
24285
|
const results = [];
|
|
@@ -23925,7 +24295,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
23925
24295
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
23926
24296
|
continue;
|
|
23927
24297
|
}
|
|
23928
|
-
const fullPath =
|
|
24298
|
+
const fullPath = path37.join(current, entry.name);
|
|
23929
24299
|
if (entry.isDirectory()) {
|
|
23930
24300
|
await walk(fullPath, depth + 1);
|
|
23931
24301
|
} else {
|
|
@@ -23959,11 +24329,11 @@ var init_glob = __esm({
|
|
|
23959
24329
|
inputSchema: inputSchema2,
|
|
23960
24330
|
isReadOnly: true,
|
|
23961
24331
|
async call(input, context) {
|
|
23962
|
-
const baseDir = input.path ?
|
|
24332
|
+
const baseDir = input.path ? path37.isAbsolute(input.path) ? input.path : path37.resolve(context.cwd, input.path) : context.cwd;
|
|
23963
24333
|
try {
|
|
23964
24334
|
const entries = await walkDir(baseDir);
|
|
23965
24335
|
const matched = entries.filter(
|
|
23966
|
-
(e) => simpleGlobMatch(
|
|
24336
|
+
(e) => simpleGlobMatch(path37.relative(baseDir, e.path), input.pattern)
|
|
23967
24337
|
);
|
|
23968
24338
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
23969
24339
|
if (matched.length === 0) {
|
|
@@ -23989,7 +24359,7 @@ __export(grep_exports, {
|
|
|
23989
24359
|
});
|
|
23990
24360
|
import { spawn as spawn2 } from "child_process";
|
|
23991
24361
|
import fs5 from "fs/promises";
|
|
23992
|
-
import
|
|
24362
|
+
import path38 from "path";
|
|
23993
24363
|
import { z as z3 } from "zod";
|
|
23994
24364
|
function runRipgrep(input, searchPath, context) {
|
|
23995
24365
|
return new Promise((resolve, reject) => {
|
|
@@ -24043,7 +24413,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
24043
24413
|
}
|
|
24044
24414
|
for (const entry of entries) {
|
|
24045
24415
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
24046
|
-
const fullPath =
|
|
24416
|
+
const fullPath = path38.join(dir, entry.name);
|
|
24047
24417
|
if (entry.isDirectory()) {
|
|
24048
24418
|
await walk(fullPath);
|
|
24049
24419
|
} else {
|
|
@@ -24089,7 +24459,7 @@ var init_grep = __esm({
|
|
|
24089
24459
|
inputSchema: inputSchema3,
|
|
24090
24460
|
isReadOnly: true,
|
|
24091
24461
|
async call(input, context) {
|
|
24092
|
-
const searchPath = input.path ?
|
|
24462
|
+
const searchPath = input.path ? path38.isAbsolute(input.path) ? input.path : path38.resolve(context.cwd, input.path) : context.cwd;
|
|
24093
24463
|
try {
|
|
24094
24464
|
const result = await runRipgrep(input, searchPath, context);
|
|
24095
24465
|
return result;
|
|
@@ -24114,7 +24484,7 @@ __export(file_write_exports, {
|
|
|
24114
24484
|
FileWriteTool: () => FileWriteTool
|
|
24115
24485
|
});
|
|
24116
24486
|
import fs6 from "fs/promises";
|
|
24117
|
-
import
|
|
24487
|
+
import path39 from "path";
|
|
24118
24488
|
import { z as z4 } from "zod";
|
|
24119
24489
|
var inputSchema4, FileWriteTool;
|
|
24120
24490
|
var init_file_write = __esm({
|
|
@@ -24142,8 +24512,8 @@ var init_file_write = __esm({
|
|
|
24142
24512
|
return { behavior: "allow" };
|
|
24143
24513
|
},
|
|
24144
24514
|
async call(input, context) {
|
|
24145
|
-
const filePath =
|
|
24146
|
-
const dir =
|
|
24515
|
+
const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
|
|
24516
|
+
const dir = path39.dirname(filePath);
|
|
24147
24517
|
await fs6.mkdir(dir, { recursive: true });
|
|
24148
24518
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
24149
24519
|
return {
|
|
@@ -24161,7 +24531,7 @@ __export(file_edit_exports, {
|
|
|
24161
24531
|
FileEditTool: () => FileEditTool
|
|
24162
24532
|
});
|
|
24163
24533
|
import fs7 from "fs/promises";
|
|
24164
|
-
import
|
|
24534
|
+
import path40 from "path";
|
|
24165
24535
|
import { z as z5 } from "zod";
|
|
24166
24536
|
function countOccurrences(haystack, needle) {
|
|
24167
24537
|
let count = 0;
|
|
@@ -24202,7 +24572,7 @@ var init_file_edit = __esm({
|
|
|
24202
24572
|
return { behavior: "allow" };
|
|
24203
24573
|
},
|
|
24204
24574
|
async call(input, context) {
|
|
24205
|
-
const filePath =
|
|
24575
|
+
const filePath = path40.isAbsolute(input.file_path) ? input.file_path : path40.resolve(context.cwd, input.file_path);
|
|
24206
24576
|
let content;
|
|
24207
24577
|
try {
|
|
24208
24578
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -24444,7 +24814,7 @@ var init_bash = __esm({
|
|
|
24444
24814
|
// src/tui/views/CommandCenter.tsx
|
|
24445
24815
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
24446
24816
|
import TextInput from "ink-text-input";
|
|
24447
|
-
import
|
|
24817
|
+
import path41 from "path";
|
|
24448
24818
|
import { homedir as homedir6 } from "os";
|
|
24449
24819
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
24450
24820
|
function CommandCenterView({
|
|
@@ -24479,15 +24849,15 @@ function CommandCenterView({
|
|
|
24479
24849
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
24480
24850
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
24481
24851
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
24482
|
-
const { readFileSync:
|
|
24852
|
+
const { readFileSync: readFileSync28, existsSync: existsSync32 } = await import("fs");
|
|
24483
24853
|
const { join } = await import("path");
|
|
24484
24854
|
const { homedir: homedir8 } = await import("os");
|
|
24485
24855
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
24486
24856
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
24487
24857
|
let providerConfigs = {};
|
|
24488
|
-
if (
|
|
24858
|
+
if (existsSync32(configPath)) {
|
|
24489
24859
|
try {
|
|
24490
|
-
const raw = JSON.parse(
|
|
24860
|
+
const raw = JSON.parse(readFileSync28(configPath, "utf8"));
|
|
24491
24861
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
24492
24862
|
if (raw.providers && typeof raw.providers === "object") {
|
|
24493
24863
|
providerConfigs = raw.providers;
|
|
@@ -24548,7 +24918,7 @@ function CommandCenterView({
|
|
|
24548
24918
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
24549
24919
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
24550
24920
|
for (const f of agentFiles) {
|
|
24551
|
-
const data = JSON.parse(
|
|
24921
|
+
const data = JSON.parse(readFileSync28(join(markerDir, f), "utf8"));
|
|
24552
24922
|
if (data.agentRole) {
|
|
24553
24923
|
agentRole = data.agentRole;
|
|
24554
24924
|
break;
|
|
@@ -24693,7 +25063,7 @@ function CommandCenterView({
|
|
|
24693
25063
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
24694
25064
|
projectName: p.projectName,
|
|
24695
25065
|
exeSession: p.exeSession,
|
|
24696
|
-
projectDir:
|
|
25066
|
+
projectDir: path41.join(homedir6(), p.projectName),
|
|
24697
25067
|
employeeCount: p.employees.length,
|
|
24698
25068
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
24699
25069
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -24731,7 +25101,7 @@ function CommandCenterView({
|
|
|
24731
25101
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
24732
25102
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
24733
25103
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
24734
|
-
const { existsSync:
|
|
25104
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
24735
25105
|
const { join } = await import("path");
|
|
24736
25106
|
const client = getClient2();
|
|
24737
25107
|
if (!client) {
|
|
@@ -24802,7 +25172,7 @@ function CommandCenterView({
|
|
|
24802
25172
|
}
|
|
24803
25173
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
24804
25174
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
24805
|
-
const hasGit = projectDir ?
|
|
25175
|
+
const hasGit = projectDir ? existsSync32(join(projectDir, ".git")) : false;
|
|
24806
25176
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
24807
25177
|
projectList.push({
|
|
24808
25178
|
projectName: name,
|
|
@@ -24827,7 +25197,7 @@ function CommandCenterView({
|
|
|
24827
25197
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
24828
25198
|
try {
|
|
24829
25199
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
24830
|
-
setHealth((h) => ({ ...h, daemon:
|
|
25200
|
+
setHealth((h) => ({ ...h, daemon: existsSync32(pidPath) ? "running" : "stopped" }));
|
|
24831
25201
|
} catch {
|
|
24832
25202
|
}
|
|
24833
25203
|
const activityResult = await client.execute(
|
|
@@ -25697,7 +26067,7 @@ var init_useOrchestrator = __esm({
|
|
|
25697
26067
|
|
|
25698
26068
|
// src/tui/views/Sessions.tsx
|
|
25699
26069
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
25700
|
-
import
|
|
26070
|
+
import path42 from "path";
|
|
25701
26071
|
import { homedir as homedir7 } from "os";
|
|
25702
26072
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
25703
26073
|
function isCoordinatorEntry(entry) {
|
|
@@ -25735,7 +26105,7 @@ function SessionsView({
|
|
|
25735
26105
|
if (demo) {
|
|
25736
26106
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
25737
26107
|
...p,
|
|
25738
|
-
projectDir:
|
|
26108
|
+
projectDir: path42.join(homedir7(), p.projectName),
|
|
25739
26109
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
25740
26110
|
})));
|
|
25741
26111
|
return;
|
|
@@ -26145,6 +26515,133 @@ var init_Sessions = __esm({
|
|
|
26145
26515
|
}
|
|
26146
26516
|
});
|
|
26147
26517
|
|
|
26518
|
+
// src/lib/tui-data.ts
|
|
26519
|
+
var tui_data_exports = {};
|
|
26520
|
+
__export(tui_data_exports, {
|
|
26521
|
+
loadMemoryDashboard: () => loadMemoryDashboard,
|
|
26522
|
+
loadTaskList: () => loadTaskList,
|
|
26523
|
+
loadTeamMetrics: () => loadTeamMetrics,
|
|
26524
|
+
searchWikiMemoryRows: () => searchWikiMemoryRows
|
|
26525
|
+
});
|
|
26526
|
+
async function loadMemoryDashboard(limit) {
|
|
26527
|
+
const client = getClient();
|
|
26528
|
+
const [countResult, recentResult, agentResult] = await Promise.all([
|
|
26529
|
+
client.execute("SELECT COUNT(*) as cnt FROM memories"),
|
|
26530
|
+
client.execute({
|
|
26531
|
+
sql: "SELECT agent_id, tool_name, project_name, raw_text, timestamp FROM memories ORDER BY timestamp DESC LIMIT ?",
|
|
26532
|
+
args: [limit]
|
|
26533
|
+
}),
|
|
26534
|
+
client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id ORDER BY cnt DESC")
|
|
26535
|
+
]);
|
|
26536
|
+
return {
|
|
26537
|
+
total: Number(countResult.rows[0]?.cnt ?? 0),
|
|
26538
|
+
recent: recentResult.rows.map((row) => ({
|
|
26539
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
26540
|
+
toolName: String(row.tool_name ?? ""),
|
|
26541
|
+
projectName: String(row.project_name ?? ""),
|
|
26542
|
+
rawText: String(row.raw_text ?? ""),
|
|
26543
|
+
timestamp: String(row.timestamp ?? "")
|
|
26544
|
+
})),
|
|
26545
|
+
byAgent: agentResult.rows.map((row) => ({
|
|
26546
|
+
agentId: String(row.agent_id ?? "unknown"),
|
|
26547
|
+
count: Number(row.cnt ?? 0)
|
|
26548
|
+
}))
|
|
26549
|
+
};
|
|
26550
|
+
}
|
|
26551
|
+
async function loadTeamMetrics(employeeNames) {
|
|
26552
|
+
const client = getClient();
|
|
26553
|
+
const memoryCounts = /* @__PURE__ */ new Map();
|
|
26554
|
+
const projectsByEmployee = /* @__PURE__ */ new Map();
|
|
26555
|
+
const currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
26556
|
+
const scope = sessionScopeFilter();
|
|
26557
|
+
const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
|
|
26558
|
+
for (const row of memResult.rows) {
|
|
26559
|
+
memoryCounts.set(String(row.agent_id), Number(row.cnt));
|
|
26560
|
+
}
|
|
26561
|
+
for (const employeeName of employeeNames) {
|
|
26562
|
+
const [projectResult, taskResult] = await Promise.all([
|
|
26563
|
+
client.execute({
|
|
26564
|
+
sql: `SELECT DISTINCT project_name,
|
|
26565
|
+
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
26566
|
+
FROM tasks
|
|
26567
|
+
WHERE assigned_to = ? AND status IN ('open','in_progress','done')${scope.sql}
|
|
26568
|
+
GROUP BY project_name
|
|
26569
|
+
ORDER BY urgency ASC
|
|
26570
|
+
LIMIT 5`,
|
|
26571
|
+
args: [employeeName, ...scope.args]
|
|
26572
|
+
}),
|
|
26573
|
+
client.execute({
|
|
26574
|
+
sql: `SELECT title FROM tasks
|
|
26575
|
+
WHERE assigned_to = ? AND status = 'in_progress'${scope.sql}
|
|
26576
|
+
ORDER BY updated_at DESC
|
|
26577
|
+
LIMIT 1`,
|
|
26578
|
+
args: [employeeName, ...scope.args]
|
|
26579
|
+
})
|
|
26580
|
+
]);
|
|
26581
|
+
const projects = projectResult.rows.map((row) => {
|
|
26582
|
+
const urgency = Number(row.urgency);
|
|
26583
|
+
return {
|
|
26584
|
+
name: String(row.project_name),
|
|
26585
|
+
status: urgency === 1 ? "active" : urgency === 2 ? "has_tasks" : "idle"
|
|
26586
|
+
};
|
|
26587
|
+
});
|
|
26588
|
+
if (projects.length > 0) projectsByEmployee.set(employeeName, projects);
|
|
26589
|
+
if (taskResult.rows.length > 0) {
|
|
26590
|
+
currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
|
|
26591
|
+
}
|
|
26592
|
+
}
|
|
26593
|
+
return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
|
|
26594
|
+
}
|
|
26595
|
+
async function loadTaskList() {
|
|
26596
|
+
const client = getClient();
|
|
26597
|
+
const scope = sessionScopeFilter();
|
|
26598
|
+
const result = await client.execute({
|
|
26599
|
+
sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
|
|
26600
|
+
FROM tasks
|
|
26601
|
+
WHERE 1=1${scope.sql}
|
|
26602
|
+
ORDER BY
|
|
26603
|
+
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,
|
|
26604
|
+
CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
|
|
26605
|
+
created_at DESC`,
|
|
26606
|
+
args: [...scope.args]
|
|
26607
|
+
});
|
|
26608
|
+
return result.rows.map((row) => ({
|
|
26609
|
+
id: String(row.id ?? ""),
|
|
26610
|
+
title: String(row.title ?? ""),
|
|
26611
|
+
priority: String(row.priority ?? "p2").toUpperCase(),
|
|
26612
|
+
assignedTo: String(row.assigned_to ?? ""),
|
|
26613
|
+
assignedBy: String(row.assigned_by ?? ""),
|
|
26614
|
+
status: String(row.status ?? "open"),
|
|
26615
|
+
projectName: String(row.project_name ?? ""),
|
|
26616
|
+
createdAt: String(row.created_at ?? ""),
|
|
26617
|
+
result: String(row.result ?? "")
|
|
26618
|
+
}));
|
|
26619
|
+
}
|
|
26620
|
+
async function searchWikiMemoryRows(query) {
|
|
26621
|
+
const client = getClient();
|
|
26622
|
+
const result = await client.execute({
|
|
26623
|
+
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
26624
|
+
FROM memories
|
|
26625
|
+
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
26626
|
+
ORDER BY timestamp DESC LIMIT 20`,
|
|
26627
|
+
args: [`%${query}%`]
|
|
26628
|
+
});
|
|
26629
|
+
return result.rows.map((row) => ({
|
|
26630
|
+
id: String(row.id),
|
|
26631
|
+
agentId: String(row.agent_id),
|
|
26632
|
+
rawText: String(row.raw_text),
|
|
26633
|
+
timestamp: String(row.timestamp),
|
|
26634
|
+
projectName: String(row.project_name ?? "")
|
|
26635
|
+
}));
|
|
26636
|
+
}
|
|
26637
|
+
var init_tui_data = __esm({
|
|
26638
|
+
"src/lib/tui-data.ts"() {
|
|
26639
|
+
"use strict";
|
|
26640
|
+
init_database();
|
|
26641
|
+
init_task_scope();
|
|
26642
|
+
}
|
|
26643
|
+
});
|
|
26644
|
+
|
|
26148
26645
|
// src/tui/views/Tasks.tsx
|
|
26149
26646
|
import React20, { useState as useState10, useEffect as useEffect12, useMemo as useMemo5 } from "react";
|
|
26150
26647
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -26264,37 +26761,22 @@ function TasksView({ onBack }) {
|
|
|
26264
26761
|
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
26265
26762
|
return withTrace2("tui.tasks.loadTasks", async () => {
|
|
26266
26763
|
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
|
-
}
|
|
26764
|
+
const { loadTaskList: loadTaskList2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
26765
|
+
const tasks = await loadTaskList2();
|
|
26766
|
+
setAllTasks(
|
|
26767
|
+
tasks.map((task) => ({
|
|
26768
|
+
id: task.id,
|
|
26769
|
+
priority: task.priority,
|
|
26770
|
+
title: task.title,
|
|
26771
|
+
assignee: task.assignedTo,
|
|
26772
|
+
assignedBy: task.assignedBy,
|
|
26773
|
+
status: task.status,
|
|
26774
|
+
project: task.projectName,
|
|
26775
|
+
createdAt: task.createdAt,
|
|
26776
|
+
result: task.result
|
|
26777
|
+
}))
|
|
26778
|
+
);
|
|
26779
|
+
setDbError(null);
|
|
26298
26780
|
} catch (err) {
|
|
26299
26781
|
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
26300
26782
|
} finally {
|
|
@@ -26423,7 +26905,6 @@ var init_Tasks = __esm({
|
|
|
26423
26905
|
await init_ink2();
|
|
26424
26906
|
init_DemoContext();
|
|
26425
26907
|
init_demo_data();
|
|
26426
|
-
init_task_scope();
|
|
26427
26908
|
STATUS_FILTERS = ["all", "open", "in_progress", "done", "blocked", "needs_review"];
|
|
26428
26909
|
PRIORITY_COLORS = {
|
|
26429
26910
|
P0: "red",
|
|
@@ -26839,12 +27320,12 @@ async function loadGatewayConfig() {
|
|
|
26839
27320
|
state.running = false;
|
|
26840
27321
|
}
|
|
26841
27322
|
try {
|
|
26842
|
-
const { existsSync:
|
|
27323
|
+
const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
|
|
26843
27324
|
const { join } = await import("path");
|
|
26844
27325
|
const home = process.env.HOME ?? "";
|
|
26845
27326
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
26846
|
-
if (
|
|
26847
|
-
const raw = JSON.parse(
|
|
27327
|
+
if (existsSync32(configPath)) {
|
|
27328
|
+
const raw = JSON.parse(readFileSync28(configPath, "utf8"));
|
|
26848
27329
|
state.port = raw.port ?? 3100;
|
|
26849
27330
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
26850
27331
|
if (raw.adapters) {
|
|
@@ -27414,41 +27895,16 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
27414
27895
|
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
27415
27896
|
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
27416
27897
|
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
|
-
}
|
|
27898
|
+
const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
27899
|
+
const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
|
|
27900
|
+
memoryCounts = teamMetrics.memoryCounts;
|
|
27901
|
+
projectsByEmployee = new Map(
|
|
27902
|
+
Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
|
|
27903
|
+
name,
|
|
27904
|
+
projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
|
|
27905
|
+
])
|
|
27906
|
+
);
|
|
27907
|
+
currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
|
|
27452
27908
|
} catch {
|
|
27453
27909
|
}
|
|
27454
27910
|
const teamData = roster.map((emp) => {
|
|
@@ -27467,12 +27923,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
27467
27923
|
setMembers(teamData);
|
|
27468
27924
|
setDbError(null);
|
|
27469
27925
|
try {
|
|
27470
|
-
const { existsSync:
|
|
27926
|
+
const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
|
|
27471
27927
|
const { join } = await import("path");
|
|
27472
27928
|
const home = process.env.HOME ?? "";
|
|
27473
27929
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
27474
|
-
if (
|
|
27475
|
-
const raw = JSON.parse(
|
|
27930
|
+
if (existsSync32(gatewayConfig)) {
|
|
27931
|
+
const raw = JSON.parse(readFileSync28(gatewayConfig, "utf8"));
|
|
27476
27932
|
if (raw.agents && raw.agents.length > 0) {
|
|
27477
27933
|
setExternals(raw.agents.map((a) => ({
|
|
27478
27934
|
name: a.name,
|
|
@@ -27639,7 +28095,6 @@ var init_Team = __esm({
|
|
|
27639
28095
|
init_demo_data();
|
|
27640
28096
|
init_useOrchestrator();
|
|
27641
28097
|
init_agent_status();
|
|
27642
|
-
init_task_scope();
|
|
27643
28098
|
DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
|
|
27644
28099
|
}
|
|
27645
28100
|
});
|
|
@@ -27653,8 +28108,8 @@ __export(wiki_client_exports, {
|
|
|
27653
28108
|
listDocuments: () => listDocuments,
|
|
27654
28109
|
listWorkspaces: () => listWorkspaces
|
|
27655
28110
|
});
|
|
27656
|
-
async function wikiFetch(config,
|
|
27657
|
-
const url = `${config.baseUrl}/api/v1${
|
|
28111
|
+
async function wikiFetch(config, path46, method = "GET", body) {
|
|
28112
|
+
const url = `${config.baseUrl}/api/v1${path46}`;
|
|
27658
28113
|
const headers = {
|
|
27659
28114
|
Authorization: `Bearer ${config.apiKey}`,
|
|
27660
28115
|
"Content-Type": "application/json"
|
|
@@ -27687,7 +28142,7 @@ async function wikiFetch(config, path45, method = "GET", body) {
|
|
|
27687
28142
|
}
|
|
27688
28143
|
}
|
|
27689
28144
|
if (!response.ok) {
|
|
27690
|
-
throw new Error(`Wiki API ${method} ${
|
|
28145
|
+
throw new Error(`Wiki API ${method} ${path46}: ${response.status} ${response.statusText}`);
|
|
27691
28146
|
}
|
|
27692
28147
|
return response.json();
|
|
27693
28148
|
} finally {
|
|
@@ -27933,24 +28388,8 @@ function WikiView({ onBack }) {
|
|
|
27933
28388
|
}
|
|
27934
28389
|
setSearchLoading(true);
|
|
27935
28390
|
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
|
-
);
|
|
28391
|
+
const { searchWikiMemoryRows: searchWikiMemoryRows2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
|
|
28392
|
+
setSearchResults(await searchWikiMemoryRows2(query));
|
|
27954
28393
|
} catch {
|
|
27955
28394
|
setSearchResults([]);
|
|
27956
28395
|
} finally {
|
|
@@ -28297,12 +28736,12 @@ function SettingsView({ onBack }) {
|
|
|
28297
28736
|
}
|
|
28298
28737
|
setProviders(providerList);
|
|
28299
28738
|
try {
|
|
28300
|
-
const { existsSync:
|
|
28739
|
+
const { existsSync: existsSync32 } = await import("fs");
|
|
28301
28740
|
const { join } = await import("path");
|
|
28302
28741
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
28303
28742
|
const cfg = await loadConfig2();
|
|
28304
28743
|
const home = process.env.HOME ?? "";
|
|
28305
|
-
const hasKey =
|
|
28744
|
+
const hasKey = existsSync32(join(home, ".exe-os", "master.key"));
|
|
28306
28745
|
if (cfg.cloud) {
|
|
28307
28746
|
setCloud({
|
|
28308
28747
|
configured: true,
|
|
@@ -28315,22 +28754,22 @@ function SettingsView({ onBack }) {
|
|
|
28315
28754
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
28316
28755
|
let daemon = "unknown";
|
|
28317
28756
|
try {
|
|
28318
|
-
daemon =
|
|
28757
|
+
daemon = existsSync32(pidPath) ? "running" : "stopped";
|
|
28319
28758
|
} catch {
|
|
28320
28759
|
}
|
|
28321
28760
|
let version = "unknown";
|
|
28322
28761
|
try {
|
|
28323
|
-
const { readFileSync:
|
|
28324
|
-
const { createRequire:
|
|
28325
|
-
const require2 =
|
|
28762
|
+
const { readFileSync: readFileSync28 } = await import("fs");
|
|
28763
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
28764
|
+
const require2 = createRequire3(import.meta.url);
|
|
28326
28765
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
28327
|
-
const pkg = JSON.parse(
|
|
28766
|
+
const pkg = JSON.parse(readFileSync28(pkgPath, "utf8"));
|
|
28328
28767
|
version = pkg.version;
|
|
28329
28768
|
} catch {
|
|
28330
28769
|
try {
|
|
28331
|
-
const { readFileSync:
|
|
28770
|
+
const { readFileSync: readFileSync28 } = await import("fs");
|
|
28332
28771
|
const { join: joinPath } = await import("path");
|
|
28333
|
-
const pkg = JSON.parse(
|
|
28772
|
+
const pkg = JSON.parse(readFileSync28(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
28334
28773
|
version = pkg.version;
|
|
28335
28774
|
} catch {
|
|
28336
28775
|
}
|
|
@@ -29131,15 +29570,15 @@ __export(installer_exports2, {
|
|
|
29131
29570
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
29132
29571
|
});
|
|
29133
29572
|
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 =
|
|
29573
|
+
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
|
|
29574
|
+
import path43 from "path";
|
|
29575
|
+
import os18 from "os";
|
|
29576
|
+
async function registerOpenCodeMcp(packageRoot, homeDir = os18.homedir()) {
|
|
29577
|
+
const configDir = path43.join(homeDir, ".config", "opencode");
|
|
29578
|
+
const configPath = path43.join(configDir, "opencode.json");
|
|
29140
29579
|
await mkdir7(configDir, { recursive: true });
|
|
29141
29580
|
let config = {};
|
|
29142
|
-
if (
|
|
29581
|
+
if (existsSync29(configPath)) {
|
|
29143
29582
|
try {
|
|
29144
29583
|
config = JSON.parse(await readFile6(configPath, "utf-8"));
|
|
29145
29584
|
} catch {
|
|
@@ -29151,7 +29590,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
|
|
|
29151
29590
|
}
|
|
29152
29591
|
const newEntry = {
|
|
29153
29592
|
type: "local",
|
|
29154
|
-
command: ["node",
|
|
29593
|
+
command: ["node", path43.join(packageRoot, "dist", "mcp", "server.js")],
|
|
29155
29594
|
enabled: true
|
|
29156
29595
|
};
|
|
29157
29596
|
const current = config.mcp["exe-os"];
|
|
@@ -29165,15 +29604,15 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
|
|
|
29165
29604
|
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
29166
29605
|
return true;
|
|
29167
29606
|
}
|
|
29168
|
-
async function installOpenCodePlugin(packageRoot, homeDir =
|
|
29169
|
-
const pluginDir =
|
|
29170
|
-
const pluginPath =
|
|
29607
|
+
async function installOpenCodePlugin(packageRoot, homeDir = os18.homedir()) {
|
|
29608
|
+
const pluginDir = path43.join(homeDir, ".config", "opencode", "plugins");
|
|
29609
|
+
const pluginPath = path43.join(pluginDir, "exe-os.mjs");
|
|
29171
29610
|
await mkdir7(pluginDir, { recursive: true });
|
|
29172
29611
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
29173
29612
|
/__PACKAGE_ROOT__/g,
|
|
29174
29613
|
packageRoot.replace(/\\/g, "\\\\")
|
|
29175
29614
|
);
|
|
29176
|
-
if (
|
|
29615
|
+
if (existsSync29(pluginPath)) {
|
|
29177
29616
|
const existing = await readFile6(pluginPath, "utf-8");
|
|
29178
29617
|
if (existing === pluginContent) {
|
|
29179
29618
|
return false;
|
|
@@ -29182,17 +29621,17 @@ async function installOpenCodePlugin(packageRoot, homeDir = os17.homedir()) {
|
|
|
29182
29621
|
await writeFile7(pluginPath, pluginContent);
|
|
29183
29622
|
return true;
|
|
29184
29623
|
}
|
|
29185
|
-
function verifyOpenCodeHooks(homeDir =
|
|
29186
|
-
const configPath =
|
|
29187
|
-
const pluginPath =
|
|
29188
|
-
if (!
|
|
29624
|
+
function verifyOpenCodeHooks(homeDir = os18.homedir()) {
|
|
29625
|
+
const configPath = path43.join(homeDir, ".config", "opencode", "opencode.json");
|
|
29626
|
+
const pluginPath = path43.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
29627
|
+
if (!existsSync29(configPath)) return false;
|
|
29189
29628
|
try {
|
|
29190
|
-
const config = JSON.parse(
|
|
29629
|
+
const config = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
29191
29630
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
29192
29631
|
} catch {
|
|
29193
29632
|
return false;
|
|
29194
29633
|
}
|
|
29195
|
-
if (!
|
|
29634
|
+
if (!existsSync29(pluginPath)) return false;
|
|
29196
29635
|
return true;
|
|
29197
29636
|
}
|
|
29198
29637
|
async function runOpenCodeInstaller(homeDir) {
|
|
@@ -29225,19 +29664,19 @@ __export(installer_exports3, {
|
|
|
29225
29664
|
verifyCodexHooks: () => verifyCodexHooks
|
|
29226
29665
|
});
|
|
29227
29666
|
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 =
|
|
29667
|
+
import { existsSync as existsSync30 } from "fs";
|
|
29668
|
+
import path44 from "path";
|
|
29669
|
+
import os19 from "os";
|
|
29670
|
+
async function mergeCodexHooks(packageRoot, homeDir = os19.homedir()) {
|
|
29671
|
+
const codexDir = path44.join(homeDir, ".codex");
|
|
29672
|
+
const hooksPath = path44.join(codexDir, "hooks.json");
|
|
29673
|
+
const logsDir = path44.join(homeDir, ".exe-os", "logs");
|
|
29674
|
+
const hookLogPath = path44.join(logsDir, "hooks.log");
|
|
29236
29675
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
29237
29676
|
await mkdir8(codexDir, { recursive: true });
|
|
29238
29677
|
await mkdir8(logsDir, { recursive: true });
|
|
29239
29678
|
let hooksJson = {};
|
|
29240
|
-
if (
|
|
29679
|
+
if (existsSync30(hooksPath)) {
|
|
29241
29680
|
try {
|
|
29242
29681
|
hooksJson = JSON.parse(await readFile7(hooksPath, "utf-8"));
|
|
29243
29682
|
} catch {
|
|
@@ -29254,7 +29693,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29254
29693
|
hooks: [
|
|
29255
29694
|
{
|
|
29256
29695
|
type: "command",
|
|
29257
|
-
command: `node "${
|
|
29696
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
29258
29697
|
timeout: 30,
|
|
29259
29698
|
statusMessage: "exe-os: loading memory brief"
|
|
29260
29699
|
}
|
|
@@ -29269,11 +29708,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29269
29708
|
hooks: [
|
|
29270
29709
|
{
|
|
29271
29710
|
type: "command",
|
|
29272
|
-
command: `node "${
|
|
29711
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
29273
29712
|
},
|
|
29274
29713
|
{
|
|
29275
29714
|
type: "command",
|
|
29276
|
-
command: `node "${
|
|
29715
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
29277
29716
|
}
|
|
29278
29717
|
]
|
|
29279
29718
|
},
|
|
@@ -29285,11 +29724,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29285
29724
|
hooks: [
|
|
29286
29725
|
{
|
|
29287
29726
|
type: "command",
|
|
29288
|
-
command: `node "${
|
|
29727
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
29289
29728
|
},
|
|
29290
29729
|
{
|
|
29291
29730
|
type: "command",
|
|
29292
|
-
command: `node "${
|
|
29731
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
29293
29732
|
timeout: 5
|
|
29294
29733
|
}
|
|
29295
29734
|
]
|
|
@@ -29302,7 +29741,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29302
29741
|
hooks: [
|
|
29303
29742
|
{
|
|
29304
29743
|
type: "command",
|
|
29305
|
-
command: `node "${
|
|
29744
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
29306
29745
|
}
|
|
29307
29746
|
]
|
|
29308
29747
|
},
|
|
@@ -29315,7 +29754,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29315
29754
|
hooks: [
|
|
29316
29755
|
{
|
|
29317
29756
|
type: "command",
|
|
29318
|
-
command: `node "${
|
|
29757
|
+
command: `node "${path44.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
29319
29758
|
}
|
|
29320
29759
|
]
|
|
29321
29760
|
},
|
|
@@ -29346,9 +29785,9 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
|
|
|
29346
29785
|
await writeFile8(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
29347
29786
|
return { added, skipped };
|
|
29348
29787
|
}
|
|
29349
|
-
function verifyCodexHooks(homeDir =
|
|
29350
|
-
const hooksPath =
|
|
29351
|
-
if (!
|
|
29788
|
+
function verifyCodexHooks(homeDir = os19.homedir()) {
|
|
29789
|
+
const hooksPath = path44.join(homeDir, ".codex", "hooks.json");
|
|
29790
|
+
if (!existsSync30(hooksPath)) return false;
|
|
29352
29791
|
try {
|
|
29353
29792
|
const hooksJson = JSON.parse(
|
|
29354
29793
|
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
@@ -29368,14 +29807,14 @@ function verifyCodexHooks(homeDir = os18.homedir()) {
|
|
|
29368
29807
|
return false;
|
|
29369
29808
|
}
|
|
29370
29809
|
}
|
|
29371
|
-
async function installCodexStatusLine(homeDir =
|
|
29810
|
+
async function installCodexStatusLine(homeDir = os19.homedir()) {
|
|
29372
29811
|
const prefs = loadPreferences(homeDir);
|
|
29373
29812
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
29374
|
-
const codexDir =
|
|
29375
|
-
const configPath =
|
|
29813
|
+
const codexDir = path44.join(homeDir, ".codex");
|
|
29814
|
+
const configPath = path44.join(codexDir, "config.toml");
|
|
29376
29815
|
await mkdir8(codexDir, { recursive: true });
|
|
29377
29816
|
let content = "";
|
|
29378
|
-
if (
|
|
29817
|
+
if (existsSync30(configPath)) {
|
|
29379
29818
|
content = await readFile7(configPath, "utf-8");
|
|
29380
29819
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
29381
29820
|
return "already-configured";
|
|
@@ -29423,14 +29862,14 @@ var init_installer3 = __esm({
|
|
|
29423
29862
|
});
|
|
29424
29863
|
|
|
29425
29864
|
// src/bin/cli.ts
|
|
29426
|
-
import { existsSync as
|
|
29427
|
-
import
|
|
29428
|
-
import
|
|
29865
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync9, rmSync } from "fs";
|
|
29866
|
+
import path45 from "path";
|
|
29867
|
+
import os20 from "os";
|
|
29429
29868
|
var args = process.argv.slice(2);
|
|
29430
29869
|
if (args.includes("--version") || args.includes("-v")) {
|
|
29431
29870
|
try {
|
|
29432
|
-
const pkgPath =
|
|
29433
|
-
const pkg = JSON.parse(
|
|
29871
|
+
const pkgPath = path45.join(path45.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
29872
|
+
const pkg = JSON.parse(readFileSync27(pkgPath, "utf8"));
|
|
29434
29873
|
console.log(pkg.version);
|
|
29435
29874
|
} catch {
|
|
29436
29875
|
console.log("unknown");
|
|
@@ -29594,11 +30033,11 @@ ID: ${result.id}`);
|
|
|
29594
30033
|
});
|
|
29595
30034
|
await init_App2().then(() => App_exports);
|
|
29596
30035
|
} else {
|
|
29597
|
-
const claudeDir =
|
|
29598
|
-
const settingsPath =
|
|
29599
|
-
const hasClaudeCode =
|
|
30036
|
+
const claudeDir = path45.join(os20.homedir(), ".claude");
|
|
30037
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30038
|
+
const hasClaudeCode = existsSync31(settingsPath) && (() => {
|
|
29600
30039
|
try {
|
|
29601
|
-
const raw =
|
|
30040
|
+
const raw = readFileSync27(settingsPath, "utf8");
|
|
29602
30041
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
29603
30042
|
} catch {
|
|
29604
30043
|
return false;
|
|
@@ -29608,9 +30047,9 @@ ID: ${result.id}`);
|
|
|
29608
30047
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
29609
30048
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
29610
30049
|
try {
|
|
29611
|
-
const rosterPath =
|
|
29612
|
-
if (
|
|
29613
|
-
const roster = JSON.parse(
|
|
30050
|
+
const rosterPath = path45.join(os20.homedir(), ".exe-os", "exe-employees.json");
|
|
30051
|
+
if (existsSync31(rosterPath)) {
|
|
30052
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29614
30053
|
const coo = roster.find((e) => e.role === "COO");
|
|
29615
30054
|
if (coo) cooName = coo.name;
|
|
29616
30055
|
}
|
|
@@ -29674,14 +30113,14 @@ async function runCodexInstall() {
|
|
|
29674
30113
|
}
|
|
29675
30114
|
}
|
|
29676
30115
|
async function runClaudeCheck() {
|
|
29677
|
-
const claudeDir =
|
|
29678
|
-
const settingsPath =
|
|
29679
|
-
const claudeJsonPath =
|
|
30116
|
+
const claudeDir = path45.join(os20.homedir(), ".claude");
|
|
30117
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30118
|
+
const claudeJsonPath = path45.join(os20.homedir(), ".claude.json");
|
|
29680
30119
|
let ok = true;
|
|
29681
|
-
if (
|
|
30120
|
+
if (existsSync31(settingsPath)) {
|
|
29682
30121
|
let settings;
|
|
29683
30122
|
try {
|
|
29684
|
-
settings = JSON.parse(
|
|
30123
|
+
settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
|
|
29685
30124
|
} catch {
|
|
29686
30125
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
29687
30126
|
ok = false;
|
|
@@ -29707,10 +30146,10 @@ async function runClaudeCheck() {
|
|
|
29707
30146
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
29708
30147
|
ok = false;
|
|
29709
30148
|
}
|
|
29710
|
-
if (
|
|
30149
|
+
if (existsSync31(claudeJsonPath)) {
|
|
29711
30150
|
let claudeJson;
|
|
29712
30151
|
try {
|
|
29713
|
-
claudeJson = JSON.parse(
|
|
30152
|
+
claudeJson = JSON.parse(readFileSync27(claudeJsonPath, "utf8"));
|
|
29714
30153
|
} catch {
|
|
29715
30154
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
29716
30155
|
ok = false;
|
|
@@ -29729,8 +30168,8 @@ async function runClaudeCheck() {
|
|
|
29729
30168
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
29730
30169
|
ok = false;
|
|
29731
30170
|
}
|
|
29732
|
-
const skillsDir =
|
|
29733
|
-
if (
|
|
30171
|
+
const skillsDir = path45.join(claudeDir, "skills");
|
|
30172
|
+
if (existsSync31(skillsDir)) {
|
|
29734
30173
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
29735
30174
|
} else {
|
|
29736
30175
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -29746,17 +30185,17 @@ async function runClaudeCheck() {
|
|
|
29746
30185
|
async function runClaudeUninstall(flags = []) {
|
|
29747
30186
|
const dryRun = flags.includes("--dry-run");
|
|
29748
30187
|
const purge = flags.includes("--purge");
|
|
29749
|
-
const homeDir =
|
|
29750
|
-
const claudeDir =
|
|
29751
|
-
const settingsPath =
|
|
29752
|
-
const claudeJsonPath =
|
|
29753
|
-
const exeOsDir =
|
|
30188
|
+
const homeDir = os20.homedir();
|
|
30189
|
+
const claudeDir = path45.join(homeDir, ".claude");
|
|
30190
|
+
const settingsPath = path45.join(claudeDir, "settings.json");
|
|
30191
|
+
const claudeJsonPath = path45.join(homeDir, ".claude.json");
|
|
30192
|
+
const exeOsDir = path45.join(homeDir, ".exe-os");
|
|
29754
30193
|
let removed = 0;
|
|
29755
30194
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
29756
30195
|
let settings = {};
|
|
29757
|
-
if (
|
|
30196
|
+
if (existsSync31(settingsPath)) {
|
|
29758
30197
|
try {
|
|
29759
|
-
settings = JSON.parse(
|
|
30198
|
+
settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
|
|
29760
30199
|
} catch {
|
|
29761
30200
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
29762
30201
|
if (purge) {
|
|
@@ -29794,15 +30233,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29794
30233
|
permCount = before - settings.permissions.allow.length;
|
|
29795
30234
|
}
|
|
29796
30235
|
if (!dryRun) {
|
|
29797
|
-
|
|
30236
|
+
writeFileSync20(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
29798
30237
|
}
|
|
29799
30238
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
29800
30239
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
29801
30240
|
removed++;
|
|
29802
30241
|
}
|
|
29803
30242
|
}
|
|
29804
|
-
if (
|
|
29805
|
-
const raw =
|
|
30243
|
+
if (existsSync31(claudeJsonPath)) {
|
|
30244
|
+
const raw = readFileSync27(claudeJsonPath, "utf8");
|
|
29806
30245
|
if (raw.length > 1e6) {
|
|
29807
30246
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
29808
30247
|
} else {
|
|
@@ -29823,7 +30262,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29823
30262
|
}
|
|
29824
30263
|
if (removedMcp) {
|
|
29825
30264
|
if (!dryRun) {
|
|
29826
|
-
|
|
30265
|
+
writeFileSync20(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
29827
30266
|
}
|
|
29828
30267
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
29829
30268
|
removed++;
|
|
@@ -29831,14 +30270,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29831
30270
|
}
|
|
29832
30271
|
}
|
|
29833
30272
|
}
|
|
29834
|
-
const skillsDir =
|
|
29835
|
-
if (
|
|
30273
|
+
const skillsDir = path45.join(claudeDir, "skills");
|
|
30274
|
+
if (existsSync31(skillsDir)) {
|
|
29836
30275
|
let skillCount = 0;
|
|
29837
30276
|
try {
|
|
29838
30277
|
const entries = readdirSync9(skillsDir);
|
|
29839
30278
|
for (const entry of entries) {
|
|
29840
30279
|
if (entry.startsWith("exe")) {
|
|
29841
|
-
const fullPath =
|
|
30280
|
+
const fullPath = path45.join(skillsDir, entry);
|
|
29842
30281
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
29843
30282
|
skillCount++;
|
|
29844
30283
|
}
|
|
@@ -29850,30 +30289,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29850
30289
|
removed++;
|
|
29851
30290
|
}
|
|
29852
30291
|
}
|
|
29853
|
-
const claudeMdPath =
|
|
29854
|
-
if (
|
|
29855
|
-
const content =
|
|
30292
|
+
const claudeMdPath = path45.join(claudeDir, "CLAUDE.md");
|
|
30293
|
+
if (existsSync31(claudeMdPath)) {
|
|
30294
|
+
const content = readFileSync27(claudeMdPath, "utf8");
|
|
29856
30295
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
29857
30296
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
29858
30297
|
const startIdx = content.indexOf(startMarker);
|
|
29859
30298
|
const endIdx = content.indexOf(endMarker);
|
|
29860
30299
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
29861
30300
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
29862
|
-
if (!dryRun)
|
|
30301
|
+
if (!dryRun) writeFileSync20(claudeMdPath, cleaned);
|
|
29863
30302
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
29864
30303
|
removed++;
|
|
29865
30304
|
}
|
|
29866
30305
|
}
|
|
29867
|
-
const agentsDir =
|
|
29868
|
-
if (
|
|
30306
|
+
const agentsDir = path45.join(claudeDir, "agents");
|
|
30307
|
+
if (existsSync31(agentsDir)) {
|
|
29869
30308
|
let agentCount = 0;
|
|
29870
30309
|
try {
|
|
29871
30310
|
const entries = readdirSync9(agentsDir).filter((f) => f.endsWith(".md"));
|
|
29872
30311
|
let knownNames = /* @__PURE__ */ new Set();
|
|
29873
|
-
const rosterPath =
|
|
29874
|
-
if (
|
|
30312
|
+
const rosterPath = path45.join(exeOsDir, "exe-employees.json");
|
|
30313
|
+
if (existsSync31(rosterPath)) {
|
|
29875
30314
|
try {
|
|
29876
|
-
const roster = JSON.parse(
|
|
30315
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29877
30316
|
knownNames = new Set(roster.map((e) => e.name));
|
|
29878
30317
|
} catch {
|
|
29879
30318
|
}
|
|
@@ -29881,7 +30320,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29881
30320
|
for (const entry of entries) {
|
|
29882
30321
|
const name = entry.replace(/\.md$/, "");
|
|
29883
30322
|
if (knownNames.has(name)) {
|
|
29884
|
-
if (!dryRun) rmSync(
|
|
30323
|
+
if (!dryRun) rmSync(path45.join(agentsDir, entry), { force: true });
|
|
29885
30324
|
agentCount++;
|
|
29886
30325
|
}
|
|
29887
30326
|
}
|
|
@@ -29892,16 +30331,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29892
30331
|
removed++;
|
|
29893
30332
|
}
|
|
29894
30333
|
}
|
|
29895
|
-
const projectsDir =
|
|
29896
|
-
if (
|
|
30334
|
+
const projectsDir = path45.join(claudeDir, "projects");
|
|
30335
|
+
if (existsSync31(projectsDir)) {
|
|
29897
30336
|
let projectCount = 0;
|
|
29898
30337
|
try {
|
|
29899
30338
|
const projects = readdirSync9(projectsDir);
|
|
29900
30339
|
for (const proj of projects) {
|
|
29901
|
-
const projSettings =
|
|
29902
|
-
if (!
|
|
30340
|
+
const projSettings = path45.join(projectsDir, proj, "settings.json");
|
|
30341
|
+
if (!existsSync31(projSettings)) continue;
|
|
29903
30342
|
try {
|
|
29904
|
-
const pSettings = JSON.parse(
|
|
30343
|
+
const pSettings = JSON.parse(readFileSync27(projSettings, "utf8"));
|
|
29905
30344
|
let changed = false;
|
|
29906
30345
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
29907
30346
|
const before = pSettings.permissions.allow.length;
|
|
@@ -29911,7 +30350,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29911
30350
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
29912
30351
|
}
|
|
29913
30352
|
if (changed && !dryRun) {
|
|
29914
|
-
|
|
30353
|
+
writeFileSync20(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
29915
30354
|
}
|
|
29916
30355
|
if (changed) projectCount++;
|
|
29917
30356
|
} catch {
|
|
@@ -29935,18 +30374,18 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29935
30374
|
};
|
|
29936
30375
|
const exeBinPath = findExeBin3();
|
|
29937
30376
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
29938
|
-
const binDir =
|
|
30377
|
+
const binDir = path45.dirname(exeBinPath);
|
|
29939
30378
|
let symlinkCount = 0;
|
|
29940
|
-
const rosterPath =
|
|
29941
|
-
if (
|
|
29942
|
-
const roster = JSON.parse(
|
|
30379
|
+
const rosterPath = path45.join(exeOsDir, "exe-employees.json");
|
|
30380
|
+
if (existsSync31(rosterPath)) {
|
|
30381
|
+
const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
|
|
29943
30382
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
29944
30383
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
29945
30384
|
for (const emp of roster) {
|
|
29946
30385
|
if (emp.name === coordinatorName) continue;
|
|
29947
30386
|
for (const suffix of ["", "-opencode"]) {
|
|
29948
|
-
const linkPath =
|
|
29949
|
-
if (
|
|
30387
|
+
const linkPath = path45.join(binDir, `${emp.name}${suffix}`);
|
|
30388
|
+
if (existsSync31(linkPath)) {
|
|
29950
30389
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
29951
30390
|
symlinkCount++;
|
|
29952
30391
|
}
|
|
@@ -29959,7 +30398,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
29959
30398
|
}
|
|
29960
30399
|
} catch {
|
|
29961
30400
|
}
|
|
29962
|
-
if (purge &&
|
|
30401
|
+
if (purge && existsSync31(exeOsDir)) {
|
|
29963
30402
|
if (!dryRun) {
|
|
29964
30403
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
29965
30404
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -29984,7 +30423,7 @@ async function checkForUpdateOnBoot() {
|
|
|
29984
30423
|
const config = await loadConfig2();
|
|
29985
30424
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
29986
30425
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
29987
|
-
const packageRoot =
|
|
30426
|
+
const packageRoot = path45.resolve(
|
|
29988
30427
|
new URL("../..", import.meta.url).pathname
|
|
29989
30428
|
);
|
|
29990
30429
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -30044,7 +30483,7 @@ async function runActivate(key) {
|
|
|
30044
30483
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
30045
30484
|
if (idTemplate) {
|
|
30046
30485
|
const idPath = identityPath2(name);
|
|
30047
|
-
const dir =
|
|
30486
|
+
const dir = path45.dirname(idPath);
|
|
30048
30487
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
30049
30488
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
30050
30489
|
}
|