概览
dsh-overleaf
README / ZH
插件文档
dsh-overleaf
DeepSeek Harness(DSH)Web 的 Overleaf 嵌入工作台插件。它在会话页顶部的 对话 / 轨迹 / 上下文 旁新增第四个选项:通过同源反向代理加载你的 Overleaf 站点(公有云或自托管,如 https://tex.nju.edu.cn),页面完整可操作——编辑、编译、PDF 预览全部保留——页面下方仍是原生 DSH 对话输入框;选区引用、光标处写入、LaTeX 辅助面板在两者之间打通。
+--------------------------------------------------------------+
| 会话页选项条: 对话 | 轨迹 | 上下文 | [Overleaf] |
+--------------------------------------------------------------+
| 工具栏: 刷新 / 新窗口 / 登录 / Cookie / 辅助面板 |
| +----------------------------------------------------------+ |
| | https://.../overleaf-proxy/...(同源 iframe) | |
| | Overleaf 编辑器、编译器、PDF 预览全部可用 | |
| +----------------------------------------------------------+ |
| 选区浮动「引用」气泡 —— 选区桥 |
| 状态条 / 辅助面板(AI 写入、选区 AI、文档大纲……) |
+--------------------------------------------------------------+
| DSH composer(原生,未做任何改动) |
+--------------------------------------------------------------+
- 许可证:MIT
- 目标运行时:DeepSeek Harness
0.1.1-rc.2web profile(http://127.0.0.1:3080) - 协议:附带符合 dsh-std 互操作规范的静态 Community v0.15
dsh-plugin.json清单;经典双半区 bundle 加载仍是主激活路径。
界面预览
运行在 DSH 会话页中的 Overleaf 编辑器——完整编辑器与项目文件树,下方保留 DSH 对话输入框:
分屏视图:LaTeX 源码与 PDF 预览并排,配合 Recompile——编译、预览、迭代都不离开对话页:
辅助面板——三大工具并排:AI 写入与自动捕获插入、选区 AI(审阅后替换原选区)、编译修复(读取日志错误/警告并一键智能体修复):
[ ai-insert ]——用自然语言描述需求,DSH 智能体完成写作后,面板会自动捕获智能体的产出并填入"自定义内容"框——检查后一键插入编辑器光标处。[ selection-ai ]——在编辑器中选中文字,让智能体解释或改写,审阅替换内容后替换原选区;原文/选区漂移保护默认开启,也可取消勾选后按保存的原锚点位置强制替换(切换文件仍会拒绝)。[ compile-fix ]——Recompile 后读取编译日志中的错误与警告,让智能体针对当前打开的文档提出自动修复。- 本地
.bib同步——ai-insert与selection-ai面板都提供“更新 Overleaf .bib”:默认递归检测当前 DSH 工作区中的.bib,也可填写工作区内的绝对/相对路径;点击后按同名文件更新 Overleaf,并等待自动保存确认。 - 当前
.tex双向同步——辅助面板“状态”页默认把当前 Overleaf 源码同步到工作区:递归检测本地.tex,没有候选时在工作区根目录新建同名文件;也可选择本地路径。切换为“本地 → Overleaf”后,可读取本地文件或使用手动粘贴的完整 LaTeX 内容,但必须先确认整篇覆盖警告,并通过文档 ID、内容版本、修改前快照和保存事件校验。
为什么需要它
Overleaf 的每个响应都带 X-Frame-Options / CSP frame-ancestors,直接 <iframe src="https://tex.nju.edu.cn"> 会被浏览器拒绝。本插件在 DSH 宿主进程内实现了一个 HTTP/1.1 反向代理:浏览器所有请求走 /overleaf-proxy/<原路径> 再转发到你配置的上游站点——上游被锁定为唯一配置来源,没有开放 SSRF 面。iframe 与 GUI 同源之后,浏览器级桥接才成为可能:嵌入编辑器里的文本选区可以结构化地进入对话框,生成的内容也可以写回编辑器光标处。
功能与验收对照
| 需求 | 实现状态 |
|---|---|
| R1 · 会话页第 4 个选项 | conversation.view 条目 id:"overleaf"、order:30;官方 tab 条可见时即可见(tabs >= 2) |
| R2 · 可配置地址 | 设置页(设置 > 插件 > 插件配置 > dsh-overleaf)修改 baseUrl;保存后热切换代理目标,无需重启 |
| R3 · 原站功能可用 | 流式反向代理保留路径与查询串;响应除取景限制头外透传;小幅 HTML 正文做链接/资源重定基并注入桥接脚本 |
| R4 · 底部原生输入框 | 视图只替换消息区域;composer、工作区记录、交付物一概不动 |
| R5 · 选区引用 | iframe 内 selectionchange 浮出引用按钮;点击经官方引用管线写入 chip(inputTriggers.registerSource({name:'quote-ref'}) codec),管线缺失时退化为纯文本块引用 |
| R6 · 光标处生成 | 模板插入(section/subsection/figure/table/equation/BibTeX)与自由粘贴通过 CodeMirror API 写入实时光标(CM5 主通道,CM6 探测,可编辑兜底)。自动写入模型回复列入后续计划;按任务书要求注明所属 lane |
| R7 · 辅助功能 | 辅助面板:AI 写入;针对编辑器选区向智能体提问或生成可审阅的替换内容(默认安全校验,可显式切换强制模式);本地 .bib 安全同步到 Overleaf 同名文档;当前 Overleaf .tex 与工作区 .tex 双向同步(默认拉取到本地,反向整篇覆盖需显式确认和版本校验);读取编译日志并让智能体自动修复错误/警告;文档大纲跳转;登录/登出/Cookie 管理;状态上报 |
安装
npm 包名 dsh-overleaf 已被另一项目占用,因此本插件不发布 npm——请直接从 GitHub 安装。仓库已提交预构建的 lib/ 产物,git 安装无需构建步骤、无需 allowBuilds 授权:
# 从 GitHub 安装,跟踪 main 分支(推荐,跟随最新特性):
dsh plugin --profile web add github:gychen-NJU/dsh-overleaf
# 或锁定某个已发布的版本:
dsh plugin --profile web add github:gychen-NJU/dsh-overleaf#v0.3.9
从 release 资产安装(在 Releases 下载 .tgz):
dsh plugin --profile web add ./dsh-overleaf-0.3.9.tgz
随后重启一次 web 服务(客户端 bundle 在启动期进入 boot 图谱):
dsh --profile web web # 用你平时的方式启动即可
确认组合仍然成立:
dsh --profile web --dump-config # 应看到 "# == dsh-overleaf" 配置块
默认上游为 https://www.overleaf.com。要接入其它实例(自托管 Overleaf、tex.nju.edu.cn 等),打开 设置 > 插件 > 插件配置 > dsh-overleaf,修改 baseUrl 并保存——代理目标即刻热切换,无需重启。
随时可以干净卸载:
dsh plugin --profile web remove dsh-overleaf
共存保证
刻意避开已知插件的每一条命名面:
| 面 | dsh-overleaf 占用 | 其他插件已有占用 |
|---|---|---|
| Cordis 行 id | overleaf-workbench |
overleaf(better-overleaf) |
| 客户端模块 id | dsh-overleaf(必须等于包名) |
dsh-better-overleaf |
| HTTP 路由 | /overleaf-proxy/*、/overleaf/workbench/* |
/overleaf/*(better-overleaf)、/api/dsh-browser/* |
| WS 升级 | /overleaf-proxy/socket.io[/] 精确匹配 |
无已知 |
| 凭据 ref | OVERLEAF_WORKBENCH_COOKIE |
OVERLEAF_COOKIE / OVERLEAF_GIT_TOKEN |
| 数据目录 | ~/.dsh/plugin-data/dsh-overleaf-workbench/browser-profile |
~/.dsh/plugin-data/dsh-overleaf/... |
| 会话视图 id | overleaf,order 30 |
chat 0 / trajectory 10 / context 20 |
两侧全部软失败:缺 credentials 服务则停用凭据存储(每次请求退化为手动粘贴模式)、缺 settings 则跳过设置卡;客户端任何异常只打日志不抛出,绝不阻塞 GUI 启动。
架构
src/
index.ts 宿主侧统一导出 + 默认 Service 类(cordis loader 目标)
service.ts 路由、status/login/projects 操作、settings 命名空间接线
config.ts schemastery schema + 默认值 + origin 归一化
proxy.ts ReverseProxy:流式 HTTP 反代 + 原始升级隧道
inject-script.ts 浏览器桥接脚本 bridge.js 的源头
login-cdp.ts 直连 CDP 登录(移植自 Hoemr/dsh-better-overleaf, MIT)
credentials.ts OVERLEAF_WORKBENCH_COOKIE credentialRef
types.ts wire 类型
client/
index.ts 客户端 apply(): 字典、quote-ref source、视图槽位、
设置卡槽位(对 settingsScope 软等待)
view.tsx OverleafView 组件(工具栏/iframe/CTA/面板/对话框)
workbench.ts 根 ctx 捕获、引用注册表、composer 写入辅助
settings-card.tsx 按 'dsh-overleaf' 命名空间的暂存式设置表单
locales.ts zh/en 平铺字典(zh 为 key 源)
scripts/
smoke-offline.mjs 假上下文夹具:路由普查、JSON 流程、HTML 重写断言、
cookie/logout 生命周期、client factory 物化 + stub 服务上的
apply()
smoke-live.mjs 以真实 @deepseek-ai/dsh-host-webserver 起 OS 随机端口,
指向本地 fixture 上游,验证重定基/注入、Set-Cookie 收域、
二进制流、JSON 路由、真实 RFC6455 隧道往返、bridge.js 资产路由
请求链路概述:
- 浏览器向 DSH 服务器请求
/overleaf-proxy/<path>?<query>。 - 宿主 handler 重建头部(host 改写为上游、委托
Origin、cookie 与已存凭据合并、文本正文保持 identity 编码以便改写)。 - 上游响应流式转发;响应头调整:去
X-Frame-Options、从 CSP 移除frame-ancestors、绝对重定向改挂到代理前缀下、Set-Cookie的 Domain 属性剥离(Cookie 落为 host-only)。 - 不超过 4MB 的
text/html缓冲一次处理:根相对的href/src/action/poster/data-src与srcset加前缀,并在<head>后注入<base href="/overleaf-proxy/">与桥接脚本。超大 HTML 及其他类型一律原样流式。 - WebSocket:真实 webserver 把精确升级路径分派给 TCP/TLS 隧道——向上游重放握手字节再双向逐字节拼接。
在被代理文档内,桥接脚本安装防御性包装(fetch、XMLHttpRequest.open、EventSource、WebSocket),让运行期新建的根相对 URL 也落回前缀;通过 CodeMirror API 上报选区及安全锚点;监测新编译/缓存响应,并复用 Overleaf 原生日志请求抓取本次构建的 output.log/.blg 供自动修复面板使用;暴露光标写入、默认带冲突检测且可显式强制的选区替换、大纲和跳转命令;并在每次变更前保存 localStorage 快照供回滚。
登录
两条路径共用同一凭据库:
- 直连 CDP 抓取(推荐):插件用你选择的 Chromium 系浏览器(
auto自动发现默认浏览器与已装 Chromium;可指定渠道或路径)以独立配置目录(~/.dsh/plugin-data/dsh-overleaf-workbench/browser-profile)加预留 loopback 调试端口启动。登录一次并保持窗口打开;插件轮询Storage.getCookies/Network.getAllCookies,直到 (1) 配置主机名下出现至少一个非偏好类 Cookie,(2) 有页面停留在该站点且不在登录/SSO 页,(3) 组装出的 Cookie 头通过宽容的服务端校验。因此它同时兼容标准 Overleaf 与 TeXPage 系部署(如tex.nju.edu.cn,其会话 Cookie 名完全不同)。提前关闭登录窗口会立即中止抓取——此时改用粘贴 Cookie。登录请求立即返回、视图轮询进度,工具栏不会卡死。 - 手动粘贴:DevTools 复制整行 Cookie 经工具栏对话框粘贴入库;保存前以 redirect-manual GET 校验
<baseUrl>/project。
国内网络登录 www.overleaf.com 时,反向代理内嵌页面可能被 Google reCAPTCHA 阻塞(见下方 CAPTCHA 说明),此时推荐使用复制 Cookie 的方式登录:
F12(或Ctrl+Shift+J)打开 Web 工具控制台 → Application(应用程序) 页签;- 在 Cookie 面板中找到
overleaf_session2的值,从它的 Cookie Value 中复制完整值; - 复制 Cookie Value,并补齐为
overleaf_session2=<Cookie Value>的格式后粘贴入库。
CAPTCHA 说明(国内网络登录 www.overleaf.com)
Overleaf 登录使用 Google reCAPTCHA(资源在 google.com / gstatic.com)。两个后果:
- 内嵌页面永远无法完成登录——reCAPTCHA 站点密钥锁死 www.overleaf.com 域名,在回环代理源下必然失败。内嵌页出现登录表单时视图会给出提示,引导改用弹窗/粘贴 Cookie。
- CDP 弹窗需要能直连 Google。国内网络请在 设置 > 插件 > 插件配置 > dsh-overleaf 里把
loginProxyServer设为代理客户端 HTTP 端口(Clash 典型为http://127.0.0.1:7890,纯端口号亦可),登录浏览器会以--proxy-server启动;留空则用系统默认。不可达时就会看到 "captcha not available"。
Cookie 值从不进入插件 config、路由返回值、日志或客户端存储。
安全模型
- 所有插件路由 socket 级 loopback 围栏(
127.0.0.1/::1);非回环调用者在读取请求体之前就被 403。 - 代理目标锁定单一配置 origin——URL 解析拒绝协议/路径/主机覆盖,不存在开放中继。
- 取景保护只对这一个用户主动选择的上游放宽,且只服务于刻意请求它的 loopback 客户端;其余头全部保留。
- 请像对待任何能持有你 LaTeX 账号会话的工具一样对待
baseUrl:只有当你的工作站本身就是信任边界时才接入内网实例。 - Cookie 只上行转发、从不出现在 API 返回里;logout 立即清除存储的凭据。
- 嵌入页与 GUI 同源,上游脚本也在其中运行——请自行审查所嵌入的内容。
已知限制
- Cookie 带上游标志位:现代 Chrome/Firefox/Edge 认为 loopback 可信,
SecureCookie 可经http://127.0.0.1:3080下发;老浏览器可能丢弃(宿主侧注入不受影响)。 - WS 精确匹配要求客户端访问
/overleaf-proxy/socket.io[/]:桥接包装会改写标准路径;绕过这些路径的特殊传输将退化到轮询。 - 某些纯客户端框架计算的 URL 依赖包装与
<base>兜底;若站点开启无关路径的外连通道需自行补路由规则。 - CM6 支持依赖常见句柄探测;若 Overleaf 完成 CM6 迁移且内部句柄不同,模板插入退化为可编辑焦点兜底。
- 公有云个别项目页可能出现空白或重复渲染,需要按实例版本微调重定基规则;在宿主侧设
DSH_OVERLEAF_DEBUG=1可打印 CSP 剥离日志。
开发
pnpm install
pnpm build # tsc -b(类型 + 可运行 ESM/CJS 发射物)再 tsdown
pnpm test # smoke-offline.mjs + smoke-live.mjs(无需起 DSH 实例)
pnpm typecheck
构建产物为 lib/index.js(node 半区,ESM)与 lib/client.js(浏览器半区,惰性 CJS 闭包,经 window.__ModuleLoader__.load({ id:'dsh-overleaf', factory }) 注册——模块 id 必须等于 npm 包名,dsh-client-modules 就是按包名匹配每个 /plugins/<pkg>/client.js bundle 的注册)。客户端 bundle 只允许 require 平台种子表中的 React(含 jsx-runtime),其余全部内联——纯度门与社区惯例一致。
要在真实 profile 里试用本地改动:打包 tarball 后 add、重启 web 服务,然后同时观察外壳与 iframe 两份 DevTools 控制台中 [dsh-overleaf] 前缀日志。
兼容性说明
- 针对 DSH
0.1.1-rc.2web profile 验证;peer 区间接受宿主服务>=0.1.0-rc.5、cordis^4.0.1。 - Node
^22.19 || >=24。 - 可与
dsh-better-sidebar/dsh-better-overleaf/dsh-context/ paperlab 等并存;见共存表。 dsh-plugin.json遵循 dsh-std Community v0.15;实现了@dsh-std/adapter-dsh的 Host 可静态发现该清单,普通 profile 直接忽略。
致谢
- Hoemr/dsh-better-overleaf(MIT):直连 CDP 登录设计,已在
login-cdp.ts中适配(域名参数化过滤 + 专属配置目录)。 - Nono-neko/dsh-browser:验证了 DSH 上同源反向代理 + loopback 围栏模式。
- wangwei-wade/dsh-quote-annotate:建立了引用 chip 的插入/序列化流程,本插件将其扩展到代理页面。
- Yan-Zero/dsh-std:本包遵循的静态清单协议。
许可证
LIMITATIONS
已知限制
- Cookie 带上游标志位:现代 Chrome/Firefox/Edge 认为 loopback 可信,`Secure` Cookie 可经 `http://127.0.0.1:3080` 下发;老浏览器可能丢弃(宿主侧注入不受影响)。 - WS 精确匹配要求客户端访问 `/overleaf-proxy/socket.io[/]`:桥接包装会改写标准路径;绕过这些路径的特殊传输将退化到轮询。 - 某些纯客户端框架计算的 URL 依赖包装与 `<base>` 兜底;若站点开启无关路径的外连通道需自行补路由规则。 - CM6 支持依赖常见句柄探测;若 Overleaf 完成 CM6 迁移且内部句柄不同,模板插入退化为可编辑焦点兜底。 - 公有云个别项目页可能出现空白或重复渲染,需要按实例版本微调重定基规则;在宿主侧设 `DSH_OVERLEAF_DEBUG=1` 可打印 CSP 剥离日志。
