@askexenow/exe-os 0.8.38 → 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 +46 -10
- package/dist/bin/backfill-responses.js +46 -10
- package/dist/bin/backfill-vectors.js +42 -8
- package/dist/bin/cleanup-stale-review-tasks.js +37 -8
- package/dist/bin/cli.js +281 -154
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +39 -5
- package/dist/bin/exe-boot.js +237 -111
- package/dist/bin/exe-call.js +11 -6
- package/dist/bin/exe-cloud.js +99 -28
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +37 -8
- package/dist/bin/exe-export-behaviors.js +39 -10
- package/dist/bin/exe-forget.js +38 -9
- package/dist/bin/exe-gateway.js +109 -42
- package/dist/bin/exe-heartbeat.js +49 -20
- package/dist/bin/exe-kill.js +39 -10
- package/dist/bin/exe-launch-agent.js +58 -22
- package/dist/bin/exe-link.js +184 -85
- package/dist/bin/exe-new-employee.js +21 -7
- package/dist/bin/exe-pending-messages.js +46 -17
- package/dist/bin/exe-pending-notifications.js +37 -8
- package/dist/bin/exe-pending-reviews.js +47 -18
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +34 -5
- package/dist/bin/exe-search.js +47 -10
- package/dist/bin/exe-session-cleanup.js +56 -19
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +34 -5
- package/dist/bin/exe-team.js +34 -5
- package/dist/bin/git-sweep.js +38 -9
- package/dist/bin/graph-backfill.js +37 -8
- package/dist/bin/graph-export.js +37 -8
- package/dist/bin/install.js +1 -1
- package/dist/bin/scan-tasks.js +40 -11
- package/dist/bin/setup.js +58 -24
- package/dist/bin/shard-migrate.js +37 -8
- package/dist/bin/wiki-sync.js +39 -9
- package/dist/gateway/index.js +102 -37
- package/dist/hooks/bug-report-worker.js +62 -28
- package/dist/hooks/commit-complete.js +38 -9
- package/dist/hooks/error-recall.js +49 -8
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +151 -37
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +39 -9
- package/dist/hooks/notification.js +37 -7
- package/dist/hooks/post-compact.js +37 -7
- package/dist/hooks/pre-compact.js +35 -6
- package/dist/hooks/pre-tool-use.js +52 -14
- package/dist/hooks/prompt-ingest-worker.js +56 -10
- package/dist/hooks/prompt-submit.js +61 -23
- package/dist/hooks/response-ingest-worker.js +57 -11
- package/dist/hooks/session-end.js +43 -10
- package/dist/hooks/session-start.js +46 -8
- package/dist/hooks/stop.js +37 -7
- package/dist/hooks/subagent-stop.js +37 -7
- package/dist/hooks/summary-worker.js +317 -99
- package/dist/index.js +87 -22
- package/dist/lib/cloud-sync.js +172 -78
- 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/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +71 -28
- package/dist/lib/hybrid-search.js +47 -10
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +13 -4
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +37 -8
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +37 -8
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +97 -43
- 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 +1 -1
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +35 -6
- package/dist/tui/App.js +177 -95
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -88,10 +88,19 @@ You talk to your COO. Your COO delegates. Each employee has:
|
|
|
88
88
|
### 1. Install
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
+
# macOS
|
|
92
|
+
xcode-select --install && brew install tmux
|
|
93
|
+
npm install -g @askexenow/exe-os
|
|
94
|
+
|
|
95
|
+
# Linux / WSL2
|
|
96
|
+
sudo apt install -y tmux git cmake g++ libsecret-1-dev curl
|
|
91
97
|
npm install -g @askexenow/exe-os
|
|
98
|
+
|
|
99
|
+
# Windows — must use WSL2 (native Windows not supported)
|
|
100
|
+
# Run `wsl --install` in PowerShell first, then follow Linux steps inside WSL2
|
|
92
101
|
```
|
|
93
102
|
|
|
94
|
-
Requires Node.js
|
|
103
|
+
Requires Node.js 22+ and tmux. See **[docs/install.md](docs/install.md)** for full platform-specific instructions and troubleshooting.
|
|
95
104
|
|
|
96
105
|
### 2. Run Setup
|
|
97
106
|
|
|
@@ -219,17 +228,17 @@ exe-os/
|
|
|
219
228
|
|
|
220
229
|
## Requirements
|
|
221
230
|
|
|
222
|
-
- **Node.js
|
|
231
|
+
- **Node.js 22+**
|
|
223
232
|
- **tmux** — for multi-agent session management
|
|
224
233
|
- **Claude Code** subscription (Mode 1) or API key (Mode 2)
|
|
225
234
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
brew install tmux node
|
|
235
|
+
| Platform | Quick Install |
|
|
236
|
+
|----------|-------------|
|
|
237
|
+
| macOS | `brew install tmux node` + Xcode CLI Tools |
|
|
238
|
+
| Linux | `apt install tmux git cmake g++ libsecret-1-dev` + Node.js 22+ |
|
|
239
|
+
| Windows | WSL2 required (`wsl --install`), then follow Linux steps |
|
|
229
240
|
|
|
230
|
-
|
|
231
|
-
apt install tmux nodejs
|
|
232
|
-
```
|
|
241
|
+
Full guide: **[docs/install.md](docs/install.md)**
|
|
233
242
|
|
|
234
243
|
---
|
|
235
244
|
|
|
@@ -10,15 +10,15 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
14
14
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
15
|
import path2 from "path";
|
|
16
|
-
import
|
|
16
|
+
import os2 from "os";
|
|
17
17
|
function resolveDataDir() {
|
|
18
18
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
19
19
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
|
-
const newDir = path2.join(
|
|
21
|
-
const legacyDir = path2.join(
|
|
20
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
21
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
22
22
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
23
|
try {
|
|
24
24
|
renameSync(legacyDir, newDir);
|
|
@@ -105,7 +105,7 @@ async function loadConfig() {
|
|
|
105
105
|
normalizeAutoUpdate(migratedCfg);
|
|
106
106
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
107
107
|
if (config.dbPath.startsWith("~")) {
|
|
108
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
108
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
109
109
|
}
|
|
110
110
|
return config;
|
|
111
111
|
} catch {
|
|
@@ -538,6 +538,7 @@ async function ensureSchema() {
|
|
|
538
538
|
const client = getRawClient();
|
|
539
539
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
540
540
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
541
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
541
542
|
try {
|
|
542
543
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
543
544
|
} catch {
|
|
@@ -1331,11 +1332,12 @@ async function ensureSchema() {
|
|
|
1331
1332
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1332
1333
|
import { existsSync } from "fs";
|
|
1333
1334
|
import path from "path";
|
|
1335
|
+
import os from "os";
|
|
1334
1336
|
import crypto from "crypto";
|
|
1335
1337
|
var SERVICE = "exe-mem";
|
|
1336
1338
|
var ACCOUNT = "master-key";
|
|
1337
1339
|
function getKeyDir() {
|
|
1338
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1340
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1339
1341
|
}
|
|
1340
1342
|
function getKeyPath() {
|
|
1341
1343
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1372,6 +1374,30 @@ async function getMasterKey() {
|
|
|
1372
1374
|
|
|
1373
1375
|
// src/lib/store.ts
|
|
1374
1376
|
init_config();
|
|
1377
|
+
var INIT_MAX_RETRIES = 3;
|
|
1378
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1379
|
+
function isBusyError2(err) {
|
|
1380
|
+
if (err instanceof Error) {
|
|
1381
|
+
const msg = err.message.toLowerCase();
|
|
1382
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1383
|
+
}
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
async function retryOnBusy2(fn, label) {
|
|
1387
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1388
|
+
try {
|
|
1389
|
+
return await fn();
|
|
1390
|
+
} catch (err) {
|
|
1391
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1392
|
+
process.stderr.write(
|
|
1393
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1394
|
+
`
|
|
1395
|
+
);
|
|
1396
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
throw new Error("unreachable");
|
|
1400
|
+
}
|
|
1375
1401
|
var _pendingRecords = [];
|
|
1376
1402
|
var _batchSize = 20;
|
|
1377
1403
|
var _flushIntervalMs = 1e4;
|
|
@@ -1406,14 +1432,17 @@ async function initStore(options) {
|
|
|
1406
1432
|
dbPath,
|
|
1407
1433
|
encryptionKey: hexKey
|
|
1408
1434
|
});
|
|
1409
|
-
await ensureSchema();
|
|
1435
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1410
1436
|
try {
|
|
1411
1437
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1412
1438
|
initShardManager2(hexKey);
|
|
1413
1439
|
} catch {
|
|
1414
1440
|
}
|
|
1415
1441
|
const client = getClient();
|
|
1416
|
-
const vResult = await
|
|
1442
|
+
const vResult = await retryOnBusy2(
|
|
1443
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1444
|
+
"version-query"
|
|
1445
|
+
);
|
|
1417
1446
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1418
1447
|
}
|
|
1419
1448
|
function classifyTier(record) {
|
|
@@ -1630,8 +1659,13 @@ var _buffer = "";
|
|
|
1630
1659
|
var _requestCount = 0;
|
|
1631
1660
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
1632
1661
|
var _pending = /* @__PURE__ */ new Map();
|
|
1662
|
+
var MAX_BUFFER = 1e7;
|
|
1633
1663
|
function handleData(chunk) {
|
|
1634
1664
|
_buffer += chunk.toString();
|
|
1665
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1666
|
+
_buffer = "";
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1635
1669
|
let newlineIdx;
|
|
1636
1670
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1637
1671
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -1945,10 +1979,12 @@ function isMainModule(importMetaUrl) {
|
|
|
1945
1979
|
var TOOL_NAME = "backfill-conversation";
|
|
1946
1980
|
var MIN_MESSAGES = 3;
|
|
1947
1981
|
var MAX_SUMMARY_LENGTH = 4e3;
|
|
1982
|
+
var MAX_WALK_DEPTH = 10;
|
|
1948
1983
|
async function findJsonlFiles(sinceDate, projectFilter) {
|
|
1949
1984
|
const projectsDir = path5.join(homedir(), ".claude", "projects");
|
|
1950
1985
|
const files = [];
|
|
1951
|
-
async function walk(dir) {
|
|
1986
|
+
async function walk(dir, depth = 0) {
|
|
1987
|
+
if (depth > MAX_WALK_DEPTH) return;
|
|
1952
1988
|
let entries;
|
|
1953
1989
|
try {
|
|
1954
1990
|
entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -1959,7 +1995,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
1959
1995
|
const full = path5.join(dir, entry.name);
|
|
1960
1996
|
if (entry.isDirectory()) {
|
|
1961
1997
|
if (entry.name === "subagents" || entry.name === "tool-results") continue;
|
|
1962
|
-
await walk(full);
|
|
1998
|
+
await walk(full, depth + 1);
|
|
1963
1999
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
1964
2000
|
try {
|
|
1965
2001
|
const s = await stat(full);
|
|
@@ -10,15 +10,15 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
14
14
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
15
|
import path2 from "path";
|
|
16
|
-
import
|
|
16
|
+
import os2 from "os";
|
|
17
17
|
function resolveDataDir() {
|
|
18
18
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
19
19
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
|
-
const newDir = path2.join(
|
|
21
|
-
const legacyDir = path2.join(
|
|
20
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
21
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
22
22
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
23
|
try {
|
|
24
24
|
renameSync(legacyDir, newDir);
|
|
@@ -105,7 +105,7 @@ async function loadConfig() {
|
|
|
105
105
|
normalizeAutoUpdate(migratedCfg);
|
|
106
106
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
107
107
|
if (config.dbPath.startsWith("~")) {
|
|
108
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
108
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
109
109
|
}
|
|
110
110
|
return config;
|
|
111
111
|
} catch {
|
|
@@ -537,6 +537,7 @@ async function ensureSchema() {
|
|
|
537
537
|
const client = getRawClient();
|
|
538
538
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
539
539
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
540
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
540
541
|
try {
|
|
541
542
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
542
543
|
} catch {
|
|
@@ -1330,11 +1331,12 @@ async function ensureSchema() {
|
|
|
1330
1331
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1331
1332
|
import { existsSync } from "fs";
|
|
1332
1333
|
import path from "path";
|
|
1334
|
+
import os from "os";
|
|
1333
1335
|
import crypto from "crypto";
|
|
1334
1336
|
var SERVICE = "exe-mem";
|
|
1335
1337
|
var ACCOUNT = "master-key";
|
|
1336
1338
|
function getKeyDir() {
|
|
1337
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1339
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1338
1340
|
}
|
|
1339
1341
|
function getKeyPath() {
|
|
1340
1342
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1371,6 +1373,30 @@ async function getMasterKey() {
|
|
|
1371
1373
|
|
|
1372
1374
|
// src/lib/store.ts
|
|
1373
1375
|
init_config();
|
|
1376
|
+
var INIT_MAX_RETRIES = 3;
|
|
1377
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1378
|
+
function isBusyError2(err) {
|
|
1379
|
+
if (err instanceof Error) {
|
|
1380
|
+
const msg = err.message.toLowerCase();
|
|
1381
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1382
|
+
}
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
async function retryOnBusy2(fn, label) {
|
|
1386
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1387
|
+
try {
|
|
1388
|
+
return await fn();
|
|
1389
|
+
} catch (err) {
|
|
1390
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1391
|
+
process.stderr.write(
|
|
1392
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1393
|
+
`
|
|
1394
|
+
);
|
|
1395
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
throw new Error("unreachable");
|
|
1399
|
+
}
|
|
1374
1400
|
var _pendingRecords = [];
|
|
1375
1401
|
var _batchSize = 20;
|
|
1376
1402
|
var _flushIntervalMs = 1e4;
|
|
@@ -1405,14 +1431,17 @@ async function initStore(options) {
|
|
|
1405
1431
|
dbPath,
|
|
1406
1432
|
encryptionKey: hexKey
|
|
1407
1433
|
});
|
|
1408
|
-
await ensureSchema();
|
|
1434
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1409
1435
|
try {
|
|
1410
1436
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1411
1437
|
initShardManager2(hexKey);
|
|
1412
1438
|
} catch {
|
|
1413
1439
|
}
|
|
1414
1440
|
const client = getClient();
|
|
1415
|
-
const vResult = await
|
|
1441
|
+
const vResult = await retryOnBusy2(
|
|
1442
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1443
|
+
"version-query"
|
|
1444
|
+
);
|
|
1416
1445
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1417
1446
|
}
|
|
1418
1447
|
function classifyTier(record) {
|
|
@@ -1629,8 +1658,13 @@ var _buffer = "";
|
|
|
1629
1658
|
var _requestCount = 0;
|
|
1630
1659
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
1631
1660
|
var _pending = /* @__PURE__ */ new Map();
|
|
1661
|
+
var MAX_BUFFER = 1e7;
|
|
1632
1662
|
function handleData(chunk) {
|
|
1633
1663
|
_buffer += chunk.toString();
|
|
1664
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1665
|
+
_buffer = "";
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1634
1668
|
let newlineIdx;
|
|
1635
1669
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1636
1670
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -1945,11 +1979,13 @@ var MIN_LENGTH = 100;
|
|
|
1945
1979
|
var MAX_LENGTH = 5e3;
|
|
1946
1980
|
var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1947
1981
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
1982
|
+
var MAX_WALK_DEPTH = 10;
|
|
1948
1983
|
async function findRecentJsonlFiles() {
|
|
1949
1984
|
const projectsDir = path5.join(homedir(), ".claude", "projects");
|
|
1950
1985
|
const cutoff = Date.now() - SEVEN_DAYS_MS;
|
|
1951
1986
|
const files = [];
|
|
1952
|
-
async function walk(dir) {
|
|
1987
|
+
async function walk(dir, depth = 0) {
|
|
1988
|
+
if (depth > MAX_WALK_DEPTH) return;
|
|
1953
1989
|
let entries;
|
|
1954
1990
|
try {
|
|
1955
1991
|
entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -1959,7 +1995,7 @@ async function findRecentJsonlFiles() {
|
|
|
1959
1995
|
for (const entry of entries) {
|
|
1960
1996
|
const full = path5.join(dir, entry.name);
|
|
1961
1997
|
if (entry.isDirectory()) {
|
|
1962
|
-
await walk(full);
|
|
1998
|
+
await walk(full, depth + 1);
|
|
1963
1999
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
1964
2000
|
try {
|
|
1965
2001
|
const s = await stat(full);
|
|
@@ -10,15 +10,15 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
14
14
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
15
|
import path2 from "path";
|
|
16
|
-
import
|
|
16
|
+
import os2 from "os";
|
|
17
17
|
function resolveDataDir() {
|
|
18
18
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
19
19
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
|
-
const newDir = path2.join(
|
|
21
|
-
const legacyDir = path2.join(
|
|
20
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
21
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
22
22
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
23
|
try {
|
|
24
24
|
renameSync(legacyDir, newDir);
|
|
@@ -105,7 +105,7 @@ async function loadConfig() {
|
|
|
105
105
|
normalizeAutoUpdate(migratedCfg);
|
|
106
106
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
107
107
|
if (config.dbPath.startsWith("~")) {
|
|
108
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
108
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
109
109
|
}
|
|
110
110
|
return config;
|
|
111
111
|
} catch {
|
|
@@ -526,6 +526,7 @@ async function ensureSchema() {
|
|
|
526
526
|
const client = getRawClient();
|
|
527
527
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
528
528
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
529
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
529
530
|
try {
|
|
530
531
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
531
532
|
} catch {
|
|
@@ -1319,11 +1320,12 @@ async function ensureSchema() {
|
|
|
1319
1320
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1320
1321
|
import { existsSync } from "fs";
|
|
1321
1322
|
import path from "path";
|
|
1323
|
+
import os from "os";
|
|
1322
1324
|
import crypto from "crypto";
|
|
1323
1325
|
var SERVICE = "exe-mem";
|
|
1324
1326
|
var ACCOUNT = "master-key";
|
|
1325
1327
|
function getKeyDir() {
|
|
1326
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1328
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1327
1329
|
}
|
|
1328
1330
|
function getKeyPath() {
|
|
1329
1331
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1360,6 +1362,30 @@ async function getMasterKey() {
|
|
|
1360
1362
|
|
|
1361
1363
|
// src/lib/store.ts
|
|
1362
1364
|
init_config();
|
|
1365
|
+
var INIT_MAX_RETRIES = 3;
|
|
1366
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1367
|
+
function isBusyError2(err) {
|
|
1368
|
+
if (err instanceof Error) {
|
|
1369
|
+
const msg = err.message.toLowerCase();
|
|
1370
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1371
|
+
}
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
async function retryOnBusy2(fn, label) {
|
|
1375
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1376
|
+
try {
|
|
1377
|
+
return await fn();
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1380
|
+
process.stderr.write(
|
|
1381
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1382
|
+
`
|
|
1383
|
+
);
|
|
1384
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
throw new Error("unreachable");
|
|
1388
|
+
}
|
|
1363
1389
|
var _pendingRecords = [];
|
|
1364
1390
|
var _batchSize = 20;
|
|
1365
1391
|
var _flushIntervalMs = 1e4;
|
|
@@ -1394,14 +1420,17 @@ async function initStore(options) {
|
|
|
1394
1420
|
dbPath,
|
|
1395
1421
|
encryptionKey: hexKey
|
|
1396
1422
|
});
|
|
1397
|
-
await ensureSchema();
|
|
1423
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1398
1424
|
try {
|
|
1399
1425
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1400
1426
|
initShardManager2(hexKey);
|
|
1401
1427
|
} catch {
|
|
1402
1428
|
}
|
|
1403
1429
|
const client = getClient();
|
|
1404
|
-
const vResult = await
|
|
1430
|
+
const vResult = await retryOnBusy2(
|
|
1431
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1432
|
+
"version-query"
|
|
1433
|
+
);
|
|
1405
1434
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1406
1435
|
}
|
|
1407
1436
|
function vectorToBlob(vector) {
|
|
@@ -1429,8 +1458,13 @@ var _buffer = "";
|
|
|
1429
1458
|
var _requestCount = 0;
|
|
1430
1459
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
1431
1460
|
var _pending = /* @__PURE__ */ new Map();
|
|
1461
|
+
var MAX_BUFFER = 1e7;
|
|
1432
1462
|
function handleData(chunk) {
|
|
1433
1463
|
_buffer += chunk.toString();
|
|
1464
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1465
|
+
_buffer = "";
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1434
1468
|
let newlineIdx;
|
|
1435
1469
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1436
1470
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -10,15 +10,15 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
14
14
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
15
|
import path2 from "path";
|
|
16
|
-
import
|
|
16
|
+
import os2 from "os";
|
|
17
17
|
function resolveDataDir() {
|
|
18
18
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
19
19
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
|
-
const newDir = path2.join(
|
|
21
|
-
const legacyDir = path2.join(
|
|
20
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
21
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
22
22
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
23
|
try {
|
|
24
24
|
renameSync(legacyDir, newDir);
|
|
@@ -105,7 +105,7 @@ async function loadConfig() {
|
|
|
105
105
|
normalizeAutoUpdate(migratedCfg);
|
|
106
106
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
107
107
|
if (config.dbPath.startsWith("~")) {
|
|
108
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
108
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
109
109
|
}
|
|
110
110
|
return config;
|
|
111
111
|
} catch {
|
|
@@ -526,6 +526,7 @@ async function ensureSchema() {
|
|
|
526
526
|
const client = getRawClient();
|
|
527
527
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
528
528
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
529
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
529
530
|
try {
|
|
530
531
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
531
532
|
} catch {
|
|
@@ -1319,11 +1320,12 @@ async function ensureSchema() {
|
|
|
1319
1320
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1320
1321
|
import { existsSync } from "fs";
|
|
1321
1322
|
import path from "path";
|
|
1323
|
+
import os from "os";
|
|
1322
1324
|
import crypto from "crypto";
|
|
1323
1325
|
var SERVICE = "exe-mem";
|
|
1324
1326
|
var ACCOUNT = "master-key";
|
|
1325
1327
|
function getKeyDir() {
|
|
1326
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1328
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1327
1329
|
}
|
|
1328
1330
|
function getKeyPath() {
|
|
1329
1331
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1360,6 +1362,30 @@ async function getMasterKey() {
|
|
|
1360
1362
|
|
|
1361
1363
|
// src/lib/store.ts
|
|
1362
1364
|
init_config();
|
|
1365
|
+
var INIT_MAX_RETRIES = 3;
|
|
1366
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1367
|
+
function isBusyError2(err) {
|
|
1368
|
+
if (err instanceof Error) {
|
|
1369
|
+
const msg = err.message.toLowerCase();
|
|
1370
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1371
|
+
}
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
async function retryOnBusy2(fn, label) {
|
|
1375
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1376
|
+
try {
|
|
1377
|
+
return await fn();
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1380
|
+
process.stderr.write(
|
|
1381
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1382
|
+
`
|
|
1383
|
+
);
|
|
1384
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
throw new Error("unreachable");
|
|
1388
|
+
}
|
|
1363
1389
|
var _pendingRecords = [];
|
|
1364
1390
|
var _batchSize = 20;
|
|
1365
1391
|
var _flushIntervalMs = 1e4;
|
|
@@ -1394,14 +1420,17 @@ async function initStore(options) {
|
|
|
1394
1420
|
dbPath,
|
|
1395
1421
|
encryptionKey: hexKey
|
|
1396
1422
|
});
|
|
1397
|
-
await ensureSchema();
|
|
1423
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1398
1424
|
try {
|
|
1399
1425
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1400
1426
|
initShardManager2(hexKey);
|
|
1401
1427
|
} catch {
|
|
1402
1428
|
}
|
|
1403
1429
|
const client = getClient();
|
|
1404
|
-
const vResult = await
|
|
1430
|
+
const vResult = await retryOnBusy2(
|
|
1431
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1432
|
+
"version-query"
|
|
1433
|
+
);
|
|
1405
1434
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1406
1435
|
}
|
|
1407
1436
|
|