skill-schedule 0.2.0 → 0.3.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.
Files changed (116) hide show
  1. package/SKILL.md +27 -6
  2. package/dist/cli/cmd_read.js +96 -104
  3. package/dist/cli/cmd_read.js.map +1 -1
  4. package/dist/cli/config.d.ts +16 -0
  5. package/dist/cli/config.d.ts.map +1 -0
  6. package/dist/cli/config.js +84 -0
  7. package/dist/cli/config.js.map +1 -0
  8. package/dist/cli/health.d.ts +7 -0
  9. package/dist/cli/health.d.ts.map +1 -0
  10. package/dist/cli/health.js +32 -0
  11. package/dist/cli/health.js.map +1 -0
  12. package/dist/config.d.ts +43 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +64 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/fetch/db.d.ts +4 -0
  17. package/dist/fetch/db.d.ts.map +1 -1
  18. package/dist/fetch/db.js +10 -0
  19. package/dist/fetch/db.js.map +1 -1
  20. package/dist/fetch/errors.d.ts +1 -1
  21. package/dist/fetch/errors.d.ts.map +1 -1
  22. package/dist/fetch/errors.js.map +1 -1
  23. package/dist/fetch/feishu.d.ts +33 -9
  24. package/dist/fetch/feishu.d.ts.map +1 -1
  25. package/dist/fetch/feishu.js +137 -23
  26. package/dist/fetch/feishu.js.map +1 -1
  27. package/dist/fetch/index.d.ts +4 -4
  28. package/dist/fetch/index.d.ts.map +1 -1
  29. package/dist/fetch/index.js +3 -3
  30. package/dist/fetch/index.js.map +1 -1
  31. package/dist/fetch/paths.d.ts +5 -3
  32. package/dist/fetch/paths.d.ts.map +1 -1
  33. package/dist/fetch/paths.js +19 -24
  34. package/dist/fetch/paths.js.map +1 -1
  35. package/dist/health.d.ts +41 -0
  36. package/dist/health.d.ts.map +1 -0
  37. package/dist/health.js +580 -0
  38. package/dist/health.js.map +1 -0
  39. package/dist/help/helpFile.d.ts +6 -27
  40. package/dist/help/helpFile.d.ts.map +1 -1
  41. package/dist/help/helpFile.js +13 -50
  42. package/dist/help/helpFile.js.map +1 -1
  43. package/dist/help/helpPaths.d.ts +13 -12
  44. package/dist/help/helpPaths.d.ts.map +1 -1
  45. package/dist/help/helpPaths.js +23 -29
  46. package/dist/help/helpPaths.js.map +1 -1
  47. package/dist/help/lookup.d.ts.map +1 -1
  48. package/dist/help/lookup.js +8 -1
  49. package/dist/help/lookup.js.map +1 -1
  50. package/dist/help/output.d.ts +15 -14
  51. package/dist/help/output.d.ts.map +1 -1
  52. package/dist/help/output.js +40 -53
  53. package/dist/help/output.js.map +1 -1
  54. package/dist/help/scenes/help-assets.d.ts +10 -10
  55. package/dist/help/scenes/help-assets.d.ts.map +1 -1
  56. package/dist/help/scenes/help-assets.js +10 -10
  57. package/dist/help/scenes/help-assets.js.map +1 -1
  58. package/dist/plan/check.d.ts +3 -0
  59. package/dist/plan/check.d.ts.map +1 -0
  60. package/dist/plan/check.js +71 -0
  61. package/dist/plan/check.js.map +1 -0
  62. package/dist/plan/context.d.ts +18 -0
  63. package/dist/plan/context.d.ts.map +1 -0
  64. package/dist/plan/context.js +4 -0
  65. package/dist/plan/context.js.map +1 -0
  66. package/dist/plan/ensure.d.ts +4 -0
  67. package/dist/plan/ensure.d.ts.map +1 -0
  68. package/dist/plan/ensure.js +140 -0
  69. package/dist/plan/ensure.js.map +1 -0
  70. package/dist/plan/index.d.ts +8 -0
  71. package/dist/plan/index.d.ts.map +1 -0
  72. package/dist/plan/index.js +56 -0
  73. package/dist/plan/index.js.map +1 -0
  74. package/dist/plan/iso.d.ts +27 -0
  75. package/dist/plan/iso.d.ts.map +1 -0
  76. package/dist/plan/iso.js +55 -0
  77. package/dist/plan/iso.js.map +1 -0
  78. package/dist/plan/overview.d.ts +23 -0
  79. package/dist/plan/overview.d.ts.map +1 -0
  80. package/dist/plan/overview.js +38 -0
  81. package/dist/plan/overview.js.map +1 -0
  82. package/dist/plan/pull.d.ts +25 -0
  83. package/dist/plan/pull.d.ts.map +1 -0
  84. package/dist/plan/pull.js +83 -0
  85. package/dist/plan/pull.js.map +1 -0
  86. package/dist/plan/receipt.d.ts +52 -0
  87. package/dist/plan/receipt.d.ts.map +1 -0
  88. package/dist/plan/receipt.js +57 -0
  89. package/dist/plan/receipt.js.map +1 -0
  90. package/dist/plan/remoteDay.d.ts +62 -0
  91. package/dist/plan/remoteDay.d.ts.map +1 -0
  92. package/dist/plan/remoteDay.js +102 -0
  93. package/dist/plan/remoteDay.js.map +1 -0
  94. package/dist/plan/single.d.ts +4 -0
  95. package/dist/plan/single.d.ts.map +1 -0
  96. package/dist/plan/single.js +176 -0
  97. package/dist/plan/single.js.map +1 -0
  98. package/dist/plan/sync.d.ts +14 -0
  99. package/dist/plan/sync.d.ts.map +1 -0
  100. package/dist/plan/sync.js +158 -0
  101. package/dist/plan/sync.js.map +1 -0
  102. package/dist/plan/upsert.d.ts +5 -0
  103. package/dist/plan/upsert.d.ts.map +1 -0
  104. package/dist/plan/upsert.js +102 -0
  105. package/dist/plan/upsert.js.map +1 -0
  106. package/dist/policy/index.d.ts +2 -2
  107. package/dist/policy/index.d.ts.map +1 -1
  108. package/dist/policy/index.js +1 -1
  109. package/dist/policy/index.js.map +1 -1
  110. package/dist/policy/plan.d.ts +16 -1
  111. package/dist/policy/plan.d.ts.map +1 -1
  112. package/dist/policy/plan.js +51 -1
  113. package/dist/policy/plan.js.map +1 -1
  114. package/dist/policy/wakewords.js +4 -4
  115. package/dist/policy/wakewords.js.map +1 -1
  116. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ // 日程与计划·#18 改计划 与 #19 删计划(单条合成写)。
