opencode-oncall 0.1.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 (105) hide show
  1. package/LICENSE +151 -0
  2. package/README.md +50 -0
  3. package/dist/common-settings-actions.d.ts +15 -0
  4. package/dist/common-settings-actions.js +48 -0
  5. package/dist/common-settings-store.d.ts +1 -0
  6. package/dist/common-settings-store.js +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/plugin-hooks.d.ts +51 -0
  10. package/dist/plugin-hooks.js +288 -0
  11. package/dist/plugin.d.ts +10 -0
  12. package/dist/plugin.js +115 -0
  13. package/dist/settings-store.d.ts +50 -0
  14. package/dist/settings-store.js +214 -0
  15. package/dist/store-paths.d.ts +16 -0
  16. package/dist/store-paths.js +61 -0
  17. package/dist/ui/wechat-menu.d.ts +26 -0
  18. package/dist/ui/wechat-menu.js +90 -0
  19. package/dist/wechat/bind-flow.d.ts +29 -0
  20. package/dist/wechat/bind-flow.js +207 -0
  21. package/dist/wechat/bridge.d.ts +136 -0
  22. package/dist/wechat/bridge.js +1059 -0
  23. package/dist/wechat/broker-client.d.ts +23 -0
  24. package/dist/wechat/broker-client.js +274 -0
  25. package/dist/wechat/broker-endpoint.d.ts +21 -0
  26. package/dist/wechat/broker-endpoint.js +78 -0
  27. package/dist/wechat/broker-entry.d.ts +123 -0
  28. package/dist/wechat/broker-entry.js +1321 -0
  29. package/dist/wechat/broker-launcher.d.ts +37 -0
  30. package/dist/wechat/broker-launcher.js +418 -0
  31. package/dist/wechat/broker-mutation-queue.d.ts +93 -0
  32. package/dist/wechat/broker-mutation-queue.js +126 -0
  33. package/dist/wechat/broker-server.d.ts +86 -0
  34. package/dist/wechat/broker-server.js +1340 -0
  35. package/dist/wechat/broker-state-store.d.ts +335 -0
  36. package/dist/wechat/broker-state-store.js +1964 -0
  37. package/dist/wechat/command-parser.d.ts +18 -0
  38. package/dist/wechat/command-parser.js +58 -0
  39. package/dist/wechat/compat/jiti-loader.d.ts +27 -0
  40. package/dist/wechat/compat/jiti-loader.js +118 -0
  41. package/dist/wechat/compat/openclaw-account-helpers.d.ts +29 -0
  42. package/dist/wechat/compat/openclaw-account-helpers.js +60 -0
  43. package/dist/wechat/compat/openclaw-bind-helpers.d.ts +29 -0
  44. package/dist/wechat/compat/openclaw-bind-helpers.js +169 -0
  45. package/dist/wechat/compat/openclaw-guided-smoke.d.ts +180 -0
  46. package/dist/wechat/compat/openclaw-guided-smoke.js +1134 -0
  47. package/dist/wechat/compat/openclaw-public-entry.d.ts +33 -0
  48. package/dist/wechat/compat/openclaw-public-entry.js +62 -0
  49. package/dist/wechat/compat/openclaw-public-helpers.d.ts +70 -0
  50. package/dist/wechat/compat/openclaw-public-helpers.js +68 -0
  51. package/dist/wechat/compat/openclaw-qr-gateway.d.ts +15 -0
  52. package/dist/wechat/compat/openclaw-qr-gateway.js +39 -0
  53. package/dist/wechat/compat/openclaw-smoke.d.ts +48 -0
  54. package/dist/wechat/compat/openclaw-smoke.js +100 -0
  55. package/dist/wechat/compat/openclaw-sync-buf.d.ts +24 -0
  56. package/dist/wechat/compat/openclaw-sync-buf.js +80 -0
  57. package/dist/wechat/compat/openclaw-updates-send.d.ts +47 -0
  58. package/dist/wechat/compat/openclaw-updates-send.js +38 -0
  59. package/dist/wechat/compat/qrcode-terminal-loader.d.ts +12 -0
  60. package/dist/wechat/compat/qrcode-terminal-loader.js +16 -0
  61. package/dist/wechat/compat/slash-guard.d.ts +11 -0
  62. package/dist/wechat/compat/slash-guard.js +24 -0
  63. package/dist/wechat/dead-letter-store.d.ts +48 -0
  64. package/dist/wechat/dead-letter-store.js +224 -0
  65. package/dist/wechat/debug-bundle-collector.d.ts +49 -0
  66. package/dist/wechat/debug-bundle-collector.js +580 -0
  67. package/dist/wechat/debug-bundle-flow.d.ts +37 -0
  68. package/dist/wechat/debug-bundle-flow.js +180 -0
  69. package/dist/wechat/debug-bundle-redaction.d.ts +14 -0
  70. package/dist/wechat/debug-bundle-redaction.js +339 -0
  71. package/dist/wechat/handle.d.ts +10 -0
  72. package/dist/wechat/handle.js +57 -0
  73. package/dist/wechat/ipc-auth.d.ts +6 -0
  74. package/dist/wechat/ipc-auth.js +39 -0
  75. package/dist/wechat/latest-account-state-store.d.ts +8 -0
  76. package/dist/wechat/latest-account-state-store.js +38 -0
  77. package/dist/wechat/notification-dispatcher.d.ts +34 -0
  78. package/dist/wechat/notification-dispatcher.js +266 -0
  79. package/dist/wechat/notification-format.d.ts +15 -0
  80. package/dist/wechat/notification-format.js +196 -0
  81. package/dist/wechat/notification-store.d.ts +72 -0
  82. package/dist/wechat/notification-store.js +807 -0
  83. package/dist/wechat/notification-types.d.ts +37 -0
  84. package/dist/wechat/notification-types.js +1 -0
  85. package/dist/wechat/openclaw-account-adapter.d.ts +30 -0
  86. package/dist/wechat/openclaw-account-adapter.js +60 -0
  87. package/dist/wechat/operator-store.d.ts +9 -0
  88. package/dist/wechat/operator-store.js +69 -0
  89. package/dist/wechat/protocol.d.ts +150 -0
  90. package/dist/wechat/protocol.js +197 -0
  91. package/dist/wechat/question-interaction.d.ts +24 -0
  92. package/dist/wechat/question-interaction.js +180 -0
  93. package/dist/wechat/request-store.d.ts +108 -0
  94. package/dist/wechat/request-store.js +669 -0
  95. package/dist/wechat/session-digest.d.ts +50 -0
  96. package/dist/wechat/session-digest.js +167 -0
  97. package/dist/wechat/state-paths.d.ts +26 -0
  98. package/dist/wechat/state-paths.js +92 -0
  99. package/dist/wechat/status-format.d.ts +26 -0
  100. package/dist/wechat/status-format.js +616 -0
  101. package/dist/wechat/token-store.d.ts +20 -0
  102. package/dist/wechat/token-store.js +193 -0
  103. package/dist/wechat/wechat-status-runtime.d.ts +89 -0
  104. package/dist/wechat/wechat-status-runtime.js +518 -0
  105. package/package.json +74 -0
