返回 DeepSeek-Reasonix
write_access_commands.go
根目录 / internal / bot / write_access_commands.go
1 package bot
2
3 import (
4 "context"
5 "strings"
6
7 "reasonix/internal/sandbox"
8 )
9
10 func (gw *BotGateway) normalizeApprovalShortcut(key, text string) (string, bool) {
11 id := gw.currentPendingApprovalID(key)
12 if id == "" {
13 return "", false
14 }
15 if gw.pendingApprovalIsRecovery(key, id) {
16 command, ok := recoveryShortcutCommand(text, gw.pendingRecoveryCanGrantTask(key, id))
17 return shortcutWithID(command, id, ok)
18 }
19 if gw.pendingApprovalIsWriteAccess(key, id) {
20 command, ok := writeAccessShortcutCommand(text)
21 return shortcutWithID(command, id, ok)
22 }
23 command, ok := approvalShortcutCommand(text)
24 return shortcutWithID(command, id, ok)
25 }
26
27 func shortcutWithID(command, id string, ok bool) (string, bool) {
28 if !ok {
29 return "", false
30 }
31 return command + " " + id, true
32 }
33
34 func writeAccessShortcutCommand(text string) (string, bool) {
35 switch strings.ToLower(strings.TrimSpace(text)) {
36 case "1", "y", "yes", "ok", "同意", "批准", "允许", "允许一次":
37 return "/approve", true
38 case "2", "a", "session", "本会话", "本会话允许":
39 return "/approve-session", true
40 case "3", "0", "n", "no", "deny", "拒绝":
41 return "/deny", true
42 default:
43 return "", false
44 }
45 }
46
47 func (gw *BotGateway) pendingApprovalIsWriteAccess(key, id string) bool {
48 gw.mu.Lock()
49 defer gw.mu.Unlock()
50 state, ok := gw.controllers[key]
51 if !ok || state.pendingApprovals == nil {
52 return false
53 }
54 a, ok := state.pendingApprovals[id]
55 return ok && isWriteAccessApproval(a)
56 }
57
58 func (gw *BotGateway) handleSlashCommand(ctx context.Context, adapter Adapter, key string, msg InboundMessage) {
59 if gw.handleWriteAccessSlashCommand(ctx, adapter, msg, key) {
60 return
61 }
62 gw.handleSlashCommandCore(ctx, adapter, key, msg)
63 }
64
65 func (gw *BotGateway) handleWriteAccessSlashCommand(ctx context.Context, adapter Adapter, msg InboundMessage, key string) bool {
66 switch {
67 case strings.HasPrefix(msg.Text, "/approve-session"):
68 gw.handleScopedApprove(ctx, adapter, msg, key, sandbox.ApprovalScopeSession, "用法: /approve-session <id>", "已批准本会话写入范围。")
69 return true
70 default:
71 return false
72 }
73 }
74
75 func (gw *BotGateway) handleScopedApprove(ctx context.Context, adapter Adapter, msg InboundMessage, key string, scope sandbox.ApprovalScope, usage, okText string) {
76 if !gw.requireCommandRole(ctx, adapter, msg, "approver") {
77 return
78 }
79 parts := strings.Fields(msg.Text)
80 if len(parts) < 2 {
81 _ = gw.sendText(ctx, adapter, msg, usage)
82 return
83 }
84 gw.mu.Lock()
85 state, ok := gw.controllers[key]
86 gw.mu.Unlock()
87 if !ok || state.ctrl == nil {
88 _ = gw.sendText(ctx, adapter, msg, "没有找到当前会话中的待审批操作,请重新触发一次操作。")
89 return
90 }
91 if gw.pendingApprovalIsRecovery(key, parts[1]) {
92 _ = gw.sendText(ctx, adapter, msg, "恢复确认不支持会话/项目写入授权,请使用继续或换个办法。")
93 return
94 }
95 err := state.ctrl.ResolveApproval(parts[1], true, scope)
96 gw.forgetPendingApproval(key, parts[1])
97 if err != nil {
98 _ = gw.sendText(ctx, adapter, msg, "保存失败: "+err.Error())
99 return
100 }
101 _ = gw.sendText(ctx, adapter, msg, okText)
102 }
103
104 func botHelpText() string {
105 return "可用命令:\n" +
106 "/stop - 停止当前任务\n/new - 开始新会话\n/reset - 重置会话\n" +
107 "/approve <id> - 批准操作(仅本次)\n/approve-session <id> - 本会话允许此范围\n" +
108 "/deny <id> - 拒绝操作\n" +
109 "/answer <id> <选项> - 回答交互问题\n" +
110 "/mode read-only|workspace-write|danger-full-access|status - 切换或查看权限模式\n/queue steer|followup|collect|interrupt|status - 切换或查看队列模式\n" +
111 "/projects [关键词] - 查看可切换项目索引\n/use project <id|名称> - 将当前远端会话切到某个项目\n" +
112 "/sessions search <关键词> - 搜索可 attach 的历史会话\n/attach session <id|关键词> - 绑定当前远端会话到已有历史会话\n" +
113 "/search all <关键词> - 跨已索引项目检索文件内容\n" +
114 "/desktop status|watch|approve|deny|answer - 桌面端上帝视角(需内嵌运行)\n" +
115 "/status - 查看状态\n/help - 显示帮助"
116 }
117
117 lines GO