🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话管家 mikufan39/dsh-mikufan

DeepSeek Harness
mikufan3912 天前有提交已提供中文文档扫描:含敏感能力

集中管理会话删除、开机动画与局域网访问等六项功能

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/24 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add mikufan39/dsh-mikufan
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么[目录]—
dsh 原生插件 · market
暂无时间戳·判据 →
装得上吗[实装验证]—
本站尚未做安装检查
暂无时间戳·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 12 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 72 处,展示前 62 条)
  • 读取本地文件
    index.js:216
    await stat(path);
  • 读取本地文件
    index.js:247
    text = await readFile(this.file, 'utf8');
  • 写入本地文件
    index.js:299
    await mkdir(dirname(this.file), { recursive: true });
  • 写入本地文件
    index.js:301
    await writeFile(temporary, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
  • 写入本地文件
    index.js:302
    await rename(temporary, this.file);
  • 读取本地文件
    index.js:345
    text = readFileSync(this.file, 'utf8');
  • 写入本地文件
    index.js:407
    await mkdir(dirname(this.file), { recursive: true });
  • 写入本地文件
    index.js:409
    await writeFile(temporary, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
  • 写入本地文件
    index.js:410
    await rename(temporary, this.file);
  • 读取本地文件
    index.js:436
    entries = await readdir(sessionsRoot, { withFileTypes: true });
  • 删除本地文件递归
    index.js:446
    await rm(candidate, { recursive: true, force: true, maxRetries: 3 });
  • 删除本地文件
    index.js:464
    await rm(candidate, { force: true });
  • 读取本地文件
    index.js:520
    text = await readFile(file, 'utf8');
  • 执行外部命令固定命令
    index.js:1266
    ? spawn('explorer.exe', [`/select,${target}`], { detached: true, stdio: 'ignore' })
  • 执行外部命令固定命令
    index.js:1268
    ? spawn('open', ['-R', target], { detached: true, stdio: 'ignore' })
  • 执行外部命令固定命令
    index.js:1269
    : spawn('xdg-open', [dirname(target)], { detached: true, stdio: 'ignore' });
  • 读取本地文件
    splash.js:189
    const [htmlInfo, bridgeInfo] = await Promise.all([stat(SPLASH_HTML_URL), stat(SPLASH_BRIDGE_URL)]);
  • 读取本地文件
    splash.js:194
    readFile(SPLASH_HTML_URL, 'utf8'),
  • 读取本地文件
    splash.js:195
    readFile(SPLASH_BRIDGE_URL, 'utf8'),
  • 写入本地文件
    memory.js:289
    await mkdir(dirname(this.file), { recursive: true });
  • 写入本地文件
    memory.js:291
    await writeFile(temporary, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
  • 写入本地文件
    memory.js:292
    await rename(temporary, this.file);
  • 读取本地文件
    lan.js:174
    return parse(await readFile(file, 'utf8'));
  • 读取本地文件
    lan.js:180
    return parse(await readFile(legacyFile, 'utf8'));
  • 写入本地文件
    lan.js:203
    await mkdir(dirname(file), { recursive: true });
  • 写入本地文件
    lan.js:206
    await writeFile(temporary, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
  • 写入本地文件
    lan.js:207
    await rename(temporary, file);
  • 发起网络请求
    lan.js:493
    const request = http.request(
  • 发起网络请求
    lan.js:580
    const upstreamRequest = http.request(
  • 发起网络请求
    lan.js:655
    const upstreamRequest = http.request({
  • 读取本地文件
    skills.js:207
    await stat(path);
  • 写入本地文件
    skills.js:217
    await writeFile(temporary, text, 'utf8');
  • 写入本地文件
    skills.js:218
    await rename(temporary, file);
  • 写入本地文件
    skills.js:228
    await mkdir(root, { recursive: true });
  • 写入本地文件
    skills.js:298
    await rename(staging, target);
  • 删除本地文件递归
    skills.js:300
    await rm(staging, { recursive: true, force: true }).catch(() => {});
  • 读取本地文件
    skills.js:320
    const parsed = JSON.parse(await readFile(file, 'utf8'));
  • 写入本地文件
    skills.js:332
    await mkdir(dirname(file), { recursive: true });
  • 读取本地文件
    skills.js:361
    dirents = await readdir(root, { withFileTypes: true });
  • 读取本地文件
    skills.js:379
    const targetInfo = await stat(target).catch(() => undefined);
  • 读取本地文件
    skills.js:386
    const info = await stat(file).catch(() => undefined);
  • 读取本地文件
    skills.js:388
    const text = await readFile(file, 'utf8').catch(() => undefined);
  • 写入本地文件
    skills.js:478
    await mkdir(directory, { recursive: false });
  • 删除本地文件
    skills.js:519
    await rm(isBundle ? bundleDir : file, { recursive: isBundle, force: true });
  • 读取本地文件
    agent.js:53
    const info = await stat(path);
  • 读取本地文件
    agent.js:54
    const raw = await readFile(path, 'utf8');
  • 删除本地文件
    agent.js:81
    await rm(path, { force: true });
  • 写入本地文件
    agent.js:88
    await mkdir(dshHome, { recursive: true });
  • 写入本地文件
    agent.js:89
    await writeFile(path, content, 'utf8');
  • 读取本地文件
    agent.js:90
    const info = await stat(path);
  • 读取本地文件
    mcp.js:210
    raw = await readFile(file, 'utf8');
  • 写入本地文件
    mcp.js:242
    await mkdir(join(file, '..'), { recursive: true });
  • 写入本地文件
    mcp.js:244
    await writeFile(temp, document, 'utf8');
  • 写入本地文件
    mcp.js:245
    await rename(temp, file);
  • 写入本地文件
    restart.js:81
    await mkdir(dirname(file), { recursive: true });
  • 写入本地文件
    restart.js:82
    await appendFile(file, `[${new Date().toISOString()}] ${line}\n`, 'utf8');
  • 读取本地文件
    restart.js:92
    const info = await stat(file);
  • 写入本地文件
    restart.js:94
    await rename(file, `${file}.1`);
  • 写入本地文件
    relaunch.mjs:45
    await mkdir(dirname(file), { recursive: true });
  • 写入本地文件
    relaunch.mjs:46
    await appendFile(file, `[${new Date().toISOString()}] ${line}\n`, 'utf8');
  • 写入本地文件
    relaunch.mjs:107
    await mkdir(dirname(log), { recursive: true });
  • 执行外部命令来自配置/变量
    relaunch.mjs:113
    const child = spawn(command, args, {
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DeepSeek Harness 插件包:清理会话、启动画面、本地记忆、局域网访问代理、技能管理器(内置 skill-creator)和 AGENTS.md 编辑器,全部从侧边栏控制面板管理。

README

由 DeepSeek 最新模型翻译生成
dsh-mikufan DeepSeek Harness 插件包(bundle)。版本 0.10.3。 - 包名 / 行 id:dsh-mikufan - 图标:icon.jpg - 显示元数据:locale/en.json、locale/zh.json - 六个功能,开关与管理页统一收在侧栏「控制面板」: 1. Delete-sessions(删除会话) 2. Startup animation(开机动画) 3. Local memory(长期记忆) 4. LAN access(局域网访问) 5. Skills(技能) 6. Agent(全局指令) 曾经拆成两个独立插件(dsh-mikufan + dsh-mikufan-startup-animation),后按需求撤回:一个插件包只能有一个浏览器半边,因此两个功能回到同一个插件里,用内置开关区分。 功能 1:Delete-sessions(删除会话) 交互 1. 侧栏会话列表 → 会话行的「⋯」菜单 → 新增一行 删除会话(红色、带分隔线,位于「归档」「复制会话 ID」之后)。 2. 选择后该会话进入「待删除」状态:列表中的会话标题出现删除线并转为次要颜色。 3. 待删除的会话只剩一个操作:行尾悬停出现的 取消删除标记 按钮;原来的「⋯」菜单、归档、置顶三个控件对该行全部收起(其他会话不受影响)。
展开完整文档(README 剩余 1 节)
dsh-mikufan

DeepSeek Harness 插件包(bundle)。版本 0.10.3。

- 包名 / 行 id:dsh-mikufan
- 图标:icon.jpg
- 显示元数据:locale/en.json、locale/zh.json
- 六个功能,开关与管理页统一收在侧栏「控制面板」:
1. Delete-sessions(删除会话)
2. Startup animation(开机动画)
3. Local memory(长期记忆)
4. LAN access(局域网访问)
5. Skills(技能)
6. Agent(全局指令)

曾经拆成两个独立插件(dsh-mikufan + dsh-mikufan-startup-animation),后按需求撤回:一个插件包只能有一个浏览器半边,因此两个功能回到同一个插件里,用内置开关区分。

功能 1:Delete-sessions(删除会话)

交互

1. 侧栏会话列表 → 会话行的「⋯」菜单 → 新增一行 删除会话(红色、带分隔线,位于「归档」「复制会话 ID」之后)。
2. 选择后该会话进入「待删除」状态:列表中的会话标题出现删除线并转为次要颜色。
3. 待删除的会话只剩一个操作:行尾悬停出现的 取消删除标记 按钮;原来的「⋯」菜单、归档、置顶三个控件对该行全部收起(其他会话不受影响)。
4. 待删除状态由 Host 持久化(~/.dsh/dsh-mikufan/delete-sessions.json),刷新页面后仍然保留。
5. 继续对话即自动还原:只要用户在被标记的会话里又发了一条消息(会话日志追加 user/message),标记立刻被静默清掉——没有提示、没有弹窗,删除线自己消失,该会话重新按普通会话处理。
6. 下次启动 Harness 时,所有仍然保持待删除的会话被真正删除(含磁盘文件):
- 会话日志目录 ~/.dsh/sessions/<工作区>/<会话 ID>/(含 session.v4.jsonl.zstd)
- 投影缓存 ~/.dsh/storages/session_projcache/sessions/<会话 ID>.json
- 工作区登记表 ~/.dsh/storages/workspace.json 中的 sessionIds / archivedSessionIds / pinnedSessionIds 引用(存在活动 storage domain 时通过 domain 写入,否则直接改写文件)
- 正在运行/被占用的会话会被跳过并保留标记,下次启动重试;删除失败同样保留标记。

开关(内置)

- 控制面板 → 仪表页「删除会话」卡片(默认开,立即生效):
- 关闭后:会话「⋯」菜单里的删除入口与行尾「取消删除标记」按钮消失,已有的删除线样式清除,停止对账轮询,并且下次启动不再执行删除清扫(已标记的会话保留,数据不动)。
- 重新打开后:菜单入口恢复,并立即与 Host 对账一次,恢复显示待删除状态。

功能 2:Startup animation(开机动画)

素材:用户提供的 D:\Mikufan\Documents\deepseek-harness\dsh-splash\index.html(5 秒「光点汇聚」,自包含 HTML + Canvas 2D,无外部资源)。包内的 splash/index.html 与素材逐字节一致 (SHA-256 80da47cfef444a70addb107e510c7ed3210409c682519a72548a9862aa47c5e3)。

交互

1. 在应用渲染之前铺满全屏播放一次:640 颗光点汇聚成 DeepSeek 鲸标,定格在结束帧,约 0.65 秒后淡出(0.5 秒)露出已经加载好的 Harness 界面。
2. 每次完整页面加载播放一次(打开、刷新、新标签页);SPA 内切换会话不重播。
3. 跳过:点击画面任意位置,或按 Esc,覆盖层立即淡出。
4. 控制面板 → 仪表页「启动动画」卡片:开关(默认开),修改在下次刷新页面生效。
5. 减少动态效果(prefers-reduced-motion: reduce):不播放粒子动画,直接呈现结束帧,短暂停留后淡出。
6. 调试:URL 加 ?mikufan-splash=off 可对这一次加载关闭;后台标签页加载时自动不播放。
7. 动画页可直接访问 /mikufan/splash 预览,独立模式保留素材自带的重播 / 全屏控件。

实现

| 半边 | 文件 | 说明 |
| Host | splash.js | webserver/index-inject 的三行注入(样式 / 覆盖层 / 引导脚本)、GET /mikufan/splash(ETag、304、HEAD、信任栅栏)、素材读取与组合缓存 |
| Host | index.js | DeleteSessionStore + PreferencesStore(三个内置开关:删除会话 / 启动动画 / 长期记忆,同步读、串行原子写)+ GET/POST /mikufan/api/preferences + 挂载路由 |
| Client | client.js | 控制面板仪表页的功能卡(宿主同款 role="switch" 结构与 token 样式;图标 + 标题行开关 + 页脚状态)+ 偏好读取 / 乐观写入 / 失败回滚 + 按 deleteSessionsEnabled 门控删除功能 UI;旧设置页与插件详情页入口已移除 |
| 素材 | splash/index.html | 用户素材原样副本 |
| 素材 | splash/embed-bridge.html | 只在服务时插入到 </body> 前的嵌入桥;素材文件本身不被改写 |

Host 端点(同源 fetch,与侧栏插件同样的 Host/Origin 信任栅栏):

GET /mikufan/api/marked -> { ok: true, value: { ids: [sessionId...] } }
POST /mikufan/api/mark {sessionId} -> { ok: true, value: { ids, changed } }
POST /mikufan/api/unmark {sessionId} -> { ok: true, value: { ids, changed } }

GET /mikufan/api/preferences -> { deleteSessionsEnabled, splashEnabled, memoryEnabled }
POST /mikufan/api/preferences {deleteSessionsEnabled?, -> { deleteSessionsEnabled, splashEnabled, memoryEnabled, changed }
splashEnabled?, memoryEnabled?}

GET /mikufan/splash -> 组合后的 HTML(iframe 使用;ETag / 304 / HEAD)

偏好文件:~/.dsh/dsh-mikufan/preferences.json({ version: 3, deleteSessionsEnabled, splashEnabled, memoryEnabled, updatedAt },原子写;
读取兼容旧版仅有 splashEnabled / 无 memoryEnabled 的文件)。

设计要点:

- 删除线不依赖任何「行内挂载」:本包注入一条规则,按 data-row-key="session:<id>" 定位,只作用于该行标题的 _title 元素(或第二个 span 回退),因此会话运行中、已归档时同样生效,不使用代价高昂的 :has(),也不改写其他插件的 DOM。
- 已标记的行只保留一个控件:同一套按 id 生成的规则把该行 .rowActions 里的「⋯」触发器、归档、置顶按钮(三者共用 _iconButton 类)隐藏,本包在 sidebar.workspaces.session.row.action 注册的「取消删除标记」是唯一可见项;文案、图标、悬停行为与宿主行按钮一致。
- 标记集合归属 Host,Client 首屏读取一次后乐观更新,失败回滚。
- 自动还原是静默的:Host 订阅 session/event,在该会话提交 user/message 时清掉标记并落盘,不弹任何提示;Client 在「有待删除标记」期间每 2 秒与 Host 对账一次(没有标记时不发请求),回到标签页时也会立即对账,因此删除线会自己消失。
- 清扫只在进程启动后的第一次激活执行(进程内一次性声明),避免改配置/热重载时提前删掉用户刚标记的会话;deleteSessionsEnabled 关闭时直接跳过清扫。
- 开机动画覆盖层不能由 Client half 挂载:它要覆盖 React shell 出现之前的空白期。因此 Host 订阅 webserver/index-inject,把三行注入渲染到 <body> 开头,浏览器解析到它们时应用模块还没执行; iframe 并行加载 /mikufan/splash,应用在覆盖层下方正常启动。Fail-open:iframe 7 秒内没有 ready 就直接移除覆盖层,不会把用户锁在黑屏里。
- 三个功能开关(删除会话 / 启动动画 / 长期记忆)都只在侧栏「控制面板」的仪表页渲染,与 Host 偏好读写同一份文件;设置页与插件详情页的旧入口已移除。
- 客户端文案走 client locale 服务(命名空间 dsh-mikufan,zh/en 两套)。

功能 3:Local memory(长期记忆)

Agent 的纯本地长期记忆(零依赖外部服务,存 ~/.dsh/dsh-mikufan/memory.json,不上传)。

Agent 侧:四个工具

| 工具 | 作用 |
| memory_write | 写一条事实(可选 type,或带 id 更新既有条目)。返回语义相近的既有条目,提示 Agent 用 UPDATE 代替 ADD(Mem0 式「检索-再决策」;本引擎自身不静默合并) |
| memory_search | 关键词检索(中英文均可;中文走 bigram + 单字覆盖,无需分词依赖) |
| memory_list | 列出全部记忆(可按 type 过滤) |
| memory_delete | 按 id 删除一条 |

- 条目类型(Claude Code auto-memory 四分类):user(用户角色/偏好)、feedback(行为纠正)、 project(项目决策/进行中工作,明确排除可从代码库推导的内容)、reference(外部资料指引)。
- 工具描述已写入使用准则:不存密钥/凭据/临时任务状态;效果层用 harness 的 defineTool 编译 schema 并校验参数。
- 上限保护:最多 200 条(超出拒绝写入并提示清理)、单条 ≤2000 字符。

注入:会话记忆索引

每个会话的 system prompt 通过 systemPrompt.context(order 150,紧随运行时上下文)自动携带
indexText() 生成的紧凑索引(每条一行、[type] content 截断至 160 字符,最多 40 条,超出时
提示改用 memory_search)。空库、功能关闭或条目被停用时注入空文本(框架丢弃空 context)。

WebUI:「控制面板 → 记忆」页

记忆管理是侧栏「控制面板」的第二个子页(与「控制面板」「局域网访问」共用同一套卡片语言):浮动胶囊标签栏切换页面,页面标题下是记忆卡片网格。

- 卡片网格(两列,窄屏单列):每页第一张卡片永远是空白卡——虚线描边、中间一个 +,点开「新建记忆」弹窗;
- 计数行右对齐搜索与刷新:左侧「共 N 条记忆 · M 条启用」,右侧是胶囊搜索框(按内容、类型标签、来源即时过滤卡片;过滤时计数后追加「· 匹配 N」)和圆形刷新按钮(重新读一次 Host,读取中图标旋转;失败会显示读取失败提示);
- 每条记忆一张圆角矩形卡片:标题行是类型标签(用户 / 纠正 / 项目 / 参考)、来源(Agent / 手动)与启用开关(role="switch",与仪表页开关同一控件);正文只显示前三行(CSS -webkit-line-clamp,点击正文即打开编辑弹窗看全文);页脚状态栏固定两个动作:编辑(铅笔)与删除(叉号,
- 第一次点击变为「确认删除」,3 秒后自动复位;右侧显示「更新于 MM-DD HH:mm」;
- 新建 / 编辑都用弹窗(react-dom 的 createPortal 挂到 document.body,复用宿主弹窗语言:遮罩用 --dsw-alias-bg-mask-1 + 4px 模糊,面板用 --dsw-alias-bg-layer-2 + --dsw-radius-panel + --dsw-elevation-prominent)。弹窗内是内容文本框(2000 字上限计数、超限标红、Ctrl/Cmd+Enter 保存)、四个类型芯片,以及「取消 / 创建(保存)」;Esc、点遮罩、右上角叉号都能关闭,保存中不响应关闭;
- 停用的条目仍在网格里,正文变灰、开关关闭(停用=保留但不注入索引);
- 全部操作走乐观更新 + 失败回滚;读写走 Host HTTP 端点(同源 + 信任栅栏,与侧栏其他端点一致)。

开关(内置)

- 控制面板 → 仪表页「记忆」卡片(默认开,立即生效):关闭后工具集卸载、索引注入为空、侧栏面板显示「off」状态但已存数据不删除;重新打开即恢复。

实现与端点

| 半边 | 文件 | 说明 |
| Host | memory.js | MemoryStore(原子写 JSON 文档)、similarity/queryTerms(关键词+bigram 检索)、createMemoryTools(四个工具定义)、indexText(索引生成,40 条/160 字符上限) |
| Host | index.js | 偏好开关 memoryEnabled(preferences v3)+ 工具注册(ctx.tools.register,按开关门控)+ systemPrompt.context 注入 + memory 路由 |
| Client | client.js | 控制面板的「记忆」子页(MemoryPageBody:卡片网格 + MemoryAddCard 空白卡 + MemoryCard 条目卡 + MemoryDialog 新建/编辑弹窗)+ 仪表页的记忆开关与计数页脚 + 记忆 store(乐观更新) |

Host 端点(同源 fetch):

GET /mikufan/api/memory -> { ok: true, value: { entries } }
POST /mikufan/api/memory {content, type?} -> { ok: true, value: { entry } }
PATCH /mikufan/api/memory/<id> {content?, type?, enabled?} -> { ok: true, value: { entry } }
DELETE /mikufan/api/memory/<id> -> { ok: true, value: { deleted, id } }

记忆文档:~/.dsh/dsh-mikufan/memory.json({ version: 1, entries: [...], updatedAt },原子写;
条目 { id, content, type, source: 'agent'|'manual', enabled, createdAt, updatedAt })。

设计要点:

- 不静默合并:memory_write 只返回 top-3 相近条目(阈值 0.34)由模型决策;与 Claude Code 一致,覆盖/删除都是显式动作(带 id 更新、memory_delete、面板删除)。
- 停用而非删除:面板和工具都支持 enabled: false——过期记忆退出索引但保留数据,可随时恢复。
- 工具按 isConcurrencySafe: true 声明可并行(写路径经 store 内部串行写链保护)。

功能 4:LAN access(局域网访问)

第二条监听(默认 3090)免 token 反向代理到本机 127.0.0.1 入口:代理自己持有回环会话 cookie,LAN 设备直接打开即可;本机入口与登录方式不变。纯明文 HTTP,只适合可信网络。

WebUI:「控制面板 → 局域网访问」页

四张卡片,与仪表页同一套卡片语言(2×2 网格,窄屏单列;第一行是开关 + 它管辖的上限,第二行是地址与端口):

| 卡片 | 作用 | 页脚状态 |
| 局域网访问 | 开关(与仪表页功能卡一模一样:48px 图标 + 标题行开关 + 描述 + 页脚);说明「需手动开启」 | 已开启 http://ip:port(带复制)/ 未开启 / 启动失败原因 |
| 最大连接数 | 按 IP 计,0 表示不限,上限 256 | 当前不限制设备数 / 当前上限 N 台设备 · 已连接 M |
| IP 地址 | 自动探测 / 检测到的地址 / 手动输入 | 自动探测:地址,或 手动指定:地址 |
| 端口 | 默认 3090(1–65535) | 当前端口 N |

- 开关与下拉立即生效(Host 重启代理);数字输入与手动 IP 失焦即保存(Enter 等同失焦),非法值就地标红且不会提交。
- 页面文案按简化稿收敛:副标题「局域网快速访问」,三张可编辑卡的说明分别是「需手动开启」「按 IP 计,0 表示不限」「默认 3090」;原顶部安全提示条已移除。

偏好与实现

- 偏好文件 ~/.dsh/dsh-mikufan/lan-preferences.json,字段 lanAccessEnabled / lanHost / lanPort / lanMaxConnections(version 1;旧文件缺 lanMaxConnections 时按 0 处理);从旧 dsh-lan-access 升级时读穿旧偏好文件。
- 每次启动 dsh 后开关默认关闭:启动时若文件里的 lanAccessEnabled 为 true,Host 会把它写回 false 并且不启动代理;lanHost / lanPort / lanMaxConnections 保留,需要在控制面板或局域网页面手动开启。GET lan/status 返回的开关也会以「本次进程是否手动开启」为准。
- 最大连接数按设备(peer IP)计:同一 IP 的多条 keep-alive / SSE / WebSocket 连接共用一个名额;超出上限的新设备先收到 503 JSON(code: too-many-devices),其请求不会被转发到本机入口。改设置会重启代理并重置计数。
- 端点:GET /mikufan/api/lan/status、 POST /mikufan/api/lan/preferences {lanAccessEnabled?, lanHost?, lanPort?, lanMaxConnections?}。

功能 5:Skills(技能)

Harness 本身不带技能目录:插件启动时创建 ~/.dsh/skills(DSH_HOME 下的 skills,即 skill provider 的 user-dsh 根),并把插件自带的 skill-creator 技能安装进去——首次运行复制一次,之后绝不覆盖用户改过的版本;<dshHome>/dsh-mikufan/bundled-skills.json 记录安装事实,用户卸载该技能后不会再被自动装回。

自带的 skill-creator 已针对本 Harness 改写:技能目录指向 ~/.dsh/skills,开关用 disable-model-invocation,并注明 agents/openai.yaml 与 PyYAML 脚本只对 Codex 有效。

WebUI:「控制面板 → Skills」页

- 与「记忆」同构的卡片网格:第一张是新建技能卡片(虚线 + 加号),其余每张是一枚技能卡;
- 卡片不用图标:第一行是技能名(kebab-case 目录名 / frontmatter name),第二行是描述(两行截断);标题行右侧是开关,页脚是打开与详情;
- 计数行右侧有搜索技能(与记忆页同一套胶囊搜索框:按技能名/描述即时过滤,过滤时追加「· 匹配 N」)和刷新按钮;左侧显示「共 N 个技能」与管理根目录的绝对路径;
- 开关 = disable-model-invocation:开(默认)表示模型可见;关只写一个 frontmatter 标志,文件保留、模型目录里不再出现;
- 新建技能:先确保根目录与 skill-creator 存在,然后新开一个会话,输入框里只预填一个 /skill-creator 技能引用(与手动输入 / 选技能完全一致)——真正的创建由 skill-creator 引导,用户补充需求后自己发送;复用的空白会话里若有旧草稿会被这条引用替换;
- 打开:与「立即试用」同一条路径——新开一个会话,输入框只预填该技能的 /技能名 引用(复用空白会话时替换旧草稿),不自动发送;
- 详情:参考图样式的浮层——标题 / 描述 / 开关 /「⋯」菜单(在文件资源管理器中显示、复制 Markdown)/ 关闭,正文是渲染后的 SKILL.md,页脚是卸载(二次确认,删除整个技能目录)与立即试用(新会话只预填该技能的 /技能名 引用)。
- 仪表页摘要卡:控制面板首页有一张 Skills 卡(无开关),页脚显示「启用了 x 个技能,共 Y 个」,挂载时读取技能列表,无需先进入 Skills 页。

实现与端点

- skills.js:splitSkillDocument / readSkillFrontmatter(解析顶层标量与 >/| 块标量)、 summarizeSkill(与 provider 同样严格:kebab-case 名、必需描述、拒绝 camelCase 调用键、非法布尔值——无效技能不列出)、createSkill、setSkillEnabled(文本级改写 disable-model-invocation,保留其余 frontmatter、正文与换行风格)、removeSkill、 listSkills(bundle 优先、符号链接跟随、不可解析的 id 不列出)、installBundledSkill。
写入均为临时文件 + rename 的原子写。
- 端点:GET /mikufan/api/skills、GET|PATCH|DELETE /mikufan/api/skills/<id>、 POST /mikufan/api/skills/<id>/reveal、POST /mikufan/api/skills/ensure。

功能 6:Agent(全局指令)

编辑 Harness 每次会话都会加载的全局指令文件 <DSH home>/AGENTS.md(默认 ~/.dsh/AGENTS.md)——也就是 @deepseek-ai/dsh-agent-instructions 解析的用户全局指令链;仓库/项目里的 AGENTS.md 仍然照常叠加生效。

WebUI:「控制面板 → Agent」页

- 页面只有一个文本框(默认空白),占位提示 [添加自定义指令...];
- 文本框下方左侧显示该文件的绝对路径,右侧是保存按钮(Ctrl/Cmd+Enter 同效);
- 打开页面时先读取文件:已有内容会填进文本框便于继续编辑;文件不存在就是空白(正常状态);
- 点保存把整段文本写入 ~/.dsh/AGENTS.md(UTF-8,原样保存),成功后提示「已保存」;
- 清空后保存会移除该文件(相当于撤销全局指令);保存失败保留用户输入并提示重试;
- 文本框上限 64 Ki 字符,写入为就地覆盖(保持符号链接指向,适配 dotfiles 仓库)。
- 仪表页摘要卡:控制面板首页有一张 Agent 卡(无开关),页脚显示「上次更新时间:10-23 22:05」,取 ~/.dsh/AGENTS.md 的修改时间;文件不存在时显示「上次更新时间:—」。

实现与端点

| 半边 | 文件 | 说明 |
| Host | agent.js | readAgentInstructions(缺失文件=空白、去 BOM)、saveAgentInstructions(类型/长度校验、空文本删除、就地写 UTF-8) |
| Host | index.js | GET/POST /mikufan/api/agent 路由 + agentHome(resolveDshHome()) |
| Client | client.js | 控制面板的「Agent」子页(AgentPageBody:单个文本框 + 路径 + 保存按钮 + 状态提示)与独立样式表 agent-panel.css |

GET /mikufan/api/agent -> { ok: true, value: { path, exists, content, updatedAt } }
POST /mikufan/api/agent {content} -> { ok: true, value: { path, exists, content, bytes, updatedAt, saved } }
(content 为空白时删除文件:{ exists: false, removed: true, updatedAt: '' })

验证

测试套件(tests/)与交接文档(HANDOFF.md)仅保留在本地,不随本仓库发布;以下命令在插件根目录可用。
powershell
node tests/sweep.test.mjs # Host:清扫、存活会话跳过、两种登记表路径、继续对话还原、路由契约
node tests/client.test.mjs # Client:菜单行、行悬停按钮、样式规则、三个开关的写入/回滚/门控、面板注册、记忆卡片网格与新建/编辑弹窗、搜索与刷新、局域网四卡片页、Skills 卡片/详情/新建技能引用、Agent 单文本框页、仪表页 Skills/Agent 摘要卡与页脚
node tests/skills.test.mjs # 技能:解析/列表/创建/开关/删除/内置安装 + skills 路由
node tests/agent.test.mjs # 全局指令:空白读取/BOM/原样写入/清空移除/长度上限/更新时间 + agent 路由
node tests/splash.test.mjs # 动画:注入行、组合文档、路由契约、偏好读写、preferences API
node tests/memory.test.mjs # 记忆:store 生命周期、检索语义、四个工具契约、索引上限、memory 路由
node tests/lan.test.mjs # 局域网:连接数上限校验、偏好读写与旧文件读穿、真实 socket 的上限拒绝/释放

七个脚本都只操作临时目录或桩对象,不会触碰真实的 ~/.dsh。

已知边界(0.10.3)

- 删除在下次启动时执行,当前进程内不会立刻消失(这是本组件约定的语义);插件热重载不触发清扫。
- 若某会话在标记后仍被 Harness 保持打开,重启后该会话会被删除,属于预期行为;只要期间再发一条消息就会自动还原,从而保留下来。
- 删除线依赖工作区侧栏的行属性 data-row-key 与标题类名中的 _title 片段;收起三控件依赖宿主行按钮共用的 _iconButton 类名。上游大幅改版时样式可能失效(不会报错,也不会影响其他功能)。
- 会话级联数据(附件 blob 等)不在此清理范围内;附件按内容哈希共享,删除会话不会删除 blob。
- 动画是页面级的:只在完整页面加载时出现;SPA 内导航、客户端 HMR 热更新不重播。
- 动画固定使用素材的近黑舞台,不跟随 Harness 明暗主题(开机画面是品牌帧,不是应用界面)。
- 「开机动画」开关只对后续页面加载生效;「删除会话」开关对 Host 立即生效,浏览器 UI 的增减在下次页面刷新后生效。
- 六个功能共用一个插件:插件管理页里是一张卡片、一行组件;各功能开关在侧栏「控制面板」仪表页。
- 记忆检索是关键词/bigram 级别的,不是向量语义检索;改写幅度大的中英文混合表述可能漏召回(memory_write 的相近条目提示只是辅助,不构成保证)。v2 可引入 embedding。
- 记忆索引每条截断 160 字符、上限 40 条;超出的条目仍在库里,但模型只能通过 memory_search 看到它们。
- 「长期记忆」开关关闭时工具与索引立即下线(HMR 重激活生效);侧栏面板仍可查看/管理已存数据。
- Agent 页只编辑用户全局文件 ~/.dsh/AGENTS.md;项目/仓库里的 AGENTS.md 由 Harness 自动叠加,本页不读取也不修改它们(页面副标题已说明「代码仓库的说明也可能适用」)。
- 全局指令在会话的下一次请求才进入上下文:dsh-agent-instructions 不监听外部编辑,已经带过指令基线的会话要等下一次请求/恢复会话才重新对账;新会话立即生效。
- 清空文本框后保存是删除文件(不是写一个空文件);文件被其他程序占用时保存会失败,输入保留在页面上。
预览分享卡片长什么样 ↓mikufan39/dsh-mikufan
分享 · DeepSeek Harness Hub
mikufan39/dsh-mikufan

集中管理会话删除、开机动画与局域网访问等六项功能

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部