Publish an HTML file (single file or entire directory) into a shareable Feishu app in seconds and get an access URL.
feishu-cli: if not yet installed, go to riba2534/feishu-cli to get the installation method.
Prerequisites
- Authentication: all
appscommands require User Access Token - scope:
spark:app:write(create / update / html-publish / access-scope-set)、spark:app:read(access-scope-get / list) - Login(⚠️ key pitfall): feishu-cli's
auth login --scopeisreplacenotmerge —— running--scope "spark:app:write"bare will wipe out all existing scopes. The correct approach is to merge the spark scope into your complete scope stringand log in together:
feishu-cli auth check --scope "spark:app:write" # 先预检当前是否已有
feishu-cli auth login --scope "<你现有的全部 scope> spark:app:read spark:app:write"
Typical flow (three-step deployment)
# 1. 创建一个 HTML 应用,拿 app_id(CLI 已剥掉飞书响应的 data 外层,jq 路径为 .app.app_id)
feishu-cli apps create --name "我的页面" --app-type HTML
# 2. 把 HTML 目录/文件打包发布,返回访问 URL(jq 路径为 .url)
feishu-cli apps html-publish --app-id app_xxx --path ./dist
# 3. 设访问范围(默认创建后通常仅自己可见)
feishu-cli apps access-scope-set --app-id app_xxx --scope tenant
Command quick reference
1. Create app apps create
feishu-cli apps create --name "我的页面" --app-type HTML
feishu-cli apps create --name "Dashboard" --app-type HTML --description "数据看板" --icon-url https://...
feishu-cli apps create --name x --app-type HTML --dry-run # 只看将要发的请求
--app-typecurrently only supportsHTML- take
.app.app_idfrom the response for use in subsequent commands (the CLI has already stripped the Feishu response'sdataouter layer, so it is not.data.app.app_id)
2. Publish HTML apps html-publish(one-click deployment)
feishu-cli apps html-publish --app-id app_xxx --path ./dist # 目录形态
feishu-cli apps html-publish --app-id app_xxx --path ./index.html # 单文件形态
feishu-cli apps html-publish --app-id app_xxx --path ./dist --dry-run # 看打包清单 + 凭证扫描
feishu-cli apps html-publish --app-id app_xxx --path ./dist --allow-sensitive # 放行凭证文件
- Must have index.html: in directory form, the root directory must contain
index.html; in single-file form, the file name must be exactlyindex.html(Miaoda uses it as the app entry point) - Packaging method:
--pathis packaged entirely into a single in-memory tar.gz and uploaded in a single multipart request; uncompressed ≤ 200MB, packaged tar.gz ≤ 20MB, single.htmlfile ≤ 10MB (hard constraint on the Miaoda server side; when exceeded, the client intercepts in advance and names the file,--dry-runbackfillsoversize_html) - Credential file guard: by default blocks
.env/.env.*/.npmrc/.netrc/.git-credentials/.aws/credentials/.docker/config.json/.kube/config, and a hit exits non-zero (--dry-runis also blocked); if you really want to publish, add--allow-sensitive - take
.urlfrom the response, which is the access address (the CLI only whitelist-extracts the single field url)
3. Modify app apps update
feishu-cli apps update --app-id app_xxx --name "新名字"
feishu-cli apps update --app-id app_xxx --description "更新后的描述" # --name / --description 至少一个
4. Access scope apps access-scope-get / apps access-scope-set
feishu-cli apps access-scope-get --app-id app_xxx
feishu-cli apps access-scope-set --app-id app_xxx --scope tenant # 组织内可见
feishu-cli apps access-scope-set --app-id app_xxx --scope public --require-login=true # 互联网公开(require-login 必填)
feishu-cli apps access-scope-set --app-id app_xxx --scope specific \
--targets '[{"type":"user","id":"ou_xxx"},{"type":"department","id":"od_xxx"},{"type":"chat","id":"oc_xxx"}]'
feishu-cli apps access-scope-set --app-id app_xxx --scope specific \
--targets '[{"type":"user","id":"ou_xxx"}]' --apply-enabled --approver ou_appr # 开放申请 + 审批人
--scopechoose one of three:specific(specific people, maps to backendRange)/public(public on the internet, maps toAll)/tenant(within the organization, maps toTenant)specific: must be configured with--targets(unified format; when sending the request it is automatically split into the backend's users/departments/chats)public: must explicitly provide--require-login(true/false, cannot rely on the default)tenant: does not accept other flags
Where does app_id come from
- the one you just
apps create: take it from the returned.app.app_id(the CLI has already stripped thedataouter layer) - someone else's/existing app: have the user provide the Miaoda app link, and extract it from
https://miaoda.feishu.cn/app/app_xxxin/app/the segment afterapp_xxxstring
Output and troubleshooting
- All commands support
--format json|pretty|table|ndjson|csv+--jq, write commands support--dry-run(--dry-runand likewise respect--format/--jq) - The output has already stripped the Feishu response's
dataouter layer:apps createis directly{"app":{"app_id":...}}(use jq with.app.app_id),apps html-publishdirectly is{"url":...}(use jq.url) - Business error
code=90002= app does not exist or no access permission (verify app_id);code=90001= tar.gz uploaded successfully but server-side build failed (use--dry-runto check the package file list) - When an insufficient scope error occurs:
feishu-cli auth check --scope "spark:app:write"pre-check, then merge the full scope as per the "Prerequisites" above and log in again