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-sync-client

v0.1.7

Published

DSH configuration sync client: host-plane plugin row that syncs agent presets and settings namespaces with dsh-sync-server.

Readme

dsh-sync-client

DSH 配置同步系统的客户端:一个 host 平面的 Cordis 插件包(bundle),把用户自建的 agent preset($DSH_HOME/.agent-presets/)、用户 skills($DSH_HOME/skills/)与 白名单 settings 命名空间,通过内容寻址方式同步到自托管服务端 dsh-sync-server。零运行时依赖(核心引擎纯 Node 标准库);DSH 接入层遵循本部署的 @deepseek-ai/* 包约定。

组成

src/
├── index.js           # Cordis 插件(对象式导出):工具注册 + 周期/事件触发 + HTTP 桥接路由
├── client.js          # 浏览器半部(clientModules):状态徽标 + 冲突面板
├── bridge.js          # host→浏览器 JSON 桥(/dsh-sync/* 路由的纯逻辑,可测试)
├── engine.js          # 3-way 同步引擎(扫描→差异→push/pull/冲突)+ 根适配器
├── scanner.js         # 预设/技能树扫描与忽略规则(node_modules/.git/超限文件)
├── manifest.js        # 本地持久状态(设备 ID、base 清单、上次同步结果)
├── transport.js       # dsh-sync-server /v1 HTTP 客户端(重试/退避/超时)
├── credentials.js     # 状态目录凭据文件(0600,CLI 与降级路径)
├── settings-adapter.js# settings 命名空间根适配器(JSON 快照 + replace 回写)
├── paths.js           # DSH_HOME / 预设根 / 技能根 / 状态目录解析
└── cli.js             # 独立 CLI:register / status / now
test/                  # 64 个用例:单元 + fake server e2e + bridge + UI
cordis.patch.yml       # bundle 补丁:向组合插入本插件行

Web UI(M3,经 clientModules 交付)

package.json 声明 dsh.client = { platform: "web", inject: [...] }, exports["./client"] 指向 src/client.js;dsh-client-modules 服务扫描后将其作为 /plugins/dsh-sync-client/client.js 注入页面。

| 位置 | 内容 | | --- | --- | | sidebar.footer.action | 状态徽标:状态点(绿=已同步 / 橙=有冲突 / 红=错误 / 灰=未配置)+ 冲突数量角标;提供“同步”(双向)和“获取”(仅拉取)两个按钮 | | settings.section(“数据同步”页) | 状态/设备/上次同步/最近结果/错误 + “配置”按钮(打开弹窗填写服务端地址和管理密钥,点击“保存”注册或切换连接)+ “同步”和“获取”按钮 + 冲突裁决 |

数据通道:浏览器同源 fetch → host 半部 ctx.webServer 注册的 JSON 路由:

| 路由 | 方法 | 说明 | | --- | --- | --- | | /dsh-sync/status | GET | 状态 + 冲突列表 + 动态配置能力 | | /dsh-sync/setup | POST | 运行时注册并切换连接(body: { serverUrl, adminToken, deviceName? }) | | /dsh-sync/sync | POST | 立即同步(body: { direction }) | | /dsh-sync/conflicts | GET | 冲突列表 | | /dsh-sync/resolve | POST | 裁决(body: { path, choice }) |

路由逻辑在 bridge.js(纯函数,返回 { ok, ... } 信封),由测试直接覆盖;UI 本身 只做渲染与轮询(30s)。同源信任模型与参考插件 dsh-plugin-deepseek-usage 一致 (仅本机 Web 界面可达)。

安装(M2 形态,M4 会做成正式发布流程)

  1. 打包:npm pack(生成 dsh-sync-client-0.1.7.tgz)。

  2. 安装进 web profile(把本包作为 bundle 插入组合,行内 name 走 node_modules 解析):

    dsh plugin --profile web add ./dsh-sync-client-0.1.7.tgz

    等价手工方式:把包放入 $DSH_HOME/profiles/web/node_modules/,并在 $DSH_HOME/profiles/web/package.json 的 dependencies 与 dsh.profile.bundles 中登记(桌面端还会同步 .dsh-desktop-plugin-sync.json)。

  3. 重启 DSH,行 dsh-sync-client 生效:启动即跑一轮初始同步,之后按 syncIntervalMs 周期同步,并在 settings/updated 与预设目录下的 fs/observed 事件时防抖触发。

注册设备(三选一)

方式 A — 独立 CLI(推荐,无需把管理令牌交给模型):

# 在要同步的机器上执行一次:
node src/cli.js register --server https://sync.example.com --admin-token <管理员令牌> --name laptop-a
node src/cli.js now        # 立即跑一轮同步
node src/cli.js status     # 查看状态

CLI 只同步 presets/ 与 skills/ 文件根(无 DSH 运行时);settings 命名空间由 已装行的插件在进程内同步。CLI 注册或切换服务端时会自动重置 3-way base,与插件 行为一致。

方式 B — “数据同步”设置页(弹窗配置):点击“配置”按钮打开弹窗,填写服务端地址和 管理密钥,点击“保存”即可在运行时注册或切换服务端,不需要重启。管理密钥只用于本次注册 请求且保存后随即从表单清空;本地仅保存服务端签发的设备 token。弹窗支持“取消”、点击 遮罩或按 Esc 关闭。

方式 C — 会话内工具 dsh_sync_setup:传 serverUrl + adminToken(仅首次)注册 并存储凭据;之后所有 dsh_sync_* 工具可用。设备凭据同时写入 DSH credentials 服务的 grant record(dsh-sync/device)与状态目录文件(0600)。

行配置(cordis.patch.yml 或组合行 config)

| 键 | 默认 | 说明 | | --- | --- | --- | | serverUrl | '' | 尚未动态配置时使用的初始服务端地址 | | settingsNamespaces | agent-default-model, ui-theme, agent-presets, llm-pi-ai | 非敏感 settings 命名空间白名单;内联 secret 会整项跳过 | | presetRoot | 自动 | 覆盖预设根(默认取 agentPresets.roots 的 user 根或 $DSH_HOME/.agent-presets) | | skillsRoot | $DSH_HOME/skills | 覆盖用户技能根 | | syncIntervalMs | 300000 | 周期同步间隔(≥10s) | | maxFileBytes | 1048576 | 单文件同步上限(与服务端一致) | | syncCredentials | false | 选择开启:把凭据文档 $DSH_HOME/.credentials.yaml 一并同步(见下节;服务端明文存储) | | disabled | false | 关闭插件 |

插件只挂载上表中的明确白名单根,不会递归扫描整个 dshdesktop。.credentials.yaml 默认不同步(可用 syncCredentials 开启,见下节);.dsh-sync/、sessions/、 storages/、profiles/、runtime/、npm-cache/、npm-cli/ 以及 dsh-desktop.db* 始终不进入同步;permission 设置可显式加入白名单,但默认不跨设备复制安全策略。

凭据同步(可选,默认关闭)

开启 syncCredentials: true 后,凭据文档 $DSH_HOME/.credentials.yaml (credentials 服务托管 refs: 环境密钥与 records: 授权记录的文件)作为 credentials/credentials.yaml 参与同一套 3-way 同步。要点:

  • 明文上传:服务端 blob 内容寻址、不加密。只在自建、TLS、自己控制的服务端上 开启(E2E 加密是 M5 计划,在此之前服务器能看到全部密钥)。
  • 热生效:本地 credentials 服务 watcher 会在文件被 pull 覆盖后自动热重载, 另一台机器的 API key 无需重启即可用;反之,本地通过模型页等改密钥会触发 credentials/reference-updated / credentials/record-updated 事件,防抖推送。
  • 设备 token 隔离:开启后本插件的设备凭据只存 .dsh-sync/credentials.json (绝不同步),不再写入 credentials 服务 record——被同步的文档永远不会替换掉本机 用来认证的 token;旧 record 会被一次性迁移到状态目录文件。
  • 冲突:文档是单文件整体对比,双边都改 → 冲突副本物化为 .credentials.yaml.conflict-<sha8>(同目录普通文件,credentials 服务的 watcher 只盯原文件名,不会误加载),人工合并后裁决 keep-local / keep-remote。
  • CLI:dsh-sync now --credentials 在独立 CLI 轮次中包含该文件根。

工具(注册给 Agent)

| 工具 | 作用 | | --- | --- | | dsh_sync_status | 服务端/设备/最近同步/冲突/错误总览 | | dsh_sync_now | 立即一轮同步(full/push/pull) | | dsh_sync_conflicts | 列出当前冲突 | | dsh_sync_resolve | 裁决:keep-local / keep-remote / keep-both | | dsh_sync_setup | 配置服务端与设备凭据 |

同步语义(与 DESIGN.md §4/§7 一致)

  • 3-way 合并:本地 / 远端 / 上次同步点(base);差异以内容哈希判定,与时钟无关。
  • push:先上传 blob(内容寻址、幂等),再原子提交清单(含墓碑删除)。
  • pull:按远端写回文件(临时文件 + 原子改名),并更新 base。
  • 冲突(双边改动且内容不同):保守策略——本地保留,远端版本物化为确定性的 <path>.conflict-<sha8> 副本(会作为新文件同步到所有机器),并向服务端日志上报 conflict;base 不动,冲突持续到用 dsh_sync_resolve 裁决。
    • keep-local:本地版本推送为远端(删除冗余副本)。
    • keep-remote:远端覆盖本地,并清理 conflict-* 副本。
    • keep-both:本地另存 <path>.local-<sha8>,远端占位原路径。
  • settings 命名空间:以 settings/<ns> 伪文件参与同一套 3-way;写回走 settings.replace(保留服务权威性并触发其自身的变更事件);不支持远端删除命名空间。 扫描和写回都会拒绝含内联 apiKey/token/secret/password 等字段的命名空间; apiKeyEnv 这类环境变量引用可以同步。命名空间冲突不会物化 .conflict-* 副本 (伪文件无处安放,只上报冲突等待裁决),keep-both 对 settings 根不可用,会返回 明确错误——请用 keep-local / keep-remote 裁决。
  • 状态文件容错:state.json 损坏或版本不符时不会让插件行装载失败——旧文件被 隔离为 state.json.corrupt-<ts>,base 重置为空(后续轮次保守收敛),错误写入 状态供 UI/doctor 展示。
  • 离线/失败:捕获错误写入状态(不破坏 base),指数退避自动重试(30s→30min)。

测试

node --test    # 64 个用例:scanner/manifest/transport/credentials/settings 单元 +
               # engine 全场景(push/pull/tombstone/冲突三类解析/settings 冲突/凭据文档/忽略/离线)+
               # bridge(状态/同步/冲突/裁决信封/请求体上限)+
               # e2e 双设备全流程(fake server 实现 Go 端 /v1 契约)+
               # client-ui(设置页渲染/弹窗/裁决错误展示)

与 M1 服务端的端到端验证

# 终端 1:起真实 Go 服务端
ADMIN_TOKEN=$(openssl rand -hex 32) DSH_SYNC_DATA_DIR=./data PORT=8080 go run ../server/cmd/api

# 终端 2:注册并同步
export DSH_HOME=$(mktemp -d)   # 隔离的 DSH 家目录
node src/cli.js register --server http://127.0.0.1:8080 --admin-token <token> --name laptop-a
mkdir -p "$DSH_HOME/.agent-presets/my-preset"
echo 'name: my-preset' > "$DSH_HOME/.agent-presets/my-preset/agent.cordis.yml"
node src/cli.js now