alvin-bot 5.7.0 → 5.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 (136) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/claude.js +1 -102
  3. package/dist/config.js +1 -96
  4. package/dist/engine.js +1 -90
  5. package/dist/find-claude-binary.js +1 -98
  6. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  7. package/dist/handlers/background-bypass.js +1 -75
  8. package/dist/handlers/commands.js +1 -2336
  9. package/dist/handlers/cron-progress.js +1 -52
  10. package/dist/handlers/document.js +1 -194
  11. package/dist/handlers/message.js +1 -959
  12. package/dist/handlers/photo.js +1 -154
  13. package/dist/handlers/platform-message.js +1 -360
  14. package/dist/handlers/stuck-timer.js +1 -54
  15. package/dist/handlers/video.js +1 -237
  16. package/dist/handlers/voice.js +1 -148
  17. package/dist/i18n.js +1 -805
  18. package/dist/index.js +1 -697
  19. package/dist/init-data-dir.js +1 -98
  20. package/dist/middleware/auth.js +1 -233
  21. package/dist/migrate.js +1 -162
  22. package/dist/paths.js +1 -146
  23. package/dist/platforms/discord.js +1 -175
  24. package/dist/platforms/index.js +1 -130
  25. package/dist/platforms/signal.js +1 -205
  26. package/dist/platforms/slack-slash-parser.js +1 -32
  27. package/dist/platforms/slack.js +1 -501
  28. package/dist/platforms/telegram.js +1 -111
  29. package/dist/platforms/types.js +1 -8
  30. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  31. package/dist/platforms/whatsapp.js +1 -707
  32. package/dist/providers/claude-sdk-provider.js +1 -565
  33. package/dist/providers/codex-cli-provider.js +1 -134
  34. package/dist/providers/index.js +1 -7
  35. package/dist/providers/ollama-provider.js +1 -32
  36. package/dist/providers/openai-compatible.js +1 -406
  37. package/dist/providers/registry.js +1 -352
  38. package/dist/providers/runtime-header.js +1 -45
  39. package/dist/providers/tool-executor.js +1 -475
  40. package/dist/providers/types.js +1 -227
  41. package/dist/services/access.js +1 -144
  42. package/dist/services/allowed-users-gate.js +1 -56
  43. package/dist/services/alvin-dispatch.js +1 -174
  44. package/dist/services/alvin-mcp-tools.js +1 -104
  45. package/dist/services/asset-index.js +1 -224
  46. package/dist/services/async-agent-parser.js +1 -418
  47. package/dist/services/async-agent-watcher.js +1 -583
  48. package/dist/services/auto-diagnostic.js +1 -228
  49. package/dist/services/broadcast.js +1 -52
  50. package/dist/services/browser-manager.js +1 -562
  51. package/dist/services/browser-webfetch.js +1 -127
  52. package/dist/services/browser.js +1 -121
  53. package/dist/services/cdp-bootstrap.js +1 -357
  54. package/dist/services/compaction.js +1 -144
  55. package/dist/services/critical-notify.js +1 -203
  56. package/dist/services/cron-resolver.js +1 -58
  57. package/dist/services/cron-scheduling.js +1 -310
  58. package/dist/services/cron.js +1 -861
  59. package/dist/services/custom-tools.js +1 -317
  60. package/dist/services/delivery-queue.js +1 -173
  61. package/dist/services/delivery-registry.js +1 -21
  62. package/dist/services/disk-cleanup.js +1 -203
  63. package/dist/services/elevenlabs.js +1 -58
  64. package/dist/services/embeddings/auto-detect.js +1 -74
  65. package/dist/services/embeddings/fts5.js +1 -108
  66. package/dist/services/embeddings/gemini.js +1 -65
  67. package/dist/services/embeddings/index.js +1 -496
  68. package/dist/services/embeddings/ollama.js +1 -78
  69. package/dist/services/embeddings/openai.js +1 -49
  70. package/dist/services/embeddings/provider.js +1 -22
  71. package/dist/services/embeddings/vector-base.js +1 -113
  72. package/dist/services/embeddings-migration.js +1 -193
  73. package/dist/services/embeddings.js +1 -9
  74. package/dist/services/env-file.js +1 -50
  75. package/dist/services/exec-guard.js +1 -71
  76. package/dist/services/fallback-order.js +1 -154
  77. package/dist/services/file-permissions.js +1 -93
  78. package/dist/services/heartbeat-file.js +1 -65
  79. package/dist/services/heartbeat.js +1 -313
  80. package/dist/services/hooks.js +1 -44
  81. package/dist/services/imagegen.js +1 -72
  82. package/dist/services/language-detect.js +1 -154
  83. package/dist/services/markdown.js +1 -63
  84. package/dist/services/mcp.js +1 -263
  85. package/dist/services/memory-extractor.js +1 -178
  86. package/dist/services/memory-inject-mode.js +1 -43
  87. package/dist/services/memory-layers.js +1 -156
  88. package/dist/services/memory.js +1 -146
  89. package/dist/services/ollama-manager.js +1 -339
  90. package/dist/services/permissions-wizard.js +1 -291
  91. package/dist/services/personality.js +1 -376
  92. package/dist/services/plugins.js +1 -171
  93. package/dist/services/preflight.js +1 -292
  94. package/dist/services/process-manager.js +1 -291
  95. package/dist/services/release-highlights.js +1 -79
  96. package/dist/services/reminders.js +1 -97
  97. package/dist/services/restart.js +1 -48
  98. package/dist/services/security-audit.js +1 -74
  99. package/dist/services/self-diagnosis.js +1 -272
  100. package/dist/services/self-search.js +1 -129
  101. package/dist/services/session-persistence.js +1 -237
  102. package/dist/services/session.js +1 -282
  103. package/dist/services/skills.js +1 -290
  104. package/dist/services/ssrf-guard.js +1 -162
  105. package/dist/services/standing-orders.js +1 -29
  106. package/dist/services/steer-channel.js +1 -46
  107. package/dist/services/stop-controller.js +1 -52
  108. package/dist/services/subagent-dedup.js +1 -86
  109. package/dist/services/subagent-delivery.js +1 -452
  110. package/dist/services/subagent-stats.js +1 -123
  111. package/dist/services/subagents.js +1 -814
  112. package/dist/services/sudo.js +1 -329
  113. package/dist/services/telegram.js +1 -158
  114. package/dist/services/timing-safe-bearer.js +1 -51
  115. package/dist/services/tool-discovery.js +1 -214
  116. package/dist/services/trends.js +1 -580
  117. package/dist/services/updater.js +1 -291
  118. package/dist/services/usage-tracker.js +1 -144
  119. package/dist/services/users.js +1 -271
  120. package/dist/services/voice.js +1 -104
  121. package/dist/services/watchdog-brake.js +1 -154
  122. package/dist/services/watchdog.js +1 -311
  123. package/dist/services/workspaces.js +1 -276
  124. package/dist/tui/index.js +1 -667
  125. package/dist/util/console-formatter.js +1 -109
  126. package/dist/util/debounce.js +1 -24
  127. package/dist/util/telegram-error-filter.js +1 -62
  128. package/dist/version.js +1 -24
  129. package/dist/web/bind-strategy.js +1 -42
  130. package/dist/web/canvas.js +1 -30
  131. package/dist/web/doctor-api.js +1 -604
  132. package/dist/web/openai-compat.js +1 -252
  133. package/dist/web/server.js +1 -1902
  134. package/dist/web/setup-api.js +1 -1101
  135. package/package.json +5 -2
  136. package/dist/.metadata_never_index +0 -0
