Faiszal Anwar97 downloadsTwo-way sync between your Obsidian vault and a Lark Wiki space. Uses lark-cli under the hood. Includes setup wizard, one-click sync, and three-way conflict resolution.
Sync one or more Lark Wiki spaces into your Obsidian vault. Uses lark-cli as the transport so auth, scopes, and API coverage are handled upstream — this plugin adds the Obsidian-side UX (ribbon button, setup wizard, paste-a-link, conflict-aware sync, image download).
Status: v0.0.10 — pull works end-to-end across multiple spaces, with full-tree pagination, folder mirroring, GFM-table rendering, and image attachments. Push exists but is unverified end-to-end. Three-way conflict modal still falls back to a
.remote.conflict.mdsidecar.
📥 Lark).lark-cli, pick a space from a dropdown, or paste any wiki URL and the plugin resolves the space + root node for you.wiki nodes list automatically.<lark-table>, <text>, etc.) get converted to GFM pipe tables and clean markdown.<image token="…"/> references download via lark-cli docs +media-download into _attachments/<token>.<ext> and embed as ![[<token>.<ext>]]. Cached across syncs.lastSyncedHash enables proper conflict detection (skip / pull / push / conflict / reconcile). Sync state is keyed by Lark nodeToken, so it survives any future path-mapping changes.Lark Wiki Sync — Nexus Wiki: ↓ pulling 12/48 · FRD.md.lark-cli error message, so you don't have to spelunk through the dev console.<lark-table> and pulled image embeds (![[<token>.<ext>]]) become <image token="..."/> before they hit Lark's update API, so structure survives the round trip. Image embeds whose target is not a known Lark token are left alone (newly-pasted local images aren't yet uploaded — that's a future item).https://<tenant>.feishu.cn/wiki/<node_token>) are rewritten to [[Target Doc]] Obsidian wikilinks when the destination is already in the sync state. Click → jumps to the synced file; backlinks panel works.lark_sync: false to a file's frontmatter to skip just that file on every sync without removing it from either side. Useful for in-progress drafts you don't want pushed yet.* matches anything except /; ** matches anything including /.sync) and command palette entries.lark-cliThis plugin is a thin Obsidian-side wrapper. All Lark/Feishu API calls are made by lark-cli, an open-source CLI maintained by the Larksuite team. The plugin shells out to it for every list, fetch, update, and media download — so auth, scopes, rate limiting, and API coverage live upstream.
Install (requires Node 18+):
npm install -g @larksuite/cli
# verify it's on your PATH
lark-cli --version
Initial setup (one-time per machine):
lark-cli config init # set your tenant + app credentials, follow the prompts
See the larksuite/cli README for full options (Feishu vs. Lark, app vs. tenant credentials, MCP integration).
Authorize the right scopes for what you want this plugin to do:
# pull-only (read tree, fetch doc bodies, download images)
lark-cli auth login --scope "wiki:space:retrieve wiki:node:retrieve docx:document:readonly drive:drive:readonly"
# bidirectional (above + write back to Lark on push)
lark-cli auth login --scope "wiki:space:retrieve wiki:node:retrieve docx:document:readonly docx:document drive:drive:readonly"
When the plugin hits a missing scope, the post-sync results modal will tell you exactly which scope to add and give you the command to copy-paste.
isDesktopOnly: true — the plugin uses Node's child_process.spawn to run lark-cli. iOS / Android Obsidian don't expose that, so they're not supported.
fszlnwr/obsidian-lark-wiki-sync.git clone https://github.com/fszlnwr/obsidian-lark-wiki-sync.git
cd /path/to/your/vault/.obsidian/plugins
ln -s /absolute/path/to/obsidian-lark-wiki-sync ./lark-wiki-sync
cd lark-wiki-sync
npm install
npm run dev # watches main.ts → main.js
Then in Obsidian: Settings → Community plugins → reload → enable "Lark Wiki Sync".
main.ts Plugin entry, ribbon, commands, settings migration
src/settings.ts Settings schema (spaces[]) + settings tab
src/ui/SetupWizardModal.ts Add-a-space wizard (dropdown OR paste-a-link)
src/lark/LarkCli.ts Shell-out wrapper around lark-cli
src/sync/SyncEngine.ts Per-space pull/push/conflict orchestration
src/state/StateStore.ts Per-file hash + last-sync timestamps
src/util/hash.ts SHA-1 helper
src/util/parseWikiUrl.ts Lark URL → node_token parser
src/util/larkToObsidianMd.ts Lark-flavoured MD → Obsidian MD converter (pull)
src/util/obsidianToLarkMd.ts Obsidian MD → Lark-flavoured MD converter (push)
📥 Lark/
Nexus Wiki/
FRD.md
FSD/
Loyalty engine spec.md
_attachments/
<image-token>.jpg
Another Wiki Space/
...
_attachments/
...
Each space is fully self-contained — delete its folder and everything for that space is gone, no orphans.
For each docx node, compare three hashes:
lastSyncedHash vs. localHash |
lastSyncedHash vs. remoteHash |
Action |
|---|---|---|
| same | same | skip |
| same | changed | pull |
| changed | same | push (confirmed) |
| changed | changed | conflict → apply policy |
Special branches when there is no prior lastSyncedHash (first sync of a node, or after a reset):
lastSyncedHash is the common ancestor. The remote hash is computed AFTER the Lark→Obsidian markdown transform, so re-pulling the same Lark content produces a stable hash. State is keyed by nodeToken, so renaming localRoot or restructuring folders does not orphan it.
lark-cli path (blank = use PATH), identity (user / bot).📥 LarknodeToken, reconcile branch); push confirmation modal--mode overwrite (lark-cli's replace_all mode is selection-scoped despite the name)<lark-table>, ![[<token>.<ext>]] → <image token="..."/> on push[[Target]] when the destination is syncedlark_sync: false frontmatter flag + glob ignore patterns<h2> in favour of section .setHeading(); move inline style.width to a CSS class[[…]] → Lark URL on push), needs tenant host capture<sheet> rendering / linklark_sync: false frontmatter flag, auto-sync timer, polishlarkToObsidianMd.ts) and URL parser (parseWikiUrl.ts) are pure functions and would benefit from a small test harness.MIT