skill-schedule 0.1.0 → 0.2.1
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/SKILL.md +105 -0
- package/dist/cli/cmd_read.js +174 -116
- package/dist/cli/cmd_read.js.map +1 -1
- package/dist/cli/config.d.ts +16 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +84 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/health.d.ts +7 -0
- package/dist/cli/health.d.ts.map +1 -0
- package/dist/cli/health.js +32 -0
- package/dist/cli/health.js.map +1 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/fetch/db.d.ts +4 -0
- package/dist/fetch/db.d.ts.map +1 -1
- package/dist/fetch/db.js +10 -0
- package/dist/fetch/db.js.map +1 -1
- package/dist/fetch/errors.d.ts +1 -1
- package/dist/fetch/errors.d.ts.map +1 -1
- package/dist/fetch/errors.js.map +1 -1
- package/dist/fetch/feishu.d.ts +33 -9
- package/dist/fetch/feishu.d.ts.map +1 -1
- package/dist/fetch/feishu.js +137 -23
- package/dist/fetch/feishu.js.map +1 -1
- package/dist/fetch/index.d.ts +4 -4
- package/dist/fetch/index.d.ts.map +1 -1
- package/dist/fetch/index.js +3 -3
- package/dist/fetch/index.js.map +1 -1
- package/dist/fetch/paths.d.ts +5 -3
- package/dist/fetch/paths.d.ts.map +1 -1
- package/dist/fetch/paths.js +19 -24
- package/dist/fetch/paths.js.map +1 -1
- package/dist/health.d.ts +41 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +580 -0
- package/dist/health.js.map +1 -0
- package/dist/help/helpFile.d.ts +69 -0
- package/dist/help/helpFile.d.ts.map +1 -0
- package/dist/help/helpFile.js +136 -0
- package/dist/help/helpFile.js.map +1 -0
- package/dist/help/helpPaths.d.ts +14 -0
- package/dist/help/helpPaths.d.ts.map +1 -0
- package/dist/help/helpPaths.js +40 -0
- package/dist/help/helpPaths.js.map +1 -0
- package/dist/help/lookup.d.ts.map +1 -1
- package/dist/help/lookup.js +8 -1
- package/dist/help/lookup.js.map +1 -1
- package/dist/help/output.d.ts +22 -0
- package/dist/help/output.d.ts.map +1 -0
- package/dist/help/output.js +79 -0
- package/dist/help/output.js.map +1 -0
- package/dist/help/scenes/help-assets.d.ts +97 -0
- package/dist/help/scenes/help-assets.d.ts.map +1 -0
- package/dist/help/scenes/help-assets.js +2148 -0
- package/dist/help/scenes/help-assets.js.map +1 -0
- package/dist/plan/check.d.ts +3 -0
- package/dist/plan/check.d.ts.map +1 -0
- package/dist/plan/check.js +71 -0
- package/dist/plan/check.js.map +1 -0
- package/dist/plan/context.d.ts +18 -0
- package/dist/plan/context.d.ts.map +1 -0
- package/dist/plan/context.js +4 -0
- package/dist/plan/context.js.map +1 -0
- package/dist/plan/ensure.d.ts +4 -0
- package/dist/plan/ensure.d.ts.map +1 -0
- package/dist/plan/ensure.js +140 -0
- package/dist/plan/ensure.js.map +1 -0
- package/dist/plan/index.d.ts +8 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan/index.js +56 -0
- package/dist/plan/index.js.map +1 -0
- package/dist/plan/iso.d.ts +27 -0
- package/dist/plan/iso.d.ts.map +1 -0
- package/dist/plan/iso.js +55 -0
- package/dist/plan/iso.js.map +1 -0
- package/dist/plan/overview.d.ts +23 -0
- package/dist/plan/overview.d.ts.map +1 -0
- package/dist/plan/overview.js +38 -0
- package/dist/plan/overview.js.map +1 -0
- package/dist/plan/pull.d.ts +25 -0
- package/dist/plan/pull.d.ts.map +1 -0
- package/dist/plan/pull.js +83 -0
- package/dist/plan/pull.js.map +1 -0
- package/dist/plan/receipt.d.ts +52 -0
- package/dist/plan/receipt.d.ts.map +1 -0
- package/dist/plan/receipt.js +57 -0
- package/dist/plan/receipt.js.map +1 -0
- package/dist/plan/remoteDay.d.ts +62 -0
- package/dist/plan/remoteDay.d.ts.map +1 -0
- package/dist/plan/remoteDay.js +102 -0
- package/dist/plan/remoteDay.js.map +1 -0
- package/dist/plan/single.d.ts +4 -0
- package/dist/plan/single.d.ts.map +1 -0
- package/dist/plan/single.js +176 -0
- package/dist/plan/single.js.map +1 -0
- package/dist/plan/sync.d.ts +14 -0
- package/dist/plan/sync.d.ts.map +1 -0
- package/dist/plan/sync.js +158 -0
- package/dist/plan/sync.js.map +1 -0
- package/dist/plan/upsert.d.ts +5 -0
- package/dist/plan/upsert.d.ts.map +1 -0
- package/dist/plan/upsert.js +102 -0
- package/dist/plan/upsert.js.map +1 -0
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +1 -1
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/plan.d.ts +16 -1
- package/dist/policy/plan.d.ts.map +1 -1
- package/dist/policy/plan.js +51 -1
- package/dist/policy/plan.js.map +1 -1
- package/dist/policy/wakewords.js +4 -4
- package/dist/policy/wakewords.js.map +1 -1
- package/dist/render/html.d.ts +0 -6
- package/dist/render/html.d.ts.map +1 -1
- package/dist/render/html.js +0 -17
- package/dist/render/html.js.map +1 -1
- package/dist/render/index.d.ts +2 -1
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +3 -1
- package/dist/render/index.js.map +1 -1
- package/dist/shared/templateFill.d.ts +4 -0
- package/dist/shared/templateFill.d.ts.map +1 -0
- package/dist/shared/templateFill.js +20 -0
- package/dist/shared/templateFill.js.map +1 -0
- package/package.json +7 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type LarkEvent } from '../fetch/feishu.js';
|
|
2
|
+
export interface RemoteEvent extends LarkEvent {
|
|
3
|
+
/** 本地口径的时间槽(跨日 00:00 已换成本地 23:59)。 */
|
|
4
|
+
slotStart: string;
|
|
5
|
+
slotEnd: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PullResult {
|
|
8
|
+
events: RemoteEvent[];
|
|
9
|
+
errors: string[];
|
|
10
|
+
/** 读数原料:两路各发了几次请求(分片合规、阶段 3 补了几条,都由它读)。 */
|
|
11
|
+
calls: {
|
|
12
|
+
agenda: number;
|
|
13
|
+
search: number;
|
|
14
|
+
detail: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function pullFeishuDate(cli: string, date: string): PullResult;
|
|
18
|
+
/** 远端自然键:四元组(日期 + 起 + 止 + 标题)——本地键是三元组,两侧口径不同是既有事实(S-01/S-03)。 */
|
|
19
|
+
export declare function remoteKeyOf(date: string, e: {
|
|
20
|
+
slotStart: string;
|
|
21
|
+
slotEnd: string;
|
|
22
|
+
summary: string;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function localKeyOf(date: string, timeStart: string, timeEnd: string): string;
|
|
25
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/plan/pull.ts"],"names":[],"mappings":"AAcA,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGhG,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2CAA2C;IAC3C,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3D;AASD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAmDpE;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE5G;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// 日程与计划·三阶段合并拉取(能力内部件):把某一天的远端事件读全,且读得准。
|
|
2
|
+
//
|
|
3
|
+
// 老实现的教训(`feishu_sync.py:398-565`):单用 `+agenda` 有索引延迟(刚建的可能读不到),
|
|
4
|
+
// 单用 `+search-event` 既没有 description、一天又只回前 20 条。于是三阶段合并:
|
|
5
|
+
// 阶段 1 甲路:`+agenda` 整日(带 description)
|
|
6
|
+
// 阶段 1 乙路:`+search-event` 按 6 小时**分片**(索引最新,规避单次返回上限)
|
|
7
|
+
// 阶段 2 :按 event_id 合并去重(甲路优先——它带 description)
|
|
8
|
+
// 阶段 3 :description 仍为空者,逐条 `events get` 补齐(归属锚靠它才判得出)
|
|
9
|
+
//
|
|
10
|
+
// 有意偏离 D-13(老实现缺陷):老家分片循环在异常分支里 `continue` 跳过了 `cur = cur_end`,
|
|
11
|
+
// 远端一抖就原地转圈、`ThreadPoolExecutor` 退出时 `wait` 等它 → 整条命令挂死
|
|
12
|
+
// (`feishu_sync.py:546-550`;实测 25s 超时未返回、同一窗口重复查 472 次)。本实现**游标一定推进**:
|
|
13
|
+
// 分片失败只记账并继续下一片;两路全断则上抛,由调用方降级——绝不空转。
|
|
14
|
+
import { ScheduleFetchError } from '../fetch/errors.js';
|
|
15
|
+
import { larkAgenda, larkSearchEvents, larkGetEvent } from '../fetch/feishu.js';
|
|
16
|
+
import { dayStartISO, dayEndISO, shardWindows, remoteSlotOf } from './iso.js';
|
|
17
|
+
function msg(e) { return e instanceof Error ? e.message : String(e); }
|
|
18
|
+
function toRemote(e) {
|
|
19
|
+
const slot = remoteSlotOf(e.start, e.end);
|
|
20
|
+
return { ...e, slotStart: slot.start, slotEnd: slot.end };
|
|
21
|
+
}
|
|
22
|
+
export function pullFeishuDate(cli, date) {
|
|
23
|
+
const errors = [];
|
|
24
|
+
const calls = { agenda: 0, search: 0, detail: 0 };
|
|
25
|
+
// 阶段 1 甲路:议程(失败不致命——乙路兜底,与老家同口径)
|
|
26
|
+
let agenda = [];
|
|
27
|
+
let agendaFailed = false;
|
|
28
|
+
calls.agenda++;
|
|
29
|
+
try {
|
|
30
|
+
agenda = larkAgenda(cli, dayStartISO(date), dayEndISO(date));
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
agendaFailed = true;
|
|
34
|
+
errors.push('议程拉取失败:' + msg(e));
|
|
35
|
+
}
|
|
36
|
+
// 阶段 1 乙路:分片检索(每片独立成败;失败只记账,游标照推)
|
|
37
|
+
const shards = shardWindows(date);
|
|
38
|
+
const found = [];
|
|
39
|
+
let shardFailed = 0;
|
|
40
|
+
for (const w of shards) {
|
|
41
|
+
calls.search++;
|
|
42
|
+
try {
|
|
43
|
+
found.push(...larkSearchEvents(cli, w.start, w.end));
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
shardFailed++;
|
|
47
|
+
errors.push('分片检索失败(' + w.start + '~' + w.end + '):' + msg(e));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (agendaFailed && shardFailed === shards.length) {
|
|
51
|
+
// 两路全断=远端真的不可达:上抛让调用方降级,不在这里假装「远端没有事件」。
|
|
52
|
+
throw new ScheduleFetchError('LARK_BAD_RESPONSE', '远端不可达:议程与全部 ' + shards.length + ' 个分片都失败(' + errors[0] + ')');
|
|
53
|
+
}
|
|
54
|
+
// 阶段 2:合并去重(甲路优先)
|
|
55
|
+
const byId = new Map();
|
|
56
|
+
for (const e of agenda)
|
|
57
|
+
byId.set(e.eventId, e);
|
|
58
|
+
for (const e of found)
|
|
59
|
+
if (!byId.has(e.eventId))
|
|
60
|
+
byId.set(e.eventId, e);
|
|
61
|
+
// 阶段 3:逐条补 description(归属锚只有补齐后才判得准)
|
|
62
|
+
for (const e of byId.values()) {
|
|
63
|
+
if (e.description)
|
|
64
|
+
continue;
|
|
65
|
+
calls.detail++;
|
|
66
|
+
const full = larkGetEvent(cli, e.eventId);
|
|
67
|
+
if (full) {
|
|
68
|
+
e.description = full.description;
|
|
69
|
+
e.owned = full.owned;
|
|
70
|
+
if (!e.summary)
|
|
71
|
+
e.summary = full.summary;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { events: [...byId.values()].map(toRemote), errors, calls };
|
|
75
|
+
}
|
|
76
|
+
/** 远端自然键:四元组(日期 + 起 + 止 + 标题)——本地键是三元组,两侧口径不同是既有事实(S-01/S-03)。 */
|
|
77
|
+
export function remoteKeyOf(date, e) {
|
|
78
|
+
return date + '|' + e.slotStart + '|' + e.slotEnd + '|' + e.summary;
|
|
79
|
+
}
|
|
80
|
+
export function localKeyOf(date, timeStart, timeEnd) {
|
|
81
|
+
return date + '|' + timeStart + '|' + timeEnd;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/plan/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export declare const LOCAL_STATES: readonly ["created", "found", "updated", "deleted", "upserted", "unchanged", "preview", "reviewed", "checked"];
|
|
2
|
+
export type LocalState = (typeof LOCAL_STATES)[number];
|
|
3
|
+
/** 远端侧状态:`found_feishu`/`created_feishu`/`unavailable` 三值沿用老家口径(S-04),后两值是合成写新增的档。 */
|
|
4
|
+
export declare const REMOTE_STATES: readonly ["found_feishu", "created_feishu", "updated_feishu", "deleted_feishu", "unavailable", "skipped", "none"];
|
|
5
|
+
export type RemoteState = (typeof REMOTE_STATES)[number];
|
|
6
|
+
export interface PlanReceipt {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
message: string;
|
|
9
|
+
op: string;
|
|
10
|
+
/** 本地侧做了什么。 */
|
|
11
|
+
local: LocalState;
|
|
12
|
+
/** 远端侧做了什么(`unavailable`=远端没成;`skipped`=调用方显式只要本地;`none`=本 op 与远端无关)。 */
|
|
13
|
+
remote: RemoteState;
|
|
14
|
+
/** 远端对象的标识(没有就 null——不写空串冒充)。 */
|
|
15
|
+
remoteId: string | null;
|
|
16
|
+
/** 两侧是否都对齐(出口据此给退出码)。 */
|
|
17
|
+
achieved: boolean;
|
|
18
|
+
/** 真失败:非空即「这一趟没达成」(退出码非 0)。 */
|
|
19
|
+
errors: string[];
|
|
20
|
+
/** 说明性的话(已自动处理/不是我的对象等):进回执给人看,不影响达成判定。 */
|
|
21
|
+
notes: string[];
|
|
22
|
+
id?: number;
|
|
23
|
+
date?: string;
|
|
24
|
+
dates?: string[];
|
|
25
|
+
counts?: Record<string, number>;
|
|
26
|
+
items?: unknown[];
|
|
27
|
+
/** op 自己的附加格(例如自检的「留下了什么/怎么清」)。 */
|
|
28
|
+
extra?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface ReceiptInput {
|
|
31
|
+
op: string;
|
|
32
|
+
message: string;
|
|
33
|
+
local: LocalState;
|
|
34
|
+
remote: RemoteState;
|
|
35
|
+
remoteId?: string | null;
|
|
36
|
+
errors?: string[];
|
|
37
|
+
notes?: string[];
|
|
38
|
+
id?: number;
|
|
39
|
+
date?: string;
|
|
40
|
+
dates?: string[];
|
|
41
|
+
counts?: Record<string, number>;
|
|
42
|
+
items?: unknown[];
|
|
43
|
+
/** op 自己的附加格(例如自检的「留下了什么/怎么清」)。 */
|
|
44
|
+
extra?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/** 达成判据:远端侧不是「没成」,且没有记账错误。
|
|
47
|
+
* `skipped`(参数只要本地)与 `none`(本 op 与远端无关)都算达成——调用方要的就是这个范围。 */
|
|
48
|
+
export declare function achievedOf(remote: RemoteState, errors: string[]): boolean;
|
|
49
|
+
export declare function buildReceipt(input: ReceiptInput): PlanReceipt;
|
|
50
|
+
/** 远端侧一句话(人读):写在 message 里,机读看 `remote` 字段。 */
|
|
51
|
+
export declare const REMOTE_LABEL: Record<RemoteState, string>;
|
|
52
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../src/plan/receipt.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,gHAAiH,CAAC;AAC3I,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,sFAAsF;AACtF,eAAO,MAAM,aAAa,mHAEhB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE,WAAW,CAAC;IACpB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yBAAyB;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;8DAC8D;AAC9D,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAGzE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAqB7D;AAED,+CAA+C;AAC/C,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQpD,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// 日程与计划·合成写回执(能力内部件):一条写命令**分字段**报「本地侧 / 远端侧 / 远端标识」。
|
|
2
|
+
//
|
|
3
|
+
// 判据出处:`docs/agents/合成写判据.md` 第一节第 4/5 条;修正的有意偏离 D-14——
|
|
4
|
+
// 老家回执在「命中本地且已有远端标识」那一支根本没有 `feishu` 键(`schedule_db.py:1062` 对 `:1066`/`:1083`),
|
|
5
|
+
// 消费方按字段取会 KeyError。本实现**三格永在**,且「这一趟算不算达成」是显式字段(`achieved`),
|
|
6
|
+
// 由出口映射成退出码(没达成 → 非 0,照裁定 A6②/用户故事 6;降级照老 A6①:远端不可用不拦住本地写)。
|
|
7
|
+
export const LOCAL_STATES = ['created', 'found', 'updated', 'deleted', 'upserted', 'unchanged', 'preview', 'reviewed', 'checked'];
|
|
8
|
+
/** 远端侧状态:`found_feishu`/`created_feishu`/`unavailable` 三值沿用老家口径(S-04),后两值是合成写新增的档。 */
|
|
9
|
+
export const REMOTE_STATES = [
|
|
10
|
+
'found_feishu', 'created_feishu', 'updated_feishu', 'deleted_feishu', 'unavailable', 'skipped', 'none',
|
|
11
|
+
];
|
|
12
|
+
/** 达成判据:远端侧不是「没成」,且没有记账错误。
|
|
13
|
+
* `skipped`(参数只要本地)与 `none`(本 op 与远端无关)都算达成——调用方要的就是这个范围。 */
|
|
14
|
+
export function achievedOf(remote, errors) {
|
|
15
|
+
if (errors.length)
|
|
16
|
+
return false;
|
|
17
|
+
return remote !== 'unavailable';
|
|
18
|
+
}
|
|
19
|
+
export function buildReceipt(input) {
|
|
20
|
+
const errors = input.errors ?? [];
|
|
21
|
+
const achieved = achievedOf(input.remote, errors);
|
|
22
|
+
const out = {
|
|
23
|
+
ok: achieved,
|
|
24
|
+
message: input.message,
|
|
25
|
+
op: input.op,
|
|
26
|
+
local: input.local,
|
|
27
|
+
remote: input.remote,
|
|
28
|
+
remoteId: input.remoteId ?? null,
|
|
29
|
+
achieved,
|
|
30
|
+
errors,
|
|
31
|
+
notes: input.notes ?? [],
|
|
32
|
+
};
|
|
33
|
+
if (input.id !== undefined)
|
|
34
|
+
out.id = input.id;
|
|
35
|
+
if (input.date !== undefined)
|
|
36
|
+
out.date = input.date;
|
|
37
|
+
if (input.dates !== undefined)
|
|
38
|
+
out.dates = input.dates;
|
|
39
|
+
if (input.counts !== undefined)
|
|
40
|
+
out.counts = input.counts;
|
|
41
|
+
if (input.items !== undefined)
|
|
42
|
+
out.items = input.items;
|
|
43
|
+
if (input.extra !== undefined)
|
|
44
|
+
Object.assign(out, input.extra);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** 远端侧一句话(人读):写在 message 里,机读看 `remote` 字段。 */
|
|
48
|
+
export const REMOTE_LABEL = {
|
|
49
|
+
found_feishu: '远端已有(认出来了,未重复建)',
|
|
50
|
+
created_feishu: '远端已建',
|
|
51
|
+
updated_feishu: '远端已改',
|
|
52
|
+
deleted_feishu: '远端已删',
|
|
53
|
+
unavailable: '远端没成(远端不可用或写失败)',
|
|
54
|
+
skipped: '远端按参数跳过(只做本地)',
|
|
55
|
+
none: '本命令不碰远端',
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../src/plan/receipt.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,wDAAwD;AACxD,iFAAiF;AACjF,8DAA8D;AAC9D,4DAA4D;AAC5D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAG3I,sFAAsF;AACtF,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM;CAC9F,CAAC;AA6CX;8DAC8D;AAC9D,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,MAAgB;IAC9D,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,MAAM,KAAK,aAAa,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAmB;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,GAAG,GAAgB;QACvB,EAAE,EAAE,QAAQ;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;QAChC,QAAQ;QACR,MAAM;QACN,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;KACzB,CAAC;IACF,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS;QAAE,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACvD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACvD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,YAAY,GAAgC;IACvD,YAAY,EAAE,iBAAiB;IAC/B,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,MAAM;IACtB,WAAW,EAAE,iBAAiB;IAC9B,OAAO,EAAE,eAAe;IACxB,IAAI,EAAE,SAAS;CAChB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type RemoteEvent } from './pull.js';
|
|
2
|
+
import type { RemoteState } from './receipt.js';
|
|
3
|
+
export interface RemoteSnapshot {
|
|
4
|
+
cli: string;
|
|
5
|
+
date: string;
|
|
6
|
+
events: RemoteEvent[];
|
|
7
|
+
/** 拉取期间记下的错(分片失败等):不假装远端干净,也不因此阻断本地写。 */
|
|
8
|
+
pullErrors: string[];
|
|
9
|
+
calls: {
|
|
10
|
+
agenda: number;
|
|
11
|
+
search: number;
|
|
12
|
+
detail: number;
|
|
13
|
+
};
|
|
14
|
+
/** 已被本地事件认领的远端对象 id(=远端一侧的「已对上」集合)。 */
|
|
15
|
+
claimed: Set<string>;
|
|
16
|
+
}
|
|
17
|
+
/** 一次拉取;拿不到远端(两路全断)即上抛,由调用方按降级处理。 */
|
|
18
|
+
export declare function takeSnapshot(cli: string, date: string): RemoteSnapshot;
|
|
19
|
+
export interface AlignSpec {
|
|
20
|
+
date: string;
|
|
21
|
+
timeStart: string;
|
|
22
|
+
timeEnd: string;
|
|
23
|
+
title: string;
|
|
24
|
+
notes?: string | null;
|
|
25
|
+
/** 本地已存的远端标识(有就先按它认;认不到再按四元组认;都没有就建)。 */
|
|
26
|
+
remoteId?: string | null;
|
|
27
|
+
}
|
|
28
|
+
export interface AlignOutcome {
|
|
29
|
+
remote: RemoteState;
|
|
30
|
+
remoteId: string | null;
|
|
31
|
+
/** 真失败(远端建/写失败等):非空即这一条没对齐。 */
|
|
32
|
+
errors: string[];
|
|
33
|
+
/** 说明性的话(已自动改认/不是我的对象):不影响达成判定。 */
|
|
34
|
+
notes: string[];
|
|
35
|
+
}
|
|
36
|
+
/** 远端判重:先按本地存的标识认,再按四元组认(只看带归属锚的)。 */
|
|
37
|
+
export declare function matchIn(snap: RemoteSnapshot, spec: AlignSpec): RemoteEvent | null;
|
|
38
|
+
/**
|
|
39
|
+
* 把一条本地事实对齐到远端:命中即回填标识(幂等),未命中即建(描述带归属锚)。
|
|
40
|
+
* 拿不到远端标识(D-11)**绝不写空**:返回 `unavailable` + 明确错误,让调用方与退出码如实反映。
|
|
41
|
+
*/
|
|
42
|
+
export declare function alignOne(snap: RemoteSnapshot, spec: AlignSpec): AlignOutcome;
|
|
43
|
+
/**
|
|
44
|
+
* 同槽兜底清理(S-12 的同槽一侧):飞书日历没有「改时间」,改时段=建新 + 删旧;
|
|
45
|
+
* 删旧失败或历史上留了重复时,把该槽位里**带归属锚**的其它对象删掉,只留 keeper。
|
|
46
|
+
* 不带锚的一律不动。
|
|
47
|
+
*/
|
|
48
|
+
export declare function cleanSlot(snap: RemoteSnapshot, spec: AlignSpec & {
|
|
49
|
+
keepId: string;
|
|
50
|
+
}): {
|
|
51
|
+
deleted: string[];
|
|
52
|
+
errors: string[];
|
|
53
|
+
};
|
|
54
|
+
/** 孤儿=带归属锚、但没有任何本地事件认领的远端对象(S-12:该清;不带锚的进不来这个集合)。 */
|
|
55
|
+
export declare function orphansOf(snap: RemoteSnapshot): RemoteEvent[];
|
|
56
|
+
/** 删远端对象(删计划、孤儿清理、自检清理共用这一个出口)。 */
|
|
57
|
+
export declare function removeRemote(snap: RemoteSnapshot, eventId: string): {
|
|
58
|
+
ok: boolean;
|
|
59
|
+
error?: string;
|
|
60
|
+
};
|
|
61
|
+
export declare function rethrowRemoteUnavailable(e: unknown): never;
|
|
62
|
+
//# sourceMappingURL=remoteDay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteDay.d.ts","sourceRoot":"","sources":["../../src/plan/remoteDay.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,yCAAyC;IACzC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,uCAAuC;IACvC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAGtE;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mCAAmC;IACnC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,sCAAsC;AACtC,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,IAAI,CAQjF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,GAAG,YAAY,CA+B5E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAiB7H;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,EAAE,CAE7D;AAED,mCAAmC;AACnC,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAUnG;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAG1D"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// 日程与计划·远端当日快照与判重(能力内部件):一次拉取、多次判定、一次收口。
|
|
2
|
+
//
|
|
3
|
+
// 两侧自然键**不必相同**(判据第一节):本地是 `(日期, 起, 止)` 三元组(title 只展示、不参与身份,S-02),
|
|
4
|
+
// 远端是 `(日期, 起, 止, 标题)` 四元组(S-03)。所以判重有两道:
|
|
5
|
+
// ① 本地有没有这一条 → 由 `fetch/db.ts` 的三元组查重管(不在本件)
|
|
6
|
+
// ② 远端有没有这一条 → 由本件管(**不再只看本地存的标识**,这就是 D-12 的修法:
|
|
7
|
+
// 老家 `op=sync` 调了 `searchFeishuEvents` 却把返回值丢了,见 `cmd_read.ts:293` 旧码)
|
|
8
|
+
//
|
|
9
|
+
// 「谁是我管的」只有一个判据:远端对象描述里带归属锚(S-06/S-12)。不带锚的**一律不许动**。
|
|
10
|
+
import { ScheduleFetchError } from '../fetch/errors.js';
|
|
11
|
+
import { larkCreateEvent, larkDeleteEvent, composeFeishuDescription } from '../fetch/feishu.js';
|
|
12
|
+
import { pullFeishuDate } from './pull.js';
|
|
13
|
+
import { toFeishuISO } from './iso.js';
|
|
14
|
+
/** 一次拉取;拿不到远端(两路全断)即上抛,由调用方按降级处理。 */
|
|
15
|
+
export function takeSnapshot(cli, date) {
|
|
16
|
+
const pulled = pullFeishuDate(cli, date);
|
|
17
|
+
return { cli, date, events: pulled.events, pullErrors: pulled.errors, calls: pulled.calls, claimed: new Set() };
|
|
18
|
+
}
|
|
19
|
+
/** 远端判重:先按本地存的标识认,再按四元组认(只看带归属锚的)。 */
|
|
20
|
+
export function matchIn(snap, spec) {
|
|
21
|
+
if (spec.remoteId) {
|
|
22
|
+
const byId = snap.events.find((e) => e.eventId === spec.remoteId);
|
|
23
|
+
if (byId)
|
|
24
|
+
return byId;
|
|
25
|
+
}
|
|
26
|
+
return snap.events.find((e) => e.owned && e.summary === spec.title && e.slotStart === spec.timeStart && e.slotEnd === spec.timeEnd) ?? null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 把一条本地事实对齐到远端:命中即回填标识(幂等),未命中即建(描述带归属锚)。
|
|
30
|
+
* 拿不到远端标识(D-11)**绝不写空**:返回 `unavailable` + 明确错误,让调用方与退出码如实反映。
|
|
31
|
+
*/
|
|
32
|
+
export function alignOne(snap, spec) {
|
|
33
|
+
const errors = [];
|
|
34
|
+
const notes = [];
|
|
35
|
+
const hit = matchIn(snap, spec);
|
|
36
|
+
if (hit) {
|
|
37
|
+
snap.claimed.add(hit.eventId);
|
|
38
|
+
if (spec.remoteId && spec.remoteId !== hit.eventId) {
|
|
39
|
+
notes.push('本地存的远端标识与远端实际不符,已按四元组改认:' + spec.remoteId + ' → ' + hit.eventId);
|
|
40
|
+
}
|
|
41
|
+
return { remote: 'found_feishu', remoteId: hit.eventId, errors, notes };
|
|
42
|
+
}
|
|
43
|
+
// 远端同槽位有不带锚的对象:那不是我的,不动它,但要说出来(免得用户以为系统漏了)。
|
|
44
|
+
const foreign = snap.events.find((e) => !e.owned && e.summary === spec.title && e.slotStart === spec.timeStart && e.slotEnd === spec.timeEnd);
|
|
45
|
+
if (foreign)
|
|
46
|
+
notes.push('远端同槽位存在不带归属标记的事件(不是本技能管的,不动它):' + foreign.eventId);
|
|
47
|
+
try {
|
|
48
|
+
const created = larkCreateEvent(snap.cli, toFeishuISO(spec.date, spec.timeStart), toFeishuISO(spec.date, spec.timeEnd), spec.title, composeFeishuDescription(spec.notes));
|
|
49
|
+
snap.events.push({ ...created, slotStart: spec.timeStart, slotEnd: spec.timeEnd });
|
|
50
|
+
snap.claimed.add(created.eventId);
|
|
51
|
+
return { remote: 'created_feishu', remoteId: created.eventId, errors, notes };
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
const m = e instanceof Error ? e.message : String(e);
|
|
55
|
+
return { remote: 'unavailable', remoteId: null, errors: [...errors, m], notes };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 同槽兜底清理(S-12 的同槽一侧):飞书日历没有「改时间」,改时段=建新 + 删旧;
|
|
60
|
+
* 删旧失败或历史上留了重复时,把该槽位里**带归属锚**的其它对象删掉,只留 keeper。
|
|
61
|
+
* 不带锚的一律不动。
|
|
62
|
+
*/
|
|
63
|
+
export function cleanSlot(snap, spec) {
|
|
64
|
+
const deleted = [];
|
|
65
|
+
const errors = [];
|
|
66
|
+
const others = snap.events.filter((e) => e.owned && e.eventId !== spec.keepId && e.slotStart === spec.timeStart && e.slotEnd === spec.timeEnd);
|
|
67
|
+
for (const e of others) {
|
|
68
|
+
try {
|
|
69
|
+
larkDeleteEvent(snap.cli, e.eventId);
|
|
70
|
+
deleted.push(e.eventId);
|
|
71
|
+
snap.claimed.delete(e.eventId);
|
|
72
|
+
snap.events = snap.events.filter((x) => x.eventId !== e.eventId);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
errors.push('同槽清理失败:' + e.eventId + '(' + (err instanceof Error ? err.message : String(err)) + ')');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { deleted, errors };
|
|
79
|
+
}
|
|
80
|
+
/** 孤儿=带归属锚、但没有任何本地事件认领的远端对象(S-12:该清;不带锚的进不来这个集合)。 */
|
|
81
|
+
export function orphansOf(snap) {
|
|
82
|
+
return snap.events.filter((e) => e.owned && !snap.claimed.has(e.eventId));
|
|
83
|
+
}
|
|
84
|
+
/** 删远端对象(删计划、孤儿清理、自检清理共用这一个出口)。 */
|
|
85
|
+
export function removeRemote(snap, eventId) {
|
|
86
|
+
try {
|
|
87
|
+
larkDeleteEvent(snap.cli, eventId);
|
|
88
|
+
snap.claimed.delete(eventId);
|
|
89
|
+
snap.events = snap.events.filter((e) => e.eventId !== eventId);
|
|
90
|
+
return { ok: true };
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
const m = e instanceof Error ? e.message : String(e);
|
|
94
|
+
return { ok: false, error: m };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function rethrowRemoteUnavailable(e) {
|
|
98
|
+
if (e instanceof ScheduleFetchError)
|
|
99
|
+
throw e;
|
|
100
|
+
throw new ScheduleFetchError('LARK_BAD_RESPONSE', '远端不可达:' + (e instanceof Error ? e.message : String(e)));
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=remoteDay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteDay.js","sourceRoot":"","sources":["../../src/plan/remoteDay.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,mEAAmE;AACnE,0CAA0C;AAC1C,+CAA+C;AAC/C,mDAAmD;AACnD,4EAA4E;AAC5E,EAAE;AACF,sDAAsD;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,cAAc,EAAoB,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAcvC,qCAAqC;AACrC,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,IAAY;IACpD,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;AAC1H,CAAC;AAqBD,sCAAsC;AACtC,MAAM,UAAU,OAAO,CAAC,IAAoB,EAAE,IAAe;IAC3D,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClE,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CACrB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAC3G,IAAI,IAAI,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAoB,EAAE,IAAe;IAC5D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,GAAG,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;YACnD,KAAK,CAAC,IAAI,CAAC,0BAA0B,GAAG,IAAI,CAAC,QAAQ,GAAG,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IACD,4CAA4C;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAC5G,CAAC;IACF,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,gCAAgC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,eAAe,CAC7B,IAAI,CAAC,GAAG,EACR,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EACtC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EACpC,IAAI,CAAC,KAAK,EACV,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CACrC,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAChF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IAClF,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAoB,EAAE,IAAoC;IAClF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAC5G,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACtG,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,SAAS,CAAC,IAAoB;IAC5C,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,YAAY,CAAC,IAAoB,EAAE,OAAe;IAChE,IAAI,CAAC;QACH,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;QAC/D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,CAAU;IACjD,IAAI,CAAC,YAAY,kBAAkB;QAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,kBAAkB,CAAC,mBAAmB,EAAE,QAAQ,GAAG,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7G,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../src/plan/single.ts"],"names":[],"mappings":"AAUA,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAIhF,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAiGtD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAmD1D"}
|
|
@@ -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":"AAAA,kCAAkC;AAClC,EAAE;AACF,mDAAmD;AACnD,kEAAkE;AAClE,qDAAqD;AACrD,iEAAiE;AACjE,yBAAyB;AACzB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AACpJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAqC,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAuB,MAAM,gBAAgB,CAAC;AAEtG,MAAM,UAAU,SAAS,CAAC,GAAc;IACtC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAI,KAAK,CAAC,UAAiC,IAAI,MAAM,CAAC,UAAU,CAAC;IAChF,MAAM,OAAO,GAAI,KAAK,CAAC,QAA+B,IAAI,MAAM,CAAC,QAAQ,CAAC;IAC1E,IAAI,SAAS,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,EAAE,2BAA2B,GAAG,SAAS,GAAG,GAAG,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC;IAClH,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,KAA+B,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,SAAS,KAAK,MAAM,CAAC,UAAU,IAAI,OAAO,KAAK,MAAM,CAAC,QAAQ,CAAC;IAE7E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;IAC5F,IAAI,MAAmB,CAAC;IACxB,IAAI,QAAQ,GAAkB,KAAK,CAAC,eAAe,CAAC;IAEpD,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,IAAI,IAAI,GAA0B,IAAI,CAAC;QACvC,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;YAChC,IAAI,KAAK,EAAE,CAAC;gBACV,gCAAgC;gBAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE;oBAC1B,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO;oBACxD,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI;iBACvD,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpD,MAAM,GAAG,aAAa,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC;oBACnC,IAAI,GAAG,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACjC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;wBACpC,IAAI,GAAG,CAAC,EAAE;4BAAE,MAAM,CAAC,aAAa,EAAE,CAAC;;4BAC9B,MAAM,CAAC,IAAI,CAAC,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;oBAC9D,CAAC;oBACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE;wBAC3B,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO;wBACxD,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ;qBAC9D,CAAC,CAAC;oBACH,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;oBAC5B,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;oBAC1C,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAChD,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;oBACzB,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE;oBACzB,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ;oBACtE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,eAAe;iBACxE,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;gBACzB,IAAI,GAAG,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;oBAClD,MAAM,GAAG,aAAa,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC;oBAChE,MAAM,IAAI,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBACnD,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,WAAW,KAAK,IAAI,CAAC,EAAE,CAAC;wBACrE,IAAI,CAAC;4BACH,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;4BACpF,MAAM,CAAC,aAAa,EAAE,CAAC;4BACvB,MAAM,GAAG,gBAAgB,CAAC;wBAC5B,CAAC;wBAAC,OAAO,CAAC,EAAE,CAAC;4BACX,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;4BACrE,MAAM,GAAG,aAAa,CAAC;wBACzB,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;oBACtB,CAAC;oBACD,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,CAAC,eAAe;wBAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;oBAC3F,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,aAAa,CAAC;QACzB,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,GAAG,aAAa,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,SAAS,CAAC;IACrB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,GAAG,EAAE,GAAG,GAAG,GAAG,OAAO,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,SAAS,GAAG,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;UACrG,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;IACrC,OAAO,aAAa,CAAC,YAAY,CAAC;QAChC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM;KACvG,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAc;IAC1C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC;IACnC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACpD,IAAI,MAAmB,CAAC;IACxB,IAAI,QAAQ,GAAkB,MAAM,CAAC,eAAe,CAAC;IAErD,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,IAAI,IAAI,GAA0B,IAAI,CAAC;QACvC,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;YAChC,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;gBACrD,IAAI,CAAC,CAAC,EAAE;oBAAE,MAAM,CAAC,aAAa,EAAE,CAAC;;oBAC5B,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,eAAe,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;YAC5E,CAAC;YACD,4CAA4C;YAC5C,yBAAyB;YACzB,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE;gBAC3B,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ;gBACzE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;aACrD,CAAC,CAAC;YACH,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa;gBACpC,CAAC,CAAC,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC;QACxF,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,aAAa,CAAC;QACzB,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,GAAG,aAAa,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,SAAS,CAAC;IACrB,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;UACpF,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;IACrC,OAAO,aAAa,CAAC,YAAY,CAAC;QAChC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM;KAC5G,CAAC,CAAC,CAAC;AACN,CAAC"}
|