@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
2
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3
5
|
var __esm = (fn, res) => function __init() {
|
|
4
6
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
7
|
};
|
|
@@ -7,6 +9,15 @@ var __export = (target, all) => {
|
|
|
7
9
|
for (var name in all)
|
|
8
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
11
|
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
10
21
|
|
|
11
22
|
// src/lib/db-retry.ts
|
|
12
23
|
function isBusyError(err) {
|
|
@@ -63,9 +74,34 @@ var init_db_retry = __esm({
|
|
|
63
74
|
}
|
|
64
75
|
});
|
|
65
76
|
|
|
77
|
+
// src/lib/secure-files.ts
|
|
78
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
79
|
+
import { chmod, mkdir } from "fs/promises";
|
|
80
|
+
async function ensurePrivateDir(dirPath) {
|
|
81
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
82
|
+
try {
|
|
83
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function enforcePrivateFile(filePath) {
|
|
88
|
+
try {
|
|
89
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
94
|
+
var init_secure_files = __esm({
|
|
95
|
+
"src/lib/secure-files.ts"() {
|
|
96
|
+
"use strict";
|
|
97
|
+
PRIVATE_DIR_MODE = 448;
|
|
98
|
+
PRIVATE_FILE_MODE = 384;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
66
102
|
// src/lib/config.ts
|
|
67
|
-
import { readFile, writeFile
|
|
68
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
103
|
+
import { readFile, writeFile } from "fs/promises";
|
|
104
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
69
105
|
import path from "path";
|
|
70
106
|
import os from "os";
|
|
71
107
|
function resolveDataDir() {
|
|
@@ -73,7 +109,7 @@ function resolveDataDir() {
|
|
|
73
109
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
74
110
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
75
111
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
76
|
-
if (!
|
|
112
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
77
113
|
try {
|
|
78
114
|
renameSync(legacyDir, newDir);
|
|
79
115
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -136,9 +172,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
136
172
|
}
|
|
137
173
|
async function loadConfig() {
|
|
138
174
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
139
|
-
await
|
|
175
|
+
await ensurePrivateDir(dir);
|
|
140
176
|
const configPath = path.join(dir, "config.json");
|
|
141
|
-
if (!
|
|
177
|
+
if (!existsSync2(configPath)) {
|
|
142
178
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
143
179
|
}
|
|
144
180
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -151,6 +187,7 @@ async function loadConfig() {
|
|
|
151
187
|
`);
|
|
152
188
|
try {
|
|
153
189
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
190
|
+
await enforcePrivateFile(configPath);
|
|
154
191
|
} catch {
|
|
155
192
|
}
|
|
156
193
|
}
|
|
@@ -170,6 +207,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
170
207
|
var init_config = __esm({
|
|
171
208
|
"src/lib/config.ts"() {
|
|
172
209
|
"use strict";
|
|
210
|
+
init_secure_files();
|
|
173
211
|
EXE_AI_DIR = resolveDataDir();
|
|
174
212
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
175
213
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -248,7 +286,7 @@ var init_config = __esm({
|
|
|
248
286
|
|
|
249
287
|
// src/lib/employees.ts
|
|
250
288
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
251
|
-
import { existsSync as
|
|
289
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
252
290
|
import { execSync } from "child_process";
|
|
253
291
|
import path2 from "path";
|
|
254
292
|
import os2 from "os";
|
|
@@ -265,7 +303,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
265
303
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
266
304
|
}
|
|
267
305
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
268
|
-
if (!
|
|
306
|
+
if (!existsSync3(employeesPath)) return [];
|
|
269
307
|
try {
|
|
270
308
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
271
309
|
} catch {
|
|
@@ -1210,6 +1248,7 @@ async function ensureSchema() {
|
|
|
1210
1248
|
project TEXT NOT NULL,
|
|
1211
1249
|
summary TEXT NOT NULL,
|
|
1212
1250
|
task_file TEXT,
|
|
1251
|
+
session_scope TEXT,
|
|
1213
1252
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1214
1253
|
created_at TEXT NOT NULL
|
|
1215
1254
|
);
|
|
@@ -1218,7 +1257,7 @@ async function ensureSchema() {
|
|
|
1218
1257
|
ON notifications(read);
|
|
1219
1258
|
|
|
1220
1259
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1221
|
-
ON notifications(agent_id);
|
|
1260
|
+
ON notifications(agent_id, session_scope);
|
|
1222
1261
|
|
|
1223
1262
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1224
1263
|
ON notifications(task_file);
|
|
@@ -1256,6 +1295,7 @@ async function ensureSchema() {
|
|
|
1256
1295
|
target_agent TEXT NOT NULL,
|
|
1257
1296
|
target_project TEXT,
|
|
1258
1297
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1298
|
+
session_scope TEXT,
|
|
1259
1299
|
content TEXT NOT NULL,
|
|
1260
1300
|
priority TEXT DEFAULT 'normal',
|
|
1261
1301
|
status TEXT DEFAULT 'pending',
|
|
@@ -1269,10 +1309,31 @@ async function ensureSchema() {
|
|
|
1269
1309
|
);
|
|
1270
1310
|
|
|
1271
1311
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1272
|
-
ON messages(target_agent, status);
|
|
1312
|
+
ON messages(target_agent, session_scope, status);
|
|
1273
1313
|
|
|
1274
1314
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1275
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1315
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1316
|
+
`);
|
|
1317
|
+
try {
|
|
1318
|
+
await client.execute({
|
|
1319
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1320
|
+
args: []
|
|
1321
|
+
});
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
try {
|
|
1325
|
+
await client.execute({
|
|
1326
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1327
|
+
args: []
|
|
1328
|
+
});
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
await client.executeMultiple(`
|
|
1332
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1333
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1334
|
+
|
|
1335
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1336
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1276
1337
|
`);
|
|
1277
1338
|
try {
|
|
1278
1339
|
await client.execute({
|
|
@@ -1856,6 +1917,13 @@ async function ensureSchema() {
|
|
|
1856
1917
|
} catch {
|
|
1857
1918
|
}
|
|
1858
1919
|
}
|
|
1920
|
+
try {
|
|
1921
|
+
await client.execute({
|
|
1922
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
1923
|
+
args: []
|
|
1924
|
+
});
|
|
1925
|
+
} catch {
|
|
1926
|
+
}
|
|
1859
1927
|
}
|
|
1860
1928
|
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
|
|
1861
1929
|
var init_database = __esm({
|
|
@@ -1933,6 +2001,7 @@ var shard_manager_exports = {};
|
|
|
1933
2001
|
__export(shard_manager_exports, {
|
|
1934
2002
|
disposeShards: () => disposeShards,
|
|
1935
2003
|
ensureShardSchema: () => ensureShardSchema,
|
|
2004
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1936
2005
|
getReadyShardClient: () => getReadyShardClient,
|
|
1937
2006
|
getShardClient: () => getShardClient,
|
|
1938
2007
|
getShardsDir: () => getShardsDir,
|
|
@@ -1942,14 +2011,17 @@ __export(shard_manager_exports, {
|
|
|
1942
2011
|
shardExists: () => shardExists
|
|
1943
2012
|
});
|
|
1944
2013
|
import path5 from "path";
|
|
1945
|
-
import { existsSync as
|
|
2014
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1946
2015
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1947
2016
|
function initShardManager(encryptionKey) {
|
|
1948
2017
|
_encryptionKey = encryptionKey;
|
|
1949
|
-
if (!
|
|
1950
|
-
|
|
2018
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
2019
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1951
2020
|
}
|
|
1952
2021
|
_shardingEnabled = true;
|
|
2022
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2023
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2024
|
+
_evictionTimer.unref();
|
|
1953
2025
|
}
|
|
1954
2026
|
function isShardingEnabled() {
|
|
1955
2027
|
return _shardingEnabled;
|
|
@@ -1966,21 +2038,28 @@ function getShardClient(projectName) {
|
|
|
1966
2038
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1967
2039
|
}
|
|
1968
2040
|
const cached = _shards.get(safeName);
|
|
1969
|
-
if (cached)
|
|
2041
|
+
if (cached) {
|
|
2042
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2043
|
+
return cached;
|
|
2044
|
+
}
|
|
2045
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2046
|
+
evictLRU();
|
|
2047
|
+
}
|
|
1970
2048
|
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1971
2049
|
const client = createClient2({
|
|
1972
2050
|
url: `file:${dbPath}`,
|
|
1973
2051
|
encryptionKey: _encryptionKey
|
|
1974
2052
|
});
|
|
1975
2053
|
_shards.set(safeName, client);
|
|
2054
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1976
2055
|
return client;
|
|
1977
2056
|
}
|
|
1978
2057
|
function shardExists(projectName) {
|
|
1979
2058
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1980
|
-
return
|
|
2059
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1981
2060
|
}
|
|
1982
2061
|
function listShards() {
|
|
1983
|
-
if (!
|
|
2062
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1984
2063
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1985
2064
|
}
|
|
1986
2065
|
async function ensureShardSchema(client) {
|
|
@@ -2032,6 +2111,8 @@ async function ensureShardSchema(client) {
|
|
|
2032
2111
|
for (const col of [
|
|
2033
2112
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2034
2113
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2114
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2115
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2035
2116
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2036
2117
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2037
2118
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2169,21 +2250,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2169
2250
|
await ensureShardSchema(client);
|
|
2170
2251
|
return client;
|
|
2171
2252
|
}
|
|
2253
|
+
function evictLRU() {
|
|
2254
|
+
let oldest = null;
|
|
2255
|
+
let oldestTime = Infinity;
|
|
2256
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2257
|
+
if (time < oldestTime) {
|
|
2258
|
+
oldestTime = time;
|
|
2259
|
+
oldest = name;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
if (oldest) {
|
|
2263
|
+
const client = _shards.get(oldest);
|
|
2264
|
+
if (client) {
|
|
2265
|
+
client.close();
|
|
2266
|
+
}
|
|
2267
|
+
_shards.delete(oldest);
|
|
2268
|
+
_shardLastAccess.delete(oldest);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
function evictIdleShards() {
|
|
2272
|
+
const now = Date.now();
|
|
2273
|
+
const toEvict = [];
|
|
2274
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2275
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2276
|
+
toEvict.push(name);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
for (const name of toEvict) {
|
|
2280
|
+
const client = _shards.get(name);
|
|
2281
|
+
if (client) {
|
|
2282
|
+
client.close();
|
|
2283
|
+
}
|
|
2284
|
+
_shards.delete(name);
|
|
2285
|
+
_shardLastAccess.delete(name);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
function getOpenShardCount() {
|
|
2289
|
+
return _shards.size;
|
|
2290
|
+
}
|
|
2172
2291
|
function disposeShards() {
|
|
2292
|
+
if (_evictionTimer) {
|
|
2293
|
+
clearInterval(_evictionTimer);
|
|
2294
|
+
_evictionTimer = null;
|
|
2295
|
+
}
|
|
2173
2296
|
for (const [, client] of _shards) {
|
|
2174
2297
|
client.close();
|
|
2175
2298
|
}
|
|
2176
2299
|
_shards.clear();
|
|
2300
|
+
_shardLastAccess.clear();
|
|
2177
2301
|
_shardingEnabled = false;
|
|
2178
2302
|
_encryptionKey = null;
|
|
2179
2303
|
}
|
|
2180
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2304
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2181
2305
|
var init_shard_manager = __esm({
|
|
2182
2306
|
"src/lib/shard-manager.ts"() {
|
|
2183
2307
|
"use strict";
|
|
2184
2308
|
init_config();
|
|
2185
2309
|
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
2310
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2311
|
+
MAX_OPEN_SHARDS = 10;
|
|
2312
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2186
2313
|
_shards = /* @__PURE__ */ new Map();
|
|
2314
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2315
|
+
_evictionTimer = null;
|
|
2187
2316
|
_encryptionKey = null;
|
|
2188
2317
|
_shardingEnabled = false;
|
|
2189
2318
|
}
|
|
@@ -2389,16 +2518,176 @@ var init_session_registry = __esm({
|
|
|
2389
2518
|
|
|
2390
2519
|
// src/lib/session-key.ts
|
|
2391
2520
|
import { execSync as execSync2 } from "child_process";
|
|
2521
|
+
function normalizeCommand(command) {
|
|
2522
|
+
const trimmed = command.trim().toLowerCase();
|
|
2523
|
+
const parts = trimmed.split(/[\\/]/);
|
|
2524
|
+
return parts[parts.length - 1] ?? trimmed;
|
|
2525
|
+
}
|
|
2526
|
+
function detectRuntimeFromCommand(command) {
|
|
2527
|
+
const normalized = normalizeCommand(command);
|
|
2528
|
+
for (const [runtime, commands] of Object.entries(RUNTIME_COMMANDS)) {
|
|
2529
|
+
if (commands.includes(normalized)) {
|
|
2530
|
+
return runtime;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
return null;
|
|
2534
|
+
}
|
|
2535
|
+
function resolveRuntimeProcess() {
|
|
2536
|
+
let pid = process.ppid;
|
|
2537
|
+
for (let i = 0; i < 10; i++) {
|
|
2538
|
+
try {
|
|
2539
|
+
const info = execSync2(`ps -p ${pid} -o ppid=,comm=`, {
|
|
2540
|
+
encoding: "utf8",
|
|
2541
|
+
timeout: 2e3
|
|
2542
|
+
}).trim();
|
|
2543
|
+
const match = info.match(/^\s*(\d+)\s+(.+)$/);
|
|
2544
|
+
if (!match) break;
|
|
2545
|
+
const [, ppid, cmd] = match;
|
|
2546
|
+
const runtime = detectRuntimeFromCommand(cmd ?? "");
|
|
2547
|
+
if (runtime) {
|
|
2548
|
+
return { pid: String(pid), runtime };
|
|
2549
|
+
}
|
|
2550
|
+
pid = parseInt(ppid, 10);
|
|
2551
|
+
if (pid <= 1) break;
|
|
2552
|
+
} catch {
|
|
2553
|
+
break;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
function getSessionKey() {
|
|
2559
|
+
if (_cached) return _cached;
|
|
2560
|
+
if (process.env.EXE_SESSION_KEY) {
|
|
2561
|
+
_cached = process.env.EXE_SESSION_KEY;
|
|
2562
|
+
return _cached;
|
|
2563
|
+
}
|
|
2564
|
+
const resolved = resolveRuntimeProcess();
|
|
2565
|
+
if (resolved) {
|
|
2566
|
+
_cachedRuntime = resolved.runtime;
|
|
2567
|
+
_cached = resolved.pid;
|
|
2568
|
+
return _cached;
|
|
2569
|
+
}
|
|
2570
|
+
_cached = process.env.CLAUDE_CODE_SSE_PORT ?? String(process.ppid);
|
|
2571
|
+
return _cached;
|
|
2572
|
+
}
|
|
2573
|
+
var _cached, _cachedRuntime, RUNTIME_COMMANDS;
|
|
2392
2574
|
var init_session_key = __esm({
|
|
2393
2575
|
"src/lib/session-key.ts"() {
|
|
2394
2576
|
"use strict";
|
|
2577
|
+
_cached = null;
|
|
2578
|
+
_cachedRuntime = null;
|
|
2579
|
+
RUNTIME_COMMANDS = {
|
|
2580
|
+
claude: ["claude", "claude.exe", "claude-native"],
|
|
2581
|
+
codex: ["codex"],
|
|
2582
|
+
opencode: ["opencode"]
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
|
|
2587
|
+
// src/lib/tmux-transport.ts
|
|
2588
|
+
var tmux_transport_exports = {};
|
|
2589
|
+
__export(tmux_transport_exports, {
|
|
2590
|
+
TmuxTransport: () => TmuxTransport
|
|
2591
|
+
});
|
|
2592
|
+
import { execFileSync } from "child_process";
|
|
2593
|
+
var QUIET, TmuxTransport;
|
|
2594
|
+
var init_tmux_transport = __esm({
|
|
2595
|
+
"src/lib/tmux-transport.ts"() {
|
|
2596
|
+
"use strict";
|
|
2597
|
+
QUIET = {
|
|
2598
|
+
encoding: "utf8",
|
|
2599
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2600
|
+
};
|
|
2601
|
+
TmuxTransport = class {
|
|
2602
|
+
getMySession() {
|
|
2603
|
+
try {
|
|
2604
|
+
return execFileSync("tmux", ["display-message", "-p", "#{session_name}"], QUIET).trim() || null;
|
|
2605
|
+
} catch {
|
|
2606
|
+
return null;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
listSessions() {
|
|
2610
|
+
try {
|
|
2611
|
+
return execFileSync("tmux", ["list-sessions", "-F", "#{session_name}"], QUIET).trim().split("\n").filter(Boolean);
|
|
2612
|
+
} catch {
|
|
2613
|
+
return [];
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
isAlive(target) {
|
|
2617
|
+
try {
|
|
2618
|
+
const sessions = this.listSessions();
|
|
2619
|
+
if (!sessions.includes(target)) return false;
|
|
2620
|
+
const paneStatus = execFileSync(
|
|
2621
|
+
"tmux",
|
|
2622
|
+
["list-panes", "-t", target, "-F", "#{pane_dead}"],
|
|
2623
|
+
QUIET
|
|
2624
|
+
).trim();
|
|
2625
|
+
return paneStatus !== "1";
|
|
2626
|
+
} catch {
|
|
2627
|
+
return false;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
sendKeys(target, keys) {
|
|
2631
|
+
execFileSync("tmux", ["send-keys", "-t", target, keys, "Enter"], QUIET);
|
|
2632
|
+
}
|
|
2633
|
+
capturePane(target, lines) {
|
|
2634
|
+
const args = ["capture-pane", "-t", target, "-p"];
|
|
2635
|
+
if (lines) args.push("-S", `-${lines}`);
|
|
2636
|
+
return execFileSync("tmux", args, { ...QUIET, timeout: 3e3 });
|
|
2637
|
+
}
|
|
2638
|
+
isPaneInCopyMode(target) {
|
|
2639
|
+
try {
|
|
2640
|
+
const result = execFileSync(
|
|
2641
|
+
"tmux",
|
|
2642
|
+
["display-message", "-p", "-t", target, "#{pane_in_mode}"],
|
|
2643
|
+
{ ...QUIET, timeout: 3e3 }
|
|
2644
|
+
).trim();
|
|
2645
|
+
return result === "1";
|
|
2646
|
+
} catch {
|
|
2647
|
+
return false;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
spawn(name, config) {
|
|
2651
|
+
try {
|
|
2652
|
+
const args = ["new-session", "-d", "-s", name];
|
|
2653
|
+
if (config.cwd) args.push("-c", config.cwd);
|
|
2654
|
+
args.push(config.command);
|
|
2655
|
+
execFileSync("tmux", args);
|
|
2656
|
+
return { sessionName: name };
|
|
2657
|
+
} catch (e) {
|
|
2658
|
+
return { sessionName: name, error: `spawn failed: ${e}` };
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
kill(target) {
|
|
2662
|
+
try {
|
|
2663
|
+
execFileSync("tmux", ["kill-session", "-t", target], QUIET);
|
|
2664
|
+
} catch {
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
pipeLog(target, logFile) {
|
|
2668
|
+
try {
|
|
2669
|
+
const safePath = logFile.replace(/'/g, "'\\''");
|
|
2670
|
+
execFileSync("tmux", ["pipe-pane", "-t", target, `cat >> '${safePath}'`], QUIET);
|
|
2671
|
+
} catch {
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2395
2675
|
}
|
|
2396
2676
|
});
|
|
2397
2677
|
|
|
2398
2678
|
// src/lib/transport.ts
|
|
2679
|
+
function getTransport() {
|
|
2680
|
+
if (!_transport) {
|
|
2681
|
+
const { TmuxTransport: TmuxTransport2 } = (init_tmux_transport(), __toCommonJS(tmux_transport_exports));
|
|
2682
|
+
_transport = new TmuxTransport2();
|
|
2683
|
+
}
|
|
2684
|
+
return _transport;
|
|
2685
|
+
}
|
|
2686
|
+
var _transport;
|
|
2399
2687
|
var init_transport = __esm({
|
|
2400
2688
|
"src/lib/transport.ts"() {
|
|
2401
2689
|
"use strict";
|
|
2690
|
+
_transport = null;
|
|
2402
2691
|
}
|
|
2403
2692
|
});
|
|
2404
2693
|
|
|
@@ -2458,7 +2747,7 @@ var init_runtime_table = __esm({
|
|
|
2458
2747
|
});
|
|
2459
2748
|
|
|
2460
2749
|
// src/lib/agent-config.ts
|
|
2461
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as
|
|
2750
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6 } from "fs";
|
|
2462
2751
|
import path7 from "path";
|
|
2463
2752
|
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
2464
2753
|
var init_agent_config = __esm({
|
|
@@ -2466,6 +2755,7 @@ var init_agent_config = __esm({
|
|
|
2466
2755
|
"use strict";
|
|
2467
2756
|
init_config();
|
|
2468
2757
|
init_runtime_table();
|
|
2758
|
+
init_secure_files();
|
|
2469
2759
|
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2470
2760
|
DEFAULT_MODELS = {
|
|
2471
2761
|
claude: "claude-opus-4",
|
|
@@ -2476,7 +2766,7 @@ var init_agent_config = __esm({
|
|
|
2476
2766
|
});
|
|
2477
2767
|
|
|
2478
2768
|
// src/lib/intercom-queue.ts
|
|
2479
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as
|
|
2769
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2480
2770
|
import path8 from "path";
|
|
2481
2771
|
import os6 from "os";
|
|
2482
2772
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
@@ -2490,8 +2780,11 @@ var init_intercom_queue = __esm({
|
|
|
2490
2780
|
});
|
|
2491
2781
|
|
|
2492
2782
|
// src/lib/license.ts
|
|
2493
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as
|
|
2783
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2494
2784
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2785
|
+
import { createRequire as createRequire2 } from "module";
|
|
2786
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2787
|
+
import os7 from "os";
|
|
2495
2788
|
import path9 from "path";
|
|
2496
2789
|
import { jwtVerify, importSPKI } from "jose";
|
|
2497
2790
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
@@ -2506,7 +2799,7 @@ var init_license = __esm({
|
|
|
2506
2799
|
});
|
|
2507
2800
|
|
|
2508
2801
|
// src/lib/plan-limits.ts
|
|
2509
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
2802
|
+
import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
|
|
2510
2803
|
import path10 from "path";
|
|
2511
2804
|
var CACHE_PATH2;
|
|
2512
2805
|
var init_plan_limits = __esm({
|
|
@@ -2521,9 +2814,49 @@ var init_plan_limits = __esm({
|
|
|
2521
2814
|
});
|
|
2522
2815
|
|
|
2523
2816
|
// src/lib/tmux-routing.ts
|
|
2817
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync, readdirSync as readdirSync2 } from "fs";
|
|
2524
2818
|
import path11 from "path";
|
|
2525
|
-
import
|
|
2819
|
+
import os8 from "os";
|
|
2526
2820
|
import { fileURLToPath } from "url";
|
|
2821
|
+
function getMySession() {
|
|
2822
|
+
return getTransport().getMySession();
|
|
2823
|
+
}
|
|
2824
|
+
function extractRootExe(name) {
|
|
2825
|
+
if (!name) return null;
|
|
2826
|
+
if (!name.includes("-")) return name;
|
|
2827
|
+
const parts = name.split("-").filter(Boolean);
|
|
2828
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
2829
|
+
}
|
|
2830
|
+
function getParentExe(sessionKey) {
|
|
2831
|
+
try {
|
|
2832
|
+
const data = JSON.parse(readFileSync7(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2833
|
+
return data.parentExe || null;
|
|
2834
|
+
} catch {
|
|
2835
|
+
return null;
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
function resolveExeSession() {
|
|
2839
|
+
const mySession = getMySession();
|
|
2840
|
+
if (!mySession) return null;
|
|
2841
|
+
const fromSessionName = extractRootExe(mySession);
|
|
2842
|
+
try {
|
|
2843
|
+
const key = getSessionKey();
|
|
2844
|
+
const parentExe = getParentExe(key);
|
|
2845
|
+
if (parentExe) {
|
|
2846
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
2847
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
2848
|
+
process.stderr.write(
|
|
2849
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
2850
|
+
`
|
|
2851
|
+
);
|
|
2852
|
+
return fromSessionName;
|
|
2853
|
+
}
|
|
2854
|
+
return fromCache;
|
|
2855
|
+
}
|
|
2856
|
+
} catch {
|
|
2857
|
+
}
|
|
2858
|
+
return fromSessionName ?? mySession;
|
|
2859
|
+
}
|
|
2527
2860
|
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
2528
2861
|
var init_tmux_routing = __esm({
|
|
2529
2862
|
"src/lib/tmux-routing.ts"() {
|
|
@@ -2539,21 +2872,45 @@ var init_tmux_routing = __esm({
|
|
|
2539
2872
|
init_intercom_queue();
|
|
2540
2873
|
init_plan_limits();
|
|
2541
2874
|
init_employees();
|
|
2542
|
-
SPAWN_LOCK_DIR = path11.join(
|
|
2543
|
-
SESSION_CACHE = path11.join(
|
|
2544
|
-
INTERCOM_LOG2 = path11.join(
|
|
2875
|
+
SPAWN_LOCK_DIR = path11.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
2876
|
+
SESSION_CACHE = path11.join(os8.homedir(), ".exe-os", "session-cache");
|
|
2877
|
+
INTERCOM_LOG2 = path11.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
2545
2878
|
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2546
2879
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2547
2880
|
}
|
|
2548
2881
|
});
|
|
2549
2882
|
|
|
2883
|
+
// src/lib/task-scope.ts
|
|
2884
|
+
function getCurrentSessionScope() {
|
|
2885
|
+
try {
|
|
2886
|
+
return resolveExeSession();
|
|
2887
|
+
} catch {
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
2892
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
2893
|
+
if (!scope) return { sql: "", args: [] };
|
|
2894
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
2895
|
+
return {
|
|
2896
|
+
sql: ` AND ${col} = ?`,
|
|
2897
|
+
args: [scope]
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
var init_task_scope = __esm({
|
|
2901
|
+
"src/lib/task-scope.ts"() {
|
|
2902
|
+
"use strict";
|
|
2903
|
+
init_tmux_routing();
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2550
2907
|
// src/lib/store.ts
|
|
2551
2908
|
import { createHash } from "crypto";
|
|
2552
2909
|
init_database();
|
|
2553
2910
|
|
|
2554
2911
|
// src/lib/keychain.ts
|
|
2555
2912
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2556
|
-
import { existsSync as
|
|
2913
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2557
2914
|
import path4 from "path";
|
|
2558
2915
|
import os4 from "os";
|
|
2559
2916
|
var SERVICE = "exe-mem";
|
|
@@ -2583,7 +2940,7 @@ async function getMasterKey() {
|
|
|
2583
2940
|
}
|
|
2584
2941
|
}
|
|
2585
2942
|
const keyPath = getKeyPath();
|
|
2586
|
-
if (!
|
|
2943
|
+
if (!existsSync4(keyPath)) {
|
|
2587
2944
|
process.stderr.write(
|
|
2588
2945
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2589
2946
|
`
|
|
@@ -2687,6 +3044,7 @@ async function initStore(options) {
|
|
|
2687
3044
|
// src/lib/messaging.ts
|
|
2688
3045
|
init_database();
|
|
2689
3046
|
init_tmux_routing();
|
|
3047
|
+
init_task_scope();
|
|
2690
3048
|
import crypto from "crypto";
|
|
2691
3049
|
function rowToMessage(row) {
|
|
2692
3050
|
return {
|
|
@@ -2696,6 +3054,7 @@ function rowToMessage(row) {
|
|
|
2696
3054
|
targetAgent: row.target_agent,
|
|
2697
3055
|
targetProject: row.target_project ?? null,
|
|
2698
3056
|
targetDevice: row.target_device,
|
|
3057
|
+
sessionScope: row.session_scope ?? null,
|
|
2699
3058
|
content: row.content,
|
|
2700
3059
|
priority: row.priority ?? "normal",
|
|
2701
3060
|
status: row.status ?? "pending",
|
|
@@ -2708,21 +3067,24 @@ function rowToMessage(row) {
|
|
|
2708
3067
|
failureReason: row.failure_reason ?? null
|
|
2709
3068
|
};
|
|
2710
3069
|
}
|
|
2711
|
-
async function getPendingMessages(targetAgent) {
|
|
3070
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
2712
3071
|
const client = getClient();
|
|
3072
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
2713
3073
|
const result = await client.execute({
|
|
2714
3074
|
sql: `SELECT * FROM messages
|
|
2715
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
3075
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
2716
3076
|
ORDER BY id`,
|
|
2717
|
-
args: [targetAgent]
|
|
3077
|
+
args: [targetAgent, ...scope.args]
|
|
2718
3078
|
});
|
|
2719
3079
|
return result.rows.map((row) => rowToMessage(row));
|
|
2720
3080
|
}
|
|
2721
|
-
async function markRead(messageId) {
|
|
3081
|
+
async function markRead(messageId, sessionScope) {
|
|
2722
3082
|
const client = getClient();
|
|
3083
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
2723
3084
|
await client.execute({
|
|
2724
|
-
sql:
|
|
2725
|
-
|
|
3085
|
+
sql: `UPDATE messages SET status = 'read'
|
|
3086
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
3087
|
+
args: [messageId, ...scope.args]
|
|
2726
3088
|
});
|
|
2727
3089
|
}
|
|
2728
3090
|
|