sandbase-harness
sandbaseai
Local-first, self-hosted AI agent runtime and MCP bridge with sandboxed sessions, memory, credentials, audit/replay, and a local Console.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:KIDLi1412/dsh-session-cost
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
DSH(DeepSeek Harness)Web 插件:把本次会话的 Token 费用估算与 DeepSeek API 余额并入输入框下方的自带统计栏。
dsh-token-meter 的 tokenUsage 投影一致),再按 CNY 单价表(lib/cost.js)计算费用,混合多模型的会话也精确。GET {baseURL}/user/balance(参考插件 dsh-usage-stats 的余额方案),凭据经 DSH 的 credentials 缝解析,2 分钟内存缓存 + 单飞防抖;?refresh=1 可强制绕过缓存(状态栏的 ⟳ 手动刷新即用此参数)。费用/余额是一个与自带统计项同款的可点击 pill,追加在自带统计栏同一行;若该会话自带统计栏整行都不存在(全新会话,或冷会话加载的头一秒),pill 会先独立显示在输入框上方(同一套字号/内边距/圆角),统计栏一出现就自动并回那一行:

--dsw-specific-menu 背景、标题 + 分隔线 + dt/dd 网格、12/18 字号):标题行左侧「费用」、右侧总额;网格里每个模型一行(输入/输出 tokens 与费用,跨峰谷时附 高峰/空闲 拆分),然后是余额与充值/赠送构成——读数网格只有数字;再往下是「更新于 HH:MM + ⟳ 手动刷新」一行,其下一行就是低余额阈值输入框(低余额阈值 [ 10 ] 元,见下);最底是计价说明。点击面板外或按 Esc 关闭。position:fixed 挂到 document.body 并做视口夹取(与原生 stat dialog 相同的 measure→place 流程、同样的 8px 间距 / 12px 边距),所以不会被任何祖先容器的 overflow 裁掉;数据更新走原地 patch(不重建节点),因此面板开着时刷新数值不会闪断、也不会丢焦点。StatsPills:居中的 flex 行、由带图标的 pill 组成(data-composer-stats 标记),取代了此前单行省略号文本的 StatsLine。本插件的 pill 因此按同一套 13/20 字号层级、同一 1px 8px 内边距与 24px 圆角、同一 hover / aria-expanded 背景渲染,自带 ¥ 图标,视觉上与自带 pill 齐平。StatsPills 在会话有步骤或 token 之前返回 null(整行都不存在),所以插件必须能在统计栏之后挂载的情况下仍然接上去——见下文「兼容性」。sync() 只是不画东西,观察器始终在岗。width:100%、max-width:748px、gap:12px,且不做 overflow 裁剪),追加的费用段只是这一组里的第三项,宽度本来就够——插件不写统计栏的任何行内样式。0.1.x 早期版本曾需要把统计行放宽才能显示追加段(≤ 0.1.4 的 StatsLine 有 748px 上限 + 省略号截断),那个补丁已在 0.2.3 删除。设置项就在明细面板里、「更新于 / ⟳」的下一行:低余额阈值 [ 10 ] 元,改完按回车或点开别处即生效。从 0.3.0 起这个值作为本插件的 profile 配置保存(DSH 0.2.0 的设置模型:Config → 以 entry id session-cost 为键的表单 → 写进 profile patch),旧的 ~/.dsh/settings.yaml 阈值会被 Host 在升级后一次性抢救回配置;0.1.1 及更早版本的 localStorage 配置也会在首次加载时自动迁移。

