makea-cli 0.1.16__tar.gz → 0.1.18__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 (48) hide show
  1. {makea_cli-0.1.16 → makea_cli-0.1.18}/PKG-INFO +2 -1
  2. {makea_cli-0.1.16 → makea_cli-0.1.18}/README.md +1 -0
  3. makea_cli-0.1.18/makea_cli/__init__.py +1 -0
  4. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/api_client.py +79 -0
  5. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/__init__.py +2 -0
  6. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/directory/cmd.py +81 -0
  7. makea_cli-0.1.18/makea_cli/commands/library/cmd.py +281 -0
  8. makea_cli-0.1.18/makea_cli/commands/metrics/__init__.py +0 -0
  9. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/supplier/cmd.py +1 -2
  10. {makea_cli-0.1.16 → makea_cli-0.1.18}/pyproject.toml +1 -1
  11. makea_cli-0.1.16/makea_cli/__init__.py +0 -1
  12. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
  13. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
  14. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/link-supplier-user/SKILL.md +0 -0
  15. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
  16. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
  17. {makea_cli-0.1.16 → makea_cli-0.1.18}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
  18. {makea_cli-0.1.16 → makea_cli-0.1.18}/.gitignore +0 -0
  19. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea-collection-upload/SKILL.md +0 -0
  20. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea-collection-upload/scripts/create_products.py +0 -0
  21. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/__main__.py +0 -0
  22. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/auth_pkce.py +0 -0
  23. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/_util.py +0 -0
  24. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/analytics/__init__.py +0 -0
  25. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/analytics/cmd.py +0 -0
  26. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/auth/__init__.py +0 -0
  27. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/auth/cmd.py +0 -0
  28. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/designer/__init__.py +0 -0
  29. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/designer/cmd.py +0 -0
  30. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/directory/__init__.py +0 -0
  31. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/document/__init__.py +0 -0
  32. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/document/cmd.py +0 -0
  33. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/email/__init__.py +0 -0
  34. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/email/cmd.py +0 -0
  35. {makea_cli-0.1.16/makea_cli/commands/metrics → makea_cli-0.1.18/makea_cli/commands/library}/__init__.py +0 -0
  36. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/metrics/cmd.py +0 -0
  37. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/orders/__init__.py +0 -0
  38. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/orders/cmd.py +0 -0
  39. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/product/__init__.py +0 -0
  40. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/product/cmd.py +0 -0
  41. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/commands/supplier/__init__.py +0 -0
  42. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/config.py +0 -0
  43. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/main.py +0 -0
  44. {makea_cli-0.1.16 → makea_cli-0.1.18}/makea_cli/posthog_client.py +0 -0
  45. {makea_cli-0.1.16 → makea_cli-0.1.18}/npm/makea-cli/README.md +0 -0
  46. {makea_cli-0.1.16 → makea_cli-0.1.18}/npm/makea-cli/makea-cli.mjs +0 -0
  47. {makea_cli-0.1.16 → makea_cli-0.1.18}/npm/makea-cli/package.json +0 -0
  48. {makea_cli-0.1.16 → makea_cli-0.1.18}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makea-cli
3
- Version: 0.1.16
3
+ Version: 0.1.18
4
4
  Summary: Makea admin CLI (browser login + Makea admin HTTP API)
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: httpx>=0.27
@@ -77,6 +77,7 @@ makea-cli auth --logout
77
77
  - `makea-cli auth` — 浏览器登录并保存令牌
78
78
  - `makea-cli auth --logout` — 清除本机令牌
79
79
  - `makea-cli list-users` — 列出用户(需管理员权限)
80
+ - `makea-cli create-user <email> --user-type "<type>" [--password ...] [--name ...] [--company-name ...] [--username ...]` — 注册用户并直接确认 Cognito 账号(POST `/admin/users/create`,不发验证码邮件,创建后即可用该密码登录)
80
81
  - `makea-cli list-suppliers` — 列出供应商(需管理员权限)
81
82
  - `makea-cli create-quote-request -s <supplier_id> -p <product_reference_id> [--quote-type SAMPLING|PRODUCTION] [--message ...] [--due-date YYYY-MM-DD]` — 创建并发送 quote request(POST `/admin/supplier/create_quote_request`)
82
83
  - `makea-cli get-quote-requests-by-supplier-id <supplier_id> [--status <status>] [-n <limit>]` — 直连后端按 supplier_id 查询 quote requests
