artdam-cli 0.2.0__tar.gz → 0.2.2__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 (33) hide show
  1. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/PKG-INFO +2 -2
  2. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/README.md +1 -1
  3. artdam_cli-0.2.2/artdam_cli/__init__.py +1 -0
  4. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/folder_cmd.py +3 -4
  5. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/folders.py +3 -4
  6. artdam_cli-0.2.2/artdam_cli/commands/login.py +112 -0
  7. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/search.py +3 -4
  8. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/tag_cmd.py +3 -4
  9. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/tags.py +3 -4
  10. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/upload.py +3 -4
  11. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/config.py +0 -9
  12. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/main.py +0 -2
  13. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/PKG-INFO +2 -2
  14. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/SOURCES.txt +0 -1
  15. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/pyproject.toml +1 -1
  16. artdam_cli-0.2.0/artdam_cli/__init__.py +0 -1
  17. artdam_cli-0.2.0/artdam_cli/commands/login.py +0 -31
  18. artdam_cli-0.2.0/artdam_cli/commands/project.py +0 -28
  19. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/client.py +0 -0
  20. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/__init__.py +0 -0
  21. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/comment.py +0 -0
  22. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/delete_cmd.py +0 -0
  23. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/download.py +0 -0
  24. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/favorite.py +0 -0
  25. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/get.py +0 -0
  26. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/rate.py +0 -0
  27. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/restore.py +0 -0
  28. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli/commands/update.py +0 -0
  29. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/dependency_links.txt +0 -0
  30. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/entry_points.txt +0 -0
  31. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/requires.txt +0 -0
  32. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/artdam_cli.egg-info/top_level.txt +0 -0
  33. {artdam_cli-0.2.0 → artdam_cli-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: artdam-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: ArtDAM 数字资产管理平台命令行工具
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -229,7 +229,7 @@ artdam update <asset_id> --description "AI 生成的场景描述"
229
229
 
230
230
  ```json
231
231
  {
232
- "base_url": "https://artdam.dsworks.cn",
232
+ "base_url": "https://artdam.xindong.com",
233
233
  "token": "eyJ..."
234
234
  }
235
235
  ```
@@ -219,7 +219,7 @@ artdam update <asset_id> --description "AI 生成的场景描述"
219
219
 
220
220
  ```json
221
221
  {
222
- "base_url": "https://artdam.dsworks.cn",
222
+ "base_url": "https://artdam.xindong.com",
223
223
  "token": "eyJ..."
224
224
  }
225
225
  ```
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -1,6 +1,6 @@
1
1
  import click
2
2
 
3
- from artdam_cli import client, config
3
+ from artdam_cli import client
4
4
 
5
5
 
6
6
  @click.group(name="folder")
@@ -10,11 +10,10 @@ def folder_group() -> None:
10
10
 
11
11
  @folder_group.command(name="create")
12
12
  @click.argument("name")
13
- @click.option("--project", "-p", type=int, default=None, help="项目 ID(省略则使用默认项目)")
13
+ @click.option("--project", "-p", type=int, required=True, help="项目 ID")
14
14
  @click.option("--parent", type=int, default=None, help="父文件夹 ID(不填则创建在根目录)")
15
- def folder_create(name: str, project: int | None, parent: int | None) -> None:
15
+ def folder_create(name: str, project: int, parent: int | None) -> None:
16
16
  """创建文件夹。"""
17
- project = config.require_project(project)
18
17
  body: dict = {"name": name, "project_id": project}
19
18
  if parent is not None:
20
19
  body["parent_id"] = parent
@@ -4,7 +4,7 @@ import click
4
4
  from rich.console import Console
5
5
  from rich.tree import Tree
6
6
 
7
- from artdam_cli import client, config
7
+ from artdam_cli import client
8
8
 
9
9
  console = Console()
10
10
 
@@ -17,11 +17,10 @@ def _build_tree(node: Tree, items: list[dict], parent_id: int | None) -> None:
17
17
 
18
18
 
19
19
  @click.command()
20
- @click.option("--project", "-p", default=None, type=int, help="项目 ID(省略则使用默认项目)")
20
+ @click.option("--project", "-p", required=True, type=int, help="项目 ID")
21
21
  @click.option("--json", "as_json", is_flag=True, help="输出原始 JSON")
22
- def folders(project: int | None, as_json: bool) -> None:
22
+ def folders(project: int, as_json: bool) -> None:
23
23
  """查看文件夹结构。"""
24
- project = config.require_project(project)
25
24
  data = client.get("/api/collections/", params={"project_id": project})
26
25
  items = data if isinstance(data, list) else data.get("items", [])
27
26
 
@@ -0,0 +1,112 @@
1
+ import threading
2
+ import webbrowser
3
+ from http.server import BaseHTTPRequestHandler, HTTPServer
4
+ from urllib.parse import parse_qs, urlparse
5
+
6
+ import click
7
+ import httpx
8
+
9
+ from artdam_cli import config
10
+
11
+ _SUCCESS_HTML = """<!DOCTYPE html>
12
+ <html><head><meta charset="utf-8"><title>ArtDAM CLI</title>
13
+ <style>body{font-family:sans-serif;text-align:center;padding:80px;background:#0b1117;color:#edf2f7}
14
+ h2{color:#7eb893}p{color:#b7c2ce}</style></head>
15
+ <body><h2>&#x2705; SSO 登录成功</h2>
16
+ <p>可以关闭此页面,返回终端继续操作。</p>
17
+ </body></html>""".encode()
18
+
19
+
20
+ def _sso_login(url: str) -> None:
21
+ """Browser Callback Flow:启动本地 HTTP server,打开浏览器走完整 CAS SSO 流程。"""
22
+ token_holder: dict = {"value": None}
23
+ done = threading.Event()
24
+
25
+ class _Handler(BaseHTTPRequestHandler):
26
+ def do_GET(self):
27
+ params = parse_qs(urlparse(self.path).query)
28
+ token = params.get("token", [None])[0]
29
+ if token:
30
+ token_holder["value"] = token
31
+ self.send_response(200)
32
+ self.send_header("Content-Type", "text/html; charset=utf-8")
33
+ self.end_headers()
34
+ self.wfile.write(_SUCCESS_HTML)
35
+ done.set()
36
+ else:
37
+ self.send_response(400)
38
+ self.end_headers()
39
+
40
+ def log_message(self, *_): # 抑制默认 server 日志
41
+ pass
42
+
43
+ # 绑定 port=0 让 OS 分配可用端口,避免 find-then-bind 竞争
44
+ server = HTTPServer(("127.0.0.1", 0), _Handler)
45
+ port = server.server_address[1]
46
+
47
+ t = threading.Thread(target=server.serve_forever, daemon=True)
48
+ t.start()
49
+
50
+ login_url = f"{url}/api/auth/sso/login?cli_port={port}"
51
+ click.echo("正在打开浏览器进行 SSO 登录...")
52
+ if not webbrowser.open(login_url):
53
+ click.echo(f"无法自动打开浏览器,请手动访问:\n {login_url}")
54
+
55
+ click.echo("等待 SSO 授权(120 秒超时)...")
56
+ if not done.wait(timeout=120):
57
+ server.shutdown()
58
+ raise SystemExit("登录超时(120 秒),请重试")
59
+
60
+ server.shutdown()
61
+ config.save({"base_url": url, "token": token_holder["value"]})
62
+ click.echo("✓ SSO 登录成功,配置已保存到 ~/.artdam/config.json")
63
+
64
+
65
+ def _password_login(url: str, username: str | None, passwd: str | None) -> None:
66
+ """账号密码登录(仅 admin / 服务账号使用)。"""
67
+ if not username:
68
+ username = click.prompt("用户名")
69
+ if not passwd:
70
+ passwd = click.prompt("密码", hide_input=True)
71
+ try:
72
+ r = httpx.post(
73
+ f"{url}/api/auth/login",
74
+ json={"username": username, "password": passwd},
75
+ timeout=10,
76
+ )
77
+ except httpx.ConnectError:
78
+ raise SystemExit(f"无法连接到 {url},请检查地址")
79
+ if r.status_code != 200:
80
+ raise SystemExit(f"登录失败: {r.json().get('detail', r.text)}")
81
+ token = r.json().get("access_token")
82
+ if not token:
83
+ raise SystemExit("登录响应中未找到 token")
84
+ config.save({"base_url": url, "token": token})
85
+ click.echo("✓ 登录成功,配置已保存到 ~/.artdam/config.json")
86
+
87
+
88
+ @click.command()
89
+ @click.option(
90
+ "--url",
91
+ default="https://artdam.xindong.com",
92
+ help="服务器地址,默认: https://artdam.xindong.com",
93
+ )
94
+ @click.option(
95
+ "--password",
96
+ "use_password",
97
+ is_flag=True,
98
+ help="使用账号密码登录(仅 admin/服务账号,默认走 SSO)",
99
+ )
100
+ @click.option("--username", default=None, help="用户名(仅 --password 模式)")
101
+ @click.option(
102
+ "--passwd", default=None, hide_input=True, help="密码(仅 --password 模式)"
103
+ )
104
+ def login(
105
+ url: str, use_password: bool, username: str | None, passwd: str | None
106
+ ) -> None:
107
+ """登录 ArtDAM。默认走 SSO 浏览器授权,加 --password 使用账号密码(仅管理员)。"""
108
+ url = url.rstrip("/")
109
+ if use_password:
110
+ _password_login(url, username, passwd)
111
+ else:
112
+ _sso_login(url)
@@ -4,21 +4,20 @@ import click
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
6
 
7
- from artdam_cli import client, config
7
+ from artdam_cli import client
8
8
 
9
9
  console = Console()
10
10
 
11
11
 
12
12
  @click.command()
13
13
  @click.argument("keyword")
14
- @click.option("--project", "-p", default=None, type=int, help="项目 ID(省略则使用默认项目)")
14
+ @click.option("--project", "-p", required=True, type=int, help="项目 ID")
15
15
  @click.option("--limit", "-n", default=20, show_default=True, help="返回数量上限")
16
16
  @click.option("--type", "mime", default=None, help="文件类型,例: image/png")
17
17
  @click.option("--folder", default=None, type=int, help="限定文件夹 ID")
18
18
  @click.option("--json", "as_json", is_flag=True, help="输出原始 JSON(AI Agent 用)")
19
- def search(keyword: str, project: int | None, limit: int, mime: str | None, folder: int | None, as_json: bool) -> None:
19
+ def search(keyword: str, project: int, limit: int, mime: str | None, folder: int | None, as_json: bool) -> None:
20
20
  """搜索资产。"""
21
- project = config.require_project(project)
22
21
  params: dict = {
23
22
  "project_id": project,
24
23
  "q": keyword,
@@ -1,6 +1,6 @@
1
1
  import click
2
2
 
3
- from artdam_cli import client, config
3
+ from artdam_cli import client
4
4
 
5
5
 
6
6
  @click.group(name="tag")
@@ -28,9 +28,8 @@ def tag_remove(asset_id: int, tag_id: int) -> None:
28
28
 
29
29
  @tag_group.command(name="create")
30
30
  @click.argument("name")
31
- @click.option("--project", "-p", type=int, default=None, help="项目 ID(省略则使用默认项目)")
32
- def tag_create(name: str, project: int | None) -> None:
31
+ @click.option("--project", "-p", type=int, required=True, help="项目 ID")
32
+ def tag_create(name: str, project: int) -> None:
33
33
  """创建新标签。"""
34
- project = config.require_project(project)
35
34
  data = client.post("/api/tags/", {"name": name, "project_id": project})
36
35
  click.echo(f"✓ 标签已创建 id={data.get('id')} name={data.get('name')}")
@@ -4,18 +4,17 @@ import click
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
6
 
7
- from artdam_cli import client, config
7
+ from artdam_cli import client
8
8
 
9
9
  console = Console()
10
10
 
11
11
 
12
12
  @click.command()
13
- @click.option("--project", "-p", default=None, type=int, help="项目 ID(省略则使用默认项目)")
13
+ @click.option("--project", "-p", required=True, type=int, help="项目 ID")
14
14
  @click.option("--search", "-s", default=None, help="按名称过滤")
15
15
  @click.option("--json", "as_json", is_flag=True, help="输出原始 JSON")
16
- def tags(project: int | None, search: str | None, as_json: bool) -> None:
16
+ def tags(project: int, search: str | None, as_json: bool) -> None:
17
17
  """列出项目标签。"""
18
- project = config.require_project(project)
19
18
  params: dict = {"project_id": project}
20
19
  if search:
21
20
  data = client.get("/api/tags/search", params={"project_id": project, "q": search})
@@ -2,16 +2,15 @@ import os
2
2
 
3
3
  import click
4
4
 
5
- from artdam_cli import client, config
5
+ from artdam_cli import client
6
6
 
7
7
 
8
8
  @click.command()
9
9
  @click.argument("file_path", type=click.Path(exists=True))
10
- @click.option("--project", "-p", type=int, default=None, help="项目 ID(省略则使用默认项目)")
10
+ @click.option("--project", "-p", type=int, required=True, help="项目 ID")
11
11
  @click.option("--folder", "-f", type=int, default=None, help="目标文件夹 ID")
12
- def upload(file_path: str, project: int | None, folder: int | None) -> None:
12
+ def upload(file_path: str, project: int, folder: int | None) -> None:
13
13
  """上传文件到 ArtDAM。"""
14
- project = config.require_project(project)
15
14
  params: dict = {"project_id": project}
16
15
  if folder is not None:
17
16
  params["folder_id"] = folder
@@ -28,12 +28,3 @@ def require(key: str) -> str:
28
28
  if not value:
29
29
  raise SystemExit(f"未配置 {key},请先运行: artdam login")
30
30
  return value
31
-
32
-
33
- def require_project(provided: int | None) -> int:
34
- if provided is not None:
35
- return provided
36
- value = get("default_project")
37
- if value is None:
38
- raise SystemExit("未指定项目 ID,请用 -p <id> 或先运行: artdam project use <id>")
39
- return int(value)
@@ -15,7 +15,6 @@ from artdam_cli.commands.folder_cmd import folder_group
15
15
  from artdam_cli.commands.comment import comment
16
16
  from artdam_cli.commands.rate import rate
17
17
  from artdam_cli.commands.favorite import favorite
18
- from artdam_cli.commands.project import project_group
19
18
 
20
19
 
21
20
  @click.group()
@@ -39,4 +38,3 @@ cli.add_command(folder_group)
39
38
  cli.add_command(comment)
40
39
  cli.add_command(rate)
41
40
  cli.add_command(favorite)
42
- cli.add_command(project_group)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: artdam-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: ArtDAM 数字资产管理平台命令行工具
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -229,7 +229,7 @@ artdam update <asset_id> --description "AI 生成的场景描述"
229
229
 
230
230
  ```json
231
231
  {
232
- "base_url": "https://artdam.dsworks.cn",
232
+ "base_url": "https://artdam.xindong.com",
233
233
  "token": "eyJ..."
234
234
  }
235
235
  ```
@@ -19,7 +19,6 @@ artdam_cli/commands/folder_cmd.py
19
19
  artdam_cli/commands/folders.py
20
20
  artdam_cli/commands/get.py
21
21
  artdam_cli/commands/login.py
22
- artdam_cli/commands/project.py
23
22
  artdam_cli/commands/rate.py
24
23
  artdam_cli/commands/restore.py
25
24
  artdam_cli/commands/search.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "artdam-cli"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  requires-python = ">=3.10"
9
9
  description = "ArtDAM 数字资产管理平台命令行工具"
10
10
  readme = "README.md"
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
@@ -1,31 +0,0 @@
1
- import click
2
- import httpx
3
-
4
- from artdam_cli import config
5
-
6
-
7
- @click.command()
8
- @click.option("--url", default="https://artdam.dsworks.cn", help="服务器地址,默认: https://artdam.dsworks.cn")
9
- @click.option("--username", prompt="用户名")
10
- @click.option("--password", prompt="密码", hide_input=True)
11
- def login(url: str, username: str, password: str) -> None:
12
- """登录 ArtDAM,保存 token 到本地。"""
13
- url = url.rstrip("/")
14
- try:
15
- r = httpx.post(
16
- f"{url}/api/auth/login",
17
- json={"username": username, "password": password},
18
- timeout=10,
19
- )
20
- except httpx.ConnectError:
21
- raise SystemExit(f"无法连接到 {url},请检查地址")
22
-
23
- if r.status_code != 200:
24
- raise SystemExit(f"登录失败: {r.json().get('detail', r.text)}")
25
-
26
- token = r.json().get("access_token")
27
- if not token:
28
- raise SystemExit("登录响应中未找到 token")
29
-
30
- config.save({"base_url": url, "token": token})
31
- click.echo(f"✓ 登录成功,配置已保存到 ~/.artdam/config.json")
@@ -1,28 +0,0 @@
1
- import click
2
-
3
- from artdam_cli import config
4
-
5
-
6
- @click.group(name="project")
7
- def project_group() -> None:
8
- """管理默认项目(省去每次输入 -p)。"""
9
-
10
-
11
- @project_group.command(name="use")
12
- @click.argument("project_id", type=int)
13
- def project_use(project_id: int) -> None:
14
- """设置默认项目 ID。"""
15
- data = config.load()
16
- data["default_project"] = project_id
17
- config.save(data)
18
- click.echo(f"✓ 默认项目已设置为 {project_id}")
19
-
20
-
21
- @project_group.command(name="show")
22
- def project_show() -> None:
23
- """查看当前默认项目。"""
24
- pid = config.get("default_project")
25
- if pid is None:
26
- click.echo("未设置默认项目,运行: artdam project use <id>")
27
- else:
28
- click.echo(f"默认项目: {pid}")
File without changes