npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-perm-gate

v2.6.2

Published

DSH permission-gate for the DeepSeek Harness 0.1.2+ line: a single self-sufficient, deterministic-first, fail-closed gate covering P0 hard-deny -> P1 session grant -> P2 static rule (allow/deny) chain -> P3 optional LLM semantic classifier -> P4 ask, with

Readme

dsh-perm-gate

兼容性说明: v2.0.0 自带 ja / ko 字典,但官方 DSH 的 LocaleRuntime 只暴露 zh / en(LOCALE_IDS = ["zh", "en"])。在原版 DSH 上选择 ja / ko 会报 locale "<id>" is not registered。请使用更新了 LOCALE_IDS (locale-settings.ts)与 LOCALES 标签(client/index.ts)的 DSH fork 并重新构建。

▼ DSH 版本适配

两个 DSH 版本线从两个长期分支分别维护,各有一套版本号系列、engines.dsh 和 npm 分发标签(发布布局):

| DSH 版本 | 分支 | 版本号 | npm 标签 | | --- | --- | --- | --- | | 0.1.0-rc.7 ~ 0.1.1-rc.x | legacy | 1.x | @legacy | | 0.1.2-alpha.1 ~ 0.1.5-rc.0 | main | 2.x | @latest / @dsh-0.1.2 (@2.x 是范围) | | 0.1.5-rc.1+ | sync/0.1.5-from-main(=compat/0.1.5) | 3.x | GitHub ref 安装(暂无 dist-tag) |

版本序列号跟的是 DSH 线(1.x = DSH ≤ 0.1.1,2.x = DSH 0.1.2+),两条大版本互 相隔离:锁在 ^1.x 的安装绝不会解析到 2.x,反之亦然。engines.dsh 表达同样的 分界,但 DSH 从不读取它——真正把旧 DSH 钉在 1.x 上的是版本范围与 dist-tag。

@deepseek-ai/dsh-client-runtime 在 0.1.2-alpha.1 中已被移除——不仅仅是更名。 legacy 线仍通过它访问 ctx.slots;main 从 @deepseek-ai/dsh-client-ui-renderer/client 获得相同的声明。两处版本敏感 接缝通过能力探测处理,而非版本号检查:(1)设置注册使用 register,两线 都存在(installSection 是新增项,不是替代);(2)effectivePolicy 在两 线上都是 user-approval 服务的私有方法,因此通过 typeof 探测读取,缺失 或抛错时降级为「策略未知」。

版本 2.6.1 —— 变更见 Changelog。

一个单一自足、确定性优先、fail-closed 的 DeepSeek Harness 权限门插件。

对每个工具调用按固定优先级链裁决:

| 阶段 | 决策 | 含义 | | ---- | ---- | ---- | | P0 | deny | 确定性硬拒:凭据材料 / 受保护路径改写 / 危险 shell | | P1 | allow | 精确、有界的会话放行 grant | | P2 | deny/allow/ask | 静态规则链:黑名单优先,其次 allow,再 ask | | P3 | allow/deny/ask | 可选 LLM 语义分类器(默认关闭) | | P4 | ask | 官方 approval seam |

严格 fail-closed:P0 永不因 grant / 规则 / 分类器 / 人工而放行。

特性

  • 命令白/黑名单 — 基于 argv 分解匹配(非裸字符串),递归下钻 sh -c/bash -c、识别管道、重定向目标、递归/强制(rm -rf)。
  • deny 优先 — 命中黑名单即拒绝,胜过任何 allow。
  • 会话放行 — 精确的 (工具, 规范化 fingerprint) grant,带 TTL + maxUses;换目标绝不复用。子代理继承但不可自授。
  • 纯函数规则引擎 — glob/regex 编译 + ReDoS 上限、坏规则 loud fail、按源内容哈希缓存。
  • 审计 — 每次决策写为 {ignorable:true} 事件并带 callId;模型可见理由与记录一致。
  • 自动审查档位(机器值 permissive)——一个独立审批模式(区别于只读、完全权限与白名单档),既不是"自动审批",也不授予泛化权限。前端只暴露一个开关(permissive),后台四个审批策略可组合、由插件设置决定——仍对 P0 保持 fail-closed。权限下拉框与设置行都按产品名「自动审查」显示;图标见下文(内置档自带,插件档需补丁)。
  • 沙箱提权自动答复(trustEscalation)— 沙箱提权是从 shell / pwsh / edit 工具体内部(tools/pre-execute 之后)发出的,所以门禁从未见过它,一个它自动放行的调用仍会弹出确认。开启后,门禁以 callId 精确匹配已放行调用并直接答复。

