@jsonstudio/rcc 0.89.2239 → 0.90.1

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 (237) hide show
  1. package/README.md +27 -0
  2. package/dist/build-info.js +2 -2
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli/commands/claude.js +1 -0
  5. package/dist/cli/commands/claude.js.map +1 -1
  6. package/dist/cli/commands/codex.js +1 -0
  7. package/dist/cli/commands/codex.js.map +1 -1
  8. package/dist/cli/commands/guardian-daemon.d.ts +2 -0
  9. package/dist/cli/commands/guardian-daemon.js +299 -0
  10. package/dist/cli/commands/guardian-daemon.js.map +1 -0
  11. package/dist/cli/commands/init/camoufox.js +1 -1
  12. package/dist/cli/commands/init/camoufox.js.map +1 -1
  13. package/dist/cli/commands/launcher/types.d.ts +6 -0
  14. package/dist/cli/commands/launcher-kernel.js +456 -109
  15. package/dist/cli/commands/launcher-kernel.js.map +1 -1
  16. package/dist/cli/commands/port.js +28 -8
  17. package/dist/cli/commands/port.js.map +1 -1
  18. package/dist/cli/commands/restart.d.ts +4 -0
  19. package/dist/cli/commands/restart.js +91 -42
  20. package/dist/cli/commands/restart.js.map +1 -1
  21. package/dist/cli/commands/start-types.d.ts +4 -0
  22. package/dist/cli/commands/start.js +108 -65
  23. package/dist/cli/commands/start.js.map +1 -1
  24. package/dist/cli/commands/stop.d.ts +3 -0
  25. package/dist/cli/commands/stop.js +30 -63
  26. package/dist/cli/commands/stop.js.map +1 -1
  27. package/dist/cli/config/init-provider-catalog.js +8 -3
  28. package/dist/cli/config/init-provider-catalog.js.map +1 -1
  29. package/dist/cli/guardian/client.d.ts +38 -0
  30. package/dist/cli/guardian/client.js +237 -0
  31. package/dist/cli/guardian/client.js.map +1 -0
  32. package/dist/cli/guardian/paths.d.ts +7 -0
  33. package/dist/cli/guardian/paths.js +13 -0
  34. package/dist/cli/guardian/paths.js.map +1 -0
  35. package/dist/cli/guardian/types.d.ts +30 -0
  36. package/dist/cli/guardian/types.js +2 -0
  37. package/dist/cli/guardian/types.js.map +1 -0
  38. package/dist/cli/register/guardian-daemon-command.d.ts +2 -0
  39. package/dist/cli/register/guardian-daemon-command.js +5 -0
  40. package/dist/cli/register/guardian-daemon-command.js.map +1 -0
  41. package/dist/cli/server/port-utils.js +57 -1
  42. package/dist/cli/server/port-utils.js.map +1 -1
  43. package/dist/cli.js +48 -0
  44. package/dist/cli.js.map +1 -1
  45. package/dist/commands/oauth.js +6 -6
  46. package/dist/commands/oauth.js.map +1 -1
  47. package/dist/config/routecodex-config-loader.js +66 -1
  48. package/dist/config/routecodex-config-loader.js.map +1 -1
  49. package/dist/config/virtual-router-builder.js +18 -0
  50. package/dist/config/virtual-router-builder.js.map +1 -1
  51. package/dist/config/virtual-router-types.js +20 -5
  52. package/dist/config/virtual-router-types.js.map +1 -1
  53. package/dist/daemon-admin-ui/assets/index-C8vP_c5E.js +15 -0
  54. package/dist/daemon-admin-ui/assets/index-DjIoHmNv.css +1 -0
  55. package/dist/daemon-admin-ui/index.html +13 -0
  56. package/dist/docs/daemon-admin-ui.html +328 -57
  57. package/dist/index.d.ts +9 -0
  58. package/dist/index.js +268 -10
  59. package/dist/index.js.map +1 -1
  60. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.d.ts +1 -0
  61. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js +36 -0
  62. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js.map +1 -1
  63. package/dist/manager/modules/quota/provider-quota-daemon.events.js +50 -1
  64. package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -1
  65. package/dist/providers/auth/antigravity-user-agent.js +78 -31
  66. package/dist/providers/auth/antigravity-user-agent.js.map +1 -1
  67. package/dist/providers/auth/gemini-cli-userinfo-helper.js +94 -63
  68. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
  69. package/dist/providers/auth/iflow-userinfo-helper.js +1 -1
  70. package/dist/providers/auth/iflow-userinfo-helper.js.map +1 -1
  71. package/dist/providers/auth/oauth-error-message.d.ts +1 -0
  72. package/dist/providers/auth/oauth-error-message.js +44 -0
  73. package/dist/providers/auth/oauth-error-message.js.map +1 -0
  74. package/dist/providers/auth/oauth-lifecycle/error-detection.js +42 -8
  75. package/dist/providers/auth/oauth-lifecycle/error-detection.js.map +1 -1
  76. package/dist/providers/auth/oauth-lifecycle/token-io.d.ts +1 -0
  77. package/dist/providers/auth/oauth-lifecycle/token-io.js +12 -0
  78. package/dist/providers/auth/oauth-lifecycle/token-io.js.map +1 -1
  79. package/dist/providers/auth/oauth-lifecycle.js +502 -87
  80. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  81. package/dist/providers/auth/oauth-repair-cooldown.js +2 -7
  82. package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
  83. package/dist/providers/auth/oauth-repair-env.js +3 -5
  84. package/dist/providers/auth/oauth-repair-env.js.map +1 -1
  85. package/dist/providers/auth/oauth-utils/error-extraction.js +42 -8
  86. package/dist/providers/auth/oauth-utils/error-extraction.js.map +1 -1
  87. package/dist/providers/core/config/camoufox-actions.d.ts +31 -0
  88. package/dist/providers/core/config/camoufox-actions.js +461 -0
  89. package/dist/providers/core/config/camoufox-actions.js.map +1 -0
  90. package/dist/providers/core/config/camoufox-launcher.d.ts +3 -0
  91. package/dist/providers/core/config/camoufox-launcher.js +518 -160
  92. package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
  93. package/dist/providers/core/config/oauth-flows.js +6 -44
  94. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  95. package/dist/providers/core/config/provider-oauth-configs.js +51 -7
  96. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  97. package/dist/providers/core/runtime/provider-error-classifier.js +32 -15
  98. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  99. package/dist/providers/core/runtime/provider-family-profile-utils.js +1 -1
  100. package/dist/providers/core/runtime/provider-family-profile-utils.js.map +1 -1
  101. package/dist/providers/core/runtime/provider-response-postprocessor.js +61 -14
  102. package/dist/providers/core/runtime/provider-response-postprocessor.js.map +1 -1
  103. package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
  104. package/dist/providers/core/strategies/oauth-auth-code-flow.js +124 -19
  105. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  106. package/dist/providers/core/strategies/oauth-device-flow.js +6 -3
  107. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  108. package/dist/providers/profile/families/iflow-profile.js +83 -10
  109. package/dist/providers/profile/families/iflow-profile.js.map +1 -1
  110. package/dist/scripts/camoufox/launch-auth.mjs +112 -5
  111. package/dist/server/handlers/config-admin-handler.js +9 -2
  112. package/dist/server/handlers/config-admin-handler.js.map +1 -1
  113. package/dist/server/handlers/handler-utils.js +3 -12
  114. package/dist/server/handlers/handler-utils.js.map +1 -1
  115. package/dist/server/handlers/logging.js +3 -4
  116. package/dist/server/handlers/logging.js.map +1 -1
  117. package/dist/server/runtime/http-server/clock-client-reaper.js +3 -26
  118. package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -1
  119. package/dist/server/runtime/http-server/clock-client-registry-utils.d.ts +4 -0
  120. package/dist/server/runtime/http-server/clock-client-registry-utils.js +74 -16
  121. package/dist/server/runtime/http-server/clock-client-registry-utils.js.map +1 -1
  122. package/dist/server/runtime/http-server/clock-client-registry.d.ts +15 -0
  123. package/dist/server/runtime/http-server/clock-client-registry.js +300 -6
  124. package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -1
  125. package/dist/server/runtime/http-server/clock-client-routes.js +49 -19
  126. package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -1
  127. package/dist/server/runtime/http-server/clock-daemon-log-throttle.d.ts +16 -0
  128. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js +49 -0
  129. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js.map +1 -1
  130. package/dist/server/runtime/http-server/clock-scope-resolution.d.ts +14 -0
  131. package/dist/server/runtime/http-server/clock-scope-resolution.js +212 -0
  132. package/dist/server/runtime/http-server/clock-scope-resolution.js.map +1 -0
  133. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +5 -3
  134. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
  135. package/dist/server/runtime/http-server/daemon-admin/control-handler.js +104 -15
  136. package/dist/server/runtime/http-server/daemon-admin/control-handler.js.map +1 -1
  137. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +2 -2
  138. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  139. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.d.ts +24 -0
  140. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js +316 -70
  141. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js.map +1 -1
  142. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +190 -1
  143. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  144. package/dist/server/runtime/http-server/daemon-admin/routing-policy.js +18 -29
  145. package/dist/server/runtime/http-server/daemon-admin/routing-policy.js.map +1 -1
  146. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +2 -0
  147. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
  148. package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +8 -1
  149. package/dist/server/runtime/http-server/daemon-admin-routes.js +30 -0
  150. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
  151. package/dist/server/runtime/http-server/executor/client-injection-flow.d.ts +14 -0
  152. package/dist/server/runtime/http-server/executor/client-injection-flow.js +287 -0
  153. package/dist/server/runtime/http-server/executor/client-injection-flow.js.map +1 -0
  154. package/dist/server/runtime/http-server/executor/index.d.ts +1 -1
  155. package/dist/server/runtime/http-server/executor/index.js +1 -1
  156. package/dist/server/runtime/http-server/executor/index.js.map +1 -1
  157. package/dist/server/runtime/http-server/executor/provider-response-converter.js +236 -62
  158. package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
  159. package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +1 -0
  160. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +12 -0
  161. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -1
  162. package/dist/server/runtime/http-server/executor/request-retry-helpers.js +16 -12
  163. package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -1
  164. package/dist/server/runtime/http-server/executor/sse-error-handler.d.ts +1 -0
  165. package/dist/server/runtime/http-server/executor/sse-error-handler.js +13 -2
  166. package/dist/server/runtime/http-server/executor/sse-error-handler.js.map +1 -1
  167. package/dist/server/runtime/http-server/executor/usage-aggregator.d.ts +0 -12
  168. package/dist/server/runtime/http-server/executor/usage-aggregator.js +84 -88
  169. package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
  170. package/dist/server/runtime/http-server/executor-metadata.js +328 -7
  171. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  172. package/dist/server/runtime/http-server/executor-response.d.ts +1 -0
  173. package/dist/server/runtime/http-server/executor-response.js +52 -58
  174. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  175. package/dist/server/runtime/http-server/http-server-bootstrap.js +50 -6
  176. package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
  177. package/dist/server/runtime/http-server/http-server-clock-daemon.d.ts +1 -0
  178. package/dist/server/runtime/http-server/http-server-clock-daemon.js +186 -44
  179. package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -1
  180. package/dist/server/runtime/http-server/http-server-lifecycle.js +4 -4
  181. package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
  182. package/dist/server/runtime/http-server/hub-shadow-compare.js +1 -1
  183. package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
  184. package/dist/server/runtime/http-server/index.d.ts +1 -0
  185. package/dist/server/runtime/http-server/index.js +1 -0
  186. package/dist/server/runtime/http-server/index.js.map +1 -1
  187. package/dist/server/runtime/http-server/middleware.js +82 -4
  188. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  189. package/dist/server/runtime/http-server/request-executor.js +6 -5
  190. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  191. package/dist/server/runtime/http-server/routes.d.ts +2 -1
  192. package/dist/server/runtime/http-server/routes.js +4 -2
  193. package/dist/server/runtime/http-server/routes.js.map +1 -1
  194. package/dist/server/runtime/http-server/session-dir.js +12 -1
  195. package/dist/server/runtime/http-server/session-dir.js.map +1 -1
  196. package/dist/server/runtime/http-server/stats-manager.d.ts +35 -0
  197. package/dist/server/runtime/http-server/stats-manager.js +269 -21
  198. package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
  199. package/dist/server/runtime/http-server/stopmessage-scope-rebind.d.ts +13 -0
  200. package/dist/server/runtime/http-server/stopmessage-scope-rebind.js +168 -0
  201. package/dist/server/runtime/http-server/stopmessage-scope-rebind.js.map +1 -0
  202. package/dist/server/runtime/http-server/tmux-session-probe.d.ts +10 -0
  203. package/dist/server/runtime/http-server/tmux-session-probe.js +97 -0
  204. package/dist/server/runtime/http-server/tmux-session-probe.js.map +1 -1
  205. package/dist/server-lifecycle/port-utils.d.ts +2 -1
  206. package/dist/server-lifecycle/port-utils.js +84 -4
  207. package/dist/server-lifecycle/port-utils.js.map +1 -1
  208. package/dist/token-daemon/index.d.ts +1 -0
  209. package/dist/token-daemon/index.js +17 -12
  210. package/dist/token-daemon/index.js.map +1 -1
  211. package/dist/utils/clock-client-token.d.ts +2 -1
  212. package/dist/utils/clock-client-token.js +52 -8
  213. package/dist/utils/clock-client-token.js.map +1 -1
  214. package/dist/utils/clock-scope-trace.d.ts +11 -0
  215. package/dist/utils/clock-scope-trace.js +41 -0
  216. package/dist/utils/clock-scope-trace.js.map +1 -0
  217. package/dist/utils/llms-engine-shadow.js +1 -1
  218. package/dist/utils/llms-engine-shadow.js.map +1 -1
  219. package/docs/DAEMON_CONTROL_PLANE.md +1 -0
  220. package/docs/ROUTING_POLICY_SCHEMA.md +4 -2
  221. package/docs/daemon-admin-ui.html +328 -57
  222. package/docs/design/servertool-stopmessage-lifecycle.md +109 -0
  223. package/docs/exec-command-guard-policy.example.v1.json +7 -1
  224. package/docs/providers/antigravity-gemini-provider-compat.md +2 -2
  225. package/package.json +21 -5
  226. package/scripts/build-core.mjs +12 -0
  227. package/scripts/camoufox/launch-auth.mjs +112 -5
  228. package/scripts/ci/repo-sanity.mjs +1 -0
  229. package/scripts/install-global.sh +6 -0
  230. package/scripts/install-verify.mjs +33 -16
  231. package/scripts/run-bg.sh +226 -43
  232. package/scripts/run-fg-gtimeout.sh +158 -14
  233. package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +3 -3
  234. package/scripts/tests/ci-jest.mjs +9 -1
  235. package/scripts/triage-errorsamples.mjs +216 -0
  236. package/scripts/verify-codex-error-samples.mjs +92 -15
  237. package/scripts/verify-install-e2e.mjs +57 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/rcc",
