@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
|
@@ -7,6 +7,12 @@ import fs from 'node:fs';
|
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
|
|
10
|
+
function isTruthy(value) {
|
|
11
|
+
if (!value) return false;
|
|
12
|
+
const v = String(value).trim().toLowerCase();
|
|
13
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
function parseArgs(argv) {
|
|
11
17
|
const args = { profile: 'default', url: '', autoMode: '', devMode: false };
|
|
12
18
|
const list = argv.slice(2);
|
|
@@ -65,6 +71,44 @@ async function getCamoufoxCacheRoot() {
|
|
|
65
71
|
});
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
function resolveCamoufoxBinary(cacheRoot) {
|
|
75
|
+
const override = (process.env.ROUTECODEX_CAMOUFOX_BINARY || '').trim();
|
|
76
|
+
if (override) {
|
|
77
|
+
return override;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const isMac = process.platform === 'darwin';
|
|
81
|
+
if (!cacheRoot && isMac) {
|
|
82
|
+
// Best-effort fallback when python3 is unavailable/broken:
|
|
83
|
+
// Camoufox's packaged app is commonly placed under ~/Library/Caches/camoufox/Camoufox.app.
|
|
84
|
+
const guessed = path.join(os.homedir(), 'Library', 'Caches', 'camoufox');
|
|
85
|
+
if (fs.existsSync(path.join(guessed, 'Camoufox.app'))) {
|
|
86
|
+
cacheRoot = guessed;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (cacheRoot && isMac) {
|
|
90
|
+
const appPath = path.join(cacheRoot, 'Camoufox.app');
|
|
91
|
+
const macBinary = path.join(appPath, 'Contents', 'MacOS', 'camoufox');
|
|
92
|
+
if (fs.existsSync(macBinary)) {
|
|
93
|
+
return macBinary;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const located = spawnSync('which', ['camoufox'], { encoding: 'utf-8' });
|
|
99
|
+
if (located.status === 0) {
|
|
100
|
+
const resolved = String(located.stdout || '').trim();
|
|
101
|
+
if (resolved) {
|
|
102
|
+
return resolved;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// ignore lookup failure
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return 'camoufox';
|
|
110
|
+
}
|
|
111
|
+
|
|
68
112
|
async function ensureProfileDir(profileId) {
|
|
69
113
|
const root = path.join(os.homedir(), '.routecodex', 'camoufox-profiles');
|
|
70
114
|
const dir = path.join(root, profileId);
|
|
@@ -108,17 +152,16 @@ async function main() {
|
|
|
108
152
|
|
|
109
153
|
const cacheRoot = await getCamoufoxCacheRoot();
|
|
110
154
|
if (!cacheRoot) {
|
|
111
|
-
console.
|
|
112
|
-
'[camoufox-launch-auth] Failed to resolve Camoufox cache root via "python3 -m camoufox path"'
|
|
155
|
+
console.warn(
|
|
156
|
+
'[camoufox-launch-auth] Failed to resolve Camoufox cache root via "python3 -m camoufox path"; falling back to PATH/override.'
|
|
113
157
|
);
|
|
114
|
-
process.exit(1);
|
|
115
158
|
}
|
|
116
159
|
|
|
117
160
|
const camoufoxBinary = resolveCamoufoxBinary(cacheRoot);
|
|
118
161
|
|
|
119
162
|
if (autoMode && autoMode.trim().toLowerCase() === 'iflow') {
|
|
120
163
|
try {
|
|
121
|
-
await
|
|
164
|
+
await runAutoFlowWithFallback('iflow', { url, profileDir, profileId, camoufoxBinary, devMode });
|
|
122
165
|
process.exit(0);
|
|
123
166
|
} catch (error) {
|
|
124
167
|
console.error(
|
|
@@ -132,7 +175,7 @@ async function main() {
|
|
|
132
175
|
|
|
133
176
|
if (autoMode && autoMode.trim().toLowerCase() === 'gemini') {
|
|
134
177
|
try {
|
|
135
|
-
await
|
|
178
|
+
await runAutoFlowWithFallback('gemini', { url, profileDir, profileId, camoufoxBinary, devMode });
|
|
136
179
|
process.exit(0);
|
|
137
180
|
} catch (error) {
|
|
138
181
|
console.error(
|
|
@@ -146,7 +189,7 @@ async function main() {
|
|
|
146
189
|
|
|
147
190
|
if (autoMode && autoMode.trim().toLowerCase() === 'antigravity') {
|
|
148
191
|
try {
|
|
149
|
-
await
|
|
192
|
+
await runAutoFlowWithFallback('antigravity', { url, profileDir, profileId, camoufoxBinary, devMode });
|
|
150
193
|
process.exit(0);
|
|
151
194
|
} catch (error) {
|
|
152
195
|
console.error(
|
|
@@ -166,59 +209,29 @@ main().catch((err) => {
|
|
|
166
209
|
process.exit(1);
|
|
167
210
|
});
|
|
168
211
|
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
try {
|
|
179
|
-
const located = spawnSync('which', ['camoufox'], { encoding: 'utf-8' });
|
|
180
|
-
if (located.status === 0) {
|
|
181
|
-
const resolved = located.stdout.trim();
|
|
182
|
-
if (resolved) {
|
|
183
|
-
return resolved;
|
|
212
|
+
async function launchManualCamoufox({ camoufoxBinary, profileDir, url }) {
|
|
213
|
+
let browserExitCode = 0;
|
|
214
|
+
let browser = null;
|
|
215
|
+
const shutdownBrowser = (signal = 'SIGTERM') => {
|
|
216
|
+
try {
|
|
217
|
+
if (!browser) {
|
|
218
|
+
return;
|
|
184
219
|
}
|
|
220
|
+
browser.kill(signal);
|
|
221
|
+
} catch {
|
|
222
|
+
// ignore
|
|
185
223
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
224
|
+
};
|
|
225
|
+
['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
|
|
226
|
+
process.on(signal, () => shutdownBrowser(signal));
|
|
227
|
+
});
|
|
191
228
|
|
|
192
|
-
async function launchManualCamoufox({ camoufoxBinary, profileDir, url }) {
|
|
193
|
-
let browserExitCode = 0;
|
|
194
229
|
try {
|
|
195
|
-
|
|
196
|
-
detached:
|
|
230
|
+
browser = spawn(camoufoxBinary, ['-profile', profileDir, url], {
|
|
231
|
+
detached: false,
|
|
197
232
|
stdio: 'ignore'
|
|
198
233
|
});
|
|
199
234
|
|
|
200
|
-
const shutdownBrowser = (signal = 'SIGTERM') => {
|
|
201
|
-
try {
|
|
202
|
-
if (browser.pid) {
|
|
203
|
-
try {
|
|
204
|
-
process.kill(-browser.pid, signal);
|
|
205
|
-
return;
|
|
206
|
-
} catch {
|
|
207
|
-
// process group kill may fail on non-unix systems; fall back to direct kill
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
browser.kill(signal);
|
|
211
|
-
} catch {
|
|
212
|
-
// ignore
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
['SIGTERM', 'SIGINT'].forEach((signal) => {
|
|
217
|
-
process.on(signal, () => {
|
|
218
|
-
shutdownBrowser(signal);
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
235
|
browserExitCode = await new Promise((resolve) => {
|
|
223
236
|
browser.on('exit', (code) => resolve(code ?? 0));
|
|
224
237
|
browser.on('error', () => resolve(1));
|
|
@@ -234,6 +247,95 @@ async function launchManualCamoufox({ camoufoxBinary, profileDir, url }) {
|
|
|
234
247
|
process.exit(browserExitCode);
|
|
235
248
|
}
|
|
236
249
|
|
|
250
|
+
function isSelectorOrTimeoutError(error) {
|
|
251
|
+
const message = error instanceof Error ? error.message : String(error || '');
|
|
252
|
+
return (
|
|
253
|
+
/timeout/i.test(message) ||
|
|
254
|
+
/waiting for selector/i.test(message) ||
|
|
255
|
+
/strict mode violation/i.test(message) ||
|
|
256
|
+
message.includes('未能定位') ||
|
|
257
|
+
message.includes('无法定位')
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function runHeadedManualAssistFlow({ url, profileDir, camoufoxBinary, timeoutMs, label }) {
|
|
262
|
+
let firefox;
|
|
263
|
+
try {
|
|
264
|
+
({ firefox } = await import('playwright-core'));
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`playwright-core is required for headed manual assist (${error instanceof Error ? error.message : String(error)})`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.warn(
|
|
272
|
+
`[camoufox-launch-auth] ${label}: falling back to headed mode for manual completion (no selector match).`
|
|
273
|
+
);
|
|
274
|
+
cleanupExistingCamoufox(profileDir);
|
|
275
|
+
const context = await firefox.launchPersistentContext(profileDir, {
|
|
276
|
+
executablePath: camoufoxBinary,
|
|
277
|
+
headless: false,
|
|
278
|
+
acceptDownloads: false
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
let closed = false;
|
|
282
|
+
const shutdown = async () => {
|
|
283
|
+
if (closed) return;
|
|
284
|
+
closed = true;
|
|
285
|
+
await context.close().catch(() => {});
|
|
286
|
+
};
|
|
287
|
+
['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
|
|
288
|
+
process.on(signal, () => {
|
|
289
|
+
void shutdown().finally(() => process.exit(0));
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
const page = context.pages()[0] || (await context.newPage());
|
|
295
|
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 }).catch(() => {});
|
|
296
|
+
console.log('[camoufox-launch-auth] Headed browser opened. Please complete OAuth manually...');
|
|
297
|
+
const callbackPage = await waitForCallback(context, page, timeoutMs);
|
|
298
|
+
await callbackPage.waitForLoadState('load', { timeout: 120000 }).catch(() => {});
|
|
299
|
+
console.log('[camoufox-launch-auth] OAuth callback detected, manual completion finished.');
|
|
300
|
+
} finally {
|
|
301
|
+
await shutdown();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function runAutoFlowWithFallback(kind, options) {
|
|
306
|
+
const mode = String(kind || '').trim().toLowerCase();
|
|
307
|
+
const label = mode || 'auto';
|
|
308
|
+
const timeoutMs = Number(process.env.ROUTECODEX_CAMOUFOX_GEMINI_TIMEOUT_MS || 120_000);
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
if (mode === 'iflow') {
|
|
312
|
+
await runIflowAutoFlow(options);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (mode === 'gemini') {
|
|
316
|
+
await runGeminiAutoFlow(options);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (mode === 'antigravity') {
|
|
320
|
+
await runAntigravityAutoFlow(options);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
throw new Error(`Unknown auto mode: ${mode}`);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (!options.devMode && isSelectorOrTimeoutError(error)) {
|
|
326
|
+
await runHeadedManualAssistFlow({
|
|
327
|
+
url: options.url,
|
|
328
|
+
profileDir: options.profileDir,
|
|
329
|
+
camoufoxBinary: options.camoufoxBinary,
|
|
330
|
+
timeoutMs: Number(process.env.ROUTECODEX_OAUTH_TIMEOUT_MS || 10 * 60_000),
|
|
331
|
+
label
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
237
339
|
async function runIflowAutoFlow({ url, profileDir, profileId, camoufoxBinary, devMode }) {
|
|
238
340
|
let firefox;
|
|
239
341
|
try {
|
|
@@ -252,6 +354,17 @@ async function runIflowAutoFlow({ url, profileDir, profileId, camoufoxBinary, de
|
|
|
252
354
|
headless,
|
|
253
355
|
acceptDownloads: false
|
|
254
356
|
});
|
|
357
|
+
let closing = false;
|
|
358
|
+
const shutdown = async () => {
|
|
359
|
+
if (closing) return;
|
|
360
|
+
closing = true;
|
|
361
|
+
await context.close().catch(() => {});
|
|
362
|
+
};
|
|
363
|
+
['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
|
|
364
|
+
process.on(signal, () => {
|
|
365
|
+
void shutdown().finally(() => process.exit(0));
|
|
366
|
+
});
|
|
367
|
+
});
|
|
255
368
|
|
|
256
369
|
let callbackObserved = false;
|
|
257
370
|
try {
|
|
@@ -352,7 +465,7 @@ async function runIflowAutoFlow({ url, profileDir, profileId, camoufoxBinary, de
|
|
|
352
465
|
}
|
|
353
466
|
throw error;
|
|
354
467
|
} finally {
|
|
355
|
-
await
|
|
468
|
+
await shutdown();
|
|
356
469
|
}
|
|
357
470
|
}
|
|
358
471
|
|
|
@@ -374,6 +487,17 @@ async function runGeminiAutoFlow({ url, profileDir, camoufoxBinary, devMode }) {
|
|
|
374
487
|
headless: !devMode,
|
|
375
488
|
acceptDownloads: false
|
|
376
489
|
});
|
|
490
|
+
let closing = false;
|
|
491
|
+
const shutdown = async () => {
|
|
492
|
+
if (closing) return;
|
|
493
|
+
closing = true;
|
|
494
|
+
await context.close().catch(() => {});
|
|
495
|
+
};
|
|
496
|
+
['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
|
|
497
|
+
process.on(signal, () => {
|
|
498
|
+
void shutdown().finally(() => process.exit(0));
|
|
499
|
+
});
|
|
500
|
+
});
|
|
377
501
|
|
|
378
502
|
let callbackObserved = false;
|
|
379
503
|
try {
|
|
@@ -440,7 +564,7 @@ async function runGeminiAutoFlow({ url, profileDir, camoufoxBinary, devMode }) {
|
|
|
440
564
|
await callbackPage.waitForLoadState('load', { timeout: timeoutMs }).catch(() => {});
|
|
441
565
|
console.log('[camoufox-launch-auth] OAuth callback detected, automation complete.');
|
|
442
566
|
} finally {
|
|
443
|
-
await
|
|
567
|
+
await shutdown();
|
|
444
568
|
}
|
|
445
569
|
}
|
|
446
570
|
|
|
@@ -462,6 +586,17 @@ async function runAntigravityAutoFlow({ url, profileDir, camoufoxBinary, devMode
|
|
|
462
586
|
headless: !devMode,
|
|
463
587
|
acceptDownloads: false
|
|
464
588
|
});
|
|
589
|
+
let closing = false;
|
|
590
|
+
const shutdown = async () => {
|
|
591
|
+
if (closing) return;
|
|
592
|
+
closing = true;
|
|
593
|
+
await context.close().catch(() => {});
|
|
594
|
+
};
|
|
595
|
+
['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
|
|
596
|
+
process.on(signal, () => {
|
|
597
|
+
void shutdown().finally(() => process.exit(0));
|
|
598
|
+
});
|
|
599
|
+
});
|
|
465
600
|
|
|
466
601
|
let callbackObserved = false;
|
|
467
602
|
try {
|
|
@@ -544,7 +679,7 @@ async function runAntigravityAutoFlow({ url, profileDir, camoufoxBinary, devMode
|
|
|
544
679
|
}
|
|
545
680
|
throw error;
|
|
546
681
|
} finally {
|
|
547
|
-
await
|
|
682
|
+
await shutdown();
|
|
548
683
|
}
|
|
549
684
|
}
|
|
550
685
|
|
|
@@ -584,7 +719,7 @@ async function waitForElementInPages(context, selector, timeoutMs) {
|
|
|
584
719
|
return null;
|
|
585
720
|
}
|
|
586
721
|
|
|
587
|
-
async function waitForCallback(context, fallbackPage) {
|
|
722
|
+
async function waitForCallback(context, fallbackPage, timeoutMs = 120000) {
|
|
588
723
|
const isCallbackUrl = (current) => {
|
|
589
724
|
if (typeof current !== 'string') {
|
|
590
725
|
return false;
|
|
@@ -605,13 +740,13 @@ async function waitForCallback(context, fallbackPage) {
|
|
|
605
740
|
}
|
|
606
741
|
|
|
607
742
|
try {
|
|
608
|
-
await fallbackPage.waitForURL((current) => isCallbackUrl(current), { timeout:
|
|
743
|
+
await fallbackPage.waitForURL((current) => isCallbackUrl(current), { timeout: timeoutMs });
|
|
609
744
|
return fallbackPage;
|
|
610
745
|
} catch {
|
|
611
746
|
// ignore and wait for popup
|
|
612
747
|
}
|
|
613
748
|
|
|
614
|
-
const callback = await context.waitForEvent('page', { timeout:
|
|
749
|
+
const callback = await context.waitForEvent('page', { timeout: timeoutMs });
|
|
615
750
|
await callback.waitForLoadState('domcontentloaded', { timeout: 60000 }).catch(() => {});
|
|
616
751
|
return callback;
|
|
617
752
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function runGit(args) {
|
|
6
|
+
const out = spawnSync('git', args, { encoding: 'utf8' });
|
|
7
|
+
if (out.status !== 0) {
|
|
8
|
+
throw new Error(`git ${args.join(' ')} failed: ${out.stderr || out.stdout}`);
|
|
9
|
+
}
|
|
10
|
+
return String(out.stdout || '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isIgnoredByGit(p) {
|
|
14
|
+
const out = spawnSync('git', ['check-ignore', '-q', p], { encoding: 'utf8' });
|
|
15
|
+
return out.status === 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function listRootEntries() {
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
return fs
|
|
21
|
+
.readdirSync(cwd, { withFileTypes: true })
|
|
22
|
+
.filter((d) => d.name !== '.git')
|
|
23
|
+
.filter((d) => !isIgnoredByGit(d.name))
|
|
24
|
+
.map((d) => d.name)
|
|
25
|
+
.sort();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isForbiddenRootFile(p) {
|
|
29
|
+
const base = path.posix.basename(p);
|
|
30
|
+
const allow = new Set(['AGENTS.md', 'README.md', 'task.md']);
|
|
31
|
+
if (allow.has(base)) return false;
|
|
32
|
+
if (/^test-.*\.(mjs|js|ts|py)$/i.test(base)) return true;
|
|
33
|
+
if (/^debug-.*\.(mjs|js|ts)$/i.test(base)) return true;
|
|
34
|
+
if (/\.pid$/i.test(base)) return true;
|
|
35
|
+
if (/\.tgz$/i.test(base)) return true;
|
|
36
|
+
if (base === 'plan.md') return true;
|
|
37
|
+
if (base === 'task-fallback.md') return true;
|
|
38
|
+
if (base === 'task.archive.md') return true;
|
|
39
|
+
if (base === 'WARP.md') return true;
|
|
40
|
+
if (base === 'CLAUDE.md') return true;
|
|
41
|
+
if (/(_SUMMARY|_FIX)_/i.test(base) && base.toLowerCase().endsWith('.md')) return true;
|
|
42
|
+
// Disallow ad-hoc root markdown by default (docs belong under docs/).
|
|
43
|
+
if (base.toLowerCase().endsWith('.md')) return true;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isForbiddenTrackedPath(p) {
|
|
48
|
+
// Keep the rules narrow and explicit: this is an audit guard, not a policy engine.
|
|
49
|
+
if (p.startsWith('docs/archive/')) return true;
|
|
50
|
+
if (p.startsWith('scripts/dev/')) return true;
|
|
51
|
+
if (p.startsWith('scripts/test-') && p.endsWith('.mjs')) return true;
|
|
52
|
+
if (p.startsWith('tools/')) return true;
|
|
53
|
+
if (p.startsWith('replay/')) return true;
|
|
54
|
+
if (p.startsWith('servertool/')) return true;
|
|
55
|
+
if (p.startsWith('verified-configs/')) return true;
|
|
56
|
+
if (p.startsWith('interpreter/')) return true;
|
|
57
|
+
if (p.startsWith('exporters/')) return true;
|
|
58
|
+
if (p.startsWith('bin/')) return true;
|
|
59
|
+
if (p.startsWith('.npm-cache-local/')) return true;
|
|
60
|
+
if (p.startsWith('.claude/')) return true;
|
|
61
|
+
if (p.startsWith('.iflow/')) return true;
|
|
62
|
+
if (p === '.secrets.baseline') return true;
|
|
63
|
+
if (p.endsWith('/.DS_Store') || p === '.DS_Store') return true;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function checkRootLayout() {
|
|
68
|
+
// Fixed top-level layout. Adding new root entries requires an explicit policy change.
|
|
69
|
+
const allowed = new Set([
|
|
70
|
+
'eslint.config.js',
|
|
71
|
+
'.github',
|
|
72
|
+
'.gitignore',
|
|
73
|
+
'AGENTS.md',
|
|
74
|
+
'README.md',
|
|
75
|
+
'config',
|
|
76
|
+
'configsamples',
|
|
77
|
+
'dist',
|
|
78
|
+
'docs',
|
|
79
|
+
'jest.config.js',
|
|
80
|
+
'node_modules',
|
|
81
|
+
'package',
|
|
82
|
+
'package-lock.json',
|
|
83
|
+
'package.json',
|
|
84
|
+
'rcc',
|
|
85
|
+
'samples',
|
|
86
|
+
'scripts',
|
|
87
|
+
'sharedmodule',
|
|
88
|
+
'src',
|
|
89
|
+
'task.md',
|
|
90
|
+
'tests',
|
|
91
|
+
'tmp',
|
|
92
|
+
'tsconfig.json',
|
|
93
|
+
'vendor',
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const rootEntries = listRootEntries();
|
|
97
|
+
const unexpected = rootEntries.filter((name) => !allowed.has(name));
|
|
98
|
+
if (unexpected.length) {
|
|
99
|
+
console.error('[repo-sanity] unexpected root entries (top-level is fixed):');
|
|
100
|
+
for (const name of unexpected) console.error(`- ${name}`);
|
|
101
|
+
process.exit(2);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function checkUntrackedNotIgnored() {
|
|
106
|
+
// Fail fast if anything new appears outside gitignore (anywhere in repo).
|
|
107
|
+
const out = runGit(['ls-files', '--others', '--exclude-standard']);
|
|
108
|
+
const paths = out
|
|
109
|
+
.split('\n')
|
|
110
|
+
.map((s) => s.trim())
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
if (paths.length) {
|
|
113
|
+
console.error('[repo-sanity] untracked files not ignored (add them to git or gitignore):');
|
|
114
|
+
for (const p of paths.slice(0, 200)) console.error(`- ${p}`);
|
|
115
|
+
if (paths.length > 200) console.error(`- ... (${paths.length - 200} more)`);
|
|
116
|
+
process.exit(2);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const files = runGit(['ls-files']).split('\n').map((s) => s.trim()).filter(Boolean);
|
|
121
|
+
const forbidden = [];
|
|
122
|
+
for (const p of files) {
|
|
123
|
+
if (isForbiddenTrackedPath(p)) forbidden.push(p);
|
|
124
|
+
if (!p.includes('/') && isForbiddenRootFile(p)) forbidden.push(p);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (forbidden.length) {
|
|
128
|
+
console.error('[repo-sanity] forbidden tracked files detected:');
|
|
129
|
+
for (const p of Array.from(new Set(forbidden)).sort()) {
|
|
130
|
+
console.error(`- ${p}`);
|
|
131
|
+
}
|
|
132
|
+
process.exit(2);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
checkRootLayout();
|
|
136
|
+
checkUntrackedNotIgnored();
|
|
137
|
+
|
|
138
|
+
console.log('[repo-sanity] ok');
|
|
@@ -6,6 +6,7 @@ import os from 'os';
|
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { spawn } from 'child_process';
|
|
8
8
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
9
|
+
import http from 'node:http';
|
|
9
10
|
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const PROJECT_ROOT = path.resolve(__dirname, '../..');
|
|
@@ -251,7 +252,17 @@ async function waitForHealth(port, serverProc, timeoutMs = 20000) {
|
|
|
251
252
|
try {
|
|
252
253
|
const res = await fetch(`http://127.0.0.1:${port}/health`, { method: 'GET' });
|
|
253
254
|
if (res.ok) {
|
|
254
|
-
|
|
255
|
+
// /health becomes reachable before runtime is fully initialized (server starts listening first
|
|
256
|
+
// to support token portal). Mock regressions must wait until hub pipeline is ready.
|
|
257
|
+
try {
|
|
258
|
+
const data = await res.json();
|
|
259
|
+
const ready = data && (data.ready === true || data.pipelineReady === true || data.status === 'ok');
|
|
260
|
+
if (ready) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
// ignore JSON errors, retry
|
|
265
|
+
}
|
|
255
266
|
}
|
|
256
267
|
} catch {
|
|
257
268
|
// retry
|
|
@@ -276,6 +287,150 @@ async function stopServer(child, forceTimeout = 5000) {
|
|
|
276
287
|
child.kill('SIGKILL');
|
|
277
288
|
}
|
|
278
289
|
|
|
290
|
+
async function createLocalUpstreamServer(handler) {
|
|
291
|
+
return await new Promise((resolve, reject) => {
|
|
292
|
+
const server = http.createServer(handler);
|
|
293
|
+
server.on('error', reject);
|
|
294
|
+
server.listen(0, '127.0.0.1', () => {
|
|
295
|
+
const address = server.address();
|
|
296
|
+
if (!address || typeof address !== 'object') {
|
|
297
|
+
reject(new Error('Failed to obtain listen address for upstream server'));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
resolve({
|
|
301
|
+
server,
|
|
302
|
+
port: address.port
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function buildIflowUaProbeConfig(port, upstreamPort) {
|
|
309
|
+
return {
|
|
310
|
+
version: '1.0.0',
|
|
311
|
+
virtualrouter: {
|
|
312
|
+
inputProtocol: 'openai',
|
|
313
|
+
outputProtocol: 'openai',
|
|
314
|
+
providers: {
|
|
315
|
+
iflow: {
|
|
316
|
+
id: 'iflow',
|
|
317
|
+
enabled: true,
|
|
318
|
+
type: 'iflow',
|
|
319
|
+
baseURL: `http://127.0.0.1:${upstreamPort}/v1`,
|
|
320
|
+
compatibilityProfile: 'chat:iflow',
|
|
321
|
+
auth: {
|
|
322
|
+
type: 'apikey',
|
|
323
|
+
apiKey: 'test-upstream-token'
|
|
324
|
+
},
|
|
325
|
+
models: {
|
|
326
|
+
'glm-4.7': { supportsStreaming: false }
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
routing: {
|
|
331
|
+
default: ['iflow.glm-4.7']
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
httpserver: {
|
|
335
|
+
host: '127.0.0.1',
|
|
336
|
+
port
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function runIflowUserAgentRegression() {
|
|
342
|
+
const seen = {
|
|
343
|
+
path: '',
|
|
344
|
+
headers: {},
|
|
345
|
+
body: ''
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const { server: upstream, port: upstreamPort } = await createLocalUpstreamServer(async (req, res) => {
|
|
349
|
+
try {
|
|
350
|
+
seen.path = String(req.url || '');
|
|
351
|
+
seen.headers = req.headers || {};
|
|
352
|
+
let raw = '';
|
|
353
|
+
req.setEncoding('utf8');
|
|
354
|
+
req.on('data', (chunk) => {
|
|
355
|
+
raw += chunk;
|
|
356
|
+
});
|
|
357
|
+
await new Promise((resolve) => req.on('end', resolve));
|
|
358
|
+
seen.body = raw;
|
|
359
|
+
} catch {
|
|
360
|
+
// ignore
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
364
|
+
res.end(
|
|
365
|
+
JSON.stringify({
|
|
366
|
+
id: 'chatcmpl_mock_iflow_ua',
|
|
367
|
+
object: 'chat.completion',
|
|
368
|
+
created: Math.floor(Date.now() / 1000),
|
|
369
|
+
model: 'glm-4.7',
|
|
370
|
+
choices: [
|
|
371
|
+
{
|
|
372
|
+
index: 0,
|
|
373
|
+
message: { role: 'assistant', content: 'ok' },
|
|
374
|
+
finish_reason: 'stop'
|
|
375
|
+
}
|
|
376
|
+
],
|
|
377
|
+
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }
|
|
378
|
+
})
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const port = 5750;
|
|
383
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'routecodex-iflow-ua-'));
|
|
384
|
+
const configPath = path.join(dir, 'config.json');
|
|
385
|
+
await fs.writeFile(configPath, JSON.stringify(buildIflowUaProbeConfig(port, upstreamPort), null, 2), 'utf-8');
|
|
386
|
+
|
|
387
|
+
const entry = path.join(PROJECT_ROOT, 'dist', 'index.js');
|
|
388
|
+
const child = spawn(process.execPath, [entry], {
|
|
389
|
+
cwd: PROJECT_ROOT,
|
|
390
|
+
env: {
|
|
391
|
+
...process.env,
|
|
392
|
+
ROUTECODEX_PORT: String(port),
|
|
393
|
+
ROUTECODEX_CONFIG_PATH: configPath,
|
|
394
|
+
RCC_PORT: String(port),
|
|
395
|
+
RCC_CONFIG_PATH: configPath
|
|
396
|
+
},
|
|
397
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
await waitForHealth(port, child);
|
|
402
|
+
const res = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
|
403
|
+
method: 'POST',
|
|
404
|
+
headers: {
|
|
405
|
+
'Content-Type': 'application/json',
|
|
406
|
+
// If UA precedence is wrong, this inbound UA will leak to upstream and break iFlow glm-4.7.
|
|
407
|
+
'User-Agent': 'curl/8.7.1'
|
|
408
|
+
},
|
|
409
|
+
body: JSON.stringify({
|
|
410
|
+
model: 'iflow.glm-4.7',
|
|
411
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
412
|
+
max_tokens: 16,
|
|
413
|
+
stream: false
|
|
414
|
+
})
|
|
415
|
+
});
|
|
416
|
+
const text = await res.text();
|
|
417
|
+
if (!res.ok) {
|
|
418
|
+
throw new Error(`ua probe request failed: HTTP ${res.status}: ${text}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const upstreamUa = typeof seen.headers['user-agent'] === 'string' ? seen.headers['user-agent'] : '';
|
|
422
|
+
if (upstreamUa !== 'iFlow-Cli') {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`iflow UA regression: expected upstream user-agent="iFlow-Cli", got ${JSON.stringify(upstreamUa)} (path=${seen.path})`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
} finally {
|
|
428
|
+
await stopServer(child);
|
|
429
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
430
|
+
await new Promise((resolve) => upstream.close(() => resolve()));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
279
434
|
function collectInvalidNames(payload) {
|
|
280
435
|
const failures = [];
|
|
281
436
|
const check = (value, location) => {
|
|
@@ -546,6 +701,7 @@ async function runSample(sample, index) {
|
|
|
546
701
|
|
|
547
702
|
async function main() {
|
|
548
703
|
await ensureCliAvailable();
|
|
704
|
+
await runIflowUserAgentRegression();
|
|
549
705
|
const samples = await loadRegistry();
|
|
550
706
|
const watchedTags = new Set(['invalid_name', 'missing_output', 'missing_tool_call_id', 'require_fc_call_ids', 'regression']);
|
|
551
707
|
const regressionSamples = samples.filter(
|