dsh-calendar 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -35
- package/README.en.md +25 -195
- package/README.md +24 -228
- package/docs/CHANGELOG.en.md +57 -0
- package/docs/USAGE.en.md +205 -0
- package/docs/USAGE.md +235 -0
- package/docs/VALIDATION.md +25 -0
- package/docs/validation/0.10.0.md +27 -0
- package/lib/caldav.d.ts +7 -0
- package/lib/caldav.js +59 -1
- package/lib/client.js +2115 -2008
- package/lib/config.d.ts +2 -2
- package/lib/import-ical.d.ts +28 -0
- package/lib/import-ical.js +235 -0
- package/lib/import-web.d.ts +31 -0
- package/lib/import-web.js +179 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +4 -0
- package/lib/web.d.ts +2 -0
- package/lib/web.js +2 -0
- package/package.json +6 -4
- package/scripts/google-oauth.mjs +279 -279
package/CHANGELOG.md
CHANGED
|
@@ -1,22 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 更新记录
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[返回简介](README.md) · [使用说明](docs/USAGE.md) · [验证记录](docs/VALIDATION.md)
|
|
4
|
+
|
|
5
|
+
[历史英文记录](docs/CHANGELOG.en.md)
|
|
6
|
+
|
|
7
|
+
## 0.10.0 (2026-10-03)
|
|
8
|
+
|
|
9
|
+
- 新增 ICS 导入:选择文件或粘贴内容,预览后自行勾选、分批确认;保留重复规则、排除日期、改期例外和时区定义。
|
|
10
|
+
- 未标注时区的时间必须主动选择解释时区;未知时区、无效日期及不完整的重复系列显示具体修正原因。补齐 IANA 时区使用 `timezones-ical-library`,文件自带时区仅在本次解析中生效。
|
|
11
|
+
- 在目标日历完整检查 UID,跳过已有日程。部分失败可单独重试;预览过期或连接切换后必须重新预览;写入使用 `If-None-Match: *`,不覆盖已有对象。
|
|
12
|
+
- 导入为私人副本,移除参与者、组织者及非屏幕提醒;重叠提示只覆盖系列首场,界面明确说明范围。
|
|
13
|
+
- 连接表单区分模型 API 密钥与日历授权;修复 390px 宽度下设置页导航挤压日历面板的问题。
|
|
14
|
+
- 验证范围与限制见 [验收记录](docs/VALIDATION.md) 和 [导入说明](docs/USAGE.md#导入-ics-日程)。
|
|
15
|
+
|
|
16
|
+
## 0.9.4 (2026-10-02)
|
|
17
|
+
|
|
18
|
+
- Upgrade Undici to 8.11.2; production dependency audit reports 0 findings.
|
|
19
|
+
- Add local HTTP proxy integration tests for CalDAV REPORT method/body/auth preservation and cancellation during response streaming.
|
|
20
|
+
- Clarify that proxy use depends on server reachability, rather than requiring a VPN by provider name.
|
|
21
|
+
- Windows: 180 tests passed. Browser acceptance covers local CalDAV agenda, connection testing and configuration saving in official DSH 0.2.0-rc.2.
|
|
22
|
+
|
|
23
|
+
## 0.9.3 (2026-10-01)
|
|
4
24
|
|
|
5
25
|
- 修复 Google 切换到 iCloud、Nextcloud 或自建 CalDAV 时仍继承 OAuth 认证方式的问题;测试连接和保存使用新服务商的认证方式,原服务商的已有凭据继续保留。
|
|
6
26
|
- 连接摘要识别环境变量中的密码、client secret 和 refresh token,输入框仅提示“已配置”,不回传或复制环境密钥;纯空白值不算已配置。
|
|
7
27
|
- 修改连接草稿后清除过时的测试结论,避免把旧账号的失败或成功显示成新草稿的结果。
|
|
8
28
|
- 4 项新回归先复现后修复,178 项 Windows 测试通过;验证宿主为官方源码构建的 Harness 0.2.0-rc.2。
|
|
9
29
|
|
|
10
|
-
## 0.9.2
|
|
30
|
+
## 0.9.2 (2026-09-29)
|
|
11
31
|
|
|
12
32
|
- 修复:OAuth 刷新失败改为**按错误码给出可操作指引**(授权失效 / 应用配置错误 / 权限范围不足 / 限流或服务端错误 / 需重新验证),并在诊断信息里带上 OAuth 标识与 HTTP 状态。
|
|
13
33
|
- 安全:错误信息**只接受标准错误标识符**(长度与字符集受限),绝不回显 `error_description` 等自由文本,避免把 client secret 或 refresh token 带进日志与界面。
|
|
14
|
-
|
|
34
|
+
|
|
35
|
+
## 0.9.1 (2026-09-28)
|
|
15
36
|
|
|
16
37
|
- 兼容验证更新到 Harness 0.2.0-rc.1:173 项测试与 18 插件共同加载检查通过(96 工具 / 35 技能注册)。
|
|
17
38
|
- 运行时代码未变;同步中英文兼容性声明。
|
|
18
39
|
|
|
19
|
-
## 0.9.0
|
|
40
|
+
## 0.9.0 (2026-09-27)
|
|
20
41
|
|
|
21
42
|
- 适配 Harness 0.1.7 设置表单与旧配置导入,正确声明设置服务依赖。示例日历改为只读,保留视图切换和详情查看,避免示例操作写入真实日历。
|
|
22
43
|
|
|
@@ -27,25 +48,25 @@
|
|
|
27
48
|
- 自带设置页向宿主登记 `configure({ auto: false })`,并声明 `tools` 与 `settings` 服务依赖,保证设置面板接入宿主存储。
|
|
28
49
|
- **测试 163 → 172**:Config 形状(volatile / secret / 无默认值)、活引用摊平、两代宿主判定、mutate 与 replace 两条写路径、搬迁的一次性与「不串实例」、apply 在 0.1.7 宿主上不再抛错且面板路由照常挂载、面板保存全链路。
|
|
29
50
|
|
|
30
|
-
## 0.8.4
|
|
51
|
+
## 0.8.4 (2026-09-23)
|
|
31
52
|
|
|
32
53
|
- **适配 Harness 0.1.7 的 Web 子路径部署**:面板请求改为跟随应用挂载路径。此前宿主被反向代理挂在子路径下时,设置与事件操作会打到站点根,一律 404。
|
|
33
54
|
- 未配置账号/密码时的提示改为指向面板「连接设置」(不再要求用户去改 `cordis.patch.yml` 并重启)。
|
|
34
55
|
- 新增子路径部署下的请求路径回归(`test/public-mount.test.mjs`)。
|
|
35
56
|
|
|
36
|
-
## 0.8.3
|
|
57
|
+
## 0.8.3 (2026-09-21)
|
|
37
58
|
|
|
38
59
|
- 已知事件的修改、删除及创建后回读改为直接 CalDAV multiget,省掉整集合索引查询;每次仍读取最新 ETag,保留条件写入。
|
|
39
60
|
- 限定事件地址属于当前日历;无 calendar-data 的响应不会触发写入或删除。
|
|
40
61
|
- 新增真实 tsdav 请求回归,覆盖请求次数、ETag、取消与缺失事件;全量 161 项测试通过。更新中英文介绍,补齐已有的可视化面板与连接设置说明。
|
|
41
62
|
|
|
42
|
-
## 0.8.2
|
|
63
|
+
## 0.8.2 (2026-09-20)
|
|
43
64
|
|
|
44
65
|
- **修主题色误用**:此前把宿主遮罩 token `--dsw-alias-bg-mask-3` 当淡色 hover/底纹用;DSH 0.1.6-alpha.2 里它实测亮/暗主题都是 `rgba(0,0,0,.48)`,导致周视图「今天」整列、月视图格子 hover、加载骨架屏、详情 tag 全部发深灰(浏览器视觉验收抓到,视觉上像一块无字灰板)。现改为 `color-mix`(label-primary 5%~7% / business-primary 7%),mask-3 只保留在 `.dshc-overlay` / `.dshc-modalwrap` 两处真正的遮罩上。
|
|
45
66
|
- **修 Esc 关错层**:详情抽屉/新建表单打开时按 Esc 会连整个日历浮层面板一起关掉。现在 Esc 只关最上面一层,第二次才关面板;焦点在上层宿主 `PRIM.Modal` 里时让行给宿主;输入法组合状态(`isComposing`)不误关;设置页与对话页两个面板并存时一次也只关一层,并按实际打开顺序关。
|
|
46
67
|
- **测试**:render 测试 7 → 15 条(新增抽屉/宿主 Modal 真假路径/宿主高层浮层让行/普通宿主元素兜底/层序/IME 等用例),全量 154 pass。该版经两位独立评审:首轮抓到「宿主 Portal 抢 Esc」「双面板一次连关两层」「IME 误关」,二轮抓到「宿主高层 Modal 盖上来时关错下面那层」,均已修。
|
|
47
68
|
|
|
48
|
-
## 0.8.1
|
|
69
|
+
## 0.8.1 (2026-09-20)
|
|
49
70
|
|
|
50
71
|
- **新增真 DOM 行为测试层**(`test/render.test.mjs`,devDependencies 里加 `react` / `react-dom` / `jsdom`,不进发布包):用 jsdom + 真 React 渲染周视图,然后真派发 `pointerdown/move/up`、`click`、`keydown`。7 条覆盖:渲染不抛、点事件不会顺手弹「新建」、拖动只提交一次且按视口坐标换算、拖动中 Esc 零请求、卸载后不留监听器且之后的 pointerup 不提交、拖到边缘 rAF 持续滚动、重复日程被面板挡住。
|
|
51
72
|
- **它的第一批产出就是三个真问题**:
|
|
@@ -53,7 +74,7 @@
|
|
|
53
74
|
② `requestAnimationFrame` / `cancelAnimationFrame` 按全局调用:浏览器里有,但模块作用域下应当显式走 `window.*`。
|
|
54
75
|
③ 三处 React key 警告(日期头、事件块、幽灵块、日列的子节点用了数组却没给 key)——dev 模式控制台一直在吵。
|
|
55
76
|
|
|
56
|
-
## 0.8.0
|
|
77
|
+
## 0.8.0 (2026-09-19)
|
|
57
78
|
|
|
58
79
|
- **新增:周视图拖拽改期**。拖动事件块上下改时间、左右换天(15 分钟吸附);拖块底部 6px 改时长;拖动中显示幽灵落点,与已有日程重叠时描成黄色(允许重叠,只是提醒);拖到容器上下边缘自动滚动,长会议不用先滚再拖。
|
|
59
80
|
- **新增:键盘改期**。Tab 聚焦后 ↑/↓ 挪 15 分钟、Shift+↑/↓ 挪 1 小时、←/→ 挪一天、Enter 打开详情、拖动中 Esc 取消 —— 没有鼠标也能完成一次改期。
|
|
@@ -66,14 +87,14 @@
|
|
|
66
87
|
- **修一个渲染即崩的 P0**:焦点恢复的 `useEffect` 依赖数组引用了声明在后面的 `data`,TDZ 直接抛错、整个面板打不开(纯函数测试测不到,评审渲染才发现)。
|
|
67
88
|
- 明确取舍:拖动越过午夜**滚到次日**(与 Google 日历一致,也让幽灵块按 `slot.dayKey` 的日列渲染自洽),而不是硬钳在当天最后一刻。
|
|
68
89
|
|
|
69
|
-
## 0.7.0
|
|
90
|
+
## 0.7.0 (2026-09-19)
|
|
70
91
|
|
|
71
92
|
- **新增**:面板**记住上次用的视图**(月/周/议程,存 localStorage;隐私模式写不进去就退回「月」),下次打开不再固定跳回月视图。
|
|
72
93
|
- **新增**:周视图**默认落在 07:00**(半夜那几格基本没人看,但需要时还能往上滚,不裁剪任何事件)。
|
|
73
94
|
- **适配窄容器**:面板在设置页里比对话页浮层窄得多,现在用容器查询自适应 —— 窄的时候隐藏芯片里的时间前缀(让日程标题露出来)、隐藏重复的面板标题、收紧工具栏间距。
|
|
74
95
|
- **修复**:月视图的格子由 `<button>` 改为 `<div role="button" tabIndex=0>`。按钮不能嵌套按钮(芯片本身是按钮),一旦同格有多条日程,浏览器会把外层按钮提前闭合、把芯片挤成网格的兄弟节点;现在结构合法,键盘 Enter/空格仍可新建,并加了回归测试(月视图里不允许出现 `button`)。
|
|
75
96
|
|
|
76
|
-
## 0.6.0
|
|
97
|
+
## 0.6.0 (2026-09-19)
|
|
77
98
|
|
|
78
99
|
- **新增设置页日历面板**:月 / 周 / 议程三种视图;点日程开右侧详情(时间、时长、重复规则的人话、地点、备注、uid),可直接改标题时间、加重复、删除(二次确认);新建支持全天 / 地点 / 备注 / RRULE;保存前做冲突检测。
|
|
79
100
|
- **新增对话页悬浮入口**:右下角 📅 小按钮点开同一个面板(非模态、Esc 关闭)。自绘 root 使用平台 seed 模块表里的 `react-dom/client`,控件优先用官方 `ui-primitives`(Button / Input / Checkbox / Modal / writeClipboard),老宿主缺这些时自动退回自绘控件,面板不会整块消失。
|
|
@@ -94,32 +115,37 @@
|
|
|
94
115
|
- **修复**:Windows 上打开浏览器改用 `rundll32 url.dll,FileProtocolHandler`。此前用 `cmd /c start <url>`,授权 URL 里的 `&` 被 cmd 当成命令分隔符,浏览器只收到第一段 —— Google 的表现是 `Required parameter is missing: response_type`(预检查不出来,因为脚本自己发的请求是完整的)。换 token 脚本改为**交互式**:直接敲 `node scripts/google-oauth.mjs`,它会依次问 clientId 与 clientSecret,并识别「把文档示例文字当值粘进来」这种情况。
|
|
95
116
|
- CI 增加 `node --check lib/client.js`(网页端源码不走 tsc,只能语法自检)。测试 79 → 88 项。
|
|
96
117
|
|
|
97
|
-
|
|
118
|
+
## 0.5.4 (2026-09-19)
|
|
119
|
+
|
|
120
|
+
- **修复**:①`calendar_update` 重建 VEVENT 会静默丢掉原事件的 ATTENDEE / ORGANIZER / EXDATE / STATUS / CATEGORIES / VALARM(改一次标题就删掉邀请人和提醒),现在改为在原始 VCALENDAR 上做字段级覆盖,并补齐 RFC 5545 必需的 VERSION / PRODID / UID / DTSTAMP;②RECURRENCE-ID 覆盖实例被忽略(被单独改期的实例按原时间+旧标题返回,或被 EXDATE 排掉后整条消失),现在建覆盖映射并在展开时替换对应实例;③`calendar_create` 返回本地拼的 uid 而忽略服务器 `Location`,导致 create 后 update 找不到事件(现在优先取 Location,缺失时回读确认),且 create 的 PUT body 补上 VERSION / PRODID / DTSTAMP。**优化**:重复事件展开预算耗尽时不再静默返回 0 条(DTSTART 在 2010 的每小时系列实测 680ms 且返回空),改为显式报错并提示缩小时间范围或补 COUNT/UNTIL;`calendar_search` 支持 `start` / `end` 并走 timeRange 查询(缺省用有界窗口),不再先整本下载再过滤。测试 68 → 79 项。
|
|
121
|
+
|
|
122
|
+
## 0.5.3 (2026-09-12)
|
|
123
|
+
|
|
124
|
+
- 复验官方 Harness 0.1.5-rc.1,更新整套同载与真实服务验证记录;运行时代码未变。
|
|
98
125
|
|
|
99
|
-
##
|
|
126
|
+
## 0.5.2 (2026-09-08)
|
|
100
127
|
|
|
101
|
-
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
128
|
+
- 补充官方 Harness 0.1.3-alpha.2 的安装、加载与真实 Google 工具调用验证,更新兼容性和 Node 版本要求;运行时代码与 0.5.0 相同。
|
|
129
|
+
|
|
130
|
+
## 0.5.1 (2026-09-08)
|
|
131
|
+
|
|
132
|
+
- 补充真实 Google OAuth/CalDAV 读取验证、`calendar.readonly` 与 `calendar` 范围对比及 Testing 刷新令牌到期说明;运行时代码与 0.5.0 相同。
|
|
133
|
+
|
|
134
|
+
## 0.5.0 (2026-09-07)
|
|
135
|
+
|
|
136
|
+
- 修复 Google CalDAV #2:新增 OAuth 凭据与环境变量配置、请求时刷新、取消与代理透传;健康检查区分 Basic/OAuth,修正误导的应用专用密码说明。保留其他服务的 Basic 认证。
|
|
137
|
+
|
|
138
|
+
## 0.4.0 (2026-08-26)
|
|
139
|
+
|
|
140
|
+
- 新增 `calendar_health` 自检(离线检查 CalDAV 端点与凭据配置,不验证连接)。
|
|
141
|
+
|
|
142
|
+
## 0.3.2 (2026-08-15)
|
|
143
|
+
|
|
144
|
+
- 修复 `calendar_update` 更新其他字段时丢失 `rrule` 的问题。
|
|
109
145
|
- 更新与新建都会校验 `end >= start`,并拒绝 `2025-02-30` 这类不存在的日期。
|
|
110
146
|
- `calendar_list` / `calendar_search` 输出按开始时间稳定排序;搜索 `limit` clamp 到 1-200。
|
|
111
147
|
- CalDAV 客户端创建失败后清空缓存,下一次调用可自动重试,不再永久复用 rejected promise。
|
|
112
148
|
|
|
113
|
-
##
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- **0.5.3 (2026-09-11)**: revalidate official Harness 0.1.5-rc.1 and refresh suite co-load and live-service evidence; runtime code is unchanged.
|
|
117
|
-
- **0.5.2 (2026-09-08)**: document installation, loading and real Google tool execution in official Harness 0.1.3-alpha.2; update compatibility and Node requirements. Runtime code is unchanged from 0.5.0.
|
|
118
|
-
- **0.5.1 (2026-09-08)**: document live Google OAuth/CalDAV read validation, the `calendar.readonly` versus `calendar` scope results and Testing refresh-token expiration. Runtime code is unchanged from 0.5.0.
|
|
119
|
-
- **0.5.0 (2026-09-07)**: fix Google CalDAV #2 with OAuth configuration/environment credentials, request-time refresh, cancellation and proxy forwarding. Make health checks and error guidance authentication-aware; retain Basic authentication for other servers.
|
|
120
|
-
- **0.4.0**: new `calendar_health` self-check (offline endpoint and credential configuration checks, not a connection test).
|
|
121
|
-
- **0.3.2**:
|
|
122
|
-
- Fix `calendar_update` dropping `rrule` while updating other fields.
|
|
123
|
-
- Validate `end >= start` and reject impossible dates such as `2025-02-30`.
|
|
124
|
-
- Sort `calendar_list` / `calendar_search` output by start time and clamp search `limit` to 1-200.
|
|
125
|
-
- Reset the cached CalDAV client after creation failure so the next tool call can retry.
|
|
149
|
+
## 更早的改动
|
|
150
|
+
|
|
151
|
+
完整历史可查阅 [GitHub 提交记录](https://github.com/STARDUSTLC666/dsh-calendar/commits/main)。
|
package/README.en.md
CHANGED
|
@@ -1,217 +1,47 @@
|
|
|
1
1
|
# dsh-calendar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[中文](README.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
View, create and reschedule CalDAV events from DSH.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/dsh-calendar) [](https://www.npmjs.com/package/dsh-calendar)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## What it does
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- Month, week and agenda views with drag and keyboard rescheduling.
|
|
12
|
+
- Create, search, update and delete events, including recurring occurrences.
|
|
13
|
+
- Import an ICS file or pasted content after reviewing timezones, duplicates and overlap hints.
|
|
14
|
+
- Connect Google, iCloud, Nextcloud or your own CalDAV server.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Install
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Version **0.9.0** (2026-09-23) follows the settings interface introduced in Harness **0.1.7**: the host removed `ctx.settings.register`, so configuration now lives in the entry's own `Config`. Every connection field is declared `volatile` (panel edits apply live, no restart) and the three credentials carry the `secret` role (never returned to the browser, only whether they are set). On the first start after an upgrade the plugin moves the retired `dsh-calendar` section of `settings.yaml` into the profile — filling only absent keys, once, with values already written in the profile winning. Hosts on 0.1.5 / 0.1.6 keep the previous namespace registration unchanged. The verified host baseline is official-source **0.2.0-rc.2**; live CalDAV operations still require their own account validation.
|
|
18
|
-
|
|
19
|
-
Version **0.8.4** (2026-09-23) supports the Web mount paths introduced in Harness **0.1.7**. The calendar panel keeps its requests under the application path when deployed through a reverse proxy, preserving normal settings and event operations.
|
|
20
|
-
|
|
21
|
-
On 2026-09-10, npm `dsh-calendar@0.5.2` passed installation through this Harness release's official CLI and registration of all 6 tools. Host execution of `calendar_list` refreshed a real Google token (200), read via CalDAV REPORT (207) and rendered model-facing results. Only Google reads were tested; no writes were performed. This is a historical read-only verification record.
|
|
22
|
-
|
|
23
|
-
Follows the official [plugin packaging and installation requirements](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md): an ESM entry point, prebuilt `lib/`, `dsh.bundle.patch` and a `cordis.patch.yml` layer. The plugin explicitly injects `tools` and supplies JSON Schema parameters, canonical output and rendering, with no runtime imports of `@deepseek-ai/*` internals. Use Node 22.19 or later within 22.x, or Node 24 or later. Harness is evolving rapidly; the version above is the tested baseline.
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
dsh plugin --profile web add dsh-calendar
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
After installing, restart dsh. The plugin inserts a `calendar` config line into the profile (see this package's cordis.patch.yml). The default provider is custom with no credentials filled in; the plugin still loads, but tools throw a Chinese guidance error when called, prompting you to complete the configuration.
|
|
32
|
-
|
|
33
|
-
## Uninstall
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
dsh plugin --profile web remove dsh-calendar
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## Configuration
|
|
43
|
-
|
|
44
|
-
**Recommended: configure it in the panel (0.6.0+)**
|
|
45
|
-
|
|
46
|
-
Open「Connection」in the panel toolbar, pick a provider, fill in the address and account, hit **Test connection** (it really lists the next 30 days) and then **Save and enable**. The tools pick the new configuration up on their next call — no restart, no YAML editing.
|
|
47
|
-
|
|
48
|
-
What the panel saves lives in your local `settings.yaml` under the `dsh-calendar` namespace (the password is a secret field: no logs, no exports). If the host has no settings service (or the namespace cannot be registered), the connection is stored in the plugin’s own file `$DSH_HOME/data/dsh-calendar/connection.json` (0600) instead, and the panel says which one is in use. **The YAML remains the base layer**: any field the panel never touched still comes from it, so an existing `cordis.patch.yml` setup needs no migration; a headless host without a settings page still configures through YAML only.
|
|
49
|
-
|
|
50
|
-
Per-provider notes:
|
|
51
|
-
|
|
52
|
-
- **iCloud**: `caldavUrl` looks like `https://caldav.icloud.com/<numeric id>/calendars/<calendar id>/`; use an [app-specific password](https://appleid.apple.com/)
|
|
53
|
-
- **Nextcloud / self-hosted**: server URL + username + calendar name (self-hosted: the full collection URL, keep the trailing slash) and an app password
|
|
54
|
-
- **Google**: OAuth only — Google rejects every Basic password, including app passwords; needs `clientId` / `clientSecret` / `refreshToken` / `calendarId`
|
|
55
|
-
|
|
56
|
-
### Or: write YAML (advanced / headless)
|
|
57
|
-
|
|
58
|
-
The fields below mirror the panel form one-to-one; whatever the panel never set comes from here.
|
|
59
|
-
All configuration lives in your profile's cordis.patch.yml; override the `calendar` line by id (overriding replaces that line's config wholesale). Common fields:
|
|
60
|
-
|
|
61
|
-
- `provider`: google | icloud | nextcloud | custom
|
|
62
|
-
- `caldavUrl`: full calendar collection URL (required for custom / icloud; google / nextcloud can also override the preset manually)
|
|
63
|
-
- `authMethod`: `basic` | `oauth`; Google defaults to and requires `oauth`, other providers default to `basic`. Opt in explicitly for another OAuth server. Google credentials in the environment never switch a Basic provider to OAuth automatically.
|
|
64
|
-
- `username` / `password`: required only for Basic authentication. Use an app-specific password for iCloud, preferably via `DSH_CALENDAR_PASSWORD`. **Google CalDAV rejects all Basic passwords, including app-specific passwords.**
|
|
65
|
-
- `clientId` / `clientSecret` / `refreshToken`: required for OAuth; also read from `DSH_CALENDAR_CLIENT_ID` / `DSH_CALENDAR_CLIENT_SECRET` / `DSH_CALENDAR_REFRESH_TOKEN`. Non-empty config values take precedence over environment variables. Never commit secrets or tokens to Git.
|
|
66
|
-
- `tokenUrl`: also read from `DSH_CALENDAR_TOKEN_URL`; defaults to `https://oauth2.googleapis.com/token` for Google and is required for other OAuth providers. OAuth tokenUrl and caldavUrl must use HTTPS without embedded credentials, query parameters or fragments.
|
|
67
|
-
- `calendarId`: google-specific, the calendar ID (usually your email)
|
|
68
|
-
- `host` / `user` / `calendar`: nextcloud-specific
|
|
69
|
-
- `proxyUrl`: optional HTTP proxy (e.g. `http://127.0.0.1:7890`); both OAuth token requests and CalDAV requests use this transport
|
|
70
|
-
|
|
71
|
-
### Google example
|
|
72
|
-
|
|
73
|
-
```yaml
|
|
74
|
-
- id: calendar
|
|
75
|
-
name: dsh-calendar
|
|
76
|
-
config:
|
|
77
|
-
provider: google
|
|
78
|
-
calendarId: you@gmail.com
|
|
79
|
-
# authMethod: oauth # the default for Google
|
|
80
|
-
# Provide clientId / clientSecret / refreshToken via environment variables
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Google's CalDAV collection URL is assembled by the plugin: `https://apidata.googleusercontent.com/caldav/v2/<calendarId>/events`.
|
|
84
|
-
|
|
85
|
-
Set these placeholder values in the same terminal that starts source-run `pnpm dsh` or ordinary `dsh`:
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
export DSH_CALENDAR_CLIENT_ID='your OAuth client ID'
|
|
89
|
-
export DSH_CALENDAR_CLIENT_SECRET='your OAuth client secret'
|
|
90
|
-
export DSH_CALENDAR_REFRESH_TOKEN='your authorized refresh token'
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
These credentials come from your own Google Cloud OAuth client and a user authorization, not an email app password. Follow the [Google CalDAV setup guide](https://developers.google.com/workspace/calendar/caldav/v2/guide) to enable the API and configure OAuth. Request `https://www.googleapis.com/auth/calendar` for calendar read/write and offline access (`access_type=offline`) to obtain a refresh token; see [Google's offline authorization documentation](https://developers.google.com/identity/protocols/oauth2/web-server#offline). Supply an already authorized refresh token, or mint one with the helper below.
|
|
94
|
-
**Getting a refresh token (one command)**: register the client as a **Desktop app**, then:
|
|
18
|
+
In DSH Desktop, install `dsh-calendar` from the Plugins panel. If the bundled dsh command is available:
|
|
95
19
|
|
|
96
20
|
```bash
|
|
97
|
-
|
|
98
|
-
node scripts/google-oauth.mjs --client-id <your clientId> --client-secret <your clientSecret>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
The script spins up a **one-shot local callback** (`http://127.0.0.1:<random port>/`), opens the browser for consent, exchanges the code and prints the refresh token — no hand-built URLs, no third-party playground.
|
|
102
|
-
|
|
103
|
-
Paste it into the panel’s「Connection」form (provider Google, calendarId = your Gmail address), or use the `DSH_CALENDAR_CLIENT_ID` / `DSH_CALENDAR_CLIENT_SECRET` / `DSH_CALENDAR_REFRESH_TOKEN` environment variables.
|
|
104
|
-
|
|
105
|
-
> While the OAuth consent screen is still in **Testing**, the refresh token lasts 7 days: hit **Publish app** on the OAuth consent screen to make it permanent (no review needed for personal use).
|
|
106
|
-
|
|
107
|
-
**Google CalDAV scope validation (2026-09-08):** for the same account and calendar, `calendar.readonly` allowed token refresh (200) and collection PROPFIND (207), but the event REPORT returned 403. With the `calendar` scope above, REPORT returned 207; a fresh verification process also refreshed the token and read successfully. Use this CalDAV scope configuration and verify an actual `calendar_list` request: successful token acquisition or collection discovery alone does not prove events are readable. This live test performed reads only; Google write operations were not tested.
|
|
108
|
-
|
|
109
|
-
The `calendar` scope permits viewing, editing, sharing and deleting accessible calendars; review that access before granting it. See [Google's scope definitions](https://developers.google.com/workspace/calendar/api/auth). For an external OAuth app in **Testing**, a refresh token with Calendar scopes expires after 7 days. Long-term use needs reauthorization or an appropriate production configuration under Google's requirements; see [refresh token expiration](https://developers.google.com/identity/protocols/oauth2#expiration).
|
|
110
|
-
|
|
111
|
-
Access tokens are cached in memory and checked before each DAV request, with refresh before expiry. Both token and DAV requests inherit the tool call's AbortSignal. A 401 invalidates the token for the next call; **writes are never replayed automatically**. OAuth requests do not follow redirects or send Bearer tokens to cross-origin object hrefs, so configure the final calendar collection URL. Runtime tokens are not written to configuration or logs. If another OAuth provider rotates refresh tokens, supply valid credentials again when restarting.
|
|
112
|
-
|
|
113
|
-
### iCloud example
|
|
114
|
-
|
|
115
|
-
```yaml
|
|
116
|
-
- id: calendar
|
|
117
|
-
name: dsh-calendar
|
|
118
|
-
config:
|
|
119
|
-
provider: icloud
|
|
120
|
-
username: you@icloud.com
|
|
121
|
-
caldavUrl: https://caldav.icloud.com/123456789/calendars/<日历ID>/
|
|
122
|
-
# password 推荐用环境变量 DSH_CALENDAR_PASSWORD
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
iCloud needs the full calendar collection URL (with your user ID and calendar ID); you can find the specific calendar address in the calendar CalDAV settings on icloud.com.
|
|
126
|
-
|
|
127
|
-
### Nextcloud example
|
|
128
|
-
|
|
129
|
-
```yaml
|
|
130
|
-
- id: calendar
|
|
131
|
-
name: dsh-calendar
|
|
132
|
-
config:
|
|
133
|
-
provider: nextcloud
|
|
134
|
-
username: alice
|
|
135
|
-
host: https://cloud.example.com
|
|
136
|
-
user: alice
|
|
137
|
-
calendar: personal
|
|
138
|
-
# password 推荐用环境变量 DSH_CALENDAR_PASSWORD
|
|
21
|
+
dsh plugin --profile desktop add dsh-calendar
|
|
139
22
|
```
|
|
140
23
|
|
|
141
|
-
|
|
24
|
+
For the web version, replace `desktop` with `web`. Restart DSH after installation.
|
|
142
25
|
|
|
143
|
-
|
|
26
|
+
## Start using it
|
|
144
27
|
|
|
145
|
-
|
|
146
|
-
- id: calendar
|
|
147
|
-
name: dsh-calendar
|
|
148
|
-
config:
|
|
149
|
-
provider: custom
|
|
150
|
-
caldavUrl: https://dav.example.com/calendars/me/work/
|
|
151
|
-
username: me
|
|
152
|
-
# password 推荐用环境变量 DSH_CALENDAR_PASSWORD
|
|
153
|
-
```
|
|
28
|
+
Open Settings → Calendar, configure a provider, test the connection and save. Then ask: “Show this week’s events and find a one-hour gap.”
|
|
154
29
|
|
|
155
|
-
|
|
30
|
+
To migrate events, choose Import ICS. Previewing does not change your calendar. Select events and confirm the import; existing events are skipped and failed items can be retried separately.
|
|
156
31
|
|
|
157
|
-
|
|
32
|
+
## Requirements and configuration
|
|
158
33
|
|
|
159
|
-
|
|
34
|
+
Calendar credentials are required. Google uses OAuth2; other providers may use app passwords. Configure a proxy only when your network requires it.
|
|
160
35
|
|
|
161
|
-
|
|
36
|
+
Detailed configuration, tool arguments and troubleshooting are in the [usage guide](docs/USAGE.en.md). For standalone development, follow the Node requirement in [package.json](package.json).
|
|
162
37
|
|
|
163
|
-
##
|
|
38
|
+
## Documentation
|
|
164
39
|
|
|
165
|
-
|
|
40
|
+
- [Usage and troubleshooting](docs/USAGE.en.md)
|
|
41
|
+
- [Changelog](CHANGELOG.md)
|
|
42
|
+
- [Validation scope and history](docs/VALIDATION.md)
|
|
43
|
+
- [Report a problem or suggest a feature](https://github.com/STARDUSTLC666/dsh-calendar/issues)
|
|
166
44
|
|
|
167
|
-
##
|
|
168
|
-
|
|
169
|
-
- `calendar_health`: offline provider, endpoint and Basic/OAuth credential-completeness checks; never displays secrets or connects to the server.
|
|
170
|
-
- `calendar_list`: list events in a time range (start/end, ISO 8601; defaults to the next 7 days). Recurring events are expanded by default (`expand` defaults to true, `maxOccurrences` defaults to 30, clamped to 1-200): each occurrence is a separate row with `isOccurrence: true` and `seriesStart`; non-recurring events keep `isOccurrence: false`. With `expand=false`, recurring events are returned as a single original entry with `rrule`. Results are stably sorted by start time.
|
|
171
|
-
- `calendar_create`: create an event (summary/start/end required; description/location/allDay/rrule optional). Validates real calendar dates and `end >= start`.
|
|
172
|
-
- `calendar_update`: edit an event by uid (summary/start/end/description/location/allDay/rrule optional; omitted fields keep their original values, including the recurrence rule and original properties such as ATTENDEE / ORGANIZER / VALARM).
|
|
173
|
-
- `calendar_delete`: delete an event by uid
|
|
174
|
-
- `calendar_search`: search events by keyword within the `start`~`end` window (defaults to one year before/after now; client-side filter over title/description/location/UID, case-insensitive; `limit` defaults to 50, clamped to 1-200, and results are sorted by start time).
|
|
175
|
-
|
|
176
|
-
The stable event identifier `uid` is the CalDAV href (full object URL); `calendar_update` / `calendar_delete` use it.
|
|
177
|
-
|
|
178
|
-
## Calendar panel in Settings (0.6.0+)
|
|
179
|
-
|
|
180
|
-
Once installed, DSH grows a「Calendar」section in Settings; the chat page also gets a small 📅 button in the bottom-right that opens the same panel (non-modal, Esc closes).
|
|
181
|
-
|
|
182
|
-
- **Three views**: month (6×7 grid, today highlighted, click a cell to create), week (24-hour time grid starting at 07:00, overlapping events side by side) and agenda (grouped by day). The panel **remembers the view you used last**.
|
|
183
|
-
- **Drag to reschedule**: in the week view drag an event up/down to change the time, left/right to change the day (15-minute snapping), and drag its bottom edge to change the duration. A drop preview shows where it lands and turns yellow when it overlaps something (overlaps are allowed, it is only a warning). Keyboard works too: `↑/↓` 15 minutes, `Shift+↑/↓` an hour, `←/→` a day, `Esc` cancels mid-drag. Every move offers a 5-second undo and rolls back (then re-reads the server) if the request fails. **Recurring events and events longer than 24 hours cannot be dragged yet** — edit those in the details drawer.
|
|
184
|
-
- **Click to edit**: open any event in the right-hand drawer — time, duration, a human-readable repeat rule (`FREQ=WEEKLY;COUNT=6` → "weekly (6 times)"), location, notes, one-click uid copy — then edit or delete (deletion asks twice).
|
|
185
|
-
- **Create**: title, date, start/end, all-day, location, notes, repeat (daily/weekly/monthly/yearly, or a raw RRULE).
|
|
186
|
-
- **Conflict notice**: before saving, overlapping events are listed and you are asked whether to continue.
|
|
187
|
-
- **Configurable from the panel itself**: without a CalDAV account the panel shows guidance whose「Connection」button opens the form (test first, then save); a clearly labelled sample is one click away and never pretends to be your real calendar.
|
|
188
|
-
- **Theme and locale aware**: colours come from the host design tokens (`--dsw-*`), so light/dark themes follow automatically; copy follows Settings → General.
|
|
189
|
-
- **Security**: the panel only talks to the plugin’s own `/_dsh/dsh-calendar/settings`, which is loopback-only and re-checks Host, Origin and Content-Type on writes; credentials are never echoed back.
|
|
190
|
-
## Time and timezone
|
|
191
|
-
|
|
192
|
-
Input and output are uniformly ISO 8601. Timed events are output in UTC (e.g. `2025-01-15T01:00:00Z`); all-day events output `YYYY-MM-DD`. Input may carry a timezone offset (e.g. `2025-01-15T09:00:00+08:00`); the plugin converts to UTC internally for storage.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
## Changelog
|
|
196
|
-
|
|
197
|
-
- **0.5.4 (2026-09-18)**: fixes for `calendar_update` dropping ATTENDEE/VALARM and other original properties, ignored RECURRENCE-ID overrides, `calendar_create` inventing a uid instead of using the server Location, and silent empty expansion results; `calendar_search` gained a time range. 79 tests.
|
|
198
|
-
- **0.5.3 (2026-09-11)**: revalidated against official Harness 0.1.5-rc.1 and refreshed co-load / live-service verification; runtime code unchanged.
|
|
199
|
-
- **0.5.2 and earlier**: see [CHANGELOG.md](CHANGELOG.md).
|
|
200
|
-
|
|
201
|
-
## Known limitations
|
|
202
|
-
|
|
203
|
-
- Recurring event expansion: calendar_list expands RRULE by default via ICAL.RecurExpansion (`expand=true`), capped by `maxOccurrences`, and raises an explicit error when the iteration budget is exhausted; calendar_search only queries the `start`~`end` window (default one year before/after now) and still returns the original series (not expanded).
|
|
204
|
-
- Single-instance reads vs edit/delete: calendar_list honors RECURRENCE-ID overrides when expanding (an occurrence separately rescheduled/retitled is returned with the override time and fields, including when the original instant is EXDATE-excluded); calendar_update / calendar_delete still operate on the whole recurring series (by uid) and cannot modify or delete just one occurrence.
|
|
205
|
-
- OAuth credentials must be obtained beforehand: refresh-token authentication is supported, but there is no browser login UI / login CLI and runtime tokens are not written back to configuration.
|
|
206
|
-
- Timezone rules: events with TZID (named timezone) are output converted to UTC (Z); all-day boundaries, DST, and other complex timezone rules are not handled finely.
|
|
207
|
-
- Calendar discovery: iCloud requires manually filling the full calendar collection URL; no principal auto-discovery or multi-calendar selection.
|
|
208
|
-
- Cancellation/timeout: tools use `timeoutMs` (60 seconds) and forward the host AbortSignal into token refresh and DAV network requests. Concurrent calls cancel independently.
|
|
209
|
-
|
|
210
|
-
## Development
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
pnpm install
|
|
214
|
-
pnpm test # 构建 + node --test
|
|
215
|
-
```
|
|
45
|
+
## License
|
|
216
46
|
|
|
217
|
-
|
|
47
|
+
[MIT](LICENSE)
|