@aimarket/warden 0.6.0 → 0.8.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.
- package/CHANGELOG.md +24 -1
- package/README-es.md +57 -6
- package/README-fr.md +57 -6
- package/README-ru.md +57 -6
- package/README-zh.md +57 -6
- package/README.md +57 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-rpc.d.ts +3 -1
- package/dist/mcp-rpc.d.ts.map +1 -1
- package/dist/mcp-rpc.js +32 -8
- package/dist/mcp-rpc.js.map +1 -1
- package/dist/mcp-server.js +50 -44
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools.d.ts +1 -12
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +92 -18
- package/dist/mcp-tools.js.map +1 -1
- package/dist/pin-store.d.ts +13 -0
- package/dist/pin-store.d.ts.map +1 -0
- package/dist/pin-store.js +44 -0
- package/dist/pin-store.js.map +1 -0
- package/dist/pinning.d.ts +4 -3
- package/dist/pinning.d.ts.map +1 -1
- package/dist/pinning.js +16 -3
- package/dist/pinning.js.map +1 -1
- package/dist/state.d.ts +7 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +61 -0
- package/dist/state.js.map +1 -0
- package/dist/static-scan.d.ts +2 -2
- package/dist/static-scan.d.ts.map +1 -1
- package/dist/static-scan.js +107 -8
- package/dist/static-scan.js.map +1 -1
- package/dist/threat-feed.d.ts +9 -0
- package/dist/threat-feed.d.ts.map +1 -1
- package/dist/threat-feed.js +50 -7
- package/dist/threat-feed.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wrap-cli.d.ts +21 -0
- package/dist/wrap-cli.d.ts.map +1 -0
- package/dist/wrap-cli.js +158 -0
- package/dist/wrap-cli.js.map +1 -0
- package/dist/wrap-state.d.ts +19 -0
- package/dist/wrap-state.d.ts.map +1 -0
- package/dist/wrap-state.js +62 -0
- package/dist/wrap-state.js.map +1 -0
- package/dist/wrap-wire.d.ts +13 -0
- package/dist/wrap-wire.d.ts.map +1 -0
- package/dist/wrap-wire.js +88 -0
- package/dist/wrap-wire.js.map +1 -0
- package/dist/wrap.d.ts +3 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +316 -0
- package/dist/wrap.js.map +1 -0
- package/docs/gates.es.md +224 -0
- package/docs/gates.fr.md +228 -0
- package/docs/gates.md +219 -0
- package/docs/gates.ru.md +225 -0
- package/docs/gates.zh.md +178 -0
- package/docs/security-hardening.es.md +40 -0
- package/docs/security-hardening.fr.md +40 -0
- package/docs/security-hardening.md +40 -0
- package/docs/security-hardening.ru.md +40 -0
- package/docs/security-hardening.zh.md +40 -0
- package/package.json +7 -4
package/dist/wrap-cli.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createHash, createPublicKey } from 'node:crypto';
|
|
3
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { asPolicy } from './mcp-tools.js';
|
|
6
|
+
import { defaultStateDir, readState, withStateLock, writeState } from './state.js';
|
|
7
|
+
import { FilePinStore, pinRevision } from './pin-store.js';
|
|
8
|
+
import { Warden, ThreatFeed, canonicalToolsHash, serverIdentityHash, displaySafe } from './index.js';
|
|
9
|
+
import { observationPath, observationRevision, describeReview } from './wrap-state.js';
|
|
10
|
+
export async function parseWrapArgs(argv) {
|
|
11
|
+
const mode = argv.shift();
|
|
12
|
+
if (mode !== 'wrap' && mode !== 'pins')
|
|
13
|
+
throw new Error('Usage: warden-mcp wrap [flags] -- command [args] | pins status|approve|revoke --id ID');
|
|
14
|
+
const opts = { mode, args: [], stateDir: defaultStateDir(), policy: asPolicy(undefined), auditOnly: false };
|
|
15
|
+
if (mode === 'pins') {
|
|
16
|
+
opts.action = argv.shift();
|
|
17
|
+
if (!['status', 'approve', 'revoke'].includes(opts.action ?? ''))
|
|
18
|
+
throw new Error('pins requires status, approve or revoke');
|
|
19
|
+
}
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
while (argv.length) {
|
|
22
|
+
const flag = argv.shift();
|
|
23
|
+
if (flag === '--' && mode === 'wrap') {
|
|
24
|
+
opts.command = argv.shift();
|
|
25
|
+
opts.args = argv;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
if (seen.has(flag))
|
|
29
|
+
throw new Error(`Duplicate flag ${flag}`);
|
|
30
|
+
seen.add(flag);
|
|
31
|
+
if (flag === '--audit-only' && mode === 'wrap') {
|
|
32
|
+
opts.auditOnly = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const allowed = mode === 'wrap' ? ['--id', '--state-dir', '--policy', '--feed', '--feed-key', '--verdict-log'] : ['--id', '--state-dir', '--feed', '--feed-key'];
|
|
36
|
+
if (!allowed.includes(flag))
|
|
37
|
+
throw new Error(`Unknown flag ${flag}`);
|
|
38
|
+
const value = argv.shift();
|
|
39
|
+
if (!value || value.startsWith('--'))
|
|
40
|
+
throw new Error(`Missing value for ${flag}`);
|
|
41
|
+
switch (flag) {
|
|
42
|
+
case '--id':
|
|
43
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value))
|
|
44
|
+
throw new Error('--id must be a slug (1–128 letters, digits, ., _ or -)');
|
|
45
|
+
opts.id = value;
|
|
46
|
+
break;
|
|
47
|
+
case '--state-dir':
|
|
48
|
+
opts.stateDir = value;
|
|
49
|
+
break;
|
|
50
|
+
case '--feed':
|
|
51
|
+
opts.feed = value;
|
|
52
|
+
break;
|
|
53
|
+
case '--feed-key':
|
|
54
|
+
opts.feedKey = value;
|
|
55
|
+
break;
|
|
56
|
+
case '--verdict-log':
|
|
57
|
+
opts.verdictLog = value;
|
|
58
|
+
break;
|
|
59
|
+
case '--policy': {
|
|
60
|
+
const raw = JSON.parse(await readFile(value, 'utf8'));
|
|
61
|
+
// asPolicy preserves compatibility for MCP callers; CLI files must not silently repair bad types.
|
|
62
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
63
|
+
throw new Error('policy must be an object');
|
|
64
|
+
for (const key of Object.keys(raw))
|
|
65
|
+
if (!['blockAtSeverity', 'pinToolDefs', 'allowUnknownServers', 'sensitiveToolPatterns'].includes(key))
|
|
66
|
+
throw new Error(`Unknown policy field ${key}`);
|
|
67
|
+
for (const key of ['pinToolDefs', 'allowUnknownServers'])
|
|
68
|
+
if (key in raw && typeof raw[key] !== 'boolean')
|
|
69
|
+
throw new Error(`policy.${key} must be boolean`);
|
|
70
|
+
if ('blockAtSeverity' in raw && typeof raw.blockAtSeverity !== 'string')
|
|
71
|
+
throw new Error('policy.blockAtSeverity must be a severity');
|
|
72
|
+
if ('sensitiveToolPatterns' in raw && (!Array.isArray(raw.sensitiveToolPatterns) || !raw.sensitiveToolPatterns.every((v) => typeof v === 'string')))
|
|
73
|
+
throw new Error('policy.sensitiveToolPatterns must be strings');
|
|
74
|
+
opts.policy = asPolicy(raw);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (mode === 'wrap' && !opts.command)
|
|
80
|
+
throw new Error('wrap requires -- command [args]');
|
|
81
|
+
if (mode === 'pins' && !opts.id)
|
|
82
|
+
throw new Error('pins requires --id');
|
|
83
|
+
if (opts.feed) {
|
|
84
|
+
const url = new URL(opts.feed);
|
|
85
|
+
if (!['https:', 'http:'].includes(url.protocol))
|
|
86
|
+
throw new Error('--feed requires an HTTP(S) URL');
|
|
87
|
+
}
|
|
88
|
+
if (opts.feedKey) {
|
|
89
|
+
if (!/^(?:[a-fA-F0-9]{2})+$/.test(opts.feedKey) || createPublicKey({ key: Buffer.from(opts.feedKey, 'hex'), format: 'der', type: 'spki' }).asymmetricKeyType !== 'ed25519')
|
|
90
|
+
throw new Error('--feed-key must be an Ed25519 SPKI DER hex key');
|
|
91
|
+
}
|
|
92
|
+
return opts;
|
|
93
|
+
}
|
|
94
|
+
export function wrapServer(opts) {
|
|
95
|
+
const id = opts.id ?? createHash('sha256').update(JSON.stringify([opts.command, opts.args])).digest('hex');
|
|
96
|
+
return { id, name: id, transport: 'stdio', command: opts.command, args: opts.args };
|
|
97
|
+
}
|
|
98
|
+
export function wrapLogger(auditOnly = false) {
|
|
99
|
+
const write = (message) => process.stderr.write(`warden: ${auditOnly ? 'AUDIT-ONLY ' : ''}${displaySafe(message, 8192)}\n`);
|
|
100
|
+
const logger = { debug: () => { }, info: () => { }, warn: write, error: write, child: () => logger };
|
|
101
|
+
return logger;
|
|
102
|
+
}
|
|
103
|
+
export async function loadWrapFeed(opts) {
|
|
104
|
+
const log = wrapLogger(opts.auditOnly);
|
|
105
|
+
const feed = new ThreatFeed({ feedPublicKey: opts.feedKey, stateDir: opts.stateDir, log });
|
|
106
|
+
await feed.load(opts.feed);
|
|
107
|
+
if (opts.feed && feed.status.stale)
|
|
108
|
+
log.warn('Remote feed unavailable or stale; using retained records and built-in floor');
|
|
109
|
+
return feed;
|
|
110
|
+
}
|
|
111
|
+
export async function runPins(opts) {
|
|
112
|
+
if (!process.stdin.isTTY)
|
|
113
|
+
throw new Error('pins requires a human terminal (TTY on stdin); refusing non-interactive review');
|
|
114
|
+
const id = opts.id, path = observationPath(opts.stateDir, id), store = new FilePinStore(join(opts.stateDir, 'pins'));
|
|
115
|
+
const { previous, current } = await withStateLock(path, async () => ({ previous: await store.getPin(id), current: await readState(path) }));
|
|
116
|
+
process.stdout.write(describeReview(previous, current) + '\n');
|
|
117
|
+
if (opts.action === 'status')
|
|
118
|
+
return;
|
|
119
|
+
if (opts.action === 'approve' && !current)
|
|
120
|
+
throw new Error('No observed definitions; run wrap first');
|
|
121
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
122
|
+
let answer;
|
|
123
|
+
try {
|
|
124
|
+
answer = await rl.question(`Type ${opts.action} ${id} to confirm: `);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
rl.close();
|
|
128
|
+
}
|
|
129
|
+
if (answer !== `${opts.action} ${id}`)
|
|
130
|
+
throw new Error('Not confirmed; pin unchanged');
|
|
131
|
+
const feed = await loadWrapFeed(opts);
|
|
132
|
+
await withStateLock(path, async () => {
|
|
133
|
+
if (observationRevision(await readState(path)) !== observationRevision(current))
|
|
134
|
+
throw new Error('Observed definitions changed since review; inspect status again');
|
|
135
|
+
if (opts.action === 'revoke') {
|
|
136
|
+
// Persist denial before deleting the pin, so a crash cannot accidentally enable TOFU.
|
|
137
|
+
if (current)
|
|
138
|
+
await writeState(path, { ...current, revoked: true });
|
|
139
|
+
await store.replace(id, pinRevision(previous));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const tools = current.tools ?? previous?.tools;
|
|
143
|
+
if (!tools)
|
|
144
|
+
throw new Error('No reviewed tool snapshot available');
|
|
145
|
+
const policy = asPolicy(current.policy);
|
|
146
|
+
// Re-approval removes only pin drift; the other gates still have to pass.
|
|
147
|
+
const warden = Warden.create({ policy, threatFeed: feed, store: { getPin: async () => undefined, putPin: async () => { } } });
|
|
148
|
+
const verdict = await warden.vet(current.server, tools);
|
|
149
|
+
if (!verdict.allow)
|
|
150
|
+
throw new Error(`Approval blocked: ${verdict.findings.map(f => f.code).join(', ')}`);
|
|
151
|
+
await store.replace(id, pinRevision(previous), { serverId: id, tools, toolsHash: canonicalToolsHash(tools), toolsHashVersion: 2,
|
|
152
|
+
identityHash: serverIdentityHash(current.server), toolNames: tools.map(t => t.name).sort(), approvedAt: new Date().toISOString() });
|
|
153
|
+
await writeState(path, { ...current, revoked: false });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
process.stdout.write(`${opts.action}: ${displaySafe(id)}\n`);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=wrap-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-cli.js","sourceRoot":"","sources":["../src/wrap-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FilePinStore, pinRevision } from './pin-store.js';
|
|
2
|
+
import { Warden } from './index.js';
|
|
3
|
+
import type { McpServerRef, ToolDef, WardenPolicy, WardenVerdict } from './types.js';
|
|
4
|
+
import type { ThreatFeed } from './threat-feed.js';
|
|
5
|
+
export interface Observation {
|
|
6
|
+
server: McpServerRef;
|
|
7
|
+
/** Absent when vetLaunch refused to start the new command. */
|
|
8
|
+
tools?: ToolDef[];
|
|
9
|
+
policy: WardenPolicy;
|
|
10
|
+
revoked?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function observationPath(dir: string, id: string): string;
|
|
13
|
+
export declare function observationRevision(value: Observation | undefined): string;
|
|
14
|
+
export declare function observe(dir: string, value: Observation): Promise<void>;
|
|
15
|
+
/** One lock spans TOFU's read/vet/approve, as well as human review's final CAS. */
|
|
16
|
+
export declare function vetAndPin(dir: string, server: McpServerRef, tools: ToolDef[], policy: WardenPolicy, store: FilePinStore, warden: Warden, feed: ThreatFeed, auditOnly: boolean, isCurrent: () => boolean, record: (v: WardenVerdict) => void): Promise<WardenVerdict>;
|
|
17
|
+
export declare function describeReview(previous: Awaited<ReturnType<FilePinStore['getPin']>>, current: Observation | undefined): string;
|
|
18
|
+
export { pinRevision };
|
|
19
|
+
//# sourceMappingURL=wrap-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-state.d.ts","sourceRoot":"","sources":["../src/wrap-state.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAuD,MAAM,YAAY,CAAC;AACzF,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AACD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/D;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAE1E;AACD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAO5E;AAED,mFAAmF;AACnF,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EACvG,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAiBjK;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAiB9H;AACD,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pinRevision } from './pin-store.js';
|
|
4
|
+
import { readState, writeState, withStateLock } from './state.js';
|
|
5
|
+
import { Warden, canonicalToolsHash, serverIdentityHash, displaySafe } from './index.js';
|
|
6
|
+
export function observationPath(dir, id) {
|
|
7
|
+
return join(dir, 'wrap', createHash('sha256').update(id).digest('hex') + '.json');
|
|
8
|
+
}
|
|
9
|
+
export function observationRevision(value) {
|
|
10
|
+
return createHash('sha256').update(JSON.stringify(value) ?? 'null').digest('hex');
|
|
11
|
+
}
|
|
12
|
+
export async function observe(dir, value) {
|
|
13
|
+
const path = observationPath(dir, value.server.id);
|
|
14
|
+
await withStateLock(path, async () => {
|
|
15
|
+
const previous = await readState(path);
|
|
16
|
+
const tools = value.tools ?? (previous?.revoked && serverIdentityHash(previous.server) === serverIdentityHash(value.server) ? previous.tools : undefined);
|
|
17
|
+
await writeState(path, { ...value, tools, revoked: previous?.revoked });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** One lock spans TOFU's read/vet/approve, as well as human review's final CAS. */
|
|
21
|
+
export async function vetAndPin(dir, server, tools, policy, store, warden, feed, auditOnly, isCurrent, record) {
|
|
22
|
+
const path = observationPath(dir, server.id);
|
|
23
|
+
return withStateLock(path, async () => {
|
|
24
|
+
const previous = await readState(path);
|
|
25
|
+
await writeState(path, { server, tools, policy, revoked: previous?.revoked });
|
|
26
|
+
const verdict = await warden.vet(server, tools);
|
|
27
|
+
record(verdict);
|
|
28
|
+
if (!auditOnly && !isCurrent())
|
|
29
|
+
throw new Error('tools changed, blocked by WARDEN during verification');
|
|
30
|
+
if (previous?.revoked && !auditOnly)
|
|
31
|
+
throw new Error('Approval revoked; inspect pins status and approve before reconnecting');
|
|
32
|
+
if (!auditOnly && verdict.allow && policy.pinToolDefs && !previous?.revoked && !await store.getPin(server.id)) {
|
|
33
|
+
// Use the published approve API with a CAS adapter: other hosts may share this store.
|
|
34
|
+
const firstContact = Warden.create({ policy, threatFeed: feed,
|
|
35
|
+
store: { getPin: id => store.getPin(id), putPin: pin => store.replace(server.id, null, pin) } });
|
|
36
|
+
await firstContact.approve(server, tools);
|
|
37
|
+
}
|
|
38
|
+
return verdict;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function describeReview(previous, current) {
|
|
42
|
+
const oldTools = previous?.tools ?? [];
|
|
43
|
+
const newTools = current?.tools ?? oldTools;
|
|
44
|
+
const lines = [`Previous identity: ${previous?.identityHash ?? '(none)'}`,
|
|
45
|
+
`Observed identity: ${current ? serverIdentityHash(current.server) : '(none)'}`,
|
|
46
|
+
`Launch: ${current ? JSON.stringify(current.server) : '(none)'}`];
|
|
47
|
+
if (current && !current.tools)
|
|
48
|
+
lines.push('Launch was blocked; tool definitions below are the PREVIOUS snapshot, not fetched from the new command.');
|
|
49
|
+
for (const name of new Set([...oldTools, ...newTools].map(t => t.name))) {
|
|
50
|
+
const old = oldTools.find(t => t.name === name), next = newTools.find(t => t.name === name);
|
|
51
|
+
if (JSON.stringify(old) !== JSON.stringify(next)) {
|
|
52
|
+
if (old)
|
|
53
|
+
lines.push('- ' + JSON.stringify(old));
|
|
54
|
+
if (next)
|
|
55
|
+
lines.push('+ ' + JSON.stringify(next));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
lines.push(`Previous tools: ${previous?.toolsHash ?? '(none)'}`, `Observed tools: ${canonicalToolsHash(newTools)}`, `Revoked: ${current?.revoked === true}`);
|
|
59
|
+
return lines.map(line => displaySafe(line, 2_000_000)).join('\n');
|
|
60
|
+
}
|
|
61
|
+
export { pinRevision };
|
|
62
|
+
//# sourceMappingURL=wrap-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-state.js","sourceRoot":"","sources":["../src/wrap-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAgB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAWzF,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,EAAU;IACrD,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AACpF,CAAC;AACD,MAAM,UAAU,mBAAmB,CAAC,KAA8B;IAChE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpF,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,KAAkB;IAC3D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,aAAa,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAc,IAAI,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC1J,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,MAAoB,EAAE,KAAgB,EAAE,MAAoB,EACvG,KAAmB,EAAE,MAAc,EAAE,IAAgB,EAAE,SAAkB,EAAE,SAAwB,EAAE,MAAkC;IACvI,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAC7C,OAAO,aAAa,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAc,IAAI,CAAC,CAAC;QACpD,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,OAAO,CAAC,CAAC;QAChB,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACxG,IAAI,QAAQ,EAAE,OAAO,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC9H,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAC9G,sFAAsF;YACtF,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI;gBAC3D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACnG,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAqD,EAAE,OAAgC;IACpH,MAAM,QAAQ,GAAG,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAC;IAC5C,MAAM,KAAK,GAAG,CAAC,sBAAsB,QAAQ,EAAE,YAAY,IAAI,QAAQ,EAAE;QACvE,sBAAsB,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAC/E,WAAW,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACpE,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,yGAAyG,CAAC,CAAC;IACrJ,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAC5F,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,IAAI,GAAG;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,mBAAmB,QAAQ,EAAE,SAAS,IAAI,QAAQ,EAAE,EAAE,mBAAmB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAChH,YAAY,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpE,CAAC;AACD,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
export declare const WRAP_FRAME_BYTES: number;
|
|
3
|
+
export type FrameMode = 'unknown' | 'ndjson' | 'lsp';
|
|
4
|
+
/** Incremental, bounded framing. Raw JSON bodies are retained for transparent forwarding. */
|
|
5
|
+
export declare class WrapFrames {
|
|
6
|
+
mode: FrameMode;
|
|
7
|
+
private buf;
|
|
8
|
+
constructor(mode?: FrameMode);
|
|
9
|
+
push(chunk: Buffer): string[];
|
|
10
|
+
end(): string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function readFrames(stream: Readable, frames: WrapFrames, onBody: (body: string) => void, onError: (e: unknown) => void, onEnd: () => void): void;
|
|
13
|
+
//# sourceMappingURL=wrap-wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-wire.d.ts","sourceRoot":"","sources":["../src/wrap-wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,gBAAgB,QAAmB,CAAC;AACjD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;AAErD,6FAA6F;AAC7F,qBAAa,UAAU;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,GAAG,CAA2B;gBAC1B,IAAI,GAAE,SAAqB;IACvC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAuC7B,GAAG,IAAI,MAAM,EAAE;CAKhB;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAWvJ"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const WRAP_FRAME_BYTES = 32 * 1024 * 1024;
|
|
2
|
+
/** Incremental, bounded framing. Raw JSON bodies are retained for transparent forwarding. */
|
|
3
|
+
export class WrapFrames {
|
|
4
|
+
mode;
|
|
5
|
+
buf = Buffer.alloc(0);
|
|
6
|
+
constructor(mode = 'unknown') { this.mode = mode; }
|
|
7
|
+
push(chunk) {
|
|
8
|
+
this.buf = Buffer.concat([this.buf, chunk]);
|
|
9
|
+
if (this.mode === 'unknown') {
|
|
10
|
+
const prefix = this.buf.subarray(0, 8193).toString('ascii').trimStart().toLowerCase();
|
|
11
|
+
if (!prefix || 'content-length:'.startsWith(prefix)) {
|
|
12
|
+
if (this.buf.length > 8192)
|
|
13
|
+
throw new Error('MCP header too large');
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
this.mode = prefix.startsWith('content-length:') ? 'lsp' : 'ndjson';
|
|
17
|
+
}
|
|
18
|
+
const bodies = [];
|
|
19
|
+
while (this.buf.length) {
|
|
20
|
+
let start = 0, end, consumed;
|
|
21
|
+
if (this.mode === 'lsp') {
|
|
22
|
+
const header = this.buf.indexOf('\r\n\r\n');
|
|
23
|
+
if (header < 0) {
|
|
24
|
+
if (this.buf.length > 8192)
|
|
25
|
+
throw new Error('MCP header too large');
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
if (header > 8192)
|
|
29
|
+
throw new Error('MCP header too large');
|
|
30
|
+
const lengths = [...this.buf.subarray(0, header).toString('ascii').matchAll(/^Content-Length:\s*(\d+)\s*$/gim)];
|
|
31
|
+
const length = lengths.length === 1 ? Number(lengths[0][1]) : NaN;
|
|
32
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > WRAP_FRAME_BYTES)
|
|
33
|
+
throw new Error('Invalid or oversized MCP frame');
|
|
34
|
+
start = header + 4;
|
|
35
|
+
end = start + length;
|
|
36
|
+
consumed = end;
|
|
37
|
+
if (this.buf.length < end)
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
end = this.buf.indexOf(10);
|
|
42
|
+
consumed = end + 1;
|
|
43
|
+
if (end < 0) {
|
|
44
|
+
if (this.buf.length > WRAP_FRAME_BYTES)
|
|
45
|
+
throw new Error('MCP frame too large');
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (end > WRAP_FRAME_BYTES)
|
|
49
|
+
throw new Error('MCP frame too large');
|
|
50
|
+
}
|
|
51
|
+
const body = this.buf.subarray(start, end).toString('utf8');
|
|
52
|
+
this.buf = this.buf.subarray(consumed);
|
|
53
|
+
if (body.trim())
|
|
54
|
+
bodies.push(body);
|
|
55
|
+
}
|
|
56
|
+
return bodies;
|
|
57
|
+
}
|
|
58
|
+
end() {
|
|
59
|
+
if (!this.buf.length)
|
|
60
|
+
return [];
|
|
61
|
+
if (this.mode === 'lsp')
|
|
62
|
+
throw new Error('Incomplete MCP frame');
|
|
63
|
+
return this.push(Buffer.from('\n'));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function readFrames(stream, frames, onBody, onError, onEnd) {
|
|
67
|
+
stream.on('data', chunk => {
|
|
68
|
+
try {
|
|
69
|
+
for (const body of frames.push(Buffer.from(chunk)))
|
|
70
|
+
onBody(body);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
onError(e);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
stream.on('end', () => {
|
|
77
|
+
try {
|
|
78
|
+
for (const body of frames.end())
|
|
79
|
+
onBody(body);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
onError(e);
|
|
83
|
+
}
|
|
84
|
+
onEnd();
|
|
85
|
+
});
|
|
86
|
+
stream.on('error', onError);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=wrap-wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-wire.js","sourceRoot":"","sources":["../src/wrap-wire.ts"],"names":[],"mappings":"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"}
|
package/dist/wrap.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAQA,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAavF,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA0NhE"}
|