@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/exe-boot.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");
|
|
@@ -268,7 +306,7 @@ var init_config = __esm({
|
|
|
268
306
|
|
|
269
307
|
// src/lib/employees.ts
|
|
270
308
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
271
|
-
import { existsSync as
|
|
309
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
272
310
|
import { execSync } from "child_process";
|
|
273
311
|
import path2 from "path";
|
|
274
312
|
import os2 from "os";
|
|
@@ -289,7 +327,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
289
327
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
290
328
|
}
|
|
291
329
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
292
|
-
if (!
|
|
330
|
+
if (!existsSync3(employeesPath)) {
|
|
293
331
|
return [];
|
|
294
332
|
}
|
|
295
333
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -304,7 +342,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
304
342
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
305
343
|
}
|
|
306
344
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
307
|
-
if (!
|
|
345
|
+
if (!existsSync3(employeesPath)) return [];
|
|
308
346
|
try {
|
|
309
347
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
310
348
|
} catch {
|
|
@@ -355,7 +393,7 @@ function registerBinSymlinks(name) {
|
|
|
355
393
|
for (const suffix of ["", "-opencode"]) {
|
|
356
394
|
const linkName = `${name}${suffix}`;
|
|
357
395
|
const linkPath = path2.join(binDir, linkName);
|
|
358
|
-
if (
|
|
396
|
+
if (existsSync3(linkPath)) {
|
|
359
397
|
skipped.push(linkName);
|
|
360
398
|
continue;
|
|
361
399
|
}
|
|
@@ -1020,13 +1058,50 @@ var init_database_adapter = __esm({
|
|
|
1020
1058
|
}
|
|
1021
1059
|
});
|
|
1022
1060
|
|
|
1061
|
+
// src/lib/daemon-auth.ts
|
|
1062
|
+
import crypto from "crypto";
|
|
1063
|
+
import path4 from "path";
|
|
1064
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1065
|
+
function normalizeToken(token) {
|
|
1066
|
+
if (!token) return null;
|
|
1067
|
+
const trimmed = token.trim();
|
|
1068
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1069
|
+
}
|
|
1070
|
+
function readDaemonToken() {
|
|
1071
|
+
try {
|
|
1072
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
1073
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
1074
|
+
} catch {
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
function ensureDaemonToken(seed) {
|
|
1079
|
+
const existing = readDaemonToken();
|
|
1080
|
+
if (existing) return existing;
|
|
1081
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1082
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1083
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
1084
|
+
`, "utf8");
|
|
1085
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1086
|
+
return token;
|
|
1087
|
+
}
|
|
1088
|
+
var DAEMON_TOKEN_PATH;
|
|
1089
|
+
var init_daemon_auth = __esm({
|
|
1090
|
+
"src/lib/daemon-auth.ts"() {
|
|
1091
|
+
"use strict";
|
|
1092
|
+
init_config();
|
|
1093
|
+
init_secure_files();
|
|
1094
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1023
1098
|
// src/lib/exe-daemon-client.ts
|
|
1024
1099
|
import net from "net";
|
|
1025
1100
|
import os4 from "os";
|
|
1026
1101
|
import { spawn } from "child_process";
|
|
1027
1102
|
import { randomUUID } from "crypto";
|
|
1028
|
-
import { existsSync as
|
|
1029
|
-
import
|
|
1103
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
1104
|
+
import path5 from "path";
|
|
1030
1105
|
import { fileURLToPath } from "url";
|
|
1031
1106
|
function handleData(chunk) {
|
|
1032
1107
|
_buffer += chunk.toString();
|
|
@@ -1054,9 +1129,9 @@ function handleData(chunk) {
|
|
|
1054
1129
|
}
|
|
1055
1130
|
}
|
|
1056
1131
|
function cleanupStaleFiles() {
|
|
1057
|
-
if (
|
|
1132
|
+
if (existsSync5(PID_PATH)) {
|
|
1058
1133
|
try {
|
|
1059
|
-
const pid = parseInt(
|
|
1134
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1060
1135
|
if (pid > 0) {
|
|
1061
1136
|
try {
|
|
1062
1137
|
process.kill(pid, 0);
|
|
@@ -1077,11 +1152,11 @@ function cleanupStaleFiles() {
|
|
|
1077
1152
|
}
|
|
1078
1153
|
}
|
|
1079
1154
|
function findPackageRoot() {
|
|
1080
|
-
let dir =
|
|
1081
|
-
const { root } =
|
|
1155
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1156
|
+
const { root } = path5.parse(dir);
|
|
1082
1157
|
while (dir !== root) {
|
|
1083
|
-
if (
|
|
1084
|
-
dir =
|
|
1158
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
1159
|
+
dir = path5.dirname(dir);
|
|
1085
1160
|
}
|
|
1086
1161
|
return null;
|
|
1087
1162
|
}
|
|
@@ -1107,16 +1182,17 @@ function spawnDaemon() {
|
|
|
1107
1182
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1108
1183
|
return;
|
|
1109
1184
|
}
|
|
1110
|
-
const daemonPath =
|
|
1111
|
-
if (!
|
|
1185
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1186
|
+
if (!existsSync5(daemonPath)) {
|
|
1112
1187
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1113
1188
|
`);
|
|
1114
1189
|
return;
|
|
1115
1190
|
}
|
|
1116
1191
|
const resolvedPath = daemonPath;
|
|
1192
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1117
1193
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1118
1194
|
`);
|
|
1119
|
-
const logPath =
|
|
1195
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1120
1196
|
let stderrFd = "ignore";
|
|
1121
1197
|
try {
|
|
1122
1198
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1134,7 +1210,8 @@ function spawnDaemon() {
|
|
|
1134
1210
|
TMUX_PANE: void 0,
|
|
1135
1211
|
// Prevents resolveExeSession() from scoping to one session
|
|
1136
1212
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1137
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1213
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1214
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1138
1215
|
}
|
|
1139
1216
|
});
|
|
1140
1217
|
child.unref();
|
|
@@ -1241,13 +1318,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1241
1318
|
return;
|
|
1242
1319
|
}
|
|
1243
1320
|
const id = randomUUID();
|
|
1321
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1244
1322
|
const timer = setTimeout(() => {
|
|
1245
1323
|
_pending.delete(id);
|
|
1246
1324
|
resolve({ error: "Request timeout" });
|
|
1247
1325
|
}, timeoutMs);
|
|
1248
1326
|
_pending.set(id, { resolve, timer });
|
|
1249
1327
|
try {
|
|
1250
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1328
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1251
1329
|
} catch {
|
|
1252
1330
|
clearTimeout(timer);
|
|
1253
1331
|
_pending.delete(id);
|
|
@@ -1258,17 +1336,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1258
1336
|
function isClientConnected() {
|
|
1259
1337
|
return _connected;
|
|
1260
1338
|
}
|
|
1261
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1339
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1262
1340
|
var init_exe_daemon_client = __esm({
|
|
1263
1341
|
"src/lib/exe-daemon-client.ts"() {
|
|
1264
1342
|
"use strict";
|
|
1265
1343
|
init_config();
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1344
|
+
init_daemon_auth();
|
|
1345
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1346
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1347
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1269
1348
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1270
1349
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1271
1350
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1351
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1272
1352
|
_socket = null;
|
|
1273
1353
|
_connected = false;
|
|
1274
1354
|
_buffer = "";
|
|
@@ -1847,6 +1927,7 @@ async function ensureSchema() {
|
|
|
1847
1927
|
project TEXT NOT NULL,
|
|
1848
1928
|
summary TEXT NOT NULL,
|
|
1849
1929
|
task_file TEXT,
|
|
1930
|
+
session_scope TEXT,
|
|
1850
1931
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1851
1932
|
created_at TEXT NOT NULL
|
|
1852
1933
|
);
|
|
@@ -1855,7 +1936,7 @@ async function ensureSchema() {
|
|
|
1855
1936
|
ON notifications(read);
|
|
1856
1937
|
|
|
1857
1938
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1858
|
-
ON notifications(agent_id);
|
|
1939
|
+
ON notifications(agent_id, session_scope);
|
|
1859
1940
|
|
|
1860
1941
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1861
1942
|
ON notifications(task_file);
|
|
@@ -1893,6 +1974,7 @@ async function ensureSchema() {
|
|
|
1893
1974
|
target_agent TEXT NOT NULL,
|
|
1894
1975
|
target_project TEXT,
|
|
1895
1976
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1977
|
+
session_scope TEXT,
|
|
1896
1978
|
content TEXT NOT NULL,
|
|
1897
1979
|
priority TEXT DEFAULT 'normal',
|
|
1898
1980
|
status TEXT DEFAULT 'pending',
|
|
@@ -1906,10 +1988,31 @@ async function ensureSchema() {
|
|
|
1906
1988
|
);
|
|
1907
1989
|
|
|
1908
1990
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1909
|
-
ON messages(target_agent, status);
|
|
1991
|
+
ON messages(target_agent, session_scope, status);
|
|
1910
1992
|
|
|
1911
1993
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1912
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1994
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1995
|
+
`);
|
|
1996
|
+
try {
|
|
1997
|
+
await client.execute({
|
|
1998
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1999
|
+
args: []
|
|
2000
|
+
});
|
|
2001
|
+
} catch {
|
|
2002
|
+
}
|
|
2003
|
+
try {
|
|
2004
|
+
await client.execute({
|
|
2005
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2006
|
+
args: []
|
|
2007
|
+
});
|
|
2008
|
+
} catch {
|
|
2009
|
+
}
|
|
2010
|
+
await client.executeMultiple(`
|
|
2011
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2012
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2013
|
+
|
|
2014
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2015
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1913
2016
|
`);
|
|
1914
2017
|
try {
|
|
1915
2018
|
await client.execute({
|
|
@@ -2493,6 +2596,13 @@ async function ensureSchema() {
|
|
|
2493
2596
|
} catch {
|
|
2494
2597
|
}
|
|
2495
2598
|
}
|
|
2599
|
+
try {
|
|
2600
|
+
await client.execute({
|
|
2601
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2602
|
+
args: []
|
|
2603
|
+
});
|
|
2604
|
+
} catch {
|
|
2605
|
+
}
|
|
2496
2606
|
}
|
|
2497
2607
|
async function disposeDatabase() {
|
|
2498
2608
|
if (_walCheckpointTimer) {
|
|
@@ -2734,14 +2844,14 @@ __export(keychain_exports, {
|
|
|
2734
2844
|
setMasterKey: () => setMasterKey
|
|
2735
2845
|
});
|
|
2736
2846
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2737
|
-
import { existsSync as
|
|
2738
|
-
import
|
|
2847
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2848
|
+
import path6 from "path";
|
|
2739
2849
|
import os5 from "os";
|
|
2740
2850
|
function getKeyDir() {
|
|
2741
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2851
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2742
2852
|
}
|
|
2743
2853
|
function getKeyPath() {
|
|
2744
|
-
return
|
|
2854
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2745
2855
|
}
|
|
2746
2856
|
async function tryKeytar() {
|
|
2747
2857
|
try {
|
|
@@ -2762,7 +2872,7 @@ async function getMasterKey() {
|
|
|
2762
2872
|
}
|
|
2763
2873
|
}
|
|
2764
2874
|
const keyPath = getKeyPath();
|
|
2765
|
-
if (!
|
|
2875
|
+
if (!existsSync6(keyPath)) {
|
|
2766
2876
|
process.stderr.write(
|
|
2767
2877
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2768
2878
|
`
|
|
@@ -2805,7 +2915,7 @@ async function deleteMasterKey() {
|
|
|
2805
2915
|
}
|
|
2806
2916
|
}
|
|
2807
2917
|
const keyPath = getKeyPath();
|
|
2808
|
-
if (
|
|
2918
|
+
if (existsSync6(keyPath)) {
|
|
2809
2919
|
await unlink(keyPath);
|
|
2810
2920
|
}
|
|
2811
2921
|
}
|
|
@@ -2907,6 +3017,7 @@ var shard_manager_exports = {};
|
|
|
2907
3017
|
__export(shard_manager_exports, {
|
|
2908
3018
|
disposeShards: () => disposeShards,
|
|
2909
3019
|
ensureShardSchema: () => ensureShardSchema,
|
|
3020
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2910
3021
|
getReadyShardClient: () => getReadyShardClient,
|
|
2911
3022
|
getShardClient: () => getShardClient,
|
|
2912
3023
|
getShardsDir: () => getShardsDir,
|
|
@@ -2915,15 +3026,18 @@ __export(shard_manager_exports, {
|
|
|
2915
3026
|
listShards: () => listShards,
|
|
2916
3027
|
shardExists: () => shardExists
|
|
2917
3028
|
});
|
|
2918
|
-
import
|
|
2919
|
-
import { existsSync as
|
|
3029
|
+
import path7 from "path";
|
|
3030
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2920
3031
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2921
3032
|
function initShardManager(encryptionKey) {
|
|
2922
3033
|
_encryptionKey = encryptionKey;
|
|
2923
|
-
if (!
|
|
2924
|
-
|
|
3034
|
+
if (!existsSync7(SHARDS_DIR)) {
|
|
3035
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2925
3036
|
}
|
|
2926
3037
|
_shardingEnabled = true;
|
|
3038
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3039
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3040
|
+
_evictionTimer.unref();
|
|
2927
3041
|
}
|
|
2928
3042
|
function isShardingEnabled() {
|
|
2929
3043
|
return _shardingEnabled;
|
|
@@ -2940,21 +3054,28 @@ function getShardClient(projectName) {
|
|
|
2940
3054
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2941
3055
|
}
|
|
2942
3056
|
const cached = _shards.get(safeName);
|
|
2943
|
-
if (cached)
|
|
2944
|
-
|
|
3057
|
+
if (cached) {
|
|
3058
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3059
|
+
return cached;
|
|
3060
|
+
}
|
|
3061
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3062
|
+
evictLRU();
|
|
3063
|
+
}
|
|
3064
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2945
3065
|
const client = createClient2({
|
|
2946
3066
|
url: `file:${dbPath}`,
|
|
2947
3067
|
encryptionKey: _encryptionKey
|
|
2948
3068
|
});
|
|
2949
3069
|
_shards.set(safeName, client);
|
|
3070
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2950
3071
|
return client;
|
|
2951
3072
|
}
|
|
2952
3073
|
function shardExists(projectName) {
|
|
2953
3074
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2954
|
-
return
|
|
3075
|
+
return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2955
3076
|
}
|
|
2956
3077
|
function listShards() {
|
|
2957
|
-
if (!
|
|
3078
|
+
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2958
3079
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2959
3080
|
}
|
|
2960
3081
|
async function ensureShardSchema(client) {
|
|
@@ -3006,6 +3127,8 @@ async function ensureShardSchema(client) {
|
|
|
3006
3127
|
for (const col of [
|
|
3007
3128
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
3008
3129
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3130
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3131
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
3009
3132
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
3010
3133
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
3011
3134
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -3143,21 +3266,69 @@ async function getReadyShardClient(projectName) {
|
|
|
3143
3266
|
await ensureShardSchema(client);
|
|
3144
3267
|
return client;
|
|
3145
3268
|
}
|
|
3269
|
+
function evictLRU() {
|
|
3270
|
+
let oldest = null;
|
|
3271
|
+
let oldestTime = Infinity;
|
|
3272
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3273
|
+
if (time < oldestTime) {
|
|
3274
|
+
oldestTime = time;
|
|
3275
|
+
oldest = name;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
if (oldest) {
|
|
3279
|
+
const client = _shards.get(oldest);
|
|
3280
|
+
if (client) {
|
|
3281
|
+
client.close();
|
|
3282
|
+
}
|
|
3283
|
+
_shards.delete(oldest);
|
|
3284
|
+
_shardLastAccess.delete(oldest);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
function evictIdleShards() {
|
|
3288
|
+
const now = Date.now();
|
|
3289
|
+
const toEvict = [];
|
|
3290
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3291
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3292
|
+
toEvict.push(name);
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
for (const name of toEvict) {
|
|
3296
|
+
const client = _shards.get(name);
|
|
3297
|
+
if (client) {
|
|
3298
|
+
client.close();
|
|
3299
|
+
}
|
|
3300
|
+
_shards.delete(name);
|
|
3301
|
+
_shardLastAccess.delete(name);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
function getOpenShardCount() {
|
|
3305
|
+
return _shards.size;
|
|
3306
|
+
}
|
|
3146
3307
|
function disposeShards() {
|
|
3308
|
+
if (_evictionTimer) {
|
|
3309
|
+
clearInterval(_evictionTimer);
|
|
3310
|
+
_evictionTimer = null;
|
|
3311
|
+
}
|
|
3147
3312
|
for (const [, client] of _shards) {
|
|
3148
3313
|
client.close();
|
|
3149
3314
|
}
|
|
3150
3315
|
_shards.clear();
|
|
3316
|
+
_shardLastAccess.clear();
|
|
3151
3317
|
_shardingEnabled = false;
|
|
3152
3318
|
_encryptionKey = null;
|
|
3153
3319
|
}
|
|
3154
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3320
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3155
3321
|
var init_shard_manager = __esm({
|
|
3156
3322
|
"src/lib/shard-manager.ts"() {
|
|
3157
3323
|
"use strict";
|
|
3158
3324
|
init_config();
|
|
3159
|
-
SHARDS_DIR =
|
|
3325
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
3326
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3327
|
+
MAX_OPEN_SHARDS = 10;
|
|
3328
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3160
3329
|
_shards = /* @__PURE__ */ new Map();
|
|
3330
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3331
|
+
_evictionTimer = null;
|
|
3161
3332
|
_encryptionKey = null;
|
|
3162
3333
|
_shardingEnabled = false;
|
|
3163
3334
|
}
|
|
@@ -3262,14 +3433,14 @@ __export(session_registry_exports, {
|
|
|
3262
3433
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
3263
3434
|
registerSession: () => registerSession
|
|
3264
3435
|
});
|
|
3265
|
-
import { readFileSync as
|
|
3436
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
3266
3437
|
import { execSync as execSync2 } from "child_process";
|
|
3267
|
-
import
|
|
3438
|
+
import path8 from "path";
|
|
3268
3439
|
import os6 from "os";
|
|
3269
3440
|
function registerSession(entry) {
|
|
3270
|
-
const dir =
|
|
3271
|
-
if (!
|
|
3272
|
-
|
|
3441
|
+
const dir = path8.dirname(REGISTRY_PATH);
|
|
3442
|
+
if (!existsSync8(dir)) {
|
|
3443
|
+
mkdirSync3(dir, { recursive: true });
|
|
3273
3444
|
}
|
|
3274
3445
|
const sessions = listSessions();
|
|
3275
3446
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -3278,11 +3449,11 @@ function registerSession(entry) {
|
|
|
3278
3449
|
} else {
|
|
3279
3450
|
sessions.push(entry);
|
|
3280
3451
|
}
|
|
3281
|
-
|
|
3452
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
3282
3453
|
}
|
|
3283
3454
|
function listSessions() {
|
|
3284
3455
|
try {
|
|
3285
|
-
const raw =
|
|
3456
|
+
const raw = readFileSync5(REGISTRY_PATH, "utf8");
|
|
3286
3457
|
return JSON.parse(raw);
|
|
3287
3458
|
} catch {
|
|
3288
3459
|
return [];
|
|
@@ -3303,7 +3474,7 @@ function pruneStaleSessions() {
|
|
|
3303
3474
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
3304
3475
|
const pruned = sessions.length - alive.length;
|
|
3305
3476
|
if (pruned > 0) {
|
|
3306
|
-
|
|
3477
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
3307
3478
|
}
|
|
3308
3479
|
return pruned;
|
|
3309
3480
|
}
|
|
@@ -3311,7 +3482,7 @@ var REGISTRY_PATH;
|
|
|
3311
3482
|
var init_session_registry = __esm({
|
|
3312
3483
|
"src/lib/session-registry.ts"() {
|
|
3313
3484
|
"use strict";
|
|
3314
|
-
REGISTRY_PATH =
|
|
3485
|
+
REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
3315
3486
|
}
|
|
3316
3487
|
});
|
|
3317
3488
|
|
|
@@ -3591,12 +3762,12 @@ var init_runtime_table = __esm({
|
|
|
3591
3762
|
});
|
|
3592
3763
|
|
|
3593
3764
|
// src/lib/agent-config.ts
|
|
3594
|
-
import { readFileSync as
|
|
3595
|
-
import
|
|
3765
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync9 } from "fs";
|
|
3766
|
+
import path9 from "path";
|
|
3596
3767
|
function loadAgentConfig() {
|
|
3597
|
-
if (!
|
|
3768
|
+
if (!existsSync9(AGENT_CONFIG_PATH)) return {};
|
|
3598
3769
|
try {
|
|
3599
|
-
return JSON.parse(
|
|
3770
|
+
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
3600
3771
|
} catch {
|
|
3601
3772
|
return {};
|
|
3602
3773
|
}
|
|
@@ -3615,7 +3786,8 @@ var init_agent_config = __esm({
|
|
|
3615
3786
|
"use strict";
|
|
3616
3787
|
init_config();
|
|
3617
3788
|
init_runtime_table();
|
|
3618
|
-
|
|
3789
|
+
init_secure_files();
|
|
3790
|
+
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
3619
3791
|
DEFAULT_MODELS = {
|
|
3620
3792
|
claude: "claude-opus-4",
|
|
3621
3793
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -3633,17 +3805,17 @@ __export(intercom_queue_exports, {
|
|
|
3633
3805
|
queueIntercom: () => queueIntercom,
|
|
3634
3806
|
readQueue: () => readQueue
|
|
3635
3807
|
});
|
|
3636
|
-
import { readFileSync as
|
|
3637
|
-
import
|
|
3808
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
3809
|
+
import path10 from "path";
|
|
3638
3810
|
import os7 from "os";
|
|
3639
3811
|
function ensureDir() {
|
|
3640
|
-
const dir =
|
|
3641
|
-
if (!
|
|
3812
|
+
const dir = path10.dirname(QUEUE_PATH);
|
|
3813
|
+
if (!existsSync10(dir)) mkdirSync4(dir, { recursive: true });
|
|
3642
3814
|
}
|
|
3643
3815
|
function readQueue() {
|
|
3644
3816
|
try {
|
|
3645
|
-
if (!
|
|
3646
|
-
return JSON.parse(
|
|
3817
|
+
if (!existsSync10(QUEUE_PATH)) return [];
|
|
3818
|
+
return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
|
|
3647
3819
|
} catch {
|
|
3648
3820
|
return [];
|
|
3649
3821
|
}
|
|
@@ -3651,7 +3823,7 @@ function readQueue() {
|
|
|
3651
3823
|
function writeQueue(queue) {
|
|
3652
3824
|
ensureDir();
|
|
3653
3825
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
3654
|
-
|
|
3826
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
3655
3827
|
renameSync3(tmp, QUEUE_PATH);
|
|
3656
3828
|
}
|
|
3657
3829
|
function queueIntercom(targetSession, reason) {
|
|
@@ -3743,10 +3915,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3743
3915
|
var init_intercom_queue = __esm({
|
|
3744
3916
|
"src/lib/intercom-queue.ts"() {
|
|
3745
3917
|
"use strict";
|
|
3746
|
-
QUEUE_PATH =
|
|
3918
|
+
QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
3747
3919
|
MAX_RETRIES2 = 5;
|
|
3748
3920
|
TTL_MS = 60 * 60 * 1e3;
|
|
3749
|
-
INTERCOM_LOG =
|
|
3921
|
+
INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3750
3922
|
}
|
|
3751
3923
|
});
|
|
3752
3924
|
|
|
@@ -3767,9 +3939,12 @@ __export(license_exports, {
|
|
|
3767
3939
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
3768
3940
|
validateLicense: () => validateLicense
|
|
3769
3941
|
});
|
|
3770
|
-
import { readFileSync as
|
|
3942
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
|
|
3771
3943
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3772
|
-
import
|
|
3944
|
+
import { createRequire as createRequire2 } from "module";
|
|
3945
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3946
|
+
import os8 from "os";
|
|
3947
|
+
import path11 from "path";
|
|
3773
3948
|
import { jwtVerify, importSPKI } from "jose";
|
|
3774
3949
|
async function fetchRetry(url, init) {
|
|
3775
3950
|
try {
|
|
@@ -3780,37 +3955,37 @@ async function fetchRetry(url, init) {
|
|
|
3780
3955
|
}
|
|
3781
3956
|
}
|
|
3782
3957
|
function loadDeviceId() {
|
|
3783
|
-
const deviceJsonPath =
|
|
3958
|
+
const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
|
|
3784
3959
|
try {
|
|
3785
|
-
if (
|
|
3786
|
-
const data = JSON.parse(
|
|
3960
|
+
if (existsSync11(deviceJsonPath)) {
|
|
3961
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
3787
3962
|
if (data.deviceId) return data.deviceId;
|
|
3788
3963
|
}
|
|
3789
3964
|
} catch {
|
|
3790
3965
|
}
|
|
3791
3966
|
try {
|
|
3792
|
-
if (
|
|
3793
|
-
const id2 =
|
|
3967
|
+
if (existsSync11(DEVICE_ID_PATH)) {
|
|
3968
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
3794
3969
|
if (id2) return id2;
|
|
3795
3970
|
}
|
|
3796
3971
|
} catch {
|
|
3797
3972
|
}
|
|
3798
3973
|
const id = randomUUID3();
|
|
3799
3974
|
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3800
|
-
|
|
3975
|
+
writeFileSync6(DEVICE_ID_PATH, id, "utf8");
|
|
3801
3976
|
return id;
|
|
3802
3977
|
}
|
|
3803
3978
|
function loadLicense() {
|
|
3804
3979
|
try {
|
|
3805
|
-
if (!
|
|
3806
|
-
return
|
|
3980
|
+
if (!existsSync11(LICENSE_PATH)) return null;
|
|
3981
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
3807
3982
|
} catch {
|
|
3808
3983
|
return null;
|
|
3809
3984
|
}
|
|
3810
3985
|
}
|
|
3811
3986
|
function saveLicense(apiKey) {
|
|
3812
3987
|
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3813
|
-
|
|
3988
|
+
writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3814
3989
|
}
|
|
3815
3990
|
async function verifyLicenseJwt(token) {
|
|
3816
3991
|
try {
|
|
@@ -3836,8 +4011,8 @@ async function verifyLicenseJwt(token) {
|
|
|
3836
4011
|
}
|
|
3837
4012
|
async function getCachedLicense() {
|
|
3838
4013
|
try {
|
|
3839
|
-
if (!
|
|
3840
|
-
const raw = JSON.parse(
|
|
4014
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
4015
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
3841
4016
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3842
4017
|
return await verifyLicenseJwt(raw.token);
|
|
3843
4018
|
} catch {
|
|
@@ -3846,8 +4021,8 @@ async function getCachedLicense() {
|
|
|
3846
4021
|
}
|
|
3847
4022
|
function readCachedToken() {
|
|
3848
4023
|
try {
|
|
3849
|
-
if (!
|
|
3850
|
-
const raw = JSON.parse(
|
|
4024
|
+
if (!existsSync11(CACHE_PATH)) return null;
|
|
4025
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
3851
4026
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3852
4027
|
} catch {
|
|
3853
4028
|
return null;
|
|
@@ -3881,57 +4056,131 @@ function getRawCachedPlan() {
|
|
|
3881
4056
|
}
|
|
3882
4057
|
function cacheResponse(token) {
|
|
3883
4058
|
try {
|
|
3884
|
-
|
|
4059
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
3885
4060
|
} catch {
|
|
3886
4061
|
}
|
|
3887
4062
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
4063
|
+
function loadPrismaForLicense() {
|
|
4064
|
+
if (_prismaFailed) return null;
|
|
4065
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
4066
|
+
if (!dbUrl) {
|
|
4067
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
|
|
4068
|
+
if (!existsSync11(path11.join(exeDbRoot, "package.json"))) {
|
|
4069
|
+
_prismaFailed = true;
|
|
4070
|
+
return null;
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
if (!_prismaPromise) {
|
|
4074
|
+
_prismaPromise = (async () => {
|
|
4075
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
4076
|
+
if (explicitPath) {
|
|
4077
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
4078
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
4079
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
4080
|
+
return new Ctor2();
|
|
4081
|
+
}
|
|
4082
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
|
|
4083
|
+
const req = createRequire2(path11.join(exeDbRoot, "package.json"));
|
|
4084
|
+
const entry = req.resolve("@prisma/client");
|
|
4085
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
4086
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4087
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
4088
|
+
return new Ctor();
|
|
4089
|
+
})().catch((err) => {
|
|
4090
|
+
_prismaFailed = true;
|
|
4091
|
+
_prismaPromise = null;
|
|
4092
|
+
throw err;
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
return _prismaPromise;
|
|
4096
|
+
}
|
|
4097
|
+
async function validateViaPostgres(apiKey) {
|
|
4098
|
+
const loader = loadPrismaForLicense();
|
|
4099
|
+
if (!loader) return null;
|
|
4100
|
+
try {
|
|
4101
|
+
const prisma = await loader;
|
|
4102
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
4103
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
4104
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
4105
|
+
apiKey
|
|
4106
|
+
);
|
|
4107
|
+
if (!rows || rows.length === 0) return null;
|
|
4108
|
+
const row = rows[0];
|
|
4109
|
+
if (row.status !== "active") return null;
|
|
4110
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
4111
|
+
const plan = row.plan;
|
|
4112
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
4113
|
+
return {
|
|
4114
|
+
valid: true,
|
|
4115
|
+
plan,
|
|
4116
|
+
email: row.email,
|
|
4117
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
4118
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
4119
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
4120
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
4121
|
+
};
|
|
4122
|
+
} catch {
|
|
4123
|
+
return null;
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
3890
4127
|
try {
|
|
3891
4128
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3892
4129
|
method: "POST",
|
|
3893
4130
|
headers: { "Content-Type": "application/json" },
|
|
3894
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
4131
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
3895
4132
|
signal: AbortSignal.timeout(1e4)
|
|
3896
4133
|
});
|
|
3897
|
-
if (res.ok)
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
}
|
|
3906
|
-
if (data.token) {
|
|
3907
|
-
cacheResponse(data.token);
|
|
3908
|
-
const verified = await verifyLicenseJwt(data.token);
|
|
3909
|
-
if (verified) return verified;
|
|
3910
|
-
}
|
|
3911
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3912
|
-
return {
|
|
3913
|
-
valid: data.valid,
|
|
3914
|
-
plan: data.plan,
|
|
3915
|
-
email: data.email,
|
|
3916
|
-
expiresAt: data.expiresAt,
|
|
3917
|
-
deviceLimit: limits.devices,
|
|
3918
|
-
employeeLimit: limits.employees,
|
|
3919
|
-
memoryLimit: limits.memories
|
|
3920
|
-
};
|
|
4134
|
+
if (!res.ok) return null;
|
|
4135
|
+
const data = await res.json();
|
|
4136
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
4137
|
+
if (!data.valid) return null;
|
|
4138
|
+
if (data.token) {
|
|
4139
|
+
cacheResponse(data.token);
|
|
4140
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
4141
|
+
if (verified) return verified;
|
|
3921
4142
|
}
|
|
3922
|
-
const
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
4143
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
4144
|
+
return {
|
|
4145
|
+
valid: data.valid,
|
|
4146
|
+
plan: data.plan,
|
|
4147
|
+
email: data.email,
|
|
4148
|
+
expiresAt: data.expiresAt,
|
|
4149
|
+
deviceLimit: limits.devices,
|
|
4150
|
+
employeeLimit: limits.employees,
|
|
4151
|
+
memoryLimit: limits.memories
|
|
4152
|
+
};
|
|
3927
4153
|
} catch {
|
|
3928
|
-
|
|
3929
|
-
if (cached) return cached;
|
|
3930
|
-
const rawFallback = getRawCachedPlan();
|
|
3931
|
-
if (rawFallback) return rawFallback;
|
|
3932
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
4154
|
+
return null;
|
|
3933
4155
|
}
|
|
3934
4156
|
}
|
|
4157
|
+
async function validateLicense(apiKey, deviceId) {
|
|
4158
|
+
const did = deviceId ?? loadDeviceId();
|
|
4159
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
4160
|
+
if (pgResult) {
|
|
4161
|
+
try {
|
|
4162
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
4163
|
+
} catch {
|
|
4164
|
+
}
|
|
4165
|
+
return pgResult;
|
|
4166
|
+
}
|
|
4167
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
4168
|
+
if (cfResult) return cfResult;
|
|
4169
|
+
const cached = await getCachedLicense();
|
|
4170
|
+
if (cached) return cached;
|
|
4171
|
+
try {
|
|
4172
|
+
if (existsSync11(CACHE_PATH)) {
|
|
4173
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
4174
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
4175
|
+
return raw.pgLicense;
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
} catch {
|
|
4179
|
+
}
|
|
4180
|
+
const rawFallback = getRawCachedPlan();
|
|
4181
|
+
if (rawFallback) return rawFallback;
|
|
4182
|
+
return { ...FREE_LICENSE, valid: false };
|
|
4183
|
+
}
|
|
3935
4184
|
function getCacheAgeMs() {
|
|
3936
4185
|
try {
|
|
3937
4186
|
const { statSync: statSync2 } = __require("fs");
|
|
@@ -3945,9 +4194,9 @@ async function checkLicense() {
|
|
|
3945
4194
|
let key = loadLicense();
|
|
3946
4195
|
if (!key) {
|
|
3947
4196
|
try {
|
|
3948
|
-
const configPath =
|
|
3949
|
-
if (
|
|
3950
|
-
const raw = JSON.parse(
|
|
4197
|
+
const configPath = path11.join(EXE_AI_DIR, "config.json");
|
|
4198
|
+
if (existsSync11(configPath)) {
|
|
4199
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
3951
4200
|
const cloud = raw.cloud;
|
|
3952
4201
|
if (cloud?.apiKey) {
|
|
3953
4202
|
key = cloud.apiKey;
|
|
@@ -4101,14 +4350,14 @@ function stopLicenseRevalidation() {
|
|
|
4101
4350
|
_revalTimer = null;
|
|
4102
4351
|
}
|
|
4103
4352
|
}
|
|
4104
|
-
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;
|
|
4353
|
+
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;
|
|
4105
4354
|
var init_license = __esm({
|
|
4106
4355
|
"src/lib/license.ts"() {
|
|
4107
4356
|
"use strict";
|
|
4108
4357
|
init_config();
|
|
4109
|
-
LICENSE_PATH =
|
|
4110
|
-
CACHE_PATH =
|
|
4111
|
-
DEVICE_ID_PATH =
|
|
4358
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
4359
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4360
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4112
4361
|
API_BASE = "https://askexe.com/cloud";
|
|
4113
4362
|
RETRY_DELAY_MS = 500;
|
|
4114
4363
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -4132,18 +4381,20 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
4132
4381
|
employeeLimit: 1,
|
|
4133
4382
|
memoryLimit: 5e3
|
|
4134
4383
|
};
|
|
4384
|
+
_prismaPromise = null;
|
|
4385
|
+
_prismaFailed = false;
|
|
4135
4386
|
CACHE_MAX_AGE_MS = 36e5;
|
|
4136
4387
|
_revalTimer = null;
|
|
4137
4388
|
}
|
|
4138
4389
|
});
|
|
4139
4390
|
|
|
4140
4391
|
// src/lib/plan-limits.ts
|
|
4141
|
-
import { readFileSync as
|
|
4142
|
-
import
|
|
4392
|
+
import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
|
|
4393
|
+
import path12 from "path";
|
|
4143
4394
|
function getLicenseSync() {
|
|
4144
4395
|
try {
|
|
4145
|
-
if (!
|
|
4146
|
-
const raw = JSON.parse(
|
|
4396
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
4397
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
4147
4398
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4148
4399
|
const parts = raw.token.split(".");
|
|
4149
4400
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4181,8 +4432,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4181
4432
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
4182
4433
|
let count = 0;
|
|
4183
4434
|
try {
|
|
4184
|
-
if (
|
|
4185
|
-
const raw =
|
|
4435
|
+
if (existsSync12(filePath)) {
|
|
4436
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
4186
4437
|
const employees = JSON.parse(raw);
|
|
4187
4438
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4188
4439
|
}
|
|
@@ -4211,29 +4462,30 @@ var init_plan_limits = __esm({
|
|
|
4211
4462
|
this.name = "PlanLimitError";
|
|
4212
4463
|
}
|
|
4213
4464
|
};
|
|
4214
|
-
CACHE_PATH2 =
|
|
4465
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
4215
4466
|
}
|
|
4216
4467
|
});
|
|
4217
4468
|
|
|
4218
4469
|
// src/lib/notifications.ts
|
|
4219
|
-
import
|
|
4220
|
-
import
|
|
4221
|
-
import
|
|
4470
|
+
import crypto2 from "crypto";
|
|
4471
|
+
import path13 from "path";
|
|
4472
|
+
import os9 from "os";
|
|
4222
4473
|
import {
|
|
4223
|
-
readFileSync as
|
|
4474
|
+
readFileSync as readFileSync10,
|
|
4224
4475
|
readdirSync as readdirSync2,
|
|
4225
4476
|
unlinkSync as unlinkSync3,
|
|
4226
|
-
existsSync as
|
|
4477
|
+
existsSync as existsSync13,
|
|
4227
4478
|
rmdirSync
|
|
4228
4479
|
} from "fs";
|
|
4229
4480
|
async function writeNotification(notification) {
|
|
4230
4481
|
try {
|
|
4231
4482
|
const client = getClient();
|
|
4232
|
-
const id =
|
|
4483
|
+
const id = crypto2.randomUUID();
|
|
4233
4484
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4485
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
4234
4486
|
await client.execute({
|
|
4235
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
4236
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4487
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
4488
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4237
4489
|
args: [
|
|
4238
4490
|
id,
|
|
4239
4491
|
notification.agentId,
|
|
@@ -4242,6 +4494,7 @@ async function writeNotification(notification) {
|
|
|
4242
4494
|
notification.project,
|
|
4243
4495
|
notification.summary,
|
|
4244
4496
|
notification.taskFile ?? null,
|
|
4497
|
+
sessionScope,
|
|
4245
4498
|
now
|
|
4246
4499
|
]
|
|
4247
4500
|
});
|
|
@@ -4250,21 +4503,22 @@ async function writeNotification(notification) {
|
|
|
4250
4503
|
`);
|
|
4251
4504
|
}
|
|
4252
4505
|
}
|
|
4253
|
-
async function readUnreadNotifications(agentFilter) {
|
|
4506
|
+
async function readUnreadNotifications(agentFilter, sessionScope) {
|
|
4254
4507
|
try {
|
|
4255
4508
|
const client = getClient();
|
|
4256
4509
|
const conditions = ["read = 0"];
|
|
4257
4510
|
const args = [];
|
|
4511
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4258
4512
|
if (agentFilter) {
|
|
4259
4513
|
conditions.push("agent_id = ?");
|
|
4260
4514
|
args.push(agentFilter);
|
|
4261
4515
|
}
|
|
4262
4516
|
const result = await client.execute({
|
|
4263
|
-
sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, created_at
|
|
4517
|
+
sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, session_scope, created_at
|
|
4264
4518
|
FROM notifications
|
|
4265
|
-
WHERE ${conditions.join(" AND ")}
|
|
4519
|
+
WHERE ${conditions.join(" AND ")}${scope.sql}
|
|
4266
4520
|
ORDER BY created_at ASC`,
|
|
4267
|
-
args
|
|
4521
|
+
args: [...args, ...scope.args]
|
|
4268
4522
|
});
|
|
4269
4523
|
return result.rows.map((r) => ({
|
|
4270
4524
|
id: String(r.id),
|
|
@@ -4274,6 +4528,7 @@ async function readUnreadNotifications(agentFilter) {
|
|
|
4274
4528
|
project: String(r.project),
|
|
4275
4529
|
summary: String(r.summary),
|
|
4276
4530
|
taskFile: r.task_file ? String(r.task_file) : void 0,
|
|
4531
|
+
sessionScope: r.session_scope == null ? null : String(r.session_scope),
|
|
4277
4532
|
timestamp: String(r.created_at),
|
|
4278
4533
|
read: false
|
|
4279
4534
|
}));
|
|
@@ -4281,54 +4536,60 @@ async function readUnreadNotifications(agentFilter) {
|
|
|
4281
4536
|
return [];
|
|
4282
4537
|
}
|
|
4283
4538
|
}
|
|
4284
|
-
async function markAsRead(ids) {
|
|
4539
|
+
async function markAsRead(ids, sessionScope) {
|
|
4285
4540
|
if (ids.length === 0) return;
|
|
4286
4541
|
try {
|
|
4287
4542
|
const client = getClient();
|
|
4288
4543
|
const placeholders = ids.map(() => "?").join(", ");
|
|
4544
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4289
4545
|
await client.execute({
|
|
4290
|
-
sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})`,
|
|
4291
|
-
args: ids
|
|
4546
|
+
sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})${scope.sql}`,
|
|
4547
|
+
args: [...ids, ...scope.args]
|
|
4292
4548
|
});
|
|
4293
4549
|
} catch {
|
|
4294
4550
|
}
|
|
4295
4551
|
}
|
|
4296
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
4552
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
4297
4553
|
try {
|
|
4298
4554
|
const client = getClient();
|
|
4555
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4299
4556
|
await client.execute({
|
|
4300
|
-
sql:
|
|
4301
|
-
|
|
4557
|
+
sql: `UPDATE notifications SET read = 1
|
|
4558
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
4559
|
+
args: [taskFile, ...scope.args]
|
|
4302
4560
|
});
|
|
4303
4561
|
} catch {
|
|
4304
4562
|
}
|
|
4305
4563
|
}
|
|
4306
|
-
async function cleanupOldNotifications(daysOld = CLEANUP_DAYS) {
|
|
4564
|
+
async function cleanupOldNotifications(daysOld = CLEANUP_DAYS, sessionScope) {
|
|
4307
4565
|
try {
|
|
4308
4566
|
const client = getClient();
|
|
4309
4567
|
const cutoff = new Date(
|
|
4310
4568
|
Date.now() - daysOld * 24 * 60 * 60 * 1e3
|
|
4311
4569
|
).toISOString();
|
|
4570
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4312
4571
|
const result = await client.execute({
|
|
4313
|
-
sql:
|
|
4314
|
-
args: [cutoff]
|
|
4572
|
+
sql: `DELETE FROM notifications WHERE created_at < ?${scope.sql}`,
|
|
4573
|
+
args: [cutoff, ...scope.args]
|
|
4315
4574
|
});
|
|
4316
4575
|
return result.rowsAffected;
|
|
4317
4576
|
} catch {
|
|
4318
4577
|
return 0;
|
|
4319
4578
|
}
|
|
4320
4579
|
}
|
|
4321
|
-
async function markDoneTaskNotificationsAsRead() {
|
|
4580
|
+
async function markDoneTaskNotificationsAsRead(sessionScope) {
|
|
4322
4581
|
try {
|
|
4323
4582
|
const client = getClient();
|
|
4583
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4324
4584
|
const result = await client.execute({
|
|
4325
4585
|
sql: `UPDATE notifications SET read = 1
|
|
4326
4586
|
WHERE read = 0
|
|
4327
4587
|
AND task_file IS NOT NULL
|
|
4588
|
+
${scope.sql}
|
|
4328
4589
|
AND task_file IN (
|
|
4329
|
-
SELECT task_file FROM tasks WHERE status = 'done'
|
|
4590
|
+
SELECT task_file FROM tasks WHERE status = 'done'${scope.sql}
|
|
4330
4591
|
)`,
|
|
4331
|
-
args: []
|
|
4592
|
+
args: [...scope.args, ...scope.args]
|
|
4332
4593
|
});
|
|
4333
4594
|
return result.rowsAffected;
|
|
4334
4595
|
} catch {
|
|
@@ -4336,9 +4597,9 @@ async function markDoneTaskNotificationsAsRead() {
|
|
|
4336
4597
|
}
|
|
4337
4598
|
}
|
|
4338
4599
|
async function migrateJsonNotifications() {
|
|
4339
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
4340
|
-
const notifDir =
|
|
4341
|
-
if (!
|
|
4600
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path13.join(os9.homedir(), ".exe-os");
|
|
4601
|
+
const notifDir = path13.join(base, "notifications");
|
|
4602
|
+
if (!existsSync13(notifDir)) return 0;
|
|
4342
4603
|
let migrated = 0;
|
|
4343
4604
|
try {
|
|
4344
4605
|
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -4346,19 +4607,20 @@ async function migrateJsonNotifications() {
|
|
|
4346
4607
|
const client = getClient();
|
|
4347
4608
|
for (const file of files) {
|
|
4348
4609
|
try {
|
|
4349
|
-
const filePath =
|
|
4350
|
-
const data = JSON.parse(
|
|
4610
|
+
const filePath = path13.join(notifDir, file);
|
|
4611
|
+
const data = JSON.parse(readFileSync10(filePath, "utf8"));
|
|
4351
4612
|
await client.execute({
|
|
4352
|
-
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
4353
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4613
|
+
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
4614
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4354
4615
|
args: [
|
|
4355
|
-
|
|
4616
|
+
crypto2.randomUUID(),
|
|
4356
4617
|
data.agentId ?? "unknown",
|
|
4357
4618
|
data.agentRole ?? "unknown",
|
|
4358
4619
|
data.event ?? "session_summary",
|
|
4359
4620
|
data.project ?? "unknown",
|
|
4360
4621
|
data.summary ?? "",
|
|
4361
4622
|
data.taskFile ?? null,
|
|
4623
|
+
null,
|
|
4362
4624
|
data.read ? 1 : 0,
|
|
4363
4625
|
data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4364
4626
|
]
|
|
@@ -4384,6 +4646,7 @@ var init_notifications = __esm({
|
|
|
4384
4646
|
"src/lib/notifications.ts"() {
|
|
4385
4647
|
"use strict";
|
|
4386
4648
|
init_database();
|
|
4649
|
+
init_task_scope();
|
|
4387
4650
|
CLEANUP_DAYS = 7;
|
|
4388
4651
|
}
|
|
4389
4652
|
});
|
|
@@ -4401,7 +4664,7 @@ __export(session_kill_telemetry_exports, {
|
|
|
4401
4664
|
recordSessionKill: () => recordSessionKill,
|
|
4402
4665
|
sumTokensSavedSince: () => sumTokensSavedSince
|
|
4403
4666
|
});
|
|
4404
|
-
import
|
|
4667
|
+
import crypto3 from "crypto";
|
|
4405
4668
|
async function recordSessionKill(input) {
|
|
4406
4669
|
try {
|
|
4407
4670
|
const client = getClient();
|
|
@@ -4411,7 +4674,7 @@ async function recordSessionKill(input) {
|
|
|
4411
4674
|
ticks_idle, estimated_tokens_saved)
|
|
4412
4675
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
4413
4676
|
args: [
|
|
4414
|
-
|
|
4677
|
+
crypto3.randomUUID(),
|
|
4415
4678
|
input.sessionName,
|
|
4416
4679
|
input.agentId,
|
|
4417
4680
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4493,12 +4756,12 @@ var init_session_kill_telemetry = __esm({
|
|
|
4493
4756
|
});
|
|
4494
4757
|
|
|
4495
4758
|
// src/lib/tasks-crud.ts
|
|
4496
|
-
import
|
|
4497
|
-
import
|
|
4498
|
-
import
|
|
4759
|
+
import crypto4 from "crypto";
|
|
4760
|
+
import path14 from "path";
|
|
4761
|
+
import os10 from "os";
|
|
4499
4762
|
import { execSync as execSync5 } from "child_process";
|
|
4500
4763
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4501
|
-
import { existsSync as
|
|
4764
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
4502
4765
|
async function writeCheckpoint(input) {
|
|
4503
4766
|
const client = getClient();
|
|
4504
4767
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -4614,7 +4877,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
4614
4877
|
}
|
|
4615
4878
|
async function createTaskCore(input) {
|
|
4616
4879
|
const client = getClient();
|
|
4617
|
-
const id =
|
|
4880
|
+
const id = crypto4.randomUUID();
|
|
4618
4881
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4619
4882
|
const slug = slugify(input.title);
|
|
4620
4883
|
let earlySessionScope = null;
|
|
@@ -4673,8 +4936,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4673
4936
|
}
|
|
4674
4937
|
if (input.baseDir) {
|
|
4675
4938
|
try {
|
|
4676
|
-
await mkdir4(
|
|
4677
|
-
await mkdir4(
|
|
4939
|
+
await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4940
|
+
await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4678
4941
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4679
4942
|
await ensureGitignoreExe(input.baseDir);
|
|
4680
4943
|
} catch {
|
|
@@ -4710,13 +4973,19 @@ ${laneWarning}` : laneWarning;
|
|
|
4710
4973
|
});
|
|
4711
4974
|
if (input.baseDir) {
|
|
4712
4975
|
try {
|
|
4713
|
-
const EXE_OS_DIR =
|
|
4714
|
-
const mdPath =
|
|
4715
|
-
const mdDir =
|
|
4716
|
-
if (!
|
|
4976
|
+
const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
|
|
4977
|
+
const mdPath = path14.join(EXE_OS_DIR, taskFile);
|
|
4978
|
+
const mdDir = path14.dirname(mdPath);
|
|
4979
|
+
if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4717
4980
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4718
4981
|
const mdContent = `# ${input.title}
|
|
4719
4982
|
|
|
4983
|
+
## MANDATORY: When done
|
|
4984
|
+
|
|
4985
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4986
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4987
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4988
|
+
|
|
4720
4989
|
**ID:** ${id}
|
|
4721
4990
|
**Status:** ${initialStatus}
|
|
4722
4991
|
**Priority:** ${input.priority}
|
|
@@ -4730,12 +4999,6 @@ ${laneWarning}` : laneWarning;
|
|
|
4730
4999
|
## Context
|
|
4731
5000
|
|
|
4732
5001
|
${input.context}
|
|
4733
|
-
|
|
4734
|
-
## MANDATORY: When done
|
|
4735
|
-
|
|
4736
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
4737
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4738
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4739
5002
|
`;
|
|
4740
5003
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
4741
5004
|
} catch (err) {
|
|
@@ -4984,7 +5247,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4984
5247
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4985
5248
|
} catch {
|
|
4986
5249
|
}
|
|
4987
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5250
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4988
5251
|
try {
|
|
4989
5252
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4990
5253
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -5013,9 +5276,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5013
5276
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5014
5277
|
}
|
|
5015
5278
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5016
|
-
const archPath =
|
|
5279
|
+
const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5017
5280
|
try {
|
|
5018
|
-
if (
|
|
5281
|
+
if (existsSync14(archPath)) return;
|
|
5019
5282
|
const template = [
|
|
5020
5283
|
`# ${projectName} \u2014 System Architecture`,
|
|
5021
5284
|
"",
|
|
@@ -5048,10 +5311,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5048
5311
|
}
|
|
5049
5312
|
}
|
|
5050
5313
|
async function ensureGitignoreExe(baseDir) {
|
|
5051
|
-
const gitignorePath =
|
|
5314
|
+
const gitignorePath = path14.join(baseDir, ".gitignore");
|
|
5052
5315
|
try {
|
|
5053
|
-
if (
|
|
5054
|
-
const content =
|
|
5316
|
+
if (existsSync14(gitignorePath)) {
|
|
5317
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5055
5318
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5056
5319
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5057
5320
|
} else {
|
|
@@ -5082,58 +5345,42 @@ var init_tasks_crud = __esm({
|
|
|
5082
5345
|
});
|
|
5083
5346
|
|
|
5084
5347
|
// src/lib/tasks-review.ts
|
|
5085
|
-
import
|
|
5086
|
-
import { existsSync as
|
|
5348
|
+
import path15 from "path";
|
|
5349
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5087
5350
|
async function countPendingReviews(sessionScope) {
|
|
5088
5351
|
const client = getClient();
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
args: [sessionScope]
|
|
5093
|
-
});
|
|
5094
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5095
|
-
}
|
|
5352
|
+
const scope = strictSessionScopeFilter(
|
|
5353
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5354
|
+
);
|
|
5096
5355
|
const result = await client.execute({
|
|
5097
|
-
sql:
|
|
5098
|
-
|
|
5356
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5357
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
5358
|
+
args: [...scope.args]
|
|
5099
5359
|
});
|
|
5100
5360
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5101
5361
|
}
|
|
5102
5362
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
5103
5363
|
const client = getClient();
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
5108
|
-
AND session_scope = ?`,
|
|
5109
|
-
args: [sinceIso, sessionScope]
|
|
5110
|
-
});
|
|
5111
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5112
|
-
}
|
|
5364
|
+
const scope = strictSessionScopeFilter(
|
|
5365
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5366
|
+
);
|
|
5113
5367
|
const result = await client.execute({
|
|
5114
5368
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5115
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
5116
|
-
args: [sinceIso]
|
|
5369
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
5370
|
+
args: [sinceIso, ...scope.args]
|
|
5117
5371
|
});
|
|
5118
5372
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5119
5373
|
}
|
|
5120
5374
|
async function listPendingReviews(limit, sessionScope) {
|
|
5121
5375
|
const client = getClient();
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
WHERE status = 'needs_review'
|
|
5126
|
-
AND session_scope = ?
|
|
5127
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
5128
|
-
args: [sessionScope, limit]
|
|
5129
|
-
});
|
|
5130
|
-
return result2.rows;
|
|
5131
|
-
}
|
|
5376
|
+
const scope = strictSessionScopeFilter(
|
|
5377
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5378
|
+
);
|
|
5132
5379
|
const result = await client.execute({
|
|
5133
5380
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
5134
|
-
WHERE status = 'needs_review'
|
|
5381
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
5135
5382
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
5136
|
-
args: [limit]
|
|
5383
|
+
args: [...scope.args, limit]
|
|
5137
5384
|
});
|
|
5138
5385
|
return result.rows;
|
|
5139
5386
|
}
|
|
@@ -5145,7 +5392,7 @@ async function cleanupOrphanedReviews() {
|
|
|
5145
5392
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
5146
5393
|
AND assigned_by = 'system'
|
|
5147
5394
|
AND title LIKE 'Review:%'
|
|
5148
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
5395
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
5149
5396
|
args: [now]
|
|
5150
5397
|
});
|
|
5151
5398
|
const r1b = await client.execute({
|
|
@@ -5264,11 +5511,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5264
5511
|
);
|
|
5265
5512
|
}
|
|
5266
5513
|
try {
|
|
5267
|
-
const cacheDir =
|
|
5268
|
-
if (
|
|
5514
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
5515
|
+
if (existsSync15(cacheDir)) {
|
|
5269
5516
|
for (const f of readdirSync3(cacheDir)) {
|
|
5270
5517
|
if (f.startsWith("review-notified-")) {
|
|
5271
|
-
unlinkSync4(
|
|
5518
|
+
unlinkSync4(path15.join(cacheDir, f));
|
|
5272
5519
|
}
|
|
5273
5520
|
}
|
|
5274
5521
|
}
|
|
@@ -5285,11 +5532,12 @@ var init_tasks_review = __esm({
|
|
|
5285
5532
|
init_tmux_routing();
|
|
5286
5533
|
init_session_key();
|
|
5287
5534
|
init_state_bus();
|
|
5535
|
+
init_task_scope();
|
|
5288
5536
|
}
|
|
5289
5537
|
});
|
|
5290
5538
|
|
|
5291
5539
|
// src/lib/tasks-chain.ts
|
|
5292
|
-
import
|
|
5540
|
+
import path16 from "path";
|
|
5293
5541
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5294
5542
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5295
5543
|
const client = getClient();
|
|
@@ -5306,7 +5554,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5306
5554
|
});
|
|
5307
5555
|
for (const ur of unblockedRows.rows) {
|
|
5308
5556
|
try {
|
|
5309
|
-
const ubFile =
|
|
5557
|
+
const ubFile = path16.join(baseDir, String(ur.task_file));
|
|
5310
5558
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5311
5559
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5312
5560
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5341,7 +5589,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
5341
5589
|
const scScope = sessionScopeFilter();
|
|
5342
5590
|
const remaining = await client.execute({
|
|
5343
5591
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5344
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
5592
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
5345
5593
|
args: [parentTaskId, ...scScope.args]
|
|
5346
5594
|
});
|
|
5347
5595
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -5380,7 +5628,7 @@ __export(project_name_exports, {
|
|
|
5380
5628
|
getProjectName: () => getProjectName
|
|
5381
5629
|
});
|
|
5382
5630
|
import { execSync as execSync6 } from "child_process";
|
|
5383
|
-
import
|
|
5631
|
+
import path17 from "path";
|
|
5384
5632
|
function getProjectName(cwd) {
|
|
5385
5633
|
const dir = cwd ?? process.cwd();
|
|
5386
5634
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -5393,7 +5641,7 @@ function getProjectName(cwd) {
|
|
|
5393
5641
|
timeout: 2e3,
|
|
5394
5642
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5395
5643
|
}).trim();
|
|
5396
|
-
repoRoot =
|
|
5644
|
+
repoRoot = path17.dirname(gitCommonDir);
|
|
5397
5645
|
} catch {
|
|
5398
5646
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
5399
5647
|
cwd: dir,
|
|
@@ -5402,11 +5650,11 @@ function getProjectName(cwd) {
|
|
|
5402
5650
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5403
5651
|
}).trim();
|
|
5404
5652
|
}
|
|
5405
|
-
_cached2 =
|
|
5653
|
+
_cached2 = path17.basename(repoRoot);
|
|
5406
5654
|
_cachedCwd = dir;
|
|
5407
5655
|
return _cached2;
|
|
5408
5656
|
} catch {
|
|
5409
|
-
_cached2 =
|
|
5657
|
+
_cached2 = path17.basename(dir);
|
|
5410
5658
|
_cachedCwd = dir;
|
|
5411
5659
|
return _cached2;
|
|
5412
5660
|
}
|
|
@@ -5553,10 +5801,10 @@ var init_tasks_notify = __esm({
|
|
|
5553
5801
|
});
|
|
5554
5802
|
|
|
5555
5803
|
// src/lib/behaviors.ts
|
|
5556
|
-
import
|
|
5804
|
+
import crypto5 from "crypto";
|
|
5557
5805
|
async function storeBehavior(opts) {
|
|
5558
5806
|
const client = getClient();
|
|
5559
|
-
const id =
|
|
5807
|
+
const id = crypto5.randomUUID();
|
|
5560
5808
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5561
5809
|
await client.execute({
|
|
5562
5810
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -5585,7 +5833,7 @@ __export(skill_learning_exports, {
|
|
|
5585
5833
|
storeTrajectory: () => storeTrajectory,
|
|
5586
5834
|
sweepTrajectories: () => sweepTrajectories
|
|
5587
5835
|
});
|
|
5588
|
-
import
|
|
5836
|
+
import crypto6 from "crypto";
|
|
5589
5837
|
async function extractTrajectory(taskId, agentId) {
|
|
5590
5838
|
const client = getClient();
|
|
5591
5839
|
const result = await client.execute({
|
|
@@ -5614,11 +5862,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
5614
5862
|
return signature;
|
|
5615
5863
|
}
|
|
5616
5864
|
function hashSignature(signature) {
|
|
5617
|
-
return
|
|
5865
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
5618
5866
|
}
|
|
5619
5867
|
async function storeTrajectory(opts) {
|
|
5620
5868
|
const client = getClient();
|
|
5621
|
-
const id =
|
|
5869
|
+
const id = crypto6.randomUUID();
|
|
5622
5870
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5623
5871
|
const signatureHash = hashSignature(opts.signature);
|
|
5624
5872
|
await client.execute({
|
|
@@ -5883,8 +6131,8 @@ __export(tasks_exports, {
|
|
|
5883
6131
|
updateTaskStatus: () => updateTaskStatus,
|
|
5884
6132
|
writeCheckpoint: () => writeCheckpoint
|
|
5885
6133
|
});
|
|
5886
|
-
import
|
|
5887
|
-
import { writeFileSync as
|
|
6134
|
+
import path18 from "path";
|
|
6135
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5888
6136
|
async function createTask(input) {
|
|
5889
6137
|
const result = await createTaskCore(input);
|
|
5890
6138
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5903,12 +6151,12 @@ async function updateTask(input) {
|
|
|
5903
6151
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5904
6152
|
try {
|
|
5905
6153
|
const agent = String(row.assigned_to);
|
|
5906
|
-
const cacheDir =
|
|
5907
|
-
const cachePath =
|
|
6154
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
6155
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
5908
6156
|
if (input.status === "in_progress") {
|
|
5909
6157
|
mkdirSync6(cacheDir, { recursive: true });
|
|
5910
|
-
|
|
5911
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
6158
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6159
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5912
6160
|
try {
|
|
5913
6161
|
unlinkSync5(cachePath);
|
|
5914
6162
|
} catch {
|
|
@@ -5916,10 +6164,10 @@ async function updateTask(input) {
|
|
|
5916
6164
|
}
|
|
5917
6165
|
} catch {
|
|
5918
6166
|
}
|
|
5919
|
-
if (input.status === "done") {
|
|
6167
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5920
6168
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5921
6169
|
}
|
|
5922
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
6170
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5923
6171
|
try {
|
|
5924
6172
|
const client = getClient();
|
|
5925
6173
|
const taskTitle = String(row.title);
|
|
@@ -5935,7 +6183,7 @@ async function updateTask(input) {
|
|
|
5935
6183
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5936
6184
|
try {
|
|
5937
6185
|
const draftClient = getClient();
|
|
5938
|
-
if (input.status === "done") {
|
|
6186
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5939
6187
|
await draftClient.execute({
|
|
5940
6188
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
5941
6189
|
args: [assignedAgent]
|
|
@@ -5952,7 +6200,7 @@ async function updateTask(input) {
|
|
|
5952
6200
|
try {
|
|
5953
6201
|
const client = getClient();
|
|
5954
6202
|
const cascaded = await client.execute({
|
|
5955
|
-
sql: `UPDATE tasks SET status = '
|
|
6203
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
5956
6204
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
5957
6205
|
args: [now, taskId]
|
|
5958
6206
|
});
|
|
@@ -5965,14 +6213,14 @@ async function updateTask(input) {
|
|
|
5965
6213
|
} catch {
|
|
5966
6214
|
}
|
|
5967
6215
|
}
|
|
5968
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
6216
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
5969
6217
|
if (isTerminal) {
|
|
5970
6218
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
5971
6219
|
if (!isCoordinator) {
|
|
5972
6220
|
notifyTaskDone();
|
|
5973
6221
|
}
|
|
5974
6222
|
await markTaskNotificationsRead(taskFile);
|
|
5975
|
-
if (input.status === "done") {
|
|
6223
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5976
6224
|
try {
|
|
5977
6225
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5978
6226
|
} catch {
|
|
@@ -5992,7 +6240,7 @@ async function updateTask(input) {
|
|
|
5992
6240
|
}
|
|
5993
6241
|
}
|
|
5994
6242
|
}
|
|
5995
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6243
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5996
6244
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
5997
6245
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
5998
6246
|
taskId,
|
|
@@ -6364,6 +6612,7 @@ __export(tmux_routing_exports, {
|
|
|
6364
6612
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
6365
6613
|
isExeSession: () => isExeSession,
|
|
6366
6614
|
isSessionBusy: () => isSessionBusy,
|
|
6615
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
6367
6616
|
notifyParentExe: () => notifyParentExe,
|
|
6368
6617
|
parseParentExe: () => parseParentExe,
|
|
6369
6618
|
registerParentExe: () => registerParentExe,
|
|
@@ -6374,13 +6623,13 @@ __export(tmux_routing_exports, {
|
|
|
6374
6623
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6375
6624
|
});
|
|
6376
6625
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
6377
|
-
import { readFileSync as
|
|
6378
|
-
import
|
|
6379
|
-
import
|
|
6626
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
6627
|
+
import path19 from "path";
|
|
6628
|
+
import os11 from "os";
|
|
6380
6629
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6381
6630
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6382
6631
|
function spawnLockPath(sessionName) {
|
|
6383
|
-
return
|
|
6632
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6384
6633
|
}
|
|
6385
6634
|
function isProcessAlive(pid) {
|
|
6386
6635
|
try {
|
|
@@ -6391,13 +6640,13 @@ function isProcessAlive(pid) {
|
|
|
6391
6640
|
}
|
|
6392
6641
|
}
|
|
6393
6642
|
function acquireSpawnLock2(sessionName) {
|
|
6394
|
-
if (!
|
|
6643
|
+
if (!existsSync16(SPAWN_LOCK_DIR)) {
|
|
6395
6644
|
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
6396
6645
|
}
|
|
6397
6646
|
const lockFile = spawnLockPath(sessionName);
|
|
6398
|
-
if (
|
|
6647
|
+
if (existsSync16(lockFile)) {
|
|
6399
6648
|
try {
|
|
6400
|
-
const lock = JSON.parse(
|
|
6649
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
6401
6650
|
const age = Date.now() - lock.timestamp;
|
|
6402
6651
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
6403
6652
|
return false;
|
|
@@ -6405,7 +6654,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
6405
6654
|
} catch {
|
|
6406
6655
|
}
|
|
6407
6656
|
}
|
|
6408
|
-
|
|
6657
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
6409
6658
|
return true;
|
|
6410
6659
|
}
|
|
6411
6660
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -6417,13 +6666,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6417
6666
|
function resolveBehaviorsExporterScript() {
|
|
6418
6667
|
try {
|
|
6419
6668
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6420
|
-
const scriptPath =
|
|
6421
|
-
|
|
6669
|
+
const scriptPath = path19.join(
|
|
6670
|
+
path19.dirname(thisFile),
|
|
6422
6671
|
"..",
|
|
6423
6672
|
"bin",
|
|
6424
6673
|
"exe-export-behaviors.js"
|
|
6425
6674
|
);
|
|
6426
|
-
return
|
|
6675
|
+
return existsSync16(scriptPath) ? scriptPath : null;
|
|
6427
6676
|
} catch {
|
|
6428
6677
|
return null;
|
|
6429
6678
|
}
|
|
@@ -6489,12 +6738,12 @@ function extractRootExe(name) {
|
|
|
6489
6738
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6490
6739
|
}
|
|
6491
6740
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
6492
|
-
if (!
|
|
6741
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
6493
6742
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6494
6743
|
}
|
|
6495
6744
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6496
|
-
const filePath =
|
|
6497
|
-
|
|
6745
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6746
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
6498
6747
|
parentExe: rootExe,
|
|
6499
6748
|
dispatchedBy: dispatchedBy || rootExe,
|
|
6500
6749
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6502,7 +6751,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6502
6751
|
}
|
|
6503
6752
|
function getParentExe(sessionKey) {
|
|
6504
6753
|
try {
|
|
6505
|
-
const data = JSON.parse(
|
|
6754
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6506
6755
|
return data.parentExe || null;
|
|
6507
6756
|
} catch {
|
|
6508
6757
|
return null;
|
|
@@ -6510,8 +6759,8 @@ function getParentExe(sessionKey) {
|
|
|
6510
6759
|
}
|
|
6511
6760
|
function getDispatchedBy(sessionKey) {
|
|
6512
6761
|
try {
|
|
6513
|
-
const data = JSON.parse(
|
|
6514
|
-
|
|
6762
|
+
const data = JSON.parse(readFileSync12(
|
|
6763
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6515
6764
|
"utf8"
|
|
6516
6765
|
));
|
|
6517
6766
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6581,8 +6830,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
6581
6830
|
}
|
|
6582
6831
|
function readDebounceState() {
|
|
6583
6832
|
try {
|
|
6584
|
-
if (!
|
|
6585
|
-
const raw = JSON.parse(
|
|
6833
|
+
if (!existsSync16(DEBOUNCE_FILE)) return {};
|
|
6834
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
6586
6835
|
const state = {};
|
|
6587
6836
|
for (const [key, val] of Object.entries(raw)) {
|
|
6588
6837
|
if (typeof val === "number") {
|
|
@@ -6598,8 +6847,8 @@ function readDebounceState() {
|
|
|
6598
6847
|
}
|
|
6599
6848
|
function writeDebounceState(state) {
|
|
6600
6849
|
try {
|
|
6601
|
-
if (!
|
|
6602
|
-
|
|
6850
|
+
if (!existsSync16(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6851
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
6603
6852
|
} catch {
|
|
6604
6853
|
}
|
|
6605
6854
|
}
|
|
@@ -6697,8 +6946,8 @@ function sendIntercom(targetSession) {
|
|
|
6697
6946
|
try {
|
|
6698
6947
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6699
6948
|
const agent = baseAgentName(rawAgent);
|
|
6700
|
-
const markerPath =
|
|
6701
|
-
if (
|
|
6949
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6950
|
+
if (existsSync16(markerPath)) {
|
|
6702
6951
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6703
6952
|
return "debounced";
|
|
6704
6953
|
}
|
|
@@ -6707,8 +6956,8 @@ function sendIntercom(targetSession) {
|
|
|
6707
6956
|
try {
|
|
6708
6957
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6709
6958
|
const agent = baseAgentName(rawAgent);
|
|
6710
|
-
const taskDir =
|
|
6711
|
-
if (
|
|
6959
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
6960
|
+
if (existsSync16(taskDir)) {
|
|
6712
6961
|
const files = readdirSync4(taskDir).filter(
|
|
6713
6962
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
6714
6963
|
);
|
|
@@ -6768,6 +7017,21 @@ function notifyParentExe(sessionKey) {
|
|
|
6768
7017
|
}
|
|
6769
7018
|
return true;
|
|
6770
7019
|
}
|
|
7020
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
7021
|
+
const transport = getTransport();
|
|
7022
|
+
try {
|
|
7023
|
+
const sessions = transport.listSessions();
|
|
7024
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
7025
|
+
execSync7(
|
|
7026
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
7027
|
+
{ timeout: 3e3 }
|
|
7028
|
+
);
|
|
7029
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
7030
|
+
return true;
|
|
7031
|
+
} catch {
|
|
7032
|
+
return false;
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
6771
7035
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
6772
7036
|
if (isCoordinatorName(employeeName)) {
|
|
6773
7037
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6841,26 +7105,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6841
7105
|
const transport = getTransport();
|
|
6842
7106
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6843
7107
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6844
|
-
const logDir =
|
|
6845
|
-
const logFile =
|
|
6846
|
-
if (!
|
|
7108
|
+
const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
|
|
7109
|
+
const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7110
|
+
if (!existsSync16(logDir)) {
|
|
6847
7111
|
mkdirSync7(logDir, { recursive: true });
|
|
6848
7112
|
}
|
|
6849
7113
|
transport.kill(sessionName);
|
|
6850
7114
|
let cleanupSuffix = "";
|
|
6851
7115
|
try {
|
|
6852
7116
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6853
|
-
const cleanupScript =
|
|
6854
|
-
if (
|
|
7117
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7118
|
+
if (existsSync16(cleanupScript)) {
|
|
6855
7119
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6856
7120
|
}
|
|
6857
7121
|
} catch {
|
|
6858
7122
|
}
|
|
6859
7123
|
try {
|
|
6860
|
-
const claudeJsonPath =
|
|
7124
|
+
const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
|
|
6861
7125
|
let claudeJson = {};
|
|
6862
7126
|
try {
|
|
6863
|
-
claudeJson = JSON.parse(
|
|
7127
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6864
7128
|
} catch {
|
|
6865
7129
|
}
|
|
6866
7130
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6868,17 +7132,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6868
7132
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6869
7133
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6870
7134
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6871
|
-
|
|
7135
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6872
7136
|
} catch {
|
|
6873
7137
|
}
|
|
6874
7138
|
try {
|
|
6875
|
-
const settingsDir =
|
|
7139
|
+
const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
|
|
6876
7140
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6877
|
-
const projSettingsDir =
|
|
6878
|
-
const settingsPath =
|
|
7141
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
7142
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
6879
7143
|
let settings = {};
|
|
6880
7144
|
try {
|
|
6881
|
-
settings = JSON.parse(
|
|
7145
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
6882
7146
|
} catch {
|
|
6883
7147
|
}
|
|
6884
7148
|
const perms = settings.permissions ?? {};
|
|
@@ -6907,7 +7171,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6907
7171
|
perms.allow = allow;
|
|
6908
7172
|
settings.permissions = perms;
|
|
6909
7173
|
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6910
|
-
|
|
7174
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6911
7175
|
}
|
|
6912
7176
|
} catch {
|
|
6913
7177
|
}
|
|
@@ -6922,8 +7186,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6922
7186
|
let behaviorsFlag = "";
|
|
6923
7187
|
let legacyFallbackWarned = false;
|
|
6924
7188
|
if (!useExeAgent && !useBinSymlink) {
|
|
6925
|
-
const identityPath =
|
|
6926
|
-
|
|
7189
|
+
const identityPath = path19.join(
|
|
7190
|
+
os11.homedir(),
|
|
6927
7191
|
".exe-os",
|
|
6928
7192
|
"identity",
|
|
6929
7193
|
`${employeeName}.md`
|
|
@@ -6932,13 +7196,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6932
7196
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6933
7197
|
if (hasAgentFlag) {
|
|
6934
7198
|
identityFlag = ` --agent ${employeeName}`;
|
|
6935
|
-
} else if (
|
|
7199
|
+
} else if (existsSync16(identityPath)) {
|
|
6936
7200
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6937
7201
|
legacyFallbackWarned = true;
|
|
6938
7202
|
}
|
|
6939
7203
|
const behaviorsFile = exportBehaviorsSync(
|
|
6940
7204
|
employeeName,
|
|
6941
|
-
|
|
7205
|
+
path19.basename(spawnCwd),
|
|
6942
7206
|
sessionName
|
|
6943
7207
|
);
|
|
6944
7208
|
if (behaviorsFile) {
|
|
@@ -6953,16 +7217,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6953
7217
|
}
|
|
6954
7218
|
let sessionContextFlag = "";
|
|
6955
7219
|
try {
|
|
6956
|
-
const ctxDir =
|
|
7220
|
+
const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
6957
7221
|
mkdirSync7(ctxDir, { recursive: true });
|
|
6958
|
-
const ctxFile =
|
|
7222
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6959
7223
|
const ctxContent = [
|
|
6960
7224
|
`## Session Context`,
|
|
6961
7225
|
`You are running in tmux session: ${sessionName}.`,
|
|
6962
7226
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6963
7227
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6964
7228
|
].join("\n");
|
|
6965
|
-
|
|
7229
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
6966
7230
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6967
7231
|
} catch {
|
|
6968
7232
|
}
|
|
@@ -7039,8 +7303,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7039
7303
|
transport.pipeLog(sessionName, logFile);
|
|
7040
7304
|
try {
|
|
7041
7305
|
const mySession = getMySession();
|
|
7042
|
-
const dispatchInfo =
|
|
7043
|
-
|
|
7306
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7307
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
7044
7308
|
dispatchedBy: mySession,
|
|
7045
7309
|
rootExe: exeSession,
|
|
7046
7310
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -7114,15 +7378,15 @@ var init_tmux_routing = __esm({
|
|
|
7114
7378
|
init_intercom_queue();
|
|
7115
7379
|
init_plan_limits();
|
|
7116
7380
|
init_employees();
|
|
7117
|
-
SPAWN_LOCK_DIR =
|
|
7118
|
-
SESSION_CACHE =
|
|
7381
|
+
SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
7382
|
+
SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7119
7383
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7120
7384
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7121
7385
|
VERIFY_PANE_LINES = 200;
|
|
7122
7386
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7123
7387
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7124
|
-
INTERCOM_LOG2 =
|
|
7125
|
-
DEBOUNCE_FILE =
|
|
7388
|
+
INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
7389
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7126
7390
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7127
7391
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7128
7392
|
}
|
|
@@ -7145,6 +7409,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
7145
7409
|
args: [scope]
|
|
7146
7410
|
};
|
|
7147
7411
|
}
|
|
7412
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
7413
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
7414
|
+
if (!scope) return { sql: "", args: [] };
|
|
7415
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
7416
|
+
return {
|
|
7417
|
+
sql: ` AND ${col} = ?`,
|
|
7418
|
+
args: [scope]
|
|
7419
|
+
};
|
|
7420
|
+
}
|
|
7148
7421
|
var init_task_scope = __esm({
|
|
7149
7422
|
"src/lib/task-scope.ts"() {
|
|
7150
7423
|
"use strict";
|
|
@@ -7179,14 +7452,14 @@ __export(worker_gate_exports, {
|
|
|
7179
7452
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
7180
7453
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
7181
7454
|
});
|
|
7182
|
-
import { readdirSync as readdirSync6, writeFileSync as
|
|
7183
|
-
import
|
|
7455
|
+
import { readdirSync as readdirSync6, writeFileSync as writeFileSync10, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync17 } from "fs";
|
|
7456
|
+
import path21 from "path";
|
|
7184
7457
|
function tryAcquireWorkerSlot() {
|
|
7185
7458
|
try {
|
|
7186
7459
|
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
7187
7460
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
7188
|
-
const reservationPath =
|
|
7189
|
-
|
|
7461
|
+
const reservationPath = path21.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
7462
|
+
writeFileSync10(reservationPath, String(process.pid));
|
|
7190
7463
|
const files = readdirSync6(WORKER_PID_DIR);
|
|
7191
7464
|
let alive = 0;
|
|
7192
7465
|
for (const f of files) {
|
|
@@ -7203,7 +7476,7 @@ function tryAcquireWorkerSlot() {
|
|
|
7203
7476
|
alive++;
|
|
7204
7477
|
} catch {
|
|
7205
7478
|
try {
|
|
7206
|
-
unlinkSync8(
|
|
7479
|
+
unlinkSync8(path21.join(WORKER_PID_DIR, f));
|
|
7207
7480
|
} catch {
|
|
7208
7481
|
}
|
|
7209
7482
|
}
|
|
@@ -7227,20 +7500,20 @@ function tryAcquireWorkerSlot() {
|
|
|
7227
7500
|
function registerWorkerPid(pid) {
|
|
7228
7501
|
try {
|
|
7229
7502
|
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
7230
|
-
|
|
7503
|
+
writeFileSync10(path21.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
7231
7504
|
} catch {
|
|
7232
7505
|
}
|
|
7233
7506
|
}
|
|
7234
7507
|
function cleanupWorkerPid() {
|
|
7235
7508
|
try {
|
|
7236
|
-
unlinkSync8(
|
|
7509
|
+
unlinkSync8(path21.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
7237
7510
|
} catch {
|
|
7238
7511
|
}
|
|
7239
7512
|
}
|
|
7240
7513
|
function tryAcquireBackfillLock() {
|
|
7241
7514
|
try {
|
|
7242
7515
|
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
7243
|
-
if (
|
|
7516
|
+
if (existsSync17(BACKFILL_LOCK)) {
|
|
7244
7517
|
try {
|
|
7245
7518
|
const pid = parseInt(
|
|
7246
7519
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -7256,7 +7529,7 @@ function tryAcquireBackfillLock() {
|
|
|
7256
7529
|
} catch {
|
|
7257
7530
|
}
|
|
7258
7531
|
}
|
|
7259
|
-
|
|
7532
|
+
writeFileSync10(BACKFILL_LOCK, String(process.pid));
|
|
7260
7533
|
return true;
|
|
7261
7534
|
} catch {
|
|
7262
7535
|
return true;
|
|
@@ -7273,9 +7546,9 @@ var init_worker_gate = __esm({
|
|
|
7273
7546
|
"src/lib/worker-gate.ts"() {
|
|
7274
7547
|
"use strict";
|
|
7275
7548
|
init_config();
|
|
7276
|
-
WORKER_PID_DIR =
|
|
7549
|
+
WORKER_PID_DIR = path21.join(EXE_AI_DIR, "worker-pids");
|
|
7277
7550
|
MAX_CONCURRENT_WORKERS = 3;
|
|
7278
|
-
BACKFILL_LOCK =
|
|
7551
|
+
BACKFILL_LOCK = path21.join(WORKER_PID_DIR, "backfill.lock");
|
|
7279
7552
|
}
|
|
7280
7553
|
});
|
|
7281
7554
|
|
|
@@ -7287,13 +7560,13 @@ __export(crypto_exports, {
|
|
|
7287
7560
|
initSyncCrypto: () => initSyncCrypto,
|
|
7288
7561
|
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
7289
7562
|
});
|
|
7290
|
-
import
|
|
7563
|
+
import crypto7 from "crypto";
|
|
7291
7564
|
function initSyncCrypto(masterKey) {
|
|
7292
7565
|
if (masterKey.length !== 32) {
|
|
7293
7566
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
7294
7567
|
}
|
|
7295
7568
|
_syncKey = Buffer.from(
|
|
7296
|
-
|
|
7569
|
+
crypto7.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
7297
7570
|
);
|
|
7298
7571
|
}
|
|
7299
7572
|
function isSyncCryptoInitialized() {
|
|
@@ -7307,8 +7580,8 @@ function requireSyncKey() {
|
|
|
7307
7580
|
}
|
|
7308
7581
|
function encryptSyncBlob(data) {
|
|
7309
7582
|
const key = requireSyncKey();
|
|
7310
|
-
const iv =
|
|
7311
|
-
const cipher =
|
|
7583
|
+
const iv = crypto7.randomBytes(IV_LENGTH);
|
|
7584
|
+
const cipher = crypto7.createCipheriv(ALGORITHM, key, iv);
|
|
7312
7585
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
7313
7586
|
const tag = cipher.getAuthTag();
|
|
7314
7587
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -7322,7 +7595,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
7322
7595
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
7323
7596
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
7324
7597
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
7325
|
-
const decipher =
|
|
7598
|
+
const decipher = crypto7.createDecipheriv(ALGORITHM, key, iv);
|
|
7326
7599
|
decipher.setAuthTag(tag);
|
|
7327
7600
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
7328
7601
|
}
|
|
@@ -7377,8 +7650,8 @@ __export(crdt_sync_exports, {
|
|
|
7377
7650
|
rebuildFromDb: () => rebuildFromDb
|
|
7378
7651
|
});
|
|
7379
7652
|
import * as Y from "yjs";
|
|
7380
|
-
import { readFileSync as
|
|
7381
|
-
import
|
|
7653
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync18, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
|
|
7654
|
+
import path22 from "path";
|
|
7382
7655
|
import { homedir } from "os";
|
|
7383
7656
|
function getStatePath() {
|
|
7384
7657
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -7390,9 +7663,9 @@ function initCrdtDoc() {
|
|
|
7390
7663
|
if (doc) return doc;
|
|
7391
7664
|
doc = new Y.Doc();
|
|
7392
7665
|
const sp = getStatePath();
|
|
7393
|
-
if (
|
|
7666
|
+
if (existsSync18(sp)) {
|
|
7394
7667
|
try {
|
|
7395
|
-
const state =
|
|
7668
|
+
const state = readFileSync14(sp);
|
|
7396
7669
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
7397
7670
|
} catch {
|
|
7398
7671
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -7534,10 +7807,10 @@ function persistState() {
|
|
|
7534
7807
|
if (!doc) return;
|
|
7535
7808
|
try {
|
|
7536
7809
|
const sp = getStatePath();
|
|
7537
|
-
const dir =
|
|
7538
|
-
if (!
|
|
7810
|
+
const dir = path22.dirname(sp);
|
|
7811
|
+
if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
|
|
7539
7812
|
const state = Y.encodeStateAsUpdate(doc);
|
|
7540
|
-
|
|
7813
|
+
writeFileSync11(sp, Buffer.from(state));
|
|
7541
7814
|
} catch {
|
|
7542
7815
|
}
|
|
7543
7816
|
}
|
|
@@ -7578,7 +7851,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
7578
7851
|
var init_crdt_sync = __esm({
|
|
7579
7852
|
"src/lib/crdt-sync.ts"() {
|
|
7580
7853
|
"use strict";
|
|
7581
|
-
DEFAULT_STATE_PATH =
|
|
7854
|
+
DEFAULT_STATE_PATH = path22.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
7582
7855
|
_statePathOverride = null;
|
|
7583
7856
|
doc = null;
|
|
7584
7857
|
}
|
|
@@ -7610,39 +7883,107 @@ __export(cloud_sync_exports, {
|
|
|
7610
7883
|
cloudSync: () => cloudSync,
|
|
7611
7884
|
mergeConfig: () => mergeConfig,
|
|
7612
7885
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
7886
|
+
pushToPostgres: () => pushToPostgres,
|
|
7613
7887
|
recordRosterDeletion: () => recordRosterDeletion
|
|
7614
7888
|
});
|
|
7615
|
-
import { readFileSync as
|
|
7616
|
-
import
|
|
7617
|
-
import
|
|
7889
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync19, readdirSync as readdirSync7, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
7890
|
+
import crypto8 from "crypto";
|
|
7891
|
+
import path23 from "path";
|
|
7618
7892
|
import { homedir as homedir2 } from "os";
|
|
7619
7893
|
function sqlSafe(v) {
|
|
7620
7894
|
return v === void 0 ? null : v;
|
|
7621
7895
|
}
|
|
7622
7896
|
function logError(msg) {
|
|
7623
7897
|
try {
|
|
7624
|
-
const logPath =
|
|
7898
|
+
const logPath = path23.join(homedir2(), ".exe-os", "workers.log");
|
|
7625
7899
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
7626
7900
|
`);
|
|
7627
7901
|
} catch {
|
|
7628
7902
|
}
|
|
7629
7903
|
}
|
|
7904
|
+
function loadPgClient() {
|
|
7905
|
+
if (_pgFailed) return null;
|
|
7906
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
7907
|
+
const configPath = path23.join(EXE_AI_DIR, "config.json");
|
|
7908
|
+
let cloudPostgresUrl;
|
|
7909
|
+
try {
|
|
7910
|
+
if (existsSync19(configPath)) {
|
|
7911
|
+
const cfg = JSON.parse(readFileSync15(configPath, "utf8"));
|
|
7912
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
7913
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
7914
|
+
_pgFailed = true;
|
|
7915
|
+
return null;
|
|
7916
|
+
}
|
|
7917
|
+
}
|
|
7918
|
+
} catch {
|
|
7919
|
+
}
|
|
7920
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
7921
|
+
if (!url) {
|
|
7922
|
+
_pgFailed = true;
|
|
7923
|
+
return null;
|
|
7924
|
+
}
|
|
7925
|
+
if (!_pgPromise) {
|
|
7926
|
+
_pgPromise = (async () => {
|
|
7927
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
7928
|
+
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
7929
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path23.join(homedir2(), "exe-db");
|
|
7930
|
+
const req = createRequire3(path23.join(exeDbRoot, "package.json"));
|
|
7931
|
+
const entry = req.resolve("@prisma/client");
|
|
7932
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
7933
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
7934
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
7935
|
+
return new Ctor();
|
|
7936
|
+
})().catch(() => {
|
|
7937
|
+
_pgFailed = true;
|
|
7938
|
+
_pgPromise = null;
|
|
7939
|
+
throw new Error("pg_unavailable");
|
|
7940
|
+
});
|
|
7941
|
+
}
|
|
7942
|
+
return _pgPromise;
|
|
7943
|
+
}
|
|
7944
|
+
async function pushToPostgres(records) {
|
|
7945
|
+
const loader = loadPgClient();
|
|
7946
|
+
if (!loader) return 0;
|
|
7947
|
+
let prisma;
|
|
7948
|
+
try {
|
|
7949
|
+
prisma = await loader;
|
|
7950
|
+
} catch {
|
|
7951
|
+
return 0;
|
|
7952
|
+
}
|
|
7953
|
+
let inserted = 0;
|
|
7954
|
+
for (const rec of records) {
|
|
7955
|
+
try {
|
|
7956
|
+
await prisma.$executeRawUnsafe(
|
|
7957
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
7958
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
7959
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
7960
|
+
String(rec.id ?? ""),
|
|
7961
|
+
JSON.stringify(rec),
|
|
7962
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
7963
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
7964
|
+
);
|
|
7965
|
+
inserted++;
|
|
7966
|
+
} catch {
|
|
7967
|
+
}
|
|
7968
|
+
}
|
|
7969
|
+
return inserted;
|
|
7970
|
+
}
|
|
7630
7971
|
async function withRosterLock(fn) {
|
|
7631
7972
|
try {
|
|
7632
7973
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
7633
7974
|
closeSync2(fd);
|
|
7634
|
-
|
|
7975
|
+
writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
|
|
7635
7976
|
} catch (err) {
|
|
7636
7977
|
if (err.code === "EEXIST") {
|
|
7637
7978
|
try {
|
|
7638
|
-
const ts = parseInt(
|
|
7979
|
+
const ts = parseInt(readFileSync15(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
7639
7980
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
7640
7981
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
7641
7982
|
}
|
|
7642
7983
|
unlinkSync10(ROSTER_LOCK_PATH);
|
|
7643
7984
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
7644
7985
|
closeSync2(fd);
|
|
7645
|
-
|
|
7986
|
+
writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
|
|
7646
7987
|
} catch (retryErr) {
|
|
7647
7988
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
7648
7989
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -7912,6 +8253,10 @@ async function cloudSync(config) {
|
|
|
7912
8253
|
const maxVersion = Number(records[records.length - 1].version);
|
|
7913
8254
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
7914
8255
|
if (!pushOk) break;
|
|
8256
|
+
try {
|
|
8257
|
+
await pushToPostgres(records);
|
|
8258
|
+
} catch {
|
|
8259
|
+
}
|
|
7915
8260
|
await client.execute({
|
|
7916
8261
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
7917
8262
|
args: [String(maxVersion)]
|
|
@@ -8016,8 +8361,8 @@ async function cloudSync(config) {
|
|
|
8016
8361
|
try {
|
|
8017
8362
|
const employees = await loadEmployees();
|
|
8018
8363
|
rosterResult.employees = employees.length;
|
|
8019
|
-
const idDir =
|
|
8020
|
-
if (
|
|
8364
|
+
const idDir = path23.join(EXE_AI_DIR, "identity");
|
|
8365
|
+
if (existsSync19(idDir)) {
|
|
8021
8366
|
rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
|
|
8022
8367
|
}
|
|
8023
8368
|
} catch {
|
|
@@ -8038,62 +8383,62 @@ async function cloudSync(config) {
|
|
|
8038
8383
|
function recordRosterDeletion(name) {
|
|
8039
8384
|
let deletions = [];
|
|
8040
8385
|
try {
|
|
8041
|
-
if (
|
|
8042
|
-
deletions = JSON.parse(
|
|
8386
|
+
if (existsSync19(ROSTER_DELETIONS_PATH)) {
|
|
8387
|
+
deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
8043
8388
|
}
|
|
8044
8389
|
} catch {
|
|
8045
8390
|
}
|
|
8046
8391
|
if (!deletions.includes(name)) deletions.push(name);
|
|
8047
|
-
|
|
8392
|
+
writeFileSync12(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
8048
8393
|
}
|
|
8049
8394
|
function consumeRosterDeletions() {
|
|
8050
8395
|
try {
|
|
8051
|
-
if (!
|
|
8052
|
-
const deletions = JSON.parse(
|
|
8053
|
-
|
|
8396
|
+
if (!existsSync19(ROSTER_DELETIONS_PATH)) return [];
|
|
8397
|
+
const deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
8398
|
+
writeFileSync12(ROSTER_DELETIONS_PATH, "[]");
|
|
8054
8399
|
return deletions;
|
|
8055
8400
|
} catch {
|
|
8056
8401
|
return [];
|
|
8057
8402
|
}
|
|
8058
8403
|
}
|
|
8059
8404
|
function buildRosterBlob(paths) {
|
|
8060
|
-
const rosterPath = paths?.rosterPath ??
|
|
8061
|
-
const identityDir = paths?.identityDir ??
|
|
8062
|
-
const configPath = paths?.configPath ??
|
|
8405
|
+
const rosterPath = paths?.rosterPath ?? path23.join(EXE_AI_DIR, "exe-employees.json");
|
|
8406
|
+
const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
|
|
8407
|
+
const configPath = paths?.configPath ?? path23.join(EXE_AI_DIR, "config.json");
|
|
8063
8408
|
let roster = [];
|
|
8064
|
-
if (
|
|
8409
|
+
if (existsSync19(rosterPath)) {
|
|
8065
8410
|
try {
|
|
8066
|
-
roster = JSON.parse(
|
|
8411
|
+
roster = JSON.parse(readFileSync15(rosterPath, "utf-8"));
|
|
8067
8412
|
} catch {
|
|
8068
8413
|
}
|
|
8069
8414
|
}
|
|
8070
8415
|
const identities = {};
|
|
8071
|
-
if (
|
|
8416
|
+
if (existsSync19(identityDir)) {
|
|
8072
8417
|
for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
8073
8418
|
try {
|
|
8074
|
-
identities[file] =
|
|
8419
|
+
identities[file] = readFileSync15(path23.join(identityDir, file), "utf-8");
|
|
8075
8420
|
} catch {
|
|
8076
8421
|
}
|
|
8077
8422
|
}
|
|
8078
8423
|
}
|
|
8079
8424
|
let config;
|
|
8080
|
-
if (
|
|
8425
|
+
if (existsSync19(configPath)) {
|
|
8081
8426
|
try {
|
|
8082
|
-
config = JSON.parse(
|
|
8427
|
+
config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
8083
8428
|
} catch {
|
|
8084
8429
|
}
|
|
8085
8430
|
}
|
|
8086
8431
|
let agentConfig;
|
|
8087
|
-
const agentConfigPath =
|
|
8088
|
-
if (
|
|
8432
|
+
const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
|
|
8433
|
+
if (existsSync19(agentConfigPath)) {
|
|
8089
8434
|
try {
|
|
8090
|
-
agentConfig = JSON.parse(
|
|
8435
|
+
agentConfig = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
|
|
8091
8436
|
} catch {
|
|
8092
8437
|
}
|
|
8093
8438
|
}
|
|
8094
8439
|
const deletedNames = consumeRosterDeletions();
|
|
8095
8440
|
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
8096
|
-
const hash =
|
|
8441
|
+
const hash = crypto8.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
8097
8442
|
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
8098
8443
|
}
|
|
8099
8444
|
async function cloudPushRoster(config) {
|
|
@@ -8163,23 +8508,24 @@ async function cloudPullRoster(config) {
|
|
|
8163
8508
|
}
|
|
8164
8509
|
}
|
|
8165
8510
|
function mergeConfig(remoteConfig, configPath) {
|
|
8166
|
-
const cfgPath = configPath ??
|
|
8511
|
+
const cfgPath = configPath ?? path23.join(EXE_AI_DIR, "config.json");
|
|
8167
8512
|
let local = {};
|
|
8168
|
-
if (
|
|
8513
|
+
if (existsSync19(cfgPath)) {
|
|
8169
8514
|
try {
|
|
8170
|
-
local = JSON.parse(
|
|
8515
|
+
local = JSON.parse(readFileSync15(cfgPath, "utf-8"));
|
|
8171
8516
|
} catch {
|
|
8172
8517
|
}
|
|
8173
8518
|
}
|
|
8174
8519
|
const merged = { ...remoteConfig, ...local };
|
|
8175
|
-
const dir =
|
|
8176
|
-
|
|
8177
|
-
|
|
8520
|
+
const dir = path23.dirname(cfgPath);
|
|
8521
|
+
ensurePrivateDirSync(dir);
|
|
8522
|
+
writeFileSync12(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
8523
|
+
enforcePrivateFileSync(cfgPath);
|
|
8178
8524
|
}
|
|
8179
8525
|
async function mergeRosterFromRemote(remote, paths) {
|
|
8180
8526
|
return withRosterLock(async () => {
|
|
8181
8527
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
8182
|
-
const identityDir = paths?.identityDir ??
|
|
8528
|
+
const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
|
|
8183
8529
|
const localEmployees = await loadEmployees(rosterPath);
|
|
8184
8530
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
8185
8531
|
let added = 0;
|
|
@@ -8200,15 +8546,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
8200
8546
|
) ?? lookupKey;
|
|
8201
8547
|
const remoteIdentity = remote.identities[matchedKey];
|
|
8202
8548
|
if (remoteIdentity) {
|
|
8203
|
-
if (!
|
|
8204
|
-
const idPath =
|
|
8549
|
+
if (!existsSync19(identityDir)) mkdirSync11(identityDir, { recursive: true });
|
|
8550
|
+
const idPath = path23.join(identityDir, `${remoteEmp.name}.md`);
|
|
8205
8551
|
let localIdentity = null;
|
|
8206
8552
|
try {
|
|
8207
|
-
localIdentity =
|
|
8553
|
+
localIdentity = existsSync19(idPath) ? readFileSync15(idPath, "utf-8") : null;
|
|
8208
8554
|
} catch {
|
|
8209
8555
|
}
|
|
8210
8556
|
if (localIdentity !== remoteIdentity) {
|
|
8211
|
-
|
|
8557
|
+
writeFileSync12(idPath, remoteIdentity, "utf-8");
|
|
8212
8558
|
identitiesUpdated++;
|
|
8213
8559
|
}
|
|
8214
8560
|
}
|
|
@@ -8234,16 +8580,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
8234
8580
|
}
|
|
8235
8581
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
8236
8582
|
try {
|
|
8237
|
-
const agentConfigPath =
|
|
8583
|
+
const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
|
|
8238
8584
|
let local = {};
|
|
8239
|
-
if (
|
|
8585
|
+
if (existsSync19(agentConfigPath)) {
|
|
8240
8586
|
try {
|
|
8241
|
-
local = JSON.parse(
|
|
8587
|
+
local = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
|
|
8242
8588
|
} catch {
|
|
8243
8589
|
}
|
|
8244
8590
|
}
|
|
8245
8591
|
const merged = { ...remote.agentConfig, ...local };
|
|
8246
|
-
|
|
8592
|
+
ensurePrivateDirSync(path23.dirname(agentConfigPath));
|
|
8593
|
+
writeFileSync12(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
8594
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
8247
8595
|
} catch {
|
|
8248
8596
|
}
|
|
8249
8597
|
}
|
|
@@ -8667,7 +9015,7 @@ async function cloudPullDocuments(config) {
|
|
|
8667
9015
|
}
|
|
8668
9016
|
return { pulled };
|
|
8669
9017
|
}
|
|
8670
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
9018
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
8671
9019
|
var init_cloud_sync = __esm({
|
|
8672
9020
|
"src/lib/cloud-sync.ts"() {
|
|
8673
9021
|
"use strict";
|
|
@@ -8678,12 +9026,15 @@ var init_cloud_sync = __esm({
|
|
|
8678
9026
|
init_config();
|
|
8679
9027
|
init_crdt_sync();
|
|
8680
9028
|
init_employees();
|
|
9029
|
+
init_secure_files();
|
|
8681
9030
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
8682
9031
|
FETCH_TIMEOUT_MS = 3e4;
|
|
8683
9032
|
PUSH_BATCH_SIZE = 5e3;
|
|
8684
|
-
ROSTER_LOCK_PATH =
|
|
9033
|
+
ROSTER_LOCK_PATH = path23.join(EXE_AI_DIR, "roster-merge.lock");
|
|
8685
9034
|
LOCK_STALE_MS = 3e4;
|
|
8686
|
-
|
|
9035
|
+
_pgPromise = null;
|
|
9036
|
+
_pgFailed = false;
|
|
9037
|
+
ROSTER_DELETIONS_PATH = path23.join(EXE_AI_DIR, "roster-deletions.json");
|
|
8687
9038
|
}
|
|
8688
9039
|
});
|
|
8689
9040
|
|
|
@@ -8695,7 +9046,7 @@ __export(schedules_exports, {
|
|
|
8695
9046
|
listSchedules: () => listSchedules,
|
|
8696
9047
|
parseHumanCron: () => parseHumanCron
|
|
8697
9048
|
});
|
|
8698
|
-
import
|
|
9049
|
+
import crypto9 from "crypto";
|
|
8699
9050
|
import { execSync as execSync9 } from "child_process";
|
|
8700
9051
|
async function ensureDb() {
|
|
8701
9052
|
if (!isInitialized()) {
|
|
@@ -8764,7 +9115,7 @@ function parseHumanCron(input) {
|
|
|
8764
9115
|
async function createSchedule(input) {
|
|
8765
9116
|
await ensureDb();
|
|
8766
9117
|
const client = getClient();
|
|
8767
|
-
const id =
|
|
9118
|
+
const id = crypto9.randomUUID().slice(0, 8);
|
|
8768
9119
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8769
9120
|
const prompt = input.prompt ?? input.description;
|
|
8770
9121
|
await client.execute({
|
|
@@ -8865,10 +9216,10 @@ var init_schedules = __esm({
|
|
|
8865
9216
|
|
|
8866
9217
|
// src/bin/exe-boot.ts
|
|
8867
9218
|
init_employees();
|
|
8868
|
-
import
|
|
9219
|
+
import path24 from "path";
|
|
8869
9220
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
8870
|
-
import { existsSync as
|
|
8871
|
-
import
|
|
9221
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
|
|
9222
|
+
import os12 from "os";
|
|
8872
9223
|
|
|
8873
9224
|
// src/lib/employee-templates.ts
|
|
8874
9225
|
init_global_procedures();
|
|
@@ -9370,18 +9721,18 @@ init_notifications();
|
|
|
9370
9721
|
init_config();
|
|
9371
9722
|
init_session_key();
|
|
9372
9723
|
init_employees();
|
|
9373
|
-
import { readFileSync as
|
|
9724
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
|
|
9374
9725
|
import { execSync as execSync8 } from "child_process";
|
|
9375
|
-
import
|
|
9376
|
-
var CACHE_DIR =
|
|
9726
|
+
import path20 from "path";
|
|
9727
|
+
var CACHE_DIR = path20.join(EXE_AI_DIR, "session-cache");
|
|
9377
9728
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
9378
9729
|
function getMarkerPath() {
|
|
9379
|
-
return
|
|
9730
|
+
return path20.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
9380
9731
|
}
|
|
9381
9732
|
function writeActiveAgent(agentId, agentRole) {
|
|
9382
9733
|
try {
|
|
9383
9734
|
mkdirSync8(CACHE_DIR, { recursive: true });
|
|
9384
|
-
|
|
9735
|
+
writeFileSync9(
|
|
9385
9736
|
getMarkerPath(),
|
|
9386
9737
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
9387
9738
|
);
|
|
@@ -9391,11 +9742,11 @@ function writeActiveAgent(agentId, agentRole) {
|
|
|
9391
9742
|
function cleanupSessionMarkers() {
|
|
9392
9743
|
const key = getSessionKey();
|
|
9393
9744
|
try {
|
|
9394
|
-
unlinkSync7(
|
|
9745
|
+
unlinkSync7(path20.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
9395
9746
|
} catch {
|
|
9396
9747
|
}
|
|
9397
9748
|
try {
|
|
9398
|
-
unlinkSync7(
|
|
9749
|
+
unlinkSync7(path20.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
9399
9750
|
} catch {
|
|
9400
9751
|
}
|
|
9401
9752
|
}
|
|
@@ -9485,7 +9836,7 @@ async function boot(options) {
|
|
|
9485
9836
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
9486
9837
|
for (const dir of employeeDirs) {
|
|
9487
9838
|
const employee = dir.name;
|
|
9488
|
-
const taskDir =
|
|
9839
|
+
const taskDir = path24.join(exeDir, employee);
|
|
9489
9840
|
let files;
|
|
9490
9841
|
try {
|
|
9491
9842
|
files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
|
|
@@ -9496,7 +9847,7 @@ async function boot(options) {
|
|
|
9496
9847
|
const taskFilePath = `exe/${employee}/${file}`;
|
|
9497
9848
|
let content;
|
|
9498
9849
|
try {
|
|
9499
|
-
content = readFs(
|
|
9850
|
+
content = readFs(path24.join(taskDir, file), "utf8");
|
|
9500
9851
|
} catch {
|
|
9501
9852
|
continue;
|
|
9502
9853
|
}
|
|
@@ -9582,12 +9933,12 @@ async function boot(options) {
|
|
|
9582
9933
|
}
|
|
9583
9934
|
try {
|
|
9584
9935
|
for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
|
|
9585
|
-
const reviewDir =
|
|
9586
|
-
if (
|
|
9936
|
+
const reviewDir = path24.join(process.cwd(), "exe", reviewDirName);
|
|
9937
|
+
if (existsSync20(reviewDir)) {
|
|
9587
9938
|
for (const f of readdirSync8(reviewDir)) {
|
|
9588
9939
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
9589
9940
|
try {
|
|
9590
|
-
unlinkSync11(
|
|
9941
|
+
unlinkSync11(path24.join(reviewDir, f));
|
|
9591
9942
|
} catch {
|
|
9592
9943
|
}
|
|
9593
9944
|
}
|
|
@@ -9633,12 +9984,12 @@ async function boot(options) {
|
|
|
9633
9984
|
});
|
|
9634
9985
|
const taskFile = String(r.task_file);
|
|
9635
9986
|
try {
|
|
9636
|
-
const filePath =
|
|
9637
|
-
if (
|
|
9638
|
-
let content =
|
|
9987
|
+
const filePath = path24.join(process.cwd(), taskFile);
|
|
9988
|
+
if (existsSync20(filePath)) {
|
|
9989
|
+
let content = readFileSync16(filePath, "utf8");
|
|
9639
9990
|
content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
|
|
9640
|
-
const { writeFileSync:
|
|
9641
|
-
|
|
9991
|
+
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
9992
|
+
writeFileSync13(filePath, content);
|
|
9642
9993
|
}
|
|
9643
9994
|
} catch {
|
|
9644
9995
|
}
|
|
@@ -10132,19 +10483,19 @@ async function boot(options) {
|
|
|
10132
10483
|
})()
|
|
10133
10484
|
]);
|
|
10134
10485
|
try {
|
|
10135
|
-
const configPath =
|
|
10136
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
10486
|
+
const configPath = path24.join(
|
|
10487
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
|
|
10137
10488
|
"config.json"
|
|
10138
10489
|
);
|
|
10139
|
-
if (
|
|
10140
|
-
const raw = JSON.parse(
|
|
10490
|
+
if (existsSync20(configPath)) {
|
|
10491
|
+
const raw = JSON.parse(readFileSync16(configPath, "utf8"));
|
|
10141
10492
|
briefData.cloudConnected = !!(raw.cloud || raw.turso);
|
|
10142
10493
|
}
|
|
10143
10494
|
} catch {
|
|
10144
10495
|
}
|
|
10145
10496
|
try {
|
|
10146
|
-
const backfillFlagPath =
|
|
10147
|
-
const isBackfillNeeded = () =>
|
|
10497
|
+
const backfillFlagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
10498
|
+
const isBackfillNeeded = () => existsSync20(backfillFlagPath);
|
|
10148
10499
|
const coverageResult = await client.execute({
|
|
10149
10500
|
sql: `SELECT COUNT(*) as total,
|
|
10150
10501
|
SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
|
|
@@ -10166,8 +10517,8 @@ async function boot(options) {
|
|
|
10166
10517
|
let daemonRunning = false;
|
|
10167
10518
|
let daemonUptime;
|
|
10168
10519
|
let daemonRequestsServed;
|
|
10169
|
-
const socketPath =
|
|
10170
|
-
if (
|
|
10520
|
+
const socketPath = path24.join(EXE_AI_DIR, "exed.sock");
|
|
10521
|
+
if (existsSync20(socketPath)) {
|
|
10171
10522
|
try {
|
|
10172
10523
|
const net2 = await import("net");
|
|
10173
10524
|
const health = await new Promise((resolve) => {
|
|
@@ -10209,10 +10560,10 @@ async function boot(options) {
|
|
|
10209
10560
|
}
|
|
10210
10561
|
}
|
|
10211
10562
|
if (!daemonRunning) {
|
|
10212
|
-
const pidPath =
|
|
10213
|
-
if (
|
|
10563
|
+
const pidPath = path24.join(EXE_AI_DIR, "exed.pid");
|
|
10564
|
+
if (existsSync20(pidPath)) {
|
|
10214
10565
|
try {
|
|
10215
|
-
const pid = parseInt(
|
|
10566
|
+
const pid = parseInt(readFileSync16(pidPath, "utf8").trim(), 10);
|
|
10216
10567
|
if (pid > 0) {
|
|
10217
10568
|
process.kill(pid, 0);
|
|
10218
10569
|
daemonRunning = true;
|
|
@@ -10223,8 +10574,8 @@ async function boot(options) {
|
|
|
10223
10574
|
}
|
|
10224
10575
|
if (nullCount === 0) {
|
|
10225
10576
|
try {
|
|
10226
|
-
const flagPath =
|
|
10227
|
-
if (
|
|
10577
|
+
const flagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
10578
|
+
if (existsSync20(flagPath)) {
|
|
10228
10579
|
const { unlinkSync: unlinkSync12 } = await import("fs");
|
|
10229
10580
|
unlinkSync12(flagPath);
|
|
10230
10581
|
}
|
|
@@ -10250,10 +10601,10 @@ async function boot(options) {
|
|
|
10250
10601
|
const { spawn: spawn2 } = await import("child_process");
|
|
10251
10602
|
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
10252
10603
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
10253
|
-
const backfillPath =
|
|
10254
|
-
if (
|
|
10604
|
+
const backfillPath = path24.resolve(path24.dirname(thisFile), "backfill-vectors.js");
|
|
10605
|
+
if (existsSync20(backfillPath)) {
|
|
10255
10606
|
const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
|
|
10256
|
-
const workerLogPath =
|
|
10607
|
+
const workerLogPath = path24.join(EXE_AI_DIR, "workers.log");
|
|
10257
10608
|
let stderrFd = "ignore";
|
|
10258
10609
|
try {
|
|
10259
10610
|
stderrFd = openSync3(workerLogPath, "a");
|
|
@@ -10283,8 +10634,8 @@ async function boot(options) {
|
|
|
10283
10634
|
const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
|
|
10284
10635
|
const missing = [];
|
|
10285
10636
|
for (const bin of criticalBinaries) {
|
|
10286
|
-
const binPath =
|
|
10287
|
-
if (!
|
|
10637
|
+
const binPath = path24.resolve(path24.dirname(thisFile), bin);
|
|
10638
|
+
if (!existsSync20(binPath)) {
|
|
10288
10639
|
missing.push(`dist/bin/${bin}`);
|
|
10289
10640
|
}
|
|
10290
10641
|
}
|
|
@@ -10313,7 +10664,7 @@ async function boot(options) {
|
|
|
10313
10664
|
console.log(brief);
|
|
10314
10665
|
return;
|
|
10315
10666
|
}
|
|
10316
|
-
const sessionDir =
|
|
10667
|
+
const sessionDir = path24.join(EXE_AI_DIR, "sessions", coordinatorName);
|
|
10317
10668
|
await mkdir5(sessionDir, { recursive: true });
|
|
10318
10669
|
const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
|
|
10319
10670
|
|
|
@@ -10322,7 +10673,7 @@ async function boot(options) {
|
|
|
10322
10673
|
# Status Brief
|
|
10323
10674
|
|
|
10324
10675
|
${brief}`;
|
|
10325
|
-
await writeFile6(
|
|
10676
|
+
await writeFile6(path24.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
|
|
10326
10677
|
const unread = await readUnreadNotifications();
|
|
10327
10678
|
if (unread.length > 0) {
|
|
10328
10679
|
console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
|
|
@@ -10331,12 +10682,12 @@ ${brief}`;
|
|
|
10331
10682
|
await cleanupOldNotifications();
|
|
10332
10683
|
console.log(brief);
|
|
10333
10684
|
try {
|
|
10334
|
-
const configPath2 =
|
|
10335
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
10685
|
+
const configPath2 = path24.join(
|
|
10686
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
|
|
10336
10687
|
"config.json"
|
|
10337
10688
|
);
|
|
10338
|
-
if (
|
|
10339
|
-
const rawCfg = JSON.parse(
|
|
10689
|
+
if (existsSync20(configPath2)) {
|
|
10690
|
+
const rawCfg = JSON.parse(readFileSync16(configPath2, "utf8"));
|
|
10340
10691
|
const cloudCfg = rawCfg.cloud;
|
|
10341
10692
|
if (cloudCfg?.apiKey) {
|
|
10342
10693
|
const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|