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

iCloud 日历 CaiMingshen/dsh-icloud-calendar

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

通过 CalDAV 读写 iCloud 日历事件

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

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

自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add CaiMingshen/dsh-icloud-calendar
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

未发布到 npm registry,仅可从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-icloud-calendar(未发布到 npm,仅可源码安装)
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/16 14:08:10

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 10 处,展示前 9 条)
  • 发起网络请求
    lib/client.js:180
    const principalResponse = await this.request('PROPFIND', this.credentials.serverUrl, {
  • 发起网络请求
    lib/client.js:191
    const homeResponse = await this.request('PROPFIND', principal, {
  • 发起网络请求
    lib/client.js:208
    const response = await this.request('PROPFIND', home, {
  • 发起网络请求
    lib/client.js:313
    const response = await this.request('REPORT', calendar.url, {
  • 发起网络请求
    lib/client.js:368
    const response = await this.request('GET', url, { expect: [200], signal, context: 'Reading an event' });
  • 发起网络请求
    lib/client.js:396
    const response = await this.request('PUT', url, {
  • 执行外部命令来自配置/变量
    lib/credentials.js:52
    const child = execFile(file, [...args], {
  • 写入本地文件
    lib/credentials.js:123
    await mkdir(this.directory, { recursive: true, mode: 0o700 });
  • 删除本地文件
    lib/credentials.js:143
    await rm(this.fileFor(account));
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/dsh-util-values@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • CalDAV读写日历事件
  • 跨平台凭据存储
  • 删除前试运行确认

Apple iCloud 日历走 CalDAV 接入 DeepSeek Harness(DSH):五个工具、跨平台凭据存储、请求超时,以及删除前必须确认精确对象数量的试运行。

README

由 DeepSeek 最新模型翻译生成
dsh-icloud-calendar auth set --account you@example.com 工具 插件向模型暴露 5 个工具: | 工具 | 作用 | | icloud_calendar_list | 列出所有日历(名称、URL、颜色、是否只读) | | icloud_calendar_events | 按时间窗读取事件,自动展开重复日程 | | icloud_calendar_create | 新建事件 | | icloud_calendar_update | 修改事件 | | icloud_calendar_delete | 删除事件(四道闸门,见下) | 删除的四道闸门 删除默认关闭,且即使打开也要连过四关: 1. allowDeletes: true —— 配置层开关 2. maxDeletesPerCall —— 单次调用删除数量上限,默认 1 3. 事件必须带 ETag —— 防止误删已被改动的条目 4. 模型必须显式传入 confirm: true —— 挡住自动补全式的误触 任何一关不过,删除都会被拒绝并说明原因。
展开完整文档(README 剩余 1 节)
dsh-icloud-calendar auth set --account you@example.com

工具

插件向模型暴露 5 个工具:

| 工具 | 作用 |
| icloud_calendar_list | 列出所有日历(名称、URL、颜色、是否只读) |
| icloud_calendar_events | 按时间窗读取事件,自动展开重复日程 |
| icloud_calendar_create | 新建事件 |
| icloud_calendar_update | 修改事件 |
| icloud_calendar_delete | 删除事件(四道闸门,见下) |

删除的四道闸门

删除默认关闭,且即使打开也要连过四关:

1. allowDeletes: true —— 配置层开关
2. maxDeletesPerCall —— 单次调用删除数量上限,默认 1
3. 事件必须带 ETag —— 防止误删已被改动的条目
4. 模型必须显式传入 confirm: true —— 挡住自动补全式的误触

任何一关不过,删除都会被拒绝并说明原因。

命令行

dsh-icloud-calendar status # 查看连接状态与日历数量 dsh-icloud-calendar status --probe # 主动探测 iCloud 连通性(首次使用建议先跑) dsh-icloud-calendar calendars # 列出日历 dsh-icloud-calendar auth set # 写入 App 专用密码到系统凭据存储 dsh-icloud-calendar auth clear # 清除已存的密码

开发

pnpm install
pnpm build # tsc 编译到 lib/
pnpm test # 77 项测试
pnpm lint

测试覆盖 DAV 读取器、内存 CalDAV 假服务器,以及一套真实 cordis 挂载测试 —— 全部离线运行,不需要真实 iCloud 账号。

许可

MIT
Windows:密码只经过 stdin,不进 argv、不进 shell 历史 $p = Read-Host 'App 专用密码' $p | node lib/cli.js login --account you@example.com

macOS / Linux
read -rs -p 'app-specific password: ' P && printf '%s' "$P" | node lib/cli.js login --account you@example.com

安全边界:

- 密钥永不进入 profile 配置、工具参数、session log 或错误信息;
- DPAPI 明文只交给 PowerShell 子进程的 stdin,磁盘上是 DPAPI 密文(%LOCALAPPDATA%\dsh-icloud-calendar,.gitignore 已排除 .dpapi.xml);
- Linux 走 secret-tool 时密码走 stdin;macOS 钥匙串是唯一例外,security 只接受命令行参数,argv 会短暂可见(已在代码注释中标注);
- 环境变量名刻意不叫 PASSWORD* 之外的花样(DSH_CALENDAR_PASSWORD 那类名字会被部分日志脱敏工具整行打码,反而破坏文件)——本插件用 ICLOUD_APP_PASSWORD,DPAPI 子进程内部用 DSH_ICLOUD_STORE_FILE。

工具(模型可见)

| 工具 | 说明 |
| calendar_status | 配置/凭据来源/连通性自检,不返回密码 |
| calendar_list_calendars | 列出日历(名称、集合 URL、颜色、时区、组件类型);iCloud 提醒事项列表会标 supportsEvents=false |
| calendar_list_events | 按时间窗读事件,重复日程展开为逐条,可取消的实例被跳过;返回 uid 供删除使用 |
| calendar_create_event | 建事件;两个日期=全天事件(end 为排他),两个带偏移的时间戳=定时事件 |
| calendar_delete_events | 删除,默认干跑,四道闸门见下 |

删除的四道闸门(针对"一次误删 8 条")

1. 总开关:allowDeletes: false 是默认值,不打开就没有任何删除路径;
2. 窗口必填:start / end 必填,且删除窗口上限(默认 90 天)比读窗口(366 天)更小;
3. 精确指名:只能给 uid(来自 calendar_list_events)或 summaryEquals(完整标题、区分大小写)——子串/模糊匹配一律拒绝;
4. 二次确认:默认 dryRun=true 只报告匹配结果;真删必须 dryRun=false 且 confirmCount 等于匹配到的对象数,同时受 maxDeletesPerCall(默认 1)封顶。

注意:命中重复日程时"对象数"小于"出现次数",删除会移除整个重复序列,结果里会明确写出这一点。

命令行(不走模型也能用)

dsh-icloud-calendar status [--probe] [--json]
dsh-icloud-calendar login --account you@example.com # 密码从 stdin 读 dsh-icloud-calendar logout --account you@example.com dsh-icloud-calendar calendars dsh-icloud-calendar events --start 2026-01-05T00:00:00+08:00 --end 2026-01-12T00:00:00+08:00 dsh-icloud-calendar add --summary "评审" --start 2026-01-05T09:00:00+08:00 --end 2026-01-05T10:00:00+08:00 --time-zone Asia/Shanghai dsh-icloud-calendar delete --start 2026-01-01T00:00:00+08:00 --end 2026-02-01T00:00:00+08:00 --uid <uid> dsh-icloud-calendar delete ... --uid <uid> --dry-run=false --yes

CLI 复用同一套客户端与删除闸门;delete 永远先跑一次干跑,再要求 --yes,所以 --yes 单独出现不会删掉任何没人看过的东西。

踩坑清单(实测)

| # | 坑 | 现象 | 处理 |
| 1 | 用登录密码 | HTTP 401,看起来像服务器故障 | 只能 App 专用密码;错误码 CALENDAR_AUTH_FAILED 会直接说明 |
| 2 | 每次调用新建连接 | 单次操作约 13 秒 | host 服务常驻复用连接;CLI 单进程内完成多步;读操作本身按日历串行以免被限流 |
| 3 | DPAPI 走 PowerShell | 本机实测每次读写约 9 秒 | 只在建立连接时读一次密钥并缓存;不要把凭据读取放进每步 |
| 4 | 删除慢 | 按日历全扫描,事件越多越慢 | 删除必须带日期窗口(默认 ≤90 天),不做全库扫描 |
| 5 | 删除过杀 | 模糊匹配一次删掉 8 条 | 精确匹配 + 干跑 + confirmCount + 上限 1 |
| 6 | 没有超时 | 网络卡住就干等 | 每请求 30s deadline,429/5xx 指数退避重试;401 不重试(避免锁号) |
| 7 | 时区 | 全天事件差一天、无偏移时间戳含义不明 | 无 offset 的时间戳直接拒绝;全天事件按 iCalendar 的日期字段读取,不经过本地时区 |
| 8 | 代理 | 国内直连 iCloud 可能不通 | proxyUrl(需要可选依赖 undici,未装时给出明确提示) |
| 9 | 热重载预期 | 换了插件 JS 却没生效 | 新增 bundle 可经 HMR 生效,替换已安装包必须重启 |
| 10 | 重复日程 | 以为删的是"这一次" | 删除作用于整个对象(整个序列),结果中明确提示 |
| 11 | Reminders 列表 | 被当成日历读/写 | 若服务端公布了组件集合且不含 VEVENT,该集合会被跳过、显式指名则报错。但实测中国区 iCloud 的 19 个日历全部不公布组件集合,所以无法预判:对「提醒」集合查询一个月返回 0 条,代价只是多一次请求。写入该集合的行为未在本插件内验证 |
| 12 | 多日历歧义 | 不知道写进哪个日历 | 只有唯一候选时才允许省略 calendar,否则报错并列出候选。实测账号里有两个都叫「家庭」的日历,裸用名字会被拒绝并要求给集合 URL |
| 13 | 中国区账号的分区主机 | 以为要连 caldav.icloud.com | 入口仍填 https://caldav.icloud.com/ 即可;principal/calendar-home 发现会跟着服务端给出的分区主机走。实测真实账号的日历 URL 落在 p236-caldav.icloud.com.cn,19 个日历全部正常 |

架构

| 文件 | 职责 |
| src/client.ts | CalDAV 客户端:principal/home 自动发现、calendar-query、PUT/GET/DELETE、超时与重试、可选代理 |
| src/xml.ts | 面向 CalDAV 多状态响应的极小 XML 读取器(命名空间前缀无关) |
| src/ical.ts | iCalendar 语义:时间戳校验、重复日程展开、事件构造与折行、全天事件边界 |
| src/credentials.ts | 跨平台凭据存储(DPAPI / 钥匙串 / libsecret / 关闭) |
| src/config.ts | schemastery 配置模式、默认值、凭据解析与来源说明 |
| src/policy.ts | 删除闸门(唯一实现,服务与命令行共用) |
| src/host.ts | cordis 服务 ctx.icloudCalendar:连接复用、缓存、策略 |
| src/tools.ts | defineTool 注册 5 个模型可见工具 |
| src/cli.ts | 独立命令行入口 |
| tests/ | vitest:DAV 读取器(xml.test.ts,含「只返回第一个自闭合元素」这类回归)、内存 CalDAV 假服务器(401/503/超时)、iCalendar、删除闸门、凭据存储、以及用真实 cordis 上下文挂载两个插件行的激活测试 |

一个刻意的取舍:不手填日历集合 URL。插件走标准的 current-user-principal → calendar-home-set → 列日历 发现流程,所以换账号/换服务器只要改 account。

开发

pnpm install
pnpm typecheck # src + tests
pnpm test # 77 项:DAV 读取器 + mock CalDAV + 真实 cordis 挂载
pnpm build # tsc → lib/

测试不需要 Apple ID、不需要网络:tests/mock-caldav.ts 是一个进程内 HTTP 服务器,实现了发现、REPORT、PUT、GET、DELETE,并可注入 401、连续 503、慢响应。tests/activation.test.ts 则用真实 @deepseek-ai/cordis 上下文把 host 行与 tools 行挂起来,断言服务注册、inject 满足、5 个工具入册、calendar_status 可执行、以及只读闸门生效。

启动验证(以及为什么「没报错」能算证据)

除单测外,还做了两层真机验证:

1. 组合层:dsh plugin --profile <probe> add <本包> 后 dsh --profile <probe> --dump-config,能看到 # == dsh-icloud-calendar 层与两行;
2. 启动层:从官方 headless 模板建一个临时 profile 装上本包并真实启动,进程 2.2 秒内走到 LLM 阶段、只因缺少 LLM 凭据退出(MISSING_CREDENTIAL),没有任何插件行报错。

第 2 条之所以有说服力,是因为做了反证实验:把 cordis.patch.yml 里的模块名改成不存在的路径后,启动会直接卡在组合阶段(600 秒不退出),而不是静默继续。也就是说「启动能走到 LLM 阶段」确实等价于「两行插件成功激活」,而不是「错误被吞了」。

3. 端到端:把测试用的 CalDAV 服务编译成独立进程跑在真实 HTTP 端口上,再用构建产物 lib/ 里的命令行走完整流程(结果见下表)。这条路径覆盖了真实的 HTTP 层、iCalendar 构造、策略闸门与退出码,而不只是单测内的函数调用。

| 命令 | 结果 |
| status --probe | 配置/凭据来源/连接全部正常,退出码 0 |
| calendars | 5 个日历:Reminders 标 EVENTS=no、Shared 标 READ-ONLY=yes、Mixed(VTODO+VEVENT)标 EVENTS=yes |
| events | 5 条出现:1 条单次 + 4 条 weekly 展开(RRULE 在窗口内正确展开) |
| add(09:00+08:00) | 建成 2026-01-06T01:00:00Z,时区正确折算 |
| delete(未加 --dry-run=false) | DRY RUN — 1 object(s),什么都没删 |
| delete(--dry-run=false 但没 --yes) | 拒绝:CALENDAR_UNSAFE_DELETE: Refusing to delete 1 object(s) without --yes |
| delete --summary-equals=E2E(部分标题) | 匹配 0 条 —— 模糊匹配确实不存在 |
| delete --summary-equals=E2E review --dry-run=false --yes | DELETED — 1 object(s) |
| 删除后再 events | 新建的那条消失,原有 5 条出现完好 |
| 退出码契约 | 成功 0 / 闸门或配置问题 1 / 未知命令 2 / help 0 |

已知限制 / 路线图

- 没有设置页表单(配置项未标 .volatile()),改配置需编辑 profile 并重载;
- 暂不支持更新事件(calendar_update_event)与多账号同时挂载(可用多个 profile 行 + 不同 account 变通);
- 重复日程的 RRULE 展开在客户端完成,单序列上限 maxOccurrencesPerSeries(默认 400),命中上限会在结果里报告;
- 未做真实 iCloud 账号的端到端验收(见文首状态说明)。

许可证与致谢

MIT。

设计与实现参考了同一生态里的两个开源项目(代码为独立实现,未复制):

- moziforge/calendar-plugins(Apache-2.0)—— iCloud CalDAV 的凭据与错误码处理思路;
- STARDUSTLC666/dsh-calendar(MIT)—— DSH 插件形态与多日历解析实践。

插件形态遵循官方 cordis-plugin-development skill 与 docs/user/develop/basic/publish.md。
预览分享卡片长什么样 ↓CaiMingshen/dsh-icloud-calendar
分享 · DeepSeek Harness Hub
CaiMingshen/dsh-icloud-calendar

通过 CalDAV 读写 iCloud 日历事件

这是你的仓库?认领条目

回到顶部