@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.
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/dist/auth/api-key.d.ts +2 -0
- package/dist/auth/api-key.test.d.ts +1 -0
- package/dist/auth/jwt-manager.d.ts +18 -0
- package/dist/auth/jwt-manager.test.d.ts +1 -0
- package/dist/cjs/auth/api-key.js +10 -0
- package/dist/cjs/auth/api-key.test.js +29 -0
- package/dist/cjs/auth/jwt-manager.js +94 -0
- package/dist/cjs/auth/jwt-manager.test.js +99 -0
- package/dist/cjs/conversion/prompt-converter.js +57 -0
- package/dist/cjs/conversion/prompt-converter.test.js +95 -0
- package/dist/cjs/conversion/response-converter.js +233 -0
- package/dist/cjs/conversion/response-converter.test.js +65 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/model/devstral-language-model.js +399 -0
- package/dist/cjs/model/devstral-language-model.test.js +410 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/protocol/connect-frame.js +40 -0
- package/dist/cjs/protocol/connect-frame.test.js +36 -0
- package/dist/cjs/protocol/protobuf.js +114 -0
- package/dist/cjs/protocol/protobuf.test.js +58 -0
- package/dist/cjs/provider.js +13 -0
- package/dist/cjs/provider.test.js +61 -0
- package/dist/cjs/transport/http.js +83 -0
- package/dist/cjs/transport/http.test.js +196 -0
- package/dist/cjs/types/index.js +2 -0
- package/dist/conversion/prompt-converter.d.ts +49 -0
- package/dist/conversion/prompt-converter.test.d.ts +1 -0
- package/dist/conversion/response-converter.d.ts +12 -0
- package/dist/conversion/response-converter.test.d.ts +1 -0
- package/dist/esm/auth/api-key.js +7 -0
- package/dist/esm/auth/api-key.test.js +27 -0
- package/dist/esm/auth/jwt-manager.js +90 -0
- package/dist/esm/auth/jwt-manager.test.js +97 -0
- package/dist/esm/conversion/prompt-converter.js +54 -0
- package/dist/esm/conversion/prompt-converter.test.js +93 -0
- package/dist/esm/conversion/response-converter.js +230 -0
- package/dist/esm/conversion/response-converter.test.js +63 -0
- package/dist/esm/dist/cjs/index.js +3 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/model/devstral-language-model.js +395 -0
- package/dist/esm/model/devstral-language-model.test.js +408 -0
- package/dist/esm/protocol/connect-frame.js +36 -0
- package/dist/esm/protocol/connect-frame.test.js +34 -0
- package/dist/esm/protocol/protobuf.js +108 -0
- package/dist/esm/protocol/protobuf.test.js +56 -0
- package/dist/esm/provider.js +9 -0
- package/dist/esm/provider.test.js +59 -0
- package/dist/esm/scripts/postbuild.js +10 -0
- package/dist/esm/src/index.js +1 -0
- package/dist/esm/transport/http.js +78 -0
- package/dist/esm/transport/http.test.js +194 -0
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/vitest.config.js +6 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/model/devstral-language-model.d.ts +118 -0
- package/dist/model/devstral-language-model.test.d.ts +1 -0
- package/dist/protocol/connect-frame.d.ts +10 -0
- package/dist/protocol/connect-frame.test.d.ts +1 -0
- package/dist/protocol/protobuf.d.ts +11 -0
- package/dist/protocol/protobuf.test.d.ts +1 -0
- package/dist/provider.d.ts +5 -0
- package/dist/provider.test.d.ts +1 -0
- package/dist/transport/http.d.ts +22 -0
- package/dist/transport/http.test.d.ts +1 -0
- package/dist/types/index.d.ts +37 -0
- package/package.json +51 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { arch, cpus, hostname, platform, release, totalmem, version as osVersion } from 'node:os';
|
|
3
|
+
import { resolveApiKey } from '../auth/api-key.js';
|
|
4
|
+
import { JwtManager } from '../auth/jwt-manager.js';
|
|
5
|
+
import { convertPrompt } from '../conversion/prompt-converter.js';
|
|
6
|
+
import { convertResponse } from '../conversion/response-converter.js';
|
|
7
|
+
import { connectFrameDecode, connectFrameEncode } from '../protocol/connect-frame.js';
|
|
8
|
+
import { ProtobufEncoder } from '../protocol/protobuf.js';
|
|
9
|
+
import { DevstralTransport } from '../transport/http.js';
|
|
10
|
+
const DEFAULT_BASE_URL = 'https://server.self-serve.windsurf.com';
|
|
11
|
+
const DEFAULT_MODEL_ID = 'MODEL_SWE_1_6_FAST';
|
|
12
|
+
const API_SERVICE_PATH = '/exa.api_server_pb.ApiServerService';
|
|
13
|
+
const DEVSTRAL_STREAM_PATH = '/GetDevstralStream';
|
|
14
|
+
const CONNECT_TIMEOUT_MS = '30000';
|
|
15
|
+
const WS_APP = 'windsurf';
|
|
16
|
+
const WS_APP_VER = process.env.WS_APP_VER ?? '1.48.2';
|
|
17
|
+
const WS_LS_VER = process.env.WS_LS_VER ?? '1.9544.35';
|
|
18
|
+
const SENTRY_PUBLIC_KEY = 'b813f73488da69eedec534dba1029111';
|
|
19
|
+
const CONNECT_USER_AGENT = 'connect-go/1.18.1 (go1.25.5)';
|
|
20
|
+
export class DevstralLanguageModel {
|
|
21
|
+
specificationVersion = 'v3';
|
|
22
|
+
provider = 'windsurf';
|
|
23
|
+
modelId;
|
|
24
|
+
supportedUrls = {};
|
|
25
|
+
apiKey;
|
|
26
|
+
baseURL;
|
|
27
|
+
headers;
|
|
28
|
+
transport;
|
|
29
|
+
jwtManager;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.apiKey = resolveApiKey(options);
|
|
32
|
+
this.baseURL = trimTrailingSlash(options.baseURL ?? DEFAULT_BASE_URL);
|
|
33
|
+
this.modelId = options.modelId ?? DEFAULT_MODEL_ID;
|
|
34
|
+
this.headers = options.headers ?? {};
|
|
35
|
+
this.transport = options.transport ?? new DevstralTransport({ fetch: options.fetch });
|
|
36
|
+
this.jwtManager =
|
|
37
|
+
options.jwtManager ??
|
|
38
|
+
new JwtManager({
|
|
39
|
+
fetch: options.fetch,
|
|
40
|
+
authBase: `${this.baseURL}/exa.auth_pb.AuthService`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async doGenerate(options) {
|
|
44
|
+
const jwt = await this.jwtManager.getJwt(this.apiKey);
|
|
45
|
+
const messages = convertPrompt(options.prompt);
|
|
46
|
+
const requestPayload = buildGenerateRequest({
|
|
47
|
+
apiKey: this.apiKey,
|
|
48
|
+
jwt,
|
|
49
|
+
messages,
|
|
50
|
+
tools: options.tools,
|
|
51
|
+
});
|
|
52
|
+
// connectFrameEncode now handles gzip compression internally (default compress=true)
|
|
53
|
+
const requestFrame = connectFrameEncode(requestPayload);
|
|
54
|
+
const headers = createConnectHeaders(this.headers);
|
|
55
|
+
const responseFrame = await this.transport.postUnary(`${this.baseURL}${API_SERVICE_PATH}${DEVSTRAL_STREAM_PATH}`, requestFrame, headers);
|
|
56
|
+
const responsePayloads = connectFrameDecode(responseFrame);
|
|
57
|
+
const payloads = responsePayloads.length > 0 ? responsePayloads : [responseFrame];
|
|
58
|
+
const content = payloads.flatMap((payload) => toV3Content(convertResponse(payload)));
|
|
59
|
+
return {
|
|
60
|
+
content,
|
|
61
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
62
|
+
usage: emptyUsage(),
|
|
63
|
+
warnings: [],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async doStream(options) {
|
|
67
|
+
const jwt = await this.jwtManager.getJwt(this.apiKey);
|
|
68
|
+
const messages = convertPrompt(options.prompt);
|
|
69
|
+
const requestPayload = buildGenerateRequest({
|
|
70
|
+
apiKey: this.apiKey,
|
|
71
|
+
jwt,
|
|
72
|
+
messages,
|
|
73
|
+
tools: options.tools,
|
|
74
|
+
});
|
|
75
|
+
// connectFrameEncode now handles gzip compression internally (default compress=true)
|
|
76
|
+
const requestFrame = connectFrameEncode(requestPayload);
|
|
77
|
+
const headers = createConnectHeaders(this.headers);
|
|
78
|
+
const byteStream = await this.transport.postStreaming(`${this.baseURL}${API_SERVICE_PATH}${DEVSTRAL_STREAM_PATH}`, requestFrame, headers, options.abortSignal);
|
|
79
|
+
return {
|
|
80
|
+
stream: new ReadableStream({
|
|
81
|
+
start: async (controller) => {
|
|
82
|
+
const reader = byteStream.getReader();
|
|
83
|
+
const abortHandler = () => {
|
|
84
|
+
safeClose(controller);
|
|
85
|
+
};
|
|
86
|
+
options.abortSignal?.addEventListener('abort', abortHandler, { once: true });
|
|
87
|
+
let textSegmentId = null;
|
|
88
|
+
let textSegmentCounter = 0;
|
|
89
|
+
let pending = Buffer.alloc(0);
|
|
90
|
+
const closeTextSegment = () => {
|
|
91
|
+
if (textSegmentId == null) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
safeEnqueue(controller, {
|
|
95
|
+
type: 'text-end',
|
|
96
|
+
id: textSegmentId,
|
|
97
|
+
});
|
|
98
|
+
textSegmentId = null;
|
|
99
|
+
};
|
|
100
|
+
try {
|
|
101
|
+
safeEnqueue(controller, { type: 'stream-start', warnings: [] });
|
|
102
|
+
safeEnqueue(controller, { type: 'response-metadata', modelId: this.modelId });
|
|
103
|
+
while (!isAborted(options.abortSignal)) {
|
|
104
|
+
const next = await reader.read();
|
|
105
|
+
if (next.done) {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
if (next.value.byteLength === 0) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
pending = Buffer.concat([pending, Buffer.from(next.value)]);
|
|
112
|
+
while (true) {
|
|
113
|
+
const frameResult = readNextConnectFrame(pending);
|
|
114
|
+
if (frameResult == null) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
pending = frameResult.rest;
|
|
118
|
+
const contentParts = toV3Content(convertResponse(frameResult.payload));
|
|
119
|
+
for (const part of contentParts) {
|
|
120
|
+
if (isAborted(options.abortSignal)) {
|
|
121
|
+
safeClose(controller);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (part.type === 'text') {
|
|
125
|
+
if (textSegmentId == null) {
|
|
126
|
+
textSegmentCounter += 1;
|
|
127
|
+
textSegmentId = `text_${textSegmentCounter}`;
|
|
128
|
+
safeEnqueue(controller, { type: 'text-start', id: textSegmentId });
|
|
129
|
+
}
|
|
130
|
+
safeEnqueue(controller, {
|
|
131
|
+
type: 'text-delta',
|
|
132
|
+
id: textSegmentId,
|
|
133
|
+
delta: part.text,
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
closeTextSegment();
|
|
138
|
+
safeEnqueue(controller, {
|
|
139
|
+
type: 'tool-input-start',
|
|
140
|
+
toolCallId: part.toolCallId,
|
|
141
|
+
toolName: part.toolName,
|
|
142
|
+
});
|
|
143
|
+
safeEnqueue(controller, {
|
|
144
|
+
type: 'tool-input-delta',
|
|
145
|
+
toolCallId: part.toolCallId,
|
|
146
|
+
delta: part.args,
|
|
147
|
+
});
|
|
148
|
+
safeEnqueue(controller, {
|
|
149
|
+
type: 'tool-input-end',
|
|
150
|
+
toolCallId: part.toolCallId,
|
|
151
|
+
});
|
|
152
|
+
safeEnqueue(controller, part);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (isAborted(options.abortSignal)) {
|
|
157
|
+
safeClose(controller);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
closeTextSegment();
|
|
161
|
+
safeEnqueue(controller, {
|
|
162
|
+
type: 'finish',
|
|
163
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
164
|
+
usage: emptyUsage(),
|
|
165
|
+
});
|
|
166
|
+
safeClose(controller);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (!isAborted(options.abortSignal)) {
|
|
170
|
+
closeTextSegment();
|
|
171
|
+
safeEnqueue(controller, {
|
|
172
|
+
type: 'error',
|
|
173
|
+
error,
|
|
174
|
+
});
|
|
175
|
+
safeEnqueue(controller, {
|
|
176
|
+
type: 'finish',
|
|
177
|
+
finishReason: { unified: 'stop', raw: 'stop' },
|
|
178
|
+
usage: emptyUsage(),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
safeClose(controller);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
options.abortSignal?.removeEventListener('abort', abortHandler);
|
|
185
|
+
reader.releaseLock();
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const CONNECT_FRAME_HEADER_BYTES = 5;
|
|
193
|
+
function readNextConnectFrame(buffer) {
|
|
194
|
+
if (buffer.length < CONNECT_FRAME_HEADER_BYTES) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const payloadLength = buffer.readUInt32BE(1);
|
|
198
|
+
const frameLength = CONNECT_FRAME_HEADER_BYTES + payloadLength;
|
|
199
|
+
if (buffer.length < frameLength) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const frame = buffer.subarray(0, frameLength);
|
|
203
|
+
const decoded = connectFrameDecode(frame);
|
|
204
|
+
return {
|
|
205
|
+
payload: decoded[0] ?? Buffer.alloc(0),
|
|
206
|
+
rest: buffer.subarray(frameLength),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function safeEnqueue(controller, part) {
|
|
210
|
+
if (isControllerClosed(controller)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
controller.enqueue(part);
|
|
214
|
+
}
|
|
215
|
+
function safeClose(controller) {
|
|
216
|
+
if (isControllerClosed(controller)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
controller.close();
|
|
220
|
+
}
|
|
221
|
+
function isControllerClosed(controller) {
|
|
222
|
+
try {
|
|
223
|
+
controller.desiredSize;
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function isAborted(signal) {
|
|
231
|
+
return signal?.aborted === true;
|
|
232
|
+
}
|
|
233
|
+
function emptyUsage() {
|
|
234
|
+
return {
|
|
235
|
+
inputTokens: {
|
|
236
|
+
total: undefined,
|
|
237
|
+
noCache: undefined,
|
|
238
|
+
cacheRead: undefined,
|
|
239
|
+
cacheWrite: undefined,
|
|
240
|
+
},
|
|
241
|
+
outputTokens: {
|
|
242
|
+
total: undefined,
|
|
243
|
+
text: undefined,
|
|
244
|
+
reasoning: undefined,
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function toV3Content(parts) {
|
|
249
|
+
return parts.map((part) => {
|
|
250
|
+
if (part.type === 'tool-call') {
|
|
251
|
+
return {
|
|
252
|
+
type: 'tool-call',
|
|
253
|
+
toolCallType: 'function',
|
|
254
|
+
toolCallId: part.toolCallId,
|
|
255
|
+
toolName: part.toolName,
|
|
256
|
+
args: JSON.stringify(part.args),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return part;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function buildGenerateRequest(input) {
|
|
263
|
+
const request = new ProtobufEncoder();
|
|
264
|
+
request.writeMessage(1, buildMetadata(input.apiKey, input.jwt));
|
|
265
|
+
for (const message of input.messages) {
|
|
266
|
+
request.writeMessage(2, buildMessage(message));
|
|
267
|
+
}
|
|
268
|
+
// Tools are sent as a single JSON string at field 3 (not repeated messages)
|
|
269
|
+
if (input.tools && Object.keys(input.tools).length > 0) {
|
|
270
|
+
const toolsArray = Object.entries(input.tools).map(([name, tool]) => ({
|
|
271
|
+
type: 'function',
|
|
272
|
+
function: {
|
|
273
|
+
name,
|
|
274
|
+
description: tool.description ?? '',
|
|
275
|
+
parameters: tool.parameters ?? {},
|
|
276
|
+
},
|
|
277
|
+
}));
|
|
278
|
+
request.writeString(3, JSON.stringify(toolsArray));
|
|
279
|
+
}
|
|
280
|
+
return request.toBuffer();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Build metadata protobuf with correct field numbers matching the reference implementation.
|
|
284
|
+
*
|
|
285
|
+
* Field mapping:
|
|
286
|
+
* 1: WS_APP ("windsurf")
|
|
287
|
+
* 2: WS_APP_VER ("1.48.2")
|
|
288
|
+
* 3: apiKey
|
|
289
|
+
* 4: locale ("zh-cn")
|
|
290
|
+
* 5: systemInfo JSON
|
|
291
|
+
* 7: WS_LS_VER ("1.9544.35")
|
|
292
|
+
* 8: cpuInfo JSON
|
|
293
|
+
* 12: WS_APP ("windsurf")
|
|
294
|
+
* 21: jwt
|
|
295
|
+
* 30: bytes [0x00, 0x01]
|
|
296
|
+
*/
|
|
297
|
+
function buildMetadata(apiKey, jwt) {
|
|
298
|
+
const plat = platform();
|
|
299
|
+
const systemInfo = {
|
|
300
|
+
Os: plat,
|
|
301
|
+
Arch: arch(),
|
|
302
|
+
Release: release(),
|
|
303
|
+
Version: osVersion(),
|
|
304
|
+
Machine: arch(),
|
|
305
|
+
Nodename: hostname(),
|
|
306
|
+
Sysname: plat === 'darwin' ? 'Darwin' : plat === 'win32' ? 'Windows_NT' : 'Linux',
|
|
307
|
+
ProductVersion: '',
|
|
308
|
+
};
|
|
309
|
+
const cpuList = cpus();
|
|
310
|
+
const ncpu = cpuList.length || 4;
|
|
311
|
+
const cpuInfo = {
|
|
312
|
+
NumSockets: 1,
|
|
313
|
+
NumCores: ncpu,
|
|
314
|
+
NumThreads: ncpu,
|
|
315
|
+
VendorID: '',
|
|
316
|
+
Family: '0',
|
|
317
|
+
Model: '0',
|
|
318
|
+
ModelName: cpuList[0]?.model || 'Unknown',
|
|
319
|
+
Memory: totalmem(),
|
|
320
|
+
};
|
|
321
|
+
const metadata = new ProtobufEncoder();
|
|
322
|
+
metadata.writeString(1, WS_APP);
|
|
323
|
+
metadata.writeString(2, WS_APP_VER);
|
|
324
|
+
metadata.writeString(3, apiKey);
|
|
325
|
+
metadata.writeString(4, 'zh-cn');
|
|
326
|
+
metadata.writeString(5, JSON.stringify(systemInfo));
|
|
327
|
+
metadata.writeString(7, WS_LS_VER);
|
|
328
|
+
metadata.writeString(8, JSON.stringify(cpuInfo));
|
|
329
|
+
metadata.writeString(12, WS_APP);
|
|
330
|
+
metadata.writeString(21, jwt);
|
|
331
|
+
metadata.writeBytes(30, Buffer.from([0x00, 0x01]));
|
|
332
|
+
return metadata;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Build message protobuf with correct field numbers matching the reference implementation.
|
|
336
|
+
*
|
|
337
|
+
* Field mapping:
|
|
338
|
+
* 2: role (1=user, 2=assistant, 4=tool_result, 5=system)
|
|
339
|
+
* 3: content
|
|
340
|
+
* 6: toolCall (nested message with fields 1=callId, 2=name, 3=argsJson)
|
|
341
|
+
* 7: refCallId
|
|
342
|
+
*/
|
|
343
|
+
function buildMessage(message) {
|
|
344
|
+
const encoded = new ProtobufEncoder();
|
|
345
|
+
// Role at field 2 (not 1)
|
|
346
|
+
encoded.writeVarint(2, message.role);
|
|
347
|
+
// Content at field 3 (not 2)
|
|
348
|
+
encoded.writeString(3, message.content);
|
|
349
|
+
const toolCallId = getMetadataString(message, 'toolCallId');
|
|
350
|
+
const toolName = getMetadataString(message, 'toolName');
|
|
351
|
+
const toolArgsJson = getMetadataString(message, 'toolArgsJson');
|
|
352
|
+
if (toolCallId && toolName && toolArgsJson) {
|
|
353
|
+
// Tool call as nested message at field 6
|
|
354
|
+
const toolCall = new ProtobufEncoder();
|
|
355
|
+
toolCall.writeString(1, toolCallId);
|
|
356
|
+
toolCall.writeString(2, toolName);
|
|
357
|
+
toolCall.writeString(3, toolArgsJson);
|
|
358
|
+
encoded.writeMessage(6, toolCall);
|
|
359
|
+
}
|
|
360
|
+
const refCallId = getMetadataString(message, 'refCallId');
|
|
361
|
+
if (refCallId) {
|
|
362
|
+
encoded.writeString(7, refCallId);
|
|
363
|
+
}
|
|
364
|
+
return encoded;
|
|
365
|
+
}
|
|
366
|
+
function getMetadataString(message, key) {
|
|
367
|
+
const value = message.metadata?.[key];
|
|
368
|
+
return typeof value === 'string' ? value : null;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Create Connect-RPC headers matching the reference implementation.
|
|
372
|
+
* Note: Authorization header is NOT included - JWT is in metadata field 21.
|
|
373
|
+
*/
|
|
374
|
+
function createConnectHeaders(headers) {
|
|
375
|
+
const traceId = randomUUID().replace(/-/g, '');
|
|
376
|
+
const spanId = randomUUID().replace(/-/g, '').slice(0, 16);
|
|
377
|
+
return new Headers({
|
|
378
|
+
...headers,
|
|
379
|
+
'Content-Type': 'application/connect+proto',
|
|
380
|
+
'Connect-Protocol-Version': '1',
|
|
381
|
+
'Connect-Timeout-Ms': CONNECT_TIMEOUT_MS,
|
|
382
|
+
'Connect-Accept-Encoding': 'gzip',
|
|
383
|
+
'Connect-Content-Encoding': 'gzip',
|
|
384
|
+
'Accept-Encoding': 'identity',
|
|
385
|
+
'User-Agent': CONNECT_USER_AGENT,
|
|
386
|
+
Baggage: `sentry-release=language-server-windsurf@${WS_LS_VER},` +
|
|
387
|
+
'sentry-environment=stable,sentry-sampled=false,' +
|
|
388
|
+
`sentry-trace_id=${traceId},` +
|
|
389
|
+
`sentry-public_key=${SENTRY_PUBLIC_KEY}`,
|
|
390
|
+
'Sentry-Trace': `${traceId}-${spanId}-0`,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
function trimTrailingSlash(value) {
|
|
394
|
+
return value.endsWith('/') ? value.slice(0, -1) : value;
|
|
395
|
+
}
|