@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,356 @@
1
+ /**
2
+ * OpenAPI 3.1 Specification for `nimbus serve`
3
+ *
4
+ * Returns a static OpenAPI document describing every endpoint exposed by
5
+ * the headless API server. The spec is served at GET /api/openapi.json so
6
+ * that consumers (Swagger UI, code generators, etc.) can discover the API
7
+ * programmatically.
8
+ *
9
+ * @module cli/openapi-spec
10
+ */
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Public API
14
+ // ---------------------------------------------------------------------------
15
+
16
+ /**
17
+ * Build and return the full OpenAPI 3.1 specification object.
18
+ *
19
+ * The return type is intentionally `Record<string, unknown>` rather than
20
+ * a strongly-typed OpenAPI interface, because the spec is serialized to
21
+ * JSON verbatim and consumed by external tooling, not by TypeScript code.
22
+ */
23
+ export function getOpenAPISpec(): Record<string, unknown> {
24
+ return {
25
+ openapi: '3.1.0',
26
+ info: {
27
+ title: 'Nimbus API',
28
+ version: '0.2.0',
29
+ description:
30
+ 'Headless HTTP API for the Nimbus AI Cloud Engineering Agent. ' +
31
+ 'Supports SSE streaming for real-time agent responses, session ' +
32
+ 'management, and non-interactive single-prompt execution.',
33
+ license: {
34
+ name: 'MIT',
35
+ },
36
+ },
37
+ servers: [{ url: 'http://localhost:4200', description: 'Local development' }],
38
+ paths: {
39
+ // -----------------------------------------------------------------
40
+ // Health
41
+ // -----------------------------------------------------------------
42
+ '/api/health': {
43
+ get: {
44
+ summary: 'Health check',
45
+ operationId: 'getHealth',
46
+ tags: ['System'],
47
+ responses: {
48
+ '200': {
49
+ description: 'Server health status',
50
+ content: {
51
+ 'application/json': {
52
+ schema: {
53
+ type: 'object',
54
+ properties: {
55
+ status: { type: 'string', enum: ['ok'] },
56
+ version: { type: 'string' },
57
+ uptime: { type: 'number', description: 'Seconds since server start' },
58
+ db: { type: 'boolean' },
59
+ llm: { type: 'boolean' },
60
+ },
61
+ required: ['status', 'version', 'uptime', 'db', 'llm'],
62
+ },
63
+ },
64
+ },
65
+ },
66
+ },
67
+ },
68
+ },
69
+
70
+ // -----------------------------------------------------------------
71
+ // Chat (SSE streaming)
72
+ // -----------------------------------------------------------------
73
+ '/api/chat': {
74
+ post: {
75
+ summary: 'Send a chat message (SSE streaming)',
76
+ operationId: 'chat',
77
+ tags: ['Chat'],
78
+ description:
79
+ 'Sends a user message to the agent and returns an SSE stream. ' +
80
+ 'Events: session, text, tool_start, tool_end, done, error.',
81
+ requestBody: {
82
+ required: true,
83
+ content: {
84
+ 'application/json': {
85
+ schema: {
86
+ type: 'object',
87
+ required: ['message'],
88
+ properties: {
89
+ message: { type: 'string', description: 'User message to send to the agent' },
90
+ sessionId: {
91
+ type: 'string',
92
+ description: 'Session ID to continue. Auto-generated if omitted.',
93
+ },
94
+ model: {
95
+ type: 'string',
96
+ description: 'Model alias or fully qualified model name',
97
+ },
98
+ mode: {
99
+ type: 'string',
100
+ enum: ['plan', 'build', 'deploy'],
101
+ default: 'build',
102
+ description: 'Agent mode controlling available tools',
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ },
109
+ responses: {
110
+ '200': {
111
+ description: 'SSE stream of agent responses',
112
+ content: {
113
+ 'text/event-stream': {
114
+ schema: {
115
+ type: 'string',
116
+ description: 'Server-Sent Events stream',
117
+ },
118
+ },
119
+ },
120
+ },
121
+ },
122
+ },
123
+ },
124
+
125
+ // -----------------------------------------------------------------
126
+ // Run (non-interactive)
127
+ // -----------------------------------------------------------------
128
+ '/api/run': {
129
+ post: {
130
+ summary: 'Non-interactive single prompt execution',
131
+ operationId: 'run',
132
+ tags: ['Chat'],
133
+ description:
134
+ 'Executes a single prompt through the agent loop and returns ' +
135
+ 'the complete result as JSON. Blocks until the agent finishes.',
136
+ requestBody: {
137
+ required: true,
138
+ content: {
139
+ 'application/json': {
140
+ schema: {
141
+ type: 'object',
142
+ required: ['prompt'],
143
+ properties: {
144
+ prompt: { type: 'string', description: 'Prompt to execute' },
145
+ model: { type: 'string', description: 'Model alias or full name' },
146
+ mode: {
147
+ type: 'string',
148
+ enum: ['plan', 'build', 'deploy'],
149
+ default: 'build',
150
+ },
151
+ },
152
+ },
153
+ },
154
+ },
155
+ },
156
+ responses: {
157
+ '200': {
158
+ description: 'Complete agent response',
159
+ content: {
160
+ 'application/json': {
161
+ schema: {
162
+ type: 'object',
163
+ properties: {
164
+ sessionId: { type: 'string' },
165
+ response: { type: 'string', description: 'Final assistant message content' },
166
+ turns: { type: 'integer', description: 'Number of LLM turns taken' },
167
+ usage: { $ref: '#/components/schemas/Usage' },
168
+ cost: { type: 'number', description: 'Total estimated cost in USD' },
169
+ },
170
+ required: ['sessionId', 'response', 'turns', 'usage', 'cost'],
171
+ },
172
+ },
173
+ },
174
+ },
175
+ '500': {
176
+ description: 'Agent execution error',
177
+ content: {
178
+ 'application/json': {
179
+ schema: { $ref: '#/components/schemas/Error' },
180
+ },
181
+ },
182
+ },
183
+ },
184
+ },
185
+ },
186
+
187
+ // -----------------------------------------------------------------
188
+ // Sessions list
189
+ // -----------------------------------------------------------------
190
+ '/api/sessions': {
191
+ get: {
192
+ summary: 'List all sessions',
193
+ operationId: 'listSessions',
194
+ tags: ['Sessions'],
195
+ responses: {
196
+ '200': {
197
+ description: 'Array of session records',
198
+ content: {
199
+ 'application/json': {
200
+ schema: {
201
+ type: 'object',
202
+ properties: {
203
+ sessions: {
204
+ type: 'array',
205
+ items: { $ref: '#/components/schemas/Session' },
206
+ },
207
+ },
208
+ required: ['sessions'],
209
+ },
210
+ },
211
+ },
212
+ },
213
+ },
214
+ },
215
+ },
216
+
217
+ // -----------------------------------------------------------------
218
+ // Session by ID (GET + POST)
219
+ // -----------------------------------------------------------------
220
+ '/api/session/{id}': {
221
+ get: {
222
+ summary: 'Get session details and conversation messages',
223
+ operationId: 'getSession',
224
+ tags: ['Sessions'],
225
+ parameters: [
226
+ {
227
+ name: 'id',
228
+ in: 'path',
229
+ required: true,
230
+ schema: { type: 'string' },
231
+ description: 'Session UUID',
232
+ },
233
+ ],
234
+ responses: {
235
+ '200': {
236
+ description: 'Session details with conversation history',
237
+ content: {
238
+ 'application/json': {
239
+ schema: {
240
+ type: 'object',
241
+ properties: {
242
+ session: { $ref: '#/components/schemas/Session' },
243
+ messages: {
244
+ type: 'array',
245
+ items: { type: 'object' },
246
+ description: 'LLM message history',
247
+ },
248
+ },
249
+ required: ['session', 'messages'],
250
+ },
251
+ },
252
+ },
253
+ },
254
+ '404': {
255
+ description: 'Session not found',
256
+ content: {
257
+ 'application/json': {
258
+ schema: { $ref: '#/components/schemas/Error' },
259
+ },
260
+ },
261
+ },
262
+ },
263
+ },
264
+ post: {
265
+ summary: 'Continue an existing session (SSE streaming)',
266
+ operationId: 'continueSession',
267
+ tags: ['Sessions'],
268
+ parameters: [
269
+ {
270
+ name: 'id',
271
+ in: 'path',
272
+ required: true,
273
+ schema: { type: 'string' },
274
+ description: 'Session UUID',
275
+ },
276
+ ],
277
+ requestBody: {
278
+ required: true,
279
+ content: {
280
+ 'application/json': {
281
+ schema: {
282
+ type: 'object',
283
+ required: ['message'],
284
+ properties: {
285
+ message: { type: 'string', description: 'Follow-up message' },
286
+ model: { type: 'string', description: 'Model override for this turn' },
287
+ },
288
+ },
289
+ },
290
+ },
291
+ },
292
+ responses: {
293
+ '200': {
294
+ description: 'SSE stream of agent responses',
295
+ content: { 'text/event-stream': {} },
296
+ },
297
+ '404': {
298
+ description: 'Session not found',
299
+ content: {
300
+ 'application/json': {
301
+ schema: { $ref: '#/components/schemas/Error' },
302
+ },
303
+ },
304
+ },
305
+ },
306
+ },
307
+ },
308
+ },
309
+
310
+ // -------------------------------------------------------------------
311
+ // Shared Components
312
+ // -------------------------------------------------------------------
313
+ components: {
314
+ schemas: {
315
+ Session: {
316
+ type: 'object',
317
+ properties: {
318
+ id: { type: 'string', format: 'uuid' },
319
+ name: { type: 'string' },
320
+ status: { type: 'string', enum: ['active', 'suspended', 'completed'] },
321
+ mode: { type: 'string', enum: ['plan', 'build', 'deploy'] },
322
+ model: { type: 'string' },
323
+ tokenCount: { type: 'integer' },
324
+ costUSD: { type: 'number' },
325
+ createdAt: { type: 'string', format: 'date-time' },
326
+ updatedAt: { type: 'string', format: 'date-time' },
327
+ },
328
+ required: ['id', 'name', 'status', 'mode'],
329
+ },
330
+ Usage: {
331
+ type: 'object',
332
+ properties: {
333
+ promptTokens: { type: 'integer' },
334
+ completionTokens: { type: 'integer' },
335
+ totalTokens: { type: 'integer' },
336
+ },
337
+ required: ['promptTokens', 'completionTokens', 'totalTokens'],
338
+ },
339
+ Error: {
340
+ type: 'object',
341
+ properties: {
342
+ error: { type: 'string', description: 'Human-readable error message' },
343
+ },
344
+ required: ['error'],
345
+ },
346
+ },
347
+ securitySchemes: {
348
+ basicAuth: {
349
+ type: 'http',
350
+ scheme: 'basic',
351
+ description: 'Optional HTTP Basic Auth. Enable with --auth user:pass.',
352
+ },
353
+ },
354
+ },
355
+ };
356
+ }
package/src/cli/run.ts ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Non-Interactive CLI Mode
3
+ *
4
+ * Runs the Nimbus agent with a prompt from the command line.
5
+ * Outputs results to stdout and exits.
6
+ *
7
+ * Usage:
8
+ * nimbus run "deploy the staging environment"
9
+ * nimbus run "fix the failing tests" --auto-approve
10
+ * echo "analyze this repo" | nimbus run --stdin
11
+ * nimbus run "estimate costs" --format json --model anthropic/claude-haiku-4-5
12
+ */
13
+
14
+ import { runAgentLoop } from '../agent/loop';
15
+ import { createPermissionState, checkPermission } from '../agent/permissions';
16
+ import { defaultToolRegistry } from '../tools/schemas/types';
17
+ import { standardTools } from '../tools/schemas/standard';
18
+ import { devopsTools } from '../tools/schemas/devops';
19
+ import type { AgentMode } from '../agent/system-prompt';
20
+ import type { LLMRouter } from '../llm/router';
21
+
22
+ /** Options parsed from command-line arguments */
23
+ export interface RunOptions {
24
+ /** The prompt to execute */
25
+ prompt: string;
26
+ /** Output format */
27
+ format: 'text' | 'json';
28
+ /** Skip permission prompts — auto-approve everything */
29
+ autoApprove: boolean;
30
+ /** Read prompt from stdin */
31
+ stdin: boolean;
32
+ /** Model override */
33
+ model?: string;
34
+ /** Agent mode override */
35
+ mode: AgentMode;
36
+ /** Maximum turns */
37
+ maxTurns: number;
38
+ }
39
+
40
+ /** Result of a non-interactive run */
41
+ export interface RunResult {
42
+ /** Whether the run completed successfully */
43
+ success: boolean;
44
+ /** The final output text */
45
+ output: string;
46
+ /** Number of turns taken */
47
+ turns: number;
48
+ /** Token usage */
49
+ usage: {
50
+ promptTokens: number;
51
+ completionTokens: number;
52
+ totalTokens: number;
53
+ };
54
+ /** Cost in USD */
55
+ cost: number;
56
+ /** Whether the run was interrupted */
57
+ interrupted: boolean;
58
+ }
59
+
60
+ /**
61
+ * Parse `nimbus run` CLI arguments.
62
+ */
63
+ export function parseRunArgs(args: string[]): RunOptions {
64
+ let prompt = '';
65
+ let format: 'text' | 'json' = 'text';
66
+ let autoApprove = false;
67
+ let stdin = false;
68
+ let model: string | undefined;
69
+ let mode: AgentMode = 'build';
70
+ let maxTurns = 50;
71
+
72
+ const positional: string[] = [];
73
+
74
+ for (let i = 0; i < args.length; i++) {
75
+ const arg = args[i];
76
+
77
+ switch (arg) {
78
+ case '--format':
79
+ format = (args[++i] ?? 'text') as 'text' | 'json';
80
+ break;
81
+ case '--json':
82
+ format = 'json';
83
+ break;
84
+ case '--auto-approve':
85
+ case '-y':
86
+ autoApprove = true;
87
+ break;
88
+ case '--stdin':
89
+ stdin = true;
90
+ break;
91
+ case '--model':
92
+ model = args[++i];
93
+ break;
94
+ case '--mode':
95
+ mode = (args[++i] ?? 'build') as AgentMode;
96
+ break;
97
+ case '--max-turns':
98
+ maxTurns = parseInt(args[++i] ?? '50', 10);
99
+ break;
100
+ default:
101
+ if (!arg.startsWith('-')) {
102
+ positional.push(arg);
103
+ }
104
+ break;
105
+ }
106
+ }
107
+
108
+ prompt = positional.join(' ');
109
+
110
+ return { prompt, format, autoApprove, stdin, model, mode, maxTurns };
111
+ }
112
+
113
+ /**
114
+ * Execute a non-interactive run.
115
+ */
116
+ export async function executeRun(router: LLMRouter, options: RunOptions): Promise<RunResult> {
117
+ // Get prompt from stdin if requested
118
+ let prompt = options.prompt;
119
+ if (options.stdin && !prompt) {
120
+ prompt = await readStdin();
121
+ }
122
+
123
+ if (!prompt) {
124
+ return {
125
+ success: false,
126
+ output: 'Error: No prompt provided. Usage: nimbus run "your prompt"',
127
+ turns: 0,
128
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
129
+ cost: 0,
130
+ interrupted: false,
131
+ };
132
+ }
133
+
134
+ // Set up tool registry
135
+ const registry = defaultToolRegistry;
136
+ if (registry.size === 0) {
137
+ // Register all built-in tools
138
+ for (const tool of [...standardTools, ...devopsTools]) {
139
+ try {
140
+ registry.register(tool);
141
+ } catch {
142
+ /* skip duplicates */
143
+ }
144
+ }
145
+ }
146
+
147
+ // Set up permission state
148
+ const permissionState = createPermissionState();
149
+
150
+ // Collect output
151
+ const outputParts: string[] = [];
152
+
153
+ // Run the agent loop
154
+ const result = await runAgentLoop(prompt, [], {
155
+ router,
156
+ toolRegistry: registry,
157
+ mode: options.mode,
158
+ maxTurns: options.maxTurns,
159
+ model: options.model,
160
+ cwd: process.cwd(),
161
+
162
+ onText: text => {
163
+ outputParts.push(text);
164
+ if (options.format === 'text') {
165
+ process.stdout.write(text);
166
+ }
167
+ },
168
+
169
+ onToolCallStart: toolCall => {
170
+ if (options.format === 'text') {
171
+ process.stderr.write(`\n[Tool: ${toolCall.name}]\n`);
172
+ }
173
+ },
174
+
175
+ onToolCallEnd: (toolCall, result) => {
176
+ if (options.format === 'text' && result.isError) {
177
+ process.stderr.write(`[Error: ${result.error}]\n`);
178
+ }
179
+ },
180
+
181
+ checkPermission: async (tool, input) => {
182
+ if (options.autoApprove) {
183
+ return 'allow';
184
+ }
185
+ const decision = checkPermission(tool, input, permissionState);
186
+ if (decision === 'ask') {
187
+ // In non-interactive mode without --auto-approve, deny by default
188
+ return 'deny';
189
+ }
190
+ return decision;
191
+ },
192
+ });
193
+
194
+ const output = outputParts.join('');
195
+
196
+ // Format output
197
+ if (options.format === 'json') {
198
+ const jsonResult = {
199
+ success: !result.interrupted,
200
+ output,
201
+ turns: result.turns,
202
+ usage: result.usage,
203
+ cost: result.totalCost,
204
+ interrupted: result.interrupted,
205
+ };
206
+ console.log(JSON.stringify(jsonResult, null, 2));
207
+ } else if (options.format === 'text') {
208
+ // Text was already streamed above
209
+ console.log(''); // Final newline
210
+ }
211
+
212
+ return {
213
+ success: !result.interrupted,
214
+ output,
215
+ turns: result.turns,
216
+ usage: result.usage,
217
+ cost: result.totalCost,
218
+ interrupted: result.interrupted,
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Read all input from stdin.
224
+ */
225
+ async function readStdin(): Promise<string> {
226
+ const chunks: Buffer[] = [];
227
+
228
+ return new Promise(resolve => {
229
+ process.stdin.on('data', chunk => chunks.push(Buffer.from(chunk)));
230
+ process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8').trim()));
231
+
232
+ // If stdin is a TTY (no pipe), resolve immediately
233
+ if (process.stdin.isTTY) {
234
+ resolve('');
235
+ }
236
+ });
237
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * HTTP Basic Auth Middleware for `nimbus serve`
3
+ *
4
+ * Provides optional HTTP Basic Authentication for the headless API server.
5
+ * Disabled by default for local development; enabled via `--auth user:pass`.
6
+ *
7
+ * Unauthenticated endpoints (always bypassed):
8
+ * - GET /api/health
9
+ * - GET /api/openapi.json
10
+ * - OPTIONS (CORS preflight)
11
+ *
12
+ * @module cli/serve-auth
13
+ */
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Public Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /** Credentials for HTTP Basic Auth. */
20
+ export interface ServeAuthOptions {
21
+ /** Username for Basic Auth. */
22
+ readonly username: string;
23
+ /** Password for Basic Auth. */
24
+ readonly password: string;
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Paths that are always public (no auth required)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const PUBLIC_PATHS = new Set(['/api/health', '/api/openapi.json']);
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Middleware Factory
35
+ // ---------------------------------------------------------------------------
36
+
37
+ /**
38
+ * Create an Elysia-compatible `onBeforeHandle` function that enforces
39
+ * HTTP Basic Authentication on protected endpoints.
40
+ *
41
+ * @param options - The username and password to validate against.
42
+ * @returns A handler that short-circuits with 401 when credentials are
43
+ * missing or invalid, or `undefined` to let the request through.
44
+ */
45
+ export function createAuthMiddleware(
46
+ options: ServeAuthOptions
47
+ ): (ctx: { request: Request; set: any }) => { error: string } | undefined {
48
+ const expectedToken = btoa(`${options.username}:${options.password}`);
49
+
50
+ return ({ request, set }: { request: Request; set: any }) => {
51
+ const url = new URL(request.url);
52
+
53
+ // Skip auth for public endpoints
54
+ if (PUBLIC_PATHS.has(url.pathname)) {
55
+ return undefined;
56
+ }
57
+
58
+ // Skip auth for CORS preflight requests
59
+ if (request.method === 'OPTIONS') {
60
+ return undefined;
61
+ }
62
+
63
+ const authHeader = request.headers.get('Authorization');
64
+ if (!authHeader) {
65
+ set.status = 401;
66
+ set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
67
+ return { error: 'Authentication required' };
68
+ }
69
+
70
+ const [scheme, token] = authHeader.split(' ');
71
+ if (scheme !== 'Basic' || token !== expectedToken) {
72
+ set.status = 401;
73
+ set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
74
+ return { error: 'Invalid credentials' };
75
+ }
76
+
77
+ // Credentials valid -- proceed
78
+ return undefined;
79
+ };
80
+ }