chenbhao commited on
Commit
157862d
·
1 Parent(s): 04fbbc8

fix: web search and fetch

Browse files
bun.lock CHANGED
@@ -45,7 +45,6 @@
45
  "ajv": "^8.18.0",
46
  "asciichart": "^1.5.25",
47
  "auto-bind": "^5.0.1",
48
- "axios": "^1.14.0",
49
  "bidi-js": "^1.0.3",
50
  "cacache": "^20.0.4",
51
  "chalk": "^5.6.2",
@@ -514,12 +513,8 @@
514
 
515
  "asciichart": ["asciichart@1.5.25", "", {}, "sha512-PNxzXIPPOtWq8T7bgzBtk9cI2lgS4SJZthUHEiQ1aoIc3lNzGfUvIvo9LiAnq26TACo9t1/4qP6KTGAUbzX9Xg=="],
516
 
517
- "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
518
-
519
  "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
520
 
521
- "axios": ["axios@1.14.0", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ=="],
522
-
523
  "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
524
 
525
  "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
@@ -574,8 +569,6 @@
574
 
575
  "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
576
 
577
- "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
578
-
579
  "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
580
 
581
  "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
@@ -606,8 +599,6 @@
606
 
607
  "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
608
 
609
- "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
610
-
611
  "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
612
 
613
  "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
@@ -638,8 +629,6 @@
638
 
639
  "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
640
 
641
- "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
642
-
643
  "es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="],
644
 
645
  "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@@ -684,10 +673,6 @@
684
 
685
  "flora-colossus": ["flora-colossus@2.0.0", "", { "dependencies": { "debug": "^4.3.4", "fs-extra": "^10.1.0" } }, "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA=="],
686
 
687
- "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
688
-
689
- "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
690
-
691
  "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
692
 
693
  "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
@@ -734,8 +719,6 @@
734
 
735
  "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
736
 
737
- "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
738
-
739
  "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
740
 
741
  "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
@@ -932,8 +915,6 @@
932
 
933
  "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
934
 
935
- "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
936
-
937
  "qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
938
 
939
  "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
@@ -1406,8 +1387,6 @@
1406
 
1407
  "external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
1408
 
1409
- "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
1410
-
1411
  "gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
1412
 
1413
  "gtoken/gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
@@ -1556,8 +1535,6 @@
1556
 
1557
  "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
1558
 
1559
- "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
1560
-
1561
  "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
1562
 
1563
  "gtoken/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
 
45
  "ajv": "^8.18.0",
46
  "asciichart": "^1.5.25",
47
  "auto-bind": "^5.0.1",
 
48
  "bidi-js": "^1.0.3",
49
  "cacache": "^20.0.4",
50
  "chalk": "^5.6.2",
 
513
 
514
  "asciichart": ["asciichart@1.5.25", "", {}, "sha512-PNxzXIPPOtWq8T7bgzBtk9cI2lgS4SJZthUHEiQ1aoIc3lNzGfUvIvo9LiAnq26TACo9t1/4qP6KTGAUbzX9Xg=="],
515
 
 
 
516
  "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
517
 
 
 
518
  "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
519
 
520
  "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
 
569
 
570
  "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
571
 
 
 
572
  "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
573
 
574
  "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
 
599
 
600
  "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
601
 
 
 
602
  "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
603
 
604
  "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
 
629
 
630
  "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
631
 
 
 
632
  "es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="],
633
 
634
  "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
 
673
 
674
  "flora-colossus": ["flora-colossus@2.0.0", "", { "dependencies": { "debug": "^4.3.4", "fs-extra": "^10.1.0" } }, "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA=="],
675
 
 
 
 
 
676
  "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
677
 
678
  "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
 
719
 
720
  "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
721
 
 
 
722
  "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
723
 
724
  "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
 
915
 
916
  "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
917
 
 
 
918
  "qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
919
 
920
  "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
 
1387
 
1388
  "external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
1389
 
 
 
1390
  "gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
1391
 
1392
  "gtoken/gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
 
1535
 
1536
  "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
1537
 
 
 
1538
  "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
1539
 
1540
  "gtoken/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
package.json CHANGED
@@ -60,7 +60,6 @@
60
  "ajv": "^8.18.0",
61
  "asciichart": "^1.5.25",
62
  "auto-bind": "^5.0.1",
63
- "axios": "^1.14.0",
64
  "bidi-js": "^1.0.3",
65
  "cacache": "^20.0.4",
