@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.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- 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
|
+
}
|