makea-cli 0.2.0__tar.gz → 0.2.1__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 (64) hide show
  1. {makea_cli-0.2.0 → makea_cli-0.2.1}/CHANGELOG.md +19 -0
  2. {makea_cli-0.2.0 → makea_cli-0.2.1}/PKG-INFO +13 -1
  3. {makea_cli-0.2.0 → makea_cli-0.2.1}/README.md +12 -0
  4. makea_cli-0.2.1/makea_cli/__init__.py +1 -0
  5. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/api_client.py +92 -0
  6. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/brand/cmd.py +162 -0
  7. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/factory/cmd.py +143 -0
  8. {makea_cli-0.2.0 → makea_cli-0.2.1}/pyproject.toml +1 -1
  9. {makea_cli-0.2.0 → makea_cli-0.2.1}/tests/golden_manifest.json +613 -0
  10. {makea_cli-0.2.0 → makea_cli-0.2.1}/tests/test_persona_commands.py +115 -0
  11. makea_cli-0.2.0/makea_cli/__init__.py +0 -1
  12. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
  13. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
  14. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/link-supplier-user/SKILL.md +0 -0
  15. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
  16. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
  17. {makea_cli-0.2.0 → makea_cli-0.2.1}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
  18. {makea_cli-0.2.0 → makea_cli-0.2.1}/.gitignore +0 -0
  19. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea-collection-upload/SKILL.md +0 -0
  20. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea-collection-upload/scripts/create_products.py +0 -0
  21. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/__main__.py +0 -0
  22. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/auth_pkce.py +0 -0
  23. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/__init__.py +0 -0
  24. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/_util.py +0 -0
  25. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/analytics/__init__.py +0 -0
  26. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/analytics/cmd.py +0 -0
  27. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/auth/__init__.py +0 -0
  28. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/auth/cmd.py +0 -0
  29. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/brand/__init__.py +0 -0
  30. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/designer/__init__.py +0 -0
  31. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/designer/cmd.py +0 -0
  32. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/directory/__init__.py +0 -0
  33. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/directory/cmd.py +0 -0
  34. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/document/__init__.py +0 -0
  35. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/document/cmd.py +0 -0
  36. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/email/__init__.py +0 -0
  37. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/email/cmd.py +0 -0
  38. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/factory/__init__.py +0 -0
  39. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/library/__init__.py +0 -0
  40. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/library/cmd.py +0 -0
  41. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/metrics/__init__.py +0 -0
  42. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/metrics/cmd.py +0 -0
  43. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/orders/__init__.py +0 -0
  44. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/orders/cmd.py +0 -0
  45. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/product/__init__.py +0 -0
  46. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/product/cmd.py +0 -0
  47. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/production/__init__.py +0 -0
  48. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/production/cmd.py +0 -0
  49. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/production/pricing_spec.py +0 -0
  50. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/supplier/__init__.py +0 -0
  51. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/commands/supplier/cmd.py +0 -0
  52. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/config.py +0 -0
  53. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/errors.py +0 -0
  54. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/identity.py +0 -0
  55. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/main.py +0 -0
  56. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/output.py +0 -0
  57. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/posthog_client.py +0 -0
  58. {makea_cli-0.2.0 → makea_cli-0.2.1}/makea_cli/registry.py +0 -0
  59. {makea_cli-0.2.0 → makea_cli-0.2.1}/npm/makea-cli/README.md +0 -0
  60. {makea_cli-0.2.0 → makea_cli-0.2.1}/npm/makea-cli/makea-cli.mjs +0 -0
  61. {makea_cli-0.2.0 → makea_cli-0.2.1}/npm/makea-cli/package.json +0 -0
  62. {makea_cli-0.2.0 → makea_cli-0.2.1}/tests/test_output_contract.py +0 -0
  63. {makea_cli-0.2.0 → makea_cli-0.2.1}/tests/test_registry.py +0 -0
  64. {makea_cli-0.2.0 → makea_cli-0.2.1}/uv.lock +0 -0
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ Each persona can now clear its own queue. `list task` and `list action` could show
6
+ you the work; nothing could close it.
7
+
8
+ - **designer**: `list action`, `update task --complete|--dismiss`,
9
+ `update action --acknowledge|--snooze DAYS|--dismiss`,
10
+ `update note --accept|--reject` (a task the assistant proposed),
11
+ `update proposal --note N` (apply one proposed spec change),
12
+ `update suggested-action -a note:action` (accept several at once, one write).
13
+ - **supplier**: `list task`, `list quote-edit-request`, `update task`, `update action`,
14
+ `update quote <rfq> <link> --file` (revise a quote already sent), and
15
+ `update rfq --status|--archive|--accept-terms` — a factory could submit a quote
16
+ but had no way to correct it or answer the invitation.
17
+
18
+ Acknowledging an action does not close it: an action is closed by the fact it asks
19
+ for. The help text says so, because the opposite assumption is easy to make and
20
+ produces a dashboard that lies.
21
+
3
22
  ## 0.2.0