66
  "chalk": "^5.6.2",
 
60
  "ajv": "^8.18.0",
61
  "asciichart": "^1.5.25",
62
  "auto-bind": "^5.0.1",
 
63
  "bidi-js": "^1.0.3",
64
  "cacache": "^20.0.4",
65
  "chalk": "^5.6.2",
src/tools/WebFetchTool/utils.ts CHANGED
@@ -1,4 +1,3 @@
1
- import axios, { type AxiosResponse } from 'axios'
2
  import { LRUCache } from 'lru-cache'
3
  import {
4
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
@@ -47,6 +46,28 @@ class EgressBlockedError extends Error {
47
  }
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  // Cache for storing fetched URL content
51
  type CacheEntry = {
52
  bytes: number
@@ -180,12 +201,13 @@ export async function checkDomainBlocklist(
180
  return { status: 'allowed' }
181
  }
182
  try {
183
- const response = await axios.get(
184
  `https://api.anthropic.com/api/web/domain_info?domain=${encodeURIComponent(domain)}`,
185
  { timeout: DOMAIN_CHECK_TIMEOUT_MS },
186
  )
187
  if (response.status === 200) {
188
- if (response.data.can_fetch === true) {
 
189
  DOMAIN_CHECK_CACHE.set(domain, true)
190
  return { status: 'allowed' }
191
  }
@@ -264,29 +286,24 @@ export async function getWithPermittedRedirects(
264
  signal: AbortSignal,
265
  redirectChecker: (originalUrl: string, redirectUrl: string) => boolean,
266
  depth = 0,
267
- ): Promise<AxiosResponse<ArrayBuffer> | RedirectInfo> {
268
  if (depth > MAX_REDIRECTS) {
269
  throw new Error(`Too many redirects (exceeded ${MAX_REDIRECTS})`)
270
  }
271
  try {
272
- return await axios.get(url, {
273
  signal,
274
  timeout: FETCH_TIMEOUT_MS,
275
- maxRedirects: 0,
276
- responseType: 'arraybuffer',
277
- maxContentLength: MAX_HTTP_CONTENT_LENGTH,
278
  headers: {
279
  Accept: 'text/markdown, text/html, */*',
280
  'User-Agent': getWebFetchUserAgent(),
281
  },
282
  })
283
- } catch (error) {
284
- if (
285
- axios.isAxiosError(error) &&
286
- error.response &&
287
- [301, 302, 307, 308].includes(error.response.status)
288
- ) {
289
- const redirectLocation = error.response.headers.location
290
  if (!redirectLocation) {
291
  throw new Error('Redirect missing Location header')
292
  }
@@ -308,28 +325,35 @@ export async function getWithPermittedRedirects(
308
  type: 'redirect',
309
  originalUrl: url,
310
  redirectUrl,
311
- statusCode: error.response.status,
312
  }
313
  }
314
  }
315
 
316
- // Detect egress proxy blocks: the proxy returns 403 with
317
- // X-Proxy-Error: blocked-by-allowlist when egress is restricted
318
- if (
319
- axios.isAxiosError(error) &&
320
- error.response?.status === 403 &&
321
- error.response.headers['x-proxy-error'] === 'blocked-by-allowlist'
322
- ) {
323
  const hostname = new URL(url).hostname
324
  throw new EgressBlockedError(hostname)
325
  }
326
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  throw error
328
  }
329
  }
330
 
331
  function isRedirectInfo(
332
- response: AxiosResponse<ArrayBuffer> | RedirectInfo,
333
  ): response is RedirectInfo {
334
  return 'type' in response && response.type === 'redirect'
335
  }
@@ -425,12 +449,8 @@ export async function getURLMarkdownContent(
425
  return response
426
  }
427
 
428
- const rawBuffer = Buffer.from(response.data)
429
- // Release the axios-held ArrayBuffer copy; rawBuffer owns the bytes now.
430
- // This lets GC reclaim up to MAX_HTTP_CONTENT_LENGTH (10MB) before Turndown
431
- // builds its DOM tree (which can be 3-5x the HTML size).
432
- ;(response as { data: unknown }).data = null
433
- const contentType = response.headers['content-type'] ?? ''
434
 
435
  // Binary content: save raw bytes to disk with a proper extension so Claude
436
  // can inspect the file later. We still fall through to the utf-8 decode +
 
 
1
  import { LRUCache } from 'lru-cache'
2
  import {
3
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
 
46
  }
47
  }
48
 
49
+ /**
50
+ * Fetch with timeout support using AbortSignal
51
+ */
52
+ async function fetchWithTimeout(
53
+ url: string,
54
+ options: RequestInit & { timeout?: number } = {},
55
+ ): Promise<Response> {
56
+ const { timeout = 30000, ...fetchOptions } = options
57
+ const controller = new AbortController()
58
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
59
+
60
+ try {
61
+ const response = await fetch(url, {
62
+ ...fetchOptions,
63
+ signal: controller.signal,
64
+ })
65
+ return response
66
+ } finally {
67
+ clearTimeout(timeoutId)
68
+ }
69
+ }
70
+
71
  // Cache for storing fetched URL content
72
  type CacheEntry = {
73
  bytes: number
 
201
  return { status: 'allowed' }
202
  }
203
  try {
204
+ const response = await fetchWithTimeout(
205
  `https://api.anthropic.com/api/web/domain_info?domain=${encodeURIComponent(domain)}`,
206
  { timeout: DOMAIN_CHECK_TIMEOUT_MS },
207
  )
208
  if (response.status === 200) {
209
+ const data = await response.json()
210
+ if (data.can_fetch === true) {
211
  DOMAIN_CHECK_CACHE.set(domain, true)
212
  return { status: 'allowed' }
213
  }
 
286
  signal: AbortSignal,
287
  redirectChecker: (originalUrl: string, redirectUrl: string) => boolean,
288
  depth = 0,
289
+ ): Promise<Response | RedirectInfo> {
290
  if (depth > MAX_REDIRECTS) {
291
  throw new Error(`Too many redirects (exceeded ${MAX_REDIRECTS})`)
292
  }
293
  try {
294
+ const response = await fetchWithTimeout(url, {
295
  signal,
296
  timeout: FETCH_TIMEOUT_MS,
297
+ redirect: 'manual', // Handle redirects manually
 
 
298
  headers: {
299
  Accept: 'text/markdown, text/html, */*',
300
  'User-Agent': getWebFetchUserAgent(),
301
  },
302
  })
303
+
304
+ // Check for redirect status codes
305
+ if ([301, 302, 307, 308].includes(response.status)) {
306
+ const redirectLocation = response.headers.get('location')
 
 
 
307
  if (!redirectLocation) {
308
  throw new Error('Redirect missing Location header')
309
  }
 
325
  type: 'redirect',
326
  originalUrl: url,
327
  redirectUrl,
328
+ statusCode: response.status,
329
  }
330
  }
331
  }
332
 
333
+ // Check for egress proxy blocks
334
+ if (response.status === 403 && response.headers.get('x-proxy-error') === 'blocked-by-allowlist') {
 
 
 
 
 
335
  const hostname = new URL(url).hostname
336
  throw new EgressBlockedError(hostname)
337
  }
338
 
339
+ return response
340
+ } catch (error) {
341
+ // Re-throw custom errors
342
+ if (error instanceof EgressBlockedError) {
343
+ throw error
344
+ }
345
+
346
+ // Handle abort errors
347
+ if (error instanceof Error && error.name === 'AbortError') {
348
+ throw new AbortError()
349
+ }
350
+
351
  throw error
352
  }
353
  }
354
 
355
  function isRedirectInfo(
356
+ response: Response | RedirectInfo,
357
  ): response is RedirectInfo {
358
  return 'type' in response && response.type === 'redirect'
359
  }
 
449
  return response
450
  }
