drinkzen-admin-cli 0.3.0__tar.gz → 0.3.3__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 (19) hide show
  1. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/PKG-INFO +4 -1
  2. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/README.md +3 -0
  3. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/cli.py +18 -0
  4. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/skill.py +4 -2
  5. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin_cli.egg-info/PKG-INFO +4 -1
  6. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin_cli.egg-info/SOURCES.txt +1 -0
  7. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/pyproject.toml +1 -1
  8. drinkzen_admin_cli-0.3.3/tests/test_skill.py +26 -0
  9. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/tests/test_standalone_cli.py +28 -0
  10. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/__init__.py +0 -0
  11. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/client.py +0 -0
  12. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/config.py +0 -0
  13. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/formatting.py +0 -0
  14. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin/menu_options.py +0 -0
  15. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin_cli.egg-info/dependency_links.txt +0 -0
  16. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin_cli.egg-info/entry_points.txt +0 -0
  17. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/drinkzen_admin_cli.egg-info/top_level.txt +0 -0
  18. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/setup.cfg +0 -0
  19. {drinkzen_admin_cli-0.3.0 → drinkzen_admin_cli-0.3.3}/tests/test_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drinkzen-admin-cli
3
- Version: 0.3.0
3
+ Version: 0.3.3
4
4
  Summary: Standalone CLI client for DrinkZen Admin operations
5
5
  Author: DrinkZen Team
6
6
  License: MIT
@@ -156,6 +156,9 @@ drinkzen-admin brand update 霸王茶姬 --name "霸王茶姬 CHAGEE" --alias "C
156
156
 
157
157
  # 上传品牌 Logo 并标注数据置信度
158
158
  drinkzen-admin brand set-logo 霸王茶姬 ./logo.png --confidence verified --source-name "官方小程序"
159
+
160
+ # 只读核对数据库 Logo 引用与 COS 文件,识别缺失引用与孤儿对象
161
+ drinkzen-admin brand audit-logos --json
159
162
  ```
160
163
 
161
164
  ### 2. 菜单模板管理 (`menu-template`)
@@ -134,6 +134,9 @@ drinkzen-admin brand update 霸王茶姬 --name "霸王茶姬 CHAGEE" --alias "C
134
134
 
135
135
  # 上传品牌 Logo 并标注数据置信度
136
136
  drinkzen-admin brand set-logo 霸王茶姬 ./logo.png --confidence verified --source-name "官方小程序"
137
+
138
+ # 只读核对数据库 Logo 引用与 COS 文件,识别缺失引用与孤儿对象
139
+ drinkzen-admin brand audit-logos --json
137
140
  ```
138
141
 
139
142
  ### 2. 菜单模板管理 (`menu-template`)
