@magpie-community/opencode-zen-free-auth 0.1.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.
@@ -0,0 +1,542 @@
1
+ const MAX_BYTES = 32 * 1024 * 1024;
2
+ const encoder = new TextEncoder();
3
+
4
+ class GuardError extends Error {}
5
+
6
+ function headersFor(response) {
7
+ const headers = new Headers(response.headers);
8
+ headers.delete("content-length");
9
+ headers.delete("content-encoding");
10
+ return headers;
11
+ }
12
+
13
+ function failure(protocol, message, stream = false) {
14
+ if (protocol === "anthropic") return { type: "error", error: { type: "api_error", message } };
15
+ if (protocol === "responses") {
16
+ return stream
17
+ ? { type: "error", code: "undeclared_tool", message }
18
+ : { error: { type: "upstream_error", code: "undeclared_tool", message } };
19
+ }
20
+ return { error: { message, type: "upstream_error" } };
21
+ }
22
+
23
+ function object(value) {
24
+ return value !== null && typeof value === "object" && !Array.isArray(value);
25
+ }
26
+
27
+ function list(value) {
28
+ if (value == null) return [];
29
+ if (!Array.isArray(value)) throw new GuardError("Invalid upstream tool snapshot");
30
+ return value;
31
+ }
32
+
33
+ function nameGuard(policy) {
34
+ const declared = new Set(policy.declared);
35
+ const injected = new Set(policy.injected);
36
+ const folded = new Map();
37
+ for (const name of declared) {
38
+ const key = name.toLowerCase();
39
+ folded.set(key, folded.has(key) ? null : name);
40
+ }
41
+ return (name) => {
42
+ if (typeof name !== "string" || !name) throw new GuardError("Missing upstream tool name");
43
+ if (declared.has(name)) return name;
44
+ const match = injected.has(name) && folded.get(name.toLowerCase());
45
+ if (match) return match;
46
+ throw new GuardError("Upstream called a tool not declared by the client");
47
+ };
48
+ }
49
+
50
+ function checkFunction(value, checkName) {
51
+ if (!object(value)) throw new GuardError("Invalid upstream function call");
52
+ const name = checkName(value.name);
53
+ const changed = name !== value.name;
54
+ value.name = name;
55
+ return changed;
56
+ }
57
+
58
+ function checkChatMessage(message, checkName) {
59
+ if (!object(message)) return false;
60
+ let changed = false;
61
+ if (message.function_call != null) changed = checkFunction(message.function_call, checkName);
62
+ for (const call of list(message.tool_calls)) {
63
+ if (!object(call) || (call.type != null && call.type !== "function")) {
64
+ throw new GuardError("Unsupported upstream tool call");
65
+ }
66
+ changed = checkFunction(call.function, checkName) || changed;
67
+ }
68
+ return changed;
69
+ }
70
+
71
+ function checkSnapshots(data, protocol, checkName) {
72
+ let changed = false;
73
+ if (protocol === "chat") {
74
+ for (const choice of list(data.choices)) {
75
+ if (!object(choice)) throw new GuardError("Invalid upstream choice");
76
+ changed = checkChatMessage(choice.message, checkName) || changed;
77
+ }
78
+ return changed;
79
+ }
80
+ const stack = [data];
81
+ while (stack.length) {
82
+ const value = stack.pop();
83
+ if (!object(value)) throw new GuardError("Invalid upstream tool snapshot");
84
+ if (
85
+ protocol === "responses" &&
86
+ typeof value.type === "string" &&
87
+ value.type.endsWith("_call") &&
88
+ value.type !== "function_call"
89
+ ) {
90
+ throw new GuardError("Unsupported upstream tool call type");
91
+ }
92
+ if (
93
+ (protocol === "responses" && value.type === "function_call") ||
94
+ (protocol === "anthropic" && ["tool_use", "server_tool_use"].includes(value.type))
95
+ ) {
96
+ changed = checkFunction(value, checkName) || changed;
97
+ }
98
+ const arrays = protocol === "responses" ? ["output", "content"] : ["content"];
99
+ const objects =
100
+ protocol === "responses" ? ["response", "item", "part"] : ["message", "content_block"];
101
+ for (const key of arrays) {
102
+ // Text content is not a snapshot container.
103
+ if (typeof value[key] === "string") continue;
104
+ for (const item of list(value[key])) stack.push(item);
105
+ }
106
+ for (const key of objects) {
107
+ if (value[key] != null) stack.push(value[key]);
108
+ }
109
+ }
110
+ return changed;
111
+ }
112
+
113
+ function position(value) {
114
+ const result = value ?? 0;
115
+ if (!Number.isSafeInteger(result) || result < 0)
116
+ throw new GuardError("Invalid upstream tool index");
117
+ return result;
118
+ }
119
+
120
+ function appendFunction(target, fragment) {
121
+ if (!object(fragment)) throw new GuardError("Invalid upstream function delta");
122
+ for (const [key, value] of Object.entries(fragment)) {
123
+ if (key === "name" || key === "arguments") {
124
+ if (typeof value !== "string") throw new GuardError("Invalid upstream function delta");
125
+ target[key] = (target[key] ?? "") + value;
126
+ } else {
127
+ Object.defineProperty(target, key, {
128
+ value,
129
+ writable: true,
130
+ enumerable: true,
131
+ configurable: true,
132
+ });
133
+ }
134
+ }
135
+ }
136
+
137
+ function chatBuffer(checkName) {
138
+ const pending = new Map();
139
+ let bytes = 0;
140
+ function charge(state, value) {
141
+ const size = encoder.encode(JSON.stringify(value)).byteLength;
142
+ bytes += size;
143
+ state.bytes += size;
144
+ if (bytes > MAX_BYTES)
145
+ throw new GuardError("Pending upstream tools exceed the 32 MiB buffer limit");
146
+ }
147
+ function stateFor(index, data) {
148
+ if (!pending.has(index)) {
149
+ const metadata = {};
150
+ for (const key of [
151
+ "id",
152
+ "object",
153
+ "created",
154
+ "model",
155
+ "system_fingerprint",
156
+ "service_tier",
157
+ ]) {
158
+ if (data[key] !== undefined) metadata[key] = data[key];
159
+ }
160
+ const state = { calls: new Map(), legacy: null, metadata, bytes: 0 };
161
+ pending.set(index, state);
162
+ charge(state, metadata);
163
+ }
164
+ return pending.get(index);
165
+ }
166
+ function complete(state) {
167
+ const delta = {};
168
+ if (state.calls.size) {
169
+ delta.tool_calls = [...state.calls].sort(([a], [b]) => a - b).map(([, value]) => value);
170
+ }
171
+ if (state.legacy) delta.function_call = state.legacy;
172
+ checkChatMessage(delta, checkName);
173
+ return delta;
174
+ }
175
+ function remove(index) {
176
+ const state = pending.get(index);
177
+ if (state) bytes -= state.bytes;
178
+ pending.delete(index);
179
+ }
180
+ return {
181
+ get size() {
182
+ return pending.size;
183
+ },
184
+ clear() {
185
+ pending.clear();
186
+ bytes = 0;
187
+ },
188
+ apply(data) {
189
+ let changed = checkSnapshots(data, "chat", checkName);
190
+ const choices = list(data.choices);
191
+ for (const choice of choices) {
192
+ const delta = choice.delta;
193
+ if (!object(delta)) continue;
194
+ if (delta.tool_calls == null && delta.function_call == null) continue;
195
+ changed = true;
196
+ const state = stateFor(position(choice.index), data);
197
+ for (const fragment of list(delta.tool_calls)) {
198
+ if (!object(fragment) || (fragment.type != null && fragment.type !== "function")) {
199
+ throw new GuardError("Unsupported upstream tool delta");
200
+ }
201
+ charge(state, fragment);
202
+ const index = position(fragment.index);
203
+ const previous = state.calls.get(index) ?? { index, function: {} };
204
+ const value = { ...previous, ...fragment, index, function: previous.function };
205
+ if (fragment.function != null) appendFunction(value.function, fragment.function);
206
+ state.calls.set(index, value);
207
+ }
208
+ if (delta.function_call != null) {
209
+ charge(state, delta.function_call);
210
+ state.legacy ??= {};
211
+ appendFunction(state.legacy, delta.function_call);
212
+ }
213
+ delete delta.tool_calls;
214
+ delete delta.function_call;
215
+ }
216
+ // Validate every finishing choice before publishing any part of this event.
217
+ for (const choice of choices) {
218
+ if (choice.finish_reason == null) continue;
219
+ const index = position(choice.index);
220
+ const state = pending.get(index);
221
+ if (!state) continue;
222
+ choice.delta = { ...choice.delta, ...complete(state) };
223
+ remove(index);
224
+ changed = true;
225
+ }
226
+ if (!changed) return { data, changed: false };
227
+ data.choices = choices.filter(
228
+ (choice) =>
229
+ choice.finish_reason != null ||
230
+ Object.keys(choice.delta ?? {}).length ||
231
+ Object.keys(choice).some((key) => !["index", "delta", "finish_reason"].includes(key)),
232
+ );
233
+ return { data: data.choices.length || data.usage != null ? data : null, changed: true };
234
+ },
235
+ finish() {
236
+ if (!pending.size) return null;
237
+ const choices = [...pending]
238
+ .sort(([a], [b]) => a - b)
239
+ .map(([index, state]) => ({ index, delta: complete(state), finish_reason: null }));
240
+ const metadata = pending.values().next().value.metadata;
241
+ pending.clear();
242
+ bytes = 0;
243
+ return { ...metadata, choices };
244
+ },
245
+ };
246
+ }
247
+
248
+ function parseJSON(text) {
249
+ let value;
250
+ try {
251
+ value = JSON.parse(text);
252
+ } catch {
253
+ throw new GuardError("Invalid JSON in upstream response");
254
+ }
255
+ if (!object(value)) throw new GuardError("Invalid JSON object in upstream response");
256
+ return value;
257
+ }
258
+
259
+ function encodeEvent(data, name = "") {
260
+ return `${name ? `event: ${name}\n` : ""}data: ${JSON.stringify(data)}\n\n`;
261
+ }
262
+
263
+ function rewrite(frame, data) {
264
+ const lines = frame.lines.filter((line) => line !== "data" && !line.startsWith("data:"));
265
+ lines.push(`data: ${JSON.stringify(data)}`);
266
+ return `${lines.join("\n")}\n\n`;
267
+ }
268
+
269
+ async function* framesFrom(reader) {
270
+ let parts = [];
271
+ let lineBytes = 0;
272
+ let eventBytes = 0;
273
+ let lines = [];
274
+ let rawLines = [];
275
+ let skipLF = false;
276
+ let pendingFrame;
277
+ let firstLine = true;
278
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
279
+ function append(value) {
280
+ eventBytes += value.byteLength;
281
+ if (eventBytes > MAX_BYTES)
282
+ throw new GuardError("Upstream SSE event exceeds the 32 MiB buffer limit");
283
+ if (value.byteLength) {
284
+ parts.push(value);
285
+ lineBytes += value.byteLength;
286
+ }
287
+ }
288
+ while (true) {
289
+ const { done, value } = await reader.read();
290
+ if (done) {
291
+ if (pendingFrame) {
292
+ yield pendingFrame;
293
+ pendingFrame = null;
294
+ eventBytes = 0;
295
+ }
296
+ if (eventBytes || lineBytes || lines.length)
297
+ throw new GuardError("Upstream SSE ended inside an event");
298
+ return;
299
+ }
300
+ let start = 0;
301
+ if (skipLF && value.length) {
302
+ if (value[0] === 10) {
303
+ eventBytes++;
304
+ if (pendingFrame) pendingFrame.raw += "\n";
305
+ else rawLines[rawLines.length - 1] += "\n";
306
+ start = 1;
307
+ }
308
+ if (eventBytes > MAX_BYTES)
309
+ throw new GuardError("Upstream SSE event exceeds the 32 MiB buffer limit");
310
+ skipLF = false;
311
+ if (pendingFrame) {
312
+ const frame = pendingFrame;
313
+ pendingFrame = null;
314
+ eventBytes = 0;
315
+ yield frame;
316
+ }
317
+ }
318
+ for (let offset = start; offset < value.length; offset++) {
319
+ if (value[offset] !== 10 && value[offset] !== 13) continue;
320
+ append(value.subarray(start, offset));
321
+ if (++eventBytes > MAX_BYTES)
322
+ throw new GuardError("Upstream SSE event exceeds the 32 MiB buffer limit");
323
+ const bytes = new Uint8Array(lineBytes);
324
+ let cursor = 0;
325
+ for (const part of parts) {
326
+ bytes.set(part, cursor);
327
+ cursor += part.length;
328
+ }
329
+ let line;
330
+ try {
331
+ line = decoder.decode(bytes);
332
+ } catch {
333
+ throw new GuardError("Invalid UTF-8 in upstream SSE event");
334
+ }
335
+ parts = [];
336
+ lineBytes = 0;
337
+ let rawLine = line + String.fromCharCode(value[offset]);
338
+ if (firstLine) {
339
+ line = line.replace(/^\uFEFF/, "");
340
+ firstLine = false;
341
+ }
342
+ if (value[offset] === 13) {
343
+ if (value[offset + 1] === 10) {
344
+ rawLine += "\n";
345
+ offset++;
346
+ if (++eventBytes > MAX_BYTES)
347
+ throw new GuardError("Upstream SSE event exceeds the 32 MiB buffer limit");
348
+ } else if (offset + 1 === value.length) skipLF = true;
349
+ }
350
+ start = offset + 1;
351
+ rawLines.push(rawLine);
352
+ if (line) {
353
+ lines.push(line);
354
+ } else {
355
+ const frameLines = lines;
356
+ lines = [];
357
+ let name = "";
358
+ const data = [];
359
+ for (const item of frameLines) {
360
+ const colon = item.indexOf(":");
361
+ const key = colon < 0 ? item : item.slice(0, colon);
362
+ let content = colon < 0 ? "" : item.slice(colon + 1);
363
+ if (content.startsWith(" ")) content = content.slice(1);
364
+ if (key === "event") name = content;
365
+ if (key === "data") data.push(content);
366
+ }
367
+ const frame = {
368
+ lines: frameLines,
369
+ name,
370
+ text: data.join("\n"),
371
+ raw: rawLines.join(""),
372
+ };
373
+ rawLines = [];
374
+ if (skipLF) {
375
+ // Preserve a trailing CRLF even when its LF arrives in the next chunk.
376
+ pendingFrame = frame;
377
+ } else {
378
+ eventBytes = 0;
379
+ yield frame;
380
+ }
381
+ }
382
+ }
383
+ append(value.subarray(start));
384
+ }
385
+ }
386
+
387
+ function guardedStream(response, protocol, checkName) {
388
+ const reader = response.body?.getReader();
389
+ const chat = protocol === "chat" ? chatBuffer(checkName) : null;
390
+ let frames = reader ? framesFrom(reader) : null;
391
+ let completed = false;
392
+ let stopped = false;
393
+ let released = false;
394
+ let cancellation;
395
+ function release() {
396
+ if (reader && !released) {
397
+ reader.releaseLock();
398
+ released = true;
399
+ }
400
+ chat?.clear();
401
+ frames = null;
402
+ }
403
+ function cancel(reason) {
404
+ cancellation ??= (async () => {
405
+ try {
406
+ await reader?.cancel(reason);
407
+ } finally {
408
+ release();
409
+ }
410
+ })();
411
+ return cancellation;
412
+ }
413
+ return new ReadableStream(
414
+ {
415
+ async pull(controller) {
416
+ try {
417
+ while (!stopped) {
418
+ const next = frames ? await frames.next() : { done: true };
419
+ if (stopped) return;
420
+ if (next.done) {
421
+ if (!completed || chat?.size)
422
+ throw new GuardError("Upstream SSE ended without a completion marker");
423
+ stopped = true;
424
+ release();
425
+ controller.close();
426
+ return;
427
+ }
428
+ const frame = next.value;
429
+ if (frame.name !== "error" && !frame.text.trim()) {
430
+ controller.enqueue(encoder.encode(frame.raw));
431
+ return;
432
+ }
433
+ if (frame.name !== "error" && frame.text.trim() === "[DONE]") {
434
+ if (protocol !== "chat")
435
+ throw new GuardError("Unexpected upstream completion marker");
436
+ const final = chat.finish();
437
+ completed = true;
438
+ controller.enqueue(encoder.encode((final ? encodeEvent(final) : "") + frame.raw));
439
+ return;
440
+ }
441
+ const data = frame.name === "error" ? null : parseJSON(frame.text);
442
+ const type = data?.type ?? frame.name;
443
+ if (
444
+ frame.name === "error" ||
445
+ type === "error" ||
446
+ data?.error ||
447
+ type === "response.failed" ||
448
+ data?.response?.status === "failed"
449
+ ) {
450
+ stopped = true;
451
+ try {
452
+ await cancel();
453
+ } catch (cause) {
454
+ controller.error(cause);
455
+ return;
456
+ }
457
+ controller.enqueue(encoder.encode(frame.raw));
458
+ controller.close();
459
+ return;
460
+ }
461
+ let changed;
462
+ let result = data;
463
+ if (chat) {
464
+ const checked = chat.apply(data);
465
+ changed = checked.changed;
466
+ result = checked.data;
467
+ } else {
468
+ changed = checkSnapshots(data, protocol, checkName);
469
+ }
470
+ if (
471
+ (protocol === "responses" &&
472
+ ["response.completed", "response.incomplete"].includes(type)) ||
473
+ (protocol === "anthropic" && type === "message_stop")
474
+ )
475
+ completed = true;
476
+ if (result) {
477
+ controller.enqueue(encoder.encode(changed ? rewrite(frame, result) : frame.raw));
478
+ return;
479
+ }
480
+ }
481
+ } catch (error) {
482
+ if (stopped) return;
483
+ stopped = true;
484
+ if (error instanceof GuardError) {
485
+ try {
486
+ await cancel(error);
487
+ } catch (cause) {
488
+ controller.error(cause);
489
+ return;
490
+ }
491
+ controller.enqueue(
492
+ encoder.encode(
493
+ encodeEvent(
494
+ failure(protocol, error.message, true),
495
+ protocol === "chat" ? "" : "error",
496
+ ),
497
+ ),
498
+ );
499
+ controller.close();
500
+ } else {
501
+ release();
502
+ controller.error(error);
503
+ }
504
+ }
505
+ },
506
+ async cancel(reason) {
507
+ stopped = true;
508
+ await cancel(reason);
509
+ },
510
+ },
511
+ { highWaterMark: 0 },
512
+ );
513
+ }
514
+
515
+ export async function guardToolResponse(response, protocol, policy) {
516
+ if (!response.ok) return response;
517
+ if (!["chat", "responses", "anthropic"].includes(protocol))
518
+ throw new TypeError(`Unsupported protocol: ${protocol}`);
519
+ const checkName = nameGuard(policy);
520
+ const headers = headersFor(response);
521
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0].trim().toLowerCase();
522
+ if (contentType === "text/event-stream") {
523
+ return new Response(guardedStream(response, protocol, checkName), {
524
+ status: response.status,
525
+ statusText: response.statusText,
526
+ headers,
527
+ });
528
+ }
529
+ const text = await response.text();
530
+ let value;
531
+ let status = response.status;
532
+ try {
533
+ value = parseJSON(text);
534
+ checkSnapshots(value, protocol, checkName);
535
+ } catch (error) {
536
+ if (!(error instanceof GuardError)) throw error;
537
+ status = 502;
538
+ value = failure(protocol, error.message);
539
+ }
540
+ headers.set("content-type", "application/json");
541
+ return new Response(JSON.stringify(value), { status, headers });
542
+ }