dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
通过十个工具创建与编辑飞书 / Lark 云内容——多维表格及其记录、把 Markdown 转成原生块的文档、电子表格、知识库、云空间文件与消息,并提供 Base V3 的仪表盘图表与服务端聚合查询。
English | 中文
Deep Feishu / Lark integration for DeepSeek Harness.
It gives the agent eight tools that act on a real Feishu tenant (and a browser panel that shows what it did and fixes authorization in one click):
| Tool | What it does |
|---|---|
feishu_bitable | Create Bitable (多维表格) apps with their tables and fields in one call; list tables and fields; insert, search, update, and delete records in batches |
feishu_dashboard | Create dashboards and chart blocks inside a Bitable (柱状图 / 折线图 / 饼图 / 排行榜 / 指标卡 / NPS … 15 types), position them, and read a chart's computed numbers |
feishu_query | Run a grouped, aggregated query over a table (group by, sum / avg / min / max / count / distinct_count, filter, sort, top-N) with the aggregation done server-side |
feishu_doc | Create documents; append Markdown (converted to native blocks), append raw blocks, read plain text, or import a local Markdown/Word/Excel/PowerPoint/PDF file as a new cloud document |
feishu_sheet | Create spreadsheets; add sheets; read, write, and append ranges |
feishu_drive | Browse folders, create folders, upload and download files, move, delete, read metadata |
feishu_wiki | List wiki spaces and nodes, resolve a wiki token to its document, create and move nodes |
feishu_message | Send text, interactive cards, rich posts, files, and images; reply in a thread; post through a custom-bot webhook |
feishu_auth | Diagnose authorization, produce the authorization URL, complete a manual code exchange, refresh, and log out |
feishu_api | Call any documented Feishu open-platform path, with the same auth, retry, and error mapping |
New content lands in the user's own cloud drive because the tools act as the authorized user, not as the app. When no user grant is stored they fall back to the app identity and say so in a warning rather than silently filing documents into a space the user cannot see.
Open the Feishu Open Platform (or Lark) and create a custom app.
Under 凭证与基础信息, note the App ID and App Secret.
Under 安全设置 → 重定向 URL, add the callback this plugin serves:
http://127.0.0.1:19387/api/feishu/oauth/callback
The port is whatever DeepSeek Harness serves on (the plugin reads it from the
host and reports the exact URI in feishu_auth action status).
Under 权限管理, add the scopes below, then publish a version — scopes only apply after the version is released:
| Scope | Needed for |
|---|---|
offline_access | Required — without it Feishu returns no refresh token and the user must re-authorize every couple of hours |
bitable:app | Bitable |
docx:document | Documents |
drive:drive | Drive, imports, uploads |
sheets:spreadsheet | Spreadsheets |
wiki:wiki | Wiki |
im:message, im:resource | Messages and file/image sending |
contact:user.base:readonly | Reading the authorized user's name |
See Install below, then restart DeepSeek Harness.
Open 设置 → 飞书 / Feishu. Everything is editable there — no YAML by hand:
Saving writes only this plugin's entry in the profile's cordis.patch.yml,
which the harness hot-reloads, so most changes apply without a restart. The App
Secret is never written to that file: it goes straight into the credential
store under the reference name, and the page only ever shows whether it is set.
Press 授权飞书 on the settings page, or ask the agent to run feishu_auth
with action authorize_url. The browser lands on the callback, the grant is
stored, and every later call acts as that user. The same form shows the redirect
URL to register on the Feishu app and lists the scopes to grant.
Host without a reachable browser callback (a remote or headless session)? Open the URL anywhere, then pass the
codequery parameter tofeishu_authwith actioncomplete.
Two ways, and they are alternatives — do not do both. Each creates a loader
entry with id feishu, and the loader rejects a composition that defines one id
twice with duplicate loader entry id.
The package declares a dsh.bundle patch, so a bundle-aware CLI installs and
mounts it in one step:
dsh plugin add github:2048lr/dsh-feishu
That appends dsh-feishu to dsh.profile.bundles and profile boot merges
cordis.patch.yml, which inserts the plugin row. Nothing else
needs editing.
The settings page is aware of this: when the profile mounts the bundle, it writes
an id-targeted config override rather than a second insert, so saving from
the GUI never breaks the composition.
The plugin must be resolvable from the profile's node_modules, so it is copied
there:
# from the repository root
pwsh -File scripts/install.ps1
The script copies the package into the profile's node_modules and inserts the
entry into cordis.patch.yml:
- insert:
- id: feishu
name: dsh-feishu
config:
appId: cli_xxxxxxxxxxxx
# appSecretRef: FEISHU_APP_SECRET
# brand: feishu # or lark
# defaultFolderToken: fldcnxxxxxxxx
Restart DeepSeek Harness. The host half is a Node module cached in-process, so a reload alone does not pick it up. The browser half reloads with the page.
After that first restart, use 设置 → 飞书 / Feishu instead of editing the YAML: the page rewrites the block the installer added and the harness hot-reloads it.
@deepseek-ai/* importsA third-party plugin is loaded from a junction or a copied directory inside the
profile, where a bare @deepseek-ai/dsh-tools specifier resolves to whichever
DSH installation is reachable — which may not be the running host. The host half
therefore imports only Node builtins and hand-writes its tool definitions
and its configuration validation. The test suite checks every generated schema
against the real SDK validator, so the hand-written form cannot drift from what
the registry accepts.
All fields are optional except appId.
| Field | Default | Meaning |
|---|---|---|
appId | — | The Feishu app id. Required. |
appSecretRef | FEISHU_APP_SECRET | Credential reference resolved through the harness credential store, then the environment. |
appSecret | '' | Literal fallback. Discouraged: it puts a secret in configuration. |
brand | feishu | feishu (open.feishu.cn) or lark (open.larksuite.com). |
apiBaseUrl / accountsBaseUrl | per brand | Override the endpoints (for a proxy or a private gateway). |
defaultFolderToken | '' | Where new documents, sheets, Bitable apps, and uploads go when a call omits a folder. |
defaultIdentity | auto | auto, user, or app. auto uses the user when authorized and otherwise the app, reporting the fallback. |
toolPrefix | feishu_ | Rename the tools (for example fs_). |
requestTimeoutMs | 30000 | Per-attempt timeout. |
maxRetries | 2 | Total attempts for rate-limited or transient failures (capped at 6). |
enableTools | true | Register the model-facing tools. |
enableOAuth | true | Mount the /api/feishu/* routes (status, activity, config, OAuth). |
enableUi | true | Show the floating panel. |
oauthRedirectUri | auto | Override the callback. Empty means "use the host webserver's loopback address". |
oauthScopes | recommended set | Scopes requested by the authorization flow. |
registerSystemPrompt | true | Append the Feishu guidance section to the system prompt. |
activityLogSize | 50 | Entries retained for the browser panel. |
maxRecordsPerPage | 500 | Page-size cap for paginated Feishu reads. |
patchFilePath | auto | The profile patch file the settings page edits. Not writable from the browser. |
userAccessToken | '' | Static user token for headless use; skips OAuth entirely. |
Environment fallbacks: FEISHU_APP_ID, FEISHU_APP_SECRET, FEISHU_BRAND,
FEISHU_FOLDER_TOKEN, FEISHU_USER_ACCESS_TOKEN.
The plugin uses both, deliberately:
/bitable/v1/apps/...) for apps, tables, fields, and records. It is the
documented public API, and it is what the official Lark/Feishu SDK ships./base/v3/bases/...) for the two capabilities V1 does not have at all:
dashboards with charts, and server-side aggregation. Note the resource is
bases, not apps — the V1 and V3 paths are not interchangeable, and asking
the wrong one answers a plain-text 404 page not found, not a business error.V3 is newer than the published SDK, so it is not discoverable from the SDK's typings; the shapes here were taken from the official Lark CLI and verified against the live API.
| V1 | V3 | |
|---|---|---|
| Field types | numbers (1 = text) | strings ("text") |
| Records | row objects {record_id, fields} | columnar data matrix + field_id_list |
| Batch writes | {records: [{fields}]} | record_id_list / by_filter + patch |
| Aggregation | none | POST .../data/query, a DSL naming tables and fields |
| Charts | none | dashboards + dashboards/{id}/blocks |
A chart block is described by one data_config object, which names a table and
its fields rather than addressing them by id:
{
"table_name": "订单表",
"series": [{ "field_name": "金额", "rollup": "SUM" }],
"group_by": [{ "field_name": "类别", "mode": "integrated", "sort": { "type": "value", "order": "desc" } }],
"filter": { "conjunction": "and", "conditions": [{ "field_name": "状态", "operator": "is", "value": "已完成" }] }
}
Use count_all: true instead of series to count rows, and {"text": "…"}
for a text block. Aggregations live in feishu_query rather than here, so a
question like "which region sold most" is one call instead of a table scan.
lib/
index.js composition root: config, identity, client, tools, routes
config.js normalization, brand endpoints, defaults
identity.js app credentials, tenant/app tokens, user grant, OAuth, refresh
http.js one HTTP request: timeout, bounded retries, byte ceiling
client.js the Feishu wire contract: envelope, auth, multipart, pagination
errors.js failure shaping and per-code remediation hints
local-file.js path resolution against the session workspace, upload/download
activity.js bounded in-memory record for the panel
entry-config.js safe reader/writer for this plugin's own loader entry
routes.js /api/feishu/{status,activity,config,oauth/*}
tools/
kit.js JSON-Schema-subset validator, definition helper, result envelope
auth.js bitable.js docx.js sheet.js drive.js wiki.js im.js raw.js
index.js assembly, prefixing, activity tracing
ui/client.js browser half: the floating panel and the settings page
The page is self-contained: it talks to this plugin's own
/api/feishu/config endpoint rather than the harness settings service, so the
plugin keeps importing nothing from the SDK. A save does three things:
routes.js — the redirect URL and
the API base URLs must be http(s)://, the brand and identity are
enumerations, the numeric fields are range-checked, and patchFilePath is not
writable from the browser at all;entry-config.js accepts only a path shaped like
.../profiles/<name>/cordis.patch.yml, locates the block by its markers (or
by a structural scan for the entry that names the plugin), carries over any
key the page does not manage verbatim, writes atomically, and keeps one
backup;ctx.credentials.set, so the secret never
reaches the file and is never returned by any endpoint.A field submitted as null is removed from the entry, which is how the page
resets a value to the plugin default.
dsh-feishu/user-token, together with its refresh token, and
survives restarts. It is refreshed lazily when it nears expiry, and once more
if Feishu rejects it mid-call.Feishu reports business failures as HTTP 200 with a non-zero code. The client
unwraps both shapes into a FeishuError carrying the code, the HTTP status, the
path, any permission_violations, and a remediation hint. A token-expiry code
earns exactly one automatic refresh-and-retry; everything else is surfaced with
the hint attached, because "the app lacks a scope" and "the user never
authorized" need different fixes.
Every tool takes an identity argument (auto, user, app). user puts
new documents in the authorized person's drive and is the default; app acts as
the application. auto prefers the user and, when none is stored, uses the app
and reports the fallback as a warning, so a document never quietly ends up in
the wrong drive.
npm test # or: node test/run-tests.mjs
The suite runs without the harness. It drives a mock Feishu tenant through a
fake fetch, and it validates two things against the real DSH SDK rather
than a local reimplementation:
assertSupportedJsonSchema;jsonSchemaToTs, so PTC mode keeps
working.The SDK is deliberately not a dependency, so the suite looks for the real
@deepseek-ai/dsh-tools in the usual profile and global locations and skips
those two checks when it cannot find it (which is what happens in CI). Point
DSH_TOOLS_PATH at dsh-tools/lib/index.js to force a specific copy.
state it issued and refuses a mismatch.feishu_api is a full escape hatch by design. Register it only in
compositions where the agent is trusted with the app's scopes.pwsh -File scripts/uninstall.ps1
Then restart DeepSeek Harness and delete the stored grant if you want the user authorization gone as well.
Feishu / Lark integrations are a crowded space, so it is worth being precise about what this plugin is and is not. Everything below is a point-in-time description; follow the links for the current state.
@larksuiteoapi/lark-mcp) — the official Model Context Protocol
server, and by far the broadest option: it wraps the published OpenAPI as MCP
tools with presets, spanning IM, calendar, contacts, approval, drive, and more.
Two caveats matter for document work, and both come from its own README and
tool list: editing Feishu cloud-document content is stated as unsupported
(import and read only), and its Bitable coverage is the V1 surface, where a
dashboard appears only as list and copy — no creating or updating a
dashboard or a chart block, and no aggregation.lark-base skills and reference documents. This plugin's V3 endpoint shapes
and its data_config schema were taken from here and then verified against
the live API.The DSH plugin market lists well over a hundred Feishu/Lark plugins, but the overwhelming majority are IM bridges — talk to your agent from Feishu, approval cards, notifications — which is a different job from this one. The closest in kind are:
This plugin is deliberately narrow and deep. Where it goes beyond the alternatives above:
feishu_query), so "top N by …" is one call
instead of a table scan.For anything outside that list — calendar, approval, contacts, IM — reach for the
official MCP or the official CLI, or use the feishu_api escape hatch.
MIT © 2026 Run Liu
Feishu, Lark, and 飞书 are trademarks of their respective owners. This project is an independent integration and is not affiliated with or endorsed by them.