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

@reedef/r1

v0.7.5

Published

Personal backup & sync CLI — Obsidian vault ↔ S3-compatible storage via rclone bisync, with Resend email notifications and launchd scheduling on macOS.

Readme

r1

个人备份与同步 CLI 工具集。

安装

git clone <repo-url> ~/Project/r1
cd ~/Project/r1
npm link        # 全局可用 r1

依赖:Node 18+、rclonebrew install rclone)。

cloudcli 子命令

修复 CloudCLI Terminal 插件因 node-pty 原生模块与当前平台不匹配导致的 Disconnected / Connection lost

r1 cloudcli fix-terminal

命令会读取 CloudCLI 主包当前使用的 node-pty 版本,在 Terminal 插件目录精确安装同版本,执行插件构建,并验证 node-pty 能否加载。它会更新插件自己的 package.json / package-lock.json,但不会复制主包的 node_modules,也不会自动重启 CloudCLI。

默认路径:

  • CloudCLI 主包:$(npm root -g)/@cloudcli-ai/cloudcli
  • Terminal 插件:~/.claude-code-ui/plugins/cloudcli-plugin-terminal

路径不同时可使用参数:

r1 cloudcli fix-terminal \
  --cloudcli-dir /path/to/@cloudcli-ai/cloudcli \
  --plugin-dir /path/to/cloudcli-plugin-terminal

也兼容环境变量 CLOUDCLI_DIRPLUGIN_DIRGLOBAL_ROOT,命令行参数优先。在 Docker 场景中应进入容器后执行。修复成功后,请按 PM2、systemd、Docker Compose、手动进程或桌面应用等实际部署方式重启 CloudCLI。

self-update 子命令

默认行为:

  • 运行任意普通命令前,r1 会按 ~/.config/r1/self-update.json 中的 intervalHours 检查 npm 最新版
  • 发现新版本时直接升级,不询问;升级成功后自动重跑原命令
  • 升级 r1 时会同步收敛内置保留仓库 r1-skills[email protected]:Reedef/r1-skills.git
  • 非黑名单 Skill 会自动安装到已存在的 Agent Skill 目录;黑名单 Skill 会被自动卸载

命令:

r1 self-update
r1 self-update config --interval-hours 24
r1 self-update blacklist add r1-dingdoc
r1 self-update blacklist remove r1-dingdoc
r1 self-update blacklist list

obsidian 子命令

支持多个 Obsidian vault,每个 vault 与各自的 S3 兼容存储双向同步(rclone bisync),结果通过 Resend 发邮件。

命令一览

r1 obsidian list                       # 列出已配置的 vault
r1 obsidian sync <vault>               # 跑一次指定 vault 的双向同步
r1 obsidian install <vault> | --all    # 装 launchd 定时任务
r1 obsidian uninstall <vault> | --all  # 卸载 launchd 定时任务
r1 obsidian status [vault]             # 查 launchd / env / rclone remote 状态

每个 vault 一个独立 launchd job(label 为 com.aliyz.obsidian-backup.<vault>),互不影响。

首次配置

1. 准备全局 env(只放 Resend API key)

跑一次 r1 obsidian install,env 文件不存在时会问你是否生成空模板(mode 600),y 即可,然后编辑:

$EDITOR ~/.config/r1/obsidian-backup.env

填入:

RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxx
# [email protected]   # 可选 fallback,vault 没配 notifyEmail 时使用

2. 写 vault 配置 ~/.config/r1/obsidian.json

{
  "vaults": [
    {
      "name": "main",
      "local": "~/Documents/Notes",
      "remote": "qiniu-obsidian:yz-aio/obsidian-notes",
      "schedule": [
        { "hour": 10, "minute": 45 },
        { "hour": 17, "minute": 45 }
      ],
      "maxDelete": 10,
      "notifyEmail": "[email protected]",
      "localBackup": false
    },
    {
      "name": "work",
      "local": "~/Documents/WorkNotes",
      "remote": "qiniu-obsidian:yz-aio/work-notes",
      "schedule": [{ "hour": 22, "minute": 0 }],
      "notifyEmail": "[email protected]",
      "localBackup": true
    }
  ]
}