4
23
 
5
24
  **The command names changed.** Every command is now `makea-cli <persona> <verb> <noun>`:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: makea-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Makea admin CLI (browser login + Makea admin HTTP API)
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: click<9,>=8.1
@@ -154,6 +154,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
154
154
  | `makea-cli designer get quote-advice` | read | `GET /designer/product/quote_advice` | which quote to award and why, from cache. Reading never spends a model call; the web app is what asks for a fresh one. | — |
155
155
  | `makea-cli designer get tech-pack` | read | `GET /designer/products/{product_id}/technical-specifications` | the product's tech pack. | — |
156
156
  | `makea-cli designer get tech-pack-history` | read | `GET /designer/products/{product_id}/specification-change-history` | who changed which spec field, when, and from what. | — |
157
+ | `makea-cli designer list action` | read | `GET /designer/actions` | the pending actions on you, optionally for one product. An action is closed by the fact it asks for, not by acknowled… | — |
157
158
  | `makea-cli designer list channel` | read | `GET /designer/products/{product_id}/channels` | the product's conversation channels. | — |
158
159
  | `makea-cli designer list collection` | read | `GET /designer/collections` | the brand's collections. | — |
159
160
  | `makea-cli designer list library-element` | read | `GET /designer/library_components` | the brand's own shelf (fabrics, trims, colours, artwork); --section narrows to one kind. | — |
@@ -166,7 +167,12 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
166
167
  | `makea-cli designer list task` | read | `GET /designer/tasks` | what the platform is waiting on you for. | — |
167
168
  | `makea-cli designer search note` | read | `GET /designer/products/{product_id}/channel_search` | full-text search across a product's conversation; --channel narrows to one channel key. | — |
168
169
  | `makea-cli designer set product-collection` | write(auto) | `POST /designer/products/{id}/link-to-collection` | assign product to a collection. | `designer-link-product-to-collection` |
170
+ | `makea-cli designer update action` | write(auto) | `POST /designer/actions/{action_id}/{acknowledge\|snooze\|dismiss}` | respond to one of the pending actions from `designer list action`. An action is closed by the fact it asks for, not b… | — |
171
+ | `makea-cli designer update note` | write(ask) | `POST /designer/products/{product_id}/notes/{note_id}/{accept\|reject}` | decide on a task the assistant proposed on a product. | — |
169
172
  | `makea-cli designer update product` | write(auto) | `POST /designer/product/update_basic_info` | patch fields on a product. --cover-image / --back-image take a document id and are what the assistant's set_product_i… | — |
173
+ | `makea-cli designer update proposal` | write(ask) | `POST /designer/beeboo_proposals/approve` | apply one spec change the assistant proposed. This edits the pack, so read the proposal first. | — |
174
+ | `makea-cli designer update suggested-action` | write(ask) | `POST /designer/channels/{channel_id}/suggested_actions/bulk_accept` | accept several of a channel's suggested actions in one write. --action takes note_id:action_id and repeats. | — |
175
+ | `makea-cli designer update task` | write(auto) | `POST /designer/tasks/{complete\|dismiss}` | close one of the tasks from `designer list task`. Complete means you did it; dismiss means it does not apply. | — |
170
176
  | `makea-cli designer upload document` | write(auto) | `POST /designer/document/upload` | upload a file for the current designer token; optional product_id (omit to start a new draft id server-side). | `designer-upload-document` |
171
177
 
172
178
  ### `supplier`
@@ -183,8 +189,14 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
183
189
  | `makea-cli supplier list channel` | read | `GET /supplier/products/{product_id}/channels` | the channels you are part of on a product. | — |
184
190
  | `makea-cli supplier list note` | read | `GET /supplier/products/{product_id}/channels/{channel_key}/notes` | the conversation in one channel. | — |
185
191
  | `makea-cli supplier list production-order` | read | `GET /supplier/production_orders` | your production orders, optionally for one RFQ. | — |
