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 +1 -1
- cometapi_cli/app.py +2 -2
- cometapi_cli/commands/account.py +1 -1
- cometapi_cli/commands/balance.py +1 -1
- cometapi_cli/commands/chat.py +3 -3
- cometapi_cli/commands/config_cmd.py +77 -11
- cometapi_cli/commands/doctor.py +1 -1
- cometapi_cli/commands/logs.py +19 -48
- cometapi_cli/commands/models.py +57 -6
- cometapi_cli/commands/repl.py +1 -1
- cometapi_cli/commands/stats.py +1 -1
- cometapi_cli/commands/tasks.py +2 -2
- cometapi_cli/commands/tokens.py +1 -1
- {cometapi_cli-0.1.0.dist-info → cometapi_cli-0.2.1.dist-info}/METADATA +31 -13
- cometapi_cli-0.2.1.dist-info/RECORD +27 -0
- cometapi_cli-0.1.0.dist-info/RECORD +0 -27
- {cometapi_cli-0.1.0.dist-info → cometapi_cli-0.2.1.dist-info}/WHEEL +0 -0
- {cometapi_cli-0.1.0.dist-info → cometapi_cli-0.2.1.dist-info}/entry_points.txt +0 -0
- {cometapi_cli-0.1.0.dist-info → cometapi_cli-0.2.1.dist-info}/licenses/LICENSE +0 -0
cometapi_cli/__init__.py
CHANGED
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,
|
cometapi_cli/commands/account.py
CHANGED
|
@@ -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)."""
|
cometapi_cli/commands/balance.py
CHANGED
|
@@ -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.
|
cometapi_cli/commands/chat.py
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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="
|
|
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()
|
cometapi_cli/commands/doctor.py
CHANGED
|
@@ -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."""
|
cometapi_cli/commands/logs.py
CHANGED
|
@@ -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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
cometapi_cli/commands/models.py
CHANGED
|
@@ -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[
|
|
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 =
|
|
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"
|
|
94
|
+
columns={"id": "cyan"},
|
|
44
95
|
)
|
cometapi_cli/commands/repl.py
CHANGED
|
@@ -71,7 +71,7 @@ def run_repl() -> None:
|
|
|
71
71
|
completer = WordCompleter(
|
|
72
72
|
REPL_COMMANDS + [
|
|
73
73
|
"--json", "--format", "--model", "--system",
|
|
74
|
-
"--
|
|
74
|
+
"--limit", "--help", "--page",
|
|
75
75
|
"--type", "--token-name", "--start", "--end",
|
|
76
76
|
"--group", "--export", "--platform", "--task-id",
|
|
77
77
|
"--status", "--action",
|
cometapi_cli/commands/stats.py
CHANGED
|
@@ -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)."""
|
cometapi_cli/commands/tasks.py
CHANGED
|
@@ -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)."""
|
cometapi_cli/commands/tokens.py
CHANGED
|
@@ -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
|
|
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/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
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/
|
|
9
|
-
Project-URL: Issues, https://github.com/
|
|
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
|
[](https://pypi.org/project/cometapi-cli/)
|
|
43
43
|
[](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+ · [
|
|
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/
|
|
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/
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|