@askexenow/exe-os 0.8.37 → 0.8.39
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/README.md +17 -8
- package/dist/bin/backfill-conversations.js +112 -70
- package/dist/bin/backfill-responses.js +53 -18
- package/dist/bin/backfill-vectors.js +43 -16
- package/dist/bin/cleanup-stale-review-tasks.js +38 -16
- package/dist/bin/cli.js +790 -468
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +46 -13
- package/dist/bin/exe-boot.js +288 -129
- package/dist/bin/exe-call.js +20 -10
- package/dist/bin/exe-cloud.js +135 -30
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +38 -16
- package/dist/bin/exe-export-behaviors.js +43 -21
- package/dist/bin/exe-forget.js +39 -17
- package/dist/bin/exe-gateway.js +159 -50
- package/dist/bin/exe-heartbeat.js +53 -31
- package/dist/bin/exe-kill.js +40 -18
- package/dist/bin/exe-launch-agent.js +109 -36
- package/dist/bin/exe-link.js +196 -87
- package/dist/bin/exe-new-employee.js +56 -17
- package/dist/bin/exe-pending-messages.js +47 -25
- package/dist/bin/exe-pending-notifications.js +38 -16
- package/dist/bin/exe-pending-reviews.js +51 -29
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +41 -13
- package/dist/bin/exe-search.js +57 -21
- package/dist/bin/exe-session-cleanup.js +67 -31
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +35 -13
- package/dist/bin/exe-team.js +35 -13
- package/dist/bin/git-sweep.js +45 -17
- package/dist/bin/graph-backfill.js +38 -16
- package/dist/bin/graph-export.js +38 -16
- package/dist/bin/install.js +10 -1
- package/dist/bin/scan-tasks.js +47 -19
- package/dist/bin/setup.js +444 -259
- package/dist/bin/shard-migrate.js +38 -16
- package/dist/bin/wiki-sync.js +40 -17
- package/dist/gateway/index.js +113 -48
- package/dist/hooks/bug-report-worker.js +66 -39
- package/dist/hooks/commit-complete.js +45 -17
- package/dist/hooks/error-recall.js +60 -20
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +174 -45
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +46 -17
- package/dist/hooks/notification.js +44 -15
- package/dist/hooks/post-compact.js +44 -15
- package/dist/hooks/pre-compact.js +42 -14
- package/dist/hooks/pre-tool-use.js +59 -22
- package/dist/hooks/prompt-ingest-worker.js +75 -14
- package/dist/hooks/prompt-submit.js +75 -32
- package/dist/hooks/response-ingest-worker.js +76 -15
- package/dist/hooks/session-end.js +54 -22
- package/dist/hooks/session-start.js +57 -20
- package/dist/hooks/stop.js +44 -15
- package/dist/hooks/subagent-stop.js +44 -15
- package/dist/hooks/summary-worker.js +339 -106
- package/dist/index.js +94 -23
- package/dist/lib/cloud-sync.js +191 -80
- package/dist/lib/config.js +4 -1
- package/dist/lib/consolidation.js +5 -4
- package/dist/lib/database.js +1 -0
- package/dist/lib/device-registry.js +2 -1
- package/dist/lib/embedder.js +9 -1
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +95 -36
- package/dist/lib/hybrid-search.js +57 -21
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +56 -6
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +38 -16
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +44 -16
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +280 -155
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +14 -6
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +36 -28
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +42 -8
- package/dist/tui/App.js +220 -99
- package/package.json +5 -3
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -103,6 +97,7 @@ async function ensureSchema() {
|
|
|
103
97
|
const client = getRawClient();
|
|
104
98
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
105
99
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
100
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
106
101
|
try {
|
|
107
102
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
108
103
|
} catch {
|
|
@@ -903,15 +898,15 @@ var init_database = __esm({
|
|
|
903
898
|
});
|
|
904
899
|
|
|
905
900
|
// src/lib/config.ts
|
|
906
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
901
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
907
902
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
908
903
|
import path2 from "path";
|
|
909
|
-
import
|
|
904
|
+
import os2 from "os";
|
|
910
905
|
function resolveDataDir() {
|
|
911
906
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
912
907
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
913
|
-
const newDir = path2.join(
|
|
914
|
-
const legacyDir = path2.join(
|
|
908
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
909
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
915
910
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
916
911
|
try {
|
|
917
912
|
renameSync(legacyDir, newDir);
|
|
@@ -998,7 +993,7 @@ async function loadConfig() {
|
|
|
998
993
|
normalizeAutoUpdate(migratedCfg);
|
|
999
994
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1000
995
|
if (config.dbPath.startsWith("~")) {
|
|
1001
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
996
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1002
997
|
}
|
|
1003
998
|
return config;
|
|
1004
999
|
} catch {
|
|
@@ -1105,7 +1100,7 @@ __export(shard_manager_exports, {
|
|
|
1105
1100
|
shardExists: () => shardExists
|
|
1106
1101
|
});
|
|
1107
1102
|
import path3 from "path";
|
|
1108
|
-
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
1103
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
|
|
1109
1104
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1110
1105
|
function initShardManager(encryptionKey) {
|
|
1111
1106
|
_encryptionKey = encryptionKey;
|
|
@@ -1144,8 +1139,7 @@ function shardExists(projectName) {
|
|
|
1144
1139
|
}
|
|
1145
1140
|
function listShards() {
|
|
1146
1141
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
1147
|
-
|
|
1148
|
-
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1142
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1149
1143
|
}
|
|
1150
1144
|
async function ensureShardSchema(client) {
|
|
1151
1145
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1350,10 +1344,10 @@ var init_employees = __esm({
|
|
|
1350
1344
|
// src/lib/notifications.ts
|
|
1351
1345
|
import crypto2 from "crypto";
|
|
1352
1346
|
import path5 from "path";
|
|
1353
|
-
import
|
|
1347
|
+
import os3 from "os";
|
|
1354
1348
|
import {
|
|
1355
1349
|
readFileSync as readFileSync3,
|
|
1356
|
-
readdirSync,
|
|
1350
|
+
readdirSync as readdirSync2,
|
|
1357
1351
|
unlinkSync,
|
|
1358
1352
|
existsSync as existsSync5,
|
|
1359
1353
|
rmdirSync
|
|
@@ -1380,12 +1374,12 @@ var init_tasks_crud = __esm({
|
|
|
1380
1374
|
|
|
1381
1375
|
// src/lib/session-registry.ts
|
|
1382
1376
|
import path7 from "path";
|
|
1383
|
-
import
|
|
1377
|
+
import os4 from "os";
|
|
1384
1378
|
var REGISTRY_PATH;
|
|
1385
1379
|
var init_session_registry = __esm({
|
|
1386
1380
|
"src/lib/session-registry.ts"() {
|
|
1387
1381
|
"use strict";
|
|
1388
|
-
REGISTRY_PATH = path7.join(
|
|
1382
|
+
REGISTRY_PATH = path7.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
1389
1383
|
}
|
|
1390
1384
|
});
|
|
1391
1385
|
|
|
@@ -1436,14 +1430,14 @@ var init_provider_table = __esm({
|
|
|
1436
1430
|
// src/lib/intercom-queue.ts
|
|
1437
1431
|
import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
1438
1432
|
import path8 from "path";
|
|
1439
|
-
import
|
|
1433
|
+
import os5 from "os";
|
|
1440
1434
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
1441
1435
|
var init_intercom_queue = __esm({
|
|
1442
1436
|
"src/lib/intercom-queue.ts"() {
|
|
1443
1437
|
"use strict";
|
|
1444
|
-
QUEUE_PATH = path8.join(
|
|
1438
|
+
QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
1445
1439
|
TTL_MS = 60 * 60 * 1e3;
|
|
1446
|
-
INTERCOM_LOG = path8.join(
|
|
1440
|
+
INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
1447
1441
|
}
|
|
1448
1442
|
});
|
|
1449
1443
|
|
|
@@ -1480,7 +1474,7 @@ var init_plan_limits = __esm({
|
|
|
1480
1474
|
|
|
1481
1475
|
// src/lib/tmux-routing.ts
|
|
1482
1476
|
import path11 from "path";
|
|
1483
|
-
import
|
|
1477
|
+
import os6 from "os";
|
|
1484
1478
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1485
1479
|
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1486
1480
|
var init_tmux_routing = __esm({
|
|
@@ -1494,9 +1488,9 @@ var init_tmux_routing = __esm({
|
|
|
1494
1488
|
init_provider_table();
|
|
1495
1489
|
init_intercom_queue();
|
|
1496
1490
|
init_plan_limits();
|
|
1497
|
-
SPAWN_LOCK_DIR = path11.join(
|
|
1498
|
-
SESSION_CACHE = path11.join(
|
|
1499
|
-
INTERCOM_LOG2 = path11.join(
|
|
1491
|
+
SPAWN_LOCK_DIR = path11.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
1492
|
+
SESSION_CACHE = path11.join(os6.homedir(), ".exe-os", "session-cache");
|
|
1493
|
+
INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
1500
1494
|
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1501
1495
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1502
1496
|
}
|
|
@@ -1504,7 +1498,7 @@ var init_tmux_routing = __esm({
|
|
|
1504
1498
|
|
|
1505
1499
|
// src/lib/tasks-review.ts
|
|
1506
1500
|
import path12 from "path";
|
|
1507
|
-
import { existsSync as existsSync10, readdirSync as
|
|
1501
|
+
import { existsSync as existsSync10, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1508
1502
|
async function listPendingReviews(limit) {
|
|
1509
1503
|
const client = getClient();
|
|
1510
1504
|
const result = await client.execute({
|
|
@@ -1535,11 +1529,12 @@ init_database();
|
|
|
1535
1529
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1536
1530
|
import { existsSync } from "fs";
|
|
1537
1531
|
import path from "path";
|
|
1532
|
+
import os from "os";
|
|
1538
1533
|
import crypto from "crypto";
|
|
1539
1534
|
var SERVICE = "exe-mem";
|
|
1540
1535
|
var ACCOUNT = "master-key";
|
|
1541
1536
|
function getKeyDir() {
|
|
1542
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1537
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1543
1538
|
}
|
|
1544
1539
|
function getKeyPath() {
|
|
1545
1540
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1576,6 +1571,30 @@ async function getMasterKey() {
|
|
|
1576
1571
|
|
|
1577
1572
|
// src/lib/store.ts
|
|
1578
1573
|
init_config();
|
|
1574
|
+
var INIT_MAX_RETRIES = 3;
|
|
1575
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1576
|
+
function isBusyError2(err) {
|
|
1577
|
+
if (err instanceof Error) {
|
|
1578
|
+
const msg = err.message.toLowerCase();
|
|
1579
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1580
|
+
}
|
|
1581
|
+
return false;
|
|
1582
|
+
}
|
|
1583
|
+
async function retryOnBusy2(fn, label) {
|
|
1584
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1585
|
+
try {
|
|
1586
|
+
return await fn();
|
|
1587
|
+
} catch (err) {
|
|
1588
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1589
|
+
process.stderr.write(
|
|
1590
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1591
|
+
`
|
|
1592
|
+
);
|
|
1593
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
throw new Error("unreachable");
|
|
1597
|
+
}
|
|
1579
1598
|
var _pendingRecords = [];
|
|
1580
1599
|
var _batchSize = 20;
|
|
1581
1600
|
var _flushIntervalMs = 1e4;
|
|
@@ -1610,14 +1629,17 @@ async function initStore(options) {
|
|
|
1610
1629
|
dbPath,
|
|
1611
1630
|
encryptionKey: hexKey
|
|
1612
1631
|
});
|
|
1613
|
-
await ensureSchema();
|
|
1632
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1614
1633
|
try {
|
|
1615
1634
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1616
1635
|
initShardManager2(hexKey);
|
|
1617
1636
|
} catch {
|
|
1618
1637
|
}
|
|
1619
1638
|
const client = getClient();
|
|
1620
|
-
const vResult = await
|
|
1639
|
+
const vResult = await retryOnBusy2(
|
|
1640
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1641
|
+
"version-query"
|
|
1642
|
+
);
|
|
1621
1643
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1622
1644
|
}
|
|
1623
1645
|
|
package/dist/bin/exe-rename.js
CHANGED
|
@@ -111,6 +111,7 @@ async function ensureSchema() {
|
|
|
111
111
|
const client = getRawClient();
|
|
112
112
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
113
113
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
114
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
114
115
|
try {
|
|
115
116
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
116
117
|
} catch {
|
|
@@ -931,7 +932,7 @@ import { execSync } from "child_process";
|
|
|
931
932
|
import path2 from "path";
|
|
932
933
|
|
|
933
934
|
// src/lib/config.ts
|
|
934
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
935
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
935
936
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
936
937
|
import path from "path";
|
|
937
938
|
import os from "os";
|
|
@@ -1053,15 +1054,20 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
1053
1054
|
await mkdir2(path2.dirname(employeesPath), { recursive: true });
|
|
1054
1055
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1055
1056
|
}
|
|
1057
|
+
function findExeBin() {
|
|
1058
|
+
try {
|
|
1059
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
1060
|
+
} catch {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1056
1064
|
function registerBinSymlinks(name) {
|
|
1057
1065
|
const created = [];
|
|
1058
1066
|
const skipped = [];
|
|
1059
1067
|
const errors = [];
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
} catch {
|
|
1064
|
-
errors.push("Could not find 'exe' in PATH");
|
|
1068
|
+
const exeBinPath = findExeBin();
|
|
1069
|
+
if (!exeBinPath) {
|
|
1070
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
1065
1071
|
return { created, skipped, errors };
|
|
1066
1072
|
}
|
|
1067
1073
|
const binDir = path2.dirname(exeBinPath);
|
|
@@ -1240,9 +1246,17 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
1240
1246
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1241
1247
|
}
|
|
1242
1248
|
}
|
|
1249
|
+
function findExeBin2() {
|
|
1250
|
+
try {
|
|
1251
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
1252
|
+
} catch {
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1243
1256
|
function removeOldSymlinks(name) {
|
|
1244
1257
|
try {
|
|
1245
|
-
const exeBinPath =
|
|
1258
|
+
const exeBinPath = findExeBin2();
|
|
1259
|
+
if (!exeBinPath) return;
|
|
1246
1260
|
const binDir = path3.dirname(exeBinPath);
|
|
1247
1261
|
for (const suffix of ["", "-opencode"]) {
|
|
1248
1262
|
const linkPath = path3.join(binDir, `${name}${suffix}`);
|
package/dist/bin/exe-review.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -16,7 +10,7 @@ var __export = (target, all) => {
|
|
|
16
10
|
};
|
|
17
11
|
|
|
18
12
|
// src/lib/config.ts
|
|
19
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
13
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
20
14
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
21
15
|
import path from "path";
|
|
22
16
|
import os from "os";
|
|
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
|
|
|
218
212
|
shardExists: () => shardExists
|
|
219
213
|
});
|
|
220
214
|
import path4 from "path";
|
|
221
|
-
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
215
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
222
216
|
import { createClient as createClient2 } from "@libsql/client";
|
|
223
217
|
function initShardManager(encryptionKey) {
|
|
224
218
|
_encryptionKey = encryptionKey;
|
|
@@ -257,7 +251,6 @@ function shardExists(projectName) {
|
|
|
257
251
|
}
|
|
258
252
|
function listShards() {
|
|
259
253
|
if (!existsSync4(SHARDS_DIR)) return [];
|
|
260
|
-
const { readdirSync } = __require("fs");
|
|
261
254
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
262
255
|
}
|
|
263
256
|
async function ensureShardSchema(client) {
|
|
@@ -561,6 +554,7 @@ async function ensureSchema() {
|
|
|
561
554
|
const client = getRawClient();
|
|
562
555
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
563
556
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
557
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
564
558
|
try {
|
|
565
559
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
566
560
|
} catch {
|
|
@@ -1351,14 +1345,15 @@ async function ensureSchema() {
|
|
|
1351
1345
|
}
|
|
1352
1346
|
|
|
1353
1347
|
// src/lib/keychain.ts
|
|
1354
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod } from "fs/promises";
|
|
1348
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1355
1349
|
import { existsSync as existsSync3 } from "fs";
|
|
1356
1350
|
import path3 from "path";
|
|
1351
|
+
import os2 from "os";
|
|
1357
1352
|
import crypto from "crypto";
|
|
1358
1353
|
var SERVICE = "exe-mem";
|
|
1359
1354
|
var ACCOUNT = "master-key";
|
|
1360
1355
|
function getKeyDir() {
|
|
1361
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(
|
|
1356
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
|
|
1362
1357
|
}
|
|
1363
1358
|
function getKeyPath() {
|
|
1364
1359
|
return path3.join(getKeyDir(), "master.key");
|
|
@@ -1395,6 +1390,30 @@ async function getMasterKey() {
|
|
|
1395
1390
|
|
|
1396
1391
|
// src/lib/store.ts
|
|
1397
1392
|
init_config();
|
|
1393
|
+
var INIT_MAX_RETRIES = 3;
|
|
1394
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1395
|
+
function isBusyError2(err) {
|
|
1396
|
+
if (err instanceof Error) {
|
|
1397
|
+
const msg = err.message.toLowerCase();
|
|
1398
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1399
|
+
}
|
|
1400
|
+
return false;
|
|
1401
|
+
}
|
|
1402
|
+
async function retryOnBusy2(fn, label) {
|
|
1403
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1404
|
+
try {
|
|
1405
|
+
return await fn();
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1408
|
+
process.stderr.write(
|
|
1409
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1410
|
+
`
|
|
1411
|
+
);
|
|
1412
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
throw new Error("unreachable");
|
|
1416
|
+
}
|
|
1398
1417
|
var _pendingRecords = [];
|
|
1399
1418
|
var _batchSize = 20;
|
|
1400
1419
|
var _flushIntervalMs = 1e4;
|
|
@@ -1429,14 +1448,17 @@ async function initStore(options) {
|
|
|
1429
1448
|
dbPath,
|
|
1430
1449
|
encryptionKey: hexKey
|
|
1431
1450
|
});
|
|
1432
|
-
await ensureSchema();
|
|
1451
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1433
1452
|
try {
|
|
1434
1453
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1435
1454
|
initShardManager2(hexKey);
|
|
1436
1455
|
} catch {
|
|
1437
1456
|
}
|
|
1438
1457
|
const client = getClient();
|
|
1439
|
-
const vResult = await
|
|
1458
|
+
const vResult = await retryOnBusy2(
|
|
1459
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1460
|
+
"version-query"
|
|
1461
|
+
);
|
|
1440
1462
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1441
1463
|
}
|
|
1442
1464
|
function classifyTier(record) {
|
|
@@ -1479,6 +1501,12 @@ async function writeMemory(record) {
|
|
|
1479
1501
|
supersedes_id: record.supersedes_id ?? null
|
|
1480
1502
|
};
|
|
1481
1503
|
_pendingRecords.push(dbRow);
|
|
1504
|
+
const MAX_PENDING = 1e3;
|
|
1505
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
1506
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
1507
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
1508
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
1509
|
+
}
|
|
1482
1510
|
if (_flushTimer === null) {
|
|
1483
1511
|
_flushTimer = setInterval(() => {
|
|
1484
1512
|
void flushBatch();
|
package/dist/bin/exe-search.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -112,6 +106,7 @@ async function ensureSchema() {
|
|
|
112
106
|
const client = getRawClient();
|
|
113
107
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
114
108
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
109
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
115
110
|
try {
|
|
116
111
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
117
112
|
} catch {
|
|
@@ -923,9 +918,10 @@ var init_database = __esm({
|
|
|
923
918
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
924
919
|
import { existsSync } from "fs";
|
|
925
920
|
import path from "path";
|
|
921
|
+
import os from "os";
|
|
926
922
|
import crypto from "crypto";
|
|
927
923
|
function getKeyDir() {
|
|
928
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
924
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
929
925
|
}
|
|
930
926
|
function getKeyPath() {
|
|
931
927
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -984,15 +980,15 @@ __export(config_exports, {
|
|
|
984
980
|
migrateConfig: () => migrateConfig,
|
|
985
981
|
saveConfig: () => saveConfig
|
|
986
982
|
});
|
|
987
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
983
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
988
984
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
989
985
|
import path2 from "path";
|
|
990
|
-
import
|
|
986
|
+
import os2 from "os";
|
|
991
987
|
function resolveDataDir() {
|
|
992
988
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
993
989
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
994
|
-
const newDir = path2.join(
|
|
995
|
-
const legacyDir = path2.join(
|
|
990
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
991
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
996
992
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
997
993
|
try {
|
|
998
994
|
renameSync(legacyDir, newDir);
|
|
@@ -1079,7 +1075,7 @@ async function loadConfig() {
|
|
|
1079
1075
|
normalizeAutoUpdate(migratedCfg);
|
|
1080
1076
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1081
1077
|
if (config.dbPath.startsWith("~")) {
|
|
1082
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
1078
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1083
1079
|
}
|
|
1084
1080
|
return config;
|
|
1085
1081
|
} catch {
|
|
@@ -1110,6 +1106,9 @@ async function saveConfig(config) {
|
|
|
1110
1106
|
await mkdir2(dir, { recursive: true });
|
|
1111
1107
|
const configPath = path2.join(dir, "config.json");
|
|
1112
1108
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1109
|
+
if (config.cloud?.apiKey) {
|
|
1110
|
+
await chmod2(configPath, 384);
|
|
1111
|
+
}
|
|
1113
1112
|
}
|
|
1114
1113
|
async function loadConfigFrom(configPath) {
|
|
1115
1114
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -1225,7 +1224,7 @@ __export(shard_manager_exports, {
|
|
|
1225
1224
|
shardExists: () => shardExists
|
|
1226
1225
|
});
|
|
1227
1226
|
import path3 from "path";
|
|
1228
|
-
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
1227
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
|
|
1229
1228
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1230
1229
|
function initShardManager(encryptionKey) {
|
|
1231
1230
|
_encryptionKey = encryptionKey;
|
|
@@ -1264,8 +1263,7 @@ function shardExists(projectName) {
|
|
|
1264
1263
|
}
|
|
1265
1264
|
function listShards() {
|
|
1266
1265
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
1267
|
-
|
|
1268
|
-
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1266
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1269
1267
|
}
|
|
1270
1268
|
async function ensureShardSchema(client) {
|
|
1271
1269
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1470,6 +1468,28 @@ __export(store_exports, {
|
|
|
1470
1468
|
vectorToBlob: () => vectorToBlob,
|
|
1471
1469
|
writeMemory: () => writeMemory
|
|
1472
1470
|
});
|
|
1471
|
+
function isBusyError2(err) {
|
|
1472
|
+
if (err instanceof Error) {
|
|
1473
|
+
const msg = err.message.toLowerCase();
|
|
1474
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1475
|
+
}
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
async function retryOnBusy2(fn, label) {
|
|
1479
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1480
|
+
try {
|
|
1481
|
+
return await fn();
|
|
1482
|
+
} catch (err) {
|
|
1483
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1484
|
+
process.stderr.write(
|
|
1485
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1486
|
+
`
|
|
1487
|
+
);
|
|
1488
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
throw new Error("unreachable");
|
|
1492
|
+
}
|
|
1473
1493
|
async function initStore(options) {
|
|
1474
1494
|
if (_flushTimer !== null) {
|
|
1475
1495
|
clearInterval(_flushTimer);
|
|
@@ -1498,14 +1518,17 @@ async function initStore(options) {
|
|
|
1498
1518
|
dbPath,
|
|
1499
1519
|
encryptionKey: hexKey
|
|
1500
1520
|
});
|
|
1501
|
-
await ensureSchema();
|
|
1521
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1502
1522
|
try {
|
|
1503
1523
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1504
1524
|
initShardManager2(hexKey);
|
|
1505
1525
|
} catch {
|
|
1506
1526
|
}
|
|
1507
1527
|
const client = getClient();
|
|
1508
|
-
const vResult = await
|
|
1528
|
+
const vResult = await retryOnBusy2(
|
|
1529
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1530
|
+
"version-query"
|
|
1531
|
+
);
|
|
1509
1532
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1510
1533
|
}
|
|
1511
1534
|
function classifyTier(record) {
|
|
@@ -1548,6 +1571,12 @@ async function writeMemory(record) {
|
|
|
1548
1571
|
supersedes_id: record.supersedes_id ?? null
|
|
1549
1572
|
};
|
|
1550
1573
|
_pendingRecords.push(dbRow);
|
|
1574
|
+
const MAX_PENDING = 1e3;
|
|
1575
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
1576
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
1577
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
1578
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
1579
|
+
}
|
|
1551
1580
|
if (_flushTimer === null) {
|
|
1552
1581
|
_flushTimer = setInterval(() => {
|
|
1553
1582
|
void flushBatch();
|
|
@@ -1879,7 +1908,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
1879
1908
|
return 0;
|
|
1880
1909
|
}
|
|
1881
1910
|
}
|
|
1882
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1911
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1883
1912
|
var init_store = __esm({
|
|
1884
1913
|
"src/lib/store.ts"() {
|
|
1885
1914
|
"use strict";
|
|
@@ -1887,6 +1916,8 @@ var init_store = __esm({
|
|
|
1887
1916
|
init_database();
|
|
1888
1917
|
init_keychain();
|
|
1889
1918
|
init_config();
|
|
1919
|
+
INIT_MAX_RETRIES = 3;
|
|
1920
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
1890
1921
|
_pendingRecords = [];
|
|
1891
1922
|
_batchSize = 20;
|
|
1892
1923
|
_flushIntervalMs = 1e4;
|
|
@@ -1994,6 +2025,10 @@ import path4 from "path";
|
|
|
1994
2025
|
import { fileURLToPath } from "url";
|
|
1995
2026
|
function handleData(chunk) {
|
|
1996
2027
|
_buffer += chunk.toString();
|
|
2028
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
2029
|
+
_buffer = "";
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
1997
2032
|
let newlineIdx;
|
|
1998
2033
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1999
2034
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -2301,7 +2336,7 @@ function disconnectClient() {
|
|
|
2301
2336
|
entry.resolve({ error: "Client disconnected" });
|
|
2302
2337
|
}
|
|
2303
2338
|
}
|
|
2304
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
|
|
2339
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
2305
2340
|
var init_exe_daemon_client = __esm({
|
|
2306
2341
|
"src/lib/exe-daemon-client.ts"() {
|
|
2307
2342
|
"use strict";
|
|
@@ -2318,6 +2353,7 @@ var init_exe_daemon_client = __esm({
|
|
|
2318
2353
|
_requestCount = 0;
|
|
2319
2354
|
HEALTH_CHECK_INTERVAL = 100;
|
|
2320
2355
|
_pending = /* @__PURE__ */ new Map();
|
|
2356
|
+
MAX_BUFFER = 1e7;
|
|
2321
2357
|
}
|
|
2322
2358
|
});
|
|
2323
2359
|
|
|
@@ -2446,7 +2482,7 @@ __export(file_grep_exports, {
|
|
|
2446
2482
|
grepProjectFiles: () => grepProjectFiles
|
|
2447
2483
|
});
|
|
2448
2484
|
import { execSync as execSync2 } from "child_process";
|
|
2449
|
-
import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2485
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2450
2486
|
import path6 from "path";
|
|
2451
2487
|
import crypto2 from "crypto";
|
|
2452
2488
|
function hasRipgrep() {
|
|
@@ -2591,7 +2627,7 @@ function collectFiles(root, patterns) {
|
|
|
2591
2627
|
const basename = path6.basename(dir);
|
|
2592
2628
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2593
2629
|
try {
|
|
2594
|
-
const entries =
|
|
2630
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2595
2631
|
for (const entry of entries) {
|
|
2596
2632
|
if (files.length >= MAX_FILES) return;
|
|
2597
2633
|
const rel = path6.join(relative, entry.name);
|