@jsonstudio/rcc 0.89.2202 → 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.
- package/README.md +27 -0
- package/dist/build-info.js +2 -2
- package/dist/build-info.js.map +1 -1
- package/dist/cli/commands/claude.js +1 -0
- package/dist/cli/commands/claude.js.map +1 -1
- package/dist/cli/commands/codex.js +1 -0
- package/dist/cli/commands/codex.js.map +1 -1
- package/dist/cli/commands/guardian-daemon.d.ts +2 -0
- package/dist/cli/commands/guardian-daemon.js +299 -0
- package/dist/cli/commands/guardian-daemon.js.map +1 -0
- package/dist/cli/commands/init/camoufox.js +1 -1
- package/dist/cli/commands/init/camoufox.js.map +1 -1
- package/dist/cli/commands/init.js +15 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/launcher/types.d.ts +6 -0
- package/dist/cli/commands/launcher-kernel.js +456 -109
- package/dist/cli/commands/launcher-kernel.js.map +1 -1
- package/dist/cli/commands/port.js +28 -8
- package/dist/cli/commands/port.js.map +1 -1
- package/dist/cli/commands/restart.d.ts +4 -0
- package/dist/cli/commands/restart.js +91 -42
- package/dist/cli/commands/restart.js.map +1 -1
- package/dist/cli/commands/start-types.d.ts +4 -0
- package/dist/cli/commands/start.js +112 -68
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.js +30 -63
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/init-config.js +15 -1
- package/dist/cli/config/init-config.js.map +1 -1
- package/dist/cli/config/init-provider-catalog.js +13 -5
- package/dist/cli/config/init-provider-catalog.js.map +1 -1
- package/dist/cli/guardian/client.d.ts +38 -0
- package/dist/cli/guardian/client.js +237 -0
- package/dist/cli/guardian/client.js.map +1 -0
- package/dist/cli/guardian/paths.d.ts +7 -0
- package/dist/cli/guardian/paths.js +13 -0
- package/dist/cli/guardian/paths.js.map +1 -0
- package/dist/cli/guardian/types.d.ts +30 -0
- package/dist/cli/guardian/types.js +2 -0
- package/dist/cli/guardian/types.js.map +1 -0
- package/dist/cli/register/guardian-daemon-command.d.ts +2 -0
- package/dist/cli/register/guardian-daemon-command.js +5 -0
- package/dist/cli/register/guardian-daemon-command.js.map +1 -0
- package/dist/cli/server/port-utils.js +57 -1
- package/dist/cli/server/port-utils.js.map +1 -1
- package/dist/cli.js +48 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/oauth.js +6 -6
- package/dist/commands/oauth.js.map +1 -1
- package/dist/commands/provider-update.js +12 -0
- package/dist/commands/provider-update.js.map +1 -1
- package/dist/config/routecodex-config-loader.js +66 -1
- package/dist/config/routecodex-config-loader.js.map +1 -1
- package/dist/config/virtual-router-builder.js +18 -0
- package/dist/config/virtual-router-builder.js.map +1 -1
- package/dist/config/virtual-router-types.js +20 -5
- package/dist/config/virtual-router-types.js.map +1 -1
- package/dist/daemon-admin-ui/assets/index-C8vP_c5E.js +15 -0
- package/dist/daemon-admin-ui/assets/index-DjIoHmNv.css +1 -0
- package/dist/daemon-admin-ui/index.html +13 -0
- package/dist/docs/daemon-admin-ui.html +328 -57
- package/dist/index.d.ts +9 -0
- package/dist/index.js +268 -10
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.d.ts +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js +36 -0
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js.map +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.events.js +50 -1
- package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -1
- package/dist/providers/auth/antigravity-user-agent.js +78 -31
- package/dist/providers/auth/antigravity-user-agent.js.map +1 -1
- package/dist/providers/auth/gemini-cli-userinfo-helper.js +94 -63
- package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/iflow-userinfo-helper.js +1 -1
- package/dist/providers/auth/iflow-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/oauth-error-message.d.ts +1 -0
- package/dist/providers/auth/oauth-error-message.js +44 -0
- package/dist/providers/auth/oauth-error-message.js.map +1 -0
- package/dist/providers/auth/oauth-lifecycle/error-detection.js +42 -8
- package/dist/providers/auth/oauth-lifecycle/error-detection.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle/token-io.d.ts +1 -0
- package/dist/providers/auth/oauth-lifecycle/token-io.js +12 -0
- package/dist/providers/auth/oauth-lifecycle/token-io.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +502 -87
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/oauth-repair-cooldown.js +2 -7
- package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
- package/dist/providers/auth/oauth-repair-env.js +3 -5
- package/dist/providers/auth/oauth-repair-env.js.map +1 -1
- package/dist/providers/auth/oauth-utils/error-extraction.js +42 -8
- package/dist/providers/auth/oauth-utils/error-extraction.js.map +1 -1
- package/dist/providers/auth/qwen-userinfo-helper.js +18 -3
- package/dist/providers/auth/qwen-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/tokenfile-auth.d.ts +1 -0
- package/dist/providers/auth/tokenfile-auth.js +15 -9
- package/dist/providers/auth/tokenfile-auth.js.map +1 -1
- package/dist/providers/core/config/camoufox-actions.d.ts +31 -0
- package/dist/providers/core/config/camoufox-actions.js +461 -0
- package/dist/providers/core/config/camoufox-actions.js.map +1 -0
- package/dist/providers/core/config/camoufox-launcher.d.ts +3 -0
- package/dist/providers/core/config/camoufox-launcher.js +518 -160
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
- package/dist/providers/core/config/oauth-flows.js +6 -44
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.js +51 -7
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +13 -4
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +1 -0
- package/dist/providers/core/runtime/http-transport-provider.js +60 -1
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.js +32 -15
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/provider-family-profile-utils.js +1 -1
- package/dist/providers/core/runtime/provider-family-profile-utils.js.map +1 -1
- package/dist/providers/core/runtime/provider-response-postprocessor.js +61 -14
- package/dist/providers/core/runtime/provider-response-postprocessor.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +124 -19
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/strategies/oauth-device-flow.js +6 -3
- package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
- package/dist/providers/profile/families/iflow-profile.js +83 -10
- package/dist/providers/profile/families/iflow-profile.js.map +1 -1
- package/dist/providers/profile/families/qwen-profile.js +203 -0
- package/dist/providers/profile/families/qwen-profile.js.map +1 -1
- package/dist/scripts/camoufox/launch-auth.mjs +112 -5
- package/dist/server/handlers/config-admin-handler.js +9 -2
- package/dist/server/handlers/config-admin-handler.js.map +1 -1
- package/dist/server/handlers/handler-utils.js +3 -14
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/logging.js +3 -4
- package/dist/server/handlers/logging.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-reaper.d.ts +1 -0
- package/dist/server/runtime/http-server/clock-client-reaper.js +21 -15
- package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-registry-utils.d.ts +4 -0
- package/dist/server/runtime/http-server/clock-client-registry-utils.js +74 -16
- package/dist/server/runtime/http-server/clock-client-registry-utils.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-registry.d.ts +15 -0
- package/dist/server/runtime/http-server/clock-client-registry.js +300 -6
- package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-routes.js +49 -19
- package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -1
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.d.ts +16 -0
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.js +49 -0
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.js.map +1 -1
- package/dist/server/runtime/http-server/clock-scope-resolution.d.ts +14 -0
- package/dist/server/runtime/http-server/clock-scope-resolution.js +212 -0
- package/dist/server/runtime/http-server/clock-scope-resolution.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +5 -3
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/control-handler.js +104 -15
- package/dist/server/runtime/http-server/daemon-admin/control-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +2 -2
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.d.ts +24 -0
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js +316 -70
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +190 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/routing-policy.js +18 -29
- package/dist/server/runtime/http-server/daemon-admin/routing-policy.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +2 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +8 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.js +30 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor/client-injection-flow.d.ts +14 -0
- package/dist/server/runtime/http-server/executor/client-injection-flow.js +287 -0
- package/dist/server/runtime/http-server/executor/client-injection-flow.js.map +1 -0
- package/dist/server/runtime/http-server/executor/index.d.ts +1 -1
- package/dist/server/runtime/http-server/executor/index.js +1 -1
- package/dist/server/runtime/http-server/executor/index.js.map +1 -1
- package/dist/server/runtime/http-server/executor/provider-response-converter.js +236 -62
- package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
- package/dist/server/runtime/http-server/executor/provider-response-utils.js +5 -0
- package/dist/server/runtime/http-server/executor/provider-response-utils.js.map +1 -1
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +2 -0
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +60 -0
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -1
- package/dist/server/runtime/http-server/executor/request-retry-helpers.js +20 -8
- package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -1
- package/dist/server/runtime/http-server/executor/sse-error-handler.d.ts +1 -0
- package/dist/server/runtime/http-server/executor/sse-error-handler.js +13 -2
- package/dist/server/runtime/http-server/executor/sse-error-handler.js.map +1 -1
- package/dist/server/runtime/http-server/executor/usage-aggregator.d.ts +0 -12
- package/dist/server/runtime/http-server/executor/usage-aggregator.js +84 -88
- package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +328 -7
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.d.ts +1 -0
- package/dist/server/runtime/http-server/executor-response.js +52 -50
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-bootstrap.js +55 -6
- package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-clock-daemon.d.ts +1 -0
- package/dist/server/runtime/http-server/http-server-clock-daemon.js +199 -44
- package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-lifecycle.js +4 -4
- package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +1 -0
- package/dist/server/runtime/http-server/index.js +1 -0
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.js +82 -4
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +26 -7
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +2 -1
- package/dist/server/runtime/http-server/routes.js +4 -2
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/session-dir.js +12 -1
- package/dist/server/runtime/http-server/session-dir.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +35 -0
- package/dist/server/runtime/http-server/stats-manager.js +269 -21
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.d.ts +13 -0
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.js +168 -0
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.js.map +1 -0
- package/dist/server/runtime/http-server/tmux-session-probe.d.ts +10 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js +97 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js.map +1 -1
- package/dist/server-lifecycle/port-utils.d.ts +2 -1
- package/dist/server-lifecycle/port-utils.js +84 -4
- package/dist/server-lifecycle/port-utils.js.map +1 -1
- package/dist/token-daemon/index.d.ts +1 -0
- package/dist/token-daemon/index.js +17 -12
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/utils/clock-client-token.d.ts +2 -1
- package/dist/utils/clock-client-token.js +52 -8
- package/dist/utils/clock-client-token.js.map +1 -1
- package/dist/utils/clock-scope-trace.d.ts +11 -0
- package/dist/utils/clock-scope-trace.js +41 -0
- package/dist/utils/clock-scope-trace.js.map +1 -0
- package/dist/utils/llms-engine-shadow.js +1 -1
- package/dist/utils/llms-engine-shadow.js.map +1 -1
- package/docs/DAEMON_CONTROL_PLANE.md +1 -0
- package/docs/ROUTING_POLICY_SCHEMA.md +4 -2
- package/docs/daemon-admin-ui.html +328 -57
- package/docs/design/servertool-stopmessage-lifecycle.md +109 -0
- package/docs/exec-command-guard-policy.example.v1.json +7 -1
- package/docs/providers/antigravity-gemini-provider-compat.md +2 -2
- package/package.json +23 -6
- package/scripts/build-core.mjs +12 -0
- package/scripts/camoufox/launch-auth.mjs +112 -5
- package/scripts/ci/repo-sanity.mjs +1 -0
- package/scripts/cleanup-stale-server-pids.mjs +142 -0
- package/scripts/install-global.sh +8 -0
- package/scripts/install-verify.mjs +33 -16
- package/scripts/run-bg.sh +226 -43
- package/scripts/run-fg-gtimeout.sh +158 -14
- package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +3 -3
- package/scripts/tests/ci-jest.mjs +9 -1
- package/scripts/triage-errorsamples.mjs +216 -0
- package/scripts/verify-codex-error-samples.mjs +92 -15
- package/scripts/verify-e2e-toolcall.mjs +12 -1
- package/scripts/verify-install-e2e.mjs +69 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonstudio/rcc",
|
|
3
|
-
"version": "0.
|
|
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 build && npm run fix:cli-permission && npm run
|
|
34
|
-
"build:
|
|
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",
|
|
@@ -63,6 +68,7 @@
|
|
|
63
68
|
"lint:fix": "ESLINT_USE_FLAT_CONFIG=1 eslint \"src/**/*.ts\" --fix --no-cache",
|
|
64
69
|
"lint:strict": "ESLINT_USE_FLAT_CONFIG=1 eslint \"src/**/*.ts\" --max-warnings 0 --no-cache",
|
|
65
70
|
"clean": "rm -rf dist coverage",
|
|
71
|
+
"cleanup:stale-server-pids": "node scripts/cleanup-stale-server-pids.mjs --quiet",
|
|
66
72
|
"prebuild": "npm run verify:repo-sanity && echo skip-lint",
|
|
67
73
|
"prepare": "",
|
|
68
74
|
"postinstall": "node scripts/ensure-cli-executable.mjs",
|
|
@@ -70,6 +76,7 @@
|
|
|
70
76
|
"verify:apply-patch-regressions": "node scripts/verify-apply-patch-regressions.mjs",
|
|
71
77
|
"verify:exec-command": "node scripts/tests/exec-command-loop.mjs",
|
|
72
78
|
"verify:errorsamples": "node scripts/verify-codex-error-samples.mjs",
|
|
79
|
+
"triage:errorsamples": "node scripts/triage-errorsamples.mjs",
|
|
73
80
|
"verify:e2e-gemini-followup-sample": "node scripts/verify-e2e-gemini-followup-sample.mjs",
|
|
74
81
|
"install:global": "./scripts/install-global.sh",
|
|
75
82
|
"install:release": "./scripts/install-release.sh",
|
|
@@ -150,7 +157,7 @@
|
|
|
150
157
|
},
|
|
151
158
|
"dependencies": {
|
|
152
159
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
153
|
-
"@jsonstudio/llms": "^0.6.
|
|
160
|
+
"@jsonstudio/llms": "^0.6.2172",
|
|
154
161
|
"@lmstudio/sdk": "^1.5.0",
|
|
155
162
|
"@radix-ui/react-switch": "^1.2.6",
|
|
156
163
|
"@types/socket.io": "^3.0.1",
|
|
@@ -187,28 +194,38 @@
|
|
|
187
194
|
},
|
|
188
195
|
"devDependencies": {
|
|
189
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",
|
|
190
200
|
"@types/cors": "^2.8.13",
|
|
191
201
|
"@types/express": "^4.17.17",
|
|
192
202
|
"@types/jest": "^29.5.5",
|
|
193
203
|
"@types/node": "^20.19.25",
|
|
204
|
+
"@types/react": "^19.2.2",
|
|
205
|
+
"@types/react-dom": "^19.2.2",
|
|
194
206
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
195
207
|
"@typescript-eslint/parser": "^6.7.4",
|
|
196
208
|
"@typescript-eslint/typescript-estree": "^6.7.4",
|
|
209
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
197
210
|
"complexity-report": "^2.0.0-alpha",
|
|
198
211
|
"depcheck": "^1.4.7",
|
|
199
212
|
"eslint": "^8.50.0",
|
|
200
213
|
"eslint-plugin-complexity": "^1.0.2",
|
|
201
214
|
"husky": "^9.0.0",
|
|
202
215
|
"jest": "^29.7.0",
|
|
216
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
203
217
|
"jscpd": "^4.0.5",
|
|
204
218
|
"lint-staged": "^15.0.0",
|
|
205
219
|
"npm-check-updates": "^18.3.0",
|
|
206
220
|
"package-json-validator": "^0.30.0",
|
|
207
221
|
"prettier": "^3.6.2",
|
|
222
|
+
"react": "^19.2.0",
|
|
223
|
+
"react-dom": "^19.2.0",
|
|
208
224
|
"ts-jest": "^29.1.1",
|
|
209
225
|
"ts-node": "^10.9.2",
|
|
210
226
|
"tsx": "^4.0.0",
|
|
211
|
-
"typescript": "^5.9.3"
|
|
227
|
+
"typescript": "^5.9.3",
|
|
228
|
+
"vite": "^7.1.12"
|
|
212
229
|
},
|
|
213
230
|
"bundleDependencies": []
|
|
214
231
|
}
|
package/scripts/build-core.mjs
CHANGED
|
@@ -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
|
-
|
|
264
|
-
|
|
287
|
+
// 清理已知进程,不使用 pkill 普杀
|
|
288
|
+
// 使用 pgrep 查找匹配的进程,然后逐个验证后终止
|
|
265
289
|
try {
|
|
266
|
-
spawnSync('
|
|
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
|
-
//
|
|
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
|
-
|
|
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);
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
const quiet = process.argv.includes('--quiet');
|
|
8
|
+
const routeCodexHome = path.join(os.homedir(), '.routecodex');
|
|
9
|
+
|
|
10
|
+
function log(message) {
|
|
11
|
+
if (!quiet) {
|
|
12
|
+
console.log(`[cleanup:server-pids] ${message}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isPidAlive(pid) {
|
|
17
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
process.kill(pid, 0);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readProcessCommand(pid) {
|
|
29
|
+
if (process.platform === 'win32') {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const result = spawnSync('ps', ['-p', String(pid), '-o', 'command='], { encoding: 'utf8' });
|
|
34
|
+
if (result.error || Number(result.status ?? 0) !== 0) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
return String(result.stdout || '').trim().toLowerCase();
|
|
38
|
+
} catch {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isTrustedRouteCodexCommand(command) {
|
|
44
|
+
const normalized = String(command || '').trim().toLowerCase();
|
|
45
|
+
if (!normalized) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (normalized.includes('routecodex/dist/index.js')) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (normalized.includes('routecodex/dist/cli.js')) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (normalized.includes('@jsonstudio/rcc') && normalized.includes('/dist/index.js')) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (normalized.includes('jsonstudio-rcc') && normalized.includes('/dist/index.js')) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isPidListeningOnPort(pid, port) {
|
|
64
|
+
if (process.platform === 'win32') {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const result = spawnSync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-t'], { encoding: 'utf8' });
|
|
69
|
+
if (result.error) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (Number(result.status ?? 0) !== 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const pids = String(result.stdout || '')
|
|
76
|
+
.split(/\r?\n/)
|
|
77
|
+
.map((entry) => Number.parseInt(entry.trim(), 10))
|
|
78
|
+
.filter((entry) => Number.isFinite(entry) && entry > 0);
|
|
79
|
+
return pids.includes(pid);
|
|
80
|
+
} catch {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cleanupPidFile(filePath) {
|
|
86
|
+
const fileName = path.basename(filePath);
|
|
87
|
+
const match = fileName.match(/^server-(\d+)\.pid$/i);
|
|
88
|
+
const filePort = match ? Number.parseInt(match[1], 10) : null;
|
|
89
|
+
let raw = '';
|
|
90
|
+
try {
|
|
91
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
92
|
+
} catch {
|
|
93
|
+
return { removed: false, reason: 'unreadable' };
|
|
94
|
+
}
|
|
95
|
+
const pid = Number.parseInt(String(raw || '').trim(), 10);
|
|
96
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
97
|
+
fs.rmSync(filePath, { force: true });
|
|
98
|
+
return { removed: true, reason: 'invalid_pid' };
|
|
99
|
+
}
|
|
100
|
+
if (!isPidAlive(pid)) {
|
|
101
|
+
fs.rmSync(filePath, { force: true });
|
|
102
|
+
return { removed: true, reason: 'pid_not_alive' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const command = readProcessCommand(pid);
|
|
106
|
+
if (command && !isTrustedRouteCodexCommand(command)) {
|
|
107
|
+
fs.rmSync(filePath, { force: true });
|
|
108
|
+
return { removed: true, reason: 'pid_not_routecodex' };
|
|
109
|
+
}
|
|
110
|
+
if (Number.isFinite(filePort) && filePort > 0 && !isPidListeningOnPort(pid, filePort)) {
|
|
111
|
+
fs.rmSync(filePath, { force: true });
|
|
112
|
+
return { removed: true, reason: 'pid_not_listening_on_port' };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { removed: false, reason: `kept:${fileName}:${pid}` };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function main() {
|
|
119
|
+
if (!fs.existsSync(routeCodexHome)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const entries = fs.readdirSync(routeCodexHome);
|
|
123
|
+
const candidates = entries
|
|
124
|
+
.filter((name) => /^server-\d+\.pid$/i.test(name) || name === 'server.cli.pid')
|
|
125
|
+
.map((name) => path.join(routeCodexHome, name));
|
|
126
|
+
|
|
127
|
+
let removed = 0;
|
|
128
|
+
let kept = 0;
|
|
129
|
+
for (const filePath of candidates) {
|
|
130
|
+
const result = cleanupPidFile(filePath);
|
|
131
|
+
if (result.removed) {
|
|
132
|
+
removed += 1;
|
|
133
|
+
log(`removed ${path.basename(filePath)} (${result.reason})`);
|
|
134
|
+
} else {
|
|
135
|
+
kept += 1;
|
|
136
|
+
log(`kept ${path.basename(filePath)} (${result.reason})`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
log(`done removed=${removed} kept=${kept}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main();
|
|
@@ -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)..."
|
|
@@ -244,11 +250,13 @@ cleanup_old_install() {
|
|
|
244
250
|
main() {
|
|
245
251
|
check_node
|
|
246
252
|
cleanup_old_install
|
|
253
|
+
node scripts/cleanup-stale-server-pids.mjs --quiet || true
|
|
247
254
|
build_project
|
|
248
255
|
global_install
|
|
249
256
|
link_global_llms_dev
|
|
250
257
|
verify_install
|
|
251
258
|
verify_server_health
|
|
259
|
+
node scripts/cleanup-stale-server-pids.mjs --quiet || true
|
|
252
260
|
|
|
253
261
|
echo ""
|
|
254
262
|
echo "🎉 全局安装完成!"
|
|
@@ -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
|
-
|
|
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
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
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 健康检查通过');
|