字段说明:

| 字段 | 必填 | 说明 | |---|---|---| | name | ✓ | vault 唯一标识,只能用 a-zA-Z0-9_- | | local | ✓ | 本地 vault 路径,支持 ~/ | | remote | ✓ | rclone remote 路径,格式 remote:bucket/dir | | schedule | – | launchd 触发时间数组,默认 [10:45, 17:45] | | maxDelete | – | 误删兜底,默认 10 | | notifyEmail | – | 收件人;缺省时回退到 env 里的 NOTIFY_EMAIL | | localBackup | – | 默认 false;为 true 时每周自动做一次本地 zip 备份(见下文) |

3. 确保 rclone 里有对应的 remote

rclone config   # 添加 qiniu-obsidian 等 remote(S3/Qiniu/...)

4. 装定时任务

r1 obsidian install --all     # 或 r1 obsidian install <vault>

5. 每个 vault 首次必须建 bisync 基线

r1 obsidian sync main --resync
r1 obsidian sync work --resync

⚠️ 危险动作:bisync 第一次跑必须 --resync 建基线。--resync 默认用本地数据覆盖远端,运行时会先给出二次确认(非交互环境如 launchd 会直接中止,不会误建基线)。跑之前确认本地和远端内容一致(可以先 rclone check ~/Documents/Notes qiniu-obsidian:yz-aio/obsidian-notes)。

如果你想反过来用远端覆盖本地,不要用 --resync,改用普通同步并放宽误删兜底,例如 r1 obsidian sync main --max-delete=99

日常使用

r1 obsidian sync main                       # 手动跑一次
r1 obsidian sync main --max-delete 50       # 临时放宽误删兜底
r1 obsidian sync main --dry-run             # 只看不动
r1 obsidian sync main --skip-mail           # 不发邮件
r1 obsidian status                          # 看所有 vault 状态
r1 obsidian status main                     # 单 vault

立即触发某个 launchd job(不等定时):

launchctl kickstart gui/$(id -u)/com.aliyz.obsidian-backup.main

加 / 删 vault

加:编辑 ~/.config/r1/obsidian.json 增项 → r1 obsidian install <new>r1 obsidian sync <new> --resync

删:先 r1 obsidian uninstall <name> → 从 obsidian.json 中删除该项。

本地周备份(localBackup)

vault 配置里设 "localBackup": true 后:

  • 每次 r1 obsidian sync <vault> 成功后,检查 ~/backup/obsidian/<vault>/ 下最近一个 .zip 的修改时间,距今 ≥ 7 天才会再做一次(首次没有 zip 则立刻做)。
  • 备份文件名:<vault>-YYYYMMDD-HHmmss.zip
  • 备份完成后发独立邮件通知(主题 📦 Obsidian [<vault>] 本周本地备份完成 @ <host>)。
  • 备份失败只打印,不影响 sync 退出码;--skip-mail 只跳过邮件,备份仍会创建。
  • 不做自动轮转,老 zip 自己清理;如果想手动触发下一次备份,删掉 ~/backup/obsidian/<vault>/ 里最新那个 zip 即可。

默认行为(所有 vault 共用)

  • 排除:.DS_Store*.log
  • 冲突策略:--conflict-resolve newer,输者重命名为 *.conflict-1
  • 日志:/tmp/obsidian-backup-<vault>.log + .err,rclone 详细 JSON 日志在 ~/obsidian_notes_rclone_backup-<vault>.log
  • 邮件发件人:Obsidian Backup <[email protected]>(Resend 沙箱)
  • bisync 状态文件:~/Library/Caches/rclone/bisync/(rclone 默认,按 path1/path2 自动区分不同 vault)

邮件主题示例

