@build-astron-co/nimbus 0.2.0 → 0.4.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 (469) hide show
  1. package/bin/nimbus +26 -10
  2. package/bin/nimbus.cmd +41 -0
  3. package/bin/nimbus.mjs +70 -0
  4. package/completions/nimbus.bash +38 -0
  5. package/completions/nimbus.fish +48 -0
  6. package/completions/nimbus.zsh +81 -0
  7. package/dist/src/agent/compaction-agent.js +215 -0
  8. package/dist/src/agent/context-manager.js +385 -0
  9. package/dist/src/agent/context.js +322 -0
  10. package/dist/src/agent/deploy-preview.js +395 -0
  11. package/dist/src/agent/expand-files.js +95 -0
  12. package/dist/src/agent/index.js +18 -0
  13. package/dist/src/agent/loop.js +1535 -0
  14. package/dist/src/agent/modes.js +347 -0
  15. package/dist/src/agent/permissions.js +396 -0
  16. package/dist/src/agent/subagents/base.js +67 -0
  17. package/dist/src/agent/subagents/cost.js +45 -0
  18. package/dist/src/agent/subagents/explore.js +36 -0
  19. package/dist/src/agent/subagents/general.js +41 -0
  20. package/dist/src/agent/subagents/index.js +88 -0
  21. package/dist/src/agent/subagents/infra.js +52 -0
  22. package/dist/src/agent/subagents/security.js +60 -0
  23. package/dist/src/agent/system-prompt.js +860 -0
  24. package/dist/src/app.js +152 -0
  25. package/dist/src/audit/activity-log.js +209 -0
  26. package/dist/src/audit/compliance-checker.js +419 -0
  27. package/dist/src/audit/cost-tracker.js +231 -0
  28. package/dist/src/audit/index.js +10 -0
  29. package/dist/src/audit/security-scanner.js +490 -0
  30. package/dist/src/auth/guard.js +64 -0
  31. package/dist/src/auth/index.js +19 -0
  32. package/dist/src/auth/keychain.js +79 -0
  33. package/dist/src/auth/oauth.js +389 -0
  34. package/dist/src/auth/providers.js +415 -0
  35. package/dist/src/auth/sso.js +87 -0
  36. package/dist/src/auth/store.js +424 -0
  37. package/dist/src/auth/types.js +5 -0
  38. package/dist/src/cli/index.js +8 -0
  39. package/dist/src/cli/init.js +1048 -0
  40. package/dist/src/cli/openapi-spec.js +346 -0
  41. package/dist/src/cli/run.js +505 -0
  42. package/dist/src/cli/serve-auth.js +56 -0
  43. package/dist/src/cli/serve.js +432 -0
  44. package/dist/src/cli/web.js +50 -0
  45. package/dist/src/cli.js +1574 -0
  46. package/dist/src/clients/core-engine-client.js +156 -0
  47. package/dist/src/clients/enterprise-client.js +246 -0
  48. package/dist/src/clients/generator-client.js +219 -0
  49. package/dist/src/clients/git-client.js +367 -0
  50. package/dist/src/clients/github-client.js +229 -0
  51. package/dist/src/clients/helm-client.js +299 -0
  52. package/dist/src/clients/index.js +18 -0
  53. package/dist/src/clients/k8s-client.js +270 -0
  54. package/dist/src/clients/llm-client.js +119 -0
  55. package/dist/src/clients/rest-client.js +104 -0
  56. package/dist/src/clients/service-discovery.js +35 -0
  57. package/dist/src/clients/terraform-client.js +302 -0
  58. package/dist/src/clients/tools-client.js +1227 -0
  59. package/dist/src/clients/ws-client.js +93 -0
  60. package/dist/src/commands/alias.js +91 -0
  61. package/dist/src/commands/analyze/index.js +313 -0
  62. package/dist/src/commands/apply/helm.js +375 -0
  63. package/dist/src/commands/apply/index.js +176 -0
  64. package/dist/src/commands/apply/k8s.js +350 -0
  65. package/dist/src/commands/apply/terraform.js +465 -0
  66. package/dist/src/commands/ask.js +137 -0
  67. package/dist/src/commands/audit/index.js +322 -0
  68. package/dist/src/commands/auth-cloud.js +345 -0
  69. package/dist/src/commands/auth-list.js +112 -0
  70. package/dist/src/commands/auth-profile.js +104 -0
  71. package/dist/src/commands/auth-refresh.js +161 -0
  72. package/dist/src/commands/auth-status.js +122 -0
  73. package/dist/src/commands/aws/ec2.js +402 -0
  74. package/dist/src/commands/aws/iam.js +304 -0
  75. package/dist/src/commands/aws/index.js +108 -0
  76. package/dist/src/commands/aws/lambda.js +317 -0
  77. package/dist/src/commands/aws/rds.js +345 -0
  78. package/dist/src/commands/aws/s3.js +346 -0
  79. package/dist/src/commands/aws/vpc.js +302 -0
  80. package/dist/src/commands/aws-discover.js +413 -0
  81. package/dist/src/commands/aws-terraform.js +618 -0
  82. package/dist/src/commands/azure/aks.js +305 -0
  83. package/dist/src/commands/azure/functions.js +200 -0
  84. package/dist/src/commands/azure/index.js +93 -0
  85. package/dist/src/commands/azure/storage.js +378 -0
  86. package/dist/src/commands/azure/vm.js +291 -0
  87. package/dist/src/commands/billing/index.js +224 -0
  88. package/dist/src/commands/chat.js +259 -0
  89. package/dist/src/commands/completions.js +255 -0
  90. package/dist/src/commands/config.js +291 -0
  91. package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
  92. package/dist/src/commands/cost/estimator.js +73 -0
  93. package/dist/src/commands/cost/index.js +625 -0
  94. package/dist/src/commands/cost/parsers/terraform.js +234 -0
  95. package/dist/src/commands/cost/parsers/types.js +4 -0
  96. package/dist/src/commands/cost/pricing/aws.js +501 -0
  97. package/dist/src/commands/cost/pricing/azure.js +462 -0
  98. package/dist/src/commands/cost/pricing/gcp.js +359 -0
  99. package/dist/src/commands/cost/pricing/index.js +24 -0
  100. package/dist/src/commands/demo.js +196 -0
  101. package/dist/src/commands/deploy.js +215 -0
  102. package/dist/src/commands/doctor.js +1291 -0
  103. package/dist/src/commands/drift/index.js +674 -0
  104. package/dist/src/commands/explain.js +235 -0
  105. package/dist/src/commands/export.js +120 -0
  106. package/dist/src/commands/feedback.js +319 -0
  107. package/dist/src/commands/fix.js +263 -0
  108. package/dist/src/commands/fs/index.js +338 -0
  109. package/dist/src/commands/gcp/compute.js +266 -0
  110. package/dist/src/commands/gcp/functions.js +221 -0
  111. package/dist/src/commands/gcp/gke.js +357 -0
  112. package/dist/src/commands/gcp/iam.js +295 -0
  113. package/dist/src/commands/gcp/index.js +105 -0
  114. package/dist/src/commands/gcp/storage.js +232 -0
  115. package/dist/src/commands/generate-helm.js +1026 -0
  116. package/dist/src/commands/generate-k8s.js +1263 -0
  117. package/dist/src/commands/generate-terraform.js +1058 -0
  118. package/dist/src/commands/gh/index.js +663 -0
  119. package/dist/src/commands/git/index.js +1208 -0
  120. package/dist/src/commands/helm/index.js +985 -0
  121. package/dist/src/commands/help.js +639 -0
  122. package/dist/src/commands/history.js +120 -0
  123. package/dist/src/commands/import.js +782 -0
  124. package/dist/src/commands/incident.js +144 -0
  125. package/dist/src/commands/index.js +109 -0
  126. package/dist/src/commands/init.js +955 -0
  127. package/dist/src/commands/k8s/index.js +979 -0
  128. package/dist/src/commands/login.js +588 -0
  129. package/dist/src/commands/logout.js +61 -0
  130. package/dist/src/commands/logs.js +160 -0
  131. package/dist/src/commands/onboarding.js +382 -0
  132. package/dist/src/commands/pipeline.js +153 -0
  133. package/dist/src/commands/plan/display.js +216 -0
  134. package/dist/src/commands/plan/index.js +525 -0
  135. package/dist/src/commands/plugin.js +325 -0
  136. package/dist/src/commands/preview.js +356 -0
  137. package/dist/src/commands/profile.js +297 -0
  138. package/dist/src/commands/questionnaire.js +1021 -0
  139. package/dist/src/commands/resume.js +35 -0
  140. package/dist/src/commands/rollback.js +259 -0
  141. package/dist/src/commands/rollout.js +74 -0
  142. package/dist/src/commands/runbook.js +307 -0
  143. package/dist/src/commands/schedule.js +202 -0
  144. package/dist/src/commands/status.js +213 -0
  145. package/dist/src/commands/team/index.js +309 -0
  146. package/dist/src/commands/team-context.js +200 -0
  147. package/dist/src/commands/template.js +204 -0
  148. package/dist/src/commands/tf/index.js +989 -0
  149. package/dist/src/commands/upgrade.js +515 -0
  150. package/dist/src/commands/usage/index.js +118 -0
  151. package/dist/src/commands/version.js +145 -0
  152. package/dist/src/commands/watch.js +127 -0
  153. package/dist/src/compat/index.js +2 -0
  154. package/dist/src/compat/runtime.js +10 -0
  155. package/dist/src/compat/sqlite.js +144 -0
  156. package/dist/src/config/index.js +6 -0
  157. package/dist/src/config/manager.js +469 -0
  158. package/dist/src/config/mode-store.js +57 -0
  159. package/dist/src/config/profiles.js +66 -0
  160. package/dist/src/config/safety-policy.js +251 -0
  161. package/dist/src/config/schema.js +107 -0
  162. package/dist/src/config/types.js +311 -0
  163. package/dist/src/config/workspace-state.js +38 -0
  164. package/dist/src/context/context-db.js +138 -0
  165. package/dist/src/demo/index.js +295 -0
  166. package/dist/src/demo/scenarios/full-journey.js +226 -0
  167. package/dist/src/demo/scenarios/getting-started.js +124 -0
  168. package/dist/src/demo/scenarios/helm-release.js +334 -0
  169. package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
  170. package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
  171. package/dist/src/demo/types.js +6 -0
  172. package/dist/src/engine/cost-estimator.js +334 -0
  173. package/dist/src/engine/diagram-generator.js +192 -0
  174. package/dist/src/engine/drift-detector.js +688 -0
  175. package/dist/src/engine/executor.js +832 -0
  176. package/dist/src/engine/index.js +39 -0
  177. package/dist/src/engine/orchestrator.js +436 -0
  178. package/dist/src/engine/planner.js +616 -0
  179. package/dist/src/engine/safety.js +609 -0
  180. package/dist/src/engine/verifier.js +664 -0
  181. package/dist/src/enterprise/audit.js +241 -0
  182. package/dist/src/enterprise/auth.js +189 -0
  183. package/dist/src/enterprise/billing.js +512 -0
  184. package/dist/src/enterprise/index.js +16 -0
  185. package/dist/src/enterprise/teams.js +315 -0
  186. package/dist/src/generator/best-practices.js +1375 -0
  187. package/dist/src/generator/helm.js +495 -0
  188. package/dist/src/generator/index.js +11 -0
  189. package/dist/src/generator/intent-parser.js +420 -0
  190. package/dist/src/generator/kubernetes.js +773 -0
  191. package/dist/src/generator/terraform.js +1472 -0
  192. package/dist/src/history/index.js +6 -0
  193. package/dist/src/history/manager.js +199 -0
  194. package/dist/src/history/types.js +6 -0
  195. package/dist/src/hooks/config.js +318 -0
  196. package/dist/src/hooks/engine.js +317 -0
  197. package/dist/src/hooks/index.js +2 -0
  198. package/dist/src/llm/auth-bridge.js +157 -0
  199. package/dist/src/llm/circuit-breaker.js +116 -0
  200. package/dist/src/llm/config-loader.js +172 -0
  201. package/dist/src/llm/cost-calculator.js +137 -0
  202. package/dist/src/llm/index.js +7 -0
  203. package/dist/src/llm/model-aliases.js +99 -0
  204. package/dist/src/llm/provider-registry.js +57 -0
  205. package/dist/src/llm/providers/anthropic.js +430 -0
  206. package/dist/src/llm/providers/bedrock.js +409 -0
  207. package/dist/src/llm/providers/google.js +344 -0
  208. package/dist/src/llm/providers/ollama.js +661 -0
  209. package/dist/src/llm/providers/openai-compatible.js +289 -0
  210. package/dist/src/llm/providers/openai.js +284 -0
  211. package/dist/src/llm/providers/openrouter.js +293 -0
  212. package/dist/src/llm/router.js +844 -0
  213. package/dist/src/llm/types.js +69 -0
  214. package/dist/src/lsp/client.js +239 -0
  215. package/dist/src/lsp/languages.js +95 -0
  216. package/dist/src/lsp/manager.js +243 -0
  217. package/dist/src/mcp/client.js +289 -0
  218. package/dist/src/mcp/index.js +5 -0
  219. package/dist/src/mcp/manager.js +113 -0
  220. package/dist/src/nimbus.js +212 -0
  221. package/dist/src/plugins/index.js +13 -0
  222. package/dist/src/plugins/loader.js +280 -0
  223. package/dist/src/plugins/manager.js +282 -0
  224. package/dist/src/plugins/types.js +23 -0
  225. package/dist/src/scanners/cicd-scanner.js +230 -0
  226. package/dist/src/scanners/cloud-scanner.js +415 -0
  227. package/dist/src/scanners/framework-scanner.js +430 -0
  228. package/dist/src/scanners/iac-scanner.js +350 -0
  229. package/dist/src/scanners/index.js +454 -0
  230. package/dist/src/scanners/language-scanner.js +258 -0
  231. package/dist/src/scanners/package-manager-scanner.js +252 -0
  232. package/dist/src/scanners/types.js +6 -0
  233. package/dist/src/sessions/manager.js +395 -0
  234. package/dist/src/sessions/types.js +4 -0
  235. package/dist/src/sharing/sync.js +238 -0
  236. package/dist/src/sharing/viewer.js +131 -0
  237. package/dist/src/snapshots/index.js +1 -0
  238. package/dist/src/snapshots/manager.js +432 -0
  239. package/dist/src/state/artifacts.js +94 -0
  240. package/dist/src/state/audit.js +73 -0
  241. package/dist/src/state/billing.js +126 -0
  242. package/dist/src/state/checkpoints.js +81 -0
  243. package/dist/src/state/config.js +58 -0
  244. package/dist/src/state/conversations.js +7 -0
  245. package/dist/src/state/credentials.js +96 -0
  246. package/dist/src/state/db.js +53 -0
  247. package/dist/src/state/index.js +23 -0
  248. package/dist/src/state/messages.js +76 -0
  249. package/dist/src/state/projects.js +92 -0
  250. package/dist/src/state/schema.js +233 -0
  251. package/dist/src/state/sessions.js +79 -0
  252. package/dist/src/state/teams.js +131 -0
  253. package/dist/src/telemetry.js +91 -0
  254. package/dist/src/tools/aws-ops.js +747 -0
  255. package/dist/src/tools/azure-ops.js +491 -0
  256. package/dist/src/tools/file-ops.js +451 -0
  257. package/dist/src/tools/gcp-ops.js +559 -0
  258. package/dist/src/tools/git-ops.js +557 -0
  259. package/dist/src/tools/github-ops.js +460 -0
  260. package/dist/src/tools/helm-ops.js +634 -0
  261. package/dist/src/tools/index.js +16 -0
  262. package/dist/src/tools/k8s-ops.js +579 -0
  263. package/dist/src/tools/schemas/converter.js +129 -0
  264. package/dist/src/tools/schemas/devops.js +3319 -0
  265. package/dist/src/tools/schemas/index.js +19 -0
  266. package/dist/src/tools/schemas/standard.js +966 -0
  267. package/dist/src/tools/schemas/types.js +409 -0
  268. package/dist/src/tools/spawn-exec.js +109 -0
  269. package/dist/src/tools/terraform-ops.js +627 -0
  270. package/dist/src/types/config.js +1 -0
  271. package/dist/src/types/drift.js +4 -0
  272. package/dist/src/types/enterprise.js +5 -0
  273. package/dist/src/types/index.js +14 -0
  274. package/dist/src/types/plan.js +1 -0
  275. package/dist/src/types/request.js +1 -0
  276. package/dist/src/types/response.js +1 -0
  277. package/dist/src/types/service.js +1 -0
  278. package/dist/src/ui/App.js +1672 -0
  279. package/dist/src/ui/DeployPreview.js +60 -0
  280. package/dist/src/ui/FileDiffModal.js +108 -0
  281. package/dist/src/ui/Header.js +46 -0
  282. package/dist/src/ui/HelpModal.js +9 -0
  283. package/dist/src/ui/InputBox.js +408 -0
  284. package/dist/src/ui/MessageList.js +795 -0
  285. package/dist/src/ui/PermissionPrompt.js +72 -0
  286. package/dist/src/ui/StatusBar.js +109 -0
  287. package/dist/src/ui/TerminalPane.js +31 -0
  288. package/dist/src/ui/ToolCallDisplay.js +303 -0
  289. package/dist/src/ui/TreePane.js +83 -0
  290. package/dist/src/ui/chat-ui.js +721 -0
  291. package/dist/src/ui/index.js +11 -0
  292. package/dist/src/ui/ink/index.js +1325 -0
  293. package/dist/src/ui/streaming.js +137 -0
  294. package/dist/src/ui/theme.js +78 -0
  295. package/dist/src/ui/types.js +7 -0
  296. package/dist/src/utils/analytics.js +61 -0
  297. package/dist/src/utils/cost-warning.js +25 -0
  298. package/dist/src/utils/env.js +42 -0
  299. package/dist/src/utils/errors.js +54 -0
  300. package/dist/src/utils/event-bus.js +22 -0
  301. package/dist/src/utils/index.js +16 -0
  302. package/dist/src/utils/logger.js +150 -0
  303. package/dist/src/utils/rate-limiter.js +90 -0
  304. package/dist/src/utils/service-auth.js +36 -0
  305. package/dist/src/utils/validation.js +39 -0
  306. package/dist/src/version.js +3 -0
  307. package/dist/src/watcher/index.js +192 -0
  308. package/dist/src/wizard/approval.js +275 -0
  309. package/dist/src/wizard/index.js +13 -0
  310. package/dist/src/wizard/prompts.js +273 -0
  311. package/dist/src/wizard/types.js +4 -0
  312. package/dist/src/wizard/ui.js +453 -0
  313. package/dist/src/wizard/wizard.js +227 -0
  314. package/package.json +31 -23
  315. package/src/__tests__/alias.test.ts +133 -0
  316. package/src/__tests__/app.test.ts +1 -1
  317. package/src/__tests__/audit.test.ts +1 -1
  318. package/src/__tests__/circuit-breaker.test.ts +1 -1
  319. package/src/__tests__/cli-run.test.ts +237 -1
  320. package/src/__tests__/compat-sqlite.test.ts +68 -0
  321. package/src/__tests__/context-manager.test.ts +131 -1
  322. package/src/__tests__/context.test.ts +1 -1
  323. package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
  324. package/src/__tests__/doctor.test.ts +48 -0
  325. package/src/__tests__/enterprise.test.ts +1 -1
  326. package/src/__tests__/export.test.ts +236 -0
  327. package/src/__tests__/gap-11-18-20.test.ts +958 -0
  328. package/src/__tests__/generator.test.ts +1 -1
  329. package/src/__tests__/helm-streaming.test.ts +127 -0
  330. package/src/__tests__/hooks.test.ts +1 -1
  331. package/src/__tests__/incident.test.ts +179 -0
  332. package/src/__tests__/init.test.ts +55 -4
  333. package/src/__tests__/intent-parser.test.ts +1 -1
  334. package/src/__tests__/llm-router.test.ts +1 -1
  335. package/src/__tests__/logs.test.ts +107 -0
  336. package/src/__tests__/loop-errors.test.ts +244 -0
  337. package/src/__tests__/lsp.test.ts +1 -1
  338. package/src/__tests__/modes.test.ts +1 -1
  339. package/src/__tests__/perf-optimizations.test.ts +847 -0
  340. package/src/__tests__/permissions.test.ts +1 -1
  341. package/src/__tests__/pipeline.test.ts +50 -0
  342. package/src/__tests__/polish-phase3.test.ts +340 -0
  343. package/src/__tests__/profile.test.ts +237 -0
  344. package/src/__tests__/rollback.test.ts +83 -0
  345. package/src/__tests__/runbook.test.ts +219 -0
  346. package/src/__tests__/schedule.test.ts +206 -0
  347. package/src/__tests__/serve.test.ts +1 -1
  348. package/src/__tests__/sessions.test.ts +96 -1
  349. package/src/__tests__/sharing.test.ts +53 -1
  350. package/src/__tests__/snapshots.test.ts +1 -1
  351. package/src/__tests__/standalone-migration.test.ts +199 -0
  352. package/src/__tests__/state-db.test.ts +1 -1
  353. package/src/__tests__/status.test.ts +158 -0
  354. package/src/__tests__/stream-with-tools.test.ts +71 -25
  355. package/src/__tests__/subagents.test.ts +1 -1
  356. package/src/__tests__/system-prompt.test.ts +82 -3
  357. package/src/__tests__/terminal-gap-v2.test.ts +395 -0
  358. package/src/__tests__/terminal-parity.test.ts +393 -0
  359. package/src/__tests__/tf-apply.test.ts +187 -0
  360. package/src/__tests__/tool-converter.test.ts +1 -1
  361. package/src/__tests__/tool-schemas.test.ts +209 -4
  362. package/src/__tests__/tools.test.ts +4 -3
  363. package/src/__tests__/version-json.test.ts +184 -0
  364. package/src/__tests__/version.test.ts +1 -1
  365. package/src/__tests__/watch.test.ts +129 -0
  366. package/src/agent/compaction-agent.ts +40 -1
  367. package/src/agent/context-manager.ts +67 -3
  368. package/src/agent/deploy-preview.ts +62 -1
  369. package/src/agent/expand-files.ts +108 -0
  370. package/src/agent/loop.ts +1312 -31
  371. package/src/agent/permissions.ts +51 -4
  372. package/src/agent/system-prompt.ts +573 -19
  373. package/src/app.ts +58 -0
  374. package/src/audit/security-scanner.ts +45 -0
  375. package/src/auth/keychain.ts +82 -0
  376. package/src/auth/oauth.ts +15 -5
  377. package/src/cli/init.ts +378 -5
  378. package/src/cli/run.ts +407 -16
  379. package/src/cli/serve.ts +78 -1
  380. package/src/cli/web.ts +10 -6
  381. package/src/cli.ts +312 -1
  382. package/src/clients/service-discovery.ts +30 -25
  383. package/src/commands/alias.ts +100 -0
  384. package/src/commands/audit/index.ts +121 -2
  385. package/src/commands/auth-cloud.ts +113 -0
  386. package/src/commands/auth-refresh.ts +187 -0
  387. package/src/commands/aws-discover.ts +144 -251
  388. package/src/commands/aws-terraform.ts +68 -118
  389. package/src/commands/chat.ts +9 -3
  390. package/src/commands/completions.ts +268 -0
  391. package/src/commands/config.ts +26 -0
  392. package/src/commands/cost/index.ts +218 -2
  393. package/src/commands/deploy.ts +260 -0
  394. package/src/commands/doctor.ts +744 -152
  395. package/src/commands/drift/index.ts +371 -23
  396. package/src/commands/export.ts +146 -0
  397. package/src/commands/generate-k8s.ts +9 -61
  398. package/src/commands/generate-terraform.ts +191 -449
  399. package/src/commands/help.ts +212 -36
  400. package/src/commands/history.ts +8 -1
  401. package/src/commands/incident.ts +166 -0
  402. package/src/commands/init.ts +5 -0
  403. package/src/commands/login.ts +86 -1
  404. package/src/commands/logs.ts +167 -0
  405. package/src/commands/onboarding.ts +211 -34
  406. package/src/commands/pipeline.ts +186 -0
  407. package/src/commands/plugin.ts +398 -0
  408. package/src/commands/profile.ts +342 -0
  409. package/src/commands/questionnaire.ts +0 -98
  410. package/src/commands/resume.ts +26 -34
  411. package/src/commands/rollback.ts +315 -0
  412. package/src/commands/rollout.ts +88 -0
  413. package/src/commands/runbook.ts +346 -0
  414. package/src/commands/schedule.ts +236 -0
  415. package/src/commands/status.ts +252 -0
  416. package/src/commands/team-context.ts +220 -0
  417. package/src/commands/template.ts +58 -57
  418. package/src/commands/tf/index.ts +70 -11
  419. package/src/commands/upgrade.ts +57 -0
  420. package/src/commands/version.ts +54 -50
  421. package/src/commands/watch.ts +153 -0
  422. package/src/compat/runtime.ts +1 -1
  423. package/src/compat/sqlite.ts +75 -5
  424. package/src/config/mode-store.ts +62 -0
  425. package/src/config/profiles.ts +84 -0
  426. package/src/config/types.ts +83 -1
  427. package/src/config/workspace-state.ts +53 -0
  428. package/src/engine/cost-estimator.ts +52 -10
  429. package/src/engine/executor.ts +33 -2
  430. package/src/engine/planner.ts +68 -1
  431. package/src/generator/terraform.ts +8 -0
  432. package/src/history/manager.ts +2 -74
  433. package/src/hooks/engine.ts +5 -4
  434. package/src/llm/cost-calculator.ts +2 -2
  435. package/src/llm/providers/anthropic.ts +50 -21
  436. package/src/llm/router.ts +76 -7
  437. package/src/lsp/languages.ts +3 -0
  438. package/src/lsp/manager.ts +21 -5
  439. package/src/nimbus.ts +37 -18
  440. package/src/sessions/manager.ts +108 -1
  441. package/src/sharing/sync.ts +4 -0
  442. package/src/sharing/viewer.ts +66 -0
  443. package/src/tools/file-ops.ts +22 -0
  444. package/src/tools/schemas/devops.ts +3007 -117
  445. package/src/tools/schemas/standard.ts +5 -1
  446. package/src/tools/schemas/types.ts +31 -1
  447. package/src/tools/spawn-exec.ts +148 -0
  448. package/src/ui/App.tsx +1183 -66
  449. package/src/ui/DeployPreview.tsx +62 -57
  450. package/src/ui/FileDiffModal.tsx +162 -0
  451. package/src/ui/Header.tsx +87 -24
  452. package/src/ui/HelpModal.tsx +57 -0
  453. package/src/ui/InputBox.tsx +163 -10
  454. package/src/ui/MessageList.tsx +487 -40
  455. package/src/ui/PermissionPrompt.tsx +17 -5
  456. package/src/ui/StatusBar.tsx +122 -3
  457. package/src/ui/TerminalPane.tsx +84 -0
  458. package/src/ui/ToolCallDisplay.tsx +252 -18
  459. package/src/ui/TreePane.tsx +132 -0
  460. package/src/ui/chat-ui.ts +41 -44
  461. package/src/ui/ink/index.ts +771 -38
  462. package/src/ui/streaming.ts +1 -1
  463. package/src/ui/theme.ts +104 -0
  464. package/src/ui/types.ts +18 -0
  465. package/src/version.ts +1 -1
  466. package/src/watcher/index.ts +66 -15
  467. package/src/wizard/types.ts +1 -0
  468. package/src/wizard/ui.ts +1 -1
  469. package/tsconfig.json +2 -2
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Azure Storage CLI Commands
3
+ *
4
+ * Operations for Azure Storage accounts and blobs
5
+ */
6
+ import { execFile } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { logger } from '../../utils';
9
+ import { ui } from '../../wizard/ui';
10
+ import { loadSafetyPolicy, evaluateSafety, } from '../../config/safety-policy';
11
+ import { promptForApproval, displaySafetySummary } from '../../wizard/approval';
12
+ const execFileAsync = promisify(execFile);
13
+ /**
14
+ * Run storage safety checks
15
+ */
16
+ async function runStorageSafetyChecks(action, target, options) {
17
+ const safetyPolicy = loadSafetyPolicy();
18
+ const context = {
19
+ operation: action,
20
+ type: 'azure',
21
+ environment: options.subscription || 'default',
22
+ resources: [target],
23
+ metadata: {
24
+ resourceType: 'storage',
25
+ resourceGroup: options.resourceGroup,
26
+ },
27
+ };
28
+ return evaluateSafety(context, safetyPolicy);
29
+ }
30
+ /**
31
+ * Main Storage command router
32
+ */
33
+ export async function storageCommand(action, args, options) {
34
+ logger.info('Running Azure Storage command', { action, args, options });
35
+ switch (action) {
36
+ case 'account':
37
+ await accountCommand(args[0], args.slice(1), options);
38
+ break;
39
+ case 'container':
40
+ await containerCommand(args[0], args.slice(1), options);
41
+ break;
42
+ case 'blob':
43
+ await blobCommand(args[0], args.slice(1), options);
44
+ break;
45
+ default:
46
+ showStorageHelp();
47
+ break;
48
+ }
49
+ }
50
+ /**
51
+ * Storage account subcommand
52
+ */
53
+ async function accountCommand(action, args, options) {
54
+ switch (action) {
55
+ case 'list':
56
+ await listAccounts(options);
57
+ break;
58
+ case 'show':
59
+ if (!args[0]) {
60
+ ui.error('Account name required');
61
+ return;
62
+ }
63
+ await showAccount(args[0], options);
64
+ break;
65
+ default:
66
+ ui.print(ui.bold('Storage Account Commands:'));
67
+ ui.print(' list List all storage accounts');
68
+ ui.print(' show <name> -g <rg> Show account details');
69
+ break;
70
+ }
71
+ }
72
+ /**
73
+ * Container subcommand
74
+ */
75
+ async function containerCommand(action, args, options) {
76
+ switch (action) {
77
+ case 'list':
78
+ if (!args[0]) {
79
+ ui.error('Account name required');
80
+ return;
81
+ }
82
+ await listContainers(args[0], options);
83
+ break;
84
+ case 'delete':
85
+ if (!args[0] || !args[1]) {
86
+ ui.error('Account name and container name required');
87
+ return;
88
+ }
89
+ await deleteContainer(args[0], args[1], options);
90
+ break;
91
+ default:
92
+ ui.print(ui.bold('Container Commands:'));
93
+ ui.print(' list <account> List containers in account');
94
+ ui.print(' delete <account> <container> Delete container (requires approval)');
95
+ break;
96
+ }
97
+ }
98
+ /**
99
+ * Blob subcommand
100
+ */
101
+ async function blobCommand(action, args, options) {
102
+ switch (action) {
103
+ case 'list':
104
+ if (!args[0] || !args[1]) {
105
+ ui.error('Account name and container name required');
106
+ return;
107
+ }
108
+ await listBlobs(args[0], args[1], options);
109
+ break;
110
+ default:
111
+ ui.print(ui.bold('Blob Commands:'));
112
+ ui.print(' list <account> <container> List blobs in container');
113
+ break;
114
+ }
115
+ }
116
+ /**
117
+ * List storage accounts
118
+ */
119
+ async function listAccounts(options) {
120
+ ui.header('Azure Storage Accounts');
121
+ ui.newLine();
122
+ const azArgs = ['storage', 'account', 'list', '-o', 'json'];
123
+ if (options.subscription) {
124
+ azArgs.push('--subscription', options.subscription);
125
+ }
126
+ if (options.resourceGroup) {
127
+ azArgs.push('-g', options.resourceGroup);
128
+ }
129
+ try {
130
+ ui.startSpinner({ message: 'Fetching storage accounts...' });
131
+ const { stdout } = await execFileAsync('az', azArgs);
132
+ ui.stopSpinnerSuccess('Accounts fetched');
133
+ const accounts = JSON.parse(stdout || '[]');
134
+ if (accounts.length === 0) {
135
+ ui.info('No storage accounts found');
136
+ return;
137
+ }
138
+ ui.print(`Found ${accounts.length} account(s)\n`);
139
+ // Display table
140
+ ui.print(ui.color(`${'Name'.padEnd(30) +
141
+ 'Resource Group'.padEnd(25) +
142
+ 'Location'.padEnd(15) +
143
+ 'Kind'.padEnd(15)}Status`, 'cyan'));
144
+ ui.print('─'.repeat(100));
145
+ for (const account of accounts) {
146
+ const name = account.name?.substring(0, 29) || '';
147
+ const rg = account.resourceGroup?.substring(0, 24) || '';
148
+ const location = account.location?.substring(0, 14) || '';
149
+ const kind = account.kind?.substring(0, 14) || '';
150
+ const status = account.provisioningState || '';
151
+ const statusColor = status === 'Succeeded' ? 'green' : status === 'Failed' ? 'red' : 'white';
152
+ ui.print(`${name.padEnd(30)}${rg.padEnd(25)}${location.padEnd(15)}${kind.padEnd(15)}${ui.color(status, statusColor)}`);
153
+ }
154
+ }
155
+ catch (error) {
156
+ ui.stopSpinnerFail('Failed to fetch accounts');
157
+ const message = error instanceof Error ? error.message : 'Unknown error';
158
+ logger.error('Failed to list storage accounts', { error: message });
159
+ ui.error(`Failed to list accounts: ${message}`);
160
+ }
161
+ }
162
+ /**
163
+ * Show storage account details
164
+ */
165
+ async function showAccount(accountName, options) {
166
+ ui.header(`Storage Account: ${accountName}`);
167
+ ui.newLine();
168
+ if (!options.resourceGroup) {
169
+ ui.error('Resource group required (use -g)');
170
+ return;
171
+ }
172
+ const azArgs = [
173
+ 'storage',
174
+ 'account',
175
+ 'show',
176
+ '-n',
177
+ accountName,
178
+ '-g',
179
+ options.resourceGroup,
180
+ '-o',
181
+ 'json',
182
+ ];
183
+ if (options.subscription) {
184
+ azArgs.push('--subscription', options.subscription);
185
+ }
186
+ try {
187
+ ui.startSpinner({ message: 'Fetching account details...' });
188
+ const { stdout } = await execFileAsync('az', azArgs);
189
+ ui.stopSpinnerSuccess('Details fetched');
190
+ const account = JSON.parse(stdout);
191
+ ui.print(ui.bold('Basic Information:'));
192
+ ui.print(` Name: ${account.name}`);
193
+ ui.print(` Resource Group: ${account.resourceGroup}`);
194
+ ui.print(` Location: ${account.location}`);
195
+ ui.print(` Kind: ${account.kind}`);
196
+ ui.print(` Status: ${account.provisioningState}`);
197
+ ui.newLine();
198
+ ui.print(ui.bold('Properties:'));
199
+ ui.print(` SKU: ${account.sku?.name}`);
200
+ ui.print(` Tier: ${account.sku?.tier}`);
201
+ ui.print(` Access Tier: ${account.accessTier || 'N/A'}`);
202
+ ui.print(` HTTPS Only: ${account.enableHttpsTrafficOnly ? 'Yes' : 'No'}`);
203
+ ui.newLine();
204
+ ui.print(ui.bold('Encryption:'));
205
+ ui.print(` Services: ${Object.keys(account.encryption?.services || {}).join(', ')}`);
206
+ ui.print(` Key Source: ${account.encryption?.keySource}`);
207
+ ui.newLine();
208
+ ui.print(ui.bold('Endpoints:'));
209
+ const endpoints = account.primaryEndpoints || {};
210
+ if (endpoints.blob) {
211
+ ui.print(` Blob: ${endpoints.blob}`);
212
+ }
213
+ if (endpoints.file) {
214
+ ui.print(` File: ${endpoints.file}`);
215
+ }
216
+ if (endpoints.queue) {
217
+ ui.print(` Queue: ${endpoints.queue}`);
218
+ }
219
+ if (endpoints.table) {
220
+ ui.print(` Table: ${endpoints.table}`);
221
+ }
222
+ }
223
+ catch (error) {
224
+ ui.stopSpinnerFail('Failed to fetch details');
225
+ const message = error instanceof Error ? error.message : 'Unknown error';
226
+ logger.error('Failed to show account', { error: message });
227
+ ui.error(`Failed to show account: ${message}`);
228
+ }
229
+ }
230
+ /**
231
+ * List containers in a storage account
232
+ */
233
+ async function listContainers(accountName, options) {
234
+ ui.header(`Containers in ${accountName}`);
235
+ ui.newLine();
236
+ const azArgs = [
237
+ 'storage',
238
+ 'container',
239
+ 'list',
240
+ '--account-name',
241
+ accountName,
242
+ '-o',
243
+ 'json',
244
+ '--auth-mode',
245
+ 'login',
246
+ ];
247
+ if (options.subscription) {
248
+ azArgs.push('--subscription', options.subscription);
249
+ }
250
+ try {
251
+ ui.startSpinner({ message: 'Fetching containers...' });
252
+ const { stdout } = await execFileAsync('az', azArgs);
253
+ ui.stopSpinnerSuccess('Containers fetched');
254
+ const containers = JSON.parse(stdout || '[]');
255
+ if (containers.length === 0) {
256
+ ui.info('No containers found');
257
+ return;
258
+ }
259
+ ui.print(`Found ${containers.length} container(s)\n`);
260
+ for (const container of containers) {
261
+ ui.print(` ${ui.color(container.name, 'cyan')}`);
262
+ if (container.properties?.lastModified) {
263
+ ui.print(ui.dim(` Last Modified: ${container.properties.lastModified}`));
264
+ }
265
+ }
266
+ }
267
+ catch (error) {
268
+ ui.stopSpinnerFail('Failed to fetch containers');
269
+ const message = error instanceof Error ? error.message : 'Unknown error';
270
+ logger.error('Failed to list containers', { error: message });
271
+ ui.error(`Failed to list containers: ${message}`);
272
+ }
273
+ }
274
+ /**
275
+ * Delete a container (requires safety approval)
276
+ */
277
+ async function deleteContainer(accountName, containerName, options) {
278
+ // Run safety checks
279
+ const safetyResult = await runStorageSafetyChecks('delete', `${accountName}/${containerName}`, options);
280
+ displaySafetySummary({
281
+ operation: `delete container ${containerName}`,
282
+ risks: safetyResult.risks,
283
+ passed: safetyResult.passed,
284
+ });
285
+ if (safetyResult.requiresApproval) {
286
+ const result = await promptForApproval({
287
+ title: 'Delete Storage Container',
288
+ operation: `az storage container delete --name ${containerName} --account-name ${accountName}`,
289
+ risks: safetyResult.risks,
290
+ });
291
+ if (!result.approved) {
292
+ ui.warning('Operation cancelled');
293
+ return;
294
+ }
295
+ }
296
+ const azArgs = [
297
+ 'storage',
298
+ 'container',
299
+ 'delete',
300
+ '--name',
301
+ containerName,
302
+ '--account-name',
303
+ accountName,
304
+ '--auth-mode',
305
+ 'login',
306
+ ];
307
+ if (options.subscription) {
308
+ azArgs.push('--subscription', options.subscription);
309
+ }
310
+ try {
311
+ ui.startSpinner({ message: `Deleting container ${containerName}...` });
312
+ await execFileAsync('az', azArgs);
313
+ ui.stopSpinnerSuccess(`Container ${containerName} deleted`);
314
+ }
315
+ catch (error) {
316
+ ui.stopSpinnerFail('Failed to delete container');
317
+ const message = error instanceof Error ? error.message : 'Unknown error';
318
+ logger.error('Failed to delete container', { error: message });
319
+ ui.error(`Failed to delete container: ${message}`);
320
+ }
321
+ }
322
+ /**
323
+ * List blobs in a container
324
+ */
325
+ async function listBlobs(accountName, containerName, options) {
326
+ ui.header(`Blobs in ${accountName}/${containerName}`);
327
+ ui.newLine();
328
+ const azArgs = [
329
+ 'storage',
330
+ 'blob',
331
+ 'list',
332
+ '--container-name',
333
+ containerName,
334
+ '--account-name',
335
+ accountName,
336
+ '-o',
337
+ 'json',
338
+ '--auth-mode',
339
+ 'login',
340
+ ];
341
+ if (options.subscription) {
342
+ azArgs.push('--subscription', options.subscription);
343
+ }
344
+ try {
345
+ ui.startSpinner({ message: 'Fetching blobs...' });
346
+ const { stdout } = await execFileAsync('az', azArgs);
347
+ ui.stopSpinnerSuccess('Blobs fetched');
348
+ const blobs = JSON.parse(stdout || '[]');
349
+ if (blobs.length === 0) {
350
+ ui.info('No blobs found');
351
+ return;
352
+ }
353
+ ui.print(`Found ${blobs.length} blob(s)\n`);
354
+ for (const blob of blobs) {
355
+ const size = blob.properties?.contentLength
356
+ ? `${Math.round(blob.properties.contentLength / 1024)} KB`
357
+ : '';
358
+ ui.print(` ${blob.name} ${ui.dim(size)}`);
359
+ }
360
+ }
361
+ catch (error) {
362
+ ui.stopSpinnerFail('Failed to fetch blobs');
363
+ const message = error instanceof Error ? error.message : 'Unknown error';
364
+ logger.error('Failed to list blobs', { error: message });
365
+ ui.error(`Failed to list blobs: ${message}`);
366
+ }
367
+ }
368
+ /**
369
+ * Show Storage help
370
+ */
371
+ function showStorageHelp() {
372
+ ui.print(ui.bold('Storage Commands:'));
373
+ ui.print(' account list List storage accounts');
374
+ ui.print(' account show <name> -g <rg> Show account details');
375
+ ui.print(' container list <account> List containers');
376
+ ui.print(' container delete <account> <name> Delete container (requires approval)');
377
+ ui.print(' blob list <account> <container> List blobs');
378
+ }
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Azure Virtual Machine CLI Commands
3
+ *
4
+ * Operations for Azure VMs
5
+ */
6
+ import { execFile } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { logger } from '../../utils';
9
+ import { ui } from '../../wizard/ui';
10
+ import { loadSafetyPolicy, evaluateSafety, } from '../../config/safety-policy';
11
+ import { promptForApproval, displaySafetySummary } from '../../wizard/approval';
12
+ const execFileAsync = promisify(execFile);
13
+ /**
14
+ * Run VM safety checks
15
+ */
16
+ async function runVmSafetyChecks(action, vmName, options) {
17
+ const safetyPolicy = loadSafetyPolicy();
18
+ const context = {
19
+ operation: action,
20
+ type: 'azure',
21
+ environment: options.subscription || 'default',
22
+ resources: [vmName],
23
+ metadata: {
24
+ resourceType: 'virtual-machine',
25
+ resourceGroup: options.resourceGroup,
26
+ },
27
+ };
28
+ return evaluateSafety(context, safetyPolicy);
29
+ }
30
+ /**
31
+ * Main VM command router
32
+ */
33
+ export async function vmCommand(action, args, options) {
34
+ logger.info('Running Azure VM command', { action, args, options });
35
+ switch (action) {
36
+ case 'list':
37
+ await listVms(options);
38
+ break;
39
+ case 'show':
40
+ if (!args[0]) {
41
+ ui.error('VM name required');
42
+ return;
43
+ }
44
+ await showVm(args[0], options);
45
+ break;
46
+ case 'start':
47
+ if (!args[0]) {
48
+ ui.error('VM name required');
49
+ return;
50
+ }
51
+ await startVm(args[0], options);
52
+ break;
53
+ case 'stop':
54
+ if (!args[0]) {
55
+ ui.error('VM name required');
56
+ return;
57
+ }
58
+ await stopVm(args[0], options);
59
+ break;
60
+ case 'delete':
61
+ if (!args[0]) {
62
+ ui.error('VM name required');
63
+ return;
64
+ }
65
+ await deleteVm(args[0], options);
66
+ break;
67
+ default:
68
+ showVmHelp();
69
+ break;
70
+ }
71
+ }
72
+ /**
73
+ * List VMs
74
+ */
75
+ async function listVms(options) {
76
+ ui.header('Azure Virtual Machines');
77
+ ui.newLine();
78
+ const azArgs = ['vm', 'list', '-o', 'json'];
79
+ if (options.subscription) {
80
+ azArgs.push('--subscription', options.subscription);
81
+ }
82
+ if (options.resourceGroup) {
83
+ azArgs.push('-g', options.resourceGroup);
84
+ }
85
+ try {
86
+ ui.startSpinner({ message: 'Fetching VMs...' });
87
+ const { stdout } = await execFileAsync('az', azArgs);
88
+ ui.stopSpinnerSuccess('VMs fetched');
89
+ const vms = JSON.parse(stdout || '[]');
90
+ if (vms.length === 0) {
91
+ ui.info('No VMs found');
92
+ return;
93
+ }
94
+ ui.print(`Found ${vms.length} VM(s)\n`);
95
+ // Display table
96
+ ui.print(ui.color(`${'Name'.padEnd(25) +
97
+ 'Resource Group'.padEnd(25) +
98
+ 'Location'.padEnd(15) +
99
+ 'VM Size'.padEnd(20)}Status`, 'cyan'));
100
+ ui.print('─'.repeat(100));
101
+ for (const vm of vms) {
102
+ const name = vm.name?.substring(0, 24) || '';
103
+ const rg = vm.resourceGroup?.substring(0, 24) || '';
104
+ const location = vm.location?.substring(0, 14) || '';
105
+ const size = vm.hardwareProfile?.vmSize?.substring(0, 19) || '';
106
+ const status = vm.provisioningState || '';
107
+ const statusColor = status === 'Succeeded'
108
+ ? 'green'
109
+ : status === 'Failed'
110
+ ? 'red'
111
+ : status === 'Updating'
112
+ ? 'yellow'
113
+ : 'white';
114
+ ui.print(`${name.padEnd(25)}${rg.padEnd(25)}${location.padEnd(15)}${size.padEnd(20)}${ui.color(status, statusColor)}`);
115
+ }
116
+ }
117
+ catch (error) {
118
+ ui.stopSpinnerFail('Failed to fetch VMs');
119
+ const message = error instanceof Error ? error.message : 'Unknown error';
120
+ logger.error('Failed to list VMs', { error: message });
121
+ ui.error(`Failed to list VMs: ${message}`);
122
+ }
123
+ }
124
+ /**
125
+ * Show VM details
126
+ */
127
+ async function showVm(vmName, options) {
128
+ ui.header(`VM: ${vmName}`);
129
+ ui.newLine();
130
+ if (!options.resourceGroup) {
131
+ ui.error('Resource group required (use -g)');
132
+ return;
133
+ }
134
+ const azArgs = [
135
+ 'vm',
136
+ 'show',
137
+ '-n',
138
+ vmName,
139
+ '-g',
140
+ options.resourceGroup,
141
+ '-o',
142
+ 'json',
143
+ '--show-details',
144
+ ];
145
+ if (options.subscription) {
146
+ azArgs.push('--subscription', options.subscription);
147
+ }
148
+ try {
149
+ ui.startSpinner({ message: 'Fetching VM details...' });
150
+ const { stdout } = await execFileAsync('az', azArgs);
151
+ ui.stopSpinnerSuccess('Details fetched');
152
+ const vm = JSON.parse(stdout);
153
+ ui.print(ui.bold('Basic Information:'));
154
+ ui.print(` Name: ${vm.name}`);
155
+ ui.print(` Resource Group: ${vm.resourceGroup}`);
156
+ ui.print(` Location: ${vm.location}`);
157
+ ui.print(` VM Size: ${vm.hardwareProfile?.vmSize}`);
158
+ ui.print(` State: ${vm.powerState || 'Unknown'}`);
159
+ ui.newLine();
160
+ ui.print(ui.bold('OS:'));
161
+ ui.print(` Publisher: ${vm.storageProfile?.imageReference?.publisher || 'Custom'}`);
162
+ ui.print(` Offer: ${vm.storageProfile?.imageReference?.offer || 'N/A'}`);
163
+ ui.print(` SKU: ${vm.storageProfile?.imageReference?.sku || 'N/A'}`);
164
+ ui.newLine();
165
+ ui.print(ui.bold('Network:'));
166
+ if (vm.publicIps) {
167
+ ui.print(` Public IP: ${vm.publicIps}`);
168
+ }
169
+ if (vm.privateIps) {
170
+ ui.print(` Private IP: ${vm.privateIps}`);
171
+ }
172
+ if (vm.fqdns) {
173
+ ui.print(` FQDN: ${vm.fqdns}`);
174
+ }
175
+ ui.newLine();
176
+ ui.print(ui.bold('Disks:'));
177
+ ui.print(` OS Disk: ${vm.storageProfile?.osDisk?.name}`);
178
+ ui.print(` OS Disk Size: ${vm.storageProfile?.osDisk?.diskSizeGb} GB`);
179
+ const dataDisks = vm.storageProfile?.dataDisks || [];
180
+ if (dataDisks.length > 0) {
181
+ ui.print(` Data Disks: ${dataDisks.length}`);
182
+ }
183
+ }
184
+ catch (error) {
185
+ ui.stopSpinnerFail('Failed to fetch details');
186
+ const message = error instanceof Error ? error.message : 'Unknown error';
187
+ logger.error('Failed to show VM', { error: message });
188
+ ui.error(`Failed to show VM: ${message}`);
189
+ }
190
+ }
191
+ /**
192
+ * Start a VM
193
+ */
194
+ async function startVm(vmName, options) {
195
+ if (!options.resourceGroup) {
196
+ ui.error('Resource group required (use -g)');
197
+ return;
198
+ }
199
+ const azArgs = ['vm', 'start', '-n', vmName, '-g', options.resourceGroup];
200
+ if (options.subscription) {
201
+ azArgs.push('--subscription', options.subscription);
202
+ }
203
+ try {
204
+ ui.startSpinner({ message: `Starting VM ${vmName}...` });
205
+ await execFileAsync('az', azArgs);
206
+ ui.stopSpinnerSuccess(`VM ${vmName} started`);
207
+ }
208
+ catch (error) {
209
+ ui.stopSpinnerFail('Failed to start VM');
210
+ const message = error instanceof Error ? error.message : 'Unknown error';
211
+ logger.error('Failed to start VM', { error: message });
212
+ ui.error(`Failed to start VM: ${message}`);
213
+ }
214
+ }
215
+ /**
216
+ * Stop a VM
217
+ */
218
+ async function stopVm(vmName, options) {
219
+ if (!options.resourceGroup) {
220
+ ui.error('Resource group required (use -g)');
221
+ return;
222
+ }
223
+ const azArgs = ['vm', 'stop', '-n', vmName, '-g', options.resourceGroup];
224
+ if (options.subscription) {
225
+ azArgs.push('--subscription', options.subscription);
226
+ }
227
+ try {
228
+ ui.startSpinner({ message: `Stopping VM ${vmName}...` });
229
+ await execFileAsync('az', azArgs);
230
+ ui.stopSpinnerSuccess(`VM ${vmName} stopped`);
231
+ }
232
+ catch (error) {
233
+ ui.stopSpinnerFail('Failed to stop VM');
234
+ const message = error instanceof Error ? error.message : 'Unknown error';
235
+ logger.error('Failed to stop VM', { error: message });
236
+ ui.error(`Failed to stop VM: ${message}`);
237
+ }
238
+ }
239
+ /**
240
+ * Delete a VM (requires safety approval)
241
+ */
242
+ async function deleteVm(vmName, options) {
243
+ if (!options.resourceGroup) {
244
+ ui.error('Resource group required (use -g)');
245
+ return;
246
+ }
247
+ // Run safety checks
248
+ const safetyResult = await runVmSafetyChecks('delete', vmName, options);
249
+ displaySafetySummary({
250
+ operation: `delete VM ${vmName}`,
251
+ risks: safetyResult.risks,
252
+ passed: safetyResult.passed,
253
+ });
254
+ if (safetyResult.requiresApproval) {
255
+ const result = await promptForApproval({
256
+ title: 'Delete Virtual Machine',
257
+ operation: `az vm delete -n ${vmName} -g ${options.resourceGroup}`,
258
+ risks: safetyResult.risks,
259
+ });
260
+ if (!result.approved) {
261
+ ui.warning('Operation cancelled');
262
+ return;
263
+ }
264
+ }
265
+ const azArgs = ['vm', 'delete', '-n', vmName, '-g', options.resourceGroup, '--yes'];
266
+ if (options.subscription) {
267
+ azArgs.push('--subscription', options.subscription);
268
+ }
269
+ try {
270
+ ui.startSpinner({ message: `Deleting VM ${vmName}...` });
271
+ await execFileAsync('az', azArgs);
272
+ ui.stopSpinnerSuccess(`VM ${vmName} deleted`);
273
+ }
274
+ catch (error) {
275
+ ui.stopSpinnerFail('Failed to delete VM');
276
+ const message = error instanceof Error ? error.message : 'Unknown error';
277
+ logger.error('Failed to delete VM', { error: message });
278
+ ui.error(`Failed to delete VM: ${message}`);
279
+ }
280
+ }
281
+ /**
282
+ * Show VM help
283
+ */
284
+ function showVmHelp() {
285
+ ui.print(ui.bold('VM Commands:'));
286
+ ui.print(' list List all VMs');
287
+ ui.print(' show <name> -g <rg> Show VM details');
288
+ ui.print(' start <name> -g <rg> Start a VM');
289
+ ui.print(' stop <name> -g <rg> Stop a VM');
290
+ ui.print(' delete <name> -g <rg> Delete a VM (requires approval)');
291
+ }