dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
把 DeepSeek Harness 的 AI 编码控制台变成泰拉瑞亚像素世界——向导陪你写代码,真实对话、工具审批、难度切换,单文件零依赖。
把 DeepSeek Harness 的 Web UI 换成一整块泰拉瑞亚(Terraria)像素世界——向导陪你写代码。
English Version
dsh_theme_terraria(npm 包名 dsh-theme-terraria)是 DeepSeek Harness(dsh)的泰拉瑞亚主题插件。它用一个自包含的 index.html(约 98 KB,零外部 JS 依赖)替换官方 React 前端,把「AI 编码代理控制台」包装成一场像素冒险:
index.js 导出插件规范硬性要求的 apply(ctx),cordis.patch.yml 声明 dsh.bundle 补丁层,一条 dsh plugin --profile web add dsh-theme-terraria 即可安装/卸载,不改动 dsh 安装中的任何文件。官方 UI 概念 · 泰拉瑞亚主题映射 Agent Preset(预设) · 难度档:旅途 / 软核 / 中核 / 硬核 会话(Session) · 旅途 / 冒险 用户消息 · 冒险者 工具审批请求 · 审批石板 任务清单(todo) · 任务清单 HUD API 密钥状态 · 生命水晶指示灯 插件组合 · 随难度更换的装备
dsh --version 可用^22.19 || >=24 与 pnpm(dsh plugin 内部转发给 pnpm 管理依赖)# 1. 把主题作为插件安装进 web profile
dsh plugin --profile web add dsh-theme-terraria
# 2. 启动 Web 服务
dsh web
# 默认地址:http://127.0.0.1:3080/dsh plugin add 会把 dsh-theme-terraria 写入 web profile 的依赖与 dsh.profile.bundles 层列表;本插件的 cordis.patch.yml 补丁层停用官方 web-runtime 行并插入同构的 terraria-theme 行,插件入口 index.js 导出的 apply(ctx) 再把官方 @deepseek-ai/dsh-web-app 的前端解析器指向本包的 web/index.html——除换皮外,官方 Web 运行时的全部行为(dist 伺服、/api 信任边界、DSH_WEB_URL、URL 打印)保持不变。
# 从 GitHub 仓库安装
dsh plugin --profile web add github:10086ggqq/dsh_theme_terraria
# 或从本地检出的本仓库安装
dsh plugin --profile web add /path/to/dsh_theme_terraria在 deepseek-harness 仓库内以源码方式运行 dsh 时,上述命令加pnpm前缀(如pnpm dsh plugin --profile web add ...、pnpm dsh web)。
桌面应用窗口有两个硬限制:主窗口加载的是 dsh-app://app/,而这个协议把 /、/index.html、 /assets/*、/favicon.svg、/manifest.webmanifest 直接映射到应用内置的官方前端, 宿主服务出来的 dist 在窗口里永远不会被请求;只有其余路径才会带上宿主 cookie 转发给宿主。
本插件因此走两条路:
/terraria/theme.html(就是 web/index.html),然后把窗口导航到该地址 —— 桌面窗口里跑的就是网页端那同一份 UI:标题屏、向导对话、 人物创建、任务 HUD、8-bit 音效、壁纸与桌宠全部一致。/api/* 仍由桌面壳带 cookie 转发, WebSocket 连宿主 origin(地址由宿主经 __DSH_TRANSPORT__.streamBaseUrl 提供)。
注入式像素皮肤(skin/terraria.css 覆盖官方 --dsw-* 令牌与通用元素样式),至少不是原样。
桌面壳专属适配:
--dsw-* 令牌,给窗口控制按钮那一条与原生菜单配色;(navigator.windowControlsOverlay 的 titlebar-area,拿不到时按 138 × DPR 估算), 让「对话 / 终端 / 音效 / 音乐 / 设置」整体左移,而不是把整个界面往下压;
-webkit-app-region: drag,其中的按钮显式 no-drag,所以拖得动窗口也点得动按钮(早期用整条 fixed 拖拽带,会把顶栏按钮的点击全部吞掉);
#modal-root)显式 no-drag,并且打开期间给 `` 挂data-terraria-modal 把底下的拖拽区整体撤掉 —— 标题屏那条整屏 drag 区会把画在它上面的浮层点击 全部吃掉(「继续会话」「人物桌宠」的「关闭」点了没反应就是这个);弹窗还可以用 Esc、 点背景或点「关闭」退出。
宿主侧 DSH_TERRARIA_STANDALONE=0 关接管,DSH_TERRARIA_SKIN=0 连兜底皮肤也关。
localStorage['terraria.takeover'] = '0' 并一直生效,所以回到官方界面后,右下角会挂一颗「🌳 泰拉瑞亚主题」像素按钮(也可以按 Alt+T), 点它清掉那个标记并重新接管。每次决策的结论都写在 localStorage['terraria.inject'] 里,界面不对时先看它。
sk-...,点「保存密钥」;credentials.set 存到宿主机(.dsh-home/.credentials.yaml),页面本身不持久保存明文;坑 · 现象 · 解法 桌面应用窗口里看不到主题 · 插件显示「运行中」,应用窗口仍是官方界面 · 桌面窗口不使用宿主服务的 dist,0.2.0 起改由注入层把窗口接管到主题页;注入行是宿主启动时的一次性快照,必须完整退出应用再重启(只刷新窗口或重载插件无效)。接管没生效时会退回「官方界面 + 像素皮肤」 桌面窗口里是「黄黄的官方界面」而不是泰拉瑞亚界面 · 有像素配色/像素字体,但布局还是官方那套(侧栏「插件 / 自动化任务」、中间「探索未至之境」),重启也不变 · 不是渲染坏了:这是接管被关掉的状态。localStorage['terraria.takeover'] = '0'(主题页「切回官方界面」/ Alt+O 写入)会一直生效。点右下角「🌳 泰拉瑞亚主题」或按 Alt+T 即可回去;也可以 F12 执行 localStorage.removeItem('terraria.takeover'); location.reload()。死循环时看 localStorage['terraria.inject'] 里记的那一步 桌面窗口看不出主题、连接一直转圈 · 停在主题页但连不上宿主 · 主题页标题屏点「切回官方界面」或按 Alt+O 先回到可用界面,再把控制台报错发出来 浏览器缓存旧 UI · 更新插件后页面还是旧样子 · 强制刷新 Ctrl+F5,或地址加 ?v=2 绕过缓存 dsh plugin add 失败 · 报 pnpm not found on PATH 或 pnpm 错误 · dsh plugin 内部转发给 pnpm:安装 pnpm 并重开终端;git 方式安装的报错按提示在 profile 的 pnpm-workspace.yaml 补 allowBuilds 后重试 密钥保存失败 · toast 报「保存失败」 · 确认 dsh web 由本机终端启动;沙箱化环境可能拦截宿主目录写入 图片壁纸存不下 · 「图片过大,保存失败」 · localStorage 上限约 5 MB,主题已自动把非 GIF 图片压缩到 1920px JPEG;仍失败请换更小的图或用视频壁纸 文件夹选择器拿不到完整路径 · 选择文件夹后只有文件夹名 · 浏览器安全限制(所有网页一致),主题会用「使用 E:\完整\路径」按钮智能回填已注册的同名目录,否则提示手动补全;桌面应用里没有这个限制:直接调 preload 的原生目录选择器,返回的就是完整路径 顶栏的「音效 / 音乐 / 设置」被窗口控制按钮压住 · 桌面窗口右上角的最小化/最大化/关闭盖住按钮,且点不动 · 顶栏按 titlebar-area 实测宽度右侧留白让按钮左移;拖动区改成顶栏 + 标题屏的 -webkit-app-region(按钮 no-drag),不再用整条拖拽带吞点击。改完需重载页面(见下一条) 弹窗里的按钮(「关闭」等)点了没反应 · 桌面窗口停在标题屏时,整屏 drag 区把浮层的点击全吃掉 · 浮层 #modal-root 显式 no-drag,弹窗打开期间再用 data-terraria-modal 撤掉底下的拖拽区;Esc 也能关弹窗。改完需重载页面(见下一条) 改了主题页看不到效果 · 桌面应用里没有刷新快捷键(Windows 上应用菜单只有 F12 开发者工具) · 设置弹窗里点「重新载入界面」,或 F12 后在控制台执行 location.reload();注入行变更才需要完整重启应用 WebSocket 断线 · 顶部提示「连接已断开,重连中…」 · 属正常自动重连(2 秒间隔);宿主进程退出则需重启 dsh web
整个 UI 的视觉基调由三层构成:
forest.png)+ 标题屏大树(Tree.png),image-rendering: pixelated 保持像素锐利;支持用户更换(见壁纸系统)。#181e58,内板 #2e3692)、3 px 深色描边(#0e1340)、8 px 圆角、宋体加粗(SimSun/Songti SC)。--gold)标题与高亮、火把橙(--torch)分区标题、羊皮纸白(--parchment)正文,配深色投影 text-shadow: 1px 1px 0 var(--black)。woff2 约 600 KB,内嵌于 web/ 目录,中文渲染无锯齿;游戏主菜单复刻:logo、五个像素菜单项(新的会话 / 继续会话 / 插件管理 / 设置 / 人物桌宠)、森林背景上的大树剪影。
dsh_theme_terraria;会话没挂工作空间时退回模式名)、连接状态灯(「已连接到向导世界」)、对话/终端双标签切换、音效开关、音乐开关、插件管理、设置入口。模型名只在 HUD「当前模型」里显示,顶栏不再重复挂一个徽章;xiangdao.png)与红心印记(heart.png),右侧输入框与七个像素按钮:附件、模式选择、工作空间、权限设置、模型选择、停止、发送。一个回合只留一行「过程」+ 一条结果回复:思考原文、工具调用、中间步骤正文(「Now the endpoint mapping:」这类过渡话)全部收进可折叠的过程块——运行中展开看得见进度,回合结束自动收起成一行摘要(如「过程 · 已思考 1.1k 字 · 7 次工具 · 3 段中间说明 · 24s」)。于是聊天流里只有你的消息、每个回合一行过程摘要和结果气泡;没有正文的步骤不留任何气泡(以前会留下只有一行 token 统计或「已思考 · N 字」的砖块);outputTokens 都在这里累计)。session/follow 的 snapshot 只是一窗(实测约 300 条、可能只含最近一次提问),所以 loadHistory() 接着用 session/page(throughSeq 含 / beforeSeq 不含)自动往前翻 4 页;翻不完的在消息区顶部与导航面板留「⬆ 加载更早的历史」按钮(一次最多再翻 12 页 / 6000 条)。加载后按时间序重排,并按高度差补偿保持原来的阅读位置;pre-wrap。输入框上方的草稿附件栏 + 三条入口,走的是官方 UI 同一套协议:
{ type:'image', mediaType, data, name } 内联送出,其它格式的位图先用 canvas 转 PNG;草稿栏显示 56px 缩略图;
POST /api/session/uploadFileBinary?sessionId=&name=(application/octet-stream)拿到 receiptId,再以 { type:'file', receiptId } 送出;草稿栏显示文件卡(名字 + 大小 + 上传状态);
点击「模式选择」弹出泰拉瑞亚人物创建风格弹窗(这是全主题最还原的一处):
Style_1_male_walking.gif)+ 「模式选择」标题;#8a4b7c)、软核(青 #388e8d)、中核(棕 #855526)、硬核(红 #8a3a3b);#6be1d8)+ 内侧微光描边;scale(0.96) 缩放反馈;agentPreset.select 切换后端预设;顶栏会话名显示当前工作空间名。标题屏「继续会话」按文件夹树排会话(session/list + workspace/follow 两份数据对齐):
dsh_theme_terraria)+ 完整路径 + 会话数,点一下折叠 / 展开(▾ / ▸);运行中 标记与工作目录;workspace/archiveSession(正在运行的会话会一并停止它的运行);归档后的会话不再出现在工作空间树下,而是收进底部「已归档」父节点,那里每行是「取消归档」(workspace/unarchiveSession),放回原工作空间;origin === 'subagent' 或带 parentSessionId 的子智能体会话不入列,它们的父对话归档时一起进了归档集,之前正是它们把这一栏撑成一长串「第 x 个对话」;workspace/follow 的 sessionIds 为准,cwd 命中工作空间路径时兜底补上;两份数据都没覆盖到的会话按 cwd 尾段各自成组,没有 cwd 的落到「未归入工作空间」;children.length > 0 才建组),「删完 / 归档完父对话也就消失」由此成立;DSH 官方接口没有「删除单个会话」,所以这里不提供删除按钮——归档就是收起对话的入口。设置弹窗分四区(原来「插件」区已独立成顶栏的「插件管理」按钮):
#set-role / #set-role-detail):显示当前用哪种身份在跑——「DeepSeek 账号 · 已登录 · 」或「API 密钥 · 未登录 DeepSeek 账号,用密钥直连」,账号登录时补充一句「凭据由宿主保管并自动用于模型调用,无需再填写 API 密钥」。数据来自同源 GET /terraria/account(宿主侧主题插件问账号服务 getState / getProfile,只投影昵称、联系方式与头像)。账号登录时这一行右侧还有「退出登录」按钮(#set-signout,btn-danger 小按钮):点击先弹确认框(弹框前查 account/hasRunningAccountTasks,有任务在跑就写明会打断、查不到就按未知措辞),确认后走宿主的 account/signOut(客户端身份 {version, locale, timezoneOffsetSeconds},与官方前端同一端点)——宿主删掉本机账号凭据、在后台向 Platform 吊销授权,API 密钥不受影响;退出后页面轮询 /terraria/account 直到不再是账号登录,角色与 HUD 状态灯就地刷新。密钥模式下按钮隐藏(没有可退的登录)。这个按钮只属于设置弹窗,对话页(HUD / 顶栏)不显示它;credentials.set 存宿主;窄屏下按钮自动换行不遮挡输入框;顶栏「插件管理」(标题屏菜单里也有)打开插件弹窗,走宿主 pluginManager 服务:
registry.npmjs.org/-/v1/search,带 CORS),dsh 相关的结果自动排前面;空关键词按 keywords:dsh-plugin 搜;搜索不可用时提示直接粘贴包名;__DSH_HOST_PATHS__.pathFor)、也可以把文件夹拖进这一行;支持宿主的全部 spec 形态——绝对路径(D:\code\my-plugin,目录里要有 package.json)、*.tgz(本地或 URL)、git 仓库(github:你/仓库、https://github.com/你/仓库、git+https://…、git@host:path)、registry 包名(可带 @版本)。本地路径必须是绝对路径:file:///D:/… 与相对路径都会被宿主拒绝(实测 host 报 not-a-package: the path does not exist / invalid-spec: a local path must be absolute);pluginManager/inspect 先解析 spec(包名 / npm 别名 / git 地址 / 本地路径 / tarball),显示它是不是 bundle、版本与描述;被拒绝时给出 problem 与原因(如 not-found + registry 报错原文、already-installed);pluginManager/installBundle(带客户端生成的 requestId,enabled: true),结果按 application 区分 applied / restart-required / failed,失败时附错误码、诊断与 pnpm 输出尾部;安装日志区也会收集宿主推来的 plugin-manager/install-log;pluginManager/listBundles 列出全部 bundle,按来源分成两组,两组都能折叠——个人(写进当前 profile 依赖、自己装的,可停用 / 卸载)默认展开,官方(随 dsh 安装提供,只能停用)默认收起(个人为空时自动展开);每行可启用 / 停用(setBundleEnabled),可卸载的还能卸载(removeBundle);双击任意一行进入插件详情页;management-required 受保护)、可选 bundle 标记、错误码与描述;下面是「包含 N 个插件」(逐条列模块名、中文说明、是否运行中)和「原始信息」(bundle 的 JSON)——两块都默认折叠,点标题才展开。详情页里也能直接启用 / 停用 / 卸载,改完就地刷新;插件被卸载则自动退回列表;pluginManager/registries 解析出来的源(本机是 registry.npmjs.org + 回退 registry.npmmirror.com);搜索查的是 npm registry 的索引,不是 GitHub。所以:
pluginManager/inspect 会直接告诉你 not-found);@you/pkg)经常要靠精确名字才搜得到。装法不受影响:把仓库地址填进「本地 / Git 来源安装」即可(https://github.com/你/仓库、github:你/仓库, 实测 inspect 返回 {status:"accepted",kind:"git",host:"github.com"});本地检出目录填绝对路径, 打包好的填 *.tgz 路径。pnpm 会自己 clone / 解包 / 装依赖。
__DSH_DIRECTORY_PICKER__)、拖入的文件夹用 __DSH_HOST_PATHS__.pathFor(file) 取真实宿主路径,两者都一步拿到完整路径; 浏览器里没有这两个能力,退回 webkitdirectory + 「使用 E:\完整\路径」智能回填已注册同名目录。