@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,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy Preview
|
|
3
|
+
*
|
|
4
|
+
* Generates dry-run previews for infrastructure changes with
|
|
5
|
+
* blast radius analysis. Shows what will be created, modified,
|
|
6
|
+
* or destroyed before the user approves execution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { exec } from 'node:child_process';
|
|
10
|
+
import { promisify } from 'node:util';
|
|
11
|
+
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
/** A single resource change in the preview */
|
|
15
|
+
export interface ResourceChange {
|
|
16
|
+
/** Resource type and name (e.g., "aws_instance.web") */
|
|
17
|
+
resource: string;
|
|
18
|
+
/** Change action */
|
|
19
|
+
action: 'create' | 'update' | 'destroy' | 'replace' | 'read' | 'no-op';
|
|
20
|
+
/** Brief description of what changes */
|
|
21
|
+
details?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The full deploy preview result */
|
|
25
|
+
export interface DeployPreview {
|
|
26
|
+
/** The deployment tool (terraform/kubectl/helm) */
|
|
27
|
+
tool: string;
|
|
28
|
+
/** The action being previewed */
|
|
29
|
+
action: string;
|
|
30
|
+
/** Working directory */
|
|
31
|
+
workdir: string;
|
|
32
|
+
/** Individual resource changes */
|
|
33
|
+
changes: ResourceChange[];
|
|
34
|
+
/** Summary counts */
|
|
35
|
+
summary: {
|
|
36
|
+
toCreate: number;
|
|
37
|
+
toUpdate: number;
|
|
38
|
+
toDestroy: number;
|
|
39
|
+
toReplace: number;
|
|
40
|
+
unchanged: number;
|
|
41
|
+
};
|
|
42
|
+
/** Raw plan output */
|
|
43
|
+
rawOutput: string;
|
|
44
|
+
/** Estimated cost impact (if available) */
|
|
45
|
+
costImpact?: string;
|
|
46
|
+
/** Whether the preview succeeded */
|
|
47
|
+
success: boolean;
|
|
48
|
+
/** Error message if preview failed */
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate a deploy preview for the given action.
|
|
54
|
+
*
|
|
55
|
+
* Dispatches to the appropriate provider-specific preview generator
|
|
56
|
+
* based on the action keyword (terraform, kubectl/kubernetes, or helm).
|
|
57
|
+
*
|
|
58
|
+
* @param action - The action keyword (e.g., "terraform apply", "kubectl apply", "helm install")
|
|
59
|
+
* @param workdir - Working directory containing the IaC files
|
|
60
|
+
* @returns DeployPreview result
|
|
61
|
+
*/
|
|
62
|
+
export async function generateDeployPreview(
|
|
63
|
+
action: string,
|
|
64
|
+
workdir: string
|
|
65
|
+
): Promise<DeployPreview> {
|
|
66
|
+
const actionLower = action.toLowerCase();
|
|
67
|
+
|
|
68
|
+
if (actionLower.includes('terraform')) {
|
|
69
|
+
return generateTerraformPreview(workdir);
|
|
70
|
+
}
|
|
71
|
+
if (actionLower.includes('kubectl') || actionLower.includes('kubernetes')) {
|
|
72
|
+
return generateKubectlPreview(action, workdir);
|
|
73
|
+
}
|
|
74
|
+
if (actionLower.includes('helm')) {
|
|
75
|
+
return generateHelmPreview(action, workdir);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
tool: 'unknown',
|
|
80
|
+
action,
|
|
81
|
+
workdir,
|
|
82
|
+
changes: [],
|
|
83
|
+
summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
|
|
84
|
+
rawOutput: '',
|
|
85
|
+
success: false,
|
|
86
|
+
error: `Unsupported action for deploy preview: ${action}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Format a deploy preview into a human-readable string.
|
|
92
|
+
*
|
|
93
|
+
* Produces a summary block with change counts, a detailed change list
|
|
94
|
+
* using +/~/- symbols, optional cost impact, and a blast-radius warning
|
|
95
|
+
* when destructive operations are present.
|
|
96
|
+
*
|
|
97
|
+
* @param preview - The deploy preview to format
|
|
98
|
+
* @returns Formatted multi-line string suitable for terminal display
|
|
99
|
+
*/
|
|
100
|
+
export function formatDeployPreview(preview: DeployPreview): string {
|
|
101
|
+
if (!preview.success) {
|
|
102
|
+
return `Deploy Preview Failed\n${'='.repeat(40)}\n\nError: ${preview.error}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const lines: string[] = [];
|
|
106
|
+
lines.push(`Deploy Preview: ${preview.tool} ${preview.action}`);
|
|
107
|
+
lines.push('='.repeat(50));
|
|
108
|
+
lines.push(`Working directory: ${preview.workdir}`);
|
|
109
|
+
lines.push('');
|
|
110
|
+
|
|
111
|
+
// Summary
|
|
112
|
+
const { toCreate, toUpdate, toDestroy, toReplace, unchanged } = preview.summary;
|
|
113
|
+
lines.push('Summary:');
|
|
114
|
+
if (toCreate > 0) {
|
|
115
|
+
lines.push(` + ${toCreate} to create`);
|
|
116
|
+
}
|
|
117
|
+
if (toUpdate > 0) {
|
|
118
|
+
lines.push(` ~ ${toUpdate} to update`);
|
|
119
|
+
}
|
|
120
|
+
if (toReplace > 0) {
|
|
121
|
+
lines.push(` +/- ${toReplace} to replace`);
|
|
122
|
+
}
|
|
123
|
+
if (toDestroy > 0) {
|
|
124
|
+
lines.push(` - ${toDestroy} to destroy`);
|
|
125
|
+
}
|
|
126
|
+
if (unchanged > 0) {
|
|
127
|
+
lines.push(` = ${unchanged} unchanged`);
|
|
128
|
+
}
|
|
129
|
+
lines.push('');
|
|
130
|
+
|
|
131
|
+
// Detailed changes
|
|
132
|
+
if (preview.changes.length > 0) {
|
|
133
|
+
lines.push('Changes:');
|
|
134
|
+
for (const change of preview.changes) {
|
|
135
|
+
const symbol = getChangeSymbol(change.action);
|
|
136
|
+
const detail = change.details ? ` (${change.details})` : '';
|
|
137
|
+
lines.push(` ${symbol} ${change.resource}${detail}`);
|
|
138
|
+
}
|
|
139
|
+
lines.push('');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Cost impact
|
|
143
|
+
if (preview.costImpact) {
|
|
144
|
+
lines.push(`Cost Impact: ${preview.costImpact}`);
|
|
145
|
+
lines.push('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Blast radius warning
|
|
149
|
+
if (toDestroy > 0) {
|
|
150
|
+
lines.push('WARNING: This operation will DESTROY resources.');
|
|
151
|
+
lines.push('');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return lines.join('\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------
|
|
158
|
+
// Provider-specific preview generators
|
|
159
|
+
// ---------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Run `terraform plan` and parse the human-readable output into
|
|
163
|
+
* structured {@link ResourceChange} entries.
|
|
164
|
+
*/
|
|
165
|
+
async function generateTerraformPreview(workdir: string): Promise<DeployPreview> {
|
|
166
|
+
const base = {
|
|
167
|
+
tool: 'terraform',
|
|
168
|
+
action: 'apply',
|
|
169
|
+
workdir,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// Run terraform plan with JSON output
|
|
174
|
+
const { stdout, stderr } = await execAsync(
|
|
175
|
+
'terraform plan -no-color -detailed-exitcode 2>&1 || true',
|
|
176
|
+
{ cwd: workdir, timeout: 300_000, maxBuffer: 10 * 1024 * 1024 }
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const output = stdout || stderr;
|
|
180
|
+
const changes = parseTerraformPlanOutput(output);
|
|
181
|
+
const summary = summarizeChanges(changes);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
...base,
|
|
185
|
+
changes,
|
|
186
|
+
summary,
|
|
187
|
+
rawOutput: output,
|
|
188
|
+
success: true,
|
|
189
|
+
};
|
|
190
|
+
} catch (error: unknown) {
|
|
191
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
192
|
+
return {
|
|
193
|
+
...base,
|
|
194
|
+
changes: [],
|
|
195
|
+
summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
|
|
196
|
+
rawOutput: '',
|
|
197
|
+
success: false,
|
|
198
|
+
error: msg,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Run `kubectl diff` against the current cluster state and parse
|
|
205
|
+
* the unified-diff output into {@link ResourceChange} entries.
|
|
206
|
+
*/
|
|
207
|
+
async function generateKubectlPreview(action: string, workdir: string): Promise<DeployPreview> {
|
|
208
|
+
const base = {
|
|
209
|
+
tool: 'kubectl',
|
|
210
|
+
action,
|
|
211
|
+
workdir,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const { stdout } = await execAsync('kubectl diff -f . 2>&1 || true', {
|
|
216
|
+
cwd: workdir,
|
|
217
|
+
timeout: 60_000,
|
|
218
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const changes = parseKubectlDiffOutput(stdout);
|
|
222
|
+
const summary = summarizeChanges(changes);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
...base,
|
|
226
|
+
changes,
|
|
227
|
+
summary,
|
|
228
|
+
rawOutput: stdout,
|
|
229
|
+
success: true,
|
|
230
|
+
};
|
|
231
|
+
} catch (error: unknown) {
|
|
232
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
233
|
+
return {
|
|
234
|
+
...base,
|
|
235
|
+
changes: [],
|
|
236
|
+
summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
|
|
237
|
+
rawOutput: '',
|
|
238
|
+
success: false,
|
|
239
|
+
error: msg,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Run `helm template` to render the chart locally and report the
|
|
246
|
+
* rendered manifests as a single create change.
|
|
247
|
+
*/
|
|
248
|
+
async function generateHelmPreview(action: string, workdir: string): Promise<DeployPreview> {
|
|
249
|
+
const base = {
|
|
250
|
+
tool: 'helm',
|
|
251
|
+
action,
|
|
252
|
+
workdir,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// Use helm template to show what would be deployed
|
|
257
|
+
const { stdout } = await execAsync('helm template . 2>&1', {
|
|
258
|
+
cwd: workdir,
|
|
259
|
+
timeout: 60_000,
|
|
260
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
...base,
|
|
265
|
+
changes: [{ resource: 'helm-chart', action: 'create', details: 'Template rendered' }],
|
|
266
|
+
summary: { toCreate: 1, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
|
|
267
|
+
rawOutput: stdout,
|
|
268
|
+
success: true,
|
|
269
|
+
};
|
|
270
|
+
} catch (error: unknown) {
|
|
271
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
272
|
+
return {
|
|
273
|
+
...base,
|
|
274
|
+
changes: [],
|
|
275
|
+
summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
|
|
276
|
+
rawOutput: '',
|
|
277
|
+
success: false,
|
|
278
|
+
error: msg,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------
|
|
284
|
+
// Output parsers
|
|
285
|
+
// ---------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Parse the human-readable `terraform plan` output into structured
|
|
289
|
+
* {@link ResourceChange} entries.
|
|
290
|
+
*
|
|
291
|
+
* Recognises lines such as:
|
|
292
|
+
* `# aws_instance.web will be created`
|
|
293
|
+
* `# aws_instance.web will be updated in-place`
|
|
294
|
+
* `# aws_instance.web will be destroyed`
|
|
295
|
+
* `# aws_instance.web must be replaced`
|
|
296
|
+
*
|
|
297
|
+
* Falls back to the summary line (`Plan: X to add, Y to change, Z to destroy.`)
|
|
298
|
+
* when no individual resource lines are found.
|
|
299
|
+
*/
|
|
300
|
+
function parseTerraformPlanOutput(output: string): ResourceChange[] {
|
|
301
|
+
const changes: ResourceChange[] = [];
|
|
302
|
+
const lines = output.split('\n');
|
|
303
|
+
|
|
304
|
+
for (const line of lines) {
|
|
305
|
+
// Match lines like: " # aws_instance.web will be created"
|
|
306
|
+
const createMatch = line.match(/^\s*#\s+(\S+)\s+will be created/);
|
|
307
|
+
if (createMatch) {
|
|
308
|
+
changes.push({ resource: createMatch[1], action: 'create' });
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const updateMatch = line.match(/^\s*#\s+(\S+)\s+will be updated/);
|
|
313
|
+
if (updateMatch) {
|
|
314
|
+
changes.push({ resource: updateMatch[1], action: 'update' });
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const destroyMatch = line.match(/^\s*#\s+(\S+)\s+will be destroyed/);
|
|
319
|
+
if (destroyMatch) {
|
|
320
|
+
changes.push({ resource: destroyMatch[1], action: 'destroy' });
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const replaceMatch = line.match(/^\s*#\s+(\S+)\s+must be replaced/);
|
|
325
|
+
if (replaceMatch) {
|
|
326
|
+
changes.push({ resource: replaceMatch[1], action: 'replace' });
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Also match: "Plan: X to add, Y to change, Z to destroy."
|
|
331
|
+
const summaryMatch = line.match(
|
|
332
|
+
/Plan:\s+(\d+)\s+to add,\s+(\d+)\s+to change,\s+(\d+)\s+to destroy/
|
|
333
|
+
);
|
|
334
|
+
if (summaryMatch && changes.length === 0) {
|
|
335
|
+
// If we didn't find specific resources, create generic entries
|
|
336
|
+
const toAdd = parseInt(summaryMatch[1]);
|
|
337
|
+
const toChange = parseInt(summaryMatch[2]);
|
|
338
|
+
const toDestroy = parseInt(summaryMatch[3]);
|
|
339
|
+
|
|
340
|
+
for (let i = 0; i < toAdd; i++) {
|
|
341
|
+
changes.push({ resource: `resource_${i + 1}`, action: 'create' });
|
|
342
|
+
}
|
|
343
|
+
for (let i = 0; i < toChange; i++) {
|
|
344
|
+
changes.push({ resource: `resource_${toAdd + i + 1}`, action: 'update' });
|
|
345
|
+
}
|
|
346
|
+
for (let i = 0; i < toDestroy; i++) {
|
|
347
|
+
changes.push({ resource: `resource_${toAdd + toChange + i + 1}`, action: 'destroy' });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return changes;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Parse `kubectl diff` unified-diff output into structured
|
|
357
|
+
* {@link ResourceChange} entries.
|
|
358
|
+
*
|
|
359
|
+
* Each `diff -u` header is treated as a separate resource update.
|
|
360
|
+
* An empty or "no changes" output yields an empty array.
|
|
361
|
+
*/
|
|
362
|
+
function parseKubectlDiffOutput(output: string): ResourceChange[] {
|
|
363
|
+
const changes: ResourceChange[] = [];
|
|
364
|
+
const resourceRegex = /^diff -u.*\/(\S+)/gm;
|
|
365
|
+
let match;
|
|
366
|
+
|
|
367
|
+
while ((match = resourceRegex.exec(output)) !== null) {
|
|
368
|
+
changes.push({ resource: match[1], action: 'update' });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// If output is empty, no changes
|
|
372
|
+
if (output.trim() === '' || output.includes('no changes')) {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// If we found no specific resources but have output, assume updates
|
|
377
|
+
if (changes.length === 0 && output.trim().length > 0) {
|
|
378
|
+
changes.push({ resource: 'kubernetes-resources', action: 'update' });
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return changes;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Aggregate an array of {@link ResourceChange} entries into the
|
|
386
|
+
* summary counts used by {@link DeployPreview}.
|
|
387
|
+
*/
|
|
388
|
+
function summarizeChanges(changes: ResourceChange[]): DeployPreview['summary'] {
|
|
389
|
+
return {
|
|
390
|
+
toCreate: changes.filter(c => c.action === 'create').length,
|
|
391
|
+
toUpdate: changes.filter(c => c.action === 'update').length,
|
|
392
|
+
toDestroy: changes.filter(c => c.action === 'destroy').length,
|
|
393
|
+
toReplace: changes.filter(c => c.action === 'replace').length,
|
|
394
|
+
unchanged: changes.filter(c => c.action === 'no-op').length,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Map a {@link ResourceChange} action to a single-character symbol
|
|
400
|
+
* for display in the formatted preview.
|
|
401
|
+
*
|
|
402
|
+
* | Action | Symbol |
|
|
403
|
+
* |-----------|--------|
|
|
404
|
+
* | create | `+` |
|
|
405
|
+
* | update | `~` |
|
|
406
|
+
* | destroy | `-` |
|
|
407
|
+
* | replace | `+/-` |
|
|
408
|
+
* | read | `>` |
|
|
409
|
+
* | no-op | `=` |
|
|
410
|
+
*/
|
|
411
|
+
function getChangeSymbol(action: ResourceChange['action']): string {
|
|
412
|
+
switch (action) {
|
|
413
|
+
case 'create':
|
|
414
|
+
return '+';
|
|
415
|
+
case 'update':
|
|
416
|
+
return '~';
|
|
417
|
+
case 'destroy':
|
|
418
|
+
return '-';
|
|
419
|
+
case 'replace':
|
|
420
|
+
return '+/-';
|
|
421
|
+
case 'read':
|
|
422
|
+
return '>';
|
|
423
|
+
case 'no-op':
|
|
424
|
+
return '=';
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent System — Barrel re-exports
|
|
3
|
+
*
|
|
4
|
+
* Core agentic loop, system prompt builder, permission engine,
|
|
5
|
+
* context manager, and subagent system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Core agentic loop
|
|
9
|
+
export {
|
|
10
|
+
type AgentLoopOptions,
|
|
11
|
+
type AgentLoopResult,
|
|
12
|
+
type ToolCallInfo,
|
|
13
|
+
runAgentLoop,
|
|
14
|
+
getToolsForMode,
|
|
15
|
+
} from './loop';
|
|
16
|
+
|
|
17
|
+
// System prompt builder
|
|
18
|
+
export {
|
|
19
|
+
type AgentMode,
|
|
20
|
+
type SystemPromptOptions,
|
|
21
|
+
buildSystemPrompt,
|
|
22
|
+
loadNimbusMd,
|
|
23
|
+
} from './system-prompt';
|
|
24
|
+
|
|
25
|
+
// Permission engine
|
|
26
|
+
export {
|
|
27
|
+
type PermissionDecision,
|
|
28
|
+
type PermissionContext,
|
|
29
|
+
type PermissionSessionState,
|
|
30
|
+
type PermissionConfig,
|
|
31
|
+
checkPermission,
|
|
32
|
+
createPermissionState,
|
|
33
|
+
approveForSession,
|
|
34
|
+
approveActionForSession,
|
|
35
|
+
} from './permissions';
|
|
36
|
+
|
|
37
|
+
// Context manager — @file/@folder reference resolution
|
|
38
|
+
export {
|
|
39
|
+
type FileReference,
|
|
40
|
+
type ContextOptions,
|
|
41
|
+
type ContextResult,
|
|
42
|
+
resolveReferences,
|
|
43
|
+
buildContextInjection,
|
|
44
|
+
fuzzyFileSearch,
|
|
45
|
+
} from './context';
|
|
46
|
+
|
|
47
|
+
// Deploy preview system
|
|
48
|
+
export {
|
|
49
|
+
type ResourceChange,
|
|
50
|
+
type DeployPreview,
|
|
51
|
+
generateDeployPreview,
|
|
52
|
+
formatDeployPreview,
|
|
53
|
+
} from './deploy-preview';
|
|
54
|
+
|
|
55
|
+
// Subagent system
|
|
56
|
+
export {
|
|
57
|
+
Subagent,
|
|
58
|
+
type SubagentConfig,
|
|
59
|
+
type SubagentResult,
|
|
60
|
+
type SubagentType,
|
|
61
|
+
createSubagent,
|
|
62
|
+
parseAgentMention,
|
|
63
|
+
createExploreSubagent,
|
|
64
|
+
createInfraSubagent,
|
|
65
|
+
createSecuritySubagent,
|
|
66
|
+
createCostSubagent,
|
|
67
|
+
createGeneralSubagent,
|
|
68
|
+
} from './subagents';
|