cometapi-cli 0.1.0__tar.gz → 0.2.0__tar.gz
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-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +46 -0
- cometapi_cli-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- cometapi_cli-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/.github/workflows/publish.yml +7 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/AGENTS.md +17 -5
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/CHANGELOG.md +3 -3
- cometapi_cli-0.2.0/CODE_OF_CONDUCT.md +54 -0
- cometapi_cli-0.2.0/CONTRIBUTING.md +48 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/PKG-INFO +29 -7
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/README.md +24 -2
- cometapi_cli-0.2.0/SECURITY.md +27 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/init.md +29 -8
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/pyproject.toml +5 -5
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/__init__.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/app.py +2 -2
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/account.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/balance.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/chat.py +3 -3
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/config_cmd.py +77 -11
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/doctor.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/logs.py +19 -48
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/models.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/repl.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/stats.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/tasks.py +2 -2
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/tokens.py +1 -1
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_logs.py +0 -14
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/.github/workflows/ci.yml +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/.gitignore +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/LICENSE +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/SKILL.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/README.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/authentication.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/account.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/balance.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/chat.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/config.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/doctor.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/logs.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/models.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/repl.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/stats.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/tasks.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/commands/tokens.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/configuration.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/errors.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/installation.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/docs/output-formats.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/skills/live-test/SKILL.md +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/client.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/__init__.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/commands/chat_repl.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/config.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/console.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/constants.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/errors.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/formatters.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/src/cometapi_cli/main.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/__init__.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/conftest.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_account.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_balance.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_chat.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_config.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_doctor.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_errors.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_formatters.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_help.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_models.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_stats.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_tasks.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/tests/test_tokens.py +0 -0
- {cometapi_cli-0.1.0 → cometapi_cli-0.2.0}/uv.lock +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug in CometAPI CLI
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: Description
|
|
9
|
+
description: A clear description of the bug.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: steps
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to Reproduce
|
|
16
|
+
description: How can we reproduce this?
|
|
17
|
+
placeholder: |
|
|
18
|
+
1. Run `cometapi ...`
|
|
19
|
+
2. ...
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: expected
|
|
24
|
+
attributes:
|
|
25
|
+
label: Expected Behavior
|
|
26
|
+
description: What did you expect to happen?
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: actual
|
|
31
|
+
attributes:
|
|
32
|
+
label: Actual Behavior
|
|
33
|
+
description: What actually happened? Include any error output.
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: environment
|
|
38
|
+
attributes:
|
|
39
|
+
label: Environment
|
|
40
|
+
description: Version and platform info.
|
|
41
|
+
placeholder: |
|
|
42
|
+
- CLI version: (cometapi --version)
|
|
43
|
+
- Python version:
|
|
44
|
+
- OS:
|
|
45
|
+
validations:
|
|
46
|
+
required: true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What problem does this feature solve?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: solution
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed Solution
|
|
16
|
+
description: How should this work?
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: alternatives
|
|
21
|
+
attributes:
|
|
22
|
+
label: Alternatives Considered
|
|
23
|
+
description: Any other approaches you've considered?
|
|
24
|
+
validations:
|
|
25
|
+
required: false
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR do? -->
|
|
4
|
+
|
|
5
|
+
## Related Issues
|
|
6
|
+
|
|
7
|
+
<!-- Link related issues: Fixes #123, Closes #456 -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Tests pass (`uv run pytest -v`)
|
|
12
|
+
- [ ] Lint passes (`uv run ruff check src/ tests/`)
|
|
13
|
+
- [ ] CHANGELOG.md updated (if user-facing change)
|
|
14
|
+
- [ ] Documentation updated (if applicable)
|
|
@@ -6,7 +6,7 @@ on:
|
|
|
6
6
|
- "v*"
|
|
7
7
|
|
|
8
8
|
permissions:
|
|
9
|
-
contents:
|
|
9
|
+
contents: write
|
|
10
10
|
id-token: write
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
@@ -28,3 +28,9 @@ jobs:
|
|
|
28
28
|
|
|
29
29
|
- name: Publish to PyPI
|
|
30
30
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
31
|
+
|
|
32
|
+
- name: Create GitHub Release
|
|
33
|
+
uses: softprops/action-gh-release@v2
|
|
34
|
+
with:
|
|
35
|
+
generate_release_notes: true
|
|
36
|
+
files: dist/*
|
|
@@ -82,24 +82,36 @@ Package name: `cometapi-cli`. Triggered automatically by pushing a `v*` tag to G
|
|
|
82
82
|
### Release SOP
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
# 1.
|
|
85
|
+
# 1. Update CHANGELOG.md with the new version section
|
|
86
|
+
|
|
87
|
+
# 2. Bump version in TWO places (must match):
|
|
86
88
|
# - pyproject.toml: version = "X.Y.Z"
|
|
87
89
|
# - src/cometapi_cli/__init__.py: __version__ = "X.Y.Z"
|
|
88
90
|
|
|
89
|
-
#
|
|
90
|
-
git
|
|
91
|
-
git
|
|
91
|
+
# 3. Merge dev → main
|
|
92
|
+
git checkout main
|
|
93
|
+
git merge dev
|
|
92
94
|
git push origin main
|
|
93
95
|
|
|
94
|
-
#
|
|
96
|
+
# 4. Tag and push — triggers publish.yml automatically
|
|
97
|
+
# (publishes to PyPI + creates GitHub Release with auto-generated notes)
|
|
95
98
|
git tag vX.Y.Z
|
|
96
99
|
git push origin vX.Y.Z
|
|
100
|
+
|
|
101
|
+
# 5. Switch back to dev
|
|
102
|
+
git checkout dev
|
|
97
103
|
```
|
|
98
104
|
|
|
99
105
|
### Verify
|
|
100
106
|
|
|
101
107
|
```bash
|
|
108
|
+
# Check PyPI
|
|
102
109
|
open https://pypi.org/project/cometapi-cli/
|
|
110
|
+
|
|
111
|
+
# Check GitHub Release
|
|
112
|
+
open https://github.com/cometapi-dev/cometapi-cli/releases
|
|
113
|
+
|
|
114
|
+
# Install and test
|
|
103
115
|
pip install cometapi-cli==X.Y.Z
|
|
104
116
|
cometapi --version
|
|
105
117
|
```
|
|
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.2.0] — 2026-04-
|
|
8
|
+
## [0.2.0] — 2026-04-13
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
@@ -18,11 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
18
|
- **Config management**: `config show/set/unset/path` subcommands, TOML config at `~/.config/cometapi/config.toml`
|
|
19
19
|
- **Persistent output_format**: set default output format in `config.toml`
|
|
20
20
|
- **Doctor diagnostics**: connectivity, auth, SDK, and config health checks
|
|
21
|
-
- **Setup wizard**: `cometapi init` with API key validation
|
|
21
|
+
- **Setup wizard**: `cometapi init` with API key validation, existing config detection, and reconfiguration flow
|
|
22
22
|
- **Shell completion**: bash, zsh, fish, powershell
|
|
23
23
|
- **Error handling**: structured exit codes (sysexits.h compatible), user-friendly messages
|
|
24
24
|
- **Security**: credential masking, 0600 file permissions, creation URL guidance
|
|
25
|
-
- **CI/CD**: GitHub Actions for testing (Python 3.10–3.13) and
|
|
25
|
+
- **CI/CD**: GitHub Actions for testing (Python 3.10–3.13), PyPI publishing, and GitHub Releases
|
|
26
26
|
|
|
27
27
|
### Changed
|
|
28
28
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to a positive environment:
|
|
15
|
+
|
|
16
|
+
- Using welcoming and inclusive language
|
|
17
|
+
- Being respectful of differing viewpoints and experiences
|
|
18
|
+
- Gracefully accepting constructive criticism
|
|
19
|
+
- Focusing on what is best for the community
|
|
20
|
+
- Showing empathy towards other community members
|
|
21
|
+
|
|
22
|
+
Examples of unacceptable behavior:
|
|
23
|
+
|
|
24
|
+
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
25
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
26
|
+
- Public or private harassment
|
|
27
|
+
- Publishing others' private information without explicit permission
|
|
28
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
29
|
+
|
|
30
|
+
## Enforcement Responsibilities
|
|
31
|
+
|
|
32
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
33
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
34
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
35
|
+
or harmful.
|
|
36
|
+
|
|
37
|
+
## Scope
|
|
38
|
+
|
|
39
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
40
|
+
an individual is officially representing the community in public spaces.
|
|
41
|
+
|
|
42
|
+
## Enforcement
|
|
43
|
+
|
|
44
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
45
|
+
reported to the community leaders responsible for enforcement at
|
|
46
|
+
**support@cometapi.com**.
|
|
47
|
+
|
|
48
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
49
|
+
|
|
50
|
+
## Attribution
|
|
51
|
+
|
|
52
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/),
|
|
53
|
+
version 2.1, available at
|
|
54
|
+
<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Contributing to CometAPI CLI
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! Here's how to get started.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/cometapi-dev/cometapi-cli.git
|
|
9
|
+
cd cometapi-cli
|
|
10
|
+
uv sync --extra dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Making Changes
|
|
14
|
+
|
|
15
|
+
1. **Fork** the repository and create a feature branch from `main`.
|
|
16
|
+
2. **Write or update tests** for your change.
|
|
17
|
+
3. **Run tests and lint** before submitting:
|
|
18
|
+
```bash
|
|
19
|
+
uv run pytest -v
|
|
20
|
+
uv run ruff check src/ tests/
|
|
21
|
+
```
|
|
22
|
+
4. **Open a pull request** with a clear description of the change.
|
|
23
|
+
|
|
24
|
+
## Code Style
|
|
25
|
+
|
|
26
|
+
- Follow [Ruff](https://docs.astral.sh/ruff/) defaults (configured in `pyproject.toml`).
|
|
27
|
+
- Keep the code typed — use type hints for all public functions.
|
|
28
|
+
- All network calls go through `CometClient` — never use raw HTTP in commands.
|
|
29
|
+
|
|
30
|
+
## Reporting Bugs
|
|
31
|
+
|
|
32
|
+
Open an issue using the **Bug Report** template. Include:
|
|
33
|
+
- CLI version (`cometapi --version`)
|
|
34
|
+
- Python version
|
|
35
|
+
- OS
|
|
36
|
+
- Steps to reproduce
|
|
37
|
+
|
|
38
|
+
## Suggesting Features
|
|
39
|
+
|
|
40
|
+
Open an issue using the **Feature Request** template.
|
|
41
|
+
|
|
42
|
+
## Code of Conduct
|
|
43
|
+
|
|
44
|
+
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you agree to abide by its terms.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cometapi-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -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
|
|
@@ -223,6 +237,14 @@ 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
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
247
|
+
|
|
226
248
|
## License
|
|
227
249
|
|
|
228
250
|
MIT
|
|
@@ -126,6 +126,20 @@ cometapi config unset api_key # Remove a value
|
|
|
126
126
|
cometapi config path # Show config file path
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### Examples
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Switch default output to JSON (all commands)
|
|
133
|
+
cometapi config set output_format json
|
|
134
|
+
|
|
135
|
+
# Change default model
|
|
136
|
+
cometapi config set default_model claude-sonnet-4-6
|
|
137
|
+
|
|
138
|
+
# Override per-command (regardless of config)
|
|
139
|
+
cometapi models --format yaml
|
|
140
|
+
cometapi balance --json
|
|
141
|
+
```
|
|
142
|
+
|
|
129
143
|
**Priority**: CLI flags > config file > environment variables > defaults
|
|
130
144
|
|
|
131
145
|
| Key | Env Variable | Description |
|
|
@@ -148,7 +162,7 @@ CometAPI CLI is designed to be agent-friendly:
|
|
|
148
162
|
## Development
|
|
149
163
|
|
|
150
164
|
```bash
|
|
151
|
-
git clone https://github.com/
|
|
165
|
+
git clone https://github.com/cometapi-dev/cometapi-cli.git
|
|
152
166
|
cd cometapi-cli
|
|
153
167
|
uv sync --extra dev
|
|
154
168
|
|
|
@@ -161,7 +175,7 @@ uv run cometapi --version # verify
|
|
|
161
175
|
<summary>Without uv (pip)</summary>
|
|
162
176
|
|
|
163
177
|
```bash
|
|
164
|
-
git clone https://github.com/
|
|
178
|
+
git clone https://github.com/cometapi-dev/cometapi-cli.git
|
|
165
179
|
cd cometapi-cli
|
|
166
180
|
pip install -e ".[dev]"
|
|
167
181
|
pytest -v
|
|
@@ -185,6 +199,14 @@ pytest -v
|
|
|
185
199
|
| `Connection failed` | Run `cometapi doctor` to diagnose connectivity |
|
|
186
200
|
| `Access token not configured` | Only needed for `account`/`stats` commands — run `cometapi init` to add one |
|
|
187
201
|
|
|
202
|
+
## Contributing
|
|
203
|
+
|
|
204
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
209
|
+
|
|
188
210
|
## License
|
|
189
211
|
|
|
190
212
|
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---------|-----------|
|
|
7
|
+
| 0.2.x | ✅ |
|
|
8
|
+
| < 0.2 | ❌ |
|
|
9
|
+
|
|
10
|
+
## Reporting a Vulnerability
|
|
11
|
+
|
|
12
|
+
If you discover a security vulnerability, please report it responsibly:
|
|
13
|
+
|
|
14
|
+
1. **Do NOT open a public issue.**
|
|
15
|
+
2. Email **support@cometapi.com** with details of the vulnerability.
|
|
16
|
+
3. Include steps to reproduce, if possible.
|
|
17
|
+
|
|
18
|
+
We will acknowledge receipt within 48 hours and aim to provide a fix or mitigation plan within 7 days.
|
|
19
|
+
|
|
20
|
+
## Security Practices
|
|
21
|
+
|
|
22
|
+
- API keys and access tokens are **never** logged or displayed in full — only the last 4 characters are shown.
|
|
23
|
+
- Config files are stored with restrictive permissions (`0600`).
|
|
24
|
+
- Credentials should **never** be committed to version control.
|
|
25
|
+
- Create credentials at:
|
|
26
|
+
- API Key: https://www.cometapi.com/console/token
|
|
27
|
+
- Access Token: https://www.cometapi.com/console/personal
|
|
@@ -18,14 +18,16 @@ No credentials required — this command sets them up.
|
|
|
18
18
|
|
|
19
19
|
## Wizard Flow
|
|
20
20
|
|
|
21
|
-
1. **
|
|
22
|
-
2. **
|
|
23
|
-
3. **
|
|
24
|
-
4. **
|
|
25
|
-
5. **
|
|
26
|
-
6. **
|
|
21
|
+
1. **Existing config check** — if a config file exists, displays a summary of current settings (secrets masked) and asks whether to continue with reconfiguration. User can abort to keep config unchanged.
|
|
22
|
+
2. **Security notice** — displays a warning about credential handling and links to creation pages.
|
|
23
|
+
3. **API key prompt** — asks for `COMETAPI_KEY`. Shows current value (masked) as default if already configured. Displays `(Enter to keep)` hint when a value exists.
|
|
24
|
+
4. **Connectivity test** — if an API key is provided, tests connectivity by calling `models.list()`.
|
|
25
|
+
5. **Access token prompt** — asks whether to configure `COMETAPI_ACCESS_TOKEN` (optional). Shows current value (masked) as default. Displays `(Enter to keep)` hint when a value exists.
|
|
26
|
+
6. **Default model** — asks for the default model to use with `chat` (default: `gpt-5.4`).
|
|
27
|
+
7. **Default output format** — asks for the default output format (default: `table`).
|
|
28
|
+
8. **Save** — writes configuration to `~/.config/cometapi/config.toml` with `0600` permissions. If no values were changed, skips writing and displays "No changes made."
|
|
27
29
|
|
|
28
|
-
## Example Session
|
|
30
|
+
## Example Session (First Run)
|
|
29
31
|
|
|
30
32
|
```
|
|
31
33
|
🚀 Welcome
|
|
@@ -49,6 +51,7 @@ Configure access token? (for account/stats commands) [y/n]: y
|
|
|
49
51
|
Access Token (COMETAPI_ACCESS_TOKEN): pat-...
|
|
50
52
|
|
|
51
53
|
Default model [gpt-5.4]: gpt-5.4
|
|
54
|
+
Default output format (table, json, yaml, csv, markdown) [table]: table
|
|
52
55
|
|
|
53
56
|
✅ Setup Complete
|
|
54
57
|
Configuration saved to /Users/you/.config/cometapi/config.toml
|
|
@@ -62,7 +65,25 @@ Next steps:
|
|
|
62
65
|
|
|
63
66
|
## Re-Running Init
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
When a configuration file already exists, `init` displays a summary of current settings and prompts for confirmation before proceeding:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
📋 Current Config
|
|
72
|
+
Existing configuration found:
|
|
73
|
+
|
|
74
|
+
api_key = ****abcd
|
|
75
|
+
access_token = ****wxyz
|
|
76
|
+
default_model = gpt-5.4
|
|
77
|
+
output_format = table
|
|
78
|
+
|
|
79
|
+
Press Enter on each prompt to keep the current value.
|
|
80
|
+
|
|
81
|
+
Continue with reconfiguration? [Y/n]:
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Press **Enter** or **y** to proceed — each prompt shows `(Enter to keep)` so you can skip unchanged fields.
|
|
85
|
+
- Press **n** to abort — config file remains untouched.
|
|
86
|
+
- If you go through all prompts without changing anything, the wizard prints "No changes made" and does not rewrite the file.
|
|
66
87
|
|
|
67
88
|
## Credential Links
|
|
68
89
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cometapi-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "CometAPI CLI — official command-line interface for the CometAPI AI gateway"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -40,11 +40,11 @@ dev = [
|
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
43
|
-
Homepage = "https://github.com/
|
|
44
|
-
Repository = "https://github.com/
|
|
43
|
+
Homepage = "https://github.com/cometapi-dev/cometapi-cli"
|
|
44
|
+
Repository = "https://github.com/cometapi-dev/cometapi-cli"
|
|
45
45
|
Documentation = "https://docs.cometapi.com"
|
|
46
|
-
Changelog = "https://github.com/
|
|
47
|
-
Issues = "https://github.com/
|
|
46
|
+
Changelog = "https://github.com/cometapi-dev/cometapi-cli/blob/main/CHANGELOG.md"
|
|
47
|
+
Issues = "https://github.com/cometapi-dev/cometapi-cli/issues"
|
|
48
48
|
|
|
49
49
|
[project.scripts]
|
|
50
50
|
cometapi = "cometapi_cli.app:app"
|
|
@@ -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(
|
|
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()
|
|
@@ -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
|
-
|
|
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
|
|
|
@@ -16,7 +16,7 @@ def models(
|
|
|
16
16
|
ctx: typer.Context,
|
|
17
17
|
search: Annotated[str | None, typer.Option("--search", "-s", help="Filter models by name.")] = None,
|
|
18
18
|
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,
|
|
19
|
+
output_format: Annotated[OutputFormat | None, typer.Option("--format", "-f", help="Output format (table, json, yaml, csv, markdown).")] = None,
|
|
20
20
|
json_output: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
|
|
21
21
|
) -> None:
|
|
22
22
|
"""List available models."""
|
|
@@ -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",
|
|
@@ -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)."""
|
|
@@ -61,11 +61,6 @@ class TestLogsTable:
|
|
|
61
61
|
start_timestamp=None, end_timestamp=None, group=None,
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
-
def test_logs_search(self, cli_runner, patched_client):
|
|
65
|
-
result = cli_runner("logs", "--search", "claude")
|
|
66
|
-
assert result.exit_code == 0
|
|
67
|
-
patched_client.search_logs.assert_called_once_with(keyword="claude")
|
|
68
|
-
|
|
69
64
|
def test_logs_filter_start_date(self, cli_runner, patched_client):
|
|
70
65
|
result = cli_runner("logs", "--start", "2024-01-15")
|
|
71
66
|
assert result.exit_code == 0
|
|
@@ -226,12 +221,3 @@ class TestLogsDetail:
|
|
|
226
221
|
data = json.loads(result.output)
|
|
227
222
|
assert len(data) == 2
|
|
228
223
|
|
|
229
|
-
|
|
230
|
-
class TestLogsSearchDeprecation:
|
|
231
|
-
"""Test --search deprecation warning."""
|
|
232
|
-
|
|
233
|
-
def test_logs_search_deprecation_warning(self, cli_runner, patched_client):
|
|
234
|
-
result = cli_runner("logs", "--search", "anything")
|
|
235
|
-
assert result.exit_code == 0
|
|
236
|
-
patched_client.search_logs.assert_called_once_with(keyword="anything")
|
|
237
|
-
# Warning should still be emitted (may be on stderr).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|