@autotask/atools-tool 0.1.7 → 0.1.8

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.
@@ -1,6 +1,7 @@
1
1
  import http from 'node:http';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import crypto from 'node:crypto';
4
5
 
5
6
  const DEFAULT_PORT = 18888;
6
7
  const DEFAULT_UPSTREAM = 'https://sub2api.atools.live';
@@ -10,6 +11,9 @@ const DEFAULT_MAX_REQ_BYTES = 68000;
10
11
  const DEFAULT_DROP_TOOLS_ON_COMPACT = false;
11
12
  const DEFAULT_STRIP_PREVIOUS_RESPONSE_ID = false;
12
13
  const DEFAULT_FORCE_MODEL = '';
14
+ const DEFAULT_DEBUG_DUMP = false;
15
+ const DEFAULT_DEBUG_MAX_BODY = 800000;
16
+ const DEFAULT_DEBUG_INCLUDE_AUTH = false;
13
17
  const SUPPORTED_MODEL_IDS = new Set(['gpt-5.2', 'gpt-5.3-codex', 'gpt-5.4']);
14
18
  const FORWARD_HEADER_ALLOWLIST = new Set([
15
19
  'authorization',
@@ -111,6 +115,15 @@ function normalizeResponsesPayload(payload, {
111
115
  });
112
116
  }
113
117
 
118
+ const hasFunctionCallOutput = extractFunctionCallOutputIds(out).length > 0;
119
+ if ((Array.isArray(out.tools) && out.tools.length > 0) || hasFunctionCallOutput) {
120
+ out.store = true;
121
+ }
122
+
123
+ if (Array.isArray(out.tools) && out.tools.length > 0 && (out.tool_choice == null || out.tool_choice === '')) {
124
+ out.tool_choice = 'auto';
125
+ }
126
+
114
127
  return pruneResponsesPayload(out);
115
128
  }
116
129
 
@@ -200,6 +213,236 @@ function sleep(ms) {
200
213
  return new Promise((resolve) => setTimeout(resolve, ms));
201
214
  }
202
215
 