@@ -66,6 +66,7 @@ makea-cli auth --logout
66
66
  - `makea-cli auth` — 浏览器登录并保存令牌
67
67
  - `makea-cli auth --logout` — 清除本机令牌
68
68
  - `makea-cli list-users` — 列出用户(需管理员权限)
69
+ - `makea-cli create-user <email> --user-type "<type>" [--password ...] [--name ...] [--company-name ...] [--username ...]` — 注册用户并直接确认 Cognito 账号(POST `/admin/users/create`,不发验证码邮件,创建后即可用该密码登录)
69
70
  - `makea-cli list-suppliers` — 列出供应商(需管理员权限)
70
71
  - `makea-cli create-quote-request -s <supplier_id> -p <product_reference_id> [--quote-type SAMPLING|PRODUCTION] [--message ...] [--due-date YYYY-MM-DD]` — 创建并发送 quote request(POST `/admin/supplier/create_quote_request`)
71
72
  - `makea-cli get-quote-requests-by-supplier-id <supplier_id> [--status <status>] [-n <limit>]` — 直连后端按 supplier_id 查询 quote requests
@@ -0,0 +1 @@
1
+ __version__ = "0.1.17"
@@ -79,6 +79,35 @@ def put_json(path: str, body: Any, *, timeout: float = 60.0) -> Any:
79
79
  return r.json()
80
80
 
81
81
 
82
+ def patch_json(path: str, body: Any, *, timeout: float = 60.0) -> Any:
83
+ """PATCH JSON body (same error handling as ``post_json``)."""
84
+ url = f"{api_base_url()}{path}"
85
+ headers = {**_headers(), "Content-Type": "application/json"}
86
+ with httpx.Client(timeout=timeout) as client:
87
+ r = client.patch(url, headers=headers, json=body)
88
+ if r.status_code >= 400:
89
+ try:
90
+ detail = r.json()
91
+ except json.JSONDecodeError:
92
+ detail = r.text
93
+ raise RuntimeError(f"HTTP {r.status_code}: {detail}")
94
+ return r.json()
95
+
96
+
97
+ def delete_json(path: str, *, params: dict[str, Any] | None = None, timeout: float = 60.0) -> Any:
98
+ """DELETE request (same error handling as ``get_json``)."""
99
+ url = f"{api_base_url()}{path}"
100
+ with httpx.Client(timeout=timeout) as client:
101
+ r = client.delete(url, headers=_headers(), params=params)
102
+ if r.status_code >= 400:
103
+ try:
104
+ detail = r.json()
105
+ except json.JSONDecodeError:
106
+ detail = r.text
107
+ raise RuntimeError(f"HTTP {r.status_code}: {detail}")
108
+ return r.json()
109
+
110
+
82
111
  def send_admin_raw_email(body: dict[str, Any]) -> Any:
