@konbakuyomu/smart-search-linux-x64 0.1.28 → 0.1.29

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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/runtime/smart-search/_internal/base_library.zip +0 -0
  3. package/runtime/smart-search/_internal/certifi/cacert.pem +0 -104
  4. package/runtime/smart-search/_internal/smart_search/assets/i18n/messages.json +108 -0
  5. package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/cli-core.md +1 -1
  6. package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/command-patterns.md +2 -0
  7. package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/provider-routing.md +11 -3
  8. package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/setup-config.md +2 -0
  9. package/runtime/smart-search/_internal/smart_search/assets/ui/index.html +1 -1
  10. package/runtime/smart-search/_internal/smart_search/cli.py +35 -3
  11. package/runtime/smart-search/_internal/smart_search/config.py +20 -1
  12. package/runtime/smart-search/_internal/smart_search/jev_search.py +9 -1
  13. package/runtime/smart-search/_internal/smart_search/provider_descriptions.py +14 -0
  14. package/runtime/smart-search/_internal/smart_search/providers/__init__.py +2 -0
  15. package/runtime/smart-search/_internal/smart_search/providers/deepseek.py +110 -0
  16. package/runtime/smart-search/_internal/smart_search/service.py +84 -10
  17. package/runtime/smart-search/_internal/smart_search/ui_metadata.py +18 -0
  18. package/runtime/smart-search/_internal/smart_search/ui_server.py +44 -3
  19. package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/METADATA +1 -1
  20. package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/RECORD +25 -23
  21. package/runtime/smart-search/package.json +7 -7
  22. package/runtime/smart-search/smart-search +0 -0
  23. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/INSTALLER +0 -0
  24. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/REQUESTED +0 -0
  25. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/WHEEL +0 -0
  26. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/direct_url.json +0 -0
  27. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/entry_points.txt +0 -0
  28. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/licenses/LICENSE +0 -0
  29. /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.29.dist-info}/top_level.txt +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konbakuyomu/smart-search-linux-x64",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Self-contained Smart Search CLI for linux/x64",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1975,110 +1975,6 @@ jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
1975
1975
  3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
1976
1976
  -----END CERTIFICATE-----
1977
1977
 
1978
- # Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
1979
- # Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
1980
- # Label: "emSign Root CA - G1"
1981
- # Serial: 235931866688319308814040
1982
- # MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac
1983
- # SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c
1984
- # SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67
1985
- -----BEGIN CERTIFICATE-----
1986
- MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD
1987
- VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU
1988
- ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH
1989
- MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO
1990
- MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv
1991
- Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN
1992
- BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz
1993
- f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO
1994
- 8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq
1995
- d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM
1996
- tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt
1997
- Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB
1998
- o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD
1999
- AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x
2000
- PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM
2001
- wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d
2002
- GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH
2003
- 6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby
2004
- RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
2005
- iN66zB+Afko=
2006
- -----END CERTIFICATE-----
2007
-
2008
- # Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
2009
- # Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
2010
- # Label: "emSign ECC Root CA - G3"
2011
- # Serial: 287880440101571086945156
2012
- # MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40
2013
- # SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1
2014
- # SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b
2015
- -----BEGIN CERTIFICATE-----
2016
- MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
2017
- EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
2018
- bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
2019
- RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
2020
- TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
2021
- b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
2022
- djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
2023
- WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
2024
- fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
2025
- zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
2026
- hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
2027
- CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
2028
- +JbNR6iC8hZVdyR+EhCVBCyj
2029
- -----END CERTIFICATE-----
2030
-
2031
- # Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
2032
- # Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
2033
- # Label: "emSign Root CA - C1"
2034
- # Serial: 825510296613316004955058
2035
- # MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68
2036
- # SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01
2037
- # SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f
2038
- -----BEGIN CERTIFICATE-----
2039
- MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG
2040
- A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg
2041
- SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw
2042
- MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
2043
- biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v
2044
- dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ
2045
- BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ
2046
- HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH
2047
- 3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH
2048
- GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c
2049
- xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1
2050
- aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq
2051
- TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
2052
- BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87
2053
- /kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4
2054
- kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG
2055
- YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT
2056
- +xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo
2057
- WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
2058
- -----END CERTIFICATE-----
2059
-
2060
- # Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
2061
- # Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
2062
- # Label: "emSign ECC Root CA - C3"
2063
- # Serial: 582948710642506000014504
2064
- # MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5
2065
- # SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66
2066
- # SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3
2067
- -----BEGIN CERTIFICATE-----
2068
- MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG
2069
- EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx
2070
- IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw
2071
- MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
2072
- biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND
2073
- IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci
2074
- MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti
2075
- sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O
2076
- BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
2077
- Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c
2078
- 3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J
2079
- 0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
2080
- -----END CERTIFICATE-----
2081
-
2082
1978
  # Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post
2083
1979
  # Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post
2084
1980
  # Label: "Hongkong Post Root CA 3"
@@ -17858,5 +17858,113 @@
17858
17858
  "{0} 另外,联网搜索能力检查未通过:{1}。OpenAI-compatible 不会发送搜索工具;如果该模型只能通过 Responses 的 web_search 工具联网,请把 XAI_API_URL、XAI_API_KEY、XAI_MODEL 指向支持 /v1/responses 的中转站。": [
17859
17859
  "{0} 另外,联网搜索能力检查未通过:{1}。OpenAI-compatible 不会发送搜索工具;如果该模型只能通过 Responses 的 web_search 工具联网,请把 XAI_API_URL、XAI_API_KEY、XAI_MODEL 指向支持 /v1/responses 的中转站。",
17860
17860
  "{0} Also, the web search capability check did not pass: {1}. OpenAI-compatible sends no search tool; if this model can only search through the Responses web_search tool, point XAI_API_URL, XAI_API_KEY and XAI_MODEL at a relay that supports /v1/responses."
17861
+ ],
17862
+ "DeepSeek search response is not a JSON object": [
17863
+ "DeepSeek 搜索响应不是 JSON 对象",
17864
+ "DeepSeek search response is not a JSON object"
17865
+ ],
17866
+ "DeepSeek search response is missing results": [
17867
+ "DeepSeek 搜索响应缺少 results",
17868
+ "DeepSeek search response is missing results"
17869
+ ],
17870
+ "DeepSeek fetch response is not a JSON object": [
17871
+ "DeepSeek 抓取响应不是 JSON 对象",
17872
+ "DeepSeek fetch response is not a JSON object"
17873
+ ],
17874
+ "DeepSeek fetch content is not text": [
17875
+ "DeepSeek 抓取内容不是文本",
17876
+ "DeepSeek fetch content is not text"
17877
+ ],
17878
+ "DeepSeek search: {0}": [
17879
+ "DeepSeek 搜索:{0}",
17880
+ "DeepSeek search: {0}"
17881
+ ],
17882
+ "DeepSeek fetch: {0}": [
17883
+ "DeepSeek 抓取:{0}",
17884
+ "DeepSeek fetch: {0}"
17885
+ ],
17886
+ "DeepSeek fetch returned no content for {0}": [
17887
+ "DeepSeek 抓取 {0} 没有返回正文",
17888
+ "DeepSeek fetch returned no content for {0}"
17889
+ ],
17890
+ "DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable.": [
17891
+ "DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用",
17892
+ "DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable."
17893
+ ],
17894
+ "DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用": [
17895
+ "DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用",
17896
+ "DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable."
17897
+ ],
17898
+ "Both DeepSeek search and fetch APIs are available": [
17899
+ "DeepSeek 搜索与抓取 API 均可用",
17900
+ "Both DeepSeek search and fetch APIs are available"
17901
+ ],
17902
+ "DeepSeek 搜索与抓取 API 均可用": [
17903
+ "DeepSeek 搜索与抓取 API 均可用",
17904
+ "Both DeepSeek search and fetch APIs are available"
17905
+ ],
17906
+ "DeepSeek API request timed out": [
17907
+ "DeepSeek API 请求超时",
17908
+ "DeepSeek API request timed out"
17909
+ ],
17910
+ "DeepSeek API 请求超时": [
17911
+ "DeepSeek API 请求超时",
17912
+ "DeepSeek API request timed out"
17913
+ ],
17914
+ "Save DEEPSEEK_API_KEY.": [
17915
+ "保存 DEEPSEEK_API_KEY。",
17916
+ "Save DEEPSEEK_API_KEY."
17917
+ ],
17918
+ "Save DEEPSEEK_API_URL.": [
17919
+ "保存 DEEPSEEK_API_URL。",
17920
+ "Save DEEPSEEK_API_URL."
17921
+ ],
17922
+ "Save DEEPSEEK_TIMEOUT_SECONDS.": [
17923
+ "保存 DEEPSEEK_TIMEOUT_SECONDS。",
17924
+ "Save DEEPSEEK_TIMEOUT_SECONDS."
17925
+ ],
17926
+ "没有找到 WSL 发行版。": [
17927
+ "没有找到 WSL 发行版。",
17928
+ "No WSL distribution was found."
17929
+ ],
17930
+ "没有在 WSL 中找到 smart-search。请先在 WSL 中安装 CLI,并确认 bash 能直接运行 smart-search。": [
17931
+ "没有在 WSL 中找到 smart-search。请先在 WSL 中安装 CLI,并确认 bash 能直接运行 smart-search。",
17932
+ "smart-search was not found in WSL. Install the CLI in WSL first and make sure bash can run smart-search directly."
17933
+ ],
17934
+ "位于 WSL {0},使用该发行版中的配置和 Skills。请在 WSL 终端用原安装方式更新 CLI。": [
17935
+ "位于 WSL {0},使用该发行版中的配置和 Skills。请在 WSL 终端用原安装方式更新 CLI。",
17936
+ "Installed in WSL {0}; it uses that distribution's configuration and Skills. Update the CLI from a WSL terminal with the tool you installed it with."
17937
+ ],
17938
+ "WSL {0} 中的 CLI 版本过旧,App 无法连接。请在 WSL 终端更新:npm install -g @konbakuyomu/smart-search@latest": [
17939
+ "WSL {0} 中的 CLI 版本过旧,App 无法连接。请在 WSL 终端更新:npm install -g @konbakuyomu/smart-search@latest",
17940
+ "The CLI in WSL {0} is too old for the App to connect. Update it from a WSL terminal: npm install -g @konbakuyomu/smart-search@latest"
17941
+ ],
17942
+ "旧版": [
17943
+ "旧版",
17944
+ "old version"
17945
+ ],
17946
+ "当前连接的是 WSL 中的 CLI,只观察它自己的配置目录。": [
17947
+ "当前连接的是 WSL 中的 CLI,只观察它自己的配置目录。",
17948
+ "Connected to a CLI in WSL; only its own configuration directory is observed."
17949
+ ],
17950
+ "这是 WSL 中的目录。需要更换时,在 WSL 中设置 SMART_SEARCH_CONFIG_DIR。": [
17951
+ "这是 WSL 中的目录。需要更换时,在 WSL 中设置 SMART_SEARCH_CONFIG_DIR。",
17952
+ "This directory is inside WSL. To use another one, set SMART_SEARCH_CONFIG_DIR in WSL."
17953
+ ],
17954
+ "WSL 中的 CLI 使用该发行版自己的配置和 Skills。未运行的发行版不会自动启动。": [
17955
+ "WSL 中的 CLI 使用该发行版自己的配置和 Skills。未运行的发行版不会自动启动。",
17956
+ "A CLI in WSL uses that distribution's own configuration and Skills. Stopped distributions are not started automatically."
17957
+ ],
17958
+ "未运行:{0}。“检测 WSL”会启动这些发行版并查找 CLI。": [
17959
+ "未运行:{0}。“检测 WSL”会启动这些发行版并查找 CLI。",
17960
+ "Not running: {0}. Detect WSL starts them to look for the CLI."
17961
+ ],
17962
+ "检测 WSL": [
17963
+ "检测 WSL",
17964
+ "Detect WSL"
17965
+ ],
17966
+ "{0} · 需要更新": [
17967
+ "{0} · 需要更新",
17968
+ "{0} · update needed"
17861
17969
  ]