216
+ function parseBool(value, fallback = false) {
217
+ if (typeof value === 'boolean') return value;
218
+ if (value == null) return fallback;
219
+ const normalized = String(value).trim().toLowerCase();
220
+ if (!normalized) return fallback;
221
+ return ['1', 'true', 'yes', 'on'].includes(normalized);
222
+ }
223
+
224
+ function maskSecret(value) {
225
+ const text = String(value ?? '');
226
+ if (text.length <= 10) return '***';
227
+ return `${text.slice(0, 6)}...${text.slice(-4)}`;
228
+ }
229
+
230
+ function toHeaderObject(headers, { includeAuth = DEFAULT_DEBUG_INCLUDE_AUTH } = {}) {
231
+ const out = {};
232
+ if (!headers) return out;
233
+
234
+ if (typeof headers.forEach === 'function') {
235
+ headers.forEach((value, key) => {
236
+ const low = String(key || '').toLowerCase();
237
+ if (!includeAuth && (low === 'authorization' || low === 'api-key' || low === 'x-api-key')) {
238
+ out[key] = maskSecret(value);
239
+ } else {
240
+ out[key] = String(value);
241
+ }
242
+ });
243
+ return out;
244
+ }
245
+
246
+ for (const [key, rawValue] of Object.entries(headers)) {
247
+ const value = normalizeHeaderValue(rawValue);
248
+ const low = String(key || '').toLowerCase();
249
+ if (!includeAuth && (low === 'authorization' || low === 'api-key' || low === 'x-api-key')) {
250
+ out[key] = maskSecret(value);
251
+ } else {
252
+ out[key] = value;
253
+ }
254
+ }
255
+ return out;
256
+ }
257
+
258
+ function bufferToDebugText(buf, maxChars = DEFAULT_DEBUG_MAX_BODY) {
259
+ const text = Buffer.isBuffer(buf) ? buf.toString('utf8') : String(buf ?? '');
260
+ if (!Number.isFinite(maxChars) || maxChars <= 0) return text;
261
+ if (text.length <= maxChars) return text;
262
+ return `${text.slice(0, maxChars)}\n...[truncated ${text.length - maxChars} chars]`;
263
+ }
264
+
265
+ function hashKey(text) {
266
+ return crypto.createHash('sha1').update(String(text)).digest('hex').slice(0, 16);
267
+ }
268
+
269
+ function extractAuthIdentity(headers = {}) {
270
+ const auth = normalizeHeaderValue(headers.authorization || headers.Authorization || '');
271
+ if (auth) return auth;
272
+ const apiKey = normalizeHeaderValue(headers['x-api-key'] || headers['api-key'] || '');
273
+ return apiKey;
274
+ }
275
+
276
+ function buildConversationKey(headers = {}, payload = {}) {
277
+ const authIdentity = extractAuthIdentity(headers);
278
+ if (!authIdentity) return '';
279
+ const modelRaw = typeof payload?.model === 'string' ? payload.model : '*';
280
+ const model = normalizeModelId(modelRaw) || '*';
281
+ return hashKey(`${authIdentity}|${model}`);
282
+ }
283
+
284
+ function buildPromptContextKey(headers = {}, payload = {}) {
285
+ const authIdentity = extractAuthIdentity(headers);
286
+ if (!authIdentity) return '';
287
+ const promptCacheKey = typeof payload?.prompt_cache_key === 'string'
288
+ ? payload.prompt_cache_key.trim()
289
+ : '';
290
+ if (!promptCacheKey) return '';
291
+ const modelRaw = typeof payload?.model === 'string' ? payload.model : '*';
292
+ const model = normalizeModelId(modelRaw) || '*';
293
+ return hashKey(`${authIdentity}|${model}|${promptCacheKey}`);
294
+ }
295
+
296
+ function extractFunctionCallOutputIds(payload = {}) {
297
+ const ids = [];
298
+ const input = Array.isArray(payload.input) ? payload.input : [];
299
+ for (const item of input) {
300
+ if (!item || typeof item !== 'object') continue;
301
+
302
+ if (item.type === 'function_call_output' && typeof item.call_id === 'string' && item.call_id) {
303
+ ids.push(item.call_id);
304
+ continue;
305
+ }
306
+
307
+ if (Array.isArray(item.content)) {
308
+ for (const part of item.content) {
309
+ if (!part || typeof part !== 'object') continue;
310
+ if (part.type === 'function_call_output' && typeof part.call_id === 'string' && part.call_id) {
311
+ ids.push(part.call_id);
312
+ }
313
+ }
314
+ }
315
+ }
316
+ return ids;
317
+ }
318
+
319
+ function extractItemReferenceIds(payload = {}) {
320
+ const ids = [];
321
+ const input = Array.isArray(payload.input) ? payload.input : [];
322
+ for (const item of input) {
323
+ if (!item || typeof item !== 'object') continue;
324
+ if (item.type === 'item_reference' && typeof item.id === 'string' && item.id) {
325
+ ids.push(item.id);
326
+ }
327
+ }
328
+ return ids;
329
+ }
330
+
331
+ function extractResponseId(contentType, buffer) {
332
+ const body = bufferToDebugText(buffer, 3_000_000);
333
+ const ct = String(contentType || '').toLowerCase();
334
+
335
+ if (ct.includes('application/json')) {
336
+ try {
337
+ const parsed = JSON.parse(body);
338
+ if (parsed && typeof parsed.id === 'string' && parsed.id) return parsed.id;
339
+ if (parsed?.response && typeof parsed.response.id === 'string' && parsed.response.id) return parsed.response.id;
340
+ } catch {
341
+ return '';
342
+ }
343
+ return '';
344
+ }
345
+
346
+ if (ct.includes('text/event-stream')) {
347
+ const chunks = body.split('\n\n');
348
+ for (const chunk of chunks) {
349
+ const dataLines = chunk.split('\n').filter((line) => line.startsWith('data: '));
350
+ if (!dataLines.length) continue;
351
+ const dataText = dataLines.map((line) => line.slice(6)).join('\n');
352
+ if (!dataText || dataText === '[DONE]') continue;
353
+ try {
354
+ const parsed = JSON.parse(dataText);
355
+ if (parsed?.type === 'response.created' && typeof parsed?.response?.id === 'string') {
356
+ return parsed.response.id;
357
+ }
358
+ if (parsed?.type === 'response.completed' && typeof parsed?.response?.id === 'string') {
359
+ return parsed.response.id;
360
+ }
361
+ } catch {
362
+ // ignore malformed SSE chunk
363
+ }
364
+ }
365
+ }
366
+
367
+ return '';
368
+ }
369
+
370
+ function setCachedResponseId(cache, key, responseId) {
371
+ if (!key || !responseId) return;
372
+ cache.set(key, responseId);
373
+ if (cache.size > 300) {
374
+ const first = cache.keys().next();
375
+ if (!first.done) cache.delete(first.value);
376
+ }
377
+ }
378
+
379
+ function setCachedCallIdContext(cache, callId, responseId) {
380
+ if (!callId || !responseId) return;
381
+ cache.set(callId, responseId);
382
+ if (cache.size > 1200) {
383
+ const first = cache.keys().next();
384
+ if (!first.done) cache.delete(first.value);
385
+ }
386
+ }
387
+
388
+ function extractFunctionCallIdsFromResponse(contentType, buffer) {
389
+ const ids = [];
390
+ const body = bufferToDebugText(buffer, 3_000_000);
391
+ const ct = String(contentType || '').toLowerCase();
392
+
393
+ if (ct.includes('application/json')) {
394
+ try {
395
+ const parsed = JSON.parse(body);
396
+ const output = Array.isArray(parsed?.output) ? parsed.output : [];
397
+ for (const item of output) {
398
+ if (item?.type === 'function_call' && typeof item.call_id === 'string' && item.call_id) {
399
+ ids.push(item.call_id);
400
+ }
401
+ }
402
+ } catch {
403
+ return ids;
404
+ }
405
+ return ids;
406
+ }
407
+
408
+ if (ct.includes('text/event-stream')) {
409
+ const chunks = body.split('\n\n');
410
+ for (const chunk of chunks) {
411
+ const dataLines = chunk.split('\n').filter((line) => line.startsWith('data: '));
412
+ if (!dataLines.length) continue;
413
+ const dataText = dataLines.map((line) => line.slice(6)).join('\n');
414
+ if (!dataText || dataText === '[DONE]') continue;
415
+ try {
416
+ const parsed = JSON.parse(dataText);
417
+ if (parsed?.type === 'response.output_item.added') {
418
+ const item = parsed?.item;
419
+ if (item?.type === 'function_call' && typeof item.call_id === 'string' && item.call_id) {
420
+ ids.push(item.call_id);
421
+ }
422
+ }
423
+ } catch {
424
+ // ignore malformed SSE chunk
425
+ }
426
+ }
427
+ }
428
+
429
+ return ids;
430
+ }
431
+
432
+ function parseSseEventDataText(eventBlock = '') {
433
+ const dataLines = String(eventBlock)
434
+ .split('\n')
435
+ .filter((line) => line.startsWith('data: '));
436
+ if (!dataLines.length) return null;
437
+ const dataText = dataLines.map((line) => line.slice(6)).join('\n');
438
+ if (!dataText || dataText === '[DONE]') return null;
439
+ try {
440
+ return JSON.parse(dataText);
441
+ } catch {
442
+ return null;
443
+ }
444
+ }
445
+
203
446
  async function forward(url, req, headers, body) {
204
447
  return fetch(url, {
205
448
  method: req.method,
@@ -252,6 +495,15 @@ export async function createProxyServer(options = {}) {
252
495
  const stripPreviousResponseId = options.stripPreviousResponseId
253
496
  ?? ['1', 'true', 'yes', 'on'].includes(String(process.env.SUB2API_COMPAT_STRIP_PREVIOUS_RESPONSE_ID || '').toLowerCase());
254
497
  const forceModel = String(options.forceModel ?? process.env.SUB2API_COMPAT_FORCE_MODEL ?? '').trim();
498
+ const debugDump = parseBool(options.debugDump ?? process.env.SUB2API_COMPAT_DEBUG_DUMP, DEFAULT_DEBUG_DUMP);
499
+ const debugMaxBody = Number(options.debugMaxBody ?? process.env.SUB2API_COMPAT_DEBUG_MAX_BODY ?? DEFAULT_DEBUG_MAX_BODY);
500
+ const debugIncludeAuth = parseBool(
501
+ options.debugIncludeAuth ?? process.env.SUB2API_COMPAT_DEBUG_INCLUDE_AUTH,
502
+ DEFAULT_DEBUG_INCLUDE_AUTH
503
+ );
504
+ const responseContextCache = new Map();
505
+ const promptContextCache = new Map();
506
+ const callIdContextCache = new Map();
255
507
 
256
508
  const server = http.createServer(async (req, res) => {
257
509
  const startAt = Date.now();
@@ -262,21 +514,83 @@ export async function createProxyServer(options = {}) {
262
514
  const rawBody = Buffer.concat(chunks);
263
515
 
264
516
  const baseHeaders = buildCodexLikeHeaders(req.headers, userAgent);
517
+ const incomingHeaders = toHeaderObject(req.headers, { includeAuth: debugIncludeAuth });
265
518
 
266
519
  const isResponsesPath = req.method === 'POST' && url.pathname.endsWith('/responses');
267
520
  let body = rawBody;
268
521
  let compactBody = null;
269
522
  let aggressiveBody = null;
270
523
  let minimalBody = null;
524
+ let requestedStream = false;
525
+ let rawBodyText = '';
526
+ let normalizedBodyText = '';
527
+ let conversationKey = '';
528
+ let promptContextKey = '';
529
+ let functionCallOutputIds = [];
271
530
 
272
531
  if (isResponsesPath && rawBody.length > 0) {
273
532
  try {
274
533
  const parsed = JSON.parse(rawBody.toString('utf8'));
534
+ rawBodyText = JSON.stringify(parsed);
275
535
  const inputModel = typeof parsed.model === 'string' ? parsed.model : '';
276
536
  const normalized = normalizeResponsesPayload(parsed, {
277
537
  stripPreviousResponseId,
278
538
  forceModel
279
539
  });
540
+ requestedStream = normalized?.stream === true;
541
+ conversationKey = buildConversationKey(req.headers, normalized);
542
+ promptContextKey = buildPromptContextKey(req.headers, normalized);
543
+ functionCallOutputIds = extractFunctionCallOutputIds(normalized);
544
+ if (functionCallOutputIds.length > 0 && !normalized.previous_response_id) {
545
+ let cachedResponseId = '';
546
+ let source = '';
547
+ const matched = new Set();
548
+ for (const callId of functionCallOutputIds) {
549
+ const responseId = callIdContextCache.get(callId);
550
+ if (responseId) matched.add(responseId);
551
+ }
552
+ if (matched.size === 1) {
553
+ cachedResponseId = [...matched][0];
554
+ source = 'call_id';
555
+ } else if (promptContextKey) {
556
+ cachedResponseId = promptContextCache.get(promptContextKey) || '';
557
+ if (cachedResponseId) source = 'prompt_cache_key';
558
+ }
559
+ if (!cachedResponseId && conversationKey) {
560
+ cachedResponseId = responseContextCache.get(conversationKey) || '';
561
+ if (cachedResponseId) source = 'conversation';
562
+ }
563
+ if (cachedResponseId) {
564
+ normalized.previous_response_id = cachedResponseId;
565
+ normalized.store = true;
566
+ appendLog(logFile, {
567
+ method: req.method,
568
+ path: url.pathname,
569
+ contextRepair: {
570
+ source,
571
+ previous_response_id: cachedResponseId,
572
+ function_call_output_count: functionCallOutputIds.length
573
+ }
574
+ });
575
+ }
576
+ if (!cachedResponseId && Array.isArray(normalized.input)) {
577
+ const existingRefs = new Set(extractItemReferenceIds(normalized));
578
+ const missingCallIds = [...new Set(functionCallOutputIds)].filter((id) => !existingRefs.has(id));
579
+ if (missingCallIds.length > 0) {
580
+ const refs = missingCallIds.map((id) => ({ type: 'item_reference', id }));
581
+ normalized.input = [...refs, ...normalized.input];
582
+ appendLog(logFile, {
583
+ method: req.method,
584
+ path: url.pathname,
585
+ contextRepair: {
586
+ item_reference_injected: missingCallIds.length
587
+ }
588
+ });
589
+ }
590
+ }
591
+ }
592
+ requestedStream = normalized?.stream === true;
593
+ normalizedBodyText = JSON.stringify(normalized);
280
594
  const outputModel = typeof normalized.model === 'string' ? normalized.model : '';
281
595
  body = Buffer.from(JSON.stringify(normalized));
282
596
  if (body.length > maxReqBytes) {
@@ -310,8 +624,11 @@ export async function createProxyServer(options = {}) {
310
624
  });
311
625
  }
312
626
  } catch {
627
+ rawBodyText = bufferToDebugText(rawBody, debugMaxBody);
313
628
  // Keep original body if parse fails.
314
629
  }
630
+ } else if (rawBody.length > 0) {
631
+ rawBodyText = bufferToDebugText(rawBody, debugMaxBody);
315
632
  }
316
633
 
317
634
  const requestBodies = [body];
@@ -330,11 +647,17 @@ export async function createProxyServer(options = {}) {
330
647
  requestBodies.push(minimalBody);
331
648
  }
332
649
 
333
- let resp;
334
650
  let attempts = 0;
335
651
  let primaryStatus = null;
336
652
  let compacted = false;
337
653
  let strategy = 'full';
654
+ let finalStatus = 502;
655
+ let finalHeaders = {};
656
+ let outBuffer = Buffer.alloc(0);
657
+ let streamResp = null;
658
+ let streamObservedResponseId = '';
659
+ const streamObservedCallIds = new Set();
660
+ const debugAttempts = [];
338
661
  const maxAttempts = isResponsesPath ? retryMax : 1;
339
662
  while (attempts < maxAttempts) {
340
663
  attempts += 1;
@@ -347,8 +670,42 @@ export async function createProxyServer(options = {}) {
347
670
  const primaryHeaders = isResponsesPath ? buildHeaders(baseHeaders, requestBody.length) : baseHeaders;
348
671
 
349
672
  const primaryResp = await forward(url, req, primaryHeaders, requestBody);
673
+ const attemptHeaders = toHeaderObject(primaryResp.headers, { includeAuth: debugIncludeAuth });
350
674
  primaryStatus = primaryResp.status;
351
- resp = primaryResp;
675
+ finalStatus = primaryResp.status;
676
+ finalHeaders = attemptHeaders;
677
+
678
+ if (requestedStream && primaryResp.status < 500) {
679
+ streamResp = primaryResp;
680
+ if (debugDump) {
681
+ debugAttempts.push({
682
+ attempt: attempts,
683
+ strategy,
684
+ requestHeaders: toHeaderObject(primaryHeaders, { includeAuth: debugIncludeAuth }),
685
+ requestBody: bufferToDebugText(requestBody, debugMaxBody),
686
+ responseStatus: primaryResp.status,
687
+ responseHeaders: attemptHeaders,
688
+ responseBody: '[stream passthrough]'
689
+ });
690
+ }
691
+ break;
692
+ }
693
+
694
+ const attemptBuffer = Buffer.from(await primaryResp.arrayBuffer());
695
+ outBuffer = attemptBuffer;
696
+
697
+ if (debugDump) {
698
+ debugAttempts.push({
699
+ attempt: attempts,
700
+ strategy,
701
+ requestHeaders: toHeaderObject(primaryHeaders, { includeAuth: debugIncludeAuth }),
702
+ requestBody: bufferToDebugText(requestBody, debugMaxBody),
703
+ responseStatus: primaryResp.status,
704
+ responseHeaders: attemptHeaders,
705
+ responseBody: bufferToDebugText(attemptBuffer, debugMaxBody)
706
+ });
707
+ }
708
+
352
709
  if (primaryResp.status < 500) break;
353
710
 
354
711
  if (attempts < maxAttempts) {
@@ -356,20 +713,91 @@ export async function createProxyServer(options = {}) {
356
713
  }
357
714
  }
358
715
 
359
- res.statusCode = resp.status;
360
- resp.headers.forEach((value, key) => {
716
+ res.statusCode = finalStatus;
717
+ Object.entries(finalHeaders).forEach(([key, value]) => {
361
718
  if (key.toLowerCase() === 'transfer-encoding') return;
362
719
  res.setHeader(key, value);
363
720
  });
364
721
 
365
- const outBuffer = Buffer.from(await resp.arrayBuffer());
366
- const upstreamError = resp.status >= 500
722
+ if (streamResp?.body) {
723
+ const streamed = [];
724
+ let sseCarry = '';
725
+ for await (const chunk of streamResp.body) {
726
+ const buf = Buffer.from(chunk);
727
+ streamed.push(buf);
728
+ if (isResponsesPath) {
729
+ sseCarry += buf.toString('utf8').replace(/\r\n/g, '\n');
730
+ let splitIndex = sseCarry.indexOf('\n\n');
731
+ while (splitIndex >= 0) {
732
+ const eventBlock = sseCarry.slice(0, splitIndex);
733
+ sseCarry = sseCarry.slice(splitIndex + 2);
734
+ const parsedEvent = parseSseEventDataText(eventBlock);
735
+ if (parsedEvent) {
736
+ const eventResponseId = typeof parsedEvent?.response?.id === 'string'
737
+ ? parsedEvent.response.id
738
+ : '';
739
+ if (!streamObservedResponseId && eventResponseId) {
740
+ streamObservedResponseId = eventResponseId;
741
+ setCachedResponseId(responseContextCache, conversationKey, streamObservedResponseId);
742
+ setCachedResponseId(promptContextCache, promptContextKey, streamObservedResponseId);
743
+ }
744
+ if (
745
+ parsedEvent?.type === 'response.output_item.added'
746
+ || parsedEvent?.type === 'response.output_item.done'
747
+ ) {
748
+ const item = parsedEvent?.item;
749
+ const callId = (item?.type === 'function_call' && typeof item?.call_id === 'string')
750
+ ? item.call_id
751
+ : '';
752
+ if (callId) {
753
+ streamObservedCallIds.add(callId);
754
+ if (streamObservedResponseId) {
755
+ setCachedCallIdContext(callIdContextCache, callId, streamObservedResponseId);
756
+ }
757
+ }
758
+ }
759
+ }
760
+ splitIndex = sseCarry.indexOf('\n\n');
761
+ }
762
+ }
763
+ res.write(buf);
764
+ }
765
+ outBuffer = Buffer.concat(streamed);
766
+ res.end();
767
+ } else {
768
+ res.end(outBuffer);
769
+ }
770
+
771
+ const upstreamError = finalStatus >= 500
367
772
  ? outBuffer.toString('utf8').replace(/\s+/g, ' ').slice(0, 240)
368
773
  : '';
774
+ const finalContentType = finalHeaders['content-type'] || '';
775
+ const responseId = streamObservedResponseId || extractResponseId(finalContentType, outBuffer);
776
+ if (responseId) {
777
+ setCachedResponseId(responseContextCache, conversationKey, responseId);
778
+ setCachedResponseId(promptContextCache, promptContextKey, responseId);
779
+ const callIds = extractFunctionCallIdsFromResponse(finalContentType, outBuffer);
780
+ for (const callId of streamObservedCallIds) {
781
+ setCachedCallIdContext(callIdContextCache, callId, responseId);
782
+ }
783
+ for (const callId of callIds) {
784
+ setCachedCallIdContext(callIdContextCache, callId, responseId);
785
+ }
786
+ if (callIds.length > 0 || streamObservedCallIds.size > 0) {
787
+ appendLog(logFile, {
788
+ method: req.method,
789
+ path: url.pathname,
790
+ contextCacheUpdate: {
791
+ response_id: responseId,
792
+ call_ids: Math.max(callIds.length, streamObservedCallIds.size)
793
+ }
794
+ });
795
+ }
796
+ }
369
797
  appendLog(logFile, {
370
798
  method: req.method,
371
799
  path: url.pathname,
372
- status: resp.status,
800
+ status: finalStatus,
373
801
  attempts,
374
802
  primaryStatus,
375
803
  compacted,
@@ -380,7 +808,24 @@ export async function createProxyServer(options = {}) {
380
808
  ...(upstreamError ? { upstreamError } : {}),
381
809
  durationMs: Date.now() - startAt
382
810
  });
383
- res.end(outBuffer);
811
+ if (debugDump) {
812
+ appendLog(logFile, {
813
+ kind: 'debug_dump',
814
+ method: req.method,
815
+ path: url.pathname,
816
+ upstream: url.toString(),
817
+ incomingHeaders,
818
+ normalizedHeaders: toHeaderObject(baseHeaders, { includeAuth: debugIncludeAuth }),
819
+ rawRequestBody: bufferToDebugText(rawBodyText || rawBody, debugMaxBody),
820
+ normalizedRequestBody: bufferToDebugText(normalizedBodyText || body, debugMaxBody),
821
+ attempts: debugAttempts,
822
+ finalResponse: {
823
+ status: finalStatus,
824
+ headers: finalHeaders,
825
+ body: bufferToDebugText(outBuffer, debugMaxBody)
826
+ }
827
+ });
828
+ }
384
829
  } catch (err) {
385
830
  const message = String(err?.message || err);
386
831
  appendLog(logFile, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autotask/atools-tool",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "ATools CLI for OpenClaw proxy compatibility and interactive model configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",