3
- "version": "0.89.2239",
3
+ "version": "0.90.001",
4
4
  "description": "Multi-provider OpenAI proxy server with anthropic/responses/chat support (release)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,9 +29,11 @@
29
29
  "node": ">=20 <26"
30
30
  },
31
31
  "scripts": {
32
- "build": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
33
- "build:dev": "bash -lc 'export BUILD_MODE=dev; npm run cleanup:stale-server-pids || true; trap \"npm run cleanup:stale-server-pids >/dev/null 2>&1 || true\" EXIT; npm run build && npm run fix:cli-permission && npm run test:unified-hub-shadow && npm run verify:e2e-toolcall && npm run verify:apply-patch && npm run verify:apply-patch-regressions && npm run verify:exec-command && npm run test:routing-instructions && npm run test:cli && npm run install:global && npm run mock:regressions && npm run test:blackbox-antigravity-parity && npm run verify:errorsamples && npm run verify:e2e-gemini-followup-sample'",
34
- "build:min": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
32
+ "build": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && npm run build:webui && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
33
+ "build:dev": "bash -lc 'export BUILD_MODE=dev ROUTECODEX_BUILD_RESTART_ONLY=1; npm run build && npm run fix:cli-permission && npm run install:global'",
34
+ "build:dev:full": "bash -lc 'export BUILD_MODE=dev ROUTECODEX_BUILD_RESTART_ONLY=1; npm run cleanup:stale-server-pids || true; trap \"npm run cleanup:stale-server-pids >/dev/null 2>&1 || true\" EXIT; npm run build && npm run fix:cli-permission && npm run test:unified-hub-shadow && npm run verify:e2e-toolcall && npm run verify:apply-patch && npm run verify:apply-patch-regressions && npm run verify:exec-command && npm run test:routing-instructions && npm run test:cli && npm run install:global && npm run mock:regressions && npm run test:blackbox-antigravity-parity && npm run verify:errorsamples && npm run verify:e2e-gemini-followup-sample'",
35
+ "build:min": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && npm run build:webui && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
36
+ "build:webui": "vite build --config webui/vite.config.ts",
35
37
  "prepack": "echo skip-prepack",
36
38
  "postbuild": "node scripts/ensure-cli-executable.mjs",
37
39
  "build:watch": "tsc --watch",
@@ -39,6 +41,7 @@
39
41
  "dev": "tsx watch src/index.ts",
40
42
  "jest:run": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
41
43
  "test": "npm run test:routing-instructions && npm run mock:regressions",
44
+ "test:regressions": "npm run test:unified-hub-shadow && npm run mock:regressions && npm run test:blackbox-antigravity-parity && npm run verify:errorsamples",
42
45
  "test:blackbox-antigravity-parity": "node scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs",
43
46
  "test:ci:jest": "node scripts/tests/ci-jest.mjs",
44
47
  "test:ci:jest:coverage": "node scripts/tests/ci-jest.mjs --coverage",
@@ -53,6 +56,8 @@
53
56
  "test:coverage": "npm run jest:run -- --coverage",
54
57
  "test:integration": "npm run jest:run -- --testPathPattern=integration",
55
58
  "test:e2e": "npm run jest:run -- --testPathPattern=e2e",
59
+ "test:webui": "npm run jest:run -- --runTestsByPath tests/frontend/webui-app.utils.spec.ts tests/frontend/webui-app.render.spec.tsx tests/frontend/webui-app.integration.spec.tsx tests/frontend/webui-app.pages.spec.tsx tests/frontend/webui-app.edge.spec.tsx",
60
+ "test:webui:coverage": "npm run jest:run -- --runTestsByPath tests/frontend/webui-app.utils.spec.ts tests/frontend/webui-app.render.spec.tsx tests/frontend/webui-app.integration.spec.tsx tests/frontend/webui-app.pages.spec.tsx tests/frontend/webui-app.edge.spec.tsx --coverage --collectCoverageFrom=webui/src/App.tsx",
56
61
  "test:performance": "npm run jest:run -- --testPathPattern=performance",
57
62
  "test:protocol": "npm run jest:run -- --testPathPattern=protocol-tools-e2e.spec.ts --runInBand --detectOpenHandles --forceExit",
58
63
  "test:dry-run": "node tests/basic-dry-run.mjs",
@@ -71,6 +76,7 @@
71
76
  "verify:apply-patch-regressions": "node scripts/verify-apply-patch-regressions.mjs",
72
77
  "verify:exec-command": "node scripts/tests/exec-command-loop.mjs",
73
78
  "verify:errorsamples": "node scripts/verify-codex-error-samples.mjs",
79
+ "triage:errorsamples": "node scripts/triage-errorsamples.mjs",
74
80
  "verify:e2e-gemini-followup-sample": "node scripts/verify-e2e-gemini-followup-sample.mjs",
75
81
  "install:global": "./scripts/install-global.sh",
76
82
  "install:release": "./scripts/install-release.sh",
@@ -188,28 +194,38 @@
188
194
  },
