@hibanacloud/core 0.3.15
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/dist/.last_build +0 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/src/__mocks__/fs/promises.d.ts +11 -0
- package/dist/src/__mocks__/fs/promises.js +17 -0
- package/dist/src/__mocks__/fs/promises.js.map +1 -0
- package/dist/src/code_assist/codeAssist.d.ts +12 -0
- package/dist/src/code_assist/codeAssist.js +31 -0
- package/dist/src/code_assist/codeAssist.js.map +1 -0
- package/dist/src/code_assist/converter.d.ts +74 -0
- package/dist/src/code_assist/converter.js +160 -0
- package/dist/src/code_assist/converter.js.map +1 -0
- package/dist/src/code_assist/converter.test.d.ts +6 -0
- package/dist/src/code_assist/converter.test.js +372 -0
- package/dist/src/code_assist/converter.test.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
- package/dist/src/code_assist/oauth-credential-storage.js +109 -0
- package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.test.d.ts +6 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js +136 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
- package/dist/src/code_assist/oauth2.d.ts +22 -0
- package/dist/src/code_assist/oauth2.js +431 -0
- package/dist/src/code_assist/oauth2.js.map +1 -0
- package/dist/src/code_assist/oauth2.test.d.ts +6 -0
- package/dist/src/code_assist/oauth2.test.js +818 -0
- package/dist/src/code_assist/oauth2.test.js.map +1 -0
- package/dist/src/code_assist/server.d.ts +37 -0
- package/dist/src/code_assist/server.js +148 -0
- package/dist/src/code_assist/server.js.map +1 -0
- package/dist/src/code_assist/server.test.d.ts +6 -0
- package/dist/src/code_assist/server.test.js +159 -0
- package/dist/src/code_assist/server.test.js.map +1 -0
- package/dist/src/code_assist/setup.d.ts +20 -0
- package/dist/src/code_assist/setup.js +101 -0
- package/dist/src/code_assist/setup.js.map +1 -0
- package/dist/src/code_assist/setup.test.d.ts +6 -0
- package/dist/src/code_assist/setup.test.js +171 -0
- package/dist/src/code_assist/setup.test.js.map +1 -0
- package/dist/src/code_assist/types.d.ts +163 -0
- package/dist/src/code_assist/types.js +46 -0
- package/dist/src/code_assist/types.js.map +1 -0
- package/dist/src/config/config.d.ts +431 -0
- package/dist/src/config/config.js +869 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/config.test.d.ts +6 -0
- package/dist/src/config/config.test.js +896 -0
- package/dist/src/config/config.test.js.map +1 -0
- package/dist/src/config/constants.d.ts +11 -0
- package/dist/src/config/constants.js +16 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/config/flashFallback.test.d.ts +6 -0
- package/dist/src/config/flashFallback.test.js +87 -0
- package/dist/src/config/flashFallback.test.js.map +1 -0
- package/dist/src/config/models.d.ts +28 -0
- package/dist/src/config/models.js +42 -0
- package/dist/src/config/models.js.map +1 -0
- package/dist/src/config/models.test.d.ts +6 -0
- package/dist/src/config/models.test.js +55 -0
- package/dist/src/config/models.test.js.map +1 -0
- package/dist/src/config/storage.d.ts +34 -0
- package/dist/src/config/storage.js +95 -0
- package/dist/src/config/storage.js.map +1 -0
- package/dist/src/config/storage.test.d.ts +6 -0
- package/dist/src/config/storage.test.js +43 -0
- package/dist/src/config/storage.test.js.map +1 -0
- package/dist/src/core/__tests__/openaiTimeoutHandling.test.d.ts +6 -0
- package/dist/src/core/__tests__/openaiTimeoutHandling.test.js +295 -0
- package/dist/src/core/__tests__/openaiTimeoutHandling.test.js.map +1 -0
- package/dist/src/core/__tests__/orphanedToolCallsTest.d.ts +64 -0
- package/dist/src/core/__tests__/orphanedToolCallsTest.js +122 -0
- package/dist/src/core/__tests__/orphanedToolCallsTest.js.map +1 -0
- package/dist/src/core/baseLlmClient.d.ts +49 -0
- package/dist/src/core/baseLlmClient.js +104 -0
- package/dist/src/core/baseLlmClient.js.map +1 -0
- package/dist/src/core/baseLlmClient.test.d.ts +6 -0
- package/dist/src/core/baseLlmClient.test.js +323 -0
- package/dist/src/core/baseLlmClient.test.js.map +1 -0
- package/dist/src/core/client.d.ts +56 -0
- package/dist/src/core/client.js +522 -0
- package/dist/src/core/client.js.map +1 -0
- package/dist/src/core/client.test.d.ts +6 -0
- package/dist/src/core/client.test.js +1829 -0
- package/dist/src/core/client.test.js.map +1 -0
- package/dist/src/core/contentGenerator.d.ts +52 -0
- package/dist/src/core/contentGenerator.js +106 -0
- package/dist/src/core/contentGenerator.js.map +1 -0
- package/dist/src/core/contentGenerator.test.d.ts +6 -0
- package/dist/src/core/contentGenerator.test.js +77 -0
- package/dist/src/core/contentGenerator.test.js.map +1 -0
- package/dist/src/core/coreToolScheduler.d.ts +134 -0
- package/dist/src/core/coreToolScheduler.js +774 -0
- package/dist/src/core/coreToolScheduler.js.map +1 -0
- package/dist/src/core/coreToolScheduler.test.d.ts +6 -0
- package/dist/src/core/coreToolScheduler.test.js +1802 -0
- package/dist/src/core/coreToolScheduler.test.js.map +1 -0
- package/dist/src/core/geminiChat.d.ts +124 -0
- package/dist/src/core/geminiChat.js +518 -0
- package/dist/src/core/geminiChat.js.map +1 -0
- package/dist/src/core/geminiChat.test.d.ts +6 -0
- package/dist/src/core/geminiChat.test.js +1149 -0
- package/dist/src/core/geminiChat.test.js.map +1 -0
- package/dist/src/core/geminiRequest.d.ts +13 -0
- package/dist/src/core/geminiRequest.js +11 -0
- package/dist/src/core/geminiRequest.js.map +1 -0
- package/dist/src/core/geminiRequest.test.d.ts +6 -0
- package/dist/src/core/geminiRequest.test.js +73 -0
- package/dist/src/core/geminiRequest.test.js.map +1 -0
- package/dist/src/core/logger.d.ts +67 -0
- package/dist/src/core/logger.js +361 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/logger.test.d.ts +6 -0
- package/dist/src/core/logger.test.js +534 -0
- package/dist/src/core/logger.test.js.map +1 -0
- package/dist/src/core/loggingContentGenerator.d.ts +25 -0
- package/dist/src/core/loggingContentGenerator.js +94 -0
- package/dist/src/core/loggingContentGenerator.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.d.ts +16 -0
- package/dist/src/core/nonInteractiveToolExecutor.js +29 -0
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.d.ts +6 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js +294 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/constants.d.ts +7 -0
- package/dist/src/core/openaiContentGenerator/constants.js +8 -0
- package/dist/src/core/openaiContentGenerator/constants.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/converter.d.ts +108 -0
- package/dist/src/core/openaiContentGenerator/converter.js +851 -0
- package/dist/src/core/openaiContentGenerator/converter.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/converter.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/converter.test.js +108 -0
- package/dist/src/core/openaiContentGenerator/converter.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.d.ts +20 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.js +82 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.test.js +287 -0
- package/dist/src/core/openaiContentGenerator/errorHandler.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/index.d.ts +22 -0
- package/dist/src/core/openaiContentGenerator/index.js +49 -0
- package/dist/src/core/openaiContentGenerator/index.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.d.ts +21 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.js +105 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.test.js +230 -0
- package/dist/src/core/openaiContentGenerator/openaiContentGenerator.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/pipeline.d.ts +66 -0
- package/dist/src/core/openaiContentGenerator/pipeline.js +265 -0
- package/dist/src/core/openaiContentGenerator/pipeline.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/pipeline.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/pipeline.test.js +1028 -0
- package/dist/src/core/openaiContentGenerator/pipeline.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/README.md +61 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.d.ts +14 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.js +28 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.test.d.ts +1 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.test.js +138 -0
- package/dist/src/core/openaiContentGenerator/provider/anthropic.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.d.ts +63 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.js +241 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.test.js +707 -0
- package/dist/src/core/openaiContentGenerator/provider/dashscope.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.d.ts +14 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.js +70 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.test.js +151 -0
- package/dist/src/core/openaiContentGenerator/provider/deepseek.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/default.d.ts +15 -0
- package/dist/src/core/openaiContentGenerator/provider/default.js +48 -0
- package/dist/src/core/openaiContentGenerator/provider/default.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/default.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/provider/default.test.js +176 -0
- package/dist/src/core/openaiContentGenerator/provider/default.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/index.d.ts +7 -0
- package/dist/src/core/openaiContentGenerator/provider/index.js +7 -0
- package/dist/src/core/openaiContentGenerator/provider/index.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.d.ts +17 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.js +25 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.test.js +66 -0
- package/dist/src/core/openaiContentGenerator/provider/modelscope.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.d.ts +10 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.js +33 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.test.js +175 -0
- package/dist/src/core/openaiContentGenerator/provider/openrouter.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/provider/types.d.ts +23 -0
- package/dist/src/core/openaiContentGenerator/provider/types.js +2 -0
- package/dist/src/core/openaiContentGenerator/provider/types.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.d.ts +145 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.js +381 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.test.js +537 -0
- package/dist/src/core/openaiContentGenerator/streamingToolCallParser.test.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.d.ts +36 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.js +150 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.js.map +1 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.test.d.ts +6 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.test.js +978 -0
- package/dist/src/core/openaiContentGenerator/telemetryService.test.js.map +1 -0
- package/dist/src/core/prompts.d.ts +73 -0
- package/dist/src/core/prompts.js +813 -0
- package/dist/src/core/prompts.js.map +1 -0
- package/dist/src/core/prompts.test.d.ts +6 -0
- package/dist/src/core/prompts.test.js +488 -0
- package/dist/src/core/prompts.test.js.map +1 -0
- package/dist/src/core/tokenLimits.d.ts +25 -0
- package/dist/src/core/tokenLimits.js +194 -0
- package/dist/src/core/tokenLimits.js.map +1 -0
- package/dist/src/core/tokenLimits.test.d.ts +1 -0
- package/dist/src/core/tokenLimits.test.js +304 -0
- package/dist/src/core/tokenLimits.test.js.map +1 -0
- package/dist/src/core/turn.d.ts +152 -0
- package/dist/src/core/turn.js +187 -0
- package/dist/src/core/turn.js.map +1 -0
- package/dist/src/core/turn.test.d.ts +6 -0
- package/dist/src/core/turn.test.js +628 -0
- package/dist/src/core/turn.test.js.map +1 -0
- package/dist/src/fallback/handler.d.ts +7 -0
- package/dist/src/fallback/handler.js +97 -0
- package/dist/src/fallback/handler.js.map +1 -0
- package/dist/src/fallback/handler.test.d.ts +6 -0
- package/dist/src/fallback/handler.test.js +130 -0
- package/dist/src/fallback/handler.test.js.map +1 -0
- package/dist/src/fallback/types.d.ts +14 -0
- package/dist/src/fallback/types.js +7 -0
- package/dist/src/fallback/types.js.map +1 -0
- package/dist/src/generated/git-commit.d.ts +7 -0
- package/dist/src/generated/git-commit.js +10 -0
- package/dist/src/generated/git-commit.js.map +1 -0
- package/dist/src/ide/constants.d.ts +9 -0
- package/dist/src/ide/constants.js +10 -0
- package/dist/src/ide/constants.js.map +1 -0
- package/dist/src/ide/detect-ide.d.ts +56 -0
- package/dist/src/ide/detect-ide.js +68 -0
- package/dist/src/ide/detect-ide.js.map +1 -0
- package/dist/src/ide/detect-ide.test.d.ts +6 -0
- package/dist/src/ide/detect-ide.test.js +113 -0
- package/dist/src/ide/detect-ide.test.js.map +1 -0
- package/dist/src/ide/ide-client.d.ts +110 -0
- package/dist/src/ide/ide-client.js +651 -0
- package/dist/src/ide/ide-client.js.map +1 -0
- package/dist/src/ide/ide-client.test.d.ts +6 -0
- package/dist/src/ide/ide-client.test.js +390 -0
- package/dist/src/ide/ide-client.test.js.map +1 -0
- package/dist/src/ide/ide-installer.d.ts +14 -0
- package/dist/src/ide/ide-installer.js +112 -0
- package/dist/src/ide/ide-installer.js.map +1 -0
- package/dist/src/ide/ide-installer.test.d.ts +6 -0
- package/dist/src/ide/ide-installer.test.js +134 -0
- package/dist/src/ide/ide-installer.test.js.map +1 -0
- package/dist/src/ide/ideContext.d.ts +44 -0
- package/dist/src/ide/ideContext.js +101 -0
- package/dist/src/ide/ideContext.js.map +1 -0
- package/dist/src/ide/ideContext.test.d.ts +6 -0
- package/dist/src/ide/ideContext.test.js +393 -0
- package/dist/src/ide/ideContext.test.js.map +1 -0
- package/dist/src/ide/process-utils.d.ts +21 -0
- package/dist/src/ide/process-utils.js +171 -0
- package/dist/src/ide/process-utils.js.map +1 -0
- package/dist/src/ide/process-utils.test.d.ts +6 -0
- package/dist/src/ide/process-utils.test.js +158 -0
- package/dist/src/ide/process-utils.test.js.map +1 -0
- package/dist/src/ide/types.d.ts +486 -0
- package/dist/src/ide/types.js +138 -0
- package/dist/src/ide/types.js.map +1 -0
- package/dist/src/index.d.ts +99 -0
- package/dist/src/index.js +112 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/index.test.d.ts +6 -0
- package/dist/src/index.test.js +12 -0
- package/dist/src/index.test.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.d.ts +23 -0
- package/dist/src/mcp/google-auth-provider.js +72 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.test.d.ts +6 -0
- package/dist/src/mcp/google-auth-provider.test.js +89 -0
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-provider.d.ts +150 -0
- package/dist/src/mcp/oauth-provider.js +613 -0
- package/dist/src/mcp/oauth-provider.js.map +1 -0
- package/dist/src/mcp/oauth-provider.test.d.ts +6 -0
- package/dist/src/mcp/oauth-provider.test.js +847 -0
- package/dist/src/mcp/oauth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.d.ts +65 -0
- package/dist/src/mcp/oauth-token-storage.js +180 -0
- package/dist/src/mcp/oauth-token-storage.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/oauth-token-storage.test.js +299 -0
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -0
- package/dist/src/mcp/oauth-utils.d.ts +119 -0
- package/dist/src/mcp/oauth-utils.js +236 -0
- package/dist/src/mcp/oauth-utils.js.map +1 -0
- package/dist/src/mcp/oauth-utils.test.d.ts +6 -0
- package/dist/src/mcp/oauth-utils.test.js +199 -0
- package/dist/src/mcp/oauth-utils.test.js.map +1 -0
- package/dist/src/mcp/sa-impersonation-provider.d.ts +33 -0
- package/dist/src/mcp/sa-impersonation-provider.js +130 -0
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -0
- package/dist/src/mcp/sa-impersonation-provider.test.d.ts +6 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js +117 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js.map +1 -0
- package/dist/src/mcp/token-storage/base-token-storage.d.ts +19 -0
- package/dist/src/mcp/token-storage/base-token-storage.js +36 -0
- package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.js +160 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
- package/dist/src/mcp/token-storage/file-token-storage.js +144 -0
- package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js +235 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/index.d.ts +11 -0
- package/dist/src/mcp/token-storage/index.js +12 -0
- package/dist/src/mcp/token-storage/index.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +31 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js +190 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js +254 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/types.d.ts +38 -0
- package/dist/src/mcp/token-storage/types.js +11 -0
- package/dist/src/mcp/token-storage/types.js.map +1 -0
- package/dist/src/mocks/msw.d.ts +6 -0
- package/dist/src/mocks/msw.js +8 -0
- package/dist/src/mocks/msw.js.map +1 -0
- package/dist/src/oauth/callbackServer.d.ts +15 -0
- package/dist/src/oauth/callbackServer.js +89 -0
- package/dist/src/oauth/callbackServer.js.map +1 -0
- package/dist/src/oauth/oauthClient.d.ts +57 -0
- package/dist/src/oauth/oauthClient.js +233 -0
- package/dist/src/oauth/oauthClient.js.map +1 -0
- package/dist/src/oauth/pkce.d.ts +25 -0
- package/dist/src/oauth/pkce.js +45 -0
- package/dist/src/oauth/pkce.js.map +1 -0
- package/dist/src/oauth/tokenManager.d.ts +37 -0
- package/dist/src/oauth/tokenManager.js +110 -0
- package/dist/src/oauth/tokenManager.js.map +1 -0
- package/dist/src/output/json-formatter.d.ts +11 -0
- package/dist/src/output/json-formatter.js +30 -0
- package/dist/src/output/json-formatter.js.map +1 -0
- package/dist/src/output/json-formatter.test.d.ts +6 -0
- package/dist/src/output/json-formatter.test.js +266 -0
- package/dist/src/output/json-formatter.test.js.map +1 -0
- package/dist/src/output/types.d.ts +25 -0
- package/dist/src/output/types.js +17 -0
- package/dist/src/output/types.js.map +1 -0
- package/dist/src/prompts/mcp-prompts.d.ts +8 -0
- package/dist/src/prompts/mcp-prompts.js +13 -0
- package/dist/src/prompts/mcp-prompts.js.map +1 -0
- package/dist/src/prompts/prompt-registry.d.ts +34 -0
- package/dist/src/prompts/prompt-registry.js +63 -0
- package/dist/src/prompts/prompt-registry.js.map +1 -0
- package/dist/src/qwen/qwenContentGenerator.d.ts +70 -0
- package/dist/src/qwen/qwenContentGenerator.js +180 -0
- package/dist/src/qwen/qwenContentGenerator.js.map +1 -0
- package/dist/src/qwen/qwenContentGenerator.test.d.ts +6 -0
- package/dist/src/qwen/qwenContentGenerator.test.js +1178 -0
- package/dist/src/qwen/qwenContentGenerator.test.js.map +1 -0
- package/dist/src/qwen/qwenOAuth2.d.ts +194 -0
- package/dist/src/qwen/qwenOAuth2.js +594 -0
- package/dist/src/qwen/qwenOAuth2.js.map +1 -0
- package/dist/src/qwen/qwenOAuth2.test.d.ts +6 -0
- package/dist/src/qwen/qwenOAuth2.test.js +1724 -0
- package/dist/src/qwen/qwenOAuth2.test.js.map +1 -0
- package/dist/src/qwen/sharedTokenManager.d.ts +196 -0
- package/dist/src/qwen/sharedTokenManager.js +647 -0
- package/dist/src/qwen/sharedTokenManager.js.map +1 -0
- package/dist/src/qwen/sharedTokenManager.test.d.ts +7 -0
- package/dist/src/qwen/sharedTokenManager.test.js +662 -0
- package/dist/src/qwen/sharedTokenManager.test.js.map +1 -0
- package/dist/src/services/chatCompressionService.d.ts +32 -0
- package/dist/src/services/chatCompressionService.js +180 -0
- package/dist/src/services/chatCompressionService.js.map +1 -0
- package/dist/src/services/chatCompressionService.test.d.ts +6 -0
- package/dist/src/services/chatCompressionService.test.js +292 -0
- package/dist/src/services/chatCompressionService.test.js.map +1 -0
- package/dist/src/services/chatRecordingService.d.ts +144 -0
- package/dist/src/services/chatRecordingService.js +330 -0
- package/dist/src/services/chatRecordingService.js.map +1 -0
- package/dist/src/services/chatRecordingService.test.d.ts +6 -0
- package/dist/src/services/chatRecordingService.test.js +332 -0
- package/dist/src/services/chatRecordingService.test.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.d.ts +45 -0
- package/dist/src/services/fileDiscoveryService.js +104 -0
- package/dist/src/services/fileDiscoveryService.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.test.d.ts +6 -0
- package/dist/src/services/fileDiscoveryService.test.js +143 -0
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -0
- package/dist/src/services/fileSystemService.d.ts +40 -0
- package/dist/src/services/fileSystemService.js +29 -0
- package/dist/src/services/fileSystemService.js.map +1 -0
- package/dist/src/services/fileSystemService.test.d.ts +6 -0
- package/dist/src/services/fileSystemService.test.js +41 -0
- package/dist/src/services/fileSystemService.test.js.map +1 -0
- package/dist/src/services/gitService.d.ts +22 -0
- package/dist/src/services/gitService.js +98 -0
- package/dist/src/services/gitService.js.map +1 -0
- package/dist/src/services/gitService.test.d.ts +6 -0
- package/dist/src/services/gitService.test.js +187 -0
- package/dist/src/services/gitService.test.js.map +1 -0
- package/dist/src/services/loopDetectionService.d.ts +103 -0
- package/dist/src/services/loopDetectionService.js +379 -0
- package/dist/src/services/loopDetectionService.js.map +1 -0
- package/dist/src/services/loopDetectionService.test.d.ts +6 -0
- package/dist/src/services/loopDetectionService.test.js +588 -0
- package/dist/src/services/loopDetectionService.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.d.ts +102 -0
- package/dist/src/services/shellExecutionService.js +522 -0
- package/dist/src/services/shellExecutionService.js.map +1 -0
- package/dist/src/services/shellExecutionService.test.d.ts +6 -0
- package/dist/src/services/shellExecutionService.test.js +655 -0
- package/dist/src/services/shellExecutionService.test.js.map +1 -0
- package/dist/src/subagents/builtin-agents.d.ts +35 -0
- package/dist/src/subagents/builtin-agents.js +85 -0
- package/dist/src/subagents/builtin-agents.js.map +1 -0
- package/dist/src/subagents/builtin-agents.test.d.ts +6 -0
- package/dist/src/subagents/builtin-agents.test.js +78 -0
- package/dist/src/subagents/builtin-agents.test.js.map +1 -0
- package/dist/src/subagents/index.d.ts +29 -0
- package/dist/src/subagents/index.js +15 -0
- package/dist/src/subagents/index.js.map +1 -0
- package/dist/src/subagents/subagent-events.d.ts +95 -0
- package/dist/src/subagents/subagent-events.js +31 -0
- package/dist/src/subagents/subagent-events.js.map +1 -0
- package/dist/src/subagents/subagent-hooks.d.ts +29 -0
- package/dist/src/subagents/subagent-hooks.js +7 -0
- package/dist/src/subagents/subagent-hooks.js.map +1 -0
- package/dist/src/subagents/subagent-manager.d.ts +170 -0
- package/dist/src/subagents/subagent-manager.js +596 -0
- package/dist/src/subagents/subagent-manager.js.map +1 -0
- package/dist/src/subagents/subagent-manager.test.d.ts +6 -0
- package/dist/src/subagents/subagent-manager.test.js +822 -0
- package/dist/src/subagents/subagent-manager.test.js.map +1 -0
- package/dist/src/subagents/subagent-statistics.d.ts +46 -0
- package/dist/src/subagents/subagent-statistics.js +193 -0
- package/dist/src/subagents/subagent-statistics.js.map +1 -0
- package/dist/src/subagents/subagent-statistics.test.d.ts +6 -0
- package/dist/src/subagents/subagent-statistics.test.js +231 -0
- package/dist/src/subagents/subagent-statistics.test.js.map +1 -0
- package/dist/src/subagents/subagent.d.ts +162 -0
- package/dist/src/subagents/subagent.js +677 -0
- package/dist/src/subagents/subagent.js.map +1 -0
- package/dist/src/subagents/subagent.test.d.ts +6 -0
- package/dist/src/subagents/subagent.test.js +477 -0
- package/dist/src/subagents/subagent.test.js.map +1 -0
- package/dist/src/subagents/types.d.ts +218 -0
- package/dist/src/subagents/types.js +58 -0
- package/dist/src/subagents/types.js.map +1 -0
- package/dist/src/subagents/types.test.d.ts +6 -0
- package/dist/src/subagents/types.test.js +31 -0
- package/dist/src/subagents/types.test.js.map +1 -0
- package/dist/src/subagents/validation.d.ts +63 -0
- package/dist/src/subagents/validation.js +293 -0
- package/dist/src/subagents/validation.js.map +1 -0
- package/dist/src/subagents/validation.test.d.ts +6 -0
- package/dist/src/subagents/validation.test.js +330 -0
- package/dist/src/subagents/validation.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +137 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +915 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +18 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +594 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +115 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +291 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -0
- package/dist/src/telemetry/config.d.ts +31 -0
- package/dist/src/telemetry/config.js +76 -0
- package/dist/src/telemetry/config.js.map +1 -0
- package/dist/src/telemetry/config.test.d.ts +6 -0
- package/dist/src/telemetry/config.test.js +124 -0
- package/dist/src/telemetry/config.test.js.map +1 -0
- package/dist/src/telemetry/constants.d.ts +36 -0
- package/dist/src/telemetry/constants.js +38 -0
- package/dist/src/telemetry/constants.js.map +1 -0
- package/dist/src/telemetry/file-exporters.d.ts +29 -0
- package/dist/src/telemetry/file-exporters.js +62 -0
- package/dist/src/telemetry/file-exporters.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +25 -0
- package/dist/src/telemetry/index.js +31 -0
- package/dist/src/telemetry/index.js.map +1 -0
- package/dist/src/telemetry/integration.test.circular.d.ts +6 -0
- package/dist/src/telemetry/integration.test.circular.js +95 -0
- package/dist/src/telemetry/integration.test.circular.js.map +1 -0
- package/dist/src/telemetry/loggers.d.ts +37 -0
- package/dist/src/telemetry/loggers.js +651 -0
- package/dist/src/telemetry/loggers.js.map +1 -0
- package/dist/src/telemetry/loggers.test.circular.d.ts +6 -0
- package/dist/src/telemetry/loggers.test.circular.js +107 -0
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -0
- package/dist/src/telemetry/loggers.test.d.ts +6 -0
- package/dist/src/telemetry/loggers.test.js +978 -0
- package/dist/src/telemetry/loggers.test.js.map +1 -0
- package/dist/src/telemetry/metrics.d.ts +320 -0
- package/dist/src/telemetry/metrics.js +532 -0
- package/dist/src/telemetry/metrics.js.map +1 -0
- package/dist/src/telemetry/metrics.test.d.ts +6 -0
- package/dist/src/telemetry/metrics.test.js +744 -0
- package/dist/src/telemetry/metrics.test.js.map +1 -0
- package/dist/src/telemetry/qwen-logger/event-types.d.ts +88 -0
- package/dist/src/telemetry/qwen-logger/event-types.js +2 -0
- package/dist/src/telemetry/qwen-logger/event-types.js.map +1 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.d.ts +91 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.js +685 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.js.map +1 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.test.d.ts +6 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.test.js +317 -0
- package/dist/src/telemetry/qwen-logger/qwen-logger.test.js.map +1 -0
- package/dist/src/telemetry/sdk.d.ts +9 -0
- package/dist/src/telemetry/sdk.js +164 -0
- package/dist/src/telemetry/sdk.js.map +1 -0
- package/dist/src/telemetry/sdk.test.d.ts +6 -0
- package/dist/src/telemetry/sdk.test.js +116 -0
- package/dist/src/telemetry/sdk.test.js.map +1 -0
- package/dist/src/telemetry/telemetry-utils.d.ts +6 -0
- package/dist/src/telemetry/telemetry-utils.js +14 -0
- package/dist/src/telemetry/telemetry-utils.js.map +1 -0
- package/dist/src/telemetry/telemetry-utils.test.d.ts +6 -0
- package/dist/src/telemetry/telemetry-utils.test.js +40 -0
- package/dist/src/telemetry/telemetry-utils.test.js.map +1 -0
- package/dist/src/telemetry/telemetry.test.d.ts +6 -0
- package/dist/src/telemetry/telemetry.test.js +50 -0
- package/dist/src/telemetry/telemetry.test.js.map +1 -0
- package/dist/src/telemetry/tool-call-decision.d.ts +13 -0
- package/dist/src/telemetry/tool-call-decision.js +29 -0
- package/dist/src/telemetry/tool-call-decision.js.map +1 -0
- package/dist/src/telemetry/types.d.ts +327 -0
- package/dist/src/telemetry/types.js +558 -0
- package/dist/src/telemetry/types.js.map +1 -0
- package/dist/src/telemetry/uiTelemetry.d.ts +75 -0
- package/dist/src/telemetry/uiTelemetry.js +152 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -0
- package/dist/src/telemetry/uiTelemetry.test.d.ts +6 -0
- package/dist/src/telemetry/uiTelemetry.test.js +625 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -0
- package/dist/src/test-utils/config.d.ts +17 -0
- package/dist/src/test-utils/config.js +32 -0
- package/dist/src/test-utils/config.js.map +1 -0
- package/dist/src/test-utils/index.d.ts +6 -0
- package/dist/src/test-utils/index.js +7 -0
- package/dist/src/test-utils/index.js.map +1 -0
- package/dist/src/test-utils/mock-tool.d.ts +66 -0
- package/dist/src/test-utils/mock-tool.js +121 -0
- package/dist/src/test-utils/mock-tool.js.map +1 -0
- package/dist/src/test-utils/mockWorkspaceContext.d.ts +13 -0
- package/dist/src/test-utils/mockWorkspaceContext.js +24 -0
- package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -0
- package/dist/src/tools/diffOptions.d.ts +9 -0
- package/dist/src/tools/diffOptions.js +46 -0
- package/dist/src/tools/diffOptions.js.map +1 -0
- package/dist/src/tools/diffOptions.test.d.ts +6 -0
- package/dist/src/tools/diffOptions.test.js +155 -0
- package/dist/src/tools/diffOptions.test.js.map +1 -0
- package/dist/src/tools/edit.d.ts +55 -0
- package/dist/src/tools/edit.js +421 -0
- package/dist/src/tools/edit.js.map +1 -0
- package/dist/src/tools/edit.test.d.ts +6 -0
- package/dist/src/tools/edit.test.js +684 -0
- package/dist/src/tools/edit.test.js.map +1 -0
- package/dist/src/tools/exitPlanMode.d.ts +28 -0
- package/dist/src/tools/exitPlanMode.js +132 -0
- package/dist/src/tools/exitPlanMode.js.map +1 -0
- package/dist/src/tools/exitPlanMode.test.d.ts +6 -0
- package/dist/src/tools/exitPlanMode.test.js +178 -0
- package/dist/src/tools/exitPlanMode.test.js.map +1 -0
- package/dist/src/tools/glob.d.ts +44 -0
- package/dist/src/tools/glob.js +190 -0
- package/dist/src/tools/glob.js.map +1 -0
- package/dist/src/tools/glob.test.d.ts +6 -0
- package/dist/src/tools/glob.test.js +442 -0
- package/dist/src/tools/glob.test.js.map +1 -0
- package/dist/src/tools/grep.d.ts +44 -0
- package/dist/src/tools/grep.js +425 -0
- package/dist/src/tools/grep.js.map +1 -0
- package/dist/src/tools/grep.test.d.ts +6 -0
- package/dist/src/tools/grep.test.js +360 -0
- package/dist/src/tools/grep.test.js.map +1 -0
- package/dist/src/tools/ls.d.ts +68 -0
- package/dist/src/tools/ls.js +215 -0
- package/dist/src/tools/ls.js.map +1 -0
- package/dist/src/tools/ls.test.d.ts +6 -0
- package/dist/src/tools/ls.test.js +249 -0
- package/dist/src/tools/ls.test.js.map +1 -0
- package/dist/src/tools/mcp-client-manager.d.ts +40 -0
- package/dist/src/tools/mcp-client-manager.js +83 -0
- package/dist/src/tools/mcp-client-manager.js.map +1 -0
- package/dist/src/tools/mcp-client-manager.test.d.ts +6 -0
- package/dist/src/tools/mcp-client-manager.test.js +58 -0
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -0
- package/dist/src/tools/mcp-client.d.ts +189 -0
- package/dist/src/tools/mcp-client.js +957 -0
- package/dist/src/tools/mcp-client.js.map +1 -0
- package/dist/src/tools/mcp-client.test.d.ts +6 -0
- package/dist/src/tools/mcp-client.test.js +309 -0
- package/dist/src/tools/mcp-client.test.js.map +1 -0
- package/dist/src/tools/mcp-tool.d.ts +24 -0
- package/dist/src/tools/mcp-tool.js +268 -0
- package/dist/src/tools/mcp-tool.js.map +1 -0
- package/dist/src/tools/mcp-tool.test.d.ts +6 -0
- package/dist/src/tools/mcp-tool.test.js +714 -0
- package/dist/src/tools/mcp-tool.test.js.map +1 -0
- package/dist/src/tools/memoryTool.d.ts +41 -0
- package/dist/src/tools/memoryTool.js +396 -0
- package/dist/src/tools/memoryTool.js.map +1 -0
- package/dist/src/tools/memoryTool.test.d.ts +6 -0
- package/dist/src/tools/memoryTool.test.js +419 -0
- package/dist/src/tools/memoryTool.test.js.map +1 -0
- package/dist/src/tools/modifiable-tool.d.ts +32 -0
- package/dist/src/tools/modifiable-tool.js +88 -0
- package/dist/src/tools/modifiable-tool.js.map +1 -0
- package/dist/src/tools/modifiable-tool.test.d.ts +6 -0
- package/dist/src/tools/modifiable-tool.test.js +193 -0
- package/dist/src/tools/modifiable-tool.test.js.map +1 -0
- package/dist/src/tools/read-file.d.ts +35 -0
- package/dist/src/tools/read-file.js +124 -0
- package/dist/src/tools/read-file.js.map +1 -0
- package/dist/src/tools/read-file.test.d.ts +6 -0
- package/dist/src/tools/read-file.test.js +339 -0
- package/dist/src/tools/read-file.test.js.map +1 -0
- package/dist/src/tools/read-many-files.d.ts +60 -0
- package/dist/src/tools/read-many-files.js +391 -0
- package/dist/src/tools/read-many-files.js.map +1 -0
- package/dist/src/tools/read-many-files.test.d.ts +6 -0
- package/dist/src/tools/read-many-files.test.js +566 -0
- package/dist/src/tools/read-many-files.test.js.map +1 -0
- package/dist/src/tools/ripGrep.d.ts +44 -0
- package/dist/src/tools/ripGrep.js +233 -0
- package/dist/src/tools/ripGrep.js.map +1 -0
- package/dist/src/tools/ripGrep.test.d.ts +6 -0
- package/dist/src/tools/ripGrep.test.js +529 -0
- package/dist/src/tools/ripGrep.test.js.map +1 -0
- package/dist/src/tools/shell.d.ts +33 -0
- package/dist/src/tools/shell.js +388 -0
- package/dist/src/tools/shell.js.map +1 -0
- package/dist/src/tools/shell.test.d.ts +6 -0
- package/dist/src/tools/shell.test.js +566 -0
- package/dist/src/tools/shell.test.js.map +1 -0
- package/dist/src/tools/smart-edit.d.ts +91 -0
- package/dist/src/tools/smart-edit.js +703 -0
- package/dist/src/tools/smart-edit.js.map +1 -0
- package/dist/src/tools/smart-edit.test.d.ts +6 -0
- package/dist/src/tools/smart-edit.test.js +542 -0
- package/dist/src/tools/smart-edit.test.js.map +1 -0
- package/dist/src/tools/task.d.ts +59 -0
- package/dist/src/tools/task.js +412 -0
- package/dist/src/tools/task.js.map +1 -0
- package/dist/src/tools/task.test.d.ts +6 -0
- package/dist/src/tools/task.test.js +369 -0
- package/dist/src/tools/task.test.js.map +1 -0
- package/dist/src/tools/todoWrite.d.ts +42 -0
- package/dist/src/tools/todoWrite.js +407 -0
- package/dist/src/tools/todoWrite.js.map +1 -0
- package/dist/src/tools/todoWrite.test.d.ts +6 -0
- package/dist/src/tools/todoWrite.test.js +234 -0
- package/dist/src/tools/todoWrite.test.js.map +1 -0
- package/dist/src/tools/tool-error.d.ts +45 -0
- package/dist/src/tools/tool-error.js +61 -0
- package/dist/src/tools/tool-error.js.map +1 -0
- package/dist/src/tools/tool-names.d.ts +56 -0
- package/dist/src/tools/tool-names.js +62 -0
- package/dist/src/tools/tool-names.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +87 -0
- package/dist/src/tools/tool-registry.js +370 -0
- package/dist/src/tools/tool-registry.js.map +1 -0
- package/dist/src/tools/tool-registry.test.d.ts +6 -0
- package/dist/src/tools/tool-registry.test.js +332 -0
- package/dist/src/tools/tool-registry.test.js.map +1 -0
- package/dist/src/tools/tools.d.ts +327 -0
- package/dist/src/tools/tools.js +258 -0
- package/dist/src/tools/tools.js.map +1 -0
- package/dist/src/tools/tools.test.d.ts +6 -0
- package/dist/src/tools/tools.test.js +205 -0
- package/dist/src/tools/tools.test.js.map +1 -0
- package/dist/src/tools/web-fetch.d.ts +31 -0
- package/dist/src/tools/web-fetch.js +163 -0
- package/dist/src/tools/web-fetch.js.map +1 -0
- package/dist/src/tools/web-fetch.test.d.ts +6 -0
- package/dist/src/tools/web-fetch.test.js +133 -0
- package/dist/src/tools/web-fetch.test.js.map +1 -0
- package/dist/src/tools/web-search/base-provider.d.ts +31 -0
- package/dist/src/tools/web-search/base-provider.js +34 -0
- package/dist/src/tools/web-search/base-provider.js.map +1 -0
- package/dist/src/tools/web-search/index.d.ts +24 -0
- package/dist/src/tools/web-search/index.js +245 -0
- package/dist/src/tools/web-search/index.js.map +1 -0
- package/dist/src/tools/web-search/index.test.d.ts +6 -0
- package/dist/src/tools/web-search/index.test.js +237 -0
- package/dist/src/tools/web-search/index.test.js.map +1 -0
- package/dist/src/tools/web-search/providers/dashscope-provider.d.ts +23 -0
- package/dist/src/tools/web-search/providers/dashscope-provider.js +120 -0
- package/dist/src/tools/web-search/providers/dashscope-provider.js.map +1 -0
- package/dist/src/tools/web-search/providers/google-provider.d.ts +17 -0
- package/dist/src/tools/web-search/providers/google-provider.js +55 -0
- package/dist/src/tools/web-search/providers/google-provider.js.map +1 -0
- package/dist/src/tools/web-search/providers/tavily-provider.d.ts +17 -0
- package/dist/src/tools/web-search/providers/tavily-provider.js +54 -0
- package/dist/src/tools/web-search/providers/tavily-provider.js.map +1 -0
- package/dist/src/tools/web-search/types.d.ts +138 -0
- package/dist/src/tools/web-search/types.js +7 -0
- package/dist/src/tools/web-search/types.js.map +1 -0
- package/dist/src/tools/web-search/utils.d.ts +28 -0
- package/dist/src/tools/web-search/utils.js +35 -0
- package/dist/src/tools/web-search/utils.js.map +1 -0
- package/dist/src/tools/write-file.d.ts +52 -0
- package/dist/src/tools/write-file.js +293 -0
- package/dist/src/tools/write-file.js.map +1 -0
- package/dist/src/tools/write-file.test.d.ts +6 -0
- package/dist/src/tools/write-file.test.js +516 -0
- package/dist/src/tools/write-file.test.js.map +1 -0
- package/dist/src/utils/LruCache.d.ts +13 -0
- package/dist/src/utils/LruCache.js +38 -0
- package/dist/src/utils/LruCache.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.d.ts +24 -0
- package/dist/src/utils/bfsFileSearch.js +95 -0
- package/dist/src/utils/bfsFileSearch.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.test.d.ts +6 -0
- package/dist/src/utils/bfsFileSearch.test.js +163 -0
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -0
- package/dist/src/utils/browser.d.ts +13 -0
- package/dist/src/utils/browser.js +50 -0
- package/dist/src/utils/browser.js.map +1 -0
- package/dist/src/utils/editHelper.d.ts +53 -0
- package/dist/src/utils/editHelper.js +359 -0
- package/dist/src/utils/editHelper.js.map +1 -0
- package/dist/src/utils/editHelper.test.d.ts +6 -0
- package/dist/src/utils/editHelper.test.js +93 -0
- package/dist/src/utils/editHelper.test.js.map +1 -0
- package/dist/src/utils/editor.d.ts +28 -0
- package/dist/src/utils/editor.js +177 -0
- package/dist/src/utils/editor.js.map +1 -0
- package/dist/src/utils/editor.test.d.ts +6 -0
- package/dist/src/utils/editor.test.js +437 -0
- package/dist/src/utils/editor.test.js.map +1 -0
- package/dist/src/utils/environmentContext.d.ts +22 -0
- package/dist/src/utils/environmentContext.js +108 -0
- package/dist/src/utils/environmentContext.js.map +1 -0
- package/dist/src/utils/environmentContext.test.d.ts +6 -0
- package/dist/src/utils/environmentContext.test.js +219 -0
- package/dist/src/utils/environmentContext.test.js.map +1 -0
- package/dist/src/utils/errorParsing.d.ts +8 -0
- package/dist/src/utils/errorParsing.js +93 -0
- package/dist/src/utils/errorParsing.js.map +1 -0
- package/dist/src/utils/errorParsing.test.d.ts +6 -0
- package/dist/src/utils/errorParsing.test.js +172 -0
- package/dist/src/utils/errorParsing.test.js.map +1 -0
- package/dist/src/utils/errorReporting.d.ts +14 -0
- package/dist/src/utils/errorReporting.js +88 -0
- package/dist/src/utils/errorReporting.js.map +1 -0
- package/dist/src/utils/errorReporting.test.d.ts +6 -0
- package/dist/src/utils/errorReporting.test.js +130 -0
- package/dist/src/utils/errorReporting.test.js.map +1 -0
- package/dist/src/utils/errors.d.ts +39 -0
- package/dist/src/utils/errors.js +96 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/fetch.d.ts +11 -0
- package/dist/src/utils/fetch.js +51 -0
- package/dist/src/utils/fetch.js.map +1 -0
- package/dist/src/utils/fileUtils.d.ts +69 -0
- package/dist/src/utils/fileUtils.js +426 -0
- package/dist/src/utils/fileUtils.js.map +1 -0
- package/dist/src/utils/fileUtils.test.d.ts +6 -0
- package/dist/src/utils/fileUtils.test.js +685 -0
- package/dist/src/utils/fileUtils.test.js.map +1 -0
- package/dist/src/utils/filesearch/crawlCache.d.ts +25 -0
- package/dist/src/utils/filesearch/crawlCache.js +57 -0
- package/dist/src/utils/filesearch/crawlCache.js.map +1 -0
- package/dist/src/utils/filesearch/crawlCache.test.d.ts +6 -0
- package/dist/src/utils/filesearch/crawlCache.test.js +103 -0
- package/dist/src/utils/filesearch/crawlCache.test.js.map +1 -0
- package/dist/src/utils/filesearch/crawler.d.ts +15 -0
- package/dist/src/utils/filesearch/crawler.js +50 -0
- package/dist/src/utils/filesearch/crawler.js.map +1 -0
- package/dist/src/utils/filesearch/crawler.test.d.ts +6 -0
- package/dist/src/utils/filesearch/crawler.test.js +468 -0
- package/dist/src/utils/filesearch/crawler.test.js.map +1 -0
- package/dist/src/utils/filesearch/fileSearch.d.ts +38 -0
- package/dist/src/utils/filesearch/fileSearch.js +191 -0
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -0
- package/dist/src/utils/filesearch/fileSearch.test.d.ts +6 -0
- package/dist/src/utils/filesearch/fileSearch.test.js +642 -0
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -0
- package/dist/src/utils/filesearch/ignore.d.ts +42 -0
- package/dist/src/utils/filesearch/ignore.js +106 -0
- package/dist/src/utils/filesearch/ignore.js.map +1 -0
- package/dist/src/utils/filesearch/ignore.test.d.ts +6 -0
- package/dist/src/utils/filesearch/ignore.test.js +144 -0
- package/dist/src/utils/filesearch/ignore.test.js.map +1 -0
- package/dist/src/utils/filesearch/result-cache.d.ts +33 -0
- package/dist/src/utils/filesearch/result-cache.js +59 -0
- package/dist/src/utils/filesearch/result-cache.js.map +1 -0
- package/dist/src/utils/filesearch/result-cache.test.d.ts +6 -0
- package/dist/src/utils/filesearch/result-cache.test.js +46 -0
- package/dist/src/utils/filesearch/result-cache.test.js.map +1 -0
- package/dist/src/utils/flashFallback.test.d.ts +6 -0
- package/dist/src/utils/flashFallback.test.js +122 -0
- package/dist/src/utils/flashFallback.test.js.map +1 -0
- package/dist/src/utils/formatters.d.ts +6 -0
- package/dist/src/utils/formatters.js +16 -0
- package/dist/src/utils/formatters.js.map +1 -0
- package/dist/src/utils/generateContentResponseUtilities.d.ts +13 -0
- package/dist/src/utils/generateContentResponseUtilities.js +80 -0
- package/dist/src/utils/generateContentResponseUtilities.js.map +1 -0
- package/dist/src/utils/generateContentResponseUtilities.test.d.ts +6 -0
- package/dist/src/utils/generateContentResponseUtilities.test.js +235 -0
- package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -0
- package/dist/src/utils/getFolderStructure.d.ts +31 -0
- package/dist/src/utils/getFolderStructure.js +246 -0
- package/dist/src/utils/getFolderStructure.js.map +1 -0
- package/dist/src/utils/getFolderStructure.test.d.ts +6 -0
- package/dist/src/utils/getFolderStructure.test.js +282 -0
- package/dist/src/utils/getFolderStructure.test.js.map +1 -0
- package/dist/src/utils/getPty.d.ts +19 -0
- package/dist/src/utils/getPty.js +23 -0
- package/dist/src/utils/getPty.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.d.ts +16 -0
- package/dist/src/utils/gitIgnoreParser.js +152 -0
- package/dist/src/utils/gitIgnoreParser.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/gitIgnoreParser.test.js +185 -0
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/gitUtils.d.ts +17 -0
- package/dist/src/utils/gitUtils.js +61 -0
- package/dist/src/utils/gitUtils.js.map +1 -0
- package/dist/src/utils/ignorePatterns.d.ts +103 -0
- package/dist/src/utils/ignorePatterns.js +220 -0
- package/dist/src/utils/ignorePatterns.js.map +1 -0
- package/dist/src/utils/ignorePatterns.test.d.ts +6 -0
- package/dist/src/utils/ignorePatterns.test.js +250 -0
- package/dist/src/utils/ignorePatterns.test.js.map +1 -0
- package/dist/src/utils/installationManager.d.ts +16 -0
- package/dist/src/utils/installationManager.js +50 -0
- package/dist/src/utils/installationManager.js.map +1 -0
- package/dist/src/utils/installationManager.test.d.ts +6 -0
- package/dist/src/utils/installationManager.test.js +83 -0
- package/dist/src/utils/installationManager.test.js.map +1 -0
- package/dist/src/utils/language-detection.d.ts +6 -0
- package/dist/src/utils/language-detection.js +101 -0
- package/dist/src/utils/language-detection.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.d.ts +26 -0
- package/dist/src/utils/llm-edit-fixer.js +131 -0
- package/dist/src/utils/llm-edit-fixer.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
- package/dist/src/utils/llm-edit-fixer.test.js +186 -0
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.d.ts +16 -0
- package/dist/src/utils/memoryDiscovery.js +272 -0
- package/dist/src/utils/memoryDiscovery.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.test.d.ts +6 -0
- package/dist/src/utils/memoryDiscovery.test.js +244 -0
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -0
- package/dist/src/utils/memoryImportProcessor.d.ts +42 -0
- package/dist/src/utils/memoryImportProcessor.js +284 -0
- package/dist/src/utils/memoryImportProcessor.js.map +1 -0
- package/dist/src/utils/memoryImportProcessor.test.d.ts +6 -0
- package/dist/src/utils/memoryImportProcessor.test.js +587 -0
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -0
- package/dist/src/utils/messageInspectors.d.ts +8 -0
- package/dist/src/utils/messageInspectors.js +16 -0
- package/dist/src/utils/messageInspectors.js.map +1 -0
- package/dist/src/utils/nextSpeakerChecker.d.ts +12 -0
- package/dist/src/utils/nextSpeakerChecker.js +97 -0
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -0
- package/dist/src/utils/nextSpeakerChecker.test.d.ts +6 -0
- package/dist/src/utils/nextSpeakerChecker.test.js +181 -0
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -0
- package/dist/src/utils/openaiLogger.d.ts +42 -0
- package/dist/src/utils/openaiLogger.js +138 -0
- package/dist/src/utils/openaiLogger.js.map +1 -0
- package/dist/src/utils/openaiLogger.test.d.ts +6 -0
- package/dist/src/utils/openaiLogger.test.js +304 -0
- package/dist/src/utils/openaiLogger.test.js.map +1 -0
- package/dist/src/utils/partUtils.d.ts +35 -0
- package/dist/src/utils/partUtils.js +133 -0
- package/dist/src/utils/partUtils.js.map +1 -0
- package/dist/src/utils/partUtils.test.d.ts +6 -0
- package/dist/src/utils/partUtils.test.js +241 -0
- package/dist/src/utils/partUtils.test.js.map +1 -0
- package/dist/src/utils/pathReader.d.ts +17 -0
- package/dist/src/utils/pathReader.js +92 -0
- package/dist/src/utils/pathReader.js.map +1 -0
- package/dist/src/utils/pathReader.test.d.ts +6 -0
- package/dist/src/utils/pathReader.test.js +365 -0
- package/dist/src/utils/pathReader.test.js.map +1 -0
- package/dist/src/utils/paths.d.ts +93 -0
- package/dist/src/utils/paths.js +229 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/paths.test.d.ts +6 -0
- package/dist/src/utils/paths.test.js +438 -0
- package/dist/src/utils/paths.test.js.map +1 -0
- package/dist/src/utils/projectSummary.d.ts +22 -0
- package/dist/src/utils/projectSummary.js +86 -0
- package/dist/src/utils/projectSummary.js.map +1 -0
- package/dist/src/utils/promptIdContext.d.ts +7 -0
- package/dist/src/utils/promptIdContext.js +8 -0
- package/dist/src/utils/promptIdContext.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.d.ts +20 -0
- package/dist/src/utils/quotaErrorDetection.js +114 -0
- package/dist/src/utils/quotaErrorDetection.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.test.d.ts +6 -0
- package/dist/src/utils/quotaErrorDetection.test.js +153 -0
- package/dist/src/utils/quotaErrorDetection.test.js.map +1 -0
- package/dist/src/utils/qwenIgnoreParser.d.ts +18 -0
- package/dist/src/utils/qwenIgnoreParser.js +69 -0
- package/dist/src/utils/qwenIgnoreParser.js.map +1 -0
- package/dist/src/utils/qwenIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/qwenIgnoreParser.test.js +50 -0
- package/dist/src/utils/qwenIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.d.ts +112 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.js +401 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.js.map +1 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.test.d.ts +6 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.test.js +114 -0
- package/dist/src/utils/request-tokenizer/imageTokenizer.test.js.map +1 -0
- package/dist/src/utils/request-tokenizer/index.d.ts +18 -0
- package/dist/src/utils/request-tokenizer/index.js +30 -0
- package/dist/src/utils/request-tokenizer/index.js.map +1 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.d.ts +56 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.js +263 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.js.map +1 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.test.d.ts +6 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.test.js +245 -0
- package/dist/src/utils/request-tokenizer/requestTokenizer.test.js.map +1 -0
- package/dist/src/utils/request-tokenizer/supportedImageFormats.d.ts +30 -0
- package/dist/src/utils/request-tokenizer/supportedImageFormats.js +41 -0
- package/dist/src/utils/request-tokenizer/supportedImageFormats.js.map +1 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.d.ts +29 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.js +88 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.js.map +1 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.test.d.ts +6 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.test.js +253 -0
- package/dist/src/utils/request-tokenizer/textTokenizer.test.js.map +1 -0
- package/dist/src/utils/request-tokenizer/types.d.ts +55 -0
- package/dist/src/utils/request-tokenizer/types.js +7 -0
- package/dist/src/utils/request-tokenizer/types.js.map +1 -0
- package/dist/src/utils/retry.d.ts +32 -0
- package/dist/src/utils/retry.js +303 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/dist/src/utils/retry.test.d.ts +6 -0
- package/dist/src/utils/retry.test.js +474 -0
- package/dist/src/utils/retry.test.js.map +1 -0
- package/dist/src/utils/ripgrepUtils.d.ts +61 -0
- package/dist/src/utils/ripgrepUtils.js +230 -0
- package/dist/src/utils/ripgrepUtils.js.map +1 -0
- package/dist/src/utils/ripgrepUtils.test.d.ts +6 -0
- package/dist/src/utils/ripgrepUtils.test.js +101 -0
- package/dist/src/utils/ripgrepUtils.test.js.map +1 -0
- package/dist/src/utils/safeJsonParse.d.ts +15 -0
- package/dist/src/utils/safeJsonParse.js +41 -0
- package/dist/src/utils/safeJsonParse.js.map +1 -0
- package/dist/src/utils/safeJsonParse.test.d.ts +6 -0
- package/dist/src/utils/safeJsonParse.test.js +112 -0
- package/dist/src/utils/safeJsonParse.test.js.map +1 -0
- package/dist/src/utils/safeJsonStringify.d.ts +13 -0
- package/dist/src/utils/safeJsonStringify.js +25 -0
- package/dist/src/utils/safeJsonStringify.js.map +1 -0
- package/dist/src/utils/safeJsonStringify.test.d.ts +6 -0
- package/dist/src/utils/safeJsonStringify.test.js +61 -0
- package/dist/src/utils/safeJsonStringify.test.js.map +1 -0
- package/dist/src/utils/schemaValidator.d.ts +15 -0
- package/dist/src/utils/schemaValidator.js +67 -0
- package/dist/src/utils/schemaValidator.js.map +1 -0
- package/dist/src/utils/schemaValidator.test.d.ts +6 -0
- package/dist/src/utils/schemaValidator.test.js +113 -0
- package/dist/src/utils/schemaValidator.test.js.map +1 -0
- package/dist/src/utils/secure-browser-launcher.d.ts +23 -0
- package/dist/src/utils/secure-browser-launcher.js +165 -0
- package/dist/src/utils/secure-browser-launcher.js.map +1 -0
- package/dist/src/utils/secure-browser-launcher.test.d.ts +6 -0
- package/dist/src/utils/secure-browser-launcher.test.js +149 -0
- package/dist/src/utils/secure-browser-launcher.test.js.map +1 -0
- package/dist/src/utils/session.d.ts +6 -0
- package/dist/src/utils/session.js +8 -0
- package/dist/src/utils/session.js.map +1 -0
- package/dist/src/utils/shell-utils.d.ts +152 -0
- package/dist/src/utils/shell-utils.js +467 -0
- package/dist/src/utils/shell-utils.js.map +1 -0
- package/dist/src/utils/shell-utils.test.d.ts +6 -0
- package/dist/src/utils/shell-utils.test.js +351 -0
- package/dist/src/utils/shell-utils.test.js.map +1 -0
- package/dist/src/utils/shellReadOnlyChecker.d.ts +6 -0
- package/dist/src/utils/shellReadOnlyChecker.js +247 -0
- package/dist/src/utils/shellReadOnlyChecker.js.map +1 -0
- package/dist/src/utils/shellReadOnlyChecker.test.d.ts +6 -0
- package/dist/src/utils/shellReadOnlyChecker.test.js +47 -0
- package/dist/src/utils/shellReadOnlyChecker.test.js.map +1 -0
- package/dist/src/utils/subagentGenerator.d.ts +20 -0
- package/dist/src/utils/subagentGenerator.js +120 -0
- package/dist/src/utils/subagentGenerator.js.map +1 -0
- package/dist/src/utils/subagentGenerator.test.d.ts +6 -0
- package/dist/src/utils/subagentGenerator.test.js +135 -0
- package/dist/src/utils/subagentGenerator.test.js.map +1 -0
- package/dist/src/utils/summarizer.d.ts +25 -0
- package/dist/src/utils/summarizer.js +51 -0
- package/dist/src/utils/summarizer.js.map +1 -0
- package/dist/src/utils/summarizer.test.d.ts +6 -0
- package/dist/src/utils/summarizer.test.js +131 -0
- package/dist/src/utils/summarizer.test.js.map +1 -0
- package/dist/src/utils/systemEncoding.d.ts +40 -0
- package/dist/src/utils/systemEncoding.js +149 -0
- package/dist/src/utils/systemEncoding.js.map +1 -0
- package/dist/src/utils/systemEncoding.test.d.ts +6 -0
- package/dist/src/utils/systemEncoding.test.js +368 -0
- package/dist/src/utils/systemEncoding.test.js.map +1 -0
- package/dist/src/utils/terminalSerializer.d.ts +25 -0
- package/dist/src/utils/terminalSerializer.js +432 -0
- package/dist/src/utils/terminalSerializer.js.map +1 -0
- package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
- package/dist/src/utils/terminalSerializer.test.js +176 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -0
- package/dist/src/utils/testUtils.d.ts +29 -0
- package/dist/src/utils/testUtils.js +70 -0
- package/dist/src/utils/testUtils.js.map +1 -0
- package/dist/src/utils/textUtils.d.ts +18 -0
- package/dist/src/utils/textUtils.js +42 -0
- package/dist/src/utils/textUtils.js.map +1 -0
- package/dist/src/utils/textUtils.test.d.ts +6 -0
- package/dist/src/utils/textUtils.test.js +59 -0
- package/dist/src/utils/textUtils.test.js.map +1 -0
- package/dist/src/utils/thoughtUtils.d.ts +21 -0
- package/dist/src/utils/thoughtUtils.js +39 -0
- package/dist/src/utils/thoughtUtils.js.map +1 -0
- package/dist/src/utils/thoughtUtils.test.d.ts +6 -0
- package/dist/src/utils/thoughtUtils.test.js +78 -0
- package/dist/src/utils/thoughtUtils.test.js.map +1 -0
- package/dist/src/utils/tool-utils.d.ts +22 -0
- package/dist/src/utils/tool-utils.js +121 -0
- package/dist/src/utils/tool-utils.js.map +1 -0
- package/dist/src/utils/tool-utils.test.d.ts +6 -0
- package/dist/src/utils/tool-utils.test.js +100 -0
- package/dist/src/utils/tool-utils.test.js.map +1 -0
- package/dist/src/utils/userAccountManager.d.ts +20 -0
- package/dist/src/utils/userAccountManager.js +114 -0
- package/dist/src/utils/userAccountManager.js.map +1 -0
- package/dist/src/utils/userAccountManager.test.d.ts +6 -0
- package/dist/src/utils/userAccountManager.test.js +223 -0
- package/dist/src/utils/userAccountManager.test.js.map +1 -0
- package/dist/src/utils/workspaceContext.d.ts +66 -0
- package/dist/src/utils/workspaceContext.js +171 -0
- package/dist/src/utils/workspaceContext.js.map +1 -0
- package/dist/src/utils/workspaceContext.test.d.ts +6 -0
- package/dist/src/utils/workspaceContext.test.js +318 -0
- package/dist/src/utils/workspaceContext.test.js.map +1 -0
- package/dist/src/utils/yaml-parser.d.ts +29 -0
- package/dist/src/utils/yaml-parser.js +172 -0
- package/dist/src/utils/yaml-parser.js.map +1 -0
- package/dist/src/utils/yaml-parser.test.d.ts +6 -0
- package/dist/src/utils/yaml-parser.test.js +170 -0
- package/dist/src/utils/yaml-parser.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +106 -0
- package/scripts/postinstall.js +100 -0
- package/vendor/ripgrep/COPYING +3 -0
- package/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/vendor/ripgrep/arm64-linux/rg +0 -0
- package/vendor/ripgrep/x64-darwin/rg +0 -0
- package/vendor/ripgrep/x64-linux/rg +0 -0
- package/vendor/ripgrep/x64-win32/rg.exe +0 -0
|
@@ -0,0 +1,1724 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import {} from 'child_process';
|
|
9
|
+
import { generateCodeChallenge, generateCodeVerifier, generatePKCEPair, isDeviceAuthorizationSuccess, isDeviceTokenPending, isDeviceTokenSuccess, isErrorResponse, QwenOAuth2Client, } from './qwenOAuth2.js';
|
|
10
|
+
import { SharedTokenManager, TokenManagerError, TokenError, } from './sharedTokenManager.js';
|
|
11
|
+
// Mock SharedTokenManager
|
|
12
|
+
vi.mock('./sharedTokenManager.js', () => ({
|
|
13
|
+
SharedTokenManager: class {
|
|
14
|
+
static instance = null;
|
|
15
|
+
static getInstance() {
|
|
16
|
+
if (!this.instance) {
|
|
17
|
+
this.instance = new this();
|
|
18
|
+
}
|
|
19
|
+
return this.instance;
|
|
20
|
+
}
|
|
21
|
+
async getValidCredentials(qwenClient) {
|
|
22
|
+
// Try to get credentials from the client first
|
|
23
|
+
const clientCredentials = qwenClient.getCredentials();
|
|
24
|
+
if (clientCredentials && clientCredentials.access_token) {
|
|
25
|
+
return clientCredentials;
|
|
26
|
+
}
|
|
27
|
+
// Fall back to default mock credentials if client has none
|
|
28
|
+
return {
|
|
29
|
+
access_token: 'new-access-token',
|
|
30
|
+
refresh_token: 'valid-refresh-token',
|
|
31
|
+
resource_url: undefined,
|
|
32
|
+
token_type: 'Bearer',
|
|
33
|
+
expiry_date: Date.now() + 3600000,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
getCurrentCredentials() {
|
|
37
|
+
// Return null to let the client manage its own credentials
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
clearCache() {
|
|
41
|
+
// Do nothing in mock
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
TokenManagerError: class extends Error {
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = 'TokenManagerError';
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
TokenError: {
|
|
51
|
+
REFRESH_FAILED: 'REFRESH_FAILED',
|
|
52
|
+
NO_REFRESH_TOKEN: 'NO_REFRESH_TOKEN',
|
|
53
|
+
LOCK_TIMEOUT: 'LOCK_TIMEOUT',
|
|
54
|
+
FILE_ACCESS_ERROR: 'FILE_ACCESS_ERROR',
|
|
55
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
// Mock qrcode-terminal
|
|
59
|
+
vi.mock('qrcode-terminal', () => ({
|
|
60
|
+
default: {
|
|
61
|
+
generate: vi.fn(),
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
// Mock open
|
|
65
|
+
vi.mock('open', () => ({
|
|
66
|
+
default: vi.fn(),
|
|
67
|
+
}));
|
|
68
|
+
// Mock process.stdout.write
|
|
69
|
+
vi.mock('process', () => ({
|
|
70
|
+
stdout: {
|
|
71
|
+
write: vi.fn(),
|
|
72
|
+
},
|
|
73
|
+
}));
|
|
74
|
+
// Mock file system operations
|
|
75
|
+
vi.mock('node:fs', () => ({
|
|
76
|
+
promises: {
|
|
77
|
+
readFile: vi.fn(),
|
|
78
|
+
writeFile: vi.fn(),
|
|
79
|
+
unlink: vi.fn(),
|
|
80
|
+
mkdir: vi.fn().mockResolvedValue(undefined),
|
|
81
|
+
},
|
|
82
|
+
}));
|
|
83
|
+
describe('PKCE Code Generation', () => {
|
|
84
|
+
describe('generateCodeVerifier', () => {
|
|
85
|
+
it('should generate a code verifier with correct length', () => {
|
|
86
|
+
const codeVerifier = generateCodeVerifier();
|
|
87
|
+
expect(codeVerifier).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
|
88
|
+
});
|
|
89
|
+
it('should generate different verifiers on subsequent calls', () => {
|
|
90
|
+
const verifier1 = generateCodeVerifier();
|
|
91
|
+
const verifier2 = generateCodeVerifier();
|
|
92
|
+
expect(verifier1).not.toBe(verifier2);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('generateCodeChallenge', () => {
|
|
96
|
+
it('should generate code challenge from verifier', () => {
|
|
97
|
+
const verifier = 'test-verifier-1234567890abcdefghijklmnopqrst';
|
|
98
|
+
const challenge = generateCodeChallenge(verifier);
|
|
99
|
+
// Should be base64url encoded
|
|
100
|
+
expect(challenge).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
101
|
+
expect(challenge).not.toBe(verifier);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('generatePKCEPair', () => {
|
|
105
|
+
it('should generate valid PKCE pair', () => {
|
|
106
|
+
const { code_verifier, code_challenge } = generatePKCEPair();
|
|
107
|
+
expect(code_verifier).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
|
108
|
+
expect(code_challenge).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
109
|
+
expect(code_verifier).not.toBe(code_challenge);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('Type Guards', () => {
|
|
114
|
+
describe('isDeviceAuthorizationSuccess', () => {
|
|
115
|
+
it('should return true for successful authorization response', () => {
|
|
116
|
+
const expectedBaseUrl = process.env['DEBUG']
|
|
117
|
+
? 'https://pre4-chat.qwen.ai'
|
|
118
|
+
: 'https://chat.qwen.ai';
|
|
119
|
+
const successResponse = {
|
|
120
|
+
device_code: 'test-device-code',
|
|
121
|
+
user_code: 'TEST123',
|
|
122
|
+
verification_uri: `${expectedBaseUrl}/device`,
|
|
123
|
+
verification_uri_complete: `${expectedBaseUrl}/device?code=TEST123`,
|
|
124
|
+
expires_in: 1800,
|
|
125
|
+
};
|
|
126
|
+
expect(isDeviceAuthorizationSuccess(successResponse)).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
it('should return false for error response', () => {
|
|
129
|
+
const errorResponse = {
|
|
130
|
+
error: 'INVALID_REQUEST',
|
|
131
|
+
error_description: 'The request parameters are invalid',
|
|
132
|
+
};
|
|
133
|
+
expect(isDeviceAuthorizationSuccess(errorResponse)).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('isDeviceTokenPending', () => {
|
|
137
|
+
it('should return true for pending response', () => {
|
|
138
|
+
const pendingResponse = {
|
|
139
|
+
status: 'pending',
|
|
140
|
+
};
|
|
141
|
+
expect(isDeviceTokenPending(pendingResponse)).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
it('should return false for success response', () => {
|
|
144
|
+
const successResponse = {
|
|
145
|
+
access_token: 'valid-access-token',
|
|
146
|
+
refresh_token: 'valid-refresh-token',
|
|
147
|
+
token_type: 'Bearer',
|
|
148
|
+
expires_in: 3600,
|
|
149
|
+
scope: 'openid profile email model.completion',
|
|
150
|
+
};
|
|
151
|
+
expect(isDeviceTokenPending(successResponse)).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
it('should return false for error response', () => {
|
|
154
|
+
const errorResponse = {
|
|
155
|
+
error: 'ACCESS_DENIED',
|
|
156
|
+
error_description: 'User denied the authorization request',
|
|
157
|
+
};
|
|
158
|
+
expect(isDeviceTokenPending(errorResponse)).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('isDeviceTokenSuccess', () => {
|
|
162
|
+
it('should return true for successful token response', () => {
|
|
163
|
+
const successResponse = {
|
|
164
|
+
access_token: 'valid-access-token',
|
|
165
|
+
refresh_token: 'valid-refresh-token',
|
|
166
|
+
token_type: 'Bearer',
|
|
167
|
+
expires_in: 3600,
|
|
168
|
+
scope: 'openid profile email model.completion',
|
|
169
|
+
};
|
|
170
|
+
expect(isDeviceTokenSuccess(successResponse)).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
it('should return false for pending response', () => {
|
|
173
|
+
const pendingResponse = {
|
|
174
|
+
status: 'pending',
|
|
175
|
+
};
|
|
176
|
+
expect(isDeviceTokenSuccess(pendingResponse)).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
it('should return false for error response', () => {
|
|
179
|
+
const errorResponse = {
|
|
180
|
+
error: 'ACCESS_DENIED',
|
|
181
|
+
error_description: 'User denied the authorization request',
|
|
182
|
+
};
|
|
183
|
+
expect(isDeviceTokenSuccess(errorResponse)).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
it('should return false for null access token', () => {
|
|
186
|
+
const nullTokenResponse = {
|
|
187
|
+
access_token: null,
|
|
188
|
+
token_type: 'Bearer',
|
|
189
|
+
expires_in: 3600,
|
|
190
|
+
};
|
|
191
|
+
expect(isDeviceTokenSuccess(nullTokenResponse)).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
it('should return false for empty access token', () => {
|
|
194
|
+
const emptyTokenResponse = {
|
|
195
|
+
access_token: '',
|
|
196
|
+
token_type: 'Bearer',
|
|
197
|
+
expires_in: 3600,
|
|
198
|
+
};
|
|
199
|
+
expect(isDeviceTokenSuccess(emptyTokenResponse)).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
describe('isErrorResponse', () => {
|
|
203
|
+
it('should return true for error responses', () => {
|
|
204
|
+
const errorResponse = {
|
|
205
|
+
error: 'INVALID_REQUEST',
|
|
206
|
+
error_description: 'The request parameters are invalid',
|
|
207
|
+
};
|
|
208
|
+
expect(isErrorResponse(errorResponse)).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
it('should return false for successful responses', () => {
|
|
211
|
+
const successResponse = {
|
|
212
|
+
device_code: 'test-device-code',
|
|
213
|
+
user_code: 'TEST123',
|
|
214
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
215
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
216
|
+
expires_in: 1800,
|
|
217
|
+
};
|
|
218
|
+
expect(isErrorResponse(successResponse)).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
describe('QwenOAuth2Client', () => {
|
|
223
|
+
let client;
|
|
224
|
+
let originalFetch;
|
|
225
|
+
beforeEach(() => {
|
|
226
|
+
// Create client instance
|
|
227
|
+
client = new QwenOAuth2Client();
|
|
228
|
+
// Mock fetch
|
|
229
|
+
originalFetch = global.fetch;
|
|
230
|
+
global.fetch = vi.fn();
|
|
231
|
+
});
|
|
232
|
+
afterEach(() => {
|
|
233
|
+
global.fetch = originalFetch;
|
|
234
|
+
vi.clearAllMocks();
|
|
235
|
+
});
|
|
236
|
+
describe('requestDeviceAuthorization', () => {
|
|
237
|
+
it('should successfully request device authorization', async () => {
|
|
238
|
+
const mockResponse = {
|
|
239
|
+
ok: true,
|
|
240
|
+
json: async () => ({
|
|
241
|
+
device_code: 'test-device-code',
|
|
242
|
+
user_code: 'TEST123',
|
|
243
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
244
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
245
|
+
expires_in: 1800,
|
|
246
|
+
}),
|
|
247
|
+
};
|
|
248
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
249
|
+
const result = await client.requestDeviceAuthorization({
|
|
250
|
+
scope: 'openid profile email model.completion',
|
|
251
|
+
code_challenge: 'test-challenge',
|
|
252
|
+
code_challenge_method: 'S256',
|
|
253
|
+
});
|
|
254
|
+
expect(result).toEqual({
|
|
255
|
+
device_code: 'test-device-code',
|
|
256
|
+
user_code: 'TEST123',
|
|
257
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
258
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
259
|
+
expires_in: 1800,
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
it('should handle error response', async () => {
|
|
263
|
+
const mockResponse = {
|
|
264
|
+
ok: true,
|
|
265
|
+
json: async () => ({
|
|
266
|
+
error: 'INVALID_REQUEST',
|
|
267
|
+
error_description: 'The request parameters are invalid',
|
|
268
|
+
}),
|
|
269
|
+
};
|
|
270
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
271
|
+
await expect(client.requestDeviceAuthorization({
|
|
272
|
+
scope: 'openid profile email model.completion',
|
|
273
|
+
code_challenge: 'test-challenge',
|
|
274
|
+
code_challenge_method: 'S256',
|
|
275
|
+
})).rejects.toThrow('Device authorization failed: INVALID_REQUEST - The request parameters are invalid');
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
describe('refreshAccessToken', () => {
|
|
279
|
+
beforeEach(() => {
|
|
280
|
+
// Set up client with credentials
|
|
281
|
+
client.setCredentials({
|
|
282
|
+
access_token: 'old-token',
|
|
283
|
+
refresh_token: 'test-refresh-token',
|
|
284
|
+
token_type: 'Bearer',
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
it('should successfully refresh access token', async () => {
|
|
288
|
+
const mockResponse = {
|
|
289
|
+
ok: true,
|
|
290
|
+
json: async () => ({
|
|
291
|
+
access_token: 'new-access-token',
|
|
292
|
+
token_type: 'Bearer',
|
|
293
|
+
expires_in: 3600,
|
|
294
|
+
resource_url: 'https://new-endpoint.com',
|
|
295
|
+
}),
|
|
296
|
+
};
|
|
297
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
298
|
+
const result = await client.refreshAccessToken();
|
|
299
|
+
expect(result).toEqual({
|
|
300
|
+
access_token: 'new-access-token',
|
|
301
|
+
token_type: 'Bearer',
|
|
302
|
+
expires_in: 3600,
|
|
303
|
+
resource_url: 'https://new-endpoint.com',
|
|
304
|
+
});
|
|
305
|
+
// Verify credentials were updated
|
|
306
|
+
const credentials = client.getCredentials();
|
|
307
|
+
expect(credentials.access_token).toBe('new-access-token');
|
|
308
|
+
});
|
|
309
|
+
it('should handle refresh error', async () => {
|
|
310
|
+
const mockResponse = {
|
|
311
|
+
ok: true,
|
|
312
|
+
json: async () => ({
|
|
313
|
+
error: 'INVALID_GRANT',
|
|
314
|
+
error_description: 'The refresh token is invalid',
|
|
315
|
+
}),
|
|
316
|
+
};
|
|
317
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
318
|
+
await expect(client.refreshAccessToken()).rejects.toThrow('Token refresh failed: INVALID_GRANT - The refresh token is invalid');
|
|
319
|
+
});
|
|
320
|
+
it('should successfully refresh access token and update credentials', async () => {
|
|
321
|
+
// Clear any previous calls
|
|
322
|
+
vi.clearAllMocks();
|
|
323
|
+
const mockResponse = {
|
|
324
|
+
ok: true,
|
|
325
|
+
json: async () => ({
|
|
326
|
+
access_token: 'new-access-token',
|
|
327
|
+
token_type: 'Bearer',
|
|
328
|
+
expires_in: 3600,
|
|
329
|
+
resource_url: 'https://new-endpoint.com',
|
|
330
|
+
}),
|
|
331
|
+
};
|
|
332
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
333
|
+
const result = await client.refreshAccessToken();
|
|
334
|
+
// Verify the response
|
|
335
|
+
expect(result).toMatchObject({
|
|
336
|
+
access_token: 'new-access-token',
|
|
337
|
+
token_type: 'Bearer',
|
|
338
|
+
expires_in: 3600,
|
|
339
|
+
resource_url: 'https://new-endpoint.com',
|
|
340
|
+
});
|
|
341
|
+
// Verify credentials were updated
|
|
342
|
+
const credentials = client.getCredentials();
|
|
343
|
+
expect(credentials).toMatchObject({
|
|
344
|
+
access_token: 'new-access-token',
|
|
345
|
+
token_type: 'Bearer',
|
|
346
|
+
refresh_token: 'test-refresh-token', // Should preserve existing refresh token
|
|
347
|
+
resource_url: 'https://new-endpoint.com',
|
|
348
|
+
});
|
|
349
|
+
expect(credentials.expiry_date).toBeDefined();
|
|
350
|
+
});
|
|
351
|
+
it('should use new refresh token if provided in response', async () => {
|
|
352
|
+
// Clear any previous calls
|
|
353
|
+
vi.clearAllMocks();
|
|
354
|
+
const mockResponse = {
|
|
355
|
+
ok: true,
|
|
356
|
+
json: async () => ({
|
|
357
|
+
access_token: 'new-access-token',
|
|
358
|
+
token_type: 'Bearer',
|
|
359
|
+
expires_in: 3600,
|
|
360
|
+
refresh_token: 'new-refresh-token', // New refresh token provided
|
|
361
|
+
resource_url: 'https://new-endpoint.com',
|
|
362
|
+
}),
|
|
363
|
+
};
|
|
364
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
365
|
+
await client.refreshAccessToken();
|
|
366
|
+
// Verify the credentials contain the new refresh token
|
|
367
|
+
const credentials = client.getCredentials();
|
|
368
|
+
expect(credentials.refresh_token).toBe('new-refresh-token');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
describe('getAccessToken', () => {
|
|
372
|
+
it('should return access token if valid and not expired', async () => {
|
|
373
|
+
// Set valid credentials
|
|
374
|
+
client.setCredentials({
|
|
375
|
+
access_token: 'valid-token',
|
|
376
|
+
expiry_date: Date.now() + 60 * 60 * 1000, // 1 hour from now
|
|
377
|
+
});
|
|
378
|
+
const result = await client.getAccessToken();
|
|
379
|
+
expect(result.token).toBe('valid-token');
|
|
380
|
+
});
|
|
381
|
+
it('should refresh token if access token is expired', async () => {
|
|
382
|
+
// Set expired credentials with refresh token
|
|
383
|
+
client.setCredentials({
|
|
384
|
+
access_token: 'expired-token',
|
|
385
|
+
refresh_token: 'valid-refresh-token',
|
|
386
|
+
expiry_date: Date.now() - 1000, // 1 second ago
|
|
387
|
+
});
|
|
388
|
+
// Override the client's SharedTokenManager instance directly
|
|
389
|
+
client.sharedManager = {
|
|
390
|
+
getValidCredentials: vi.fn().mockResolvedValue({
|
|
391
|
+
access_token: 'new-access-token',
|
|
392
|
+
refresh_token: 'valid-refresh-token',
|
|
393
|
+
token_type: 'Bearer',
|
|
394
|
+
expiry_date: Date.now() + 3600000,
|
|
395
|
+
}),
|
|
396
|
+
};
|
|
397
|
+
const result = await client.getAccessToken();
|
|
398
|
+
expect(result.token).toBe('new-access-token');
|
|
399
|
+
});
|
|
400
|
+
it('should return undefined if no access token and no refresh token', async () => {
|
|
401
|
+
client.setCredentials({});
|
|
402
|
+
// Override the client's SharedTokenManager instance directly
|
|
403
|
+
client.sharedManager = {
|
|
404
|
+
getValidCredentials: vi
|
|
405
|
+
.fn()
|
|
406
|
+
.mockRejectedValue(new Error('No credentials available')),
|
|
407
|
+
};
|
|
408
|
+
const result = await client.getAccessToken();
|
|
409
|
+
expect(result.token).toBeUndefined();
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
describe('pollDeviceToken', () => {
|
|
413
|
+
it('should successfully poll for device token', async () => {
|
|
414
|
+
const mockResponse = {
|
|
415
|
+
ok: true,
|
|
416
|
+
json: async () => ({
|
|
417
|
+
access_token: 'new-access-token',
|
|
418
|
+
refresh_token: 'new-refresh-token',
|
|
419
|
+
token_type: 'Bearer',
|
|
420
|
+
expires_in: 3600,
|
|
421
|
+
scope: 'openid profile email model.completion',
|
|
422
|
+
}),
|
|
423
|
+
};
|
|
424
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
425
|
+
const result = await client.pollDeviceToken({
|
|
426
|
+
device_code: 'test-device-code',
|
|
427
|
+
code_verifier: 'test-code-verifier',
|
|
428
|
+
});
|
|
429
|
+
expect(result).toEqual({
|
|
430
|
+
access_token: 'new-access-token',
|
|
431
|
+
refresh_token: 'new-refresh-token',
|
|
432
|
+
token_type: 'Bearer',
|
|
433
|
+
expires_in: 3600,
|
|
434
|
+
scope: 'openid profile email model.completion',
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
it('should return pending status when authorization is pending', async () => {
|
|
438
|
+
const mockResponse = {
|
|
439
|
+
ok: true,
|
|
440
|
+
json: async () => ({
|
|
441
|
+
status: 'pending',
|
|
442
|
+
}),
|
|
443
|
+
};
|
|
444
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
445
|
+
const result = await client.pollDeviceToken({
|
|
446
|
+
device_code: 'test-device-code',
|
|
447
|
+
code_verifier: 'test-code-verifier',
|
|
448
|
+
});
|
|
449
|
+
expect(result).toEqual({
|
|
450
|
+
status: 'pending',
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
it('should handle HTTP error responses', async () => {
|
|
454
|
+
const mockResponse = {
|
|
455
|
+
ok: false,
|
|
456
|
+
status: 400,
|
|
457
|
+
statusText: 'Bad Request',
|
|
458
|
+
text: async () => 'Invalid device code',
|
|
459
|
+
};
|
|
460
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
461
|
+
await expect(client.pollDeviceToken({
|
|
462
|
+
device_code: 'invalid-device-code',
|
|
463
|
+
code_verifier: 'test-code-verifier',
|
|
464
|
+
})).rejects.toThrow('Device token poll failed: 400 Bad Request');
|
|
465
|
+
});
|
|
466
|
+
it('should include status code in error for better handling', async () => {
|
|
467
|
+
const mockResponse = {
|
|
468
|
+
ok: false,
|
|
469
|
+
status: 429,
|
|
470
|
+
statusText: 'Too Many Requests',
|
|
471
|
+
text: async () => 'Rate limited',
|
|
472
|
+
};
|
|
473
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
474
|
+
try {
|
|
475
|
+
await client.pollDeviceToken({
|
|
476
|
+
device_code: 'test-device-code',
|
|
477
|
+
code_verifier: 'test-code-verifier',
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
expect(error.status).toBe(429);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
it('should handle authorization_pending with HTTP 400 according to RFC 8628', async () => {
|
|
485
|
+
const errorData = {
|
|
486
|
+
error: 'authorization_pending',
|
|
487
|
+
error_description: 'The authorization request is still pending',
|
|
488
|
+
};
|
|
489
|
+
const mockResponse = {
|
|
490
|
+
ok: false,
|
|
491
|
+
status: 400,
|
|
492
|
+
statusText: 'Bad Request',
|
|
493
|
+
text: async () => JSON.stringify(errorData),
|
|
494
|
+
json: async () => errorData,
|
|
495
|
+
};
|
|
496
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
497
|
+
const result = await client.pollDeviceToken({
|
|
498
|
+
device_code: 'test-device-code',
|
|
499
|
+
code_verifier: 'test-code-verifier',
|
|
500
|
+
});
|
|
501
|
+
expect(result).toEqual({
|
|
502
|
+
status: 'pending',
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
it('should handle slow_down with HTTP 429 according to RFC 8628', async () => {
|
|
506
|
+
const errorData = {
|
|
507
|
+
error: 'slow_down',
|
|
508
|
+
error_description: 'The client is polling too frequently',
|
|
509
|
+
};
|
|
510
|
+
const mockResponse = {
|
|
511
|
+
ok: false,
|
|
512
|
+
status: 429,
|
|
513
|
+
statusText: 'Too Many Requests',
|
|
514
|
+
text: async () => JSON.stringify(errorData),
|
|
515
|
+
json: async () => errorData,
|
|
516
|
+
};
|
|
517
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
518
|
+
const result = await client.pollDeviceToken({
|
|
519
|
+
device_code: 'test-device-code',
|
|
520
|
+
code_verifier: 'test-code-verifier',
|
|
521
|
+
});
|
|
522
|
+
expect(result).toEqual({
|
|
523
|
+
status: 'pending',
|
|
524
|
+
slowDown: true,
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
describe('refreshAccessToken error handling', () => {
|
|
529
|
+
beforeEach(() => {
|
|
530
|
+
client.setCredentials({
|
|
531
|
+
access_token: 'old-token',
|
|
532
|
+
refresh_token: 'test-refresh-token',
|
|
533
|
+
token_type: 'Bearer',
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
it('should throw error if no refresh token available', async () => {
|
|
537
|
+
client.setCredentials({ access_token: 'token' });
|
|
538
|
+
await expect(client.refreshAccessToken()).rejects.toThrow('No refresh token available');
|
|
539
|
+
});
|
|
540
|
+
it('should handle 400 status as expired refresh token', async () => {
|
|
541
|
+
const mockResponse = {
|
|
542
|
+
ok: false,
|
|
543
|
+
status: 400,
|
|
544
|
+
statusText: 'Bad Request',
|
|
545
|
+
text: async () => 'Refresh token expired',
|
|
546
|
+
};
|
|
547
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
548
|
+
await expect(client.refreshAccessToken()).rejects.toThrow("Refresh token expired or invalid. Please use '/auth' to re-authenticate.");
|
|
549
|
+
});
|
|
550
|
+
it('should handle other HTTP error statuses', async () => {
|
|
551
|
+
const mockResponse = {
|
|
552
|
+
ok: false,
|
|
553
|
+
status: 500,
|
|
554
|
+
statusText: 'Internal Server Error',
|
|
555
|
+
text: async () => 'Server error',
|
|
556
|
+
};
|
|
557
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
558
|
+
await expect(client.refreshAccessToken()).rejects.toThrow('Token refresh failed: 500 Internal Server Error');
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
describe('credentials management', () => {
|
|
562
|
+
it('should set and get credentials correctly', () => {
|
|
563
|
+
const credentials = {
|
|
564
|
+
access_token: 'test-token',
|
|
565
|
+
refresh_token: 'test-refresh',
|
|
566
|
+
token_type: 'Bearer',
|
|
567
|
+
expiry_date: Date.now() + 3600000,
|
|
568
|
+
};
|
|
569
|
+
client.setCredentials(credentials);
|
|
570
|
+
expect(client.getCredentials()).toEqual(credentials);
|
|
571
|
+
});
|
|
572
|
+
it('should handle empty credentials', () => {
|
|
573
|
+
client.setCredentials({});
|
|
574
|
+
expect(client.getCredentials()).toEqual({});
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
describe('getQwenOAuthClient', () => {
|
|
579
|
+
let mockConfig;
|
|
580
|
+
let originalFetch;
|
|
581
|
+
beforeEach(() => {
|
|
582
|
+
mockConfig = {
|
|
583
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(false),
|
|
584
|
+
};
|
|
585
|
+
originalFetch = global.fetch;
|
|
586
|
+
global.fetch = vi.fn();
|
|
587
|
+
});
|
|
588
|
+
afterEach(() => {
|
|
589
|
+
global.fetch = originalFetch;
|
|
590
|
+
vi.clearAllMocks();
|
|
591
|
+
});
|
|
592
|
+
it('should load cached credentials if available', async () => {
|
|
593
|
+
const fs = await import('node:fs');
|
|
594
|
+
const mockCredentials = {
|
|
595
|
+
access_token: 'cached-token',
|
|
596
|
+
refresh_token: 'cached-refresh',
|
|
597
|
+
token_type: 'Bearer',
|
|
598
|
+
expiry_date: Date.now() + 3600000,
|
|
599
|
+
};
|
|
600
|
+
vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockCredentials));
|
|
601
|
+
// Mock SharedTokenManager to use cached credentials
|
|
602
|
+
const mockTokenManager = {
|
|
603
|
+
getValidCredentials: vi.fn().mockResolvedValue(mockCredentials),
|
|
604
|
+
};
|
|
605
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
606
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
607
|
+
const client = await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
608
|
+
expect(client).toBeInstanceOf(Object);
|
|
609
|
+
expect(mockTokenManager.getValidCredentials).toHaveBeenCalled();
|
|
610
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
611
|
+
});
|
|
612
|
+
it('should handle cached credentials refresh failure', async () => {
|
|
613
|
+
const fs = await import('node:fs');
|
|
614
|
+
const mockCredentials = {
|
|
615
|
+
access_token: 'cached-token',
|
|
616
|
+
refresh_token: 'expired-refresh',
|
|
617
|
+
token_type: 'Bearer',
|
|
618
|
+
expiry_date: Date.now() + 3600000, // Valid expiry time so loadCachedQwenCredentials returns true
|
|
619
|
+
};
|
|
620
|
+
vi.mocked(fs.promises.readFile).mockResolvedValue(JSON.stringify(mockCredentials));
|
|
621
|
+
// Mock SharedTokenManager to fail with a specific error
|
|
622
|
+
const mockTokenManager = {
|
|
623
|
+
getValidCredentials: vi
|
|
624
|
+
.fn()
|
|
625
|
+
.mockRejectedValue(new Error('Token refresh failed')),
|
|
626
|
+
};
|
|
627
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
628
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
629
|
+
// Mock device flow to also fail
|
|
630
|
+
const mockAuthResponse = {
|
|
631
|
+
ok: true,
|
|
632
|
+
json: async () => ({
|
|
633
|
+
error: 'invalid_request',
|
|
634
|
+
error_description: 'Invalid request parameters',
|
|
635
|
+
}),
|
|
636
|
+
};
|
|
637
|
+
vi.mocked(global.fetch).mockResolvedValue(mockAuthResponse);
|
|
638
|
+
// The function should handle the invalid cached credentials and throw the expected error
|
|
639
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Device authorization flow failed');
|
|
640
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
describe('CredentialsClearRequiredError', () => {
|
|
644
|
+
it('should create error with correct name and message', async () => {
|
|
645
|
+
const { CredentialsClearRequiredError } = await import('./qwenOAuth2.js');
|
|
646
|
+
const message = 'Test error message';
|
|
647
|
+
const originalError = { status: 400, response: 'Bad Request' };
|
|
648
|
+
const error = new CredentialsClearRequiredError(message, originalError);
|
|
649
|
+
expect(error.name).toBe('CredentialsClearRequiredError');
|
|
650
|
+
expect(error.message).toBe(message);
|
|
651
|
+
expect(error.originalError).toBe(originalError);
|
|
652
|
+
expect(error instanceof Error).toBe(true);
|
|
653
|
+
});
|
|
654
|
+
it('should work without originalError', async () => {
|
|
655
|
+
const { CredentialsClearRequiredError } = await import('./qwenOAuth2.js');
|
|
656
|
+
const message = 'Test error message';
|
|
657
|
+
const error = new CredentialsClearRequiredError(message);
|
|
658
|
+
expect(error.name).toBe('CredentialsClearRequiredError');
|
|
659
|
+
expect(error.message).toBe(message);
|
|
660
|
+
expect(error.originalError).toBeUndefined();
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
describe('clearQwenCredentials', () => {
|
|
664
|
+
it('should successfully clear credentials file', async () => {
|
|
665
|
+
const { promises: fs } = await import('node:fs');
|
|
666
|
+
const { clearQwenCredentials } = await import('./qwenOAuth2.js');
|
|
667
|
+
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
|
668
|
+
await expect(clearQwenCredentials()).resolves.not.toThrow();
|
|
669
|
+
expect(fs.unlink).toHaveBeenCalled();
|
|
670
|
+
});
|
|
671
|
+
it('should handle file not found error gracefully', async () => {
|
|
672
|
+
const { promises: fs } = await import('node:fs');
|
|
673
|
+
const { clearQwenCredentials } = await import('./qwenOAuth2.js');
|
|
674
|
+
const notFoundError = new Error('File not found');
|
|
675
|
+
notFoundError.code = 'ENOENT';
|
|
676
|
+
vi.mocked(fs.unlink).mockRejectedValue(notFoundError);
|
|
677
|
+
await expect(clearQwenCredentials()).resolves.not.toThrow();
|
|
678
|
+
});
|
|
679
|
+
it('should handle other file system errors gracefully', async () => {
|
|
680
|
+
const { promises: fs } = await import('node:fs');
|
|
681
|
+
const { clearQwenCredentials } = await import('./qwenOAuth2.js');
|
|
682
|
+
const permissionError = new Error('Permission denied');
|
|
683
|
+
vi.mocked(fs.unlink).mockRejectedValue(permissionError);
|
|
684
|
+
// Should not throw but may log warning
|
|
685
|
+
await expect(clearQwenCredentials()).resolves.not.toThrow();
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
describe('QwenOAuth2Client - Additional Error Scenarios', () => {
|
|
689
|
+
let client;
|
|
690
|
+
let originalFetch;
|
|
691
|
+
beforeEach(() => {
|
|
692
|
+
client = new QwenOAuth2Client();
|
|
693
|
+
originalFetch = global.fetch;
|
|
694
|
+
global.fetch = vi.fn();
|
|
695
|
+
});
|
|
696
|
+
afterEach(() => {
|
|
697
|
+
global.fetch = originalFetch;
|
|
698
|
+
vi.clearAllMocks();
|
|
699
|
+
});
|
|
700
|
+
describe('requestDeviceAuthorization HTTP errors', () => {
|
|
701
|
+
it('should handle HTTP error response with non-ok status', async () => {
|
|
702
|
+
const mockResponse = {
|
|
703
|
+
ok: false,
|
|
704
|
+
status: 500,
|
|
705
|
+
statusText: 'Internal Server Error',
|
|
706
|
+
text: async () => 'Server error occurred',
|
|
707
|
+
};
|
|
708
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
709
|
+
await expect(client.requestDeviceAuthorization({
|
|
710
|
+
scope: 'openid profile email model.completion',
|
|
711
|
+
code_challenge: 'test-challenge',
|
|
712
|
+
code_challenge_method: 'S256',
|
|
713
|
+
})).rejects.toThrow('Device authorization failed: 500 Internal Server Error. Response: Server error occurred');
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
describe('getQwenOAuthClient - Enhanced Error Scenarios', () => {
|
|
718
|
+
let mockConfig;
|
|
719
|
+
let originalFetch;
|
|
720
|
+
beforeEach(() => {
|
|
721
|
+
mockConfig = {
|
|
722
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(false),
|
|
723
|
+
};
|
|
724
|
+
originalFetch = global.fetch;
|
|
725
|
+
global.fetch = vi.fn();
|
|
726
|
+
});
|
|
727
|
+
afterEach(() => {
|
|
728
|
+
global.fetch = originalFetch;
|
|
729
|
+
vi.clearAllMocks();
|
|
730
|
+
});
|
|
731
|
+
it('should handle generic refresh token errors', async () => {
|
|
732
|
+
const { promises: fs } = await import('node:fs');
|
|
733
|
+
const mockCredentials = {
|
|
734
|
+
access_token: 'cached-token',
|
|
735
|
+
refresh_token: 'some-refresh-token',
|
|
736
|
+
token_type: 'Bearer',
|
|
737
|
+
expiry_date: Date.now() + 3600000,
|
|
738
|
+
};
|
|
739
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockCredentials));
|
|
740
|
+
// Mock SharedTokenManager to fail
|
|
741
|
+
const mockTokenManager = {
|
|
742
|
+
getValidCredentials: vi
|
|
743
|
+
.fn()
|
|
744
|
+
.mockRejectedValue(new Error('Refresh failed')),
|
|
745
|
+
};
|
|
746
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
747
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
748
|
+
// Mock device flow to also fail
|
|
749
|
+
const mockAuthResponse = {
|
|
750
|
+
ok: true,
|
|
751
|
+
json: async () => ({
|
|
752
|
+
error: 'invalid_request',
|
|
753
|
+
error_description: 'Invalid request parameters',
|
|
754
|
+
}),
|
|
755
|
+
};
|
|
756
|
+
vi.mocked(global.fetch).mockResolvedValue(mockAuthResponse);
|
|
757
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Device authorization flow failed');
|
|
758
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
759
|
+
});
|
|
760
|
+
it('should handle different authentication failure reasons - timeout', async () => {
|
|
761
|
+
const { promises: fs } = await import('node:fs');
|
|
762
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
763
|
+
// Mock SharedTokenManager to fail
|
|
764
|
+
const mockTokenManager = {
|
|
765
|
+
getValidCredentials: vi
|
|
766
|
+
.fn()
|
|
767
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
768
|
+
};
|
|
769
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
770
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
771
|
+
// Mock device authorization to succeed but polling to timeout
|
|
772
|
+
const mockAuthResponse = {
|
|
773
|
+
ok: true,
|
|
774
|
+
json: async () => ({
|
|
775
|
+
device_code: 'test-device-code',
|
|
776
|
+
user_code: 'TEST123',
|
|
777
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
778
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
779
|
+
expires_in: 0.1, // Very short timeout for testing
|
|
780
|
+
}),
|
|
781
|
+
};
|
|
782
|
+
const mockPendingResponse = {
|
|
783
|
+
ok: true,
|
|
784
|
+
json: async () => ({
|
|
785
|
+
status: 'pending',
|
|
786
|
+
}),
|
|
787
|
+
};
|
|
788
|
+
global.fetch = vi
|
|
789
|
+
.fn()
|
|
790
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
791
|
+
.mockResolvedValue(mockPendingResponse);
|
|
792
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Authorization timeout, please restart the process.');
|
|
793
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
794
|
+
});
|
|
795
|
+
it('should handle authentication failure reason - rate limit', async () => {
|
|
796
|
+
const { promises: fs } = await import('node:fs');
|
|
797
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
798
|
+
// Mock SharedTokenManager to fail
|
|
799
|
+
const mockTokenManager = {
|
|
800
|
+
getValidCredentials: vi
|
|
801
|
+
.fn()
|
|
802
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
803
|
+
};
|
|
804
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
805
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
806
|
+
// Mock device authorization to succeed but polling to get rate limited
|
|
807
|
+
const mockAuthResponse = {
|
|
808
|
+
ok: true,
|
|
809
|
+
json: async () => ({
|
|
810
|
+
device_code: 'test-device-code',
|
|
811
|
+
user_code: 'TEST123',
|
|
812
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
813
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
814
|
+
expires_in: 1800,
|
|
815
|
+
}),
|
|
816
|
+
};
|
|
817
|
+
const mockRateLimitResponse = {
|
|
818
|
+
ok: false,
|
|
819
|
+
status: 429,
|
|
820
|
+
statusText: 'Too Many Requests',
|
|
821
|
+
text: async () => 'Rate limited',
|
|
822
|
+
};
|
|
823
|
+
global.fetch = vi
|
|
824
|
+
.fn()
|
|
825
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
826
|
+
.mockResolvedValue(mockRateLimitResponse);
|
|
827
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Too many requests. The server is rate limiting our requests. Please select a different authentication method or try again later.');
|
|
828
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
829
|
+
});
|
|
830
|
+
it('should handle authentication failure reason - error', async () => {
|
|
831
|
+
const { promises: fs } = await import('node:fs');
|
|
832
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
833
|
+
// Mock SharedTokenManager to fail
|
|
834
|
+
const mockTokenManager = {
|
|
835
|
+
getValidCredentials: vi
|
|
836
|
+
.fn()
|
|
837
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
838
|
+
};
|
|
839
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
840
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
841
|
+
// Mock device authorization to fail
|
|
842
|
+
const mockAuthResponse = {
|
|
843
|
+
ok: true,
|
|
844
|
+
json: async () => ({
|
|
845
|
+
error: 'invalid_request',
|
|
846
|
+
error_description: 'Invalid request parameters',
|
|
847
|
+
}),
|
|
848
|
+
};
|
|
849
|
+
global.fetch = vi.fn().mockResolvedValue(mockAuthResponse);
|
|
850
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Device authorization flow failed');
|
|
851
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
describe('authWithQwenDeviceFlow - Comprehensive Testing', () => {
|
|
855
|
+
let mockConfig;
|
|
856
|
+
let originalFetch;
|
|
857
|
+
beforeEach(() => {
|
|
858
|
+
mockConfig = {
|
|
859
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(false),
|
|
860
|
+
};
|
|
861
|
+
originalFetch = global.fetch;
|
|
862
|
+
global.fetch = vi.fn();
|
|
863
|
+
// Mock setTimeout to avoid real delays in tests
|
|
864
|
+
vi.useFakeTimers();
|
|
865
|
+
});
|
|
866
|
+
afterEach(() => {
|
|
867
|
+
global.fetch = originalFetch;
|
|
868
|
+
vi.clearAllMocks();
|
|
869
|
+
vi.useRealTimers();
|
|
870
|
+
});
|
|
871
|
+
it('should handle device authorization error response', async () => {
|
|
872
|
+
const { promises: fs } = await import('node:fs');
|
|
873
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
874
|
+
// Mock SharedTokenManager to fail
|
|
875
|
+
const mockTokenManager = {
|
|
876
|
+
getValidCredentials: vi
|
|
877
|
+
.fn()
|
|
878
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
879
|
+
};
|
|
880
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
881
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
882
|
+
const mockAuthResponse = {
|
|
883
|
+
ok: true,
|
|
884
|
+
json: async () => ({
|
|
885
|
+
error: 'invalid_client',
|
|
886
|
+
error_description: 'Client authentication failed',
|
|
887
|
+
}),
|
|
888
|
+
};
|
|
889
|
+
global.fetch = vi.fn().mockResolvedValue(mockAuthResponse);
|
|
890
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Device authorization flow failed');
|
|
891
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
892
|
+
});
|
|
893
|
+
it('should handle successful authentication flow', async () => {
|
|
894
|
+
const { promises: fs } = await import('node:fs');
|
|
895
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
896
|
+
const mockAuthResponse = {
|
|
897
|
+
ok: true,
|
|
898
|
+
json: async () => ({
|
|
899
|
+
device_code: 'test-device-code',
|
|
900
|
+
user_code: 'TEST123',
|
|
901
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
902
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
903
|
+
expires_in: 1800,
|
|
904
|
+
}),
|
|
905
|
+
};
|
|
906
|
+
const mockTokenResponse = {
|
|
907
|
+
ok: true,
|
|
908
|
+
json: async () => ({
|
|
909
|
+
access_token: 'new-access-token',
|
|
910
|
+
refresh_token: 'new-refresh-token',
|
|
911
|
+
token_type: 'Bearer',
|
|
912
|
+
expires_in: 3600,
|
|
913
|
+
scope: 'openid profile email model.completion',
|
|
914
|
+
}),
|
|
915
|
+
};
|
|
916
|
+
vi.mocked(global.fetch)
|
|
917
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
918
|
+
.mockResolvedValue(mockTokenResponse);
|
|
919
|
+
const client = await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
920
|
+
expect(client).toBeInstanceOf(Object);
|
|
921
|
+
});
|
|
922
|
+
it('should handle 401 error during token polling', async () => {
|
|
923
|
+
const { promises: fs } = await import('node:fs');
|
|
924
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
925
|
+
// Mock SharedTokenManager to fail
|
|
926
|
+
const mockTokenManager = {
|
|
927
|
+
getValidCredentials: vi
|
|
928
|
+
.fn()
|
|
929
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
930
|
+
};
|
|
931
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
932
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
933
|
+
const mockAuthResponse = {
|
|
934
|
+
ok: true,
|
|
935
|
+
json: async () => ({
|
|
936
|
+
device_code: 'test-device-code',
|
|
937
|
+
user_code: 'TEST123',
|
|
938
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
939
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
940
|
+
expires_in: 1800,
|
|
941
|
+
}),
|
|
942
|
+
};
|
|
943
|
+
const mock401Response = {
|
|
944
|
+
ok: false,
|
|
945
|
+
status: 401,
|
|
946
|
+
statusText: 'Unauthorized',
|
|
947
|
+
text: async () => 'Device code expired',
|
|
948
|
+
};
|
|
949
|
+
global.fetch = vi
|
|
950
|
+
.fn()
|
|
951
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
952
|
+
.mockResolvedValue(mock401Response);
|
|
953
|
+
await expect(import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig))).rejects.toThrow('Device code expired or invalid, please restart the authorization process.');
|
|
954
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
955
|
+
});
|
|
956
|
+
it('should handle token polling with browser launch suppressed', async () => {
|
|
957
|
+
const { promises: fs } = await import('node:fs');
|
|
958
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
959
|
+
// Mock SharedTokenManager to fail initially so device flow is used
|
|
960
|
+
const mockTokenManager = {
|
|
961
|
+
getValidCredentials: vi
|
|
962
|
+
.fn()
|
|
963
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
964
|
+
};
|
|
965
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
966
|
+
SharedTokenManager.getInstance = vi.fn().mockReturnValue(mockTokenManager);
|
|
967
|
+
// Mock browser launch as suppressed
|
|
968
|
+
mockConfig.isBrowserLaunchSuppressed = vi.fn().mockReturnValue(true);
|
|
969
|
+
const mockAuthResponse = {
|
|
970
|
+
ok: true,
|
|
971
|
+
json: async () => ({
|
|
972
|
+
device_code: 'test-device-code',
|
|
973
|
+
user_code: 'TEST123',
|
|
974
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
975
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
976
|
+
expires_in: 1800,
|
|
977
|
+
}),
|
|
978
|
+
};
|
|
979
|
+
const mockTokenResponse = {
|
|
980
|
+
ok: true,
|
|
981
|
+
json: async () => ({
|
|
982
|
+
access_token: 'new-access-token',
|
|
983
|
+
refresh_token: 'new-refresh-token',
|
|
984
|
+
token_type: 'Bearer',
|
|
985
|
+
expires_in: 3600,
|
|
986
|
+
scope: 'openid profile email model.completion',
|
|
987
|
+
}),
|
|
988
|
+
};
|
|
989
|
+
global.fetch = vi
|
|
990
|
+
.fn()
|
|
991
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
992
|
+
.mockResolvedValue(mockTokenResponse);
|
|
993
|
+
const client = await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
994
|
+
expect(client).toBeInstanceOf(Object);
|
|
995
|
+
expect(mockConfig.isBrowserLaunchSuppressed).toHaveBeenCalled();
|
|
996
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
describe('Browser Launch and Error Handling', () => {
|
|
1000
|
+
let mockConfig;
|
|
1001
|
+
let originalFetch;
|
|
1002
|
+
beforeEach(() => {
|
|
1003
|
+
mockConfig = {
|
|
1004
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(false),
|
|
1005
|
+
};
|
|
1006
|
+
originalFetch = global.fetch;
|
|
1007
|
+
global.fetch = vi.fn();
|
|
1008
|
+
});
|
|
1009
|
+
afterEach(() => {
|
|
1010
|
+
global.fetch = originalFetch;
|
|
1011
|
+
vi.clearAllMocks();
|
|
1012
|
+
});
|
|
1013
|
+
it('should handle browser launch failure gracefully', async () => {
|
|
1014
|
+
const { promises: fs } = await import('node:fs');
|
|
1015
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
1016
|
+
// Mock open to throw error
|
|
1017
|
+
const open = await import('open');
|
|
1018
|
+
vi.mocked(open.default).mockRejectedValue(new Error('Browser launch failed'));
|
|
1019
|
+
const mockAuthResponse = {
|
|
1020
|
+
ok: true,
|
|
1021
|
+
json: async () => ({
|
|
1022
|
+
device_code: 'test-device-code',
|
|
1023
|
+
user_code: 'TEST123',
|
|
1024
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1025
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1026
|
+
expires_in: 1800,
|
|
1027
|
+
}),
|
|
1028
|
+
};
|
|
1029
|
+
const mockTokenResponse = {
|
|
1030
|
+
ok: true,
|
|
1031
|
+
json: async () => ({
|
|
1032
|
+
access_token: 'new-access-token',
|
|
1033
|
+
refresh_token: 'new-refresh-token',
|
|
1034
|
+
token_type: 'Bearer',
|
|
1035
|
+
expires_in: 3600,
|
|
1036
|
+
scope: 'openid profile email model.completion',
|
|
1037
|
+
}),
|
|
1038
|
+
};
|
|
1039
|
+
vi.mocked(global.fetch)
|
|
1040
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
1041
|
+
.mockResolvedValue(mockTokenResponse);
|
|
1042
|
+
const client = await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1043
|
+
expect(client).toBeInstanceOf(Object);
|
|
1044
|
+
});
|
|
1045
|
+
it('should handle browser child process error gracefully', async () => {
|
|
1046
|
+
const { promises: fs } = await import('node:fs');
|
|
1047
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached credentials'));
|
|
1048
|
+
// Mock open to return a child process that will emit error
|
|
1049
|
+
const open = await import('open');
|
|
1050
|
+
const mockChildProcess = {
|
|
1051
|
+
on: vi.fn((event, callback) => {
|
|
1052
|
+
if (event === 'error') {
|
|
1053
|
+
// Call the error handler immediately for testing
|
|
1054
|
+
setTimeout(() => callback(new Error('Process spawn failed')), 0);
|
|
1055
|
+
}
|
|
1056
|
+
}),
|
|
1057
|
+
};
|
|
1058
|
+
vi.mocked(open.default).mockResolvedValue(mockChildProcess);
|
|
1059
|
+
const mockAuthResponse = {
|
|
1060
|
+
ok: true,
|
|
1061
|
+
json: async () => ({
|
|
1062
|
+
device_code: 'test-device-code',
|
|
1063
|
+
user_code: 'TEST123',
|
|
1064
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1065
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1066
|
+
expires_in: 1800,
|
|
1067
|
+
}),
|
|
1068
|
+
};
|
|
1069
|
+
const mockTokenResponse = {
|
|
1070
|
+
ok: true,
|
|
1071
|
+
json: async () => ({
|
|
1072
|
+
access_token: 'new-access-token',
|
|
1073
|
+
refresh_token: 'new-refresh-token',
|
|
1074
|
+
token_type: 'Bearer',
|
|
1075
|
+
expires_in: 3600,
|
|
1076
|
+
scope: 'openid profile email model.completion',
|
|
1077
|
+
}),
|
|
1078
|
+
};
|
|
1079
|
+
vi.mocked(global.fetch)
|
|
1080
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
1081
|
+
.mockResolvedValue(mockTokenResponse);
|
|
1082
|
+
const client = await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1083
|
+
expect(client).toBeInstanceOf(Object);
|
|
1084
|
+
});
|
|
1085
|
+
});
|
|
1086
|
+
describe('Event Emitter Integration', () => {
|
|
1087
|
+
it('should export qwenOAuth2Events as EventEmitter', async () => {
|
|
1088
|
+
const { qwenOAuth2Events } = await import('./qwenOAuth2.js');
|
|
1089
|
+
expect(qwenOAuth2Events).toBeInstanceOf(EventEmitter);
|
|
1090
|
+
});
|
|
1091
|
+
it('should define correct event enum values', async () => {
|
|
1092
|
+
const { QwenOAuth2Event } = await import('./qwenOAuth2.js');
|
|
1093
|
+
expect(QwenOAuth2Event.AuthUri).toBe('auth-uri');
|
|
1094
|
+
expect(QwenOAuth2Event.AuthProgress).toBe('auth-progress');
|
|
1095
|
+
expect(QwenOAuth2Event.AuthCancel).toBe('auth-cancel');
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
describe('Utility Functions', () => {
|
|
1099
|
+
describe('objectToUrlEncoded', () => {
|
|
1100
|
+
it('should encode object properties to URL-encoded format', async () => {
|
|
1101
|
+
// Since objectToUrlEncoded is private, we test it indirectly through the client
|
|
1102
|
+
const objectToUrlEncoded = (data) => Object.keys(data)
|
|
1103
|
+
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
|
1104
|
+
.join('&');
|
|
1105
|
+
const testData = {
|
|
1106
|
+
client_id: 'test-client',
|
|
1107
|
+
scope: 'openid profile',
|
|
1108
|
+
redirect_uri: 'https://example.com/callback',
|
|
1109
|
+
};
|
|
1110
|
+
const result = objectToUrlEncoded(testData);
|
|
1111
|
+
expect(result).toContain('client_id=test-client');
|
|
1112
|
+
expect(result).toContain('scope=openid%20profile');
|
|
1113
|
+
expect(result).toContain('redirect_uri=https%3A%2F%2Fexample.com%2Fcallback');
|
|
1114
|
+
});
|
|
1115
|
+
it('should handle special characters', async () => {
|
|
1116
|
+
const objectToUrlEncoded = (data) => Object.keys(data)
|
|
1117
|
+
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
|
1118
|
+
.join('&');
|
|
1119
|
+
const testData = {
|
|
1120
|
+
'param with spaces': 'value with spaces',
|
|
1121
|
+
'param&with&s': 'value&with&s',
|
|
1122
|
+
'param=with=equals': 'value=with=equals',
|
|
1123
|
+
};
|
|
1124
|
+
const result = objectToUrlEncoded(testData);
|
|
1125
|
+
expect(result).toContain('param%20with%20spaces=value%20with%20spaces');
|
|
1126
|
+
expect(result).toContain('param%26with%26amps=value%26with%26amps');
|
|
1127
|
+
expect(result).toContain('param%3Dwith%3Dequals=value%3Dwith%3Dequals');
|
|
1128
|
+
});
|
|
1129
|
+
it('should handle empty object', async () => {
|
|
1130
|
+
const objectToUrlEncoded = (data) => Object.keys(data)
|
|
1131
|
+
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
|
1132
|
+
.join('&');
|
|
1133
|
+
const result = objectToUrlEncoded({});
|
|
1134
|
+
expect(result).toBe('');
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
describe('getQwenCachedCredentialPath', () => {
|
|
1138
|
+
it('should return correct path to cached credentials', async () => {
|
|
1139
|
+
const os = await import('os');
|
|
1140
|
+
const path = await import('path');
|
|
1141
|
+
const expectedPath = path.join(os.homedir(), '.qwen', 'oauth_creds.json');
|
|
1142
|
+
// Since this is a private function, we test it indirectly through clearQwenCredentials
|
|
1143
|
+
const { promises: fs } = await import('node:fs');
|
|
1144
|
+
const { clearQwenCredentials } = await import('./qwenOAuth2.js');
|
|
1145
|
+
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
|
1146
|
+
await clearQwenCredentials();
|
|
1147
|
+
expect(fs.unlink).toHaveBeenCalledWith(expectedPath);
|
|
1148
|
+
});
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
describe('Credential Caching Functions', () => {
|
|
1152
|
+
describe('cacheQwenCredentials', () => {
|
|
1153
|
+
it('should create directory and write credentials to file', async () => {
|
|
1154
|
+
// Mock the internal cacheQwenCredentials function by creating client and calling refresh
|
|
1155
|
+
const client = new QwenOAuth2Client();
|
|
1156
|
+
client.setCredentials({
|
|
1157
|
+
refresh_token: 'test-refresh',
|
|
1158
|
+
});
|
|
1159
|
+
const mockResponse = {
|
|
1160
|
+
ok: true,
|
|
1161
|
+
json: async () => ({
|
|
1162
|
+
access_token: 'new-token',
|
|
1163
|
+
token_type: 'Bearer',
|
|
1164
|
+
expires_in: 3600,
|
|
1165
|
+
}),
|
|
1166
|
+
};
|
|
1167
|
+
global.fetch = vi.fn().mockResolvedValue(mockResponse);
|
|
1168
|
+
await client.refreshAccessToken();
|
|
1169
|
+
// Note: File caching is now handled by SharedTokenManager, so these calls won't happen
|
|
1170
|
+
// This test verifies that refreshAccessToken works correctly
|
|
1171
|
+
const updatedCredentials = client.getCredentials();
|
|
1172
|
+
expect(updatedCredentials.access_token).toBe('new-token');
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
describe('loadCachedQwenCredentials', () => {
|
|
1176
|
+
it('should load and validate cached credentials successfully', async () => {
|
|
1177
|
+
const { promises: fs } = await import('node:fs');
|
|
1178
|
+
const mockCredentials = {
|
|
1179
|
+
access_token: 'cached-token',
|
|
1180
|
+
refresh_token: 'cached-refresh',
|
|
1181
|
+
token_type: 'Bearer',
|
|
1182
|
+
expiry_date: Date.now() + 3600000,
|
|
1183
|
+
};
|
|
1184
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockCredentials));
|
|
1185
|
+
// Test through getQwenOAuthClient which calls loadCachedQwenCredentials
|
|
1186
|
+
const mockConfig = {
|
|
1187
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(true),
|
|
1188
|
+
};
|
|
1189
|
+
// Make SharedTokenManager fail to test the fallback
|
|
1190
|
+
const mockTokenManager = {
|
|
1191
|
+
getValidCredentials: vi
|
|
1192
|
+
.fn()
|
|
1193
|
+
.mockRejectedValue(new Error('No cached creds')),
|
|
1194
|
+
};
|
|
1195
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
1196
|
+
SharedTokenManager.getInstance = vi
|
|
1197
|
+
.fn()
|
|
1198
|
+
.mockReturnValue(mockTokenManager);
|
|
1199
|
+
// Mock successful auth flow after cache load fails
|
|
1200
|
+
const mockAuthResponse = {
|
|
1201
|
+
ok: true,
|
|
1202
|
+
json: async () => ({
|
|
1203
|
+
device_code: 'test-device-code',
|
|
1204
|
+
user_code: 'TEST123',
|
|
1205
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1206
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1207
|
+
expires_in: 1800,
|
|
1208
|
+
}),
|
|
1209
|
+
};
|
|
1210
|
+
const mockTokenResponse = {
|
|
1211
|
+
ok: true,
|
|
1212
|
+
json: async () => ({
|
|
1213
|
+
access_token: 'new-access-token',
|
|
1214
|
+
refresh_token: 'new-refresh-token',
|
|
1215
|
+
token_type: 'Bearer',
|
|
1216
|
+
expires_in: 3600,
|
|
1217
|
+
scope: 'openid profile email model.completion',
|
|
1218
|
+
}),
|
|
1219
|
+
};
|
|
1220
|
+
global.fetch = vi
|
|
1221
|
+
.fn()
|
|
1222
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
1223
|
+
.mockResolvedValue(mockTokenResponse);
|
|
1224
|
+
try {
|
|
1225
|
+
await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1226
|
+
}
|
|
1227
|
+
catch {
|
|
1228
|
+
// Expected to fail in test environment
|
|
1229
|
+
}
|
|
1230
|
+
expect(fs.readFile).toHaveBeenCalled();
|
|
1231
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
1232
|
+
});
|
|
1233
|
+
it('should handle invalid cached credentials gracefully', async () => {
|
|
1234
|
+
const { promises: fs } = await import('node:fs');
|
|
1235
|
+
// Mock file read to return invalid JSON
|
|
1236
|
+
vi.mocked(fs.readFile).mockResolvedValue('invalid-json');
|
|
1237
|
+
const mockConfig = {
|
|
1238
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(true),
|
|
1239
|
+
};
|
|
1240
|
+
const mockTokenManager = {
|
|
1241
|
+
getValidCredentials: vi
|
|
1242
|
+
.fn()
|
|
1243
|
+
.mockRejectedValue(new Error('No cached creds')),
|
|
1244
|
+
};
|
|
1245
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
1246
|
+
SharedTokenManager.getInstance = vi
|
|
1247
|
+
.fn()
|
|
1248
|
+
.mockReturnValue(mockTokenManager);
|
|
1249
|
+
// Mock auth flow
|
|
1250
|
+
const mockAuthResponse = {
|
|
1251
|
+
ok: true,
|
|
1252
|
+
json: async () => ({
|
|
1253
|
+
device_code: 'test-device-code',
|
|
1254
|
+
user_code: 'TEST123',
|
|
1255
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1256
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1257
|
+
expires_in: 1800,
|
|
1258
|
+
}),
|
|
1259
|
+
};
|
|
1260
|
+
const mockTokenResponse = {
|
|
1261
|
+
ok: true,
|
|
1262
|
+
json: async () => ({
|
|
1263
|
+
access_token: 'new-token',
|
|
1264
|
+
refresh_token: 'new-refresh',
|
|
1265
|
+
token_type: 'Bearer',
|
|
1266
|
+
expires_in: 3600,
|
|
1267
|
+
}),
|
|
1268
|
+
};
|
|
1269
|
+
global.fetch = vi
|
|
1270
|
+
.fn()
|
|
1271
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
1272
|
+
.mockResolvedValue(mockTokenResponse);
|
|
1273
|
+
try {
|
|
1274
|
+
await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1275
|
+
}
|
|
1276
|
+
catch {
|
|
1277
|
+
// Expected to fail in test environment
|
|
1278
|
+
}
|
|
1279
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
1280
|
+
});
|
|
1281
|
+
it('should handle file access errors', async () => {
|
|
1282
|
+
const { promises: fs } = await import('node:fs');
|
|
1283
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('File not found'));
|
|
1284
|
+
const mockConfig = {
|
|
1285
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(true),
|
|
1286
|
+
};
|
|
1287
|
+
const mockTokenManager = {
|
|
1288
|
+
getValidCredentials: vi
|
|
1289
|
+
.fn()
|
|
1290
|
+
.mockRejectedValue(new Error('No cached creds')),
|
|
1291
|
+
};
|
|
1292
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
1293
|
+
SharedTokenManager.getInstance = vi
|
|
1294
|
+
.fn()
|
|
1295
|
+
.mockReturnValue(mockTokenManager);
|
|
1296
|
+
// Mock device flow to fail quickly
|
|
1297
|
+
const mockAuthResponse = {
|
|
1298
|
+
ok: true,
|
|
1299
|
+
json: async () => ({
|
|
1300
|
+
error: 'invalid_request',
|
|
1301
|
+
error_description: 'Invalid request parameters',
|
|
1302
|
+
}),
|
|
1303
|
+
};
|
|
1304
|
+
global.fetch = vi.fn().mockResolvedValue(mockAuthResponse);
|
|
1305
|
+
// Should proceed to device flow when cache loading fails
|
|
1306
|
+
try {
|
|
1307
|
+
await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1308
|
+
}
|
|
1309
|
+
catch {
|
|
1310
|
+
// Expected to fail in test environment
|
|
1311
|
+
}
|
|
1312
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
1313
|
+
});
|
|
1314
|
+
});
|
|
1315
|
+
});
|
|
1316
|
+
describe('Enhanced Error Handling and Edge Cases', () => {
|
|
1317
|
+
let client;
|
|
1318
|
+
let originalFetch;
|
|
1319
|
+
beforeEach(() => {
|
|
1320
|
+
client = new QwenOAuth2Client();
|
|
1321
|
+
originalFetch = global.fetch;
|
|
1322
|
+
global.fetch = vi.fn();
|
|
1323
|
+
});
|
|
1324
|
+
afterEach(() => {
|
|
1325
|
+
global.fetch = originalFetch;
|
|
1326
|
+
vi.clearAllMocks();
|
|
1327
|
+
});
|
|
1328
|
+
describe('QwenOAuth2Client getAccessToken enhanced scenarios', () => {
|
|
1329
|
+
it('should return undefined when SharedTokenManager fails (no fallback)', async () => {
|
|
1330
|
+
// Set up client with valid credentials (but we don't use fallback anymore)
|
|
1331
|
+
client.setCredentials({
|
|
1332
|
+
access_token: 'fallback-token',
|
|
1333
|
+
expiry_date: Date.now() + 3600000, // Valid for 1 hour
|
|
1334
|
+
});
|
|
1335
|
+
// Override the client's SharedTokenManager instance directly to ensure it fails
|
|
1336
|
+
client.sharedManager = {
|
|
1337
|
+
getValidCredentials: vi
|
|
1338
|
+
.fn()
|
|
1339
|
+
.mockRejectedValue(new Error('Manager failed')),
|
|
1340
|
+
};
|
|
1341
|
+
// Mock console.warn to avoid test noise
|
|
1342
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1343
|
+
const result = await client.getAccessToken();
|
|
1344
|
+
// With our race condition fix, we no longer fall back to local credentials
|
|
1345
|
+
// to ensure single source of truth
|
|
1346
|
+
expect(result.token).toBeUndefined();
|
|
1347
|
+
expect(consoleSpy).toHaveBeenCalledWith('Failed to get access token from shared manager:', expect.any(Error));
|
|
1348
|
+
consoleSpy.mockRestore();
|
|
1349
|
+
});
|
|
1350
|
+
it('should return undefined when both manager and cache fail', async () => {
|
|
1351
|
+
// Set up client with expired credentials
|
|
1352
|
+
client.setCredentials({
|
|
1353
|
+
access_token: 'expired-token',
|
|
1354
|
+
expiry_date: Date.now() - 1000, // Expired
|
|
1355
|
+
});
|
|
1356
|
+
// Override the client's SharedTokenManager instance directly to ensure it fails
|
|
1357
|
+
client.sharedManager = {
|
|
1358
|
+
getValidCredentials: vi
|
|
1359
|
+
.fn()
|
|
1360
|
+
.mockRejectedValue(new Error('Manager failed')),
|
|
1361
|
+
};
|
|
1362
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1363
|
+
const result = await client.getAccessToken();
|
|
1364
|
+
expect(result.token).toBeUndefined();
|
|
1365
|
+
consoleSpy.mockRestore();
|
|
1366
|
+
});
|
|
1367
|
+
it('should handle missing credentials gracefully', async () => {
|
|
1368
|
+
// No credentials set
|
|
1369
|
+
client.setCredentials({});
|
|
1370
|
+
// Override the client's SharedTokenManager instance directly to ensure it fails
|
|
1371
|
+
client.sharedManager = {
|
|
1372
|
+
getValidCredentials: vi
|
|
1373
|
+
.fn()
|
|
1374
|
+
.mockRejectedValue(new Error('No credentials')),
|
|
1375
|
+
};
|
|
1376
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1377
|
+
const result = await client.getAccessToken();
|
|
1378
|
+
expect(result.token).toBeUndefined();
|
|
1379
|
+
consoleSpy.mockRestore();
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1382
|
+
describe('Enhanced requestDeviceAuthorization scenarios', () => {
|
|
1383
|
+
it('should include x-request-id header', async () => {
|
|
1384
|
+
const mockResponse = {
|
|
1385
|
+
ok: true,
|
|
1386
|
+
json: async () => ({
|
|
1387
|
+
device_code: 'test-device-code',
|
|
1388
|
+
user_code: 'TEST123',
|
|
1389
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1390
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1391
|
+
expires_in: 1800,
|
|
1392
|
+
}),
|
|
1393
|
+
};
|
|
1394
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1395
|
+
await client.requestDeviceAuthorization({
|
|
1396
|
+
scope: 'openid profile email model.completion',
|
|
1397
|
+
code_challenge: 'test-challenge',
|
|
1398
|
+
code_challenge_method: 'S256',
|
|
1399
|
+
});
|
|
1400
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
1401
|
+
headers: expect.objectContaining({
|
|
1402
|
+
'x-request-id': expect.any(String),
|
|
1403
|
+
}),
|
|
1404
|
+
}));
|
|
1405
|
+
});
|
|
1406
|
+
it('should include correct Content-Type and Accept headers', async () => {
|
|
1407
|
+
const mockResponse = {
|
|
1408
|
+
ok: true,
|
|
1409
|
+
json: async () => ({
|
|
1410
|
+
device_code: 'test-device-code',
|
|
1411
|
+
user_code: 'TEST123',
|
|
1412
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1413
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1414
|
+
expires_in: 1800,
|
|
1415
|
+
}),
|
|
1416
|
+
};
|
|
1417
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1418
|
+
await client.requestDeviceAuthorization({
|
|
1419
|
+
scope: 'openid profile email model.completion',
|
|
1420
|
+
code_challenge: 'test-challenge',
|
|
1421
|
+
code_challenge_method: 'S256',
|
|
1422
|
+
});
|
|
1423
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
1424
|
+
headers: expect.objectContaining({
|
|
1425
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
1426
|
+
Accept: 'application/json',
|
|
1427
|
+
}),
|
|
1428
|
+
}));
|
|
1429
|
+
});
|
|
1430
|
+
it('should send correct form data', async () => {
|
|
1431
|
+
const mockResponse = {
|
|
1432
|
+
ok: true,
|
|
1433
|
+
json: async () => ({
|
|
1434
|
+
device_code: 'test-device-code',
|
|
1435
|
+
user_code: 'TEST123',
|
|
1436
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1437
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1438
|
+
expires_in: 1800,
|
|
1439
|
+
}),
|
|
1440
|
+
};
|
|
1441
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1442
|
+
await client.requestDeviceAuthorization({
|
|
1443
|
+
scope: 'test-scope',
|
|
1444
|
+
code_challenge: 'test-challenge',
|
|
1445
|
+
code_challenge_method: 'S256',
|
|
1446
|
+
});
|
|
1447
|
+
const [, options] = vi.mocked(global.fetch).mock.calls[0];
|
|
1448
|
+
expect(options?.body).toContain('client_id=f0304373b74a44d2b584a3fb70ca9e56');
|
|
1449
|
+
expect(options?.body).toContain('scope=test-scope');
|
|
1450
|
+
expect(options?.body).toContain('code_challenge=test-challenge');
|
|
1451
|
+
expect(options?.body).toContain('code_challenge_method=S256');
|
|
1452
|
+
});
|
|
1453
|
+
});
|
|
1454
|
+
describe('Enhanced pollDeviceToken scenarios', () => {
|
|
1455
|
+
it('should handle JSON parsing error during error response', async () => {
|
|
1456
|
+
const mockResponse = {
|
|
1457
|
+
ok: false,
|
|
1458
|
+
status: 400,
|
|
1459
|
+
statusText: 'Bad Request',
|
|
1460
|
+
json: vi.fn().mockRejectedValue(new Error('Invalid JSON')),
|
|
1461
|
+
text: vi.fn().mockResolvedValue('Invalid request format'),
|
|
1462
|
+
};
|
|
1463
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1464
|
+
await expect(client.pollDeviceToken({
|
|
1465
|
+
device_code: 'test-device-code',
|
|
1466
|
+
code_verifier: 'test-verifier',
|
|
1467
|
+
})).rejects.toThrow('Device token poll failed: 400 Bad Request');
|
|
1468
|
+
});
|
|
1469
|
+
it('should include status code in thrown errors', async () => {
|
|
1470
|
+
const mockResponse = {
|
|
1471
|
+
ok: false,
|
|
1472
|
+
status: 500,
|
|
1473
|
+
statusText: 'Internal Server Error',
|
|
1474
|
+
json: vi.fn().mockRejectedValue(new Error('Invalid JSON')),
|
|
1475
|
+
text: vi.fn().mockResolvedValue('Internal server error'),
|
|
1476
|
+
};
|
|
1477
|
+
global.fetch = vi
|
|
1478
|
+
.fn()
|
|
1479
|
+
.mockResolvedValue(mockResponse);
|
|
1480
|
+
await expect(client.pollDeviceToken({
|
|
1481
|
+
device_code: 'test-device-code',
|
|
1482
|
+
code_verifier: 'test-verifier',
|
|
1483
|
+
})).rejects.toMatchObject({
|
|
1484
|
+
message: expect.stringContaining('Device token poll failed: 500 Internal Server Error'),
|
|
1485
|
+
status: 500,
|
|
1486
|
+
});
|
|
1487
|
+
});
|
|
1488
|
+
it('should handle authorization_pending with correct status', async () => {
|
|
1489
|
+
const errorData = {
|
|
1490
|
+
error: 'authorization_pending',
|
|
1491
|
+
error_description: 'Authorization request is pending',
|
|
1492
|
+
};
|
|
1493
|
+
const mockResponse = {
|
|
1494
|
+
ok: false,
|
|
1495
|
+
status: 400,
|
|
1496
|
+
statusText: 'Bad Request',
|
|
1497
|
+
text: vi.fn().mockResolvedValue(JSON.stringify(errorData)),
|
|
1498
|
+
json: vi.fn().mockResolvedValue(errorData),
|
|
1499
|
+
};
|
|
1500
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1501
|
+
const result = await client.pollDeviceToken({
|
|
1502
|
+
device_code: 'test-device-code',
|
|
1503
|
+
code_verifier: 'test-verifier',
|
|
1504
|
+
});
|
|
1505
|
+
expect(result).toEqual({ status: 'pending' });
|
|
1506
|
+
});
|
|
1507
|
+
});
|
|
1508
|
+
describe('Enhanced refreshAccessToken scenarios', () => {
|
|
1509
|
+
it('should call clearQwenCredentials on 400 error', async () => {
|
|
1510
|
+
client.setCredentials({
|
|
1511
|
+
refresh_token: 'expired-refresh',
|
|
1512
|
+
});
|
|
1513
|
+
const { promises: fs } = await import('node:fs');
|
|
1514
|
+
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
|
1515
|
+
const mockResponse = {
|
|
1516
|
+
ok: false,
|
|
1517
|
+
status: 400,
|
|
1518
|
+
text: async () => 'Bad Request',
|
|
1519
|
+
};
|
|
1520
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1521
|
+
await expect(client.refreshAccessToken()).rejects.toThrow("Refresh token expired or invalid. Please use '/auth' to re-authenticate.");
|
|
1522
|
+
expect(fs.unlink).toHaveBeenCalled();
|
|
1523
|
+
});
|
|
1524
|
+
it('should throw CredentialsClearRequiredError on 400 error', async () => {
|
|
1525
|
+
const { CredentialsClearRequiredError } = await import('./qwenOAuth2.js');
|
|
1526
|
+
client.setCredentials({
|
|
1527
|
+
refresh_token: 'expired-refresh',
|
|
1528
|
+
});
|
|
1529
|
+
const { promises: fs } = await import('node:fs');
|
|
1530
|
+
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
|
1531
|
+
const mockResponse = {
|
|
1532
|
+
ok: false,
|
|
1533
|
+
status: 400,
|
|
1534
|
+
text: async () => 'Bad Request',
|
|
1535
|
+
};
|
|
1536
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1537
|
+
await expect(client.refreshAccessToken()).rejects.toThrow(CredentialsClearRequiredError);
|
|
1538
|
+
try {
|
|
1539
|
+
await client.refreshAccessToken();
|
|
1540
|
+
}
|
|
1541
|
+
catch (error) {
|
|
1542
|
+
expect(error).toBeInstanceOf(CredentialsClearRequiredError);
|
|
1543
|
+
if (error instanceof CredentialsClearRequiredError) {
|
|
1544
|
+
expect(error.originalError).toEqual({
|
|
1545
|
+
status: 400,
|
|
1546
|
+
response: 'Bad Request',
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
expect(fs.unlink).toHaveBeenCalled();
|
|
1551
|
+
});
|
|
1552
|
+
it('should preserve existing refresh token when new one not provided', async () => {
|
|
1553
|
+
const originalRefreshToken = 'original-refresh-token';
|
|
1554
|
+
client.setCredentials({
|
|
1555
|
+
refresh_token: originalRefreshToken,
|
|
1556
|
+
});
|
|
1557
|
+
const mockResponse = {
|
|
1558
|
+
ok: true,
|
|
1559
|
+
json: async () => ({
|
|
1560
|
+
access_token: 'new-access-token',
|
|
1561
|
+
token_type: 'Bearer',
|
|
1562
|
+
expires_in: 3600,
|
|
1563
|
+
// No refresh_token in response
|
|
1564
|
+
}),
|
|
1565
|
+
};
|
|
1566
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1567
|
+
await client.refreshAccessToken();
|
|
1568
|
+
const credentials = client.getCredentials();
|
|
1569
|
+
expect(credentials.refresh_token).toBe(originalRefreshToken);
|
|
1570
|
+
});
|
|
1571
|
+
it('should include resource_url when provided in response', async () => {
|
|
1572
|
+
client.setCredentials({
|
|
1573
|
+
refresh_token: 'test-refresh',
|
|
1574
|
+
});
|
|
1575
|
+
const mockResponse = {
|
|
1576
|
+
ok: true,
|
|
1577
|
+
json: async () => ({
|
|
1578
|
+
access_token: 'new-access-token',
|
|
1579
|
+
token_type: 'Bearer',
|
|
1580
|
+
expires_in: 3600,
|
|
1581
|
+
resource_url: 'https://new-resource-url.com',
|
|
1582
|
+
}),
|
|
1583
|
+
};
|
|
1584
|
+
vi.mocked(global.fetch).mockResolvedValue(mockResponse);
|
|
1585
|
+
await client.refreshAccessToken();
|
|
1586
|
+
const credentials = client.getCredentials();
|
|
1587
|
+
expect(credentials.resource_url).toBe('https://new-resource-url.com');
|
|
1588
|
+
});
|
|
1589
|
+
});
|
|
1590
|
+
});
|
|
1591
|
+
describe('SharedTokenManager Integration in QwenOAuth2Client', () => {
|
|
1592
|
+
let client;
|
|
1593
|
+
beforeEach(() => {
|
|
1594
|
+
client = new QwenOAuth2Client();
|
|
1595
|
+
});
|
|
1596
|
+
it('should use SharedTokenManager instance in constructor', () => {
|
|
1597
|
+
const sharedManager = client.sharedManager;
|
|
1598
|
+
expect(sharedManager).toBeDefined();
|
|
1599
|
+
});
|
|
1600
|
+
it('should handle TokenManagerError types correctly in getQwenOAuthClient', async () => {
|
|
1601
|
+
const mockConfig = {
|
|
1602
|
+
isBrowserLaunchSuppressed: vi.fn().mockReturnValue(true),
|
|
1603
|
+
};
|
|
1604
|
+
// Test different TokenManagerError types
|
|
1605
|
+
const tokenErrors = [
|
|
1606
|
+
{ type: TokenError.NO_REFRESH_TOKEN, message: 'No refresh token' },
|
|
1607
|
+
{ type: TokenError.REFRESH_FAILED, message: 'Token refresh failed' },
|
|
1608
|
+
{ type: TokenError.NETWORK_ERROR, message: 'Network error' },
|
|
1609
|
+
{ type: TokenError.REFRESH_FAILED, message: 'Refresh failed' },
|
|
1610
|
+
];
|
|
1611
|
+
for (const errorInfo of tokenErrors) {
|
|
1612
|
+
const tokenError = new TokenManagerError(errorInfo.type, errorInfo.message);
|
|
1613
|
+
const mockTokenManager = {
|
|
1614
|
+
getValidCredentials: vi.fn().mockRejectedValue(tokenError),
|
|
1615
|
+
};
|
|
1616
|
+
const originalGetInstance = SharedTokenManager.getInstance;
|
|
1617
|
+
SharedTokenManager.getInstance = vi
|
|
1618
|
+
.fn()
|
|
1619
|
+
.mockReturnValue(mockTokenManager);
|
|
1620
|
+
const { promises: fs } = await import('node:fs');
|
|
1621
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error('No cached file'));
|
|
1622
|
+
// Mock device flow to succeed
|
|
1623
|
+
const mockAuthResponse = {
|
|
1624
|
+
ok: true,
|
|
1625
|
+
json: async () => ({
|
|
1626
|
+
device_code: 'test-device-code',
|
|
1627
|
+
user_code: 'TEST123',
|
|
1628
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1629
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1630
|
+
expires_in: 1800,
|
|
1631
|
+
}),
|
|
1632
|
+
};
|
|
1633
|
+
const mockTokenResponse = {
|
|
1634
|
+
ok: true,
|
|
1635
|
+
json: async () => ({
|
|
1636
|
+
access_token: 'new-token',
|
|
1637
|
+
refresh_token: 'new-refresh',
|
|
1638
|
+
token_type: 'Bearer',
|
|
1639
|
+
expires_in: 3600,
|
|
1640
|
+
}),
|
|
1641
|
+
};
|
|
1642
|
+
global.fetch = vi
|
|
1643
|
+
.fn()
|
|
1644
|
+
.mockResolvedValueOnce(mockAuthResponse)
|
|
1645
|
+
.mockResolvedValue(mockTokenResponse);
|
|
1646
|
+
try {
|
|
1647
|
+
await import('./qwenOAuth2.js').then((module) => module.getQwenOAuthClient(mockConfig));
|
|
1648
|
+
}
|
|
1649
|
+
catch {
|
|
1650
|
+
// Expected to fail in test environment
|
|
1651
|
+
}
|
|
1652
|
+
SharedTokenManager.getInstance = originalGetInstance;
|
|
1653
|
+
vi.clearAllMocks();
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
describe('Constants and Configuration', () => {
|
|
1658
|
+
it('should have correct OAuth endpoints', async () => {
|
|
1659
|
+
// Test that the constants are properly defined by checking they're used in requests
|
|
1660
|
+
const client = new QwenOAuth2Client();
|
|
1661
|
+
const mockResponse = {
|
|
1662
|
+
ok: true,
|
|
1663
|
+
json: async () => ({
|
|
1664
|
+
device_code: 'test-device-code',
|
|
1665
|
+
user_code: 'TEST123',
|
|
1666
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1667
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1668
|
+
expires_in: 1800,
|
|
1669
|
+
}),
|
|
1670
|
+
};
|
|
1671
|
+
global.fetch = vi.fn().mockResolvedValue(mockResponse);
|
|
1672
|
+
await client.requestDeviceAuthorization({
|
|
1673
|
+
scope: 'test-scope',
|
|
1674
|
+
code_challenge: 'test-challenge',
|
|
1675
|
+
code_challenge_method: 'S256',
|
|
1676
|
+
});
|
|
1677
|
+
const [url] = vi.mocked(global.fetch).mock.calls[0];
|
|
1678
|
+
expect(url).toBe('https://chat.qwen.ai/api/v1/oauth2/device/code');
|
|
1679
|
+
});
|
|
1680
|
+
it('should use correct client ID in requests', async () => {
|
|
1681
|
+
const client = new QwenOAuth2Client();
|
|
1682
|
+
const mockResponse = {
|
|
1683
|
+
ok: true,
|
|
1684
|
+
json: async () => ({
|
|
1685
|
+
device_code: 'test-device-code',
|
|
1686
|
+
user_code: 'TEST123',
|
|
1687
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1688
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1689
|
+
expires_in: 1800,
|
|
1690
|
+
}),
|
|
1691
|
+
};
|
|
1692
|
+
global.fetch = vi.fn().mockResolvedValue(mockResponse);
|
|
1693
|
+
await client.requestDeviceAuthorization({
|
|
1694
|
+
scope: 'test-scope',
|
|
1695
|
+
code_challenge: 'test-challenge',
|
|
1696
|
+
code_challenge_method: 'S256',
|
|
1697
|
+
});
|
|
1698
|
+
const [, options] = vi.mocked(global.fetch).mock.calls[0];
|
|
1699
|
+
expect(options?.body).toContain('client_id=f0304373b74a44d2b584a3fb70ca9e56');
|
|
1700
|
+
});
|
|
1701
|
+
it('should use correct default scope', async () => {
|
|
1702
|
+
// Test the default scope constant by checking it's used in device flow
|
|
1703
|
+
const client = new QwenOAuth2Client();
|
|
1704
|
+
const mockResponse = {
|
|
1705
|
+
ok: true,
|
|
1706
|
+
json: async () => ({
|
|
1707
|
+
device_code: 'test-device-code',
|
|
1708
|
+
user_code: 'TEST123',
|
|
1709
|
+
verification_uri: 'https://chat.qwen.ai/device',
|
|
1710
|
+
verification_uri_complete: 'https://chat.qwen.ai/device?code=TEST123',
|
|
1711
|
+
expires_in: 1800,
|
|
1712
|
+
}),
|
|
1713
|
+
};
|
|
1714
|
+
global.fetch = vi.fn().mockResolvedValue(mockResponse);
|
|
1715
|
+
await client.requestDeviceAuthorization({
|
|
1716
|
+
scope: 'openid profile email model.completion',
|
|
1717
|
+
code_challenge: 'test-challenge',
|
|
1718
|
+
code_challenge_method: 'S256',
|
|
1719
|
+
});
|
|
1720
|
+
const [, options] = vi.mocked(global.fetch).mock.calls[0];
|
|
1721
|
+
expect(options?.body).toContain('scope=openid%20profile%20email%20model.completion');
|
|
1722
|
+
});
|
|
1723
|
+
});
|
|
1724
|
+
//# sourceMappingURL=qwenOAuth2.test.js.map
|