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-compose-panel

v0.1.3

Published

DeepSeek Harness plugin: Docker Compose projects, container control and live per-service logs in the native right sidebar.

Readme

dsh-compose-panel

English | 中文

DSH CI license: MIT dsh-plugin

一个 DeepSeek Harness 插件:把 Docker Compose 项目放进原生右侧栏——每个项目目录一组、每个容器实时状态、悬停即可 启停或重启、每个服务一个流式日志标签页。

它针对的正是单文件容器面板会做错的那种工作区布局:一个仓库里的多个子目录各自 拥有自己的 compose.yaml。

工作区 ~/work/shop
├── compose.yaml            → 分组 “shop”           2/2 运行中
├── services/api/compose.yaml  → 分组 “services/api” 1/3 运行中
└── tools/mail/compose.yaml    → 分组 “tools/mail”  未启动

截图

右侧栏里的面板:每个项目目录一个可折叠分组,容器状态汇总在标题右侧。

容器面板:每个项目目录一组,每个容器一行,状态右对齐

鼠标停在某行时,状态单元格会被该行适用的操作替代——分组标题上是项目级操作,容器行上是单服务操作。

悬停时操作按钮替代状态栏,结果由横幅提示

点击容器行即为该服务打开流式日志标签页。

单服务日志页,带回放行数、时间戳与换行控制

功能

  • 每个项目目录一组。 项目以目录为键,绝不用 compose 项目名,因此 -p、 COMPOSE_PROJECT_NAME 和 .env 都无法把两套不同的栈合并成一行。
  • 三种发现来源。 运行中与已停止的容器、Docker 已知但当前没有容器的项目、 以及从未启动过的 compose 文件——三者都会出现,所以一个没动过的 compose.yaml 也会显示为“未启动”的项目,而不是消失。
  • 限定在工作区内。 只列出 DSH 工作区内的项目;目录归属按最长匹配确定,工作区 之外的容器会被统计出来而不是被悄悄丢弃。
  • 可折叠分组、状态右对齐、行高固定(指针划过时页面不会跳动)。
  • 悬停操作。 鼠标停在某行时,状态单元格会换成该行适用的操作:项目级的 up -d / restart / stop,或单个服务的同样三项。任何命令都不会在未点击时执行。
  • 实时日志流。 点击服务行即为该服务打开日志标签页;点击项目标题则为整个项目 打开。每个目标保留自己的标签页。
  • 静默刷新。 空闲时每 45 秒、仍有服务在启动时每 8 秒重新读取一次,且不会丢失 滚动位置、已展开的分组或当前选择。
  • 与产品一致的样式:只用 Harness 自己的主题变量,因此自动跟随浅色/深色模式与强调色, 不需要额外设置页。

环境要求

| 要求 | 说明 | | --- | --- | | DSH >= 0.1.5-rc.1 | 实测于 0.1.5-rc.1、0.1.5-rc.2、0.1.7-rc.2。0.1.7 系列需要本插件 0.1.2 及以上,见 DSH 版本。 | | 带 web profile 的 DSH | 插件注册一个右侧栏标签类型和一条 HTTP 路由,二者都由 webServer 提供。 | | 宿主进程 的 PATH 中有 Docker CLI | docker ps、docker compose ls 以子进程方式执行;需要 compose v2 插件(docker compose,不是 docker-compose)。 | | POSIX 系统 | 路径按 POSIX 处理;Windows 未经验证。 | | 至少一个 DSH 工作区 | 只列出已注册工作区之内的目录。 |

DSH 版本

| DSH | 插件版本 | 说明 | | --- | --- | --- | | 0.1.7-rc.2 | 0.1.2 及以上 | 0.1.1 什么都不注册——「容器」标签直接不出现。 | | 0.1.7-rc.1 | 0.1.2 及以上 | 同一个竞态;0.1.2 已对照该版本的发布包核实,未端到端实测。 | | 0.1.5-rc.1 / 0.1.5-rc.2 | 0.1.1 与 0.1.2 均可 | 两者都可用。0.1.2 没有引入新 API——只加了一个 guide 字段和依赖声明——其激活门已在这条线钉定的 cordis 版本上实测通过。 |

