@askexenow/exe-os 0.9.7 → 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 +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
package/dist/lib/embedder.js
CHANGED
|
@@ -8,6 +8,44 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// src/lib/secure-files.ts
|
|
12
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
13
|
+
import { chmod, mkdir } from "fs/promises";
|
|
14
|
+
async function ensurePrivateDir(dirPath) {
|
|
15
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
16
|
+
try {
|
|
17
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function ensurePrivateDirSync(dirPath) {
|
|
22
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
23
|
+
try {
|
|
24
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function enforcePrivateFile(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function enforcePrivateFileSync(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
41
|
+
var init_secure_files = __esm({
|
|
42
|
+
"src/lib/secure-files.ts"() {
|
|
43
|
+
"use strict";
|
|
44
|
+
PRIVATE_DIR_MODE = 448;
|
|
45
|
+
PRIVATE_FILE_MODE = 384;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
11
49
|
// src/lib/config.ts
|
|
12
50
|
var config_exports = {};
|
|
13
51
|
__export(config_exports, {
|
|
@@ -24,8 +62,8 @@ __export(config_exports, {
|
|
|
24
62
|
migrateConfig: () => migrateConfig,
|
|
25
63
|
saveConfig: () => saveConfig
|
|
26
64
|
});
|
|
27
|
-
import { readFile, writeFile
|
|
28
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
65
|
+
import { readFile, writeFile } from "fs/promises";
|
|
66
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
29
67
|
import path from "path";
|
|
30
68
|
import os from "os";
|
|
31
69
|
function resolveDataDir() {
|
|
@@ -33,7 +71,7 @@ function resolveDataDir() {
|
|
|
33
71
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
34
72
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
35
73
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
36
|
-
if (!
|
|
74
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
37
75
|
try {
|
|
38
76
|
renameSync(legacyDir, newDir);
|
|
39
77
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -96,9 +134,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
96
134
|
}
|
|
97
135
|
async function loadConfig() {
|
|
98
136
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
99
|
-
await
|
|
137
|
+
await ensurePrivateDir(dir);
|
|
100
138
|
const configPath = path.join(dir, "config.json");
|
|
101
|
-
if (!
|
|
139
|
+
if (!existsSync2(configPath)) {
|
|
102
140
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
103
141
|
}
|
|
104
142
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -111,6 +149,7 @@ async function loadConfig() {
|
|
|
111
149
|
`);
|
|
112
150
|
try {
|
|
113
151
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
152
|
+
await enforcePrivateFile(configPath);
|
|
114
153
|
} catch {
|
|
115
154
|
}
|
|
116
155
|
}
|
|
@@ -129,7 +168,7 @@ async function loadConfig() {
|
|
|
129
168
|
function loadConfigSync() {
|
|
130
169
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
131
170
|
const configPath = path.join(dir, "config.json");
|
|
132
|
-
if (!
|
|
171
|
+
if (!existsSync2(configPath)) {
|
|
133
172
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
134
173
|
}
|
|
135
174
|
try {
|
|
@@ -147,12 +186,10 @@ function loadConfigSync() {
|
|
|
147
186
|
}
|
|
148
187
|
async function saveConfig(config) {
|
|
149
188
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
150
|
-
await
|
|
189
|
+
await ensurePrivateDir(dir);
|
|
151
190
|
const configPath = path.join(dir, "config.json");
|
|
152
191
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
153
|
-
|
|
154
|
-
await chmod(configPath, 384);
|
|
155
|
-
}
|
|
192
|
+
await enforcePrivateFile(configPath);
|
|
156
193
|
}
|
|
157
194
|
async function loadConfigFrom(configPath) {
|
|
158
195
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -172,6 +209,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
172
209
|
var init_config = __esm({
|
|
173
210
|
"src/lib/config.ts"() {
|
|
174
211
|
"use strict";
|
|
212
|
+
init_secure_files();
|
|
175
213
|
EXE_AI_DIR = resolveDataDir();
|
|
176
214
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
177
215
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -257,20 +295,58 @@ import net from "net";
|
|
|
257
295
|
import os2 from "os";
|
|
258
296
|
import { spawn } from "child_process";
|
|
259
297
|
import { randomUUID } from "crypto";
|
|
260
|
-
import { existsSync as
|
|
261
|
-
import
|
|
298
|
+
import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
299
|
+
import path3 from "path";
|
|
262
300
|
import { fileURLToPath } from "url";
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
301
|
+
|
|
302
|
+
// src/lib/daemon-auth.ts
|
|
303
|
+
init_config();
|
|
304
|
+
init_secure_files();
|
|
305
|
+
import crypto from "crypto";
|
|
306
|
+
import path2 from "path";
|
|
307
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
308
|
+
var DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
|
|
309
|
+
function normalizeToken(token) {
|
|
310
|
+
if (!token) return null;
|
|
311
|
+
const trimmed = token.trim();
|
|
312
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
313
|
+
}
|
|
314
|
+
function readDaemonToken() {
|
|
315
|
+
try {
|
|
316
|
+
if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
|
|
317
|
+
return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function ensureDaemonToken(seed) {
|
|
323
|
+
const existing = readDaemonToken();
|
|
324
|
+
if (existing) return existing;
|
|
325
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
326
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
327
|
+
writeFileSync(DAEMON_TOKEN_PATH, `${token}
|
|
328
|
+
`, "utf8");
|
|
329
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
330
|
+
return token;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/lib/exe-daemon-client.ts
|
|
334
|
+
var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
335
|
+
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
336
|
+
var SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
266
337
|
var SPAWN_LOCK_STALE_MS = 3e4;
|
|
267
338
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
268
339
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
340
|
+
var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
269
341
|
var _socket = null;
|
|
270
342
|
var _connected = false;
|
|
271
343
|
var _buffer = "";
|
|
272
344
|
var _requestCount = 0;
|
|
345
|
+
var _consecutiveFailures = 0;
|
|
273
346
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
347
|
+
var MAX_RETRIES_BEFORE_RESTART = 3;
|
|
348
|
+
var RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
349
|
+
var MIN_DAEMON_AGE_MS = 3e4;
|
|
274
350
|
var _pending = /* @__PURE__ */ new Map();
|
|
275
351
|
var MAX_BUFFER = 1e7;
|
|
276
352
|
function handleData(chunk) {
|
|
@@ -299,9 +375,9 @@ function handleData(chunk) {
|
|
|
299
375
|
}
|
|
300
376
|
}
|
|
301
377
|
function cleanupStaleFiles() {
|
|
302
|
-
if (
|
|
378
|
+
if (existsSync4(PID_PATH)) {
|
|
303
379
|
try {
|
|
304
|
-
const pid = parseInt(
|
|
380
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
305
381
|
if (pid > 0) {
|
|
306
382
|
try {
|
|
307
383
|
process.kill(pid, 0);
|
|
@@ -322,11 +398,11 @@ function cleanupStaleFiles() {
|
|
|
322
398
|
}
|
|
323
399
|
}
|
|
324
400
|
function findPackageRoot() {
|
|
325
|
-
let dir =
|
|
326
|
-
const { root } =
|
|
401
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
402
|
+
const { root } = path3.parse(dir);
|
|
327
403
|
while (dir !== root) {
|
|
328
|
-
if (
|
|
329
|
-
dir =
|
|
404
|
+
if (existsSync4(path3.join(dir, "package.json"))) return dir;
|
|
405
|
+
dir = path3.dirname(dir);
|
|
330
406
|
}
|
|
331
407
|
return null;
|
|
332
408
|
}
|
|
@@ -352,16 +428,17 @@ function spawnDaemon() {
|
|
|
352
428
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
353
429
|
return;
|
|
354
430
|
}
|
|
355
|
-
const daemonPath =
|
|
356
|
-
if (!
|
|
431
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
432
|
+
if (!existsSync4(daemonPath)) {
|
|
357
433
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
358
434
|
`);
|
|
359
435
|
return;
|
|
360
436
|
}
|
|
361
437
|
const resolvedPath = daemonPath;
|
|
438
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
362
439
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
363
440
|
`);
|
|
364
|
-
const logPath =
|
|
441
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
365
442
|
let stderrFd = "ignore";
|
|
366
443
|
try {
|
|
367
444
|
stderrFd = openSync(logPath, "a");
|
|
@@ -379,7 +456,8 @@ function spawnDaemon() {
|
|
|
379
456
|
TMUX_PANE: void 0,
|
|
380
457
|
// Prevents resolveExeSession() from scoping to one session
|
|
381
458
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
382
|
-
EXE_DAEMON_PID: PID_PATH
|
|
459
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
460
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
383
461
|
}
|
|
384
462
|
});
|
|
385
463
|
child.unref();
|
|
@@ -489,13 +567,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
489
567
|
return;
|
|
490
568
|
}
|
|
491
569
|
const id = randomUUID();
|
|
570
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
492
571
|
const timer = setTimeout(() => {
|
|
493
572
|
_pending.delete(id);
|
|
494
573
|
resolve({ error: "Request timeout" });
|
|
495
574
|
}, timeoutMs);
|
|
496
575
|
_pending.set(id, { resolve, timer });
|
|
497
576
|
try {
|
|
498
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
577
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
499
578
|
} catch {
|
|
500
579
|
clearTimeout(timer);
|
|
501
580
|
_pending.delete(id);
|
|
@@ -512,74 +591,123 @@ async function pingDaemon() {
|
|
|
512
591
|
return null;
|
|
513
592
|
}
|
|
514
593
|
function killAndRespawnDaemon() {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
594
|
+
if (!acquireSpawnLock()) {
|
|
595
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
596
|
+
if (_socket) {
|
|
597
|
+
_socket.destroy();
|
|
598
|
+
_socket = null;
|
|
599
|
+
}
|
|
600
|
+
_connected = false;
|
|
601
|
+
_buffer = "";
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
try {
|
|
605
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
606
|
+
if (existsSync4(PID_PATH)) {
|
|
607
|
+
try {
|
|
608
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
609
|
+
if (pid > 0) {
|
|
610
|
+
try {
|
|
611
|
+
process.kill(pid, "SIGKILL");
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
523
614
|
}
|
|
615
|
+
} catch {
|
|
524
616
|
}
|
|
617
|
+
}
|
|
618
|
+
if (_socket) {
|
|
619
|
+
_socket.destroy();
|
|
620
|
+
_socket = null;
|
|
621
|
+
}
|
|
622
|
+
_connected = false;
|
|
623
|
+
_buffer = "";
|
|
624
|
+
try {
|
|
625
|
+
unlinkSync(PID_PATH);
|
|
525
626
|
} catch {
|
|
526
627
|
}
|
|
628
|
+
try {
|
|
629
|
+
unlinkSync(SOCKET_PATH);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
spawnDaemon();
|
|
633
|
+
} finally {
|
|
634
|
+
releaseSpawnLock();
|
|
527
635
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
_socket = null;
|
|
531
|
-
}
|
|
532
|
-
_connected = false;
|
|
533
|
-
_buffer = "";
|
|
636
|
+
}
|
|
637
|
+
function isDaemonTooYoung() {
|
|
534
638
|
try {
|
|
535
|
-
|
|
639
|
+
const stat = statSync(PID_PATH);
|
|
640
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
536
641
|
} catch {
|
|
642
|
+
return false;
|
|
537
643
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
644
|
+
}
|
|
645
|
+
async function retryThenRestart(doRequest, label) {
|
|
646
|
+
const result = await doRequest();
|
|
647
|
+
if (!result.error) {
|
|
648
|
+
_consecutiveFailures = 0;
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
_consecutiveFailures++;
|
|
652
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
653
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
654
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
655
|
+
`);
|
|
656
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
657
|
+
if (!_connected) {
|
|
658
|
+
if (!await connectToSocket()) continue;
|
|
659
|
+
}
|
|
660
|
+
const retry = await doRequest();
|
|
661
|
+
if (!retry.error) {
|
|
662
|
+
_consecutiveFailures = 0;
|
|
663
|
+
return retry;
|
|
664
|
+
}
|
|
665
|
+
_consecutiveFailures++;
|
|
666
|
+
}
|
|
667
|
+
if (isDaemonTooYoung()) {
|
|
668
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
669
|
+
`);
|
|
670
|
+
return { error: result.error };
|
|
671
|
+
}
|
|
672
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
673
|
+
`);
|
|
674
|
+
killAndRespawnDaemon();
|
|
675
|
+
const start = Date.now();
|
|
676
|
+
let delay = 200;
|
|
677
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
678
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
679
|
+
if (await connectToSocket()) break;
|
|
680
|
+
delay = Math.min(delay * 2, 3e3);
|
|
541
681
|
}
|
|
542
|
-
|
|
682
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
683
|
+
const final = await doRequest();
|
|
684
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
685
|
+
return final;
|
|
543
686
|
}
|
|
544
687
|
async function embedViaClient(text, priority = "high") {
|
|
545
688
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
546
689
|
_requestCount++;
|
|
547
690
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
548
691
|
const health = await pingDaemon();
|
|
549
|
-
if (!health) {
|
|
692
|
+
if (!health && !isDaemonTooYoung()) {
|
|
550
693
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
551
694
|
`);
|
|
552
695
|
killAndRespawnDaemon();
|
|
553
696
|
const start = Date.now();
|
|
554
|
-
let
|
|
697
|
+
let d = 200;
|
|
555
698
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
556
|
-
await new Promise((r) => setTimeout(r,
|
|
699
|
+
await new Promise((r) => setTimeout(r, d));
|
|
557
700
|
if (await connectToSocket()) break;
|
|
558
|
-
|
|
701
|
+
d = Math.min(d * 2, 3e3);
|
|
559
702
|
}
|
|
560
703
|
if (!_connected) return null;
|
|
561
704
|
}
|
|
562
705
|
}
|
|
563
|
-
const result = await
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
killAndRespawnDaemon();
|
|
569
|
-
const start = Date.now();
|
|
570
|
-
let delay = 200;
|
|
571
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
572
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
573
|
-
if (await connectToSocket()) break;
|
|
574
|
-
delay = Math.min(delay * 2, 3e3);
|
|
575
|
-
}
|
|
576
|
-
if (!_connected) return null;
|
|
577
|
-
const retry = await sendRequest([text], priority);
|
|
578
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
579
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
580
|
-
`);
|
|
581
|
-
}
|
|
582
|
-
return null;
|
|
706
|
+
const result = await retryThenRestart(
|
|
707
|
+
() => sendRequest([text], priority),
|
|
708
|
+
"Embed"
|
|
709
|
+
);
|
|
710
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
583
711
|
}
|
|
584
712
|
function disconnectClient() {
|
|
585
713
|
if (_socket) {
|
|
@@ -625,10 +753,10 @@ async function disposeEmbedder() {
|
|
|
625
753
|
async function embedDirect(text) {
|
|
626
754
|
const llamaCpp = await import("node-llama-cpp");
|
|
627
755
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
628
|
-
const { existsSync:
|
|
629
|
-
const
|
|
630
|
-
const modelPath =
|
|
631
|
-
if (!
|
|
756
|
+
const { existsSync: existsSync5 } = await import("fs");
|
|
757
|
+
const path4 = await import("path");
|
|
758
|
+
const modelPath = path4.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
759
|
+
if (!existsSync5(modelPath)) {
|
|
632
760
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
633
761
|
}
|
|
634
762
|
const llama = await llamaCpp.getLlama();
|
|
@@ -3,9 +3,18 @@ var __esm = (fn, res) => function __init() {
|
|
|
3
3
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
+
// src/lib/secure-files.ts
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
8
|
+
import { chmod, mkdir } from "fs/promises";
|
|
9
|
+
var init_secure_files = __esm({
|
|
10
|
+
"src/lib/secure-files.ts"() {
|
|
11
|
+
"use strict";
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
6
15
|
// src/lib/config.ts
|
|
7
|
-
import { readFile, writeFile
|
|
8
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
16
|
+
import { readFile, writeFile } from "fs/promises";
|
|
17
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
9
18
|
import path from "path";
|
|
10
19
|
import os from "os";
|
|
11
20
|
function resolveDataDir() {
|
|
@@ -13,7 +22,7 @@ function resolveDataDir() {
|
|
|
13
22
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
14
23
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
15
24
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
16
|
-
if (!
|
|
25
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
17
26
|
try {
|
|
18
27
|
renameSync(legacyDir, newDir);
|
|
19
28
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -28,6 +37,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
28
37
|
var init_config = __esm({
|
|
29
38
|
"src/lib/config.ts"() {
|
|
30
39
|
"use strict";
|
|
40
|
+
init_secure_files();
|
|
31
41
|
EXE_AI_DIR = resolveDataDir();
|
|
32
42
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
33
43
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -103,11 +113,27 @@ import { createClient } from "@libsql/client";
|
|
|
103
113
|
// src/lib/employees.ts
|
|
104
114
|
init_config();
|
|
105
115
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
106
|
-
import { existsSync as
|
|
116
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
107
117
|
import { execSync } from "child_process";
|
|
108
118
|
import path2 from "path";
|
|
109
119
|
import os2 from "os";
|
|
110
120
|
var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
121
|
+
var IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
122
|
+
|
|
123
|
+
// src/lib/database-adapter.ts
|
|
124
|
+
import os3 from "os";
|
|
125
|
+
import path3 from "path";
|
|
126
|
+
import { createRequire } from "module";
|
|
127
|
+
import { pathToFileURL } from "url";
|
|
128
|
+
var BOOLEAN_COLUMNS_BY_TABLE = {
|
|
129
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
130
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
131
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
132
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
133
|
+
};
|
|
134
|
+
var BOOLEAN_COLUMN_NAMES = new Set(
|
|
135
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
136
|
+
);
|
|
111
137
|
|
|
112
138
|
// src/lib/platform-procedures.ts
|
|
113
139
|
var PLATFORM_PROCEDURES = [
|