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.
- package/CHANGELOG.md +25 -0
- package/README.md +25 -31
- package/dist/claude.js +1 -102
- package/dist/config.js +1 -96
- package/dist/engine.js +1 -90
- package/dist/find-claude-binary.js +1 -98
- package/dist/handlers/async-agent-chunk-handler.js +1 -50
- package/dist/handlers/background-bypass.js +1 -75
- package/dist/handlers/commands.js +1 -2336
- package/dist/handlers/cron-progress.js +1 -52
- package/dist/handlers/document.js +1 -194
- package/dist/handlers/message.js +1 -959
- package/dist/handlers/photo.js +1 -154
- package/dist/handlers/platform-message.js +1 -360
- package/dist/handlers/stuck-timer.js +1 -54
- package/dist/handlers/video.js +1 -237
- package/dist/handlers/voice.js +1 -148
- package/dist/i18n.js +1 -805
- package/dist/index.js +1 -697
- package/dist/init-data-dir.js +1 -98
- package/dist/middleware/auth.js +1 -233
- package/dist/migrate.js +1 -162
- package/dist/paths.js +1 -146
- package/dist/platforms/discord.js +1 -175
- package/dist/platforms/index.js +1 -130
- package/dist/platforms/signal.js +1 -205
- package/dist/platforms/slack-slash-parser.js +1 -32
- package/dist/platforms/slack.js +1 -501
- package/dist/platforms/telegram.js +1 -111
- package/dist/platforms/types.js +1 -8
- package/dist/platforms/whatsapp-auth-helpers.js +1 -53
- package/dist/platforms/whatsapp.js +1 -707
- package/dist/providers/claude-sdk-provider.js +1 -565
- package/dist/providers/codex-cli-provider.js +1 -134
- package/dist/providers/index.js +1 -7
- package/dist/providers/ollama-provider.js +1 -32
- package/dist/providers/openai-compatible.js +1 -406
- package/dist/providers/registry.js +1 -352
- package/dist/providers/runtime-header.js +1 -45
- package/dist/providers/tool-executor.js +1 -475
- package/dist/providers/types.js +1 -227
- package/dist/services/access.js +1 -144
- package/dist/services/allowed-users-gate.js +1 -56
- package/dist/services/alvin-dispatch.js +1 -174
- package/dist/services/alvin-mcp-tools.js +1 -104
- package/dist/services/asset-index.js +1 -224
- package/dist/services/async-agent-parser.js +1 -418
- package/dist/services/async-agent-watcher.js +1 -583
- package/dist/services/auto-diagnostic.js +1 -228
- package/dist/services/broadcast.js +1 -52
- package/dist/services/browser-manager.js +1 -562
- package/dist/services/browser-webfetch.js +1 -127
- package/dist/services/browser.js +1 -121
- package/dist/services/cdp-bootstrap.js +1 -357
- package/dist/services/compaction.js +1 -144
- package/dist/services/critical-notify.js +1 -203
- package/dist/services/cron-resolver.js +1 -58
- package/dist/services/cron-scheduling.js +1 -310
- package/dist/services/cron.js +1 -861
- package/dist/services/custom-tools.js +1 -317
- package/dist/services/delivery-queue.js +1 -173
- package/dist/services/delivery-registry.js +1 -21
- package/dist/services/disk-cleanup.js +1 -203
- package/dist/services/elevenlabs.js +1 -58
- package/dist/services/embeddings/auto-detect.js +1 -74
- package/dist/services/embeddings/fts5.js +1 -108
- package/dist/services/embeddings/gemini.js +1 -65
- package/dist/services/embeddings/index.js +1 -496
- package/dist/services/embeddings/ollama.js +1 -78
- package/dist/services/embeddings/openai.js +1 -49
- package/dist/services/embeddings/provider.js +1 -22
- package/dist/services/embeddings/vector-base.js +1 -113
- package/dist/services/embeddings-migration.js +1 -193
- package/dist/services/embeddings.js +1 -9
- package/dist/services/env-file.js +1 -50
- package/dist/services/exec-guard.js +1 -71
- package/dist/services/fallback-order.js +1 -154
- package/dist/services/file-permissions.js +1 -93
- package/dist/services/heartbeat-file.js +1 -65
- package/dist/services/heartbeat.js +1 -313
- package/dist/services/hooks.js +1 -44
- package/dist/services/imagegen.js +1 -72
- package/dist/services/language-detect.js +1 -154
- package/dist/services/markdown.js +1 -63
- package/dist/services/mcp.js +1 -263
- package/dist/services/memory-extractor.js +1 -178
- package/dist/services/memory-inject-mode.js +1 -43
- package/dist/services/memory-layers.js +1 -156
- package/dist/services/memory.js +1 -146
- package/dist/services/ollama-manager.js +1 -339
- package/dist/services/permissions-wizard.js +1 -291
- package/dist/services/personality.js +1 -376
- package/dist/services/plugins.js +1 -171
- package/dist/services/preflight.js +1 -292
- package/dist/services/process-manager.js +1 -291
- package/dist/services/release-highlights.js +1 -79
- package/dist/services/reminders.js +1 -97
- package/dist/services/restart.js +1 -48
- package/dist/services/security-audit.js +1 -74
- package/dist/services/self-diagnosis.js +1 -272
- package/dist/services/self-search.js +1 -129
- package/dist/services/session-persistence.js +1 -237
- package/dist/services/session.js +1 -282
- package/dist/services/skills.js +1 -290
- package/dist/services/ssrf-guard.js +1 -162
- package/dist/services/standing-orders.js +1 -29
- package/dist/services/steer-channel.js +1 -46
- package/dist/services/stop-controller.js +1 -52
- package/dist/services/subagent-dedup.js +1 -86
- package/dist/services/subagent-delivery.js +1 -452
- package/dist/services/subagent-stats.js +1 -123
- package/dist/services/subagents.js +1 -814
- package/dist/services/sudo.js +1 -329
- package/dist/services/telegram.js +1 -158
- package/dist/services/timing-safe-bearer.js +1 -51
- package/dist/services/tool-discovery.js +1 -214
- package/dist/services/trends.js +1 -580
- package/dist/services/updater.js +1 -291
- package/dist/services/usage-tracker.js +1 -144
- package/dist/services/users.js +1 -271
- package/dist/services/voice.js +1 -104
- package/dist/services/watchdog-brake.js +1 -154
- package/dist/services/watchdog.js +1 -311
- package/dist/services/workspaces.js +1 -276
- package/dist/tui/index.js +1 -667
- package/dist/util/console-formatter.js +1 -109
- package/dist/util/debounce.js +1 -24
- package/dist/util/telegram-error-filter.js +1 -62
- package/dist/version.js +1 -24
- package/dist/web/bind-strategy.js +1 -42
- package/dist/web/canvas.js +1 -30
- package/dist/web/doctor-api.js +1 -604
- package/dist/web/openai-compat.js +1 -252
- package/dist/web/server.js +1 -1902
- package/dist/web/setup-api.js +1 -1101
- package/package.json +5 -2
- 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};});}
|