17862
17970
  }
@@ -48,7 +48,7 @@
48
48
  - `smart-search map URL [--instructions TEXT] [--max-depth N] [--max-breadth N] [--limit N] [--timeout SECONDS] [--format json|markdown|content] [--output PATH]`
49
49
  - `smart-search doctor [--format json|markdown|content] [--output PATH]`
50
50
  - `smart-search diagnose openai-compatible [--timeout SECONDS] [--format json|markdown] [--output PATH]`
51
- - `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--skip-skills] [--install-skills CSV] [--skills-root PATH] [--xai-api-url URL] [--xai-api-key KEY] [--xai-model ID] [--xai-tools-explicit CSV] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-fallback-models CSV] [--openai-compatible-api-mode chat-completions|responses] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--intent-router jev|local] [--search-timeout SECONDS] [--provider-cooldown SECONDS] [--provider-failure-threshold N] [--exa-key KEY] [--context7-key KEY] [--zhipu-key KEY] [--zhipu-api-url URL] [--zhipu-search-engine ENGINE] [--zhipu-mcp-key KEY] [--zhipu-mcp-search-api-url URL] [--zhipu-mcp-reader-api-url URL] [--zhipu-mcp-zread-api-url URL] [--zhipu-mcp-timeout SECONDS] [--jina-key KEY] [--jina-reader-api-url URL] [--jina-respond-with MODE] [--jina-timeout SECONDS] [--tavily-api-url URL] [--tavily-key KEY] [--firecrawl-api-url URL] [--firecrawl-key KEY] [--tinyfish-key KEY] [--tinyfish-search-api-url URL] [--tinyfish-fetch-api-url URL] [--tinyfish-timeout SECONDS] [--anysearch-api-url URL] [--anysearch-key KEY] [--anysearch-timeout SECONDS] [--sciverse-api-url URL] [--sciverse-token TOKEN] [--sciverse-timeout SECONDS] [--format json|markdown|content] [--output PATH]`
51
+ - `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--skip-skills] [--install-skills CSV] [--skills-root PATH] [--xai-api-url URL] [--xai-api-key KEY] [--xai-model ID] [--xai-tools-explicit CSV] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-fallback-models CSV] [--openai-compatible-api-mode chat-completions|responses] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--intent-router jev|local] [--search-timeout SECONDS] [--provider-cooldown SECONDS] [--provider-failure-threshold N] [--exa-key KEY] [--context7-key KEY] [--zhipu-key KEY] [--zhipu-api-url URL] [--zhipu-search-engine ENGINE] [--zhipu-mcp-key KEY] [--zhipu-mcp-search-api-url URL] [--zhipu-mcp-reader-api-url URL] [--zhipu-mcp-zread-api-url URL] [--zhipu-mcp-timeout SECONDS] [--jina-key KEY] [--jina-reader-api-url URL] [--jina-respond-with MODE] [--jina-timeout SECONDS] [--tavily-api-url URL] [--tavily-key KEY] [--firecrawl-api-url URL] [--firecrawl-key KEY] [--tinyfish-key KEY] [--tinyfish-search-api-url URL] [--tinyfish-fetch-api-url URL] [--tinyfish-timeout SECONDS] [--deepseek-key KEY] [--deepseek-api-url URL] [--deepseek-timeout SECONDS] [--anysearch-api-url URL] [--anysearch-key KEY] [--anysearch-timeout SECONDS] [--sciverse-api-url URL] [--sciverse-token TOKEN] [--sciverse-timeout SECONDS] [--format json|markdown|content] [--output PATH]`
52
52
  - `smart-search config path|list|set|unset ... [--format json|markdown|content] [--output PATH]`
53
53
  - `smart-search model set MODEL [--format json|markdown|content] [--output PATH]`
54
54
  - `smart-search model current [--format json|markdown|content] [--output PATH]`
@@ -96,6 +96,8 @@ smart-search config set FIRECRAWL_API_URL "https://api.firecrawl.dev/v2" --forma
96
96
  smart-search config set TINYFISH_API_KEY "key" --format json
97
97
  smart-search config set TINYFISH_SEARCH_API_URL "https://api.search.tinyfish.ai" --format json
98
98
  smart-search config set TINYFISH_FETCH_API_URL "https://api.fetch.tinyfish.ai" --format json
99
+ smart-search config set DEEPSEEK_API_KEY "key" --format json
100
+ smart-search config set DEEPSEEK_API_URL "https://api.deepseek.com" --format json
99
101
  smart-search model current --format json
100
102
  smart-search doctor --format json
101
103
  smart-search doctor --format markdown
@@ -32,11 +32,11 @@ For research, `SMART_SEARCH_JEV_SYNTHESIZE` accepts `true`, `false`, and `auto`.
32
32
  - Jina Reader is `web_fetch` only, not a general search provider. `JINA_API_KEY` is required before Jina satisfies the standard minimum profile; anonymous `r.jina.ai` is explicit/experimental fetch behavior.
33
33
  - Same-capability fallback is allowed; model answers and web search are mutual fallbacks in ordinary search. Context7 is not used for unrelated broad web queries, and page extraction providers are not used as docs search providers.
34
34
  - `TAVILY_ENABLED=false` removes Tavily from registered `web_search` and `web_fetch` routes even when its key is present. Direct Tavily search/extract calls, `map`, `doctor`, and smoke must make no Tavily network request; `map` reports a local configuration error. Firecrawl remains independently configured and all fallback stays within its capability.
35
- - In Jev mode, Jev chooses among allowed capabilities and providers for the question; there is no mandatory main-model call or fixed global provider order. Local routing prioritizes suitable capabilities and configured preferences.
36
- - `main_search` contains xAI Responses and OpenAI-compatible model channels; `web_search` contains Zhipu REST/MCP, Tavily, Firecrawl and TinyFish; `docs_search` contains Context7 and Exa. Failure fallback stays within the selected capability family; model answers and web search are mutual fallbacks in ordinary search.
35
+ - In Jev mode, Jev chooses among allowed capabilities and providers for the question; there is no mandatory main-model call or fixed global provider order. Local routing prioritizes suitable capabilities and configured preferences; a docs question with no docs channel configured uses web search instead of no channel.
36
+ - `main_search` contains xAI Responses and OpenAI-compatible model channels; `web_search` contains Zhipu REST/MCP, Tavily, Firecrawl, TinyFish and DeepSeek; `docs_search` contains Context7 and Exa. Failure fallback stays within the selected capability family; model answers and web search are mutual fallbacks in ordinary search.
37
37
  - Ordinary Context7 uses the first library in Context7's ranked candidates, without a second Jev call. Research may use Jev to select the matching library, using the first candidate if Jev fails or no score reaches its threshold. Empty results may fall through to same-capability Exa; explicit Context7 commands remain unchanged.
38
38
  - `--extra-sources N` controls the selected channel's result count. It does not add channels, split a budget between providers, or make Tavily/Firecrawl a required extra stage. A known-URL search follows the configured Jev/local routing; local routing prefers page reading.
39
- - `fetch` tries Tavily first, then Jina with `JINA_API_KEY`, then Zhipu Coding Plan MCP Reader, then Firecrawl, then TinyFish.
39
+ - `fetch` tries Tavily first, then Jina with `JINA_API_KEY`, then Zhipu Coding Plan MCP Reader, then Firecrawl, then TinyFish, then DeepSeek.
40
40
 
41
41
  TinyFish:
42
42
 
@@ -47,6 +47,14 @@ TinyFish:
47
47
  - Fetch reads `results[0].text`; `errors[]` entries are surfaced as `provider_error` with the upstream message. A payload with neither results nor errors is `provider_error`, and a non-object payload is `parse_error`.
48
48
  - Challenge pages such as `Checking if the site connection is secure` are reported as `quality_error` so same-capability fallback can continue.
49
49
  - `401`/403 map to `auth_error`, `408` to `timeout`, `429` to `rate_limited`, `5xx` to `network_error`, and `400`/422 to `parameter_error`.
