@build-astron-co/nimbus 0.2.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/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Registry & Validation
|
|
3
|
+
* Static metadata and validation functions for LLM providers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { LLMProviderName, ProviderInfo, ProviderValidationResult } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Registry of all supported LLM providers
|
|
10
|
+
*/
|
|
11
|
+
export const PROVIDER_REGISTRY: Record<LLMProviderName, ProviderInfo> = {
|
|
12
|
+
anthropic: {
|
|
13
|
+
name: 'anthropic',
|
|
14
|
+
displayName: 'Anthropic (Claude)',
|
|
15
|
+
description: 'Claude Sonnet 4, Opus 4, Haiku 4',
|
|
16
|
+
envVarName: 'ANTHROPIC_API_KEY',
|
|
17
|
+
apiKeyUrl: 'https://console.anthropic.com/settings/keys',
|
|
18
|
+
requiresApiKey: true,
|
|
19
|
+
models: [
|
|
20
|
+
{ id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', isDefault: true },
|
|
21
|
+
{ id: 'claude-opus-4-20250514', name: 'Claude Opus 4' },
|
|
22
|
+
{ id: 'claude-haiku-4-20250514', name: 'Claude Haiku 4' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
openai: {
|
|
26
|
+
name: 'openai',
|
|
27
|
+
displayName: 'OpenAI (GPT)',
|
|
28
|
+
description: 'GPT-4o, GPT-4o-mini',
|
|
29
|
+
envVarName: 'OPENAI_API_KEY',
|
|
30
|
+
apiKeyUrl: 'https://platform.openai.com/api-keys',
|
|
31
|
+
requiresApiKey: true,
|
|
32
|
+
models: [
|
|
33
|
+
{ id: 'gpt-4o', name: 'GPT-4o', isDefault: true },
|
|
34
|
+
{ id: 'gpt-4o-mini', name: 'GPT-4o Mini' },
|
|
35
|
+
{ id: 'gpt-4-turbo', name: 'GPT-4 Turbo' },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
google: {
|
|
39
|
+
name: 'google',
|
|
40
|
+
displayName: 'Google (Gemini)',
|
|
41
|
+
description: 'Gemini 2.0 Flash, Gemini 1.5 Pro',
|
|
42
|
+
envVarName: 'GOOGLE_API_KEY',
|
|
43
|
+
apiKeyUrl: 'https://aistudio.google.com/app/apikey',
|
|
44
|
+
requiresApiKey: true,
|
|
45
|
+
models: [
|
|
46
|
+
{ id: 'gemini-2.0-flash', name: 'Gemini 2.0 Flash', isDefault: true },
|
|
47
|
+
{ id: 'gemini-1.5-pro', name: 'Gemini 1.5 Pro' },
|
|
48
|
+
{ id: 'gemini-1.5-flash', name: 'Gemini 1.5 Flash' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
openrouter: {
|
|
52
|
+
name: 'openrouter',
|
|
53
|
+
displayName: 'OpenRouter',
|
|
54
|
+
description: 'Access multiple models via OpenRouter',
|
|
55
|
+
envVarName: 'OPENROUTER_API_KEY',
|
|
56
|
+
apiKeyUrl: 'https://openrouter.ai/keys',
|
|
57
|
+
requiresApiKey: true,
|
|
58
|
+
models: [
|
|
59
|
+
{
|
|
60
|
+
id: 'anthropic/claude-sonnet-4',
|
|
61
|
+
name: 'Claude Sonnet 4 (via OpenRouter)',
|
|
62
|
+
isDefault: true,
|
|
63
|
+
},
|
|
64
|
+
{ id: 'openai/gpt-4o', name: 'GPT-4o (via OpenRouter)' },
|
|
65
|
+
{ id: 'google/gemini-pro', name: 'Gemini Pro (via OpenRouter)' },
|
|
66
|
+
{ id: 'meta-llama/llama-3.1-405b-instruct', name: 'Llama 3.1 405B' },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
ollama: {
|
|
70
|
+
name: 'ollama',
|
|
71
|
+
displayName: 'Ollama (Local)',
|
|
72
|
+
description: 'Local models: Llama 3.2, CodeLlama, Mistral',
|
|
73
|
+
requiresApiKey: false,
|
|
74
|
+
supportsBaseUrl: true,
|
|
75
|
+
defaultBaseUrl: 'http://localhost:11434',
|
|
76
|
+
models: [
|
|
77
|
+
{ id: 'llama3.2', name: 'Llama 3.2', isDefault: true },
|
|
78
|
+
{ id: 'codellama', name: 'CodeLlama' },
|
|
79
|
+
{ id: 'mistral', name: 'Mistral' },
|
|
80
|
+
{ id: 'deepseek-coder', name: 'DeepSeek Coder' },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
groq: {
|
|
84
|
+
name: 'groq',
|
|
85
|
+
displayName: 'Groq',
|
|
86
|
+
description: 'Ultra-fast inference: Llama 3, Mixtral',
|
|
87
|
+
envVarName: 'GROQ_API_KEY',
|
|
88
|
+
apiKeyUrl: 'https://console.groq.com/keys',
|
|
89
|
+
requiresApiKey: true,
|
|
90
|
+
models: [
|
|
91
|
+
{ id: 'llama-3.3-70b-versatile', name: 'Llama 3.3 70B', isDefault: true },
|
|
92
|
+
{ id: 'llama-3.1-8b-instant', name: 'Llama 3.1 8B Instant' },
|
|
93
|
+
{ id: 'mixtral-8x7b-32768', name: 'Mixtral 8x7B' },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
together: {
|
|
97
|
+
name: 'together',
|
|
98
|
+
displayName: 'Together AI',
|
|
99
|
+
description: 'Open-source models: Llama, CodeLlama, Mistral',
|
|
100
|
+
envVarName: 'TOGETHER_API_KEY',
|
|
101
|
+
apiKeyUrl: 'https://api.together.xyz/settings/api-keys',
|
|
102
|
+
requiresApiKey: true,
|
|
103
|
+
models: [
|
|
104
|
+
{
|
|
105
|
+
id: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',
|
|
106
|
+
name: 'Llama 3.3 70B Turbo',
|
|
107
|
+
isDefault: true,
|
|
108
|
+
},
|
|
109
|
+
{ id: 'meta-llama/Llama-3.1-8B-Instruct-Turbo', name: 'Llama 3.1 8B Turbo' },
|
|
110
|
+
{ id: 'mistralai/Mixtral-8x7B-Instruct-v0.1', name: 'Mixtral 8x7B' },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
deepseek: {
|
|
114
|
+
name: 'deepseek',
|
|
115
|
+
displayName: 'DeepSeek',
|
|
116
|
+
description: 'DeepSeek V3, DeepSeek Coder',
|
|
117
|
+
envVarName: 'DEEPSEEK_API_KEY',
|
|
118
|
+
apiKeyUrl: 'https://platform.deepseek.com/api_keys',
|
|
119
|
+
requiresApiKey: true,
|
|
120
|
+
models: [
|
|
121
|
+
{ id: 'deepseek-chat', name: 'DeepSeek V3', isDefault: true },
|
|
122
|
+
{ id: 'deepseek-coder', name: 'DeepSeek Coder' },
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
fireworks: {
|
|
126
|
+
name: 'fireworks',
|
|
127
|
+
displayName: 'Fireworks AI',
|
|
128
|
+
description: 'Fast inference: Llama, Mixtral, FireFunction',
|
|
129
|
+
envVarName: 'FIREWORKS_API_KEY',
|
|
130
|
+
apiKeyUrl: 'https://fireworks.ai/api-keys',
|
|
131
|
+
requiresApiKey: true,
|
|
132
|
+
models: [
|
|
133
|
+
{
|
|
134
|
+
id: 'accounts/fireworks/models/llama-v3p3-70b-instruct',
|
|
135
|
+
name: 'Llama 3.3 70B',
|
|
136
|
+
isDefault: true,
|
|
137
|
+
},
|
|
138
|
+
{ id: 'accounts/fireworks/models/mixtral-8x7b-instruct', name: 'Mixtral 8x7B' },
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
perplexity: {
|
|
142
|
+
name: 'perplexity',
|
|
143
|
+
displayName: 'Perplexity',
|
|
144
|
+
description: 'Search-augmented AI: Sonar models',
|
|
145
|
+
envVarName: 'PERPLEXITY_API_KEY',
|
|
146
|
+
apiKeyUrl: 'https://www.perplexity.ai/settings/api',
|
|
147
|
+
requiresApiKey: true,
|
|
148
|
+
models: [
|
|
149
|
+
{ id: 'sonar-pro', name: 'Sonar Pro', isDefault: true },
|
|
150
|
+
{ id: 'sonar', name: 'Sonar' },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
bedrock: {
|
|
154
|
+
name: 'bedrock',
|
|
155
|
+
displayName: 'AWS Bedrock',
|
|
156
|
+
description: 'Claude via AWS Bedrock (uses IAM credentials)',
|
|
157
|
+
envVarName: 'AWS_ACCESS_KEY_ID',
|
|
158
|
+
requiresApiKey: false,
|
|
159
|
+
supportsBaseUrl: true,
|
|
160
|
+
defaultBaseUrl: 'us-east-1',
|
|
161
|
+
models: [
|
|
162
|
+
{
|
|
163
|
+
id: 'anthropic.claude-sonnet-4-20250514-v1:0',
|
|
164
|
+
name: 'Claude Sonnet 4 (Bedrock)',
|
|
165
|
+
isDefault: true,
|
|
166
|
+
},
|
|
167
|
+
{ id: 'anthropic.claude-haiku-4-20250514-v1:0', name: 'Claude Haiku 4 (Bedrock)' },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get provider info from registry
|
|
174
|
+
*/
|
|
175
|
+
export function getProviderInfo(name: LLMProviderName): ProviderInfo {
|
|
176
|
+
return PROVIDER_REGISTRY[name];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get all provider names
|
|
181
|
+
*/
|
|
182
|
+
export function getProviderNames(): LLMProviderName[] {
|
|
183
|
+
return Object.keys(PROVIDER_REGISTRY) as LLMProviderName[];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get the default model for a provider
|
|
188
|
+
*/
|
|
189
|
+
export function getDefaultModel(name: LLMProviderName): string {
|
|
190
|
+
const info = PROVIDER_REGISTRY[name];
|
|
191
|
+
const defaultModel = info.models.find(m => m.isDefault);
|
|
192
|
+
return defaultModel?.id || info.models[0].id;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Validate a provider's API key or configuration
|
|
197
|
+
* Makes lightweight test API calls to verify credentials
|
|
198
|
+
*/
|
|
199
|
+
export async function validateProviderApiKey(
|
|
200
|
+
provider: LLMProviderName,
|
|
201
|
+
apiKey?: string,
|
|
202
|
+
baseUrl?: string
|
|
203
|
+
): Promise<ProviderValidationResult> {
|
|
204
|
+
try {
|
|
205
|
+
switch (provider) {
|
|
206
|
+
case 'anthropic':
|
|
207
|
+
return await validateAnthropic(apiKey);
|
|
208
|
+
case 'openai':
|
|
209
|
+
return await validateOpenAI(apiKey);
|
|
210
|
+
case 'google':
|
|
211
|
+
return await validateGoogle(apiKey);
|
|
212
|
+
case 'openrouter':
|
|
213
|
+
return await validateOpenRouter(apiKey);
|
|
214
|
+
case 'ollama':
|
|
215
|
+
return await validateOllama(baseUrl);
|
|
216
|
+
case 'groq':
|
|
217
|
+
return await validateOpenAICompatible(apiKey, 'https://api.groq.com/openai/v1/models');
|
|
218
|
+
case 'together':
|
|
219
|
+
return await validateOpenAICompatible(apiKey, 'https://api.together.xyz/v1/models');
|
|
220
|
+
case 'deepseek':
|
|
221
|
+
return await validateOpenAICompatible(apiKey, 'https://api.deepseek.com/v1/models');
|
|
222
|
+
case 'fireworks':
|
|
223
|
+
return await validateOpenAICompatible(
|
|
224
|
+
apiKey,
|
|
225
|
+
'https://api.fireworks.ai/inference/v1/models'
|
|
226
|
+
);
|
|
227
|
+
case 'perplexity':
|
|
228
|
+
return await validateOpenAICompatible(apiKey, 'https://api.perplexity.ai/models');
|
|
229
|
+
case 'bedrock':
|
|
230
|
+
return await validateBedrock(baseUrl);
|
|
231
|
+
default:
|
|
232
|
+
return { valid: false, error: `Unknown provider: ${provider}` };
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
236
|
+
return { valid: false, error: message };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Validate Anthropic API key
|
|
242
|
+
* Uses POST /v1/messages with max_tokens: 1
|
|
243
|
+
*/
|
|
244
|
+
async function validateAnthropic(apiKey?: string): Promise<ProviderValidationResult> {
|
|
245
|
+
if (!apiKey) {
|
|
246
|
+
return { valid: false, error: 'API key is required' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
250
|
+
method: 'POST',
|
|
251
|
+
headers: {
|
|
252
|
+
'Content-Type': 'application/json',
|
|
253
|
+
'x-api-key': apiKey,
|
|
254
|
+
'anthropic-version': '2023-06-01',
|
|
255
|
+
},
|
|
256
|
+
body: JSON.stringify({
|
|
257
|
+
model: 'claude-haiku-4-20250514',
|
|
258
|
+
max_tokens: 1,
|
|
259
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
260
|
+
}),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (response.ok) {
|
|
264
|
+
return { valid: true };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check for specific error types
|
|
268
|
+
if (response.status === 401) {
|
|
269
|
+
return { valid: false, error: 'Invalid API key' };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (response.status === 400) {
|
|
273
|
+
// 400 errors with valid auth often mean the key is valid but request is malformed
|
|
274
|
+
// For validation purposes, a 400 with auth working is acceptable
|
|
275
|
+
const body = (await response.json().catch(() => ({}))) as { error?: { type?: string } };
|
|
276
|
+
if (body.error?.type === 'invalid_request_error') {
|
|
277
|
+
// Key is valid, just request was bad
|
|
278
|
+
return { valid: true };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
283
|
+
return { valid: false, error: `API error: ${response.status} - ${errorText}` };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Validate OpenAI API key
|
|
288
|
+
* Uses GET /v1/models
|
|
289
|
+
*/
|
|
290
|
+
async function validateOpenAI(apiKey?: string): Promise<ProviderValidationResult> {
|
|
291
|
+
if (!apiKey) {
|
|
292
|
+
return { valid: false, error: 'API key is required' };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const response = await fetch('https://api.openai.com/v1/models', {
|
|
296
|
+
method: 'GET',
|
|
297
|
+
headers: {
|
|
298
|
+
Authorization: `Bearer ${apiKey}`,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (response.ok) {
|
|
303
|
+
const data = (await response.json()) as { data?: Array<{ id: string }> };
|
|
304
|
+
const models = data.data?.map(m => m.id) || [];
|
|
305
|
+
return { valid: true, models };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (response.status === 401) {
|
|
309
|
+
return { valid: false, error: 'Invalid API key' };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
313
|
+
return { valid: false, error: `API error: ${response.status} - ${errorText}` };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Validate Google API key
|
|
318
|
+
* Uses GET /v1/models?key=<key>
|
|
319
|
+
*/
|
|
320
|
+
async function validateGoogle(apiKey?: string): Promise<ProviderValidationResult> {
|
|
321
|
+
if (!apiKey) {
|
|
322
|
+
return { valid: false, error: 'API key is required' };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const response = await fetch(
|
|
326
|
+
`https://generativelanguage.googleapis.com/v1/models?key=${encodeURIComponent(apiKey)}`,
|
|
327
|
+
{
|
|
328
|
+
method: 'GET',
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
if (response.ok) {
|
|
333
|
+
const data = (await response.json()) as { models?: Array<{ name: string }> };
|
|
334
|
+
const models = data.models?.map(m => m.name) || [];
|
|
335
|
+
return { valid: true, models };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (response.status === 400 || response.status === 403) {
|
|
339
|
+
return { valid: false, error: 'Invalid API key' };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
343
|
+
return { valid: false, error: `API error: ${response.status} - ${errorText}` };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Validate OpenRouter API key
|
|
348
|
+
* Uses GET /api/v1/models with Bearer auth
|
|
349
|
+
*/
|
|
350
|
+
async function validateOpenRouter(apiKey?: string): Promise<ProviderValidationResult> {
|
|
351
|
+
if (!apiKey) {
|
|
352
|
+
return { valid: false, error: 'API key is required' };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const response = await fetch('https://openrouter.ai/api/v1/models', {
|
|
356
|
+
method: 'GET',
|
|
357
|
+
headers: {
|
|
358
|
+
Authorization: `Bearer ${apiKey}`,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (response.ok) {
|
|
363
|
+
const data = (await response.json()) as { data?: Array<{ id: string }> };
|
|
364
|
+
const models = data.data?.map(m => m.id) || [];
|
|
365
|
+
return { valid: true, models };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (response.status === 401) {
|
|
369
|
+
return { valid: false, error: 'Invalid API key' };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
373
|
+
return { valid: false, error: `API error: ${response.status} - ${errorText}` };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Validate OpenAI-compatible provider API key
|
|
378
|
+
* Uses GET /v1/models (or equivalent) with Bearer auth
|
|
379
|
+
*/
|
|
380
|
+
async function validateOpenAICompatible(
|
|
381
|
+
apiKey?: string,
|
|
382
|
+
modelsUrl?: string
|
|
383
|
+
): Promise<ProviderValidationResult> {
|
|
384
|
+
if (!apiKey) {
|
|
385
|
+
return { valid: false, error: 'API key is required' };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const url = modelsUrl || 'https://api.openai.com/v1/models';
|
|
389
|
+
|
|
390
|
+
const response = await fetch(url, {
|
|
391
|
+
method: 'GET',
|
|
392
|
+
headers: {
|
|
393
|
+
Authorization: `Bearer ${apiKey}`,
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
if (response.ok) {
|
|
398
|
+
return { valid: true };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (response.status === 401 || response.status === 403) {
|
|
402
|
+
return { valid: false, error: 'Invalid API key' };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
406
|
+
return { valid: false, error: `API error: ${response.status} - ${errorText}` };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Validate Ollama connection
|
|
411
|
+
* Uses GET <baseUrl>/api/tags (no key needed)
|
|
412
|
+
*/
|
|
413
|
+
async function validateOllama(baseUrl?: string): Promise<ProviderValidationResult> {
|
|
414
|
+
const url = baseUrl || 'http://localhost:11434';
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const response = await fetch(`${url}/api/tags`, {
|
|
418
|
+
method: 'GET',
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
if (response.ok) {
|
|
422
|
+
const data = (await response.json()) as { models?: Array<{ name: string }> };
|
|
423
|
+
const models = data.models?.map(m => m.name) || [];
|
|
424
|
+
return { valid: true, models };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
428
|
+
return { valid: false, error: `Ollama API error: ${response.status} - ${errorText}` };
|
|
429
|
+
} catch (error) {
|
|
430
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
431
|
+
return {
|
|
432
|
+
valid: false,
|
|
433
|
+
error: `Cannot connect to Ollama at ${url}. Is Ollama running?`,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Validate AWS Bedrock credentials
|
|
442
|
+
* Checks for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env vars,
|
|
443
|
+
* or AWS credentials file (~/.aws/credentials).
|
|
444
|
+
*/
|
|
445
|
+
async function validateBedrock(_region?: string): Promise<ProviderValidationResult> {
|
|
446
|
+
const hasAccessKey = !!process.env.AWS_ACCESS_KEY_ID;
|
|
447
|
+
const hasSecretKey = !!process.env.AWS_SECRET_ACCESS_KEY;
|
|
448
|
+
const hasProfile = !!process.env.AWS_PROFILE;
|
|
449
|
+
|
|
450
|
+
// Check for AWS credentials file
|
|
451
|
+
const fs = await import('fs');
|
|
452
|
+
const path = await import('path');
|
|
453
|
+
const os = await import('os');
|
|
454
|
+
const credentialsPath = path.join(os.homedir(), '.aws', 'credentials');
|
|
455
|
+
const hasCredentialsFile = fs.existsSync(credentialsPath);
|
|
456
|
+
|
|
457
|
+
if (hasAccessKey && hasSecretKey) {
|
|
458
|
+
return { valid: true };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (hasProfile || hasCredentialsFile) {
|
|
462
|
+
return { valid: true };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
valid: false,
|
|
467
|
+
error:
|
|
468
|
+
'AWS credentials not found. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or configure ~/.aws/credentials.',
|
|
469
|
+
};
|
|
470
|
+
}
|
package/src/auth/sso.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSO Device Flow
|
|
3
|
+
* Enterprise SSO authentication using device code flow
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { authClient } from '../clients/enterprise-client';
|
|
7
|
+
import type { DeviceCodeResponse } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* SSO Device Flow for enterprise authentication
|
|
11
|
+
* Uses the auth-service for device code management
|
|
12
|
+
*/
|
|
13
|
+
export class SSODeviceFlow {
|
|
14
|
+
private deviceCode: string | null = null;
|
|
15
|
+
private interval: number = 5;
|
|
16
|
+
private expiresAt: number = 0;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Initiate device code flow
|
|
20
|
+
* Returns device code and user code for the client
|
|
21
|
+
*/
|
|
22
|
+
async initiate(): Promise<DeviceCodeResponse> {
|
|
23
|
+
const response = await authClient.initiateDeviceFlow();
|
|
24
|
+
|
|
25
|
+
this.deviceCode = response.deviceCode;
|
|
26
|
+
this.interval = response.interval || 5;
|
|
27
|
+
this.expiresAt = Date.now() + response.expiresIn * 1000;
|
|
28
|
+
|
|
29
|
+
return response;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Poll for authorization
|
|
34
|
+
* Returns access token when user authorizes, null if pending
|
|
35
|
+
*/
|
|
36
|
+
async poll(): Promise<string | null> {
|
|
37
|
+
if (!this.deviceCode) {
|
|
38
|
+
throw new Error('Device code not initiated. Call initiate() first.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (Date.now() > this.expiresAt) {
|
|
42
|
+
throw new Error('Device code expired. Please start the login process again.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const response = await authClient.pollDeviceCode(this.deviceCode);
|
|
46
|
+
|
|
47
|
+
if (response.accessToken) {
|
|
48
|
+
return response.accessToken;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (response.error === 'authorization_pending') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (response.error === 'slow_down') {
|
|
56
|
+
this.interval += 5;
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (response.error === 'expired_token') {
|
|
61
|
+
throw new Error('Device code expired. Please start the login process again.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (response.error === 'access_denied') {
|
|
65
|
+
throw new Error('Authorization was denied.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw new Error(response.errorDescription || response.error || 'Unknown error');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get polling interval in milliseconds
|
|
73
|
+
*/
|
|
74
|
+
getPollingInterval(): number {
|
|
75
|
+
return this.interval * 1000;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Wait for authorization by polling
|
|
80
|
+
* Returns access token when user completes authorization
|
|
81
|
+
*/
|
|
82
|
+
async waitForAuthorization(onPoll?: () => void, abortSignal?: AbortSignal): Promise<string> {
|
|
83
|
+
for (;;) {
|
|
84
|
+
if (abortSignal?.aborted) {
|
|
85
|
+
throw new Error('Authorization cancelled');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const token = await this.poll();
|
|
89
|
+
|
|
90
|
+
if (token) {
|
|
91
|
+
return token;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onPoll?.();
|
|
95
|
+
await this.sleep(this.getPollingInterval());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private sleep(ms: number): Promise<void> {
|
|
100
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Validate an SSO access token
|
|
106
|
+
*/
|
|
107
|
+
export async function validateSSOToken(accessToken: string): Promise<{
|
|
108
|
+
valid: boolean;
|
|
109
|
+
userId?: string;
|
|
110
|
+
teamId?: string;
|
|
111
|
+
}> {
|
|
112
|
+
return authClient.validateToken(accessToken);
|
|
113
|
+
}
|