cometapi-cli 0.1.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.
cometapi_cli/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """CometAPI CLI — professional terminal interface for CometAPI."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.1"
cometapi_cli/app.py CHANGED
@@ -33,11 +33,11 @@ def main(
33
33
  ctx: typer.Context,
34
34
  version: Annotated[
35
35
  bool | None,
36
- typer.Option("--version", "-V", help="Show version and exit.", callback=_version_callback, is_eager=True),
36
+ typer.Option("--version", "-V", "-v", help="Show version and exit.", callback=_version_callback, is_eager=True),
37
37
  ] = None,
38
38
  output_format: Annotated[
39
39
  OutputFormat,
40
- typer.Option("--format", "-f", help="Output format."),
40
+ typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown)."),
41
41
  ] = OutputFormat.TABLE,
42
42
  json_output: Annotated[
43
43
  bool,
@@ -14,7 +14,7 @@ from ..formatters import OutputFormat, output, resolve_format
14
14
  @handle_errors
15
15
  def account(
16
16
  ctx: typer.Context,
17
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
17
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
18
18
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
19
19
  ) -> None:
20
20
  """Show your CometAPI account profile (requires access token)."""
@@ -18,7 +18,7 @@ def balance(
18
18
  str | None,
19
19
  typer.Option("--source", "-s", help="Data source: 'account' (full account) or 'token' (current API key)."),
20
20
  ] = None,
21
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
21
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
22
22
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
23
23
  ) -> None:
24
24
  """Show your CometAPI account balance.
@@ -17,12 +17,12 @@ from ..formatters import OutputFormat, resolve_format
17
17
  def chat(
18
18
  ctx: typer.Context,
19
19
  message: Annotated[str | None, typer.Argument(help="Message to send. Omit to enter interactive REPL.")] = None,
20
- model: Annotated[str | None, typer.Option("--model", "-m", help="Model to use.")] = None,
20
+ model: Annotated[str | None, typer.Option("--model", "-m", help="Model to use (default: from config or gpt-5.4).")] = None,
21
21
  system: Annotated[str | None, typer.Option("--system", "-s", help="System prompt.")] = None,
22
- temperature: Annotated[float | None, typer.Option("--temperature", "-t", help="Sampling temperature.")] = None,
22
+ temperature: Annotated[float | None, typer.Option("--temperature", "-t", help="Sampling temperature (0.0–2.0).")] = None,
23
23
  max_tokens: Annotated[int | None, typer.Option("--max-tokens", help="Max tokens in response.")] = None,
24
24
  stream: Annotated[bool, typer.Option("--stream/--no-stream", help="Stream output.")] = True,
25
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
25
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
26
26
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
27
27
  ) -> None:
28
28
  """Send a chat message, or start interactive REPL (no args)."""
@@ -13,7 +13,12 @@ from ..console import console, err_console
13
13
  from ..errors import handle_errors
14
14
  from ..formatters import OutputFormat, output, resolve_format
15
15
 
16
- config_app = typer.Typer(name="config", help="Manage CLI configuration.")
16
+ config_app = typer.Typer(
17
+ name="config",
18
+ help="Manage CLI configuration.",
19
+ no_args_is_help=True,
20
+ context_settings={"help_option_names": ["-h", "--help"]},
21
+ )
17
22
 
18
23
 
19
24
  @handle_errors
@@ -31,6 +36,34 @@ def init(ctx: typer.Context) -> None:
31
36
  )
32
37
  )
33
38
 
39
+ cfg = load_config()
40
+ has_existing = bool(cfg)
41
+
42
+ # Show existing config summary and let user decide
43
+ if has_existing:
44
+ summary_lines = []
45
+ for key in ("api_key", "access_token", "default_model", "output_format", "base_url"):
46
+ val = cfg.get(key)
47
+ if val:
48
+ display = mask_secret(str(val)) if key in ("api_key", "access_token") else val
49
+ summary_lines.append(f" [bold]{key}[/bold] = {display}")
50
+ if summary_lines:
51
+ console.print(
52
+ Panel(
53
+ "[bold yellow]Existing configuration found:[/bold yellow]\n\n"
54
+ + "\n".join(summary_lines)
55
+ + "\n\n[dim]Press Enter on each prompt to keep the current value.[/dim]",
56
+ title="📋 Current Config",
57
+ border_style="yellow",
58
+ )
59
+ )
60
+ if not Confirm.ask(
61
+ "[bold]Continue with reconfiguration?[/bold]",
62
+ default=True,
63
+ ):
64
+ console.print("[dim]Aborted. Config unchanged.[/dim]")
65
+ return
66
+
34
67
  console.print(
35
68
  Panel(
36
69
  "[bold yellow]⚠ Security Notice[/bold yellow]\n\n"
@@ -43,17 +76,19 @@ def init(ctx: typer.Context) -> None:
43
76
  )
44
77
  )