@@ -63,6 +63,16 @@ def cmd_brand_show(
63
63
  return 0
64
64
 
65
65
 
66
+ def cmd_brand_audit_logos(client: AdminCLIClient, is_json: bool = False) -> int:
67
+ """Print the API's read-only COS/database logo consistency audit."""
68
+ status, data = client.request("GET", "/api/admin/brands/logo-audit")
69
+ if status != 200:
70
+ print(format_cli_output({"error": data}, is_json))
71
+ return 1
72
+ print(format_cli_output(data, is_json))
73
+ return 0
74
+
75
+
66
76
  def cmd_brand_update(
67
77
  client: AdminCLIClient,
68
78
  brand_id_or_name: str,
@@ -429,6 +439,12 @@ def build_parser() -> argparse.ArgumentParser:
429
439
  b_show = brand_sub.add_parser("show", help="Show brand details", parents=[common_parser])
430
440
  b_show.add_argument("brand", help="Brand ID or name")
431
441
 
442
+ brand_sub.add_parser(
443
+ "audit-logos",
444
+ help="Read-only audit of brand Logo references against object storage",
445
+ parents=[common_parser],
446
+ )
447
+
432
448
  b_update = brand_sub.add_parser("update", help="Update brand information", parents=[common_parser])
433
449
  b_update.add_argument("brand", help="Brand ID or name")
434
450
  b_update.add_argument("--name", default=None, help="New brand name")
@@ -515,6 +531,8 @@ def main(args: list[str] | None = None) -> int:
515
531
  return cmd_brand_list(client, search=parsed.search, active_only=parsed.active_only, is_json=is_json)
516
532
  if parsed.brand_action == "show":
517
533
  return cmd_brand_show(client, brand_id_or_name=parsed.brand, is_json=is_json)
534
+ if parsed.brand_action == "audit-logos":
535
+ return cmd_brand_audit_logos(client, is_json=is_json)
518
536
  if parsed.brand_action == "update":
519
537
  return cmd_brand_update(
520
538
  client,
@@ -6,7 +6,8 @@ import urllib.request
6
6
  from pathlib import Path
7
7
 
8
8
  SKILL_NAME = "drinkzen-admin"
9
- SKILL_URL = "https://raw.githubusercontent.com/xiaolinstar/drinkzen/main/.agents/skills/drinkzen-admin/SKILL.md"
9
+ SKILL_URL = "https://drinkzen.cn/skills/drinkzen-admin/SKILL.md"
10
+ SKILL_USER_AGENT = "DrinkZenAdminCLI/1.0 (+https://drinkzen.cn)"
10
11
 
11
12
 
12
13
  def skill_path(scope: str = "project", project_dir: str | None = None) -> Path:
@@ -27,7 +28,8 @@ def install_skill(
27
28
  if destination.exists() and not force:
28
29
  raise FileExistsError(f"Skill already exists: {destination}. Use --force to replace it.")
29
30
  destination.parent.mkdir(parents=True, exist_ok=True)
30
- with urllib.request.urlopen(url, timeout=20) as response:
31
+ request = urllib.request.Request(url, headers={"User-Agent": SKILL_USER_AGENT})
32
+ with urllib.request.urlopen(request, timeout=20) as response:
31
33
  content = response.read()
32
34
  if not content:
33
35
  raise RuntimeError("Downloaded Skill is empty")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drinkzen-admin-cli
3
- Version: 0.3.0
3
+ Version: 0.3.3
4
4
  Summary: Standalone CLI client for DrinkZen Admin operations
5
5
  Author: DrinkZen Team
6
6
  License: MIT
@@ -156,6 +156,9 @@ drinkzen-admin brand update 霸王茶姬 --name "霸王茶姬 CHAGEE" --alias "C
156
156
 
157
157
  # 上传品牌 Logo 并标注数据置信度
158
158
  drinkzen-admin brand set-logo 霸王茶姬 ./logo.png --confidence verified --source-name "官方小程序"
159
+
160
+ # 只读核对数据库 Logo 引用与 COS 文件,识别缺失引用与孤儿对象
161
+ drinkzen-admin brand audit-logos --json
159
162
  ```
160
163
 
161
164
  ### 2. 菜单模板管理 (`menu-template`)
@@ -13,4 +13,5 @@ drinkzen_admin_cli.egg-info/dependency_links.txt
13
13
  drinkzen_admin_cli.egg-info/entry_points.txt
14
14
  drinkzen_admin_cli.egg-info/top_level.txt
15
15
  tests/test_config.py
16
+ tests/test_skill.py
16
17
  tests/test_standalone_cli.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "drinkzen-admin-cli"
7
- version = "0.3.0"
7
+ version = "0.3.3"
8
8
  description = "Standalone CLI client for DrinkZen Admin operations"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,26 @@
1
+ from pathlib import Path
2
+ from tempfile import TemporaryDirectory
3
+ from unittest.mock import patch
4
+
5
+ from drinkzen_admin.skill import SKILL_USER_AGENT, install_skill
6
+
7
+
8
+ class SkillResponse:
9
+ def __enter__(self) -> "SkillResponse":
10
+ return self
11
+
12
+ def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> None:
13
+ return None
14
+
15
+ def read(self) -> bytes:
16
+ return b"---\nname: drinkzen-admin\n---\n"
17
+
18
+
19
+ def test_install_skill_identifies_the_cli_request() -> None:
20
+ with TemporaryDirectory() as temp_dir:
21
+ with patch("urllib.request.urlopen", return_value=SkillResponse()) as urlopen:
22
+ destination = install_skill(project_dir=temp_dir)
23
+
24
+ request = urlopen.call_args.args[0]
25
+ assert request.get_header("User-agent") == SKILL_USER_AGENT
26
+ assert Path(destination).read_text(encoding="utf-8").startswith("---")
@@ -71,6 +71,34 @@ class TestStandaloneCLI(unittest.TestCase):
71
71
  self.assertTrue(output.get("dry_run"))
72
72
  self.assertEqual(output["proposed"]["name"], "新名称")
73
73
 
74
+ @patch("drinkzen_admin.cli.AdminCLIClient")
75
+ def test_brand_audit_logos_command(self, mock_client_cls):
76
+ mock_client = MagicMock()
77
+ mock_client.request.return_value = (
78
+ 200,
79
+ {
80
+ "backend": "CosObjectStorage",
81
+ "total_brands": 14,
82
+ "referenced_logo_count": 9,
83
+ "missing_referenced_objects": [],
84
+ "recoverable_unlinked_objects": [],
85
+ "orphan_object_keys": ["brands/old/logo.png"],
86
+ },
87
+ )
88
+ mock_client_cls.return_value = mock_client
89
+
90
+ buf = io.StringIO()
91
+ with patch("sys.argv", ["drinkzen-admin", "brand", "audit-logos", "--json"]):
92
+ with redirect_stdout(buf):
93
+ exit_code = main()
94
+
95
+ self.assertEqual(exit_code, 0)
96
+ output = json.loads(buf.getvalue())
97
+ self.assertEqual(output["referenced_logo_count"], 9)
98
+ mock_client.request.assert_called_once_with(
99
+ "GET", "/api/admin/brands/logo-audit"
100
+ )
101
+
74
102
  def test_menu_template_validate_command(self):
75
103
  valid_template = json.dumps({
76
104
  "optionGroups": [