界面预览

设置卡片 —— 自动审查档位:前端只暴露一个开关,后台四个策略可组合;LlmAssist 可跟随会话当前模型,也可钉住 Provider / Model,并自带健康测试与裁决学习:

设置卡片 —— 自动审查档位 LlmAssist —— Provider / Model / API Key

门禁实际加载的规则——直接读自 dsh-perm-gate-rules 设置命名空间;面板按设计只读,改不了它显示的内容:

内置规则(只读)

权限审批记录——本会话每一次裁决,最新在上,附带模型看到的理由:

权限审批记录

裁决条——自动放行会写明依据(只读工具,或 LLM 判定 safe);停摆也绝不静默:

ALLOW 条 —— 只读内置工具 ALLOW 条 —— llm-assist 判定 safe GATE OFF 条 —— 门禁停摆

安装

需要先安装 DeepSeek Harness。

dsh plugin --profile web add dsh-perm-gate

完整的安装、升级、迁移与排查步骤见中文安装指南(另有 English / 日本語 / 한국어)。

配置

cordis.yml:

- id: dsh-perm-gate
  name: dsh-perm-gate
  config:
    rulesFile: ./permissions.yaml   # 可选;默认 $DSH_HOME/perm-gate/rules.yml
    dshHome: $DSH_HOME
    defaultAction: ask
    gatePresets: [permissive, permissive-full]   # 门禁生效的档位(默认值)
    sessionSweep: true              # 每小时清理已归档/已删除会话的门禁数据

会话清扫(session sweep)

插件启动时及每小时读取 DSH 的工作区存储($DSH_HOME/storages/workspace.json,只读), 对门禁持有授权链数据的每个会话做归类。已被 DSH 归档(global.archivedSessionIds) 或彻底不存在的会话,其决策事件会从 $DSH_HOME/perm-gate/events.jsonl 中移除, 其决策前文件快照会从 $DSH_HOME/perm-gate/snapshots/ 中删除——宿主已视为消失的数据, 审查页也不再保留其历史。活跃会话不受影响;无法归属的行(空 sessionId)永不删除; 任何失败都 fail-open:本轮跳过,一小时后重试。设 sessionSweep: false 关闭; workspaceStoreFile 可覆盖存储路径。恢复归档会话不会找回已被清扫的历史。

规则示例:见 examples/permissions.example.yaml。

网络策略(可选开启)

本地 HTTP/CONNECT 代理,用同一份规则文件审查 shell 子进程的出站流量,并对无规则 覆盖的目标提供审批通道。默认关闭 —— 开启后会绑定回环端口并改写子进程的代理环境变量, 因此绝不隐式启用。

- id: dsh-perm-gate
  config:
    networkEnabled: false          # 总开关(默认 false)
    networkMode: whitelist         # deny-all | whitelist | allow-all
    networkUnlisted: ask           # ask | deny —— 未列出目标的处理方式
    networkUnattributed: allow     # allow | deny —— 无 shell 归属的流量
    networkInjectEnv: true         # 为子进程改写 HTTP(S)_PROXY / ALL_PROXY
    networkAskTimeoutMs: 120000    # 审批等待上限,超时按拒绝处理
    networkGrantTtlMs: 1800000     # 一次批准的会话有效期

分层行为:没有 allow 规则,任何目标都出不去。未列出的目标会升级到交互审批,挂在该 shell 命令的会话上;批准后该目标在本次会话内放行。deny 规则永不升级为审批 —— 审批 只能为「无规则禁止的目标」拓宽可达性,永远不能推翻一条说「不」的规则。

边界 —— 依赖它之前请先读这段:代理是协作式策略层,不是强制边界。它只能看到 愿意读代理环境变量的客户端的流量。

| 客户端 | 能拦吗 | |--------|--------| | curl、wget、git、Go net/http、Python requests | ✅ | | Node.js http / https / fetch | ❌ 直连,代理看不到 | | Java(未加 -D 代理参数)、.NET HttpClient | ❌ | | 原始 socket、自写 TCP | ❌ | | DNS、QUIC/HTTP3、非 HTTP 协议 | ❌ | | 连接字面 IP | ❌ |

