@cubeai/dsh-feishu-channels 0.1.0 → 0.2.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/README.md +16 -16
- package/client.js +94 -36
- package/cordis.patch.yml +1 -1
- package/docs/dsh-feishu-channels.md +538 -0
- package/locale/zh.json +1 -1
- package/package.json +2 -3
- package/src/channels/qr/feishu.js +1 -1
- package/src/channels/qr/hub.js +2 -2
- package/src/engine/chat-commands.js +4 -4
- package/src/engine/command-messages.js +3 -3
- package/src/engine/gateway.js +1 -1
- package/src/manager.js +29 -24
- package/docs/coexistence.md +0 -112
- package/docs/dsh-feishu-channels-plan.md +0 -1406
- package/docs/ui-revamp-plan.md +0 -263
- package/docs/verification.md +0 -119
package/README.md
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
# DSH飞书频道
|
|
2
2
|
|
|
3
|
-
`@cubeai/dsh-feishu-channels`
|
|
3
|
+
`@cubeai/dsh-feishu-channels`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
把飞书机器人应用接入本机 DeepSeek Harness。
|
|
6
|
+
|
|
7
|
+
## 术语
|
|
8
|
+
|
|
9
|
+
- **频道**:本插件中的一个飞书接入单元,对应一个飞书自建机器人应用;每个频道有自己的模型、工作区、权限等配置。
|
|
10
|
+
- **飞书账号**:扫码或授权时使用的飞书用户/租户账号。添加频道不会创建新的飞书用户账号,而是在当前账号下创建或绑定机器人应用。
|
|
6
11
|
|
|
7
12
|
## 功能
|
|
8
13
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
14
|
+
- 扫码创建飞书机器人应用,自动在本插件添加频道,添加时请填写频道名称。
|
|
15
|
+
- 多飞书频道,每个频道独立的模型、工作区、权限、Agent 预设、私聊准入、发送方式、命令权限。
|
|
11
16
|
- 文本、Markdown 卡片回复、图片、文件、群聊 @、审批卡片、提问卡片、状态表情、延迟投递、斜杠命令。
|
|
12
|
-
- 左侧边栏页签 **飞书频道**(`任务 |
|
|
17
|
+
- 左侧边栏页签 **飞书频道**(`任务 | 飞书频道`)承载全部频道与设置;频道设置里可查看/复制 App ID,显示/复制 App Secret。
|
|
13
18
|
|
|
14
19
|
## 安装(本地开发)
|
|
15
20
|
|
|
@@ -41,17 +46,12 @@ dsh plugin --profile web add https://openi.pcl.ac.cn/dsh/dsh-feishu-channels.git
|
|
|
41
46
|
dsh plugin --profile web add @cubeai/dsh-feishu-channels
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## 飞书应用配置
|
|
47
|
-
|
|
48
|
-
1. 打开侧边栏 **飞书频道**,点击左侧 **添加飞书账号**。
|
|
49
|
-
2. 填写频道名称并扫码;名称会预填到飞书应用创建页。
|
|
50
|
-
3. 添加成功后账号会立即出现在频道页,不需要等首条消息。
|
|
49
|
+
## 添加飞书频道
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
1. 打开侧边栏 **飞书频道**,点击 **添加飞书频道**。
|
|
52
|
+
2. 填写频道名称并用手机飞书App扫码。
|
|
53
|
+
3. 添加成功后频道会立即出现在频道页,不需要等首条消息。
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
- 凭据:DSH 凭据服务,ref 前缀 `dfc_`
|
|
56
|
-
- 会话:`dfc:<账号>:<dm|group>:<时间戳>:<chatId>`
|
|
55
|
+
## 技术实现
|
|
57
56
|
|
|
57
|
+
如需了解内部结构、状态目录、API、消息流程和侧栏共存机制,见 [技术实现说明](docs/dsh-feishu-channels.md)。
|
package/client.js
CHANGED
|
@@ -106,6 +106,7 @@ window.__ModuleLoader__.load({
|
|
|
106
106
|
const IconDownloadOutline = pickHostIcon("IconDownloadOutlineRegular");
|
|
107
107
|
const IconCopyOutline = pickHostIcon("IconCopyOutlineRegular");
|
|
108
108
|
const IconLoadingOutline = pickHostIcon("IconLoadingOutlineRegular");
|
|
109
|
+
const IconStopFill = pickHostIcon("IconStopFillRegular", "IconStopFill16", "IconStopFill");
|
|
109
110
|
function alertIcon(type) {
|
|
110
111
|
if (type === "success") return h(IconCheckCircleOutline, { size: 14 });
|
|
111
112
|
if (type === "warning") return h(IconWarningOutline, { size: 14 });
|
|
@@ -653,51 +654,51 @@ window.__ModuleLoader__.load({
|
|
|
653
654
|
const DFC_LOCALES = {
|
|
654
655
|
zh: {
|
|
655
656
|
"diagnostic.title": "连接诊断", "diagnostic.scope": "以下为本次平台接口或心跳检查结果。未发送测试消息,消息投递权限仍未验证。", "diagnostic.credentials": "应用凭据验证", "diagnostic.bot": "机器人身份查询", "diagnostic.gateway": "网关查询", "diagnostic.passed": "通过", "diagnostic.failed": "失败", "diagnostic.unverified": "未验证",
|
|
656
|
-
"diagnostic.local-state": "
|
|
657
|
-
"connection.checkFailed": "状态检查未完成,请重试;仍失败时查看本机日志。", "connection.diagnostics": "连接诊断", "action.collapse": "收起", "action.expand": "展开", "composer.remoteTitle": "飞书频道会话", "composer.remoteHint": "该会话只能在飞书客户端继续;本地输入已禁用。", "composer.model": "模型", "composer.modelLoading": "模型加载中", "composer.modelUnavailable": "模型不可用", "composer.modelReadOnly": "
|
|
657
|
+
"diagnostic.local-state": "无法读取凭据或初始化本地频道状态,请检查本机配置及文件访问权限。", "diagnostic.cancelled": "诊断已取消,未确认连接状态。", "diagnostic.ok": "平台已返回有效响应", "diagnostic.auth": "凭据缺失或失效,请重新配置或扫码绑定。", "diagnostic.permission": "平台拒绝访问,请检查应用权限。", "diagnostic.rate-limit": "平台限流,请稍后再试。", "diagnostic.server": "平台服务异常,请稍后再试。", "diagnostic.network": "网络请求失败,请检查网络或代理。", "diagnostic.timeout": "诊断超时,请检查网络后重试。", "diagnostic.invalid-response": "平台响应不完整或格式不符,无法确认可用。", "diagnostic.rejected": "平台拒绝请求,请核对应用配置和权限。", "diagnostic.missing-context": "缺少原聊天上下文,请用扫码账号先发送一条消息后重试。", "diagnostic.not-connected": "当前没有可验证的连接,请先重新连接。", "diagnostic.unsupported": "当前 SDK 或配置不支持此诊断,未判定为通过。", "diagnostic.changed": "频道状态已变化,请重新诊断。", "connection.restartRequired": "前后端版本不一致。请完整重启 DSH 并刷新页面后重新诊断。", "connection.receiveUnknown": "无法读取接收开关,请重启 DSH 并刷新页面。",
|
|
658
|
+
"connection.checkFailed": "状态检查未完成,请重试;仍失败时查看本机日志。", "connection.diagnostics": "连接诊断", "action.collapse": "收起", "action.expand": "展开", "composer.remoteTitle": "飞书频道会话", "composer.remoteHint": "该会话只能在飞书客户端继续;本地输入已禁用。", "composer.model": "模型", "composer.modelLoading": "模型加载中", "composer.modelUnavailable": "模型不可用", "composer.modelReadOnly": "在频道设置中切换模型", "composer.stopTitle": "停止生成?", "composer.stopDescription": "将中断当前会话正在运行的大模型推理;已排队的消息会保留。", "composer.stopConfirm": "停止生成",
|
|
658
659
|
"connection.connected": "已连接", "connection.connecting": "连接中", "connection.reconnecting": "重连中", "connection.disconnected": "未连接", "connection.stopped": "已停止", "connection.error": "连接异常", "connection.unknown": "状态未知", "connection.paused": "接收已暂停",
|
|
659
|
-
"connection.title": "本次状态检查", "connection.scope": "仅检查本机渠道运行状态,未验证平台凭据或消息收发。", "connection.checkedAt": "检查时间", "connection.receiveOn": "接收开关已开启", "connection.receiveOff": "
|
|
660
|
-
"connection.next.connected": "可在原聊天发送消息验证收发;私聊仍需符合准入设置。", "connection.next.connecting": "请稍后再次检查;等待扫码时请完成手机确认。", "connection.next.reconnecting": "渠道正在尝试恢复,请稍后再次检查。", "connection.next.disconnected": "请重新连接;需要扫码的渠道可重新添加绑定。", "connection.next.stopped": "
|
|
661
|
-
"settings.label": "飞书频道", "settings.title": "飞书频道", "settings.description": "
|
|
660
|
+
"connection.title": "本次状态检查", "connection.scope": "仅检查本机渠道运行状态,未验证平台凭据或消息收发。", "connection.checkedAt": "检查时间", "connection.receiveOn": "接收开关已开启", "connection.receiveOff": "接收开关已关闭;请在频道列表开启后再发送消息。", "connection.checking": "检查中…",
|
|
661
|
+
"connection.next.connected": "可在原聊天发送消息验证收发;私聊仍需符合准入设置。", "connection.next.connecting": "请稍后再次检查;等待扫码时请完成手机确认。", "connection.next.reconnecting": "渠道正在尝试恢复,请稍后再次检查。", "connection.next.disconnected": "请重新连接;需要扫码的渠道可重新添加绑定。", "connection.next.stopped": "请点击重新连接以启动频道。", "connection.next.error": "请尝试重新连接;仍有异常时查看本机日志。", "connection.next.unknown": "暂时无法确认状态,请稍后再检查或查看本机日志。",
|
|
662
|
+
"settings.label": "飞书频道", "settings.title": "飞书频道", "settings.description": "管理飞书机器人频道。每个频道独立选择工作区、模型和权限,配置仅保存在本机。", "settings.aria": "飞书频道", "settings.publicChatNotice": "未批准用户可以发起聊天,但不能批准工具调用。", "pending.notice": "有访问请求。批准后该用户才能驱动本机助手。", "action.approve": "批准", "action.deny": "拒绝", "loading": "加载中…",
|
|
662
663
|
"account.presetUnavailable": "预设不可用", "account.presetNote": "更改后使用新会话,旧会话保留。", "action.settings": "设置", "command.dm": "私聊命令", "command.group": "群聊命令", "command.enabled": "可以执行命令", "command.disabled": "仅正常对话",
|
|
663
|
-
"account.workspace": "工作区", "account.currentWorkspace": "当前工作区", "account.selectWorkspace": "请选择工作区", "account.selectModel": "请选择模型", "account.selectPermission": "请选择权限", "account.privateAccess": "私聊准入", "account.privateApproved": "仅已批准用户", "account.privateAll": "允许所有私聊用户", "account.sendMode": "发送方式", "account.sendQueue": "排队发送", "account.sendSteer": "插话发送", "account.sendSteerNote": "插话发送会把消息作为补充指令立即交给运行中的任务;任务空闲时与排队发送相同。", "account.autoNameNote": "该名称会作为本地频道名称,并预填到飞书应用创建页。", "account.defaultName": "{channel}
|
|
664
|
-
"account.remark": "
|
|
665
|
-
"account.count": "{count}
|
|
666
|
-
"action.addAccount": "
|
|
667
|
-
"channel.feishu": "飞书", "bind.title": "
|
|
668
|
-
"qr.feishu": "
|
|
664
|
+
"account.workspace": "工作区", "account.currentWorkspace": "当前工作区", "account.selectWorkspace": "请选择工作区", "account.selectModel": "请选择模型", "account.selectPermission": "请选择权限", "account.privateAccess": "私聊准入", "account.privateApproved": "仅已批准用户", "account.privateAll": "允许所有私聊用户", "account.sendMode": "发送方式", "account.sendQueue": "排队发送", "account.sendSteer": "插话发送", "account.sendSteerNote": "插话发送会把消息作为补充指令立即交给运行中的任务;任务空闲时与排队发送相同。", "account.autoNameNote": "该名称会作为本地频道名称,并预填到飞书应用创建页。", "account.defaultName": "{channel}频道 {count}", "account.channelName": "频道名称", "account.noSessions": "还没有会话,请通过飞书客户端给机器人发一条消息...", "channel.empty": "还没有飞书频道。点击上方“添加飞书频道”开始。", "session.untitled": "未命名会话", "app.info": "飞书机器人应用信息", "app.id": "App ID", "app.secret": "App Secret", "app.copy": "复制", "app.copied": "已复制", "app.reveal": "显示", "app.hide": "隐藏", "app.notSaved": "未保存",
|
|
665
|
+
"account.remark": "频道名称", "account.rename": "修改频道名称",
|
|
666
|
+
"account.count": "{count} 个频道", "account.statusProcessing": "处理中…", "account.statusNotConnected": "未连接", "account.receive": "接收消息", "account.receiveDescription": "关闭后保留频道配置,但不接收新消息", "account.removeConfirm": "确定移除这个频道?本机保存的配置和凭据将一并删除。",
|
|
667
|
+
"action.addAccount": "添加飞书频道", "action.generateQr": "生成二维码", "action.checkConnection": "诊断连接", "action.reconnectAccount": "重新连接", "action.removeAccount": "删除频道", "status.saving": "保存中…", "status.saved": "已保存",
|
|
668
|
+
"channel.feishu": "飞书", "bind.title": "添加飞书频道", "bind.tabsAria": "绑定方式", "bind.quick": "快捷绑定(推荐)", "bind.manual": "手动配置", "bind.saving": "正在保存频道…", "bind.success": "绑定成功,频道已连接", "bind.newIdentity": "检测到新的机器人身份,已创建新频道", "bind.qrAlt": "{channel} 绑定二维码", "bind.generating": "正在生成…", "bind.expire": "二维码 {time} 后过期", "bind.scanned": "已扫码,请在手机上确认", "bind.retry": "请重新生成二维码", "bind.refresh": "重新生成二维码", "action.saving": "保存中…", "action.confirm": "确认",
|
|
669
|
+
"qr.feishu": "请在手机上使用飞书App扫描二维码,将自动创建飞书机器人应用", "qr.default": "请使用对应 App 扫描二维码",
|
|
669
670
|
"action.more": "{channel} 更多",
|
|
670
|
-
"error.loadAssistant": "
|
|
671
|
+
"error.loadAssistant": "无法加载模型配置", "error.noModels": "当前 Host 还没有可用模型,请先在网页里配置提供商", "error.save": "保存失败", "error.chooseWorkspace": "请选择工作区目录", "error.workspaceUnavailable": "当前 Host 无法新增工作区", "error.addWorkspace": "新增工作区失败", "error.load": "加载失败", "error.connection": "无法连接本机 IM 助理接口", "error.request": "请求失败", "error.action": "操作失败", "error.qr": "无法生成二维码", "error.detailsInLog": "操作失败,请查看服务器日志。",
|
|
671
672
|
"composer.noWorkspaces": "暂无工作区", "composer.addWorkspace": "添加工作区…", "composer.workspaceRequired": "请先添加工作区,否则无法保存", "composer.permission": "权限", "composer.noModels": "暂无模型", "action.cancel": "取消",
|
|
672
673
|
"permission.readOnly": "只读", "permission.workspaceWrite": "工作区写入", "permission.fullAccess": "完全访问",
|
|
673
|
-
"rail.workspace": "工作区", "rail.sessions": "会话", "rail.search": "搜索", "rail.searchPlaceholder": "搜索会话...", "rail.clearSearch": "清除搜索", "rail.filter": "筛选", "rail.group": "分组方式", "rail.byWorkspace": "按工作区", "rail.list": "单列表", "rail.byWorkspaceTree": "按工作区树", "rail.byChannel": "
|
|
674
|
+
"rail.workspace": "工作区", "rail.sessions": "会话", "rail.search": "搜索", "rail.searchPlaceholder": "搜索会话...", "rail.clearSearch": "清除搜索", "rail.filter": "筛选", "rail.group": "分组方式", "rail.byWorkspace": "按工作区", "rail.list": "单列表", "rail.byWorkspaceTree": "按工作区树", "rail.byChannel": "按飞书频道", "rail.treeUnavailable": "当前宿主未提供官方任务树。", "rail.groupReadOnly": "飞书分组不能在这里重命名或删除。", "rail.sort": "排序方式", "rail.recent": "最近更新", "rail.showArchived": "显示已归档", "rail.onlyArchived": "仅显示已归档", "rail.running": "运行中", "rail.idle": "空闲", "rail.waitingApproval": "等待审批", "rail.planReview": "计划待审", "rail.waitingAnswer": "等待回答", "rail.subagents": "{n} 个子代理运行中", "rail.completed": "已完成", "rail.archived": "已归档", "rail.compactApproval": "待审批", "rail.compactPlan": "计划待审", "rail.compactAnswer": "待回答", "rail.renameAria": "重命名会话", "rail.rename": "重命名", "rail.fork": "分叉会话", "rail.archive": "归档会话", "rail.unarchive": "恢复会话", "rail.deleteSession": "删除会话", "rail.deleteSessionConfirm": "删除会话", "rail.deleteSessionDescription": "将永久删除会话“{name}”及其子代理(含正在运行的)和全部记录(对话内容、统计、缓存),此操作不可恢复。", "rail.deleteSessionPending": "正在删除会话…", "rail.deleteSessionFailed": "删除会话失败:{message}", "rail.deleteSessionClose": "关闭", "rail.deleteSessionCancel": "取消", "rail.groupActions": "“{name}”的飞书操作", "rail.channelSettings": "飞书频道设置", "rail.addChannel": "添加飞书频道", "rail.empty": "还没有飞书会话。先在设置 → 飞书频道 里连接飞书机器人,并给机器人发一条消息。", "rail.ungrouped": "未分组", "rail.tabsAria": "工作区分类", "rail.tasks": "任务", "rail.channels": "频道", "rail.feishu": "飞书频道",
|
|
674
675
|
"time.now": "刚刚", "time.minutes": "{n}分钟", "time.hours": "{n}小时", "time.days": "{n}天", "time.months": "{n}个月", "time.years": "{n}年", "time.ago": "{t}前",
|
|
675
676
|
"copy": "复制", "hover.copied": "已复制",
|
|
676
|
-
"server.unknownChannel": "未知渠道", "server.channelUnconfigured": "渠道未配置", "server.sessionMissing": "会话不存在", "server.accountMissing": "
|
|
677
|
+
"server.unknownChannel": "未知渠道", "server.channelUnconfigured": "渠道未配置", "server.sessionMissing": "会话不存在", "server.accountMissing": "频道不存在", "server.accountConnectFailed": "频道连接失败,请查看本机日志", "server.accountReconnectFailed": "重新连接频道失败,请查看本机日志", "server.selectAccountSettings": "请选择提供商、模型、工作区或权限", "server.selectModel": "请选择提供商和模型", "server.selectWorkspace": "请选择工作区", "server.createWorkspace": "请先添加工作区", "server.selectPermission": "请选择权限", "server.missingCredentials": "凭据不足,无法启动渠道", "server.qrUnsupported": "该渠道不支持扫码绑定", "server.qrExpired": "二维码已过期", "server.qrIncomplete": "扫码未完成", "server.accessDenied": "未授权:请管理员在设置 → 飞书频道 中批准你的访问。", "server.remarkTooLong": "频道名称不能超过 40 个字符", "server.remarkControl": "频道名称不能包含换行或控制字符",
|
|
677
678
|
"status.stopped": "已停止", "status.longConnection": "长连接已建立", },
|
|
678
679
|
en: {
|
|
679
680
|
"diagnostic.title": "Connection diagnostics", "diagnostic.scope": "Results from platform queries or a connection heartbeat in this check. No test messages were sent; message delivery permissions remain unverified.", "diagnostic.credentials": "Application credentials", "diagnostic.bot": "Bot identity query", "diagnostic.gateway": "Gateway query", "diagnostic.passed": "Passed", "diagnostic.failed": "Failed", "diagnostic.unverified": "Unverified",
|
|
680
|
-
"diagnostic.local-state": "Could not read credentials or initialize local
|
|
681
|
-
"connection.checkFailed": "The status check could not be completed. Retry, or check the local logs if it keeps failing.", "connection.diagnostics": "Connection diagnostics", "action.collapse": "Collapse", "action.expand": "Expand", "composer.remoteTitle": "Feishu channel session", "composer.remoteHint": "This session can only continue in Feishu; local input is disabled.", "composer.model": "Model", "composer.modelLoading": "Loading models", "composer.modelUnavailable": "Model unavailable", "composer.modelReadOnly": "Change the model in
|
|
681
|
+
"diagnostic.local-state": "Could not read credentials or initialize local channel state. Check local configuration and file permissions.", "diagnostic.cancelled": "The check was cancelled. Connection status was not verified.", "diagnostic.ok": "The platform returned a valid response", "diagnostic.auth": "Credentials are missing or invalid. Configure the app or pair it again.", "diagnostic.permission": "Access was denied. Check application permissions.", "diagnostic.rate-limit": "The platform rate limit was reached. Retry later.", "diagnostic.server": "The platform service failed. Retry later.", "diagnostic.network": "The request failed. Check your network or proxy.", "diagnostic.timeout": "The check timed out. Check your network and retry.", "diagnostic.invalid-response": "The platform response was incomplete or invalid. Availability could not be confirmed.", "diagnostic.rejected": "The platform rejected the request. Check application settings and permissions.", "diagnostic.missing-context": "The original chat context is missing. Send a message from the paired account and retry.", "diagnostic.not-connected": "No active connection is available to verify. Reconnect first.", "diagnostic.unsupported": "The current SDK or configuration does not support this check. It has not passed.", "diagnostic.changed": "The channel state changed. Run diagnostics again.", "connection.restartRequired": "The frontend and backend versions do not match. Fully restart DSH, refresh this page, and retry.", "connection.receiveUnknown": "The receiving setting is unavailable. Restart DSH and refresh this page.",
|
|
682
|
+
"connection.checkFailed": "The status check could not be completed. Retry, or check the local logs if it keeps failing.", "connection.diagnostics": "Connection diagnostics", "action.collapse": "Collapse", "action.expand": "Expand", "composer.remoteTitle": "Feishu channel session", "composer.remoteHint": "This session can only continue in Feishu; local input is disabled.", "composer.model": "Model", "composer.modelLoading": "Loading models", "composer.modelUnavailable": "Model unavailable", "composer.modelReadOnly": "Change the model in channel settings", "composer.stopTitle": "Stop generating?", "composer.stopDescription": "This interrupts the running model turn in this session; queued messages are preserved.", "composer.stopConfirm": "Stop generating",
|
|
682
683
|
"connection.connected": "Connected", "connection.connecting": "Connecting", "connection.reconnecting": "Reconnecting", "connection.disconnected": "Disconnected", "connection.stopped": "Stopped", "connection.error": "Connection error", "connection.unknown": "Status unknown", "connection.paused": "Receiving paused",
|
|
683
|
-
"connection.title": "Status check result", "connection.scope": "Checks local channel runtime only. Platform credentials and message delivery have not been verified.", "connection.checkedAt": "Checked at", "connection.receiveOn": "Receiving is enabled", "connection.receiveOff": "Receiving is disabled. Enable it in the
|
|
684
|
-
"connection.next.connected": "Send a message in the original chat to verify delivery. Private chats still follow access settings.", "connection.next.connecting": "Check again shortly. Complete confirmation on your phone if a QR scan is pending.", "connection.next.reconnecting": "The channel is attempting to recover. Check again shortly.", "connection.next.disconnected": "Reconnect the
|
|
685
|
-
"settings.label": "Feishu Channel", "settings.title": "Feishu Channel", "settings.description": "Manage Feishu bot
|
|
684
|
+
"connection.title": "Status check result", "connection.scope": "Checks local channel runtime only. Platform credentials and message delivery have not been verified.", "connection.checkedAt": "Checked at", "connection.receiveOn": "Receiving is enabled", "connection.receiveOff": "Receiving is disabled. Enable it in the channel list before sending messages.", "connection.checking": "Checking…",
|
|
685
|
+
"connection.next.connected": "Send a message in the original chat to verify delivery. Private chats still follow access settings.", "connection.next.connecting": "Check again shortly. Complete confirmation on your phone if a QR scan is pending.", "connection.next.reconnecting": "The channel is attempting to recover. Check again shortly.", "connection.next.disconnected": "Reconnect the channel. QR channels may need to be added again.", "connection.next.stopped": "Select Reconnect to start the channel.", "connection.next.error": "Try reconnecting. If the issue persists, check the local logs.", "connection.next.unknown": "The current state could not be confirmed. Check again later or inspect the local logs.",
|
|
686
|
+
"settings.label": "Feishu Channel", "settings.title": "Feishu Channel", "settings.description": "Manage Feishu bot channels. Each channel has its own workspace, model, and permission settings, stored only on this machine.", "settings.aria": "Feishu Channel", "settings.publicChatNotice": "Unapproved users can start chats, but they cannot approve tool calls.", "pending.notice": "There are access requests. Approve a user before they can control the local assistant.", "action.approve": "Approve", "action.deny": "Deny", "loading": "Loading…",
|
|
686
687
|
"account.presetUnavailable": "Preset unavailable", "account.presetNote": "Changes start a new session; previous sessions are kept.", "action.settings": "Settings", "command.dm": "DM commands", "command.group": "Group commands", "command.enabled": "Commands enabled", "command.disabled": "Conversation only",
|
|
687
|
-
"account.workspace": "Workspace", "account.currentWorkspace": "Current workspace", "account.selectWorkspace": "Select a workspace", "account.selectModel": "Select a model", "account.selectPermission": "Select a permission", "account.privateAccess": "Private chat access", "account.privateApproved": "Approved users only", "account.privateAll": "Allow all DM users", "account.sendMode": "Send mode", "account.sendQueue": "Queue", "account.sendSteer": "Steer", "account.sendSteerNote": "Steer hands the message to the running task as additional instructions at its next step; when the task is idle it behaves like Queue.", "account.autoNameNote": "Used as the local channel name and prefilled on the Feishu app creation page.", "account.defaultName": "{channel}
|
|
688
|
-
"account.remark": "
|
|
689
|
-
"account.count": "{count}
|
|
690
|
-
"action.addAccount": "Add Feishu
|
|
691
|
-
"channel.feishu": "Feishu", "bind.title": "Add Feishu
|
|
688
|
+
"account.workspace": "Workspace", "account.currentWorkspace": "Current workspace", "account.selectWorkspace": "Select a workspace", "account.selectModel": "Select a model", "account.selectPermission": "Select a permission", "account.privateAccess": "Private chat access", "account.privateApproved": "Approved users only", "account.privateAll": "Allow all DM users", "account.sendMode": "Send mode", "account.sendQueue": "Queue", "account.sendSteer": "Steer", "account.sendSteerNote": "Steer hands the message to the running task as additional instructions at its next step; when the task is idle it behaves like Queue.", "account.autoNameNote": "Used as the local channel name and prefilled on the Feishu app creation page.", "account.defaultName": "{channel} channel {count}", "account.channelName": "Channel name", "account.noSessions": "No sessions yet. Send the bot a message from the Feishu client...", "channel.empty": "No Feishu channels yet. Use “Add Feishu channel” above.", "session.untitled": "Untitled session", "app.info": "Feishu app info", "app.id": "App ID", "app.secret": "App Secret", "app.copy": "Copy", "app.copied": "Copied", "app.reveal": "Show", "app.hide": "Hide", "app.notSaved": "Not saved",
|
|
689
|
+
"account.remark": "Channel name", "account.rename": "Edit channel name",
|
|
690
|
+
"account.count": "{count} channels", "account.statusProcessing": "Processing…", "account.statusNotConnected": "Not connected", "account.receive": "Receive messages", "account.receiveDescription": "Turn this off to keep the channel settings without receiving new messages", "account.removeConfirm": "Remove this channel? Its saved settings and credentials will also be deleted.",
|
|
691
|
+
"action.addAccount": "Add Feishu channel", "action.generateQr": "Generate QR code", "action.checkConnection": "Diagnose connection", "action.reconnectAccount": "Reconnect", "action.removeAccount": "Remove", "status.saving": "Saving…", "status.saved": "Saved",
|
|
692
|
+
"channel.feishu": "Feishu", "bind.title": "Add Feishu channel", "bind.tabsAria": "Setup method", "bind.quick": "Quick setup (recommended)", "bind.manual": "Manual setup", "bind.saving": "Saving channel…", "bind.success": "Connected successfully", "bind.newIdentity": "A new bot identity was detected and a new channel was created", "bind.qrAlt": "{channel} setup QR code", "bind.generating": "Generating…", "bind.expire": "QR code expires in {time}", "bind.scanned": "Scanned. Confirm on your phone.", "bind.retry": "Generate a new QR code", "bind.refresh": "Generate a new QR code", "action.saving": "Saving…", "action.confirm": "Confirm",
|
|
692
693
|
"qr.feishu": "Scan with Feishu; a bot will be created automatically", "qr.default": "Scan the QR code with the corresponding app",
|
|
693
694
|
"action.more": "More options for {channel}",
|
|
694
|
-
"error.loadAssistant": "Could not load
|
|
695
|
+
"error.loadAssistant": "Could not load model settings", "error.noModels": "No models are available in the Host. Configure a provider in the web app first.", "error.save": "Could not save", "error.chooseWorkspace": "Choose a workspace directory", "error.workspaceUnavailable": "This Host cannot create workspaces", "error.addWorkspace": "Could not add workspace", "error.load": "Could not load", "error.connection": "Could not connect to the local Feishu Channel API", "error.request": "Request failed", "error.action": "Action failed", "error.qr": "Could not generate a QR code", "error.detailsInLog": "The operation failed. Check the server logs for details.",
|
|
695
696
|
"composer.noWorkspaces": "No workspaces", "composer.addWorkspace": "Add workspace…", "composer.workspaceRequired": "Add a workspace before saving.", "composer.permission": "Permission", "composer.noModels": "No models", "action.cancel": "Cancel",
|
|
696
697
|
"permission.readOnly": "Read Only", "permission.workspaceWrite": "Workspace Write", "permission.fullAccess": "Full access",
|
|
697
698
|
"rail.workspace": "Workspaces", "rail.sessions": "Sessions", "rail.search": "Search", "rail.searchPlaceholder": "Search sessions...", "rail.clearSearch": "Clear search", "rail.filter": "Filter", "rail.group": "Group by", "rail.byWorkspace": "By workspace", "rail.list": "Single list", "rail.byWorkspaceTree": "Workspace Tree", "rail.byChannel": "By channel", "rail.treeUnavailable": "The official tree is unavailable in this Host.", "rail.groupReadOnly": "Feishu groups cannot be renamed or deleted here.", "rail.sort": "Sort by", "rail.recent": "Recently updated", "rail.showArchived": "Show archived", "rail.onlyArchived": "Archived only", "rail.running": "Running", "rail.idle": "Idle", "rail.waitingApproval": "Waiting for approval", "rail.planReview": "Plan to review", "rail.waitingAnswer": "Waiting for an answer", "rail.subagents": "{n} subagents running", "rail.completed": "Completed", "rail.archived": "Archived", "rail.compactApproval": "Approval", "rail.compactPlan": "Plan review", "rail.compactAnswer": "Answer", "rail.renameAria": "Rename session", "rail.rename": "Rename", "rail.fork": "Fork session", "rail.archive": "Archive session", "rail.unarchive": "Restore session", "rail.deleteSession": "Delete session", "rail.deleteSessionConfirm": "Delete session", "rail.deleteSessionDescription": "This permanently deletes session “{name}”, its child agents (including any that are still running), and all of its records (conversation, stats, cache). This cannot be undone.", "rail.deleteSessionPending": "Deleting session…", "rail.deleteSessionFailed": "Could not delete the session: {message}", "rail.deleteSessionClose": "Close", "rail.deleteSessionCancel": "Cancel", "rail.groupActions": "Actions for {name}", "rail.channelSettings": "Channel settings", "rail.addChannel": "Add channel", "rail.empty": "No Feishu sessions yet. Connect a Feishu bot in Settings → Feishu Channel, then send it a message.", "rail.ungrouped": "Ungrouped", "rail.tabsAria": "Workspace categories", "rail.tasks": "Tasks", "rail.channels": "Channels", "rail.feishu": "Feishu",
|
|
698
699
|
"time.now": "now", "time.minutes": "{n}min", "time.hours": "{n}h", "time.days": "{n}d", "time.months": "{n}mo", "time.years": "{n}y", "time.ago": "{t} ago",
|
|
699
700
|
"copy": "Copy", "hover.copied": "Copied",
|
|
700
|
-
"server.unknownChannel": "Unknown channel", "server.channelUnconfigured": "Channel is not configured", "server.sessionMissing": "Session does not exist", "server.accountMissing": "
|
|
701
|
+
"server.unknownChannel": "Unknown channel", "server.channelUnconfigured": "Channel is not configured", "server.sessionMissing": "Session does not exist", "server.accountMissing": "Channel does not exist", "server.accountConnectFailed": "Could not connect the channel. Check the local logs.", "server.accountReconnectFailed": "Could not reconnect the channel. Check the local logs.", "server.selectAccountSettings": "Select a provider, model, workspace, or permission setting", "server.selectModel": "Select a provider and model", "server.selectWorkspace": "Select a workspace", "server.createWorkspace": "Add a workspace first.", "server.selectPermission": "Select a permission", "server.missingCredentials": "The channel cannot start because credentials are missing", "server.qrUnsupported": "This channel does not support QR setup", "server.qrExpired": "QR code has expired", "server.qrIncomplete": "QR setup was not completed", "server.accessDenied": "Access denied: ask an administrator to approve you in Settings → Feishu Channel.", "server.remarkTooLong": "Channel name cannot exceed 40 characters", "server.remarkControl": "Channel name cannot contain line breaks or control characters",
|
|
701
702
|
"status.stopped": "Stopped", "status.longConnection": "Long connection established", },
|
|
702
703
|
};
|
|
703
704
|
const fallbackT = (key) => key;
|
|
@@ -881,10 +882,10 @@ window.__ModuleLoader__.load({
|
|
|
881
882
|
.dfc-btn-danger-primary,.dfc-btn-danger.dfc-btn-danger-primary{background:var(--dsw-alias-state-error-primary,var(--dfc-danger))!important;border-color:var(--dsw-alias-state-error-primary,var(--dfc-danger))!important;color:#fff!important}
|
|
882
883
|
@container (max-width:480px){.dfc-account-row{grid-template-columns:minmax(0,1fr) auto auto;gap:8px;padding:10px}.dfc-account-receive>span{display:none}.dfc-platform-head{padding:10px 12px}.dfc-account-list{padding:0 12px 12px}}
|
|
883
884
|
|
|
884
|
-
.dfc-channel-page{display:flex;flex-direction:column;gap:10px;min-height:0;height:100%;box-sizing:border-box;padding:10px
|
|
885
|
-
.dfc-channel-head{display:flex;align-items:center;justify-content:
|
|
886
|
-
.dfc-channel-add{height:
|
|
887
|
-
.dfc-channel-add:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))!important;color:var(--dsw-alias-label-primary,#eee)!important}
|
|
885
|
+
.dfc-channel-page{display:flex;flex-direction:column;gap:10px;min-height:0;height:100%;box-sizing:border-box;padding:10px 14px 16px 3px;overflow:auto}
|
|
886
|
+
.dfc-channel-head{display:flex;align-items:center;justify-content:stretch;gap:8px;min-height:30px;padding:0 0 8px;flex:none;width:100%}
|
|
887
|
+
.dfc-channel-add{width:100%!important;height:30px!important;min-height:30px!important;padding:0 10px!important;border:0.5px solid var(--dsw-alias-border-l2,#494949)!important;border-radius:var(--dsw-radius-sm,8px)!important;background:var(--dsw-alias-bg-layer-1,transparent)!important;color:var(--dsw-alias-label-secondary,#b9b9b9)!important;font-size:12px!important;font-weight:500!important;box-shadow:none!important}
|
|
888
|
+
.dfc-channel-add:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))!important;border-color:var(--dsw-alias-border-l3,#666)!important;color:var(--dsw-alias-label-primary,#eee)!important}
|
|
888
889
|
.dfc-channel-empty{display:flex;align-items:center;justify-content:center;min-height:120px;padding:18px;border:0.5px dashed var(--dsw-alias-border-l2,#494949);border-radius:var(--dsw-radius-md,12px);color:var(--dsw-alias-label-tertiary,#888);font-size:12px;line-height:1.6;text-align:center}
|
|
889
890
|
.dfc-channel-pending{padding:10px;border:0.5px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#d29922) 35%,transparent);border-radius:var(--dsw-radius-md,12px);background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#d29922) 8%,transparent)}
|
|
890
891
|
.dfc-channel-pending-title{font-size:12px;font-weight:600;color:var(--dsw-alias-state-warn-primary,#d29922);margin-bottom:6px}
|
|
@@ -940,6 +941,9 @@ window.__ModuleLoader__.load({
|
|
|
940
941
|
.dfc-remote-model-label{font-size:12px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888);white-space:nowrap}
|
|
941
942
|
.dfc-remote-model-value{box-sizing:border-box;max-width:260px;padding:5px 9px;border:0.5px solid var(--dsw-alias-border-l2,#494949);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#eee);font-size:13px;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
942
943
|
.dfc-remote-model-missing{font-size:11px}
|
|
944
|
+
.dfc-remote-stop{flex:none;width:30px!important;height:30px!important;min-width:30px!important;padding:0!important;border-radius:50%!important;background:#111!important;border:0.5px solid #333!important;color:#fff!important;margin-left:8px;box-shadow:none!important}
|
|
945
|
+
.dfc-remote-stop:hover:not(:disabled){background:#000!important;border-color:#444!important;color:#fff!important}
|
|
946
|
+
.dfc-remote-stop svg{fill:currentColor!important}
|
|
943
947
|
.dfc-remote-model-missing{flex:none;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#888);white-space:nowrap}
|
|
944
948
|
@media (max-width:520px){.dfc-app-fields{grid-template-columns:minmax(0,1fr)}.dfc-remote-composer-card{flex-wrap:wrap}.dfc-remote-model{margin-left:auto}.dfc-remote-model-value{max-width:180px}}
|
|
945
949
|
|
|
@@ -999,7 +1003,7 @@ window.__ModuleLoader__.load({
|
|
|
999
1003
|
const platform = instance.split("_", 1)[0];
|
|
1000
1004
|
return platform || undefined;
|
|
1001
1005
|
}
|
|
1002
|
-
/** 从 dfc:<account>:<kind>:...
|
|
1006
|
+
/** 从 dfc:<account>:<kind>:... 中取出飞书频道 id。 */
|
|
1003
1007
|
function accountIdOfSession(sessionId) {
|
|
1004
1008
|
const value = String(sessionId || "");
|
|
1005
1009
|
if (!value.startsWith("dfc:")) return "";
|
|
@@ -1192,8 +1196,8 @@ window.__ModuleLoader__.load({
|
|
|
1192
1196
|
|
|
1193
1197
|
const SERVER_TEXT_KEYS = new Map([
|
|
1194
1198
|
["未知渠道", "server.unknownChannel"], ["渠道未配置", "server.channelUnconfigured"], ["会话不存在", "server.sessionMissing"],
|
|
1195
|
-
["
|
|
1196
|
-
["
|
|
1199
|
+
["频道不存在", "server.accountMissing"], ["频道连接失败,请查看本机日志", "server.accountConnectFailed"], ["重新连接频道失败,请查看本机日志", "server.accountReconnectFailed"],
|
|
1200
|
+
["频道名称不能超过 40 个字符", "server.remarkTooLong"], ["频道名称不能包含换行或控制字符", "server.remarkControl"],
|
|
1197
1201
|
["请选择提供商、模型、工作区或权限", "server.selectAccountSettings"], ["请选择提供商和模型", "server.selectModel"], ["请选择工作区", "server.selectWorkspace"], ["请先添加工作区", "server.createWorkspace"], ["请选择权限", "server.selectPermission"], ["凭据不足,无法启动渠道", "server.missingCredentials"],
|
|
1198
1202
|
["该渠道不支持扫码绑定", "server.qrUnsupported"], ["二维码已过期", "server.qrExpired"], ["扫码未完成", "server.qrIncomplete"],
|
|
1199
1203
|
["未授权:请管理员在设置 → 飞书频道 中批准你的访问。", "server.accessDenied"],
|
|
@@ -1250,7 +1254,7 @@ window.__ModuleLoader__.load({
|
|
|
1250
1254
|
|
|
1251
1255
|
function defaultChannelName(ch) {
|
|
1252
1256
|
const count = Array.isArray(ch && ch.accounts) ? ch.accounts.length : 0;
|
|
1253
|
-
return
|
|
1257
|
+
return `飞书频道${Math.max(1, count + 1)}`;
|
|
1254
1258
|
}
|
|
1255
1259
|
|
|
1256
1260
|
function channelLabel(ch, t) {
|
|
@@ -2212,7 +2216,6 @@ window.__ModuleLoader__.load({
|
|
|
2212
2216
|
h("div", { className: "dfc-picker-field" },
|
|
2213
2217
|
h("span", { className: "dfc-picker-label" }, t("account.remark")),
|
|
2214
2218
|
h(AccountRemarkEditor, { account, t, always: true, disabled: Boolean(busy), onSave: (name) => save({ name }) }),
|
|
2215
|
-
h("div", { className: "dfc-picker-note" }, t("account.remarkNote")),
|
|
2216
2219
|
),
|
|
2217
2220
|
h(AccountSettingsPicker, {
|
|
2218
2221
|
value: draft,
|
|
@@ -2348,6 +2351,57 @@ window.__ModuleLoader__.load({
|
|
|
2348
2351
|
);
|
|
2349
2352
|
}
|
|
2350
2353
|
|
|
2354
|
+
function FeishuStopButton({ ctx, sessionId, useSession, t }) {
|
|
2355
|
+
const running = useSession((state) => Boolean(state && state.running));
|
|
2356
|
+
const [modal, modalHolder] = Modal.useModal();
|
|
2357
|
+
if (!running) return null;
|
|
2358
|
+
const cancel = async () => {
|
|
2359
|
+
const scoped = ctx && ctx.sessions && typeof ctx.sessions.scope === "function"
|
|
2360
|
+
? ctx.sessions.scope(sessionId)
|
|
2361
|
+
: undefined;
|
|
2362
|
+
const conversation = scoped && typeof scoped.get === "function"
|
|
2363
|
+
? scoped.get("conversation")
|
|
2364
|
+
: undefined;
|
|
2365
|
+
if (conversation && typeof conversation.cancel === "function") {
|
|
2366
|
+
await conversation.cancel();
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
if (ctx && ctx.sessions && typeof ctx.sessions.cancel === "function") {
|
|
2370
|
+
await ctx.sessions.cancel(sessionId);
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
throw new Error("session cancel unavailable");
|
|
2374
|
+
};
|
|
2375
|
+
const requestStop = () => {
|
|
2376
|
+
modal.confirm({
|
|
2377
|
+
title: t("composer.stopTitle"),
|
|
2378
|
+
description: t("composer.stopDescription"),
|
|
2379
|
+
okText: t("composer.stopConfirm"),
|
|
2380
|
+
cancelText: t("action.cancel"),
|
|
2381
|
+
okButtonProps: { danger: true },
|
|
2382
|
+
onOk: async () => {
|
|
2383
|
+
try {
|
|
2384
|
+
await cancel();
|
|
2385
|
+
} catch (error) {
|
|
2386
|
+
console.warn("[dsh-feishu-channels] 无法停止推理", error);
|
|
2387
|
+
}
|
|
2388
|
+
},
|
|
2389
|
+
});
|
|
2390
|
+
};
|
|
2391
|
+
return h(React.Fragment, null,
|
|
2392
|
+
modalHolder,
|
|
2393
|
+
h(Button, {
|
|
2394
|
+
size: "sm",
|
|
2395
|
+
variant: "outline",
|
|
2396
|
+
className: "dfc-remote-stop",
|
|
2397
|
+
icon: h(IconStopFill, { size: 12 }),
|
|
2398
|
+
"aria-label": t("composer.stopConfirm"),
|
|
2399
|
+
title: t("composer.stopConfirm"),
|
|
2400
|
+
onClick: requestStop,
|
|
2401
|
+
}),
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2351
2405
|
function FeishuComposerGuard(props) {
|
|
2352
2406
|
const t = props.t || fallbackT;
|
|
2353
2407
|
return h("div", { className: "dfc-remote-composer", "data-dfc-composer-guard": "1", role: "group", "aria-label": t("composer.remoteTitle") },
|
|
@@ -2358,6 +2412,9 @@ window.__ModuleLoader__.load({
|
|
|
2358
2412
|
h("div", { className: "dfc-remote-composer-hint" }, t("composer.remoteHint")),
|
|
2359
2413
|
),
|
|
2360
2414
|
h(FeishuRemoteModelSelect, { sessionId: props.sessionId, t }),
|
|
2415
|
+
typeof props.useSession === "function"
|
|
2416
|
+
? h(FeishuStopButton, { ctx: props.ctx, sessionId: props.sessionId, useSession: props.useSession, t })
|
|
2417
|
+
: null,
|
|
2361
2418
|
),
|
|
2362
2419
|
);
|
|
2363
2420
|
}
|
|
@@ -2472,8 +2529,9 @@ window.__ModuleLoader__.load({
|
|
|
2472
2529
|
return h("section", { className: "dfc-channel-page", "aria-label": t("settings.label") },
|
|
2473
2530
|
h("div", { className: "dfc-channel-head" },
|
|
2474
2531
|
h(Button, {
|
|
2475
|
-
variant: "
|
|
2532
|
+
variant: "outline",
|
|
2476
2533
|
size: "sm",
|
|
2534
|
+
block: true,
|
|
2477
2535
|
className: "dfc-channel-add",
|
|
2478
2536
|
icon: h(IconPlusOutline, { size: 13 }),
|
|
2479
2537
|
disabled: !data,
|
package/cordis.patch.yml
CHANGED