50
+ DeepSeek:
51
+
52
+ - `DEEPSEEK_API_KEY` is optional and registers DeepSeek for both `web_search` and `web_fetch`; it is the same key as the DeepSeek model API. It never satisfies `main_search` or `docs_search`. Jev may select it directly; the direct `fetch` fallback order remains as listed above.
53
+ - `DEEPSEEK_API_URL` defaults to `https://api.deepseek.com`; search calls `POST /v1/tools/search` with `{"query", "count"}` and fetch calls `POST /v1/tools/fetch` with `{"url"}`, both with `Authorization: Bearer`. A trailing `/v1` in the base URL is accepted. Never log or echo the key.
54
+ - DeepSeek accepts queries of at most 128 characters, so longer queries are shortened at a word boundary. `site:`, `-site:`, `lang:`, `after:`/`before:` and quoted phrases in the query pass through unchanged.
55
+ - Search results normalize `summary` (falling back to `snippet`) to `description`, `site_name` to `source`, and `date` to `published_date`. Summaries are not page text: fetch a key page before citing it. An empty result set is a successful empty response.
56
+ - Fetch returns the page Markdown. HTTP 403 (URL refused by DeepSeek's safety policy) and 404 (no usable page content, which currently includes PDF links and private network addresses) mean that page has nothing to read: the attempt is `empty`, fallback continues, and the provider is not cooled down. `401` maps to `auth_error`, `402` (insufficient balance) to `provider_error`, `429` to `rate_limited`, `5xx` to `network_error`.
57
+ - DeepSeek bills each successful search or fetch request. `doctor` and `providers test deepseek` send one real request to each.
50
58
  - `map` currently uses Tavily only.
51
59
  - `exa-search` and `exa-similar` use Exa only.
52
60
  - `context7-library` and `context7-docs` use Context7 only.
@@ -98,6 +98,8 @@
98
98
  - Use `smart-search setup --non-interactive --tinyfish-key "key"` to add TinyFish to `web_search` and `web_fetch`. It never satisfies `main_search` or `docs_search`; Jev can select it among the allowed channels.
99
99
  - `TINYFISH_SEARCH_API_URL` defaults to `https://api.search.tinyfish.ai`, and `TINYFISH_FETCH_API_URL` defaults to `https://api.fetch.tinyfish.ai`. Both authenticate with the `X-API-Key` header, not a bearer token.
100
100
  - `TINYFISH_TIMEOUT_SECONDS` defaults to `60` and covers both TinyFish search and fetch reads.
101
+ - Use `smart-search setup --non-interactive --deepseek-key "key"` to add DeepSeek to `web_search` and `web_fetch` with the same key as the DeepSeek model API. It never satisfies `main_search` or `docs_search`; Jev can select it among the allowed channels.
102
+ - `DEEPSEEK_API_URL` defaults to `https://api.deepseek.com`, and `DEEPSEEK_TIMEOUT_SECONDS` defaults to `60` and covers both DeepSeek search and fetch reads. Each successful DeepSeek request is billed.
101
103
 
102
104
  ## Provider Failure Cooldown
103
105
 
@@ -691,7 +691,7 @@ details.more[open] > summary::before { content: "▾ "; }
691
691
  { capability: "docs_search", title: "stepDocs",
692
692
  providers: ["context7", "exa"] },
693
693
  { capability: "web_fetch", title: "stepFetch",
694
- providers: ["tavily", "jina", "zhipu-mcp-reader", "zhipu-mcp", "firecrawl"] }
694
+ providers: ["tavily", "jina", "zhipu-mcp-reader", "zhipu-mcp", "firecrawl", "deepseek"] }
695
695
  ];
696
696
 
697
697
  function viewGettingStarted() {
@@ -627,6 +627,7 @@ def _format_doctor_markdown(data: dict[str, Any]) -> str:
627
627
  ("jina", data.get("jina_connection_test") or {}),
628
628
  ("firecrawl", data.get("firecrawl_connection_test") or {}),
629
629
  ("tinyfish", data.get("tinyfish_connection_test") or {}),
630
+ ("deepseek", data.get("deepseek_connection_test") or {}),
630
631
  ("zhipu", data.get("zhipu_connection_test") or {}),
631
632
  ("zhipu-mcp", data.get("zhipu_mcp_connection_test") or {}),
632
633
  ("context7", data.get("context7_connection_test") or {}),
@@ -1478,6 +1479,7 @@ def _display_provider(provider: str, lang: str) -> str:
1478
1479
  "tavily": "Tavily",
1479
1480
  "firecrawl": "Firecrawl",
1480
1481
  "tinyfish": "TinyFish",
1482
+ "deepseek": "DeepSeek",
1481
1483
  "anysearch": "AnySearch",
1482
1484
  "sciverse": "Sciverse",
1483
1485
  }
@@ -2078,7 +2080,7 @@ def _prompt_web_fetch(values: dict[str, str], current: dict[str, str], lang: str
2078
2080
  "选择 web_fetch provider",
2079
2081
  "Choose web_fetch providers",
2080
2082
  ),
2081
- ["tavily", "jina", "firecrawl", "tinyfish"],
2083
+ ["tavily", "jina", "firecrawl", "tinyfish", "deepseek"],
2082
2084
  default_selected,
2083
2085
  lang,
2084
2086
  )
@@ -2110,6 +2112,13 @@ def _prompt_web_fetch(values: dict[str, str], current: dict[str, str], lang: str
2110
2112
  current.get("TINYFISH_API_KEY", ""),
2111
2113
  lang=lang,
2112
2114
  )
2115
+ if "deepseek" in selected:
2116
+ values["DEEPSEEK_API_KEY"] = _prompt_value(
2117
+ "DEEPSEEK_API_KEY",
2118
+ "DeepSeek API key",
2119
+ current.get("DEEPSEEK_API_KEY", ""),
2120
+ lang=lang,
2121
+ )
2113
2122
 
2114
2123
 
2115
2124
  def _prompt_optional_enhancements(values: dict[str, str], current: dict[str, str], lang: str) -> None:
@@ -2347,6 +2356,9 @@ def _run_advanced_setup_prompts(values: dict[str, str], current: dict[str, str],
2347
2356
  ("TINYFISH_SEARCH_API_URL", "TinyFish Search API URL", True),
2348
2357
  ("TINYFISH_FETCH_API_URL", "TinyFish Fetch API URL", True),
2349
2358
  ("TINYFISH_TIMEOUT_SECONDS", "TinyFish timeout seconds", True),
2359
+ ("DEEPSEEK_API_KEY", "DeepSeek API key", True),
2360
+ ("DEEPSEEK_API_URL", "DeepSeek API URL", True),
2361
+ ("DEEPSEEK_TIMEOUT_SECONDS", "DeepSeek timeout seconds", True),
2350
2362
  ("ANYSEARCH_API_URL", "AnySearch MCP API URL", True),
2351
2363
  ("ANYSEARCH_API_KEY", "AnySearch API key", True),
2352
2364
  ("ANYSEARCH_TIMEOUT_SECONDS", "AnySearch timeout seconds", True),
@@ -2370,7 +2382,7 @@ def _run_advanced_setup_prompts(values: dict[str, str], current: dict[str, str],
2370
2382
  value = _normalize_custom_base_url(value)
2371
2383
  elif key == "SCIVERSE_API_URL":
2372
2384
  value = _normalize_custom_base_url(value)
2373
- elif key in {"TINYFISH_SEARCH_API_URL", "TINYFISH_FETCH_API_URL"}:
2385
+ elif key in {"TINYFISH_SEARCH_API_URL", "TINYFISH_FETCH_API_URL", "DEEPSEEK_API_URL"}:
2374
2386
  value = _normalize_custom_base_url(value)
2375
2387
  values[key] = value
2376
2388
 
@@ -2775,6 +2787,9 @@ def _run_setup(args: argparse.Namespace) -> int:
2775
2787
  "TINYFISH_SEARCH_API_URL": _normalize_custom_base_url(args.tinyfish_search_api_url),
2776
2788
  "TINYFISH_FETCH_API_URL": _normalize_custom_base_url(args.tinyfish_fetch_api_url),
2777
2789
  "TINYFISH_TIMEOUT_SECONDS": args.tinyfish_timeout,
2790
+ "DEEPSEEK_API_KEY": args.deepseek_key,
2791
+ "DEEPSEEK_API_URL": _normalize_custom_base_url(args.deepseek_api_url),
2792
+ "DEEPSEEK_TIMEOUT_SECONDS": args.deepseek_timeout,
2778
2793
  "ANYSEARCH_API_URL": _normalize_custom_base_url(args.anysearch_api_url),
2779
2794
  "ANYSEARCH_API_KEY": args.anysearch_key,
2780
2795
  "ANYSEARCH_TIMEOUT_SECONDS": args.anysearch_timeout,
@@ -2885,6 +2900,7 @@ def _run_regression() -> int:
2885
2900
  "tests/test_jina_provider.py",
2886
2901
  "tests/test_tinyfish_provider.py",
2887
2902
  "tests/test_tinyfish_service.py",
2903
+ "tests/test_deepseek_provider.py",
2888
2904
  "tests/test_zhipu_mcp_provider.py",
2889
2905
  "tests/test_smoke.py",
2890
2906
  "tests/test_intent_router.py",
@@ -3484,6 +3500,9 @@ def build_parser() -> argparse.ArgumentParser:
3484
3500
  setup_parser.add_argument("--tinyfish-search-api-url", default="", help="Save TINYFISH_SEARCH_API_URL.")
3485
3501
  setup_parser.add_argument("--tinyfish-fetch-api-url", default="", help="Save TINYFISH_FETCH_API_URL.")
3486
3502
  setup_parser.add_argument("--tinyfish-timeout", default="", help="Save TINYFISH_TIMEOUT_SECONDS.")
3503
+ setup_parser.add_argument("--deepseek-key", default="", help="Save DEEPSEEK_API_KEY.")
3504
+ setup_parser.add_argument("--deepseek-api-url", default="", help="Save DEEPSEEK_API_URL.")
3505
+ setup_parser.add_argument("--deepseek-timeout", default="", help="Save DEEPSEEK_TIMEOUT_SECONDS.")
3487
3506
  setup_parser.add_argument("--anysearch-api-url", default="", help="Save ANYSEARCH_API_URL.")
3488
3507
  setup_parser.add_argument("--anysearch-key", default="", help="Save ANYSEARCH_API_KEY.")
3489
3508
  setup_parser.add_argument("--anysearch-timeout", default="", help="Save ANYSEARCH_TIMEOUT_SECONDS.")
@@ -3562,6 +3581,18 @@ def _main(argv: list[str] | None = None) -> int:
3562
3581
  return EXIT_RUNTIME_ERROR
3563
3582
 
3564
3583
 
3584
+ def _desktop_launch_command() -> list[str]:
3585
+ """Argv that starts this same CLI without a shell, e.g. inside WSL from the Windows App."""
3586
+ package_root = os.environ.get("SMART_SEARCH_PACKAGE_ROOT", "")
3587
+ node = os.environ.get("SMART_SEARCH_NODE_PATH", "")
3588
+ wrapper = Path(package_root) / "npm" / "bin" / "smart-search.js"
3589
+ if package_root and node and wrapper.is_file():
3590
+ return [node, str(wrapper)]
3591
+ if getattr(sys, "frozen", False):
3592
+ return [sys.executable]
3593
+ return [sys.executable, "-m", "smart_search.cli"]
3594
+
3595
+
3565
3596
  def main(argv: list[str] | None = None) -> int:
3566
3597
  arguments = list(sys.argv[1:] if argv is None else argv)
3567
3598
  if arguments == ["--desktop-backend"]:
@@ -3569,7 +3600,8 @@ def main(argv: list[str] | None = None) -> int:
3569
3600
  return desktop_main()
3570
3601
  if arguments == ["--desktop-capabilities"]:
3571
3602
  print(json.dumps({"product": "smart-search", "version": _get_version(), "activity_protocol_version": 1,
3572
- "desktop_protocol_version": 1, "skills_protocol_version": 1}))
3603
+ "desktop_protocol_version": 1, "skills_protocol_version": 1,
3604
+ "launch_command": _desktop_launch_command()}))
3573
3605
  return EXIT_OK