因此 node -e "require('http').get('http://host/')" 这类命令不会被拦截。请把它当作 「防误操作的护栏 + 声明意图的地方」,而不是密闭沙箱。

DSH 自身的网络流量 —— 内建网络工具与 LLM 传输 —— 刻意不管:这些连接不带 shell 归属, 而 networkUnattributed: allow(默认)会直接放行。审查它们会导致宿主把自己拦死, 那比漏拦严重得多。只有在你确定宿主的客户端不读代理环境变量时,才考虑改成 deny。

实时状态查询:GET /api/dsh-perm-gate/network(模式 / 绑定 / 端口 / 代理存活 / 环境注入 状态 / 阻断计数 / 最近阻断)。

自动审查档位(机器值 permissive)

自动审查是权限下拉框里一个独立审批档,与只读 / 工作区内修改 / 完全权限 / 白名单平行。 它不是泛化的"自动审批"、也不授予泛化权限:只会在人类/LLM 接缝之前收窄或放宽决策, P0 硬拒绝在本门禁自身的档位作用域内始终单调且不可协商。

P0 是档位作用域内的,不是全局的。 门禁只在会话权限档位属于 gatePresets (默认 permissive / permissive-full)时生效。其他档位 —— 只读、工作区内修改、完全权限 —— 下整个门禁停用,包括 P0 硬拒绝,因为该档位自身的策略接管了这个会话。这是刻意设计 (见配置表的 gatePresets),但也就意味着「P0 不可协商」成立于门禁的档位之内,而非所有档位。 停用不是静默的:每次会话档位切换会记录一条 stand-down 事件,浏览器在输入框上方常驻一条 GATE OFF 提示条。把 gatePresets 设为 ['*'] 可让 P0 重新变成全局。

提供两个变体 —— 因为预设的 sandbox 与 approval 是两根独立旋钮,把它们绑死会逼出 一个糟糕的取舍:

| 下拉框名称 | 机器值 | sandbox | approval | |-----------|--------|---------|----------| | 自动审查 | permissive | workspace-write | ask | | 自动审查(高权限) | permissive-full | danger-full-access | ask |

普通档保留内置文件沙箱。而那个沙箱同时拒绝子进程启动所需的命名管道 —— 所以 git clone、 MSYS2/Cygwin 的 sh.exe、ConPTY 都会以 Win32 error 5 / couldn't create signal pipe 失败。 又因为门禁只在 gatePresets 列出的档位里生效,想用门禁就必须接受这个限制。 「自动审查(高权限)」解开了这个耦合:审批行为完全相同,但不限制文件沙箱 —— 档位自带的描述已把代价 写明:流程更顺畅、审批仍逐次生效,但不再有系统沙箱兜底。两者都在默认 gatePresets 里,任选其一都能获得完整的 P0–P4 链路 —— 门禁只读预设的名字,从不读 sandbox 模式。

下拉框里的名字是宿主提供的产品名,不是逐语言的字典项:DSH 对插件档位在两个权限界面上 (通用设置默认档行、输入栏权限选择器)都原样渲染补丁里的 name:,只给三个内置档提供自己的本地化 标签,因此 cordis.patch.yml 直接写中文名,对所有会话一致。

图标是另一回事。 输入栏的图标表是闭合的,表自己的注释写明了规则:host-configured names outside the design set get none。两个档位都不是内置值,图标全靠 npx dsh-perm-gate-patch-glyph 往那张表里补两项 ——「自动审查」复制 workspace-write 的 盾+铅笔,「自动审查(高权限)」复制 danger-full-access 的盾+感叹号,各自对应自己实际共用的 文件沙箱。该补丁改的是宿主包,每次 DSH 升级都会丢 —— 见 DSH 升级后:重打输入区图标补丁。

cordis.yml:

- id: dsh-perm-gate
  name: dsh-perm-gate
  config:
    rulesFile: ./permissions.yaml
    defaultAction: ask
    permissive: true            # 前端唯一的开关(启用独立档)
    permissiveStrategies:        # 后台策略,可组合
      trustAutoAllow: true       # 作用域内安全操作自动放行;危险/未知转 ask
      alwaysConfirm: false       # 一律逐次 ask;允许控件附带重复允许/迁白名单按钮
      trustEscalation: true      # 门禁已放行的调用,其自身的沙箱提权免确认
      llmAssist: false           # 先由 LLM 分类裁决;ask/无分类器时回退到人工

