alvin-bot 5.6.2 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +1 -1
- 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 -130
- 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 -443
- 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 -0
- 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 -1831
- 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 _0x49b55b=_0xb91f,_0x5d2f5a=_0xb91f;(function(_0x1a9252,_0x15f86b){const _0x2a5b3e=_0xb91f,_0x57619b=_0xb91f,_0x2196ae=_0x1a9252();while(!![]){try{const _0x334adb=parseInt(_0x2a5b3e(0x198))/(-0x2330+-0xa2f+-0x58*-0x84)+parseInt(_0x57619b(0x151))/(-0x87b*-0x1+0x1*0x2581+-0x16fd*0x2)*(parseInt(_0x2a5b3e(0x15d))/(0x4b0*-0x2+0x1a0e+-0x10ab))+parseInt(_0x2a5b3e(0x16e))/(0x731+-0x4c*0xf+-0x2b9*0x1)+-parseInt(_0x2a5b3e(0x188))/(-0x8a3+0x1f79+0x1*-0x16d1)*(-parseInt(_0x2a5b3e(0x17f))/(0x2075+-0x1dae+0x8d*-0x5))+-parseInt(_0x57619b(0x17b))/(0x1c0a+-0x851*0x2+-0xb61)*(-parseInt(_0x57619b(0x179))/(-0x70*0x31+-0xa82+-0x1ffa*-0x1))+parseInt(_0x57619b(0x14f))/(-0x1d*0x4b+0x1*0x1d68+0x2*-0xa70)+-parseInt(_0x2a5b3e(0x191))/(0x26af+-0x1e09+-0x4*0x227)*(parseInt(_0x57619b(0x195))/(-0x1*-0x37b+-0x9d*-0x19+-0x12c5));if(_0x334adb===_0x15f86b)break;else _0x2196ae['push'](_0x2196ae['shift']());}catch(_0x1cfbc8){_0x2196ae['push'](_0x2196ae['shift']());}}}(_0x5e9c,0x12fca+0x17e722+0x2*-0x53283));const _0x6c385=(function(){let _0x231008=!![];return function(_0x29e6d3,_0x25dbb5){const _0x51b09b=_0x231008?function(){if(_0x25dbb5){const _0x4eda7d=_0x25dbb5['apply'](_0x29e6d3,arguments);return _0x25dbb5=null,_0x4eda7d;}}:function(){};return _0x231008=![],_0x51b09b;};}()),_0x59bd9c=_0x6c385(this,function(){const _0x5c6114=_0xb91f,_0x195c10=_0xb91f;return _0x59bd9c[_0x5c6114(0x164)]()[_0x195c10(0x16a)](_0x5c6114(0x186)+'+$')[_0x195c10(0x164)]()[_0x5c6114(0x1a4)+'r'](_0x59bd9c)['search'](_0x5c6114(0x186)+'+$');});_0x59bd9c();import{spawn,execFile}from'child_process';import{promisify}from'util';import _0x2e39b5 from'fs';import{resolve,dirname}from'path';import _0x20a69b from'os';const execFileAsync=promisify(execFile),DATA_DIR=process[_0x49b55b(0x19e)][_0x5d2f5a(0x187)+_0x49b55b(0x158)]||resolve(_0x20a69b[_0x5d2f5a(0x190)](),'.alvin-bot'),PID_FILE=resolve(DATA_DIR,_0x5d2f5a(0x166)),MODEL_FILE=resolve(DATA_DIR,_0x5d2f5a(0x162)+'el'),OLLAMA_API_BASE=_0x5d2f5a(0x173)+_0x5d2f5a(0x18f)+'34',DAEMON_READY_TIMEOUT_MS=0x5e94+0x681b+-0x8c17,PRELOAD_TIMEOUT_MS=0x8baf+-0x13858+0x19709,KEEP_ALIVE=_0x49b55b(0x168);let managedProcess=null,managedModel=null;function _0x5e9c(){const _0xbcb9d4=['DgfSzsbWAwqGzG','y29UC3rYDwn0BW','D2fYBG','ywvTB24GzgLKia','zxHPC3rZu3LUyW','igLZig5VigXVBG','AgLUide1CW','Aw5JBhvKzxm','BgvHBMLUzYb1Ca','C2vYDMu','BIbMywLSzwq6ia','l2fWAs90ywDZ','yxbWBgLJyxrPBW','mti4ota4odLkAenytfm','CMvHzezPBgvtEq','mtuYt1HlrKPJ','y29TBwfUzd0','w29SBgfTyv0GCa','w29SBgfTyv0GCG','CgLK','z2vYig9SBgfTyq','ywLSzwqGDg8GAW','x0rjuG','DgfYDhvWihjLyW','BwvZC2fNzq','l2fWAs9Nzw5LCG','BM93','ndC1mJLJzurNy3C','zsbMCM9TihbYzq','CML0zsbZDgf0zq','yNjLDYbPBNn0yq','BwTKAxjtEw5J','B2XSyw1HlM1Vza','DgLTzw91Da','Dg9tDhjPBMC','BMLUzYaOBw9Kzq','B2XSyw1HlNbPza','BI9QC29U','mZbT','ktOG','C2vHCMnO','DhjPBq','BM8GBg9Uz2vYia','yxrL','nti3ndq2menlu2TKvq','ywLSzwqGDg8GCa','zw1VBJOG','AwqGzMLSzsbWBW','A2LSBa','Ahr0CdOVl2XVyW','igzPBgvZoIa','kg1VzgvSpq','EsdIGjqGChjLBg9H','C3rYAw5NAwz5','w29SBgfTyv0GzG','ntK5ndGWoevtBgHgua','Aw5HCNKGBM90ia','mtrbwMf3Dei','w29SBgfTyv0GyG','zgLUzYbTB2rLBa','DxrMltG','nLHhyuPAtW','ywLSzwqGDg8GDW','BweGzMLYC3qGka','w29SBgfTyv0Gza','BMfNzwqGC3rHDa','Bw9UihbPzd0','u0Lhvevstq','kcGOlISPkYKRkq','quXwsu5Frefuqq','nJaYnZuZmfbxsvrNCG','ywvTB24GCMvHza','w29SBgfTyv0GCW','ihjLywr5ihDPDa','Ew5J','CMv3l2jPBI9VBa','B2XSyw1H','ywXOB3n0oJeXna','Ag9TzwrPCG','nZaXntq4mZbRuuzXswK','zwWGzMLSztOG','ihnLCNzL','DgfYDgLUzYbKyq','mtflD1DkAKS','l2jPBI9VBgXHBq','Dw5Yzwy','mtmXotmXn3n6s2HezW','zxjYB3i','DMLVDxmGCNvUia','D3jPDgvgAwXLuW','Bg9N','l3vZCI9SB2nHBa','zw52','l3vZCI9IAw4VBW','AwXLicHWAwq9','AwDUB3jL','Dw5SAw5Ru3LUyW'];_0x5e9c=function(){return _0xbcb9d4;};return _0x5e9c();}async function verifyPidIsOllama(_0x3cb166){const _0x49e494=_0x49b55b,_0x1570d3=_0x49b55b;try{const {stdout:_0x26a710}=await execFileAsync('ps',['-p',String(_0x3cb166),'-o',_0x49e494(0x152)],{'timeout':0xbb8});return _0x26a710['toLowerCas'+'e']()[_0x49e494(0x149)](_0x1570d3(0x18e));}catch{return![];}}function loadManagedModelFromDisk(){const _0x4c1273=_0x5d2f5a,_0x69cc42=_0x5d2f5a;try{if(_0x2e39b5[_0x4c1273(0x1a7)](MODEL_FILE))return _0x2e39b5[_0x69cc42(0x150)+'nc'](MODEL_FILE,'utf-8')[_0x4c1273(0x16b)]()||null;}catch{}return null;}function persistManagedModel(_0x586c7c){const _0x3377e7=_0x49b55b,_0x25a2bc=_0x49b55b;try{_0x2e39b5[_0x3377e7(0x161)](dirname(MODEL_FILE),{'recursive':!![]});if(_0x586c7c)_0x2e39b5[_0x3377e7(0x19b)+_0x3377e7(0x18c)](MODEL_FILE,_0x586c7c,'utf-8');else _0x2e39b5[_0x3377e7(0x1a7)](MODEL_FILE)&&_0x2e39b5[_0x25a2bc(0x1a2)](MODEL_FILE);}catch(_0x1d8a45){console['warn'](_0x3377e7(0x178)+_0x25a2bc(0x16f)+'ersist\x20mod'+_0x25a2bc(0x192)+_0x1d8a45);}}function _0xb91f(_0x53d05e,_0xb47279){_0x53d05e=_0x53d05e-(0x196a*0x1+0xf48+-0x276b*0x1);const _0x207280=_0x5e9c();let _0x5a162e=_0x207280[_0x53d05e];if(_0xb91f['KTrkML']===undefined){var _0x3b8ff4=function(_0x49feb6){const _0x2ff3f2='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x5e1c92='',_0x1b7e34='',_0x28b1c8=_0x5e1c92+_0x3b8ff4;for(let _0x246b07=-0x4f1+-0x1ffa*0x1+0x24eb,_0x321096,_0x37a923,_0x52797c=0x1f87+0x22b4+-0x423b;_0x37a923=_0x49feb6['charAt'](_0x52797c++);~_0x37a923&&(_0x321096=_0x246b07%(0xba4+-0x1a08+0xe68)?_0x321096*(0x1e*0x59+-0x14e6+0x2*0x55c)+_0x37a923:_0x37a923,_0x246b07++%(0xbc8+-0x1d46+0x1182))?_0x5e1c92+=_0x28b1c8['charCodeAt'](_0x52797c+(-0x3*0x876+-0xe40+-0x13d6*-0x2))-(-0xb7*-0xf+0x1e46+-0x28f5)!==0x143+0x1*-0x1950+0x180d?String['fromCharCode'](-0x93*-0x7+0x29*-0xe+0x28*-0x5&_0x321096>>(-(-0x1*-0x1b05+-0x160+-0x19a3*0x1)*_0x246b07&-0x133+-0xc52+0xd8b)):_0x246b07:-0x2*-0xdee+-0x3*-0x883+-0x3565){_0x37a923=_0x2ff3f2['indexOf'](_0x37a923);}for(let _0x159769=0x26e4+-0x3*-0x241+0xd*-0x383,_0xe42f4a=_0x5e1c92['length'];_0x159769<_0xe42f4a;_0x159769++){_0x1b7e34+='%'+('00'+_0x5e1c92['charCodeAt'](_0x159769)['toString'](0x167c+0x3ac*0x7+-0xc08*0x4))['slice'](-(0x16*0x2+0x203c+-0x2066));}return decodeURIComponent(_0x1b7e34);};_0xb91f['QOcvYw']=_0x3b8ff4,_0xb91f['nZBhZx']={},_0xb91f['KTrkML']=!![];}const _0xf9656=_0x207280[0x2362+-0x34+-0x232e],_0x4176eb=_0x53d05e+_0xf9656,_0x5dd2ae=_0xb91f['nZBhZx'][_0x4176eb];if(!_0x5dd2ae){const _0x3a7561=function(_0x4c9cc1){this['rDYRog']=_0x4c9cc1,this['CfvOHE']=[0x355*-0x2+0x67*-0x1d+0x1256*0x1,0x43*0x6f+-0x205a+-0x34d*-0x1,0x2278+-0xee1*0x1+-0x1397],this['womNjT']=function(){return'newState';},this['GcQEUi']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['xtBPkP']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x3a7561['prototype']['rGBkYs']=function(){const _0x414474=new RegExp(this['GcQEUi']+this['xtBPkP']),_0xe79990=_0x414474['test'](this['womNjT']['toString']())?--this['CfvOHE'][-0x106+-0x833*-0x2+0xf5f*-0x1]:--this['CfvOHE'][-0xf7a*-0x1+0x878+-0x17f2];return this['nmlhdh'](_0xe79990);},_0x3a7561['prototype']['nmlhdh']=function(_0x34ae3b){if(!Boolean(~_0x34ae3b))return _0x34ae3b;return this['yUIYwq'](this['rDYRog']);},_0x3a7561['prototype']['yUIYwq']=function(_0x5aee88){for(let _0x43f69d=0x5c6+-0x1dd5+0x3*0x805,_0x518e35=this['CfvOHE']['length'];_0x43f69d<_0x518e35;_0x43f69d++){this['CfvOHE']['push'](Math['round'](Math['random']())),_0x518e35=this['CfvOHE']['length'];}return _0x5aee88(this['CfvOHE'][0x1bc5+0xa1*-0x25+0xc*-0x60]);},new _0x3a7561(_0xb91f)['rGBkYs'](),_0x5a162e=_0xb91f['QOcvYw'](_0x5a162e),_0xb91f['nZBhZx'][_0x4176eb]=_0x5a162e;}else _0x5a162e=_0x5dd2ae;return _0x5a162e;}async function reconcileStalePidFile(){const _0xf76b2c=_0x49b55b,_0x1d5e8e=_0x5d2f5a;if(!_0x2e39b5[_0xf76b2c(0x1a7)](PID_FILE))return;try{const _0x2c8e9a=_0x2e39b5['readFileSy'+'nc'](PID_FILE,_0xf76b2c(0x17e))[_0x1d5e8e(0x16b)](),_0x5f27a7=parseInt(_0x2c8e9a,0x1e*0x59+-0x14e6+0x1*0xa82);if(isNaN(_0x5f27a7)||_0x5f27a7<=0xbc8+-0x1d46+0x117e){_0x2e39b5[_0x1d5e8e(0x1a2)](PID_FILE);return;}const _0x37f078=await verifyPidIsOllama(_0x5f27a7);!_0x37f078&&(console[_0x1d5e8e(0x19c)](_0x1d5e8e(0x18a)+_0x1d5e8e(0x1a3)+_0x1d5e8e(0x1a0)+_0x5f27a7+(_0x1d5e8e(0x147)+_0x1d5e8e(0x156)+')\x20—\x20removi'+'ng')),_0x2e39b5[_0xf76b2c(0x1a2)](PID_FILE),persistManagedModel(null));}catch{try{_0x2e39b5['unlinkSync'](PID_FILE);}catch{}}}export async function isDaemonRunning(){const _0x347128=_0x49b55b,_0x2b8799=_0x5d2f5a;try{const _0x14496d=await fetch(OLLAMA_API_BASE+_0x347128(0x14d),{'signal':AbortSignal[_0x2b8799(0x163)](-0x3*0x876+-0xe40+-0x17b9*-0x2)});return _0x14496d['ok'];}catch{return![];}}async function findOllamaBinary(){const _0x4803d1=_0x5d2f5a,_0x3e94ae=_0x5d2f5a,_0x34d9bd=['/opt/homeb'+_0x4803d1(0x18d)+'lama',_0x3e94ae(0x19d)+_0x3e94ae(0x196)+'a',_0x3e94ae(0x19f)+'llama'];for(const _0x22acb5 of _0x34d9bd){if(_0x2e39b5[_0x3e94ae(0x1a7)](_0x22acb5))return _0x22acb5;}try{const {stdout:_0x37407c}=await execFileAsync('which',[_0x4803d1(0x18e)],{'timeout':0xbb8});return _0x37407c[_0x4803d1(0x16b)]()||null;}catch{return null;}}async function waitForDaemon(_0x3ac6a7=DAEMON_READY_TIMEOUT_MS){const _0x25778b=_0x5d2f5a,_0x4c37fc=_0x5d2f5a,_0x4b21e5=Date[_0x25778b(0x15c)]();while(Date[_0x25778b(0x15c)]()-_0x4b21e5<_0x3ac6a7){if(await isDaemonRunning())return!![];await new Promise(_0x4f0ecd=>setTimeout(_0x4f0ecd,-0xb7*-0xf+0x1e46+-0x270b));}return![];}async function preloadModel(_0x231626){const _0x4b6993=_0x49b55b,_0x3843cc=_0x5d2f5a;try{await fetch(OLLAMA_API_BASE+('/api/gener'+_0x4b6993(0x16d)),{'method':'POST','headers':{'Content-Type':'applicatio'+'n/json'},'body':JSON['stringify']({'model':_0x231626,'prompt':'','keep_alive':KEEP_ALIVE}),'signal':AbortSignal[_0x3843cc(0x163)](PRELOAD_TIMEOUT_MS)});}catch(_0x51a098){const _0x56879d=_0x51a098 instanceof Error?_0x51a098[_0x3843cc(0x15a)]:String(_0x51a098);console[_0x3843cc(0x1a5)](_0x4b6993(0x153)+'reload\x20war'+_0x4b6993(0x165)+'l='+_0x231626+_0x3843cc(0x169)+_0x56879d);}}async function unloadModel(_0x4461e8){const _0x29d311=_0x5d2f5a,_0x327290=_0x5d2f5a;try{await fetch(OLLAMA_API_BASE+(_0x29d311(0x15b)+_0x327290(0x16d)),{'method':'POST','headers':{'Content-Type':_0x327290(0x14e)+_0x327290(0x167)},'body':JSON[_0x29d311(0x177)]({'model':_0x4461e8,'keep_alive':0x0}),'signal':AbortSignal[_0x327290(0x163)](0x143+0x1*-0x1950+0x2b95)});}catch{}}export async function ensureRunning(_0x2f0a33){const _0x21cc49=_0x49b55b,_0x136afb=_0x49b55b;await reconcileStalePidFile();if(await isDaemonRunning())return await preloadModel(_0x2f0a33),managedModel=_0x2f0a33,_0x2e39b5[_0x21cc49(0x1a7)](PID_FILE)&&persistManagedModel(_0x2f0a33),!![];const _0x2fe66a=await findOllamaBinary();if(!_0x2fe66a)return console['error'](_0x136afb(0x17c)+_0x21cc49(0x17a)+'found\x20—\x20in'+'stall\x20olla'+_0x136afb(0x181)+_0x136afb(0x160)+'ll\x20ollama)'),![];console[_0x136afb(0x19c)](_0x136afb(0x18a)+_0x136afb(0x194)+_0x136afb(0x170)+_0x2fe66a+_0x21cc49(0x193));const _0x570839=spawn(_0x2fe66a,[_0x21cc49(0x14b)],{'detached':!![],'stdio':_0x136afb(0x1a1),'env':process[_0x136afb(0x19e)]});_0x570839[_0x136afb(0x197)]();if(!_0x570839[_0x21cc49(0x155)])return console[_0x136afb(0x199)](_0x136afb(0x18a)+'pawn\x20faile'+'d\x20—\x20no\x20pid'),![];try{_0x2e39b5[_0x136afb(0x161)](dirname(PID_FILE),{'recursive':!![]}),_0x2e39b5[_0x21cc49(0x19b)+'ync'](PID_FILE,String(_0x570839[_0x136afb(0x155)]),'utf-8'),persistManagedModel(_0x2f0a33);}catch(_0x53f716){console[_0x21cc49(0x1a5)](_0x136afb(0x178)+_0x136afb(0x180)+_0x21cc49(0x15f)+_0x136afb(0x174)+_0x53f716);}managedProcess=_0x570839,managedModel=_0x2f0a33;const _0x4d155e=await waitForDaemon();if(!_0x4d155e){console[_0x136afb(0x199)](_0x136afb(0x182)+_0x21cc49(0x1a6)+'not\x20become'+_0x21cc49(0x18b)+_0x21cc49(0x148));try{process[_0x21cc49(0x172)](_0x570839[_0x136afb(0x155)],_0x136afb(0x185));}catch{}try{_0x2e39b5[_0x21cc49(0x1a2)](PID_FILE);}catch{}return persistManagedModel(null),![];}return console[_0x136afb(0x19c)]('[ollama]\x20d'+_0x136afb(0x189)+_0x21cc49(0x176)+_0x21cc49(0x17d)+':\x20'+_0x2f0a33),await preloadModel(_0x2f0a33),!![];}export async function ensureStopped(){const _0x24d8e3=_0x49b55b,_0x603fa8=_0x5d2f5a;if(!_0x2e39b5[_0x24d8e3(0x1a7)](PID_FILE))return;let _0x19079c=null;try{const _0x2f70e3=_0x2e39b5[_0x603fa8(0x150)+'nc'](PID_FILE,_0x603fa8(0x17e))[_0x24d8e3(0x16b)](),_0x2601ab=parseInt(_0x2f70e3,-0x93*-0x7+0x29*-0xe+0x59*-0x5);if(!isNaN(_0x2601ab)&&_0x2601ab>-0x1*-0x1b05+-0x160+-0x65*0x41)_0x19079c=_0x2601ab;}catch{}const _0x11d354=_0x19079c?await verifyPidIsOllama(_0x19079c):![];if(!_0x11d354){console[_0x24d8e3(0x19c)](_0x24d8e3(0x153)+_0x603fa8(0x171)+'ints\x20to\x20pi'+'d='+_0x19079c+('\x20which\x20is\x20'+_0x24d8e3(0x16c)+'ollama\x20—\x20c'+_0x603fa8(0x14a)));try{_0x2e39b5[_0x603fa8(0x1a2)](PID_FILE);}catch{}persistManagedModel(null),managedProcess=null,managedModel=null;return;}const _0x5ae033=managedModel||loadManagedModelFromDisk();_0x5ae033&&await unloadModel(_0x5ae033);try{process[_0x603fa8(0x172)](_0x19079c,'SIGTERM'),console[_0x24d8e3(0x19c)](_0x24d8e3(0x18a)+'topped\x20dae'+_0x603fa8(0x184)+_0x19079c);}catch(_0x2c08ee){const _0x12e081=_0x2c08ee instanceof Error?_0x2c08ee['message']:String(_0x2c08ee);console[_0x603fa8(0x1a5)](_0x603fa8(0x178)+_0x603fa8(0x157)+'ill\x20pid='+_0x19079c+':\x20'+_0x12e081);}try{_0x2e39b5[_0x24d8e3(0x1a2)](PID_FILE);}catch{}persistManagedModel(null),managedProcess=null,managedModel=null;}export function isBotManaged(){const _0x3a6510=_0x49b55b;return _0x2e39b5[_0x3a6510(0x1a7)](PID_FILE);}export function getManagedModel(){return managedModel||loadManagedModelFromDisk();}void((async()=>{const _0x37baf8=_0x5d2f5a,_0x4a4e50=_0x49b55b;try{await reconcileStalePidFile();if(_0x2e39b5[_0x37baf8(0x1a7)](PID_FILE)){const _0x278e8f=loadManagedModelFromDisk();_0x278e8f&&(managedModel=_0x278e8f,console['log'](_0x37baf8(0x154)+'estored\x20ma'+_0x4a4e50(0x183)+_0x4a4e50(0x15e)+_0x37baf8(0x19a)+_0x4a4e50(0x175)+_0x278e8f+')'));}}catch(_0x21732a){console[_0x4a4e50(0x1a5)](_0x37baf8(0x18a)+_0x37baf8(0x159)+'onciliatio'+_0x4a4e50(0x14c)+_0x21732a);}})());
|