@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-link.js
CHANGED
|
@@ -132,6 +132,44 @@ var init_keychain = __esm({
|
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
// src/lib/secure-files.ts
|
|
136
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
137
|
+
import { chmod as chmod2, mkdir as mkdir2 } from "fs/promises";
|
|
138
|
+
async function ensurePrivateDir(dirPath) {
|
|
139
|
+
await mkdir2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
140
|
+
try {
|
|
141
|
+
await chmod2(dirPath, PRIVATE_DIR_MODE);
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function ensurePrivateDirSync(dirPath) {
|
|
146
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
147
|
+
try {
|
|
148
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function enforcePrivateFile(filePath) {
|
|
153
|
+
try {
|
|
154
|
+
await chmod2(filePath, PRIVATE_FILE_MODE);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function enforcePrivateFileSync(filePath) {
|
|
159
|
+
try {
|
|
160
|
+
if (existsSync2(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
165
|
+
var init_secure_files = __esm({
|
|
166
|
+
"src/lib/secure-files.ts"() {
|
|
167
|
+
"use strict";
|
|
168
|
+
PRIVATE_DIR_MODE = 448;
|
|
169
|
+
PRIVATE_FILE_MODE = 384;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
135
173
|
// src/lib/config.ts
|
|
136
174
|
var config_exports = {};
|
|
137
175
|
__export(config_exports, {
|
|
@@ -148,8 +186,8 @@ __export(config_exports, {
|
|
|
148
186
|
migrateConfig: () => migrateConfig,
|
|
149
187
|
saveConfig: () => saveConfig
|
|
150
188
|
});
|
|
151
|
-
import { readFile as readFile2, writeFile as writeFile2
|
|
152
|
-
import { readFileSync, existsSync as
|
|
189
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
190
|
+
import { readFileSync, existsSync as existsSync3, renameSync } from "fs";
|
|
153
191
|
import path2 from "path";
|
|
154
192
|
import os2 from "os";
|
|
155
193
|
function resolveDataDir() {
|
|
@@ -157,7 +195,7 @@ function resolveDataDir() {
|
|
|
157
195
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
158
196
|
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
159
197
|
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
160
|
-
if (!
|
|
198
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
161
199
|
try {
|
|
162
200
|
renameSync(legacyDir, newDir);
|
|
163
201
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -220,9 +258,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
220
258
|
}
|
|
221
259
|
async function loadConfig() {
|
|
222
260
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
223
|
-
await
|
|
261
|
+
await ensurePrivateDir(dir);
|
|
224
262
|
const configPath = path2.join(dir, "config.json");
|
|
225
|
-
if (!
|
|
263
|
+
if (!existsSync3(configPath)) {
|
|
226
264
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
227
265
|
}
|
|
228
266
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -235,6 +273,7 @@ async function loadConfig() {
|
|
|
235
273
|
`);
|
|
236
274
|
try {
|
|
237
275
|
await writeFile2(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
276
|
+
await enforcePrivateFile(configPath);
|
|
238
277
|
} catch {
|
|
239
278
|
}
|
|
240
279
|
}
|
|
@@ -253,7 +292,7 @@ async function loadConfig() {
|
|
|
253
292
|
function loadConfigSync() {
|
|
254
293
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
255
294
|
const configPath = path2.join(dir, "config.json");
|
|
256
|
-
if (!
|
|
295
|
+
if (!existsSync3(configPath)) {
|
|
257
296
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
258
297
|
}
|
|
259
298
|
try {
|
|
@@ -271,12 +310,10 @@ function loadConfigSync() {
|
|
|
271
310
|
}
|
|
272
311
|
async function saveConfig(config) {
|
|
273
312
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
274
|
-
await
|
|
313
|
+
await ensurePrivateDir(dir);
|
|
275
314
|
const configPath = path2.join(dir, "config.json");
|
|
276
315
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
277
|
-
|
|
278
|
-
await chmod2(configPath, 384);
|
|
279
|
-
}
|
|
316
|
+
await enforcePrivateFile(configPath);
|
|
280
317
|
}
|
|
281
318
|
async function loadConfigFrom(configPath) {
|
|
282
319
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -296,6 +333,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
296
333
|
var init_config = __esm({
|
|
297
334
|
"src/lib/config.ts"() {
|
|
298
335
|
"use strict";
|
|
336
|
+
init_secure_files();
|
|
299
337
|
EXE_AI_DIR = resolveDataDir();
|
|
300
338
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
301
339
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -488,7 +526,7 @@ var init_db_retry = __esm({
|
|
|
488
526
|
|
|
489
527
|
// src/lib/employees.ts
|
|
490
528
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
491
|
-
import { existsSync as
|
|
529
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
492
530
|
import { execSync } from "child_process";
|
|
493
531
|
import path3 from "path";
|
|
494
532
|
import os3 from "os";
|
|
@@ -505,7 +543,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
505
543
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
506
544
|
}
|
|
507
545
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
508
|
-
if (!
|
|
546
|
+
if (!existsSync4(employeesPath)) {
|
|
509
547
|
return [];
|
|
510
548
|
}
|
|
511
549
|
const raw = await readFile3(employeesPath, "utf-8");
|
|
@@ -520,7 +558,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
520
558
|
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
521
559
|
}
|
|
522
560
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
523
|
-
if (!
|
|
561
|
+
if (!existsSync4(employeesPath)) return [];
|
|
524
562
|
try {
|
|
525
563
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
526
564
|
} catch {
|
|
@@ -554,7 +592,7 @@ function registerBinSymlinks(name) {
|
|
|
554
592
|
for (const suffix of ["", "-opencode"]) {
|
|
555
593
|
const linkName = `${name}${suffix}`;
|
|
556
594
|
const linkPath = path3.join(binDir, linkName);
|
|
557
|
-
if (
|
|
595
|
+
if (existsSync4(linkPath)) {
|
|
558
596
|
skipped.push(linkName);
|
|
559
597
|
continue;
|
|
560
598
|
}
|
|
@@ -1163,13 +1201,50 @@ var init_database_adapter = __esm({
|
|
|
1163
1201
|
}
|
|
1164
1202
|
});
|
|
1165
1203
|
|
|
1204
|
+
// src/lib/daemon-auth.ts
|
|
1205
|
+
import crypto2 from "crypto";
|
|
1206
|
+
import path5 from "path";
|
|
1207
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1208
|
+
function normalizeToken(token) {
|
|
1209
|
+
if (!token) return null;
|
|
1210
|
+
const trimmed = token.trim();
|
|
1211
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1212
|
+
}
|
|
1213
|
+
function readDaemonToken() {
|
|
1214
|
+
try {
|
|
1215
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1216
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
1217
|
+
} catch {
|
|
1218
|
+
return null;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
function ensureDaemonToken(seed) {
|
|
1222
|
+
const existing = readDaemonToken();
|
|
1223
|
+
if (existing) return existing;
|
|
1224
|
+
const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
|
|
1225
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1226
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
1227
|
+
`, "utf8");
|
|
1228
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1229
|
+
return token;
|
|
1230
|
+
}
|
|
1231
|
+
var DAEMON_TOKEN_PATH;
|
|
1232
|
+
var init_daemon_auth = __esm({
|
|
1233
|
+
"src/lib/daemon-auth.ts"() {
|
|
1234
|
+
"use strict";
|
|
1235
|
+
init_config();
|
|
1236
|
+
init_secure_files();
|
|
1237
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1166
1241
|
// src/lib/exe-daemon-client.ts
|
|
1167
1242
|
import net from "net";
|
|
1168
1243
|
import os5 from "os";
|
|
1169
1244
|
import { spawn } from "child_process";
|
|
1170
1245
|
import { randomUUID } from "crypto";
|
|
1171
|
-
import { existsSync as
|
|
1172
|
-
import
|
|
1246
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
1247
|
+
import path6 from "path";
|
|
1173
1248
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1174
1249
|
function handleData(chunk) {
|
|
1175
1250
|
_buffer += chunk.toString();
|
|
@@ -1197,9 +1272,9 @@ function handleData(chunk) {
|
|
|
1197
1272
|
}
|
|
1198
1273
|
}
|
|
1199
1274
|
function cleanupStaleFiles() {
|
|
1200
|
-
if (
|
|
1275
|
+
if (existsSync6(PID_PATH)) {
|
|
1201
1276
|
try {
|
|
1202
|
-
const pid = parseInt(
|
|
1277
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1203
1278
|
if (pid > 0) {
|
|
1204
1279
|
try {
|
|
1205
1280
|
process.kill(pid, 0);
|
|
@@ -1220,11 +1295,11 @@ function cleanupStaleFiles() {
|
|
|
1220
1295
|
}
|
|
1221
1296
|
}
|
|
1222
1297
|
function findPackageRoot() {
|
|
1223
|
-
let dir =
|
|
1224
|
-
const { root } =
|
|
1298
|
+
let dir = path6.dirname(fileURLToPath2(import.meta.url));
|
|
1299
|
+
const { root } = path6.parse(dir);
|
|
1225
1300
|
while (dir !== root) {
|
|
1226
|
-
if (
|
|
1227
|
-
dir =
|
|
1301
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1302
|
+
dir = path6.dirname(dir);
|
|
1228
1303
|
}
|
|
1229
1304
|
return null;
|
|
1230
1305
|
}
|
|
@@ -1250,16 +1325,17 @@ function spawnDaemon() {
|
|
|
1250
1325
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1251
1326
|
return;
|
|
1252
1327
|
}
|
|
1253
|
-
const daemonPath =
|
|
1254
|
-
if (!
|
|
1328
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1329
|
+
if (!existsSync6(daemonPath)) {
|
|
1255
1330
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1256
1331
|
`);
|
|
1257
1332
|
return;
|
|
1258
1333
|
}
|
|
1259
1334
|
const resolvedPath = daemonPath;
|
|
1335
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1260
1336
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1261
1337
|
`);
|
|
1262
|
-
const logPath =
|
|
1338
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1263
1339
|
let stderrFd = "ignore";
|
|
1264
1340
|
try {
|
|
1265
1341
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1277,7 +1353,8 @@ function spawnDaemon() {
|
|
|
1277
1353
|
TMUX_PANE: void 0,
|
|
1278
1354
|
// Prevents resolveExeSession() from scoping to one session
|
|
1279
1355
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1280
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1356
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1357
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1281
1358
|
}
|
|
1282
1359
|
});
|
|
1283
1360
|
child.unref();
|
|
@@ -1384,13 +1461,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1384
1461
|
return;
|
|
1385
1462
|
}
|
|
1386
1463
|
const id = randomUUID();
|
|
1464
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1387
1465
|
const timer = setTimeout(() => {
|
|
1388
1466
|
_pending.delete(id);
|
|
1389
1467
|
resolve({ error: "Request timeout" });
|
|
1390
1468
|
}, timeoutMs);
|
|
1391
1469
|
_pending.set(id, { resolve, timer });
|
|
1392
1470
|
try {
|
|
1393
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1471
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1394
1472
|
} catch {
|
|
1395
1473
|
clearTimeout(timer);
|
|
1396
1474
|
_pending.delete(id);
|
|
@@ -1401,17 +1479,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1401
1479
|
function isClientConnected() {
|
|
1402
1480
|
return _connected;
|
|
1403
1481
|
}
|
|
1404
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1482
|
+
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;
|
|
1405
1483
|
var init_exe_daemon_client = __esm({
|
|
1406
1484
|
"src/lib/exe-daemon-client.ts"() {
|
|
1407
1485
|
"use strict";
|
|
1408
1486
|
init_config();
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1487
|
+
init_daemon_auth();
|
|
1488
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1489
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1490
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1412
1491
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1413
1492
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1414
1493
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1494
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1415
1495
|
_socket = null;
|
|
1416
1496
|
_connected = false;
|
|
1417
1497
|
_buffer = "";
|
|
@@ -1990,6 +2070,7 @@ async function ensureSchema() {
|
|
|
1990
2070
|
project TEXT NOT NULL,
|
|
1991
2071
|
summary TEXT NOT NULL,
|
|
1992
2072
|
task_file TEXT,
|
|
2073
|
+
session_scope TEXT,
|
|
1993
2074
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1994
2075
|
created_at TEXT NOT NULL
|
|
1995
2076
|
);
|
|
@@ -1998,7 +2079,7 @@ async function ensureSchema() {
|
|
|
1998
2079
|
ON notifications(read);
|
|
1999
2080
|
|
|
2000
2081
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2001
|
-
ON notifications(agent_id);
|
|
2082
|
+
ON notifications(agent_id, session_scope);
|
|
2002
2083
|
|
|
2003
2084
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2004
2085
|
ON notifications(task_file);
|
|
@@ -2036,6 +2117,7 @@ async function ensureSchema() {
|
|
|
2036
2117
|
target_agent TEXT NOT NULL,
|
|
2037
2118
|
target_project TEXT,
|
|
2038
2119
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2120
|
+
session_scope TEXT,
|
|
2039
2121
|
content TEXT NOT NULL,
|
|
2040
2122
|
priority TEXT DEFAULT 'normal',
|
|
2041
2123
|
status TEXT DEFAULT 'pending',
|
|
@@ -2049,10 +2131,31 @@ async function ensureSchema() {
|
|
|
2049
2131
|
);
|
|
2050
2132
|
|
|
2051
2133
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2052
|
-
ON messages(target_agent, status);
|
|
2134
|
+
ON messages(target_agent, session_scope, status);
|
|
2053
2135
|
|
|
2054
2136
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2055
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2137
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2138
|
+
`);
|
|
2139
|
+
try {
|
|
2140
|
+
await client.execute({
|
|
2141
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2142
|
+
args: []
|
|
2143
|
+
});
|
|
2144
|
+
} catch {
|
|
2145
|
+
}
|
|
2146
|
+
try {
|
|
2147
|
+
await client.execute({
|
|
2148
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2149
|
+
args: []
|
|
2150
|
+
});
|
|
2151
|
+
} catch {
|
|
2152
|
+
}
|
|
2153
|
+
await client.executeMultiple(`
|
|
2154
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2155
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2156
|
+
|
|
2157
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2158
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2056
2159
|
`);
|
|
2057
2160
|
try {
|
|
2058
2161
|
await client.execute({
|
|
@@ -2636,6 +2739,13 @@ async function ensureSchema() {
|
|
|
2636
2739
|
} catch {
|
|
2637
2740
|
}
|
|
2638
2741
|
}
|
|
2742
|
+
try {
|
|
2743
|
+
await client.execute({
|
|
2744
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2745
|
+
args: []
|
|
2746
|
+
});
|
|
2747
|
+
} catch {
|
|
2748
|
+
}
|
|
2639
2749
|
}
|
|
2640
2750
|
async function disposeDatabase() {
|
|
2641
2751
|
if (_walCheckpointTimer) {
|
|
@@ -2694,29 +2804,32 @@ var init_compress = __esm({
|
|
|
2694
2804
|
});
|
|
2695
2805
|
|
|
2696
2806
|
// src/lib/license.ts
|
|
2697
|
-
import { readFileSync as
|
|
2807
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
2698
2808
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2699
|
-
import
|
|
2809
|
+
import { createRequire as createRequire2 } from "module";
|
|
2810
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2811
|
+
import os6 from "os";
|
|
2812
|
+
import path7 from "path";
|
|
2700
2813
|
import { jwtVerify, importSPKI } from "jose";
|
|
2701
2814
|
function loadDeviceId() {
|
|
2702
|
-
const deviceJsonPath =
|
|
2815
|
+
const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
|
|
2703
2816
|
try {
|
|
2704
|
-
if (
|
|
2705
|
-
const data = JSON.parse(
|
|
2817
|
+
if (existsSync7(deviceJsonPath)) {
|
|
2818
|
+
const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
|
|
2706
2819
|
if (data.deviceId) return data.deviceId;
|
|
2707
2820
|
}
|
|
2708
2821
|
} catch {
|
|
2709
2822
|
}
|
|
2710
2823
|
try {
|
|
2711
|
-
if (
|
|
2712
|
-
const id2 =
|
|
2824
|
+
if (existsSync7(DEVICE_ID_PATH)) {
|
|
2825
|
+
const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
|
|
2713
2826
|
if (id2) return id2;
|
|
2714
2827
|
}
|
|
2715
2828
|
} catch {
|
|
2716
2829
|
}
|
|
2717
2830
|
const id = randomUUID2();
|
|
2718
|
-
|
|
2719
|
-
|
|
2831
|
+
mkdirSync2(EXE_AI_DIR, { recursive: true });
|
|
2832
|
+
writeFileSync3(DEVICE_ID_PATH, id, "utf8");
|
|
2720
2833
|
return id;
|
|
2721
2834
|
}
|
|
2722
2835
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
@@ -2724,9 +2837,9 @@ var init_license = __esm({
|
|
|
2724
2837
|
"src/lib/license.ts"() {
|
|
2725
2838
|
"use strict";
|
|
2726
2839
|
init_config();
|
|
2727
|
-
LICENSE_PATH =
|
|
2728
|
-
CACHE_PATH =
|
|
2729
|
-
DEVICE_ID_PATH =
|
|
2840
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2841
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2842
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2730
2843
|
}
|
|
2731
2844
|
});
|
|
2732
2845
|
|
|
@@ -2749,8 +2862,8 @@ __export(crdt_sync_exports, {
|
|
|
2749
2862
|
rebuildFromDb: () => rebuildFromDb
|
|
2750
2863
|
});
|
|
2751
2864
|
import * as Y from "yjs";
|
|
2752
|
-
import { readFileSync as
|
|
2753
|
-
import
|
|
2865
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2866
|
+
import path8 from "path";
|
|
2754
2867
|
import { homedir } from "os";
|
|
2755
2868
|
function getStatePath() {
|
|
2756
2869
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -2762,9 +2875,9 @@ function initCrdtDoc() {
|
|
|
2762
2875
|
if (doc) return doc;
|
|
2763
2876
|
doc = new Y.Doc();
|
|
2764
2877
|
const sp = getStatePath();
|
|
2765
|
-
if (
|
|
2878
|
+
if (existsSync8(sp)) {
|
|
2766
2879
|
try {
|
|
2767
|
-
const state =
|
|
2880
|
+
const state = readFileSync6(sp);
|
|
2768
2881
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
2769
2882
|
} catch {
|
|
2770
2883
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -2906,10 +3019,10 @@ function persistState() {
|
|
|
2906
3019
|
if (!doc) return;
|
|
2907
3020
|
try {
|
|
2908
3021
|
const sp = getStatePath();
|
|
2909
|
-
const dir =
|
|
2910
|
-
if (!
|
|
3022
|
+
const dir = path8.dirname(sp);
|
|
3023
|
+
if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
|
|
2911
3024
|
const state = Y.encodeStateAsUpdate(doc);
|
|
2912
|
-
|
|
3025
|
+
writeFileSync4(sp, Buffer.from(state));
|
|
2913
3026
|
} catch {
|
|
2914
3027
|
}
|
|
2915
3028
|
}
|
|
@@ -2950,7 +3063,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
2950
3063
|
var init_crdt_sync = __esm({
|
|
2951
3064
|
"src/lib/crdt-sync.ts"() {
|
|
2952
3065
|
"use strict";
|
|
2953
|
-
DEFAULT_STATE_PATH =
|
|
3066
|
+
DEFAULT_STATE_PATH = path8.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
2954
3067
|
_statePathOverride = null;
|
|
2955
3068
|
doc = null;
|
|
2956
3069
|
}
|
|
@@ -2982,39 +3095,107 @@ __export(cloud_sync_exports, {
|
|
|
2982
3095
|
cloudSync: () => cloudSync,
|
|
2983
3096
|
mergeConfig: () => mergeConfig,
|
|
2984
3097
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
3098
|
+
pushToPostgres: () => pushToPostgres,
|
|
2985
3099
|
recordRosterDeletion: () => recordRosterDeletion
|
|
2986
3100
|
});
|
|
2987
|
-
import { readFileSync as
|
|
2988
|
-
import
|
|
2989
|
-
import
|
|
3101
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
3102
|
+
import crypto3 from "crypto";
|
|
3103
|
+
import path9 from "path";
|
|
2990
3104
|
import { homedir as homedir2 } from "os";
|
|
2991
3105
|
function sqlSafe(v) {
|
|
2992
3106
|
return v === void 0 ? null : v;
|
|
2993
3107
|
}
|
|
2994
3108
|
function logError(msg) {
|
|
2995
3109
|
try {
|
|
2996
|
-
const logPath =
|
|
3110
|
+
const logPath = path9.join(homedir2(), ".exe-os", "workers.log");
|
|
2997
3111
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
2998
3112
|
`);
|
|
2999
3113
|
} catch {
|
|
3000
3114
|
}
|
|
3001
3115
|
}
|
|
3116
|
+
function loadPgClient() {
|
|
3117
|
+
if (_pgFailed) return null;
|
|
3118
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
3119
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
3120
|
+
let cloudPostgresUrl;
|
|
3121
|
+
try {
|
|
3122
|
+
if (existsSync9(configPath)) {
|
|
3123
|
+
const cfg = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3124
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
3125
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
3126
|
+
_pgFailed = true;
|
|
3127
|
+
return null;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
} catch {
|
|
3131
|
+
}
|
|
3132
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
3133
|
+
if (!url) {
|
|
3134
|
+
_pgFailed = true;
|
|
3135
|
+
return null;
|
|
3136
|
+
}
|
|
3137
|
+
if (!_pgPromise) {
|
|
3138
|
+
_pgPromise = (async () => {
|
|
3139
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
3140
|
+
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
3141
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(homedir2(), "exe-db");
|
|
3142
|
+
const req = createRequire3(path9.join(exeDbRoot, "package.json"));
|
|
3143
|
+
const entry = req.resolve("@prisma/client");
|
|
3144
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
3145
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3146
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
3147
|
+
return new Ctor();
|
|
3148
|
+
})().catch(() => {
|
|
3149
|
+
_pgFailed = true;
|
|
3150
|
+
_pgPromise = null;
|
|
3151
|
+
throw new Error("pg_unavailable");
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
return _pgPromise;
|
|
3155
|
+
}
|
|
3156
|
+
async function pushToPostgres(records) {
|
|
3157
|
+
const loader = loadPgClient();
|
|
3158
|
+
if (!loader) return 0;
|
|
3159
|
+
let prisma;
|
|
3160
|
+
try {
|
|
3161
|
+
prisma = await loader;
|
|
3162
|
+
} catch {
|
|
3163
|
+
return 0;
|
|
3164
|
+
}
|
|
3165
|
+
let inserted = 0;
|
|
3166
|
+
for (const rec of records) {
|
|
3167
|
+
try {
|
|
3168
|
+
await prisma.$executeRawUnsafe(
|
|
3169
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
3170
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
3171
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
3172
|
+
String(rec.id ?? ""),
|
|
3173
|
+
JSON.stringify(rec),
|
|
3174
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
3175
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
3176
|
+
);
|
|
3177
|
+
inserted++;
|
|
3178
|
+
} catch {
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return inserted;
|
|
3182
|
+
}
|
|
3002
3183
|
async function withRosterLock(fn) {
|
|
3003
3184
|
try {
|
|
3004
3185
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3005
3186
|
closeSync2(fd);
|
|
3006
|
-
|
|
3187
|
+
writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3007
3188
|
} catch (err) {
|
|
3008
3189
|
if (err.code === "EEXIST") {
|
|
3009
3190
|
try {
|
|
3010
|
-
const ts = parseInt(
|
|
3191
|
+
const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
3011
3192
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
3012
3193
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
3013
3194
|
}
|
|
3014
3195
|
unlinkSync4(ROSTER_LOCK_PATH);
|
|
3015
3196
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3016
3197
|
closeSync2(fd);
|
|
3017
|
-
|
|
3198
|
+
writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3018
3199
|
} catch (retryErr) {
|
|
3019
3200
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
3020
3201
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -3284,6 +3465,10 @@ async function cloudSync(config) {
|
|
|
3284
3465
|
const maxVersion = Number(records[records.length - 1].version);
|
|
3285
3466
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
3286
3467
|
if (!pushOk) break;
|
|
3468
|
+
try {
|
|
3469
|
+
await pushToPostgres(records);
|
|
3470
|
+
} catch {
|
|
3471
|
+
}
|
|
3287
3472
|
await client.execute({
|
|
3288
3473
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
3289
3474
|
args: [String(maxVersion)]
|
|
@@ -3388,8 +3573,8 @@ async function cloudSync(config) {
|
|
|
3388
3573
|
try {
|
|
3389
3574
|
const employees = await loadEmployees();
|
|
3390
3575
|
rosterResult.employees = employees.length;
|
|
3391
|
-
const idDir =
|
|
3392
|
-
if (
|
|
3576
|
+
const idDir = path9.join(EXE_AI_DIR, "identity");
|
|
3577
|
+
if (existsSync9(idDir)) {
|
|
3393
3578
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
3394
3579
|
}
|
|
3395
3580
|
} catch {
|
|
@@ -3410,62 +3595,62 @@ async function cloudSync(config) {
|
|
|
3410
3595
|
function recordRosterDeletion(name) {
|
|
3411
3596
|
let deletions = [];
|
|
3412
3597
|
try {
|
|
3413
|
-
if (
|
|
3414
|
-
deletions = JSON.parse(
|
|
3598
|
+
if (existsSync9(ROSTER_DELETIONS_PATH)) {
|
|
3599
|
+
deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
3415
3600
|
}
|
|
3416
3601
|
} catch {
|
|
3417
3602
|
}
|
|
3418
3603
|
if (!deletions.includes(name)) deletions.push(name);
|
|
3419
|
-
|
|
3604
|
+
writeFileSync5(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
3420
3605
|
}
|
|
3421
3606
|
function consumeRosterDeletions() {
|
|
3422
3607
|
try {
|
|
3423
|
-
if (!
|
|
3424
|
-
const deletions = JSON.parse(
|
|
3425
|
-
|
|
3608
|
+
if (!existsSync9(ROSTER_DELETIONS_PATH)) return [];
|
|
3609
|
+
const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
3610
|
+
writeFileSync5(ROSTER_DELETIONS_PATH, "[]");
|
|
3426
3611
|
return deletions;
|
|
3427
3612
|
} catch {
|
|
3428
3613
|
return [];
|
|
3429
3614
|
}
|
|
3430
3615
|
}
|
|
3431
3616
|
function buildRosterBlob(paths) {
|
|
3432
|
-
const rosterPath = paths?.rosterPath ??
|
|
3433
|
-
const identityDir = paths?.identityDir ??
|
|
3434
|
-
const configPath = paths?.configPath ??
|
|
3617
|
+
const rosterPath = paths?.rosterPath ?? path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
3618
|
+
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
3619
|
+
const configPath = paths?.configPath ?? path9.join(EXE_AI_DIR, "config.json");
|
|
3435
3620
|
let roster = [];
|
|
3436
|
-
if (
|
|
3621
|
+
if (existsSync9(rosterPath)) {
|
|
3437
3622
|
try {
|
|
3438
|
-
roster = JSON.parse(
|
|
3623
|
+
roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
|
|
3439
3624
|
} catch {
|
|
3440
3625
|
}
|
|
3441
3626
|
}
|
|
3442
3627
|
const identities = {};
|
|
3443
|
-
if (
|
|
3628
|
+
if (existsSync9(identityDir)) {
|
|
3444
3629
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
3445
3630
|
try {
|
|
3446
|
-
identities[file] =
|
|
3631
|
+
identities[file] = readFileSync7(path9.join(identityDir, file), "utf-8");
|
|
3447
3632
|
} catch {
|
|
3448
3633
|
}
|
|
3449
3634
|
}
|
|
3450
3635
|
}
|
|
3451
3636
|
let config;
|
|
3452
|
-
if (
|
|
3637
|
+
if (existsSync9(configPath)) {
|
|
3453
3638
|
try {
|
|
3454
|
-
config = JSON.parse(
|
|
3639
|
+
config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
3455
3640
|
} catch {
|
|
3456
3641
|
}
|
|
3457
3642
|
}
|
|
3458
3643
|
let agentConfig;
|
|
3459
|
-
const agentConfigPath =
|
|
3460
|
-
if (
|
|
3644
|
+
const agentConfigPath = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
3645
|
+
if (existsSync9(agentConfigPath)) {
|
|
3461
3646
|
try {
|
|
3462
|
-
agentConfig = JSON.parse(
|
|
3647
|
+
agentConfig = JSON.parse(readFileSync7(agentConfigPath, "utf-8"));
|
|
3463
3648
|
} catch {
|
|
3464
3649
|
}
|
|
3465
3650
|
}
|
|
3466
3651
|
const deletedNames = consumeRosterDeletions();
|
|
3467
3652
|
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
3468
|
-
const hash =
|
|
3653
|
+
const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
3469
3654
|
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
3470
3655
|
}
|
|
3471
3656
|
async function cloudPushRoster(config) {
|
|
@@ -3535,23 +3720,24 @@ async function cloudPullRoster(config) {
|
|
|
3535
3720
|
}
|
|
3536
3721
|
}
|
|
3537
3722
|
function mergeConfig(remoteConfig, configPath) {
|
|
3538
|
-
const cfgPath = configPath ??
|
|
3723
|
+
const cfgPath = configPath ?? path9.join(EXE_AI_DIR, "config.json");
|
|
3539
3724
|
let local = {};
|
|
3540
|
-
if (
|
|
3725
|
+
if (existsSync9(cfgPath)) {
|
|
3541
3726
|
try {
|
|
3542
|
-
local = JSON.parse(
|
|
3727
|
+
local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
|
|
3543
3728
|
} catch {
|
|
3544
3729
|
}
|
|
3545
3730
|
}
|
|
3546
3731
|
const merged = { ...remoteConfig, ...local };
|
|
3547
|
-
const dir =
|
|
3548
|
-
|
|
3549
|
-
|
|
3732
|
+
const dir = path9.dirname(cfgPath);
|
|
3733
|
+
ensurePrivateDirSync(dir);
|
|
3734
|
+
writeFileSync5(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
3735
|
+
enforcePrivateFileSync(cfgPath);
|
|
3550
3736
|
}
|
|
3551
3737
|
async function mergeRosterFromRemote(remote, paths) {
|
|
3552
3738
|
return withRosterLock(async () => {
|
|
3553
3739
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
3554
|
-
const identityDir = paths?.identityDir ??
|
|
3740
|
+
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
3555
3741
|
const localEmployees = await loadEmployees(rosterPath);
|
|
3556
3742
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
3557
3743
|
let added = 0;
|
|
@@ -3572,15 +3758,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
3572
3758
|
) ?? lookupKey;
|
|
3573
3759
|
const remoteIdentity = remote.identities[matchedKey];
|
|
3574
3760
|
if (remoteIdentity) {
|
|
3575
|
-
if (!
|
|
3576
|
-
const idPath =
|
|
3761
|
+
if (!existsSync9(identityDir)) mkdirSync4(identityDir, { recursive: true });
|
|
3762
|
+
const idPath = path9.join(identityDir, `${remoteEmp.name}.md`);
|
|
3577
3763
|
let localIdentity = null;
|
|
3578
3764
|
try {
|
|
3579
|
-
localIdentity =
|
|
3765
|
+
localIdentity = existsSync9(idPath) ? readFileSync7(idPath, "utf-8") : null;
|
|
3580
3766
|
} catch {
|
|
3581
3767
|
}
|
|
3582
3768
|
if (localIdentity !== remoteIdentity) {
|
|
3583
|
-
|
|
3769
|
+
writeFileSync5(idPath, remoteIdentity, "utf-8");
|
|
3584
3770
|
identitiesUpdated++;
|
|
3585
3771
|
}
|
|
3586
3772
|
}
|
|
@@ -3606,16 +3792,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
3606
3792
|
}
|
|
3607
3793
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
3608
3794
|
try {
|
|
3609
|
-
const agentConfigPath =
|
|
3795
|
+
const agentConfigPath = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
3610
3796
|
let local = {};
|
|
3611
|
-
if (
|
|
3797
|
+
if (existsSync9(agentConfigPath)) {
|
|
3612
3798
|
try {
|
|
3613
|
-
local = JSON.parse(
|
|
3799
|
+
local = JSON.parse(readFileSync7(agentConfigPath, "utf-8"));
|
|
3614
3800
|
} catch {
|
|
3615
3801
|
}
|
|
3616
3802
|
}
|
|
3617
3803
|
const merged = { ...remote.agentConfig, ...local };
|
|
3618
|
-
|
|
3804
|
+
ensurePrivateDirSync(path9.dirname(agentConfigPath));
|
|
3805
|
+
writeFileSync5(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
3806
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
3619
3807
|
} catch {
|
|
3620
3808
|
}
|
|
3621
3809
|
}
|
|
@@ -4039,7 +4227,7 @@ async function cloudPullDocuments(config) {
|
|
|
4039
4227
|
}
|
|
4040
4228
|
return { pulled };
|
|
4041
4229
|
}
|
|
4042
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
4230
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
4043
4231
|
var init_cloud_sync = __esm({
|
|
4044
4232
|
"src/lib/cloud-sync.ts"() {
|
|
4045
4233
|
"use strict";
|
|
@@ -4050,12 +4238,15 @@ var init_cloud_sync = __esm({
|
|
|
4050
4238
|
init_config();
|
|
4051
4239
|
init_crdt_sync();
|
|
4052
4240
|
init_employees();
|
|
4241
|
+
init_secure_files();
|
|
4053
4242
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
4054
4243
|
FETCH_TIMEOUT_MS = 3e4;
|
|
4055
4244
|
PUSH_BATCH_SIZE = 5e3;
|
|
4056
|
-
ROSTER_LOCK_PATH =
|
|
4245
|
+
ROSTER_LOCK_PATH = path9.join(EXE_AI_DIR, "roster-merge.lock");
|
|
4057
4246
|
LOCK_STALE_MS = 3e4;
|
|
4058
|
-
|
|
4247
|
+
_pgPromise = null;
|
|
4248
|
+
_pgFailed = false;
|
|
4249
|
+
ROSTER_DELETIONS_PATH = path9.join(EXE_AI_DIR, "roster-deletions.json");
|
|
4059
4250
|
}
|
|
4060
4251
|
});
|
|
4061
4252
|
|