@blockrun/clawrouter 0.10.4 → 0.10.6

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
@@ -587,7 +587,7 @@ var DEFAULT_ROUTING_CONFIG = {
587
587
  },
588
588
  scoring: {
589
589
  tokenCountThresholds: { simple: 50, complex: 500 },
590
- // Multilingual keywords: English + Chinese (中文) + Japanese (日本語) + Russian (Русский) + German (Deutsch)
590
+ // Multilingual keywords: EN + ZH + JA + RU + DE + ES + PT + KO + AR
591
591
  codeKeywords: [
592
592
  // English
593
593
  "function",
@@ -641,7 +641,51 @@ var DEFAULT_ROUTING_CONFIG = {
641
641
  "erwarten",
642
642
  "konstante",
643
643
  "variable",
644
- "zur\xFCckgeben"
644
+ "zur\xFCckgeben",
645
+ // Spanish
646
+ "funci\xF3n",
647
+ "clase",
648
+ "importar",
649
+ "definir",
650
+ "consulta",
651
+ "as\xEDncrono",
652
+ "esperar",
653
+ "constante",
654
+ "variable",
655
+ "retornar",
656
+ // Portuguese
657
+ "fun\xE7\xE3o",
658
+ "classe",
659
+ "importar",
660
+ "definir",
661
+ "consulta",
662
+ "ass\xEDncrono",
663
+ "aguardar",
664
+ "constante",
665
+ "vari\xE1vel",
666
+ "retornar",
667
+ // Korean
668
+ "\uD568\uC218",
669
+ "\uD074\uB798\uC2A4",
670
+ "\uAC00\uC838\uC624\uAE30",
671
+ "\uC815\uC758",
672
+ "\uCFFC\uB9AC",
673
+ "\uBE44\uB3D9\uAE30",
674
+ "\uB300\uAE30",
675
+ "\uC0C1\uC218",
676
+ "\uBCC0\uC218",
677
+ "\uBC18\uD658",
678
+ // Arabic
679
+ "\u062F\u0627\u0644\u0629",
680
+ "\u0641\u0626\u0629",
681
+ "\u0627\u0633\u062A\u064A\u0631\u0627\u062F",
682
+ "\u062A\u0639\u0631\u064A\u0641",
683
+ "\u0627\u0633\u062A\u0639\u0644\u0627\u0645",
684
+ "\u063A\u064A\u0631 \u0645\u062A\u0632\u0627\u0645\u0646",
685
+ "\u0627\u0646\u062A\u0638\u0627\u0631",
686
+ "\u062B\u0627\u0628\u062A",
687
+ "\u0645\u062A\u063A\u064A\u0631",
688
+ "\u0625\u0631\u062C\u0627\u0639"
645
689
  ],
646
690
  reasoningKeywords: [
647
691
  // English
@@ -692,7 +736,46 @@ var DEFAULT_ROUTING_CONFIG = {
692
736
  "gedankenkette",
693
737
  "formal",
694
738
  "mathematisch",
695
- "logisch"
739
+ "logisch",
740
+ // Spanish
741
+ "demostrar",
742
+ "teorema",
743
+ "derivar",
744
+ "paso a paso",
745
+ "cadena de pensamiento",
746
+ "formalmente",
747
+ "matem\xE1tico",
748
+ "prueba",
749
+ "l\xF3gicamente",
750
+ // Portuguese
751
+ "provar",
752
+ "teorema",
753
+ "derivar",
754
+ "passo a passo",
755
+ "cadeia de pensamento",
756
+ "formalmente",
757
+ "matem\xE1tico",
758
+ "prova",
759
+ "logicamente",
760
+ // Korean
761
+ "\uC99D\uBA85",
762
+ "\uC815\uB9AC",
763
+ "\uB3C4\uCD9C",
764
+ "\uB2E8\uACC4\uBCC4",
765
+ "\uC0AC\uACE0\uC758 \uC5F0\uC1C4",
766
+ "\uD615\uC2DD\uC801",
767
+ "\uC218\uD559\uC801",
768
+ "\uB17C\uB9AC\uC801",
769
+ // Arabic
770
+ "\u0625\u062B\u0628\u0627\u062A",
771
+ "\u0646\u0638\u0631\u064A\u0629",
772
+ "\u0627\u0634\u062A\u0642\u0627\u0642",
773
+ "\u062E\u0637\u0648\u0629 \u0628\u062E\u0637\u0648\u0629",
774
+ "\u0633\u0644\u0633\u0644\u0629 \u0627\u0644\u062A\u0641\u0643\u064A\u0631",
775
+ "\u0631\u0633\u0645\u064A\u0627\u064B",
776
+ "\u0631\u064A\u0627\u0636\u064A",
777
+ "\u0628\u0631\u0647\u0627\u0646",
778
+ "\u0645\u0646\u0637\u0642\u064A\u0627\u064B"
696
779
  ],
697
780
  simpleKeywords: [
698
781
  // English
@@ -745,7 +828,45 @@ var DEFAULT_ROUTING_CONFIG = {
745
828
  "wie alt",
746
829
  "wer ist",
747
830
  "wann",
748
- "erkl\xE4re"
831
+ "erkl\xE4re",
832
+ // Spanish
833
+ "qu\xE9 es",
834
+ "definir",
835
+ "traducir",
836
+ "hola",
837
+ "s\xED o no",
838
+ "capital de",
839
+ "cu\xE1ntos a\xF1os",
840
+ "qui\xE9n es",
841
+ "cu\xE1ndo",
842
+ // Portuguese
843
+ "o que \xE9",
844
+ "definir",
845
+ "traduzir",
846
+ "ol\xE1",
847
+ "sim ou n\xE3o",
848
+ "capital de",
849
+ "quantos anos",
850
+ "quem \xE9",
851
+ "quando",
852
+ // Korean
853
+ "\uBB34\uC5C7",
854
+ "\uC815\uC758",
855
+ "\uBC88\uC5ED",
856
+ "\uC548\uB155\uD558\uC138\uC694",
857
+ "\uC608 \uB610\uB294 \uC544\uB2C8\uC624",
858
+ "\uC218\uB3C4",
859
+ "\uB204\uAD6C",
860
+ "\uC5B8\uC81C",
861
+ // Arabic
862
+ "\u0645\u0627 \u0647\u0648",
863
+ "\u062A\u0639\u0631\u064A\u0641",
864
+ "\u062A\u0631\u062C\u0645",
865
+ "\u0645\u0631\u062D\u0628\u0627",
866
+ "\u0646\u0639\u0645 \u0623\u0648 \u0644\u0627",
867
+ "\u0639\u0627\u0635\u0645\u0629",
868
+ "\u0645\u0646 \u0647\u0648",
869
+ "\u0645\u062A\u0649"
749
870
  ],
750
871
  technicalKeywords: [
751
872
  // English
@@ -790,7 +911,39 @@ var DEFAULT_ROUTING_CONFIG = {
790
911
  "kubernetes",
791
912
  "mikroservice",
792
913
  "datenbank",
793
- "infrastruktur"
914
+ "infrastruktur",
915
+ // Spanish
916
+ "algoritmo",
917
+ "optimizar",
918
+ "arquitectura",
919
+ "distribuido",
920
+ "microservicio",
921
+ "base de datos",
922
+ "infraestructura",
923
+ // Portuguese
924
+ "algoritmo",
925
+ "otimizar",
926
+ "arquitetura",
927
+ "distribu\xEDdo",
928
+ "microsservi\xE7o",
929
+ "banco de dados",
930
+ "infraestrutura",
931
+ // Korean
932
+ "\uC54C\uACE0\uB9AC\uC998",
933
+ "\uCD5C\uC801\uD654",
934
+ "\uC544\uD0A4\uD14D\uCC98",
935
+ "\uBD84\uC0B0",
936
+ "\uB9C8\uC774\uD06C\uB85C\uC11C\uBE44\uC2A4",
937
+ "\uB370\uC774\uD130\uBCA0\uC774\uC2A4",
938
+ "\uC778\uD504\uB77C",
939
+ // Arabic
940
+ "\u062E\u0648\u0627\u0631\u0632\u0645\u064A\u0629",
941
+ "\u062A\u062D\u0633\u064A\u0646",
942
+ "\u0628\u0646\u064A\u0629",
943
+ "\u0645\u0648\u0632\u0639",
944
+ "\u062E\u062F\u0645\u0629 \u0645\u0635\u063A\u0631\u0629",
945
+ "\u0642\u0627\u0639\u062F\u0629 \u0628\u064A\u0627\u0646\u0627\u062A",
946
+ "\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629"
794
947
  ],
795
948
  creativeKeywords: [
796
949
  // English
@@ -835,7 +988,38 @@ var DEFAULT_ROUTING_CONFIG = {
835
988
  "kreativ",
836
989
  "vorstellen",
837
990
  "schreibe",
838
- "erz\xE4hlung"
991
+ "erz\xE4hlung",
992
+ // Spanish
993
+ "historia",
994
+ "poema",
995
+ "componer",
996
+ "lluvia de ideas",
997
+ "creativo",
998
+ "imaginar",
999
+ "escribe",
1000
+ // Portuguese
1001
+ "hist\xF3ria",
1002
+ "poema",
1003
+ "compor",
1004
+ "criativo",
1005
+ "imaginar",
1006
+ "escreva",
1007
+ // Korean
1008
+ "\uC774\uC57C\uAE30",
1009
+ "\uC2DC",
1010
+ "\uC791\uACE1",
1011
+ "\uBE0C\uB808\uC778\uC2A4\uD1A0\uBC0D",
1012
+ "\uCC3D\uC758\uC801",
1013
+ "\uC0C1\uC0C1",
1014
+ "\uC791\uC131",
1015
+ // Arabic
1016
+ "\u0642\u0635\u0629",
1017
+ "\u0642\u0635\u064A\u062F\u0629",
1018
+ "\u062A\u0623\u0644\u064A\u0641",
1019
+ "\u0639\u0635\u0641 \u0630\u0647\u0646\u064A",
1020
+ "\u0625\u0628\u062F\u0627\u0639\u064A",
1021
+ "\u062A\u062E\u064A\u0644",
1022
+ "\u0627\u0643\u062A\u0628"
839
1023
  ],
840
1024
  // New dimension keyword lists (multilingual)
841
1025
  imperativeVerbs: [
@@ -896,7 +1080,42 @@ var DEFAULT_ROUTING_CONFIG = {
896
1080
  "generieren",
897
1081
  "bereitstellen",
898
1082
  "konfigurieren",
899
- "einrichten"
1083
+ "einrichten",
1084
+ // Spanish
1085
+ "construir",
1086
+ "crear",
1087
+ "implementar",
1088
+ "dise\xF1ar",
1089
+ "desarrollar",
1090
+ "generar",
1091
+ "desplegar",
1092
+ "configurar",
1093
+ // Portuguese
1094
+ "construir",
1095
+ "criar",
1096
+ "implementar",
1097
+ "projetar",
1098
+ "desenvolver",
1099
+ "gerar",
1100
+ "implantar",
1101
+ "configurar",
1102
+ // Korean
1103
+ "\uAD6C\uCD95",
1104
+ "\uC0DD\uC131",
1105
+ "\uAD6C\uD604",
1106
+ "\uC124\uACC4",
1107
+ "\uAC1C\uBC1C",
1108
+ "\uBC30\uD3EC",
1109
+ "\uC124\uC815",
1110
+ // Arabic
1111
+ "\u0628\u0646\u0627\u0621",
1112
+ "\u0625\u0646\u0634\u0627\u0621",
1113
+ "\u062A\u0646\u0641\u064A\u0630",
1114
+ "\u062A\u0635\u0645\u064A\u0645",
1115
+ "\u062A\u0637\u0648\u064A\u0631",
1116
+ "\u062A\u0648\u0644\u064A\u062F",
1117
+ "\u0646\u0634\u0631",
1118
+ "\u0625\u0639\u062F\u0627\u062F"
900
1119
  ],
901
1120
  constraintIndicators: [
902
1121
  // English
@@ -942,7 +1161,41 @@ var DEFAULT_ROUTING_CONFIG = {
942
1161
  "maximal",
943
1162
  "minimal",
944
1163
  "grenze",
945
- "budget"
1164
+ "budget",
1165
+ // Spanish
1166
+ "como m\xE1ximo",
1167
+ "al menos",
1168
+ "dentro de",
1169
+ "no m\xE1s de",
1170
+ "m\xE1ximo",
1171
+ "m\xEDnimo",
1172
+ "l\xEDmite",
1173
+ "presupuesto",
1174
+ // Portuguese
1175
+ "no m\xE1ximo",
1176
+ "pelo menos",
1177
+ "dentro de",
1178
+ "n\xE3o mais que",
1179
+ "m\xE1ximo",
1180
+ "m\xEDnimo",
1181
+ "limite",
1182
+ "or\xE7amento",
1183
+ // Korean
1184
+ "\uC774\uD558",
1185
+ "\uC774\uC0C1",
1186
+ "\uCD5C\uB300",
1187
+ "\uCD5C\uC18C",
1188
+ "\uC81C\uD55C",
1189
+ "\uC608\uC0B0",
1190
+ // Arabic
1191
+ "\u0639\u0644\u0649 \u0627\u0644\u0623\u0643\u062B\u0631",
1192
+ "\u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644",
1193
+ "\u0636\u0645\u0646",
1194
+ "\u0644\u0627 \u064A\u0632\u064A\u062F \u0639\u0646",
1195
+ "\u0623\u0642\u0635\u0649",
1196
+ "\u0623\u062F\u0646\u0649",
1197
+ "\u062D\u062F",
1198
+ "\u0645\u064A\u0632\u0627\u0646\u064A\u0629"
946
1199
  ],
947
1200
  outputFormatKeywords: [
948
1201
  // English
@@ -970,7 +1223,23 @@ var DEFAULT_ROUTING_CONFIG = {
970
1223
  // German
971
1224
  "tabelle",
972
1225
  "formatieren als",
973
- "strukturiert"
1226
+ "strukturiert",
1227
+ // Spanish
1228
+ "tabla",
1229
+ "formatear como",
1230
+ "estructurado",
1231
+ // Portuguese
1232
+ "tabela",
1233
+ "formatar como",
1234
+ "estruturado",
1235
+ // Korean
1236
+ "\uD14C\uC774\uBE14",
1237
+ "\uD615\uC2DD",
1238
+ "\uAD6C\uC870\uD654",
1239
+ // Arabic
1240
+ "\u062C\u062F\u0648\u0644",
1241
+ "\u062A\u0646\u0633\u064A\u0642",
1242
+ "\u0645\u0646\u0638\u0645"
974
1243
  ],
975
1244
  referenceKeywords: [
976
1245
  // English
@@ -1015,7 +1284,39 @@ var DEFAULT_ROUTING_CONFIG = {
1015
1284
  "dokumentation",
1016
1285
  "der code",
1017
1286
  "fr\xFCher",
1018
- "anhang"
1287
+ "anhang",
1288
+ // Spanish
1289
+ "arriba",
1290
+ "abajo",
1291
+ "anterior",
1292
+ "siguiente",
1293
+ "documentaci\xF3n",
1294
+ "el c\xF3digo",
1295
+ "adjunto",
1296
+ // Portuguese
1297
+ "acima",
1298
+ "abaixo",
1299
+ "anterior",
1300
+ "seguinte",
1301
+ "documenta\xE7\xE3o",
1302
+ "o c\xF3digo",
1303
+ "anexo",
1304
+ // Korean
1305
+ "\uC704",
1306
+ "\uC544\uB798",
1307
+ "\uC774\uC804",
1308
+ "\uB2E4\uC74C",
1309
+ "\uBB38\uC11C",
1310
+ "\uCF54\uB4DC",
1311
+ "\uCCA8\uBD80",
1312
+ // Arabic
1313
+ "\u0623\u0639\u0644\u0627\u0647",
1314
+ "\u0623\u062F\u0646\u0627\u0647",
1315
+ "\u0627\u0644\u0633\u0627\u0628\u0642",
1316
+ "\u0627\u0644\u062A\u0627\u0644\u064A",
1317
+ "\u0627\u0644\u0648\u062B\u0627\u0626\u0642",
1318
+ "\u0627\u0644\u0643\u0648\u062F",
1319
+ "\u0645\u0631\u0641\u0642"
1019
1320
  ],
1020
1321
  negationKeywords: [
1021
1322
  // English
@@ -1057,7 +1358,34 @@ var DEFAULT_ROUTING_CONFIG = {
1057
1358
  "ohne",
1058
1359
  "au\xDFer",
1059
1360
  "ausschlie\xDFen",
1060
- "nicht mehr"
1361
+ "nicht mehr",
1362
+ // Spanish
1363
+ "no hagas",
1364
+ "evitar",
1365
+ "nunca",
1366
+ "sin",
1367
+ "excepto",
1368
+ "excluir",
1369
+ // Portuguese
1370
+ "n\xE3o fa\xE7a",
1371
+ "evitar",
1372
+ "nunca",
1373
+ "sem",
1374
+ "exceto",
1375
+ "excluir",
1376
+ // Korean
1377
+ "\uD558\uC9C0 \uB9C8",
1378
+ "\uD53C\uD558\uB2E4",
1379
+ "\uC808\uB300",
1380
+ "\uC5C6\uC774",
1381
+ "\uC81C\uC678",
1382
+ // Arabic
1383
+ "\u0644\u0627 \u062A\u0641\u0639\u0644",
1384
+ "\u062A\u062C\u0646\u0628",
1385
+ "\u0623\u0628\u062F\u0627\u064B",
1386
+ "\u0628\u062F\u0648\u0646",
1387
+ "\u0628\u0627\u0633\u062A\u062B\u0646\u0627\u0621",
1388
+ "\u0627\u0633\u062A\u0628\u0639\u0627\u062F"
1061
1389
  ],
1062
1390
  domainSpecificKeywords: [
1063
1391
  // English
@@ -1104,7 +1432,33 @@ var DEFAULT_ROUTING_CONFIG = {
1104
1432
  "topologisch",
1105
1433
  "homomorph",
1106
1434
  "zero-knowledge",
1107
- "gitterbasiert"
1435
+ "gitterbasiert",
1436
+ // Spanish
1437
+ "cu\xE1ntico",
1438
+ "fot\xF3nica",
1439
+ "gen\xF3mica",
1440
+ "prote\xF3mica",
1441
+ "topol\xF3gico",
1442
+ "homom\xF3rfico",
1443
+ // Portuguese
1444
+ "qu\xE2ntico",
1445
+ "fot\xF4nica",
1446
+ "gen\xF4mica",
1447
+ "prote\xF4mica",
1448
+ "topol\xF3gico",
1449
+ "homom\xF3rfico",
1450
+ // Korean
1451
+ "\uC591\uC790",
1452
+ "\uD3EC\uD1A0\uB2C9\uC2A4",
1453
+ "\uC720\uC804\uCCB4\uD559",
1454
+ "\uC704\uC0C1",
1455
+ "\uB3D9\uD615",
1456
+ // Arabic
1457
+ "\u0643\u0645\u064A",
1458
+ "\u0636\u0648\u0626\u064A\u0627\u062A",
1459
+ "\u062C\u064A\u0646\u0648\u0645\u064A\u0627\u062A",
1460
+ "\u0637\u0648\u0628\u0648\u0644\u0648\u062C\u064A",
1461
+ "\u062A\u0645\u0627\u062B\u0644\u064A"
1108
1462
  ],
1109
1463
  // Agentic task keywords - file ops, execution, multi-step, iterative work
1110
1464
  // Pruned: removed overly common words like "then", "first", "run", "test", "build"
@@ -1160,7 +1514,58 @@ var DEFAULT_ROUTING_CONFIG = {
1160
1514
  "\u8C03\u8BD5",
1161
1515
  "\u76F4\u5230",
1162
1516
  "\u786E\u8BA4",
1163
- "\u9A8C\u8BC1"
1517
+ "\u9A8C\u8BC1",
1518
+ // Spanish
1519
+ "leer archivo",
1520
+ "editar",
1521
+ "modificar",
1522
+ "actualizar",
1523
+ "ejecutar",
1524
+ "desplegar",
1525
+ "instalar",
1526
+ "paso 1",
1527
+ "paso 2",
1528
+ "arreglar",
1529
+ "depurar",
1530
+ "verificar",
1531
+ // Portuguese
1532
+ "ler arquivo",
1533
+ "editar",
1534
+ "modificar",
1535
+ "atualizar",
1536
+ "executar",
1537
+ "implantar",
1538
+ "instalar",
1539
+ "passo 1",
1540
+ "passo 2",
1541
+ "corrigir",
1542
+ "depurar",
1543
+ "verificar",
1544
+ // Korean
1545
+ "\uD30C\uC77C \uC77D\uAE30",
1546
+ "\uD3B8\uC9D1",
1547
+ "\uC218\uC815",
1548
+ "\uC5C5\uB370\uC774\uD2B8",
1549
+ "\uC2E4\uD589",
1550
+ "\uBC30\uD3EC",
1551
+ "\uC124\uCE58",
1552
+ "\uB2E8\uACC4 1",
1553
+ "\uB2E8\uACC4 2",
1554
+ "\uB514\uBC84\uADF8",
1555
+ "\uD655\uC778",
1556
+ // Arabic
1557
+ "\u0642\u0631\u0627\u0621\u0629 \u0645\u0644\u0641",
1558
+ "\u062A\u062D\u0631\u064A\u0631",
1559
+ "\u062A\u0639\u062F\u064A\u0644",
1560
+ "\u062A\u062D\u062F\u064A\u062B",
1561
+ "\u062A\u0646\u0641\u064A\u0630",
1562
+ "\u0646\u0634\u0631",
1563
+ "\u062A\u062B\u0628\u064A\u062A",
1564
+ "\u0627\u0644\u062E\u0637\u0648\u0629 1",
1565
+ "\u0627\u0644\u062E\u0637\u0648\u0629 2",
1566
+ "\u0625\u0635\u0644\u0627\u062D",
1567
+ "\u062A\u0635\u062D\u064A\u062D",
1568
+ "\u062A\u062D\u0642\u0642"
1164
1569
  ],
1165
1570
  // Dimension weights (sum to 1.0)
1166
1571
  dimensionWeights: {
@@ -4190,6 +4595,63 @@ function estimateAmount(modelId, bodyLength, maxTokens) {
4190
4595
  const amountMicros = Math.max(100, Math.ceil(costUsd * 1.2 * 1e6));
4191
4596
  return amountMicros.toString();
4192
4597
  }
4598
+ async function proxyPartnerRequest(req, res, apiBase, payFetch) {
4599
+ const startTime = Date.now();
4600
+ const upstreamUrl = `${apiBase}${req.url}`;
4601
+ const bodyChunks = [];
4602
+ for await (const chunk of req) {
4603
+ bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4604
+ }
4605
+ const body = Buffer.concat(bodyChunks);
4606
+ const headers = {};
4607
+ for (const [key, value] of Object.entries(req.headers)) {
4608
+ if (key === "host" || key === "connection" || key === "transfer-encoding" || key === "content-length")
4609
+ continue;
4610
+ if (typeof value === "string") headers[key] = value;
4611
+ }
4612
+ if (!headers["content-type"]) headers["content-type"] = "application/json";
4613
+ headers["user-agent"] = USER_AGENT;
4614
+ console.log(`[ClawRouter] Partner request: ${req.method} ${req.url}`);
4615
+ const upstream = await payFetch(upstreamUrl, {
4616
+ method: req.method ?? "POST",
4617
+ headers,
4618
+ body: body.length > 0 ? new Uint8Array(body) : void 0
4619
+ });
4620
+ const responseHeaders = {};
4621
+ upstream.headers.forEach((value, key) => {
4622
+ if (key === "transfer-encoding" || key === "connection" || key === "content-encoding") return;
4623
+ responseHeaders[key] = value;
4624
+ });
4625
+ res.writeHead(upstream.status, responseHeaders);
4626
+ if (upstream.body) {
4627
+ const reader = upstream.body.getReader();
4628
+ try {
4629
+ while (true) {
4630
+ const { done, value } = await reader.read();
4631
+ if (done) break;
4632
+ safeWrite(res, Buffer.from(value));
4633
+ }
4634
+ } finally {
4635
+ reader.releaseLock();
4636
+ }
4637
+ }
4638
+ res.end();
4639
+ const latencyMs = Date.now() - startTime;
4640
+ console.log(`[ClawRouter] Partner response: ${upstream.status} (${latencyMs}ms)`);
4641
+ logUsage({
4642
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4643
+ model: "partner",
4644
+ tier: "PARTNER",
4645
+ cost: 0,
4646
+ // Actual cost handled by x402 settlement
4647
+ baselineCost: 0,
4648
+ savings: 0,
4649
+ latencyMs,
4650
+ partnerId: (req.url?.split("?")[0] ?? "").replace(/^\/v1\//, "").replace(/\//g, "_") || "unknown",
4651
+ service: "partner"
4652
+ }).catch(() => {
4653
+ });
4654
+ }
4193
4655
  async function startProxy(options) {
4194
4656
  const apiBase = options.apiBase ?? BLOCKRUN_API;
4195
4657
  const listenPort = options.port ?? getProxyPort();
@@ -4300,6 +4762,23 @@ async function startProxy(options) {
4300
4762
  res.end(JSON.stringify({ object: "list", data: models }));
4301
4763
  return;
4302
4764
  }
4765
+ if (req.url?.match(/^\/v1\/(?:x|partner)\//)) {
4766
+ try {
4767
+ await proxyPartnerRequest(req, res, apiBase, payFetch);
4768
+ } catch (err) {
4769
+ const error = err instanceof Error ? err : new Error(String(err));
4770
+ options.onError?.(error);
4771
+ if (!res.headersSent) {
4772
+ res.writeHead(502, { "Content-Type": "application/json" });
4773
+ res.end(
4774
+ JSON.stringify({
4775
+ error: { message: `Partner proxy error: ${error.message}`, type: "partner_error" }
4776
+ })
4777
+ );
4778
+ }
4779
+ }
4780
+ return;
4781
+ }
4303
4782
  if (!req.url?.startsWith("/v1")) {
4304
4783
  res.writeHead(404, { "Content-Type": "application/json" });
4305
4784
  res.end(JSON.stringify({ error: "Not found" }));
@@ -5162,10 +5641,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5162
5641
  }
5163
5642
  throw err;
5164
5643
  }
5165
- if (routingDecision) {
5644
+ const logModel = routingDecision?.model ?? modelId;
5645
+ if (logModel) {
5166
5646
  const estimatedInputTokens = Math.ceil(body.length / 4);
5167
5647
  const accurateCosts = calculateModelCost(
5168
- routingDecision.model,
5648
+ logModel,
5169
5649
  routerOpts.modelPricing,
5170
5650
  estimatedInputTokens,
5171
5651
  maxTokens,
@@ -5175,8 +5655,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5175
5655
  const baselineWithBuffer = accurateCosts.baselineCost * 1.2;
5176
5656
  const entry = {
5177
5657
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5178
- model: routingDecision.model,
5179
- tier: routingDecision.tier,
5658
+ model: logModel,
5659
+ tier: routingDecision?.tier ?? "DIRECT",
5180
5660
  cost: costWithBuffer,
5181
5661
  baselineCost: baselineWithBuffer,
5182
5662
  savings: accurateCosts.savings,
@@ -5387,7 +5867,9 @@ function printDiagnostics(result) {
5387
5867
  console.log("System");
5388
5868
  console.log(` ${green(`OS: ${result.system.os}`)}`);
5389
5869
  console.log(` ${green(`Node: ${result.system.nodeVersion}`)}`);
5390
- console.log(` ${green(`Memory: ${result.system.memoryFree} free / ${result.system.memoryTotal}`)}`);
5870
+ console.log(
5871
+ ` ${green(`Memory: ${result.system.memoryFree} free / ${result.system.memoryTotal}`)}`
5872
+ );
5391
5873
  console.log("\nWallet");
5392
5874
  if (result.wallet.exists && result.wallet.valid) {
5393
5875
  console.log(` ${green(`Key: ${WALLET_FILE} (${result.wallet.source})`)}`);
@@ -5406,7 +5888,9 @@ function printDiagnostics(result) {
5406
5888
  }
5407
5889
  console.log("\nNetwork");
5408
5890
  if (result.network.blockrunApi.reachable) {
5409
- console.log(` ${green(`BlockRun API: reachable (${result.network.blockrunApi.latencyMs}ms)`)}`);
5891
+ console.log(
5892
+ ` ${green(`BlockRun API: reachable (${result.network.blockrunApi.latencyMs}ms)`)}`
5893
+ );
5410
5894
  } else {
5411
5895
  console.log(` ${red("BlockRun API: unreachable")}`);
5412
5896
  }
@@ -5416,7 +5900,9 @@ function printDiagnostics(result) {
5416
5900
  console.log(` ${red(`Local proxy: not running on :${result.network.localProxy.port}`)}`);
5417
5901
  }
5418
5902
  console.log("\nLogs");
5419
- console.log(` ${green(`Last 24h: ${result.logs.requestsLast24h} requests, ${result.logs.costLast24h} spent`)}`);
5903
+ console.log(
5904
+ ` ${green(`Last 24h: ${result.logs.requestsLast24h} requests, ${result.logs.costLast24h} spent`)}`
5905
+ );
5420
5906
  if (result.logs.errorsFound > 0) {
5421
5907
  console.log(` ${yellow(`${result.logs.errorsFound} errors found in logs`)}`);
5422
5908
  }
@@ -5535,6 +6021,36 @@ async function runDoctor(userQuestion, model = "sonnet") {
5535
6021
  await analyzeWithAI(result, userQuestion, model);
5536
6022
  }
5537
6023
 
6024
+ // src/partners/registry.ts
6025
+ var PARTNER_SERVICES = [
6026
+ {
6027
+ id: "x_users_lookup",
6028
+ name: "Twitter/X User Lookup",
6029
+ partner: "AttentionVC",
6030
+ description: "Look up Twitter/X user profiles by username. Returns follower counts, verification status, bio, and more. Accepts up to 100 usernames per request.",
6031
+ proxyPath: "/x/users/lookup",
6032
+ method: "POST",
6033
+ params: [
6034
+ {
6035
+ name: "usernames",
6036
+ type: "string[]",
6037
+ description: 'Array of Twitter/X usernames to look up (without @ prefix). Example: ["elonmusk", "naval"]',
6038
+ required: true
6039
+ }
6040
+ ],
6041
+ pricing: {
6042
+ perUnit: "$0.001",
6043
+ unit: "user",
6044
+ minimum: "$0.01 (10 users)",
6045
+ maximum: "$0.10 (100 users)"
6046
+ },
6047
+ example: {
6048
+ input: { usernames: ["elonmusk", "naval", "balaboris"] },
6049
+ description: "Look up 3 Twitter/X user profiles"
6050
+ }
6051
+ }
6052
+ ];
6053
+
5538
6054
  // src/cli.ts
5539
6055
  function printHelp() {
5540
6056
  console.log(`
@@ -5543,6 +6059,7 @@ ClawRouter v${VERSION} - Smart LLM Router
5543
6059
  Usage:
5544
6060
  clawrouter [options]
5545
6061
  clawrouter doctor [opus] [question]
6062
+ clawrouter partners [test]
5546
6063
 
5547
6064
  Options:
5548
6065
  --version, -v Show version number
@@ -5552,6 +6069,8 @@ Options:
5552
6069
  Commands:
5553
6070
  doctor AI-powered diagnostics (default: Sonnet ~$0.003)
5554
6071
  doctor opus Use Opus for deeper analysis (~$0.01)
6072
+ partners List available partner APIs with pricing
6073
+ partners test Test partner API endpoints (expect 402 = alive)
5555
6074
 
5556
6075
  Examples:
5557
6076
  # Start standalone proxy
@@ -5577,7 +6096,14 @@ For more info: https://github.com/BlockRunAI/ClawRouter
5577
6096
  `);
5578
6097
  }
5579
6098
  function parseArgs(args) {
5580
- const result = { version: false, help: false, doctor: false, port: void 0 };
6099
+ const result = {
6100
+ version: false,
6101
+ help: false,
6102
+ doctor: false,
6103
+ partners: false,
6104
+ partnersTest: false,
6105
+ port: void 0
6106
+ };
5581
6107
  for (let i = 0; i < args.length; i++) {
5582
6108
  const arg = args[i];
5583
6109
  if (arg === "--version" || arg === "-v") {
@@ -5586,6 +6112,12 @@ function parseArgs(args) {
5586
6112
  result.help = true;
5587
6113
  } else if (arg === "doctor" || arg === "--doctor") {
5588
6114
  result.doctor = true;
6115
+ } else if (arg === "partners") {
6116
+ result.partners = true;
6117
+ if (args[i + 1] === "test") {
6118
+ result.partnersTest = true;
6119
+ i++;
6120
+ }
5589
6121
  } else if (arg === "--port" && args[i + 1]) {
5590
6122
  result.port = parseInt(args[i + 1], 10);
5591
6123
  i++;
@@ -5620,6 +6152,40 @@ async function main() {
5620
6152
  await runDoctor(userQuestion, model);
5621
6153
  process.exit(0);
5622
6154
  }
6155
+ if (args.partners) {
6156
+ if (PARTNER_SERVICES.length === 0) {
6157
+ console.log("No partner APIs available.");
6158
+ process.exit(0);
6159
+ }
6160
+ console.log(`
6161
+ ClawRouter Partner APIs (v${VERSION})
6162
+ `);
6163
+ for (const svc of PARTNER_SERVICES) {
6164
+ console.log(` ${svc.name} (${svc.partner})`);
6165
+ console.log(` ${svc.description}`);
6166
+ console.log(` Tool: blockrun_${svc.id}`);
6167
+ console.log(` Method: ${svc.method} /v1${svc.proxyPath}`);
6168
+ console.log(` Pricing: ${svc.pricing.perUnit} per ${svc.pricing.unit} (min ${svc.pricing.minimum}, max ${svc.pricing.maximum})`);
6169
+ console.log();
6170
+ }
6171
+ if (args.partnersTest) {
6172
+ console.log("Testing partner endpoints...\n");
6173
+ const apiBase = "https://blockrun.ai/api";
6174
+ for (const svc of PARTNER_SERVICES) {
6175
+ const url = `${apiBase}/v1${svc.proxyPath}`;
6176
+ try {
6177
+ const response = await fetch(url, { method: "GET" });
6178
+ const status = response.status;
6179
+ const ok = status === 402 ? "alive (402 = payment required)" : `status ${status}`;
6180
+ console.log(` ${svc.id}: ${ok}`);
6181
+ } catch (err) {
6182
+ console.log(` ${svc.id}: error - ${err instanceof Error ? err.message : String(err)}`);
6183
+ }
6184
+ }
6185
+ console.log();
6186
+ }
6187
+ process.exit(0);
6188
+ }
5623
6189
  const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();
5624
6190
  if (source === "generated") {
5625
6191
  console.log(`[ClawRouter] Generated new wallet: ${address}`);