maybeai-sheet-cli 0.5.0__tar.gz → 0.7.0__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.
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/PKG-INFO +28 -6
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/README.md +27 -5
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/pyproject.toml +9 -1
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/__init__.py +1 -1
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/cli.py +1 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/db_table.py +156 -5
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/excel_worksheet.py +6 -1
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/workbook.py +56 -7
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/endpoints.py +3 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_cli.py +99 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_endpoints.py +3 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_two_engine_cli.py +133 -4
- maybeai_sheet_cli-0.5.0/.claude/settings.local.json +0 -9
- maybeai_sheet_cli-0.5.0/CLAUDE.md +0 -46
- maybeai_sheet_cli-0.5.0/cli-packaging-plan.md +0 -279
- maybeai_sheet_cli-0.5.0/cli-plan.md +0 -389
- maybeai_sheet_cli-0.5.0/docs/agent-friendly-command-tree.md +0 -1424
- maybeai_sheet_cli-0.5.0/docs/operation-plan-cli-no-dsl-plan.md +0 -459
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-command-tree.md +0 -1270
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-phase-2-routes.md +0 -57
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-phase-2.md +0 -708
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-be-new-backend-contracts-plan.md +0 -928
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-deferred-table-routing-plan.md +0 -713
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-excel-worksheet-multi-table-plan.md +0 -683
- maybeai_sheet_cli-0.5.0/docs/superpowers/plans/2026-07-01-mbs-two-engine-command-refactor.md +0 -139
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/2026-07-01-mbs-deferred-routing-e2e-report.html +0 -239
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/2026-07-01-phase1-test-review.html +0 -326
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/2026-07-01-phase2-test-review.html +0 -309
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/20260701-130317-excel-table-empty-range-e2e.html +0 -99
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/20260701-130447-excel-table-empty-range-e2e.html +0 -23
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/20260701-130556-excel-table-empty-range-e2e.html +0 -204
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/20260701-230803-local-cli-endpoint-verification.html +0 -52
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/20260701-232511-local7011-cli-table-metadata-verification.html +0 -78
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130317.json +0 -79
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130447.json +0 -82
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130556.json +0 -414
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/e2e-artifacts/multi-table-empty-range-20260701-130317.xlsx +0 -0
- maybeai_sheet_cli-0.5.0/docs/superpowers/reports/e2e-artifacts/openpyxl-multi-table-empty-range-20260701-130447.xlsx +0 -0
- maybeai_sheet_cli-0.5.0/docs/verification-matrix-fallback.md +0 -35
- maybeai_sheet_cli-0.5.0/uv.lock +0 -415
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/.gitignore +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/client.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/__init__.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/_phase2.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/cell.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/column.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/excel_table.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/file.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/formula.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/history.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/range.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/raw.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/row.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/share.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/sheet.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/style.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/table.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/worksheet.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/config.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/errors.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/formatters.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/models/__init__.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/models/share.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/models/sheet.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/models/workbook.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/options.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/protocol.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/resolver.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/table_resolver.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/update.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_formatters.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_phase2_cli.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_resolver.py +0 -0
- {maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/tests/test_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maybeai-sheet-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: CLI for common MaybeAI spreadsheet operations
|
|
5
5
|
Project-URL: Homepage, https://github.com/OmniMCP-AI/maybeai-sheet-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/OmniMCP-AI/maybeai-sheet-cli
|
|
@@ -135,6 +135,13 @@ Import a large table-like `.xlsx` into SheetTable/PG:
|
|
|
135
135
|
mbs workbook import ./large-table.xlsx --engine postgres
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
+
Import a mixed workbook and let the backend select engines per worksheet:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
mbs workbook import ./mixed-workbook.xlsx --engine auto
|
|
142
|
+
mbs workbook import ./mixed-workbook.xlsx --engine "postgres,excel,excel,postgres"
|
|
143
|
+
```
|
|
144
|
+
|
|
138
145
|
Append rows and verify:
|
|
139
146
|
|
|
140
147
|
```bash
|
|
@@ -219,7 +226,7 @@ mbs
|
|
|
219
226
|
│
|
|
220
227
|
├── excel_worksheet
|
|
221
228
|
│ ├── read API Read the full range of the worksheet.
|
|
222
|
-
│ ├── list-table API
|
|
229
|
+
│ ├── list-table API Detect content-backed tables in a worksheet.
|
|
223
230
|
│ ├── create API Create a worksheet, optionally with starter values.
|
|
224
231
|
│ ├── update API Update worksheet metadata.
|
|
225
232
|
│ ├── sample PLAN Return bounded worksheet/range sample rows.
|
|
@@ -252,9 +259,9 @@ mbs
|
|
|
252
259
|
| |- lineage API Trace cell/range lineage.
|
|
253
260
|
│
|
|
254
261
|
├── db_worksheet(AKA db_table)
|
|
255
|
-
│ ├── create
|
|
256
|
-
│ ├── update
|
|
257
|
-
│ ├── delete
|
|
262
|
+
│ ├── create PLAN Create a PG-backed table from JSON row data.
|
|
263
|
+
│ ├── update PLAN Update PG-backed rows from JSON row data.
|
|
264
|
+
│ ├── delete PLAN Delete PG-backed rows/table; requires --yes unless --dry-run.
|
|
258
265
|
│ ├── schema API Infer table schema from a bounded PG read.
|
|
259
266
|
│ ├── sample API Return bounded table sample rows.
|
|
260
267
|
│ ├── read API Read PG-backed table rows.
|
|
@@ -344,6 +351,7 @@ mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20
|
|
|
344
351
|
mbs excel-worksheet create --doc-id abc123 --name Actuals # API
|
|
345
352
|
mbs excel-worksheet update --doc-id abc123 --worksheet-name Actuals --title "Actuals FY26" # API
|
|
346
353
|
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals # API
|
|
354
|
+
mbs excel-worksheet list-table --doc-id abc123 --gid 3 # API
|
|
347
355
|
mbs excel-worksheet sample --doc-id abc123 --worksheet-name Actuals --range A1:Z5000 --limit 50 # PLAN
|
|
348
356
|
|
|
349
357
|
# excel-worksheet range
|
|
@@ -363,8 +371,17 @@ mbs excel-table style columns-width --doc-id abc123 --worksheet-name Orders --ta
|
|
|
363
371
|
mbs db-table metadata --doc-id abc123 --name orders-large # API
|
|
364
372
|
mbs db-table schema --doc-id abc123 --name orders-large # API
|
|
365
373
|
mbs db-table sample --doc-id abc123 --name orders-large --limit 50 # API
|
|
366
|
-
mbs db-table read --doc-id abc123 --name orders-large --limit
|
|
374
|
+
mbs db-table read --doc-id abc123 --name orders-large --limit 100 --offset 200 # API
|
|
367
375
|
mbs db-table insert --doc-id abc123 --name orders-large --rows rows.json # API
|
|
376
|
+
mbs db-table create --doc-id abc123 --name Orders --rows orders.json # PLAN
|
|
377
|
+
mbs db-table update --doc-id abc123 --name Orders --key order_id --rows orders_patch.json # PLAN
|
|
378
|
+
mbs db-table update --doc-id abc123 --name Orders --row-index 42 --rows row_patch.json # PLAN
|
|
379
|
+
mbs db-table update --doc-id abc123 --name Orders --where '{"status":"open"}' --rows status_patch.json # PLAN
|
|
380
|
+
mbs db-table update --doc-id abc123 --name Orders --column-index 3 --column-name status # PLAN
|
|
381
|
+
mbs db-table delete --doc-id abc123 --name Orders --where '{"order_id":"A1"}' --yes # PLAN
|
|
382
|
+
mbs db-table delete --doc-id abc123 --name Orders --row-index 42 --count 10 --yes # PLAN
|
|
383
|
+
mbs db-table delete --doc-id abc123 --name Orders --column-index 3 --yes # PLAN
|
|
384
|
+
mbs db-table delete --doc-id abc123 --name Orders --column-name status --yes # PLAN
|
|
368
385
|
|
|
369
386
|
# row
|
|
370
387
|
mbs excel-worksheet row insert --doc-id abc123 --worksheet-name Actuals --row 10 --count 2 # API
|
|
@@ -404,6 +421,11 @@ mbs version restore --doc-id abc123 --version-id v42 --dry-run # PLAN
|
|
|
404
421
|
mbs share create --doc-id abc123 --email teammate@example.com --role viewer # API
|
|
405
422
|
```
|
|
406
423
|
|
|
424
|
+
`db-table read`, `schema`, and `sample` keep reads bounded. The default read
|
|
425
|
+
limit is 100 rows, and `--limit` / `--offset` are capped at 10,000 to avoid
|
|
426
|
+
accidental large PG scans. Use a keyset-style backend contract for deeper
|
|
427
|
+
pagination when it becomes available.
|
|
428
|
+
|
|
407
429
|
Refactor planning is tracked separately from this README:
|
|
408
430
|
`docs/superpowers/plans/2026-07-01-mbs-two-engine-command-refactor.md`.
|
|
409
431
|
|
|
@@ -111,6 +111,13 @@ Import a large table-like `.xlsx` into SheetTable/PG:
|
|
|
111
111
|
mbs workbook import ./large-table.xlsx --engine postgres
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
Import a mixed workbook and let the backend select engines per worksheet:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mbs workbook import ./mixed-workbook.xlsx --engine auto
|
|
118
|
+
mbs workbook import ./mixed-workbook.xlsx --engine "postgres,excel,excel,postgres"
|
|
119
|
+
```
|
|
120
|
+
|
|
114
121
|
Append rows and verify:
|
|
115
122
|
|
|
116
123
|
```bash
|
|
@@ -195,7 +202,7 @@ mbs
|
|
|
195
202
|
│
|
|
196
203
|
├── excel_worksheet
|
|
197
204
|
│ ├── read API Read the full range of the worksheet.
|
|
198
|
-
│ ├── list-table API
|
|
205
|
+
│ ├── list-table API Detect content-backed tables in a worksheet.
|
|
199
206
|
│ ├── create API Create a worksheet, optionally with starter values.
|
|
200
207
|
│ ├── update API Update worksheet metadata.
|
|
201
208
|
│ ├── sample PLAN Return bounded worksheet/range sample rows.
|
|
@@ -228,9 +235,9 @@ mbs
|
|
|
228
235
|
| |- lineage API Trace cell/range lineage.
|
|
229
236
|
│
|
|
230
237
|
├── db_worksheet(AKA db_table)
|
|
231
|
-
│ ├── create
|
|
232
|
-
│ ├── update
|
|
233
|
-
│ ├── delete
|
|
238
|
+
│ ├── create PLAN Create a PG-backed table from JSON row data.
|
|
239
|
+
│ ├── update PLAN Update PG-backed rows from JSON row data.
|
|
240
|
+
│ ├── delete PLAN Delete PG-backed rows/table; requires --yes unless --dry-run.
|
|
234
241
|
│ ├── schema API Infer table schema from a bounded PG read.
|
|
235
242
|
│ ├── sample API Return bounded table sample rows.
|
|
236
243
|
│ ├── read API Read PG-backed table rows.
|
|
@@ -320,6 +327,7 @@ mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20
|
|
|
320
327
|
mbs excel-worksheet create --doc-id abc123 --name Actuals # API
|
|
321
328
|
mbs excel-worksheet update --doc-id abc123 --worksheet-name Actuals --title "Actuals FY26" # API
|
|
322
329
|
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals # API
|
|
330
|
+
mbs excel-worksheet list-table --doc-id abc123 --gid 3 # API
|
|
323
331
|
mbs excel-worksheet sample --doc-id abc123 --worksheet-name Actuals --range A1:Z5000 --limit 50 # PLAN
|
|
324
332
|
|
|
325
333
|
# excel-worksheet range
|
|
@@ -339,8 +347,17 @@ mbs excel-table style columns-width --doc-id abc123 --worksheet-name Orders --ta
|
|
|
339
347
|
mbs db-table metadata --doc-id abc123 --name orders-large # API
|
|
340
348
|
mbs db-table schema --doc-id abc123 --name orders-large # API
|
|
341
349
|
mbs db-table sample --doc-id abc123 --name orders-large --limit 50 # API
|
|
342
|
-
mbs db-table read --doc-id abc123 --name orders-large --limit
|
|
350
|
+
mbs db-table read --doc-id abc123 --name orders-large --limit 100 --offset 200 # API
|
|
343
351
|
mbs db-table insert --doc-id abc123 --name orders-large --rows rows.json # API
|
|
352
|
+
mbs db-table create --doc-id abc123 --name Orders --rows orders.json # PLAN
|
|
353
|
+
mbs db-table update --doc-id abc123 --name Orders --key order_id --rows orders_patch.json # PLAN
|
|
354
|
+
mbs db-table update --doc-id abc123 --name Orders --row-index 42 --rows row_patch.json # PLAN
|
|
355
|
+
mbs db-table update --doc-id abc123 --name Orders --where '{"status":"open"}' --rows status_patch.json # PLAN
|
|
356
|
+
mbs db-table update --doc-id abc123 --name Orders --column-index 3 --column-name status # PLAN
|
|
357
|
+
mbs db-table delete --doc-id abc123 --name Orders --where '{"order_id":"A1"}' --yes # PLAN
|
|
358
|
+
mbs db-table delete --doc-id abc123 --name Orders --row-index 42 --count 10 --yes # PLAN
|
|
359
|
+
mbs db-table delete --doc-id abc123 --name Orders --column-index 3 --yes # PLAN
|
|
360
|
+
mbs db-table delete --doc-id abc123 --name Orders --column-name status --yes # PLAN
|
|
344
361
|
|
|
345
362
|
# row
|
|
346
363
|
mbs excel-worksheet row insert --doc-id abc123 --worksheet-name Actuals --row 10 --count 2 # API
|
|
@@ -380,6 +397,11 @@ mbs version restore --doc-id abc123 --version-id v42 --dry-run # PLAN
|
|
|
380
397
|
mbs share create --doc-id abc123 --email teammate@example.com --role viewer # API
|
|
381
398
|
```
|
|
382
399
|
|
|
400
|
+
`db-table read`, `schema`, and `sample` keep reads bounded. The default read
|
|
401
|
+
limit is 100 rows, and `--limit` / `--offset` are capped at 10,000 to avoid
|
|
402
|
+
accidental large PG scans. Use a keyset-style backend contract for deeper
|
|
403
|
+
pagination when it becomes available.
|
|
404
|
+
|
|
383
405
|
Refactor planning is tracked separately from this README:
|
|
384
406
|
`docs/superpowers/plans/2026-07-01-mbs-two-engine-command-refactor.md`.
|
|
385
407
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maybeai-sheet-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0"
|
|
8
8
|
description = "CLI for common MaybeAI spreadsheet operations"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -39,3 +39,11 @@ mbs = "maybeai_sheet.cli:app"
|
|
|
39
39
|
|
|
40
40
|
[tool.hatch.build.targets.wheel]
|
|
41
41
|
packages = ["src/maybeai_sheet"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
include = [
|
|
45
|
+
"/src",
|
|
46
|
+
"/tests",
|
|
47
|
+
"/README.md",
|
|
48
|
+
"/pyproject.toml",
|
|
49
|
+
]
|
|
@@ -22,6 +22,8 @@ from ..table_resolver import (
|
|
|
22
22
|
from ._phase2 import bounded_limit
|
|
23
23
|
|
|
24
24
|
app = typer.Typer(help="PG/SheetTable-backed table operations.", no_args_is_help=True)
|
|
25
|
+
DB_TABLE_SAFE_MAX_LIMIT = 10_000
|
|
26
|
+
DB_TABLE_SAFE_MAX_OFFSET = 10_000
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def _state(
|
|
@@ -79,13 +81,27 @@ def _read_payload(
|
|
|
79
81
|
resolved: ResolvedTableTarget,
|
|
80
82
|
*,
|
|
81
83
|
limit: int,
|
|
84
|
+
offset: int = 0,
|
|
82
85
|
) -> dict[str, Any]:
|
|
83
|
-
|
|
86
|
+
payload: dict[str, Any] = {
|
|
84
87
|
"uri": target.uri,
|
|
85
88
|
"worksheet_name": resolved.worksheet_name,
|
|
86
|
-
"range_address": bounded_a1_range(resolved.range_address, limit),
|
|
87
89
|
"value_render_option": "FORMATTED_VALUE",
|
|
90
|
+
"limit": limit,
|
|
88
91
|
}
|
|
92
|
+
if offset:
|
|
93
|
+
payload["offset"] = offset
|
|
94
|
+
if resolved.range_address:
|
|
95
|
+
payload["range_address"] = bounded_a1_range(resolved.range_address, limit)
|
|
96
|
+
return payload
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _bounded_offset(value: int) -> int:
|
|
100
|
+
if value < 0:
|
|
101
|
+
raise UsageError("--offset must be at least 0.")
|
|
102
|
+
if value > DB_TABLE_SAFE_MAX_OFFSET:
|
|
103
|
+
raise UsageError(f"--offset must be {DB_TABLE_SAFE_MAX_OFFSET} or less.")
|
|
104
|
+
return value
|
|
89
105
|
|
|
90
106
|
|
|
91
107
|
def _load_rows(rows: Path) -> list[dict[str, Any]]:
|
|
@@ -168,6 +184,7 @@ def schema(
|
|
|
168
184
|
verbose=verbose,
|
|
169
185
|
timeout=timeout,
|
|
170
186
|
)
|
|
187
|
+
bounded = bounded_limit(limit, option="--limit", maximum=DB_TABLE_SAFE_MAX_LIMIT)
|
|
171
188
|
workbook_target = resolve_target(state)
|
|
172
189
|
client = MaybeAIClient(state)
|
|
173
190
|
resolved, target = _resolve_db_table(
|
|
@@ -179,7 +196,11 @@ def schema(
|
|
|
179
196
|
endpoint = "/api/v1/excel/read_sheet"
|
|
180
197
|
response = client.post_json(
|
|
181
198
|
endpoint,
|
|
182
|
-
_read_payload(
|
|
199
|
+
_read_payload(
|
|
200
|
+
target,
|
|
201
|
+
resolved,
|
|
202
|
+
limit=bounded,
|
|
203
|
+
),
|
|
183
204
|
)
|
|
184
205
|
result = {
|
|
185
206
|
**schema_from_read_payload(response),
|
|
@@ -224,6 +245,7 @@ def sample(
|
|
|
224
245
|
verbose=verbose,
|
|
225
246
|
timeout=timeout,
|
|
226
247
|
)
|
|
248
|
+
bounded = bounded_limit(limit, option="--limit", maximum=DB_TABLE_SAFE_MAX_LIMIT)
|
|
227
249
|
workbook_target = resolve_target(state)
|
|
228
250
|
client = MaybeAIClient(state)
|
|
229
251
|
resolved, target = _resolve_db_table(
|
|
@@ -232,7 +254,6 @@ def sample(
|
|
|
232
254
|
name=name,
|
|
233
255
|
backend_id=backend_id,
|
|
234
256
|
)
|
|
235
|
-
bounded = bounded_limit(limit, option="--limit")
|
|
236
257
|
endpoint = "/api/v1/excel/read_sheet"
|
|
237
258
|
response = client.post_json(endpoint, _read_payload(target, resolved, limit=bounded))
|
|
238
259
|
result = {
|
|
@@ -264,6 +285,7 @@ def read(
|
|
|
264
285
|
name: Optional[str] = typer.Option(None, "--name", help="PG-backed table name."),
|
|
265
286
|
backend_id: Optional[str] = typer.Option(None, "--backend-id", help="Advanced backend table id."),
|
|
266
287
|
limit: int = typer.Option(100, "--limit", min=1, help="Maximum rows to return."),
|
|
288
|
+
offset: int = typer.Option(0, "--offset", min=0, help="Rows to skip before reading."),
|
|
267
289
|
output: Optional[str] = output_option(),
|
|
268
290
|
verbose: Optional[bool] = verbose_option(),
|
|
269
291
|
timeout: Optional[float] = timeout_option(),
|
|
@@ -278,6 +300,8 @@ def read(
|
|
|
278
300
|
verbose=verbose,
|
|
279
301
|
timeout=timeout,
|
|
280
302
|
)
|
|
303
|
+
bounded = bounded_limit(limit, option="--limit", maximum=DB_TABLE_SAFE_MAX_LIMIT)
|
|
304
|
+
bounded_offset = _bounded_offset(offset)
|
|
281
305
|
workbook_target = resolve_target(state)
|
|
282
306
|
client = MaybeAIClient(state)
|
|
283
307
|
resolved, target = _resolve_db_table(
|
|
@@ -289,7 +313,12 @@ def read(
|
|
|
289
313
|
endpoint = "/api/v1/excel/read_sheet"
|
|
290
314
|
response = client.post_json(
|
|
291
315
|
endpoint,
|
|
292
|
-
_read_payload(
|
|
316
|
+
_read_payload(
|
|
317
|
+
target,
|
|
318
|
+
resolved,
|
|
319
|
+
limit=bounded,
|
|
320
|
+
offset=bounded_offset,
|
|
321
|
+
),
|
|
293
322
|
)
|
|
294
323
|
render_output(
|
|
295
324
|
build_command_output(
|
|
@@ -304,6 +333,128 @@ def read(
|
|
|
304
333
|
handle_cli_error(error)
|
|
305
334
|
|
|
306
335
|
|
|
336
|
+
DB_TABLE_CREATE_EXAMPLE = """Example:
|
|
337
|
+
|
|
338
|
+
mbs db-table create --doc-id <doc_id> --name Orders --rows orders.json
|
|
339
|
+
|
|
340
|
+
`orders.json` should contain a JSON array of row objects, similar to workbook
|
|
341
|
+
import data:
|
|
342
|
+
|
|
343
|
+
[
|
|
344
|
+
{"order_id": "A1", "amount": 120.5},
|
|
345
|
+
{"order_id": "A2", "amount": 88.0}
|
|
346
|
+
]
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
DB_TABLE_UPDATE_EXAMPLE = """Example:
|
|
351
|
+
|
|
352
|
+
mbs db-table update --doc-id <doc_id> --name Orders --key order_id --rows orders_patch.json
|
|
353
|
+
mbs db-table update --doc-id <doc_id> --name Orders --row-index 42 --rows row_patch.json
|
|
354
|
+
mbs db-table update --doc-id <doc_id> --name Orders --where '{"status":"open"}' --rows status_patch.json
|
|
355
|
+
mbs db-table update --doc-id <doc_id> --name Orders --column-index 3 --column-name status
|
|
356
|
+
|
|
357
|
+
`orders_patch.json` should contain row objects keyed by --key. Positional
|
|
358
|
+
updates use 1-based --row-index and --column-index when the backend exposes a
|
|
359
|
+
stable SheetTable index contract.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
DB_TABLE_DELETE_EXAMPLE = """Example:
|
|
364
|
+
|
|
365
|
+
mbs db-table delete --doc-id <doc_id> --name Orders --where '{"order_id":"A1"}' --yes
|
|
366
|
+
mbs db-table delete --doc-id <doc_id> --name Orders --row-index 42 --yes
|
|
367
|
+
mbs db-table delete --doc-id <doc_id> --name Orders --row-index 42 --count 10 --yes
|
|
368
|
+
mbs db-table delete --doc-id <doc_id> --name Orders --column-index 3 --yes
|
|
369
|
+
mbs db-table delete --doc-id <doc_id> --name Orders --column-name status --yes
|
|
370
|
+
|
|
371
|
+
Use --dry-run first once the backend route is available.
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _planned_db_table_error(command: str, example: str) -> None:
|
|
376
|
+
raise UsageError(
|
|
377
|
+
f"db-table {command} is planned but the backend route is not implemented yet.\n\n"
|
|
378
|
+
f"{example.strip()}"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@app.command(
|
|
383
|
+
"create",
|
|
384
|
+
help="Planned: create a PG/SheetTable-backed table from JSON row data.",
|
|
385
|
+
epilog=DB_TABLE_CREATE_EXAMPLE,
|
|
386
|
+
)
|
|
387
|
+
def create(
|
|
388
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
389
|
+
name: Optional[str] = typer.Option(None, "--name", help="New PG-backed table name."),
|
|
390
|
+
rows: Optional[Path] = typer.Option(
|
|
391
|
+
None,
|
|
392
|
+
"--rows",
|
|
393
|
+
exists=True,
|
|
394
|
+
readable=True,
|
|
395
|
+
resolve_path=True,
|
|
396
|
+
help="Path to JSON rows payload.",
|
|
397
|
+
),
|
|
398
|
+
) -> None:
|
|
399
|
+
_ = (doc_id, name, rows)
|
|
400
|
+
try:
|
|
401
|
+
_planned_db_table_error("create", DB_TABLE_CREATE_EXAMPLE)
|
|
402
|
+
except Exception as error: # pragma: no cover - CLI boundary
|
|
403
|
+
handle_cli_error(error)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@app.command(
|
|
407
|
+
"update",
|
|
408
|
+
help="Planned: update PG/SheetTable-backed rows from JSON row data.",
|
|
409
|
+
epilog=DB_TABLE_UPDATE_EXAMPLE,
|
|
410
|
+
)
|
|
411
|
+
def update(
|
|
412
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
413
|
+
name: Optional[str] = typer.Option(None, "--name", help="PG-backed table name."),
|
|
414
|
+
key: Optional[str] = typer.Option(None, "--key", help="Unique key column for matching rows."),
|
|
415
|
+
row_index: Optional[int] = typer.Option(None, "--row-index", min=1, help="1-based SheetTable row index to update."),
|
|
416
|
+
column_index: Optional[int] = typer.Option(None, "--column-index", min=1, help="1-based SheetTable column index to update."),
|
|
417
|
+
column_name: Optional[str] = typer.Option(None, "--column-name", help="Column name to update."),
|
|
418
|
+
where: Optional[str] = typer.Option(None, "--where", help="JSON object filter for rows to update."),
|
|
419
|
+
rows: Optional[Path] = typer.Option(
|
|
420
|
+
None,
|
|
421
|
+
"--rows",
|
|
422
|
+
exists=True,
|
|
423
|
+
readable=True,
|
|
424
|
+
resolve_path=True,
|
|
425
|
+
help="Path to JSON rows payload.",
|
|
426
|
+
),
|
|
427
|
+
) -> None:
|
|
428
|
+
_ = (doc_id, name, key, row_index, column_index, column_name, where, rows)
|
|
429
|
+
try:
|
|
430
|
+
_planned_db_table_error("update", DB_TABLE_UPDATE_EXAMPLE)
|
|
431
|
+
except Exception as error: # pragma: no cover - CLI boundary
|
|
432
|
+
handle_cli_error(error)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@app.command(
|
|
436
|
+
"delete",
|
|
437
|
+
help="Planned: delete PG/SheetTable-backed rows or a table.",
|
|
438
|
+
epilog=DB_TABLE_DELETE_EXAMPLE,
|
|
439
|
+
)
|
|
440
|
+
def delete(
|
|
441
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
442
|
+
name: Optional[str] = typer.Option(None, "--name", help="PG-backed table name."),
|
|
443
|
+
where: Optional[str] = typer.Option(None, "--where", help="JSON object filter for rows to delete."),
|
|
444
|
+
row_index: Optional[int] = typer.Option(None, "--row-index", min=1, help="1-based SheetTable row index to delete."),
|
|
445
|
+
column_index: Optional[int] = typer.Option(None, "--column-index", min=1, help="1-based SheetTable column index to delete."),
|
|
446
|
+
column_name: Optional[str] = typer.Option(None, "--column-name", help="Column name to delete."),
|
|
447
|
+
count: int = typer.Option(1, "--count", min=1, help="Number of rows to delete from --row-index."),
|
|
448
|
+
yes: bool = typer.Option(False, "--yes", help="Confirm destructive delete."),
|
|
449
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview delete when backend support exists."),
|
|
450
|
+
) -> None:
|
|
451
|
+
_ = (doc_id, name, where, row_index, column_index, column_name, count, yes, dry_run)
|
|
452
|
+
try:
|
|
453
|
+
_planned_db_table_error("delete", DB_TABLE_DELETE_EXAMPLE)
|
|
454
|
+
except Exception as error: # pragma: no cover - CLI boundary
|
|
455
|
+
handle_cli_error(error)
|
|
456
|
+
|
|
457
|
+
|
|
307
458
|
@app.command("insert")
|
|
308
459
|
def insert(
|
|
309
460
|
ctx: typer.Context,
|
{maybeai_sheet_cli-0.5.0 → maybeai_sheet_cli-0.7.0}/src/maybeai_sheet/commands/excel_worksheet.py
RENAMED
|
@@ -288,6 +288,7 @@ def list_table(
|
|
|
288
288
|
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
289
289
|
url: Optional[str] = typer.Option(None, "--url", help="MaybeAI workbook URL."),
|
|
290
290
|
uri: Optional[str] = typer.Option(None, "--uri", help="Resolved workbook URI."),
|
|
291
|
+
gid: Optional[int] = typer.Option(None, "--gid", help="Worksheet gid."),
|
|
291
292
|
worksheet_name: Optional[str] = typer.Option(None, "--worksheet-name", help="Optional worksheet name filter."),
|
|
292
293
|
output: Optional[str] = output_option(),
|
|
293
294
|
verbose: Optional[bool] = verbose_option(),
|
|
@@ -299,12 +300,16 @@ def list_table(
|
|
|
299
300
|
doc_id=doc_id,
|
|
300
301
|
url=url,
|
|
301
302
|
uri=uri,
|
|
303
|
+
gid=gid,
|
|
302
304
|
worksheet_name=worksheet_name,
|
|
303
305
|
output=output,
|
|
304
306
|
verbose=verbose,
|
|
305
307
|
timeout=timeout,
|
|
306
308
|
)
|
|
307
|
-
target = resolve_target(
|
|
309
|
+
target = resolve_target(
|
|
310
|
+
state,
|
|
311
|
+
force_gid_uri=bool(state.gid is not None and not state.worksheet_name),
|
|
312
|
+
)
|
|
308
313
|
if target.worksheet_name or target.gid is not None:
|
|
309
314
|
endpoint = "/api/v1/excel/table/metadata"
|
|
310
315
|
payload: dict[str, object] = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
4
|
+
from typing import Any, Optional
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
@@ -18,15 +18,63 @@ from ._phase2 import bounded_limit
|
|
|
18
18
|
app = typer.Typer(help="Workbook-level operations.", no_args_is_help=True)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
_WORKSHEET_NAME_ALIASES = ("title", "name", "sheet_name")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _simplify_list_worksheets_response(response: dict[str, Any]) -> dict[str, Any]:
|
|
25
|
+
simplified = dict(response)
|
|
26
|
+
worksheets = simplified.get("worksheets")
|
|
27
|
+
if not isinstance(worksheets, list):
|
|
28
|
+
return simplified
|
|
29
|
+
simplified["worksheets"] = [
|
|
30
|
+
_simplify_worksheet_item(item) if isinstance(item, dict) else item
|
|
31
|
+
for item in worksheets
|
|
32
|
+
]
|
|
33
|
+
return simplified
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _simplify_worksheet_item(item: dict[str, Any]) -> dict[str, Any]:
|
|
37
|
+
simplified = dict(item)
|
|
38
|
+
canonical = simplified.get("worksheet_name")
|
|
39
|
+
if not canonical:
|
|
40
|
+
for alias in _WORKSHEET_NAME_ALIASES:
|
|
41
|
+
value = simplified.get(alias)
|
|
42
|
+
if value:
|
|
43
|
+
canonical = value
|
|
44
|
+
simplified["worksheet_name"] = value
|
|
45
|
+
break
|
|
46
|
+
for alias in _WORKSHEET_NAME_ALIASES:
|
|
47
|
+
if simplified.get(alias) == canonical:
|
|
48
|
+
simplified.pop(alias, None)
|
|
49
|
+
return simplified
|
|
50
|
+
|
|
51
|
+
|
|
21
52
|
def _normalize_create_from_file_engine(engine: str | None) -> str | None:
|
|
22
53
|
if engine is None:
|
|
23
54
|
return None
|
|
24
55
|
normalized = engine.strip().lower()
|
|
25
|
-
if normalized
|
|
56
|
+
if normalized == "":
|
|
26
57
|
return None
|
|
27
|
-
if
|
|
58
|
+
if "," in normalized:
|
|
59
|
+
return ",".join(_normalize_engine_token(part) for part in normalized.split(","))
|
|
60
|
+
token = _normalize_engine_token(normalized)
|
|
61
|
+
if token == "excelize":
|
|
62
|
+
return None
|
|
63
|
+
return token
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _normalize_engine_token(value: str) -> str:
|
|
67
|
+
normalized = value.strip().lower()
|
|
68
|
+
if normalized in {"auto"}:
|
|
69
|
+
return "auto"
|
|
70
|
+
if normalized in {"excel", "excelize", "xlsx"}:
|
|
71
|
+
return "excelize"
|
|
72
|
+
if normalized in {"pg", "postgres", "postgresql", "sheettable", "sheet_table"}:
|
|
28
73
|
return "postgres"
|
|
29
|
-
raise UsageError(
|
|
74
|
+
raise UsageError(
|
|
75
|
+
"Unsupported engine. Use 'auto', 'excelize', 'postgres', 'pg', "
|
|
76
|
+
"or a comma-separated per-worksheet list such as 'postgres,excel,excel'."
|
|
77
|
+
)
|
|
30
78
|
|
|
31
79
|
|
|
32
80
|
@app.callback()
|
|
@@ -130,7 +178,7 @@ def create_from_file(
|
|
|
130
178
|
engine: Optional[str] = typer.Option(
|
|
131
179
|
None,
|
|
132
180
|
"--engine",
|
|
133
|
-
help="Creation engine: excelize (default)
|
|
181
|
+
help="Creation engine: excelize (default), postgres/pg, auto, or comma-separated per-worksheet engines.",
|
|
134
182
|
),
|
|
135
183
|
output: Optional[str] = output_option(),
|
|
136
184
|
verbose: Optional[bool] = verbose_option(),
|
|
@@ -187,7 +235,7 @@ def import_workbook(
|
|
|
187
235
|
engine: Optional[str] = typer.Option(
|
|
188
236
|
None,
|
|
189
237
|
"--engine",
|
|
190
|
-
help="Creation engine: excelize (default)
|
|
238
|
+
help="Creation engine: excelize (default), postgres/pg, auto, or comma-separated per-worksheet engines.",
|
|
191
239
|
),
|
|
192
240
|
output: Optional[str] = output_option(),
|
|
193
241
|
verbose: Optional[bool] = verbose_option(),
|
|
@@ -399,11 +447,12 @@ def list_worksheets(
|
|
|
399
447
|
target = resolve_target(state)
|
|
400
448
|
endpoint = "/api/v1/excel/list_worksheets"
|
|
401
449
|
response = MaybeAIClient(state).post_json(endpoint, {"uri": target.uri})
|
|
450
|
+
result = _simplify_list_worksheets_response(response) if isinstance(response, dict) else response
|
|
402
451
|
render_output(
|
|
403
452
|
build_command_output(
|
|
404
453
|
endpoint=endpoint,
|
|
405
454
|
target=target,
|
|
406
|
-
result=
|
|
455
|
+
result=result,
|
|
407
456
|
metadata=get_endpoint_metadata("workbook.list-worksheets") if state.verbose else None,
|
|
408
457
|
),
|
|
409
458
|
state.output,
|
|
@@ -65,6 +65,9 @@ COMMAND_ENDPOINTS: dict[str, EndpointMetadata] = {
|
|
|
65
65
|
"db-table.sample": _meta("db-table.sample", "API", "POST", "/api/v1/excel/read_sheet", notes="CLI-composed sample from bounded PG read."),
|
|
66
66
|
"db-table.read": _meta("db-table.read", "API", "POST", "/api/v1/excel/read_sheet", large_output=True, default_limit=100),
|
|
67
67
|
"db-table.insert": _meta("db-table.insert", "API", "POST", "/api/v1/excel/append_rows", verify_supported=True),
|
|
68
|
+
"db-table.create": _meta("db-table.create", "PLAN", None, None, notes="Planned native db-table create route; CLI help documents the target rows-file shape."),
|
|
69
|
+
"db-table.update": _meta("db-table.update", "PLAN", None, None, notes="Planned native db-table update route; CLI help documents keyed JSON row patches."),
|
|
70
|
+
"db-table.delete": _meta("db-table.delete", "PLAN", None, None, destructive=True, confirmation_required=True, notes="Planned native db-table delete route."),
|
|
68
71
|
"worksheet.list": _meta("worksheet.list", "NOW", "POST", "/api/v1/excel/list_worksheets"),
|
|
69
72
|
"worksheet.create": _meta("worksheet.create", "NOW", "POST", "/api/v1/excel/write_new_worksheet"),
|
|
70
73
|
"range.read": _meta("range.read", "NOW", "POST", "/api/v1/excel/read_sheet"),
|