UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

110 lines (109 loc) 7.59 kB
import type { PointDiffEntry } from "../local-config"; /** * 删除(点位 / 属性)感知闸口 + 三档授权模型。 * * 定位(务必如实):这套**最大化"让用户感知删除 + 逼守约定的 AI 喊人",不是技术强制** * 唯一硬保证:删除不会在第一次调用就发生(exit 2,fail-closed);CLI 在 AI 下游,保证不了 * 那第二步授权是「人」做的。真正"挡任何 AI"只能放 harness(之上)或后端(之外),不在此。 * 详见 docs/superpowers/specs/2026-06-17-ai-property-deletion-gate-design.md。 */ /** 汇总一份 diff 里所有「会删除的东西」为 canonical token:点位 `<type>[<key>]`、属性 `<type>[<key>].<array>.<id>`*/ export declare function deletionTokens(diffs: PointDiffEntry[]): string[]; /** * nonce 绑死「本次删除集 + 所属插件」:tokens 或 pluginId 任一变 → nonce 变 → 旧 grant 失效。 * pluginId 进 hash 输入(而非可见 token)→ 授权按工程隔离:A、B 两个工程即便删除集逐字节相同, * nonce 也不同,grant 不会跨工程串用;allow-delete 仍只按完整 nonce 匹配、无需知道 pluginId。 * 稳定排序后取 sha256 前 12 位。 */ export declare function computeNonce(tokens: string[], pluginId: string): string; /** 项目 `<cwd>/.lpm/config.json` 或全局 `~/.lpm/config.json` 任一 `allowDelete:true` → standing allow。 */ export declare function hasStandingAllow(cwd?: string): boolean; /** 分别返回项目 / 全局 standing allow 的开启状态(供 `lpm allow-delete --status`)。 */ export declare function getStandingAllowStatus(cwd?: string): { project: boolean; global: boolean; }; export type StandingScope = 'project' | 'global'; /** 设/清 standing allow(高风险弃权——调用方负责在设置当下打风险警示 + 取用户确认)。返回写入的文件路径。 */ export declare function setStandingAllow(scope: StandingScope, allow: boolean, cwd?: string): string; /** * 写一次性删除授权(由 `lpm allow-delete` 在用户确认后调用)。返回写入路径。 * cwd 必须是插件工程根,否则 throw——与 writePendingDeletion / setStandingAllow 一致,绝不在错误目录 litter .lpm/。 * 调用方(allow-delete)负责在 token 兜底流里 catch:那条流靠回传 grant token 放行,本地 grant 文件对它无用。 */ export declare function writeDeleteGrant(nonce: string, tokens: string[], cwd?: string): string; /** 命中则消费(删除授权文件)并返回 true;一次性、用完即焚。 */ export declare function consumeDeleteGrant(nonce: string, cwd?: string): boolean; /** 授权是否存在(不消费)——`set` 用它「放行但不消费」,把一次性消费留给 `update` 的真推送。 */ export declare function hasDeleteGrant(nonce: string, cwd?: string): boolean; export interface DeletionTokenPayload { v: 1; pluginId: string; nonce: string; tokens: string[]; /** 仅供 allow-delete 展示权威清单的精简 diff(type/key/name/status/removedProperties),不含字段级 changes。 */ diffs: PointDiffEntry[]; } /** 把「本次删除集 + 所属插件」编码成自包含、可 copy-paste 的 token(`LPMDEL1.<base64url(payload)>`)。 */ export declare function encodeDeletionToken(pluginId: string, diffs: PointDiffEntry[]): string; /** 解码 + 自校验(nonce 必须 = computeNonce(tokens, pluginId))。破损 / 篡改 / 非 token / 空 → undefined。 */ export declare function decodeDeletionToken(token: string | undefined): DeletionTokenPayload | undefined; /** * grant token 是否授权「此刻这批删除」:解码 + 校验 token 的 nonce 等于用「当前真实删除集 + pluginId」 * 重算的 nonce。token 多删 / 少删 / 属于别的工程 → nonce 不等 → 不放行。 */ export declare function deletionGrantTokenPasses(token: string | undefined, pluginId: string, diffs: PointDiffEntry[]): boolean; export interface PendingDeletion { nonce: string; tokens: string[]; /** 被拦时的删除条目(只留展示所需字段),供 `lpm allow-delete` 打权威清单——不依赖重算。 */ diffs: PointDiffEntry[]; /** 所属插件——供 allow-delete 的 nonce 路径重新编出 grant token G(沙箱回传用)。旧记录可能缺省。 */ pluginId?: string; } /** 闸口拦下时持久化本次删除清单(`set` 在写本地前就 exit 2,draft 未落盘,故 allow-delete 不能靠重算)。 */ export declare function writePendingDeletion(nonce: string, diffs: PointDiffEntry[], pluginId?: string, cwd?: string): void; export declare function readPendingDeletion(nonce: string, cwd?: string): PendingDeletion | undefined; export declare function removePendingDeletion(nonce: string, cwd?: string): void; export interface DeletionGateResult { tokens: string[]; nonce: string; hasDeletion: boolean; /** 是否放行:无删除、或 standing allow 命中、或一次性 grant 命中(已消费)、或 grant token 校验通过。 */ pass: boolean; via: 'none' | 'standing' | 'grant' | 'grant-token'; } /** * 评估删除闸口。无删除 → pass=true / hasDeletion=false。调用方:`!pass && hasDeletion` → 打 notice + exit 2。 * 放行优先级:standing allow → grant token(无状态、跨沙箱)→ 本地一次性 grant 文件。 * `opts.consume`(默认 true):本地 grant **文件**命中时是否消费(grant token 是无状态的,本就不消费)。 * `update`(不可逆推送)消费;`set`(可逆写本地)只「探测不消费」,把一次性消费留给 update——授权一次两步都过。 * `opts.grantToken`:用户 `allow-delete` 打印、经命令行 `--delete-grant` 回传的 grant token(沙箱场景)。 */ export declare function tryPassDeletionGate(diffs: PointDiffEntry[], pluginId: string, cwd?: string, opts?: { consume?: boolean; grantToken?: string; }): DeletionGateResult; export declare function countDeletions(diffs: PointDiffEntry[]): { points: number; properties: number; }; /** * 顶部汇总横幅——「扫一眼也漏不掉」。无删除返回 ''。 * 先打**总数**再分维度,避免「0 个点位、…」开头那个 0 抢视觉、让人误读成"没删东西" * (只删属性、点位本身没删时尤其会被埋)。 */ export declare function deletionBanner(diffs: PointDiffEntry[]): string; /** 删除清单两段:整点位 / 属性。每段为空则不出现。 */ export declare function formatDeletionLines(diffs: PointDiffEntry[]): string[]; /** * 删除被拦时写 stderr。文案承重:**把授权指派给「用户」、不给 AI 递自助解锁配方** * `lpm allow-delete` 由用户在自己终端跑、打权威清单 + 强制确认。 * * 主路径(同一项目目录):pending / grant 落项目 <cwd>/.lpm/(沙箱 bind-mount 的共享 workdir, * 沙箱内外同绝对路径可见)。打印 `lpm --cwd <abs> allow-delete <nonce>`——带 `--cwd` 让用户在任意 * 目录都能跑、且必定落到同一个项目 .lpm/(防止在错的目录跑找不到 pending)。 * 兜底(极少:项目目录都不共享):自包含 token——用户 `allow-delete <token>` → grant token G → * agent `update --delete-grant <G>`,全程不依赖任何共享目录。`pluginId` 进 pending + token。 */ export declare function writeDeletionBlockNotice(diffs: PointDiffEntry[], nonce: string, pluginId: string, cwd?: string): void;