@lanbaolu/dsh-wechat-bridge 0.1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/cordis.patch.yml +12 -0
  4. package/docs/feasibility-plan.md +62 -0
  5. package/lib/bridge/commands/handlers.js +155 -0
  6. package/lib/bridge/commands/handlers.js.map +1 -0
  7. package/lib/bridge/commands/router.js +52 -0
  8. package/lib/bridge/commands/router.js.map +1 -0
  9. package/lib/bridge/config.js +40 -0
  10. package/lib/bridge/config.js.map +1 -0
  11. package/lib/bridge/constants.js +14 -0
  12. package/lib/bridge/constants.js.map +1 -0
  13. package/lib/bridge/dsh-client.js +100 -0
  14. package/lib/bridge/dsh-client.js.map +1 -0
  15. package/lib/bridge/logger.js +75 -0
  16. package/lib/bridge/logger.js.map +1 -0
  17. package/lib/bridge/main.js +546 -0
  18. package/lib/bridge/main.js.map +1 -0
  19. package/lib/bridge/pending-queue.js +55 -0
  20. package/lib/bridge/pending-queue.js.map +1 -0
  21. package/lib/bridge/session.js +84 -0
  22. package/lib/bridge/session.js.map +1 -0
  23. package/lib/bridge/store.js +38 -0
  24. package/lib/bridge/store.js.map +1 -0
  25. package/lib/bridge/wechat/accounts.js +50 -0
  26. package/lib/bridge/wechat/accounts.js.map +1 -0
  27. package/lib/bridge/wechat/api.js +197 -0
  28. package/lib/bridge/wechat/api.js.map +1 -0
  29. package/lib/bridge/wechat/cdn.js +45 -0
  30. package/lib/bridge/wechat/cdn.js.map +1 -0
  31. package/lib/bridge/wechat/crypto.js +17 -0
  32. package/lib/bridge/wechat/crypto.js.map +1 -0
  33. package/lib/bridge/wechat/login.js +139 -0
  34. package/lib/bridge/wechat/login.js.map +1 -0
  35. package/lib/bridge/wechat/media.js +138 -0
  36. package/lib/bridge/wechat/media.js.map +1 -0
  37. package/lib/bridge/wechat/monitor.js +104 -0
  38. package/lib/bridge/wechat/monitor.js.map +1 -0
  39. package/lib/bridge/wechat/send.js +172 -0
  40. package/lib/bridge/wechat/send.js.map +1 -0
  41. package/lib/bridge/wechat/sync-buf.js +11 -0
  42. package/lib/bridge/wechat/sync-buf.js.map +1 -0
  43. package/lib/bridge/wechat/types.js +35 -0
  44. package/lib/bridge/wechat/types.js.map +1 -0
  45. package/lib/bridge/wechat/upload.js +111 -0
  46. package/lib/bridge/wechat/upload.js.map +1 -0
  47. package/lib/client/Panel.js +205 -0
  48. package/lib/client/Panel.js.map +1 -0
  49. package/lib/client/index.js +11 -0
  50. package/lib/client/index.js.map +1 -0
  51. package/lib/client.js +376 -0
  52. package/lib/client.js.map +1 -0
  53. package/lib/index.js +828 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/types/bridge/commands/handlers.d.ts +18 -0
  56. package/lib/types/bridge/commands/router.d.ts +26 -0
  57. package/lib/types/bridge/config.d.ts +7 -0
  58. package/lib/types/bridge/constants.d.ts +10 -0
  59. package/lib/types/bridge/dsh-client.d.ts +39 -0
  60. package/lib/types/bridge/logger.d.ts +13 -0
  61. package/lib/types/bridge/main.d.ts +1 -0
  62. package/lib/types/bridge/pending-queue.d.ts +10 -0
  63. package/lib/types/bridge/session.d.ts +22 -0
  64. package/lib/types/bridge/store.d.ts +11 -0
  65. package/lib/types/bridge/wechat/accounts.d.ts +14 -0
  66. package/lib/types/bridge/wechat/api.d.ts +32 -0
  67. package/lib/types/bridge/wechat/cdn.d.ts +2 -0
  68. package/lib/types/bridge/wechat/crypto.d.ts +4 -0
  69. package/lib/types/bridge/wechat/login.d.ts +31 -0
  70. package/lib/types/bridge/wechat/media.d.ts +20 -0
  71. package/lib/types/bridge/wechat/monitor.d.ts +10 -0
  72. package/lib/types/bridge/wechat/send.d.ts +6 -0
  73. package/lib/types/bridge/wechat/sync-buf.d.ts +2 -0
  74. package/lib/types/bridge/wechat/types.d.ts +135 -0
  75. package/lib/types/bridge/wechat/upload.d.ts +11 -0
  76. package/lib/types/client/Panel.d.ts +3 -0
  77. package/lib/types/client/index.d.ts +9 -0
  78. package/lib/types/index.d.ts +39 -0
  79. package/package.json +92 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-wechat-bridge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # @lanbaolu/dsh-wechat-bridge
