@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
|
@@ -89,6 +89,44 @@ var init_db_retry = __esm({
|
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
// src/lib/secure-files.ts
|
|
93
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
94
|
+
import { chmod, mkdir } from "fs/promises";
|
|
95
|
+
async function ensurePrivateDir(dirPath) {
|
|
96
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
97
|
+
try {
|
|
98
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function ensurePrivateDirSync(dirPath) {
|
|
103
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
104
|
+
try {
|
|
105
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function enforcePrivateFile(filePath) {
|
|
110
|
+
try {
|
|
111
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function enforcePrivateFileSync(filePath) {
|
|
116
|
+
try {
|
|
117
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
122
|
+
var init_secure_files = __esm({
|
|
123
|
+
"src/lib/secure-files.ts"() {
|
|
124
|
+
"use strict";
|
|
125
|
+
PRIVATE_DIR_MODE = 448;
|
|
126
|
+
PRIVATE_FILE_MODE = 384;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
92
130
|
// src/lib/config.ts
|
|
93
131
|
var config_exports = {};
|
|
94
132
|
__export(config_exports, {
|
|
@@ -105,8 +143,8 @@ __export(config_exports, {
|
|
|
105
143
|
migrateConfig: () => migrateConfig,
|
|
106
144
|
saveConfig: () => saveConfig
|
|
107
145
|
});
|
|
108
|
-
import { readFile, writeFile
|
|
109
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
146
|
+
import { readFile, writeFile } from "fs/promises";
|
|
147
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
110
148
|
import path from "path";
|
|
111
149
|
import os from "os";
|
|
112
150
|
function resolveDataDir() {
|
|
@@ -114,7 +152,7 @@ function resolveDataDir() {
|
|
|
114
152
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
115
153
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
116
154
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
117
|
-
if (!
|
|
155
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
118
156
|
try {
|
|
119
157
|
renameSync(legacyDir, newDir);
|
|
120
158
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -177,9 +215,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
177
215
|
}
|
|
178
216
|
async function loadConfig() {
|
|
179
217
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
180
|
-
await
|
|
218
|
+
await ensurePrivateDir(dir);
|
|
181
219
|
const configPath = path.join(dir, "config.json");
|
|
182
|
-
if (!
|
|
220
|
+
if (!existsSync2(configPath)) {
|
|
183
221
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
184
222
|
}
|
|
185
223
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -192,6 +230,7 @@ async function loadConfig() {
|
|
|
192
230
|
`);
|
|
193
231
|
try {
|
|
194
232
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
233
|
+
await enforcePrivateFile(configPath);
|
|
195
234
|
} catch {
|
|
196
235
|
}
|
|
197
236
|
}
|
|
@@ -210,7 +249,7 @@ async function loadConfig() {
|
|
|
210
249
|
function loadConfigSync() {
|
|
211
250
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
212
251
|
const configPath = path.join(dir, "config.json");
|
|
213
|
-
if (!
|
|
252
|
+
if (!existsSync2(configPath)) {
|
|
214
253
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
215
254
|
}
|
|
216
255
|
try {
|
|
@@ -228,12 +267,10 @@ function loadConfigSync() {
|
|
|
228
267
|
}
|
|
229
268
|
async function saveConfig(config) {
|
|
230
269
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
231
|
-
await
|
|
270
|
+
await ensurePrivateDir(dir);
|
|
232
271
|
const configPath = path.join(dir, "config.json");
|
|
233
272
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
234
|
-
|
|
235
|
-
await chmod(configPath, 384);
|
|
236
|
-
}
|
|
273
|
+
await enforcePrivateFile(configPath);
|
|
237
274
|
}
|
|
238
275
|
async function loadConfigFrom(configPath) {
|
|
239
276
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -253,6 +290,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
253
290
|
var init_config = __esm({
|
|
254
291
|
"src/lib/config.ts"() {
|
|
255
292
|
"use strict";
|
|
293
|
+
init_secure_files();
|
|
256
294
|
EXE_AI_DIR = resolveDataDir();
|
|
257
295
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
258
296
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -331,7 +369,7 @@ var init_config = __esm({
|
|
|
331
369
|
|
|
332
370
|
// src/lib/employees.ts
|
|
333
371
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
334
|
-
import { existsSync as
|
|
372
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
335
373
|
import { execSync } from "child_process";
|
|
336
374
|
import path2 from "path";
|
|
337
375
|
import os2 from "os";
|
|
@@ -355,7 +393,7 @@ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
|
355
393
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
356
394
|
}
|
|
357
395
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
358
|
-
if (!
|
|
396
|
+
if (!existsSync3(employeesPath)) {
|
|
359
397
|
return [];
|
|
360
398
|
}
|
|
361
399
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -370,7 +408,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
370
408
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
371
409
|
}
|
|
372
410
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
373
|
-
if (!
|
|
411
|
+
if (!existsSync3(employeesPath)) return [];
|
|
374
412
|
try {
|
|
375
413
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
376
414
|
} catch {
|
|
@@ -404,7 +442,7 @@ function registerBinSymlinks(name) {
|
|
|
404
442
|
for (const suffix of ["", "-opencode"]) {
|
|
405
443
|
const linkName = `${name}${suffix}`;
|
|
406
444
|
const linkPath = path2.join(binDir, linkName);
|
|
407
|
-
if (
|
|
445
|
+
if (existsSync3(linkPath)) {
|
|
408
446
|
skipped.push(linkName);
|
|
409
447
|
continue;
|
|
410
448
|
}
|
|
@@ -1013,13 +1051,50 @@ var init_database_adapter = __esm({
|
|
|
1013
1051
|
}
|
|
1014
1052
|
});
|
|
1015
1053
|
|
|
1054
|
+
// src/lib/daemon-auth.ts
|
|
1055
|
+
import crypto from "crypto";
|
|
1056
|
+
import path4 from "path";
|
|
1057
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1058
|
+
function normalizeToken(token) {
|
|
1059
|
+
if (!token) return null;
|
|
1060
|
+
const trimmed = token.trim();
|
|
1061
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1062
|
+
}
|
|
1063
|
+
function readDaemonToken() {
|
|
1064
|
+
try {
|
|
1065
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
1066
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
1067
|
+
} catch {
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
function ensureDaemonToken(seed) {
|
|
1072
|
+
const existing = readDaemonToken();
|
|
1073
|
+
if (existing) return existing;
|
|
1074
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1075
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1076
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
1077
|
+
`, "utf8");
|
|
1078
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1079
|
+
return token;
|
|
1080
|
+
}
|
|
1081
|
+
var DAEMON_TOKEN_PATH;
|
|
1082
|
+
var init_daemon_auth = __esm({
|
|
1083
|
+
"src/lib/daemon-auth.ts"() {
|
|
1084
|
+
"use strict";
|
|
1085
|
+
init_config();
|
|
1086
|
+
init_secure_files();
|
|
1087
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1016
1091
|
// src/lib/exe-daemon-client.ts
|
|
1017
1092
|
import net from "net";
|
|
1018
1093
|
import os4 from "os";
|
|
1019
1094
|
import { spawn } from "child_process";
|
|
1020
1095
|
import { randomUUID } from "crypto";
|
|
1021
|
-
import { existsSync as
|
|
1022
|
-
import
|
|
1096
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
1097
|
+
import path5 from "path";
|
|
1023
1098
|
import { fileURLToPath } from "url";
|
|
1024
1099
|
function handleData(chunk) {
|
|
1025
1100
|
_buffer += chunk.toString();
|
|
@@ -1047,9 +1122,9 @@ function handleData(chunk) {
|
|
|
1047
1122
|
}
|
|
1048
1123
|
}
|
|
1049
1124
|
function cleanupStaleFiles() {
|
|
1050
|
-
if (
|
|
1125
|
+
if (existsSync5(PID_PATH)) {
|
|
1051
1126
|
try {
|
|
1052
|
-
const pid = parseInt(
|
|
1127
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1053
1128
|
if (pid > 0) {
|
|
1054
1129
|
try {
|
|
1055
1130
|
process.kill(pid, 0);
|
|
@@ -1070,11 +1145,11 @@ function cleanupStaleFiles() {
|
|
|
1070
1145
|
}
|
|
1071
1146
|
}
|
|
1072
1147
|
function findPackageRoot() {
|
|
1073
|
-
let dir =
|
|
1074
|
-
const { root } =
|
|
1148
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1149
|
+
const { root } = path5.parse(dir);
|
|
1075
1150
|
while (dir !== root) {
|
|
1076
|
-
if (
|
|
1077
|
-
dir =
|
|
1151
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
1152
|
+
dir = path5.dirname(dir);
|
|
1078
1153
|
}
|
|
1079
1154
|
return null;
|
|
1080
1155
|
}
|
|
@@ -1100,16 +1175,17 @@ function spawnDaemon() {
|
|
|
1100
1175
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1101
1176
|
return;
|
|
1102
1177
|
}
|
|
1103
|
-
const daemonPath =
|
|
1104
|
-
if (!
|
|
1178
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1179
|
+
if (!existsSync5(daemonPath)) {
|
|
1105
1180
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1106
1181
|
`);
|
|
1107
1182
|
return;
|
|
1108
1183
|
}
|
|
1109
1184
|
const resolvedPath = daemonPath;
|
|
1185
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1110
1186
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1111
1187
|
`);
|
|
1112
|
-
const logPath =
|
|
1188
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1113
1189
|
let stderrFd = "ignore";
|
|
1114
1190
|
try {
|
|
1115
1191
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1127,7 +1203,8 @@ function spawnDaemon() {
|
|
|
1127
1203
|
TMUX_PANE: void 0,
|
|
1128
1204
|
// Prevents resolveExeSession() from scoping to one session
|
|
1129
1205
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1130
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1206
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1207
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1131
1208
|
}
|
|
1132
1209
|
});
|
|
1133
1210
|
child.unref();
|
|
@@ -1237,13 +1314,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1237
1314
|
return;
|
|
1238
1315
|
}
|
|
1239
1316
|
const id = randomUUID();
|
|
1317
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1240
1318
|
const timer = setTimeout(() => {
|
|
1241
1319
|
_pending.delete(id);
|
|
1242
1320
|
resolve({ error: "Request timeout" });
|
|
1243
1321
|
}, timeoutMs);
|
|
1244
1322
|
_pending.set(id, { resolve, timer });
|
|
1245
1323
|
try {
|
|
1246
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1324
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1247
1325
|
} catch {
|
|
1248
1326
|
clearTimeout(timer);
|
|
1249
1327
|
_pending.delete(id);
|
|
@@ -1272,9 +1350,9 @@ function killAndRespawnDaemon() {
|
|
|
1272
1350
|
}
|
|
1273
1351
|
try {
|
|
1274
1352
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1275
|
-
if (
|
|
1353
|
+
if (existsSync5(PID_PATH)) {
|
|
1276
1354
|
try {
|
|
1277
|
-
const pid = parseInt(
|
|
1355
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1278
1356
|
if (pid > 0) {
|
|
1279
1357
|
try {
|
|
1280
1358
|
process.kill(pid, "SIGKILL");
|
|
@@ -1394,17 +1472,19 @@ function disconnectClient() {
|
|
|
1394
1472
|
function isClientConnected() {
|
|
1395
1473
|
return _connected;
|
|
1396
1474
|
}
|
|
1397
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
1475
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
1398
1476
|
var init_exe_daemon_client = __esm({
|
|
1399
1477
|
"src/lib/exe-daemon-client.ts"() {
|
|
1400
1478
|
"use strict";
|
|
1401
1479
|
init_config();
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1480
|
+
init_daemon_auth();
|
|
1481
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1482
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1483
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1405
1484
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1406
1485
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1407
1486
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1487
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1408
1488
|
_socket = null;
|
|
1409
1489
|
_connected = false;
|
|
1410
1490
|
_buffer = "";
|
|
@@ -1989,6 +2069,7 @@ async function ensureSchema() {
|
|
|
1989
2069
|
project TEXT NOT NULL,
|
|
1990
2070
|
summary TEXT NOT NULL,
|
|
1991
2071
|
task_file TEXT,
|
|
2072
|
+
session_scope TEXT,
|
|
1992
2073
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1993
2074
|
created_at TEXT NOT NULL
|
|
1994
2075
|
);
|
|
@@ -1997,7 +2078,7 @@ async function ensureSchema() {
|
|
|
1997
2078
|
ON notifications(read);
|
|
1998
2079
|
|
|
1999
2080
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2000
|
-
ON notifications(agent_id);
|
|
2081
|
+
ON notifications(agent_id, session_scope);
|
|
2001
2082
|
|
|
2002
2083
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2003
2084
|
ON notifications(task_file);
|
|
@@ -2035,6 +2116,7 @@ async function ensureSchema() {
|
|
|
2035
2116
|
target_agent TEXT NOT NULL,
|
|
2036
2117
|
target_project TEXT,
|
|
2037
2118
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2119
|
+
session_scope TEXT,
|
|
2038
2120
|
content TEXT NOT NULL,
|
|
2039
2121
|
priority TEXT DEFAULT 'normal',
|
|
2040
2122
|
status TEXT DEFAULT 'pending',
|
|
@@ -2048,10 +2130,31 @@ async function ensureSchema() {
|
|
|
2048
2130
|
);
|
|
2049
2131
|
|
|
2050
2132
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2051
|
-
ON messages(target_agent, status);
|
|
2133
|
+
ON messages(target_agent, session_scope, status);
|
|
2052
2134
|
|
|
2053
2135
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2054
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2136
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2137
|
+
`);
|
|
2138
|
+
try {
|
|
2139
|
+
await client.execute({
|
|
2140
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2141
|
+
args: []
|
|
2142
|
+
});
|
|
2143
|
+
} catch {
|
|
2144
|
+
}
|
|
2145
|
+
try {
|
|
2146
|
+
await client.execute({
|
|
2147
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2148
|
+
args: []
|
|
2149
|
+
});
|
|
2150
|
+
} catch {
|
|
2151
|
+
}
|
|
2152
|
+
await client.executeMultiple(`
|
|
2153
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2154
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2155
|
+
|
|
2156
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2157
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2055
2158
|
`);
|
|
2056
2159
|
try {
|
|
2057
2160
|
await client.execute({
|
|
@@ -2635,6 +2738,13 @@ async function ensureSchema() {
|
|
|
2635
2738
|
} catch {
|
|
2636
2739
|
}
|
|
2637
2740
|
}
|
|
2741
|
+
try {
|
|
2742
|
+
await client.execute({
|
|
2743
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2744
|
+
args: []
|
|
2745
|
+
});
|
|
2746
|
+
} catch {
|
|
2747
|
+
}
|
|
2638
2748
|
}
|
|
2639
2749
|
async function disposeDatabase() {
|
|
2640
2750
|
if (_walCheckpointTimer) {
|
|
@@ -2682,14 +2792,14 @@ __export(keychain_exports, {
|
|
|
2682
2792
|
setMasterKey: () => setMasterKey
|
|
2683
2793
|
});
|
|
2684
2794
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2685
|
-
import { existsSync as
|
|
2686
|
-
import
|
|
2795
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2796
|
+
import path6 from "path";
|
|
2687
2797
|
import os5 from "os";
|
|
2688
2798
|
function getKeyDir() {
|
|
2689
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2799
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2690
2800
|
}
|
|
2691
2801
|
function getKeyPath() {
|
|
2692
|
-
return
|
|
2802
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2693
2803
|
}
|
|
2694
2804
|
async function tryKeytar() {
|
|
2695
2805
|
try {
|
|
@@ -2710,7 +2820,7 @@ async function getMasterKey() {
|
|
|
2710
2820
|
}
|
|
2711
2821
|
}
|
|
2712
2822
|
const keyPath = getKeyPath();
|
|
2713
|
-
if (!
|
|
2823
|
+
if (!existsSync6(keyPath)) {
|
|
2714
2824
|
process.stderr.write(
|
|
2715
2825
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2716
2826
|
`
|
|
@@ -2753,7 +2863,7 @@ async function deleteMasterKey() {
|
|
|
2753
2863
|
}
|
|
2754
2864
|
}
|
|
2755
2865
|
const keyPath = getKeyPath();
|
|
2756
|
-
if (
|
|
2866
|
+
if (existsSync6(keyPath)) {
|
|
2757
2867
|
await unlink(keyPath);
|
|
2758
2868
|
}
|
|
2759
2869
|
}
|
|
@@ -2855,6 +2965,7 @@ var shard_manager_exports = {};
|
|
|
2855
2965
|
__export(shard_manager_exports, {
|
|
2856
2966
|
disposeShards: () => disposeShards,
|
|
2857
2967
|
ensureShardSchema: () => ensureShardSchema,
|
|
2968
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2858
2969
|
getReadyShardClient: () => getReadyShardClient,
|
|
2859
2970
|
getShardClient: () => getShardClient,
|
|
2860
2971
|
getShardsDir: () => getShardsDir,
|
|
@@ -2863,15 +2974,18 @@ __export(shard_manager_exports, {
|
|
|
2863
2974
|
listShards: () => listShards,
|
|
2864
2975
|
shardExists: () => shardExists
|
|
2865
2976
|
});
|
|
2866
|
-
import
|
|
2867
|
-
import { existsSync as
|
|
2977
|
+
import path7 from "path";
|
|
2978
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2868
2979
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2869
2980
|
function initShardManager(encryptionKey) {
|
|
2870
2981
|
_encryptionKey = encryptionKey;
|
|
2871
|
-
if (!
|
|
2872
|
-
|
|
2982
|
+
if (!existsSync7(SHARDS_DIR)) {
|
|
2983
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2873
2984
|
}
|
|
2874
2985
|
_shardingEnabled = true;
|
|
2986
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2987
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2988
|
+
_evictionTimer.unref();
|
|
2875
2989
|
}
|
|
2876
2990
|
function isShardingEnabled() {
|
|
2877
2991
|
return _shardingEnabled;
|
|
@@ -2888,21 +3002,28 @@ function getShardClient(projectName) {
|
|
|
2888
3002
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2889
3003
|
}
|
|
2890
3004
|
const cached = _shards.get(safeName);
|
|
2891
|
-
if (cached)
|
|
2892
|
-
|
|
3005
|
+
if (cached) {
|
|
3006
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3007
|
+
return cached;
|
|
3008
|
+
}
|
|
3009
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3010
|
+
evictLRU();
|
|
3011
|
+
}
|
|
3012
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2893
3013
|
const client = createClient2({
|
|
2894
3014
|
url: `file:${dbPath}`,
|
|
2895
3015
|
encryptionKey: _encryptionKey
|
|
2896
3016
|
});
|
|
2897
3017
|
_shards.set(safeName, client);
|
|
3018
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2898
3019
|
return client;
|
|
2899
3020
|
}
|
|
2900
3021
|
function shardExists(projectName) {
|
|
2901
3022
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2902
|
-
return
|
|
3023
|
+
return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2903
3024
|
}
|
|
2904
3025
|
function listShards() {
|
|
2905
|
-
if (!
|
|
3026
|
+
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2906
3027
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2907
3028
|
}
|
|
2908
3029
|
async function ensureShardSchema(client) {
|
|
@@ -2954,6 +3075,8 @@ async function ensureShardSchema(client) {
|
|
|
2954
3075
|
for (const col of [
|
|
2955
3076
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2956
3077
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3078
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3079
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2957
3080
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2958
3081
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2959
3082
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -3091,21 +3214,69 @@ async function getReadyShardClient(projectName) {
|
|
|
3091
3214
|
await ensureShardSchema(client);
|
|
3092
3215
|
return client;
|
|
3093
3216
|
}
|
|
3217
|
+
function evictLRU() {
|
|
3218
|
+
let oldest = null;
|
|
3219
|
+
let oldestTime = Infinity;
|
|
3220
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3221
|
+
if (time < oldestTime) {
|
|
3222
|
+
oldestTime = time;
|
|
3223
|
+
oldest = name;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
if (oldest) {
|
|
3227
|
+
const client = _shards.get(oldest);
|
|
3228
|
+
if (client) {
|
|
3229
|
+
client.close();
|
|
3230
|
+
}
|
|
3231
|
+
_shards.delete(oldest);
|
|
3232
|
+
_shardLastAccess.delete(oldest);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
function evictIdleShards() {
|
|
3236
|
+
const now = Date.now();
|
|
3237
|
+
const toEvict = [];
|
|
3238
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3239
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3240
|
+
toEvict.push(name);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
for (const name of toEvict) {
|
|
3244
|
+
const client = _shards.get(name);
|
|
3245
|
+
if (client) {
|
|
3246
|
+
client.close();
|
|
3247
|
+
}
|
|
3248
|
+
_shards.delete(name);
|
|
3249
|
+
_shardLastAccess.delete(name);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function getOpenShardCount() {
|
|
3253
|
+
return _shards.size;
|
|
3254
|
+
}
|
|
3094
3255
|
function disposeShards() {
|
|
3256
|
+
if (_evictionTimer) {
|
|
3257
|
+
clearInterval(_evictionTimer);
|
|
3258
|
+
_evictionTimer = null;
|
|
3259
|
+
}
|
|
3095
3260
|
for (const [, client] of _shards) {
|
|
3096
3261
|
client.close();
|
|
3097
3262
|
}
|
|
3098
3263
|
_shards.clear();
|
|
3264
|
+
_shardLastAccess.clear();
|
|
3099
3265
|
_shardingEnabled = false;
|
|
3100
3266
|
_encryptionKey = null;
|
|
3101
3267
|
}
|
|
3102
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3268
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3103
3269
|
var init_shard_manager = __esm({
|
|
3104
3270
|
"src/lib/shard-manager.ts"() {
|
|
3105
3271
|
"use strict";
|
|
3106
3272
|
init_config();
|
|
3107
|
-
SHARDS_DIR =
|
|
3273
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
3274
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3275
|
+
MAX_OPEN_SHARDS = 10;
|
|
3276
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3108
3277
|
_shards = /* @__PURE__ */ new Map();
|
|
3278
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3279
|
+
_evictionTimer = null;
|
|
3109
3280
|
_encryptionKey = null;
|
|
3110
3281
|
_shardingEnabled = false;
|
|
3111
3282
|
}
|
|
@@ -3298,56 +3469,14 @@ ${p.content}`).join("\n\n");
|
|
|
3298
3469
|
}
|
|
3299
3470
|
});
|
|
3300
3471
|
|
|
3301
|
-
// src/lib/notifications.ts
|
|
3302
|
-
import crypto from "crypto";
|
|
3303
|
-
import path7 from "path";
|
|
3304
|
-
import os6 from "os";
|
|
3305
|
-
import {
|
|
3306
|
-
readFileSync as readFileSync4,
|
|
3307
|
-
readdirSync as readdirSync2,
|
|
3308
|
-
unlinkSync as unlinkSync3,
|
|
3309
|
-
existsSync as existsSync6,
|
|
3310
|
-
rmdirSync
|
|
3311
|
-
} from "fs";
|
|
3312
|
-
async function writeNotification(notification) {
|
|
3313
|
-
try {
|
|
3314
|
-
const client = getClient();
|
|
3315
|
-
const id = crypto.randomUUID();
|
|
3316
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3317
|
-
await client.execute({
|
|
3318
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3319
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3320
|
-
args: [
|
|
3321
|
-
id,
|
|
3322
|
-
notification.agentId,
|
|
3323
|
-
notification.agentRole,
|
|
3324
|
-
notification.event,
|
|
3325
|
-
notification.project,
|
|
3326
|
-
notification.summary,
|
|
3327
|
-
notification.taskFile ?? null,
|
|
3328
|
-
now
|
|
3329
|
-
]
|
|
3330
|
-
});
|
|
3331
|
-
} catch (err) {
|
|
3332
|
-
process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
3333
|
-
`);
|
|
3334
|
-
}
|
|
3335
|
-
}
|
|
3336
|
-
var init_notifications = __esm({
|
|
3337
|
-
"src/lib/notifications.ts"() {
|
|
3338
|
-
"use strict";
|
|
3339
|
-
init_database();
|
|
3340
|
-
}
|
|
3341
|
-
});
|
|
3342
|
-
|
|
3343
3472
|
// src/lib/session-registry.ts
|
|
3344
3473
|
import path8 from "path";
|
|
3345
|
-
import
|
|
3474
|
+
import os6 from "os";
|
|
3346
3475
|
var REGISTRY_PATH;
|
|
3347
3476
|
var init_session_registry = __esm({
|
|
3348
3477
|
"src/lib/session-registry.ts"() {
|
|
3349
3478
|
"use strict";
|
|
3350
|
-
REGISTRY_PATH = path8.join(
|
|
3479
|
+
REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
3351
3480
|
}
|
|
3352
3481
|
});
|
|
3353
3482
|
|
|
@@ -3582,7 +3711,7 @@ var init_runtime_table = __esm({
|
|
|
3582
3711
|
});
|
|
3583
3712
|
|
|
3584
3713
|
// src/lib/agent-config.ts
|
|
3585
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
3714
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8 } from "fs";
|
|
3586
3715
|
import path9 from "path";
|
|
3587
3716
|
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
3588
3717
|
var init_agent_config = __esm({
|
|
@@ -3590,6 +3719,7 @@ var init_agent_config = __esm({
|
|
|
3590
3719
|
"use strict";
|
|
3591
3720
|
init_config();
|
|
3592
3721
|
init_runtime_table();
|
|
3722
|
+
init_secure_files();
|
|
3593
3723
|
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
3594
3724
|
DEFAULT_MODELS = {
|
|
3595
3725
|
claude: "claude-opus-4",
|
|
@@ -3600,16 +3730,16 @@ var init_agent_config = __esm({
|
|
|
3600
3730
|
});
|
|
3601
3731
|
|
|
3602
3732
|
// src/lib/intercom-queue.ts
|
|
3603
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
3733
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
|
|
3604
3734
|
import path10 from "path";
|
|
3605
|
-
import
|
|
3735
|
+
import os7 from "os";
|
|
3606
3736
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
3607
3737
|
var init_intercom_queue = __esm({
|
|
3608
3738
|
"src/lib/intercom-queue.ts"() {
|
|
3609
3739
|
"use strict";
|
|
3610
|
-
QUEUE_PATH = path10.join(
|
|
3740
|
+
QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
3611
3741
|
TTL_MS = 60 * 60 * 1e3;
|
|
3612
|
-
INTERCOM_LOG = path10.join(
|
|
3742
|
+
INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3613
3743
|
}
|
|
3614
3744
|
});
|
|
3615
3745
|
|
|
@@ -3630,8 +3760,11 @@ __export(license_exports, {
|
|
|
3630
3760
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
3631
3761
|
validateLicense: () => validateLicense
|
|
3632
3762
|
});
|
|
3633
|
-
import { readFileSync as readFileSync7, writeFileSync as
|
|
3763
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
3634
3764
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3765
|
+
import { createRequire as createRequire2 } from "module";
|
|
3766
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3767
|
+
import os8 from "os";
|
|
3635
3768
|
import path11 from "path";
|
|
3636
3769
|
import { jwtVerify, importSPKI } from "jose";
|
|
3637
3770
|
async function fetchRetry(url, init) {
|
|
@@ -3645,14 +3778,14 @@ async function fetchRetry(url, init) {
|
|
|
3645
3778
|
function loadDeviceId() {
|
|
3646
3779
|
const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
|
|
3647
3780
|
try {
|
|
3648
|
-
if (
|
|
3781
|
+
if (existsSync10(deviceJsonPath)) {
|
|
3649
3782
|
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
3650
3783
|
if (data.deviceId) return data.deviceId;
|
|
3651
3784
|
}
|
|
3652
3785
|
} catch {
|
|
3653
3786
|
}
|
|
3654
3787
|
try {
|
|
3655
|
-
if (
|
|
3788
|
+
if (existsSync10(DEVICE_ID_PATH)) {
|
|
3656
3789
|
const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
|
|
3657
3790
|
if (id2) return id2;
|
|
3658
3791
|
}
|
|
@@ -3660,12 +3793,12 @@ function loadDeviceId() {
|
|
|
3660
3793
|
}
|
|
3661
3794
|
const id = randomUUID3();
|
|
3662
3795
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
3663
|
-
|
|
3796
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
3664
3797
|
return id;
|
|
3665
3798
|
}
|
|
3666
3799
|
function loadLicense() {
|
|
3667
3800
|
try {
|
|
3668
|
-
if (!
|
|
3801
|
+
if (!existsSync10(LICENSE_PATH)) return null;
|
|
3669
3802
|
return readFileSync7(LICENSE_PATH, "utf8").trim();
|
|
3670
3803
|
} catch {
|
|
3671
3804
|
return null;
|
|
@@ -3673,7 +3806,7 @@ function loadLicense() {
|
|
|
3673
3806
|
}
|
|
3674
3807
|
function saveLicense(apiKey) {
|
|
3675
3808
|
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
3676
|
-
|
|
3809
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3677
3810
|
}
|
|
3678
3811
|
async function verifyLicenseJwt(token) {
|
|
3679
3812
|
try {
|
|
@@ -3699,7 +3832,7 @@ async function verifyLicenseJwt(token) {
|
|
|
3699
3832
|
}
|
|
3700
3833
|
async function getCachedLicense() {
|
|
3701
3834
|
try {
|
|
3702
|
-
if (!
|
|
3835
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3703
3836
|
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
3704
3837
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3705
3838
|
return await verifyLicenseJwt(raw.token);
|
|
@@ -3709,7 +3842,7 @@ async function getCachedLicense() {
|
|
|
3709
3842
|
}
|
|
3710
3843
|
function readCachedToken() {
|
|
3711
3844
|
try {
|
|
3712
|
-
if (!
|
|
3845
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3713
3846
|
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
3714
3847
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3715
3848
|
} catch {
|
|
@@ -3744,56 +3877,130 @@ function getRawCachedPlan() {
|
|
|
3744
3877
|
}
|
|
3745
3878
|
function cacheResponse(token) {
|
|
3746
3879
|
try {
|
|
3747
|
-
|
|
3880
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
3748
3881
|
} catch {
|
|
3749
3882
|
}
|
|
3750
3883
|
}
|
|
3751
|
-
|
|
3752
|
-
|
|
3884
|
+
function loadPrismaForLicense() {
|
|
3885
|
+
if (_prismaFailed) return null;
|
|
3886
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
3887
|
+
if (!dbUrl) {
|
|
3888
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
|
|
3889
|
+
if (!existsSync10(path11.join(exeDbRoot, "package.json"))) {
|
|
3890
|
+
_prismaFailed = true;
|
|
3891
|
+
return null;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
if (!_prismaPromise) {
|
|
3895
|
+
_prismaPromise = (async () => {
|
|
3896
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
3897
|
+
if (explicitPath) {
|
|
3898
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
3899
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
3900
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
3901
|
+
return new Ctor2();
|
|
3902
|
+
}
|
|
3903
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
|
|
3904
|
+
const req = createRequire2(path11.join(exeDbRoot, "package.json"));
|
|
3905
|
+
const entry = req.resolve("@prisma/client");
|
|
3906
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
3907
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3908
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
3909
|
+
return new Ctor();
|
|
3910
|
+
})().catch((err) => {
|
|
3911
|
+
_prismaFailed = true;
|
|
3912
|
+
_prismaPromise = null;
|
|
3913
|
+
throw err;
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3916
|
+
return _prismaPromise;
|
|
3917
|
+
}
|
|
3918
|
+
async function validateViaPostgres(apiKey) {
|
|
3919
|
+
const loader = loadPrismaForLicense();
|
|
3920
|
+
if (!loader) return null;
|
|
3921
|
+
try {
|
|
3922
|
+
const prisma = await loader;
|
|
3923
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
3924
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
3925
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
3926
|
+
apiKey
|
|
3927
|
+
);
|
|
3928
|
+
if (!rows || rows.length === 0) return null;
|
|
3929
|
+
const row = rows[0];
|
|
3930
|
+
if (row.status !== "active") return null;
|
|
3931
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
3932
|
+
const plan = row.plan;
|
|
3933
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
3934
|
+
return {
|
|
3935
|
+
valid: true,
|
|
3936
|
+
plan,
|
|
3937
|
+
email: row.email,
|
|
3938
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
3939
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
3940
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
3941
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
3942
|
+
};
|
|
3943
|
+
} catch {
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
3753
3948
|
try {
|
|
3754
3949
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3755
3950
|
method: "POST",
|
|
3756
3951
|
headers: { "Content-Type": "application/json" },
|
|
3757
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
3952
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
3758
3953
|
signal: AbortSignal.timeout(1e4)
|
|
3759
3954
|
});
|
|
3760
|
-
if (res.ok)
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3955
|
+
if (!res.ok) return null;
|
|
3956
|
+
const data = await res.json();
|
|
3957
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
3958
|
+
if (!data.valid) return null;
|
|
3959
|
+
if (data.token) {
|
|
3960
|
+
cacheResponse(data.token);
|
|
3961
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
3962
|
+
if (verified) return verified;
|
|
3963
|
+
}
|
|
3964
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3965
|
+
return {
|
|
3966
|
+
valid: data.valid,
|
|
3967
|
+
plan: data.plan,
|
|
3968
|
+
email: data.email,
|
|
3969
|
+
expiresAt: data.expiresAt,
|
|
3970
|
+
deviceLimit: limits.devices,
|
|
3971
|
+
employeeLimit: limits.employees,
|
|
3972
|
+
memoryLimit: limits.memories
|
|
3973
|
+
};
|
|
3974
|
+
} catch {
|
|
3975
|
+
return null;
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
async function validateLicense(apiKey, deviceId) {
|
|
3979
|
+
const did = deviceId ?? loadDeviceId();
|
|
3980
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
3981
|
+
if (pgResult) {
|
|
3982
|
+
try {
|
|
3983
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
3984
|
+
} catch {
|
|
3985
|
+
}
|
|
3986
|
+
return pgResult;
|
|
3987
|
+
}
|
|
3988
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
3989
|
+
if (cfResult) return cfResult;
|
|
3990
|
+
const cached = await getCachedLicense();
|
|
3991
|
+
if (cached) return cached;
|
|
3992
|
+
try {
|
|
3993
|
+
if (existsSync10(CACHE_PATH)) {
|
|
3994
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
3995
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
3996
|
+
return raw.pgLicense;
|
|
3773
3997
|
}
|
|
3774
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3775
|
-
return {
|
|
3776
|
-
valid: data.valid,
|
|
3777
|
-
plan: data.plan,
|
|
3778
|
-
email: data.email,
|
|
3779
|
-
expiresAt: data.expiresAt,
|
|
3780
|
-
deviceLimit: limits.devices,
|
|
3781
|
-
employeeLimit: limits.employees,
|
|
3782
|
-
memoryLimit: limits.memories
|
|
3783
|
-
};
|
|
3784
3998
|
}
|
|
3785
|
-
const cached = await getCachedLicense();
|
|
3786
|
-
if (cached) return cached;
|
|
3787
|
-
const raw = getRawCachedPlan();
|
|
3788
|
-
if (raw) return raw;
|
|
3789
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
3790
3999
|
} catch {
|
|
3791
|
-
const cached = await getCachedLicense();
|
|
3792
|
-
if (cached) return cached;
|
|
3793
|
-
const rawFallback = getRawCachedPlan();
|
|
3794
|
-
if (rawFallback) return rawFallback;
|
|
3795
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
3796
4000
|
}
|
|
4001
|
+
const rawFallback = getRawCachedPlan();
|
|
4002
|
+
if (rawFallback) return rawFallback;
|
|
4003
|
+
return { ...FREE_LICENSE, valid: false };
|
|
3797
4004
|
}
|
|
3798
4005
|
function getCacheAgeMs() {
|
|
3799
4006
|
try {
|
|
@@ -3809,7 +4016,7 @@ async function checkLicense() {
|
|
|
3809
4016
|
if (!key) {
|
|
3810
4017
|
try {
|
|
3811
4018
|
const configPath = path11.join(EXE_AI_DIR, "config.json");
|
|
3812
|
-
if (
|
|
4019
|
+
if (existsSync10(configPath)) {
|
|
3813
4020
|
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3814
4021
|
const cloud = raw.cloud;
|
|
3815
4022
|
if (cloud?.apiKey) {
|
|
@@ -3964,7 +4171,7 @@ function stopLicenseRevalidation() {
|
|
|
3964
4171
|
_revalTimer = null;
|
|
3965
4172
|
}
|
|
3966
4173
|
}
|
|
3967
|
-
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;
|
|
4174
|
+
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;
|
|
3968
4175
|
var init_license = __esm({
|
|
3969
4176
|
"src/lib/license.ts"() {
|
|
3970
4177
|
"use strict";
|
|
@@ -3995,6 +4202,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
3995
4202
|
employeeLimit: 1,
|
|
3996
4203
|
memoryLimit: 5e3
|
|
3997
4204
|
};
|
|
4205
|
+
_prismaPromise = null;
|
|
4206
|
+
_prismaFailed = false;
|
|
3998
4207
|
CACHE_MAX_AGE_MS = 36e5;
|
|
3999
4208
|
_revalTimer = null;
|
|
4000
4209
|
}
|
|
@@ -4011,11 +4220,11 @@ __export(plan_limits_exports, {
|
|
|
4011
4220
|
countActiveMemories: () => countActiveMemories,
|
|
4012
4221
|
getLicenseSync: () => getLicenseSync
|
|
4013
4222
|
});
|
|
4014
|
-
import { readFileSync as readFileSync8, existsSync as
|
|
4223
|
+
import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
|
|
4015
4224
|
import path12 from "path";
|
|
4016
4225
|
function getLicenseSync() {
|
|
4017
4226
|
try {
|
|
4018
|
-
if (!
|
|
4227
|
+
if (!existsSync11(CACHE_PATH2)) return freeLicense();
|
|
4019
4228
|
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
4020
4229
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4021
4230
|
const parts = raw.token.split(".");
|
|
@@ -4083,7 +4292,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4083
4292
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
4084
4293
|
let count = 0;
|
|
4085
4294
|
try {
|
|
4086
|
-
if (
|
|
4295
|
+
if (existsSync11(filePath)) {
|
|
4087
4296
|
const raw = readFileSync8(filePath, "utf8");
|
|
4088
4297
|
const employees = JSON.parse(raw);
|
|
4089
4298
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
@@ -4126,7 +4335,7 @@ var init_plan_limits = __esm({
|
|
|
4126
4335
|
});
|
|
4127
4336
|
|
|
4128
4337
|
// src/lib/tmux-routing.ts
|
|
4129
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
4338
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync2 } from "fs";
|
|
4130
4339
|
import path13 from "path";
|
|
4131
4340
|
import os9 from "os";
|
|
4132
4341
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -4216,6 +4425,51 @@ var init_task_scope = __esm({
|
|
|
4216
4425
|
}
|
|
4217
4426
|
});
|
|
4218
4427
|
|
|
4428
|
+
// src/lib/notifications.ts
|
|
4429
|
+
import crypto2 from "crypto";
|
|
4430
|
+
import path14 from "path";
|
|
4431
|
+
import os10 from "os";
|
|
4432
|
+
import {
|
|
4433
|
+
readFileSync as readFileSync10,
|
|
4434
|
+
readdirSync as readdirSync3,
|
|
4435
|
+
unlinkSync as unlinkSync3,
|
|
4436
|
+
existsSync as existsSync13,
|
|
4437
|
+
rmdirSync
|
|
4438
|
+
} from "fs";
|
|
4439
|
+
async function writeNotification(notification) {
|
|
4440
|
+
try {
|
|
4441
|
+
const client = getClient();
|
|
4442
|
+
const id = crypto2.randomUUID();
|
|
4443
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4444
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
4445
|
+
await client.execute({
|
|
4446
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
4447
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4448
|
+
args: [
|
|
4449
|
+
id,
|
|
4450
|
+
notification.agentId,
|
|
4451
|
+
notification.agentRole,
|
|
4452
|
+
notification.event,
|
|
4453
|
+
notification.project,
|
|
4454
|
+
notification.summary,
|
|
4455
|
+
notification.taskFile ?? null,
|
|
4456
|
+
sessionScope,
|
|
4457
|
+
now
|
|
4458
|
+
]
|
|
4459
|
+
});
|
|
4460
|
+
} catch (err) {
|
|
4461
|
+
process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
4462
|
+
`);
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
var init_notifications = __esm({
|
|
4466
|
+
"src/lib/notifications.ts"() {
|
|
4467
|
+
"use strict";
|
|
4468
|
+
init_database();
|
|
4469
|
+
init_task_scope();
|
|
4470
|
+
}
|
|
4471
|
+
});
|
|
4472
|
+
|
|
4219
4473
|
// src/lib/embedder.ts
|
|
4220
4474
|
var embedder_exports = {};
|
|
4221
4475
|
__export(embedder_exports, {
|
|
@@ -4253,10 +4507,10 @@ async function disposeEmbedder() {
|
|
|
4253
4507
|
async function embedDirect(text) {
|
|
4254
4508
|
const llamaCpp = await import("node-llama-cpp");
|
|
4255
4509
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4256
|
-
const { existsSync:
|
|
4257
|
-
const
|
|
4258
|
-
const modelPath =
|
|
4259
|
-
if (!
|
|
4510
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
4511
|
+
const path19 = await import("path");
|
|
4512
|
+
const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4513
|
+
if (!existsSync18(modelPath)) {
|
|
4260
4514
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4261
4515
|
}
|
|
4262
4516
|
const llama = await llamaCpp.getLlama();
|
|
@@ -4294,14 +4548,14 @@ __export(worker_gate_exports, {
|
|
|
4294
4548
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
4295
4549
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
4296
4550
|
});
|
|
4297
|
-
import { readdirSync as readdirSync4, writeFileSync as
|
|
4298
|
-
import
|
|
4551
|
+
import { readdirSync as readdirSync4, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6, existsSync as existsSync14 } from "fs";
|
|
4552
|
+
import path15 from "path";
|
|
4299
4553
|
function tryAcquireWorkerSlot() {
|
|
4300
4554
|
try {
|
|
4301
4555
|
mkdirSync6(WORKER_PID_DIR, { recursive: true });
|
|
4302
4556
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
4303
|
-
const reservationPath =
|
|
4304
|
-
|
|
4557
|
+
const reservationPath = path15.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
4558
|
+
writeFileSync7(reservationPath, String(process.pid));
|
|
4305
4559
|
const files = readdirSync4(WORKER_PID_DIR);
|
|
4306
4560
|
let alive = 0;
|
|
4307
4561
|
for (const f of files) {
|
|
@@ -4318,7 +4572,7 @@ function tryAcquireWorkerSlot() {
|
|
|
4318
4572
|
alive++;
|
|
4319
4573
|
} catch {
|
|
4320
4574
|
try {
|
|
4321
|
-
unlinkSync4(
|
|
4575
|
+
unlinkSync4(path15.join(WORKER_PID_DIR, f));
|
|
4322
4576
|
} catch {
|
|
4323
4577
|
}
|
|
4324
4578
|
}
|
|
@@ -4342,20 +4596,20 @@ function tryAcquireWorkerSlot() {
|
|
|
4342
4596
|
function registerWorkerPid(pid) {
|
|
4343
4597
|
try {
|
|
4344
4598
|
mkdirSync6(WORKER_PID_DIR, { recursive: true });
|
|
4345
|
-
|
|
4599
|
+
writeFileSync7(path15.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
4346
4600
|
} catch {
|
|
4347
4601
|
}
|
|
4348
4602
|
}
|
|
4349
4603
|
function cleanupWorkerPid() {
|
|
4350
4604
|
try {
|
|
4351
|
-
unlinkSync4(
|
|
4605
|
+
unlinkSync4(path15.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
4352
4606
|
} catch {
|
|
4353
4607
|
}
|
|
4354
4608
|
}
|
|
4355
4609
|
function tryAcquireBackfillLock() {
|
|
4356
4610
|
try {
|
|
4357
4611
|
mkdirSync6(WORKER_PID_DIR, { recursive: true });
|
|
4358
|
-
if (
|
|
4612
|
+
if (existsSync14(BACKFILL_LOCK)) {
|
|
4359
4613
|
try {
|
|
4360
4614
|
const pid = parseInt(
|
|
4361
4615
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -4371,7 +4625,7 @@ function tryAcquireBackfillLock() {
|
|
|
4371
4625
|
} catch {
|
|
4372
4626
|
}
|
|
4373
4627
|
}
|
|
4374
|
-
|
|
4628
|
+
writeFileSync7(BACKFILL_LOCK, String(process.pid));
|
|
4375
4629
|
return true;
|
|
4376
4630
|
} catch {
|
|
4377
4631
|
return true;
|
|
@@ -4388,9 +4642,9 @@ var init_worker_gate = __esm({
|
|
|
4388
4642
|
"src/lib/worker-gate.ts"() {
|
|
4389
4643
|
"use strict";
|
|
4390
4644
|
init_config();
|
|
4391
|
-
WORKER_PID_DIR =
|
|
4645
|
+
WORKER_PID_DIR = path15.join(EXE_AI_DIR, "worker-pids");
|
|
4392
4646
|
MAX_CONCURRENT_WORKERS = 3;
|
|
4393
|
-
BACKFILL_LOCK =
|
|
4647
|
+
BACKFILL_LOCK = path15.join(WORKER_PID_DIR, "backfill.lock");
|
|
4394
4648
|
}
|
|
4395
4649
|
});
|
|
4396
4650
|
|
|
@@ -4402,13 +4656,13 @@ __export(crypto_exports, {
|
|
|
4402
4656
|
initSyncCrypto: () => initSyncCrypto,
|
|
4403
4657
|
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
4404
4658
|
});
|
|
4405
|
-
import
|
|
4659
|
+
import crypto3 from "crypto";
|
|
4406
4660
|
function initSyncCrypto(masterKey) {
|
|
4407
4661
|
if (masterKey.length !== 32) {
|
|
4408
4662
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
4409
4663
|
}
|
|
4410
4664
|
_syncKey = Buffer.from(
|
|
4411
|
-
|
|
4665
|
+
crypto3.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
4412
4666
|
);
|
|
4413
4667
|
}
|
|
4414
4668
|
function isSyncCryptoInitialized() {
|
|
@@ -4422,8 +4676,8 @@ function requireSyncKey() {
|
|
|
4422
4676
|
}
|
|
4423
4677
|
function encryptSyncBlob(data) {
|
|
4424
4678
|
const key = requireSyncKey();
|
|
4425
|
-
const iv =
|
|
4426
|
-
const cipher =
|
|
4679
|
+
const iv = crypto3.randomBytes(IV_LENGTH);
|
|
4680
|
+
const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
|
|
4427
4681
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
4428
4682
|
const tag = cipher.getAuthTag();
|
|
4429
4683
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -4437,7 +4691,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
4437
4691
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
4438
4692
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
4439
4693
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
4440
|
-
const decipher =
|
|
4694
|
+
const decipher = crypto3.createDecipheriv(ALGORITHM, key, iv);
|
|
4441
4695
|
decipher.setAuthTag(tag);
|
|
4442
4696
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
4443
4697
|
}
|
|
@@ -4492,8 +4746,8 @@ __export(crdt_sync_exports, {
|
|
|
4492
4746
|
rebuildFromDb: () => rebuildFromDb
|
|
4493
4747
|
});
|
|
4494
4748
|
import * as Y from "yjs";
|
|
4495
|
-
import { readFileSync as
|
|
4496
|
-
import
|
|
4749
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
4750
|
+
import path16 from "path";
|
|
4497
4751
|
import { homedir } from "os";
|
|
4498
4752
|
function getStatePath() {
|
|
4499
4753
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -4505,9 +4759,9 @@ function initCrdtDoc() {
|
|
|
4505
4759
|
if (doc) return doc;
|
|
4506
4760
|
doc = new Y.Doc();
|
|
4507
4761
|
const sp = getStatePath();
|
|
4508
|
-
if (
|
|
4762
|
+
if (existsSync15(sp)) {
|
|
4509
4763
|
try {
|
|
4510
|
-
const state =
|
|
4764
|
+
const state = readFileSync11(sp);
|
|
4511
4765
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
4512
4766
|
} catch {
|
|
4513
4767
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -4649,10 +4903,10 @@ function persistState() {
|
|
|
4649
4903
|
if (!doc) return;
|
|
4650
4904
|
try {
|
|
4651
4905
|
const sp = getStatePath();
|
|
4652
|
-
const dir =
|
|
4653
|
-
if (!
|
|
4906
|
+
const dir = path16.dirname(sp);
|
|
4907
|
+
if (!existsSync15(dir)) mkdirSync7(dir, { recursive: true });
|
|
4654
4908
|
const state = Y.encodeStateAsUpdate(doc);
|
|
4655
|
-
|
|
4909
|
+
writeFileSync8(sp, Buffer.from(state));
|
|
4656
4910
|
} catch {
|
|
4657
4911
|
}
|
|
4658
4912
|
}
|
|
@@ -4693,7 +4947,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
4693
4947
|
var init_crdt_sync = __esm({
|
|
4694
4948
|
"src/lib/crdt-sync.ts"() {
|
|
4695
4949
|
"use strict";
|
|
4696
|
-
DEFAULT_STATE_PATH =
|
|
4950
|
+
DEFAULT_STATE_PATH = path16.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
4697
4951
|
_statePathOverride = null;
|
|
4698
4952
|
doc = null;
|
|
4699
4953
|
}
|
|
@@ -4725,39 +4979,107 @@ __export(cloud_sync_exports, {
|
|
|
4725
4979
|
cloudSync: () => cloudSync,
|
|
4726
4980
|
mergeConfig: () => mergeConfig,
|
|
4727
4981
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4982
|
+
pushToPostgres: () => pushToPostgres,
|
|
4728
4983
|
recordRosterDeletion: () => recordRosterDeletion
|
|
4729
4984
|
});
|
|
4730
|
-
import { readFileSync as
|
|
4731
|
-
import
|
|
4732
|
-
import
|
|
4985
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync16, readdirSync as readdirSync5, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
4986
|
+
import crypto4 from "crypto";
|
|
4987
|
+
import path17 from "path";
|
|
4733
4988
|
import { homedir as homedir2 } from "os";
|
|
4734
4989
|
function sqlSafe(v) {
|
|
4735
4990
|
return v === void 0 ? null : v;
|
|
4736
4991
|
}
|
|
4737
4992
|
function logError(msg) {
|
|
4738
4993
|
try {
|
|
4739
|
-
const logPath =
|
|
4994
|
+
const logPath = path17.join(homedir2(), ".exe-os", "workers.log");
|
|
4740
4995
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
4741
4996
|
`);
|
|
4742
4997
|
} catch {
|
|
4743
4998
|
}
|
|
4744
4999
|
}
|
|
5000
|
+
function loadPgClient() {
|
|
5001
|
+
if (_pgFailed) return null;
|
|
5002
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
5003
|
+
const configPath = path17.join(EXE_AI_DIR, "config.json");
|
|
5004
|
+
let cloudPostgresUrl;
|
|
5005
|
+
try {
|
|
5006
|
+
if (existsSync16(configPath)) {
|
|
5007
|
+
const cfg = JSON.parse(readFileSync12(configPath, "utf8"));
|
|
5008
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
5009
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
5010
|
+
_pgFailed = true;
|
|
5011
|
+
return null;
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
} catch {
|
|
5015
|
+
}
|
|
5016
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
5017
|
+
if (!url) {
|
|
5018
|
+
_pgFailed = true;
|
|
5019
|
+
return null;
|
|
5020
|
+
}
|
|
5021
|
+
if (!_pgPromise) {
|
|
5022
|
+
_pgPromise = (async () => {
|
|
5023
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
5024
|
+
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
5025
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path17.join(homedir2(), "exe-db");
|
|
5026
|
+
const req = createRequire3(path17.join(exeDbRoot, "package.json"));
|
|
5027
|
+
const entry = req.resolve("@prisma/client");
|
|
5028
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
5029
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
5030
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
5031
|
+
return new Ctor();
|
|
5032
|
+
})().catch(() => {
|
|
5033
|
+
_pgFailed = true;
|
|
5034
|
+
_pgPromise = null;
|
|
5035
|
+
throw new Error("pg_unavailable");
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
return _pgPromise;
|
|
5039
|
+
}
|
|
5040
|
+
async function pushToPostgres(records) {
|
|
5041
|
+
const loader = loadPgClient();
|
|
5042
|
+
if (!loader) return 0;
|
|
5043
|
+
let prisma;
|
|
5044
|
+
try {
|
|
5045
|
+
prisma = await loader;
|
|
5046
|
+
} catch {
|
|
5047
|
+
return 0;
|
|
5048
|
+
}
|
|
5049
|
+
let inserted = 0;
|
|
5050
|
+
for (const rec of records) {
|
|
5051
|
+
try {
|
|
5052
|
+
await prisma.$executeRawUnsafe(
|
|
5053
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
5054
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
5055
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
5056
|
+
String(rec.id ?? ""),
|
|
5057
|
+
JSON.stringify(rec),
|
|
5058
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
5059
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
5060
|
+
);
|
|
5061
|
+
inserted++;
|
|
5062
|
+
} catch {
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
return inserted;
|
|
5066
|
+
}
|
|
4745
5067
|
async function withRosterLock(fn) {
|
|
4746
5068
|
try {
|
|
4747
5069
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
4748
5070
|
closeSync2(fd);
|
|
4749
|
-
|
|
5071
|
+
writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4750
5072
|
} catch (err) {
|
|
4751
5073
|
if (err.code === "EEXIST") {
|
|
4752
5074
|
try {
|
|
4753
|
-
const ts = parseInt(
|
|
5075
|
+
const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
4754
5076
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
4755
5077
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
4756
5078
|
}
|
|
4757
5079
|
unlinkSync6(ROSTER_LOCK_PATH);
|
|
4758
5080
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
4759
5081
|
closeSync2(fd);
|
|
4760
|
-
|
|
5082
|
+
writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4761
5083
|
} catch (retryErr) {
|
|
4762
5084
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
4763
5085
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -5027,6 +5349,10 @@ async function cloudSync(config) {
|
|
|
5027
5349
|
const maxVersion = Number(records[records.length - 1].version);
|
|
5028
5350
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
5029
5351
|
if (!pushOk) break;
|
|
5352
|
+
try {
|
|
5353
|
+
await pushToPostgres(records);
|
|
5354
|
+
} catch {
|
|
5355
|
+
}
|
|
5030
5356
|
await client.execute({
|
|
5031
5357
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
5032
5358
|
args: [String(maxVersion)]
|
|
@@ -5131,8 +5457,8 @@ async function cloudSync(config) {
|
|
|
5131
5457
|
try {
|
|
5132
5458
|
const employees = await loadEmployees();
|
|
5133
5459
|
rosterResult.employees = employees.length;
|
|
5134
|
-
const idDir =
|
|
5135
|
-
if (
|
|
5460
|
+
const idDir = path17.join(EXE_AI_DIR, "identity");
|
|
5461
|
+
if (existsSync16(idDir)) {
|
|
5136
5462
|
rosterResult.identities = readdirSync5(idDir).filter((f) => f.endsWith(".md")).length;
|
|
5137
5463
|
}
|
|
5138
5464
|
} catch {
|
|
@@ -5153,62 +5479,62 @@ async function cloudSync(config) {
|
|
|
5153
5479
|
function recordRosterDeletion(name) {
|
|
5154
5480
|
let deletions = [];
|
|
5155
5481
|
try {
|
|
5156
|
-
if (
|
|
5157
|
-
deletions = JSON.parse(
|
|
5482
|
+
if (existsSync16(ROSTER_DELETIONS_PATH)) {
|
|
5483
|
+
deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5158
5484
|
}
|
|
5159
5485
|
} catch {
|
|
5160
5486
|
}
|
|
5161
5487
|
if (!deletions.includes(name)) deletions.push(name);
|
|
5162
|
-
|
|
5488
|
+
writeFileSync9(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
5163
5489
|
}
|
|
5164
5490
|
function consumeRosterDeletions() {
|
|
5165
5491
|
try {
|
|
5166
|
-
if (!
|
|
5167
|
-
const deletions = JSON.parse(
|
|
5168
|
-
|
|
5492
|
+
if (!existsSync16(ROSTER_DELETIONS_PATH)) return [];
|
|
5493
|
+
const deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5494
|
+
writeFileSync9(ROSTER_DELETIONS_PATH, "[]");
|
|
5169
5495
|
return deletions;
|
|
5170
5496
|
} catch {
|
|
5171
5497
|
return [];
|
|
5172
5498
|
}
|
|
5173
5499
|
}
|
|
5174
5500
|
function buildRosterBlob(paths) {
|
|
5175
|
-
const rosterPath = paths?.rosterPath ??
|
|
5176
|
-
const identityDir = paths?.identityDir ??
|
|
5177
|
-
const configPath = paths?.configPath ??
|
|
5501
|
+
const rosterPath = paths?.rosterPath ?? path17.join(EXE_AI_DIR, "exe-employees.json");
|
|
5502
|
+
const identityDir = paths?.identityDir ?? path17.join(EXE_AI_DIR, "identity");
|
|
5503
|
+
const configPath = paths?.configPath ?? path17.join(EXE_AI_DIR, "config.json");
|
|
5178
5504
|
let roster = [];
|
|
5179
|
-
if (
|
|
5505
|
+
if (existsSync16(rosterPath)) {
|
|
5180
5506
|
try {
|
|
5181
|
-
roster = JSON.parse(
|
|
5507
|
+
roster = JSON.parse(readFileSync12(rosterPath, "utf-8"));
|
|
5182
5508
|
} catch {
|
|
5183
5509
|
}
|
|
5184
5510
|
}
|
|
5185
5511
|
const identities = {};
|
|
5186
|
-
if (
|
|
5512
|
+
if (existsSync16(identityDir)) {
|
|
5187
5513
|
for (const file of readdirSync5(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
5188
5514
|
try {
|
|
5189
|
-
identities[file] =
|
|
5515
|
+
identities[file] = readFileSync12(path17.join(identityDir, file), "utf-8");
|
|
5190
5516
|
} catch {
|
|
5191
5517
|
}
|
|
5192
5518
|
}
|
|
5193
5519
|
}
|
|
5194
5520
|
let config;
|
|
5195
|
-
if (
|
|
5521
|
+
if (existsSync16(configPath)) {
|
|
5196
5522
|
try {
|
|
5197
|
-
config = JSON.parse(
|
|
5523
|
+
config = JSON.parse(readFileSync12(configPath, "utf-8"));
|
|
5198
5524
|
} catch {
|
|
5199
5525
|
}
|
|
5200
5526
|
}
|
|
5201
5527
|
let agentConfig;
|
|
5202
|
-
const agentConfigPath =
|
|
5203
|
-
if (
|
|
5528
|
+
const agentConfigPath = path17.join(EXE_AI_DIR, "agent-config.json");
|
|
5529
|
+
if (existsSync16(agentConfigPath)) {
|
|
5204
5530
|
try {
|
|
5205
|
-
agentConfig = JSON.parse(
|
|
5531
|
+
agentConfig = JSON.parse(readFileSync12(agentConfigPath, "utf-8"));
|
|
5206
5532
|
} catch {
|
|
5207
5533
|
}
|
|
5208
5534
|
}
|
|
5209
5535
|
const deletedNames = consumeRosterDeletions();
|
|
5210
5536
|
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
5211
|
-
const hash =
|
|
5537
|
+
const hash = crypto4.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5212
5538
|
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
5213
5539
|
}
|
|
5214
5540
|
async function cloudPushRoster(config) {
|
|
@@ -5278,23 +5604,24 @@ async function cloudPullRoster(config) {
|
|
|
5278
5604
|
}
|
|
5279
5605
|
}
|
|
5280
5606
|
function mergeConfig(remoteConfig, configPath) {
|
|
5281
|
-
const cfgPath = configPath ??
|
|
5607
|
+
const cfgPath = configPath ?? path17.join(EXE_AI_DIR, "config.json");
|
|
5282
5608
|
let local = {};
|
|
5283
|
-
if (
|
|
5609
|
+
if (existsSync16(cfgPath)) {
|
|
5284
5610
|
try {
|
|
5285
|
-
local = JSON.parse(
|
|
5611
|
+
local = JSON.parse(readFileSync12(cfgPath, "utf-8"));
|
|
5286
5612
|
} catch {
|
|
5287
5613
|
}
|
|
5288
5614
|
}
|
|
5289
5615
|
const merged = { ...remoteConfig, ...local };
|
|
5290
|
-
const dir =
|
|
5291
|
-
|
|
5292
|
-
|
|
5616
|
+
const dir = path17.dirname(cfgPath);
|
|
5617
|
+
ensurePrivateDirSync(dir);
|
|
5618
|
+
writeFileSync9(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
5619
|
+
enforcePrivateFileSync(cfgPath);
|
|
5293
5620
|
}
|
|
5294
5621
|
async function mergeRosterFromRemote(remote, paths) {
|
|
5295
5622
|
return withRosterLock(async () => {
|
|
5296
5623
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
5297
|
-
const identityDir = paths?.identityDir ??
|
|
5624
|
+
const identityDir = paths?.identityDir ?? path17.join(EXE_AI_DIR, "identity");
|
|
5298
5625
|
const localEmployees = await loadEmployees(rosterPath);
|
|
5299
5626
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
5300
5627
|
let added = 0;
|
|
@@ -5315,15 +5642,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
5315
5642
|
) ?? lookupKey;
|
|
5316
5643
|
const remoteIdentity = remote.identities[matchedKey];
|
|
5317
5644
|
if (remoteIdentity) {
|
|
5318
|
-
if (!
|
|
5319
|
-
const idPath =
|
|
5645
|
+
if (!existsSync16(identityDir)) mkdirSync8(identityDir, { recursive: true });
|
|
5646
|
+
const idPath = path17.join(identityDir, `${remoteEmp.name}.md`);
|
|
5320
5647
|
let localIdentity = null;
|
|
5321
5648
|
try {
|
|
5322
|
-
localIdentity =
|
|
5649
|
+
localIdentity = existsSync16(idPath) ? readFileSync12(idPath, "utf-8") : null;
|
|
5323
5650
|
} catch {
|
|
5324
5651
|
}
|
|
5325
5652
|
if (localIdentity !== remoteIdentity) {
|
|
5326
|
-
|
|
5653
|
+
writeFileSync9(idPath, remoteIdentity, "utf-8");
|
|
5327
5654
|
identitiesUpdated++;
|
|
5328
5655
|
}
|
|
5329
5656
|
}
|
|
@@ -5349,16 +5676,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
5349
5676
|
}
|
|
5350
5677
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
5351
5678
|
try {
|
|
5352
|
-
const agentConfigPath =
|
|
5679
|
+
const agentConfigPath = path17.join(EXE_AI_DIR, "agent-config.json");
|
|
5353
5680
|
let local = {};
|
|
5354
|
-
if (
|
|
5681
|
+
if (existsSync16(agentConfigPath)) {
|
|
5355
5682
|
try {
|
|
5356
|
-
local = JSON.parse(
|
|
5683
|
+
local = JSON.parse(readFileSync12(agentConfigPath, "utf-8"));
|
|
5357
5684
|
} catch {
|
|
5358
5685
|
}
|
|
5359
5686
|
}
|
|
5360
5687
|
const merged = { ...remote.agentConfig, ...local };
|
|
5361
|
-
|
|
5688
|
+
ensurePrivateDirSync(path17.dirname(agentConfigPath));
|
|
5689
|
+
writeFileSync9(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
5690
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
5362
5691
|
} catch {
|
|
5363
5692
|
}
|
|
5364
5693
|
}
|
|
@@ -5782,7 +6111,7 @@ async function cloudPullDocuments(config) {
|
|
|
5782
6111
|
}
|
|
5783
6112
|
return { pulled };
|
|
5784
6113
|
}
|
|
5785
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
6114
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
5786
6115
|
var init_cloud_sync = __esm({
|
|
5787
6116
|
"src/lib/cloud-sync.ts"() {
|
|
5788
6117
|
"use strict";
|
|
@@ -5793,12 +6122,15 @@ var init_cloud_sync = __esm({
|
|
|
5793
6122
|
init_config();
|
|
5794
6123
|
init_crdt_sync();
|
|
5795
6124
|
init_employees();
|
|
6125
|
+
init_secure_files();
|
|
5796
6126
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
5797
6127
|
FETCH_TIMEOUT_MS = 3e4;
|
|
5798
6128
|
PUSH_BATCH_SIZE = 5e3;
|
|
5799
|
-
ROSTER_LOCK_PATH =
|
|
6129
|
+
ROSTER_LOCK_PATH = path17.join(EXE_AI_DIR, "roster-merge.lock");
|
|
5800
6130
|
LOCK_STALE_MS = 3e4;
|
|
5801
|
-
|
|
6131
|
+
_pgPromise = null;
|
|
6132
|
+
_pgFailed = false;
|
|
6133
|
+
ROSTER_DELETIONS_PATH = path17.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5802
6134
|
}
|
|
5803
6135
|
});
|
|
5804
6136
|
|
|
@@ -6165,10 +6497,10 @@ init_database();
|
|
|
6165
6497
|
init_notifications();
|
|
6166
6498
|
init_task_scope();
|
|
6167
6499
|
init_employees();
|
|
6168
|
-
import
|
|
6500
|
+
import crypto5 from "crypto";
|
|
6169
6501
|
import { execSync as execSync4 } from "child_process";
|
|
6170
|
-
import { existsSync as
|
|
6171
|
-
import
|
|
6502
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, openSync as openSync3, closeSync as closeSync3 } from "fs";
|
|
6503
|
+
import path18 from "path";
|
|
6172
6504
|
async function main() {
|
|
6173
6505
|
const agentId = process.env.AGENT_ID ?? "default";
|
|
6174
6506
|
const agentRole = process.env.AGENT_ROLE ?? "employee";
|
|
@@ -6248,7 +6580,7 @@ async function main() {
|
|
|
6248
6580
|
if (limitReached) {
|
|
6249
6581
|
} else {
|
|
6250
6582
|
await writeMemory({
|
|
6251
|
-
id:
|
|
6583
|
+
id: crypto5.randomUUID(),
|
|
6252
6584
|
agent_id: agentId,
|
|
6253
6585
|
agent_role: agentRole,
|
|
6254
6586
|
session_id: `auto-summary-${Date.now()}`,
|
|
@@ -6303,8 +6635,8 @@ async function main() {
|
|
|
6303
6635
|
}
|
|
6304
6636
|
try {
|
|
6305
6637
|
const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6306
|
-
const flagPath =
|
|
6307
|
-
if (
|
|
6638
|
+
const flagPath = path18.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
|
|
6639
|
+
if (existsSync17(flagPath)) {
|
|
6308
6640
|
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
6309
6641
|
if (!tryAcquireWorkerSlot2()) {
|
|
6310
6642
|
process.stderr.write("[summary-worker] Backfill needed but worker gate full \u2014 skipping\n");
|
|
@@ -6312,11 +6644,11 @@ async function main() {
|
|
|
6312
6644
|
const { spawn: spawn2 } = await import("child_process");
|
|
6313
6645
|
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
6314
6646
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
6315
|
-
const backfillPath =
|
|
6316
|
-
if (
|
|
6647
|
+
const backfillPath = path18.resolve(path18.dirname(thisFile), "backfill-vectors.js");
|
|
6648
|
+
if (existsSync17(backfillPath)) {
|
|
6317
6649
|
const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6318
|
-
const bLogPath =
|
|
6319
|
-
mkdirSync9(
|
|
6650
|
+
const bLogPath = path18.join(exeDir2, "workers.log");
|
|
6651
|
+
mkdirSync9(path18.dirname(bLogPath), { recursive: true });
|
|
6320
6652
|
const bLogFd = openSync3(bLogPath, "a");
|
|
6321
6653
|
const child = spawn2(process.execPath, [backfillPath], {
|
|
6322
6654
|
detached: true,
|