@bxb1337/windsurf-fast-context 1.0.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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +337 -0
  3. package/dist/auth/api-key.d.ts +2 -0
  4. package/dist/auth/api-key.test.d.ts +1 -0
  5. package/dist/auth/jwt-manager.d.ts +18 -0
  6. package/dist/auth/jwt-manager.test.d.ts +1 -0
  7. package/dist/cjs/auth/api-key.js +10 -0
  8. package/dist/cjs/auth/api-key.test.js +29 -0
  9. package/dist/cjs/auth/jwt-manager.js +94 -0
  10. package/dist/cjs/auth/jwt-manager.test.js +99 -0
  11. package/dist/cjs/conversion/prompt-converter.js +57 -0
  12. package/dist/cjs/conversion/prompt-converter.test.js +95 -0
  13. package/dist/cjs/conversion/response-converter.js +233 -0
  14. package/dist/cjs/conversion/response-converter.test.js +65 -0
  15. package/dist/cjs/index.js +23 -0
  16. package/dist/cjs/model/devstral-language-model.js +399 -0
  17. package/dist/cjs/model/devstral-language-model.test.js +410 -0
  18. package/dist/cjs/package.json +3 -0
  19. package/dist/cjs/protocol/connect-frame.js +40 -0
  20. package/dist/cjs/protocol/connect-frame.test.js +36 -0
  21. package/dist/cjs/protocol/protobuf.js +114 -0
  22. package/dist/cjs/protocol/protobuf.test.js +58 -0
  23. package/dist/cjs/provider.js +13 -0
  24. package/dist/cjs/provider.test.js +61 -0
  25. package/dist/cjs/transport/http.js +83 -0
  26. package/dist/cjs/transport/http.test.js +196 -0
  27. package/dist/cjs/types/index.js +2 -0
  28. package/dist/conversion/prompt-converter.d.ts +49 -0
  29. package/dist/conversion/prompt-converter.test.d.ts +1 -0
  30. package/dist/conversion/response-converter.d.ts +12 -0
  31. package/dist/conversion/response-converter.test.d.ts +1 -0
  32. package/dist/esm/auth/api-key.js +7 -0
  33. package/dist/esm/auth/api-key.test.js +27 -0
  34. package/dist/esm/auth/jwt-manager.js +90 -0
  35. package/dist/esm/auth/jwt-manager.test.js +97 -0
  36. package/dist/esm/conversion/prompt-converter.js +54 -0
  37. package/dist/esm/conversion/prompt-converter.test.js +93 -0
  38. package/dist/esm/conversion/response-converter.js +230 -0
  39. package/dist/esm/conversion/response-converter.test.js +63 -0
  40. package/dist/esm/dist/cjs/index.js +3 -0
  41. package/dist/esm/index.js +3 -0
  42. package/dist/esm/model/devstral-language-model.js +395 -0
  43. package/dist/esm/model/devstral-language-model.test.js +408 -0
  44. package/dist/esm/protocol/connect-frame.js +36 -0
  45. package/dist/esm/protocol/connect-frame.test.js +34 -0
  46. package/dist/esm/protocol/protobuf.js +108 -0
  47. package/dist/esm/protocol/protobuf.test.js +56 -0
  48. package/dist/esm/provider.js +9 -0
  49. package/dist/esm/provider.test.js +59 -0
  50. package/dist/esm/scripts/postbuild.js +10 -0
  51. package/dist/esm/src/index.js +1 -0
  52. package/dist/esm/transport/http.js +78 -0
  53. package/dist/esm/transport/http.test.js +194 -0
  54. package/dist/esm/types/index.js +1 -0
  55. package/dist/esm/vitest.config.js +6 -0
  56. package/dist/index.cjs +2 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +1 -0
  59. package/dist/model/devstral-language-model.d.ts +118 -0
  60. package/dist/model/devstral-language-model.test.d.ts +1 -0
  61. package/dist/protocol/connect-frame.d.ts +10 -0
  62. package/dist/protocol/connect-frame.test.d.ts +1 -0
  63. package/dist/protocol/protobuf.d.ts +11 -0
  64. package/dist/protocol/protobuf.test.d.ts +1 -0
  65. package/dist/provider.d.ts +5 -0
  66. package/dist/provider.test.d.ts +1 -0
  67. package/dist/transport/http.d.ts +22 -0
  68. package/dist/transport/http.test.d.ts +1 -0
  69. package/dist/types/index.d.ts +37 -0
  70. package/package.json +51 -0