192
+ | `makea-cli supplier list quote-edit-request` | read | `GET /supplier/quote_requests/{quote_request_id}/quote_edit_requests` | changes the brand has asked you to make to a quote you already sent. | — |
186
193
  | `makea-cli supplier list rfq` | read | `GET /supplier/quote_requests` | the RFQs (quote requests) sent to you. | — |
194
+ | `makea-cli supplier list task` | read | `GET /supplier/tasks` | your onboarding and per-order to-dos. | — |
187
195
  | `makea-cli supplier submit quote` | write(ask) | `POST /supplier/quote_requests/{id}/submit_quote` | submit your quote for an RFQ. --file is the supplier link payload as JSON (same shape the web form posts); run with -… | — |
196
+ | `makea-cli supplier update action` | write(auto) | `POST /supplier/actions/{action_id}/{acknowledge\|snooze}` | respond to a pending action. Acknowledging says you have seen it; the action still closes on the fact it asks for. | — |
197
+ | `makea-cli supplier update quote` | write(ask) | `POST /supplier/quote_requests/{quote_request_id}/links/{link_id}/edit` | revise a quote you already sent. --file is the same payload shape as submit; --dry-run first. | — |
198
+ | `makea-cli supplier update rfq` | write(ask) | `POST /supplier/quote_requests/{quote_request_id}/{status\|archive\|accept_terms}` | answer an RFQ. --status accepts or declines the invitation; --archive takes it off your board; --accept-terms accepts… | — |
199
+ | `makea-cli supplier update task` | write(auto) | `POST /supplier/tasks/{complete\|dismiss}` | close one of the tasks from `supplier list task`. | — |
188
200
 
189
201
  其它:`makea-cli auth [--logout]` 登录/登出;`makea-cli version` 版本号。
190
202
 
@@ -142,6 +142,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
142
142
  | `makea-cli designer get quote-advice` | read | `GET /designer/product/quote_advice` | which quote to award and why, from cache. Reading never spends a model call; the web app is what asks for a fresh one. | — |
143
143
  | `makea-cli designer get tech-pack` | read | `GET /designer/products/{product_id}/technical-specifications` | the product's tech pack. | — |
144
144
  | `makea-cli designer get tech-pack-history` | read | `GET /designer/products/{product_id}/specification-change-history` | who changed which spec field, when, and from what. | — |
145
+ | `makea-cli designer list action` | read | `GET /designer/actions` | the pending actions on you, optionally for one product. An action is closed by the fact it asks for, not by acknowled… | — |
145
146
  | `makea-cli designer list channel` | read | `GET /designer/products/{product_id}/channels` | the product's conversation channels. | — |
146
147
  | `makea-cli designer list collection` | read | `GET /designer/collections` | the brand's collections. | — |
147
148
  | `makea-cli designer list library-element` | read | `GET /designer/library_components` | the brand's own shelf (fabrics, trims, colours, artwork); --section narrows to one kind. | — |
@@ -154,7 +155,12 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
154
155
  | `makea-cli designer list task` | read | `GET /designer/tasks` | what the platform is waiting on you for. | — |
155
156
  | `makea-cli designer search note` | read | `GET /designer/products/{product_id}/channel_search` | full-text search across a product's conversation; --channel narrows to one channel key. | — |
156
157
  | `makea-cli designer set product-collection` | write(auto) | `POST /designer/products/{id}/link-to-collection` | assign product to a collection. | `designer-link-product-to-collection` |
158
+ | `makea-cli designer update action` | write(auto) | `POST /designer/actions/{action_id}/{acknowledge\|snooze\|dismiss}` | respond to one of the pending actions from `designer list action`. An action is closed by the fact it asks for, not b… | — |
159
+ | `makea-cli designer update note` | write(ask) | `POST /designer/products/{product_id}/notes/{note_id}/{accept\|reject}` | decide on a task the assistant proposed on a product. | — |
157
160
  | `makea-cli designer update product` | write(auto) | `POST /designer/product/update_basic_info` | patch fields on a product. --cover-image / --back-image take a document id and are what the assistant's set_product_i… | — |
161
+ | `makea-cli designer update proposal` | write(ask) | `POST /designer/beeboo_proposals/approve` | apply one spec change the assistant proposed. This edits the pack, so read the proposal first. | — |
162
+ | `makea-cli designer update suggested-action` | write(ask) | `POST /designer/channels/{channel_id}/suggested_actions/bulk_accept` | accept several of a channel's suggested actions in one write. --action takes note_id:action_id and repeats. | — |
163
+ | `makea-cli designer update task` | write(auto) | `POST /designer/tasks/{complete\|dismiss}` | close one of the tasks from `designer list task`. Complete means you did it; dismiss means it does not apply. | — |
158
164
  | `makea-cli designer upload document` | write(auto) | `POST /designer/document/upload` | upload a file for the current designer token; optional product_id (omit to start a new draft id server-side). | `designer-upload-document` |
