pi-lark-gateway 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/LICENSE +10 -0
- package/README.md +180 -0
- package/SECURITY.md +51 -0
- package/bin/pi-lark-gateway.js +26 -0
- package/deploy/dev.pi.lark-gateway.plist +19 -0
- package/deploy/logrotate.conf +11 -0
- package/deploy/pi-lark-gateway.service +21 -0
- package/deploy/service.env.example +8 -0
- package/docs/INSTALL.md +130 -0
- package/docs/MEDIA.md +72 -0
- package/docs/OPERATIONS.md +119 -0
- package/docs/PERMISSIONS.md +84 -0
- package/docs/RELEASING.md +116 -0
- package/npm-shrinkwrap.json +2842 -0
- package/package.json +53 -0
- package/scripts/backup.js +64 -0
- package/scripts/doctor.js +31 -0
- package/scripts/restart.js +23 -0
- package/src/agent/answer.js +24 -0
- package/src/agent/archive-store.js +103 -0
- package/src/agent/archive-summary.js +31 -0
- package/src/agent/image-compat.js +39 -0
- package/src/agent/index.js +44 -0
- package/src/agent/limiter.js +17 -0
- package/src/agent/run-control.js +28 -0
- package/src/agent/session-factory.js +26 -0
- package/src/agent/session-lifecycle.js +78 -0
- package/src/agent/sessions.js +20 -0
- package/src/agent/title.js +22 -0
- package/src/approvals/card.js +32 -0
- package/src/approvals/eligibility.js +13 -0
- package/src/approvals/index.js +3 -0
- package/src/approvals/service.js +97 -0
- package/src/config/grants.js +13 -0
- package/src/config/index.js +6 -0
- package/src/config/policy.js +23 -0
- package/src/config/schema.js +37 -0
- package/src/config/store.js +53 -0
- package/src/config/text-rules.js +22 -0
- package/src/config/watch.js +21 -0
- package/src/controls/index.js +51 -0
- package/src/controls/permissions.js +14 -0
- package/src/debug/index.js +64 -0
- package/src/gateway/index.js +91 -0
- package/src/gateway/route.js +31 -0
- package/src/index.js +26 -0
- package/src/lark/card-stream.js +123 -0
- package/src/lark/cards.js +15 -0
- package/src/lark/connection.js +24 -0
- package/src/lark/index.js +6 -0
- package/src/lark/messages.js +61 -0
- package/src/lark/metadata.js +26 -0
- package/src/lark/reactions.js +17 -0
- package/src/lark/resource-context.js +13 -0
- package/src/lark/resources.js +75 -0
- package/src/media/content.js +41 -0
- package/src/media/errors.js +17 -0
- package/src/media/files.js +82 -0
- package/src/media/index.js +4 -0
- package/src/media/service.js +53 -0
- package/src/media/tool.js +32 -0
- package/src/messages/card-pages.js +26 -0
- package/src/messages/card-response.js +76 -0
- package/src/messages/card.js +36 -0
- package/src/messages/handler.js +68 -0
- package/src/messages/index.js +4 -0
- package/src/messages/markdown-slices.js +34 -0
- package/src/messages/normalize.js +26 -0
- package/src/messages/response.js +15 -0
- package/src/messages/thread-store.js +21 -0
- package/src/onboarding/finish.js +12 -0
- package/src/onboarding/index.js +79 -0
- package/src/onboarding/manual.js +33 -0
- package/src/onboarding/permissions.js +45 -0
- package/src/onboarding/registration.js +72 -0
- package/src/progress/index.js +36 -0
- package/src/progress/timeline.js +110 -0
- package/src/progress/tool-preview.js +29 -0
- package/src/restart/command.js +45 -0
- package/src/restart/control.js +116 -0
- package/src/restart/index.js +6 -0
- package/src/restart/scheduler.js +39 -0
- package/src/runtime/index.js +11 -0
- package/src/runtime/supervise.js +25 -0
- package/src/storage/index.js +14 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0-rc.1 — GA candidate (not yet published)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Single-process limit of ten concurrent conversation tasks; FIFO waiting and per-conversation serialization.
|
|
7
|
+
- Idle session archiving after 30 days: isolated summaries, private persistence, cleanup recovery, and summary-based continuation.
|
|
8
|
+
- Manual Feishu setup with hidden secret input, local doctor, private stopped-service backups, and clean-home installation smoke test.
|
|
9
|
+
- CI matrix, checked source archive packaging with lockfile and source commit metadata; installation, operations, release and security documentation.
|
|
10
|
+
- npm runtime distribution with executable pi-lark-gateway CLI, generated shrinkwrap, tarball installation smoke and gated beta/latest publication.
|
|
11
|
+
- Branch-driven GitHub Actions releases: each release/x.y.z push generates x.y.z-beta.<run> on beta; a same-repository release PR merged into main publishes x.y.z on latest. Version manifests are stamped in staging without source commits.
|
|
12
|
+
- Reusable test matrix, exact-artifact smoke/verification, opt-in npm OIDC publication and draft-to-public GitHub Releases with immutable assets and integrity-checked retries.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Remove duplicate local release planners, packers and publishers after adopting the pinned shared Action; retain workflow contract tests, fail-closed source publication and isolated installation smoke fixtures, with package-content checks also applied to the Action's exact tarball.
|
|
16
|
+
- Constrain inline image previews with a 278px JSON 2.0 column instead of legacy compact-width image attributes; keep default card width and existing error fallback behavior.
|
|
17
|
+
- Use default-width reply cards with compact inline image previews, preserving full-image viewing on click.
|
|
18
|
+
- Embed outgoing images in the current response card instead of standalone image messages; serialize image/text updates, retain images through finalization and native fallback, and fail explicitly without an active card.
|
|
19
|
+
- Fix undefined image MIME errors with the installed Pi provider's flat ImageContent contract; normalize legacy image blocks on history replay without rewriting original JSONL, with real provider-serialization and SDK-resume regression tests.
|
|
20
|
+
- Receive image/file/audio/video attachments after admission, feed supported images to vision models, and add a current-conversation file-sending tool gated by tools:all, bounded private storage and safe paths; document resource permissions and media limits.
|
|
21
|
+
- Document audited Feishu application permissions and required event/callback setup; show the same checklist and app-specific permissions link after QR/manual onboarding, with offline `setup --permissions` and `--permissions-json` commands.
|
|
22
|
+
- Prefer native CardKit typewriter updates for assistant text in card mode, with per-card sequencing/throttling, stream finalization and automatic same-message fallback to legacy card updates; leave tool details immediate and normal replies unchanged.
|
|
23
|
+
- Recognize a leading Feishu `/restart` command at gateway ingress for owner/admins, accepting trailing remarks and plain rich-text messages without any model fallback, respecting access policy and waiting for confirmation plus all accepted output/cleanup before the deferred restart.
|
|
24
|
+
- Add `restart` CLI scheduling: wait for all accepted replies, card updates and cleanup before a delayed managed restart; coalesce requests and never force-kill the active response.
|
|
25
|
+
- Collapse adjacent same-kind card tool calls as `tool ×N`, without parentheses; show the full sanitized operation in a fenced Markdown block while the newest call remains the last visible timeline output, including after it completes. Collapse details only when subsequent text or a new tool appears, and preserve balanced code fences across byte-budgeted continuation cards.
|
|
26
|
+
- Card replies retain streamed assistant commentary, chronological tool statuses and final text without duplicating the SDK's aggregate answer. Live overflow reuses continuation cards; stopped/error cards preserve partial progress. Normal text reply mode is unchanged.
|
|
27
|
+
- Release workflow directly calls the SHA-pinned SokuRitszZ/npm-release-action for planning, packaging and publication; gateway-specific matrix tests and exact-tarball CLI smoke remain in this repository. Shared artifacts use bundle.json, SHA256SUMS and a source/ archive root.
|
|
28
|
+
- New installations require owner approval for unknown users; groups require a mention; tools default to disabled.
|
|
29
|
+
- Domestic Feishu only. International Lark setup/configuration is rejected.
|
|
30
|
+
- Direct dependency versions pinned; existing explicit access/tool settings are preserved.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Archival failures keep available original transcripts and do not block normal conversations. Resumed conversations invalidate stale cleanup plans.
|
|
34
|
+
- Setup now accurately describes whitelist approval rather than open access.
|
|
35
|
+
|
|
36
|
+
### Known limits
|
|
37
|
+
- Queue length/per-user rate limits are not bounded; ten-session concurrency is not a memory or cost quota.
|
|
38
|
+
- Deduplication is in memory; restart may replay work. Shutdown queue handling remains unchanged.
|
|
39
|
+
- Tool-enabled deployment is not sandboxed; approvals are not a host security boundary.
|
|
40
|
+
- Image/audio/file parsing is not implemented. Archived summaries are lossy.
|
|
41
|
+
- Scan registration uses an upstream interface with no public stability guarantee; manual setup is the fallback.
|
|
42
|
+
|
|
43
|
+
## 0.1.0
|
|
44
|
+
|
|
45
|
+
Initial locally deployed gateway: Feishu WebSocket, Pi SDK conversation sessions,
|
|
46
|
+
access approval, configuration reload, normal/card replies and response controls.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
All rights reserved.
|
|
2
|
+
|
|
3
|
+
No open-source license is granted for pi-lark-gateway at this time.
|
|
4
|
+
Use, redistribution, and publication require permission from the rights holder.
|
|
5
|
+
The package is marked UNLICENSED. Public npm distribution requires the rights
|
|
6
|
+
holder's explicit approval; npm availability does not itself grant a license.
|
|
7
|
+
|
|
8
|
+
Third-party dependencies retain their own licenses. This notice does not alter
|
|
9
|
+
any rights or obligations under those licenses. An open-source license may only
|
|
10
|
+
be selected and applied with the rights holder's approval.
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# pi-lark-gateway
|
|
2
|
+
|
|
3
|
+
飞书官方 SDK WebSocket → pi SDK → 原地更新回复。Node >=22.21。GA 支持范围仅国内飞书,国际版 Lark 不在支持承诺内。
|
|
4
|
+
|
|
5
|
+
npm 发布后可直接安装(当前候选版尚未上传):
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g pi-lark-gateway@beta --ignore-scripts # 分支候选版;正式版用 @latest 或固定版本
|
|
9
|
+
pi-lark-gateway setup
|
|
10
|
+
pi-lark-gateway doctor
|
|
11
|
+
pi-lark-gateway start
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
也支持源码安装:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm ci --ignore-scripts
|
|
18
|
+
npm run setup # 扫码创建并关联机器人
|
|
19
|
+
# npm run setup -- --manual # 已有应用/扫码不可用时手动接入
|
|
20
|
+
npm run doctor # 只做本地检查
|
|
21
|
+
npm start
|
|
22
|
+
npm run verify
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
网络需要代理时设置 `HTTPS_PROXY` / `HTTP_PROXY`。默认复用 `~/.pi/agent` 的模型凭据。会话策略 `tools: "none"` 禁用工具、扩展和 Skills;`tools: "all"` 开放 pi 默认工具并加载本机扩展、Skills 和上下文。支持文字、富文本、图片及文件/音视频附件。图片下载后直接作为视觉输入传给支持图片的模型;其余文件保存到会话附件目录,不自动转写或解析。`tools:all` 提供 `gateway_send_file`:图片嵌入当前回复卡片(需 `replyMode: "card"`),不另发图片消息;其他文件仍作为附件发送。默认工具关闭不变。详见 [多媒体收发](docs/MEDIA.md)。
|
|
26
|
+
|
|
27
|
+
## 安装、部署和发布
|
|
28
|
+
|
|
29
|
+
- [干净环境安装](docs/INSTALL.md):Node/Pi 模型授权、扫码与手动接入、平台权限和首次验收。
|
|
30
|
+
- [飞书应用权限清单](docs/PERMISSIONS.md):基础、增强及按需敏感权限与事件/回调。安装前可运行 `pi-lark-gateway setup --permissions` 查看,或用 `--permissions-json` 导出推荐应用权限;扫码与手动引导完成后会显示当前应用的权限链接。
|
|
31
|
+
- [运维手册](docs/OPERATIONS.md):Linux systemd / macOS LaunchAgent、日志轮转、本地诊断、备份恢复、升级回滚。
|
|
32
|
+
- [发布流程](docs/RELEASING.md):release/x.y.z 分支自动 beta、PR 合入 main 自动正式版、npm OIDC 和失败重试。
|
|
33
|
+
- [变更记录](CHANGELOG.md) · [安全说明](SECURITY.md) · [许可状态](LICENSE)
|
|
34
|
+
|
|
35
|
+
当前源码开发版本为 `1.0.0-rc.1`,CI 的实际发布版本由 `release/x.y.z` 分支名决定。支持 npm 运行时包(带 shrinkwrap)与源码包双渠道分发;配置可信发布者并开启发布开关后,release 分支每次 push 自动发新的 beta,PR 合入 main 自动发正式版。CI 同步修改产物的 package/lockfile,不向源码分支回写版本提交,也不会部署或重启服务。许可证暂为 `UNLICENSED`,不是开源授权。新机器必须先按安装文档配置 Pi 模型授权,单跑 setup 不会获得模型凭据。
|
|
36
|
+
|
|
37
|
+
部署模板位于 `deploy/`,需按目标主机填写绝对路径,**不会自动安装/重启**。同一 App ID 只运行一个实例,避免与手动 `npm start` 并行。
|
|
38
|
+
|
|
39
|
+
owner/管理员可在飞书发送以 `/restart` 为首个指令的文本或纯文字富文本消息,例如 `/restart 测试 PI`(群聊按现有规则 @ 机器人);尾部附言忽略,命令在 gateway 入口由代码识别并分流,不经过模型,先回复确认,再走延迟重启流程。执行前重新检查角色、访问权限和拉黑状态,同一进程内重复投递不重复登记。普通聊天中提及 `/restart` 不会被当作命令。
|
|
40
|
+
|
|
41
|
+
终端需要重启时用 `pi-lark-gateway restart`(源码:`npm run restart`)。命令只登记定时任务并返回,等待所有已接收回复(含排队任务)、卡片/续卡或普通消息发送、进度收尾及表情撤回结束,再在空闲延迟后退出并由启动器拉起;默认延迟 1 秒,可用 `--delay-ms 3000` 调整。不要在正在回复的工具调用中直接 kill 或 `launchctl kickstart -k`。首次升级旧进程须在对话结束后重启一次才能启用;详见 [运维说明](docs/OPERATIONS.md#等待输出完成后重启)。
|
|
42
|
+
|
|
43
|
+
## 代码结构
|
|
44
|
+
|
|
45
|
+
每个 feature 独立目录,`index.js` 为公开入口;跨 feature 通过入口导入。编排文件不承载具体 SDK/存储实现。
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
src/
|
|
49
|
+
├── index.js # 进程启动、代理、退出信号
|
|
50
|
+
├── gateway/ # 应用组装、事件处理流程编排
|
|
51
|
+
├── onboarding/ # 初始化 CLI、注册协议
|
|
52
|
+
├── config/ # schema、持久化迁移、策略、热加载
|
|
53
|
+
├── approvals/ # 审批服务、申请条件、卡片渲染
|
|
54
|
+
├── messages/ # 消息归一化、队列、回复流程、话题映射
|
|
55
|
+
├── progress/ # 文本/工具时间线、进度收集和节流
|
|
56
|
+
├── debug/ # 单消息虚拟身份转换
|
|
57
|
+
├── agent/ # pi 会话池、模型回答
|
|
58
|
+
├── lark/ # 飞书连接、消息、卡片、表情、元信息 API
|
|
59
|
+
└── storage/ # 私有 JSON 原子写入
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`npm start` 和 `npm run setup` 用法不变,配置与会话存储路径不变。详见 `AGENTS.md` 的结构约定。
|
|
63
|
+
|
|
64
|
+
## 本地配置 v2
|
|
65
|
+
|
|
66
|
+
默认配置目录 `~/.config/pi-lark-gateway/`:
|
|
67
|
+
|
|
68
|
+
- `config.json`:Bot 配置、全局访问策略、owner/admins、模型。
|
|
69
|
+
- `credentials-<UUID>.json`:独立 App Secret,权限 600;由 `bot.credentialsFile` 引用,不进仓库。
|
|
70
|
+
- `groups.json`:可选的逐群策略覆盖。只支持本地编辑,**没有群内卡片或配置命令**。
|
|
71
|
+
|
|
72
|
+
`PI_LARK_CONFIG=/绝对路径/config.json npm start` 可使用另一份配置。v1 首次启动会自动迁移:将密钥移出 config,保留旧运行时的“所有用户、有问必答”,不会按旧的未生效字段突然禁用群聊。配置及密钥均以私有临时文件原子写入;配置仍为明文,不是加密存储。
|
|
73
|
+
|
|
74
|
+
新安装默认最小权限:群聊每条消息必须 @,owner 直接使用,其他人需 owner 私聊审批;私聊默认同样需要审批。工具默认关闭(同时禁用本机扩展、Skills 和上下文文件),回复 normal,管理员与白名单初始为空。审批只授予当前会话访问权,批准后需重新发送消息。未配置 owner 时不允许陌生人使用,也不发送审批请求。现有配置及旧版本迁移保留原有策略,不自动覆盖。
|
|
75
|
+
|
|
76
|
+
config 示例(真实配置由 setup 生成):
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"version": 2,
|
|
81
|
+
"bot": {
|
|
82
|
+
"appId": "cli_xxx",
|
|
83
|
+
"domain": "feishu",
|
|
84
|
+
"name": null,
|
|
85
|
+
"openId": null,
|
|
86
|
+
"credentialsFile": "credentials-xxx.json",
|
|
87
|
+
"connectionMode": "websocket"
|
|
88
|
+
},
|
|
89
|
+
"access": {
|
|
90
|
+
"owner": "ou_owner",
|
|
91
|
+
"admins": [],
|
|
92
|
+
"private": { "enabled": true, "users": "allowlist", "allowedUsers": [], "trigger": "all", "tools": "none", "onUnknown": "ask_owner" },
|
|
93
|
+
"groups": { "enabled": true, "users": "allowlist", "allowedUsers": [], "trigger": "mention", "tools": "none", "onUnknown": "ask_owner" }
|
|
94
|
+
},
|
|
95
|
+
"session": { "private": "chat", "group": "thread" },
|
|
96
|
+
"model": null
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `model: null` 使用 pi 默认/会话模型;显式选择为 `{"provider":"...","id":"..."}`。
|
|
101
|
+
- `users`: `all` 或 `allowlist`。白名单模式仅允许 listed users、owner、admins;`enabled:false` 对所有人禁用。
|
|
102
|
+
- `trigger`: `all` 或 `mention`。群 mention 模式匹配机器人 Open ID,或下述文本白名单;话题内追问也使用同样规则。私聊忽略 trigger 和文本规则。
|
|
103
|
+
- `allowTextPatterns`: 可选正则字符串数组,任意一条匹配即可替代 @,**不会绕过用户白名单/审批**。例如 `["测试\\s*PI", "^问答:"]`(JSON 文件中反斜杠需写成 `\\`)。不配置或空数组保持原行为。
|
|
104
|
+
- `denyTextPatterns`: 可选正则字符串数组,任意一条匹配即静默拒绝,不回复、不申请审批;优先于 @ 和文本白名单,`trigger: "all"` 时也生效。
|
|
105
|
+
- 文本规则支持全局群策略及逐群完整覆盖,匹配普通文本和富文本标题/正文,排除 @ 占位符和附件元数据。采用 JavaScript Unicode 正则、区分大小写,不写 `/…/` 包裹;每个列表最多 50 条,每条 1–1000 字符。请仅配置可信、简单的正则,避免嵌套量词等可能导致回溯耗时的表达式。无效正则会拒绝整次配置载入。
|
|
106
|
+
- `tools` 支持 `none` / `all`,在后续任务开始时生效。`all` 可读写宿主文件、执行命令,并加载本机搜索/记忆扩展;不是沙箱,获准用户可能访问个人记忆。白名单、审批、拉黑仍独立生效。标题摘要始终无工具。
|
|
107
|
+
- owner/admins 区分管理身份与普通使用者;由于没有远程配置入口,管理身份目前用于白名单豁免和后续扩展。
|
|
108
|
+
- Bot 名称和 Open ID 启动时通过 API 尝试获取,保存在状态目录 `bot-meta.json`;`bot.openId` 可本地显式覆盖。
|
|
109
|
+
|
|
110
|
+
逐群配置使用完整策略,不做字段级合并:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"version": 1,
|
|
115
|
+
"groups": {
|
|
116
|
+
"oc_example": { "enabled": true, "users": "allowlist", "allowedUsers": ["ou_example"], "trigger": "mention", "tools": "none" }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`config.json` 顶层可选 `answerTimeoutMs`:毫秒整数,省略、`null` 或 `0` 表示不设 gateway 回复超时(默认);例如 `300000` 表示 5 分钟,最大 `2147483647`。支持热加载,仅对之后开始的回复生效;不包含排队时间,不改变模型服务自身的网络超时。用户仍可手动停止。
|
|
122
|
+
|
|
123
|
+
**热加载**:每 3 秒校验并载入访问策略和逐群配置;无效变更保留上一份有效配置并记录警告。仅影响后续消息,不终止已接收任务。修改 App ID、Secret、地区、连接模式或模型需要重启。当前仅支持 WebSocket、私聊按 chat、群聊按 thread。
|
|
124
|
+
|
|
125
|
+
开放策略允许所有能够向机器人发消息的人消耗模型额度。单进程全局默认最多同时处理 **10 个会话**,超出的 FIFO 等待,同一会话仍串行。后台归档也共享这 10 个名额;这是会话任务并发限制,不是所有 HTTP 请求数限制。尚未实现等待队列上限或逐用户限流,不适合不受信任的大群。已有配置显式设置的 `tools: "all"` 不会自动改为 `none`,关闭工具需本地修改对应策略。
|
|
126
|
+
|
|
127
|
+
## 群白名单的 Owner 审批
|
|
128
|
+
|
|
129
|
+
群策略 `users: "allowlist"` 时,未授权用户触发有效对话请求会给 `access.owner` 发送私聊卡片。策略可加 `onUnknown: "ask_owner"`(未填写时默认)或 `"deny"`(静默拒绝)。私聊同样支持白名单问询 owner,使用 `access.private` 策略,授权限定到对应私聊会话。
|
|
130
|
+
|
|
131
|
+
- 只有当前 owner 可以点「授权 / 拒绝」;还会校验卡片消息 ID、请求随机 ID、过期和处理状态。
|
|
132
|
+
- 授权仅针对该用户 + 该群;不改变工具权限、不绕过群禁用或触发条件。授权后用户需重新发消息,不自动重放原请求。
|
|
133
|
+
- 待审批请求 24 小时有效;同会话同用户合并待审批请求。拒绝后发送新消息可立即重新申请,同一消息重投不重复申请。投递失败冷却 60 秒。
|
|
134
|
+
- 授权、拉黑和审批状态保存在应用状态目录 `access-approvals.json`(600),重启保留。已授权卡片可「收回权限」或「拉黑」,待审批/拒绝卡片也可拉黑;拉黑后提供「解除拉黑」。仅当前 owner 可操作。收回只删除审批授权,不覆盖本地显式白名单/管理员或 all 策略;拉黑优先于所有普通准入策略,按用户+会话限定,停止回复和申请。解除拉黑不会恢复旧授权。操作影响后续消息,不中止已在执行的任务。旧申请被新申请取代后不能再操作。
|
|
135
|
+
- 在飞书开放平台「事件与回调」的 **回调配置** 中添加 `card.action.trigger`,使用长连接。仅订阅消息事件不够;权限/发布要求以控制台为准。机器人必须能向 owner 发私聊。
|
|
136
|
+
- 全开放模式不会触发审批。本次升级不自动把现有群切换为白名单。
|
|
137
|
+
|
|
138
|
+
### Owner 专用调试
|
|
139
|
+
|
|
140
|
+
在群或私聊发送一条多行消息:首行 `/debug assume 张三`(也可写邮箱),换行后写正常问题。允许前置 @机器人。仅真实 owner 可用(admins 不可用);首行被移除,余下正文以虚拟发送者进入正常权限、审批、回复流程。身份只对这一条消息生效。
|
|
141
|
+
|
|
142
|
+
名字/邮箱是**虚拟身份标签**,不会查询通讯录或授权同名真实员工。`all` 策略直接回答,`allowlist` 且未授权则私聊 owner 问询;禁用会话则拒绝。不会强制审批或修改正式策略。测试授权只针对 `debug_` 前缀身份,审批卡片标注 `[模拟测试]`。
|
|
143
|
+
|
|
144
|
+
批准后重新发送相同标签加正常问题,即正常回答;拒绝后发送新消息可再次问询。不同会话授权隔离。仍需在飞书开放平台订阅 `card.action.trigger` 回调。
|
|
145
|
+
|
|
146
|
+
## 回复与会话
|
|
147
|
+
|
|
148
|
+
私聊/群策略支持 `replyMode: "normal" | "card"`,省略默认 normal,可热加载。逐群覆盖使用同一字段。
|
|
149
|
+
|
|
150
|
+
- normal:文本占位、工具进度、原地更新最终文本。
|
|
151
|
+
- card:立即发送灰色等待卡片;生成中蓝色,完成绿色,失败红色。标题用当前会话模型在独立临时会话中总结本次消息意图,不写入正式对话;每条回复会额外调用一次模型,标题失败/超时回退为“对话回复”。正文按实际顺序实时保留本轮助手中间说明、工具名称/状态及最终答复;工具完成后在原位置更新;相邻同名调用合并为 `✅ read ×3`,中间有说明文字或其他工具就分开,不加外围括号。只有仍处于时间线最后一条可见输出位置的最新调用展示操作详情:在工具名称下方用 Markdown 代码块完整显示命令/路径,保留换行和缩进,不再限制 100 字符。工具完成或失败不会立即收起详情;直到后续说明、新工具调用或最终答复出现时,才收起旧详情,只保留名称、状态和次数,也不会回退展示较早的调用。空消息事件不算后续输出。不把之前的文本覆盖掉,也不重复追加最终聚合文本。长内容生成中就拆为续卡,后续原地更新这些续卡;结束后所有卡片更新为终态,停止/失败保留已输出内容并追加提示。
|
|
152
|
+
- card 文本优先使用 CardKit 原生打字机:客户端每 30ms 显示 2 字符,使用 fast 策略避免积压;助手文本追加走流式文本接口,工具状态/详情及非追加改写直接更新,不逐字播放工具代码。每卡操作序号严格递增,请求间隔至少 150ms;长轮次在更新前按需重新开启流式窗口,结束后关闭流式模式。需要 `cardkit:card:write` 权限;创建或更新失败后,本轮自动降级至原来的整卡更新,保留消息 ID、控制按钮和续卡,不修改应用权限。正常文本消息模式不变。流式创建成功后的消息发送若遇到不确定的网络失败,不盲目重复发送;沿用原有回复错误处理。
|
|
153
|
+
- card 蓝色阶段在首张卡片提供「停止」与输入框 +「打断」,续卡不重复提供控制按钮。停止请求立即取消当前模型运行,待当前工具响应取消后显示橙色“已停止”,不会取消其他排队消息;已完成的外部副作用不会回滚,独立后台子任务不保证被一并停止。打断通过 SDK steering 注入,在下一个执行边界处理,不强杀当前工具。仅本轮发起人、owner、管理员可操作,且实时检查访问权限与拉黑状态。结束/重启后旧卡片控制失效。
|
|
154
|
+
- 同话题普通新消息立即显示等待卡片,但模型执行、最终回复与清理按顺序完成后才启动下一条;不同话题可并行。卡片打断是唯一绕过普通消息队列的输入途径。
|
|
155
|
+
- 模式在收到消息时选定,不中途转换已发出的消息。回复卡片使用 JSON 2.0(需飞书客户端 7.20+),正文放在 `body.elements` 的 Markdown 组件中,以支持标题、引用和表格;标题保持纯文本。按钮使用 2.0 的 `behaviors` 回传和 `form_action_type` 表单提交协议。实际排版以飞书支持的 Markdown 语法为准。
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
收到消息加随机表情,立即发送占位回复。群内使用 `reply_in_thread` 创建话题。normal 模式生成中最多显示最近 10 条工具名称和状态,完成后原地替换为最终答案,长回答分段续发;card 模式实时保留整轮文本与工具时间线(工具条目不限制为最近 10 条)。两种模式均不展示内部推理或原始工具结果。normal 不展示参数;card 只提取白名单操作字段(bash/powershell 命令,read/write/edit/ls 路径,grep/find 模式和路径,web_search 查询),不展示写入内容、编辑补丁或任意参数对象。先检查完整操作中的常见凭据标记,涉及敏感操作时整段隐藏;其余内容完整放入不与正文冲突的代码围栏,控制字符可视化。长代码块按实际请求字节预算续卡,每张卡片独立闭合/重新打开围栏;后续可见文字或新调用出现后,旧详情及其续卡内容一并清除。完成后撤回本次表情。
|
|
159
|
+
|
|
160
|
+
私聊整个 chat 共用上下文;群内按话题隔离,同话题参与者共享。状态在 `~/.local/share/pi-lark-gateway/<App ID>/`,包含话题映射、Bot 元信息和哈希会话目录中的 JSONL。旧版按用户拆分的历史不自动合并。重启恢复会话;事件去重仅存在内存中。
|
|
161
|
+
|
|
162
|
+
### 会话过期归档
|
|
163
|
+
|
|
164
|
+
连续 **30 天**没有处理新交互的会话会被归档(按最后一轮开始/结束时间及 JSONL 修改时间中的较新值计算,失败/停止的交互也刷新时间)。启动、每小时巡检和再次访问时检查;正在处理或已进入会话池排队的任务不会被后台归档。
|
|
165
|
+
|
|
166
|
+
归档使用隔离的无工具模型会话,分段生成历史摘要;明确写入“该会话已过期被归档”。摘要先以权限 600 原子保存到原会话目录的 `archive.json`,再删除被摘要覆盖的会话 JSONL 并释放缓存。下次对话以摘要作为历史上下文开启新会话,不恢复原始逐条记录。连续多次归档会合并上一份摘要。
|
|
167
|
+
|
|
168
|
+
- 摘要生成、校验或保存失败时保留原始记录,记录脱敏错误,**正常聊天继续**,不把归档当成回复的前置条件。
|
|
169
|
+
- 删除中断可由持久化清理清单恢复;原文件已改变时停止清理,不删除新内容。用户恢复聊天后旧清理计划作废,保留现存原文件及已保存摘要,重新达到空闲期才再次归档。
|
|
170
|
+
- 不删除工具生成的其他文件、话题映射或授权状态。`activity.json` 记录交互时间,摘要长期保留。
|
|
171
|
+
- 摘要会产生额外模型调用费用,并且是有损压缩;不保留系统提示、内部推理、工具参数或图片二进制。原始 JSONL 合计超过 32 MiB 时自动归档暂缓并记录错误,需人工处理,避免截断历史后误删。
|
|
172
|
+
- 仅网关运行时执行巡检;停机期间的过期会话在下次启动后处理。
|
|
173
|
+
|
|
174
|
+
## 平台配置与初始化协议
|
|
175
|
+
|
|
176
|
+
飞书开放平台启用长连接,订阅 `im.message.receive_v1`,赋予发消息、编辑消息、表情回复所需权限。普通群消息是否投递仍由飞书授权决定:不 @ 的全群消息可能需要 `im:message.group_msg`,按控制台说明发布审批。历史拉取权限失败不等于 @事件或话题回复失败。
|
|
177
|
+
|
|
178
|
+
扫码参考 Hermes commit `69948c005791d4d0e51670ca015238c2c7663749`:账号服务 `/oauth/v1/app/registration` 的 init → begin(PersonalAgent/client_secret)→ poll。注册接口独立于消息 SDK,不能视为已验证的公开稳定 API;受租户/地区/上游变更影响。这是创建关联应用,不是已有应用的 OAuth。支持时效、取消、拒绝和 slow_down;拒绝国际版租户和关联链接。不会输出密钥或覆盖已存在的配置。扫码不可用时使用 `npm run setup -- --manual`。
|
|
179
|
+
|
|
180
|
+
自动化测试使用模拟 transport;维护者已确认国内飞书既有功能 E2E 通过。新部署仍应完成最小 owner 回复验证。`npm run smoke:clean` 仅验证无凭据干净安装,不会发消息或调用模型。
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Supported scope
|
|
4
|
+
|
|
5
|
+
Domestic Feishu, a single gateway process per bot, trusted operator-owned macOS/Linux hosts.
|
|
6
|
+
New installations default to owner/approved-user access and no tools. Do not expose an
|
|
7
|
+
open-access tool-enabled bot to untrusted users. This is not a multi-tenant sandbox.
|
|
8
|
+
|
|
9
|
+
`tools: all` can execute host commands, read files, and load local extensions and memory.
|
|
10
|
+
Group participants share thread context. Never treat a prompt instruction as credential
|
|
11
|
+
isolation. Use a dedicated OS account and minimal model/bot privileges.
|
|
12
|
+
|
|
13
|
+
## Sensitive data
|
|
14
|
+
|
|
15
|
+
Configuration, credential files, session JSONL, archives and backups are private plaintext,
|
|
16
|
+
not encrypted storage. Keep files 600 and directories 700, use disk encryption and encrypt
|
|
17
|
+
backups before copying off-host. Do not commit `.env`, credentials, `~/.pi`, state or logs.
|
|
18
|
+
The release packer uses an allowlist and rejects common credential/session filenames;
|
|
19
|
+
this does not replace human secret review of source files.
|
|
20
|
+
|
|
21
|
+
## Reporting
|
|
22
|
+
|
|
23
|
+
Do not post secrets, raw transcripts or exploit details in public issues/group chats.
|
|
24
|
+
Report privately to the deployment owner through your existing trusted channel. Before
|
|
25
|
+
external release the rights holder must publish a dedicated private security contact;
|
|
26
|
+
no public reporting address is invented here.
|
|
27
|
+
|
|
28
|
+
If a credential may have leaked: stop the service, revoke/rotate affected bot/model
|
|
29
|
+
credentials at the provider, restrict access, preserve necessary evidence privately,
|
|
30
|
+
then update local files and verify before restarting. Deleting logs is not remediation.
|
|
31
|
+
|
|
32
|
+
## Automated publication
|
|
33
|
+
|
|
34
|
+
Protect main, release/* branches and workflow changes. Once NPM_PUBLISH_ENABLED=true,
|
|
35
|
+
trusted release/x.y.z pushes authorize beta publication; merging a same-repository
|
|
36
|
+
release PR into main authorizes the stable version. Closed-unmerged or forked PRs do
|
|
37
|
+
not publish. Configure npm's trusted publisher for release.yml and the npm-publish
|
|
38
|
+
GitHub environment (allow release/* and main branches); tests/builds are read-only, only the npm job
|
|
39
|
+
gets id-token:write, and only the final GitHub Release job gets contents:write.
|
|
40
|
+
Do not add long-lived npm tokens as a silent fallback. Publication jobs download and
|
|
41
|
+
verify the already-tested artifact rather than executing dependency installation or
|
|
42
|
+
rebuilding it. Version stamping only changes staged manifests, not the source branch;
|
|
43
|
+
artifacts retain the actual push/merge commit identity. Existing registry versions and
|
|
44
|
+
Release assets are never overwritten. Automatically generated v* tags are release
|
|
45
|
+
records, not workflow triggers.
|
|
46
|
+
|
|
47
|
+
## Updates
|
|
48
|
+
|
|
49
|
+
Install only verified release artifacts; compare SHA-256 with a trusted distribution
|
|
50
|
+
channel. A checksum alone is not a signature. Review dependency/license changes and run
|
|
51
|
+
`npm run release:check` before publishing. Never run `npm audit fix --force` unattended.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { superviseCommand } from '../src/runtime/index.js';
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const [command = '--help', ...args] = process.argv.slice(2);
|
|
10
|
+
const commands = { start: 'src/index.js', setup: 'src/onboarding/index.js', doctor: 'scripts/doctor.js', backup: 'scripts/backup.js', restart: 'scripts/restart.js' };
|
|
11
|
+
const help = () => console.log('用法:pi-lark-gateway <start|setup|doctor|backup|restart> [参数]\n setup [--manual] 配置国内飞书;默认白名单审批、工具关闭\n start 启动网关(单实例)\n doctor 本地诊断,不联网\n backup 停服务后的私有备份\n restart 登记任务,等回复发送完成后延迟重启\n --version 显示版本');
|
|
12
|
+
if (['--help', '-h', 'help'].includes(command)) help();
|
|
13
|
+
else if (['--version', '-v'].includes(command)) console.log(JSON.parse(await fs.readFile(path.join(root, 'package.json'))).version);
|
|
14
|
+
else if (!Object.hasOwn(commands, command)) { help(); process.exitCode = 1; }
|
|
15
|
+
else if (command === 'start' && args.length) {
|
|
16
|
+
if (args.length === 1 && ['--help', '-h'].includes(args[0])) help();
|
|
17
|
+
else { console.error('start 不接受参数;自定义配置请设置 PI_LARK_CONFIG。'); process.exitCode = 1; }
|
|
18
|
+
} else {
|
|
19
|
+
// Spawn with the supported Node proxy flag; npm bin shebangs cannot portably set it.
|
|
20
|
+
superviseCommand({
|
|
21
|
+
spawnChild: () => spawn(process.execPath, ['--use-env-proxy', path.join(root, commands[command]), ...(command === 'setup' ? ['setup'] : []), ...args], { stdio: 'inherit', env: { ...process.env, PI_LARK_CLI: '1' } }),
|
|
22
|
+
restartable: command === 'start',
|
|
23
|
+
onError: () => console.error('gateway_command_failed'),
|
|
24
|
+
onExit: code => { process.exitCode = code; },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0"><dict>
|
|
4
|
+
<key>Label</key><string>dev.pi.lark-gateway</string>
|
|
5
|
+
<key>ProgramArguments</key><array><string>@NODE@</string><string>--use-env-proxy</string><string>@APP_DIR@/src/index.js</string></array>
|
|
6
|
+
<key>WorkingDirectory</key><string>@APP_DIR@</string>
|
|
7
|
+
<key>RunAtLoad</key><true/>
|
|
8
|
+
<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
|
|
9
|
+
<key>ThrottleInterval</key><integer>5</integer>
|
|
10
|
+
<key>Umask</key><integer>63</integer>
|
|
11
|
+
<key>EnvironmentVariables</key><dict>
|
|
12
|
+
<key>HOME</key><string>@HOME@</string>
|
|
13
|
+
<key>PI_TELEMETRY</key><string>0</string>
|
|
14
|
+
<key>PI_SKIP_VERSION_CHECK</key><string>1</string>
|
|
15
|
+
<key>PI_LARK_KEEP_AWAKE</key><string>ac</string>
|
|
16
|
+
</dict>
|
|
17
|
+
<key>StandardOutPath</key><string>@HOME@/Library/Logs/pi-lark-gateway/stdout.log</string>
|
|
18
|
+
<key>StandardErrorPath</key><string>@HOME@/Library/Logs/pi-lark-gateway/stderr.log</string>
|
|
19
|
+
</dict></plist>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Pi Feishu gateway
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=simple
|
|
8
|
+
WorkingDirectory=@APP_DIR@
|
|
9
|
+
ExecStart="@NODE@" --use-env-proxy "@APP_DIR@/src/index.js"
|
|
10
|
+
EnvironmentFile=-%h/.config/pi-lark-gateway/service.env
|
|
11
|
+
Environment=PI_TELEMETRY=0
|
|
12
|
+
Environment=PI_SKIP_VERSION_CHECK=1
|
|
13
|
+
UMask=0077
|
|
14
|
+
Restart=on-failure
|
|
15
|
+
RestartSec=5
|
|
16
|
+
TimeoutStopSec=20
|
|
17
|
+
StandardOutput=journal
|
|
18
|
+
StandardError=journal
|
|
19
|
+
|
|
20
|
+
[Install]
|
|
21
|
+
WantedBy=default.target
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Local file only: save as ~/.config/pi-lark-gateway/service.env and chmod 600.
|
|
2
|
+
# Do not put real secrets in this template or commit a filled copy.
|
|
3
|
+
# PI_LARK_CONFIG=/absolute/path/to/config.json
|
|
4
|
+
# HTTPS_PROXY=http://127.0.0.1:YOUR_PORT
|
|
5
|
+
# HTTP_PROXY=http://127.0.0.1:YOUR_PORT
|
|
6
|
+
# NO_PROXY=localhost,127.0.0.1
|
|
7
|
+
# Prefer Pi's private credential store. Provider environment keys, if required,
|
|
8
|
+
# belong only in the local service.env, never in a shared unit/plist.
|
package/docs/INSTALL.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# 干净环境安装
|
|
2
|
+
|
|
3
|
+
## 支持范围
|
|
4
|
+
|
|
5
|
+
国内飞书;单机器人、单网关进程;macOS 或 Linux。Node.js **>=22.21.0**,推荐 Node 24 LTS;npm 随 Node 安装。系统需有 `tar`。不承诺国际版 Lark 或 Windows。源码开发版本为 `1.0.0-rc.1`,实际 CI 发布版本由 release 分支名决定,不等于已发布 GA。
|
|
6
|
+
|
|
7
|
+
使用专用普通 OS 用户,不要以 root 运行。网关访问该用户的 `HOME`;常驻服务必须使用同一用户。需要外网访问飞书及所选模型服务,代理按实际环境配置,不预设端口。
|
|
8
|
+
|
|
9
|
+
## 1. 获取程序:npm 或源码包
|
|
10
|
+
|
|
11
|
+
### npm 安装(对应版本正式上传后)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g pi-lark-gateway@beta --ignore-scripts # release 分支候选版
|
|
15
|
+
# 稳定版使用 @latest,生产建议固定 @具体版本
|
|
16
|
+
pi-lark-gateway --version
|
|
17
|
+
pi-lark-gateway --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
不需要 clone 仓库;npm 包包含 `pi-lark-gateway` CLI 和锁定依赖的 `npm-shrinkwrap.json`,不包含源码测试/发布脚本。当前版本未上传前,不能把上述安装当成已经可用的 registry 版本。
|
|
21
|
+
|
|
22
|
+
之后仍须完成下文的模型授权和飞书配置。npm 用户将文中的 `npm run setup` / `npm run doctor` / `npm start` / `npm run backup` / `npm run restart` 分别替换为 `pi-lark-gateway setup` / `doctor` / `start` / `backup` / `restart`,**不需要 npm 的 `--` 参数分隔符**。例如 `pi-lark-gateway setup --manual`。
|
|
23
|
+
|
|
24
|
+
### 源码包安装
|
|
25
|
+
|
|
26
|
+
GitHub Release 使用共享 Action 产物:`pi-lark-gateway-<版本>-source.tar.gz`、npm `.tgz`、`SHA256SUMS` 和 `bundle.json`。校验文件也必须来自可信渠道。
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# 下载源码包、npm tgz 和 SHA256SUMS 到同一目录;macOS 校验如下
|
|
30
|
+
# Linux 可用 sha256sum -c SHA256SUMS
|
|
31
|
+
shasum -a 256 -c SHA256SUMS
|
|
32
|
+
# 建议解压到一个新的版本目录,避免覆盖已有 source/ 目录
|
|
33
|
+
tar -xzf pi-lark-gateway-<版本>-source.tar.gz
|
|
34
|
+
cd source
|
|
35
|
+
node --version
|
|
36
|
+
npm ci --ignore-scripts
|
|
37
|
+
npm run verify
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
正式产物统一由共享 Action 构建;本地安装 smoke 产生的临时测试包不是发布包。
|
|
41
|
+
|
|
42
|
+
版本目录可以放在 `~/.local/opt/pi-lark-gateway/`。保留 `package-lock.json`,部署不要改用 `npm update` 或拷贝别的 OS 的 `node_modules`。依赖无需安装生命周期脚本,使用 `--ignore-scripts`。仅解压你信任的包。
|
|
43
|
+
|
|
44
|
+
## 2. 配置 Pi 模型授权(新机器必须做)
|
|
45
|
+
|
|
46
|
+
源码安装无需另装全局 Pi,在源码目录使用已锁定版本的 CLI:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
./node_modules/.bin/pi
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
npm 安装用户可使用 `npm exec --package=@earendil-works/pi-coding-agent@0.86.1 -- pi` 打开与 SDK 配套的 Pi CLI(下载公开依赖,不要在 root 下登录)。两种方式使用同一 OS 用户的 `~/.pi/agent`。
|
|
53
|
+
|
|
54
|
+
进入 Pi 后:
|
|
55
|
+
1. `/login` 选择支持订阅登录的模型提供方并完成授权;或者按提供方说明配置对应 API key 环境变量。
|
|
56
|
+
2. `/model` 选择可用模型,Ctrl+S 保存启动默认模型。
|
|
57
|
+
3. 发送一个无敏感内容的测试问题,验证授权、网络和额度;这会产生真实模型调用。
|
|
58
|
+
4. `/quit` 退出。
|
|
59
|
+
|
|
60
|
+
授权通常保存在 `~/.pi/agent/auth.json`,默认模型在 `~/.pi/agent/settings.json`。网关复用同一用户的配置。若用 API key 环境变量,常驻服务也必须获得它;不要把密钥放进命令行参数、Git、群聊或共享的 service/plist 文件。按 [运维说明](OPERATIONS.md) 使用权限 600 的本地环境配置或提供方凭据存储。
|
|
61
|
+
|
|
62
|
+
`config.json` 中也可显式设置 `model: {"provider":"提供方标识","id":"模型标识"}`,使用 Pi 中实际列出的值,不要照抄虚构模型名。SDK 自定义模型/提供方见已安装 Pi 包的 `docs/providers.md` 和 `docs/models.md`。
|
|
63
|
+
|
|
64
|
+
## 3. 创建/接入飞书应用
|
|
65
|
+
|
|
66
|
+
### 先查看权限要求(离线,不授权)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run setup -- --permissions
|
|
70
|
+
# 推荐权限 JSON(不含非 @ 群消息敏感权限):
|
|
71
|
+
npm run --silent setup -- --permissions-json
|
|
72
|
+
# npm 全局安装对应:pi-lark-gateway setup --permissions / --permissions-json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
详见 [飞书应用权限清单](PERMISSIONS.md):基础 3 项、增强 5 项、按需敏感权限、事件/回调及官方依据。以上命令不联网、不读取或覆盖配置,现有用户也可运行。扫码和手动配置完成后都会再次显示检查清单及当前应用的权限管理直达链接;程序不代为申请权限。
|
|
76
|
+
|
|
77
|
+
### 扫码创建
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run setup
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
仅国内飞书。扫码创建并关联新应用,不是已有应用 OAuth。配置已存在时拒绝覆盖,可用 `--config /绝对路径/config.json` 另建配置。
|
|
84
|
+
|
|
85
|
+
### 扫码不可用:手动接入已有应用
|
|
86
|
+
|
|
87
|
+
在飞书开放平台创建企业自建应用并启用机器人,获取 App ID、App Secret 与该应用对应的 owner open_id。不要使用其他应用下的 open_id。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm run setup -- --manual
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
交互式录入;App Secret 不回显、不写入 shell history;owner 必填。此命令只保存配置,不替你创建应用或验证服务端凭据。
|
|
94
|
+
|
|
95
|
+
### 平台配置清单
|
|
96
|
+
|
|
97
|
+
在开放平台按实际应用权限说明完成:
|
|
98
|
+
- 启用机器人;事件订阅使用长连接,订阅 `im.message.receive_v1`。
|
|
99
|
+
- 在**回调配置**中另加 `card.action.trigger` 并使用长连接;否则审批/停止/打断按钮无效。
|
|
100
|
+
- 基础应用权限:`im:message.p2p_msg:readonly`(私聊接收)、`im:message.group_at_msg:readonly`(群 @ 接收)、`im:message:send_as_bot`(发送/回复/编辑文本及卡片)。仅做单一聊天类型时可省略另一接收权限。
|
|
101
|
+
- 推荐增强权限:`im:message.reactions:write_only`(添加/撤回表情)、`im:chat:read`(审批群名称)、`cardkit:card:write`(原生打字机)。缺少时分别省略表情、显示群 ID、降级整卡更新,不阻断基础回复。
|
|
102
|
+
- 多媒体权限:`im:message:readonly`(下载收到的图片/文件)和 `im:resource:upload`(上传图片/文件供发送)。识图还需支持视觉的模型;`tools:none` 仍可识图,但不能调用发送文件工具。详情见 [多媒体收发](MEDIA.md)。
|
|
103
|
+
- 仅接收非 @ 群消息时申请敏感权限 `im:message.group_msg`,再显式配置群触发规则。不要为启动网关额外申请通讯录、云文档或云盘权限;无需重复添加 `im:message:update`,上述发送权限已覆盖当前编辑 API。
|
|
104
|
+
- 发布应用版本、完成租户审批、配置可用范围;将机器人加入测试群,确认能向 owner 发私聊。
|
|
105
|
+
- 扫码未返回 owner 时,先在本地补齐 `access.owner`,否则陌生用户无法申请审批。
|
|
106
|
+
|
|
107
|
+
新配置默认:owner 可用;其他用户须获 owner 审批;群内每条消息需 @;工具关闭。已有配置不会自动改变准入或工具权限。
|
|
108
|
+
|
|
109
|
+
## 4. 检查并首次启动
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run doctor
|
|
113
|
+
npm start
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
自定义配置:`PI_LARK_CONFIG=/绝对路径/config.json npm run doctor`,启动时使用同一变量。
|
|
117
|
+
|
|
118
|
+
`doctor` 仅检查本地 Node/依赖/配置/文件权限/owner,不证明模型、飞书权限或 WebSocket 在线。启动日志需出现连接状态;用 owner 私聊和群 @ 完成首条回复验收,检查未授权用户仍需审批。不要同时启动第二个消费者。
|
|
119
|
+
|
|
120
|
+
确认后停止前台实例,再配置 [常驻部署](OPERATIONS.md)。
|
|
121
|
+
|
|
122
|
+
## 无凭据自动安装验证
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm run smoke:clean
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
在临时 HOME 和目录内复制发布文件、`npm ci --ignore-scripts`、运行测试及启动失败检查;不读取宿主 Pi/飞书凭据、不调用模型或飞书。会下载公开 npm 依赖,结束删除临时目录。这不是替代真实 E2E。
|
|
129
|
+
|
|
130
|
+
源码维护者还应执行 `npm run smoke:npm`,验证实际 tgz 在临时全局 prefix 下安装后 CLI 可用;不会更改本机全局包,也不会发布到 registry。
|
package/docs/MEDIA.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 多媒体收发
|
|
2
|
+
|
|
3
|
+
## 之前为什么看不到图片
|
|
4
|
+
|
|
5
|
+
旧版只提取文字和富文本中的文字,既不下载 `image_key`/`file_key` 对应的二进制,也不把图片传给 Pi 的 `session.prompt(..., { images })`。只把图片名字、文件路径或 image_key 写进文本不能让模型看到图片。
|
|
6
|
+
|
|
7
|
+
新版在访问准入、消息去重和会话排队之后下载当前消息资源;图片以 Pi SDK 的 base64 ImageContent 传给模型。已被旧版忽略的图片需要重发,不会为补全旧会话而回溯下载聊天历史。
|
|
8
|
+
|
|
9
|
+
### `unsupported MIME type 'undefined'` 兼容修复
|
|
10
|
+
|
|
11
|
+
Pi SDK 0.86.1 的文档示例使用嵌套 `source.mediaType/data`,但当前安装包内的 OpenAI/Codex 请求转换器读取顶层 `mimeType/data`。网关已改用运行时实际支持的格式,并在会话加载/重建上下文时兼容转换之前保存的嵌套图片。
|
|
12
|
+
|
|
13
|
+
转换只作用于交给 SDK 的上下文副本:不删除图片、不覆盖原始 JSONL,也不清空会话。原始文件保持可恢复,正常后续消息仍按 SDK 规则追加。升级后需重启加载新代码;已保存但因该错误失败的图片可在原会话中继续使用,不需要删会话或重发。完全没有保存图片的更早版本仍需重发。
|
|
14
|
+
|
|
15
|
+
回归测试直接运行当前安装包中的请求转换器,检查最终 `data:image/...;base64,...` 与原图字节,并覆盖真实 SDK 的历史会话恢复;不再仅检查模拟 `session.prompt` 收到了图片参数。
|
|
16
|
+
|
|
17
|
+
## 能力与边界
|
|
18
|
+
|
|
19
|
+
| 接收内容 | 处理方式 |
|
|
20
|
+
|---|---|
|
|
21
|
+
| 单张图片、富文本内图片(含只有图、没有文字的富文本) | 下载 PNG/JPEG/GIF/WebP 并作为视觉输入;文件形式上传的这些图片也可识别 |
|
|
22
|
+
| 普通文件(PDF、Office、文本等) | 保存原文件,并向模型提供受限本地路径和元信息;不假装已经解析内容。`tools:all` 时可进一步用已安装的工具处理 |
|
|
23
|
+
| 音频、视频 | 下载并保存为文件;不自动转写、不自动抽帧,不宣称已经听过或看过 |
|
|
24
|
+
| 表情包、合并转发中的附件、卡片内资源 | 暂不支持;请直接发送原文件 |
|
|
25
|
+
|
|
26
|
+
- 图片识别需模型的 `input` 声明包含 `image`。非视觉模型会得到明确错误提示,而不是丢图后编造分析。
|
|
27
|
+
- `tools:none` 仍可使用视觉输入;不授予文件系统读取、终端或附件发送工具。默认策略保持不变。
|
|
28
|
+
- 群聊仍要求**本条消息 @ 机器人**。建议将 @、问题和图片放入同一富文本;仅先 @ 再单独发一条无 @ 附件不会绕过规则。需要非 @ 处理时,须明确配置策略并申请相应敏感接收权限。
|
|
29
|
+
- 图片格式不支持或超过视觉输入限制时,会保存文件并明确标记“未传入视觉模型”,提示转换/压缩;不会假装识别。
|
|
30
|
+
|
|
31
|
+
## 发送图片和文件
|
|
32
|
+
|
|
33
|
+
`tools:all` 的会话提供 `gateway_send_file`。例如用户明确要求“把生成的图发给我”后,助手先把文件写到当前工作目录的 `attachments/outbox/result.png`,再调用:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{"path":"attachments/outbox/result.png"}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
PNG/JPEG/GIF/WebP 默认**嵌入本轮已有的回复卡片**,不另发独立图片消息。图片固定保留在首张卡片,后续文字流式更新、最终答案、停止/错误状态及原生卡片降级都不会覆盖图片;超长文字仍按原规则使用续卡。卡片使用 `width_mode: "default"`(桌面宽版/iPad 上限 600px),不再撑满聊天窗口。图片放入 JSON 2.0 的 `width: "278px"` 列容器,使用 `flex_mode: "none"` 在窄屏按比例收缩,将预览最大宽度限制为 278px;不再使用历史图片属性 `compact_width` / `custom_width`。保留完整展示、不裁剪及点击放大;实际显示受飞书客户端与窗口尺寸影响。列像素宽度要求飞书 V7.4+,详见[官方分栏组件说明](https://open.feishu.cn/document/feishu-cards/card-json-v2-components/containers/column-set)。
|
|
40
|
+
|
|
41
|
+
要求当前回复使用 `replyMode: "card"`。普通文本模式或卡片占位发送失败时,工具明确提示不可插图,不另建图片消息或偷偷改变会话配置;请启用卡片模式后再试。图片上传成功不等于已显示,只有当前卡片更新成功才返回发送成功。
|
|
42
|
+
|
|
43
|
+
仅当用户明确要求作为普通文件附件时使用 `asFile: true`(会另发文件消息)。其他内容,包括音频和视频,也以普通文件附件发送,**不是语音气泡或原生视频播放器消息**。不自动调用转码器。
|
|
44
|
+
|
|
45
|
+
安全边界:
|
|
46
|
+
- 只能发送当前会话 `attachments/inbox` 或 `attachments/outbox` 内的普通文件。拒绝任意主机路径、其他会话路径、隐藏文件、符号链接、硬链接和目录。
|
|
47
|
+
- 工具不接受 chat_id、用户 ID 或远程 URL,只能回复绑定的当前消息;群消息保留原话题映射。
|
|
48
|
+
- 用户明确请求或确认后才可发送,群聊成员均可见;禁止发送凭据与私人会话记录。此意图约束也写入工具和系统提示,不是逐次弹窗审批机制。
|
|
49
|
+
- `tools:all` 本身仍是可信用户的主机工具访问,**不是沙箱**。文件路径限制不防御拥有同一 OS 账号任意写权限的恶意进程,也不能代替用户审查拟发送内容。
|
|
50
|
+
- 上传前及回复前重新检查会话工具/准入权限和本轮有效性。并行会话不会共享接收人,旧轮工具不能向新轮发送。
|
|
51
|
+
- 每轮最多发送 4 个附件,串行上传/发送;同一工具调用 ID 复用结果,消息使用稳定 UUID。结果不确定时不盲目重试,应先检查聊天记录。停止不会撤回已经发送的附件。
|
|
52
|
+
|
|
53
|
+
## 限制与存储
|
|
54
|
+
|
|
55
|
+
- 每条消息最多 4 个附件;单文件与单条消息附件合计最多 **30 MiB**。
|
|
56
|
+
- 自动视觉输入单图最多 **5 MiB**;大图请压缩。模型提供商仍可能有额外尺寸、格式和请求限制。
|
|
57
|
+
- 卡片图片上传最多 10 MiB,超过时提示压缩,不自动改为独立文件消息;飞书还可能按像素/编码限制拒绝图片。
|
|
58
|
+
- 每个资源下载、上传或发送等待最多 30 秒。下载检查声明长度和实际流量,并在超限/停止时关闭流;不抓取任意 URL。
|
|
59
|
+
- 收到的文件位于 `~/.local/share/pi-lark-gateway/<App ID>/<会话 hash>/attachments/inbox/`,使用随机前缀避免覆盖;发送产物放在相邻 `outbox/`。网关创建的目录权限 0700,下载文件 0600。
|
|
60
|
+
- 单个网关应用的收件箱配额为 **512 MiB**,启动后首次使用重新统计已存在的收件箱。达到配额停止新增附件,不自动删除用户数据;管理员人工检查、清理后重启重新统计。外部手工改动不实时重新扫描。
|
|
61
|
+
- 配额仅统计网关管理的收件箱,不含模型生成的 outbox、备份或会话 JSONL。视觉输入可能在 SDK 会话记录中包含图片数据,须保护状态目录、监控磁盘;原有 30 天会话归档不会自动清除 inbox/outbox 文件。
|
|
62
|
+
|
|
63
|
+
## 权限与验收
|
|
64
|
+
|
|
65
|
+
新增应用权限详见 [权限清单](PERMISSIONS.md):
|
|
66
|
+
- 下载收到的附件:`im:message:readonly`(或接口接受的既有读取权限)。
|
|
67
|
+
- 上传图片/文件:`im:resource:upload`(已有 `im:resource` 可替代)。
|
|
68
|
+
- 发送附件:已有的 `im:message:send_as_bot`。
|
|
69
|
+
|
|
70
|
+
开通后按租户要求发布/审批。`setup --permissions` 与 `setup --permissions-json` 已包含新清单,不会自动授权。升级运行中的网关需要另行安排安全重启。
|
|
71
|
+
|
|
72
|
+
本地测试使用模拟平台二进制流、临时目录和模拟模型,不需要真实凭据;不能代替真实客户端验收。建议按顺序用 owner 私聊发一张清晰 PNG、富文本多图、PDF、音视频文件,再在开启工具的会话中明确要求发回生成的图片/文件。确认群 @、审批、停止和线程归属仍正确;权限缺失或非视觉模型须有明确提示。
|