amap-cli 0.1.3__tar.gz → 0.1.4__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.
- amap_cli-0.1.3/README.md → amap_cli-0.1.4/PKG-INFO +32 -0
- amap_cli-0.1.3/PKG-INFO → amap_cli-0.1.4/README.md +25 -7
- {amap_cli-0.1.3 → amap_cli-0.1.4}/docs/manual-verification.md +2 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/pyproject.toml +1 -1
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/api.py +29 -24
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/cli.py +8 -1
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/config.py +18 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/uv.lock +1 -1
- {amap_cli-0.1.3 → amap_cli-0.1.4}/.gitignore +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/.trae/specs/build-amap-cli/checklist.md +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/.trae/specs/build-amap-cli/spec.md +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/.trae/specs/build-amap-cli/tasks.md +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/amap-gui-reference.md +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/skills/amap-cli/SKILL.md +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/__init__.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/__main__.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/distance.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/errors.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/geocode.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/geocode_command.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/output.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/params.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/route.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/search_poi.py +0 -0
- {amap_cli-0.1.3 → amap_cli-0.1.4}/src/amap_cli/skill.py +0 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: amap-cli
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Pure command-line CLI for Amap APIs.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
1
8
|
# amap-cli
|
|
2
9
|
|
|
3
10
|
`amap-cli` 是一个基于高德开放平台 Web API 的纯命令行工具,面向终端环境和 AI Agent 使用。
|
|
@@ -29,6 +36,13 @@ uvx amap-cli --help
|
|
|
29
36
|
uvx amap-cli config set --api-key <YOUR_AMAP_KEY>
|
|
30
37
|
```
|
|
31
38
|
|
|
39
|
+
高德免费账号默认限流较严,工具默认会在每次 API 调用结束后 sleep `0.34` 秒;如需调整或关闭,可配置:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uvx amap-cli config set --request-sleep-seconds 0.34
|
|
43
|
+
uvx amap-cli config set --request-sleep-seconds 0
|
|
44
|
+
```
|
|
45
|
+
|
|
32
46
|
地理编码示例:
|
|
33
47
|
|
|
34
48
|
```bash
|
|
@@ -123,6 +137,18 @@ amap-cli install-skill --dir ~/.agents/skills --force
|
|
|
123
137
|
amap-cli config set --api-key <YOUR_AMAP_KEY>
|
|
124
138
|
```
|
|
125
139
|
|
|
140
|
+
设置每次高德 API 调用结束后的 sleep 时间:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
amap-cli config set --request-sleep-seconds 0.34
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
关闭该 sleep:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
amap-cli config set --request-sleep-seconds 0
|
|
150
|
+
```
|
|
151
|
+
|
|
126
152
|
查看当前配置:
|
|
127
153
|
|
|
128
154
|
```bash
|
|
@@ -135,6 +161,12 @@ amap-cli config show
|
|
|
135
161
|
~/Library/Application Support/amap-cli/config.json
|
|
136
162
|
```
|
|
137
163
|
|
|
164
|
+
说明:
|
|
165
|
+
|
|
166
|
+
- `request_sleep_seconds` 默认为 `0.34`
|
|
167
|
+
- 该配置会在每次真实的高德 API 调用结束后执行一次 sleep
|
|
168
|
+
- 设置为 `0` 表示关闭节流等待
|
|
169
|
+
|
|
138
170
|
### 安装 Skill
|
|
139
171
|
|
|
140
172
|
基础格式:
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: amap-cli
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: Pure command-line CLI for Amap APIs.
|
|
5
|
-
Requires-Python: >=3.11
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
|
|
8
1
|
# amap-cli
|
|
9
2
|
|
|
10
3
|
`amap-cli` 是一个基于高德开放平台 Web API 的纯命令行工具,面向终端环境和 AI Agent 使用。
|
|
@@ -36,6 +29,13 @@ uvx amap-cli --help
|
|
|
36
29
|
uvx amap-cli config set --api-key <YOUR_AMAP_KEY>
|
|
37
30
|
```
|
|
38
31
|
|
|
32
|
+
高德免费账号默认限流较严,工具默认会在每次 API 调用结束后 sleep `0.34` 秒;如需调整或关闭,可配置:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvx amap-cli config set --request-sleep-seconds 0.34
|
|
36
|
+
uvx amap-cli config set --request-sleep-seconds 0
|
|
37
|
+
```
|
|
38
|
+
|
|
39
39
|
地理编码示例:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
@@ -130,6 +130,18 @@ amap-cli install-skill --dir ~/.agents/skills --force
|
|
|
130
130
|
amap-cli config set --api-key <YOUR_AMAP_KEY>
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
设置每次高德 API 调用结束后的 sleep 时间:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
amap-cli config set --request-sleep-seconds 0.34
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
关闭该 sleep:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
amap-cli config set --request-sleep-seconds 0
|
|
143
|
+
```
|
|
144
|
+
|
|
133
145
|
查看当前配置:
|
|
134
146
|
|
|
135
147
|
```bash
|
|
@@ -142,6 +154,12 @@ amap-cli config show
|
|
|
142
154
|
~/Library/Application Support/amap-cli/config.json
|
|
143
155
|
```
|
|
144
156
|
|
|
157
|
+
说明:
|
|
158
|
+
|
|
159
|
+
- `request_sleep_seconds` 默认为 `0.34`
|
|
160
|
+
- 该配置会在每次真实的高德 API 调用结束后执行一次 sleep
|
|
161
|
+
- 设置为 `0` 表示关闭节流等待
|
|
162
|
+
|
|
145
163
|
### 安装 Skill
|
|
146
164
|
|
|
147
165
|
基础格式:
|
|
@@ -74,6 +74,7 @@ uv run amap-cli distance --from 116.397,39.909 --to 116.407,39.904
|
|
|
74
74
|
```bash
|
|
75
75
|
TMP_HOME=$(mktemp -d)
|
|
76
76
|
HOME="$TMP_HOME" uv run amap-cli config set --api-key test-demo-key --timeout-seconds 12
|
|
77
|
+
HOME="$TMP_HOME" uv run amap-cli config set --request-sleep-seconds 0
|
|
77
78
|
HOME="$TMP_HOME" uv run amap-cli config show
|
|
78
79
|
```
|
|
79
80
|
|
|
@@ -83,6 +84,7 @@ HOME="$TMP_HOME" uv run amap-cli config show
|
|
|
83
84
|
- macOS 下配置落在 `~/Library/Application Support/amap-cli/config.json`
|
|
84
85
|
- 再次执行 `config show` 时可自动读取刚写入的配置
|
|
85
86
|
- `api_key` 输出已脱敏
|
|
87
|
+
- `request_sleep_seconds` 可写入为 `0`,用于关闭默认的请求后 sleep
|
|
86
88
|
|
|
87
89
|
### 5. `route` 典型输入通过 CLI 主入口走通到 handler
|
|
88
90
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import time
|
|
6
7
|
from typing import Any
|
|
7
8
|
from urllib.error import HTTPError, URLError
|
|
8
9
|
from urllib.parse import urlencode
|
|
@@ -39,32 +40,36 @@ class AmapApiClient:
|
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
try:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
try:
|
|
44
|
+
with urlopen(request, timeout=self.config.timeout_seconds) as response:
|
|
45
|
+
body = response.read().decode("utf-8")
|
|
46
|
+
except HTTPError as exc:
|
|
47
|
+
response_body = exc.read().decode("utf-8", errors="ignore")
|
|
48
|
+
raise ApiRequestError(
|
|
49
|
+
f"高德 API HTTP 请求失败:{exc.code}",
|
|
50
|
+
details={"status": exc.code, "body": response_body},
|
|
51
|
+
) from exc
|
|
52
|
+
except URLError as exc:
|
|
53
|
+
raise ApiRequestError(
|
|
54
|
+
"无法连接高德 API。",
|
|
55
|
+
details={"reason": str(exc.reason)},
|
|
56
|
+
) from exc
|
|
57
|
+
except TimeoutError as exc:
|
|
58
|
+
raise ApiRequestError("请求高德 API 超时。") from exc
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
try:
|
|
61
|
+
payload = json.loads(body)
|
|
62
|
+
except json.JSONDecodeError as exc:
|
|
63
|
+
raise ApiRequestError(
|
|
64
|
+
"高德 API 返回了非 JSON 响应。",
|
|
65
|
+
details={"body": body[:500]},
|
|
66
|
+
) from exc
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
self._raise_for_amap_error(payload)
|
|
69
|
+
return payload
|
|
70
|
+
finally:
|
|
71
|
+
if self.config.request_sleep_seconds > 0:
|
|
72
|
+
time.sleep(self.config.request_sleep_seconds)
|
|
68
73
|
|
|
69
74
|
def _build_url(self, path: str, params: dict[str, Any]) -> str:
|
|
70
75
|
"""Build a full request URL from config and query params."""
|
|
@@ -56,6 +56,11 @@ def _register_config_command(
|
|
|
56
56
|
type=float,
|
|
57
57
|
help="请求超时时间(秒)",
|
|
58
58
|
)
|
|
59
|
+
set_parser.add_argument(
|
|
60
|
+
"--request-sleep-seconds",
|
|
61
|
+
type=float,
|
|
62
|
+
help="每次高德 API 调用结束后的 sleep 时间(秒,0 表示关闭)",
|
|
63
|
+
)
|
|
59
64
|
set_parser.set_defaults(handler=_handle_config_set)
|
|
60
65
|
|
|
61
66
|
show_parser = config_subparsers.add_parser("show", help="查看当前配置(敏感信息脱敏)")
|
|
@@ -73,15 +78,17 @@ def _handle_config_set(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
73
78
|
args.api_key is None
|
|
74
79
|
and args.base_url is None
|
|
75
80
|
and args.timeout_seconds is None
|
|
81
|
+
and args.request_sleep_seconds is None
|
|
76
82
|
):
|
|
77
83
|
raise ValidationError(
|
|
78
|
-
"请至少提供一个配置项,例如 `--api-key`、`--base-url` 或 `--
|
|
84
|
+
"请至少提供一个配置项,例如 `--api-key`、`--base-url`、`--timeout-seconds` 或 `--request-sleep-seconds`。"
|
|
79
85
|
)
|
|
80
86
|
|
|
81
87
|
config = save_config(
|
|
82
88
|
api_key=args.api_key,
|
|
83
89
|
base_url=args.base_url,
|
|
84
90
|
timeout_seconds=args.timeout_seconds,
|
|
91
|
+
request_sleep_seconds=args.request_sleep_seconds,
|
|
85
92
|
)
|
|
86
93
|
return {
|
|
87
94
|
"message": "配置已保存。",
|
|
@@ -15,6 +15,7 @@ APP_NAME = "amap-cli"
|
|
|
15
15
|
CONFIG_FILE_NAME = "config.json"
|
|
16
16
|
DEFAULT_BASE_URL = "https://restapi.amap.com"
|
|
17
17
|
DEFAULT_TIMEOUT_SECONDS = 10.0
|
|
18
|
+
DEFAULT_REQUEST_SLEEP_SECONDS = 0.34
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@dataclass(slots=True)
|
|
@@ -24,6 +25,7 @@ class AmapConfig:
|
|
|
24
25
|
api_key: str | None = None
|
|
25
26
|
base_url: str = DEFAULT_BASE_URL
|
|
26
27
|
timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS
|
|
28
|
+
request_sleep_seconds: float = DEFAULT_REQUEST_SLEEP_SECONDS
|
|
27
29
|
|
|
28
30
|
@classmethod
|
|
29
31
|
def from_dict(cls, raw: dict[str, Any]) -> "AmapConfig":
|
|
@@ -31,6 +33,10 @@ class AmapConfig:
|
|
|
31
33
|
api_key = raw.get("api_key")
|
|
32
34
|
base_url = raw.get("base_url", DEFAULT_BASE_URL)
|
|
33
35
|
timeout_seconds = raw.get("timeout_seconds", DEFAULT_TIMEOUT_SECONDS)
|
|
36
|
+
request_sleep_seconds = raw.get(
|
|
37
|
+
"request_sleep_seconds",
|
|
38
|
+
DEFAULT_REQUEST_SLEEP_SECONDS,
|
|
39
|
+
)
|
|
34
40
|
|
|
35
41
|
if api_key is not None and not isinstance(api_key, str):
|
|
36
42
|
raise ConfigError("配置文件中的 api_key 格式无效。")
|
|
@@ -38,6 +44,11 @@ class AmapConfig:
|
|
|
38
44
|
raise ConfigError("配置文件中的 base_url 格式无效。")
|
|
39
45
|
if not isinstance(timeout_seconds, (int, float)) or timeout_seconds <= 0:
|
|
40
46
|
raise ConfigError("配置文件中的 timeout_seconds 格式无效。")
|
|
47
|
+
if (
|
|
48
|
+
not isinstance(request_sleep_seconds, (int, float))
|
|
49
|
+
or request_sleep_seconds < 0
|
|
50
|
+
):
|
|
51
|
+
raise ConfigError("配置文件中的 request_sleep_seconds 格式无效。")
|
|
41
52
|
|
|
42
53
|
normalized_api_key = None
|
|
43
54
|
if api_key is not None:
|
|
@@ -47,6 +58,7 @@ class AmapConfig:
|
|
|
47
58
|
api_key=normalized_api_key,
|
|
48
59
|
base_url=base_url.strip(),
|
|
49
60
|
timeout_seconds=float(timeout_seconds),
|
|
61
|
+
request_sleep_seconds=float(request_sleep_seconds),
|
|
50
62
|
)
|
|
51
63
|
|
|
52
64
|
def to_dict(self, *, mask_secrets: bool = False) -> dict[str, Any]:
|
|
@@ -149,6 +161,7 @@ def save_config(
|
|
|
149
161
|
api_key: str | None = None,
|
|
150
162
|
base_url: str | None = None,
|
|
151
163
|
timeout_seconds: float | None = None,
|
|
164
|
+
request_sleep_seconds: float | None = None,
|
|
152
165
|
) -> AmapConfig:
|
|
153
166
|
"""Persist config updates to the OS-specific config path."""
|
|
154
167
|
config = load_config(required=False) or AmapConfig()
|
|
@@ -170,6 +183,11 @@ def save_config(
|
|
|
170
183
|
raise ValidationError("`--timeout-seconds` 必须大于 0。")
|
|
171
184
|
config.timeout_seconds = float(timeout_seconds)
|
|
172
185
|
|
|
186
|
+
if request_sleep_seconds is not None:
|
|
187
|
+
if request_sleep_seconds < 0:
|
|
188
|
+
raise ValidationError("`--request-sleep-seconds` 不能小于 0。")
|
|
189
|
+
config.request_sleep_seconds = float(request_sleep_seconds)
|
|
190
|
+
|
|
173
191
|
path = get_config_path()
|
|
174
192
|
try:
|
|
175
193
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
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
|