(llmAssist 的真实接收 LLM 在设置页填 classifierEndpoint / classifierModel,OpenAI 兼容的自定义 API 均可。设置页可选择接收来源:自定义 API(任何 OpenAI 兼容端点,内置小米 MiMo https://api.xiaomimimo.com/v1 等预设)或宿主模型组(复用 DSH 会话已配置的 llm 服务与当前模型组,可用 classifierProvider / classifierModel 覆盖);并提供健康测试按钮,一键验证接收 LLM 的连通性与延迟。)

trustAutoAllow 是中间档基线(rule-allow 自动放行)。alwaysConfirm 让每次越界都走审批面板,其 「允许控件」含两个扩展按钮:本会话重复允许该类(会话限次 grant,approveRepeat)与 允许所有类型(把命令词持久写进 permissions.yaml 的 allow 白名单,approveAllowEverywhere)。 llmAssist 调用配置的真实 LLM(任意 OpenAI 兼容 API)自动裁决 ask,结果不确定/出错时回退人工 接缝——始终 fail-closed。trustEscalation(档位开启时默认开)答复门禁已放行的调用在其工具体内 提出的 sandbox_permissions 提权;见下文。permissive 关闭时,门禁行为与之前完全一致。

沙箱提权:为何 safe 裁决仍会弹窗

一个工具调用可能触发两个独立的审批。门禁负责第一个——它的 ask,在 tools/pre-execute 瀑布上。第二个来自工具体内部的 approveEscalation,在 tools/execute 时刻,只要模型传了 sandbox_permissions + justification;此时 tools/pre-execute 已结算,门禁的放行从未到达它。 LLM 评定为 safe 且门禁自动放行的调用因此仍会弹出确认。

trustEscalation 填补这个缺口。门禁记住每个它正面向上放行的调用(以宿主 callId 为键,提权 请求会重复该值),并在本处自行答复 allowed-once。它仅在全部满足时适用:

  • 自动审查档位开启且 trustEscalation 开启;
  • 请求携带门禁放行的 callId,且工具名匹配;
  • 原因为已知的提权,指明 workspace-write 或 danger-full-access。

其余所有情况——未知原因、不同的调用、门禁要求或拒绝的调用、approval: never 透传——都保持交给人 工,因此未来 DSH 措辞变更时 fail-closed。自动答复记录在事件流中 (verdict: "escalation-auto",mode: <目标模式>)。关闭开关可使沙箱放宽保持人工审批,其余 自动放行不变。

权限下拉里可选档位

cordis.patch.yml 在 DSH 的 permission.config.presets 里新增了 permissive preset (sandbox: workspace-write、approval: ask、名称 自动审查),位于工作区内修改与 完全权限之间。DSH 的 bundle patch 对这个 map 是整表替换而非逐键合并,所以该文件还必须重述三个内置档 (read-only / workspace-write / danger-full-access,取自 @deepseek-ai/dsh-base/cordis.patch.yml);test/patch-presets.spec.ts 固定了这份键集合。因此会话权限 下拉里会出现「自动审查」这个独立可选审批档,而不是"auto-approval"档。

门禁只在 gatePresets 列出的档位里生效(默认 ['permissive', 'permissive-full'],即本插件新增的 两个档位)。在其余任何档位 (Read Only、Workspace Write、Full access、custom)里,门禁的判定流程完全不运行:不放行、不弹审批、 不拒绝、不执行 P0 硬拒绝、不做黑名单关键词拦截,也不写审计事件——该档位自己的策略说了算。这正是重点所在: danger-full-access 的定义就是"全权限、不弹审批",用 ask 去覆盖它毫无意义(该档 approval: never 会让审批接缝 在任何 answerer 运行之前直接返回 rejected,被转发的 ask 只能得到 the user rejected tool "...",面板根本不会 弹出),用硬拒绝去覆盖它则等于悄悄推翻用户选定的档位。gatePresets: ['*'] 可让门禁重新全局生效(含硬拒绝层); 在生效档位内,若会话生效的审批策略为 never,ask 仍会降级为放行。

在 UI 里可配置

该档位也可在运行时从 设置 → 插件 → 自动审查 调整(插件浏览器端渲染的 settings.plugins.tab 页面):一个开关切换 permissive,四个开关编辑后台 permissiveStrategies。host 端 live 读取该命名空间,改动对下一条工具调用即时生效,无需重启。 这是一个独立审批类,不是 DSH 的"auto-approval"档。

风险分级 llmAssist、裁决学习与事件流

开启 llmAssist 后,接收 LLM(自定义 OpenAI 兼容端点,或 DSH 宿主模型组——见上文)按结构化协议逐条评估 ask。判定发生在门禁的 tools/pre-execute 瀑布内部、决策返回宿主之前:safe 直接放行,审批面板根本不会出现;只有真正无法确定的判定才会弹到你面前。

  • safe → 自动放行(审计来源为 classifier),不弹面板。
  • risky + 硬风险类别(deletion、credential、remote、system、bulk)→ 维持人工确认(ask)。分类器永不拒绝:拒绝只属于确定性层(P0 硬拒绝、黑名单关键词、显式 deny: 规则),所以被误判的类别永远可协商,而不会变成无法申诉的封禁。硬类别与 neutral 仅保留一条关键区别:永不进入学习,因此反复确认也不可能把它沉淀成自动放行。 (拿模型的判断当拒绝依据是实测出来的问题:一条无害的 git commit -F … 被判 remote,直接自动拒绝——没有面板、也没有可重试的授权入口。)
  • risky:neutral → 若开启 riskLearning(设置卡片内,默认关闭),人工批准且真实执行的 neutral 风险会按 tool|类别 计数;计数达到 riskThreshold(默认 3)且新调用的操作指纹(命令词 + 目标基名)命中已确认样本时,同一操作自动放行。不同目标永不复用该放行。开启学习沉淀(riskSediment,默认开)后,满阈值 key 的确认样本会成为确定性放行规则:指纹精确命中即直接放行、无需再过 LLM——即使关闭 llmAssist 也继续生效;沉淀规则在设置卡片中可见、可管理(终止学习 / 删除样本)。
  • 超时(riskTimeoutMs,默认 20s,重试 1 次)、传输失败与协议外输出均维持原 ask——门禁绝不猜测。

学习状态持久化在插件自有 JSON($DSH_HOME/perm-gate/learning.json 或 learningFile),不写入你的 YAML 规则文件。每次决策都会追加到 $DSH_HOME/perm-gate/events.jsonl(或 eventsFile),并经 GET /api/dsh-perm-gate/events?sessionId=&since= 提供;浏览器端轮询该接口,在输入框上方以提示条展示最新决策(ask 常驻至下一条事件),并在对话视图的「审批记录」页签按时间倒序列出本会话的全部判定。

每次决策涉及的文件都会在改动落地前快照(每事件 ≤5 个文件、单文件 ≤256 KB)到 $DSH_HOME/perm-gate/snapshots/;「审批记录」页签中每个文件 chip 可点开行级改动对比(GET /api/dsh-perm-gate/diff),并可撤销该改动——向会话投递恢复指令(POST /api/dsh-perm-gate/revert)。快照管理条支持按会话或全量清理(GET /api/dsh-perm-gate/snapshots-stats / POST /api/dsh-perm-gate/snapshots-clear)。

转人工的 ask 会被跟踪到人工给出答复为止:一个被动 approval/request 观察者记录封闭结果(allowed-once → 人工通过、rejected → 人工拒绝、cancelled → 人工取消、unavailable → 拒绝,因为不存在审批通道);当观察者无法关联该 ask 时(缺 callId、无 approval 服务、上游监听者短路),由 tools/result 兜底结算同一个 ask。人工通过会显示通过后的学习进度(n/阈值),通知条也会为三种终态分别打标。

插件还内置一份预置黑名单关键词(继承自 dsh-approval-gate 的 DEFAULT_DENY_KEYWORDS: rm -rf、push --force、drop table、mkfs、git reset --hard、docker system prune 等), 调用文本命中任一关键词(大小写不敏感子串)即直接拒绝,且先于白名单 / 授权 / LLM。黑名单在设置 卡片中按列表查看与增删(预置条目带标签,可一键恢复预置);未设置或为空时应用预置列表——黑名单 不会静默关闭。 「自动审查」与「自动审查(高权限)」在选择器里都画图标 —— 图标由安装指南里描述的那次宿主补丁 从各自共用的文件沙箱档位复制而来(workspace-write 与 danger-full-access)。没有该补丁时, 两个档位在所有界面上都是纯文字;它们的标签与门禁不受影响。

CLI(独立 dry-run)

dsh-perm-gate --rules permissions.yaml --tool bash --args '{"command":"pnpm install"}'
dsh-perm-gate --rules permissions.yaml --list

开发

npm run typecheck
npm test
npm run build

许可证

MIT