crbro-memory 1.7.0 → 1.8.1
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/README.md +260 -224
- package/bin/crbro.mjs +17 -3
- package/dist/engine/brain.d.ts.map +1 -1
- package/dist/engine/brain.js +4 -0
- package/dist/engine/brain.js.map +1 -1
- package/dist/engine/keychain.d.ts +32 -0
- package/dist/engine/keychain.d.ts.map +1 -0
- package/dist/engine/keychain.js +329 -0
- package/dist/engine/keychain.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +111 -7
- package/dist/server.js.map +1 -1
- package/package.json +53 -53
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── CRBRO Keychain Broker ───────────────────────────────────────
|
|
3
|
+
//
|
|
4
|
+
// The secret filter says no. It refuses to write a credential into the brain,
|
|
5
|
+
// which is right — but it leaves the user holding a password and nowhere to
|
|
6
|
+
// put it, so it goes back into a config file in plain text and nothing was
|
|
7
|
+
// gained. This module is the other half of that sentence: "no, not in memory —
|
|
8
|
+
// I put it in your keychain and remembered the name."
|
|
9
|
+
//
|
|
10
|
+
// CRBRO stores nothing itself and invents no crypto. Every platform already
|
|
11
|
+
// ships a credential store that is better reviewed than anything written here
|
|
12
|
+
// could be, so the job is to broker access to it and stay out of the way:
|
|
13
|
+
//
|
|
14
|
+
// macOS security(1), the login keychain
|
|
15
|
+
// Linux secret-tool(1), the Secret Service the desktop already runs
|
|
16
|
+
// Windows DPAPI through PowerShell, sealed to the user account
|
|
17
|
+
//
|
|
18
|
+
// Windows is the odd one. Its Credential Manager has no supported way to read
|
|
19
|
+
// a secret back from the command line — cmdkey writes but will not return the
|
|
20
|
+
// value, and reading needs P/Invoke into CredRead. So there the secret is
|
|
21
|
+
// sealed with DPAPI, which is the same crypto the Credential Manager uses,
|
|
22
|
+
// into a file only that Windows account can open. Copied to another machine,
|
|
23
|
+
// lifted from a backup or pushed to a repository, it is unreadable.
|
|
24
|
+
//
|
|
25
|
+
// The store lives OUTSIDE the brain on purpose. Nothing under ~/.crbro is ever
|
|
26
|
+
// consulted here, so no sync, no team space and no `crbro_share` can reach a
|
|
27
|
+
// secret even if some future bug tried to.
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.KeychainUnavailable = void 0;
|
|
30
|
+
exports.detectBackend = detectBackend;
|
|
31
|
+
exports.setSecret = setSecret;
|
|
32
|
+
exports.getSecret = getSecret;
|
|
33
|
+
exports.listSecrets = listSecrets;
|
|
34
|
+
exports.removeSecret = removeSecret;
|
|
35
|
+
exports._resetWindowsStoreForTests = _resetWindowsStoreForTests;
|
|
36
|
+
const node_child_process_1 = require("node:child_process");
|
|
37
|
+
const node_os_1 = require("node:os");
|
|
38
|
+
const node_path_1 = require("node:path");
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
/** Namespace under which every CRBRO secret is filed in the OS store. */
|
|
41
|
+
const SERVICE = 'crbro';
|
|
42
|
+
/**
|
|
43
|
+
* Windows only: the sealed file, deliberately a sibling of the brain, never
|
|
44
|
+
* inside it. CRBRO_KEYS_DIR redirects it, which is how the tests avoid writing
|
|
45
|
+
* into the real store — a test suite that can only run by touching the user's
|
|
46
|
+
* own credentials is a test suite nobody runs twice.
|
|
47
|
+
*/
|
|
48
|
+
function winPaths() {
|
|
49
|
+
const dir = process.env['CRBRO_KEYS_DIR'] || (0, node_path_1.join)((0, node_os_1.homedir)(), '.crbro-keys');
|
|
50
|
+
return { dir, file: (0, node_path_1.join)(dir, 'keys.dpapi') };
|
|
51
|
+
}
|
|
52
|
+
class KeychainUnavailable extends Error {
|
|
53
|
+
reason;
|
|
54
|
+
constructor(reason) {
|
|
55
|
+
super(reason);
|
|
56
|
+
this.reason = reason;
|
|
57
|
+
this.name = 'KeychainUnavailable';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.KeychainUnavailable = KeychainUnavailable;
|
|
61
|
+
/** Runs a command without a shell, so a value with spaces or quotes cannot be reinterpreted. */
|
|
62
|
+
function run(cmd, args, input) {
|
|
63
|
+
return (0, node_child_process_1.spawnSync)(cmd, args, {
|
|
64
|
+
encoding: 'utf8',
|
|
65
|
+
input,
|
|
66
|
+
timeout: 30_000,
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Which store this machine can actually use — not which one it should have.
|
|
72
|
+
* Returns null with a reason the caller can show the user, because "no
|
|
73
|
+
* keychain here" is a normal answer on a headless box, not a failure.
|
|
74
|
+
*/
|
|
75
|
+
function detectBackend() {
|
|
76
|
+
const os = (0, node_os_1.platform)();
|
|
77
|
+
if (os === 'darwin') {
|
|
78
|
+
const probe = run('security', ['-h']);
|
|
79
|
+
if (probe.error)
|
|
80
|
+
return { backend: null, reason: 'security(1) not found — unexpected on macOS.' };
|
|
81
|
+
return { backend: 'macos-keychain' };
|
|
82
|
+
}
|
|
83
|
+
if (os === 'win32') {
|
|
84
|
+
const probe = run('powershell.exe', ['-NoProfile', '-Command', '$PSVersionTable.PSVersion.Major']);
|
|
85
|
+
if (probe.error || probe.status !== 0) {
|
|
86
|
+
return { backend: null, reason: 'PowerShell not available, so DPAPI cannot be reached.' };
|
|
87
|
+
}
|
|
88
|
+
return { backend: 'windows-dpapi' };
|
|
89
|
+
}
|
|
90
|
+
// Linux and the BSDs. secret-tool needs a running Secret Service over D-Bus;
|
|
91
|
+
// over SSH with no desktop session there is usually none, and that is worth
|
|
92
|
+
// saying plainly rather than failing with a D-Bus error.
|
|
93
|
+
const probe = run('secret-tool', ['--help']);
|
|
94
|
+
if (probe.error) {
|
|
95
|
+
return { backend: null, reason: 'secret-tool not installed. On Debian/Ubuntu: apt install libsecret-tools.' };
|
|
96
|
+
}
|
|
97
|
+
if (!process.env['DBUS_SESSION_BUS_ADDRESS']) {
|
|
98
|
+
return {
|
|
99
|
+
backend: null,
|
|
100
|
+
reason: 'secret-tool is installed but no D-Bus session is running, which is normal over SSH. ' +
|
|
101
|
+
'Start a session bus or use the CRBRO_SECRET_* environment variables instead.',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { backend: 'linux-secret-service' };
|
|
105
|
+
}
|
|
106
|
+
function requireBackend() {
|
|
107
|
+
const { backend, reason } = detectBackend();
|
|
108
|
+
if (!backend)
|
|
109
|
+
throw new KeychainUnavailable(reason ?? 'No credential store available on this machine.');
|
|
110
|
+
return backend;
|
|
111
|
+
}
|
|
112
|
+
// Every DPAPI call costs a PowerShell start, about a second. A script that
|
|
113
|
+
// needs four credentials would pay four seconds for no reason, so a read is
|
|
114
|
+
// held briefly in memory.
|
|
115
|
+
//
|
|
116
|
+
// Briefly is the whole point. The MCP server is long-lived, and a cache with
|
|
117
|
+
// no expiry would keep every secret in the heap for as long as the editor is
|
|
118
|
+
// open. Thirty seconds covers the burst of reads at the start of a task and
|
|
119
|
+
// nothing beyond it.
|
|
120
|
+
const CACHE_TTL_MS = 30_000;
|
|
121
|
+
let cached = null;
|
|
122
|
+
function invalidateCache() {
|
|
123
|
+
cached = null;
|
|
124
|
+
}
|
|
125
|
+
function psDpapi(script, input) {
|
|
126
|
+
const r = run('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script], input);
|
|
127
|
+
if (r.error || r.status !== 0) {
|
|
128
|
+
throw new KeychainUnavailable(`DPAPI call failed: ${(r.stderr || '').trim() || 'unknown error'}`);
|
|
129
|
+
}
|
|
130
|
+
return r.stdout;
|
|
131
|
+
}
|
|
132
|
+
function winRead() {
|
|
133
|
+
const { file } = winPaths();
|
|
134
|
+
if (cached && cached.file === file && Date.now() - cached.at < CACHE_TTL_MS) {
|
|
135
|
+
return cached.store;
|
|
136
|
+
}
|
|
137
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
138
|
+
return {};
|
|
139
|
+
const sealed = (0, node_fs_1.readFileSync)(file, 'utf8').trim();
|
|
140
|
+
if (!sealed)
|
|
141
|
+
return {};
|
|
142
|
+
// ConvertTo-SecureString without -Key is DPAPI at CurrentUser scope: only
|
|
143
|
+
// this Windows account, on this machine, can turn it back into text.
|
|
144
|
+
const json = psDpapi('$e = [Console]::In.ReadToEnd().Trim();' +
|
|
145
|
+
'$s = ConvertTo-SecureString $e;' +
|
|
146
|
+
'$b = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($s);' +
|
|
147
|
+
'try { [Runtime.InteropServices.Marshal]::PtrToStringAuto($b) }' +
|
|
148
|
+
'finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($b) }', sealed);
|
|
149
|
+
try {
|
|
150
|
+
const store = JSON.parse(json);
|
|
151
|
+
cached = { store, at: Date.now(), file };
|
|
152
|
+
return store;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
throw new KeychainUnavailable('The key store exists but could not be read. It was sealed by a different Windows account.');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function winWrite(store) {
|
|
159
|
+
const { dir, file } = winPaths();
|
|
160
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
161
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
162
|
+
const sealed = psDpapi('$j = [Console]::In.ReadToEnd();' +
|
|
163
|
+
'ConvertTo-SecureString $j -AsPlainText -Force | ConvertFrom-SecureString', JSON.stringify(store)).trim();
|
|
164
|
+
// Atomic: a crash mid-write leaves the previous store intact rather than a
|
|
165
|
+
// truncated file that would take every secret with it.
|
|
166
|
+
const tmp = `${file}.tmp`;
|
|
167
|
+
(0, node_fs_1.writeFileSync)(tmp, sealed, { encoding: 'ascii', mode: 0o600 });
|
|
168
|
+
(0, node_fs_1.renameSync)(tmp, file);
|
|
169
|
+
cached = { store, at: Date.now(), file };
|
|
170
|
+
}
|
|
171
|
+
// ─── macOS: an index of its own ──────────────────────────────────
|
|
172
|
+
//
|
|
173
|
+
// There is no clean way to list the items of one service. dump-keychain asks
|
|
174
|
+
// for authorisation once per item, and find-generic-password -g returns a
|
|
175
|
+
// single match and prints the password to stderr behind a "password: " prefix
|
|
176
|
+
// — reading that back just to enumerate names would mean handling secrets
|
|
177
|
+
// for no reason. So the names live in an item of their own: one extra write
|
|
178
|
+
// per change, and a listing that tells the truth.
|
|
179
|
+
// security(1) prints the whole value as hex pairs if a single byte of it is
|
|
180
|
+
// not printable, and a UTF-8 accent is two bytes above 0x7F — so "contraseña"
|
|
181
|
+
// comes back as "636f6e747261736ec3b161". Worse, it is ambiguous: the literal
|
|
182
|
+
// secret "deadbeef" is returned exactly like the hex of the bytes DE AD BE EF,
|
|
183
|
+
// and nothing in the output says which one it was.
|
|
184
|
+
//
|
|
185
|
+
// Base64 is printable ASCII from end to end, so the hex path is never taken
|
|
186
|
+
// and any byte sequence survives the round trip unchanged.
|
|
187
|
+
function macEncode(value) {
|
|
188
|
+
return Buffer.from(value, 'utf8').toString('base64');
|
|
189
|
+
}
|
|
190
|
+
function macDecode(raw) {
|
|
191
|
+
// -w always ends with a newline of its own (putchar), and only that one is
|
|
192
|
+
// removed: .trim() would eat spaces that belong to the secret.
|
|
193
|
+
return Buffer.from(raw.replace(/\n$/, ''), 'base64').toString('utf8');
|
|
194
|
+
}
|
|
195
|
+
const MAC_INDEX = '__crbro_index__';
|
|
196
|
+
function macReadIndex() {
|
|
197
|
+
const r = run('security', ['find-generic-password', '-s', SERVICE, '-a', MAC_INDEX, '-w']);
|
|
198
|
+
if (r.status !== 0)
|
|
199
|
+
return {};
|
|
200
|
+
try {
|
|
201
|
+
return JSON.parse(macDecode(r.stdout));
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return {};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function macWriteIndex(index) {
|
|
208
|
+
run('security', ['add-generic-password', '-U', '-s', SERVICE, '-a', MAC_INDEX, '-w', macEncode(JSON.stringify(index)), '-T', '/usr/bin/security']);
|
|
209
|
+
}
|
|
210
|
+
// ─── Public surface ──────────────────────────────────────────────
|
|
211
|
+
function setSecret(name, value, description = '') {
|
|
212
|
+
const backend = requireBackend();
|
|
213
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
214
|
+
throw new Error(`Invalid secret name "${name}". Use SCREAMING_SNAKE_CASE, e.g. WORDPRESS_APP_PASSWORD.`);
|
|
215
|
+
}
|
|
216
|
+
if (!value)
|
|
217
|
+
throw new Error('Refusing to store an empty value.');
|
|
218
|
+
if (backend === 'windows-dpapi') {
|
|
219
|
+
const store = winRead();
|
|
220
|
+
store[name] = { value, description, updated: new Date().toISOString().slice(0, 10) };
|
|
221
|
+
winWrite(store);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (backend === 'macos-keychain') {
|
|
225
|
+
// -U updates in place instead of erroring when the item already exists.
|
|
226
|
+
// -w with the value on argv is visible to `ps` for the life of the call;
|
|
227
|
+
// security(1) offers no stdin form, so the exposure is unavoidable and
|
|
228
|
+
// measured in milliseconds on the user's own machine.
|
|
229
|
+
const r = run('security', ['add-generic-password', '-U', '-s', SERVICE, '-a', name, '-w', macEncode(value), '-j', description, '-T', '/usr/bin/security']);
|
|
230
|
+
if (r.status !== 0)
|
|
231
|
+
throw new KeychainUnavailable(`Keychain refused the write: ${(r.stderr || '').trim()}`);
|
|
232
|
+
const index = macReadIndex();
|
|
233
|
+
index[name] = { description, updated: new Date().toISOString().slice(0, 10) };
|
|
234
|
+
macWriteIndex(index);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// secret-tool reads the secret from stdin, so it never reaches the process
|
|
238
|
+
// table. It stays open waiting for EOF, which spawnSync gives it.
|
|
239
|
+
const r = run('secret-tool', ['store', '--label', `CRBRO ${name}`, 'service', SERVICE, 'account', name, 'description', description], value);
|
|
240
|
+
if (r.status !== 0)
|
|
241
|
+
throw new KeychainUnavailable(`secret-tool refused the write: ${(r.stderr || '').trim()}`);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The value, or null when there is no such secret. An environment variable of
|
|
245
|
+
* the same name wins, which is what makes CI and one-off overrides work
|
|
246
|
+
* without touching the store.
|
|
247
|
+
*/
|
|
248
|
+
function getSecret(name) {
|
|
249
|
+
const fromEnv = process.env[name];
|
|
250
|
+
if (fromEnv)
|
|
251
|
+
return fromEnv;
|
|
252
|
+
const backend = requireBackend();
|
|
253
|
+
if (backend === 'windows-dpapi') {
|
|
254
|
+
return winRead()[name]?.value ?? null;
|
|
255
|
+
}
|
|
256
|
+
if (backend === 'macos-keychain') {
|
|
257
|
+
const r = run('security', ['find-generic-password', '-s', SERVICE, '-a', name, '-w']);
|
|
258
|
+
// security(1) returns the OSStatus itself, truncated to eight bits: 44 is
|
|
259
|
+
// errSecItemNotFound, 36 is errSecInteractionNotAllowed — a locked
|
|
260
|
+
// keychain, which is the normal state over SSH and in CI. Reporting that
|
|
261
|
+
// one as "no such secret" would send someone hunting for a typo.
|
|
262
|
+
if (r.status === 36) {
|
|
263
|
+
throw new KeychainUnavailable('The macOS keychain is locked, which is usual over SSH or in CI. Unlock it in a desktop session, ' +
|
|
264
|
+
'or pass the credential as an environment variable instead.');
|
|
265
|
+
}
|
|
266
|
+
if (r.status !== 0)
|
|
267
|
+
return null;
|
|
268
|
+
return macDecode(r.stdout);
|
|
269
|
+
}
|
|
270
|
+
// Unlike security(1), secret-tool adds no trailing newline when stdout
|
|
271
|
+
// is a pipe: write_password_stdout only appends one for a tty. Trimming
|
|
272
|
+
// here would silently eat a newline that was genuinely part of the secret.
|
|
273
|
+
// Exit 1 with nothing on stderr is how it reports "no such item"; anything
|
|
274
|
+
// on stderr is a real failure and must not be mistaken for absence.
|
|
275
|
+
const r = run('secret-tool', ['lookup', 'service', SERVICE, 'account', name]);
|
|
276
|
+
if (r.status === 0)
|
|
277
|
+
return r.stdout;
|
|
278
|
+
if (r.status === 1 && !(r.stderr || '').trim())
|
|
279
|
+
return null;
|
|
280
|
+
throw new KeychainUnavailable(`secret-tool failed: ${(r.stderr || '').trim() || `exit ${r.status}`}`);
|
|
281
|
+
}
|
|
282
|
+
/** Names and descriptions only. Values are never returned by design. */
|
|
283
|
+
function listSecrets() {
|
|
284
|
+
const backend = requireBackend();
|
|
285
|
+
if (backend === 'windows-dpapi') {
|
|
286
|
+
return Object.entries(winRead())
|
|
287
|
+
.map(([name, e]) => ({ name, description: e.description, updated: e.updated }))
|
|
288
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
289
|
+
}
|
|
290
|
+
if (backend === 'macos-keychain') {
|
|
291
|
+
return Object.entries(macReadIndex())
|
|
292
|
+
.map(([name, e]) => ({ name, description: e.description, updated: e.updated }))
|
|
293
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
294
|
+
}
|
|
295
|
+
const r = run('secret-tool', ['search', '--all', 'service', SERVICE]);
|
|
296
|
+
if (r.status !== 0)
|
|
297
|
+
return [];
|
|
298
|
+
const names = [...r.stdout.matchAll(/^attribute\.account = (.+)$/gm)].map(m => m[1].trim());
|
|
299
|
+
return [...new Set(names)].sort().map(name => ({ name, description: '', updated: '' }));
|
|
300
|
+
}
|
|
301
|
+
function removeSecret(name) {
|
|
302
|
+
const backend = requireBackend();
|
|
303
|
+
if (backend === 'windows-dpapi') {
|
|
304
|
+
const store = winRead();
|
|
305
|
+
if (!(name in store))
|
|
306
|
+
return false;
|
|
307
|
+
delete store[name];
|
|
308
|
+
winWrite(store);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
if (backend === 'macos-keychain') {
|
|
312
|
+
const gone = run('security', ['delete-generic-password', '-s', SERVICE, '-a', name]).status === 0;
|
|
313
|
+
if (gone) {
|
|
314
|
+
const index = macReadIndex();
|
|
315
|
+
delete index[name];
|
|
316
|
+
macWriteIndex(index);
|
|
317
|
+
}
|
|
318
|
+
return gone;
|
|
319
|
+
}
|
|
320
|
+
return run('secret-tool', ['clear', 'service', SERVICE, 'account', name]).status === 0;
|
|
321
|
+
}
|
|
322
|
+
/** Windows only, and only for tests: forget the sealed file entirely. */
|
|
323
|
+
function _resetWindowsStoreForTests() {
|
|
324
|
+
invalidateCache();
|
|
325
|
+
const { file } = winPaths();
|
|
326
|
+
if ((0, node_fs_1.existsSync)(file))
|
|
327
|
+
(0, node_fs_1.unlinkSync)(file);
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/engine/keychain.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoCpE,wBAAgB,YAAY,IAAI,SAAS,CAkmCxC"}
|
package/dist/server.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─── CRBRO MCP Server ────────────────────────────────────────────
|
|
3
|
-
// Main server with all
|
|
3
|
+
// Main server with all 22 tools registered
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.createServer = createServer;
|
|
6
6
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
7
7
|
const zod_1 = require("zod");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
8
10
|
const brain_js_1 = require("./engine/brain.js");
|
|
9
11
|
const cortex_js_1 = require("./engine/cortex.js");
|
|
10
12
|
const synapses_js_1 = require("./engine/synapses.js");
|
|
@@ -14,10 +16,26 @@ const prefrontal_js_1 = require("./engine/prefrontal.js");
|
|
|
14
16
|
const index_js_1 = require("./search/index.js");
|
|
15
17
|
const maintenance_js_1 = require("./engine/maintenance.js");
|
|
16
18
|
const space_js_1 = require("./sync/space.js");
|
|
19
|
+
const keychain_js_1 = require("./engine/keychain.js");
|
|
20
|
+
/**
|
|
21
|
+
* The version of CRBRO that is actually running. The manifest carries its own
|
|
22
|
+
* version, but that one stamps the brain FORMAT and has not moved since 1.0.0
|
|
23
|
+
* — reporting it as "the version" told every user the same thing regardless of
|
|
24
|
+
* what they had installed, which is no use to anyone deciding whether to
|
|
25
|
+
* update.
|
|
26
|
+
*/
|
|
27
|
+
function runningVersion() {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8')).version;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return 'unknown';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
17
35
|
function createServer() {
|
|
18
36
|
const server = new mcp_js_1.McpServer({
|
|
19
37
|
name: 'crbro-memory',
|
|
20
|
-
version: '1.
|
|
38
|
+
version: '1.8.1',
|
|
21
39
|
});
|
|
22
40
|
// ─── Initialize engines ──────────────────────────────────────
|
|
23
41
|
const brain = new brain_js_1.Brain();
|
|
@@ -37,9 +55,9 @@ function createServer() {
|
|
|
37
55
|
// to this machine's own log. Nobody ever writes to anyone else's file, so
|
|
38
56
|
// two people working at once have nothing to collide over.
|
|
39
57
|
(0, space_js_1.attachSync)(brain, cortex);
|
|
40
|
-
// v1.4.0: CRBRO is fully free —
|
|
41
|
-
//
|
|
42
|
-
//
|
|
58
|
+
// v1.4.0: CRBRO is fully free — no license, no network calls. The former
|
|
59
|
+
// license engine (Firestore-backed freemium) lives in git history before
|
|
60
|
+
// that version if it is ever needed again.
|
|
43
61
|
// ═══════════════════════════════════════════════════════════════
|
|
44
62
|
// TOOL 1: crbro_boot — Boot sequence
|
|
45
63
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -111,7 +129,8 @@ function createServer() {
|
|
|
111
129
|
content: [{
|
|
112
130
|
type: 'text',
|
|
113
131
|
text: JSON.stringify({
|
|
114
|
-
|
|
132
|
+
crbro_version: runningVersion(),
|
|
133
|
+
brain_format: manifest.version,
|
|
115
134
|
total_neurons: manifest.total_neurons,
|
|
116
135
|
total_synapses: manifest.total_synapses,
|
|
117
136
|
total_sessions: manifest.total_sessions,
|
|
@@ -175,7 +194,8 @@ function createServer() {
|
|
|
175
194
|
redaction_note: result.redacted.length > 0
|
|
176
195
|
? `Stored, but ${result.redacted.length} credential(s) were replaced with a marker: ` +
|
|
177
196
|
`${result.redacted.join(', ')}. The sentence around them was kept. ` +
|
|
178
|
-
'
|
|
197
|
+
'Do not try to store the value again. Offer the user crbro_secret instead: ' +
|
|
198
|
+
'it puts the credential in the OS keychain, and then you record only its name here.'
|
|
179
199
|
: undefined,
|
|
180
200
|
total_facts: result.neuron.facts.length,
|
|
181
201
|
total_decisions: result.neuron.decisions.length,
|
|
@@ -701,6 +721,90 @@ function createServer() {
|
|
|
701
721
|
}
|
|
702
722
|
});
|
|
703
723
|
// ═══════════════════════════════════════════════════════════════
|
|
724
|
+
// TOOL 22: crbro_secret — Credentials, brokered to the OS keychain
|
|
725
|
+
// ═══════════════════════════════════════════════════════════════
|
|
726
|
+
server.tool('crbro_secret', 'Store and read credentials in the operating system own keychain — macOS Keychain, the Linux Secret Service, or DPAPI on Windows. CRBRO keeps no copy and invents no crypto: it brokers access to the store the machine already has, outside the brain, where no sync and no team space can reach it. Use it the moment the user hands you a credential: store it here, then record only the NAME with crbro_learn, never the value. Use get when a task needs one, and do not print the value back to the user unless they asked for that specific secret. Names are SCREAMING_SNAKE_CASE, e.g. WORDPRESS_APP_PASSWORD.', {
|
|
727
|
+
action: zod_1.z.enum(['get', 'set', 'list', 'remove', 'status'])
|
|
728
|
+
.describe('get = read one, set = store or update one, list = names only, remove = delete one, status = which keychain this machine offers'),
|
|
729
|
+
name: zod_1.z.string().optional().describe('Secret name in SCREAMING_SNAKE_CASE'),
|
|
730
|
+
value: zod_1.z.string().optional().describe('The credential itself. Only for set.'),
|
|
731
|
+
description: zod_1.z.string().optional().describe('What it is for, e.g. "WordPress example.com - REST API"'),
|
|
732
|
+
}, async (args) => {
|
|
733
|
+
try {
|
|
734
|
+
let payload;
|
|
735
|
+
if (args.action === 'status') {
|
|
736
|
+
const { backend, reason } = (0, keychain_js_1.detectBackend)();
|
|
737
|
+
payload = {
|
|
738
|
+
backend,
|
|
739
|
+
available: backend !== null,
|
|
740
|
+
message: backend
|
|
741
|
+
? `Credentials on this machine are stored in: ${backend}.`
|
|
742
|
+
: `No credential store available here. ${reason ?? ''} Credentials can still be passed as environment variables.`,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
else if (args.action === 'list') {
|
|
746
|
+
const entries = (0, keychain_js_1.listSecrets)();
|
|
747
|
+
payload = {
|
|
748
|
+
count: entries.length,
|
|
749
|
+
secrets: entries,
|
|
750
|
+
note: 'Names only. Values are never listed, by design.',
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
else if (!args.name) {
|
|
754
|
+
throw new Error(`"name" is required for action "${args.action}".`);
|
|
755
|
+
}
|
|
756
|
+
else if (args.action === 'set') {
|
|
757
|
+
if (!args.value)
|
|
758
|
+
throw new Error('"value" is required for action "set".');
|
|
759
|
+
(0, keychain_js_1.setSecret)(args.name, args.value, args.description ?? '');
|
|
760
|
+
payload = {
|
|
761
|
+
stored: args.name,
|
|
762
|
+
message: `Stored in the OS keychain as ${args.name}. Now record the NAME in the brain with crbro_learn — never the value — so a later session knows where to look.`,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
else if (args.action === 'remove') {
|
|
766
|
+
const gone = (0, keychain_js_1.removeSecret)(args.name);
|
|
767
|
+
payload = {
|
|
768
|
+
removed: gone,
|
|
769
|
+
message: gone
|
|
770
|
+
? `${args.name} deleted from the keychain.`
|
|
771
|
+
: `No secret named ${args.name} was found.`,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
const value = (0, keychain_js_1.getSecret)(args.name);
|
|
776
|
+
payload = value === null
|
|
777
|
+
? {
|
|
778
|
+
found: false,
|
|
779
|
+
message: `No secret named ${args.name}. Run action "list" to see what is stored, or ask the user for it and store it with action "set".`,
|
|
780
|
+
}
|
|
781
|
+
: {
|
|
782
|
+
found: true,
|
|
783
|
+
name: args.name,
|
|
784
|
+
value,
|
|
785
|
+
note: 'Use it for the task at hand. Do not repeat it back to the user and do not write it into any file.',
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
content: [{
|
|
790
|
+
type: 'text',
|
|
791
|
+
text: JSON.stringify(payload, null, 2),
|
|
792
|
+
}],
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
catch (err) {
|
|
796
|
+
return {
|
|
797
|
+
content: [{
|
|
798
|
+
type: 'text',
|
|
799
|
+
text: err instanceof keychain_js_1.KeychainUnavailable
|
|
800
|
+
? `No credential store available: ${err.message}`
|
|
801
|
+
: `CRBRO secret error: ${err instanceof Error ? err.message : String(err)}`,
|
|
802
|
+
}],
|
|
803
|
+
isError: true,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
// ═══════════════════════════════════════════════════════════════
|
|
704
808
|
// TOOL 18: crbro_forget — Remove knowledge for good
|
|
705
809
|
// ═══════════════════════════════════════════════════════════════
|
|
706
810
|
server.tool('crbro_forget', 'Permanently remove facts from a neuron. This is for things that must not exist at all — a credential, personal data, something stored by mistake. For knowledge that merely stopped being true, use crbro_revise instead, which keeps the history. The whole neuron is copied to .quarantine/ before anything is removed, so a mistake can be undone by hand. Always tell the user what you are about to remove and get their agreement first.', {
|