@askexenow/exe-os 0.9.8 → 0.9.10
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 +1411 -953
- 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 +913 -543
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +418 -262
- 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 +793 -485
- 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 +566 -357
- 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 +530 -319
- 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 +547 -336
- 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 +649 -417
- package/dist/hooks/bug-report-worker.js +486 -316
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +528 -317
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3442 -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 +534 -323
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +614 -382
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +569 -347
- 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 +664 -431
- 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 +1049 -680
- 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 +422 -357
- package/dist/lib/tmux-routing.js +314 -248
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1408 -672
- 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 +448 -371
- 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 +1983 -315
- package/dist/runtime/index.js +567 -355
- package/dist/tui/App.js +887 -531
- package/package.json +4 -4
|
@@ -64,9 +64,47 @@ var init_db_retry = __esm({
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
// src/lib/secure-files.ts
|
|
68
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
69
|
+
import { chmod, mkdir } from "fs/promises";
|
|
70
|
+
async function ensurePrivateDir(dirPath) {
|
|
71
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
72
|
+
try {
|
|
73
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function ensurePrivateDirSync(dirPath) {
|
|
78
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
79
|
+
try {
|
|
80
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function enforcePrivateFile(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function enforcePrivateFileSync(filePath) {
|
|
91
|
+
try {
|
|
92
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
97
|
+
var init_secure_files = __esm({
|
|
98
|
+
"src/lib/secure-files.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
PRIVATE_DIR_MODE = 448;
|
|
101
|
+
PRIVATE_FILE_MODE = 384;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
67
105
|
// src/lib/config.ts
|
|
68
|
-
import { readFile, writeFile
|
|
69
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
106
|
+
import { readFile, writeFile } from "fs/promises";
|
|
107
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
70
108
|
import path from "path";
|
|
71
109
|
import os from "os";
|
|
72
110
|
function resolveDataDir() {
|
|
@@ -74,7 +112,7 @@ function resolveDataDir() {
|
|
|
74
112
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
75
113
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
76
114
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
77
|
-
if (!
|
|
115
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
78
116
|
try {
|
|
79
117
|
renameSync(legacyDir, newDir);
|
|
80
118
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -137,9 +175,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
137
175
|
}
|
|
138
176
|
async function loadConfig() {
|
|
139
177
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
140
|
-
await
|
|
178
|
+
await ensurePrivateDir(dir);
|
|
141
179
|
const configPath = path.join(dir, "config.json");
|
|
142
|
-
if (!
|
|
180
|
+
if (!existsSync2(configPath)) {
|
|
143
181
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
144
182
|
}
|
|
145
183
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -152,6 +190,7 @@ async function loadConfig() {
|
|
|
152
190
|
`);
|
|
153
191
|
try {
|
|
154
192
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
193
|
+
await enforcePrivateFile(configPath);
|
|
155
194
|
} catch {
|
|
156
195
|
}
|
|
157
196
|
}
|
|
@@ -171,6 +210,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
171
210
|
var init_config = __esm({
|
|
172
211
|
"src/lib/config.ts"() {
|
|
173
212
|
"use strict";
|
|
213
|
+
init_secure_files();
|
|
174
214
|
EXE_AI_DIR = resolveDataDir();
|
|
175
215
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
176
216
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -249,7 +289,7 @@ var init_config = __esm({
|
|
|
249
289
|
|
|
250
290
|
// src/lib/employees.ts
|
|
251
291
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
252
|
-
import { existsSync as
|
|
292
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
253
293
|
import { execSync } from "child_process";
|
|
254
294
|
import path2 from "path";
|
|
255
295
|
import os2 from "os";
|
|
@@ -270,7 +310,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
270
310
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
271
311
|
}
|
|
272
312
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
273
|
-
if (!
|
|
313
|
+
if (!existsSync3(employeesPath)) return [];
|
|
274
314
|
try {
|
|
275
315
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
276
316
|
} catch {
|
|
@@ -1215,6 +1255,7 @@ async function ensureSchema() {
|
|
|
1215
1255
|
project TEXT NOT NULL,
|
|
1216
1256
|
summary TEXT NOT NULL,
|
|
1217
1257
|
task_file TEXT,
|
|
1258
|
+
session_scope TEXT,
|
|
1218
1259
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1219
1260
|
created_at TEXT NOT NULL
|
|
1220
1261
|
);
|
|
@@ -1223,7 +1264,7 @@ async function ensureSchema() {
|
|
|
1223
1264
|
ON notifications(read);
|
|
1224
1265
|
|
|
1225
1266
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1226
|
-
ON notifications(agent_id);
|
|
1267
|
+
ON notifications(agent_id, session_scope);
|
|
1227
1268
|
|
|
1228
1269
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1229
1270
|
ON notifications(task_file);
|
|
@@ -1261,6 +1302,7 @@ async function ensureSchema() {
|
|
|
1261
1302
|
target_agent TEXT NOT NULL,
|
|
1262
1303
|
target_project TEXT,
|
|
1263
1304
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1305
|
+
session_scope TEXT,
|
|
1264
1306
|
content TEXT NOT NULL,
|
|
1265
1307
|
priority TEXT DEFAULT 'normal',
|
|
1266
1308
|
status TEXT DEFAULT 'pending',
|
|
@@ -1274,10 +1316,31 @@ async function ensureSchema() {
|
|
|
1274
1316
|
);
|
|
1275
1317
|
|
|
1276
1318
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1277
|
-
ON messages(target_agent, status);
|
|
1319
|
+
ON messages(target_agent, session_scope, status);
|
|
1278
1320
|
|
|
1279
1321
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1280
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1322
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1323
|
+
`);
|
|
1324
|
+
try {
|
|
1325
|
+
await client.execute({
|
|
1326
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1327
|
+
args: []
|
|
1328
|
+
});
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
try {
|
|
1332
|
+
await client.execute({
|
|
1333
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1334
|
+
args: []
|
|
1335
|
+
});
|
|
1336
|
+
} catch {
|
|
1337
|
+
}
|
|
1338
|
+
await client.executeMultiple(`
|
|
1339
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1340
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1341
|
+
|
|
1342
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1343
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1281
1344
|
`);
|
|
1282
1345
|
try {
|
|
1283
1346
|
await client.execute({
|
|
@@ -1861,6 +1924,13 @@ async function ensureSchema() {
|
|
|
1861
1924
|
} catch {
|
|
1862
1925
|
}
|
|
1863
1926
|
}
|
|
1927
|
+
try {
|
|
1928
|
+
await client.execute({
|
|
1929
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1930
|
+
args: []
|
|
1931
|
+
});
|
|
1932
|
+
} catch {
|
|
1933
|
+
}
|
|
1864
1934
|
}
|
|
1865
1935
|
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1866
1936
|
var init_database = __esm({
|
|
@@ -1883,6 +1953,7 @@ var shard_manager_exports = {};
|
|
|
1883
1953
|
__export(shard_manager_exports, {
|
|
1884
1954
|
disposeShards: () => disposeShards,
|
|
1885
1955
|
ensureShardSchema: () => ensureShardSchema,
|
|
1956
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1886
1957
|
getReadyShardClient: () => getReadyShardClient,
|
|
1887
1958
|
getShardClient: () => getShardClient,
|
|
1888
1959
|
getShardsDir: () => getShardsDir,
|
|
@@ -1892,14 +1963,17 @@ __export(shard_manager_exports, {
|
|
|
1892
1963
|
shardExists: () => shardExists
|
|
1893
1964
|
});
|
|
1894
1965
|
import path5 from "path";
|
|
1895
|
-
import { existsSync as
|
|
1966
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1896
1967
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1897
1968
|
function initShardManager(encryptionKey) {
|
|
1898
1969
|
_encryptionKey = encryptionKey;
|
|
1899
|
-
if (!
|
|
1900
|
-
|
|
1970
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
1971
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1901
1972
|
}
|
|
1902
1973
|
_shardingEnabled = true;
|
|
1974
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
1975
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
1976
|
+
_evictionTimer.unref();
|
|
1903
1977
|
}
|
|
1904
1978
|
function isShardingEnabled() {
|
|
1905
1979
|
return _shardingEnabled;
|
|
@@ -1916,21 +1990,28 @@ function getShardClient(projectName) {
|
|
|
1916
1990
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1917
1991
|
}
|
|
1918
1992
|
const cached = _shards.get(safeName);
|
|
1919
|
-
if (cached)
|
|
1993
|
+
if (cached) {
|
|
1994
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1995
|
+
return cached;
|
|
1996
|
+
}
|
|
1997
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
1998
|
+
evictLRU();
|
|
1999
|
+
}
|
|
1920
2000
|
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1921
2001
|
const client = createClient2({
|
|
1922
2002
|
url: `file:${dbPath}`,
|
|
1923
2003
|
encryptionKey: _encryptionKey
|
|
1924
2004
|
});
|
|
1925
2005
|
_shards.set(safeName, client);
|
|
2006
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1926
2007
|
return client;
|
|
1927
2008
|
}
|
|
1928
2009
|
function shardExists(projectName) {
|
|
1929
2010
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1930
|
-
return
|
|
2011
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1931
2012
|
}
|
|
1932
2013
|
function listShards() {
|
|
1933
|
-
if (!
|
|
2014
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1934
2015
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1935
2016
|
}
|
|
1936
2017
|
async function ensureShardSchema(client) {
|
|
@@ -1982,6 +2063,8 @@ async function ensureShardSchema(client) {
|
|
|
1982
2063
|
for (const col of [
|
|
1983
2064
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
1984
2065
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2066
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2067
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
1985
2068
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
1986
2069
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
1987
2070
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2119,21 +2202,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2119
2202
|
await ensureShardSchema(client);
|
|
2120
2203
|
return client;
|
|
2121
2204
|
}
|
|
2205
|
+
function evictLRU() {
|
|
2206
|
+
let oldest = null;
|
|
2207
|
+
let oldestTime = Infinity;
|
|
2208
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2209
|
+
if (time < oldestTime) {
|
|
2210
|
+
oldestTime = time;
|
|
2211
|
+
oldest = name;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
if (oldest) {
|
|
2215
|
+
const client = _shards.get(oldest);
|
|
2216
|
+
if (client) {
|
|
2217
|
+
client.close();
|
|
2218
|
+
}
|
|
2219
|
+
_shards.delete(oldest);
|
|
2220
|
+
_shardLastAccess.delete(oldest);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
function evictIdleShards() {
|
|
2224
|
+
const now = Date.now();
|
|
2225
|
+
const toEvict = [];
|
|
2226
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2227
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2228
|
+
toEvict.push(name);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
for (const name of toEvict) {
|
|
2232
|
+
const client = _shards.get(name);
|
|
2233
|
+
if (client) {
|
|
2234
|
+
client.close();
|
|
2235
|
+
}
|
|
2236
|
+
_shards.delete(name);
|
|
2237
|
+
_shardLastAccess.delete(name);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
function getOpenShardCount() {
|
|
2241
|
+
return _shards.size;
|
|
2242
|
+
}
|
|
2122
2243
|
function disposeShards() {
|
|
2244
|
+
if (_evictionTimer) {
|
|
2245
|
+
clearInterval(_evictionTimer);
|
|
2246
|
+
_evictionTimer = null;
|
|
2247
|
+
}
|
|
2123
2248
|
for (const [, client] of _shards) {
|
|
2124
2249
|
client.close();
|
|
2125
2250
|
}
|
|
2126
2251
|
_shards.clear();
|
|
2252
|
+
_shardLastAccess.clear();
|
|
2127
2253
|
_shardingEnabled = false;
|
|
2128
2254
|
_encryptionKey = null;
|
|
2129
2255
|
}
|
|
2130
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2256
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2131
2257
|
var init_shard_manager = __esm({
|
|
2132
2258
|
"src/lib/shard-manager.ts"() {
|
|
2133
2259
|
"use strict";
|
|
2134
2260
|
init_config();
|
|
2135
2261
|
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
2262
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2263
|
+
MAX_OPEN_SHARDS = 10;
|
|
2264
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2136
2265
|
_shards = /* @__PURE__ */ new Map();
|
|
2266
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2267
|
+
_evictionTimer = null;
|
|
2137
2268
|
_encryptionKey = null;
|
|
2138
2269
|
_shardingEnabled = false;
|
|
2139
2270
|
}
|
|
@@ -2326,13 +2457,50 @@ ${p.content}`).join("\n\n");
|
|
|
2326
2457
|
}
|
|
2327
2458
|
});
|
|
2328
2459
|
|
|
2460
|
+
// src/lib/daemon-auth.ts
|
|
2461
|
+
import crypto from "crypto";
|
|
2462
|
+
import path6 from "path";
|
|
2463
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
2464
|
+
function normalizeToken(token) {
|
|
2465
|
+
if (!token) return null;
|
|
2466
|
+
const trimmed = token.trim();
|
|
2467
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2468
|
+
}
|
|
2469
|
+
function readDaemonToken() {
|
|
2470
|
+
try {
|
|
2471
|
+
if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
|
|
2472
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
2473
|
+
} catch {
|
|
2474
|
+
return null;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
function ensureDaemonToken(seed) {
|
|
2478
|
+
const existing = readDaemonToken();
|
|
2479
|
+
if (existing) return existing;
|
|
2480
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
2481
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2482
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
2483
|
+
`, "utf8");
|
|
2484
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2485
|
+
return token;
|
|
2486
|
+
}
|
|
2487
|
+
var DAEMON_TOKEN_PATH;
|
|
2488
|
+
var init_daemon_auth = __esm({
|
|
2489
|
+
"src/lib/daemon-auth.ts"() {
|
|
2490
|
+
"use strict";
|
|
2491
|
+
init_config();
|
|
2492
|
+
init_secure_files();
|
|
2493
|
+
DAEMON_TOKEN_PATH = path6.join(EXE_AI_DIR, "exed.token");
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2329
2497
|
// src/lib/exe-daemon-client.ts
|
|
2330
2498
|
import net from "net";
|
|
2331
2499
|
import os5 from "os";
|
|
2332
2500
|
import { spawn } from "child_process";
|
|
2333
2501
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2334
|
-
import { existsSync as
|
|
2335
|
-
import
|
|
2502
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
2503
|
+
import path7 from "path";
|
|
2336
2504
|
import { fileURLToPath } from "url";
|
|
2337
2505
|
function handleData(chunk) {
|
|
2338
2506
|
_buffer += chunk.toString();
|
|
@@ -2360,9 +2528,9 @@ function handleData(chunk) {
|
|
|
2360
2528
|
}
|
|
2361
2529
|
}
|
|
2362
2530
|
function cleanupStaleFiles() {
|
|
2363
|
-
if (
|
|
2531
|
+
if (existsSync7(PID_PATH)) {
|
|
2364
2532
|
try {
|
|
2365
|
-
const pid = parseInt(
|
|
2533
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2366
2534
|
if (pid > 0) {
|
|
2367
2535
|
try {
|
|
2368
2536
|
process.kill(pid, 0);
|
|
@@ -2383,11 +2551,11 @@ function cleanupStaleFiles() {
|
|
|
2383
2551
|
}
|
|
2384
2552
|
}
|
|
2385
2553
|
function findPackageRoot() {
|
|
2386
|
-
let dir =
|
|
2387
|
-
const { root } =
|
|
2554
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
2555
|
+
const { root } = path7.parse(dir);
|
|
2388
2556
|
while (dir !== root) {
|
|
2389
|
-
if (
|
|
2390
|
-
dir =
|
|
2557
|
+
if (existsSync7(path7.join(dir, "package.json"))) return dir;
|
|
2558
|
+
dir = path7.dirname(dir);
|
|
2391
2559
|
}
|
|
2392
2560
|
return null;
|
|
2393
2561
|
}
|
|
@@ -2413,16 +2581,17 @@ function spawnDaemon() {
|
|
|
2413
2581
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2414
2582
|
return;
|
|
2415
2583
|
}
|
|
2416
|
-
const daemonPath =
|
|
2417
|
-
if (!
|
|
2584
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2585
|
+
if (!existsSync7(daemonPath)) {
|
|
2418
2586
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2419
2587
|
`);
|
|
2420
2588
|
return;
|
|
2421
2589
|
}
|
|
2422
2590
|
const resolvedPath = daemonPath;
|
|
2591
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2423
2592
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2424
2593
|
`);
|
|
2425
|
-
const logPath =
|
|
2594
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
2426
2595
|
let stderrFd = "ignore";
|
|
2427
2596
|
try {
|
|
2428
2597
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2440,7 +2609,8 @@ function spawnDaemon() {
|
|
|
2440
2609
|
TMUX_PANE: void 0,
|
|
2441
2610
|
// Prevents resolveExeSession() from scoping to one session
|
|
2442
2611
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2443
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2612
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2613
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2444
2614
|
}
|
|
2445
2615
|
});
|
|
2446
2616
|
child.unref();
|
|
@@ -2550,13 +2720,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2550
2720
|
return;
|
|
2551
2721
|
}
|
|
2552
2722
|
const id = randomUUID2();
|
|
2723
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2553
2724
|
const timer = setTimeout(() => {
|
|
2554
2725
|
_pending.delete(id);
|
|
2555
2726
|
resolve({ error: "Request timeout" });
|
|
2556
2727
|
}, timeoutMs);
|
|
2557
2728
|
_pending.set(id, { resolve, timer });
|
|
2558
2729
|
try {
|
|
2559
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2730
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2560
2731
|
} catch {
|
|
2561
2732
|
clearTimeout(timer);
|
|
2562
2733
|
_pending.delete(id);
|
|
@@ -2585,9 +2756,9 @@ function killAndRespawnDaemon() {
|
|
|
2585
2756
|
}
|
|
2586
2757
|
try {
|
|
2587
2758
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2588
|
-
if (
|
|
2759
|
+
if (existsSync7(PID_PATH)) {
|
|
2589
2760
|
try {
|
|
2590
|
-
const pid = parseInt(
|
|
2761
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
2591
2762
|
if (pid > 0) {
|
|
2592
2763
|
try {
|
|
2593
2764
|
process.kill(pid, "SIGKILL");
|
|
@@ -2691,17 +2862,19 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2691
2862
|
);
|
|
2692
2863
|
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2693
2864
|
}
|
|
2694
|
-
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;
|
|
2865
|
+
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;
|
|
2695
2866
|
var init_exe_daemon_client = __esm({
|
|
2696
2867
|
"src/lib/exe-daemon-client.ts"() {
|
|
2697
2868
|
"use strict";
|
|
2698
2869
|
init_config();
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2870
|
+
init_daemon_auth();
|
|
2871
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
2872
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
2873
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2702
2874
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2703
2875
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2704
2876
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2877
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2705
2878
|
_socket = null;
|
|
2706
2879
|
_connected = false;
|
|
2707
2880
|
_buffer = "";
|
|
@@ -2717,10 +2890,10 @@ var init_exe_daemon_client = __esm({
|
|
|
2717
2890
|
});
|
|
2718
2891
|
|
|
2719
2892
|
// src/bin/backfill-conversations.ts
|
|
2720
|
-
import
|
|
2893
|
+
import crypto2 from "crypto";
|
|
2721
2894
|
import { createReadStream } from "fs";
|
|
2722
2895
|
import { readdir, stat } from "fs/promises";
|
|
2723
|
-
import
|
|
2896
|
+
import path8 from "path";
|
|
2724
2897
|
import { createInterface } from "readline";
|
|
2725
2898
|
import { homedir } from "os";
|
|
2726
2899
|
import { parseArgs } from "util";
|
|
@@ -2736,7 +2909,7 @@ init_database();
|
|
|
2736
2909
|
|
|
2737
2910
|
// src/lib/keychain.ts
|
|
2738
2911
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2739
|
-
import { existsSync as
|
|
2912
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2740
2913
|
import path4 from "path";
|
|
2741
2914
|
import os4 from "os";
|
|
2742
2915
|
var SERVICE = "exe-mem";
|
|
@@ -2766,7 +2939,7 @@ async function getMasterKey() {
|
|
|
2766
2939
|
}
|
|
2767
2940
|
}
|
|
2768
2941
|
const keyPath = getKeyPath();
|
|
2769
|
-
if (!
|
|
2942
|
+
if (!existsSync4(keyPath)) {
|
|
2770
2943
|
process.stderr.write(
|
|
2771
2944
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2772
2945
|
`
|
|
@@ -3215,7 +3388,7 @@ var MIN_MESSAGES = 3;
|
|
|
3215
3388
|
var MAX_SUMMARY_LENGTH = 4e3;
|
|
3216
3389
|
var MAX_WALK_DEPTH = 10;
|
|
3217
3390
|
async function findJsonlFiles(sinceDate, projectFilter) {
|
|
3218
|
-
const projectsDir =
|
|
3391
|
+
const projectsDir = path8.join(homedir(), ".claude", "projects");
|
|
3219
3392
|
const files = [];
|
|
3220
3393
|
async function walk(dir, depth = 0) {
|
|
3221
3394
|
if (depth > MAX_WALK_DEPTH) return;
|
|
@@ -3226,7 +3399,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
3226
3399
|
return;
|
|
3227
3400
|
}
|
|
3228
3401
|
for (const entry of entries) {
|
|
3229
|
-
const full =
|
|
3402
|
+
const full = path8.join(dir, entry.name);
|
|
3230
3403
|
if (entry.isDirectory()) {
|
|
3231
3404
|
if (entry.name === "subagents" || entry.name === "tool-results") continue;
|
|
3232
3405
|
await walk(full, depth + 1);
|
|
@@ -3251,7 +3424,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
3251
3424
|
if (!entry.isDirectory()) continue;
|
|
3252
3425
|
const decoded = decodeProjectDir(entry.name);
|
|
3253
3426
|
if (decoded.toLowerCase().includes(projectFilter.toLowerCase())) {
|
|
3254
|
-
await walk(
|
|
3427
|
+
await walk(path8.join(projectsDir, entry.name));
|
|
3255
3428
|
}
|
|
3256
3429
|
}
|
|
3257
3430
|
} else {
|
|
@@ -3268,14 +3441,14 @@ function decodeProjectDir(dirName) {
|
|
|
3268
3441
|
return dirName;
|
|
3269
3442
|
}
|
|
3270
3443
|
function projectNameFromPath(filePath) {
|
|
3271
|
-
const projectsDir =
|
|
3272
|
-
const relative =
|
|
3273
|
-
const projectDir = relative.split(
|
|
3444
|
+
const projectsDir = path8.join(homedir(), ".claude", "projects");
|
|
3445
|
+
const relative = path8.relative(projectsDir, filePath);
|
|
3446
|
+
const projectDir = relative.split(path8.sep)[0] ?? "unknown";
|
|
3274
3447
|
return decodeProjectDir(projectDir);
|
|
3275
3448
|
}
|
|
3276
3449
|
async function parseConversation(filePath) {
|
|
3277
3450
|
const conv = {
|
|
3278
|
-
sessionId:
|
|
3451
|
+
sessionId: path8.basename(filePath, ".jsonl"),
|
|
3279
3452
|
projectName: projectNameFromPath(filePath),
|
|
3280
3453
|
cwd: void 0,
|
|
3281
3454
|
startTime: void 0,
|
|
@@ -3339,7 +3512,7 @@ async function parseConversation(filePath) {
|
|
|
3339
3512
|
}
|
|
3340
3513
|
}
|
|
3341
3514
|
if (conv.cwd) {
|
|
3342
|
-
conv.projectName =
|
|
3515
|
+
conv.projectName = path8.basename(conv.cwd);
|
|
3343
3516
|
const worktreeMatch = conv.cwd.match(/\.worktrees\/([^/]+)/);
|
|
3344
3517
|
if (worktreeMatch?.[1]) {
|
|
3345
3518
|
conv.agentId = worktreeMatch[1];
|
|
@@ -3512,7 +3685,7 @@ async function backfillConversations(options) {
|
|
|
3512
3685
|
}
|
|
3513
3686
|
}
|
|
3514
3687
|
await writeMemory({
|
|
3515
|
-
id:
|
|
3688
|
+
id: crypto2.randomUUID(),
|
|
3516
3689
|
agent_id: conv.agentId,
|
|
3517
3690
|
agent_role: isCoordinatorName(conv.agentId) ? "COO" : "specialist",
|
|
3518
3691
|
session_id: conv.sessionId,
|