@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/setup.js
CHANGED
|
@@ -15,6 +15,44 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/lib/secure-files.ts
|
|
19
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
20
|
+
import { chmod, mkdir } from "fs/promises";
|
|
21
|
+
async function ensurePrivateDir(dirPath) {
|
|
22
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
23
|
+
try {
|
|
24
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function ensurePrivateDirSync(dirPath) {
|
|
29
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
30
|
+
try {
|
|
31
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function enforcePrivateFile(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function enforcePrivateFileSync(filePath) {
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
48
|
+
var init_secure_files = __esm({
|
|
49
|
+
"src/lib/secure-files.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
PRIVATE_DIR_MODE = 448;
|
|
52
|
+
PRIVATE_FILE_MODE = 384;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
18
56
|
// src/lib/config.ts
|
|
19
57
|
var config_exports = {};
|
|
20
58
|
__export(config_exports, {
|
|
@@ -31,8 +69,8 @@ __export(config_exports, {
|
|
|
31
69
|
migrateConfig: () => migrateConfig,
|
|
32
70
|
saveConfig: () => saveConfig
|
|
33
71
|
});
|
|
34
|
-
import { readFile, writeFile
|
|
35
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
72
|
+
import { readFile, writeFile } from "fs/promises";
|
|
73
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
36
74
|
import path from "path";
|
|
37
75
|
import os from "os";
|
|
38
76
|
function resolveDataDir() {
|
|
@@ -40,7 +78,7 @@ function resolveDataDir() {
|
|
|
40
78
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
41
79
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
42
80
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
43
|
-
if (!
|
|
81
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
44
82
|
try {
|
|
45
83
|
renameSync(legacyDir, newDir);
|
|
46
84
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -103,9 +141,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
103
141
|
}
|
|
104
142
|
async function loadConfig() {
|
|
105
143
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
106
|
-
await
|
|
144
|
+
await ensurePrivateDir(dir);
|
|
107
145
|
const configPath = path.join(dir, "config.json");
|
|
108
|
-
if (!
|
|
146
|
+
if (!existsSync2(configPath)) {
|
|
109
147
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
110
148
|
}
|
|
111
149
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -118,6 +156,7 @@ async function loadConfig() {
|
|
|
118
156
|
`);
|
|
119
157
|
try {
|
|
120
158
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
159
|
+
await enforcePrivateFile(configPath);
|
|
121
160
|
} catch {
|
|
122
161
|
}
|
|
123
162
|
}
|
|
@@ -136,7 +175,7 @@ async function loadConfig() {
|
|
|
136
175
|
function loadConfigSync() {
|
|
137
176
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
138
177
|
const configPath = path.join(dir, "config.json");
|
|
139
|
-
if (!
|
|
178
|
+
if (!existsSync2(configPath)) {
|
|
140
179
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
141
180
|
}
|
|
142
181
|
try {
|
|
@@ -154,12 +193,10 @@ function loadConfigSync() {
|
|
|
154
193
|
}
|
|
155
194
|
async function saveConfig(config) {
|
|
156
195
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
157
|
-
await
|
|
196
|
+
await ensurePrivateDir(dir);
|
|
158
197
|
const configPath = path.join(dir, "config.json");
|
|
159
198
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
160
|
-
|
|
161
|
-
await chmod(configPath, 384);
|
|
162
|
-
}
|
|
199
|
+
await enforcePrivateFile(configPath);
|
|
163
200
|
}
|
|
164
201
|
async function loadConfigFrom(configPath) {
|
|
165
202
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -179,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
179
216
|
var init_config = __esm({
|
|
180
217
|
"src/lib/config.ts"() {
|
|
181
218
|
"use strict";
|
|
219
|
+
init_secure_files();
|
|
182
220
|
EXE_AI_DIR = resolveDataDir();
|
|
183
221
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
184
222
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -265,7 +303,7 @@ __export(keychain_exports, {
|
|
|
265
303
|
setMasterKey: () => setMasterKey
|
|
266
304
|
});
|
|
267
305
|
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
268
|
-
import { existsSync as
|
|
306
|
+
import { existsSync as existsSync3 } from "fs";
|
|
269
307
|
import path2 from "path";
|
|
270
308
|
import os2 from "os";
|
|
271
309
|
function getKeyDir() {
|
|
@@ -293,7 +331,7 @@ async function getMasterKey() {
|
|
|
293
331
|
}
|
|
294
332
|
}
|
|
295
333
|
const keyPath = getKeyPath();
|
|
296
|
-
if (!
|
|
334
|
+
if (!existsSync3(keyPath)) {
|
|
297
335
|
process.stderr.write(
|
|
298
336
|
`[keychain] Key not found at ${keyPath} (HOME=${os2.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
299
337
|
`
|
|
@@ -336,7 +374,7 @@ async function deleteMasterKey() {
|
|
|
336
374
|
}
|
|
337
375
|
}
|
|
338
376
|
const keyPath = getKeyPath();
|
|
339
|
-
if (
|
|
377
|
+
if (existsSync3(keyPath)) {
|
|
340
378
|
await unlink(keyPath);
|
|
341
379
|
}
|
|
342
380
|
}
|
|
@@ -387,13 +425,50 @@ var init_memory = __esm({
|
|
|
387
425
|
}
|
|
388
426
|
});
|
|
389
427
|
|
|
428
|
+
// src/lib/daemon-auth.ts
|
|
429
|
+
import crypto from "crypto";
|
|
430
|
+
import path4 from "path";
|
|
431
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
432
|
+
function normalizeToken(token) {
|
|
433
|
+
if (!token) return null;
|
|
434
|
+
const trimmed = token.trim();
|
|
435
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
436
|
+
}
|
|
437
|
+
function readDaemonToken() {
|
|
438
|
+
try {
|
|
439
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
440
|
+
return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
|
|
441
|
+
} catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function ensureDaemonToken(seed) {
|
|
446
|
+
const existing = readDaemonToken();
|
|
447
|
+
if (existing) return existing;
|
|
448
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
449
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
450
|
+
writeFileSync(DAEMON_TOKEN_PATH, `${token}
|
|
451
|
+
`, "utf8");
|
|
452
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
453
|
+
return token;
|
|
454
|
+
}
|
|
455
|
+
var DAEMON_TOKEN_PATH;
|
|
456
|
+
var init_daemon_auth = __esm({
|
|
457
|
+
"src/lib/daemon-auth.ts"() {
|
|
458
|
+
"use strict";
|
|
459
|
+
init_config();
|
|
460
|
+
init_secure_files();
|
|
461
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
390
465
|
// src/lib/exe-daemon-client.ts
|
|
391
466
|
import net from "net";
|
|
392
467
|
import os3 from "os";
|
|
393
468
|
import { spawn } from "child_process";
|
|
394
469
|
import { randomUUID } from "crypto";
|
|
395
|
-
import { existsSync as
|
|
396
|
-
import
|
|
470
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
471
|
+
import path5 from "path";
|
|
397
472
|
import { fileURLToPath } from "url";
|
|
398
473
|
function handleData(chunk) {
|
|
399
474
|
_buffer += chunk.toString();
|
|
@@ -421,9 +496,9 @@ function handleData(chunk) {
|
|
|
421
496
|
}
|
|
422
497
|
}
|
|
423
498
|
function cleanupStaleFiles() {
|
|
424
|
-
if (
|
|
499
|
+
if (existsSync6(PID_PATH)) {
|
|
425
500
|
try {
|
|
426
|
-
const pid = parseInt(
|
|
501
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
427
502
|
if (pid > 0) {
|
|
428
503
|
try {
|
|
429
504
|
process.kill(pid, 0);
|
|
@@ -444,11 +519,11 @@ function cleanupStaleFiles() {
|
|
|
444
519
|
}
|
|
445
520
|
}
|
|
446
521
|
function findPackageRoot() {
|
|
447
|
-
let dir =
|
|
448
|
-
const { root } =
|
|
522
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
523
|
+
const { root } = path5.parse(dir);
|
|
449
524
|
while (dir !== root) {
|
|
450
|
-
if (
|
|
451
|
-
dir =
|
|
525
|
+
if (existsSync6(path5.join(dir, "package.json"))) return dir;
|
|
526
|
+
dir = path5.dirname(dir);
|
|
452
527
|
}
|
|
453
528
|
return null;
|
|
454
529
|
}
|
|
@@ -474,16 +549,17 @@ function spawnDaemon() {
|
|
|
474
549
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
475
550
|
return;
|
|
476
551
|
}
|
|
477
|
-
const daemonPath =
|
|
478
|
-
if (!
|
|
552
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
553
|
+
if (!existsSync6(daemonPath)) {
|
|
479
554
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
480
555
|
`);
|
|
481
556
|
return;
|
|
482
557
|
}
|
|
483
558
|
const resolvedPath = daemonPath;
|
|
559
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
484
560
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
485
561
|
`);
|
|
486
|
-
const logPath =
|
|
562
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
487
563
|
let stderrFd = "ignore";
|
|
488
564
|
try {
|
|
489
565
|
stderrFd = openSync(logPath, "a");
|
|
@@ -501,7 +577,8 @@ function spawnDaemon() {
|
|
|
501
577
|
TMUX_PANE: void 0,
|
|
502
578
|
// Prevents resolveExeSession() from scoping to one session
|
|
503
579
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
504
|
-
EXE_DAEMON_PID: PID_PATH
|
|
580
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
581
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
505
582
|
}
|
|
506
583
|
});
|
|
507
584
|
child.unref();
|
|
@@ -611,13 +688,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
611
688
|
return;
|
|
612
689
|
}
|
|
613
690
|
const id = randomUUID();
|
|
691
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
614
692
|
const timer = setTimeout(() => {
|
|
615
693
|
_pending.delete(id);
|
|
616
694
|
resolve({ error: "Request timeout" });
|
|
617
695
|
}, timeoutMs);
|
|
618
696
|
_pending.set(id, { resolve, timer });
|
|
619
697
|
try {
|
|
620
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
698
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
621
699
|
} catch {
|
|
622
700
|
clearTimeout(timer);
|
|
623
701
|
_pending.delete(id);
|
|
@@ -646,9 +724,9 @@ function killAndRespawnDaemon() {
|
|
|
646
724
|
}
|
|
647
725
|
try {
|
|
648
726
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
649
|
-
if (
|
|
727
|
+
if (existsSync6(PID_PATH)) {
|
|
650
728
|
try {
|
|
651
|
-
const pid = parseInt(
|
|
729
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
652
730
|
if (pid > 0) {
|
|
653
731
|
try {
|
|
654
732
|
process.kill(pid, "SIGKILL");
|
|
@@ -768,17 +846,19 @@ function disconnectClient() {
|
|
|
768
846
|
function isClientConnected() {
|
|
769
847
|
return _connected;
|
|
770
848
|
}
|
|
771
|
-
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;
|
|
849
|
+
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;
|
|
772
850
|
var init_exe_daemon_client = __esm({
|
|
773
851
|
"src/lib/exe-daemon-client.ts"() {
|
|
774
852
|
"use strict";
|
|
775
853
|
init_config();
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
854
|
+
init_daemon_auth();
|
|
855
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
856
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
857
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
779
858
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
780
859
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
781
860
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
861
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
782
862
|
_socket = null;
|
|
783
863
|
_connected = false;
|
|
784
864
|
_buffer = "";
|
|
@@ -830,10 +910,10 @@ async function disposeEmbedder() {
|
|
|
830
910
|
async function embedDirect(text) {
|
|
831
911
|
const llamaCpp = await import("node-llama-cpp");
|
|
832
912
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
833
|
-
const { existsSync:
|
|
834
|
-
const
|
|
835
|
-
const modelPath =
|
|
836
|
-
if (!
|
|
913
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
914
|
+
const path16 = await import("path");
|
|
915
|
+
const modelPath = path16.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
916
|
+
if (!existsSync16(modelPath)) {
|
|
837
917
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
838
918
|
}
|
|
839
919
|
const llama = await llamaCpp.getLlama();
|
|
@@ -878,9 +958,12 @@ __export(license_exports, {
|
|
|
878
958
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
879
959
|
validateLicense: () => validateLicense
|
|
880
960
|
});
|
|
881
|
-
import { readFileSync as
|
|
961
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
882
962
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
883
|
-
import
|
|
963
|
+
import { createRequire } from "module";
|
|
964
|
+
import { pathToFileURL } from "url";
|
|
965
|
+
import os4 from "os";
|
|
966
|
+
import path6 from "path";
|
|
884
967
|
import { jwtVerify, importSPKI } from "jose";
|
|
885
968
|
async function fetchRetry(url, init) {
|
|
886
969
|
try {
|
|
@@ -891,37 +974,37 @@ async function fetchRetry(url, init) {
|
|
|
891
974
|
}
|
|
892
975
|
}
|
|
893
976
|
function loadDeviceId() {
|
|
894
|
-
const deviceJsonPath =
|
|
977
|
+
const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
|
|
895
978
|
try {
|
|
896
|
-
if (
|
|
897
|
-
const data = JSON.parse(
|
|
979
|
+
if (existsSync7(deviceJsonPath)) {
|
|
980
|
+
const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
|
|
898
981
|
if (data.deviceId) return data.deviceId;
|
|
899
982
|
}
|
|
900
983
|
} catch {
|
|
901
984
|
}
|
|
902
985
|
try {
|
|
903
|
-
if (
|
|
904
|
-
const id2 =
|
|
986
|
+
if (existsSync7(DEVICE_ID_PATH)) {
|
|
987
|
+
const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
|
|
905
988
|
if (id2) return id2;
|
|
906
989
|
}
|
|
907
990
|
} catch {
|
|
908
991
|
}
|
|
909
992
|
const id = randomUUID2();
|
|
910
|
-
|
|
911
|
-
|
|
993
|
+
mkdirSync2(EXE_AI_DIR, { recursive: true });
|
|
994
|
+
writeFileSync2(DEVICE_ID_PATH, id, "utf8");
|
|
912
995
|
return id;
|
|
913
996
|
}
|
|
914
997
|
function loadLicense() {
|
|
915
998
|
try {
|
|
916
|
-
if (!
|
|
917
|
-
return
|
|
999
|
+
if (!existsSync7(LICENSE_PATH)) return null;
|
|
1000
|
+
return readFileSync4(LICENSE_PATH, "utf8").trim();
|
|
918
1001
|
} catch {
|
|
919
1002
|
return null;
|
|
920
1003
|
}
|
|
921
1004
|
}
|
|
922
1005
|
function saveLicense(apiKey) {
|
|
923
|
-
|
|
924
|
-
|
|
1006
|
+
mkdirSync2(EXE_AI_DIR, { recursive: true });
|
|
1007
|
+
writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
925
1008
|
}
|
|
926
1009
|
async function verifyLicenseJwt(token) {
|
|
927
1010
|
try {
|
|
@@ -947,8 +1030,8 @@ async function verifyLicenseJwt(token) {
|
|
|
947
1030
|
}
|
|
948
1031
|
async function getCachedLicense() {
|
|
949
1032
|
try {
|
|
950
|
-
if (!
|
|
951
|
-
const raw = JSON.parse(
|
|
1033
|
+
if (!existsSync7(CACHE_PATH)) return null;
|
|
1034
|
+
const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
|
|
952
1035
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
953
1036
|
return await verifyLicenseJwt(raw.token);
|
|
954
1037
|
} catch {
|
|
@@ -957,8 +1040,8 @@ async function getCachedLicense() {
|
|
|
957
1040
|
}
|
|
958
1041
|
function readCachedToken() {
|
|
959
1042
|
try {
|
|
960
|
-
if (!
|
|
961
|
-
const raw = JSON.parse(
|
|
1043
|
+
if (!existsSync7(CACHE_PATH)) return null;
|
|
1044
|
+
const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
|
|
962
1045
|
return typeof raw.token === "string" ? raw.token : null;
|
|
963
1046
|
} catch {
|
|
964
1047
|
return null;
|
|
@@ -992,56 +1075,130 @@ function getRawCachedPlan() {
|
|
|
992
1075
|
}
|
|
993
1076
|
function cacheResponse(token) {
|
|
994
1077
|
try {
|
|
995
|
-
|
|
1078
|
+
writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
996
1079
|
} catch {
|
|
997
1080
|
}
|
|
998
1081
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1082
|
+
function loadPrismaForLicense() {
|
|
1083
|
+
if (_prismaFailed) return null;
|
|
1084
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
1085
|
+
if (!dbUrl) {
|
|
1086
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
|
|
1087
|
+
if (!existsSync7(path6.join(exeDbRoot, "package.json"))) {
|
|
1088
|
+
_prismaFailed = true;
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (!_prismaPromise) {
|
|
1093
|
+
_prismaPromise = (async () => {
|
|
1094
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1095
|
+
if (explicitPath) {
|
|
1096
|
+
const mod2 = await import(pathToFileURL(explicitPath).href);
|
|
1097
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
1098
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
1099
|
+
return new Ctor2();
|
|
1100
|
+
}
|
|
1101
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
|
|
1102
|
+
const req = createRequire(path6.join(exeDbRoot, "package.json"));
|
|
1103
|
+
const entry = req.resolve("@prisma/client");
|
|
1104
|
+
const mod = await import(pathToFileURL(entry).href);
|
|
1105
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
1106
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
1107
|
+
return new Ctor();
|
|
1108
|
+
})().catch((err) => {
|
|
1109
|
+
_prismaFailed = true;
|
|
1110
|
+
_prismaPromise = null;
|
|
1111
|
+
throw err;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
return _prismaPromise;
|
|
1115
|
+
}
|
|
1116
|
+
async function validateViaPostgres(apiKey) {
|
|
1117
|
+
const loader = loadPrismaForLicense();
|
|
1118
|
+
if (!loader) return null;
|
|
1119
|
+
try {
|
|
1120
|
+
const prisma = await loader;
|
|
1121
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1122
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
1123
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
1124
|
+
apiKey
|
|
1125
|
+
);
|
|
1126
|
+
if (!rows || rows.length === 0) return null;
|
|
1127
|
+
const row = rows[0];
|
|
1128
|
+
if (row.status !== "active") return null;
|
|
1129
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
1130
|
+
const plan = row.plan;
|
|
1131
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
1132
|
+
return {
|
|
1133
|
+
valid: true,
|
|
1134
|
+
plan,
|
|
1135
|
+
email: row.email,
|
|
1136
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
1137
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
1138
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
1139
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
1140
|
+
};
|
|
1141
|
+
} catch {
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
1001
1146
|
try {
|
|
1002
1147
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
1003
1148
|
method: "POST",
|
|
1004
1149
|
headers: { "Content-Type": "application/json" },
|
|
1005
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
1150
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
1006
1151
|
signal: AbortSignal.timeout(1e4)
|
|
1007
1152
|
});
|
|
1008
|
-
if (res.ok)
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1153
|
+
if (!res.ok) return null;
|
|
1154
|
+
const data = await res.json();
|
|
1155
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
1156
|
+
if (!data.valid) return null;
|
|
1157
|
+
if (data.token) {
|
|
1158
|
+
cacheResponse(data.token);
|
|
1159
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
1160
|
+
if (verified) return verified;
|
|
1161
|
+
}
|
|
1162
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
1163
|
+
return {
|
|
1164
|
+
valid: data.valid,
|
|
1165
|
+
plan: data.plan,
|
|
1166
|
+
email: data.email,
|
|
1167
|
+
expiresAt: data.expiresAt,
|
|
1168
|
+
deviceLimit: limits.devices,
|
|
1169
|
+
employeeLimit: limits.employees,
|
|
1170
|
+
memoryLimit: limits.memories
|
|
1171
|
+
};
|
|
1172
|
+
} catch {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
async function validateLicense(apiKey, deviceId) {
|
|
1177
|
+
const did = deviceId ?? loadDeviceId();
|
|
1178
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
1179
|
+
if (pgResult) {
|
|
1180
|
+
try {
|
|
1181
|
+
writeFileSync2(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
return pgResult;
|
|
1185
|
+
}
|
|
1186
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
1187
|
+
if (cfResult) return cfResult;
|
|
1188
|
+
const cached = await getCachedLicense();
|
|
1189
|
+
if (cached) return cached;
|
|
1190
|
+
try {
|
|
1191
|
+
if (existsSync7(CACHE_PATH)) {
|
|
1192
|
+
const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
|
|
1193
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
1194
|
+
return raw.pgLicense;
|
|
1021
1195
|
}
|
|
1022
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
1023
|
-
return {
|
|
1024
|
-
valid: data.valid,
|
|
1025
|
-
plan: data.plan,
|
|
1026
|
-
email: data.email,
|
|
1027
|
-
expiresAt: data.expiresAt,
|
|
1028
|
-
deviceLimit: limits.devices,
|
|
1029
|
-
employeeLimit: limits.employees,
|
|
1030
|
-
memoryLimit: limits.memories
|
|
1031
|
-
};
|
|
1032
1196
|
}
|
|
1033
|
-
const cached = await getCachedLicense();
|
|
1034
|
-
if (cached) return cached;
|
|
1035
|
-
const raw = getRawCachedPlan();
|
|
1036
|
-
if (raw) return raw;
|
|
1037
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
1038
1197
|
} catch {
|
|
1039
|
-
const cached = await getCachedLicense();
|
|
1040
|
-
if (cached) return cached;
|
|
1041
|
-
const rawFallback = getRawCachedPlan();
|
|
1042
|
-
if (rawFallback) return rawFallback;
|
|
1043
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
1044
1198
|
}
|
|
1199
|
+
const rawFallback = getRawCachedPlan();
|
|
1200
|
+
if (rawFallback) return rawFallback;
|
|
1201
|
+
return { ...FREE_LICENSE, valid: false };
|
|
1045
1202
|
}
|
|
1046
1203
|
function getCacheAgeMs() {
|
|
1047
1204
|
try {
|
|
@@ -1056,9 +1213,9 @@ async function checkLicense() {
|
|
|
1056
1213
|
let key = loadLicense();
|
|
1057
1214
|
if (!key) {
|
|
1058
1215
|
try {
|
|
1059
|
-
const configPath =
|
|
1060
|
-
if (
|
|
1061
|
-
const raw = JSON.parse(
|
|
1216
|
+
const configPath = path6.join(EXE_AI_DIR, "config.json");
|
|
1217
|
+
if (existsSync7(configPath)) {
|
|
1218
|
+
const raw = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
1062
1219
|
const cloud = raw.cloud;
|
|
1063
1220
|
if (cloud?.apiKey) {
|
|
1064
1221
|
key = cloud.apiKey;
|
|
@@ -1212,14 +1369,14 @@ function stopLicenseRevalidation() {
|
|
|
1212
1369
|
_revalTimer = null;
|
|
1213
1370
|
}
|
|
1214
1371
|
}
|
|
1215
|
-
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;
|
|
1372
|
+
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;
|
|
1216
1373
|
var init_license = __esm({
|
|
1217
1374
|
"src/lib/license.ts"() {
|
|
1218
1375
|
"use strict";
|
|
1219
1376
|
init_config();
|
|
1220
|
-
LICENSE_PATH =
|
|
1221
|
-
CACHE_PATH =
|
|
1222
|
-
DEVICE_ID_PATH =
|
|
1377
|
+
LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
|
|
1378
|
+
CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
|
|
1379
|
+
DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
|
|
1223
1380
|
API_BASE = "https://askexe.com/cloud";
|
|
1224
1381
|
RETRY_DELAY_MS = 500;
|
|
1225
1382
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -1243,6 +1400,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
1243
1400
|
employeeLimit: 1,
|
|
1244
1401
|
memoryLimit: 5e3
|
|
1245
1402
|
};
|
|
1403
|
+
_prismaPromise = null;
|
|
1404
|
+
_prismaFailed = false;
|
|
1246
1405
|
CACHE_MAX_AGE_MS = 36e5;
|
|
1247
1406
|
_revalTimer = null;
|
|
1248
1407
|
}
|
|
@@ -1256,13 +1415,13 @@ __export(crypto_exports, {
|
|
|
1256
1415
|
initSyncCrypto: () => initSyncCrypto,
|
|
1257
1416
|
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
1258
1417
|
});
|
|
1259
|
-
import
|
|
1418
|
+
import crypto2 from "crypto";
|
|
1260
1419
|
function initSyncCrypto(masterKey) {
|
|
1261
1420
|
if (masterKey.length !== 32) {
|
|
1262
1421
|
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
1263
1422
|
}
|
|
1264
1423
|
_syncKey = Buffer.from(
|
|
1265
|
-
|
|
1424
|
+
crypto2.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
1266
1425
|
);
|
|
1267
1426
|
}
|
|
1268
1427
|
function isSyncCryptoInitialized() {
|
|
@@ -1276,8 +1435,8 @@ function requireSyncKey() {
|
|
|
1276
1435
|
}
|
|
1277
1436
|
function encryptSyncBlob(data) {
|
|
1278
1437
|
const key = requireSyncKey();
|
|
1279
|
-
const iv =
|
|
1280
|
-
const cipher =
|
|
1438
|
+
const iv = crypto2.randomBytes(IV_LENGTH);
|
|
1439
|
+
const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
|
|
1281
1440
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
1282
1441
|
const tag = cipher.getAuthTag();
|
|
1283
1442
|
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
@@ -1291,7 +1450,7 @@ function decryptSyncBlob(ciphertext) {
|
|
|
1291
1450
|
const iv = combined.subarray(0, IV_LENGTH);
|
|
1292
1451
|
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
1293
1452
|
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
1294
|
-
const decipher =
|
|
1453
|
+
const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
|
|
1295
1454
|
decipher.setAuthTag(tag);
|
|
1296
1455
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
1297
1456
|
}
|
|
@@ -1402,20 +1561,21 @@ __export(agent_config_exports, {
|
|
|
1402
1561
|
saveAgentConfig: () => saveAgentConfig,
|
|
1403
1562
|
setAgentRuntime: () => setAgentRuntime
|
|
1404
1563
|
});
|
|
1405
|
-
import { readFileSync as
|
|
1406
|
-
import
|
|
1564
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8 } from "fs";
|
|
1565
|
+
import path7 from "path";
|
|
1407
1566
|
function loadAgentConfig() {
|
|
1408
|
-
if (!
|
|
1567
|
+
if (!existsSync8(AGENT_CONFIG_PATH)) return {};
|
|
1409
1568
|
try {
|
|
1410
|
-
return JSON.parse(
|
|
1569
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
1411
1570
|
} catch {
|
|
1412
1571
|
return {};
|
|
1413
1572
|
}
|
|
1414
1573
|
}
|
|
1415
1574
|
function saveAgentConfig(config) {
|
|
1416
|
-
const dir =
|
|
1417
|
-
|
|
1418
|
-
|
|
1575
|
+
const dir = path7.dirname(AGENT_CONFIG_PATH);
|
|
1576
|
+
ensurePrivateDirSync(dir);
|
|
1577
|
+
writeFileSync3(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1578
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
1419
1579
|
}
|
|
1420
1580
|
function getAgentRuntime(agentId) {
|
|
1421
1581
|
const config = loadAgentConfig();
|
|
@@ -1455,7 +1615,8 @@ var init_agent_config = __esm({
|
|
|
1455
1615
|
"use strict";
|
|
1456
1616
|
init_config();
|
|
1457
1617
|
init_runtime_table();
|
|
1458
|
-
|
|
1618
|
+
init_secure_files();
|
|
1619
|
+
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
1459
1620
|
KNOWN_RUNTIMES = {
|
|
1460
1621
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
1461
1622
|
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
@@ -1502,10 +1663,10 @@ __export(employees_exports, {
|
|
|
1502
1663
|
validateEmployeeName: () => validateEmployeeName
|
|
1503
1664
|
});
|
|
1504
1665
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
|
|
1505
|
-
import { existsSync as
|
|
1666
|
+
import { existsSync as existsSync9, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
1506
1667
|
import { execSync } from "child_process";
|
|
1507
|
-
import
|
|
1508
|
-
import
|
|
1668
|
+
import path8 from "path";
|
|
1669
|
+
import os5 from "os";
|
|
1509
1670
|
function normalizeRole(role) {
|
|
1510
1671
|
return (role ?? "").trim().toLowerCase();
|
|
1511
1672
|
}
|
|
@@ -1541,7 +1702,7 @@ function validateEmployeeName(name) {
|
|
|
1541
1702
|
return { valid: true };
|
|
1542
1703
|
}
|
|
1543
1704
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
1544
|
-
if (!
|
|
1705
|
+
if (!existsSync9(employeesPath)) {
|
|
1545
1706
|
return [];
|
|
1546
1707
|
}
|
|
1547
1708
|
const raw = await readFile3(employeesPath, "utf-8");
|
|
@@ -1552,13 +1713,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
1552
1713
|
}
|
|
1553
1714
|
}
|
|
1554
1715
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
1555
|
-
await mkdir4(
|
|
1716
|
+
await mkdir4(path8.dirname(employeesPath), { recursive: true });
|
|
1556
1717
|
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1557
1718
|
}
|
|
1558
1719
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
1559
|
-
if (!
|
|
1720
|
+
if (!existsSync9(employeesPath)) return [];
|
|
1560
1721
|
try {
|
|
1561
|
-
return JSON.parse(
|
|
1722
|
+
return JSON.parse(readFileSync6(employeesPath, "utf-8"));
|
|
1562
1723
|
} catch {
|
|
1563
1724
|
return [];
|
|
1564
1725
|
}
|
|
@@ -1603,9 +1764,9 @@ function addEmployee(employees, employee) {
|
|
|
1603
1764
|
function appendToCoordinatorTeam(employee) {
|
|
1604
1765
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1605
1766
|
if (!coordinator) return;
|
|
1606
|
-
const idPath =
|
|
1607
|
-
if (!
|
|
1608
|
-
const content =
|
|
1767
|
+
const idPath = path8.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1768
|
+
if (!existsSync9(idPath)) return;
|
|
1769
|
+
const content = readFileSync6(idPath, "utf-8");
|
|
1609
1770
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1610
1771
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1611
1772
|
if (!teamMatch || teamMatch.index === void 0) return;
|
|
@@ -1621,7 +1782,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
1621
1782
|
} else {
|
|
1622
1783
|
updated = content.trimEnd() + "\n" + entry;
|
|
1623
1784
|
}
|
|
1624
|
-
|
|
1785
|
+
writeFileSync4(idPath, updated, "utf-8");
|
|
1625
1786
|
}
|
|
1626
1787
|
function capitalize(s) {
|
|
1627
1788
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -1655,14 +1816,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
1655
1816
|
emp.name = emp.name.toLowerCase();
|
|
1656
1817
|
changed = true;
|
|
1657
1818
|
try {
|
|
1658
|
-
const identityDir =
|
|
1659
|
-
const oldPath =
|
|
1660
|
-
const newPath =
|
|
1661
|
-
if (
|
|
1819
|
+
const identityDir = path8.join(os5.homedir(), ".exe-os", "identity");
|
|
1820
|
+
const oldPath = path8.join(identityDir, `${oldName}.md`);
|
|
1821
|
+
const newPath = path8.join(identityDir, `${emp.name}.md`);
|
|
1822
|
+
if (existsSync9(oldPath) && !existsSync9(newPath)) {
|
|
1662
1823
|
renameSync3(oldPath, newPath);
|
|
1663
|
-
} else if (
|
|
1664
|
-
const content =
|
|
1665
|
-
|
|
1824
|
+
} else if (existsSync9(oldPath) && oldPath !== newPath) {
|
|
1825
|
+
const content = readFileSync6(oldPath, "utf-8");
|
|
1826
|
+
writeFileSync4(newPath, content, "utf-8");
|
|
1666
1827
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
1667
1828
|
unlinkSync3(oldPath);
|
|
1668
1829
|
}
|
|
@@ -1692,7 +1853,7 @@ function registerBinSymlinks(name) {
|
|
|
1692
1853
|
errors.push("Could not find 'exe-os' in PATH");
|
|
1693
1854
|
return { created, skipped, errors };
|
|
1694
1855
|
}
|
|
1695
|
-
const binDir =
|
|
1856
|
+
const binDir = path8.dirname(exeBinPath);
|
|
1696
1857
|
let target;
|
|
1697
1858
|
try {
|
|
1698
1859
|
target = readlinkSync(exeBinPath);
|
|
@@ -1702,8 +1863,8 @@ function registerBinSymlinks(name) {
|
|
|
1702
1863
|
}
|
|
1703
1864
|
for (const suffix of ["", "-opencode"]) {
|
|
1704
1865
|
const linkName = `${name}${suffix}`;
|
|
1705
|
-
const linkPath =
|
|
1706
|
-
if (
|
|
1866
|
+
const linkPath = path8.join(binDir, linkName);
|
|
1867
|
+
if (existsSync9(linkPath)) {
|
|
1707
1868
|
skipped.push(linkName);
|
|
1708
1869
|
continue;
|
|
1709
1870
|
}
|
|
@@ -1721,20 +1882,20 @@ var init_employees = __esm({
|
|
|
1721
1882
|
"src/lib/employees.ts"() {
|
|
1722
1883
|
"use strict";
|
|
1723
1884
|
init_config();
|
|
1724
|
-
EMPLOYEES_PATH =
|
|
1885
|
+
EMPLOYEES_PATH = path8.join(EXE_AI_DIR, "exe-employees.json");
|
|
1725
1886
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1726
1887
|
COORDINATOR_ROLE = "COO";
|
|
1727
1888
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1728
|
-
IDENTITY_DIR =
|
|
1889
|
+
IDENTITY_DIR = path8.join(EXE_AI_DIR, "identity");
|
|
1729
1890
|
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1730
1891
|
}
|
|
1731
1892
|
});
|
|
1732
1893
|
|
|
1733
1894
|
// src/lib/database-adapter.ts
|
|
1734
|
-
import
|
|
1735
|
-
import
|
|
1736
|
-
import { createRequire } from "module";
|
|
1737
|
-
import { pathToFileURL } from "url";
|
|
1895
|
+
import os6 from "os";
|
|
1896
|
+
import path9 from "path";
|
|
1897
|
+
import { createRequire as createRequire2 } from "module";
|
|
1898
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1738
1899
|
function quotedIdentifier(identifier) {
|
|
1739
1900
|
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1740
1901
|
}
|
|
@@ -2036,17 +2197,17 @@ async function loadPrismaClient() {
|
|
|
2036
2197
|
prismaClientPromise = (async () => {
|
|
2037
2198
|
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
2038
2199
|
if (explicitPath) {
|
|
2039
|
-
const module2 = await import(
|
|
2200
|
+
const module2 = await import(pathToFileURL2(explicitPath).href);
|
|
2040
2201
|
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
2041
2202
|
if (!PrismaClient2) {
|
|
2042
2203
|
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
2043
2204
|
}
|
|
2044
2205
|
return new PrismaClient2();
|
|
2045
2206
|
}
|
|
2046
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
2047
|
-
const requireFromExeDb =
|
|
2207
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os6.homedir(), "exe-db");
|
|
2208
|
+
const requireFromExeDb = createRequire2(path9.join(exeDbRoot, "package.json"));
|
|
2048
2209
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
2049
|
-
const module = await import(
|
|
2210
|
+
const module = await import(pathToFileURL2(prismaEntry).href);
|
|
2050
2211
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
2051
2212
|
if (!PrismaClient) {
|
|
2052
2213
|
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
@@ -2884,6 +3045,7 @@ async function ensureSchema() {
|
|
|
2884
3045
|
project TEXT NOT NULL,
|
|
2885
3046
|
summary TEXT NOT NULL,
|
|
2886
3047
|
task_file TEXT,
|
|
3048
|
+
session_scope TEXT,
|
|
2887
3049
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2888
3050
|
created_at TEXT NOT NULL
|
|
2889
3051
|
);
|
|
@@ -2892,7 +3054,7 @@ async function ensureSchema() {
|
|
|
2892
3054
|
ON notifications(read);
|
|
2893
3055
|
|
|
2894
3056
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2895
|
-
ON notifications(agent_id);
|
|
3057
|
+
ON notifications(agent_id, session_scope);
|
|
2896
3058
|
|
|
2897
3059
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2898
3060
|
ON notifications(task_file);
|
|
@@ -2930,6 +3092,7 @@ async function ensureSchema() {
|
|
|
2930
3092
|
target_agent TEXT NOT NULL,
|
|
2931
3093
|
target_project TEXT,
|
|
2932
3094
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
3095
|
+
session_scope TEXT,
|
|
2933
3096
|
content TEXT NOT NULL,
|
|
2934
3097
|
priority TEXT DEFAULT 'normal',
|
|
2935
3098
|
status TEXT DEFAULT 'pending',
|
|
@@ -2943,10 +3106,31 @@ async function ensureSchema() {
|
|
|
2943
3106
|
);
|
|
2944
3107
|
|
|
2945
3108
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2946
|
-
ON messages(target_agent, status);
|
|
3109
|
+
ON messages(target_agent, session_scope, status);
|
|
2947
3110
|
|
|
2948
3111
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2949
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
3112
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
3113
|
+
`);
|
|
3114
|
+
try {
|
|
3115
|
+
await client.execute({
|
|
3116
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
3117
|
+
args: []
|
|
3118
|
+
});
|
|
3119
|
+
} catch {
|
|
3120
|
+
}
|
|
3121
|
+
try {
|
|
3122
|
+
await client.execute({
|
|
3123
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
3124
|
+
args: []
|
|
3125
|
+
});
|
|
3126
|
+
} catch {
|
|
3127
|
+
}
|
|
3128
|
+
await client.executeMultiple(`
|
|
3129
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
3130
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
3131
|
+
|
|
3132
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
3133
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2950
3134
|
`);
|
|
2951
3135
|
try {
|
|
2952
3136
|
await client.execute({
|
|
@@ -3530,6 +3714,13 @@ async function ensureSchema() {
|
|
|
3530
3714
|
} catch {
|
|
3531
3715
|
}
|
|
3532
3716
|
}
|
|
3717
|
+
try {
|
|
3718
|
+
await client.execute({
|
|
3719
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
3720
|
+
args: []
|
|
3721
|
+
});
|
|
3722
|
+
} catch {
|
|
3723
|
+
}
|
|
3533
3724
|
}
|
|
3534
3725
|
async function disposeDatabase() {
|
|
3535
3726
|
if (_walCheckpointTimer) {
|
|
@@ -3606,8 +3797,8 @@ __export(crdt_sync_exports, {
|
|
|
3606
3797
|
rebuildFromDb: () => rebuildFromDb
|
|
3607
3798
|
});
|
|
3608
3799
|
import * as Y from "yjs";
|
|
3609
|
-
import { readFileSync as
|
|
3610
|
-
import
|
|
3800
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
3801
|
+
import path10 from "path";
|
|
3611
3802
|
import { homedir } from "os";
|
|
3612
3803
|
function getStatePath() {
|
|
3613
3804
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -3619,9 +3810,9 @@ function initCrdtDoc() {
|
|
|
3619
3810
|
if (doc) return doc;
|
|
3620
3811
|
doc = new Y.Doc();
|
|
3621
3812
|
const sp = getStatePath();
|
|
3622
|
-
if (
|
|
3813
|
+
if (existsSync10(sp)) {
|
|
3623
3814
|
try {
|
|
3624
|
-
const state =
|
|
3815
|
+
const state = readFileSync7(sp);
|
|
3625
3816
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
3626
3817
|
} catch {
|
|
3627
3818
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -3763,10 +3954,10 @@ function persistState() {
|
|
|
3763
3954
|
if (!doc) return;
|
|
3764
3955
|
try {
|
|
3765
3956
|
const sp = getStatePath();
|
|
3766
|
-
const dir =
|
|
3767
|
-
if (!
|
|
3957
|
+
const dir = path10.dirname(sp);
|
|
3958
|
+
if (!existsSync10(dir)) mkdirSync3(dir, { recursive: true });
|
|
3768
3959
|
const state = Y.encodeStateAsUpdate(doc);
|
|
3769
|
-
|
|
3960
|
+
writeFileSync5(sp, Buffer.from(state));
|
|
3770
3961
|
} catch {
|
|
3771
3962
|
}
|
|
3772
3963
|
}
|
|
@@ -3807,7 +3998,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
3807
3998
|
var init_crdt_sync = __esm({
|
|
3808
3999
|
"src/lib/crdt-sync.ts"() {
|
|
3809
4000
|
"use strict";
|
|
3810
|
-
DEFAULT_STATE_PATH =
|
|
4001
|
+
DEFAULT_STATE_PATH = path10.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
3811
4002
|
_statePathOverride = null;
|
|
3812
4003
|
doc = null;
|
|
3813
4004
|
}
|
|
@@ -3839,39 +4030,107 @@ __export(cloud_sync_exports, {
|
|
|
3839
4030
|
cloudSync: () => cloudSync,
|
|
3840
4031
|
mergeConfig: () => mergeConfig,
|
|
3841
4032
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4033
|
+
pushToPostgres: () => pushToPostgres,
|
|
3842
4034
|
recordRosterDeletion: () => recordRosterDeletion
|
|
3843
4035
|
});
|
|
3844
|
-
import { readFileSync as
|
|
3845
|
-
import
|
|
3846
|
-
import
|
|
4036
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
4037
|
+
import crypto3 from "crypto";
|
|
4038
|
+
import path11 from "path";
|
|
3847
4039
|
import { homedir as homedir2 } from "os";
|
|
3848
4040
|
function sqlSafe(v) {
|
|
3849
4041
|
return v === void 0 ? null : v;
|
|
3850
4042
|
}
|
|
3851
4043
|
function logError(msg) {
|
|
3852
4044
|
try {
|
|
3853
|
-
const logPath =
|
|
4045
|
+
const logPath = path11.join(homedir2(), ".exe-os", "workers.log");
|
|
3854
4046
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
3855
4047
|
`);
|
|
3856
4048
|
} catch {
|
|
3857
4049
|
}
|
|
3858
4050
|
}
|
|
4051
|
+
function loadPgClient() {
|
|
4052
|
+
if (_pgFailed) return null;
|
|
4053
|
+
const postgresUrl = process.env.DATABASE_URL;
|
|
4054
|
+
const configPath = path11.join(EXE_AI_DIR, "config.json");
|
|
4055
|
+
let cloudPostgresUrl;
|
|
4056
|
+
try {
|
|
4057
|
+
if (existsSync11(configPath)) {
|
|
4058
|
+
const cfg = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
4059
|
+
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
4060
|
+
if (cfg.cloud?.syncToPostgres === false) {
|
|
4061
|
+
_pgFailed = true;
|
|
4062
|
+
return null;
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
} catch {
|
|
4066
|
+
}
|
|
4067
|
+
const url = postgresUrl || cloudPostgresUrl;
|
|
4068
|
+
if (!url) {
|
|
4069
|
+
_pgFailed = true;
|
|
4070
|
+
return null;
|
|
4071
|
+
}
|
|
4072
|
+
if (!_pgPromise) {
|
|
4073
|
+
_pgPromise = (async () => {
|
|
4074
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
4075
|
+
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
4076
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(homedir2(), "exe-db");
|
|
4077
|
+
const req = createRequire3(path11.join(exeDbRoot, "package.json"));
|
|
4078
|
+
const entry = req.resolve("@prisma/client");
|
|
4079
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
4080
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4081
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
4082
|
+
return new Ctor();
|
|
4083
|
+
})().catch(() => {
|
|
4084
|
+
_pgFailed = true;
|
|
4085
|
+
_pgPromise = null;
|
|
4086
|
+
throw new Error("pg_unavailable");
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
return _pgPromise;
|
|
4090
|
+
}
|
|
4091
|
+
async function pushToPostgres(records) {
|
|
4092
|
+
const loader = loadPgClient();
|
|
4093
|
+
if (!loader) return 0;
|
|
4094
|
+
let prisma;
|
|
4095
|
+
try {
|
|
4096
|
+
prisma = await loader;
|
|
4097
|
+
} catch {
|
|
4098
|
+
return 0;
|
|
4099
|
+
}
|
|
4100
|
+
let inserted = 0;
|
|
4101
|
+
for (const rec of records) {
|
|
4102
|
+
try {
|
|
4103
|
+
await prisma.$executeRawUnsafe(
|
|
4104
|
+
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
4105
|
+
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
4106
|
+
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
4107
|
+
String(rec.id ?? ""),
|
|
4108
|
+
JSON.stringify(rec),
|
|
4109
|
+
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
4110
|
+
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
4111
|
+
);
|
|
4112
|
+
inserted++;
|
|
4113
|
+
} catch {
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
return inserted;
|
|
4117
|
+
}
|
|
3859
4118
|
async function withRosterLock(fn) {
|
|
3860
4119
|
try {
|
|
3861
4120
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3862
4121
|
closeSync2(fd);
|
|
3863
|
-
|
|
4122
|
+
writeFileSync6(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3864
4123
|
} catch (err) {
|
|
3865
4124
|
if (err.code === "EEXIST") {
|
|
3866
4125
|
try {
|
|
3867
|
-
const ts = parseInt(
|
|
4126
|
+
const ts = parseInt(readFileSync8(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
3868
4127
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
3869
4128
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
3870
4129
|
}
|
|
3871
4130
|
unlinkSync5(ROSTER_LOCK_PATH);
|
|
3872
4131
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3873
4132
|
closeSync2(fd);
|
|
3874
|
-
|
|
4133
|
+
writeFileSync6(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3875
4134
|
} catch (retryErr) {
|
|
3876
4135
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
3877
4136
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -4141,6 +4400,10 @@ async function cloudSync(config) {
|
|
|
4141
4400
|
const maxVersion = Number(records[records.length - 1].version);
|
|
4142
4401
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
4143
4402
|
if (!pushOk) break;
|
|
4403
|
+
try {
|
|
4404
|
+
await pushToPostgres(records);
|
|
4405
|
+
} catch {
|
|
4406
|
+
}
|
|
4144
4407
|
await client.execute({
|
|
4145
4408
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
4146
4409
|
args: [String(maxVersion)]
|
|
@@ -4245,8 +4508,8 @@ async function cloudSync(config) {
|
|
|
4245
4508
|
try {
|
|
4246
4509
|
const employees = await loadEmployees();
|
|
4247
4510
|
rosterResult.employees = employees.length;
|
|
4248
|
-
const idDir =
|
|
4249
|
-
if (
|
|
4511
|
+
const idDir = path11.join(EXE_AI_DIR, "identity");
|
|
4512
|
+
if (existsSync11(idDir)) {
|
|
4250
4513
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
4251
4514
|
}
|
|
4252
4515
|
} catch {
|
|
@@ -4267,62 +4530,62 @@ async function cloudSync(config) {
|
|
|
4267
4530
|
function recordRosterDeletion(name) {
|
|
4268
4531
|
let deletions = [];
|
|
4269
4532
|
try {
|
|
4270
|
-
if (
|
|
4271
|
-
deletions = JSON.parse(
|
|
4533
|
+
if (existsSync11(ROSTER_DELETIONS_PATH)) {
|
|
4534
|
+
deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
4272
4535
|
}
|
|
4273
4536
|
} catch {
|
|
4274
4537
|
}
|
|
4275
4538
|
if (!deletions.includes(name)) deletions.push(name);
|
|
4276
|
-
|
|
4539
|
+
writeFileSync6(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
4277
4540
|
}
|
|
4278
4541
|
function consumeRosterDeletions() {
|
|
4279
4542
|
try {
|
|
4280
|
-
if (!
|
|
4281
|
-
const deletions = JSON.parse(
|
|
4282
|
-
|
|
4543
|
+
if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
|
|
4544
|
+
const deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
4545
|
+
writeFileSync6(ROSTER_DELETIONS_PATH, "[]");
|
|
4283
4546
|
return deletions;
|
|
4284
4547
|
} catch {
|
|
4285
4548
|
return [];
|
|
4286
4549
|
}
|
|
4287
4550
|
}
|
|
4288
4551
|
function buildRosterBlob(paths) {
|
|
4289
|
-
const rosterPath = paths?.rosterPath ??
|
|
4290
|
-
const identityDir = paths?.identityDir ??
|
|
4291
|
-
const configPath = paths?.configPath ??
|
|
4552
|
+
const rosterPath = paths?.rosterPath ?? path11.join(EXE_AI_DIR, "exe-employees.json");
|
|
4553
|
+
const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
|
|
4554
|
+
const configPath = paths?.configPath ?? path11.join(EXE_AI_DIR, "config.json");
|
|
4292
4555
|
let roster = [];
|
|
4293
|
-
if (
|
|
4556
|
+
if (existsSync11(rosterPath)) {
|
|
4294
4557
|
try {
|
|
4295
|
-
roster = JSON.parse(
|
|
4558
|
+
roster = JSON.parse(readFileSync8(rosterPath, "utf-8"));
|
|
4296
4559
|
} catch {
|
|
4297
4560
|
}
|
|
4298
4561
|
}
|
|
4299
4562
|
const identities = {};
|
|
4300
|
-
if (
|
|
4563
|
+
if (existsSync11(identityDir)) {
|
|
4301
4564
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
4302
4565
|
try {
|
|
4303
|
-
identities[file] =
|
|
4566
|
+
identities[file] = readFileSync8(path11.join(identityDir, file), "utf-8");
|
|
4304
4567
|
} catch {
|
|
4305
4568
|
}
|
|
4306
4569
|
}
|
|
4307
4570
|
}
|
|
4308
4571
|
let config;
|
|
4309
|
-
if (
|
|
4572
|
+
if (existsSync11(configPath)) {
|
|
4310
4573
|
try {
|
|
4311
|
-
config = JSON.parse(
|
|
4574
|
+
config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4312
4575
|
} catch {
|
|
4313
4576
|
}
|
|
4314
4577
|
}
|
|
4315
4578
|
let agentConfig;
|
|
4316
|
-
const agentConfigPath =
|
|
4317
|
-
if (
|
|
4579
|
+
const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
|
|
4580
|
+
if (existsSync11(agentConfigPath)) {
|
|
4318
4581
|
try {
|
|
4319
|
-
agentConfig = JSON.parse(
|
|
4582
|
+
agentConfig = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
|
|
4320
4583
|
} catch {
|
|
4321
4584
|
}
|
|
4322
4585
|
}
|
|
4323
4586
|
const deletedNames = consumeRosterDeletions();
|
|
4324
4587
|
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
4325
|
-
const hash =
|
|
4588
|
+
const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
4326
4589
|
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
4327
4590
|
}
|
|
4328
4591
|
async function cloudPushRoster(config) {
|
|
@@ -4392,23 +4655,24 @@ async function cloudPullRoster(config) {
|
|
|
4392
4655
|
}
|
|
4393
4656
|
}
|
|
4394
4657
|
function mergeConfig(remoteConfig, configPath) {
|
|
4395
|
-
const cfgPath = configPath ??
|
|
4658
|
+
const cfgPath = configPath ?? path11.join(EXE_AI_DIR, "config.json");
|
|
4396
4659
|
let local = {};
|
|
4397
|
-
if (
|
|
4660
|
+
if (existsSync11(cfgPath)) {
|
|
4398
4661
|
try {
|
|
4399
|
-
local = JSON.parse(
|
|
4662
|
+
local = JSON.parse(readFileSync8(cfgPath, "utf-8"));
|
|
4400
4663
|
} catch {
|
|
4401
4664
|
}
|
|
4402
4665
|
}
|
|
4403
4666
|
const merged = { ...remoteConfig, ...local };
|
|
4404
|
-
const dir =
|
|
4405
|
-
|
|
4406
|
-
|
|
4667
|
+
const dir = path11.dirname(cfgPath);
|
|
4668
|
+
ensurePrivateDirSync(dir);
|
|
4669
|
+
writeFileSync6(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
4670
|
+
enforcePrivateFileSync(cfgPath);
|
|
4407
4671
|
}
|
|
4408
4672
|
async function mergeRosterFromRemote(remote, paths) {
|
|
4409
4673
|
return withRosterLock(async () => {
|
|
4410
4674
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
4411
|
-
const identityDir = paths?.identityDir ??
|
|
4675
|
+
const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
|
|
4412
4676
|
const localEmployees = await loadEmployees(rosterPath);
|
|
4413
4677
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
4414
4678
|
let added = 0;
|
|
@@ -4429,15 +4693,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
4429
4693
|
) ?? lookupKey;
|
|
4430
4694
|
const remoteIdentity = remote.identities[matchedKey];
|
|
4431
4695
|
if (remoteIdentity) {
|
|
4432
|
-
if (!
|
|
4433
|
-
const idPath =
|
|
4696
|
+
if (!existsSync11(identityDir)) mkdirSync4(identityDir, { recursive: true });
|
|
4697
|
+
const idPath = path11.join(identityDir, `${remoteEmp.name}.md`);
|
|
4434
4698
|
let localIdentity = null;
|
|
4435
4699
|
try {
|
|
4436
|
-
localIdentity =
|
|
4700
|
+
localIdentity = existsSync11(idPath) ? readFileSync8(idPath, "utf-8") : null;
|
|
4437
4701
|
} catch {
|
|
4438
4702
|
}
|
|
4439
4703
|
if (localIdentity !== remoteIdentity) {
|
|
4440
|
-
|
|
4704
|
+
writeFileSync6(idPath, remoteIdentity, "utf-8");
|
|
4441
4705
|
identitiesUpdated++;
|
|
4442
4706
|
}
|
|
4443
4707
|
}
|
|
@@ -4463,16 +4727,18 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
4463
4727
|
}
|
|
4464
4728
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
4465
4729
|
try {
|
|
4466
|
-
const agentConfigPath =
|
|
4730
|
+
const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
|
|
4467
4731
|
let local = {};
|
|
4468
|
-
if (
|
|
4732
|
+
if (existsSync11(agentConfigPath)) {
|
|
4469
4733
|
try {
|
|
4470
|
-
local = JSON.parse(
|
|
4734
|
+
local = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
|
|
4471
4735
|
} catch {
|
|
4472
4736
|
}
|
|
4473
4737
|
}
|
|
4474
4738
|
const merged = { ...remote.agentConfig, ...local };
|
|
4475
|
-
|
|
4739
|
+
ensurePrivateDirSync(path11.dirname(agentConfigPath));
|
|
4740
|
+
writeFileSync6(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
4741
|
+
enforcePrivateFileSync(agentConfigPath);
|
|
4476
4742
|
} catch {
|
|
4477
4743
|
}
|
|
4478
4744
|
}
|
|
@@ -4896,7 +5162,7 @@ async function cloudPullDocuments(config) {
|
|
|
4896
5162
|
}
|
|
4897
5163
|
return { pulled };
|
|
4898
5164
|
}
|
|
4899
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
5165
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
4900
5166
|
var init_cloud_sync = __esm({
|
|
4901
5167
|
"src/lib/cloud-sync.ts"() {
|
|
4902
5168
|
"use strict";
|
|
@@ -4907,12 +5173,15 @@ var init_cloud_sync = __esm({
|
|
|
4907
5173
|
init_config();
|
|
4908
5174
|
init_crdt_sync();
|
|
4909
5175
|
init_employees();
|
|
5176
|
+
init_secure_files();
|
|
4910
5177
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
4911
5178
|
FETCH_TIMEOUT_MS = 3e4;
|
|
4912
5179
|
PUSH_BATCH_SIZE = 5e3;
|
|
4913
|
-
ROSTER_LOCK_PATH =
|
|
5180
|
+
ROSTER_LOCK_PATH = path11.join(EXE_AI_DIR, "roster-merge.lock");
|
|
4914
5181
|
LOCK_STALE_MS = 3e4;
|
|
4915
|
-
|
|
5182
|
+
_pgPromise = null;
|
|
5183
|
+
_pgFailed = false;
|
|
5184
|
+
ROSTER_DELETIONS_PATH = path11.join(EXE_AI_DIR, "roster-deletions.json");
|
|
4916
5185
|
}
|
|
4917
5186
|
});
|
|
4918
5187
|
|
|
@@ -4922,37 +5191,39 @@ __export(preferences_exports, {
|
|
|
4922
5191
|
loadPreferences: () => loadPreferences,
|
|
4923
5192
|
savePreferences: () => savePreferences
|
|
4924
5193
|
});
|
|
4925
|
-
import { existsSync as
|
|
4926
|
-
import
|
|
4927
|
-
import
|
|
4928
|
-
function loadPreferences(homeDir =
|
|
4929
|
-
const configPath =
|
|
4930
|
-
if (!
|
|
5194
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
5195
|
+
import path12 from "path";
|
|
5196
|
+
import os7 from "os";
|
|
5197
|
+
function loadPreferences(homeDir = os7.homedir()) {
|
|
5198
|
+
const configPath = path12.join(homeDir, ".exe-os", "config.json");
|
|
5199
|
+
if (!existsSync12(configPath)) return {};
|
|
4931
5200
|
try {
|
|
4932
|
-
const config = JSON.parse(
|
|
5201
|
+
const config = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4933
5202
|
return config.preferences ?? {};
|
|
4934
5203
|
} catch {
|
|
4935
5204
|
return {};
|
|
4936
5205
|
}
|
|
4937
5206
|
}
|
|
4938
|
-
function savePreferences(prefs, homeDir =
|
|
4939
|
-
const configDir =
|
|
4940
|
-
const configPath =
|
|
4941
|
-
|
|
5207
|
+
function savePreferences(prefs, homeDir = os7.homedir()) {
|
|
5208
|
+
const configDir = path12.join(homeDir, ".exe-os");
|
|
5209
|
+
const configPath = path12.join(configDir, "config.json");
|
|
5210
|
+
ensurePrivateDirSync(configDir);
|
|
4942
5211
|
let config = {};
|
|
4943
|
-
if (
|
|
5212
|
+
if (existsSync12(configPath)) {
|
|
4944
5213
|
try {
|
|
4945
|
-
config = JSON.parse(
|
|
5214
|
+
config = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4946
5215
|
} catch {
|
|
4947
5216
|
config = {};
|
|
4948
5217
|
}
|
|
4949
5218
|
}
|
|
4950
5219
|
config.preferences = prefs;
|
|
4951
|
-
|
|
5220
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
5221
|
+
enforcePrivateFileSync(configPath);
|
|
4952
5222
|
}
|
|
4953
5223
|
var init_preferences = __esm({
|
|
4954
5224
|
"src/lib/preferences.ts"() {
|
|
4955
5225
|
"use strict";
|
|
5226
|
+
init_secure_files();
|
|
4956
5227
|
}
|
|
4957
5228
|
});
|
|
4958
5229
|
|
|
@@ -5723,17 +5994,17 @@ __export(identity_exports, {
|
|
|
5723
5994
|
listIdentities: () => listIdentities,
|
|
5724
5995
|
updateIdentity: () => updateIdentity
|
|
5725
5996
|
});
|
|
5726
|
-
import { existsSync as
|
|
5997
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
5727
5998
|
import { readdirSync as readdirSync2 } from "fs";
|
|
5728
|
-
import
|
|
5999
|
+
import path13 from "path";
|
|
5729
6000
|
import { createHash as createHash2 } from "crypto";
|
|
5730
6001
|
function ensureDir() {
|
|
5731
|
-
if (!
|
|
5732
|
-
|
|
6002
|
+
if (!existsSync13(IDENTITY_DIR2)) {
|
|
6003
|
+
mkdirSync5(IDENTITY_DIR2, { recursive: true });
|
|
5733
6004
|
}
|
|
5734
6005
|
}
|
|
5735
6006
|
function identityPath(agentId) {
|
|
5736
|
-
return
|
|
6007
|
+
return path13.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
5737
6008
|
}
|
|
5738
6009
|
function parseFrontmatter(raw) {
|
|
5739
6010
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -5774,8 +6045,8 @@ function contentHash(content) {
|
|
|
5774
6045
|
}
|
|
5775
6046
|
function getIdentity(agentId) {
|
|
5776
6047
|
const filePath = identityPath(agentId);
|
|
5777
|
-
if (!
|
|
5778
|
-
const raw =
|
|
6048
|
+
if (!existsSync13(filePath)) return null;
|
|
6049
|
+
const raw = readFileSync10(filePath, "utf-8");
|
|
5779
6050
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
5780
6051
|
return {
|
|
5781
6052
|
agentId,
|
|
@@ -5789,7 +6060,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
5789
6060
|
ensureDir();
|
|
5790
6061
|
const filePath = identityPath(agentId);
|
|
5791
6062
|
const hash = contentHash(content);
|
|
5792
|
-
|
|
6063
|
+
writeFileSync8(filePath, content, "utf-8");
|
|
5793
6064
|
try {
|
|
5794
6065
|
const client = getClient();
|
|
5795
6066
|
await client.execute({
|
|
@@ -5845,7 +6116,7 @@ var init_identity = __esm({
|
|
|
5845
6116
|
"use strict";
|
|
5846
6117
|
init_config();
|
|
5847
6118
|
init_database();
|
|
5848
|
-
IDENTITY_DIR2 =
|
|
6119
|
+
IDENTITY_DIR2 = path13.join(EXE_AI_DIR, "identity");
|
|
5849
6120
|
}
|
|
5850
6121
|
});
|
|
5851
6122
|
|
|
@@ -6396,36 +6667,36 @@ __export(session_wrappers_exports, {
|
|
|
6396
6667
|
generateSessionWrappers: () => generateSessionWrappers
|
|
6397
6668
|
});
|
|
6398
6669
|
import {
|
|
6399
|
-
existsSync as
|
|
6400
|
-
readFileSync as
|
|
6401
|
-
writeFileSync as
|
|
6402
|
-
mkdirSync as
|
|
6403
|
-
chmodSync,
|
|
6670
|
+
existsSync as existsSync14,
|
|
6671
|
+
readFileSync as readFileSync11,
|
|
6672
|
+
writeFileSync as writeFileSync9,
|
|
6673
|
+
mkdirSync as mkdirSync6,
|
|
6674
|
+
chmodSync as chmodSync2,
|
|
6404
6675
|
readdirSync as readdirSync3,
|
|
6405
6676
|
unlinkSync as unlinkSync6
|
|
6406
6677
|
} from "fs";
|
|
6407
|
-
import
|
|
6678
|
+
import path14 from "path";
|
|
6408
6679
|
import { homedir as homedir3 } from "os";
|
|
6409
6680
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
6410
6681
|
const home = homeDir ?? homedir3();
|
|
6411
|
-
const binDir =
|
|
6412
|
-
const rosterPath =
|
|
6413
|
-
|
|
6414
|
-
const exeStartDst =
|
|
6682
|
+
const binDir = path14.join(home, ".exe-os", "bin");
|
|
6683
|
+
const rosterPath = path14.join(home, ".exe-os", "exe-employees.json");
|
|
6684
|
+
mkdirSync6(binDir, { recursive: true });
|
|
6685
|
+
const exeStartDst = path14.join(binDir, "exe-start");
|
|
6415
6686
|
const candidates = [
|
|
6416
|
-
|
|
6417
|
-
|
|
6687
|
+
path14.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
6688
|
+
path14.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
6418
6689
|
];
|
|
6419
6690
|
for (const src of candidates) {
|
|
6420
|
-
if (
|
|
6421
|
-
|
|
6422
|
-
|
|
6691
|
+
if (existsSync14(src)) {
|
|
6692
|
+
writeFileSync9(exeStartDst, readFileSync11(src));
|
|
6693
|
+
chmodSync2(exeStartDst, 493);
|
|
6423
6694
|
break;
|
|
6424
6695
|
}
|
|
6425
6696
|
}
|
|
6426
6697
|
let employees = [];
|
|
6427
6698
|
try {
|
|
6428
|
-
employees = JSON.parse(
|
|
6699
|
+
employees = JSON.parse(readFileSync11(rosterPath, "utf8"));
|
|
6429
6700
|
} catch {
|
|
6430
6701
|
return { created: 0, pathConfigured: false };
|
|
6431
6702
|
}
|
|
@@ -6435,9 +6706,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
6435
6706
|
try {
|
|
6436
6707
|
for (const f of readdirSync3(binDir)) {
|
|
6437
6708
|
if (f === "exe-start") continue;
|
|
6438
|
-
const fPath =
|
|
6709
|
+
const fPath = path14.join(binDir, f);
|
|
6439
6710
|
try {
|
|
6440
|
-
const content =
|
|
6711
|
+
const content = readFileSync11(fPath, "utf8");
|
|
6441
6712
|
if (content.includes("exe-start")) {
|
|
6442
6713
|
unlinkSync6(fPath);
|
|
6443
6714
|
}
|
|
@@ -6452,31 +6723,31 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
6452
6723
|
`;
|
|
6453
6724
|
for (const emp of employees) {
|
|
6454
6725
|
for (let n = 1; n <= MAX_N; n++) {
|
|
6455
|
-
const wrapperPath =
|
|
6456
|
-
|
|
6457
|
-
|
|
6726
|
+
const wrapperPath = path14.join(binDir, `${emp.name}${n}`);
|
|
6727
|
+
writeFileSync9(wrapperPath, wrapperContent);
|
|
6728
|
+
chmodSync2(wrapperPath, 493);
|
|
6458
6729
|
created++;
|
|
6459
6730
|
}
|
|
6460
6731
|
}
|
|
6461
6732
|
const codexLauncherCandidates = [
|
|
6462
|
-
|
|
6463
|
-
|
|
6733
|
+
path14.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
6734
|
+
path14.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
6464
6735
|
];
|
|
6465
6736
|
let codexLauncher = null;
|
|
6466
6737
|
for (const c of codexLauncherCandidates) {
|
|
6467
|
-
if (
|
|
6738
|
+
if (existsSync14(c)) {
|
|
6468
6739
|
codexLauncher = c;
|
|
6469
6740
|
break;
|
|
6470
6741
|
}
|
|
6471
6742
|
}
|
|
6472
6743
|
if (codexLauncher) {
|
|
6473
6744
|
for (const emp of employees) {
|
|
6474
|
-
const wrapperPath =
|
|
6745
|
+
const wrapperPath = path14.join(binDir, `${emp.name}-codex`);
|
|
6475
6746
|
const content = `#!/bin/bash
|
|
6476
6747
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
6477
6748
|
`;
|
|
6478
|
-
|
|
6479
|
-
|
|
6749
|
+
writeFileSync9(wrapperPath, content);
|
|
6750
|
+
chmodSync2(wrapperPath, 493);
|
|
6480
6751
|
created++;
|
|
6481
6752
|
}
|
|
6482
6753
|
}
|
|
@@ -6494,24 +6765,24 @@ export PATH="${binDir}:$PATH"
|
|
|
6494
6765
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
6495
6766
|
const profilePaths = [];
|
|
6496
6767
|
if (shell.includes("zsh")) {
|
|
6497
|
-
profilePaths.push(
|
|
6768
|
+
profilePaths.push(path14.join(home, ".zshrc"));
|
|
6498
6769
|
} else if (shell.includes("bash")) {
|
|
6499
|
-
profilePaths.push(
|
|
6500
|
-
profilePaths.push(
|
|
6770
|
+
profilePaths.push(path14.join(home, ".bashrc"));
|
|
6771
|
+
profilePaths.push(path14.join(home, ".bash_profile"));
|
|
6501
6772
|
} else {
|
|
6502
|
-
profilePaths.push(
|
|
6773
|
+
profilePaths.push(path14.join(home, ".profile"));
|
|
6503
6774
|
}
|
|
6504
6775
|
for (const profilePath of profilePaths) {
|
|
6505
6776
|
try {
|
|
6506
6777
|
let content = "";
|
|
6507
6778
|
try {
|
|
6508
|
-
content =
|
|
6779
|
+
content = readFileSync11(profilePath, "utf8");
|
|
6509
6780
|
} catch {
|
|
6510
6781
|
}
|
|
6511
6782
|
if (content.includes(".exe-os/bin")) {
|
|
6512
6783
|
return false;
|
|
6513
6784
|
}
|
|
6514
|
-
|
|
6785
|
+
writeFileSync9(profilePath, content + exportLine);
|
|
6515
6786
|
return true;
|
|
6516
6787
|
} catch {
|
|
6517
6788
|
continue;
|
|
@@ -6530,14 +6801,14 @@ var init_session_wrappers = __esm({
|
|
|
6530
6801
|
// src/lib/setup-wizard.ts
|
|
6531
6802
|
init_config();
|
|
6532
6803
|
init_keychain();
|
|
6533
|
-
import
|
|
6534
|
-
import { existsSync as
|
|
6535
|
-
import
|
|
6536
|
-
import
|
|
6804
|
+
import crypto4 from "crypto";
|
|
6805
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync7 } from "fs";
|
|
6806
|
+
import os8 from "os";
|
|
6807
|
+
import path15 from "path";
|
|
6537
6808
|
import { createInterface } from "readline";
|
|
6538
6809
|
|
|
6539
6810
|
// src/lib/model-downloader.ts
|
|
6540
|
-
import { createWriteStream, createReadStream, existsSync as
|
|
6811
|
+
import { createWriteStream, createReadStream, existsSync as existsSync4, unlinkSync, renameSync as renameSync2 } from "fs";
|
|
6541
6812
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
6542
6813
|
import { createHash } from "crypto";
|
|
6543
6814
|
import path3 from "path";
|
|
@@ -6550,7 +6821,7 @@ async function downloadModel(opts) {
|
|
|
6550
6821
|
const destPath = path3.join(destDir, LOCAL_FILENAME);
|
|
6551
6822
|
const tmpPath = destPath + ".tmp";
|
|
6552
6823
|
await mkdir3(destDir, { recursive: true });
|
|
6553
|
-
if (
|
|
6824
|
+
if (existsSync4(destPath)) {
|
|
6554
6825
|
const hash = await fileHash(destPath);
|
|
6555
6826
|
if (hash === EXPECTED_SHA256) {
|
|
6556
6827
|
return destPath;
|
|
@@ -6562,7 +6833,7 @@ async function downloadModel(opts) {
|
|
|
6562
6833
|
let downloaded = 0;
|
|
6563
6834
|
for (let attempt = 1; attempt <= MAX_RETRIES2; attempt++) {
|
|
6564
6835
|
try {
|
|
6565
|
-
if (
|
|
6836
|
+
if (existsSync4(tmpPath)) unlinkSync(tmpPath);
|
|
6566
6837
|
const response = await fetchFn(GGUF_URL, {
|
|
6567
6838
|
redirect: "follow",
|
|
6568
6839
|
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
@@ -6607,7 +6878,7 @@ async function downloadModel(opts) {
|
|
|
6607
6878
|
process.stderr.write(`
|
|
6608
6879
|
Download attempt ${attempt} failed, retrying...
|
|
6609
6880
|
`);
|
|
6610
|
-
if (
|
|
6881
|
+
if (existsSync4(tmpPath)) unlinkSync(tmpPath);
|
|
6611
6882
|
}
|
|
6612
6883
|
}
|
|
6613
6884
|
}
|
|
@@ -6625,32 +6896,32 @@ async function fileHash(filePath) {
|
|
|
6625
6896
|
|
|
6626
6897
|
// src/lib/setup-wizard.ts
|
|
6627
6898
|
function findPackageRoot2() {
|
|
6628
|
-
let dir =
|
|
6629
|
-
const root =
|
|
6899
|
+
let dir = path15.dirname(new URL(import.meta.url).pathname);
|
|
6900
|
+
const root = path15.parse(dir).root;
|
|
6630
6901
|
while (dir !== root) {
|
|
6631
|
-
const pkgPath =
|
|
6632
|
-
if (
|
|
6902
|
+
const pkgPath = path15.join(dir, "package.json");
|
|
6903
|
+
if (existsSync15(pkgPath)) {
|
|
6633
6904
|
try {
|
|
6634
|
-
const pkg = JSON.parse(
|
|
6905
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
6635
6906
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
6636
6907
|
} catch {
|
|
6637
6908
|
}
|
|
6638
6909
|
}
|
|
6639
|
-
dir =
|
|
6910
|
+
dir = path15.dirname(dir);
|
|
6640
6911
|
}
|
|
6641
6912
|
return null;
|
|
6642
6913
|
}
|
|
6643
|
-
var SETUP_STATE_PATH =
|
|
6914
|
+
var SETUP_STATE_PATH = path15.join(os8.homedir(), ".exe-os", "setup-state.json");
|
|
6644
6915
|
function loadSetupState() {
|
|
6645
6916
|
try {
|
|
6646
|
-
return JSON.parse(
|
|
6917
|
+
return JSON.parse(readFileSync12(SETUP_STATE_PATH, "utf8"));
|
|
6647
6918
|
} catch {
|
|
6648
6919
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6649
6920
|
}
|
|
6650
6921
|
}
|
|
6651
6922
|
function saveSetupState(state) {
|
|
6652
|
-
|
|
6653
|
-
|
|
6923
|
+
mkdirSync7(path15.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
6924
|
+
writeFileSync10(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
6654
6925
|
}
|
|
6655
6926
|
function clearSetupState() {
|
|
6656
6927
|
try {
|
|
@@ -6669,10 +6940,10 @@ function ask(rl, prompt) {
|
|
|
6669
6940
|
});
|
|
6670
6941
|
}
|
|
6671
6942
|
function getAvailableMemoryGB() {
|
|
6672
|
-
return
|
|
6943
|
+
return os8.freemem() / (1024 * 1024 * 1024);
|
|
6673
6944
|
}
|
|
6674
6945
|
function getTotalMemoryGB() {
|
|
6675
|
-
return
|
|
6946
|
+
return os8.totalmem() / (1024 * 1024 * 1024);
|
|
6676
6947
|
}
|
|
6677
6948
|
function isLowMemory() {
|
|
6678
6949
|
return getAvailableMemoryGB() < 2;
|
|
@@ -6683,8 +6954,8 @@ async function validateModel(log) {
|
|
|
6683
6954
|
if (totalGB <= 8 || isLowMemory()) {
|
|
6684
6955
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
6685
6956
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
6686
|
-
const modelPath =
|
|
6687
|
-
if (
|
|
6957
|
+
const modelPath = path15.join(MODELS_DIR, LOCAL_FILENAME);
|
|
6958
|
+
if (existsSync15(modelPath)) {
|
|
6688
6959
|
const { statSync: statSync2 } = await import("fs");
|
|
6689
6960
|
const size = statSync2(modelPath).size;
|
|
6690
6961
|
if (size > 300 * 1e6) {
|
|
@@ -6775,7 +7046,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6775
7046
|
if (state.completedSteps.length > 0) {
|
|
6776
7047
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
6777
7048
|
}
|
|
6778
|
-
if (
|
|
7049
|
+
if (existsSync15(LEGACY_LANCE_PATH)) {
|
|
6779
7050
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
6780
7051
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
6781
7052
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -6787,7 +7058,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6787
7058
|
log("Encryption key already exists \u2014 skipping generation.");
|
|
6788
7059
|
} else {
|
|
6789
7060
|
log("Generating 256-bit encryption key...");
|
|
6790
|
-
const key =
|
|
7061
|
+
const key = crypto4.randomBytes(32);
|
|
6791
7062
|
await setMasterKey(key);
|
|
6792
7063
|
log("Encryption key generated and stored securely.");
|
|
6793
7064
|
}
|
|
@@ -6939,19 +7210,19 @@ async function runSetupWizard(opts = {}) {
|
|
|
6939
7210
|
await saveConfig(config);
|
|
6940
7211
|
log("");
|
|
6941
7212
|
try {
|
|
6942
|
-
const claudeJsonPath =
|
|
7213
|
+
const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
|
|
6943
7214
|
let claudeJson = {};
|
|
6944
7215
|
try {
|
|
6945
|
-
claudeJson = JSON.parse(
|
|
7216
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6946
7217
|
} catch {
|
|
6947
7218
|
}
|
|
6948
7219
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
6949
7220
|
const projects = claudeJson.projects;
|
|
6950
|
-
for (const dir of [process.cwd(),
|
|
7221
|
+
for (const dir of [process.cwd(), os8.homedir()]) {
|
|
6951
7222
|
if (!projects[dir]) projects[dir] = {};
|
|
6952
7223
|
projects[dir].hasTrustDialogAccepted = true;
|
|
6953
7224
|
}
|
|
6954
|
-
|
|
7225
|
+
writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6955
7226
|
} catch {
|
|
6956
7227
|
}
|
|
6957
7228
|
state.completedSteps.push(5);
|
|
@@ -6965,7 +7236,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6965
7236
|
const prefs = { ...existingPrefs };
|
|
6966
7237
|
log("=== Config Defaults ===");
|
|
6967
7238
|
log("");
|
|
6968
|
-
const ghosttyDetected =
|
|
7239
|
+
const ghosttyDetected = existsSync15(path15.join(os8.homedir(), ".config", "ghostty")) || existsSync15(path15.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
6969
7240
|
if (ghosttyDetected) {
|
|
6970
7241
|
const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
6971
7242
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -7012,7 +7283,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7012
7283
|
let missingIdentities = [];
|
|
7013
7284
|
for (const emp of roster) {
|
|
7014
7285
|
const idPath = identityPath2(emp.name);
|
|
7015
|
-
if (!
|
|
7286
|
+
if (!existsSync15(idPath)) {
|
|
7016
7287
|
missingIdentities.push(emp.name);
|
|
7017
7288
|
}
|
|
7018
7289
|
}
|
|
@@ -7044,7 +7315,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7044
7315
|
}
|
|
7045
7316
|
missingIdentities = [];
|
|
7046
7317
|
for (const emp of roster) {
|
|
7047
|
-
if (!
|
|
7318
|
+
if (!existsSync15(identityPath2(emp.name))) {
|
|
7048
7319
|
missingIdentities.push(emp.name);
|
|
7049
7320
|
}
|
|
7050
7321
|
}
|
|
@@ -7109,9 +7380,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
7109
7380
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
7110
7381
|
if (cooIdentityContent) {
|
|
7111
7382
|
const cooIdPath = identityPath2(cooName);
|
|
7112
|
-
|
|
7383
|
+
mkdirSync7(path15.dirname(cooIdPath), { recursive: true });
|
|
7113
7384
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
7114
|
-
|
|
7385
|
+
writeFileSync10(cooIdPath, replaced, "utf-8");
|
|
7115
7386
|
}
|
|
7116
7387
|
registerBinSymlinks2(cooName);
|
|
7117
7388
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -7205,9 +7476,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
7205
7476
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
7206
7477
|
if (ctoIdentityContent) {
|
|
7207
7478
|
const ctoIdPath = identityPath2(ctoName);
|
|
7208
|
-
|
|
7479
|
+
mkdirSync7(path15.dirname(ctoIdPath), { recursive: true });
|
|
7209
7480
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
7210
|
-
|
|
7481
|
+
writeFileSync10(ctoIdPath, replaced, "utf-8");
|
|
7211
7482
|
}
|
|
7212
7483
|
registerBinSymlinks2(ctoName);
|
|
7213
7484
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -7228,9 +7499,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
7228
7499
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
7229
7500
|
if (cmoIdentityContent) {
|
|
7230
7501
|
const cmoIdPath = identityPath2(cmoName);
|
|
7231
|
-
|
|
7502
|
+
mkdirSync7(path15.dirname(cmoIdPath), { recursive: true });
|
|
7232
7503
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
7233
|
-
|
|
7504
|
+
writeFileSync10(cmoIdPath, replaced, "utf-8");
|
|
7234
7505
|
}
|
|
7235
7506
|
registerBinSymlinks2(cmoName);
|
|
7236
7507
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -7252,7 +7523,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7252
7523
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
7253
7524
|
}
|
|
7254
7525
|
if (wrapResult.pathConfigured) {
|
|
7255
|
-
const binDir =
|
|
7526
|
+
const binDir = path15.join(os8.homedir(), ".exe-os", "bin");
|
|
7256
7527
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
7257
7528
|
pathJustConfigured = true;
|
|
7258
7529
|
}
|
|
@@ -7295,7 +7566,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7295
7566
|
const pkgRoot2 = findPackageRoot2();
|
|
7296
7567
|
if (pkgRoot2) {
|
|
7297
7568
|
try {
|
|
7298
|
-
version = JSON.parse(
|
|
7569
|
+
version = JSON.parse(readFileSync12(path15.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
7299
7570
|
} catch {
|
|
7300
7571
|
}
|
|
7301
7572
|
}
|