2
+ //
3
+ // 改计划:本地改一列,远端跟着改。**时段变了=远端删旧建新**(飞书日历没有「改时间」这个动作,
4
+ // 老实现同口径 `schedule_cli.py:1160-1200`),建新成功后才删旧;删旧失败走同槽兜底清理(S-12),
5
+ // 保证远端同槽里只剩一条(否则用户日历上会同时挂着旧+新)。内容变了(题/备注)→ 直接改远端那一条。
6
+ // 删计划:本地软删(`is_active=0`)+ 远端真删——`deleteFeishuEvent` 在老家**零调用**,
7
+ // 本实现把它接上(缺口在 #659 已点名)。
8
+ import { getPlanEvent, setFeishuEventId, updatePlanEvent, deactivatePlanEvent, larkUpdateEvent, composeFeishuDescription } from '../fetch/index.js';
9
+ import { SchedulePolicyError } from '../fetch/errors.js';
10
+ import { validateUpdateInput, toMinutes } from '../policy/index.js';
11
+ import { receiptResult } from './context.js';
12
+ import { buildReceipt, REMOTE_LABEL } from './receipt.js';
13
+ import { takeSnapshot, alignOne, cleanSlot, removeRemote } from './remoteDay.js';
14
+ export function runUpdate(ctx) {
15
+ const { id, patch } = validateUpdateInput(ctx.params);
16
+ const before = getPlanEvent(ctx.handle, id);
17
+ const nextStart = patch.time_start ?? before.time_start;
18
+ const nextEnd = patch.time_end ?? before.time_end;
19
+ if (toMinutes(nextEnd) <= toMinutes(nextStart)) {
20
+ throw new SchedulePolicyError('POLICY_BAD_TIME', 'time_end 必须晚于 time_start(' + nextStart + '~' + nextEnd + ')');
21
+ }
22
+ const after = updatePlanEvent(ctx.handle, id, patch);
23
+ const moved = nextStart !== before.time_start || nextEnd !== before.time_end;
24
+ const errors = [];
25
+ const notes = [];
26
+ const counts = { moved: moved ? 1 : 0, remoteDeleted: 0, slotCleaned: 0, remoteUpdated: 0 };
27
+ let remote;
28
+ let remoteId = after.feishu_event_id;
29
+ if (ctx.cli) {
30
+ let snap = null;
31
+ try {
32
+ snap = takeSnapshot(ctx.cli, after.date);
33
+ }
34
+ catch (e) {
35
+ errors.push(e instanceof Error ? e.message : String(e));
36
+ }
37
+ if (snap) {
38
+ errors.push(...snap.pullErrors);
39
+ if (moved) {
40
+ // 建新的(先建后删:建失败时旧的不动,用户日历不会出现空窗)
41
+ const made = alignOne(snap, {
42
+ date: after.date, timeStart: nextStart, timeEnd: nextEnd,
43
+ title: after.title, notes: after.notes, remoteId: null,
44
+ });
45
+ errors.push(...made.errors);
46
+ notes.push(...made.notes);
47
+ if (made.remote === 'unavailable' || !made.remoteId) {
48
+ remote = 'unavailable';
49
+ }
50
+ else {
51
+ const old = before.feishu_event_id;
52
+ if (old && old !== made.remoteId) {
53
+ const del = removeRemote(snap, old);
54
+ if (del.ok)
55
+ counts.remoteDeleted++;
56
+ else
57
+ errors.push('删旧远端事件失败:' + old + '(' + del.error + ')');
58
+ }
59
+ const slot = cleanSlot(snap, {
60
+ date: after.date, timeStart: nextStart, timeEnd: nextEnd,
61
+ title: after.title, notes: after.notes, keepId: made.remoteId,
62
+ });
63
+ errors.push(...slot.errors);
64
+ counts.slotCleaned += slot.deleted.length;
65
+ setFeishuEventId(ctx.handle, id, made.remoteId);
66
+ remoteId = made.remoteId;
67
+ remote = made.remote;
68
+ }
69
+ }
70
+ else {
71
+ const out = alignOne(snap, {
72
+ date: after.date, timeStart: after.time_start, timeEnd: after.time_end,
73
+ title: after.title, notes: after.notes, remoteId: after.feishu_event_id,
74
+ });
75
+ errors.push(...out.errors);
76
+ notes.push(...out.notes);
77
+ if (out.remote === 'unavailable' || !out.remoteId) {
78
+ remote = 'unavailable';
79
+ }
80
+ else {
81
+ const hit = snap.events.find((e) => e.eventId === out.remoteId);
82
+ const want = composeFeishuDescription(after.notes);
83
+ if (hit && (hit.summary !== after.title || hit.description !== want)) {
84
+ try {
85
+ larkUpdateEvent(ctx.cli, out.remoteId, { summary: after.title, description: want });
86
+ counts.remoteUpdated++;
87
+ remote = 'updated_feishu';
88
+ }
89
+ catch (e) {
90
+ errors.push('远端改失败:' + (e instanceof Error ? e.message : String(e)));
91
+ remote = 'unavailable';
92
+ }
93
+ }
94
+ else {
95
+ remote = out.remote;
96
+ }
97
+ if (out.remoteId !== after.feishu_event_id)
98
+ setFeishuEventId(ctx.handle, id, out.remoteId);
99
+ remoteId = out.remoteId;
100
+ }
101
+ }
102
+ }
103
+ else {
104
+ remote = 'unavailable';
105
+ }
106
+ }
107
+ else if (ctx.remoteWhy) {
108
+ errors.push(ctx.remoteWhy);
109
+ remote = 'unavailable';
110
+ }
111
+ else {
112
+ remote = 'skipped';
113
+ }
114
+ const changes = Object.keys(patch).join('/');
115
+ const message = '已改计划 #' + id + '(' + changes + ')' + (moved ? ',时段 → ' + nextStart + '~' + nextEnd : '')
116
+ + '(' + REMOTE_LABEL[remote] + ')';
117
+ return receiptResult(buildReceipt({
118
+ op: 'update', message, local: 'updated', remote, remoteId, errors, notes, id, date: after.date, counts,
119
+ }));
120
+ }
121
+ export function runDeactivate(ctx) {
122
+ const id = ctx.params.id;
123
+ const before = getPlanEvent(ctx.handle, id);
124
+ deactivatePlanEvent(ctx.handle, id);
125
+ const errors = [];
126
+ const notes = [];
127
+ const counts = { remoteDeleted: 0, slotCleaned: 0 };
128
+ let remote;
129
+ let remoteId = before.feishu_event_id;
130
+ if (ctx.cli) {
131
+ let snap = null;
132
+ try {
133
+ snap = takeSnapshot(ctx.cli, before.date);
134
+ }
135
+ catch (e) {
136
+ errors.push(e instanceof Error ? e.message : String(e));
137
+ }
138
+ if (snap) {
139
+ errors.push(...snap.pullErrors);
140
+ if (before.feishu_event_id) {
141
+ const r = removeRemote(snap, before.feishu_event_id);
142
+ if (r.ok)
143
+ counts.remoteDeleted++;
144
+ else
145
+ errors.push('远端删失败:' + before.feishu_event_id + '(' + r.error + ')');
146
+ }
147
+ // 同槽兜底:软删之后这个槽位不该再留任何「我管的」远端对象(老实现只在改时段时兜底,
148
+ // 删除路径同样会留残——同一口径,一并兜住)。
149
+ const slot = cleanSlot(snap, {
150
+ date: before.date, timeStart: before.time_start, timeEnd: before.time_end,
151
+ title: before.title, notes: before.notes, keepId: '',
152
+ });
153
+ counts.slotCleaned += slot.deleted.length;
154
+ errors.push(...slot.errors);
155
+ remoteId = null;
156
+ remote = errors.length ? 'unavailable'
157
+ : counts.remoteDeleted + counts.slotCleaned > 0 ? 'deleted_feishu' : 'found_feishu';
158
+ }
159
+ else {
160
+ remote = 'unavailable';
161
+ }
162
+ }
163
+ else if (ctx.remoteWhy) {
164
+ errors.push(ctx.remoteWhy);
165
+ remote = 'unavailable';
166
+ }
167
+ else {
168
+ remote = 'skipped';
169
+ }
170
+ const message = '已删计划(软删 #' + id + ')' + (remote === 'deleted_feishu' ? ',远端那条也删了' : '')
171
+ + '(' + REMOTE_LABEL[remote] + ')';
172
+ return receiptResult(buildReceipt({
173
+ op: 'deactivate', message, local: 'deleted', remote, remoteId, errors, notes, id, date: before.date, counts,
174
+ }));
175
+ }
176
+ //# sourceMappingURL=single.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single.js","sourceRoot":"","sources":["../../src/plan/single.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { PlanEvent } from '../fetch/index.js';
2
+ import { type PlanOpCtx, type PlanOpResult } from './context.js';
3
+ import { type RemoteSnapshot } from './remoteDay.js';
4
+ export interface ReconcileOutcome {
5
+ backfilled: {
6
+ id: number;
7
+ remoteId: string;
8
+ }[];
9
+ skipped: string[];
10
+ }
11
+ /** 阶段 0:远端 → 本地回填标识。唯一命中才回填;多候选记「需手工处理」并跳过。 */
12
+ export declare function reconcileIds(snap: RemoteSnapshot, locals: PlanEvent[]): ReconcileOutcome;
13
+ export declare function runSync(ctx: PlanOpCtx): PlanOpResult;
14
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/plan/sync.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAuE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE1H,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAmBxF;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CA4GpD"}
@@ -0,0 +1,158 @@
1
+ // 日程与计划·#20 日程管家同步(op=sync):反向对账(阶段 0)+ 全量 diff + 两侧兜底清理。
2
+ //
3
+ // 阶段 0 · 反向对账(S-11):远端有、本地缺标识的,按 `(起, 止, 标题)` 索引回填;**多条候选不猜**(跳过并记账)。
4
+ // 阶段 A · 按本地存的标识配对:远端那条若被手动改了时段,尊重用户,把时段同步回本地(老口径);
5
+ // 题/备注与本地不一致则以本地为准改远端。
6
+ // 阶段 B · 本地没标识的按四元组认;认不出才建。**过去日期默认不补建**(避免死灰复燃,老口径 `feishu_sync.py:756-759`)。
7
+ // 阶段 C · 远端有、本地没有、且**带归属锚**的 → 孤儿,删;不带锚的一律不动(S-12)。
8
+ // 收口 · 同一时间槽里带锚的冗余对象(历史重复建的残留)有兜底清理(S-12 同槽一侧)。
9
+ //
10
+ // 远端不可用即阻断(S-09/老家唯一「不可用就阻断」的入口):回执标 `unavailable`,退出码非 0。
11
+ import { listPlanEvents, setFeishuEventId, updatePlanEvent, larkUpdateEvent, composeFeishuDescription } from '../fetch/index.js';
12
+ import { resolveDateParam, todayStr } from '../policy/index.js';
13
+ import { receiptResult } from './context.js';
14
+ import { buildReceipt } from './receipt.js';
15
+ import { takeSnapshot, alignOne, matchIn, orphansOf, removeRemote, cleanSlot } from './remoteDay.js';
16
+ /** 阶段 0:远端 → 本地回填标识。唯一命中才回填;多候选记「需手工处理」并跳过。 */
17
+ export function reconcileIds(snap, locals) {
18
+ const index = new Map();
19
+ for (const e of snap.events) {
20
+ if (!e.owned)
21
+ continue;
22
+ const k = e.slotStart + '|' + e.slotEnd + '|' + e.summary;
23
+ index.set(k, [...(index.get(k) ?? []), e.eventId]);
24
+ }
25
+ const out = { backfilled: [], skipped: [] };
26
+ for (const e of locals) {
27
+ if (e.feishu_event_id) {
28
+ // 已绑标识的也认领一下:免得阶段 C 把它们当成孤儿删掉。
29
+ if (snap.events.some((r) => r.eventId === e.feishu_event_id))
30
+ snap.claimed.add(e.feishu_event_id);
31
+ continue;
32
+ }
33
+ const hit = index.get(e.time_start + '|' + e.time_end + '|' + e.title) ?? [];
34
+ if (hit.length === 1)
35
+ out.backfilled.push({ id: e.id, remoteId: hit[0] });
36
+ else if (hit.length > 1)
37
+ out.skipped.push('本地 #' + e.id + '「' + e.title + '」远端有 ' + hit.length + ' 条撞键候选,跳过(需手工处理)');
38
+ }
39
+ return out;
40
+ }
41
+ export function runSync(ctx) {
42
+ const date = resolveDateParam(ctx.params);
43
+ if (!ctx.cli) {
44
+ const why = ctx.remoteWhy ?? '远端不可用';
45
+ return receiptResult(buildReceipt({
46
+ op: 'sync',
47
+ message: '重同步阻断:远端不可用(' + why + ')',
48
+ local: 'unchanged', remote: 'unavailable', remoteId: null,
49
+ errors: [why], date,
50
+ }));
51
+ }
52
+ const errors = [];
53
+ const notes = [];
54
+ let snap;
55
+ try {
56
+ snap = takeSnapshot(ctx.cli, date);
57
+ }
58
+ catch (e) {
59
+ const m = e instanceof Error ? e.message : String(e);
60
+ return receiptResult(buildReceipt({
61
+ op: 'sync', message: '重同步阻断:' + m, local: 'unchanged', remote: 'unavailable',
62
+ remoteId: null, errors: [m], date,
63
+ }));
64
+ }
65
+ errors.push(...snap.pullErrors);
66
+ // 阶段 0:反向对账
67
+ const rec = reconcileIds(snap, listPlanEvents(ctx.handle, date));
68
+ for (const m of rec.backfilled)
69
+ setFeishuEventId(ctx.handle, m.id, m.remoteId);
70
+ notes.push(...rec.skipped);
71
+ const isPast = date < todayStr();
72
+ const counts = { backfilled: rec.backfilled.length, created: 0, updated: 0, deleted: 0, timeSynced: 0, pastSkipped: 0, duplicates: 0, failed: 0 };
73
+ // 阶段 A/B:本地 → 远端
74
+ for (const e of listPlanEvents(ctx.handle, date)) {
75
+ const spec = {
76
+ date, timeStart: e.time_start, timeEnd: e.time_end,
77
+ title: e.title, notes: e.notes, remoteId: e.feishu_event_id,
78
+ };
79
+ const hit = matchIn(snap, spec);
80
+ if (!hit) {
81
+ if (isPast) {
82
+ counts.pastSkipped++;
83
+ continue;
84
+ } // 过去日期:本地有、远端无 → 默认不补建
85
+ const made = alignOne(snap, spec);
86
+ errors.push(...made.errors);
87
+ notes.push(...made.notes);
88
+ if (made.remote === 'unavailable' || !made.remoteId) {
89
+ counts.failed++;
90
+ continue;
91
+ }
92
+ setFeishuEventId(ctx.handle, e.id, made.remoteId);
93
+ counts.created++;
94
+ continue;
95
+ }
96
+ snap.claimed.add(hit.eventId);
97
+ if (hit.eventId !== e.feishu_event_id)
98
+ setFeishuEventId(ctx.handle, e.id, hit.eventId);
99
+ // 飞书侧被手动改了时段 → 尊重用户,把时段同步回本地
100
+ if (hit.slotStart !== e.time_start || hit.slotEnd !== e.time_end) {
101
+ updatePlanEvent(ctx.handle, e.id, { time_start: hit.slotStart, time_end: hit.slotEnd });
102
+ counts.timeSynced++;
103
+ }
104
+ const want = composeFeishuDescription(e.notes);
105
+ if (hit.summary !== e.title || hit.description !== want) {
106
+ try {
107
+ larkUpdateEvent(ctx.cli, hit.eventId, { summary: e.title, description: want });
108
+ counts.updated++;
109
+ }
110
+ catch (err) {
111
+ counts.failed++;
112
+ errors.push('远端改失败:' + hit.eventId + '(' + (err instanceof Error ? err.message : String(err)) + ')');
113
+ }
114
+ }
115
+ }
116
+ // 阶段 C:孤儿(带锚、无人认领)→ 删
117
+ for (const orphan of orphansOf(snap)) {
118
+ const r = removeRemote(snap, orphan.eventId);
119
+ if (r.ok)
120
+ counts.deleted++;
121
+ else {
122
+ counts.failed++;
123
+ errors.push('孤儿清理失败:' + orphan.eventId + '(' + r.error + ')');
124
+ }
125
+ }
126
+ // 收口:同槽冗余(带锚)兜底清理——留一条(优先认领过的那条),其余删。
127
+ const owned = snap.events.filter((e) => e.owned);
128
+ const bySlot = new Map();
129
+ for (const e of owned) {
130
+ const k = e.slotStart + '|' + e.slotEnd;
131
+ bySlot.set(k, [...(bySlot.get(k) ?? []), e]);
132
+ }
133
+ for (const [, group] of bySlot) {
134
+ if (group.length < 2)
135
+ continue;
136
+ counts.duplicates += group.length - 1;
137
+ const keeper = group.find((g) => snap.claimed.has(g.eventId)) ?? group[0];
138
+ const slot = cleanSlot(snap, {
139
+ date, timeStart: keeper.slotStart, timeEnd: keeper.slotEnd,
140
+ title: keeper.summary, notes: null, keepId: keeper.eventId,
141
+ });
142
+ counts.deleted += slot.deleted.length;
143
+ errors.push(...slot.errors);
144
+ }
145
+ const remote = counts.failed > 0 ? 'unavailable'
146
+ : counts.created > 0 ? 'created_feishu'
147
+ : counts.updated > 0 ? 'updated_feishu'
148
+ : counts.deleted > 0 ? 'deleted_feishu' : 'found_feishu';
149
+ const message = '已同步 ' + date + ':回填 ' + counts.backfilled + '/新增 ' + counts.created
150
+ + '/更新 ' + counts.updated + '/清理 ' + counts.deleted + '/时段回写 ' + counts.timeSynced
151
+ + (counts.pastSkipped ? '/过去日期跳过 ' + counts.pastSkipped : '')
152
+ + (counts.duplicates ? '/同槽冗余 ' + counts.duplicates : '')
153
+ + (errors.length ? '/错误 ' + errors.length : '');
154
+ return receiptResult(buildReceipt({
155
+ op: 'sync', message, local: 'updated', remote, remoteId: null, errors, notes, date, counts,
156
+ }));
157
+ }
158
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/plan/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type PlanOpCtx, type PlanOpResult } from './context.js';
2
+ /** `op=preview`:只校验不落盘(24h 覆盖 + 字段合法),不碰远端。 */
3
+ export declare function runPreview(ctx: PlanOpCtx): PlanOpResult;
4
+ export declare function runUpsert(ctx: PlanOpCtx): PlanOpResult;
5
+ //# sourceMappingURL=upsert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../src/plan/upsert.ts"],"names":[],"mappings":"AASA,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAIhF,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAUvD;AAYD,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAyDtD"}
@@ -0,0 +1,102 @@
1
+ // 日程与计划·#17 商量计划(整日覆盖式写入)与它的预览档。
2
+ //
3
+ // 两种语义分得开(S-13):
4
+ // `op=upsert`=**覆盖**:整日先软删再整批落,本地以 (日期, 起, 止) 为身份;
5
+ // `op=ensure`=**缺则补**:命中即认,不改既有字段。
6
+ // 覆盖的那一侧还要把远端也覆盖掉:逐条对齐(建/认)+ 该日**带归属锚**却已无本地对应的远端对象删掉
7
+ // (S-12 的孤儿一侧;老实现把这一步留在 `diff_and_sync`,本实现随合成写一起做掉)。
8
+ import { upsertPlanEvents, setFeishuEventId } from '../fetch/index.js';
9
+ import { validateUpsertInput } from '../policy/index.js';
10
+ import { receiptResult } from './context.js';
11
+ import { buildReceipt, REMOTE_LABEL } from './receipt.js';
12
+ import { takeSnapshot, alignOne, orphansOf, removeRemote } from './remoteDay.js';
13
+ /** `op=preview`:只校验不落盘(24h 覆盖 + 字段合法),不碰远端。 */
14
+ export function runPreview(ctx) {
15
+ const { date, events } = validateUpsertInput(ctx.params);
16
+ return receiptResult(buildReceipt({
17
+ op: 'preview',
18
+ message: '预览通过:' + date + ' ' + events.length + ' 个事件 00:00~24:00 连续(确认后 op=upsert 落盘)',
19
+ local: 'preview',
20
+ remote: 'none',
21
+ remoteId: null,
22
+ date,
23
+ }));
24
+ }
25
+ /** 一整日里逐条动作合起来算哪一档:只要有一条没成,就是「远端没成」。 */
26
+ function dominantRemote(counts) {
27
+ if (counts.failed > 0)
28
+ return 'unavailable';
29
+ if (counts.created > 0)
30
+ return 'created_feishu';
31
+ if (counts.deleted > 0)
32
+ return 'deleted_feishu';
33
+ return 'found_feishu';
34
+ }
35
+ export function runUpsert(ctx) {
36
+ const { date, events } = validateUpsertInput(ctx.params);
37
+ const rows = upsertPlanEvents(ctx.handle, date, events);
38
+ const errors = [];
39
+ const notes = [];
40
+ const counts = { created: 0, found: 0, deleted: 0, failed: 0 };
41
+ let remote;
42
+ if (ctx.cli) {
43
+ let snap = null;
44
+ try {
45
+ snap = takeSnapshot(ctx.cli, date);
46
+ }
47
+ catch (e) {
48
+ errors.push(e instanceof Error ? e.message : String(e));
49
+ }
50
+ if (snap) {
51
+ errors.push(...snap.pullErrors);
52
+ for (const row of rows) {
53
+ const out = alignOne(snap, {
54
+ date,
55
+ timeStart: row.time_start,
56
+ timeEnd: row.time_end,
57
+ title: row.title,
58
+ notes: row.notes,
59
+ remoteId: row.feishu_event_id,
60
+ });
61
+ errors.push(...out.errors);
62
+ notes.push(...out.notes);
63
+ if (out.remote === 'created_feishu')
64
+ counts.created++;
65
+ else if (out.remote === 'found_feishu')
66
+ counts.found++;
67
+ else
68
+ counts.failed++;
69
+ if (out.remoteId && out.remoteId !== row.feishu_event_id)
70
+ setFeishuEventId(ctx.handle, row.id, out.remoteId);
71
+ }
72
+ // 覆盖语义的第二半:本地这一日已没有的、带归属锚的远端对象,删掉。
73
+ for (const orphan of orphansOf(snap)) {
74
+ const r = removeRemote(snap, orphan.eventId);
75
+ if (r.ok)
76
+ counts.deleted++;
77
+ else {
78
+ counts.failed++;
79
+ errors.push('孤儿清理失败:' + orphan.eventId + '(' + r.error + ')');
80
+ }
81
+ }
82
+ remote = dominantRemote(counts);
83
+ }
84
+ else {
85
+ remote = 'unavailable';
86
+ }
87
+ }
88
+ else if (ctx.remoteWhy) {
89
+ errors.push(ctx.remoteWhy);
90
+ remote = 'unavailable';
91
+ }
92
+ else {
93
+ remote = 'skipped';
94
+ }
95
+ const message = '已落盘(覆盖):' + date + ' ' + rows.length + ' 个事件;远端新增 ' + counts.created
96
+ + '/认出 ' + counts.found + '/清理 ' + counts.deleted + '(' + REMOTE_LABEL[remote] + ')';
97
+ return receiptResult(buildReceipt({
98
+ op: 'upsert', message, local: 'upserted', remote,
99
+ remoteId: null, errors, notes, date, counts: { ...counts, events: rows.length },
100
+ }));
101
+ }
102
+ //# sourceMappingURL=upsert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upsert.js","sourceRoot":"","sources":["../../src/plan/upsert.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,8 @@ export type { Anomaly } from './category.js';
3
3
  export { todayStr, relativeToDate, relativeToRange, recentNDays, RELATIVE_DATES, RELATIVE_RANGES, } from './routing.js';
