@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
package/dist/bin/exe-team.js
CHANGED
|
@@ -262,7 +262,7 @@ function listShards() {
|
|
|
262
262
|
}
|
|
263
263
|
async function ensureShardSchema(client) {
|
|
264
264
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
265
|
-
await client.execute("PRAGMA busy_timeout =
|
|
265
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
266
266
|
try {
|
|
267
267
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
268
268
|
} catch {
|
|
@@ -449,7 +449,7 @@ var init_shard_manager = __esm({
|
|
|
449
449
|
// src/lib/employees.ts
|
|
450
450
|
init_config();
|
|
451
451
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
452
|
-
import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
|
|
452
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
|
|
453
453
|
import { execSync } from "child_process";
|
|
454
454
|
import path2 from "path";
|
|
455
455
|
var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
@@ -467,12 +467,65 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
467
467
|
|
|
468
468
|
// src/lib/database.ts
|
|
469
469
|
import { createClient } from "@libsql/client";
|
|
470
|
+
|
|
471
|
+
// src/lib/db-retry.ts
|
|
472
|
+
var MAX_RETRIES = 3;
|
|
473
|
+
var BASE_DELAY_MS = 200;
|
|
474
|
+
var MAX_JITTER_MS = 300;
|
|
475
|
+
function isBusyError(err) {
|
|
476
|
+
if (err instanceof Error) {
|
|
477
|
+
const msg = err.message.toLowerCase();
|
|
478
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
479
|
+
}
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
function delay(ms) {
|
|
483
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
484
|
+
}
|
|
485
|
+
async function retryOnBusy(fn, label) {
|
|
486
|
+
let lastError;
|
|
487
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
488
|
+
try {
|
|
489
|
+
return await fn();
|
|
490
|
+
} catch (err) {
|
|
491
|
+
lastError = err;
|
|
492
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
493
|
+
throw err;
|
|
494
|
+
}
|
|
495
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
496
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
497
|
+
process.stderr.write(
|
|
498
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
499
|
+
`
|
|
500
|
+
);
|
|
501
|
+
await delay(backoff + jitter);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
throw lastError;
|
|
505
|
+
}
|
|
506
|
+
function wrapWithRetry(client) {
|
|
507
|
+
return new Proxy(client, {
|
|
508
|
+
get(target, prop, receiver) {
|
|
509
|
+
if (prop === "execute") {
|
|
510
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
511
|
+
}
|
|
512
|
+
if (prop === "batch") {
|
|
513
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
514
|
+
}
|
|
515
|
+
return Reflect.get(target, prop, receiver);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/lib/database.ts
|
|
470
521
|
var _client = null;
|
|
522
|
+
var _resilientClient = null;
|
|
471
523
|
var initTurso = initDatabase;
|
|
472
524
|
async function initDatabase(config) {
|
|
473
525
|
if (_client) {
|
|
474
526
|
_client.close();
|
|
475
527
|
_client = null;
|
|
528
|
+
_resilientClient = null;
|
|
476
529
|
}
|
|
477
530
|
const opts = {
|
|
478
531
|
url: `file:${config.dbPath}`
|
|
@@ -481,17 +534,24 @@ async function initDatabase(config) {
|
|
|
481
534
|
opts.encryptionKey = config.encryptionKey;
|
|
482
535
|
}
|
|
483
536
|
_client = createClient(opts);
|
|
537
|
+
_resilientClient = wrapWithRetry(_client);
|
|
484
538
|
}
|
|
485
539
|
function getClient() {
|
|
540
|
+
if (!_resilientClient) {
|
|
541
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
542
|
+
}
|
|
543
|
+
return _resilientClient;
|
|
544
|
+
}
|
|
545
|
+
function getRawClient() {
|
|
486
546
|
if (!_client) {
|
|
487
547
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
488
548
|
}
|
|
489
549
|
return _client;
|
|
490
550
|
}
|
|
491
551
|
async function ensureSchema() {
|
|
492
|
-
const client =
|
|
552
|
+
const client = getRawClient();
|
|
493
553
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
494
|
-
await client.execute("PRAGMA busy_timeout =
|
|
554
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
495
555
|
try {
|
|
496
556
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
497
557
|
} catch {
|
package/dist/bin/git-sweep.js
CHANGED
|
@@ -24,6 +24,61 @@ var init_memory = __esm({
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
// src/lib/db-retry.ts
|
|
28
|
+
function isBusyError(err) {
|
|
29
|
+
if (err instanceof Error) {
|
|
30
|
+
const msg = err.message.toLowerCase();
|
|
31
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function delay(ms) {
|
|
36
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
37
|
+
}
|
|
38
|
+
async function retryOnBusy(fn, label) {
|
|
39
|
+
let lastError;
|
|
40
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
41
|
+
try {
|
|
42
|
+
return await fn();
|
|
43
|
+
} catch (err) {
|
|
44
|
+
lastError = err;
|
|
45
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
49
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
50
|
+
process.stderr.write(
|
|
51
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
52
|
+
`
|
|
53
|
+
);
|
|
54
|
+
await delay(backoff + jitter);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
throw lastError;
|
|
58
|
+
}
|
|
59
|
+
function wrapWithRetry(client) {
|
|
60
|
+
return new Proxy(client, {
|
|
61
|
+
get(target, prop, receiver) {
|
|
62
|
+
if (prop === "execute") {
|
|
63
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
64
|
+
}
|
|
65
|
+
if (prop === "batch") {
|
|
66
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
67
|
+
}
|
|
68
|
+
return Reflect.get(target, prop, receiver);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
73
|
+
var init_db_retry = __esm({
|
|
74
|
+
"src/lib/db-retry.ts"() {
|
|
75
|
+
"use strict";
|
|
76
|
+
MAX_RETRIES = 3;
|
|
77
|
+
BASE_DELAY_MS = 200;
|
|
78
|
+
MAX_JITTER_MS = 300;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
27
82
|
// src/lib/database.ts
|
|
28
83
|
var database_exports = {};
|
|
29
84
|
__export(database_exports, {
|
|
@@ -31,6 +86,7 @@ __export(database_exports, {
|
|
|
31
86
|
disposeTurso: () => disposeTurso,
|
|
32
87
|
ensureSchema: () => ensureSchema,
|
|
33
88
|
getClient: () => getClient,
|
|
89
|
+
getRawClient: () => getRawClient,
|
|
34
90
|
initDatabase: () => initDatabase,
|
|
35
91
|
initTurso: () => initTurso,
|
|
36
92
|
isInitialized: () => isInitialized
|
|
@@ -40,6 +96,7 @@ async function initDatabase(config) {
|
|
|
40
96
|
if (_client) {
|
|
41
97
|
_client.close();
|
|
42
98
|
_client = null;
|
|
99
|
+
_resilientClient = null;
|
|
43
100
|
}
|
|
44
101
|
const opts = {
|
|
45
102
|
url: `file:${config.dbPath}`
|
|
@@ -48,20 +105,27 @@ async function initDatabase(config) {
|
|
|
48
105
|
opts.encryptionKey = config.encryptionKey;
|
|
49
106
|
}
|
|
50
107
|
_client = createClient(opts);
|
|
108
|
+
_resilientClient = wrapWithRetry(_client);
|
|
51
109
|
}
|
|
52
110
|
function isInitialized() {
|
|
53
111
|
return _client !== null;
|
|
54
112
|
}
|
|
55
113
|
function getClient() {
|
|
114
|
+
if (!_resilientClient) {
|
|
115
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
116
|
+
}
|
|
117
|
+
return _resilientClient;
|
|
118
|
+
}
|
|
119
|
+
function getRawClient() {
|
|
56
120
|
if (!_client) {
|
|
57
121
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
58
122
|
}
|
|
59
123
|
return _client;
|
|
60
124
|
}
|
|
61
125
|
async function ensureSchema() {
|
|
62
|
-
const client =
|
|
126
|
+
const client = getRawClient();
|
|
63
127
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
64
|
-
await client.execute("PRAGMA busy_timeout =
|
|
128
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
65
129
|
try {
|
|
66
130
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
67
131
|
} catch {
|
|
@@ -854,13 +918,16 @@ async function disposeDatabase() {
|
|
|
854
918
|
if (_client) {
|
|
855
919
|
_client.close();
|
|
856
920
|
_client = null;
|
|
921
|
+
_resilientClient = null;
|
|
857
922
|
}
|
|
858
923
|
}
|
|
859
|
-
var _client, initTurso, disposeTurso;
|
|
924
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
860
925
|
var init_database = __esm({
|
|
861
926
|
"src/lib/database.ts"() {
|
|
862
927
|
"use strict";
|
|
928
|
+
init_db_retry();
|
|
863
929
|
_client = null;
|
|
930
|
+
_resilientClient = null;
|
|
864
931
|
initTurso = initDatabase;
|
|
865
932
|
disposeTurso = disposeDatabase;
|
|
866
933
|
}
|
|
@@ -1162,7 +1229,7 @@ function listShards() {
|
|
|
1162
1229
|
}
|
|
1163
1230
|
async function ensureShardSchema(client) {
|
|
1164
1231
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1165
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1232
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1166
1233
|
try {
|
|
1167
1234
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1168
1235
|
} catch {
|
|
@@ -262,7 +262,7 @@ function listShards() {
|
|
|
262
262
|
}
|
|
263
263
|
async function ensureShardSchema(client) {
|
|
264
264
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
265
|
-
await client.execute("PRAGMA busy_timeout =
|
|
265
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
266
266
|
try {
|
|
267
267
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
268
268
|
} catch {
|
|
@@ -448,12 +448,65 @@ var init_shard_manager = __esm({
|
|
|
448
448
|
|
|
449
449
|
// src/lib/database.ts
|
|
450
450
|
import { createClient } from "@libsql/client";
|
|
451
|
+
|
|
452
|
+
// src/lib/db-retry.ts
|
|
453
|
+
var MAX_RETRIES = 3;
|
|
454
|
+
var BASE_DELAY_MS = 200;
|
|
455
|
+
var MAX_JITTER_MS = 300;
|
|
456
|
+
function isBusyError(err) {
|
|
457
|
+
if (err instanceof Error) {
|
|
458
|
+
const msg = err.message.toLowerCase();
|
|
459
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
function delay(ms) {
|
|
464
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
465
|
+
}
|
|
466
|
+
async function retryOnBusy(fn, label) {
|
|
467
|
+
let lastError;
|
|
468
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
469
|
+
try {
|
|
470
|
+
return await fn();
|
|
471
|
+
} catch (err) {
|
|
472
|
+
lastError = err;
|
|
473
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
474
|
+
throw err;
|
|
475
|
+
}
|
|
476
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
477
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
478
|
+
process.stderr.write(
|
|
479
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
480
|
+
`
|
|
481
|
+
);
|
|
482
|
+
await delay(backoff + jitter);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
throw lastError;
|
|
486
|
+
}
|
|
487
|
+
function wrapWithRetry(client) {
|
|
488
|
+
return new Proxy(client, {
|
|
489
|
+
get(target, prop, receiver) {
|
|
490
|
+
if (prop === "execute") {
|
|
491
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
492
|
+
}
|
|
493
|
+
if (prop === "batch") {
|
|
494
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
495
|
+
}
|
|
496
|
+
return Reflect.get(target, prop, receiver);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/lib/database.ts
|
|
451
502
|
var _client = null;
|
|
503
|
+
var _resilientClient = null;
|
|
452
504
|
var initTurso = initDatabase;
|
|
453
505
|
async function initDatabase(config) {
|
|
454
506
|
if (_client) {
|
|
455
507
|
_client.close();
|
|
456
508
|
_client = null;
|
|
509
|
+
_resilientClient = null;
|
|
457
510
|
}
|
|
458
511
|
const opts = {
|
|
459
512
|
url: `file:${config.dbPath}`
|
|
@@ -462,17 +515,24 @@ async function initDatabase(config) {
|
|
|
462
515
|
opts.encryptionKey = config.encryptionKey;
|
|
463
516
|
}
|
|
464
517
|
_client = createClient(opts);
|
|
518
|
+
_resilientClient = wrapWithRetry(_client);
|
|
465
519
|
}
|
|
466
520
|
function getClient() {
|
|
521
|
+
if (!_resilientClient) {
|
|
522
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
523
|
+
}
|
|
524
|
+
return _resilientClient;
|
|
525
|
+
}
|
|
526
|
+
function getRawClient() {
|
|
467
527
|
if (!_client) {
|
|
468
528
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
469
529
|
}
|
|
470
530
|
return _client;
|
|
471
531
|
}
|
|
472
532
|
async function ensureSchema() {
|
|
473
|
-
const client =
|
|
533
|
+
const client = getRawClient();
|
|
474
534
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
475
|
-
await client.execute("PRAGMA busy_timeout =
|
|
535
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
476
536
|
try {
|
|
477
537
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
478
538
|
} catch {
|
|
@@ -1266,6 +1326,7 @@ async function disposeDatabase() {
|
|
|
1266
1326
|
if (_client) {
|
|
1267
1327
|
_client.close();
|
|
1268
1328
|
_client = null;
|
|
1329
|
+
_resilientClient = null;
|
|
1269
1330
|
}
|
|
1270
1331
|
}
|
|
1271
1332
|
|
package/dist/bin/graph-export.js
CHANGED
|
@@ -262,7 +262,7 @@ function listShards() {
|
|
|
262
262
|
}
|
|
263
263
|
async function ensureShardSchema(client) {
|
|
264
264
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
265
|
-
await client.execute("PRAGMA busy_timeout =
|
|
265
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
266
266
|
try {
|
|
267
267
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
268
268
|
} catch {
|
|
@@ -452,12 +452,65 @@ import path4 from "path";
|
|
|
452
452
|
|
|
453
453
|
// src/lib/database.ts
|
|
454
454
|
import { createClient } from "@libsql/client";
|
|
455
|
+
|
|
456
|
+
// src/lib/db-retry.ts
|
|
457
|
+
var MAX_RETRIES = 3;
|
|
458
|
+
var BASE_DELAY_MS = 200;
|
|
459
|
+
var MAX_JITTER_MS = 300;
|
|
460
|
+
function isBusyError(err) {
|
|
461
|
+
if (err instanceof Error) {
|
|
462
|
+
const msg = err.message.toLowerCase();
|
|
463
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
464
|
+
}
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
function delay(ms) {
|
|
468
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
469
|
+
}
|
|
470
|
+
async function retryOnBusy(fn, label) {
|
|
471
|
+
let lastError;
|
|
472
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
473
|
+
try {
|
|
474
|
+
return await fn();
|
|
475
|
+
} catch (err) {
|
|
476
|
+
lastError = err;
|
|
477
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
478
|
+
throw err;
|
|
479
|
+
}
|
|
480
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
481
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
482
|
+
process.stderr.write(
|
|
483
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
484
|
+
`
|
|
485
|
+
);
|
|
486
|
+
await delay(backoff + jitter);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
throw lastError;
|
|
490
|
+
}
|
|
491
|
+
function wrapWithRetry(client) {
|
|
492
|
+
return new Proxy(client, {
|
|
493
|
+
get(target, prop, receiver) {
|
|
494
|
+
if (prop === "execute") {
|
|
495
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
496
|
+
}
|
|
497
|
+
if (prop === "batch") {
|
|
498
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
499
|
+
}
|
|
500
|
+
return Reflect.get(target, prop, receiver);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// src/lib/database.ts
|
|
455
506
|
var _client = null;
|
|
507
|
+
var _resilientClient = null;
|
|
456
508
|
var initTurso = initDatabase;
|
|
457
509
|
async function initDatabase(config) {
|
|
458
510
|
if (_client) {
|
|
459
511
|
_client.close();
|
|
460
512
|
_client = null;
|
|
513
|
+
_resilientClient = null;
|
|
461
514
|
}
|
|
462
515
|
const opts = {
|
|
463
516
|
url: `file:${config.dbPath}`
|
|
@@ -466,17 +519,24 @@ async function initDatabase(config) {
|
|
|
466
519
|
opts.encryptionKey = config.encryptionKey;
|
|
467
520
|
}
|
|
468
521
|
_client = createClient(opts);
|
|
522
|
+
_resilientClient = wrapWithRetry(_client);
|
|
469
523
|
}
|
|
470
524
|
function getClient() {
|
|
525
|
+
if (!_resilientClient) {
|
|
526
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
527
|
+
}
|
|
528
|
+
return _resilientClient;
|
|
529
|
+
}
|
|
530
|
+
function getRawClient() {
|
|
471
531
|
if (!_client) {
|
|
472
532
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
473
533
|
}
|
|
474
534
|
return _client;
|
|
475
535
|
}
|
|
476
536
|
async function ensureSchema() {
|
|
477
|
-
const client =
|
|
537
|
+
const client = getRawClient();
|
|
478
538
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
479
|
-
await client.execute("PRAGMA busy_timeout =
|
|
539
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
480
540
|
try {
|
|
481
541
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
482
542
|
} catch {
|
|
@@ -1270,6 +1330,7 @@ async function disposeDatabase() {
|
|
|
1270
1330
|
if (_client) {
|
|
1271
1331
|
_client.close();
|
|
1272
1332
|
_client = null;
|
|
1333
|
+
_resilientClient = null;
|
|
1273
1334
|
}
|
|
1274
1335
|
}
|
|
1275
1336
|
|
package/dist/bin/install.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/adapters/claude/installer.ts
|
|
4
4
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
5
|
-
import { existsSync as existsSync4, readFileSync as
|
|
5
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
6
6
|
import path4 from "path";
|
|
7
7
|
import os3 from "os";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
|
|
21
21
|
// src/lib/employees.ts
|
|
22
22
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
23
|
-
import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
|
|
23
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
|
|
24
24
|
import { execSync } from "child_process";
|
|
25
25
|
import path2 from "path";
|
|
26
26
|
|
|
@@ -208,7 +208,7 @@ function resolvePackageRoot() {
|
|
|
208
208
|
const pkgPath = path4.join(dir, "package.json");
|
|
209
209
|
if (existsSync4(pkgPath)) {
|
|
210
210
|
try {
|
|
211
|
-
const pkg = JSON.parse(
|
|
211
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
212
212
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
213
213
|
} catch {
|
|
214
214
|
}
|
package/dist/bin/scan-tasks.js
CHANGED
|
@@ -15,6 +15,61 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/lib/db-retry.ts
|
|
19
|
+
function isBusyError(err) {
|
|
20
|
+
if (err instanceof Error) {
|
|
21
|
+
const msg = err.message.toLowerCase();
|
|
22
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function delay(ms) {
|
|
27
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
+
}
|
|
29
|
+
async function retryOnBusy(fn, label) {
|
|
30
|
+
let lastError;
|
|
31
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
32
|
+
try {
|
|
33
|
+
return await fn();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
lastError = err;
|
|
36
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
40
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
41
|
+
process.stderr.write(
|
|
42
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
43
|
+
`
|
|
44
|
+
);
|
|
45
|
+
await delay(backoff + jitter);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw lastError;
|
|
49
|
+
}
|
|
50
|
+
function wrapWithRetry(client) {
|
|
51
|
+
return new Proxy(client, {
|
|
52
|
+
get(target, prop, receiver) {
|
|
53
|
+
if (prop === "execute") {
|
|
54
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
55
|
+
}
|
|
56
|
+
if (prop === "batch") {
|
|
57
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
58
|
+
}
|
|
59
|
+
return Reflect.get(target, prop, receiver);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
64
|
+
var init_db_retry = __esm({
|
|
65
|
+
"src/lib/db-retry.ts"() {
|
|
66
|
+
"use strict";
|
|
67
|
+
MAX_RETRIES = 3;
|
|
68
|
+
BASE_DELAY_MS = 200;
|
|
69
|
+
MAX_JITTER_MS = 300;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
18
73
|
// src/lib/database.ts
|
|
19
74
|
var database_exports = {};
|
|
20
75
|
__export(database_exports, {
|
|
@@ -22,6 +77,7 @@ __export(database_exports, {
|
|
|
22
77
|
disposeTurso: () => disposeTurso,
|
|
23
78
|
ensureSchema: () => ensureSchema,
|
|
24
79
|
getClient: () => getClient,
|
|
80
|
+
getRawClient: () => getRawClient,
|
|
25
81
|
initDatabase: () => initDatabase,
|
|
26
82
|
initTurso: () => initTurso,
|
|
27
83
|
isInitialized: () => isInitialized
|
|
@@ -31,6 +87,7 @@ async function initDatabase(config) {
|
|
|
31
87
|
if (_client) {
|
|
32
88
|
_client.close();
|
|
33
89
|
_client = null;
|
|
90
|
+
_resilientClient = null;
|
|
34
91
|
}
|
|
35
92
|
const opts = {
|
|
36
93
|
url: `file:${config.dbPath}`
|
|
@@ -39,20 +96,27 @@ async function initDatabase(config) {
|
|
|
39
96
|
opts.encryptionKey = config.encryptionKey;
|
|
40
97
|
}
|
|
41
98
|
_client = createClient(opts);
|
|
99
|
+
_resilientClient = wrapWithRetry(_client);
|
|
42
100
|
}
|
|
43
101
|
function isInitialized() {
|
|
44
102
|
return _client !== null;
|
|
45
103
|
}
|
|
46
104
|
function getClient() {
|
|
105
|
+
if (!_resilientClient) {
|
|
106
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
107
|
+
}
|
|
108
|
+
return _resilientClient;
|
|
109
|
+
}
|
|
110
|
+
function getRawClient() {
|
|
47
111
|
if (!_client) {
|
|
48
112
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
49
113
|
}
|
|
50
114
|
return _client;
|
|
51
115
|
}
|
|
52
116
|
async function ensureSchema() {
|
|
53
|
-
const client =
|
|
117
|
+
const client = getRawClient();
|
|
54
118
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
55
|
-
await client.execute("PRAGMA busy_timeout =
|
|
119
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
56
120
|
try {
|
|
57
121
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
58
122
|
} catch {
|
|
@@ -845,13 +909,16 @@ async function disposeDatabase() {
|
|
|
845
909
|
if (_client) {
|
|
846
910
|
_client.close();
|
|
847
911
|
_client = null;
|
|
912
|
+
_resilientClient = null;
|
|
848
913
|
}
|
|
849
914
|
}
|
|
850
|
-
var _client, initTurso, disposeTurso;
|
|
915
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
851
916
|
var init_database = __esm({
|
|
852
917
|
"src/lib/database.ts"() {
|
|
853
918
|
"use strict";
|
|
919
|
+
init_db_retry();
|
|
854
920
|
_client = null;
|
|
921
|
+
_resilientClient = null;
|
|
855
922
|
initTurso = initDatabase;
|
|
856
923
|
disposeTurso = disposeDatabase;
|
|
857
924
|
}
|
|
@@ -1162,7 +1229,7 @@ function listShards() {
|
|
|
1162
1229
|
}
|
|
1163
1230
|
async function ensureShardSchema(client) {
|
|
1164
1231
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1165
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1232
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1166
1233
|
try {
|
|
1167
1234
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1168
1235
|
} catch {
|