deskbuddy 0.2.0-beta.4 → 0.2.0-beta.40

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 (59) hide show
  1. package/README.md +2 -2
  2. package/dist/cbchannel.cjs +187 -0
  3. package/dist/chunk-YZGPNQYG.js +12096 -0
  4. package/dist/cli.js +17363 -4055
  5. package/dist/deskbuddy-dsh.mjs +983 -0
  6. package/dist/enable-QGQVV2TC.js +15 -0
  7. package/dist/gate.cjs +752 -45
  8. package/dist/hook.cjs +1748 -76
  9. package/dist/launcher.cjs +471 -0
  10. package/dist/ocTranslate.mjs +144 -0
  11. package/dist/opencode.mjs +1025 -61
  12. package/dist/wake.cjs +15 -5
  13. package/docs/INSTALL.md +24 -10
  14. package/hook/cbchannel.cjs +187 -0
  15. package/hook/deskbuddy-dsh.d.mts +40 -0
  16. package/hook/deskbuddy-dsh.mjs +983 -0
  17. package/hook/gate.cjs +752 -45
  18. package/hook/hook-debug.log +466 -0
  19. package/hook/hook.cjs +1748 -76
  20. package/hook/launcher.cjs +471 -0
  21. package/hook/ocTranslate.d.mts +9 -0
  22. package/hook/ocTranslate.mjs +144 -0
  23. package/hook/opencode.d.mts +50 -1
  24. package/hook/opencode.mjs +1025 -61
  25. package/hook/wake.cjs +15 -5
  26. package/package.json +3 -2
  27. package/vendor/node-pty/LICENSE +69 -0
  28. package/vendor/node-pty/lib/conpty_console_list_agent.js +16 -0
  29. package/vendor/node-pty/lib/eventEmitter2.js +47 -0
  30. package/vendor/node-pty/lib/index.js +52 -0
  31. package/vendor/node-pty/lib/interfaces.js +7 -0
  32. package/vendor/node-pty/lib/shared/conout.js +11 -0
  33. package/vendor/node-pty/lib/terminal.js +190 -0
  34. package/vendor/node-pty/lib/types.js +7 -0
  35. package/vendor/node-pty/lib/unixTerminal.js +346 -0
  36. package/vendor/node-pty/lib/utils.js +39 -0
  37. package/vendor/node-pty/lib/windowsConoutConnection.js +125 -0
  38. package/vendor/node-pty/lib/windowsPtyAgent.js +320 -0
  39. package/vendor/node-pty/lib/windowsTerminal.js +199 -0
  40. package/vendor/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  41. package/vendor/node-pty/package.json +7 -0
  42. package/vendor/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  43. package/vendor/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  44. package/vendor/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  45. package/vendor/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  46. package/vendor/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  47. package/vendor/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  48. package/vendor/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  49. package/vendor/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  50. package/vendor/node-pty/prebuilds/win32-arm64/pty.node +0 -0
  51. package/vendor/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  52. package/vendor/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
  53. package/vendor/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  54. package/vendor/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  55. package/vendor/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  56. package/vendor/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  57. package/vendor/node-pty/prebuilds/win32-x64/pty.node +0 -0
  58. package/vendor/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
  59. package/vendor/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DeskBuddy CLI
2
2
 
3
- 让 DeskBuddy 桌面硬件和电脑上的 AI 编码智能体(**Claude Code**、**Codex**、**WorkBuddy**、**Qoder CN**、**opencode**)配合:
3
+ 让 DeskBuddy 桌面硬件和电脑上的 AI 编码智能体(**Claude Code**、**Codex**、**WorkBuddy**、**Qoder CN**、**Qoder**、**opencode**)配合:
4
4
 
5
5
  - **状态上屏**:每个会话窗口在干活、在等你确认、还是做完了,抬头看一眼桌上的屏就知道
6
6
  - **远程下指令**:在设备上对某个任务说一句话,电脑上的智能体接着干;也能在设备上新建任务
@@ -34,7 +34,7 @@ npx deskbuddy@beta status # 看
34
34
  | `deskbuddy status` | 当前环境 / 授权 / 已启用工具 / 本地会话 / 采集范围 |
35
35
  | `deskbuddy logout [--yes]` | 移除 hook 与自启并清除本机所有数据 |
36
36
 
37
- `tool` 可选:`claude_code`、`codex`、`workbuddy`、`qoder`、`opencode`。
37
+ `tool` 可选:`claude_code`、`codex`、`workbuddy`、`qoder_cn`(国内版)、`qoder`(国际版)、`opencode`。
38
38
 
