@askexenow/exe-os 0.8.32 → 0.8.36
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 +332 -348
- package/dist/bin/backfill-responses.js +72 -12
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1518 -1122
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +80 -18
- package/dist/bin/exe-boot.js +408 -89
- package/dist/bin/exe-call.js +83 -24
- package/dist/bin/exe-dispatch.js +18 -10
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +64 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +121 -36
- package/dist/bin/exe-heartbeat.js +77 -13
- package/dist/bin/exe-kill.js +64 -3
- package/dist/bin/exe-launch-agent.js +162 -35
- package/dist/bin/exe-link.js +946 -0
- package/dist/bin/exe-new-employee.js +121 -36
- package/dist/bin/exe-pending-messages.js +72 -7
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +75 -10
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +64 -4
- package/dist/bin/exe-search.js +79 -13
- package/dist/bin/exe-session-cleanup.js +91 -26
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +71 -4
- package/dist/bin/graph-backfill.js +64 -3
- package/dist/bin/graph-export.js +64 -3
- package/dist/bin/install.js +3 -3
- package/dist/bin/scan-tasks.js +71 -4
- package/dist/bin/setup.js +156 -38
- package/dist/bin/shard-migrate.js +64 -3
- package/dist/bin/wiki-sync.js +64 -3
- package/dist/gateway/index.js +122 -37
- package/dist/hooks/bug-report-worker.js +209 -23
- package/dist/hooks/commit-complete.js +71 -4
- package/dist/hooks/error-recall.js +79 -13
- package/dist/hooks/ingest-worker.js +129 -43
- package/dist/hooks/instructions-loaded.js +71 -4
- package/dist/hooks/notification.js +71 -4
- package/dist/hooks/post-compact.js +71 -4
- package/dist/hooks/pre-compact.js +71 -4
- package/dist/hooks/pre-tool-use.js +413 -194
- package/dist/hooks/prompt-ingest-worker.js +82 -22
- package/dist/hooks/prompt-submit.js +103 -37
- package/dist/hooks/response-ingest-worker.js +87 -22
- package/dist/hooks/session-end.js +71 -4
- package/dist/hooks/session-start.js +79 -13
- package/dist/hooks/stop.js +71 -4
- package/dist/hooks/subagent-stop.js +71 -4
- package/dist/hooks/summary-worker.js +303 -50
- package/dist/index.js +134 -46
- package/dist/lib/cloud-sync.js +209 -15
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +48 -22
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +136 -53
- package/dist/lib/hybrid-search.js +79 -13
- package/dist/lib/identity-templates.js +57 -6
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +22 -14
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +64 -3
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +48 -21
- package/dist/lib/tmux-routing.js +47 -20
- package/dist/mcp/server.js +727 -58
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +151 -24
- package/dist/mcp/tools/deactivate-behavior.js +3 -3
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +17 -8
- package/dist/mcp/tools/send-message.js +24 -16
- package/dist/mcp/tools/update-task.js +25 -16
- package/dist/runtime/index.js +112 -24
- package/dist/tui/App.js +139 -36
- package/package.json +6 -2
- package/src/commands/exe/rename.md +12 -0
|
@@ -23,12 +23,68 @@ var init_memory = __esm({
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// src/lib/db-retry.ts
|
|
27
|
+
function isBusyError(err) {
|
|
28
|
+
if (err instanceof Error) {
|
|
29
|
+
const msg = err.message.toLowerCase();
|
|
30
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function delay(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
async function retryOnBusy(fn, label) {
|
|
38
|
+
let lastError;
|
|
39
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
lastError = err;
|
|
44
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
48
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
51
|
+
`
|
|
52
|
+
);
|
|
53
|
+
await delay(backoff + jitter);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw lastError;
|
|
57
|
+
}
|
|
58
|
+
function wrapWithRetry(client) {
|
|
59
|
+
return new Proxy(client, {
|
|
60
|
+
get(target, prop, receiver) {
|
|
61
|
+
if (prop === "execute") {
|
|
62
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
63
|
+
}
|
|
64
|
+
if (prop === "batch") {
|
|
65
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
66
|
+
}
|
|
67
|
+
return Reflect.get(target, prop, receiver);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
72
|
+
var init_db_retry = __esm({
|
|
73
|
+
"src/lib/db-retry.ts"() {
|
|
74
|
+
"use strict";
|
|
75
|
+
MAX_RETRIES = 3;
|
|
76
|
+
BASE_DELAY_MS = 200;
|
|
77
|
+
MAX_JITTER_MS = 300;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
26
81
|
// src/lib/database.ts
|
|
27
82
|
import { createClient } from "@libsql/client";
|
|
28
83
|
async function initDatabase(config) {
|
|
29
84
|
if (_client) {
|
|
30
85
|
_client.close();
|
|
31
86
|
_client = null;
|
|
87
|
+
_resilientClient = null;
|
|
32
88
|
}
|
|
33
89
|
const opts = {
|
|
34
90
|
url: `file:${config.dbPath}`
|
|
@@ -37,20 +93,27 @@ async function initDatabase(config) {
|
|
|
37
93
|
opts.encryptionKey = config.encryptionKey;
|
|
38
94
|
}
|
|
39
95
|
_client = createClient(opts);
|
|
96
|
+
_resilientClient = wrapWithRetry(_client);
|
|
40
97
|
}
|
|
41
98
|
function isInitialized() {
|
|
42
99
|
return _client !== null;
|
|
43
100
|
}
|
|
44
101
|
function getClient() {
|
|
102
|
+
if (!_resilientClient) {
|
|
103
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
104
|
+
}
|
|
105
|
+
return _resilientClient;
|
|
106
|
+
}
|
|
107
|
+
function getRawClient() {
|
|
45
108
|
if (!_client) {
|
|
46
109
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
47
110
|
}
|
|
48
111
|
return _client;
|
|
49
112
|
}
|
|
50
113
|
async function ensureSchema() {
|
|
51
|
-
const client =
|
|
114
|
+
const client = getRawClient();
|
|
52
115
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
53
|
-
await client.execute("PRAGMA busy_timeout =
|
|
116
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
54
117
|
try {
|
|
55
118
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
56
119
|
} catch {
|
|
@@ -839,11 +902,13 @@ async function ensureSchema() {
|
|
|
839
902
|
}
|
|
840
903
|
}
|
|
841
904
|
}
|
|
842
|
-
var _client, initTurso;
|
|
905
|
+
var _client, _resilientClient, initTurso;
|
|
843
906
|
var init_database = __esm({
|
|
844
907
|
"src/lib/database.ts"() {
|
|
845
908
|
"use strict";
|
|
909
|
+
init_db_retry();
|
|
846
910
|
_client = null;
|
|
911
|
+
_resilientClient = null;
|
|
847
912
|
initTurso = initDatabase;
|
|
848
913
|
}
|
|
849
914
|
});
|
|
@@ -1149,7 +1214,7 @@ function listShards() {
|
|
|
1149
1214
|
}
|
|
1150
1215
|
async function ensureShardSchema(client) {
|
|
1151
1216
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1152
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1217
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1153
1218
|
try {
|
|
1154
1219
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1155
1220
|
} catch {
|
|
@@ -1510,11 +1575,11 @@ async function connectEmbedDaemon() {
|
|
|
1510
1575
|
}
|
|
1511
1576
|
}
|
|
1512
1577
|
const start = Date.now();
|
|
1513
|
-
let
|
|
1578
|
+
let delay2 = 100;
|
|
1514
1579
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1515
|
-
await new Promise((r) => setTimeout(r,
|
|
1580
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1516
1581
|
if (await connectToSocket()) return true;
|
|
1517
|
-
|
|
1582
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1518
1583
|
}
|
|
1519
1584
|
return false;
|
|
1520
1585
|
}
|
|
@@ -1606,11 +1671,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
1606
1671
|
`);
|
|
1607
1672
|
killAndRespawnDaemon();
|
|
1608
1673
|
const start = Date.now();
|
|
1609
|
-
let
|
|
1674
|
+
let delay2 = 200;
|
|
1610
1675
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1611
|
-
await new Promise((r) => setTimeout(r,
|
|
1676
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1612
1677
|
if (await connectToSocket()) break;
|
|
1613
|
-
|
|
1678
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1614
1679
|
}
|
|
1615
1680
|
if (!_connected) return null;
|
|
1616
1681
|
}
|
|
@@ -1622,11 +1687,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
1622
1687
|
`);
|
|
1623
1688
|
killAndRespawnDaemon();
|
|
1624
1689
|
const start = Date.now();
|
|
1625
|
-
let
|
|
1690
|
+
let delay2 = 200;
|
|
1626
1691
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1627
|
-
await new Promise((r) => setTimeout(r,
|
|
1692
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1628
1693
|
if (await connectToSocket()) break;
|
|
1629
|
-
|
|
1694
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1630
1695
|
}
|
|
1631
1696
|
if (!_connected) return null;
|
|
1632
1697
|
const retry = await sendRequest([text], priority);
|
|
@@ -1739,7 +1804,7 @@ var init_embedder = __esm({
|
|
|
1739
1804
|
|
|
1740
1805
|
// src/lib/employees.ts
|
|
1741
1806
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
1742
|
-
import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
|
|
1807
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
|
|
1743
1808
|
import { execSync as execSync2 } from "child_process";
|
|
1744
1809
|
import path6 from "path";
|
|
1745
1810
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
@@ -1763,7 +1828,7 @@ var init_employees = __esm({
|
|
|
1763
1828
|
});
|
|
1764
1829
|
|
|
1765
1830
|
// src/lib/license.ts
|
|
1766
|
-
import { readFileSync as
|
|
1831
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
1767
1832
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1768
1833
|
import path7 from "path";
|
|
1769
1834
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -1771,14 +1836,14 @@ function loadDeviceId() {
|
|
|
1771
1836
|
const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
|
|
1772
1837
|
try {
|
|
1773
1838
|
if (existsSync6(deviceJsonPath)) {
|
|
1774
|
-
const data = JSON.parse(
|
|
1839
|
+
const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
|
|
1775
1840
|
if (data.deviceId) return data.deviceId;
|
|
1776
1841
|
}
|
|
1777
1842
|
} catch {
|
|
1778
1843
|
}
|
|
1779
1844
|
try {
|
|
1780
1845
|
if (existsSync6(DEVICE_ID_PATH)) {
|
|
1781
|
-
const id2 =
|
|
1846
|
+
const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
|
|
1782
1847
|
if (id2) return id2;
|
|
1783
1848
|
}
|
|
1784
1849
|
} catch {
|
|
@@ -1791,7 +1856,7 @@ function loadDeviceId() {
|
|
|
1791
1856
|
function loadLicense() {
|
|
1792
1857
|
try {
|
|
1793
1858
|
if (!existsSync6(LICENSE_PATH)) return null;
|
|
1794
|
-
return
|
|
1859
|
+
return readFileSync4(LICENSE_PATH, "utf8").trim();
|
|
1795
1860
|
} catch {
|
|
1796
1861
|
return null;
|
|
1797
1862
|
}
|
|
@@ -1821,7 +1886,7 @@ async function verifyLicenseJwt(token) {
|
|
|
1821
1886
|
async function getCachedLicense() {
|
|
1822
1887
|
try {
|
|
1823
1888
|
if (!existsSync6(CACHE_PATH)) return null;
|
|
1824
|
-
const raw = JSON.parse(
|
|
1889
|
+
const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
|
|
1825
1890
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
1826
1891
|
return await verifyLicenseJwt(raw.token);
|
|
1827
1892
|
} catch {
|
|
@@ -1937,12 +2002,12 @@ __export(plan_limits_exports, {
|
|
|
1937
2002
|
countActiveMemories: () => countActiveMemories,
|
|
1938
2003
|
getLicenseSync: () => getLicenseSync
|
|
1939
2004
|
});
|
|
1940
|
-
import { readFileSync as
|
|
2005
|
+
import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
|
|
1941
2006
|
import path8 from "path";
|
|
1942
2007
|
function getLicenseSync() {
|
|
1943
2008
|
try {
|
|
1944
2009
|
if (!existsSync7(CACHE_PATH2)) return freeLicense();
|
|
1945
|
-
const raw = JSON.parse(
|
|
2010
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH2, "utf8"));
|
|
1946
2011
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
1947
2012
|
const parts = raw.token.split(".");
|
|
1948
2013
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2010,7 +2075,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2010
2075
|
let count = 0;
|
|
2011
2076
|
try {
|
|
2012
2077
|
if (existsSync7(filePath)) {
|
|
2013
|
-
const raw =
|
|
2078
|
+
const raw = readFileSync5(filePath, "utf8");
|
|
2014
2079
|
const employees = JSON.parse(raw);
|
|
2015
2080
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2016
2081
|
}
|
|
@@ -212,6 +212,61 @@ var init_memory = __esm({
|
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
// src/lib/db-retry.ts
|
|
216
|
+
function isBusyError(err) {
|
|
217
|
+
if (err instanceof Error) {
|
|
218
|
+
const msg = err.message.toLowerCase();
|
|
219
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
function delay(ms) {
|
|
224
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
225
|
+
}
|
|
226
|
+
async function retryOnBusy(fn, label) {
|
|
227
|
+
let lastError;
|
|
228
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
229
|
+
try {
|
|
230
|
+
return await fn();
|
|
231
|
+
} catch (err) {
|
|
232
|
+
lastError = err;
|
|
233
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
237
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
238
|
+
process.stderr.write(
|
|
239
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
240
|
+
`
|
|
241
|
+
);
|
|
242
|
+
await delay(backoff + jitter);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
throw lastError;
|
|
246
|
+
}
|
|
247
|
+
function wrapWithRetry(client) {
|
|
248
|
+
return new Proxy(client, {
|
|
249
|
+
get(target, prop, receiver) {
|
|
250
|
+
if (prop === "execute") {
|
|
251
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
252
|
+
}
|
|
253
|
+
if (prop === "batch") {
|
|
254
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
255
|
+
}
|
|
256
|
+
return Reflect.get(target, prop, receiver);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
261
|
+
var init_db_retry = __esm({
|
|
262
|
+
"src/lib/db-retry.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
MAX_RETRIES = 3;
|
|
265
|
+
BASE_DELAY_MS = 200;
|
|
266
|
+
MAX_JITTER_MS = 300;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
215
270
|
// src/lib/database.ts
|
|
216
271
|
var database_exports = {};
|
|
217
272
|
__export(database_exports, {
|
|
@@ -219,6 +274,7 @@ __export(database_exports, {
|
|
|
219
274
|
disposeTurso: () => disposeTurso,
|
|
220
275
|
ensureSchema: () => ensureSchema,
|
|
221
276
|
getClient: () => getClient,
|
|
277
|
+
getRawClient: () => getRawClient,
|
|
222
278
|
initDatabase: () => initDatabase,
|
|
223
279
|
initTurso: () => initTurso,
|
|
224
280
|
isInitialized: () => isInitialized
|
|
@@ -228,6 +284,7 @@ async function initDatabase(config) {
|
|
|
228
284
|
if (_client) {
|
|
229
285
|
_client.close();
|
|
230
286
|
_client = null;
|
|
287
|
+
_resilientClient = null;
|
|
231
288
|
}
|
|
232
289
|
const opts = {
|
|
233
290
|
url: `file:${config.dbPath}`
|
|
@@ -236,20 +293,27 @@ async function initDatabase(config) {
|
|
|
236
293
|
opts.encryptionKey = config.encryptionKey;
|
|
237
294
|
}
|
|
238
295
|
_client = createClient(opts);
|
|
296
|
+
_resilientClient = wrapWithRetry(_client);
|
|
239
297
|
}
|
|
240
298
|
function isInitialized() {
|
|
241
299
|
return _client !== null;
|
|
242
300
|
}
|
|
243
301
|
function getClient() {
|
|
302
|
+
if (!_resilientClient) {
|
|
303
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
304
|
+
}
|
|
305
|
+
return _resilientClient;
|
|
306
|
+
}
|
|
307
|
+
function getRawClient() {
|
|
244
308
|
if (!_client) {
|
|
245
309
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
246
310
|
}
|
|
247
311
|
return _client;
|
|
248
312
|
}
|
|
249
313
|
async function ensureSchema() {
|
|
250
|
-
const client =
|
|
314
|
+
const client = getRawClient();
|
|
251
315
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
252
|
-
await client.execute("PRAGMA busy_timeout =
|
|
316
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
253
317
|
try {
|
|
254
318
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
255
319
|
} catch {
|
|
@@ -1042,13 +1106,16 @@ async function disposeDatabase() {
|
|
|
1042
1106
|
if (_client) {
|
|
1043
1107
|
_client.close();
|
|
1044
1108
|
_client = null;
|
|
1109
|
+
_resilientClient = null;
|
|
1045
1110
|
}
|
|
1046
1111
|
}
|
|
1047
|
-
var _client, initTurso, disposeTurso;
|
|
1112
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1048
1113
|
var init_database = __esm({
|
|
1049
1114
|
"src/lib/database.ts"() {
|
|
1050
1115
|
"use strict";
|
|
1116
|
+
init_db_retry();
|
|
1051
1117
|
_client = null;
|
|
1118
|
+
_resilientClient = null;
|
|
1052
1119
|
initTurso = initDatabase;
|
|
1053
1120
|
disposeTurso = disposeDatabase;
|
|
1054
1121
|
}
|
|
@@ -1161,7 +1228,7 @@ function listShards() {
|
|
|
1161
1228
|
}
|
|
1162
1229
|
async function ensureShardSchema(client) {
|
|
1163
1230
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1164
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1231
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1165
1232
|
try {
|
|
1166
1233
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1167
1234
|
} catch {
|
|
@@ -266,12 +266,68 @@ var init_memory = __esm({
|
|
|
266
266
|
}
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
+
// src/lib/db-retry.ts
|
|
270
|
+
function isBusyError(err) {
|
|
271
|
+
if (err instanceof Error) {
|
|
272
|
+
const msg = err.message.toLowerCase();
|
|
273
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
function delay(ms) {
|
|
278
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
279
|
+
}
|
|
280
|
+
async function retryOnBusy(fn, label) {
|
|
281
|
+
let lastError;
|
|
282
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
283
|
+
try {
|
|
284
|
+
return await fn();
|
|
285
|
+
} catch (err) {
|
|
286
|
+
lastError = err;
|
|
287
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
288
|
+
throw err;
|
|
289
|
+
}
|
|
290
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
291
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
292
|
+
process.stderr.write(
|
|
293
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
294
|
+
`
|
|
295
|
+
);
|
|
296
|
+
await delay(backoff + jitter);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
throw lastError;
|
|
300
|
+
}
|
|
301
|
+
function wrapWithRetry(client) {
|
|
302
|
+
return new Proxy(client, {
|
|
303
|
+
get(target, prop, receiver) {
|
|
304
|
+
if (prop === "execute") {
|
|
305
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
306
|
+
}
|
|
307
|
+
if (prop === "batch") {
|
|
308
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
309
|
+
}
|
|
310
|
+
return Reflect.get(target, prop, receiver);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
315
|
+
var init_db_retry = __esm({
|
|
316
|
+
"src/lib/db-retry.ts"() {
|
|
317
|
+
"use strict";
|
|
318
|
+
MAX_RETRIES = 3;
|
|
319
|
+
BASE_DELAY_MS = 200;
|
|
320
|
+
MAX_JITTER_MS = 300;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
269
324
|
// src/lib/database.ts
|
|
270
325
|
import { createClient } from "@libsql/client";
|
|
271
326
|
async function initDatabase(config) {
|
|
272
327
|
if (_client) {
|
|
273
328
|
_client.close();
|
|
274
329
|
_client = null;
|
|
330
|
+
_resilientClient = null;
|
|
275
331
|
}
|
|
276
332
|
const opts = {
|
|
277
333
|
url: `file:${config.dbPath}`
|
|
@@ -280,17 +336,24 @@ async function initDatabase(config) {
|
|
|
280
336
|
opts.encryptionKey = config.encryptionKey;
|
|
281
337
|
}
|
|
282
338
|
_client = createClient(opts);
|
|
339
|
+
_resilientClient = wrapWithRetry(_client);
|
|
283
340
|
}
|
|
284
341
|
function getClient() {
|
|
342
|
+
if (!_resilientClient) {
|
|
343
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
344
|
+
}
|
|
345
|
+
return _resilientClient;
|
|
346
|
+
}
|
|
347
|
+
function getRawClient() {
|
|
285
348
|
if (!_client) {
|
|
286
349
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
287
350
|
}
|
|
288
351
|
return _client;
|
|
289
352
|
}
|
|
290
353
|
async function ensureSchema() {
|
|
291
|
-
const client =
|
|
354
|
+
const client = getRawClient();
|
|
292
355
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
293
|
-
await client.execute("PRAGMA busy_timeout =
|
|
356
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
294
357
|
try {
|
|
295
358
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
296
359
|
} catch {
|
|
@@ -1083,13 +1146,16 @@ async function disposeDatabase() {
|
|
|
1083
1146
|
if (_client) {
|
|
1084
1147
|
_client.close();
|
|
1085
1148
|
_client = null;
|
|
1149
|
+
_resilientClient = null;
|
|
1086
1150
|
}
|
|
1087
1151
|
}
|
|
1088
|
-
var _client, initTurso, disposeTurso;
|
|
1152
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1089
1153
|
var init_database = __esm({
|
|
1090
1154
|
"src/lib/database.ts"() {
|
|
1091
1155
|
"use strict";
|
|
1156
|
+
init_db_retry();
|
|
1092
1157
|
_client = null;
|
|
1158
|
+
_resilientClient = null;
|
|
1093
1159
|
initTurso = initDatabase;
|
|
1094
1160
|
disposeTurso = disposeDatabase;
|
|
1095
1161
|
}
|
|
@@ -1202,7 +1268,7 @@ function listShards() {
|
|
|
1202
1268
|
}
|
|
1203
1269
|
async function ensureShardSchema(client) {
|
|
1204
1270
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1205
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1271
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1206
1272
|
try {
|
|
1207
1273
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1208
1274
|
} catch {
|
|
@@ -2087,11 +2153,11 @@ async function connectEmbedDaemon() {
|
|
|
2087
2153
|
}
|
|
2088
2154
|
}
|
|
2089
2155
|
const start = Date.now();
|
|
2090
|
-
let
|
|
2156
|
+
let delay2 = 100;
|
|
2091
2157
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2092
|
-
await new Promise((r) => setTimeout(r,
|
|
2158
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2093
2159
|
if (await connectToSocket()) return true;
|
|
2094
|
-
|
|
2160
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2095
2161
|
}
|
|
2096
2162
|
return false;
|
|
2097
2163
|
}
|
|
@@ -2183,11 +2249,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2183
2249
|
`);
|
|
2184
2250
|
killAndRespawnDaemon();
|
|
2185
2251
|
const start = Date.now();
|
|
2186
|
-
let
|
|
2252
|
+
let delay2 = 200;
|
|
2187
2253
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2188
|
-
await new Promise((r) => setTimeout(r,
|
|
2254
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2189
2255
|
if (await connectToSocket()) break;
|
|
2190
|
-
|
|
2256
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2191
2257
|
}
|
|
2192
2258
|
if (!_connected) return null;
|
|
2193
2259
|
}
|
|
@@ -2199,11 +2265,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2199
2265
|
`);
|
|
2200
2266
|
killAndRespawnDaemon();
|
|
2201
2267
|
const start = Date.now();
|
|
2202
|
-
let
|
|
2268
|
+
let delay2 = 200;
|
|
2203
2269
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2204
|
-
await new Promise((r) => setTimeout(r,
|
|
2270
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2205
2271
|
if (await connectToSocket()) break;
|
|
2206
|
-
|
|
2272
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2207
2273
|
}
|
|
2208
2274
|
if (!_connected) return null;
|
|
2209
2275
|
const retry = await sendRequest([text], priority);
|
package/dist/hooks/stop.js
CHANGED
|
@@ -231,6 +231,61 @@ var init_memory = __esm({
|
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
// src/lib/db-retry.ts
|
|
235
|
+
function isBusyError(err) {
|
|
236
|
+
if (err instanceof Error) {
|
|
237
|
+
const msg = err.message.toLowerCase();
|
|
238
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
function delay(ms) {
|
|
243
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
244
|
+
}
|
|
245
|
+
async function retryOnBusy(fn, label) {
|
|
246
|
+
let lastError;
|
|
247
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
248
|
+
try {
|
|
249
|
+
return await fn();
|
|
250
|
+
} catch (err) {
|
|
251
|
+
lastError = err;
|
|
252
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
256
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
257
|
+
process.stderr.write(
|
|
258
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
259
|
+
`
|
|
260
|
+
);
|
|
261
|
+
await delay(backoff + jitter);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
throw lastError;
|
|
265
|
+
}
|
|
266
|
+
function wrapWithRetry(client) {
|
|
267
|
+
return new Proxy(client, {
|
|
268
|
+
get(target, prop, receiver) {
|
|
269
|
+
if (prop === "execute") {
|
|
270
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
271
|
+
}
|
|
272
|
+
if (prop === "batch") {
|
|
273
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
274
|
+
}
|
|
275
|
+
return Reflect.get(target, prop, receiver);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
280
|
+
var init_db_retry = __esm({
|
|
281
|
+
"src/lib/db-retry.ts"() {
|
|
282
|
+
"use strict";
|
|
283
|
+
MAX_RETRIES = 3;
|
|
284
|
+
BASE_DELAY_MS = 200;
|
|
285
|
+
MAX_JITTER_MS = 300;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
234
289
|
// src/lib/database.ts
|
|
235
290
|
var database_exports = {};
|
|
236
291
|
__export(database_exports, {
|
|
@@ -238,6 +293,7 @@ __export(database_exports, {
|
|
|
238
293
|
disposeTurso: () => disposeTurso,
|
|
239
294
|
ensureSchema: () => ensureSchema,
|
|
240
295
|
getClient: () => getClient,
|
|
296
|
+
getRawClient: () => getRawClient,
|
|
241
297
|
initDatabase: () => initDatabase,
|
|
242
298
|
initTurso: () => initTurso,
|
|
243
299
|
isInitialized: () => isInitialized
|
|
@@ -247,6 +303,7 @@ async function initDatabase(config) {
|
|
|
247
303
|
if (_client) {
|
|
248
304
|
_client.close();
|
|
249
305
|
_client = null;
|
|
306
|
+
_resilientClient = null;
|
|
250
307
|
}
|
|
251
308
|
const opts = {
|
|
252
309
|
url: `file:${config.dbPath}`
|
|
@@ -255,20 +312,27 @@ async function initDatabase(config) {
|
|
|
255
312
|
opts.encryptionKey = config.encryptionKey;
|
|
256
313
|
}
|
|
257
314
|
_client = createClient(opts);
|
|
315
|
+
_resilientClient = wrapWithRetry(_client);
|
|
258
316
|
}
|
|
259
317
|
function isInitialized() {
|
|
260
318
|
return _client !== null;
|
|
261
319
|
}
|
|
262
320
|
function getClient() {
|
|
321
|
+
if (!_resilientClient) {
|
|
322
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
323
|
+
}
|
|
324
|
+
return _resilientClient;
|
|
325
|
+
}
|
|
326
|
+
function getRawClient() {
|
|
263
327
|
if (!_client) {
|
|
264
328
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
265
329
|
}
|
|
266
330
|
return _client;
|
|
267
331
|
}
|
|
268
332
|
async function ensureSchema() {
|
|
269
|
-
const client =
|
|
333
|
+
const client = getRawClient();
|
|
270
334
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
271
|
-
await client.execute("PRAGMA busy_timeout =
|
|
335
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
272
336
|
try {
|
|
273
337
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
274
338
|
} catch {
|
|
@@ -1061,13 +1125,16 @@ async function disposeDatabase() {
|
|
|
1061
1125
|
if (_client) {
|
|
1062
1126
|
_client.close();
|
|
1063
1127
|
_client = null;
|
|
1128
|
+
_resilientClient = null;
|
|
1064
1129
|
}
|
|
1065
1130
|
}
|
|
1066
|
-
var _client, initTurso, disposeTurso;
|
|
1131
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1067
1132
|
var init_database = __esm({
|
|
1068
1133
|
"src/lib/database.ts"() {
|
|
1069
1134
|
"use strict";
|
|
1135
|
+
init_db_retry();
|
|
1070
1136
|
_client = null;
|
|
1137
|
+
_resilientClient = null;
|
|
1071
1138
|
initTurso = initDatabase;
|
|
1072
1139
|
disposeTurso = disposeDatabase;
|
|
1073
1140
|
}
|
|
@@ -1180,7 +1247,7 @@ function listShards() {
|
|
|
1180
1247
|
}
|
|
1181
1248
|
async function ensureShardSchema(client) {
|
|
1182
1249
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1183
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1250
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1184
1251
|
try {
|
|
1185
1252
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1186
1253
|
} catch {
|