@wingman-ai/gateway 0.1.0
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/.wingman/agents/README.md +161 -0
- package/.wingman/agents/coding/agent.md +147 -0
- package/.wingman/agents/coding/implementor.md +56 -0
- package/.wingman/agents/main/agent.md +19 -0
- package/.wingman/agents/researcher/agent.md +62 -0
- package/.wingman/agents/stock-trader/agent.md +223 -0
- package/.wingman/agents/stock-trader/chain-curator.md +24 -0
- package/.wingman/agents/stock-trader/goal-translator.md +42 -0
- package/.wingman/agents/stock-trader/guardrails-veto.md +11 -0
- package/.wingman/agents/stock-trader/path-planner.md +23 -0
- package/.wingman/agents/stock-trader/regime-analyst.md +15 -0
- package/.wingman/agents/stock-trader/risk.md +20 -0
- package/.wingman/agents/stock-trader/selection.md +22 -0
- package/.wingman/agents/stock-trader/strategy-composer.md +38 -0
- package/.wingman/agents/wingman/agent.json +12 -0
- package/bin/wingman +7 -0
- package/dist/agent/config/agentConfig.cjs +95 -0
- package/dist/agent/config/agentConfig.d.ts +187 -0
- package/dist/agent/config/agentConfig.js +52 -0
- package/dist/agent/config/agentLoader.cjs +242 -0
- package/dist/agent/config/agentLoader.d.ts +42 -0
- package/dist/agent/config/agentLoader.js +208 -0
- package/dist/agent/config/mcpClientManager.cjs +168 -0
- package/dist/agent/config/mcpClientManager.d.ts +41 -0
- package/dist/agent/config/mcpClientManager.js +134 -0
- package/dist/agent/config/modelFactory.cjs +175 -0
- package/dist/agent/config/modelFactory.d.ts +33 -0
- package/dist/agent/config/modelFactory.js +141 -0
- package/dist/agent/config/toolRegistry.cjs +111 -0
- package/dist/agent/config/toolRegistry.d.ts +25 -0
- package/dist/agent/config/toolRegistry.js +71 -0
- package/dist/agent/middleware/additional-messages.cjs +60 -0
- package/dist/agent/middleware/additional-messages.d.ts +7 -0
- package/dist/agent/middleware/additional-messages.js +26 -0
- package/dist/agent/middleware/hooks/executor.cjs +137 -0
- package/dist/agent/middleware/hooks/executor.d.ts +52 -0
- package/dist/agent/middleware/hooks/executor.js +103 -0
- package/dist/agent/middleware/hooks/input-builder.cjs +55 -0
- package/dist/agent/middleware/hooks/input-builder.d.ts +15 -0
- package/dist/agent/middleware/hooks/input-builder.js +21 -0
- package/dist/agent/middleware/hooks/matcher.cjs +59 -0
- package/dist/agent/middleware/hooks/matcher.d.ts +27 -0
- package/dist/agent/middleware/hooks/matcher.js +22 -0
- package/dist/agent/middleware/hooks/merger.cjs +54 -0
- package/dist/agent/middleware/hooks/merger.d.ts +18 -0
- package/dist/agent/middleware/hooks/merger.js +20 -0
- package/dist/agent/middleware/hooks/types.cjs +62 -0
- package/dist/agent/middleware/hooks/types.d.ts +82 -0
- package/dist/agent/middleware/hooks/types.js +19 -0
- package/dist/agent/middleware/hooks.cjs +79 -0
- package/dist/agent/middleware/hooks.d.ts +19 -0
- package/dist/agent/middleware/hooks.js +45 -0
- package/dist/agent/middleware/media-compat.cjs +80 -0
- package/dist/agent/middleware/media-compat.d.ts +7 -0
- package/dist/agent/middleware/media-compat.js +46 -0
- package/dist/agent/tests/agentConfig.test.cjs +132 -0
- package/dist/agent/tests/agentConfig.test.d.ts +1 -0
- package/dist/agent/tests/agentConfig.test.js +126 -0
- package/dist/agent/tests/agentLoader.test.cjs +235 -0
- package/dist/agent/tests/agentLoader.test.d.ts +1 -0
- package/dist/agent/tests/agentLoader.test.js +229 -0
- package/dist/agent/tests/modelFactory.test.cjs +114 -0
- package/dist/agent/tests/modelFactory.test.d.ts +1 -0
- package/dist/agent/tests/modelFactory.test.js +108 -0
- package/dist/agent/tests/test-agent-loader.cjs +33 -0
- package/dist/agent/tests/test-agent-loader.d.ts +1 -0
- package/dist/agent/tests/test-agent-loader.js +27 -0
- package/dist/agent/tests/test-subagent-loading.cjs +99 -0
- package/dist/agent/tests/test-subagent-loading.d.ts +1 -0
- package/dist/agent/tests/test-subagent-loading.js +93 -0
- package/dist/agent/tests/toolRegistry.test.cjs +109 -0
- package/dist/agent/tests/toolRegistry.test.d.ts +1 -0
- package/dist/agent/tests/toolRegistry.test.js +103 -0
- package/dist/agent/tools/code_search.cjs +108 -0
- package/dist/agent/tools/code_search.d.ts +24 -0
- package/dist/agent/tools/code_search.js +74 -0
- package/dist/agent/tools/command_execute.cjs +136 -0
- package/dist/agent/tools/command_execute.d.ts +12 -0
- package/dist/agent/tools/command_execute.js +99 -0
- package/dist/agent/tools/git_status.cjs +126 -0
- package/dist/agent/tools/git_status.d.ts +15 -0
- package/dist/agent/tools/git_status.js +92 -0
- package/dist/agent/tools/internet_search.cjs +93 -0
- package/dist/agent/tools/internet_search.d.ts +25 -0
- package/dist/agent/tools/internet_search.js +56 -0
- package/dist/agent/tools/think.cjs +53 -0
- package/dist/agent/tools/think.d.ts +26 -0
- package/dist/agent/tools/think.js +16 -0
- package/dist/agent/tools/web_crawler.cjs +180 -0
- package/dist/agent/tools/web_crawler.d.ts +31 -0
- package/dist/agent/tools/web_crawler.js +143 -0
- package/dist/agent/utils.cjs +54 -0
- package/dist/agent/utils.d.ts +1 -0
- package/dist/agent/utils.js +10 -0
- package/dist/cli/commands/agent.cjs +169 -0
- package/dist/cli/commands/agent.d.ts +15 -0
- package/dist/cli/commands/agent.js +125 -0
- package/dist/cli/commands/gateway.cjs +601 -0
- package/dist/cli/commands/gateway.d.ts +12 -0
- package/dist/cli/commands/gateway.js +567 -0
- package/dist/cli/commands/init.cjs +681 -0
- package/dist/cli/commands/init.d.ts +10 -0
- package/dist/cli/commands/init.js +634 -0
- package/dist/cli/commands/provider.cjs +208 -0
- package/dist/cli/commands/provider.d.ts +5 -0
- package/dist/cli/commands/provider.js +174 -0
- package/dist/cli/commands/skill.cjs +145 -0
- package/dist/cli/commands/skill.d.ts +10 -0
- package/dist/cli/commands/skill.js +111 -0
- package/dist/cli/config/loader.cjs +143 -0
- package/dist/cli/config/loader.d.ts +14 -0
- package/dist/cli/config/loader.js +109 -0
- package/dist/cli/config/schema.cjs +262 -0
- package/dist/cli/config/schema.d.ts +268 -0
- package/dist/cli/config/schema.js +213 -0
- package/dist/cli/core/agentInvoker.cjs +284 -0
- package/dist/cli/core/agentInvoker.d.ts +77 -0
- package/dist/cli/core/agentInvoker.js +247 -0
- package/dist/cli/core/commandHandler.cjs +257 -0
- package/dist/cli/core/commandHandler.d.ts +62 -0
- package/dist/cli/core/commandHandler.js +223 -0
- package/dist/cli/core/database/bunSqliteAdapter.cjs +87 -0
- package/dist/cli/core/database/bunSqliteAdapter.d.ts +34 -0
- package/dist/cli/core/database/bunSqliteAdapter.js +53 -0
- package/dist/cli/core/loggerBridge.cjs +42 -0
- package/dist/cli/core/loggerBridge.d.ts +8 -0
- package/dist/cli/core/loggerBridge.js +8 -0
- package/dist/cli/core/outputManager.cjs +106 -0
- package/dist/cli/core/outputManager.d.ts +43 -0
- package/dist/cli/core/outputManager.js +72 -0
- package/dist/cli/core/sessionManager.cjs +535 -0
- package/dist/cli/core/sessionManager.d.ts +111 -0
- package/dist/cli/core/sessionManager.js +486 -0
- package/dist/cli/core/streamParser.cjs +328 -0
- package/dist/cli/core/streamParser.d.ts +42 -0
- package/dist/cli/core/streamParser.js +288 -0
- package/dist/cli/index.cjs +211 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +205 -0
- package/dist/cli/services/skillRepository.cjs +178 -0
- package/dist/cli/services/skillRepository.d.ts +35 -0
- package/dist/cli/services/skillRepository.js +144 -0
- package/dist/cli/services/skillService.cjs +336 -0
- package/dist/cli/services/skillService.d.ts +48 -0
- package/dist/cli/services/skillService.js +302 -0
- package/dist/cli/types/gateway.cjs +18 -0
- package/dist/cli/types/gateway.d.ts +18 -0
- package/dist/cli/types/gateway.js +0 -0
- package/dist/cli/types/init.cjs +18 -0
- package/dist/cli/types/init.d.ts +13 -0
- package/dist/cli/types/init.js +0 -0
- package/dist/cli/types/provider.cjs +18 -0
- package/dist/cli/types/provider.d.ts +9 -0
- package/dist/cli/types/provider.js +0 -0
- package/dist/cli/types/skill.cjs +18 -0
- package/dist/cli/types/skill.d.ts +71 -0
- package/dist/cli/types/skill.js +0 -0
- package/dist/cli/types.cjs +18 -0
- package/dist/cli/types.d.ts +175 -0
- package/dist/cli/types.js +0 -0
- package/dist/cli/ui/AgentOutput.cjs +82 -0
- package/dist/cli/ui/AgentOutput.d.ts +8 -0
- package/dist/cli/ui/AgentOutput.js +38 -0
- package/dist/cli/ui/App.cjs +285 -0
- package/dist/cli/ui/App.d.ts +6 -0
- package/dist/cli/ui/App.js +241 -0
- package/dist/cli/ui/ErrorDisplay.cjs +65 -0
- package/dist/cli/ui/ErrorDisplay.d.ts +8 -0
- package/dist/cli/ui/ErrorDisplay.js +21 -0
- package/dist/cli/ui/LogDisplay.cjs +74 -0
- package/dist/cli/ui/LogDisplay.d.ts +13 -0
- package/dist/cli/ui/LogDisplay.js +30 -0
- package/dist/cli/ui/SessionListDisplay.cjs +135 -0
- package/dist/cli/ui/SessionListDisplay.d.ts +9 -0
- package/dist/cli/ui/SessionListDisplay.js +91 -0
- package/dist/cli/ui/blockHelpers.cjs +80 -0
- package/dist/cli/ui/blockHelpers.d.ts +21 -0
- package/dist/cli/ui/blockHelpers.js +40 -0
- package/dist/cli/ui/components/ToolCallDisplay.cjs +207 -0
- package/dist/cli/ui/components/ToolCallDisplay.d.ts +7 -0
- package/dist/cli/ui/components/ToolCallDisplay.js +162 -0
- package/dist/cli/ui/components/ToolResultDisplay.cjs +86 -0
- package/dist/cli/ui/components/ToolResultDisplay.d.ts +8 -0
- package/dist/cli/ui/components/ToolResultDisplay.js +42 -0
- package/dist/cli/ui/toolDisplayHelpers.cjs +112 -0
- package/dist/cli/ui/toolDisplayHelpers.d.ts +3 -0
- package/dist/cli/ui/toolDisplayHelpers.js +72 -0
- package/dist/gateway/adapters/discord.cjs +298 -0
- package/dist/gateway/adapters/discord.d.ts +42 -0
- package/dist/gateway/adapters/discord.js +246 -0
- package/dist/gateway/auth.cjs +94 -0
- package/dist/gateway/auth.d.ts +36 -0
- package/dist/gateway/auth.js +60 -0
- package/dist/gateway/broadcast.cjs +131 -0
- package/dist/gateway/broadcast.d.ts +76 -0
- package/dist/gateway/broadcast.js +97 -0
- package/dist/gateway/client.cjs +282 -0
- package/dist/gateway/client.d.ts +141 -0
- package/dist/gateway/client.js +248 -0
- package/dist/gateway/daemon.cjs +195 -0
- package/dist/gateway/daemon.d.ts +67 -0
- package/dist/gateway/daemon.js +161 -0
- package/dist/gateway/discovery/index.cjs +72 -0
- package/dist/gateway/discovery/index.d.ts +3 -0
- package/dist/gateway/discovery/index.js +3 -0
- package/dist/gateway/discovery/mdns.cjs +221 -0
- package/dist/gateway/discovery/mdns.d.ts +37 -0
- package/dist/gateway/discovery/mdns.js +177 -0
- package/dist/gateway/discovery/tailscale.cjs +140 -0
- package/dist/gateway/discovery/tailscale.d.ts +31 -0
- package/dist/gateway/discovery/tailscale.js +106 -0
- package/dist/gateway/discovery/types.cjs +18 -0
- package/dist/gateway/discovery/types.d.ts +41 -0
- package/dist/gateway/discovery/types.js +0 -0
- package/dist/gateway/env.cjs +45 -0
- package/dist/gateway/env.d.ts +2 -0
- package/dist/gateway/env.js +8 -0
- package/dist/gateway/hooks/loader.cjs +137 -0
- package/dist/gateway/hooks/loader.d.ts +10 -0
- package/dist/gateway/hooks/loader.js +103 -0
- package/dist/gateway/hooks/registry.cjs +128 -0
- package/dist/gateway/hooks/registry.d.ts +13 -0
- package/dist/gateway/hooks/registry.js +94 -0
- package/dist/gateway/hooks/types.cjs +58 -0
- package/dist/gateway/hooks/types.d.ts +50 -0
- package/dist/gateway/hooks/types.js +18 -0
- package/dist/gateway/http/agents.cjs +280 -0
- package/dist/gateway/http/agents.d.ts +2 -0
- package/dist/gateway/http/agents.js +246 -0
- package/dist/gateway/http/fs.cjs +81 -0
- package/dist/gateway/http/fs.d.ts +2 -0
- package/dist/gateway/http/fs.js +47 -0
- package/dist/gateway/http/providers.cjs +120 -0
- package/dist/gateway/http/providers.d.ts +2 -0
- package/dist/gateway/http/providers.js +86 -0
- package/dist/gateway/http/routines.cjs +196 -0
- package/dist/gateway/http/routines.d.ts +20 -0
- package/dist/gateway/http/routines.js +159 -0
- package/dist/gateway/http/sessions.cjs +241 -0
- package/dist/gateway/http/sessions.d.ts +2 -0
- package/dist/gateway/http/sessions.js +207 -0
- package/dist/gateway/http/types.cjs +18 -0
- package/dist/gateway/http/types.d.ts +25 -0
- package/dist/gateway/http/types.js +0 -0
- package/dist/gateway/http/voice.cjs +167 -0
- package/dist/gateway/http/voice.d.ts +2 -0
- package/dist/gateway/http/voice.js +133 -0
- package/dist/gateway/http/webhooks.cjs +353 -0
- package/dist/gateway/http/webhooks.d.ts +22 -0
- package/dist/gateway/http/webhooks.js +313 -0
- package/dist/gateway/index.cjs +119 -0
- package/dist/gateway/index.d.ts +8 -0
- package/dist/gateway/index.js +9 -0
- package/dist/gateway/node.cjs +218 -0
- package/dist/gateway/node.d.ts +112 -0
- package/dist/gateway/node.js +184 -0
- package/dist/gateway/router.cjs +85 -0
- package/dist/gateway/router.d.ts +9 -0
- package/dist/gateway/router.js +51 -0
- package/dist/gateway/rpcClient.cjs +152 -0
- package/dist/gateway/rpcClient.d.ts +24 -0
- package/dist/gateway/rpcClient.js +118 -0
- package/dist/gateway/server.cjs +1175 -0
- package/dist/gateway/server.d.ts +185 -0
- package/dist/gateway/server.js +1138 -0
- package/dist/gateway/transport/http.cjs +153 -0
- package/dist/gateway/transport/http.d.ts +25 -0
- package/dist/gateway/transport/http.js +119 -0
- package/dist/gateway/transport/index.cjs +40 -0
- package/dist/gateway/transport/index.d.ts +3 -0
- package/dist/gateway/transport/index.js +3 -0
- package/dist/gateway/transport/types.cjs +18 -0
- package/dist/gateway/transport/types.d.ts +59 -0
- package/dist/gateway/transport/types.js +0 -0
- package/dist/gateway/transport/websocket.cjs +132 -0
- package/dist/gateway/transport/websocket.d.ts +21 -0
- package/dist/gateway/transport/websocket.js +98 -0
- package/dist/gateway/types.cjs +18 -0
- package/dist/gateway/types.d.ts +215 -0
- package/dist/gateway/types.js +0 -0
- package/dist/gateway/validation.cjs +225 -0
- package/dist/gateway/validation.d.ts +157 -0
- package/dist/gateway/validation.js +158 -0
- package/dist/index.cjs +95 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/logger.cjs +270 -0
- package/dist/logger.d.ts +54 -0
- package/dist/logger.js +215 -0
- package/dist/providers/copilot.cjs +148 -0
- package/dist/providers/copilot.d.ts +3 -0
- package/dist/providers/copilot.js +114 -0
- package/dist/providers/credentials.cjs +154 -0
- package/dist/providers/credentials.d.ts +26 -0
- package/dist/providers/credentials.js +99 -0
- package/dist/providers/oauth.cjs +279 -0
- package/dist/providers/oauth.d.ts +13 -0
- package/dist/providers/oauth.js +245 -0
- package/dist/providers/registry.cjs +138 -0
- package/dist/providers/registry.d.ts +32 -0
- package/dist/providers/registry.js +98 -0
- package/dist/tests/additionalMessageMiddleware.test.cjs +45 -0
- package/dist/tests/additionalMessageMiddleware.test.d.ts +1 -0
- package/dist/tests/additionalMessageMiddleware.test.js +39 -0
- package/dist/tests/agent-config-voice.test.cjs +25 -0
- package/dist/tests/agent-config-voice.test.d.ts +1 -0
- package/dist/tests/agent-config-voice.test.js +19 -0
- package/dist/tests/agentInvokerAttachments.test.cjs +67 -0
- package/dist/tests/agentInvokerAttachments.test.d.ts +1 -0
- package/dist/tests/agentInvokerAttachments.test.js +61 -0
- package/dist/tests/attachments-utils.test.cjs +46 -0
- package/dist/tests/attachments-utils.test.d.ts +1 -0
- package/dist/tests/attachments-utils.test.js +40 -0
- package/dist/tests/bunSqliteAdapter.test.cjs +265 -0
- package/dist/tests/bunSqliteAdapter.test.d.ts +1 -0
- package/dist/tests/bunSqliteAdapter.test.js +259 -0
- package/dist/tests/candleRange.test.cjs +48 -0
- package/dist/tests/candleRange.test.d.ts +1 -0
- package/dist/tests/candleRange.test.js +42 -0
- package/dist/tests/cli-config-loader.test.cjs +364 -0
- package/dist/tests/cli-config-loader.test.d.ts +1 -0
- package/dist/tests/cli-config-loader.test.js +358 -0
- package/dist/tests/cli-init.test.cjs +82 -0
- package/dist/tests/cli-init.test.d.ts +1 -0
- package/dist/tests/cli-init.test.js +76 -0
- package/dist/tests/discord-adapter.test.cjs +55 -0
- package/dist/tests/discord-adapter.test.d.ts +1 -0
- package/dist/tests/discord-adapter.test.js +49 -0
- package/dist/tests/gateway.test.cjs +319 -0
- package/dist/tests/gateway.test.d.ts +1 -0
- package/dist/tests/gateway.test.js +313 -0
- package/dist/tests/hooks-matcher.test.cjs +309 -0
- package/dist/tests/hooks-matcher.test.d.ts +1 -0
- package/dist/tests/hooks-matcher.test.js +303 -0
- package/dist/tests/hooks-merger.test.cjs +528 -0
- package/dist/tests/hooks-merger.test.d.ts +1 -0
- package/dist/tests/hooks-merger.test.js +522 -0
- package/dist/tests/integration/agent-invocation.integration.test.cjs +264 -0
- package/dist/tests/integration/agent-invocation.integration.test.d.ts +1 -0
- package/dist/tests/integration/agent-invocation.integration.test.js +258 -0
- package/dist/tests/integration/finnhub-candles.integration.test.cjs +98 -0
- package/dist/tests/integration/finnhub-candles.integration.test.d.ts +1 -0
- package/dist/tests/integration/finnhub-candles.integration.test.js +92 -0
- package/dist/tests/logger.test.cjs +353 -0
- package/dist/tests/logger.test.d.ts +1 -0
- package/dist/tests/logger.test.js +347 -0
- package/dist/tests/mediaCompatibilityMiddleware.test.cjs +106 -0
- package/dist/tests/mediaCompatibilityMiddleware.test.d.ts +1 -0
- package/dist/tests/mediaCompatibilityMiddleware.test.js +100 -0
- package/dist/tests/routines-api.test.cjs +107 -0
- package/dist/tests/routines-api.test.d.ts +1 -0
- package/dist/tests/routines-api.test.js +101 -0
- package/dist/tests/sessionMessageAttachments.test.cjs +108 -0
- package/dist/tests/sessionMessageAttachments.test.d.ts +1 -0
- package/dist/tests/sessionMessageAttachments.test.js +102 -0
- package/dist/tests/sessionMessageRole.test.cjs +44 -0
- package/dist/tests/sessionMessageRole.test.d.ts +1 -0
- package/dist/tests/sessionMessageRole.test.js +38 -0
- package/dist/tests/sessionStateMessages.test.cjs +72 -0
- package/dist/tests/sessionStateMessages.test.d.ts +1 -0
- package/dist/tests/sessionStateMessages.test.js +66 -0
- package/dist/tests/sessions-api.test.cjs +68 -0
- package/dist/tests/sessions-api.test.d.ts +1 -0
- package/dist/tests/sessions-api.test.js +62 -0
- package/dist/tests/technicalIndicators.test.cjs +82 -0
- package/dist/tests/technicalIndicators.test.d.ts +1 -0
- package/dist/tests/technicalIndicators.test.js +76 -0
- package/dist/tests/toolDisplayHelpers.test.cjs +43 -0
- package/dist/tests/toolDisplayHelpers.test.d.ts +1 -0
- package/dist/tests/toolDisplayHelpers.test.js +37 -0
- package/dist/tests/voice-config.test.cjs +35 -0
- package/dist/tests/voice-config.test.d.ts +1 -0
- package/dist/tests/voice-config.test.js +29 -0
- package/dist/tests/yahooCandles.test.cjs +111 -0
- package/dist/tests/yahooCandles.test.d.ts +1 -0
- package/dist/tests/yahooCandles.test.js +105 -0
- package/dist/tools/finance/candleRange.cjs +71 -0
- package/dist/tools/finance/candleRange.d.ts +21 -0
- package/dist/tools/finance/candleRange.js +28 -0
- package/dist/tools/finance/optionsAnalytics.cjs +222 -0
- package/dist/tools/finance/optionsAnalytics.d.ts +44 -0
- package/dist/tools/finance/optionsAnalytics.js +188 -0
- package/dist/tools/finance/optionsAnalytics.test.cjs +128 -0
- package/dist/tools/finance/optionsAnalytics.test.d.ts +1 -0
- package/dist/tools/finance/optionsAnalytics.test.js +122 -0
- package/dist/tools/finance/technicalIndicators.cjs +111 -0
- package/dist/tools/finance/technicalIndicators.d.ts +15 -0
- package/dist/tools/finance/technicalIndicators.js +68 -0
- package/dist/tools/finance/yahooCandles.cjs +125 -0
- package/dist/tools/finance/yahooCandles.d.ts +41 -0
- package/dist/tools/finance/yahooCandles.js +85 -0
- package/dist/tools/mcp-finance.cjs +649 -0
- package/dist/tools/mcp-finance.d.ts +1 -0
- package/dist/tools/mcp-finance.js +631 -0
- package/dist/types/agents.cjs +18 -0
- package/dist/types/agents.d.ts +11 -0
- package/dist/types/agents.js +0 -0
- package/dist/types/hooks.cjs +18 -0
- package/dist/types/hooks.d.ts +82 -0
- package/dist/types/hooks.js +0 -0
- package/dist/types/mcp.cjs +86 -0
- package/dist/types/mcp.d.ts +107 -0
- package/dist/types/mcp.js +40 -0
- package/dist/types/voice.cjs +103 -0
- package/dist/types/voice.d.ts +117 -0
- package/dist/types/voice.js +51 -0
- package/dist/utils/attachments.cjs +46 -0
- package/dist/utils/attachments.d.ts +7 -0
- package/dist/utils/attachments.js +12 -0
- package/dist/voice/config.cjs +52 -0
- package/dist/voice/config.d.ts +8 -0
- package/dist/voice/config.js +18 -0
- package/dist/webui/assets/index-BA0HaStz.css +1 -0
- package/dist/webui/assets/index-NHgTZsWN.js +112 -0
- package/dist/webui/assets/wingman_icon-DOy91UEF.webp +0 -0
- package/dist/webui/assets/wingman_logo-Cogyt3qm.webp +0 -0
- package/dist/webui/index.html +19 -0
- package/package.json +130 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getProviderSpec } from "./registry.js";
|
|
5
|
+
const CREDENTIALS_VERSION = 1;
|
|
6
|
+
const CREDENTIALS_DIR = join(homedir(), ".wingman");
|
|
7
|
+
const CREDENTIALS_PATH = join(CREDENTIALS_DIR, "credentials.json");
|
|
8
|
+
function getCredentialsPath() {
|
|
9
|
+
return CREDENTIALS_PATH;
|
|
10
|
+
}
|
|
11
|
+
function emptyCredentials() {
|
|
12
|
+
return {
|
|
13
|
+
version: CREDENTIALS_VERSION,
|
|
14
|
+
updatedAt: new Date().toISOString(),
|
|
15
|
+
providers: {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function readCredentialsFile() {
|
|
19
|
+
if (!existsSync(CREDENTIALS_PATH)) return emptyCredentials();
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(CREDENTIALS_PATH, "utf-8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
const providers = parsed && "object" == typeof parsed.providers && parsed.providers ? parsed.providers : {};
|
|
24
|
+
return {
|
|
25
|
+
version: "number" == typeof parsed?.version ? parsed.version : CREDENTIALS_VERSION,
|
|
26
|
+
updatedAt: "string" == typeof parsed?.updatedAt ? parsed.updatedAt : new Date().toISOString(),
|
|
27
|
+
providers
|
|
28
|
+
};
|
|
29
|
+
} catch {
|
|
30
|
+
return emptyCredentials();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function writeCredentialsFile(data) {
|
|
34
|
+
mkdirSync(CREDENTIALS_DIR, {
|
|
35
|
+
recursive: true
|
|
36
|
+
});
|
|
37
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify(data, null, 2));
|
|
38
|
+
}
|
|
39
|
+
function getProviderCredentials(provider) {
|
|
40
|
+
const file = readCredentialsFile();
|
|
41
|
+
return file.providers[provider];
|
|
42
|
+
}
|
|
43
|
+
function setProviderCredentials(provider, credentials) {
|
|
44
|
+
const file = readCredentialsFile();
|
|
45
|
+
file.providers[provider] = {
|
|
46
|
+
...file.providers[provider] || {},
|
|
47
|
+
...credentials
|
|
48
|
+
};
|
|
49
|
+
file.updatedAt = new Date().toISOString();
|
|
50
|
+
writeCredentialsFile(file);
|
|
51
|
+
}
|
|
52
|
+
function deleteProviderCredentials(provider) {
|
|
53
|
+
const file = readCredentialsFile();
|
|
54
|
+
if (!file.providers[provider]) return false;
|
|
55
|
+
delete file.providers[provider];
|
|
56
|
+
file.updatedAt = new Date().toISOString();
|
|
57
|
+
writeCredentialsFile(file);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
function saveProviderToken(providerName, token) {
|
|
61
|
+
const provider = getProviderSpec(providerName);
|
|
62
|
+
if (!provider) throw new Error(`Unknown provider: "${providerName}"`);
|
|
63
|
+
const normalized = token.trim();
|
|
64
|
+
if (!normalized) throw new Error("Token is required");
|
|
65
|
+
const existing = getProviderCredentials(provider.name) || {};
|
|
66
|
+
const updated = {
|
|
67
|
+
...existing
|
|
68
|
+
};
|
|
69
|
+
if ("oauth" === provider.type) {
|
|
70
|
+
updated.accessToken = normalized;
|
|
71
|
+
updated.tokenType = "bearer";
|
|
72
|
+
} else updated.apiKey = normalized;
|
|
73
|
+
setProviderCredentials(provider.name, updated);
|
|
74
|
+
return updated;
|
|
75
|
+
}
|
|
76
|
+
function resolveProviderToken(providerName) {
|
|
77
|
+
const provider = getProviderSpec(providerName);
|
|
78
|
+
if (!provider) return {
|
|
79
|
+
source: "missing"
|
|
80
|
+
};
|
|
81
|
+
for (const envVar of provider.envVars){
|
|
82
|
+
const value = process.env[envVar];
|
|
83
|
+
if (value && value.trim()) return {
|
|
84
|
+
token: value.trim(),
|
|
85
|
+
source: "env",
|
|
86
|
+
envVar
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const credentials = getProviderCredentials(provider.name);
|
|
90
|
+
const token = credentials?.accessToken ?? credentials?.apiKey ?? credentials?.refreshToken;
|
|
91
|
+
if (token && token.trim()) return {
|
|
92
|
+
token: token.trim(),
|
|
93
|
+
source: "credentials"
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
source: "missing"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export { deleteProviderCredentials, getCredentialsPath, getProviderCredentials, readCredentialsFile, resolveProviderToken, saveProviderToken, setProviderCredentials, writeCredentialsFile };
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
loginWithLocalCallback: ()=>loginWithLocalCallback
|
|
28
|
+
});
|
|
29
|
+
const external_node_http_namespaceObject = require("node:http");
|
|
30
|
+
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
31
|
+
const external_node_child_process_namespaceObject = require("node:child_process");
|
|
32
|
+
const external_node_url_namespaceObject = require("node:url");
|
|
33
|
+
const external_registry_cjs_namespaceObject = require("./registry.cjs");
|
|
34
|
+
const DEFAULT_TIMEOUT_MS = 300000;
|
|
35
|
+
async function loginWithLocalCallback(providerName, options = {}) {
|
|
36
|
+
const provider = (0, external_registry_cjs_namespaceObject.getProviderSpec)(providerName);
|
|
37
|
+
if (!provider || !provider.oauth) throw new Error(`OAuth is not configured for provider "${providerName}"`);
|
|
38
|
+
const session = await createOAuthSession(provider.oauth, options);
|
|
39
|
+
const openBrowser = false !== options.openBrowser;
|
|
40
|
+
if (openBrowser) try {
|
|
41
|
+
await launchBrowser(session.authorizationUrl);
|
|
42
|
+
} catch {
|
|
43
|
+
options.onMessage?.("Unable to auto-open browser.");
|
|
44
|
+
}
|
|
45
|
+
options.onMessage?.(`Open this URL to continue:\n${session.authorizationUrl}`);
|
|
46
|
+
let callback;
|
|
47
|
+
try {
|
|
48
|
+
callback = await session.waitForCallback;
|
|
49
|
+
} finally{
|
|
50
|
+
session.close();
|
|
51
|
+
}
|
|
52
|
+
const token = await exchangeCodeForToken(provider.name, provider.oauth, {
|
|
53
|
+
code: callback.code,
|
|
54
|
+
codeVerifier: session.codeVerifier,
|
|
55
|
+
redirectUri: session.redirectUri,
|
|
56
|
+
clientId: resolveClientId(provider.oauth, options),
|
|
57
|
+
clientSecret: resolveClientSecret(provider.oauth, options)
|
|
58
|
+
});
|
|
59
|
+
return token;
|
|
60
|
+
}
|
|
61
|
+
async function createOAuthSession(oauth, options) {
|
|
62
|
+
const redirectHost = options.redirectHost || "127.0.0.1";
|
|
63
|
+
const redirectPort = options.redirectPort ?? 53682;
|
|
64
|
+
const redirectPath = oauth.redirectPath || "/oauth/callback";
|
|
65
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
66
|
+
const usePkce = false !== oauth.usePkce;
|
|
67
|
+
const state = base64Url((0, external_node_crypto_namespaceObject.randomBytes)(16));
|
|
68
|
+
const codeVerifier = usePkce ? base64Url((0, external_node_crypto_namespaceObject.randomBytes)(32)) : "";
|
|
69
|
+
const codeChallenge = usePkce ? base64Url((0, external_node_crypto_namespaceObject.createHash)("sha256").update(codeVerifier).digest()) : "";
|
|
70
|
+
const server = (0, external_node_http_namespaceObject.createServer)();
|
|
71
|
+
const waitForCallback = new Promise((resolve, reject)=>{
|
|
72
|
+
const timer = setTimeout(()=>{
|
|
73
|
+
reject(new Error("Login timed out. Please try again."));
|
|
74
|
+
server.close();
|
|
75
|
+
}, timeoutMs);
|
|
76
|
+
server.on("request", (req, res)=>{
|
|
77
|
+
if (!req.url) {
|
|
78
|
+
res.statusCode = 400;
|
|
79
|
+
res.end("Missing request URL.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const url = new external_node_url_namespaceObject.URL(req.url, `http://${redirectHost}`);
|
|
83
|
+
if (url.pathname !== redirectPath) {
|
|
84
|
+
res.statusCode = 404;
|
|
85
|
+
res.end("Not found.");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const error = url.searchParams.get("error");
|
|
89
|
+
if (error) {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
const errorDescription = url.searchParams.get("error_description") || error;
|
|
92
|
+
res.statusCode = 400;
|
|
93
|
+
res.end(`Login failed: ${errorDescription}`);
|
|
94
|
+
reject(new Error(`Login failed: ${errorDescription}`));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const code = url.searchParams.get("code");
|
|
98
|
+
const callbackState = url.searchParams.get("state");
|
|
99
|
+
if (!code || !callbackState) {
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
res.statusCode = 400;
|
|
102
|
+
res.end("Missing authorization response.");
|
|
103
|
+
reject(new Error("Missing authorization response."));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (callbackState !== state) {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
res.statusCode = 400;
|
|
109
|
+
res.end("State mismatch.");
|
|
110
|
+
reject(new Error("State mismatch."));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
res.statusCode = 200;
|
|
115
|
+
res.end("Login complete. You can close this window.");
|
|
116
|
+
resolve({
|
|
117
|
+
code,
|
|
118
|
+
state: callbackState
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
await new Promise((resolve, reject)=>{
|
|
124
|
+
server.listen(redirectPort, redirectHost, ()=>resolve());
|
|
125
|
+
server.on("error", reject);
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
129
|
+
throw new Error(`Failed to start local callback server on ${redirectHost}:${redirectPort}. ${message}. Use --redirect-port to choose a different port.`);
|
|
130
|
+
}
|
|
131
|
+
const address = server.address();
|
|
132
|
+
const redirectUri = `http://${redirectHost}:${address.port}${redirectPath}`;
|
|
133
|
+
const authorizationUrl = buildAuthorizationUrl(oauth, {
|
|
134
|
+
clientId: resolveClientId(oauth, options),
|
|
135
|
+
redirectUri,
|
|
136
|
+
state,
|
|
137
|
+
codeChallenge,
|
|
138
|
+
scopes: resolveScopes(oauth, options),
|
|
139
|
+
usePkce
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
authorizationUrl,
|
|
143
|
+
redirectUri,
|
|
144
|
+
waitForCallback,
|
|
145
|
+
close: ()=>server.close(),
|
|
146
|
+
codeVerifier
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function resolveClientId(oauth, options) {
|
|
150
|
+
const value = options.clientId || resolveEnv(oauth.clientIdEnv) || oauth.defaultClientId;
|
|
151
|
+
if (!value) {
|
|
152
|
+
const envList = oauth.clientIdEnv.join(", ");
|
|
153
|
+
throw new Error(`Missing OAuth client ID. Set ${envList} or pass --client-id.`);
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
function resolveClientSecret(oauth, options) {
|
|
158
|
+
const value = options.clientSecret || resolveEnv(oauth.clientSecretEnv || []) || oauth.defaultClientSecret;
|
|
159
|
+
return value || void 0;
|
|
160
|
+
}
|
|
161
|
+
function resolveScopes(oauth, options) {
|
|
162
|
+
if (options.scopes && options.scopes.length > 0) return options.scopes;
|
|
163
|
+
return oauth.scopes || [];
|
|
164
|
+
}
|
|
165
|
+
function buildAuthorizationUrl(oauth, input) {
|
|
166
|
+
const url = new external_node_url_namespaceObject.URL(oauth.authorizationUrl);
|
|
167
|
+
url.searchParams.set("client_id", input.clientId);
|
|
168
|
+
url.searchParams.set("redirect_uri", input.redirectUri);
|
|
169
|
+
url.searchParams.set("response_type", "code");
|
|
170
|
+
url.searchParams.set("state", input.state);
|
|
171
|
+
if (input.usePkce) {
|
|
172
|
+
url.searchParams.set("code_challenge", input.codeChallenge);
|
|
173
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
174
|
+
}
|
|
175
|
+
if (input.scopes.length > 0) url.searchParams.set("scope", input.scopes.join(oauth.scopeSeparator || " "));
|
|
176
|
+
if (oauth.authorizationParams) for (const [key, value] of Object.entries(oauth.authorizationParams))url.searchParams.set(key, value);
|
|
177
|
+
return url.toString();
|
|
178
|
+
}
|
|
179
|
+
async function exchangeCodeForToken(providerName, oauth, input) {
|
|
180
|
+
const body = new external_node_url_namespaceObject.URLSearchParams({
|
|
181
|
+
client_id: input.clientId,
|
|
182
|
+
code: input.code,
|
|
183
|
+
redirect_uri: input.redirectUri,
|
|
184
|
+
grant_type: "authorization_code"
|
|
185
|
+
});
|
|
186
|
+
if (false !== oauth.usePkce) body.set("code_verifier", input.codeVerifier);
|
|
187
|
+
if (input.clientSecret) body.set("client_secret", input.clientSecret);
|
|
188
|
+
if (oauth.tokenParams) for (const [key, value] of Object.entries(oauth.tokenParams))body.set(key, value);
|
|
189
|
+
const response = await fetch(oauth.tokenUrl, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
193
|
+
Accept: "application/json",
|
|
194
|
+
...oauth.tokenHeaders || {}
|
|
195
|
+
},
|
|
196
|
+
body: body.toString()
|
|
197
|
+
});
|
|
198
|
+
const contentType = response.headers.get("content-type") || "";
|
|
199
|
+
const rawText = await response.text();
|
|
200
|
+
let payload = {};
|
|
201
|
+
payload = "form" === oauth.tokenResponseType ? Object.fromEntries(new external_node_url_namespaceObject.URLSearchParams(rawText)) : "json" === oauth.tokenResponseType ? JSON.parse(rawText) : contentType.includes("application/json") ? JSON.parse(rawText) : Object.fromEntries(new external_node_url_namespaceObject.URLSearchParams(rawText));
|
|
202
|
+
if (!response.ok || payload.error) {
|
|
203
|
+
const description = payload.error_description || payload.error || rawText;
|
|
204
|
+
throw new Error(`Token exchange failed: ${description}`);
|
|
205
|
+
}
|
|
206
|
+
const credentials = mapTokenResponse(providerName, payload);
|
|
207
|
+
if (payload.expires_in) {
|
|
208
|
+
const expiresIn = Number(payload.expires_in);
|
|
209
|
+
if (!Number.isNaN(expiresIn)) credentials.expiresAt = new Date(Date.now() + 1000 * expiresIn).toISOString();
|
|
210
|
+
}
|
|
211
|
+
if (!credentials.expiresAt && payload.expires_at) {
|
|
212
|
+
const expiresAt = Number(payload.expires_at);
|
|
213
|
+
if (!Number.isNaN(expiresAt)) credentials.expiresAt = new Date(1000 * expiresAt).toISOString();
|
|
214
|
+
}
|
|
215
|
+
return credentials;
|
|
216
|
+
}
|
|
217
|
+
function mapTokenResponse(providerName, payload) {
|
|
218
|
+
const mapper = TOKEN_MAPPERS[providerName];
|
|
219
|
+
if (mapper) return mapper(payload);
|
|
220
|
+
const accessToken = payload.access_token;
|
|
221
|
+
if (!accessToken) throw new Error("Token exchange failed: missing access token.");
|
|
222
|
+
return {
|
|
223
|
+
accessToken,
|
|
224
|
+
refreshToken: payload.refresh_token,
|
|
225
|
+
tokenType: payload.token_type || "bearer"
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const TOKEN_MAPPERS = {
|
|
229
|
+
copilot: (payload)=>{
|
|
230
|
+
const accessToken = payload.access_token;
|
|
231
|
+
if (!accessToken) throw new Error("Token exchange failed: missing access token.");
|
|
232
|
+
return {
|
|
233
|
+
refreshToken: accessToken,
|
|
234
|
+
tokenType: payload.token_type || "bearer"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
function resolveEnv(envVars) {
|
|
239
|
+
for (const key of envVars){
|
|
240
|
+
const value = process.env[key];
|
|
241
|
+
if (value && value.trim()) return value.trim();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function base64Url(buffer) {
|
|
245
|
+
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
246
|
+
}
|
|
247
|
+
async function launchBrowser(url) {
|
|
248
|
+
const platform = process.platform;
|
|
249
|
+
if ("darwin" === platform) return void await spawnBrowser("open", [
|
|
250
|
+
url
|
|
251
|
+
]);
|
|
252
|
+
if ("win32" === platform) return void await spawnBrowser("cmd", [
|
|
253
|
+
"/c",
|
|
254
|
+
"start",
|
|
255
|
+
"",
|
|
256
|
+
url
|
|
257
|
+
]);
|
|
258
|
+
await spawnBrowser("xdg-open", [
|
|
259
|
+
url
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
function spawnBrowser(command, args) {
|
|
263
|
+
return new Promise((resolve, reject)=>{
|
|
264
|
+
const child = (0, external_node_child_process_namespaceObject.spawn)(command, args, {
|
|
265
|
+
stdio: "ignore",
|
|
266
|
+
detached: true
|
|
267
|
+
});
|
|
268
|
+
child.once("error", reject);
|
|
269
|
+
child.once("spawn", ()=>resolve());
|
|
270
|
+
child.unref();
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
exports.loginWithLocalCallback = __webpack_exports__.loginWithLocalCallback;
|
|
274
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
275
|
+
"loginWithLocalCallback"
|
|
276
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
277
|
+
Object.defineProperty(exports, '__esModule', {
|
|
278
|
+
value: true
|
|
279
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ProviderName } from "./registry.js";
|
|
2
|
+
import type { ProviderCredentials } from "./credentials.js";
|
|
3
|
+
export interface OAuthLoginOptions {
|
|
4
|
+
clientId?: string;
|
|
5
|
+
clientSecret?: string;
|
|
6
|
+
scopes?: string[];
|
|
7
|
+
redirectHost?: string;
|
|
8
|
+
redirectPort?: number;
|
|
9
|
+
openBrowser?: boolean;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
onMessage?: (message: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function loginWithLocalCallback(providerName: ProviderName, options?: OAuthLoginOptions): Promise<ProviderCredentials>;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { URL, URLSearchParams } from "node:url";
|
|
5
|
+
import { getProviderSpec } from "./registry.js";
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 300000;
|
|
7
|
+
async function loginWithLocalCallback(providerName, options = {}) {
|
|
8
|
+
const provider = getProviderSpec(providerName);
|
|
9
|
+
if (!provider || !provider.oauth) throw new Error(`OAuth is not configured for provider "${providerName}"`);
|
|
10
|
+
const session = await createOAuthSession(provider.oauth, options);
|
|
11
|
+
const openBrowser = false !== options.openBrowser;
|
|
12
|
+
if (openBrowser) try {
|
|
13
|
+
await launchBrowser(session.authorizationUrl);
|
|
14
|
+
} catch {
|
|
15
|
+
options.onMessage?.("Unable to auto-open browser.");
|
|
16
|
+
}
|
|
17
|
+
options.onMessage?.(`Open this URL to continue:\n${session.authorizationUrl}`);
|
|
18
|
+
let callback;
|
|
19
|
+
try {
|
|
20
|
+
callback = await session.waitForCallback;
|
|
21
|
+
} finally{
|
|
22
|
+
session.close();
|
|
23
|
+
}
|
|
24
|
+
const token = await exchangeCodeForToken(provider.name, provider.oauth, {
|
|
25
|
+
code: callback.code,
|
|
26
|
+
codeVerifier: session.codeVerifier,
|
|
27
|
+
redirectUri: session.redirectUri,
|
|
28
|
+
clientId: resolveClientId(provider.oauth, options),
|
|
29
|
+
clientSecret: resolveClientSecret(provider.oauth, options)
|
|
30
|
+
});
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
async function createOAuthSession(oauth, options) {
|
|
34
|
+
const redirectHost = options.redirectHost || "127.0.0.1";
|
|
35
|
+
const redirectPort = options.redirectPort ?? 53682;
|
|
36
|
+
const redirectPath = oauth.redirectPath || "/oauth/callback";
|
|
37
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
38
|
+
const usePkce = false !== oauth.usePkce;
|
|
39
|
+
const state = base64Url(randomBytes(16));
|
|
40
|
+
const codeVerifier = usePkce ? base64Url(randomBytes(32)) : "";
|
|
41
|
+
const codeChallenge = usePkce ? base64Url(createHash("sha256").update(codeVerifier).digest()) : "";
|
|
42
|
+
const server = createServer();
|
|
43
|
+
const waitForCallback = new Promise((resolve, reject)=>{
|
|
44
|
+
const timer = setTimeout(()=>{
|
|
45
|
+
reject(new Error("Login timed out. Please try again."));
|
|
46
|
+
server.close();
|
|
47
|
+
}, timeoutMs);
|
|
48
|
+
server.on("request", (req, res)=>{
|
|
49
|
+
if (!req.url) {
|
|
50
|
+
res.statusCode = 400;
|
|
51
|
+
res.end("Missing request URL.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const url = new URL(req.url, `http://${redirectHost}`);
|
|
55
|
+
if (url.pathname !== redirectPath) {
|
|
56
|
+
res.statusCode = 404;
|
|
57
|
+
res.end("Not found.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const error = url.searchParams.get("error");
|
|
61
|
+
if (error) {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
const errorDescription = url.searchParams.get("error_description") || error;
|
|
64
|
+
res.statusCode = 400;
|
|
65
|
+
res.end(`Login failed: ${errorDescription}`);
|
|
66
|
+
reject(new Error(`Login failed: ${errorDescription}`));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const code = url.searchParams.get("code");
|
|
70
|
+
const callbackState = url.searchParams.get("state");
|
|
71
|
+
if (!code || !callbackState) {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
res.statusCode = 400;
|
|
74
|
+
res.end("Missing authorization response.");
|
|
75
|
+
reject(new Error("Missing authorization response."));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (callbackState !== state) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
res.statusCode = 400;
|
|
81
|
+
res.end("State mismatch.");
|
|
82
|
+
reject(new Error("State mismatch."));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
res.statusCode = 200;
|
|
87
|
+
res.end("Login complete. You can close this window.");
|
|
88
|
+
resolve({
|
|
89
|
+
code,
|
|
90
|
+
state: callbackState
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
try {
|
|
95
|
+
await new Promise((resolve, reject)=>{
|
|
96
|
+
server.listen(redirectPort, redirectHost, ()=>resolve());
|
|
97
|
+
server.on("error", reject);
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
throw new Error(`Failed to start local callback server on ${redirectHost}:${redirectPort}. ${message}. Use --redirect-port to choose a different port.`);
|
|
102
|
+
}
|
|
103
|
+
const address = server.address();
|
|
104
|
+
const redirectUri = `http://${redirectHost}:${address.port}${redirectPath}`;
|
|
105
|
+
const authorizationUrl = buildAuthorizationUrl(oauth, {
|
|
106
|
+
clientId: resolveClientId(oauth, options),
|
|
107
|
+
redirectUri,
|
|
108
|
+
state,
|
|
109
|
+
codeChallenge,
|
|
110
|
+
scopes: resolveScopes(oauth, options),
|
|
111
|
+
usePkce
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
authorizationUrl,
|
|
115
|
+
redirectUri,
|
|
116
|
+
waitForCallback,
|
|
117
|
+
close: ()=>server.close(),
|
|
118
|
+
codeVerifier
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function resolveClientId(oauth, options) {
|
|
122
|
+
const value = options.clientId || resolveEnv(oauth.clientIdEnv) || oauth.defaultClientId;
|
|
123
|
+
if (!value) {
|
|
124
|
+
const envList = oauth.clientIdEnv.join(", ");
|
|
125
|
+
throw new Error(`Missing OAuth client ID. Set ${envList} or pass --client-id.`);
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function resolveClientSecret(oauth, options) {
|
|
130
|
+
const value = options.clientSecret || resolveEnv(oauth.clientSecretEnv || []) || oauth.defaultClientSecret;
|
|
131
|
+
return value || void 0;
|
|
132
|
+
}
|
|
133
|
+
function resolveScopes(oauth, options) {
|
|
134
|
+
if (options.scopes && options.scopes.length > 0) return options.scopes;
|
|
135
|
+
return oauth.scopes || [];
|
|
136
|
+
}
|
|
137
|
+
function buildAuthorizationUrl(oauth, input) {
|
|
138
|
+
const url = new URL(oauth.authorizationUrl);
|
|
139
|
+
url.searchParams.set("client_id", input.clientId);
|
|
140
|
+
url.searchParams.set("redirect_uri", input.redirectUri);
|
|
141
|
+
url.searchParams.set("response_type", "code");
|
|
142
|
+
url.searchParams.set("state", input.state);
|
|
143
|
+
if (input.usePkce) {
|
|
144
|
+
url.searchParams.set("code_challenge", input.codeChallenge);
|
|
145
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
146
|
+
}
|
|
147
|
+
if (input.scopes.length > 0) url.searchParams.set("scope", input.scopes.join(oauth.scopeSeparator || " "));
|
|
148
|
+
if (oauth.authorizationParams) for (const [key, value] of Object.entries(oauth.authorizationParams))url.searchParams.set(key, value);
|
|
149
|
+
return url.toString();
|
|
150
|
+
}
|
|
151
|
+
async function exchangeCodeForToken(providerName, oauth, input) {
|
|
152
|
+
const body = new URLSearchParams({
|
|
153
|
+
client_id: input.clientId,
|
|
154
|
+
code: input.code,
|
|
155
|
+
redirect_uri: input.redirectUri,
|
|
156
|
+
grant_type: "authorization_code"
|
|
157
|
+
});
|
|
158
|
+
if (false !== oauth.usePkce) body.set("code_verifier", input.codeVerifier);
|
|
159
|
+
if (input.clientSecret) body.set("client_secret", input.clientSecret);
|
|
160
|
+
if (oauth.tokenParams) for (const [key, value] of Object.entries(oauth.tokenParams))body.set(key, value);
|
|
161
|
+
const response = await fetch(oauth.tokenUrl, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
165
|
+
Accept: "application/json",
|
|
166
|
+
...oauth.tokenHeaders || {}
|
|
167
|
+
},
|
|
168
|
+
body: body.toString()
|
|
169
|
+
});
|
|
170
|
+
const contentType = response.headers.get("content-type") || "";
|
|
171
|
+
const rawText = await response.text();
|
|
172
|
+
let payload = {};
|
|
173
|
+
payload = "form" === oauth.tokenResponseType ? Object.fromEntries(new URLSearchParams(rawText)) : "json" === oauth.tokenResponseType ? JSON.parse(rawText) : contentType.includes("application/json") ? JSON.parse(rawText) : Object.fromEntries(new URLSearchParams(rawText));
|
|
174
|
+
if (!response.ok || payload.error) {
|
|
175
|
+
const description = payload.error_description || payload.error || rawText;
|
|
176
|
+
throw new Error(`Token exchange failed: ${description}`);
|
|
177
|
+
}
|
|
178
|
+
const credentials = mapTokenResponse(providerName, payload);
|
|
179
|
+
if (payload.expires_in) {
|
|
180
|
+
const expiresIn = Number(payload.expires_in);
|
|
181
|
+
if (!Number.isNaN(expiresIn)) credentials.expiresAt = new Date(Date.now() + 1000 * expiresIn).toISOString();
|
|
182
|
+
}
|
|
183
|
+
if (!credentials.expiresAt && payload.expires_at) {
|
|
184
|
+
const expiresAt = Number(payload.expires_at);
|
|
185
|
+
if (!Number.isNaN(expiresAt)) credentials.expiresAt = new Date(1000 * expiresAt).toISOString();
|
|
186
|
+
}
|
|
187
|
+
return credentials;
|
|
188
|
+
}
|
|
189
|
+
function mapTokenResponse(providerName, payload) {
|
|
190
|
+
const mapper = TOKEN_MAPPERS[providerName];
|
|
191
|
+
if (mapper) return mapper(payload);
|
|
192
|
+
const accessToken = payload.access_token;
|
|
193
|
+
if (!accessToken) throw new Error("Token exchange failed: missing access token.");
|
|
194
|
+
return {
|
|
195
|
+
accessToken,
|
|
196
|
+
refreshToken: payload.refresh_token,
|
|
197
|
+
tokenType: payload.token_type || "bearer"
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const TOKEN_MAPPERS = {
|
|
201
|
+
copilot: (payload)=>{
|
|
202
|
+
const accessToken = payload.access_token;
|
|
203
|
+
if (!accessToken) throw new Error("Token exchange failed: missing access token.");
|
|
204
|
+
return {
|
|
205
|
+
refreshToken: accessToken,
|
|
206
|
+
tokenType: payload.token_type || "bearer"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function resolveEnv(envVars) {
|
|
211
|
+
for (const key of envVars){
|
|
212
|
+
const value = process.env[key];
|
|
213
|
+
if (value && value.trim()) return value.trim();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function base64Url(buffer) {
|
|
217
|
+
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
218
|
+
}
|
|
219
|
+
async function launchBrowser(url) {
|
|
220
|
+
const platform = process.platform;
|
|
221
|
+
if ("darwin" === platform) return void await spawnBrowser("open", [
|
|
222
|
+
url
|
|
223
|
+
]);
|
|
224
|
+
if ("win32" === platform) return void await spawnBrowser("cmd", [
|
|
225
|
+
"/c",
|
|
226
|
+
"start",
|
|
227
|
+
"",
|
|
228
|
+
url
|
|
229
|
+
]);
|
|
230
|
+
await spawnBrowser("xdg-open", [
|
|
231
|
+
url
|
|
232
|
+
]);
|
|
233
|
+
}
|
|
234
|
+
function spawnBrowser(command, args) {
|
|
235
|
+
return new Promise((resolve, reject)=>{
|
|
236
|
+
const child = spawn(command, args, {
|
|
237
|
+
stdio: "ignore",
|
|
238
|
+
detached: true
|
|
239
|
+
});
|
|
240
|
+
child.once("error", reject);
|
|
241
|
+
child.once("spawn", ()=>resolve());
|
|
242
|
+
child.unref();
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
export { loginWithLocalCallback };
|