mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
Editing most of a page previously meant one block_replace part per element, each carrying that element's full XML (coordinates, size, font size included). Restyling every text block on a page was impractical as a result. +update-slide takes the page the caller wants, reads the page that is there, and sends one element-level part per difference. The indirection is forced: ReplacePart.block_id is validated as a short ELEMENT id (it must start with "b"), so a single part covering the page is impossible — the page's own id is "p"-prefixed and the background fill's id is "f"-prefixed, and both are rejected with 3350001 while element-level parts on the same page succeed. Element ids are the only handles this endpoint offers. --content is the page's target state. An element carrying its original id is replaced when it differs, an element without an id is inserted at the position it was written, an element missing from --content is deleted, and a missing <note> clears the speaker notes. Nothing differing means no request is sent at all. Comparison is canonical because the server returns pretty-printed XML with reordered attributes and injected style defaults the caller never wrote: attributes are namespace-qualified, quoted and sorted, and indentation between structural elements is dropped. Text inside a <p> subtree is compared verbatim and escaped — SML collapses a literal space between inline tags, but a preserved space written as   decodes to the very same token, so trimming would drop a real edit as unchanged; and without escaping, a paragraph holding the literal text "</p><p>" reads identically to two empty paragraphs. What is sent is the caller's exact bytes, so their formatting survives into the page. Anything the diff cannot express fails loudly instead of being silently dropped: a background change (<style> has no id of its own and its <fill> id is "f"-prefixed), a reorder of existing elements (there is no move operation), an id that does not exist on the page (omit the id to create an element), a root id that differs from --slide-id (the classic read-page-A-write-page-B mistake), any slide-level structure other than one <style>, one <data> and one <note>, and container attributes — the root additionally accepts the SML namespace in the three forms the repository's SXSD validator recognizes (the official identifier and the two server read-back spellings), since the primary workflow feeds +xml-get output straight back in. Unrepresentable structure in --content is a validation error; a page that comes back with it is a failed precondition. Pages carrying an <undefined> placeholder — the server's stand-in for an object it could not export, such as a whiteboard read without its export option — are refused outright. Whether the whole-page rewrite behind slide.replace preserves an untouched placeholder is a server-owned behavior that no self-contained test can pin down (boards cannot be created programmatically: the CLI has no whiteboard-create and SML has no whiteboard element), and editing on an unverifiable assumption risks silently destroying the one object the caller cannot see. +replace-slide remains the element-level path for such pages. --revision-id is documented as what it is. Passing a stale revision is not rejected by the backend: it means "apply against this snapshot", so pinning an older one discards every later edit to the page. -1, the default, is the safe value. slides:presentation:read sits in the enforced pre-flight scopes, not in ConditionalScopes: every execution reads the page before writing it, and ConditionalScopes is metadata only, so a write-only token would reach the GET before failing. Also: - +update as a hidden alias, derived from the canonical shortcut so scopes, identities and flags cannot drift. Agents reach for "slide update" before reading --help and used to burn turns on the error plus a help dump. - --xml / --slide-xml / --slide-content / --content-xml as hidden spellings of --content, scoped to the whole-page commands: --content exists on other slides shortcuts, and resolving these aliases there would rewrite a mistyped flag into one the caller never meant to use. - A backend failure reason becomes a typed API error rather than a field on a success envelope. - www.larkoffice.com joins the public-domain allowlist: it is the host of the SML namespace identifier, the same vendor domain family as the already-listed www.feishu.cn / www.larksuite.com, and the quality gate matches exact hostnames. - Live and dry-run E2E coverage per AGENTS.md. The live workflow walks replace, insert, delete and the no-op against the real API, checks a background change is refused with the page left untouched and the deck order holds, and is self-contained where that is knowable: for stored credentials it probes the cleanup scopes with a dry-run delete and refuses to create a deck it cannot delete (unexpected probe failures are fatal); environment tokens carry no scope metadata, so those runs rest on the documented requirement that the CI identity is provisioned with the cleanup scopes, and a cleanup failure stays fatal and visible. The dry-run suite pins the validation process contract (launch error, exit code 2, empty stdout, typed envelope on stderr). An earlier design passed every HTTP stub and was rejected outright by the real endpoint; coverage.md records that the live test skips without a user token, which is the blind spot that allowed it. - Skill docs: what the command does, what it refuses, why the background cannot be changed, and a portable (BSD/GNU) sed spelling in the primary example.