451
 
452
+ const rawBuffer = Buffer.from(await response.arrayBuffer())
453
+ const contentType = response.headers.get('content-type') ?? ''
 
 
 
 
454
 
455
  // Binary content: save raw bytes to disk with a proper extension so Claude
456
  // can inspect the file later. We still fall through to the utf-8 decode +
src/tools/WebSearchTool/WebSearchTool.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
2
  import { z } from 'zod/v4'
3
  import { buildTool, type ToolDef } from '../../Tool.js'
4
  import { lazySchema } from '../../utils/lazySchema.js'
@@ -33,6 +33,7 @@ const searchResultSchema = lazySchema(() => {
33
  const searchHitSchema = z.object({
34
  title: z.string().describe('The title of the search result'),
35
  url: z.string().describe('The URL of the search result'),
 
36
  })
37
 
38
  return z.object({
@@ -64,63 +65,138 @@ export type { WebSearchProgress } from '../../types/tools.js'
64
  import type { WebSearchProgress } from '../../types/tools.js'
65
 
66
  /**
67
- * Search DuckDuckGo and return results
68
  */
69
- async function searchDuckDuckGo(query: string): Promise<Array<{ title: string; url: string }>> {
70
- const url = new URL('https://duckduckgo.com/html/')
71
  url.searchParams.set('q', query)
 
 
 
72
 
73
- const response = await fetch(url.toString(), {
74
- headers: {
75
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
76
- },
77
- })
78
 
79
  if (!response.ok) {
80
  throw new Error(`HTTP ${response.status}: ${response.statusText}`)
81
  }
82
 
83
- const html = await response.text()
84
-
85
- // Parse HTML to extract search results
86
- const results: Array<{ title: string; url: string }> = []
87
-
88
- // DuckDuckGo search results are in <a class="result__a"> tags
89
- // with title as text content and href as URL
90
- const resultRegex = /<a[^>]*class="result__a"[^>]*>(.*?)<\/a>/gi
91
- let match: RegExpExecArray | null
92
-
93
- while ((match = resultRegex.exec(html)) !== null && results.length < 10) {
94
- const title = match[1]
95
- .replace(/<[^>]*>/g, '') // Remove HTML tags
96
- .replace(/&amp;/g, '&')
97
- .replace(/&lt;/g, '<')
98
- .replace(/&gt;/g, '>')
99
- .replace(/&quot;/g, '"')
100
- .trim()
101
-
102
- // Extract URL from href attribute
103
- const hrefMatch = match[0].match(/href="([^"]*)"/)
104
- if (hrefMatch) {
105
- let url = hrefMatch[1]
106
-
107
- // DuckDuckGo URLs are often redirects
108
- if (url.startsWith('/l/?uddg=')) {
109
- const urlMatch = url.match(/uddg=([^&]+)/)
110
- if (urlMatch) {
111
- url = decodeURIComponent(urlMatch[1])
 
 
 
112
  }
113
  }
 
 
114
 
115
- if (title && url) {
116
- results.push({ title, url })
 
 
 
 
 
 
 
117
  }
118
  }
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  return results
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * Filter search results by domain
126
  */
@@ -154,7 +230,7 @@ function filterDomains(
154
  })
155
  }
156
 
157
- export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
158
  name: WEB_SEARCH_TOOL_NAME,
159
  description: 'Search the web and return search results with titles, URLs, and snippets.',
160
  getToolUseSummary,
@@ -179,9 +255,9 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
179
  return true
180
  },
181
  toAutoClassifierInput(input) {
182
- return input.query
183
  },
184
- async checkPermissions(_input): Promise<PermissionResult> {
185
  return {
186
  behavior: 'passthrough',
187
  message: 'WebSearchTool requires permission.',
@@ -207,9 +283,16 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
207
  // string entries in results[] (phantom match). Nothing to search.
208
  return ''
209
  },
210
- async validateInput(input) {
 
 
 
 
 
 
 
211
  const { query, allowed_domains, blocked_domains } = input
212
- if (!query.length) {
213
  return {
214
  result: false,
215
  message: 'Error: Missing query',
@@ -228,6 +311,16 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
228
  },
229
  async call(input, context, _canUseTool, _parentMessage, onProgress) {
230
  const startTime = performance.now()
 
 
 
 
 
 
 
 
 
 
231
  const { query, allowed_domains, blocked_domains } = input
232
 
233
  // Progress update: starting search
@@ -239,8 +332,8 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
239
  }
240
 
241
  try {
242
- // Call DuckDuckGo search
243
- const results = await searchDuckDuckGo(query)
244
 
245
  // Filter results by domain if specified
246
  let filteredResults = results
@@ -275,6 +368,7 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
275
  content: filteredResults.map(r => ({
276
  title: r.title,
277
  url: r.url,
 
278
  }))
279
  })
280
  }
@@ -301,9 +395,18 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
301
  }
302
  },
303
  mapToolResultToToolResultBlockParam(output, toolUseID) {
 
 
 
 
 
 
 
304
  const { query, results } = output
305
 
306
- let formattedOutput = `Web search results for query: "${query}"\n\n`
 
 
307
 
308
  // Process the results array - it can contain both string summaries and search result objects.
309
  // Guard against null/undefined entries that can appear after JSON round-tripping
 
1
+ import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
2
  import { z } from 'zod/v4'
3
  import { buildTool, type ToolDef } from '../../Tool.js'
4
  import { lazySchema } from '../../utils/lazySchema.js'
 
33
  const searchHitSchema = z.object({
34
  title: z.string().describe('The title of the search result'),
35
  url: z.string().describe('The URL of the search result'),
36
+ snippet: z.string().optional().describe('The snippet/description of the search result'),
37
  })
38
 
39
  return z.object({
 
65
  import type { WebSearchProgress } from '../../types/tools.js'
66
 
67
  /**
68
+ * Search using DuckDuckGo Instant Answer API
69
  */
70
+ async function searchDuckDuckGoAPI(query: string): Promise<Array<{ title: string; url: string; snippet?: string }>> {
71
+ const url = new URL('https://api.duckduckgo.com/')
72
  url.searchParams.set('q', query)
73
+ url.searchParams.set('format', 'json')
74
+ url.searchParams.set('no_html', '1')
75
+ url.searchParams.set('skip_disambig', '0')
76
 
77
+ const response = await fetch(url.toString())
 
 
 
 
78
 
79
  if (!response.ok) {
80
  throw new Error(`HTTP ${response.status}: ${response.statusText}`)
81
  }
82
 
83
+ const data = await response.json()
84
+ const results: Array<{ title: string; url: string; snippet?: string }> = []
85
+
86
+ // Add abstract if available
87
+ if (data.Abstract && data.AbstractURL) {
88
+ results.push({
89
+ title: data.Heading || query,
90
+ url: data.AbstractURL,
91
+ snippet: data.Abstract,
92
+ })
93
+ }
94
+
95
+ // Add related topics
96
+ if (data.RelatedTopics && Array.isArray(data.RelatedTopics)) {
97
+ for (const topic of data.RelatedTopics) {
98
+ if (topic.Text && topic.FirstURL) {
99
+ results.push({
100
+ title: topic.Text.split(' - ')[0] || 'Related',
101
+ url: topic.FirstURL,
102
+ snippet: topic.Text,
103
+ })
104
+ }
105
+ // Handle nested topics
106
+ if (topic.Topics && Array.isArray(topic.Topics)) {
107
+ for (const subTopic of topic.Topics) {
108
+ if (subTopic.Text && subTopic.FirstURL) {
109
+ results.push({
110
+ title: subTopic.Text.split(' - ')[0] || 'Related',
111
+ url: subTopic.FirstURL,
112
+ snippet: subTopic.Text,
113
+ })
114
+ }
115
  }
116
  }
117
+ }
118
+ }
119
 
120
+ // Add results from Infobox if available
121
+ if (data.Infobox?.content && Array.isArray(data.Infobox.content)) {
122
+ for (const item of data.Infobox.content) {
123
+ if (item.label && item.value && item.url) {
124
+ results.push({
125
+ title: item.label,
126
+ url: item.url,
127
+ snippet: `${item.label}: ${item.value}`,
128
+ })
129
  }
130
  }
131
  }
132
 
133
+ return results.slice(0, 10)
134
+ }
135
+
136
+ /**
137
+ * Search Wikipedia API
138
+ */
139
+ async function searchWikipedia(query: string): Promise<Array<{ title: string; url: string; snippet?: string }>> {
140
+ const url = new URL('https://en.wikipedia.org/w/api.php')
141
+ url.searchParams.set('action', 'query')
142
+ url.searchParams.set('list', 'search')
143
+ url.searchParams.set('srsearch', query)
144
+ url.searchParams.set('format', 'json')
145
+ url.searchParams.set('srlimit', '10')
146
+ url.searchParams.set('srprop', 'snippet')
147
+
148
+ const response = await fetch(url.toString())
149
+
150
+ if (!response.ok) {
151
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`)
152
+ }
153
+
154
+ const data = await response.json()
155
+ const results: Array<{ title: string; url: string; snippet?: string }> = []
156
+
157
+ if (data.query?.search) {
158
+ for (const item of data.query.search) {
159
+ results.push({
160
+ title: item.title,
161
+ url: `https://en.wikipedia.org/wiki/${encodeURIComponent(item.title.replace(/ /g, '_'))}`,
162
+ snippet: item.snippet?.replace(/<\/?span[^>]*>/g, ''),
163
+ })
164
+ }
165
+ }
166
+
167
  return results
168
  }
169
 
170
+ /**
171
+ * Combined search using multiple sources
172
+ */
173
+ async function searchBrave(query: string): Promise<Array<{ title: string; url: string; snippet?: string }>> {
174
+ const results: Array<{ title: string; url: string; snippet?: string }> = []
175
+
176
+ // Try DuckDuckGo Instant Answer API first
177
+ try {
178
+ const ddgResults = await searchDuckDuckGoAPI(query)
179
+ results.push(...ddgResults)
180
+ } catch (e) {
181
+ // Continue to next source
182
+ }
183
+
184
+ // Add Wikipedia results
185
+ try {
186
+ const wikiResults = await searchWikipedia(query)
187
+ for (const result of wikiResults) {
188
+ // Avoid duplicates
189
+ if (!results.some(r => r.url === result.url)) {
190
+ results.push(result)
191
+ }
192
+ }
193
+ } catch (e) {
194
+ // Continue
195
+ }
196
+
197
+ return results.slice(0, 10)
198
+ }
199
+
200
  /**
201
  * Filter search results by domain
202
  */
 
230
  })
