makea-cli 0.2.27__tar.gz → 0.2.29__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 (86) hide show
  1. {makea_cli-0.2.27 → makea_cli-0.2.29}/CHANGELOG.md +35 -0
  2. {makea_cli-0.2.27 → makea_cli-0.2.29}/PKG-INFO +8 -1
  3. {makea_cli-0.2.27 → makea_cli-0.2.29}/README.md +3 -0
  4. makea_cli-0.2.29/makea_cli/__init__.py +1 -0
  5. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/__init__.py +2 -0
  6. makea_cli-0.2.29/makea_cli/commands/files/cmd.py +84 -0
  7. makea_cli-0.2.29/makea_cli/commands/tasks/__init__.py +0 -0
  8. makea_cli-0.2.29/makea_cli/file_reader.py +1347 -0
  9. {makea_cli-0.2.27 → makea_cli-0.2.29}/pyproject.toml +7 -1
  10. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/golden_manifest.json +129 -0
  11. makea_cli-0.2.29/tests/test_file_reader.py +273 -0
  12. makea_cli-0.2.29/uv.lock +471 -0
  13. makea_cli-0.2.27/makea_cli/__init__.py +0 -1
  14. makea_cli-0.2.27/uv.lock +0 -220
  15. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
  16. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
  17. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/link-supplier-user/SKILL.md +0 -0
  18. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
  19. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
  20. {makea_cli-0.2.27 → makea_cli-0.2.29}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
  21. {makea_cli-0.2.27 → makea_cli-0.2.29}/.gitignore +0 -0
  22. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea-collection-upload/SKILL.md +0 -0
  23. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea-collection-upload/scripts/create_products.py +0 -0
  24. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/__main__.py +0 -0
  25. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/api_client.py +0 -0
  26. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/auth_pkce.py +0 -0
  27. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/broker.py +0 -0
  28. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/_util.py +0 -0
  29. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/about/__init__.py +0 -0
  30. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/about/cmd.py +0 -0
  31. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/analytics/__init__.py +0 -0
  32. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/analytics/cmd.py +0 -0
  33. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/auth/__init__.py +0 -0
  34. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/auth/cmd.py +0 -0
  35. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/brand/__init__.py +0 -0
  36. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/brand/cmd.py +0 -0
  37. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/designer/__init__.py +0 -0
  38. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/designer/cmd.py +0 -0
  39. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/directory/__init__.py +0 -0
  40. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/directory/cmd.py +0 -0
  41. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/document/__init__.py +0 -0
  42. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/document/cmd.py +0 -0
  43. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/email/__init__.py +0 -0
  44. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/email/cmd.py +0 -0
  45. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/factory/__init__.py +0 -0
  46. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/factory/cmd.py +0 -0
  47. {makea_cli-0.2.27/makea_cli/commands/flags → makea_cli-0.2.29/makea_cli/commands/files}/__init__.py +0 -0
  48. {makea_cli-0.2.27/makea_cli/commands/library → makea_cli-0.2.29/makea_cli/commands/flags}/__init__.py +0 -0
  49. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/flags/cmd.py +0 -0
  50. {makea_cli-0.2.27/makea_cli/commands/metrics → makea_cli-0.2.29/makea_cli/commands/library}/__init__.py +0 -0
  51. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/library/cmd.py +0 -0
  52. {makea_cli-0.2.27/makea_cli/commands/playbook → makea_cli-0.2.29/makea_cli/commands/metrics}/__init__.py +0 -0
  53. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/metrics/cmd.py +0 -0
  54. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/orders/__init__.py +0 -0
  55. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/orders/cmd.py +0 -0
  56. {makea_cli-0.2.27/makea_cli/commands/shipping → makea_cli-0.2.29/makea_cli/commands/playbook}/__init__.py +0 -0
  57. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/playbook/cmd.py +0 -0
  58. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/product/__init__.py +0 -0
  59. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/product/cmd.py +0 -0
  60. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/production/__init__.py +0 -0
  61. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/production/cmd.py +0 -0
  62. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/production/pricing_spec.py +0 -0
  63. {makea_cli-0.2.27/makea_cli/commands/tasks → makea_cli-0.2.29/makea_cli/commands/shipping}/__init__.py +0 -0
  64. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/shipping/cmd.py +0 -0
  65. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/supplier/__init__.py +0 -0
  66. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/supplier/cmd.py +0 -0
  67. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/commands/tasks/cmd.py +0 -0
  68. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/companion.py +0 -0
  69. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/config.py +0 -0
  70. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/errors.py +0 -0
  71. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/identity.py +0 -0
  72. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/main.py +0 -0
  73. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/output.py +0 -0
  74. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/posthog_client.py +0 -0
  75. {makea_cli-0.2.27 → makea_cli-0.2.29}/makea_cli/registry.py +0 -0
  76. {makea_cli-0.2.27 → makea_cli-0.2.29}/npm/makea-cli/README.md +0 -0
  77. {makea_cli-0.2.27 → makea_cli-0.2.29}/npm/makea-cli/makea-cli.mjs +0 -0
  78. {makea_cli-0.2.27 → makea_cli-0.2.29}/npm/makea-cli/package.json +0 -0
  79. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_broker.py +0 -0
  80. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_companion.py +0 -0
  81. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_currency_conversion.py +0 -0
  82. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_output_contract.py +0 -0
  83. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_persona_commands.py +0 -0
  84. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_pm_reads.py +0 -0
  85. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_registry.py +0 -0
  86. {makea_cli-0.2.27 → makea_cli-0.2.29}/tests/test_supplier_scorecard.py +0 -0
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.29
4
+
5
+ The file reader stays inside a web worker's memory (TECH-145).
6
+
7
+ - It no longer raises Pillow's global decompression limit, and decodes no
8
+ image over 120 MP (the same bound as the backend); JPEGs decode at reduced
9
+ scale. A DXF sheet draws at most 40 pieces. Pictures shrink before they are
10
+ flattened, so a large PSD is not copied at full size several times.
11
+ - `python -m makea_cli.file_reader <name>` reads the file on stdin and prints
12
+ the reading as JSON; `read_in_child()` runs it that way, so a long-lived
13
+ worker gets its memory back when the child exits.
14
+
15
+ ## 0.2.28
16
+
17
+ Read the files brands send that no model reads (TECH-143).
18
+
19
+ `makea-cli designer get file <path>` (also `supplier` and `admin`) reads a file
20
+ on this machine and saves PNG pictures of what it draws next to it. No API call.
21
+
22
+ - Pattern DXF (AAMA/ASTM, Gerber, Optitex, Chinese CAD): every piece with its
23
+ sizes, cut quantity, size, seam allowance, notches and grain line, and a sheet
24
+ of the pieces. Chinese piece names come out right (GBK).
25
+ - RUL grade rules: sizes, sample size, units, the rules.
26
+ - Illustrator: as the PDF it holds, or, when it was saved without PDF content
27
+ (CorelDRAW's AI export), drawn from Illustrator's own data.
28
+ - EPS: header facts and its preview picture. Photoshop: size, layers, picture.
29
+ - Embroidery: PES, DST, EXP, JEF, VP3 and more (size, stitches, threads,
30
+ stitch-out); Wilcom EMB (stitches, threads, fabric, backing, preview).
31
+ - ZIP: every file inside, each read the same way. AVIF, TIFF, BMP: as pictures.
32
+ SVG: size, colours, text.
33
+ - Anything else says what it is and what to export instead.
34
+
35
+ The same reader (`makea_cli.file_reader`) is what the Makea backend uses to read
36
+ these uploads for the tech pack and to preview them.
37
+
3
38
  ## 0.2.27
4
39
 
5
40
  Notes and misfires on the Makea task queue.
@@ -1,11 +1,15 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: makea-cli
3
- Version: 0.2.27
3
+ Version: 0.2.29
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
7
7
  Requires-Dist: httpx>=0.27
8
+ Requires-Dist: olefile>=0.47
9
+ Requires-Dist: pillow>=11.3
8
10
  Requires-Dist: platformdirs>=4.2
11
+ Requires-Dist: pyembroidery>=1.5
12
+ Requires-Dist: pypdfium2>=4.30
9
13
  Requires-Dist: rich>=13.7
10
14
  Requires-Dist: typer<0.25,>=0.12
11
15
  Description-Content-Type: text/markdown
@@ -118,6 +122,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
118
122
  | `makea-cli admin get about` | read | `GET /admin/about/{target}` | one account's About you (`--user`: a supplier account's person gets their factory's page; `--supplier`): facts, removed, unknown, paused, topic keys. | — |
119
123
  | `makea-cli admin get active-users` | read | `POST /admin/analytics/query` | Distinct active users per day over the last N days (DAU series). | `analytics-active-users` |
120
124
  | `makea-cli admin get analytics-query` | read | `POST /admin/analytics/query` | Run an arbitrary HogQL query (escape hatch). Example: analytics-query "SELECT count() FROM events" | `analytics-query` |
125
+ | `makea-cli admin get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
121
126
  | `makea-cli admin get currency-conversion` | read | `GET /admin/fx/rates` | the platform's own exchange rates and rounding (rate table for several --from; one --from with --amount converts it). | `convert-currency` |
122
127
  | `makea-cli admin get feature-usage` | read | `POST /admin/analytics/query` | How many distinct users used a feature recently, plus event volume. Matches $pageview path and feature_time_spent eve… | `analytics-feature-usage` |
123
128
  | `makea-cli admin get growth-snapshot` | read | `GET /admin/growth/snapshots/{id}` | one reconciled Growth Analysis snapshot (default `latest`); `--block` prints one block (headline, gates, review_items, …). | — |
@@ -202,6 +207,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
202
207
  | `makea-cli designer delete sampling-order` | write(never) | `DELETE /designer/sampling_orders/{sampling_order_id}` | delete a DRAFT sample order. The backend refuses any other status. Graded `never`. | — |
203
208
  | `makea-cli designer delete shipping-order` | write(never) | `POST /designer/shipping_orders/{shipping_order_id}/cancel` | the endpoint is called cancel but it unlinks every production order and removes the shipment, so it is a delete here.… | — |
204
209
  | `makea-cli designer download document` | read | `GET /designer/document/download` | save bytes for user_id + document_id (designer-authenticated; same query contract as admin document download). | `designer-download-document` |
210
+ | `makea-cli designer get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
205
211
  | `makea-cli designer get collection` | read | `GET /designer/collections/{collection_id}/overview` | one collection with its products, orders and lifecycle in a single read. | — |
206
212
  | `makea-cli designer get lifecycle` | read | `GET /designer/product_orders/{product_order_id}/lifecycle` | which stage this product is in. | — |
207
213
  | `makea-cli designer get product` | read | `GET /designer/get_product` | one product: status, documents, prices, links. | — |
@@ -245,6 +251,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
245
251
  |---|---|---|---|---|
246
252
  | `makea-cli supplier create note` | write(ask) | `POST /supplier/products/{id}/channels/{key}/notes` | post a message into a product channel you are part of. | — |
247
253
  | `makea-cli supplier download document` | read | `GET /supplier/document/download` | save a document you are allowed to read. | — |
254
+ | `makea-cli supplier get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
248
255
  | `makea-cli supplier get pack` | read | `GET /supplier/quote_requests/{id}/technical-specifications` | the tech pack for an RFQ, as the factory is allowed to see it (`--readable`: labels, values and blank essentials only). | — |
249
256
  | `makea-cli supplier get pack-changes` | read | `GET /supplier/products/{product_id}/specification-change-history` | what changed in the pack since you quoted, field by field. | — |
250
257
  | `makea-cli supplier get profile` | read | `GET /supplier/profile` | your own supplier profile. | — |
@@ -106,6 +106,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
106
106
  | `makea-cli admin get about` | read | `GET /admin/about/{target}` | one account's About you (`--user`: a supplier account's person gets their factory's page; `--supplier`): facts, removed, unknown, paused, topic keys. | — |
107
107
  | `makea-cli admin get active-users` | read | `POST /admin/analytics/query` | Distinct active users per day over the last N days (DAU series). | `analytics-active-users` |
108
108
  | `makea-cli admin get analytics-query` | read | `POST /admin/analytics/query` | Run an arbitrary HogQL query (escape hatch). Example: analytics-query "SELECT count() FROM events" | `analytics-query` |
109
+ | `makea-cli admin get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
109
110
  | `makea-cli admin get currency-conversion` | read | `GET /admin/fx/rates` | the platform's own exchange rates and rounding (rate table for several --from; one --from with --amount converts it). | `convert-currency` |
110
111
  | `makea-cli admin get feature-usage` | read | `POST /admin/analytics/query` | How many distinct users used a feature recently, plus event volume. Matches $pageview path and feature_time_spent eve… | `analytics-feature-usage` |
111
112
  | `makea-cli admin get growth-snapshot` | read | `GET /admin/growth/snapshots/{id}` | one reconciled Growth Analysis snapshot (default `latest`); `--block` prints one block (headline, gates, review_items, …). | — |
@@ -190,6 +191,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
190
191
  | `makea-cli designer delete sampling-order` | write(never) | `DELETE /designer/sampling_orders/{sampling_order_id}` | delete a DRAFT sample order. The backend refuses any other status. Graded `never`. | — |
191
192
  | `makea-cli designer delete shipping-order` | write(never) | `POST /designer/shipping_orders/{shipping_order_id}/cancel` | the endpoint is called cancel but it unlinks every production order and removes the shipment, so it is a delete here.… | — |
192
193
  | `makea-cli designer download document` | read | `GET /designer/document/download` | save bytes for user_id + document_id (designer-authenticated; same query contract as admin document download). | `designer-download-document` |
194
+ | `makea-cli designer get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
193
195
  | `makea-cli designer get collection` | read | `GET /designer/collections/{collection_id}/overview` | one collection with its products, orders and lifecycle in a single read. | — |
194
196
  | `makea-cli designer get lifecycle` | read | `GET /designer/product_orders/{product_order_id}/lifecycle` | which stage this product is in. | — |
195
197
  | `makea-cli designer get product` | read | `GET /designer/get_product` | one product: status, documents, prices, links. | — |
@@ -233,6 +235,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
233
235
  |---|---|---|---|---|
234
236
  | `makea-cli supplier create note` | write(ask) | `POST /supplier/products/{id}/channels/{key}/notes` | post a message into a product channel you are part of. | — |
235
237
  | `makea-cli supplier download document` | read | `GET /supplier/document/download` | save a document you are allowed to read. | — |
238
+ | `makea-cli supplier get file` | read | local (makea_cli.file_reader) | read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or AVIF/TIFF file on this machine; saves PNG pictures of what it draws next to it. No API call. | — |
236
239
  | `makea-cli supplier get pack` | read | `GET /supplier/quote_requests/{id}/technical-specifications` | the tech pack for an RFQ, as the factory is allowed to see it (`--readable`: labels, values and blank essentials only). | — |
237
240
  | `makea-cli supplier get pack-changes` | read | `GET /supplier/products/{product_id}/specification-change-history` | what changed in the pack since you quoted, field by field. | — |
238
241
  | `makea-cli supplier get profile` | read | `GET /supplier/profile` | your own supplier profile. | — |
@@ -0,0 +1 @@
1
+ __version__ = "0.2.29"
@@ -11,6 +11,7 @@ from makea_cli.commands.directory.cmd import register as register_directory
11
11
  from makea_cli.commands.document.cmd import register as register_document
12
12
  from makea_cli.commands.email.cmd import register as register_email
13
13
  from makea_cli.commands.factory.cmd import register as register_factory
14
+ from makea_cli.commands.files.cmd import register as register_files
14
15
  from makea_cli.commands.flags.cmd import register as register_flags
15
16
  from makea_cli.commands.library.cmd import register as register_library
16
17
  from makea_cli.commands.metrics.cmd import register as register_metrics
@@ -38,6 +39,7 @@ def register_all(app: typer.Typer) -> None:
38
39
  register_email(app)
39
40
  register_library(app)
40
41
  register_factory(app)
42
+ register_files(app)
41
43
  register_brand(app)
42
44
  register_playbook(app)
43
45
  register_about(app)
@@ -0,0 +1,84 @@
1
+ """``makea-cli <persona> get file PATH``: read a file a brand sent, locally.
2
+
3
+ Pattern DXF/RUL, Illustrator, EPS, Photoshop, embroidery (PES/DST/EMB), ZIP,
4
+ AVIF/TIFF/SVG. No API call: the file never leaves the machine. Pictures of what
5
+ the file draws are saved next to it so the caller can look at them (an agent
6
+ reads them with its image tool).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from pathlib import Path
13
+
14
+ import typer
15
+ from rich.console import Console
16
+
17
+ from makea_cli.commands._util import print_json_or_exit
18
+ from makea_cli.file_reader import read_path
19
+ from makea_cli.registry import command
20
+
21
+ console = Console()
22
+
23
+ _HELP = (
24
+ "Read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or "
25
+ "AVIF/TIFF file on this machine: what it states plus PNG pictures of what it draws."
26
+ )
27
+
28
+
29
+ def _slug(text: str) -> str:
30
+ return re.sub(r"[^A-Za-z0-9]+", "-", text).strip("-").lower()[:40] or "picture"
31
+
32
+
33
+ def read_file_payload(path: Path, pictures_dir: Path | None) -> dict:
34
+ if not path.is_file():
35
+ raise FileNotFoundError(f"{path} is not a file")
36
+ reading = read_path(str(path))
37
+ out_dir = pictures_dir or path.parent
38
+ saved = []
39
+ pictures = reading.all_pictures()
40
+ if pictures:
41
+ out_dir.mkdir(parents=True, exist_ok=True)
42
+ for i, pic in enumerate(pictures, 1):
43
+ target = out_dir / f"{path.stem}.{i}-{_slug(pic.label)}.png"
44
+ target.write_bytes(pic.png)
45
+ saved.append({"label": pic.label, "path": str(target)})
46
+ payload = {
47
+ "file": str(path),
48
+ "kind": reading.kind,
49
+ "format": reading.format,
50
+ "readable": reading.readable,
51
+ "readout": reading.markdown(),
52
+ "pictures": saved,
53
+ }
54
+ if saved:
55
+ payload["next"] = (
56
+ "Look at each picture to read what it shows; the readout lists only stated facts."
57
+ )
58
+ return payload
59
+
60
+
61
+ def register(app: typer.Typer) -> None:
62
+ for persona in ("designer", "supplier", "admin"):
63
+ _register_for(app, persona)
64
+
65
+
66
+ def _register_for(app: typer.Typer, persona: str) -> None:
67
+ @command(
68
+ app,
69
+ persona,
70
+ "get",
71
+ "file",
72
+ api="LOCAL file_reader",
73
+ external="local",
74
+ help=_HELP,
75
+ )
76
+ def get_file(
77
+ path: Path = typer.Argument(..., help="The file to read."),
78
+ pictures_dir: Path | None = typer.Option(
79
+ None,
80
+ "--pictures-dir",
81
+ help="Where to save the PNG pictures (default: next to the file).",
82
+ ),
83
+ ) -> None:
84
+ print_json_or_exit(console, lambda: read_file_payload(path, pictures_dir))
File without changes