159
165
 
160
166
  ### `supplier`
@@ -171,8 +177,14 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
171
177
  | `makea-cli supplier list channel` | read | `GET /supplier/products/{product_id}/channels` | the channels you are part of on a product. | — |
172
178
  | `makea-cli supplier list note` | read | `GET /supplier/products/{product_id}/channels/{channel_key}/notes` | the conversation in one channel. | — |
173
179
  | `makea-cli supplier list production-order` | read | `GET /supplier/production_orders` | your production orders, optionally for one RFQ. | — |
180
+ | `makea-cli supplier list quote-edit-request` | read | `GET /supplier/quote_requests/{quote_request_id}/quote_edit_requests` | changes the brand has asked you to make to a quote you already sent. | — |
174
181
  | `makea-cli supplier list rfq` | read | `GET /supplier/quote_requests` | the RFQs (quote requests) sent to you. | — |
182
+ | `makea-cli supplier list task` | read | `GET /supplier/tasks` | your onboarding and per-order to-dos. | — |
175
183
  | `makea-cli supplier submit quote` | write(ask) | `POST /supplier/quote_requests/{id}/submit_quote` | submit your quote for an RFQ. --file is the supplier link payload as JSON (same shape the web form posts); run with -… | — |
184
+ | `makea-cli supplier update action` | write(auto) | `POST /supplier/actions/{action_id}/{acknowledge\|snooze}` | respond to a pending action. Acknowledging says you have seen it; the action still closes on the fact it asks for. | — |
185
+ | `makea-cli supplier update quote` | write(ask) | `POST /supplier/quote_requests/{quote_request_id}/links/{link_id}/edit` | revise a quote you already sent. --file is the same payload shape as submit; --dry-run first. | — |
186
+ | `makea-cli supplier update rfq` | write(ask) | `POST /supplier/quote_requests/{quote_request_id}/{status\|archive\|accept_terms}` | answer an RFQ. --status accepts or declines the invitation; --archive takes it off your board; --accept-terms accepts… | — |
187
+ | `makea-cli supplier update task` | write(auto) | `POST /supplier/tasks/{complete\|dismiss}` | close one of the tasks from `supplier list task`. | — |
176
188
 
177
189
  其它:`makea-cli auth [--logout]` 登录/登出;`makea-cli version` 版本号。
178
190
 
@@ -0,0 +1 @@
1
+ __version__ = "0.2.1"
@@ -1220,3 +1220,95 @@ def supplier_download_document(
1220
1220
  ) -> tuple[bytes, str | None]:
1221
1221
  """GET /supplier/document/download — a document this factory may read."""
1222
1222
  return _download("/supplier/document/download", {"document_id": document_id}, timeout=timeout)
