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,339 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ollama Manager — on-demand daemon lifecycle for fallback use.
|
|
3
|
-
*
|
|
4
|
-
* The bot uses Ollama as a local fallback when the primary provider is down.
|
|
5
|
-
* Historically the user had to run `ollama serve` themselves — if they forgot,
|
|
6
|
-
* the fallback silently failed. This service spawns the daemon on demand,
|
|
7
|
-
* preloads the target model into VRAM, and tears it all down once the primary
|
|
8
|
-
* provider is healthy again.
|
|
9
|
-
*
|
|
10
|
-
* Key invariants:
|
|
11
|
-
* • Only kills instances the bot started itself (tracked via PID file).
|
|
12
|
-
* An externally-managed ollama is left alone.
|
|
13
|
-
* • Preload uses Ollama's native /api/generate endpoint with an empty
|
|
14
|
-
* prompt and keep_alive=30m, so the first real query is not cold.
|
|
15
|
-
* • Unload sets keep_alive=0 to flush the model from VRAM immediately.
|
|
16
|
-
* • All spawns are detached with stdio=ignore, so the child survives the
|
|
17
|
-
* bot crashing but still gets cleaned up on graceful shutdown.
|
|
18
|
-
*/
|
|
19
|
-
import { spawn, execFile } from "child_process";
|
|
20
|
-
import { promisify } from "util";
|
|
21
|
-
import fs from "fs";
|
|
22
|
-
import { resolve, dirname } from "path";
|
|
23
|
-
import os from "os";
|
|
24
|
-
const execFileAsync = promisify(execFile);
|
|
25
|
-
const DATA_DIR = process.env.ALVIN_DATA_DIR || resolve(os.homedir(), ".alvin-bot");
|
|
26
|
-
const PID_FILE = resolve(DATA_DIR, "ollama.pid");
|
|
27
|
-
const MODEL_FILE = resolve(DATA_DIR, "ollama.model");
|
|
28
|
-
const OLLAMA_API_BASE = "http://localhost:11434";
|
|
29
|
-
const DAEMON_READY_TIMEOUT_MS = 15_000;
|
|
30
|
-
const PRELOAD_TIMEOUT_MS = 60_000;
|
|
31
|
-
const KEEP_ALIVE = "30m";
|
|
32
|
-
let managedProcess = null;
|
|
33
|
-
let managedModel = null;
|
|
34
|
-
// ── PID / Process verification ─────────────────────────────────────────────
|
|
35
|
-
/**
|
|
36
|
-
* Verify that `pid` is actually an ollama process by inspecting its command
|
|
37
|
-
* via `ps`. This prevents the classic PID-reuse bug where we'd kill a
|
|
38
|
-
* random process after a bot crash left a stale pid file pointing at
|
|
39
|
-
* something the OS has since re-assigned.
|
|
40
|
-
*/
|
|
41
|
-
async function verifyPidIsOllama(pid) {
|
|
42
|
-
try {
|
|
43
|
-
const { stdout } = await execFileAsync("ps", ["-p", String(pid), "-o", "command="], {
|
|
44
|
-
timeout: 3_000,
|
|
45
|
-
});
|
|
46
|
-
return stdout.toLowerCase().includes("ollama");
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// ps exits non-zero if pid doesn't exist — treat as "not ollama"
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function loadManagedModelFromDisk() {
|
|
54
|
-
try {
|
|
55
|
-
if (fs.existsSync(MODEL_FILE)) {
|
|
56
|
-
return fs.readFileSync(MODEL_FILE, "utf-8").trim() || null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch { /* ignore */ }
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
function persistManagedModel(model) {
|
|
63
|
-
try {
|
|
64
|
-
fs.mkdirSync(dirname(MODEL_FILE), { recursive: true });
|
|
65
|
-
if (model) {
|
|
66
|
-
fs.writeFileSync(MODEL_FILE, model, "utf-8");
|
|
67
|
-
}
|
|
68
|
-
else if (fs.existsSync(MODEL_FILE)) {
|
|
69
|
-
fs.unlinkSync(MODEL_FILE);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
console.warn(`[ollama] failed to persist model file: ${err}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Reconcile stale state left behind from a previous bot run.
|
|
78
|
-
* If the PID file points at a process that is no longer ollama (crashed,
|
|
79
|
-
* PID reused, never existed), remove the file so we don't try to kill
|
|
80
|
-
* the wrong process later. Called lazily from ensureRunning / ensureStopped.
|
|
81
|
-
*/
|
|
82
|
-
async function reconcileStalePidFile() {
|
|
83
|
-
if (!fs.existsSync(PID_FILE))
|
|
84
|
-
return;
|
|
85
|
-
try {
|
|
86
|
-
const raw = fs.readFileSync(PID_FILE, "utf-8").trim();
|
|
87
|
-
const pid = parseInt(raw, 10);
|
|
88
|
-
if (isNaN(pid) || pid <= 0) {
|
|
89
|
-
fs.unlinkSync(PID_FILE);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const isOllama = await verifyPidIsOllama(pid);
|
|
93
|
-
if (!isOllama) {
|
|
94
|
-
console.log(`[ollama] stale pid file (pid=${pid} is no longer ollama) — removing`);
|
|
95
|
-
fs.unlinkSync(PID_FILE);
|
|
96
|
-
persistManagedModel(null);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// If we can't read/parse it, drop it
|
|
101
|
-
try {
|
|
102
|
-
fs.unlinkSync(PID_FILE);
|
|
103
|
-
}
|
|
104
|
-
catch { /* ignore */ }
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
export async function isDaemonRunning() {
|
|
108
|
-
try {
|
|
109
|
-
const res = await fetch(`${OLLAMA_API_BASE}/api/tags`, {
|
|
110
|
-
signal: AbortSignal.timeout(2_000),
|
|
111
|
-
});
|
|
112
|
-
return res.ok;
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async function findOllamaBinary() {
|
|
119
|
-
// Common install paths — macOS Homebrew, Linux, /usr/local
|
|
120
|
-
const candidates = [
|
|
121
|
-
"/opt/homebrew/bin/ollama",
|
|
122
|
-
"/usr/local/bin/ollama",
|
|
123
|
-
"/usr/bin/ollama",
|
|
124
|
-
];
|
|
125
|
-
for (const p of candidates) {
|
|
126
|
-
if (fs.existsSync(p))
|
|
127
|
-
return p;
|
|
128
|
-
}
|
|
129
|
-
// Fallback: `which ollama` (async, no event-loop block)
|
|
130
|
-
try {
|
|
131
|
-
const { stdout } = await execFileAsync("which", ["ollama"], { timeout: 3_000 });
|
|
132
|
-
return stdout.trim() || null;
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
async function waitForDaemon(timeoutMs = DAEMON_READY_TIMEOUT_MS) {
|
|
139
|
-
const start = Date.now();
|
|
140
|
-
while (Date.now() - start < timeoutMs) {
|
|
141
|
-
if (await isDaemonRunning())
|
|
142
|
-
return true;
|
|
143
|
-
await new Promise(r => setTimeout(r, 500));
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
async function preloadModel(model) {
|
|
148
|
-
try {
|
|
149
|
-
await fetch(`${OLLAMA_API_BASE}/api/generate`, {
|
|
150
|
-
method: "POST",
|
|
151
|
-
headers: { "Content-Type": "application/json" },
|
|
152
|
-
body: JSON.stringify({
|
|
153
|
-
model,
|
|
154
|
-
prompt: "",
|
|
155
|
-
keep_alive: KEEP_ALIVE,
|
|
156
|
-
}),
|
|
157
|
-
signal: AbortSignal.timeout(PRELOAD_TIMEOUT_MS),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
162
|
-
console.warn(`[ollama] preload warning (model=${model}): ${msg}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
async function unloadModel(model) {
|
|
166
|
-
try {
|
|
167
|
-
await fetch(`${OLLAMA_API_BASE}/api/generate`, {
|
|
168
|
-
method: "POST",
|
|
169
|
-
headers: { "Content-Type": "application/json" },
|
|
170
|
-
body: JSON.stringify({
|
|
171
|
-
model,
|
|
172
|
-
keep_alive: 0, // immediate VRAM unload
|
|
173
|
-
}),
|
|
174
|
-
signal: AbortSignal.timeout(5_000),
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
// ignore — daemon may already be stopping
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Ensure the Ollama daemon is running and the specified model is loaded.
|
|
183
|
-
* Idempotent. If an externally-managed daemon is already running, we use
|
|
184
|
-
* it and just preload the model, but leave it for ensureStopped() to decide
|
|
185
|
-
* whether to kill it (it won't — only bot-spawned daemons get killed).
|
|
186
|
-
*/
|
|
187
|
-
export async function ensureRunning(model) {
|
|
188
|
-
// Drop any stale pid file from a previous run before deciding anything.
|
|
189
|
-
await reconcileStalePidFile();
|
|
190
|
-
if (await isDaemonRunning()) {
|
|
191
|
-
// Daemon is already up — either we started it in a previous bot run
|
|
192
|
-
// (pid file still valid) or user started it externally (no pid file).
|
|
193
|
-
// In both cases we preload the target model so the first query is warm.
|
|
194
|
-
await preloadModel(model);
|
|
195
|
-
managedModel = model;
|
|
196
|
-
// If a valid pid file exists, we inherit ownership of that daemon
|
|
197
|
-
// (it was bot-managed before a crash/restart). Update the model file.
|
|
198
|
-
if (fs.existsSync(PID_FILE)) {
|
|
199
|
-
persistManagedModel(model);
|
|
200
|
-
}
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
const binary = await findOllamaBinary();
|
|
204
|
-
if (!binary) {
|
|
205
|
-
console.error("[ollama] binary not found — install ollama first (brew install ollama)");
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
console.log(`[ollama] starting daemon: ${binary} serve`);
|
|
209
|
-
const proc = spawn(binary, ["serve"], {
|
|
210
|
-
detached: true,
|
|
211
|
-
stdio: "ignore",
|
|
212
|
-
env: process.env,
|
|
213
|
-
});
|
|
214
|
-
proc.unref();
|
|
215
|
-
if (!proc.pid) {
|
|
216
|
-
console.error("[ollama] spawn failed — no pid");
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
// Persist the PID + model so we can kill/unload correctly on cleanup,
|
|
220
|
-
// even after a bot restart loses the in-memory references.
|
|
221
|
-
try {
|
|
222
|
-
fs.mkdirSync(dirname(PID_FILE), { recursive: true });
|
|
223
|
-
fs.writeFileSync(PID_FILE, String(proc.pid), "utf-8");
|
|
224
|
-
persistManagedModel(model);
|
|
225
|
-
}
|
|
226
|
-
catch (err) {
|
|
227
|
-
console.warn(`[ollama] failed to write state files: ${err}`);
|
|
228
|
-
}
|
|
229
|
-
managedProcess = proc;
|
|
230
|
-
managedModel = model;
|
|
231
|
-
const ready = await waitForDaemon();
|
|
232
|
-
if (!ready) {
|
|
233
|
-
console.error("[ollama] daemon did not become ready within 15s");
|
|
234
|
-
// Clean up: we spawned something that didn't come up. Best effort kill.
|
|
235
|
-
try {
|
|
236
|
-
process.kill(proc.pid, "SIGTERM");
|
|
237
|
-
}
|
|
238
|
-
catch { /* ignore */ }
|
|
239
|
-
try {
|
|
240
|
-
fs.unlinkSync(PID_FILE);
|
|
241
|
-
}
|
|
242
|
-
catch { /* ignore */ }
|
|
243
|
-
persistManagedModel(null);
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
console.log(`[ollama] daemon ready — preloading model: ${model}`);
|
|
247
|
-
await preloadModel(model);
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Stop the daemon if we started it, unload the model from VRAM.
|
|
252
|
-
* Does nothing if the daemon was started externally (no PID file).
|
|
253
|
-
*/
|
|
254
|
-
export async function ensureStopped() {
|
|
255
|
-
if (!fs.existsSync(PID_FILE)) {
|
|
256
|
-
// No PID file = externally managed daemon. Don't touch it.
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
let pid = null;
|
|
260
|
-
try {
|
|
261
|
-
const raw = fs.readFileSync(PID_FILE, "utf-8").trim();
|
|
262
|
-
const parsed = parseInt(raw, 10);
|
|
263
|
-
if (!isNaN(parsed) && parsed > 0)
|
|
264
|
-
pid = parsed;
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
// ignore
|
|
268
|
-
}
|
|
269
|
-
// Verify the PID actually points at an ollama process before SIGTERM.
|
|
270
|
-
// Prevents the classic PID-reuse bug where we'd kill a random process
|
|
271
|
-
// after a bot crash/restart left a stale pid file.
|
|
272
|
-
const pidIsOllama = pid ? await verifyPidIsOllama(pid) : false;
|
|
273
|
-
if (!pidIsOllama) {
|
|
274
|
-
console.log(`[ollama] pid file points to pid=${pid} which is no longer ollama — cleaning up`);
|
|
275
|
-
try {
|
|
276
|
-
fs.unlinkSync(PID_FILE);
|
|
277
|
-
}
|
|
278
|
-
catch { /* ignore */ }
|
|
279
|
-
persistManagedModel(null);
|
|
280
|
-
managedProcess = null;
|
|
281
|
-
managedModel = null;
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
// Unload the model first so VRAM is freed even if the kill races.
|
|
285
|
-
// Model name might be in memory (current run) or on disk (survived a restart).
|
|
286
|
-
const modelToUnload = managedModel || loadManagedModelFromDisk();
|
|
287
|
-
if (modelToUnload) {
|
|
288
|
-
await unloadModel(modelToUnload);
|
|
289
|
-
}
|
|
290
|
-
try {
|
|
291
|
-
process.kill(pid, "SIGTERM");
|
|
292
|
-
console.log(`[ollama] stopped daemon pid=${pid}`);
|
|
293
|
-
}
|
|
294
|
-
catch (err) {
|
|
295
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
296
|
-
console.warn(`[ollama] failed to kill pid=${pid}: ${msg}`);
|
|
297
|
-
}
|
|
298
|
-
// Clean up state
|
|
299
|
-
try {
|
|
300
|
-
fs.unlinkSync(PID_FILE);
|
|
301
|
-
}
|
|
302
|
-
catch { /* ignore */ }
|
|
303
|
-
persistManagedModel(null);
|
|
304
|
-
managedProcess = null;
|
|
305
|
-
managedModel = null;
|
|
306
|
-
}
|
|
307
|
-
/** Whether the current daemon was spawned by the bot (via PID file). */
|
|
308
|
-
export function isBotManaged() {
|
|
309
|
-
return fs.existsSync(PID_FILE);
|
|
310
|
-
}
|
|
311
|
-
/** Currently loaded model name, if any. */
|
|
312
|
-
export function getManagedModel() {
|
|
313
|
-
return managedModel || loadManagedModelFromDisk();
|
|
314
|
-
}
|
|
315
|
-
// ── Module-load side effects ──────────────────────────────────────────────
|
|
316
|
-
//
|
|
317
|
-
// On first import (bot startup), reconcile any stale pid file from a previous
|
|
318
|
-
// crashed run AND restore the in-memory managedModel if the daemon is still
|
|
319
|
-
// alive. Best-effort — failures are logged but not fatal.
|
|
320
|
-
//
|
|
321
|
-
// NOTE: SIGTERM/SIGINT handling lives in src/index.ts (the bot's shutdown()
|
|
322
|
-
// function). That function calls ensureStopped() directly — we deliberately
|
|
323
|
-
// do NOT install our own signal handler here, to avoid racing with the
|
|
324
|
-
// bot's own cleanup path.
|
|
325
|
-
void (async () => {
|
|
326
|
-
try {
|
|
327
|
-
await reconcileStalePidFile();
|
|
328
|
-
if (fs.existsSync(PID_FILE)) {
|
|
329
|
-
const diskModel = loadManagedModelFromDisk();
|
|
330
|
-
if (diskModel) {
|
|
331
|
-
managedModel = diskModel;
|
|
332
|
-
console.log(`[ollama] restored managed state from previous run (model=${diskModel})`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
catch (err) {
|
|
337
|
-
console.warn(`[ollama] startup reconciliation failed: ${err}`);
|
|
338
|
-
}
|
|
339
|
-
})();
|
|
1
|
+
const _0x51d2f6=_0x3142,_0x46c4d8=_0x3142;(function(_0x3c4e49,_0x5a5326){const _0x21e0ae=_0x3142,_0x211c47=_0x3142,_0x125dbe=_0x3c4e49();while(!![]){try{const _0x29280f=-parseInt(_0x21e0ae(0x16f))/(-0xc30+0x877*-0x1+0x14a8)*(-parseInt(_0x21e0ae(0x17d))/(0xd68+0x1f30+-0x36e*0xd))+-parseInt(_0x211c47(0x168))/(-0x1ee4+0x7ae+0x1739)+parseInt(_0x211c47(0x169))/(-0x241f+0x1*-0x26af+0x4ad2)*(-parseInt(_0x211c47(0x14e))/(-0x3*0xc5c+0x26dc+0x1c3*-0x1))+parseInt(_0x211c47(0x164))/(-0x1531+-0xb39+0x2070)+-parseInt(_0x21e0ae(0x17a))/(-0x2*0xc54+0x3ee*0x9+-0xaaf)+parseInt(_0x211c47(0x16d))/(0x400*-0x1+-0xde+-0x2*-0x273)+parseInt(_0x21e0ae(0x121))/(-0x1*0x2271+-0x2670+0x48ea);if(_0x29280f===_0x5a5326)break;else _0x125dbe['push'](_0x125dbe['shift']());}catch(_0x25bfd8){_0x125dbe['push'](_0x125dbe['shift']());}}}(_0x2ab1,0x14b*-0x22f+0x1cf*0x886+-0x1*-0xa3ff));const _0x49e154=(function(){let _0x547aa2=!![];return function(_0x4cfecf,_0x501b59){const _0x2437d2=_0x547aa2?function(){const _0x364720=_0x3142;if(_0x501b59){const _0x36c8a4=_0x501b59[_0x364720(0x158)](_0x4cfecf,arguments);return _0x501b59=null,_0x36c8a4;}}:function(){};return _0x547aa2=![],_0x2437d2;};}()),_0x357343=_0x49e154(this,function(){const _0x59d97b=_0x3142,_0xff8f59=_0x3142;return _0x357343['toString']()[_0x59d97b(0x159)](_0x59d97b(0x144)+'+$')[_0x59d97b(0x161)]()[_0x59d97b(0x154)+'r'](_0x357343)[_0x59d97b(0x159)]('(((.+)+)+)'+'+$');});_0x357343();import{spawn,execFile}from'child_process';import{promisify}from'util';import _0x539748 from'fs';import{resolve,dirname}from'path';import _0x29415d from'os';function _0x2ab1(){const _0x208787=['mtu4nde5ohfdBvbWsa','DgfYDgLUzYbKyq','C2vYDMu','zcdIGjqGBM8GCgLK','l2jPBI9VBgXHBq','zgLUzYbTB2rLBa','zw52','DxrMltG','BweGzMLYC3qGka','Bw9UihbPzd0','zxjZAxn0ig1Vza','zxHPC3rZu3LUyW','Dw5SAw5Ru3LUyW','l2fWAs9Nzw5LCG','l3vZCI9SB2nHBa','AgLUide1CW','DhjPBq','l29WDc9OB21LyG','z2vYig9SBgfTyq','u0Lhvevstq','BI9QC29U','BgvHBMLUzYb1Ca','ywLSzwqGDg8GCa','kg1VzgvSpq','yNjLDYbPBNn0yq','zsbMCM9TihbYzq','lMfSDMLUlwjVDa','ywLSzwqGDg8GDW','C3rHBgWGB2XSyq','CgLK','BM93','BgXHBwe','w29SBgfTyv0Gza','BwvZC2fNzq','x0rjuG','kcGOlISPkYKRkq','A2LSBa','yxrL','w29SBgfTyv0GCa','Cgf3BIbMywLSzq','Dw5Yzwy','C3rYAw5NAwz5','zxjYB3i','B25JAwXPyxrPBW','igzPBgvZoIa','ntq2nJCWvgHZzhb1','ywXOB3n0oJeXna','CML0zsbZDgf0zq','ywvTB24GCMvHza','AwDUB3jL','AwXSihbPzd0','y29UC3rYDwn0BW','zw1VBJOG','DMLVDxmGCNvUia','Aw50CYb0BYbWAq','yxbWBhK','C2vHCMnO','D2fYBG','Aw5JBhvKzxm','y29TBwfUzd0','B2XSyw1H','CMvHzezPBgvtEq','ihDOAwnOigLZia','zM91BMqG4OcuigLU','Dg9tDhjPBMC','D3jPDgvgAwXLuW','ksdIGjqGCMvTB3zP','ode0odC5mMnJEvzHuW','w29SBgfTyv0GyG','Bg9N','Ag9TzwrPCG','mteXndCZneLNvhr4uq','ngDusLbNtq','ywLSzwqGDg8GAW','AwqGzMLSzsbWBW','DgfYDhvWihjLyW','mta3mZaZmJbSzLbtAKy','Ew5J','otu2m2LHq1LOEa','zwWGzMLSztOG','BM90igjLy29Tzq','B2XSyw1HlNbPza','mZbT','ywvTB24GzgLKia','EsdIGjqGChjLBg9H','Ahr0CdOVl2XVyW','w29SBgfTyv0GzG','BMLUzYaOBw9Kzq','ue9tva','mta4mJuWnJz3DezkzLq','AwXLicHWAwq9','DgfSzsbWAwqGzG','nhLQv1Llyq','CMv3l2jPBI9VBa','w29SBgfTyv0GCW','l2fWAs90ywDZ','DgLTzw91Da','yxbWBgLJyxrPBW'];_0x2ab1=function(){return _0x208787;};return _0x2ab1();}const execFileAsync=promisify(execFile),DATA_DIR=process[_0x51d2f6(0x127)]['ALVIN_DATA'+_0x51d2f6(0x143)]||resolve(_0x29415d[_0x46c4d8(0x167)](),_0x46c4d8(0x13b)),PID_FILE=resolve(DATA_DIR,_0x51d2f6(0x172)),MODEL_FILE=resolve(DATA_DIR,'ollama.mod'+'el'),OLLAMA_API_BASE=_0x51d2f6(0x176)+_0x46c4d8(0x14f)+'34',DAEMON_READY_TIMEOUT_MS=-0x6*-0xceb+-0x6deb+-0x5b01*-0x1,PRELOAD_TIMEOUT_MS=-0xe87*-0x3+0x70a4+0x3*0x1a0d,KEEP_ALIVE=_0x46c4d8(0x173);let managedProcess=null,managedModel=null;async function verifyPidIsOllama(_0x817a72){const _0x5a584e=_0x51d2f6,_0x59be8c=_0x51d2f6;try{const {stdout:_0x3f4892}=await execFileAsync('ps',['-p',String(_0x817a72),'-o',_0x5a584e(0x15c)],{'timeout':0xbb8});return _0x3f4892['toLowerCas'+'e']()[_0x59be8c(0x15b)](_0x5a584e(0x15d));}catch{return![];}}function loadManagedModelFromDisk(){const _0x337041=_0x46c4d8,_0x42756f=_0x46c4d8;try{if(_0x539748[_0x337041(0x12c)](MODEL_FILE))return _0x539748[_0x337041(0x15e)+'nc'](MODEL_FILE,_0x337041(0x128))[_0x42756f(0x131)]()||null;}catch{}return null;}function persistManagedModel(_0x2e8535){const _0x5baa43=_0x46c4d8,_0x4d04ea=_0x46c4d8;try{_0x539748['mkdirSync'](dirname(MODEL_FILE),{'recursive':!![]});if(_0x2e8535)_0x539748['writeFileS'+_0x5baa43(0x16e)](MODEL_FILE,_0x2e8535,_0x5baa43(0x128));else _0x539748[_0x5baa43(0x12c)](MODEL_FILE)&&_0x539748[_0x5baa43(0x12d)](MODEL_FILE);}catch(_0x1df2a4){console[_0x4d04ea(0x15a)](_0x4d04ea(0x177)+_0x5baa43(0x137)+_0x5baa43(0x12b)+_0x4d04ea(0x170)+_0x1df2a4);}}async function reconcileStalePidFile(){const _0x10ec87=_0x46c4d8,_0x501aa8=_0x46c4d8;if(!_0x539748[_0x10ec87(0x12c)](PID_FILE))return;try{const _0x409837=_0x539748[_0x10ec87(0x15e)+'nc'](PID_FILE,_0x501aa8(0x128))[_0x10ec87(0x131)](),_0x203579=parseInt(_0x409837,-0x192a*0x1+-0xba*0x1+0x19ee);if(isNaN(_0x203579)||_0x203579<=-0x1682*-0x1+-0x5*-0x1db+-0x1*0x1fc9){_0x539748[_0x501aa8(0x12d)](PID_FILE);return;}const _0xa8a061=await verifyPidIsOllama(_0x203579);!_0xa8a061&&(console[_0x501aa8(0x166)](_0x501aa8(0x11d)+_0x10ec87(0x17c)+_0x501aa8(0x17b)+_0x203579+('\x20is\x20no\x20lon'+_0x10ec87(0x133)+_0x501aa8(0x163)+'ng')),_0x539748[_0x501aa8(0x12d)](PID_FILE),persistManagedModel(null));}catch{try{_0x539748['unlinkSync'](PID_FILE);}catch{}}}export async function isDaemonRunning(){const _0x501f6f=_0x51d2f6;try{const _0x11e916=await fetch(OLLAMA_API_BASE+_0x501f6f(0x11e),{'signal':AbortSignal['timeout'](0xd6+0x2c5*0x3+0xb*-0x1f)});return _0x11e916['ok'];}catch{return![];}}function _0x3142(_0x21c29d,_0x5c7936){_0x21c29d=_0x21c29d-(0x26e8+0x21d*-0xb+-0xe8c);const _0x1ce4f2=_0x2ab1();let _0x252853=_0x1ce4f2[_0x21c29d];if(_0x3142['aVKQWO']===undefined){var _0x3dbc8a=function(_0x1273a3){const _0x51beea='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2990f1='',_0x244d21='',_0x392201=_0x2990f1+_0x3dbc8a;for(let _0x413949=-0x7*-0x2f+-0x1380+0x1237,_0x94e229,_0x54373b,_0x15c7e3=0x15a6+-0x2*-0xceb+-0x2f7c;_0x54373b=_0x1273a3['charAt'](_0x15c7e3++);~_0x54373b&&(_0x94e229=_0x413949%(0x4*0x79+-0x36e*0xb+0x23da)?_0x94e229*(0xc82+0x39a+-0x244*0x7)+_0x54373b:_0x54373b,_0x413949++%(0x1c29+0x1*-0x25c7+-0x9a2*-0x1))?_0x2990f1+=_0x392201['charCodeAt'](_0x15c7e3+(-0x1*-0x6af+0x3*-0x243+0x24))-(0x8*0x8e+0x194c+-0xb5*0x2a)!==0x1498+-0xa3*-0x1+-0x153b?String['fromCharCode'](-0x19f4+0x137*0xb+0x4a*0x2f&_0x94e229>>(-(0x581*-0x2+0x2044+-0x1540)*_0x413949&0x527*0x3+-0x2*-0x27d+0xd1*-0x19)):_0x413949:-0x2a8*-0xa+-0x1*0x966+-0x112a){_0x54373b=_0x51beea['indexOf'](_0x54373b);}for(let _0xa42938=0xdda+-0xcaf+-0x12b,_0x4bfb88=_0x2990f1['length'];_0xa42938<_0x4bfb88;_0xa42938++){_0x244d21+='%'+('00'+_0x2990f1['charCodeAt'](_0xa42938)['toString'](0x1b7*0x9+0xc9f+-0xdff*0x2))['slice'](-(0x14cf+0x251*-0xd+0x254*0x4));}return decodeURIComponent(_0x244d21);};_0x3142['IXfbPH']=_0x3dbc8a,_0x3142['pYdgbD']={},_0x3142['aVKQWO']=!![];}const _0x455999=_0x1ce4f2[0x1*0x1a7+0x1ca9+-0x1e50],_0x2c7f15=_0x21c29d+_0x455999,_0x57699f=_0x3142['pYdgbD'][_0x2c7f15];if(!_0x57699f){const _0x51f7e4=function(_0x10bc22){this['VjCyDO']=_0x10bc22,this['YXhVwu']=[0x14a+0x1*-0x3bc+0x273,-0x276+0x1e97*0x1+-0x1c21,-0x2b*0xd+0x2280+-0x2051],this['vRHDOo']=function(){return'newState';},this['WoEPcV']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['oHIbuO']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x51f7e4['prototype']['uFepQi']=function(){const _0x3a36f0=new RegExp(this['WoEPcV']+this['oHIbuO']),_0x1e2889=_0x3a36f0['test'](this['vRHDOo']['toString']())?--this['YXhVwu'][-0xac4+0x1a73*-0x1+0x2538]:--this['YXhVwu'][-0x458+0x2a5*-0x1+-0x6fd*-0x1];return this['CYJCKd'](_0x1e2889);},_0x51f7e4['prototype']['CYJCKd']=function(_0x342c51){if(!Boolean(~_0x342c51))return _0x342c51;return this['YpWcKp'](this['VjCyDO']);},_0x51f7e4['prototype']['YpWcKp']=function(_0x173fef){for(let _0x4c1d27=-0x2*-0x63a+-0x1cbd+-0x17b*-0xb,_0x1cb82a=this['YXhVwu']['length'];_0x4c1d27<_0x1cb82a;_0x4c1d27++){this['YXhVwu']['push'](Math['round'](Math['random']())),_0x1cb82a=this['YXhVwu']['length'];}return _0x173fef(this['YXhVwu'][-0x99f*-0x3+0xeab*0x1+-0x2b88]);},new _0x51f7e4(_0x3142)['uFepQi'](),_0x252853=_0x3142['IXfbPH'](_0x252853),_0x3142['pYdgbD'][_0x2c7f15]=_0x252853;}else _0x252853=_0x57699f;return _0x252853;}async function findOllamaBinary(){const _0x13d6dd=_0x46c4d8,_0x148ca4=_0x46c4d8,_0x1cde70=[_0x13d6dd(0x132)+_0x13d6dd(0x17e)+'lama',_0x148ca4(0x12f)+_0x148ca4(0x125)+'a','/usr/bin/o'+_0x13d6dd(0x140)];for(const _0x428679 of _0x1cde70){if(_0x539748[_0x148ca4(0x12c)](_0x428679))return _0x428679;}try{const {stdout:_0x3f1849}=await execFileAsync('which',[_0x148ca4(0x15d)],{'timeout':0xbb8});return _0x3f1849[_0x148ca4(0x131)]()||null;}catch{return null;}}async function waitForDaemon(_0x40c6a5=DAEMON_READY_TIMEOUT_MS){const _0x3836ce=_0x51d2f6,_0x2be331=_0x51d2f6,_0x490318=Date[_0x3836ce(0x13f)]();while(Date[_0x2be331(0x13f)]()-_0x490318<_0x40c6a5){if(await isDaemonRunning())return!![];await new Promise(_0x1ec0cf=>setTimeout(_0x1ec0cf,-0x8af+0x1*0x1f8f+-0x14ec));}return![];}async function preloadModel(_0x34b1c8){const _0x2625fe=_0x51d2f6,_0x42fc43=_0x46c4d8;try{await fetch(OLLAMA_API_BASE+('/api/gener'+_0x2625fe(0x146)),{'method':_0x2625fe(0x179),'headers':{'Content-Type':_0x42fc43(0x120)+'n/json'},'body':JSON[_0x42fc43(0x14a)]({'model':_0x34b1c8,'prompt':'','keep_alive':KEEP_ALIVE}),'signal':AbortSignal[_0x2625fe(0x11f)](PRELOAD_TIMEOUT_MS)});}catch(_0x13cda1){const _0x491622=_0x13cda1 instanceof Error?_0x13cda1['message']:String(_0x13cda1);console[_0x42fc43(0x15a)]('[ollama]\x20p'+'reload\x20war'+_0x42fc43(0x178)+'l='+_0x34b1c8+'):\x20'+_0x491622);}}async function unloadModel(_0xaef972){const _0x1dacec=_0x46c4d8,_0x42b54a=_0x51d2f6;try{await fetch(OLLAMA_API_BASE+(_0x1dacec(0x12e)+'ate'),{'method':'POST','headers':{'Content-Type':_0x42b54a(0x120)+_0x1dacec(0x135)},'body':JSON[_0x1dacec(0x14a)]({'model':_0xaef972,'keep_alive':0x0}),'signal':AbortSignal[_0x42b54a(0x11f)](0x17e9+-0x2641+-0x21e0*-0x1)});}catch{}}export async function ensureRunning(_0x2f1179){const _0xcc7e73=_0x51d2f6,_0x54954c=_0x46c4d8;await reconcileStalePidFile();if(await isDaemonRunning())return await preloadModel(_0x2f1179),managedModel=_0x2f1179,_0x539748[_0xcc7e73(0x12c)](PID_FILE)&&persistManagedModel(_0x2f1179),!![];const _0x3e80f3=await findOllamaBinary();if(!_0x3e80f3)return console[_0x54954c(0x14b)](_0xcc7e73(0x165)+'inary\x20not\x20'+_0x54954c(0x160)+_0xcc7e73(0x13d)+_0xcc7e73(0x129)+_0x54954c(0x139)+'ll\x20ollama)'),![];console[_0xcc7e73(0x166)](_0xcc7e73(0x11d)+_0xcc7e73(0x122)+_0xcc7e73(0x155)+_0x3e80f3+'\x20serve');const _0x518629=spawn(_0x3e80f3,[_0xcc7e73(0x123)],{'detached':!![],'stdio':_0xcc7e73(0x152),'env':process[_0x54954c(0x127)]});_0x518629[_0x54954c(0x149)]();if(!_0x518629[_0xcc7e73(0x13e)])return console[_0x54954c(0x14b)](_0xcc7e73(0x11d)+_0xcc7e73(0x148)+_0x54954c(0x124)),![];try{_0x539748['mkdirSync'](dirname(PID_FILE),{'recursive':!![]}),_0x539748[_0xcc7e73(0x162)+'ync'](PID_FILE,String(_0x518629[_0x54954c(0x13e)]),'utf-8'),persistManagedModel(_0x2f1179);}catch(_0x52ff72){console[_0x54954c(0x15a)]('[ollama]\x20f'+_0x54954c(0x13c)+_0xcc7e73(0x150)+_0x54954c(0x14d)+_0x52ff72);}managedProcess=_0x518629,managedModel=_0x2f1179;const _0x3d7697=await waitForDaemon();if(!_0x3d7697){console[_0xcc7e73(0x14b)](_0xcc7e73(0x141)+_0x54954c(0x174)+_0xcc7e73(0x171)+'\x20ready\x20wit'+_0xcc7e73(0x130));try{process[_0x54954c(0x145)](_0x518629['pid'],'SIGTERM');}catch{}try{_0x539748[_0xcc7e73(0x12d)](PID_FILE);}catch{}return persistManagedModel(null),![];}return console[_0xcc7e73(0x166)]('[ollama]\x20d'+_0x54954c(0x151)+_0x54954c(0x175)+_0xcc7e73(0x126)+':\x20'+_0x2f1179),await preloadModel(_0x2f1179),!![];}export async function ensureStopped(){const _0x46f16d=_0x51d2f6,_0x11947d=_0x46c4d8;if(!_0x539748[_0x46f16d(0x12c)](PID_FILE))return;let _0x31b413=null;try{const _0x320ea9=_0x539748[_0x46f16d(0x15e)+'nc'](PID_FILE,_0x11947d(0x128))[_0x46f16d(0x131)](),_0x2e2005=parseInt(_0x320ea9,0x5b*0x3b+0x16fe+0xd*-0x361);if(!isNaN(_0x2e2005)&&_0x2e2005>0x1574+0x5*-0x3ff+0x1d*-0xd)_0x31b413=_0x2e2005;}catch{}const _0x52aaae=_0x31b413?await verifyPidIsOllama(_0x31b413):![];if(!_0x52aaae){console[_0x11947d(0x166)](_0x11947d(0x147)+_0x46f16d(0x16b)+_0x11947d(0x157)+'d='+_0x31b413+(_0x11947d(0x15f)+'no\x20longer\x20'+'ollama\x20—\x20c'+_0x11947d(0x136)));try{_0x539748[_0x46f16d(0x12d)](PID_FILE);}catch{}persistManagedModel(null),managedProcess=null,managedModel=null;return;}const _0x58bd34=managedModel||loadManagedModelFromDisk();_0x58bd34&&await unloadModel(_0x58bd34);try{process[_0x11947d(0x145)](_0x31b413,_0x11947d(0x134)),console[_0x11947d(0x166)](_0x11947d(0x11d)+'topped\x20dae'+_0x46f16d(0x12a)+_0x31b413);}catch(_0x4eff52){const _0x314293=_0x4eff52 instanceof Error?_0x4eff52[_0x11947d(0x142)]:String(_0x4eff52);console['warn'](_0x46f16d(0x177)+_0x11947d(0x16a)+_0x11947d(0x153)+_0x31b413+':\x20'+_0x314293);}try{_0x539748[_0x11947d(0x12d)](PID_FILE);}catch{}persistManagedModel(null),managedProcess=null,managedModel=null;}export function isBotManaged(){const _0x52b060=_0x51d2f6;return _0x539748[_0x52b060(0x12c)](PID_FILE);}export function getManagedModel(){return managedModel||loadManagedModelFromDisk();}void((async()=>{const _0x467a37=_0x46c4d8,_0x2a17a0=_0x51d2f6;try{await reconcileStalePidFile();if(_0x539748[_0x467a37(0x12c)](PID_FILE)){const _0x2b21a8=loadManagedModelFromDisk();_0x2b21a8&&(managedModel=_0x2b21a8,console[_0x467a37(0x166)]('[ollama]\x20r'+'estored\x20ma'+'naged\x20stat'+_0x467a37(0x13a)+_0x467a37(0x156)+_0x2a17a0(0x138)+_0x2b21a8+')'));}}catch(_0x16c0da){console[_0x467a37(0x15a)]('[ollama]\x20s'+_0x467a37(0x16c)+_0x467a37(0x14c)+'n\x20failed:\x20'+_0x16c0da);}})());
|