✅ Obsidian [main] 同步成功 @ Jacks-MacBook-Pro
❌ Obsidian [work] 同步失败 @ Jacks-MacBook-Pro

mcp 子命令

直连任意 streamable-http 的 MCP 网关(例如钉钉文档),用 JSON-RPC over HTTP 调用其工具,不需要 MCP SDK,也不依赖 Claude / 任何 MCP 客户端

命令一览

r1 mcp list                          # 列出已配置的 server
r1 mcp add <name> <url> [--type T]   # 注册 / 更新一个 server(默认 type=streamable-http)
r1 mcp remove <name>                 # 删除一个 server
r1 mcp <server>                      # 列出该 server 的工具(等价于 <server> tools)
r1 mcp <server> tools                # 同上
r1 mcp <server> call <tool> [json]   # 调用工具,参数是一段 JSON 字符串

用法示例(以钉钉文档为例)

# 1. 注册 server(url 里的 ?key= 就是鉴权,整串从钉钉 MCP 配置里拿)
r1 mcp add dingdoc "https://mcp-gw.dingtalk.com/server/xxxx?key=yyyy"

# 2. 看有哪些工具
r1 mcp dingdoc

# 3. 调用工具
r1 mcp dingdoc call list_nodes '{"pageSize":10}'
r1 mcp dingdoc call search_documents '{"keyword":"周报"}'
r1 mcp dingdoc call get_document_content '{"nodeId":"xxxx"}'

配置 ~/.config/r1/mcp.json

r1 mcp add 会自动维护这个文件(权限锁 0600),也可手动编辑:

{
  "servers": {
    "dingdoc": {
      "type": "streamable-http",
      "url": "https://mcp-gw.dingtalk.com/server/xxxx?key=yyyy"
    }
  }
}

| 字段 | 必填 | 说明 | |---|---|---| | <name> | ✓ | server 唯一标识,只能用 a-zA-Z0-9_-,且不能是 list/add/remove | | url | ✓ | MCP 网关地址,http/https;鉴权一般就在 query 里(如 ?key=) | | type | – | 默认 streamable-http(目前仅支持这一种) |

⚠️ 这个文件里含访问凭证(url 中的 key),不要提交到 git、不要分享r1 mcp list 输出会把 key/token 等自动打码。

行为说明

  • 这类网关实测是无状态的:单次 POST 即调用,不做 initialize 握手 / session 管理。
  • 响应是 application/json 或 SSE,两种都解析。工具结果若是 JSON 字符串会自动美化输出。
  • 请求 30s 超时;HTTP 非 2xx、JSON-RPC error、网络失败都会给出可读报错(url 中的凭证已打码)。

skill 子命令(个人 GitHub Skills)

管理自己有权限读取或写入的 GitHub Skill 仓库。安装时直接把 Claude 的 ~/.claude/skills/<skill> 与 Codex 的 ~/.agents/skills/<skill> 软链接到 r1 管理的 Git 工作树,因此直接编辑 Agent 目录中的 Skill,等同于修改该工作树,可再发布回源仓库。

命令一览

r1 skill repo add <name> <github-url> [--ref main] [--path skills]
r1 skill repo list
r1 skill repo remove <name>
r1 skill available <repo>
r1 skill find <keyword> [--repo <name>]
r1 skill install <skill> | <repo>/<skill> [--agent claude|codex|all]
r1 skill list                                    # 列出全部已安装 Skill,标记 r1 管理项
r1 skill status
r1 skill update <repo>/<skill> | --all [--dry-run]
r1 skill publish <repo>/<skill> | --all [-m "message"] [-y] [--dry-run]

首次使用

r1 skill repo add infra [email protected]:Reedef/ai.infra.git
r1 skill find dingdoc
r1 skill available infra
r1 skill install r1-dingdoc --agent all

仓库必须是 GitHub SSH URL(如 [email protected]:owner/repo.git)或 HTTPS URL。GitHub 的访问权限由本机 SSH key、Credential Manager 或 gh auth login 决定;r1 不保存 token。

