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

容器化 Web 界面 devockr/idsh

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

让 DSH 更强大。

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · platform
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 16 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 39 处,展示前 35 条)
  • 读取本地文件
    packages/host-broker/src/broker.ts:142
    const stats = await stat(resolved).catch(() => null)
  • 读取本地文件
    packages/host-broker/src/broker.ts:163
    await access(candidate, fsConstants.X_OK)
  • 写入本地文件
    packages/host-broker/src/broker.ts:270
    await mkdir(path.dirname(auditPath), {
  • 读取本地文件
    packages/host-broker/src/broker.ts:275
    const audit = await stat(auditPath).catch(() => null)
  • 写入本地文件
    packages/host-broker/src/broker.ts:277
    await chmod(auditPath, FILE_MODE).catch(() => null)
  • 删除本地文件递归
    packages/host-broker/src/broker.ts:282
    await rm(buildRoot, { recursive: true, force: true }).catch(() => null)
  • 写入本地文件
    packages/host-broker/src/broker.ts:283
    await mkdir(buildRoot, { recursive: true, mode: DIRECTORY_MODE }).catch(() => null)
  • 写入本地文件
    packages/host-broker/src/broker.ts:299
    await chmod(options.socketPath, SOCKET_MODE)
  • 删除本地文件
    packages/host-broker/src/broker.ts:306
    await unlink(options.socketPath).catch(() => null)
  • 删除本地文件
    packages/host-broker/src/broker.ts:323
    await unlink(options.socketPath).catch(() => null)
  • 写入本地文件
    packages/host-broker/src/broker.ts:704
    return appendFile(file, `${JSON.stringify(record)}\n`, { mode: FILE_MODE })
  • 写入本地文件
    packages/host-broker/src/broker.ts:716
    const created = await mkdir(directory, {
  • 读取本地文件
    packages/host-broker/src/broker.ts:721
    const existing = await stat(directory)
  • 读取本地文件
    packages/host-broker/src/broker.ts:730
    const socket = await lstat(socketPath).catch(() => null)
  • 删除本地文件
    packages/host-broker/src/broker.ts:740
    await unlink(socketPath)
  • 执行外部命令来自配置/变量
    packages/host-broker/src/sources.ts:146
    const child = spawn(program, args, {
  • 写入本地文件
    packages/host-broker/src/sources.ts:198
    .then(() => appendFile(logPath, chunk, { mode: FILE_MODE }))
  • 写入本地文件
    packages/host-broker/src/sources.ts:227
    .then(() => appendFile(logPath, chunk, { mode: FILE_MODE }))
  • 读取本地文件
    packages/host-broker/src/sources.ts:370
    info = await stat(filePath)
  • 读取本地文件
    packages/host-broker/src/sources.ts:408
    return await readFile(path.join(ctx.procDir, file), 'utf8')
  • 读取本地文件
    packages/host-broker/src/sources.ts:466
    names = await readdir(ctx.procDir)
  • 读取本地文件
    packages/host-broker/src/sources.ts:480
    const stat = await readFile(path.join(ctx.procDir, name, 'stat'), 'utf8')
  • 读取本地文件
    packages/host-broker/src/sources.ts:485
    const status = await readFile(path.join(ctx.procDir, name, 'status'), 'utf8')
  • 执行外部命令来自配置/变量
    packages/host-broker/src/sources.ts:555
    const child = spawn(
  • 写入本地文件
    packages/host-broker/src/sources.ts:586
    void appendFile(logPath, `compose did not finish within ${String(COMPOSE_TIMEOUT_MS)} ms\n`, {
  • 写入本地文件
    packages/host-broker/src/sources.ts:631
    void appendFile(logPath, `spawn failed: ${error.message}\n`, {
  • 读取本地文件
    packages/host-broker/src/sources.ts:792
    text = await readFile(path.join(ctx.stateDir, 'rollback'), 'utf8')
  • 写入本地文件
    packages/host-broker/src/sources.ts:1203
    await appendFile(
  • 删除本地文件递归
    packages/host-broker/src/sources.ts:1512
    await rm(dir, { recursive: true, force: true }).catch(() => null)
  • 写入本地文件
    packages/host-broker/src/sources.ts:1524
    await mkdir(root, { recursive: true, mode: DIRECTORY_MODE })
  • 删除本地文件递归
    packages/host-broker/src/sources.ts:1549
    await rm(dir, { recursive: true, force: true }).catch(() => null)
  • 读取本地文件
    packages/host-ops/src/stream.ts:82
    return statSync(file).size
  • 读取本地文件
    packages/host-ops/src/stream.ts:93
    size = statSync(file).size
  • 发起网络请求
    packages/mobile/src/client.ts:124
    const response = await fetch(link.href)
  • 发起网络请求
    packages/privileged-ui/src/client.ts:88
    const response = await fetch('/idsh/trusted', {
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
idsh GitHub Actions 容器化的 DeepSeek Harness(dsh)Web UI。 包 | 包 | 说明 | 版本 | | @idsh/mobile | 触摸 CSS、回车换行、主屏图标 | | | @idsh/privileged-ui | 在非回环来源上提供设置页,把决策交给 dsh | | | @idsh/remote-access | 连接卡住时,保持 UI 的多路复用 WebSocket 存活 | | 文档 pnpm docs:dev 会提供这份 README、CHANGELOG.md、每个包的 README.md 和 CHANGELOG.md,以及镜像的 changelog;pnpm docs:build 会把静态副本写入 dist/。 Markdown 文件就是源文件——没有需要同步的东西。
展开完整文档(README 剩余 1 节)
idsh

GitHub Actions

容器化的 DeepSeek Harness(dsh)Web UI。

包

| 包 | 说明 | 版本 |
| @idsh/mobile | 触摸 CSS、回车换行、主屏图标 | |
| @idsh/privileged-ui | 在非回环来源上提供设置页,把决策交给 dsh | |
| @idsh/remote-access | 连接卡住时,保持 UI 的多路复用 WebSocket 存活 | |

文档

pnpm docs:dev 会提供这份 README、CHANGELOG.md、每个包的 README.md 和 CHANGELOG.md,以及镜像的 changelog;pnpm docs:build 会把静态副本写入 dist/。
Markdown 文件就是源文件——没有需要同步的东西。

环境要求

- 带 BuildKit + Docker Compose v2 的 Docker

快速开始

1. 提供宿主机 uid/gid(export 出来,或者写进 .env
export UID # both shells define the real UID
export GID="$(id -g)" # bash defines only UID; zsh defines GID too

.env 已加入 gitignore,放在工作目录里是安全的。

DeepSeek API key 不是环境变量:容器启动后,在 Web UI 的设置里添加一次即可。它保存在状态目录中,因此重建后依然存在,也永远不会进入镜像或进程环境。

2. 用模板创建 compose 文件并构建:

cp docker-compose.example.yml docker-compose.yml
docker compose up -d --build

docker-compose.yml 本身已加入 gitignore:这份副本承载你自己的主机名、局域网地址和代理设置,这些内容不该出现在公开仓库里。

模板只挂载 dsh 自己的状态;需要什么就自己加(见「端口与卷」)。

3. 从日志里取出一次性 token:

docker compose logs dsh
dsh web: http://<the address it bound>:3080/?token=...
take the token from that line; the address in it is the one inside the container,
so open it through the published port as in the next step (--public-url replaces
the address when set, and then the logged URL is the one to open)

4. 在 Docker 主机上打开(端口发布在 15080,仅回环地址):

http://127.0.0.1:15080/?token=<token>

它只在主机上响应:模板把发布的端口绑定到 127.0.0.1,所以要么在那台机器的浏览器里访问,要么通过那里的反向代理访问。想在所有网卡上发布,只需改一行,见「端口与卷」。

entrypoint 决定服务器绑定哪个地址:它把容器自身的地址交给 dsh,而发布的端口正是转发到这个地址——容器内部监听 127.0.0.1 的进程收不到这种连接。显式指定 --host 优先,DSH_BIND_HOST 会覆盖这个选择;设 DSH_BIND_HOST=127.0.0.1 可让服务器留在回环地址上,此时发布的端口够不到它,你得改走容器自己的网络命名空间(docker exec,或 docker run --network container:dsh)。只绑定一个地址意味着:端口一旦发布,每个局域网地址都能访问 UI;对外暴露前,先读 @idsh/remote-access 的安全说明。

镜像还会把这个包预置进一份全新的配置档,它在那里的实际职责是 UI 的多路复用 WebSocket:它会调高心跳间隔,让连接在卡顿时也能存活。它为绑定准备的 0.0.0.0 兜底地址不可达,因为 entrypoint 已经定义了地址,而且 0.2.1-alpha.2 及之后的版本无论通配符来自哪里都会拒绝它。因此,禁用这个包——在 UI 的 Plugins 页面操作,或从 dsh.profile.bundles 里删掉——不再能把服务器退回回环地址;做这件事的是 DSH_BIND_HOST。两种情况下都不会卸载任何东西,因为镜像声明的包会在下次启动时放回。

token 每次重启都会轮换。

配置

环境变量(见 docker-compose.example.yml):

| 变量 | 用途 | 默认值 |
| PUID / PGID | dsh 运行所用的 uid/gid(bind mount 的文件也归它所有);compose 会转发宿主机的 $UID/$GID | $UID / $GID |
| NPM_REGISTRY | npm/pnpm/yarn 的 registry:镜像构建和用户级配置都用它(GitHub Actions 上的发布构建传官方源) | https://registry.npmmirror.com/ |
| DSH_TRUSTED_HOSTS | 空格分隔的 host authority,dsh 的浏览器信任围栏放行这些来源 | — |
| DSH_PUBLIC_URL | 对外公布的 HTTP(S) 根地址,用于前面有剥离前缀的代理时;配合 DSH_TRUSTED_HOSTS 一起用 | — |
| DSH_BIND_HOST | dsh 绑定的具体地址,默认绑定容器自身的地址(拒绝通配地址):设为 127.0.0.1 就放弃远程访问;有多个网络导致默认值有歧义时,指定其中一个地址 | 本容器 |
| http_proxy / https_proxy / all_proxy / no_proxy | dsh 发起 HTTP 请求时使用的出站代理 | — |

启动时 registry 会写进 ~/.npmrc、~/.yarnrc 和 ~/.yarnrc.yml(corepack 和 node-gyp 的镜像随之跟进),不写进环境变量——所以项目自己固定了 registry 的,仍然以项目为准。因此 NPM_CONFIG_REGISTRY 和 YARN_NPM_REGISTRY_SERVER 不会被读取;从旧 compose 文件里删掉它们。没有默认值的变量会原样透传按名称处理,所以未设置的值会保持未设置,不会变成空值。

端口与卷:

- 127.0.0.1:15080:3080 —— 宿主机端口 15080 → dsh 内部 web 服务器(3080),绑定到回环地址,只有宿主机上的反向代理能访问。改成 15080:3080 即可直接发布该端口;此时访问该端口所用的任何 authority 都必须被围栏信任。
- ./.dsh → /home/app/.dsh —— dsh 状态与凭据(持久化)。这是模板唯一的挂载。
- 其余挂载自行添加,例如用 ./workspace:/home/app/Workspaces 挂工作区,或用 ./ssh:/home/app/.ssh 挂 ssh 配置。模板刻意不规定这些。

发布

packages/ 下的配套包用 changesets 发布。改动其中某个包的 pull request 要带一个 changeset(pnpm changeset);合并到 main 后, Release 工作流会把所有待处理的 changeset 汇总成一个「Version Packages」pull request,再合并那个 pull request,就会把升过版的包发布到 npm。
包清单里的 version 字段是这条流程的产物,不要手动改。

镜像里固化的内容随这次发布一起变动:pnpm run version 会运行 tools/sync-image-pins.mts,所以版本 pull request 会把 Dockerfile 里的 dsh plugin … 行升到本次要发布的版本。镜像在 image/ 里有自己的版本号,那是个只承载版本号的私有包;某次发布若改动了镜像固化的内容,就要在它的某个 changeset 里升这个版本号——没有 changeset 升它的发布,CI 会拒绝。版本号一动,工作流就构建镜像,并以 :latest、:sha-<commit> 和 :<version> 推送到 ghcr.io/devockr/idsh 以及 Docker Hub 上的 devockr/idsh,这样部署时可以直接拉取,不必自行构建。GHCR 会把每个新包建成私有:它继承的是所关联仓库的权限,不是可见性。所以要么把 idsh 改成公开一次(包设置,然后 Danger Zone——改回私有就再也改不回来了),要么带 token 拉取。Docker Hub 两样都不需要:那里的仓库默认就是公开的。

包页面在 amd64 和 arm64 旁边会列出 unknown/unknown 行:那是构建的 provenance 与 SBOM 证明,写进同一个 manifest list,本身没有平台。有了它们,才能把某个 digest 追溯到产生它的 commit 和工作流。

说明

- 以你的宿主机用户身份运行 —— 容器以 root 启动,把它的 app 账户重新指向 PUID/PGID,修正 home 骨架的属主(bind 挂载不动),然后用 setpriv 降权。挂载里的文件属主是你,不是容器的 uid。
- 一次性命令 —— docker exec 进去是 root;要用工作用户开 shell,用 docker exec -u app …;要执行任意 dsh 调用,用 docker exec dsh dsh …。
额外的 docker run <image> <args> 会追加在 dsh 的 web
- flags —— 因此它们无法覆盖 --profile web。
- 沙箱 —— dsh 的文件沙箱需要用户命名空间或 Landlock。主机没有这些能力时(例如 Synology 的 4.4 内核),要么设置 DSH_PERMISSION_MODE=danger-full-access,要么在 UI 提示时批准 danger-full-access 提权。
- 插件 —— 镜像在构建时烘焙一个配置档:dsh 自带的 web 模板,加上它的配套包 —— @idsh/remote-access、@idsh/privileged-ui(局域网内的设置页,镜像过去用 sed 把它塞进 dsh)和 @idsh/mobile(触摸 CSS、回车换行、主屏图标)—— 按包名从 npm 安装(dev 镜像,也就是我们在本地跑的镜像,改为把本检出里的 packages/ 以 link: 形式烘焙进去)。没有配置档的部署从这一份起步;已有配置档则把这些包保持在镜像声明的版本 —— 按每个包自己的 package.json 比较 —— 把缺失的、损坏的或低于该版本的包补回去,补的方式与镜像安装时完全一致(dev 镜像里是链接,运行的是挂载在 /opt/dsh/packages 的已构建检出 —— 改代码、重新构建、重启;其余情况是文件)。版本等于或高于该版本的一律不动,固定版本或 fork 就住在这里;关闭某个包的做法是把它从 bundle 列表里去掉(UI 的 Plugins 页),而不是卸载它。插件放在 .dsh 挂载里,重建后依然存在。
- UI 探针 —— node tools/dev/ui-probe.mts 会在隔离的 $DSH_HOME 里启动一个一次性实例,用真实浏览器驱动它,这样无需碰线上配置档就能检查客户端插件的运行时行为。它安装镜像烘焙的那套插件 —— 也就是部署实际拿到的东西 —— --install <pnpm-spec> 在此基础上追加或替换,检出或候选包就是这样探测的;--bare 只从随附模板起步,用来缩小范围、定位是哪个插件出了问题。它需要 :dev 镜像 —— pnpm dev 从检出构建它,docker-compose.dev.yml 带着它读取的浏览器设置,这需要 Compose 2.24.4+ 才支持其中的 !reset 标签 —— 也就是带浏览器库的那个镜像;--no-browser 跳过浏览器,改为检查服务端返回的启动图。
- 远程暴露 —— 到达 UI 的是入口点绑定的地址和你发布的端口:发布的端口转发到那个地址,绑定同时把它放进 dsh 的浏览器信任围栏。除非网络可信,否则在前面加上 TLS 和认证(或 VPN);这个包本身做什么,见 @idsh/remote-access 的 README。
- HTTPS 反向代理 —— 如果用 nginx 挡在前面,保持 Host 头不变(proxy_set_header Host $http_host;),好让浏览器信任围栏通过,并加上 WebSocket 升级头。

赞助者与支持者

赞助者与支持者

赞助者

| 1stG | RxTS | UnTS |

支持者

| 1stG | RxTS | UnTS |

许可证

MIT © JounQin
预览分享卡片长什么样 ↓devockr/idsh
分享 · DeepSeek Harness Hub
devockr/idsh

让 DSH 更强大。

这是你的仓库?认领条目

回到顶部