@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.
Files changed (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * GCP GKE CLI Commands
3
+ *
4
+ * Operations for Google Kubernetes Engine clusters
5
+ */
6
+
7
+ import { execFile } from 'child_process';
8
+ import { promisify } from 'util';
9
+ import { logger } from '../../utils';
10
+ import { ui } from '../../wizard/ui';
11
+ import {
12
+ loadSafetyPolicy,
13
+ evaluateSafety,
14
+ type SafetyContext,
15
+ type SafetyCheckResult,
16
+ } from '../../config/safety-policy';
17
+ import { promptForApproval, displaySafetySummary } from '../../wizard/approval';
18
+ import type { GcpCommandOptions } from './index';
19
+
20
+ const execFileAsync = promisify(execFile);
21
+
22
+ /**
23
+ * Run GKE safety checks
24
+ */
25
+ async function runGkeSafetyChecks(
26
+ action: string,
27
+ clusterName: string,
28
+ options: GcpCommandOptions
29
+ ): Promise<SafetyCheckResult> {
30
+ const safetyPolicy = loadSafetyPolicy();
31
+
32
+ const context: SafetyContext = {
33
+ operation: action,
34
+ type: 'gcp',
35
+ environment: options.project || 'default',
36
+ resources: [clusterName],
37
+ metadata: {
38
+ resourceType: 'gke-cluster',
39
+ resourceId: clusterName,
40
+ },
41
+ };
42
+
43
+ return evaluateSafety(context, safetyPolicy);
44
+ }
45
+
46
+ /**
47
+ * Main GKE command router
48
+ */
49
+ export async function gkeCommand(
50
+ action: string,
51
+ args: string[],
52
+ options: GcpCommandOptions
53
+ ): Promise<void> {
54
+ logger.info('Running GCP GKE command', { action, args, options });
55
+
56
+ switch (action) {
57
+ case 'clusters':
58
+ case 'list':
59
+ await listClusters(options);
60
+ break;
61
+
62
+ case 'describe':
63
+ if (!args[0]) {
64
+ ui.error('Cluster name required');
65
+ return;
66
+ }
67
+ await describeCluster(args[0], options);
68
+ break;
69
+
70
+ case 'get-credentials':
71
+ if (!args[0]) {
72
+ ui.error('Cluster name required');
73
+ return;
74
+ }
75
+ await getCredentials(args[0], options);
76
+ break;
77
+
78
+ case 'node-pools':
79
+ if (!args[0]) {
80
+ ui.error('Cluster name required');
81
+ return;
82
+ }
83
+ await listNodePools(args[0], options);
84
+ break;
85
+
86
+ case 'resize':
87
+ if (args.length < 2) {
88
+ ui.error('Cluster name and node count required');
89
+ return;
90
+ }
91
+ await resizeCluster(args[0], parseInt(args[1], 10), options);
92
+ break;
93
+
94
+ case 'delete':
95
+ if (!args[0]) {
96
+ ui.error('Cluster name required');
97
+ return;
98
+ }
99
+ await deleteCluster(args[0], options);
100
+ break;
101
+
102
+ default:
103
+ showGkeHelp();
104
+ break;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * List GKE clusters
110
+ */
111
+ async function listClusters(options: GcpCommandOptions): Promise<void> {
112
+ ui.header('GKE Clusters');
113
+ ui.newLine();
114
+
115
+ const gcloudArgs = ['container', 'clusters', 'list', '--format=json'];
116
+ if (options.project) {
117
+ gcloudArgs.push(`--project=${options.project}`);
118
+ }
119
+ if (options.region) {
120
+ gcloudArgs.push(`--region=${options.region}`);
121
+ }
122
+ if (options.zone) {
123
+ gcloudArgs.push(`--zone=${options.zone}`);
124
+ }
125
+
126
+ try {
127
+ ui.startSpinner({ message: 'Fetching clusters...' });
128
+ const { stdout } = await execFileAsync('gcloud', gcloudArgs);
129
+ ui.stopSpinnerSuccess('Clusters fetched');
130
+
131
+ const clusters = JSON.parse(stdout || '[]');
132
+
133
+ if (clusters.length === 0) {
134
+ ui.info('No clusters found');
135
+ return;
136
+ }
137
+
138
+ ui.print(`Found ${clusters.length} cluster(s)\n`);
139
+
140
+ // Display table
141
+ ui.print(
142
+ ui.color(
143
+ `${
144
+ 'Name'.padEnd(25) + 'Location'.padEnd(20) + 'Node Count'.padEnd(12) + 'Status'.padEnd(12)
145
+ }Version`,
146
+ 'cyan'
147
+ )
148
+ );
149
+ ui.print('─'.repeat(90));
150
+
151
+ for (const cluster of clusters) {
152
+ const name = cluster.name?.substring(0, 24) || '';
153
+ const location = cluster.location?.substring(0, 19) || '';
154
+ const nodeCount = String(cluster.currentNodeCount || 0);
155
+ const status = cluster.status || '';
156
+ const version = cluster.currentMasterVersion || '';
157
+
158
+ const statusColor =
159
+ status === 'RUNNING'
160
+ ? 'green'
161
+ : status === 'PROVISIONING'
162
+ ? 'yellow'
163
+ : status === 'ERROR'
164
+ ? 'red'
165
+ : 'white';
166
+
167
+ ui.print(
168
+ `${name.padEnd(25)}${location.padEnd(20)}${nodeCount.padEnd(12)}${ui.color(status.padEnd(12), statusColor as 'green' | 'yellow' | 'red' | 'white')}${version}`
169
+ );
170
+ }
171
+ } catch (error: unknown) {
172
+ ui.stopSpinnerFail('Failed to fetch clusters');
173
+ const message = error instanceof Error ? error.message : 'Unknown error';
174
+ logger.error('Failed to list clusters', { error: message });
175
+ ui.error(`Failed to list clusters: ${message}`);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Describe a specific cluster
181
+ */
182
+ async function describeCluster(clusterName: string, options: GcpCommandOptions): Promise<void> {
183
+ ui.header(`Cluster: ${clusterName}`);
184
+ ui.newLine();
185
+
186
+ const gcloudArgs = ['container', 'clusters', 'describe', clusterName, '--format=json'];
187
+ if (options.project) {
188
+ gcloudArgs.push(`--project=${options.project}`);
189
+ }
190
+ if (options.region) {
191
+ gcloudArgs.push(`--region=${options.region}`);
192
+ }
193
+ if (options.zone) {
194
+ gcloudArgs.push(`--zone=${options.zone}`);
195
+ }
196
+
197
+ try {
198
+ ui.startSpinner({ message: 'Fetching cluster details...' });
199
+ const { stdout } = await execFileAsync('gcloud', gcloudArgs);
200
+ ui.stopSpinnerSuccess('Details fetched');
201
+
202
+ const cluster = JSON.parse(stdout);
203
+
204
+ ui.print(ui.bold('Basic Information:'));
205
+ ui.print(` Name: ${cluster.name}`);
206
+ ui.print(` Location: ${cluster.location}`);
207
+ ui.print(` Status: ${cluster.status}`);
208
+ ui.print(` Master Version: ${cluster.currentMasterVersion}`);
209
+ ui.print(` Node Count: ${cluster.currentNodeCount}`);
210
+ ui.print(` Endpoint: ${cluster.endpoint}`);
211
+ ui.newLine();
212
+
213
+ ui.print(ui.bold('Node Config:'));
214
+ const nodeConfig = cluster.nodeConfig || {};
215
+ ui.print(` Machine Type: ${nodeConfig.machineType}`);
216
+ ui.print(` Disk Size: ${nodeConfig.diskSizeGb} GB`);
217
+ ui.print(` Disk Type: ${nodeConfig.diskType}`);
218
+ ui.newLine();
219
+
220
+ ui.print(ui.bold('Networking:'));
221
+ ui.print(` Network: ${cluster.network}`);
222
+ ui.print(` Subnetwork: ${cluster.subnetwork}`);
223
+ ui.print(` Cluster CIDR: ${cluster.clusterIpv4Cidr}`);
224
+ ui.print(` Services CIDR: ${cluster.servicesIpv4Cidr}`);
225
+ } catch (error: unknown) {
226
+ ui.stopSpinnerFail('Failed to fetch details');
227
+ const message = error instanceof Error ? error.message : 'Unknown error';
228
+ logger.error('Failed to describe cluster', { error: message });
229
+ ui.error(`Failed to describe cluster: ${message}`);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Get cluster credentials for kubectl
235
+ */
236
+ async function getCredentials(clusterName: string, options: GcpCommandOptions): Promise<void> {
237
+ const gcloudArgs = ['container', 'clusters', 'get-credentials', clusterName];
238
+ if (options.project) {
239
+ gcloudArgs.push(`--project=${options.project}`);
240
+ }
241
+ if (options.region) {
242
+ gcloudArgs.push(`--region=${options.region}`);
243
+ }
244
+ if (options.zone) {
245
+ gcloudArgs.push(`--zone=${options.zone}`);
246
+ }
247
+
248
+ try {
249
+ ui.startSpinner({ message: `Getting credentials for ${clusterName}...` });
250
+ await execFileAsync('gcloud', gcloudArgs);
251
+ ui.stopSpinnerSuccess(`Credentials configured for cluster ${clusterName}`);
252
+ ui.info('You can now use kubectl to interact with this cluster');
253
+ } catch (error: unknown) {
254
+ ui.stopSpinnerFail('Failed to get credentials');
255
+ const message = error instanceof Error ? error.message : 'Unknown error';
256
+ logger.error('Failed to get credentials', { error: message });
257
+ ui.error(`Failed to get credentials: ${message}`);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * List node pools in a cluster
263
+ */
264
+ async function listNodePools(clusterName: string, options: GcpCommandOptions): Promise<void> {
265
+ ui.header(`Node Pools in ${clusterName}`);
266
+ ui.newLine();
267
+
268
+ const gcloudArgs = ['container', 'node-pools', 'list', '--cluster', clusterName, '--format=json'];
269
+ if (options.project) {
270
+ gcloudArgs.push(`--project=${options.project}`);
271
+ }
272
+ if (options.region) {
273
+ gcloudArgs.push(`--region=${options.region}`);
274
+ }
275
+ if (options.zone) {
276
+ gcloudArgs.push(`--zone=${options.zone}`);
277
+ }
278
+
279
+ try {
280
+ ui.startSpinner({ message: 'Fetching node pools...' });
281
+ const { stdout } = await execFileAsync('gcloud', gcloudArgs);
282
+ ui.stopSpinnerSuccess('Node pools fetched');
283
+
284
+ const nodePools = JSON.parse(stdout || '[]');
285
+
286
+ if (nodePools.length === 0) {
287
+ ui.info('No node pools found');
288
+ return;
289
+ }
290
+
291
+ ui.print(`Found ${nodePools.length} node pool(s)\n`);
292
+
293
+ // Display table
294
+ ui.print(
295
+ ui.color(
296
+ `${'Name'.padEnd(25) + 'Machine Type'.padEnd(20) + 'Node Count'.padEnd(12)}Version`,
297
+ 'cyan'
298
+ )
299
+ );
300
+ ui.print('─'.repeat(75));
301
+
302
+ for (const pool of nodePools) {
303
+ const name = pool.name?.substring(0, 24) || '';
304
+ const machineType = pool.config?.machineType?.substring(0, 19) || '';
305
+ const nodeCount = String(pool.initialNodeCount || 0);
306
+ const version = pool.version || '';
307
+
308
+ ui.print(`${name.padEnd(25)}${machineType.padEnd(20)}${nodeCount.padEnd(12)}${version}`);
309
+ }
310
+ } catch (error: unknown) {
311
+ ui.stopSpinnerFail('Failed to fetch node pools');
312
+ const message = error instanceof Error ? error.message : 'Unknown error';
313
+ logger.error('Failed to list node pools', { error: message });
314
+ ui.error(`Failed to list node pools: ${message}`);
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Resize cluster node pool
320
+ */
321
+ async function resizeCluster(
322
+ clusterName: string,
323
+ nodeCount: number,
324
+ options: GcpCommandOptions
325
+ ): Promise<void> {
326
+ // Run safety checks
327
+ const safetyResult = await runGkeSafetyChecks('resize', clusterName, options);
328
+
329
+ displaySafetySummary({
330
+ operation: `resize ${clusterName} to ${nodeCount} nodes`,
331
+ risks: safetyResult.risks,
332
+ passed: safetyResult.passed,
333
+ });
334
+
335
+ if (safetyResult.requiresApproval) {
336
+ const result = await promptForApproval({
337
+ title: 'Resize GKE Cluster',
338
+ operation: `gcloud container clusters resize ${clusterName} --num-nodes=${nodeCount}`,
339
+ risks: safetyResult.risks,
340
+ });
341
+
342
+ if (!result.approved) {
343
+ ui.warning('Operation cancelled');
344
+ return;
345
+ }
346
+ }
347
+
348
+ const gcloudArgs = [
349
+ 'container',
350
+ 'clusters',
351
+ 'resize',
352
+ clusterName,
353
+ `--num-nodes=${nodeCount}`,
354
+ '--quiet',
355
+ ];
356
+ if (options.project) {
357
+ gcloudArgs.push(`--project=${options.project}`);
358
+ }
359
+ if (options.region) {
360
+ gcloudArgs.push(`--region=${options.region}`);
361
+ }
362
+ if (options.zone) {
363
+ gcloudArgs.push(`--zone=${options.zone}`);
364
+ }
365
+
366
+ try {
367
+ ui.startSpinner({ message: `Resizing cluster ${clusterName} to ${nodeCount} nodes...` });
368
+ await execFileAsync('gcloud', gcloudArgs);
369
+ ui.stopSpinnerSuccess(`Cluster ${clusterName} resized to ${nodeCount} nodes`);
370
+ } catch (error: unknown) {
371
+ ui.stopSpinnerFail('Failed to resize cluster');
372
+ const message = error instanceof Error ? error.message : 'Unknown error';
373
+ logger.error('Failed to resize cluster', { error: message });
374
+ ui.error(`Failed to resize cluster: ${message}`);
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Delete a cluster (requires safety approval)
380
+ */
381
+ async function deleteCluster(clusterName: string, options: GcpCommandOptions): Promise<void> {
382
+ // Run safety checks
383
+ const safetyResult = await runGkeSafetyChecks('delete', clusterName, options);
384
+
385
+ displaySafetySummary({
386
+ operation: `delete cluster ${clusterName}`,
387
+ risks: safetyResult.risks,
388
+ passed: safetyResult.passed,
389
+ });
390
+
391
+ if (safetyResult.requiresApproval) {
392
+ const result = await promptForApproval({
393
+ title: 'Delete GKE Cluster',
394
+ operation: `gcloud container clusters delete ${clusterName}`,
395
+ risks: safetyResult.risks,
396
+ });
397
+
398
+ if (!result.approved) {
399
+ ui.warning('Operation cancelled');
400
+ return;
401
+ }
402
+ }
403
+
404
+ const gcloudArgs = ['container', 'clusters', 'delete', clusterName, '--quiet'];
405
+ if (options.project) {
406
+ gcloudArgs.push(`--project=${options.project}`);
407
+ }
408
+ if (options.region) {
409
+ gcloudArgs.push(`--region=${options.region}`);
410
+ }
411
+ if (options.zone) {
412
+ gcloudArgs.push(`--zone=${options.zone}`);
413
+ }
414
+
415
+ try {
416
+ ui.startSpinner({ message: `Deleting cluster ${clusterName}...` });
417
+ await execFileAsync('gcloud', gcloudArgs);
418
+ ui.stopSpinnerSuccess(`Cluster ${clusterName} deleted`);
419
+ } catch (error: unknown) {
420
+ ui.stopSpinnerFail('Failed to delete cluster');
421
+ const message = error instanceof Error ? error.message : 'Unknown error';
422
+ logger.error('Failed to delete cluster', { error: message });
423
+ ui.error(`Failed to delete cluster: ${message}`);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Show GKE help
429
+ */
430
+ function showGkeHelp(): void {
431
+ ui.print(ui.bold('GKE Commands:'));
432
+ ui.print(' clusters List all GKE clusters');
433
+ ui.print(' describe <name> Show cluster details');
434
+ ui.print(' get-credentials <name> Configure kubectl for cluster');
435
+ ui.print(' node-pools <cluster> List node pools in cluster');
436
+ ui.print(' resize <cluster> <count> Resize cluster (requires approval)');
437
+ ui.print(' delete <name> Delete cluster (requires approval)');
438
+ }