@cubeai/dsh-feishu-channels 0.0.0-stage → 0.2.0

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.
Files changed (65) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +15 -0
  3. package/README.md +56 -2
  4. package/client.js +4189 -0
  5. package/cordis.patch.yml +4 -0
  6. package/docs/coexistence.md +112 -0
  7. package/docs/dsh-feishu-channels-plan.md +1406 -0
  8. package/docs/ui-revamp-plan.md +263 -0
  9. package/docs/verification.md +120 -0
  10. package/icon.svg +17 -0
  11. package/locale/en.json +6 -0
  12. package/locale/zh.json +6 -0
  13. package/package.json +76 -4
  14. package/src/channels/connection-state.js +12 -0
  15. package/src/channels/diagnostics.js +77 -0
  16. package/src/channels/factory.js +20 -0
  17. package/src/channels/feishu-app.js +267 -0
  18. package/src/channels/feishu.js +399 -0
  19. package/src/channels/media.js +9 -0
  20. package/src/channels/meta.js +24 -0
  21. package/src/channels/qr/feishu.js +84 -0
  22. package/src/channels/qr/hub.js +229 -0
  23. package/src/channels/qr/render.js +8 -0
  24. package/src/channels/qr/shared.js +50 -0
  25. package/src/engine/abort.js +35 -0
  26. package/src/engine/access.js +23 -0
  27. package/src/engine/agent-options.js +19 -0
  28. package/src/engine/approval.js +70 -0
  29. package/src/engine/assistant-settings.js +38 -0
  30. package/src/engine/assistant-text.js +11 -0
  31. package/src/engine/atomic-file.js +45 -0
  32. package/src/engine/chat-commands.js +752 -0
  33. package/src/engine/choice-delivery.js +20 -0
  34. package/src/engine/choices.js +103 -0
  35. package/src/engine/command-locale.js +70 -0
  36. package/src/engine/command-messages.js +434 -0
  37. package/src/engine/command-permissions.js +38 -0
  38. package/src/engine/command-replies.js +59 -0
  39. package/src/engine/credentials.js +54 -0
  40. package/src/engine/deferred-delivery.js +260 -0
  41. package/src/engine/delivery-history.js +39 -0
  42. package/src/engine/file-delivery.js +215 -0
  43. package/src/engine/file-input.js +52 -0
  44. package/src/engine/file-log.js +25 -0
  45. package/src/engine/gateway.js +1337 -0
  46. package/src/engine/image-input.js +31 -0
  47. package/src/engine/json-state.js +25 -0
  48. package/src/engine/keyed-queue.js +20 -0
  49. package/src/engine/merge.js +56 -0
  50. package/src/engine/message-progress.js +274 -0
  51. package/src/engine/question.js +201 -0
  52. package/src/engine/quiet-logger.js +25 -0
  53. package/src/engine/reply-stream.js +86 -0
  54. package/src/engine/router.js +713 -0
  55. package/src/engine/secure-file.js +8 -0
  56. package/src/engine/seen-store.js +43 -0
  57. package/src/engine/session-export.js +64 -0
  58. package/src/engine/session-history.js +30 -0
  59. package/src/engine/session-id.js +62 -0
  60. package/src/engine/session-store.js +109 -0
  61. package/src/engine/session-title.js +29 -0
  62. package/src/engine/split.js +291 -0
  63. package/src/engine/workspace-path.js +9 -0
  64. package/src/index.js +62 -0
  65. package/src/manager.js +1325 -0
