@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,819 @@
1
+ /**
2
+ * Kubernetes Operations — Embedded tool (stripped HTTP wrappers)
3
+ *
4
+ * Copied from services/k8s-tools-service/src/k8s/operations.ts
5
+ * Uses child_process instead of Bun.$ for portability in the embedded binary.
6
+ */
7
+
8
+ import { exec, spawn } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import { logger } from '../utils';
11
+
12
+ const execAsync = promisify(exec);
13
+
14
+ export interface KubernetesConfig {
15
+ kubeconfig?: string;
16
+ context?: string;
17
+ namespace?: string;
18
+ }
19
+
20
+ export interface GetOptions {
21
+ resource: string;
22
+ name?: string;
23
+ namespace?: string;
24
+ selector?: string;
25
+ allNamespaces?: boolean;
26
+ output?: 'json' | 'yaml' | 'wide' | 'name';
27
+ }
28
+
29
+ export interface ApplyOptions {
30
+ manifest: string;
31
+ namespace?: string;
32
+ dryRun?: boolean;
33
+ force?: boolean;
34
+ serverSide?: boolean;
35
+ }
36
+
37
+ export interface DeleteOptions {
38
+ resource: string;
39
+ name?: string;
40
+ namespace?: string;
41
+ selector?: string;
42
+ force?: boolean;
43
+ gracePeriod?: number;
44
+ }
45
+
46
+ export interface LogsOptions {
47
+ pod: string;
48
+ namespace?: string;
49
+ container?: string;
50
+ follow?: boolean;
51
+ tail?: number;
52
+ previous?: boolean;
53
+ since?: string;
54
+ timestamps?: boolean;
55
+ }
56
+
57
+ export interface ExecOptions {
58
+ pod: string;
59
+ namespace?: string;
60
+ container?: string;
61
+ command: string[];
62
+ stdin?: boolean;
63
+ tty?: boolean;
64
+ }
65
+
66
+ export interface DescribeOptions {
67
+ resource: string;
68
+ name?: string;
69
+ namespace?: string;
70
+ selector?: string;
71
+ allNamespaces?: boolean;
72
+ }
73
+
74
+ export interface PortForwardOptions {
75
+ resource: string;
76
+ name: string;
77
+ namespace?: string;
78
+ ports: string[];
79
+ address?: string;
80
+ }
81
+
82
+ export interface ScaleOptions {
83
+ resource: string;
84
+ name: string;
85
+ namespace?: string;
86
+ replicas: number;
87
+ }
88
+
89
+ export interface RolloutOptions {
90
+ resource: string;
91
+ name: string;
92
+ namespace?: string;
93
+ action: 'status' | 'history' | 'restart' | 'undo' | 'pause' | 'resume';
94
+ revision?: number;
95
+ }
96
+
97
+ export interface CommandResult {
98
+ success: boolean;
99
+ output: string;
100
+ error?: string;
101
+ exitCode: number;
102
+ }
103
+
104
+ export interface PortForwardResult {
105
+ success: boolean;
106
+ resource: string;
107
+ ports: string[];
108
+ namespace: string;
109
+ address: string;
110
+ message: string;
111
+ pid?: number;
112
+ }
113
+
114
+ export interface CopyOptions {
115
+ source: string;
116
+ destination: string;
117
+ namespace?: string;
118
+ container?: string;
119
+ }
120
+
121
+ export interface LabelOptions {
122
+ resource: string;
123
+ name: string;
124
+ namespace?: string;
125
+ labels: Record<string, string | null>;
126
+ overwrite?: boolean;
127
+ }
128
+
129
+ export interface AnnotateOptions {
130
+ resource: string;
131
+ name: string;
132
+ namespace?: string;
133
+ annotations: Record<string, string | null>;
134
+ overwrite?: boolean;
135
+ }
136
+
137
+ export interface PatchOptions {
138
+ resource: string;
139
+ name: string;
140
+ namespace?: string;
141
+ patch: Record<string, unknown>;
142
+ type?: 'json' | 'merge' | 'strategic';
143
+ }
144
+
145
+ export interface DrainOptions {
146
+ force?: boolean;
147
+ ignoreDaemonsets?: boolean;
148
+ deleteEmptyDirData?: boolean;
149
+ gracePeriod?: number;
150
+ timeout?: string;
151
+ }
152
+
153
+ /**
154
+ * Kubernetes operations class wrapping kubectl CLI
155
+ */
156
+ export class KubernetesOperations {
157
+ private kubectlPath: string;
158
+ private kubeconfig?: string;
159
+ private context?: string;
160
+ private defaultNamespace: string;
161
+
162
+ constructor(config: KubernetesConfig = {}) {
163
+ this.kubectlPath = 'kubectl';
164
+ this.kubeconfig = config.kubeconfig;
165
+ this.context = config.context;
166
+ this.defaultNamespace = config.namespace || 'default';
167
+ }
168
+
169
+ /**
170
+ * Build base kubectl command with common flags
171
+ */
172
+ private buildBaseArgs(): string[] {
173
+ const args: string[] = [];
174
+ if (this.kubeconfig) {
175
+ args.push('--kubeconfig', this.kubeconfig);
176
+ }
177
+ if (this.context) {
178
+ args.push('--context', this.context);
179
+ }
180
+ return args;
181
+ }
182
+
183
+ /**
184
+ * Execute kubectl command
185
+ */
186
+ private async execute(args: string[]): Promise<CommandResult> {
187
+ const baseArgs = this.buildBaseArgs();
188
+ const fullArgs = [...baseArgs, ...args];
189
+ const command = `${this.kubectlPath} ${fullArgs.join(' ')}`;
190
+
191
+ logger.debug(`Executing kubectl command: ${command}`);
192
+
193
+ try {
194
+ const result = await execAsync(command, {
195
+ maxBuffer: 10 * 1024 * 1024,
196
+ timeout: 120000,
197
+ });
198
+ return {
199
+ success: true,
200
+ output: result.stdout.trim(),
201
+ exitCode: 0,
202
+ };
203
+ } catch (error: any) {
204
+ const exitCode = error.code ?? 1;
205
+ const stderr = error.stderr?.trim() || error.message;
206
+ const stdout = error.stdout?.trim() || '';
207
+
208
+ logger.error(`kubectl command failed: ${command}`, { exitCode, stderr });
209
+
210
+ return {
211
+ success: false,
212
+ output: stdout,
213
+ error: stderr,
214
+ exitCode,
215
+ };
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Get Kubernetes resources
221
+ */
222
+ async get(options: GetOptions): Promise<CommandResult> {
223
+ const args = ['get', options.resource];
224
+
225
+ if (options.name) {
226
+ args.push(options.name);
227
+ }
228
+
229
+ if (options.allNamespaces) {
230
+ args.push('--all-namespaces');
231
+ } else if (options.namespace) {
232
+ args.push('-n', options.namespace);
233
+ } else {
234
+ args.push('-n', this.defaultNamespace);
235
+ }
236
+
237
+ if (options.selector) {
238
+ args.push('-l', options.selector);
239
+ }
240
+
241
+ if (options.output) {
242
+ args.push('-o', options.output);
243
+ }
244
+
245
+ return this.execute(args);
246
+ }
247
+
248
+ /**
249
+ * Apply a manifest to the cluster
250
+ */
251
+ async apply(options: ApplyOptions): Promise<CommandResult> {
252
+ const args = ['apply', '-f', '-'];
253
+
254
+ if (options.namespace) {
255
+ args.push('-n', options.namespace);
256
+ }
257
+
258
+ if (options.dryRun) {
259
+ args.push('--dry-run=client');
260
+ }
261
+
262
+ if (options.force) {
263
+ args.push('--force');
264
+ }
265
+
266
+ if (options.serverSide) {
267
+ args.push('--server-side');
268
+ }
269
+
270
+ const baseArgs = this.buildBaseArgs();
271
+ const fullArgs = [...baseArgs, ...args];
272
+
273
+ logger.debug(`Applying manifest with kubectl`);
274
+
275
+ try {
276
+ const result = await execAsync(
277
+ `echo '${options.manifest.replace(/'/g, "'\\''")}' | ${this.kubectlPath} ${fullArgs.join(' ')}`,
278
+ { maxBuffer: 10 * 1024 * 1024, timeout: 120000 }
279
+ );
280
+ return {
281
+ success: true,
282
+ output: result.stdout.trim(),
283
+ exitCode: 0,
284
+ };
285
+ } catch (error: any) {
286
+ const exitCode = error.code ?? 1;
287
+ const stderr = error.stderr?.trim() || error.message;
288
+
289
+ return {
290
+ success: false,
291
+ output: '',
292
+ error: stderr,
293
+ exitCode,
294
+ };
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Delete Kubernetes resources
300
+ */
301
+ async delete(options: DeleteOptions): Promise<CommandResult> {
302
+ const args = ['delete', options.resource];
303
+
304
+ if (options.name) {
305
+ args.push(options.name);
306
+ }
307
+
308
+ if (options.namespace) {
309
+ args.push('-n', options.namespace);
310
+ } else {
311
+ args.push('-n', this.defaultNamespace);
312
+ }
313
+
314
+ if (options.selector) {
315
+ args.push('-l', options.selector);
316
+ }
317
+
318
+ if (options.force) {
319
+ args.push('--force');
320
+ }
321
+
322
+ if (options.gracePeriod !== undefined) {
323
+ args.push('--grace-period', options.gracePeriod.toString());
324
+ }
325
+
326
+ return this.execute(args);
327
+ }
328
+
329
+ /**
330
+ * Get logs from a pod
331
+ */
332
+ async logs(options: LogsOptions): Promise<CommandResult> {
333
+ const args = ['logs', options.pod];
334
+
335
+ if (options.namespace) {
336
+ args.push('-n', options.namespace);
337
+ } else {
338
+ args.push('-n', this.defaultNamespace);
339
+ }
340
+
341
+ if (options.container) {
342
+ args.push('-c', options.container);
343
+ }
344
+
345
+ if (options.follow) {
346
+ args.push('-f');
347
+ }
348
+
349
+ if (options.tail !== undefined) {
350
+ args.push('--tail', options.tail.toString());
351
+ }
352
+
353
+ if (options.previous) {
354
+ args.push('--previous');
355
+ }
356
+
357
+ if (options.since) {
358
+ args.push('--since', options.since);
359
+ }
360
+
361
+ if (options.timestamps) {
362
+ args.push('--timestamps');
363
+ }
364
+
365
+ return this.execute(args);
366
+ }
367
+
368
+ /**
369
+ * Execute command in a pod
370
+ */
371
+ async exec(options: ExecOptions): Promise<CommandResult> {
372
+ const args = ['exec', options.pod];
373
+
374
+ if (options.namespace) {
375
+ args.push('-n', options.namespace);
376
+ } else {
377
+ args.push('-n', this.defaultNamespace);
378
+ }
379
+
380
+ if (options.container) {
381
+ args.push('-c', options.container);
382
+ }
383
+
384
+ if (options.stdin) {
385
+ args.push('-i');
386
+ }
387
+
388
+ if (options.tty) {
389
+ args.push('-t');
390
+ }
391
+
392
+ args.push('--');
393
+ args.push(...options.command);
394
+
395
+ return this.execute(args);
396
+ }
397
+
398
+ /**
399
+ * Describe Kubernetes resources
400
+ */
401
+ async describe(options: DescribeOptions): Promise<CommandResult> {
402
+ const args = ['describe', options.resource];
403
+
404
+ if (options.name) {
405
+ args.push(options.name);
406
+ }
407
+
408
+ if (options.allNamespaces) {
409
+ args.push('--all-namespaces');
410
+ } else if (options.namespace) {
411
+ args.push('-n', options.namespace);
412
+ } else {
413
+ args.push('-n', this.defaultNamespace);
414
+ }
415
+
416
+ if (options.selector) {
417
+ args.push('-l', options.selector);
418
+ }
419
+
420
+ return this.execute(args);
421
+ }
422
+
423
+ /**
424
+ * Scale a deployment, replicaset, or statefulset
425
+ */
426
+ async scale(options: ScaleOptions): Promise<CommandResult> {
427
+ const args = [
428
+ 'scale',
429
+ `${options.resource}/${options.name}`,
430
+ '--replicas',
431
+ options.replicas.toString(),
432
+ ];
433
+
434
+ if (options.namespace) {
435
+ args.push('-n', options.namespace);
436
+ } else {
437
+ args.push('-n', this.defaultNamespace);
438
+ }
439
+
440
+ return this.execute(args);
441
+ }
442
+
443
+ /**
444
+ * Manage rollouts
445
+ */
446
+ async rollout(options: RolloutOptions): Promise<CommandResult> {
447
+ const args = ['rollout', options.action, `${options.resource}/${options.name}`];
448
+
449
+ if (options.namespace) {
450
+ args.push('-n', options.namespace);
451
+ } else {
452
+ args.push('-n', this.defaultNamespace);
453
+ }
454
+
455
+ if (options.action === 'undo' && options.revision !== undefined) {
456
+ args.push('--to-revision', options.revision.toString());
457
+ }
458
+
459
+ return this.execute(args);
460
+ }
461
+
462
+ /**
463
+ * Get cluster information
464
+ */
465
+ async clusterInfo(): Promise<CommandResult> {
466
+ return this.execute(['cluster-info']);
467
+ }
468
+
469
+ /**
470
+ * Get current context
471
+ */
472
+ async currentContext(): Promise<CommandResult> {
473
+ return this.execute(['config', 'current-context']);
474
+ }
475
+
476
+ /**
477
+ * Get list of contexts
478
+ */
479
+ async getContexts(): Promise<CommandResult> {
480
+ return this.execute(['config', 'get-contexts', '-o', 'name']);
481
+ }
482
+
483
+ /**
484
+ * Set current context
485
+ */
486
+ async useContext(context: string): Promise<CommandResult> {
487
+ return this.execute(['config', 'use-context', context]);
488
+ }
489
+
490
+ /**
491
+ * Get namespaces
492
+ */
493
+ async getNamespaces(): Promise<CommandResult> {
494
+ return this.execute(['get', 'namespaces', '-o', 'json']);
495
+ }
496
+
497
+ /**
498
+ * Create namespace
499
+ */
500
+ async createNamespace(name: string): Promise<CommandResult> {
501
+ return this.execute(['create', 'namespace', name]);
502
+ }
503
+
504
+ /**
505
+ * Delete namespace
506
+ */
507
+ async deleteNamespace(name: string): Promise<CommandResult> {
508
+ return this.execute(['delete', 'namespace', name]);
509
+ }
510
+
511
+ /**
512
+ * Get events in a namespace
513
+ */
514
+ async getEvents(namespace?: string, fieldSelector?: string): Promise<CommandResult> {
515
+ const args = ['get', 'events', '-o', 'json'];
516
+
517
+ if (namespace) {
518
+ args.push('-n', namespace);
519
+ } else {
520
+ args.push('-n', this.defaultNamespace);
521
+ }
522
+
523
+ if (fieldSelector) {
524
+ args.push('--field-selector', fieldSelector);
525
+ }
526
+
527
+ return this.execute(args);
528
+ }
529
+
530
+ /**
531
+ * Top pods - show resource usage
532
+ */
533
+ async topPods(namespace?: string, selector?: string): Promise<CommandResult> {
534
+ const args = ['top', 'pods'];
535
+
536
+ if (namespace) {
537
+ args.push('-n', namespace);
538
+ } else {
539
+ args.push('-n', this.defaultNamespace);
540
+ }
541
+
542
+ if (selector) {
543
+ args.push('-l', selector);
544
+ }
545
+
546
+ return this.execute(args);
547
+ }
548
+
549
+ /**
550
+ * Top nodes - show node resource usage
551
+ */
552
+ async topNodes(): Promise<CommandResult> {
553
+ return this.execute(['top', 'nodes']);
554
+ }
555
+
556
+ /**
557
+ * Get API resources
558
+ */
559
+ async apiResources(): Promise<CommandResult> {
560
+ return this.execute(['api-resources', '-o', 'wide']);
561
+ }
562
+
563
+ /**
564
+ * Get version information
565
+ */
566
+ async version(): Promise<CommandResult> {
567
+ return this.execute(['version', '-o', 'json']);
568
+ }
569
+
570
+ /**
571
+ * Port forward to a pod or service
572
+ * Note: This starts a background process and returns immediately
573
+ */
574
+ async portForward(options: PortForwardOptions): Promise<PortForwardResult> {
575
+ const args = ['port-forward'];
576
+
577
+ // Resource type and name
578
+ const resourceSpec = `${options.resource}/${options.name}`;
579
+ args.push(resourceSpec);
580
+
581
+ // Add ports
582
+ for (const port of options.ports) {
583
+ args.push(port);
584
+ }
585
+
586
+ // Namespace
587
+ if (options.namespace) {
588
+ args.push('-n', options.namespace);
589
+ } else {
590
+ args.push('-n', this.defaultNamespace);
591
+ }
592
+
593
+ // Address to listen on
594
+ if (options.address) {
595
+ args.push('--address', options.address);
596
+ }
597
+
598
+ const baseArgs = this.buildBaseArgs();
599
+ const fullArgs = [...baseArgs, ...args];
600
+
601
+ logger.info(`Starting port-forward: ${this.kubectlPath} ${fullArgs.join(' ')}`);
602
+
603
+ // Spawn the port-forward as a background process
604
+ const proc = spawn(this.kubectlPath, fullArgs, {
605
+ stdio: ['ignore', 'pipe', 'pipe'],
606
+ });
607
+
608
+ // Wait briefly for the "Forwarding from..." confirmation or an error
609
+ const result = await new Promise<PortForwardResult>(resolve => {
610
+ let stderr = '';
611
+
612
+ const timeout = setTimeout(() => {
613
+ // If no output after 5s, assume it started
614
+ proc.unref();
615
+ resolve({
616
+ success: true,
617
+ resource: resourceSpec,
618
+ ports: options.ports,
619
+ namespace: options.namespace || this.defaultNamespace,
620
+ address: options.address || '127.0.0.1',
621
+ message: `Port-forward started for ${resourceSpec}`,
622
+ pid: proc.pid,
623
+ });
624
+ }, 5000);
625
+
626
+ proc.stdout?.on('data', (data: Buffer) => {
627
+ const output = data.toString();
628
+ if (output.includes('Forwarding from')) {
629
+ clearTimeout(timeout);
630
+ proc.unref();
631
+ resolve({
632
+ success: true,
633
+ resource: resourceSpec,
634
+ ports: options.ports,
635
+ namespace: options.namespace || this.defaultNamespace,
636
+ address: options.address || '127.0.0.1',
637
+ message: output.trim(),
638
+ pid: proc.pid,
639
+ });
640
+ }
641
+ });
642
+
643
+ proc.stderr?.on('data', (data: Buffer) => {
644
+ stderr += data.toString();
645
+ });
646
+
647
+ proc.on('error', err => {
648
+ clearTimeout(timeout);
649
+ resolve({
650
+ success: false,
651
+ resource: resourceSpec,
652
+ ports: options.ports,
653
+ namespace: options.namespace || this.defaultNamespace,
654
+ address: options.address || '127.0.0.1',
655
+ message: `Port-forward failed: ${err.message}`,
656
+ });
657
+ });
658
+
659
+ proc.on('close', code => {
660
+ if (code !== 0) {
661
+ clearTimeout(timeout);
662
+ resolve({
663
+ success: false,
664
+ resource: resourceSpec,
665
+ ports: options.ports,
666
+ namespace: options.namespace || this.defaultNamespace,
667
+ address: options.address || '127.0.0.1',
668
+ message: stderr.trim() || `Port-forward exited with code ${code}`,
669
+ });
670
+ }
671
+ });
672
+ });
673
+
674
+ return result;
675
+ }
676
+
677
+ /**
678
+ * Copy files to/from a pod
679
+ */
680
+ async cp(options: CopyOptions): Promise<CommandResult> {
681
+ const args = ['cp'];
682
+
683
+ if (options.namespace) {
684
+ args.push('-n', options.namespace);
685
+ } else {
686
+ args.push('-n', this.defaultNamespace);
687
+ }
688
+
689
+ if (options.container) {
690
+ args.push('-c', options.container);
691
+ }
692
+
693
+ // Source and destination
694
+ args.push(options.source, options.destination);
695
+
696
+ return this.execute(args);
697
+ }
698
+
699
+ /**
700
+ * Label a resource
701
+ */
702
+ async label(options: LabelOptions): Promise<CommandResult> {
703
+ const args = ['label', `${options.resource}/${options.name}`];
704
+
705
+ if (options.namespace) {
706
+ args.push('-n', options.namespace);
707
+ } else {
708
+ args.push('-n', this.defaultNamespace);
709
+ }
710
+
711
+ // Add labels
712
+ for (const [key, value] of Object.entries(options.labels)) {
713
+ if (value === null) {
714
+ args.push(`${key}-`); // Remove label
715
+ } else {
716
+ args.push(`${key}=${value}`);
717
+ }
718
+ }
719
+
720
+ if (options.overwrite) {
721
+ args.push('--overwrite');
722
+ }
723
+
724
+ return this.execute(args);
725
+ }
726
+
727
+ /**
728
+ * Annotate a resource
729
+ */
730
+ async annotate(options: AnnotateOptions): Promise<CommandResult> {
731
+ const args = ['annotate', `${options.resource}/${options.name}`];
732
+
733
+ if (options.namespace) {
734
+ args.push('-n', options.namespace);
735
+ } else {
736
+ args.push('-n', this.defaultNamespace);
737
+ }
738
+
739
+ // Add annotations
740
+ for (const [key, value] of Object.entries(options.annotations)) {
741
+ if (value === null) {
742
+ args.push(`${key}-`); // Remove annotation
743
+ } else {
744
+ args.push(`${key}=${value}`);
745
+ }
746
+ }
747
+
748
+ if (options.overwrite) {
749
+ args.push('--overwrite');
750
+ }
751
+
752
+ return this.execute(args);
753
+ }
754
+
755
+ /**
756
+ * Patch a resource
757
+ */
758
+ async patch(options: PatchOptions): Promise<CommandResult> {
759
+ const args = ['patch', options.resource, options.name];
760
+
761
+ if (options.namespace) {
762
+ args.push('-n', options.namespace);
763
+ } else {
764
+ args.push('-n', this.defaultNamespace);
765
+ }
766
+
767
+ args.push('--type', options.type || 'strategic');
768
+ args.push('-p', JSON.stringify(options.patch));
769
+
770
+ return this.execute(args);
771
+ }
772
+
773
+ /**
774
+ * Cordon a node (mark as unschedulable)
775
+ */
776
+ async cordon(nodeName: string): Promise<CommandResult> {
777
+ return this.execute(['cordon', nodeName]);
778
+ }
779
+
780
+ /**
781
+ * Uncordon a node (mark as schedulable)
782
+ */
783
+ async uncordon(nodeName: string): Promise<CommandResult> {
784
+ return this.execute(['uncordon', nodeName]);
785
+ }
786
+
787
+ /**
788
+ * Drain a node
789
+ */
790
+ async drain(nodeName: string, options?: DrainOptions): Promise<CommandResult> {
791
+ const args = ['drain', nodeName];
792
+
793
+ if (options?.force) {
794
+ args.push('--force');
795
+ }
796
+ if (options?.ignoreDaemonsets) {
797
+ args.push('--ignore-daemonsets');
798
+ }
799
+ if (options?.deleteEmptyDirData) {
800
+ args.push('--delete-emptydir-data');
801
+ }
802
+ if (options?.gracePeriod !== undefined) {
803
+ args.push('--grace-period', options.gracePeriod.toString());
804
+ }
805
+ if (options?.timeout) {
806
+ args.push('--timeout', options.timeout);
807
+ }
808
+
809
+ return this.execute(args);
810
+ }
811
+
812
+ /**
813
+ * Taint a node
814
+ */
815
+ async taint(nodeName: string, taints: string[]): Promise<CommandResult> {
816
+ const args = ['taint', 'nodes', nodeName, ...taints];
817
+ return this.execute(args);
818
+ }
819
+ }