1use clap::{Parser, Subcommand, ValueEnum};
2
3#[derive(ValueEnum, Clone, Debug)]
4pub enum OutputFormat {
5 Json,
7 Yaml,
9 Table,
11}
12
13#[derive(Parser, Debug)]
14#[allow(clippy::struct_excessive_bools)]
15#[command(
16 author,
17 version,
18 about = "Aperture: Dynamic CLI generator for OpenAPI specifications",
19 long_about = "Aperture dynamically generates commands from OpenAPI 3.x specifications.\n\
20 It serves as a bridge between autonomous AI agents and APIs by consuming\n\
21 OpenAPI specs and creating a rich command-line interface with built-in\n\
22 security, caching, and agent-friendly features.\n\n\
23 Examples:\n \
24 aperture config add myapi api-spec.yaml\n \
25 aperture api myapi users get-user --id 123\n \
26 aperture config list\n\n\
27 Agent-friendly features:\n \
28 aperture api myapi --describe-json # Get capability manifest\n \
29 aperture --json-errors api myapi ... # Structured error output\n \
30 aperture api myapi --dry-run ... # Show request without executing"
31)]
32pub struct Cli {
33 #[arg(
35 long,
36 global = true,
37 help = "Output capability manifest as JSON (can be filtered with --jq)"
38 )]
39 pub describe_json: bool,
40
41 #[arg(long, global = true, help = "Output errors in JSON format")]
44 pub json_errors: bool,
45
46 #[arg(long, global = true, help = "Show request details without executing")]
48 pub dry_run: bool,
49
50 #[arg(
52 long,
53 global = true,
54 value_name = "KEY",
55 help = "Set idempotency key header"
56 )]
57 pub idempotency_key: Option<String>,
58
59 #[arg(
61 long,
62 global = true,
63 value_enum,
64 default_value = "json",
65 help = "Output format for response data"
66 )]
67 pub format: OutputFormat,
68
69 #[arg(
71 long,
72 global = true,
73 value_name = "FILTER",
74 help = "Apply JQ filter to JSON output (e.g., '.name', '.[] | select(.active)', '.batch_execution_summary.operations[] | select(.success == false)')"
75 )]
76 pub jq: Option<String>,
77
78 #[arg(
80 long,
81 global = true,
82 value_name = "PATH",
83 help = "Path to batch file (JSON or YAML) containing multiple operations"
84 )]
85 pub batch_file: Option<String>,
86
87 #[arg(
89 long,
90 global = true,
91 value_name = "N",
92 default_value = "5",
93 help = "Maximum number of concurrent requests for batch operations"
94 )]
95 pub batch_concurrency: usize,
96
97 #[arg(
99 long,
100 global = true,
101 value_name = "N",
102 help = "Rate limit for batch operations (requests per second)"
103 )]
104 pub batch_rate_limit: Option<u32>,
105
106 #[arg(
108 long,
109 global = true,
110 help = "Enable response caching (can speed up repeated requests)"
111 )]
112 pub cache: bool,
113
114 #[arg(
116 long,
117 global = true,
118 conflicts_with = "cache",
119 help = "Disable response caching"
120 )]
121 pub no_cache: bool,
122
123 #[arg(
125 long,
126 global = true,
127 value_name = "SECONDS",
128 help = "Cache TTL in seconds (default: 300)"
129 )]
130 pub cache_ttl: Option<u64>,
131
132 #[arg(
134 long,
135 global = true,
136 help = "Use positional arguments for path parameters (legacy syntax)"
137 )]
138 pub positional_args: bool,
139
140 #[command(subcommand)]
141 pub command: Commands,
142}
143
144#[derive(Subcommand, Debug)]
145pub enum Commands {
146 #[command(long_about = "Manage your collection of OpenAPI specifications.\n\n\
148 Add specifications to make their operations available as commands,\n\
149 list currently registered specs, remove unused ones, or edit\n\
150 existing specifications in your default editor.")]
151 Config {
152 #[command(subcommand)]
153 command: ConfigCommands,
154 },
155 #[command(
157 long_about = "Display a tree-like summary of all available commands for an API.\n\n\
158 Shows operations organized by tags, making it easy to discover\n\
159 what functionality is available in a registered API specification.\n\
160 This provides an overview without having to use --help on each operation.\n\n\
161 Example:\n \
162 aperture list-commands myapi"
163 )]
164 ListCommands {
165 context: String,
167 },
168 #[command(
170 long_about = "Execute operations from a registered API specification.\n\n\
171 The context refers to the name you gave when adding the spec.\n\
172 Commands are dynamically generated based on the OpenAPI specification,\n\
173 organized by tags (e.g., 'users', 'posts', 'orders').\n\n\
174 Examples:\n \
175 aperture api myapi users get-user --id 123\n \
176 aperture api myapi posts create-post --body '{\"title\":\"Hello\"}'\n \
177 aperture api myapi --help # See available operations"
178 )]
179 Api {
180 context: String,
182 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
184 args: Vec<String>,
185 },
186 #[command(long_about = "Search for API operations by keyword or pattern.\n\n\
188 Search through all registered API specifications to find\n\
189 relevant operations. The search includes operation IDs,\n\
190 descriptions, paths, and HTTP methods.\n\n\
191 Examples:\n \
192 aperture search 'list users' # Find user listing operations\n \
193 aperture search 'POST create' # Find POST operations with 'create'\n \
194 aperture search issues --api sm # Search only in 'sm' API\n \
195 aperture search 'get.*by.*id' # Regex pattern search")]
196 Search {
197 query: String,
199 #[arg(long, value_name = "API", help = "Search only in specified API")]
201 api: Option<String>,
202 #[arg(long, help = "Show detailed information for each result")]
204 verbose: bool,
205 },
206 #[command(
208 name = "exec",
209 long_about = "Execute API operations using shortcuts instead of full paths.\n\n\
210 This command attempts to resolve shortcuts to their full command paths:\n\
211 - Direct operation IDs: getUserById --id 123\n\
212 - HTTP method + path: GET /users/123\n\
213 - Tag-based shortcuts: users list\n\n\
214 When multiple matches are found, you'll get suggestions to choose from.\n\n\
215 Examples:\n \
216 aperture exec getUserById --id 123\n \
217 aperture exec GET /users/123\n \
218 aperture exec users list"
219 )]
220 Exec {
221 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
223 args: Vec<String>,
224 },
225 #[command(
227 long_about = "Get comprehensive documentation for APIs and operations.\n\n\
228 This provides detailed information including parameters, examples,\n\
229 response schemas, and authentication requirements. Use it to learn\n\
230 about available functionality without trial and error.\n\n\
231 Examples:\n \
232 aperture docs # Interactive help menu\n \
233 aperture docs myapi # API overview\n \
234 aperture docs myapi users get-user # Detailed command help"
235 )]
236 Docs {
237 api: Option<String>,
239 tag: Option<String>,
241 operation: Option<String>,
243 #[arg(long, help = "Enhanced formatting with examples and tips")]
245 enhanced: bool,
246 },
247 #[command(
249 long_about = "Display comprehensive API overview with statistics and examples.\n\n\
250 Shows operation counts, method distribution, available categories,\n\
251 and sample commands to help you get started quickly with any API.\n\n\
252 Examples:\n \
253 aperture overview myapi\n \
254 aperture overview --all # Overview of all registered APIs"
255 )]
256 Overview {
257 api: Option<String>,
259 #[arg(long, conflicts_with = "api", help = "Show overview for all APIs")]
261 all: bool,
262 },
263}
264
265#[derive(Subcommand, Debug)]
266pub enum ConfigCommands {
267 #[command(
269 long_about = "Add an OpenAPI 3.x specification to your configuration.\n\n\
270 This validates the specification, extracts operations, and creates\n\
271 a cached representation for fast command generation. The spec name\n\
272 becomes the context for executing API operations.\n\n\
273 Supported formats: YAML (.yaml, .yml)\n\
274 Supported auth: API Key, Bearer Token\n\n\
275 Examples:\n \
276 aperture config add myapi ./openapi.yaml\n \
277 aperture config add myapi https://api.example.com/openapi.yaml"
278 )]
279 Add {
280 name: String,
282 file_or_url: String,
284 #[arg(long, help = "Replace the specification if it already exists")]
286 force: bool,
287 #[arg(
289 long,
290 help = "Reject entire spec if any endpoints have unsupported content types (e.g., multipart/form-data, XML). Default behavior skips unsupported endpoints with warnings."
291 )]
292 strict: bool,
293 },
294 #[command(
296 long_about = "Display all currently registered API specifications.\n\n\
297 Shows the names you can use as contexts with 'aperture api'.\n\
298 Use this to see what APIs are available for command generation."
299 )]
300 List {
301 #[arg(long, help = "Show detailed information about each API")]
303 verbose: bool,
304 },
305 #[command(
307 long_about = "Remove a registered API specification and its cached data.\n\n\
308 This removes both the original specification file and the\n\
309 generated cache, making the API operations unavailable.\n\
310 Use 'aperture config list' to see available specifications."
311 )]
312 Remove {
313 name: String,
315 },
316 #[command(
318 long_about = "Open an API specification in your default text editor.\n\n\
319 Uses the $EDITOR environment variable to determine which editor\n\
320 to use. After editing, you may need to re-add the specification\n\
321 to update the cached representation.\n\n\
322 Example:\n \
323 export EDITOR=vim\n \
324 aperture config edit myapi"
325 )]
326 Edit {
327 name: String,
329 },
330 #[command(long_about = "Set the base URL for an API specification.\n\n\
332 This overrides the base URL from the OpenAPI spec and the\n\
333 APERTURE_BASE_URL environment variable. You can set a general\n\
334 override or environment-specific URLs.\n\n\
335 Examples:\n \
336 aperture config set-url myapi https://api.example.com\n \
337 aperture config set-url myapi --env staging https://staging.example.com\n \
338 aperture config set-url myapi --env prod https://prod.example.com")]
339 SetUrl {
340 name: String,
342 url: String,
344 #[arg(long, value_name = "ENV", help = "Set URL for specific environment")]
346 env: Option<String>,
347 },
348 #[command(
350 long_about = "Display the base URL configuration for an API specification.\n\n\
351 Shows the configured base URL override and any environment-specific\n\
352 URLs. Also displays what URL would be used based on current\n\
353 environment settings.\n\n\
354 Example:\n \
355 aperture config get-url myapi"
356 )]
357 GetUrl {
358 name: String,
360 },
361 #[command(
363 long_about = "Display all configured base URLs across all API specifications.\n\n\
364 Shows general overrides and environment-specific configurations\n\
365 for each registered API. Useful for reviewing your URL settings\n\
366 at a glance."
367 )]
368 ListUrls {},
369 #[command(
371 long_about = "Configure authentication secrets for API specifications.\n\n\
372 This allows you to set environment variable mappings for security\n\
373 schemes without modifying the OpenAPI specification file. These\n\
374 settings take precedence over x-aperture-secret extensions.\n\n\
375 Examples:\n \
376 aperture config set-secret myapi bearerAuth --env API_TOKEN\n \
377 aperture config set-secret myapi apiKey --env API_KEY\n \
378 aperture config set-secret myapi --interactive"
379 )]
380 SetSecret {
381 api_name: String,
383 scheme_name: Option<String>,
385 #[arg(long, value_name = "VAR", help = "Environment variable name")]
387 env: Option<String>,
388 #[arg(long, conflicts_with_all = ["scheme_name", "env"], help = "Configure secrets interactively")]
390 interactive: bool,
391 },
392 #[command(
394 long_about = "Display configured secret mappings for an API specification.\n\n\
395 Shows which security schemes are configured with environment\n\
396 variables and which ones still rely on x-aperture-secret\n\
397 extensions or are undefined.\n\n\
398 Example:\n \
399 aperture config list-secrets myapi"
400 )]
401 ListSecrets {
402 api_name: String,
404 },
405 #[command(
407 long_about = "Remove a configured secret mapping for a specific security scheme.\n\n\
408 This will remove the environment variable mapping for the specified\n\
409 security scheme, causing it to fall back to x-aperture-secret\n\
410 extensions or become undefined.\n\n\
411 Examples:\n \
412 aperture config remove-secret myapi bearerAuth\n \
413 aperture config remove-secret myapi apiKey"
414 )]
415 RemoveSecret {
416 api_name: String,
418 scheme_name: String,
420 },
421 #[command(
423 long_about = "Remove all configured secret mappings for an API specification.\n\n\
424 This will remove all environment variable mappings for the API,\n\
425 causing all security schemes to fall back to x-aperture-secret\n\
426 extensions or become undefined. Use with caution.\n\n\
427 Examples:\n \
428 aperture config clear-secrets myapi\n \
429 aperture config clear-secrets myapi --force"
430 )]
431 ClearSecrets {
432 api_name: String,
434 #[arg(long, help = "Skip confirmation prompt")]
436 force: bool,
437 },
438 #[command(
440 long_about = "Regenerate binary cache files for API specifications.\n\n\
441 This is useful when cache files become corrupted or when upgrading\n\
442 between versions of Aperture that have incompatible cache formats.\n\
443 You can reinitialize all specs or target a specific one.\n\n\
444 Examples:\n \
445 aperture config reinit --all # Reinitialize all specs\n \
446 aperture config reinit myapi # Reinitialize specific spec"
447 )]
448 Reinit {
449 context: Option<String>,
451 #[arg(long, conflicts_with = "context", help = "Reinitialize all specs")]
453 all: bool,
454 },
455 #[command(long_about = "Clear cached API responses to free up disk space.\n\n\
457 You can clear cache for a specific API or all cached responses.\n\
458 This is useful when you want to ensure fresh data from the API\n\
459 or free up disk space.\n\n\
460 Examples:\n \
461 aperture config clear-cache myapi # Clear cache for specific API\n \
462 aperture config clear-cache --all # Clear all cached responses")]
463 ClearCache {
464 api_name: Option<String>,
466 #[arg(long, conflicts_with = "api_name", help = "Clear all response cache")]
468 all: bool,
469 },
470 #[command(long_about = "Display statistics about cached API responses.\n\n\
472 Shows cache size, number of entries, and hit/miss rates.\n\
473 Useful for monitoring cache effectiveness and disk usage.\n\n\
474 Examples:\n \
475 aperture config cache-stats myapi # Stats for specific API\n \
476 aperture config cache-stats # Stats for all APIs")]
477 CacheStats {
478 api_name: Option<String>,
480 },
481}