@@ -0,0 +1,408 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { extractStrings } from '../protocol/protobuf.js';
3
+ import { connectFrameDecode, connectFrameEncode } from '../protocol/connect-frame.js';
4
+ import { DevstralLanguageModel } from './devstral-language-model.js';
5
+ function bufferFromBody(body) {
6
+ if (body == null) {
7
+ return Buffer.alloc(0);
8
+ }
9
+ if (typeof body === 'string') {
10
+ return Buffer.from(body, 'utf8');
11
+ }
12
+ if (body instanceof ArrayBuffer) {
13
+ return Buffer.from(body);
14
+ }
15
+ if (ArrayBuffer.isView(body)) {
16
+ return Buffer.from(body.buffer, body.byteOffset, body.byteLength);
17
+ }
18
+ throw new Error(`Unsupported request body type: ${typeof body}`);
19
+ }
20
+ function makeJwt(exp, tag) {
21
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
22
+ const payload = Buffer.from(JSON.stringify({ exp, tag })).toString('base64url');
23
+ return `${header}.${payload}.signature`;
24
+ }
25
+ function chunkBuffer(buffer, chunkSize) {
26
+ const chunks = [];
27
+ for (let offset = 0; offset < buffer.length; offset += chunkSize) {
28
+ chunks.push(Uint8Array.from(buffer.subarray(offset, offset + chunkSize)));
29
+ }
30
+ return chunks;
31
+ }
32
+ function makeChunkedBody(chunks, options = {}) {
33
+ let index = 0;
34
+ return new ReadableStream({
35
+ async pull(controller) {
36
+ if (options.signal?.aborted) {
37
+ controller.close();
38
+ return;
39
+ }
40
+ if (index >= chunks.length) {
41
+ controller.close();
42
+ return;
43
+ }
44
+ if (options.pauseAtChunkIndex === index && options.waitForChunk) {
45
+ await options.waitForChunk;
46
+ }
47
+ if ((options.delayMs ?? 0) > 0) {
48
+ await new Promise((resolve) => setTimeout(resolve, options.delayMs));
49
+ }
50
+ if (options.signal?.aborted) {
51
+ controller.close();
52
+ return;
53
+ }
54
+ const chunk = chunks[index];
55
+ if (chunk) {
56
+ controller.enqueue(chunk);
57
+ }
58
+ index += 1;
59
+ },
60
+ });
61
+ }
62
+ function waitFor(ms) {
63
+ return new Promise((resolve) => setTimeout(resolve, ms));
64
+ }
65
+ function decodeRequestPayload(body) {
66
+ // Body is now a connect frame directly (gzip is inside the frame, not outside)
67
+ const decodedFrames = connectFrameDecode(body);
68
+ return decodedFrames[0] ?? Buffer.alloc(0);
69
+ }
70
+ async function collectStreamParts(stream) {
71
+ const reader = stream.getReader();
72
+ const parts = [];
73
+ while (true) {
74
+ const next = await reader.read();
75
+ if (next.done) {
76
+ break;
77
+ }
78
+ parts.push(next.value);
79
+ }
80
+ return parts;
81
+ }
82
+ describe('DevstralLanguageModel doGenerate', () => {
83
+ it('generate request uses connect endpoint and connect+proto gzip headers', async () => {
84
+ const calls = [];
85
+ const requestBodies = [];
86
+ const jwt = makeJwt(4_050_000_000, 'connect-gzip');
87
+ const fakeFetch = async (input, init) => {
88
+ const url = String(input);
89
+ calls.push({ url, init });
90
+ if (url.endsWith('/GetUserJwt')) {
91
+ return new Response(Uint8Array.from(Buffer.from(jwt, 'utf8')), { status: 200 });
92
+ }
93
+ requestBodies.push(bufferFromBody(init?.body));
94
+ return new Response(Uint8Array.from(connectFrameEncode(Buffer.from('ok', 'utf8'))), { status: 200 });
95
+ };
96
+ const model = new DevstralLanguageModel({ apiKey: 'test-api-key', fetch: fakeFetch, baseURL: 'https://windsurf.test' });
97
+ await model.doGenerate({
98
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Use connect route.' }] }],
99
+ });
100
+ const generateCall = calls[1];
101
+ expect(generateCall?.url).toBe('https://windsurf.test/exa.api_server_pb.ApiServerService/GetDevstralStream');
102
+ const headers = new Headers(generateCall?.init?.headers);
103
+ expect(headers.get('content-type')).toBe('application/connect+proto');
104
+ expect(headers.get('connect-protocol-version')).toBe('1');
105
+ expect(headers.get('connect-timeout-ms')).toBe('30000');
106
+ expect(headers.get('connect-accept-encoding')).toBe('gzip');
107
+ expect(headers.get('connect-content-encoding')).toBe('gzip');
108
+ expect(headers.get('accept-encoding')).toBe('identity');
109
+ const body = requestBodies[0];
110
+ expect(body?.readUInt8(0)).toBe(1);
111
+ const strings = extractStrings(decodeRequestPayload(body ?? Buffer.alloc(0)));
112
+ const combined = strings.join('\n');
113
+ expect(combined).toContain('Use connect route.');
114
+ });
115
+ it('generate request returns plain text content', async () => {
116
+ const calls = [];
117
+ const requestBodies = [];
118
+ const jwt = makeJwt(4_100_000_000, 'generate');
119
+ const fakeFetch = async (input, init) => {
120
+ const url = String(input);
121
+ calls.push({ url, init });
122
+ if (url.endsWith('/GetUserJwt')) {
123
+ return new Response(Uint8Array.from(Buffer.from(`prefix:${jwt}:suffix`, 'utf8')), { status: 200 });
124
+ }
125
+ requestBodies.push(bufferFromBody(init?.body));
126
+ return new Response(Uint8Array.from(connectFrameEncode(Buffer.from('generated answer', 'utf8'))), {
127
+ status: 200,
128
+ });
129
+ };
130
+ const model = new DevstralLanguageModel({ apiKey: 'test-api-key', fetch: fakeFetch, baseURL: 'https://windsurf.test' });
131
+ expect(model.specificationVersion).toBe('v3');
132
+ expect(model.supportedUrls).toEqual({});
133
+ const result = await model.doGenerate({
134
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Find auth logic.' }] }],
135
+ });
136
+ expect(result.content).toEqual([{ type: 'text', text: 'generated answer' }]);
137
+ expect(result.finishReason).toEqual({ unified: 'stop', raw: 'stop' });
138
+ expect(result.usage).toEqual({
139
+ inputTokens: {
140
+ total: undefined,
141
+ noCache: undefined,
142
+ cacheRead: undefined,
143
+ cacheWrite: undefined,
144
+ },
145
+ outputTokens: {
146
+ total: undefined,
147
+ text: undefined,
148
+ reasoning: undefined,
149
+ },
150
+ });
151
+ expect(calls).toHaveLength(2);
152
+ expect(calls[0]?.url).toBe('https://windsurf.test/exa.auth_pb.AuthService/GetUserJwt');
153
+ expect(calls[1]?.url).toBe('https://windsurf.test/exa.api_server_pb.ApiServerService/GetDevstralStream');
154
+ const strings = extractStrings(decodeRequestPayload(requestBodies[0] ?? Buffer.alloc(0)));
155
+ const combined = strings.join('\n');
156
+ expect(combined).toContain('test-api-key');
157
+ expect(combined).toContain(jwt);
158
+ expect(combined).toContain('Find auth logic.');
159
+ });
160
+ it('generate request with tool markers returns tool-call content parts', async () => {
161
+ const requestBodies = [];
162
+ const jwt = makeJwt(4_200_000_000, 'tools');
163
+ const fakeFetch = async (input, init) => {
164
+ const url = String(input);
165
+ if (url.endsWith('/GetUserJwt')) {
166
+ return new Response(Uint8Array.from(Buffer.from(jwt, 'utf8')), { status: 200 });
167
+ }
168
+ requestBodies.push(bufferFromBody(init?.body));
169
+ const payload = Buffer.from('[TOOL_CALLS]searchRepo[ARGS]{"query":"jwt manager"}', 'utf8');
170
+ return new Response(Uint8Array.from(connectFrameEncode(payload)), { status: 200 });
171
+ };
172
+ const model = new DevstralLanguageModel({ apiKey: 'tools-key', fetch: fakeFetch, baseURL: 'https://windsurf.test' });
173
+ const result = await model.doGenerate({
174
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Inspect jwt manager.' }] }],
175
+ tools: {
176
+ searchRepo: {
177
+ description: 'Search repository files',
178
+ parameters: {
179
+ type: 'object',
180
+ properties: {
181
+ query: { type: 'string' },
182
+ },
183
+ required: ['query'],
184
+ },
185
+ },
186
+ },
187
+ });
188
+ expect(result.content).toEqual([
189
+ {
190
+ type: 'tool-call',
191
+ toolCallType: 'function',
192
+ toolCallId: 'toolcall_1',
193
+ toolName: 'searchRepo',
194
+ args: '{"query":"jwt manager"}',
195
+ },
196
+ ]);
197
+ const strings = extractStrings(decodeRequestPayload(requestBodies[0] ?? Buffer.alloc(0)));
198
+ const combined = strings.join('\n');
199
+ expect(combined).toContain('searchRepo');
200
+ expect(combined).toContain('Search repository files');
201
+ expect(combined).toContain('Inspect jwt manager.');
202
+ expect(combined).toContain('query');
203
+ });
204
+ });
205
+ describe('DevstralLanguageModel doStream', () => {
206
+ it('stream-text resolves before full body arrives and emits parts incrementally', async () => {
207
+ const jwt = makeJwt(4_300_000_000, 'stream-text');
208
+ const firstFrame = connectFrameEncode(Buffer.from('hello ', 'utf8'));
209
+ const secondFrame = connectFrameEncode(Buffer.from('world', 'utf8'));
210
+ let releaseSecondFrame;
211
+ const waitForSecondFrame = new Promise((resolve) => {
212
+ releaseSecondFrame = () => resolve();
213
+ });
214
+ const chunks = [
215
+ Uint8Array.from(firstFrame.subarray(0, 3)),
216
+ Uint8Array.from(firstFrame.subarray(3, 8)),
217
+ Uint8Array.from(firstFrame.subarray(8)),
218
+ Uint8Array.from(secondFrame.subarray(0, 4)),
219
+ Uint8Array.from(secondFrame.subarray(4)),
220
+ ];
221
+ const calls = [];
222
+ const fakeFetch = async (input, init) => {
223
+ const url = String(input);
224
+ calls.push({ url, init });
225
+ if (url.endsWith('/GetUserJwt')) {
226
+ return new Response(Uint8Array.from(Buffer.from(jwt, 'utf8')), { status: 200 });
227
+ }
228
+ return new Response(makeChunkedBody(chunks, {
229
+ pauseAtChunkIndex: 3,
230
+ waitForChunk: waitForSecondFrame,
231
+ }), { status: 200 });
232
+ };
233
+ const model = new DevstralLanguageModel({
234
+ apiKey: 'stream-api-key',
235
+ fetch: fakeFetch,
236
+ baseURL: 'https://windsurf.test',
237
+ });
238
+ const resultPromise = model.doStream({
239
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Stream text.' }] }],
240
+ });
241
+ try {
242
+ const earlyResolution = await Promise.race([
243
+ resultPromise.then(() => 'resolved'),
244
+ waitFor(30).then(() => 'timed-out'),
245
+ ]);
246
+ expect(earlyResolution).toBe('resolved');
247
+ const result = await resultPromise;
248
+ const reader = result.stream.getReader();
249
+ const firstParts = [];
250
+ while (firstParts.length < 4) {
251
+ const next = await reader.read();
252
+ if (next.done) {
253
+ break;
254
+ }
255
+ firstParts.push(next.value);
256
+ if (next.value.type === 'text-delta') {
257
+ break;
258
+ }
259
+ }
260
+ expect(firstParts.map((part) => part.type)).toEqual([
261
+ 'stream-start',
262
+ 'response-metadata',
263
+ 'text-start',
264
+ 'text-delta',
265
+ ]);
266
+ expect(firstParts[3]).toMatchObject({ type: 'text-delta', delta: 'hello ' });
267
+ releaseSecondFrame?.();
268
+ const remainingParts = [];
269
+ while (true) {
270
+ const next = await reader.read();
271
+ if (next.done) {
272
+ break;
273
+ }
274
+ remainingParts.push(next.value);
275
+ }
276
+ const parts = [...firstParts, ...remainingParts];
277
+ expect(parts.map((part) => part.type)).toEqual([
278
+ 'stream-start',
279
+ 'response-metadata',
280
+ 'text-start',
281
+ 'text-delta',
282
+ 'text-delta',
283
+ 'text-end',
284
+ 'finish',
285
+ ]);
286
+ expect(parts[0]).toEqual({ type: 'stream-start', warnings: [] });
287
+ expect(parts[1]).toMatchObject({ type: 'response-metadata' });
288
+ expect(parts[4]).toMatchObject({ type: 'text-delta', delta: 'world' });
289
+ expect(parts[6]).toEqual({
290
+ type: 'finish',
291
+ finishReason: { unified: 'stop', raw: 'stop' },
292
+ usage: {
293
+ inputTokens: {
294
+ total: undefined,
295
+ noCache: undefined,
296
+ cacheRead: undefined,
297
+ cacheWrite: undefined,
298
+ },
299
+ outputTokens: {
300
+ total: undefined,
301
+ text: undefined,
302
+ reasoning: undefined,
303
+ },
304
+ },
305
+ });
306
+ }
307
+ finally {
308
+ releaseSecondFrame?.();
309
+ }
310
+ expect(calls).toHaveLength(2);
311
+ expect(calls[0]?.url).toBe('https://windsurf.test/exa.auth_pb.AuthService/GetUserJwt');
312
+ expect(calls[1]?.url).toBe('https://windsurf.test/exa.api_server_pb.ApiServerService/GetDevstralStream');
313
+ });
314
+ it('stream-tool emits tool-input deltas before final tool-call and finish', async () => {
315
+ const jwt = makeJwt(4_300_000_001, 'stream-tool');
316
+ const toolPayload = Buffer.from('[TOOL_CALLS]searchRepo[ARGS]{"query":"jwt manager"}', 'utf8');
317
+ const frames = Buffer.concat([connectFrameEncode(toolPayload)]);
318
+ const fakeFetch = async (input) => {
319
+ const url = String(input);
320
+ if (url.endsWith('/GetUserJwt')) {
321
+ return new Response(Uint8Array.from(Buffer.from(jwt, 'utf8')), { status: 200 });
322
+ }
323
+ return new Response(makeChunkedBody(chunkBuffer(frames, 6)), { status: 200 });
324
+ };
325
+ const model = new DevstralLanguageModel({
326
+ apiKey: 'stream-tool-key',
327
+ fetch: fakeFetch,
328
+ baseURL: 'https://windsurf.test',
329
+ });
330
+ const result = await model.doStream({
331
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Call tools.' }] }],
332
+ tools: {
333
+ searchRepo: {
334
+ description: 'Search repository files',
335
+ parameters: {
336
+ type: 'object',
337
+ properties: {
338
+ query: { type: 'string' },
339
+ },
340
+ required: ['query'],
341
+ },
342
+ },
343
+ },
344
+ });
345
+ const parts = await collectStreamParts(result.stream);
346
+ expect(parts.map((part) => part.type)).toEqual([
347
+ 'stream-start',
348
+ 'response-metadata',
349
+ 'tool-input-start',
350
+ 'tool-input-delta',
351
+ 'tool-input-end',
352
+ 'tool-call',
353
+ 'finish',
354
+ ]);
355
+ expect(parts[3]).toMatchObject({ type: 'tool-input-delta', delta: '{"query":"jwt manager"}' });
356
+ expect(parts[5]).toEqual({
357
+ type: 'tool-call',
358
+ toolCallType: 'function',
359
+ toolCallId: 'toolcall_1',
360
+ toolName: 'searchRepo',
361
+ args: '{"query":"jwt manager"}',
362
+ });
363
+ });
364
+ it('abort stops stream mid-response', async () => {
365
+ const controller = new AbortController();
366
+ const jwt = makeJwt(4_300_000_002, 'abort');
367
+ const frames = Buffer.concat([
368
+ connectFrameEncode(Buffer.from('first ', 'utf8')),
369
+ connectFrameEncode(Buffer.from('second ', 'utf8')),
370
+ connectFrameEncode(Buffer.from('third', 'utf8')),
371
+ ]);
372
+ const fakeFetch = async (input, init) => {
373
+ const url = String(input);
374
+ if (url.endsWith('/GetUserJwt')) {
375
+ return new Response(Uint8Array.from(Buffer.from(jwt, 'utf8')), { status: 200 });
376
+ }
377
+ return new Response(makeChunkedBody(chunkBuffer(frames, 5), { delayMs: 10, signal: init?.signal ?? undefined }), {
378
+ status: 200,
379
+ });
380
+ };
381
+ const model = new DevstralLanguageModel({
382
+ apiKey: 'stream-abort-key',
383
+ fetch: fakeFetch,
384
+ baseURL: 'https://windsurf.test',
385
+ });
386
+ const result = await model.doStream({
387
+ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Abort stream.' }] }],
388
+ abortSignal: controller.signal,
389
+ });
390
+ const reader = result.stream.getReader();
391
+ const seen = [];
392
+ while (true) {
393
+ const next = await reader.read();
394
+ if (next.done) {
395
+ break;
396
+ }
397
+ const part = next.value;
398
+ seen.push(part);
399
+ if (part.type === 'text-delta') {
400
+ controller.abort();
401
+ }
402
+ }
403
+ const types = seen.map((part) => part.type);
404
+ expect(types).toContain('text-delta');
405
+ expect(types.filter((type) => type === 'text-delta')).toHaveLength(1);
406
+ expect(types).not.toContain('finish');
407
+ });
408
+ });
@@ -0,0 +1,36 @@
1
+ import { gunzipSync, gzipSync } from 'node:zlib';
2
+ const FRAME_HEADER_SIZE = 5;
3
+ const FLAG_COMPRESSED = 1;
4
+ /**
5
+ * Encode a protobuf payload into a Connect-RPC frame.
6
+ *
7
+ * @param payload - The raw protobuf bytes to encode
8
+ * @param compress - Whether to gzip-compress the payload (default: true)
9
+ * When true, the payload is gzipped and flags=1 is set
10
+ * @returns Buffer containing: [flags byte][4-byte length][payload (gzipped if compress=true)]
11
+ */
12
+ export function connectFrameEncode(payload, compress = true) {
13
+ const payloadBytes = compress ? gzipSync(payload) : payload;
14
+ const frame = Buffer.allocUnsafe(FRAME_HEADER_SIZE + payloadBytes.length);
15
+ frame.writeUInt8(compress ? FLAG_COMPRESSED : 0, 0);
16
+ frame.writeUInt32BE(payloadBytes.length, 1);
17
+ payloadBytes.copy(frame, FRAME_HEADER_SIZE);
18
+ return frame;
19
+ }
20
+ export function connectFrameDecode(buffer) {
21
+ const decoded = [];
22
+ let offset = 0;
23
+ while (offset + FRAME_HEADER_SIZE <= buffer.length) {
24
+ const flags = buffer.readUInt8(offset);
25
+ const payloadLength = buffer.readUInt32BE(offset + 1);
26
+ const payloadStart = offset + FRAME_HEADER_SIZE;
27
+ const payloadEnd = payloadStart + payloadLength;
28
+ if (payloadEnd > buffer.length) {
29
+ break;
30
+ }
31
+ const payload = buffer.subarray(payloadStart, payloadEnd);
32
+ decoded.push(flags === FLAG_COMPRESSED ? gunzipSync(payload) : Buffer.from(payload));
33
+ offset = payloadEnd;
34
+ }
35
+ return decoded;
36
+ }
@@ -0,0 +1,34 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { connectFrameDecode, connectFrameEncode } from './connect-frame.js';
5
+ const fixtureSimpleFrame = readFileSync(join(process.cwd(), 'test/fixtures/connect/frame-simple.bin'));
6
+ const fixtureGzipFrame = readFileSync(join(process.cwd(), 'test/fixtures/connect/frame-gzip.bin'));
7
+ const fixtureMultiFrame = readFileSync(join(process.cwd(), 'test/fixtures/connect/frame-multi.bin'));
8
+ const payloadHello = Buffer.from('CONNECT\x01hello', 'utf8');
9
+ const payloadWorld = Buffer.from('CONNECT\x02world', 'utf8');
10
+ describe('connectFrameEncode', () => {
11
+ it('roundtrip encodes and decodes uncompressed payload', () => {
12
+ const encoded = connectFrameEncode(payloadHello, false);
13
+ expect(encoded.readUInt8(0)).toBe(0);
14
+ expect(encoded.readUInt32BE(1)).toBe(payloadHello.length);
15
+ expect(encoded.subarray(5)).toEqual(payloadHello);
16
+ expect(connectFrameDecode(encoded)).toEqual([payloadHello]);
17
+ });
18
+ it('gzip roundtrip uses compressed frame and decodes back to source payload', () => {
19
+ const encoded = connectFrameEncode(payloadWorld, true);
20
+ expect(encoded.readUInt8(0)).toBe(1);
21
+ expect(connectFrameDecode(encoded)).toEqual([payloadWorld]);
22
+ });
23
+ });
24
+ describe('connectFrameDecode fixtures', () => {
25
+ it('decodes frame-simple fixture payload', () => {
26
+ expect(connectFrameDecode(fixtureSimpleFrame)).toEqual([payloadHello]);
27
+ });
28
+ it('decodes gzip fixture payload', () => {
29
+ expect(connectFrameDecode(fixtureGzipFrame)).toEqual([payloadWorld]);
30
+ });
31
+ it('decodes multiple concatenated frames in order', () => {
32
+ expect(connectFrameDecode(fixtureMultiFrame)).toEqual([payloadHello, payloadHello]);
33
+ });
34
+ });
@@ -0,0 +1,108 @@
1
+ function encodeVarint(value) {
2
+ if (!Number.isInteger(value) || value < 0) {
3
+ throw new Error(`Varint value must be a non-negative integer: ${value}`);
4
+ }
5
+ const bytes = [];
6
+ let remaining = value;
7
+ while (remaining >= 0x80) {
8
+ bytes.push((remaining & 0x7f) | 0x80);
9
+ remaining = Math.floor(remaining / 0x80);
10
+ }
11
+ bytes.push(remaining);
12
+ return Buffer.from(bytes);
13
+ }
14
+ function decodeUtf8IfValid(value) {
15
+ if (value.length === 0) {
16
+ return '';
17
+ }
18
+ const decoded = value.toString('utf8');
19
+ const encoded = Buffer.from(decoded, 'utf8');
20
+ if (!encoded.equals(value)) {
21
+ return null;
22
+ }
23
+ return decoded;
24
+ }
25
+ export class ProtobufEncoder {
26
+ chunks = [];
27
+ writeVarint(field, value) {
28
+ this.writeTag(field, 0);
29
+ this.chunks.push(encodeVarint(value));
30
+ }
31
+ writeString(field, value) {
32
+ this.writeBytes(field, Buffer.from(value, 'utf8'));
33
+ }
34
+ writeBytes(field, data) {
35
+ this.writeTag(field, 2);
36
+ this.chunks.push(encodeVarint(data.length));
37
+ this.chunks.push(data);
38
+ }
39
+ writeMessage(field, encoder) {
40
+ this.writeBytes(field, encoder.toBuffer());
41
+ }
42
+ toBuffer() {
43
+ return Buffer.concat(this.chunks);
44
+ }
45
+ writeTag(field, wireType) {
46
+ if (!Number.isInteger(field) || field <= 0) {
47
+ throw new Error(`Field number must be a positive integer: ${field}`);
48
+ }
49
+ this.chunks.push(encodeVarint((field << 3) | wireType));
50
+ }
51
+ }
52
+ export function decodeVarint(buffer, offset) {
53
+ let value = 0;
54
+ let shift = 0;
55
+ let cursor = offset;
56
+ while (cursor < buffer.length) {
57
+ const byte = buffer.readUInt8(cursor);
58
+ value += (byte & 0x7f) * 2 ** shift;
59
+ cursor += 1;
60
+ if ((byte & 0x80) === 0) {
61
+ return [value, cursor];
62
+ }
63
+ shift += 7;
64
+ }
65
+ throw new Error('Unexpected end of buffer while decoding varint');
66
+ }
67
+ export function extractStrings(buffer) {
68
+ const result = [];
69
+ let offset = 0;
70
+ while (offset < buffer.length) {
71
+ let key;
72
+ [key, offset] = decodeVarint(buffer, offset);
73
+ const wireType = key & 0x07;
74
+ if (wireType === 0) {
75
+ [, offset] = decodeVarint(buffer, offset);
76
+ continue;
77
+ }
78
+ if (wireType === 1) {
79
+ offset += 8;
80
+ continue;
81
+ }
82
+ if (wireType === 2) {
83
+ let length;
84
+ [length, offset] = decodeVarint(buffer, offset);
85
+ const end = offset + length;
86
+ if (end > buffer.length) {
87
+ break;
88
+ }
89
+ const slice = buffer.subarray(offset, end);
90
+ const candidate = decodeUtf8IfValid(slice);
91
+ if (candidate !== null) {
92
+ result.push(candidate);
93
+ }
94
+ else {
95
+ const nested = extractStrings(slice);
96
+ result.push(...nested);
97
+ }
98
+ offset = end;
99
+ continue;
100
+ }
101
+ if (wireType === 5) {
102
+ offset += 4;
103
+ continue;
104
+ }
105
+ break;
106
+ }
107
+ return result;
108
+ }
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { ProtobufEncoder, decodeVarint, extractStrings } from './protobuf.js';
5
+ const fixtureMsg1 = readFileSync(join(process.cwd(), 'test/fixtures/protobuf/msg-1.bin'));
6
+ const fixtureMsg2 = readFileSync(join(process.cwd(), 'test/fixtures/protobuf/msg-2.bin'));
7
+ describe('ProtobufEncoder', () => {
8
+ it('matches fixture bytes for synthetic protobuf messages', () => {
9
+ const encoder1 = new ProtobufEncoder();
10
+ encoder1.writeVarint(1, 150);
11
+ encoder1.writeString(2, 'alpha');
12
+ const encoder2 = new ProtobufEncoder();
13
+ encoder2.writeVarint(1, 42);
14
+ encoder2.writeString(2, 'beta');
15
+ expect(encoder1.toBuffer()).toEqual(fixtureMsg1);
16
+ expect(encoder2.toBuffer()).toEqual(fixtureMsg2);
17
+ });
18
+ it('writes bytes and nested messages', () => {
19
+ const nested = new ProtobufEncoder();
20
+ nested.writeString(1, 'xy');
21
+ const encoder = new ProtobufEncoder();
22
+ encoder.writeBytes(3, Buffer.from([0xaa, 0xbb]));
23
+ encoder.writeMessage(4, nested);
24
+ expect(Array.from(encoder.toBuffer())).toEqual([
25
+ 0x1a,
26
+ 0x02,
27
+ 0xaa,
28
+ 0xbb,
29
+ 0x22,
30
+ 0x04,
31
+ 0x0a,
32
+ 0x02,
33
+ 0x78,
34
+ 0x79,
35
+ ]);
36
+ });
37
+ });
38
+ describe('decodeVarint', () => {
39
+ it('decodes varint value and returns new offset', () => {
40
+ const buffer = Buffer.from([0x96, 0x01, 0x2a]);
41
+ const [first, firstOffset] = decodeVarint(buffer, 0);
42
+ const [second, secondOffset] = decodeVarint(buffer, firstOffset);
43
+ expect(first).toBe(150);
44
+ expect(firstOffset).toBe(2);
45
+ expect(second).toBe(42);
46
+ expect(secondOffset).toBe(3);
47
+ });
48
+ });
49
+ describe('extractStrings', () => {
50
+ it('extractStrings returns all embedded strings from fixtures', () => {
51
+ const combined = Buffer.concat([fixtureMsg1, fixtureMsg2]);
52
+ expect(extractStrings(fixtureMsg1)).toEqual(['alpha']);
53
+ expect(extractStrings(fixtureMsg2)).toEqual(['beta']);
54
+ expect(extractStrings(combined)).toEqual(['alpha', 'beta']);
55
+ });
56
+ });
@@ -0,0 +1,9 @@
1
+ import { DevstralLanguageModel } from './model/devstral-language-model.js';
2
+ export function createWindsurfProvider(options = {}) {
3
+ return (modelId) => {
4
+ const opts = { ...options, modelId };
5
+ return new DevstralLanguageModel(opts);
6
+ };
7
+ }
8
+ export const windsurf = createWindsurfProvider();
9
+ export default windsurf;