@jsonstudio/rcc 0.89.1205 → 0.89.1457
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 +53 -1412
- package/configsamples/config.json +426 -0
- package/configsamples/config.reference.json +58 -0
- package/configsamples/provider/crs/config.v1.json +46 -0
- package/configsamples/provider/glm/config.v1.json +81 -0
- package/configsamples/provider/glm-anthropic/config.v1.json +45 -0
- package/configsamples/provider/iflow/config.v1.json +74 -0
- package/configsamples/provider/kimi/config.v1.json +41 -0
- package/configsamples/provider/lmstudio/config.v1.json +101 -0
- package/configsamples/provider/mimo/config.v1.json +35 -0
- package/configsamples/provider/modelscope/config.v1.json +96 -0
- package/configsamples/provider/qwen/config.v1.json +38 -0
- package/configsamples/provider/tab/config.v1.json +50 -0
- package/configsamples/provider/tabglm/config.v1.json +49 -0
- package/dist/build-info.js +2 -2
- package/dist/cli/commands/code.js +12 -6
- package/dist/cli/commands/code.js.map +1 -1
- package/dist/cli/commands/config.d.ts +2 -1
- package/dist/cli/commands/config.js +77 -103
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/examples.js +6 -6
- package/dist/cli/commands/examples.js.map +1 -1
- package/dist/cli/commands/init.d.ts +28 -0
- package/dist/cli/commands/init.js +94 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/port.js +10 -2
- package/dist/cli/commands/port.js.map +1 -1
- package/dist/cli/commands/restart.js +5 -2
- package/dist/cli/commands/restart.js.map +1 -1
- package/dist/cli/commands/start.js +25 -22
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.js +1 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.js +1 -0
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/bundled-docs.d.ts +20 -0
- package/dist/cli/config/bundled-docs.js +91 -0
- package/dist/cli/config/bundled-docs.js.map +1 -0
- package/dist/cli/config/init-config.d.ts +37 -0
- package/dist/cli/config/init-config.js +212 -0
- package/dist/cli/config/init-config.js.map +1 -0
- package/dist/cli/config/init-provider-catalog.d.ts +8 -0
- package/dist/cli/config/init-provider-catalog.js +187 -0
- package/dist/cli/config/init-provider-catalog.js.map +1 -0
- package/dist/cli/register/init-command.d.ts +3 -0
- package/dist/cli/register/init-command.js +5 -0
- package/dist/cli/register/init-command.js.map +1 -0
- package/dist/cli.js +28 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini/gemini-protocol-client.js +2 -1
- package/dist/client/gemini/gemini-protocol-client.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +40 -16
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/client/openai/chat-protocol-client.js +2 -1
- package/dist/client/openai/chat-protocol-client.js.map +1 -1
- package/dist/client/responses/responses-protocol-client.js +2 -1
- package/dist/client/responses/responses-protocol-client.js.map +1 -1
- package/dist/config/risk-control-config.d.ts +94 -0
- package/dist/config/risk-control-config.js +196 -0
- package/dist/config/risk-control-config.js.map +1 -0
- package/dist/constants/index.d.ts +6 -0
- package/dist/constants/index.js +13 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +2113 -190
- package/dist/error-handling/quiet-error-handling-center.js +46 -8
- package/dist/error-handling/quiet-error-handling-center.js.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/health/index.d.ts +1 -1
- package/dist/manager/modules/quota/antigravity-quota-manager.d.ts +70 -0
- package/dist/manager/modules/quota/antigravity-quota-manager.js +442 -0
- package/dist/manager/modules/quota/antigravity-quota-manager.js.map +1 -0
- package/dist/manager/modules/quota/index.d.ts +3 -127
- package/dist/manager/modules/quota/index.js +2 -1093
- package/dist/manager/modules/quota/index.js.map +1 -1
- package/dist/manager/modules/quota/provider-key-normalization.d.ts +3 -0
- package/dist/manager/modules/quota/provider-key-normalization.js +155 -0
- package/dist/manager/modules/quota/provider-key-normalization.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.d.ts +9 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js +115 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.d.ts +77 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.d.ts +12 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.js +239 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.js +404 -0
- package/dist/manager/modules/quota/provider-quota-daemon.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.d.ts +11 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +192 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.d.ts +8 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js +96 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.d.ts +19 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.js +37 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.js.map +1 -0
- package/dist/manager/modules/routing/index.d.ts +1 -0
- package/dist/manager/modules/routing/index.js +11 -25
- package/dist/manager/modules/routing/index.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.d.ts +2 -0
- package/dist/manager/quota/provider-quota-center.js +80 -82
- package/dist/manager/quota/provider-quota-center.js.map +1 -1
- package/dist/modules/llmswitch/bridge.d.ts +16 -18
- package/dist/modules/llmswitch/bridge.js +293 -94
- package/dist/modules/llmswitch/bridge.js.map +1 -1
- package/dist/modules/llmswitch/core-loader.d.ts +4 -2
- package/dist/modules/llmswitch/core-loader.js +32 -20
- package/dist/modules/llmswitch/core-loader.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.js +3 -2
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/modules/pipeline/utils/debug-logger.js +1 -1
- package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
- package/dist/providers/auth/antigravity-userinfo-helper.d.ts +2 -1
- package/dist/providers/auth/antigravity-userinfo-helper.js +25 -4
- package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/iflow-cookie-auth.js +0 -2
- package/dist/providers/auth/iflow-cookie-auth.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +2 -23
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/tokenfile-auth.d.ts +2 -0
- package/dist/providers/auth/tokenfile-auth.js +33 -1
- package/dist/providers/auth/tokenfile-auth.js.map +1 -1
- package/dist/providers/core/config/camoufox-launcher.d.ts +5 -0
- package/dist/providers/core/config/camoufox-launcher.js +40 -4
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +7 -18
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/antigravity-quota-client.js +6 -3
- package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.d.ts +2 -7
- package/dist/providers/core/runtime/base-provider.js +84 -165
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +7 -0
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +368 -97
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.d.ts +3 -0
- package/dist/providers/core/runtime/http-request-executor.js +110 -38
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +17 -0
- package/dist/providers/core/runtime/http-transport-provider.js +165 -16
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.js +10 -0
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/provider-factory.js +7 -5
- package/dist/providers/core/runtime/provider-factory.js.map +1 -1
- package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +6 -0
- package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
- package/dist/providers/core/runtime/rate-limit-manager.d.ts +1 -12
- package/dist/providers/core/runtime/rate-limit-manager.js +4 -77
- package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.d.ts +1 -7
- package/dist/providers/core/runtime/responses-provider.js +12 -93
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +12 -8
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +36 -46
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-logger.d.ts +1 -1
- package/dist/providers/core/utils/provider-error-reporter.d.ts +3 -1
- package/dist/providers/core/utils/provider-error-reporter.js +3 -0
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.js +1 -4
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/mock/mock-provider-runtime.js +57 -27
- package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
- package/dist/scripts/camoufox/launch-auth.mjs +193 -58
- package/dist/server/handlers/handler-utils.js +8 -3
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +1 -1
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.d.ts +2 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +103 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.d.ts +5 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js +77 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.d.ts +18 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.js +89 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +1 -2
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +226 -24
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +47 -8
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js +68 -4
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +3 -4
- package/dist/server/runtime/http-server/daemon-admin-routes.js +9 -14
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.js +0 -16
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js +110 -34
- package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +5 -3
- package/dist/server/runtime/http-server/index.js +281 -136
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.js +19 -1
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +59 -24
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +12 -3
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/session-dir.d.ts +2 -0
- package/dist/server/runtime/http-server/session-dir.js +59 -0
- package/dist/server/runtime/http-server/session-dir.js.map +1 -0
- package/dist/server/runtime/http-server/types.d.ts +0 -4
- package/dist/server/utils/utf8-chunk-buffer.js +6 -3
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
- package/dist/server/utils/warmup-storm-tracker.js +1 -1
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
- package/dist/server-factory.d.ts +6 -28
- package/dist/server-factory.js +8 -93
- package/dist/server-factory.js.map +1 -1
- package/dist/token-daemon/index.js +2 -2
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/token-daemon/provider-registry.js +0 -1
- package/dist/token-daemon/provider-registry.js.map +1 -1
- package/dist/token-daemon/server-utils.js +8 -9
- package/dist/token-daemon/server-utils.js.map +1 -1
- package/dist/token-daemon/token-utils.js +1 -1
- package/dist/token-daemon/token-utils.js.map +1 -1
- package/dist/tools/semantic-replay.js +2 -2
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/tools/stats-request-events.d.ts +1 -1
- package/dist/tools/stats-usage.js +6 -3
- package/dist/tools/stats-usage.js.map +1 -1
- package/dist/utils/llms-engine-shadow.d.ts +19 -0
- package/dist/utils/llms-engine-shadow.js +209 -0
- package/dist/utils/llms-engine-shadow.js.map +1 -0
- package/dist/utils/runtime-versions.js +2 -1
- package/dist/utils/runtime-versions.js.map +1 -1
- package/dist/utils/strip-internal-keys.d.ts +12 -0
- package/dist/utils/strip-internal-keys.js +28 -0
- package/dist/utils/strip-internal-keys.js.map +1 -0
- package/docs/ARCHITECTURE.md +402 -0
- package/docs/CHAT_PROCESS_PROTOCOL_AND_PIPELINE.md +221 -0
- package/docs/CODEX_AND_CLAUDE_CODE.md +69 -0
- package/docs/CONFIG_ARCHITECTURE.md +517 -0
- package/docs/ERROR_HANDLING_AUDIT.md +0 -0
- package/docs/GCLI2API_PARITY_GAPS.md +98 -0
- package/docs/INSTALLATION_AND_QUICKSTART.md +74 -0
- package/docs/INSTRUCTION_MARKUP.md +89 -0
- package/docs/MODULE_ENHANCEMENT_SYSTEM.md +666 -0
- package/docs/PORTS.md +36 -0
- package/docs/PROVIDERS_BUILTIN.md +111 -0
- package/docs/PROVIDER_TYPES.md +55 -0
- package/docs/SERVERTOOL_CLOCK_DESIGN.md +233 -0
- package/docs/USAGE_HANDLING_ANALYSIS.md +335 -0
- package/docs/USER_CONFIG_PARSER_CHANGES.md +175 -0
- package/docs/V3_INBOUND_OUTBOUND_DESIGN.md +86 -0
- package/docs/VIRTUAL_ROUTER_PRIORITY_AND_HEALTH.md +125 -0
- package/docs/anthropic-request-golden-samples.md +50 -0
- package/docs/antigravity-gemini-format-cleanup.md +102 -0
- package/docs/antigravity-routing-contract.md +31 -0
- package/docs/ccr-alignment-enhancetool.md +105 -0
- package/docs/chat-glm-500-analysis.md +79 -0
- package/docs/chat-request-golden-samples.md +42 -0
- package/docs/chat-semantic-expansion-plan.md +84 -0
- package/docs/cli-command-inventory.md +76 -0
- package/docs/codex-samples-replay.md +50 -0
- package/docs/daemon-admin-api-design.md +350 -0
- package/docs/daemon-admin-module-structure.md +169 -0
- package/docs/daemon-admin-ui.html +3394 -0
- package/docs/debug-system-design.md +734 -0
- package/docs/debugging/gemini-sse-root-cause.md +52 -0
- package/docs/debugging/sse_encoding_failure_analysis.md +53 -0
- package/docs/dry-run/README.md +721 -0
- package/docs/error-handling-v2.md +92 -0
- package/docs/exec-command-guard-policy.example.v1.json +42 -0
- package/docs/fixes/gemini-protocol-mapping.md +57 -0
- package/docs/fixes/oauth-portal-timing-fix.md +202 -0
- package/docs/fixes/web-search-hop3-fix.md +265 -0
- package/docs/glm-api-reference.md +390 -0
- package/docs/glm-chat-completions.md +1779 -0
- package/docs/glm-history-inline-images.md +44 -0
- package/docs/golden-ci-library.md +66 -0
- package/docs/lmstudio-dry-run-summary.md +203 -0
- package/docs/lmstudio-tool-calling.md +214 -0
- package/docs/mapping-tables/anthropic-to-openai.json +290 -0
- package/docs/mapping-tables/iflow-to-openai.json +215 -0
- package/docs/mapping-tables/openai-passthrough.json +190 -0
- package/docs/mapping-tables/openai-to-iflow.json +227 -0
- package/docs/monitoring/Design.md +61 -0
- package/docs/multi-token-auth-guide.md +66 -0
- package/docs/oauth-authentication-guide.md +168 -0
- package/docs/oauth-iflow-implementation.md +153 -0
- package/docs/pipeline-routing-report.md +209 -0
- package/docs/plans/manager-daemon/PLAN.md +86 -0
- package/docs/plans/provider-config-v2-plan.md +176 -0
- package/docs/plans/provider-runtime-manager-plan.md +209 -0
- package/docs/plans/transparent-429-failover.md +89 -0
- package/docs/plans/unified-hub-framework-v1.md +245 -0
- package/docs/provider-config-v2-ui-design.md +181 -0
- package/docs/provider-quota-design.md +129 -0
- package/docs/providers/gemini-provider.md +62 -0
- package/docs/providers/lmstudio-v2-migration-report.md +102 -0
- package/docs/providers/provider-composite-design.md +142 -0
- package/docs/providers/provider-composite-testing.md +98 -0
- package/docs/providers/provider-type-only-migration.md +111 -0
- package/docs/rccx-wasm-migration.md +74 -0
- package/docs/refactoring/architecture-comparison-diagram.md +140 -0
- package/docs/refactoring/compatibility-v2-architecture-design.md +738 -0
- package/docs/refactoring/workflow-compatibility-refactoring-design.md +361 -0
- package/docs/reports/routing-classification-report.json +24 -0
- package/docs/reports/routing-classification-report.md +18 -0
- package/docs/reports/thinking-keywords-report.json +19 -0
- package/docs/responses/README.md +156 -0
- package/docs/responses-generic-provider.md +86 -0
- package/docs/responses-passthrough-provider-design.md +202 -0
- package/docs/routing-awrr-health-weighted-round-robin.md +179 -0
- package/docs/routing-instructions.md +393 -0
- package/docs/servertool-framework.md +65 -0
- package/docs/stop-message-auto.md +225 -0
- package/docs/streaming-flow.html +30 -0
- package/docs/streaming-flow.md +182 -0
- package/docs/token-daemon-preview.html +490 -0
- package/docs/token-refresh-daemon-plan.md +269 -0
- package/docs/transformation-tables/Gemini-FinishReason/345/256/214/346/225/264/350/275/254/346/215/242/350/241/250.json +233 -0
- package/docs/transformation-tables/README.md +225 -0
- package/docs/transformation-tables/claude-code-router-anthropic-to-gemini.json +283 -0
- package/docs/transformation-tables/claude-code-router-anthropic-to-openai.json +208 -0
- package/docs/transformation-tables/claude-code-router-openai-to-anthropic.json +261 -0
- package/docs/transformation-tables/claude-code-router-openai-to-gemini.json +208 -0
- package/docs/transformation-tables/claude-code-router-openai-to-lmstudio.json +182 -0
- package/docs/transformation-tables/claude-code-router-openai-to-ollama.json +250 -0
- package/docs/transformation-tables/claude-code-router-openai-to-textgenwebui.json +295 -0
- package/docs/transformation-tables/claude-code-router-provider-conversions.json +193 -0
- package/docs/transformation-tables//345/256/214/346/225/264/347/232/204/345/267/245/345/205/267/346/211/247/350/241/214/346/265/201/347/250/213/350/275/254/346/215/242/350/241/250.json +299 -0
- package/docs/transformation-tables//345/257/271/350/257/235/345/216/206/345/217/262/347/273/264/346/212/244/345/210/206/346/236/220.md +134 -0
- package/docs/transformation-tables//345/267/245/345/205/267/350/260/203/347/224/250/346/250/241/345/274/217/345/210/206/346/236/220.md +158 -0
- package/docs/transformation-tables//347/212/266/346/200/201/347/256/241/347/220/206/351/234/200/346/261/202/345/210/206/346/236/220.md +175 -0
- package/docs/transformation-tables//351/235/231/346/200/201/350/241/250vs/345/212/250/346/200/201/345/210/206/346/236/220.md +189 -0
- package/docs/transformation-tables//351/235/231/346/200/201/350/241/250/345/207/206/347/241/256/346/200/247/350/257/204/344/274/260.md +179 -0
- package/docs/transformation-tables//351/235/236/346/265/201/345/274/217/345/234/272/346/231/257/345/210/206/346/236/220.md +189 -0
- package/docs/v2-architecture/IMPLEMENTATION-ROADMAP.md +367 -0
- package/docs/v2-architecture/OPTIMIZED-DESIGN.md +827 -0
- package/docs/v2-architecture/PRERUN-CONNECTION-DESIGN.md +716 -0
- package/docs/v2-architecture/README.md +549 -0
- package/docs/verification/modelscope-verify.md +59 -0
- package/docs/verified-configs/README.md +60 -0
- package/docs/verified-configs/v0.45.0/README.md +244 -0
- package/docs/verified-configs/v0.45.0/lmstudio-5521-gpt-oss-20b-mlx.json +135 -0
- package/docs/verified-configs/v0.45.0/merged-config.5521.json +1205 -0
- package/docs/verified-configs/v0.45.0/merged-config.qwen-5522.json +1559 -0
- package/docs/verified-configs/v0.45.0/qwen-5522-qwen3-coder-plus-final.json +221 -0
- package/docs/verified-configs/v0.45.0/qwen-5522-qwen3-coder-plus-fixed.json +242 -0
- package/docs/verified-configs/v0.45.0/qwen-5522-qwen3-coder-plus.json +242 -0
- package/docs/web-search-service-design.md +322 -0
- package/package.json +26 -15
- package/scripts/build-core.mjs +3 -1
- package/scripts/camoufox/launch-auth.mjs +193 -58
- package/scripts/ci/repo-sanity.mjs +138 -0
- package/scripts/mock-provider/run-regressions.mjs +157 -1
- package/scripts/monitor-diff.mjs +126 -0
- package/scripts/pack-mode.mjs +19 -1
- package/scripts/pack-rcc.mjs +63 -0
- package/scripts/run-bg.sh +0 -14
- package/scripts/tests/ci-jest.mjs +119 -0
- package/scripts/tools-dev/responses-debug-client/README.md +23 -0
- package/scripts/tools-dev/responses-debug-client/payloads/poem.json +13 -0
- package/scripts/tools-dev/responses-debug-client/payloads/sample-no-tools.json +98 -0
- package/scripts/tools-dev/responses-debug-client/payloads/text.json +13 -0
- package/scripts/tools-dev/responses-debug-client/payloads/tool.json +27 -0
- package/scripts/tools-dev/responses-debug-client/run.mjs +65 -0
- package/scripts/tools-dev/responses-debug-client/src/index.ts +281 -0
- package/scripts/tools-dev/run-llmswitch-chat.mjs +53 -0
- package/scripts/tools-dev/server-tools-dev/run-web-fetch.mjs +65 -0
- package/scripts/unified-hub-shadow-compare.mjs +33 -13
- package/scripts/vendor-core.mjs +13 -3
- package/scripts/verify-e2e-toolcall.mjs +115 -26
- package/dist/modules/llmswitch/pipeline-registry.d.ts +0 -57
- package/dist/modules/llmswitch/pipeline-registry.js +0 -229
- package/dist/modules/llmswitch/pipeline-registry.js.map +0 -1
- package/dist/server/RouteCodexServer.d.ts +0 -13
- package/dist/server/RouteCodexServer.js +0 -25
- package/dist/server/RouteCodexServer.js.map +0 -1
- package/dist/v2/conversion/hub/snapshot-recorder.d.ts +0 -12
- package/dist/v2/conversion/hub/snapshot-recorder.js +0 -22
- package/dist/v2/conversion/hub/snapshot-recorder.js.map +0 -1
- package/scripts/test-fc-responses.mjs +0 -66
- package/scripts/test-guidance.mjs +0 -100
- package/scripts/test-iflow-web-search.mjs +0 -141
- package/scripts/test-iflow.mjs +0 -379
- package/scripts/test-tool-exec.mjs +0 -26
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import { pathToFileURL } from 'url';
|
|
6
|
-
|
|
7
|
-
const HOME = os.homedir();
|
|
8
|
-
const base = path.join(HOME, '.routecodex', 'codex-samples');
|
|
9
|
-
const guidanceMod = path.resolve(process.cwd(), 'sharedmodule/llmswitch-core/dist/guidance/index.js');
|
|
10
|
-
|
|
11
|
-
async function loadGuidance() {
|
|
12
|
-
const mod = await import(pathToFileURL(guidanceMod).href);
|
|
13
|
-
const { refineSystemToolGuidance, augmentOpenAITools, augmentAnthropicTools, buildSystemToolGuidance } = mod;
|
|
14
|
-
return { refineSystemToolGuidance, augmentOpenAITools, augmentAnthropicTools, buildSystemToolGuidance };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function readJsonSafe(p) {
|
|
18
|
-
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function collectFiles(dir) {
|
|
22
|
-
const out = [];
|
|
23
|
-
try {
|
|
24
|
-
for (const name of fs.readdirSync(dir)) {
|
|
25
|
-
const full = path.join(dir, name);
|
|
26
|
-
const st = fs.statSync(full);
|
|
27
|
-
if (st.isFile()) out.push(full);
|
|
28
|
-
}
|
|
29
|
-
} catch { /* ignore */ }
|
|
30
|
-
return out;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function extractRequest(obj) {
|
|
34
|
-
// Try common capture shapes
|
|
35
|
-
if (obj && typeof obj === 'object') {
|
|
36
|
-
if (obj.body && typeof obj.body === 'object') return obj.body;
|
|
37
|
-
if (obj.data && typeof obj.data === 'object') return obj.data;
|
|
38
|
-
}
|
|
39
|
-
return obj;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function hasOpenAITools(tools) {
|
|
43
|
-
return Array.isArray(tools) && tools.some(t => t && typeof t === 'object' && t.function && typeof t.function === 'object');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function hasAnthropicTools(tools) {
|
|
47
|
-
return Array.isArray(tools) && tools.some(t => t && typeof t === 'object' && t.input_schema);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function main() {
|
|
51
|
-
const { refineSystemToolGuidance, augmentOpenAITools, augmentAnthropicTools } = await loadGuidance();
|
|
52
|
-
const targets = [
|
|
53
|
-
path.join(base, 'openai-chat'),
|
|
54
|
-
path.join(base, 'responses-replay'),
|
|
55
|
-
path.join(base, 'anth-replay')
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
let total = 0, refined = 0, augmented = 0;
|
|
59
|
-
|
|
60
|
-
for (const d of targets) {
|
|
61
|
-
const files = collectFiles(d);
|
|
62
|
-
for (const f of files) {
|
|
63
|
-
const j = readJsonSafe(f);
|
|
64
|
-
if (!j) continue;
|
|
65
|
-
const req = extractRequest(j);
|
|
66
|
-
if (!req || typeof req !== 'object') continue;
|
|
67
|
-
total++;
|
|
68
|
-
// Refine system/instructions
|
|
69
|
-
try {
|
|
70
|
-
if (Array.isArray(req.messages) && req.messages.length && req.messages[0]?.role === 'system' && typeof req.messages[0].content === 'string') {
|
|
71
|
-
const before = req.messages[0].content || '';
|
|
72
|
-
const after = refineSystemToolGuidance(before);
|
|
73
|
-
if (after !== before) refined++;
|
|
74
|
-
} else if (typeof req.instructions === 'string' && req.instructions) {
|
|
75
|
-
const before = req.instructions;
|
|
76
|
-
const after = refineSystemToolGuidance(before);
|
|
77
|
-
if (after !== before) refined++;
|
|
78
|
-
}
|
|
79
|
-
} catch { /* ignore */ }
|
|
80
|
-
// Augment tools
|
|
81
|
-
try {
|
|
82
|
-
if (hasOpenAITools(req.tools)) {
|
|
83
|
-
const before = JSON.stringify(req.tools);
|
|
84
|
-
const t = augmentOpenAITools(req.tools);
|
|
85
|
-
const after = JSON.stringify(t);
|
|
86
|
-
if (after !== before) augmented++;
|
|
87
|
-
} else if (hasAnthropicTools(req.tools)) {
|
|
88
|
-
const before = JSON.stringify(req.tools);
|
|
89
|
-
const t = augmentAnthropicTools(req.tools);
|
|
90
|
-
const after = JSON.stringify(t);
|
|
91
|
-
if (after !== before) augmented++;
|
|
92
|
-
}
|
|
93
|
-
} catch { /* ignore */ }
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
console.log(JSON.stringify({ base, scannedDirs: targets, total, refined, augmented }, null, 2));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* IFlow web_search 直连测试脚本(不经过 RouteCodex pipeline)
|
|
4
|
-
*
|
|
5
|
-
* 目标:完全复用 iFlow CLI 内置 WebSearchTool 的请求协议,验证
|
|
6
|
-
* `https://apis.iflow.cn/v1/chat/retrieve` 能否正常返回搜索结果。
|
|
7
|
-
*
|
|
8
|
-
* 用法:
|
|
9
|
-
* node scripts/test-iflow-web-search.mjs
|
|
10
|
-
*
|
|
11
|
-
* 可选环境变量:
|
|
12
|
-
* IFLOW_SETTINGS_PATH 自定义 settings.json 路径(默认 ~/.iflow/settings.json)
|
|
13
|
-
* IFLOW_SEARCH_QUERY 搜索关键词(默认: "today international news 2025")
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import fs from 'node:fs/promises';
|
|
17
|
-
import os from 'node:os';
|
|
18
|
-
import path from 'node:path';
|
|
19
|
-
|
|
20
|
-
async function main() {
|
|
21
|
-
const settingsPath =
|
|
22
|
-
process.env.IFLOW_SETTINGS_PATH ||
|
|
23
|
-
path.join(os.homedir(), '.iflow', 'settings.json');
|
|
24
|
-
const query =
|
|
25
|
-
process.env.IFLOW_SEARCH_QUERY || 'today international news 2025';
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
`[iflow-web-search] Using settings: ${settingsPath}, query="${query}"`
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
const settingsRaw = await fs.readFile(settingsPath, 'utf-8');
|
|
32
|
-
const settings = JSON.parse(settingsRaw);
|
|
33
|
-
|
|
34
|
-
const baseUrl =
|
|
35
|
-
(typeof settings.baseUrl === 'string' && settings.baseUrl.trim()) ||
|
|
36
|
-
'https://apis.iflow.cn/v1';
|
|
37
|
-
const searchApiKey =
|
|
38
|
-
(typeof settings.searchApiKey === 'string' &&
|
|
39
|
-
settings.searchApiKey.trim()) ||
|
|
40
|
-
(typeof settings.apiKey === 'string' && settings.apiKey.trim()) ||
|
|
41
|
-
null;
|
|
42
|
-
|
|
43
|
-
if (!searchApiKey) {
|
|
44
|
-
console.error(
|
|
45
|
-
'[iflow-web-search] ERROR: searchApiKey/apiKey not found in settings.json'
|
|
46
|
-
);
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const url = `${baseUrl.replace(/\/$/, '')}/chat/retrieve`;
|
|
51
|
-
|
|
52
|
-
// Body 对齐 iFlow CLI WebSearchTool.executeInternal
|
|
53
|
-
const body = {
|
|
54
|
-
query,
|
|
55
|
-
history: {},
|
|
56
|
-
userId: 2,
|
|
57
|
-
userIp: '42.120.74.197',
|
|
58
|
-
appCode: 'SEARCH_CHATBOT',
|
|
59
|
-
chatId: Date.now(), // 使用时间戳作为 chatId 即可
|
|
60
|
-
phase: 'UNIFY',
|
|
61
|
-
enableQueryRewrite: false,
|
|
62
|
-
enableRetrievalSecurity: false,
|
|
63
|
-
enableIntention: false,
|
|
64
|
-
searchEngineList: ['GOOGLE', 'BING', 'SCHOLAR', 'AIPGC', 'PDF']
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
console.log(`[iflow-web-search] POST ${url}`);
|
|
68
|
-
|
|
69
|
-
const controller = new AbortController();
|
|
70
|
-
const timeoutMs = Number(process.env.IFLOW_SEARCH_TIMEOUT_MS || '30000');
|
|
71
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
const res = await fetch(url, {
|
|
75
|
-
method: 'POST',
|
|
76
|
-
headers: {
|
|
77
|
-
Authorization: `Bearer ${searchApiKey}`,
|
|
78
|
-
'Content-Type': 'application/json'
|
|
79
|
-
},
|
|
80
|
-
body: JSON.stringify(body),
|
|
81
|
-
signal: controller.signal
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
clearTimeout(timeoutId);
|
|
85
|
-
|
|
86
|
-
console.log(`[iflow-web-search] HTTP ${res.status}`);
|
|
87
|
-
const json = await res.json().catch(async () => {
|
|
88
|
-
const text = await res.text();
|
|
89
|
-
console.error('[iflow-web-search] Non-JSON response:', text.slice(0, 500));
|
|
90
|
-
throw new Error('Response is not valid JSON');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const dataArray = Array.isArray(json?.data) ? json.data : [];
|
|
94
|
-
console.log(
|
|
95
|
-
`[iflow-web-search] data.length = ${dataArray.length}, keys=${Object.keys(
|
|
96
|
-
json || {}
|
|
97
|
-
).join(',')}`
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
if (!res.ok) {
|
|
101
|
-
console.error(
|
|
102
|
-
'[iflow-web-search] ERROR: upstream returned non-200 status',
|
|
103
|
-
JSON.stringify(json, null, 2).slice(0, 2000)
|
|
104
|
-
);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!dataArray.length) {
|
|
109
|
-
console.warn(
|
|
110
|
-
'[iflow-web-search] WARN: data array is empty, check query or account permissions.'
|
|
111
|
-
);
|
|
112
|
-
} else {
|
|
113
|
-
const first = dataArray[0] || {};
|
|
114
|
-
console.log(
|
|
115
|
-
'[iflow-web-search] First item:',
|
|
116
|
-
JSON.stringify(
|
|
117
|
-
{
|
|
118
|
-
title: first.title,
|
|
119
|
-
url: first.url,
|
|
120
|
-
time: first.time,
|
|
121
|
-
abstractInfo: first.abstractInfo
|
|
122
|
-
},
|
|
123
|
-
null,
|
|
124
|
-
2
|
|
125
|
-
)
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log('[iflow-web-search] ✅ connectivity OK');
|
|
130
|
-
} catch (error) {
|
|
131
|
-
clearTimeout(timeoutId);
|
|
132
|
-
console.error('[iflow-web-search] ❌ request failed:', error);
|
|
133
|
-
process.exit(1);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
main().catch((error) => {
|
|
138
|
-
console.error('[iflow-web-search] ❌ unexpected error:', error);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
});
|
|
141
|
-
|
package/scripts/test-iflow.mjs
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// iFlow test script with OAuth token lifecycle management + optional tool SSE loop
|
|
3
|
-
// Examples:
|
|
4
|
-
// - Ensure token then proxy chat: RC_BASE=http://127.0.0.1:5506 node scripts/test-iflow.mjs --mode=proxy --endpoint=/v1/chat/completions
|
|
5
|
-
// - Ensure token then upstream chat: IFLOW_CLIENT_ID=... node scripts/test-iflow.mjs --mode=upstream
|
|
6
|
-
// - Responses tool loop (one-shot delta): RC_BASE=http://127.0.0.1:5506 node scripts/test-iflow.mjs --mode=proxy --endpoint=/v1/responses --tools
|
|
7
|
-
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import fsp from 'fs/promises';
|
|
10
|
-
import os from 'os';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { spawn } from 'child_process';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Options via env/args
|
|
16
|
-
* - MODE: proxy | upstream (default: proxy)
|
|
17
|
-
* - RC_BASE: default http://127.0.0.1:5506
|
|
18
|
-
* - RC_ENDPOINT: default /v1/chat/completions (also supports /v1/responses)
|
|
19
|
-
* - IFLOW_MODEL: default gpt-4o-mini
|
|
20
|
-
* - TEXT: default "hello from RouteCodex test"
|
|
21
|
-
* - CONFIG: RouteCodex user config path (default ~/.routecodex/config/v2/iflow-only.json)
|
|
22
|
-
* - tools: flag to run /v1/responses SSE tool loop
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
const args = Object.fromEntries(process.argv.slice(2).map(kv => {
|
|
26
|
-
const m = kv.match(/^--([^=]+)=(.*)$/);
|
|
27
|
-
if (m) return [m[1], m[2]];
|
|
28
|
-
if (kv.startsWith('--')) return [kv.slice(2), true];
|
|
29
|
-
return [kv, true];
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
const MODE = String(args.mode || process.env.MODE || 'proxy');
|
|
33
|
-
const RC_BASE = String(process.env.RC_BASE || 'http://127.0.0.1:5506').replace(/\/$/, '');
|
|
34
|
-
const RC_ENDPOINT = String(args.endpoint || process.env.RC_ENDPOINT || '/v1/chat/completions');
|
|
35
|
-
// 默认模型更新为 iFlow-ROME-30BA3B,除非通过 IFLOW_MODEL 显式覆盖。
|
|
36
|
-
const IFLOW_MODEL = String(process.env.IFLOW_MODEL || 'iFlow-ROME-30BA3B');
|
|
37
|
-
const TEXT = String(process.env.TEXT || 'hello from RouteCodex test');
|
|
38
|
-
const RUN_TOOLS = !!args.tools;
|
|
39
|
-
const CONFIG_PATH = expandHome(String(process.env.CONFIG || args.config || path.join(os.homedir(), '.routecodex', 'config', 'v2', 'iflow-only.json')));
|
|
40
|
-
|
|
41
|
-
function expandHome(p) { return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p; }
|
|
42
|
-
|
|
43
|
-
async function loadIFlowOAuthConfig(configPath) {
|
|
44
|
-
const raw = await fsp.readFile(configPath, 'utf-8');
|
|
45
|
-
const j = JSON.parse(raw);
|
|
46
|
-
const prov = j?.virtualrouter?.providers?.iflow;
|
|
47
|
-
if (!prov) throw new Error('iflow provider not found in config');
|
|
48
|
-
const oauth = prov?.oauth?.default || {};
|
|
49
|
-
return {
|
|
50
|
-
clientId: process.env.IFLOW_CLIENT_ID || oauth.clientId,
|
|
51
|
-
deviceCodeUrl: process.env.IFLOW_DEVICE_CODE_URL || oauth.deviceCodeUrl || oauth.device_code_url,
|
|
52
|
-
tokenUrl: process.env.IFLOW_TOKEN_URL || oauth.tokenUrl,
|
|
53
|
-
scopes: Array.isArray(oauth.scopes) ? oauth.scopes : (typeof oauth.scope === 'string' ? oauth.scope.split(/[\s,]+/).filter(Boolean) : ['inference']),
|
|
54
|
-
tokenFile: expandHome(oauth.tokenFile || path.join(os.homedir(), '.routecodex', 'tokens', 'iflow-default.json')),
|
|
55
|
-
apiBase: prov?.baseURL || 'https://api.iflow.cn/v1'
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function readToken(file) {
|
|
60
|
-
try { const txt = await fsp.readFile(file, 'utf-8'); return JSON.parse(txt); } catch { return null; }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function saveToken(file, tok) {
|
|
64
|
-
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
65
|
-
const withIssued = { ...tok };
|
|
66
|
-
if (!withIssued.issued_at) withIssued.issued_at = Math.floor(Date.now()/1000);
|
|
67
|
-
await fsp.writeFile(file, JSON.stringify(withIssued, null, 2), 'utf-8');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function isExpired(tok, skewSec = 60) {
|
|
71
|
-
const now = Math.floor(Date.now()/1000);
|
|
72
|
-
const issued = Number(tok.issued_at || 0);
|
|
73
|
-
const expAt = tok.expires_at ? Number(tok.expires_at) : (tok.expires_in ? issued + Number(tok.expires_in) : 0);
|
|
74
|
-
if (!expAt) return false; // if unknown, assume valid
|
|
75
|
-
return now >= (expAt - skewSec);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function refreshToken(oauth, tok) {
|
|
79
|
-
if (!tok?.refresh_token) throw new Error('No refresh_token to refresh');
|
|
80
|
-
const body = new URLSearchParams();
|
|
81
|
-
body.set('grant_type', 'refresh_token');
|
|
82
|
-
body.set('client_id', oauth.clientId);
|
|
83
|
-
body.set('refresh_token', tok.refresh_token);
|
|
84
|
-
if (process.env.IFLOW_CLIENT_SECRET) body.set('client_secret', process.env.IFLOW_CLIENT_SECRET);
|
|
85
|
-
const res = await fetch(oauth.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body });
|
|
86
|
-
const text = await res.text();
|
|
87
|
-
if (!res.ok) throw new Error(`refresh failed ${res.status}: ${text}`);
|
|
88
|
-
const j = JSON.parse(text);
|
|
89
|
-
const merged = { ...tok, ...j, issued_at: Math.floor(Date.now()/1000) };
|
|
90
|
-
if (j.expires_in && !j.expires_at) merged.expires_at = merged.issued_at + Number(j.expires_in);
|
|
91
|
-
return merged;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function openBrowser(url) {
|
|
95
|
-
try {
|
|
96
|
-
const platform = process.platform;
|
|
97
|
-
if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
98
|
-
else if (platform === 'win32') spawn('cmd', ['/c', 'start', '""', url]);
|
|
99
|
-
else spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
100
|
-
} catch { /* ignore */ }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function deviceCodeFlow(oauth) {
|
|
104
|
-
const body = new URLSearchParams();
|
|
105
|
-
body.set('client_id', oauth.clientId);
|
|
106
|
-
if (oauth.scopes?.length) body.set('scope', oauth.scopes.join(' '));
|
|
107
|
-
const res = await fetch(oauth.deviceCodeUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body });
|
|
108
|
-
const txt = await res.text();
|
|
109
|
-
if (!res.ok) throw new Error(`device code request failed ${res.status}: ${txt}`);
|
|
110
|
-
const dc = JSON.parse(txt);
|
|
111
|
-
const verifyUrl = dc.verification_uri_complete || dc.verification_uri;
|
|
112
|
-
console.log(`Open this URL to authorize:
|
|
113
|
-
${verifyUrl}
|
|
114
|
-
code: ${dc.user_code || '(auto)'}`);
|
|
115
|
-
if (verifyUrl) await openBrowser(verifyUrl);
|
|
116
|
-
const intervalMs = Math.max(5, Number(dc.interval || 5)) * 1000;
|
|
117
|
-
const deadline = Date.now() + (Number(dc.expires_in || 600) * 1000);
|
|
118
|
-
while (Date.now() < deadline) {
|
|
119
|
-
await new Promise(r => setTimeout(r, intervalMs));
|
|
120
|
-
const poll = new URLSearchParams();
|
|
121
|
-
poll.set('grant_type', 'urn:ietf:params:oauth:grant-type:device_code');
|
|
122
|
-
poll.set('device_code', dc.device_code);
|
|
123
|
-
poll.set('client_id', oauth.clientId);
|
|
124
|
-
if (process.env.IFLOW_CLIENT_SECRET) poll.set('client_secret', process.env.IFLOW_CLIENT_SECRET);
|
|
125
|
-
const pr = await fetch(oauth.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: poll });
|
|
126
|
-
const ptxt = await pr.text();
|
|
127
|
-
if (pr.status === 200) {
|
|
128
|
-
const tok = JSON.parse(ptxt);
|
|
129
|
-
tok.issued_at = Math.floor(Date.now()/1000);
|
|
130
|
-
if (tok.expires_in && !tok.expires_at) tok.expires_at = tok.issued_at + Number(tok.expires_in);
|
|
131
|
-
return tok;
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
const j = JSON.parse(ptxt);
|
|
135
|
-
const err = j.error || '';
|
|
136
|
-
if (err === 'authorization_pending') continue;
|
|
137
|
-
if (err === 'slow_down') { await new Promise(r => setTimeout(r, 5000)); continue; }
|
|
138
|
-
throw new Error(`device flow error: ${ptxt}`);
|
|
139
|
-
} catch {
|
|
140
|
-
throw new Error(`device flow error: ${ptxt}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
throw new Error('device code expired');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function ensureToken(oauth) {
|
|
147
|
-
let tok = await readToken(oauth.tokenFile);
|
|
148
|
-
if (tok && !isExpired(tok)) return tok;
|
|
149
|
-
if (tok && tok.refresh_token) {
|
|
150
|
-
try {
|
|
151
|
-
console.log('[iflow] refreshing token...');
|
|
152
|
-
tok = await refreshToken(oauth, tok);
|
|
153
|
-
await saveToken(oauth.tokenFile, tok);
|
|
154
|
-
return tok;
|
|
155
|
-
} catch (e) {
|
|
156
|
-
console.warn(`[iflow] refresh failed: ${e?.message || e}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
console.log('[iflow] starting device-code flow to obtain new token...');
|
|
160
|
-
const newTok = await deviceCodeFlow(oauth);
|
|
161
|
-
await saveToken(oauth.tokenFile, newTok);
|
|
162
|
-
return newTok;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function requestUpstreamChat(oauth, token) {
|
|
166
|
-
const url = `${oauth.apiBase.replace(/\/$/, '')}/chat/completions`;
|
|
167
|
-
const payload = { model: IFLOW_MODEL, messages: [{ role: 'user', content: TEXT }], stream: false };
|
|
168
|
-
const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${token.access_token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
169
|
-
const body = await res.text();
|
|
170
|
-
console.log(`[UPSTREAM] status=${res.status}`);
|
|
171
|
-
try { console.log(JSON.stringify(JSON.parse(body), null, 2)); } catch { console.log(body); }
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function requestUpstreamWebSearch(oauth, token) {
|
|
175
|
-
const url = `${oauth.apiBase.replace(/\/$/, '')}/chat/completions`;
|
|
176
|
-
const payload = {
|
|
177
|
-
model: IFLOW_MODEL,
|
|
178
|
-
messages: [
|
|
179
|
-
{
|
|
180
|
-
role: 'system',
|
|
181
|
-
content: 'You are an up-to-date web search engine. Call the web_search tool to fetch current results, then answer based on the tool output.'
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
role: 'user',
|
|
185
|
-
content: `${TEXT}. 请先调用 web_search 工具检索相关信息,再根据搜索结果回答。`
|
|
186
|
-
}
|
|
187
|
-
],
|
|
188
|
-
tools: [
|
|
189
|
-
{
|
|
190
|
-
type: 'function',
|
|
191
|
-
function: {
|
|
192
|
-
name: 'web_search',
|
|
193
|
-
description: 'Perform web search over the public internet and return up-to-date results.',
|
|
194
|
-
parameters: {
|
|
195
|
-
type: 'object',
|
|
196
|
-
properties: {
|
|
197
|
-
query: {
|
|
198
|
-
type: 'string',
|
|
199
|
-
description: 'Search query string.'
|
|
200
|
-
},
|
|
201
|
-
recency: {
|
|
202
|
-
type: 'string',
|
|
203
|
-
description: 'Optional recency filter such as "day", "week", or "month".'
|
|
204
|
-
},
|
|
205
|
-
count: {
|
|
206
|
-
type: 'integer',
|
|
207
|
-
minimum: 1,
|
|
208
|
-
maximum: 50,
|
|
209
|
-
description: 'Maximum number of search results to retrieve (1-50).'
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
required: ['query']
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
],
|
|
217
|
-
tool_choice: {
|
|
218
|
-
type: 'function',
|
|
219
|
-
function: {
|
|
220
|
-
name: 'web_search'
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
stream: false
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const res = await fetch(url, {
|
|
227
|
-
method: 'POST',
|
|
228
|
-
headers: {
|
|
229
|
-
'Authorization': `Bearer ${token.access_token}`,
|
|
230
|
-
'Content-Type': 'application/json'
|
|
231
|
-
},
|
|
232
|
-
body: JSON.stringify(payload)
|
|
233
|
-
});
|
|
234
|
-
const text = await res.text();
|
|
235
|
-
console.log(`[UPSTREAM][web_search] status=${res.status}`);
|
|
236
|
-
let json = null;
|
|
237
|
-
try {
|
|
238
|
-
json = JSON.parse(text);
|
|
239
|
-
console.log(JSON.stringify(json, null, 2));
|
|
240
|
-
} catch {
|
|
241
|
-
console.log(text);
|
|
242
|
-
throw new Error('iflow web_search returned non-JSON payload');
|
|
243
|
-
}
|
|
244
|
-
if (!res.ok) {
|
|
245
|
-
throw new Error(`iflow web_search failed: HTTP ${res.status}`);
|
|
246
|
-
}
|
|
247
|
-
const firstChoice = Array.isArray(json.choices) ? json.choices[0] : null;
|
|
248
|
-
const msg = firstChoice?.message || {};
|
|
249
|
-
const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
250
|
-
console.log(`[UPSTREAM][web_search] tool_calls=${toolCalls.length}`);
|
|
251
|
-
if (!toolCalls.length) {
|
|
252
|
-
console.warn('[UPSTREAM][web_search] no tool_calls returned, web_search tool may not be enabled for this model.');
|
|
253
|
-
} else {
|
|
254
|
-
const names = toolCalls
|
|
255
|
-
.map((tc) => (tc && tc.function && typeof tc.function.name === 'string' ? tc.function.name : ''))
|
|
256
|
-
.filter(Boolean);
|
|
257
|
-
console.log(`[UPSTREAM][web_search] tool names: ${names.join(', ')}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async function requestProxyChat() {
|
|
262
|
-
const url = `${RC_BASE}${RC_ENDPOINT}`;
|
|
263
|
-
const payload = { model: IFLOW_MODEL, messages: [{ role: 'user', content: TEXT }], stream: false };
|
|
264
|
-
const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
265
|
-
const body = await res.text();
|
|
266
|
-
console.log(`[PROXY] ${url} status=${res.status}`);
|
|
267
|
-
try { console.log(JSON.stringify(JSON.parse(body), null, 2)); } catch { console.log(body); }
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async function runResponsesToolLoop() {
|
|
271
|
-
const url = `${RC_BASE}/v1/responses`;
|
|
272
|
-
const payload = {
|
|
273
|
-
model: IFLOW_MODEL,
|
|
274
|
-
input: [ { type: 'text', text: `${TEXT}. 请使用可用的工具完成任务。` } ],
|
|
275
|
-
stream: true
|
|
276
|
-
};
|
|
277
|
-
const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, body: JSON.stringify(payload) });
|
|
278
|
-
if (!res.ok || !res.body) { console.error(`[RESPONSES] HTTP ${res.status}`); console.log(await res.text()); return; }
|
|
279
|
-
const reader = res.body.getReader();
|
|
280
|
-
const decoder = new TextDecoder();
|
|
281
|
-
let buf = '';
|
|
282
|
-
let responseId = '';
|
|
283
|
-
let required = null;
|
|
284
|
-
console.log('[RESPONSES] streaming...');
|
|
285
|
-
while (true) {
|
|
286
|
-
const { value, done } = await reader.read();
|
|
287
|
-
if (done) break;
|
|
288
|
-
buf += decoder.decode(value, { stream: true });
|
|
289
|
-
const chunks = buf.split(/\n\n/);
|
|
290
|
-
buf = chunks.pop() || '';
|
|
291
|
-
for (const chunk of chunks) {
|
|
292
|
-
const lines = chunk.split(/\n/);
|
|
293
|
-
let ev = '';
|
|
294
|
-
let data = '';
|
|
295
|
-
for (const ln of lines) {
|
|
296
|
-
if (ln.startsWith('event:')) ev = ln.slice(6).trim();
|
|
297
|
-
if (ln.startsWith('data:')) data += ln.slice(5).trim();
|
|
298
|
-
}
|
|
299
|
-
if (!data) continue;
|
|
300
|
-
try {
|
|
301
|
-
const j = JSON.parse(data);
|
|
302
|
-
if (j?.response?.id && !responseId) responseId = j.response.id;
|
|
303
|
-
if (ev === 'response.required_action' || (j?.type === 'response.required_action')) {
|
|
304
|
-
required = j;
|
|
305
|
-
}
|
|
306
|
-
if (ev === 'response.done' || j?.type === 'response.done') {
|
|
307
|
-
console.log('[RESPONSES] done');
|
|
308
|
-
}
|
|
309
|
-
} catch { /* ignore */ }
|
|
310
|
-
}
|
|
311
|
-
if (required && responseId) break; // got required_action
|
|
312
|
-
}
|
|
313
|
-
if (!required || !responseId) { console.warn('[RESPONSES] no required_action found'); return; }
|
|
314
|
-
const toolCalls = required?.response?.required_action?.submit_tool_outputs?.tool_calls || [];
|
|
315
|
-
console.log(`[RESPONSES] tool_calls: ${toolCalls.length}`);
|
|
316
|
-
const outputs = toolCalls.map(tc => ({ tool_call_id: tc?.id || tc?.tool_call_id || '', output: 'ok' }));
|
|
317
|
-
const contUrl = `${RC_BASE}/v1/responses/${encodeURIComponent(responseId)}/submit_tool_outputs`;
|
|
318
|
-
const contPayload = { model: IFLOW_MODEL, tool_outputs: outputs, stream: true };
|
|
319
|
-
const cont = await fetch(contUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, body: JSON.stringify(contPayload) });
|
|
320
|
-
console.log(`[RESPONSES][CONT] status=${cont.status}`);
|
|
321
|
-
// Read until response.done or short timeout
|
|
322
|
-
if (cont.body) {
|
|
323
|
-
const reader2 = cont.body.getReader();
|
|
324
|
-
const decoder2 = new TextDecoder();
|
|
325
|
-
let buf2 = '';
|
|
326
|
-
const until = Date.now() + 15000; // 15s max
|
|
327
|
-
let finished = false;
|
|
328
|
-
while (Date.now() < until) {
|
|
329
|
-
const { value, done } = await reader2.read();
|
|
330
|
-
if (done) break;
|
|
331
|
-
buf2 += decoder2.decode(value, { stream: true });
|
|
332
|
-
const parts = buf2.split(/\n\n/);
|
|
333
|
-
buf2 = parts.pop() || '';
|
|
334
|
-
for (const ch of parts) {
|
|
335
|
-
const lines = ch.split(/\n/);
|
|
336
|
-
let ev = '';
|
|
337
|
-
let data = '';
|
|
338
|
-
for (const ln of lines) {
|
|
339
|
-
if (ln.startsWith('event:')) ev = ln.slice(6).trim();
|
|
340
|
-
if (ln.startsWith('data:')) data += ln.slice(5).trim();
|
|
341
|
-
}
|
|
342
|
-
try {
|
|
343
|
-
const jj = JSON.parse(data);
|
|
344
|
-
if (ev === 'response.done' || jj?.type === 'response.done') {
|
|
345
|
-
console.log('[RESPONSES][CONT] done');
|
|
346
|
-
finished = true;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
} catch { /* ignore */ }
|
|
350
|
-
}
|
|
351
|
-
if (finished) break;
|
|
352
|
-
}
|
|
353
|
-
try { await reader2.cancel(); } catch { /* ignore */ }
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async function main() {
|
|
358
|
-
// 1) Load OAuth config & ensure token freshness before any real request
|
|
359
|
-
const oauth = await loadIFlowOAuthConfig(CONFIG_PATH);
|
|
360
|
-
const token = await ensureToken(oauth);
|
|
361
|
-
|
|
362
|
-
// 2) Perform requested action
|
|
363
|
-
if (MODE === 'upstream') {
|
|
364
|
-
await requestUpstreamChat(oauth, token);
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
if (MODE === 'websearch') {
|
|
368
|
-
await requestUpstreamWebSearch(oauth, token);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
if (RUN_TOOLS) {
|
|
372
|
-
await requestProxyChat(); // warmup
|
|
373
|
-
await runResponsesToolLoop();
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
await requestProxyChat();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
main().catch(err => { console.error(err); process.exit(1); });
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { executeTool } from '../dist/server/utils/tool-executor.js';
|
|
2
|
-
|
|
3
|
-
async function run() {
|
|
4
|
-
const payloads = [
|
|
5
|
-
{ name: 'string_cmd_readme_head', args: { command: 'find . -type f -name "README*" | head -20' } },
|
|
6
|
-
{ name: 'string_cmd_md_grep', args: { command: 'find . -type f -name "*.md" | grep -i readme | head -20' } },
|
|
7
|
-
{ name: 'argv_cmd_simple', args: { command: ['find', '.', '-type', 'f', '-name', 'README*'] } },
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
for (const p of payloads) {
|
|
11
|
-
const argStr = JSON.stringify(p.args);
|
|
12
|
-
const res = await executeTool({ id: 'test', name: 'shell', args: argStr });
|
|
13
|
-
console.log('--- case:', p.name, '---');
|
|
14
|
-
console.log('args:', argStr);
|
|
15
|
-
if (res.error) {
|
|
16
|
-
console.log('ERROR:', res.error);
|
|
17
|
-
} else {
|
|
18
|
-
const out = (res.output || '').split('\n').slice(0, 10).join('\n');
|
|
19
|
-
console.log('OK (first 10 lines)');
|
|
20
|
-
console.log(out);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
run().catch(e => { console.error(e); process.exit(1); });
|
|
26
|
-
|