omnius 1.0.152 → 1.0.154

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/dist/index.js CHANGED
@@ -529083,14 +529083,16 @@ async function scanOllamaProcesses(options2 = {}) {
529083
529083
  ]);
529084
529084
  const snapshots = [];
529085
529085
  for (const row of psRows) {
529086
- if (!isOllamaServeCommand(row.command))
529086
+ const serve = isOllamaServeCommand(row.command);
529087
+ const runner = !serve && isOllamaRunnerCommand(row.command);
529088
+ if (!serve && !runner)
529087
529089
  continue;
529088
- const env2 = await readProcEnv(system, row.pid);
529090
+ const env2 = serve ? await readProcEnv(system, row.pid) : { readable: false, values: {} };
529089
529091
  const ports = uniqueNumbers([
529090
529092
  ...portMap.get(row.pid) ?? [],
529091
- ...portsFromEnv(env2.values)
529093
+ ...serve ? portsFromEnv(env2.values) : []
529092
529094
  ]);
529093
- const ollamaApiPorts = await probeOllamaPorts(system, ports);
529095
+ const ollamaApiPorts = serve ? await probeOllamaPorts(system, ports) : [];
529094
529096
  snapshots.push({
529095
529097
  pid: row.pid,
529096
529098
  ppid: row.ppid,
@@ -529103,7 +529105,9 @@ async function scanOllamaProcesses(options2 = {}) {
529103
529105
  envReadable: env2.readable,
529104
529106
  env: env2.values,
529105
529107
  gpuMemoryMb: gpuMemoryByPid.get(row.pid) ?? null,
529106
- isOllamaServe: true
529108
+ isOllamaServe: serve,
529109
+ isOllamaRunner: runner,
529110
+ runnerModelBlob: runner ? parseRunnerModelBlob(row.command) : null
529107
529111
  });
529108
529112
  }
529109
529113
  const decisions = classifyOllamaProcesses(snapshots, {
@@ -529181,11 +529185,11 @@ async function cleanupStaleOllamaProcesses(options2 = {}) {
529181
529185
  continue;
529182
529186
  }
529183
529187
  try {
529184
- system.kill(proc.pid, "SIGTERM");
529188
+ signalOllamaProcess(system, proc, "SIGTERM");
529185
529189
  await system.sleep(options2.graceMs ?? DEFAULT_GRACE_MS);
529186
529190
  let signal = "SIGTERM";
529187
529191
  if (system.isPidAlive(proc.pid)) {
529188
- system.kill(proc.pid, "SIGKILL");
529192
+ signalOllamaProcess(system, proc, "SIGKILL");
529189
529193
  signal = "SIGKILL";
529190
529194
  await system.sleep(250);
529191
529195
  }
@@ -529223,6 +529227,20 @@ function classifyOllamaProcesses(processes, options2 = {}) {
529223
529227
  const hasPortEvidence = proc.ports.length > 0 && proc.ollamaApiPorts.length > 0;
529224
529228
  const lowCpu = proc.cpuPct === null || proc.cpuPct <= cpuThresholdPct;
529225
529229
  const oldEnough = proc.elapsedMs === null || proc.elapsedMs >= minAgeMs;
529230
+ if (proc.isOllamaRunner) {
529231
+ const liveServePids = new Set(processes.filter((p2) => p2.isOllamaServe).map((p2) => p2.pid));
529232
+ const parentIsLiveServe = proc.ppid !== null && liveServePids.has(proc.ppid);
529233
+ if (parentIsLiveServe) {
529234
+ return decision(proc, "keep", "active-pool-owned", true, false, "deterministic", [`runner managed by live ollama serve pid=${proc.ppid}`]);
529235
+ }
529236
+ reasons.push(`orphan ollama runner (ppid=${proc.ppid ?? "?"} is not a live ollama serve)`);
529237
+ if (proc.runnerModelBlob)
529238
+ reasons.push(`holds model blob ${basename18(proc.runnerModelBlob)}`);
529239
+ if (proc.gpuMemoryMb !== null && proc.gpuMemoryMb > 0) {
529240
+ reasons.push(`holding ${proc.gpuMemoryMb} MB GPU memory`);
529241
+ }
529242
+ return decision(proc, "terminate", "orphan-runner", false, true, "deterministic", reasons);
529243
+ }
529226
529244
  if (!proc.isOllamaServe) {
529227
529245
  return decision(proc, "keep", "unknown-ollama", false, false, "deterministic", ["not an ollama serve process"]);
529228
529246
  }
@@ -529264,6 +529282,23 @@ function decision(proc, action, classification, protectedProc, eligible, source,
529264
529282
  reasons
529265
529283
  };
529266
529284
  }
529285
+ function signalOllamaProcess(system, proc, signal) {
529286
+ let delivered = false;
529287
+ if (proc.isOllamaServe) {
529288
+ try {
529289
+ system.kill(-proc.pid, signal);
529290
+ delivered = true;
529291
+ } catch {
529292
+ }
529293
+ }
529294
+ try {
529295
+ system.kill(proc.pid, signal);
529296
+ delivered = true;
529297
+ } catch (err) {
529298
+ if (!delivered)
529299
+ throw err;
529300
+ }
529301
+ }
529267
529302
  async function maybeApplyInference(scan, options2, system) {
529268
529303
  const mode = options2.useInference ?? "auto";
529269
529304
  if (mode === false || process.env["OMNIUS_OLLAMA_CLEANUP_INFERENCE"] === "0") {
@@ -529577,6 +529612,14 @@ function isOllamaServeCommand(command) {
529577
529612
  const [exe, subcommand] = command.trim().split(/\s+/);
529578
529613
  return basename18(exe ?? "") === "ollama" && subcommand === "serve";
529579
529614
  }
529615
+ function isOllamaRunnerCommand(command) {
529616
+ const [exe, subcommand] = command.trim().split(/\s+/);
529617
+ return basename18(exe ?? "") === "ollama" && subcommand === "runner";
529618
+ }
529619
+ function parseRunnerModelBlob(command) {
529620
+ const m2 = command.match(/--model\s+(\S+)/);
529621
+ return m2?.[1] ?? null;
529622
+ }
529580
529623
  function portsFromEnv(env2) {
529581
529624
  const host = env2["OLLAMA_HOST"];
529582
529625
  if (!host)
@@ -530144,15 +530187,31 @@ var init_ollama_pool = __esm({
530144
530187
  const child = spawn23(config.ollamaBinary, ["serve"], {
530145
530188
  env: env2,
530146
530189
  stdio: ["ignore", "pipe", "pipe"],
530147
- detached: false
530190
+ detached: true
530148
530191
  });
530192
+ child.unref();
530149
530193
  const proc = {
530150
530194
  pid: child.pid ?? -1,
530151
530195
  kill: () => {
530152
- try {
530153
- child.kill("SIGTERM");
530154
- } catch {
530155
- }
530196
+ const pid = child.pid;
530197
+ if (!pid || pid <= 0)
530198
+ return;
530199
+ const tryKill = (target, sig) => {
530200
+ try {
530201
+ process.kill(target, sig);
530202
+ return true;
530203
+ } catch {
530204
+ return false;
530205
+ }
530206
+ };
530207
+ const groupKilled = tryKill(-pid, "SIGTERM");
530208
+ if (!groupKilled)
530209
+ tryKill(pid, "SIGTERM");
530210
+ setTimeout(() => {
530211
+ if (tryKill(-pid, "SIGKILL"))
530212
+ return;
530213
+ tryKill(pid, "SIGKILL");
530214
+ }, 8e3).unref();
530156
530215
  }
530157
530216
  };
530158
530217
  child.stdout?.on("data", () => {
@@ -530242,6 +530301,65 @@ var init_ollama_pool = __esm({
530242
530301
  }, null));
530243
530302
  this.startReaper();
530244
530303
  this.scheduleStartupCleanupScan();
530304
+ this.installProcessExitHooks();
530305
+ }
530306
+ /**
530307
+ * Process-exit hooks that kill every spawned `ollama serve` subprocess
530308
+ * group on Omnius shutdown. Catches all the ways the previous orphan
530309
+ * runners were created:
530310
+ *
530311
+ * - SIGTERM / SIGINT (systemd stop, Ctrl-C, kill PID)
530312
+ * - process.exit() from a fatal error path
530313
+ * - 'beforeExit' (graceful natural shutdown)
530314
+ *
530315
+ * The 'exit' listener is intentionally synchronous-only because Node has
530316
+ * already started tearing down by then — it just fires `process.kill` on
530317
+ * the recorded PGIDs and returns. Async work is impossible at that stage.
530318
+ *
530319
+ * Signal handlers also forward the signal after teardown so any other
530320
+ * shutdown plumbing (web framework graceful close, etc.) still observes
530321
+ * the original signal — we don't swallow it.
530322
+ *
530323
+ * Idempotent across multiple pool instances in the same process: every
530324
+ * pool only kills its OWN pids.
530325
+ */
530326
+ installProcessExitHooks() {
530327
+ if (process.env["NODE_ENV"] === "test" || process.env["VITEST"])
530328
+ return;
530329
+ if (process.env["OMNIUS_OLLAMA_PROCESS_EXIT_HOOK"] === "0")
530330
+ return;
530331
+ const killAllPoolOwnedSync = (sig) => {
530332
+ for (const inst of this.instances) {
530333
+ if (!inst.state.poolOwned)
530334
+ continue;
530335
+ const pid = inst.state.pid;
530336
+ if (!pid || pid <= 0)
530337
+ continue;
530338
+ try {
530339
+ process.kill(-pid, sig);
530340
+ } catch {
530341
+ try {
530342
+ process.kill(pid, sig);
530343
+ } catch {
530344
+ }
530345
+ }
530346
+ }
530347
+ };
530348
+ const onSignal = (sig) => {
530349
+ killAllPoolOwnedSync(sig);
530350
+ process.removeListener("SIGTERM", onSigterm);
530351
+ process.removeListener("SIGINT", onSigint);
530352
+ try {
530353
+ process.kill(process.pid, sig);
530354
+ } catch {
530355
+ }
530356
+ };
530357
+ const onSigterm = () => onSignal("SIGTERM");
530358
+ const onSigint = () => onSignal("SIGINT");
530359
+ process.on("SIGTERM", onSigterm);
530360
+ process.on("SIGINT", onSigint);
530361
+ process.on("exit", () => killAllPoolOwnedSync("SIGTERM"));
530362
+ process.on("beforeExit", () => killAllPoolOwnedSync("SIGTERM"));
530245
530363
  }
530246
530364
  /**
530247
530365
  * Resolve the effective agent id for an acquire request. Explicit option
@@ -530834,7 +530952,7 @@ var init_ollama_pool = __esm({
530834
530952
  return;
530835
530953
  const handle2 = setTimeout(async () => {
530836
530954
  try {
530837
- const cleanupOnStart = process.env["OMNIUS_OLLAMA_CLEANUP_ON_START"] === "1";
530955
+ const cleanupOnStart = process.env["OMNIUS_OLLAMA_CLEANUP_ON_START"] !== "0";
530838
530956
  const { cleanupStaleOllamaProcesses: cleanupStaleOllamaProcesses2 } = await Promise.resolve().then(() => (init_ollama_pool_cleanup(), ollama_pool_cleanup_exports));
530839
530957
  const report2 = await cleanupStaleOllamaProcesses2({
530840
530958
  dryRun: !cleanupOnStart,
@@ -530847,6 +530965,9 @@ var init_ollama_pool = __esm({
530847
530965
  if (stale.length > 0) {
530848
530966
  this.emit(cleanupOnStart ? "stale-process-cleanup" : "stale-processes-found", {
530849
530967
  staleCount: stale.length,
530968
+ // Surface orphan-runner counts separately so observability can
530969
+ // alert on the VRAM-hostage failure mode specifically.
530970
+ orphanRunnerCount: report2.decisions.filter((d2) => d2.classification === "orphan-runner" && d2.action === "terminate").length,
530850
530971
  dryRun: report2.dryRun,
530851
530972
  terminated: report2.terminated,
530852
530973
  skipped: report2.skipped,
@@ -582430,6 +582551,15 @@ ${CONTENT_BG_SEQ}`);
582430
582551
  return;
582431
582552
  }
582432
582553
  if (key?.name === "return" || key?.name === "enter") {
582554
+ if (key?.shift) {
582555
+ const currentLine2 = rl.line ?? "";
582556
+ const cursor = typeof rl.cursor === "number" ? rl.cursor : currentLine2.length;
582557
+ rl.line = currentLine2.slice(0, cursor) + "\n" + currentLine2.slice(cursor);
582558
+ rl.cursor = cursor + 1;
582559
+ self2.updateFooterHeight();
582560
+ self2.renderFooterAndPositionInput();
582561
+ return;
582562
+ }
582433
582563
  const currentLine = rl.line ?? "";
582434
582564
  rl.line = "";
582435
582565
  rl.cursor = 0;
@@ -633985,6 +634115,12 @@ var init_direct_input = __esm({
633985
634115
  }
633986
634116
  break;
633987
634117
  }
634118
+ if (remaining.length >= 2 && (remaining[1] === "\r" || remaining[1] === "\n")) {
634119
+ this._insertText("\n");
634120
+ i2 += 2;
634121
+ if (remaining[1] === "\r" && remaining[2] === "\n") i2++;
634122
+ continue;
634123
+ }
633988
634124
  if (remaining.length === 1) {
633989
634125
  break;
633990
634126
  }
@@ -633992,6 +634128,21 @@ var init_direct_input = __esm({
633992
634128
  continue;
633993
634129
  }
633994
634130
  if (code8 < 32 || code8 === 127) {
634131
+ if (code8 === 13) {
634132
+ if (this._preSubmit?.()) {
634133
+ i2++;
634134
+ continue;
634135
+ }
634136
+ this._submit();
634137
+ i2++;
634138
+ if (this._buffer[i2] === "\n") i2++;
634139
+ continue;
634140
+ }
634141
+ if (code8 === 10) {
634142
+ this._insertText("\n");
634143
+ i2++;
634144
+ continue;
634145
+ }
633995
634146
  this._handleControl(code8);
633996
634147
  i2++;
633997
634148
  continue;
@@ -634194,12 +634345,6 @@ var init_direct_input = __esm({
634194
634345
  /** Handle control characters (ASCII < 32 and DEL) */
634195
634346
  _handleControl(code8) {
634196
634347
  switch (code8) {
634197
- case 13:
634198
- // Enter (CR)
634199
- case 10:
634200
- if (this._preSubmit?.()) return;
634201
- this._submit();
634202
- return;
634203
634348
  case 127:
634204
634349
  // Backspace (DEL)
634205
634350
  case 8:
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.152",
3
+ "version": "1.0.154",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.152",
9
+ "version": "1.0.154",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.152",
3
+ "version": "1.0.154",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",