@wenbin_wb/dsh-bridge 2.2.8 → 2.2.9
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/lib/platform/conversation-bridge.js +63 -39
- package/lib/qq/node.js +5 -21
- package/package.json +1 -1
|
@@ -636,9 +636,11 @@ export class ConversationBridge {
|
|
|
636
636
|
const prompt = [
|
|
637
637
|
`## ⚠️ 操作权限确认 (#${number})`,
|
|
638
638
|
'',
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
639
|
+
'| 项目 | 详情 |',
|
|
640
|
+
'| :--- | :--- |',
|
|
641
|
+
`| **调用工具** | \`${req.toolName}\` |`,
|
|
642
|
+
...(req.reason ? [`| **申请原因** | ${String(req.reason).replace(/\|/g, '|')} |`] : []),
|
|
643
|
+
`| **等待超时** | ${timeoutMin} 分钟 (超时自动拒绝) |`,
|
|
642
644
|
'',
|
|
643
645
|
`> 回复 \`/yes\` (或 \`1\`) 批准执行`,
|
|
644
646
|
`> 回复 \`/no\` (或 \`2\`) 拒绝执行`,
|
|
@@ -827,11 +829,19 @@ async function routeCommand(node, text) {
|
|
|
827
829
|
await node.sendText(`## 🗂️ 可用工作区\n\n> 当前没有已注册的工作区。可使用 \`/new <提示词> @<路径>\` 指定项目目录。`)
|
|
828
830
|
return true
|
|
829
831
|
}
|
|
830
|
-
const
|
|
831
|
-
const titleStr = w.title && w.title !== w.path ?
|
|
832
|
-
|
|
832
|
+
const rows = workspaces.map((w, i) => {
|
|
833
|
+
const titleStr = w.title && w.title !== w.path ? w.title : getWorkspaceBasename(w.path)
|
|
834
|
+
const safeTitle = titleStr.replace(/\|/g, '|')
|
|
835
|
+
return `| **@${i + 1}** | ${safeTitle} | \`${w.path}\` |`
|
|
833
836
|
})
|
|
834
|
-
await node.sendText(
|
|
837
|
+
await node.sendText([
|
|
838
|
+
`## 🗂️ 可用工作区 (共 ${workspaces.length} 个)`,
|
|
839
|
+
`> 新建会话:发送 \`/new <提示词> @序号\` 或 \`/new <提示词> @路径\``,
|
|
840
|
+
'',
|
|
841
|
+
'| 序号 | 工作区名称 | 目录路径 |',
|
|
842
|
+
'| :--- | :--- | :--- |',
|
|
843
|
+
...rows,
|
|
844
|
+
].join('\n'))
|
|
835
845
|
return true
|
|
836
846
|
}
|
|
837
847
|
case 'new': {
|
|
@@ -901,10 +911,23 @@ async function routeCommand(node, text) {
|
|
|
901
911
|
const lastTurn = [...(session.events ?? [])].reverse().find((e) => e.type === 'turn/end')
|
|
902
912
|
const reason = lastTurn ? describeTurnEnd(lastTurn.data.reason) : '尚未运行'
|
|
903
913
|
const title = session.title || (session.events ? sessionLabel(session) : '')
|
|
904
|
-
const
|
|
905
|
-
const
|
|
914
|
+
const shortId = fmtSessionId(session.id)
|
|
915
|
+
const cwd = session.header?.cwd || node.config?.cwd || ''
|
|
906
916
|
|
|
907
|
-
|
|
917
|
+
const content = [
|
|
918
|
+
`## 📊 Agent 状态看板`,
|
|
919
|
+
'',
|
|
920
|
+
'| 属性 | 当前状态 / 参数 |',
|
|
921
|
+
'| :--- | :--- |',
|
|
922
|
+
`| **会话 ID** | \`${shortId}\` |`,
|
|
923
|
+
...(title ? [`| **会话标题** | ${title.replace(/\|/g, '|')} |`] : []),
|
|
924
|
+
...(cwd ? [`| **工作区** | \`${cwd}\` |`] : []),
|
|
925
|
+
`| **Agent 状态** | ${status} |`,
|
|
926
|
+
`| **累计事件** | ${session.seq ?? 0} 条 |`,
|
|
927
|
+
`| **最近执行** | ${reason} |`,
|
|
928
|
+
].join('\n')
|
|
929
|
+
|
|
930
|
+
await node.sendText(content)
|
|
908
931
|
return true
|
|
909
932
|
}
|
|
910
933
|
case 'start': // 别名:首次扫码自动开始一个会话
|
|
@@ -949,22 +972,21 @@ async function renderSessions(node) {
|
|
|
949
972
|
let idx = 0
|
|
950
973
|
for (const [cwd, sessions] of sortedGroups) {
|
|
951
974
|
const groupName = cwd === '(未指定)' ? '📁 未指定工作区' : `📁 **${getWorkspaceBasename(cwd)}**`
|
|
952
|
-
|
|
953
|
-
parts.push(
|
|
954
|
-
parts.push('
|
|
975
|
+
parts.push(groupName)
|
|
976
|
+
parts.push('')
|
|
977
|
+
parts.push('| 序号 | 会话标题 / 摘要 | 时间 | 状态 |')
|
|
978
|
+
parts.push('| :--- | :--- | :--- | :--- |')
|
|
955
979
|
for (const session of sessions.slice(0, 20)) {
|
|
956
980
|
idx += 1
|
|
957
981
|
const isActive = session.id === node.activeSessionId
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
const
|
|
961
|
-
const when = session.createdAt ? fmtTime(session.createdAt) : ''
|
|
962
|
-
|
|
963
|
-
parts.push(`- **#${idx}** ${label}${activeTag}`)
|
|
964
|
-
parts.push(` \`${fmtSessionId(session.id)}\`${timeStr}`)
|
|
982
|
+
const statusTag = isActive ? '`[当前]`' : '-'
|
|
983
|
+
const rawTitle = session.title || (session.events ? sessionLabel(session) : '')
|
|
984
|
+
const safeTitle = (rawTitle || '新会话 (待输入)').replace(/\|/g, '|').replace(/\r?\n/g, ' ')
|
|
985
|
+
const when = session.createdAt ? fmtTime(session.createdAt) : '-'
|
|
986
|
+
parts.push(`| **#${idx}** | ${safeTitle} | ${when} | ${statusTag} |`)
|
|
965
987
|
}
|
|
966
988
|
if (sessions.length > 20) {
|
|
967
|
-
parts.push(
|
|
989
|
+
parts.push(`*…该工作区共 ${sessions.length} 个会话,仅显示前 20 个*`)
|
|
968
990
|
}
|
|
969
991
|
parts.push('')
|
|
970
992
|
}
|
|
@@ -1009,26 +1031,28 @@ function helpText() {
|
|
|
1009
1031
|
return [
|
|
1010
1032
|
'## 🤖 常用指令帮助',
|
|
1011
1033
|
'',
|
|
1012
|
-
'
|
|
1013
|
-
'',
|
|
1014
|
-
'
|
|
1015
|
-
'
|
|
1016
|
-
'
|
|
1017
|
-
'
|
|
1018
|
-
'
|
|
1019
|
-
'
|
|
1020
|
-
'
|
|
1021
|
-
'',
|
|
1022
|
-
'**📁 环境与状态**',
|
|
1023
|
-
'',
|
|
1024
|
-
'- `/workspaces` — 查看已注册的工作区列表',
|
|
1025
|
-
'- `/status` — 查看 Agent 运行状态与事件摘要',
|
|
1026
|
-
'- `/help` — 查看此帮助菜单',
|
|
1034
|
+
'### 💬 会话控制',
|
|
1035
|
+
'| 指令 | 说明 | 示例 |',
|
|
1036
|
+
'| :--- | :--- | :--- |',
|
|
1037
|
+
'| `/sessions` | 查看所有会话表格列表 | `/sessions` 或 `/list` |',
|
|
1038
|
+
'| `/use <编号>` | 切换到指定编号会话 | `/use 1` 或 `/resume 1` |',
|
|
1039
|
+
'| `/new <提示词>` | 在当前工作区新建会话 | `/new 帮我写个脚本` |',
|
|
1040
|
+
'| `/new <词> @N` | 在指定工作区新建会话 | `/new 帮我写个脚本 @1` |',
|
|
1041
|
+
'| `/stop` | 中断停止当前正在执行的任务 | `/stop` |',
|
|
1042
|
+
'| `/end` | 结束当前会话(回到空闲) | `/end` |',
|
|
1027
1043
|
'',
|
|
1028
|
-
'
|
|
1044
|
+
'### 📁 环境与状态',
|
|
1045
|
+
'| 指令 | 说明 |',
|
|
1046
|
+
'| :--- | :--- |',
|
|
1047
|
+
'| `/workspaces` | 查看可用工作区表格列表 |',
|
|
1048
|
+
'| `/status` | 查看 Agent 运行状态看板 |',
|
|
1049
|
+
'| `/help` | 查看此帮助菜单 |',
|
|
1029
1050
|
'',
|
|
1030
|
-
'
|
|
1031
|
-
'
|
|
1051
|
+
'### 🔐 权限确认',
|
|
1052
|
+
'| 指令 | 快捷数字 | 说明 |',
|
|
1053
|
+
'| :--- | :--- | :--- |',
|
|
1054
|
+
'| `/yes` | `1` | 批准当前工具执行请求 |',
|
|
1055
|
+
'| `/no` | `2` | 拒绝当前工具执行请求 |',
|
|
1032
1056
|
].join('\n')
|
|
1033
1057
|
}
|
|
1034
1058
|
|
package/lib/qq/node.js
CHANGED
|
@@ -18,8 +18,7 @@ const STREAM_CHUNK_SIZE = 400
|
|
|
18
18
|
/**
|
|
19
19
|
* 将文本转成 QQ 安全 Markdown:
|
|
20
20
|
* - 标题降级:QQ 仅标准支持 # 与 ##,超出(###、####)降级为 ## 或加粗
|
|
21
|
-
* -
|
|
22
|
-
* - 表格(| a | b | / |---|)降级为纯文本(QQ Markdown 不支持表格,会截断/报错)
|
|
21
|
+
* - 列表/表格前空行:QQ 规定列表前若为普通文本必须有空行隔开,自动补齐空行
|
|
23
22
|
* - markdown 图片语法  转为链接 [alt](url)(避免图片转存失败导致整条失败)
|
|
24
23
|
* - 代码块内容原样保留
|
|
25
24
|
*/
|
|
@@ -28,10 +27,7 @@ function sanitizeQQMarkdown(text) {
|
|
|
28
27
|
const lines = String(text).split('\n')
|
|
29
28
|
const out = []
|
|
30
29
|
let inFence = false
|
|
31
|
-
|
|
32
|
-
const isSep = (l) => /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(l) && l.includes('-')
|
|
33
|
-
const isTableRow = (l) => /^\s*\|.*\|\s*$/.test(l)
|
|
34
|
-
const isListLine = (l) => /^\s*(-|\*|\d+\.)\s+/.test(l)
|
|
30
|
+
const isListOrTable = (l) => /^\s*(-|\*|\d+\.|\/|\|)\s+/.test(l) || /^\s*\|.*\|\s*$/.test(l)
|
|
35
31
|
const isHeading = (l) => /^\s*#{1,6}\s+/.test(l)
|
|
36
32
|
|
|
37
33
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -43,18 +39,6 @@ function sanitizeQQMarkdown(text) {
|
|
|
43
39
|
}
|
|
44
40
|
if (inFence) { out.push(line); continue }
|
|
45
41
|
|
|
46
|
-
// 表格处理
|
|
47
|
-
const nextIsSep = i + 1 < lines.length && isSep(lines[i + 1])
|
|
48
|
-
if (isTableRow(line) && (inTable || nextIsSep)) {
|
|
49
|
-
inTable = true
|
|
50
|
-
if (!isSep(line)) {
|
|
51
|
-
// 去掉首尾 |,内部 | 转空格
|
|
52
|
-
out.push(line.replace(/^\s*\|\s*/, '').replace(/\s*\|\s*$/, '').replace(/\s*\|\s*/g, ' '))
|
|
53
|
-
}
|
|
54
|
-
continue
|
|
55
|
-
}
|
|
56
|
-
inTable = false
|
|
57
|
-
|
|
58
42
|
// 标题级别规范化:QQ 标准支持 # 和 ##,超出(###+)统一转为 ##
|
|
59
43
|
if (isHeading(line)) {
|
|
60
44
|
line = line.replace(/^\s*#{3,}\s+/, '## ')
|
|
@@ -63,10 +47,10 @@ function sanitizeQQMarkdown(text) {
|
|
|
63
47
|
// 图片语法转为链接
|
|
64
48
|
line = line.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '[$1]($2)')
|
|
65
49
|
|
|
66
|
-
//
|
|
67
|
-
if (
|
|
50
|
+
// 列表/表格前空行保障:若前一行是非空普通文本且不是标题/列表/引用/表格,则插入空行保证 QQ 正确解析
|
|
51
|
+
if (isListOrTable(line) && out.length > 0) {
|
|
68
52
|
const prev = out[out.length - 1]
|
|
69
|
-
if (prev && prev.trim() && !
|
|
53
|
+
if (prev && prev.trim() && !isListOrTable(prev) && !isHeading(prev) && !prev.startsWith('>')) {
|
|
70
54
|
out.push('')
|
|
71
55
|
}
|
|
72
56
|
}
|
package/package.json
CHANGED