r1 skill find 仅搜索 r1 skill repo add 配置的个人仓库,匹配 Skill 目录名及 SKILL.mdnamedescription;不会搜索公开市场。r1 skill install <skill> 在该范围内唯一命中时自动安装;同名结果有多个时会要求显式指定 <repo>/<skill>r1 skill list 会扫描 Claude 与 Codex 的实际 Skill 目录,列出全部发现的 Skill,并标记哪些由 r1 管理。

Agent 检测规则:只要存在 ~/.claude,就视为已安装 Claude;只要存在 ~/.codex~/.agents,就视为已安装 Codex。安装 Skill 时若目标目录不存在,会自动创建 ~/.claude/skills~/.agents/skills

更新与发布

# 仅把远端内容快进到本地工作树;有未发布修改时会拒绝覆盖
r1 skill update --all

# 在 ~/.claude/skills 或 ~/.agents/skills 中编辑过 Skill 后,检查状态并发布
r1 skill status
r1 skill publish infra/r1-dingdoc -m "docs: 更新钉钉文档 Skill"

publish 会先显示 diff 和目标分支,确认后仅暂存指定 Skill,执行 commitpull --rebasepush。没有写权限或遇到 rebase 冲突时会停止,保留本地修改与提交,绝不强推。非交互环境必须显式加 -y

配置位于 ~/.config/r1/skills.json(权限 0600),本地 Git 工作树位于 ~/.cache/r1/skill-repos/。不要手动删除工作树;如需卸载或迁移,请先发布未提交的改动。

ding 子命令(钉钉文档 + 通讯录)

mcp 是通用网关,参数得手写 JSON;ding 是它上面的一层顺手封装,把动词直接映射成钉钉 MCP 工具调用,参数用普通 flag 传,不用手拼 JSON。分两组:

  • ding doc <动词> —— 钉钉在线文档(文字文档):读/最近/搜/建/改/删/加协作者 + 账号/文档库管理
  • ding user search <关键词> —— 钉钉通讯录:搜成员拿 userId

兼容:旧写法 r1 dingdoc <...> 仍等价于 r1 ding doc <...>

ding doc 多账号

一个「账号」= 一份钉钉文档 MCP 授权 URL(含 ?key=,绑定某个登录的钉钉账号)。凭证复用存在 ~/.config/r1/mcp.json,默认账号名单独记在 ~/.config/r1/dingdoc.json。命令默认走默认账号,加 --account <名> 临时切到别的账号。

r1 ding doc account add work "https://mcp-gw.dingtalk.com/server/xxx?key=yyy"  # 加账号(首个自动设默认)
r1 ding doc account add home "https://mcp-gw.dingtalk.com/server/aaa?key=bbb"
r1 ding doc account list          # 列出账号,标出默认(key 自动打码)
r1 ding doc account use home      # 切默认账号
r1 ding doc account remove home   # 删账号

URL 从 https://mcp.dingtalk.com/#/detail?mcpId=9629 右侧「获取 MCP Server 配置」复制 StreamableHttp 那条。换钉钉账号就用那个账号登录后各复制一份,account add 成不同名字即可。

兼容:早期直接 r1 mcp add dingdoc "<url>" 的用户,没设默认账号时命令自动回退到名为 dingdoc 的账号,无需改动。

ding doc 文档库映射

repo 保存“易记名称 → workspaceId + 钉钉账号”的本地映射,并可指定一个默认文档库。添加时会先通过对应账号读取一次文档库,验证 ID 和权限;首个映射自动设为默认。

r1 ding doc repo add ai文档 <workspace-id>                 # 使用默认账号验证并保存
r1 ding doc repo add 团队知识库 <workspace-id> --account work # 绑定指定账号
r1 ding doc repo list                                      # 列映射并标出默认
r1 ding doc repo use ai文档                                # 切换默认文档库
r1 ding doc repo remove ai文档                             # 仅删本地映射,不删远端库

