@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,130 @@
1
+ import { logger, ServiceUnavailableError, TimeoutError, getServiceAuthHeaders } from '../utils';
2
+ import type { APIResponse } from '../types';
3
+
4
+ export interface RestClientOptions {
5
+ timeout?: number;
6
+ headers?: Record<string, string>;
7
+ retries?: number;
8
+ }
9
+
10
+ /**
11
+ * REST Client for inter-service communication
12
+ */
13
+ export class RestClient {
14
+ private baseUrl: string;
15
+ private timeout: number;
16
+ private headers: Record<string, string>;
17
+ private retries: number;
18
+
19
+ constructor(baseUrl: string, options: RestClientOptions = {}) {
20
+ this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
21
+ this.timeout = options.timeout || 30000;
22
+ this.headers = {
23
+ 'Content-Type': 'application/json',
24
+ ...getServiceAuthHeaders(),
25
+ ...options.headers,
26
+ };
27
+ this.retries = options.retries || 0;
28
+ }
29
+
30
+ async get<T>(path: string): Promise<APIResponse<T>> {
31
+ return this.request<T>('GET', path);
32
+ }
33
+
34
+ async post<T>(path: string, body?: unknown): Promise<APIResponse<T>> {
35
+ return this.request<T>('POST', path, body);
36
+ }
37
+
38
+ async put<T>(path: string, body?: unknown): Promise<APIResponse<T>> {
39
+ return this.request<T>('PUT', path, body);
40
+ }
41
+
42
+ async delete<T>(path: string): Promise<APIResponse<T>> {
43
+ return this.request<T>('DELETE', path);
44
+ }
45
+
46
+ private async request<T>(
47
+ method: string,
48
+ path: string,
49
+ body?: unknown,
50
+ attempt: number = 0
51
+ ): Promise<APIResponse<T>> {
52
+ const url = `${this.baseUrl}${path}`;
53
+
54
+ try {
55
+ logger.debug(`${method} ${url}`);
56
+
57
+ const controller = new AbortController();
58
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
59
+
60
+ const response = await fetch(url, {
61
+ method,
62
+ headers: this.headers,
63
+ body: body ? JSON.stringify(body) : undefined,
64
+ signal: controller.signal,
65
+ });
66
+
67
+ clearTimeout(timeoutId);
68
+
69
+ if (!response.ok) {
70
+ const error = await response.text();
71
+ return {
72
+ success: false,
73
+ error: {
74
+ code: `HTTP_${response.status}`,
75
+ message: error || response.statusText,
76
+ service: this.baseUrl,
77
+ timestamp: new Date().toISOString(),
78
+ },
79
+ };
80
+ }
81
+
82
+ const data = await response.json();
83
+ return {
84
+ success: true,
85
+ data: data as T,
86
+ };
87
+ } catch (error: any) {
88
+ // Handle abort (timeout)
89
+ if (error.name === 'AbortError') {
90
+ if (attempt < this.retries) {
91
+ logger.warn(`Request timeout, retrying (${attempt + 1}/${this.retries})...`);
92
+ return this.request<T>(method, path, body, attempt + 1);
93
+ }
94
+ throw new TimeoutError(`${method} ${path}`, this.baseUrl, this.timeout);
95
+ }
96
+
97
+ // Handle connection errors
98
+ if (error.code === 'ECONNREFUSED' || error.cause?.code === 'ECONNREFUSED') {
99
+ throw new ServiceUnavailableError(this.baseUrl, {
100
+ url,
101
+ error: error.message,
102
+ });
103
+ }
104
+
105
+ // Other errors
106
+ return {
107
+ success: false,
108
+ error: {
109
+ code: 'REQUEST_ERROR',
110
+ message: error.message || 'Unknown error',
111
+ service: this.baseUrl,
112
+ timestamp: new Date().toISOString(),
113
+ details: error,
114
+ },
115
+ };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Health check helper
121
+ */
122
+ async healthCheck(): Promise<boolean> {
123
+ try {
124
+ const response = await this.get('/health');
125
+ return response.success;
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,33 @@
1
+ import { getEnv } from '../utils';
2
+
3
+ /**
4
+ * Service Discovery
5
+ * Provides URLs for all microservices based on environment
6
+ */
7
+
8
+ export const ServiceURLs = {
9
+ CLI: getEnv('CLI_SERVICE_URL', 'http://localhost:3000'),
10
+ CORE_ENGINE: getEnv('CORE_ENGINE_SERVICE_URL', 'http://localhost:3001'),
11
+ LLM: getEnv('LLM_SERVICE_URL', 'http://localhost:3002'),
12
+ GENERATOR: getEnv('GENERATOR_SERVICE_URL', 'http://localhost:3003'),
13
+ GIT_TOOLS: getEnv('GIT_TOOLS_SERVICE_URL', 'http://localhost:3004'),
14
+ FS_TOOLS: getEnv('FS_TOOLS_SERVICE_URL', 'http://localhost:3005'),
15
+ TERRAFORM_TOOLS: getEnv('TERRAFORM_TOOLS_SERVICE_URL', 'http://localhost:3006'),
16
+ K8S_TOOLS: getEnv('K8S_TOOLS_SERVICE_URL', 'http://localhost:3007'),
17
+ HELM_TOOLS: getEnv('HELM_TOOLS_SERVICE_URL', 'http://localhost:3008'),
18
+ AWS_TOOLS: getEnv('AWS_TOOLS_SERVICE_URL', 'http://localhost:3009'),
19
+ GITHUB_TOOLS: getEnv('GITHUB_TOOLS_SERVICE_URL', 'http://localhost:3010'),
20
+ STATE: getEnv('STATE_SERVICE_URL', 'http://localhost:3011'),
21
+ AUTH: getEnv('AUTH_SERVICE_URL', 'http://localhost:3012'),
22
+ TEAM: getEnv('TEAM_SERVICE_URL', 'http://localhost:3013'),
23
+ BILLING: getEnv('BILLING_SERVICE_URL', 'http://localhost:3014'),
24
+ AUDIT: getEnv('AUDIT_SERVICE_URL', 'http://localhost:3015'),
25
+ GCP_TOOLS: getEnv('GCP_TOOLS_URL', 'http://localhost:3016'),
26
+ AZURE_TOOLS: getEnv('AZURE_TOOLS_URL', 'http://localhost:3017'),
27
+ } as const;
28
+
29
+ export const WebSocketURLs = {
30
+ CORE_ENGINE: getEnv('CORE_ENGINE_WS_URL', 'ws://localhost:3101'),
31
+ LLM: getEnv('LLM_WS_URL', 'ws://localhost:3102'),
32
+ GENERATOR: getEnv('GENERATOR_WS_URL', 'ws://localhost:3103'),
33
+ } as const;
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Terraform Tools Client
3
+ *
4
+ * REST client for communicating with the Terraform Tools Service
5
+ */
6
+
7
+ import { RestClient, ServiceURLs } from '.';
8
+
9
+ export interface TerraformInitResult {
10
+ success: boolean;
11
+ output: string;
12
+ error?: string;
13
+ }
14
+
15
+ export interface TerraformPlanResult {
16
+ success: boolean;
17
+ output: string;
18
+ hasChanges: boolean;
19
+ planFile?: string;
20
+ error?: string;
21
+ }
22
+
23
+ export interface TerraformApplyResult {
24
+ success: boolean;
25
+ output: string;
26
+ error?: string;
27
+ }
28
+
29
+ export interface TerraformValidateResult {
30
+ valid: boolean;
31
+ output: string;
32
+ error?: string;
33
+ }
34
+
35
+ export interface TerraformFmtResult {
36
+ success: boolean;
37
+ output: string;
38
+ changed?: boolean;
39
+ files?: string[];
40
+ error?: string;
41
+ }
42
+
43
+ export interface TerraformOutputResult {
44
+ success: boolean;
45
+ output: string;
46
+ outputs?: Record<string, { value: unknown; type: string; sensitive?: boolean }>;
47
+ error?: string;
48
+ }
49
+
50
+ export interface TerraformWorkspaceResult {
51
+ success: boolean;
52
+ output: string;
53
+ workspaces?: string[];
54
+ current?: string;
55
+ error?: string;
56
+ }
57
+
58
+ export interface TerraformImportResult {
59
+ success: boolean;
60
+ output: string;
61
+ error?: string;
62
+ }
63
+
64
+ export interface TerraformStateResult {
65
+ success: boolean;
66
+ output: string;
67
+ resources?: string[];
68
+ error?: string;
69
+ }
70
+
71
+ export interface TerraformTaintResult {
72
+ success: boolean;
73
+ output: string;
74
+ error?: string;
75
+ }
76
+
77
+ export interface TerraformGraphResult {
78
+ success: boolean;
79
+ output: string;
80
+ dot?: string;
81
+ error?: string;
82
+ }
83
+
84
+ export interface TerraformForceUnlockResult {
85
+ success: boolean;
86
+ output: string;
87
+ error?: string;
88
+ }
89
+
90
+ export interface TerraformRefreshResult {
91
+ success: boolean;
92
+ output: string;
93
+ error?: string;
94
+ }
95
+
96
+ /**
97
+ * Client for Terraform Tools Service
98
+ */
99
+ export class TerraformClient {
100
+ private client: RestClient;
101
+
102
+ constructor(baseUrl?: string) {
103
+ this.client = new RestClient(baseUrl || ServiceURLs.TERRAFORM_TOOLS);
104
+ }
105
+
106
+ /**
107
+ * Initialize a Terraform working directory
108
+ */
109
+ async init(directory: string): Promise<TerraformInitResult> {
110
+ const response = await this.client.post<TerraformInitResult>('/api/terraform/init', {
111
+ directory,
112
+ });
113
+ if (response.success && response.data) {
114
+ return response.data;
115
+ }
116
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
117
+ }
118
+
119
+ /**
120
+ * Generate a Terraform execution plan
121
+ */
122
+ async plan(
123
+ directory: string,
124
+ options?: {
125
+ varFile?: string;
126
+ vars?: Record<string, string>;
127
+ out?: string;
128
+ }
129
+ ): Promise<TerraformPlanResult> {
130
+ const response = await this.client.post<TerraformPlanResult>('/api/terraform/plan', {
131
+ directory,
132
+ ...options,
133
+ });
134
+ if (response.success && response.data) {
135
+ return response.data;
136
+ }
137
+ return {
138
+ success: false,
139
+ output: '',
140
+ hasChanges: false,
141
+ error: response.error?.message || 'Unknown error',
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Apply Terraform changes
147
+ */
148
+ async apply(
149
+ directory: string,
150
+ options?: {
151
+ planFile?: string;
152
+ autoApprove?: boolean;
153
+ varFile?: string;
154
+ vars?: Record<string, string>;
155
+ }
156
+ ): Promise<TerraformApplyResult> {
157
+ const response = await this.client.post<TerraformApplyResult>('/api/terraform/apply', {
158
+ directory,
159
+ ...options,
160
+ });
161
+ if (response.success && response.data) {
162
+ return response.data;
163
+ }
164
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
165
+ }
166
+
167
+ /**
168
+ * Validate Terraform configuration
169
+ */
170
+ async validate(directory: string): Promise<TerraformValidateResult> {
171
+ const response = await this.client.post<TerraformValidateResult>('/api/terraform/validate', {
172
+ directory,
173
+ });
174
+ if (response.success && response.data) {
175
+ return response.data;
176
+ }
177
+ return { valid: false, output: '', error: response.error?.message || 'Unknown error' };
178
+ }
179
+
180
+ /**
181
+ * Destroy Terraform-managed infrastructure
182
+ */
183
+ async destroy(
184
+ directory: string,
185
+ options?: {
186
+ autoApprove?: boolean;
187
+ varFile?: string;
188
+ }
189
+ ): Promise<TerraformApplyResult> {
190
+ const response = await this.client.post<TerraformApplyResult>('/api/terraform/destroy', {
191
+ directory,
192
+ ...options,
193
+ });
194
+ if (response.success && response.data) {
195
+ return response.data;
196
+ }
197
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
198
+ }
199
+
200
+ /**
201
+ * Show Terraform state
202
+ */
203
+ async show(directory: string): Promise<{ success: boolean; output: string }> {
204
+ const response = await this.client.post<{ success: boolean; output: string }>(
205
+ '/api/terraform/show',
206
+ { directory }
207
+ );
208
+ if (response.success && response.data) {
209
+ return response.data;
210
+ }
211
+ return { success: false, output: '' };
212
+ }
213
+
214
+ /**
215
+ * Format Terraform configuration files
216
+ */
217
+ async fmt(
218
+ directory: string,
219
+ options?: {
220
+ check?: boolean;
221
+ recursive?: boolean;
222
+ diff?: boolean;
223
+ }
224
+ ): Promise<TerraformFmtResult> {
225
+ const response = await this.client.post<TerraformFmtResult>('/api/terraform/fmt', {
226
+ workingDir: directory,
227
+ ...options,
228
+ });
229
+ if (response.success && response.data) {
230
+ return response.data;
231
+ }
232
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
233
+ }
234
+
235
+ /**
236
+ * Manage Terraform workspaces
237
+ */
238
+ workspace = {
239
+ list: async (directory: string): Promise<TerraformWorkspaceResult> => {
240
+ const params = new URLSearchParams();
241
+ params.set('workingDir', directory);
242
+ const response = await this.client.get<TerraformWorkspaceResult>(
243
+ `/api/terraform/workspace/list?${params.toString()}`
244
+ );
245
+ if (response.success && response.data) {
246
+ return response.data;
247
+ }
248
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
249
+ },
250
+
251
+ select: async (name: string, directory: string): Promise<TerraformWorkspaceResult> => {
252
+ const response = await this.client.post<TerraformWorkspaceResult>(
253
+ '/api/terraform/workspace/select',
254
+ { name, workingDir: directory }
255
+ );
256
+ if (response.success && response.data) {
257
+ return response.data;
258
+ }
259
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
260
+ },
261
+
262
+ new: async (name: string, directory: string): Promise<TerraformWorkspaceResult> => {
263
+ const response = await this.client.post<TerraformWorkspaceResult>(
264
+ '/api/terraform/workspace/new',
265
+ { name, workingDir: directory }
266
+ );
267
+ if (response.success && response.data) {
268
+ return response.data;
269
+ }
270
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
271
+ },
272
+
273
+ delete: async (name: string, directory: string): Promise<TerraformWorkspaceResult> => {
274
+ const response = await this.client.post<TerraformWorkspaceResult>(
275
+ '/api/terraform/workspace/delete',
276
+ { name, workingDir: directory }
277
+ );
278
+ if (response.success && response.data) {
279
+ return response.data;
280
+ }
281
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
282
+ },
283
+ };
284
+
285
+ /**
286
+ * Import existing infrastructure into Terraform state
287
+ */
288
+ async import(directory: string, address: string, id: string): Promise<TerraformImportResult> {
289
+ const response = await this.client.post<TerraformImportResult>('/api/terraform/import', {
290
+ workingDir: directory,
291
+ address,
292
+ id,
293
+ });
294
+ if (response.success && response.data) {
295
+ return response.data;
296
+ }
297
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
298
+ }
299
+
300
+ /**
301
+ * Manage Terraform state
302
+ */
303
+ state = {
304
+ list: async (directory: string): Promise<TerraformStateResult> => {
305
+ const params = new URLSearchParams();
306
+ params.set('workingDir', directory);
307
+ const response = await this.client.get<TerraformStateResult>(
308
+ `/api/terraform/state/list?${params.toString()}`
309
+ );
310
+ if (response.success && response.data) {
311
+ return response.data;
312
+ }
313
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
314
+ },
315
+
316
+ show: async (address: string, directory: string): Promise<TerraformStateResult> => {
317
+ const params = new URLSearchParams();
318
+ params.set('address', address);
319
+ params.set('workingDir', directory);
320
+ const response = await this.client.get<TerraformStateResult>(
321
+ `/api/terraform/state/show?${params.toString()}`
322
+ );
323
+ if (response.success && response.data) {
324
+ return response.data;
325
+ }
326
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
327
+ },
328
+
329
+ mv: async (
330
+ directory: string,
331
+ source: string,
332
+ destination: string
333
+ ): Promise<TerraformStateResult> => {
334
+ const response = await this.client.post<TerraformStateResult>('/api/terraform/state/mv', {
335
+ directory,
336
+ source,
337
+ destination,
338
+ });
339
+ if (response.success && response.data) {
340
+ return response.data;
341
+ }
342
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
343
+ },
344
+
345
+ pull: async (directory: string): Promise<TerraformStateResult> => {
346
+ const params = new URLSearchParams();
347
+ params.set('directory', directory);
348
+ const response = await this.client.get<TerraformStateResult>(
349
+ `/api/terraform/state/pull?${params.toString()}`
350
+ );
351
+ if (response.success && response.data) {
352
+ return response.data;
353
+ }
354
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
355
+ },
356
+
357
+ push: async (
358
+ directory: string,
359
+ options?: { stateFile?: string; force?: boolean }
360
+ ): Promise<TerraformStateResult> => {
361
+ const response = await this.client.post<TerraformStateResult>('/api/terraform/state/push', {
362
+ directory,
363
+ ...options,
364
+ });
365
+ if (response.success && response.data) {
366
+ return response.data;
367
+ }
368
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
369
+ },
370
+ };
371
+
372
+ /**
373
+ * Taint a resource, marking it for recreation on next apply
374
+ */
375
+ async taint(directory: string, address: string): Promise<TerraformTaintResult> {
376
+ const response = await this.client.post<TerraformTaintResult>('/api/terraform/taint', {
377
+ directory,
378
+ address,
379
+ });
380
+ if (response.success && response.data) {
381
+ return response.data;
382
+ }
383
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
384
+ }
385
+
386
+ /**
387
+ * Untaint a resource, removing the taint mark
388
+ */
389
+ async untaint(directory: string, address: string): Promise<TerraformTaintResult> {
390
+ const response = await this.client.post<TerraformTaintResult>('/api/terraform/untaint', {
391
+ directory,
392
+ address,
393
+ });
394
+ if (response.success && response.data) {
395
+ return response.data;
396
+ }
397
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
398
+ }
399
+
400
+ /**
401
+ * Generate a resource dependency graph in DOT format
402
+ */
403
+ async graph(
404
+ directory: string,
405
+ options?: { type?: 'plan' | 'apply' }
406
+ ): Promise<TerraformGraphResult> {
407
+ const params = new URLSearchParams();
408
+ params.set('directory', directory);
409
+ if (options?.type) {
410
+ params.set('type', options.type);
411
+ }
412
+ const response = await this.client.get<TerraformGraphResult>(
413
+ `/api/terraform/graph?${params.toString()}`
414
+ );
415
+ if (response.success && response.data) {
416
+ return response.data;
417
+ }
418
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
419
+ }
420
+
421
+ /**
422
+ * Force unlock a locked state
423
+ */
424
+ async forceUnlock(directory: string, lockId: string): Promise<TerraformForceUnlockResult> {
425
+ const response = await this.client.post<TerraformForceUnlockResult>(
426
+ '/api/terraform/force-unlock',
427
+ { directory, lockId }
428
+ );
429
+ if (response.success && response.data) {
430
+ return response.data;
431
+ }
432
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
433
+ }
434
+
435
+ /**
436
+ * Refresh Terraform state against real infrastructure
437
+ */
438
+ async refresh(
439
+ directory: string,
440
+ options?: { varFile?: string }
441
+ ): Promise<TerraformRefreshResult> {
442
+ const response = await this.client.post<TerraformRefreshResult>('/api/terraform/refresh', {
443
+ directory,
444
+ ...options,
445
+ });
446
+ if (response.success && response.data) {
447
+ return response.data;
448
+ }
449
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
450
+ }
451
+
452
+ /**
453
+ * Show Terraform output values
454
+ */
455
+ async output(directory: string, name?: string): Promise<TerraformOutputResult> {
456
+ const params = new URLSearchParams();
457
+ params.set('workingDir', directory);
458
+ if (name) {
459
+ params.set('name', name);
460
+ }
461
+ const query = params.toString() ? `?${params.toString()}` : '';
462
+ const response = await this.client.get<TerraformOutputResult>(`/api/terraform/output${query}`);
463
+ if (response.success && response.data) {
464
+ return response.data;
465
+ }
466
+ return { success: false, output: '', error: response.error?.message || 'Unknown error' };
467
+ }
468
+
469
+ /**
470
+ * Check if service is available
471
+ */
472
+ async isAvailable(): Promise<boolean> {
473
+ try {
474
+ const response = await this.client.get<{ status: string }>('/health');
475
+ return response.success && response.data?.status === 'healthy';
476
+ } catch {
477
+ return false;
478
+ }
479
+ }
480
+ }
481
+
482
+ export const terraformClient = new TerraformClient();