weflow-cli 1.6.4 → 1.8.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 (205) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/CHANGELOG.md +1966 -0
  3. package/OPERATIONS.md +808 -0
  4. package/README.en.md +93 -7
  5. package/README.md +31 -6
  6. package/bin/weflow-cli.ts +1245 -26
  7. package/dist/bin/weflow-cli.js +1349 -72
  8. package/dist/bin/weflow-cli.js.map +1 -1
  9. package/dist/mcp-server/index.js +39 -13
  10. package/dist/mcp-server/index.js.map +1 -1
  11. package/dist/src/core/appMsgFormat.d.ts +37 -0
  12. package/dist/src/core/appMsgFormat.d.ts.map +1 -0
  13. package/dist/src/core/appMsgFormat.js +106 -0
  14. package/dist/src/core/appMsgFormat.js.map +1 -0
  15. package/dist/src/core/ntCore.d.ts +43 -0
  16. package/dist/src/core/ntCore.d.ts.map +1 -1
  17. package/dist/src/core/ntCore.js +31 -0
  18. package/dist/src/core/ntCore.js.map +1 -1
  19. package/dist/src/core/sqlcipherCore.d.ts +0 -12
  20. package/dist/src/core/sqlcipherCore.d.ts.map +1 -1
  21. package/dist/src/core/sqlcipherCore.js +5 -64
  22. package/dist/src/core/sqlcipherCore.js.map +1 -1
  23. package/dist/src/core/wcdbCore.d.ts +1 -1
  24. package/dist/src/core/wcdbCore.d.ts.map +1 -1
  25. package/dist/src/core/wcdbCore.js +5 -1
  26. package/dist/src/core/wcdbCore.js.map +1 -1
  27. package/dist/src/panel/client.d.ts +25 -0
  28. package/dist/src/panel/client.d.ts.map +1 -0
  29. package/dist/src/panel/client.js +34 -0
  30. package/dist/src/panel/client.js.map +1 -0
  31. package/dist/src/panel/endpoint.d.ts +45 -0
  32. package/dist/src/panel/endpoint.d.ts.map +1 -0
  33. package/dist/src/panel/endpoint.js +132 -0
  34. package/dist/src/panel/endpoint.js.map +1 -0
  35. package/dist/src/panel/launch.d.ts +50 -0
  36. package/dist/src/panel/launch.d.ts.map +1 -0
  37. package/dist/src/panel/launch.js +74 -0
  38. package/dist/src/panel/launch.js.map +1 -0
  39. package/dist/src/panel/server.d.ts +23 -0
  40. package/dist/src/panel/server.d.ts.map +1 -0
  41. package/dist/src/panel/server.js +413 -0
  42. package/dist/src/panel/server.js.map +1 -0
  43. package/dist/src/panel/userId.d.ts +37 -0
  44. package/dist/src/panel/userId.d.ts.map +1 -0
  45. package/dist/src/panel/userId.js +56 -0
  46. package/dist/src/panel/userId.js.map +1 -0
  47. package/dist/src/services/assistantDaemon.d.ts +20 -3
  48. package/dist/src/services/assistantDaemon.d.ts.map +1 -1
  49. package/dist/src/services/assistantDaemon.js +46 -9
  50. package/dist/src/services/assistantDaemon.js.map +1 -1
  51. package/dist/src/services/assistantEval.d.ts +141 -0
  52. package/dist/src/services/assistantEval.d.ts.map +1 -0
  53. package/dist/src/services/assistantEval.js +497 -0
  54. package/dist/src/services/assistantEval.js.map +1 -0
  55. package/dist/src/services/assistantMemory.d.ts +106 -4
  56. package/dist/src/services/assistantMemory.d.ts.map +1 -1
  57. package/dist/src/services/assistantMemory.js +396 -61
  58. package/dist/src/services/assistantMemory.js.map +1 -1
  59. package/dist/src/services/assistantPrivacy.d.ts +15 -2
  60. package/dist/src/services/assistantPrivacy.d.ts.map +1 -1
  61. package/dist/src/services/assistantPrivacy.js +18 -4
  62. package/dist/src/services/assistantPrivacy.js.map +1 -1
  63. package/dist/src/services/assistantRouter.d.ts +41 -0
  64. package/dist/src/services/assistantRouter.d.ts.map +1 -0
  65. package/dist/src/services/assistantRouter.js +151 -0
  66. package/dist/src/services/assistantRouter.js.map +1 -0
  67. package/dist/src/services/assistantService.d.ts +209 -3
  68. package/dist/src/services/assistantService.d.ts.map +1 -1
  69. package/dist/src/services/assistantService.js +590 -75
  70. package/dist/src/services/assistantService.js.map +1 -1
  71. package/dist/src/services/assistantTools.d.ts +205 -1
  72. package/dist/src/services/assistantTools.d.ts.map +1 -1
  73. package/dist/src/services/assistantTools.js +1089 -58
  74. package/dist/src/services/assistantTools.js.map +1 -1
  75. package/dist/src/services/assistantTrace.d.ts +47 -0
  76. package/dist/src/services/assistantTrace.d.ts.map +1 -0
  77. package/dist/src/services/assistantTrace.js +150 -0
  78. package/dist/src/services/assistantTrace.js.map +1 -0
  79. package/dist/src/services/chatService.d.ts +13 -0
  80. package/dist/src/services/chatService.d.ts.map +1 -1
  81. package/dist/src/services/chatService.js +23 -0
  82. package/dist/src/services/chatService.js.map +1 -1
  83. package/dist/src/services/configService.d.ts +9 -0
  84. package/dist/src/services/configService.d.ts.map +1 -1
  85. package/dist/src/services/configService.js +15 -8
  86. package/dist/src/services/configService.js.map +1 -1
  87. package/dist/src/services/messageQuery.d.ts +35 -0
  88. package/dist/src/services/messageQuery.d.ts.map +1 -1
  89. package/dist/src/services/messageQuery.js +61 -0
  90. package/dist/src/services/messageQuery.js.map +1 -1
  91. package/dist/src/services/pythonBridge.d.ts +41 -0
  92. package/dist/src/services/pythonBridge.d.ts.map +1 -0
  93. package/dist/src/services/pythonBridge.js +119 -0
  94. package/dist/src/services/pythonBridge.js.map +1 -0
  95. package/dist/src/services/syncService.d.ts +127 -0
  96. package/dist/src/services/syncService.d.ts.map +1 -0
  97. package/dist/src/services/syncService.js +263 -0
  98. package/dist/src/services/syncService.js.map +1 -0
  99. package/dist/src/services/syncState.d.ts +113 -0
  100. package/dist/src/services/syncState.d.ts.map +1 -0
  101. package/dist/src/services/syncState.js +214 -0
  102. package/dist/src/services/syncState.js.map +1 -0
  103. package/dist/src/services/wechatMessageService.d.ts +25 -0
  104. package/dist/src/services/wechatMessageService.d.ts.map +1 -1
  105. package/dist/src/services/wechatMessageService.js +69 -7
  106. package/dist/src/services/wechatMessageService.js.map +1 -1
  107. package/dist/src/utils/atomicWrite.d.ts +4 -0
  108. package/dist/src/utils/atomicWrite.d.ts.map +1 -0
  109. package/dist/src/utils/atomicWrite.js +60 -0
  110. package/dist/src/utils/atomicWrite.js.map +1 -0
  111. package/dist/src/utils/dateRange.d.ts +15 -0
  112. package/dist/src/utils/dateRange.d.ts.map +1 -1
  113. package/dist/src/utils/dateRange.js +47 -0
  114. package/dist/src/utils/dateRange.js.map +1 -1
  115. package/dist/src/utils/text.d.ts +18 -0
  116. package/dist/src/utils/text.d.ts.map +1 -0
  117. package/dist/src/utils/text.js +20 -0
  118. package/dist/src/utils/text.js.map +1 -0
  119. package/docs/AI_INTERFACE.md +2 -0
  120. package/docs/DECISIONS.md +1484 -20
  121. package/docs/EXTENDING.md +187 -0
  122. package/docs/FUNCTIONAL-IMPROVEMENT-SPEC.md +338 -0
  123. package/docs/JEV/345/272/224/347/224/250/346/226/275/345/267/245/346/226/207/346/241/243.md +310 -0
  124. package/docs/MCP.md +23 -4
  125. package/docs/PROJECT_STATE.md +299 -96
  126. package/docs/SETUP.md +22 -0
  127. package/docs/SYNC_CONTRACT.md +180 -0
  128. package/mcp-server/index.ts +681 -657
  129. package/package.json +84 -83
  130. package/resources/panel/ball-position.cjs +153 -0
  131. package/resources/panel/index.html +46 -0
  132. package/resources/panel/main.cjs +447 -0
  133. package/resources/panel/mascot-focus.png +0 -0
  134. package/resources/panel/mascot-happy.png +0 -0
  135. package/resources/panel/mascot-sorry.png +0 -0
  136. package/resources/panel/mascot-tired.png +0 -0
  137. package/resources/panel/mascot.png +0 -0
  138. package/resources/panel/package.json +7 -0
  139. package/resources/panel/panel.css +407 -0
  140. package/resources/panel/preload.cjs +72 -0
  141. package/resources/panel/quick-menu.cjs +197 -0
  142. package/resources/panel/renderer.js +441 -0
  143. package/resources/panel/tray-menu.cjs +40 -0
  144. package/resources/panel/tray.png +0 -0
  145. package/scripts/_utils.py +403 -54
  146. package/scripts/annual_report.py +4 -2
  147. package/scripts/article_notes.py +423 -0
  148. package/scripts/backfill_articles.py +438 -0
  149. package/scripts/biz_daily.py +590 -105
  150. package/scripts/chat_notes.py +470 -0
  151. package/scripts/classify_daily.py +10 -18
  152. package/scripts/compile_wiki.py +1059 -46
  153. package/scripts/create_reading_notes.py +199 -14
  154. package/scripts/decide.py +253 -0
  155. package/scripts/draft_reply.py +671 -0
  156. package/scripts/export_chat_html.py +212 -46
  157. package/scripts/extract_todos.py +31 -5
  158. package/scripts/fav_notes.py +318 -0
  159. package/scripts/fix_topics.py +2 -1
  160. package/scripts/generate_ai_report.py +214 -9
  161. package/scripts/generate_html.py +18 -3
  162. package/scripts/generate_review.py +2 -2
  163. package/scripts/jev_client.py +294 -0
  164. package/scripts/jev_probe.py +440 -0
  165. package/scripts/nt_common.py +82 -0
  166. package/scripts/nt_decrypt.py +1755 -1352
  167. package/scripts/pipeline.py +27 -2
  168. package/scripts/promote_ideas.py +19 -4
  169. package/scripts/quality_eval.py +707 -0
  170. package/scripts/rag_chat.py +4 -4
  171. package/scripts/read_image.py +187 -0
  172. package/scripts/reply_debt.py +488 -0
  173. package/scripts/route_cards.py +453 -0
  174. package/scripts/semantic_search.py +208 -49
  175. package/scripts/user_notes.py +315 -0
  176. package/scripts/vault_rag.py +13 -7
  177. package/scripts/vault_search.py +21 -4
  178. package/scripts/wiki_lint.py +397 -0
  179. package/src/core/appMsgFormat.ts +117 -0
  180. package/src/core/ntCore.ts +67 -0
  181. package/src/core/sqlcipherCore.ts +5 -61
  182. package/src/core/wcdbCore.ts +6 -2
  183. package/src/panel/client.ts +49 -0
  184. package/src/panel/endpoint.ts +144 -0
  185. package/src/panel/launch.ts +101 -0
  186. package/src/panel/server.ts +424 -0
  187. package/src/panel/userId.ts +72 -0
  188. package/src/services/assistantDaemon.ts +57 -9
  189. package/src/services/assistantEval.ts +611 -0
  190. package/src/services/assistantMemory.ts +413 -57
  191. package/src/services/assistantPrivacy.ts +18 -4
  192. package/src/services/assistantRouter.ts +191 -0
  193. package/src/services/assistantService.ts +671 -74
  194. package/src/services/assistantTools.ts +1118 -57
  195. package/src/services/assistantTrace.ts +175 -0
  196. package/src/services/chatService.ts +30 -0
  197. package/src/services/configService.ts +403 -385
  198. package/src/services/messageQuery.ts +97 -0
  199. package/src/services/pythonBridge.ts +139 -0
  200. package/src/services/syncService.ts +363 -0
  201. package/src/services/syncState.ts +275 -0
  202. package/src/services/wechatMessageService.ts +73 -7
  203. package/src/utils/atomicWrite.ts +56 -0
  204. package/src/utils/dateRange.ts +46 -0
  205. package/src/utils/text.ts +20 -0
