webaiapi 0.0.0-stage → 0.1.0

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 (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -2
  3. package/bin/webapi.mjs +45 -0
  4. package/dist/browser/auth-capture.d.ts +110 -0
  5. package/dist/browser/auth-capture.d.ts.map +1 -0
  6. package/dist/browser/auth-capture.js +487 -0
  7. package/dist/browser/auth-capture.js.map +1 -0
  8. package/dist/browser/credentials.d.ts +102 -0
  9. package/dist/browser/credentials.d.ts.map +1 -0
  10. package/dist/browser/credentials.js +209 -0
  11. package/dist/browser/credentials.js.map +1 -0
  12. package/dist/browser/js-eval.d.ts +44 -0
  13. package/dist/browser/js-eval.d.ts.map +1 -0
  14. package/dist/browser/js-eval.js +20 -0
  15. package/dist/browser/js-eval.js.map +1 -0
  16. package/dist/browser/login-flow.d.ts +171 -0
  17. package/dist/browser/login-flow.d.ts.map +1 -0
  18. package/dist/browser/login-flow.js +606 -0
  19. package/dist/browser/login-flow.js.map +1 -0
  20. package/dist/browser/manager.d.ts +144 -0
  21. package/dist/browser/manager.d.ts.map +1 -0
  22. package/dist/browser/manager.js +586 -0
  23. package/dist/browser/manager.js.map +1 -0
  24. package/dist/browser/platforms.d.ts +96 -0
  25. package/dist/browser/platforms.d.ts.map +1 -0
  26. package/dist/browser/platforms.js +288 -0
  27. package/dist/browser/platforms.js.map +1 -0
  28. package/dist/browser/sse-capture.d.ts +23 -0
  29. package/dist/browser/sse-capture.d.ts.map +1 -0
  30. package/dist/browser/sse-capture.js +199 -0
  31. package/dist/browser/sse-capture.js.map +1 -0
  32. package/dist/browser/stealth.d.ts +12 -0
  33. package/dist/browser/stealth.d.ts.map +1 -0
  34. package/dist/browser/stealth.js +12 -0
  35. package/dist/browser/stealth.js.map +1 -0
  36. package/dist/cli.d.ts +12 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +441 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/config.d.ts +90 -0
  41. package/dist/config.d.ts.map +1 -0
  42. package/dist/config.js +246 -0
  43. package/dist/config.js.map +1 -0
  44. package/dist/controllers/history-db.d.ts +56 -0
  45. package/dist/controllers/history-db.d.ts.map +1 -0
  46. package/dist/controllers/history-db.js +260 -0
  47. package/dist/controllers/history-db.js.map +1 -0
  48. package/dist/controllers/request-pool.d.ts +78 -0
  49. package/dist/controllers/request-pool.d.ts.map +1 -0
  50. package/dist/controllers/request-pool.js +222 -0
  51. package/dist/controllers/request-pool.js.map +1 -0
  52. package/dist/controllers/router.d.ts +172 -0
  53. package/dist/controllers/router.d.ts.map +1 -0
  54. package/dist/controllers/router.js +569 -0
  55. package/dist/controllers/router.js.map +1 -0
  56. package/dist/index.d.ts +72 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +61 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/log/api-logger.d.ts +55 -0
  61. package/dist/log/api-logger.d.ts.map +1 -0
  62. package/dist/log/api-logger.js +264 -0
  63. package/dist/log/api-logger.js.map +1 -0
  64. package/dist/log/debug-logger.d.ts +49 -0
  65. package/dist/log/debug-logger.d.ts.map +1 -0
  66. package/dist/log/debug-logger.js +285 -0
  67. package/dist/log/debug-logger.js.map +1 -0
  68. package/dist/log/logger.d.ts +16 -0
  69. package/dist/log/logger.d.ts.map +1 -0
  70. package/dist/log/logger.js +35 -0
  71. package/dist/log/logger.js.map +1 -0
  72. package/dist/postinstall.d.ts +3 -0
  73. package/dist/postinstall.d.ts.map +1 -0
  74. package/dist/postinstall.js +90 -0
  75. package/dist/postinstall.js.map +1 -0
  76. package/dist/providers/base.d.ts +83 -0
  77. package/dist/providers/base.d.ts.map +1 -0
  78. package/dist/providers/base.js +51 -0
  79. package/dist/providers/base.js.map +1 -0
  80. package/dist/providers/chatglm-dom-extract.d.ts +24 -0
  81. package/dist/providers/chatglm-dom-extract.d.ts.map +1 -0
  82. package/dist/providers/chatglm-dom-extract.js +175 -0
  83. package/dist/providers/chatglm-dom-extract.js.map +1 -0
  84. package/dist/providers/chatglm-dom.d.ts +110 -0
  85. package/dist/providers/chatglm-dom.d.ts.map +1 -0
  86. package/dist/providers/chatglm-dom.js +384 -0
  87. package/dist/providers/chatglm-dom.js.map +1 -0
  88. package/dist/providers/chatglm-sse.d.ts +119 -0
  89. package/dist/providers/chatglm-sse.d.ts.map +1 -0
  90. package/dist/providers/chatglm-sse.js +241 -0
  91. package/dist/providers/chatglm-sse.js.map +1 -0
  92. package/dist/providers/chatglm.d.ts +288 -0
  93. package/dist/providers/chatglm.d.ts.map +1 -0
  94. package/dist/providers/chatglm.js +1631 -0
  95. package/dist/providers/chatglm.js.map +1 -0
  96. package/dist/routes/chat.d.ts +117 -0
  97. package/dist/routes/chat.d.ts.map +1 -0
  98. package/dist/routes/chat.js +1346 -0
  99. package/dist/routes/chat.js.map +1 -0
  100. package/dist/routes/compress.d.ts +142 -0
  101. package/dist/routes/compress.d.ts.map +1 -0
  102. package/dist/routes/compress.js +551 -0
  103. package/dist/routes/compress.js.map +1 -0
  104. package/dist/routes/failover.d.ts +34 -0
  105. package/dist/routes/failover.d.ts.map +1 -0
  106. package/dist/routes/failover.js +221 -0
  107. package/dist/routes/failover.js.map +1 -0
  108. package/dist/routes/helpers.d.ts +174 -0
  109. package/dist/routes/helpers.d.ts.map +1 -0
  110. package/dist/routes/helpers.js +525 -0
  111. package/dist/routes/helpers.js.map +1 -0
  112. package/dist/routes/meta.d.ts +31 -0
  113. package/dist/routes/meta.d.ts.map +1 -0
  114. package/dist/routes/meta.js +184 -0
  115. package/dist/routes/meta.js.map +1 -0
  116. package/dist/routes/sse.d.ts +82 -0
  117. package/dist/routes/sse.d.ts.map +1 -0
  118. package/dist/routes/sse.js +77 -0
  119. package/dist/routes/sse.js.map +1 -0
  120. package/dist/server/app.d.ts +24 -0
  121. package/dist/server/app.d.ts.map +1 -0
  122. package/dist/server/app.js +292 -0
  123. package/dist/server/app.js.map +1 -0
  124. package/dist/server/middleware/api-key.d.ts +61 -0
  125. package/dist/server/middleware/api-key.d.ts.map +1 -0
  126. package/dist/server/middleware/api-key.js +95 -0
  127. package/dist/server/middleware/api-key.js.map +1 -0
  128. package/dist/server/middleware/rate-limit.d.ts +67 -0
  129. package/dist/server/middleware/rate-limit.d.ts.map +1 -0
  130. package/dist/server/middleware/rate-limit.js +134 -0
  131. package/dist/server/middleware/rate-limit.js.map +1 -0
  132. package/dist/server/middleware/timeout.d.ts +34 -0
  133. package/dist/server/middleware/timeout.d.ts.map +1 -0
  134. package/dist/server/middleware/timeout.js +64 -0
  135. package/dist/server/middleware/timeout.js.map +1 -0
  136. package/dist/server/types.d.ts +20 -0
  137. package/dist/server/types.d.ts.map +1 -0
  138. package/dist/server/types.js +2 -0
  139. package/dist/server/types.js.map +1 -0
  140. package/dist/tools/build.d.ts +14 -0
  141. package/dist/tools/build.d.ts.map +1 -0
  142. package/dist/tools/build.js +105 -0
  143. package/dist/tools/build.js.map +1 -0
  144. package/dist/tools/clean.d.ts +32 -0
  145. package/dist/tools/clean.d.ts.map +1 -0
  146. package/dist/tools/clean.js +65 -0
  147. package/dist/tools/clean.js.map +1 -0
  148. package/dist/tools/convert.d.ts +50 -0
  149. package/dist/tools/convert.d.ts.map +1 -0
  150. package/dist/tools/convert.js +289 -0
  151. package/dist/tools/convert.js.map +1 -0
  152. package/dist/tools/names.d.ts +138 -0
  153. package/dist/tools/names.d.ts.map +1 -0
  154. package/dist/tools/names.js +578 -0
  155. package/dist/tools/names.js.map +1 -0
  156. package/dist/tools/parse.d.ts +31 -0
  157. package/dist/tools/parse.d.ts.map +1 -0
  158. package/dist/tools/parse.js +273 -0
  159. package/dist/tools/parse.js.map +1 -0
  160. package/dist/tools/placeholder.d.ts +21 -0
  161. package/dist/tools/placeholder.d.ts.map +1 -0
  162. package/dist/tools/placeholder.js +135 -0
  163. package/dist/tools/placeholder.js.map +1 -0
  164. package/dist/tools/prompts.d.ts +42 -0
  165. package/dist/tools/prompts.d.ts.map +1 -0
  166. package/dist/tools/prompts.js +69 -0
  167. package/dist/tools/prompts.js.map +1 -0
  168. package/dist/tools/protocol.d.ts +58 -0
  169. package/dist/tools/protocol.d.ts.map +1 -0
  170. package/dist/tools/protocol.js +287 -0
  171. package/dist/tools/protocol.js.map +1 -0
  172. package/dist/tools/repair.d.ts +83 -0
  173. package/dist/tools/repair.d.ts.map +1 -0
  174. package/dist/tools/repair.js +510 -0
  175. package/dist/tools/repair.js.map +1 -0
  176. package/dist/tools/workspace.d.ts +40 -0
  177. package/dist/tools/workspace.d.ts.map +1 -0
  178. package/dist/tools/workspace.js +224 -0
  179. package/dist/tools/workspace.js.map +1 -0
  180. package/dist/types/message.d.ts +87 -0
  181. package/dist/types/message.d.ts.map +1 -0
  182. package/dist/types/message.js +85 -0
  183. package/dist/types/message.js.map +1 -0
  184. package/dist/types/openai.d.ts +100 -0
  185. package/dist/types/openai.d.ts.map +1 -0
  186. package/dist/types/openai.js +66 -0
  187. package/dist/types/openai.js.map +1 -0
  188. package/dist/utils/errors.d.ts +82 -0
  189. package/dist/utils/errors.d.ts.map +1 -0
  190. package/dist/utils/errors.js +115 -0
  191. package/dist/utils/errors.js.map +1 -0
  192. package/dist/utils/mutex.d.ts +31 -0
  193. package/dist/utils/mutex.d.ts.map +1 -0
  194. package/dist/utils/mutex.js +61 -0
  195. package/dist/utils/mutex.js.map +1 -0
  196. package/dist/utils/prompt.d.ts +150 -0
  197. package/dist/utils/prompt.d.ts.map +1 -0
  198. package/dist/utils/prompt.js +514 -0
  199. package/dist/utils/prompt.js.map +1 -0
  200. package/dist/utils/text.d.ts +30 -0
  201. package/dist/utils/text.d.ts.map +1 -0
  202. package/dist/utils/text.js +70 -0
  203. package/dist/utils/text.js.map +1 -0
  204. package/package.json +67 -4
  205. package/postinstall.mjs +34 -0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 反检测脚本(浏览器注入用)—— 与 `legacy-python/src/browser/stealth.py` 中的
3
+ * `STEALTH_JS` **逐字节一致**(用脚本抽取而非手抄,避免抄错)。
4
+ *
5
+ * 用法:`page.addInitScript({ content: STEALTH_JS })`
6
+ * —— 用 `{ content }` 形式而不是裸字符串,避免 Playwright 把字符串当成文件路径。
7
+ *
8
+ * TODO(可选):有类型化空间 —— 可改写成真正的 TS 函数以获得类型检查与断点调试。
9
+ * 当前优先保证与原实现行为一致。
10
+ */
11
+ export declare const STEALTH_JS = "\n(() => {\n 'use strict';\n\n // ---- 1.1 \u9690\u85CF webdriver \u6807\u5FD7 ----\n // Playwright/Selenium \u9ED8\u8BA4\u4F1A\u8BBE\u7F6E navigator.webdriver = true\uFF0C\u8FD9\u662F\u6700\u5E38\u89C1\u7684\u68C0\u6D4B\u70B9\n try {\n Object.defineProperty(Navigator.prototype, 'webdriver', {\n get: () => undefined,\n configurable: true,\n });\n } catch (e) {}\n // \u515C\u5E95\uFF1A\u76F4\u63A5\u5220\u9664\u5C5E\u6027\n try {\n delete Navigator.prototype.webdriver;\n } catch (e) {}\n\n // ---- 1.2 \u4F2A\u9020 navigator.languages ----\n try {\n Object.defineProperty(navigator, 'languages', {\n get: () => ['zh-CN', 'zh', 'en-US', 'en'],\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.3 \u4F2A\u9020 navigator.platform\uFF08\u4E0E Linux x86_64 UA \u5339\u914D\uFF09----\n try {\n Object.defineProperty(navigator, 'platform', {\n get: () => 'Linux x86_64',\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.4 \u4F2A\u9020 navigator.vendor ----\n try {\n Object.defineProperty(navigator, 'vendor', {\n get: () => 'Google Inc.',\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.5 \u4F2A\u9020 navigator.plugins\uFF08Chromium \u9ED8\u8BA4\u6709 5 \u4E2A PDF \u76F8\u5173\u63D2\u4EF6\uFF09----\n // \u81EA\u52A8\u5316\u6A21\u5F0F\u4E0B plugins \u4E3A\u7A7A\u6570\u7EC4\uFF0C\u4F1A\u88AB\u68C0\u6D4B\n try {\n const fakePluginData = [\n { name: 'PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Chrome PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Chromium PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Microsoft Edge PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'WebKit built-in PDF', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n ];\n const fakePlugins = fakePluginData.map(p => {\n const plugin = Object.create(Plugin.prototype);\n Object.defineProperties(plugin, {\n name: { value: p.name },\n filename: { value: p.filename },\n description: { value: p.description },\n length: { value: 1 },\n });\n return plugin;\n });\n Object.defineProperty(navigator, 'plugins', {\n get: () => fakePlugins,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.6 \u4F2A\u9020 navigator.mimeTypes ----\n try {\n const fakeMimeTypes = [\n { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },\n { type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format' },\n ];\n Object.defineProperty(navigator, 'mimeTypes', {\n get: () => fakeMimeTypes,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.7 \u4F2A\u9020 hardwareConcurrency / deviceMemory ----\n try {\n Object.defineProperty(navigator, 'hardwareConcurrency', {\n get: () => 8,\n configurable: true,\n });\n } catch (e) {}\n try {\n Object.defineProperty(navigator, 'deviceMemory', {\n get: () => 8,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.8 \u4FEE\u590D navigator.permissions.query ----\n // \u81EA\u52A8\u5316\u6A21\u5F0F\u4E0B permissions.query \u884C\u4E3A\u5F02\u5E38\uFF0C\u90E8\u5206\u7F51\u7AD9\u7528\u4E8E\u68C0\u6D4B\n try {\n const originalQuery = navigator.permissions && navigator.permissions.query;\n if (originalQuery) {\n navigator.permissions.query = function (parameters) {\n if (parameters && parameters.name === 'notifications') {\n return Promise.resolve({\n state: typeof Notification !== 'undefined'\n ? Notification.permission\n : 'prompt',\n onchange: null,\n });\n }\n return originalQuery.call(navigator.permissions, parameters);\n };\n }\n } catch (e) {}\n\n // ---- 1.9 \u6CE8\u5165 window.chrome\uFF08Chromium \u6B63\u5E38\u6709\u6B64\u5BF9\u8C61\uFF09----\n // \u81EA\u52A8\u5316\u6A21\u5F0F\u4E0B window.chrome \u53EF\u80FD\u7F3A\u5931\u6216\u88AB\u7B80\u5316\n try {\n if (!window.chrome) {\n window.chrome = {};\n }\n if (!window.chrome.runtime) {\n window.chrome.runtime = {\n // \u771F\u5B9E Chrome \u5728\u6269\u5C55\u672A\u5B89\u88C5\u65F6\u4E5F\u4FDD\u7559 runtime \u5BF9\u8C61\n OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', UPDATE: 'update' },\n OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' },\n PlatformArch: { ARM: 'arm', ARM64: 'arm64', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },\n PlatformOs: { ANDROID: 'android', CROS: 'cros', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' },\n };\n }\n // chrome.app \u5728\u666E\u901A\u6D4F\u89C8\u65F6\u5B58\u5728\n if (!window.chrome.app) {\n window.chrome.app = { isInstalled: false };\n }\n } catch (e) {}\n\n // ---- 1.10 \u4F2A\u9020 WebGL vendor/renderer ----\n // \u81EA\u52A8\u5316\u6A21\u5F0F\u4E0B GPU \u4FE1\u606F\u53EF\u80FD\u4E3A SwiftShader\uFF0C\u660E\u663E\u66B4\u9732\u865A\u62DF\u73AF\u5883\n try {\n const getParameter = WebGLRenderingContext.prototype.getParameter;\n WebGLRenderingContext.prototype.getParameter = function (parameter) {\n // UNMASKED_VENDOR_WEBGL = 37445\n if (parameter === 37445) return 'Intel Inc.';\n // UNMASKED_RENDERER_WEBGL = 37446\n if (parameter === 37446) return 'Intel(R) Iris(R) Plus Graphics 640 (Kaby Lake 3 eDRAM)';\n return getParameter.call(this, parameter);\n };\n // WebGL2 \u540C\u6837\u5904\u7406\n if (typeof WebGL2RenderingContext !== 'undefined') {\n const getParameter2 = WebGL2RenderingContext.prototype.getParameter;\n WebGL2RenderingContext.prototype.getParameter = function (parameter) {\n if (parameter === 37445) return 'Intel Inc.';\n if (parameter === 37446) return 'Intel(R) Iris(R) Plus Graphics 640 (Kaby Lake 3 eDRAM)';\n return getParameter2.call(this, parameter);\n };\n }\n } catch (e) {}\n\n // ---- 1.11 \u9690\u85CF Playwright/CDP \u75D5\u8FF9 ----\n // Runtime.enable \u4F1A\u5728 console.debug \u4E0A\u7559\u4E0B stack trace\uFF0C\u90E8\u5206\u7F51\u7AD9\u901A\u8FC7\u6B64\u68C0\u6D4B\n try {\n const originalDebug = console.debug;\n console.debug = function () {\n return originalDebug.apply(console, arguments);\n };\n // \u79FB\u9664 console.debug.toString \u66B4\u9732\u7684\u539F\u751F\u4EE3\u7801\u7279\u5F81\n try {\n Object.defineProperty(console.debug, 'toString', {\n value: () => 'function debug() { [native code] }',\n });\n } catch (e) {}\n } catch (e) {}\n\n // ---- 1.12 \u4FEE\u590D iframe contentWindow \u68C0\u6D4B ----\n // \u90E8\u5206\u7F51\u7AD9\u901A\u8FC7 iframe.contentWindow.chrome \u68C0\u6D4B\u81EA\u52A8\u5316\n try {\n const originalContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');\n if (originalContentWindow && originalContentWindow.get) {\n Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', {\n get: function () {\n const result = originalContentWindow.get.call(this);\n if (result) {\n try {\n if (!result.chrome) result.chrome = window.chrome;\n } catch (e) {}\n }\n return result;\n },\n configurable: true,\n });\n }\n } catch (e) {}\n\n // ---- 1.13 \u9690\u85CF\u81EA\u52A8\u5316\u76F8\u5173\u7684 toString \u75D5\u8FF9 ----\n // \u81EA\u5B9A\u4E49\u7684\u51FD\u6570 toString \u4F1A\u66B4\u9732 \"function () { ... }\" \u800C\u975E native code\n const nativeToStringFn = Function.prototype.toString;\n const fakeNativeToString = function () {\n if (this === fakeNativeToString) return 'function toString() { [native code] }';\n // \u5BF9\u6211\u4EEC override \u7684\u51FD\u6570\u8FD4\u56DE native code\n if (typeof this === 'function' && this.__native__) return this.__native__;\n return nativeToStringFn.call(this);\n };\n // \u4E0D\u5F3A\u5236\u66FF\u6362\uFF0C\u907F\u514D\u7834\u574F\u6B63\u5E38 JS \u884C\u4E3A\n\n // ---- 1.14 \u4FEE\u590D navigator.getBattery\uFF08\u90E8\u5206\u7F51\u7AD9\u7528\u4E8E\u6307\u7EB9\uFF09----\n try {\n if (!navigator.getBattery) {\n navigator.getBattery = () => Promise.resolve({\n charging: true,\n chargingTime: 0,\n dischargingTime: Infinity,\n level: 1,\n addEventListener: () => {},\n removeEventListener: () => {},\n });\n }\n } catch (e) {}\n\n // ---- 1.15 \u5A92\u4F53\u8BBE\u5907\u4F2A\u9020 ----\n // \u81EA\u52A8\u5316\u6A21\u5F0F\u4E0B navigator.mediaDevices.enumerateDevices \u53EF\u80FD\u8FD4\u56DE\u7A7A\n try {\n if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {\n const originalEnumerate = navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);\n navigator.mediaDevices.enumerateDevices = async function () {\n const devices = await originalEnumerate();\n if (devices && devices.length > 0) return devices;\n // \u8FD4\u56DE\u4F2A\u9020\u7684\u97F3\u9891/\u89C6\u9891\u8BBE\u5907\n return [\n { kind: 'audioinput', deviceId: 'default', groupId: 'default', label: '' },\n { kind: 'audiooutput', deviceId: 'default', groupId: 'default', label: '' },\n { kind: 'videoinput', deviceId: 'default', groupId: 'default', label: '' },\n ];\n };\n }\n } catch (e) {}\n\n})();\n";
12
+ //# sourceMappingURL=stealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stealth.d.ts","sourceRoot":"","sources":["../../src/browser/stealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,6oVAA8tS,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 反检测脚本(浏览器注入用)—— 与 `legacy-python/src/browser/stealth.py` 中的
3
+ * `STEALTH_JS` **逐字节一致**(用脚本抽取而非手抄,避免抄错)。
4
+ *
5
+ * 用法:`page.addInitScript({ content: STEALTH_JS })`
6
+ * —— 用 `{ content }` 形式而不是裸字符串,避免 Playwright 把字符串当成文件路径。
7
+ *
8
+ * TODO(可选):有类型化空间 —— 可改写成真正的 TS 函数以获得类型检查与断点调试。
9
+ * 当前优先保证与原实现行为一致。
10
+ */
11
+ export const STEALTH_JS = "\n(() => {\n 'use strict';\n\n // ---- 1.1 隐藏 webdriver 标志 ----\n // Playwright/Selenium 默认会设置 navigator.webdriver = true,这是最常见的检测点\n try {\n Object.defineProperty(Navigator.prototype, 'webdriver', {\n get: () => undefined,\n configurable: true,\n });\n } catch (e) {}\n // 兜底:直接删除属性\n try {\n delete Navigator.prototype.webdriver;\n } catch (e) {}\n\n // ---- 1.2 伪造 navigator.languages ----\n try {\n Object.defineProperty(navigator, 'languages', {\n get: () => ['zh-CN', 'zh', 'en-US', 'en'],\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.3 伪造 navigator.platform(与 Linux x86_64 UA 匹配)----\n try {\n Object.defineProperty(navigator, 'platform', {\n get: () => 'Linux x86_64',\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.4 伪造 navigator.vendor ----\n try {\n Object.defineProperty(navigator, 'vendor', {\n get: () => 'Google Inc.',\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.5 伪造 navigator.plugins(Chromium 默认有 5 个 PDF 相关插件)----\n // 自动化模式下 plugins 为空数组,会被检测\n try {\n const fakePluginData = [\n { name: 'PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Chrome PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Chromium PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'Microsoft Edge PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n { name: 'WebKit built-in PDF', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },\n ];\n const fakePlugins = fakePluginData.map(p => {\n const plugin = Object.create(Plugin.prototype);\n Object.defineProperties(plugin, {\n name: { value: p.name },\n filename: { value: p.filename },\n description: { value: p.description },\n length: { value: 1 },\n });\n return plugin;\n });\n Object.defineProperty(navigator, 'plugins', {\n get: () => fakePlugins,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.6 伪造 navigator.mimeTypes ----\n try {\n const fakeMimeTypes = [\n { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },\n { type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format' },\n ];\n Object.defineProperty(navigator, 'mimeTypes', {\n get: () => fakeMimeTypes,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.7 伪造 hardwareConcurrency / deviceMemory ----\n try {\n Object.defineProperty(navigator, 'hardwareConcurrency', {\n get: () => 8,\n configurable: true,\n });\n } catch (e) {}\n try {\n Object.defineProperty(navigator, 'deviceMemory', {\n get: () => 8,\n configurable: true,\n });\n } catch (e) {}\n\n // ---- 1.8 修复 navigator.permissions.query ----\n // 自动化模式下 permissions.query 行为异常,部分网站用于检测\n try {\n const originalQuery = navigator.permissions && navigator.permissions.query;\n if (originalQuery) {\n navigator.permissions.query = function (parameters) {\n if (parameters && parameters.name === 'notifications') {\n return Promise.resolve({\n state: typeof Notification !== 'undefined'\n ? Notification.permission\n : 'prompt',\n onchange: null,\n });\n }\n return originalQuery.call(navigator.permissions, parameters);\n };\n }\n } catch (e) {}\n\n // ---- 1.9 注入 window.chrome(Chromium 正常有此对象)----\n // 自动化模式下 window.chrome 可能缺失或被简化\n try {\n if (!window.chrome) {\n window.chrome = {};\n }\n if (!window.chrome.runtime) {\n window.chrome.runtime = {\n // 真实 Chrome 在扩展未安装时也保留 runtime 对象\n OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', UPDATE: 'update' },\n OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' },\n PlatformArch: { ARM: 'arm', ARM64: 'arm64', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },\n PlatformOs: { ANDROID: 'android', CROS: 'cros', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' },\n };\n }\n // chrome.app 在普通浏览时存在\n if (!window.chrome.app) {\n window.chrome.app = { isInstalled: false };\n }\n } catch (e) {}\n\n // ---- 1.10 伪造 WebGL vendor/renderer ----\n // 自动化模式下 GPU 信息可能为 SwiftShader,明显暴露虚拟环境\n try {\n const getParameter = WebGLRenderingContext.prototype.getParameter;\n WebGLRenderingContext.prototype.getParameter = function (parameter) {\n // UNMASKED_VENDOR_WEBGL = 37445\n if (parameter === 37445) return 'Intel Inc.';\n // UNMASKED_RENDERER_WEBGL = 37446\n if (parameter === 37446) return 'Intel(R) Iris(R) Plus Graphics 640 (Kaby Lake 3 eDRAM)';\n return getParameter.call(this, parameter);\n };\n // WebGL2 同样处理\n if (typeof WebGL2RenderingContext !== 'undefined') {\n const getParameter2 = WebGL2RenderingContext.prototype.getParameter;\n WebGL2RenderingContext.prototype.getParameter = function (parameter) {\n if (parameter === 37445) return 'Intel Inc.';\n if (parameter === 37446) return 'Intel(R) Iris(R) Plus Graphics 640 (Kaby Lake 3 eDRAM)';\n return getParameter2.call(this, parameter);\n };\n }\n } catch (e) {}\n\n // ---- 1.11 隐藏 Playwright/CDP 痕迹 ----\n // Runtime.enable 会在 console.debug 上留下 stack trace,部分网站通过此检测\n try {\n const originalDebug = console.debug;\n console.debug = function () {\n return originalDebug.apply(console, arguments);\n };\n // 移除 console.debug.toString 暴露的原生代码特征\n try {\n Object.defineProperty(console.debug, 'toString', {\n value: () => 'function debug() { [native code] }',\n });\n } catch (e) {}\n } catch (e) {}\n\n // ---- 1.12 修复 iframe contentWindow 检测 ----\n // 部分网站通过 iframe.contentWindow.chrome 检测自动化\n try {\n const originalContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');\n if (originalContentWindow && originalContentWindow.get) {\n Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', {\n get: function () {\n const result = originalContentWindow.get.call(this);\n if (result) {\n try {\n if (!result.chrome) result.chrome = window.chrome;\n } catch (e) {}\n }\n return result;\n },\n configurable: true,\n });\n }\n } catch (e) {}\n\n // ---- 1.13 隐藏自动化相关的 toString 痕迹 ----\n // 自定义的函数 toString 会暴露 \"function () { ... }\" 而非 native code\n const nativeToStringFn = Function.prototype.toString;\n const fakeNativeToString = function () {\n if (this === fakeNativeToString) return 'function toString() { [native code] }';\n // 对我们 override 的函数返回 native code\n if (typeof this === 'function' && this.__native__) return this.__native__;\n return nativeToStringFn.call(this);\n };\n // 不强制替换,避免破坏正常 JS 行为\n\n // ---- 1.14 修复 navigator.getBattery(部分网站用于指纹)----\n try {\n if (!navigator.getBattery) {\n navigator.getBattery = () => Promise.resolve({\n charging: true,\n chargingTime: 0,\n dischargingTime: Infinity,\n level: 1,\n addEventListener: () => {},\n removeEventListener: () => {},\n });\n }\n } catch (e) {}\n\n // ---- 1.15 媒体设备伪造 ----\n // 自动化模式下 navigator.mediaDevices.enumerateDevices 可能返回空\n try {\n if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {\n const originalEnumerate = navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);\n navigator.mediaDevices.enumerateDevices = async function () {\n const devices = await originalEnumerate();\n if (devices && devices.length > 0) return devices;\n // 返回伪造的音频/视频设备\n return [\n { kind: 'audioinput', deviceId: 'default', groupId: 'default', label: '' },\n { kind: 'audiooutput', deviceId: 'default', groupId: 'default', label: '' },\n { kind: 'videoinput', deviceId: 'default', groupId: 'default', label: '' },\n ];\n };\n }\n } catch (e) {}\n\n})();\n";
12
+ //# sourceMappingURL=stealth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stealth.js","sourceRoot":"","sources":["../../src/browser/stealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,2tSAA2tS,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ command: string;
4
+ positional: string[];
5
+ flags: Record<string, string | boolean>;
6
+ }
7
+ /** 极简参数解析(避免为一个 CLI 引入 commander/yargs)。 */
8
+ export declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
9
+ export declare function helpText(): string;
10
+ export declare function runCli(argv?: ReadonlyArray<string>): Promise<number>;
11
+ export {};
12
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmCA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;CACzC;AAED,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,UAAU,CA6BjE;AAiBD,wBAAgB,QAAQ,IAAI,MAAM,CAoDjC;AA2PD,wBAAsB,MAAM,CAAC,IAAI,GAAE,aAAa,CAAC,MAAM,CAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqExF"}
package/dist/cli.js ADDED
@@ -0,0 +1,441 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI 入口 —— 合并了 Python 版的 `apis.sh`(服务运维)与 `scripts/capture_auth.py`
4
+ * (凭证捕获)。
5
+ *
6
+ * 用法:
7
+ * ```
8
+ * webapi start 启动服务(前台阻塞)
9
+ * webapi status 查看运行状态(账号 / 辅助通道 / 请求池)
10
+ * webapi accounts 只看账号与角色
11
+ * webapi health 健康检查
12
+ * webapi login chatglm [--account x] 交互式登录并捕获凭证
13
+ * webapi logout chatglm [--account x] 删除某账号的凭证
14
+ * webapi --help
15
+ * ```
16
+ *
17
+ * ## 为什么用 CLI 而不是 shell 脚本
18
+ * Python 版的 `apis.sh` 有个必须存在的理由:它在 shell 层把环境里的
19
+ * `DEBUG=release` 归一化成 `DEBUG=false`,否则 pydantic 的 `Settings.debug: bool`
20
+ * 会校验失败。TS 版的配置解析器(`toBool`)**自己就容错**,
21
+ * 所以 shell 包装层不再需要,CLI 用 Node 直接跑。
22
+ */
23
+ import { existsSync, realpathSync } from 'node:fs';
24
+ import { fileURLToPath } from 'node:url';
25
+ import { settings } from './config.js';
26
+ import { listAccounts, loadAuth } from './browser/credentials.js';
27
+ import { PLATFORM_IDS, PLATFORM_LIST } from './browser/platforms.js';
28
+ import { logger } from './log/logger.js';
29
+ import { padDisplay } from './utils/prompt.js';
30
+ /** 极简参数解析(避免为一个 CLI 引入 commander/yargs)。 */
31
+ export function parseArgs(argv) {
32
+ const args = argv.slice(2);
33
+ const flags = {};
34
+ const positional = [];
35
+ for (let i = 0; i < args.length; i += 1) {
36
+ const arg = args[i];
37
+ if (arg.startsWith('--')) {
38
+ const name = arg.slice(2);
39
+ if (name.includes('=')) {
40
+ const idx = name.indexOf('=');
41
+ flags[name.slice(0, idx)] = name.slice(idx + 1);
42
+ continue;
43
+ }
44
+ const next = args[i + 1];
45
+ if (next !== undefined && !next.startsWith('-')) {
46
+ flags[name] = next;
47
+ i += 1;
48
+ }
49
+ else {
50
+ flags[name] = true;
51
+ }
52
+ }
53
+ else if (arg.startsWith('-') && arg.length > 1) {
54
+ flags[arg.slice(1)] = true;
55
+ }
56
+ else {
57
+ positional.push(arg);
58
+ }
59
+ }
60
+ return { command: positional[0] ?? 'help', positional: positional.slice(1), flags };
61
+ }
62
+ /** 取一个字符串型 flag(`--account=acc2` / `--account acc2` 两种写法都支持)。 */
63
+ function strFlag(flags, name) {
64
+ const value = flags[name];
65
+ return typeof value === 'string' && value !== '' ? value : undefined;
66
+ }
67
+ /** 取一个布尔型 flag(`--yes`)。 */
68
+ function boolFlag(flags, name) {
69
+ return flags[name] === true;
70
+ }
71
+ /* ==================================================================== */
72
+ /* 帮助 */
73
+ /* ==================================================================== */
74
+ export function helpText() {
75
+ // 平台清单由注册表生成(`platforms.ts`)——加平台不用改这里。
76
+ // ⚠️ 必须用 padDisplay(按显示宽度)而不是 padEnd:中文/全角占两列,
77
+ // padEnd 会让第二列参差不齐。
78
+ const platformLines = PLATFORM_LIST.map((spec) => {
79
+ const tag = spec.providerImplemented ? '可对话' : '暂仅可捕获凭证';
80
+ return ` ${padDisplay(spec.id, 10)} ${padDisplay(spec.displayName, 18)} ${tag}`;
81
+ }).join('\n');
82
+ return `
83
+ webapi —— 把国产 AI 聊天网页包装成 OpenAI 兼容接口
84
+
85
+ 用法:
86
+ webapi login [<platform>] [--account <id>] 登录并捕获凭证(可多账号,交互式选择)
87
+ webapi logout [<platform>] [--account <id>] 删除某账号的凭证(交互式选择)
88
+ webapi accounts 账号与角色(多账号是否生效的权威视图)
89
+ webapi status 运行状态(模型路由 / 辅助通道 / 请求池)
90
+ webapi health 健康检查(各平台登录状态)
91
+ webapi doctor 自检(Node / Firefox / 凭证 / 端口)
92
+ webapi start 启动服务(前台运行,Ctrl-C 退出)
93
+ webapi --help
94
+ webapi --version
95
+
96
+ 平台(login / logout 不给参数时会让你选):
97
+ ${platformLines}
98
+
99
+ 首次使用(两步):
100
+ 1. npx playwright install firefox 下载浏览器(约 80MB,只需一次)
101
+ 2. webapi login 选平台 → 登录并捕获凭证(会打开可见浏览器)
102
+
103
+ 多账号:
104
+ 同一平台登录第二个账号即可启用「辅助通道」—— 历史压缩与工具调用转换
105
+ 因此能与主对话并行(只有一个账号时这些任务与主对话串行,功能正常但更慢)。
106
+ 账号名会从登录身份自动派生(用户名 → uid),不用自己想。
107
+ ⚠️ 两个账号必须是不同的人:抓到的身份与 Cookie 都会被校验,重复了会拦下来。
108
+
109
+ 非交互(脚本 / CI): webapi login chatglm --account acc2 --yes
110
+
111
+ 环境变量(可用 .env):
112
+ API_HOST / API_PORT 监听地址与端口(默认 ${settings.apiHost}:${settings.apiPort})
113
+ API_PREFIX 路由前缀(默认 ${settings.apiPrefix})
114
+ REQUEST_TIMEOUT 请求超时秒数(默认 ${settings.requestTimeout})
115
+ RATE_LIMIT_ENABLED 是否限流(默认 ${String(settings.rateLimitEnabled)})
116
+ RATE_LIMIT_PER_MINUTE 每客户端每分钟请求数(默认 ${settings.rateLimitPerMinute})
117
+ API_KEYS 逗号分隔的密钥列表
118
+ API_KEY_REQUIRED 是否强制校验(默认 ${String(settings.apiKeyRequired)})
119
+ DEBUG_LOGS 是否写调试日志(默认 ${String(settings.debugLogs)})
120
+ CHATGLM_MAIN_ACCOUNT 钉死主通道账号(默认启动时随机分配)
121
+ NO_COLOR 设了就不输出 ANSI 颜色
122
+
123
+ 服务地址: http://${settings.apiHost}:${settings.apiPort}${settings.apiPrefix}
124
+ `.trim();
125
+ }
126
+ /* ==================================================================== */
127
+ /* 子命令 */
128
+ /* ==================================================================== */
129
+ /**
130
+ * 请求本地服务的 JSON 接口(失败返回 null)。
131
+ *
132
+ * ⚠️ 超时必须**按端点区分**:
133
+ * - `/status`、`/models` 很快(只读文件),几秒足够;
134
+ * - `/health` 会**真的启动浏览器并整页探测登录态**,首次要 6~20s ——
135
+ * 用统一的 8s 超时会得到「明明服务在跑却说没在跑」的误导结论。
136
+ */
137
+ async function fetchJson(path, timeoutMs = 8000) {
138
+ const url = `http://${settings.apiHost}:${settings.apiPort}${settings.apiPrefix}${path}`;
139
+ try {
140
+ const controller = new AbortController();
141
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
142
+ try {
143
+ const resp = await fetch(url, { signal: controller.signal });
144
+ if (!resp.ok)
145
+ return null;
146
+ return (await resp.json());
147
+ }
148
+ finally {
149
+ clearTimeout(timer);
150
+ }
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ /** `status` / `accounts`:格式化账号与辅助通道视图。 */
157
+ function renderAccounts(payload) {
158
+ const lines = [];
159
+ const accounts = (payload['accounts'] ?? {});
160
+ const platforms = Object.keys(accounts);
161
+ if (platforms.length === 0) {
162
+ lines.push('(未发现任何凭证:请先运行 `webapi login chatglm`)');
163
+ return lines;
164
+ }
165
+ for (const platform of platforms) {
166
+ lines.push(`【${platform}】`);
167
+ for (const acc of accounts[platform] ?? []) {
168
+ const parts = [
169
+ ` ${acc.account_id}`,
170
+ `身份=${acc.identity || '(未知)'}`,
171
+ `Cookie=${acc.cookies}`,
172
+ `角色=${acc.roles.join('+')}`,
173
+ ];
174
+ if (acc.cooldown_seconds > 0)
175
+ parts.push(`冷却中(${acc.cooldown_seconds}s)`);
176
+ if (acc.busy)
177
+ parts.push('使用中');
178
+ lines.push(parts.join(' '));
179
+ }
180
+ }
181
+ const aux = (payload['aux_channel'] ?? {});
182
+ lines.push('');
183
+ lines.push(aux['enabled']
184
+ ? `辅助通道: 已启用(${String(aux['platform'])}/${String(aux['account_id'])},` +
185
+ `身份 ${String(aux['identity'] || '未知')})→ 压缩/转换可与主对话并行`
186
+ : '辅助通道: 未启用(同平台只有一个账号)→ 辅助任务与主对话串行');
187
+ return lines;
188
+ }
189
+ /** `status` 子命令。 */
190
+ async function cmdStatus() {
191
+ const payload = await fetchJson('/status');
192
+ if (!payload) {
193
+ console.error(`无法连接服务(http://${settings.apiHost}:${settings.apiPort}${settings.apiPrefix})。` +
194
+ '请先运行 `webapi start`。');
195
+ return 1;
196
+ }
197
+ console.log(renderAccounts(payload).join('\n'));
198
+ const pool = payload['request_pool'];
199
+ if (pool) {
200
+ console.log('');
201
+ console.log(`请求池: 总 ${String(pool['total_requests'])}(队列 ${String(pool['queue_size'])})` +
202
+ ` ${JSON.stringify(pool['status_counts'])}`);
203
+ }
204
+ return 0;
205
+ }
206
+ /** `accounts` 子命令。 */
207
+ async function cmdAccounts() {
208
+ const payload = await fetchJson('/status');
209
+ if (!payload) {
210
+ console.error('无法连接服务,请先运行 `webapi start`。');
211
+ return 1;
212
+ }
213
+ console.log(renderAccounts(payload).join('\n'));
214
+ return 0;
215
+ }
216
+ /** `health` 子命令。 */
217
+ async function cmdHealth() {
218
+ // ⚠️ /health 会启动浏览器探测登录态,首次可能要 20s+,不能用短超时
219
+ console.log('正在检查各平台登录状态(首次会启动浏览器,请稍候)...');
220
+ const payload = await fetchJson('/health', 120_000);
221
+ if (!payload) {
222
+ console.error('服务不可用(健康检查失败或超时)。');
223
+ return 1;
224
+ }
225
+ console.log(`状态: ${String(payload['status'])} 版本: ${String(payload['version'])}`);
226
+ const providers = (payload['providers'] ?? {});
227
+ for (const [name, info] of Object.entries(providers)) {
228
+ console.log(` ${name}: ${info['status']}${info['message'] ? ` (${info['message']})` : ''}`);
229
+ }
230
+ return payload['status'] === 'ok' ? 0 : 1;
231
+ }
232
+ /* `logout` 已迁到 `browser/login-flow.ts` 的 `runLogout()`:
233
+ * 它需要交互式选账号,而且**不能**再默认删 `default`(旧实现在这里就会静默删错)。 */
234
+ /**
235
+ * `doctor` 子命令:自检。
236
+ *
237
+ * 为什么需要:这个包**装完之后最常见的失败**有两个,而且都不是代码问题 ——
238
+ * ① 没下 Firefox 二进制(Playwright 不随包分发浏览器,约 80MB);
239
+ * ② 没捕获凭证(等于没登录,平台会一直回「请求过于频繁」)。
240
+ * 两者都会表现为「装好了但跑不起来」,`doctor` 把它们变成一句可执行的提示。
241
+ */
242
+ async function cmdDoctor() {
243
+ let problems = 0;
244
+ const ok = (msg) => console.log(` ✅ ${msg}`);
245
+ const bad = (msg, fix) => {
246
+ problems += 1;
247
+ console.log(` ❌ ${msg}`);
248
+ if (fix)
249
+ console.log(` → ${fix}`);
250
+ };
251
+ const warn = (msg, fix) => {
252
+ console.log(` ⚠️ ${msg}`);
253
+ if (fix)
254
+ console.log(` → ${fix}`);
255
+ };
256
+ console.log('\n【运行时】');
257
+ const major = Number(process.versions.node.split('.')[0] ?? 0);
258
+ const minor = Number(process.versions.node.split('.')[1] ?? 0);
259
+ const nodeOk = major > 20 || (major === 20 && minor >= 11);
260
+ if (nodeOk)
261
+ ok(`Node ${process.versions.node}`);
262
+ else
263
+ bad(`Node ${process.versions.node} 过旧(需要 >= 20.11)`);
264
+ try {
265
+ const require = (await import('node:module')).createRequire(import.meta.url);
266
+ require('node:sqlite');
267
+ ok('node:sqlite 可用(跨平台历史注入正常)');
268
+ }
269
+ catch {
270
+ warn('node:sqlite 不可用(需要 Node >= 22.5)', '历史消息功能会退化为「不注入历史」,对话本身不受影响');
271
+ }
272
+ console.log('\n【浏览器】');
273
+ try {
274
+ const { firefox } = await import('playwright');
275
+ const exe = firefox.executablePath();
276
+ if (existsSync(exe)) {
277
+ ok(`Firefox 二进制已就绪: ${exe}`);
278
+ }
279
+ else {
280
+ bad('Firefox 二进制缺失(Playwright 不随包分发浏览器)', 'npx playwright install firefox');
281
+ }
282
+ }
283
+ catch (error) {
284
+ bad(`无法加载 playwright: ${String(error)}`, 'npm i playwright');
285
+ }
286
+ console.log('\n【凭证】');
287
+ let totalAccounts = 0;
288
+ for (const platform of PLATFORM_IDS) {
289
+ let accounts = [];
290
+ try {
291
+ accounts = listAccounts(platform);
292
+ }
293
+ catch {
294
+ accounts = [];
295
+ }
296
+ if (accounts.length === 0)
297
+ continue;
298
+ totalAccounts += accounts.length;
299
+ const detail = accounts
300
+ .map((acc) => {
301
+ const auth = loadAuth(platform, { accountId: acc });
302
+ const label = auth?.account_info?.label ?? auth?.account_info?.identifier ?? '未知';
303
+ const cookies = auth?.cookies?.length ?? 0;
304
+ return `${acc}(${label}, ${cookies} cookie)`;
305
+ })
306
+ .join(' ');
307
+ ok(`${platform}: ${detail}`);
308
+ }
309
+ if (totalAccounts === 0) {
310
+ bad('没有任何凭证(等于没登录,平台会一直回「请求过于频繁」)', 'webapi login chatglm');
311
+ }
312
+ else if (totalAccounts === 1) {
313
+ warn('只有 1 个账号 → 辅助通道不会启用,压缩/转换与主对话串行(功能正常,只是慢一些)', '再加一个账号:webapi login chatglm --account acc2');
314
+ }
315
+ console.log('\n【服务】');
316
+ // ⚠️ 用 `/status` 判「在不在跑」:它只读凭证文件,毫秒级返回。
317
+ // 不能用 `/health` —— 那个会启动浏览器整页探测,8s 超时下会把
318
+ // 「正在跑但探测很慢」误报成「没在跑」。
319
+ const alive = await fetchJson('/status', 6000);
320
+ if (!alive) {
321
+ console.log(' ℹ️ 服务未在运行(正常,如果你还没 webapi start)');
322
+ }
323
+ else {
324
+ ok(`服务在跑: http://${settings.apiHost}:${settings.apiPort}${settings.apiPrefix}`);
325
+ console.log(' 正在检查登录状态(会启动浏览器,请稍候)...');
326
+ const health = await fetchJson('/health', 120_000);
327
+ const providers = (health?.['providers'] ?? {});
328
+ for (const [name, info] of Object.entries(providers)) {
329
+ if (info['status'] === 'connected')
330
+ ok(`${name}: connected`);
331
+ else
332
+ warn(`${name}: ${info['status']}`, 'webapi login chatglm');
333
+ }
334
+ if (!health)
335
+ warn('登录状态查询超时(服务本身正常)', 'webapi health 可单独重试');
336
+ }
337
+ console.log('');
338
+ if (problems === 0) {
339
+ console.log('自检通过 ✅ 下一步: webapi start\n');
340
+ return 0;
341
+ }
342
+ console.log(`发现 ${problems} 个必须解决的问题(见上方 ❌)\n`);
343
+ return 1;
344
+ }
345
+ /* ==================================================================== */
346
+ /* 入口 */
347
+ /* ==================================================================== */
348
+ export async function runCli(argv = process.argv) {
349
+ const { command, positional, flags } = parseArgs(argv);
350
+ /* `webapi --version` / `-v`:发布后校验要用它(见 release 技能第 8 步)。
351
+ * ⚠️ `parseArgs` 会把 `--xxx` 收进 `flags`(`--help` 也因此变成 command='help'),
352
+ * 所以这里必须在 switch **之前**判 flags。 */
353
+ if (flags['version'] || flags['v']) {
354
+ const { settings } = await import('./config.js');
355
+ console.log(settings.appVersion);
356
+ return 0;
357
+ }
358
+ switch (command) {
359
+ case 'help':
360
+ case '--help':
361
+ case '-h':
362
+ console.log(helpText());
363
+ return 0;
364
+ case 'version': {
365
+ const { settings } = await import('./config.js');
366
+ console.log(settings.appVersion);
367
+ return 0;
368
+ }
369
+ case 'start': {
370
+ const { startServer } = await import('./server/app.js');
371
+ // startServer 返回退出码(0 正常 / 1 启动失败,如端口被占用)——
372
+ // 不能忽略它,否则失败时 CLI 仍以 0 退出,脚本会误判成功
373
+ return await startServer();
374
+ }
375
+ case 'status':
376
+ return cmdStatus();
377
+ case 'accounts':
378
+ return cmdAccounts();
379
+ case 'health':
380
+ return cmdHealth();
381
+ case 'doctor':
382
+ return cmdDoctor();
383
+ case 'login': {
384
+ const { runLogin } = await import('./browser/login-flow.js');
385
+ return runLogin({
386
+ platform: strFlag(flags, 'platform') ?? positional[0],
387
+ account: strFlag(flags, 'account'),
388
+ yes: boolFlag(flags, 'yes') || boolFlag(flags, 'y'),
389
+ noInput: boolFlag(flags, 'no-input'),
390
+ });
391
+ }
392
+ case 'logout': {
393
+ const { runLogout } = await import('./browser/login-flow.js');
394
+ return runLogout({
395
+ platform: strFlag(flags, 'platform') ?? positional[0],
396
+ account: strFlag(flags, 'account'),
397
+ yes: boolFlag(flags, 'yes') || boolFlag(flags, 'y'),
398
+ noInput: boolFlag(flags, 'no-input'),
399
+ });
400
+ }
401
+ default:
402
+ console.error(`未知命令: ${command}\n`);
403
+ console.log(helpText());
404
+ return 1;
405
+ }
406
+ }
407
+ /* `describeExistingAccounts` 已删除:它是死代码(全仓库只有定义、没有调用),
408
+ * 而且职责与 `login-flow.ts` 的 `readExistingAccounts()` 重复。
409
+ * 需要「列出账号及其身份」请用那个。 */
410
+ /**
411
+ * 本模块是否被**直接执行**(而不是被 import)。
412
+ *
413
+ * ⚠️ 不能用 `argv[1].endsWith('cli.js')` 这种写法:npm 安装后 bin 是**符号链接**
414
+ * (`node_modules/.bin/webapi` → `../webaiapi/dist/cli.js`),而 Node 的
415
+ * `process.argv[1]` 是**未经 realpath 解析**的路径 —— 名字对不上,
416
+ * CLI 会静默什么都不做(最难查的一类"装好了但没反应")。
417
+ * 这里对两边都做 realpath 再比。
418
+ */
419
+ function isDirectExecution() {
420
+ const entry = process.argv[1];
421
+ if (!entry)
422
+ return false;
423
+ try {
424
+ return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
425
+ }
426
+ catch {
427
+ return false;
428
+ }
429
+ }
430
+ // 直接执行时才跑(被 import 时不跑)
431
+ if (isDirectExecution()) {
432
+ runCli()
433
+ .then((code) => {
434
+ process.exitCode = code;
435
+ })
436
+ .catch((error) => {
437
+ logger.error(`CLI 异常: ${String(error)}`);
438
+ process.exitCode = 1;
439
+ });
440
+ }
441
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 配置管理 —— `src/utils/config.py` 的 TypeScript 移植。
3
+ *
4
+ * 与原实现的差异(都是有意为之,已在 `.codebuddy/memory` 记录):
5
+ *
6
+ * 1. **零依赖**:不再用 pydantic-settings,`.env` 与进程环境变量由本模块自己读
7
+ * (自带极简 dotenv 解析,约 30 行)。
8
+ * 2. **`DEBUG` 兼容非布尔字符串**:实测某些系统预置 `DEBUG=release`,pydantic 会
9
+ * 直接校验失败导致服务起不来。Python 侧只能靠 `apis.sh` 在 shell 层兜底,
10
+ * 这里在**代码层**兜底并告警 —— 换任何启动方式都不会再踩。
11
+ * 3. **砍掉从未生效的配置**(用 `grep settings\\.<name>` 逐个核对过使用点):
12
+ * - `redis_url` / `max_instances` / `max_requests_per_instance` /
13
+ * `health_check_interval` / `request_queue_max_size`:**0 处引用**
14
+ * - `browser_pool.*` / `user_data_dir`:只被 `browser/pool.py` 引用(该模块
15
+ * 只在 `shutdown_event` 里调一次 `clear_all()`,池从未被填充,重写时丢弃)
16
+ * - `accounts` / `platforms`:只被 `load_config(config_file)` 的 YAML 分支引用,
17
+ * 而该函数始终以无参调用 → YAML 分支从未执行
18
+ */
19
+ /** 浏览器池配置在 Python 版存在,但只被已废弃的 pool.py 使用,故不移植。 */
20
+ export interface Settings {
21
+ appName: string;
22
+ appVersion: string;
23
+ debug: boolean;
24
+ apiHost: string;
25
+ apiPort: number;
26
+ apiPrefix: string;
27
+ browserHeadless: boolean;
28
+ browserTimeout: number;
29
+ requestTimeout: number;
30
+ logLevel: string;
31
+ logDir: string;
32
+ debugLogs: boolean;
33
+ availableModels: string;
34
+ apiKeys: string;
35
+ apiKeyRequired: boolean;
36
+ rateLimitPerMinute: number;
37
+ rateLimitEnabled: boolean;
38
+ defaultWorkspacePaths: string;
39
+ /** 凭证目录(`~/.ai-api/auth`)。保留原始 `~`,取用时过 {@link expandHome}。 */
40
+ authDir: string;
41
+ }
42
+ /**
43
+ * 宽松布尔解析。
44
+ *
45
+ * Python 版的 `Settings.debug: bool` 遇到 `DEBUG=release` 会直接抛
46
+ * `ValidationError`,导致服务完全起不来(`apis.sh` 里那段 shell 兜底就是为此)。
47
+ * 这里改为:能识别的按值解析,识别不了的回退默认值并告警。
48
+ *
49
+ * @param raw 原始字符串;`undefined` 表示未设置
50
+ * @param key 用于告警的键名
51
+ * @param fallback 无法识别时的回退值
52
+ */
53
+ export declare function toBool(raw: string | undefined, key: string, fallback: boolean): boolean;
54
+ /** 整数解析;非法值回退默认并告警。 */
55
+ export declare function toInt(raw: string | undefined, key: string, fallback: number): number;
56
+ /**
57
+ * 极简 `.env` 解析(不覆盖已存在的进程环境变量)。
58
+ *
59
+ * 支持:`KEY=value`、`export KEY=value`、`#` 注释、单/双引号、值内含 `=`。
60
+ */
61
+ export declare function parseDotEnv(text: string): Record<string, string>;
62
+ /** 展开开头的 `~` 为用户主目录(Python 侧的 `os.path.expanduser`)。 */
63
+ export declare function expandHome(path: string): string;
64
+ export interface LoadSettingsOptions {
65
+ /** 显式给出的环境变量;缺省用 `process.env`。 */
66
+ env?: Record<string, string | undefined>;
67
+ /** `.env` 文件路径;传 `null` 表示不读文件,传 `undefined` 表示读 `.env`。 */
68
+ dotEnvPath?: string | null;
69
+ /** 读 `.env` 文件(注入依赖,便于测试)。 */
70
+ readFile?: (path: string) => string;
71
+ }
72
+ /**
73
+ * 组装配置。
74
+ *
75
+ * 优先级:`.env` 文件 < 进程环境变量 < 默认值(即环境变量覆盖 `.env`)。
76
+ */
77
+ export declare function loadSettings(options?: LoadSettingsOptions): Settings;
78
+ /** 全局配置单例(等价于 Python 的 `settings = load_config()`)。 */
79
+ export declare const settings: Settings;
80
+ /** 对外展示的模型列表(逗号分隔 → 数组),等价于 `get_available_models_list()`。 */
81
+ export declare function getAvailableModelsList(source?: Settings): string[];
82
+ /** API Key 集合,等价于 `get_api_keys()`。 */
83
+ export declare function getApiKeys(source?: Settings): Set<string>;
84
+ /**
85
+ * 默认工作目录列表,等价于 `get_default_workspace_paths()`。
86
+ *
87
+ * 只保留**实际存在**的目录;未配置时回退当前工作目录。
88
+ */
89
+ export declare function getDefaultWorkspacePaths(source?: Settings, exists?: (path: string) => boolean): string[];
90
+ //# sourceMappingURL=config.d.ts.map