231
  }
232
 
233
+ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchProgress>({
234
  name: WEB_SEARCH_TOOL_NAME,
235
  description: 'Search the web and return search results with titles, URLs, and snippets.',
236
  getToolUseSummary,
 
255
  return true
256
  },
257
  toAutoClassifierInput(input) {
258
+ return input?.query ?? ''
259
  },
260
+ async checkPermissions(_input, _context): Promise<PermissionResult> {
261
  return {
262
  behavior: 'passthrough',
263
  message: 'WebSearchTool requires permission.',
 
283
  // string entries in results[] (phantom match). Nothing to search.
284
  return ''
285
  },
286
+ async validateInput(input, _context) {
287
+ if (!input) {
288
+ return {
289
+ result: false,
290
+ message: 'Error: Missing input',
291
+ errorCode: 1,
292
+ }
293
+ }
294
  const { query, allowed_domains, blocked_domains } = input
295
+ if (!query?.length) {
296
  return {
297
  result: false,
298
  message: 'Error: Missing query',
 
311
  },
312
  async call(input, context, _canUseTool, _parentMessage, onProgress) {
313
  const startTime = performance.now()
314
+
315
+ if (!input?.query) {
316
+ const endTime = performance.now()
317
+ return {
318
+ query: '',
319
+ results: ['Error: Missing query'],
320
+ durationSeconds: (endTime - startTime) / 1000,
321
+ }
322
+ }
323
+
324
  const { query, allowed_domains, blocked_domains } = input
325
 
326
  // Progress update: starting search
 
332
  }
333
 
334
  try {
335
+ // Call Brave Search
336
+ const results = await searchBrave(query)
337
 
338
  // Filter results by domain if specified
339
  let filteredResults = results
 
368
  content: filteredResults.map(r => ({
369
  title: r.title,
370
  url: r.url,
371
+ snippet: r.snippet,
372
  }))
373
  })
374
  }
 
395
  }
396
  },
