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

@asong111/dsh-cloud-sync

v0.3.12

Published

Cloud sync for the dsh web GUI: bidirectionally sync plugins, config, agent-presets, skills and task history between two machines through a private Git remote (default SSH). Auto-installs dependencies after pull; excludes plaintext secrets (credentials/SS

Readme

dsh-cloud-sync

在两台不同网络的电脑之间,通过一个私有 Git 远端(默认 SSH 免密)做 DSH 云端同步:把 ~/.dsh 下的插件清单、配置、agent presets、skills、任务历史双向同步,做到换台电脑接着干。拉取后自动安装依赖,并默认排除明文密钥。

独立插件项目(不依赖 dsh-web-ui 全家桶),与 DSH 本身同款双 face(host + client)插件范式,热插拔、不改 dsh 源码。

特性

  • 双向同步:push(本机 → 云端远端)、pull(云端远端 → 本机)。
  • 自动接力:autoSync 定时「先拉后推」——autoPush 默认开,A/B 机顺序切换时本机变更自动上传,无需手动点「上传」。
  • 自动装依赖:pull 后检测到 package.json / pnpm-lock.yaml 变化时,自动 dsh plugin install 复现插件(installAfterPull 默认开,可在 GUI 关)。
  • 安全:默认排除 .credentials.yaml、dsh-ssh.json(含明文 SSH 密码)、设备匿名 ID、pet.json;node_modules / .git 一律不进镜像。
  • 冲突保守:不静默覆盖;冲突时先备份本机旧文件,标记「待处理」,绝不 --force 推送。
  • GUI:侧边栏「云同步」面板(配置远端 / 一键上传 / 一键拉取 / 自动同步开关与间隔 / 拉后自动推送开关)。
  • 动作日志:面板「日志」页按天展示每次 push/pull/resolve 新增/修改/删除的具体文件;原始数据落盘 ~/dsh-sync/logs/<profile>/sync-<日期>.jsonl。
  • 同步介质:Git(有版本历史、可冲突检测、可 age/git-crypt 加密)。

支持的同步内容

| 内容 | 路径 | 说明 | |---|---|---| | 插件清单 | profiles/<profile>/package.json + pnpm-lock.yaml | 不拷 node_modules,拉取后自动 install | | 配置 | profiles/<profile>/cordis.patch.yml、settings.yaml、cordis.patch.yml(home) | settings.yaml 已确认无明文密钥 | | agent presets | .agent-presets/ | 梁神等预设 | | skills | skills/ | | | 任务历史 | sessions/、storages/、task-board/ | |

明确不同步(不计划纳入):attachments/(会话附件对象,换机后旧会话中的贴图/附件不迁移)——需要的话请自行用云盘同步目录(Drive/坚果云)管理;skill 内容(含其展示/媒体资产)照常同步,作为 skill 的组成部分跟随版本。运行时/临时态同样永不入镜像(见安全红线)。

架构 — 同步是怎么工作的

给开发者看的三张图,覆盖整个设计。引擎(host 半)在 src/engine.ts,面板(browser 半)在 src/client/。

1. 数据流(单机)

                       ~/.dsh   (live dsh data)
   +------------------------------------------------------------------+
   | profiles/<profile>/   package.json  cordis.patch.yml             |
   |                       pnpm-lock.yaml  pnpm-workspace.yaml        |
   | .agent-presets/   skills/   sessions/   storages/   task-board/  |
   | settings.yaml   cordis.patch.yml                                 |
   +---------------------------------+--------------------------------+
                                     |
        push: content-hash copy ①   |   pull: copy back ③ (union rules,
        (skip node_modules/.git,     |        backup before overwrite,
         secret blacklist,           |        active files <60s skipped)
         active files <60s)          |
                                     v
   +------------------------------------------------------------------+
   | ~/dsh-sync/<profile>/   mirror working tree (a git repo)         |
   +---------------------------------+--------------------------------+
                                     |
              git pull --rebase ②    |    git add -A && commit ②
              (remote -> mirror)     |    && git push (mirror -> remote)
                                     v
   +------------------------------------------------------------------+
   |  private Git remote   [email protected]:<user>/dsh-sync.git         |
   |  (SSH; history + conflict detection come free with git)          |
   +------------------------------------------------------------------+

   per-machine side stores (never synced):
     ~/dsh-cloud-sync-backup/<ts>/<dsh-rel>   pre-overwrite backups
     ~/dsh-sync/logs/<profile>/sync-YYYY-MM-DD.jsonl   action log
  • ① push:把 ~/.dsh 增量复制进镜像(按 sha256 内容哈希,相同则跳过;排除黑名单与近 60 秒内仍在变动的文件),然后 git add -A && commit && push。
  • ② git 负责镜像 ↔ 远端,pull 用 --rebase,冲突时保留镜像原地不动、提示处理,绝不 --force。
  • ③ pull:镜像复制回 ~/.dsh;覆盖/删除前先备份到 ~/dsh-cloud-sync-backup/<时间戳>/(保留 dsh 相对路径);sessions/ storages/ task-board/ 是并集语义——远端没有的本地内容绝不删除(防止误删未推送的会话)。

2. 双机接力(顺序切换,时间段不重叠)

autoSync(定时)+ autoPush(默认开)让同步循环变成全自动接力:A 机在本机周期内就把变更推上云端,B 机下次拉取即可接上,无需手动点「上传」。

    ┌──────────────┐                         ┌──────────────┐
    │  Machine A   │                         │  Machine B   │
    │   (works)    │                         │   (works)    │
    │              │                         │              │
    │  autoSync    │                         │  autoSync    │
    │  every N min │                         │  every N min │
    └──────┬───────┘                         └──────┬───────┘
           │                                       │
           │   ┌─────────────────────────────┐     │
           └──►│  private Git remote (SSH)   │◄────┘
               │  [email protected]:<user>/sync │
               └─────────────────────────────┘
      A pulls (enter) then pushes its        B pulls A's work on
      work every tick (autoPush ON)          boot, then pushes back

3. 单次 autoSync 的决策流程

 autoSync tick (every autoSyncIntervalMin)
        │
        ▼
  fetch probe  (1 network round-trip)
        │
        ├── fetch failed? ──────────► skip — retry next tick (no log)
        │
        ▼
  remote has new commits (behind > 0)?
        │
        ├── no: nothing new on remote
        │        ├─ autoPush on ──► local content-hash scan, push if changed
        │        └─ autoPush off ─► done — full no-op, no log
        │
        ▼  yes
  pull --rebase   (remote → mirror)
        │
        ├── conflict? ──────────────► STOP — mirror left pending;
        │                              resolve via GUI「以远端为准」
        ├── hard error? ─────────────► STOP — retry at the next tick
        │
        ▼  ok
  copy mirror → ~/.dsh
   • backup before every overwrite / delete
   • sessions/ storages/ task-board/ are a UNION — never pruned
   • files modified within 60s are skipped (reported as skipped)
        │
        ├─ deps changed && installAfterPull? ──► dsh plugin install
        │
        ▼
  autoPush on? ── no ──► done
        │ yes
        ▼
  copy ~/.dsh → mirror (content-hash aware)
        │
        ├─ nothing changed? ────────► skip — no empty commit
        ├─ only active files? ──────► skip them too, push nothing
        ▼
  git add -A && commit && push

关键行为

  • 复制以内容哈希(sha256)为准,而非 mtime:字节相同的文件不会重复拷贝,空闲机器不产生空提交。
  • 60 秒活跃窗口保护(DSH_CLOUD_SYNC_ACTIVE_MS 可覆盖):push/pull 双向都不拷贝、不覆盖、不删除「可能正在写入」的文件;在 mtime 窗口之上加 size 稳定判定(DSH_CLOUD_SYNC_SETTLE_MS,默认 10s)——同一文件连续多次同步观测字节数不变即视为写完放行,长流式会话写入不再被永久跳过;跳过结果进入 skipped 并在面板/日志显示。
  • 定时 tick 探测优先:每个 tick 只做 1 次 fetch + 本地 rev-list 判断 behind;远端无新提交且无需推送时完全 no-op——不扫描文件树、不写日志(日志只记录真实发生的 pull/push)。
  • autoPush 只在「拉取成功且无冲突」后触发:失败或冲突的 pull 绝不自动推送(镜像保持待处理,等待 GUI 处理)。
  • 本机改动保护(基线):本机自上次成功同步后改动过的文件(如手动卸载插件改动的 package.json)不会被 pull 覆盖或删除,而是保留并在下次 push 推上云端——「卸载了又被拉回来」不再发生;首次同步(无本机基线)仍以云端为准。
  • 密钥与运行时态永不进镜像:.credentials.yaml、dsh-ssh.json、.anonymous-user-id、pet.json、node_modules/、.git/。

单写者契约(接力模型)

插件按顺序接力设计:任何时刻只有一台机器在写,另一台 pull 后接管。在该约束下:

  • storages/workspace.json 是 dsh 的全局权威注册表(工作区列表与 archivedSessionIds 归档标签)。当前使用的机器独占它,另一台 pull 时整文件覆盖——不做字段级合并。合并且要猜测 dsh 的 schema,且无法表达「删除」(归档=加标签、恢复=删标签,纯 union 会把已经恢复的会话又归档回去)。
  • 若 workspace.json(或任何文件)出现冲突,说明接力断了一环:某台机器改了它却没先 push。解决方式:确认「后使用的那台」的版本为最新后「以远端为准」(本机冲突版本已备份在 ~/dsh-cloud-sync-backup/)。

安装与使用

# A 机 / B 机都执行一次
dsh plugin --profile web add @asong111/dsh-cloud-sync

# GUI 侧边栏「云同步」:
#   1) 填远端仓库地址(如 [email protected]:ASong111/dsh-sync.git)
#   2) 点「↑ 上传到云端」完成首次 push
#   3) 另一台电脑点「↓ 从云端拉取」→ 自动装依赖

两机 SSH 免密(Git 鉴权,一次配置)

  • 同一把密钥:把 A 机 ~/.ssh/id_rsa + id_rsa.pub 复制到 B 机 ~/.ssh/;或
  • 各自密钥:在 B 机新生成密钥,把公钥加到 GitHub 账号。

installAfterPull 走 npm registry 网络,与 Git 的 SSH 鉴权相互独立。

完整的两机部署走查(首次上传/拉取、link: 依赖注意事项、ERR_PNPM_UNEXPECTED_STORE 修复、冲突恢复)见 DEPLOY.md。

开发

pnpm install
pnpm test      # vitest:excludes / store / engine(双机模拟)
pnpm build     # tsc + tsdown -> lib/
pnpm watch

安全红线

以下文件永不进入同步(硬编码黑名单,无法通过配置强行纳入):

  • ~/.dsh/.credentials.yaml
  • ~/.dsh/dsh-ssh.json
  • ~/.dsh/.anonymous-user-id
  • ~/.dsh/pet.json
  • node_modules/、.git/
  • 运行时/临时态永不同步:*.lock(如 ledger-v2.lock)、*.tmp、*.writing 后缀,以及 scheduler-v2.json(task-board 调度心跳)——每机临时状态(ledger-v2.json 任务数据仍正常同步)

许可证

Apache-2.0