1223
+
1224
+
1225
+ # --- clearing the queue: tasks, actions, and the assistant's proposals ----------
1226
+
1227
+
1228
+ def designer_task_action(action: str, task_id: str) -> Any:
1229
+ """POST /designer/tasks/{dismiss|complete} — body ``{"task_id": "<type>#<key>"}``."""
1230
+ return post_json(f"/designer/tasks/{action}", {"task_id": task_id})
1231
+
1232
+
1233
+ def designer_action_state(action_id: str, state: str, *, days: int | None = None) -> Any:
1234
+ """POST /designer/actions/{id}/{acknowledge|snooze|dismiss}."""
1235
+ body: dict[str, Any] = {}
1236
+ if state == "snooze" and days is not None:
1237
+ body["days"] = days
1238
+ return post_json(f"/designer/actions/{action_id}/{state}", body)
1239
+
1240
+
1241
+ def designer_note_decision(product_id: str, note_id: str, decision: str) -> Any:
1242
+ """POST /designer/products/{id}/notes/{note_id}/{accept|reject} — a task candidate."""
1243
+ return post_json(f"/designer/products/{product_id}/notes/{note_id}/{decision}", {})
1244
+
1245
+
1246
+ def designer_approve_proposal(note_id: str, proposal_id: str) -> Any:
1247
+ """POST /designer/beeboo_proposals/approve — apply one of the assistant's spec proposals."""
1248
+ return post_json(
1249
+ "/designer/beeboo_proposals/approve", {"note_id": note_id, "proposal_id": proposal_id}
1250
+ )
1251
+
1252
+
1253
+ def designer_bulk_accept_actions(channel_id: str, actions: list[dict[str, str]]) -> Any:
1254
+ """POST /designer/channels/{id}/suggested_actions/bulk_accept — accept several at once."""
1255
+ return post_json(
1256
+ f"/designer/channels/{channel_id}/suggested_actions/bulk_accept", {"actions": actions}
1257
+ )
1258
+
1259
+
1260
+ def supplier_task_action(action: str, task_id: str) -> Any:
1261
+ """POST /supplier/tasks/{dismiss|complete} — body ``{"task_id": "<type>#<key>"}``."""
1262
+ return post_json(f"/supplier/tasks/{action}", {"task_id": task_id})
1263
+
1264
+
1265
+ def supplier_action_state(action_id: str, state: str, *, days: int | None = None) -> Any:
1266
+ """POST /supplier/actions/{id}/{acknowledge|snooze}."""
1267
+ body: dict[str, Any] = {}
1268
+ if state == "snooze" and days is not None:
1269
+ body["days"] = days
1270
+ return post_json(f"/supplier/actions/{action_id}/{state}", body)
1271
+
1272
+
1273
+ # --- the factory's quote lifecycle ----------------------------------------------
1274
+
1275
+
1276
+ def supplier_edit_quote(quote_request_id: str, link_id: str, body: dict[str, Any]) -> Any:
1277
+ """POST /supplier/quote_requests/{id}/links/{link_id}/edit — revise a quote already sent."""
1278
+ return post_json(
1279
+ f"/supplier/quote_requests/{quote_request_id}/links/{link_id}/edit", body, timeout=120.0
1280
+ )
1281
+
1282
+
1283
+ def supplier_set_rfq_status(quote_request_id: str, status: str) -> Any:
1284
+ """POST /supplier/quote_requests/{id}/status — accept or decline the invitation."""
1285
+ return post_json(f"/supplier/quote_requests/{quote_request_id}/status", {"status": status})
1286
+
1287
+
1288
+ def supplier_archive_rfq(quote_request_id: str) -> Any:
1289
+ """POST /supplier/quote_requests/{id}/archive — take it off your board."""
1290
+ return post_json(f"/supplier/quote_requests/{quote_request_id}/archive", {})
1291
+
1292
+
1293
+ def supplier_accept_terms(quote_request_id: str, agreement_keys: list[str]) -> Any:
1294
+ """POST /supplier/quote_requests/{id}/accept_terms — accept the named agreements."""
1295
+ return post_json(
1296
+ f"/supplier/quote_requests/{quote_request_id}/accept_terms",
1297
+ {"agreement_keys": agreement_keys},
1298
+ )
1299
+
1300
+
1301
+ def supplier_list_quote_edit_requests(quote_request_id: str) -> Any:
1302
+ """GET /supplier/quote_requests/{id}/quote_edit_requests — the brand's change requests."""
1303
+ return get_json(f"/supplier/quote_requests/{quote_request_id}/quote_edit_requests")
1304
+
1305
+
1306
+ def designer_list_actions(*, product_id: str | None = None) -> Any:
1307
+ """GET /designer/actions — the brand's pending actions, optionally for one product."""
1308
+ params = {"product_id": product_id} if product_id else None
1309
+ return get_json("/designer/actions", params=params)
1310
+
1311
+
1312
+ def supplier_list_tasks() -> Any:
1313
+ """GET /supplier/tasks — the factory's onboarding and per-order to-dos."""
1314
+ return get_json("/supplier/tasks")
@@ -272,6 +272,23 @@ def register(app: typer.Typer) -> None:
272
272
  """List the brand's open tasks."""
273
273
  print_json_or_exit(console, lambda: api_client.designer_list_tasks())
274
274
 
275
+ @command(
276
+ app,
277
+ "designer",
278
+ "list",
279
+ "action",
280
+ api="GET /designer/actions",
281
+ help=(
282
+ "GET /designer/actions — the pending actions on you, optionally for one product. "
283
+ "An action is closed by the fact it asks for, not by acknowledging it."
284
+ ),
285
+ )
286
+ def list_actions(
287
+ product: str | None = typer.Option(None, "--product", "-p", help="Only this product's."),
288
+ ) -> None:
289
+ """List the brand's pending actions."""
290
+ print_json_or_exit(console, lambda: api_client.designer_list_actions(product_id=product))
291
+
275
292
  @command(
276
293
  app,
277
294
  "designer",
@@ -377,6 +394,151 @@ def register(app: typer.Typer) -> None:
377
394
  console, lambda: api_client.designer_update_product_basic_info(product_id, payload)
378
395
  )