0.2.6 起不再有「设置 → 插件 → 插件配置 → 会话费用显示」那张卡片:这个阈值只影响本面板与 pill 的颜色,所以编辑器搬进了面板本身(持久化的 namespace 没变,旧值原样沿用)。
0.1.5 起移除了「独立状态栏」显示方式(统计栏下方单独一行),只保留并入统计栏;旧配置里的
displayMode键会被忽略。
展开面板内容(示例):
费用 ¥1.9400
────────────────────────────────────────────────
deepseek-v4-flash 输入 169,013 · 输出 46,512 · ¥1.8900
deepseek-v4-pro 输入 1,000 · 输出 500 · ¥0.0500 · 高峰 ¥0.02 · 空闲 ¥0.03
余额 ¥36.44
充值余额 ¥30.00
赠送余额 ¥6.44
更新于 10:32 ⟳
低余额阈值 [ 10 ] 元
费用为估算值:token 用量来自会话日志,单价见官方定价页(…)。
从 npm 安装:
dsh plugin --profile web add @kidli1412/dsh-session-cost
从 GitHub 安装:
dsh plugin --profile web add github:KIDLi1412/dsh-session-cost
本地开发(链接安装,改动即时生效):
dsh plugin --profile web add link:path/to/dsh-session-cost
安装后重启 dsh web,浏览器硬刷新(Ctrl+Shift+R)。打开任意会话即可在自带统计行末尾看到费用与余额。
移除:
dsh plugin --profile web remove @kidli1412/dsh-session-cost
dsh.compatibility.dshReleases 将当前版本线 0.2.0-rc.2 声明为 compatible(DSH STORE 的精确逐版本兼容证据;仅范围声明不会恢复上架)。插件使用的客户端注入(dsh-api-remotes / dsh-client-connection / dsh-client-locale / dsh-client-ui-conversation / dsh-client-ui-settings)、客户端服务(slots / locale / configForms)与 Host 服务(webServer 精确路由、settings.describe / settings.update、credentials.resolve、session.seq + session.eventAt)在 0.2.0 版本线上均已在真实安装中核对。^22.19.0 || >=24.0.0(与 DSH 一致)。engines.dsh: ^0.2.0-rc.2,并将运行时依赖的 lockstep 宿主包声明为 peerDependencies(dsh-host-webserver / dsh-session / dsh-credentials / dsh-settings 与客户端模块 dsh-api-remotes / dsh-client-connection / dsh-client-locale / dsh-client-ui-conversation / dsh-client-ui-settings,均为 ^0.2.0-rc.2);插件市场会据此显示"宿主要求"并判断与当前 DSH 是否匹配。这些包由 DSH 运行时提供、本插件从不 import,所以同时标为 peerDependenciesMeta.optional——npm/pnpm 不再把它们装进依赖树(0.2.0 起它们彼此还有 peer 关系,沿用旧的自动安装会直接解析冲突),而"宿主要求"的语义不受影响。rows: []、统计栏里什么都没有)——peerDependencies 里的 @deepseek-ai/dsh* 与运行时版本(semver.satisfies(..., { includePrerelease: true })),^0.1.5-rc.1 不满足 0.2.0-rc.2(caret 封顶在 <0.2.0),因此本版把 engines.dsh 与 9 个宿主 peer 一起提到 ^0.2.0-rc.2。顺带把宿主包标为 optional peer,见上一条。settings.register(ns, schema)"没了,settings.get(ns) 也没了。现在插件导出的 Config(schemastery)就是设置表单,以profile entry id(本插件 bundle patch 里的 id: session-cost)为键;Host 通过 ctx.settings.describe() 读别人的条目、ctx.settings.update(ns, patch) 写;浏览器端用 ctx.configForms.get("session-cost")(getSnapshot / subscribe / set),旧的 ctx.settingsScope.bind({ namespace }) 已从客户端消失。本插件的配置存取因此整体改到这上面(面板里的阈值输入框 UI 不变)。.bOPqQW_root 不再自带宽度/内边距(width:100% + 748px 上限 + 侧边 clearance 移交给 composer 的 dock 容器),字号降到 calc(--dsh-content-font-size-secondary - 1px),pill 圆角由 24px 改为 999px + corner-shape:round,stat-dialog 面板改用 --dsw-radius-lg 并加 backdrop-filter。本插件的 pill、solo 兜底行与明细面板按同一套值重绘,视觉仍与自带 pill 齐平。dsh web 的每个请求加了签名 cookie(dsh web authentication required),插件的两个端点因此只有带 cookie 的同源页面能到达;Host 侧的 loopback 精确路由fence 保留为第二道防线。升级时会自动抢救旧阈值:DSH 自己的
settings.yaml导入按 entry id 进行,而当时本插件正因上面的 peer 不符被拒挂载,导入失败后值只留在~/.dsh/settings.yaml.imported里。本版 Host 在启动后(等 Loader settle)读一次该文档,若条目还没有用户值就把非默认的lowBalanceThreshold写进配置——一次性、只读、失败即跳过。
@deepseek-ai/dsh-client-ui-primitives。0.1.5 起该包不再随 DSH 安装(依赖树里已无此包,客户端模块图因此没有这一行),而 plugin bundle 的 require() 对未注册模块是抛错的(loader 的 loud 语义),一处 require 就会让整个客户端 half 加载失败:dock 锚点、合并段、面板全部消失。本插件的图标改为内联 SVG 自绘,bundle 不再依赖任何可选宿主模块。data-composer-stats 属性定位(0.1.5 新增),文本 N 轮 · M 步 只作旧版回退且改为非锚定匹配(0.1.5 的 pill 文本已无 · 分隔)。StatsPills 在会话有步骤/token 前返回 null(统计栏迟到)→ 观察器必须监听容器子树(childList + characterData + subtree);重启 host 后首次 summary/余额响应仍在路上(数据迟到)→ 观察器必须无条件安装。deepseek-flash,且官方在 2026-09-10 发布 V4.1-Flash 并调价;本版引入价格世代模型(legacy / v4:* / 当前 peak/offpeak)与别名解析,详见下文「定价表」。
交互上,费用/余额段从"悬停气泡"改为与自带统计项同款的可点击 pill + 点击展开面板。isConnected 时才重新挂载观察器——一次这样的回调就让观察器永久失效:费用消失,切回新会话也不恢复,必须刷新页面(刷新等于重新挂载、装上新的观察器)。0.2.1 无条件重挂(脱离文档的子树照样派发变更),并加 1 秒看门狗兜底补挂;观察器也改为每个组件挂载只装一次,数据变化走 sync() 推入(不再每 30 秒拆掉重建节点)。[data-solo],字号/内边距/最大宽度照抄 .bOPqQW_root),统计栏一出现即挪回栏内——全新会话与冷会话加载中都有费用读数。document.body 的 portal,而原地 patch 用 node.querySelector("[data-slot=panel]") 找它,恒为 null。后果是面板里的读数(总额、余额、更新时间)从来不刷新,并且 0.2.1 之后点击 pill 打不开面板(展开态写不进那个"找不到的"面板)。0.2.2 改为通过 mergePanels 注册表(panelOf)取面板,展开/收起、读数更新都真正落在同一个面板元素上;同时处理器改为点击时从组件的稳定 state 对象上读取(节点寿命已跨越多次渲染,刷新按钮必须作用于当前会话)。StatsLine 自带 748px 上限 + overflow:hidden + 省略号截断,会把追加的费用段裁掉,早期版本的插件因此用行内样式把统计行"放宽 + 取消裁剪"(效果同 zh_pro「统计全显示」,自包含、不依赖它,卸载时按 WeakMap 记下的原值精确还原)。0.1.5 的 StatsPills 根节点(.bOPqQW_root)是居中 flex 行(width:100%、max-width:748px、gap:12px),既没有 overflow:hidden 也不做省略号截断——追加段只是这一组里的第三项,宽度本来就够,补丁已删除,插件不再写自带统计栏的任何行内样式(dom-smoke 断言统计栏的行内样式在挂载 / 重新挂载 / 卸载后逐项不变,重新引入任何 max-width 写入都会失败)。dt 是 row-label,dd 是 row-value / panel-balance),而原地 patch 过去用 cell.querySelector("[data-slot=row-value]") 去取它们——querySelector 只搜后代,于是每个单元格都返回 null、patchText 直接跳过:手动 ⟳ 刷新后「账户余额」会跟着变(它按 slot 名单独 patch),「充值余额 / 赠送余额」和逐模型读数却停在旧值。0.2.5 改为直接 patch 单元格自身,并补齐两处:data-warn 的同步(模型变成未计价时那一行要变红)与列表在原位增长 / 收缩(暂无 token 用量 ↔ 模型行,节点自身 slot 数不变时不再抛错或被吞掉)。dt/dd)的最后一行,读数和设置混在一起;现在它是面板里独立的一行、紧跟在更新时间与 ⟳ 刷新下面,左边标签、右边输入框(与读数同一右边界)。读数网格因此恢复为纯数字(模型行 + 余额构成),设置项与面板的控制按钮聚在一起。位置之外的行为不变(提交/回车、拒绝空值与负数、刷新不覆盖半途输入、写回同一个 namespace)。settings.plugin.item 注册(客户端从此只注册 composer dock 一个槽位)。持久化路径没变:仍是 session-cost settings namespace → ~/.dsh/settings.yaml,旧值原样沿用,0.1.1 及更早的 localStorage 迁移逻辑仍在。空串或负数不提交并把输入框恢复成已存值。.events 数组——事件总数读 session.seq、逐条读 session.eventAt(seq)(与官方 dsh-token-meter 相同的读法),费用折叠已适配;客户端注入模块列表同步为新架构模块(见上)。dshReleases 只列当前版本线(0.2.0-rc.2),engines.dsh 也是 ^0.2.0-rc.2。需要 DSH 0.1.5 线的用户请使用 0.2.7;需要 0.1.2 线的请使用 0.1.9(更早的 0.1.x 见各自版本的 README)。| 文件 | 角色 |
|---|---|
lib/index.js |
服务端:导出 Config(lowBalanceThreshold,DSH 0.2.0 据此生成设置表单);GET /api/session-cost/summary?session=<id>(增量折叠会话事件并按模型计价)、GET /api/session-cost/balance(DeepSeek 余额,loopback-only 精确路由,?refresh=1 强制绕过缓存);经 settings.describe() 读 llm-deepseek 条目、经 credentials 缝解析 API Key;启动后一次性抢救旧 settings.yaml 里的阈值 |
lib/cost.js |
纯函数:按模型 token 折叠(replace-last-sample 语义)+ CNY 单价表 + 费用计算 |
lib/balance.js |
纯函数:DeepSeek 余额接口查询与状态归一化 |
lib/client.js |
浏览器端:只注册 conversation.composer.dock 槽位(id session-cost, order 100);把费用/余额 pill 追加进自带统计栏 DOM(startStatsRowObserver:子树 MutationObserver + 无条件重挂 + 1 秒看门狗,统计栏迟到/被 React 重渲染/切会话重建后都会重新挂载;统计栏不存在时先挂在锚点自身;updateMergeNode 原地 patch 数值;不改统计栏自身的任何样式),点击展开挂到 document.body 的明细面板(placeOpenPanel 做视口夹取),面板里「更新于 / ⟳」下面一行是低余额阈值输入框(经 ctx.configForms.get("session-cost") 读写本插件配置) |
费用为估算值:token 用量来自会话日志中 provider 上报的 usage 样本,单价表为写死的默认值,价格变动后请更新 lib/cost.js 的 DEFAULT_PRICING(或通过插件配置 pricing 覆盖)。
取自官方定价页(模型 & 价格 中文版)。计费为峰谷 + 价格世代两层:
| 世代 | 生效区间(北京时间) | 说明 |
|---|---|---|
peak / offpeak |
2026-09-10 0:00 起(V4.1-Flash 发布) | 当前价,见下表 |
v4:peak / v4:offpeak |
2026-08-17 0:00 – 2026-09-09 | V4 时代的峰谷价(Flash ¥1.5/3、¥0.05/0.10、¥4.5/9),V4_ERA_PRICING |
legacy |
2026-08-17 0:00 之前 | 平峰旧价(Flash ¥1、¥0.02、¥2),LEGACY_PRICING |
当前价(2026-09-10 起,CNY / 百万 tokens):
| 模型 id | 输入(缓存未命中)空闲 / 高峰 | 输入(缓存命中)空闲 / 高峰 | 输出 空闲 / 高峰 |
|---|---|---|---|
| deepseek-flash(V4.1-Flash,DSH 0.1.5 实际路由的 id) | ¥1 / ¥2 | ¥0.02 / ¥0.04 | ¥4 / ¥8 |
| deepseek-v4-flash(已下线,别名到 V4.1-Flash) | ¥1 / ¥2 | ¥0.02 / ¥0.04 | ¥4 / ¥8 |
| deepseek-v41-flash(同一模型的另一种拼写) | ¥1 / ¥2 | ¥0.02 / ¥0.04 | ¥4 / ¥8 |
| deepseek-v4-flash-vision-exp(已下线,别名到 V4.1-Flash) | ¥1 / ¥2 | ¥0.02 / ¥0.04 | ¥4 / ¥8 |
| deepseek-v4-pro(V4-Pro-0813,官方确认 2026-09-14 后继续提供) | ¥4.5 / ¥9 | ¥0.15 / ¥0.30 | ¥13.5 / ¥27 |
| deepseek-chat / deepseek-reasoner(V3 遗留名,2026-07-24 起别名到 Flash) | ¥1 / ¥2 | ¥0.02 / ¥0.04 | ¥4 / ¥8 |
模型 id 会变,变了就会静默算成 ¥0:DSH 0.1.5 把 V4-Flash 路由成短 id
deepseek-flash(界面显示 "DeepSeek-V41-Flash"),而旧表里只有deepseek-v4-flash—— 找不到单价 → 整场会话费用恒为 0,这正是 0.1.5 升级后的"费用一直是 0"。因此本版:
- 当前表列出全部仍被接受的 id(含已下线的旧名,避免历史会话读成未计价);
- 匹配改为名称边界前缀:
deepseek-v4-flash-2026-01这类带日期后缀的 id 归到deepseek-v4-flash,而deepseek-v99这种不同型号不会被误当成 flash,而是判为未计价;世代表(V4_ERA_PRICING/LEGACY_PRICING)通过PRICING_ALIASES做同样的别名解析,旧世代里的新 id 也按旧价计费;- 明细面板里未匹配到单价的模型显示红色 未计价,不再伪装成 ¥0;
GET /api/session-cost/summary附带diagnostics(事件数 / 已识别模型 / 已计价模型数),便于一眼定位。
cacheWrite 无 DeepSeek 等价项(上下文缓存自动命中计费),默认按缓存未命中输入价计(分时段),避免低估。
明细面板会显示高峰 / 空闲 / V4 价 / 旧价的费用拆分(跨时段或跨世代时)。
官方英文页另有美元价格(Flash $0.15/$0.30 输入、$0.003/$0.006 缓存命中、$0.6/$1.2 输出),与本表的人民币价按同一份价目表换算,插件统一按人民币计价(与余额接口的 CNY 口径一致)。
插件配置(可选)可覆盖定价——平峰格式(所有时段同价)或分时段格式:
# ~/.dsh/settings.yaml 或 profile 插件配置
session-cost:
pricing:
deepseek-v4-flash:
input: 1
cacheRead: 0.02
cacheWrite: 1
output: 2
# 或分时段(offpeak/peak 各自覆盖,未给字段继承默认):
# deepseek-v4-pro:
# offpeak: { input: 4.5, output: 13.5 }
# peak: { input: 9, output: 27 }
pricing 与配置卡写入的 lowBalanceThreshold 共存于同一个 session-cost: section,互不覆盖(schemastery 解析保留未知键;pricing 仍由服务端从插件 config 读取)。
llm-deepseek 命名空间的 apiKeyEnv(默认 DEEPSEEK_API_KEY)。MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: token-usage。