@jsonstudio/rcc 0.89.1803 → 0.89.1959
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/configsamples/config.json +19 -0
- package/configsamples/provider/deepseek/config.v1.json +59 -0
- package/dist/build-info.js +2 -2
- package/dist/cli/commands/claude.d.ts +4 -0
- package/dist/cli/commands/claude.js +56 -0
- package/dist/cli/commands/claude.js.map +1 -0
- package/dist/cli/commands/clock-admin.d.ts +20 -0
- package/dist/cli/commands/clock-admin.js +234 -0
- package/dist/cli/commands/clock-admin.js.map +1 -0
- package/dist/cli/commands/code.d.ts +0 -42
- package/dist/cli/commands/code.js +4 -414
- package/dist/cli/commands/code.js.map +1 -1
- package/dist/cli/commands/codex.d.ts +4 -0
- package/dist/cli/commands/codex.js +43 -0
- package/dist/cli/commands/codex.js.map +1 -0
- package/dist/cli/commands/examples.js +13 -16
- package/dist/cli/commands/examples.js.map +1 -1
- package/dist/cli/commands/init/basic.d.ts +40 -0
- package/dist/cli/commands/init/basic.js +482 -0
- package/dist/cli/commands/init/basic.js.map +1 -0
- package/dist/cli/commands/init/camoufox.d.ts +7 -0
- package/dist/cli/commands/init/camoufox.js +59 -0
- package/dist/cli/commands/init/camoufox.js.map +1 -0
- package/dist/cli/commands/init/interactive.d.ts +18 -0
- package/dist/cli/commands/init/interactive.js +223 -0
- package/dist/cli/commands/init/interactive.js.map +1 -0
- package/dist/cli/commands/init/shared.d.ts +66 -0
- package/dist/cli/commands/init/shared.js +9 -0
- package/dist/cli/commands/init/shared.js.map +1 -0
- package/dist/cli/commands/init/workflows.d.ts +29 -0
- package/dist/cli/commands/init/workflows.js +341 -0
- package/dist/cli/commands/init/workflows.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -26
- package/dist/cli/commands/init.js +220 -53
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/launcher-kernel.d.ts +78 -0
- package/dist/cli/commands/launcher-kernel.js +1194 -0
- package/dist/cli/commands/launcher-kernel.js.map +1 -0
- package/dist/cli/commands/start.js +27 -1
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +24 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +1 -0
- package/dist/cli/commands/stop.js +201 -4
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/commands/tmux-inject.d.ts +20 -0
- package/dist/cli/commands/tmux-inject.js +212 -0
- package/dist/cli/commands/tmux-inject.js.map +1 -0
- package/dist/cli/config/init-provider-catalog.js +34 -0
- package/dist/cli/config/init-provider-catalog.js.map +1 -1
- package/dist/cli/register/claude-command.d.ts +3 -0
- package/dist/cli/register/claude-command.js +5 -0
- package/dist/cli/register/claude-command.js.map +1 -0
- package/dist/cli/register/clock-admin-command.d.ts +3 -0
- package/dist/cli/register/clock-admin-command.js +5 -0
- package/dist/cli/register/clock-admin-command.js.map +1 -0
- package/dist/cli/register/codex-command.d.ts +3 -0
- package/dist/cli/register/codex-command.js +5 -0
- package/dist/cli/register/codex-command.js.map +1 -0
- package/dist/cli/register/status-config-commands.d.ts +2 -0
- package/dist/cli/register/status-config-commands.js.map +1 -1
- package/dist/cli/register/tmux-inject-command.d.ts +3 -0
- package/dist/cli/register/tmux-inject-command.js +5 -0
- package/dist/cli/register/tmux-inject-command.js.map +1 -0
- package/dist/cli/server/port-utils.d.ts +3 -2
- package/dist/cli/server/port-utils.js +171 -32
- package/dist/cli/server/port-utils.js.map +1 -1
- package/dist/cli.js +45 -6
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini/gemini-protocol-client.js +56 -5
- package/dist/client/gemini/gemini-protocol-client.js.map +1 -1
- package/dist/commands/token-daemon.js +59 -7
- package/dist/commands/token-daemon.js.map +1 -1
- package/dist/commands/validate.js +87 -15
- package/dist/commands/validate.js.map +1 -1
- package/dist/config/routecodex-config-loader.js +31 -2
- package/dist/config/routecodex-config-loader.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +948 -74
- package/dist/index.d.ts +1 -0
- package/dist/index.js +325 -37
- package/dist/index.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.js +8 -14
- package/dist/manager/quota/provider-quota-center.js.map +1 -1
- package/dist/modules/llmswitch/bridge.d.ts +39 -0
- package/dist/modules/llmswitch/bridge.js +169 -0
- package/dist/modules/llmswitch/bridge.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.js +1 -1
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/providers/auth/deepseek-account-auth.d.ts +39 -0
- package/dist/providers/auth/deepseek-account-auth.js +329 -0
- package/dist/providers/auth/deepseek-account-auth.js.map +1 -0
- package/dist/providers/auth/deepseek-account-token-acquirer.d.ts +15 -0
- package/dist/providers/auth/deepseek-account-token-acquirer.js +644 -0
- package/dist/providers/auth/deepseek-account-token-acquirer.js.map +1 -0
- package/dist/providers/auth/oauth-lifecycle.js +26 -4
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/oauth-repair-cooldown.d.ts +5 -0
- package/dist/providers/auth/oauth-repair-cooldown.js +39 -0
- package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
- package/dist/providers/auth/token-scanner/index.d.ts +6 -0
- package/dist/providers/auth/token-scanner/index.js +53 -0
- package/dist/providers/auth/token-scanner/index.js.map +1 -1
- package/dist/providers/core/api/provider-config.d.ts +17 -2
- package/dist/providers/core/api/provider-types.d.ts +6 -0
- package/dist/providers/core/api/provider-types.js.map +1 -1
- package/dist/providers/core/config/camoufox-launcher.d.ts +7 -0
- package/dist/providers/core/config/camoufox-launcher.js +68 -21
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +19 -0
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/contracts/deepseek-provider-contract.d.ts +34 -0
- package/dist/providers/core/contracts/deepseek-provider-contract.js +100 -0
- package/dist/providers/core/contracts/deepseek-provider-contract.js.map +1 -0
- package/dist/providers/core/runtime/anthropic-http-provider.d.ts +0 -5
- package/dist/providers/core/runtime/anthropic-http-provider.js +0 -26
- package/dist/providers/core/runtime/anthropic-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/deepseek-http-provider.d.ts +35 -0
- package/dist/providers/core/runtime/deepseek-http-provider.js +373 -0
- package/dist/providers/core/runtime/deepseek-http-provider.js.map +1 -0
- package/dist/providers/core/runtime/deepseek-session-pow.d.ts +55 -0
- package/dist/providers/core/runtime/deepseek-session-pow.js +422 -0
- package/dist/providers/core/runtime/deepseek-session-pow.js.map +1 -0
- package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +0 -3
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +0 -72
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-http-provider.d.ts +1 -7
- package/dist/providers/core/runtime/gemini-http-provider.js +3 -110
- package/dist/providers/core/runtime/gemini-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.d.ts +1 -0
- package/dist/providers/core/runtime/http-request-executor.js +4 -0
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +10 -4
- package/dist/providers/core/runtime/http-transport-provider.js +308 -82
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/iflow-http-provider.d.ts +0 -4
- package/dist/providers/core/runtime/iflow-http-provider.js +0 -28
- package/dist/providers/core/runtime/iflow-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-factory.d.ts +5 -0
- package/dist/providers/core/runtime/provider-factory.js +59 -6
- package/dist/providers/core/runtime/provider-factory.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.d.ts +0 -2
- package/dist/providers/core/runtime/responses-provider.js +0 -11
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/strategies/oauth-device-flow.js +16 -1
- package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
- package/dist/providers/core/utils/provider-type-utils.js +2 -1
- package/dist/providers/core/utils/provider-type-utils.js.map +1 -1
- package/dist/providers/profile/families/anthropic-profile.d.ts +2 -0
- package/dist/providers/profile/families/anthropic-profile.js +32 -0
- package/dist/providers/profile/families/anthropic-profile.js.map +1 -0
- package/dist/providers/profile/families/antigravity-profile.d.ts +2 -0
- package/dist/providers/profile/families/antigravity-profile.js +109 -0
- package/dist/providers/profile/families/antigravity-profile.js.map +1 -0
- package/dist/providers/profile/families/glm-profile.d.ts +2 -0
- package/dist/providers/profile/families/glm-profile.js +48 -0
- package/dist/providers/profile/families/glm-profile.js.map +1 -0
- package/dist/providers/profile/families/iflow-profile.d.ts +2 -0
- package/dist/providers/profile/families/iflow-profile.js +232 -0
- package/dist/providers/profile/families/iflow-profile.js.map +1 -0
- package/dist/providers/profile/families/qwen-profile.d.ts +2 -0
- package/dist/providers/profile/families/qwen-profile.js +14 -0
- package/dist/providers/profile/families/qwen-profile.js.map +1 -0
- package/dist/providers/profile/families/responses-profile.d.ts +2 -0
- package/dist/providers/profile/families/responses-profile.js +28 -0
- package/dist/providers/profile/families/responses-profile.js.map +1 -0
- package/dist/providers/profile/profile-contracts.d.ts +74 -0
- package/dist/providers/profile/profile-contracts.js +2 -0
- package/dist/providers/profile/profile-contracts.js.map +1 -0
- package/dist/providers/profile/profile-registry.d.ts +3 -0
- package/dist/providers/profile/profile-registry.js +40 -0
- package/dist/providers/profile/profile-registry.js.map +1 -0
- package/dist/providers/profile/provider-directory.d.ts +2 -0
- package/dist/providers/profile/provider-directory.js +55 -0
- package/dist/providers/profile/provider-directory.js.map +1 -0
- package/dist/providers/profile/provider-profile-loader.js +43 -3
- package/dist/providers/profile/provider-profile-loader.js.map +1 -1
- package/dist/providers/profile/provider-profile.d.ts +8 -0
- package/dist/scripts/deepseek/pow-solver.mjs +146 -0
- package/dist/scripts/deepseek/sha3_wasm_bg.7b9ca65ddd.wasm +0 -0
- package/dist/server/handlers/config-admin-handler.js +27 -0
- package/dist/server/handlers/config-admin-handler.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-registry.d.ts +113 -0
- package/dist/server/runtime/http-server/clock-client-registry.js +592 -0
- package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -0
- package/dist/server/runtime/http-server/clock-client-routes.d.ts +2 -0
- package/dist/server/runtime/http-server/clock-client-routes.js +481 -0
- package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -0
- package/dist/server/runtime/http-server/clock-daemon-inject-config.d.ts +1 -0
- package/dist/server/runtime/http-server/clock-daemon-inject-config.js +11 -0
- package/dist/server/runtime/http-server/clock-daemon-inject-config.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +3 -3
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/auth-session.d.ts +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js +18 -2
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/control-handler.js +2 -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 +65 -7
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +37 -1
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-provider.js +55 -0
- package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.js +49 -1
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +10 -0
- package/dist/server/runtime/http-server/index.js +534 -9
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/managed-process-probe.d.ts +6 -0
- package/dist/server/runtime/http-server/managed-process-probe.js +294 -0
- package/dist/server/runtime/http-server/managed-process-probe.js.map +1 -0
- package/dist/server/runtime/http-server/middleware.js +16 -1
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/provider-utils.js +6 -2
- package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +1 -0
- package/dist/server/runtime/http-server/request-executor.js +360 -35
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +95 -3
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +10 -0
- package/dist/server/runtime/http-server/stats-manager.js +119 -16
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/tmux-session-probe.d.ts +3 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js +101 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js.map +1 -0
- package/dist/server/utils/stage-logger.js +21 -5
- package/dist/server/utils/stage-logger.js.map +1 -1
- package/dist/token-daemon/index.js +59 -10
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/token-daemon/server-utils.d.ts +1 -0
- package/dist/token-daemon/server-utils.js +4 -1
- package/dist/token-daemon/server-utils.js.map +1 -1
- package/dist/token-daemon/token-daemon.js +38 -4
- package/dist/token-daemon/token-daemon.js.map +1 -1
- package/dist/token-daemon/token-types.d.ts +1 -1
- package/dist/token-daemon/token-types.js +2 -1
- package/dist/token-daemon/token-types.js.map +1 -1
- package/dist/token-daemon/token-utils.js +5 -2
- package/dist/token-daemon/token-utils.js.map +1 -1
- package/dist/utils/clock-client-token.d.ts +3 -0
- package/dist/utils/clock-client-token.js +54 -0
- package/dist/utils/clock-client-token.js.map +1 -0
- package/dist/utils/managed-server-pids.d.ts +25 -0
- package/dist/utils/managed-server-pids.js +176 -0
- package/dist/utils/managed-server-pids.js.map +1 -0
- package/dist/utils/process-lifecycle-logger.d.ts +8 -0
- package/dist/utils/process-lifecycle-logger.js +151 -0
- package/dist/utils/process-lifecycle-logger.js.map +1 -0
- package/dist/utils/runtime-exit-forensics.d.ts +30 -0
- package/dist/utils/runtime-exit-forensics.js +101 -0
- package/dist/utils/runtime-exit-forensics.js.map +1 -0
- package/dist/utils/shutdown-caller-context.d.ts +22 -0
- package/dist/utils/shutdown-caller-context.js +25 -0
- package/dist/utils/shutdown-caller-context.js.map +1 -0
- package/docs/PROVIDERS_BUILTIN.md +8 -0
- package/docs/PROVIDER_TYPES.md +3 -1
- package/docs/SERVERTOOL_PRE_COMMAND_HOOKS.md +85 -0
- package/docs/clock-client-daemon-design.md +343 -0
- package/docs/daemon-admin-ui.html +948 -74
- package/docs/providers/deepseek-web-provider-design.md +192 -0
- package/docs/routing-instructions.md +4 -1
- package/docs/stop-message-auto.md +4 -3
- package/docs/v2-architecture/PROVIDER-V2-CHANGESET-RELEASE-CHECKLIST.md +80 -0
- package/docs/v2-architecture/PROVIDER-V2-LAYERING-ADR-DRAFT.md +225 -0
- package/docs/v2-architecture/PROVIDER-V2-MIGRATION-MATRIX-DRAFT.md +88 -0
- package/docs/v2-architecture/PROVIDER-V2-PHASED-MIGRATION-ROLLBACK-DRAFT.md +164 -0
- package/docs/v2-architecture/PROVIDER-V2-PROFILE-API-REGISTRY-DRAFT.md +201 -0
- package/docs/v2-architecture/PROVIDER-V2-PROFILE-GEMINI-DRAFT.md +56 -0
- package/docs/v2-architecture/PROVIDER-V2-REFACTOR-OVERVIEW-DRAFT.md +102 -0
- package/docs/v2-architecture/PROVIDER-V2-VERIFICATION-MATRIX-DRAFT.md +163 -0
- package/package.json +10 -9
- package/scripts/copy-compat-assets.mjs +18 -0
- package/scripts/copy-modules-config.mjs +1 -0
- package/scripts/deepseek/pow-solver.mjs +146 -0
- package/scripts/deepseek/sha3_wasm_bg.7b9ca65ddd.wasm +0 -0
- package/scripts/ensure-cli-executable.mjs +64 -0
- package/scripts/install-global.sh +5 -2
- package/scripts/install.sh +1 -1
- package/scripts/monitor/daemon-kill-watch.mjs +184 -0
- package/scripts/monitor/port-kill-watch.sh +74 -0
- package/scripts/quick-install.sh +1 -1
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
* - 保持API兼容性
|
|
9
9
|
*/
|
|
10
10
|
import express, {} from 'express';
|
|
11
|
+
import * as fs from 'node:fs/promises';
|
|
12
|
+
import path from 'node:path';
|
|
11
13
|
import { ErrorHandlingCenter } from 'rcc-errorhandling';
|
|
12
14
|
import { ProviderFactory } from '../../../providers/core/runtime/provider-factory.js';
|
|
13
15
|
import { PipelineDebugLogger as PipelineDebugLoggerImpl } from '../../../modules/pipeline/utils/debug-logger.js';
|
|
14
16
|
import { attachProviderRuntimeMetadata } from '../../../providers/core/runtime/provider-runtime-metadata.js';
|
|
15
17
|
import { preloadAntigravityAliasUserAgents, primeAntigravityUserAgentVersion } from '../../../providers/auth/antigravity-user-agent.js';
|
|
16
18
|
import { getAntigravityWarmupBlacklistDurationMs, isAntigravityWarmupEnabled, warmupCheckAntigravityAlias } from '../../../providers/auth/antigravity-warmup.js';
|
|
19
|
+
import { shutdownCamoufoxLaunchers } from '../../../providers/core/config/camoufox-launcher.js';
|
|
17
20
|
import { AuthFileResolver } from '../../../config/auth-file-resolver.js';
|
|
18
21
|
import { buildProviderProfiles } from '../../../providers/profile/provider-profile-loader.js';
|
|
19
22
|
import { isStageLoggingEnabled, logPipelineStage } from '../../utils/stage-logger.js';
|
|
@@ -22,7 +25,7 @@ import { registerHttpRoutes, registerOAuthPortalRoute } from './routes.js';
|
|
|
22
25
|
import { mapProviderProtocol, normalizeProviderType, resolveProviderIdentity, asRecord } from './provider-utils.js';
|
|
23
26
|
import { resolveRepoRoot } from './llmswitch-loader.js';
|
|
24
27
|
import { enhanceProviderRequestId } from '../../utils/request-id-manager.js';
|
|
25
|
-
import { convertProviderResponse as bridgeConvertProviderResponse, createSnapshotRecorder as bridgeCreateSnapshotRecorder, rebindResponsesConversationRequestId, extractSessionIdentifiersFromMetadata, bootstrapVirtualRouterConfig, getProviderSuccessCenter, getHubPipelineCtor } from '../../../modules/llmswitch/bridge.js';
|
|
28
|
+
import { convertProviderResponse as bridgeConvertProviderResponse, createSnapshotRecorder as bridgeCreateSnapshotRecorder, rebindResponsesConversationRequestId, extractSessionIdentifiersFromMetadata, bootstrapVirtualRouterConfig, getProviderSuccessCenter, getHubPipelineCtor, resolveClockConfigSnapshot, reserveClockDueTasks, commitClockDueReservation, clearClockTasksSnapshot } from '../../../modules/llmswitch/bridge.js';
|
|
26
29
|
import { initializeRouteErrorHub, reportRouteError } from '../../../error-handling/route-error-hub.js';
|
|
27
30
|
import { writeClientSnapshot } from '../../../providers/core/utils/snapshot-writer.js';
|
|
28
31
|
import { createServerColoredLogger } from './colored-logger.js';
|
|
@@ -38,11 +41,71 @@ import { canonicalizeServerId } from './server-id.js';
|
|
|
38
41
|
import { StatsManager } from './stats-manager.js';
|
|
39
42
|
import { loadRouteCodexConfig } from '../../../config/routecodex-config-loader.js';
|
|
40
43
|
import { buildInfo } from '../../../build-info.js';
|
|
44
|
+
import { getClockClientRegistry } from './clock-client-registry.js';
|
|
45
|
+
import { toExactMatchClockConfig } from './clock-daemon-inject-config.js';
|
|
46
|
+
import { isTmuxSessionAlive, killManagedTmuxSession } from './tmux-session-probe.js';
|
|
47
|
+
import { terminateManagedClientProcess } from './managed-process-probe.js';
|
|
41
48
|
import { recordHubShadowCompareDiff, resolveHubShadowCompareConfig, shouldRunHubShadowCompare } from './hub-shadow-compare.js';
|
|
42
49
|
import { recordLlmsEngineShadowDiff, isLlmsEngineShadowEnabledForSubpath, resolveLlmsEngineShadowConfig, shouldRunLlmsEngineShadowForSubpath } from '../../../utils/llms-engine-shadow.js';
|
|
43
50
|
import { resolveLlmswitchCoreVersion } from '../../../utils/runtime-versions.js';
|
|
44
51
|
const DEFAULT_MAX_PROVIDER_ATTEMPTS = 6;
|
|
45
52
|
const DEFAULT_ANTIGRAVITY_MAX_PROVIDER_ATTEMPTS = 20;
|
|
53
|
+
const RETRYABLE_SSE_ERROR_CODE_HINTS = [
|
|
54
|
+
'internal_network_failure',
|
|
55
|
+
'network_error',
|
|
56
|
+
'api_connection_error',
|
|
57
|
+
'service_unavailable',
|
|
58
|
+
'internal_server_error',
|
|
59
|
+
'overloaded_error',
|
|
60
|
+
'rate_limit_error',
|
|
61
|
+
'request_timeout',
|
|
62
|
+
'timeout'
|
|
63
|
+
];
|
|
64
|
+
const RETRYABLE_SSE_MESSAGE_HINTS = [
|
|
65
|
+
'internal network failure',
|
|
66
|
+
'network failure',
|
|
67
|
+
'network error',
|
|
68
|
+
'temporarily unavailable',
|
|
69
|
+
'temporarily unreachable',
|
|
70
|
+
'upstream disconnected',
|
|
71
|
+
'connection reset',
|
|
72
|
+
'connection closed',
|
|
73
|
+
'timed out',
|
|
74
|
+
'timeout'
|
|
75
|
+
];
|
|
76
|
+
function firstNonEmptyString(candidates) {
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
if (typeof candidate !== 'string') {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const trimmed = candidate.trim();
|
|
82
|
+
if (trimmed) {
|
|
83
|
+
return trimmed;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function firstFiniteNumber(candidates) {
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
if (typeof candidate === 'number' && Number.isFinite(candidate)) {
|
|
91
|
+
return candidate;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
function isRetryableSseWrapperError(message, errorCode, status) {
|
|
97
|
+
if (typeof status === 'number' && Number.isFinite(status)) {
|
|
98
|
+
if (status === 408 || status === 425 || status === 429 || status >= 500) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const normalizedCode = typeof errorCode === 'string' ? errorCode.trim().toLowerCase() : '';
|
|
103
|
+
if (normalizedCode && RETRYABLE_SSE_ERROR_CODE_HINTS.some((hint) => normalizedCode.includes(hint))) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const loweredMessage = message.toLowerCase();
|
|
107
|
+
return RETRYABLE_SSE_MESSAGE_HINTS.some((hint) => loweredMessage.includes(hint));
|
|
108
|
+
}
|
|
46
109
|
function resolveMaxProviderAttempts() {
|
|
47
110
|
const raw = String(process.env.ROUTECODEX_MAX_PROVIDER_ATTEMPTS || process.env.RCC_MAX_PROVIDER_ATTEMPTS || '')
|
|
48
111
|
.trim()
|
|
@@ -116,6 +179,10 @@ export class RouteCodexHttpServer {
|
|
|
116
179
|
hubPolicyMode = null;
|
|
117
180
|
hubPipelineEngineShadow = null;
|
|
118
181
|
hubPipelineConfigForShadow = null;
|
|
182
|
+
clockDaemonInjectTimer = null;
|
|
183
|
+
clockDaemonInjectTickInFlight = false;
|
|
184
|
+
lastClockDaemonInjectErrorAtMs = 0;
|
|
185
|
+
lastClockDaemonCleanupAtMs = 0;
|
|
119
186
|
constructor(config) {
|
|
120
187
|
this.config = config;
|
|
121
188
|
this.app = express();
|
|
@@ -318,6 +385,9 @@ export class RouteCodexHttpServer {
|
|
|
318
385
|
if (!patched.defaultModel && profile.metadata?.defaultModel) {
|
|
319
386
|
patched.defaultModel = profile.metadata.defaultModel;
|
|
320
387
|
}
|
|
388
|
+
if (!patched.deepseek && profile.metadata?.deepseek) {
|
|
389
|
+
patched.deepseek = profile.metadata.deepseek;
|
|
390
|
+
}
|
|
321
391
|
return this.canonicalizeRuntimeProvider(patched);
|
|
322
392
|
}
|
|
323
393
|
canonicalizeRuntimeProvider(runtime) {
|
|
@@ -499,6 +569,200 @@ export class RouteCodexHttpServer {
|
|
|
499
569
|
}
|
|
500
570
|
return true;
|
|
501
571
|
}
|
|
572
|
+
shouldEnableClockDaemonInjectLoop() {
|
|
573
|
+
const raw = String(process.env.ROUTECODEX_CLOCK_DAEMON_INJECT_ENABLE || process.env.RCC_CLOCK_DAEMON_INJECT_ENABLE || '').trim().toLowerCase();
|
|
574
|
+
if (raw === '0' || raw === 'false' || raw === 'off' || raw === 'no') {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
if (raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes') {
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
if (process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test') {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
resolveRawClockConfig() {
|
|
586
|
+
const user = this.userConfig && typeof this.userConfig === 'object' ? this.userConfig : {};
|
|
587
|
+
const vr = user.virtualrouter && typeof user.virtualrouter === 'object' ? user.virtualrouter : null;
|
|
588
|
+
if (vr && Object.prototype.hasOwnProperty.call(vr, 'clock')) {
|
|
589
|
+
return vr.clock;
|
|
590
|
+
}
|
|
591
|
+
if (Object.prototype.hasOwnProperty.call(user, 'clock')) {
|
|
592
|
+
return user.clock;
|
|
593
|
+
}
|
|
594
|
+
const artCfg = this.currentRouterArtifacts &&
|
|
595
|
+
this.currentRouterArtifacts.config &&
|
|
596
|
+
typeof this.currentRouterArtifacts.config === 'object'
|
|
597
|
+
? this.currentRouterArtifacts.config
|
|
598
|
+
: null;
|
|
599
|
+
if (artCfg && Object.prototype.hasOwnProperty.call(artCfg, 'clock')) {
|
|
600
|
+
return artCfg.clock;
|
|
601
|
+
}
|
|
602
|
+
return undefined;
|
|
603
|
+
}
|
|
604
|
+
stopClockDaemonInjectLoop() {
|
|
605
|
+
if (this.clockDaemonInjectTimer) {
|
|
606
|
+
clearInterval(this.clockDaemonInjectTimer);
|
|
607
|
+
this.clockDaemonInjectTimer = null;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
startClockDaemonInjectLoop() {
|
|
611
|
+
this.stopClockDaemonInjectLoop();
|
|
612
|
+
if (!this.shouldEnableClockDaemonInjectLoop()) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const rawTick = String(process.env.ROUTECODEX_CLOCK_DAEMON_INJECT_TICK_MS || process.env.RCC_CLOCK_DAEMON_INJECT_TICK_MS || '').trim();
|
|
616
|
+
const parsedTick = rawTick ? Number.parseInt(rawTick, 10) : NaN;
|
|
617
|
+
const tickMs = Number.isFinite(parsedTick) && parsedTick >= 200 ? Math.floor(parsedTick) : 1500;
|
|
618
|
+
this.clockDaemonInjectTimer = setInterval(() => {
|
|
619
|
+
void this.tickClockDaemonInjectLoop();
|
|
620
|
+
}, tickMs);
|
|
621
|
+
this.clockDaemonInjectTimer.unref?.();
|
|
622
|
+
void this.tickClockDaemonInjectLoop();
|
|
623
|
+
}
|
|
624
|
+
async tickClockDaemonInjectLoop() {
|
|
625
|
+
if (this.clockDaemonInjectTickInFlight) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
this.clockDaemonInjectTickInFlight = true;
|
|
629
|
+
try {
|
|
630
|
+
const rawClockConfig = this.resolveRawClockConfig();
|
|
631
|
+
const resolvedClockConfig = await resolveClockConfigSnapshot(rawClockConfig);
|
|
632
|
+
if (!resolvedClockConfig) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const clockConfig = toExactMatchClockConfig(resolvedClockConfig);
|
|
636
|
+
const sessionDir = String(process.env.ROUTECODEX_SESSION_DIR || '').trim();
|
|
637
|
+
const clockDir = sessionDir ? path.join(sessionDir, 'clock') : '';
|
|
638
|
+
const entries = clockDir
|
|
639
|
+
? await fs.readdir(clockDir, { withFileTypes: true }).catch(() => [])
|
|
640
|
+
: [];
|
|
641
|
+
const registry = getClockClientRegistry();
|
|
642
|
+
const now = Date.now();
|
|
643
|
+
const cleanupRequestId = `clock_cleanup_${now}_${Math.random().toString(16).slice(2, 8)}`;
|
|
644
|
+
const staleCleanup = registry.cleanupStaleHeartbeats({
|
|
645
|
+
nowMs: now,
|
|
646
|
+
terminateManagedTmuxSession: (tmuxSessionId) => killManagedTmuxSession(tmuxSessionId),
|
|
647
|
+
terminateManagedClientProcess: (processInfo) => terminateManagedClientProcess(processInfo)
|
|
648
|
+
});
|
|
649
|
+
const deadTmuxCleanup = registry.cleanupDeadTmuxSessions({
|
|
650
|
+
isTmuxSessionAlive,
|
|
651
|
+
terminateManagedTmuxSession: (tmuxSessionId) => killManagedTmuxSession(tmuxSessionId),
|
|
652
|
+
terminateManagedClientProcess: (processInfo) => terminateManagedClientProcess(processInfo)
|
|
653
|
+
});
|
|
654
|
+
const removedConversationSessionIds = Array.from(new Set([
|
|
655
|
+
...staleCleanup.removedConversationSessionIds,
|
|
656
|
+
...deadTmuxCleanup.removedConversationSessionIds
|
|
657
|
+
]));
|
|
658
|
+
if (removedConversationSessionIds.length > 0) {
|
|
659
|
+
for (const removedConversationSessionId of removedConversationSessionIds) {
|
|
660
|
+
await clearClockTasksSnapshot({
|
|
661
|
+
sessionId: removedConversationSessionId,
|
|
662
|
+
config: clockConfig
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const hasCleanupActions = staleCleanup.removedDaemonIds.length > 0
|
|
667
|
+
|| deadTmuxCleanup.removedDaemonIds.length > 0;
|
|
668
|
+
if (hasCleanupActions && Date.now() - this.lastClockDaemonCleanupAtMs > 2000) {
|
|
669
|
+
this.lastClockDaemonCleanupAtMs = Date.now();
|
|
670
|
+
console.log('[RouteCodexHttpServer] clock daemon cleanup audit:', {
|
|
671
|
+
requestId: cleanupRequestId,
|
|
672
|
+
staleHeartbeat: {
|
|
673
|
+
reason: 'heartbeat_timeout',
|
|
674
|
+
staleAfterMs: staleCleanup.staleAfterMs,
|
|
675
|
+
removedDaemonIds: staleCleanup.removedDaemonIds,
|
|
676
|
+
removedTmuxSessionIds: staleCleanup.removedTmuxSessionIds,
|
|
677
|
+
removedConversationSessionIds: staleCleanup.removedConversationSessionIds,
|
|
678
|
+
killedTmuxSessionIds: staleCleanup.killedTmuxSessionIds,
|
|
679
|
+
failedKillTmuxSessionIds: staleCleanup.failedKillTmuxSessionIds,
|
|
680
|
+
skippedKillTmuxSessionIds: staleCleanup.skippedKillTmuxSessionIds,
|
|
681
|
+
killedManagedClientPids: staleCleanup.killedManagedClientPids,
|
|
682
|
+
failedKillManagedClientPids: staleCleanup.failedKillManagedClientPids,
|
|
683
|
+
skippedKillManagedClientPids: staleCleanup.skippedKillManagedClientPids
|
|
684
|
+
},
|
|
685
|
+
deadTmux: {
|
|
686
|
+
reason: 'tmux_not_alive',
|
|
687
|
+
removedDaemonIds: deadTmuxCleanup.removedDaemonIds,
|
|
688
|
+
removedTmuxSessionIds: deadTmuxCleanup.removedTmuxSessionIds,
|
|
689
|
+
removedConversationSessionIds: deadTmuxCleanup.removedConversationSessionIds,
|
|
690
|
+
killedTmuxSessionIds: deadTmuxCleanup.killedTmuxSessionIds,
|
|
691
|
+
failedKillTmuxSessionIds: deadTmuxCleanup.failedKillTmuxSessionIds,
|
|
692
|
+
skippedKillTmuxSessionIds: deadTmuxCleanup.skippedKillTmuxSessionIds,
|
|
693
|
+
killedManagedClientPids: deadTmuxCleanup.killedManagedClientPids,
|
|
694
|
+
failedKillManagedClientPids: deadTmuxCleanup.failedKillManagedClientPids,
|
|
695
|
+
skippedKillManagedClientPids: deadTmuxCleanup.skippedKillManagedClientPids
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
for (const entry of entries) {
|
|
700
|
+
if (!entry || typeof entry.name !== 'string') {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (!entry.name.endsWith('.json')) {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (typeof entry.isFile === 'function' && !entry.isFile()) {
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const sessionId = entry.name.slice(0, -'.json'.length).trim();
|
|
710
|
+
if (!sessionId) {
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
const reservationId = 'clockd_inject_' + now + '_' + Math.random().toString(16).slice(2, 8);
|
|
714
|
+
const reserved = await reserveClockDueTasks({
|
|
715
|
+
reservationId,
|
|
716
|
+
sessionId,
|
|
717
|
+
config: clockConfig,
|
|
718
|
+
requestId: reservationId
|
|
719
|
+
});
|
|
720
|
+
if (!reserved || !reserved.reservation || typeof reserved.injectText !== 'string' || !reserved.injectText.trim()) {
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const bind = registry.bindConversationSession({ conversationSessionId: sessionId });
|
|
724
|
+
const text = [
|
|
725
|
+
'[Clock Reminder]: scheduled tasks are due.',
|
|
726
|
+
reserved.injectText.trim(),
|
|
727
|
+
'You may call tools to complete these tasks.',
|
|
728
|
+
'MANDATORY: if waiting is needed, use the clock tool to schedule wake-up (clock.schedule) now; do not only promise to wait.'
|
|
729
|
+
].join('\n');
|
|
730
|
+
const injected = await registry.inject({
|
|
731
|
+
sessionId,
|
|
732
|
+
text,
|
|
733
|
+
requestId: reservationId,
|
|
734
|
+
source: 'clock.daemon.inject'
|
|
735
|
+
});
|
|
736
|
+
if (!injected.ok) {
|
|
737
|
+
const nowWarn = Date.now();
|
|
738
|
+
if (nowWarn - this.lastClockDaemonInjectErrorAtMs > 5000) {
|
|
739
|
+
this.lastClockDaemonInjectErrorAtMs = nowWarn;
|
|
740
|
+
console.warn('[RouteCodexHttpServer] clock daemon inject skipped:', {
|
|
741
|
+
sessionId,
|
|
742
|
+
injectReason: injected.reason,
|
|
743
|
+
bindOk: bind.ok,
|
|
744
|
+
bindReason: bind.reason
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
await commitClockDueReservation({
|
|
750
|
+
reservation: reserved.reservation,
|
|
751
|
+
config: clockConfig
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
const now = Date.now();
|
|
757
|
+
if (now - this.lastClockDaemonInjectErrorAtMs > 5000) {
|
|
758
|
+
this.lastClockDaemonInjectErrorAtMs = now;
|
|
759
|
+
console.warn('[RouteCodexHttpServer] clock daemon inject loop tick failed:', error);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
finally {
|
|
763
|
+
this.clockDaemonInjectTickInFlight = false;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
502
766
|
/**
|
|
503
767
|
* 初始化服务器
|
|
504
768
|
*/
|
|
@@ -610,7 +874,12 @@ export class RouteCodexHttpServer {
|
|
|
610
874
|
return new Promise((resolve, reject) => {
|
|
611
875
|
this.server = this.app.listen(this.config.server.port, this.config.server.host, () => {
|
|
612
876
|
this._isRunning = true;
|
|
613
|
-
|
|
877
|
+
const boundAddress = this.server?.address();
|
|
878
|
+
const resolvedPort = boundAddress && typeof boundAddress === 'object' && typeof boundAddress.port === 'number'
|
|
879
|
+
? boundAddress.port
|
|
880
|
+
: this.config.server.port;
|
|
881
|
+
process.env.ROUTECODEX_SERVER_PORT = String(resolvedPort);
|
|
882
|
+
console.log(`[RouteCodexHttpServer] Server started on ${this.config.server.host}:${resolvedPort}`);
|
|
614
883
|
resolve();
|
|
615
884
|
});
|
|
616
885
|
// In test runners (Jest), prevent the listen handle from keeping the process alive
|
|
@@ -639,6 +908,16 @@ export class RouteCodexHttpServer {
|
|
|
639
908
|
* 停止服务器
|
|
640
909
|
*/
|
|
641
910
|
async stop() {
|
|
911
|
+
this.stopClockDaemonInjectLoop();
|
|
912
|
+
try {
|
|
913
|
+
await shutdownCamoufoxLaunchers();
|
|
914
|
+
}
|
|
915
|
+
catch {
|
|
916
|
+
// ignore launcher cleanup errors
|
|
917
|
+
}
|
|
918
|
+
if (!this.server) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
642
921
|
if (this.server) {
|
|
643
922
|
// Best-effort: close any open keep-alive sockets so server.close can finish.
|
|
644
923
|
for (const socket of this.activeSockets) {
|
|
@@ -879,6 +1158,7 @@ export class RouteCodexHttpServer {
|
|
|
879
1158
|
this.hubPipelineConfigForShadow = hubConfig;
|
|
880
1159
|
this.hubPipelineEngineShadow = null;
|
|
881
1160
|
await this.initializeProviderRuntimes(bootstrapArtifacts);
|
|
1161
|
+
this.startClockDaemonInjectLoop();
|
|
882
1162
|
}
|
|
883
1163
|
buildHandlerContext() {
|
|
884
1164
|
return {
|
|
@@ -1177,6 +1457,21 @@ export class RouteCodexHttpServer {
|
|
|
1177
1457
|
if (rawType === 'iflow-cookie') {
|
|
1178
1458
|
return { ...auth, type: 'apikey', rawType: auth.rawType ?? 'iflow-cookie' };
|
|
1179
1459
|
}
|
|
1460
|
+
if (rawType === 'deepseek-account') {
|
|
1461
|
+
// Compatibility note:
|
|
1462
|
+
// legacy deepseek configs may still carry value/secretRef/mobile/password/clientId/clientSecret.
|
|
1463
|
+
// Runtime auth for deepseek-account is tokenFile(+alias)-only; we strip legacy fields instead of
|
|
1464
|
+
// failing init so existing deployments can migrate without downtime.
|
|
1465
|
+
const tokenFile = pickString(authRecord.tokenFile, authRecord.token_file);
|
|
1466
|
+
const accountAlias = pickString(authRecord.accountAlias, authRecord.account_alias, runtime.keyAlias);
|
|
1467
|
+
return {
|
|
1468
|
+
type: 'apikey',
|
|
1469
|
+
rawType: auth.rawType ?? 'deepseek-account',
|
|
1470
|
+
value: '',
|
|
1471
|
+
...(accountAlias ? { accountAlias } : {}),
|
|
1472
|
+
...(tokenFile ? { tokenFile } : {})
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1180
1475
|
const value = await this.resolveApiKeyValue(runtime, auth);
|
|
1181
1476
|
return { ...auth, type: 'apikey', value };
|
|
1182
1477
|
}
|
|
@@ -1267,6 +1562,7 @@ export class RouteCodexHttpServer {
|
|
|
1267
1562
|
}
|
|
1268
1563
|
}));
|
|
1269
1564
|
this.providerHandles.clear();
|
|
1565
|
+
ProviderFactory.clearInstanceCache?.();
|
|
1270
1566
|
}
|
|
1271
1567
|
async executePipeline(input) {
|
|
1272
1568
|
if (!this.isPipelineReady()) {
|
|
@@ -1337,7 +1633,26 @@ export class RouteCodexHttpServer {
|
|
|
1337
1633
|
stream: metadataForIteration.stream
|
|
1338
1634
|
});
|
|
1339
1635
|
const originalRequestSnapshot = this.cloneRequestPayload(input.body);
|
|
1340
|
-
|
|
1636
|
+
let pipelineResult;
|
|
1637
|
+
try {
|
|
1638
|
+
pipelineResult = await this.runHubPipeline(input, metadataForIteration);
|
|
1639
|
+
}
|
|
1640
|
+
catch (pipelineError) {
|
|
1641
|
+
const pipelineErrorCode = typeof pipelineError.code === 'string'
|
|
1642
|
+
? String(pipelineError.code).trim()
|
|
1643
|
+
: '';
|
|
1644
|
+
const pipelineErrorMessage = pipelineError instanceof Error
|
|
1645
|
+
? pipelineError.message
|
|
1646
|
+
: String(pipelineError ?? 'Unknown error');
|
|
1647
|
+
const isPoolExhaustedError = pipelineErrorCode === 'PROVIDER_NOT_AVAILABLE' ||
|
|
1648
|
+
pipelineErrorCode === 'ERR_NO_PROVIDER_TARGET' ||
|
|
1649
|
+
/all providers unavailable/i.test(pipelineErrorMessage) ||
|
|
1650
|
+
/virtual router did not produce a provider target/i.test(pipelineErrorMessage);
|
|
1651
|
+
if (firstError && isPoolExhaustedError) {
|
|
1652
|
+
throw firstError;
|
|
1653
|
+
}
|
|
1654
|
+
throw pipelineError;
|
|
1655
|
+
}
|
|
1341
1656
|
const pipelineMetadata = pipelineResult.metadata ?? {};
|
|
1342
1657
|
const mergedMetadata = { ...metadataForIteration, ...pipelineMetadata };
|
|
1343
1658
|
this.logStage(`${pipelineLabel}.completed`, providerRequestId, {
|
|
@@ -1370,6 +1685,33 @@ export class RouteCodexHttpServer {
|
|
|
1370
1685
|
requestId: input.requestId,
|
|
1371
1686
|
retryable: true
|
|
1372
1687
|
});
|
|
1688
|
+
try {
|
|
1689
|
+
const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
|
|
1690
|
+
emitProviderError({
|
|
1691
|
+
error,
|
|
1692
|
+
stage: 'provider.runtime.resolve',
|
|
1693
|
+
runtime: {
|
|
1694
|
+
requestId: input.requestId,
|
|
1695
|
+
providerKey: target.providerKey,
|
|
1696
|
+
providerId: target.providerKey.split('.')[0],
|
|
1697
|
+
providerType: String(target.providerType || 'unknown'),
|
|
1698
|
+
providerProtocol: String(target.outboundProfile || ''),
|
|
1699
|
+
routeName: pipelineResult.routingDecision?.routeName,
|
|
1700
|
+
pipelineId: target.providerKey,
|
|
1701
|
+
target
|
|
1702
|
+
},
|
|
1703
|
+
dependencies: this.getModuleDependencies(),
|
|
1704
|
+
recoverable: false,
|
|
1705
|
+
affectsHealth: true,
|
|
1706
|
+
details: {
|
|
1707
|
+
reason: 'runtime_not_initialized',
|
|
1708
|
+
providerKey: target.providerKey
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
catch {
|
|
1713
|
+
// best-effort
|
|
1714
|
+
}
|
|
1373
1715
|
if (!firstError) {
|
|
1374
1716
|
firstError = error;
|
|
1375
1717
|
}
|
|
@@ -1393,6 +1735,35 @@ export class RouteCodexHttpServer {
|
|
|
1393
1735
|
requestId: input.requestId,
|
|
1394
1736
|
retryable: true
|
|
1395
1737
|
});
|
|
1738
|
+
try {
|
|
1739
|
+
const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
|
|
1740
|
+
emitProviderError({
|
|
1741
|
+
error: initError ?? error,
|
|
1742
|
+
stage: 'provider.runtime.resolve',
|
|
1743
|
+
runtime: {
|
|
1744
|
+
requestId: input.requestId,
|
|
1745
|
+
providerKey: target.providerKey,
|
|
1746
|
+
providerId: target.providerKey.split('.')[0],
|
|
1747
|
+
providerType: String(target.providerType || 'unknown'),
|
|
1748
|
+
providerProtocol: String(target.outboundProfile || ''),
|
|
1749
|
+
routeName: pipelineResult.routingDecision?.routeName,
|
|
1750
|
+
pipelineId: target.providerKey,
|
|
1751
|
+
runtimeKey,
|
|
1752
|
+
target
|
|
1753
|
+
},
|
|
1754
|
+
dependencies: this.getModuleDependencies(),
|
|
1755
|
+
recoverable: false,
|
|
1756
|
+
affectsHealth: true,
|
|
1757
|
+
details: {
|
|
1758
|
+
reason: 'runtime_handle_missing',
|
|
1759
|
+
providerKey: target.providerKey,
|
|
1760
|
+
runtimeKey
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
catch {
|
|
1765
|
+
// best-effort
|
|
1766
|
+
}
|
|
1396
1767
|
if (!firstError) {
|
|
1397
1768
|
firstError = error;
|
|
1398
1769
|
}
|
|
@@ -1511,6 +1882,61 @@ export class RouteCodexHttpServer {
|
|
|
1511
1882
|
response: normalized,
|
|
1512
1883
|
pipelineMetadata: mergedMetadata
|
|
1513
1884
|
});
|
|
1885
|
+
// Treat upstream 429 as provider failure across protocols to avoid
|
|
1886
|
+
// silently returning success and to let Virtual Router failover to other candidates.
|
|
1887
|
+
// Keep existing Gemini compatibility behavior for 400/4xx thoughtSignature-like failures.
|
|
1888
|
+
const convertedStatus = typeof converted.status === 'number' ? converted.status : undefined;
|
|
1889
|
+
const isGlobalRetryable429 = convertedStatus === 429;
|
|
1890
|
+
const isGeminiCompatFailure = typeof convertedStatus === 'number' &&
|
|
1891
|
+
convertedStatus >= 400 &&
|
|
1892
|
+
(isAntigravityProviderKey(target.providerKey) ||
|
|
1893
|
+
(typeof target.providerKey === 'string' && target.providerKey.startsWith('gemini-cli.'))) &&
|
|
1894
|
+
providerProtocol === 'gemini-chat';
|
|
1895
|
+
if (isGlobalRetryable429 || isGeminiCompatFailure) {
|
|
1896
|
+
const bodyForError = converted.body && typeof converted.body === 'object'
|
|
1897
|
+
? converted.body
|
|
1898
|
+
: undefined;
|
|
1899
|
+
const errMsg = bodyForError && bodyForError.error && typeof bodyForError.error === 'object'
|
|
1900
|
+
? String(bodyForError.error.message || bodyForError.error || '')
|
|
1901
|
+
: '';
|
|
1902
|
+
const statusCode = typeof convertedStatus === 'number' ? convertedStatus : 500;
|
|
1903
|
+
const errorToThrow = new Error(errMsg && errMsg.trim().length ? errMsg : `HTTP ${statusCode}`);
|
|
1904
|
+
errorToThrow.statusCode = statusCode;
|
|
1905
|
+
errorToThrow.status = statusCode;
|
|
1906
|
+
errorToThrow.response = { data: bodyForError };
|
|
1907
|
+
try {
|
|
1908
|
+
const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
|
|
1909
|
+
emitProviderError({
|
|
1910
|
+
error: errorToThrow,
|
|
1911
|
+
stage: 'provider.http',
|
|
1912
|
+
runtime: {
|
|
1913
|
+
requestId: input.requestId,
|
|
1914
|
+
providerKey: target.providerKey,
|
|
1915
|
+
providerId: handle.providerId,
|
|
1916
|
+
providerType: handle.providerType,
|
|
1917
|
+
providerFamily: handle.providerFamily,
|
|
1918
|
+
providerProtocol,
|
|
1919
|
+
routeName: pipelineResult.routingDecision?.routeName,
|
|
1920
|
+
pipelineId: target.providerKey,
|
|
1921
|
+
target,
|
|
1922
|
+
runtimeKey
|
|
1923
|
+
},
|
|
1924
|
+
dependencies: this.getModuleDependencies(),
|
|
1925
|
+
statusCode,
|
|
1926
|
+
recoverable: statusCode === 429,
|
|
1927
|
+
affectsHealth: true,
|
|
1928
|
+
details: {
|
|
1929
|
+
source: 'converted_response_status',
|
|
1930
|
+
convertedStatus: statusCode,
|
|
1931
|
+
wrappedErrorResponse: true
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
catch {
|
|
1936
|
+
// best-effort; never block retry/failover path
|
|
1937
|
+
}
|
|
1938
|
+
throw errorToThrow;
|
|
1939
|
+
}
|
|
1514
1940
|
const usage = this.extractUsageFromResult(converted, mergedMetadata);
|
|
1515
1941
|
// QuotaManager listens to provider error/success events; avoid duplicating accounting here.
|
|
1516
1942
|
this.stats.recordCompletion(statsRequestId, { usage, error: false });
|
|
@@ -2000,12 +2426,21 @@ export class RouteCodexHttpServer {
|
|
|
2000
2426
|
if (body && typeof body === 'object') {
|
|
2001
2427
|
const wrapperError = this.extractSseWrapperError(body);
|
|
2002
2428
|
if (wrapperError) {
|
|
2003
|
-
const
|
|
2429
|
+
const codeSuffix = wrapperError.errorCode ? ` [${wrapperError.errorCode}]` : '';
|
|
2430
|
+
const error = new Error(`Upstream SSE error event${codeSuffix}: ${wrapperError.message}`);
|
|
2004
2431
|
error.code = 'SSE_DECODE_ERROR';
|
|
2432
|
+
if (wrapperError.errorCode) {
|
|
2433
|
+
error.upstreamCode = wrapperError.errorCode;
|
|
2434
|
+
}
|
|
2435
|
+
error.retryable = wrapperError.retryable;
|
|
2436
|
+
if (wrapperError.retryable) {
|
|
2437
|
+
error.status = 503;
|
|
2438
|
+
error.statusCode = 503;
|
|
2439
|
+
}
|
|
2005
2440
|
throw error;
|
|
2006
2441
|
}
|
|
2007
2442
|
}
|
|
2008
|
-
if (options.processMode === 'passthrough') {
|
|
2443
|
+
if (options.processMode === 'passthrough' && !options.wantsStream) {
|
|
2009
2444
|
return options.response;
|
|
2010
2445
|
}
|
|
2011
2446
|
const entry = (options.entryEndpoint || '').toLowerCase();
|
|
@@ -2039,6 +2474,12 @@ export class RouteCodexHttpServer {
|
|
|
2039
2474
|
const baseContext = {
|
|
2040
2475
|
...(metadataBag ?? {})
|
|
2041
2476
|
};
|
|
2477
|
+
if (baseContext.capturedChatRequest === undefined &&
|
|
2478
|
+
options.originalRequest &&
|
|
2479
|
+
typeof options.originalRequest === 'object' &&
|
|
2480
|
+
!Array.isArray(options.originalRequest)) {
|
|
2481
|
+
baseContext.capturedChatRequest = options.originalRequest;
|
|
2482
|
+
}
|
|
2042
2483
|
// 将 HubPipeline metadata.routeName 映射为 AdapterContext.routeId,
|
|
2043
2484
|
// 便于 llmswitch-core 在第三跳中使用 routeHint 复用首次路由决策。
|
|
2044
2485
|
if (typeof metadataBag?.routeName === 'string') {
|
|
@@ -2181,10 +2622,11 @@ export class RouteCodexHttpServer {
|
|
|
2181
2622
|
if (!record || typeof record !== 'object' || depth < 0) {
|
|
2182
2623
|
return undefined;
|
|
2183
2624
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2625
|
+
if (record.mode === 'sse') {
|
|
2626
|
+
const normalized = this.normalizeSseWrapperErrorValue(record.error, depth);
|
|
2627
|
+
if (normalized) {
|
|
2628
|
+
return normalized;
|
|
2629
|
+
}
|
|
2188
2630
|
}
|
|
2189
2631
|
const nestedKeys = ['body', 'data', 'payload', 'response'];
|
|
2190
2632
|
for (const key of nestedKeys) {
|
|
@@ -2199,6 +2641,89 @@ export class RouteCodexHttpServer {
|
|
|
2199
2641
|
}
|
|
2200
2642
|
return undefined;
|
|
2201
2643
|
}
|
|
2644
|
+
normalizeSseWrapperErrorValue(value, depth) {
|
|
2645
|
+
if (value === undefined || value === null || depth < 0) {
|
|
2646
|
+
return undefined;
|
|
2647
|
+
}
|
|
2648
|
+
if (typeof value === 'string') {
|
|
2649
|
+
const trimmed = value.trim();
|
|
2650
|
+
if (!trimmed) {
|
|
2651
|
+
return undefined;
|
|
2652
|
+
}
|
|
2653
|
+
if (depth > 0 && (trimmed.startsWith('{') || trimmed.startsWith('['))) {
|
|
2654
|
+
try {
|
|
2655
|
+
const parsed = JSON.parse(trimmed);
|
|
2656
|
+
const parsedInfo = this.normalizeSseWrapperErrorValue(parsed, depth - 1);
|
|
2657
|
+
if (parsedInfo) {
|
|
2658
|
+
return parsedInfo;
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
catch {
|
|
2662
|
+
// fallback to raw string
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
return {
|
|
2666
|
+
message: trimmed,
|
|
2667
|
+
retryable: isRetryableSseWrapperError(trimmed)
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
2671
|
+
return undefined;
|
|
2672
|
+
}
|
|
2673
|
+
const record = value;
|
|
2674
|
+
const directMessage = firstNonEmptyString([
|
|
2675
|
+
record.message,
|
|
2676
|
+
record.error_message,
|
|
2677
|
+
record.errorMessage
|
|
2678
|
+
]);
|
|
2679
|
+
const directCode = firstNonEmptyString([
|
|
2680
|
+
record.code,
|
|
2681
|
+
record.error_code,
|
|
2682
|
+
record.errorCode,
|
|
2683
|
+
record.type
|
|
2684
|
+
]);
|
|
2685
|
+
const directStatus = firstFiniteNumber([
|
|
2686
|
+
record.status,
|
|
2687
|
+
record.statusCode,
|
|
2688
|
+
record.status_code,
|
|
2689
|
+
record.http_status
|
|
2690
|
+
]);
|
|
2691
|
+
if (depth > 0) {
|
|
2692
|
+
for (const key of ['error', 'data', 'payload', 'details', 'body', 'response']) {
|
|
2693
|
+
const nestedInfo = this.normalizeSseWrapperErrorValue(record[key], depth - 1);
|
|
2694
|
+
if (nestedInfo) {
|
|
2695
|
+
const mergedCode = nestedInfo.errorCode ?? directCode;
|
|
2696
|
+
const retryable = nestedInfo.retryable || isRetryableSseWrapperError(nestedInfo.message, mergedCode, directStatus);
|
|
2697
|
+
return {
|
|
2698
|
+
message: nestedInfo.message,
|
|
2699
|
+
...(mergedCode ? { errorCode: mergedCode } : {}),
|
|
2700
|
+
retryable
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
if (directMessage) {
|
|
2706
|
+
return {
|
|
2707
|
+
message: directMessage,
|
|
2708
|
+
...(directCode ? { errorCode: directCode } : {}),
|
|
2709
|
+
retryable: isRetryableSseWrapperError(directMessage, directCode, directStatus)
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
try {
|
|
2713
|
+
const serialized = JSON.stringify(record);
|
|
2714
|
+
if (serialized && serialized !== '{}') {
|
|
2715
|
+
return {
|
|
2716
|
+
message: serialized,
|
|
2717
|
+
...(directCode ? { errorCode: directCode } : {}),
|
|
2718
|
+
retryable: isRetryableSseWrapperError(serialized, directCode, directStatus)
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
catch {
|
|
2723
|
+
// ignore stringify failures
|
|
2724
|
+
}
|
|
2725
|
+
return undefined;
|
|
2726
|
+
}
|
|
2202
2727
|
extractClientModelId(metadata, originalRequest) {
|
|
2203
2728
|
const candidates = [
|
|
2204
2729
|
metadata.clientModelId,
|