@jsonstudio/rcc 0.89.164 → 0.89.333
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 +18 -15
- package/dist/build-info.js +3 -3
- package/dist/build-info.js.map +1 -1
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +28 -5
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/error-handling/quiet-error-handling-center.d.ts +9 -0
- package/dist/error-handling/quiet-error-handling-center.js +141 -0
- package/dist/error-handling/quiet-error-handling-center.js.map +1 -0
- package/dist/error-handling/route-error-hub.js +8 -2
- package/dist/error-handling/route-error-hub.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.d.ts +2 -0
- package/dist/modules/pipeline/utils/colored-logger.js +20 -3
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/providers/auth/antigravity-userinfo-helper.d.ts +10 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js +140 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -0
- package/dist/providers/auth/oauth-lifecycle.js +140 -8
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/token-scanner/index.d.ts +32 -0
- package/dist/providers/auth/token-scanner/index.js +86 -0
- package/dist/providers/auth/token-scanner/index.js.map +1 -0
- package/dist/providers/auth/tokenfile-auth.d.ts +17 -0
- package/dist/providers/auth/tokenfile-auth.js +27 -5
- package/dist/providers/auth/tokenfile-auth.js.map +1 -1
- package/dist/providers/core/api/provider-types.d.ts +10 -0
- package/dist/providers/core/config/oauth-flows.d.ts +2 -0
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.js +85 -0
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +15 -1
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.d.ts +8 -1
- package/dist/providers/core/runtime/base-provider.js +176 -108
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +9 -5
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +271 -69
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-http-provider.d.ts +1 -2
- package/dist/providers/core/runtime/gemini-http-provider.js +0 -12
- package/dist/providers/core/runtime/gemini-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.d.ts +42 -0
- package/dist/providers/core/runtime/http-request-executor.js +133 -0
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -0
- package/dist/providers/core/runtime/http-transport-provider.d.ts +32 -12
- package/dist/providers/core/runtime/http-transport-provider.js +464 -426
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.d.ts +25 -0
- package/dist/providers/core/runtime/provider-error-classifier.js +149 -0
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -0
- package/dist/providers/core/runtime/provider-error-types.d.ts +23 -0
- package/dist/providers/core/runtime/provider-error-types.js +2 -0
- package/dist/providers/core/runtime/provider-error-types.js.map +1 -0
- package/dist/providers/core/runtime/provider-factory.d.ts +1 -0
- package/dist/providers/core/runtime/provider-factory.js +43 -8
- package/dist/providers/core/runtime/provider-factory.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.d.ts +3 -3
- package/dist/providers/core/runtime/responses-provider.js +48 -114
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +11 -3
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.d.ts +5 -0
- package/dist/providers/core/utils/http-client.js +29 -3
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-reporter.js +8 -2
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.js +5 -1
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/profile/provider-profile-loader.js +8 -4
- package/dist/providers/profile/provider-profile-loader.js.map +1 -1
- package/dist/runtime/runtime-flags.d.ts +4 -0
- package/dist/runtime/runtime-flags.js +32 -0
- package/dist/runtime/runtime-flags.js.map +1 -0
- package/dist/server/handlers/handler-utils.js +29 -2
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/messages-handler.js +27 -26
- package/dist/server/handlers/messages-handler.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +35 -1
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +1 -0
- package/dist/server/runtime/http-server/index.js +39 -4
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +4 -0
- package/dist/server/runtime/http-server/request-executor.js +99 -4
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/utils/sse-request-parser.d.ts +1 -0
- package/dist/server/utils/sse-request-parser.js +17 -6
- package/dist/server/utils/sse-request-parser.js.map +1 -1
- package/dist/server/utils/warmup-detector.d.ts +7 -0
- package/dist/server/utils/warmup-detector.js +125 -0
- package/dist/server/utils/warmup-detector.js.map +1 -0
- package/dist/server/utils/warmup-storm-tracker.d.ts +9 -0
- package/dist/server/utils/warmup-storm-tracker.js +61 -0
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -0
- package/dist/utils/debug-utils.js +14 -0
- package/dist/utils/debug-utils.js.map +1 -1
- package/dist/utils/error-handler-registry.js +6 -5
- package/dist/utils/error-handler-registry.js.map +1 -1
- package/dist/utils/error-handling-utils.js +4 -3
- package/dist/utils/error-handling-utils.js.map +1 -1
- package/dist/utils/log-helpers.d.ts +6 -0
- package/dist/utils/log-helpers.js +90 -0
- package/dist/utils/log-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +55 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/snapshot-writer.js +2 -6
- package/dist/utils/snapshot-writer.js.map +1 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/codecs/gemini-openai-codec.js +15 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.d.ts +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.js +25 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.d.ts +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.js +155 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.js +264 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.d.ts +3 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.js +209 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.d.ts +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.js +63 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.d.ts +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.js +85 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.d.ts +5 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.js +121 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.d.ts +5 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.js +76 -0
- package/{dist/providers/compat/utils/snapshot-writer.d.ts → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.d.ts} +2 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.js +21 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.d.ts +6 -0
- package/{dist/providers/compat/glm/utils/tool-schema-helpers.js → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.js} +6 -1
- package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.d.ts +17 -22
- package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.js +35 -99
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +187 -13
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +177 -9
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +10 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +14 -10
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +7 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +5 -665
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.d.ts +9 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +845 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +47 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +35 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +3 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/response-runtime.js +19 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.d.ts +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.js +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +51 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.d.ts +4 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.js +62 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-response-utils.js +23 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-canonicalizer.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +11 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +251 -12
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +11 -4
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +21 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +76 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +11 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +187 -28
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +22 -457
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/health-manager.js +2 -7
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.d.ts +7 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +66 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +6 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.js +16 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.d.ts +15 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.js +56 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.d.ts +13 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.js +403 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +21 -1
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +1 -0
- package/node_modules/@jsonstudio/llms/package.json +2 -2
- package/package.json +13 -11
- package/scripts/README.md +26 -12
- package/scripts/auth-antigravity-token.mjs +64 -0
- package/scripts/auth-gemini-cli-token.mjs +96 -0
- package/scripts/auth-iflow-manual.mjs +81 -0
- package/scripts/auth-iflow-token-direct.mjs +87 -0
- package/scripts/auth-iflow-token.mjs +77 -0
- package/scripts/copy-compat-assets.mjs +3 -15
- package/scripts/install-verify.mjs +1 -0
- package/scripts/pack-mode.mjs +30 -1
- package/scripts/publish-rcc.mjs +31 -0
- package/scripts/replay-codex-sample.mjs +13 -8
- package/scripts/tests/chat-pipeline-blackbox.mjs +1 -1
- package/scripts/tools/capture-provider-goldens.mjs +8 -7
- package/scripts/verify-client-headers.mjs +224 -0
- package/dist/providers/compat/base-compatibility.d.ts +0 -27
- package/dist/providers/compat/base-compatibility.js +0 -143
- package/dist/providers/compat/base-compatibility.js.map +0 -1
- package/dist/providers/compat/compat-directory-loader.d.ts +0 -4
- package/dist/providers/compat/compat-directory-loader.js +0 -85
- package/dist/providers/compat/compat-directory-loader.js.map +0 -1
- package/dist/providers/compat/compatibility-adapter.d.ts +0 -18
- package/dist/providers/compat/compatibility-adapter.js +0 -104
- package/dist/providers/compat/compatibility-adapter.js.map +0 -1
- package/dist/providers/compat/compatibility-factory.d.ts +0 -57
- package/dist/providers/compat/compatibility-factory.js +0 -155
- package/dist/providers/compat/compatibility-factory.js.map +0 -1
- package/dist/providers/compat/compatibility-interface.d.ts +0 -35
- package/dist/providers/compat/compatibility-interface.js +0 -2
- package/dist/providers/compat/compatibility-interface.js.map +0 -1
- package/dist/providers/compat/compatibility-manager.d.ts +0 -85
- package/dist/providers/compat/compatibility-manager.js +0 -368
- package/dist/providers/compat/compatibility-manager.js.map +0 -1
- package/dist/providers/compat/config/config-compatibility.d.ts +0 -28
- package/dist/providers/compat/config/config-compatibility.js +0 -95
- package/dist/providers/compat/config/config-compatibility.js.map +0 -1
- package/dist/providers/compat/field-mapping.d.ts +0 -102
- package/dist/providers/compat/field-mapping.js +0 -447
- package/dist/providers/compat/field-mapping.js.map +0 -1
- package/dist/providers/compat/filters/blacklist-sanitizer.d.ts +0 -45
- package/dist/providers/compat/filters/blacklist-sanitizer.js +0 -133
- package/dist/providers/compat/filters/blacklist-sanitizer.js.map +0 -1
- package/dist/providers/compat/filters/response-blacklist-sanitizer.d.ts +0 -28
- package/dist/providers/compat/filters/response-blacklist-sanitizer.js +0 -138
- package/dist/providers/compat/filters/response-blacklist-sanitizer.js.map +0 -1
- package/dist/providers/compat/filters/universal-shape-filter.js.map +0 -1
- package/dist/providers/compat/glm/config/blacklist-rules.json +0 -22
- package/dist/providers/compat/glm/config/field-mappings.json +0 -92
- package/dist/providers/compat/glm/config/response-blacklist.json +0 -7
- package/dist/providers/compat/glm/config/shape-filters.json +0 -37
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.d.ts +0 -28
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js +0 -306
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js.map +0 -1
- package/dist/providers/compat/glm/functions/glm-processor.d.ts +0 -50
- package/dist/providers/compat/glm/functions/glm-processor.js +0 -134
- package/dist/providers/compat/glm/functions/glm-processor.js.map +0 -1
- package/dist/providers/compat/glm/glm-compatibility.d.ts +0 -34
- package/dist/providers/compat/glm/glm-compatibility.js +0 -117
- package/dist/providers/compat/glm/glm-compatibility.js.map +0 -1
- package/dist/providers/compat/glm/hooks/base-hook.d.ts +0 -21
- package/dist/providers/compat/glm/hooks/base-hook.js +0 -53
- package/dist/providers/compat/glm/hooks/base-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.d.ts +0 -24
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js +0 -268
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.d.ts +0 -21
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js +0 -171
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.d.ts +0 -25
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js +0 -236
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.d.ts +0 -26
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js +0 -186
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js.map +0 -1
- package/dist/providers/compat/glm/index.d.ts +0 -24
- package/dist/providers/compat/glm/index.js +0 -29
- package/dist/providers/compat/glm/index.js.map +0 -1
- package/dist/providers/compat/glm/utils/tool-schema-helpers.d.ts +0 -3
- package/dist/providers/compat/glm/utils/tool-schema-helpers.js.map +0 -1
- package/dist/providers/compat/iflow/config/field-mappings.json +0 -92
- package/dist/providers/compat/iflow/config/shape-filters.json +0 -37
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.d.ts +0 -34
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js +0 -386
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js.map +0 -1
- package/dist/providers/compat/iflow/functions/iflow-processor.d.ts +0 -53
- package/dist/providers/compat/iflow/functions/iflow-processor.js +0 -215
- package/dist/providers/compat/iflow/functions/iflow-processor.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/base-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/base-hook.js +0 -59
- package/dist/providers/compat/iflow/hooks/base-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js +0 -279
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.d.ts +0 -20
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js +0 -180
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js +0 -232
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.d.ts +0 -25
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js +0 -216
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js.map +0 -1
- package/dist/providers/compat/iflow/iflow-compatibility.d.ts +0 -24
- package/dist/providers/compat/iflow/iflow-compatibility.js +0 -94
- package/dist/providers/compat/iflow/iflow-compatibility.js.map +0 -1
- package/dist/providers/compat/index.d.ts +0 -59
- package/dist/providers/compat/index.js +0 -83
- package/dist/providers/compat/index.js.map +0 -1
- package/dist/providers/compat/lmstudio-compatibility.d.ts +0 -44
- package/dist/providers/compat/lmstudio-compatibility.js +0 -193
- package/dist/providers/compat/lmstudio-compatibility.js.map +0 -1
- package/dist/providers/compat/passthrough-compatibility.d.ts +0 -29
- package/dist/providers/compat/passthrough-compatibility.js +0 -83
- package/dist/providers/compat/passthrough-compatibility.js.map +0 -1
- package/dist/providers/compat/profiles/chat/glm/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/glm/index.js +0 -6
- package/dist/providers/compat/profiles/chat/glm/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/iflow/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/iflow/index.js +0 -6
- package/dist/providers/compat/profiles/chat/iflow/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/lmstudio/index.js +0 -6
- package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/qwen/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/qwen/index.js +0 -6
- package/dist/providers/compat/profiles/chat/qwen/index.js.map +0 -1
- package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +0 -6
- package/dist/providers/compat/profiles/compat/passthrough/index.js +0 -6
- package/dist/providers/compat/profiles/compat/passthrough/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/c4m/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/c4m/index.js +0 -6
- package/dist/providers/compat/profiles/responses/c4m/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/default/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/default/index.js +0 -6
- package/dist/providers/compat/profiles/responses/default/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/fai/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/fai/index.js +0 -6
- package/dist/providers/compat/profiles/responses/fai/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/fc/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/fc/index.js +0 -6
- package/dist/providers/compat/profiles/responses/fc/index.js.map +0 -1
- package/dist/providers/compat/qwen/index.d.ts +0 -4
- package/dist/providers/compat/qwen/index.js +0 -6
- package/dist/providers/compat/qwen/index.js.map +0 -1
- package/dist/providers/compat/qwen-compatibility.d.ts +0 -52
- package/dist/providers/compat/qwen-compatibility.js +0 -330
- package/dist/providers/compat/qwen-compatibility.js.map +0 -1
- package/dist/providers/compat/register-compat-module.d.ts +0 -8
- package/dist/providers/compat/register-compat-module.js +0 -53
- package/dist/providers/compat/register-compat-module.js.map +0 -1
- package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +0 -27
- package/dist/providers/compat/responses/c4m-responses-compatibility.js +0 -197
- package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +0 -1
- package/dist/providers/compat/standard-compatibility-utils.d.ts +0 -1
- package/dist/providers/compat/standard-compatibility-utils.js +0 -77
- package/dist/providers/compat/standard-compatibility-utils.js.map +0 -1
- package/dist/providers/compat/standard-compatibility.d.ts +0 -31
- package/dist/providers/compat/standard-compatibility.js +0 -118
- package/dist/providers/compat/standard-compatibility.js.map +0 -1
- package/dist/providers/compat/utils/snapshot-writer.js +0 -62
- package/dist/providers/compat/utils/snapshot-writer.js.map +0 -1
- package/scripts/check-glm-compat.mjs +0 -47
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* 各协议具体行为(OpenAI Chat、Responses、Anthropic、Gemini 等)通过子类覆写钩子实现。
|
|
10
10
|
*/
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
import fs from 'node:fs/promises';
|
|
11
13
|
import { BaseProvider } from './base-provider.js';
|
|
12
14
|
import { HttpClient } from '../utils/http-client.js';
|
|
13
15
|
import { DynamicProfileLoader, ServiceProfileValidator } from '../config/service-profiles.js';
|
|
@@ -16,83 +18,35 @@ import { OAuthAuthProvider } from '../../auth/oauth-auth.js';
|
|
|
16
18
|
import { logOAuthDebug } from '../../auth/oauth-logger.js';
|
|
17
19
|
import { TokenFileAuthProvider } from '../../auth/tokenfile-auth.js';
|
|
18
20
|
import { ensureValidOAuthToken, handleUpstreamInvalidOAuthToken } from '../../auth/oauth-lifecycle.js';
|
|
19
|
-
import {
|
|
20
|
-
import { attachProviderSseSnapshotStream,
|
|
21
|
-
import { attachProviderRuntimeMetadata } from './provider-runtime-metadata.js';
|
|
21
|
+
import { fetchAntigravityProjectId } from '../../auth/antigravity-userinfo-helper.js';
|
|
22
|
+
import { attachProviderSseSnapshotStream, writeProviderSnapshot } from '../utils/snapshot-writer.js';
|
|
23
|
+
import { attachProviderRuntimeMetadata, extractProviderRuntimeMetadata } from './provider-runtime-metadata.js';
|
|
22
24
|
import { OpenAIChatProtocolClient } from '../../../client/openai/chat-protocol-client.js';
|
|
23
|
-
|
|
25
|
+
import { HttpRequestExecutor } from './http-request-executor.js';
|
|
26
|
+
import { extractStatusCodeFromError } from './provider-error-classifier.js';
|
|
24
27
|
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
25
|
-
|
|
26
|
-
const passthrough = async (_stage, _target, data) => ({
|
|
27
|
-
data,
|
|
28
|
-
metrics: {
|
|
29
|
-
executionTime: 0,
|
|
30
|
-
hookCount: 0,
|
|
31
|
-
successCount: 0,
|
|
32
|
-
readCount: 0,
|
|
33
|
-
writeCount: 0,
|
|
34
|
-
transformCount: 0
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
return {
|
|
38
|
-
getBidirectionalHookManager: () => ({
|
|
39
|
-
registerHook: () => { },
|
|
40
|
-
unregisterHook: () => { },
|
|
41
|
-
executeHookChain: passthrough,
|
|
42
|
-
setDebugConfig: () => { }
|
|
43
|
-
}),
|
|
44
|
-
setDebugConfig: () => { },
|
|
45
|
-
initialize: async () => { },
|
|
46
|
-
getStats: () => ({ enabled: false }),
|
|
47
|
-
healthCheck: async () => ({ healthy: true }),
|
|
48
|
-
start: async () => { },
|
|
49
|
-
stop: async () => { },
|
|
50
|
-
shutdown: async () => { }
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const readStatusCodeFromResponse = (error) => {
|
|
54
|
-
const response = error?.response;
|
|
55
|
-
const directStatus = typeof response?.status === 'number' ? response.status : undefined;
|
|
56
|
-
const directStatusCode = typeof response?.statusCode === 'number'
|
|
57
|
-
? response.statusCode
|
|
58
|
-
: undefined;
|
|
59
|
-
const nestedStatus = response &&
|
|
60
|
-
typeof response === 'object' &&
|
|
61
|
-
typeof response?.data?.status === 'number'
|
|
62
|
-
? response.data.status
|
|
63
|
-
: undefined;
|
|
64
|
-
const nestedErrorStatus = response &&
|
|
65
|
-
typeof response === 'object' &&
|
|
66
|
-
typeof response?.data?.error?.status === 'number'
|
|
67
|
-
? response.data.error.status
|
|
68
|
-
: undefined;
|
|
69
|
-
return [directStatus, directStatusCode, nestedStatus, nestedErrorStatus].find((candidate) => typeof candidate === 'number' && Number.isFinite(candidate));
|
|
70
|
-
};
|
|
71
|
-
const DEFAULT_USER_AGENT = 'RouteCodex/2.0';
|
|
28
|
+
const DEFAULT_USER_AGENT = 'codex_cli_rs/0.73.0 (Mac OS 15.6.1; arm64) iTerm.app/3.6.5';
|
|
72
29
|
export class HttpTransportProvider extends BaseProvider {
|
|
73
30
|
type;
|
|
74
31
|
authProvider = null;
|
|
75
32
|
httpClient;
|
|
76
33
|
serviceProfile;
|
|
77
|
-
hookSystemIntegration;
|
|
78
34
|
protocolClient;
|
|
35
|
+
requestExecutor;
|
|
79
36
|
injectedConfig = null;
|
|
80
|
-
hooksEnabled;
|
|
81
37
|
constructor(config, dependencies, moduleType, protocolClient) {
|
|
82
38
|
super(config, dependencies);
|
|
83
39
|
this.type = moduleType;
|
|
84
40
|
this.protocolClient = protocolClient ?? new OpenAIChatProtocolClient();
|
|
85
|
-
this.hooksEnabled = ENABLE_PROVIDER_HOOKS;
|
|
86
41
|
// 获取服务配置档案
|
|
87
42
|
this.serviceProfile = this.getServiceProfile();
|
|
88
43
|
// 验证配置
|
|
89
44
|
this.validateConfig();
|
|
90
45
|
// 创建HTTP客户端
|
|
91
46
|
this.createHttpClient();
|
|
47
|
+
this.requestExecutor = new HttpRequestExecutor(this.httpClient, this.createRequestExecutorDeps());
|
|
92
48
|
// 创建认证提供者
|
|
93
49
|
this.authProvider = this.createAuthProvider();
|
|
94
|
-
// 初始化Hook系统集成
|
|
95
|
-
this.hookSystemIntegration = this.initializeHookSystem();
|
|
96
50
|
}
|
|
97
51
|
/**
|
|
98
52
|
* 确保认证提供者完成初始化(避免 ApiKeyAuthProvider 未初始化导致的报错)
|
|
@@ -104,9 +58,10 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
104
58
|
const providerConfig = this.config.config;
|
|
105
59
|
const extensions = this.getConfigExtensions();
|
|
106
60
|
const auth = providerConfig.auth;
|
|
61
|
+
const usesTokenFile = this.authProvider instanceof TokenFileAuthProvider;
|
|
107
62
|
if (this.normalizeAuthMode(auth.type) === 'oauth') {
|
|
108
63
|
const oauthAuth = auth;
|
|
109
|
-
const oauthProviderId = this.ensureOAuthProviderId(oauthAuth, extensions);
|
|
64
|
+
const oauthProviderId = this.oauthProviderId || this.ensureOAuthProviderId(oauthAuth, extensions);
|
|
110
65
|
const forceReauthorize = false;
|
|
111
66
|
const tokenFileHint = oauthAuth.tokenFile ?? '(default)';
|
|
112
67
|
logOAuthDebug(`[OAuth] [init] provider=${oauthProviderId} type=${auth.type} tokenFile=${tokenFileHint} forceReauth=${forceReauthorize}`);
|
|
@@ -121,9 +76,13 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
121
76
|
openBrowser: true,
|
|
122
77
|
forceReauthorize
|
|
123
78
|
});
|
|
79
|
+
if (this.oauthProviderId === 'antigravity') {
|
|
80
|
+
await this.ensureAntigravityProjectMetadata(oauthAuth);
|
|
81
|
+
}
|
|
124
82
|
logOAuthDebug('[OAuth] [init] ensureValid OK');
|
|
125
83
|
try {
|
|
126
84
|
if (this.authProvider instanceof TokenFileAuthProvider) {
|
|
85
|
+
// 令牌文件可能在 ensureValidOAuthToken 中被创建/更新,重新加载一次
|
|
127
86
|
await this.authProvider.initialize();
|
|
128
87
|
}
|
|
129
88
|
else {
|
|
@@ -142,17 +101,11 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
142
101
|
const msg = err?.message ? String(err.message) : String(error);
|
|
143
102
|
console.error(`[OAuth] [init] ensureValid ERROR: ${msg}`);
|
|
144
103
|
this.dependencies.logger?.logModule?.(this.id, 'oauth-init-error', {
|
|
145
|
-
providerType:
|
|
104
|
+
providerType: oauthProviderId,
|
|
146
105
|
error: msg
|
|
147
106
|
});
|
|
148
107
|
throw error;
|
|
149
108
|
}
|
|
150
|
-
try {
|
|
151
|
-
this.authProvider.getOAuthClient?.()?.loadToken?.();
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
// ignore
|
|
155
|
-
}
|
|
156
109
|
}
|
|
157
110
|
else {
|
|
158
111
|
try {
|
|
@@ -163,14 +116,6 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
163
116
|
}
|
|
164
117
|
}
|
|
165
118
|
}
|
|
166
|
-
if (this.hooksEnabled) {
|
|
167
|
-
await this.hookSystemIntegration.initialize();
|
|
168
|
-
this.configureHookDebugging();
|
|
169
|
-
this.dependencies.logger?.logModule(this.id, 'provider-hook-system-initialized', {
|
|
170
|
-
providerType: this.providerType,
|
|
171
|
-
integrationEnabled: true
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
119
|
}
|
|
175
120
|
catch (error) {
|
|
176
121
|
// 暴露问题,快速失败,便于定位凭证问题
|
|
@@ -200,62 +145,6 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
200
145
|
getConfig() {
|
|
201
146
|
return this.injectedConfig ?? this.config.config ?? null;
|
|
202
147
|
}
|
|
203
|
-
/**
|
|
204
|
-
* 初始化Hook系统集成
|
|
205
|
-
*/
|
|
206
|
-
initializeHookSystem() {
|
|
207
|
-
if (!this.hooksEnabled) {
|
|
208
|
-
return createNoopHookSystemIntegration();
|
|
209
|
-
}
|
|
210
|
-
return createHookSystemIntegration(this.dependencies, this.id, {
|
|
211
|
-
enabled: true,
|
|
212
|
-
debugMode: true,
|
|
213
|
-
snapshotEnabled: true,
|
|
214
|
-
migrationMode: true
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* 配置Hook调试(保持向后兼容)
|
|
219
|
-
*/
|
|
220
|
-
configureHookDebugging() {
|
|
221
|
-
if (!this.hooksEnabled) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
// 设置调试配置(使用统一Hook系统的阶段字符串)
|
|
226
|
-
const debugConfig = {
|
|
227
|
-
enabled: true,
|
|
228
|
-
level: 'verbose',
|
|
229
|
-
maxDataSize: 1024 * 64, // 64KB 单次输出上限,避免过大控制台噪声
|
|
230
|
-
stages: [
|
|
231
|
-
'request_preprocessing',
|
|
232
|
-
'request_validation',
|
|
233
|
-
'authentication',
|
|
234
|
-
'http_request',
|
|
235
|
-
'http_response',
|
|
236
|
-
'response_validation',
|
|
237
|
-
'response_postprocessing',
|
|
238
|
-
'error_handling'
|
|
239
|
-
],
|
|
240
|
-
outputFormat: 'structured',
|
|
241
|
-
outputTargets: ['console'],
|
|
242
|
-
performanceThresholds: {
|
|
243
|
-
maxHookExecutionTime: 500, // 单个Hook 500ms告警
|
|
244
|
-
maxTotalExecutionTime: 5000, // 阶段总时长 5s 告警
|
|
245
|
-
maxDataSize: 1024 * 256 // 256KB 数据告警
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
this.hookSystemIntegration.setDebugConfig(debugConfig);
|
|
249
|
-
this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-configured', {
|
|
250
|
-
providerType: this.providerType
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-error', {
|
|
255
|
-
error: error instanceof Error ? error.message : String(error)
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
148
|
getServiceProfile() {
|
|
260
149
|
const cfg = this.config.config;
|
|
261
150
|
const profileKey = this.resolveProfileKey(cfg);
|
|
@@ -349,15 +238,16 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
349
238
|
const auth = this.config.config.auth;
|
|
350
239
|
const extensions = this.getConfigExtensions();
|
|
351
240
|
const authMode = this.normalizeAuthMode(auth.type);
|
|
352
|
-
|
|
241
|
+
this.authMode = authMode;
|
|
242
|
+
const resolvedOAuthProviderId = authMode === 'oauth'
|
|
353
243
|
? this.ensureOAuthProviderId(auth, extensions)
|
|
354
|
-
:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const validation = ServiceProfileValidator.validateServiceProfile(
|
|
244
|
+
: undefined;
|
|
245
|
+
const serviceProfileKey = this.type === 'gemini-cli-http-provider'
|
|
246
|
+
? 'gemini-cli'
|
|
247
|
+
: (resolvedOAuthProviderId ?? this.providerType);
|
|
248
|
+
const validation = ServiceProfileValidator.validateServiceProfile(serviceProfileKey, authMode);
|
|
359
249
|
if (!validation.isValid) {
|
|
360
|
-
throw new Error(`Invalid auth configuration for ${
|
|
250
|
+
throw new Error(`Invalid auth configuration for ${serviceProfileKey}: ${validation.errors.join(', ')}`);
|
|
361
251
|
}
|
|
362
252
|
// 根据认证类型创建对应的认证提供者
|
|
363
253
|
if (authMode === 'apikey') {
|
|
@@ -365,16 +255,20 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
365
255
|
}
|
|
366
256
|
else if (authMode === 'oauth') {
|
|
367
257
|
const oauthAuth = auth;
|
|
258
|
+
const oauthProviderId = resolvedOAuthProviderId ?? serviceProfileKey;
|
|
259
|
+
this.oauthProviderId = oauthProviderId;
|
|
368
260
|
// For providers like Qwen/iflow/Gemini CLI where public OAuth client may not be available,
|
|
369
261
|
// allow reading tokens produced by external login tools (CLIProxyAPI) via token file.
|
|
370
|
-
const useTokenFile = (
|
|
262
|
+
const useTokenFile = (oauthProviderId === 'qwen' ||
|
|
263
|
+
oauthProviderId === 'iflow' ||
|
|
264
|
+
this.type === 'gemini-cli-http-provider') &&
|
|
371
265
|
!oauthAuth.clientId &&
|
|
372
266
|
!oauthAuth.tokenUrl &&
|
|
373
267
|
!oauthAuth.deviceCodeUrl;
|
|
374
268
|
if (useTokenFile) {
|
|
375
269
|
return new TokenFileAuthProvider(oauthAuth);
|
|
376
270
|
}
|
|
377
|
-
return new OAuthAuthProvider(oauthAuth,
|
|
271
|
+
return new OAuthAuthProvider(oauthAuth, oauthProviderId);
|
|
378
272
|
}
|
|
379
273
|
else {
|
|
380
274
|
throw new Error(`Unsupported auth type: ${auth.type}`);
|
|
@@ -405,9 +299,43 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
405
299
|
}
|
|
406
300
|
});
|
|
407
301
|
}
|
|
302
|
+
createRequestExecutorDeps() {
|
|
303
|
+
return {
|
|
304
|
+
wantsUpstreamSse: this.wantsUpstreamSse.bind(this),
|
|
305
|
+
getEffectiveEndpoint: () => this.getEffectiveEndpoint(),
|
|
306
|
+
resolveRequestEndpoint: this.resolveRequestEndpoint.bind(this),
|
|
307
|
+
buildRequestHeaders: this.buildRequestHeaders.bind(this),
|
|
308
|
+
finalizeRequestHeaders: this.finalizeRequestHeaders.bind(this),
|
|
309
|
+
applyStreamModeHeaders: this.applyStreamModeHeaders.bind(this),
|
|
310
|
+
getEffectiveBaseUrl: () => this.getEffectiveBaseUrl(),
|
|
311
|
+
buildHttpRequestBody: this.buildHttpRequestBody.bind(this),
|
|
312
|
+
prepareSseRequestBody: this.prepareSseRequestBody.bind(this),
|
|
313
|
+
getEntryEndpointFromPayload: this.getEntryEndpointFromPayload.bind(this),
|
|
314
|
+
getClientRequestIdFromContext: this.getClientRequestIdFromContext.bind(this),
|
|
315
|
+
wrapUpstreamSseResponse: this.wrapUpstreamSseResponse.bind(this),
|
|
316
|
+
getHttpRetryLimit: () => this.getHttpRetryLimit(),
|
|
317
|
+
shouldRetryHttpError: this.shouldRetryHttpError.bind(this),
|
|
318
|
+
delayBeforeHttpRetry: this.delayBeforeHttpRetry.bind(this),
|
|
319
|
+
tryRecoverOAuthAndReplay: this.tryRecoverOAuthAndReplay.bind(this),
|
|
320
|
+
normalizeHttpError: this.normalizeHttpError.bind(this)
|
|
321
|
+
};
|
|
322
|
+
}
|
|
408
323
|
async preprocessRequest(request) {
|
|
409
324
|
const context = this.createProviderContext();
|
|
410
325
|
const runtimeMetadata = context.runtimeMetadata;
|
|
326
|
+
const headersFromRequest = this.normalizeClientHeaders(request?.metadata?.clientHeaders);
|
|
327
|
+
const headersFromRuntime = this.normalizeClientHeaders(runtimeMetadata?.metadata && typeof runtimeMetadata.metadata === 'object'
|
|
328
|
+
? runtimeMetadata.metadata.clientHeaders
|
|
329
|
+
: undefined);
|
|
330
|
+
const effectiveClientHeaders = headersFromRequest ?? headersFromRuntime;
|
|
331
|
+
if (effectiveClientHeaders) {
|
|
332
|
+
if (runtimeMetadata) {
|
|
333
|
+
if (!runtimeMetadata.metadata || typeof runtimeMetadata.metadata !== 'object') {
|
|
334
|
+
runtimeMetadata.metadata = {};
|
|
335
|
+
}
|
|
336
|
+
runtimeMetadata.metadata.clientHeaders = effectiveClientHeaders;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
411
339
|
const ensureRuntimeMetadata = (payload) => {
|
|
412
340
|
if (!runtimeMetadata || !payload || typeof payload !== 'object') {
|
|
413
341
|
return;
|
|
@@ -433,50 +361,17 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
433
361
|
...processedMetadata,
|
|
434
362
|
...(entryEndpoint ? { entryEndpoint } : {}),
|
|
435
363
|
...(typeof streamFlag === 'boolean' ? { stream: !!streamFlag } : {}),
|
|
364
|
+
...(effectiveClientHeaders ? { clientHeaders: effectiveClientHeaders } : {}),
|
|
436
365
|
__origModel: inboundModel
|
|
437
366
|
};
|
|
438
367
|
}
|
|
439
368
|
catch { /* ignore */ }
|
|
440
|
-
// 流式开关:基础 Provider 统一移除入口层的 stream 标记,
|
|
441
|
-
// 具体协议(如 Responses/Anthropic)的真实流控由各自独立 Provider 处理
|
|
442
|
-
try {
|
|
443
|
-
// 统一:所有入口均移除 stream=true(Provider 始终走非流式),SSE 由上层合成
|
|
444
|
-
const requestBody = processedRequest;
|
|
445
|
-
if (requestBody.stream === true) {
|
|
446
|
-
delete requestBody.stream;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
catch { /* ignore */ }
|
|
450
|
-
// 获取Hook管理器(新的统一系统)
|
|
451
|
-
const hookManager = this.getHookManager();
|
|
452
|
-
// 🔍 Hook 1: 请求预处理阶段
|
|
453
|
-
const preprocessResult = await hookManager.executeHookChain('request_preprocessing', 'request', processedRequest, context);
|
|
454
|
-
processedRequest = preprocessResult.data;
|
|
455
|
-
ensureRuntimeMetadata(processedRequest);
|
|
456
|
-
// 🔍 Hook 2: 请求验证阶段
|
|
457
|
-
const validationResult = await hookManager.executeHookChain('request_validation', 'request', processedRequest, context);
|
|
458
|
-
processedRequest = validationResult.data;
|
|
459
|
-
ensureRuntimeMetadata(processedRequest);
|
|
460
|
-
// Provider 层不再修改工具 schema;统一入口在 llmswitch-core/兼容层
|
|
461
|
-
// Provider 层不做协议兼容改写:compatibility 由 llmswitch-core Hub Pipeline 统一处理。
|
|
462
369
|
return processedRequest;
|
|
463
370
|
}
|
|
464
371
|
async postprocessResponse(response, context) {
|
|
465
372
|
const runtime = this.getRuntimeProfile();
|
|
466
373
|
const processingTime = Date.now() - context.startTime;
|
|
467
374
|
let processedResponse = response;
|
|
468
|
-
// 获取Hook管理器(新的统一系统)
|
|
469
|
-
const hookManager = this.getHookManager();
|
|
470
|
-
// 🔍 Hook 3: HTTP响应阶段
|
|
471
|
-
const httpResponseResult = await hookManager.executeHookChain('http_response', 'response', processedResponse, context);
|
|
472
|
-
processedResponse = httpResponseResult.data;
|
|
473
|
-
// 🔍 Hook 4: 响应验证阶段
|
|
474
|
-
const validationResult = await hookManager.executeHookChain('response_validation', 'response', processedResponse, context);
|
|
475
|
-
processedResponse = validationResult.data;
|
|
476
|
-
// 🔍 Hook 5: 响应后处理阶段
|
|
477
|
-
const postprocessResult = await hookManager.executeHookChain('response_postprocessing', 'response', processedResponse, context);
|
|
478
|
-
processedResponse = postprocessResult.data;
|
|
479
|
-
// Provider 层不做协议兼容改写:compatibility 由 llmswitch-core Hub Pipeline 统一处理。
|
|
480
375
|
const originalRecord = this.asResponseRecord(response);
|
|
481
376
|
const processedRecord = this.asResponseRecord(processedResponse);
|
|
482
377
|
const sseStream = processedRecord.__sse_responses ||
|
|
@@ -494,221 +389,13 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
494
389
|
providerType: this.providerType,
|
|
495
390
|
// 对外暴露的 model 统一为入站模型
|
|
496
391
|
model: context.model ?? this.extractModel(processedRecord) ?? this.extractModel(originalRecord),
|
|
497
|
-
usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord)
|
|
498
|
-
hookMetrics: {
|
|
499
|
-
httpResponse: httpResponseResult.metrics,
|
|
500
|
-
validation: validationResult.metrics,
|
|
501
|
-
postprocess: postprocessResult.metrics
|
|
502
|
-
}
|
|
392
|
+
usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord)
|
|
503
393
|
}
|
|
504
394
|
};
|
|
505
395
|
}
|
|
506
396
|
async sendRequestInternal(request) {
|
|
507
397
|
const context = this.createProviderContext();
|
|
508
|
-
|
|
509
|
-
const hookManager = this.getHookManager();
|
|
510
|
-
// 🔍 Hook 8: HTTP请求阶段
|
|
511
|
-
const httpRequestResult = await hookManager.executeHookChain('http_request', 'request', request, context);
|
|
512
|
-
const processedRequest = httpRequestResult.data;
|
|
513
|
-
const wantsSse = this.wantsUpstreamSse(processedRequest, context);
|
|
514
|
-
// 仅传入 endpoint,让 HttpClient 按 baseUrl 进行拼接;避免 full URL 再次拼接导致 /https:/ 重复
|
|
515
|
-
const defaultEndpoint = this.getEffectiveEndpoint();
|
|
516
|
-
const endpoint = this.resolveRequestEndpoint(processedRequest, defaultEndpoint);
|
|
517
|
-
const headers = await this.buildRequestHeaders();
|
|
518
|
-
let finalHeaders = await this.finalizeRequestHeaders(headers, processedRequest);
|
|
519
|
-
finalHeaders = this.applyStreamModeHeaders(finalHeaders, wantsSse);
|
|
520
|
-
const targetUrl = `${this.getEffectiveBaseUrl().replace(/\/$/, '')}/${endpoint.startsWith('/') ? endpoint.slice(1) : endpoint}`;
|
|
521
|
-
// Flatten request body to standard OpenAI Chat JSON
|
|
522
|
-
const finalBody = this.buildHttpRequestBody(processedRequest);
|
|
523
|
-
if (wantsSse) {
|
|
524
|
-
this.prepareSseRequestBody(finalBody, context);
|
|
525
|
-
}
|
|
526
|
-
const entryEndpoint = this.getEntryEndpointFromPayload(processedRequest);
|
|
527
|
-
const clientRequestId = this.getClientRequestIdFromContext(context);
|
|
528
|
-
// 快照:provider-request(默认开启,脱敏headers)
|
|
529
|
-
try {
|
|
530
|
-
await writeProviderSnapshot({
|
|
531
|
-
phase: 'provider-request',
|
|
532
|
-
requestId: context.requestId,
|
|
533
|
-
data: finalBody,
|
|
534
|
-
headers: finalHeaders,
|
|
535
|
-
url: targetUrl,
|
|
536
|
-
entryEndpoint,
|
|
537
|
-
clientRequestId
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
catch { /* non-blocking */ }
|
|
541
|
-
// 发送HTTP请求(根据是否需要 SSE 决定传输模式)
|
|
542
|
-
let response;
|
|
543
|
-
const captureSse = shouldCaptureProviderStreamSnapshots();
|
|
544
|
-
try {
|
|
545
|
-
if (wantsSse) {
|
|
546
|
-
const upstreamStream = await this.httpClient.postStream(endpoint, finalBody, finalHeaders);
|
|
547
|
-
const streamForHost = captureSse
|
|
548
|
-
? attachProviderSseSnapshotStream(upstreamStream, {
|
|
549
|
-
requestId: context.requestId,
|
|
550
|
-
headers: finalHeaders,
|
|
551
|
-
url: targetUrl,
|
|
552
|
-
entryEndpoint,
|
|
553
|
-
clientRequestId
|
|
554
|
-
})
|
|
555
|
-
: upstreamStream;
|
|
556
|
-
response = await this.wrapUpstreamSseResponse(streamForHost, context);
|
|
557
|
-
if (!captureSse) {
|
|
558
|
-
try {
|
|
559
|
-
await writeProviderSnapshot({
|
|
560
|
-
phase: 'provider-response',
|
|
561
|
-
requestId: context.requestId,
|
|
562
|
-
data: { mode: 'sse' },
|
|
563
|
-
headers: finalHeaders,
|
|
564
|
-
url: targetUrl,
|
|
565
|
-
entryEndpoint,
|
|
566
|
-
clientRequestId
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
catch { /* non-blocking */ }
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
else {
|
|
573
|
-
response = await this.httpClient.post(endpoint, finalBody, finalHeaders);
|
|
574
|
-
try {
|
|
575
|
-
await writeProviderSnapshot({
|
|
576
|
-
phase: 'provider-response',
|
|
577
|
-
requestId: context.requestId,
|
|
578
|
-
data: response,
|
|
579
|
-
headers: finalHeaders,
|
|
580
|
-
url: targetUrl,
|
|
581
|
-
entryEndpoint,
|
|
582
|
-
clientRequestId
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
catch { /* non-blocking */ }
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
catch (error) {
|
|
589
|
-
// OAuth token 失效:尝试刷新/重获并重试一次
|
|
590
|
-
try {
|
|
591
|
-
const providerAuth = this.config.config.auth;
|
|
592
|
-
if (this.normalizeAuthMode(providerAuth.type) === 'oauth') {
|
|
593
|
-
const shouldRetry = await handleUpstreamInvalidOAuthToken(this.providerType, providerAuth, error);
|
|
594
|
-
if (shouldRetry) {
|
|
595
|
-
const retryHeaders = await this.buildRequestHeaders();
|
|
596
|
-
let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
|
|
597
|
-
finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, wantsSse);
|
|
598
|
-
if (wantsSse) {
|
|
599
|
-
const upstreamStream = await this.httpClient.postStream(endpoint, finalBody, finalRetryHeaders);
|
|
600
|
-
const streamForHost = captureSse
|
|
601
|
-
? attachProviderSseSnapshotStream(upstreamStream, {
|
|
602
|
-
requestId: context.requestId,
|
|
603
|
-
headers: finalRetryHeaders,
|
|
604
|
-
url: targetUrl,
|
|
605
|
-
entryEndpoint,
|
|
606
|
-
clientRequestId,
|
|
607
|
-
extra: { retry: true }
|
|
608
|
-
})
|
|
609
|
-
: upstreamStream;
|
|
610
|
-
const wrapped = await this.wrapUpstreamSseResponse(streamForHost, context);
|
|
611
|
-
if (!captureSse) {
|
|
612
|
-
try {
|
|
613
|
-
await writeProviderSnapshot({
|
|
614
|
-
phase: 'provider-response',
|
|
615
|
-
requestId: context.requestId,
|
|
616
|
-
data: { mode: 'sse', retry: true },
|
|
617
|
-
headers: finalRetryHeaders,
|
|
618
|
-
url: targetUrl,
|
|
619
|
-
entryEndpoint,
|
|
620
|
-
clientRequestId
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
catch { /* non-blocking */ }
|
|
624
|
-
}
|
|
625
|
-
return wrapped;
|
|
626
|
-
}
|
|
627
|
-
response = await this.httpClient.post(endpoint, finalBody, finalRetryHeaders);
|
|
628
|
-
try {
|
|
629
|
-
await writeProviderSnapshot({
|
|
630
|
-
phase: 'provider-response',
|
|
631
|
-
requestId: context.requestId,
|
|
632
|
-
data: response,
|
|
633
|
-
headers: finalRetryHeaders,
|
|
634
|
-
url: targetUrl,
|
|
635
|
-
entryEndpoint,
|
|
636
|
-
clientRequestId
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
catch { /* non-blocking */ }
|
|
640
|
-
return response;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
catch { /* ignore and fallthrough */ }
|
|
645
|
-
// 🔍 Hook 9: 错误处理阶段
|
|
646
|
-
const errorResult = await hookManager.executeHookChain('error_handling', 'error', { error, request: processedRequest, url: targetUrl, headers: finalHeaders }, context);
|
|
647
|
-
// 如果Hook处理了错误,使用Hook的返回结果
|
|
648
|
-
const hookErrorData = errorResult.data;
|
|
649
|
-
if (hookErrorData && hookErrorData.error === false) {
|
|
650
|
-
return hookErrorData;
|
|
651
|
-
}
|
|
652
|
-
// 规范化错误:补充结构化字段,移除仅文本填充的旧做法
|
|
653
|
-
const normalized = error;
|
|
654
|
-
try {
|
|
655
|
-
const msg = typeof normalized.message === 'string' ? normalized.message : String(normalized || '');
|
|
656
|
-
const m = msg.match(/HTTP\s+(\d{3})/i);
|
|
657
|
-
const parsedStatus = m ? parseInt(m[1], 10) : undefined;
|
|
658
|
-
const responseStatus = readStatusCodeFromResponse(normalized);
|
|
659
|
-
const statusCode = Number.isFinite(normalized.statusCode)
|
|
660
|
-
? Number(normalized.statusCode)
|
|
661
|
-
: Number.isFinite(normalized.status)
|
|
662
|
-
? Number(normalized.status)
|
|
663
|
-
: responseStatus ?? parsedStatus ?? undefined;
|
|
664
|
-
if (statusCode && !Number.isNaN(statusCode)) {
|
|
665
|
-
normalized.statusCode = statusCode;
|
|
666
|
-
if (!normalized.status) {
|
|
667
|
-
normalized.status = statusCode;
|
|
668
|
-
}
|
|
669
|
-
if (!normalized.code) {
|
|
670
|
-
normalized.code = `HTTP_${statusCode}`;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
// 兼容 Manager 的 code 路径(response.data.error.code)
|
|
674
|
-
if (!normalized.response) {
|
|
675
|
-
normalized.response = {};
|
|
676
|
-
}
|
|
677
|
-
if (!normalized.response.data) {
|
|
678
|
-
normalized.response.data = {};
|
|
679
|
-
}
|
|
680
|
-
if (!normalized.response.data.error) {
|
|
681
|
-
normalized.response.data.error = {};
|
|
682
|
-
}
|
|
683
|
-
if (normalized.code && !normalized.response.data.error.code) {
|
|
684
|
-
normalized.response.data.error.code = normalized.code;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
catch { /* keep original */ }
|
|
688
|
-
// 快照:provider-error(结构化写入)
|
|
689
|
-
try {
|
|
690
|
-
await writeProviderSnapshot({
|
|
691
|
-
phase: 'provider-error',
|
|
692
|
-
requestId: context.requestId,
|
|
693
|
-
data: {
|
|
694
|
-
status: normalized?.statusCode ?? normalized?.status ?? null,
|
|
695
|
-
code: normalized?.code ?? null,
|
|
696
|
-
error: typeof normalized?.message === 'string' ? normalized.message : String(normalized || '')
|
|
697
|
-
},
|
|
698
|
-
headers: finalHeaders,
|
|
699
|
-
url: targetUrl,
|
|
700
|
-
entryEndpoint,
|
|
701
|
-
clientRequestId
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
catch { /* non-blocking */ }
|
|
705
|
-
throw normalized;
|
|
706
|
-
}
|
|
707
|
-
// Provider 不处理工具修复/注入逻辑:统一收敛到 llmswitch-core 与兼容层
|
|
708
|
-
// 此处不做任何自动修复/重试,保持单次请求的幂等与可观测性
|
|
709
|
-
try { /* no-op */ }
|
|
710
|
-
catch { /* ignore */ }
|
|
711
|
-
return response;
|
|
398
|
+
return this.requestExecutor.execute(request, context);
|
|
712
399
|
}
|
|
713
400
|
wantsUpstreamSse(_request, _context) {
|
|
714
401
|
return false;
|
|
@@ -744,6 +431,135 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
744
431
|
return false;
|
|
745
432
|
}
|
|
746
433
|
}
|
|
434
|
+
getHttpRetryLimit() {
|
|
435
|
+
return 3;
|
|
436
|
+
}
|
|
437
|
+
async delayBeforeHttpRetry(attempt) {
|
|
438
|
+
const delay = Math.min(500 * attempt, 2000);
|
|
439
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
440
|
+
}
|
|
441
|
+
shouldRetryHttpError(error, attempt, maxAttempts) {
|
|
442
|
+
if (attempt >= maxAttempts) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
const normalized = error;
|
|
446
|
+
const statusCode = extractStatusCodeFromError(normalized);
|
|
447
|
+
if (statusCode && statusCode >= 500) {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
async tryRecoverOAuthAndReplay(error, requestInfo, processedRequest, captureSse, context) {
|
|
453
|
+
try {
|
|
454
|
+
const providerAuth = this.config.config.auth;
|
|
455
|
+
if (this.normalizeAuthMode(providerAuth.type) !== 'oauth') {
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
const shouldRetry = await handleUpstreamInvalidOAuthToken(this.oauthProviderId || this.providerType, providerAuth, error);
|
|
459
|
+
if (!shouldRetry) {
|
|
460
|
+
return undefined;
|
|
461
|
+
}
|
|
462
|
+
const retryHeaders = await this.buildRequestHeaders();
|
|
463
|
+
let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
|
|
464
|
+
finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, requestInfo.wantsSse);
|
|
465
|
+
if (requestInfo.wantsSse) {
|
|
466
|
+
const upstreamStream = await this.httpClient.postStream(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
|
|
467
|
+
const streamForHost = captureSse
|
|
468
|
+
? attachProviderSseSnapshotStream(upstreamStream, {
|
|
469
|
+
requestId: context.requestId,
|
|
470
|
+
headers: finalRetryHeaders,
|
|
471
|
+
url: requestInfo.targetUrl,
|
|
472
|
+
entryEndpoint: requestInfo.entryEndpoint,
|
|
473
|
+
clientRequestId: requestInfo.clientRequestId,
|
|
474
|
+
extra: { retry: true }
|
|
475
|
+
})
|
|
476
|
+
: upstreamStream;
|
|
477
|
+
const wrapped = await this.wrapUpstreamSseResponse(streamForHost, context);
|
|
478
|
+
if (!captureSse) {
|
|
479
|
+
try {
|
|
480
|
+
await writeProviderSnapshot({
|
|
481
|
+
phase: 'provider-response',
|
|
482
|
+
requestId: context.requestId,
|
|
483
|
+
data: { mode: 'sse', retry: true },
|
|
484
|
+
headers: finalRetryHeaders,
|
|
485
|
+
url: requestInfo.targetUrl,
|
|
486
|
+
entryEndpoint: requestInfo.entryEndpoint,
|
|
487
|
+
clientRequestId: requestInfo.clientRequestId
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
catch { /* non-blocking */ }
|
|
491
|
+
}
|
|
492
|
+
return wrapped;
|
|
493
|
+
}
|
|
494
|
+
const response = await this.httpClient.post(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
|
|
495
|
+
try {
|
|
496
|
+
await writeProviderSnapshot({
|
|
497
|
+
phase: 'provider-response',
|
|
498
|
+
requestId: context.requestId,
|
|
499
|
+
data: response,
|
|
500
|
+
headers: finalRetryHeaders,
|
|
501
|
+
url: requestInfo.targetUrl,
|
|
502
|
+
entryEndpoint: requestInfo.entryEndpoint,
|
|
503
|
+
clientRequestId: requestInfo.clientRequestId
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
catch { /* non-blocking */ }
|
|
507
|
+
return response;
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return undefined;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async normalizeHttpError(error, processedRequest, requestInfo, context) {
|
|
514
|
+
const normalized = error;
|
|
515
|
+
try {
|
|
516
|
+
const statusCode = extractStatusCodeFromError(normalized);
|
|
517
|
+
if (statusCode && !Number.isNaN(statusCode)) {
|
|
518
|
+
normalized.statusCode = statusCode;
|
|
519
|
+
if (!normalized.status) {
|
|
520
|
+
normalized.status = statusCode;
|
|
521
|
+
}
|
|
522
|
+
if (!normalized.code) {
|
|
523
|
+
normalized.code = `HTTP_${statusCode}`;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (!normalized.response) {
|
|
527
|
+
normalized.response = {};
|
|
528
|
+
}
|
|
529
|
+
if (!normalized.response.data) {
|
|
530
|
+
normalized.response.data = {};
|
|
531
|
+
}
|
|
532
|
+
if (!normalized.response.data.error) {
|
|
533
|
+
normalized.response.data.error = {};
|
|
534
|
+
}
|
|
535
|
+
if (normalized.code && !normalized.response.data.error.code) {
|
|
536
|
+
normalized.response.data.error.code = normalized.code;
|
|
537
|
+
}
|
|
538
|
+
if (normalized.message && !normalized.response.data.error.message) {
|
|
539
|
+
normalized.response.data.error.message = normalized.message;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
/* ignore */
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
await writeProviderSnapshot({
|
|
547
|
+
phase: 'provider-error',
|
|
548
|
+
requestId: context.requestId,
|
|
549
|
+
data: {
|
|
550
|
+
status: normalized?.statusCode ?? normalized?.status ?? null,
|
|
551
|
+
code: normalized?.code ?? null,
|
|
552
|
+
error: typeof normalized?.message === 'string' ? normalized.message : String(error || '')
|
|
553
|
+
},
|
|
554
|
+
headers: requestInfo.headers,
|
|
555
|
+
url: requestInfo.targetUrl,
|
|
556
|
+
entryEndpoint: requestInfo.entryEndpoint ?? this.getEntryEndpointFromPayload(processedRequest),
|
|
557
|
+
clientRequestId: requestInfo.clientRequestId ?? this.getClientRequestIdFromContext(context)
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
catch { /* non-blocking */ }
|
|
561
|
+
return normalized;
|
|
562
|
+
}
|
|
747
563
|
/**
|
|
748
564
|
* 为特定请求确定最终 endpoint(默认使用配置值,可由子类覆写)
|
|
749
565
|
*/
|
|
@@ -793,6 +609,8 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
793
609
|
const inboundOriginator = typeof inboundMetadata?.clientOriginator === 'string' && inboundMetadata.clientOriginator.trim()
|
|
794
610
|
? inboundMetadata.clientOriginator.trim()
|
|
795
611
|
: undefined;
|
|
612
|
+
const inboundClientHeaders = this.extractClientHeaders(runtimeMetadata);
|
|
613
|
+
const normalizedClientHeaders = this.normalizeCodexClientHeaders(inboundClientHeaders);
|
|
796
614
|
// 服务特定头部
|
|
797
615
|
const serviceHeaders = this.serviceProfile.headers || {};
|
|
798
616
|
// 配置覆盖头部
|
|
@@ -803,7 +621,7 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
803
621
|
const auth = this.config.config.auth;
|
|
804
622
|
if (this.normalizeAuthMode(auth.type) === 'oauth') {
|
|
805
623
|
const oauthAuth = auth;
|
|
806
|
-
const oauthProviderId = this.ensureOAuthProviderId(oauthAuth);
|
|
624
|
+
const oauthProviderId = this.oauthProviderId || this.ensureOAuthProviderId(oauthAuth);
|
|
807
625
|
logOAuthDebug('[OAuth] [headers] ensureValid start (openBrowser=true, forceReauth=false)');
|
|
808
626
|
try {
|
|
809
627
|
await ensureValidOAuthToken(oauthProviderId, oauthAuth, {
|
|
@@ -848,55 +666,146 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
848
666
|
...runtimeHeaders,
|
|
849
667
|
...authHeaders
|
|
850
668
|
};
|
|
851
|
-
//
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return value.trim();
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
return undefined;
|
|
863
|
-
};
|
|
864
|
-
const setHeader = (headers, target, value) => {
|
|
865
|
-
if (!value || !value.trim())
|
|
866
|
-
return;
|
|
867
|
-
const lowered = target.toLowerCase();
|
|
868
|
-
for (const key of Object.keys(headers)) {
|
|
869
|
-
if (key.toLowerCase() === lowered) {
|
|
870
|
-
headers[key] = value;
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
headers[target] = value;
|
|
875
|
-
};
|
|
669
|
+
// 保留客户端 Accept;无则默认为 application/json
|
|
670
|
+
const clientAccept = normalizedClientHeaders ? this.findHeaderValue(normalizedClientHeaders, 'Accept') : undefined;
|
|
671
|
+
if (clientAccept) {
|
|
672
|
+
this.assignHeader(finalHeaders, 'Accept', clientAccept);
|
|
673
|
+
}
|
|
674
|
+
else if (!this.findHeaderValue(finalHeaders, 'Accept')) {
|
|
675
|
+
this.assignHeader(finalHeaders, 'Accept', 'application/json');
|
|
676
|
+
}
|
|
876
677
|
// Header priority:
|
|
877
678
|
// - user/provider config (overrides/runtime) wins
|
|
878
679
|
// - otherwise inherit from inbound client headers
|
|
879
680
|
// - otherwise fall back to defaults
|
|
880
|
-
const uaFromConfig =
|
|
881
|
-
const uaFromService =
|
|
681
|
+
const uaFromConfig = this.findHeaderValue({ ...overrideHeaders, ...runtimeHeaders }, 'User-Agent');
|
|
682
|
+
const uaFromService = this.findHeaderValue(serviceHeaders, 'User-Agent');
|
|
882
683
|
const resolvedUa = uaFromConfig ?? inboundUserAgent ?? uaFromService ?? DEFAULT_USER_AGENT;
|
|
883
|
-
|
|
684
|
+
this.assignHeader(finalHeaders, 'User-Agent', resolvedUa);
|
|
884
685
|
// originator: do not invent one; only forward from config or inbound client
|
|
885
|
-
const originatorFromConfig =
|
|
886
|
-
const originatorFromService =
|
|
686
|
+
const originatorFromConfig = this.findHeaderValue({ ...overrideHeaders, ...runtimeHeaders }, 'originator');
|
|
687
|
+
const originatorFromService = this.findHeaderValue(serviceHeaders, 'originator');
|
|
887
688
|
const resolvedOriginator = originatorFromConfig ?? inboundOriginator ?? originatorFromService;
|
|
888
689
|
if (resolvedOriginator) {
|
|
889
|
-
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
690
|
+
this.assignHeader(finalHeaders, 'originator', resolvedOriginator);
|
|
691
|
+
}
|
|
692
|
+
if (normalizedClientHeaders) {
|
|
693
|
+
const conversationId = this.findHeaderValue(normalizedClientHeaders, 'conversation_id');
|
|
694
|
+
if (conversationId) {
|
|
695
|
+
this.assignHeader(finalHeaders, 'conversation_id', conversationId);
|
|
696
|
+
}
|
|
697
|
+
const sessionId = this.findHeaderValue(normalizedClientHeaders, 'session_id');
|
|
698
|
+
if (sessionId) {
|
|
699
|
+
this.assignHeader(finalHeaders, 'session_id', sessionId);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (this.isCodexUaMode()) {
|
|
703
|
+
this.ensureCodexSessionHeaders(finalHeaders, runtimeMetadata);
|
|
704
|
+
}
|
|
898
705
|
return finalHeaders;
|
|
899
706
|
}
|
|
707
|
+
isCodexUaMode() {
|
|
708
|
+
const raw = process.env.ROUTECODEX_UA_MODE ??
|
|
709
|
+
process.env.RCC_UA_MODE ??
|
|
710
|
+
'';
|
|
711
|
+
const normalized = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
|
|
712
|
+
const runtime = this.getCurrentRuntimeMetadata();
|
|
713
|
+
if (!runtime) {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
const providerType = runtime.providerType || this.providerType;
|
|
717
|
+
const entryEndpoint = this.getEntryEndpointFromRuntime(runtime);
|
|
718
|
+
// 显式 UA 模式(--codex / --ua codex):对所有 provider 激活
|
|
719
|
+
if (normalized === 'codex') {
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
// 隐式模式:未显式设置 UA 时,仅在 responses provider 且入口不是 /v1/responses 时激活
|
|
723
|
+
if (providerType === 'responses' && entryEndpoint) {
|
|
724
|
+
const lowered = entryEndpoint.trim().toLowerCase();
|
|
725
|
+
if (!lowered.includes('/responses')) {
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
normalizeCodexClientHeaders(headers) {
|
|
732
|
+
if (!headers) {
|
|
733
|
+
return undefined;
|
|
734
|
+
}
|
|
735
|
+
if (!this.isCodexUaMode()) {
|
|
736
|
+
return headers;
|
|
737
|
+
}
|
|
738
|
+
const normalizedHeaders = { ...headers };
|
|
739
|
+
this.copyHeaderValue(normalizedHeaders, headers, 'anthropic-session-id', 'session_id');
|
|
740
|
+
this.copyHeaderValue(normalizedHeaders, headers, 'anthropic-conversation-id', 'conversation_id');
|
|
741
|
+
this.copyHeaderValue(normalizedHeaders, headers, 'anthropic-user-agent', 'User-Agent');
|
|
742
|
+
this.copyHeaderValue(normalizedHeaders, headers, 'anthropic-originator', 'originator');
|
|
743
|
+
return normalizedHeaders;
|
|
744
|
+
}
|
|
745
|
+
copyHeaderValue(target, source, from, to) {
|
|
746
|
+
if (this.findHeaderValue(target, to)) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
const value = this.findHeaderValue(source, from);
|
|
750
|
+
if (value) {
|
|
751
|
+
target[to] = value;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
findHeaderValue(headers, target) {
|
|
755
|
+
const lowered = target.toLowerCase();
|
|
756
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
757
|
+
if (key.toLowerCase() === lowered && typeof value === 'string' && value.trim()) {
|
|
758
|
+
return value.trim();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return undefined;
|
|
762
|
+
}
|
|
763
|
+
assignHeader(headers, target, value) {
|
|
764
|
+
if (!value || !value.trim()) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const lowered = target.toLowerCase();
|
|
768
|
+
for (const key of Object.keys(headers)) {
|
|
769
|
+
if (key.toLowerCase() === lowered) {
|
|
770
|
+
headers[key] = value;
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
headers[target] = value;
|
|
775
|
+
}
|
|
776
|
+
ensureCodexSessionHeaders(headers, runtimeMetadata) {
|
|
777
|
+
this.setHeaderIfMissing(headers, 'session_id', this.buildCodexIdentifier('session', runtimeMetadata));
|
|
778
|
+
this.setHeaderIfMissing(headers, 'conversation_id', this.buildCodexIdentifier('conversation', runtimeMetadata));
|
|
779
|
+
}
|
|
780
|
+
setHeaderIfMissing(headers, target, value) {
|
|
781
|
+
if (this.findHeaderValue(headers, target)) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
this.assignHeader(headers, target, value);
|
|
785
|
+
}
|
|
786
|
+
buildCodexIdentifier(kind, runtimeMetadata) {
|
|
787
|
+
const fallbackId = runtimeMetadata?.metadata && typeof runtimeMetadata.metadata === 'object'
|
|
788
|
+
? runtimeMetadata.metadata.clientRequestId
|
|
789
|
+
: undefined;
|
|
790
|
+
const requestId = runtimeMetadata?.requestId ?? fallbackId;
|
|
791
|
+
const routeName = runtimeMetadata?.routeName;
|
|
792
|
+
const suffix = (requestId ?? `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)
|
|
793
|
+
.toString()
|
|
794
|
+
.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
795
|
+
const parts = ['codex_cli', kind, suffix];
|
|
796
|
+
if (routeName) {
|
|
797
|
+
parts.push(routeName.replace(/[^A-Za-z0-9_-]/g, '_'));
|
|
798
|
+
}
|
|
799
|
+
return this.enforceCodexIdentifierLength(parts.join('_'));
|
|
800
|
+
}
|
|
801
|
+
enforceCodexIdentifierLength(value) {
|
|
802
|
+
if (value.length <= CODEX_IDENTIFIER_MAX_LENGTH) {
|
|
803
|
+
return value;
|
|
804
|
+
}
|
|
805
|
+
const hash = createHash('sha256').update(value).digest('hex').slice(0, 10);
|
|
806
|
+
const keep = Math.max(1, CODEX_IDENTIFIER_MAX_LENGTH - hash.length - 1);
|
|
807
|
+
return `${value.slice(0, keep)}_${hash}`;
|
|
808
|
+
}
|
|
900
809
|
getEffectiveBaseUrl() {
|
|
901
810
|
const runtime = this.getRuntimeProfile();
|
|
902
811
|
const runtimeEndpoint = this.pickRuntimeBaseUrl(runtime);
|
|
@@ -934,9 +843,6 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
934
843
|
const trimmed = value.trim();
|
|
935
844
|
return /^https?:\/\//i.test(trimmed) || trimmed.startsWith('//');
|
|
936
845
|
}
|
|
937
|
-
getHookManager() {
|
|
938
|
-
return this.hookSystemIntegration.getBidirectionalHookManager();
|
|
939
|
-
}
|
|
940
846
|
// (工具自动修复辅助函数已删除)
|
|
941
847
|
getConfigExtensions() {
|
|
942
848
|
const extensions = this.config.config.extensions;
|
|
@@ -945,12 +851,23 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
945
851
|
: {};
|
|
946
852
|
}
|
|
947
853
|
getEntryEndpointFromPayload(payload) {
|
|
948
|
-
const
|
|
949
|
-
|
|
854
|
+
const runtimeMeta = extractProviderRuntimeMetadata(payload);
|
|
855
|
+
const metadata = (runtimeMeta && typeof runtimeMeta.metadata === 'object')
|
|
856
|
+
? runtimeMeta.metadata
|
|
857
|
+
: payload.metadata;
|
|
858
|
+
if (metadata && typeof metadata.entryEndpoint === 'string' && metadata.entryEndpoint.trim()) {
|
|
950
859
|
return metadata.entryEndpoint;
|
|
951
860
|
}
|
|
952
861
|
return undefined;
|
|
953
862
|
}
|
|
863
|
+
getEntryEndpointFromRuntime(runtime) {
|
|
864
|
+
if (!runtime || !runtime.metadata || typeof runtime.metadata !== 'object') {
|
|
865
|
+
return undefined;
|
|
866
|
+
}
|
|
867
|
+
const meta = runtime.metadata;
|
|
868
|
+
const value = meta.entryEndpoint;
|
|
869
|
+
return typeof value === 'string' && value.trim().length ? value : undefined;
|
|
870
|
+
}
|
|
954
871
|
asResponseRecord(value) {
|
|
955
872
|
if (isRecord(value)) {
|
|
956
873
|
return value;
|
|
@@ -1056,5 +973,126 @@ export class HttpTransportProvider extends BaseProvider {
|
|
|
1056
973
|
}
|
|
1057
974
|
return providerId;
|
|
1058
975
|
}
|
|
976
|
+
extractClientHeaders(source) {
|
|
977
|
+
const normalize = (value) => {
|
|
978
|
+
return this.normalizeClientHeaders(value);
|
|
979
|
+
};
|
|
980
|
+
if (!source || typeof source !== 'object') {
|
|
981
|
+
return undefined;
|
|
982
|
+
}
|
|
983
|
+
const candidates = [];
|
|
984
|
+
const metadataNode = source.metadata;
|
|
985
|
+
if (metadataNode && typeof metadataNode === 'object') {
|
|
986
|
+
const headersNode = metadataNode.clientHeaders;
|
|
987
|
+
if (headersNode) {
|
|
988
|
+
candidates.push(headersNode);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const directNode = source.clientHeaders;
|
|
992
|
+
if (directNode) {
|
|
993
|
+
candidates.push(directNode);
|
|
994
|
+
}
|
|
995
|
+
for (const candidate of candidates) {
|
|
996
|
+
const normalized = normalize(candidate);
|
|
997
|
+
if (normalized) {
|
|
998
|
+
return normalized;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return undefined;
|
|
1002
|
+
}
|
|
1003
|
+
normalizeClientHeaders(value) {
|
|
1004
|
+
if (!value || typeof value !== 'object') {
|
|
1005
|
+
return undefined;
|
|
1006
|
+
}
|
|
1007
|
+
const normalized = {};
|
|
1008
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
1009
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
1010
|
+
normalized[key] = raw;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return Object.keys(normalized).length ? normalized : undefined;
|
|
1014
|
+
}
|
|
1015
|
+
async ensureAntigravityProjectMetadata(oauthAuth) {
|
|
1016
|
+
const tokenFile = typeof oauthAuth?.tokenFile === 'string' ? oauthAuth.tokenFile.trim() : '';
|
|
1017
|
+
if (!tokenFile) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const tokenPath = tokenFile.startsWith('~/')
|
|
1021
|
+
? tokenFile.replace(/^~\//, `${process.env.HOME || ''}/`)
|
|
1022
|
+
: tokenFile;
|
|
1023
|
+
let raw;
|
|
1024
|
+
try {
|
|
1025
|
+
raw = await fs.readFile(tokenPath, 'utf-8');
|
|
1026
|
+
}
|
|
1027
|
+
catch {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
let parsed;
|
|
1031
|
+
try {
|
|
1032
|
+
parsed = JSON.parse(raw);
|
|
1033
|
+
}
|
|
1034
|
+
catch {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (this.extractProjectIdFromTokenSnapshot(parsed)) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const accessToken = this.extractAccessTokenFromSnapshot(parsed);
|
|
1041
|
+
if (!accessToken) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const baseOverride = oauthAuth.antigravityApiBase;
|
|
1045
|
+
const apiBaseHint = baseOverride || this.getEffectiveBaseUrl();
|
|
1046
|
+
const projectId = await fetchAntigravityProjectId(accessToken, apiBaseHint);
|
|
1047
|
+
if (!projectId) {
|
|
1048
|
+
logOAuthDebug('[OAuth] Antigravity: unable to resolve project_id for token file');
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
parsed.project_id = projectId;
|
|
1052
|
+
parsed.projectId = projectId;
|
|
1053
|
+
const projectsNode = parsed.projects;
|
|
1054
|
+
if (!Array.isArray(projectsNode) || !projectsNode.length) {
|
|
1055
|
+
parsed.projects = [{ projectId }];
|
|
1056
|
+
}
|
|
1057
|
+
await fs.writeFile(tokenPath, JSON.stringify(parsed, null, 2));
|
|
1058
|
+
logOAuthDebug(`[OAuth] Antigravity: persisted project_id=${projectId} for ${tokenPath}`);
|
|
1059
|
+
}
|
|
1060
|
+
extractAccessTokenFromSnapshot(snapshot) {
|
|
1061
|
+
const lower = snapshot.access_token;
|
|
1062
|
+
const upper = snapshot.AccessToken;
|
|
1063
|
+
const value = typeof lower === 'string'
|
|
1064
|
+
? lower
|
|
1065
|
+
: typeof upper === 'string'
|
|
1066
|
+
? upper
|
|
1067
|
+
: undefined;
|
|
1068
|
+
if (value && value.trim()) {
|
|
1069
|
+
return value.trim();
|
|
1070
|
+
}
|
|
1071
|
+
return undefined;
|
|
1072
|
+
}
|
|
1073
|
+
extractProjectIdFromTokenSnapshot(snapshot) {
|
|
1074
|
+
const directNode = snapshot.project_id;
|
|
1075
|
+
const direct = typeof directNode === 'string' ? directNode : undefined;
|
|
1076
|
+
if (direct && direct.trim()) {
|
|
1077
|
+
return direct.trim();
|
|
1078
|
+
}
|
|
1079
|
+
const camelNode = snapshot.projectId;
|
|
1080
|
+
const camel = typeof camelNode === 'string' ? camelNode : undefined;
|
|
1081
|
+
if (camel && camel.trim()) {
|
|
1082
|
+
return camel.trim();
|
|
1083
|
+
}
|
|
1084
|
+
if (Array.isArray(snapshot.projects)) {
|
|
1085
|
+
for (const entry of snapshot.projects ?? []) {
|
|
1086
|
+
if (entry && typeof entry === 'object' && typeof entry.projectId === 'string') {
|
|
1087
|
+
const candidate = String(entry.projectId);
|
|
1088
|
+
if (candidate.trim()) {
|
|
1089
|
+
return candidate.trim();
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return undefined;
|
|
1095
|
+
}
|
|
1059
1096
|
}
|
|
1097
|
+
const CODEX_IDENTIFIER_MAX_LENGTH = 64;
|
|
1060
1098
|
//# sourceMappingURL=http-transport-provider.js.map
|