@xiaoso/dsh-tool-plus 0.1.0-beta.2 → 0.1.0-beta.4

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 (235) hide show
  1. package/README.en.md +122 -0
  2. package/README.md +3 -2
  3. package/THIRD_PARTY_NOTICES.md +4 -35
  4. package/lib/abort-B7izMMna.mjs +51 -0
  5. package/lib/abort-D1_Z5Ut5.mjs +51 -0
  6. package/lib/abort-dIkZBV7Q.mjs +51 -0
  7. package/lib/amazon-bedrock-B0nxT06F.mjs +917 -0
  8. package/lib/amazon-bedrock-D-shAr1X.mjs +917 -0
  9. package/lib/amazon-bedrock-WQhvjHYu.mjs +917 -0
  10. package/lib/anthropic-BWsZFzU8.mjs +262 -0
  11. package/lib/anthropic-CJCauvYz.mjs +3115 -0
  12. package/lib/anthropic-CMT1Qj1t.mjs +3115 -0
  13. package/lib/anthropic-CZghe_9U.mjs +3115 -0
  14. package/lib/anthropic-sCMH7JID.mjs +262 -0
  15. package/lib/anthropic-tTntD8BD.mjs +262 -0
  16. package/lib/api-key-login-BzqNdM2y.mjs +157 -0
  17. package/lib/api-key-login-CWVwvHT1.mjs +157 -0
  18. package/lib/api-key-login-CpzSezsi.mjs +157 -0
  19. package/lib/aws-credentials-8Mgn5MAy.mjs +867 -0
  20. package/lib/aws-credentials-CXryMobh.mjs +867 -0
  21. package/lib/aws-credentials-ChG79ey-.mjs +867 -0
  22. package/lib/azure-openai-responses-CNkHiLUh.mjs +274 -0
  23. package/lib/azure-openai-responses-Cgmc-rbL.mjs +274 -0
  24. package/lib/azure-openai-responses-DuukbD0F.mjs +274 -0
  25. package/lib/bun-shim.mjs +252 -12
  26. package/lib/bundled-references-CSuBco5p.mjs +55 -0
  27. package/lib/bundled-references-Cavaffah.mjs +55 -0
  28. package/lib/bundled-references-sLN0YdXU.mjs +55 -0
  29. package/lib/callback-server-BEFNf-jf.mjs +398 -0
  30. package/lib/callback-server-CQ5Jo8F-.mjs +398 -0
  31. package/lib/callback-server-DDSrZ6l_.mjs +398 -0
  32. package/lib/client.js +111 -41
  33. package/lib/cursor-B3zg7EHj.mjs +273 -0
  34. package/lib/cursor-B6TUEmgP.mjs +135 -0
  35. package/lib/cursor-BIQfBC6V.mjs +135 -0
  36. package/lib/cursor-D8gvtTL3.mjs +135 -0
  37. package/lib/cursor-DNk6eXDd.mjs +5470 -0
  38. package/lib/cursor-DkSX2h3H.mjs +5441 -0
  39. package/lib/cursor-MjSm74IJ.mjs +273 -0
  40. package/lib/cursor-aJrXeYCb.mjs +273 -0
  41. package/lib/cursor-mJQwUKXK.mjs +5441 -0
  42. package/lib/device-code-DBo3B5nh.mjs +51 -0
  43. package/lib/device-code-DIb0FU-H.mjs +51 -0
  44. package/lib/device-code-OI_tQcmc.mjs +51 -0
  45. package/lib/devin-BHZZyMhO.mjs +95 -0
  46. package/lib/devin-BK7DHeca.mjs +114 -0
  47. package/lib/devin-CfMp3cXS.mjs +95 -0
  48. package/lib/devin-CldyPD8K.mjs +95 -0
  49. package/lib/devin-CzRpTUOA.mjs +114 -0
  50. package/lib/devin-D9zR5B_R.mjs +542 -0
  51. package/lib/devin-DXOeNyU7.mjs +542 -0
  52. package/lib/devin-Q1yem3lp.mjs +114 -0
  53. package/lib/devin-vNZTNUON.mjs +542 -0
  54. package/lib/error-B3bevhrs.mjs +1449 -0
  55. package/lib/error-Dd8DIqs-.mjs +1449 -0
  56. package/lib/error-yCSG0mGC.mjs +1449 -0
  57. package/lib/event-stream-BDPi64mK.mjs +151 -0
  58. package/lib/event-stream-BbFZJy-z.mjs +151 -0
  59. package/lib/event-stream-loUa0s1T.mjs +151 -0
  60. package/lib/fs-promises-shim-Bx0fpxuk.mjs +57 -0
  61. package/lib/github-copilot-4GaMtQmg.mjs +208 -0
  62. package/lib/github-copilot-B1yiyVRh.mjs +104 -0
  63. package/lib/github-copilot-B2HGSIJK.mjs +104 -0
  64. package/lib/github-copilot-BWT3eAOF.mjs +208 -0
  65. package/lib/github-copilot-CYkjeTi3.mjs +208 -0
  66. package/lib/github-copilot-jscUZ8Hf.mjs +104 -0
  67. package/lib/gitlab-duo-CM7Ref1H.mjs +155 -0
  68. package/lib/gitlab-duo-akICyfeD.mjs +155 -0
  69. package/lib/gitlab-duo-gubO4FNw.mjs +155 -0
  70. package/lib/gitlab-duo-workflow-BGAF6JDw.mjs +91 -0
  71. package/lib/gitlab-duo-workflow-CSEgYk3N.mjs +91 -0
  72. package/lib/gitlab-duo-workflow-D78tdn8E.mjs +91 -0
  73. package/lib/google-BS4T25Pn.mjs +30 -0
  74. package/lib/google-TFe-7-of.mjs +30 -0
  75. package/lib/google-antigravity-DGmwC2KV.mjs +198 -0
  76. package/lib/google-antigravity-DNYhgTpA.mjs +198 -0
  77. package/lib/google-antigravity-Dlkdhahq.mjs +198 -0
  78. package/lib/google-auth-B6gjsZvg.mjs +232 -0
  79. package/lib/google-auth-De7_s3eo.mjs +232 -0
  80. package/lib/google-auth-DhqpOYrX.mjs +232 -0
  81. package/lib/google-gemini-cli-BG_LRdIV.mjs +850 -0
  82. package/lib/google-gemini-cli-CWu8HvXW.mjs +211 -0
  83. package/lib/google-gemini-cli-Cb97bzQc.mjs +850 -0
  84. package/lib/google-gemini-cli-Cqds_Q5T.mjs +850 -0
  85. package/lib/google-gemini-cli-Dsztq0N5.mjs +211 -0
  86. package/lib/google-gemini-cli-zJhiOcND.mjs +211 -0
  87. package/lib/google-oauth-shared-CuEHaDDj.mjs +147 -0
  88. package/lib/google-oauth-shared-CwNWYdmR.mjs +147 -0
  89. package/lib/google-oauth-shared-yqlyqPPC.mjs +147 -0
  90. package/lib/google-shared-CgKmmdO3.mjs +760 -0
  91. package/lib/google-shared-Dr-VG-ap.mjs +760 -0
  92. package/lib/google-shared-DzhCb9b0.mjs +760 -0
  93. package/lib/google-vertex-BDN2tLaD.mjs +99 -0
  94. package/lib/google-vertex-DopVOQOG.mjs +99 -0
  95. package/lib/google-vertex-kscbkyRb.mjs +99 -0
  96. package/lib/google-zyDOG9x7.mjs +30 -0
  97. package/lib/identity-Da1GPU9a.mjs +219 -0
  98. package/lib/identity-DnB63pS7.mjs +219 -0
  99. package/lib/identity-xRP7_eSR.mjs +219 -0
  100. package/lib/idle-iterator-Baoyn3Hp.mjs +366 -0
  101. package/lib/idle-iterator-C6XbWcuB.mjs +366 -0
  102. package/lib/idle-iterator-DUjKQEGT.mjs +366 -0
  103. package/lib/index.mjs +4608 -3935
  104. package/lib/kimi--wUOWER5.mjs +236 -0
  105. package/lib/kimi-DMVI-zrZ.mjs +236 -0
  106. package/lib/kimi-Dh0pv3WZ.mjs +236 -0
  107. package/lib/markit-BL3cH47U.mjs +1417 -0
  108. package/lib/markit-BNiNY6SE.mjs +1417 -0
  109. package/lib/markit-M0jhziqU.mjs +1417 -0
  110. package/lib/minimax-code-BK-3eBbz.mjs +53 -0
  111. package/lib/minimax-code-DCbuA6Yk.mjs +53 -0
  112. package/lib/minimax-code-Uq8Qu9LW.mjs +53 -0
  113. package/lib/models-CfT4coYT.mjs +104660 -0
  114. package/lib/models-D_XnT6ay.mjs +104660 -0
  115. package/lib/models-SGASygJ3.mjs +104660 -0
  116. package/lib/ollama-BQMGPxtV.mjs +589 -0
  117. package/lib/ollama-DPzBmq89.mjs +589 -0
  118. package/lib/ollama-DoBz5SFm.mjs +589 -0
  119. package/lib/openai-codex-Bi-4S4PX.mjs +255 -0
  120. package/lib/openai-codex-CQL_kvYL.mjs +255 -0
  121. package/lib/openai-codex-Ch8CkT3p.mjs +255 -0
  122. package/lib/openai-codex-responses-CvxL_FMF.mjs +3144 -0
  123. package/lib/openai-codex-responses-Cz-2qpC7.mjs +3144 -0
  124. package/lib/openai-codex-responses-pejZgdZo.mjs +3144 -0
  125. package/lib/openai-completions-CF8IyNtP.mjs +1360 -0
  126. package/lib/openai-completions-DIf361kF.mjs +1360 -0
  127. package/lib/openai-completions-i8doczAk.mjs +1360 -0
  128. package/lib/openai-reasoning-fallback-DGVVLgd1.mjs +286 -0
  129. package/lib/openai-reasoning-fallback-Nj6yB2rY.mjs +286 -0
  130. package/lib/openai-reasoning-fallback-OdJREP1o.mjs +286 -0
  131. package/lib/openai-responses-BqXJInHX.mjs +867 -0
  132. package/lib/openai-responses-DTkBiiy-.mjs +867 -0
  133. package/lib/openai-responses-Db_a-U4-.mjs +867 -0
  134. package/lib/opencode-DaJjcUbf.mjs +38 -0
  135. package/lib/opencode-DhMolSoB.mjs +38 -0
  136. package/lib/opencode-Rq_Q2vAO.mjs +38 -0
  137. package/lib/perplexity-9b-RQ0mV.mjs +194 -0
  138. package/lib/perplexity-C4iLVMUj.mjs +194 -0
  139. package/lib/perplexity-Uu7Jzg5b.mjs +194 -0
  140. package/lib/proxy-DDqrmjjC.mjs +190 -0
  141. package/lib/proxy-ZtUcl6r5.mjs +190 -0
  142. package/lib/proxy-pM_NIv-H.mjs +190 -0
  143. package/lib/request-debug-HJMoJncL.mjs +183 -0
  144. package/lib/sanitize-text-CfcXzw5X.mjs +33 -0
  145. package/lib/scrapers-BkFfcPHj.mjs +9198 -0
  146. package/lib/scrapers-CL-WgdU1.mjs +9198 -0
  147. package/lib/scrapers-CVptboMs.mjs +9198 -0
  148. package/lib/scrapers-CipyKyCH.mjs +9198 -0
  149. package/lib/scrapers-DGfrYhtw.mjs +9198 -0
  150. package/lib/scrapers-DSUPYoUF.mjs +9198 -0
  151. package/lib/scrapers-DZ2GXmp7.mjs +9198 -0
  152. package/lib/scrapers-nhQAvc8d.mjs +9198 -0
  153. package/lib/shell-snapshot-fn-env.sh +67 -43
  154. package/lib/src-BgpusnsB.mjs +5076 -0
  155. package/lib/src-DTid0g4K.mjs +5001 -0
  156. package/lib/src-lDmkSRbm.mjs +5006 -0
  157. package/lib/stream-BlldLkVq.mjs +10236 -0
  158. package/lib/stream-CRtexYLe.mjs +10236 -0
  159. package/lib/stream-C_RAoxJC.mjs +10236 -0
  160. package/lib/stream-markup-healing-2toHXWXM.mjs +491 -0
  161. package/lib/stream-markup-healing-DMsxBx1L.mjs +491 -0
  162. package/lib/stream-markup-healing-KMkiAA00.mjs +491 -0
  163. package/lib/turndown-DN7ByzEI.mjs +667 -0
  164. package/lib/types/client/locales.d.ts +1 -1
  165. package/lib/types/config/fields.d.ts +1 -1
  166. package/lib/types/config/settings.d.ts +25 -3
  167. package/lib/types/config/truncate.d.ts +9 -5
  168. package/lib/types/tools/bash/background.d.ts +1 -1
  169. package/lib/types/tools/bash/bash-executor.d.ts +4 -0
  170. package/lib/types/tools/bash/bash-interceptor.d.ts +10 -8
  171. package/lib/types/tools/bash/cd-workdir.d.ts +35 -0
  172. package/lib/types/tools/bash/non-interactive-env.d.ts +2 -0
  173. package/lib/types/tools/bash/render.d.ts +4 -3
  174. package/lib/types/tools/bash/sanitize-text.d.ts +5 -0
  175. package/lib/types/tools/bash/shell-snapshot.d.ts +4 -1
  176. package/lib/types/tools/bash/shell-tokenize.d.ts +60 -0
  177. package/lib/types/tools/bash/streaming-output.d.ts +12 -212
  178. package/lib/types/tools/bash/which.d.ts +20 -0
  179. package/lib/types/tools/bash/windows-shell.d.ts +31 -0
  180. package/lib/types/tools/edit/adapter/index.d.ts +25 -3
  181. package/lib/types/tools/edit/adapter/tools/auto-generated-guard.d.ts +7 -17
  182. package/lib/types/tools/glob/adapter/index.d.ts +27 -1
  183. package/lib/types/tools/grep/adapter/index.d.ts +29 -1
  184. package/lib/types/tools/omp/edit/dispatch.d.ts +43 -0
  185. package/lib/types/tools/omp/edit/hashline/noop-loop-guard.d.ts +3 -1
  186. package/lib/types/tools/omp/sdk.d.ts +6 -0
  187. package/lib/types/tools/omp/tools/auto-generated-guard.d.ts +20 -0
  188. package/lib/types/tools/omp/tools/fetch.d.ts +2 -0
  189. package/lib/types/tools/omp/tools/read.d.ts +2 -8
  190. package/lib/types/tools/omp/tools/write.d.ts +4 -4
  191. package/lib/types/tools/omp/utils/image-loading.d.ts +10 -36
  192. package/lib/types/tools/read/adapter/index.d.ts +4 -1
  193. package/lib/types/tools/read-image/index.d.ts +4 -0
  194. package/lib/types/tools/shared/auto-generated-guard.dsh.d.ts +16 -4
  195. package/lib/types/tools/shared/bun-shim.d.ts +39 -3
  196. package/lib/types/tools/shared/fs-promises-shim.d.ts +3 -1
  197. package/lib/types/tools/shared/image-bridge.d.ts +98 -0
  198. package/lib/types/tools/shared/omp-prompt.d.ts +17 -9
  199. package/lib/types/tools/shared/session-state.d.ts +9 -3
  200. package/lib/utils-2Wq2eATG.mjs +8119 -0
  201. package/lib/utils-BnX8O3vi.mjs +12303 -0
  202. package/lib/utils-Bw139LTz.mjs +8119 -0
  203. package/lib/utils-CE8N8P4Q.mjs +12315 -0
  204. package/lib/utils-CZw4tVZQ.mjs +12315 -0
  205. package/lib/utils-C_MrQ_0z.mjs +12301 -0
  206. package/lib/utils-CiGN7OHR.mjs +12315 -0
  207. package/lib/utils-Cp76gCyk.mjs +8119 -0
  208. package/lib/utils-D0GRDgAr.mjs +12315 -0
  209. package/lib/utils-D0Z1fyoO.mjs +12299 -0
  210. package/lib/utils-D2YknRk4.mjs +51 -0
  211. package/lib/utils-DppGHXQB.mjs +51 -0
  212. package/lib/utils-DzXZDfYc.mjs +51 -0
  213. package/lib/utils-W8WdSBpX.mjs +12315 -0
  214. package/lib/vision-guard-01qSwFpf.mjs +92 -0
  215. package/lib/vision-guard-BDD4OrKo.mjs +92 -0
  216. package/lib/vision-guard-DzZGaOga.mjs +92 -0
  217. package/lib/wafer-CgtPHerf.mjs +17 -0
  218. package/lib/wafer-DRU0EjZX.mjs +17 -0
  219. package/lib/wafer-cBtrn6q9.mjs +17 -0
  220. package/lib/xai-oauth-Buk8XVvM.mjs +446 -0
  221. package/lib/xai-oauth-CMal8l1O.mjs +446 -0
  222. package/lib/xai-oauth-DPHDXKD7.mjs +446 -0
  223. package/lib/xiaomi-CZgwvX2G.mjs +157 -0
  224. package/lib/xiaomi-D2qT5cAC.mjs +157 -0
  225. package/lib/xiaomi-Dd87KSg5.mjs +157 -0
  226. package/lib/zai-7DWMkEqM.mjs +203 -0
  227. package/lib/zai-C59YnomG.mjs +203 -0
  228. package/lib/zai-CFeOt-53.mjs +203 -0
  229. package/lib/zip-Cg9ANN_U.mjs +1316 -0
  230. package/lib/zip-D36ZKsP8.mjs +1316 -0
  231. package/lib/zip-D6QXpvaJ.mjs +1316 -0
  232. package/package.json +14 -10
  233. package/presets/package.json +1 -1
  234. package/presets/tool-plus-ptc/preset.yml +1 -1
  235. package/presets/tool-plus-standard/preset.yml +1 -1