45
78
 
46
- cfg = load_config()
79
+ changed = False
47
80
 
48
81
  # API Key
49
82
  current_key = cfg.get("api_key", "")
50
83
  default_display = mask_secret(current_key) if current_key else None
84
+ hint = " [dim](Enter to keep)[/dim]" if current_key else ""
51
85
  api_key = Prompt.ask(
52
- "[bold]API Key[/bold] (COMETAPI_KEY)",
86
+ f"[bold]API Key[/bold] (COMETAPI_KEY){hint}",
53
87
  default=default_display,
54
88
  )
55
89
  if api_key and api_key != default_display:
56
90
  cfg["api_key"] = api_key
91
+ changed = True
57
92
 
58
93
  # Validate connectivity
59
94
  if cfg.get("api_key"):
@@ -75,22 +110,45 @@ def init(ctx: typer.Context) -> None:
75
110
  default=bool(current_token),
76
111
  ):
77
112
  default_token_display = mask_secret(current_token) if current_token else None
113
+ hint = " [dim](Enter to keep)[/dim]" if current_token else ""
78
114
  access_token = Prompt.ask(
79
- "[bold]Access Token[/bold] (COMETAPI_ACCESS_TOKEN)",
115
+ f"[bold]Access Token[/bold] (COMETAPI_ACCESS_TOKEN){hint}",
80
116
  default=default_token_display,
81
117
  )
82
118
  if access_token and access_token != default_token_display:
83
119
  cfg["access_token"] = access_token
120
+ changed = True
84
121
 
85
122
  # Default model
86
123
  console.print()
124
+ current_model = cfg.get("default_model", "")
125
+ model_default = current_model or get_default_model()
87
126
  default_model = Prompt.ask(
88
127
  "[bold]Default model[/bold]",
89
- default=cfg.get("default_model", get_default_model()),
128
+ default=model_default,
129
+ )
130
+ if default_model != current_model:
131
+ cfg["default_model"] = default_model
132
+ changed = True
133
+
134
+ # Default output format
135
+ console.print()
136
+ current_format = cfg.get("output_format", "")
137
+ format_default = current_format or "table"
138
+ output_format = Prompt.ask(
139
+ "[bold]Default output format[/bold] (table, json, yaml, csv, markdown)",
140
+ default=format_default,
90
141
  )
91
- cfg["default_model"] = default_model
142
+ if output_format != current_format:
143
+ cfg["output_format"] = output_format
144
+ changed = True
92
145
 
93
146
  # Save
147
+ if has_existing and not changed:
148
+ console.print()
149
+ console.print("[dim]No changes made. Config unchanged.[/dim]")
150
+ return
151
+
94
152
  save_config(cfg)
95
153
 
96
154
  console.print()
@@ -113,7 +171,7 @@ def init(ctx: typer.Context) -> None:
113
171
  @handle_errors
114
172
  def config_show(
115
173
  ctx: typer.Context,
116
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
174
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
117
175
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
118
176
  ) -> None:
119
177
  """Display current configuration (secrets are masked)."""
