@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,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding Command
|
|
3
|
+
*
|
|
4
|
+
* First-run wizard that detects missing LLM credentials and guides
|
|
5
|
+
* the user through initial setup. Runs before full app init so it
|
|
6
|
+
* only depends on the auth store and wizard prompts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { AuthStore } from '../auth/store';
|
|
10
|
+
import { PROVIDER_REGISTRY, getDefaultModel, validateProviderApiKey } from '../auth/providers';
|
|
11
|
+
import type { LLMProviderName } from '../auth/types';
|
|
12
|
+
import { ui } from '../wizard/ui';
|
|
13
|
+
import { select, input } from '../wizard/prompts';
|
|
14
|
+
|
|
15
|
+
export interface OnboardingOptions {
|
|
16
|
+
/** Skip the wizard and exit immediately. */
|
|
17
|
+
skip?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check whether onboarding is needed.
|
|
22
|
+
*
|
|
23
|
+
* Returns `true` if no LLM credentials are found in the auth store
|
|
24
|
+
* and no provider API key environment variables are set.
|
|
25
|
+
*/
|
|
26
|
+
export function needsOnboarding(): boolean {
|
|
27
|
+
const store = new AuthStore();
|
|
28
|
+
if (store.exists()) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hasEnvKey = !!(
|
|
33
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
34
|
+
process.env.OPENAI_API_KEY ||
|
|
35
|
+
process.env.GOOGLE_API_KEY ||
|
|
36
|
+
process.env.OPENROUTER_API_KEY ||
|
|
37
|
+
process.env.GROQ_API_KEY ||
|
|
38
|
+
process.env.TOGETHER_API_KEY ||
|
|
39
|
+
process.env.DEEPSEEK_API_KEY ||
|
|
40
|
+
process.env.FIREWORKS_API_KEY ||
|
|
41
|
+
process.env.PERPLEXITY_API_KEY ||
|
|
42
|
+
process.env.AWS_ACCESS_KEY_ID
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return !hasEnvKey;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run the first-run onboarding wizard.
|
|
50
|
+
*/
|
|
51
|
+
export async function onboardingCommand(options: OnboardingOptions = {}): Promise<void> {
|
|
52
|
+
if (options.skip) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const store = new AuthStore();
|
|
57
|
+
|
|
58
|
+
// Double-check — if credentials exist, skip silently
|
|
59
|
+
if (store.exists()) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Non-TTY detection: if stdin is not a TTY (piped input, CI, etc.),
|
|
64
|
+
// skip the interactive wizard and show setup instructions instead.
|
|
65
|
+
if (!process.stdin.isTTY) {
|
|
66
|
+
ui.newLine();
|
|
67
|
+
ui.info('No LLM provider configured. Set an API key via environment variable:');
|
|
68
|
+
ui.newLine();
|
|
69
|
+
ui.print(' export ANTHROPIC_API_KEY=sk-ant-...');
|
|
70
|
+
ui.print(' export OPENAI_API_KEY=sk-...');
|
|
71
|
+
ui.print(' export GOOGLE_API_KEY=...');
|
|
72
|
+
ui.print(' export OPENROUTER_API_KEY=sk-or-...');
|
|
73
|
+
ui.print(' export GROQ_API_KEY=gsk_...');
|
|
74
|
+
ui.print(' export DEEPSEEK_API_KEY=sk-...');
|
|
75
|
+
ui.newLine();
|
|
76
|
+
ui.info('Or run "nimbus login" in an interactive terminal.');
|
|
77
|
+
ui.newLine();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Welcome banner
|
|
83
|
+
ui.newLine();
|
|
84
|
+
ui.box({
|
|
85
|
+
title: 'Welcome to Nimbus',
|
|
86
|
+
content: [
|
|
87
|
+
'AI-Powered Cloud Engineering Agent',
|
|
88
|
+
'',
|
|
89
|
+
"Let's get you set up with an LLM provider.",
|
|
90
|
+
'This takes about 30 seconds.',
|
|
91
|
+
],
|
|
92
|
+
style: 'rounded',
|
|
93
|
+
borderColor: 'cyan',
|
|
94
|
+
titleColor: 'brightCyan',
|
|
95
|
+
padding: 1,
|
|
96
|
+
});
|
|
97
|
+
ui.newLine();
|
|
98
|
+
|
|
99
|
+
// Provider selection
|
|
100
|
+
const providerOptions = Object.values(PROVIDER_REGISTRY).map(p => ({
|
|
101
|
+
label: p.displayName,
|
|
102
|
+
value: p.name,
|
|
103
|
+
description: p.description,
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
const provider = await select<LLMProviderName>({
|
|
107
|
+
message: 'Which LLM provider would you like to use?',
|
|
108
|
+
options: providerOptions,
|
|
109
|
+
required: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!provider) {
|
|
113
|
+
ui.newLine();
|
|
114
|
+
ui.info('No provider selected. You can run "nimbus login" later to set up.');
|
|
115
|
+
ui.newLine();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const providerInfo = PROVIDER_REGISTRY[provider];
|
|
120
|
+
|
|
121
|
+
// API key input (skip for Ollama)
|
|
122
|
+
let apiKey: string | undefined;
|
|
123
|
+
let baseUrl: string | undefined;
|
|
124
|
+
|
|
125
|
+
if (providerInfo.requiresApiKey) {
|
|
126
|
+
ui.newLine();
|
|
127
|
+
if (providerInfo.apiKeyUrl) {
|
|
128
|
+
ui.info(`Get your API key at: ${ui.color(providerInfo.apiKeyUrl, 'cyan')}`);
|
|
129
|
+
}
|
|
130
|
+
ui.newLine();
|
|
131
|
+
|
|
132
|
+
apiKey = await input({
|
|
133
|
+
message: `Enter your ${providerInfo.displayName} API key`,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!apiKey) {
|
|
137
|
+
ui.newLine();
|
|
138
|
+
ui.warning('No API key entered. You can run "nimbus login" later to set up.');
|
|
139
|
+
ui.newLine();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
} else if (providerInfo.supportsBaseUrl) {
|
|
143
|
+
// Ollama — ask for base URL
|
|
144
|
+
ui.newLine();
|
|
145
|
+
baseUrl = await input({
|
|
146
|
+
message: 'Ollama server URL',
|
|
147
|
+
defaultValue: providerInfo.defaultBaseUrl || 'http://localhost:11434',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Validate credentials
|
|
152
|
+
ui.newLine();
|
|
153
|
+
ui.startSpinner({ message: `Validating ${providerInfo.displayName} credentials...` });
|
|
154
|
+
|
|
155
|
+
let validation: { valid: boolean; error?: string };
|
|
156
|
+
try {
|
|
157
|
+
validation = await validateProviderApiKey(provider, apiKey, baseUrl);
|
|
158
|
+
} catch (err: unknown) {
|
|
159
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
160
|
+
ui.stopSpinnerFail(`Validation error: ${msg}`);
|
|
161
|
+
ui.newLine();
|
|
162
|
+
ui.info('You can retry with "nimbus login" or set the API key via environment variable.');
|
|
163
|
+
ui.newLine();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!validation.valid) {
|
|
168
|
+
ui.stopSpinnerFail(`Validation failed: ${validation.error}`);
|
|
169
|
+
ui.newLine();
|
|
170
|
+
ui.info('You can retry with "nimbus login" or set the API key via environment variable:');
|
|
171
|
+
if (providerInfo.envVarName) {
|
|
172
|
+
ui.info(` export ${providerInfo.envVarName}=your-key`);
|
|
173
|
+
}
|
|
174
|
+
ui.newLine();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
ui.stopSpinnerSuccess(`${providerInfo.displayName} credentials verified!`);
|
|
179
|
+
|
|
180
|
+
// Save credentials
|
|
181
|
+
const defaultModel = getDefaultModel(provider);
|
|
182
|
+
|
|
183
|
+
store.setProvider(provider, {
|
|
184
|
+
apiKey,
|
|
185
|
+
baseUrl,
|
|
186
|
+
model: defaultModel,
|
|
187
|
+
isDefault: true,
|
|
188
|
+
validatedAt: new Date().toISOString(),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Create default config.yaml if it doesn't exist
|
|
192
|
+
try {
|
|
193
|
+
const { configManager } = await import('../config/manager');
|
|
194
|
+
if (!configManager.exists()) {
|
|
195
|
+
configManager.save();
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
/* non-critical */
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Success message
|
|
202
|
+
ui.newLine();
|
|
203
|
+
ui.box({
|
|
204
|
+
title: 'Setup Complete',
|
|
205
|
+
content: [
|
|
206
|
+
`Provider: ${providerInfo.displayName}`,
|
|
207
|
+
`Model: ${defaultModel}`,
|
|
208
|
+
'',
|
|
209
|
+
"You're ready to go! Starting interactive chat...",
|
|
210
|
+
],
|
|
211
|
+
style: 'rounded',
|
|
212
|
+
borderColor: 'green',
|
|
213
|
+
titleColor: 'brightGreen',
|
|
214
|
+
padding: 1,
|
|
215
|
+
});
|
|
216
|
+
ui.newLine();
|
|
217
|
+
} catch (err: unknown) {
|
|
218
|
+
// Catch-all for prompt failures (stdin closed, terminal not supported, etc.)
|
|
219
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
220
|
+
ui.newLine();
|
|
221
|
+
ui.warning(`Onboarding wizard failed: ${msg}`);
|
|
222
|
+
ui.newLine();
|
|
223
|
+
ui.info('Set up manually with "nimbus login" or via environment variables:');
|
|
224
|
+
ui.print(' export ANTHROPIC_API_KEY=sk-ant-...');
|
|
225
|
+
ui.print(' export OPENAI_API_KEY=sk-...');
|
|
226
|
+
ui.newLine();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Display Utilities
|
|
3
|
+
*
|
|
4
|
+
* Format and display plan output for different infrastructure types
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ui } from '../../wizard';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resource change in plan
|
|
11
|
+
*/
|
|
12
|
+
export interface ResourceChange {
|
|
13
|
+
action: string;
|
|
14
|
+
resource: string;
|
|
15
|
+
address: string;
|
|
16
|
+
before?: Record<string, unknown>;
|
|
17
|
+
after?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Plan result structure
|
|
22
|
+
*/
|
|
23
|
+
export interface PlanResult {
|
|
24
|
+
type: 'terraform' | 'k8s' | 'helm';
|
|
25
|
+
success: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
changes?: {
|
|
28
|
+
add: number;
|
|
29
|
+
change: number;
|
|
30
|
+
destroy: number;
|
|
31
|
+
};
|
|
32
|
+
resources?: ResourceChange[];
|
|
33
|
+
raw?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Action symbols for display
|
|
38
|
+
*/
|
|
39
|
+
const ACTION_SYMBOLS: Record<
|
|
40
|
+
string,
|
|
41
|
+
{ symbol: string; color: 'green' | 'yellow' | 'red' | 'blue' | 'cyan' }
|
|
42
|
+
> = {
|
|
43
|
+
create: { symbol: '+', color: 'green' },
|
|
44
|
+
add: { symbol: '+', color: 'green' },
|
|
45
|
+
apply: { symbol: '~', color: 'cyan' },
|
|
46
|
+
update: { symbol: '~', color: 'yellow' },
|
|
47
|
+
change: { symbol: '~', color: 'yellow' },
|
|
48
|
+
delete: { symbol: '-', color: 'red' },
|
|
49
|
+
destroy: { symbol: '-', color: 'red' },
|
|
50
|
+
read: { symbol: '<=', color: 'blue' },
|
|
51
|
+
'no-op': { symbol: ' ', color: 'blue' },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format action for display
|
|
56
|
+
*/
|
|
57
|
+
function formatAction(action: string): string {
|
|
58
|
+
const config = ACTION_SYMBOLS[action.toLowerCase()] || { symbol: '?', color: 'yellow' as const };
|
|
59
|
+
return ui.color(config.symbol, config.color);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Format resource for display
|
|
64
|
+
*/
|
|
65
|
+
function formatResource(resource: string): string {
|
|
66
|
+
return resource;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Display plan summary
|
|
71
|
+
*/
|
|
72
|
+
function displaySummary(changes: { add: number; change: number; destroy: number }): void {
|
|
73
|
+
ui.print('Plan Summary:');
|
|
74
|
+
ui.newLine();
|
|
75
|
+
|
|
76
|
+
const { add, change, destroy } = changes;
|
|
77
|
+
const total = add + change + destroy;
|
|
78
|
+
|
|
79
|
+
if (total === 0) {
|
|
80
|
+
ui.print(' No changes. Infrastructure is up to date.');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (add > 0) {
|
|
85
|
+
ui.print(` ${ui.color(`+ ${add} to add`, 'green')}`);
|
|
86
|
+
}
|
|
87
|
+
if (change > 0) {
|
|
88
|
+
ui.print(` ${ui.color(`~ ${change} to change`, 'yellow')}`);
|
|
89
|
+
}
|
|
90
|
+
if (destroy > 0) {
|
|
91
|
+
ui.print(` ${ui.color(`- ${destroy} to destroy`, 'red')}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ui.newLine();
|
|
95
|
+
ui.print(` Total: ${total} resource(s)`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Display resource changes
|
|
100
|
+
*/
|
|
101
|
+
function displayResources(resources: ResourceChange[]): void {
|
|
102
|
+
if (!resources || resources.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ui.newLine();
|
|
107
|
+
ui.print('Resource Changes:');
|
|
108
|
+
ui.newLine();
|
|
109
|
+
|
|
110
|
+
// Group by action
|
|
111
|
+
const grouped: Record<string, ResourceChange[]> = {};
|
|
112
|
+
|
|
113
|
+
for (const resource of resources) {
|
|
114
|
+
const action = resource.action.toLowerCase();
|
|
115
|
+
if (!grouped[action]) {
|
|
116
|
+
grouped[action] = [];
|
|
117
|
+
}
|
|
118
|
+
grouped[action].push(resource);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Display in order: create, update, delete, other
|
|
122
|
+
const order = ['create', 'add', 'update', 'change', 'apply', 'delete', 'destroy', 'read'];
|
|
123
|
+
|
|
124
|
+
for (const action of order) {
|
|
125
|
+
const items = grouped[action];
|
|
126
|
+
if (!items || items.length === 0) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const item of items) {
|
|
131
|
+
const symbol = formatAction(item.action);
|
|
132
|
+
const resource = formatResource(item.resource);
|
|
133
|
+
ui.print(` ${symbol} ${resource}`);
|
|
134
|
+
|
|
135
|
+
// Show address if different from resource name
|
|
136
|
+
if (item.address && item.address !== item.resource) {
|
|
137
|
+
ui.print(` ${ui.dim(item.address)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Display any remaining actions not in order
|
|
143
|
+
for (const [action, items] of Object.entries(grouped)) {
|
|
144
|
+
if (order.includes(action)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const item of items) {
|
|
149
|
+
const symbol = formatAction(item.action);
|
|
150
|
+
const resource = formatResource(item.resource);
|
|
151
|
+
ui.print(` ${symbol} ${resource}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Display raw plan output (detailed mode)
|
|
158
|
+
*/
|
|
159
|
+
function displayRawPlan(raw: string): void {
|
|
160
|
+
ui.newLine();
|
|
161
|
+
ui.print('Detailed Plan:');
|
|
162
|
+
ui.newLine();
|
|
163
|
+
|
|
164
|
+
// Limit output length
|
|
165
|
+
const maxLength = 5000;
|
|
166
|
+
if (raw.length > maxLength) {
|
|
167
|
+
ui.print(raw.slice(0, maxLength));
|
|
168
|
+
ui.newLine();
|
|
169
|
+
ui.print(ui.dim(`... (output truncated, ${raw.length - maxLength} more characters)`));
|
|
170
|
+
} else {
|
|
171
|
+
ui.print(raw);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Display Terraform-specific plan
|
|
177
|
+
*/
|
|
178
|
+
function displayTerraformPlan(plan: PlanResult, detailed: boolean): void {
|
|
179
|
+
if (plan.changes) {
|
|
180
|
+
displaySummary(plan.changes);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (plan.resources && plan.resources.length > 0) {
|
|
184
|
+
displayResources(plan.resources);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (detailed && plan.raw) {
|
|
188
|
+
displayRawPlan(plan.raw);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Show next steps
|
|
192
|
+
ui.newLine();
|
|
193
|
+
ui.print('Next steps:');
|
|
194
|
+
ui.print(' - Review the changes above');
|
|
195
|
+
ui.print(' - Run "nimbus apply terraform" to apply');
|
|
196
|
+
ui.print(' - Or run "nimbus tf apply" for more options');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Display Kubernetes-specific plan
|
|
201
|
+
*/
|
|
202
|
+
function displayK8sPlan(plan: PlanResult, detailed: boolean): void {
|
|
203
|
+
if (plan.changes) {
|
|
204
|
+
ui.print('Plan Summary:');
|
|
205
|
+
ui.newLine();
|
|
206
|
+
ui.print(` ${ui.color(`${plan.changes.add} resource(s) to apply`, 'cyan')}`);
|
|
207
|
+
if (plan.changes.change > 0) {
|
|
208
|
+
ui.print(` ${ui.color(`${plan.changes.change} resource(s) will be updated`, 'yellow')}`);
|
|
209
|
+
}
|
|
210
|
+
ui.newLine();
|
|
211
|
+
ui.info("Note: Kubernetes apply is idempotent - unchanged resources won't be modified");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (plan.resources && plan.resources.length > 0) {
|
|
215
|
+
displayResources(plan.resources);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (detailed && plan.raw) {
|
|
219
|
+
displayRawPlan(plan.raw);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Show next steps
|
|
223
|
+
ui.newLine();
|
|
224
|
+
ui.print('Next steps:');
|
|
225
|
+
ui.print(' - Review the resources above');
|
|
226
|
+
ui.print(' - Run "nimbus apply k8s" to apply');
|
|
227
|
+
ui.print(' - Or run "nimbus k8s apply" for more options');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Display Helm-specific plan
|
|
232
|
+
*/
|
|
233
|
+
function displayHelmPlan(plan: PlanResult, detailed: boolean): void {
|
|
234
|
+
if (plan.changes) {
|
|
235
|
+
ui.print('Plan Summary:');
|
|
236
|
+
ui.newLine();
|
|
237
|
+
if (plan.changes.add > 0) {
|
|
238
|
+
ui.print(` ${ui.color(`+ ${plan.changes.add} resource(s) to create`, 'green')}`);
|
|
239
|
+
}
|
|
240
|
+
if (plan.changes.change > 0) {
|
|
241
|
+
ui.print(` ${ui.color(`~ ${plan.changes.change} resource(s) to update`, 'yellow')}`);
|
|
242
|
+
}
|
|
243
|
+
if (plan.changes.destroy > 0) {
|
|
244
|
+
ui.print(` ${ui.color(`- ${plan.changes.destroy} resource(s) to remove`, 'red')}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (plan.resources && plan.resources.length > 0) {
|
|
249
|
+
displayResources(plan.resources);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (detailed && plan.raw) {
|
|
253
|
+
displayRawPlan(plan.raw);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Show next steps
|
|
257
|
+
ui.newLine();
|
|
258
|
+
ui.print('Next steps:');
|
|
259
|
+
ui.print(' - Review the changes above');
|
|
260
|
+
ui.print(' - Run "nimbus apply helm" to install/upgrade');
|
|
261
|
+
ui.print(' - Or run "nimbus helm install/upgrade" for more options');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Display plan result
|
|
266
|
+
*/
|
|
267
|
+
export function displayPlan(plan: PlanResult, detailed = false): void {
|
|
268
|
+
switch (plan.type) {
|
|
269
|
+
case 'terraform':
|
|
270
|
+
displayTerraformPlan(plan, detailed);
|
|
271
|
+
break;
|
|
272
|
+
case 'k8s':
|
|
273
|
+
displayK8sPlan(plan, detailed);
|
|
274
|
+
break;
|
|
275
|
+
case 'helm':
|
|
276
|
+
displayHelmPlan(plan, detailed);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|