4
4
  export { DATE_RE, TIME_RE, MONTH_RE, normalizeDate, normalizeMonth, normalizeTime, toMinutes, resolveDateParam, resolveRangeParam, validateAddInput, validateAmendInput, validateSummaryInput, validateCompareInput, } from './record.js';
5
5
  export type { AddInput, CompareKind } from './record.js';
6
- export { VALID_COMPLETIONS, validateEvent, assertCoverage24h, validateUpsertInput, validateEnsureInput, validateUpdateInput, parsePlanOp, parseRecordOp, } from './plan.js';
7
- export type { PlanEventInput, PlanWriteOp, RecordWriteOp } from './plan.js';
6
+ export { VALID_COMPLETIONS, validateEvent, assertCoverage24h, validateUpsertInput, validateEnsureInput, validateEnsureBatchInput, validateUpdateInput, parsePlanOp, parseRecordOp, parseFeishuMode, parsePlanView, PLAN_WRITE_OPS_OVERWRITE, PLAN_WRITE_OPS_FILL, } from './plan.js';
7
+ export type { PlanEventInput, EnsureBatchItem, PlanWriteOp, RecordWriteOp, PlanFeishuMode, PlanView } from './plan.js';
8
8
  export { routeWakeword, WAKE_TABLE } from './wakewords.js';