3574
3606
  try:
3575
3607
  arguments, language, warning = _command_language(arguments)
@@ -58,6 +58,7 @@ class Config:
58
58
  "tavily": "TAVILY_ENABLED",
59
59
  "firecrawl": "FIRECRAWL_ENABLED",
60
60
  "tinyfish": "TINYFISH_ENABLED",
61
+ "deepseek": "DEEPSEEK_ENABLED",
61
62
  "jina": "JINA_ENABLED",
62
63
  "anysearch": "ANYSEARCH_ENABLED",
63
64
  "sciverse": "SCIVERSE_ENABLED",
@@ -122,6 +123,9 @@ class Config:
122
123
  "TINYFISH_SEARCH_API_URL",
123
124
  "TINYFISH_FETCH_API_URL",
124
125
  "TINYFISH_TIMEOUT_SECONDS",
126
+ "DEEPSEEK_API_KEY",
127
+ "DEEPSEEK_API_URL",
128
+ "DEEPSEEK_TIMEOUT_SECONDS",
125
129
  "ANYSEARCH_API_KEY",
126
130
  "ANYSEARCH_API_URL",
127
131
  "ANYSEARCH_TIMEOUT_SECONDS",
@@ -712,7 +716,7 @@ class Config:
712
716
  if key in JEV_DEFAULTS:
713
717
  validate_jev_value(key, value)
714
718
  return
715
- if key in {"SMART_SEARCH_TIMEOUT_SECONDS", "TINYFISH_TIMEOUT_SECONDS"}:
719
+ if key in {"SMART_SEARCH_TIMEOUT_SECONDS", "TINYFISH_TIMEOUT_SECONDS", "DEEPSEEK_TIMEOUT_SECONDS"}:
716
720
  self._parse_positive_float_value(key, value)
717
721
  return
718
722
  if key == "SMART_SEARCH_PROVIDER_COOLDOWN_SECONDS":
@@ -970,6 +974,18 @@ class Config:
970
974
  def tinyfish_timeout(self) -> float:
971
975
  return self._parse_positive_float_value("TINYFISH_TIMEOUT_SECONDS", self._get_config_value("TINYFISH_TIMEOUT_SECONDS", "150") or "150")
972
976
 
977
+ @property
978
+ def deepseek_api_key(self) -> str | None:
979
+ return self._get_config_value("DEEPSEEK_API_KEY")
980
+
981
+ @property
982
+ def deepseek_api_url(self) -> str:
983
+ return self._get_config_value("DEEPSEEK_API_URL", "https://api.deepseek.com") or "https://api.deepseek.com"
984
+
985
+ @property
986
+ def deepseek_timeout(self) -> float:
987
+ return self._parse_positive_float_value("DEEPSEEK_TIMEOUT_SECONDS", self._get_config_value("DEEPSEEK_TIMEOUT_SECONDS", "60") or "60")
988
+
973
989
  @property
974
990
  def anysearch_api_url(self) -> str:
975
991
  return self._get_config_value("ANYSEARCH_API_URL", "https://api.anysearch.com/mcp") or "https://api.anysearch.com/mcp"
@@ -1253,6 +1269,9 @@ class Config:
1253
1269
  "TINYFISH_FETCH_API_URL": self.tinyfish_fetch_api_url,
1254
1270
  "TINYFISH_API_KEY": self._mask_api_key(self.tinyfish_api_key) if self.tinyfish_api_key else tr('未配置'),
1255
1271
  "TINYFISH_TIMEOUT_SECONDS": self.tinyfish_timeout,
1272
+ "DEEPSEEK_API_URL": self.deepseek_api_url,
1273
+ "DEEPSEEK_API_KEY": self._mask_api_key(self.deepseek_api_key) if self.deepseek_api_key else tr('未配置'),
1274
+ "DEEPSEEK_TIMEOUT_SECONDS": self.deepseek_timeout,
1256
1275
  "ANYSEARCH_API_URL": self.anysearch_api_url,
1257
1276
  "ANYSEARCH_API_KEY": self._mask_api_key(self.anysearch_api_key) if self.anysearch_api_key else tr('未配置'),
1258
1277
  "ANYSEARCH_TIMEOUT_SECONDS": self.anysearch_timeout,
@@ -89,9 +89,13 @@ def fallback_channels(svc: Any, query: str, candidates: list[dict], limit: int,
89
89
  if reading:
90
90
  order = svc._research_fetch_order(query, reading[0]["url"])
91
91
  return sorted(reading, key=lambda item: order.index(item["provider"]) if item["provider"] in order else len(order))[:limit]
92
+ allowed = []
92
93
  if rule.docs_intent:
93
94
  allowed = [item for item in candidates if item["operation"] == "search" and item["capability"] == "docs_search"]
94
- else:
95
+ # With no docs channel configured, a docs question still gets general web
96
+ # discovery instead of no channel at all. Failure fallback passes only
97
+ # same-family candidates, so this cannot cross a capability boundary.
98
+ if not allowed:
95
99
  allowed = [item for item in candidates if item["operation"] == "search" and
96
100
  (item["capability"] in {"main_search", "web_search"} or item["provider"] == "exa" or
97
101
  item["capability"] in rule.required_capabilities)]
@@ -162,6 +166,8 @@ class ChannelExecutor:
162
166
  content = _check_data(await svc.zhipu_mcp_reader(url)).get("content")
163
167
  elif provider == "tinyfish":
164
168
  content = _check_data(await svc.call_tinyfish_fetch(url)).get("content")
169
+ elif provider == "deepseek":
170
+ content = await svc.call_deepseek_fetch(url)
165
171
  else:
166
172
  raise ProviderCallError("parameter_error", source_message('Unsupported fetch channel'))
167
173
  results = [{"url": url, "content": content}]
@@ -177,6 +183,8 @@ class ChannelExecutor:
177
183
  results = await svc.call_firecrawl_search(query, self.count) or []
178
184
  elif provider == "tinyfish":
179
185
  results = await svc.call_tinyfish_search(query, self.count) or []
186
+ elif provider == "deepseek":
187
+ results = await svc.call_deepseek_search(query, self.count) or []
180
188
  elif provider == "anysearch":
181
189
  results = _check_data(await svc.anysearch_search(query, max_results=self.count)).get("results", [])
182
190
  elif provider == "context7":
@@ -113,6 +113,20 @@ PROVIDER_DESCRIPTIONS: dict[str, dict[str, dict[str, str]]] = {
113
113
  "guidance_zh": "擅长读取已找到的页面;不适合发现新来源或执行浏览器自动化。",
114
114
  },
115
115
  },