为什么 0.1.1 在 0.1.7 上坏了。 客户端半边只会在自己 inject 列表中的服务全部就绪后 才被 apply。sidebarRightTabs 注册表由右栏自己的 apply 提供,而那个 apply 的依赖比本插 件多——0.1.5-rc.2 是 4 个、0.1.7-rc.1 是 6 个、0.1.7-rc.2 是 7 个——所以它落在本插件之后。 0.1.1 只声明了 slots,在注册表尚未存在时就 apply,取不到便直接退出。0.1.2 把 sidebarRightTabs 声明为硬依赖,等它就绪。

关于 0.1.7-rc.1: 已核对两个 0.1.7 候选版的发布包——该版本的右栏同样提供 sidebarRightTabs,且两者钉定同一 cordis(~4.0.4),激活门正是由它实现。因此 0.1.2 在 rc.1 上应当可用,但未在该版本端到端实测。

安装

直接从 GitHub 安装——不必克隆(想固定版本就钉一个 tag):

dsh plugin --profile web add "github:yizhixiaokong/dsh-compose-panel"
# 钉版本:dsh plugin --profile web add "github:yizhixiaokong/dsh-compose-panel#v0.1.3"

从克隆安装(需要改代码时):

git clone https://github.com/yizhixiaokong/dsh-compose-panel.git
cd dsh-compose-panel
dsh plugin --profile web add "$PWD"

从 npm 安装:

dsh plugin --profile web add dsh-compose-panel

三种方式结果一致,也都不需要手工登记:dsh plugin 先跑 pnpm,再读取每个已安装依赖的 package.json——声明了 dsh.bundle 的包,其名字会被追加进 dsh.profile.bundles,由它挂载旁边的 cordis.patch.yml。 (没有该声明的依赖只会作为普通库安装并给出警告。)同样没有构建步骤——客户端部分是随 lib/ 一起发布的手写产物。

随后重启 Harness 以加载客户端产物:

dsh web

卸载:

dsh plugin --profile web remove dsh-compose-panel

客户端部分在宿主启动时完成基线化。在运行中的实例里修改 lib/client.js 不会生效,直到 dsh web 重启——刷新浏览器不够。

更新

插件市场里的「更新」按钮执行的就是下面第一条,命令行等价形式:

# 从 npm 安装的
dsh plugin --profile web add dsh-compose-panel@latest

# 从 GitHub 安装的——钉住想要的 tag;去掉 #tag 则取默认分支最新
dsh plugin --profile web add "github:yizhixiaokong/dsh-compose-panel#v0.1.3"

# 从 checkout 安装的(link:)——checkout 就是安装源
git -C <本仓库路径> pull

更新后要重启 dsh web:客户端半边在宿主启动时基线化,重启前新代码不生效。

使用

  1. 打开右侧栏,在引导页选择 🐳 容器 胶囊。
  2. 每个工作区是一个区块,每个项目目录是一个可折叠分组,标题为目录路径,右侧是 容器状态的汇总。
  3. 悬停在项目标题上可对整个项目操作;悬停在容器行上只对该服务操作。指针在行上时, 操作按钮会替代状态单元格,结果通过一条短横幅提示。
  4. 点击容器行(不是按钮)为该服务打开日志标签页;点击分组标题打开整个项目的日志。

日志标签页有自己的控件:回放行数(tail)、重新打开流、时间戳开关、自动换行,以及 (整项目标签页的)服务名列。

项目如何被发现

一次 list 调用执行三条命令并按目录合并结果:

