@blockrun/clawrouter 0.12.93 → 0.12.95

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/dist/cli.js CHANGED
@@ -18721,7 +18721,7 @@ var require_permessage_deflate = __commonJS({
18721
18721
  var kBuffers = /* @__PURE__ */ Symbol("buffers");
18722
18722
  var kError = /* @__PURE__ */ Symbol("error");
18723
18723
  var zlibLimiter;
18724
- var PerMessageDeflate = class {
18724
+ var PerMessageDeflate2 = class {
18725
18725
  /**
18726
18726
  * Creates a PerMessageDeflate instance.
18727
18727
  *
@@ -18732,6 +18732,9 @@ var require_permessage_deflate = __commonJS({
18732
18732
  * acknowledge disabling of client context takeover
18733
18733
  * @param {Number} [options.concurrencyLimit=10] The number of concurrent
18734
18734
  * calls to zlib
18735
+ * @param {Boolean} [options.isServer=false] Create the instance in either
18736
+ * server or client mode
18737
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
18735
18738
  * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
18736
18739
  * use of a custom server window size
18737
18740
  * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
@@ -18742,15 +18745,12 @@ var require_permessage_deflate = __commonJS({
18742
18745
  * deflate
18743
18746
  * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
18744
18747
  * inflate
18745
- * @param {Boolean} [isServer=false] Create the instance in either server or
18746
- * client mode
18747
- * @param {Number} [maxPayload=0] The maximum allowed message length
18748
18748
  */
18749
- constructor(options, isServer, maxPayload) {
18750
- this._maxPayload = maxPayload | 0;
18749
+ constructor(options) {
18751
18750
  this._options = options || {};
18752
18751
  this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
18753
- this._isServer = !!isServer;
18752
+ this._maxPayload = this._options.maxPayload | 0;
18753
+ this._isServer = !!this._options.isServer;
18754
18754
  this._deflate = null;
18755
18755
  this._inflate = null;
18756
18756
  this.params = null;
@@ -19059,7 +19059,7 @@ var require_permessage_deflate = __commonJS({
19059
19059
  });
19060
19060
  }
19061
19061
  };
19062
- module.exports = PerMessageDeflate;
19062
+ module.exports = PerMessageDeflate2;
19063
19063
  function deflateOnData(chunk) {
19064
19064
  this[kBuffers].push(chunk);
19065
19065
  this[kTotalLength] += chunk.length;
@@ -19294,7 +19294,7 @@ var require_receiver = __commonJS({
19294
19294
  "node_modules/ws/lib/receiver.js"(exports, module) {
19295
19295
  "use strict";
19296
19296
  var { Writable } = __require("stream");
19297
- var PerMessageDeflate = require_permessage_deflate();
19297
+ var PerMessageDeflate2 = require_permessage_deflate();
19298
19298
  var {
19299
19299
  BINARY_TYPES,
19300
19300
  EMPTY_BUFFER,
@@ -19461,7 +19461,7 @@ var require_receiver = __commonJS({
19461
19461
  return;
19462
19462
  }
19463
19463
  const compressed = (buf[0] & 64) === 64;
19464
- if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
19464
+ if (compressed && !this._extensions[PerMessageDeflate2.extensionName]) {
19465
19465
  const error = this.createError(
19466
19466
  RangeError,
19467
19467
  "RSV1 must be clear",
@@ -19705,7 +19705,7 @@ var require_receiver = __commonJS({
19705
19705
  * @private
19706
19706
  */
19707
19707
  decompress(data, cb) {
19708
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
19708
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
19709
19709
  perMessageDeflate.decompress(data, this._fin, (err, buf) => {
19710
19710
  if (err) return cb(err);
19711
19711
  if (buf.length) {
@@ -19887,7 +19887,7 @@ var require_sender = __commonJS({
19887
19887
  "use strict";
19888
19888
  var { Duplex } = __require("stream");
19889
19889
  var { randomFillSync } = __require("crypto");
19890
- var PerMessageDeflate = require_permessage_deflate();
19890
+ var PerMessageDeflate2 = require_permessage_deflate();
19891
19891
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
19892
19892
  var { isBlob, isValidStatusCode } = require_validation();
19893
19893
  var { mask: applyMask, toBuffer } = require_buffer_util();
@@ -20171,7 +20171,7 @@ var require_sender = __commonJS({
20171
20171
  * @public
20172
20172
  */
20173
20173
  send(data, options, cb) {
20174
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
20174
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
20175
20175
  let opcode = options.binary ? 2 : 1;
20176
20176
  let rsv1 = options.compress;
20177
20177
  let byteLength;
@@ -20295,7 +20295,7 @@ var require_sender = __commonJS({
20295
20295
  this.sendFrame(_Sender.frame(data, options), cb);
20296
20296
  return;
20297
20297
  }
20298
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
20298
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
20299
20299
  this._bufferedBytes += options[kByteLength];
20300
20300
  this._state = DEFLATING;
20301
20301
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
@@ -20733,11 +20733,11 @@ var require_extension = __commonJS({
20733
20733
  return offers;
20734
20734
  }
20735
20735
  function format(extensions) {
20736
- return Object.keys(extensions).map((extension) => {
20737
- let configurations = extensions[extension];
20736
+ return Object.keys(extensions).map((extension2) => {
20737
+ let configurations = extensions[extension2];
20738
20738
  if (!Array.isArray(configurations)) configurations = [configurations];
20739
20739
  return configurations.map((params) => {
20740
- return [extension].concat(
20740
+ return [extension2].concat(
20741
20741
  Object.keys(params).map((k) => {
20742
20742
  let values = params[k];
20743
20743
  if (!Array.isArray(values)) values = [values];
@@ -20763,7 +20763,7 @@ var require_websocket = __commonJS({
20763
20763
  var { randomBytes: randomBytes3, createHash: createHash4 } = __require("crypto");
20764
20764
  var { Duplex, Readable } = __require("stream");
20765
20765
  var { URL: URL2 } = __require("url");
20766
- var PerMessageDeflate = require_permessage_deflate();
20766
+ var PerMessageDeflate2 = require_permessage_deflate();
20767
20767
  var Receiver2 = require_receiver();
20768
20768
  var Sender2 = require_sender();
20769
20769
  var { isBlob } = require_validation();
@@ -20971,8 +20971,8 @@ var require_websocket = __commonJS({
20971
20971
  this.emit("close", this._closeCode, this._closeMessage);
20972
20972
  return;
20973
20973
  }
20974
- if (this._extensions[PerMessageDeflate.extensionName]) {
20975
- this._extensions[PerMessageDeflate.extensionName].cleanup();
20974
+ if (this._extensions[PerMessageDeflate2.extensionName]) {
20975
+ this._extensions[PerMessageDeflate2.extensionName].cleanup();
20976
20976
  }
20977
20977
  this._receiver.removeAllListeners();
20978
20978
  this._readyState = _WebSocket.CLOSED;
@@ -21134,7 +21134,7 @@ var require_websocket = __commonJS({
21134
21134
  fin: true,
21135
21135
  ...options
21136
21136
  };
21137
- if (!this._extensions[PerMessageDeflate.extensionName]) {
21137
+ if (!this._extensions[PerMessageDeflate2.extensionName]) {
21138
21138
  opts.compress = false;
21139
21139
  }
21140
21140
  this._sender.send(data || EMPTY_BUFFER, opts, cb);
@@ -21260,7 +21260,7 @@ var require_websocket = __commonJS({
21260
21260
  } else {
21261
21261
  try {
21262
21262
  parsedUrl = new URL2(address2);
21263
- } catch (e7) {
21263
+ } catch {
21264
21264
  throw new SyntaxError(`Invalid URL: ${address2}`);
21265
21265
  }
21266
21266
  }
@@ -21308,13 +21308,13 @@ var require_websocket = __commonJS({
21308
21308
  opts.path = parsedUrl.pathname + parsedUrl.search;
21309
21309
  opts.timeout = opts.handshakeTimeout;
21310
21310
  if (opts.perMessageDeflate) {
21311
- perMessageDeflate = new PerMessageDeflate(
21312
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
21313
- false,
21314
- opts.maxPayload
21315
- );
21311
+ perMessageDeflate = new PerMessageDeflate2({
21312
+ ...opts.perMessageDeflate,
21313
+ isServer: false,
21314
+ maxPayload: opts.maxPayload
21315
+ });
21316
21316
  opts.headers["Sec-WebSocket-Extensions"] = format({
21317
- [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
21317
+ [PerMessageDeflate2.extensionName]: perMessageDeflate.offer()
21318
21318
  });
21319
21319
  }
21320
21320
  if (protocols.length) {
@@ -21457,19 +21457,19 @@ var require_websocket = __commonJS({
21457
21457
  return;
21458
21458
  }
21459
21459
  const extensionNames = Object.keys(extensions);
21460
- if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
21460
+ if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate2.extensionName) {
21461
21461
  const message = "Server indicated an extension that was not requested";
21462
21462
  abortHandshake(websocket, socket, message);
21463
21463
  return;
21464
21464
  }
21465
21465
  try {
21466
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
21466
+ perMessageDeflate.accept(extensions[PerMessageDeflate2.extensionName]);
21467
21467
  } catch (err) {
21468
21468
  const message = "Invalid Sec-WebSocket-Extensions header";
21469
21469
  abortHandshake(websocket, socket, message);
21470
21470
  return;
21471
21471
  }
21472
- websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
21472
+ websocket._extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
21473
21473
  }
21474
21474
  websocket.setSocket(socket, head, {
21475
21475
  allowSynchronousEvents: opts.allowSynchronousEvents,
@@ -21788,9 +21788,9 @@ var require_websocket_server = __commonJS({
21788
21788
  var http2 = __require("http");
21789
21789
  var { Duplex } = __require("stream");
21790
21790
  var { createHash: createHash4 } = __require("crypto");
21791
- var extension = require_extension();
21792
- var PerMessageDeflate = require_permessage_deflate();
21793
- var subprotocol = require_subprotocol();
21791
+ var extension2 = require_extension();
21792
+ var PerMessageDeflate2 = require_permessage_deflate();
21793
+ var subprotocol2 = require_subprotocol();
21794
21794
  var WebSocket2 = require_websocket();
21795
21795
  var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
21796
21796
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
@@ -22013,7 +22013,7 @@ var require_websocket_server = __commonJS({
22013
22013
  let protocols = /* @__PURE__ */ new Set();
22014
22014
  if (secWebSocketProtocol !== void 0) {
22015
22015
  try {
22016
- protocols = subprotocol.parse(secWebSocketProtocol);
22016
+ protocols = subprotocol2.parse(secWebSocketProtocol);
22017
22017
  } catch (err) {
22018
22018
  const message = "Invalid Sec-WebSocket-Protocol header";
22019
22019
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
@@ -22023,16 +22023,16 @@ var require_websocket_server = __commonJS({
22023
22023
  const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
22024
22024
  const extensions = {};
22025
22025
  if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
22026
- const perMessageDeflate = new PerMessageDeflate(
22027
- this.options.perMessageDeflate,
22028
- true,
22029
- this.options.maxPayload
22030
- );
22026
+ const perMessageDeflate = new PerMessageDeflate2({
22027
+ ...this.options.perMessageDeflate,
22028
+ isServer: true,
22029
+ maxPayload: this.options.maxPayload
22030
+ });
22031
22031
  try {
22032
- const offers = extension.parse(secWebSocketExtensions);
22033
- if (offers[PerMessageDeflate.extensionName]) {
22034
- perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
22035
- extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
22032
+ const offers = extension2.parse(secWebSocketExtensions);
22033
+ if (offers[PerMessageDeflate2.extensionName]) {
22034
+ perMessageDeflate.accept(offers[PerMessageDeflate2.extensionName]);
22035
+ extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
22036
22036
  }
22037
22037
  } catch (err) {
22038
22038
  const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
@@ -22103,10 +22103,10 @@ var require_websocket_server = __commonJS({
22103
22103
  ws._protocol = protocol;
22104
22104
  }
22105
22105
  }
22106
- if (extensions[PerMessageDeflate.extensionName]) {
22107
- const params = extensions[PerMessageDeflate.extensionName].params;
22108
- const value = extension.format({
22109
- [PerMessageDeflate.extensionName]: [params]
22106
+ if (extensions[PerMessageDeflate2.extensionName]) {
22107
+ const params = extensions[PerMessageDeflate2.extensionName].params;
22108
+ const value = extension2.format({
22109
+ [PerMessageDeflate2.extensionName]: [params]
22110
22110
  });
22111
22111
  headers.push(`Sec-WebSocket-Extensions: ${value}`);
22112
22112
  ws._extensions = extensions;
@@ -22174,13 +22174,16 @@ var require_websocket_server = __commonJS({
22174
22174
  });
22175
22175
 
22176
22176
  // node_modules/ws/wrapper.mjs
22177
- var import_stream, import_receiver, import_sender, import_websocket, import_websocket_server, wrapper_default;
22177
+ var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
22178
22178
  var init_wrapper = __esm({
22179
22179
  "node_modules/ws/wrapper.mjs"() {
22180
22180
  "use strict";
22181
22181
  import_stream = __toESM(require_stream(), 1);
22182
+ import_extension = __toESM(require_extension(), 1);
22183
+ import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
22182
22184
  import_receiver = __toESM(require_receiver(), 1);
22183
22185
  import_sender = __toESM(require_sender(), 1);
22186
+ import_subprotocol = __toESM(require_subprotocol(), 1);
22184
22187
  import_websocket = __toESM(require_websocket(), 1);
22185
22188
  import_websocket_server = __toESM(require_websocket_server(), 1);
22186
22189
  wrapper_default = import_websocket.default;
@@ -41867,8 +41870,8 @@ var x402Client = class _x402Client {
41867
41870
  * @param extension - The client extension to register
41868
41871
  * @returns The x402Client instance for chaining
41869
41872
  */
41870
- registerExtension(extension) {
41871
- this.registeredExtensions.set(extension.key, extension);
41873
+ registerExtension(extension2) {
41874
+ this.registeredExtensions.set(extension2.key, extension2);
41872
41875
  return this;
41873
41876
  }
41874
41877
  /**
@@ -42014,9 +42017,9 @@ var x402Client = class _x402Client {
42014
42017
  return paymentPayload;
42015
42018
  }
42016
42019
  let enriched = paymentPayload;
42017
- for (const [key, extension] of this.registeredExtensions) {
42018
- if (key in paymentRequired.extensions && extension.enrichPaymentPayload) {
42019
- enriched = await extension.enrichPaymentPayload(enriched, paymentRequired);
42020
+ for (const [key, extension2] of this.registeredExtensions) {
42021
+ if (key in paymentRequired.extensions && extension2.enrichPaymentPayload) {
42022
+ enriched = await extension2.enrichPaymentPayload(enriched, paymentRequired);
42020
42023
  }
42021
42024
  }
42022
42025
  return enriched;
@@ -45644,6 +45647,48 @@ async function getStats(days = 7) {
45644
45647
  // How many entries have valid baseline tracking
45645
45648
  };
45646
45649
  }
45650
+ async function formatRecentLogs(days = 1) {
45651
+ const logFiles = await getLogFiles();
45652
+ const filesToRead = logFiles.slice(0, days);
45653
+ const allEntries = [];
45654
+ for (const file of filesToRead) {
45655
+ const entries = await parseLogFile(join4(LOG_DIR2, file));
45656
+ allEntries.push(...entries);
45657
+ }
45658
+ allEntries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
45659
+ const lines = [];
45660
+ lines.push("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
45661
+ lines.push(`\u2551 ClawRouter Request Log \u2014 last ${days === 1 ? "24h" : `${days} days`}`.padEnd(72) + "\u2551");
45662
+ lines.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
45663
+ lines.push("\u2551 Time \u2551 Model \u2551 Cost \u2551 ms \u2551 Status \u2551");
45664
+ lines.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
45665
+ if (allEntries.length === 0) {
45666
+ lines.push("\u2551 No requests found".padEnd(72) + "\u2551");
45667
+ }
45668
+ let totalCost = 0;
45669
+ for (const e7 of allEntries) {
45670
+ const time = e7.timestamp.slice(11, 19);
45671
+ const date = e7.timestamp.slice(5, 10);
45672
+ const displayTime = `${date} ${time}`;
45673
+ const model = e7.model.length > 24 ? e7.model.slice(0, 21) + "..." : e7.model;
45674
+ const cost = `$${e7.cost.toFixed(4)}`;
45675
+ const ms = e7.latencyMs > 9999 ? `${(e7.latencyMs / 1e3).toFixed(1)}s` : `${e7.latencyMs}ms`;
45676
+ const status = e7.status === "error" ? " ERROR " : " OK ";
45677
+ totalCost += e7.cost;
45678
+ lines.push(
45679
+ `\u2551 ${displayTime.padEnd(16)}\u2551 ${model.padEnd(24)}\u2551 ${cost.padStart(7)}\u2551 ${ms.padStart(4)}\u2551${status}\u2551`
45680
+ );
45681
+ }
45682
+ lines.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
45683
+ lines.push(
45684
+ `\u2551 ${allEntries.length} request${allEntries.length !== 1 ? "s" : ""} Total spent: $${totalCost.toFixed(4)}`.padEnd(72) + "\u2551"
45685
+ );
45686
+ lines.push(
45687
+ "\u2551 Logs: ~/.openclaw/blockrun/logs/ (JSONL \u2014 one entry per request)".padEnd(72) + "\u2551"
45688
+ );
45689
+ lines.push("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
45690
+ return lines.join("\n");
45691
+ }
45647
45692
  async function clearStats() {
45648
45693
  try {
45649
45694
  const files = await readdir(LOG_DIR2);
@@ -52439,6 +52484,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
52439
52484
  let accumulatedContent = "";
52440
52485
  let responseInputTokens;
52441
52486
  let responseOutputTokens;
52487
+ let requestHadError = false;
52442
52488
  const isChatCompletion = req.url?.includes("/chat/completions");
52443
52489
  const sessionId = getSessionId(req.headers);
52444
52490
  let effectiveSessionId = sessionId;
@@ -53703,6 +53749,7 @@ data: [DONE]
53703
53749
  const rawErrBody = lastError?.body || structuredMessage;
53704
53750
  const errStatus = lastError?.status || 502;
53705
53751
  const transformedErr = transformPaymentError(rawErrBody);
53752
+ requestHadError = true;
53706
53753
  if (headersSentEarly) {
53707
53754
  let errPayload;
53708
53755
  try {
@@ -53746,6 +53793,21 @@ data: [DONE]
53746
53793
  completedAt: Date.now()
53747
53794
  });
53748
53795
  }
53796
+ const errModel = routingDecision?.model ?? modelId;
53797
+ if (errModel) {
53798
+ const errPayment = paymentStore.getStore()?.amountUsd ?? 0;
53799
+ logUsage({
53800
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
53801
+ model: errModel,
53802
+ tier: routingDecision?.tier ?? "DIRECT",
53803
+ cost: errPayment,
53804
+ baselineCost: errPayment,
53805
+ savings: 0,
53806
+ latencyMs: Date.now() - startTime,
53807
+ status: "error"
53808
+ }).catch(() => {
53809
+ });
53810
+ }
53749
53811
  return;
53750
53812
  }
53751
53813
  const responseChunks = [];
@@ -54080,6 +54142,7 @@ data: [DONE]
54080
54142
  baselineCost: logBaseline,
54081
54143
  savings: logSavings,
54082
54144
  latencyMs: Date.now() - startTime,
54145
+ status: requestHadError ? "error" : "success",
54083
54146
  ...responseInputTokens !== void 0 && { inputTokens: responseInputTokens },
54084
54147
  ...responseOutputTokens !== void 0 && { outputTokens: responseOutputTokens }
54085
54148
  };
@@ -54692,6 +54755,7 @@ Usage:
54692
54755
  clawrouter doctor [opus] [question]
54693
54756
  clawrouter partners [test]
54694
54757
  clawrouter report [daily|weekly|monthly] [--json]
54758
+ clawrouter logs [--days <n>]
54695
54759
 
54696
54760
  Options:
54697
54761
  --version, -v Show version number
@@ -54701,6 +54765,8 @@ Options:
54701
54765
  Commands:
54702
54766
  doctor AI-powered diagnostics (default: Sonnet ~$0.003)
54703
54767
  doctor opus Use Opus for deeper analysis (~$0.01)
54768
+ logs Per-request breakdown: model, cost, latency, status
54769
+ logs --days 7 Show last 7 days of requests (default: 1 day)
54704
54770
  partners List available partner APIs with pricing
54705
54771
  partners test Test partner API endpoints (expect 402 = alive)
54706
54772
  wallet recover Restore wallet.key from mnemonic (if generated by ClawRouter)
@@ -54735,6 +54801,8 @@ function parseArgs(args) {
54735
54801
  version: false,
54736
54802
  help: false,
54737
54803
  doctor: false,
54804
+ logs: false,
54805
+ logsDays: 1,
54738
54806
  partners: false,
54739
54807
  partnersTest: false,
54740
54808
  report: false,
@@ -54752,6 +54820,12 @@ function parseArgs(args) {
54752
54820
  result.help = true;
54753
54821
  } else if (arg === "doctor" || arg === "--doctor") {
54754
54822
  result.doctor = true;
54823
+ } else if (arg === "logs") {
54824
+ result.logs = true;
54825
+ if (args[i + 1] === "--days" && args[i + 2]) {
54826
+ result.logsDays = parseInt(args[i + 2], 10) || 1;
54827
+ i += 2;
54828
+ }
54755
54829
  } else if (arg === "partners") {
54756
54830
  result.partners = true;
54757
54831
  if (args[i + 1] === "test") {
@@ -54812,6 +54886,11 @@ async function main() {
54812
54886
  await runDoctor(userQuestion, model);
54813
54887
  process.exit(0);
54814
54888
  }
54889
+ if (args.logs) {
54890
+ const output = await formatRecentLogs(args.logsDays);
54891
+ console.log(output);
54892
+ process.exit(0);
54893
+ }
54815
54894
  if (args.partners) {
54816
54895
  if (PARTNER_SERVICES.length === 0) {
54817
54896
  console.log("No partner APIs available.");