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}
187
188#[derive(Subcommand, Debug)]
189pub enum ConfigCommands {
190 #[command(
192 long_about = "Add an OpenAPI 3.x specification to your configuration.\n\n\
193 This validates the specification, extracts operations, and creates\n\
194 a cached representation for fast command generation. The spec name\n\
195 becomes the context for executing API operations.\n\n\
196 Supported formats: YAML (.yaml, .yml)\n\
197 Supported auth: API Key, Bearer Token\n\n\
198 Examples:\n \
199 aperture config add myapi ./openapi.yaml\n \
200 aperture config add myapi https://api.example.com/openapi.yaml"
201 )]
202 Add {
203 name: String,
205 file_or_url: String,
207 #[arg(long, help = "Replace the specification if it already exists")]
209 force: bool,
210 #[arg(
212 long,
213 help = "Reject entire spec if any endpoints have unsupported content types (e.g., multipart/form-data, XML). Default behavior skips unsupported endpoints with warnings."
214 )]
215 strict: bool,
216 },
217 #[command(
219 long_about = "Display all currently registered API specifications.\n\n\
220 Shows the names you can use as contexts with 'aperture api'.\n\
221 Use this to see what APIs are available for command generation."
222 )]
223 List {
224 #[arg(long, help = "Show detailed information about each API")]
226 verbose: bool,
227 },
228 #[command(
230 long_about = "Remove a registered API specification and its cached data.\n\n\
231 This removes both the original specification file and the\n\
232 generated cache, making the API operations unavailable.\n\
233 Use 'aperture config list' to see available specifications."
234 )]
235 Remove {
236 name: String,
238 },
239 #[command(
241 long_about = "Open an API specification in your default text editor.\n\n\
242 Uses the $EDITOR environment variable to determine which editor\n\
243 to use. After editing, you may need to re-add the specification\n\
244 to update the cached representation.\n\n\
245 Example:\n \
246 export EDITOR=vim\n \
247 aperture config edit myapi"
248 )]
249 Edit {
250 name: String,
252 },
253 #[command(long_about = "Set the base URL for an API specification.\n\n\
255 This overrides the base URL from the OpenAPI spec and the\n\
256 APERTURE_BASE_URL environment variable. You can set a general\n\
257 override or environment-specific URLs.\n\n\
258 Examples:\n \
259 aperture config set-url myapi https://api.example.com\n \
260 aperture config set-url myapi --env staging https://staging.example.com\n \
261 aperture config set-url myapi --env prod https://prod.example.com")]
262 SetUrl {
263 name: String,
265 url: String,
267 #[arg(long, value_name = "ENV", help = "Set URL for specific environment")]
269 env: Option<String>,
270 },
271 #[command(
273 long_about = "Display the base URL configuration for an API specification.\n\n\
274 Shows the configured base URL override and any environment-specific\n\
275 URLs. Also displays what URL would be used based on current\n\
276 environment settings.\n\n\
277 Example:\n \
278 aperture config get-url myapi"
279 )]
280 GetUrl {
281 name: String,
283 },
284 #[command(
286 long_about = "Display all configured base URLs across all API specifications.\n\n\
287 Shows general overrides and environment-specific configurations\n\
288 for each registered API. Useful for reviewing your URL settings\n\
289 at a glance."
290 )]
291 ListUrls {},
292 #[command(
294 long_about = "Configure authentication secrets for API specifications.\n\n\
295 This allows you to set environment variable mappings for security\n\
296 schemes without modifying the OpenAPI specification file. These\n\
297 settings take precedence over x-aperture-secret extensions.\n\n\
298 Examples:\n \
299 aperture config set-secret myapi bearerAuth --env API_TOKEN\n \
300 aperture config set-secret myapi apiKey --env API_KEY\n \
301 aperture config set-secret myapi --interactive"
302 )]
303 SetSecret {
304 api_name: String,
306 scheme_name: Option<String>,
308 #[arg(long, value_name = "VAR", help = "Environment variable name")]
310 env: Option<String>,
311 #[arg(long, conflicts_with_all = ["scheme_name", "env"], help = "Configure secrets interactively")]
313 interactive: bool,
314 },
315 #[command(
317 long_about = "Display configured secret mappings for an API specification.\n\n\
318 Shows which security schemes are configured with environment\n\
319 variables and which ones still rely on x-aperture-secret\n\
320 extensions or are undefined.\n\n\
321 Example:\n \
322 aperture config list-secrets myapi"
323 )]
324 ListSecrets {
325 api_name: String,
327 },
328 #[command(
330 long_about = "Remove a configured secret mapping for a specific security scheme.\n\n\
331 This will remove the environment variable mapping for the specified\n\
332 security scheme, causing it to fall back to x-aperture-secret\n\
333 extensions or become undefined.\n\n\
334 Examples:\n \
335 aperture config remove-secret myapi bearerAuth\n \
336 aperture config remove-secret myapi apiKey"
337 )]
338 RemoveSecret {
339 api_name: String,
341 scheme_name: String,
343 },
344 #[command(
346 long_about = "Remove all configured secret mappings for an API specification.\n\n\
347 This will remove all environment variable mappings for the API,\n\
348 causing all security schemes to fall back to x-aperture-secret\n\
349 extensions or become undefined. Use with caution.\n\n\
350 Examples:\n \
351 aperture config clear-secrets myapi\n \
352 aperture config clear-secrets myapi --force"
353 )]
354 ClearSecrets {
355 api_name: String,
357 #[arg(long, help = "Skip confirmation prompt")]
359 force: bool,
360 },
361 #[command(
363 long_about = "Regenerate binary cache files for API specifications.\n\n\
364 This is useful when cache files become corrupted or when upgrading\n\
365 between versions of Aperture that have incompatible cache formats.\n\
366 You can reinitialize all specs or target a specific one.\n\n\
367 Examples:\n \
368 aperture config reinit --all # Reinitialize all specs\n \
369 aperture config reinit myapi # Reinitialize specific spec"
370 )]
371 Reinit {
372 context: Option<String>,
374 #[arg(long, conflicts_with = "context", help = "Reinitialize all specs")]
376 all: bool,
377 },
378 #[command(long_about = "Clear cached API responses to free up disk space.\n\n\
380 You can clear cache for a specific API or all cached responses.\n\
381 This is useful when you want to ensure fresh data from the API\n\
382 or free up disk space.\n\n\
383 Examples:\n \
384 aperture config clear-cache myapi # Clear cache for specific API\n \
385 aperture config clear-cache --all # Clear all cached responses")]
386 ClearCache {
387 api_name: Option<String>,
389 #[arg(long, conflicts_with = "api_name", help = "Clear all response cache")]
391 all: bool,
392 },
393 #[command(long_about = "Display statistics about cached API responses.\n\n\
395 Shows cache size, number of entries, and hit/miss rates.\n\
396 Useful for monitoring cache effectiveness and disk usage.\n\n\
397 Examples:\n \
398 aperture config cache-stats myapi # Stats for specific API\n \
399 aperture config cache-stats # Stats for all APIs")]
400 CacheStats {
401 api_name: Option<String>,
403 },
404}