akta-pro-cli 0.3.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.
- akta_pro_cli-0.3.0/LICENSE +21 -0
- akta_pro_cli-0.3.0/PKG-INFO +134 -0
- akta_pro_cli-0.3.0/README.md +106 -0
- akta_pro_cli-0.3.0/pyproject.toml +62 -0
- akta_pro_cli-0.3.0/setup.cfg +4 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/__init__.py +8 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/__main__.py +25 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/app.py +94 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/client.py +102 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/__init__.py +1 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/account.py +35 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/alternative.py +109 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/auth.py +123 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/company.py +184 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/config.py +54 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/industry.py +46 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/news.py +232 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/commands/update.py +64 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/config.py +71 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/console.py +10 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/news_tags.py +102 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/options.py +21 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/runtime.py +157 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli/update.py +79 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/PKG-INFO +134 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/SOURCES.txt +29 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/dependency_links.txt +1 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/entry_points.txt +2 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/requires.txt +8 -0
- akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/top_level.txt +1 -0
- akta_pro_cli-0.3.0/tests/test_cli.py +374 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wokelo AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: akta-pro-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Command-line client for the akta.pro API (api.akta.pro)
|
|
5
|
+
Author-email: Wokelo <support@wokelo.ai>
|
|
6
|
+
Maintainer-email: Wokelo <support@wokelo.ai>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://akta.pro/
|
|
9
|
+
Project-URL: Repository, https://github.com/Wokelo-AI/akta-pro-cli
|
|
10
|
+
Keywords: akta-pro,akta.pro,cli,company-intelligence,market-intelligence
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: httpx>=0.27
|
|
21
|
+
Requires-Dist: typer>=0.12
|
|
22
|
+
Requires-Dist: rich>=13
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
25
|
+
Requires-Dist: respx>=0.22; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# akta.pro CLI (`akta-pro`)
|
|
30
|
+
|
|
31
|
+
A command-line client for the akta.pro REST API (`https://api.akta.pro/api/v1`) — a
|
|
32
|
+
sibling of the akta.pro MCP server. Both are thin clients over the same endpoints;
|
|
33
|
+
the CLI is the presentation layer for humans and shell scripts. It ships as its
|
|
34
|
+
own self-contained distribution (`akta-pro-cli`) with no MCP-server code.
|
|
35
|
+
|
|
36
|
+
Source: [`src/akta_pro_cli/`](src/akta_pro_cli). API reference: <https://docs.akta.pro>.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
Published on [PyPI](https://pypi.org/project/akta-pro-cli/). Install with `pipx`
|
|
41
|
+
(recommended for CLIs — isolated env) or `pip`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pipx install akta-pro-cli
|
|
45
|
+
# or
|
|
46
|
+
pip install akta-pro-cli
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then run `akta-pro --help`. Full prerequisites, auth, update, and troubleshooting
|
|
50
|
+
steps are in **[INSTALL.md](INSTALL.md)**.
|
|
51
|
+
|
|
52
|
+
The `akta-pro` command depends only on `httpx`, `typer`, `rich`.
|
|
53
|
+
|
|
54
|
+
## Authentication
|
|
55
|
+
|
|
56
|
+
v1 authenticates with an akta.pro API key (`wk_...`), minted at
|
|
57
|
+
<https://playground.akta.pro> (sign up → **API Keys**). Three ways to supply it,
|
|
58
|
+
in precedence order:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
akta-pro --api-key wk_... company search Canva # 1. explicit flag
|
|
62
|
+
export AKTA_PRO_API_KEY=wk_... # 2. environment variable
|
|
63
|
+
akta-pro login # 3. stored (prompts, or --api-key)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`akta-pro login` validates the key against a free endpoint and stores it at
|
|
67
|
+
`~/.config/akta-pro/credentials.json` (mode 0600; `%APPDATA%\akta-pro` on Windows).
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
akta-pro login --api-key wk_... # store without the prompt
|
|
71
|
+
akta-pro whoami # show the active key (masked), its source, and validate
|
|
72
|
+
akta-pro logout # remove the stored key
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
| Command | Endpoint | Cost | Notes |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| `akta-pro account` | `/mcp/account` | free | your tier + credit balance |
|
|
80
|
+
| `akta-pro company search <query>` | `/company/search` | free | run first; returns `uuid` |
|
|
81
|
+
| `akta-pro company data <company> -s ...` | `/company/enrichment` (JSON; `--markdown` → `/company/enrichment/markdown`) | per section | requires ≥1 `--section` |
|
|
82
|
+
| `akta-pro company concise <company>` | `/company/enrichment/concise` | 8 | slimmed JSON |
|
|
83
|
+
| `akta-pro industry search <query>` | `/industry/search` | free | codes feed `news signals --industry` |
|
|
84
|
+
| `akta-pro news signals [filters]` | `/news` | 0.1 + 0.01/article | anchor with `--company/--industry/--query/--title` (all optional); rich filters (`--country`, `--entity-*`, `--naics/--sic/--iptc/--iab`, `--blacklist`); compact list with `id`s |
|
|
85
|
+
| `akta-pro news detail <id>...` | `/news/by-id/` | 0.1 + 0.01/article | full bodies for ids from `signals` (max 10) |
|
|
86
|
+
| `akta-pro news types` | none (embedded) | free | tag codes for `--type-code`; offline, no key |
|
|
87
|
+
| `akta-pro headcount <company>` | `/company/headcount-trends` | 2.5 | Subscription/Enterprise |
|
|
88
|
+
| `akta-pro traffic <company>` | `/company/website-traffic` | 1.5 | Subscription/Enterprise |
|
|
89
|
+
| `akta-pro jobs <company>` | `/company/jobs` | 3 | Subscription/Enterprise |
|
|
90
|
+
| `akta-pro posts <company>` | `/company/posts` | 1.5 | Subscription/Enterprise |
|
|
91
|
+
| `akta-pro reviews employees <company>` | `/company/employee-reviews` | 1.5/50 | Subscription/Enterprise; `-n/--limit` max 100 |
|
|
92
|
+
| `akta-pro reviews products <company>` | `/company/product-reviews` | 1.5/50 | catalog first, then `--product-id`; `-n/--limit` max 50 per product |
|
|
93
|
+
|
|
94
|
+
`company data` sections (each billed separately): `firmographic`,
|
|
95
|
+
`business_model`, `company_assessment`, `trust_signal`, `company_hierarchy`,
|
|
96
|
+
`digital_presence`, `financial_estimate`, `location`, `management_profile`,
|
|
97
|
+
`product_offering`, `strategic_signal`, `customer_profile`, `industry`,
|
|
98
|
+
`technology`, plus enterprise-only `funding_detail` (3) and `mna_and_investment`
|
|
99
|
+
(5). There is no "all" — choose explicitly. The two enterprise sections are
|
|
100
|
+
auto-skipped (not an error) for non-enterprise callers, with a note.
|
|
101
|
+
|
|
102
|
+
Run `akta-pro <command> --help` for every flag.
|
|
103
|
+
|
|
104
|
+
### Examples
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
akta-pro account # check tier + credits
|
|
108
|
+
akta-pro company search "Canva"
|
|
109
|
+
akta-pro company data canva.com -s firmographic -s technology # rendered Markdown
|
|
110
|
+
akta-pro company data canva.com -s firmographic --raw # raw Markdown (pipe/save)
|
|
111
|
+
akta-pro industry search "warehouse automation"
|
|
112
|
+
akta-pro news types # find type codes
|
|
113
|
+
akta-pro news signals --company canva.com -t SD01 -n 20 # product-launch news
|
|
114
|
+
akta-pro news signals --query "crude oil prices" --json | jq '.data[].id'
|
|
115
|
+
akta-pro news detail 12345 12346 # full bodies for those ids
|
|
116
|
+
akta-pro headcount canva.com
|
|
117
|
+
akta-pro reviews products canva.com # list catalog + ids
|
|
118
|
+
akta-pro reviews products canva.com --product-id p_123 -n 50
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Output & exit codes
|
|
122
|
+
|
|
123
|
+
- **Default (TTY):** a Rich table for list results (search, industry, news),
|
|
124
|
+
rendered Markdown for `company data`, and pretty-printed JSON for nested
|
|
125
|
+
objects.
|
|
126
|
+
- **`--json`:** clean, unstyled JSON on **stdout** (valid for `| jq`). Applied
|
|
127
|
+
automatically when stdout is piped/redirected. For `company data`, `--json`
|
|
128
|
+
(alias `--raw`) emits the raw Markdown.
|
|
129
|
+
- **`-o/--output FILE`:** writes the raw JSON/text payload to a file.
|
|
130
|
+
- **`credits_consumed`** is printed to **stderr** (silence with `-q/--quiet`),
|
|
131
|
+
so it never pollutes piped JSON.
|
|
132
|
+
|
|
133
|
+
Exit codes: `0` success · `2` bad input · `3` auth (no/invalid key or `403`) ·
|
|
134
|
+
`4` other API/network error · `5` timeout.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# akta.pro CLI (`akta-pro`)
|
|
2
|
+
|
|
3
|
+
A command-line client for the akta.pro REST API (`https://api.akta.pro/api/v1`) — a
|
|
4
|
+
sibling of the akta.pro MCP server. Both are thin clients over the same endpoints;
|
|
5
|
+
the CLI is the presentation layer for humans and shell scripts. It ships as its
|
|
6
|
+
own self-contained distribution (`akta-pro-cli`) with no MCP-server code.
|
|
7
|
+
|
|
8
|
+
Source: [`src/akta_pro_cli/`](src/akta_pro_cli). API reference: <https://docs.akta.pro>.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
Published on [PyPI](https://pypi.org/project/akta-pro-cli/). Install with `pipx`
|
|
13
|
+
(recommended for CLIs — isolated env) or `pip`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pipx install akta-pro-cli
|
|
17
|
+
# or
|
|
18
|
+
pip install akta-pro-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then run `akta-pro --help`. Full prerequisites, auth, update, and troubleshooting
|
|
22
|
+
steps are in **[INSTALL.md](INSTALL.md)**.
|
|
23
|
+
|
|
24
|
+
The `akta-pro` command depends only on `httpx`, `typer`, `rich`.
|
|
25
|
+
|
|
26
|
+
## Authentication
|
|
27
|
+
|
|
28
|
+
v1 authenticates with an akta.pro API key (`wk_...`), minted at
|
|
29
|
+
<https://playground.akta.pro> (sign up → **API Keys**). Three ways to supply it,
|
|
30
|
+
in precedence order:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
akta-pro --api-key wk_... company search Canva # 1. explicit flag
|
|
34
|
+
export AKTA_PRO_API_KEY=wk_... # 2. environment variable
|
|
35
|
+
akta-pro login # 3. stored (prompts, or --api-key)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`akta-pro login` validates the key against a free endpoint and stores it at
|
|
39
|
+
`~/.config/akta-pro/credentials.json` (mode 0600; `%APPDATA%\akta-pro` on Windows).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
akta-pro login --api-key wk_... # store without the prompt
|
|
43
|
+
akta-pro whoami # show the active key (masked), its source, and validate
|
|
44
|
+
akta-pro logout # remove the stored key
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
| Command | Endpoint | Cost | Notes |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `akta-pro account` | `/mcp/account` | free | your tier + credit balance |
|
|
52
|
+
| `akta-pro company search <query>` | `/company/search` | free | run first; returns `uuid` |
|
|
53
|
+
| `akta-pro company data <company> -s ...` | `/company/enrichment` (JSON; `--markdown` → `/company/enrichment/markdown`) | per section | requires ≥1 `--section` |
|
|
54
|
+
| `akta-pro company concise <company>` | `/company/enrichment/concise` | 8 | slimmed JSON |
|
|
55
|
+
| `akta-pro industry search <query>` | `/industry/search` | free | codes feed `news signals --industry` |
|
|
56
|
+
| `akta-pro news signals [filters]` | `/news` | 0.1 + 0.01/article | anchor with `--company/--industry/--query/--title` (all optional); rich filters (`--country`, `--entity-*`, `--naics/--sic/--iptc/--iab`, `--blacklist`); compact list with `id`s |
|
|
57
|
+
| `akta-pro news detail <id>...` | `/news/by-id/` | 0.1 + 0.01/article | full bodies for ids from `signals` (max 10) |
|
|
58
|
+
| `akta-pro news types` | none (embedded) | free | tag codes for `--type-code`; offline, no key |
|
|
59
|
+
| `akta-pro headcount <company>` | `/company/headcount-trends` | 2.5 | Subscription/Enterprise |
|
|
60
|
+
| `akta-pro traffic <company>` | `/company/website-traffic` | 1.5 | Subscription/Enterprise |
|
|
61
|
+
| `akta-pro jobs <company>` | `/company/jobs` | 3 | Subscription/Enterprise |
|
|
62
|
+
| `akta-pro posts <company>` | `/company/posts` | 1.5 | Subscription/Enterprise |
|
|
63
|
+
| `akta-pro reviews employees <company>` | `/company/employee-reviews` | 1.5/50 | Subscription/Enterprise; `-n/--limit` max 100 |
|
|
64
|
+
| `akta-pro reviews products <company>` | `/company/product-reviews` | 1.5/50 | catalog first, then `--product-id`; `-n/--limit` max 50 per product |
|
|
65
|
+
|
|
66
|
+
`company data` sections (each billed separately): `firmographic`,
|
|
67
|
+
`business_model`, `company_assessment`, `trust_signal`, `company_hierarchy`,
|
|
68
|
+
`digital_presence`, `financial_estimate`, `location`, `management_profile`,
|
|
69
|
+
`product_offering`, `strategic_signal`, `customer_profile`, `industry`,
|
|
70
|
+
`technology`, plus enterprise-only `funding_detail` (3) and `mna_and_investment`
|
|
71
|
+
(5). There is no "all" — choose explicitly. The two enterprise sections are
|
|
72
|
+
auto-skipped (not an error) for non-enterprise callers, with a note.
|
|
73
|
+
|
|
74
|
+
Run `akta-pro <command> --help` for every flag.
|
|
75
|
+
|
|
76
|
+
### Examples
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
akta-pro account # check tier + credits
|
|
80
|
+
akta-pro company search "Canva"
|
|
81
|
+
akta-pro company data canva.com -s firmographic -s technology # rendered Markdown
|
|
82
|
+
akta-pro company data canva.com -s firmographic --raw # raw Markdown (pipe/save)
|
|
83
|
+
akta-pro industry search "warehouse automation"
|
|
84
|
+
akta-pro news types # find type codes
|
|
85
|
+
akta-pro news signals --company canva.com -t SD01 -n 20 # product-launch news
|
|
86
|
+
akta-pro news signals --query "crude oil prices" --json | jq '.data[].id'
|
|
87
|
+
akta-pro news detail 12345 12346 # full bodies for those ids
|
|
88
|
+
akta-pro headcount canva.com
|
|
89
|
+
akta-pro reviews products canva.com # list catalog + ids
|
|
90
|
+
akta-pro reviews products canva.com --product-id p_123 -n 50
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Output & exit codes
|
|
94
|
+
|
|
95
|
+
- **Default (TTY):** a Rich table for list results (search, industry, news),
|
|
96
|
+
rendered Markdown for `company data`, and pretty-printed JSON for nested
|
|
97
|
+
objects.
|
|
98
|
+
- **`--json`:** clean, unstyled JSON on **stdout** (valid for `| jq`). Applied
|
|
99
|
+
automatically when stdout is piped/redirected. For `company data`, `--json`
|
|
100
|
+
(alias `--raw`) emits the raw Markdown.
|
|
101
|
+
- **`-o/--output FILE`:** writes the raw JSON/text payload to a file.
|
|
102
|
+
- **`credits_consumed`** is printed to **stderr** (silence with `-q/--quiet`),
|
|
103
|
+
so it never pollutes piped JSON.
|
|
104
|
+
|
|
105
|
+
Exit codes: `0` success · `2` bad input · `3` auth (no/invalid key or `403`) ·
|
|
106
|
+
`4` other API/network error · `5` timeout.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "akta-pro-cli"
|
|
3
|
+
description = "Command-line client for the akta.pro API (api.akta.pro)"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [{ name = "Wokelo", email = "support@wokelo.ai" }]
|
|
8
|
+
maintainers = [{ name = "Wokelo", email = "support@wokelo.ai" }]
|
|
9
|
+
keywords = ["akta-pro", "akta.pro", "cli", "company-intelligence", "market-intelligence"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"httpx>=0.27",
|
|
20
|
+
"typer>=0.12",
|
|
21
|
+
"rich>=13",
|
|
22
|
+
]
|
|
23
|
+
dynamic = ["version"]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = [
|
|
27
|
+
"pytest>=8.0",
|
|
28
|
+
"respx>=0.22",
|
|
29
|
+
"ruff>=0.6",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://akta.pro/"
|
|
34
|
+
Repository = "https://github.com/Wokelo-AI/akta-pro-cli"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
akta-pro = "akta_pro_cli.__main__:main"
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["setuptools>=68"]
|
|
41
|
+
build-backend = "setuptools.build_meta"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.dynamic]
|
|
47
|
+
version = { attr = "akta_pro_cli.__version__" }
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
addopts = "-q"
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 100
|
|
55
|
+
target-version = "py311"
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
59
|
+
ignore = [
|
|
60
|
+
"E501", # line length — long help strings; left to the formatter
|
|
61
|
+
"UP042", # keep explicit `str, Enum` (avoids StrEnum's str() behaviour change)
|
|
62
|
+
]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""akta.pro CLI — a command-line client for the akta.pro REST API (https://api.akta.pro).
|
|
2
|
+
|
|
3
|
+
A standalone client (its own `akta-pro-cli` distribution) exposing the `akta-pro`
|
|
4
|
+
console command; a sibling of the akta.pro MCP server over the same `/api/v1`
|
|
5
|
+
endpoints, with no MCP-server code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Entry point for `akta-pro` and `python -m akta_pro_cli`.
|
|
2
|
+
|
|
3
|
+
Lazily imports the Typer app so a broken/partial install prints a helpful hint
|
|
4
|
+
instead of an ImportError traceback.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> None:
|
|
13
|
+
try:
|
|
14
|
+
from akta_pro_cli.app import app
|
|
15
|
+
except ModuleNotFoundError as exc: # e.g. a broken install missing typer/rich
|
|
16
|
+
sys.stderr.write(
|
|
17
|
+
f"The akta.pro CLI is missing a dependency ({exc.name}).\n"
|
|
18
|
+
"Reinstall with: pipx install akta-pro-cli\n"
|
|
19
|
+
)
|
|
20
|
+
raise SystemExit(1) from exc
|
|
21
|
+
app()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Assembles the `akta-pro` Typer application: root callback + command tree."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from akta_pro_cli import __version__
|
|
10
|
+
from akta_pro_cli.client import DEFAULT_BASE_URL
|
|
11
|
+
from akta_pro_cli.commands import (
|
|
12
|
+
account,
|
|
13
|
+
alternative,
|
|
14
|
+
auth,
|
|
15
|
+
company,
|
|
16
|
+
config,
|
|
17
|
+
industry,
|
|
18
|
+
news,
|
|
19
|
+
update,
|
|
20
|
+
)
|
|
21
|
+
from akta_pro_cli.console import err, out
|
|
22
|
+
from akta_pro_cli.runtime import AppContext
|
|
23
|
+
|
|
24
|
+
app = typer.Typer(
|
|
25
|
+
no_args_is_help=True,
|
|
26
|
+
add_completion=True,
|
|
27
|
+
rich_markup_mode="rich",
|
|
28
|
+
help="akta.pro CLI — company & market intelligence from api.akta.pro.",
|
|
29
|
+
epilog="Auth: set AKTA_PRO_API_KEY, pass --api-key, or run `akta-pro login`. Docs: https://docs.akta.pro",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _version_callback(value: bool) -> None:
|
|
34
|
+
if value:
|
|
35
|
+
out.print(f"akta-pro {__version__}")
|
|
36
|
+
# Best-effort, cached (~daily) hint — interactive only, to stderr so it
|
|
37
|
+
# never pollutes a scripted `akta-pro --version`. Never blocks or errors.
|
|
38
|
+
if out.is_terminal:
|
|
39
|
+
try:
|
|
40
|
+
from akta_pro_cli.update import cached_latest, is_newer
|
|
41
|
+
|
|
42
|
+
latest = cached_latest(timeout=2.0)
|
|
43
|
+
if latest and is_newer(latest, __version__):
|
|
44
|
+
err.print(f"[dim]A newer version v{latest} is available — run `akta-pro update`.[/]")
|
|
45
|
+
except Exception:
|
|
46
|
+
pass
|
|
47
|
+
raise typer.Exit()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.callback()
|
|
51
|
+
def main(
|
|
52
|
+
ctx: typer.Context,
|
|
53
|
+
api_key: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
typer.Option("--api-key", envvar="AKTA_PRO_API_KEY", show_default=False, help="akta.pro API key (wk_...)."),
|
|
56
|
+
] = None,
|
|
57
|
+
base_url: Annotated[
|
|
58
|
+
str | None,
|
|
59
|
+
typer.Option(
|
|
60
|
+
"--base-url",
|
|
61
|
+
envvar="AKTA_PRO_API_BASE_URL",
|
|
62
|
+
show_default=False,
|
|
63
|
+
help=f"Override the API base URL (default {DEFAULT_BASE_URL}; or persist it via `akta-pro login --base-url …`).",
|
|
64
|
+
),
|
|
65
|
+
] = None,
|
|
66
|
+
quiet: Annotated[
|
|
67
|
+
bool,
|
|
68
|
+
typer.Option("--quiet", "-q", help="Suppress the credits line on stderr."),
|
|
69
|
+
] = False,
|
|
70
|
+
timeout: Annotated[
|
|
71
|
+
float,
|
|
72
|
+
typer.Option("--timeout", help="HTTP request timeout in seconds."),
|
|
73
|
+
] = 30.0,
|
|
74
|
+
version: Annotated[
|
|
75
|
+
bool | None,
|
|
76
|
+
typer.Option("--version", callback=_version_callback, is_eager=True, help="Show version and exit."),
|
|
77
|
+
] = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Global options. Pass these before the command, e.g. `akta-pro --api-key wk_… company search Canva`."""
|
|
80
|
+
ctx.obj = AppContext(api_key=api_key, base_url=base_url, quiet=quiet, timeout=timeout)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Command groups
|
|
84
|
+
app.add_typer(company.app, name="company")
|
|
85
|
+
app.add_typer(industry.app, name="industry")
|
|
86
|
+
app.add_typer(news.app, name="news") # signals, detail, types
|
|
87
|
+
app.add_typer(alternative.reviews_app, name="reviews")
|
|
88
|
+
|
|
89
|
+
# Top-level commands
|
|
90
|
+
auth.register(app) # login, logout, whoami
|
|
91
|
+
account.register(app) # account
|
|
92
|
+
config.register(app) # config show / base-url
|
|
93
|
+
update.register(app) # update (self-update / check)
|
|
94
|
+
alternative.register(app) # headcount, traffic, jobs, posts
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Standalone synchronous HTTP client for the akta.pro REST API.
|
|
2
|
+
|
|
3
|
+
Deliberately self-contained (no dependency on `akta_mcp.*`): the server's
|
|
4
|
+
`AktaClient` reads its key from a request-scoped ContextVar set by the OAuth
|
|
5
|
+
middleware and transitively imports the Redis/Postgres token stores, none of
|
|
6
|
+
which a CLI wants. This mirrors the server's error handling and redirect
|
|
7
|
+
behaviour but takes the API key explicitly and runs synchronously.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from . import __version__
|
|
15
|
+
|
|
16
|
+
DEFAULT_BASE_URL = "https://api.akta.pro/api/v1"
|
|
17
|
+
|
|
18
|
+
# Sent on every request so the backend can distinguish (and version-track) CLI
|
|
19
|
+
# traffic. Mirrors the MCP's `X-Client-Source: AKTA-MCP`.
|
|
20
|
+
CLIENT_SOURCE = f"AKTA-PRO-CLI/{__version__}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AktaAPIError(RuntimeError):
|
|
24
|
+
"""An HTTP error from the akta.pro API, preserving the status code and body."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, message: str, *, status_code: int, body: dict | None = None):
|
|
27
|
+
super().__init__(message)
|
|
28
|
+
self.status_code = status_code
|
|
29
|
+
self.body = body
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_ERROR_MESSAGES = {
|
|
33
|
+
400: "Bad request — check the parameters.",
|
|
34
|
+
401: "Authentication failed. Check your API key (`akta-pro login`).",
|
|
35
|
+
403: (
|
|
36
|
+
"Access denied — your plan or credit balance does not cover this data "
|
|
37
|
+
"(alternative signals require Subscription/Enterprise; Funding and M&A "
|
|
38
|
+
"sections are enterprise-only)."
|
|
39
|
+
),
|
|
40
|
+
404: "Not found.",
|
|
41
|
+
429: "Rate limit exceeded. Retry with backoff.",
|
|
42
|
+
500: "akta.pro server error. Please try again later.",
|
|
43
|
+
502: "akta.pro service unavailable. Please retry.",
|
|
44
|
+
503: "akta.pro service unavailable. Please retry.",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _sanitize_http_error(exc: httpx.HTTPStatusError) -> AktaAPIError:
|
|
49
|
+
status = exc.response.status_code
|
|
50
|
+
msg = _ERROR_MESSAGES.get(status, f"Request failed with status {status}.")
|
|
51
|
+
try:
|
|
52
|
+
body = exc.response.json()
|
|
53
|
+
except ValueError:
|
|
54
|
+
body = None
|
|
55
|
+
if isinstance(body, dict):
|
|
56
|
+
detail = body.get("detail") or body.get("message") or body.get("error")
|
|
57
|
+
if detail:
|
|
58
|
+
msg = f"{msg} ({detail})"
|
|
59
|
+
return AktaAPIError(msg, status_code=status, body=body if isinstance(body, dict) else None)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _clean(params: dict | None) -> dict:
|
|
63
|
+
# Drop unset optional params so they aren't serialized onto the query string.
|
|
64
|
+
return {k: v for k, v in (params or {}).items() if v is not None}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AktaClient:
|
|
68
|
+
"""Thin synchronous httpx wrapper that sends `x-api-key` on every GET."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, base_url: str, api_key: str, timeout: float = 30.0):
|
|
71
|
+
self._api_key = api_key
|
|
72
|
+
# follow_redirects: several akta.pro routes are declared with a trailing slash,
|
|
73
|
+
# so a slashless path 307-redirects to the canonical one. The redirect is
|
|
74
|
+
# same-origin, so x-api-key is re-sent.
|
|
75
|
+
self._http = httpx.Client(base_url=base_url, timeout=timeout, follow_redirects=True)
|
|
76
|
+
|
|
77
|
+
def _headers(self) -> dict:
|
|
78
|
+
return {"x-api-key": self._api_key, "X-Client-Source": CLIENT_SOURCE}
|
|
79
|
+
|
|
80
|
+
def get(self, path: str, params: dict | None = None):
|
|
81
|
+
"""GET, returning parsed JSON when the response is JSON, else raw text.
|
|
82
|
+
|
|
83
|
+
Text is returned for Akta's server-rendered Markdown endpoints (e.g.
|
|
84
|
+
`/company/enrichment/markdown`).
|
|
85
|
+
"""
|
|
86
|
+
resp = self._http.get(path, params=_clean(params), headers=self._headers())
|
|
87
|
+
try:
|
|
88
|
+
resp.raise_for_status()
|
|
89
|
+
except httpx.HTTPStatusError as exc:
|
|
90
|
+
raise _sanitize_http_error(exc) from None
|
|
91
|
+
if "json" in resp.headers.get("content-type", "").lower():
|
|
92
|
+
return resp.json()
|
|
93
|
+
return resp.text
|
|
94
|
+
|
|
95
|
+
def close(self) -> None:
|
|
96
|
+
self._http.close()
|
|
97
|
+
|
|
98
|
+
def __enter__(self) -> AktaClient:
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
def __exit__(self, *exc: object) -> None:
|
|
102
|
+
self.close()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""akta.pro CLI command modules."""
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""`akta-pro account` — the caller's plan tier and credit balance."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
|
|
8
|
+
from akta_pro_cli.options import JsonOpt, OutOpt
|
|
9
|
+
from akta_pro_cli.runtime import emit, fetch
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _account_table(result: object) -> Table | None:
|
|
13
|
+
if not isinstance(result, dict):
|
|
14
|
+
return None
|
|
15
|
+
table = Table(title="akta.pro account", show_header=False)
|
|
16
|
+
table.add_column("Field", style="bold")
|
|
17
|
+
table.add_column("Value")
|
|
18
|
+
for key in ("package_type", "is_enterprise", "credit_balance", "currency"):
|
|
19
|
+
if key in result:
|
|
20
|
+
table.add_row(key.replace("_", " ").title(), str(result[key]))
|
|
21
|
+
return table
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def account(ctx: typer.Context, json_out: JsonOpt = False, output: OutOpt = None) -> None:
|
|
25
|
+
"""Show your plan tier (is_enterprise, package_type) and credit balance (free).
|
|
26
|
+
|
|
27
|
+
Check this before Subscription/Enterprise-only commands (headcount, traffic,
|
|
28
|
+
jobs, posts, reviews) to know whether they'll be allowed.
|
|
29
|
+
"""
|
|
30
|
+
result = fetch(ctx.obj, "/mcp/account")
|
|
31
|
+
emit(ctx.obj, result, json_out=json_out, output=output, renderer=_account_table)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def register(app: typer.Typer) -> None:
|
|
35
|
+
app.command("account")(account)
|