@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
|
@@ -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");
|
|
@@ -314,7 +352,7 @@ var init_db_retry = __esm({
|
|
|
314
352
|
|
|
315
353
|
// src/lib/employees.ts
|
|
316
354
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
317
|
-
import { existsSync as
|
|
355
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
318
356
|
import { execSync } from "child_process";
|
|
319
357
|
import path2 from "path";
|
|
320
358
|
import os2 from "os";
|
|
@@ -331,7 +369,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
331
369
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
332
370
|
}
|
|
333
371
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
334
|
-
if (!
|
|
372
|
+
if (!existsSync3(employeesPath)) return [];
|
|
335
373
|
try {
|
|
336
374
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
337
375
|
} catch {
|
|
@@ -1279,6 +1317,7 @@ async function ensureSchema() {
|
|
|
1279
1317
|
project TEXT NOT NULL,
|
|
1280
1318
|
summary TEXT NOT NULL,
|
|
1281
1319
|
task_file TEXT,
|
|
1320
|
+
session_scope TEXT,
|
|
1282
1321
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1283
1322
|
created_at TEXT NOT NULL
|
|
1284
1323
|
);
|
|
@@ -1287,7 +1326,7 @@ async function ensureSchema() {
|
|
|
1287
1326
|
ON notifications(read);
|
|
1288
1327
|
|
|
1289
1328
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1290
|
-
ON notifications(agent_id);
|
|
1329
|
+
ON notifications(agent_id, session_scope);
|
|
1291
1330
|
|
|
1292
1331
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1293
1332
|
ON notifications(task_file);
|
|
@@ -1325,6 +1364,7 @@ async function ensureSchema() {
|
|
|
1325
1364
|
target_agent TEXT NOT NULL,
|
|
1326
1365
|
target_project TEXT,
|
|
1327
1366
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1367
|
+
session_scope TEXT,
|
|
1328
1368
|
content TEXT NOT NULL,
|
|
1329
1369
|
priority TEXT DEFAULT 'normal',
|
|
1330
1370
|
status TEXT DEFAULT 'pending',
|
|
@@ -1338,10 +1378,31 @@ async function ensureSchema() {
|
|
|
1338
1378
|
);
|
|
1339
1379
|
|
|
1340
1380
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1341
|
-
ON messages(target_agent, status);
|
|
1381
|
+
ON messages(target_agent, session_scope, status);
|
|
1342
1382
|
|
|
1343
1383
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1344
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1384
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1385
|
+
`);
|
|
1386
|
+
try {
|
|
1387
|
+
await client.execute({
|
|
1388
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1389
|
+
args: []
|
|
1390
|
+
});
|
|
1391
|
+
} catch {
|
|
1392
|
+
}
|
|
1393
|
+
try {
|
|
1394
|
+
await client.execute({
|
|
1395
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1396
|
+
args: []
|
|
1397
|
+
});
|
|
1398
|
+
} catch {
|
|
1399
|
+
}
|
|
1400
|
+
await client.executeMultiple(`
|
|
1401
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1402
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1403
|
+
|
|
1404
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1405
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1345
1406
|
`);
|
|
1346
1407
|
try {
|
|
1347
1408
|
await client.execute({
|
|
@@ -1925,6 +1986,13 @@ async function ensureSchema() {
|
|
|
1925
1986
|
} catch {
|
|
1926
1987
|
}
|
|
1927
1988
|
}
|
|
1989
|
+
try {
|
|
1990
|
+
await client.execute({
|
|
1991
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1992
|
+
args: []
|
|
1993
|
+
});
|
|
1994
|
+
} catch {
|
|
1995
|
+
}
|
|
1928
1996
|
}
|
|
1929
1997
|
async function disposeDatabase() {
|
|
1930
1998
|
if (_walCheckpointTimer) {
|
|
@@ -1964,7 +2032,7 @@ var init_database = __esm({
|
|
|
1964
2032
|
|
|
1965
2033
|
// src/lib/keychain.ts
|
|
1966
2034
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1967
|
-
import { existsSync as
|
|
2035
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1968
2036
|
import path4 from "path";
|
|
1969
2037
|
import os4 from "os";
|
|
1970
2038
|
function getKeyDir() {
|
|
@@ -1992,7 +2060,7 @@ async function getMasterKey() {
|
|
|
1992
2060
|
}
|
|
1993
2061
|
}
|
|
1994
2062
|
const keyPath = getKeyPath();
|
|
1995
|
-
if (!
|
|
2063
|
+
if (!existsSync4(keyPath)) {
|
|
1996
2064
|
process.stderr.write(
|
|
1997
2065
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
1998
2066
|
`
|
|
@@ -2079,6 +2147,7 @@ var shard_manager_exports = {};
|
|
|
2079
2147
|
__export(shard_manager_exports, {
|
|
2080
2148
|
disposeShards: () => disposeShards,
|
|
2081
2149
|
ensureShardSchema: () => ensureShardSchema,
|
|
2150
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2082
2151
|
getReadyShardClient: () => getReadyShardClient,
|
|
2083
2152
|
getShardClient: () => getShardClient,
|
|
2084
2153
|
getShardsDir: () => getShardsDir,
|
|
@@ -2088,14 +2157,17 @@ __export(shard_manager_exports, {
|
|
|
2088
2157
|
shardExists: () => shardExists
|
|
2089
2158
|
});
|
|
2090
2159
|
import path5 from "path";
|
|
2091
|
-
import { existsSync as
|
|
2160
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2092
2161
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2093
2162
|
function initShardManager(encryptionKey) {
|
|
2094
2163
|
_encryptionKey = encryptionKey;
|
|
2095
|
-
if (!
|
|
2096
|
-
|
|
2164
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
2165
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2097
2166
|
}
|
|
2098
2167
|
_shardingEnabled = true;
|
|
2168
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2169
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2170
|
+
_evictionTimer.unref();
|
|
2099
2171
|
}
|
|
2100
2172
|
function isShardingEnabled() {
|
|
2101
2173
|
return _shardingEnabled;
|
|
@@ -2112,21 +2184,28 @@ function getShardClient(projectName) {
|
|
|
2112
2184
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2113
2185
|
}
|
|
2114
2186
|
const cached = _shards.get(safeName);
|
|
2115
|
-
if (cached)
|
|
2187
|
+
if (cached) {
|
|
2188
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2189
|
+
return cached;
|
|
2190
|
+
}
|
|
2191
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2192
|
+
evictLRU();
|
|
2193
|
+
}
|
|
2116
2194
|
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
2117
2195
|
const client = createClient2({
|
|
2118
2196
|
url: `file:${dbPath}`,
|
|
2119
2197
|
encryptionKey: _encryptionKey
|
|
2120
2198
|
});
|
|
2121
2199
|
_shards.set(safeName, client);
|
|
2200
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2122
2201
|
return client;
|
|
2123
2202
|
}
|
|
2124
2203
|
function shardExists(projectName) {
|
|
2125
2204
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2126
|
-
return
|
|
2205
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
2127
2206
|
}
|
|
2128
2207
|
function listShards() {
|
|
2129
|
-
if (!
|
|
2208
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
2130
2209
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2131
2210
|
}
|
|
2132
2211
|
async function ensureShardSchema(client) {
|
|
@@ -2178,6 +2257,8 @@ async function ensureShardSchema(client) {
|
|
|
2178
2257
|
for (const col of [
|
|
2179
2258
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2180
2259
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2260
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2261
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2181
2262
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2182
2263
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2183
2264
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2315,21 +2396,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2315
2396
|
await ensureShardSchema(client);
|
|
2316
2397
|
return client;
|
|
2317
2398
|
}
|
|
2399
|
+
function evictLRU() {
|
|
2400
|
+
let oldest = null;
|
|
2401
|
+
let oldestTime = Infinity;
|
|
2402
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2403
|
+
if (time < oldestTime) {
|
|
2404
|
+
oldestTime = time;
|
|
2405
|
+
oldest = name;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
if (oldest) {
|
|
2409
|
+
const client = _shards.get(oldest);
|
|
2410
|
+
if (client) {
|
|
2411
|
+
client.close();
|
|
2412
|
+
}
|
|
2413
|
+
_shards.delete(oldest);
|
|
2414
|
+
_shardLastAccess.delete(oldest);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
function evictIdleShards() {
|
|
2418
|
+
const now = Date.now();
|
|
2419
|
+
const toEvict = [];
|
|
2420
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2421
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2422
|
+
toEvict.push(name);
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
for (const name of toEvict) {
|
|
2426
|
+
const client = _shards.get(name);
|
|
2427
|
+
if (client) {
|
|
2428
|
+
client.close();
|
|
2429
|
+
}
|
|
2430
|
+
_shards.delete(name);
|
|
2431
|
+
_shardLastAccess.delete(name);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
function getOpenShardCount() {
|
|
2435
|
+
return _shards.size;
|
|
2436
|
+
}
|
|
2318
2437
|
function disposeShards() {
|
|
2438
|
+
if (_evictionTimer) {
|
|
2439
|
+
clearInterval(_evictionTimer);
|
|
2440
|
+
_evictionTimer = null;
|
|
2441
|
+
}
|
|
2319
2442
|
for (const [, client] of _shards) {
|
|
2320
2443
|
client.close();
|
|
2321
2444
|
}
|
|
2322
2445
|
_shards.clear();
|
|
2446
|
+
_shardLastAccess.clear();
|
|
2323
2447
|
_shardingEnabled = false;
|
|
2324
2448
|
_encryptionKey = null;
|
|
2325
2449
|
}
|
|
2326
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2450
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2327
2451
|
var init_shard_manager = __esm({
|
|
2328
2452
|
"src/lib/shard-manager.ts"() {
|
|
2329
2453
|
"use strict";
|
|
2330
2454
|
init_config();
|
|
2331
2455
|
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
2456
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2457
|
+
MAX_OPEN_SHARDS = 10;
|
|
2458
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2332
2459
|
_shards = /* @__PURE__ */ new Map();
|
|
2460
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2461
|
+
_evictionTimer = null;
|
|
2333
2462
|
_encryptionKey = null;
|
|
2334
2463
|
_shardingEnabled = false;
|
|
2335
2464
|
}
|
|
@@ -3192,7 +3321,7 @@ __export(reranker_exports, {
|
|
|
3192
3321
|
rerankWithScores: () => rerankWithScores
|
|
3193
3322
|
});
|
|
3194
3323
|
import path6 from "path";
|
|
3195
|
-
import { existsSync as
|
|
3324
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3196
3325
|
function resetIdleTimer() {
|
|
3197
3326
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
3198
3327
|
_idleTimer = setTimeout(() => {
|
|
@@ -3203,7 +3332,7 @@ function resetIdleTimer() {
|
|
|
3203
3332
|
}
|
|
3204
3333
|
}
|
|
3205
3334
|
function isRerankerAvailable() {
|
|
3206
|
-
return
|
|
3335
|
+
return existsSync6(path6.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
3207
3336
|
}
|
|
3208
3337
|
function getRerankerModelPath() {
|
|
3209
3338
|
return path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
@@ -3214,7 +3343,7 @@ async function ensureLoaded() {
|
|
|
3214
3343
|
return;
|
|
3215
3344
|
}
|
|
3216
3345
|
const modelPath = path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3217
|
-
if (!
|
|
3346
|
+
if (!existsSync6(modelPath)) {
|
|
3218
3347
|
throw new Error(
|
|
3219
3348
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3220
3349
|
);
|
|
@@ -3310,13 +3439,50 @@ var init_reranker = __esm({
|
|
|
3310
3439
|
}
|
|
3311
3440
|
});
|
|
3312
3441
|
|
|
3442
|
+
// src/lib/daemon-auth.ts
|
|
3443
|
+
import crypto2 from "crypto";
|
|
3444
|
+
import path7 from "path";
|
|
3445
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3446
|
+
function normalizeToken(token) {
|
|
3447
|
+
if (!token) return null;
|
|
3448
|
+
const trimmed = token.trim();
|
|
3449
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3450
|
+
}
|
|
3451
|
+
function readDaemonToken() {
|
|
3452
|
+
try {
|
|
3453
|
+
if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
|
|
3454
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
3455
|
+
} catch {
|
|
3456
|
+
return null;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
function ensureDaemonToken(seed) {
|
|
3460
|
+
const existing = readDaemonToken();
|
|
3461
|
+
if (existing) return existing;
|
|
3462
|
+
const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
|
|
3463
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
3464
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
3465
|
+
`, "utf8");
|
|
3466
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
3467
|
+
return token;
|
|
3468
|
+
}
|
|
3469
|
+
var DAEMON_TOKEN_PATH;
|
|
3470
|
+
var init_daemon_auth = __esm({
|
|
3471
|
+
"src/lib/daemon-auth.ts"() {
|
|
3472
|
+
"use strict";
|
|
3473
|
+
init_config();
|
|
3474
|
+
init_secure_files();
|
|
3475
|
+
DAEMON_TOKEN_PATH = path7.join(EXE_AI_DIR, "exed.token");
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3478
|
+
|
|
3313
3479
|
// src/lib/exe-daemon-client.ts
|
|
3314
3480
|
import net from "net";
|
|
3315
3481
|
import os5 from "os";
|
|
3316
3482
|
import { spawn } from "child_process";
|
|
3317
3483
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3318
|
-
import { existsSync as
|
|
3319
|
-
import
|
|
3484
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
3485
|
+
import path8 from "path";
|
|
3320
3486
|
import { fileURLToPath } from "url";
|
|
3321
3487
|
function handleData(chunk) {
|
|
3322
3488
|
_buffer += chunk.toString();
|
|
@@ -3344,9 +3510,9 @@ function handleData(chunk) {
|
|
|
3344
3510
|
}
|
|
3345
3511
|
}
|
|
3346
3512
|
function cleanupStaleFiles() {
|
|
3347
|
-
if (
|
|
3513
|
+
if (existsSync8(PID_PATH)) {
|
|
3348
3514
|
try {
|
|
3349
|
-
const pid = parseInt(
|
|
3515
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
3350
3516
|
if (pid > 0) {
|
|
3351
3517
|
try {
|
|
3352
3518
|
process.kill(pid, 0);
|
|
@@ -3367,11 +3533,11 @@ function cleanupStaleFiles() {
|
|
|
3367
3533
|
}
|
|
3368
3534
|
}
|
|
3369
3535
|
function findPackageRoot() {
|
|
3370
|
-
let dir =
|
|
3371
|
-
const { root } =
|
|
3536
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
3537
|
+
const { root } = path8.parse(dir);
|
|
3372
3538
|
while (dir !== root) {
|
|
3373
|
-
if (
|
|
3374
|
-
dir =
|
|
3539
|
+
if (existsSync8(path8.join(dir, "package.json"))) return dir;
|
|
3540
|
+
dir = path8.dirname(dir);
|
|
3375
3541
|
}
|
|
3376
3542
|
return null;
|
|
3377
3543
|
}
|
|
@@ -3397,16 +3563,17 @@ function spawnDaemon() {
|
|
|
3397
3563
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
3398
3564
|
return;
|
|
3399
3565
|
}
|
|
3400
|
-
const daemonPath =
|
|
3401
|
-
if (!
|
|
3566
|
+
const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
3567
|
+
if (!existsSync8(daemonPath)) {
|
|
3402
3568
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
3403
3569
|
`);
|
|
3404
3570
|
return;
|
|
3405
3571
|
}
|
|
3406
3572
|
const resolvedPath = daemonPath;
|
|
3573
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
3407
3574
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
3408
3575
|
`);
|
|
3409
|
-
const logPath =
|
|
3576
|
+
const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
|
|
3410
3577
|
let stderrFd = "ignore";
|
|
3411
3578
|
try {
|
|
3412
3579
|
stderrFd = openSync(logPath, "a");
|
|
@@ -3424,7 +3591,8 @@ function spawnDaemon() {
|
|
|
3424
3591
|
TMUX_PANE: void 0,
|
|
3425
3592
|
// Prevents resolveExeSession() from scoping to one session
|
|
3426
3593
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
3427
|
-
EXE_DAEMON_PID: PID_PATH
|
|
3594
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
3595
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
3428
3596
|
}
|
|
3429
3597
|
});
|
|
3430
3598
|
child.unref();
|
|
@@ -3534,13 +3702,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
3534
3702
|
return;
|
|
3535
3703
|
}
|
|
3536
3704
|
const id = randomUUID2();
|
|
3705
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
3537
3706
|
const timer = setTimeout(() => {
|
|
3538
3707
|
_pending.delete(id);
|
|
3539
3708
|
resolve({ error: "Request timeout" });
|
|
3540
3709
|
}, timeoutMs);
|
|
3541
3710
|
_pending.set(id, { resolve, timer });
|
|
3542
3711
|
try {
|
|
3543
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
3712
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
3544
3713
|
} catch {
|
|
3545
3714
|
clearTimeout(timer);
|
|
3546
3715
|
_pending.delete(id);
|
|
@@ -3569,9 +3738,9 @@ function killAndRespawnDaemon() {
|
|
|
3569
3738
|
}
|
|
3570
3739
|
try {
|
|
3571
3740
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
3572
|
-
if (
|
|
3741
|
+
if (existsSync8(PID_PATH)) {
|
|
3573
3742
|
try {
|
|
3574
|
-
const pid = parseInt(
|
|
3743
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
3575
3744
|
if (pid > 0) {
|
|
3576
3745
|
try {
|
|
3577
3746
|
process.kill(pid, "SIGKILL");
|
|
@@ -3688,17 +3857,19 @@ function disconnectClient() {
|
|
|
3688
3857
|
entry.resolve({ error: "Client disconnected" });
|
|
3689
3858
|
}
|
|
3690
3859
|
}
|
|
3691
|
-
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;
|
|
3860
|
+
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;
|
|
3692
3861
|
var init_exe_daemon_client = __esm({
|
|
3693
3862
|
"src/lib/exe-daemon-client.ts"() {
|
|
3694
3863
|
"use strict";
|
|
3695
3864
|
init_config();
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3865
|
+
init_daemon_auth();
|
|
3866
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
|
|
3867
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
|
|
3868
|
+
SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
3699
3869
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
3700
3870
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
3701
3871
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
3872
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
3702
3873
|
_socket = null;
|
|
3703
3874
|
_connected = false;
|
|
3704
3875
|
_buffer = "";
|
|
@@ -3750,10 +3921,10 @@ async function disposeEmbedder() {
|
|
|
3750
3921
|
async function embedDirect(text) {
|
|
3751
3922
|
const llamaCpp = await import("node-llama-cpp");
|
|
3752
3923
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3753
|
-
const { existsSync:
|
|
3754
|
-
const
|
|
3755
|
-
const modelPath =
|
|
3756
|
-
if (!
|
|
3924
|
+
const { existsSync: existsSync10 } = await import("fs");
|
|
3925
|
+
const path12 = await import("path");
|
|
3926
|
+
const modelPath = path12.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3927
|
+
if (!existsSync10(modelPath)) {
|
|
3757
3928
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
3758
3929
|
}
|
|
3759
3930
|
const llama = await llamaCpp.getLlama();
|
|
@@ -3788,7 +3959,7 @@ __export(project_name_exports, {
|
|
|
3788
3959
|
getProjectName: () => getProjectName
|
|
3789
3960
|
});
|
|
3790
3961
|
import { execSync as execSync2 } from "child_process";
|
|
3791
|
-
import
|
|
3962
|
+
import path9 from "path";
|
|
3792
3963
|
function getProjectName(cwd) {
|
|
3793
3964
|
const dir = cwd ?? process.cwd();
|
|
3794
3965
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
@@ -3801,7 +3972,7 @@ function getProjectName(cwd) {
|
|
|
3801
3972
|
timeout: 2e3,
|
|
3802
3973
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3803
3974
|
}).trim();
|
|
3804
|
-
repoRoot =
|
|
3975
|
+
repoRoot = path9.dirname(gitCommonDir);
|
|
3805
3976
|
} catch {
|
|
3806
3977
|
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
3807
3978
|
cwd: dir,
|
|
@@ -3810,11 +3981,11 @@ function getProjectName(cwd) {
|
|
|
3810
3981
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3811
3982
|
}).trim();
|
|
3812
3983
|
}
|
|
3813
|
-
_cached =
|
|
3984
|
+
_cached = path9.basename(repoRoot);
|
|
3814
3985
|
_cachedCwd = dir;
|
|
3815
3986
|
return _cached;
|
|
3816
3987
|
} catch {
|
|
3817
|
-
_cached =
|
|
3988
|
+
_cached = path9.basename(dir);
|
|
3818
3989
|
_cachedCwd = dir;
|
|
3819
3990
|
return _cached;
|
|
3820
3991
|
}
|
|
@@ -3838,9 +4009,9 @@ __export(file_grep_exports, {
|
|
|
3838
4009
|
grepProjectFiles: () => grepProjectFiles
|
|
3839
4010
|
});
|
|
3840
4011
|
import { execSync as execSync3 } from "child_process";
|
|
3841
|
-
import { readFileSync as
|
|
3842
|
-
import
|
|
3843
|
-
import
|
|
4012
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync9 } from "fs";
|
|
4013
|
+
import path10 from "path";
|
|
4014
|
+
import crypto3 from "crypto";
|
|
3844
4015
|
function hasRipgrep() {
|
|
3845
4016
|
if (_hasRg === null) {
|
|
3846
4017
|
try {
|
|
@@ -3873,13 +4044,13 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
3873
4044
|
const chunkCtx = getChunkContext(hit.filePath, hit.lineNumber);
|
|
3874
4045
|
const prefix = chunkCtx ? `[file: ${hit.filePath}:${hit.lineNumber} in ${chunkCtx}]` : `[file: ${hit.filePath}:${hit.lineNumber}]`;
|
|
3875
4046
|
return {
|
|
3876
|
-
id:
|
|
4047
|
+
id: crypto3.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
|
|
3877
4048
|
agent_id: "project",
|
|
3878
4049
|
agent_role: "file",
|
|
3879
4050
|
session_id: "file-grep",
|
|
3880
4051
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3881
4052
|
tool_name: "file_grep",
|
|
3882
|
-
project_name:
|
|
4053
|
+
project_name: path10.basename(projectRoot),
|
|
3883
4054
|
has_error: false,
|
|
3884
4055
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
3885
4056
|
vector: null,
|
|
@@ -3891,7 +4062,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
3891
4062
|
try {
|
|
3892
4063
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
3893
4064
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
3894
|
-
const source =
|
|
4065
|
+
const source = readFileSync5(filePath, "utf8");
|
|
3895
4066
|
const lines = source.split("\n");
|
|
3896
4067
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
3897
4068
|
const line = lines[i];
|
|
@@ -3953,11 +4124,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
3953
4124
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
3954
4125
|
const hits = [];
|
|
3955
4126
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
3956
|
-
const absPath =
|
|
4127
|
+
const absPath = path10.join(projectRoot, filePath);
|
|
3957
4128
|
try {
|
|
3958
4129
|
const stat = statSync2(absPath);
|
|
3959
4130
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
3960
|
-
const content =
|
|
4131
|
+
const content = readFileSync5(absPath, "utf8");
|
|
3961
4132
|
const lines = content.split("\n");
|
|
3962
4133
|
const matches = content.match(regex);
|
|
3963
4134
|
if (!matches || matches.length === 0) continue;
|
|
@@ -3980,15 +4151,15 @@ function collectFiles(root, patterns) {
|
|
|
3980
4151
|
const files = [];
|
|
3981
4152
|
function walk(dir, relative) {
|
|
3982
4153
|
if (files.length >= MAX_FILES) return;
|
|
3983
|
-
const basename =
|
|
4154
|
+
const basename = path10.basename(dir);
|
|
3984
4155
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
3985
4156
|
try {
|
|
3986
4157
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
3987
4158
|
for (const entry of entries) {
|
|
3988
4159
|
if (files.length >= MAX_FILES) return;
|
|
3989
|
-
const rel =
|
|
4160
|
+
const rel = path10.join(relative, entry.name);
|
|
3990
4161
|
if (entry.isDirectory()) {
|
|
3991
|
-
walk(
|
|
4162
|
+
walk(path10.join(dir, entry.name), rel);
|
|
3992
4163
|
} else if (entry.isFile()) {
|
|
3993
4164
|
for (const pat of patterns) {
|
|
3994
4165
|
if (matchGlob(rel, pat)) {
|
|
@@ -4020,7 +4191,7 @@ function matchGlob(filePath, pattern) {
|
|
|
4020
4191
|
if (slashIdx !== -1) {
|
|
4021
4192
|
const dir = pattern.slice(0, slashIdx);
|
|
4022
4193
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
4023
|
-
const fileDir =
|
|
4194
|
+
const fileDir = path10.dirname(filePath);
|
|
4024
4195
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
4025
4196
|
}
|
|
4026
4197
|
const ext = pattern.replace("*", "");
|
|
@@ -4028,9 +4199,9 @@ function matchGlob(filePath, pattern) {
|
|
|
4028
4199
|
}
|
|
4029
4200
|
function buildSnippet(hit, projectRoot) {
|
|
4030
4201
|
try {
|
|
4031
|
-
const absPath =
|
|
4032
|
-
if (!
|
|
4033
|
-
const lines =
|
|
4202
|
+
const absPath = path10.join(projectRoot, hit.filePath);
|
|
4203
|
+
if (!existsSync9(absPath)) return hit.matchLine;
|
|
4204
|
+
const lines = readFileSync5(absPath, "utf8").split("\n");
|
|
4034
4205
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
4035
4206
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
4036
4207
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -5274,9 +5445,9 @@ init_database();
|
|
|
5274
5445
|
|
|
5275
5446
|
// src/lib/active-agent.ts
|
|
5276
5447
|
init_config();
|
|
5277
|
-
import { readFileSync as
|
|
5448
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
5278
5449
|
import { execSync as execSync5 } from "child_process";
|
|
5279
|
-
import
|
|
5450
|
+
import path11 from "path";
|
|
5280
5451
|
|
|
5281
5452
|
// src/lib/session-key.ts
|
|
5282
5453
|
import { execSync as execSync4 } from "child_process";
|
|
@@ -5342,7 +5513,7 @@ function getSessionKey() {
|
|
|
5342
5513
|
|
|
5343
5514
|
// src/lib/active-agent.ts
|
|
5344
5515
|
init_employees();
|
|
5345
|
-
var CACHE_DIR =
|
|
5516
|
+
var CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
|
|
5346
5517
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
5347
5518
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
5348
5519
|
if (candidate === baseName) return true;
|
|
@@ -5387,12 +5558,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
5387
5558
|
return null;
|
|
5388
5559
|
}
|
|
5389
5560
|
function getMarkerPath() {
|
|
5390
|
-
return
|
|
5561
|
+
return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
5391
5562
|
}
|
|
5392
5563
|
function getActiveAgent() {
|
|
5393
5564
|
try {
|
|
5394
5565
|
const markerPath = getMarkerPath();
|
|
5395
|
-
const raw =
|
|
5566
|
+
const raw = readFileSync6(markerPath, "utf8");
|
|
5396
5567
|
const data = JSON.parse(raw);
|
|
5397
5568
|
if (data.agentId) {
|
|
5398
5569
|
if (data.startedAt) {
|