189
195
  "devDependencies": {
190
196
  "@jest/globals": "^30.1.2",
197
+ "@testing-library/jest-dom": "^6.9.1",
198
+ "@testing-library/react": "^16.3.2",
199
+ "@testing-library/user-event": "^14.6.1",
191
200
  "@types/cors": "^2.8.13",
192
201
  "@types/express": "^4.17.17",
193
202
  "@types/jest": "^29.5.5",
194
203
  "@types/node": "^20.19.25",
204
+ "@types/react": "^19.2.2",
205
+ "@types/react-dom": "^19.2.2",
195
206
  "@typescript-eslint/eslint-plugin": "^6.7.4",
196
207
  "@typescript-eslint/parser": "^6.7.4",
197
208
  "@typescript-eslint/typescript-estree": "^6.7.4",
209
+ "@vitejs/plugin-react": "^5.1.0",
198
210
  "complexity-report": "^2.0.0-alpha",
199
211
  "depcheck": "^1.4.7",
200
212
  "eslint": "^8.50.0",
201
213
  "eslint-plugin-complexity": "^1.0.2",
202
214
  "husky": "^9.0.0",
203
215
  "jest": "^29.7.0",
216
+ "jest-environment-jsdom": "^30.2.0",
204
217
  "jscpd": "^4.0.5",
205
218
  "lint-staged": "^15.0.0",
206
219
  "npm-check-updates": "^18.3.0",
207
220
  "package-json-validator": "^0.30.0",
208
221
  "prettier": "^3.6.2",
222
+ "react": "^19.2.0",
223
+ "react-dom": "^19.2.0",
209
224
  "ts-jest": "^29.1.1",
210
225
  "ts-node": "^10.9.2",
211
226
  "tsx": "^4.0.0",
212
- "typescript": "^5.9.3"
227
+ "typescript": "^5.9.3",
228
+ "vite": "^7.1.12"
213
229
  },
