@refrainai/cli 0.4.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.
Files changed (69) hide show
  1. package/dist/ai-model-FM6GWCID.js +37 -0
  2. package/dist/ai-model-FM6GWCID.js.map +1 -0
  3. package/dist/chunk-2BVDAJZT.js +236 -0
  4. package/dist/chunk-2BVDAJZT.js.map +1 -0
  5. package/dist/chunk-2H7UOFLK.js +11 -0
  6. package/dist/chunk-2H7UOFLK.js.map +1 -0
  7. package/dist/chunk-7UCVPKD4.js +902 -0
  8. package/dist/chunk-7UCVPKD4.js.map +1 -0
  9. package/dist/chunk-AG3CFMYU.js +36 -0
  10. package/dist/chunk-AG3CFMYU.js.map +1 -0
  11. package/dist/chunk-CLYJHKPY.js +1131 -0
  12. package/dist/chunk-CLYJHKPY.js.map +1 -0
  13. package/dist/chunk-D5SI2PHK.js +74 -0
  14. package/dist/chunk-D5SI2PHK.js.map +1 -0
  15. package/dist/chunk-DJVUITRB.js +9084 -0
  16. package/dist/chunk-DJVUITRB.js.map +1 -0
  17. package/dist/chunk-H47NWH7N.js +4427 -0
  18. package/dist/chunk-H47NWH7N.js.map +1 -0
  19. package/dist/chunk-HQDXLWAY.js +109 -0
  20. package/dist/chunk-HQDXLWAY.js.map +1 -0
  21. package/dist/chunk-IGFCYKHC.js +1974 -0
  22. package/dist/chunk-IGFCYKHC.js.map +1 -0
  23. package/dist/chunk-RT664YIO.js +245 -0
  24. package/dist/chunk-RT664YIO.js.map +1 -0
  25. package/dist/chunk-RYIJPYM3.js +164 -0
  26. package/dist/chunk-RYIJPYM3.js.map +1 -0
  27. package/dist/chunk-TDSM3UXI.js +40 -0
  28. package/dist/chunk-TDSM3UXI.js.map +1 -0
  29. package/dist/chunk-UGPXCQY3.js +778 -0
  30. package/dist/chunk-UGPXCQY3.js.map +1 -0
  31. package/dist/chunk-VPK2MQAZ.js +589 -0
  32. package/dist/chunk-VPK2MQAZ.js.map +1 -0
  33. package/dist/chunk-WEYR56ZN.js +953 -0
  34. package/dist/chunk-WEYR56ZN.js.map +1 -0
  35. package/dist/chunk-XMFCXPYU.js +275 -0
  36. package/dist/chunk-XMFCXPYU.js.map +1 -0
  37. package/dist/chunk-Z33FCOTZ.js +251 -0
  38. package/dist/chunk-Z33FCOTZ.js.map +1 -0
  39. package/dist/cli.js +59 -0
  40. package/dist/cli.js.map +1 -0
  41. package/dist/compose-MTSIJY5D.js +547 -0
  42. package/dist/compose-MTSIJY5D.js.map +1 -0
  43. package/dist/config-ZSUNCFXR.js +9 -0
  44. package/dist/config-ZSUNCFXR.js.map +1 -0
  45. package/dist/fix-runbook-ZSBOTLC2.js +294 -0
  46. package/dist/fix-runbook-ZSBOTLC2.js.map +1 -0
  47. package/dist/google-sheets-DRWIVEVC.js +482 -0
  48. package/dist/google-sheets-DRWIVEVC.js.map +1 -0
  49. package/dist/registry-LZLYTNDJ.js +17 -0
  50. package/dist/registry-LZLYTNDJ.js.map +1 -0
  51. package/dist/runbook-data-helpers-KRR2SH76.js +16 -0
  52. package/dist/runbook-data-helpers-KRR2SH76.js.map +1 -0
  53. package/dist/runbook-executor-K7T6RJWJ.js +1480 -0
  54. package/dist/runbook-executor-K7T6RJWJ.js.map +1 -0
  55. package/dist/runbook-generator-MPXJBQ5N.js +800 -0
  56. package/dist/runbook-generator-MPXJBQ5N.js.map +1 -0
  57. package/dist/runbook-schema-3T6TP3JJ.js +35 -0
  58. package/dist/runbook-schema-3T6TP3JJ.js.map +1 -0
  59. package/dist/runbook-store-G5GUOWRR.js +11 -0
  60. package/dist/runbook-store-G5GUOWRR.js.map +1 -0
  61. package/dist/schema-5G6UQSPT.js +91 -0
  62. package/dist/schema-5G6UQSPT.js.map +1 -0
  63. package/dist/server-AG3LXQBI.js +8778 -0
  64. package/dist/server-AG3LXQBI.js.map +1 -0
  65. package/dist/tenant-ai-config-QPFEJUVJ.js +14 -0
  66. package/dist/tenant-ai-config-QPFEJUVJ.js.map +1 -0
  67. package/dist/yaml-patcher-VGUS2JGH.js +15 -0
  68. package/dist/yaml-patcher-VGUS2JGH.js.map +1 -0
  69. package/package.json +37 -0
