把智能体接入企业内网,管好权限与知识库
30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/9 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/gischina/sti-harness.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 23 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/9) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包sti-harness(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/13 04:28:55
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 9 处,展示前 9 条)
- 读取密钥环境变量app/compose/services.mjs:46
const fromEnv = process.env.STI_BACKUP_KEY; - 读取密钥环境变量app/compose/services.mjs:69
const raw = process.env.STI_OLDER_SECRETS; - 读取密钥环境变量app/config.mjs:284
v.dshToken = parsed.token || (process.env.DSH_TOKEN || '') || DEFAULTS.dshToken; - 读取密钥环境变量app/config.mjs:286
: (process.env.DSH_TOKEN ? 'env' : (file.dshToken ? 'config.json' : 'none')); - 读取密钥环境变量app/diagnostics.mjs:336
const envKey = process.env.STI_BACKUP_KEY; - 发起网络请求public/app/40-delegate.js:189
const r0 = await fetch(path, Object.assign({ - 发起网络请求public/app/40-delegate.js:204
const r = await fetch(path, Object.assign({ - 发起网络请求public/app/70-app.js:618
const r = await fetch(url, { credentials: 'same-origin' }); - 发起网络请求public/app/71-app-workbench.js:755
const res = await fetch('/api/kernel/sessions/' + encodeURIComponent(w.sessionId) + '/turns/stream', {
扫描于 2026/10/31(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 接入企业内网
- 管理权限
- 管理知识库
STI HARNESS · 企业级时空智能体生产平台
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
STI HARNESS · 企业级时空智能体生产平台
基于 DeepSeek Harness(DSH)Runtime,面向企业内部可私有化部署的智能体生产平台。
单机单进程优先,零第三方依赖,渐进式 Node 演进。
1. 一句话定位
DSH 负责智能体如何运行,STI HARNESS 负责智能体如何进入企业生产环境。
在 DSH 的会话、技能、工具、MCP 能力之上,补齐四件企业必需品:
| 能力域 | 内容 | 交付状态 |
| 身份与权限 | 组织 / 用户 / 角色、登录鉴权(含 TOTP 两步验证)、资源级权限(含知识库级 ACL);部门(组织归属,0.17.0 起可改名 / 删除);SSO / LDAP | ⚠️ 部分 ↓ |
| 企业知识与检索 | 文档解析、分块、向量化、FTS5 + 向量混合检索、引用溯源、知识库隔离、接入对话(会话挂载知识库,0.14.0) | ✅ 已交付 |
| 治理与审计 | 审计日志、配额与用量、执行过程可追溯与产物留存 | ✅ 已交付 |
| 能力分发 | 技能 / 工具 / MCP 的市场、安装、启停、版本更新、可见性治理 | ✅ 已交付 |
「交付状态」这一列是为了让人一眼分清「已交付」与「没交付」,不把两者读成一体。
| 状态 | 含义 |
| ✅ 已交付 | 有路由、有界面、有回归,能真的用 |
| ⚠️ 部分 | 主体可用,但有明确标注的缺口 —— 见下面这条 |
| ❌ 未实装 | 刻意不做(有理由),不是"还没轮到" |
- 身份与权限 = ⚠️ 部分,一处明说的缺口:SSO / LDAP 如实不实装 ——部门级内网用不上,引入它要牵进证书 / 目录服务的运维负担(理由与结论见 docs/roadmap.md)。
部门已从"能引用、不能创建"的半成品补齐为可用(0.11.1):可创建、可列出(含真实成员数)、可调整用户归属;0.17.0 起支持改名与删除(删除带引用护栏 —— 先清成员、再删空子部门,不制造悬空引用)。它有一条明说的边界:部门不是权限载体 ——把它当成"把谁挪进管理员部就有权限"是误解,权限只来自角色与用户组。
登录另可选开 TOTP 两步验证(0.17.0;一次性恢复码;未开启者登录路径逐字不变)。
界面一览
下面这些图取自真实运行的本平台(不是设计稿):node app/main.mjs 起服务后,用零依赖的 CDP 脚本登录并逐页截图。每一张都能在本地复现 —— 图里的数字来自真实接口与真实库。
| 对话工作台 | 平台概览 |
| 对话工作台 | 平台概览 |
| 像用聊天工具一样提问;正文与「轨迹」分两个视图 —— 工具卡 / 推理 / 用量在轨迹侧,聊天侧只留正文。 | 这台机器上到底装了什么、什么没装;取不到的直接显示空态与原因,不拿占位数字凑。 |
| 知识库与检索 | 能力清单 |
| 知识库与检索 | 能力清单 |
| 导入 → 分块预览 → 混合检索 → 引用溯源;知识库可挂到会话上(RAG 闭环)。 | 技能 / MCP 双来源对账;启停立即生效(能力闸在契约层就把它摘掉,无缓存、不重启)。 |
| 审计日志 | 配额与用量 |
| 审计日志 | 配额与用量 |
| 哈希链防篡改;可校验并定位首个断裂点,而不是笼统地说"校验失败"。 | 先检后放行;数字全部来自真实 usage 事件,与事件流逐字段可比对。 |
📌 更多界面:docs/images/ 下还有可见性治理、模型中心、调度中心、备份与恢复、平台设置、账号 / 角色、契约与映射、接口清单等页的截图(同一批采集)。
快速开始
Windows:双击 start.cmd Linux / macOS: ./start.sh
只做部署自检、不启动服务(服务在跑也能安全执行) ./check.sh
然后浏览器打开 http://127.0.0.1:3090(不要直接双击 public/index.html)。
首次打开会进初始化向导:建组织 → 建所有者账号 →(可选)配一个模型凭据。之后就是登录页。
前置条件只有一个:Node.js ≥ 22.13(node:sqlite 解禁)。
不需要 Docker、不需要 npm install、不需要外部数据库。空库到可用状态一条命令完成。
详细部署、配置、备份、排障见 docs/deploy.md。
2. 设计取向
本项目不是从零起意,它的形状由两条各自独立的输入决定 —— 一条来自工程直觉,一条来自架构纪律。两者分工明确,且都只取其中一部分:
| 输入 | 取什么 | 不取什么 |
| 工程直觉(同权利人早期自研作品的经验) | 全部工程基因:零第三方依赖、单机双击即用、路由单点定义、统一重绘铁律、诚实数据源(取不到就空态,绝不假装在线)、零依赖回归脚本体系 | 单体脚本的分层方式(需拆模块);用 .json 文件当状态存储(改 SQLite) |
| 架构纪律(业界智能体平台的开源实践) | 架构思路:契约优先(AgentKernelContract)、Runtime 与产品层解耦、上游兼容代码集中隔离、治理与审计是一等公民、统一网关、能力分发 | 技术栈与部署形态:不引入 Vue3 / FastAPI / MongoDB / Redis / 向量库,不做多容器 Compose |
关键结论:值得借鉴的是架构纪律(契约优先 + Runtime 隔离 + 兼容代码收口),不是任何一套技术栈。把纪律拿过来,用零依赖 Node + 内嵌 SQLite 实现同等能力,既拿到企业级架构,又保住「双击即用」的部署优势 —— 这两件事在多数同类项目里是对立的,本项目存在的理由就是证明它们可以不对立。
⚠️ 界线:本项目只借鉴思路,不复制任何外部代码。本项目全部源码为本权利人独立实现,第三方与参考来源说明见 NOTICE。
📌 可行性评估见 docs/feasibility.md——含实测证伪的假设、能力缺口清单、风险矩阵、DSH 升级影响面与部署打包方案。
3. 四条设计原则(不可违反)
1. 零第三方依赖。 只用 Node 内置模块。唯一可能的破例是数据库驱动,且必须先有空缺才能破例(见 docs/architecture.md 的驱动抽象)。
2. 单进程、双击即用。 不装 Docker、不建 .env、不跑 npm install。企业能力在单进程内实现,而不是靠多容器堆出来。
3. 契约优先,DSH 只是 provider。 产品层与前端不直接消费 DSH API;所有 DSH 依赖走 AgentKernelContract,DSH 专属代码只允许存在于 app/kernel/dsh-provider/。
4. 不落第二份数据,不造假数据。 页面上的每个数字都来自数据库、DSH 实时接口、本机文件系统或真实探针。取不到就显示诚实的空态。
4. 技术底座(已在本机实测验证)
以下能力全部用 Node 内置模块实现,无需任何第三方包:
| 需求 | 内置方案 | 实测结论 |
| 业务数据库 | node:sqlite(DatabaseSync) | ✓ 可用(Node 22.22 / 24.10 均通过) |
| 全文检索 | SQLite FTS5 虚拟表 | ✓ 可用,CREATE VIRTUAL TABLE … USING fts5 成功 |
| 全文检索(中文) | bigram 预处理 + FTS5 | ⚠️ 实测:unicode61 对中文按整段作一词,子串检索 0 命中;trigram 对 2 字词 0 命中。必须 bigram 预处理,见 docs/feasibility.md §1.2.1 |
| 向量检索 | Float32Array 存 BLOB + 余弦相似度 | ✓ 写入 / 读回往返一致,12 字节 = 3×float32 校验通过 |
| JSON 字段 | SQLite json1 扩展 | ✓ json_extract 可用 |
| 并发读写 | SQLite WAL 模式 | ✓ 文件库可开启(内存库不适用) |
| 口令哈希 | node:crypto scrypt | ✓ 内置,无需 bcrypt |
| 会话令牌 | node:crypto HMAC-SHA256 + randomUUID | ✓ 内置,无需 jsonwebtoken |
| 凭据加密 | node:crypto AES-256-GCM | ✓ 内置 |
| 后台任务 | node:worker_threads | ✓ 内置,用于解析与向量化 |
| 自动化测试 | node:assert + 自研极简 harness | ✓ 零依赖。刻意不用 node:test:它的报告器刷新时机无法保证「末行输出 DONE fails=N」这条项目约定 |
| 压缩导出 | node:zlib(brotli / gzip) | ✓ 内置 |
| 实时流 | 自研 WebSocket 字节管道桥 | ✓ 内置 node:http upgrade + node:crypto 帧掩码,无 ws 依赖 |
依赖底线:Node ≥ 22.13(node:sqlite 解禁),推荐 Node 24。上表在 Node 22.22.2 / 24.10.0 实测通过。
⚠️ node:sqlite 目前仍是 experimental 状态(Node 22 上会打印 ExperimentalWarning)。
缓解措施见 docs/architecture.md 的「数据层驱动抽象」一节。
5. 目录结构
这份树描述的是已经建成的结构(不是规划稿)—— 每一项后面都注明了它属于哪一版。
它与磁盘是否一致,由 node tools/audit-orphans.mjs 兜底(模块可达性 + 前端分片 ↔ index.html 对账)。
sti-harness/
├─ start.cmd / start.sh # 单机启动器(环境自检 + 起服务 + 打印地址) ├─ check.cmd / check.sh # 部署自检(等价 node app/main.mjs --check) ├─ VERSION # 版本号单点维护 ├─ app/ │ ├─ main.mjs # 进程入口:装配依赖、优雅退出 │ ├─ config.mjs # 配置装配(env > config.json > 默认) │ ├─ paths.mjs # 路径推导(全项目唯一允许出现"目录从哪来"的地方) │ ├─ log.mjs / diagnostics.mjs # 日志器 / 自检(项数现读 node app/main.mjs --check,别手抄) │ ├─ http/ # 路由表(单点定义)、静态托管、WS 桥、中间件链 │ │ └─ openapi.mjs # OpenAPI 3.0.3 规格从路由表生成(P1-3,纯函数) │ │ └─ middleware/ # context / ratelimit / auth / tenant / rbac / quota / audit 七项全部实装 │ │ # 收尾钩子:finalizeAudit(有条件,只记状态变更)+ finalizeMetrics(无条件) │ ├─ kernel/ # ★ 契约层 │ │ ├─ contract.mjs # AgentKernelContract 定义 + 事件归一 │ │ ├─ dsh-provider/ # 上游专属适配器(唯一允许碰上游的地方) │ │ └─ unconfigured-provider.mjs # 未接入时的占位 provider(所有方法抛 kernel_not_configured) │ ├─ data/ # SQLite 连接、版本化迁移、仓储、主密钥 │ │ └─ migrations/ # 0001 建表 + FTS5 … 0019 两步验证 │ │ # (完整清单现读 app/data/migrations/,别手抄 —— 这里曾停在 0012) │ ├─ platform/ # ★ 八个平台模块 │ │ ├─ identity/ # 身份与权限(M1):口令 / 令牌 / 权限 / 凭据 / 仓储 / 服务 / 两步验证(TOTP,0.17.0 起) │ │ ├─ governance/ # 治理与审计(M3 + P1-2 + P1-3):哈希链 / 归档与链基准 / 配额(含并发闸)/ 成本核算 │ │ ├─ knowledge/ # ✅ 企业知识与检索(M2 + P1-2): │ │ │ # parsers/(md/txt/csv/json/html 纯 Node + PDF/Office 外部命令) │ │ │ # chunker.mjs(标题/页边界 + 重叠)/ embedding.mjs(可降级) │ │ │ # retrievers/(FTS5 bm25 + 暴力余弦 + RRF 融合)/ repos/ / service.mjs │ │ ├─ capability/ # 能力分发(M4) │ │ ├─ operation/ # 备份与校验(M5):service.mjs 门面;刻意不暴露 restore │ │ ├─ model/ # 模型中心(P1-1):配置 / 探活 / 默认与降级链,不做转发 │ │ ├─ schedule/ # 定时任务底座(P1-2):规则解析 / 到期扫描 / 执行体白名单 │ │ ├─ notify/ # 出站通知(P1-2):事件白名单 / URL 加密 / SSRF 校验 / 尽力投递;0.13.0 起有产生方与 10 分钟合并窗口(producers.mjs);0.17.0 起含企微 / 钉钉信封 │ │ └─ observe/ # 指标(P1-2):进程级计数器与 Prometheus 文本渲染(旁路,不做门禁) │ ├─ worker/ # ✅ worker_threads 池(M2 启用;拿不到线程时内联并如实标注) │ └─ util/ # ids / errors / bigram(中文分词)/ quiet-warnings ├─ public/ # 前端(boot.js 首屏引导 + index.html / style.css + app/ 下按域分片的前端代码) ├─ tools/ # 零依赖回归与审计脚本 │ ├─ gate.mjs # ★ 开发 / 测试入口只有这一个(0.13.1):按固定顺序跑完全部检查 │ │ # (项数现读 node tools/gate.mjs --list,别手抄), │ │ # 末行给 GATE DONE checks=<通过>/<总项> skipped=… fails=…; │ │ # 它同时打印"这个门禁测不出什么",免得把"没测"读成"没问题"。⚠️ 判据是 │ │ # "退出码 0 且 找到约定末行 且 末行数字是 0"三条同时成立 —— │ │ # 只看退出码会把"脚本没跑到末尾(= 什么都没检查)"读成"通过" │ ├─ test-core.mjs # 核心回归(266 用例:纯函数 / 权限矩阵穷举 / 解析分块 RRF / ZIP / 调度通知指标 / OpenAPI / 成本口径 / 知识库级 ACL(含授权主体校验与校验顺序)/ 向量预过滤三态 / 部门 / 通知重投队列 / 备份告警产生方与自检 [14] / 0.15.0 自检 [15]:明文令牌落盘三态(独立的明文键 / 地址内嵌 / 已封存) / 0.13.0 配额拦截的产生方与节流 / 0.14.0 知识注入的跨端契约(服务端标记 ↔ 前端形态识别,一次切干净)/ 0.15.0 内核接入向导(地址归一化 / 配置合并 / 诊断文案 / 待重启对账)+ 探测分类对着真实 HTTP 桩 + 运行期抹名(上游名不许随字符串穿到界面)+ 用量取数规则(挂在助手消息上的用量也要记账,且与汇总同一条规则) / 0.15.1 中文查询四级阶梯(整句切段后段间 AND 不再判死整条查询)+ assistant/message 事件按上游块类型拆 reasoning 与正文 / 0.17.0 两步验证(TOTP:RFC 6238 向量 / 防重放步号 CAS / 恢复码一次性 / 未开启者登录逐字一致)与 CSV 批量导入(逐行独立 / 上限先于哈希)+ 部门改名删除护栏 + 删账号清 TOTP 三表 / 0.21.0 工作区三档判定(五条优先级的顺序表穷举 × 四档 × 可见性 / 批量过滤按 scopeId 分组不聚合 / 可见性规范化 / 迁移 0021 的列与默认档)〔0.21.6 那套模块整块删除后,这几条改写成:档位推导 levelFromRoles(含 fail-closed)+ 档位比较 levelAtLeast + 迁移 0023 生效 —— 钉的是「workspaces 已经没有 visibility 列」而不是「默认值对不对」,否则 0023 被回退时会静默通过〕 / 0.21.4 内核两态(selectKernel 只按"配置里有没有地址"分叉:没地址 → 占位 provider,每个契约方法都抛 kernel_not_configured、绝不返回"看起来正常的空结果";有地址但连不上 → 如实报不可用)+ 版本门禁穿过真实 provider 而不是字面量("没有上游版本时不得报出『上游版本未验证』"——从前靠"内核模式 = mock 就跳过门禁"盖住,模式删了就得由 provider 自己如实报空)+ 迁移 0022 删掉 agent_sessions.kernel 列 / 0.21.5 待重启判定只剩地址一个客观量("模式"这个概念整个消失)) │ ├─ test-unit.mjs # 快速单测层(36 用例,<1s):无 IO 纯函数的毫秒级回归(入参校验器 / 中文切词与查询阶梯 / 归一化 / 算钱口径 / 0.19.0 模型目录 provider 按 id 前缀推导规则)—— 改一行纯函数不必付端到端的几十秒;两层各管各的:这层钉"函数语义",端到端钉"装配与协作" │ ├─ test-http.mjs # HTTP 端到端回归(125 用例,自带临时库 + 随机端口;含库级授权的 404 / 403 分离、0.15.0 内核接入两条路由(探测 / 保存,含「检测没通过就 409 且带回完整诊断」、老客户端再传 force 也一样被拒)与「代表性接口响应体里不得出现上游产品名」的运行期扫描 / 0.16.0 平台设置 GET / PUT(env 覆盖键 400、类型强转与范围校验、保存后实时回显)与配额编辑 PUT / 0.17.0 CSV 批量导入(逐行独立 / 只读 403 / 审计 via=import)+ 会话导出(md 与 json 同一事实、注入段剥离并注明、400 / 404、留痕)+ 两步登录 wire(真 HTTP 两步 / 恢复码 / 管理员重置) / 0.18.0 三条新路由进路由表(Skill 卸载 / 文档改名 / 备份删除 —— 备份删除连声明面 method/scope/capability 逐字段断言) / 0.21.0 工作区树与三档权限(CRUD / 可见性 / 三档 / 会话归位 / 模式与推理等级):用两个不同全局角色的探针(viewer 与 member)——只用一个 viewer 验到的是路由层 RBAC,请求根本走不到档位判定那一层;钉住「挡住 view/write 的分别是哪一层(RBAC 给 detail.needs,工作区层给人话档位名)」、「删工作区不级联(detachedSessions 计数 + 会话仍在)」、「模式与推理等级落库后回读」、「workspace.* 动作全部留审计」;另修掉运行期抹名扫描的一处假红(随机 id 里的 dsh 三个字母,改成词边界匹配) / 0.21.6 工作区权限整块删除:三条反向断言 —— 路由表里工作区恰好 4 条且 workspace.grants. 不许回来、树不再下发 levelLabels/grantRoles/myLevel、授权端点对管理员也 404;member 改工作区必须是 403 而不是 404(「看不见就 404」那条随可见性一起删了,这两者的区别只有反向断言钉得住);全组织可见(viewer / member 都看得见里面的会话,读它是 200);canManage 仍用正反对照(member 对自己的 true / 对别人的 false;admin 对别人的 true —— 同一条会话两个角色两个结论,不这样对照「一律 false」也全绿);默认工作区里要有落脚会话且播种幂等(独立租户二次调用不再补、workspace.default.seed 与 session.default.seed 各恰一条) / 0.21.1 判定同源与默认工作区:列表回显 canManage 用正反对照钉住(view 档 false → write 档「自己的 true / 别人的 false」 → full 档 true;未分组 manageNeed: null)——只测一边在「一律要 full」的实现下照样绿;默认工作区播种(独立租户幂等 / 二次调用不改 cwd / 审计恰一条 workspace.default.seed);初始化向导走完即有一个能立刻用的默认工作区(cwd 形态 .../workspace、visibility: 'open'、myLevel: 'full'、ownerUserId = 建组织的那个人) / 0.21.3 内核热切换(保存即生效):两条钉子都必须观测门面本身(读 kernel.ready / kernel.dshStatus)而不只是读响应体 —— 响应体可能只是把请求里的配置回显了一遍,那正是「热切换」的第一种假货;① 保存一个可达地址 → restartRequired:false、kernel.ready 当场变 true、status.restart.pending 变 false(旧行为里这句恒真,它必须有能力变假);② 目标连不上时照切(直接驱动装配层:0.21.5 删掉 force 之后这条路径走不到 HTTP 口了)—— 从前那段「会降级所以先不切」的保护判断,保护的是已删除的内置内核;保护对象没了,剩下的只有「保存什么就是什么」;另钉住 env 覆盖时仍报 restartRequired:true —— 不许因为支持了热切换,就对「文件改了、进程没变」这种情形也谎报已生效。本版最大的坑在装配层:新增 deps 能力 switchKernel 时测试脚手架没注入,接口会如实降级成「需重启」,于是「保存即生效」这条路径在回归里静默测不到(断言照样全绿)—— 少接一根线不会报错,只会让断言测了个寂寞 / 0.21.4:/api/health、/api/system/status、/api/kernel/status 一律报 ready 布尔,不再有 mode;「保存连不上的地址」的钉子改成直接驱动装配层(0.21.5 删掉 force 后走不到 HTTP 口) / 0.21.5:保存口只认「检测通过」 —— 非 ok 一律 409 且带回完整诊断,老客户端再传 force: true 也一样被拒(顺便钉住「别把这条旁路加回来」);写盘仍会顺手清掉老配置里的 kernel 键) │ ├─ test-knowledge.mjs # 知识层端到端(34 用例:中文召回 / 引用链 / 降级 / 重试与重建) │ ├─ test-frontend.mjs # 前端行为回归(226 用例 / 录制 148 条:起真实服务录响应 → vm 里逐片真跑 public/app/.js,分片清单现读 index.html,含事件委托派发与工作台最后一公里;0.13.0 App 成员跨片解析;0.14.0 知识注入标记契约对账 / 0.16.0 行内出口钉(MCP 登记 / 删账号 / 模型编辑 / 会话改名删除 / 配额编辑 / 绑定切换)+ 内联样式护栏(基线 129) / 0.17.0:TOTP 登记与两步登录 UI、部门改名 / 删除弹层(含取消不发请求)、CSV 导入弹层(口令不回显)、会话导出按钮(流式中禁用) / 0.18.0:用户组改名 / 用户姓名编辑 / 调度任务行内编辑 / 通知渠道行内编辑 / 会话日志 log.zip 下载 / Skill 卸载 / 知识文档改名 / 备份删除(弹层护栏与权限收敛) / 0.19.0:工作台「可用能力」面板四段(差距行与无工具清单 RPC 如实标注)、助手正文安全 markdown 子集渲染(javascript: 退化 / 原始 HTML 字面)、工具卡 JSON 富格式 / 0.20.0:工作台「实际调用」面板与工具卡来源对账三态(登记 / 仅探测到 / 未登记,钉住"不按名字前缀猜 MCP")、能力清单页与来源页的工具登记表双来源(含假差距不许渲染) / 0.21.0:工作台左栏改工作区树(工作区 → 会话 → 「未分组」)、输入卡选择器(模式 / 推理等级)、没有会话时的品牌空态页、工作区增改删与会话在工作区之间移动的动作接线(含树过滤与折叠) / 0.21.1:管理动作收进行内「⋯」(菜单是状态不是 DOM 浮层 —— 展开后才有入口、一次只开一个、点空白收起、不误触外层行;浮层的定位/遮挡在 vm 层测不到)、工作区行「+新会话」建在那一行、未分组桶只摆「新建」、模型与推理等级同排到输入卡(其中「工作目录…」真实发 PATCH、view 档禁用写明档位、「工作区权限…」按所在工作区解析后展开三条断言,0.21.6 已随对应动作一并删除) / 0.21.2:工作台改全屏视图(有品牌头、无管理顶栏 / 无页标题)、输入卡新结构(徽片行在卡外、模型与等级与圆形发送钮在 .wb-card-foot 内且 #wb-model 恰一个)、空态不渲染噪音条(知识库条 / 附件条 / 消息流)、过滤器 / 检索栏 / 右栏默认收起(用例须先显式展开)、未分组空桶不渲染、以及全屏工作台自带登出入口(顶栏已不在 —— 这条钉的是真机层抓到的真缺陷) / 0.21.4:未接入内核时的整页阻断(pageKernelGate)—— "每个渲染分支都必须拼 dlgOverlay()"这条不变式多了一个成员;sessionStorage 从 vm 沙箱里删掉("记住本会话关过引导"那个机制随可关闭弹窗一起没了,留着它只会让下一个"没声明的全局"静默通过) / 0.21.5:接入弹框收敛成一屏(那些"不许长"的断言钉的是它 —— 四五屏说明文里"填地址、点检测"会被淹掉);检测没通过就不能保存(本地拦住不发请求 + 服务端 409 兜底);改动输入即作废上一次的检测结论(否则"先探个好地址再改成另一个"就是绕过检测的路);保存成功当场关闭弹框并落到落地页(判据用 switched.ready 而不是"请求返回了 200") / 0.21.6:左栏结构收口与工作区权限整块删除 —— ①工作区行行尾的两个动作恒在 DOM 里(显隐是 CSS 的 hover,不是条件渲染:真机层只能靠 hover 让它们现身,DOM 里没有就等于功能没做),顺序是 ⋯ 在左、+ 在右;②权限模块的删除用双向反向断言钉:8 个已删动作 typeof App[x] === "undefined" 且渲染结果里没有对应 data-act,两个菜单里不许出现「权限」字样,前端一次都不请求工作区授权名单(/api/workspaces/:id/grants 那种形状 —— 知识库的 ACL 是另一码事,不算在内);③左栏顶部「新会话」胶囊与「插件」入口已删、输入卡上方没有 #wb-ws、没有 wb-level-chip(这类删除最容易做成"藏起来",而藏起来的那份会一直在);④行尾「+」在没有 session:write 时是禁用并写明原因的态(权限矩阵是布尔对象,写成数组会把 read 也抹掉 → 落地页直接回落到概览,测到的是另一个页面) / 0.21.7 界面收口:删会话内检索与清屏(反向断言:typeof App.wbRunSearch === "undefined"、wbReset 也没有、输入卡上方找不到 #wb-q,且 wbToggleRail 之外右栏不再有第二颗钮)、删归因徽标并把"选中的"与"绑定的"并成一件事(三条:选择器即绑定项 / 打开会话时按选择器补绑 / 补绑失败静默)、删右栏「实际调用」与「可用能力」两段(改成"工具卡各带来源徽标"正面 + 右栏那两块的反向断言;wbAttributeTool / wbCatalog 与 w.caps 拉取仍在)、图标一律内联 SVG(svg.wb-ico,并把 📁 演示工作区 / ⚙ 标准模式 这类字符断言改成结构断言 —— 按符号断言下次换图标又得改一遍)、新增的 Dlg.browse 目录选择器三态(逐级选 cwd / '' 是有效选择 / null 是取消 / 路径还没读出来就点确定必须拦住,否则空串会被当成"没给 cwd"、静默建出一个没有工作目录的工作区);另有一组直接读样式表的钉子:展开钮不许住在 里(住进去就会被 display:none 一起藏掉)、.wb-sec-tools 收着用 opacity:0 而不是 display:none(后者会让按钮从 Tab 序列里消失)且必须配 :focus-within、--wb-compose-w 必须是纯长度(写进 calc(100%) 会在两个不同父元素上解析出两个值,"同宽"根本不成立)且空态下 .wb-stream 里不许有子节点) │ ├─ test-governance.mjs # 治理层端到端(59 用例:审计链 / 配额 / 产物 / 备份闭环 / MCP 启停与注销 / 0.18.0:Skill 卸载(zip 删目录 / 非落盘与越界只注销 / 审计恰三条)+ 文档改名(不重新解析)+ 备份删除(末份 409 / 形态白名单)) │ ├─ test-stream.mjs # 回合流式回归(24 用例:NDJSON 帧序列 / 中途失败 / 用量归集) │ ├─ test-browser.mjs # ★ 真机浏览器验收(23 场景,0.15.3 引入、0.17.0 增补:零依赖 CDP 驱动本机 Edge/Chrome 无头实例, │ │ # setup→登录→22 页逐路由扫荡→组织身份 / RAG 闭环 / 治理 / 运维四条业务流 │ │ # + UI 不变式四则(窄视口右栏可达与开合 / 对话框 Tab 焦点陷阱 / 侧栏滚动位置跨重绘保持 / │ │ # 工作台收口:左栏收起后可展开 · 节头划过显形 · 正文与输入卡同轴) │ │ # + 全局异常对账(放最后,此前全部场景的未捕获异常一并对账); │ │ # 真实 DOM/JS 是 vm 层的结构性盲区 —— 0.15.1 取消语义、0.15.0 挂库不刷新、 │ │ # 0.14.2 侧栏键盘、0.15.3 createJob/createChannel 表单丢失全是这类; │ │ # 0.21.1 又抓到一次同类:改名按钮收进会话行的「⋯」后, │ │ # "等 [data-act=wbRenameSession] 出现"必然超时 —— 入口在菜单里, │ │ # 要改的是交互路径(先点 ⋯)而不是超时时间;vm 层只验"HTML 里有没有它", │ │ # 只有真机层会真的去找它、点它。
│ │ # 0.21.2 抓到的是一个真缺陷(不是用例写错了):工作台改成全屏视图后 │ │ # 不再渲染顶栏,而「登出」此前只存在于顶栏 —— 进了工作台就再也登不出去。
│ │ # 一条用例把身份换错,后面十几条会集体假红,而错误信息只会说"选择器没找到"; │ │ # 为此 t() 在失败时打一份现场快照(hash / 视图 / 登录是谁 / 外壳元素), │ │ # 并可用 STI_BROWSER_TRACE=1 打开逐场景身份轨迹 —— 先排登录态,再排渲染。
│ │ # 0.21.7 补的三条都是 vm 层的结构性盲区:布局(展开钮收起来之后是否真的 │ │ # 可见、可点、落在视口里)、CSS :hover(br.click() 发的是 JS 合成 click、 │ │ # isTrusted=false,浏览器不触发 hover 伪类 —— 必须走 CDP │ │ # Input.dispatchMouseEvent 发真实指针移动,再读 computed opacity)、 │ │ # 几何(正文列与输入卡的左右边缘差几像素,vm 层连盒子宽度都算不出来)。
│ │ # ⚠️ 另记一条写用例本身的坑:模板字符串里的注释不许出现反引号 —— │ │ # 它会把模板提前闭合、后面的文本被当成 JS 解析,报出一个与现场毫无关系的 │ │ # ico is not defined(排查时先看报错所在的那一行是不是注释)。
│ │ # 找不到浏览器则如实 SKIP,不计入通过) │ ├─ test-external.mjs # ★ 外部内核端到端(12 用例;上游不在场则如实 SKIP) │ ├─ test-external-stream.mjs # ★ 外部内核的增量到达(真上游下的流式;无上游则自报 SKIP) │ ├─ kernel-stub.mjs # ★ 回归用的确定性内核替身(0.21.4 从 app/kernel/ 移到 tools/) │ │ # 只被测试脚手架显式注入(createKernel({ provider: createKernelStub() })), │ │ # 产品路径永远走不到它 —— 没配地址就是"未接入",没有可降级的本地内核 │ ├─ verify-upstream.mjs # ★ 对运行中的上游核实方法名与参数形状(升级后必跑) │ ├─ bench-search.mjs # 检索性能基准(P50/P95 + 预过滤对照的延迟与召回损失,写入 docs/benchmark.md) │ ├─ bench-concurrency.mjs # 并发与长稳基准(吞吐 / P95 / 错误率 / 内存 / 事件循环滞后;开环定速须配 --open-sender=process,写入 docs/benchmark-concurrency.md) │ ├─ bench-scenarios.mjs # 基准场景定义(主进程与独立发包端共用同一份,否则两边压的不是同一个东西) │ ├─ audit-layering.mjs # 分层审计(上游概念是否泄漏到产品层) │ ├─ audit-tenant-isolation.mjs # 多租户隔离静态扫描(隔离表清单从迁移事实生成) │ ├─ audit-orphans.mjs # ★ 模块孤儿 + 断链 import + 前端分片 ↔ index.html 双向对账(0.13.1) │ ├─ audit-coverage.mjs # ★ 覆盖对账(0.15.3):22 页 × vm/浏览器双层锚点 + 143 个 API × 测试语料引用, │ │ # 「全功能可用」不再是口头承诺 —— 漏网即红(白名单 1 条,必须写明理由) │ ├─ pack-release.mjs # 白名单打包 + 打包即扫描(条目名 + 凭据形态 + 令牌形态三重扫描, │ │ # 令牌那一重覆盖 app/ + public/ + tools/ + docs/ —— 凡随包出去的目录都扫; │ │ # 0.15.0 起它本身也是门禁第 14 项 packscan) │ ├─ restore-backup.mjs # 离线恢复(防误覆盖 + 恢复后 integrity_check 自证) │ ├─ adopt-data-dir.mjs # 把非规范命名的库文件采纳成规范名(0.10.1 更名后的升级通道,只复制不删原件) │ ├─ seal-kernel-token.mjs # ★ 把老配置里明文的内核令牌就地封成密文(0.15.0) │ │ # 默认只报告;--apply --force 才写(写前备份,写后往返校验) │ │ # 为什么需要它:0.15.0 之后写的都是密文,但在那之前写下的配置 │ │ # 不会自己变;明文那个形状不报任何错,却会随备份包一起被带走 │ ├─ record-fixtures.mjs # 真实上游事件流录制(脱敏) │ └─ snapshot-upstream-contract.mjs # 上游类型契约快照比对 ├─ docs/ # 架构 / 路线图 / 可行性 / 部署 / 性能基准;文档地图与单一真相源见 docs/README.md └─ data/ # 运行时数据(库、上传、主密钥)—— 不入版本库
📌 开发 / 测试入口只有一个(0.13.1 起):node tools/gate.mjs。
它按固定顺序跑完上面全部检查(部署自检 + 主回归 + 静态审计 + 2 个"要真上游"的外部验收),末行给出一行可核对的结论;换机器、换人、半年后再回来,跑的是同一份清单 —— 清单就写在脚本里。
只想跑其中几项:node tools/gate.mjs --only=core,http;只看清单:--list。
⚠️ 它会如实打印"这个门禁测不出什么"(多机压测、长稳、真上游对话、浏览器里的布局 / CSS / 真实鼠标轨迹与多标签并发、TLS 边界),并且 SKIP 不算通过 —— 无上游时那两项记 ~,不计入 checks=<通过>/<总项> 的分子。
⚠️ 这些脚本各跑各的进程、各起自己的临时库与随机端口,可以单独执行; gate 只是顺序与判据的单点定义,不是新的运行容器。
6. 文档索引
📌 先看 docs/README.md:文档地图与单一真相源。
它写明每份文档该写什么、以及同一条边界陈述以哪一份为准(属主 / 摘要 / 记录三类)。
下表是"有哪几份",docs/README.md 是"该信哪一份"。
| 文档 | 内容 |
| docs/README.md | 文档地图与单一真相源(0.11.2 / 体检 §A5):文档清单、每条边界陈述的属主、三类文档的读法、归档规矩 |
| docs/deploy.md | 部署与运维:三分钟跑起来、自检、配置、上游内核接入、备份、打包、对外服务的 TLS 边界、排障表、当前版本的诚实边界 |
| docs/architecture.md | 分层架构、请求链路、契约层设计、数据模型、关键机制(含 §7.8 前端分片与加载顺序)、规模边界、明确的不做清单 |
| docs/compatibility.md | 上游关系与 DSH 适配层:共享契约层的边界、上游版本兼容策略、历史沿革 |
| docs/versioning.md | 版本号属主:1.0.0 为什么保留给正式版、minor 与 patch 的分工、产品 / 上游 DSH / 内核契约三个序列的区别、完整新旧对照表、改版本的收口清单 |
| docs/roadmap.md | 里程碑拆解、每个里程碑的交付物与验收标准、风险与对策(决策记录,非现状) |
| docs/benchmark.md | 检索性能基准(数字属主):10 万 chunk 规模下的实测 P50/P95、口径说明、暴力扫描的规模边界 |
| docs/benchmark-concurrency.md | 并发与长稳基准(数字属主):三种压法 —— 闭环阶梯(吞吐 / 延迟分位 / 事件循环滞后 / 内存)、开环定速(拐点,须配独立发包端)、30 分钟长稳(内存与延迟漂移) |
| docs/feasibility.md | 可行性与风险评估:四个问题的直接回答、实测证伪的假设、16 条风险矩阵、DSH 升级影响分析、安装部署集成打包方案、6 个决策点(含当前状态) |
| docs/assessment-0.11.0.md | 当前体检(0.11.0):逐层核查、§D1 已关闭 / §D2 仍未关闭、P0~P3 建议顺序表 |
| docs/review-0.13.1.md | 时点核查(0.13.1):一次不采信自述的全量复核 —— 重编号有无改坏、查出的 3 处失真、5 条建议与 7 条明确不做,以及「这个工程的问题已经不在代码里」的判断(记录类,勿当现状读) |
| docs/history/ | 历史快照(已归档):0.6.0 / 0.7.0 两份大检查,开头带 ⚠️ 历史快照 横幅,勿当现状读 |
7. 本项目在关键取舍上站哪一边
下表把最常见的几组对立取舍摆出来,写清本项目选了哪一边以及为什么不选另一边 ——比「我们支持 X」更重要的,是「我们明确不要 Y,理由是什么」。
| 维度 | 常见的那一边 | sti-harness | 为什么这样选 |
| 依赖 | 引入框架与 ORM | 零第三方运行时依赖 | 企业内网交付最难的是「审批一个包」,不是「写一个函数」;内置能力已够用(见 §4) |
| 部署 | 多容器编排 | 双击即用(单进程) | 目标场景是一台生产机;多容器带来的运维成本远大于它带来的弹性收益 |
| 数据 | 外部数据库 / 缓存 / 向量库 | 单文件 SQLite(FTS5 + 向量 BLOB) | 备份 = 拷一个文件;WAL 已满足单机并发;换 ANN 的规模拐点写在 docs/benchmark.md |
| 多租户 | 库级多实例 | 库内租户列 + 静态隔离审计 | 隔离判据必须可自动核对(audit-tenant-isolation.mjs),而不是靠人记得加 WHERE |
| 上游耦合 | 产品层直连上游 API | 契约层收口(AgentKernelContract) | 上游是快速迭代的;把依赖限制在一个目录内,升级时的改动范围才可枚举 |
| 治理 | 上线后再补审计 | 治理与审计是一等公民 | 事后补的审计必然有盲区(历史数据无法追溯);留痕要从第一条记录就有 |
| 能力治理 | 工具也登记成「可治理能力」 | 只有 Skill / MCP 可治理,工具不可 | 平台命令不了工具(无执行路径、上游无工具清单 RPC)—— 登记一份管不住的清单是「第二份真相」(0.21.11 起删除) |
| 界面数据 | 乐观渲染 / 占位数字 | 诚实数据源:取不到就空态 | 一个假的数字会让运维做出错误的判断,代价远高于一块空态 |
| 测试 | 只看退出码 | 退出码 + 约定末行 + 数字为 0,三条同时成立 | 只看退出码会把「脚本没跑到末尾(= 什么都没检查)」读成「通过」—— 假绿比红更坏 |
8. 许可证
Apache License 2.0 —— 全文见 LICENSE,第三方与参考来源说明见 NOTICE。
选它的理由:企业客户常见的内网/自建交付需要一条无传染性的宽松许可;而本项目零第三方运行时依赖,不存在需要兼容的第三方许可,选宽松许可没有代价。
⚠️ 一条必须持续保持的界线:本项目只借鉴思路,不复制任何外部代码 ——全部源码为本权利人独立实现。第三方与参考来源说明见 NOTICE。
Copyright 2026 liwei(易智瑞西安) <liwei@geoscene.cn>. Licensed under the Apache License, Version 2.0.预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
gischina/sti-harness
把智能体接入企业内网,管好权限与知识库
相关插件 · 安全 / 权限
智能体逆向工程morluto/rea
用智能体逆向工程任何东西,从应用行为到原生二进制。
★ 415,267已汉化DSH
73.3
智能体集群框架ruvnet/ruflo
面向 Claude Code 与 Codex 的智能体元基座meta-harness。
★ 80,414已汉化MCP
71.9
WorkBuddy 模型接入corrinehu/dsh-workbuddy-connect
2026 年 10 月 20 日起,WorkBuddy…
★ 1,604已汉化DSH
71.7
破甲一键通z91772524-ai/pojia-next
十五个 AI…
★ 967已汉化DSH
71.6
AI 代理虚拟终端strukto-ai/mirage
Mirage 是给 AI 智能体用的虚拟终端:一层 shell,统管数据、命令行和运行时。Unix…
★ 3,756已汉化DSH
70.8
Agent 操作层agentic-os-org/ANOLISA
ANOLISA(Agentic Nexus Operating Layer & Interface System Architecture)|智能体操作系统,带运行时、安全、可观测性,以及 Tokenless 响应压缩,降低 token 用量与成本。
★ 649已汉化DSH
70.7