| 来源 | 命令 | 贡献 | | --- | --- | --- | | 容器 | docker ps -a --format '{{json .}}' | 所有容器,按 com.docker.compose.project.working_dir 标签分组。 | | 已知项目 | docker compose ls --all --format json | 已存在但当前没有容器的项目(其 ConfigFiles 为绝对路径)。 | | 未动过的文件 | 每个工作区一次有界的 find | 从未启动过的 compose 文件:深度 4,并剪掉 node_modules、.git、vendor、dist、build、target、.venv、venv、__pycache__、.next、.cache、.tox。 |

项目目录就是连接键。扫描识别的 compose 文件名包括 compose.yml、 compose.yaml、docker-compose.yml、docker-compose.yaml,以及 compose.<name>.y*ml / docker-compose.<name>.y*ml 变体。

HTTP 接口

宿主部分注册一条前缀路由 /compose/api。所有响应使用同一信封: 成功为 {"ok":true,"value":…},失败为 {"ok":false,"error":{"code","message"}}。

任何命令执行之前,请求先经过围栏校验。两个 POST 方法要求 Host 为 loopback 地址(localhost、127.0.0.1、::1、*.localhost,可带端口)或位于 web runtime 的信任列表中;被浏览器标记为 Sec-Fetch-Site: cross-site 的请求会被拒绝; 如果带 Origin,其 host 必须等于 Host。日志流使用读侧变体:socket 对端必须是 loopback(或 Host 受信任)、不得携带转发头(forwarded、x-forwarded-for、 x-real-ip),且允许缺少 Origin——浏览器在同源 EventSource 上不会发送它。

POST /compose/api/list

// 请求——root 可选,不传即列出所有工作区
{ "root": "/home/me/work/shop" }
// 响应 value
{
  "workspaces": [{ "path": "/home/me/work/shop", "title": "shop" }],
  "groups": [{
    "root": "/home/me/work/shop",
    "title": "shop",
    "scanError": "",
    "projects": [/* … */]
  }],
  "total": 7,       // 所选工作区内的容器数
  "outside": 2,     // 找到但在任何工作区之外的容器数
  "standalone": 1,  // 没有 compose 项目标签的容器数
  "notices": []
}

POST /compose/api/action

{
  "action": "restart",          // "up" | "stop" | "restart",封闭集合
  "dir": "/home/me/work/shop",  // 项目目录,必须在某个工作区内
  "service": "api",             // 可选;省略表示整个项目
  "files": ["/home/me/work/shop/compose.yaml"]  // 可选,传给 -f 的 compose 文件
}
// 响应 value;code 是 docker 的退出码,output 是其合并输出
{ "action": "restart", "dir": "…", "service": "api", "code": 0, "output": "" }

这里刻意没有 down:删除容器与数据卷不属于一次侧栏点击该做的事。service 必须匹配 ^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$,每个 -f 路径都必须位于 dir 之内。argv 直接交给 spawn,从不经过 shell。

GET /compose/api/logs(Server-Sent Events)

| 参数 | 含义 | | --- | --- | | dir | 项目目录(必填,必须在工作区内)。 | | service | 单个服务;省略即整个项目。 | | tail | 首次回放行数:默认 200,限制在 1…5000。 | | timestamps | 为 1 时加上 --timestamps;默认关闭。 | | file | compose 文件(-f),可重复。不传时宿主从 docker compose ls 推导,再退化为浅层 find。 |

帧为 data: JSON 对象:

{ "open": true, "command": "docker compose -f … logs -f --tail 200 api" }
{ "out": "api-1  | listening on :3000\n" }   // stdout 分片,已按 UTF-8 安全切分
{ "err": "…" }                               // stderr 分片,结构相同
{ "eof": true, "code": 0 }                   // docker 退出
{ "error": "spawn failed: …" }

日志流带 NO_COLOR=1,每 15 秒发送一次注释 ping 以便发现断开的对端,累计 4MB 后停止; 标签页关闭、刷新或跳转时立即结束 docker 进程——logs -f 自己永远不会退出。