9
9
  export type { ScheduleKey, WakeRoute, WakeEntry } from './wakewords.js';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACrE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EACzE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,GACpE,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,GACxF,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EACnF,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EACzE,oBAAoB,EAAE,oBAAoB,GAC3C,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACnD,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAC7D,WAAW,EAAE,aAAa,GAC3B,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACrE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EACzE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,GACpE,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,GACxF,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EACnF,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EACzE,oBAAoB,EAAE,oBAAoB,GAC3C,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACnD,mBAAmB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,mBAAmB,EACvF,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAC1D,wBAAwB,EAAE,mBAAmB,GAC9C,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACvH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  export { LEVEL1_WHITELIST, DEFAULT_WHITELIST, parseCategory, normalizeCategory, listLevel1, listLevel2, getEmojiPrefix, l1Of, fmtDur, fmtDurShort, fmtPct, HEALTH_TARGETS, healthDimScore, computeHealthScore, detectAnomalies, } from './category.js';
2
2
  export { todayStr, relativeToDate, relativeToRange, recentNDays, RELATIVE_DATES, RELATIVE_RANGES, } from './routing.js';
3
3
  export { DATE_RE, TIME_RE, MONTH_RE, normalizeDate, normalizeMonth, normalizeTime, toMinutes, resolveDateParam, resolveRangeParam, validateAddInput, validateAmendInput, validateSummaryInput, validateCompareInput, } from './record.js';