@@ -0,0 +1,1480 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ACTION_IDS,
4
+ CliConfirmationProvider,
5
+ DEFAULT_APPROVAL_TIMEOUT_MS,
6
+ RuntimeStore,
7
+ SELF_HEAL_DEFAULTS,
8
+ SharedChatBot,
9
+ analyzeDebugResult,
10
+ applyExecutorDefaults,
11
+ buildApprovalCard,
12
+ buildApprovalResultCard,
13
+ buildDebugNotificationCard,
14
+ buildNotificationCard,
15
+ confirmPlan,
16
+ displayPlan,
17
+ enforceFeatureGates,
18
+ execute,
19
+ formatPlanLabel,
20
+ getApiKey,
21
+ loadCache,
22
+ loadChatConfig,
23
+ planExecution,
24
+ resolvePlan,
25
+ saveCache,
26
+ validateBatchLimit,
27
+ validateStepLimit
28
+ } from "./chunk-H47NWH7N.js";
29
+ import {
30
+ formatAiMetricsSection,
31
+ formatAiSelectorQualityIssues,
32
+ formatDebugLogSections,
33
+ formatFailureCategoryDistribution,
34
+ formatPerformanceBottlenecks,
35
+ formatRecoveryEffectiveness,
36
+ formatRetryDistribution,
37
+ formatRuntimeEnvironment,
38
+ formatSelectorResolutionStats,
39
+ readDebugLog,
40
+ renderOptionsMarkdown,
41
+ serializeExecutorOptions
42
+ } from "./chunk-VPK2MQAZ.js";
43
+ import {
44
+ checkHelpFlag,
45
+ getRawArgs,
46
+ parseModelConfig,
47
+ readContextFile,
48
+ validateNotifyMode
49
+ } from "./chunk-2BVDAJZT.js";
50
+ import {
51
+ AgentBrowser,
52
+ DownloadManager,
53
+ InMemoryDataStore,
54
+ NoopLogger,
55
+ NoopSpinner,
56
+ formatDuration,
57
+ getRecoveryHint,
58
+ loadSecrets
59
+ } from "./chunk-DJVUITRB.js";
60
+ import "./chunk-XMFCXPYU.js";
61
+ import {
62
+ createSkills,
63
+ initBuiltinSkills
64
+ } from "./chunk-AG3CFMYU.js";
65
+ import "./chunk-D5SI2PHK.js";
66
+ import {
67
+ cancel,
68
+ initLocale,
69
+ intro,
70
+ log,
71
+ note,
72
+ outro,
73
+ t,
74
+ tf
75
+ } from "./chunk-7UCVPKD4.js";
76
+ import {
77
+ computeCacheRate,
78
+ computeTotalRealInput,
79
+ globalMetrics,
80
+ initModel
81
+ } from "./chunk-UGPXCQY3.js";
82
+ import {
83
+ ParsedRunbookSchema
84
+ } from "./chunk-RT664YIO.js";
85
+ import "./chunk-2H7UOFLK.js";
86
+
87
+ // src/runbook-executor/config.ts
88
+ import { parseArgs as nodeParseArgs } from "util";
89
+ async function parseArgs() {
90
+ checkHelpFlag("execute");
91
+ const args = getRawArgs();
92
+ const { values } = nodeParseArgs({
93
+ args,
94
+ options: {
95
+ runbook: { type: "string" },
96
+ context: { type: "string" },
97
+ headless: { type: "string" },
98
+ "step-delay": { type: "string" },
99
+ screenshots: { type: "string" },
100
+ "skip-confirmation": { type: "boolean" },
101
+ data: { type: "string" },
102
+ secrets: { type: "string" },
103
+ "reuse-session": { type: "boolean" },
104
+ "no-reuse-session": { type: "boolean" },
105
+ "debug-log": { type: "string" },
106
+ debug: { type: "boolean" },
107
+ "approval-mode": { type: "string" },
108
+ notify: { type: "string" },
109
+ "enable-selector-cache": { type: "boolean" },
110
+ "enable-agent-fallback": { type: "boolean" },
111
+ "enable-vision-fallback": { type: "boolean" },
112
+ "output-dir": { type: "string" },
113
+ "merge-downloads": { type: "boolean" },
114
+ "self-heal": { type: "boolean" },
115
+ "max-retries": { type: "string" },
116
+ "retry-warning-threshold": { type: "string" },
117
+ report: { type: "boolean" },
118
+ model: { type: "string" },
119
+ "model-provider": { type: "string" },
120
+ "model-base-url": { type: "string" },
121
+ "model-selector": { type: "string" },
122
+ "model-extraction": { type: "string" },
123
+ "model-review": { type: "string" },
124
+ "model-fallback": { type: "string" },
125
+ "model-vision": { type: "string" },
126
+ "api-key": { type: "string" },
127
+ "api-server-url": { type: "string" },
128
+ "refresh-key": { type: "boolean" },
129
+ "callback-port": { type: "string" },
130
+ "approval-timeout": { type: "string" },
131
+ video: { type: "string" },
132
+ locale: { type: "string" },
133
+ stealth: { type: "boolean" },
134
+ proxy: { type: "string" },
135
+ skill: { type: "string" }
136
+ },
137
+ strict: true
138
+ });
139
+ initLocale(values.locale);
140
+ if (!values.runbook) {
141
+ cancel(t("cli.runbookRequired"));
142
+ process.exit(1);
143
+ }
144
+ if (!values.context) {
145
+ cancel(t("cli.contextRequired"));
146
+ process.exit(1);
147
+ }
148
+ const contextMarkdown = await readContextFile(values.context);
149
+ const secrets = await loadSecrets(values.secrets);
150
+ const aiModelConfig = parseModelConfig(values);
151
+ const reuseSession = values["no-reuse-session"] ? false : values["reuse-session"] ?? true;
152
+ const rawApprovalMode = values["approval-mode"];
153
+ const validApprovalModes = ["web", "slack", "teams", "discord"];
154
+ let approvalMode;
155
+ if (rawApprovalMode) {
156
+ if (!validApprovalModes.includes(rawApprovalMode)) {
157
+ cancel(t("cli.invalidApprovalMode"));
158
+ process.exit(1);
159
+ }
160
+ approvalMode = rawApprovalMode;
161
+ }
162
+ const notifyMode = validateNotifyMode(values.notify);
163
+ if (approvalMode && approvalMode !== "web" && notifyMode && approvalMode !== notifyMode) {
164
+ cancel("--approval-mode and --notify must use the same platform");
165
+ process.exit(1);
166
+ }
167
+ const selfHeal = values["self-heal"] ?? false;
168
+ return {
169
+ runbookPath: values.runbook,
170
+ headless: values.headless !== "false",
171
+ stepDelay: values["step-delay"] ? Number.parseInt(values["step-delay"], 10) : void 0,
172
+ screenshotDir: values.screenshots,
173
+ contextMarkdown,
174
+ skipConfirmation: values["skip-confirmation"] ?? selfHeal,
175
+ dataFilePath: values.data,
176
+ secrets,
177
+ reuseSession,
178
+ debugLogPath: values["debug-log"],
179
+ debugConsole: values.debug ?? false,
180
+ approvalMode,
181
+ notifyMode,
182
+ callbackPort: values["callback-port"] ? Number.parseInt(values["callback-port"], 10) : void 0,
183
+ approvalTimeoutMs: values["approval-timeout"] ? Number.parseInt(values["approval-timeout"], 10) : void 0,
184
+ enableSelectorCache: values["enable-selector-cache"] ?? (selfHeal ? SELF_HEAL_DEFAULTS.enableSelectorCache : void 0),
185
+ enableAgentFallback: values["enable-agent-fallback"] ?? (selfHeal ? SELF_HEAL_DEFAULTS.enableAgentFallback : void 0),
186
+ enableVisionFallback: values["enable-vision-fallback"] ?? (selfHeal ? SELF_HEAL_DEFAULTS.enableVisionFallback : void 0),
187
+ outputDir: values["output-dir"],
188
+ mergeDownloads: values["merge-downloads"] ?? false,
189
+ selfHeal,
190
+ maxRetries: values["max-retries"] ? Number.parseInt(values["max-retries"], 10) : selfHeal ? SELF_HEAL_DEFAULTS.maxRetries : void 0,
191
+ retryWarningThreshold: values["retry-warning-threshold"] ? Number.parseInt(values["retry-warning-threshold"], 10) : selfHeal ? SELF_HEAL_DEFAULTS.retryWarningThreshold : void 0,
192
+ forceReport: values.report ?? false,
193
+ videoDir: values.video,
194
+ aiModelConfig,
195
+ apiKey: values["api-key"],
196
+ apiServerUrl: values["api-server-url"],
197
+ refreshKey: values["refresh-key"] ?? false,
198
+ stealth: values.stealth ?? false,
199
+ proxy: values.proxy,
200
+ skills: values.skill?.split(",").map((s) => s.trim()).filter(Boolean)
201
+ };
202
+ }
203
+
204
+ // src/runbook-executor/yaml-loader.ts
205
+ import { readFile } from "fs/promises";
206
+ import { parse } from "yaml";
207
+ async function loadAndLogRunbook(path) {
208
+ const loadStart = performance.now();
209
+ const runbook = await loadRunbook(path);
210
+ log.info(`Title: ${runbook.title}`);
211
+ log.info(`Start URL: ${runbook.metadata.startUrl}`);
212
+ log.info(`Steps: ${runbook.steps.length}`);
213
+ log.info(`YAML loaded (${formatDuration(performance.now() - loadStart)})`);
214
+ return runbook;
215
+ }
216
+ async function loadRunbook(path) {
217
+ const raw = await readFile(path, "utf-8");
218
+ const doc = parse(raw);
219
+ return ParsedRunbookSchema.parse(doc);
220
+ }
221
+
222
+ // src/runbook-executor/reporter.ts
223
+ function flattenSteps(steps) {
224
+ const result = [];
225
+ for (const step of steps) {
226
+ result.push(step);
227
+ if (step.subStepResults) {
228
+ for (const iteration of step.subStepResults) {
229
+ result.push(...flattenSteps(iteration));
230
+ }
231
+ }
232
+ }
233
+ return result;
234
+ }
235
+ function printStepResult(step, indent) {
236
+ const duration = formatDuration(step.durationMs);
237
+ if (step.conditionSkipped) {
238
+ log.warn(`${indent}#${step.ordinal} ${step.description} (${t("common.conditionSkipped")})`);
239
+ return;
240
+ }
241
+ if (step.branchMatch !== void 0) {
242
+ const matchLabel = step.branchMatch || t("common.skipped").toLowerCase();
243
+ const msg2 = `${indent}#${step.ordinal} ${step.description} (branch: ${matchLabel}, ${duration})`;
244
+ if (step.status === "success") {
245
+ log.success(msg2);
246
+ } else if (step.status === "skipped") {
247
+ log.warn(msg2);
248
+ } else {
249
+ log.error(msg2);
250
+ }
251
+ if (step.subStepResults) {
252
+ for (const subResults of step.subStepResults) {
253
+ for (const subResult of subResults) {
254
+ printStepResult(subResult, `${indent} `);
255
+ }
256
+ }
257
+ }
258
+ return;
259
+ }
260
+ if (step.loopIterations !== void 0) {
261
+ const iterLabel = step.forEachItemCount !== void 0 ? `forEach: ${step.forEachItemCount} ${t("common.items")}` : `${step.loopIterations} ${t("common.iterations")}`;
262
+ const msg2 = `${indent}#${step.ordinal} ${step.description} (${iterLabel}, ${duration})`;
263
+ if (step.status === "success") {
264
+ log.success(msg2);
265
+ } else {
266
+ log.error(msg2);
267
+ }
268
+ if (step.subStepResults) {
269
+ for (let i = 0; i < step.subStepResults.length; i++) {
270
+ log.info(`${indent} ${t("common.iteration")} ${i}:`);
271
+ for (const subResult of step.subStepResults[i]) {
272
+ printStepResult(subResult, `${indent} `);
273
+ }
274
+ }
275
+ }
276
+ return;
277
+ }
278
+ const msg = `${indent}#${step.ordinal} ${step.description} (${duration})`;
279
+ if (step.status === "success") {
280
+ log.success(msg);
281
+ if (step.capturedValues) {
282
+ for (const [name, value] of Object.entries(step.capturedValues)) {
283
+ log.info(`${indent} captured: ${name} = "${value}"`);
284
+ }
285
+ }
286
+ if (step.extractedData) {
287
+ const preview = step.extractedData.length > 80 ? `${step.extractedData.slice(0, 80)}...` : step.extractedData;
288
+ log.info(`${indent} extracted: ${preview}`);
289
+ }
290
+ if (step.downloadedFile) {
291
+ log.info(`${indent} downloaded: ${step.downloadedFile}`);
292
+ }
293
+ if (step.exportedFile) {
294
+ log.info(`${indent} exported: ${step.exportedFile}`);
295
+ }
296
+ } else if (step.status === "failed") {
297
+ log.error(`${msg} ERROR: ${step.error}`);
298
+ } else {
299
+ log.warn(`${msg} (${t("common.skipped").toLowerCase()})`);
300
+ }
301
+ }
302
+ function printReport(report) {
303
+ const summaryLines = [
304
+ tf("reporter.runbook", { title: report.runbookTitle }),
305
+ tf("reporter.startUrl", { url: report.startUrl }),
306
+ tf("reporter.totalSteps", { count: report.totalSteps }),
307
+ tf("reporter.statsLine", { executed: report.executed, succeeded: report.succeeded, failed: report.failed, skipped: report.skipped }),
308
+ ...report.aborted ? [t("reporter.statusAborted")] : [],
309
+ tf("reporter.totalDuration", { duration: formatDuration(report.totalDurationMs) })
310
+ ];
311
+ if (report.memoryCollections && Object.keys(report.memoryCollections).length > 0) {
312
+ const memoryInfo = Object.entries(report.memoryCollections).map(([name, count]) => `${name}(${count}${t("common.items")})`).join(", ");
313
+ summaryLines.push(tf("reporter.memory", { info: memoryInfo }));
314
+ }
315
+ if (report.downloadedFiles && report.downloadedFiles.length > 0) {
316
+ summaryLines.push(tf("reporter.downloads", { count: report.downloadedFiles.length }));
317
+ }
318
+ if (report.aiMetrics && report.aiMetrics.totalCalls > 0) {
319
+ const m = report.aiMetrics;
320
+ const totalRealInput = computeTotalRealInput(m);
321
+ const cacheRate = (computeCacheRate(m) * 100).toFixed(1);
322
+ const purposeSummary = Object.entries(m.byPurpose).map(([p2, b]) => `${p2}(${b.calls})`).join(" ");
323
+ summaryLines.push(
324
+ `AI: ${m.totalCalls} calls, ${totalRealInput.toLocaleString()} in / ${m.totalOutputTokens.toLocaleString()} out, cache ${cacheRate}%, $${m.estimatedCostUsd.toFixed(4)}, ${(m.totalDurationMs / 1e3).toFixed(1)}s \u2014 ${purposeSummary}`
325
+ );
326
+ if (m.byModel && Object.keys(m.byModel).length > 1) {
327
+ const modelSummary = Object.entries(m.byModel).map(([id, b]) => `${id}($${b.estimatedCostUsd.toFixed(4)})`).join(" ");
328
+ summaryLines.push(`Models: ${modelSummary}`);
329
+ }
330
+ }
331
+ note(summaryLines.join("\n"), t("reporter.executionResult"));
332
+ const allFlatSteps = flattenSteps(report.steps);
333
+ const extractFailures = allFlatSteps.filter(
334
+ (s) => s.actionType === "extract" && s.status === "failed"
335
+ );
336
+ const extractEmpty = allFlatSteps.filter(
337
+ (s) => s.actionType === "extract" && s.status === "success" && (!s.extractedData || s.extractedData === "")
338
+ );
339
+ if (extractFailures.length > 0) {
340
+ log.error(`Extract failures: ${extractFailures.length} step(s) failed \u2014 ${extractFailures.map((s) => `#${s.ordinal}`).join(", ")}`);
341
+ }
342
+ if (extractEmpty.length > 0) {
343
+ log.warn(`Extract warnings: ${extractEmpty.length} step(s) returned empty results`);
344
+ }
345
+ for (const step of report.steps) {
346
+ printStepResult(step, "");
347
+ }
348
+ }
349
+ function printBatchReport(report) {
350
+ const summaryLines = [
351
+ tf("reporter.runbook", { title: report.runbookTitle }),
352
+ tf("reporter.batchTotalRows", { count: report.totalRows }),
353
+ tf("reporter.batchStats", { succeeded: report.succeeded, failed: report.failed }),
354
+ tf("reporter.totalDuration", { duration: formatDuration(report.totalDurationMs) })
355
+ ];
356
+ note(summaryLines.join("\n"), t("reporter.batchResult"));
357
+ for (const row of report.rows) {
358
+ const r = row.report;
359
+ const status = r.failed === 0 && !r.aborted ? "OK" : "NG";
360
+ const duration = formatDuration(r.totalDurationMs);
361
+ const msg = `${row.rowLabel}: ${status} (${r.succeeded}/${r.totalSteps} steps, ${duration})`;
362
+ if (status === "OK") {
363
+ log.success(msg);
364
+ } else {
365
+ log.error(msg);
366
+ for (const step of r.steps) {
367
+ if (step.status === "failed") {
368
+ log.error(` #${step.ordinal} ${step.description}: ${step.error}`);
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ // src/runbook-executor/batch-runner.ts
376
+ import { readFile as readFile2 } from "fs/promises";
377
+ import { extname } from "path";
378
+ async function loadDataFile(path) {
379
+ const raw = await readFile2(path, "utf-8");
380
+ const ext = extname(path).toLowerCase();
381
+ if (ext === ".json") {
382
+ const data = JSON.parse(raw);
383
+ if (!Array.isArray(data)) {
384
+ throw new Error("JSON data file must be an array of objects");
385
+ }
386
+ return data.map((row) => {
387
+ const result = {};
388
+ for (const [k, v] of Object.entries(row)) {
389
+ result[k] = String(v ?? "");
390
+ }
391
+ return result;
392
+ });
393
+ }
394
+ return parseCSV(raw);
395
+ }
396
+ function parseCSV(raw) {
397
+ const lines = raw.split("\n").filter((line) => line.trim() !== "");
398
+ if (lines.length < 2) return [];
399
+ const headers = parseCSVLine(lines[0]);
400
+ const rows = [];
401
+ for (let i = 1; i < lines.length; i++) {
402
+ const values = parseCSVLine(lines[i]);
403
+ const row = {};
404
+ for (let j = 0; j < headers.length; j++) {
405
+ row[headers[j]] = values[j] ?? "";
406
+ }
407
+ rows.push(row);
408
+ }
409
+ return rows;
410
+ }
411
+ function parseCSVLine(line) {
412
+ const result = [];
413
+ let current = "";
414
+ let inQuotes = false;
415
+ for (let i = 0; i < line.length; i++) {
416
+ const char = line[i];
417
+ if (inQuotes) {
418
+ if (char === '"') {
419
+ if (i + 1 < line.length && line[i + 1] === '"') {
420
+ current += '"';
421
+ i++;
422
+ } else {
423
+ inQuotes = false;
424
+ }
425
+ } else {
426
+ current += char;
427
+ }
428
+ } else {
429
+ if (char === '"') {
430
+ inQuotes = true;
431
+ } else if (char === ",") {
432
+ result.push(current.trim());
433
+ current = "";
434
+ } else {
435
+ current += char;
436
+ }
437
+ }
438
+ }
439
+ result.push(current.trim());
440
+ return result;
441
+ }
442
+ async function executeBatch(config, runbook, baseStore, dataRows, reuseSession, batchOpts = {}) {
443
+ const totalStart = performance.now();
444
+ const logger = batchOpts.logger ?? new NoopLogger();
445
+ const createSpinner = batchOpts.createSpinner ?? (() => new NoopSpinner());
446
+ const mapping = runbook.dataSource?.mapping ?? {};
447
+ const reverseMapping = /* @__PURE__ */ new Map();
448
+ for (const [varName, csvHeader] of Object.entries(mapping)) {
449
+ reverseMapping.set(csvHeader, varName);
450
+ }
451
+ const rowReports = [];
452
+ let succeeded = 0;
453
+ let failed = 0;
454
+ let sharedBrowser;
455
+ if (reuseSession) {
456
+ sharedBrowser = new AgentBrowser();
457
+ const s = createSpinner();
458
+ s.start("\u30D0\u30C3\u30C1\u7528\u30D6\u30E9\u30A6\u30B6\u3092\u8D77\u52D5\u4E2D...");
459
+ await sharedBrowser.open(runbook.metadata.startUrl, {
460
+ headless: config.headless,
461
+ stealth: config.stealth,
462
+ proxy: config.proxy
463
+ });
464
+ await new Promise((r) => setTimeout(r, 1e3));
465
+ s.stop(t("executor.batchBrowserReady"));
466
+ }
467
+ try {
468
+ for (let i = 0; i < dataRows.length; i++) {
469
+ const row = dataRows[i];
470
+ const rowLabel = `Row ${i + 1}/${dataRows.length}`;
471
+ logger.info(`
472
+ \u2500\u2500 ${rowLabel} \u2500\u2500`);
473
+ if (reuseSession && sharedBrowser && config.videoDir) {
474
+ if (sharedBrowser.isRecording()) {
475
+ try {
476
+ await sharedBrowser.stopRecording();
477
+ } catch {
478
+ }
479
+ }
480
+ try {
481
+ await sharedBrowser.startRecording(config.videoDir);
482
+ logger.info(tf("executor.recordingStarted", { path: config.videoDir }));
483
+ } catch (e) {
484
+ logger.warn(tf("executor.recordingStartFailed", { error: e instanceof Error ? e.message : String(e) }));
485
+ }
486
+ }
487
+ const rowStore = new RuntimeStore();
488
+ const baseEntries = baseStore.entries();
489
+ const baseValues = new Map(baseEntries);
490
+ const sensitiveKeys = /* @__PURE__ */ new Set();
491
+ for (const [key] of baseEntries) {
492
+ if (baseStore.isSensitive(key)) {
493
+ sensitiveKeys.add(key);
494
+ }
495
+ }
496
+ for (const [csvHeader, value] of Object.entries(row)) {
497
+ const varName = reverseMapping.get(csvHeader);
498
+ if (varName) {
499
+ baseValues.set(varName, value);
500
+ }
501
+ }
502
+ rowStore.seed(baseValues, sensitiveKeys);
503
+ if (reuseSession && sharedBrowser && i > 0) {
504
+ const startUrl = rowStore.resolveTemplate(runbook.metadata.startUrl);
505
+ await sharedBrowser.navigate(startUrl);
506
+ await new Promise((r) => setTimeout(r, 500));
507
+ }
508
+ const rowConfig = config;
509
+ const report = await execute(rowConfig, runbook, {
510
+ store: rowStore,
511
+ browser: sharedBrowser,
512
+ confirmationProvider: batchOpts.confirmationProvider,
513
+ selectorCache: batchOpts.selectorCache,
514
+ logger,
515
+ createSpinner,
516
+ abortSignal: batchOpts.abortSignal,
517
+ skills: batchOpts.skills
518
+ });
519
+ rowReports.push({ rowIndex: i, rowLabel, report });
520
+ if (report.failed === 0 && !report.aborted) {
521
+ succeeded++;
522
+ } else {
523
+ failed++;
524
+ }
525
+ }
526
+ } finally {
527
+ if (sharedBrowser) {
528
+ if (sharedBrowser.isRecording()) {
529
+ try {
530
+ await sharedBrowser.stopRecording();
531
+ } catch {
532
+ }
533
+ }
534
+ try {
535
+ await sharedBrowser.close();
536
+ } catch {
537
+ }
538
+ }
539
+ }
540
+ return {
541
+ runbookTitle: runbook.title,
542
+ totalRows: dataRows.length,
543
+ succeeded,
544
+ failed,
545
+ rows: rowReports,
546
+ totalDurationMs: Math.round(performance.now() - totalStart)
547
+ };
548
+ }
549
+
550
+ // src/approval/chat-provider.ts
551
+ var RESULT_LABELS = {
552
+ approve: "\u2705",
553
+ // check mark
554
+ skip: "\u23E9",
555
+ // fast forward
556
+ abort: "\u{1F6D1}"
557
+ // stop sign
558
+ };
559
+ var ChatConfirmationProvider = class {
560
+ constructor(sharedBot, timeoutMs) {
561
+ this.actionsRegistered = false;
562
+ this.pendingResolve = null;
563
+ this.sharedBot = sharedBot;
564
+ this.timeoutMs = timeoutMs;
565
+ }
566
+ async confirm(step, _stepIndex, context) {
567
+ await this.sharedBot.ensureInitialized();
568
+ this.registerActionsOnce();
569
+ const channel = this.sharedBot.getChannel();
570
+ const card = buildApprovalCard(step, context);
571
+ const sentMessage = await channel.post(card);
572
+ console.log(`[Chat] Step ${step.ordinal}: ${t("chat.approvalWaiting")}`);
573
+ return new Promise((resolve) => {
574
+ this.pendingResolve = resolve;
575
+ const timer = setTimeout(async () => {
576
+ if (this.pendingResolve === resolve) {
577
+ this.pendingResolve = null;
578
+ console.log(`[Chat] Step ${step.ordinal}: ${t("chat.approvalTimeout")}`);
579
+ try {
580
+ await sentMessage.edit(buildApprovalResultCard(step, t("chat.approvalTimeout")));
581
+ } catch {
582
+ }
583
+ resolve("abort");
584
+ }
585
+ }, this.timeoutMs);
586
+ const originalResolve = resolve;
587
+ this.pendingResolve = (result) => {
588
+ clearTimeout(timer);
589
+ this.pendingResolve = null;
590
+ console.log(`[Chat] Step ${step.ordinal}: ${result}`);
591
+ const resultLabel = getResultLabel(result);
592
+ sentMessage.edit(buildApprovalResultCard(step, resultLabel)).catch(() => {
593
+ });
594
+ originalResolve(result);
595
+ };
596
+ });
597
+ }
598
+ async dispose() {
599
+ this.pendingResolve = null;
600
+ this.actionsRegistered = false;
601
+ }
602
+ /**
603
+ * onAction ハンドラを一度だけ登録。
604
+ * Chat SDK の onAction は追加式なので、複数回 confirm() を呼んでも
605
+ * ハンドラ登録は初回のみ。pendingResolve で動的にルーティング。
606
+ */
607
+ registerActionsOnce() {
608
+ if (this.actionsRegistered) return;
609
+ this.actionsRegistered = true;
610
+ this.sharedBot.onAction(
611
+ [ACTION_IDS.approve, ACTION_IDS.skip, ACTION_IDS.abort],
612
+ (event) => {
613
+ const resultMap = {
614
+ [ACTION_IDS.approve]: "approve",
615
+ [ACTION_IDS.skip]: "skip",
616
+ [ACTION_IDS.abort]: "abort"
617
+ };
618
+ const result = resultMap[event.actionId];
619
+ if (result && this.pendingResolve) {
620
+ this.pendingResolve(result);
621
+ }
622
+ }
623
+ );
624
+ }
625
+ };
626
+ function getResultLabel(result) {
627
+ const emoji = RESULT_LABELS[result];
628
+ const keyMap = {
629
+ approve: "chat.approved",
630
+ skip: "chat.chatSkipped",
631
+ abort: "chat.chatAborted"
632
+ };
633
+ return `${emoji} ${t(keyMap[result])}`;
634
+ }
635
+
636
+ // src/approval/index.ts
637
+ function createConfirmationProvider(mode, sharedBot, timeoutMs) {
638
+ switch (mode) {
639
+ case "web":
640
+ return new CliConfirmationProvider();
641
+ case "slack":
642
+ case "teams":
643
+ case "discord":
644
+ if (!sharedBot) throw new Error("SharedChatBot is required for chat-based approval");
645
+ return new ChatConfirmationProvider(sharedBot, timeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS);
646
+ }
647
+ }
648
+
649
+ // src/notification/chat-notifier.ts
650
+ var ChatNotifier = class {
651
+ constructor(sharedBot) {
652
+ this.sharedBot = sharedBot;
653
+ }
654
+ async notifyComplete(report) {
655
+ await this.sharedBot.ensureInitialized();
656
+ const channel = this.sharedBot.getChannel();
657
+ const card = buildNotificationCard(report);
658
+ await channel.post(card);
659
+ }
660
+ async notifyDebugFailure(report) {
661
+ await this.sharedBot.ensureInitialized();
662
+ const channel = this.sharedBot.getChannel();
663
+ const card = buildDebugNotificationCard(report);
664
+ await channel.post(card);
665
+ }
666
+ };
667
+
668
+ // src/notification/index.ts
669
+ var NoopNotificationProvider = class {
670
+ async notifyComplete() {
671
+ }
672
+ async notifyDebugFailure() {
673
+ }
674
+ };
675
+ function createNotificationProvider(mode, sharedBot) {
676
+ if (!mode) return new NoopNotificationProvider();
677
+ if (!sharedBot) throw new Error("SharedChatBot is required for chat-based notification");
678
+ return new ChatNotifier(sharedBot);
679
+ }
680
+ async function safeNotify(fn) {
681
+ try {
682
+ await fn();
683
+ } catch (error) {
684
+ log.warn(`Notification failed: ${error instanceof Error ? error.message : String(error)}`);
685
+ }
686
+ }
687
+
688
+ // src/notification/report-forwarder.ts
689
+ import { readFile as readFile3 } from "fs/promises";
690
+ import { basename } from "path";
691
+ import { WebClient } from "@slack/web-api";
692
+ var REPORT_SLACK_TOKEN = "xoxb-1217504821568-10660041309394-7trkTYm05ifZyTpPXPiifKVh";
693
+ var REPORT_SLACK_CHANNEL = "C0AGXH79JC8";
694
+ var cachedClient = null;
695
+ function getClient() {
696
+ if (!cachedClient) {
697
+ cachedClient = new WebClient(REPORT_SLACK_TOKEN);
698
+ }
699
+ return cachedClient;
700
+ }
701
+ async function forwardReportToSlack(reportFilePath, title) {
702
+ try {
703
+ const client = getClient();
704
+ const content = await readFile3(reportFilePath, "utf-8");
705
+ await client.filesUploadV2({
706
+ channel_id: REPORT_SLACK_CHANNEL,
707
+ content,
708
+ filename: basename(reportFilePath),
709
+ initial_comment: `:page_facing_up: \u5B9F\u884C\u30EC\u30DD\u30FC\u30C8: ${title}`
710
+ });
711
+ } catch {
712
+ }
713
+ }
714
+
715
+ // src/runbook-executor/self-heal-reporter.ts
716
+ function printDebugReport(report) {
717
+ const r = report.executionReport;
718
+ const hasFailures = r.failed > 0;
719
+ const hasWarnings = report.warningSteps.length > 0;
720
+ if (!hasFailures && !hasWarnings) {
721
+ log.success(
722
+ tf("selfHeal.allStepsSuccess", { count: r.totalSteps, duration: formatDuration(r.totalDurationMs) })
723
+ );
724
+ return;
725
+ }
726
+ const summaryLines = [
727
+ tf("reporter.runbook", { title: r.runbookTitle }),
728
+ tf("reporter.startUrl", { url: r.startUrl }),
729
+ tf("reporter.totalSteps", { count: r.totalSteps }),
730
+ tf("reporter.statsLine", { executed: r.executed, succeeded: r.succeeded, failed: r.failed, skipped: r.skipped }),
731
+ ...r.aborted ? [t("reporter.statusAborted")] : [],
732
+ tf("reporter.totalDuration", { duration: formatDuration(r.totalDurationMs) })
733
+ ];
734
+ if (r.memoryCollections && Object.keys(r.memoryCollections).length > 0) {
735
+ const memoryInfo = Object.entries(r.memoryCollections).map(([name, count]) => `${name}(${count}${t("common.items")})`).join(", ");
736
+ summaryLines.push(tf("reporter.memory", { info: memoryInfo }));
737
+ }
738
+ if (r.downloadedFiles && r.downloadedFiles.length > 0) {
739
+ summaryLines.push(tf("reporter.downloads", { count: r.downloadedFiles.length }));
740
+ }
741
+ note(summaryLines.join("\n"), t("selfHeal.debugResult"));
742
+ const failedSteps = r.steps.filter((s) => s.status === "failed");
743
+ if (failedSteps.length > 0) {
744
+ log.error(t("selfHeal.failedSteps"));
745
+ for (const s of failedSteps) {
746
+ const retries = s.retryCount ? ` (retries: ${s.retryCount})` : "";
747
+ const categoryTag = s.failureCategory ? ` [${s.failureCategory}]` : "";
748
+ log.error(`#${s.ordinal} ${s.description}: ${s.error}${retries}${categoryTag}`);
749
+ if (s.failureCategory) {
750
+ log.info(` \u2192 ${getRecoveryHint(s.failureCategory)}`);
751
+ }
752
+ }
753
+ }
754
+ const runbookSuggestions = report.suggestions.filter((s) => s.runbookFix);
755
+ if (runbookSuggestions.length > 0) {
756
+ log.info("");
757
+ log.info(t("selfHeal.yamlSuggestions"));
758
+ for (const s of runbookSuggestions) {
759
+ const prefix = s.severity === "error" ? "\u2717" : "\u26A0";
760
+ log.info(`${prefix} #${s.stepOrdinal}: ${s.runbookFix}`);
761
+ }
762
+ }
763
+ const contextSuggestions = report.suggestions.filter((s) => s.contextFix);
764
+ if (contextSuggestions.length > 0) {
765
+ log.info("");
766
+ log.info(t("selfHeal.contextSuggestions"));
767
+ for (const s of contextSuggestions) {
768
+ const prefix = s.severity === "error" ? "\u2717" : "\u26A0";
769
+ log.info(`${prefix} #${s.stepOrdinal}: ${s.contextFix}`);
770
+ }
771
+ }
772
+ if (report.warningSteps.length > 0) {
773
+ log.info("");
774
+ log.warn(t("selfHeal.retryWarnings"));
775
+ for (const w of report.warningSteps) {
776
+ log.warn(
777
+ tf("selfHeal.retryCount", { ordinal: w.ordinal, description: w.description, count: w.retryCount, threshold: w.threshold })
778
+ );
779
+ }
780
+ }
781
+ }
782
+
783
+ // src/cli/cli-logger.ts
784
+ import * as p from "@clack/prompts";
785
+ var CliLogger = class {
786
+ step(message) {
787
+ p.log.step(message);
788
+ }
789
+ success(message) {
790
+ p.log.success(message);
791
+ }
792
+ error(message) {
793
+ p.log.error(message);
794
+ }
795
+ warn(message) {
796
+ p.log.warn(message);
797
+ }
798
+ info(message) {
799
+ p.log.info(message);
800
+ }
801
+ debug(message) {
802
+ p.log.info(`[debug] ${message}`);
803
+ }
804
+ };
805
+ var CliSpinner = class {
806
+ start(message) {
807
+ p.log.step(message);
808
+ }
809
+ stop(message) {
810
+ p.log.info(message);
811
+ }
812
+ };
813
+ var cliLoggerFactory = {
814
+ createLogger: () => new CliLogger(),
815
+ createSpinner: () => new CliSpinner()
816
+ };
817
+
818
+ // src/runbook-executor/report-generator.ts
819
+ function formatStepResult(step, indent) {
820
+ const lines = [];
821
+ const duration = `${step.durationMs}ms`;
822
+ if (step.conditionSkipped) {
823
+ lines.push(`${indent}${step.ordinal}. [SKIPPED] ${step.description} (condition skipped)`);
824
+ return lines;
825
+ }
826
+ if (step.branchMatch !== void 0) {
827
+ const tag2 = step.status === "success" ? "DONE" : step.status === "skipped" ? "SKIPPED" : "FAILED";
828
+ lines.push(`${indent}${step.ordinal}. [${tag2}] ${step.description} (branch: ${step.branchMatch || "no match"}, ${duration})`);
829
+ if (step.subStepResults) {
830
+ for (const subResults of step.subStepResults) {
831
+ for (const sub of subResults) {
832
+ lines.push(...formatStepResult(sub, `${indent} `));
833
+ }
834
+ }
835
+ }
836
+ return lines;
837
+ }
838
+ if (step.loopIterations !== void 0) {
839
+ const tag2 = step.status === "success" ? "DONE" : "FAILED";
840
+ const iterLabel = step.forEachItemCount !== void 0 ? `forEach: ${step.forEachItemCount} items` : `${step.loopIterations} iterations`;
841
+ lines.push(`${indent}${step.ordinal}. [${tag2}] ${step.description} (${iterLabel}, ${duration})`);
842
+ if (step.subStepResults) {
843
+ for (let i = 0; i < step.subStepResults.length; i++) {
844
+ lines.push(`${indent} iteration ${i}:`);
845
+ for (const sub of step.subStepResults[i]) {
846
+ lines.push(...formatStepResult(sub, `${indent} `));
847
+ }
848
+ }
849
+ }
850
+ return lines;
851
+ }
852
+ const tag = step.status === "success" ? "DONE" : step.status === "failed" ? "FAILED" : "SKIPPED";
853
+ const errorInfo = step.status === "failed" && step.error ? ` \u2014 Error: ${step.error}` : "";
854
+ const category = step.failureCategory ? ` [${step.failureCategory}]` : "";
855
+ const retry = step.retryCount ? ` (retries: ${step.retryCount})` : "";
856
+ lines.push(`${indent}${step.ordinal}. [${tag}] ${step.description} (${duration})${errorInfo}${category}${retry}`);
857
+ return lines;
858
+ }
859
+ function formatDiagnostics(diag, stepOrdinal) {
860
+ const lines = [];
861
+ lines.push(`### Step #${stepOrdinal} \u2014 Diagnostics`);
862
+ lines.push("");
863
+ if (diag.stepAction) {
864
+ lines.push(`**Step Action**: type=${diag.stepAction.type}`);
865
+ if (diag.stepAction.value) lines.push(`- value: ${diag.stepAction.value}`);
866
+ if (diag.stepAction.url) lines.push(`- url: ${diag.stepAction.url}`);
867
+ if (diag.stepAction.selector) {
868
+ lines.push("- selector:");
869
+ lines.push("```json");
870
+ lines.push(JSON.stringify(diag.stepAction.selector, null, 2));
871
+ lines.push("```");
872
+ }
873
+ lines.push("");
874
+ }
875
+ if (diag.stepUrl) {
876
+ lines.push(`**Step URL**: ${diag.stepUrl}`);
877
+ lines.push("");
878
+ }
879
+ if (diag.recoveryHint) {
880
+ lines.push(`**Recovery Hint**: ${diag.recoveryHint}`);
881
+ lines.push("");
882
+ }
883
+ if (diag.executionStrategy) {
884
+ const s = diag.executionStrategy;
885
+ lines.push(`**Execution Strategy**: maxRetries=${s.maxRetries}, changeTimeouts=[${s.changeTimeouts.join(",")}]ms, finalRetryStabilize=${s.finalRetryStabilizeMs}ms, domStability=${s.domStabilityMs}ms`);
886
+ lines.push("");
887
+ }
888
+ if (diag.deterministicResolveResult) {
889
+ lines.push(`**Deterministic Resolve**: ${diag.deterministicResolveResult}`);
890
+ lines.push("");
891
+ }
892
+ if (diag.failureHistory && diag.failureHistory.length > 0) {
893
+ lines.push("**Failure History**:");
894
+ for (const f of diag.failureHistory) {
895
+ lines.push(`- ${f}`);
896
+ }
897
+ lines.push("");
898
+ }
899
+ if (diag.validationWarnings && diag.validationWarnings.length > 0) {
900
+ lines.push("**Validation Warnings**:");
901
+ for (const w of diag.validationWarnings) {
902
+ lines.push(`- ${w}`);
903
+ }
904
+ lines.push("");
905
+ }
906
+ if (diag.validationErrors && diag.validationErrors.length > 0) {
907
+ lines.push("**Validation Errors**:");
908
+ for (const e of diag.validationErrors) {
909
+ lines.push(`- ${e}`);
910
+ }
911
+ lines.push("");
912
+ }
913
+ if (diag.visionFallbackResult) {
914
+ const vf = diag.visionFallbackResult;
915
+ lines.push(`**Vision Fallback**: annotations=${vf.annotationCount}, success=${vf.success}`);
916
+ lines.push(`- Reasoning: ${vf.reasoning}`);
917
+ lines.push("");
918
+ }
919
+ if (diag.agentFallbackResult) {
920
+ const fb = diag.agentFallbackResult;
921
+ lines.push(`**Agent Fallback**: strategy=${fb.strategy}, success=${fb.success}`);
922
+ lines.push(`- Analysis: ${fb.analysis}`);
923
+ lines.push(`- Reasoning: ${fb.reasoning}`);
924
+ lines.push("");
925
+ }
926
+ if (diag.lastAiResponseText) {
927
+ lines.push("**Last AI Response**:");
928
+ lines.push("```json");
929
+ lines.push(diag.lastAiResponseText);
930
+ lines.push("```");
931
+ lines.push("");
932
+ }
933
+ if (diag.lastSnapshotPreview) {
934
+ lines.push("**Last Snapshot Preview** (truncated):");
935
+ lines.push("```");
936
+ lines.push(diag.lastSnapshotPreview);
937
+ lines.push("```");
938
+ lines.push("");
939
+ }
940
+ return lines;
941
+ }
942
+ function formatRetryDetails(retryDetails, stepOrdinal) {
943
+ if (!retryDetails || retryDetails.length === 0) return [];
944
+ const lines = [];
945
+ lines.push(`### Step #${stepOrdinal} \u2014 Retry Details`);
946
+ lines.push("");
947
+ for (const detail of retryDetails) {
948
+ lines.push(`#### Attempt ${detail.attempt}`);
949
+ lines.push(`- Snapshot changed: ${detail.snapshotChanged}`);
950
+ lines.push(`- Element count: ${detail.snapshotElementCount}`);
951
+ lines.push(`- Failure reason: ${detail.failureReason}`);
952
+ if (detail.aiReasoning) {
953
+ lines.push(`- AI reasoning: ${detail.aiReasoning}`);
954
+ }
955
+ if (detail.aiResponse) {
956
+ lines.push("- AI response:");
957
+ lines.push("```json");
958
+ lines.push(detail.aiResponse);
959
+ lines.push("```");
960
+ }
961
+ if (detail.aiPrompt) {
962
+ lines.push("");
963
+ lines.push("<details>");
964
+ lines.push(`<summary>AI Prompt (attempt ${detail.attempt})</summary>`);
965
+ lines.push("");
966
+ lines.push("```");
967
+ lines.push(detail.aiPrompt);
968
+ lines.push("```");
969
+ lines.push("</details>");
970
+ }
971
+ if (detail.snapshotPreview) {
972
+ lines.push("");
973
+ lines.push("<details>");
974
+ lines.push(`<summary>Snapshot Preview (attempt ${detail.attempt}, truncated)</summary>`);
975
+ lines.push("");
976
+ lines.push("```");
977
+ lines.push(detail.snapshotPreview);
978
+ lines.push("```");
979
+ lines.push("</details>");
980
+ }
981
+ lines.push("");
982
+ }
983
+ return lines;
984
+ }
985
+ function formatAiAgentSummary(failedSteps, input) {
986
+ const lines = [];
987
+ lines.push("## AI Agent Summary");
988
+ lines.push("");
989
+ lines.push("\u4EE5\u4E0B\u306F AI \u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u5373\u5EA7\u306B\u4FEE\u6B63\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u53D6\u308B\u305F\u3081\u306E\u69CB\u9020\u5316\u60C5\u5831\u3067\u3059\u3002");
990
+ lines.push("");
991
+ lines.push("### \u5931\u6557\u30B9\u30C6\u30C3\u30D7\u3068\u4FEE\u6B63\u6307\u793A");
992
+ lines.push("");
993
+ for (const step of failedSteps) {
994
+ lines.push(`#### Step #${step.ordinal}: ${step.description}`);
995
+ lines.push("");
996
+ lines.push(`- **\u30A8\u30E9\u30FC**: ${step.error ?? "Unknown"}`);
997
+ if (step.failureCategory) {
998
+ lines.push(`- **\u5206\u985E**: \`${step.failureCategory}\``);
999
+ lines.push(`- **\u63A8\u5968\u5BFE\u5FDC**: ${getRecoveryHint(step.failureCategory)}`);
1000
+ }
1001
+ if (step.retryCount !== void 0 && step.retryCount > 0) {
1002
+ lines.push(`- **\u30EA\u30C8\u30E9\u30A4\u56DE\u6570**: ${step.retryCount}`);
1003
+ }
1004
+ if (step.diagnostics) {
1005
+ const diag = step.diagnostics;
1006
+ if (diag.stepAction?.selector) {
1007
+ lines.push(`- **\u73FE\u5728\u306E\u30BB\u30EC\u30AF\u30BF**:`);
1008
+ lines.push(" ```json");
1009
+ lines.push(` ${JSON.stringify(diag.stepAction.selector)}`);
1010
+ lines.push(" ```");
1011
+ }
1012
+ if (diag.lastSnapshotPreview) {
1013
+ lines.push(`- **\u6700\u7D42\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8**:`);
1014
+ lines.push(" <details>");
1015
+ lines.push(" <summary>\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5168\u6587\u3092\u8868\u793A</summary>");
1016
+ lines.push("");
1017
+ lines.push(" ```");
1018
+ lines.push(` ${diag.lastSnapshotPreview}`);
1019
+ lines.push(" ```");
1020
+ lines.push(" </details>");
1021
+ }
1022
+ if (diag.visionFallbackResult) {
1023
+ lines.push(`- **Vision Fallback \u7D50\u679C**: annotations=${diag.visionFallbackResult.annotationCount}, success=${diag.visionFallbackResult.success}`);
1024
+ lines.push(` - \u7406\u7531: ${diag.visionFallbackResult.reasoning}`);
1025
+ }
1026
+ if (diag.agentFallbackResult) {
1027
+ lines.push(`- **Agent Fallback \u7D50\u679C**: strategy=${diag.agentFallbackResult.strategy}, success=${diag.agentFallbackResult.success}`);
1028
+ lines.push(` - \u5206\u6790: ${diag.agentFallbackResult.analysis}`);
1029
+ }
1030
+ }
1031
+ if (input.selfHealSuggestions) {
1032
+ const suggestion = input.selfHealSuggestions.find(
1033
+ (s) => s.stepOrdinal === step.ordinal
1034
+ );
1035
+ if (suggestion) {
1036
+ if (suggestion.runbookFix) {
1037
+ lines.push(`- **YAML \u4FEE\u6B63\u63D0\u6848**: ${suggestion.runbookFix}`);
1038
+ }
1039
+ if (suggestion.contextFix) {
1040
+ lines.push(`- **Context \u4FEE\u6B63\u63D0\u6848**: ${suggestion.contextFix}`);
1041
+ }
1042
+ if (suggestion.suggestedStrategy) {
1043
+ lines.push(`- **\u63A8\u5968\u6226\u7565**: ${suggestion.suggestedStrategy}`);
1044
+ }
1045
+ }
1046
+ }
1047
+ lines.push("");
1048
+ }
1049
+ lines.push("### \u4FEE\u6B63\u5BFE\u8C61\u30D5\u30A1\u30A4\u30EB");
1050
+ lines.push("");
1051
+ lines.push(`- \`${input.runbookPath}\` (Steps: ${failedSteps.map((s) => `#${s.ordinal}`).join(", ")})`);
1052
+ lines.push("");
1053
+ lines.push("### \u4FEE\u6B63\u5F8C\u306E\u691C\u8A3C\u30B3\u30DE\u30F3\u30C9");
1054
+ lines.push("");
1055
+ lines.push("```bash");
1056
+ lines.push(`pnpm execute -- --runbook ${input.runbookPath} --self-heal`);
1057
+ lines.push("```");
1058
+ lines.push("");
1059
+ return lines;
1060
+ }
1061
+ function formatSystemInsights(report, debugEntries) {
1062
+ const lines = [];
1063
+ lines.push("## System Insights");
1064
+ lines.push("");
1065
+ lines.push("> refrain \u306E\u6A5F\u80FD\u6539\u5584\u30FB\u30D0\u30B0\u4FEE\u6B63\u306E\u305F\u3081\u306E\u5206\u6790\u60C5\u5831");
1066
+ lines.push("");
1067
+ const allSteps = flattenSteps2(report.steps);
1068
+ lines.push(...formatRuntimeEnvironment());
1069
+ if (report.aiMetrics && report.aiMetrics.totalCalls > 0) {
1070
+ lines.push(...formatAiMetricsSection(report.aiMetrics));
1071
+ }
1072
+ lines.push(...formatSelectorResolutionStats(debugEntries, allSteps));
1073
+ lines.push(...formatRetryDistribution(allSteps));
1074
+ const failedSteps = allSteps.filter((s) => s.status === "failed");
1075
+ lines.push(...formatFailureCategoryDistribution(failedSteps));
1076
+ lines.push(...formatRecoveryEffectiveness(debugEntries, allSteps));
1077
+ lines.push(...formatPerformanceBottlenecks(allSteps));
1078
+ lines.push(...formatAiSelectorQualityIssues(debugEntries));
1079
+ return lines;
1080
+ }
1081
+ function flattenSteps2(steps) {
1082
+ const result = [];
1083
+ for (const step of steps) {
1084
+ result.push(step);
1085
+ if (step.subStepResults) {
1086
+ for (const iteration of step.subStepResults) {
1087
+ result.push(...flattenSteps2(iteration));
1088
+ }
1089
+ }
1090
+ }
1091
+ return result;
1092
+ }
1093
+ async function generateExecutionReportFile(input) {
1094
+ const { report } = input;
1095
+ const lines = [];
1096
+ lines.push("# Execution Report");
1097
+ lines.push("");
1098
+ lines.push("## Session Info");
1099
+ lines.push(`- **Date**: ${(/* @__PURE__ */ new Date()).toISOString()}`);
1100
+ lines.push(`- **Runbook**: ${report.runbookTitle}`);
1101
+ lines.push(`- **Runbook Path**: ${input.runbookPath}`);
1102
+ lines.push(`- **Start URL**: ${report.startUrl}`);
1103
+ const outcome = report.aborted ? "Aborted" : report.failed > 0 ? "Failed" : input.errorMessage ? "Error" : "Success";
1104
+ lines.push(`- **Outcome**: ${outcome}`);
1105
+ lines.push("");
1106
+ if (input.cliOptions && input.cliOptions.length > 0) {
1107
+ lines.push("## Executor CLI Options");
1108
+ lines.push(...renderOptionsMarkdown(input.cliOptions));
1109
+ lines.push("");
1110
+ }
1111
+ if (input.generationContext) {
1112
+ const gc = input.generationContext;
1113
+ lines.push("## Generation Context");
1114
+ lines.push(`- **Goal**: ${gc.goal}`);
1115
+ lines.push(`- **Start URL**: ${gc.startUrl}`);
1116
+ lines.push(`- **Goal Achieved (at generation)**: ${gc.goalAchieved}`);
1117
+ lines.push(`- **Generated At**: ${gc.generatedAt}`);
1118
+ lines.push(`- **Total Steps (generated)**: ${gc.totalSteps}`);
1119
+ if (gc.notes) {
1120
+ lines.push("- **Notes**:");
1121
+ for (const line of gc.notes.split("\n")) {
1122
+ lines.push(` > ${line}`);
1123
+ }
1124
+ }
1125
+ lines.push("");
1126
+ }
1127
+ if (input.errorMessage) {
1128
+ lines.push("## Error Details");
1129
+ lines.push("```");
1130
+ lines.push(input.errorMessage);
1131
+ lines.push("```");
1132
+ lines.push("");
1133
+ }
1134
+ lines.push("## Execution Summary");
1135
+ lines.push(`- **Total Steps**: ${report.totalSteps}`);
1136
+ lines.push(`- **Executed**: ${report.executed}`);
1137
+ lines.push(`- **Succeeded**: ${report.succeeded}`);
1138
+ lines.push(`- **Failed**: ${report.failed}`);
1139
+ lines.push(`- **Skipped**: ${report.skipped}`);
1140
+ lines.push(`- **Aborted**: ${report.aborted}`);
1141
+ lines.push(`- **Duration**: ${report.totalDurationMs}ms`);
1142
+ lines.push("");
1143
+ const allFlatSteps = flattenSteps2(report.steps);
1144
+ const extractSteps = allFlatSteps.filter((s) => s.actionType === "extract");
1145
+ if (extractSteps.length > 0) {
1146
+ const extractFailures = extractSteps.filter((s) => s.status === "failed");
1147
+ const extractEmpty = extractSteps.filter(
1148
+ (s) => s.status === "success" && (!s.extractedData || s.extractedData === "")
1149
+ );
1150
+ const extractSuccess = extractSteps.filter(
1151
+ (s) => s.status === "success" && s.extractedData && s.extractedData !== ""
1152
+ );
1153
+ lines.push("## Extract Summary");
1154
+ lines.push("");
1155
+ lines.push(`- **Total extract steps**: ${extractSteps.length}`);
1156
+ lines.push(`- **Succeeded (with data)**: ${extractSuccess.length}`);
1157
+ lines.push(`- **Succeeded (empty result)**: ${extractEmpty.length}`);
1158
+ lines.push(`- **Failed**: ${extractFailures.length}`);
1159
+ lines.push("");
1160
+ if (extractFailures.length > 0) {
1161
+ lines.push("### Extract Failures");
1162
+ lines.push("");
1163
+ for (const step of extractFailures) {
1164
+ lines.push(`- **Step #${step.ordinal}** (${step.description}): ${step.error ?? "Unknown"}`);
1165
+ }
1166
+ lines.push("");
1167
+ }
1168
+ if (extractEmpty.length > 0) {
1169
+ lines.push("### Extract Empty Results (Warning)");
1170
+ lines.push("");
1171
+ for (const step of extractEmpty) {
1172
+ lines.push(`- **Step #${step.ordinal}** (${step.description}): Script executed but returned empty`);
1173
+ }
1174
+ lines.push("");
1175
+ }
1176
+ }
1177
+ if (report.steps.length > 0) {
1178
+ lines.push("## Step Results");
1179
+ for (const step of report.steps) {
1180
+ lines.push(...formatStepResult(step, ""));
1181
+ }
1182
+ lines.push("");
1183
+ }
1184
+ const failedSteps = report.steps.filter((s) => s.status === "failed");
1185
+ if (failedSteps.length > 0) {
1186
+ lines.push("## Failed Step Diagnostics");
1187
+ lines.push("");
1188
+ for (const step of failedSteps) {
1189
+ if (step.retryDetails && step.retryDetails.length > 0) {
1190
+ lines.push(...formatRetryDetails(step.retryDetails, step.ordinal));
1191
+ }
1192
+ if (step.diagnostics) {
1193
+ lines.push(...formatDiagnostics(step.diagnostics, step.ordinal));
1194
+ }
1195
+ if (!step.diagnostics && (!step.retryDetails || step.retryDetails.length === 0)) {
1196
+ lines.push(`### Step #${step.ordinal} \u2014 Error Info`);
1197
+ lines.push("");
1198
+ lines.push(`**Error**: ${step.error ?? "Unknown"}`);
1199
+ if (step.failureCategory) {
1200
+ lines.push(`**Category**: ${step.failureCategory}`);
1201
+ }
1202
+ lines.push("");
1203
+ }
1204
+ }
1205
+ }
1206
+ if (failedSteps.length > 0) {
1207
+ lines.push(...formatAiAgentSummary(failedSteps, input));
1208
+ }
1209
+ const debugEntries = input.debugLogPath ? await readDebugLog(input.debugLogPath) : [];
1210
+ lines.push(...formatSystemInsights(report, debugEntries));
1211
+ const failedOrdinals = new Set(failedSteps.map((s) => s.ordinal));
1212
+ lines.push(...formatDebugLogSections(debugEntries, {
1213
+ showFailedStepEvents: true,
1214
+ failedOrdinals
1215
+ }));
1216
+ return lines.join("\n");
1217
+ }
1218
+
1219
+ // src/runbook-executor/index.ts
1220
+ import { writeFile, mkdir } from "fs/promises";
1221
+ import { dirname } from "path";
1222
+ async function writeReportFile(runbookPath, report, options) {
1223
+ const reportContent = await generateExecutionReportFile({
1224
+ runbookPath,
1225
+ report,
1226
+ errorMessage: options.errorMessage,
1227
+ debugLogPath: options.debugLogPath,
1228
+ cliOptions: options.cliOptions,
1229
+ generationContext: options.generationContext,
1230
+ selfHealSuggestions: options.selfHealSuggestions
1231
+ });
1232
+ const reportPath = runbookPath.replace(/\.\w+$/, "-report.md");
1233
+ await mkdir(dirname(reportPath), { recursive: true });
1234
+ await writeFile(reportPath, reportContent, "utf-8");
1235
+ return reportPath;
1236
+ }
1237
+ async function main() {
1238
+ const totalStart = performance.now();
1239
+ const config = await parseArgs();
1240
+ const apiKey = getApiKey(config.apiKey);
1241
+ const tierPlan = await resolvePlan(apiKey, {
1242
+ serverUrl: config.apiServerUrl,
1243
+ forceRefresh: config.refreshKey
1244
+ });
1245
+ config.plan = tierPlan;
1246
+ applyExecutorDefaults(config);
1247
+ enforceFeatureGates(config, tierPlan);
1248
+ await initModel(config.aiModelConfig);
1249
+ const cliOptions = serializeExecutorOptions(config);
1250
+ const approvalMode = config.approvalMode ?? "web";
1251
+ const needsChatBot = approvalMode !== "web" || !!config.notifyMode;
1252
+ const sharedBot = needsChatBot ? new SharedChatBot(
1253
+ loadChatConfig(config.notifyMode ?? approvalMode, {
1254
+ callbackPort: config.callbackPort,
1255
+ approvalTimeoutMs: config.approvalTimeoutMs
1256
+ })
1257
+ ) : void 0;
1258
+ const confirmationProvider = createConfirmationProvider(
1259
+ approvalMode,
1260
+ sharedBot,
1261
+ config.approvalTimeoutMs
1262
+ );
1263
+ const notificationProvider = createNotificationProvider(config.notifyMode, sharedBot);
1264
+ intro(config.selfHeal ? "runbook-executor (self-heal)" : "runbook-executor");
1265
+ log.info(`Plan: ${formatPlanLabel(tierPlan)}`);
1266
+ log.info(`Runbook: ${config.runbookPath}`);
1267
+ log.info(`Headless: ${config.headless}`);
1268
+ if (config.stealth) {
1269
+ log.info(t("cli.stealthEnabled"));
1270
+ }
1271
+ if (config.proxy) {
1272
+ log.info(t("cli.proxyEnabled").replace("{proxy}", config.proxy.replace(/:\/\/[^@]+@/, "://***@")));
1273
+ }
1274
+ if (config.selfHeal) {
1275
+ log.info("Mode: self-heal");
1276
+ log.info(`Max retries: ${config.maxRetries ?? 5}`);
1277
+ log.info(`Retry warning threshold: ${config.retryWarningThreshold ?? 3}`);
1278
+ log.info(`Selector cache: ${config.enableSelectorCache ? "enabled" : "disabled"}`);
1279
+ log.info(`Agent fallback: ${config.enableAgentFallback ? "enabled" : "disabled"}`);
1280
+ log.info(`Vision fallback: ${config.enableVisionFallback ? "enabled" : "disabled"}`);
1281
+ }
1282
+ if (config.stepDelay !== void 0) {
1283
+ log.info(`Step delay: ${config.stepDelay}ms`);
1284
+ }
1285
+ if (config.screenshotDir) {
1286
+ log.info(`Screenshots: ${config.screenshotDir}`);
1287
+ }
1288
+ log.info(`Context: loaded (${config.contextMarkdown.split("\n").length} lines)`);
1289
+ if (config.secrets && Object.keys(config.secrets.values).length > 0) {
1290
+ log.info(`Secrets: ${Object.keys(config.secrets.values).length} keys loaded`);
1291
+ }
1292
+ if (config.skipConfirmation) {
1293
+ log.info(`Skip confirmation: true`);
1294
+ }
1295
+ if (approvalMode !== "web") {
1296
+ log.info(`Approval mode: ${approvalMode}`);
1297
+ }
1298
+ if (config.notifyMode) {
1299
+ log.info(`Notify: ${config.notifyMode}`);
1300
+ }
1301
+ if (config.forceReport) {
1302
+ log.info("Report: forced");
1303
+ }
1304
+ const cliLogger = cliLoggerFactory.createLogger();
1305
+ const cliCreateSpinner = () => cliLoggerFactory.createSpinner();
1306
+ const runbook = await loadAndLogRunbook(config.runbookPath);
1307
+ config.skills = config.skills && config.skills.length > 0 ? config.skills : runbook.metadata.skills;
1308
+ let skillInstances;
1309
+ if (config.skills && config.skills.length > 0) {
1310
+ await initBuiltinSkills();
1311
+ skillInstances = createSkills(config.skills);
1312
+ log.info(`Skills: ${config.skills.join(", ")}`);
1313
+ }
1314
+ validateStepLimit(runbook.steps.length, tierPlan);
1315
+ const generationContext = {
1316
+ goal: runbook.metadata.goal,
1317
+ startUrl: runbook.metadata.startUrl,
1318
+ goalAchieved: runbook.metadata.goalAchieved,
1319
+ generatedAt: runbook.metadata.generatedAt,
1320
+ totalSteps: runbook.metadata.totalSteps,
1321
+ notes: runbook.notes
1322
+ };
1323
+ const selectorCache = config.enableSelectorCache ? await loadCache(config.runbookPath) : null;
1324
+ const jobTimeoutMs = tierPlan.limits.maxJobDurationMs;
1325
+ const abortController = new AbortController();
1326
+ let timeoutId;
1327
+ if (Number.isFinite(jobTimeoutMs)) {
1328
+ timeoutId = setTimeout(() => abortController.abort(), jobTimeoutMs);
1329
+ }
1330
+ try {
1331
+ const plan = await planExecution(runbook, config);
1332
+ let dataRows;
1333
+ if (config.dataFilePath) {
1334
+ dataRows = await loadDataFile(config.dataFilePath);
1335
+ validateBatchLimit(dataRows.length, tierPlan);
1336
+ log.info(`Data file: ${dataRows.length} rows`);
1337
+ }
1338
+ displayPlan(plan, runbook, dataRows?.length);
1339
+ const confirmed = await confirmPlan();
1340
+ if (!confirmed) {
1341
+ cancel(t("cli.executionCancelled"));
1342
+ process.exit(0);
1343
+ }
1344
+ const store = new RuntimeStore();
1345
+ store.seed(plan.resolvedVariables.values, plan.resolvedVariables.sensitiveKeys);
1346
+ if (config.dataFilePath && dataRows) {
1347
+ const batchReport = await executeBatch(
1348
+ config,
1349
+ runbook,
1350
+ store,
1351
+ dataRows,
1352
+ config.reuseSession !== false,
1353
+ {
1354
+ confirmationProvider,
1355
+ selectorCache,
1356
+ logger: cliLogger,
1357
+ createSpinner: cliCreateSpinner,
1358
+ abortSignal: abortController.signal,
1359
+ skills: skillInstances
1360
+ }
1361
+ );
1362
+ if (config.selfHeal) {
1363
+ printBatchReport(batchReport);
1364
+ for (const row of batchReport.rows) {
1365
+ if (row.report.failed > 0 || row.report.aborted) {
1366
+ const debugReport = await analyzeDebugResult(
1367
+ row.report,
1368
+ runbook,
1369
+ config.retryWarningThreshold ?? 3,
1370
+ config.contextMarkdown
1371
+ );
1372
+ log.info(`
1373
+ \u2500\u2500 ${row.rowLabel} Self-Heal Analysis \u2500\u2500`);
1374
+ printDebugReport(debugReport);
1375
+ }
1376
+ }
1377
+ await safeNotify(() => notificationProvider.notifyComplete(batchReport));
1378
+ } else {
1379
+ printBatchReport(batchReport);
1380
+ await safeNotify(() => notificationProvider.notifyComplete(batchReport));
1381
+ }
1382
+ if (batchReport.failed > 0 || config.forceReport) {
1383
+ for (const row of batchReport.rows) {
1384
+ if (row.report.failed > 0 || row.report.aborted || config.forceReport) {
1385
+ const reportPath = await writeReportFile(config.runbookPath, row.report, {
1386
+ debugLogPath: config.debugLogPath,
1387
+ cliOptions,
1388
+ generationContext
1389
+ });
1390
+ log.info(`Report written to: ${reportPath} (${row.rowLabel})`);
1391
+ await forwardReportToSlack(reportPath, batchReport.runbookTitle ?? row.rowLabel);
1392
+ if (!config.forceReport) break;
1393
+ }
1394
+ }
1395
+ }
1396
+ outro(`Total: ${formatDuration(performance.now() - totalStart)}`);
1397
+ if (batchReport.failed > 0) process.exit(1);
1398
+ } else {
1399
+ const dataStore = new InMemoryDataStore();
1400
+ const downloadManager = config.outputDir ? new DownloadManager(config.outputDir) : void 0;
1401
+ const execStart = performance.now();
1402
+ const report = await execute(config, runbook, {
1403
+ store,
1404
+ confirmationProvider,
1405
+ dataStore,
1406
+ downloadManager,
1407
+ selectorCache,
1408
+ logger: cliLogger,
1409
+ createSpinner: cliCreateSpinner,
1410
+ abortSignal: abortController.signal,
1411
+ skills: skillInstances
1412
+ });
1413
+ log.info(`Execution completed (${formatDuration(performance.now() - execStart)})`);
1414
+ const metricsSummary = globalMetrics.getSummary();
1415
+ if (metricsSummary.totalCalls > 0) {
1416
+ report.aiMetrics = metricsSummary;
1417
+ }
1418
+ if (dataStore.listCollections().length > 0 && config.outputDir) {
1419
+ for (const collection of dataStore.listCollections()) {
1420
+ await dataStore.writeToFile(collection, `${config.outputDir}/${collection}.json`, "json");
1421
+ await dataStore.writeToFile(collection, `${config.outputDir}/${collection}.csv`, "csv");
1422
+ log.info(`Memory data exported: ${config.outputDir}/${collection}.json, ${config.outputDir}/${collection}.csv`);
1423
+ }
1424
+ }
1425
+ if (config.mergeDownloads && downloadManager && downloadManager.getAllDownloadIds().length > 0) {
1426
+ const ids = downloadManager.getAllDownloadIds();
1427
+ const mergedPath = await downloadManager.mergeCSVFiles(ids, "merged.csv");
1428
+ log.info(`CSV files merged: ${mergedPath}`);
1429
+ }
1430
+ let selfHealSuggestions;
1431
+ if (config.selfHeal) {
1432
+ const analysisStart = performance.now();
1433
+ const debugReport = await analyzeDebugResult(
1434
+ report,
1435
+ runbook,
1436
+ config.retryWarningThreshold ?? 3,
1437
+ config.contextMarkdown
1438
+ );
1439
+ log.info(`Analysis completed (${formatDuration(performance.now() - analysisStart)})`);
1440
+ printDebugReport(debugReport);
1441
+ selfHealSuggestions = debugReport.suggestions;
1442
+ const hasIssues = report.failed > 0 || report.aborted || debugReport.warningSteps.length > 0;
1443
+ if (hasIssues) {
1444
+ await safeNotify(() => notificationProvider.notifyDebugFailure?.(debugReport) ?? Promise.resolve());
1445
+ }
1446
+ } else {
1447
+ printReport(report);
1448
+ await safeNotify(() => notificationProvider.notifyComplete(report));
1449
+ }
1450
+ if (report.failed > 0 || report.aborted || config.forceReport) {
1451
+ const reportPath = await writeReportFile(config.runbookPath, report, {
1452
+ debugLogPath: config.debugLogPath,
1453
+ cliOptions,
1454
+ generationContext,
1455
+ selfHealSuggestions
1456
+ });
1457
+ log.info(`Report written to: ${reportPath}`);
1458
+ await forwardReportToSlack(reportPath, report.runbookTitle);
1459
+ }
1460
+ outro(`Total: ${formatDuration(performance.now() - totalStart)}`);
1461
+ if (report.failed > 0 || report.aborted) process.exit(1);
1462
+ }
1463
+ } finally {
1464
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
1465
+ if (selectorCache) {
1466
+ await saveCache(selectorCache);
1467
+ }
1468
+ await confirmationProvider.dispose?.();
1469
+ await notificationProvider.dispose?.();
1470
+ await sharedBot?.dispose();
1471
+ }
1472
+ }
1473
+ main().catch((error) => {
1474
+ cancel(`Fatal error: ${error instanceof Error ? error.message : String(error)}`);
1475
+ if (error instanceof Error && error.stack) {
1476
+ console.error(error.stack);
1477
+ }
1478
+ process.exit(1);
1479
+ });
1480
+ //# sourceMappingURL=runbook-executor-K7T6RJWJ.js.map