makea-cli 0.2.15__tar.gz → 0.2.16__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.
- {makea_cli-0.2.15 → makea_cli-0.2.16}/CHANGELOG.md +22 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/PKG-INFO +6 -1
- {makea_cli-0.2.15 → makea_cli-0.2.16}/README.md +5 -0
- makea_cli-0.2.16/makea_cli/__init__.py +1 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/api_client.py +54 -3
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/__init__.py +2 -0
- makea_cli-0.2.16/makea_cli/commands/playbook/__init__.py +0 -0
- makea_cli-0.2.16/makea_cli/commands/playbook/cmd.py +329 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/pyproject.toml +1 -1
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/golden_manifest.json +437 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_persona_commands.py +78 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/uv.lock +1 -1
- makea_cli-0.2.15/makea_cli/__init__.py +0 -1
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/link-supplier-user/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/.gitignore +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea-collection-upload/SKILL.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea-collection-upload/scripts/create_products.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/__main__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/auth_pkce.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/broker.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/_util.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/analytics/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/analytics/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/auth/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/auth/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/brand/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/brand/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/designer/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/designer/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/directory/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/directory/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/document/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/document/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/email/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/email/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/factory/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/factory/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/library/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/library/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/metrics/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/metrics/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/orders/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/orders/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/product/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/product/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/production/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/production/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/production/pricing_spec.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/supplier/__init__.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/commands/supplier/cmd.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/config.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/errors.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/identity.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/main.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/output.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/posthog_client.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/makea_cli/registry.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/npm/makea-cli/README.md +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/npm/makea-cli/makea-cli.mjs +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/npm/makea-cli/package.json +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_broker.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_currency_conversion.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_output_contract.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_pm_reads.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_registry.py +0 -0
- {makea_cli-0.2.15 → makea_cli-0.2.16}/tests/test_supplier_scorecard.py +0 -0
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.16
|
|
4
|
+
|
|
5
|
+
New: an admin can read, add, edit and remove the AI playbook rules of one
|
|
6
|
+
person (backend 2026-10-18, deploy e408a077 or later).
|
|
7
|
+
|
|
8
|
+
- `admin list rule --user <user_id|email> [--status …] [--all]`: every rule
|
|
9
|
+
that holds for the person, from their team, their own account and, for a
|
|
10
|
+
factory, its company. Archived rules only with `--all`.
|
|
11
|
+
- `admin get rule <rule_id> --user …`: one rule with its evidence.
|
|
12
|
+
- `admin create rule --user … --text … --reason …`: admin-authored, active
|
|
13
|
+
unless `--status`. Goes to the team when there is one (`--scope` to pick).
|
|
14
|
+
A repeat or a conflict is refused unless `--force`.
|
|
15
|
+
- `admin update rule <rule_id> --user … --reason …`: text, status,
|
|
16
|
+
applies_to, which products (`--when`) or mode.
|
|
17
|
+
- `admin delete rule <rule_id> --user … --reason …`: archives the rule, the
|
|
18
|
+
way the brand's own dismiss does. It leaves the brand's Rules page and every
|
|
19
|
+
prompt, and Beeboo will not propose the same words again. `update rule
|
|
20
|
+
--status active` brings it back.
|
|
21
|
+
|
|
22
|
+
Every write needs `--reason`; the rule records who changed it, why and what
|
|
23
|
+
it was before. An email on two accounts is refused with both user ids.
|
|
24
|
+
|
|
3
25
|
## 0.2.15
|
|
4
26
|
|
|
5
27
|
**Behaviour change: `admin get currency-conversion` returns different
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: makea-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.16
|
|
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
|
|
@@ -92,9 +92,11 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
92
92
|
| `makea-cli admin backfill supplier-id` | write(never) | `POST /admin/supplier/backfill_supplier_id_with_quote_requests` | Migrate supplier_id from a wrong UUID to the Cognito user sub (user_id): profile + Cognito supplierReferenceId, optio… | `backfill-supplier-id` |
|
|
93
93
|
| `makea-cli admin create library-element` | write(auto) | `POST /admin/designer/{user_id}/library_components` | save a reusable component under a designer. Provide --section and --values (see admin get library-schema). | `library-create` |
|
|
94
94
|
| `makea-cli admin create quote-request` | write(ask) | `POST /admin/supplier/create_quote_request` | create/send a quote request to a supplier for a product (admin). | `create-quote-request` |
|
|
95
|
+
| `makea-cli admin create rule` | write(ask) | `POST /admin/playbook/users/{user}/rules` | add a rule to one person's AI playbook (user_id or email); admin-authored, active unless --status. Refuses a repeat or a conflict unless --force. --reason required. | — |
|
|
95
96
|
| `makea-cli admin create supplier` | write(ask) | `POST /admin/supplier/add_supplier` | create supplier from JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the field taxonomy (… | `add-supplier` |
|
|
96
97
|
| `makea-cli admin create user` | write(never) | `POST /admin/users/create` | register a user and confirm the Cognito account in one step (no verification email). Allowed --user-type values: "Fas… | `create-user` |
|
|
97
98
|
| `makea-cli admin delete library-element` | write(never) | `DELETE /admin/designer/{user_id}/library_components/{component_id}` | remove a component from the designer's org library. | `library-delete` |
|
|
99
|
+
| `makea-cli admin delete rule` | write(ask) | `DELETE /admin/playbook/users/{user}/rules/{rule_id}` | remove a rule: archived, with who and why kept on it (`update rule --status active` restores it). --reason required. | — |
|
|
98
100
|
| `makea-cli admin download document` | read | `GET /admin/document/download` | save a document file (document id + designer user_id). | `download-document` |
|
|
99
101
|
| `makea-cli admin download product-document` | read | `GET /admin/document/download` | Resolve product via admin list product-order payload, then GET /admin/document/download. | `download-product-document` |
|
|
100
102
|
| `makea-cli admin generate pricing-groups` | write(ask) | `POST /admin/production_orders/{id}/details/update` | Lay a production order's variants into pricing groups along one axis (--by style \| colour \| variant \| all) with th… | `production-groups-generate` |
|
|
@@ -106,6 +108,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
106
108
|
| `makea-cli admin get production-pricing` | read | `GET /admin/production_orders/{id}/details` | show a production order's price tiers, MOQ, sizes/colours, pricing groups and cart coverage. | `production-pricing-show` |
|
|
107
109
|
| `makea-cli admin get quote-request` | read | `GET /admin/quote_requests/{quote_request_id}` | one quote request in full. | — |
|
|
108
110
|
| `makea-cli admin get revenue-metrics` | read | `GET /admin/business-metrics/cumulative-payment-and-platform-revenue` | platform-wide payment received and platform revenue (service fee + transaction fee) bucketed over a date range. | `get-cumulative-payment-and-platform-revenue` |
|
|
111
|
+
| `makea-cli admin get rule` | read | `GET /admin/playbook/users/{user}/rules/{rule_id}` | one rule in full, with its evidence: where it came from, who changed it and why. | — |
|
|
109
112
|
| `makea-cli admin get supplier-profile-schema` | read | `GET /admin/supplier/profile/schema` | supplier profile v2 schema (section/field taxonomy: sections, fields, options, required rules). | `get-supplier-profile-schema` |
|
|
110
113
|
| `makea-cli admin get tech-spec-extraction` | read | `GET /admin/products/{id}/extract-tech-specifications` | AI-generate specs (not saved). | `extract-ai-tech-spec` |
|
|
111
114
|
| `makea-cli admin get top-clicks` | read | `POST /admin/analytics/query` | which buttons/elements get clicked most. | `analytics-top-clicks` |
|
|
@@ -116,6 +119,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
116
119
|
| `makea-cli admin list production-order` | read | `GET /admin/production_orders?type=PRODUCTION` | 大货 (production) orders only. For restock use admin list restock-order; for shipping use admin list shipping-order. | `list-all-production-orders` |
|
|
117
120
|
| `makea-cli admin list quote-request` | read | `GET /admin/quote_requests` | quote requests, optionally for one product (--product) or one supplier (--supplier, GET /admin/quote_requests/supplie… | — |
|
|
118
121
|
| `makea-cli admin list restock-order` | read | `GET /admin/production_orders?type=RESTOCK` | restock orders only. Restock shares the production_orders table; this filters to type=RESTOCK. | `list-all-restock-orders` |
|
|
122
|
+
| `makea-cli admin list rule` | read | `GET /admin/playbook/users/{user}/rules` | every AI playbook rule that holds for one person (team, own account, factory): status, scope, text, which products, effect, source. Archived ones only with --all. | — |
|
|
119
123
|
| `makea-cli admin list sampling-order` | read | `GET /admin/sampling_orders/get_all` | paginated list of all sampling orders (admin). | `list-all-sampling-orders` |
|
|
120
124
|
| `makea-cli admin list shipping-order` | read | `GET /admin/shipping_orders` | paginated list of all shipping orders (admin). | `list-all-shipping-orders` |
|
|
121
125
|
| `makea-cli admin list supplier` | read | `GET /admin/supplier/get_all_suppliers` | list all suppliers (admin). | `list-suppliers` |
|
|
@@ -129,6 +133,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
129
133
|
| `makea-cli admin update library-element` | write(auto) | `PATCH /admin/designer/{user_id}/library_components/{component_id}` | update a component's name and/or values (empty value clears a field). | `library-update` |
|
|
130
134
|
| `makea-cli admin update quote` | write(ask) | `POST /admin/product/edit_supplier_link_override_info` | admin updates an existing quote (supplier link) in place. Only the options you pass are sent, and the backend leaves … | `update-supplier-quote` |
|
|
131
135
|
| `makea-cli admin update quote-request` | write(ask) | `PATCH /admin/quote_requests/{quote_request_id}/update` | set a quote request's stage, status and/or due date. Only what you pass is changed. This is the door that unblocks a … | `update-quote-request` |
|
|
136
|
+
| `makea-cli admin update rule` | write(ask) | `PATCH /admin/playbook/users/{user}/rules/{rule_id}` | change a rule's text, status, applies_to, when or mode. --reason required; the rule keeps what it was before. | — |
|
|
132
137
|
| `makea-cli admin update supplier` | write(ask) | `PUT /admin/supplier/update_supplier` | update an existing supplier from a JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the fi… | `update-supplier` |
|
|
133
138
|
| `makea-cli admin update tech-spec` | write(ask) | `POST /admin/products/{id}/technical-specifications` | persist specs from a JSON file. | `save-product-tech-spec` |
|
|
134
139
|
| `makea-cli admin update tracking-status` | write(ask) | `POST /admin/tracking/carrier_status` | apply a carrier status looked up by hand (tracking-sweep skill) to one order: status, fan-out, delivered emails (--silent: state only). | — |
|
|
@@ -80,9 +80,11 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
80
80
|
| `makea-cli admin backfill supplier-id` | write(never) | `POST /admin/supplier/backfill_supplier_id_with_quote_requests` | Migrate supplier_id from a wrong UUID to the Cognito user sub (user_id): profile + Cognito supplierReferenceId, optio… | `backfill-supplier-id` |
|
|
81
81
|
| `makea-cli admin create library-element` | write(auto) | `POST /admin/designer/{user_id}/library_components` | save a reusable component under a designer. Provide --section and --values (see admin get library-schema). | `library-create` |
|
|
82
82
|
| `makea-cli admin create quote-request` | write(ask) | `POST /admin/supplier/create_quote_request` | create/send a quote request to a supplier for a product (admin). | `create-quote-request` |
|
|
83
|
+
| `makea-cli admin create rule` | write(ask) | `POST /admin/playbook/users/{user}/rules` | add a rule to one person's AI playbook (user_id or email); admin-authored, active unless --status. Refuses a repeat or a conflict unless --force. --reason required. | — |
|
|
83
84
|
| `makea-cli admin create supplier` | write(ask) | `POST /admin/supplier/add_supplier` | create supplier from JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the field taxonomy (… | `add-supplier` |
|
|
84
85
|
| `makea-cli admin create user` | write(never) | `POST /admin/users/create` | register a user and confirm the Cognito account in one step (no verification email). Allowed --user-type values: "Fas… | `create-user` |
|
|
85
86
|
| `makea-cli admin delete library-element` | write(never) | `DELETE /admin/designer/{user_id}/library_components/{component_id}` | remove a component from the designer's org library. | `library-delete` |
|
|
87
|
+
| `makea-cli admin delete rule` | write(ask) | `DELETE /admin/playbook/users/{user}/rules/{rule_id}` | remove a rule: archived, with who and why kept on it (`update rule --status active` restores it). --reason required. | — |
|
|
86
88
|
| `makea-cli admin download document` | read | `GET /admin/document/download` | save a document file (document id + designer user_id). | `download-document` |
|
|
87
89
|
| `makea-cli admin download product-document` | read | `GET /admin/document/download` | Resolve product via admin list product-order payload, then GET /admin/document/download. | `download-product-document` |
|
|
88
90
|
| `makea-cli admin generate pricing-groups` | write(ask) | `POST /admin/production_orders/{id}/details/update` | Lay a production order's variants into pricing groups along one axis (--by style \| colour \| variant \| all) with th… | `production-groups-generate` |
|
|
@@ -94,6 +96,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
94
96
|
| `makea-cli admin get production-pricing` | read | `GET /admin/production_orders/{id}/details` | show a production order's price tiers, MOQ, sizes/colours, pricing groups and cart coverage. | `production-pricing-show` |
|
|
95
97
|
| `makea-cli admin get quote-request` | read | `GET /admin/quote_requests/{quote_request_id}` | one quote request in full. | — |
|
|
96
98
|
| `makea-cli admin get revenue-metrics` | read | `GET /admin/business-metrics/cumulative-payment-and-platform-revenue` | platform-wide payment received and platform revenue (service fee + transaction fee) bucketed over a date range. | `get-cumulative-payment-and-platform-revenue` |
|
|
99
|
+
| `makea-cli admin get rule` | read | `GET /admin/playbook/users/{user}/rules/{rule_id}` | one rule in full, with its evidence: where it came from, who changed it and why. | — |
|
|
97
100
|
| `makea-cli admin get supplier-profile-schema` | read | `GET /admin/supplier/profile/schema` | supplier profile v2 schema (section/field taxonomy: sections, fields, options, required rules). | `get-supplier-profile-schema` |
|
|
98
101
|
| `makea-cli admin get tech-spec-extraction` | read | `GET /admin/products/{id}/extract-tech-specifications` | AI-generate specs (not saved). | `extract-ai-tech-spec` |
|
|
99
102
|
| `makea-cli admin get top-clicks` | read | `POST /admin/analytics/query` | which buttons/elements get clicked most. | `analytics-top-clicks` |
|
|
@@ -104,6 +107,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
104
107
|
| `makea-cli admin list production-order` | read | `GET /admin/production_orders?type=PRODUCTION` | 大货 (production) orders only. For restock use admin list restock-order; for shipping use admin list shipping-order. | `list-all-production-orders` |
|
|
105
108
|
| `makea-cli admin list quote-request` | read | `GET /admin/quote_requests` | quote requests, optionally for one product (--product) or one supplier (--supplier, GET /admin/quote_requests/supplie… | — |
|
|
106
109
|
| `makea-cli admin list restock-order` | read | `GET /admin/production_orders?type=RESTOCK` | restock orders only. Restock shares the production_orders table; this filters to type=RESTOCK. | `list-all-restock-orders` |
|
|
110
|
+
| `makea-cli admin list rule` | read | `GET /admin/playbook/users/{user}/rules` | every AI playbook rule that holds for one person (team, own account, factory): status, scope, text, which products, effect, source. Archived ones only with --all. | — |
|
|
107
111
|
| `makea-cli admin list sampling-order` | read | `GET /admin/sampling_orders/get_all` | paginated list of all sampling orders (admin). | `list-all-sampling-orders` |
|
|
108
112
|
| `makea-cli admin list shipping-order` | read | `GET /admin/shipping_orders` | paginated list of all shipping orders (admin). | `list-all-shipping-orders` |
|
|
109
113
|
| `makea-cli admin list supplier` | read | `GET /admin/supplier/get_all_suppliers` | list all suppliers (admin). | `list-suppliers` |
|
|
@@ -117,6 +121,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
117
121
|
| `makea-cli admin update library-element` | write(auto) | `PATCH /admin/designer/{user_id}/library_components/{component_id}` | update a component's name and/or values (empty value clears a field). | `library-update` |
|
|
118
122
|
| `makea-cli admin update quote` | write(ask) | `POST /admin/product/edit_supplier_link_override_info` | admin updates an existing quote (supplier link) in place. Only the options you pass are sent, and the backend leaves … | `update-supplier-quote` |
|
|
119
123
|
| `makea-cli admin update quote-request` | write(ask) | `PATCH /admin/quote_requests/{quote_request_id}/update` | set a quote request's stage, status and/or due date. Only what you pass is changed. This is the door that unblocks a … | `update-quote-request` |
|
|
124
|
+
| `makea-cli admin update rule` | write(ask) | `PATCH /admin/playbook/users/{user}/rules/{rule_id}` | change a rule's text, status, applies_to, when or mode. --reason required; the rule keeps what it was before. | — |
|
|
120
125
|
| `makea-cli admin update supplier` | write(ask) | `PUT /admin/supplier/update_supplier` | update an existing supplier from a JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the fi… | `update-supplier` |
|
|
121
126
|
| `makea-cli admin update tech-spec` | write(ask) | `POST /admin/products/{id}/technical-specifications` | persist specs from a JSON file. | `save-product-tech-spec` |
|
|
122
127
|
| `makea-cli admin update tracking-status` | write(ask) | `POST /admin/tracking/carrier_status` | apply a carrier status looked up by hand (tracking-sweep skill) to one order: status, fan-out, delivered emails (--silent: state only). | — |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.16"
|
|
@@ -5,7 +5,7 @@ import mimetypes
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from pathlib import PurePosixPath
|
|
7
7
|
from typing import Any
|
|
8
|
-
from urllib.parse import unquote
|
|
8
|
+
from urllib.parse import quote, unquote
|
|
9
9
|
|
|
10
10
|
import httpx
|
|
11
11
|
|
|
@@ -197,8 +197,14 @@ def patch_json(path: str, body: Any, *, timeout: float = 60.0) -> Any:
|
|
|
197
197
|
return _request("PATCH", path, json_body=body, timeout=timeout)
|
|
198
198
|
|
|
199
199
|
|
|
200
|
-
def delete_json(
|
|
201
|
-
|
|
200
|
+
def delete_json(
|
|
201
|
+
path: str,
|
|
202
|
+
*,
|
|
203
|
+
params: dict[str, Any] | None = None,
|
|
204
|
+
body: Any = None,
|
|
205
|
+
timeout: float = 60.0,
|
|
206
|
+
) -> Any:
|
|
207
|
+
return _request("DELETE", path, params=params, json_body=body, timeout=timeout)
|
|
202
208
|
|
|
203
209
|
|
|
204
210
|
def send_admin_raw_email(body: dict[str, Any]) -> Any:
|
|
@@ -1047,6 +1053,51 @@ def admin_delete_library_component(
|
|
|
1047
1053
|
)
|
|
1048
1054
|
|
|
1049
1055
|
|
|
1056
|
+
# --- admin: one person's AI playbook rules ---------------------------------------
|
|
1057
|
+
# Addressed by user_id or email; the backend resolves every scope the person's
|
|
1058
|
+
# rules live in (team, own account, factory) and refuses an email that is on
|
|
1059
|
+
# more than one account. Every write carries a reason the rule keeps.
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
def _user_rules_path(user: str, rule_id: str | None = None) -> str:
|
|
1063
|
+
path = f"/admin/playbook/users/{quote(user.strip(), safe='@')}/rules"
|
|
1064
|
+
return f"{path}/{quote(rule_id, safe='')}" if rule_id else path
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
def admin_list_user_rules(
|
|
1068
|
+
user: str, *, status: str | None = None, include_archived: bool = False
|
|
1069
|
+
) -> Any:
|
|
1070
|
+
"""GET /admin/playbook/users/{user}/rules — every rule that holds for one person."""
|
|
1071
|
+
params: dict[str, Any] = {}
|
|
1072
|
+
if status:
|
|
1073
|
+
params["status"] = status
|
|
1074
|
+
if include_archived:
|
|
1075
|
+
params["include_archived"] = "1"
|
|
1076
|
+
return get_json(_user_rules_path(user), params=params or None)
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def admin_get_user_rule(user: str, rule_id: str) -> Any:
|
|
1080
|
+
"""GET /admin/playbook/users/{user}/rules/{rule_id} — one rule, evidence included."""
|
|
1081
|
+
return get_json(_user_rules_path(user, rule_id))
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def admin_create_user_rule(user: str, body: dict[str, Any]) -> Any:
|
|
1085
|
+
"""POST /admin/playbook/users/{user}/rules — {text, reason, scope?, applies_to?,
|
|
1086
|
+
when?, mode?, category?, polarity?, status?, force?}."""
|
|
1087
|
+
return post_json(_user_rules_path(user), body)
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
def admin_update_user_rule(user: str, rule_id: str, body: dict[str, Any]) -> Any:
|
|
1091
|
+
"""PATCH /admin/playbook/users/{user}/rules/{rule_id} — {text?, status?,
|
|
1092
|
+
applies_to?, when?, mode?, reason}."""
|
|
1093
|
+
return patch_json(_user_rules_path(user, rule_id), body)
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def admin_delete_user_rule(user: str, rule_id: str, reason: str) -> Any:
|
|
1097
|
+
"""DELETE /admin/playbook/users/{user}/rules/{rule_id} — archives it, with the reason."""
|
|
1098
|
+
return delete_json(_user_rules_path(user, rule_id), body={"reason": reason})
|
|
1099
|
+
|
|
1100
|
+
|
|
1050
1101
|
# --- supplier persona: a factory acting on its own RFQs, quotes and orders --------
|
|
1051
1102
|
|
|
1052
1103
|
|
|
@@ -13,6 +13,7 @@ from makea_cli.commands.factory.cmd import register as register_factory
|
|
|
13
13
|
from makea_cli.commands.library.cmd import register as register_library
|
|
14
14
|
from makea_cli.commands.metrics.cmd import register as register_metrics
|
|
15
15
|
from makea_cli.commands.orders.cmd import register as register_orders
|
|
16
|
+
from makea_cli.commands.playbook.cmd import register as register_playbook
|
|
16
17
|
from makea_cli.commands.product.cmd import register as register_product
|
|
17
18
|
from makea_cli.commands.production.cmd import register as register_production
|
|
18
19
|
from makea_cli.commands.supplier.cmd import register as register_supplier
|
|
@@ -33,3 +34,4 @@ def register_all(app: typer.Typer) -> None:
|
|
|
33
34
|
register_library(app)
|
|
34
35
|
register_factory(app)
|
|
35
36
|
register_brand(app)
|
|
37
|
+
register_playbook(app)
|
|
File without changes
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""Admin commands for one person's AI playbook rules (Knowledge > Rules).
|
|
2
|
+
|
|
3
|
+
A *rule* is one line Beeboo follows for an account ("Always book DHL Express",
|
|
4
|
+
"Approve PP samples from photos"). Rules live on the brand's team playbook, on
|
|
5
|
+
the person's own account, and, for a factory, on its company playbook. These
|
|
6
|
+
commands address the person (``--user`` takes a user_id or an email) and the
|
|
7
|
+
backend finds every scope; an email on more than one account is refused with
|
|
8
|
+
the candidates, so pass the user_id then.
|
|
9
|
+
|
|
10
|
+
Every write needs ``--reason``. The rule keeps it as an evidence entry
|
|
11
|
+
(``admin_add`` / ``admin_edit`` / ``admin_delete`` with who, why, when and,
|
|
12
|
+
for edits and deletes, what the rule was before), and the audit log records it.
|
|
13
|
+
``delete`` archives: the rule leaves the brand's Rules page and every prompt,
|
|
14
|
+
the row stays as the record, and ``update --status active`` brings it back.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
import typer
|
|
23
|
+
from rich.console import Console
|
|
24
|
+
|
|
25
|
+
from makea_cli import api_client
|
|
26
|
+
from makea_cli.commands._util import print_json_or_exit
|
|
27
|
+
from makea_cli.registry import command
|
|
28
|
+
|
|
29
|
+
console = Console()
|
|
30
|
+
|
|
31
|
+
_STATUSES = ("pending", "active", "muted", "archived")
|
|
32
|
+
_SCOPES = ("org", "user", "supplier")
|
|
33
|
+
_APPLIES_TO_KINDS = ("style", "collection", "supplier", "element")
|
|
34
|
+
_MODES = ("remind", "act")
|
|
35
|
+
|
|
36
|
+
_RULES_EPILOG = """
|
|
37
|
+
--user takes a user_id (Cognito sub) or an email.
|
|
38
|
+
|
|
39
|
+
Scopes: ORG#… is the brand's team playbook, USER#… the person's own,
|
|
40
|
+
SUPPLIER#… a factory's. A new rule goes to the factory playbook for a supplier,
|
|
41
|
+
else the team's (the person's own when there is no team); --scope overrides.
|
|
42
|
+
|
|
43
|
+
--applies-to narrows a rule to one thing: KIND:ID[:LABEL] with KIND one of
|
|
44
|
+
style, collection, supplier, element (e.g. supplier:sup_123:Ningbo Knit).
|
|
45
|
+
"all" (update only) widens it back to the whole account.
|
|
46
|
+
|
|
47
|
+
--when is which products it is for, as JSON:
|
|
48
|
+
'{"categories": ["knitwear"], "service_modes": ["cmt"]}'
|
|
49
|
+
Order conditions the rule already has (units, destination) are kept; {} means
|
|
50
|
+
every product.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
makea-cli admin list rule --user brand@example.com
|
|
54
|
+
makea-cli admin get rule rul_0123456789ab --user brand@example.com
|
|
55
|
+
makea-cli admin create rule --user brand@example.com \\
|
|
56
|
+
--text "Always book DHL Express for samples" --reason "Agreed on the 14 Oct call"
|
|
57
|
+
makea-cli admin update rule rul_0123456789ab --user brand@example.com \\
|
|
58
|
+
--status muted --reason "Brand asked to pause it by email"
|
|
59
|
+
makea-cli admin delete rule rul_0123456789ab --user brand@example.com \\
|
|
60
|
+
--reason "Learned from another brand's thread"
|
|
61
|
+
""".strip()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _user_option() -> Any:
|
|
65
|
+
return typer.Option(..., "--user", "-u", help="Target person: user_id or email.")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _reason_option() -> Any:
|
|
69
|
+
return typer.Option(
|
|
70
|
+
...,
|
|
71
|
+
"--reason",
|
|
72
|
+
"-r",
|
|
73
|
+
help="Why (required). Kept on the rule and in the audit log.",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _reason(raw: str) -> str:
|
|
78
|
+
reason = " ".join(raw.split())
|
|
79
|
+
if not reason:
|
|
80
|
+
raise typer.BadParameter("--reason must not be empty")
|
|
81
|
+
return reason
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _applies_to(raw: str) -> dict[str, str]:
|
|
85
|
+
if raw.strip().lower() == "all":
|
|
86
|
+
return {"kind": "all"}
|
|
87
|
+
kind, sep, rest = raw.partition(":")
|
|
88
|
+
item_id, _, label = rest.partition(":")
|
|
89
|
+
kind = kind.strip().lower()
|
|
90
|
+
if not sep or kind not in _APPLIES_TO_KINDS or not item_id.strip():
|
|
91
|
+
raise typer.BadParameter(
|
|
92
|
+
f"--applies-to: expected KIND:ID[:LABEL] with KIND one of {', '.join(_APPLIES_TO_KINDS)}"
|
|
93
|
+
)
|
|
94
|
+
out = {"kind": kind, "id": item_id.strip()}
|
|
95
|
+
if label.strip():
|
|
96
|
+
out["label"] = label.strip()
|
|
97
|
+
return out
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _when(raw: str) -> dict[str, Any]:
|
|
101
|
+
try:
|
|
102
|
+
data = json.loads(raw)
|
|
103
|
+
except json.JSONDecodeError as e:
|
|
104
|
+
raise typer.BadParameter(f"--when: invalid JSON ({e})") from e
|
|
105
|
+
if not isinstance(data, dict):
|
|
106
|
+
raise typer.BadParameter("--when: expected a JSON object")
|
|
107
|
+
return data
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _choice(value: str | None, allowed: tuple[str, ...], flag: str) -> str | None:
|
|
111
|
+
if value is None:
|
|
112
|
+
return None
|
|
113
|
+
value = value.strip().lower()
|
|
114
|
+
if value not in allowed:
|
|
115
|
+
raise typer.BadParameter(f"{flag}: one of {', '.join(allowed)}")
|
|
116
|
+
return value
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def register(app: typer.Typer) -> None:
|
|
120
|
+
@command(
|
|
121
|
+
app,
|
|
122
|
+
"admin",
|
|
123
|
+
"list",
|
|
124
|
+
"rule",
|
|
125
|
+
api="GET /admin/playbook/users/{user}/rules",
|
|
126
|
+
help=(
|
|
127
|
+
"GET /admin/playbook/users/{user}/rules — every AI playbook rule that holds "
|
|
128
|
+
"for one person (team, own account, factory): status, scope, text, "
|
|
129
|
+
"applies_to/when, mode/effect, source line, dates. Archived ones only with --all."
|
|
130
|
+
),
|
|
131
|
+
epilog=_RULES_EPILOG,
|
|
132
|
+
)
|
|
133
|
+
def list_rules(
|
|
134
|
+
user: str = _user_option(),
|
|
135
|
+
status: str | None = typer.Option(
|
|
136
|
+
None,
|
|
137
|
+
"--status",
|
|
138
|
+
"-s",
|
|
139
|
+
help=f"Only these statuses, comma-separated ({', '.join(_STATUSES)}).",
|
|
140
|
+
),
|
|
141
|
+
include_archived: bool = typer.Option(
|
|
142
|
+
False, "--all", help="Also list archived (deleted) rules."
|
|
143
|
+
),
|
|
144
|
+
) -> None:
|
|
145
|
+
"""List a person's playbook rules."""
|
|
146
|
+
if status:
|
|
147
|
+
for s in status.split(","):
|
|
148
|
+
_choice(s, _STATUSES, "--status")
|
|
149
|
+
print_json_or_exit(
|
|
150
|
+
console,
|
|
151
|
+
lambda: api_client.admin_list_user_rules(
|
|
152
|
+
user, status=status, include_archived=include_archived
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
@command(
|
|
157
|
+
app,
|
|
158
|
+
"admin",
|
|
159
|
+
"get",
|
|
160
|
+
"rule",
|
|
161
|
+
api="GET /admin/playbook/users/{user}/rules/{rule_id}",
|
|
162
|
+
help=(
|
|
163
|
+
"GET /admin/playbook/users/{user}/rules/{rule_id} — one rule in full, "
|
|
164
|
+
"evidence (where it came from, who changed it and why) included."
|
|
165
|
+
),
|
|
166
|
+
epilog=_RULES_EPILOG,
|
|
167
|
+
)
|
|
168
|
+
def get_rule(
|
|
169
|
+
rule_id: str = typer.Argument(..., help="Rule id (rul_…)."),
|
|
170
|
+
user: str = _user_option(),
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Show one playbook rule with its evidence."""
|
|
173
|
+
print_json_or_exit(console, lambda: api_client.admin_get_user_rule(user, rule_id))
|
|
174
|
+
|
|
175
|
+
@command(
|
|
176
|
+
app,
|
|
177
|
+
"admin",
|
|
178
|
+
"create",
|
|
179
|
+
"rule",
|
|
180
|
+
api="POST /admin/playbook/users/{user}/rules",
|
|
181
|
+
side_effects=[
|
|
182
|
+
"Beeboo follows the rule in this account's AI answers from now on",
|
|
183
|
+
"shows on the brand's Knowledge > Rules page (brand scopes)",
|
|
184
|
+
],
|
|
185
|
+
approval="ask",
|
|
186
|
+
reversible=True,
|
|
187
|
+
idempotent=False,
|
|
188
|
+
help=(
|
|
189
|
+
"POST /admin/playbook/users/{user}/rules — add a rule to a person's playbook "
|
|
190
|
+
"(admin-authored, active unless --status). Refuses a repeat or a conflict "
|
|
191
|
+
"unless --force. --reason required."
|
|
192
|
+
),
|
|
193
|
+
epilog=_RULES_EPILOG,
|
|
194
|
+
)
|
|
195
|
+
def create_rule(
|
|
196
|
+
user: str = _user_option(),
|
|
197
|
+
text: str = typer.Option(..., "--text", help="The rule, one line (max 160 chars)."),
|
|
198
|
+
reason: str = _reason_option(),
|
|
199
|
+
scope: str | None = typer.Option(
|
|
200
|
+
None, "--scope", help=f"Where it lives: {', '.join(_SCOPES)} (default: see below)."
|
|
201
|
+
),
|
|
202
|
+
applies_to: str | None = typer.Option(
|
|
203
|
+
None, "--applies-to", help="Narrow to one thing: KIND:ID[:LABEL]."
|
|
204
|
+
),
|
|
205
|
+
when: str | None = typer.Option(
|
|
206
|
+
None,
|
|
207
|
+
"--when",
|
|
208
|
+
help='Which products, JSON: {"categories": [...], "service_modes": [...]}.',
|
|
209
|
+
),
|
|
210
|
+
mode: str | None = typer.Option(
|
|
211
|
+
None, "--mode", help="remind (default): Beeboo tells; act: Beeboo does it."
|
|
212
|
+
),
|
|
213
|
+
category: str | None = typer.Option(
|
|
214
|
+
None,
|
|
215
|
+
"--category",
|
|
216
|
+
help="communication, spec_defaults, sourcing, commercial, logistics, process, "
|
|
217
|
+
"hard_constraint, … (default: classified from the text).",
|
|
218
|
+
),
|
|
219
|
+
polarity: str | None = typer.Option(
|
|
220
|
+
None, "--polarity", help="always, never or prefer (default: from the text)."
|
|
221
|
+
),
|
|
222
|
+
status: str | None = typer.Option(
|
|
223
|
+
None, "--status", help="Start as pending, active (default) or muted."
|
|
224
|
+
),
|
|
225
|
+
force: bool = typer.Option(
|
|
226
|
+
False, "--force", help="Add even if it repeats or disagrees with an existing rule."
|
|
227
|
+
),
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Add a playbook rule for a person."""
|
|
230
|
+
body: dict[str, Any] = {"text": text, "reason": _reason(reason)}
|
|
231
|
+
if scope is not None:
|
|
232
|
+
body["scope"] = _choice(scope, _SCOPES, "--scope")
|
|
233
|
+
if applies_to is not None:
|
|
234
|
+
body["applies_to"] = _applies_to(applies_to)
|
|
235
|
+
if body["applies_to"].get("kind") == "all":
|
|
236
|
+
body.pop("applies_to")
|
|
237
|
+
if when is not None:
|
|
238
|
+
body["when"] = _when(when)
|
|
239
|
+
if mode is not None:
|
|
240
|
+
body["mode"] = _choice(mode, _MODES, "--mode")
|
|
241
|
+
if category is not None:
|
|
242
|
+
body["category"] = category.strip().lower()
|
|
243
|
+
if polarity is not None:
|
|
244
|
+
body["polarity"] = _choice(polarity, ("always", "never", "prefer"), "--polarity")
|
|
245
|
+
if status is not None:
|
|
246
|
+
body["status"] = _choice(status, ("pending", "active", "muted"), "--status")
|
|
247
|
+
if force:
|
|
248
|
+
body["force"] = True
|
|
249
|
+
print_json_or_exit(console, lambda: api_client.admin_create_user_rule(user, body))
|
|
250
|
+
|
|
251
|
+
@command(
|
|
252
|
+
app,
|
|
253
|
+
"admin",
|
|
254
|
+
"update",
|
|
255
|
+
"rule",
|
|
256
|
+
api="PATCH /admin/playbook/users/{user}/rules/{rule_id}",
|
|
257
|
+
side_effects=[
|
|
258
|
+
"changes what Beeboo does for this account",
|
|
259
|
+
"a learned rule becomes authored and stops expiring",
|
|
260
|
+
],
|
|
261
|
+
approval="ask",
|
|
262
|
+
reversible=True,
|
|
263
|
+
idempotent=True,
|
|
264
|
+
help=(
|
|
265
|
+
"PATCH /admin/playbook/users/{user}/rules/{rule_id} — change a rule's text, "
|
|
266
|
+
"status, applies_to, when or mode. --reason required; the rule keeps what it was before."
|
|
267
|
+
),
|
|
268
|
+
epilog=_RULES_EPILOG,
|
|
269
|
+
)
|
|
270
|
+
def update_rule(
|
|
271
|
+
rule_id: str = typer.Argument(..., help="Rule id (rul_…)."),
|
|
272
|
+
user: str = _user_option(),
|
|
273
|
+
reason: str = _reason_option(),
|
|
274
|
+
text: str | None = typer.Option(None, "--text", help="New words."),
|
|
275
|
+
status: str | None = typer.Option(None, "--status", help=f"One of {', '.join(_STATUSES)}."),
|
|
276
|
+
applies_to: str | None = typer.Option(
|
|
277
|
+
None, "--applies-to", help='KIND:ID[:LABEL], or "all" for the whole account.'
|
|
278
|
+
),
|
|
279
|
+
when: str | None = typer.Option(
|
|
280
|
+
None, "--when", help="Which products, JSON ({} for every product)."
|
|
281
|
+
),
|
|
282
|
+
mode: str | None = typer.Option(None, "--mode", help="remind or act."),
|
|
283
|
+
) -> None:
|
|
284
|
+
"""Edit a person's playbook rule."""
|
|
285
|
+
body: dict[str, Any] = {}
|
|
286
|
+
if text is not None:
|
|
287
|
+
body["text"] = text
|
|
288
|
+
if status is not None:
|
|
289
|
+
body["status"] = _choice(status, _STATUSES, "--status")
|
|
290
|
+
if applies_to is not None:
|
|
291
|
+
body["applies_to"] = _applies_to(applies_to)
|
|
292
|
+
if when is not None:
|
|
293
|
+
body["when"] = _when(when)
|
|
294
|
+
if mode is not None:
|
|
295
|
+
body["mode"] = _choice(mode, _MODES, "--mode")
|
|
296
|
+
if not body:
|
|
297
|
+
raise typer.BadParameter(
|
|
298
|
+
"nothing to change: pass --text, --status, --applies-to, --when or --mode"
|
|
299
|
+
)
|
|
300
|
+
body["reason"] = _reason(reason)
|
|
301
|
+
print_json_or_exit(console, lambda: api_client.admin_update_user_rule(user, rule_id, body))
|
|
302
|
+
|
|
303
|
+
@command(
|
|
304
|
+
app,
|
|
305
|
+
"admin",
|
|
306
|
+
"delete",
|
|
307
|
+
"rule",
|
|
308
|
+
api="DELETE /admin/playbook/users/{user}/rules/{rule_id}",
|
|
309
|
+
side_effects=[
|
|
310
|
+
"archives the rule: off the brand's Rules page and out of every AI prompt",
|
|
311
|
+
"the learner will not propose the same words again",
|
|
312
|
+
],
|
|
313
|
+
approval="ask",
|
|
314
|
+
reversible=True,
|
|
315
|
+
idempotent=False,
|
|
316
|
+
help=(
|
|
317
|
+
"DELETE /admin/playbook/users/{user}/rules/{rule_id} — remove a rule (archived, "
|
|
318
|
+
"with who and why kept on it; update --status active restores it). --reason required."
|
|
319
|
+
),
|
|
320
|
+
epilog=_RULES_EPILOG,
|
|
321
|
+
)
|
|
322
|
+
def delete_rule(
|
|
323
|
+
rule_id: str = typer.Argument(..., help="Rule id (rul_…)."),
|
|
324
|
+
user: str = _user_option(),
|
|
325
|
+
reason: str = _reason_option(),
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Remove (archive) a person's playbook rule."""
|
|
328
|
+
why = _reason(reason)
|
|
329
|
+
print_json_or_exit(console, lambda: api_client.admin_delete_user_rule(user, rule_id, why))
|