@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,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety Policy Configuration
|
|
3
|
+
*
|
|
4
|
+
* Defines safety policies for infrastructure operations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Risk severity levels
|
|
12
|
+
*/
|
|
13
|
+
export type RiskSeverity = 'critical' | 'high' | 'medium' | 'low';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Risk definition
|
|
17
|
+
*/
|
|
18
|
+
export interface Risk {
|
|
19
|
+
id: string;
|
|
20
|
+
severity: RiskSeverity;
|
|
21
|
+
message: string;
|
|
22
|
+
details?: Record<string, unknown>;
|
|
23
|
+
canProceed: boolean;
|
|
24
|
+
requiresApproval: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Safety check result
|
|
29
|
+
*/
|
|
30
|
+
export interface SafetyCheckResult {
|
|
31
|
+
passed: boolean;
|
|
32
|
+
risks: Risk[];
|
|
33
|
+
blockers: Risk[];
|
|
34
|
+
requiresApproval: boolean;
|
|
35
|
+
estimatedCost?: number;
|
|
36
|
+
affectedResources?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Safety policy configuration
|
|
41
|
+
*/
|
|
42
|
+
export interface SafetyPolicy {
|
|
43
|
+
/** Operations that always require approval */
|
|
44
|
+
alwaysRequireApproval: string[];
|
|
45
|
+
/** Protected environments that require extra caution */
|
|
46
|
+
protectedEnvironments: string[];
|
|
47
|
+
/** Cost threshold that triggers approval ($) */
|
|
48
|
+
costThreshold: number;
|
|
49
|
+
/** Operations to skip safety checks for */
|
|
50
|
+
skipSafetyFor: string[];
|
|
51
|
+
/** Custom rules */
|
|
52
|
+
customRules?: SafetyRule[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Custom safety rule
|
|
57
|
+
*/
|
|
58
|
+
export interface SafetyRule {
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
severity: RiskSeverity;
|
|
63
|
+
check: (context: SafetyContext) => boolean;
|
|
64
|
+
message: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Context for safety checks
|
|
69
|
+
*/
|
|
70
|
+
export interface SafetyContext {
|
|
71
|
+
operation: string;
|
|
72
|
+
type: 'terraform' | 'kubernetes' | 'helm' | 'aws' | 'gcp' | 'azure';
|
|
73
|
+
environment?: string;
|
|
74
|
+
resources?: string[];
|
|
75
|
+
estimatedCost?: number;
|
|
76
|
+
planOutput?: string;
|
|
77
|
+
metadata?: Record<string, unknown>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Default safety policy
|
|
82
|
+
*/
|
|
83
|
+
export const defaultSafetyPolicy: SafetyPolicy = {
|
|
84
|
+
alwaysRequireApproval: ['destroy', 'delete', 'terminate', 'update', 'apply', 'create'],
|
|
85
|
+
protectedEnvironments: ['production', 'prod', 'prd', 'live', 'main', 'master'],
|
|
86
|
+
costThreshold: 500,
|
|
87
|
+
skipSafetyFor: ['plan', 'validate', 'show', 'list', 'get', 'describe', 'logs', 'status'],
|
|
88
|
+
customRules: [],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Load safety policy from config file or use defaults
|
|
93
|
+
*/
|
|
94
|
+
export function loadSafetyPolicy(configPath?: string): SafetyPolicy {
|
|
95
|
+
// Try to load from workspace config
|
|
96
|
+
const nimbusDir = path.join(process.cwd(), '.nimbus');
|
|
97
|
+
const configFile = configPath || path.join(nimbusDir, 'config.yaml');
|
|
98
|
+
|
|
99
|
+
if (fs.existsSync(configFile)) {
|
|
100
|
+
try {
|
|
101
|
+
const content = fs.readFileSync(configFile, 'utf-8');
|
|
102
|
+
const policy = parseSafetyConfig(content);
|
|
103
|
+
if (policy) {
|
|
104
|
+
return { ...defaultSafetyPolicy, ...policy };
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Use defaults
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return defaultSafetyPolicy;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Parse safety config from YAML content
|
|
116
|
+
*/
|
|
117
|
+
function parseSafetyConfig(content: string): Partial<SafetyPolicy> | null {
|
|
118
|
+
// Simple YAML parsing for safety section
|
|
119
|
+
const safetyMatch = content.match(/safety:\s*\n((?:[ \t]+.+\n?)*)/);
|
|
120
|
+
if (!safetyMatch) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const safetySection = safetyMatch[1];
|
|
125
|
+
const policy: Partial<SafetyPolicy> = {};
|
|
126
|
+
|
|
127
|
+
// Parse requireApproval
|
|
128
|
+
const approvalMatch = safetySection.match(/requireApproval:\s*(true|false)/);
|
|
129
|
+
if (approvalMatch) {
|
|
130
|
+
if (approvalMatch[1] === 'false') {
|
|
131
|
+
policy.alwaysRequireApproval = [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Parse protectedEnvironments
|
|
136
|
+
const envMatch = safetySection.match(/protectedEnvironments:\s*\[([^\]]+)\]/);
|
|
137
|
+
if (envMatch) {
|
|
138
|
+
policy.protectedEnvironments = envMatch[1].split(',').map(s => s.trim().replace(/['"]/g, ''));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Parse costThreshold
|
|
142
|
+
const costMatch = safetySection.match(/costThreshold:\s*(\d+)/);
|
|
143
|
+
if (costMatch) {
|
|
144
|
+
policy.costThreshold = parseInt(costMatch[1], 10);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return policy;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check if an operation requires safety checks
|
|
152
|
+
*/
|
|
153
|
+
export function requiresSafetyCheck(
|
|
154
|
+
operation: string,
|
|
155
|
+
policy: SafetyPolicy = defaultSafetyPolicy
|
|
156
|
+
): boolean {
|
|
157
|
+
const normalizedOp = operation.toLowerCase();
|
|
158
|
+
return !policy.skipSafetyFor.some(skip => normalizedOp.includes(skip.toLowerCase()));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if an operation requires approval
|
|
163
|
+
*/
|
|
164
|
+
export function requiresApproval(
|
|
165
|
+
operation: string,
|
|
166
|
+
context: SafetyContext,
|
|
167
|
+
policy: SafetyPolicy = defaultSafetyPolicy
|
|
168
|
+
): boolean {
|
|
169
|
+
const normalizedOp = operation.toLowerCase();
|
|
170
|
+
|
|
171
|
+
// Check if operation is in always require list
|
|
172
|
+
if (policy.alwaysRequireApproval.some(op => normalizedOp.includes(op.toLowerCase()))) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check if environment is protected
|
|
177
|
+
if (context.environment) {
|
|
178
|
+
const normalizedEnv = context.environment.toLowerCase();
|
|
179
|
+
if (policy.protectedEnvironments.some(env => normalizedEnv.includes(env.toLowerCase()))) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check cost threshold
|
|
185
|
+
if (context.estimatedCost && context.estimatedCost > policy.costThreshold) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Evaluate safety for an operation
|
|
194
|
+
*/
|
|
195
|
+
export function evaluateSafety(
|
|
196
|
+
context: SafetyContext,
|
|
197
|
+
policy: SafetyPolicy = defaultSafetyPolicy
|
|
198
|
+
): SafetyCheckResult {
|
|
199
|
+
const risks: Risk[] = [];
|
|
200
|
+
const blockers: Risk[] = [];
|
|
201
|
+
let requiresApprovalFlag = false;
|
|
202
|
+
|
|
203
|
+
// Check for destroy/delete operations
|
|
204
|
+
if (['destroy', 'delete', 'terminate'].some(op => context.operation.toLowerCase().includes(op))) {
|
|
205
|
+
const risk: Risk = {
|
|
206
|
+
id: 'destructive-operation',
|
|
207
|
+
severity: 'critical',
|
|
208
|
+
message: `Destructive operation: ${context.operation}`,
|
|
209
|
+
canProceed: true,
|
|
210
|
+
requiresApproval: true,
|
|
211
|
+
};
|
|
212
|
+
risks.push(risk);
|
|
213
|
+
requiresApprovalFlag = true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Check for protected environment
|
|
217
|
+
if (context.environment) {
|
|
218
|
+
const normalizedEnv = context.environment.toLowerCase();
|
|
219
|
+
if (policy.protectedEnvironments.some(env => normalizedEnv.includes(env.toLowerCase()))) {
|
|
220
|
+
const risk: Risk = {
|
|
221
|
+
id: 'protected-environment',
|
|
222
|
+
severity: 'high',
|
|
223
|
+
message: `Operating on protected environment: ${context.environment}`,
|
|
224
|
+
canProceed: true,
|
|
225
|
+
requiresApproval: true,
|
|
226
|
+
};
|
|
227
|
+
risks.push(risk);
|
|
228
|
+
requiresApprovalFlag = true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check cost threshold
|
|
233
|
+
if (context.estimatedCost && context.estimatedCost > policy.costThreshold) {
|
|
234
|
+
const risk: Risk = {
|
|
235
|
+
id: 'high-cost',
|
|
236
|
+
severity: 'high',
|
|
237
|
+
message: `Estimated cost $${context.estimatedCost} exceeds threshold $${policy.costThreshold}`,
|
|
238
|
+
details: {
|
|
239
|
+
estimatedCost: context.estimatedCost,
|
|
240
|
+
threshold: policy.costThreshold,
|
|
241
|
+
},
|
|
242
|
+
canProceed: true,
|
|
243
|
+
requiresApproval: true,
|
|
244
|
+
};
|
|
245
|
+
risks.push(risk);
|
|
246
|
+
requiresApprovalFlag = true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check for mutations in apply
|
|
250
|
+
if (context.operation.toLowerCase().includes('apply')) {
|
|
251
|
+
const risk: Risk = {
|
|
252
|
+
id: 'mutation-operation',
|
|
253
|
+
severity: 'medium',
|
|
254
|
+
message: 'This operation will modify infrastructure',
|
|
255
|
+
canProceed: true,
|
|
256
|
+
requiresApproval: true,
|
|
257
|
+
};
|
|
258
|
+
risks.push(risk);
|
|
259
|
+
requiresApprovalFlag = true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check custom rules
|
|
263
|
+
if (policy.customRules) {
|
|
264
|
+
for (const rule of policy.customRules) {
|
|
265
|
+
try {
|
|
266
|
+
if (rule.check(context)) {
|
|
267
|
+
const risk: Risk = {
|
|
268
|
+
id: rule.id,
|
|
269
|
+
severity: rule.severity,
|
|
270
|
+
message: rule.message,
|
|
271
|
+
canProceed: rule.severity !== 'critical',
|
|
272
|
+
requiresApproval: rule.severity === 'critical' || rule.severity === 'high',
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
if (!risk.canProceed) {
|
|
276
|
+
blockers.push(risk);
|
|
277
|
+
} else {
|
|
278
|
+
risks.push(risk);
|
|
279
|
+
if (risk.requiresApproval) {
|
|
280
|
+
requiresApprovalFlag = true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
// Skip rule on error
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Analyze plan output for resource changes
|
|
291
|
+
if (context.planOutput) {
|
|
292
|
+
const changes = analyzePlanOutput(context.planOutput);
|
|
293
|
+
if (changes.destroy > 0) {
|
|
294
|
+
const risk: Risk = {
|
|
295
|
+
id: 'resource-destruction',
|
|
296
|
+
severity: 'high',
|
|
297
|
+
message: `${changes.destroy} resources will be destroyed`,
|
|
298
|
+
details: { count: changes.destroy },
|
|
299
|
+
canProceed: true,
|
|
300
|
+
requiresApproval: true,
|
|
301
|
+
};
|
|
302
|
+
risks.push(risk);
|
|
303
|
+
requiresApprovalFlag = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
passed: blockers.length === 0,
|
|
309
|
+
risks,
|
|
310
|
+
blockers,
|
|
311
|
+
requiresApproval: requiresApprovalFlag,
|
|
312
|
+
estimatedCost: context.estimatedCost,
|
|
313
|
+
affectedResources: context.resources,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Analyze Terraform plan output for changes
|
|
319
|
+
*/
|
|
320
|
+
function analyzePlanOutput(output: string): { add: number; change: number; destroy: number } {
|
|
321
|
+
const addMatch = output.match(/(\d+) to add/);
|
|
322
|
+
const changeMatch = output.match(/(\d+) to change/);
|
|
323
|
+
const destroyMatch = output.match(/(\d+) to destroy/);
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
add: addMatch ? parseInt(addMatch[1], 10) : 0,
|
|
327
|
+
change: changeMatch ? parseInt(changeMatch[1], 10) : 0,
|
|
328
|
+
destroy: destroyMatch ? parseInt(destroyMatch[1], 10) : 0,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Format risks for display
|
|
334
|
+
*/
|
|
335
|
+
export function formatRisks(risks: Risk[]): string[] {
|
|
336
|
+
return risks.map(risk => {
|
|
337
|
+
const icon = getSeverityIcon(risk.severity);
|
|
338
|
+
return `${icon} [${risk.severity.toUpperCase()}] ${risk.message}`;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get icon for severity level
|
|
344
|
+
*/
|
|
345
|
+
function getSeverityIcon(severity: RiskSeverity): string {
|
|
346
|
+
switch (severity) {
|
|
347
|
+
case 'critical':
|
|
348
|
+
return '🔴';
|
|
349
|
+
case 'high':
|
|
350
|
+
return '🟠';
|
|
351
|
+
case 'medium':
|
|
352
|
+
return '🟡';
|
|
353
|
+
case 'low':
|
|
354
|
+
return '🔵';
|
|
355
|
+
default:
|
|
356
|
+
return '⚪';
|
|
357
|
+
}
|
|
358
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod Config Schema
|
|
3
|
+
*
|
|
4
|
+
* Validates NimbusConfig at load time and on set() operations.
|
|
5
|
+
* Uses safeParse to gracefully handle invalid configs without crashing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
|
|
10
|
+
export const WorkspaceConfigSchema = z.object({
|
|
11
|
+
defaultProvider: z.string().optional(),
|
|
12
|
+
outputDirectory: z.string().optional(),
|
|
13
|
+
name: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const CostOptimizationConfigSchema = z.object({
|
|
17
|
+
enabled: z.boolean().optional(),
|
|
18
|
+
cheap_model: z.string().optional(),
|
|
19
|
+
expensive_model: z.string().optional(),
|
|
20
|
+
// A5/A6: task-category routing arrays
|
|
21
|
+
use_cheap_model_for: z.array(z.string()).optional(),
|
|
22
|
+
use_expensive_model_for: z.array(z.string()).optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// A3/A4: per-provider configuration map entry
|
|
26
|
+
export const ProviderConfigSchema = z.object({
|
|
27
|
+
api_key: z.string().optional(),
|
|
28
|
+
base_url: z.string().url().optional(),
|
|
29
|
+
models: z.array(z.string()).optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// A7/A8: provider fallback configuration
|
|
33
|
+
export const FallbackConfigSchema = z.object({
|
|
34
|
+
enabled: z.boolean().optional(),
|
|
35
|
+
providers: z.array(z.string()).optional(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const LLMConfigSchema = z.object({
|
|
39
|
+
defaultModel: z.string().optional(),
|
|
40
|
+
// A2: explicit default provider name
|
|
41
|
+
default_provider: z.string().optional(),
|
|
42
|
+
temperature: z.number().min(0).max(1).optional(),
|
|
43
|
+
maxTokens: z.number().positive().optional(),
|
|
44
|
+
cost_optimization: CostOptimizationConfigSchema.optional(),
|
|
45
|
+
// A3/A4: per-provider configuration keyed by provider name
|
|
46
|
+
providers: z.record(z.string(), ProviderConfigSchema).optional(),
|
|
47
|
+
// A7/A8: fallback provider chain
|
|
48
|
+
fallback: FallbackConfigSchema.optional(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const HistoryConfigSchema = z.object({
|
|
52
|
+
maxEntries: z.number().positive().optional(),
|
|
53
|
+
enabled: z.boolean().optional(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const AutoApproveConfigSchema = z.object({
|
|
57
|
+
read: z.boolean().optional(),
|
|
58
|
+
generate: z.boolean().optional(),
|
|
59
|
+
create: z.boolean().optional(),
|
|
60
|
+
update: z.boolean().optional(),
|
|
61
|
+
delete: z.boolean().optional(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const SafetyConfigSchema = z.object({
|
|
65
|
+
requireConfirmation: z.boolean().optional(),
|
|
66
|
+
dryRunByDefault: z.boolean().optional(),
|
|
67
|
+
auto_approve: AutoApproveConfigSchema.optional(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const UIConfigSchema = z.object({
|
|
71
|
+
theme: z.enum(['dark', 'light', 'auto']).optional(),
|
|
72
|
+
colors: z.boolean().optional(),
|
|
73
|
+
spinner: z.enum(['dots', 'line', 'simple']).optional(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export const PersonaConfigSchema = z.object({
|
|
77
|
+
// A9: added 'custom' to the mode enum
|
|
78
|
+
mode: z
|
|
79
|
+
.enum(['professional', 'assistant', 'expert', 'standard', 'concise', 'detailed', 'custom'])
|
|
80
|
+
.optional(),
|
|
81
|
+
verbosity: z.enum(['minimal', 'normal', 'detailed', 'verbose']).optional(),
|
|
82
|
+
custom: z.string().optional(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export const CloudProviderConfigSchema = z.object({
|
|
86
|
+
default_region: z.string().optional(),
|
|
87
|
+
default_profile: z.string().optional(),
|
|
88
|
+
default_project: z.string().optional(),
|
|
89
|
+
default_subscription: z.string().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const CloudConfigSchema = z.object({
|
|
93
|
+
default_provider: z.enum(['aws', 'gcp', 'azure']).optional(),
|
|
94
|
+
aws: CloudProviderConfigSchema.optional(),
|
|
95
|
+
gcp: CloudProviderConfigSchema.optional(),
|
|
96
|
+
azure: CloudProviderConfigSchema.optional(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const TerraformDefaultsSchema = z.object({
|
|
100
|
+
default_backend: z.enum(['s3', 'gcs', 'azurerm', 'local']).optional(),
|
|
101
|
+
state_bucket: z.string().optional(),
|
|
102
|
+
lock_table: z.string().optional(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const KubernetesDefaultsSchema = z.object({
|
|
106
|
+
default_context: z.string().optional(),
|
|
107
|
+
default_namespace: z.string().optional(),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export const NimbusConfigSchema = z.object({
|
|
111
|
+
version: z.number().optional(),
|
|
112
|
+
// A1: opt-in/out of anonymous telemetry
|
|
113
|
+
telemetry: z.boolean().optional(),
|
|
114
|
+
workspace: WorkspaceConfigSchema.optional(),
|
|
115
|
+
llm: LLMConfigSchema.optional(),
|
|
116
|
+
history: HistoryConfigSchema.optional(),
|
|
117
|
+
safety: SafetyConfigSchema.optional(),
|
|
118
|
+
ui: UIConfigSchema.optional(),
|
|
119
|
+
persona: PersonaConfigSchema.optional(),
|
|
120
|
+
cloud: CloudConfigSchema.optional(),
|
|
121
|
+
terraform: TerraformDefaultsSchema.optional(),
|
|
122
|
+
kubernetes: KubernetesDefaultsSchema.optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export type ValidatedNimbusConfig = z.infer<typeof NimbusConfigSchema>;
|