限制与已知行为

  • 其余操作只读。 只有 up -d、stop、restart 可达,侧栏不会以其他方式改变状态。
  • 标签解析。 docker ps 把 compose 项目标签作为逗号分隔字符串返回;compose 标签 从不包含逗号,因此按 , 切分在此是安全的。
  • 日志前缀。 Docker 自带的 <service> | 前缀在整项目标签页中保留;在单服务 标签页中默认隐藏,因为它只是在重复标签页标题。
  • 首次加载。 首次 list 会为每个工作区执行一次 find;目录树极大时面板需要一点 时间才填满,之后的刷新会复用上次结果。
  • 不依赖 shell 插件。 容器状态来自 Docker CLI,因此 Docker 守护进程停止时会显示 错误提示,而不是一个空面板。

开发

lib/index.js        宿主部分——路由、发现、docker 调用
lib/client.js       客户端产物——侧栏标签页、列表、日志查看器
cordis.patch.yml    本包贡献的那一行宿主配置
scripts/smoke.mjs   离线检查(不需要 Docker、Harness 或网络)
npm test          # node scripts/smoke.mjs

冒烟测试会用桩全局变量物化客户端产物,并把宿主部分应用到桩上下文。它锁定两类在线 排查代价很高的故障:工厂内绑定在声明之前被读取(会在 profile 启动时抛错并连带卸载 其后的每个插件行),以及侧栏座位或路由集合发生变化。

修改 lib/client.js 或 lib/index.js 之后需要重启 dsh web——客户端产物在启动时 完成基线化。

仓库说明

版本 tag 与 package.json 保持一致——当前这棵树是 v0.1.1。

package.json、CHANGELOG.md、徽章以及上文安装命令中的 yizhixiaokong 占位符代表本仓库 将要推送到的 GitHub 账号,发布前请替换。本仓库的提交使用中性的 dsh-plugins <[email protected]> 身份,以免把个人邮箱带进公开历史;若希望署自己 的名字,可设置 git config user.name / user.email 后在推送前执行 git commit --amend --reset-author。

进入插件市场

awesome-dsh-plugin 的插件列表由「一个插件一个 YAML 文件」生成,其 CI 会拿本仓库核对提交的形式。机械 层面的要求本仓库已经满足:package.json 中与 cordis.patch.yml 并列声明了 dsh.bundle;官方的 @deepseek-ai/* 包按规范声明为 peerDependencies(本包并未 导入它们,但本包是一个 Cordis 插件);没有任何需要安装的依赖,也没有构建步骤。剩下 的属于仓库所有者的操作:

  1. 加上 dsh-plugin topic—— gh repo edit yizhixiaokong/dsh-compose-panel --add-topic dsh-plugin;

  2. 让仓库创建满 1 天(列表的 CI 会拒绝更年轻的仓库);

  3. 提一个 PR,只新增 data/plugins/yizhixiaokong__dsh-compose-panel.yml:

    url: https://github.com/yizhixiaokong/dsh-compose-panel
    name: yizhixiaokong/dsh-compose-panel
    category: dev
    description:
      en: 'Lists the Docker Compose projects of your DSH workspaces in the right sidebar, one group per project directory, with container status, project- and service-level up/stop/restart, and streamed per-service logs.'
      zh: '在右侧栏列出各 DSH 工作区中的 Docker Compose 项目,按项目目录分组,显示容器状态,支持项目级与服务级 up/stop/restart 以及单服务日志流。'

市场详情页的截图条读取与 package.json 并列的 screenshots.json(1–8 张图);本仓库 声明的是 assets/01-projects.png、assets/02-actions.png、assets/03-logs.png,按此 顺序展示。

发布到 npm

npm login --registry=https://registry.npmjs.org   # 镜像既不能登录,也不能发布
npm publish --registry=https://registry.npmjs.org

npm publish --dry-run 只打印将要上传的内容而不上传;prepublishOnly 会先跑一遍 npm test。已发布包的 repository 字段指回本仓库——列表据此把 npm 包与条目关联, 随后展示的就是更短的 dsh plugin --profile web add dsh-compose-panel,而不是 GitHub 形式。

许可证

MIT