@amaster.ai/pi-lark 0.1.6 → 0.1.8
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.
- package/README.md +5 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/skills/lark-apps/SKILL.md +59 -14
- package/skills/lark-apps/creative-design/agents/assets/vision-probe.png +0 -0
- package/skills/lark-apps/creative-design/agents/fork-verifier-agent.md +71 -0
- package/skills/lark-apps/creative-design/agents/vision-probe-agent.md +41 -0
- package/skills/lark-apps/creative-design/assets/index.html +27 -0
- package/skills/lark-apps/creative-design/creative-design.md +239 -0
- package/skills/lark-apps/creative-design/references/aily.md +39 -0
- package/skills/lark-apps/creative-design/references/animated-video.md +34 -0
- package/skills/lark-apps/creative-design/references/charts.md +165 -0
- package/skills/lark-apps/creative-design/references/claude.md +36 -0
- package/skills/lark-apps/creative-design/references/codex.md +32 -0
- package/skills/lark-apps/creative-design/references/data-report.md +108 -0
- package/skills/lark-apps/creative-design/references/frontend-design.md +71 -0
- package/skills/lark-apps/creative-design/references/hi-fi-design.md +32 -0
- package/skills/lark-apps/creative-design/references/interactive-prototype.md +24 -0
- package/skills/lark-apps/creative-design/references/make-a-deck.md +133 -0
- package/skills/lark-apps/creative-design/references/visual-exposure.md +82 -0
- package/skills/lark-apps/creative-design/references/wireframe.md +14 -0
- package/skills/lark-apps/creative-design/starter-components/android-frame.jsx +188 -0
- package/skills/lark-apps/creative-design/starter-components/animations.jsx +773 -0
- package/skills/lark-apps/creative-design/starter-components/browser-window.jsx +122 -0
- package/skills/lark-apps/creative-design/starter-components/deck-stage.js +2483 -0
- package/skills/lark-apps/creative-design/starter-components/design-canvas.jsx +1432 -0
- package/skills/lark-apps/creative-design/starter-components/ios-frame.jsx +270 -0
- package/skills/lark-apps/creative-design/starter-components/macos-window.jsx +197 -0
- package/skills/lark-apps/creative-design/starter-components/tweaks-panel.jsx +752 -0
- package/skills/lark-apps/references/lark-apps-automation.md +80 -2
- package/skills/lark-apps/references/lark-apps-cache.md +61 -0
- package/skills/lark-apps/references/lark-apps-cloud-dev.md +5 -5
- package/skills/lark-apps/references/lark-apps-create.md +6 -4
- package/skills/lark-apps/references/lark-apps-db.md +1 -1
- package/skills/lark-apps/references/lark-apps-env-pull.md +1 -1
- package/skills/lark-apps/references/lark-apps-file.md +2 -2
- package/skills/lark-apps/references/lark-apps-get.md +1 -1
- package/skills/lark-apps/references/lark-apps-git-credential.md +1 -1
- package/skills/lark-apps/references/lark-apps-html-publish.md +4 -8
- package/skills/lark-apps/references/lark-apps-init.md +1 -1
- package/skills/lark-apps/references/lark-apps-list.md +2 -2
- package/skills/lark-apps/references/lark-apps-local-dev.md +80 -11
- package/skills/lark-apps/references/lark-apps-openapi-key.md +1 -1
- package/skills/lark-apps/references/lark-apps-release-create.md +2 -2
- package/skills/lark-apps/references/lark-apps-release-get.md +3 -3
- package/skills/lark-base/SKILL.md +34 -19
- package/skills/lark-base/references/lark-base-cell-value.md +3 -3
- package/skills/lark-base/references/lark-base-dashboard-block-get-data.md +17 -1
- package/skills/lark-base/references/lark-base-dashboard.md +17 -4
- package/skills/lark-base/references/lark-base-data-query-guide.md +8 -0
- package/skills/lark-base/references/lark-base-data-query.md +11 -4
- package/skills/lark-base/references/lark-base-field-create.md +21 -6
- package/skills/lark-base/references/lark-base-field-json.md +9 -6
- package/skills/lark-base/references/lark-base-field-update.md +17 -1
- package/skills/lark-base/references/lark-base-filter-condition.md +179 -0
- package/skills/lark-base/references/lark-base-form-questions-create.md +40 -7
- package/skills/lark-base/references/lark-base-form-questions-update.md +73 -20
- package/skills/lark-base/references/lark-base-form-submit.md +16 -7
- package/skills/lark-base/references/lark-base-record-batch-create.md +12 -10
- package/skills/lark-base/references/lark-base-record-batch-update.md +11 -9
- package/skills/lark-base/references/lark-base-record-upsert.md +1 -1
- package/skills/lark-base/references/lark-base-role-guide.md +11 -0
- package/skills/lark-base/references/lark-base-view-set-filter.md +11 -137
- package/skills/lark-base/references/role-config.md +31 -5
- package/skills/lark-calendar/SKILL.md +14 -8
- package/skills/lark-calendar/references/lark-calendar-create.md +6 -5
- package/skills/lark-calendar/references/lark-calendar-recurring.md +1 -0
- package/skills/lark-calendar/references/lark-calendar-room-find.md +2 -1
- package/skills/lark-calendar/references/lark-calendar-schedule-clear-time.md +1 -0
- package/skills/lark-calendar/references/lark-calendar-suggestion.md +1 -1
- package/skills/lark-calendar/references/lark-calendar-update.md +10 -4
- package/skills/lark-contact/SKILL.md +19 -3
- package/skills/lark-contact/references/lark-contact-search-bot.md +60 -0
- package/skills/lark-doc/SKILL.md +26 -61
- package/skills/lark-doc/references/genres/business-analysis.md +30 -0
- package/skills/lark-doc/references/genres/data-report.md +32 -0
- package/skills/lark-doc/references/genres/email.md +38 -0
- package/skills/lark-doc/references/genres/execution-plan.md +27 -0
- package/skills/lark-doc/references/genres/formal-doc.md +37 -0
- package/skills/lark-doc/references/genres/meeting-minutes.md +24 -0
- package/skills/lark-doc/references/genres/memo-brief.md +25 -0
- package/skills/lark-doc/references/genres/official-redhead.md +73 -0
- package/skills/lark-doc/references/genres/prd.md +26 -0
- package/skills/lark-doc/references/genres/proposal.md +24 -0
- package/skills/lark-doc/references/genres/research-report.md +32 -0
- package/skills/lark-doc/references/genres/retrospective.md +25 -0
- package/skills/lark-doc/references/genres/route-consumer.md +37 -0
- package/skills/lark-doc/references/genres/route-creative.md +36 -0
- package/skills/lark-doc/references/genres/route-knowledge.md +39 -0
- package/skills/lark-doc/references/genres/route-marketing.md +40 -0
- package/skills/lark-doc/references/genres/route-media.md +36 -0
- package/skills/lark-doc/references/genres/route-opinion.md +38 -0
- package/skills/lark-doc/references/genres/route-personal-brand.md +36 -0
- package/skills/lark-doc/references/genres/route-platform.md +9 -0
- package/skills/lark-doc/references/genres/route-report.md +10 -0
- package/skills/lark-doc/references/genres/route-workplace.md +17 -0
- package/skills/lark-doc/references/genres/sop-tutorial.md +41 -0
- package/skills/lark-doc/references/genres/technical-doc.md +39 -0
- package/skills/lark-doc/references/genres/wechat.md +39 -0
- package/skills/lark-doc/references/genres/weekly-report.md +24 -0
- package/skills/lark-doc/references/genres/white-paper.md +32 -0
- package/skills/lark-doc/references/genres/xiaohongshu.md +38 -0
- package/skills/lark-doc/references/lark-doc-create-workflow.md +121 -0
- package/skills/lark-doc/references/lark-doc-create.md +22 -48
- package/skills/lark-doc/references/lark-doc-fetch.md +84 -93
- package/skills/lark-doc/references/lark-doc-history.md +16 -15
- package/skills/lark-doc/references/lark-doc-md.md +5 -1
- package/skills/lark-doc/references/lark-doc-media-download.md +2 -1
- package/skills/lark-doc/references/lark-doc-script.md +76 -0
- package/skills/lark-doc/references/lark-doc-update.md +70 -222
- package/skills/lark-doc/references/lark-doc-whiteboard.md +14 -17
- package/skills/lark-doc/references/lark-doc-xml-extended-blocks.md +46 -0
- package/skills/lark-doc/references/lark-doc-xml.md +38 -166
- package/skills/lark-drive/SKILL.md +32 -50
- package/skills/lark-drive/references/lark-drive-add-comment.md +2 -4
- package/skills/lark-drive/references/lark-drive-add-reply.md +47 -0
- package/skills/lark-drive/references/lark-drive-apply-permission.md +3 -3
- package/skills/lark-drive/references/lark-drive-batch-query-comments.md +46 -0
- package/skills/lark-drive/references/lark-drive-comment-content.md +50 -0
- package/skills/lark-drive/references/lark-drive-comment-location.md +9 -15
- package/skills/lark-drive/references/lark-drive-copy.md +87 -0
- package/skills/lark-drive/references/lark-drive-delete-reply.md +48 -0
- package/skills/lark-drive/references/lark-drive-download.md +6 -1
- package/skills/lark-drive/references/lark-drive-export.md +3 -0
- package/skills/lark-drive/references/lark-drive-list-comments.md +25 -68
- package/skills/lark-drive/references/lark-drive-list-replies.md +54 -0
- package/skills/lark-drive/references/lark-drive-member-add.md +2 -2
- package/skills/lark-drive/references/lark-drive-member-list.md +65 -0
- package/skills/lark-drive/references/lark-drive-permission-get-setting.md +48 -0
- package/skills/lark-drive/references/lark-drive-preview.md +11 -1
- package/skills/lark-drive/references/lark-drive-react-reply.md +51 -0
- package/skills/lark-drive/references/lark-drive-reactions.md +27 -25
- package/skills/lark-drive/references/lark-drive-resolve-comment.md +45 -0
- package/skills/lark-drive/references/lark-drive-restore-comment.md +46 -0
- package/skills/lark-drive/references/lark-drive-search.md +7 -1
- package/skills/lark-drive/references/lark-drive-secure-label.md +1 -1
- package/skills/lark-drive/references/lark-drive-task-result.md +3 -0
- package/skills/lark-drive/references/lark-drive-update-reply.md +46 -0
- package/skills/lark-drive/references/lark-drive-update-title.md +78 -0
- package/skills/lark-drive/references/lark-drive-upload.md +1 -0
- package/skills/lark-drive/references/lark-drive-workflow-permission-governance-commands.md +38 -8
- package/skills/lark-drive/references/lark-drive-workflow-permission-governance-outputs.md +10 -10
- package/skills/lark-drive/references/lark-drive-workflow-permission-governance.md +22 -20
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector-execute.md +273 -0
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector-recall.md +202 -0
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector-resolve-verify.md +231 -0
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector-review-plan.md +248 -0
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector-setup.md +174 -0
- package/skills/lark-drive/references/lark-drive-workflow-topic-move-collector.md +202 -0
- package/skills/lark-drive/references/lark-drive-workflow.md +5 -4
- package/skills/lark-event/SKILL.md +8 -4
- package/skills/lark-event/references/lark-event-application.md +38 -0
- package/skills/lark-event/references/lark-event-vc.md +8 -2
- package/skills/lark-im/SKILL.md +9 -9
- package/skills/lark-im/references/card/card-2.0-schema.md +1 -1
- package/skills/lark-im/references/card/lark-im-card-style.md +4 -4
- package/skills/lark-im/references/card/resource/icons.md +14 -0
- package/skills/lark-im/references/lark-im-chat-list.md +9 -2
- package/skills/lark-im/references/lark-im-chat-members-list.md +7 -4
- package/skills/lark-im/references/lark-im-chat-messages-list.md +10 -3
- package/skills/lark-im/references/lark-im-chat-search.md +9 -2
- package/skills/lark-im/references/lark-im-feed-group-list-item.md +2 -2
- package/skills/lark-im/references/lark-im-feed-group-list.md +2 -2
- package/skills/lark-im/references/lark-im-feed-shortcut-list.md +1 -1
- package/skills/lark-im/references/lark-im-flag-list.md +9 -8
- package/skills/lark-im/references/lark-im-message-enrichment.md +1 -1
- package/skills/lark-im/references/lark-im-messages-resources-download.md +19 -25
- package/skills/lark-im/references/lark-im-messages-search.md +4 -5
- package/skills/lark-im/references/lark-im-threads-messages-list.md +8 -4
- package/skills/lark-mail/references/lark-mail-triage.md +19 -4
- package/skills/lark-minutes/SKILL.md +1 -1
- package/skills/lark-minutes/references/lark-minutes-search.md +6 -7
- package/skills/lark-okr/SKILL.md +71 -26
- package/skills/lark-okr/references/lark-okr-batch-create.md +19 -18
- package/skills/lark-okr/references/lark-okr-create.md +173 -0
- package/skills/lark-okr/references/lark-okr-cycle-list.md +17 -7
- package/skills/lark-okr/references/lark-okr-entities.md +1 -0
- package/skills/lark-okr/references/lark-okr-indicator-update.md +3 -1
- package/skills/lark-okr/references/lark-okr-indicators.md +61 -12
- package/skills/lark-okr/references/lark-okr-progress-list.md +21 -9
- package/skills/lark-shared/SKILL.md +3 -3
- package/skills/lark-sheets/SKILL.md +83 -82
- package/skills/lark-sheets/references/lark-sheets-batch-update.md +13 -58
- package/skills/lark-sheets/references/lark-sheets-chart.md +2 -1
- package/skills/lark-sheets/references/lark-sheets-conditional-format.md +1 -1
- package/skills/lark-sheets/references/lark-sheets-range-operations.md +5 -5
- package/skills/lark-sheets/references/lark-sheets-read-data.md +80 -6
- package/skills/lark-sheets/references/lark-sheets-sheet-structure.md +21 -10
- package/skills/lark-sheets/references/lark-sheets-styles-put.md +93 -0
- package/skills/lark-sheets/references/lark-sheets-visual-standards.md +2 -2
- package/skills/lark-sheets/references/lark-sheets-workbook.md +4 -3
- package/skills/lark-sheets/references/lark-sheets-write-cells.md +40 -12
- package/skills/lark-sheets/scripts/lark_detect_subtables.py +593 -0
- package/skills/lark-sheets/scripts/lark_inspect_workbook.py +188 -0
- package/skills/lark-sheets/scripts/lark_profile_table.py +614 -0
- package/skills/lark-sheets/scripts/lark_sheet_range.py +176 -0
- package/skills/lark-sheets/scripts/lark_sheet_read_cli.py +184 -0
- package/skills/lark-sheets/scripts/sheets_df.py +21 -3
- package/skills/lark-slides/SKILL.md +134 -104
- package/skills/lark-slides/references/asset-planning.md +6 -4
- package/skills/lark-slides/references/iconpark.md +2 -2
- package/skills/lark-slides/references/lark-slides-add-slide.md +92 -0
- package/skills/lark-slides/references/lark-slides-create.md +86 -66
- package/skills/lark-slides/references/lark-slides-delete-slide.md +65 -0
- package/skills/lark-slides/references/lark-slides-edit-workflows.md +6 -7
- package/skills/lark-slides/references/lark-slides-history.md +132 -0
- package/skills/lark-slides/references/lark-slides-media-upload.md +4 -27
- package/skills/lark-slides/references/lark-slides-pptx-template-workflows.md +7 -11
- package/skills/lark-slides/references/lark-slides-replace-slide.md +22 -4
- package/skills/lark-slides/references/lark-slides-screenshot.md +33 -15
- package/skills/lark-slides/references/lark-slides-update-slide.md +146 -0
- package/skills/lark-slides/references/lark-slides-xml-presentation-slide-get.md +2 -2
- package/skills/lark-slides/references/lark-slides-xml-presentation-slide-replace.md +2 -3
- package/skills/lark-slides/references/lark-slides-xml-presentations-get.md +90 -32
- package/skills/lark-slides/references/planning-layer.md +11 -10
- package/skills/lark-slides/references/slides_chart_demo.xml +1415 -1
- package/skills/lark-slides/references/slides_xml_schema_definition.xml +539 -79
- package/skills/lark-slides/references/troubleshooting.md +26 -9
- package/skills/lark-slides/references/validation-checklist.md +55 -18
- package/skills/lark-slides/references/visual-planning.md +25 -22
- package/skills/lark-slides/references/xml-schema-quick-ref.md +299 -51
- package/skills/lark-slides/scripts/sxsd_validator.py +1052 -0
- package/skills/lark-slides/scripts/xml_text_overlap_lint.py +1964 -195
- package/skills/lark-slides/scripts/xml_text_overlap_lint_test.py +4051 -501
- package/skills/lark-task/SKILL.md +7 -0
- package/skills/lark-task/references/lark-task-complete.md +6 -2
- package/skills/lark-task/references/lark-task-create.md +9 -0
- package/skills/lark-task/references/lark-task-update.md +6 -2
- package/skills/lark-whiteboard/SKILL.md +21 -13
- package/skills/lark-whiteboard/elements/layout.md +1 -1
- package/skills/lark-whiteboard/elements/schema.md +2 -2
- package/skills/lark-whiteboard/references/{lark-whiteboard-query.md → lark-whiteboard-export.md} +17 -16
- package/skills/lark-whiteboard/references/lark-whiteboard-update.md +7 -7
- package/skills/lark-whiteboard/references/lark-whiteboard-workflow.md +23 -31
- package/skills/lark-whiteboard/routes/dsl.md +11 -5
- package/skills/lark-whiteboard/routes/mermaid.md +3 -3
- package/skills/lark-whiteboard/routes/svg-edit.md +9 -6
- package/skills/lark-whiteboard/routes/svg.md +14 -7
- package/skills/lark-whiteboard/scenes/bar-chart.md +1 -1
- package/skills/lark-whiteboard/scenes/fishbone.md +1 -1
- package/skills/lark-whiteboard/scenes/flywheel.md +1 -1
- package/skills/lark-whiteboard/scenes/line-chart.md +1 -1
- package/skills/lark-whiteboard/scenes/mention.md +71 -0
- package/skills/lark-whiteboard/scenes/treemap.md +1 -1
- package/skills/lark-wiki/SKILL.md +6 -3
- package/skills/lark-wiki/references/lark-wiki-delete-space.md +6 -3
- package/skills/lark-doc/references/lark-doc-word-stat.md +0 -93
- package/skills/lark-doc/references/style/lark-doc-create-workflow.md +0 -47
- package/skills/lark-doc/references/style/lark-doc-style.md +0 -68
- package/skills/lark-doc/references/style/lark-doc-update-workflow.md +0 -48
- package/skills/lark-doc/scripts/doc_word_stat.py +0 -1243
- package/skills/lark-drive/references/lark-drive-comments-guide.md +0 -80
- package/skills/lark-slides/references/examples.md +0 -91
- package/skills/lark-slides/references/lark-slides-replace-pages.md +0 -95
- package/skills/lark-slides/references/lark-slides-whiteboard.md +0 -331
- package/skills/lark-slides/references/lark-slides-xml-get.md +0 -100
- package/skills/lark-slides/references/lark-slides-xml-presentation-slide-delete.md +0 -125
- package/skills/lark-slides/references/slide-templates.md +0 -201
- package/skills/lark-slides/references/slides_demo.xml +0 -226
- package/skills/lark-slides/references/xml-format-guide.md +0 -433
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
"""Range and coordinate helpers for Lark Sheet scripts."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
CELL_RE = re.compile(r"^\$?([A-Za-z]+)\$?([1-9][0-9]*)$")
|
|
11
|
+
ROW_RE = re.compile(r"^\$?([1-9][0-9]*)$")
|
|
12
|
+
COLUMN_RE = re.compile(r"^\$?([A-Za-z]+)$")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class RangeBounds:
|
|
17
|
+
start_row: int
|
|
18
|
+
start_col: int
|
|
19
|
+
end_row: int
|
|
20
|
+
end_col: int
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def row_count(self) -> int:
|
|
24
|
+
return self.end_row - self.start_row + 1
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def col_count(self) -> int:
|
|
28
|
+
return self.end_col - self.start_col + 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def col_to_index(col: str) -> int:
|
|
32
|
+
value = 0
|
|
33
|
+
for char in col.strip().upper():
|
|
34
|
+
if not ("A" <= char <= "Z"):
|
|
35
|
+
raise ValueError(f"Invalid column: {col}")
|
|
36
|
+
value = value * 26 + (ord(char) - ord("A") + 1)
|
|
37
|
+
if value <= 0:
|
|
38
|
+
raise ValueError(f"Invalid column: {col}")
|
|
39
|
+
return value
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def index_to_col(index: int) -> str:
|
|
43
|
+
if index < 1:
|
|
44
|
+
raise ValueError(f"Column index must be >= 1: {index}")
|
|
45
|
+
chars = []
|
|
46
|
+
n = index
|
|
47
|
+
while n:
|
|
48
|
+
n, rem = divmod(n - 1, 26)
|
|
49
|
+
chars.append(chr(ord("A") + rem))
|
|
50
|
+
return "".join(reversed(chars))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_cell(cell_ref: str) -> tuple[int, int]:
|
|
54
|
+
match = CELL_RE.match(cell_ref.strip())
|
|
55
|
+
if not match:
|
|
56
|
+
raise ValueError(f"Invalid cell reference: {cell_ref}")
|
|
57
|
+
col, row = match.groups()
|
|
58
|
+
return int(row), col_to_index(col)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _parse_endpoint(endpoint: str) -> tuple[str, int, int] | tuple[str, int]:
|
|
62
|
+
cell = CELL_RE.match(endpoint)
|
|
63
|
+
if cell:
|
|
64
|
+
col, row = cell.groups()
|
|
65
|
+
return "cell", int(row), col_to_index(col)
|
|
66
|
+
row = ROW_RE.match(endpoint)
|
|
67
|
+
if row:
|
|
68
|
+
return "row", int(row.group(1))
|
|
69
|
+
column = COLUMN_RE.match(endpoint)
|
|
70
|
+
if column:
|
|
71
|
+
return "column", col_to_index(column.group(1))
|
|
72
|
+
raise ValueError(f"Invalid A1 range endpoint: {endpoint}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def parse_range(
|
|
76
|
+
range_ref: str,
|
|
77
|
+
*,
|
|
78
|
+
max_row: int | None = None,
|
|
79
|
+
max_col: int | None = None,
|
|
80
|
+
) -> RangeBounds:
|
|
81
|
+
"""Parse the A1 range forms accepted by ``+csv-get``.
|
|
82
|
+
|
|
83
|
+
Open-ended forms need the caller's actual returned grid dimensions. This
|
|
84
|
+
keeps generated ranges finite without guessing a spreadsheet-wide limit.
|
|
85
|
+
"""
|
|
86
|
+
ref = range_ref.strip()
|
|
87
|
+
if "!" in ref:
|
|
88
|
+
_, ref = ref.rsplit("!", 1)
|
|
89
|
+
if not ref:
|
|
90
|
+
raise ValueError(f"Invalid A1 range: {range_ref}")
|
|
91
|
+
parts = ref.split(":")
|
|
92
|
+
if len(parts) > 2:
|
|
93
|
+
raise ValueError(f"Invalid A1 range: {range_ref}")
|
|
94
|
+
start = _parse_endpoint(parts[0])
|
|
95
|
+
end = _parse_endpoint(parts[-1])
|
|
96
|
+
|
|
97
|
+
if len(parts) == 1:
|
|
98
|
+
if start[0] != "cell":
|
|
99
|
+
raise ValueError(f"A1 range must include a cell or ':' separator: {range_ref}")
|
|
100
|
+
_, row, col = start
|
|
101
|
+
return RangeBounds(row, col, row, col)
|
|
102
|
+
|
|
103
|
+
if start[0] == end[0] == "cell":
|
|
104
|
+
_, start_row, start_col = start
|
|
105
|
+
_, end_row, end_col = end
|
|
106
|
+
elif start[0] == end[0] == "row":
|
|
107
|
+
_, start_row = start
|
|
108
|
+
_, end_row = end
|
|
109
|
+
start_col = 1
|
|
110
|
+
if max_col is None:
|
|
111
|
+
raise ValueError(f"Range needs a maximum column: {range_ref}")
|
|
112
|
+
end_col = max_col
|
|
113
|
+
elif start[0] == end[0] == "column":
|
|
114
|
+
_, start_col = start
|
|
115
|
+
_, end_col = end
|
|
116
|
+
start_row = 1
|
|
117
|
+
if max_row is None:
|
|
118
|
+
raise ValueError(f"Range needs a maximum row: {range_ref}")
|
|
119
|
+
end_row = max_row
|
|
120
|
+
elif start[0] == "cell" and end[0] == "column":
|
|
121
|
+
_, start_row, start_col = start
|
|
122
|
+
_, end_col = end
|
|
123
|
+
if max_row is None:
|
|
124
|
+
raise ValueError(f"Range needs a maximum row: {range_ref}")
|
|
125
|
+
end_row = max(start_row, max_row)
|
|
126
|
+
elif start[0] == "cell" and end[0] == "row":
|
|
127
|
+
_, start_row, start_col = start
|
|
128
|
+
_, end_row = end
|
|
129
|
+
if max_col is None:
|
|
130
|
+
raise ValueError(f"Range needs a maximum column: {range_ref}")
|
|
131
|
+
end_col = max(start_col, max_col)
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError(f"Invalid A1 range: {range_ref}")
|
|
134
|
+
return RangeBounds(min(start_row, end_row), min(start_col, end_col), max(start_row, end_row), max(start_col, end_col))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def format_cell(row: int, col: int) -> str:
|
|
138
|
+
if row < 1:
|
|
139
|
+
raise ValueError(f"Row must be >= 1: {row}")
|
|
140
|
+
return f"{index_to_col(col)}{row}"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def format_range(start_row: int, start_col: int, end_row: int, end_col: int) -> str:
|
|
144
|
+
bounds = RangeBounds(
|
|
145
|
+
min(start_row, end_row),
|
|
146
|
+
min(start_col, end_col),
|
|
147
|
+
max(start_row, end_row),
|
|
148
|
+
max(start_col, end_col),
|
|
149
|
+
)
|
|
150
|
+
start = format_cell(bounds.start_row, bounds.start_col)
|
|
151
|
+
end = format_cell(bounds.end_row, bounds.end_col)
|
|
152
|
+
return start if start == end else f"{start}:{end}"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def iter_cells(bounds: RangeBounds):
|
|
156
|
+
for row in range(bounds.start_row, bounds.end_row + 1):
|
|
157
|
+
for col in range(bounds.start_col, bounds.end_col + 1):
|
|
158
|
+
yield row, col
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def ranges_intersect(a: RangeBounds, b: RangeBounds) -> bool:
|
|
162
|
+
return not (
|
|
163
|
+
a.end_row < b.start_row
|
|
164
|
+
or b.end_row < a.start_row
|
|
165
|
+
or a.end_col < b.start_col
|
|
166
|
+
or b.end_col < a.start_col
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def range_union(a: RangeBounds, b: RangeBounds) -> RangeBounds:
|
|
171
|
+
return RangeBounds(
|
|
172
|
+
min(a.start_row, b.start_row),
|
|
173
|
+
min(a.start_col, b.start_col),
|
|
174
|
+
max(a.end_row, b.end_row),
|
|
175
|
+
max(a.end_col, b.end_col),
|
|
176
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
"""Read-only Lark Sheet subset wrapper for the helper scripts."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LarkCliError(RuntimeError):
|
|
14
|
+
def __init__(self, message: str, *, cmd: list[str] | None = None):
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.cmd = cmd or []
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_spreadsheet_args(
|
|
20
|
+
parser,
|
|
21
|
+
*,
|
|
22
|
+
require_sheet: bool = False,
|
|
23
|
+
allow_sheet: bool = True,
|
|
24
|
+
) -> None:
|
|
25
|
+
spreadsheet = parser.add_mutually_exclusive_group(required=True)
|
|
26
|
+
spreadsheet.add_argument("--url")
|
|
27
|
+
spreadsheet.add_argument("--spreadsheet-token")
|
|
28
|
+
if allow_sheet:
|
|
29
|
+
sheet = parser.add_mutually_exclusive_group(required=require_sheet)
|
|
30
|
+
sheet.add_argument("--sheet-id")
|
|
31
|
+
sheet.add_argument("--sheet-name")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _append_flag(cmd: list[str], name: str, value: Any) -> None:
|
|
35
|
+
flag = f"--{name.replace('_', '-')}"
|
|
36
|
+
if value is None:
|
|
37
|
+
return
|
|
38
|
+
if isinstance(value, bool):
|
|
39
|
+
cmd.append(flag if value else f"{flag}=false")
|
|
40
|
+
return
|
|
41
|
+
cmd.extend([flag, str(value)])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run_sheets(
|
|
45
|
+
shortcut: str,
|
|
46
|
+
*,
|
|
47
|
+
url: str | None = None,
|
|
48
|
+
spreadsheet_token: str | None = None,
|
|
49
|
+
sheet_id: str | None = None,
|
|
50
|
+
sheet_name: str | None = None,
|
|
51
|
+
flags: dict[str, Any] | None = None,
|
|
52
|
+
timeout: int = 60,
|
|
53
|
+
) -> dict[str, Any]:
|
|
54
|
+
if bool(url) == bool(spreadsheet_token):
|
|
55
|
+
raise LarkCliError("Pass exactly one of --url or --spreadsheet-token")
|
|
56
|
+
if sheet_id and sheet_name:
|
|
57
|
+
raise LarkCliError("Pass only one of --sheet-id or --sheet-name")
|
|
58
|
+
|
|
59
|
+
cmd = ["lark-cli", "sheets", shortcut]
|
|
60
|
+
_append_flag(cmd, "url", url)
|
|
61
|
+
_append_flag(cmd, "spreadsheet_token", spreadsheet_token)
|
|
62
|
+
_append_flag(cmd, "sheet_id", sheet_id)
|
|
63
|
+
_append_flag(cmd, "sheet_name", sheet_name)
|
|
64
|
+
for key, value in (flags or {}).items():
|
|
65
|
+
_append_flag(cmd, key, value)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
completed = subprocess.run(
|
|
69
|
+
cmd,
|
|
70
|
+
capture_output=True,
|
|
71
|
+
text=True,
|
|
72
|
+
timeout=timeout,
|
|
73
|
+
check=False,
|
|
74
|
+
)
|
|
75
|
+
except FileNotFoundError as exc:
|
|
76
|
+
raise LarkCliError("lark-cli not found", cmd=cmd) from exc
|
|
77
|
+
except subprocess.TimeoutExpired as exc:
|
|
78
|
+
raise LarkCliError(f"lark-cli timed out after {timeout}s", cmd=cmd) from exc
|
|
79
|
+
|
|
80
|
+
if completed.returncode != 0:
|
|
81
|
+
detail = (completed.stderr or completed.stdout or "").strip()
|
|
82
|
+
raise LarkCliError(detail or f"lark-cli exited with {completed.returncode}", cmd=cmd)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
envelope = json.loads(completed.stdout)
|
|
86
|
+
except json.JSONDecodeError as exc:
|
|
87
|
+
snippet = completed.stdout[:500].replace("\n", "\\n")
|
|
88
|
+
raise LarkCliError(f"lark-cli stdout was not JSON: {snippet}", cmd=cmd) from exc
|
|
89
|
+
|
|
90
|
+
if isinstance(envelope, dict) and envelope.get("ok") is False:
|
|
91
|
+
raise LarkCliError(json.dumps(envelope, ensure_ascii=False), cmd=cmd)
|
|
92
|
+
if not isinstance(envelope, dict):
|
|
93
|
+
raise LarkCliError("lark-cli returned a non-object JSON payload", cmd=cmd)
|
|
94
|
+
return envelope
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def envelope_data(envelope: dict[str, Any]) -> dict[str, Any]:
|
|
98
|
+
data = envelope.get("data")
|
|
99
|
+
return data if isinstance(data, dict) else envelope
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def emit_success(action: str, data: dict[str, Any], warnings: list[str] | None = None) -> None:
|
|
103
|
+
print(
|
|
104
|
+
json.dumps(
|
|
105
|
+
{
|
|
106
|
+
"ok": True,
|
|
107
|
+
"engine": "lark",
|
|
108
|
+
"action": action,
|
|
109
|
+
"data": data,
|
|
110
|
+
"warnings": warnings or [],
|
|
111
|
+
},
|
|
112
|
+
ensure_ascii=False,
|
|
113
|
+
indent=2,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def emit_error(action: str, message: str, warnings: list[str] | None = None) -> None:
|
|
119
|
+
print(
|
|
120
|
+
json.dumps(
|
|
121
|
+
{
|
|
122
|
+
"ok": False,
|
|
123
|
+
"engine": "lark",
|
|
124
|
+
"action": action,
|
|
125
|
+
"error": message,
|
|
126
|
+
"warnings": warnings or [],
|
|
127
|
+
},
|
|
128
|
+
ensure_ascii=False,
|
|
129
|
+
indent=2,
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def sheet_title(sheet: dict[str, Any]) -> str:
|
|
136
|
+
return str(sheet.get("title") or sheet.get("sheet_name") or sheet.get("name") or "")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def sheet_identifier(sheet: dict[str, Any]) -> str:
|
|
140
|
+
return str(sheet.get("sheet_id") or sheet.get("id") or "")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def sheet_locator(sheet: dict[str, Any]) -> dict[str, str]:
|
|
144
|
+
sid = sheet_identifier(sheet)
|
|
145
|
+
if sid:
|
|
146
|
+
return {"sheet_id": sid}
|
|
147
|
+
title = sheet_title(sheet)
|
|
148
|
+
if title:
|
|
149
|
+
return {"sheet_name": title}
|
|
150
|
+
return {}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def extract_sheets(workbook_data: dict[str, Any]) -> list[dict[str, Any]]:
|
|
154
|
+
sheets = workbook_data.get("sheets")
|
|
155
|
+
if isinstance(sheets, list):
|
|
156
|
+
return [sheet for sheet in sheets if isinstance(sheet, dict)]
|
|
157
|
+
workbook = workbook_data.get("workbook")
|
|
158
|
+
if isinstance(workbook, dict) and isinstance(workbook.get("sheets"), list):
|
|
159
|
+
return [sheet for sheet in workbook["sheets"] if isinstance(sheet, dict)]
|
|
160
|
+
return []
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def resolve_target_sheets(
|
|
164
|
+
workbook_data: dict[str, Any],
|
|
165
|
+
*,
|
|
166
|
+
sheet_id: str | None = None,
|
|
167
|
+
sheet_name: str | None = None,
|
|
168
|
+
require_one: bool = False,
|
|
169
|
+
) -> list[dict[str, Any]]:
|
|
170
|
+
sheets = extract_sheets(workbook_data)
|
|
171
|
+
if sheet_id:
|
|
172
|
+
matches = [sheet for sheet in sheets if sheet_identifier(sheet) == sheet_id]
|
|
173
|
+
elif sheet_name:
|
|
174
|
+
matches = [sheet for sheet in sheets if sheet_title(sheet) == sheet_name]
|
|
175
|
+
else:
|
|
176
|
+
matches = sheets
|
|
177
|
+
|
|
178
|
+
if require_one:
|
|
179
|
+
if len(matches) == 1:
|
|
180
|
+
return matches
|
|
181
|
+
if not matches:
|
|
182
|
+
raise LarkCliError("No matching sheet found")
|
|
183
|
+
raise LarkCliError("Multiple sheets matched; pass --sheet-id or --sheet-name")
|
|
184
|
+
return matches
|
|
@@ -19,11 +19,29 @@ import pandas as pd
|
|
|
19
19
|
|
|
20
20
|
def df_to_sheet(df, name, formats=None):
|
|
21
21
|
"""Pack one DataFrame into one entry of a `+table-put --sheets` payload."""
|
|
22
|
+
packed = json.loads(df.to_json(orient="split", date_format="iso"))
|
|
23
|
+
# The protocol requires string column names. pandas keeps integer labels
|
|
24
|
+
# (e.g. the default RangeIndex columns 0/1/2) as JSON numbers, while the
|
|
25
|
+
# dtypes dict keys get stringified during JSON serialization — the CLI
|
|
26
|
+
# then rejects `columns` ("cannot unmarshal number into … type string")
|
|
27
|
+
# and the dtype lookup would miss anyway. Stringify every key once, and
|
|
28
|
+
# refuse to continue when that conversion silently merges two columns.
|
|
29
|
+
normalized_labels = [str(c) for c in df.columns]
|
|
30
|
+
columns = [str(c) for c in packed["columns"]]
|
|
31
|
+
if normalized_labels != columns:
|
|
32
|
+
columns = normalized_labels
|
|
33
|
+
if len(set(columns)) != len(columns):
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"column labels collide after str() conversion; "
|
|
36
|
+
"rename the DataFrame columns before packing"
|
|
37
|
+
)
|
|
38
|
+
packed["columns"] = columns
|
|
39
|
+
dtype_values = list(df.dtypes)
|
|
22
40
|
return {
|
|
23
41
|
"name": name,
|
|
24
|
-
**
|
|
25
|
-
"dtypes":
|
|
26
|
-
**({"formats": formats} if formats else {}),
|
|
42
|
+
**packed,
|
|
43
|
+
"dtypes": {key: str(dtype) for key, dtype in zip(columns, dtype_values)},
|
|
44
|
+
**({"formats": {str(k): v for k, v in formats.items()}} if formats else {}),
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
|