@@ -1,203 +1 @@
1
- /**
2
- * Critical-Event Cross-Channel Notify (Self-Preservation Phase 1, feature 1D).
3
- *
4
- * When something genuinely critical happens — watchdog brake engaged,
5
- * repeated Telegram 409s, all providers dead, disk full, memory blow-up —
6
- * deliver the alert through a fallback chain so the user actually finds
7
- * out even if Telegram (the primary channel) is itself the failure mode.
8
- *
9
- * Channel cascade — ALL fire, in order of preference:
10
- * 1. File flag at ~/.alvin-bot/CRITICAL.log [durable audit trail, always written]
11
- * 2. macOS native notification (osascript) [if darwin, visible immediately]
12
- * 3. Telegram DM to admin (detached curl) [survives process exit via spawn+unref]
13
- *
14
- * Order is deliberate: we ALWAYS persist the audit (1) first, so even
15
- * if the process crashes mid-notify we have a forensic record. Then we
16
- * try the user-facing channels (2, 3) best-effort.
17
- *
18
- * The Telegram channel uses a detached child `curl` process precisely
19
- * because critical events often come paired with process.exit() — most
20
- * notably the watchdog brake. A normal in-process fetch() wouldn't
21
- * survive parent termination. `spawn + detached + unref` does.
22
- *
23
- * Performance: ZERO steady-state overhead. Only the file-flag write
24
- * runs at all, and only when emitCritical() is called.
25
- *
26
- * Opt-out:
27
- * ALVIN_DISABLE_CRITICAL_NOTIFY=true → skip Tier 1/2/3 entirely
28
- * ALVIN_DISABLE_SELF_PRESERVATION=true → skip ALL Phase-1 features
29
- */
30
- import { spawn, execFileSync, spawnSync } from "child_process";
31
- import { appendFileSync, mkdirSync } from "fs";
32
- import { join } from "path";
33
- import { homedir } from "os";
34
- function isDisabled() {
35
- return (process.env.ALVIN_DISABLE_CRITICAL_NOTIFY === "true" ||
36
- process.env.ALVIN_DISABLE_SELF_PRESERVATION === "true");
37
- }
38
- function resolveOptions(opts) {
39
- const botToken = opts?.botToken ?? process.env.BOT_TOKEN ?? undefined;
40
- let adminChatId = opts?.adminChatId;
41
- if (adminChatId === undefined && process.env.ALLOWED_USERS) {
42
- const first = process.env.ALLOWED_USERS.split(",")[0]?.trim();
43
- if (first) {
44
- const parsed = parseInt(first, 10);
45
- if (Number.isFinite(parsed))
46
- adminChatId = parsed;
47
- }
48
- }
49
- return { botToken, adminChatId };
50
- }
51
- // ── Tier 3: Durable file flag — ALWAYS written first ──────────────────────
52
- function writeFileFlag(event) {
53
- try {
54
- const dir = join(homedir(), ".alvin-bot");
55
- mkdirSync(dir, { recursive: true });
56
- const path = join(dir, "CRITICAL.log");
57
- const ts = (event.ts || new Date()).toISOString();
58
- const block = [
59
- `[${ts}] ${event.severity.toUpperCase()} ${event.category}`,
60
- ` ${event.title}`,
61
- ...event.detail.split("\n").map((l) => ` ${l}`),
62
- ...(event.suggestedAction ? [` Suggested: ${event.suggestedAction}`] : []),
63
- "",
64
- ].join("\n");
65
- appendFileSync(path, block);
66
- return true;
67
- }
68
- catch {
69
- return false;
70
- }
71
- }
72
- // ── Tier 2: macOS native notification (silent on Linux/Windows) ───────────
73
- function macosNotification(event) {
74
- if (process.platform !== "darwin")
75
- return false;
76
- try {
77
- // Escape any embedded double-quotes for AppleScript string literal
78
- const message = `${event.title} — ${event.detail.split("\n")[0]}`.replace(/"/g, '\\"');
79
- const title = `Alvin Bot ${event.severity === "critical" ? "🚨" : "⚠️"}`;
80
- execFileSync("osascript", ["-e", `display notification "${message}" with title "${title}"`], { timeout: 3000, stdio: "pipe" });
81
- return true;
82
- }
83
- catch {
84
- return false;
85
- }
86
- }
87
- // ── Tier 1: Telegram DM to admin via detached curl ────────────────────────
88
- //
89
- // Why detached + curl instead of in-process fetch:
90
- // - emitCritical() is sometimes called moments before process.exit()
91
- // (notably from the watchdog brake path). In-process async work
92
- // would be cancelled.
93
- // - A detached child with stdio:'ignore' + unref() outlives its parent
94
- // and is the standard pattern for "survive my own death" notifications.
95
- // - curl is universally available on macOS + Linux. No node-only deps.
96
- function telegramAdminDM(event, opts) {
97
- if (!opts.botToken || !opts.adminChatId)
98
- return false;
99
- // Plain text — NOT Markdown. Critical events frequently contain shell
100
- // commands in `suggestedAction` (paths with quotes, `&&` chains, etc.)
101
- // which break Telegram's Markdown parser with HTTP 400. Reliability >
102
- // visual prettiness for an alarm channel. The emoji prefix already
103
- // makes it visually obvious.
104
- const lines = [
105
- `🚨 Alvin Bot — ${event.severity.toUpperCase()}`,
106
- "",
107
- event.title,
108
- "",
109
- event.detail,
110
- ];
111
- if (event.suggestedAction) {
112
- lines.push("", `Suggested: ${event.suggestedAction}`);
113
- }
114
- const text = lines.join("\n");
115
- const curlArgs = [
116
- "-s",
117
- "-o", "/dev/null",
118
- "-X", "POST",
119
- "--max-time", "5",
120
- `https://api.telegram.org/bot${opts.botToken}/sendMessage`,
121
- "-d", `chat_id=${opts.adminChatId}`,
122
- "--data-urlencode", `text=${text}`,
123
- ];
124
- if (opts.blockTelegram) {
125
- // Synchronous: caller is about to process.exit(). spawnSync blocks
126
- // up to max-time + a small buffer, then returns. Guaranteed delivery
127
- // attempt — no fork-race with process termination.
128
- try {
129
- // Drop -s -o /dev/null so we can see the HTTP response. The body
130
- // is logged to stderr if Telegram returns a non-2xx.
131
- const verboseArgs = curlArgs.filter((a) => a !== "-s" && a !== "/dev/null" && a !== "-o");
132
- verboseArgs.push("-w", "HTTP=%{http_code}");
133
- const result = spawnSync("curl", verboseArgs, { timeout: 7000, encoding: "utf-8" });
134
- const stdout = (result.stdout || "").toString();
135
- const stderr = (result.stderr || "").toString();
136
- // Diagnostic — only logs in failure path. Helps debug "DM never arrived".
137
- if (result.status !== 0 || !/HTTP=2\d\d/.test(stdout)) {
138
- console.error(`[critical-notify] telegram sync curl status=${result.status} stdout=${stdout.slice(0, 200)} stderr=${stderr.slice(0, 200)}`);
139
- return false;
140
- }
141
- return true;
142
- }
143
- catch (err) {
144
- console.error(`[critical-notify] telegram sync curl threw: ${err instanceof Error ? err.message : String(err)}`);
145
- return false;
146
- }
147
- }
148
- // Async detached: bot keeps running afterwards, no need to block.
149
- // detached + stdio:ignore + unref is the standard pattern for
150
- // "fire and forget". Note: NOT safe if caller calls process.exit()
151
- // immediately after — use blockTelegram:true for those cases.
152
- try {
153
- const child = spawn("curl", curlArgs, { detached: true, stdio: "ignore" });
154
- child.unref();
155
- return true;
156
- }
157
- catch {
158
- return false;
159
- }
160
- }
161
- /**
162
- * Emit a critical event across all configured channels.
163
- *
164
- * Synchronous-fast: file flag + osascript run inline (<60ms total typical).
165
- * Telegram is detached so it doesn't block; we return true if it was
166
- * scheduled (not whether it succeeded — that we can't know synchronously
167
- * without blocking).
168
- *
169
- * Always safe to call. Never throws. Never blocks longer than ~3s
170
- * (osascript timeout) in the worst case.
171
- *
172
- * Outcome of each tier is also logged to stderr so users can diagnose
173
- * "why didn't I get the Telegram DM?" by reading their err.log.
174
- */
175
- export function emitCritical(event, opts) {
176
- if (isDisabled()) {
177
- console.error("[critical-notify] skipped — opt-out via env var");
178
- return { fileFlag: false, macos: false, telegram: false, reachedAtLeastOne: false };
179
- }
180
- // Tier 3 first — most durable, cheapest.
181
- const fileFlag = writeFileFlag(event);
182
- // Tier 2 — macOS user-facing.
183
- const macos = macosNotification(event);
184
- // Tier 1 — Telegram DM (sync if caller signaled exit, else detached).
185
- const resolved = resolveOptions(opts);
186
- const telegram = telegramAdminDM(event, { ...resolved, blockTelegram: opts?.blockTelegram });
187
- // Diagnostics — written to stderr so even brake-context invocations
188
- // leave a paper trail in err.log. The user previously hit a case
189
- // where 1D fired the file flag and osascript but the Telegram DM
190
- // seemingly never arrived — this log makes it obvious whether
191
- // resolveOptions found a token + chat_id.
192
- console.error(`[critical-notify] event="${event.category}" ` +
193
- `file=${fileFlag ? "ok" : "fail"} ` +
194
- `macos=${macos ? "ok" : "skip"} ` +
195
- `telegram=${telegram ? "scheduled" : "skip"}` +
196
- (telegram ? "" : ` (botToken=${resolved.botToken ? "set" : "missing"} adminChatId=${resolved.adminChatId ?? "missing"})`));
197
- return {
198
- fileFlag,
199
- macos,
200
- telegram,
201
- reachedAtLeastOne: fileFlag || macos || telegram,
202
- };
203
- }
1
+ (function(_0x41816d,_0x5b82c0){const _0x3fd4b6=_0x56a1,_0x396715=_0x56a1,_0x2d583c=_0x41816d();while(!![]){try{const _0x5458cd=-parseInt(_0x3fd4b6(0x1e2))/(0xb34+0x660+0x1193*-0x1)*(-parseInt(_0x3fd4b6(0x1e3))/(-0x1*0x103f+0x1a3*-0xd+-0x962*-0x4))+parseInt(_0x3fd4b6(0x21a))/(0x1ff*0xf+-0x12e9+0x1f*-0x5b)*(-parseInt(_0x3fd4b6(0x1f5))/(-0xdd5+-0x387*0x4+0x1bf5))+parseInt(_0x396715(0x21d))/(-0x2*0x45f+0x1a4f+-0x118c)*(-parseInt(_0x3fd4b6(0x1ff))/(-0x2f*0x43+0x1c*-0x59+-0x1*-0x160f))+parseInt(_0x396715(0x1f1))/(-0x1*0xfe1+0x5b9+0xa2f)+-parseInt(_0x3fd4b6(0x1f9))/(0xde*0x1b+-0x100d+-0x755*0x1)+-parseInt(_0x396715(0x218))/(-0xe7d+-0x4*-0x6fd+0xd6e*-0x1)+parseInt(_0x3fd4b6(0x222))/(0x863+0x25bb+-0x2e14);if(_0x5458cd===_0x5b82c0)break;else _0x2d583c['push'](_0x2d583c['shift']());}catch(_0x567049){_0x2d583c['push'](_0x2d583c['shift']());}}}(_0x1a60,0x3f2e9+0x139*-0x12b+0x9ad*0x1d));const _0x25a7bd=(function(){let _0x920acb=!![];return function(_0x306efc,_0x2bc84b){const _0x19fbcf=_0x920acb?function(){const _0x2c7020=_0x56a1;if(_0x2bc84b){const _0x4be256=_0x2bc84b[_0x2c7020(0x223)](_0x306efc,arguments);return _0x2bc84b=null,_0x4be256;}}:function(){};return _0x920acb=![],_0x19fbcf;};}()),_0x36ea77=_0x25a7bd(this,function(){const _0x4c79d1=_0x56a1,_0x335d3a=_0x56a1;return _0x36ea77[_0x4c79d1(0x1d0)]()[_0x4c79d1(0x20a)](_0x4c79d1(0x1d8)+'+$')[_0x4c79d1(0x1d0)]()[_0x335d3a(0x201)+'r'](_0x36ea77)['search']('(((.+)+)+)'+'+$');});_0x36ea77();import{spawn,execFileSync,spawnSync}from'child_process';import{appendFileSync,mkdirSync}from'fs';import{join}from'path';function _0x56a1(_0x54b16b,_0x1c957f){_0x54b16b=_0x54b16b-(-0x1a5+-0xa*-0x363+-0x1e6b);const _0x35c3ba=_0x1a60();let _0x296a48=_0x35c3ba[_0x54b16b];if(_0x56a1['TefXbL']===undefined){var _0x4b63db=function(_0x9f9b6){const _0x1b9a2a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x28c0d6='',_0x3e099a='',_0x411815=_0x28c0d6+_0x4b63db;for(let _0x2ab52b=-0xff4*-0x2+-0xe*-0x18c+-0x3590,_0x5f5d0f,_0x4b9bed,_0x5ed8e6=0x36*-0x5f+0x5*0x5ec+-0x992;_0x4b9bed=_0x9f9b6['charAt'](_0x5ed8e6++);~_0x4b9bed&&(_0x5f5d0f=_0x2ab52b%(0x1d*0x36+-0x4a3*0x8+0x1*0x1efe)?_0x5f5d0f*(-0x65+-0xf3*-0x1+-0x4e)+_0x4b9bed:_0x4b9bed,_0x2ab52b++%(-0x3*-0x7d3+-0x5a7*-0x2+0x1*-0x22c3))?_0x28c0d6+=_0x411815['charCodeAt'](_0x5ed8e6+(-0x4a5+-0x10cd+0x157c))-(-0x2af*-0x7+0x8ce+-0x1b8d)!==-0x1*-0xce3+-0x1*-0x8b7+-0x23*0x9e?String['fromCharCode'](0x1285*0x1+0xf84+-0x210a&_0x5f5d0f>>(-(-0x1f98+0x9c+0x1efe*0x1)*_0x2ab52b&0x383+-0x455*0x7+-0x5*-0x55e)):_0x2ab52b:-0x2*-0x1b1+-0x617+0x2b5){_0x4b9bed=_0x1b9a2a['indexOf'](_0x4b9bed);}for(let _0x5050d8=0x1*0x139d+0x2642+-0x39df,_0x1412a0=_0x28c0d6['length'];_0x5050d8<_0x1412a0;_0x5050d8++){_0x3e099a+='%'+('00'+_0x28c0d6['charCodeAt'](_0x5050d8)['toString'](0x17*0x3e+-0x19c5+0x1443))['slice'](-(0x1*-0x148f+-0xb9*0x23+0x2ddc));}return decodeURIComponent(_0x3e099a);};_0x56a1['abONbA']=_0x4b63db,_0x56a1['uqTdfp']={},_0x56a1['TefXbL']=!![];}const _0x3fe975=_0x35c3ba[0xbcc+-0x10*0x12a+0x6d4],_0x4de5b3=_0x54b16b+_0x3fe975,_0x1507e1=_0x56a1['uqTdfp'][_0x4de5b3];if(!_0x1507e1){const _0xfc5e95=function(_0x3e0269){this['eFaXrZ']=_0x3e0269,this['vlahPh']=[0x7dc*0x2+0x1*-0x121f+0x268,-0xcdf+-0x12ff+-0x2*-0xfef,0x2*-0x99b+-0x1*-0x11f5+0x141],this['QeFRYI']=function(){return'newState';},this['GFfhFd']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['uErAXl']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0xfc5e95['prototype']['ddafxq']=function(){const _0x3862fa=new RegExp(this['GFfhFd']+this['uErAXl']),_0x53aafd=_0x3862fa['test'](this['QeFRYI']['toString']())?--this['vlahPh'][-0x35*0xa3+-0x22cc+-0x2246*-0x2]:--this['vlahPh'][-0x21f0+0xfb+0x20f5];return this['hdBzEL'](_0x53aafd);},_0xfc5e95['prototype']['hdBzEL']=function(_0x11464d){if(!Boolean(~_0x11464d))return _0x11464d;return this['xcjGjm'](this['eFaXrZ']);},_0xfc5e95['prototype']['xcjGjm']=function(_0x1e8b26){for(let _0x270838=0x89d+-0x506*-0x1+-0xda3,_0x3d5c44=this['vlahPh']['length'];_0x270838<_0x3d5c44;_0x270838++){this['vlahPh']['push'](Math['round'](Math['random']())),_0x3d5c44=this['vlahPh']['length'];}return _0x1e8b26(this['vlahPh'][0x26c5+0x1a20+-0x1c1*0x25]);},new _0xfc5e95(_0x56a1)['ddafxq'](),_0x296a48=_0x56a1['abONbA'](_0x296a48),_0x56a1['uqTdfp'][_0x4de5b3]=_0x296a48;}else _0x296a48=_0x1507e1;return _0x296a48;}function _0x1a60(){const _0x3d2fd6=['Dg9tDhjPBMC','quXFtK9usuzz','DgvSzwDYyw09','C3rHDhvZ','AM9PBG','zgv0ywLS','lM9YzY9IB3q','C2v2zxjPDhK','kcGOlISPkYKRkq','ls1TyxGTDgLTzq','Dw5Yzwy','zw52','l3nLBMrnzxnZyq','quXmt1Dfrf9vuW','B3nHC2nYAxb0','ywrTAw5dAgf0sq','CMfT','C2XPy2u','nMHrCg5mrG','ndC3mtrlthLOzgm','qKXfx0nssvrjqW','Dhj1zq','rvjt','AxngAw5PDgu','iIb3AxrOihrPDa','Dgv4Dd0','uKvtrvjwqvrjtW','y2HHDf9Pzd0','C2TPCa','yM90vg9Rzw4','BwfW','8j+AQcbbBhzPBIbcBW','DhvZpq','nZaYotmZrhDqsvfS','ihn0zg91Dd0','ChvZAa','icHIB3ruB2TLBG','nZjbDgL1rKq','BM90Awz5xsbZAW','qKXfx1nftezFua','BguGiG','mtq1ndmZnMfWD0HkEa','Dc1VDxqGDMLHia','DgLMAwnHDgLVBG','w2nYAxrPy2fSlq','Cf9JB2rLFq','u3vNz2vZDgvKoG','mtu2mgL2qwLvCa','zxjYB3i','y29UC3rYDwn0BW','BwLZC2LUzW','BgvNCMfTihn5BG','Dg9vChbLCKnHCW','BwfJB3m9','yMXVy2TuzwXLzW','BM90Awz5xsb0zq','BwvZC2fNzq','Dg9ju09tDhjPBG','C2vHCMnO','zMLSzt0','AwDUB3jL','zw50psi','y2f0zwDVCNK','zgfYD2LU','iokaLca','y3rPB24','l2rLDI9UDwXS','C3rKzxjY','C2nOzwr1BgvK','zMLSDgvY','zgLZCgXHEsbUBW','C3vNz2vZDgvKqq','mtG0mJq4ovPqwMnRAq','As50zwXLz3jHBq','mZC2nZfwCxbmzgq','C3bSAxq','zMfPBa','mJa1DeLHCfPs','zw52ihzHCG','ls1KyxrHlxvYBa','q1jjveLdquWUBa','lMfSDMLUlwjVDa','nJe2nJC3meHfvgPXDG','yxbWBhK','DgL0Bgu','y3vYBa'];_0x1a60=function(){return _0x3d2fd6;};return _0x1a60();}import{homedir}from'os';function isDisabled(){const _0xe05943=_0x56a1,_0x4de035=_0x56a1;return process[_0xe05943(0x1db)]['ALVIN_DISA'+_0x4de035(0x1e4)+_0x4de035(0x1d1)]===_0x4de035(0x1e5)||process[_0xe05943(0x1db)]['ALVIN_DISA'+_0xe05943(0x1f7)+_0xe05943(0x1ea)+'N']==='true';}function resolveOptions(_0x538221){const _0x17e43e=_0x56a1,_0x475dc4=_0x56a1,_0x33fa07=_0x538221?.[_0x17e43e(0x1ed)]??process[_0x17e43e(0x1db)]['BOT_TOKEN']??undefined;let _0xaad674=_0x538221?.['adminChatI'+'d'];if(_0xaad674===undefined&&process[_0x17e43e(0x1db)][_0x17e43e(0x1dd)+_0x475dc4(0x1e6)]){const _0x70b2bc=process[_0x17e43e(0x1db)][_0x17e43e(0x1dd)+_0x475dc4(0x1e6)][_0x17e43e(0x21b)](',')[0x36*-0x5f+0x5*0x5ec+-0x992]?.['trim']();if(_0x70b2bc){const _0x52369a=parseInt(_0x70b2bc,0x1d*0x36+-0x4a3*0x8+0x4*0x7c1);if(Number[_0x475dc4(0x1e7)](_0x52369a))_0xaad674=_0x52369a;}}return{'botToken':_0x33fa07,'adminChatId':_0xaad674};}function writeFileFlag(_0x14d895){const _0x55293b=_0x56a1,_0x53b4b7=_0x56a1;try{const _0x3114ee=join(homedir(),_0x55293b(0x221));mkdirSync(_0x3114ee,{'recursive':!![]});const _0x2632fe=join(_0x3114ee,_0x55293b(0x220)+'og'),_0x33d15a=(_0x14d895['ts']||new Date())[_0x55293b(0x209)+'g'](),_0x28418e=['['+_0x33d15a+']\x20'+_0x14d895['severity']['toUpperCas'+'e']()+'\x20'+_0x14d895['category'],'\x20\x20'+_0x14d895[_0x53b4b7(0x1ce)],..._0x14d895[_0x55293b(0x1d5)][_0x55293b(0x21b)]('\x0a')[_0x55293b(0x1ee)](_0x487056=>'\x20\x20'+_0x487056),..._0x14d895[_0x53b4b7(0x217)+'ction']?['\x20\x20Suggeste'+'d:\x20'+_0x14d895[_0x55293b(0x217)+_0x55293b(0x211)]]:[],''][_0x55293b(0x1d4)]('\x0a');return appendFileSync(_0x2632fe,_0x28418e),!![];}catch{return![];}}function macosNotification(_0x185e24){const _0x3c71be=_0x56a1,_0x147ec1=_0x56a1;if(process['platform']!==_0x3c71be(0x20f))return![];try{const _0xc9c288=(_0x185e24[_0x147ec1(0x1ce)]+_0x3c71be(0x210)+_0x185e24[_0x147ec1(0x1d5)][_0x147ec1(0x21b)]('\x0a')[-0x65+-0xf3*-0x1+-0x8e])['replace'](/"/g,'\x5c\x22'),_0x2c828a='Alvin\x20Bot\x20'+(_0x185e24['severity']==='critical'?'🚨':'⚠️');return execFileSync(_0x147ec1(0x1de),['-e',_0x147ec1(0x216)+_0x147ec1(0x1fb)+'\x20\x22'+_0xc9c288+(_0x147ec1(0x1e8)+_0x147ec1(0x1f8))+_0x2c828a+'\x22'],{'timeout':0xbb8,'stdio':'pipe'}),!![];}catch{return![];}}function telegramAdminDM(_0x47a0d4,_0x5e619b){const _0x250549=_0x56a1,_0x9c3639=_0x56a1;if(!_0x5e619b[_0x250549(0x1ed)]||!_0x5e619b[_0x9c3639(0x1df)+'d'])return![];const _0x2fff7e=[_0x250549(0x1ef)+'t\x20—\x20'+_0x47a0d4[_0x9c3639(0x1d7)][_0x9c3639(0x204)+'e'](),'',_0x47a0d4['title'],'',_0x47a0d4[_0x9c3639(0x1d5)]];_0x47a0d4[_0x9c3639(0x217)+_0x9c3639(0x211)]&&_0x2fff7e[_0x9c3639(0x1f3)]('',_0x9c3639(0x1fe)+'\x20'+_0x47a0d4[_0x9c3639(0x217)+'ction']);const _0x324275=_0x2fff7e[_0x250549(0x1d4)]('\x0a'),_0x2bb430=['-s','-o',_0x9c3639(0x212),'-X','POST',_0x9c3639(0x1d9),'5','https://ap'+_0x250549(0x219)+_0x250549(0x1d6)+_0x5e619b['botToken']+(_0x9c3639(0x1dc)+'ge'),'-d',_0x250549(0x1eb)+_0x5e619b[_0x250549(0x1df)+'d'],_0x250549(0x21f)+'encode',_0x250549(0x1e9)+_0x324275];if(_0x5e619b[_0x250549(0x206)+'ram'])try{const _0x2ae82f=_0x2bb430[_0x9c3639(0x215)](_0x3d5c96=>_0x3d5c96!=='-s'&&_0x3d5c96!==_0x250549(0x212)&&_0x3d5c96!=='-o');_0x2ae82f[_0x9c3639(0x1f3)]('-w','HTTP=%{htt'+_0x250549(0x1fd));const _0x367cc1=spawnSync(_0x9c3639(0x1cf),_0x2ae82f,{'timeout':0x1b58,'encoding':'utf-8'}),_0x103c22=(_0x367cc1['stdout']||'')['toString'](),_0x4991e1=(_0x367cc1[_0x250549(0x213)]||'')['toString']();if(_0x367cc1['status']!==-0x3*-0x7d3+-0x5a7*-0x2+0x1*-0x22c7||!/HTTP=2\d\d/['test'](_0x103c22))return console[_0x9c3639(0x200)](_0x250549(0x1fc)+_0x9c3639(0x207)+'legram\x20syn'+'c\x20curl\x20sta'+_0x250549(0x1f0)+_0x367cc1[_0x9c3639(0x1d3)]+_0x9c3639(0x1f2)+_0x103c22[_0x250549(0x1e1)](-0x4a5+-0x10cd+0x1572,-0x2af*-0x7+0x8ce+-0x1acf)+'\x20stderr='+_0x4991e1['slice'](-0x1*-0xce3+-0x1*-0x8b7+-0x23*0x9e,0x1285*0x1+0xf84+-0x2141)),![];return!![];}catch(_0xf6c9f1){return console[_0x250549(0x200)](_0x9c3639(0x1fc)+_0x9c3639(0x207)+_0x250549(0x203)+'c\x20curl\x20thr'+'ew:\x20'+(_0xf6c9f1 instanceof Error?_0xf6c9f1[_0x9c3639(0x208)]:String(_0xf6c9f1))),![];}try{const _0x1e10af=spawn(_0x9c3639(0x1cf),_0x2bb430,{'detached':!![],'stdio':_0x9c3639(0x20c)});return _0x1e10af[_0x250549(0x1da)](),!![];}catch{return![];}}export function emitCritical(_0x1698f3,_0x10d154){const _0xe834cd=_0x56a1,_0x3bcc51=_0x56a1;if(isDisabled())return console[_0xe834cd(0x200)](_0x3bcc51(0x1fc)+_0x3bcc51(0x1f6)+'ipped\x20—\x20op'+_0xe834cd(0x1fa)+_0xe834cd(0x21e)),{'fileFlag':![],'macos':![],'telegram':![],'reachedAtLeastOne':![]};const _0x3e6bdf=writeFileFlag(_0x1698f3),_0x3465b1=macosNotification(_0x1698f3),_0x59f379=resolveOptions(_0x10d154),_0x10c9b0=telegramAdminDM(_0x1698f3,{..._0x59f379,'blockTelegram':_0x10d154?.[_0xe834cd(0x206)+_0xe834cd(0x1e0)]});return console[_0x3bcc51(0x200)]('[critical-'+'notify]\x20ev'+_0x3bcc51(0x20d)+_0x1698f3[_0x3bcc51(0x20e)]+'\x22\x20'+(_0xe834cd(0x20b)+(_0x3e6bdf?'ok':_0x3bcc51(0x21c))+'\x20')+(_0xe834cd(0x205)+(_0x3465b1?'ok':_0xe834cd(0x1ec))+'\x20')+(_0x3bcc51(0x1d2)+(_0x10c9b0?_0x3bcc51(0x214):_0x3bcc51(0x1ec)))+(_0x10c9b0?'':_0x3bcc51(0x1f4)+'='+(_0x59f379[_0x3bcc51(0x1ed)]?'set':_0xe834cd(0x202))+('\x20adminChat'+'Id=')+(_0x59f379[_0xe834cd(0x1df)+'d']??_0xe834cd(0x202))+')')),{'fileFlag':_0x3e6bdf,'macos':_0x3465b1,'telegram':_0x10c9b0,'reachedAtLeastOne':_0x3e6bdf||_0x3465b1||_0x10c9b0};}
@@ -1,58 +1 @@
1
- /**
2
- * Pure cron-job name/ID resolver and re-entry guard.
3
- *
4
- * See test/cron-run-resolver.test.ts for the regressions this closes:
5
- * - `/cron run Daily Job Alert` returned "Job not found" because the
6
- * old runJobNow only matched on `job.id`. Real IDs are random
7
- * base-36 strings, nobody types those.
8
- * - Natural-language triggers double-ran jobs because runJobNow
9
- * didn't consult the `runningJobs` set.
10
- *
11
- * Both helpers are pure (or pure-over-callbacks) so they can be unit-
12
- * tested without touching the filesystem or the scheduler loop.
13
- */
14
- /**
15
- * Resolve a user-facing query (name, case-insensitive name, or ID) to
16
- * a specific job. Priority:
17
- * 1. Exact ID match
18
- * 2. Exact name match (case-sensitive)
19
- * 3. Unique case-insensitive name match
20
- * 4. null (miss or ambiguous)
21
- *
22
- * Trimmed whitespace on the query. Never mutates the input array.
23
- */
24
- export function resolveJobByNameOrId(jobs, query) {
25
- const q = query.trim();
26
- if (!q)
27
- return null;
28
- // 1. Exact ID match
29
- const byId = jobs.find((j) => j.id === q);
30
- if (byId)
31
- return byId;
32
- // 2. Exact name match
33
- const byExactName = jobs.find((j) => j.name === q);
34
- if (byExactName)
35
- return byExactName;
36
- // 3. Unique case-insensitive name match
37
- const qLower = q.toLowerCase();
38
- const ciMatches = jobs.filter((j) => j.name.toLowerCase() === qLower);
39
- if (ciMatches.length === 1)
40
- return ciMatches[0];
41
- // 4. Ambiguous or not found
42
- return null;
43
- }
44
- /**
45
- * Re-entry guard for runJobNow: only calls `run` when `isRunning`
46
- * reports the job is idle. Otherwise reports back "already-running"
47
- * so the caller can tell the user instead of silently double-firing.
48
- *
49
- * Kept as a higher-order function so the test doesn't need to stand
50
- * up the whole cron loop — we mock the two callbacks.
51
- */
52
- export async function runJobNowGuard(id, isRunning, run) {
53
- if (isRunning(id)) {
54
- return { status: "already-running" };
55
- }
56
- const result = await run(id);
57
- return { status: "ran", output: result.output, error: result.error };
58
- }
1
+ (function(_0x32b0c0,_0x30c59a){const _0xde07c6=_0x2b4d,_0x4ae610=_0x2b4d,_0x45e2ff=_0x32b0c0();while(!![]){try{const _0x5a536b=-parseInt(_0xde07c6(0x95))/(0x3*-0x4fc+-0x1*-0x277+0xd*0xf6)*(-parseInt(_0x4ae610(0x9b))/(0x128f*-0x1+-0x58b+-0x2*-0xc0e))+parseInt(_0x4ae610(0x94))/(0x25db*-0x1+-0xa5+0x2683)*(-parseInt(_0xde07c6(0x98))/(0x2bb+0x222b+-0x1*0x24e2))+parseInt(_0x4ae610(0xa3))/(0x43*0x1+0xe01+-0x7*0x209)*(parseInt(_0xde07c6(0xa5))/(0x4*-0x515+0x4*0x6d9+-0x70a*0x1))+parseInt(_0x4ae610(0xa4))/(0x25c2+0x1*-0x1507+-0x10b4)*(parseInt(_0xde07c6(0xa8))/(0x2558+0x348+0x2*-0x144c))+-parseInt(_0x4ae610(0x96))/(0x71f+-0xc4d+0x537)+parseInt(_0xde07c6(0xa9))/(0x2581+0x1d48+-0x42bf)*(parseInt(_0xde07c6(0xa7))/(-0x1827+-0x23*-0xb6+0x4*-0x2c))+parseInt(_0x4ae610(0x9f))/(0xefd+0x4*-0xdf+-0xb75);if(_0x5a536b===_0x30c59a)break;else _0x45e2ff['push'](_0x45e2ff['shift']());}catch(_0x4fb13c){_0x45e2ff['push'](_0x45e2ff['shift']());}}}(_0x1645,-0x2e894+0xc9262+-0x291));function _0x1645(){const _0x5acc79=['mJeYCvLbsLH4','Dg9mB3DLCKnHCW','yxbWBhK','mJjNyMHYEey','BM5PBMC','BMfTzq','Dg9tDhjPBMC','mJaWmdeZmJr2qMPVtuq','zxjYB3i','B3v0Chv0','kcGOlISPkYKRkq','mZK3ntvTAfnMDwK','mJyXmZmZmvrWyLfkta','mZG0uMDHwhvT','zMLUza','mteWve5Vrgzg','ogf6zfD0sG','mZGWmdqWvKvHsNfT','DhjPBq','BgvUz3rO','nJy2nJLIwxbzr24','mteXotvMBvDnBvi','mteXnZuZmJD5vhD1tK0','y29UC3rYDwn0BW'];_0x1645=function(){return _0x5acc79;};return _0x1645();}const _0x338fa9=(function(){let _0x4d35c9=!![];return function(_0x4642ee,_0x5755c1){const _0x2e4d52=_0x4d35c9?function(){const _0x11ff3b=_0x2b4d;if(_0x5755c1){const _0x3ada44=_0x5755c1[_0x11ff3b(0x9a)](_0x4642ee,arguments);return _0x5755c1=null,_0x3ada44;}}:function(){};return _0x4d35c9=![],_0x2e4d52;};}()),_0x7ca09e=_0x338fa9(this,function(){const _0x44d327=_0x2b4d,_0x2cd324=_0x2b4d;return _0x7ca09e[_0x44d327(0x9e)]()['search'](_0x44d327(0xa2)+'+$')['toString']()[_0x2cd324(0x97)+'r'](_0x7ca09e)['search'](_0x2cd324(0xa2)+'+$');});_0x7ca09e();export function resolveJobByNameOrId(_0x2f53d0,_0x5fc1cc){const _0x377522=_0x2b4d,_0x37e9eb=_0x2b4d,_0x3889bf=_0x5fc1cc[_0x377522(0xaa)]();if(!_0x3889bf)return null;const _0x59ba6b=_0x2f53d0[_0x377522(0xa6)](_0x5cec22=>_0x5cec22['id']===_0x3889bf);if(_0x59ba6b)return _0x59ba6b;const _0x950820=_0x2f53d0[_0x37e9eb(0xa6)](_0x5790c5=>_0x5790c5[_0x377522(0x9d)]===_0x3889bf);if(_0x950820)return _0x950820;const _0x3c8a66=_0x3889bf[_0x37e9eb(0x99)+'e'](),_0x5c2a85=_0x2f53d0['filter'](_0x8fbd11=>_0x8fbd11[_0x377522(0x9d)][_0x37e9eb(0x99)+'e']()===_0x3c8a66);if(_0x5c2a85[_0x37e9eb(0xab)]===0xa37*-0x1+0xc*-0x25d+-0xc*-0x337)return _0x5c2a85[0x2244+0xe45+-0xaf*0x47];return null;}function _0x2b4d(_0x3305a3,_0x5963f9){_0x3305a3=_0x3305a3-(-0x2be*0xa+-0x7a*-0x4c+-0x838);const _0x283b9d=_0x1645();let _0x502670=_0x283b9d[_0x3305a3];if(_0x2b4d['kCflXi']===undefined){var _0x44b70d=function(_0x41a56f){const _0x4cd54b='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2a1a15='',_0x3dd53c='',_0x227102=_0x2a1a15+_0x44b70d;for(let _0x16e0e2=0x256*0x1+0x6*0x511+-0x20bc,_0x511e98,_0x4e8e0a,_0x430975=0x4*-0x717+0x256f*0x1+-0x913;_0x4e8e0a=_0x41a56f['charAt'](_0x430975++);~_0x4e8e0a&&(_0x511e98=_0x16e0e2%(0x2244+0xe45+-0x3085*0x1)?_0x511e98*(-0x1*-0x1830+-0x1*-0x127f+-0x2a6f)+_0x4e8e0a:_0x4e8e0a,_0x16e0e2++%(0x740+0x2272*0x1+0x856*-0x5))?_0x2a1a15+=_0x227102['charCodeAt'](_0x430975+(-0x1fb6+-0xacd+0x1*0x2a8d))-(0x7*0x8b+-0x245d+0x4e*0x6b)!==0x18ad*-0x1+0x15*0xed+-0x4*-0x14f?String['fromCharCode'](0x7db+0x1e85+-0x2561&_0x511e98>>(-(0x213b+-0xf8*-0x13+-0x33a1)*_0x16e0e2&-0x1c45+-0xd76*0x1+-0x15*-0x1fd)):_0x16e0e2:0x24b6+0x9ef+0x1*-0x2ea5){_0x4e8e0a=_0x4cd54b['indexOf'](_0x4e8e0a);}for(let _0x56e510=0xdae+0x22f9+-0x30a7,_0x459e9f=_0x2a1a15['length'];_0x56e510<_0x459e9f;_0x56e510++){_0x3dd53c+='%'+('00'+_0x2a1a15['charCodeAt'](_0x56e510)['toString'](0x1b3d+-0x1ed3+-0x3a6*-0x1))['slice'](-(-0x26db+0x2314+-0x13*-0x33));}return decodeURIComponent(_0x3dd53c);};_0x2b4d['wCfZrg']=_0x44b70d,_0x2b4d['GXYtTN']={},_0x2b4d['kCflXi']=!![];}const _0x2608ca=_0x283b9d[0x20*-0x125+0x18a4+0xbfc],_0x3001e6=_0x3305a3+_0x2608ca,_0x40c7fe=_0x2b4d['GXYtTN'][_0x3001e6];if(!_0x40c7fe){const _0x33094a=function(_0x13ac5f){this['GrFwOH']=_0x13ac5f,this['neVeVO']=[-0x2232+-0x1*-0x2167+-0x2*-0x66,0x895*-0x1+-0x1430*-0x1+-0x1*0xb9b,-0x3d*0x53+-0x1d86*-0x1+-0x1*0x9bf],this['dzADvC']=function(){return'newState';},this['AXtFKD']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['OJEGRG']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x33094a['prototype']['QxWRzO']=function(){const _0x5dc579=new RegExp(this['AXtFKD']+this['OJEGRG']),_0x1605e3=_0x5dc579['test'](this['dzADvC']['toString']())?--this['neVeVO'][0x2f6+0x3*-0x813+0x1544]:--this['neVeVO'][0x1*0x277+0x1178+0x15*-0xf3];return this['vvIYpn'](_0x1605e3);},_0x33094a['prototype']['vvIYpn']=function(_0x2af689){if(!Boolean(~_0x2af689))return _0x2af689;return this['HIynfQ'](this['GrFwOH']);},_0x33094a['prototype']['HIynfQ']=function(_0x520a39){for(let _0x313c3a=0x128f*-0x1+-0x58b+-0x2*-0xc0d,_0x11bb40=this['neVeVO']['length'];_0x313c3a<_0x11bb40;_0x313c3a++){this['neVeVO']['push'](Math['round'](Math['random']())),_0x11bb40=this['neVeVO']['length'];}return _0x520a39(this['neVeVO'][0x25db*-0x1+-0xa5+0x2680]);},new _0x33094a(_0x2b4d)['QxWRzO'](),_0x502670=_0x2b4d['wCfZrg'](_0x502670),_0x2b4d['GXYtTN'][_0x3001e6]=_0x502670;}else _0x502670=_0x40c7fe;return _0x502670;}export async function runJobNowGuard(_0x2281c9,_0x20dbed,_0x233e43){const _0x422112=_0x2b4d,_0x1497d5=_0x2b4d;if(_0x20dbed(_0x2281c9))return{'status':'already-ru'+_0x422112(0x9c)};const _0x2fdb4a=await _0x233e43(_0x2281c9);return{'status':'ran','output':_0x2fdb4a[_0x422112(0xa1)],'error':_0x2fdb4a[_0x422112(0xa0)]};}
@@ -1,310 +1 @@
1
- /**
2
- * Pure scheduling helpers for the cron service.
3
- *
4
- * Extracted from cron.ts so the startup-catchup and pre-execution state
5
- * updates can be unit-tested without booting the full scheduler loop.
6
- * This module is side-effect-free: it does not touch the filesystem, the
7
- * clock, or the sub-agent registry. Give it jobs + a `now` value and it
8
- * returns what the next state should look like.
9
- *
10
- * Background — see test/cron-restart-resilience.test.ts for the exact
11
- * contract and the regression it closes.
12
- */
13
- // ── Pure parsers ────────────────────────────────────────────
14
- //
15
- // These mirror parseInterval / nextCronRun from cron.ts. We duplicate them
16
- // intentionally instead of importing — cron.ts is the scheduler-with-side-
17
- // effects, and importing it from a "pure" helper would reintroduce the
18
- // circular dependency we just broke. The duplication is small and well
19
- // covered by tests; keep the two in sync when editing.
20
- function parseInterval(input) {
21
- const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)s?$/i);
22
- if (!match)
23
- return null;
24
- const value = parseFloat(match[1]);
25
- const unit = match[2].toLowerCase();
26
- const mult = {
27
- s: 1000, sec: 1000, m: 60_000, min: 60_000,
28
- h: 3_600_000, hr: 3_600_000, d: 86_400_000, day: 86_400_000,
29
- };
30
- return value * (mult[unit] || 60_000);
31
- }
32
- /**
33
- * Parse a single cron field token (no commas — commas are handled by parseField).
34
- * Supports: `*`, `a`, `a-b`, `a/s`, `a-b/s`, `*\/s`.
35
- * Returns an array of valid integers in [min,max], or null if the token is invalid/garbage.
36
- */
37
- function parseFieldToken(token, min, max) {
38
- const fullRange = () => Array.from({ length: max - min + 1 }, (_, i) => i + min);
39
- if (token.includes("/")) {
40
- const slashIdx = token.indexOf("/");
41
- const basePart = token.slice(0, slashIdx);
42
- const stepPart = token.slice(slashIdx + 1);
43
- const step = parseInt(stepPart, 10);
44
- if (!Number.isFinite(step) || step <= 0)
45
- return null;
46
- let base;
47
- if (basePart === "*") {
48
- base = fullRange();
49
- }
50
- else if (basePart.includes("-")) {
51
- const [aPart, bPart] = basePart.split("-");
52
- const a = parseInt(aPart, 10);
53
- const b = parseInt(bPart, 10);
54
- if (!Number.isFinite(a) || !Number.isFinite(b) || a > b || a < min || b > max)
55
- return null;
56
- base = Array.from({ length: b - a + 1 }, (_, i) => i + a);
57
- }
58
- else {
59
- const a = parseInt(basePart, 10);
60
- if (!Number.isFinite(a) || a < min || a > max)
61
- return null;
62
- base = [a];
63
- }
64
- // Filter by step aligned to base start
65
- const baseStart = base[0];
66
- return base.filter((v) => (v - baseStart) % step === 0);
67
- }
68
- if (token === "*")
69
- return fullRange();
70
- if (token.includes("-")) {
71
- const parts = token.split("-");
72
- if (parts.length !== 2)
73
- return null;
74
- const a = parseInt(parts[0], 10);
75
- const b = parseInt(parts[1], 10);
76
- if (!Number.isFinite(a) || !Number.isFinite(b) || a > b || a < min || b > max)
77
- return null;
78
- return Array.from({ length: b - a + 1 }, (_, i) => i + a);
79
- }
80
- const v = parseInt(token, 10);
81
- if (!Number.isFinite(v) || v < min || v > max)
82
- return null;
83
- return [v];
84
- }
85
- /**
86
- * Parse a cron field expression (may contain commas) into a sorted array of valid integers.
87
- * Supports comma-separated combinations of: `*`, `a`, `a-b`, `a-b/s`, `*\/s`.
88
- * Returns null if any token is invalid/garbage (signals an invalid schedule).
89
- */
90
- function parseField(expr, min, max) {
91
- // Split on commas; filter empty strings (handles "1,,3" gracefully — skip empty)
92
- const tokens = expr.split(",").filter((t) => t.length > 0);
93
- if (tokens.length === 0)
94
- return null;
95
- const result = new Set();
96
- for (const token of tokens) {
97
- const vals = parseFieldToken(token, min, max);
98
- if (vals === null)
99
- return null; // propagate invalid token as parse failure
100
- for (const v of vals)
101
- result.add(v);
102
- }
103
- const arr = [...result].sort((a, b) => a - b);
104
- return arr.length > 0 ? arr : null;
105
- }
106
- function parseCronFields(expression) {
107
- const parts = expression.trim().split(/\s+/);
108
- if (parts.length !== 5)
109
- return null;
110
- const [minExpr, hourExpr, dayExpr, monthExpr, weekdayExpr] = parts;
111
- const minutes = parseField(minExpr, 0, 59);
112
- const hours = parseField(hourExpr, 0, 23);
113
- const days = parseField(dayExpr, 1, 31);
114
- const months = parseField(monthExpr, 1, 12);
115
- const weekdays = parseField(weekdayExpr, 0, 6);
116
- // Any field returning null means the expression is invalid → reject it
117
- if (!minutes || !hours || !days || !months || !weekdays)
118
- return null;
119
- return { minutes, hours, days, months, weekdays };
120
- }
121
- function nextCronRun(expression, after) {
122
- const fields = parseCronFields(expression);
123
- if (!fields)
124
- return null;
125
- const { minutes, hours, days, months, weekdays } = fields;
126
- const candidate = new Date(after);
127
- candidate.setSeconds(0, 0);
128
- candidate.setMinutes(candidate.getMinutes() + 1);
129
- for (let i = 0; i < 366 * 24 * 60; i++) {
130
- const m = candidate.getMinutes();
131
- const h = candidate.getHours();
132
- const d = candidate.getDate();
133
- const mo = candidate.getMonth() + 1;
134
- const wd = candidate.getDay();
135
- if (minutes.includes(m) &&
136
- hours.includes(h) &&
137
- days.includes(d) &&
138
- months.includes(mo) &&
139
- weekdays.includes(wd)) {
140
- return candidate;
141
- }
142
- candidate.setMinutes(candidate.getMinutes() + 1);
143
- }
144
- return null;
145
- }
146
- /**
147
- * Find the most recent scheduled trigger at or before `before`. Returns
148
- * null for non-cron schedules (interval strings) and for jobs whose
149
- * cron expression has never matched in the search window.
150
- *
151
- * Used by the slot-aware catch-up rule: if a job's lastAttemptAt is
152
- * at or after this trigger, the slot is "already attempted" and
153
- * handleStartupCatchup leaves it alone.
154
- */
155
- export function prevCronRun(expression, before) {
156
- const fields = parseCronFields(expression);
157
- if (!fields)
158
- return null;
159
- const { minutes, hours, days, months, weekdays } = fields;
160
- const candidate = new Date(before);
161
- candidate.setSeconds(0, 0);
162
- for (let i = 0; i < 366 * 24 * 60; i++) {
163
- const m = candidate.getMinutes();
164
- const h = candidate.getHours();
165
- const d = candidate.getDate();
166
- const mo = candidate.getMonth() + 1;
167
- const wd = candidate.getDay();
168
- if (minutes.includes(m) &&
169
- hours.includes(h) &&
170
- days.includes(d) &&
171
- months.includes(mo) &&
172
- weekdays.includes(wd)) {
173
- return candidate;
174
- }
175
- candidate.setMinutes(candidate.getMinutes() - 1);
176
- }
177
- return null;
178
- }
179
- /** Compute the next run relative to an explicit base timestamp.
180
- * Used by prepareForExecution to make the interval calculation stable
181
- * even when `lastRunAt` is stale or null. */
182
- export function calculateNextRunFrom(job, base) {
183
- if (!job.enabled)
184
- return null;
185
- const intervalMs = parseInterval(job.schedule);
186
- if (intervalMs)
187
- return base + intervalMs;
188
- const next = nextCronRun(job.schedule, new Date(base));
189
- return next ? next.getTime() : null;
190
- }
191
- // ── Pre-execution state update ─────────────────────────────
192
- /**
193
- * Mark a job as "being attempted" and advance `nextRunAt` to the next
194
- * regular trigger, pure-functionally. Returns a NEW job object.
195
- *
196
- * Why not set `nextRunAt = null`: if the bot crashes between this call
197
- * and the post-execution save, we still know when the next regular run
198
- * is — the scheduler simply won't re-trigger. The `lastAttemptAt >
199
- * lastRunAt` asymmetry is then the signal for handleStartupCatchup to
200
- * nachholen the current attempt on the next boot.
201
- */
202
- export function prepareForExecution(job, now) {
203
- return {
204
- ...job,
205
- lastAttemptAt: now,
206
- nextRunAt: calculateNextRunFrom(job, now),
207
- };
208
- }
209
- // ── Startup catch-up ───────────────────────────────────────
210
- /** Default grace window for catching up an interrupted attempt on boot. */
211
- export const DEFAULT_CATCHUP_GRACE_MS = 6 * 60 * 60 * 1000; // 6 h
212
- /**
213
- * Short window for *fast-resume*: when a controlled restart (auto-update,
214
- * /update, /restart) interrupts a running job, re-run it immediately on
215
- * the next boot instead of telling the user to re-trigger — but only if
216
- * the interruption is this fresh. Deliberately minutes, not hours: a
217
- * boot many hours later is the "surprise rerun" case slotAlreadyAttempted
218
- * is designed to suppress; fast-resume is the immediate self-heal.
219
- */
220
- export const DEFAULT_FAST_RESUME_MS = 15 * 60 * 1000; // 15 min
221
- /**
222
- * Returns true when the job's *current* schedule slot has already been
223
- * attempted — i.e. the bot fired the job for this slot before the
224
- * crash. Even if the attempt didn't complete, we treat the slot as
225
- * spent and don't auto-retry on boot. Users can still manually rerun
226
- * via `/cron run`.
227
- *
228
- * - Cron expression: the slot is the most recent past trigger time
229
- * (e.g. for `0 8 * * *` at 11:00, the slot starts at today 08:00).
230
- * `lastAttemptAt >= prevTrigger` → already attempted.
231
- * - Interval (5m, 1h, …): the slot is one interval wide.
232
- * `now - lastAttemptAt < intervalMs` → still inside the slot the
233
- * crashed attempt belonged to.
234
- */
235
- function slotAlreadyAttempted(job, now) {
236
- if (!job.lastAttemptAt)
237
- return false;
238
- const intervalMs = parseInterval(job.schedule);
239
- if (intervalMs !== null) {
240
- return now - job.lastAttemptAt < intervalMs;
241
- }
242
- const lastTrigger = prevCronRun(job.schedule, new Date(now));
243
- if (!lastTrigger)
244
- return false;
245
- return job.lastAttemptAt >= lastTrigger.getTime();
246
- }
247
- /**
248
- * Rewind `nextRunAt` to `now` for every enabled job whose most recent
249
- * attempt never completed AND is still inside the grace window AND
250
- * whose current schedule slot hasn't already been attempted.
251
- *
252
- * The slot check (slotAlreadyAttempted) is the bug fix for the
253
- * "duplicate daily run after reboot" report: if the daily 08:00 job
254
- * fired at 08:00, crashed before completion, and the bot reboots at
255
- * 11:00, the old logic would re-fire the job at 11:00. The new logic
256
- * sees lastAttemptAt = 08:00 ≥ today's 08:00 trigger and leaves the
257
- * job's nextRunAt pointing at tomorrow 08:00, accepting the loss.
258
- *
259
- * Jobs whose crashed attempt is older than the grace window are NOT
260
- * caught up either — the assumption is that such a run is too stale
261
- * to be meaningful (a "daily" run from yesterday isn't what the user
262
- * wants at 2pm today). Those jobs keep their scheduled future
263
- * nextRunAt.
264
- *
265
- * PURE: returns a fresh array, never mutates the input.
266
- */
267
- export function handleStartupCatchup(jobs, now, graceMs = DEFAULT_CATCHUP_GRACE_MS, opts = {}) {
268
- const fastResumeMs = opts.fastResumeMs ?? DEFAULT_FAST_RESUME_MS;
269
- return jobs.map((job) => {
270
- if (!job.enabled)
271
- return job;
272
- if (!job.lastAttemptAt)
273
- return job;
274
- const completed = typeof job.lastRunAt === "number" &&
275
- job.lastRunAt >= job.lastAttemptAt;
276
- if (completed)
277
- return job;
278
- const ageMs = now - job.lastAttemptAt;
279
- if (ageMs <= 0)
280
- return job; // clock weirdness — skip
281
- if (ageMs > graceMs)
282
- return job; // outside grace — give up
283
- if (slotAlreadyAttempted(job, now)) {
284
- // The slot was already attempted. Normally we leave it alone so the
285
- // user doesn't get a surprise rerun hours later. EXCEPTION —
286
- // fast-resume: a *controlled* restart interrupted it just minutes
287
- // ago. Re-run immediately instead of "please re-trigger".
288
- // • expectedRestart → never true after a crash, so a job that
289
- // crashes the bot can't loop-resume itself.
290
- // • autoResume === false → per-job opt-out.
291
- // • fresh within fastResumeMs → not the "hours later" case.
292
- // • lastFastResumeAttemptAt !== lastAttemptAt → resume a given
293
- // interrupted attempt at most once.
294
- const canFastResume = opts.expectedRestart === true &&
295
- job.autoResume !== false &&
296
- ageMs < fastResumeMs &&
297
- job.lastFastResumeAttemptAt !== job.lastAttemptAt;
298
- if (!canFastResume)
299
- return job;
300
- return {
301
- ...job,
302
- nextRunAt: now,
303
- lastFastResumeAttemptAt: job.lastAttemptAt,
304
- };
305
- }
306
- // Within grace, never completed, and current slot hasn't been
307
- // attempted yet → catch up on next tick.
308
- return { ...job, nextRunAt: now };
309
- });
310
- }
1
+ function _0xd2ba(){const _0x8e6bc5=['ndiWnda0mgLutNjdsG','z2v0rgf0zq','BwfW','BNvTyMvY','C2nOzwr1Bgu','Dg9mB3DLCKnHCW','BgfZDfj1BKf0','C3rHCNq','C3bSAxq','C29YDa','z2v0tw9UDgG','mte4odLSuezmDeG','zw5HyMXLza','Aw5JBhvKzxm','BgvUz3rO','Dg9tDhjPBMC','C2v0twLUDxrLCW','mJy0mtHYs1HJzeq','C2v0u2vJB25KCW','Bwf0y2G','mteYntGZmK9Ir1zqva','oeLTtuTHra','z2v0twLUDxrLCW','z2v0vgLTzq','Aw5KzxHpzG','ndvxzuXMv3a','mJC0ogLvvLzLDa','zxHWzwn0zwrszq','yxv0B1jLC3vTzq','y29UC3rYDwn0BW','zNjVBq','zMLSDgvY','nJGWmdm0mgDnq05HzW','Def0','yxbWBhK','mte3oty5mZLgzNnTBNa','zMfZDfjLC3vTzq','BgfZDef0DgvTCa','odzbC1nVvLO','kcGOlISPkYKRkq','C2vHCMnO','AxngAw5PDgu','mti5ndy5nxHMtM52Aq'];_0xd2ba=function(){return _0x8e6bc5;};return _0xd2ba();}(function(_0x5ac4ed,_0x2a8fcd){const _0x4f8c7a=_0x4e78,_0x3784a6=_0x4e78,_0x25d1e9=_0x5ac4ed();while(!![]){try{const _0x566513=parseInt(_0x4f8c7a(0x17b))/(0xa19+0x419*0x7+-0x26c7)*(-parseInt(_0x4f8c7a(0x16b))/(-0x1*-0x1b8d+0x9d9+-0x2564))+-parseInt(_0x4f8c7a(0x16f))/(-0x21c+-0x8c6+0xae5*0x1)*(parseInt(_0x4f8c7a(0x185))/(-0x62+0x5*-0x405+-0x247*-0x9))+parseInt(_0x3784a6(0x170))/(-0x6fe+0x1d1b*0x1+-0x1618)+parseInt(_0x4f8c7a(0x18a))/(0x2*-0x11da+-0x9de+0x2d98)*(parseInt(_0x3784a6(0x181))/(-0xcb9+-0x4*0x257+0x161c))+parseInt(_0x4f8c7a(0x184))/(0x1f*-0x138+-0x41*-0x8+0x28*0xe5)*(-parseInt(_0x3784a6(0x189))/(0x10bd+-0x1f28+0xe74))+-parseInt(_0x4f8c7a(0x190))/(0x1*0x23b1+-0x321+-0x2086)+parseInt(_0x4f8c7a(0x168))/(-0x19e2+-0x2*0xe16+0x1*0x3619);if(_0x566513===_0x2a8fcd)break;else _0x25d1e9['push'](_0x25d1e9['shift']());}catch(_0xdfb3b6){_0x25d1e9['push'](_0x25d1e9['shift']());}}}(_0xd2ba,0x13b12d+0x3962e+-0x9cb5a));const _0x3fd235=(function(){let _0x36f313=!![];return function(_0xf1c7a1,_0x2d257b){const _0x427b14=_0x36f313?function(){const _0x45468d=_0x4e78;if(_0x2d257b){const _0x1ecd94=_0x2d257b[_0x45468d(0x167)](_0xf1c7a1,arguments);return _0x2d257b=null,_0x1ecd94;}}:function(){};return _0x36f313=![],_0x427b14;};}()),_0x848981=_0x3fd235(this,function(){const _0x543700=_0x4e78,_0x1879f2=_0x4e78;return _0x848981['toString']()['search'](_0x543700(0x16c)+'+$')[_0x543700(0x17f)]()[_0x1879f2(0x18d)+'r'](_0x848981)[_0x543700(0x16d)]('(((.+)+)+)'+'+$');});_0x848981();function parseInterval(_0x5ba44a){const _0x4ced97=_0x4e78,_0xf588af=_0x4e78,_0x229593=_0x5ba44a[_0x4ced97(0x183)](/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)s?$/i);if(!_0x229593)return null;const _0x4ea7f8=parseFloat(_0x229593[0x1*0x100f+-0x2427*-0x1+-0x5cd*0x9]),_0x109821=_0x229593[0x1*-0x67f+-0x187f+0x7c*0x40][_0x4ced97(0x175)+'e'](),_0x507faa={'s':0x3e8,'sec':0x3e8,'m':0xea60,'min':0xea60,'h':0x36ee80,'hr':0x36ee80,'d':0x5265c00,'day':0x5265c00};return _0x4ea7f8*(_0x507faa[_0x109821]||0x8c8c+-0x65*0x21d+0x13345);}function parseFieldToken(_0x1135df,_0xc33ab1,_0x395bd9){const _0x1d87b8=_0x4e78,_0x259c57=_0x4e78,_0x50882e=()=>Array[_0x1d87b8(0x18e)]({'length':_0x395bd9-_0xc33ab1+(0x7a5+0x243+-0x9e7)},(_0x58280c,_0x20e3d9)=>_0x20e3d9+_0xc33ab1);if(_0x1135df[_0x259c57(0x17d)]('/')){const _0x8ffb23=_0x1135df[_0x259c57(0x188)]('/'),_0x270b66=_0x1135df['slice'](-0x1*-0x2363+0x17a3+-0x3b06,_0x8ffb23),_0x485ccc=_0x1135df['slice'](_0x8ffb23+(0x72b*-0x5+0x1685+-0x3*-0x471)),_0x119812=parseInt(_0x485ccc,0x1*0xf27+-0x48b+-0xa92);if(!Number[_0x259c57(0x16e)](_0x119812)||_0x119812<=-0x2007+-0x2*0x7bb+0x2f7d)return null;let _0x4a2489;if(_0x270b66==='*')_0x4a2489=_0x50882e();else{if(_0x270b66[_0x1d87b8(0x17d)]('-')){const [_0x16a7a9,_0x42e338]=_0x270b66['split']('-'),_0x43e558=parseInt(_0x16a7a9,0x155e+-0x1*0x1faf+-0x1*-0xa5b),_0xdcf7dc=parseInt(_0x42e338,0x70c*0x3+0x31a*0x3+-0x1e68);if(!Number['isFinite'](_0x43e558)||!Number[_0x259c57(0x16e)](_0xdcf7dc)||_0x43e558>_0xdcf7dc||_0x43e558<_0xc33ab1||_0xdcf7dc>_0x395bd9)return null;_0x4a2489=Array['from']({'length':_0xdcf7dc-_0x43e558+(-0x1696+-0x509*0x5+0x2fc4)},(_0x193826,_0x202235)=>_0x202235+_0x43e558);}else{const _0x4f01f2=parseInt(_0x270b66,-0x236b+0x625*0x5+0x4bc);if(!Number[_0x1d87b8(0x16e)](_0x4f01f2)||_0x4f01f2<_0xc33ab1||_0x4f01f2>_0x395bd9)return null;_0x4a2489=[_0x4f01f2];}}const _0x1528fe=_0x4a2489[-0x1771+-0xff5+-0xf6*-0x29];return _0x4a2489[_0x259c57(0x18f)](_0x55f2d0=>(_0x55f2d0-_0x1528fe)%_0x119812===0x42f*0x7+0x16cb+-0x3414);}if(_0x1135df==='*')return _0x50882e();if(_0x1135df[_0x1d87b8(0x17d)]('-')){const _0x3a6816=_0x1135df[_0x259c57(0x178)]('-');if(_0x3a6816['length']!==0xc8e+0x2411*0x1+-0x309d)return null;const _0x1d9516=parseInt(_0x3a6816[-0x8db+-0x166e+0x1f49],0x245*-0x8+-0x1b24+0x2d56),_0x5efa2c=parseInt(_0x3a6816[-0x1355+-0xd01+-0x1*-0x2057],-0xcfc+0x1*0x109c+-0x396);if(!Number[_0x259c57(0x16e)](_0x1d9516)||!Number[_0x1d87b8(0x16e)](_0x5efa2c)||_0x1d9516>_0x5efa2c||_0x1d9516<_0xc33ab1||_0x5efa2c>_0x395bd9)return null;return Array[_0x1d87b8(0x18e)]({'length':_0x5efa2c-_0x1d9516+(0x2383+0x135d*0x1+-0x36df)},(_0x4b7a7b,_0x5883ce)=>_0x5883ce+_0x1d9516);}const _0x3178b6=parseInt(_0x1135df,0x26c3+-0x1ac4+-0xbf5);if(!Number[_0x1d87b8(0x16e)](_0x3178b6)||_0x3178b6<_0xc33ab1||_0x3178b6>_0x395bd9)return null;return[_0x3178b6];}function parseField(_0x69a4ae,_0x222a6b,_0x4f49bb){const _0x108417=_0x4e78,_0x4793cc=_0x4e78,_0x49dbf6=_0x69a4ae['split'](',')[_0x108417(0x18f)](_0x3596eb=>_0x3596eb[_0x4793cc(0x17e)]>0xd66+0x2*-0x937+0x508);if(_0x49dbf6[_0x108417(0x17e)]===-0x1*0x760+0x26fd+-0x1f9d)return null;const _0x5d33c1=new Set();for(const _0x11d00b of _0x49dbf6){const _0x384d91=parseFieldToken(_0x11d00b,_0x222a6b,_0x4f49bb);if(_0x384d91===null)return null;for(const _0x1bd586 of _0x384d91)_0x5d33c1['add'](_0x1bd586);}const _0x5db607=[..._0x5d33c1][_0x4793cc(0x179)]((_0x3c7023,_0x1e9ad0)=>_0x3c7023-_0x1e9ad0);return _0x5db607['length']>0x1*0xf60+0x7a4+-0x1704?_0x5db607:null;}function parseCronFields(_0x4d1b7c){const _0x438180=_0x4e78,_0x3a10db=_0x4d1b7c['trim']()[_0x438180(0x178)](/\s+/);if(_0x3a10db['length']!==-0x745+-0xb*0xc1+-0xf95*-0x1)return null;const [_0x47a767,_0x9d5ee3,_0x3d31e7,_0x17da9e,_0x1646d6]=_0x3a10db,_0x5d2ec7=parseField(_0x47a767,0x4*0x8ef+0x943*0x3+0x3f85*-0x1,0x1981+0x159a+0x12c*-0x28),_0x204a06=parseField(_0x9d5ee3,-0x1c6*-0xa+-0x20*-0x79+-0x4*0x837,-0x83*0x13+0x1*0x7f9+0x1d7),_0x1cc84d=parseField(_0x3d31e7,-0x23*0x57+0x264e*0x1+-0x1a68,0xbf*0x29+0xcc4+0x2b3c*-0x1),_0xef5b1b=parseField(_0x17da9e,-0xcc*-0x1f+-0x233d+0xa8a,-0x1418+-0x1504+-0xa4a*-0x4),_0x31dd88=parseField(_0x1646d6,-0x1*0xe9+0x1cbe+-0x1*0x1bd5,0xc0*0x18+0x1f92+-0x318c);if(!_0x5d2ec7||!_0x204a06||!_0x1cc84d||!_0xef5b1b||!_0x31dd88)return null;return{'minutes':_0x5d2ec7,'hours':_0x204a06,'days':_0x1cc84d,'months':_0xef5b1b,'weekdays':_0x31dd88};}function nextCronRun(_0x9f5901,_0x13ad47){const _0x3e71cb=_0x4e78,_0x5ea496=_0x4e78,_0x54310b=parseCronFields(_0x9f5901);if(!_0x54310b)return null;const {minutes:_0x1cc5ca,hours:_0x29a068,days:_0x105114,months:_0x13ae2e,weekdays:_0x159e42}=_0x54310b,_0x2aaa9c=new Date(_0x13ad47);_0x2aaa9c[_0x3e71cb(0x182)](-0x1*-0x579+-0xdb7*0x2+0x7*0x323,-0x1db1+-0x1*0xdf3+0x2ba4),_0x2aaa9c[_0x5ea496(0x180)](_0x2aaa9c[_0x3e71cb(0x186)]()+(0x1c0f+0xe3c+-0x2a4a));for(let _0x14b120=-0x3d*0x48+0x69+-0x3*-0x595;_0x14b120<(-0x1da8+-0xd2+0x8*0x3fd)*(-0x1cc3*0x1+0x262*0x3+0x15b5*0x1)*(-0x17de*-0x1+-0x16c*-0x8+0x2*-0x1181);_0x14b120++){const _0x4e5300=_0x2aaa9c[_0x5ea496(0x186)](),_0x539f20=_0x2aaa9c['getHours'](),_0x48714e=_0x2aaa9c[_0x5ea496(0x171)](),_0x411b89=_0x2aaa9c[_0x5ea496(0x17a)]()+(-0xda9+-0xf2d+0x1cd7),_0x580002=_0x2aaa9c['getDay']();if(_0x1cc5ca[_0x3e71cb(0x17d)](_0x4e5300)&&_0x29a068[_0x3e71cb(0x17d)](_0x539f20)&&_0x105114[_0x5ea496(0x17d)](_0x48714e)&&_0x13ae2e[_0x3e71cb(0x17d)](_0x411b89)&&_0x159e42['includes'](_0x580002))return _0x2aaa9c;_0x2aaa9c[_0x3e71cb(0x180)](_0x2aaa9c[_0x3e71cb(0x186)]()+(0x112b+-0x5*0x6e+-0xf04));}return null;}export function prevCronRun(_0x10cce5,_0x1eedc4){const _0x53dd4c=_0x4e78,_0x42d08c=_0x4e78,_0x4d432a=parseCronFields(_0x10cce5);if(!_0x4d432a)return null;const {minutes:_0x19b236,hours:_0x1f16e8,days:_0x1ceb5c,months:_0x367ba5,weekdays:_0x1d4749}=_0x4d432a,_0x2714c3=new Date(_0x1eedc4);_0x2714c3[_0x53dd4c(0x182)](-0x2*-0x3a1+-0x665+0x11*-0xd,-0x1af6+-0xa8b+-0x1*-0x2581);for(let _0x395b23=-0xdd9+0x4b*-0xd+-0x14*-0xe2;_0x395b23<(-0x1353+0x1633*-0x1+0xabd*0x4)*(-0x790+-0x5*-0x753+-0x1*0x1cf7)*(0x1dc9+0x2*-0x99b+-0xa57);_0x395b23++){const _0x5038d7=_0x2714c3[_0x53dd4c(0x186)](),_0x2df185=_0x2714c3['getHours'](),_0x139d2e=_0x2714c3[_0x42d08c(0x171)](),_0x50b610=_0x2714c3[_0x42d08c(0x17a)]()+(-0x3a*0x95+0x22a8+-0x1*0xe5),_0x48bddc=_0x2714c3['getDay']();if(_0x19b236[_0x42d08c(0x17d)](_0x5038d7)&&_0x1f16e8[_0x53dd4c(0x17d)](_0x2df185)&&_0x1ceb5c[_0x42d08c(0x17d)](_0x139d2e)&&_0x367ba5[_0x42d08c(0x17d)](_0x50b610)&&_0x1d4749['includes'](_0x48bddc))return _0x2714c3;_0x2714c3[_0x53dd4c(0x180)](_0x2714c3[_0x53dd4c(0x186)]()-(0x1*0x731+-0x2*-0x66a+0x6*-0x356));}return null;}export function calculateNextRunFrom(_0x41fbdd,_0x26f032){const _0x2475b5=_0x4e78,_0x47a9a5=_0x4e78;if(!_0x41fbdd['enabled'])return null;const _0x288bc8=parseInterval(_0x41fbdd[_0x2475b5(0x174)]);if(_0x288bc8)return _0x26f032+_0x288bc8;const _0x35ef8c=nextCronRun(_0x41fbdd[_0x2475b5(0x174)],new Date(_0x26f032));return _0x35ef8c?_0x35ef8c[_0x47a9a5(0x187)]():null;}export function prepareForExecution(_0x3471fb,_0x1f39eb){return{..._0x3471fb,'lastAttemptAt':_0x1f39eb,'nextRunAt':calculateNextRunFrom(_0x3471fb,_0x1f39eb)};}export const DEFAULT_CATCHUP_GRACE_MS=(0xad*-0x2a+-0x220d+0x3e75)*(0x3*-0xbaa+-0x1*-0x1a05+0x935)*(-0x1a27+0x110b+0x958)*(-0x4*-0xbc+-0x83*-0x5+-0x197);export const DEFAULT_FAST_RESUME_MS=(-0x23fa+-0x5c*0x34+-0x36b9*-0x1)*(0x3*-0x484+-0xaf7+0x18bf)*(-0x235a+-0xe7f*0x1+0x35c1);function _0x4e78(_0x26af73,_0x5f46a0){_0x26af73=_0x26af73-(-0x753*0x5+-0xd80+0x3386);const _0x4ddddd=_0xd2ba();let _0x2d46c5=_0x4ddddd[_0x26af73];if(_0x4e78['BknVEL']===undefined){var _0x2e7144=function(_0x522add){const _0x3048df='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0xd5b1f3='',_0x2d3706='',_0x26d0d1=_0xd5b1f3+_0x2e7144;for(let _0x25917d=0x281*-0xe+-0x11a0+0x34ae*0x1,_0x1ff20f,_0x4900bd,_0x200803=0x1*0x100f+-0x2427*-0x1+-0x1a1b*0x2;_0x4900bd=_0x522add['charAt'](_0x200803++);~_0x4900bd&&(_0x1ff20f=_0x25917d%(0x1*-0x67f+-0x187f+0x62*0x51)?_0x1ff20f*(0xbb6+-0x2e*0x63+0x654)+_0x4900bd:_0x4900bd,_0x25917d++%(0x7a5+0x243+-0x9e4))?_0xd5b1f3+=_0x26d0d1['charCodeAt'](_0x200803+(-0x1*-0x2363+0x17a3+-0x3afc))-(0x72b*-0x5+0x1685+-0x12*-0xbe)!==0x1*0xf27+-0x48b+-0xa9c?String['fromCharCode'](-0x2007+-0x2*0x7bb+0x307c&_0x1ff20f>>(-(0x155e+-0x1*0x1faf+-0x1*-0xa53)*_0x25917d&0x70c*0x3+0x31a*0x3+-0x1e6c)):_0x25917d:-0x1696+-0x509*0x5+0x2fc3){_0x4900bd=_0x3048df['indexOf'](_0x4900bd);}for(let _0x423abd=-0x236b+0x625*0x5+0x4b2,_0x880b4f=_0xd5b1f3['length'];_0x423abd<_0x880b4f;_0x423abd++){_0x2d3706+='%'+('00'+_0xd5b1f3['charCodeAt'](_0x423abd)['toString'](-0x1771+-0xff5+-0x13bb*-0x2))['slice'](-(0x42f*0x7+0x16cb+-0x3412));}return decodeURIComponent(_0x2d3706);};_0x4e78['vUqRrH']=_0x2e7144,_0x4e78['qwmRRn']={},_0x4e78['BknVEL']=!![];}const _0x2aaf65=_0x4ddddd[0xc8e+0x2411*0x1+-0x309f],_0x427418=_0x26af73+_0x2aaf65,_0x39038a=_0x4e78['qwmRRn'][_0x427418];if(!_0x39038a){const _0x5b4d40=function(_0x5199d6){this['SWmGrZ']=_0x5199d6,this['RzoQur']=[-0x8db+-0x166e+0x1f4a,0x245*-0x8+-0x1b24+0x2d4c,-0x1355+-0xd01+-0x1*-0x2056],this['NPFcUe']=function(){return'newState';},this['zAORWD']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['hquDDp']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x5b4d40['prototype']['eGYsUA']=function(){const _0x3a839f=new RegExp(this['zAORWD']+this['hquDDp']),_0x457d86=_0x3a839f['test'](this['NPFcUe']['toString']())?--this['RzoQur'][-0xcfc+0x1*0x109c+-0x39f]:--this['RzoQur'][0x2383+0x135d*0x1+-0x36e0];return this['jFOWjv'](_0x457d86);},_0x5b4d40['prototype']['jFOWjv']=function(_0x4b9348){if(!Boolean(~_0x4b9348))return _0x4b9348;return this['IoOTML'](this['SWmGrZ']);},_0x5b4d40['prototype']['IoOTML']=function(_0x4da624){for(let _0x4c63f7=0x26c3+-0x1ac4+-0xbff,_0x5adfc4=this['RzoQur']['length'];_0x4c63f7<_0x5adfc4;_0x4c63f7++){this['RzoQur']['push'](Math['round'](Math['random']())),_0x5adfc4=this['RzoQur']['length'];}return _0x4da624(this['RzoQur'][0xd66+0x2*-0x937+0x508]);},new _0x5b4d40(_0x4e78)['eGYsUA'](),_0x2d46c5=_0x4e78['vUqRrH'](_0x2d46c5),_0x4e78['qwmRRn'][_0x427418]=_0x2d46c5;}else _0x2d46c5=_0x39038a;return _0x2d46c5;}function slotAlreadyAttempted(_0x17e905,_0x146b5e){const _0x116c59=_0x4e78,_0x20396d=_0x4e78;if(!_0x17e905['lastAttemp'+_0x116c59(0x191)])return![];const _0x47b75b=parseInterval(_0x17e905[_0x116c59(0x174)]);if(_0x47b75b!==null)return _0x146b5e-_0x17e905[_0x20396d(0x16a)+_0x20396d(0x191)]<_0x47b75b;const _0x573596=prevCronRun(_0x17e905[_0x20396d(0x174)],new Date(_0x146b5e));if(!_0x573596)return![];return _0x17e905[_0x20396d(0x16a)+_0x116c59(0x191)]>=_0x573596[_0x116c59(0x187)]();}export function handleStartupCatchup(_0x276c2f,_0x1ddff9,_0xa7ea88=DEFAULT_CATCHUP_GRACE_MS,_0x2d3864={}){const _0x34e0f0=_0x4e78,_0x5c1466=_0x4e78,_0x338013=_0x2d3864[_0x34e0f0(0x169)+'Ms']??DEFAULT_FAST_RESUME_MS;return _0x276c2f[_0x34e0f0(0x172)](_0x2df2bd=>{const _0x2c2573=_0x5c1466,_0x305530=_0x34e0f0;if(!_0x2df2bd[_0x2c2573(0x17c)])return _0x2df2bd;if(!_0x2df2bd['lastAttemp'+_0x305530(0x191)])return _0x2df2bd;const _0xa5f4b=typeof _0x2df2bd[_0x305530(0x176)]===_0x305530(0x173)&&_0x2df2bd[_0x2c2573(0x176)]>=_0x2df2bd[_0x305530(0x16a)+'tAt'];if(_0xa5f4b)return _0x2df2bd;const _0xf478e8=_0x1ddff9-_0x2df2bd['lastAttemp'+'tAt'];if(_0xf478e8<=-0x1a30+-0x11a*-0xe+0xac4)return _0x2df2bd;if(_0xf478e8>_0xa7ea88)return _0x2df2bd;if(slotAlreadyAttempted(_0x2df2bd,_0x1ddff9)){const _0x4b05c8=_0x2d3864[_0x305530(0x18b)+_0x2c2573(0x177)]===!![]&&_0x2df2bd[_0x305530(0x18c)]!==![]&&_0xf478e8<_0x338013&&_0x2df2bd['lastFastRe'+'sumeAttemp'+'tAt']!==_0x2df2bd[_0x305530(0x16a)+'tAt'];if(!_0x4b05c8)return _0x2df2bd;return{..._0x2df2bd,'nextRunAt':_0x1ddff9,'lastFastResumeAttemptAt':_0x2df2bd[_0x305530(0x16a)+'tAt']};}return{..._0x2df2bd,'nextRunAt':_0x1ddff9};});}