397
  mapToolResultToToolResultBlockParam(output, toolUseID) {
398
+ if (!output) {
399
+ return {
400
+ tool_use_id: toolUseID,
401
+ type: 'tool_result',
402
+ content: 'Error: Missing output',
403
+ }
404
+ }
405
  const { query, results } = output
406
 
407
+ let formattedOutput = query
408
+ ? `Web search results for query: "${query}"\n\n`
409
+ : 'Web search results:\n\n'
410
 
411
  // Process the results array - it can contain both string summaries and search result objects.
412
  // Guard against null/undefined entries that can appear after JSON round-tripping
src/types/tools.ts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface WebSearchProgress {
2
+ type: 'query_update' | 'search_results_received'
3
+ query?: string
4
+ resultCount?: number
5
+ }
6
+
7
+ export interface BashProgress {
8
+ type: 'bash_progress'
9
+ output: string
10
+ fullOutput: string
11
+ elapsedTimeSeconds?: number
12
+ totalLines?: number
13
+ }
14
+
15
+ export interface MCPProgress {
16
+ type: string
17
+ [key: string]: unknown
18
+ }
19
+
20
+ export interface REPLToolProgress {
21
+ type: string
22
+ [key: string]: unknown
23
+ }
24
+
25
+ export interface SkillToolProgress {
26
+ type: string
27
+ [key: string]: unknown
28
+ }
29
+
30
+ export interface TaskOutputProgress {
31
+ type: string
32
+ [key: string]: unknown
33
+ }
34
+
35
+ export type ToolProgressData =
36
+ | WebSearchProgress
37
+ | BashProgress
38
+ | MCPProgress
39
+ | REPLToolProgress
40
+ | SkillToolProgress
41
+ | TaskOutputProgress