@@ -0,0 +1,263 @@
1
+ import { DEFAULT_OVERLAP_SECONDS, SYNC_SOURCE, emptySyncState, makeJobId, syncStateStore, } from './syncState.js';
2
+ /** A window wider than this is worth warning about: the read is not bounded. */
3
+ export const WIDE_WINDOW_SECONDS = 90 * 24 * 3600;
4
+ export class SyncRangeRequiredError extends Error {
5
+ code = 'SYNC_RANGE_REQUIRED';
6
+ constructor() {
7
+ super('首次同步需要 --since <日期> 或 --full');
8
+ this.name = 'SyncRangeRequiredError';
9
+ }
10
+ }
11
+ /**
12
+ * Stable identity for one message.
13
+ *
14
+ * `serverId` wins when nonzero; otherwise `localId` is paired with
15
+ * `createTime`. A bare `localId` is never an identity - it restarts in every
16
+ * shard, which is the whole reason D-014 exists.
17
+ *
18
+ * `shard` is intentionally absent: messages do not carry it, adding one would
19
+ * touch the fenced export contract, and overlap dedup does not need it (the
20
+ * same message read twice yields the same serverId or the same pair).
21
+ */
22
+ export function messageIdentity(source, talker, message) {
23
+ const serverId = String(message.serverId ?? '').trim();
24
+ if (serverId && serverId !== '0')
25
+ return `${source}|${talker}|s:${serverId}`;
26
+ return `${source}|${talker}|l:${message.localId}|t:${message.createTime}`;
27
+ }
28
+ /**
29
+ * Work out what to read.
30
+ *
31
+ * Without a prior checkpoint there is nothing to resume from, so the caller
32
+ * must say how far back to go - guessing "everything" would silently turn a
33
+ * routine sync into a full-history read.
34
+ */
35
+ export function computeWindow(prior, options) {
36
+ const overlapSeconds = options.overlapSeconds ?? prior?.checkpoint.overlapSeconds
37
+ ?? DEFAULT_OVERLAP_SECONDS;
38
+ const to = options.now ?? Math.floor(Date.now() / 1000);
39
+ if (options.full) {
40
+ return { from: null, to, overlapSeconds, full: true };
41
+ }
42
+ if (options.since !== undefined) {
43
+ return { from: options.since, to, overlapSeconds, full: false };
44
+ }
45
+ const newest = prior?.checkpoint.newestCreateTime;
46
+ if (prior && typeof newest === 'number') {
47
+ // Back up by the overlap: a second-resolution timestamp is not a cursor,
48
+ // so the boundary messages have to be read again and deduplicated.
49
+ return { from: Math.max(0, newest - overlapSeconds), to, overlapSeconds, full: false };
50
+ }
51
+ throw new SyncRangeRequiredError();
52
+ }
53
+ /** Drop repeats the overlap window re-read. */
54
+ export function dedupeMessages(messages, source, talker) {
55
+ const seen = new Set();
56
+ const unique = [];
57
+ for (const message of messages) {
58
+ const identity = messageIdentity(source, talker, message);
59
+ if (seen.has(identity))
60
+ continue;
61
+ seen.add(identity);
62
+ unique.push(message);
63
+ }
64
+ return { unique, duplicateCount: messages.length - unique.length };
65
+ }
66
+ /**
67
+ * Decide how much of the read we can stand behind.
68
+ *
69
+ * `partial` is the only value that must not be reported as success.
70
+ *
71
+ * A short-page truncation cannot occur here because this pass reads in one
72
+ * call rather than paging; `collectMessagesInRangeDetailed` detects that case
73
+ * for the paged readers and is where a future revision of this should look.
74
+ */
75
+ export function deriveCoverage(input) {
76
+ if (input.mayHaveMore)
77
+ return 'partial';
78
+ // Any per-shard reason means those rows were not read: a fault after opening
79
+ // (READ_FAILED), a table the reader can name nothing in (SCHEMA_MISMATCH), or
80
+ // a window the shard cannot express (WINDOW_UNAVAILABLE). Coverage is about
81
+ // whether the range was read, so all of them make it partial. A shard read
82
+ // with columns missing is *not* one of these - its rows did come back.
83
+ // A shard that never opened is a different answer and stays 'unverified'
84
+ // below: nothing is known about its rows either way, which is not the same
85
+ // claim as knowing they were missed.
86
+ const anyShardUnread = input.shards?.items.some(item => item.opened && item.reason);
87
+ if (anyShardUnread)
88
+ return 'partial';
89
+ if (!input.shards)
90
+ return 'unverified';
91
+ // A shard that never opened is not knowable either way; say so rather than
92
+ // claiming completeness or declaring failure.
93
+ if (input.shards.failed > 0)
94
+ return 'unverified';
95
+ return 'complete';
96
+ }
97
+ export function buildNextState(input) {
98
+ const state = input.prior ?? emptySyncState(input.talker, input.scope, input.source, input.window.overlapSeconds);
99
+ // Same form as coveredFrom/coveredTo: an offset, not a bare Z. A file that
100
+ // mixes the two makes "which local day was this?" unanswerable.
101
+ const stamp = isoFromSeconds(input.now ?? Math.floor(Date.now() / 1000));
102
+ const priorFrom = state.coveredFrom;
103
+ let coveredFrom = priorFrom;
104
+ if (input.window.full) {
105
+ // The database's true minimum is not knowable without another query, so
106
+ // this is the oldest row we actually saw and says so in `warnings`.
107
+ coveredFrom = input.oldestCreateTime ? isoFromSeconds(input.oldestCreateTime) : priorFrom;
108
+ }
109
+ else if (input.window.from !== null) {
110
+ const candidate = isoFromSeconds(input.window.from);
111
+ coveredFrom = priorFrom && priorFrom < candidate ? priorFrom : candidate;
112
+ }
113
+ return {
114
+ ...state,
115
+ scope: input.scope,
116
+ talker: input.talker,
117
+ source: input.source,
118
+ coveredFrom,
119
+ coveredTo: isoFromSeconds(input.window.to),
120
+ checkpoint: {
121
+ newestCreateTime: input.newestCreateTime ?? state.checkpoint.newestCreateTime,
122
+ overlapSeconds: input.window.overlapSeconds,
123
+ },
124
+ recordsRead: input.recordsRead,
125
+ recordsDeduplicated: input.recordsDeduplicated,
126
+ recordsReturned: input.recordsReturned,
127
+ shardsRead: input.shards?.opened ?? 0,
128
+ shardsFailed: input.shards?.failed ?? 0,
129
+ shards: input.shards
130
+ ? { scanned: input.shards.scanned, opened: input.shards.opened,
131
+ failed: input.shards.failed, items: input.shards.items }
132
+ : { scanned: 0, opened: 0, failed: 0, items: [] },
133
+ coverage: input.coverage,
134
+ mayHaveMore: input.mayHaveMore,
135
+ warnings: input.warnings,
136
+ // The attempt always advances; the success does not, so a partial run is
137
+ // never mistaken for a good one later.
138
+ lastAttempt: stamp,
139
+ lastSuccessfulRun: input.coverage === 'partial' ? state.lastSuccessfulRun : stamp,
140
+ updatedAt: stamp,
141
+ };
142
+ }
143
+ function isoFromSeconds(seconds) {
144
+ const date = new Date(seconds * 1000);
145
+ const offsetMinutes = -date.getTimezoneOffset();
146
+ const sign = offsetMinutes >= 0 ? '+' : '-';
147
+ const pad = (value) => String(Math.floor(Math.abs(value))).padStart(2, '0');
148
+ const local = new Date(date.getTime() + offsetMinutes * 60_000);
149
+ const body = local.toISOString().slice(0, 19);
150
+ return `${body}${sign}${pad(offsetMinutes / 60)}:${pad(offsetMinutes % 60)}`;
151
+ }
152
+ /**
153
+ * Run one pass. Writes state and a job record unless `write: false`.
154
+ *
155
+ * A partial run still writes (the spec forbids discarding usable results) but
156
+ * returns `success: false` with `code: 'SYNC_PARTIAL'` so nothing downstream
157
+ * treats it as complete.
158
+ */
159
+ export async function runSync(talker, options) {
160
+ const source = options.source ?? SYNC_SOURCE;
161
+ const scope = options.scope ?? talker;
162
+ const store = options.store ?? syncStateStore;
163
+ const prior = store.read(source, talker);
164
+ const window = computeWindow(prior, options);
165
+ const warnings = [];
166
+ if (window.full)
167
+ warnings.push('full-scan');
168
+ if (window.from !== null && window.to - window.from > WIDE_WINDOW_SECONDS) {
169
+ warnings.push('wide-window');
170
+ }
171
+ const limit = options.limit ?? 0;
172
+ const read = await options.read(talker, limit, window.from);
173
+ const { unique, duplicateCount } = dedupeMessages(read.messages, source, talker);
174
+ // The window is applied here as well as being pushed down, and this filter -
175
+ // not the pushdown - is what makes the result correct: a backend without
176
+ // range support returns the whole conversation, and one that cannot express
177
+ // the lower bound says so per shard rather than quietly dropping the bound.
178
+ const inWindow = window.from === null
179
+ ? unique
180
+ : unique.filter(m => Number(m.createTime) >= window.from);
181
+ const newestCreateTime = inWindow.length
182
+ ? Math.max(...inWindow.map(m => Number(m.createTime) || 0))
183
+ : null;
184
+ const oldestCreateTime = inWindow.length
185
+ ? Math.min(...inWindow.map(m => Number(m.createTime) || 0))
186
+ : null;
187
+ const mayHaveMore = limit > 0 && read.messages.length >= limit;
188
+ const coverage = deriveCoverage({ shards: read.shards, mayHaveMore });
189
+ if (!read.shards)
190
+ warnings.push('shard-report-unavailable');
191
+ if (read.shards) {
192
+ for (const item of read.shards.items) {
193
+ if (!item.opened)
194
+ warnings.push(`shard-unverified:${item.name}`);
195
+ else if (item.reason === 'READ_FAILED')
196
+ warnings.push(`shard-read-failed:${item.name}`);
197
+ // A shard the reader could not name anything in, or could not apply the
198
+ // window to. Both mean "this range was not read here", which must not
199
+ // pass as covered - hence a warning rather than a silent skip.
200
+ else if (item.reason)
201
+ warnings.push(`shard-not-read:${item.name}:${item.reason}`);
202
+ // Read, but with fields missing. Column names are schema, not content,
203
+ // so this stays safe to record in the state file.
204
+ else if (item.missingColumns?.length) {
205
+ warnings.push(`shard-columns-missing:${item.name}:${item.missingColumns.join('+')}`);
206
+ }
207
+ }
208
+ }
209
+ const next = buildNextState({
210
+ prior, talker, scope, source, window,
211
+ recordsRead: read.messages.length,
212
+ recordsDeduplicated: duplicateCount,
213
+ recordsReturned: inWindow.length,
214
+ newestCreateTime, oldestCreateTime,
215
+ shards: read.shards, coverage, mayHaveMore, warnings,
216
+ // Same clock as the window: without this the recorded attempt would come
217
+ // from Date.now() while window.to came from the caller's `now`.
218
+ now: options.now,
219
+ });
220
+ const partial = coverage === 'partial';
221
+ let jobId = null;
222
+ if (options.write !== false) {
223
+ jobId = makeJobId();
224
+ const job = {
225
+ schema: 'weflow-job/v1',
226
+ jobId,
227
+ kind: 'sync.messages',
228
+ state: partial ? 'partial' : 'completed',
229
+ createdAt: next.lastAttempt,
230
+ updatedAt: next.lastAttempt,
231
+ window: { from: window.from, to: window.to },
232
+ progress: { completed: inWindow.length, total: read.messages.length },
233
+ resumeable: partial,
234
+ result: {
235
+ records: inWindow.length,
236
+ recordsDeduplicated: duplicateCount,
237
+ shardsFailed: read.shards?.failed ?? 0,
238
+ coverage,
239
+ },
240
+ error: partial ? 'SYNC_PARTIAL' : null,
241
+ };
242
+ store.write(next);
243
+ store.writeJob(job);
244
+ }
245
+ return {
246
+ success: !partial,
247
+ code: partial ? 'SYNC_PARTIAL' : undefined,
248
+ action: 'sync.messages',
249
+ scope,
250
+ window,
251
+ recordsRead: read.messages.length,
252
+ recordsDeduplicated: duplicateCount,
253
+ recordsReturned: inWindow.length,
254
+ shards: read.shards,
255
+ coverage,
256
+ mayHaveMore,
257
+ partial,
258
+ warnings,
259
+ jobId,
260
+ state: next,
261
+ };
262
+ }
263
+ //# sourceMappingURL=syncService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncService.js","sourceRoot":"","sources":["../../../src/services/syncService.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,uBAAuB,EACvB,WAAW,EACX,cAAc,EACd,SAAS,EACT,cAAc,GAKf,MAAM,gBAAgB,CAAA;AAEvB,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAEjD,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACtC,IAAI,GAAG,qBAAqB,CAAA;IACrC;QACE,KAAK,CAAC,8BAA8B,CAAC,CAAA;QACrC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACtC,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,MAAc,EAAE,OAAgB;IAC9E,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACtD,IAAI,QAAQ,IAAI,QAAQ,KAAK,GAAG;QAAE,OAAO,GAAG,MAAM,IAAI,MAAM,MAAM,QAAQ,EAAE,CAAA;IAC5E,OAAO,GAAG,MAAM,IAAI,MAAM,MAAM,OAAO,CAAC,OAAO,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;AAC3E,CAAC;AAgBD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAuB,EAAE,OAAsB;IAC3E,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,EAAE,UAAU,CAAC,cAAc;WAC5E,uBAAuB,CAAA;IAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAEvD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACvD,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IACjE,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,EAAE,UAAU,CAAC,gBAAgB,CAAA;IACjD,IAAI,KAAK,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxC,yEAAyE;QACzE,mEAAmE;QACnE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IACxF,CAAC;IACD,MAAM,IAAI,sBAAsB,EAAE,CAAA;AACpC,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,cAAc,CAC5B,QAAmB,EAAE,MAAc,EAAE,MAAc;IAEnD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,MAAM,GAAc,EAAE,CAAA;IAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QACzD,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAQ;QAChC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAClB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA;AACpE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,KAG9B;IACC,IAAI,KAAK,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IACvC,6EAA6E;IAC7E,8EAA8E;IAC9E,4EAA4E;IAC5E,2EAA2E;IAC3E,uEAAuE;IACvE,yEAAyE;IACzE,2EAA2E;IAC3E,qCAAqC;IACrC,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAA;IACnF,IAAI,cAAc;QAAE,OAAO,SAAS,CAAA;IACpC,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,YAAY,CAAA;IACtC,2EAA2E;IAC3E,8CAA8C;IAC9C,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,YAAY,CAAA;IAChD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAgB9B;IACC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EACjF,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;IAC9B,2EAA2E;IAC3E,gEAAgE;IAChE,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;IAExE,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAA;IACnC,IAAI,WAAW,GAAkB,SAAS,CAAA;IAC1C,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,wEAAwE;QACxE,oEAAoE;QACpE,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC3F,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACnD,WAAW,GAAG,SAAS,IAAI,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1E,CAAC;IAED,OAAO;QACL,GAAG,KAAK;QACR,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW;QACX,SAAS,EAAE,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,UAAU,EAAE;YACV,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,UAAU,CAAC,gBAAgB;YAC7E,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;SAC5C;QACD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;QACrC,YAAY,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;QACvC,MAAM,EAAE,KAAK,CAAC,MAAM;YAClB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;gBAC1D,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE;YAC5D,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;QACnD,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,yEAAyE;QACzE,uCAAuC;QACvC,WAAW,EAAE,KAAK;QAClB,iBAAiB,EAAE,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK;QACjF,SAAS,EAAE,KAAK;KACjB,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACrC,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IAC3C,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACnF,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAC7C,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,CAAC,aAAa,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,aAAa,GAAG,EAAE,CAAC,EAAE,CAAA;AAC9E,CAAC;AAyCD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,MAAc,EAAE,OAAuB;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAA;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAA;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,cAAc,CAAA;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAE5C,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,IAAI,MAAM,CAAC,IAAI;QAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3C,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,GAAG,mBAAmB,EAAE,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IAC3D,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAEhF,6EAA6E;IAC7E,yEAAyE;IACzE,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,KAAK,IAAI;QACnC,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAK,MAAM,CAAC,IAAe,CAAC,CAAA;IAEvE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM;QACtC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,IAAI,CAAA;IACR,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM;QACtC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,IAAI,CAAA;IAER,MAAM,WAAW,GAAG,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAA;IAC9D,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IACrE,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;IAC3D,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,QAAQ,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;iBAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa;gBAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;YACvF,wEAAwE;YACxE,sEAAsE;YACtE,+DAA+D;iBAC1D,IAAI,IAAI,CAAC,MAAM;gBAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;YACjF,uEAAuE;YACvE,kDAAkD;iBAC7C,IAAI,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;gBACrC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACtF,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC;QAC1B,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;QACpC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;QACjC,mBAAmB,EAAE,cAAc;QACnC,eAAe,EAAE,QAAQ,CAAC,MAAM;QAChC,gBAAgB,EAAE,gBAAgB;QAClC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ;QACpD,yEAAyE;QACzE,gEAAgE;QAChE,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,QAAQ,KAAK,SAAS,CAAA;IACtC,IAAI,KAAK,GAAkB,IAAI,CAAA;IAC/B,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAC5B,KAAK,GAAG,SAAS,EAAE,CAAA;QACnB,MAAM,GAAG,GAAc;YACrB,MAAM,EAAE,eAAe;YACvB,KAAK;YACL,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;YACxC,SAAS,EAAE,IAAI,CAAC,WAAqB;YACrC,SAAS,EAAE,IAAI,CAAC,WAAqB;YACrC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;YAC5C,QAAQ,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YACrE,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE;gBACN,OAAO,EAAE,QAAQ,CAAC,MAAM;gBACxB,mBAAmB,EAAE,cAAc;gBACnC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;gBACtC,QAAQ;aACT;YACD,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI;SACvC,CAAA;QACD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACjB,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IACrB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC,OAAO;QACjB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;QAC1C,MAAM,EAAE,eAAe;QACvB,KAAK;QACL,MAAM;QACN,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;QACjC,mBAAmB,EAAE,cAAc;QACnC,eAAe,EAAE,QAAQ,CAAC,MAAM;QAChC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ;QACR,WAAW;QACX,OAAO;QACP,QAAQ;QACR,KAAK;QACL,KAAK,EAAE,IAAI;KACZ,CAAA;AACH,CAAC"}
@@ -0,0 +1,113 @@
1
+ export declare const SYNC_SCHEMA = "weflow-sync/v1";
2
+ export declare const JOB_SCHEMA = "weflow-job/v1";
3
+ export declare const SYNC_SOURCE = "wechat-nt";
4
+ export declare const DEFAULT_OVERLAP_SECONDS = 300;
5
+ /** 单次运行的结局。partial 是本项目新增的一档——部分完成不算成功。 */
6
+ export type JobState = 'planned' | 'waiting_confirmation' | 'running' | 'paused' | 'completed' | 'partial' | 'failed' | 'cancelled';
7
+ /**
8
+ * 覆盖可信度。这是对「有没有读全」的正面回答, 不是内部细节。
9
+ *
10
+ * - complete 扫到的分片全部打开、打开的全读成功, 且没有更多
11
+ * - unverified 有分片打不开, 但打开的都读成功——已读部分可信, 未打开的分片内容无法判断
12
+ * - partial 已打开的分片读失败, 或还有更多, 或分页提前结束
13
+ */
14
+ export type SyncCoverage = 'complete' | 'unverified' | 'partial';
15
+ export interface SyncShardItem {
16
+ /** 仅 basename——绝不落绝对路径 */
17
+ name: string;
18
+ opened: boolean;
19
+ hasTalkerTable: boolean | null;
20
+ rowsForTalker: number | null;
21
+ /** null | KEY_REJECTED | OPEN_FAILED | READ_FAILED */
22
+ reason: string | null;
23
+ }
24
+ export interface SyncState {
25
+ schema: string;
26
+ schemaVersion: number;
27
+ source: string;
28
+ scope: string;
29
+ talker: string;
30
+ lastSuccessfulRun: string | null;
31
+ lastAttempt: string | null;
32
+ coveredFrom: string | null;
33
+ coveredTo: string | null;
34
+ checkpoint: {
35
+ newestCreateTime: number | null;
36
+ overlapSeconds: number;
37
+ };
38
+ recordsRead: number;
39
+ recordsDeduplicated: number;
40
+ recordsReturned: number;
41
+ shardsRead: number;
42
+ shardsFailed: number;
43
+ shards: {
44
+ scanned: number;
45
+ opened: number;
46
+ failed: number;
47
+ items: SyncShardItem[];
48
+ };
49
+ coverage: SyncCoverage;
50
+ mayHaveMore: boolean;
51
+ warnings: string[];
52
+ updatedAt: string | null;
53
+ }
54
+ export interface JobRecord {
55
+ schema: string;
56
+ jobId: string;
57
+ kind: string;
58
+ state: JobState;
59
+ createdAt: string;
60
+ updatedAt: string;
61
+ window: {
62
+ from: number | null;
63
+ to: number | null;
64
+ } | null;
65
+ progress: {
66
+ completed: number;
67
+ total: number;
68
+ } | null;
69
+ resumeable: boolean;
70
+ result: Record<string, unknown> | null;
71
+ error: string | null;
72
+ }
73
+ /** 未知 schema 时不覆盖原文件, 交由调用方决定如何处理。 */
74
+ export declare class UnknownSchemaError extends Error {
75
+ readonly path: string;
76
+ readonly found: unknown;
77
+ constructor(path: string, found: unknown);
78
+ }
79
+ /**
80
+ * 递归剔除不该落盘的字段。
81
+ *
82
+ * 状态文件是本机产物, 但一旦被日志、issue 或截图带出去就是泄露, 所以在写入
83
+ * 这一层兜底, 而不是指望每个调用方都记得。
84
+ */
85
+ export declare function sanitizeForState(value: unknown): unknown;
86
+ /**
87
+ * 文件名里只放一个可读前缀和一个 hash。
88
+ *
89
+ * hash 后缀让 `wxid_a@chatroom` 与 `wxid_a_chatroom` 落到不同文件, 而显示名
90
+ * 不会出现在目录列表里。
91
+ */
92
+ export declare function talkerSlug(talker: string): string;
93
+ export declare function emptySyncState(talker: string, scope?: string, source?: string, overlapSeconds?: number): SyncState;
94
+ export declare class SyncStateStore {
95
+ private readonly syncDir;
96
+ private readonly jobsDir;
97
+ constructor(syncDir?: string, jobsDir?: string);
98
+ statePath(source: string, talker: string): string;
99
+ /** 读取检查点。无记录返回 null; schema 不认识则抛错, 不猜测。 */
100
+ read(source: string, talker: string): SyncState | null;
101
+ write(state: SyncState): void;
102
+ /** 所有会话的检查点, 最近尝试在前。目录缺失或单个文件损坏都不影响其余。 */
103
+ list(): SyncState[];
104
+ jobPath(jobId: string): string;
105
+ writeJob(job: JobRecord): void;
106
+ readJob(jobId: string): JobRecord | null;
107
+ /** 最近的 job 记录, 最新在前。 */
108
+ recentJobs(limit?: number): JobRecord[];
109
+ }
110
+ export declare const syncStateStore: SyncStateStore;
111
+ /** jobId 形如 job_20260919_0001。同日按时间戳后四位递增, 保证可排序。 */
112
+ export declare function makeJobId(now?: Date): string;
113
+ //# sourceMappingURL=syncState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.d.ts","sourceRoot":"","sources":["../../../src/services/syncState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * 同步检查点与任务记录的本地状态。
3
+ *
4
+ * 两个位置, 生命周期不同 (见 docs/SYNC_CONTRACT.md):
5
+ * ~/.weflow-cli/sync/<source>-<name>-<hash>.json 跨运行的检查点, 每会话一份
6
+ * ~/.weflow-cli/jobs/<jobId>.json 单次运行的记录
7
+ *
8
+ * 按会话分文件而不是一个全局文件: 全局文件要求跨会话读改写并合并, 分文件则每次
9
+ * 写入都是整文件原子替换 (沿用 assistantMemory.ts 的 tmp + rename 做法)。
10
+ *
11
+ * 两条本模块特有的规矩:
12
+ * - 遇到不认识的 schema 一律拒绝覆盖并报错, 不猜测字段含义;
13
+ * - 写盘前统一过一遍脱敏, 保证密钥/绝对路径不会进状态文件 (D-001 / D-002 / D-025)。
14
+ */
15
+ import { createHash } from 'crypto';
16
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'fs';
17
+ import { join } from 'path';
18
+ import os from 'os';
19
+ export const SYNC_SCHEMA = 'weflow-sync/v1';
20
+ export const JOB_SCHEMA = 'weflow-job/v1';
21
+ export const SYNC_SOURCE = 'wechat-nt';
22
+ export const DEFAULT_OVERLAP_SECONDS = 300;
23
+ /** 未知 schema 时不覆盖原文件, 交由调用方决定如何处理。 */
24
+ export class UnknownSchemaError extends Error {
25
+ path;
26
+ found;
27
+ constructor(path, found) {
28
+ super(`不认识的 schema: ${String(found)} (${path})`);
29
+ this.path = path;
30
+ this.found = found;
31
+ this.name = 'UnknownSchemaError';
32
+ }
33
+ }
34
+ const CONFIG_DIR = join(os.homedir(), '.weflow-cli');
35
+ const SYNC_DIR = join(CONFIG_DIR, 'sync');
36
+ const JOBS_DIR = join(CONFIG_DIR, 'jobs');
37
+ const FORBIDDEN_KEYS = /^(ntKey|ntSalt|decryptKey|favKey|favPassphrase|contactKey|snsKey|wechatOcToken|apiKey|password|auth)$/i;
38
+ /** 密钥形状的十六进制串 (>=32 hex)。 */
39
+ const HEX_KEY = /\b[0-9a-f]{32,}\b/gi;
40
+ /** Windows 盘符、UNC、以及 POSIX 绝对路径。三类都要, 早期只处理了第一类。 */
41
+ const ABS_PATH = /(?:[A-Za-z]:[\\/][^\s"']*|\\\\[^\s"']+|\/(?:Users|home|root|var|tmp|opt|mnt)\/[^\s"']*)/g;
42
+ /**
43
+ * 递归剔除不该落盘的字段。
44
+ *
45
+ * 状态文件是本机产物, 但一旦被日志、issue 或截图带出去就是泄露, 所以在写入
46
+ * 这一层兜底, 而不是指望每个调用方都记得。
47
+ */
48
+ export function sanitizeForState(value) {
49
+ if (typeof value === 'string') {
50
+ return value.replace(HEX_KEY, '<redacted>').replace(ABS_PATH, '<path>');
51
+ }
52
+ if (Array.isArray(value))
53
+ return value.map(sanitizeForState);
54
+ if (value && typeof value === 'object') {
55
+ const out = {};
56
+ for (const [key, item] of Object.entries(value)) {
57
+ if (FORBIDDEN_KEYS.test(key))
58
+ continue;
59
+ out[key] = sanitizeForState(item);
60
+ }
61
+ return out;
62
+ }
63
+ return value;
64
+ }
65
+ /**
66
+ * 文件名里只放一个可读前缀和一个 hash。
67
+ *
68
+ * hash 后缀让 `wxid_a@chatroom` 与 `wxid_a_chatroom` 落到不同文件, 而显示名
69
+ * 不会出现在目录列表里。
70
+ */
71
+ export function talkerSlug(talker) {
72
+ const readable = talker.replace(/[^A-Za-z0-9._-]+/g, '_').slice(0, 60);
73
+ const digest = createHash('sha256').update(talker).digest('hex').slice(0, 8);
74
+ return `${readable}-${digest}`;
75
+ }
76
+ function writeAtomic(path, payload) {
77
+ if (!existsSync(CONFIG_DIR))
78
+ mkdirSync(CONFIG_DIR, { recursive: true });
79
+ const tmp = `${path}.tmp`;
80
+ writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf8');
81
+ renameSync(tmp, path);
82
+ }
83
+ function readJson(path) {
84
+ try {
85
+ return JSON.parse(readFileSync(path, 'utf8'));
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ export function emptySyncState(talker, scope = talker, source = SYNC_SOURCE, overlapSeconds = DEFAULT_OVERLAP_SECONDS) {
92
+ return {
93
+ schema: SYNC_SCHEMA,
94
+ schemaVersion: 1,
95
+ source,
96
+ scope,
97
+ talker,
98
+ lastSuccessfulRun: null,
99
+ lastAttempt: null,
100
+ coveredFrom: null,
101
+ coveredTo: null,
102
+ checkpoint: { newestCreateTime: null, overlapSeconds },
103
+ recordsRead: 0,
104
+ recordsDeduplicated: 0,
105
+ recordsReturned: 0,
106
+ shardsRead: 0,
107
+ shardsFailed: 0,
108
+ shards: { scanned: 0, opened: 0, failed: 0, items: [] },
109
+ coverage: 'unverified',
110
+ mayHaveMore: false,
111
+ warnings: [],
112
+ updatedAt: null,
113
+ };
114
+ }
115
+ export class SyncStateStore {
116
+ syncDir;
117
+ jobsDir;
118
+ constructor(syncDir = SYNC_DIR, jobsDir = JOBS_DIR) {
119
+ this.syncDir = syncDir;
120
+ this.jobsDir = jobsDir;
121
+ }
122
+ statePath(source, talker) {
123
+ return join(this.syncDir, `${source}-${talkerSlug(talker)}.json`);
124
+ }
125
+ /** 读取检查点。无记录返回 null; schema 不认识则抛错, 不猜测。 */
126
+ read(source, talker) {
127
+ const path = this.statePath(source, talker);
128
+ const raw = readJson(path);
129
+ if (raw === null)
130
+ return null;
131
+ const state = raw;
132
+ if (state.schema !== SYNC_SCHEMA)
133
+ throw new UnknownSchemaError(path, state.schema);
134
+ return state;
135
+ }
136
+ write(state) {
137
+ const path = this.statePath(state.source, state.talker);
138
+ if (!existsSync(this.syncDir))
139
+ mkdirSync(this.syncDir, { recursive: true });
140
+ writeAtomic(path, sanitizeForState({ ...state, schema: SYNC_SCHEMA }));
141
+ }
142
+ /** 所有会话的检查点, 最近尝试在前。目录缺失或单个文件损坏都不影响其余。 */
143
+ list() {
144
+ try {
145
+ if (!existsSync(this.syncDir))
146
+ return [];
147
+ const states = [];
148
+ for (const name of readdirSync(this.syncDir)) {
149
+ if (!name.endsWith('.json'))
150
+ continue;
151
+ const raw = readJson(join(this.syncDir, name));
152
+ if (!raw || raw.schema !== SYNC_SCHEMA)
153
+ continue;
154
+ states.push(raw);
155
+ }
156
+ return states.sort((a, b) => String(b.lastAttempt ?? '').localeCompare(String(a.lastAttempt ?? '')));
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ }
162
+ jobPath(jobId) {
163
+ return join(this.jobsDir, `${jobId}.json`);
164
+ }
165
+ writeJob(job) {
166
+ if (!existsSync(this.jobsDir))
167
+ mkdirSync(this.jobsDir, { recursive: true });
168
+ writeAtomic(this.jobPath(job.jobId), sanitizeForState({ ...job, schema: JOB_SCHEMA }));
169
+ }
170
+ readJob(jobId) {
171
+ const path = this.jobPath(jobId);
172
+ const raw = readJson(path);
173
+ if (raw === null)
174
+ return null;
175
+ const job = raw;
176
+ if (job.schema !== JOB_SCHEMA)
177
+ throw new UnknownSchemaError(path, job.schema);
178
+ return job;
179
+ }
180
+ /** 最近的 job 记录, 最新在前。 */
181
+ recentJobs(limit = 10) {
182
+ try {
183
+ if (!existsSync(this.jobsDir))
184
+ return [];
185
+ const ids = readdirSync(this.jobsDir)
186
+ .filter(name => name.endsWith('.json'))
187
+ .map(name => name.slice(0, -'.json'.length))
188
+ .sort()
189
+ .reverse()
190
+ .slice(0, limit);
191
+ const jobs = [];
192
+ for (const id of ids) {
193
+ const job = this.readJob(id);
194
+ if (job)
195
+ jobs.push(job);
196
+ }
197
+ return jobs;
198
+ }
199
+ catch {
200
+ return [];
201
+ }
202
+ }
203
+ }
204
+ export const syncStateStore = new SyncStateStore();
205
+ /** jobId 形如 job_20260919_0001。同日按时间戳后四位递增, 保证可排序。 */
206
+ export function makeJobId(now = new Date()) {
207
+ const stamp = [
208
+ now.getFullYear(),
209
+ String(now.getMonth() + 1).padStart(2, '0'),
210
+ String(now.getDate()).padStart(2, '0'),
211
+ ].join('');
212
+ return `job_${stamp}_${String(now.getTime() % 10000).padStart(4, '0')}`;
213
+ }
214
+ //# sourceMappingURL=syncState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.js","sourceRoot":"","sources":["../../../src/services/syncState.ts"],"names":[],"mappings":"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"}