fraim-hub 2.0.209 → 2.0.210
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/src/ai-hub/conversation-store-lock.js +87 -0
- package/dist/src/ai-hub/conversation-store.js +369 -168
- package/dist/src/ai-hub/server.js +27 -5
- package/dist/src/config/learning-domains.js +221 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +109 -45
- package/package.json +3 -2
- package/public/ai-hub/script.js +127 -14
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isLockStale = isLockStale;
|
|
7
|
+
exports.withBucketLock = withBucketLock;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
11
|
+
const DEFAULT_STALE_MS = 10000;
|
|
12
|
+
// Synchronous sleep without a busy spin (the store's write path is synchronous).
|
|
13
|
+
function sleepMs(ms) {
|
|
14
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
15
|
+
}
|
|
16
|
+
function pidAlive(pid) {
|
|
17
|
+
try {
|
|
18
|
+
process.kill(pid, 0);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// ESRCH: no such process. EPERM: exists but not ours (still alive).
|
|
23
|
+
return error.code === 'EPERM';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// A lock is stale (safe to steal) if its file is unreadable/absent, its timestamp is older than
|
|
27
|
+
// staleMs, or it names a dead pid on this same host.
|
|
28
|
+
function isLockStale(lockPath, staleMs = DEFAULT_STALE_MS) {
|
|
29
|
+
let info;
|
|
30
|
+
try {
|
|
31
|
+
info = JSON.parse(fs_1.default.readFileSync(lockPath, 'utf8'));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (typeof info.ts === 'number' && Date.now() - info.ts > staleMs)
|
|
37
|
+
return true;
|
|
38
|
+
if (typeof info.pid === 'number' && info.host === os_1.default.hostname() && !pidAlive(info.pid))
|
|
39
|
+
return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// Run fn while holding an exclusive lock on lockPath. Steals a stale lock; throws if a live lock
|
|
43
|
+
// cannot be acquired within timeoutMs. Always releases the lock, even if fn throws.
|
|
44
|
+
function withBucketLock(lockPath, fn, opts = {}) {
|
|
45
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
46
|
+
const staleMs = opts.staleMs ?? DEFAULT_STALE_MS;
|
|
47
|
+
const deadline = Date.now() + timeoutMs;
|
|
48
|
+
for (;;) {
|
|
49
|
+
let fd;
|
|
50
|
+
try {
|
|
51
|
+
fd = fs_1.default.openSync(lockPath, 'wx'); // O_CREAT | O_EXCL: fails if the lock already exists
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code !== 'EEXIST')
|
|
55
|
+
throw error;
|
|
56
|
+
if (isLockStale(lockPath, staleMs)) {
|
|
57
|
+
try {
|
|
58
|
+
fs_1.default.unlinkSync(lockPath);
|
|
59
|
+
}
|
|
60
|
+
catch { /* another waiter won the steal; retry */ }
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (Date.now() > deadline) {
|
|
64
|
+
throw new Error(`conversation-store: timed out acquiring lock ${lockPath} after ${timeoutMs}ms`);
|
|
65
|
+
}
|
|
66
|
+
sleepMs(15);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
fs_1.default.writeSync(fd, JSON.stringify({ pid: process.pid, host: os_1.default.hostname(), ts: Date.now() }));
|
|
71
|
+
}
|
|
72
|
+
catch { /* the lock file content is advisory only */ }
|
|
73
|
+
try {
|
|
74
|
+
return fn();
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
try {
|
|
78
|
+
fs_1.default.closeSync(fd);
|
|
79
|
+
}
|
|
80
|
+
catch { /* already closed */ }
|
|
81
|
+
try {
|
|
82
|
+
fs_1.default.unlinkSync(lockPath);
|
|
83
|
+
}
|
|
84
|
+
catch { /* already stolen/removed */ }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|