379
396
 
397
+ @command(
398
+ app,
399
+ "designer",
400
+ "update",
401
+ "task",
402
+ api="POST /designer/tasks/{complete|dismiss}",
403
+ side_effects=["clears the task off your dashboard"],
404
+ approval="auto",
405
+ help=(
406
+ "POST /designer/tasks/{complete|dismiss} — close one of the tasks from "
407
+ "`designer list task`. Complete means you did it; dismiss means it does not apply."
408
+ ),
409
+ )
410
+ def update_task(
411
+ task_id: str = typer.Argument(..., help="Task id, the '<type>#<key>' from list task."),
412
+ complete: bool = typer.Option(False, "--complete", help="Mark it done."),
413
+ dismiss: bool = typer.Option(False, "--dismiss", help="Hide it as not applicable."),
414
+ ) -> None:
415
+ """Complete or dismiss a task."""
416
+ if complete == dismiss:
417
+ raise typer.BadParameter("pass exactly one of --complete / --dismiss")
418
+ verb = "complete" if complete else "dismiss"
419
+ print_json_or_exit(console, lambda: api_client.designer_task_action(verb, task_id))
420
+
421
+ @command(
422
+ app,
423
+ "designer",
424
+ "update",
425
+ "action",
426
+ api="POST /designer/actions/{action_id}/{acknowledge|snooze|dismiss}",
427
+ side_effects=["changes what your pending-actions list shows"],
428
+ approval="auto",
429
+ help=(
430
+ "POST /designer/actions/{id}/{acknowledge|snooze|dismiss} — respond to one of the "
431
+ "pending actions from `designer list action`. An action is closed by the fact it "
432
+ "asks for, not by this; acknowledging only says you have seen it."
433
+ ),
434
+ )
435
+ def update_action(
436
+ action_id: str = typer.Argument(..., help="Action id from list action."),
437
+ acknowledge: bool = typer.Option(False, "--acknowledge", help="Mark it seen."),
438
+ snooze: int | None = typer.Option(
439
+ None, "--snooze", metavar="DAYS", help="Hide it for this many days."
440
+ ),
441
+ dismiss: bool = typer.Option(False, "--dismiss", help="Drop it."),
442
+ ) -> None:
443
+ """Acknowledge, snooze or dismiss a pending action."""
444
+ chosen = [
445
+ n
446
+ for n, v in (
447
+ ("acknowledge", acknowledge),
448
+ ("snooze", snooze is not None),
449
+ ("dismiss", dismiss),
450
+ )
451
+ if v
452
+ ]
453
+ if len(chosen) != 1:
454
+ raise typer.BadParameter(
455
+ "pass exactly one of --acknowledge / --snooze DAYS / --dismiss"
456
+ )
457
+ state = chosen[0]
458
+ print_json_or_exit(
459
+ console, lambda: api_client.designer_action_state(action_id, state, days=snooze)
460
+ )
461
+
462
+ @command(
463
+ app,
464
+ "designer",
465
+ "update",
466
+ "note",
467
+ api="POST /designer/products/{product_id}/notes/{note_id}/{accept|reject}",
468
+ side_effects=["accepting a task candidate creates the task and notifies whoever it is for"],
469
+ approval="ask",
470
+ help=(
471
+ "POST /designer/products/{id}/notes/{note_id}/{accept|reject} — decide on a task "
472
+ "the assistant proposed on a product."
473
+ ),
474
+ )
475
+ def update_note(
476
+ note_id: str = typer.Argument(..., help="Note id carrying the proposed task."),
477
+ product: str = typer.Option(..., "--product", "-p", help="Product the note is on."),
478
+ accept: bool = typer.Option(False, "--accept", help="Create the task."),
479
+ reject: bool = typer.Option(False, "--reject", help="Turn it down."),
480
+ ) -> None:
481
+ """Accept or reject a proposed task."""
482
+ if accept == reject:
483
+ raise typer.BadParameter("pass exactly one of --accept / --reject")
484
+ decision = "accept" if accept else "reject"
485
+ print_json_or_exit(
486
+ console, lambda: api_client.designer_note_decision(product, note_id, decision)
487
+ )
488
+
489
+ @command(
490
+ app,
491
+ "designer",
492
+ "update",
493
+ "proposal",
494
+ api="POST /designer/beeboo_proposals/approve",
495
+ side_effects=[
496
+ "writes the proposed change into the tech pack",
497
+ "notifies the suppliers who hold that pack",
498
+ ],
499
+ approval="ask",
500
+ help=(
501
+ "POST /designer/beeboo_proposals/approve — apply one spec change the assistant "
502
+ "proposed. This edits the pack, so read the proposal first."
503
+ ),
504
+ )
505
+ def update_proposal(
506
+ proposal_id: str = typer.Argument(..., help="Proposal id from the note."),
507
+ note: str = typer.Option(..., "--note", help="Note id the proposal hangs on."),
508
+ ) -> None:
509
+ """Approve one assistant proposal."""
510
+ print_json_or_exit(console, lambda: api_client.designer_approve_proposal(note, proposal_id))
511
+
512
+ @command(
513
+ app,
514
+ "designer",
515
+ "update",
516
+ "suggested-action",
517
+ api="POST /designer/channels/{channel_id}/suggested_actions/bulk_accept",
518
+ side_effects=["applies every accepted action", "fans out one notification for the batch"],
519
+ approval="ask",
520
+ help=(
521
+ "POST /designer/channels/{id}/suggested_actions/bulk_accept — accept several of a "
522
+ "channel's suggested actions in one write. --action takes note_id:action_id and repeats."
523
+ ),
524
+ )
525
+ def update_suggested_actions(
526
+ channel_id: str = typer.Argument(..., help="Channel holding the notes."),
527
+ action: list[str] = typer.Option(
528
+ ..., "--action", "-a", help="note_id:action_id (repeat per action)."
529
+ ),
530
+ ) -> None:
531
+ """Accept suggested actions in bulk."""
532
+ pairs: list[dict[str, str]] = []
533
+ for raw in action:
534
+ note_id, sep, action_id = raw.partition(":")
535
+ if not sep or not note_id or not action_id:
536
+ raise typer.BadParameter(f"--action must be note_id:action_id, got {raw!r}")
537
+ pairs.append({"note_id": note_id, "action_id": action_id})
538
+ print_json_or_exit(
539
+ console, lambda: api_client.designer_bulk_accept_actions(channel_id, pairs)
540
+ )
541
+
380
542
  @command(
381
543
  app,
382
544
  "designer",
@@ -192,6 +192,149 @@ def register(app: typer.Typer) -> None:
192
192
 
193
193
  print_json_or_exit(console, run)
194
194
 
195
+ @command(
196
+ app,
197
+ "supplier",
198
+ "list",
199
+ "quote-edit-request",
200
+ api="GET /supplier/quote_requests/{quote_request_id}/quote_edit_requests",
201
+ help=(
202
+ "GET /supplier/quote_requests/{id}/quote_edit_requests — changes the brand has "
203
+ "asked you to make to a quote you already sent."
204
+ ),
205
+ )
206
+ def list_quote_edit_requests(
207
+ quote_request_id: str = typer.Argument(..., help="The quote request."),
208
+ ) -> None:
209
+ """List the brand's requested changes to your quote."""
210
+ print_json_or_exit(
211
+ console, lambda: api_client.supplier_list_quote_edit_requests(quote_request_id)
212
+ )
213
+
214
+ @command(
215
+ app,
216
+ "supplier",
217
+ "update",
218
+ "task",
219
+ api="POST /supplier/tasks/{complete|dismiss}",
220
+ side_effects=["clears the task off your dashboard"],
221
+ approval="auto",
222
+ help="POST /supplier/tasks/{complete|dismiss} — close one of the tasks from `supplier list task`.",
223
+ )
224
+ def update_task(
225
+ task_id: str = typer.Argument(..., help="Task id, the '<type>#<key>' from list task."),
226
+ complete: bool = typer.Option(False, "--complete", help="Mark it done."),
227
+ dismiss: bool = typer.Option(False, "--dismiss", help="Hide it as not applicable."),
228
+ ) -> None:
229
+ """Complete or dismiss a task."""
230
+ if complete == dismiss:
231
+ raise typer.BadParameter("pass exactly one of --complete / --dismiss")
232
+ verb = "complete" if complete else "dismiss"
233
+ print_json_or_exit(console, lambda: api_client.supplier_task_action(verb, task_id))
234
+
235
+ @command(
236
+ app,
237
+ "supplier",
238
+ "update",
239
+ "action",
240
+ api="POST /supplier/actions/{action_id}/{acknowledge|snooze}",
241
+ side_effects=["changes what your pending-actions list shows"],
242
+ approval="auto",
243
+ help=(
244
+ "POST /supplier/actions/{id}/{acknowledge|snooze} — respond to a pending action. "
245
+ "Acknowledging says you have seen it; the action still closes on the fact it asks for."
246
+ ),
247
+ )
248
+ def update_action(
249
+ action_id: str = typer.Argument(..., help="Action id from list action."),
250
+ acknowledge: bool = typer.Option(False, "--acknowledge", help="Mark it seen."),
251
+ snooze: int | None = typer.Option(
252
+ None, "--snooze", metavar="DAYS", help="Hide it for this many days."
253
+ ),
254
+ ) -> None:
255
+ """Acknowledge or snooze a pending action."""
256
+ if acknowledge == (snooze is not None):
257
+ raise typer.BadParameter("pass exactly one of --acknowledge / --snooze DAYS")
258
+ state = "acknowledge" if acknowledge else "snooze"
259
+ print_json_or_exit(
260
+ console, lambda: api_client.supplier_action_state(action_id, state, days=snooze)
261
+ )
262
+
263
+ @command(
264
+ app,
265
+ "supplier",
266
+ "list",
267
+ "task",
268
+ api="GET /supplier/tasks",
269
+ help="GET /supplier/tasks — your onboarding and per-order to-dos.",
270
+ )
271
+ def list_tasks() -> None:
272
+ """List the factory's tasks."""
273
+ print_json_or_exit(console, lambda: api_client.supplier_list_tasks())
274
+
275
+ @command(
276
+ app,
277
+ "supplier",
278
+ "update",
279
+ "quote",
280
+ api="POST /supplier/quote_requests/{quote_request_id}/links/{link_id}/edit",
281
+ side_effects=["changes the price the brand sees", "notifies the brand of the revision"],
282
+ approval="ask",
283
+ help=(
284
+ "POST /supplier/quote_requests/{id}/links/{link_id}/edit — revise a quote you "
285
+ "already sent. --file is the same payload shape as submit; --dry-run first."
286
+ ),
287
+ )
288
+ def update_quote(
289
+ quote_request_id: str = typer.Argument(..., help="The quote request."),
290
+ link_id: str = typer.Argument(..., help="The quote (supplier link) to revise."),
291
+ file: Path = typer.Option(
292
+ ..., "--file", "-f", exists=True, dir_okay=False, help="JSON file with the revision."
293
+ ),
294
+ ) -> None:
295
+ """Revise a submitted quote."""
296
+ body = _load_json_object(file)
297
+ print_json_or_exit(
298
+ console, lambda: api_client.supplier_edit_quote(quote_request_id, link_id, body)
299
+ )
300
+
301
+ @command(
302
+ app,
303
+ "supplier",
304
+ "update",
305
+ "rfq",
306
+ api="POST /supplier/quote_requests/{quote_request_id}/{status|archive|accept_terms}",
307
+ side_effects=[
308
+ "the brand sees whether you took the job",
309
+ "archiving hides it from your board",
310
+ ],
311
+ approval="ask",
312
+ help=(
313
+ "POST /supplier/quote_requests/{id}/{status|archive|accept_terms} — answer an RFQ. "
314
+ "--status accepts or declines the invitation; --archive takes it off your board; "
315
+ "--accept-terms accepts the named agreements (repeat the flag)."
316
+ ),
317
+ )
318
+ def update_rfq(
319
+ quote_request_id: str = typer.Argument(..., help="The quote request."),
320
+ status: str | None = typer.Option(None, "--status", help="New status, e.g. ACCEPTED."),
321
+ archive: bool = typer.Option(False, "--archive", help="Take it off your board."),
322
+ accept_terms: list[str] = typer.Option(
323
+ [], "--accept-terms", help="Agreement key to accept (repeat per agreement)."
324
+ ),
325
+ ) -> None:
326
+ """Answer, archive, or accept the terms of an RFQ."""
327
+ chosen = [bool(status), archive, bool(accept_terms)]
328
+ if sum(chosen) != 1:
329
+ raise typer.BadParameter("pass exactly one of --status / --archive / --accept-terms")
330
+ if status:
331
+ run = lambda: api_client.supplier_set_rfq_status(quote_request_id, status) # noqa: E731
332
+ elif archive:
333
+ run = lambda: api_client.supplier_archive_rfq(quote_request_id) # noqa: E731
334
+ else:
335
+ run = lambda: api_client.supplier_accept_terms(quote_request_id, list(accept_terms)) # noqa: E731
336
+ print_json_or_exit(console, run)
337
+
195
338
  @command(
196
339
  app,
197
340
  "supplier",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "makea-cli"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Makea admin CLI (browser login + Makea admin HTTP API)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"