@lark-project/cli
Version:
飞书项目插件开发工具
265 lines (187 loc) • 13.5 kB
Markdown
# Meegle 插件开发 CLI 命令详解
> 本文件是 [`../SKILL.md`](../SKILL.md) 的命令字典补充。SKILL.md 里 A 类速查表的"详见"列均指向本文。
## CLI 基础
**命令前缀**:所有命令均通过以下方式调用:
```bash
lpm <command> [options]
```
**工程识别**:大部分命令需要在插件工程目录下执行(存在 `plugin.config.json`)。以下命令例外,可在任意目录执行:
- `login` — 全局认证
- `whoami` — 查看登录态
- `create` — 创建新插件工程
**配置来源**:`siteDomain`、`pluginId` 等信息自动从 `plugin.config.json` 读取,无需手动传入。
---
## 命令详解
### 登录认证
```bash
# 方式 A:浏览器 OAuth 授权(交互式)
lpm login --site-domain <域名>
# 方式 B:直接设置永久 Developer Token(推荐)
lpm login --site-domain <域名> --token <developer_token>
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--site-domain` | 是 | Meegle 站点域名,含协议(如 `https://meego.feishu-boe.cn`) |
| `--token` | 否 | 永久 Developer Token,有则跳过 OAuth |
### 查看登录态
```bash
lpm whoami
```
只读,列出已登录的 Meegle 站点,并标记建议用于 `lpm create` 的站点(最近登录优先)。无参数,可在任意目录执行,**绝不打印 token**。AI 取 `siteDomain` 前可先跑它,按输出与用户确认。
### 创建插件
```bash
lpm create --site-domain <域名> --name "<名称>" [--force]
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--site-domain` | 是 | 站点域名 |
| `--name` | 是 | 插件名称 |
| `--description` | 否 | 短描述 |
| `--force` | 否 | 已在插件目录中时跳过确认 |
**产出**:在当前目录创建插件工程,生成 `plugin.config.json` + `src/` + `node_modules/`。
### 启动调试
```bash
lpm start --auto
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--auto` | 否 | 自动打开浏览器调试页面(推荐) |
| `--source-type` | 否 | `local`(用本地配置)或 `remote`(用远端配置,默认) |
**行为**:启动 webpack-dev-server + 热更新,`--auto` 时自动拼接调试 URL 并在浏览器中打开。
### 同步配置
```bash
# 本地 → 远端 + 拉回模板(推送本地点位配置 + 自动拉回远端配置和模板代码,一条命令完成)
lpm update --source-type=local
# 仅远端 → 本地(只拉取,不推送)
lpm update
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--source-type` | 否 | `local`(推送到远端 + 自动拉回模板,常规配置流程用这个)或默认不传(仅从远端拉取) |
| `--allow-delete` | 否 | 用户**明示同意删除**后才加:绕过 push 前的删除闸口(本地配置丢了远端点位时默认 exit 2 拦下)。删除不可逆,无用户明示同意不得加 |
**行为**:
- `--source-type=local`:push 前先跑删除闸口(本地相比远端丢点位且未带 `--allow-delete` → exit 2、不推送),通过后把 `point.config.local.json` 推到远端,再**自动**拉回远端最新配置 + 后端模板代码,刷新 `plugin.config.json.resources` 和 `src/features/<resourceId>/index.tsx`
- 不传 `--source-type`:从远端拉取最新点位定义,生成/更新代码模板,更新 `plugin.config.json` 的 resources;仅在本地没有未推送的改动时使用
> Stage Config 配置流程:`local-config set --from <draft>`(本地校验 + 暂存到 `point.config.local.json`)→ `update --source-type=local`(推送 + 拉回模板代码)。两步跑完,本地 + 远端 + 模板代码都同步完成。
### 构建
```bash
lpm build [--zip]
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--zip` | 否 | 构建后打包为 zip |
| `--source-type` | 否 | `local` 或 `remote` |
**产出**:`build/` 目录。
### 构建发布
**完整发布是两步串行操作**,不可跳步:
```bash
# 第 1 步:构建 + 上传产物
lpm release
# 输出:Artifact version: <artifactVersion>
# 第 2 步:版本发布(必须使用第 1 步输出的 artifactVersion)
lpm publish \
--artifact-version <artifactVersion> \
--release-notes "<版本描述>"
```
**release 参数**:
| 参数 | 必填 | 说明 |
|------|------|------|
| `--source-type` | 否 | `local` 或 `remote` |
**publish 参数**:
| 参数 | 必填 | 说明 |
|------|------|------|
| `--artifact-version` | **是** | 来自 `release` 输出的构建产物版本号 |
| `--release-notes` | 是 | 版本描述 / release notes(中文) |
| `--version` | 否 | 语义化版本号,不传则自动 patch +1 |
| `--store` | 否 | `yes`(发布到商店)/ `no`(仅内部)。**不传则继承上一版本 visibility**;首次发布默认 `yes` |
| `--upgrade` | 否 | CLI 默认 `manual`;AI 不主动传,仅当用户明确要求 `all`(自动升级所有安装)/ `limit`(按范围)时才显式传 |
**关键**:`--artifact-version` 必须从 `release` 的 stdout 中提取(正则:`/Artifact version: (\S+)/`),不可编造。
### 修改基本信息
```bash
lpm update-description \
--name "<插件名称>" \
--short-description "<短描述>" \
--detail-description "<详情描述>" \
--category-ids "<id1>,<id2>"
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--name` | 否 | 插件名称 |
| `--short-description` | 否 | 短描述(纯文本) |
| `--detail-description` | 否 | 详情描述(纯文本,CLI 自动转富文本) |
| `--category-ids` | 否 | 分类 ID 列表,逗号分隔(从 `list-categories` 获取) |
| `--icon` | 否 | 图标 URL |
> 所有参数均为可选,只传需要修改的字段即可。
### 点位配置管理
```bash
# 写入远端配置到 .lpm-cache/config/remote.json
lpm local-config get --remote
# 把 draft 写入本地 point.config.local.json(全量替换语义,本地 staging;不推送远端)。
# 写本地前跑删除闸口:draft 相比远端丢点位且未带 --allow-delete → exit 2、不写本地。
# 成功后 CLI 会:
# 1) 把已消费的 draft 和 .lpm-cache/config/remote.json 基线移出活跃目录(你无需手动清理)
# 2) 把新的完整配置写入工程根 point.config.local.json
lpm local-config set --from .lpm-cache/config/draft-<timestamp>.json
# 用户明示同意删除后才加 --allow-delete 绕过删除闸口:
lpm local-config set --from .lpm-cache/config/draft-<timestamp>.json --allow-delete
# 预览本地 vs 远端差异(CRITICAL — 推送前必跑)
# stdout:[ADDED] / [MODIFIED] / [DELETED] 列表
# exit 0: 无删除,可直接推送
# exit 2: 有删除,stderr 打印 DELETION_REQUIRES_CONFIRMATION 清单 + 指引,AI 必须转呈用户获得明示确认
lpm local-config diff
```
- `get` / `schema` 的 stdout 只回一行相对路径,JSON 落在 `.lpm-cache/` 里;AI 用 `lpm ai peek` 按需取片段,不要把整份 JSON 带进对话 context
- `set` 接受 `--from <path>`(相对或绝对路径)+ `--allow-delete`(删除确认后才加),不推送远端——推送走 `update --source-type=local`
- `diff` 无参数,对比 `point.config.local.json` 与远端当前配置
> **CRITICAL — 全量提交 + 删除确认**:`local-config set` 是全量覆盖语义,减少点位不可逆。删除在 `set` / `diff` / `update --source-type=local` 三处都会 exit 2 硬拦——必须把 stderr 逐字转呈用户,等用户明示同意删除具体点位,才用 `--allow-delete`(set / update 支持)绕过那一步重跑。完整规则见 [`../../meegle-plugin/references/shared.md`](../../meegle-plugin/references/shared.md) 的"全量提交约束"和"删除点位前置检查协议"两节。
### AI 辅助命令(`lpm ai *`)
完整列表跑 `lpm ai --help`。各命令具体用法在用到的 workflow doc 内 inline 展示(如 `lpm ai peek` 见 [`../../meegle-plugin/references/feature-config-plan.md`](../../meegle-plugin/references/feature-config-plan.md))。
**🔒 粒度铁律**(draft 编辑通用约定):peek 整点位 → AI 改 → set / Write 整点位回去;不钻 `extension[...].sub_comp[...]` 深叶子路径。
### 生成 Schema
```bash
lpm schema
```
CLI 把 JSON Schema 写入 `.lpm-cache/schema/point-schema.json` 并在 stdout 回显路径。用 `lpm ai peek` 按需切片,**不要 Read 整个文件**(约 30K tokens)。
### 清理工作区(兜底)
```bash
lpm workspace clean --scope=all # 清全部(保留 state.json)
lpm workspace clean --scope=mcp # 只清 MCP 缓存
lpm workspace clean --include-state # 连 workflow checkpoint 一起清
```
`.lpm-cache/` 的生命周期由 CLI 自动维护——`local-config set` / `publish` 成功时会按子目录清理。仅在中断后需要强制重置时才手动调用 `workspace clean`。
### 分类列表
```bash
lpm list-categories [--type plugin]
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--type` | 否 | `plugin`(默认)/ `ai` |
输出 JSON 数组,每项含 `id` 和 `name`,用于 `update-description --category-ids`。
### 权限管理
`lpm perm` 管理**本插件对应 app 的 OpenAPI 权限 scope**(不操作 Meegle 产品业务数据)。在自建后端要调 OpenAPI 时用 —— 调用前先把要用的 scope 申请上。
```bash
# 查:已开通可调的 scope(granted)+ 还能申请的(applicable),各含覆盖的 OpenAPI
lpm perm list
# 校验:要调的这些 OpenAPI 能不能调 / 缺哪些权限(接口 key / 短名 / 中文名均可)
lpm perm check --apis <api1,api2,...>
# 申请:把这些权限 key 加到本插件(已开通的自动跳过);先 perm check 看缺哪些 scope
lpm perm apply --scopes <key1,key2,...>
```
| 命令 | 参数 | 说明 |
|------|------|------|
| `perm list` | `[--raw]`;`[--plugin <id> --site-domain <url>]`(均可选,成对) | 默认 stdout 输出 CLI 派生视图:`{ appType, granted: [{scope,name,openapis:[{key,name,url}]}]`(已开通、当前可调,= `perm_info ∩ perm_meta`)`, applicable: [{scope,name,openapis}]`(还能申请的,= `perm_meta − perm_info`;`ai_node`/`ai_field` 省略,固定集不可申请)`}`。可行性看 `granted ∪ applicable` 天花板;实行看 `granted`、缺的从 `applicable` 申请。`--raw` 出后端原始 `{ perm_info, perm_meta }`(调试用) |
| `perm check` | `--apis <a,b,c>`(必填,逗号分隔的 OpenAPI 标识:resource key / 短名 / 中文名);`[--plugin <id> --site-domain <url>]`(可选,成对) | CLI 把每个接口反查到归属 scope 再比对已开通,stdout 输出判决 JSON:`satisfied`(归属 scope 已在 granted)/ `needApply: [{scope,name,apis}]`(normal 缺、可申请)/ `infeasible: [{api,reason}]`(AI 应用缺固定集 scope,做不了)/ `unknown`(此 app 目录里查无此接口)/ `ambiguous: [{api,candidates}]`(中文名撞多个 scope,改用 resource key 消歧) |
| `perm apply` | `--scopes <a,b,c>`(必填,逗号分隔的权限 key);`[--plugin <id> --site-domain <url>]`(可选,成对) | 先本地对照可申请目录(`perm_meta`)校验,非法 key 直接 `exit 1` 并提示用 `perm list`;合法则申请(`operate_type=1` 新增),stdout 输出 `{ perm_info: [...] }` 的 JSON(申请后该插件已勾选的权限列表) |
- **插件定位优先级**:`--plugin/--site-domain` 显式指定 > 插件工程目录的 `plugin.config.json` > 全局只读注册表(`~/.lpm/`,记录你碰过的插件,无 secret;单登录态 + 单插件时自动定位,多个时报错列出让你加 `--plugin`)。所以这几条命令**也能在插件工程目录之外跑**(如自己的后端代码仓);都是 token-only、不需 pluginSecret。从工程目录外定位时会在 stderr 回显 `perm <action>: plugin <id> @ <domain>`。鉴权走 `login` 拿到的 dev token,没登录会 stderr 提示。
- `perm apply` 是对 app 权限的真实变更——编排 skill(`meegle-plugin-backend`)调它前会先列出即将申请的 scope 等用户点头,见 [`../../meegle-plugin/references/shared.md`](../../meegle-plugin/references/shared.md) 的"必须先确认"清单。
- 需审核的 scope(`perm list --raw` 的 `perm_meta[].auditable_bit` 按位标记)申请后还要在开发者后台填申请原因、走审批;`perm apply` 只负责把 scope 加到草稿。AI 应用(`app_type=ai_node`/`ai_field`)的权限按点位类型自动管,`perm apply` 对它会报错。
---
## 命令依赖关系
典型顺序(前置依赖 → 后置操作):
1. `login` — 全局认证(一次性)
2. `create` — 创建插件工程(一次性)
3. `schema` → `local-config set --from <draft>` → `local-config diff` → `update --source-type=local` — 配置点位(三步:set 本地校验 + 暂存;diff 预览 + 删除确认 gate;update --source-type=local 推远端 + 拉回模板)
4. `start --auto` — 本地调试(依赖 step 3 的 `update`)
5. `update-description` — 修改基本信息(可随时)
6. `release` → `publish` — 构建发布(两步串行,不可跳)
按需(不在固定生命周期里):`perm check --apis ...`(要调的接口能不能调 / 缺哪些 scope)→ `perm apply --scopes ...` —— 自建后端要调某些 OpenAPI 时,先核可行性再把缺的 scope 申请上(编排见 `meegle-plugin-backend`)。