116
+ "deepseek": {
117
+ "search": {
118
+ "en": "Searches the web with DeepSeek's own search engine and returns linked results with long query-focused summaries.",
119
+ "zh": "通过 DeepSeek 自研搜索引擎检索网页,返回链接和与问题相关的长摘要。",
120
+ "guidance_en": "Best for broad web and Chinese-language source discovery; summaries are not full page text, so read key pages before citing them. Queries longer than 128 characters are shortened.",
121
+ "guidance_zh": "擅长广泛发现网页和中文来源;摘要不是网页全文,引用关键结论前应读取原页。超过 128 个字符的查询会被截短。",
122
+ },
123
+ "fetch": {
124
+ "en": "Fetches a known URL as markdown through DeepSeek Fetch.",
125
+ "zh": "通过 DeepSeek Fetch 将已知链接读取为 Markdown。",
126
+ "guidance_en": "Best for reading public web pages already found; unsuitable for source discovery, PDF files, login-only pages or private network addresses.",
127
+ "guidance_zh": "擅长读取已找到的公开网页;不适合发现新来源,也读不了 PDF、需登录的页面或内网地址。",
128
+ },
129
+ },
116
130
  "anysearch": {
117
131
  "search": {
118
132
  "en": "Searches AnySearch's available domains with a plain query.",
@@ -1,6 +1,7 @@
1
1
  from .base import BaseSearchProvider, SearchResult
2
2
  from .anysearch import AnySearchProvider
3
3
  from .context7 import Context7Provider
4
+ from .deepseek import DeepSeekToolsProvider
4
5
  from .openai_compatible import OpenAICompatibleSearchProvider
5
6
  from .xai_responses import XAIResponsesSearchProvider
6
7
  from .exa import ExaSearchProvider
@@ -15,6 +16,7 @@ __all__ = [
15
16
  "SearchResult",
16
17
  "AnySearchProvider",
17
18
  "Context7Provider",
19
+ "DeepSeekToolsProvider",
18
20
  "OpenAICompatibleSearchProvider",
19
21
  "XAIResponsesSearchProvider",
20
22
  "ExaSearchProvider",
@@ -0,0 +1,110 @@
1
+ """DeepSeek web search and page fetch.
2
+
3
+ Both tools live under the DeepSeek API host (`/v1/tools/search` and
4
+ `/v1/tools/fetch`) and use the same Bearer key as the DeepSeek chat API.
5
+ """
6
+ from ..i18n import source_message
7
+
8
+ from typing import Any
9
+
10
+ import httpx
11
+
12
+ from ..provider_errors import ProviderCallError, provider_call_error
13
+
14
+
15
+ MAX_QUERY_CHARS = 128
16
+ MAX_COUNT = 50
17
+ # Fetch answers 403 when its safety policy refuses a URL and 404 when the page
18
+ # has no usable Markdown. Both describe that one page, not the key or the
19
+ # service, so they read as "no content" and leave provider health alone.
20
+ PAGE_UNAVAILABLE_STATUSES = frozenset({403, 404})
21
+
22
+
23
+ def tools_endpoint(api_url: str, tool: str) -> str:
24
+ base = api_url.rstrip("/")
25
+ # The chat API documents both https://api.deepseek.com and .../v1 as its base.
26
+ if base.endswith("/v1"):
27
+ base = base[: -len("/v1")]
28
+ return f"{base}/v1/tools/{tool}"
29
+
30
+
31
+ def search_query(query: str) -> str:
32
+ """Fit a query into the 128-character limit, preferring a word boundary."""
33
+ text = " ".join(query.split())
34
+ if len(text) <= MAX_QUERY_CHARS:
35
+ return text
36
+ cut = text[:MAX_QUERY_CHARS]
37
+ space = cut.rfind(" ")
38
+ return cut[:space] if space >= MAX_QUERY_CHARS // 2 else cut
39
+
40
+
41
+ def _normalize_search_result(item: dict[str, Any]) -> dict[str, Any]:
42
+ out: dict[str, Any] = {
43
+ "title": item.get("title") or "",
44
+ "url": item.get("url") or "",
45
+ # summary is the longer, query-focused excerpt DeepSeek writes for models.
46
+ "description": item.get("summary") or item.get("snippet") or "",
47
+ "provider": "deepseek",
48
+ }
49
+ site_name = item.get("site_name") or ""
50
+ if site_name:
51
+ out["source"] = site_name
52
+ published = item.get("date") or ""
53
+ if published:
54
+ out["published_date"] = published
55
+ return out
56
+
57
+
58
+ class DeepSeekToolsProvider:
59
+ def __init__(self, api_url: str, api_key: str, timeout: float = 60.0):
60
+ self.api_url = api_url
61
+ self.api_key = api_key or ""
62
+ self.timeout = timeout
63
+
64
+ def get_provider_name(self) -> str:
65
+ return "deepseek"
66
+
67
+ async def _post(self, tool: str, body: dict[str, Any]) -> httpx.Response:
68
+ headers = {
69
+ "Authorization": f"Bearer {self.api_key}",
70
+ "Content-Type": "application/json",
71
+ "Accept": "application/json",
72
+ }
73
+ timeout = httpx.Timeout(connect=6.0, read=self.timeout, write=10.0, pool=None)
74
+ async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
75
+ return await client.post(tools_endpoint(self.api_url, tool), headers=headers, json=body)
76
+
77
+ async def search(self, query: str, max_results: int = 5) -> list[dict[str, Any]]:
78
+ """Return normalized results; an empty list means DeepSeek found nothing."""
79
+ body = {"query": search_query(query), "count": max(1, min(int(max_results), MAX_COUNT))}
80
+ try:
81
+ response = await self._post("search", body)
82
+ response.raise_for_status()
83
+ payload = response.json()
84
+ if not isinstance(payload, dict):
85
+ raise ProviderCallError("parse_error", source_message('DeepSeek search response is not a JSON object'))
86
+ results = payload.get("results")
87
+ if not isinstance(results, list):
88
+ raise ProviderCallError("parse_error", source_message('DeepSeek search response is missing results'))
89
+ except Exception as exc:
90
+ raise provider_call_error(exc, additional_secrets=(self.api_key,)) from exc
91
+ return [_normalize_search_result(item) for item in results[:max_results] if isinstance(item, dict)]
92
+
93
+ async def fetch(self, url: str) -> dict[str, str] | None:
94
+ """Return {title, content}, or None when DeepSeek has no usable text for this page."""
95
+ try:
96
+ response = await self._post("fetch", {"url": url})
97
+ if response.status_code in PAGE_UNAVAILABLE_STATUSES:
98
+ return None
99
+ response.raise_for_status()
100
+ payload = response.json()
101
+ if not isinstance(payload, dict):
102
+ raise ProviderCallError("parse_error", source_message('DeepSeek fetch response is not a JSON object'))
103
+ content = payload.get("content")
104
+ if not isinstance(content, str):
105
+ raise ProviderCallError("parse_error", source_message('DeepSeek fetch content is not text'))
106
+ except Exception as exc:
107
+ raise provider_call_error(exc, additional_secrets=(self.api_key,)) from exc
108
+ if not content.strip():
109
+ return None
110
+ return {"title": str(payload.get("title") or ""), "content": content}
@@ -30,6 +30,7 @@ from .intent_router import (
30
30
  from .logger import log_info
31
31
  from .providers.anysearch import AnySearchProvider
32
32
  from .providers.context7 import Context7Provider
33
+ from .providers.deepseek import DeepSeekToolsProvider
33
34
  from .providers.exa import ExaSearchProvider
34
35
  from .providers.jina import JinaReaderProvider
35
36
  from .providers.openai_compatible import (
@@ -176,9 +177,9 @@ RESEARCH_JS_HEAVY_KEYWORDS = {
176
177
  RESEARCH_PDF_KEYWORDS = {"pdf", "arxiv", "论文", "paper", ".pdf"}
177
178
  RESEARCH_PROFILE_ORDER = {
178
179
  "main_search": ["xai-responses", "openai-compatible"],
179
- "web_search": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
180
+ "web_search": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
180
181
  "docs_search": ["context7", "exa"],
181
- "web_fetch": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
182
+ "web_fetch": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
182
183
  "vertical_search": ["anysearch"],
183
184
  "site_map": ["tavily"],
184
185
  "synthesis": ["main-search"],
@@ -264,6 +265,14 @@ PROVIDER_PROFILES: dict[str, dict[str, Any]] = {
264
265
  "quality_filters": ["non-empty normalized result", "non-empty extracted content", "challenge page rejection"],
265
266
  "route_reasons": ["broad source discovery", "known URL extraction"],
266
267
  },
268
+ "deepseek": {
269
+ "capability": "web_search",
270
+ "capabilities": ["web_search", "web_fetch"],
271
+ "fallback_group": "web_search/web_fetch",
272
+ "minimum_profile_role": "",
273
+ "quality_filters": ["non-empty normalized result", "non-empty extracted content"],
274
+ "route_reasons": ["broad source discovery", "Chinese/current discovery", "known URL extraction"],
275
+ },
267
276
  "anysearch": {
268
277
  "capability": "vertical_search",
269
278
  "fallback_group": "vertical_search",
@@ -524,6 +533,7 @@ PROVIDER_CREDENTIAL_SOURCES: dict[str, Any] = {
524
533
  "tavily": lambda: (config.tavily_api_key, config.tavily_api_url),
525
534
  "firecrawl": lambda: (config.firecrawl_api_key, config.firecrawl_api_url),
526
535
  "tinyfish": lambda: (config.tinyfish_api_key, config.tinyfish_search_api_url, config.tinyfish_fetch_api_url),
536
+ "deepseek": lambda: (config.deepseek_api_key, config.deepseek_api_url),
527
537
  "exa": lambda: (config.exa_api_key, config.exa_base_url),
528
538
  "context7": lambda: (config.context7_api_key, config.context7_base_url),
529
539
  "jina": lambda: (config.jina_api_key, config.jina_reader_api_url),
@@ -981,6 +991,8 @@ def _provider_configured(provider: str) -> bool:
981
991
  return bool(config.firecrawl_api_key)
982
992
  if provider == "tinyfish":
983
993
  return bool(config.tinyfish_api_key)
994
+ if provider == "deepseek":
995
+ return bool(config.deepseek_api_key)
984
996
  if provider == "anysearch":
985
997
  return bool(config.anysearch_api_key)
986
998
  if provider == "sciverse":
@@ -1507,10 +1519,11 @@ def get_capability_status() -> dict[str, Any]:
1507
1519
  ("tavily", _provider_configured("tavily")),
1508
1520
  ("firecrawl", _provider_configured("firecrawl")),
1509
1521
  ("tinyfish", _provider_configured("tinyfish")),
1522
+ ("deepseek", _provider_configured("deepseek")),
1510
1523
  ]
1511
1524
  if enabled
1512
1525
  ],
1513
- "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
1526
+ "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
1514
1527
  },
1515
1528
  "docs_search": {
1516
1529
  "configured": [
@@ -1532,10 +1545,11 @@ def get_capability_status() -> dict[str, Any]:
1532
1545
  ("zhipu-mcp-reader", _provider_configured("zhipu-mcp-reader")),
1533
1546
  ("firecrawl", _provider_configured("firecrawl")),
1534
1547
  ("tinyfish", _provider_configured("tinyfish")),
1548
+ ("deepseek", _provider_configured("deepseek")),
1535
1549
  ]
1536
1550
  if enabled
1537
1551
  ],
1538
- "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
1552
+ "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
1539
1553
  },
1540
1554
  "vertical_search": {
1541
1555
  "configured": [
@@ -1800,6 +1814,8 @@ async def _run_web_fetch_fallback(
1800
1814
  providers.append("firecrawl")
1801
1815
  if _provider_configured("tinyfish"):
1802
1816
  providers.append("tinyfish")
1817
+ if _provider_configured("deepseek"):
1818
+ providers.append("deepseek")
1803
1819
  if preferred_order:
1804
1820
  allowed = {provider for provider in providers}
1805
1821
  ordered = [provider for provider in preferred_order if provider in allowed]
@@ -1849,6 +1865,8 @@ async def _run_web_fetch_fallback(
1849
1865
  health_extra = _record_provider_result(provider, status, data.get("error_type", ""), data.get("error", ""))
1850
1866
  attempts.append(_attempt("web_fetch", provider, status, start, error_type=data.get("error_type", ""), error=data.get("error", ""), extra=health_extra))
1851
1867
  continue
1868
+ elif provider == "deepseek":
1869
+ content = await bounded(call_deepseek_fetch(url))
1852
1870
  else:
1853
1871
  content = await bounded(call_firecrawl_scrape(url))
1854
1872
  if content and content.strip():
@@ -2086,6 +2104,25 @@ async def call_tinyfish_fetch(url: str) -> dict[str, Any]:
2086
2104
  return await _decode_provider_json(raw, provider="tinyfish")
2087
2105
 
2088
2106
 
2107
+ def _deepseek_provider() -> DeepSeekToolsProvider:
2108
+ return DeepSeekToolsProvider(config.deepseek_api_url, config.deepseek_api_key or "", config.deepseek_timeout)
2109
+
2110
+
2111
+ async def call_deepseek_search(query: str, max_results: int = 6) -> list[dict] | None:
2112
+ """Return normalized DeepSeek candidates; a provider failure raises for fallback."""
2113
+ if not config.provider_enabled("deepseek") or not config.deepseek_api_key:
2114
+ return None
2115
+ return await _deepseek_provider().search(query, max_results=max_results)
2116
+
2117
+
2118
+ async def call_deepseek_fetch(url: str) -> str | None:
2119
+ """Return page Markdown, or None when DeepSeek has no usable text for the URL."""
2120
+ if not config.provider_enabled("deepseek") or not config.deepseek_api_key:
2121
+ return None
2122
+ page = await _deepseek_provider().fetch(url)
2123
+ return page["content"] if page else None
2124
+
2125
+
2089
2126
  async def call_jina_reader(url: str) -> dict[str, Any]:
2090
2127
  if disabled := _provider_disabled_result("jina"):
2091
2128
  return disabled
@@ -2221,7 +2258,7 @@ async def fetch(url: str, timeout_seconds: float | None = None) -> dict[str, Any
2221
2258
 
2222
2259
  configured_fetch_providers = [
2223
2260
  provider
2224
- for provider in ("tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish")
2261
+ for provider in ("tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek")
2225
2262
  if _provider_configured(provider)
2226
2263
  ]
2227
2264
  if not configured_fetch_providers:
@@ -3390,6 +3427,33 @@ async def _test_tinyfish_connection() -> dict[str, Any]:
3390
3427
  return {"status": "ok", "message": source_message('TinyFish 搜索与抓取 API 均可用'), "response_time_ms": _elapsed_ms(start)}
3391
3428
 
3392
3429
 
3430
+ async def _test_deepseek_connection() -> dict[str, Any]:
3431
+ """Probe search and fetch once each; DeepSeek bills every successful request."""
3432
+ if disabled := _provider_disabled_result("deepseek"):
3433
+ return disabled
3434
+ if not config.deepseek_api_key:
3435
+ return {"status": "not_configured", "message": source_message('DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用')}
3436
+ start = time.time()
3437
+ provider = _deepseek_provider()
3438
+ search_result, fetch_result = await asyncio.gather(
3439
+ provider.search("DeepSeek", max_results=1),
3440
+ provider.fetch("https://example.com"),
3441
+ return_exceptions=True,
3442
+ )
3443
+ # Both tools share one key and one health fingerprint, so either failing fails the probe.
3444
+ failed = search_result if isinstance(search_result, Exception) else fetch_result
3445
+ if isinstance(failed, Exception):
3446
+ error_type, error = classify_provider_exception(failed, additional_secrets=(config.deepseek_api_key,))
3447
+ status = error_type if error_type in APPROVED_PROVIDER_ERROR_TYPES else "warning"
3448
+ message = (source_message('DeepSeek search: {0}', error) if failed is search_result
3449
+ else source_message('DeepSeek fetch: {0}', error))
3450
+ return {"status": status, "message": message, "response_time_ms": _elapsed_ms(start)}
3451
+ if fetch_result is None:
3452
+ return {"status": "warning", "message": source_message('DeepSeek fetch returned no content for {0}', "https://example.com"),
3453
+ "response_time_ms": _elapsed_ms(start)}
3454
+ return {"status": "ok", "message": source_message('DeepSeek 搜索与抓取 API 均可用'), "response_time_ms": _elapsed_ms(start)}
3455
+
3456
+
3393
3457
  DOCTOR_PROBE_PROVIDERS = {
3394
3458
  "exa_connection_test": "exa",
3395
3459
  "tavily_connection_test": "tavily",
@@ -3398,6 +3462,7 @@ DOCTOR_PROBE_PROVIDERS = {
3398
3462
  "zhipu_mcp_connection_test": "zhipu-mcp",
3399
3463
  "context7_connection_test": "context7",
3400
3464
  "tinyfish_connection_test": "tinyfish",
3465
+ "deepseek_connection_test": "deepseek",
3401
3466
  }
3402
3467
  DOCTOR_PROBE_NEUTRAL_STATUSES = {"not_configured", "configured", "skipped", "disabled"}
3403
3468
 
@@ -3441,6 +3506,7 @@ PROBE_KIND: dict[str, str] = {
3441
3506
  "zhipu-mcp": "live",
3442
3507
  "context7": "live",
3443
3508
  "tinyfish": "live",
3509
+ "deepseek": "live",
3444
3510
  "anysearch": "live",
3445
3511
  "sciverse": "live",
3446
3512
  "zhipu-mcp-reader": "shared:zhipu-mcp",
@@ -3454,6 +3520,7 @@ _LIVE_PROBES: dict[str, Any] = {
3454
3520
  "zhipu-mcp": _test_zhipu_mcp_connection,
3455
3521
  "context7": _test_context7_connection,
3456
3522
  "tinyfish": _test_tinyfish_connection,
3523
+ "deepseek": _test_deepseek_connection,
3457
3524
  "anysearch": _test_anysearch_connection,
3458
3525
  "sciverse": _test_sciverse_connection,
3459
3526
  "firecrawl": _test_firecrawl_presence,
@@ -3686,6 +3753,13 @@ async def doctor() -> dict[str, Any]:
3686
3753
  except Exception as e:
3687
3754
  info["tinyfish_connection_test"] = {"status": "error", "message": sanitize_provider_error_message(e)}
3688
3755
 
3756
+ try:
3757
+ info["deepseek_connection_test"] = await _test_deepseek_connection()
3758
+ except httpx.TimeoutException:
3759
+ info["deepseek_connection_test"] = {"status": "timeout", "message": source_message('DeepSeek API 请求超时')}
3760
+ except Exception as e:
3761
+ info["deepseek_connection_test"] = {"status": "error", "message": sanitize_provider_error_message(e)}
3762
+
3689
3763
  _record_doctor_probes(info)
3690
3764
  info["provider_health"] = provider_health_status()
3691
3765
 
@@ -3755,7 +3829,7 @@ async def doctor() -> dict[str, Any]:
3755
3829
  else:
3756
3830
  info["jev_connection_test"] = {"status": "skipped", "message": source_message('Using local routing; no Jev probe was sent.')}
3757
3831
  retrieval_tests = [*main_connection_tests.values(), *(info.get(f"{name}_connection_test", {}) for name in (
3758
- "exa", "tavily", "jina", "firecrawl", "zhipu", "zhipu_mcp", "context7", "tinyfish",
3832
+ "exa", "tavily", "jina", "firecrawl", "zhipu", "zhipu_mcp", "context7", "tinyfish", "deepseek",
3759
3833
  ))]
3760
3834
  retrieval_ready = any(test.get("status") in {"ok", "configured"} for test in retrieval_tests)
3761
3835
  info["retrieval_connection_verified"] = any(test.get("status") == "ok" for test in retrieval_tests)
@@ -3941,9 +4015,9 @@ async def _smoke_mock(start: float) -> dict[str, Any]:
3941
4015
  "fallback_chain": MAIN_SEARCH_FALLBACK_CHAIN,
3942
4016
  "ok": True,
3943
4017
  },
3944
- "web_search": {"configured": ["zhipu"], "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"], "ok": True},
4018
+ "web_search": {"configured": ["zhipu"], "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"], "ok": True},
3945
4019
  "docs_search": {"configured": ["context7"], "fallback_chain": ["context7", "exa"], "ok": True},
3946
- "web_fetch": {"configured": ["tavily"], "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"], "ok": True},
4020
+ "web_fetch": {"configured": ["tavily"], "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"], "ok": True},
3947
4021
  "vertical_search": {
3948
4022
  "configured": [],
3949
4023
  "fallback_chain": ["anysearch"],
@@ -4172,13 +4246,13 @@ async def _smoke_mock(start: float) -> dict[str, Any]:
4172
4246
  **minimum_status,
4173
4247
  "web_search": {
4174
4248
  "configured": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
4175
- "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
4249
+ "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
4176
4250
  "ok": True,
4177
4251
  },
4178
4252
  "docs_search": {"configured": ["context7", "exa"], "fallback_chain": ["context7", "exa"], "ok": True},
4179
4253
  "web_fetch": {
4180
4254
  "configured": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
4181
- "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
4255
+ "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
4182
4256
  "ok": True,
4183
4257
  },
4184
4258
  "vertical_search": {
@@ -382,6 +382,24 @@ CONFIG_FIELDS: tuple[ConfigField, ...] = (
382
382
  label_zh="TinyFish 超时(秒)", label_en="TinyFish timeout (seconds)",
383
383
  default="150", provider="tinyfish", capabilities=("web_search", "web_fetch")),
384
384
 
385
+ # ---- providers: DeepSeek ----------------------------------------------
386
+ _f(key="DEEPSEEK_API_KEY", section="providers", tier="enhancement", kind="secret",
387
+ label_zh="DeepSeek API Key", label_en="DeepSeek API key",
388
+ help_zh="与 DeepSeek 模型 API 共用 Key,一个 Key 支持网页搜索和抓取;按成功请求次数计费。测试会各发起一次真实请求。",
389
+ help_en="The same key as the DeepSeek model API; one key enables web search and page fetching. Billed per successful request. Testing makes one real request for each capability.",
390
+ provider="deepseek", capabilities=("web_search", "web_fetch"),
391
+ key_url="https://platform.deepseek.com/api_keys", docs_url="https://api-docs.deepseek.com/guides/search"),
392
+ _f(key="DEEPSEEK_API_URL", section="providers", tier="advanced", kind="url",
393
+ help_zh="DeepSeek API 基础地址,程序会自动拼接 /v1/tools/search 和 /v1/tools/fetch;使用官方服务时保留默认值。",
394
+ help_en="DeepSeek API base URL. /v1/tools/search and /v1/tools/fetch are appended automatically; keep the default for the official service.",
395
+ label_zh="DeepSeek 地址", label_en="DeepSeek API URL",
396
+ default="https://api.deepseek.com", provider="deepseek", capabilities=("web_search", "web_fetch")),
397
+ _f(key="DEEPSEEK_TIMEOUT_SECONDS", section="providers", tier="advanced", kind="float",
398
+ help_zh="等待此服务单次响应的最长秒数;超时会返回错误或按当前策略兜底,通常保留默认值。",
399
+ help_en="Maximum seconds to wait for one response from this service. A timeout returns an error or uses the configured fallback; normally leave the default.",
400
+ label_zh="DeepSeek 超时(秒)", label_en="DeepSeek timeout (seconds)",
401
+ default="60", provider="deepseek", capabilities=("web_search", "web_fetch")),
402
+
385
403
  # ---- providers: AnySearch ---------------------------------------------
386
404
  _f(key="ANYSEARCH_API_KEY", section="providers", tier="enhancement", kind="secret",
387
405
  label_zh="AnySearch API Key", label_en="AnySearch API key",
@@ -25,7 +25,9 @@ import hmac
25
25
  import json
26
26
  import os
27
27
  import secrets
28
+ import shutil
28
29
  import socket
30
+ import subprocess
29
31
  import sys
30
32
  import threading
31
33
  import time
@@ -438,10 +440,35 @@ def _should_open_browser(options: UIServerConfig) -> bool:
438
440
  if sys.platform.startswith("linux"):
439
441
  if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
440
442
  return True
441
- return bool(os.environ.get("WSL_DISTRO_NAME") or os.environ.get("WSL_INTEROP"))
443
+ return _is_wsl()
442
444
  return True
443
445
 
444
446
 
447
+ def _is_wsl() -> bool:
448
+ return sys.platform.startswith("linux") and bool(os.environ.get("WSL_DISTRO_NAME") or os.environ.get("WSL_INTEROP"))
449
+
450
+
451
+ def _open_browser(url: str) -> bool:
452
+ """Open the page, handing it to the Windows default browser inside WSL.
453
+
454
+ WSL rarely has a Linux browser, so ``webbrowser`` finds nothing or falls back
455
+ to a terminal browser. The server listens on 127.0.0.1, which WSL forwards to
456
+ Windows. An explicit $BROWSER still wins.
457
+ """
458
+ if not _is_wsl() or os.environ.get("BROWSER"):
459
+ return webbrowser.open(url)
460
+ for opener in (shutil.which("wslview"), shutil.which("explorer.exe"), "/mnt/c/Windows/explorer.exe"):
461
+ if not opener or not os.path.isfile(opener):
462
+ continue
463
+ try:
464
+ # explorer.exe exits non-zero even when it opened the page, so do not wait on it.
465
+ subprocess.Popen([opener, url], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
466
+ except OSError:
467
+ continue
468
+ return True
469
+ return False
470
+
471
+
445
472
  def _startup_notes(runtime: UIRuntime, opened: bool, lang: str) -> list[str]:
446
473
  zh = lang == "zh"
447
474
  notes = [
@@ -454,7 +481,21 @@ def _startup_notes(runtime: UIRuntime, opened: bool, lang: str) -> list[str]:
454
481
  else "The token in that link is regenerated on every run and only works on this machine."
455
482
  ),
456
483
  ]
457
- if not opened:
484
+ if _is_wsl():
485
+ if not opened:
486
+ notes.append(
487
+ "没有自动打开浏览器,在 Windows 的浏览器里打开上面的地址即可。"
488
+ if zh
489
+ else "No browser was opened; open the URL above in a Windows browser."
490
+ )
491
+ # NAT mode forwards localhost; mirrored mode's Hyper-V firewall blocks
492
+ # Windows-to-WSL connections until inbound traffic is allowed.
493
+ notes.append(
494
+ "WSL 使用 networkingMode=mirrored 时,Hyper-V 防火墙默认拦截 Windows 访问 WSL 端口;页面打不开时用 --port 固定端口,并按排障手册放行该端口。"
495
+ if zh
496
+ else "With WSL networkingMode=mirrored, the Hyper-V firewall blocks Windows from reaching WSL ports by default. If the page does not load, fix the port with --port and allow it as the troubleshooting guide describes."
497
+ )
498
+ elif not opened:
458
499
  notes.append(
459
500
  "没有自动打开浏览器,复制上面的地址即可。" if zh else "No browser was opened; copy the URL above."
460
501
  )
@@ -480,7 +521,7 @@ def serve(options: UIServerConfig, *, announce: Callable[[str], None] = lambda l
480
521
  opened = False
481
522
  if _should_open_browser(options):
482
523
  try:
483
- opened = webbrowser.open(runtime.url)
524
+ opened = _open_browser(runtime.url)
484
525
  except Exception:
485
526
  opened = False
486
527
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smart-search
3
- Version: 0.1.28
3
+ Version: 0.1.29
4
4
  Summary: CLI-first multi-source web research with xAI/OpenAI-compatible search, Exa, Context7, Zhipu, Tavily, Firecrawl, and Deep Research planning.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,13 +1,13 @@
1
1
  ../../../bin/smart-search,sha256=3Si0-mMx4MCMjCfKNF0xeBuTy8aXkDufWgG0psj8uJs,205
2
- smart_search-0.1.28.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
- smart_search-0.1.28.dist-info/METADATA,sha256=Hq7LYe74BDvia9_K6HfeDQIVI96TX7DphV9HjqK-CVI,6721
4
- smart_search-0.1.28.dist-info/RECORD,,
5
- smart_search-0.1.28.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- smart_search-0.1.28.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
- smart_search-0.1.28.dist-info/direct_url.json,sha256=lFRY6ow2tINE_t60B1wCO0WZXfSjjC9GUsp1Y2OjrAY,75
8
- smart_search-0.1.28.dist-info/entry_points.txt,sha256=fiHguhz7hQ6ybeepO0OPpZm19XCi2J-Bkigf8DGBuKM,55
9
- smart_search-0.1.28.dist-info/licenses/LICENSE,sha256=o_zbH827gkTiZfsNLJGQn1z8Z4eLAvb8heFaSqSGVv8,1067
10
- smart_search-0.1.28.dist-info/top_level.txt,sha256=mBPprcb_b3tBApeRa2nlLaGj3UVNxXCdyz6QQvDzJpc,13
2
+ smart_search-0.1.29.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
+ smart_search-0.1.29.dist-info/METADATA,sha256=J9te_ouaOFc2g25v4IxzBYvw6llk4z5jo50wFqluzEA,6721
4
+ smart_search-0.1.29.dist-info/RECORD,,
5
+ smart_search-0.1.29.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ smart_search-0.1.29.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ smart_search-0.1.29.dist-info/direct_url.json,sha256=lFRY6ow2tINE_t60B1wCO0WZXfSjjC9GUsp1Y2OjrAY,75
8
+ smart_search-0.1.29.dist-info/entry_points.txt,sha256=fiHguhz7hQ6ybeepO0OPpZm19XCi2J-Bkigf8DGBuKM,55
9
+ smart_search-0.1.29.dist-info/licenses/LICENSE,sha256=o_zbH827gkTiZfsNLJGQn1z8Z4eLAvb8heFaSqSGVv8,1067
10
+ smart_search-0.1.29.dist-info/top_level.txt,sha256=mBPprcb_b3tBApeRa2nlLaGj3UVNxXCdyz6QQvDzJpc,13
11
11
  smart_search/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
12
12
  smart_search/__pycache__/__init__.cpython-313.pyc,,
13
13
  smart_search/__pycache__/activity.cpython-313.pyc,,
@@ -40,19 +40,19 @@ smart_search/__pycache__/ui_metadata.cpython-313.pyc,,
40
40
  smart_search/__pycache__/ui_server.cpython-313.pyc,,
41
41
  smart_search/__pycache__/utils.cpython-313.pyc,,
42
42
  smart_search/activity.py,sha256=66yPPAfuaDSF4UTEuRRDefp2m1-l8eVnr8K5_ShoQCU,13368
43
- smart_search/assets/i18n/messages.json,sha256=uBrLqgRGaQ3NEYzJe0FhbYn9PWFKMJ6Rja-6wHfIWRY,631118
43
+ smart_search/assets/i18n/messages.json,sha256=eVpQhvVBwQc6eBDTZO6af0uNTmoetzzl0qRQnrB9QNk,635958
44
44
  smart_search/assets/skills/smart-search-cli/SKILL.md,sha256=465CCVKGCHfiJLDD9LlYbIhlbxWucwLiNVVAcRRlEf8,8561
45
45
  smart_search/assets/skills/smart-search-cli/agents/openai.yaml,sha256=4S1VuEER7Puw9JvxDsyI__Tdrr1j2h6aF26yHho_x8E,162
46
46
  smart_search/assets/skills/smart-search-cli/references/cli-contract.md,sha256=mZyQRioJ4_ySkxcl9WQBMGXk5fcWOEMBMnXXUGnRoEA,1843
47
- smart_search/assets/skills/smart-search-cli/references/cli-core.md,sha256=Ksjwc2cBBTRbzEyYXOI67LrRjw3n42LoTOlxqSVpqgQ,14954
48
- smart_search/assets/skills/smart-search-cli/references/command-patterns.md,sha256=Y1N7kJ12Z23ZNHy-TkJUEMj9cXR-ut6NZwU5V8ImwHA,12391
47
+ smart_search/assets/skills/smart-search-cli/references/cli-core.md,sha256=7Wf9csFgjRfQmk7EyssAZBn7BWbw6yNQcu_1-T9LJXA,15029
48
+ smart_search/assets/skills/smart-search-cli/references/command-patterns.md,sha256=PvMBXjDg0acGf-9uZAyK3p_Ox_kI5aQuhae12WsqRj0,12534
49
49
  smart_search/assets/skills/smart-search-cli/references/deep-research-mode.md,sha256=i1ZK5yo7ni5LIs79WrNOY75BmTS9G4IIuxxgQ6b6jSM,14398
50
- smart_search/assets/skills/smart-search-cli/references/provider-routing.md,sha256=ltmos046HLWl1euEwIo4ODBj54zwIotCw94DVld6Unk,25482
50
+ smart_search/assets/skills/smart-search-cli/references/provider-routing.md,sha256=XQXOI6xZUcCzg1UtDXBpggKVQAC7cNDZwYOU5tOrG8Q,27195
51
51
  smart_search/assets/skills/smart-search-cli/references/regression-release.md,sha256=SkOUwxpPm_HeBGlpUil5ybTBiwa-wAzt-765zw9kYqk,5673
52
- smart_search/assets/skills/smart-search-cli/references/setup-config.md,sha256=kiWNSOIvQSsYOCkyfTnWe_jox_Flv37Si5H252Omy2o,17755
53
- smart_search/assets/ui/index.html,sha256=IR492tBdUhAPRa6t14xZeLwS4OysHRCEdEMA1d42uEY,63788
54
- smart_search/cli.py,sha256=PTVCv-BP_OP62sH8mosNObV_gBW_gpSq-STuk0lWB9Y,164787
55
- smart_search/config.py,sha256=5Yn__0qLkmkR5ZOWnst_xDvdnlqN5kkWKB0ltEjrFZ8,55907
52
+ smart_search/assets/skills/smart-search-cli/references/setup-config.md,sha256=Bi-yqcoGb0T1SV5tzJLwW9gW_ygx00LPTKXHAHM2t7c,18213
53
+ smart_search/assets/ui/index.html,sha256=i3se6JX5iHnGXIEXhHvLKZgPzNh8JMJAeRqfIlqac3U,63800
54
+ smart_search/cli.py,sha256=TDRYJ99KPbudCwKP2w5Mvta6TiCVrCyljUNEPqnopzs,166464
55
+ smart_search/config.py,sha256=ci_r_EMotBLHP8NxyAybcRPVhw11kZCW1s7aRKorEYI,56793
56
56
  smart_search/desktop_backend.py,sha256=VYFc83YBllSljgbgx9oPPNbQLwGADZGVtN_CF08ShD8,42417
57
57
  smart_search/desktop_catalog.py,sha256=VMIsLFAD98Czetwy-ka606QnyUSk0gDBhsfz7k4QDl8,11485
58
58
  smart_search/desktop_cli.py,sha256=dpXLwf6b9Dgw9lKPTDZpKH1Z85OpIgrYyNJM2hXIu68,11069
@@ -64,16 +64,17 @@ smart_search/desktop_worker.py,sha256=Nkw_FMNNejrt8oJ6aNUmziSd7vVu_Xy9ijvfgstdaw
64
64
  smart_search/i18n.py,sha256=-mDfpi-P4AQPCUpnuI--W9c1AFurN5QPhAGYMXB02kU,4869
65
65
  smart_search/intent_router.py,sha256=bj4DnveNEFj9oIpndOZKRDlyAJwuT5q4dam_dY4cG60,7340
66
66
  smart_search/jev.py,sha256=C80OBG2RMOJCMtogltVDHUg0xoCHO_8uIT69736myq8,27169
67
- smart_search/jev_search.py,sha256=u0AiBBH9kFuALpOypTCnpfXAW3sz2RSwkFoZjb5rxBo,48084
67
+ smart_search/jev_search.py,sha256=BWBpLVDLcOl2DhRAp3iKJXeeo2twDp3csnUoAcNIkhE,48555
68
68
  smart_search/logger.py,sha256=pwfjP50ywoTRirH4H7qYTNgt4TgoMhZlX2pb8dNvO9k,1203
69
- smart_search/provider_descriptions.py,sha256=6bdv5HQsZI2noTWBKogFZh40I5CxoWOp0lhTs33GJPU,9026
69
+ smart_search/provider_descriptions.py,sha256=Y5I1vOz-trKoVfXZ3bQLVfA1aHIRAJ4maN42ya-Zqds,10234
70
70
  smart_search/provider_errors.py,sha256=IXyLo6Kc4WSP7faH5mMTQyAIVKR0BR0680qd0ZApKUo,5384
71
71
  smart_search/provider_health.py,sha256=-yIA4vvJAvyVjGLvnjEnXR1OT4cfVp0hrJTIYbKYNwY,11625
72
- smart_search/providers/__init__.py,sha256=fG_AJ3sgqUmLRvv0YAWK7DRWRBXftcrtm7NeAx4HB7c,878
72
+ smart_search/providers/__init__.py,sha256=0uh_h4VhGGgJ88jS6VL4Ih_0PcytrdpIXpgTx2uvBhw,951
73
73
  smart_search/providers/__pycache__/__init__.cpython-313.pyc,,
74
74
  smart_search/providers/__pycache__/anysearch.cpython-313.pyc,,
75
75
  smart_search/providers/__pycache__/base.cpython-313.pyc,,
76
76
  smart_search/providers/__pycache__/context7.cpython-313.pyc,,
77
+ smart_search/providers/__pycache__/deepseek.cpython-313.pyc,,
77
78
  smart_search/providers/__pycache__/exa.cpython-313.pyc,,
78
79
  smart_search/providers/__pycache__/jina.cpython-313.pyc,,
79
80
  smart_search/providers/__pycache__/openai_compatible.cpython-313.pyc,,
@@ -85,6 +86,7 @@ smart_search/providers/__pycache__/zhipu_mcp.cpython-313.pyc,,
85
86
  smart_search/providers/anysearch.py,sha256=rtuxnORxV4DsBParAR7YVKIV_06v0QdFOx3dI36jFZI,13640
86
87
  smart_search/providers/base.py,sha256=8PQWAWJ3XCmlTKV3_oaPUQ2CSU-BXGULRzKg6n3zt9s,995
87
88
  smart_search/providers/context7.py,sha256=zOImhEiKUVg60h9SbYdJRBSUfoVvS-8N6O62sudGs_s,6481
89
+ smart_search/providers/deepseek.py,sha256=br_XHY0lc5xvgppp_4dPd_g5UU9jzEVCronn_UvyoH4,4678
88
90
  smart_search/providers/exa.py,sha256=6BLnSxDLM0PKXlkcqRKw49G728yBkRicDzQiJt2vIzo,6875
89
91
  smart_search/providers/jina.py,sha256=hz8KAcFSstWT4DIBWKdEhymYjBjUvo_SFeZcwCdPkTs,4048
90
92
  smart_search/providers/openai_compatible.py,sha256=CL-zuya3gx8-8_hGn7rTT5vXtubYKfnHe2aC2jzcSI0,41439
@@ -94,12 +96,12 @@ smart_search/providers/xai_responses.py,sha256=3mkh0UuQBzxONT8lWzuwEOISXYHWst_5N
94
96
  smart_search/providers/zhipu.py,sha256=PN0_Vc51rDYlxEmz2B1vgSxEt9jXf_RIsRobGp-LBPs,7088
95
97
  smart_search/providers/zhipu_mcp.py,sha256=4XgckrHzwSdhUtMlTSvhVxrAA7Fr7QX_4BYWfSHmXUg,10976
96
98
  smart_search/sciverse_schema.py,sha256=ml5iDXEpY7cnEMz74BGTo4yxX7UvPOe_i7w4Pi5t1wY,18178
97
- smart_search/service.py,sha256=ImjlAf5EQdD5Rt89651pJa-tfauVjA3uMHk5sUHuE_E,188360
99
+ smart_search/service.py,sha256=7G9YJUDLom5jzCrYkFGKcLnix3uQCL2QKVCHYHWC-sA,192433
98
100
  smart_search/skill_installer.py,sha256=CvM9GQShaTR8ttNhilx8Up0vVsHZfsnYD5uM7wZrzBs,21782
99
101
  smart_search/skill_maintenance.py,sha256=2s2-1ZEEjRg2MfRKlaw6Y51OO86kOf9axl7fW6KMv0g,8818
100
102
  smart_search/sources.py,sha256=WvyRXqX_IdbDPpsqcSbulipxNtZvQbWv1MsZ2Ud5bTY,14376
101
103
  smart_search/state_files.py,sha256=K02dlR3Xuj2vIkRiEdrFr3OSiEsPxN5_HSo4NTmz0sc,2063
102
104
  smart_search/ui_api.py,sha256=x3ULS415MPjZ96iZSOeNK4npzOLJ6GK_DymYi6_tPgA,10027
103
- smart_search/ui_metadata.py,sha256=k0So6BzoVGPaXREHFeXJsYjlqhPZYX9QL5XHjJY0pGk,49151
104
- smart_search/ui_server.py,sha256=WI7cqihwvTPJHKSm2AJC_QuIPvBs09u72uAQwUkcimM,18559
105
+ smart_search/ui_metadata.py,sha256=6WO8WT-P9bwkmgi2i4XXXUqSL4xUijd4gJnImS_cka8,51028
106
+ smart_search/ui_server.py,sha256=pcAPiYWmJXTmX1Xpy12v97JhkbRx-nJ3zRYefGW1Tc0,20508
105
107
  smart_search/utils.py,sha256=HVpxtyveEHcpGSkIObf97GbCJVRd68f1X6ecQeH14Ks,8992
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konbakuyomu/smart-search",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "CLI-first multi-source web research with xAI/OpenAI-compatible search, Exa, Context7, Zhipu, Tavily, Firecrawl, and Deep Research planning.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/konbakuyomu/smartsearch#readme",
@@ -39,11 +39,11 @@
39
39
  },
40
40
  "smartSearchBinary": true,
41
41
  "optionalDependencies": {
42
- "@konbakuyomu/smart-search-darwin-x64": "0.1.28",
43
- "@konbakuyomu/smart-search-darwin-arm64": "0.1.28",
44
- "@konbakuyomu/smart-search-win32-x64": "0.1.28",
45
- "@konbakuyomu/smart-search-win32-arm64": "0.1.28",
46
- "@konbakuyomu/smart-search-linux-x64": "0.1.28",
47
- "@konbakuyomu/smart-search-linux-arm64": "0.1.28"
42
+ "@konbakuyomu/smart-search-darwin-x64": "0.1.29",
43
+ "@konbakuyomu/smart-search-darwin-arm64": "0.1.29",
44
+ "@konbakuyomu/smart-search-win32-x64": "0.1.29",
45
+ "@konbakuyomu/smart-search-win32-arm64": "0.1.29",
46
+ "@konbakuyomu/smart-search-linux-x64": "0.1.29",
47
+ "@konbakuyomu/smart-search-linux-arm64": "0.1.29"
48
48
  }
49
49
  }
Binary file