39
39
  ## 设计要点
40
40
 
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+ // CodeBuddy Code(WorkBuddy 的命令行)终端会话的「设备授权中继」(spec 2026-10-09 terminal-approvals §2)。
4
+ //
5
+ // 装法:deskbuddy enable workbuddy 时写进 ~/.codebuddy/.mcp.json(用户级 MCP),CodeBuddy 每个进程起一份本脚本(stdio MCP)。
6
+ // 原理:CodeBuddy 2.161.1 认 MCP 服务声明的 experimental 能力 `claude/channel` + `claude/channel/permission`,
7
+ // 交互窗口弹授权框时把同一个请求广播给声明了权限中继的服务(notifications/claude/channel/permission_request),
8
+ // 与本地框【竞速】:哪边先答算哪边(源码 HandleInterruptions:Promise.race([本地, channel]))。
9
+ // 不需要 --channels 参数(没给就不设白名单),微信 / 企微桥用的就是这条路。
10
+ // 本脚本只做转发:请求 → daemon(/gate,挂着等设备)→ 设备答了 → notifications/claude/channel/permission 回 CodeBuddy。
11
+ //
12
+ // 🔴 方向与 gate.cjs 的窗口模式一致:任何失败都【不表态】(不回通知),授权框照常由电脑前的人处理。
13
+ // 永远不主动回 allow:只有 daemon 明确给出设备的决定才回。
14
+ // 🔴 不提供任何工具、不往对话里推消息(不发 notifications/claude/channel)。
15
+ // 只 require Node 内置模块(同 hook.cjs)。
16
+ const fs = require('node:fs')
17
+ const os = require('node:os')
18
+ const path = require('node:path')
19
+ const http = require('node:http')
20
+
21
+ const BEHAVIORS = new Set(['allow', 'allow_always', 'deny', 'deny_and_exit_plan'])
22
+
23
+ function deskbuddyHome() {
24
+ return process.env.DESKBUDDY_HOME || path.join(os.homedir(), '.deskbuddy')
25
+ }
26
+
27
+ /** 卡片拒绝项文案里的产品名(读 codebuddy 包的 product.json):会话借不到时 daemon 也能照原文出全选项 */
28
+ let PRODUCT = ''
29
+
30
+ /** 只在终端里用户自己开的交互 codebuddy 里声明权限中继(与 gate.cjs isInteractiveCodebuddy 同一判据) */
31
+ function eligible() {
32
+ if (process.env.DESKBUDDY_CBCHANNEL_FORCE === '1') {
33
+ PRODUCT = process.env.DESKBUDDY_CBCHANNEL_PRODUCT || '' // 只给测试
34
+ return true
35
+ }
36
+ if (process.env.DESKBUDDY_LAUNCHED_WINDOW) return false
37
+ try {
38
+ const gate = require(path.join(__dirname, 'gate.cjs'))
39
+ const agent = gate.findCodebuddy()
40
+ if (agent === null) return false
41
+ PRODUCT = gate.codebuddyProduct(agent.script) || ''
42
+ return true
43
+ } catch (_) {
44
+ return false
45
+ }
46
+ }
47
+
48
+ function send(msg) {
49
+ try {
50
+ process.stdout.write(JSON.stringify(msg) + '\n')
51
+ } catch (_) {
52
+ /* 写不了就算了:不表态 */
53
+ }
54
+ }
55
+
56
+ const pending = new Set()
57
+
58
+ function ask(params) {
59
+ let conf
60
+ try {
61
+ conf = JSON.parse(fs.readFileSync(path.join(deskbuddyHome(), 'gate.json'), 'utf8'))
62
+ } catch (_) {
63
+ return
64
+ }
65
+ if (!conf || !Number.isInteger(conf.port) || typeof conf.token !== 'string' || !conf.token) return
66
+ const requestId = typeof params.request_id === 'string' ? params.request_id : ''
67
+ const toolName = typeof params.tool_name === 'string' ? params.tool_name : ''
68
+ if (!/^[a-z]{1,16}$/.test(requestId) || !toolName) return
69
+ const body = JSON.stringify({
70
+ mode: 'window',
71
+ tool: 'workbuddy',
72
+ hook: {
73
+ hook_event_name: 'DeskbuddyCbChannel',
74
+ tool_name: toolName,
75
+ deskbuddy_channel: {
76
+ agentPid: process.ppid,
77
+ requestId,
78
+ inputPreview: typeof params.input_preview === 'string' ? params.input_preview.slice(0, 4000) : '',
79
+ cwd: process.cwd(),
80
+ product: PRODUCT,
81
+ },
82
+ },
83
+ })
84
+ const req = http.request(
85
+ {
86
+ host: '127.0.0.1',
87
+ port: conf.port,
88
+ method: 'POST',
89
+ path: '/gate',
90
+ headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body), 'x-gate-token': conf.token },
91
+ },
92
+ (res) => {
93
+ let data = ''
94
+ res.setEncoding('utf8')
95
+ res.on('data', (c) => (data += c))
96
+ res.on('end', () => {
97
+ pending.delete(req)
98
+ if (res.statusCode !== 200) return
99
+ let out
100
+ try {
101
+ out = JSON.parse(data)
102
+ } catch (_) {
103
+ return
104
+ }
105
+ const behavior = behaviorOf(out)
106
+ if (behavior) send({ jsonrpc: '2.0', method: 'notifications/claude/channel/permission', params: { request_id: requestId, behavior } })
107
+ })
108
+ res.on('error', () => pending.delete(req))
109
+ }
110
+ )
111
+ req.on('error', () => pending.delete(req))
112
+ pending.add(req)
113
+ req.end(body)
114
+ }
115
+
116
+ /** daemon 的回复 → CodeBuddy 的 behavior。选项的本地回填(native.behavior)优先,其余按 allow / deny;不表态返回 '' */
117
+ function behaviorOf(out) {
118
+ if (!out || (out.decision !== 'allow' && out.decision !== 'deny')) return ''
119
+ const nb = out.native && typeof out.native === 'object' ? out.native.behavior : undefined
120
+ if (typeof nb === 'string' && BEHAVIORS.has(nb)) {
121
+ // 粗粒度必须一致:allow* 只配 allow,deny* 只配 deny
122
+ if (out.decision === 'allow' ? nb.startsWith('allow') : nb.startsWith('deny')) return nb
123
+ return ''
124
+ }
125
+ return out.decision
126
+ }
127
+
128
+ function onMessage(m, relay) {
129
+ if (!m || typeof m !== 'object') return
130
+ if (m.method === 'initialize' && m.id !== undefined) {
131
+ const pv = m.params && typeof m.params.protocolVersion === 'string' ? m.params.protocolVersion : '2025-06-18'
132
+ const experimental = relay ? { 'claude/channel': {}, 'claude/channel/permission': {} } : undefined
133
+ send({
134
+ jsonrpc: '2.0',
135
+ id: m.id,
136
+ result: { protocolVersion: pv, capabilities: experimental ? { experimental } : {}, serverInfo: { name: 'deskbuddy', version: '1' } },
137
+ })
138
+ return
139
+ }
140
+ if (m.method === 'notifications/claude/channel/permission_request') {
141
+ if (relay && m.params && typeof m.params === 'object') ask(m.params)
142
+ return
143
+ }
144
+ if (m.method === 'tools/list' && m.id !== undefined) return send({ jsonrpc: '2.0', id: m.id, result: { tools: [] } })
145
+ if (m.method === 'ping' && m.id !== undefined) return send({ jsonrpc: '2.0', id: m.id, result: {} })
146
+ // 其余请求:方法不存在;通知:忽略
147
+ if (m.id !== undefined && typeof m.method === 'string') send({ jsonrpc: '2.0', id: m.id, error: { code: -32601, message: 'Method not found' } })
148
+ }
149
+
150
+ function main() {
151
+ const relay = eligible()
152
+ let buf = ''
153
+ process.stdin.setEncoding('utf8')
154
+ process.stdin.on('data', (c) => {
155
+ buf += c
156
+ let i
157
+ while ((i = buf.indexOf('\n')) >= 0) {
158
+ const line = buf.slice(0, i).trim()
159
+ buf = buf.slice(i + 1)
160
+ if (!line) continue
161
+ let m
162
+ try {
163
+ m = JSON.parse(line)
164
+ } catch (_) {
165
+ continue
166
+ }
167
+ try {
168
+ onMessage(m, relay)
169
+ } catch (_) {
170
+ /* 单条出错不影响后面的 */
171
+ }
172
+ }
173
+ })
174
+ // CodeBuddy 退出 / 断开:挂着的请求一并断掉,daemon 据此撤设备上的卡
175
+ process.stdin.on('end', () => {
176
+ for (const r of pending) r.destroy()
177
+ process.exit(0)
178
+ })
179
+ }
180
+
181
+ if (require.main === module) {
182
+ process.on('uncaughtException', () => {})
183
+ process.on('unhandledRejection', () => {})
184
+ main()
185
+ } else {
186
+ module.exports = { behaviorOf, onMessage }
187
+ }