@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
|
@@ -78,6 +78,44 @@ var init_db_retry = __esm({
|
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
// src/lib/secure-files.ts
|
|
82
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
83
|
+
import { chmod, mkdir } from "fs/promises";
|
|
84
|
+
async function ensurePrivateDir(dirPath) {
|
|
85
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
86
|
+
try {
|
|
87
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function ensurePrivateDirSync(dirPath) {
|
|
92
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
93
|
+
try {
|
|
94
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function enforcePrivateFile(filePath) {
|
|
99
|
+
try {
|
|
100
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function enforcePrivateFileSync(filePath) {
|
|
105
|
+
try {
|
|
106
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
111
|
+
var init_secure_files = __esm({
|
|
112
|
+
"src/lib/secure-files.ts"() {
|
|
113
|
+
"use strict";
|
|
114
|
+
PRIVATE_DIR_MODE = 448;
|
|
115
|
+
PRIVATE_FILE_MODE = 384;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
81
119
|
// src/lib/config.ts
|
|
82
120
|
var config_exports = {};
|
|
83
121
|
__export(config_exports, {
|
|
@@ -94,8 +132,8 @@ __export(config_exports, {
|
|
|
94
132
|
migrateConfig: () => migrateConfig,
|
|
95
133
|
saveConfig: () => saveConfig
|
|
96
134
|
});
|
|
97
|
-
import { readFile, writeFile
|
|
98
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
135
|
+
import { readFile, writeFile } from "fs/promises";
|
|
136
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
99
137
|
import path2 from "path";
|
|
100
138
|
import os from "os";
|
|
101
139
|
function resolveDataDir() {
|
|
@@ -103,7 +141,7 @@ function resolveDataDir() {
|
|
|
103
141
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
104
142
|
const newDir = path2.join(os.homedir(), ".exe-os");
|
|
105
143
|
const legacyDir = path2.join(os.homedir(), ".exe-mem");
|
|
106
|
-
if (!
|
|
144
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
107
145
|
try {
|
|
108
146
|
renameSync(legacyDir, newDir);
|
|
109
147
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -166,9 +204,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
166
204
|
}
|
|
167
205
|
async function loadConfig() {
|
|
168
206
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
169
|
-
await
|
|
207
|
+
await ensurePrivateDir(dir);
|
|
170
208
|
const configPath = path2.join(dir, "config.json");
|
|
171
|
-
if (!
|
|
209
|
+
if (!existsSync2(configPath)) {
|
|
172
210
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
173
211
|
}
|
|
174
212
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -181,6 +219,7 @@ async function loadConfig() {
|
|
|
181
219
|
`);
|
|
182
220
|
try {
|
|
183
221
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
222
|
+
await enforcePrivateFile(configPath);
|
|
184
223
|
} catch {
|
|
185
224
|
}
|
|
186
225
|
}
|
|
@@ -199,7 +238,7 @@ async function loadConfig() {
|
|
|
199
238
|
function loadConfigSync() {
|
|
200
239
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
201
240
|
const configPath = path2.join(dir, "config.json");
|
|
202
|
-
if (!
|
|
241
|
+
if (!existsSync2(configPath)) {
|
|
203
242
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
204
243
|
}
|
|
205
244
|
try {
|
|
@@ -217,12 +256,10 @@ function loadConfigSync() {
|
|
|
217
256
|
}
|
|
218
257
|
async function saveConfig(config) {
|
|
219
258
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
220
|
-
await
|
|
259
|
+
await ensurePrivateDir(dir);
|
|
221
260
|
const configPath = path2.join(dir, "config.json");
|
|
222
261
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
223
|
-
|
|
224
|
-
await chmod(configPath, 384);
|
|
225
|
-
}
|
|
262
|
+
await enforcePrivateFile(configPath);
|
|
226
263
|
}
|
|
227
264
|
async function loadConfigFrom(configPath) {
|
|
228
265
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -242,6 +279,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
242
279
|
var init_config = __esm({
|
|
243
280
|
"src/lib/config.ts"() {
|
|
244
281
|
"use strict";
|
|
282
|
+
init_secure_files();
|
|
245
283
|
EXE_AI_DIR = resolveDataDir();
|
|
246
284
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
247
285
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -320,7 +358,7 @@ var init_config = __esm({
|
|
|
320
358
|
|
|
321
359
|
// src/lib/employees.ts
|
|
322
360
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
323
|
-
import { existsSync as
|
|
361
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
324
362
|
import { execSync as execSync2 } from "child_process";
|
|
325
363
|
import path3 from "path";
|
|
326
364
|
import os2 from "os";
|
|
@@ -337,7 +375,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
337
375
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
338
376
|
}
|
|
339
377
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
340
|
-
if (!
|
|
378
|
+
if (!existsSync3(employeesPath)) return [];
|
|
341
379
|
try {
|
|
342
380
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
343
381
|
} catch {
|
|
@@ -1285,6 +1323,7 @@ async function ensureSchema() {
|
|
|
1285
1323
|
project TEXT NOT NULL,
|
|
1286
1324
|
summary TEXT NOT NULL,
|
|
1287
1325
|
task_file TEXT,
|
|
1326
|
+
session_scope TEXT,
|
|
1288
1327
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1289
1328
|
created_at TEXT NOT NULL
|
|
1290
1329
|
);
|
|
@@ -1293,7 +1332,7 @@ async function ensureSchema() {
|
|
|
1293
1332
|
ON notifications(read);
|
|
1294
1333
|
|
|
1295
1334
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1296
|
-
ON notifications(agent_id);
|
|
1335
|
+
ON notifications(agent_id, session_scope);
|
|
1297
1336
|
|
|
1298
1337
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1299
1338
|
ON notifications(task_file);
|
|
@@ -1331,6 +1370,7 @@ async function ensureSchema() {
|
|
|
1331
1370
|
target_agent TEXT NOT NULL,
|
|
1332
1371
|
target_project TEXT,
|
|
1333
1372
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1373
|
+
session_scope TEXT,
|
|
1334
1374
|
content TEXT NOT NULL,
|
|
1335
1375
|
priority TEXT DEFAULT 'normal',
|
|
1336
1376
|
status TEXT DEFAULT 'pending',
|
|
@@ -1344,10 +1384,31 @@ async function ensureSchema() {
|
|
|
1344
1384
|
);
|
|
1345
1385
|
|
|
1346
1386
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1347
|
-
ON messages(target_agent, status);
|
|
1387
|
+
ON messages(target_agent, session_scope, status);
|
|
1348
1388
|
|
|
1349
1389
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1350
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1390
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1391
|
+
`);
|
|
1392
|
+
try {
|
|
1393
|
+
await client.execute({
|
|
1394
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1395
|
+
args: []
|
|
1396
|
+
});
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
try {
|
|
1400
|
+
await client.execute({
|
|
1401
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1402
|
+
args: []
|
|
1403
|
+
});
|
|
1404
|
+
} catch {
|
|
1405
|
+
}
|
|
1406
|
+
await client.executeMultiple(`
|
|
1407
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1408
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1409
|
+
|
|
1410
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1411
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1351
1412
|
`);
|
|
1352
1413
|
try {
|
|
1353
1414
|
await client.execute({
|
|
@@ -1931,6 +1992,13 @@ async function ensureSchema() {
|
|
|
1931
1992
|
} catch {
|
|
1932
1993
|
}
|
|
1933
1994
|
}
|
|
1995
|
+
try {
|
|
1996
|
+
await client.execute({
|
|
1997
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1998
|
+
args: []
|
|
1999
|
+
});
|
|
2000
|
+
} catch {
|
|
2001
|
+
}
|
|
1934
2002
|
}
|
|
1935
2003
|
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1936
2004
|
var init_database = __esm({
|
|
@@ -1953,6 +2021,7 @@ var shard_manager_exports = {};
|
|
|
1953
2021
|
__export(shard_manager_exports, {
|
|
1954
2022
|
disposeShards: () => disposeShards,
|
|
1955
2023
|
ensureShardSchema: () => ensureShardSchema,
|
|
2024
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1956
2025
|
getReadyShardClient: () => getReadyShardClient,
|
|
1957
2026
|
getShardClient: () => getShardClient,
|
|
1958
2027
|
getShardsDir: () => getShardsDir,
|
|
@@ -1962,14 +2031,17 @@ __export(shard_manager_exports, {
|
|
|
1962
2031
|
shardExists: () => shardExists
|
|
1963
2032
|
});
|
|
1964
2033
|
import path6 from "path";
|
|
1965
|
-
import { existsSync as
|
|
2034
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1966
2035
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1967
2036
|
function initShardManager(encryptionKey) {
|
|
1968
2037
|
_encryptionKey = encryptionKey;
|
|
1969
|
-
if (!
|
|
1970
|
-
|
|
2038
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
2039
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1971
2040
|
}
|
|
1972
2041
|
_shardingEnabled = true;
|
|
2042
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2043
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2044
|
+
_evictionTimer.unref();
|
|
1973
2045
|
}
|
|
1974
2046
|
function isShardingEnabled() {
|
|
1975
2047
|
return _shardingEnabled;
|
|
@@ -1986,21 +2058,28 @@ function getShardClient(projectName) {
|
|
|
1986
2058
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1987
2059
|
}
|
|
1988
2060
|
const cached = _shards.get(safeName);
|
|
1989
|
-
if (cached)
|
|
2061
|
+
if (cached) {
|
|
2062
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2063
|
+
return cached;
|
|
2064
|
+
}
|
|
2065
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2066
|
+
evictLRU();
|
|
2067
|
+
}
|
|
1990
2068
|
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
1991
2069
|
const client = createClient2({
|
|
1992
2070
|
url: `file:${dbPath}`,
|
|
1993
2071
|
encryptionKey: _encryptionKey
|
|
1994
2072
|
});
|
|
1995
2073
|
_shards.set(safeName, client);
|
|
2074
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1996
2075
|
return client;
|
|
1997
2076
|
}
|
|
1998
2077
|
function shardExists(projectName) {
|
|
1999
2078
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2000
|
-
return
|
|
2079
|
+
return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
2001
2080
|
}
|
|
2002
2081
|
function listShards() {
|
|
2003
|
-
if (!
|
|
2082
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
2004
2083
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2005
2084
|
}
|
|
2006
2085
|
async function ensureShardSchema(client) {
|
|
@@ -2052,6 +2131,8 @@ async function ensureShardSchema(client) {
|
|
|
2052
2131
|
for (const col of [
|
|
2053
2132
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2054
2133
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2134
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2135
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2055
2136
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2056
2137
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2057
2138
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2189,21 +2270,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2189
2270
|
await ensureShardSchema(client);
|
|
2190
2271
|
return client;
|
|
2191
2272
|
}
|
|
2273
|
+
function evictLRU() {
|
|
2274
|
+
let oldest = null;
|
|
2275
|
+
let oldestTime = Infinity;
|
|
2276
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2277
|
+
if (time < oldestTime) {
|
|
2278
|
+
oldestTime = time;
|
|
2279
|
+
oldest = name;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
if (oldest) {
|
|
2283
|
+
const client = _shards.get(oldest);
|
|
2284
|
+
if (client) {
|
|
2285
|
+
client.close();
|
|
2286
|
+
}
|
|
2287
|
+
_shards.delete(oldest);
|
|
2288
|
+
_shardLastAccess.delete(oldest);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
function evictIdleShards() {
|
|
2292
|
+
const now = Date.now();
|
|
2293
|
+
const toEvict = [];
|
|
2294
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2295
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2296
|
+
toEvict.push(name);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
for (const name of toEvict) {
|
|
2300
|
+
const client = _shards.get(name);
|
|
2301
|
+
if (client) {
|
|
2302
|
+
client.close();
|
|
2303
|
+
}
|
|
2304
|
+
_shards.delete(name);
|
|
2305
|
+
_shardLastAccess.delete(name);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
function getOpenShardCount() {
|
|
2309
|
+
return _shards.size;
|
|
2310
|
+
}
|
|
2192
2311
|
function disposeShards() {
|
|
2312
|
+
if (_evictionTimer) {
|
|
2313
|
+
clearInterval(_evictionTimer);
|
|
2314
|
+
_evictionTimer = null;
|
|
2315
|
+
}
|
|
2193
2316
|
for (const [, client] of _shards) {
|
|
2194
2317
|
client.close();
|
|
2195
2318
|
}
|
|
2196
2319
|
_shards.clear();
|
|
2320
|
+
_shardLastAccess.clear();
|
|
2197
2321
|
_shardingEnabled = false;
|
|
2198
2322
|
_encryptionKey = null;
|
|
2199
2323
|
}
|
|
2200
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2324
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2201
2325
|
var init_shard_manager = __esm({
|
|
2202
2326
|
"src/lib/shard-manager.ts"() {
|
|
2203
2327
|
"use strict";
|
|
2204
2328
|
init_config();
|
|
2205
2329
|
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
2330
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2331
|
+
MAX_OPEN_SHARDS = 10;
|
|
2332
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2206
2333
|
_shards = /* @__PURE__ */ new Map();
|
|
2334
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2335
|
+
_evictionTimer = null;
|
|
2207
2336
|
_encryptionKey = null;
|
|
2208
2337
|
_shardingEnabled = false;
|
|
2209
2338
|
}
|
|
@@ -2396,13 +2525,50 @@ ${p.content}`).join("\n\n");
|
|
|
2396
2525
|
}
|
|
2397
2526
|
});
|
|
2398
2527
|
|
|
2528
|
+
// src/lib/daemon-auth.ts
|
|
2529
|
+
import crypto from "crypto";
|
|
2530
|
+
import path9 from "path";
|
|
2531
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2532
|
+
function normalizeToken(token) {
|
|
2533
|
+
if (!token) return null;
|
|
2534
|
+
const trimmed = token.trim();
|
|
2535
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2536
|
+
}
|
|
2537
|
+
function readDaemonToken() {
|
|
2538
|
+
try {
|
|
2539
|
+
if (!existsSync8(DAEMON_TOKEN_PATH)) return null;
|
|
2540
|
+
return normalizeToken(readFileSync5(DAEMON_TOKEN_PATH, "utf8"));
|
|
2541
|
+
} catch {
|
|
2542
|
+
return null;
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
function ensureDaemonToken(seed) {
|
|
2546
|
+
const existing = readDaemonToken();
|
|
2547
|
+
if (existing) return existing;
|
|
2548
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
2549
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2550
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
2551
|
+
`, "utf8");
|
|
2552
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2553
|
+
return token;
|
|
2554
|
+
}
|
|
2555
|
+
var DAEMON_TOKEN_PATH;
|
|
2556
|
+
var init_daemon_auth = __esm({
|
|
2557
|
+
"src/lib/daemon-auth.ts"() {
|
|
2558
|
+
"use strict";
|
|
2559
|
+
init_config();
|
|
2560
|
+
init_secure_files();
|
|
2561
|
+
DAEMON_TOKEN_PATH = path9.join(EXE_AI_DIR, "exed.token");
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2399
2565
|
// src/lib/exe-daemon-client.ts
|
|
2400
2566
|
import net from "net";
|
|
2401
|
-
import
|
|
2567
|
+
import os6 from "os";
|
|
2402
2568
|
import { spawn } from "child_process";
|
|
2403
2569
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2404
|
-
import { existsSync as
|
|
2405
|
-
import
|
|
2570
|
+
import { existsSync as existsSync9, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
2571
|
+
import path10 from "path";
|
|
2406
2572
|
import { fileURLToPath } from "url";
|
|
2407
2573
|
function handleData(chunk) {
|
|
2408
2574
|
_buffer += chunk.toString();
|
|
@@ -2430,9 +2596,9 @@ function handleData(chunk) {
|
|
|
2430
2596
|
}
|
|
2431
2597
|
}
|
|
2432
2598
|
function cleanupStaleFiles() {
|
|
2433
|
-
if (
|
|
2599
|
+
if (existsSync9(PID_PATH)) {
|
|
2434
2600
|
try {
|
|
2435
|
-
const pid = parseInt(
|
|
2601
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
2436
2602
|
if (pid > 0) {
|
|
2437
2603
|
try {
|
|
2438
2604
|
process.kill(pid, 0);
|
|
@@ -2453,17 +2619,17 @@ function cleanupStaleFiles() {
|
|
|
2453
2619
|
}
|
|
2454
2620
|
}
|
|
2455
2621
|
function findPackageRoot() {
|
|
2456
|
-
let dir =
|
|
2457
|
-
const { root } =
|
|
2622
|
+
let dir = path10.dirname(fileURLToPath(import.meta.url));
|
|
2623
|
+
const { root } = path10.parse(dir);
|
|
2458
2624
|
while (dir !== root) {
|
|
2459
|
-
if (
|
|
2460
|
-
dir =
|
|
2625
|
+
if (existsSync9(path10.join(dir, "package.json"))) return dir;
|
|
2626
|
+
dir = path10.dirname(dir);
|
|
2461
2627
|
}
|
|
2462
2628
|
return null;
|
|
2463
2629
|
}
|
|
2464
2630
|
function spawnDaemon() {
|
|
2465
|
-
const freeGB =
|
|
2466
|
-
const totalGB =
|
|
2631
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
2632
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
2467
2633
|
if (totalGB <= 8) {
|
|
2468
2634
|
process.stderr.write(
|
|
2469
2635
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -2483,16 +2649,17 @@ function spawnDaemon() {
|
|
|
2483
2649
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2484
2650
|
return;
|
|
2485
2651
|
}
|
|
2486
|
-
const daemonPath =
|
|
2487
|
-
if (!
|
|
2652
|
+
const daemonPath = path10.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2653
|
+
if (!existsSync9(daemonPath)) {
|
|
2488
2654
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2489
2655
|
`);
|
|
2490
2656
|
return;
|
|
2491
2657
|
}
|
|
2492
2658
|
const resolvedPath = daemonPath;
|
|
2659
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2493
2660
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2494
2661
|
`);
|
|
2495
|
-
const logPath =
|
|
2662
|
+
const logPath = path10.join(path10.dirname(SOCKET_PATH), "exed.log");
|
|
2496
2663
|
let stderrFd = "ignore";
|
|
2497
2664
|
try {
|
|
2498
2665
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2510,7 +2677,8 @@ function spawnDaemon() {
|
|
|
2510
2677
|
TMUX_PANE: void 0,
|
|
2511
2678
|
// Prevents resolveExeSession() from scoping to one session
|
|
2512
2679
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2513
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2680
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2681
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2514
2682
|
}
|
|
2515
2683
|
});
|
|
2516
2684
|
child.unref();
|
|
@@ -2620,13 +2788,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2620
2788
|
return;
|
|
2621
2789
|
}
|
|
2622
2790
|
const id = randomUUID3();
|
|
2791
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2623
2792
|
const timer = setTimeout(() => {
|
|
2624
2793
|
_pending.delete(id);
|
|
2625
2794
|
resolve({ error: "Request timeout" });
|
|
2626
2795
|
}, timeoutMs);
|
|
2627
2796
|
_pending.set(id, { resolve, timer });
|
|
2628
2797
|
try {
|
|
2629
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2798
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2630
2799
|
} catch {
|
|
2631
2800
|
clearTimeout(timer);
|
|
2632
2801
|
_pending.delete(id);
|
|
@@ -2655,9 +2824,9 @@ function killAndRespawnDaemon() {
|
|
|
2655
2824
|
}
|
|
2656
2825
|
try {
|
|
2657
2826
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2658
|
-
if (
|
|
2827
|
+
if (existsSync9(PID_PATH)) {
|
|
2659
2828
|
try {
|
|
2660
|
-
const pid = parseInt(
|
|
2829
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
2661
2830
|
if (pid > 0) {
|
|
2662
2831
|
try {
|
|
2663
2832
|
process.kill(pid, "SIGKILL");
|
|
@@ -2774,17 +2943,19 @@ function disconnectClient() {
|
|
|
2774
2943
|
entry.resolve({ error: "Client disconnected" });
|
|
2775
2944
|
}
|
|
2776
2945
|
}
|
|
2777
|
-
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;
|
|
2946
|
+
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;
|
|
2778
2947
|
var init_exe_daemon_client = __esm({
|
|
2779
2948
|
"src/lib/exe-daemon-client.ts"() {
|
|
2780
2949
|
"use strict";
|
|
2781
2950
|
init_config();
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2951
|
+
init_daemon_auth();
|
|
2952
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path10.join(EXE_AI_DIR, "exed.sock");
|
|
2953
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path10.join(EXE_AI_DIR, "exed.pid");
|
|
2954
|
+
SPAWN_LOCK_PATH = path10.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2785
2955
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2786
2956
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2787
2957
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2958
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2788
2959
|
_socket = null;
|
|
2789
2960
|
_connected = false;
|
|
2790
2961
|
_buffer = "";
|
|
@@ -2836,10 +3007,10 @@ async function disposeEmbedder() {
|
|
|
2836
3007
|
async function embedDirect(text) {
|
|
2837
3008
|
const llamaCpp = await import("node-llama-cpp");
|
|
2838
3009
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2839
|
-
const { existsSync:
|
|
2840
|
-
const
|
|
2841
|
-
const modelPath =
|
|
2842
|
-
if (!
|
|
3010
|
+
const { existsSync: existsSync10 } = await import("fs");
|
|
3011
|
+
const path12 = await import("path");
|
|
3012
|
+
const modelPath = path12.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3013
|
+
if (!existsSync10(modelPath)) {
|
|
2843
3014
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2844
3015
|
}
|
|
2845
3016
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2868,9 +3039,9 @@ var init_embedder = __esm({
|
|
|
2868
3039
|
});
|
|
2869
3040
|
|
|
2870
3041
|
// src/adapters/claude/hooks/prompt-ingest-worker.ts
|
|
2871
|
-
import
|
|
2872
|
-
import { writeFileSync as
|
|
2873
|
-
import
|
|
3042
|
+
import crypto2 from "crypto";
|
|
3043
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3044
|
+
import path11 from "path";
|
|
2874
3045
|
|
|
2875
3046
|
// src/lib/project-name.ts
|
|
2876
3047
|
import { execSync } from "child_process";
|
|
@@ -2915,7 +3086,7 @@ import { createHash } from "crypto";
|
|
|
2915
3086
|
|
|
2916
3087
|
// src/lib/keychain.ts
|
|
2917
3088
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2918
|
-
import { existsSync as
|
|
3089
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2919
3090
|
import path5 from "path";
|
|
2920
3091
|
import os4 from "os";
|
|
2921
3092
|
var SERVICE = "exe-mem";
|
|
@@ -2945,7 +3116,7 @@ async function getMasterKey() {
|
|
|
2945
3116
|
}
|
|
2946
3117
|
}
|
|
2947
3118
|
const keyPath = getKeyPath();
|
|
2948
|
-
if (!
|
|
3119
|
+
if (!existsSync4(keyPath)) {
|
|
2949
3120
|
process.stderr.write(
|
|
2950
3121
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2951
3122
|
`
|
|
@@ -3371,13 +3542,16 @@ function vectorToBlob(vector) {
|
|
|
3371
3542
|
// src/lib/plan-limits.ts
|
|
3372
3543
|
init_database();
|
|
3373
3544
|
init_employees();
|
|
3374
|
-
import { readFileSync as readFileSync4, existsSync as
|
|
3545
|
+
import { readFileSync as readFileSync4, existsSync as existsSync7 } from "fs";
|
|
3375
3546
|
import path8 from "path";
|
|
3376
3547
|
|
|
3377
3548
|
// src/lib/license.ts
|
|
3378
3549
|
init_config();
|
|
3379
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as
|
|
3550
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
3380
3551
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3552
|
+
import { createRequire as createRequire2 } from "module";
|
|
3553
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3554
|
+
import os5 from "os";
|
|
3381
3555
|
import path7 from "path";
|
|
3382
3556
|
import { jwtVerify, importSPKI } from "jose";
|
|
3383
3557
|
var LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
@@ -3417,34 +3591,34 @@ var FREE_LICENSE = {
|
|
|
3417
3591
|
function loadDeviceId() {
|
|
3418
3592
|
const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
|
|
3419
3593
|
try {
|
|
3420
|
-
if (
|
|
3594
|
+
if (existsSync6(deviceJsonPath)) {
|
|
3421
3595
|
const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
|
|
3422
3596
|
if (data.deviceId) return data.deviceId;
|
|
3423
3597
|
}
|
|
3424
3598
|
} catch {
|
|
3425
3599
|
}
|
|
3426
3600
|
try {
|
|
3427
|
-
if (
|
|
3601
|
+
if (existsSync6(DEVICE_ID_PATH)) {
|
|
3428
3602
|
const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
|
|
3429
3603
|
if (id2) return id2;
|
|
3430
3604
|
}
|
|
3431
3605
|
} catch {
|
|
3432
3606
|
}
|
|
3433
3607
|
const id = randomUUID2();
|
|
3434
|
-
|
|
3608
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
3435
3609
|
writeFileSync2(DEVICE_ID_PATH, id, "utf8");
|
|
3436
3610
|
return id;
|
|
3437
3611
|
}
|
|
3438
3612
|
function loadLicense() {
|
|
3439
3613
|
try {
|
|
3440
|
-
if (!
|
|
3614
|
+
if (!existsSync6(LICENSE_PATH)) return null;
|
|
3441
3615
|
return readFileSync3(LICENSE_PATH, "utf8").trim();
|
|
3442
3616
|
} catch {
|
|
3443
3617
|
return null;
|
|
3444
3618
|
}
|
|
3445
3619
|
}
|
|
3446
3620
|
function saveLicense(apiKey) {
|
|
3447
|
-
|
|
3621
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
3448
3622
|
writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3449
3623
|
}
|
|
3450
3624
|
async function verifyLicenseJwt(token) {
|
|
@@ -3471,7 +3645,7 @@ async function verifyLicenseJwt(token) {
|
|
|
3471
3645
|
}
|
|
3472
3646
|
async function getCachedLicense() {
|
|
3473
3647
|
try {
|
|
3474
|
-
if (!
|
|
3648
|
+
if (!existsSync6(CACHE_PATH)) return null;
|
|
3475
3649
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
3476
3650
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3477
3651
|
return await verifyLicenseJwt(raw.token);
|
|
@@ -3481,7 +3655,7 @@ async function getCachedLicense() {
|
|
|
3481
3655
|
}
|
|
3482
3656
|
function readCachedToken() {
|
|
3483
3657
|
try {
|
|
3484
|
-
if (!
|
|
3658
|
+
if (!existsSync6(CACHE_PATH)) return null;
|
|
3485
3659
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
3486
3660
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3487
3661
|
} catch {
|
|
@@ -3520,52 +3694,128 @@ function cacheResponse(token) {
|
|
|
3520
3694
|
} catch {
|
|
3521
3695
|
}
|
|
3522
3696
|
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3697
|
+
var _prismaPromise = null;
|
|
3698
|
+
var _prismaFailed = false;
|
|
3699
|
+
function loadPrismaForLicense() {
|
|
3700
|
+
if (_prismaFailed) return null;
|
|
3701
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
3702
|
+
if (!dbUrl) {
|
|
3703
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
|
|
3704
|
+
if (!existsSync6(path7.join(exeDbRoot, "package.json"))) {
|
|
3705
|
+
_prismaFailed = true;
|
|
3706
|
+
return null;
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
if (!_prismaPromise) {
|
|
3710
|
+
_prismaPromise = (async () => {
|
|
3711
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
3712
|
+
if (explicitPath) {
|
|
3713
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
3714
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
3715
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
3716
|
+
return new Ctor2();
|
|
3717
|
+
}
|
|
3718
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
|
|
3719
|
+
const req = createRequire2(path7.join(exeDbRoot, "package.json"));
|
|
3720
|
+
const entry = req.resolve("@prisma/client");
|
|
3721
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
3722
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3723
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
3724
|
+
return new Ctor();
|
|
3725
|
+
})().catch((err) => {
|
|
3726
|
+
_prismaFailed = true;
|
|
3727
|
+
_prismaPromise = null;
|
|
3728
|
+
throw err;
|
|
3729
|
+
});
|
|
3730
|
+
}
|
|
3731
|
+
return _prismaPromise;
|
|
3732
|
+
}
|
|
3733
|
+
async function validateViaPostgres(apiKey) {
|
|
3734
|
+
const loader = loadPrismaForLicense();
|
|
3735
|
+
if (!loader) return null;
|
|
3736
|
+
try {
|
|
3737
|
+
const prisma = await loader;
|
|
3738
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
3739
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
3740
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
3741
|
+
apiKey
|
|
3742
|
+
);
|
|
3743
|
+
if (!rows || rows.length === 0) return null;
|
|
3744
|
+
const row = rows[0];
|
|
3745
|
+
if (row.status !== "active") return null;
|
|
3746
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
3747
|
+
const plan = row.plan;
|
|
3748
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
3749
|
+
return {
|
|
3750
|
+
valid: true,
|
|
3751
|
+
plan,
|
|
3752
|
+
email: row.email,
|
|
3753
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
3754
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
3755
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
3756
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
3757
|
+
};
|
|
3758
|
+
} catch {
|
|
3759
|
+
return null;
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
3525
3763
|
try {
|
|
3526
3764
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3527
3765
|
method: "POST",
|
|
3528
3766
|
headers: { "Content-Type": "application/json" },
|
|
3529
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
3767
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
3530
3768
|
signal: AbortSignal.timeout(1e4)
|
|
3531
3769
|
});
|
|
3532
|
-
if (res.ok)
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3770
|
+
if (!res.ok) return null;
|
|
3771
|
+
const data = await res.json();
|
|
3772
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
3773
|
+
if (!data.valid) return null;
|
|
3774
|
+
if (data.token) {
|
|
3775
|
+
cacheResponse(data.token);
|
|
3776
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
3777
|
+
if (verified) return verified;
|
|
3778
|
+
}
|
|
3779
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3780
|
+
return {
|
|
3781
|
+
valid: data.valid,
|
|
3782
|
+
plan: data.plan,
|
|
3783
|
+
email: data.email,
|
|
3784
|
+
expiresAt: data.expiresAt,
|
|
3785
|
+
deviceLimit: limits.devices,
|
|
3786
|
+
employeeLimit: limits.employees,
|
|
3787
|
+
memoryLimit: limits.memories
|
|
3788
|
+
};
|
|
3789
|
+
} catch {
|
|
3790
|
+
return null;
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
async function validateLicense(apiKey, deviceId) {
|
|
3794
|
+
const did = deviceId ?? loadDeviceId();
|
|
3795
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
3796
|
+
if (pgResult) {
|
|
3797
|
+
try {
|
|
3798
|
+
writeFileSync2(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
3799
|
+
} catch {
|
|
3800
|
+
}
|
|
3801
|
+
return pgResult;
|
|
3802
|
+
}
|
|
3803
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
3804
|
+
if (cfResult) return cfResult;
|
|
3805
|
+
const cached = await getCachedLicense();
|
|
3806
|
+
if (cached) return cached;
|
|
3807
|
+
try {
|
|
3808
|
+
if (existsSync6(CACHE_PATH)) {
|
|
3809
|
+
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
3810
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
3811
|
+
return raw.pgLicense;
|
|
3545
3812
|
}
|
|
3546
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
3547
|
-
return {
|
|
3548
|
-
valid: data.valid,
|
|
3549
|
-
plan: data.plan,
|
|
3550
|
-
email: data.email,
|
|
3551
|
-
expiresAt: data.expiresAt,
|
|
3552
|
-
deviceLimit: limits.devices,
|
|
3553
|
-
employeeLimit: limits.employees,
|
|
3554
|
-
memoryLimit: limits.memories
|
|
3555
|
-
};
|
|
3556
3813
|
}
|
|
3557
|
-
const cached = await getCachedLicense();
|
|
3558
|
-
if (cached) return cached;
|
|
3559
|
-
const raw = getRawCachedPlan();
|
|
3560
|
-
if (raw) return raw;
|
|
3561
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
3562
3814
|
} catch {
|
|
3563
|
-
const cached = await getCachedLicense();
|
|
3564
|
-
if (cached) return cached;
|
|
3565
|
-
const rawFallback = getRawCachedPlan();
|
|
3566
|
-
if (rawFallback) return rawFallback;
|
|
3567
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
3568
3815
|
}
|
|
3816
|
+
const rawFallback = getRawCachedPlan();
|
|
3817
|
+
if (rawFallback) return rawFallback;
|
|
3818
|
+
return { ...FREE_LICENSE, valid: false };
|
|
3569
3819
|
}
|
|
3570
3820
|
var CACHE_MAX_AGE_MS = 36e5;
|
|
3571
3821
|
function getCacheAgeMs() {
|
|
@@ -3582,7 +3832,7 @@ async function checkLicense() {
|
|
|
3582
3832
|
if (!key) {
|
|
3583
3833
|
try {
|
|
3584
3834
|
const configPath = path7.join(EXE_AI_DIR, "config.json");
|
|
3585
|
-
if (
|
|
3835
|
+
if (existsSync6(configPath)) {
|
|
3586
3836
|
const raw = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
3587
3837
|
const cloud = raw.cloud;
|
|
3588
3838
|
if (cloud?.apiKey) {
|
|
@@ -3655,7 +3905,7 @@ async function main() {
|
|
|
3655
3905
|
await assertMemoryLimit();
|
|
3656
3906
|
const confidence = agentId === "default" ? 0.9 : 0.7;
|
|
3657
3907
|
await writeMemory({
|
|
3658
|
-
id:
|
|
3908
|
+
id: crypto2.randomUUID(),
|
|
3659
3909
|
agent_id: agentId,
|
|
3660
3910
|
agent_role: agentRole,
|
|
3661
3911
|
session_id: sessionId,
|
|
@@ -3671,8 +3921,8 @@ async function main() {
|
|
|
3671
3921
|
if (needsBackfill) {
|
|
3672
3922
|
try {
|
|
3673
3923
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3674
|
-
const flagPath =
|
|
3675
|
-
|
|
3924
|
+
const flagPath = path11.join(exeDir, "session-cache", "needs-backfill");
|
|
3925
|
+
writeFileSync4(flagPath, "1");
|
|
3676
3926
|
} catch (err) {
|
|
3677
3927
|
process.stderr.write(`[prompt-ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
|
|
3678
3928
|
`);
|