@cortexkit/opencode-anthropic-auth 1.1.2 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-anthropic-auth",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "opencode-anthropic-auth": "dist/cli.js"
20
20
  },
21
21
  "engines": {
22
- "bun": "1.3.11"
22
+ "bun": "1.3.14"
23
23
  },
24
24
  "files": [
25
25
  "dist",
@@ -27,7 +27,8 @@
27
27
  "LICENSE"
28
28
  ],
29
29
  "scripts": {
30
- "build": "rm -rf dist && tsc -p tsconfig.build.json",
30
+ "build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --minify && tsc -p tsconfig.build.json --emitDeclarationOnly",
31
+ "build:dev": "rm -rf dist && tsc -p tsconfig.build.json",
31
32
  "dev": "bun ../../scripts/dev.ts",
32
33
  "dev:clean": "bun ../../scripts/dev-clean.ts",
33
34
  "extract": "bun ../../scripts/extract-system-prompt.ts",
@@ -40,6 +41,6 @@
40
41
  "@opencode-ai/plugin": "*"
41
42
  },
42
43
  "dependencies": {
43
- "@cortexkit/anthropic-auth-core": "1.1.2"
44
+ "@cortexkit/anthropic-auth-core": "1.2.0"
44
45
  }
45
46
  }
@@ -1,98 +0,0 @@
1
- function isRecord(value) {
2
- return typeof value === 'object' && value !== null;
3
- }
4
- function extractMessages(response) {
5
- if (Array.isArray(response))
6
- return response;
7
- if (isRecord(response) && Array.isArray(response.data))
8
- return response.data;
9
- return [];
10
- }
11
- function getRole(message) {
12
- if (!isRecord(message) || !isRecord(message.info))
13
- return undefined;
14
- return typeof message.info.role === 'string' ? message.info.role : undefined;
15
- }
16
- function extractFromMessage(message) {
17
- if (!isRecord(message) || !isRecord(message.info))
18
- return null;
19
- const info = message.info;
20
- const modelInfo = isRecord(info.model) ? info.model : undefined;
21
- const agent = typeof info.agent === 'string' ? info.agent : undefined;
22
- const providerID = typeof modelInfo?.providerID === 'string'
23
- ? modelInfo.providerID
24
- : typeof info.providerID === 'string'
25
- ? info.providerID
26
- : undefined;
27
- const modelID = typeof modelInfo?.modelID === 'string'
28
- ? modelInfo.modelID
29
- : typeof info.modelID === 'string'
30
- ? info.modelID
31
- : undefined;
32
- const variant = typeof modelInfo?.variant === 'string'
33
- ? modelInfo.variant
34
- : typeof info.variant === 'string'
35
- ? info.variant
36
- : undefined;
37
- if (!agent && (!providerID || !modelID) && !variant)
38
- return null;
39
- const context = {};
40
- if (agent)
41
- context.agent = agent;
42
- if (providerID && modelID)
43
- context.model = { providerID, modelID };
44
- if (variant)
45
- context.variant = variant;
46
- return context;
47
- }
48
- function mergeContexts(base, patch) {
49
- return {
50
- agent: base.agent ?? patch.agent,
51
- model: base.model ?? patch.model,
52
- variant: base.variant ?? patch.variant,
53
- };
54
- }
55
- function isComplete(context) {
56
- return Boolean(context.agent && context.model && context.variant);
57
- }
58
- export async function resolvePromptContext(client, sessionId) {
59
- if (!client || !sessionId)
60
- return null;
61
- const typedClient = client;
62
- if (typeof typedClient.session?.messages !== 'function')
63
- return null;
64
- let messages = [];
65
- try {
66
- messages = extractMessages(await Promise.resolve(typedClient.session.messages({
67
- path: { id: sessionId },
68
- query: { limit: 100 },
69
- })));
70
- }
71
- catch {
72
- return null;
73
- }
74
- if (messages.length === 0)
75
- return null;
76
- let result = {};
77
- for (let index = messages.length - 1; index >= 0; index--) {
78
- if (getRole(messages[index]) !== 'assistant')
79
- continue;
80
- const context = extractFromMessage(messages[index]);
81
- if (!context)
82
- continue;
83
- result = mergeContexts(result, context);
84
- if (isComplete(result))
85
- return result;
86
- }
87
- for (let index = messages.length - 1; index >= 0; index--) {
88
- const context = extractFromMessage(messages[index]);
89
- if (!context)
90
- continue;
91
- result = mergeContexts(result, context);
92
- if (isComplete(result))
93
- return result;
94
- }
95
- if (!result.agent && !result.model && !result.variant)
96
- return null;
97
- return result;
98
- }
package/dist/transform.js DELETED
@@ -1,498 +0,0 @@
1
- import { applyClaudeCodeHeaders, applyClaudeCodeMetadata, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, FAST_MODE_BETA, isFastModeSupportedModel, mergeAnthropicBetas, OPENCODE_IDENTITY_PREFIX, orderClaudeCodeBody, PARAGRAPH_REMOVAL_ANCHORS, REQUIRED_BETAS, signRequestBody, TEXT_REPLACEMENTS, TOOL_PREFIX, } from '@cortexkit/anthropic-auth-core';
2
- /**
3
- * Prefix a tool name with TOOL_PREFIX and uppercase the first character.
4
- * Claude Code uses PascalCase tool names (e.g. mcp_Bash, mcp_Read);
5
- * lowercase names (mcp_bash, mcp_read) are flagged as non-Claude-Code clients.
6
- */
7
- function prefixName(name) {
8
- return `${TOOL_PREFIX}${name.charAt(0).toUpperCase()}${name.slice(1)}`;
9
- }
10
- /**
11
- * Reverse prefixName: strip TOOL_PREFIX and restore the original leading case.
12
- */
13
- function unprefixName(name) {
14
- // StructuredOutput is still used as StructuredOutput
15
- if (name === 'StructuredOutput') {
16
- return name;
17
- }
18
- return `${name.charAt(0).toLowerCase()}${name.slice(1)}`;
19
- }
20
- /**
21
- * Merge headers from a Request object and/or a RequestInit headers value
22
- * into a single Headers instance.
23
- */
24
- export function mergeHeaders(input, init) {
25
- const headers = new Headers();
26
- if (input instanceof Request) {
27
- input.headers.forEach((value, key) => {
28
- headers.set(key, value);
29
- });
30
- }
31
- const initHeaders = init?.headers;
32
- if (initHeaders) {
33
- if (initHeaders instanceof Headers) {
34
- initHeaders.forEach((value, key) => {
35
- headers.set(key, value);
36
- });
37
- }
38
- else if (Array.isArray(initHeaders)) {
39
- for (const entry of initHeaders) {
40
- const [key, value] = entry;
41
- if (typeof value !== 'undefined') {
42
- headers.set(key, String(value));
43
- }
44
- }
45
- }
46
- else {
47
- for (const [key, value] of Object.entries(initHeaders)) {
48
- if (typeof value !== 'undefined') {
49
- headers.set(key, String(value));
50
- }
51
- }
52
- }
53
- }
54
- return headers;
55
- }
56
- /**
57
- * Merge incoming beta headers with the required OAuth betas, deduplicating.
58
- */
59
- export function mergeBetaHeaders(headers) {
60
- const incomingBeta = headers.get('anthropic-beta') || '';
61
- const incomingBetasList = incomingBeta
62
- .split(',')
63
- .map((b) => b.trim())
64
- .filter(Boolean);
65
- return [...new Set([...REQUIRED_BETAS, ...incomingBetasList])].join(',');
66
- }
67
- export function addFastModeBetaHeader(headers) {
68
- headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
69
- return headers;
70
- }
71
- /**
72
- * Set OAuth-required headers on the request: authorization, beta, user-agent.
73
- * Removes x-api-key since we're using OAuth.
74
- */
75
- export function setOAuthHeaders(headers, accessToken, options = {}) {
76
- return applyClaudeCodeHeaders(headers, accessToken, options);
77
- }
78
- /**
79
- * Add TOOL_PREFIX to tool names in the request body.
80
- * Prefixes both tool definitions and tool_use blocks in messages.
81
- */
82
- export function prefixToolNames(parsed) {
83
- if (parsed.tools && Array.isArray(parsed.tools)) {
84
- parsed.tools = parsed.tools.map((tool) => ({
85
- ...tool,
86
- name: tool.name ? prefixName(tool.name) : tool.name,
87
- }));
88
- }
89
- if (parsed.messages && Array.isArray(parsed.messages)) {
90
- parsed.messages = parsed.messages.map((msg) => {
91
- if (msg.content && Array.isArray(msg.content)) {
92
- msg.content = msg.content.map((block) => {
93
- if (block.type === 'tool_use' && block.name) {
94
- return { ...block, name: prefixName(block.name) };
95
- }
96
- return block;
97
- });
98
- }
99
- return msg;
100
- });
101
- }
102
- return JSON.stringify(orderClaudeCodeBody(parsed));
103
- }
104
- /**
105
- * Strip TOOL_PREFIX from tool names in streaming response text.
106
- */
107
- export function stripToolPrefix(text) {
108
- return text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, (_match, name) => `"name": "${unprefixName(name)}"`);
109
- }
110
- function splitToolPrefixRewriteBuffer(buffer, flush = false) {
111
- if (flush)
112
- return { ready: stripToolPrefix(buffer), pending: '' };
113
- let keepFrom = buffer.length;
114
- const marker = '"name"';
115
- const partialMarkerStart = Math.max(0, buffer.length - marker.length + 1);
116
- for (let index = partialMarkerStart; index < buffer.length; index++) {
117
- if (marker.startsWith(buffer.slice(index))) {
118
- keepFrom = Math.min(keepFrom, index);
119
- break;
120
- }
121
- }
122
- const lastName = buffer.lastIndexOf(marker);
123
- if (lastName !== -1) {
124
- const tail = buffer.slice(lastName);
125
- if (/^"name"\s*(?::\s*(?:"[^"]*)?)?$/.test(tail)) {
126
- keepFrom = Math.min(keepFrom, lastName);
127
- }
128
- }
129
- if (keepFrom < buffer.length) {
130
- return {
131
- ready: stripToolPrefix(buffer.slice(0, keepFrom)),
132
- pending: buffer.slice(keepFrom),
133
- };
134
- }
135
- return { ready: stripToolPrefix(buffer), pending: '' };
136
- }
137
- /**
138
- * Check if TLS verification should be skipped for custom API endpoints.
139
- * Only effective when ANTHROPIC_BASE_URL is also set.
140
- */
141
- export function isInsecure() {
142
- if (!process.env.ANTHROPIC_BASE_URL?.trim())
143
- return false;
144
- const raw = process.env.ANTHROPIC_INSECURE?.trim();
145
- return raw === '1' || raw === 'true';
146
- }
147
- /**
148
- * Parse ANTHROPIC_BASE_URL from the environment.
149
- * Returns a valid HTTP(S) URL or null if unset/invalid.
150
- */
151
- function resolveBaseUrl() {
152
- const raw = process.env.ANTHROPIC_BASE_URL?.trim();
153
- if (!raw)
154
- return null;
155
- try {
156
- const baseUrl = new URL(raw);
157
- if ((baseUrl.protocol !== 'http:' && baseUrl.protocol !== 'https:') ||
158
- baseUrl.username ||
159
- baseUrl.password) {
160
- return null;
161
- }
162
- return baseUrl;
163
- }
164
- catch {
165
- return null;
166
- }
167
- }
168
- /**
169
- * Rewrite the request URL to add ?beta=true for /v1/messages requests.
170
- * When ANTHROPIC_BASE_URL is set, overrides the origin (protocol + host)
171
- * for all API requests flowing through the fetch wrapper.
172
- * Returns the modified input and URL (if applicable).
173
- */
174
- export function rewriteUrl(input) {
175
- let requestUrl = null;
176
- try {
177
- if (typeof input === 'string' || input instanceof URL) {
178
- requestUrl = new URL(input.toString());
179
- }
180
- else if (input instanceof Request) {
181
- requestUrl = new URL(input.url);
182
- }
183
- }
184
- catch {
185
- requestUrl = null;
186
- }
187
- if (!requestUrl)
188
- return { input, url: null };
189
- const originalHref = requestUrl.href;
190
- const baseUrl = resolveBaseUrl();
191
- if (baseUrl) {
192
- requestUrl.protocol = baseUrl.protocol;
193
- requestUrl.host = baseUrl.host;
194
- }
195
- if (requestUrl.pathname === '/v1/messages' &&
196
- !requestUrl.searchParams.has('beta')) {
197
- requestUrl.searchParams.set('beta', 'true');
198
- }
199
- if (requestUrl.href === originalHref) {
200
- return { input, url: requestUrl };
201
- }
202
- const newInput = input instanceof Request
203
- ? new Request(requestUrl.toString(), input)
204
- : requestUrl;
205
- return { input: newInput, url: requestUrl };
206
- }
207
- /**
208
- * Sanitize OpenCode-branded strings from the system prompt text.
209
- *
210
- * 1. Removes the OPENCODE_IDENTITY paragraph.
211
- * 2. Removes any paragraph (text between blank lines) that contains
212
- * one of the PARAGRAPH_REMOVAL_ANCHORS — typically URLs that
213
- * identify OpenCode-specific content.
214
- * 3. Applies TEXT_REPLACEMENTS for inline occurrences of "OpenCode"
215
- * inside paragraphs we want to keep.
216
- *
217
- * This approach is resilient to upstream rewording of the OpenCode
218
- * prompt — as long as the anchor strings (URLs, etc.) still appear
219
- * somewhere in the paragraph, the removal works.
220
- */
221
- export function sanitizeSystemText(text) {
222
- // Split into paragraphs (separated by one or more blank lines)
223
- const paragraphs = text.split(/\n\n+/);
224
- const filtered = paragraphs.filter((paragraph) => {
225
- if (paragraph.includes(OPENCODE_IDENTITY_PREFIX)) {
226
- // If the paragraph contains the identity, drop it entirely
227
- return false;
228
- }
229
- // Remove paragraphs containing any removal anchor
230
- for (const anchor of PARAGRAPH_REMOVAL_ANCHORS) {
231
- if (paragraph.includes(anchor))
232
- return false;
233
- }
234
- return true;
235
- });
236
- let result = filtered.join('\n\n');
237
- // Apply inline text replacements
238
- for (const rule of TEXT_REPLACEMENTS) {
239
- result = result.replace(rule.match, rule.replacement);
240
- }
241
- return result.trim();
242
- }
243
- function isRecord(value) {
244
- return value != null && typeof value === 'object' && !Array.isArray(value);
245
- }
246
- /**
247
- * Sanitize system prompt and prepend Claude Code identity.
248
- * Handles all Anthropic API system formats: undefined, string, or array of text blocks.
249
- */
250
- export function prependClaudeCodeIdentity(system) {
251
- const identityBlock = {
252
- type: 'text',
253
- text: CLAUDE_CODE_IDENTITY,
254
- };
255
- if (system == null)
256
- return [identityBlock];
257
- if (typeof system === 'string') {
258
- const sanitized = sanitizeSystemText(system);
259
- if (sanitized === CLAUDE_CODE_IDENTITY)
260
- return [identityBlock];
261
- return [identityBlock, { type: 'text', text: sanitized }];
262
- }
263
- if (isRecord(system)) {
264
- const type = typeof system.type === 'string' ? system.type : 'text';
265
- const text = typeof system.text === 'string' ? system.text : '';
266
- return [identityBlock, { ...system, type, text: sanitizeSystemText(text) }];
267
- }
268
- if (!Array.isArray(system))
269
- return [identityBlock];
270
- const sanitized = system.map((item) => {
271
- if (typeof item === 'string') {
272
- return { type: 'text', text: sanitizeSystemText(item) };
273
- }
274
- if (isRecord(item) &&
275
- item.type === 'text' &&
276
- typeof item.text === 'string') {
277
- return {
278
- ...item,
279
- type: 'text',
280
- text: sanitizeSystemText(item.text),
281
- };
282
- }
283
- return { type: 'text', text: String(item) };
284
- });
285
- // Idempotency: don't double-prepend if first block already has the identity
286
- if (sanitized[0]?.text === CLAUDE_CODE_IDENTITY) {
287
- return sanitized;
288
- }
289
- return [identityBlock, ...sanitized];
290
- }
291
- const CACHE_1H_CONTROL = { type: 'ephemeral', ttl: '1h' };
292
- function getCacheControl(value) {
293
- if (isRecord(value.cache_control))
294
- return value.cache_control;
295
- if (isRecord(value.cacheControl))
296
- return value.cacheControl;
297
- return null;
298
- }
299
- function setWireCacheControl(value, withTtl) {
300
- if (!isRecord(value))
301
- return false;
302
- delete value.cacheControl;
303
- value.cache_control = withTtl
304
- ? { ...CACHE_1H_CONTROL }
305
- : { type: 'ephemeral' };
306
- return true;
307
- }
308
- function removeCacheControl(value) {
309
- if (!isRecord(value))
310
- return;
311
- delete value.cache_control;
312
- delete value.cacheControl;
313
- }
314
- function normalizeContentToArray(content) {
315
- if (Array.isArray(content))
316
- return content;
317
- if (typeof content === 'string')
318
- return [{ type: 'text', text: content }];
319
- return null;
320
- }
321
- function updateCacheControlTtl(value, cache1hEnabled) {
322
- if (!isRecord(value))
323
- return;
324
- const cacheControl = getCacheControl(value);
325
- if (!cacheControl || cacheControl.type !== 'ephemeral')
326
- return;
327
- if (cache1hEnabled) {
328
- cacheControl.ttl = '1h';
329
- }
330
- else {
331
- delete cacheControl.ttl;
332
- }
333
- }
334
- function applyCache1hTtl(parsed, cache1hEnabled) {
335
- if (Array.isArray(parsed.system)) {
336
- for (const block of parsed.system)
337
- updateCacheControlTtl(block, cache1hEnabled);
338
- }
339
- else {
340
- updateCacheControlTtl(parsed.system, cache1hEnabled);
341
- }
342
- if (!Array.isArray(parsed.messages))
343
- return;
344
- for (const message of parsed.messages) {
345
- updateCacheControlTtl(message, cache1hEnabled);
346
- if (isRecord(message) && Array.isArray(message.content)) {
347
- for (const block of message.content) {
348
- updateCacheControlTtl(block, cache1hEnabled);
349
- }
350
- }
351
- }
352
- }
353
- function walkCacheControlTargets(parsed, visitor) {
354
- if (Array.isArray(parsed.system)) {
355
- for (const block of parsed.system)
356
- visitor(block);
357
- }
358
- else {
359
- visitor(parsed.system);
360
- }
361
- if (!Array.isArray(parsed.messages))
362
- return;
363
- for (const message of parsed.messages) {
364
- visitor(message);
365
- if (isRecord(message) && Array.isArray(message.content)) {
366
- for (const block of message.content)
367
- visitor(block);
368
- }
369
- }
370
- }
371
- function removeAllCacheControls(parsed) {
372
- removeCacheControl(parsed);
373
- walkCacheControlTargets(parsed, removeCacheControl);
374
- }
375
- function applyAutomaticCache1h(parsed) {
376
- removeAllCacheControls(parsed);
377
- parsed.cache_control = { ...CACHE_1H_CONTROL };
378
- }
379
- function setMessageCacheAnchor(message) {
380
- if (!isRecord(message))
381
- return false;
382
- const content = normalizeContentToArray(message.content);
383
- if (!content?.length)
384
- return setWireCacheControl(message, true);
385
- message.content = content;
386
- const lastCacheableBlock = [...content]
387
- .reverse()
388
- .find((block) => isRecord(block) && block.type !== 'thinking');
389
- return setWireCacheControl(lastCacheableBlock ?? message, true);
390
- }
391
- function applyHybridCache1h(parsed) {
392
- removeAllCacheControls(parsed);
393
- if (Array.isArray(parsed.system)) {
394
- const identityIndex = parsed.system.findIndex((block) => isRecord(block) && block.text === CLAUDE_CODE_IDENTITY);
395
- const cacheableSystemBlocks = parsed.system
396
- .slice(identityIndex >= 0 ? identityIndex + 1 : 0)
397
- .filter(isRecord);
398
- const lastSystemBlock = cacheableSystemBlocks[cacheableSystemBlocks.length - 1];
399
- setWireCacheControl(lastSystemBlock, true);
400
- }
401
- else {
402
- setWireCacheControl(parsed.system, true);
403
- }
404
- if (!Array.isArray(parsed.messages))
405
- return;
406
- setMessageCacheAnchor(parsed.messages[0]);
407
- setMessageCacheAnchor(parsed.messages[1]);
408
- const movingMessageIndex = parsed.messages.length - 2;
409
- if (movingMessageIndex > 1) {
410
- setMessageCacheAnchor(parsed.messages[movingMessageIndex]);
411
- }
412
- }
413
- function applyCache1hStrategy(parsed, options) {
414
- if (!options.enabled) {
415
- applyCache1hTtl(parsed, false);
416
- delete parsed.cache_control;
417
- delete parsed.cacheControl;
418
- return;
419
- }
420
- if (options.mode === 'automatic') {
421
- applyAutomaticCache1h(parsed);
422
- return;
423
- }
424
- if (options.mode === 'hybrid') {
425
- applyHybridCache1h(parsed);
426
- return;
427
- }
428
- applyCache1hTtl(parsed, true);
429
- delete parsed.cacheControl;
430
- }
431
- /**
432
- * Rewrite the full request body: sanitize system prompt and prefix tool names.
433
- */
434
- export async function rewriteRequestBody(body, options = {}) {
435
- try {
436
- const parsed = JSON.parse(body);
437
- const billingHeader = Array.isArray(parsed.messages) &&
438
- parsed.messages.some((message) => message.role === 'user')
439
- ? buildBillingHeaderValue(parsed.messages, undefined, CLAUDE_CODE_ENTRYPOINT)
440
- : null;
441
- // Sanitize system prompt and prepend Claude Code identity
442
- parsed.system = prependClaudeCodeIdentity(parsed.system);
443
- // Prepend the billing header as a separate system block so the
444
- // final layout is: [billing header, identity, ...rest]
445
- if (billingHeader && Array.isArray(parsed.system)) {
446
- parsed.system.unshift({ type: 'text', text: billingHeader });
447
- }
448
- applyCache1hStrategy(parsed, {
449
- enabled: options.cache1hEnabled ?? false,
450
- mode: options.cache1hMode ?? 'explicit',
451
- });
452
- if (options.fastModeEnabled && isFastModeSupportedModel(parsed.model)) {
453
- parsed.speed = 'fast';
454
- }
455
- else if (parsed.speed === 'fast') {
456
- delete parsed.speed;
457
- }
458
- if (options.identity)
459
- applyClaudeCodeMetadata(parsed, options.identity);
460
- return await signRequestBody(prefixToolNames(parsed));
461
- }
462
- catch {
463
- return body;
464
- }
465
- }
466
- /**
467
- * Create a streaming response that strips the tool prefix from tool names.
468
- */
469
- export function createStrippedStream(response) {
470
- if (!response.body)
471
- return response;
472
- const reader = response.body.getReader();
473
- const decoder = new TextDecoder();
474
- const encoder = new TextEncoder();
475
- let pending = '';
476
- const stream = new ReadableStream({
477
- async pull(controller) {
478
- const { done, value } = await reader.read();
479
- if (done) {
480
- const flushed = splitToolPrefixRewriteBuffer(`${pending}${decoder.decode()}`, true);
481
- if (flushed.ready)
482
- controller.enqueue(encoder.encode(flushed.ready));
483
- controller.close();
484
- return;
485
- }
486
- const text = pending + decoder.decode(value, { stream: true });
487
- const rewritten = splitToolPrefixRewriteBuffer(text);
488
- pending = rewritten.pending;
489
- if (rewritten.ready)
490
- controller.enqueue(encoder.encode(rewritten.ready));
491
- },
492
- });
493
- return new Response(stream, {
494
- status: response.status,
495
- statusText: response.statusText,
496
- headers: response.headers,
497
- });
498
- }