@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
|
@@ -70,9 +70,47 @@ var init_db_retry = __esm({
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
// src/lib/secure-files.ts
|
|
74
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
75
|
+
import { chmod, mkdir } from "fs/promises";
|
|
76
|
+
async function ensurePrivateDir(dirPath) {
|
|
77
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
78
|
+
try {
|
|
79
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function ensurePrivateDirSync(dirPath) {
|
|
84
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
85
|
+
try {
|
|
86
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function enforcePrivateFile(filePath) {
|
|
91
|
+
try {
|
|
92
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function enforcePrivateFileSync(filePath) {
|
|
97
|
+
try {
|
|
98
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
103
|
+
var init_secure_files = __esm({
|
|
104
|
+
"src/lib/secure-files.ts"() {
|
|
105
|
+
"use strict";
|
|
106
|
+
PRIVATE_DIR_MODE = 448;
|
|
107
|
+
PRIVATE_FILE_MODE = 384;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
73
111
|
// src/lib/config.ts
|
|
74
|
-
import { readFile, writeFile
|
|
75
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
112
|
+
import { readFile, writeFile } from "fs/promises";
|
|
113
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
76
114
|
import path from "path";
|
|
77
115
|
import os from "os";
|
|
78
116
|
function resolveDataDir() {
|
|
@@ -80,7 +118,7 @@ function resolveDataDir() {
|
|
|
80
118
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
81
119
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
82
120
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
83
|
-
if (!
|
|
121
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
84
122
|
try {
|
|
85
123
|
renameSync(legacyDir, newDir);
|
|
86
124
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -143,9 +181,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
143
181
|
}
|
|
144
182
|
async function loadConfig() {
|
|
145
183
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
146
|
-
await
|
|
184
|
+
await ensurePrivateDir(dir);
|
|
147
185
|
const configPath = path.join(dir, "config.json");
|
|
148
|
-
if (!
|
|
186
|
+
if (!existsSync2(configPath)) {
|
|
149
187
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
150
188
|
}
|
|
151
189
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -158,6 +196,7 @@ async function loadConfig() {
|
|
|
158
196
|
`);
|
|
159
197
|
try {
|
|
160
198
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
199
|
+
await enforcePrivateFile(configPath);
|
|
161
200
|
} catch {
|
|
162
201
|
}
|
|
163
202
|
}
|
|
@@ -177,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
177
216
|
var init_config = __esm({
|
|
178
217
|
"src/lib/config.ts"() {
|
|
179
218
|
"use strict";
|
|
219
|
+
init_secure_files();
|
|
180
220
|
EXE_AI_DIR = resolveDataDir();
|
|
181
221
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
182
222
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -255,7 +295,7 @@ var init_config = __esm({
|
|
|
255
295
|
|
|
256
296
|
// src/lib/employees.ts
|
|
257
297
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
258
|
-
import { existsSync as
|
|
298
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
259
299
|
import { execSync } from "child_process";
|
|
260
300
|
import path2 from "path";
|
|
261
301
|
import os2 from "os";
|
|
@@ -272,7 +312,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
272
312
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
273
313
|
}
|
|
274
314
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
275
|
-
if (!
|
|
315
|
+
if (!existsSync3(employeesPath)) return [];
|
|
276
316
|
try {
|
|
277
317
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
278
318
|
} catch {
|
|
@@ -1217,6 +1257,7 @@ async function ensureSchema() {
|
|
|
1217
1257
|
project TEXT NOT NULL,
|
|
1218
1258
|
summary TEXT NOT NULL,
|
|
1219
1259
|
task_file TEXT,
|
|
1260
|
+
session_scope TEXT,
|
|
1220
1261
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1221
1262
|
created_at TEXT NOT NULL
|
|
1222
1263
|
);
|
|
@@ -1225,7 +1266,7 @@ async function ensureSchema() {
|
|
|
1225
1266
|
ON notifications(read);
|
|
1226
1267
|
|
|
1227
1268
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1228
|
-
ON notifications(agent_id);
|
|
1269
|
+
ON notifications(agent_id, session_scope);
|
|
1229
1270
|
|
|
1230
1271
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1231
1272
|
ON notifications(task_file);
|
|
@@ -1263,6 +1304,7 @@ async function ensureSchema() {
|
|
|
1263
1304
|
target_agent TEXT NOT NULL,
|
|
1264
1305
|
target_project TEXT,
|
|
1265
1306
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1307
|
+
session_scope TEXT,
|
|
1266
1308
|
content TEXT NOT NULL,
|
|
1267
1309
|
priority TEXT DEFAULT 'normal',
|
|
1268
1310
|
status TEXT DEFAULT 'pending',
|
|
@@ -1276,10 +1318,31 @@ async function ensureSchema() {
|
|
|
1276
1318
|
);
|
|
1277
1319
|
|
|
1278
1320
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1279
|
-
ON messages(target_agent, status);
|
|
1321
|
+
ON messages(target_agent, session_scope, status);
|
|
1280
1322
|
|
|
1281
1323
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1282
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1324
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1325
|
+
`);
|
|
1326
|
+
try {
|
|
1327
|
+
await client.execute({
|
|
1328
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1329
|
+
args: []
|
|
1330
|
+
});
|
|
1331
|
+
} catch {
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
await client.execute({
|
|
1335
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1336
|
+
args: []
|
|
1337
|
+
});
|
|
1338
|
+
} catch {
|
|
1339
|
+
}
|
|
1340
|
+
await client.executeMultiple(`
|
|
1341
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1342
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1343
|
+
|
|
1344
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1345
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1283
1346
|
`);
|
|
1284
1347
|
try {
|
|
1285
1348
|
await client.execute({
|
|
@@ -1863,6 +1926,13 @@ async function ensureSchema() {
|
|
|
1863
1926
|
} catch {
|
|
1864
1927
|
}
|
|
1865
1928
|
}
|
|
1929
|
+
try {
|
|
1930
|
+
await client.execute({
|
|
1931
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1932
|
+
args: []
|
|
1933
|
+
});
|
|
1934
|
+
} catch {
|
|
1935
|
+
}
|
|
1866
1936
|
}
|
|
1867
1937
|
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1868
1938
|
var init_database = __esm({
|
|
@@ -1885,6 +1955,7 @@ var shard_manager_exports = {};
|
|
|
1885
1955
|
__export(shard_manager_exports, {
|
|
1886
1956
|
disposeShards: () => disposeShards,
|
|
1887
1957
|
ensureShardSchema: () => ensureShardSchema,
|
|
1958
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1888
1959
|
getReadyShardClient: () => getReadyShardClient,
|
|
1889
1960
|
getShardClient: () => getShardClient,
|
|
1890
1961
|
getShardsDir: () => getShardsDir,
|
|
@@ -1894,14 +1965,17 @@ __export(shard_manager_exports, {
|
|
|
1894
1965
|
shardExists: () => shardExists
|
|
1895
1966
|
});
|
|
1896
1967
|
import path5 from "path";
|
|
1897
|
-
import { existsSync as
|
|
1968
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1898
1969
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1899
1970
|
function initShardManager(encryptionKey) {
|
|
1900
1971
|
_encryptionKey = encryptionKey;
|
|
1901
|
-
if (!
|
|
1902
|
-
|
|
1972
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
1973
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1903
1974
|
}
|
|
1904
1975
|
_shardingEnabled = true;
|
|
1976
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
1977
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
1978
|
+
_evictionTimer.unref();
|
|
1905
1979
|
}
|
|
1906
1980
|
function isShardingEnabled() {
|
|
1907
1981
|
return _shardingEnabled;
|
|
@@ -1918,21 +1992,28 @@ function getShardClient(projectName) {
|
|
|
1918
1992
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1919
1993
|
}
|
|
1920
1994
|
const cached = _shards.get(safeName);
|
|
1921
|
-
if (cached)
|
|
1995
|
+
if (cached) {
|
|
1996
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1997
|
+
return cached;
|
|
1998
|
+
}
|
|
1999
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2000
|
+
evictLRU();
|
|
2001
|
+
}
|
|
1922
2002
|
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1923
2003
|
const client = createClient2({
|
|
1924
2004
|
url: `file:${dbPath}`,
|
|
1925
2005
|
encryptionKey: _encryptionKey
|
|
1926
2006
|
});
|
|
1927
2007
|
_shards.set(safeName, client);
|
|
2008
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1928
2009
|
return client;
|
|
1929
2010
|
}
|
|
1930
2011
|
function shardExists(projectName) {
|
|
1931
2012
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1932
|
-
return
|
|
2013
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1933
2014
|
}
|
|
1934
2015
|
function listShards() {
|
|
1935
|
-
if (!
|
|
2016
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1936
2017
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1937
2018
|
}
|
|
1938
2019
|
async function ensureShardSchema(client) {
|
|
@@ -1984,6 +2065,8 @@ async function ensureShardSchema(client) {
|
|
|
1984
2065
|
for (const col of [
|
|
1985
2066
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
1986
2067
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2068
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2069
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
1987
2070
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
1988
2071
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
1989
2072
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2121,21 +2204,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2121
2204
|
await ensureShardSchema(client);
|
|
2122
2205
|
return client;
|
|
2123
2206
|
}
|
|
2207
|
+
function evictLRU() {
|
|
2208
|
+
let oldest = null;
|
|
2209
|
+
let oldestTime = Infinity;
|
|
2210
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2211
|
+
if (time < oldestTime) {
|
|
2212
|
+
oldestTime = time;
|
|
2213
|
+
oldest = name;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
if (oldest) {
|
|
2217
|
+
const client = _shards.get(oldest);
|
|
2218
|
+
if (client) {
|
|
2219
|
+
client.close();
|
|
2220
|
+
}
|
|
2221
|
+
_shards.delete(oldest);
|
|
2222
|
+
_shardLastAccess.delete(oldest);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function evictIdleShards() {
|
|
2226
|
+
const now = Date.now();
|
|
2227
|
+
const toEvict = [];
|
|
2228
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2229
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2230
|
+
toEvict.push(name);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
for (const name of toEvict) {
|
|
2234
|
+
const client = _shards.get(name);
|
|
2235
|
+
if (client) {
|
|
2236
|
+
client.close();
|
|
2237
|
+
}
|
|
2238
|
+
_shards.delete(name);
|
|
2239
|
+
_shardLastAccess.delete(name);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
function getOpenShardCount() {
|
|
2243
|
+
return _shards.size;
|
|
2244
|
+
}
|
|
2124
2245
|
function disposeShards() {
|
|
2246
|
+
if (_evictionTimer) {
|
|
2247
|
+
clearInterval(_evictionTimer);
|
|
2248
|
+
_evictionTimer = null;
|
|
2249
|
+
}
|
|
2125
2250
|
for (const [, client] of _shards) {
|
|
2126
2251
|
client.close();
|
|
2127
2252
|
}
|
|
2128
2253
|
_shards.clear();
|
|
2254
|
+
_shardLastAccess.clear();
|
|
2129
2255
|
_shardingEnabled = false;
|
|
2130
2256
|
_encryptionKey = null;
|
|
2131
2257
|
}
|
|
2132
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2258
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2133
2259
|
var init_shard_manager = __esm({
|
|
2134
2260
|
"src/lib/shard-manager.ts"() {
|
|
2135
2261
|
"use strict";
|
|
2136
2262
|
init_config();
|
|
2137
2263
|
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
2264
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2265
|
+
MAX_OPEN_SHARDS = 10;
|
|
2266
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2138
2267
|
_shards = /* @__PURE__ */ new Map();
|
|
2268
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2269
|
+
_evictionTimer = null;
|
|
2139
2270
|
_encryptionKey = null;
|
|
2140
2271
|
_shardingEnabled = false;
|
|
2141
2272
|
}
|
|
@@ -2328,13 +2459,50 @@ ${p.content}`).join("\n\n");
|
|
|
2328
2459
|
}
|
|
2329
2460
|
});
|
|
2330
2461
|
|
|
2462
|
+
// src/lib/daemon-auth.ts
|
|
2463
|
+
import crypto from "crypto";
|
|
2464
|
+
import path6 from "path";
|
|
2465
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
2466
|
+
function normalizeToken(token) {
|
|
2467
|
+
if (!token) return null;
|
|
2468
|
+
const trimmed = token.trim();
|
|
2469
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2470
|
+
}
|
|
2471
|
+
function readDaemonToken() {
|
|
2472
|
+
try {
|
|
2473
|
+
if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
|
|
2474
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
2475
|
+
} catch {
|
|
2476
|
+
return null;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
function ensureDaemonToken(seed) {
|
|
2480
|
+
const existing = readDaemonToken();
|
|
2481
|
+
if (existing) return existing;
|
|
2482
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
2483
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2484
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
2485
|
+
`, "utf8");
|
|
2486
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2487
|
+
return token;
|
|
2488
|
+
}
|
|
2489
|
+
var DAEMON_TOKEN_PATH;
|
|
2490
|
+
var init_daemon_auth = __esm({
|
|
2491
|
+
"src/lib/daemon-auth.ts"() {
|
|
2492
|
+
"use strict";
|
|
2493
|
+
init_config();
|
|
2494
|
+
init_secure_files();
|
|
2495
|
+
DAEMON_TOKEN_PATH = path6.join(EXE_AI_DIR, "exed.token");
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
|
|
2331
2499
|
// src/lib/exe-daemon-client.ts
|
|
2332
2500
|
import net from "net";
|
|
2333
2501
|
import os5 from "os";
|
|
2334
2502
|
import { spawn } from "child_process";
|
|
2335
2503
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2336
|
-
import { existsSync as
|
|
2337
|
-
import
|
|
2504
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
2505
|
+
import path7 from "path";
|
|
2338
2506
|
import { fileURLToPath } from "url";
|
|
2339
2507
|
function handleData(chunk) {
|
|
2340
2508
|
_buffer += chunk.toString();
|
|
@@ -2362,9 +2530,9 @@ function handleData(chunk) {
|
|
|
2362
2530
|
}
|
|
2363
2531
|
}
|
|
2364
2532
|
function cleanupStaleFiles() {
|
|
2365
|
-
if (
|
|
2533
|
+
if (existsSync7(PID_PATH)) {
|
|
2366
2534
|
try {
|
|
2367
|
-
const pid = parseInt(
|
|
2535
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2368
2536
|
if (pid > 0) {
|
|
2369
2537
|
try {
|
|
2370
2538
|
process.kill(pid, 0);
|
|
@@ -2385,11 +2553,11 @@ function cleanupStaleFiles() {
|
|
|
2385
2553
|
}
|
|
2386
2554
|
}
|
|
2387
2555
|
function findPackageRoot() {
|
|
2388
|
-
let dir =
|
|
2389
|
-
const { root } =
|
|
2556
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
2557
|
+
const { root } = path7.parse(dir);
|
|
2390
2558
|
while (dir !== root) {
|
|
2391
|
-
if (
|
|
2392
|
-
dir =
|
|
2559
|
+
if (existsSync7(path7.join(dir, "package.json"))) return dir;
|
|
2560
|
+
dir = path7.dirname(dir);
|
|
2393
2561
|
}
|
|
2394
2562
|
return null;
|
|
2395
2563
|
}
|
|
@@ -2415,16 +2583,17 @@ function spawnDaemon() {
|
|
|
2415
2583
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2416
2584
|
return;
|
|
2417
2585
|
}
|
|
2418
|
-
const daemonPath =
|
|
2419
|
-
if (!
|
|
2586
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2587
|
+
if (!existsSync7(daemonPath)) {
|
|
2420
2588
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2421
2589
|
`);
|
|
2422
2590
|
return;
|
|
2423
2591
|
}
|
|
2424
2592
|
const resolvedPath = daemonPath;
|
|
2593
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2425
2594
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2426
2595
|
`);
|
|
2427
|
-
const logPath =
|
|
2596
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
2428
2597
|
let stderrFd = "ignore";
|
|
2429
2598
|
try {
|
|
2430
2599
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2442,7 +2611,8 @@ function spawnDaemon() {
|
|
|
2442
2611
|
TMUX_PANE: void 0,
|
|
2443
2612
|
// Prevents resolveExeSession() from scoping to one session
|
|
2444
2613
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2445
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2614
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2615
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2446
2616
|
}
|
|
2447
2617
|
});
|
|
2448
2618
|
child.unref();
|
|
@@ -2552,13 +2722,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2552
2722
|
return;
|
|
2553
2723
|
}
|
|
2554
2724
|
const id = randomUUID2();
|
|
2725
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2555
2726
|
const timer = setTimeout(() => {
|
|
2556
2727
|
_pending.delete(id);
|
|
2557
2728
|
resolve({ error: "Request timeout" });
|
|
2558
2729
|
}, timeoutMs);
|
|
2559
2730
|
_pending.set(id, { resolve, timer });
|
|
2560
2731
|
try {
|
|
2561
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2732
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2562
2733
|
} catch {
|
|
2563
2734
|
clearTimeout(timer);
|
|
2564
2735
|
_pending.delete(id);
|
|
@@ -2587,9 +2758,9 @@ function killAndRespawnDaemon() {
|
|
|
2587
2758
|
}
|
|
2588
2759
|
try {
|
|
2589
2760
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2590
|
-
if (
|
|
2761
|
+
if (existsSync7(PID_PATH)) {
|
|
2591
2762
|
try {
|
|
2592
|
-
const pid = parseInt(
|
|
2763
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2593
2764
|
if (pid > 0) {
|
|
2594
2765
|
try {
|
|
2595
2766
|
process.kill(pid, "SIGKILL");
|
|
@@ -2693,17 +2864,19 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2693
2864
|
);
|
|
2694
2865
|
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2695
2866
|
}
|
|
2696
|
-
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;
|
|
2867
|
+
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;
|
|
2697
2868
|
var init_exe_daemon_client = __esm({
|
|
2698
2869
|
"src/lib/exe-daemon-client.ts"() {
|
|
2699
2870
|
"use strict";
|
|
2700
2871
|
init_config();
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2872
|
+
init_daemon_auth();
|
|
2873
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
2874
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
2875
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2704
2876
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2705
2877
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2706
2878
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2879
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2707
2880
|
_socket = null;
|
|
2708
2881
|
_connected = false;
|
|
2709
2882
|
_buffer = "";
|
|
@@ -2724,7 +2897,7 @@ init_database();
|
|
|
2724
2897
|
|
|
2725
2898
|
// src/lib/keychain.ts
|
|
2726
2899
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2727
|
-
import { existsSync as
|
|
2900
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2728
2901
|
import path4 from "path";
|
|
2729
2902
|
import os4 from "os";
|
|
2730
2903
|
var SERVICE = "exe-mem";
|
|
@@ -2754,7 +2927,7 @@ async function getMasterKey() {
|
|
|
2754
2927
|
}
|
|
2755
2928
|
}
|
|
2756
2929
|
const keyPath = getKeyPath();
|
|
2757
|
-
if (!
|
|
2930
|
+
if (!existsSync4(keyPath)) {
|
|
2758
2931
|
process.stderr.write(
|
|
2759
2932
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2760
2933
|
`
|
|
@@ -2930,32 +3103,32 @@ function isMainModule(importMetaUrl) {
|
|
|
2930
3103
|
}
|
|
2931
3104
|
|
|
2932
3105
|
// src/bin/backfill-vectors.ts
|
|
2933
|
-
import { existsSync as
|
|
2934
|
-
import
|
|
3106
|
+
import { existsSync as existsSync9, unlinkSync as unlinkSync4 } from "fs";
|
|
3107
|
+
import path9 from "path";
|
|
2935
3108
|
|
|
2936
3109
|
// src/lib/worker-gate.ts
|
|
2937
3110
|
init_config();
|
|
2938
|
-
import { readdirSync as readdirSync2, writeFileSync as
|
|
2939
|
-
import
|
|
2940
|
-
var WORKER_PID_DIR =
|
|
3111
|
+
import { readdirSync as readdirSync2, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
3112
|
+
import path8 from "path";
|
|
3113
|
+
var WORKER_PID_DIR = path8.join(EXE_AI_DIR, "worker-pids");
|
|
2941
3114
|
function registerWorkerPid(pid) {
|
|
2942
3115
|
try {
|
|
2943
|
-
|
|
2944
|
-
|
|
3116
|
+
mkdirSync3(WORKER_PID_DIR, { recursive: true });
|
|
3117
|
+
writeFileSync3(path8.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
2945
3118
|
} catch {
|
|
2946
3119
|
}
|
|
2947
3120
|
}
|
|
2948
3121
|
function cleanupWorkerPid() {
|
|
2949
3122
|
try {
|
|
2950
|
-
unlinkSync3(
|
|
3123
|
+
unlinkSync3(path8.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
2951
3124
|
} catch {
|
|
2952
3125
|
}
|
|
2953
3126
|
}
|
|
2954
|
-
var BACKFILL_LOCK =
|
|
3127
|
+
var BACKFILL_LOCK = path8.join(WORKER_PID_DIR, "backfill.lock");
|
|
2955
3128
|
function tryAcquireBackfillLock() {
|
|
2956
3129
|
try {
|
|
2957
|
-
|
|
2958
|
-
if (
|
|
3130
|
+
mkdirSync3(WORKER_PID_DIR, { recursive: true });
|
|
3131
|
+
if (existsSync8(BACKFILL_LOCK)) {
|
|
2959
3132
|
try {
|
|
2960
3133
|
const pid = parseInt(
|
|
2961
3134
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -2971,7 +3144,7 @@ function tryAcquireBackfillLock() {
|
|
|
2971
3144
|
} catch {
|
|
2972
3145
|
}
|
|
2973
3146
|
}
|
|
2974
|
-
|
|
3147
|
+
writeFileSync3(BACKFILL_LOCK, String(process.pid));
|
|
2975
3148
|
return true;
|
|
2976
3149
|
} catch {
|
|
2977
3150
|
return true;
|
|
@@ -2986,7 +3159,7 @@ function releaseBackfillLock() {
|
|
|
2986
3159
|
|
|
2987
3160
|
// src/bin/backfill-vectors.ts
|
|
2988
3161
|
var BATCH_SIZE = 100;
|
|
2989
|
-
var BACKFILL_FLAG =
|
|
3162
|
+
var BACKFILL_FLAG = path9.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
2990
3163
|
async function backfillVectors() {
|
|
2991
3164
|
if (!tryAcquireBackfillLock()) {
|
|
2992
3165
|
process.stderr.write("[backfill] Another backfill is already running \u2014 exiting\n");
|
|
@@ -3064,7 +3237,7 @@ async function backfillVectors() {
|
|
|
3064
3237
|
return { processed: totalProcessed, failed: totalFailed, remaining: remainingCount };
|
|
3065
3238
|
}
|
|
3066
3239
|
function isBackfillNeeded() {
|
|
3067
|
-
return
|
|
3240
|
+
return existsSync9(BACKFILL_FLAG);
|
|
3068
3241
|
}
|
|
3069
3242
|
if (isMainModule(import.meta.url)) {
|
|
3070
3243
|
backfillVectors().then((result) => {
|