UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

163 lines (105 loc) 12.4 kB
--- name: meegle-plugin-shared version: 1.0.0 description: "Meegle 插件开发共享基础:插件工程识别、Device Code OAuth 认证、Token 管理、安全规则。所有 meegle-plugin-* skill 的公共前置依赖,不单独对用户触发。" metadata: requires: bins: ["npx"] cliHelp: "lpm --help" --- # Meegle 插件开发共享规则 > 本文件只给通用前置规则(根原则 / 工程识别 / 认证 / 安全)。引用的 references 在下文各小节被触发条件时就地点名,**不要在进入 skill 的第一轮就预加载** ## 三条根原则 ### 根原则 1:无源即停(输出可溯源) AI 输出每一项有业务语义的内容,必须可追溯到合法信息源;找不到 → 立即停下问用户。 ### 根原则 2:数据完整性(写入前先拿基线) 任何对远端配置的写入都是全量覆盖,提交前以远端基线为参照、对比差异、对减少项让用户确认。 ### 根原则 3:真实数据动作显式确认 任何对远端产生真实影响的动作,执行前向用户列出"将要做什么 + 影响哪些数据",等用户显式同意才执行。 **五类必须先确认的动作**1. **创建插件**(在 Meegle 后台生成插件记录,pluginId 从此绑定) 2. **配置点位**`local-config set` 等,把 plugin.config.json 全量推到后台) 3. **配置基本信息**(更新插件名称 / 简短描述 / 详细描述 / 分类等元信息) 4. **发布插件**`publish`,发到 Meegle 市场,用户可见,最不可逆) 5. **申请 OpenAPI 权限**`lpm perm apply` 前列出即将申请的 scope 清单、等用户点头)——给插件 app 授 OpenAPI scope 是对 app 权限的真实变更:拓宽这个插件 app 能做的事、可能要走后台审批、在 app 的权限列表里可见,和创建 / 配置点位 / 配置基本信息 / 发布同级 > 遇到未分类的 CRITICAL → 按"无源即停"默认处理(最保守动作:停下问用户)。 ## 插件工程识别 **判定**:cwd 存在 `plugin.config.json` 且含 `pluginId``MII_` 开头)。 **识别后**:CLI 自动从配置读 `siteDomain` / `pluginId` / `pluginSecret` / `resources`,skill 不需要手动传。这些字段由 CLI 维护(`local-config set` 一步完成"推+拉+生成模板"),skill 只读、不直接 Edit/Write。 **执行约定(CRITICAL — 工作目录锚定,本规则唯一权威源)**:agent 的 shell 在每条命令之间**不保证保留 cwd**,且 primary cwd 不一定等于插件工程目录(插件可能嵌在子目录、或你正坐在别的仓里)。因此**每一条 `lpm` 命令都带 `--cwd <插件工程绝对路径>` 前缀**,例如 `lpm --cwd /abs/path/to/plugin local-config diff``--cwd` 让 CLI 在执行前先 chdir 到插件根,`plugin.config.json``.lpm-cache` 都按它解析;目录不对时 CLI 直接 fail-fast(错误信息带 `--cwd` 提示),不会静默跑到错的地方。识别 / `create` 通过后**立即把插件工程的绝对路径记下来**(有 checkpoint 时用 `lpm --cwd <abs> ai state set` 写进 `.lpm-cache/state.json`),之后每条命令都用它。 ## 认证 Token 由 CLI 按 `siteDomain` 分域管理。 **`--site-domain` 传参规则**- `create` / `login` 由 AI 传 `--site-domain`(用户未指定时**先跑 `lpm whoami`,一切以它列出的真实登录站点为准**:列出了已登录站点就原样列出全部真实站点供用户挑选——忽略 `← 建议` 标记、不预选、不附自定义项;仅当 whoami 报「未登录」时才用「飞书项目 / Meegle / 自定义域名」通用项让用户贴 URL) - 其他命令不传 `--site-domain`,CLI 自动从 `plugin.config.json` 读取 **前置约定**:用户在接入插件开发前应已执行过 `lpm login`(见接入文档 Step 2)。skill 直接调 CLI,token 有效性由 CLI 在命令运行时判定。 **遇 auth 错误时**:CLI 会 stderr 打印完整登录指引(方式 A token 粘贴 / 方式 B Device Code OAuth + 可执行命令)。AI **逐字转呈** 给用户,由用户执行 `lpm login` 后重试命令。 ## 安全规则 - **CLI 维护的文件/目录**`.lpm/` 内部目录、`plugin.config.json`)只通过 CLI 命令操作;`plugin.config.json` 的点位数据由 `local-config set`(本地暂存)+ `update --source-type=local`(推远端 + 拉模板代码)两步维护,skill 读它、不直接写 - **禁止输出密钥**(accessToken、pluginSecret)到终端明文 ### 自建后端红线 插件的"自建后端那一半"(webhook 接收 / 调 OpenAPI / 写回,由 feature「→ 后端那一半」编排、服务端代码交后端会话写): - **OpenAPI 鉴权凭据进后端 env,不进前端 bundle**`AUTHORING.md §8.1`)——后端代码里只写 `process.env.X` 引用,真值由用户设进后端运行环境;不从 `plugin.config.json` grep / decrypt `pluginSecret` 粘进任何地方 - **后端运行时拿 Meego 数据只走 OpenAPI**`AUTHORING.md §8.2`)——不依赖 `lark-project` skill、不 `child_process.exec('lpm ...')``lark-project` / `lpm` 是开发期本地工具,没有面向运行时的鉴权与稳定性 SLA ### 全量提交约束(CRITICAL — 防数据丢失,本规则唯一权威源) `local-config set`**全量替换**——提交什么就存什么,远端不做合并。**遗漏任何现有点位都会导致该点位被永久删除。** **操作流程(不可省略任何一步)**1.`local-config get --remote` 获取远端完整配置作为基础 2. 在完整配置基础上做局部增删改 3. 将修改后的**完整配置**作为 draft 三步提交:`local-config set --from <draft>`(本地校验 + 写入 `point.config.local.json`)→ `local-config diff`(预览改动;有删除时 exit 2 必须转呈用户获取确认)→ `update --source-type=local`(推远端 + 拉回模板代码) **底线**:必须以 `get --remote` 的基线构造完整 draft 再提交;不能只传变更部分,不能直接 Edit/Write `plugin.config.json`### `local-config set` 用户确认 gate(CRITICAL) `local-config set` 是全量替换、不可静默回滚,**执行前 MUST 列 ADDED / MODIFIED / DELETED 三类差异清单 + 等用户明示同意**("确认" / "OK" / "推送")才能跑。用户拒绝 / 提改 → 回上游改 draft 重跑,不就地改绕过。 清单格式、Checkpoint 恢复、变更字段对比展示、与 A2 删除 gate 分工见 [`feature-config-apply.md §A0`](feature-config-apply.md)(格式唯一权威源)。 ### 删除点位前置检查协议(CRITICAL — 所有调用路径前置 gate) 减少点位(draft / 本地配置丢了远端还在的点位)在三处都会被**硬拦下、`exit 2`**,都打印同一份清单(`⚠️ DELETION_REQUIRES_CONFIRMATION` 标题 + 每项 `type[key] "name"`): - `local-config set`:写本地前比对远端,丢点位 → exit 2,**不写本地** - `local-config diff`:预览,丢点位 → exit 2 - `update --source-type=local`:推送前比对远端,丢点位 → exit 2,**不推送** **任一处 exit 2 → 立即停止**:把 CLI 的 stderr **逐字转呈给用户**,等用户明示同意删除具体点位。不要静默删除、不要"帮用户总结"、不要自己判断"这是废弃点位应该删"。 用户明示同意后,才用 `--allow-delete` 绕过那一步重跑(`set` / `update` 均支持;`diff` 是纯预览无此参数)。**没有用户明示同意,绝不得加 `--allow-delete`**——删除远端点位不可逆。远端不可达时这些检查降级为告警放行(push 命中后端会再校)。 ### 无源即停(CRITICAL — 根原则 1 的执行细则) 适用范围:代码、API 调用、字段值、配置参数等所有 AI 输出场景。 **合法信息源**:用户显式输入 / 粘贴、用户授权的 `<PLACEHOLDER: ...>`、权威工具的精确返回(点位标准能力 doc、飞书项目知识 MCP、CLI 实测输出)、工程已有代码/配置。 **非法信息源**(典型的"看起来合理但编造"):AI 经验类比("通常 SDK 都有 getX/setX")、概念脑补(从 schema 字段名推断 SDK 方法名)、命名约定猜测("通常驼峰所以这里也是")。 **遇到无源时的统一动作(强制话术 + 强制停产出)**: 触发"无源即停"时 AI **MUST**1. **立即停止产出**:本轮不得再调任何产出工具(Write / Edit 写入点位 JSON、代码文件、CLI 命令等)。"停下来问用户" ≠ "嘴上停、同时继续写"。 2. **逐字输出下述话术**(允许填入具体内容、可选项 A/B/C 可剪裁为场景相关的版本,但"无法找到 … 依据 / 可选 / 禁止猜测 / 请告诉我选哪个" 四要素必须齐全): ``` 我无法在合法信息源中找到 "<具体内容>" 的依据。 可选: A. 跳过此项,输出 TODO 占位(推荐——不写错的好过写错的) B. 你提供真实值 / 示例 / 文档链接 C. 提供检索关键词,我重新查(可能找错了) 禁止我自行猜测——猜出来的内容会通过表层校验但运行时全废。 请告诉我选哪个。 ``` 3. **等待用户明确选择 A/B/C 之一** 才能继续;不得"选 A 同时继续产出其他项"的并发动作(同一批产出里任一项无源,整批阻塞)。 ## MCP 检索技巧(飞书项目知识 MCP) `mcp__feishu-project-knowledge__search_meegle_plugin_docs` 是 schema / point-type doc 没覆盖时的 fallback 主力。**实测有效模板**(按场景挑用): | 场景 | 关键词模板 | 实测示例 | |---|---|---| | **点位上下文 API**(getContext / 字段值读写 / watch 等) | `<PointName 英文> 上下文` | `Page 上下文` / `CustomField 上下文` / `Control 上下文` | | **点位整套介绍**(配置 + 开发 + 数据通信) | 直接搜 PointName 英文 | `CustomField` / `LiteAppComponent` / `Schedule` / `Button` | | **总览 / 选型** | doc 入口词 | `添加插件功能` / `添加构成` / `客户端开发概述` | | **具体功能** | 业务功能词 + 技术词 | `表格列 控件 DSL` / `字段值 读取` / `自动化连接器 配置` / `事件订阅` | | **OpenAPI 接口** | API 中文名 + "接口" | `获取工作项详情 接口` / `创建自定义字段 接口` | | **错码 / 限制 / 沙箱** | 直接关键词 | `客户端沙箱限制` / `客户端资源限制` / `saveFieldValue 错码` | **通用搜索心法**- **MCP 是 fuzzy 文本检索**,描述性查询(业务功能 + 技术词)比单查 API 名命中相关 doc 更全 - **用官方术语**`工作项`(不是"任务")/ `空间`(不是"项目")/ `字段类型`(不是"自定义字段类型")/ `控件`(不是"组件") - **中英双语** 高难关键词同时拟(如 `排期 schedule` / `字段配置 field config`- **搜不到 ≠ 不存在** — 改换近义词 / 上一层概念 / 业务场景描述 重试 1-2 次;分概念查再汇总 **MCP 缓存协议**(防 context 爆炸 — 本节是权威源,其他 doc 引用本节不复述): 1. MCP 返回后立即把**完整原文** Write 到 `.lpm-cache/mcp/<slug>.md`,按子主题用 `## <主题>` 分节 2. **chat 回复只给路径 + ≤100 字摘要**,原文不回流 3. **重访同一查询**`lpm ai peek .lpm-cache/mcp/<slug>.md "<章节标题>"` 按需取节,不要 Read 整份;7 天以上视为过期重拉 `.lpm-cache/` 由 CLI 在 create/init 时自动写入 `.gitignore`,在 `local-config set` / `publish` 成功后按子目录自动清理,AI 只负责写入、不负责清理。 ## Checkpoint(进度追踪) state.json 的读写走 `lpm ai state get` / `lpm ai state set '<json>'`——CLI 经 `workspacePaths()` 把路径解析到插件根、`assertPluginRoot` 守卫,配合 `--cwd` 在任意目录都落到插件的 `.lpm-cache/state.json`**判断规则**:子 skill 执行前用 `lpm --cwd <projectRoot> ai state get` 读 checkpoint: - **有输出** → 处于 workflow 编排中,按 [`references/checkpoint.md`](checkpoint.md) 的协议在每个 CLI 命令前后用 `lpm --cwd <projectRoot> ai state set` 更新 checkpoint - **空输出** → 独立调用,不写 checkpoint ## 错误处理 遇到错误 → Read [`references/errors.md`](errors.md) 查对应处理方式。