214
230
  "bundleDependencies": []
215
231
  }
@@ -10,6 +10,7 @@ const buildMode = buildModeRaw === 'dev' ? 'dev' : 'release';
10
10
  const tsc = path.join(root, 'node_modules', 'typescript', 'bin', 'tsc');
11
11
  const proj = path.join(root, 'sharedmodule', 'llmswitch-core', 'tsconfig.json');
12
12
  const coreRoot = path.join(root, 'sharedmodule', 'llmswitch-core');
13
+ const nativeBuildScript = path.join(coreRoot, 'scripts', 'build-native-hotpath.mjs');
13
14
  const outDir = path.join(coreRoot, 'dist');
14
15
  const requiredOutputs = [
15
16
  path.join(outDir, 'bridge', 'routecodex-adapter.js'),
@@ -75,6 +76,16 @@ function shouldSkipBuild() {
75
76
  return distMtime >= srcMtime;
76
77
  }
77
78
 
79
+ function runNativeBuild() {
80
+ if (!fs.existsSync(nativeBuildScript)) {
81
+ fail(`native build script missing: ${nativeBuildScript}`);
82
+ }
83
+ const res = spawnSync(process.execPath, [nativeBuildScript], { stdio: 'inherit', cwd: coreRoot });
84
+ if ((res.status ?? 0) !== 0) {
85
+ fail('native build failed for llmswitch-core');
86
+ }
87
+ }
88
+
78
89
  if (!fs.existsSync(tsc)) fail('TypeScript not installed in root node_modules. Run npm i.');
79
90
  if (!fs.existsSync(proj)) {
80
91
  console.log('[build-core] llmswitch-core source not found under sharedmodule; skip local core build (依赖包将用于运行/打包)');
@@ -92,6 +103,7 @@ if (skip === '1' || skip === 'true' || skip === 'yes') {
92
103
  console.log('[build-core] skip requested by env (ROUTECODEX_SKIP_CORE_BUILD/SKIP_CORE_BUILD)');
93
104
  process.exit(0);
94
105
  }
106
+ runNativeBuild();
95
107
  if (shouldSkipBuild()) {
96
108
  console.log('[build-core] dist up-to-date; skip rebuild:', outDir);
97
109
  process.exit(0);
@@ -13,6 +13,30 @@ function isTruthy(value) {
13
13
  return v === '1' || v === 'true' || v === 'yes' || v === 'on';
14
14
  }
15
15
 
16
+ function isFalsy(value) {
17
+ if (!value) return false;
18
+ const v = String(value).trim().toLowerCase();
19
+ return v === '0' || v === 'false' || v === 'no' || v === 'off';
20
+ }
21
+
22
+ function resolveIflowHeadlessMode(devMode) {
23
+ if (devMode) {
24
+ return false;
25
+ }
26
+ const raw = String(
27
+ process.env.ROUTECODEX_CAMOUFOX_IFLOW_HEADLESS ||
28
+ process.env.RCC_CAMOUFOX_IFLOW_HEADLESS ||
29
+ ''
30
+ ).trim();
31
+ if (!raw) {
32
+ return false;
33
+ }
34
+ if (isFalsy(raw)) {
35
+ return false;
36
+ }
37
+ return isTruthy(raw);
38
+ }
39
+
16
40
  function parseArgs(argv) {
17
41
  const args = { profile: 'default', url: '', autoMode: '', devMode: false };
18
42
  const list = argv.slice(2);
@@ -260,13 +284,44 @@ function cleanupExistingCamoufox(profileDir) {
260
284
  if (!profileDir) {
261
285
  return;
262
286
  }
263
- console.log('[camoufox-launch-auth] Ensuring Camoufox profile is clean before launch...');
264
- const pkillArgs = ['-f', profileDir];
287
+ // 清理已知进程,不使用 pkill 普杀
288
+ // 使用 pgrep 查找匹配的进程,然后逐个验证后终止
265
289
  try {
266
- spawnSync('pkill', pkillArgs, { stdio: 'ignore' });
290
+ const probe = spawnSync('pgrep', ['-f', profileDir], {
291
+ encoding: 'utf8',
292
+ stdio: ['ignore', 'pipe', 'ignore']
293
+ });
294
+
295
+ if (probe.status === 0 && probe.stdout) {
296
+ const selfPid = process.pid;
297
+ const lines = String(probe.stdout).split(/\r?\n/).filter(Boolean);
298
+
299
+ for (const line of lines) {
300
+ const pid = Number.parseInt(line.trim(), 10);
301
+ if (!Number.isFinite(pid) || pid <= 0 || pid === selfPid) {
302
+ continue;
303
+ }
304
+ // 验证进程命令包含 camoufox
305
+ try {
306
+ const cmdProbe = spawnSync('ps', ['-p', String(pid), '-o', 'command='], { encoding: 'utf8' });
307
+ const cmd = String(cmdProbe.stdout || '').toLowerCase();
308
+ if (cmd.includes('camoufox')) {
309
+ console.log(`[camoufox-launch-auth] Stopping known Camoufox process PID ${pid}`);
310
+ try {
311
+ process.kill(pid, 'SIGTERM');
312
+ } catch {
313
+ // 忽略终止失败
314
+ }
315
+ }
316
+ } catch {
317
+ // 忽略 ps 查询失败
318
+ }
319
+ }
320
+ }
267
321
  } catch {
268
- // pkill may not exist; ignore
322
+ // pgrep 可能不存在,忽略
269
323
  }
324
+ console.log('[camoufox-launch-auth] Ensuring Camoufox profile is clean before launch...');
270
325
  const lockNames = ['.parentlock', 'parent.lock', 'lock'];
271
326
  for (const name of lockNames) {
272
327
  const target = path.join(profileDir, name);
@@ -754,7 +809,9 @@ async function runIflowAutoFlow({ url, profileDir, profileId, camoufoxBinary, de
754
809
  );
755
810
  }
756
811
 
757
- const headless = !devMode;
812
+ // iFlow OAuth has poor reliability under headless mode (often returns anti-bot/garbled pages).
813
+ // Keep default headed to match camo CLI behavior; allow opt-in headless via env.
814
+ const headless = resolveIflowHeadlessMode(devMode);
758
815
  const timeoutMs = Number(process.env.ROUTECODEX_CAMOUFOX_IFLOW_TIMEOUT_MS || 300_000);
759
816
  console.log(`[camoufox-launch-auth] Launching Camoufox in ${headless ? 'headless' : 'headed'} mode...`);
760
817
  cleanupExistingCamoufox(profileDir);
@@ -1172,6 +1229,8 @@ async function runQwenAutoFlow({ url, profileDir, camoufoxBinary, devMode }) {
1172
1229
  }
1173
1230
 
1174
1231
  const timeoutMs = Number(process.env.ROUTECODEX_CAMOUFOX_QWEN_TIMEOUT_MS || 120_000);
1232
+ const accountPreference = (process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT || '').trim();
1233
+ const accountWaitMs = Number(process.env.ROUTECODEX_CAMOUFOX_QWEN_ACCOUNT_TIMEOUT_MS || 15_000);
1175
1234
  cleanupExistingCamoufox(profileDir);
1176
1235
  const context = await firefox.launchPersistentContext(profileDir, {
1177
1236
  executablePath: camoufoxBinary,
@@ -1231,6 +1290,54 @@ async function runQwenAutoFlow({ url, profileDir, camoufoxBinary, devMode }) {
1231
1290
  await authPage.waitForLoadState('domcontentloaded', { timeout: pageLoadTimeoutMs }).catch(() => {});
1232
1291
  }
1233
1292
 
1293
+ const qwenAccountSelectors = [
1294
+ 'div.yAlK0b[jsname="bQIQze"]',
1295
+ 'div.pGzURd[jsname="V1ur5d"]',
1296
+ 'span.accountName--ZKlffRBc',
1297
+ 'span[class^="accountName--"]',
1298
+ 'span[class*="accountName--"]',
1299
+ '.account-item',
1300
+ '[class*="account-item"]',
1301
+ '[class*="accountItem"]',
1302
+ '[data-testid*="account"]',
1303
+ '[data-test*="account"]'
1304
+ ];
1305
+ const accountResult = await waitForAnyElementInPages(context, qwenAccountSelectors, accountWaitMs);
1306
+ if (accountResult) {
1307
+ let targetAccount = accountResult.locator.first();
1308
+ if (accountPreference) {
1309
+ const preferred = accountResult.locator.filter({ hasText: accountPreference });
1310
+ if (await preferred.count()) {
1311
+ console.log(`[camoufox-launch-auth] Qwen selecting account matching preference "${accountPreference}"`);
1312
+ targetAccount = preferred.first();
1313
+ } else {
1314
+ console.warn(
1315
+ `[camoufox-launch-auth] Qwen preferred account text "${accountPreference}" not found; falling back to first account`
1316
+ );
1317
+ }
1318
+ }
1319
+ await targetAccount.scrollIntoViewIfNeeded().catch(() => {});
1320
+ await targetAccount.hover({ force: true }).catch(() => {});
1321
+ const handle = await targetAccount.elementHandle();
1322
+ const accountText = await targetAccount.innerText().catch(() => '');
1323
+ if (handle) {
1324
+ console.log(
1325
+ `[camoufox-launch-auth] Qwen account element detected (${accountText || 'unknown label'}), clicking...`
1326
+ );
1327
+ await accountResult.page.evaluate((el) => {
1328
+ const events = ['mouseenter', 'mouseover', 'mousemove', 'mousedown', 'mouseup', 'click'];
1329
+ for (const type of events) {
1330
+ el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
1331
+ }
1332
+ }, handle);
1333
+ } else {
1334
+ await targetAccount.click({ timeout: Math.min(timeoutMs, 10_000) }).catch(() => {});
1335
+ }
1336
+ await accountResult.page.waitForLoadState('domcontentloaded', { timeout: Math.min(timeoutMs, 15_000) }).catch(() => {});
1337
+ } else {
1338
+ console.log('[camoufox-launch-auth] Qwen account selector not detected; proceeding to confirm step...');
1339
+ }
1340
+
1234
1341
  console.log('[camoufox-launch-auth] Qwen authorize page loaded, waiting for confirm button...');
1235
1342
  const confirmSelector = 'button.qwen-confirm-btn';
1236
1343
  const confirmResult = await waitForElementInPages(context, confirmSelector, timeoutMs);
@@ -91,6 +91,7 @@ function checkRootLayout() {
91
91
  'task.md',
92
92
  'tests',
93
93
  'tmp',
94
+ 'tsconfig.jest.json',
94
95
  'tsconfig.json',
95
96
  'vendor',
96
97
  ]);
@@ -196,6 +196,12 @@ verify_install() {
196
196
  }
197
197
 
198
198
  verify_server_health() {
199
+ if [ "${ROUTECODEX_INSTALL_SKIP_E2E:-0}" = "1" ]; then
200
+ echo ""
201
+ echo "⏭️ 已跳过全局 CLI 端到端检查(ROUTECODEX_INSTALL_SKIP_E2E=1)"
202
+ return
203
+ fi
204
+
199
205
  local HEALTH_LOG="/tmp/routecodex-install-health-$(date +%s).log"
200
206
  echo ""
201
207
  echo "🩺 执行服务器健康&端到端检查 (chat + anthropic SSE)..."
@@ -25,6 +25,7 @@ const state = {
25
25
  logStream: null,
26
26
  logPath: '',
27
27
  baseUrl: '',
28
+ ownsServer: false,
28
29
  };
29
30
  let shuttingDown = false;
30
31
  let responsesSseParser = null;
@@ -791,9 +792,12 @@ async function verifyChatStreaming(baseUrl, model, timeoutMs, samplePayload, cha
791
792
  async function stopServer() {
792
793
  const proc = state.serverProc;
793
794
  const baseUrl = state.baseUrl;
794
- if (baseUrl) {
795
+ if (state.ownsServer && baseUrl) {
795
796
  try { await fetch(`${baseUrl}/shutdown`, { method: 'POST' }).catch(() => {}); } catch { /* ignore */ }
796
797
  }
798
+ if (!state.ownsServer) {
799
+ return;
800
+ }
797
801
  if (!proc) {
798
802
  if (state.logStream) {
799
803
  try { state.logStream.end(); } catch { /* ignore */ }
@@ -903,18 +907,23 @@ async function main() {
903
907
 
904
908
  const model = resolveModel(config);
905
909
  console.log(`🔁 模型: ${model}, 端口: ${port}`);
910
+ const buildRestartOnly = (() => {
911
+ const raw = String(process.env.ROUTECODEX_BUILD_RESTART_ONLY ?? process.env.RCC_BUILD_RESTART_ONLY ?? '').trim().toLowerCase();
912
+ return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
913
+ })();
906
914
 
907
915
  const listeners = detectPortPids(port);
908
- if (listeners.length) {
916
+ const reuseExistingServer = buildRestartOnly && listeners.length > 0;
917
+ if (listeners.length && !reuseExistingServer) {
909
918
  throw new Error(`端口 ${port} 已被使用 (PID: ${listeners.join(', ')}). 请先停止正在运行的 RouteCodex 实例再重试。`);
910
919
  }
920
+ if (reuseExistingServer) {
921
+ console.log(`ℹ build-restart-only: 检测到已运行服务 (PID: ${listeners.join(', ')}),复用现有实例进行验证。`);
922
+ }
911
923
 
912
924
  console.log('🛠️ 动态生成最新的 pipeline 配置...');
913
925
  regeneratePipelineConfig({ port, configPath });
914
926
 
915
- state.logPath = path.join(os.tmpdir(), `routecodex-install-verify-${Date.now()}.log`);
916
- const logStream = fs.createWriteStream(state.logPath, { flags: 'a' });
917
-
918
927
  let command;
919
928
  let commandArgs;
920
929
  const env = { ...process.env };
@@ -933,17 +942,25 @@ async function main() {
933
942
  env.RCC_PORT = String(port);
934
943
  }
935
944
 
936
- console.log(`🚀 启动 RouteCodex server... (launcher=${launcher === 'cli' ? command : 'node dist/index.js'})`);
937
- const serverProc = spawn(command, commandArgs, {
938
- cwd,
939
- env,
940
- stdio: ['ignore', 'pipe', 'pipe'],
941
- detached: false,
942
- });
943
- serverProc.stdout.pipe(logStream, { end: false });
944
- serverProc.stderr.pipe(logStream, { end: false });
945
- state.serverProc = serverProc;
946
- state.logStream = logStream;
945
+ if (!reuseExistingServer) {
946
+ state.logPath = path.join(os.tmpdir(), `routecodex-install-verify-${Date.now()}.log`);
947
+ const logStream = fs.createWriteStream(state.logPath, { flags: 'a' });
948
+ state.ownsServer = true;
949
+ console.log(`🚀 启动 RouteCodex server... (launcher=${launcher === 'cli' ? command : 'node dist/index.js'})`);
950
+ const serverProc = spawn(command, commandArgs, {
951
+ cwd,
952
+ env,
953
+ stdio: ['ignore', 'pipe', 'pipe'],
954
+ detached: false,
955
+ });
956
+ serverProc.stdout.pipe(logStream, { end: false });
957
+ serverProc.stderr.pipe(logStream, { end: false });
958
+ state.serverProc = serverProc;
959
+ state.logStream = logStream;
960
+ } else {
961
+ state.ownsServer = false;
962
+ state.serverProc = null;
963
+ }
947
964
 
948
965
  await waitForHealth(baseUrl, 90000);
949
966
  console.log('✅ server 健康检查通过');