# 使用指南 面向**使用者**,不是开发者。开发细节看 [README.md](README.md)。 **想看更细的?** 这批较新的能力(思考/复制/停止、插入消息与电脑端同步、分支对话、问答卡片)单独写在 [PANEL.zh.md](PANEL.zh.md):怎么用、边界在哪、哪些**还没在真机上验证过**。 --- ## 1. 它给你两条路 | 你想干什么 | 用哪个 | |---|---| | 在手机上看/继续电脑上的对话 | **手机面板** `/bridge` | | 用 Chatbox 这种 AI 客户端聊天 | **OpenAI 兼容端点** `/v1` | 两者驱动的是**同一批 DSH 会话**,用的也是同一套运行时 —— 所以权限档位和沙箱边界不会因为入口不同而不一样。你可以一边在手机上问、一边在电脑上看,对话是连续的。 --- ## 2. 第一次配置与登录 **先在电脑上打开设置页:** ``` http://127.0.0.1:19387/setup ``` 它替你干三件事: - **列出所有网卡的地址**,并直接给出可以复制的完整访问链接 —— 不用你自己 `ipconfig` 再拼 - 没配置时**一键生成并写入令牌** - 显示当前令牌来源,以及能不能在这里轮换 > 这个页面**只对本机开放**,局域网来的请求一律 403。因为它必须在"还没有令牌"的时候就能打开 —— 那是整个插件唯一一个先于令牌存在的端点。 生成令牌后**重启 DSH**,再用设置页给出的地址在手机浏览器打开: ``` http://<电脑的局域网IP>:19387/bridge?token=<你的 token> ``` - `<电脑的局域网IP>`:电脑的 LAN 地址。在电脑上查:`ipconfig`,找「以太网」或「WLAN」下面那个 IPv4。 - `<你的 token>`:在 `C:\Users\<你>\.dsh\profiles\desktop\plugins\dsh-openai-bridge\cordis.patch.yml` 里的 `token:` 那一行。 打开后地址栏里的 token 会**自动消失**(换成 cookie 并重定向到干净的 `/bridge/`),所以之后可以直接收藏 `/bridge/`。 > **token 为空 = 整个插件不挂载。** 所有路由返回 404。这是失败关闭,不是故障。 ### 第一次打开会要求「配对」 默认开着**设备白名单**:手机第一次访问会被拦下,显示「这台设备还没被允许」。 1. 手机上看到的页面会告诉你去电脑上取验证码 2. **电脑上会自动弹出设置页**(没弹就自己开 `http://127.0.0.1:19387/setup`) 3. 往下找「**设备白名单**」那张卡片,里面有一个 **6 位验证码**,点一下就能全选 4. 在手机页面填进去 → 确认 → 自动刷新进面板 **每台设备只需要做一次。** 批准后会写进 `C:\Users\<你>\.dsh\openai-bridge-clients.json`,重启也还在。想撤掉某台设备,就在同一个卡片里点「移除」。 - 验证码**只显示在电脑上**,手机的接口永远拿不到它 —— 所以光有那条带 token 的链接,别人也进不来,必须有人在电脑前。 - 填错 5 次这个码就作废;10 分钟后换一个新的,**不会永久锁死**。作废了想马上救?点卡片上的「**换一个验证码**」,旧的立刻失效。 - **自动弹窗有冷却**(默认 15 秒一次,同时最多留 16 台待配对设备):有人扫你的网段也不会弹满一屏窗口。 - `http://127.0.0.1:...`(本机)**永远不用配对**。 - 不想要这个功能?配置里设 `ipAllowlist: false`,就退回「有 token 就放行」。 > **诚实说一句:** 白名单挡不住已经在你局域网里的人 —— ARP 欺骗可以冒充已批准的地址,手机换个 Wi-Fi 也会变成一台「新设备」。它的作用是**让泄漏的 token 单独不够用**,不是当防火墙。 > **万一设置页显示不出来**(页面脚本坏了、或者浏览器有问题),直接用浏览器打开 `http://127.0.0.1:19387/setup/state` —— 那是同一个只对本机开放的 JSON,验证码就在里面的 `clientGate.pending[].code`。再不行,把配置里的 `ipAllowlist` 改成 `false` 并重启,白名单就整个关掉了。 --- ## 3. 界面 ### 顶栏 左边一个按钮:平时是 `☰`(打开会话抽屉),在会话/文件视图里会变成 `←`(返回)。 ### 会话抽屉(`☰`) - **+ 新对话** - **📁 文件** —— 浏览(见下) - 下面是所有 DSH 会话的列表:标题、时间、运行状态。**包括电脑端开的会话**,点进去就能读完整记录。 ### 分支对话 抽屉(☰)里每段会话下面都有一个 **分支** 按钮:它会从**最后一个完整回合**切一刀,复制出一段新的会话(带着前面的历史),然后直接打开新的那段。 - 切点**只会落在「跑完的回合」上** —— 不会把半个来回塞进新会话(那会让新会话一开头就有「有提问没回复」或「有工具调用没结果」) - 最后一个回合之后的东西(标题、投递标记、压缩检查点)**留在原来那段**,不会跟着漂过去 - **沙箱档位不继承**:分支出来的是本插件新建的会话,所以按新会话的规矩钉成配置里的档位(默认只读)。不想凭复制传播一个更宽的权限;真的需要,在手机上对新的那段单独申请 - **agent preset 会跟着源会话走**,所以分支出来的对话还是原来的那套组合 - 源会话从头到尾没跑完过一轮 → 会明确报错,而不是造一个没有历史的新会话 ### 插入消息:先放进去,不当场发 输入框下面有两个按钮,它们**不是一回事**: | 按钮 | 落到哪 | 作用 | |---|---|---| | **排队** | `next-turn` | 等下一轮再发。安全,默认。 | | **插话** | `next-step` | 插进**正在跑的这一轮**,影响它接下来的动作。 | 两个都是**先放进去、不当场发** —— 不会唤醒模型,所以它待在输入框上方的「待发送」托盘里,直到你发它。每条托盘消息都有 **立即发送 / 编辑 / 撤销**。 > **和电脑端同步这件事,我没有实现 —— 因为它本来就不需要实现。** 这个队列就是 harness 的 `agent.inbox`,**电脑端 GUI 读的是同一个东西**,而它的每一次改动(插入/修改/删除)都是一条**持久的 session 事件**。我读写的就是那一份列表。 > > 如果我自己在插件里存一份队列再手动同步,第一次电脑端改了别的东西而手机看不见的时候,它就会开始漂。 - **编辑**是就地改,**保持你在队列里的位置**。 - **立即发送**会把它从队列里拿出来、重新以「唤醒」的方式送出去。 - 这段会话不在内存里时,排队会**明确报 409**,而不是悄悄塞进一个你看不见的队列。 ### 看它输出的时候:思考、复制、停止 - **思考(reasoning)** 会在回复**上方**出现一个**默认折叠**的块,标题写着「思考(正在输出)」,内容是**边想边流进来的**。它不展开也不会漏掉内容;这一轮结束标题就变回「思考」。 - 如果模型这一轮根本没思考(大部分简单问题),就**不会**出现空块 —— 不会假装它想了。 - **复制**:每条消息右上角有「复制」(整条消息,包含折叠的工具结果);**每个代码块自己也有一个**。手机上手动框选一段长代码很痛苦,所以给了按钮。 - ⚠️ 这个面板是 **http** 的,浏览器只在 **https/localhost** 才给 `navigator.clipboard`,所以手机上走的是旧式复制。**如果它没成功,按钮会显示「长按选择」而不是「已复制」** —— 宁可告诉你没复制成,也不让你粘出来是上一次的内容。 - **停止**:只在**正在输出时**出现。 - 它**默认保留你排队的话** —— 停的是「这次的输出」,跟「扔掉你已经打好的字」是两件事。 ### 右下角的按钮簇 点最右下那个圆按钮展开,里面是所有"次要操作": | 按钮 | 作用 | |---|---| | 🔒 **权限** | 切换本会话的权限档位(需要电脑上显示的验证码) | | 🧠 **模型** | 换模型,以及模型的**思考强度** | | 📊 **上下文** | 看这段会话的上下文占用,需要时**压缩上下文** | | 📄 **引用文件** | 把电脑上某个文件的路径插进输入框 | | ↑ / ↓ | 回到顶端 / 跳到最新(各自在已经到底/到顶时自动隐藏) | (agent 反问你问题时出现的**问答卡片**不走这个按钮簇 —— 它直接顶掉输入框,见下面。) 它会**自动让开输入框**(按输入框高度上移),不会盖住你打字的地方。 ### 问答卡片:agent 问你问题的时候 如果 agent 用了「问你一个问题」这个工具,**那一轮会停下来等你**。答案会出现在**输入框的位置**(输入框被卡片顶掉),因为这时候你的注意力本来就该在这里。 卡片上: - **选项**点一下就选中(可以多选的题会变成开关,能选好几个) - **每道题都能自己写**:底下那行「也可以自己写」直接打字 - **提交**:一次请求里的所有题必须全答完,按钮才会亮 —— 没答完会告诉你还剩几道 - 打字的时候**卡片不会被刷新**,键盘不会自己收回去(后台每 2 秒的轮询只在内容真的变了时才重画) > 手机上答和电脑上答是**抢**的关系:谁先答就算谁的,另一边会自动消失。 > **如果你在用 Chatbox,这个永远看不到** —— OpenAI 协议里根本没有「问一个问题」这种东西,那一轮会一直卡着。要用问答就用面板。 > > **就算问题不属于你正在看的这段会话,它也会冒出来** —— 卡片上会写「来自会话 xxx」,点一下就跳过去。这是故意的:审批不显示只是让你去电脑上答,而问题不显示会让**一整轮停在那里**,手机上却没有任何提示。 > > 电脑端那个「跳过」是一次**真实的回答**(会回一个空选择),所以它会正常结束那一轮 —— 不会出现「电脑跳过了、手机还一直等着」。 --- ## 4. 权限档位与验证码 默认每个会话是 **只读**(`read-only`):能读、能跑命令,但**写文件会被沙箱拦下**,转成一条审批。 想让它能写: 1. 点 🔒 → 选「工作区可写」→ 请求变更 2. **电脑上会弹出一个窗口**,显示这次请求和**一个 6 位验证码** 3. 手机上把这个验证码填进去 → 确认 → 档位生效 验证码**只在电脑上显示**,手机端的轮询接口永远拿不到它。所以拿着手机(或者只有那串 URL)的人,没法自己给自己放权 —— 必须有人在电脑前。 - 填错 5 次这次请求作废;3 分钟不填自动过期。 - **`完全访问`(danger-full-access)不能从手机改**,请到电脑上操作。这是故意的。 - 想每次都用你自己定的固定码?配置里设 `authorizationCode: '123456'`,弹窗就只做通知。 --- ## 5. 模型与思考强度 点 🧠 → 列出真实的供应商分组和模型。点一个模型: - 如果它声明了**思考强度**,会再让你选一档(关闭 / 低 / 高 / 最大),也可以选「适配器默认」。 - 如果没有可选强度,会直接换过去。 - 选完**按钮上会直接显示**当前强度(例如 `deepseek-flash · 高`),这样你能确认它真的生效了。 **不需要验证码** —— 换模型放不宽沙箱,而且随时能换回来。 改完**从下一步生效**,已经发出去的那一步不受影响。电脑端的模型显示也会跟着变。 > **一个真实限制**:如果某段会话**正被电脑端持有**(已加载、运行中),它的模型改不了 —— 手机端会明确告诉你,而不是假装成功。新开一段对话,或者到电脑上改。 --- ## 6. 上下文占用与压缩 点 📊。这是**电脑端输入框旁边那个圆环的手机版**,读的是同一套数据,所以两边不会出现两个数字。 - **`37%`** 这种就是占用比例,和电脑端浮层上的「上下文已用 37%」完全一致。 - **~372K / 1M** 是已用 / 这段路由的窗口容量。 - 下面三行是拆分:**系统提示词 / 工具定义 / 对话消息**(颜色对应的就是电脑端那三色)。 ### 「载入」是什么意思 占用只有**会话已经载入内存**时才能读到。手机点开 📊 时,如果这段会话还没载入,会显示「读取占用」按钮 —— 按一下才会载入它。 这么做是故意的:**光看一眼不该有副作用**。载入不会让它开始回复,也不会打断电脑上正在做的事。 ### 压缩上下文 按钮 → 「压缩上下文」→ 再点一次「确认压缩」执行(两步确认,避免误触)。 它调用的是**电脑端 `/compact` 完全同一个后端**,所以: - 较早的历史会被换成**一段摘要**,在电脑端显示为一条**检查点**。 - 原始记录仍留在会话日志里,但这段对话**可见的历史会变短**。 - 会话**正在回复时不能压缩**,会告诉你等一等 —— 这是后端自己的判断,不是手机端的限制。 压缩完占用数字会**当场刷新**,你能直接看到它掉下来。 > 明细那三行是**按 token 估算**的拆分,三项之和**可能和上面的占用对不上** —— 这跟电脑端一样。占用本身来自最近一次请求的实测值,按之后的增减推算。 --- ## 7. 「引用文件」和「上传」是两件完全不同的事 这一节值得仔细看,两者的安全含义**完全相反**。 ### 📄 引用文件(推荐) 在对话页面点 📄 → 浏览 → 点一个文件 → **它的路径被插进输入框**。 ``` 帮我看看 E:/dsh-workspace/remote/src/main.js 里的问题 ``` 你在**告诉 agent 去读哪个文件**。它随后用的是自己的工具,走**正常的沙箱和审批规则**。这条路上没有新的写盘口子。 ### ⬆ 上传(在 📁 文件视图里) 把手机上的文件**传到电脑磁盘**。 ⚠️ 这是一次**直接写入**,绕过 DSH 沙箱、绕过审批、不进审计日志。所以它被限制在**唯一一个目录**: ``` /_inbox/ ``` 也就是 `E:\dsh-workspace\remote\_inbox\`(`cwd` 是配置项)。面板写不了别的地方。 **知道这个洞存在,就好。** --- ## 8. 用 Chatbox 之类接进来 Chatbox:**设置 → 模型提供方 → 添加 → 添加自定义提供方 → "OpenAI API 兼容"** | 字段 | 填什么 | |---|---| | API 密钥 | 你的 token | | API 主机 | `http://<电脑的局域网IP>:19387/v1/` | | API 路径 | `/chat/completions` | | 模型 | 列表里选。`dsh-agent` = 开新对话;其余每一条 = 一个已有 DSH 会话 | **为什么模型列表里是一堆会话?** OpenAI 协议本身没有"会话"这个概念,客户端也不认。所以只能借用模型选择器来选会话 —— 这也是为什么你**能接着手机聊电脑上开的对话**。 > 自动按第一条消息归类会话:**两段对话如果第一句话一模一样,会被归到同一个会话**。想避免就显式选会话。 --- ## 9. 常用配置 在 profile 的 `cordis.patch.yml` 里改,**改完要重启 DSH**: | 键 | 现在的值 | 作用 | |---|---|---| | `token` | 长随机串 | 空 = 整个插件禁用 | | `cwd` | `E:\dsh-workspace\remote` | 手机驱动产生的会话在哪个工作区;`_inbox` 也在这儿 | | `fileRoot` | `'*'` | 浏览范围。`'*'` = 所有固定盘;也可以写单个路径或路径数组 | | `permissionPreset` | `read-only` | **新建**会话的初始档位(不会在后续轮次里被重置) | | `permissionPresets` | `[read-only, workspace-write]` | 手机**允许请求**的档位 | | `authorizationCode` | 空 | 空 = 每次随机码显示在电脑上;填了 = 用你的固定码 | | `desktopPopup` | `true` | 是否自动在电脑上弹出授权窗口 | | `approvalScope` | `all` | 面板能为哪些会话批准操作 | --- ## 10. 安全边界(诚实版) **它保护的是完整性,不是机密性。** DSH 的沙箱是**文件效果**策略。`read-only` 挡住写入,但**不挡**读取、不挡执行命令、不挡联网外发。拿着 token 的人读得到、传得走,只是改不了。 所以: - **token 就是钥匙。** 泄漏了就等于把上面这些全交出去。 - **链路是明文 HTTP。** 同一局域网里能看到 token 和所有内容。别在不可信网络上开。 - **`fileRoot: '*'` 的范围。** agent 本来读什么都可以,所以这没有扩大 *agent* 的能力 —— 但它扩大了**一个 token 从手机能枚举和下载的范围**。想收窄就改成路径列表: ```yaml fileRoot: ['E:\dsh-workspace', 'D:\projects'] ``` - **`_inbox` 是唯一的写盘口子**,在审批链之外。 网络层面:webserver 绑在 `0.0.0.0`,所以**整个 DSH GUI 也对局域网开放了**,不只是这个插件。防火墙至少要对专用网络放行 19387。 --- ## 11. 常见问题 **改了代码/配置没反应** `lib/*.js` 和 `cordis.patch.yml` 都要**重启 DSH** 才生效。看主机日志里有没有对应的 `openai-bridge:` 行。 **手机打不开** 1. 电脑上 `ipconfig` 确认 IP 没变(换网络就会变)。 2. 电脑上先自测:`http://127.0.0.1:19387/bridge/` 能开说明服务在跑。 3. 防火墙:要有 19387 的入站放行规则,且**配置文件类型**要匹配当前网络(专用/公用)。Windows 上网络被判成「公用」而规则只对「专用」生效,是常见的坑。 **显示"需要登录"** token 不匹配。可能是 token 轮换过 —— 用新 token 重新打开一次带 `?token=` 的地址。 **返回 404 说 `unknown panel route`** 你连的是旧代码。重启。 **换了模型但没生效** 看它是不是"正由电脑端持有"的会话 —— 见第 5 节。 **档位老是变回只读** 不该再发生了(早期版本每轮都会重钉档位,已修)。如果还有,请报告:从手机发一条消息,然后问电脑端那段会话现在是什么档位。