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.
Files changed (31) hide show
  1. akta_pro_cli-0.3.0/LICENSE +21 -0
  2. akta_pro_cli-0.3.0/PKG-INFO +134 -0
  3. akta_pro_cli-0.3.0/README.md +106 -0
  4. akta_pro_cli-0.3.0/pyproject.toml +62 -0
  5. akta_pro_cli-0.3.0/setup.cfg +4 -0
  6. akta_pro_cli-0.3.0/src/akta_pro_cli/__init__.py +8 -0
  7. akta_pro_cli-0.3.0/src/akta_pro_cli/__main__.py +25 -0
  8. akta_pro_cli-0.3.0/src/akta_pro_cli/app.py +94 -0
  9. akta_pro_cli-0.3.0/src/akta_pro_cli/client.py +102 -0
  10. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/__init__.py +1 -0
  11. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/account.py +35 -0
  12. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/alternative.py +109 -0
  13. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/auth.py +123 -0
  14. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/company.py +184 -0
  15. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/config.py +54 -0
  16. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/industry.py +46 -0
  17. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/news.py +232 -0
  18. akta_pro_cli-0.3.0/src/akta_pro_cli/commands/update.py +64 -0
  19. akta_pro_cli-0.3.0/src/akta_pro_cli/config.py +71 -0
  20. akta_pro_cli-0.3.0/src/akta_pro_cli/console.py +10 -0
  21. akta_pro_cli-0.3.0/src/akta_pro_cli/news_tags.py +102 -0
  22. akta_pro_cli-0.3.0/src/akta_pro_cli/options.py +21 -0
  23. akta_pro_cli-0.3.0/src/akta_pro_cli/runtime.py +157 -0
  24. akta_pro_cli-0.3.0/src/akta_pro_cli/update.py +79 -0
  25. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/PKG-INFO +134 -0
  26. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/SOURCES.txt +29 -0
  27. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/dependency_links.txt +1 -0
  28. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/entry_points.txt +2 -0
  29. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/requires.txt +8 -0
  30. akta_pro_cli-0.3.0/src/akta_pro_cli.egg-info/top_level.txt +1 -0
  31. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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)