@@ -0,0 +1,398 @@
1
+ import { installBunShim as __installBunShim } from "./bun-shim.mjs";
2
+ __installBunShim();
3
+ import { c as OAuthError, h as LoginCancelledError, t as ConfigurationError } from "./error-yCSG0mGC.mjs";
4
+ //#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/registry/oauth/oauth.html
5
+ var oauth_default = "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>oh my pi · authentication</title>\n <style>\n :root {\n /* dark, slightly purple-tinted neutrals — never #000 */\n --ink-0: oklch(0.135 0.018 295);\n --ink-1: oklch(0.172 0.022 295);\n --ink-2: oklch(0.21 0.024 295);\n --ink-4: oklch(0.36 0.024 295);\n --ink-5: oklch(0.5 0.022 290);\n --ink-6: oklch(0.66 0.018 290);\n --ink-7: oklch(0.82 0.014 285);\n --ink-9: oklch(0.97 0.008 90);\n\n /* brand: magenta lead, cyan reply */\n --magenta: oklch(0.7 0.24 340);\n --iris: oklch(0.62 0.21 295);\n --cyan: oklch(0.81 0.14 200);\n\n /* signal */\n --success: oklch(0.78 0.16 150);\n --error: oklch(0.66 0.22 25);\n\n --bg: var(--ink-0);\n --fg: var(--ink-9);\n --fg-dim: var(--ink-7);\n --fg-faint: var(--ink-5);\n --rule: oklch(1 0 0 / 0.07);\n --rule-strong: oklch(1 0 0 / 0.14);\n --accent: var(--magenta);\n --accent-2: var(--cyan);\n\n color-scheme: dark;\n }\n\n * {\n box-sizing: border-box;\n }\n\n body {\n font-family: \"Geist\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n background:\n radial-gradient(ellipse 80vw 60vh at 18% -10%, oklch(0.7 0.24 340 / 0.13), transparent 60%),\n radial-gradient(ellipse 70vw 70vh at 110% 30%, oklch(0.81 0.14 200 / 0.09), transparent 60%),\n var(--bg);\n color: var(--fg);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1.5rem;\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n overflow-x: clip;\n }\n\n /* ascii grid backdrop */\n .grid-bg {\n position: fixed;\n inset: 0;\n background-image:\n linear-gradient(to right, oklch(1 0 0 / 0.025) 1px, transparent 1px),\n linear-gradient(to bottom, oklch(1 0 0 / 0.025) 1px, transparent 1px);\n background-size: 56px 56px;\n background-position: -1px -1px;\n mask-image: radial-gradient(ellipse at 50% 30%, #000 30%, transparent 75%);\n pointer-events: none;\n z-index: 0;\n }\n\n .container {\n position: relative;\n z-index: 1;\n background: oklch(0.172 0.022 295 / 0.72);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n border: 1px solid var(--rule);\n border-radius: 16px;\n padding: 2.5rem 2.5rem 2rem;\n width: 100%;\n max-width: 420px;\n text-align: center;\n box-shadow:\n 0 24px 48px -12px oklch(0 0 0 / 0.55),\n 0 0 0 1px oklch(1 0 0 / 0.03) inset;\n opacity: 0;\n transform: translateY(10px);\n animation: rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n }\n @keyframes rise {\n to {\n opacity: 1;\n transform: none;\n }\n }\n\n /* brand header */\n .brand {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n margin-bottom: 2rem;\n }\n .brand svg {\n width: 22px;\n height: 22px;\n }\n .brand .wordmark {\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.01em;\n color: var(--fg);\n }\n\n /* status mark */\n .mark-wrap {\n position: relative;\n width: 72px;\n height: 72px;\n margin: 0 auto 1.5rem;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .mark-wrap::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: 50%;\n background: oklch(1 0 0 / 0.05);\n z-index: 0;\n }\n .mark {\n width: 40px;\n height: 40px;\n z-index: 2;\n }\n .timer-svg {\n position: absolute;\n top: -2px;\n left: -2px;\n width: 76px;\n height: 76px;\n transform: rotate(-90deg);\n z-index: 1;\n pointer-events: none;\n }\n .timer-circle {\n fill: none;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-dasharray: 226;\n stroke-dashoffset: 0;\n }\n .countdown .timer-circle {\n animation: countdown 3s linear forwards;\n }\n @keyframes countdown {\n to {\n stroke-dashoffset: 226;\n }\n }\n\n h1 {\n font-family: \"Fraunces\", \"Iowan Old Style\", Georgia, serif;\n font-size: 1.6rem;\n font-weight: 500;\n letter-spacing: -0.022em;\n line-height: 1.15;\n margin: 0 0 0.75rem;\n text-wrap: balance;\n }\n p {\n color: var(--fg-dim);\n line-height: 1.6;\n margin: 0 0 1.75rem;\n font-size: 0.95rem;\n }\n\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n background: var(--fg);\n color: var(--bg);\n font-family: inherit;\n font-weight: 500;\n padding: 0.65rem 1.4rem;\n border-radius: 8px;\n text-decoration: none;\n font-size: 0.9rem;\n transition: opacity 0.2s, transform 0.2s;\n cursor: pointer;\n border: none;\n }\n .btn:hover {\n opacity: 0.88;\n transform: translateY(-1px);\n }\n .btn:active {\n transform: translateY(0);\n }\n\n /* State: success */\n .success .mark-wrap::before {\n background: oklch(0.78 0.16 150 / 0.12);\n }\n .success .mark {\n color: var(--success);\n }\n .success .timer-circle {\n stroke: var(--success);\n }\n .success .icon-error {\n display: none;\n }\n\n /* State: error */\n .error .mark-wrap::before {\n background: oklch(0.66 0.22 25 / 0.12);\n }\n .error .mark {\n color: var(--error);\n }\n .error .timer-circle {\n stroke: var(--error);\n }\n .error .icon-success,\n .error .timer-svg {\n display: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .container,\n .countdown .timer-circle {\n animation: none;\n }\n .container {\n opacity: 1;\n transform: none;\n }\n }\n </style>\n </head>\n <body>\n <span aria-hidden=\"true\" class=\"grid-bg\"></span>\n <div id=\"app\" class=\"container\">\n <div class=\"brand\">\n <svg viewBox=\"0 0 64 64\" aria-hidden=\"true\">\n <defs>\n <linearGradient id=\"pi-grad\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">\n <stop offset=\"0\" stop-color=\"oklch(0.7 0.24 340)\" />\n <stop offset=\".5\" stop-color=\"oklch(0.62 0.21 295)\" />\n <stop offset=\"1\" stop-color=\"oklch(0.81 0.14 200)\" />\n </linearGradient>\n </defs>\n <path fill=\"url(#pi-grad)\" d=\"M10 14h44v9H43v33h-9V23h-9v22h-9V23H10z\" />\n </svg>\n <span class=\"wordmark\">oh my pi</span>\n </div>\n\n <div class=\"mark-wrap\">\n <svg class=\"timer-svg\" viewBox=\"0 0 76 76\">\n <circle class=\"timer-circle\" cx=\"38\" cy=\"38\" r=\"36\"></circle>\n </svg>\n <svg class=\"mark icon-success\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <svg class=\"mark icon-error\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </div>\n\n <h1 id=\"title\">Authentication</h1>\n <p id=\"message\"></p>\n <button onclick=\"window.close()\" class=\"btn\">Close Window</button>\n </div>\n\n <script id=\"server-state\" type=\"application/json\">\n __OAUTH_STATE__\n <\/script>\n\n <script>\n let serverState;\n try {\n serverState = JSON.parse(document.getElementById(\"server-state\").textContent);\n } catch {\n const params = new URLSearchParams(window.location.search);\n serverState = {\n ok: params.get(\"ok\") === \"1\",\n error: params.get(\"error\") || \"Authentication failed\",\n };\n }\n\n const app = document.getElementById(\"app\");\n const title = document.getElementById(\"title\");\n const message = document.getElementById(\"message\");\n\n if (serverState.ok) {\n app.classList.add(\"success\", \"countdown\");\n title.textContent = \"Authentication Successful\";\n message.innerHTML = \"You have successfully logged in.<br>You can now close this tab.\";\n setTimeout(() => window.close(), 3000);\n } else {\n app.classList.add(\"error\");\n title.textContent = \"Authentication Failed\";\n message.textContent = serverState.error || \"An error occurred\";\n }\n <\/script>\n </body>\n</html>\n";
6
+ //#endregion
7
+ //#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/registry/oauth/callback-server.ts
8
+ /**
9
+ * Abstract base class for OAuth flows with local callback servers.
10
+ *
11
+ * Handles:
12
+ * - Port allocation (tries expected port, falls back to random)
13
+ * - Callback server setup and request handling
14
+ * - Common OAuth flow logic
15
+ *
16
+ * Providers extend this and implement:
17
+ * - generateAuthUrl(): Build provider-specific authorization URL
18
+ * - exchangeToken(): Exchange authorization code for tokens
19
+ */
20
+ const DEFAULT_TIMEOUT = 3e5;
21
+ const DEFAULT_HOSTNAME = "localhost";
22
+ const CALLBACK_PATH = "/callback";
23
+ const IPV4_LOOPBACK = "127.0.0.1";
24
+ const IPV6_LOOPBACK = "::1";
25
+ /**
26
+ * How many times a random-port bind may be redrawn when the ephemeral port it
27
+ * landed on is already held on {@link IPV6_LOOPBACK} by that exact address.
28
+ * Small on purpose: each redraw picks a fresh port, so a repeat collision is
29
+ * vanishingly unlikely.
30
+ */
31
+ const IPV6_COMPANION_ATTEMPTS = 4;
32
+ /**
33
+ * Path served by {@link OAuthCallbackFlow} that 302-redirects to the pending
34
+ * authorization URL. Kept out of {@link OAuthCallbackFlowOptions} because it
35
+ * lives on the loopback callback server alongside {@link CALLBACK_PATH} and
36
+ * must never clash with a provider-registered redirect URI (all known
37
+ * providers register `/callback`-shaped paths).
38
+ */
39
+ const LAUNCH_PATH = "/launch";
40
+ /**
41
+ * Whether a failed bind means "another process already holds this port".
42
+ * Bun surfaces `EADDRINUSE` on the error's `code` where the platform reports
43
+ * it, and otherwise only in the message, so both are checked.
44
+ */
45
+ function isAddressInUse(error) {
46
+ const code = error?.code;
47
+ if (typeof code === "string") return code === "EADDRINUSE";
48
+ return error instanceof Error && /EADDRINUSE|in use/i.test(error.message);
49
+ }
50
+ /**
51
+ * Abstract base class for OAuth flows with local callback servers.
52
+ */
53
+ var OAuthCallbackFlow = class {
54
+ ctrl;
55
+ preferredPort;
56
+ callbackPath;
57
+ callbackHostname;
58
+ redirectUri;
59
+ allowPortFallback;
60
+ #manualInputOnly;
61
+ #callbackResolve;
62
+ #callbackReject;
63
+ /**
64
+ * Authorization URL the `/launch` route currently redirects to. Set by
65
+ * {@link login} after {@link generateAuthUrl} and before {@link OAuthController.onAuth}
66
+ * fires, cleared when the server stops. `undefined` before the flow reaches
67
+ * that point and after it finishes, so `/launch` returns 503 rather than
68
+ * a stale URL.
69
+ */
70
+ #pendingAuthUrl;
71
+ constructor(ctrl, preferredPortOrOptions, callbackPath = CALLBACK_PATH) {
72
+ this.ctrl = ctrl;
73
+ if (typeof preferredPortOrOptions === "number") {
74
+ this.preferredPort = preferredPortOrOptions;
75
+ this.callbackPath = callbackPath;
76
+ this.callbackHostname = DEFAULT_HOSTNAME;
77
+ this.allowPortFallback = true;
78
+ this.#manualInputOnly = false;
79
+ return;
80
+ }
81
+ this.preferredPort = preferredPortOrOptions.preferredPort;
82
+ this.callbackPath = preferredPortOrOptions.callbackPath ?? CALLBACK_PATH;
83
+ this.callbackHostname = preferredPortOrOptions.callbackHostname ?? DEFAULT_HOSTNAME;
84
+ this.redirectUri = preferredPortOrOptions.redirectUri;
85
+ this.allowPortFallback = preferredPortOrOptions.allowPortFallback ?? true;
86
+ this.#manualInputOnly = preferredPortOrOptions.manualInputOnly ?? false;
87
+ }
88
+ /**
89
+ * Generate CSRF state token. Override if provider needs custom state generation.
90
+ */
91
+ generateState() {
92
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
93
+ crypto.getRandomValues(bytes);
94
+ return Array.from(bytes).map((value) => value.toString(16).padStart(2, "0")).join("");
95
+ }
96
+ #loginCancelledError() {
97
+ return new LoginCancelledError(`OAuth callback cancelled: ${this.ctrl.signal?.reason}`);
98
+ }
99
+ #throwIfCancelled() {
100
+ if (this.ctrl.signal?.aborted) throw this.#loginCancelledError();
101
+ }
102
+ /**
103
+ * Execute the OAuth login flow.
104
+ */
105
+ async login() {
106
+ const state = this.generateState();
107
+ this.#throwIfCancelled();
108
+ const { server, redirectUri, launchUrl } = this.#manualInputOnly ? {
109
+ server: void 0,
110
+ redirectUri: this.#buildRedirectUri(),
111
+ launchUrl: void 0
112
+ } : await this.#startCallbackServer(state);
113
+ try {
114
+ this.#throwIfCancelled();
115
+ const { url: authUrl, instructions } = await this.generateAuthUrl(state, redirectUri);
116
+ this.#throwIfCancelled();
117
+ this.#pendingAuthUrl = authUrl;
118
+ this.ctrl.onAuth?.({
119
+ url: authUrl,
120
+ launchUrl,
121
+ instructions
122
+ });
123
+ this.ctrl.onProgress?.(this.#manualInputOnly ? "Waiting for pasted authorization code..." : "Waiting for browser authentication...");
124
+ const { code } = await this.#waitForCallback(state);
125
+ this.#throwIfCancelled();
126
+ this.ctrl.onProgress?.("Exchanging authorization code for tokens...");
127
+ return await this.exchangeToken(code, state, redirectUri);
128
+ } finally {
129
+ this.#pendingAuthUrl = void 0;
130
+ server?.stop();
131
+ }
132
+ }
133
+ #buildRedirectUri() {
134
+ return this.redirectUri ?? `http://${this.callbackHostname}:${this.preferredPort}${this.callbackPath}`;
135
+ }
136
+ /**
137
+ * Start callback server, trying preferred port first, falling back to random.
138
+ * `launchUrl` is `undefined` when the caller configured `callbackPath` to
139
+ * collide with {@link LAUNCH_PATH} — the callback handler resolves the real
140
+ * callback in that case, so advertising a self-redirecting URL would be
141
+ * incorrect.
142
+ */
143
+ async #startCallbackServer(expectedState) {
144
+ try {
145
+ const server = this.#createServer(this.preferredPort, expectedState);
146
+ const actualPort = this.#resolveServerPort(server);
147
+ const launchUrl = this.#launchUrlIfSafe(actualPort);
148
+ if (this.redirectUri) return {
149
+ server,
150
+ redirectUri: this.redirectUri,
151
+ launchUrl
152
+ };
153
+ return {
154
+ server,
155
+ redirectUri: `http://${this.callbackHostname}:${actualPort}${this.callbackPath}`,
156
+ launchUrl
157
+ };
158
+ } catch (cause) {
159
+ if (this.redirectUri) throw new ConfigurationError(`OAuth callback port ${this.preferredPort} is in use, but oauth.redirectUri (${this.redirectUri}) requires this exact port. Free port ${this.preferredPort} (e.g. stop the process bound to it) and retry, or change oauth.redirectUri to point at an available port.`, { cause });
160
+ if (!this.allowPortFallback) throw new ConfigurationError(`OAuth callback port ${this.preferredPort} is in use. The OAuth provider validates redirect URIs against its registered callback, so falling back to a random port would be rejected. Free port ${this.preferredPort} (e.g. stop the process bound to it) and retry, or set oauth.callbackPort/oauth.redirectUri to a port the provider has registered.`, { cause });
161
+ const server = this.#createServer(0, expectedState);
162
+ const actualPort = this.#resolveServerPort(server);
163
+ const redirectUri = `http://${this.callbackHostname}:${actualPort}${this.callbackPath}`;
164
+ const launchUrl = this.#launchUrlIfSafe(actualPort);
165
+ this.ctrl.onProgress?.(`Preferred port ${this.preferredPort} unavailable, using port ${actualPort}`);
166
+ return {
167
+ server,
168
+ redirectUri,
169
+ launchUrl
170
+ };
171
+ }
172
+ }
173
+ /**
174
+ * Read the numeric port a callback server bound to. `Bun.Server.port` is
175
+ * declared `number | undefined` because Unix-socket servers have no port,
176
+ * but every callback flow uses TCP; a missing port here indicates a
177
+ * configuration error rather than a fallback case.
178
+ */
179
+ #resolveServerPort(server) {
180
+ const port = server.port;
181
+ if (typeof port !== "number") throw new ConfigurationError("OAuth callback server bound to a non-TCP endpoint; expected a numeric port. Check `oauth.callbackPort`/`oauth.redirectUri`.");
182
+ return port;
183
+ }
184
+ /**
185
+ * Build the `/launch` URL served by the callback server bound to `port`, or
186
+ * `undefined` when it must not be advertised:
187
+ * - the configured `callbackPath` (or a `redirectUri` whose pathname
188
+ * resolves to {@link LAUNCH_PATH}) would collide with the launch route;
189
+ * - the flow's `redirectUri` never returns to this loopback server: fixed
190
+ * non-loopback hosts, or custom schemes like GitLab Duo's `vscode://`
191
+ * URI — which `new URL` parses without complaint, so a scheme/host check
192
+ * is required, not just the parse failure path. Advertising a localhost
193
+ * `/launch` target for such flows misrepresents the callback endpoint
194
+ * and hands remote users a URL that resolves nowhere.
195
+ * Kept short (~30 chars) so UIs can advertise it as a
196
+ * viewport-truncation-safe copy target for the full authorization URL.
197
+ */
198
+ #launchUrlIfSafe(port) {
199
+ if (this.callbackPath === LAUNCH_PATH) return void 0;
200
+ if (this.redirectUri) try {
201
+ const parsed = new URL(this.redirectUri);
202
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
203
+ if (parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1" && parsed.hostname !== "[::1]") return;
204
+ if (parsed.pathname === LAUNCH_PATH) return void 0;
205
+ } catch {
206
+ return;
207
+ }
208
+ return `http://${this.callbackHostname}:${port}${LAUNCH_PATH}`;
209
+ }
210
+ /**
211
+ * Create the HTTP listener(s) for the OAuth callback.
212
+ *
213
+ * `localhost` is not a single endpoint: it resolves to both
214
+ * {@link IPV4_LOOPBACK} and {@link IPV6_LOOPBACK}, and clients commonly try
215
+ * `::1` first. Binding only the IPv4 literal hands the authorization code to
216
+ * whatever holds the IPv6 loopback on the same port — a dev server on
217
+ * `*:3000` is the common case — which answers from its own routes while this
218
+ * flow waits out the full {@link DEFAULT_TIMEOUT}. Nothing detects it either:
219
+ * a specific-address bind coexists with another process's wildcard bind, so
220
+ * `Bun.serve` reports the port as free and the random-port fallback in
221
+ * {@link #startCallbackServer} never runs.
222
+ *
223
+ * Binding both loopback literals fixes the delivery rather than dodging it:
224
+ * the kernel routes a connection to the most specific matching bind, so our
225
+ * `::1` listener receives `localhost` traffic that would otherwise reach a
226
+ * process bound to the `::` wildcard. Both listeners answer the same routes,
227
+ * so which family the client resolves stops mattering.
228
+ *
229
+ * A genuine collision — another process on exactly this loopback address and
230
+ * port — still raises EADDRINUSE and reaches the caller's in-use policy. A
231
+ * host that cannot bind `::1` at all (IPv6 disabled, address unavailable) is
232
+ * not a collision: the IPv4 listener is the only reachable endpoint there, so
233
+ * it serves alone.
234
+ */
235
+ #createServer(port, expectedState) {
236
+ if (this.callbackHostname !== DEFAULT_HOSTNAME) return this.#serve(this.callbackHostname, port, expectedState);
237
+ for (let attempt = 0;; attempt++) {
238
+ const primary = this.#serve(IPV4_LOOPBACK, port, expectedState);
239
+ const boundPort = primary.port;
240
+ if (typeof boundPort !== "number") return primary;
241
+ let companion;
242
+ try {
243
+ companion = this.#serve(IPV6_LOOPBACK, boundPort, expectedState);
244
+ } catch (cause) {
245
+ if (!isAddressInUse(cause)) return primary;
246
+ primary.stop(true);
247
+ if (port !== 0 || attempt >= IPV6_COMPANION_ATTEMPTS) throw cause;
248
+ continue;
249
+ }
250
+ return {
251
+ get port() {
252
+ return primary.port;
253
+ },
254
+ stop: (closeActiveConnections) => {
255
+ companion.stop(closeActiveConnections);
256
+ return primary.stop(closeActiveConnections);
257
+ }
258
+ };
259
+ }
260
+ }
261
+ /** Bind one loopback listener serving the callback and launch routes. */
262
+ #serve(hostname, port, expectedState) {
263
+ return Bun.serve({
264
+ hostname,
265
+ port,
266
+ reusePort: false,
267
+ fetch: (req) => this.#handleCallback(req, expectedState)
268
+ });
269
+ }
270
+ /**
271
+ * Handle OAuth callback HTTP request. Two routes on the same loopback server:
272
+ * - `callbackPath` (default `/callback`) — the provider redirect target.
273
+ * - {@link LAUNCH_PATH} (`/launch`) — 302 to the pending authorization URL so
274
+ * viewport-safe copy targets can survive TUI truncation.
275
+ *
276
+ * `callbackPath` wins any collision: an OMP config that pins the provider
277
+ * redirect at `/launch` (via `oauth.callbackPath` or a loopback
278
+ * `oauth.redirectUri`) must resolve the callback normally rather than
279
+ * self-redirect. `#startCallbackServer` also suppresses `launchUrl` in that
280
+ * case, so the launch route is never advertised when it would collide.
281
+ */
282
+ #handleCallback(req, expectedState) {
283
+ const url = new URL(req.url);
284
+ if (url.pathname !== this.callbackPath) {
285
+ if (url.pathname === LAUNCH_PATH) {
286
+ const pending = this.#pendingAuthUrl;
287
+ if (!pending) return new Response("OAuth launch URL is no longer active", { status: 503 });
288
+ return Response.redirect(pending, 302);
289
+ }
290
+ return new Response("Not Found", { status: 404 });
291
+ }
292
+ const code = url.searchParams.get("code");
293
+ const state = url.searchParams.get("state") || "";
294
+ const error = url.searchParams.get("error") || "";
295
+ const errorDescription = url.searchParams.get("error_description") || error;
296
+ let resultState;
297
+ if (error) resultState = {
298
+ ok: false,
299
+ error: `Authorization failed: ${errorDescription}`
300
+ };
301
+ else if (!code) resultState = {
302
+ ok: false,
303
+ error: "Missing authorization code"
304
+ };
305
+ else if (expectedState && state !== expectedState) resultState = {
306
+ ok: false,
307
+ error: "State mismatch - possible CSRF attack"
308
+ };
309
+ else resultState = {
310
+ ok: true,
311
+ code,
312
+ state
313
+ };
314
+ if (resultState.ok) {
315
+ const resolve = this.#callbackResolve;
316
+ queueMicrotask(() => {
317
+ resolve?.({
318
+ code: resultState.code,
319
+ state: resultState.state
320
+ });
321
+ });
322
+ } else if (error && (!expectedState || state === expectedState)) {
323
+ const reject = this.#callbackReject;
324
+ const message = resultState.error ?? `Authorization failed: ${errorDescription}`;
325
+ queueMicrotask(() => {
326
+ reject?.(new OAuthError(message, { kind: "device-auth" }));
327
+ });
328
+ }
329
+ return new Response(oauth_default.replaceAll("__OAUTH_STATE__", JSON.stringify(resultState)), {
330
+ status: resultState.ok ? 200 : 500,
331
+ headers: { "Content-Type": "text/html" }
332
+ });
333
+ }
334
+ /**
335
+ * Wait for OAuth callback or manual input (whichever comes first).
336
+ */
337
+ #waitForCallback(expectedState) {
338
+ const timeoutSignal = AbortSignal.timeout(DEFAULT_TIMEOUT);
339
+ const signal = this.ctrl.signal ? AbortSignal.any([this.ctrl.signal, timeoutSignal]) : timeoutSignal;
340
+ if (signal.aborted) return Promise.reject(this.#loginCancelledError());
341
+ const callback = Promise.withResolvers();
342
+ this.#callbackResolve = callback.resolve;
343
+ this.#callbackReject = callback.reject;
344
+ signal.addEventListener("abort", () => {
345
+ this.#callbackResolve = void 0;
346
+ this.#callbackReject = void 0;
347
+ callback.reject(new LoginCancelledError(`OAuth callback cancelled: ${signal.reason}`));
348
+ });
349
+ const callbackPromise = callback.promise;
350
+ if (this.ctrl.onManualCodeInput) {
351
+ const requestManualInput = this.ctrl.onManualCodeInput;
352
+ const manualPromise = (async () => {
353
+ while (true) {
354
+ const result = await Promise.race([callbackPromise, requestManualInput().then((input) => {
355
+ const parsed = parseCallbackInput(input);
356
+ if (!parsed.code) return null;
357
+ if (expectedState && parsed.state && parsed.state !== expectedState) return null;
358
+ return {
359
+ code: parsed.code,
360
+ state: parsed.state ?? ""
361
+ };
362
+ }).catch(() => null)]);
363
+ if (result) return result;
364
+ }
365
+ })();
366
+ return Promise.race([callbackPromise, manualPromise]);
367
+ }
368
+ return callbackPromise;
369
+ }
370
+ };
371
+ /**
372
+ * Parse a redirect URL or code string to extract code and state.
373
+ */
374
+ function parseCallbackInput(input) {
375
+ const value = input.trim();
376
+ if (!value) return {};
377
+ try {
378
+ const url = new URL(value);
379
+ return {
380
+ code: url.searchParams.get("code") ?? void 0,
381
+ state: url.searchParams.get("state") ?? void 0
382
+ };
383
+ } catch {}
384
+ if (value.includes("code=")) {
385
+ const params = new URLSearchParams(value.replace(/^[?#]/, ""));
386
+ return {
387
+ code: params.get("code") ?? void 0,
388
+ state: params.get("state") ?? void 0
389
+ };
390
+ }
391
+ const [code, state] = value.split("#", 2);
392
+ return {
393
+ code,
394
+ state
395
+ };
396
+ }
397
+ //#endregion
398
+ export { OAuthCallbackFlow as t };
package/lib/client.js CHANGED
@@ -50,7 +50,7 @@ window.__ModuleLoader__.load({
50
50
  {
51
51
  name: "interceptorEnabled",
52
52
  kind: "boolean",
53
- default: false,
53
+ default: true,
54
54
  labelKey: "interceptorEnabled",
55
55
  hintKey: "interceptorEnabledHint",
56
56
  group: "behavior",
@@ -345,6 +345,15 @@ window.__ModuleLoader__.load({
345
345
  group: "reading",
346
346
  tool: "read"
347
347
  },
348
+ {
349
+ name: "readConcurrentSafe",
350
+ kind: "boolean",
351
+ default: true,
352
+ labelKey: "readConcurrentSafe",
353
+ hintKey: "readConcurrentSafeHint",
354
+ group: "reading",
355
+ tool: "read"
356
+ },
348
357
  {
349
358
  name: "readSummarizeEnabled",
350
359
  kind: "boolean",
@@ -433,6 +442,15 @@ window.__ModuleLoader__.load({
433
442
  tool: "read",
434
443
  visibility: { requiresEnabled: "fetchEnabled" }
435
444
  },
445
+ {
446
+ name: "imagesBlockImages",
447
+ kind: "boolean",
448
+ default: false,
449
+ labelKey: "imagesBlockImages",
450
+ hintKey: "imagesBlockImagesHint",
451
+ group: "images",
452
+ tool: "read"
453
+ },
436
454
  {
437
455
  name: "imagesAutoResize",
438
456
  kind: "boolean",
@@ -443,27 +461,62 @@ window.__ModuleLoader__.load({
443
461
  tool: "read"
444
462
  },
445
463
  {
446
- name: "inspectImageMode",
447
- kind: "select",
448
- default: "auto",
449
- labelKey: "inspectImageMode",
450
- hintKey: "inspectImageModeHint",
464
+ name: "imagesResizeMaxSide",
465
+ kind: "number",
466
+ default: 1568,
467
+ labelKey: "imagesResizeMaxSide",
468
+ hintKey: "imagesResizeMaxSideHint",
451
469
  group: "images",
452
470
  tool: "read",
453
- options: [
454
- {
455
- value: "auto",
456
- labelKey: "optAuto"
457
- },
458
- {
459
- value: "on",
460
- labelKey: "optOn"
461
- },
462
- {
463
- value: "off",
464
- labelKey: "optOff"
465
- }
466
- ]
471
+ visibility: { requiresEnabled: "imagesAutoResize" }
472
+ },
473
+ {
474
+ name: "imagesResizeMaxBytes",
475
+ kind: "number",
476
+ default: 512e3,
477
+ labelKey: "imagesResizeMaxBytes",
478
+ hintKey: "imagesResizeMaxBytesHint",
479
+ group: "images",
480
+ tool: "read",
481
+ visibility: { requiresEnabled: "imagesAutoResize" }
482
+ },
483
+ {
484
+ name: "imagesResizeMinSide",
485
+ kind: "number",
486
+ default: 200,
487
+ labelKey: "imagesResizeMinSide",
488
+ hintKey: "imagesResizeMinSideHint",
489
+ group: "images",
490
+ tool: "read",
491
+ visibility: { requiresEnabled: "imagesAutoResize" }
492
+ },
493
+ {
494
+ name: "imagesResizeJpegQuality",
495
+ kind: "number",
496
+ default: 80,
497
+ labelKey: "imagesResizeJpegQuality",
498
+ hintKey: "imagesResizeJpegQualityHint",
499
+ group: "images",
500
+ tool: "read",
501
+ visibility: { requiresEnabled: "imagesAutoResize" }
502
+ },
503
+ {
504
+ name: "imagesExcludeWebp",
505
+ kind: "boolean",
506
+ default: false,
507
+ labelKey: "imagesExcludeWebp",
508
+ hintKey: "imagesExcludeWebpHint",
509
+ group: "images",
510
+ tool: "read"
511
+ },
512
+ {
513
+ name: "imagesInputMaxBytes",
514
+ kind: "number",
515
+ default: 20971520,
516
+ labelKey: "imagesInputMaxBytes",
517
+ hintKey: "imagesInputMaxBytesHint",
518
+ group: "images",
519
+ tool: "read"
467
520
  },
468
521
  {
469
522
  name: "editMode",
@@ -601,11 +654,6 @@ window.__ModuleLoader__.load({
601
654
  id: "astEdit",
602
655
  labelKey: "tabAstEdit",
603
656
  fields: TOOL_PLUS_FIELDS.filter((f) => f.tool === "astEdit").map((f) => f.name)
604
- },
605
- {
606
- id: "readImage",
607
- labelKey: "tabReadImage",
608
- fields: []
609
657
  }
610
658
  ];
611
659
  /** Look up one field definition by its schema key. */
@@ -2030,7 +2078,6 @@ window.__ModuleLoader__.load({
2030
2078
  groupBehavior: "行为",
2031
2079
  groupReading: "读取",
2032
2080
  groupSummary: "代码摘要",
2033
- groupImages: "图片",
2034
2081
  groupEditMode: "编辑模式",
2035
2082
  groupGuard: "文件守卫",
2036
2083
  noConfigTitle: "该工具无全局配置项",
@@ -2057,15 +2104,28 @@ window.__ModuleLoader__.load({
2057
2104
  fetchEnabledHint: "read 处理 http(s) URL(网页转 Markdown 供模型阅读)时是否启用抓取。",
2058
2105
  fetchMaxTimeoutSeconds: "抓取超时(秒)",
2059
2106
  fetchMaxTimeoutSecondsHint: "URL 抓取超时上限;0 表示使用默认的 30 秒。",
2060
- imagesAutoResize: "图片自动缩放",
2061
- imagesAutoResizeHint: "read 读取图片时自动缩放到可展示尺寸。",
2062
- inspectImageMode: "图片检查模式",
2063
- inspectImageModeHint: "读图工具的行为模式:auto/on/off。",
2064
2107
  groupFetch: "抓取",
2065
- optOn: "on(开启)",
2066
- optOff: "off(关闭)",
2108
+ groupImages: "图片",
2109
+ imagesAutoResize: "图片自动缩放",
2110
+ imagesAutoResizeHint: "read 读取图片时自动缩放到可展示尺寸(关则原图入库,由宿主归一化)。",
2111
+ imagesBlockImages: "禁用图片读取",
2112
+ imagesBlockImagesHint: "开启后 read 与 URL 抓取遇到图片一律只回文字提示,不展示图片(省 token,或规避敏感图外发)。",
2113
+ imagesResizeMaxSide: "缩放目标最长边",
2114
+ imagesResizeMaxSideHint: "自动缩放时把图片长边压到该像素以内(默认 1568,主流视觉模型的实际处理上限)。",
2115
+ imagesResizeMaxBytes: "缩放目标字节数",
2116
+ imagesResizeMaxBytesHint: "自动缩放的压缩预算(默认 512000 即 500KB);已小于预算四分之一的图跳过重编码。",
2117
+ imagesResizeMinSide: "缩放尺寸下限",
2118
+ imagesResizeMinSideHint: "过小的图会被放大到该边长(默认 200 像素)——多数视觉模型拒收退化小图。",
2119
+ imagesResizeJpegQuality: "首次编码质量",
2120
+ imagesResizeJpegQualityHint: "缩放后首次有损编码的质量参数,1-100(默认 80);超出字节预算时仍会自动降质降尺寸。",
2121
+ imagesExcludeWebp: "禁用 WebP 输出",
2122
+ imagesExcludeWebpHint: "部分本地推理后端(llama.cpp/Ollama 系)不能解码 WebP,命中名单的模型已自动规避;此开关对名单外的网关模型强制改出 PNG/JPEG。",
2123
+ imagesInputMaxBytes: "图片输入大小上限",
2124
+ imagesInputMaxBytesHint: "超过该大小的图片文件直接拒读(默认 20971520 即 20MB;实际还与宿主附件限额取小)。",
2067
2125
  readRenderMarkdown: "渲染 Markdown",
2068
2126
  readRenderMarkdownHint: "读取 .md 文件时按 Markdown 渲染输出。",
2127
+ readConcurrentSafe: "并发安全读取",
2128
+ readConcurrentSafeHint: "声明 read 可并发执行(宿主并行调度多个 read 调用,含图片读取);关闭后宿主串行执行。默认开启,改动即时生效。",
2069
2129
  editMode: "编辑模式",
2070
2130
  editModeHint: "edit 工具的输入解释模式:replace=字面量替换;patch=unified diff;hashline=行锚点补丁;apply_patch=Codex 信封。默认 replace。",
2071
2131
  editFuzzyMatch: "模糊匹配",
@@ -2084,7 +2144,6 @@ window.__ModuleLoader__.load({
2084
2144
  astEditEnabledHint: "启用 ast_edit 工具做结构化 AST 重写(默认开启)。",
2085
2145
  groupGrep: "匹配上下文",
2086
2146
  groupAst: "AST 工具",
2087
- optAuto: "auto(自动)",
2088
2147
  optReplace: "replace(字面量替换)",
2089
2148
  optPatch: "patch(unified diff)",
2090
2149
  optHashline: "hashline(行锚点)",
@@ -2173,7 +2232,6 @@ window.__ModuleLoader__.load({
2173
2232
  groupBehavior: "Behavior",
2174
2233
  groupReading: "Reading",
2175
2234
  groupSummary: "Code summary",
2176
- groupImages: "Images",
2177
2235
  groupEditMode: "Edit mode",
2178
2236
  groupGuard: "File guard",
2179
2237
  noConfigTitle: "No global settings for this tool",
@@ -2200,15 +2258,28 @@ window.__ModuleLoader__.load({
2200
2258
  fetchEnabledHint: "Whether fetching is enabled when read handles http(s) URLs.",
2201
2259
  fetchMaxTimeoutSeconds: "Fetch timeout (s)",
2202
2260
  fetchMaxTimeoutSecondsHint: "URL fetch timeout cap; 0 = default 30s.",
2203
- imagesAutoResize: "Auto-resize images",
2204
- imagesAutoResizeHint: "Auto-resize images to a displayable size when reading.",
2205
- inspectImageMode: "Image inspection mode",
2206
- inspectImageModeHint: "Behavior mode for inspecting images: auto/on/off.",
2207
2261
  groupFetch: "Fetching",
2208
- optOn: "on",
2209
- optOff: "off",
2262
+ groupImages: "Images",
2263
+ imagesAutoResize: "Auto-resize images",
2264
+ imagesAutoResizeHint: "Downscale images to a displayable size when reading (off: store originals and let the host normalize).",
2265
+ imagesBlockImages: "Disable image reading",
2266
+ imagesBlockImagesHint: "When on, read and URL fetches return a text note for images instead of showing them (saves tokens; keeps sensitive pictures out of requests).",
2267
+ imagesResizeMaxSide: "Resize target longest edge",
2268
+ imagesResizeMaxSideHint: "Auto-resize downscales the longest edge to at most this many pixels (default 1568, the effective vision-processing ceiling of mainstream models).",
2269
+ imagesResizeMaxBytes: "Resize target bytes",
2270
+ imagesResizeMaxBytesHint: "Compression budget for auto-resize (default 512000 = 500KB); images already below a quarter of it skip re-encoding.",
2271
+ imagesResizeMinSide: "Resize minimum edge",
2272
+ imagesResizeMinSideHint: "Undersized images are upscaled to this edge length (default 200px) — most vision backends reject degenerate tiny images.",
2273
+ imagesResizeJpegQuality: "First-pass encode quality",
2274
+ imagesResizeJpegQualityHint: "Quality (1-100, default 80) for the first lossy encode after resizing; the pipeline still walks down quality/scale when over budget.",
2275
+ imagesExcludeWebp: "Never encode WebP",
2276
+ imagesExcludeWebpHint: "Some local inference backends (llama.cpp/Ollama family) cannot decode WebP; detected models are already auto-excluded. This forces PNG/JPEG output for off-list gateway models.",
2277
+ imagesInputMaxBytes: "Image input size cap",
2278
+ imagesInputMaxBytesHint: "Image files above this size are rejected outright (default 20971520 = 20MB; also capped by the host attachment limits).",
2210
2279
  readRenderMarkdown: "Render Markdown",
2211
2280
  readRenderMarkdownHint: "Render .md files as Markdown when reading.",
2281
+ readConcurrentSafe: "Concurrency-safe reads",
2282
+ readConcurrentSafeHint: "Declare read as concurrency-safe so the host may dispatch multiple read calls (including image reads) in parallel; disable to force serial execution. Default on; changes apply immediately.",
2212
2283
  editMode: "Edit mode",
2213
2284
  editModeHint: "How edit interprets its input: replace / patch / hashline / apply_patch. Default: replace.",
2214
2285
  editFuzzyMatch: "Fuzzy matching",
@@ -2227,7 +2298,6 @@ window.__ModuleLoader__.load({
2227
2298
  astEditEnabledHint: "Enable the ast_edit tool for structural AST rewrites (on by default).",
2228
2299
  groupGrep: "Match context",
2229
2300
  groupAst: "AST tools",
2230
- optAuto: "auto",
2231
2301
  optReplace: "replace (literal)",
2232
2302
  optPatch: "patch (unified diff)",
2233
2303
  optHashline: "hashline (line anchors)",