@@ -0,0 +1,434 @@
1
+ /** IM 命令文案字典。中文原文作为稳定键,插值与用户内容分开处理。 */
2
+ export const commandEnglish = {
3
+ '序号无效或已过期,本次操作未执行。请发送 /unbind 获取新列表。': 'The number is invalid or expired; no action was taken. Send /unbind for a new list.',
4
+ "按上方完整说明选择操作": "Choose an option described above",
5
+ "✅已结束": "✅Finished",
6
+ "暂未找到可补发的结果,本次未发送,自动补发状态未改变。可稍后重试,或在网页查看原会话。": "No result is available to resend right now. Nothing was sent, and automatic recovery remains unchanged. Try again later or open the original session on the web.",
7
+ "未提交任务,不需要补发": "Task was not submitted; no resend needed",
8
+ "补发此前任务的结果(不会重新执行任务):": "Recovered result from an earlier task (the task was not run again):",
9
+ "此前任务已完成。文件请在原会话查看。": "The earlier task completed. Open the original session to view files.",
10
+ "此前任务失败,已生成的内容保留。": "The earlier task failed. Any output already generated is kept.",
11
+ "此前任务已停止,已生成的内容保留。": "The earlier task stopped. Any output already generated is kept.",
12
+ "用法:/delivery;补发指定结果:/delivery retry 记录ID。记录仅属于当前聊天和发起人。": "Usage: /delivery; resend a result: /delivery retry record-ID. Records are scoped to this chat and the original sender.",
13
+ "无法补发:请先恢复原会话绑定及访问权限。不会发送到其他会话。": "Cannot resend: restore the original session binding and access first. Results will not be sent to another session.",
14
+ "没有近期交付记录。直接发送消息开始任务;查看最近记录:/history。": "No recent delivery records. Send a message to start a task, or use /history to view recent messages.",
15
+ "等待原任务结果": "Waiting for the original task result",
16
+ "等待飞书连接可发送": "Waiting for the Feishu connection",
17
+ "正在补发": "Resending",
18
+ "已发送": "Sent",
19
+ "送达未知,自动补发已暂停": "Delivery unconfirmed; automatic resend paused",
20
+ "绑定或权限已变化,补发已暂停": "Session binding or access changed; resend paused",
21
+ "已过期,请在网页查看原会话": "Expired; open the original session on the web",
22
+ "最近交付记录(最多 10 条,保留 7 天):": "Recent deliveries (up to 10 shown, retained for 7 days):",
23
+ "补发:/delivery retry 记录ID。送达未知的结果可能重复;只补发文字,不重新执行任务,文件请在网页查看。": "Resend: /delivery retry record-ID. Unconfirmed deliveries may produce duplicates. Only text is resent; tasks are never run again. View files on the web.",
24
+ "请先在设置 → 飞书频道 页面选择模型。": "Select a model in Settings → Feishu Channel first.",
25
+ "当前会话暂时无法恢复,已保留原绑定,请稍后重试。": "This session cannot be restored right now. The previous binding is kept; try again later.",
26
+ "无法确定当前会话工作区,请使用 /workspace 重新选择。": "The session workspace could not be determined. Choose one with /workspace.",
27
+ "当前 Host 没有 agents 服务,无法创建 IM 会话": "This Host has no agents service, so an IM session cannot be created.",
28
+ "挂载会话失败 {0}:{1}": "Could not attach session {0}: {1}",
29
+ "目标工作区不可用": "The target workspace is unavailable.",
30
+ "(无文本回复)": "(no text reply)",
31
+ "无法识别宿主持久化会话条目": "The host session entry could not be parsed.",
32
+ "该会话已关联其他聊天,不能重复绑定。": "This session is already linked to another chat and cannot be bound twice.",
33
+ "Host 未提供官方权限预设服务": "This Host does not provide the official permission preset service.",
34
+ "点击下方按钮选择操作;15 分钟内有效,普通文字继续聊天。": "Choose an action using the buttons below. Valid for 15 minutes; ordinary text continues the chat.",
35
+ "发送对应命令即可操作,普通文字继续聊天。": "Send the corresponding command to take an action, or send ordinary text to continue chatting.",
36
+ "回复序号或发送对应命令;15 分钟内有效,普通文字退出菜单。": "Reply with a number or send a command. Valid for 15 minutes; ordinary text exits the menu.",
37
+ "本次处理已结束,但部分回复或文件未能发送。查看交付记录:/delivery;完整结果和文件请在网页查看,不会自动重复执行任务。": "This turn ended, but some responses or files could not be delivered. Check /delivery for delivery records, or open the web to view complete results and files. The task will not be run again automatically.",
38
+ "本次处理已停止,已返回的内容保留。可直接发消息继续。": "This turn stopped. Any results already sent are kept. Send a message to continue.",
39
+ "本次处理失败,已返回的内容保留。请查看会话状态,或补充要求后重试。": "This turn failed. Any results already sent are kept. Check the session status, or send more details to try again.",
40
+ "本次处理已结束,未返回可投递的结果。可补充要求后继续。": "This turn ended without a deliverable result. Send more details to continue.",
41
+ "也可使用 /reasoning id:等级ID;序号 15 分钟内有效。": "You can also use /reasoning id:effort-ID. List numbers are valid for 15 minutes.",
42
+ "用法:/menu [sessions|workspaces|models|presets|reasoning] [页码]": "Usage: /menu [sessions|workspaces|models|presets|reasoning] [page]",
43
+ "选择已过期,或会话、模型已变化。请发送 /reasoning 重新选择。": "This selection expired, or the session or model changed. Send /reasoning to choose again.",
44
+ "恢复默认推理": "Use default reasoning",
45
+ "模型:{0}\n当前推理:{1}": "Model: {0}\nCurrent reasoning: {1}",
46
+ "新会话已创建:{0},但未能接续。请在网页检查工作区后发送 /session {0};原聊天绑定保留。": "Session {0} was created but could not be connected. Check its workspace on the web, then send /session {0}. Your previous chat binding is kept.",
47
+ "已使用预设 {0} 新建并切换会话:{1}": "Started and switched to session {1} with preset {0}.",
48
+ "无法确定当前工作区,请先用 /workspace 选择工作区。": "The current workspace could not be determined. Choose one with /workspace first.",
49
+ "预设不存在或不可用,请发送 /presets 重新选择。": "This preset is missing or unavailable. Send /presets to choose again.",
50
+ "当前 Agent 预设:{0}": "Current Agent preset: {0}",
51
+ "用法:/preset 序号或ID;纯数字 ID:/preset id:ID;跟随默认:/preset --default": "Use /preset number or ID; for numeric IDs use /preset id:ID. Use /preset --default for the default preset.",
52
+ "选择后在当前工作区新建并切换会话,旧会话保留。账号默认预设不变。": "Selecting a preset starts and switches to a new session in this workspace. Previous sessions are kept. The account default stays unchanged.",
53
+ "跟随默认预设": "Use default preset",
54
+ "选择预设": "Choose preset",
55
+ "Agent 预设": "Agent presets",
56
+ "〔不可用〕": " [unavailable]",
57
+ "〔默认〕": " [default]",
58
+ "/presets — Agent 预设;/preset ID — 使用预设新建会话": "/presets \u2014 Agent presets; /preset ID \u2014 start a session with a preset",
59
+ "卡片发送状态暂时无法确认。若已收到,请直接使用;未收到可发送 /menu 重试。": "Card delivery could not be confirmed. Use it if it arrived; otherwise send /menu to try again.",
60
+ "暂停目标": "Pause goal",
61
+ "恢复目标": "Resume goal",
62
+ "刷新状态": "Refresh status",
63
+ "最近记录": "Recent messages",
64
+ "此卡片已失效,请打开新的 /menu。": "This card is no longer active. Open a new /menu.",
65
+ "已选择:{0}。此卡片已结束,请查看后续操作结果。": "Selected: {0}. This card is closed; see the following reply for the result.",
66
+ "调整推理": "Adjust reasoning",
67
+ "查看队列": "View queue",
68
+ "接下来可以:": "Next steps:",
69
+ "暂无可选项,可返回菜单选择其他操作。": "No options are available. Return to the menu for other actions.",
70
+ "点击按钮或发送对应命令;按钮 15 分钟内有效,普通文字继续聊天。": "Click a button or send its command. Buttons are valid for 15 minutes; ordinary text continues the conversation.",
71
+ "当前 DSH 不支持 Chat 文件输入,请升级到 0.1.5-rc.1 或更新的兼容版本后重新发送。": "This DSH version does not support Chat file input. Upgrade to 0.1.5-rc.1 or a newer supported version and resend.",
72
+ "一次最多发送 4 个文件,累计不超过 20 MiB。": "Send up to 4 files at a time, totaling no more than 20 MiB.",
73
+ "文件输入失败,整条消息未提交。请检查文件大小和格式后重新发送,或在网页 Chat 上传。": "File input failed; the message was not submitted. Check the size and format and resend, or upload in web Chat.",
74
+ "已拒绝。": "Rejected.",
75
+ "已批准。": "Approved.",
76
+ "请精准回复「批准」或「拒绝」(也支持:同意 / 不同意 / yes / allow / no / reject)。": "Reply with allow or reject (yes/no also work).",
77
+ "{0} 分钟内未回复将取消,这次不会执行。": "If there is no reply within {0} minutes, this approval is canceled and the action will not run.",
78
+ "审批已超时,这次未执行。还要的话请重新说明。": "The approval timed out, so this action was not run. Ask again if you still want it.",
79
+ "还有未完成的审批,请回复批准或拒绝,或点按钮。": "An approval is still waiting. Reply allow or reject, or tap a button.",
80
+ "原因:{0}": "Reason: {0}",
81
+ "操作参数:": "Arguments:",
82
+ "工具:{0}": "Tool: {0}",
83
+ "DeepSeek Harness 需要你的审批:": "DeepSeek Harness needs your approval:",
84
+ "请输入你的回答。": "Enter your answer.",
85
+ "请回复选项序号或文字;多选用逗号分隔,也可补充其他内容。": "Reply with option numbers or text. Separate multiple choices with commas; you can also add other details.",
86
+ "请回复一个选项序号或文字,也可直接输入其他答案。": "Reply with one option number or label, or enter your own answer.",
87
+ "请直接回复你的答案。": "Reply with your answer.",
88
+ "群聊中请 @机器人 后发送答案。": "Mention the bot when answering in a group.",
89
+ "DeepSeek Harness 需要你补充信息{0}:": "DeepSeek Harness needs your input{0}:",
90
+ '批准一次': 'Allow once',
91
+ '拒绝': 'Reject',
92
+ '提交所选': 'Submit selection',
93
+ '点击按钮或按提示回复文字。': 'Click a button or reply in text as instructed.',
94
+ "返回菜单": "Back to menu",
95
+ "下一页": "Next page",
96
+ "上一页": "Previous page",
97
+ "帮助": "Help",
98
+ "查看状态": "View status",
99
+ "导出会话": "Export session",
100
+ "停止任务": "Stop task",
101
+ "选择模型": "Select model",
102
+ "选择工作区": "Select workspace",
103
+ "选择会话": "Select session",
104
+ "新会话": "New session",
105
+ "助手操作菜单": "Assistant menu",
106
+ "选项已失效或不属于当前操作,请重新打开 /menu。": "This option has expired or belongs to another action. Open /menu again.",
107
+ "点击选项或回复序号;15 分钟内有效,普通文字退出菜单。": "Choose an option or reply with its number. Valid for 15 minutes; ordinary text exits the menu.",
108
+ '会话 ZIP 超过 32 MiB,未发送。请在网页 Chat 中执行 /export 下载。': 'The session ZIP exceeds 32 MiB and was not sent. Run /export in web Chat to download it.',
109
+ '会话已导出,ZIP 文件已发送。': 'Session exported. The ZIP file has been sent.',
110
+ '会话未能导出或发送。请在网页 Chat 中打开这条会话,执行 /export 下载 ZIP。': 'Could not export or send the session. Open this session in web Chat and run /export to download the ZIP.',
111
+ '⏳排队中': '⏳Queued',
112
+ '🤔思考中': '🤔Thinking',
113
+ '⏳等待确认': '⏳Waiting for confirmation',
114
+ '✅已完成': '✅Done',
115
+ '❌处理失败': '❌Failed',
116
+ '🚫已取消': '🚫Cancelled',
117
+ '文件“{0}”未能发送,请在网页 Chat 中打开该文件;也可让助手重新交付。': 'Could not send "{0}". Open the file in Chat on the web, or ask the assistant to deliver it again.',
118
+ "与 Chat 一致,此操作也会尝试保存后续 Chat 新会话的默认模型选择;已有其他会话不会主动修改。": "As in Chat, this also attempts to save the default model selection for future Chat sessions; other existing sessions are not changed.",
119
+ '**入口**': '**Getting around**',
120
+ '/menu(或 /m)— 打开带按钮的操作菜单,和下面的列表命令等价': '/menu (or /m) — Open the button menu; it is equivalent to the list commands below',
121
+ '/help — 显示本说明': '/help — Show this help',
122
+ '**会话与工作区**': '**Sessions and workspaces**',
123
+ '/new(或 /clear)— 新开会话;旧会话保留在飞书频道列表': '/new (or /clear) — Start a session; previous sessions stay in the Feishu channel list',
124
+ '/sessions — 列出会话,点按钮或回序号接续': '/sessions — List sessions; tap a button or reply with the number to continue one',
125
+ '/session 序号或ID — 直接接续指定会话': '/session number-or-ID — Continue that session directly',
126
+ '/workspaces — 列出工作区,点按钮或回序号在其中新建': '/workspaces — List workspaces; tap a button or reply with the number to start a session there',
127
+ '/workspace 序号或路径 — 直接在该工作区新建会话': '/workspace number-or-path — Start a session in that workspace directly',
128
+ '/history — 当前会话最近的文字记录': '/history — Recent text from the current session',
129
+ '/rename 新名称 — 给当前会话改名': '/rename new-name — Rename the current session',
130
+ '/fork — 分叉当前会话': '/fork — Fork the current session',
131
+ '**模型与推理**': '**Models and reasoning**',
132
+ '/models — 列出可选模型并选择': '/models — List available models and pick one',
133
+ '/model — 只看当前模型': '/model — Show the current model only',
134
+ '/reasoning — 列出推理等级并选择': '/reasoning — List reasoning levels and pick one',
135
+ '/presets — 列出 Agent 预设并选择': '/presets — List agent presets and pick one',
136
+ '/preset 序号或ID — 直接用该预设新建会话': '/preset number-or-ID — Start a session with that preset directly',
137
+ '**任务与交付**': '**Tasks and delivery**',
138
+ '/status(或 /current)— 当前状态': '/status (or /current) — Current status',
139
+ '/stop — 请求停止,保留队列': '/stop — Request a stop, keeping queued messages',
140
+ '/steer 补充要求 — 提交补充指令': '/steer instructions — Submit additional instructions',
141
+ '/queue — 查看队列和操作方法': '/queue — View and manage queued messages',
142
+ '/delivery — 查看交付记录和补发方法': '/delivery — Check delivery records and resend options',
143
+ '/unbind [序号|ID|all] — 解除本聊天对旧会话的登记,不再推送其输出': '/unbind [no|ID|all] — Unbind old sessions from this chat and stop their output pushes',
144
+ '/mute 序号或ID — 静音指定会话的推送;/unmute 恢复': '/mute no-or-ID — Mute pushes from a session; /unmute restores them',
145
+ '用法:/{0} 序号或ID。先发送 /unbind 查看本聊天登记的会话。': 'Usage: /{0} no-or-ID. Send /unbind first to list sessions registered to this chat.',
146
+ '本聊天登记的会话 · 共 {0} 个\n\n': 'Sessions registered to this chat · {0} total\n\n',
147
+ '〔已静音〕': '〔muted〕',
148
+ '解除登记:/unbind 序号或ID;全部解除(当前会话除外):/unbind all': 'Unbind: /unbind no-or-ID; unbind all except the current session: /unbind all',
149
+ '保留登记但暂停推送:/mute 序号或ID;恢复:/unmute 序号或ID': 'Keep the record but pause pushes: /mute no-or-ID; resume: /unmute no-or-ID',
150
+ '本聊天没有登记任何会话。': 'No sessions are registered to this chat.',
151
+ '没有需要解除的登记。': 'Nothing to unbind.',
152
+ '当前会话未受影响。': 'The current session is unaffected.',
153
+ '不能解除当前会话;请先 /new 或 /session 切换。': 'Cannot unbind the current session; switch first with /new or /session.',
154
+ '本聊天没有登记该会话,请发送 /unbind 查看。': 'This chat has no record of that session; send /unbind to list them.',
155
+ '已解除 {0} 个会话的登记,它们不再向本聊天推送输出。': 'Unbound {0} sessions; they will no longer push output to this chat.',
156
+ '已解除登记:{0}\n该会话不再向本聊天推送输出;历史仍保留在网页 Chat 中。': 'Unbound: {0}\nIt will no longer push output to this chat; history stays in web Chat.',
157
+ '查看登记:/unbind': 'List registrations: /unbind',
158
+ '会话登记异常,请发送 /unbind 查看。': 'Session registration error; send /unbind to list them.',
159
+ '已静音:{0}\n该会话的输出不再推送到本聊天;/unmute {1} 恢复。': 'Muted: {0}\nIts output will no longer be pushed to this chat; /unmute {1} to restore.',
160
+ '已恢复推送:{0}': 'Pushes restored: {0}',
161
+ '静音:/mute {0}': 'Mute: /mute {0}',
162
+ '/export — 导出当前会话 ZIP 并发送': '/export — Export the current session as a ZIP and send it',
163
+ '压缩较早上下文': 'Compact earlier context',
164
+ '管理长期目标;暂停 /goal pause;恢复 /goal resume': 'Manage the long-term goal; pause with /goal pause, resume with /goal resume',
165
+ '进入计划模式;退出 /plan off': 'Enter plan mode; leave with /plan off',
166
+ '查看当前权限及可选项': 'Show current permissions and options',
167
+ '记录会话反馈,后面跟反馈内容': 'Record session feedback, followed by the feedback text',
168
+ '提交代码简化审查': 'Submit a code simplification review',
169
+ '命令单独发送为文字;图片说明按普通消息处理。': 'Send commands as standalone text. Image captions are treated as ordinary messages.',
170
+ "当前 Host 未提供 {0},请升级 DSH。": "Your DSH installation does not provide {0}. Update DSH on your computer.",
171
+ "当前 Host 不支持 {0}。": "Your DSH installation does not support {0}.",
172
+ "无法打开会话。": "Unable to open the session.",
173
+ "无法读取会话快照。": "Unable to read the session snapshot.",
174
+ "无法读取工作区。": "Unable to read workspaces.",
175
+ "序号无效或已过期,尚未切换。请发送 /{0} 获取新列表。": "The number is invalid or expired. Nothing was switched. Send /{0} for a fresh list.",
176
+ "工作区:{0}": "Workspace: {0}",
177
+ 默认推理: "Default reasoning",
178
+ "模型:{0}/{1} · {2}": "Model: {0}/{1} · {2}",
179
+ "命令格式无效,请发送 /help。": "Invalid command format. Send /help.",
180
+ "该控制命令不接受附件,请单独发送。": "This control command does not accept attachments. Send it separately.",
181
+ "当前没有会话,请先发送消息或 /new。": "No current session. Send a message or /new first.",
182
+ "/{0} 暂不支持参数。正确用法:/{1}": "/{0} does not accept arguments. Usage: /{1}",
183
+ "查找当前会话:/status": "Find the current session: /status",
184
+ "最近文字:/history": "Recent text: /history",
185
+ "直接发送文字或 /new 创建会话": "Send text or /new to create a session",
186
+ "\n发送消息创建会话后,可查看该会话的 Chat 命令。": "\nSend a message to create a session and discover its Chat commands.",
187
+ "\n扩展命令暂时无法读取,以上内置帮助仍可使用;稍后重试 /help。": "\nExtension commands are temporarily unavailable. Built-in help above still applies; retry /help later.",
188
+ 机器人: "Bot",
189
+ "\n\n扩展命令:\n": "\n\nExtension commands:\n",
190
+ "旧会话已保留,直接发送消息即可开始。": "Previous sessions are preserved. Send a message to begin.",
191
+ "查看旧会话:/sessions": "Previous sessions: /sessions",
192
+ "查看当前配置:/status": "Current configuration: /status",
193
+ "用法:/sessions [页码]": "Usage: /sessions [page]",
194
+ "暂无可接续会话。": "No sessions available to resume.",
195
+ "直接发送消息,或 /new 开始聊天": "Send a message or /new to start chatting",
196
+ "没有第 {0} 页,共 {1} 页。": "Page {0} does not exist. There are {1} pages.",
197
+ "返回列表:/sessions {0}": "Back to list: /sessions {0}",
198
+ 未命名会话: "Untitled session",
199
+ "〔当前〕": " [current]",
200
+ "切换:/session {0}": "Switch: /session {0}",
201
+ "下一页:/sessions {0}": "Next page: /sessions {0}",
202
+ "序号 15 分钟内有效,也可使用完整会话 ID。": "Numbers expire after 15 minutes. Full session IDs also work.",
203
+ "当前没有会话。直接发送消息或 /new 开始。": "No current session. Send a message or /new to begin.",
204
+ 暂时无法读取: "Temporarily unavailable",
205
+ "当前会话:{0}\n工作区:{1}\n会话 ID:{2}": "Current session: {0}\nWorkspace: {1}\nSession ID: {2}",
206
+ "切换会话:/sessions": "Switch session: /sessions",
207
+ "详细状态:/status": "Detailed status: /status",
208
+ "会话不存在或不是可接入的普通会话。": "This session is unavailable. Send /sessions to choose another.",
209
+ "请先在 Chat 恢复该归档会话。": "Restore this archived session in Chat first.",
210
+ "会话工作区归属无效。": "The session has an invalid workspace association.",
211
+ "目标会话正在运行,请在 Chat 停止后再切换。": "The target session is running. Stop it in Chat before switching.",
212
+ "已切换会话:{0}\n工作区:{1}\n接下来的消息会发送到此会话。": "Switched to: {0}\nWorkspace: {1}\nYour next messages will go to this session.",
213
+ "最近记录:/history": "Recent messages: /history",
214
+ "重新开始:/new": "Start over: /new",
215
+ "工作区列表 · 共 {0} 个\n\n": "Workspaces · {0} total\n\n",
216
+ "也可使用已列出的绝对路径;旧会话保留,账号默认目录不变。": "You can also use a listed absolute path. Previous sessions and the account default directory are preserved.",
217
+ "别名:/workspacelist;序号 15 分钟内有效。": "Alias: /workspacelist. Numbers expire after 15 minutes.",
218
+ "还没有可用工作区。请先在网页添加,再发送 /workspaces。": "No workspaces available. Add one on the web, then send /workspaces.",
219
+ "没有找到这个已添加的工作区,尚未切换。发送 /workspaces 查看可选项;新目录请先在网页添加。": "That workspace has not been added. Nothing was switched. Send /workspaces for options; add new directories on the web first.",
220
+ "查看配置:/status": "Configuration: /status",
221
+ "切回旧会话:/sessions": "Switch back: /sessions",
222
+ "可用模型 · 共 {0} 个\n\n": "Available models · {0} total\n\n",
223
+ "查看当前模型:/model;推理选项:/reasoning": "Current model: /model; reasoning options: /reasoning",
224
+ "先发消息或 /new 创建会话,再切换模型。": "Send a message or /new to create a session before switching models.",
225
+ "序号 15 分钟内有效。": "Numbers expire after 15 minutes.",
226
+ "暂无可用模型。请在网页配置模型服务,再发送 /models。": "No models available. Configure a model provider on the web, then send /models.",
227
+ "当前没有排队消息。": "No queued messages.",
228
+ 直接发消息继续: "Send a message to continue",
229
+ "查看状态:/status": "View status: /status",
230
+ "排队消息 · 共 {0} 条\n\n": "Queued messages · {0} total\n\n",
231
+ 等待执行: "Waiting to run",
232
+ 补充指令: "Additional instruction",
233
+ 排队中: "Queued",
234
+ 附件消息: "Attachment message",
235
+ "移除:/queue remove {0}": "Remove: /queue remove {0}",
236
+ "修改:/queue edit {0} 新内容": "Edit: /queue edit {0} new-content",
237
+ "改为补充指令:/queue steer {0}": "Use as additional instruction: /queue steer {0}",
238
+ "无法读取队列。": "Unable to read the queue.",
239
+ "用法:/queue remove|steer|edit <消息ID> [新内容]": "Usage: /queue remove|steer|edit <message-ID> [new-content]",
240
+ 已移除排队消息: "Removed queued message",
241
+ 已修改排队消息: "Updated queued message",
242
+ 已将排队消息调整为补充指令: "Changed queued message to an additional instruction",
243
+ "\n新内容:": "\nNew content: ",
244
+ "查看队列:/queue": "View queue: /queue",
245
+ "账号:{0}({1})": "Account: {0} ({1})",
246
+ "会话:{0}": "Session: {0}",
247
+ 运行中: "Running",
248
+ 空闲: "Idle",
249
+ "状态:{0}": "Status: {0}",
250
+ 默认: "Default",
251
+ "模型:{0}/{1};推理:{2}": "Model: {0}/{1}; reasoning: {2}",
252
+ "模型:暂时无法读取": "Model: temporarily unavailable",
253
+ "补充要求:/steer 补充内容": "Add instructions: /steer additional-instructions",
254
+ "请求停止:/stop": "Request a stop: /stop",
255
+ "模型设置:/model": "Model settings: /model",
256
+ "如已启用目标任务,请用 /goal pause 暂停目标。": "If a goal is active, use /goal pause to pause it.",
257
+ "当前目标仍处于活跃状态,请用 /goal pause 暂停目标。": "The current goal is still active. Use /goal pause to pause it.",
258
+ "当前没有正在执行的任务。": "No task is running.",
259
+ "暂时无法确认当前会话的运行状态,未能请求停止。请用 /status 查看,或到网页检查会话。": "Unable to confirm this session's running status. Stop was not requested. Check /status or open the session on the web.",
260
+ "已请求停止当前运行;排队消息保留,可用 /queue 查看。": "Stop requested. Queued messages are preserved; view them with /queue.",
261
+ "确认运行状态:/status": "Check running status: /status",
262
+ "请填写补充要求。例如:/steer 只修改登录页面,保留现有配色\n查看状态:/status": "Enter additional instructions. Example: /steer Only change the login page and keep the current colors\nView status: /status",
263
+ "补充指令已提交。\n{0}": "Additional instructions submitted.\n{0}",
264
+ "请填写新名称,例如:/rename 九月出行计划": "Enter a new name. Example: /rename September travel plan",
265
+ "当前会话已改名:{0}": "Session renamed: {0}",
266
+ "查看会话列表:/sessions": "View sessions: /sessions",
267
+ "当前会话工作区不可用,无法分叉。": "This session's workspace is unavailable. Unable to fork.",
268
+ "当前 Host 不支持 fork。": "This Host does not support fork.",
269
+ "已分叉并切换会话:{0}\n工作区:{1}\n新消息将继续这个分叉,原会话保留。": "Forked and switched to session: {0}\nWorkspace: {1}\nNew messages will continue this fork. The original session is preserved.",
270
+ "查看继承记录:/history": "View inherited messages: /history",
271
+ "切回原会话:/sessions": "Return to the original session: /sessions",
272
+ "请先在网页检查并修复该分叉的工作区归属,然后": "Check and repair this fork's workspace association on the web first, then ",
273
+ 可稍后: "Later, ",
274
+ "分叉已创建:{0},但未能切换,当前绑定仍保留原会话。\n{1}发送 /session {2} 接续。": "Fork created: {0}, but switching failed. The original session is still selected.\n{1}send /session {2} to resume it.",
275
+ "当前模型:{0}\n模型 ID:{1}/{2}\n推理等级:{3}": "Current model: {0}\nModel ID: {1}/{2}\nReasoning level: {3}",
276
+ "选择其他模型:/models": "Choose another model: /models",
277
+ "查看推理选项:/reasoning": "Reasoning options: /reasoning",
278
+ "切换用法:/model {0}/{1} [推理等级ID]": "Switch usage: /model {0}/{1} [reasoning-level-ID]",
279
+ "参数过多。用法:/model 序号或provider/model [推理等级ID];可用模型:/models": "Too many arguments. Usage: /model number-or-provider/model [reasoning-level-ID]; available models: /models",
280
+ "用法:/model <序号或provider/model> [推理等级]": "Usage: /model <number-or-provider/model> [reasoning-level]",
281
+ "调整推理:/reasoning": "Adjust reasoning: /reasoning",
282
+ "查看模型:/models": "View models: /models",
283
+ "可选等级:\n": "Available levels:\n",
284
+ "当前模型没有可选推理等级。": "This model has no selectable reasoning levels.",
285
+ "模型:{0}\n当前推理:{1}\n{2}": "Model: {0}\nCurrent reasoning: {1}\n{2}",
286
+ "切换:/reasoning {0}": "Switch: /reasoning {0}",
287
+ "恢复默认:/reasoning --default": "Restore default: /reasoning --default",
288
+ "当前模型:/model": "Current model: /model",
289
+ "推理等级无效,请先 /reasoning 查看。": "Invalid reasoning level. Send /reasoning for options.",
290
+ "推理等级已设为:{0}\n{1}": "Reasoning level set to: {0}\n{1}",
291
+ "查看当前模型:/model": "View current model: /model",
292
+ 助手: "Assistant",
293
+ 用户: "User",
294
+ "最近文字记录(不是完整历史)\n\n": "Recent text messages (not the complete history)\n\n",
295
+ "最近记录中没有可展示的文字,图片或文件请在网页查看。": "No displayable text in recent messages. View images or files on the web.",
296
+ "继续此会话:直接发消息": "Continue this session: send a message",
297
+ "换一个会话:/sessions": "Choose another session: /sessions",
298
+ "完整记录:在网页打开当前会话": "Full history: open this session on the web",
299
+ "Chat 命令只支持文字和图片附件。": "Chat commands support only text and image attachments.",
300
+ "未知命令 /{0}。发送 /help 查看当前 Chat 支持的命令。": "Unknown command /{0}. Send /help for available Chat commands.",
301
+ "命令已返回,但未提供结果说明。请在网页核对当前状态。": "The command returned without a result description. Check the current state on the web.",
302
+ "命令执行失败,未提供具体原因。请在网页检查后重试。": "The command failed without details. Check on the web before retrying.",
303
+ "查看运行状态:/status": "View running status: /status",
304
+ "查看可选权限:/permission": "Available permissions: /permission",
305
+ "切换方式:/permission 权限ID": "Switch usage: /permission permission-ID",
306
+ "进入计划模式:/plan": "Enter plan mode: /plan",
307
+ "退出计划模式:/plan off": "Exit plan mode: /plan off",
308
+ "查看任务状态:/status": "View task status: /status",
309
+ "补充反馈:/feedback 补充内容": "Add feedback: /feedback additional-feedback",
310
+ "查看最近对话:/history": "Recent conversation: /history",
311
+ "补充要求:/steer 保留现有接口": "Add instructions: /steer Keep existing interfaces",
312
+ "暂停目标:/goal pause": "Pause goal: /goal pause",
313
+ "恢复推进:/goal resume": "Resume progress: /goal resume",
314
+ "修改目标:/goal edit 新目标": "Edit goal: /goal edit new-objective",
315
+ "查看进度:/goal": "View progress: /goal",
316
+ "恢复目标:/goal resume": "Resume goal: /goal resume",
317
+ "清除目标:/goal clear": "Clear goal: /goal clear",
318
+ "查看目标:/goal": "View goal: /goal",
319
+ "设置目标:/goal 目标内容": "Set a goal: /goal objective",
320
+ "Agent 预设:{0}": "Agent preset: {0}",
321
+ "权限:{0}": "Permissions: {0}",
322
+ 活跃: "Active",
323
+ 已暂停: "Paused",
324
+ 受阻: "Needs attention",
325
+ 已完成: "Complete",
326
+ "目标:{0}": "Goal: {0}",
327
+ 无目标: "No goal",
328
+ "排队消息:{0} 条": "Queued messages: {0}",
329
+ "当前聊天未开启命令权限,可以继续正常对话。": "Commands are disabled in this chat. You can still chat normally.",
330
+ "命令已取消。查看当前状态:/status": "Command canceled. Check current status: /status",
331
+ "命令执行失败:{0}\n\n查看用法:/help;确认当前会话:/status": "Command failed: {0}\n\nUsage: /help; check current session: /status",
332
+ "请查看本机日志。": "Open DSH on your computer to check the error details.",
333
+ "飞书助手已连接 DeepSeek Harness。直接发送文字即可开始任务。": "Feishu assistant connected to DeepSeek Harness. Send a message to start a task.",
334
+ "请先完成当前问题或审批,再执行 /{0}。": "Answer the pending question or approval, then run /{0} again.",
335
+ "当前任务正在运行,请先 /stop,等待停止后再执行 /{0}。": "A task is running. Send /stop, wait for it to stop, then run /{0} again.",
336
+ "会话列表 · 第 {0}/{1} 页 · 共 {2} 个\n\n": "Sessions · Page {0}/{1} · {2} total\n\n",
337
+ "〔已归档,需先在网页恢复〕": " [archived — restore on the web first]",
338
+ "归档状态暂时无法读取,切换时会重新校验。": "Archive status is unavailable. It will be checked again when you switch.",
339
+ "{0}〔{1}〕\n{2}": "{0} [{1}]\n{2}",
340
+ "{0}:{1}{2}": "{0}: {1}{2}",
341
+ "{0}:{1}": "{0}: {1}",
342
+ "已切换模型:{0}/{1};推理:{2}\n{3}": "Switched model to: {0}/{1}; reasoning: {2}\n{3}",
343
+ "当前会话还没有可分叉的完整回合。请完成一轮对话后再发送 /fork;也可用 /new 开始。": "There is no completed turn to fork yet. Finish one exchange, then send /fork; or start fresh with /new.",
344
+ "已请求停止。当前没有正在执行的任务;排队消息保留,可用 /queue 查看。": "Stop requested. No task is currently running. Queued messages are preserved; view them with /queue.",
345
+ "会话 ID:{0}": "Session ID: {0}",
346
+ "未授权:请管理员在设置 → 飞书频道 中批准你的访问。": "Access is pending approval. Ask the administrator to approve you in Settings → Feishu Channel.",
347
+ "上一条消息正在合并,尚未执行本次命令。等待提交后再发 {0}。": "Your previous messages are being combined. This command has not run. Send {0} again after they are submitted.",
348
+ "该会话已关联其他聊天,不能重复接续。请用 /sessions 选择其他会话,或 /new 新建。": "This session is connected to another chat and cannot be connected again. Choose another with /sessions, or start one with /new.",
349
+ "/{0} 未能完成\n{1}": "/{0} couldn’t complete\n{1}",
350
+ "〔已关联其他聊天〕": " [connected to another chat]",
351
+ "〔运行中〕": " [running]",
352
+ "例如:帮我整理今天的待办。": "Try: Help me organize today's tasks.",
353
+ 已开启新会话: "New session started",
354
+ "新建并切换:/workspace {0}": "Start a session in another workspace: /workspace {0}",
355
+ "当前只有这个工作区。直接发消息继续,或 /new 开启新会话。": "This is your only workspace. Send a message to continue, or /new to start fresh.",
356
+ "已在「{0}」开启新会话。\n工作区:{1}\n旧会话已保留,可以直接发送任务。": "Started a new session in {0}.\nWorkspace: {1}\nPrevious sessions are preserved. Send a task to begin.",
357
+ "切换:/model {0}": "Switch: /model {0}",
358
+ "当前没有其他可切换模型,继续发送消息即可。": "No other models are available. Send a message to continue.",
359
+ "当前名称已是:{0}": "The session is already named: {0}",
360
+ "只有发起当前任务的用户可以回答这个问题。": "Only the person who started this task can answer it.",
361
+ "只有发起当前任务的用户可以批准或拒绝。": "Only the person who started this task can approve or deny it.",
362
+ "请用文字回答当前问题。": "Reply in text to answer the current question.",
363
+ "问题详情仍在发送,请稍后再回答。": "The question is still being sent. Answer again in a moment.",
364
+ "请在私聊中批准或拒绝工具调用。": "Approve or deny the tool call in a private chat.",
365
+ "工具调用审批仅限已批准用户,请在网页端处理。": "Only approved users can approve tool calls. Handle this on the web.",
366
+ "消息处理失败,请查看本机日志。": "The message could not be processed. Check the local logs.",
367
+ "审批详情仍在发送,请稍后再回复。": "The approval details are still being sent. Reply again in a moment.",
368
+ "当前工具审批不能在群聊中处理,请在网页端批准或拒绝。": "This tool approval cannot be handled in a group. Approve or deny it on the web.",
369
+ "当前用户可以私聊,但工具调用审批仅限已批准用户;请在网页端处理。": "This user can chat privately, but only approved users can approve tool calls. Handle this on the web.",
370
+ "该操作需要审批,但无法在 IM 中完整展示;请在网页端处理。": "This action needs approval, but it cannot be shown completely in IM. Handle it on the web.",
371
+ "参数暂时无法读取。仍请在此批准或拒绝,不要到网页处理。": "The arguments are temporarily unavailable. Still approve or deny here; do not use the web.",
372
+ "该审批已取消,无需回复。": "This approval was canceled. No reply is needed.",
373
+ "该问题已取消,无需回复。": "This question was canceled. No reply is needed.",
374
+ "助手没有生成回复,请查看本机日志。": "The assistant did not produce a reply. Check the local logs.",
375
+ "相册图片过多,请分批发送。": "This album has too many images. Send them in smaller batches.",
376
+ "图片接收失败,请检查图片格式和大小后重新发送完整消息。": "The image could not be received. Check its format and size, then send the full message again.",
377
+ "当前会话模型不支持图片输入,请在 Chat 中切换支持视觉的模型后重新发送。": "The current session model does not support images. Switch to a vision model in Chat, then send again.",
378
+ "图片输入失败,消息未提交。请检查图片格式、大小或当前会话模型后重新发送;详情见本机日志。": "The image could not be submitted. Check the format, size, or session model, then send again. Details are in the local logs.",
379
+ "图片读取失败,请重新发送。": "The image could not be read. Send it again.",
380
+ "图片下载失败或内容为空,请重新发送。": "The image download failed or was empty. Send it again.",
381
+ "当前 Host 不支持 Chat 图片输入,请升级 DeepSeek Harness。": "This Host does not support Chat image input. Upgrade DeepSeek Harness.",
382
+ "图片或消息读取失败,请重发图片或发送文字。": "The image or message could not be read. Send the image or text again.",
383
+ "图片下载失败:{0}。整条消息未提交,请重新发送。": "The image could not be downloaded: {0}. The message was not submitted. Send it again.",
384
+ "图片或媒体下载失败,请重新发送。": "The image or media could not be downloaded. Send it again.",
385
+ "图片读取失败:{0}。请重新发送;若仍失败请管理员检查网络和机器人配置。": "The image could not be read: {0}. Send it again. If it still fails, ask an administrator to check the network and bot configuration.",
386
+ "图片下载失败:{0}。请重新发送。": "The image could not be downloaded: {0}. Send it again.",
387
+ "暂不支持该文件类型,请发送文字或图片。": "This file type is not supported. Send text or an image.",
388
+ "图片读取失败:{0}。请重试;若仍失败请管理员检查网络和机器人文件下载权限。": "The image could not be read: {0}. Try again. If it still fails, ask an administrator to check the network and the bot file-download permission.",
389
+ "下载服务器返回 HTTP {0}": "The download server returned HTTP {0}",
390
+ "下载服务器暂时不可用(HTTP {0})": "The download server is temporarily unavailable (HTTP {0})",
391
+ "下载地址被安全校验拦截": "The download address was blocked by the safety check",
392
+ "图片数量或大小超过接收限制": "The image count or size exceeds the receive limit",
393
+ "下载超时或已取消": "The download timed out or was canceled",
394
+ "图片解密失败": "The image could not be decrypted",
395
+ "图片缺少下载地址或解密信息": "The image is missing a download address or decryption details",
396
+ "不支持的图片格式": "The image format is not supported",
397
+ "服务器返回了空图片": "The server returned an empty image",
398
+ "DNS 解析失败": "DNS lookup failed",
399
+ "TLS 连接校验失败": "TLS verification failed",
400
+ "网络连接或图片处理失败": "The network connection or image processing failed",
401
+ "网络连接失败": "The network connection failed",
402
+ "下载超时": "The download timed out",
403
+ "图片超过 50 MiB 接收限制": "The image exceeds the 50 MiB receive limit",
404
+ "图片格式无效或不支持,请发送 PNG、JPEG、GIF 或 WebP": "The image format is invalid or unsupported. Send a PNG, JPEG, GIF, or WebP",
405
+ "图片解密失败或解密信息无效": "The image could not be decrypted, or the decryption details are invalid",
406
+ "图片下载安全校验失败": "The image download failed the safety check",
407
+ "图片保存失败": "The image could not be saved",
408
+ "正在思考中…": "Thinking…",
409
+ "已断开": "Disconnected",
410
+ "已断开(code {0})": "Disconnected (code {0})",
411
+ "重连失败": "Reconnect failed",
412
+ "连接中": "Connecting",
413
+ "等待网关握手": "Waiting for gateway handshake",
414
+ "鉴权中": "Authenticating",
415
+ "已连接": "Connected",
416
+ "重连中": "Reconnecting",
417
+ "连接错误": "Connection error",
418
+ "连接失败": "Connection failed",
419
+ "连接失败,请查看本机日志": "Connection failed. Check the local logs.",
420
+ "Stream 已连接": "Stream connected",
421
+ "已停止": "Stopped",
422
+ "长连接已建立": "Long connection established",
423
+ "轮询中": "Polling",
424
+ "轮询异常(详情见本机日志)": "Polling failed. Details are in the local logs.",
425
+ "未登录": "Not signed in",
426
+ "未连接": "Not connected",
427
+ "等待扫码": "Waiting for scan",
428
+ "已登录": "Signed in",
429
+ "已登录(自动恢复)": "Signed in (restored)",
430
+ "登录中": "Signing in",
431
+ "没有读到消息正文。请确认已打开 Message Content Intent,或发送文字后再 @机器人。": "No message text was received. Enable the Message Content Intent, or send text and mention the bot again.",
432
+ "图片或文件接收失败,请检查格式和大小后重新发送完整消息。": "The image or file could not be received. Check its format and size, then send the full message again.",
433
+ "请直接发送 /menu 使用命令。": "Send /menu as a normal message to use commands.",
434
+ };
@@ -0,0 +1,38 @@
1
+ export function normalizeCommandPermissions(input) {
2
+ if (input === undefined)
3
+ return { dm: { enabled: true, users: [] }, group: { enabled: true, users: [] } };
4
+ const fail = () => { throw new Error('命令权限配置无效'); };
5
+ if (!input || typeof input !== 'object' || Array.isArray(input))
6
+ return fail();
7
+ const source = input;
8
+ if (Object.keys(source).some(key => key !== 'dm' && key !== 'group'))
9
+ return fail();
10
+ const scope = (value) => {
11
+ if (!value || typeof value !== 'object' || Array.isArray(value))
12
+ return fail();
13
+ const item = value;
14
+ if (typeof item.enabled !== 'boolean' || !Array.isArray(item.users) || item.users.length > 200
15
+ || Object.keys(item).some(key => key !== 'enabled' && key !== 'users'))
16
+ return fail();
17
+ const seen = new Set();
18
+ const users = item.users.map((entry) => {
19
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
20
+ return fail();
21
+ const user = entry;
22
+ if (typeof user.userId !== 'string' || typeof user.enabled !== 'boolean'
23
+ || Object.keys(user).some(key => key !== 'userId' && key !== 'enabled'))
24
+ return fail();
25
+ const userId = user.userId.trim();
26
+ if (!userId || userId.length > 256 || /[\p{Cc}\p{Cf}]/u.test(userId) || seen.has(userId))
27
+ return fail();
28
+ seen.add(userId);
29
+ return { userId, enabled: user.enabled };
30
+ });
31
+ return { enabled: item.enabled, users };
32
+ };
33
+ return { dm: scope(source.dm), group: scope(source.group) };
34
+ }
35
+ export function canExecuteCommand(policy, kind, userId) {
36
+ // 旧测试包的用户例外仅保留格式兼容,执行权限统一由聊天类型开关决定。
37
+ return policy[kind].enabled;
38
+ }
@@ -0,0 +1,59 @@
1
+ import { replyText } from './command-locale.js';
2
+ /** 命令回复的文字排版与扩展命令指引;不推断宿主执行结果。 */
3
+ export function oneLine(value, limit = 160) {
4
+ const text = value.replace(/\s+/g, ' ').trim();
5
+ return text.length > limit ? text.slice(0, limit) + '…' : text;
6
+ }
7
+ export function related(...items) { return items.some(Boolean) ? '\n\n' + items.filter(Boolean).join('\n') : ''; }
8
+ /** 导航只提供查询和选择入口;不把停止、清空等操作作为通用快捷按钮。 */
9
+ export function commandNavigation(command, hasSession) {
10
+ const sessions = { label: replyText('选择会话'), value: '/menu sessions' };
11
+ const workspaces = { label: replyText('选择工作区'), value: '/menu workspaces' };
12
+ const models = { label: replyText('选择模型'), value: '/menu models' };
13
+ const presets = { label: replyText('选择预设'), value: '/menu presets' };
14
+ const reasoning = { label: replyText('调整推理'), value: '/reasoning' };
15
+ const status = { label: replyText('查看状态'), value: '/status' };
16
+ const routes = {
17
+ sessions: [sessions, workspaces], sessionlist: [sessions, workspaces],
18
+ session: [sessions, status], new: [sessions, models], clear: [sessions, models],
19
+ workspace: [workspaces, sessions], workspaces: [workspaces, sessions], workspacelist: [workspaces, sessions],
20
+ model: [models, reasoning, presets], models: [models, reasoning, presets],
21
+ preset: [presets, models, reasoning], presets: [presets, models, reasoning], presetlist: [presets, models, reasoning],
22
+ reasoning: [models, presets], reasonings: [models, presets], reasoninglist: [models, presets],
23
+ status: [{ label: replyText('查看队列'), value: '/queue' }, sessions],
24
+ queue: [status], stop: [status, { label: replyText('查看队列'), value: '/queue' }], steer: [status],
25
+ history: [sessions, status], rename: [sessions, status], fork: [sessions, status], export: [sessions, status],
26
+ compact: [status], permission: [status], plan: [status], goal: [status], feedback: [status], simplify: [status],
27
+ };
28
+ if (!routes[command])
29
+ return [];
30
+ return [...routes[command].filter(choice => hasSession || choice.value === '/menu presets' || choice.value === '/menu sessions' || choice.value === '/menu workspaces'),
31
+ { label: replyText('返回菜单'), value: '/menu' }];
32
+ }
33
+ export const extensionHelp = () => ({
34
+ compact: replyText('压缩较早上下文'),
35
+ goal: replyText('管理长期目标;暂停 /goal pause;恢复 /goal resume'),
36
+ plan: replyText('进入计划模式;退出 /plan off'),
37
+ permission: replyText('查看当前权限及可选项'),
38
+ feedback: replyText('记录会话反馈,后面跟反馈内容'),
39
+ simplify: replyText('提交代码简化审查'),
40
+ });
41
+ export function extensionReply(command, result, goal) {
42
+ // 宿主自然语言不是稳定协议,保留正文及反馈共享披露,不按英文子串猜测成功。
43
+ const body = result.text?.trim() ? result.text : result.kind === 'success' ? replyText('命令已返回,但未提供结果说明。请在网页核对当前状态。') : replyText('命令执行失败,未提供具体原因。请在网页检查后重试。');
44
+ const tips = {
45
+ compact: [replyText('最近记录:/history'), replyText('查看运行状态:/status')],
46
+ permission: [replyText('查看可选权限:/permission'), replyText('切换方式:/permission 权限ID')],
47
+ plan: [replyText('进入计划模式:/plan'), replyText('退出计划模式:/plan off'), replyText('查看任务状态:/status')],
48
+ feedback: [replyText('补充反馈:/feedback 补充内容'), replyText('查看最近对话:/history')],
49
+ simplify: [replyText('查看状态:/status'), replyText('补充要求:/steer 保留现有接口')],
50
+ goal: goal?.phase === 'active'
51
+ ? [goal.activation === 'armed' ? replyText('暂停目标:/goal pause') : replyText('恢复推进:/goal resume'), replyText('修改目标:/goal edit 新目标'), replyText('查看进度:/goal')]
52
+ : goal?.phase === 'paused' || goal?.phase === 'blocked'
53
+ ? [replyText('恢复目标:/goal resume'), replyText('修改目标:/goal edit 新目标'), replyText('清除目标:/goal clear')]
54
+ : [replyText('查看目标:/goal'), replyText('设置目标:/goal 目标内容')],
55
+ };
56
+ if (!Object.hasOwn(tips, command))
57
+ return body;
58
+ return (result.kind === 'success' ? body : replyText('/{0} 未能完成\n{1}', command, body)) + related(...(tips[command] || []));
59
+ }
@@ -0,0 +1,54 @@
1
+ import { mkdirSync, readFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { writePrivateFileSync } from './secure-file.js';
4
+ export function createFileVault(file) {
5
+ const read = () => {
6
+ try {
7
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
8
+ return parsed && typeof parsed === 'object' ? parsed : {};
9
+ }
10
+ catch (error) {
11
+ if (error.code === 'ENOENT')
12
+ return {};
13
+ // 损坏或不可读时拒绝继续,不能把空对象写回去覆盖唯一凭据副本。
14
+ throw new Error(`本地凭据文件无法读取: ${file}`, { cause: error });
15
+ }
16
+ };
17
+ const write = (data) => {
18
+ mkdirSync(dirname(file), { recursive: true });
19
+ writePrivateFileSync(file, `${JSON.stringify(data, null, 2)}\n`);
20
+ };
21
+ return {
22
+ async set(ref, value) {
23
+ const data = read();
24
+ data[ref] = value;
25
+ write(data);
26
+ },
27
+ async resolve(ref) { return read()[ref]; },
28
+ async unset(ref) {
29
+ const data = read();
30
+ delete data[ref];
31
+ write(data);
32
+ },
33
+ };
34
+ }
35
+ export function createServiceVault(credentials) {
36
+ return {
37
+ async set(ref, value) { await credentials.set(ref, value); },
38
+ async resolve(ref) {
39
+ const result = await credentials.resolve(ref);
40
+ if (typeof result === 'string')
41
+ return result;
42
+ return result?.value;
43
+ },
44
+ async unset(ref) { await credentials.unset(ref); },
45
+ };
46
+ }
47
+ const REF_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
48
+ /** DSH credentials 只接受 POSIX 标识符,不能带斜杠或连字符。 */
49
+ export function credentialRef(channelId, key) {
50
+ const raw = `dfc_${channelId}_${key}`.replace(/[^A-Za-z0-9_]/g, '_');
51
+ if (!REF_PATTERN.test(raw))
52
+ throw new TypeError(`非法凭据名 ${raw}`);
53
+ return raw;
54
+ }