@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
|
@@ -203,6 +203,61 @@ var init_config = __esm({
|
|
|
203
203
|
}
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
+
// src/lib/db-retry.ts
|
|
207
|
+
function isBusyError(err) {
|
|
208
|
+
if (err instanceof Error) {
|
|
209
|
+
const msg = err.message.toLowerCase();
|
|
210
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
function delay(ms) {
|
|
215
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
216
|
+
}
|
|
217
|
+
async function retryOnBusy(fn, label) {
|
|
218
|
+
let lastError;
|
|
219
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
220
|
+
try {
|
|
221
|
+
return await fn();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
lastError = err;
|
|
224
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
228
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
229
|
+
process.stderr.write(
|
|
230
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
231
|
+
`
|
|
232
|
+
);
|
|
233
|
+
await delay(backoff + jitter);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
throw lastError;
|
|
237
|
+
}
|
|
238
|
+
function wrapWithRetry(client) {
|
|
239
|
+
return new Proxy(client, {
|
|
240
|
+
get(target, prop, receiver) {
|
|
241
|
+
if (prop === "execute") {
|
|
242
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
243
|
+
}
|
|
244
|
+
if (prop === "batch") {
|
|
245
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
246
|
+
}
|
|
247
|
+
return Reflect.get(target, prop, receiver);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
252
|
+
var init_db_retry = __esm({
|
|
253
|
+
"src/lib/db-retry.ts"() {
|
|
254
|
+
"use strict";
|
|
255
|
+
MAX_RETRIES = 3;
|
|
256
|
+
BASE_DELAY_MS = 200;
|
|
257
|
+
MAX_JITTER_MS = 300;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
206
261
|
// src/lib/database.ts
|
|
207
262
|
var database_exports = {};
|
|
208
263
|
__export(database_exports, {
|
|
@@ -210,6 +265,7 @@ __export(database_exports, {
|
|
|
210
265
|
disposeTurso: () => disposeTurso,
|
|
211
266
|
ensureSchema: () => ensureSchema,
|
|
212
267
|
getClient: () => getClient,
|
|
268
|
+
getRawClient: () => getRawClient,
|
|
213
269
|
initDatabase: () => initDatabase,
|
|
214
270
|
initTurso: () => initTurso,
|
|
215
271
|
isInitialized: () => isInitialized
|
|
@@ -219,6 +275,7 @@ async function initDatabase(config) {
|
|
|
219
275
|
if (_client) {
|
|
220
276
|
_client.close();
|
|
221
277
|
_client = null;
|
|
278
|
+
_resilientClient = null;
|
|
222
279
|
}
|
|
223
280
|
const opts = {
|
|
224
281
|
url: `file:${config.dbPath}`
|
|
@@ -227,20 +284,27 @@ async function initDatabase(config) {
|
|
|
227
284
|
opts.encryptionKey = config.encryptionKey;
|
|
228
285
|
}
|
|
229
286
|
_client = createClient(opts);
|
|
287
|
+
_resilientClient = wrapWithRetry(_client);
|
|
230
288
|
}
|
|
231
289
|
function isInitialized() {
|
|
232
290
|
return _client !== null;
|
|
233
291
|
}
|
|
234
292
|
function getClient() {
|
|
293
|
+
if (!_resilientClient) {
|
|
294
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
295
|
+
}
|
|
296
|
+
return _resilientClient;
|
|
297
|
+
}
|
|
298
|
+
function getRawClient() {
|
|
235
299
|
if (!_client) {
|
|
236
300
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
237
301
|
}
|
|
238
302
|
return _client;
|
|
239
303
|
}
|
|
240
304
|
async function ensureSchema() {
|
|
241
|
-
const client =
|
|
305
|
+
const client = getRawClient();
|
|
242
306
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
243
|
-
await client.execute("PRAGMA busy_timeout =
|
|
307
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
244
308
|
try {
|
|
245
309
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
246
310
|
} catch {
|
|
@@ -1033,13 +1097,16 @@ async function disposeDatabase() {
|
|
|
1033
1097
|
if (_client) {
|
|
1034
1098
|
_client.close();
|
|
1035
1099
|
_client = null;
|
|
1100
|
+
_resilientClient = null;
|
|
1036
1101
|
}
|
|
1037
1102
|
}
|
|
1038
|
-
var _client, initTurso, disposeTurso;
|
|
1103
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1039
1104
|
var init_database = __esm({
|
|
1040
1105
|
"src/lib/database.ts"() {
|
|
1041
1106
|
"use strict";
|
|
1107
|
+
init_db_retry();
|
|
1042
1108
|
_client = null;
|
|
1109
|
+
_resilientClient = null;
|
|
1043
1110
|
initTurso = initDatabase;
|
|
1044
1111
|
disposeTurso = disposeDatabase;
|
|
1045
1112
|
}
|
|
@@ -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 {
|
|
@@ -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 {
|
|
@@ -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 {
|
|
@@ -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 {
|