@@ -0,0 +1,580 @@
1
+ import path from "node:path";
2
+ import { execFile } from "node:child_process";
3
+ import { readdir, readFile, stat } from "node:fs/promises";
4
+ import { REDACTED_ACCOUNT_ID, REDACTED_USER_ID, redactDebugBundleContent, } from "./debug-bundle-redaction.js";
5
+ import { wechatStateRoot } from "./state-paths.js";
6
+ const LARGE_IRRELEVANT_FILE_BYTES = 256 * 1024;
7
+ const LARGE_FILE_SAFE_EXTENSIONS = new Set([".json", ".jsonl", ".log", ".txt"]);
8
+ const REDACTED_CWD = "[REDACTED_CWD]";
9
+ const REDACTED_STATE_ROOT = "[REDACTED_STATE_ROOT]";
10
+ const STATE_FILE_TARGETS = [
11
+ { relativePath: "broker.json" },
12
+ { relativePath: "operator.json" },
13
+ { relativePath: "latest-account.json" },
14
+ ];
15
+ const STATE_DIRECTORY_TARGETS = [
16
+ { relativePath: "notifications" },
17
+ { relativePath: "requests" },
18
+ { relativePath: "dead-letter" },
19
+ { relativePath: "instances" },
20
+ ];
21
+ const DIAGNOSTIC_FILE_TARGETS = [
22
+ { relativePath: "wechat-status-runtime.diagnostics.jsonl" },
23
+ { relativePath: "wechat-broker.diagnostics.jsonl" },
24
+ { relativePath: "wechat-bridge.diagnostics.jsonl" },
25
+ { relativePath: "broker-startup.diagnostics.log" },
26
+ ];
27
+ let packageVersionPromise = null;
28
+ export async function collectWechatDebugBundle(options) {
29
+ const stateRoot = options.stateRoot ?? wechatStateRoot();
30
+ const stateRootStat = await safeStat(stateRoot);
31
+ if (!stateRootStat?.isDirectory()) {
32
+ throw new Error("微信状态目录不存在,无法导出调试包");
33
+ }
34
+ const mode = options.mode;
35
+ const visiblePathBuilder = createVisiblePathBuilder(mode, stateRoot);
36
+ const exportedAt = (options.now ?? new Date()).toISOString();
37
+ const cwd = options.cwd ?? process.cwd();
38
+ const gitHead = options.gitHead === undefined ? await detectGitHead(cwd) : options.gitHead;
39
+ const pluginVersion = options.pluginVersion ?? (await readPackageVersion());
40
+ const nodeVersion = options.nodeVersion ?? process.version;
41
+ const platform = options.platform ?? process.platform;
42
+ const environmentSummary = await buildEnvironmentSummary({
43
+ cwd,
44
+ gitHead,
45
+ mode,
46
+ nodeVersion,
47
+ platform,
48
+ pluginVersion,
49
+ stateRoot,
50
+ });
51
+ const entries = [];
52
+ const manifestEntries = [];
53
+ const missingPaths = [];
54
+ const skippedEntries = [];
55
+ for (const target of STATE_FILE_TARGETS) {
56
+ await collectFileTarget({
57
+ category: "state",
58
+ entries,
59
+ manifestEntries,
60
+ missingPaths,
61
+ mode,
62
+ relativePath: target.relativePath,
63
+ skippedEntries,
64
+ visiblePathBuilder,
65
+ sourcePath: path.join(stateRoot, target.relativePath),
66
+ });
67
+ }
68
+ await collectTokenDirectoryTarget({
69
+ category: "state",
70
+ entries,
71
+ manifestEntries,
72
+ missingPaths,
73
+ mode,
74
+ relativePath: "tokens",
75
+ skippedEntries,
76
+ visiblePathBuilder,
77
+ sourcePath: path.join(stateRoot, "tokens"),
78
+ });
79
+ for (const target of STATE_DIRECTORY_TARGETS) {
80
+ await collectDirectoryTarget({
81
+ category: "state",
82
+ entries,
83
+ manifestEntries,
84
+ missingPaths,
85
+ mode,
86
+ relativePath: target.relativePath,
87
+ skippedEntries,
88
+ visiblePathBuilder,
89
+ sourcePath: path.join(stateRoot, target.relativePath),
90
+ });
91
+ }
92
+ for (const target of DIAGNOSTIC_FILE_TARGETS) {
93
+ await collectFileTarget({
94
+ category: "diagnostics",
95
+ entries,
96
+ manifestEntries,
97
+ missingPaths,
98
+ mode,
99
+ relativePath: target.relativePath,
100
+ skippedEntries,
101
+ visiblePathBuilder,
102
+ sourcePath: path.join(stateRoot, target.relativePath),
103
+ });
104
+ }
105
+ const environmentEntry = createBundleEntry({
106
+ bundlePath: "environment-summary.json",
107
+ category: "metadata",
108
+ content: serializeJson(environmentSummary),
109
+ redacted: false,
110
+ sourcePath: null,
111
+ });
112
+ entries.push(environmentEntry);
113
+ manifestEntries.push(toManifestEntry(environmentEntry));
114
+ const manifestEntryMeta = {
115
+ bundlePath: "manifest.json",
116
+ category: "metadata",
117
+ redacted: false,
118
+ sourcePath: null,
119
+ };
120
+ const manifest = {
121
+ schemaVersion: 1,
122
+ mode,
123
+ exportedAt,
124
+ stateRoot: mode === "sanitized" ? REDACTED_STATE_ROOT : stateRoot,
125
+ missingPaths: sortMissingPaths(missingPaths),
126
+ skippedEntries: sortSkippedEntries(skippedEntries),
127
+ entries: sortManifestEntries([...manifestEntries, manifestEntryMeta]),
128
+ };
129
+ entries.push(createBundleEntry({
130
+ bundlePath: manifestEntryMeta.bundlePath,
131
+ category: manifestEntryMeta.category,
132
+ content: serializeJson(manifest),
133
+ redacted: false,
134
+ sourcePath: null,
135
+ }));
136
+ return {
137
+ mode,
138
+ stateRoot,
139
+ entries: sortBundleEntries(entries),
140
+ manifest,
141
+ };
142
+ }
143
+ async function collectFileTarget(input) {
144
+ let fileStat;
145
+ try {
146
+ fileStat = await safeStat(input.sourcePath);
147
+ }
148
+ catch {
149
+ input.skippedEntries.push(createSkippedBundleEntry({
150
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.peekVisibleRelativePath(input.relativePath)),
151
+ category: input.category,
152
+ reason: "file-read-failed",
153
+ sourcePath: input.sourcePath,
154
+ redactedSourcePath: input.visiblePathBuilder.peekVisibleSourcePath(input.relativePath),
155
+ }));
156
+ return;
157
+ }
158
+ if (!fileStat?.isFile()) {
159
+ input.missingPaths.push({ category: input.category, relativePath: input.relativePath });
160
+ return;
161
+ }
162
+ const bundleEntry = await loadBundleEntry({
163
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.toVisibleRelativePath(input.relativePath)),
164
+ category: input.category,
165
+ mode: input.mode,
166
+ skippedEntries: input.skippedEntries,
167
+ sourcePath: input.sourcePath,
168
+ statsSize: fileStat.size,
169
+ redactedSourcePath: input.visiblePathBuilder.toVisibleSourcePath(input.relativePath),
170
+ readFailureReason: "file-read-failed",
171
+ });
172
+ if (!bundleEntry) {
173
+ return;
174
+ }
175
+ input.entries.push(bundleEntry);
176
+ input.manifestEntries.push(toManifestEntry(bundleEntry));
177
+ }
178
+ async function collectDirectoryTarget(input) {
179
+ const directoryStat = await safeStat(input.sourcePath);
180
+ if (!directoryStat?.isDirectory()) {
181
+ input.missingPaths.push({ category: input.category, relativePath: input.relativePath });
182
+ return;
183
+ }
184
+ for (const filePath of await walkFiles(input.sourcePath)) {
185
+ const relativeSubPath = toPosixPath(path.relative(input.sourcePath, filePath));
186
+ const relativePath = path.posix.join(input.relativePath, relativeSubPath);
187
+ const fileStat = await safeStat(filePath);
188
+ if (!fileStat?.isFile()) {
189
+ input.skippedEntries.push(createSkippedBundleEntry({
190
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.peekVisibleRelativePath(relativePath)),
191
+ category: input.category,
192
+ reason: "file-disappeared",
193
+ sourcePath: filePath,
194
+ redactedSourcePath: input.visiblePathBuilder.peekVisibleSourcePath(relativePath),
195
+ }));
196
+ continue;
197
+ }
198
+ const bundleEntry = await loadBundleEntry({
199
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.toVisibleRelativePath(relativePath)),
200
+ category: input.category,
201
+ mode: input.mode,
202
+ skippedEntries: input.skippedEntries,
203
+ sourcePath: filePath,
204
+ statsSize: fileStat.size,
205
+ redactedSourcePath: input.visiblePathBuilder.toVisibleSourcePath(relativePath),
206
+ });
207
+ if (!bundleEntry) {
208
+ continue;
209
+ }
210
+ input.entries.push(bundleEntry);
211
+ input.manifestEntries.push(toManifestEntry(bundleEntry));
212
+ }
213
+ }
214
+ async function collectTokenDirectoryTarget(input) {
215
+ let directoryStat;
216
+ try {
217
+ directoryStat = await safeStat(input.sourcePath);
218
+ }
219
+ catch {
220
+ input.skippedEntries.push(createSkippedBundleEntry({
221
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.peekVisibleRelativePath(input.relativePath)),
222
+ category: input.category,
223
+ reason: "file-read-failed",
224
+ sourcePath: input.sourcePath,
225
+ redactedSourcePath: input.visiblePathBuilder.peekVisibleSourcePath(input.relativePath),
226
+ }));
227
+ input.missingPaths.push({ category: input.category, relativePath: input.relativePath });
228
+ return;
229
+ }
230
+ if (!directoryStat?.isDirectory()) {
231
+ input.missingPaths.push({ category: input.category, relativePath: input.relativePath });
232
+ return;
233
+ }
234
+ let hasEligibleToken = false;
235
+ for (const filePath of await walkFiles(input.sourcePath, {
236
+ onDirectoryError: (directoryPath, error) => {
237
+ const relativeSubPath = toPosixPath(path.relative(input.sourcePath, directoryPath));
238
+ const relativePath = relativeSubPath.length > 0 ? path.posix.join(input.relativePath, relativeSubPath) : input.relativePath;
239
+ input.skippedEntries.push(createSkippedBundleEntry({
240
+ bundlePath: bundlePathFor(input.category, input.visiblePathBuilder.toVisibleRelativePath(relativePath)),
241
+ category: input.category,
242
+ reason: isMissingError(error) ? "file-disappeared" : "file-read-failed",
243
+ sourcePath: directoryPath,
244
+ redactedSourcePath: input.visiblePathBuilder.toVisibleSourcePath(relativePath),
245
+ }));
246
+ },
247
+ })) {
248
+ const relativeSubPath = toPosixPath(path.relative(input.sourcePath, filePath));
249
+ const relativePath = path.posix.join(input.relativePath, relativeSubPath);
250
+ const isTempFile = isTemporaryTokenFile(filePath);
251
+ const isJsonFile = path.extname(filePath).toLowerCase() === ".json";
252
+ if (!isTempFile && !isJsonFile) {
253
+ continue;
254
+ }
255
+ const bundlePath = bundlePathFor(input.category, input.visiblePathBuilder.toVisibleRelativePath(relativePath));
256
+ const redactedSourcePath = input.visiblePathBuilder.toVisibleSourcePath(relativePath);
257
+ let fileStat;
258
+ try {
259
+ fileStat = await safeStat(filePath);
260
+ }
261
+ catch {
262
+ input.skippedEntries.push(createSkippedBundleEntry({
263
+ bundlePath,
264
+ category: input.category,
265
+ reason: "file-read-failed",
266
+ sourcePath: filePath,
267
+ redactedSourcePath,
268
+ }));
269
+ continue;
270
+ }
271
+ if (!fileStat?.isFile()) {
272
+ input.skippedEntries.push(createSkippedBundleEntry({
273
+ bundlePath,
274
+ category: input.category,
275
+ reason: "file-disappeared",
276
+ sourcePath: filePath,
277
+ redactedSourcePath,
278
+ }));
279
+ continue;
280
+ }
281
+ if (isTempFile) {
282
+ input.skippedEntries.push(createSkippedBundleEntry({
283
+ bundlePath,
284
+ category: input.category,
285
+ reason: "token-temp-file",
286
+ sourcePath: filePath,
287
+ redactedSourcePath,
288
+ }));
289
+ continue;
290
+ }
291
+ if (fileStat.size === 0) {
292
+ input.skippedEntries.push(createSkippedBundleEntry({
293
+ bundlePath,
294
+ category: input.category,
295
+ reason: "empty-token-file",
296
+ sourcePath: filePath,
297
+ redactedSourcePath,
298
+ }));
299
+ continue;
300
+ }
301
+ const bundleEntry = await loadBundleEntry({
302
+ bundlePath,
303
+ category: input.category,
304
+ mode: input.mode,
305
+ skippedEntries: input.skippedEntries,
306
+ sourcePath: filePath,
307
+ statsSize: fileStat.size,
308
+ redactedSourcePath,
309
+ readFailureReason: "file-read-failed",
310
+ });
311
+ if (!bundleEntry) {
312
+ continue;
313
+ }
314
+ hasEligibleToken = true;
315
+ input.entries.push(bundleEntry);
316
+ input.manifestEntries.push(toManifestEntry(bundleEntry));
317
+ }
318
+ if (!hasEligibleToken) {
319
+ input.missingPaths.push({ category: input.category, relativePath: input.relativePath });
320
+ }
321
+ }
322
+ async function loadBundleEntry(input) {
323
+ const normalizedRedactedSourcePath = input.redactedSourcePath !== input.sourcePath ? input.redactedSourcePath : null;
324
+ if (shouldSkipLargeIrrelevantFile(input.sourcePath, input.statsSize)) {
325
+ input.skippedEntries.push(createSkippedBundleEntry({
326
+ bundlePath: input.bundlePath,
327
+ category: input.category,
328
+ reason: "file-too-large",
329
+ sourcePath: input.sourcePath,
330
+ redactedSourcePath: input.redactedSourcePath,
331
+ }));
332
+ return null;
333
+ }
334
+ let content;
335
+ try {
336
+ content = await readFile(input.sourcePath);
337
+ }
338
+ catch (error) {
339
+ if (isMissingError(error)) {
340
+ input.skippedEntries.push(createSkippedBundleEntry({
341
+ bundlePath: input.bundlePath,
342
+ category: input.category,
343
+ reason: "file-disappeared",
344
+ sourcePath: input.sourcePath,
345
+ redactedSourcePath: input.redactedSourcePath,
346
+ }));
347
+ return null;
348
+ }
349
+ if (input.readFailureReason) {
350
+ input.skippedEntries.push(createSkippedBundleEntry({
351
+ bundlePath: input.bundlePath,
352
+ category: input.category,
353
+ reason: input.readFailureReason,
354
+ sourcePath: input.sourcePath,
355
+ redactedSourcePath: input.redactedSourcePath,
356
+ }));
357
+ return null;
358
+ }
359
+ throw error;
360
+ }
361
+ const redacted = input.mode === "sanitized";
362
+ return createBundleEntry({
363
+ bundlePath: input.bundlePath,
364
+ category: input.category,
365
+ content: redacted ? redactDebugBundleContent(content, { bundlePath: input.bundlePath, mode: input.mode }) : content,
366
+ redacted,
367
+ sourcePath: input.sourcePath,
368
+ redactedSourcePath: normalizedRedactedSourcePath,
369
+ });
370
+ }
371
+ async function buildEnvironmentSummary(input) {
372
+ const checks = {
373
+ "broker.json": await pathExists(path.join(input.stateRoot, "broker.json"), { failSoft: true }),
374
+ tokens: await pathExists(path.join(input.stateRoot, "tokens"), { failSoft: true }),
375
+ notifications: await pathExists(path.join(input.stateRoot, "notifications")),
376
+ requests: await pathExists(path.join(input.stateRoot, "requests")),
377
+ "dead-letter": await pathExists(path.join(input.stateRoot, "dead-letter")),
378
+ instances: await pathExists(path.join(input.stateRoot, "instances")),
379
+ "wechat-status-runtime.diagnostics.jsonl": await pathExists(path.join(input.stateRoot, "wechat-status-runtime.diagnostics.jsonl"), { failSoft: true }),
380
+ "wechat-broker.diagnostics.jsonl": await pathExists(path.join(input.stateRoot, "wechat-broker.diagnostics.jsonl"), {
381
+ failSoft: true,
382
+ }),
383
+ "wechat-bridge.diagnostics.jsonl": await pathExists(path.join(input.stateRoot, "wechat-bridge.diagnostics.jsonl"), {
384
+ failSoft: true,
385
+ }),
386
+ "broker-startup.diagnostics.log": await pathExists(path.join(input.stateRoot, "broker-startup.diagnostics.log"), {
387
+ failSoft: true,
388
+ }),
389
+ };
390
+ return {
391
+ pluginVersion: input.pluginVersion,
392
+ nodeVersion: input.nodeVersion,
393
+ platform: input.platform,
394
+ cwd: input.mode === "sanitized" ? REDACTED_CWD : input.cwd,
395
+ gitHead: input.gitHead,
396
+ mode: input.mode,
397
+ stateRoot: input.mode === "sanitized" ? REDACTED_STATE_ROOT : input.stateRoot,
398
+ stateRootExists: true,
399
+ checks,
400
+ };
401
+ }
402
+ async function walkFiles(rootPath, options = {}) {
403
+ let entries;
404
+ try {
405
+ entries = await readdir(rootPath, { withFileTypes: true });
406
+ }
407
+ catch (error) {
408
+ if (options.onDirectoryError) {
409
+ options.onDirectoryError(rootPath, error);
410
+ return [];
411
+ }
412
+ throw error;
413
+ }
414
+ const sortedEntries = [...entries].sort((left, right) => left.name.localeCompare(right.name));
415
+ const files = [];
416
+ for (const entry of sortedEntries) {
417
+ const entryPath = path.join(rootPath, entry.name);
418
+ if (entry.isDirectory()) {
419
+ files.push(...(await walkFiles(entryPath, options)));
420
+ continue;
421
+ }
422
+ if (entry.isFile()) {
423
+ files.push(entryPath);
424
+ }
425
+ }
426
+ return files;
427
+ }
428
+ function shouldSkipLargeIrrelevantFile(filePath, size) {
429
+ if (size < LARGE_IRRELEVANT_FILE_BYTES) {
430
+ return false;
431
+ }
432
+ return !LARGE_FILE_SAFE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
433
+ }
434
+ function isTemporaryTokenFile(filePath) {
435
+ return path.basename(filePath).endsWith(".tmp");
436
+ }
437
+ function createVisiblePathBuilder(mode, stateRoot) {
438
+ const accountPlaceholders = new Map();
439
+ const userPlaceholders = new Map();
440
+ const visibleRoot = mode === "sanitized" ? REDACTED_STATE_ROOT : stateRoot;
441
+ const mapVisibleRelativePath = (relativePath, accountMap, userMap) => {
442
+ if (mode !== "sanitized") {
443
+ return toPosixPath(relativePath);
444
+ }
445
+ const segments = toPosixPath(relativePath).split("/");
446
+ if (segments[0] !== "tokens" || segments.length < 2) {
447
+ return toPosixPath(relativePath);
448
+ }
449
+ segments[1] = getPlaceholder(accountMap, segments[1], REDACTED_ACCOUNT_ID);
450
+ for (let index = 2; index < segments.length; index += 1) {
451
+ const segment = segments[index];
452
+ const extension = index === segments.length - 1 ? path.posix.extname(segment) : "";
453
+ const userId = extension.length > 0 ? segment.slice(0, -extension.length) : segment;
454
+ segments[index] = `${getPlaceholder(userMap, userId, REDACTED_USER_ID)}${extension}`;
455
+ }
456
+ return segments.join("/");
457
+ };
458
+ return {
459
+ toVisibleRelativePath(relativePath) {
460
+ return mapVisibleRelativePath(relativePath, accountPlaceholders, userPlaceholders);
461
+ },
462
+ peekVisibleRelativePath(relativePath) {
463
+ return mapVisibleRelativePath(relativePath, new Map(accountPlaceholders), new Map(userPlaceholders));
464
+ },
465
+ toVisibleSourcePath(relativePath) {
466
+ return path.join(visibleRoot, ...mapVisibleRelativePath(relativePath, accountPlaceholders, userPlaceholders).split("/"));
467
+ },
468
+ peekVisibleSourcePath(relativePath) {
469
+ return path.join(visibleRoot, ...mapVisibleRelativePath(relativePath, new Map(accountPlaceholders), new Map(userPlaceholders)).split("/"));
470
+ },
471
+ };
472
+ }
473
+ function getPlaceholder(map, value, baseLabel) {
474
+ const existing = map.get(value);
475
+ if (existing) {
476
+ return existing;
477
+ }
478
+ const placeholder = `${baseLabel.slice(0, -1)}_${map.size + 1}]`;
479
+ map.set(value, placeholder);
480
+ return placeholder;
481
+ }
482
+ function createBundleEntry(entry) {
483
+ return {
484
+ bundlePath: entry.bundlePath,
485
+ category: entry.category,
486
+ content: Buffer.from(entry.content),
487
+ redacted: entry.redacted,
488
+ sourcePath: entry.sourcePath,
489
+ ...(entry.redactedSourcePath ? { redactedSourcePath: entry.redactedSourcePath } : {}),
490
+ };
491
+ }
492
+ function createSkippedBundleEntry(entry) {
493
+ const normalizedRedactedSourcePath = entry.redactedSourcePath !== entry.sourcePath ? entry.redactedSourcePath : null;
494
+ return {
495
+ bundlePath: entry.bundlePath,
496
+ category: entry.category,
497
+ reason: entry.reason,
498
+ sourcePath: normalizedRedactedSourcePath ? null : entry.sourcePath,
499
+ ...(normalizedRedactedSourcePath ? { redactedSourcePath: normalizedRedactedSourcePath } : {}),
500
+ };
501
+ }
502
+ function toManifestEntry(entry) {
503
+ return {
504
+ bundlePath: entry.bundlePath,
505
+ category: entry.category,
506
+ redacted: entry.redacted,
507
+ sourcePath: entry.redactedSourcePath ? null : entry.sourcePath,
508
+ ...(entry.redactedSourcePath ? { redactedSourcePath: entry.redactedSourcePath } : {}),
509
+ };
510
+ }
511
+ function bundlePathFor(category, relativePath) {
512
+ return `${category}/${toPosixPath(relativePath)}`;
513
+ }
514
+ function serializeJson(value) {
515
+ return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8");
516
+ }
517
+ function sortBundleEntries(entries) {
518
+ return [...entries].sort((left, right) => left.bundlePath.localeCompare(right.bundlePath));
519
+ }
520
+ function sortManifestEntries(entries) {
521
+ return [...entries].sort((left, right) => left.bundlePath.localeCompare(right.bundlePath));
522
+ }
523
+ function sortMissingPaths(entries) {
524
+ return [...entries].sort((left, right) => {
525
+ if (left.category !== right.category) {
526
+ return left.category.localeCompare(right.category);
527
+ }
528
+ return left.relativePath.localeCompare(right.relativePath);
529
+ });
530
+ }
531
+ function sortSkippedEntries(entries) {
532
+ return [...entries].sort((left, right) => left.bundlePath.localeCompare(right.bundlePath));
533
+ }
534
+ function toPosixPath(filePath) {
535
+ return filePath.replaceAll("\\", "/");
536
+ }
537
+ async function safeStat(filePath) {
538
+ try {
539
+ return await stat(filePath);
540
+ }
541
+ catch (error) {
542
+ if (isMissingError(error)) {
543
+ return null;
544
+ }
545
+ throw error;
546
+ }
547
+ }
548
+ async function pathExists(filePath, options = {}) {
549
+ try {
550
+ return (await safeStat(filePath)) !== null;
551
+ }
552
+ catch (error) {
553
+ if (options.failSoft) {
554
+ return false;
555
+ }
556
+ throw error;
557
+ }
558
+ }
559
+ function isMissingError(error) {
560
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
561
+ }
562
+ async function readPackageVersion() {
563
+ packageVersionPromise ??= readFile(new URL("../../package.json", import.meta.url), "utf8").then((content) => {
564
+ const parsed = JSON.parse(content);
565
+ return typeof parsed.version === "string" ? parsed.version : "unknown";
566
+ });
567
+ return packageVersionPromise;
568
+ }
569
+ async function detectGitHead(cwd) {
570
+ return new Promise((resolve) => {
571
+ execFile("git", ["rev-parse", "HEAD"], { cwd }, (error, stdout) => {
572
+ if (error) {
573
+ resolve(null);
574
+ return;
575
+ }
576
+ const gitHead = stdout.trim();
577
+ resolve(gitHead.length > 0 ? gitHead : null);
578
+ });
579
+ });
580
+ }
@@ -0,0 +1,37 @@
1
+ import { rm, writeFile } from "node:fs/promises";
2
+ import { type CollectWechatDebugBundleOptions } from "./debug-bundle-collector.js";
3
+ import type { WechatDebugBundleMode } from "./debug-bundle-redaction.js";
4
+ export type RunWechatDebugBundleFlowInput = CollectWechatDebugBundleOptions & {
5
+ outputRootDir?: string;
6
+ };
7
+ export type WechatDebugBundleFlowResult = {
8
+ mode: WechatDebugBundleMode;
9
+ bundlePath: string;
10
+ message: string;
11
+ };
12
+ export type WechatDebugBundleFailureCode = "missing-state-root" | "missing-diagnostics" | "zip-write-failed" | "zip-cleanup-failed" | "export-failed";
13
+ export type WechatDebugBundleFailureResult = {
14
+ ok: false;
15
+ mode: WechatDebugBundleMode;
16
+ code: WechatDebugBundleFailureCode;
17
+ message: string;
18
+ archivePath?: string;
19
+ details?: {
20
+ archivePath?: string;
21
+ cause?: string;
22
+ writeCause?: string;
23
+ cleanupCause?: string;
24
+ };
25
+ };
26
+ type RunWechatDebugBundleFlowDeps = {
27
+ writeArchiveFile?: typeof writeFile;
28
+ removeArchiveFile?: typeof rm;
29
+ };
30
+ export declare function runWechatDebugBundleFlow(input: RunWechatDebugBundleFlowInput, deps?: RunWechatDebugBundleFlowDeps): Promise<WechatDebugBundleFlowResult>;
31
+ export declare function toWechatDebugBundleFailureResult(error: unknown, input?: {
32
+ mode?: WechatDebugBundleMode;
33
+ }): WechatDebugBundleFailureResult;
34
+ export declare function toWechatDebugBundleFailureMessage(error: unknown, input?: {
35
+ mode?: WechatDebugBundleMode;
36
+ }): string;
37
+ export {};