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.
Files changed (44) hide show
  1. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/PKG-INFO +1 -1
  2. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/pyproject.toml +1 -1
  3. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/auth.py +13 -33
  4. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_auth_commands.py +4 -4
  5. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/.gitignore +0 -0
  6. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/Makefile +0 -0
  7. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/README.md +0 -0
  8. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/README.md +0 -0
  9. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/bump_version.sh +0 -0
  10. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/scripts/publish.sh +0 -0
  11. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-agent/SKILL.md +0 -0
  12. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/SKILL.md +0 -0
  13. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/references/API_REFERENCE.md +0 -0
  14. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/skills/reportify-ai/references/COMMANDS.md +0 -0
  15. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/__init__.py +0 -0
  16. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/auth_config.py +0 -0
  17. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/client.py +0 -0
  18. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/__init__.py +0 -0
  19. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/agent.py +0 -0
  20. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/channels.py +0 -0
  21. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/concepts.py +0 -0
  22. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/docs.py +0 -0
  23. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/following.py +0 -0
  24. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/kb.py +0 -0
  25. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/macro.py +0 -0
  26. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/quant.py +0 -0
  27. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/search.py +0 -0
  28. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/stock.py +0 -0
  29. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/timeline.py +0 -0
  30. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/commands/user.py +0 -0
  31. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/main.py +0 -0
  32. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/output.py +0 -0
  33. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/params.py +0 -0
  34. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/settings.py +0 -0
  35. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/src/utils.py +0 -0
  36. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/__init__.py +0 -0
  37. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/integration/test_docs_integration.py +0 -0
  38. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/integration/test_stock_integration.py +0 -0
  39. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/__init__.py +0 -0
  40. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_auth_config.py +0 -0
  41. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_docs.py +0 -0
  42. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_quant.py +0 -0
  43. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/tests/test_commands/test_search.py +0 -0
  44. {reportify_cli-0.1.48 → reportify_cli-0.1.50}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reportify-cli
3
- Version: 0.1.48
3
+ Version: 0.1.50
4
4
  Summary: CLI wrapper for Reportify SDK - Access Reportify API through command line
5
5
  Project-URL: Homepage, https://reportify.ai
6
6
  Project-URL: Documentation, https://docs.reportify.ai
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reportify-cli"
3
- version = "0.1.48"
3
+ version = "0.1.50"
4
4
  description = "CLI wrapper for Reportify SDK - Access Reportify API through command line"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -7,9 +7,9 @@
7
7
  reportify-cli auth status # 显示当前登录状态
8
8
 
9
9
  后端契约(新 oauth_server 端点):
10
- POST {base}/v1/oauth/device/code (form: client_id, resource, scope?)
11
- POST {base}/v1/oauth/token (form: grant_type, device_code, client_id, resource)
12
- POST {base}/v1/oauth/token (form: grant_type=refresh_token, refresh_token, client_id, resource)
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
- CLIENT_NAME = "Reportify CLI"
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
- ENDPOINT_REGISTER = "/v1/oauth/register"
44
- ENDPOINT_DEVICE_CODE = "/v1/oauth/device/code"
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
- """轮询 /v1/oauth/token (grant_type=device_code),直到拿到 JWT 或超时/被拒。"""
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
- # 复用已注册的 client_id(30 TTL);不存在则发起 DCR
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 /v1/oauth/token (grant_type=device_code) 的响应。
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()), # /v1/oauth/register (DCR)
170
- _ok(_device_code_payload()), # /v1/oauth/device/code
171
- _ok(_token_payload()), # /v1/oauth/token (approved)
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