reportify-cli 0.1.48__tar.gz → 0.1.50__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.
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/PKG-INFO +1 -1
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/pyproject.toml +1 -1
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/auth.py +13 -33
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_auth_commands.py +4 -4
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/.gitignore +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/Makefile +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/README.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/README.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/bump_version.sh +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/publish.sh +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-agent/SKILL.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/SKILL.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/references/API_REFERENCE.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/references/COMMANDS.md +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/__init__.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/auth_config.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/client.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/__init__.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/agent.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/channels.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/concepts.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/docs.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/following.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/kb.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/macro.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/quant.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/search.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/stock.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/timeline.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/user.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/main.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/output.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/params.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/settings.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/utils.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/__init__.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/integration/test_docs_integration.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/integration/test_stock_integration.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/__init__.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_auth_config.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_docs.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_quant.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_search.py +0 -0
- {reportify_cli-0.1.48 → reportify_cli-0.1.50}/uv.lock +0 -0
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
reportify-cli auth status # 显示当前登录状态
|
|
8
8
|
|
|
9
9
|
后端契约(新 oauth_server 端点):
|
|
10
|
-
POST {base}/
|
|
11
|
-
POST {base}/
|
|
12
|
-
POST {base}/
|
|
10
|
+
POST {base}/v2/oauth/device/code (form: client_id, resource, scope?)
|
|
11
|
+
POST {base}/v2/oauth/token (form: grant_type, device_code, client_id, resource)
|
|
12
|
+
POST {base}/v2/oauth/token (form: grant_type=refresh_token, refresh_token, client_id, resource)
|
|
13
13
|
|
|
14
14
|
返回的 access_token 是 **JWT**(aud=https://api.reportify.cn),客户端把它当
|
|
15
15
|
Bearer 直接发给主 API;主 API 的 TokenMiddleware 会本地验签校验 audience。
|
|
@@ -34,15 +34,19 @@ from src.settings import get_api_base_url
|
|
|
34
34
|
|
|
35
35
|
app = typer.Typer(help="Authentication", rich_markup_mode="rich")
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
# Reportify CLI 是 first-party 客户端, 用预注册的稳定 client_id (后端配置在
|
|
38
|
+
# OAUTH_FIRSTPARTY_CLIENTS 里), 不走 DCR. 这样可以拿到 api 域的 token (DCR
|
|
39
|
+
# 注册的客户端默认只能拿 mcp 域).
|
|
40
|
+
#
|
|
41
|
+
# Public client (PKCE 防 code 截获), client_id 公开不构成安全风险.
|
|
42
|
+
CLIENT_ID = "reportify-cli"
|
|
38
43
|
RESOURCE = "https://api.reportify.cn"
|
|
39
44
|
SCOPE = "api"
|
|
40
45
|
GRANT_TYPE_DEVICE = "urn:ietf:params:oauth:grant-type:device_code"
|
|
41
46
|
GRANT_TYPE_REFRESH = "refresh_token"
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ENDPOINT_TOKEN = "/v1/oauth/token"
|
|
48
|
+
ENDPOINT_DEVICE_CODE = "/v2/oauth/device/code"
|
|
49
|
+
ENDPOINT_TOKEN = "/v2/oauth/token"
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
# ------------------------------------------------------------------ #
|
|
@@ -70,25 +74,6 @@ def _oauth_error(response: httpx.Response) -> tuple[str, str]:
|
|
|
70
74
|
return ("unknown_error", str(detail))
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
def _register_client(client: httpx.Client) -> str:
|
|
74
|
-
"""RFC 7591 Dynamic Client Registration —— 注册本地 CLI 实例, 返回 client_id。"""
|
|
75
|
-
resp = client.post(
|
|
76
|
-
ENDPOINT_REGISTER,
|
|
77
|
-
json={
|
|
78
|
-
"client_name": CLIENT_NAME,
|
|
79
|
-
"redirect_uris": [], # device flow 不需要
|
|
80
|
-
"grant_types": [GRANT_TYPE_DEVICE, GRANT_TYPE_REFRESH],
|
|
81
|
-
"response_types": [],
|
|
82
|
-
"token_endpoint_auth_method": "none",
|
|
83
|
-
"scope": SCOPE,
|
|
84
|
-
},
|
|
85
|
-
)
|
|
86
|
-
if resp.status_code != 200:
|
|
87
|
-
err, desc = _oauth_error(resp)
|
|
88
|
-
_err(f"failed to register client ({err}): {desc}")
|
|
89
|
-
return resp.json()["client_id"]
|
|
90
|
-
|
|
91
|
-
|
|
92
77
|
def _request_device_code(client: httpx.Client, client_id: str) -> dict:
|
|
93
78
|
resp = client.post(
|
|
94
79
|
ENDPOINT_DEVICE_CODE,
|
|
@@ -103,7 +88,7 @@ def _request_device_code(client: httpx.Client, client_id: str) -> dict:
|
|
|
103
88
|
def _poll_token(
|
|
104
89
|
client: httpx.Client, device_code: str, client_id: str, interval: int, expires_in: int
|
|
105
90
|
) -> dict:
|
|
106
|
-
"""轮询 /
|
|
91
|
+
"""轮询 /v2/oauth/token (grant_type=device_code),直到拿到 JWT 或超时/被拒。"""
|
|
107
92
|
deadline = time.monotonic() + expires_in
|
|
108
93
|
current_interval = max(interval, 1)
|
|
109
94
|
typer.echo("Waiting for authorization", nl=False)
|
|
@@ -199,14 +184,9 @@ def login(
|
|
|
199
184
|
automatically; expired tokens are silently refreshed.
|
|
200
185
|
"""
|
|
201
186
|
base_url = get_api_base_url().rstrip("/")
|
|
202
|
-
#
|
|
203
|
-
existing = load_credential()
|
|
204
|
-
client_id = existing.client_id if existing and existing.client_id else None
|
|
187
|
+
client_id = CLIENT_ID # first-party 预注册, 不走 DCR
|
|
205
188
|
|
|
206
189
|
with httpx.Client(base_url=base_url, timeout=30.0) as client:
|
|
207
|
-
if not client_id:
|
|
208
|
-
client_id = _register_client(client)
|
|
209
|
-
|
|
210
190
|
device_resp = _request_device_code(client, client_id)
|
|
211
191
|
|
|
212
192
|
verification_uri_complete = device_resp.get("verification_uri_complete")
|
|
@@ -81,7 +81,7 @@ def _register_payload() -> dict:
|
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
def _token_payload() -> dict:
|
|
84
|
-
"""oauth_server /
|
|
84
|
+
"""oauth_server /v2/oauth/token (grant_type=device_code) 的响应。
|
|
85
85
|
|
|
86
86
|
JWT 格式不重要(CLI 不解码),随便给个 header.payload.signature。
|
|
87
87
|
"""
|
|
@@ -166,9 +166,9 @@ class TestLoginHappyPath:
|
|
|
166
166
|
self, tmp_config, fast_sleep, patch_client
|
|
167
167
|
):
|
|
168
168
|
patch_client.post.side_effect = [
|
|
169
|
-
_ok(_register_payload()), # /
|
|
170
|
-
_ok(_device_code_payload()), # /
|
|
171
|
-
_ok(_token_payload()), # /
|
|
169
|
+
_ok(_register_payload()), # /v2/oauth/register (DCR)
|
|
170
|
+
_ok(_device_code_payload()), # /v2/oauth/device/code
|
|
171
|
+
_ok(_token_payload()), # /v2/oauth/token (approved)
|
|
172
172
|
]
|
|
173
173
|
|
|
174
174
|
result = runner.invoke(app, ["login", "--no-browser"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/references/API_REFERENCE.md
RENAMED
|
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
|