create 没有显式传 --folder 时,会优先写入默认文档库;若钉钉明确返回写入失败,会兜底写入该账号的个人文档库。没有设置默认文档库时直接写个人文档库。网络超时或连接中断不会自动回退,避免服务端其实已经创建成功而产生重复文档。显式 --folder 始终优先。

命令一览

r1 ding doc read   <url-or-id>                 # 读正文,输出 Markdown(可 > out.md 落盘)
r1 ding doc info   <url-or-id>                 # 元信息(标题/类型/创建者/时间)
r1 ding doc search [keyword] [--page-size N]   # 搜索(省 keyword 返回最近访问)
r1 ding doc recent [--page-size N]             # 最近访问/编辑的文档(每页最多 20 条)
r1 ding doc list   [folder-id] [--page-size N] # 列目录子节点(省参数为「我的文档」根)
r1 ding doc create <title> [--folder <id>] [--body-file <f>] # 新建文档
r1 ding doc update <url-or-id> --body-file <f> [--mode append|overwrite]  # 写正文
r1 ding doc rename <url-or-id> <new-name>      # 重命名
r1 ding doc move   <url-or-id> <folder-id>     # 移动到目标目录
r1 ding doc delete <url-or-id> [-y]            # 删除(需确认,-y 跳过)
r1 ding doc grant  <url-or-id> <成员名...> [--role EDITOR|READER|DOWNLOADER|MANAGER]  # 加协作者

任意文档动作都能加 --account <名> 指定账号,例如 r1 ding doc list --account home

用法示例

# 读一篇文档落盘(read 只吐正文,方便重定向)
r1 ding doc read https://alidocs.dingtalk.com/i/nodes/xxxx > 周报.md

# 搜索(默认账号)
r1 ding doc search 周报 --page-size 20

# 最近访问/编辑的文档(包含访问、更新时间和操作类型)
r1 ding doc recent --page-size 10

# 用另一个账号搜
r1 ding doc search 周报 --account home

# 新建带正文的文档(省略 --folder 时优先写默认文档库)
r1 ding doc create "四月周报" --body-file ./body.md

# 明确写入某个目录(覆盖默认文档库)
r1 ding doc create "四月周报" --folder <folder-id> --body-file ./body.md

# 往已有文档末尾追加(默认 append,安全)
r1 ding doc update <url-or-id> --body-file ./more.md

# 加协作者:直接写名字,内部自动转 userId(默认授「可编辑」)
r1 ding doc grant <url-or-id> 张三 李四
r1 ding doc grant <url-or-id> 张三 --role READER   # 只读

grant:加协作者(自动名字转 userId)

grant 把成员名字转成钉钉 userId 再授权,省得你手动查 id。默认授 EDITOR(可编辑)。

  • 名字转 id 依赖一个通讯录 MCP(不是文档账号,就是 ding user 用的那个,默认 server 名 contacts),配置见下面「ding user」一节。
  • 纯数字会被当作 userId 直接使用,跳过查询。
  • 一个名字在通讯录里匹配到多个人会报错(不会盲授),请用更精确的名字或直接传 userId。
  • 只能授企业用户add_permission 仅支持 USER 类型);得去钉钉网页端加。

ding user(通讯录)

搜索组织成员、拿 userId(喂给 ding doc grant 或其它需要 userId 的地方)。目前只封装 search

r1 ding user search 张三                  # 返回匹配成员的 userId
r1 ding user search 张三 --contacts <名>   # 指定通讯录 server(默认 contacts)

通讯录是一个独立的 MCP(不是文档账号)。没授权时命令会提示去授权:

  1. 打开 https://aihub.dingtalk.com/#/detail?instanceId=4925735&detailType=instanceMcpDetail&mcpId=2400,完成授权后复制 StreamableHttp URL(含 ?key=)。
  2. r1 mcp add contacts "<上一步复制的 URL>"