2
+
3
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
4
+ ![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)
5
+ ![CI](https://github.com/lanbaolu/dsh-wechat-bridge/actions/workflows/ci.yml/badge.svg)
6
+ ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)
7
+
8
+ 基于 [Wechat-ggGitHub/wechat-claude-code](https://github.com/Wechat-ggGitHub/wechat-claude-code) 开发的 **DeepSeek Harness (DSH) 微信桥接插件**。
9
+
10
+ > ⚠️ 免责声明:本项目仅用于个人学习与自动化。使用非官方微信协议存在账号风险,请自行评估并承担后果。
11
+
12
+ **三端通用**:Windows / macOS / Linux 均使用纯 Node.js 进程管理,不依赖 launchd / systemd / Windows Service;同时提供 DSH 模型工具(CLI/Headless 可用)和 Web 管理面板(Web/桌面可用)。
13
+
14
+ ## 功能
15
+
16
+ - 微信扫码绑定个人微信后,在微信里直接与 **DSH 本机 Agent** 对话。
17
+ - 复用 wechat-claude-code 的 iLink Bot 微信协议层:文字、图片、语音转文字、文件收发。
18
+ - 守护进程由 DSH 插件管理:启动 / 停止 / 重启 / 状态 / 日志,全部走模型工具或 Web 面板。
19
+ - 每个微信账号对应一个 DSH 会话,DSH Host 重启后会自动 `resume` 原持久化会话,对话上下文不断档;`/clear`、`/new`、`/stop`、`/cwd`、`/model`、`/prompt` 等斜杠命令可用。
20
+ - 流式回复:DSH Agent 的 `assistant/chunk` 通过本地 SSE 推送到微信(批量发送,不刷屏)。
21
+ - 超时安抚:DSH 超过 5 分钟无输出时自动发一条“还在处理”的消息。
22
+ - 文件双向:微信发图片/文件给 DSH;DSH 回复中提到的本地文件会自动推回微信。
23
+ - 消息队列:处理中收到的普通消息会排队,等当前任务结束后继续处理。
24
+
25
+ 方向对照与后续可行性方案见 [`docs/feasibility-plan.md`](docs/feasibility-plan.md)。
26
+
27
+ ## 架构
28
+
29
+ ```
30
+ 微信 App ←→ iLink Bot API ←→ bridge daemon (Node.js)
31
+ │ HTTP + SSE (127.0.0.1, token 鉴权)
32
+
33
+ DSH Host Plugin
34
+ │ ctx.agents.create/resume + followup
35
+
36
+ DSH Agent (本机 LLM + 工具)
37
+ ```
38
+
39
+ - `src/bridge/`:从 wechat-claude-code 移植的微信协议层 + 适配 DSH 的守护进程。
40
+ - `src/index.ts`:DSH Host 插件,负责内部 API、Agent 生命周期、守护进程管理和模型工具。
41
+ - `src/client/index.ts`:Web 管理面板(`settings.section` 槽位)。
42
+
43
+ ## 安装
44
+
45
+ ### 方式一:npm 一键安装(推荐)
46
+
47
+ ```bash
48
+ npm install @lanbaolu/dsh-wechat-bridge
49
+ dsh plugin --profile web add @lanbaolu/dsh-wechat-bridge
50
+ dsh web
51
+ ```
52
+
53
+ ### 方式二:本地路径安装(开发/个人使用)
54
+
55
+ 在 DSH profile 中安装本地包:
56
+
57
+ ```bash
58
+ git clone https://github.com/lanbaolu/dsh-wechat-bridge.git
59
+ dsh plugin --profile web add /path/to/dsh-wechat-bridge
60
+ dsh web
61
+ ```
62
+
63
+ 或者使用超级注入器(开发模式):
64
+
65
+ ```bash
66
+ dev_inject_plugin /path/to/dsh-wechat-bridge
67
+ ```
68
+
69
+ ### 方式三:从源码运行
70
+
71
+ ```bash
72
+ npm install
73
+ npm run build # host → lib/
74
+ npm run build:client # client → lib/client.js
75
+ npm run typecheck
76
+ ```
77
+
78
+ > 注意:`build:client` 使用 `tsdown`,需要 Node.js 22.18+ 或 24.11+(CI 使用 22/24 验证)。运行时要求仍为 Node 18+。
79
+
80
+ ## 使用
81
+
82
+ ### 1. 扫码绑定
83
+
84
+ 推荐在 DSH Web 设置页的「📱 微信桥接」面板中完成:
85
+
86
+ 1. 打开 **Settings / 设置** → **📱 微信桥接**。
87
+ 2. 填写 DSH 工作目录。
88
+ 3. 点击 **扫码绑定**,用微信扫描页面上的二维码。
89
+ 4. 绑定成功后直接点击 **启动**。
90
+
91
+ 也可以在 DSH 所在机器终端执行:
92
+
93
+ ```bash
94
+ node lib/bridge/main.js setup
95
+ ```
96
+
97
+ 按提示用微信扫码,完成后选择 DSH 工作目录。
98
+
99
+ ### 2. 启动桥接
100
+
101
+ 在 DSH 对话中让模型执行:
102
+
103
+ - `wechat_bridge_start`
104
+ - `wechat_bridge_status`
105
+ - `wechat_bridge_logs`
106
+ - `wechat_bridge_stop`
107
+
108
+ 或者在 Web 设置页(`settings.section` 槽位)点击“启动 / 停止 / 重启”。
109
+
110
+ ### 3. 微信端命令
111
+
112
+ | 命令 | 说明 |
113
+ |------|------|
114
+ | `/help` | 显示帮助 |
115
+ | `/clear` | 清除当前 DSH 会话 |
116
+ | `/new` | 开启全新会话(等价 `/clear`) |
117
+ | `/stop` | 停止当前任务并清空排队消息 |
118
+ | `/status` | 查看会话状态 |
119
+ | `/cwd [路径]` | 查看 / 切换工作目录 |
120
+ | `/model [名称]` | 查看 / 切换模型 |
121
+ | `/prompt [内容]` | 查看 / 设置系统提示词 |
122
+ | `/history [数量]` | 查看最近对话 |
123
+ | `/send <路径>` | 发送本地文件到微信 |
124
+
125
+ ## 数据目录
126
+
127
+ 默认 `~/.dsh/wechat-bridge/`(可用 `DSH_HOME` 调整):
128
+
129
+ ```
130
+ ~/.dsh/wechat-bridge/
131
+ ├── accounts/ # 微信账号凭证(0600)
132
+ ├── sessions/ # 每个微信账号的本地会话状态
133
+ ├── session-ids.json # 微信账号 → DSH 持久化会话 ID 映射(用于重启后 resume)
134
+ ├── pending-queue/ # 发送失败暂存队列
135
+ ├── config.json # 工作目录 / 模型 / 系统提示词
136
+ └── logs/ # 运行日志
137
+ ```
138
+
139
+ ## 安全说明
140
+
141
+ - 守护进程与 DSH 插件之间的内部 API 只监听 `127.0.0.1`,并使用随机 token 鉴权。
142
+ - 微信账号凭证仅保存在本机 `~/.dsh/wechat-bridge/accounts/`,权限为 0600。
143
+ - 日志中的 token / secret / password 会自动脱敏。
144
+ - 请勿把真实账号凭证、token 或日志提交到 Issue / PR。
145
+
146
+ ## 贡献
147
+
148
+ 欢迎提交 Issue 和 PR。请先阅读 [`CONTRIBUTING.md`](CONTRIBUTING.md),并查看 [`SECURITY.md`](SECURITY.md) 了解安全报告方式。
149
+
150
+ ## License
151
+
152
+ [MIT](LICENSE)
@@ -0,0 +1,12 @@
1
+ # dsh-wechat-bridge — installable DSH profile bundle layer.
2
+ - insert:
3
+ - id: dsh-wechat-bridge
4
+ name: '@lanbaolu/dsh-wechat-bridge'
5
+ config:
6
+ dataDir: ''
7
+ host: '127.0.0.1'
8
+ port: 0
9
+ autoStart: false
10
+ provider: ''
11
+ model: ''
12
+ workingDirectory: ''
@@ -0,0 +1,62 @@
1
+ # 微信桥接方向对照与可行性方案
2
+
3
+ 对照文章《在你微信里用爽 Claude Code,我的开源 Skill 又更新了》中提到的方向和本插件的现状。
4
+
5
+ ## 已完成
6
+
7
+ | 方向 | 状态 | 说明 |
8
+ |---|---|---|
9
+ | 扫码即用 | ✅ | `node lib/bridge/main.js setup` 扫码绑定 |
10
+ | 消息不刷屏 | ✅ | 流式输出按阈值 + 定时批量发送,不再逐字刷屏 |
11
+ | 对方正在输入中 | ✅ | `startTyping` 已实现 |
12
+ | 文件发给 DSH | ✅ | 图片/文件下载到本地路径后作为上下文给 DSH |
13
+ | DSH 生成文件推回微信 | ✅ | 响应文本中识别可推送文件并自动 `sendFile` |
14
+ | 超时安抚 | ✅ | 5 分钟无输出时自动发送安抚消息 |
15
+ | 消息队列基础版 | ✅ | 串行处理 + 处理中消息重新排队,不再丢弃 |
16
+ | 跨重启续聊 | ✅ | 持久化 `accountId → dshSessionId` 映射,启动优先 `ctx.agents.resume`;失败再新建 |
17
+
18
+ ## 未完成 / 待评估
19
+
20
+ ### 1. 消息队列优化(进阶)
21
+
22
+ **现状**:消息按到达顺序串行处理;处理中的普通消息会重新排队,等当前轮结束再处理。基本不会“驴唇不对马嘴”。
23
+
24
+ **可行性方案**:
25
+ - 引入“每个微信账号一个 FIFO 队列” + “当前任务 ID”。
26
+ - 支持 `/stop` 清空队列并取消当前任务(已实现基础版)。
27
+ - 可选:同一用户连续发送时合并短消息(如 1 秒内的多条文本合并为一条 prompt)。
28
+ - 工作量:低;风险:低。
29
+
30
+ ### 2. 电脑休眠不中断
31
+
32
+ **现状**:未实现。电脑锁屏/合盖后 DSH 进程可能挂起,微信消息无法响应。
33
+
34
+ **可行性方案**:
35
+ - macOS:守护进程启动时 spawn `caffeinate -dimsu`,停止时 kill。
36
+ - Windows:`powercfg /requestoverride` 或 `powercfg /setacvalueindex` 阻止睡眠;也可用 Node `exec` 调用。
37
+ - Linux:`systemd-inhibit --what=sleep --mode=block`,或 `xdg-screensaver suspend`。
38
+ - 由 DSH 插件 Host 统一管理“抑制器子进程”,随桥接 start/stop 启停。
39
+ - 工作量:中;风险:低,但需注意用户可能不希望强制阻止睡眠,应做成配置项 `preventSleep: true/false`。
40
+
41
+ ### 3. 接续电脑会话 / 跨重启续聊
42
+
43
+ **现状**:已实现。桥接数据目录持久化 `accountId -> dshSessionId` 映射;DSH Host 重启后优先 `ctx.agents.resume` 恢复原会话,只有映射缺失、旧日志损坏或工作目录变化时才新建会话。
44
+
45
+ **后续可选**:
46
+ - 桥接守护进程改为独立 DSH SDK runtime(`@deepseek-ai/dsh-sdk-client`),完全脱离 Web GUI 进程,天然可跨重启恢复。
47
+ - 增加 `/sessionlist`、`/session <id>` 让微信端手动绑定已有 DSH 会话(对齐 dsh-im 的能力)。
48
+
49
+ ### 4. 电脑端与手机端体验完全一致
50
+
51
+ **现状**:桥接直接调用 DSH 进程内 Agent,不是另起 Claude CLI,因此编排、工具、记忆都来自同一个 DSH 运行时,体验基本一致。
52
+
53
+ **待办**:
54
+ - 微信会话与当前 Web 会话仍是不同会话;若要“接着电脑当前会话聊”,依赖上面的“接续会话”方案。
55
+ - 工作量:低(已基本达成)。
56
+
57
+ ## 建议优先级
58
+
59
+ 1. 先做**电脑休眠不中断**(配置项默认关闭,用户可选开启)。
60
+ 2. **跨重启续聊**已完成(持久化映射 + resume)。
61
+ 3. 消息队列进阶按需做(当前基础版已可用)。
62
+ 4. 按需对齐 dsh-im:多 IM 渠道、会话列表/绑定、更完整的 Host/Client 测试。
@@ -0,0 +1,155 @@
1
+ import { loadConfig, saveConfig } from '../config.js';
2
+ import { DEFAULT_WORKING_DIR } from '../constants.js';
3
+ import { readFileSync, existsSync, statSync } from 'node:fs';
4
+ import { resolve, join } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { fileURLToPath } from 'node:url';
7
+ const HELP_TEXT = `可用命令:
8
+
9
+ 会话管理:
10
+ /help 显示帮助
11
+ /stop 停止当前对话并清空排队消息
12
+ /clear 清除当前会话并开启新会话
13
+ /new 开启全新会话(等价 /clear)
14
+ /reset 完全重置(包括工作目录等设置)
15
+ /status 查看当前会话状态
16
+ /history [数量] 查看对话记录(默认最近20条)
17
+ /undo [数量] 撤销最近对话(默认1条)
18
+
19
+ 文件:
20
+ /send <路径> 发送本地文件(图片直接显示,其他文件作为附件)
21
+
22
+ 配置:
23
+ /cwd [路径] 查看或切换工作目录
24
+ /model [名称] 查看或切换模型
25
+ /prompt [内容] 查看或设置系统提示词(全局生效)
26
+
27
+ 其他:
28
+ /version 查看版本信息
29
+
30
+ 直接输入文字即可与 DSH 对话`;
31
+ export function handleHelp(_args) {
32
+ return { reply: HELP_TEXT, handled: true };
33
+ }
34
+ export function handleClear(ctx) {
35
+ const newSession = ctx.clearSession();
36
+ Object.assign(ctx.session, newSession);
37
+ return { reply: '✅ 会话已清除,下次消息将开始新会话。', handled: true };
38
+ }
39
+ /** Alias for /clear, matching the common “/new opens a fresh session” convention. */
40
+ export function handleNew(ctx) {
41
+ return handleClear(ctx);
42
+ }
43
+ export function handleCwd(ctx, args) {
44
+ if (!args) {
45
+ return { reply: `当前工作目录: ${ctx.session.workingDirectory}\n用法: /cwd <路径>`, handled: true };
46
+ }
47
+ ctx.updateSession({ workingDirectory: args });
48
+ return { reply: `✅ 工作目录已切换为: ${args}`, handled: true };
49
+ }
50
+ export function handleModel(ctx, args) {
51
+ if (!args) {
52
+ return { reply: '用法: /model <模型名称>\n例: /model deepseek-v4-flash', handled: true };
53
+ }
54
+ ctx.updateSession({ model: args });
55
+ return { reply: `✅ 模型已切换为: ${args}`, handled: true };
56
+ }
57
+ export function handleStatus(ctx) {
58
+ const s = ctx.session;
59
+ const lines = [
60
+ '📊 会话状态',
61
+ '',
62
+ `工作目录: ${s.workingDirectory}`,
63
+ `模型: ${s.model ?? '默认'}`,
64
+ `状态: ${s.state}`,
65
+ ];
66
+ return { reply: lines.join('\n'), handled: true };
67
+ }
68
+ const MAX_HISTORY_LIMIT = 100;
69
+ export function handleHistory(ctx, args) {
70
+ const limit = args ? parseInt(args, 10) : 20;
71
+ if (isNaN(limit) || limit <= 0) {
72
+ return { reply: '用法: /history [数量]\n例: /history 50(显示最近50条对话)', handled: true };
73
+ }
74
+ const effectiveLimit = Math.min(limit, MAX_HISTORY_LIMIT);
75
+ const historyText = ctx.getChatHistoryText?.(effectiveLimit) || '暂无对话记录';
76
+ return { reply: `📝 对话记录(最近${effectiveLimit}条):\n\n${historyText}`, handled: true };
77
+ }
78
+ /** 完全重置会话(包括工作目录等设置) */
79
+ export function handleReset(ctx) {
80
+ const newSession = ctx.clearSession();
81
+ newSession.workingDirectory = DEFAULT_WORKING_DIR;
82
+ Object.assign(ctx.session, newSession);
83
+ return { reply: '✅ 会话已完全重置,所有设置恢复默认。', handled: true };
84
+ }
85
+ /** 撤销最近 N 条对话 */
86
+ export function handleUndo(ctx, args) {
87
+ const count = args ? parseInt(args, 10) : 1;
88
+ if (isNaN(count) || count <= 0) {
89
+ return { reply: '用法: /undo [数量]\n例: /undo 2(撤销最近2条对话)', handled: true };
90
+ }
91
+ const history = ctx.session.chatHistory || [];
92
+ if (history.length === 0) {
93
+ return { reply: '⚠️ 没有对话记录可撤销', handled: true };
94
+ }
95
+ const actualCount = Math.min(count, history.length);
96
+ ctx.session.chatHistory = history.slice(0, -actualCount);
97
+ ctx.updateSession({ chatHistory: ctx.session.chatHistory });
98
+ return { reply: `✅ 已撤销最近 ${actualCount} 条对话`, handled: true };
99
+ }
100
+ /** 查看版本信息 */
101
+ export function handleVersion() {
102
+ try {
103
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
104
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', '..', 'package.json'), 'utf-8'));
105
+ const version = pkg.version || 'unknown';
106
+ return { reply: `dsh-wechat-bridge v${version}`, handled: true };
107
+ }
108
+ catch {
109
+ return { reply: 'dsh-wechat-bridge (version unknown)', handled: true };
110
+ }
111
+ }
112
+ export function handlePrompt(_ctx, args) {
113
+ const config = loadConfig();
114
+ if (!args) {
115
+ const current = config.systemPrompt;
116
+ if (current) {
117
+ return { reply: `📝 当前系统提示词:\n${current}\n\n用法:\n/prompt <提示词> — 设置\n/prompt clear — 清除`, handled: true };
118
+ }
119
+ return { reply: '📝 暂无系统提示词\n\n用法: /prompt <提示词>\n例: /prompt 用中文回答我', handled: true };
120
+ }
121
+ if (args.trim().toLowerCase() === 'clear') {
122
+ config.systemPrompt = undefined;
123
+ saveConfig(config);
124
+ return { reply: '✅ 系统提示词已清除', handled: true };
125
+ }
126
+ config.systemPrompt = args.trim();
127
+ saveConfig(config);
128
+ return { reply: `✅ 系统提示词已设置:\n${config.systemPrompt}`, handled: true };
129
+ }
130
+ export function handleSend(ctx, args) {
131
+ if (!args) {
132
+ return { reply: '用法: /send <文件路径>\n例: /send ~/Documents/report.pdf\n /send ./chart.png', handled: true };
133
+ }
134
+ const resolved = args.startsWith('/')
135
+ ? args
136
+ : resolve(ctx.session.workingDirectory, args.replace(/^~/, homedir()));
137
+ if (!existsSync(resolved)) {
138
+ return { reply: `文件不存在: ${resolved}`, handled: true };
139
+ }
140
+ const stat = statSync(resolved);
141
+ if (stat.isDirectory()) {
142
+ return { reply: `这是一个目录,请指定文件: ${resolved}`, handled: true };
143
+ }
144
+ if (stat.size > 25 * 1024 * 1024) {
145
+ return { reply: `文件过大 (${(stat.size / 1024 / 1024).toFixed(1)}MB),最大支持 25MB`, handled: true };
146
+ }
147
+ return { handled: true, sendFile: resolved };
148
+ }
149
+ export function handleUnknown(cmd, _args) {
150
+ return {
151
+ handled: true,
152
+ reply: `未识别命令: /${cmd}\n输入 /help 查看可用命令`,
153
+ };
154
+ }
155
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../../src/bridge/commands/handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { logger } from '../logger.js';
2
+ import { handleHelp, handleClear, handleNew, handleCwd, handleModel, handleStatus, handleHistory, handleReset, handleUndo, handleVersion, handlePrompt, handleSend, handleUnknown } from './handlers.js';
3
+ /**
4
+ * Parse and dispatch a slash command.
5
+ *
6
+ * Supported commands:
7
+ * /help - Show help text with all available commands
8
+ * /clear - Clear the current session
9
+ * /model <name> - Update the session model
10
+ * /status - Show current session info
11
+ * /history - Show recent conversation history
12
+ */
13
+ export function routeCommand(ctx) {
14
+ const text = ctx.text.trim();
15
+ if (!text.startsWith('/')) {
16
+ return { handled: false };
17
+ }
18
+ const spaceIdx = text.indexOf(' ');
19
+ const cmd = (spaceIdx === -1 ? text.slice(1) : text.slice(1, spaceIdx)).toLowerCase();
20
+ const args = spaceIdx === -1 ? '' : text.slice(spaceIdx + 1).trim();
21
+ logger.info(`Slash command: /${cmd} ${args}`.trimEnd());
22
+ switch (cmd) {
23
+ case 'help':
24
+ return handleHelp(args);
25
+ case 'clear':
26
+ return handleClear(ctx);
27
+ case 'new':
28
+ return handleNew(ctx);
29
+ case 'reset':
30
+ return handleReset(ctx);
31
+ case 'cwd':
32
+ return handleCwd(ctx, args);
33
+ case 'model':
34
+ return handleModel(ctx, args);
35
+ case 'prompt':
36
+ return handlePrompt(ctx, args);
37
+ case 'status':
38
+ return handleStatus(ctx);
39
+ case 'history':
40
+ return handleHistory(ctx, args);
41
+ case 'undo':
42
+ return handleUndo(ctx, args);
43
+ case 'send':
44
+ return handleSend(ctx, args);
45
+ case 'version':
46
+ case 'v':
47
+ return handleVersion();
48
+ default:
49
+ return handleUnknown(cmd, args);
50
+ }
51
+ }
52
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/bridge/commands/router.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAkBzM;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,GAAmB;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAE7B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACtF,MAAM,IAAI,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpE,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAExD,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;QACxB,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9B,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC/B,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC/B,KAAK,SAAS,CAAC;QACf,KAAK,GAAG;YACN,OAAO,aAAa,EAAE,CAAC;QACzB;YACE,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { DATA_DIR, DEFAULT_WORKING_DIR } from "./constants.js";
4
+ const CONFIG_PATH = join(DATA_DIR, "config.json");
5
+ const DEFAULT_CONFIG = {
6
+ workingDirectory: DEFAULT_WORKING_DIR,
7
+ };
8
+ export function loadConfig() {
9
+ try {
10
+ const content = readFileSync(CONFIG_PATH, "utf-8");
11
+ const parsed = JSON.parse(content);
12
+ const config = {
13
+ workingDirectory: parsed.workingDirectory || DEFAULT_CONFIG.workingDirectory,
14
+ model: parsed.model,
15
+ systemPrompt: parsed.systemPrompt,
16
+ };
17
+ mkdirSync(config.workingDirectory, { recursive: true });
18
+ return config;
19
+ }
20
+ catch {
21
+ const config = { ...DEFAULT_CONFIG };
22
+ mkdirSync(config.workingDirectory, { recursive: true });
23
+ return config;
24
+ }
25
+ }
26
+ export function saveConfig(config) {
27
+ mkdirSync(DATA_DIR, { recursive: true });
28
+ const data = {
29
+ workingDirectory: config.workingDirectory,
30
+ };
31
+ if (config.model)
32
+ data.model = config.model;
33
+ if (config.systemPrompt)
34
+ data.systemPrompt = config.systemPrompt;
35
+ writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2) + "\n", "utf-8");
36
+ if (process.platform !== "win32") {
37
+ chmodSync(CONFIG_PATH, 0o600);
38
+ }
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/bridge/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQ/D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;AAElD,MAAM,cAAc,GAAW;IAC7B,gBAAgB,EAAE,mBAAmB;CACtC,CAAC;AAEF,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,MAAM,GAAW;YACrB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,cAAc,CAAC,gBAAgB;YAC5E,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,YAAY,EAAE,MAAM,CAAC,YAAY;SAClC,CAAC;QACF,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QACrC,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAA2B;QACnC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;KAC1C,CAAC;IACF,IAAI,MAAM,CAAC,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC5C,IAAI,MAAM,CAAC,YAAY;QAAE,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IACjE,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Bridge data directory.
5
+ *
6
+ * Default: $DSH_HOME/wechat-bridge (or ~/.dsh/wechat-bridge when DSH_HOME is
7
+ * unset). The host plugin sets DSH_BRIDGE_DATA_DIR explicitly when spawning
8
+ * the daemon, so both the plugin and the standalone daemon always agree.
9
+ */
10
+ export const DATA_DIR = process.env.DSH_BRIDGE_DATA_DIR
11
+ || join(process.env.DSH_HOME || join(homedir(), '.dsh'), 'wechat-bridge');
12
+ export const DEFAULT_WORKING_DIR = join(homedir(), 'Documents', 'DSH');
13
+ export const CDN_BASE_URL = 'https://novac2c.cdn.weixin.qq.com/c2c';
14
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/bridge/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB;OAClD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,YAAY,GAAG,uCAAuC,CAAC"}
@@ -0,0 +1,100 @@
1
+ import { logger } from './logger.js';
2
+ export class DshClient {
3
+ baseUrl;
4
+ token;
5
+ constructor(baseUrl, token) {
6
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
7
+ this.token = token;
8
+ }
9
+ headers() {
10
+ return {
11
+ 'Content-Type': 'application/json',
12
+ Authorization: `Bearer ${this.token}`,
13
+ };
14
+ }
15
+ async status() {
16
+ const res = await fetch(`${this.baseUrl}/api/status`, {
17
+ headers: this.headers(),
18
+ signal: AbortSignal.timeout(10_000),
19
+ });
20
+ if (!res.ok)
21
+ throw new Error(`status HTTP ${res.status}: ${await res.text()}`);
22
+ return res.json();
23
+ }
24
+ async prompt(input) {
25
+ const res = await fetch(`${this.baseUrl}/api/prompt`, {
26
+ method: 'POST',
27
+ headers: this.headers(),
28
+ body: JSON.stringify(input),
29
+ signal: AbortSignal.timeout(15_000),
30
+ });
31
+ if (!res.ok) {
32
+ const text = await res.text();
33
+ throw new Error(`prompt HTTP ${res.status}: ${text}`);
34
+ }
35
+ return res.json();
36
+ }
37
+ async stop(sessionId) {
38
+ await fetch(`${this.baseUrl}/api/stop`, {
39
+ method: 'POST',
40
+ headers: this.headers(),
41
+ body: JSON.stringify({ sessionId }),
42
+ signal: AbortSignal.timeout(10_000),
43
+ });
44
+ }
45
+ async clear(sessionId) {
46
+ await fetch(`${this.baseUrl}/api/clear`, {
47
+ method: 'POST',
48
+ headers: this.headers(),
49
+ body: JSON.stringify({ sessionId }),
50
+ signal: AbortSignal.timeout(10_000),
51
+ });
52
+ }
53
+ /**
54
+ * Open an SSE stream for one session. The connection stays open across
55
+ * multiple turns; the host sends `chunk`, `status`, and `done` events.
56
+ */
57
+ async stream(sessionId, onEvent, signal) {
58
+ const url = new URL(`${this.baseUrl}/api/stream`);
59
+ url.searchParams.set('sessionId', sessionId);
60
+ const res = await fetch(url, {
61
+ headers: this.headers(),
62
+ signal,
63
+ });
64
+ if (!res.ok || !res.body) {
65
+ throw new Error(`stream HTTP ${res.status}`);
66
+ }
67
+ const reader = res.body.getReader();
68
+ const decoder = new TextDecoder();
69
+ let buffer = '';
70
+ while (true) {
71
+ const { done, value } = await reader.read();
72
+ if (done)
73
+ break;
74
+ buffer += decoder.decode(value, { stream: true });
75
+ // SSE events are separated by a blank line.
76
+ let sep;
77
+ while ((sep = buffer.indexOf('\n\n')) >= 0) {
78
+ const rawEvent = buffer.slice(0, sep);
79
+ buffer = buffer.slice(sep + 2);
80
+ for (const line of rawEvent.split('\n')) {
81
+ if (!line.startsWith('data:'))
82
+ continue;
83
+ const payload = line.slice(5).trim();
84
+ if (!payload)
85
+ continue;
86
+ try {
87
+ onEvent(JSON.parse(payload));
88
+ }
89
+ catch (err) {
90
+ logger.warn('Failed to parse SSE event', {
91
+ payload,
92
+ error: err instanceof Error ? err.message : String(err),
93
+ });
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=dsh-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsh-client.js","sourceRoot":"","sources":["../../src/bridge/dsh-client.ts"],"names":[],"mappings":"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"}