crbro-memory 2.4.0 → 2.5.0

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.
Files changed (53) hide show
  1. package/README.md +42 -3
  2. package/bin/crbro.mjs +234 -0
  3. package/dist/daemon/daemon.d.ts +25 -0
  4. package/dist/daemon/daemon.d.ts.map +1 -0
  5. package/dist/daemon/daemon.js +387 -0
  6. package/dist/daemon/daemon.js.map +1 -0
  7. package/dist/daemon/endpoint.d.ts +65 -0
  8. package/dist/daemon/endpoint.d.ts.map +1 -0
  9. package/dist/daemon/endpoint.js +308 -0
  10. package/dist/daemon/endpoint.js.map +1 -0
  11. package/dist/daemon/lines.d.ts +55 -0
  12. package/dist/daemon/lines.d.ts.map +1 -0
  13. package/dist/daemon/lines.js +142 -0
  14. package/dist/daemon/lines.js.map +1 -0
  15. package/dist/daemon/proxy.d.ts +55 -0
  16. package/dist/daemon/proxy.d.ts.map +1 -0
  17. package/dist/daemon/proxy.js +412 -0
  18. package/dist/daemon/proxy.js.map +1 -0
  19. package/dist/engine/backup.d.ts +68 -0
  20. package/dist/engine/backup.d.ts.map +1 -0
  21. package/dist/engine/backup.js +224 -0
  22. package/dist/engine/backup.js.map +1 -0
  23. package/dist/engine/brain.d.ts.map +1 -1
  24. package/dist/engine/brain.js +6 -2
  25. package/dist/engine/brain.js.map +1 -1
  26. package/dist/engine/cortex.d.ts +76 -2
  27. package/dist/engine/cortex.d.ts.map +1 -1
  28. package/dist/engine/cortex.js +169 -9
  29. package/dist/engine/cortex.js.map +1 -1
  30. package/dist/engine/dates.d.ts +28 -0
  31. package/dist/engine/dates.d.ts.map +1 -0
  32. package/dist/engine/dates.js +121 -0
  33. package/dist/engine/dates.js.map +1 -0
  34. package/dist/engine/maintenance.d.ts +85 -0
  35. package/dist/engine/maintenance.d.ts.map +1 -1
  36. package/dist/engine/maintenance.js +340 -0
  37. package/dist/engine/maintenance.js.map +1 -1
  38. package/dist/engine/triggers.d.ts +46 -0
  39. package/dist/engine/triggers.d.ts.map +1 -0
  40. package/dist/engine/triggers.js +198 -0
  41. package/dist/engine/triggers.js.map +1 -0
  42. package/dist/index.js +33 -5
  43. package/dist/index.js.map +1 -1
  44. package/dist/search/index.d.ts +56 -3
  45. package/dist/search/index.d.ts.map +1 -1
  46. package/dist/search/index.js +244 -12
  47. package/dist/search/index.js.map +1 -1
  48. package/dist/server.d.ts +32 -1
  49. package/dist/server.d.ts.map +1 -1
  50. package/dist/server.js +151 -19
  51. package/dist/server.js.map +1 -1
  52. package/hooks/crbro-guard.mjs +155 -0
  53. package/package.json +1 -1
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ // ─── Where the daemon lives, and how a client proves who it is ────
3
+ //
4
+ // One brain, one build, one endpoint. The name is derived, never configured:
5
+ // every client of the same brain running the same build computes the same
6
+ // pipe (Windows) or socket (elsewhere) and therefore finds the same daemon —
7
+ // and a client on ANOTHER build computes a different one and gets its own,
8
+ // so "the process keeps the version it started with" stays true and an
9
+ // upgrade never has to kill anything: the old daemon drains and exits idle.
10
+ //
11
+ // Trust is a shared secret on disk. The daemon writes a random token to
12
+ // <brain>/.daemon/<id>.json, readable by the owner only; a client reads it
13
+ // and both sides prove knowledge of it with an HMAC over a fresh nonce.
14
+ // That covers the two ways a local socket goes wrong: another user of the
15
+ // machine connecting to it, and another process squatting the name first
16
+ // and collecting what clients send.
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.DAEMON_PROTOCOL = void 0;
22
+ exports.packageVersion = packageVersion;
23
+ exports.buildId = buildId;
24
+ exports.daemonDir = daemonDir;
25
+ exports.endpointFor = endpointFor;
26
+ exports.privateTmpDir = privateTmpDir;
27
+ exports.secureSocketDir = secureSocketDir;
28
+ exports.stateFile = stateFile;
29
+ exports.configFingerprint = configFingerprint;
30
+ exports.blockedFile = blockedFile;
31
+ exports.markBlocked = markBlocked;
32
+ exports.blockedSince = blockedSince;
33
+ exports.clearBlocked = clearBlocked;
34
+ exports.acquireStartLock = acquireStartLock;
35
+ exports.newToken = newToken;
36
+ exports.writeState = writeState;
37
+ exports.readState = readState;
38
+ exports.removeState = removeState;
39
+ exports.listStates = listStates;
40
+ exports.proof = proof;
41
+ exports.sameProof = sameProof;
42
+ exports.newNonce = newNonce;
43
+ exports.daemonEnabled = daemonEnabled;
44
+ exports.setDaemonEnabled = setDaemonEnabled;
45
+ const node_crypto_1 = require("node:crypto");
46
+ const node_fs_1 = require("node:fs");
47
+ const node_os_1 = __importDefault(require("node:os"));
48
+ const node_path_1 = __importDefault(require("node:path"));
49
+ exports.DAEMON_PROTOCOL = 1;
50
+ const DAEMON_DIR = '.daemon';
51
+ const ENABLED_FLAG = 'enabled';
52
+ function packageVersion() {
53
+ try {
54
+ const here = __dirname; // CommonJS output: dist/daemon, or src/daemon under the test runner
55
+ for (const up of ['..', node_path_1.default.join('..', '..')]) {
56
+ try {
57
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(here, up, 'package.json'), 'utf8'));
58
+ if (pkg?.name === 'crbro-memory' && pkg.version)
59
+ return String(pkg.version);
60
+ }
61
+ catch { /* try the next level */ }
62
+ }
63
+ }
64
+ catch { /* fall through */ }
65
+ return '0.0.0';
66
+ }
67
+ /**
68
+ * Version plus the build's own timestamp. The version alone would let a
69
+ * developer's half-built dist serve the clients that launched the published
70
+ * package; the timestamp alone would not survive a reinstall of the same
71
+ * release on another path. Together: you are served by the code you launched.
72
+ */
73
+ function buildId() {
74
+ let stamp = '0';
75
+ try {
76
+ const here = __dirname; // CommonJS output: dist/daemon, or src/daemon under the test runner
77
+ for (const f of ['../server.js', '../server.ts']) {
78
+ try {
79
+ stamp = Math.floor((0, node_fs_1.statSync)(node_path_1.default.join(here, f)).mtimeMs).toString(36);
80
+ break;
81
+ }
82
+ catch { /* next */ }
83
+ }
84
+ }
85
+ catch { /* keep 0 */ }
86
+ return `${packageVersion()}-${stamp}`;
87
+ }
88
+ function daemonDir(brainRoot) {
89
+ return node_path_1.default.join(brainRoot, DAEMON_DIR);
90
+ }
91
+ function endpointKey(brainRoot, build) {
92
+ const root = node_path_1.default.resolve(brainRoot);
93
+ const canon = process.platform === 'win32' ? root.toLowerCase() : root;
94
+ return (0, node_crypto_1.createHash)('sha256').update(`${canon}|${build}`).digest('hex').slice(0, 16);
95
+ }
96
+ /** The pipe or socket path for this brain and build. */
97
+ function endpointFor(brainRoot, build = buildId()) {
98
+ const key = endpointKey(brainRoot, build);
99
+ if (process.platform === 'win32')
100
+ return `\\\\.\\pipe\\crbro-${key}`;
101
+ const inBrain = node_path_1.default.join(daemonDir(brainRoot), `${key}.sock`);
102
+ // sun_path is ~104 bytes on macOS and 108 on Linux: a deep brain path falls back to tmp —
103
+ // into a folder of this user's own, because tmp itself is everybody's.
104
+ return Buffer.byteLength(inBrain) < 100 ? inBrain : node_path_1.default.join(privateTmpDir(), `${key}.sock`);
105
+ }
106
+ /** <tmp>/crbro-<uid>: where the socket goes when the brain path is too long for one. */
107
+ function privateTmpDir() {
108
+ const uid = typeof process.getuid === 'function' ? process.getuid() : 'u';
109
+ return node_path_1.default.join(node_os_1.default.tmpdir(), `crbro-${uid}`);
110
+ }
111
+ /**
112
+ * Make the socket's folder and make sure it is ours alone. In the brain that
113
+ * is a given; in tmp somebody else may have made `crbro-<our uid>` first, as a
114
+ * folder they own or as a link to one — and a socket placed there is theirs.
115
+ */
116
+ async function secureSocketDir(endpoint) {
117
+ if (process.platform === 'win32')
118
+ return;
119
+ const dir = node_path_1.default.dirname(endpoint);
120
+ await node_fs_1.promises.mkdir(dir, { recursive: true, mode: 0o700 });
121
+ const st = await node_fs_1.promises.lstat(dir);
122
+ const mine = typeof process.getuid !== 'function' || st.uid === process.getuid();
123
+ if (!st.isDirectory() || st.isSymbolicLink() || !mine || (st.mode & 0o077) !== 0) {
124
+ if (mine && st.isDirectory() && !st.isSymbolicLink()) {
125
+ await node_fs_1.promises.chmod(dir, 0o700);
126
+ return;
127
+ }
128
+ throw new Error(`DAEMON_ENDPOINT_BLOCKED: ${dir} is not a private folder of this user`);
129
+ }
130
+ }
131
+ function stateFile(brainRoot, build = buildId()) {
132
+ return node_path_1.default.join(daemonDir(brainRoot), `${endpointKey(brainRoot, build)}.json`);
133
+ }
134
+ /**
135
+ * What a daemon would do differently from the client asking for it. One
136
+ * process serves every client, but each client was configured on its own —
137
+ * CRBRO_BACKUP_DIR pointing at a synced folder in one, CRBRO_SEMANTIC=0 in
138
+ * another — and whose settings win must not depend on who happened to start
139
+ * first. A client whose fingerprint differs from the daemon's serves itself,
140
+ * exactly as it did before daemons: its settings keep meaning what they said.
141
+ */
142
+ function configFingerprint(env = process.env) {
143
+ const v = (k) => (env[k] || '').trim();
144
+ const semantic = ['0', 'off', 'false', 'no'].includes(v('CRBRO_SEMANTIC').toLowerCase()) ? 'off'
145
+ : ['1', 'on', 'true', 'yes', 'force'].includes(v('CRBRO_SEMANTIC').toLowerCase()) ? 'on' : 'auto';
146
+ const parts = [
147
+ semantic, v('CRBRO_SEMANTIC_MODEL'), v('CRBRO_SEMANTIC_DTYPE'), v('CRBRO_SEMANTIC_HOME'), v('CRBRO_SEMANTIC_FLOOR'),
148
+ v('CRBRO_RECENCY'), v('CRBRO_SYNONYMS'), v('CRBRO_AUTOBACKUP') === '0' ? 'nobackup' : '', v('CRBRO_BACKUP_DIR'),
149
+ ];
150
+ return (0, node_crypto_1.createHash)('sha256').update(parts.join('\u0000')).digest('hex').slice(0, 12);
151
+ }
152
+ // ─── Two small files that keep start-up honest ───────────────────
153
+ /** Written by a daemon that found its endpoint held by something that is not a CRBRO daemon of this brain. */
154
+ function blockedFile(brainRoot, build = buildId()) {
155
+ return node_path_1.default.join(daemonDir(brainRoot), `${endpointKey(brainRoot, build)}.blocked`);
156
+ }
157
+ async function markBlocked(brainRoot, build, reason) {
158
+ try {
159
+ await node_fs_1.promises.mkdir(daemonDir(brainRoot), { recursive: true, mode: 0o700 });
160
+ await node_fs_1.promises.writeFile(blockedFile(brainRoot, build), JSON.stringify({ pid: process.pid, at: Date.now(), reason }));
161
+ }
162
+ catch { /* the marker is a courtesy to the proxy's patience */ }
163
+ }
164
+ /** A marker newer than `since` (ms): the daemon we just asked for could not be had, and waiting will not change it. */
165
+ async function blockedSince(brainRoot, build, since) {
166
+ try {
167
+ const m = JSON.parse(await node_fs_1.promises.readFile(blockedFile(brainRoot, build), 'utf8'));
168
+ return typeof m?.at === 'number' && m.at >= since ? String(m.reason || 'blocked') : null;
169
+ }
170
+ catch {
171
+ return null;
172
+ }
173
+ }
174
+ async function clearBlocked(brainRoot, build) {
175
+ await node_fs_1.promises.rm(blockedFile(brainRoot, build), { force: true }).catch(() => undefined);
176
+ }
177
+ const START_LOCK_STALE_MS = 10_000;
178
+ /**
179
+ * One daemon at a time through the dangerous part of starting: finding the
180
+ * endpoint busy, deciding the holder is dead, unlinking its socket, listening.
181
+ * Two starters doing that interleaved can each delete the other's live socket.
182
+ * The lock names its holder, so a starter that was killed does not hold it forever.
183
+ */
184
+ async function acquireStartLock(brainRoot, build) {
185
+ const file = node_path_1.default.join(daemonDir(brainRoot), `${endpointKey(brainRoot, build)}.lock`);
186
+ await node_fs_1.promises.mkdir(daemonDir(brainRoot), { recursive: true, mode: 0o700 });
187
+ for (let attempt = 0; attempt < 2; attempt++) {
188
+ try {
189
+ const h = await node_fs_1.promises.open(file, 'wx', 0o600);
190
+ await h.writeFile(String(process.pid));
191
+ await h.close();
192
+ return async () => { await node_fs_1.promises.rm(file, { force: true }).catch(() => undefined); };
193
+ }
194
+ catch (err) {
195
+ if (err.code !== 'EEXIST')
196
+ throw err;
197
+ let stale = false;
198
+ try {
199
+ const st = await node_fs_1.promises.stat(file);
200
+ const pid = Number((await node_fs_1.promises.readFile(file, 'utf8')).trim());
201
+ let alive = true;
202
+ try {
203
+ if (pid > 0)
204
+ process.kill(pid, 0);
205
+ else
206
+ alive = false;
207
+ }
208
+ catch (e) {
209
+ alive = e.code === 'EPERM';
210
+ }
211
+ stale = !alive || Date.now() - st.mtimeMs > START_LOCK_STALE_MS;
212
+ }
213
+ catch {
214
+ stale = true;
215
+ }
216
+ if (!stale)
217
+ return null;
218
+ await node_fs_1.promises.rm(file, { force: true }).catch(() => undefined);
219
+ }
220
+ }
221
+ return null;
222
+ }
223
+ function newToken() {
224
+ return (0, node_crypto_1.randomBytes)(32).toString('hex');
225
+ }
226
+ async function writeState(brainRoot, state) {
227
+ const dir = daemonDir(brainRoot);
228
+ await node_fs_1.promises.mkdir(dir, { recursive: true, mode: 0o700 });
229
+ const file = stateFile(brainRoot, state.build);
230
+ const tmp = `${file}.${process.pid}.tmp`;
231
+ await node_fs_1.promises.writeFile(tmp, JSON.stringify(state), { mode: 0o600 });
232
+ await node_fs_1.promises.rename(tmp, file);
233
+ }
234
+ async function readState(brainRoot, build = buildId()) {
235
+ try {
236
+ const s = JSON.parse(await node_fs_1.promises.readFile(stateFile(brainRoot, build), 'utf8'));
237
+ return s && s.protocol === exports.DAEMON_PROTOCOL && typeof s.token === 'string' && s.token.length >= 32 ? s : null;
238
+ }
239
+ catch {
240
+ return null;
241
+ }
242
+ }
243
+ async function removeState(brainRoot, build, pid) {
244
+ // Only our own: a newer daemon of the same build may have replaced the file already.
245
+ const s = await readState(brainRoot, build);
246
+ if (s && s.pid !== pid)
247
+ return;
248
+ await node_fs_1.promises.rm(stateFile(brainRoot, build), { force: true }).catch(() => undefined);
249
+ }
250
+ /** Every daemon that left a state file for this brain, whatever its build. */
251
+ async function listStates(brainRoot) {
252
+ const out = [];
253
+ try {
254
+ for (const f of await node_fs_1.promises.readdir(daemonDir(brainRoot))) {
255
+ if (!f.endsWith('.json'))
256
+ continue;
257
+ try {
258
+ const s = JSON.parse(await node_fs_1.promises.readFile(node_path_1.default.join(daemonDir(brainRoot), f), 'utf8'));
259
+ if (s?.protocol === exports.DAEMON_PROTOCOL && s.endpoint)
260
+ out.push(s);
261
+ }
262
+ catch { /* a half-written file is not a daemon */ }
263
+ }
264
+ }
265
+ catch { /* no folder, no daemons */ }
266
+ return out;
267
+ }
268
+ function proof(token, role, nonce) {
269
+ return (0, node_crypto_1.createHmac)('sha256', token).update(`${role}:${nonce}`).digest('hex');
270
+ }
271
+ function sameProof(a, b) {
272
+ const x = Buffer.from(String(a), 'utf8'), y = Buffer.from(String(b), 'utf8');
273
+ return x.length === y.length && (0, node_crypto_1.timingSafeEqual)(x, y);
274
+ }
275
+ function newNonce() {
276
+ return (0, node_crypto_1.randomBytes)(16).toString('hex');
277
+ }
278
+ // ─── On or off ───────────────────────────────────────────────────
279
+ //
280
+ // A flag in the brain, not an environment variable: the environment would
281
+ // have to be set in every client's config, and the point is that every client
282
+ // of this brain switches together. CRBRO_DAEMON=1 / =0 still wins for one
283
+ // process — for a test, or to keep one client out.
284
+ function daemonEnabled(brainRoot, env = process.env) {
285
+ const v = (env['CRBRO_DAEMON'] || '').trim();
286
+ if (v === '1')
287
+ return true;
288
+ if (v === '0')
289
+ return false;
290
+ try {
291
+ (0, node_fs_1.statSync)(node_path_1.default.join(daemonDir(brainRoot), ENABLED_FLAG));
292
+ return true;
293
+ }
294
+ catch {
295
+ return false;
296
+ }
297
+ }
298
+ async function setDaemonEnabled(brainRoot, on) {
299
+ const flag = node_path_1.default.join(daemonDir(brainRoot), ENABLED_FLAG);
300
+ if (on) {
301
+ await node_fs_1.promises.mkdir(daemonDir(brainRoot), { recursive: true, mode: 0o700 });
302
+ await node_fs_1.promises.writeFile(flag, `${new Date().toISOString()}\n`);
303
+ }
304
+ else {
305
+ await node_fs_1.promises.rm(flag, { force: true });
306
+ }
307
+ }
308
+ //# sourceMappingURL=endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../../src/daemon/endpoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { Socket } from 'node:net';
2
+ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
4
+ /** A line bigger than this is not a message, it is a mistake or an attack. */
5
+ export declare const MAX_LINE_BYTES: number;
6
+ /** Splits a byte stream into lines. Tolerates \r\n and lines split across chunks. */
7
+ export declare class LineReader {
8
+ private onLine;
9
+ private onOverflow;
10
+ private buf;
11
+ private size;
12
+ constructor(onLine: (line: string) => void, onOverflow: () => void);
13
+ push(chunk: Buffer): void;
14
+ }
15
+ /**
16
+ * The daemon's side of one client: an MCP Transport over a socket that has
17
+ * already passed the handshake. `scope` wraps every incoming message, which
18
+ * is how per-connection state (the session tally) follows the request through
19
+ * shared engines without any of them knowing about connections.
20
+ */
21
+ export declare class SocketServerTransport implements Transport {
22
+ private socket;
23
+ private scope;
24
+ onmessage?: (message: JSONRPCMessage) => void;
25
+ onclose?: () => void;
26
+ onerror?: (error: Error) => void;
27
+ private closed;
28
+ /** Lines that arrived before the server finished connecting, in order. */
29
+ private pending;
30
+ constructor(socket: Socket, scope: <T>(fn: () => T) => T);
31
+ /** The server is connected: deliver what waited, in the order it came, and stop queueing. */
32
+ open(): void;
33
+ start(): Promise<void>;
34
+ /** Fed by the daemon, which owns the reader (the handshake used it first). */
35
+ receive(line: string): void;
36
+ send(message: JSONRPCMessage): Promise<void>;
37
+ close(): Promise<void>;
38
+ private finish;
39
+ }
40
+ /**
41
+ * An in-process backend for the proxy's fallback: the same line interface as
42
+ * a socket, connected to a server living in this process.
43
+ */
44
+ export declare class LoopbackTransport implements Transport {
45
+ private deliver;
46
+ onmessage?: (message: JSONRPCMessage) => void;
47
+ onclose?: () => void;
48
+ onerror?: (error: Error) => void;
49
+ constructor(deliver: (line: string) => void);
50
+ start(): Promise<void>;
51
+ receive(line: string): void;
52
+ send(message: JSONRPCMessage): Promise<void>;
53
+ close(): Promise<void>;
54
+ }
55
+ //# sourceMappingURL=lines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../src/daemon/lines.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,8EAA8E;AAC9E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C,qFAAqF;AACrF,qBAAa,UAAU;IAIT,OAAO,CAAC,MAAM;IAA0B,OAAO,CAAC,UAAU;IAHtE,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,IAAI,CAAK;gBAEG,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAU,UAAU,EAAE,MAAM,IAAI;IAElF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAmB1B;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,SAAS;IAQzC,OAAO,CAAC,MAAM;IAAU,OAAO,CAAC,KAAK;IAPjD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,0EAA0E;IAC1E,OAAO,CAAC,OAAO,CAAuB;gBAElB,MAAM,EAAE,MAAM,EAAU,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC;IAExE,6FAA6F;IAC7F,IAAI,IAAI,IAAI;IAMN,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,8EAA8E;IAC9E,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAYrB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,OAAO,CAAC,MAAM;CAKf;AAED;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IAKrC,OAAO,CAAC,OAAO;IAJ3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;gBAEb,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;IAE7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAE5B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ // ─── Newline-delimited JSON over a stream ─────────────────────────
3
+ //
4
+ // The same framing MCP uses on stdio, so a proxy can hand a client's bytes to
5
+ // the daemon without understanding them. One JSON value per line, UTF-8.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.LoopbackTransport = exports.SocketServerTransport = exports.LineReader = exports.MAX_LINE_BYTES = void 0;
8
+ /** A line bigger than this is not a message, it is a mistake or an attack. */
9
+ exports.MAX_LINE_BYTES = 32 * 1024 * 1024;
10
+ /** Splits a byte stream into lines. Tolerates \r\n and lines split across chunks. */
11
+ class LineReader {
12
+ onLine;
13
+ onOverflow;
14
+ buf = [];
15
+ size = 0;
16
+ constructor(onLine, onOverflow) {
17
+ this.onLine = onLine;
18
+ this.onOverflow = onOverflow;
19
+ }
20
+ push(chunk) {
21
+ let start = 0;
22
+ for (let i = 0; i < chunk.length; i++) {
23
+ if (chunk[i] !== 0x0a)
24
+ continue;
25
+ const piece = chunk.subarray(start, i);
26
+ const whole = this.buf.length ? Buffer.concat([...this.buf, piece]) : piece;
27
+ this.buf = [];
28
+ this.size = 0;
29
+ start = i + 1;
30
+ const line = whole.toString('utf8').replace(/\r$/, '');
31
+ if (line)
32
+ this.onLine(line);
33
+ }
34
+ if (start < chunk.length) {
35
+ const rest = chunk.subarray(start);
36
+ this.buf.push(rest);
37
+ this.size += rest.length;
38
+ if (this.size > exports.MAX_LINE_BYTES) {
39
+ this.buf = [];
40
+ this.size = 0;
41
+ this.onOverflow();
42
+ }
43
+ }
44
+ }
45
+ }
46
+ exports.LineReader = LineReader;
47
+ /**
48
+ * The daemon's side of one client: an MCP Transport over a socket that has
49
+ * already passed the handshake. `scope` wraps every incoming message, which
50
+ * is how per-connection state (the session tally) follows the request through
51
+ * shared engines without any of them knowing about connections.
52
+ */
53
+ class SocketServerTransport {
54
+ socket;
55
+ scope;
56
+ onmessage;
57
+ onclose;
58
+ onerror;
59
+ closed = false;
60
+ /** Lines that arrived before the server finished connecting, in order. */
61
+ pending = [];
62
+ constructor(socket, scope) {
63
+ this.socket = socket;
64
+ this.scope = scope;
65
+ }
66
+ /** The server is connected: deliver what waited, in the order it came, and stop queueing. */
67
+ open() {
68
+ const waiting = this.pending || [];
69
+ this.pending = null;
70
+ for (const line of waiting)
71
+ this.receive(line);
72
+ }
73
+ async start() {
74
+ this.socket.on('close', () => this.finish());
75
+ this.socket.on('error', err => { this.onerror?.(err); this.finish(); });
76
+ }
77
+ /** Fed by the daemon, which owns the reader (the handshake used it first). */
78
+ receive(line) {
79
+ if (this.pending) {
80
+ this.pending.push(line);
81
+ return;
82
+ }
83
+ let message;
84
+ try {
85
+ message = JSON.parse(line);
86
+ }
87
+ catch (err) {
88
+ this.onerror?.(err);
89
+ return;
90
+ }
91
+ this.scope(() => this.onmessage?.(message));
92
+ }
93
+ async send(message) {
94
+ if (this.closed || this.socket.destroyed)
95
+ return;
96
+ await new Promise(resolve => {
97
+ this.socket.write(`${JSON.stringify(message)}\n`, () => resolve());
98
+ });
99
+ }
100
+ async close() {
101
+ if (!this.socket.destroyed)
102
+ this.socket.end();
103
+ this.finish();
104
+ }
105
+ finish() {
106
+ if (this.closed)
107
+ return;
108
+ this.closed = true;
109
+ this.onclose?.();
110
+ }
111
+ }
112
+ exports.SocketServerTransport = SocketServerTransport;
113
+ /**
114
+ * An in-process backend for the proxy's fallback: the same line interface as
115
+ * a socket, connected to a server living in this process.
116
+ */
117
+ class LoopbackTransport {
118
+ deliver;
119
+ onmessage;
120
+ onclose;
121
+ onerror;
122
+ constructor(deliver) {
123
+ this.deliver = deliver;
124
+ }
125
+ async start() { }
126
+ receive(line) {
127
+ try {
128
+ this.onmessage?.(JSON.parse(line));
129
+ }
130
+ catch (err) {
131
+ this.onerror?.(err);
132
+ }
133
+ }
134
+ async send(message) {
135
+ this.deliver(JSON.stringify(message));
136
+ }
137
+ async close() {
138
+ this.onclose?.();
139
+ }
140
+ }
141
+ exports.LoopbackTransport = LoopbackTransport;
142
+ //# sourceMappingURL=lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/daemon/lines.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import net from 'node:net';
2
+ import type { Readable, Writable } from 'node:stream';
3
+ import { type DaemonState } from './endpoint.js';
4
+ export interface Backend {
5
+ kind: 'daemon' | 'local';
6
+ send(line: string): void;
7
+ close(): Promise<void>;
8
+ }
9
+ type BackendEvents = {
10
+ onLine: (line: string) => void;
11
+ onClose: () => void;
12
+ };
13
+ export interface ProxyOptions {
14
+ input: Readable;
15
+ output: Writable;
16
+ brainRoot: string;
17
+ build?: string;
18
+ /** Start a daemon. Production spawns a detached process; tests start one in-process, or refuse. */
19
+ spawnDaemon?: (() => void | Promise<void>) | null;
20
+ spawnWaitMs?: number;
21
+ /** Is daemon mode still on? Asked again at every (re)connection, so `crbro daemon off` sticks. Default: yes. */
22
+ isEnabled?: () => boolean;
23
+ /** The in-process server. Tests replace it to see what happens when even that cannot start. */
24
+ local?: (ev: BackendEvents, resumed: boolean) => Promise<Backend>;
25
+ drainTimeoutMs?: number;
26
+ log?: (line: string) => void;
27
+ }
28
+ export interface ProxyHandle {
29
+ /** Resolves when the client closed its end and everything it had asked for was answered; rejects if no backend could be had at all. */
30
+ closed: Promise<void>;
31
+ backendKind(): 'daemon' | 'local' | 'none';
32
+ reconnects(): number;
33
+ }
34
+ export interface DaemonLink {
35
+ socket: net.Socket;
36
+ reader: (on: (line: string) => void) => void;
37
+ state: DaemonState;
38
+ hello: any;
39
+ }
40
+ /**
41
+ * Connect to the daemon of this brain and build, and get through the
42
+ * handshake. Null on any failure; 'config-mismatch' when the daemon is
43
+ * genuine but was started with settings that are not this client's.
44
+ */
45
+ export declare function connectDaemon(brainRoot: string, build?: string, options?: {
46
+ checkConfig?: boolean;
47
+ resumed?: boolean;
48
+ }): Promise<DaemonLink | 'config-mismatch' | null>;
49
+ /** One control exchange: `status` or `stop`. Null when there is no daemon to talk to. */
50
+ export declare function controlDaemon(brainRoot: string, op: 'status' | 'stop', build?: string): Promise<any | null>;
51
+ /** The production way to start a daemon: detached, silent, surviving this process. */
52
+ export declare function spawnDetachedDaemon(brainRoot: string): void;
53
+ export declare function runProxy(o: ProxyOptions): ProxyHandle;
54
+ export {};
55
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/daemon/proxy.ts"],"names":[],"mappings":"AAkBA,OAAO,GAAG,MAAM,UAAU,CAAC;AAI3B,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAC6F,KAAK,WAAW,EACnH,MAAM,eAAe,CAAC;AAYvB,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AACD,KAAK,aAAa,GAAG;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,WAAW,CAAC,EAAE,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,uIAAuI;IACvI,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,WAAW,IAAI,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAC3C,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC;IACnB,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAkB,EACzB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,OAAO,CAAC,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAAC,CA2ChD;AAED,yFAAyF;AACzF,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAQjH;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAY3D;AA+DD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,YAAY,GAAG,WAAW,CAiKrD"}