全部插件

DSH / BUNDLE / CLIENT-UI

dsh-overleaf

v0.3.10gychen-NJU / dsh-overleaf7fcf9ab130

可安装组合包UI 与客户端插件社区 · Topic 自动分析Web UI

概览

dsh-overleaf

Embedded Overleaf workbench tab for DeepSeek Harness Web: same-origin reverse proxy for self-hosted or public Overleaf (no X-Frame-Options limits), direct-CDP login with credential storage, selection quoting into the composer, agent-driven cursor insertion, and LaTeX workflow helpers

README / ZH

插件文档

dsh-overleaf

English | 中文

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.2 web 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-insertselection-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 资产路由

请求链路概述:

  1. 浏览器向 DSH 服务器请求 /overleaf-proxy/<path>?<query>
  2. 宿主 handler 重建头部(host 改写为上游、委托 Origin、cookie 与已存凭据合并、文本正文保持 identity 编码以便改写)。
  3. 上游响应流式转发;响应头调整:去 X-Frame-Options、从 CSP 移除 frame-ancestors、绝对重定向改挂到代理前缀下、Set-Cookie 的 Domain 属性剥离(Cookie 落为 host-only)。
  4. 不超过 4MB 的 text/html 缓冲一次处理:根相对的 href/src/action/poster/data-srcsrcset 加前缀,并在 <head> 后注入 <base href="/overleaf-proxy/"> 与桥接脚本。超大 HTML 及其他类型一律原样流式。
  5. WebSocket:真实 webserver 把精确升级路径分派给 TCP/TLS 隧道——向上游重放握手字节再双向逐字节拼接。

在被代理文档内,桥接脚本安装防御性包装(fetchXMLHttpRequest.openEventSourceWebSocket),让运行期新建的根相对 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 的方式登录:

  1. F12(或 Ctrl+Shift+J)打开 Web 工具控制台 → Application(应用程序) 页签;
  2. Cookie 面板中找到 overleaf_session2 的值,从它的 Cookie Value 中复制完整值;
  3. 复制 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 可信,Secure Cookie 可经 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.2 web 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 直接忽略。

致谢

许可证

MIT

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 剥离日志。