83
112
  """
84
113
  POST /admin/email/send — queue HTML email (from must be @makea.co).
@@ -179,6 +208,11 @@ def get_all_users(*, limit: int | None, offset: int) -> Any:
179
208
  return get_json("/admin/users", params=params)
180
209
 
181
210
 
211
+ def create_user(body: dict[str, Any], *, timeout: float = 60.0) -> Any:
212
+ """POST /admin/users/create — admin registers a confirmed Cognito user + DDB profile."""
213
+ return post_json("/admin/users/create", body, timeout=timeout)
214
+
215
+
182
216
  def update_user_profile(*, user_id: str, body: dict[str, Any]) -> Any:
183
217
  """PUT /admin/user-profiles/{user_id}/update — admin updates DDB profile + Cognito userType."""
184
218
  return put_json(f"/admin/user-profiles/{user_id}/update", body)
@@ -816,3 +850,48 @@ def update_product_technical_specifications(
816
850
  {"technical_specifications": json.dumps(technical_specifications)},
817
851
  timeout=timeout,
818
852
  )
853
+
854
+
855
+ # ── Tech-pack library (reusable library components), admin-side ──────────
856
+ def admin_library_component_schema(*, timeout: float = 60.0) -> Any:
857
+ """GET /admin/library_components/schema — the library taxonomy (sections +
858
+ library-role field descriptors). Static; no target user."""
859
+ return get_json("/admin/library_components/schema", timeout=timeout)
860
+
861
+
862
+ def admin_list_library_components(
863
+ user_id: str, *, section: str | None = None, timeout: float = 60.0
864
+ ) -> Any:
865
+ """GET /admin/designer/{user_id}/library_components — list a designer's
866
+ tech-pack library (org-fanned). Optional ``section`` scopes to one type."""
867
+ params = {"section": section} if section else None
868
+ return get_json(f"/admin/designer/{user_id}/library_components", params=params, timeout=timeout)
869
+
870
+
871
+ def admin_create_library_component(
872
+ user_id: str, body: dict[str, Any], *, timeout: float = 60.0
873
+ ) -> Any:
874
+ """POST /admin/designer/{user_id}/library_components — {section, name?,
875
+ values, documents?}."""
876
+ return post_json(f"/admin/designer/{user_id}/library_components", body, timeout=timeout)
877
+
878
+
879
+ def admin_update_library_component(
880
+ user_id: str, component_id: str, body: dict[str, Any], *, timeout: float = 60.0
881
+ ) -> Any:
882
+ """PATCH /admin/designer/{user_id}/library_components/{component_id} —
883
+ {name?, values?, documents?}."""
884
+ return patch_json(
885
+ f"/admin/designer/{user_id}/library_components/{component_id}",
886
+ body,
887
+ timeout=timeout,
888
+ )
889
+
890
+
891
+ def admin_delete_library_component(
892
+ user_id: str, component_id: str, *, timeout: float = 60.0
893
+ ) -> Any:
894
+ """DELETE /admin/designer/{user_id}/library_components/{component_id}."""
895
+ return delete_json(
896
+ f"/admin/designer/{user_id}/library_components/{component_id}", timeout=timeout
897
+ )
@@ -8,6 +8,7 @@ from makea_cli.commands.designer.cmd import register as register_designer
8
8
  from makea_cli.commands.directory.cmd import register as register_directory
9
9
  from makea_cli.commands.document.cmd import register as register_document
10
10
  from makea_cli.commands.email.cmd import register as register_email
11
+ from makea_cli.commands.library.cmd import register as register_library
11
12
  from makea_cli.commands.metrics.cmd import register as register_metrics
12
13
  from makea_cli.commands.orders.cmd import register as register_orders
13
14
  from makea_cli.commands.product.cmd import register as register_product
@@ -25,3 +26,4 @@ def register_all(app: typer.Typer) -> None:
25
26
  register_metrics(app)
26
27
  register_analytics(app)
27
28
  register_email(app)
29
+ register_library(app)
@@ -47,6 +47,87 @@ def register(app: typer.Typer) -> None:
47
47
  """
48
48
  print_json_or_exit(console, lambda: api_client.get_all_users(limit=limit, offset=offset))
49
49
 