查过的关键词会缓存到 ~/.config/r1/ding-user-cache.json(按通讯录 server 分桶),二次查询直接命中——包括 ding doc grant,它会复用你之前 ding user search(或上次 grant)查过的 userId,不再打通讯录。只缓存查到的结果;加 --refresh 跳过缓存强制重查并刷新。

只封装了 search;部门、按手机号查等其它通讯录能力直接用 r1 mcp contacts call <tool> '<json>'

选项

| 选项 | 用于 | 说明 | |---|---|---| | --account <名> | 全部 | 本次用哪个账号;省略走默认账号,回退到名为 dingdoc 的账号(--server 是其别名)| | --folder <id> | create | 显式目标目录,优先于默认文档库;省略则走默认文档库,再兜底个人文档库 | | --body-file <f> | create / update | 要写入的正文文件(真实换行,别手拼 \n)| | --mode <m> | update | append(默认,末尾追加,安全)/ overwrite(清空重写,会删掉全文含图片评论)| | --role <r> | grant | EDITOR(默认)/ READER / DOWNLOADER / MANAGER | | --contacts <名> | grant / user search | 通讯录 MCP server 名(默认 contacts)| | --refresh | grant / user search | 查 userId 时跳过缓存强制重查 | | --page-size <n> | search / recent / list | 每页条数;recent 最大 20 | | -y, --yes | delete | 跳过二次确认 |

行为说明

  • nodeId 处处可直接传文档链接 URL32 位 dentryUuid,网关自动识别。
  • 文档库映射与默认值保存在 ~/.config/r1/dingdoc.json;账号凭证仍只在 mcp.json
  • 只覆盖钉钉文字文档;电子表格 / AI 表格不在范围。不确定类型先 r1 ding doc info <url-or-id>extensionadoc 即文字文档)。
  • delete 非交互环境(管道 / cron)默认不删,需 -y 显式确认。
  • update 默认 append 而非 overwrite——overwrite 会清空全文,危险动作要显式带 --mode overwrite

文件位置一览

| 用途 | 路径 | |---|---| | 仓库代码 | ~/Project/r1/ | | Vault 配置 | ~/.config/r1/obsidian.json | | MCP server 配置(含 ding 文档账号 + contacts 通讯录凭证) | ~/.config/r1/mcp.json | | Skill 仓库配置与安装记录 | ~/.config/r1/skills.json | | Skill Git 工作树 | ~/.cache/r1/skill-repos/ | | self-update 配置 | ~/.config/r1/self-update.json | | self-update 状态缓存 | ~/.config/r1/update.json | | ding doc 默认账号、文档库映射与默认文档库 | ~/.config/r1/dingdoc.json | | ding user 查过的 userId 缓存 | ~/.config/r1/ding-user-cache.json | | 全局 env(Resend API key) | ~/.config/r1/obsidian-backup.env | | launchd plist(每 vault 一个) | ~/Library/LaunchAgents/com.aliyz.obsidian-backup.<vault>.plist | | bisync 状态 | ~/Library/Caches/rclone/bisync/ | | 本地周备份 zip(启用 localBackup 时) | ~/backup/obsidian/<vault>/<vault>-YYYYMMDD-HHmmss.zip | | 单次跑的日志 | /tmp/obsidian-backup-<vault>.{log,err} | | rclone JSON 日志 | ~/obsidian_notes_rclone_backup-<vault>.log |

在另一台 Mac 上启用

  1. brew install rclone
  2. git clone <repo-url> ~/Project/r1 && cd ~/Project/r1 && npm link
  3. 拷过去 ~/.config/rclone/rclone.conf(含 remote 凭证)
  4. 拷过去 / 重建 ~/.config/r1/obsidian.json~/.config/r1/obsidian-backup.env
  5. r1 obsidian install --all
  6. 每个 vault 跑一次 r1 obsidian sync <vault> --resync(bisync 状态不能跨机器搬运)