@@ -137,10 +195,18 @@ def config_show(
137
195
  @config_app.command("set")
138
196
  @handle_errors
139
197
  def config_set(
140
- key: Annotated[str, typer.Argument(help="Configuration key.")],
141
- value: Annotated[str, typer.Argument(help="Configuration value.")],
198
+ key: Annotated[str, typer.Argument(help="Configuration key (api_key, access_token, base_url, default_model, output_format).")],
199
+ value: Annotated[str, typer.Argument(help="Value to set. For output_format: table, json, yaml, csv, markdown.")],
142
200
  ) -> None:
143
- """Set a configuration value."""
201
+ """Set a configuration value.
202
+
203
+ Valid keys: api_key, access_token, base_url, default_model, output_format.
204
+
205
+ Examples:
206
+ cometapi config set output_format json
207
+ cometapi config set default_model claude-sonnet-4-6
208
+ cometapi config set api_key sk-xxx
209
+ """
144
210
  if key not in VALID_KEYS:
145
211
  err_console.print(f"[red bold]Error:[/] Unknown key '{key}'. Valid keys: {', '.join(sorted(VALID_KEYS))}")
146
212
  raise typer.Exit(code=2)
@@ -155,7 +221,7 @@ def config_set(
155
221
  @config_app.command("unset")
156
222
  @handle_errors
157
223
  def config_unset(
158
- key: Annotated[str, typer.Argument(help="Configuration key to remove.")],
224
+ key: Annotated[str, typer.Argument(help="Configuration key to remove (api_key, access_token, base_url, default_model, output_format).")],
159
225
  ) -> None:
160
226
  """Remove a configuration value."""
161
227
  cfg = load_config()
@@ -25,7 +25,7 @@ def _warn_mark() -> str:
25
25
  @handle_errors
26
26
  def doctor(
27
27
  ctx: typer.Context,
28
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
28
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
29
29
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
30
30
  ) -> None:
31
31
  """Check CLI configuration and API connectivity."""
@@ -162,10 +162,9 @@ def logs(
162
162
  str | None,
163
163
  typer.Option("--type", help="Filter by type: consume, topup, error, refund, manage, system."),
164
164
  ] = None,
165
- search: Annotated[str | None, typer.Option("--search", "-s", help="[deprecated] Search logs by keyword.")] = None,
166
165
  start: Annotated[str | None, typer.Option("--start", help="Start date (YYYY-MM-DD, ISO 8601, or Unix ts).")] = None,
167
166
  end: Annotated[str | None, typer.Option("--end", help="End date (YYYY-MM-DD, ISO 8601, or Unix ts).")] = None,
168
- group: Annotated[str | None, typer.Option("--group", "-g", help="Filter by group.")] = None,
167
+ group: Annotated[str | None, typer.Option("--group", "-g", help="Filter by API key group name.")] = None,
169
168
  request_id: Annotated[
170
169
  str | None,
171
170
  typer.Option("--request-id", help="Look up cost by request ID (X-Cometapi-Request-Id header)."),
@@ -177,7 +176,7 @@ def logs(
177
176
  page: Annotated[int, typer.Option("--page", "-p", help="Page number.")] = 1,
178
177
  limit: Annotated[int, typer.Option("--limit", "-l", help="Results per page.")] = 20,
179
178
  export: Annotated[bool, typer.Option("--export", help="Export logs as CSV to stdout.")] = False,
180
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
179
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
181
180
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
182
181
  ) -> None:
183
182
  """Show your usage logs (requires access token)."""
@@ -247,52 +246,24 @@ def logs(
247
246
  sys.stdout.buffer.write(csv_bytes)
248
247
  return
249
248
 
250
- if search:
251
- err_console.print(
252
- "[yellow]Warning:[/] --search only matches log type codes on the server, "
253
- "not keywords. Use --request-id, --model, or --token-name for filtering."
254
- )
255
- ignored = []
256
- if model:
257
- ignored.append("--model")
258
- if token_name:
259
- ignored.append("--token-name")
260
- if log_type:
261
- ignored.append("--type")
262
- if start:
263
- ignored.append("--start")
264
- if end:
265
- ignored.append("--end")
266
- if group:
267
- ignored.append("--group")
268
- if page != 1:
269
- ignored.append("--page")
270
- if limit != 20:
271
- ignored.append("--limit")
272
- if ignored:
273
- err_console.print(
274
- f"[yellow]Warning:[/] --search ignores other filters: {', '.join(ignored)}"
275
- )
276
- resp = client.search_logs(keyword=search)
277
- else:
278
- type_int = None
279
- if log_type:
280
- type_int = LOG_TYPE_MAP.get(log_type.lower())
281
- if type_int is None:
282
- valid = ", ".join(LOG_TYPE_MAP.keys())
283
- err_console.print(f"[red]Invalid log type:[/] {log_type}. Valid types: {valid}")
284
- raise typer.Exit(code=2)
249
+ type_int = None
250
+ if log_type:
251
+ type_int = LOG_TYPE_MAP.get(log_type.lower())
252
+ if type_int is None:
253
+ valid = ", ".join(LOG_TYPE_MAP.keys())
254
+ err_console.print(f"[red]Invalid log type:[/] {log_type}. Valid types: {valid}")
255
+ raise typer.Exit(code=2)
285
256
 
286
- resp = client.list_logs(
287
- page=page,
288
- page_size=limit,
289
- log_type=type_int,
290
- model_name=model,
291
- token_name=token_name,
292
- start_timestamp=start_ts,
293
- end_timestamp=end_ts,
294
- group=group,
295
- )
257
+ resp = client.list_logs(
258
+ page=page,
259
+ page_size=limit,
260
+ log_type=type_int,
261
+ model_name=model,
262
+ token_name=token_name,
263
+ start_timestamp=start_ts,
264
+ end_timestamp=end_ts,
265
+ group=group,
266
+ )
296
267
 
297
268
  data = extract_items(resp)
298
269
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import re
6
+ from functools import cmp_to_key
5
7
  from typing import Annotated
6
8
 
7
9
  import typer
@@ -10,13 +12,63 @@ from ..config import get_client
10
12
  from ..errors import handle_errors
11
13
  from ..formatters import OutputFormat, output, resolve_format
12
14
 
15
+ _MODEL_TOKEN_RE = re.compile(r"[a-z]+|\d+")
16
+
17
+
18
+ def _tokenize_model_id(model_id: str) -> list[str | int]:
19
+ tokens: list[str | int] = []
20
+ for segment in re.split(r"[-.]+", model_id.lower()):
21
+ if not segment:
22
+ continue
23
+ for part in _MODEL_TOKEN_RE.findall(segment):
24
+ tokens.append(int(part) if part.isdigit() else part)
25
+ return tokens
26
+
27
+
28
+ def _compare_model_ids(left: str, right: str) -> int:
29
+ left_tokens = _tokenize_model_id(left)
30
+ right_tokens = _tokenize_model_id(right)
31
+
32
+ for left_token, right_token in zip(left_tokens, right_tokens):
33
+ if left_token == right_token:
34
+ continue
35
+
36
+ if isinstance(left_token, int) and isinstance(right_token, int):
37
+ return -1 if left_token > right_token else 1
38
+
39
+ if isinstance(left_token, int):
40
+ return -1
41
+ if isinstance(right_token, int):
42
+ return 1
43
+
44
+ return -1 if left_token > right_token else 1
45
+
46
+ if len(left_tokens) == len(right_tokens):
47
+ if left.lower() == right.lower():
48
+ return 0
49
+ return -1 if left.lower() > right.lower() else 1
50
+
51
+ if len(left_tokens) < len(right_tokens):
52
+ next_token = right_tokens[len(left_tokens)]
53
+ return 1 if isinstance(next_token, int) else -1
54
+
55
+ next_token = left_tokens[len(right_tokens)]
56
+ return -1 if isinstance(next_token, int) else 1
57
+
58
+
59
+ def _compare_model_rows(left: dict[str, str], right: dict[str, str]) -> int:
60
+ return _compare_model_ids(left["id"], right["id"])
61
+
13
62
 
14
63
  @handle_errors
15
64
  def models(
16
65
  ctx: typer.Context,
17
66
  search: Annotated[str | None, typer.Option("--search", "-s", help="Filter models by name.")] = None,
18
67
  limit: Annotated[int | None, typer.Option("--limit", "-l", help="Max number of models to show.")] = None,
19
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
68
+ output_format: Annotated[
69
+ OutputFormat | None,
70
+ typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown)."),
71
+ ] = None,
20
72
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
21
73
  ) -> None:
22
74
  """List available models."""
@@ -24,15 +76,14 @@ def models(
24
76
  client = get_client()
25
77
  result = client.models.list()
26
78
 
27
- rows = sorted(
28
- [{"id": m.id, "owned_by": m.owned_by} for m in result],
29
- key=lambda r: r["id"],
30
- )
79
+ rows = [{"id": m.id} for m in result]
31
80
 
32
81
  if search:
33
82
  term = search.lower()
34
83
  rows = [r for r in rows if term in r["id"].lower()]
35
84
 
85
+ rows = sorted(rows, key=cmp_to_key(_compare_model_rows))
86
+
36
87
  if limit and limit > 0:
37
88
  rows = rows[:limit]
38
89
 
@@ -40,5 +91,5 @@ def models(
40
91
  rows,
41
92
  fmt,
42
93
  title=f"Available Models ({len(rows)})",
43
- columns={"id": "cyan", "owned_by": "green"},
94
+ columns={"id": "cyan"},
44
95
  )
@@ -71,7 +71,7 @@ def run_repl() -> None:
71
71
  completer = WordCompleter(
72
72
  REPL_COMMANDS + [
73
73
  "--json", "--format", "--model", "--system",
74
- "--search", "--limit", "--help", "--page",
74
+ "--limit", "--help", "--page",
75
75
  "--type", "--token-name", "--start", "--end",
76
76
  "--group", "--export", "--platform", "--task-id",
77
77
  "--status", "--action",
@@ -14,7 +14,7 @@ from ..formatters import OutputFormat, output, resolve_format
14
14
  @handle_errors
15
15
  def stats(
16
16
  ctx: typer.Context,
17
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
17
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
18
18
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
19
19
  ) -> None:
20
20
  """Show your CometAPI usage statistics (requires access token)."""
@@ -61,12 +61,12 @@ def tasks(
61
61
  str | None,
62
62
  typer.Option("--status", "-s", help=f"Filter by status: {', '.join(VALID_STATUSES)}."),
63
63
  ] = None,
64
- action: Annotated[str | None, typer.Option("--action", "-a", help="Filter by action type.")] = None,
64
+ action: Annotated[str | None, typer.Option("--action", "-a", help="Filter by action type (platform-dependent, e.g. generate, upscale, vary).")] = None,
65
65
  start: Annotated[str | None, typer.Option("--start", help="Start date (YYYY-MM-DD, ISO 8601, or Unix ts).")] = None,
66
66
  end: Annotated[str | None, typer.Option("--end", help="End date (YYYY-MM-DD, ISO 8601, or Unix ts).")] = None,
67
67
  page: Annotated[int, typer.Option("--page", help="Page number.")] = 1,
68
68
  limit: Annotated[int, typer.Option("--limit", "-l", help="Results per page.")] = 20,
69
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
69
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
70
70
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
71
71
  ) -> None:
72
72
  """Show your async task logs — Suno, MJ, Luma, Kling, etc. (requires access token)."""
@@ -46,7 +46,7 @@ def tokens(
46
46
  search: Annotated[str | None, typer.Option("--search", "-s", help="Search tokens by name or key.")] = None,
47
47
  page: Annotated[int, typer.Option("--page", "-p", help="Page number.")] = 1,
48
48
  limit: Annotated[int, typer.Option("--limit", "-l", help="Results per page.")] = 20,
49
- output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format.")] = None,
49
+ output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
50
50
  json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
51
51
  ) -> None:
52
52
  """List and search your API keys (requires access token)."""
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cometapi-cli
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Summary: CometAPI CLI — official command-line interface for the CometAPI AI gateway
5
- Project-URL: Homepage, https://github.com/CometAPI/cometapi-cli
6
- Project-URL: Repository, https://github.com/CometAPI/cometapi-cli
5
+ Project-URL: Homepage, https://github.com/cometapi-dev/cometapi-cli
6
+ Project-URL: Repository, https://github.com/cometapi-dev/cometapi-cli
7
7
  Project-URL: Documentation, https://docs.cometapi.com
8
- Project-URL: Changelog, https://github.com/CometAPI/cometapi-cli/blob/main/CHANGELOG.md
9
- Project-URL: Issues, https://github.com/CometAPI/cometapi-cli/issues
8
+ Project-URL: Changelog, https://github.com/cometapi-dev/cometapi-cli/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/cometapi-dev/cometapi-cli/issues
10
10
  Author-email: CometAPI <support@cometapi.com>
11
11
  License: MIT
12
12
  License-File: LICENSE
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
  [![Python](https://img.shields.io/pypi/pyversions/cometapi-cli)](https://pypi.org/project/cometapi-cli/)
43
43
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
44
44
 
45
- > **Official command-line interface for [CometAPI](https://cometapi.com)** — 500+ AI Model API, All In One API.
45
+ > **Official command-line interface for [CometAPI](https://www.cometapi.com/?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=homepage)** — 500+ AI Model API, All In One API.
46
46
 
47
47
  Access 500+ AI models at low cost, directly from the terminal. Chat, search models, check usage, and manage your account — all through a single API key.
48
48
 
@@ -59,7 +59,7 @@ pipx install cometapi-cli # isolated environment
59
59
  uv tool install cometapi-cli # uv
60
60
  ```
61
61
 
62
- **Prerequisites**: Python 3.10+ · [Get your API key](https://www.cometapi.com/console/token)
62
+ **Prerequisites**: Python 3.10+ · [CometAPI Key](https://www.cometapi.com/console/token?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=prerequisites)
63
63
 
64
64
  ## Quick Start
65
65
 
@@ -77,8 +77,8 @@ cometapi models --search gpt --limit 10
77
77
  Or configure manually:
78
78
 
79
79
  ```bash
80
- export COMETAPI_KEY="your-api-key" # https://www.cometapi.com/console/token
81
- export COMETAPI_ACCESS_TOKEN="your-access-token" # https://www.cometapi.com/console/personal (optional)
80
+ export COMETAPI_KEY="your-api-key" # https://www.cometapi.com/console/token?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=env-api-key
81
+ export COMETAPI_ACCESS_TOKEN="your-access-token" # https://www.cometapi.com/console/personal?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=env-access-token (optional)
82
82
  ```
83
83
 
84
84
  ## Commands
@@ -164,6 +164,20 @@ cometapi config unset api_key # Remove a value
164
164
  cometapi config path # Show config file path
165
165
  ```
166
166
 
167
+ ### Examples
168
+
169
+ ```bash
170
+ # Switch default output to JSON (all commands)
171
+ cometapi config set output_format json
172
+
173
+ # Change default model
174
+ cometapi config set default_model claude-sonnet-4-6
175
+
176
+ # Override per-command (regardless of config)
177
+ cometapi models --format yaml
178
+ cometapi balance --json
179
+ ```
180
+
167
181
  **Priority**: CLI flags > config file > environment variables > defaults
168
182
 
169
183
  | Key | Env Variable | Description |
@@ -186,7 +200,7 @@ CometAPI CLI is designed to be agent-friendly:
186
200
  ## Development
187
201
 
188
202
  ```bash
189
- git clone https://github.com/CometAPI/cometapi-cli.git
203
+ git clone https://github.com/cometapi-dev/cometapi-cli.git
190
204
  cd cometapi-cli
191
205
  uv sync --extra dev
192
206
 
@@ -199,7 +213,7 @@ uv run cometapi --version # verify
199
213
  <summary>Without uv (pip)</summary>
200
214
 
201
215
  ```bash
202
- git clone https://github.com/CometAPI/cometapi-cli.git
216
+ git clone https://github.com/cometapi-dev/cometapi-cli.git
203
217
  cd cometapi-cli
204
218
  pip install -e ".[dev]"
205
219
  pytest -v
@@ -212,7 +226,7 @@ pytest -v
212
226
  - API keys and access tokens are **never** logged or displayed in full — only the last 4 characters are shown
213
227
  - Config files are stored with restrictive permissions (`0600`)
214
228
  - Credentials should **never** be committed to version control
215
- - Create credentials at: [API Key](https://www.cometapi.com/console/token) · [Access Token](https://www.cometapi.com/console/personal)
229
+ - Create credentials at: [API Key](https://www.cometapi.com/console/token?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=security-api-key) · [Access Token](https://www.cometapi.com/console/personal?utm_source=cometapi-cli&utm_medium=readme&utm_campaign=oss&utm_content=security-access-token)
216
230
  - **Disclaimer**: You are responsible for all usage and charges incurred with your API keys
217
231
 
218
232
  ## Troubleshooting
@@ -223,6 +237,10 @@ pytest -v
223
237
  | `Connection failed` | Run `cometapi doctor` to diagnose connectivity |
224
238
  | `Access token not configured` | Only needed for `account`/`stats` commands — run `cometapi init` to add one |
225
239
 
240
+ ## Contributing
241
+
242
+ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
243
+
226
244
  ## License
227
245
 
228
- MIT
246
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,27 @@
1
+ cometapi_cli/__init__.py,sha256=PO4EJzXB-LMrMP41BGu4Cp0YrCXOG2oWfHl8uVdCw6Q,92
2
+ cometapi_cli/app.py,sha256=tFrkyfVVBYkjOA9zXP72bZ1csHI9HBnGF-Z4_4EkXkE,2784
3
+ cometapi_cli/client.py,sha256=4JkMSrjWwAQV8tmlwplZQ75gzt3zHno8ASKLgMCpYkU,9658
4
+ cometapi_cli/config.py,sha256=oJXQidKCOsKNYPnE8OfLLoOfsv0MSZEDICB6VShJRSA,3307
5
+ cometapi_cli/console.py,sha256=HFSU1gL9SDmwjBvgVgDraoaU50oTwCRpsAwYXYlVP9o,163
6
+ cometapi_cli/constants.py,sha256=EV2i-ag4ss1xGrU1CLiF9iPz8ggzNm4-U3wpYCC2pys,1601
7
+ cometapi_cli/errors.py,sha256=MQ3VXefVhdIFa4uUmoLf_O5d1EFuu9PfGYrBGuq-mSA,3703
8
+ cometapi_cli/formatters.py,sha256=QrXpsQHRYiJ49Rwz_7dOMoKxCcnRPumZMrxH21ZQaMw,4573
9
+ cometapi_cli/main.py,sha256=4NmO8MAAc_H30hahT6VNrdGdlaFlQ99AixKcRV5iDXE,168
10
+ cometapi_cli/commands/__init__.py,sha256=mihzLmD4ADkH9WnCBP2p1fn95lU3GyzTliAJ1XL5Ngg,29
11
+ cometapi_cli/commands/account.py,sha256=yGIY5iQeB4j1bISMUOqnvjcuD9HTd7VcWSvSFB6fAfo,1257
12
+ cometapi_cli/commands/balance.py,sha256=GUOdbfDV0u9h23XG3pqRf_tl3mUxWGl781b9eep4Rxc,1833
13
+ cometapi_cli/commands/chat.py,sha256=5VuNOF7O3vjD0f8eD0faZ3xL2IFnSa2BNadxencoPJk,3937
14
+ cometapi_cli/commands/chat_repl.py,sha256=b9lkYnbbaOb0AlSpRcq3wWHmnhtTXvaqeJiUImsMEBY,8176
15
+ cometapi_cli/commands/config_cmd.py,sha256=JU4ZS8ur2Sq1MjMZKaGLaI1TdM3CAUDE45qsKDOxD_8,8528
16
+ cometapi_cli/commands/doctor.py,sha256=EPcezrf64yzgYrzOdp_7tvfNM6hE2NBDzgMgO-O0Dnw,5337
17
+ cometapi_cli/commands/logs.py,sha256=9J7oT9KbCCGeD_zO9fwWj7g5QjaoT8qqJF_e81jGPX4,10310
18
+ cometapi_cli/commands/models.py,sha256=wtnZp0RqUE6HH2ZsZr6OYtHDwzUPIOcJBBxXWKOQ-Ig,2875
19
+ cometapi_cli/commands/repl.py,sha256=b5z1jmEXOsCrb6fwEUyv-IKot929NIPQQAbA1uas1D4,4283
20
+ cometapi_cli/commands/stats.py,sha256=6ULBb7rX5Q0yWuIxtYLataNPoJ_SkU3Y4RovgdVyROs,1388
21
+ cometapi_cli/commands/tasks.py,sha256=TW_Jgli7iyfIoHNyufbViAUMIieMNfsD8DMHt5mQA8Q,4625
22
+ cometapi_cli/commands/tokens.py,sha256=9KHer9MQOZvm_Ybxnhjb5_TtGgKyv3MtI5lqsHy-Qxg,3019
23
+ cometapi_cli-0.2.1.dist-info/METADATA,sha256=S_Gkmso14ZIEFTdRT4Hb57UVZxTh69fintCJ0ZQDKwE,8624
24
+ cometapi_cli-0.2.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
25
+ cometapi_cli-0.2.1.dist-info/entry_points.txt,sha256=xoiE2ZVNNWXTq0JRBtzC8hJ2JkdKuaBNz_fEd8OJpLs,50
26
+ cometapi_cli-0.2.1.dist-info/licenses/LICENSE,sha256=-rBwHQzkmLbty07abmGvQvsRrvDeEQUkPDhNJfTcjdE,1065
27
+ cometapi_cli-0.2.1.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- cometapi_cli/__init__.py,sha256=lv0NDx348LemOymOagNAd60v2BIPG3JVB5L7unHswJU,92
2
- cometapi_cli/app.py,sha256=0rUJ975zKApr5Htr1pVoAUgFUi0TXqYVFV8Z6MEUG6Y,2743
3
- cometapi_cli/client.py,sha256=4JkMSrjWwAQV8tmlwplZQ75gzt3zHno8ASKLgMCpYkU,9658
4
- cometapi_cli/config.py,sha256=oJXQidKCOsKNYPnE8OfLLoOfsv0MSZEDICB6VShJRSA,3307
5
- cometapi_cli/console.py,sha256=HFSU1gL9SDmwjBvgVgDraoaU50oTwCRpsAwYXYlVP9o,163
6
- cometapi_cli/constants.py,sha256=EV2i-ag4ss1xGrU1CLiF9iPz8ggzNm4-U3wpYCC2pys,1601
7
- cometapi_cli/errors.py,sha256=MQ3VXefVhdIFa4uUmoLf_O5d1EFuu9PfGYrBGuq-mSA,3703
8
- cometapi_cli/formatters.py,sha256=QrXpsQHRYiJ49Rwz_7dOMoKxCcnRPumZMrxH21ZQaMw,4573
9
- cometapi_cli/main.py,sha256=4NmO8MAAc_H30hahT6VNrdGdlaFlQ99AixKcRV5iDXE,168
10
- cometapi_cli/commands/__init__.py,sha256=mihzLmD4ADkH9WnCBP2p1fn95lU3GyzTliAJ1XL5Ngg,29
11
- cometapi_cli/commands/account.py,sha256=OGPRaNwiQ0IdSajiR1XV0NAYSYftvK4tZVYBCNMjQWQ,1222
12
- cometapi_cli/commands/balance.py,sha256=xGvHu3LXMqfcWUHRj4hHkV_NSJG_Mdzj0RTV-TTIG2s,1798
13
- cometapi_cli/commands/chat.py,sha256=4XqzaQzKwU00YYR0RAMYcjnqZKU3vKmUFDZxe2ml854,3856
14
- cometapi_cli/commands/chat_repl.py,sha256=b9lkYnbbaOb0AlSpRcq3wWHmnhtTXvaqeJiUImsMEBY,8176
15
- cometapi_cli/commands/config_cmd.py,sha256=W_TAHlti9AbeSdvEiXA9dy3p5-ckXm80HHzwJ8I2d3g,5953
16
- cometapi_cli/commands/doctor.py,sha256=Ue8uDi1TaZ1BJhIwveOuZhhyBw0Hob4XEerS6WBoYkw,5302
17
- cometapi_cli/commands/logs.py,sha256=O4tdtazEgWP2pl0T0a4oDsi1aV6mVkxR-BH_V-2ZZPM,11377
18
- cometapi_cli/commands/models.py,sha256=pUblCx3HBzOyskQJ7cfgxa-SCEHX73pVjSxHckEEr0A,1297
19
- cometapi_cli/commands/repl.py,sha256=foKPox6Hn3iEjh_nftnRWpvVjsNx0ZngSPhKDbKpViA,4295
20
- cometapi_cli/commands/stats.py,sha256=LfmmQX5iTJkkEvcg-NOjQRRjUXueUazMSnwz9YwazLs,1353
21
- cometapi_cli/commands/tasks.py,sha256=9gArl6qOhyG0T47KNdOjx44-vKQiI2cf1CYQQiW_k9k,4539
22
- cometapi_cli/commands/tokens.py,sha256=H74PFD5nYiwZrdP1PHxRevyfWtzIttfORundudCPjOc,2984
23
- cometapi_cli-0.1.0.dist-info/METADATA,sha256=ckfVsPTkEUfWW4zbcAsQC7nwAgTJF4m5dYZOLVjxqmQ,7637
24
- cometapi_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
25
- cometapi_cli-0.1.0.dist-info/entry_points.txt,sha256=xoiE2ZVNNWXTq0JRBtzC8hJ2JkdKuaBNz_fEd8OJpLs,50
26
- cometapi_cli-0.1.0.dist-info/licenses/LICENSE,sha256=-rBwHQzkmLbty07abmGvQvsRrvDeEQUkPDhNJfTcjdE,1065
27
- cometapi_cli-0.1.0.dist-info/RECORD,,