50
+ @app.command(
51
+ "create-user",
52
+ help=(
53
+ "POST /admin/users/create — register a user and confirm the Cognito "
54
+ "account in one step (no verification email). Allowed --user-type values: "
55
+ '"Fashion brands", "Other brands", "Supply Chain Consultant/Freelancer", '
56
+ '"Supplier", "ADMIN".'
57
+ ),
58
+ )
59
+ def create_user(
60
+ email: str = typer.Argument(
61
+ ...,
62
+ help="Email address; also the Cognito username unless --username is given.",
63
+ ),
64
+ user_type: str = typer.Option(
65
+ ...,
66
+ "--user-type",
67
+ help=(
68
+ "Cognito custom:userType for the new account. One of: "
69
+ '"Fashion brands", "Other brands", "Supply Chain Consultant/Freelancer", '
70
+ '"Supplier", "ADMIN" (case- and punctuation-sensitive).'
71
+ ),
72
+ ),
73
+ password: str = typer.Option(
74
+ ...,
75
+ "--password",
76
+ prompt=True,
77
+ hide_input=True,
78
+ confirmation_prompt=True,
79
+ help=(
80
+ "Initial password; set as permanent, so the user signs in with it "
81
+ "directly. Must satisfy the Cognito pool policy. Prompted when omitted."
82
+ ),
83
+ ),
84
+ name: str | None = typer.Option(
85
+ None,
86
+ "--name",
87
+ help="Optional display name (Cognito ``name`` + profile user_name).",
88
+ ),
89
+ company_name: str | None = typer.Option(
90
+ None,
91
+ "--company-name",
92
+ help="Optional company name written to the DDB profile row.",
93
+ ),
94
+ username: str | None = typer.Option(
95
+ None,
96
+ "--username",
97
+ help="Optional Cognito username when it should differ from the email.",
98
+ ),
99
+ ) -> None:
100
+ """
101
+ Admin: create a ready-to-use account (Cognito + DynamoDB profile).
102
+
103
+ The account is created with email_verified true and the password made
104
+ permanent, so it lands in CONFIRMED and the user can sign in immediately.
105
+ Cognito sends no invitation or verification email — pass the password on
106
+ yourself.
107
+
108
+ Output: JSON with success, user_id (Cognito sub), username, user_type,
109
+ status and profile_created.
110
+ """
111
+ if user_type not in _ALLOWED_USER_TYPES:
112
+ console.print(
113
+ f"[red]--user-type must be exactly one of {list(_ALLOWED_USER_TYPES)}.[/red]"
114
+ )
115
+ raise typer.Exit(1)
116
+
117
+ body: dict[str, object] = {
118
+ "email": email,
119
+ "password": password,
120
+ "user_type": user_type,
121
+ }
122
+ if name is not None:
123
+ body["user_name"] = name
124
+ if company_name is not None:
125
+ body["company_name"] = company_name
126
+ if username is not None:
127
+ body["username"] = username
128
+
129
+ print_json_or_exit(console, lambda: api_client.create_user(body))
130
+
50
131
  @app.command(
51
132
  "update-user-profile",
52
133
  help=(
@@ -0,0 +1,281 @@
1
+ """Admin commands for a designer's tech-pack library (reusable library components).
2
+
3
+ A *library component* is a designer's saved, reusable spec for one tech-pack
4
+ section — the ``role='library'`` field values of a fabric / colour / trim …
5
+ (name, composition, construction, certifications, pricing). Designers fill them
6
+ into tech packs ("use from library") and capture them back ("save to library");
7
+ the library is shared across their org the same way products are.
8
+
9
+ These commands are the admin side: they take the target designer's ``user_id``
10
+ explicitly (Cognito sub) and curate that designer/org's library on their behalf.
11
+ Require an admin Makea login (makea-cli auth).
12
+
13
+ Workflow: run ``library-schema`` first to learn the valid ``section`` ids and the
14
+ ``values`` keys (and which are required) before ``library-create`` / ``library-update``.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ import typer
24
+ from rich.console import Console
25
+
26
+ from makea_cli import api_client
27
+ from makea_cli.commands._util import print_json_or_exit
28
+
29
+ console = Console()
30
+
31
+
32
+ _LIBRARY_EPILOG = """
33
+ The tech-pack library holds reusable per-section specs (fabrics, colours, trims …)
34
+ keyed to a designer and shared across their org.
35
+
36
+ Taxonomy first
37
+ Run ``makea-cli library-schema`` to list the sections that support a library
38
+ (e.g. MAIN_FABRIC) and, per section, the ``values`` keys, their labels/types,
39
+ and which are required ("essential") before a component can be saved. The
40
+ server ignores any ``values`` key that is not a library-role field for the
41
+ section, so use exactly the keys the schema reports.
42
+
43
+ Creating / updating
44
+ ``values`` is an object of ``{"<field key>": <value>}`` — e.g.
45
+ ``{"main_fabric.library.name": "Pique Knit", "main_fabric.library.composition": "100% Cotton"}``.
46
+ On update, a provided value replaces; an empty string / null clears that field.
47
+ A component's display name defaults to its ``*.library.name`` value when ``name``
48
+ is omitted.
49
+
50
+ Org scope
51
+ Reads (``library-list``) fan across the target designer's whole org and tag each
52
+ component with ``owner_user_id``. Creates land under the USER_ID you pass;
53
+ update/delete work on any component in that user's org.
54
+ """.strip()
55
+
56
+
57
+ def _load_values(values: str | None, values_file: Path | None) -> dict[str, Any]:
58
+ """Resolve the ``values`` map from an inline JSON string or a JSON file."""
59
+ if values is not None and values_file is not None:
60
+ raise ValueError("Pass only one of --values / --values-file.")
61
+ raw: str | None = None
62
+ if values_file is not None:
63
+ raw = values_file.read_text(encoding="utf-8")
64
+ elif values is not None:
65
+ raw = values
66
+ if raw is None:
67
+ return {}
68
+ parsed = json.loads(raw)
69
+ if not isinstance(parsed, dict):
70
+ raise ValueError("values must be a JSON object of {field key: value}.")
71
+ return parsed
72
+
73
+
74
+ def register(app: typer.Typer) -> None:
75
+ @app.command(
76
+ "library-schema",
77
+ help=(
78
+ "GET /admin/library_components/schema — the tech-pack library taxonomy: "
79
+ "sections and their library-role field keys (run this before create/update)."
80
+ ),
81
+ epilog=_LIBRARY_EPILOG,
82
+ )
83
+ def library_schema(
84
+ timeout: float = typer.Option(
85
+ 60.0,
86
+ "--timeout",
87
+ "-t",
88
+ help="HTTP timeout in seconds.",
89
+ min=10.0,
90
+ max=300.0,
91
+ ),
92
+ ) -> None:
93
+ """Print the library taxonomy (sections + fields). Admin token required."""
94
+ print_json_or_exit(
95
+ console,
96
+ lambda: api_client.admin_library_component_schema(timeout=timeout),
97
+ )
98
+
99
+ @app.command(
100
+ "library-list",
101
+ help=(
102
+ "GET /admin/designer/{user_id}/library_components — list a designer's "
103
+ "tech-pack library (org-fanned). Optional --section scopes to one type."
104
+ ),
105
+ epilog=_LIBRARY_EPILOG,
106
+ )
107
+ def library_list(
108
+ user_id: str = typer.Argument(..., help="Target designer Cognito user_id (sub)."),
109
+ section: str | None = typer.Option(
110
+ None,
111
+ "--section",
112
+ "-s",
113
+ help="Scope to one section id (e.g. MAIN_FABRIC); see library-schema.",
114
+ ),
115
+ timeout: float = typer.Option(
116
+ 60.0,
117
+ "--timeout",
118
+ "-t",
119
+ help="HTTP timeout in seconds.",
120
+ min=10.0,
121
+ max=300.0,
122
+ ),
123
+ ) -> None:
124
+ """List the target designer's library components across their org."""
125
+ print_json_or_exit(
126
+ console,
127
+ lambda: api_client.admin_list_library_components(
128
+ user_id, section=section, timeout=timeout
129
+ ),
130
+ )
131
+
132
+ @app.command(
133
+ "library-create",
134
+ help=(
135
+ "POST /admin/designer/{user_id}/library_components — save a reusable "
136
+ "component under a designer. Provide --section and --values (see library-schema)."
137
+ ),
138
+ epilog=_LIBRARY_EPILOG,
139
+ )
140
+ def library_create(
141
+ user_id: str = typer.Argument(..., help="Target designer Cognito user_id (sub)."),
142
+ section: str = typer.Option(
143
+ ...,
144
+ "--section",
145
+ "-s",
146
+ help="Section id the component belongs to (e.g. MAIN_FABRIC).",
147
+ ),
148
+ name: str | None = typer.Option(
149
+ None,
150
+ "--name",
151
+ "-n",
152
+ help="Optional display name (defaults to the *.library.name value).",
153
+ ),
154
+ values: str | None = typer.Option(
155
+ None,
156
+ "--values",
157
+ help='Inline JSON object of {field key: value}, e.g. \'{"main_fabric.library.name":"Pique Knit"}\'.',
158
+ ),
159
+ values_file: Path | None = typer.Option(
160
+ None,
161
+ "--values-file",
162
+ "-f",
163
+ exists=True,
164
+ dir_okay=False,
165
+ readable=True,
166
+ help="Path to a UTF-8 JSON file holding the values object (instead of --values).",
167
+ ),
168
+ timeout: float = typer.Option(
169
+ 60.0,
170
+ "--timeout",
171
+ "-t",
172
+ help="HTTP timeout in seconds.",
173
+ min=10.0,
174
+ max=300.0,
175
+ ),
176
+ ) -> None:
177
+ """Create a library component under the target designer."""
178
+ try:
179
+ body: dict[str, Any] = {
180
+ "section": section,
181
+ "values": _load_values(values, values_file),
182
+ }
183
+ if name is not None:
184
+ body["name"] = name
185
+ except (OSError, json.JSONDecodeError, ValueError) as e:
186
+ console.print(f"[red]Invalid input: {e}[/red]")
187
+ raise typer.Exit(1) from e
188
+
189
+ print_json_or_exit(
190
+ console,
191
+ lambda: api_client.admin_create_library_component(user_id, body, timeout=timeout),
192
+ )
193
+
194
+ @app.command(
195
+ "library-update",
196
+ help=(
197
+ "PATCH /admin/designer/{user_id}/library_components/{component_id} — update "
198
+ "a component's name and/or values (empty value clears a field)."
199
+ ),
200
+ epilog=_LIBRARY_EPILOG,
201
+ )
202
+ def library_update(
203
+ user_id: str = typer.Argument(..., help="Target designer Cognito user_id (sub)."),
204
+ component_id: str = typer.Argument(..., help="Library component UUID."),
205
+ name: str | None = typer.Option(None, "--name", "-n", help="New display name."),
206
+ values: str | None = typer.Option(
207
+ None,
208
+ "--values",
209
+ help='Inline JSON object of {field key: value} to merge; "" or null clears a field.',
210
+ ),
211
+ values_file: Path | None = typer.Option(
212
+ None,
213
+ "--values-file",
214
+ "-f",
215
+ exists=True,
216
+ dir_okay=False,
217
+ readable=True,
218
+ help="Path to a UTF-8 JSON file holding the values object (instead of --values).",
219
+ ),
220
+ timeout: float = typer.Option(
221
+ 60.0,
222
+ "--timeout",
223
+ "-t",
224
+ help="HTTP timeout in seconds.",
225
+ min=10.0,
226
+ max=300.0,
227
+ ),
228
+ ) -> None:
229
+ """Update a library component's name and/or values."""
230
+ try:
231
+ body: dict[str, Any] = {}
232
+ if name is not None:
233
+ body["name"] = name
234
+ if values is not None or values_file is not None:
235
+ body["values"] = _load_values(values, values_file)
236
+ if not body:
237
+ raise ValueError("Nothing to update: pass --name and/or --values.")
238
+ except (OSError, json.JSONDecodeError, ValueError) as e:
239
+ console.print(f"[red]Invalid input: {e}[/red]")
240
+ raise typer.Exit(1) from e
241
+
242
+ print_json_or_exit(
243
+ console,
244
+ lambda: api_client.admin_update_library_component(
245
+ user_id, component_id, body, timeout=timeout
246
+ ),
247
+ )
248
+
249
+ @app.command(
250
+ "library-delete",
251
+ help=(
252
+ "DELETE /admin/designer/{user_id}/library_components/{component_id} — remove "
253
+ "a component from the designer's org library."
254
+ ),
255
+ epilog=_LIBRARY_EPILOG,
256
+ )
257
+ def library_delete(
258
+ user_id: str = typer.Argument(..., help="Target designer Cognito user_id (sub)."),
259
+ component_id: str = typer.Argument(..., help="Library component UUID."),
260
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt."),
261
+ timeout: float = typer.Option(
262
+ 60.0,
263
+ "--timeout",
264
+ "-t",
265
+ help="HTTP timeout in seconds.",
266
+ min=10.0,
267
+ max=300.0,
268
+ ),
269
+ ) -> None:
270
+ """Delete a library component from the target designer's org library."""
271
+ if not yes:
272
+ typer.confirm(
273
+ f"Delete library component {component_id} from {user_id}'s org library?",
274
+ abort=True,
275
+ )
276
+ print_json_or_exit(
277
+ console,
278
+ lambda: api_client.admin_delete_library_component(
279
+ user_id, component_id, timeout=timeout
280
+ ),
281
+ )
@@ -211,8 +211,7 @@ def register(app: typer.Typer) -> None:
211
211
  resolved_id = supplier_id or raw.get("supplier_id") or body["supplier"].get("supplier_id")
212
212
  if not resolved_id:
213
213
  console.print(
214
- "[red]supplier_id is required: pass --supplier-id or include it in the "
215
- "JSON.[/red]"
214
+ "[red]supplier_id is required: pass --supplier-id or include it in the JSON.[/red]"
216
215
  )
217
216
  raise typer.Exit(1)
218
217
  body["supplier_id"] = resolved_id
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "makea-cli"
7
- version = "0.1.16"
7
+ version = "0.1.18"
8
8
  description = "Makea admin CLI (browser login + Makea admin HTTP API)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1 +0,0 @@
1
- __version__ = "0.1.16"
File without changes
File without changes
File without changes