alvin-bot 5.7.0 → 5.8.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 (137) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -31
  3. package/dist/claude.js +1 -102
  4. package/dist/config.js +1 -96
  5. package/dist/engine.js +1 -90
  6. package/dist/find-claude-binary.js +1 -98
  7. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  8. package/dist/handlers/background-bypass.js +1 -75
  9. package/dist/handlers/commands.js +1 -2336
  10. package/dist/handlers/cron-progress.js +1 -52
  11. package/dist/handlers/document.js +1 -194
  12. package/dist/handlers/message.js +1 -959
  13. package/dist/handlers/photo.js +1 -154
  14. package/dist/handlers/platform-message.js +1 -360
  15. package/dist/handlers/stuck-timer.js +1 -54
  16. package/dist/handlers/video.js +1 -237
  17. package/dist/handlers/voice.js +1 -148
  18. package/dist/i18n.js +1 -805
  19. package/dist/index.js +1 -697
  20. package/dist/init-data-dir.js +1 -98
  21. package/dist/middleware/auth.js +1 -233
  22. package/dist/migrate.js +1 -162
  23. package/dist/paths.js +1 -146
  24. package/dist/platforms/discord.js +1 -175
  25. package/dist/platforms/index.js +1 -130
  26. package/dist/platforms/signal.js +1 -205
  27. package/dist/platforms/slack-slash-parser.js +1 -32
  28. package/dist/platforms/slack.js +1 -501
  29. package/dist/platforms/telegram.js +1 -111
  30. package/dist/platforms/types.js +1 -8
  31. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  32. package/dist/platforms/whatsapp.js +1 -707
  33. package/dist/providers/claude-sdk-provider.js +1 -565
  34. package/dist/providers/codex-cli-provider.js +1 -134
  35. package/dist/providers/index.js +1 -7
  36. package/dist/providers/ollama-provider.js +1 -32
  37. package/dist/providers/openai-compatible.js +1 -406
  38. package/dist/providers/registry.js +1 -352
  39. package/dist/providers/runtime-header.js +1 -45
  40. package/dist/providers/tool-executor.js +1 -475
  41. package/dist/providers/types.js +1 -227
  42. package/dist/services/access.js +1 -144
  43. package/dist/services/allowed-users-gate.js +1 -56
  44. package/dist/services/alvin-dispatch.js +1 -174
  45. package/dist/services/alvin-mcp-tools.js +1 -104
  46. package/dist/services/asset-index.js +1 -224
  47. package/dist/services/async-agent-parser.js +1 -418
  48. package/dist/services/async-agent-watcher.js +1 -583
  49. package/dist/services/auto-diagnostic.js +1 -228
  50. package/dist/services/broadcast.js +1 -52
  51. package/dist/services/browser-manager.js +1 -562
  52. package/dist/services/browser-webfetch.js +1 -127
  53. package/dist/services/browser.js +1 -121
  54. package/dist/services/cdp-bootstrap.js +1 -357
  55. package/dist/services/compaction.js +1 -144
  56. package/dist/services/critical-notify.js +1 -203
  57. package/dist/services/cron-resolver.js +1 -58
  58. package/dist/services/cron-scheduling.js +1 -310
  59. package/dist/services/cron.js +1 -861
  60. package/dist/services/custom-tools.js +1 -317
  61. package/dist/services/delivery-queue.js +1 -173
  62. package/dist/services/delivery-registry.js +1 -21
  63. package/dist/services/disk-cleanup.js +1 -203
  64. package/dist/services/elevenlabs.js +1 -58
  65. package/dist/services/embeddings/auto-detect.js +1 -74
  66. package/dist/services/embeddings/fts5.js +1 -108
  67. package/dist/services/embeddings/gemini.js +1 -65
  68. package/dist/services/embeddings/index.js +1 -496
  69. package/dist/services/embeddings/ollama.js +1 -78
  70. package/dist/services/embeddings/openai.js +1 -49
  71. package/dist/services/embeddings/provider.js +1 -22
  72. package/dist/services/embeddings/vector-base.js +1 -113
  73. package/dist/services/embeddings-migration.js +1 -193
  74. package/dist/services/embeddings.js +1 -9
  75. package/dist/services/env-file.js +1 -50
  76. package/dist/services/exec-guard.js +1 -71
  77. package/dist/services/fallback-order.js +1 -154
  78. package/dist/services/file-permissions.js +1 -93
  79. package/dist/services/heartbeat-file.js +1 -65
  80. package/dist/services/heartbeat.js +1 -313
  81. package/dist/services/hooks.js +1 -44
  82. package/dist/services/imagegen.js +1 -72
  83. package/dist/services/language-detect.js +1 -154
  84. package/dist/services/markdown.js +1 -63
  85. package/dist/services/mcp.js +1 -263
  86. package/dist/services/memory-extractor.js +1 -178
  87. package/dist/services/memory-inject-mode.js +1 -43
  88. package/dist/services/memory-layers.js +1 -156
  89. package/dist/services/memory.js +1 -146
  90. package/dist/services/ollama-manager.js +1 -339
  91. package/dist/services/permissions-wizard.js +1 -291
  92. package/dist/services/personality.js +1 -376
  93. package/dist/services/plugins.js +1 -171
  94. package/dist/services/preflight.js +1 -292
  95. package/dist/services/process-manager.js +1 -291
  96. package/dist/services/release-highlights.js +1 -79
  97. package/dist/services/reminders.js +1 -97
  98. package/dist/services/restart.js +1 -48
  99. package/dist/services/security-audit.js +1 -74
  100. package/dist/services/self-diagnosis.js +1 -272
  101. package/dist/services/self-search.js +1 -129
  102. package/dist/services/session-persistence.js +1 -237
  103. package/dist/services/session.js +1 -282
  104. package/dist/services/skills.js +1 -290
  105. package/dist/services/ssrf-guard.js +1 -162
  106. package/dist/services/standing-orders.js +1 -29
  107. package/dist/services/steer-channel.js +1 -46
  108. package/dist/services/stop-controller.js +1 -52
  109. package/dist/services/subagent-dedup.js +1 -86
  110. package/dist/services/subagent-delivery.js +1 -452
  111. package/dist/services/subagent-stats.js +1 -123
  112. package/dist/services/subagents.js +1 -814
  113. package/dist/services/sudo.js +1 -329
  114. package/dist/services/telegram.js +1 -158
  115. package/dist/services/timing-safe-bearer.js +1 -51
  116. package/dist/services/tool-discovery.js +1 -214
  117. package/dist/services/trends.js +1 -580
  118. package/dist/services/updater.js +1 -291
  119. package/dist/services/usage-tracker.js +1 -144
  120. package/dist/services/users.js +1 -271
  121. package/dist/services/voice.js +1 -104
  122. package/dist/services/watchdog-brake.js +1 -154
  123. package/dist/services/watchdog.js +1 -311
  124. package/dist/services/workspaces.js +1 -276
  125. package/dist/tui/index.js +1 -667
  126. package/dist/util/console-formatter.js +1 -109
  127. package/dist/util/debounce.js +1 -24
  128. package/dist/util/telegram-error-filter.js +1 -62
  129. package/dist/version.js +1 -24
  130. package/dist/web/bind-strategy.js +1 -42
  131. package/dist/web/canvas.js +1 -30
  132. package/dist/web/doctor-api.js +1 -604
  133. package/dist/web/openai-compat.js +1 -252
  134. package/dist/web/server.js +1 -1902
  135. package/dist/web/setup-api.js +1 -1101
  136. package/package.json +5 -2
  137. 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(_0x1b8130,_0x3f551b){const _0x3ab9e4=_0x43d2,_0x42db16=_0x43d2,_0x4bca0b=_0x1b8130();while(!![]){try{const _0x39d3c6=parseInt(_0x3ab9e4(0x1e6))/(0x1*0xaab+-0x1332+0x1c*0x4e)+parseInt(_0x42db16(0x1f4))/(0x21bb+-0x2d6*0x1+-0x1*0x1ee3)*(parseInt(_0x3ab9e4(0x1b8))/(0x1*-0x14c9+-0xc48+0x845*0x4))+-parseInt(_0x42db16(0x1e4))/(0x15b6+0x18d6+0x8*-0x5d1)+parseInt(_0x3ab9e4(0x1df))/(0x98e+0x22c7+-0x2c50)+-parseInt(_0x42db16(0x1c9))/(-0x13bb*0x1+-0x1ced+0x1*0x30ae)*(parseInt(_0x42db16(0x1f0))/(-0xcc8+0x1d*0x8b+-0x8*0x5e))+-parseInt(_0x42db16(0x1e0))/(-0x8*-0x409+0x8*0x3d1+0x38*-0x11f)*(parseInt(_0x42db16(0x1d2))/(-0x1eaa+-0x2ad*0x9+0x36c8))+parseInt(_0x3ab9e4(0x1db))/(-0x1893+0x1*0x5a1+0x87*0x24);if(_0x39d3c6===_0x3f551b)break;else _0x4bca0b['push'](_0x4bca0b['shift']());}catch(_0x5b1e02){_0x4bca0b['push'](_0x4bca0b['shift']());}}}(_0x1d30,-0x215f*0x22+-0xdaafe+-0x2*-0xd1a1a));const _0xdb1634=(function(){let _0x40fc2c=!![];return function(_0x52c194,_0x5b6293){const _0x20beb2=_0x40fc2c?function(){if(_0x5b6293){const _0x17108d=_0x5b6293['apply'](_0x52c194,arguments);return _0x5b6293=null,_0x17108d;}}:function(){};return _0x40fc2c=![],_0x20beb2;};}()),_0x24a6d4=_0xdb1634(this,function(){const _0x15db62=_0x43d2,_0x5b623e=_0x43d2;return _0x24a6d4[_0x15db62(0x1d3)]()[_0x15db62(0x1be)]('(((.+)+)+)'+'+$')[_0x5b623e(0x1d3)]()[_0x5b623e(0x1e8)+'r'](_0x24a6d4)[_0x5b623e(0x1be)]('(((.+)+)+)'+'+$');});_0x24a6d4();import{spawn,execFileSync,spawnSync}from'child_process';import{appendFileSync,mkdirSync}from'fs';function _0x43d2(_0x32e714,_0x2e4a5b){_0x32e714=_0x32e714-(0xcd7+-0x2659+0x1b2d);const _0x8c7c0e=_0x1d30();let _0x56066c=_0x8c7c0e[_0x32e714];if(_0x43d2['hGGQaI']===undefined){var _0x1332f9=function(_0x109e69){const _0x1d1bed='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x227e7d='',_0x3bc5ef='',_0x3d6298=_0x227e7d+_0x1332f9;for(let _0x33ddc2=-0x2*0x794+-0x29*-0x67+-0x157,_0xf25c90,_0x3014cc,_0xc3a92=-0x1*-0x1579+0x26c8+-0x3c41;_0x3014cc=_0x109e69['charAt'](_0xc3a92++);~_0x3014cc&&(_0xf25c90=_0x33ddc2%(-0xc9d*-0x2+0x16ee+-0x9c*0x4f)?_0xf25c90*(-0x1a56+-0x194c+-0x1*-0x33e2)+_0x3014cc:_0x3014cc,_0x33ddc2++%(0x636*0x1+-0xd08+0x6d6))?_0x227e7d+=_0x3d6298['charCodeAt'](_0xc3a92+(-0x36*-0x7b+-0x1*0x1633+-0xd*0x49))-(0x1767+0xb95+-0x3e2*0x9)!==0x22*0xc0+0x17ae+-0x2*0x1897?String['fromCharCode'](0x181b+-0x3*0x4ec+-0x858&_0xf25c90>>(-(-0x2*-0x716+0x1cbb+-0x2ae5)*_0x33ddc2&0x20ed*-0x1+-0x2*-0x8f4+0xf0b)):_0x33ddc2:0xdb8+-0x16b1+0x8f9){_0x3014cc=_0x1d1bed['indexOf'](_0x3014cc);}for(let _0xd6580a=-0x127e+-0xccf+0x1f4d,_0x325d9d=_0x227e7d['length'];_0xd6580a<_0x325d9d;_0xd6580a++){_0x3bc5ef+='%'+('00'+_0x227e7d['charCodeAt'](_0xd6580a)['toString'](-0x1459+-0x41c+-0x1*-0x1885))['slice'](-(0x2e3*0x6+-0x9c1*0x3+0xbf3));}return decodeURIComponent(_0x3bc5ef);};_0x43d2['YHkYgI']=_0x1332f9,_0x43d2['LpoODb']={},_0x43d2['hGGQaI']=!![];}const _0x3a87d8=_0x8c7c0e[0x8*-0x4b7+-0x1a4+-0x1*-0x275c],_0x166d9b=_0x32e714+_0x3a87d8,_0x390cd1=_0x43d2['LpoODb'][_0x166d9b];if(!_0x390cd1){const _0x3b145c=function(_0x43486b){this['KzmEmn']=_0x43486b,this['vCPDuw']=[0x3e5*-0x6+-0x93d+0x209c,-0x1d*-0x13e+-0x20d7+0x5*-0xa3,-0x330+-0x5c0+-0xd*-0xb0],this['xVhYdM']=function(){return'newState';},this['KApTAR']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['rVmFqI']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x3b145c['prototype']['ymOoQG']=function(){const _0x6bcf54=new RegExp(this['KApTAR']+this['rVmFqI']),_0x135eb1=_0x6bcf54['test'](this['xVhYdM']['toString']())?--this['vCPDuw'][-0xe64+-0x114e*-0x2+-0x1437]:--this['vCPDuw'][-0xa47+-0x1f43+0x298a];return this['MwgEiN'](_0x135eb1);},_0x3b145c['prototype']['MwgEiN']=function(_0x50e746){if(!Boolean(~_0x50e746))return _0x50e746;return this['QkSzTs'](this['KzmEmn']);},_0x3b145c['prototype']['QkSzTs']=function(_0x2b1447){for(let _0x4bff5a=-0x1d46+-0x11*0xf+0x1e45,_0x14aad9=this['vCPDuw']['length'];_0x4bff5a<_0x14aad9;_0x4bff5a++){this['vCPDuw']['push'](Math['round'](Math['random']())),_0x14aad9=this['vCPDuw']['length'];}return _0x2b1447(this['vCPDuw'][-0x1*0x2517+-0x67*-0x4+0x237b]);},new _0x3b145c(_0x43d2)['ymOoQG'](),_0x56066c=_0x43d2['YHkYgI'](_0x56066c),_0x43d2['LpoODb'][_0x166d9b]=_0x56066c;}else _0x56066c=_0x390cd1;return _0x56066c;}import{join}from'path';import{homedir}from'os';function isDisabled(){const _0x50dbef=_0x43d2,_0x565237=_0x43d2;return process[_0x50dbef(0x1cf)]['ALVIN_DISA'+'BLE_CRITIC'+_0x565237(0x1ad)]===_0x50dbef(0x1fe)||process[_0x565237(0x1cf)][_0x50dbef(0x1bf)+_0x50dbef(0x1ee)+_0x565237(0x204)+'N']===_0x565237(0x1fe);}function resolveOptions(_0x2ea6ee){const _0x4335a0=_0x43d2,_0x20716b=_0x43d2,_0x780101=_0x2ea6ee?.['botToken']??process[_0x4335a0(0x1cf)][_0x4335a0(0x1ec)]??undefined;let _0x458aaf=_0x2ea6ee?.[_0x4335a0(0x1bd)+'d'];if(_0x458aaf===undefined&&process[_0x20716b(0x1cf)][_0x20716b(0x1b9)+'ERS']){const _0x4f8030=process[_0x4335a0(0x1cf)][_0x20716b(0x1b9)+_0x20716b(0x1fd)][_0x4335a0(0x1f6)](',')[0x26c8+0x2349+-0x4a11]?.['trim']();if(_0x4f8030){const _0x4af09d=parseInt(_0x4f8030,-0xc9d*-0x2+0x16ee+-0x805*0x6);if(Number[_0x20716b(0x1cd)](_0x4af09d))_0x458aaf=_0x4af09d;}}return{'botToken':_0x780101,'adminChatId':_0x458aaf};}function writeFileFlag(_0x300ff7){const _0x1cea85=_0x43d2,_0xaec35b=_0x43d2;try{const _0x2bd7c9=join(homedir(),_0x1cea85(0x1b2));mkdirSync(_0x2bd7c9,{'recursive':!![]});const _0x12a501=join(_0x2bd7c9,_0xaec35b(0x1fa)+'og'),_0x406f0f=(_0x300ff7['ts']||new Date())['toISOStrin'+'g'](),_0x13fe0c=['['+_0x406f0f+']\x20'+_0x300ff7['severity']['toUpperCas'+'e']()+'\x20'+_0x300ff7[_0xaec35b(0x1c1)],'\x20\x20'+_0x300ff7[_0xaec35b(0x1d9)],..._0x300ff7[_0x1cea85(0x1bb)]['split']('\x0a')[_0x1cea85(0x207)](_0x2e7ecf=>'\x20\x20'+_0x2e7ecf),..._0x300ff7[_0x1cea85(0x1f5)+'ction']?[_0xaec35b(0x1ce)+_0x1cea85(0x1d6)+_0x300ff7[_0x1cea85(0x1f5)+_0x1cea85(0x1cc)]]:[],'']['join']('\x0a');return appendFileSync(_0x12a501,_0x13fe0c),!![];}catch{return![];}}function _0x1d30(){const _0x150460=['yMXVy2TuzwXLzW','y29UC3rYDwn0BW','zw5JB2rL','BM90Awz5xsbZAW','C3rHDhvZ','qK9ux1rps0vo','Dc1VDxqGDMLHia','qKXfx1nftezFua','AM9PBG','n2TgBuzHCa','iIb3AxrOihrPDa','DgvSzwDYyw09','zxC6ia','mJyWmdjsC3fyEvm','C3vNz2vZDgvKqq','C3bSAxq','BM90Awz5xsb0zq','C2TPCa','l3nLBMrnzxnZyq','q1jjveLdquWUBa','igfKBwLUq2HHDa','zw50psi','rvjt','Dhj1zq','As50zwXLz3jHBq','ls1TyxGTDgLTzq','y2HHDf9Pzd0','CgXHDgzVCM0','iokaLca','uKvtrvjwqvrjtW','zMfPBa','y3jPDgLJywW','BwfW','DhvZpq','C2XPy2u','CMfT','quXFtK9usuzz','lM9YzY9IB3q','AxbWzwqG4Ocuig9W','C3rKzxjY','DxrMltG','lMfSDMLUlwjVDa','w2nYAxrPy2fSlq','zgLZCgXHEsbUBW','BwvZC2fNzq','Dg9vChbLCKnHCW','DgvZDa','ntfJAhrMEw0','quXmt1Dfrf9vuW','zgfYD2LU','zgv0ywLS','BwLZC2LUzW','ywrTAw5dAgf0sq','C2vHCMnO','quXwsu5FreLtqq','ue9tva','y2f0zwDVCNK','CMvWBgfJzq','ls1KyxrHlxvYBa','B3nHC2nYAxb0','yYbJDxjSihrOCG','l2rLDI9UDwXS','ihn0zg91Dd0','8j+AQcbbBhzPBIbcBW','mZKXndm0A2ncEMvv','swq9','y3vYBa','y3rPB24','AxngAw5PDgu','icbtDwDNzxn0zq','zw52','Dgv4Dd0','C2v0','nZiZodDHAejhs2q','Dg9tDhjPBMC','zxjYB3i','CgLWzq','zdOG','BM90Awz5xsbLDG','AwDUB3jL','DgL0Bgu','yM90vg9Rzw4','nZeXnta2mgTtEfbSwG','C2v2zxjPDhK','BgvNCMfTihn5BG','sfruud0LE2H0Da','odeYnZCWz2jcthLi','nJmYC0Diq2Lb','C3rKB3v0','ChvZAa','ihn0zgvYCJ0','otm1mdbNz3POuMO','DcdIGjqG','mtyWmdmWwuP4D3DJ'];_0x1d30=function(){return _0x150460;};return _0x1d30();}function macosNotification(_0x168287){const _0x14dfcf=_0x43d2,_0x3cbd30=_0x43d2;if(process[_0x14dfcf(0x202)]!==_0x14dfcf(0x1ba))return![];try{const _0x2fa448=(_0x168287[_0x14dfcf(0x1d9)]+_0x3cbd30(0x203)+_0x168287['detail']['split']('\x0a')[-0x1a56+-0x194c+-0x1*-0x33a2])[_0x3cbd30(0x1c2)](/"/g,'\x5c\x22'),_0x191ed0='Alvin\x20Bot\x20'+(_0x168287[_0x3cbd30(0x1dc)]===_0x3cbd30(0x206)?'🚨':'⚠️');return execFileSync(_0x14dfcf(0x1c4),['-e',_0x3cbd30(0x1b4)+'tification'+'\x20\x22'+_0x2fa448+(_0x14dfcf(0x1f1)+'le\x20\x22')+_0x191ed0+'\x22'],{'timeout':0xbb8,'stdio':_0x3cbd30(0x1d5)}),!![];}catch{return![];}}function telegramAdminDM(_0x200eab,_0x281ea0){const _0x56dfae=_0x43d2,_0x599c8b=_0x43d2;if(!_0x281ea0[_0x56dfae(0x1da)]||!_0x281ea0['adminChatI'+'d'])return![];const _0x15d59d=[_0x56dfae(0x1c8)+_0x599c8b(0x1e5)+_0x200eab[_0x56dfae(0x1dc)][_0x56dfae(0x1b6)+'e'](),'',_0x200eab['title'],'',_0x200eab[_0x599c8b(0x1bb)]];_0x200eab[_0x599c8b(0x1f5)+'ction']&&_0x15d59d[_0x56dfae(0x1e2)]('','Suggested:'+'\x20'+_0x200eab[_0x56dfae(0x1f5)+_0x56dfae(0x1cc)]);const _0x1e5013=_0x15d59d[_0x56dfae(0x1ef)]('\x0a'),_0x16dad5=['-s','-o',_0x56dfae(0x1c6),'-X',_0x599c8b(0x1c0),_0x56dfae(0x200),'5','https://ap'+_0x56dfae(0x1ff)+_0x599c8b(0x1ae)+_0x281ea0[_0x56dfae(0x1da)]+(_0x56dfae(0x1f9)+'ge'),'-d',_0x599c8b(0x201)+_0x281ea0[_0x56dfae(0x1bd)+'d'],_0x599c8b(0x1c3)+_0x599c8b(0x1e9),_0x599c8b(0x1d0)+_0x1e5013];if(_0x281ea0[_0x56dfae(0x1e7)+_0x56dfae(0x1ac)])try{const _0x2aac5d=_0x16dad5['filter'](_0x1ddc80=>_0x1ddc80!=='-s'&&_0x1ddc80!=='/dev/null'&&_0x1ddc80!=='-o');_0x2aac5d[_0x599c8b(0x1e2)]('-w',_0x56dfae(0x1de)+'p_code}');const _0x87c1c=spawnSync(_0x599c8b(0x1cb),_0x2aac5d,{'timeout':0x1b58,'encoding':_0x599c8b(0x1b1)}),_0x2960f0=(_0x87c1c[_0x56dfae(0x1e1)]||'')[_0x56dfae(0x1d3)](),_0x4620cd=(_0x87c1c[_0x56dfae(0x1b0)]||'')[_0x599c8b(0x1d3)]();if(_0x87c1c['status']!==0x636*0x1+-0xd08+0x6d2||!/HTTP=2\d\d/[_0x599c8b(0x1b7)](_0x2960f0))return console[_0x599c8b(0x1d4)](_0x56dfae(0x1b3)+_0x599c8b(0x1f7)+'legram\x20syn'+'c\x20curl\x20sta'+_0x56dfae(0x208)+_0x87c1c[_0x599c8b(0x1eb)]+_0x599c8b(0x1c7)+_0x2960f0[_0x56dfae(0x1ab)](-0x36*-0x7b+-0x1*0x1633+-0x7*0x89,0x1767+0xb95+-0x88d*0x4)+_0x599c8b(0x1e3)+_0x4620cd[_0x56dfae(0x1ab)](0x22*0xc0+0x17ae+-0x2*0x1897,0x181b+-0x3*0x4ec+-0x88f)),![];return!![];}catch(_0x44d3cb){return console[_0x56dfae(0x1d4)](_0x56dfae(0x1b3)+'notify]\x20te'+_0x56dfae(0x1dd)+_0x56dfae(0x1c5)+_0x56dfae(0x1f3)+(_0x44d3cb instanceof Error?_0x44d3cb[_0x56dfae(0x1b5)]:String(_0x44d3cb))),![];}try{const _0xbf90a3=spawn(_0x56dfae(0x1cb),_0x16dad5,{'detached':!![],'stdio':_0x56dfae(0x1d8)});return _0xbf90a3['unref'](),!![];}catch{return![];}}export function emitCritical(_0x55ea85,_0x4bf247){const _0x585c09=_0x43d2,_0x344890=_0x43d2;if(isDisabled())return console[_0x585c09(0x1d4)](_0x585c09(0x1b3)+_0x344890(0x1ea)+_0x585c09(0x1af)+_0x344890(0x1ed)+'env\x20var'),{'fileFlag':![],'macos':![],'telegram':![],'reachedAtLeastOne':![]};const _0x4f1553=writeFileFlag(_0x55ea85),_0x9a89c9=macosNotification(_0x55ea85),_0x4b7be9=resolveOptions(_0x4bf247),_0x1a6ab3=telegramAdminDM(_0x55ea85,{..._0x4b7be9,'blockTelegram':_0x4bf247?.[_0x585c09(0x1e7)+'ram']});return console[_0x344890(0x1d4)](_0x585c09(0x1b3)+_0x585c09(0x1d7)+_0x344890(0x1fc)+_0x55ea85[_0x585c09(0x1c1)]+'\x22\x20'+('file='+(_0x4f1553?'ok':_0x344890(0x205))+'\x20')+('macos='+(_0x9a89c9?'ok':_0x585c09(0x1f8))+'\x20')+(_0x585c09(0x1f2)+(_0x1a6ab3?'scheduled':'skip'))+(_0x1a6ab3?'':'\x20(botToken'+'='+(_0x4b7be9[_0x585c09(0x1da)]?_0x585c09(0x1d1):_0x585c09(0x1bc))+(_0x585c09(0x1fb)+_0x585c09(0x1ca))+(_0x4b7be9[_0x585c09(0x1bd)+'d']??'missing')+')')),{'fileFlag':_0x4f1553,'macos':_0x9a89c9,'telegram':_0x1a6ab3,'reachedAtLeastOne':_0x4f1553||_0x9a89c9||_0x1a6ab3};}
@@ -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(_0x322ef5,_0x442fb5){const _0x22dd3f=_0x3021,_0x44563e=_0x3021,_0x51e19c=_0x322ef5();while(!![]){try{const _0x38fba7=parseInt(_0x22dd3f(0x188))/(0x2*0xeb5+0x151f*-0x1+-0x84a)+parseInt(_0x44563e(0x184))/(0x180c+-0x23f6+0xbec)+-parseInt(_0x22dd3f(0x189))/(-0x1de*-0x4+0xc0+-0x1*0x835)*(parseInt(_0x22dd3f(0x182))/(0x1c00+-0x2c3*-0x5+-0x29cb*0x1))+parseInt(_0x22dd3f(0x187))/(-0x2f*-0x3e+-0x3*0x631+0x736)*(parseInt(_0x22dd3f(0x180))/(-0x22b7+-0x100e+0x32cb))+-parseInt(_0x44563e(0x17f))/(0xa45*-0x1+0x306+0x746)+parseInt(_0x44563e(0x183))/(-0x258d+-0x429+-0x4e*-0x89)+parseInt(_0x44563e(0x18a))/(0x1dd7*-0x1+-0x2412+0x41f2);if(_0x38fba7===_0x442fb5)break;else _0x51e19c['push'](_0x51e19c['shift']());}catch(_0x208c19){_0x51e19c['push'](_0x51e19c['shift']());}}}(_0x1893,0x318e*-0x16+0x88101+0x96eb));const _0x3747e0=(function(){let _0x4fa5c5=!![];return function(_0x5f08e1,_0x4cfaa8){const _0x5024a9=_0x4fa5c5?function(){if(_0x4cfaa8){const _0x1d882f=_0x4cfaa8['apply'](_0x5f08e1,arguments);return _0x4cfaa8=null,_0x1d882f;}}:function(){};return _0x4fa5c5=![],_0x5024a9;};}()),_0x2f86f7=_0x3747e0(this,function(){const _0x4c96f8=_0x3021,_0x23ed3b=_0x3021;return _0x2f86f7['toString']()[_0x4c96f8(0x17a)](_0x4c96f8(0x181)+'+$')[_0x4c96f8(0x179)]()['constructo'+'r'](_0x2f86f7)[_0x4c96f8(0x17a)](_0x4c96f8(0x181)+'+$');});_0x2f86f7();function _0x3021(_0xe3f0a5,_0x232747){_0xe3f0a5=_0xe3f0a5-(-0x1*0x1222+-0xe*0x139+0xa*0x3ac);const _0x33613c=_0x1893();let _0x580519=_0x33613c[_0xe3f0a5];if(_0x3021['SeEOCp']===undefined){var _0x5d00a6=function(_0x2a9c0c){const _0x1331b0='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x16aa22='',_0x42ed53='',_0x1838bb=_0x16aa22+_0x5d00a6;for(let _0xb44ea5=-0x1af+-0xde3+-0x7c9*-0x2,_0x3a9a34,_0x509e58,_0x57a539=-0x491*-0x4+0x1b27+0x4d*-0x97;_0x509e58=_0x2a9c0c['charAt'](_0x57a539++);~_0x509e58&&(_0x3a9a34=_0xb44ea5%(0x1d18+-0x1*0x14a3+0x871*-0x1)?_0x3a9a34*(0x1800+0x1*0x1a97+-0x3257)+_0x509e58:_0x509e58,_0xb44ea5++%(0xb33*-0x3+0x1684+0xb19))?_0x16aa22+=_0x1838bb['charCodeAt'](_0x57a539+(0x159*0x3+0x1410+-0x1811))-(0x28e+-0x49*-0x42+-0x1*0x1556)!==-0x2b0+0x2d*0x2+0x256?String['fromCharCode'](-0x11b7+-0x1ac6+0x2d7c&_0x3a9a34>>(-(0xb*-0x1ad+-0x81+-0x19*-0xc2)*_0xb44ea5&0x1*-0x5c7+0x161c+-0xa7*0x19)):_0xb44ea5:0x59f+-0x25aa+0x200b){_0x509e58=_0x1331b0['indexOf'](_0x509e58);}for(let _0x14761e=0x14cf+-0xde0+-0x163*0x5,_0x2853e0=_0x16aa22['length'];_0x14761e<_0x2853e0;_0x14761e++){_0x42ed53+='%'+('00'+_0x16aa22['charCodeAt'](_0x14761e)['toString'](0x3*-0x9b+0x19f8+-0x7*0x371))['slice'](-(-0x120d+-0x13a6+0x25b5));}return decodeURIComponent(_0x42ed53);};_0x3021['ULEkjA']=_0x5d00a6,_0x3021['jsdbJJ']={},_0x3021['SeEOCp']=!![];}const _0x2419f0=_0x33613c[0x3*0x91f+0xef3*-0x1+-0xc6a],_0x9fbbe4=_0xe3f0a5+_0x2419f0,_0x1db2b5=_0x3021['jsdbJJ'][_0x9fbbe4];if(!_0x1db2b5){const _0x1ab0b8=function(_0x599e8d){this['OQQHyn']=_0x599e8d,this['PkVKHd']=[0x97*-0x1a+0x1142+-0x1eb,0x291+0x2*0xeb5+0x1ffb*-0x1,-0x26a2+0x180c+0xe96],this['JiwHMT']=function(){return'newState';},this['zEHvAz']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['rsPkQX']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x1ab0b8['prototype']['RqGXfH']=function(){const _0x56226b=new RegExp(this['zEHvAz']+this['rsPkQX']),_0x4c626b=_0x56226b['test'](this['JiwHMT']['toString']())?--this['PkVKHd'][0xf65*-0x2+0x778+0x1753]:--this['PkVKHd'][0x7*0x407+-0x20c+-0x8b7*0x3];return this['ejctaO'](_0x4c626b);},_0x1ab0b8['prototype']['ejctaO']=function(_0x235777){if(!Boolean(~_0x235777))return _0x235777;return this['omtRFN'](this['OQQHyn']);},_0x1ab0b8['prototype']['omtRFN']=function(_0x2d4e8b){for(let _0x322ef5=-0x3*-0x359+-0x1*0x1e07+-0x1*-0x13fc,_0x442fb5=this['PkVKHd']['length'];_0x322ef5<_0x442fb5;_0x322ef5++){this['PkVKHd']['push'](Math['round'](Math['random']())),_0x442fb5=this['PkVKHd']['length'];}return _0x2d4e8b(this['PkVKHd'][-0x291+-0x22b7+0x2548]);},new _0x1ab0b8(_0x3021)['RqGXfH'](),_0x580519=_0x3021['ULEkjA'](_0x580519),_0x3021['jsdbJJ'][_0x9fbbe4]=_0x580519;}else _0x580519=_0x1db2b5;return _0x580519;}export function resolveJobByNameOrId(_0x5d252a,_0x33a82d){const _0x27e767=_0x3021,_0x2576fb=_0x3021,_0x101f24=_0x33a82d['trim']();if(!_0x101f24)return null;const _0x6ae129=_0x5d252a['find'](_0x199e85=>_0x199e85['id']===_0x101f24);if(_0x6ae129)return _0x6ae129;const _0x4b2bed=_0x5d252a[_0x27e767(0x17b)](_0x1b0c1e=>_0x1b0c1e[_0x2576fb(0x17d)]===_0x101f24);if(_0x4b2bed)return _0x4b2bed;const _0x36f4a9=_0x101f24['toLowerCas'+'e'](),_0x55cb3f=_0x5d252a[_0x2576fb(0x17e)](_0x349f37=>_0x349f37['name'][_0x27e767(0x17c)+'e']()===_0x36f4a9);if(_0x55cb3f[_0x27e767(0x185)]===0xec*-0x19+0x1244+0x4c9)return _0x55cb3f[-0x9d9*-0x1+0x12a9+-0x1*0x1c82];return null;}function _0x1893(){const _0x586680=['mZK2mZi3nKXAuhfLqG','CMfU','BM5PBMC','Dg9tDhjPBMC','C2vHCMnO','zMLUza','Dg9mB3DLCKnHCW','BMfTzq','zMLSDgvY','mJi0mZG3mw1IAMDtCq','nKLfyLrWBG','kcGOlISPkYKRkq','mZq0ndG0yw50Eu5X','mta0nJGYneXevvvQCa','nJeWnJe4rNbmCxDp','BgvUz3rO','B3v0Chv0','mtyXmtq3nwj0vML5uG','nde0mZvRzwzUELy','mJflq3nwAuq'];_0x1893=function(){return _0x586680;};return _0x1893();}export async function runJobNowGuard(_0x482d59,_0x46d198,_0x5a3d60){const _0xd9cf28=_0x3021,_0x190b7d=_0x3021;if(_0x46d198(_0x482d59))return{'status':'already-ru'+_0xd9cf28(0x178)};const _0x1acf88=await _0x5a3d60(_0x482d59);return{'status':_0xd9cf28(0x18b),'output':_0x1acf88[_0x190b7d(0x186)],'error':_0x1acf88['error']};}
@@ -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(_0x14f974,_0x129498){const _0x1ab19d=_0x4b48,_0x5e9a17=_0x4b48,_0x353622=_0x14f974();while(!![]){try{const _0x5192a9=parseInt(_0x1ab19d(0xbb))/(-0x18c*0xe+0x3*0x4e9+0x1*0x6ee)*(parseInt(_0x1ab19d(0xd9))/(0xdc7+0x87f+-0xb22*0x2))+parseInt(_0x1ab19d(0xb4))/(0x35f+0x74a+-0xaa6)+parseInt(_0x1ab19d(0xd6))/(0x1f*0x64+0x1c88+-0x1a*0x190)+parseInt(_0x1ab19d(0xb6))/(-0x1d45+-0xce9*-0x1+0x1061*0x1)*(parseInt(_0x1ab19d(0xca))/(0x119e+0x159b+-0x45b*0x9))+parseInt(_0x5e9a17(0xc8))/(-0x261f+0x21c1+0x465)*(-parseInt(_0x5e9a17(0xd4))/(0x10f2*-0x2+-0x1580+0x1*0x376c))+-parseInt(_0x5e9a17(0xda))/(-0x1721+-0x1*0xae9+0x2213)+parseInt(_0x1ab19d(0xcb))/(0x4*0x3ad+-0x3*-0x12f+0x1237*-0x1)*(-parseInt(_0x1ab19d(0xd8))/(-0x35*0x89+-0x153*0x11+-0x365*-0xf));if(_0x5192a9===_0x129498)break;else _0x353622['push'](_0x353622['shift']());}catch(_0x2bfd9b){_0x353622['push'](_0x353622['shift']());}}}(_0x25ba,-0x48858+0x1b385+0xa23b2));const _0x3d03bf=(function(){let _0x52f3f4=!![];return function(_0x523b31,_0x4d846c){const _0x33408e=_0x52f3f4?function(){const _0x4f18e8=_0x4b48;if(_0x4d846c){const _0x51fa26=_0x4d846c[_0x4f18e8(0xc6)](_0x523b31,arguments);return _0x4d846c=null,_0x51fa26;}}:function(){};return _0x52f3f4=![],_0x33408e;};}()),_0x44b4f9=_0x3d03bf(this,function(){const _0x3d12d7=_0x4b48,_0x2daa25=_0x4b48;return _0x44b4f9['toString']()[_0x3d12d7(0xb7)](_0x2daa25(0xdf)+'+$')[_0x2daa25(0xb8)]()[_0x2daa25(0xbf)+'r'](_0x44b4f9)['search'](_0x2daa25(0xdf)+'+$');});_0x44b4f9();function parseInterval(_0x4b142a){const _0x2859cf=_0x4b48,_0x332d87=_0x4b142a[_0x2859cf(0xba)](/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)s?$/i);if(!_0x332d87)return null;const _0x3678c8=parseFloat(_0x332d87[-0xed7+0x3b7*-0x3+0x19fd]),_0x2ac3c2=_0x332d87[-0x1220+0xbf*-0x4+0x151e]['toLowerCas'+'e'](),_0x14c1c5={'s':0x3e8,'sec':0x3e8,'m':0xea60,'min':0xea60,'h':0x36ee80,'hr':0x36ee80,'d':0x5265c00,'day':0x5265c00};return _0x3678c8*(_0x14c1c5[_0x2ac3c2]||-0xb301+0x5*-0x4208+0x5*0x94b5);}function parseFieldToken(_0x1f0de0,_0x1885d7,_0x37e082){const _0x3d3ad9=_0x4b48,_0x4c2501=_0x4b48,_0x4e2009=()=>Array['from']({'length':_0x37e082-_0x1885d7+(-0x7ba+-0x5bc*0x2+0x1333)},(_0x142a69,_0x17bbd2)=>_0x17bbd2+_0x1885d7);if(_0x1f0de0[_0x3d3ad9(0xbd)]('/')){const _0x2c4244=_0x1f0de0[_0x4c2501(0xbe)]('/'),_0x39b3a1=_0x1f0de0[_0x4c2501(0xdc)](-0xeec+-0x34*0x3a+0x1*0x1ab4,_0x2c4244),_0x116313=_0x1f0de0['slice'](_0x2c4244+(-0xcaa+0x13*0x161+-0xd88)),_0x52de43=parseInt(_0x116313,-0x1b55+-0xcc+-0x1*-0x1c2b);if(!Number[_0x3d3ad9(0xc4)](_0x52de43)||_0x52de43<=-0x90e+0x8fb*-0x4+-0x12f*-0x26)return null;let _0x579661;if(_0x39b3a1==='*')_0x579661=_0x4e2009();else{if(_0x39b3a1[_0x4c2501(0xbd)]('-')){const [_0x1fc645,_0x3dae74]=_0x39b3a1['split']('-'),_0x3e7c98=parseInt(_0x1fc645,-0xc+0x2b*-0x86+0x2*0xb4c),_0x3e32ad=parseInt(_0x3dae74,-0x24ad+0x27*0xcb+0x5ca);if(!Number[_0x3d3ad9(0xc4)](_0x3e7c98)||!Number[_0x4c2501(0xc4)](_0x3e32ad)||_0x3e7c98>_0x3e32ad||_0x3e7c98<_0x1885d7||_0x3e32ad>_0x37e082)return null;_0x579661=Array[_0x3d3ad9(0xe0)]({'length':_0x3e32ad-_0x3e7c98+(0x1ed9+0x2*-0xa07+-0xaca)},(_0x407f19,_0x18a667)=>_0x18a667+_0x3e7c98);}else{const _0x44835d=parseInt(_0x39b3a1,-0xb*-0x152+0x6a6+-0x1522);if(!Number[_0x3d3ad9(0xc4)](_0x44835d)||_0x44835d<_0x1885d7||_0x44835d>_0x37e082)return null;_0x579661=[_0x44835d];}}const _0x5aa72c=_0x579661[-0x1e8a+-0x164*-0xd+-0x91*-0x16];return _0x579661[_0x3d3ad9(0xdd)](_0x357ec7=>(_0x357ec7-_0x5aa72c)%_0x52de43===-0x87*0x45+0x9c2*-0x2+0x37e7);}if(_0x1f0de0==='*')return _0x4e2009();if(_0x1f0de0[_0x3d3ad9(0xbd)]('-')){const _0x521d6b=_0x1f0de0[_0x3d3ad9(0xc3)]('-');if(_0x521d6b[_0x4c2501(0xb5)]!==-0x1058+-0x202f+-0x3089*-0x1)return null;const _0x54f912=parseInt(_0x521d6b[-0x22*0x74+-0x7ed*0x3+0x272f],-0x1*-0x2203+-0x1c7b+0x25*-0x26),_0x1e41d6=parseInt(_0x521d6b[-0x15b5*-0x1+0x1a9e+-0x3052],-0x102f+-0x1a17*0x1+0x2a50);if(!Number['isFinite'](_0x54f912)||!Number[_0x4c2501(0xc4)](_0x1e41d6)||_0x54f912>_0x1e41d6||_0x54f912<_0x1885d7||_0x1e41d6>_0x37e082)return null;return Array[_0x3d3ad9(0xe0)]({'length':_0x1e41d6-_0x54f912+(0xcb*-0x13+-0xdce+-0x38*-0x84)},(_0x1b91a0,_0x2e02ae)=>_0x2e02ae+_0x54f912);}const _0x40e72f=parseInt(_0x1f0de0,-0x269*0xf+0x7*-0x3aa+-0x9*-0x6df);if(!Number[_0x4c2501(0xc4)](_0x40e72f)||_0x40e72f<_0x1885d7||_0x40e72f>_0x37e082)return null;return[_0x40e72f];}function parseField(_0x3fde59,_0x1633f7,_0x17782a){const _0x4d42ba=_0x4b48,_0x2ccc16=_0x4b48,_0x54ae04=_0x3fde59[_0x4d42ba(0xc3)](',')[_0x2ccc16(0xdd)](_0x4e4979=>_0x4e4979[_0x2ccc16(0xb5)]>-0xf*-0x22d+-0xf08+-0x119b);if(_0x54ae04[_0x2ccc16(0xb5)]===0x1f7+-0x1a9*-0x10+-0x6d*0x43)return null;const _0xd257f8=new Set();for(const _0x27ff51 of _0x54ae04){const _0x31f04e=parseFieldToken(_0x27ff51,_0x1633f7,_0x17782a);if(_0x31f04e===null)return null;for(const _0x257e7e of _0x31f04e)_0xd257f8[_0x2ccc16(0xde)](_0x257e7e);}const _0x2c9e05=[..._0xd257f8][_0x2ccc16(0xd7)]((_0x15394f,_0x5c264b)=>_0x15394f-_0x5c264b);return _0x2c9e05[_0x4d42ba(0xb5)]>-0x209c+0x59*0x3+0x1f91?_0x2c9e05:null;}function parseCronFields(_0xd484d0){const _0x253ac2=_0x4b48,_0x453dc4=_0x4b48,_0x434b56=_0xd484d0[_0x253ac2(0xc1)]()[_0x253ac2(0xc3)](/\s+/);if(_0x434b56['length']!==0x82a+-0x43*-0x26+-0x1217*0x1)return null;const [_0x2b4808,_0x5827f2,_0x1e43b0,_0x39f41e,_0x348f83]=_0x434b56,_0x2b3e31=parseField(_0x2b4808,0x176a+-0x37a*0x4+-0x982,-0x8e6+0x1*0x162a+-0x47*0x2f),_0x542ae7=parseField(_0x5827f2,0x149*-0x13+-0x5*-0x263+0xbc*0x11,0x1afd+-0x6*0x630+-0x51d*-0x2),_0x54f805=parseField(_0x1e43b0,0x2*0xedb+0x1a1f+-0x18d*0x24,-0x351+-0xa48*0x1+0xdb8),_0x5a33b4=parseField(_0x39f41e,-0x1*-0x9ca+0x37*-0x94+0x73*0x31,-0x176b*-0x1+-0xe9+-0x1676),_0x40b666=parseField(_0x348f83,0x2*-0x691+-0x6bd*-0x5+-0x13*0x115,0xa72+-0x2*-0x131d+-0x1853*0x2);if(!_0x2b3e31||!_0x542ae7||!_0x54f805||!_0x5a33b4||!_0x40b666)return null;return{'minutes':_0x2b3e31,'hours':_0x542ae7,'days':_0x54f805,'months':_0x5a33b4,'weekdays':_0x40b666};}function nextCronRun(_0x1f4c23,_0xeb42c8){const _0x143743=_0x4b48,_0x2f9f22=_0x4b48,_0x2654c1=parseCronFields(_0x1f4c23);if(!_0x2654c1)return null;const {minutes:_0x2be016,hours:_0x340d1e,days:_0x103052,months:_0x24ef56,weekdays:_0x2a2c73}=_0x2654c1,_0x2de879=new Date(_0xeb42c8);_0x2de879[_0x143743(0xc7)](-0x17f8*0x1+0x554+-0x12a4*-0x1,0xfad*-0x1+-0x18b2+0x2b1*0xf),_0x2de879['setMinutes'](_0x2de879[_0x143743(0xd1)]()+(-0x16*0xd4+-0x2353*-0x1+-0x111a));for(let _0x63973e=-0x6df*-0x3+0xaec+0x26d*-0xd;_0x63973e<(0x1*0xaf+0x1d42*-0x1+0x1e01*0x1)*(0x1e34+0x5*0x272+-0x2a56)*(-0x2231+0x10f8+-0x6d*-0x29);_0x63973e++){const _0x197545=_0x2de879[_0x2f9f22(0xd1)](),_0x57db25=_0x2de879[_0x143743(0xd5)](),_0x135208=_0x2de879[_0x2f9f22(0xcd)](),_0x48ece5=_0x2de879[_0x2f9f22(0xb9)]()+(-0x4*0x8b+0x1e3a+-0x1c0d),_0x350351=_0x2de879['getDay']();if(_0x2be016[_0x2f9f22(0xbd)](_0x197545)&&_0x340d1e[_0x143743(0xbd)](_0x57db25)&&_0x103052['includes'](_0x135208)&&_0x24ef56[_0x143743(0xbd)](_0x48ece5)&&_0x2a2c73[_0x143743(0xbd)](_0x350351))return _0x2de879;_0x2de879[_0x143743(0xdb)](_0x2de879[_0x143743(0xd1)]()+(0x864*0x3+-0x236+-0x16f5));}return null;}export function prevCronRun(_0x32b1fc,_0x29ed84){const _0x40dd37=_0x4b48,_0x289f90=_0x4b48,_0x381534=parseCronFields(_0x32b1fc);if(!_0x381534)return null;const {minutes:_0x239b5d,hours:_0x1c5605,days:_0x210678,months:_0x23d66f,weekdays:_0x255bb0}=_0x381534,_0x2d8f1c=new Date(_0x29ed84);_0x2d8f1c['setSeconds'](0x5*0x73+0x1d7*-0x12+-0x7*-0x469,0x1c4c+0x1c39+0x12d7*-0x3);for(let _0x203bde=0x10*0x2c+0x5*-0x36f+0xe6b;_0x203bde<(0x1697+-0xca+0x7*-0x2e9)*(-0x12a2+-0x1df2+0x30ac)*(-0x9d+-0xa7*-0x2b+-0x4*0x6cd);_0x203bde++){const _0x276b0e=_0x2d8f1c[_0x40dd37(0xd1)](),_0x539abc=_0x2d8f1c[_0x289f90(0xd5)](),_0x2aef14=_0x2d8f1c[_0x289f90(0xcd)](),_0x3b6645=_0x2d8f1c[_0x289f90(0xb9)]()+(-0x20*0xb2+0x4d*-0x4f+0x2e04),_0x23bc84=_0x2d8f1c['getDay']();if(_0x239b5d['includes'](_0x276b0e)&&_0x1c5605[_0x40dd37(0xbd)](_0x539abc)&&_0x210678[_0x40dd37(0xbd)](_0x2aef14)&&_0x23d66f[_0x40dd37(0xbd)](_0x3b6645)&&_0x255bb0['includes'](_0x23bc84))return _0x2d8f1c;_0x2d8f1c[_0x40dd37(0xdb)](_0x2d8f1c[_0x289f90(0xd1)]()-(0x126b+-0x1aec+0x882));}return null;}export function calculateNextRunFrom(_0x484f6b,_0x1c4e48){const _0x36a670=_0x4b48,_0x21584b=_0x4b48;if(!_0x484f6b[_0x36a670(0xd2)])return null;const _0x5c3df0=parseInterval(_0x484f6b[_0x36a670(0xbc)]);if(_0x5c3df0)return _0x1c4e48+_0x5c3df0;const _0x2bbeb6=nextCronRun(_0x484f6b[_0x36a670(0xbc)],new Date(_0x1c4e48));return _0x2bbeb6?_0x2bbeb6['getTime']():null;}export function prepareForExecution(_0x5439e3,_0x3a3456){return{..._0x5439e3,'lastAttemptAt':_0x3a3456,'nextRunAt':calculateNextRunFrom(_0x5439e3,_0x3a3456)};}export const DEFAULT_CATCHUP_GRACE_MS=(0xc4*0x8+-0xc58+-0x31f*-0x2)*(-0x1*0x1822+-0x2c2+0x6c8*0x4)*(-0x1f21+-0x12c4*0x1+0x1*0x3221)*(0x2542+-0x132e+-0xe2c);export const DEFAULT_FAST_RESUME_MS=(-0x1*-0x166b+-0x1a5c+0x8*0x80)*(0x3f*-0x5b+0x1*-0x3d7+0x1a78)*(-0xd2e*0x1+0x675+0xaa1);function _0x25ba(){const _0x471020=['DhjPBq','BgfZDezHC3rszq','C3bSAxq','AxngAw5PDgu','z2v0vgLTzq','yxbWBhK','C2v0u2vJB25KCW','ntz5ANrhvwG','C3rHCNq','mtm4zufsveXR','mtbZrvPnqLK','BwfW','z2v0rgf0zq','zxHWzwn0zwrszq','BgfZDfj1BKf0','BgfZDef0DgvTCa','z2v0twLUDxrLCW','zw5HyMXLza','BNvTyMvY','nJq2nta0uNPTDM5X','z2v0sg91CNm','mZGYmdK3mLP0C29PyW','C29YDa','ote2mJeZmwjYzvDUta','odqYz0XUtMH2','mZa4nJaWmxD3D2rwza','C2v0twLUDxrLCW','C2XPy2u','zMLSDgvY','ywrK','kcGOlISPkYKRkq','zNjVBq','mti3ode3n3vzveLpCG','BgvUz3rO','mteWnJy1A2LOyuL1','C2vHCMnO','Dg9tDhjPBMC','z2v0tw9UDgG','Bwf0y2G','otC2wgXbyLH5','C2nOzwr1Bgu','Aw5JBhvKzxm','Aw5KzxHpzG','y29UC3rYDwn0BW','Def0'];_0x25ba=function(){return _0x471020;};return _0x25ba();}function _0x4b48(_0x321593,_0x501b7c){_0x321593=_0x321593-(0x37d+0x6*-0x3ab+0x1339);const _0x24f5cd=_0x25ba();let _0x368399=_0x24f5cd[_0x321593];if(_0x4b48['zgYNdU']===undefined){var _0xacb5fb=function(_0x30f67b){const _0x1a2137='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x5a9765='',_0x49c97f='',_0x1a6a72=_0x5a9765+_0xacb5fb;for(let _0x39a74e=0x5a7*0x4+-0xd02+0x1*-0x99a,_0x3bd373,_0x177d0c,_0x3a5db8=-0xed7+0x3b7*-0x3+0x19fc;_0x177d0c=_0x30f67b['charAt'](_0x3a5db8++);~_0x177d0c&&(_0x3bd373=_0x39a74e%(-0x1220+0xbf*-0x4+0x1520)?_0x3bd373*(-0xeeb+0x3*-0x92c+0x31*0xdf)+_0x177d0c:_0x177d0c,_0x39a74e++%(-0x7ba+-0x5bc*0x2+0x1336))?_0x5a9765+=_0x1a6a72['charCodeAt'](_0x3a5db8+(-0xeec+-0x34*0x3a+0x1*0x1abe))-(-0xcaa+0x13*0x161+-0xd7f)!==-0x1b55+-0xcc+-0x1*-0x1c21?String['fromCharCode'](-0x90e+0x8fb*-0x4+-0xf53*-0x3&_0x3bd373>>(-(-0xc+0x2b*-0x86+0x2*0xb48)*_0x39a74e&-0x24ad+0x27*0xcb+0x5c6)):_0x39a74e:0x1ed9+0x2*-0xa07+-0xacb){_0x177d0c=_0x1a2137['indexOf'](_0x177d0c);}for(let _0x134e40=-0xb*-0x152+0x6a6+-0x152c,_0x4d11d8=_0x5a9765['length'];_0x134e40<_0x4d11d8;_0x134e40++){_0x49c97f+='%'+('00'+_0x5a9765['charCodeAt'](_0x134e40)['toString'](-0x1e8a+-0x164*-0xd+-0x1ca*-0x7))['slice'](-(-0x87*0x45+0x9c2*-0x2+0x37e9));}return decodeURIComponent(_0x49c97f);};_0x4b48['fiFrBK']=_0xacb5fb,_0x4b48['FvRljN']={},_0x4b48['zgYNdU']=!![];}const _0x429796=_0x24f5cd[-0x1058+-0x202f+-0x3087*-0x1],_0x10723e=_0x321593+_0x429796,_0x7e37c2=_0x4b48['FvRljN'][_0x10723e];if(!_0x7e37c2){const _0x73666b=function(_0x4c4446){this['NqGjwa']=_0x4c4446,this['yMdmhq']=[-0x22*0x74+-0x7ed*0x3+0x2730,-0x1*-0x2203+-0x1c7b+0x18*-0x3b,-0x15b5*-0x1+0x1a9e+-0x3053],this['IUeaTq']=function(){return'newState';},this['fXDgMW']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['zqHFCm']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x73666b['prototype']['TSakSd']=function(){const _0x1eb762=new RegExp(this['fXDgMW']+this['zqHFCm']),_0x260fda=_0x1eb762['test'](this['IUeaTq']['toString']())?--this['yMdmhq'][-0x102f+-0x1a17*0x1+0x2a47]:--this['yMdmhq'][0xcb*-0x13+-0xdce+-0x13*-0x185];return this['PmUSQO'](_0x260fda);},_0x73666b['prototype']['PmUSQO']=function(_0x5e9d3d){if(!Boolean(~_0x5e9d3d))return _0x5e9d3d;return this['YzMsJR'](this['NqGjwa']);},_0x73666b['prototype']['YzMsJR']=function(_0x7929f3){for(let _0x26df4c=-0x269*0xf+0x7*-0x3aa+-0xd*-0x4c1,_0x24d404=this['yMdmhq']['length'];_0x26df4c<_0x24d404;_0x26df4c++){this['yMdmhq']['push'](Math['round'](Math['random']())),_0x24d404=this['yMdmhq']['length'];}return _0x7929f3(this['yMdmhq'][-0xf*-0x22d+-0xf08+-0x119b]);},new _0x73666b(_0x4b48)['TSakSd'](),_0x368399=_0x4b48['fiFrBK'](_0x368399),_0x4b48['FvRljN'][_0x10723e]=_0x368399;}else _0x368399=_0x7e37c2;return _0x368399;}function slotAlreadyAttempted(_0x58cccf,_0x70e9a1){const _0xb56afe=_0x4b48,_0x1ef519=_0x4b48;if(!_0x58cccf[_0xb56afe(0xd0)+_0xb56afe(0xc0)])return![];const _0x5e6bba=parseInterval(_0x58cccf[_0xb56afe(0xbc)]);if(_0x5e6bba!==null)return _0x70e9a1-_0x58cccf[_0xb56afe(0xd0)+_0x1ef519(0xc0)]<_0x5e6bba;const _0x462dbb=prevCronRun(_0x58cccf['schedule'],new Date(_0x70e9a1));if(!_0x462dbb)return![];return _0x58cccf[_0xb56afe(0xd0)+'tAt']>=_0x462dbb[_0xb56afe(0xc5)]();}export function handleStartupCatchup(_0x635607,_0x474a77,_0x21f1a8=DEFAULT_CATCHUP_GRACE_MS,_0x22e91d={}){const _0x1c22da=_0x4b48,_0x23411c=_0x22e91d['fastResume'+'Ms']??DEFAULT_FAST_RESUME_MS;return _0x635607[_0x1c22da(0xcc)](_0x16e9fd=>{const _0x25fe50=_0x1c22da,_0x3ad7e0=_0x1c22da;if(!_0x16e9fd[_0x25fe50(0xd2)])return _0x16e9fd;if(!_0x16e9fd[_0x3ad7e0(0xd0)+'tAt'])return _0x16e9fd;const _0x5e3c8a=typeof _0x16e9fd[_0x25fe50(0xcf)]===_0x3ad7e0(0xd3)&&_0x16e9fd['lastRunAt']>=_0x16e9fd[_0x3ad7e0(0xd0)+'tAt'];if(_0x5e3c8a)return _0x16e9fd;const _0x53292f=_0x474a77-_0x16e9fd[_0x3ad7e0(0xd0)+'tAt'];if(_0x53292f<=-0x81b+0x1749+0x86*-0x1d)return _0x16e9fd;if(_0x53292f>_0x21f1a8)return _0x16e9fd;if(slotAlreadyAttempted(_0x16e9fd,_0x474a77)){const _0x12b124=_0x22e91d[_0x25fe50(0xce)+_0x3ad7e0(0xc9)]===!![]&&_0x16e9fd['autoResume']!==![]&&_0x53292f<_0x23411c&&_0x16e9fd[_0x3ad7e0(0xc2)+'sumeAttemp'+_0x3ad7e0(0xc0)]!==_0x16e9fd['lastAttemp'+_0x3ad7e0(0xc0)];if(!_0x12b124)return _0x16e9fd;return{..._0x16e9fd,'nextRunAt':_0x474a77,'lastFastResumeAttemptAt':_0x16e9fd[_0x3ad7e0(0xd0)+_0x25fe50(0xc0)]};}return{..._0x16e9fd,'nextRunAt':_0x474a77};});}