@askexenow/exe-os 0.8.1 → 0.8.2
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/package.json +1 -1
- package/dist/bin/backfill-responses.js +0 -2064
- package/dist/bin/backfill-vectors.js +0 -1771
- package/dist/bin/cleanup-stale-review-tasks.js +0 -1468
- package/dist/bin/cli.js +0 -21371
- package/dist/bin/exe-agent.js +0 -2016
- package/dist/bin/exe-assign.js +0 -2176
- package/dist/bin/exe-boot.js +0 -6332
- package/dist/bin/exe-call.js +0 -341
- package/dist/bin/exe-cloud.js +0 -861
- package/dist/bin/exe-dispatch.js +0 -1159
- package/dist/bin/exe-doctor.js +0 -1786
- package/dist/bin/exe-export-behaviors.js +0 -1637
- package/dist/bin/exe-forget.js +0 -1784
- package/dist/bin/exe-gateway.js +0 -8140
- package/dist/bin/exe-healthcheck.js +0 -207
- package/dist/bin/exe-heartbeat.js +0 -1778
- package/dist/bin/exe-kill.js +0 -1622
- package/dist/bin/exe-launch-agent.js +0 -1847
- package/dist/bin/exe-link.js +0 -192
- package/dist/bin/exe-new-employee.js +0 -1384
- package/dist/bin/exe-pending-messages.js +0 -1576
- package/dist/bin/exe-pending-notifications.js +0 -1450
- package/dist/bin/exe-pending-reviews.js +0 -1598
- package/dist/bin/exe-repo-drift.js +0 -95
- package/dist/bin/exe-review.js +0 -1742
- package/dist/bin/exe-search.js +0 -3116
- package/dist/bin/exe-session-cleanup.js +0 -3360
- package/dist/bin/exe-settings.js +0 -365
- package/dist/bin/exe-status.js +0 -1661
- package/dist/bin/exe-team.js +0 -1453
- package/dist/bin/git-sweep.js +0 -2441
- package/dist/bin/graph-backfill.js +0 -2111
- package/dist/bin/graph-export.js +0 -1747
- package/dist/bin/install.js +0 -661
- package/dist/bin/list-providers.js +0 -140
- package/dist/bin/scan-tasks.js +0 -2039
- package/dist/bin/setup.js +0 -2717
- package/dist/bin/shard-migrate.js +0 -1637
- package/dist/bin/update.js +0 -98
- package/dist/bin/wiki-sync.js +0 -1657
- package/dist/gateway/index.js +0 -9256
- package/dist/hooks/bug-report-worker.js +0 -2903
- package/dist/hooks/commit-complete.js +0 -2364
- package/dist/hooks/error-recall.js +0 -3327
- package/dist/hooks/exe-heartbeat-hook.js +0 -237
- package/dist/hooks/ingest-worker.js +0 -5065
- package/dist/hooks/ingest.js +0 -695
- package/dist/hooks/instructions-loaded.js +0 -2069
- package/dist/hooks/notification.js +0 -1915
- package/dist/hooks/post-compact.js +0 -1940
- package/dist/hooks/pre-compact.js +0 -1935
- package/dist/hooks/pre-tool-use.js +0 -2380
- package/dist/hooks/prompt-ingest-worker.js +0 -2291
- package/dist/hooks/prompt-submit.js +0 -5189
- package/dist/hooks/response-ingest-worker.js +0 -2431
- package/dist/hooks/session-end.js +0 -2196
- package/dist/hooks/session-start.js +0 -3259
- package/dist/hooks/stop.js +0 -2025
- package/dist/hooks/subagent-stop.js +0 -1915
- package/dist/hooks/summary-worker.js +0 -2931
- package/dist/index.js +0 -12327
- package/dist/lib/cloud-sync.js +0 -500
- package/dist/lib/config.js +0 -235
- package/dist/lib/consolidation.js +0 -481
- package/dist/lib/crypto.js +0 -51
- package/dist/lib/database.js +0 -834
- package/dist/lib/device-registry.js +0 -1009
- package/dist/lib/embedder.js +0 -645
- package/dist/lib/employee-templates.js +0 -570
- package/dist/lib/employees.js +0 -182
- package/dist/lib/error-detector.js +0 -156
- package/dist/lib/exe-daemon-client.js +0 -456
- package/dist/lib/exe-daemon.js +0 -8699
- package/dist/lib/file-grep.js +0 -215
- package/dist/lib/hybrid-search.js +0 -3064
- package/dist/lib/identity-templates.js +0 -441
- package/dist/lib/identity.js +0 -228
- package/dist/lib/keychain.js +0 -145
- package/dist/lib/license.js +0 -382
- package/dist/lib/messaging.js +0 -1389
- package/dist/lib/reminders.js +0 -63
- package/dist/lib/schedules.js +0 -1525
- package/dist/lib/session-registry.js +0 -52
- package/dist/lib/skill-learning.js +0 -488
- package/dist/lib/status-brief.js +0 -240
- package/dist/lib/store.js +0 -1740
- package/dist/lib/task-router.js +0 -128
- package/dist/lib/tasks.js +0 -2585
- package/dist/lib/tmux-routing.js +0 -2969
- package/dist/lib/tmux-status.js +0 -261
- package/dist/lib/tmux-transport.js +0 -83
- package/dist/lib/transport.js +0 -128
- package/dist/lib/ws-auth.js +0 -19
- package/dist/lib/ws-client.js +0 -160
- package/dist/mcp/server.js +0 -10812
- package/dist/mcp/tools/complete-reminder.js +0 -67
- package/dist/mcp/tools/create-reminder.js +0 -52
- package/dist/mcp/tools/create-task.js +0 -1903
- package/dist/mcp/tools/deactivate-behavior.js +0 -268
- package/dist/mcp/tools/list-reminders.js +0 -62
- package/dist/mcp/tools/list-tasks.js +0 -491
- package/dist/mcp/tools/send-message.js +0 -1395
- package/dist/mcp/tools/update-task.js +0 -1807
- package/dist/runtime/index.js +0 -7201
- package/dist/tui/App.js +0 -17874
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
// src/lib/exe-daemon-client.ts
|
|
2
|
-
import net from "net";
|
|
3
|
-
import { spawn } from "child_process";
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
5
|
-
import { existsSync as existsSync2, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
|
|
6
|
-
import path2 from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
|
|
9
|
-
// src/lib/config.ts
|
|
10
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
11
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
12
|
-
import path from "path";
|
|
13
|
-
import os from "os";
|
|
14
|
-
function resolveDataDir() {
|
|
15
|
-
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
16
|
-
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
17
|
-
const newDir = path.join(os.homedir(), ".exe-os");
|
|
18
|
-
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
19
|
-
if (!existsSync(newDir) && existsSync(legacyDir)) {
|
|
20
|
-
try {
|
|
21
|
-
renameSync(legacyDir, newDir);
|
|
22
|
-
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
23
|
-
`);
|
|
24
|
-
} catch {
|
|
25
|
-
return legacyDir;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return newDir;
|
|
29
|
-
}
|
|
30
|
-
var EXE_AI_DIR = resolveDataDir();
|
|
31
|
-
var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
32
|
-
var MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
33
|
-
var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
34
|
-
var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
35
|
-
var CURRENT_CONFIG_VERSION = 1;
|
|
36
|
-
var DEFAULT_CONFIG = {
|
|
37
|
-
config_version: CURRENT_CONFIG_VERSION,
|
|
38
|
-
dbPath: DB_PATH,
|
|
39
|
-
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
40
|
-
embeddingDim: 1024,
|
|
41
|
-
batchSize: 20,
|
|
42
|
-
flushIntervalMs: 1e4,
|
|
43
|
-
autoIngestion: true,
|
|
44
|
-
autoRetrieval: true,
|
|
45
|
-
searchMode: "hybrid",
|
|
46
|
-
hookSearchMode: "hybrid",
|
|
47
|
-
fileGrepEnabled: true,
|
|
48
|
-
splashEffect: true,
|
|
49
|
-
consolidationEnabled: true,
|
|
50
|
-
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
51
|
-
consolidationModel: "claude-haiku-4-5-20251001",
|
|
52
|
-
consolidationMaxCallsPerRun: 20,
|
|
53
|
-
selfQueryRouter: true,
|
|
54
|
-
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
55
|
-
rerankerEnabled: true,
|
|
56
|
-
scalingRoadmap: {
|
|
57
|
-
rerankerAutoTrigger: {
|
|
58
|
-
enabled: true,
|
|
59
|
-
broadQueryMinCardinality: 5e4,
|
|
60
|
-
fetchTopK: 150,
|
|
61
|
-
returnTopK: 5
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
graphRagEnabled: true,
|
|
65
|
-
wikiEnabled: false,
|
|
66
|
-
wikiUrl: "",
|
|
67
|
-
wikiApiKey: "",
|
|
68
|
-
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
69
|
-
wikiWorkspaceMapping: {
|
|
70
|
-
exe: "Executive",
|
|
71
|
-
yoshi: "Engineering",
|
|
72
|
-
mari: "Marketing",
|
|
73
|
-
tom: "Engineering",
|
|
74
|
-
sasha: "Production"
|
|
75
|
-
},
|
|
76
|
-
wikiAutoUpdate: true,
|
|
77
|
-
wikiAutoUpdateThreshold: 0.5,
|
|
78
|
-
wikiAutoUpdateCreateNew: true,
|
|
79
|
-
skillLearning: true,
|
|
80
|
-
skillThreshold: 3,
|
|
81
|
-
skillModel: "claude-haiku-4-5-20251001",
|
|
82
|
-
exeHeartbeat: {
|
|
83
|
-
enabled: true,
|
|
84
|
-
intervalSeconds: 60,
|
|
85
|
-
staleInProgressThresholdHours: 2
|
|
86
|
-
},
|
|
87
|
-
sessionLifecycle: {
|
|
88
|
-
idleKillEnabled: true,
|
|
89
|
-
idleKillTicksRequired: 3,
|
|
90
|
-
idleKillIntercomAckWindowMs: 1e4,
|
|
91
|
-
maxAutoInstances: 10
|
|
92
|
-
},
|
|
93
|
-
autoUpdate: {
|
|
94
|
-
checkOnBoot: true,
|
|
95
|
-
autoInstall: false,
|
|
96
|
-
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// src/lib/exe-daemon-client.ts
|
|
101
|
-
var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path2.join(EXE_AI_DIR, "exed.sock");
|
|
102
|
-
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path2.join(EXE_AI_DIR, "exed.pid");
|
|
103
|
-
var SPAWN_LOCK_PATH = path2.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
104
|
-
var SPAWN_LOCK_STALE_MS = 3e4;
|
|
105
|
-
var CONNECT_TIMEOUT_MS = 15e3;
|
|
106
|
-
var REQUEST_TIMEOUT_MS = 3e4;
|
|
107
|
-
var _socket = null;
|
|
108
|
-
var _connected = false;
|
|
109
|
-
var _buffer = "";
|
|
110
|
-
var _requestCount = 0;
|
|
111
|
-
var HEALTH_CHECK_INTERVAL = 100;
|
|
112
|
-
var _pending = /* @__PURE__ */ new Map();
|
|
113
|
-
function handleData(chunk) {
|
|
114
|
-
_buffer += chunk.toString();
|
|
115
|
-
let newlineIdx;
|
|
116
|
-
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
117
|
-
const line = _buffer.slice(0, newlineIdx).trim();
|
|
118
|
-
_buffer = _buffer.slice(newlineIdx + 1);
|
|
119
|
-
if (!line) continue;
|
|
120
|
-
try {
|
|
121
|
-
const response = JSON.parse(line);
|
|
122
|
-
const entry = _pending.get(response.id);
|
|
123
|
-
if (entry) {
|
|
124
|
-
clearTimeout(entry.timer);
|
|
125
|
-
_pending.delete(response.id);
|
|
126
|
-
entry.resolve(response);
|
|
127
|
-
}
|
|
128
|
-
} catch {
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
function cleanupStaleFiles() {
|
|
133
|
-
if (existsSync2(PID_PATH)) {
|
|
134
|
-
try {
|
|
135
|
-
const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
|
|
136
|
-
if (pid > 0) {
|
|
137
|
-
try {
|
|
138
|
-
process.kill(pid, 0);
|
|
139
|
-
return;
|
|
140
|
-
} catch {
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
} catch {
|
|
144
|
-
}
|
|
145
|
-
try {
|
|
146
|
-
unlinkSync(PID_PATH);
|
|
147
|
-
} catch {
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
unlinkSync(SOCKET_PATH);
|
|
151
|
-
} catch {
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function findPackageRoot() {
|
|
156
|
-
let dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
157
|
-
const { root } = path2.parse(dir);
|
|
158
|
-
while (dir !== root) {
|
|
159
|
-
if (existsSync2(path2.join(dir, "package.json"))) return dir;
|
|
160
|
-
dir = path2.dirname(dir);
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
function spawnDaemon() {
|
|
165
|
-
const pkgRoot = findPackageRoot();
|
|
166
|
-
if (!pkgRoot) {
|
|
167
|
-
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const daemonPath = path2.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
171
|
-
if (!existsSync2(daemonPath)) {
|
|
172
|
-
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
173
|
-
`);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
const resolvedPath = daemonPath;
|
|
177
|
-
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
178
|
-
`);
|
|
179
|
-
const logPath = path2.join(path2.dirname(SOCKET_PATH), "exed.log");
|
|
180
|
-
let stderrFd = "ignore";
|
|
181
|
-
try {
|
|
182
|
-
stderrFd = openSync(logPath, "a");
|
|
183
|
-
} catch {
|
|
184
|
-
}
|
|
185
|
-
const child = spawn(process.execPath, [resolvedPath], {
|
|
186
|
-
detached: true,
|
|
187
|
-
stdio: ["ignore", "ignore", stderrFd],
|
|
188
|
-
env: {
|
|
189
|
-
...process.env,
|
|
190
|
-
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
191
|
-
EXE_DAEMON_PID: PID_PATH
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
child.unref();
|
|
195
|
-
if (typeof stderrFd === "number") {
|
|
196
|
-
try {
|
|
197
|
-
closeSync(stderrFd);
|
|
198
|
-
} catch {
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function acquireSpawnLock() {
|
|
203
|
-
try {
|
|
204
|
-
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
205
|
-
closeSync(fd);
|
|
206
|
-
return true;
|
|
207
|
-
} catch {
|
|
208
|
-
try {
|
|
209
|
-
const stat = statSync(SPAWN_LOCK_PATH);
|
|
210
|
-
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
211
|
-
try {
|
|
212
|
-
unlinkSync(SPAWN_LOCK_PATH);
|
|
213
|
-
} catch {
|
|
214
|
-
}
|
|
215
|
-
try {
|
|
216
|
-
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
217
|
-
closeSync(fd);
|
|
218
|
-
return true;
|
|
219
|
-
} catch {
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
} catch {
|
|
223
|
-
}
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function releaseSpawnLock() {
|
|
228
|
-
try {
|
|
229
|
-
unlinkSync(SPAWN_LOCK_PATH);
|
|
230
|
-
} catch {
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function connectToSocket() {
|
|
234
|
-
return new Promise((resolve) => {
|
|
235
|
-
if (_socket && _connected) {
|
|
236
|
-
resolve(true);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
240
|
-
const connectTimeout = setTimeout(() => {
|
|
241
|
-
socket.destroy();
|
|
242
|
-
resolve(false);
|
|
243
|
-
}, 2e3);
|
|
244
|
-
socket.on("connect", () => {
|
|
245
|
-
clearTimeout(connectTimeout);
|
|
246
|
-
_socket = socket;
|
|
247
|
-
_connected = true;
|
|
248
|
-
_buffer = "";
|
|
249
|
-
socket.on("data", handleData);
|
|
250
|
-
socket.on("close", () => {
|
|
251
|
-
_connected = false;
|
|
252
|
-
_socket = null;
|
|
253
|
-
for (const [id, entry] of _pending) {
|
|
254
|
-
clearTimeout(entry.timer);
|
|
255
|
-
_pending.delete(id);
|
|
256
|
-
entry.resolve({ error: "Connection closed" });
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
socket.on("error", () => {
|
|
260
|
-
_connected = false;
|
|
261
|
-
_socket = null;
|
|
262
|
-
});
|
|
263
|
-
resolve(true);
|
|
264
|
-
});
|
|
265
|
-
socket.on("error", () => {
|
|
266
|
-
clearTimeout(connectTimeout);
|
|
267
|
-
resolve(false);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
async function connectEmbedDaemon() {
|
|
272
|
-
if (_socket && _connected) return true;
|
|
273
|
-
if (await connectToSocket()) return true;
|
|
274
|
-
if (acquireSpawnLock()) {
|
|
275
|
-
try {
|
|
276
|
-
cleanupStaleFiles();
|
|
277
|
-
spawnDaemon();
|
|
278
|
-
} finally {
|
|
279
|
-
releaseSpawnLock();
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
const start = Date.now();
|
|
283
|
-
let delay = 100;
|
|
284
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
285
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
286
|
-
if (await connectToSocket()) return true;
|
|
287
|
-
delay = Math.min(delay * 2, 3e3);
|
|
288
|
-
}
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
function sendRequest(texts, priority) {
|
|
292
|
-
return new Promise((resolve) => {
|
|
293
|
-
if (!_socket || !_connected) {
|
|
294
|
-
resolve({ error: "Not connected" });
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
const id = randomUUID();
|
|
298
|
-
const timer = setTimeout(() => {
|
|
299
|
-
_pending.delete(id);
|
|
300
|
-
resolve({ error: "Request timeout" });
|
|
301
|
-
}, REQUEST_TIMEOUT_MS);
|
|
302
|
-
_pending.set(id, { resolve, timer });
|
|
303
|
-
try {
|
|
304
|
-
_socket.write(JSON.stringify({ id, texts, priority }) + "\n");
|
|
305
|
-
} catch {
|
|
306
|
-
clearTimeout(timer);
|
|
307
|
-
_pending.delete(id);
|
|
308
|
-
resolve({ error: "Write failed" });
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
async function pingDaemon() {
|
|
313
|
-
if (!_socket || !_connected) return null;
|
|
314
|
-
return new Promise((resolve) => {
|
|
315
|
-
const id = randomUUID();
|
|
316
|
-
const timer = setTimeout(() => {
|
|
317
|
-
_pending.delete(id);
|
|
318
|
-
resolve(null);
|
|
319
|
-
}, 5e3);
|
|
320
|
-
_pending.set(id, {
|
|
321
|
-
resolve: (data) => {
|
|
322
|
-
if (data.health) {
|
|
323
|
-
resolve(data.health);
|
|
324
|
-
} else {
|
|
325
|
-
resolve(null);
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
timer
|
|
329
|
-
});
|
|
330
|
-
try {
|
|
331
|
-
_socket.write(JSON.stringify({ id, type: "health" }) + "\n");
|
|
332
|
-
} catch {
|
|
333
|
-
clearTimeout(timer);
|
|
334
|
-
_pending.delete(id);
|
|
335
|
-
resolve(null);
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
function killAndRespawnDaemon() {
|
|
340
|
-
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
341
|
-
if (existsSync2(PID_PATH)) {
|
|
342
|
-
try {
|
|
343
|
-
const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
|
|
344
|
-
if (pid > 0) {
|
|
345
|
-
try {
|
|
346
|
-
process.kill(pid, "SIGKILL");
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
} catch {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (_socket) {
|
|
354
|
-
_socket.destroy();
|
|
355
|
-
_socket = null;
|
|
356
|
-
}
|
|
357
|
-
_connected = false;
|
|
358
|
-
_buffer = "";
|
|
359
|
-
try {
|
|
360
|
-
unlinkSync(PID_PATH);
|
|
361
|
-
} catch {
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
unlinkSync(SOCKET_PATH);
|
|
365
|
-
} catch {
|
|
366
|
-
}
|
|
367
|
-
spawnDaemon();
|
|
368
|
-
}
|
|
369
|
-
async function embedViaClient(text, priority = "high") {
|
|
370
|
-
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
371
|
-
_requestCount++;
|
|
372
|
-
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
373
|
-
const health = await pingDaemon();
|
|
374
|
-
if (!health) {
|
|
375
|
-
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
376
|
-
`);
|
|
377
|
-
killAndRespawnDaemon();
|
|
378
|
-
const start = Date.now();
|
|
379
|
-
let delay = 200;
|
|
380
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
381
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
382
|
-
if (await connectToSocket()) break;
|
|
383
|
-
delay = Math.min(delay * 2, 3e3);
|
|
384
|
-
}
|
|
385
|
-
if (!_connected) return null;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
const result = await sendRequest([text], priority);
|
|
389
|
-
if (!result.error && result.vectors?.[0]) return result.vectors[0];
|
|
390
|
-
if (result.error) {
|
|
391
|
-
process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
|
|
392
|
-
`);
|
|
393
|
-
killAndRespawnDaemon();
|
|
394
|
-
const start = Date.now();
|
|
395
|
-
let delay = 200;
|
|
396
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
397
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
398
|
-
if (await connectToSocket()) break;
|
|
399
|
-
delay = Math.min(delay * 2, 3e3);
|
|
400
|
-
}
|
|
401
|
-
if (!_connected) return null;
|
|
402
|
-
const retry = await sendRequest([text], priority);
|
|
403
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
404
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
405
|
-
`);
|
|
406
|
-
}
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
async function embedBatchViaClient(texts, priority = "high") {
|
|
410
|
-
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
411
|
-
_requestCount++;
|
|
412
|
-
const result = await sendRequest(texts, priority);
|
|
413
|
-
if (!result.error && result.vectors) return result.vectors;
|
|
414
|
-
if (result.error) {
|
|
415
|
-
process.stderr.write(`[exed-client] Batch embed failed (${result.error}) \u2014 attempting restart
|
|
416
|
-
`);
|
|
417
|
-
killAndRespawnDaemon();
|
|
418
|
-
const start = Date.now();
|
|
419
|
-
let delay = 200;
|
|
420
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
421
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
422
|
-
if (await connectToSocket()) break;
|
|
423
|
-
delay = Math.min(delay * 2, 3e3);
|
|
424
|
-
}
|
|
425
|
-
if (!_connected) return null;
|
|
426
|
-
const retry = await sendRequest(texts, priority);
|
|
427
|
-
if (!retry.error && retry.vectors) return retry.vectors;
|
|
428
|
-
process.stderr.write(`[exed-client] Batch retry also failed: ${retry.error ?? "no vectors"}
|
|
429
|
-
`);
|
|
430
|
-
}
|
|
431
|
-
return null;
|
|
432
|
-
}
|
|
433
|
-
function disconnectClient() {
|
|
434
|
-
if (_socket) {
|
|
435
|
-
_socket.destroy();
|
|
436
|
-
_socket = null;
|
|
437
|
-
}
|
|
438
|
-
_connected = false;
|
|
439
|
-
_buffer = "";
|
|
440
|
-
for (const [id, entry] of _pending) {
|
|
441
|
-
clearTimeout(entry.timer);
|
|
442
|
-
_pending.delete(id);
|
|
443
|
-
entry.resolve({ error: "Client disconnected" });
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
function isClientConnected() {
|
|
447
|
-
return _connected;
|
|
448
|
-
}
|
|
449
|
-
export {
|
|
450
|
-
connectEmbedDaemon,
|
|
451
|
-
disconnectClient,
|
|
452
|
-
embedBatchViaClient,
|
|
453
|
-
embedViaClient,
|
|
454
|
-
isClientConnected,
|
|
455
|
-
pingDaemon
|
|
456
|
-
};
|