4
- export { VALID_COMPLETIONS, validateEvent, assertCoverage24h, validateUpsertInput, validateEnsureInput, validateUpdateInput, parsePlanOp, parseRecordOp, } from './plan.js';
4
+ export { VALID_COMPLETIONS, validateEvent, assertCoverage24h, validateUpsertInput, validateEnsureInput, validateEnsureBatchInput, validateUpdateInput, parsePlanOp, parseRecordOp, parseFeishuMode, parsePlanView, PLAN_WRITE_OPS_OVERWRITE, PLAN_WRITE_OPS_FILL, } from './plan.js';
5
5
  export { routeWakeword, WAKE_TABLE } from './wakewords.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACrE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EACzE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,GACpE,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,GACxF,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EACnF,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EACzE,oBAAoB,EAAE,oBAAoB,GAC3C,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACnD,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAC7D,WAAW,EAAE,aAAa,GAC3B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACrE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EACzE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,GACpE,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,GACxF,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EACnF,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EACzE,oBAAoB,EAAE,oBAAoB,GAC3C,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EACnD,mBAAmB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,mBAAmB,EACvF,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAC1D,wBAAwB,EAAE,mBAAmB,GAC9C,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
@@ -15,12 +15,27 @@ export declare function validateUpsertInput(params: Record<string, unknown>): {
15
15
  export declare function validateEnsureInput(params: Record<string, unknown>): PlanEventInput & {
16
16
  date: string;
17
17
  };
18
+ export interface EnsureBatchItem extends PlanEventInput {
19
+ date: string;
20
+ }
21
+ export declare function validateEnsureBatchInput(params: Record<string, unknown>): {
22
+ items: EnsureBatchItem[];
23
+ };
18
24
  export declare function validateUpdateInput(params: Record<string, unknown>): {
19
25
  id: number;
20
26
  patch: Record<string, unknown>;
21
27
  };
22
- export type PlanWriteOp = 'preview' | 'upsert' | 'ensure' | 'update' | 'deactivate' | 'review' | 'sync';
28
+ export type PlanWriteOp = 'preview' | 'upsert' | 'ensure' | 'update' | 'deactivate' | 'review' | 'sync' | 'check';
23
29
  export declare function parsePlanOp(params: Record<string, unknown>): PlanWriteOp;
30
+ /** 写命令的两档语义(S-13):`upsert`=整段覆盖,`ensure`=缺则补。 */
31
+ export declare const PLAN_WRITE_OPS_OVERWRITE: PlanWriteOp[];
32
+ export declare const PLAN_WRITE_OPS_FILL: PlanWriteOp[];
33
+ /** 远端开关(合成写的参数面):`ensure`(缺省)=把这条事实对齐到两侧;`skip`=这一趟只要本地。 */
34
+ export type PlanFeishuMode = 'ensure' | 'skip';
35
+ export declare function parseFeishuMode(params: Record<string, unknown>): PlanFeishuMode;
36
+ /** 日程查询的两种视图:`list`(缺省,全字段)与 `aggregate`(24h 聚合视图,丢 notes/同步态/ID)。 */
37
+ export type PlanView = 'list' | 'aggregate';
38
+ export declare function parsePlanView(params: Record<string, unknown>): PlanView;
24
39
  export type RecordWriteOp = 'add' | 'amend' | 'summary';
25
40
  export declare function parseRecordOp(params: Record<string, unknown>): RecordWriteOp;
26
41
  //# sourceMappingURL=plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/policy/plan.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,iBAAiB,UAE7B,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAuBrE;AAGD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAoBhE;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAQ/G;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAGtG;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAsCnH;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAOxE;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;AAExD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAM5E"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/policy/plan.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,iBAAiB,UAE7B,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAuBrE;AAGD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAoBhE;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAQ/G;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAGtG;AAKD,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,CA0BtG;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAsCnH;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAElH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAOxE;AAED,iDAAiD;AACjD,eAAO,MAAM,wBAAwB,EAAE,WAAW,EAAe,CAAC;AAClE,eAAO,MAAM,mBAAmB,EAAE,WAAW,EAAe,CAAC;AAE7D,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE/C,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAO/E;AAED,sEAAsE;AACtE,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE5C,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAOvE;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;AAExD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAM5E"}