@krmxd/onegpt 0.1.8-beta → 0.1.9-beta

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/bin/ogpt.js CHANGED
@@ -99,13 +99,14 @@ async function main() {
99
99
  let sessionId = "";
100
100
 
101
101
  for (let i = 0; i < args.length; i++) {
102
- if (args[i] === "--no-stream" || args[i] === "-s") {
102
+ if (args[i] === "--no-stream") {
103
103
  noStream = true;
104
104
  } else if (args[i] === "--auto") {
105
105
  autoApprove = true;
106
106
  } else if (args[i] === "--continue" || args[i] === "-c") {
107
107
  continueLast = true;
108
- } else if (args[i] === "--session") {
108
+ } else if (args[i] === "--session" || args[i] === "-s") {
109
+ // -s <id> = resume session, matching the Python build's argparse
109
110
  sessionId = args[++i] || "";
110
111
  } else if (args[i] === "--model" || args[i] === "-m") {
111
112
  const name = args[++i];
@@ -132,6 +133,23 @@ Options:
132
133
 
133
134
  if (noStream) cfg.set("ui.stream", false);
134
135
 
136
+ // Local-engine bootstrap: install/start the engine and make sure a model
137
+ // exists BEFORE chatting, so users never see raw ECONNREFUSED errors.
138
+ if ((cfg.get("active_provider", "ollama") || "ollama") === "ollama"
139
+ && args[0] !== "--help") {
140
+ const { ensureLocalEngine } = require("../src/ollama-setup");
141
+ const res = await ensureLocalEngine({
142
+ host: cfg.get("providers.ollama.host"),
143
+ model: cfg.activeModel ? cfg.activeModel() : cfg.get("active_model"),
144
+ });
145
+ if (res.ok && res.model && res.model !== cfg.get("active_model")) {
146
+ cfg.set("active_model", res.model);
147
+ }
148
+ if (!res.ok && res.reason) {
149
+ console.log("\n" + res.reason + "\n");
150
+ }
151
+ }
152
+
135
153
  const cli = new CLI({ config: cfg });
136
154
 
137
155
  if (autoApprove) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krmxd/onegpt",
3
- "version": "0.1.8-beta",
3
+ "version": "0.1.9-beta",
4
4
  "description": "OGPT - AI coding assistant for the terminal with a built-in local engine",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+
3
+ // Local-engine bootstrap (parity with ogpt/utils/ollama_setup.py).
4
+ // Makes `ogpt` work right after install: installs the engine if missing,
5
+ // starts the server daemon, picks a model that fits the RAM, and pulls it -
6
+ // all installer output hidden behind short status lines.
7
+
8
+ const { spawn, spawnSync } = require("child_process");
9
+ const os = require("os");
10
+
11
+ function isInstalled() {
12
+ try {
13
+ return spawnSync("ollama", ["--version"],
14
+ { stdio: "ignore", timeout: 15000 }).status === 0;
15
+ } catch { return false; }
16
+ }
17
+
18
+ function _getJson(host, path, timeoutMs) {
19
+ return new Promise((resolve) => {
20
+ const uri = new URL(path, host);
21
+ const mod = uri.protocol === "https:" ? require("https") : require("http");
22
+ const req = mod.request(uri, { timeout: timeoutMs }, (res) => {
23
+ let data = "";
24
+ res.on("data", (c) => { data += c; });
25
+ res.on("end", () => {
26
+ try { resolve({ ok: res.statusCode === 200, json: JSON.parse(data) }); }
27
+ catch { resolve({ ok: false }); }
28
+ });
29
+ });
30
+ req.on("timeout", () => { req.destroy(); resolve({ ok: false }); });
31
+ req.on("error", () => resolve({ ok: false }));
32
+ req.end();
33
+ });
34
+ }
35
+
36
+ async function serverUp(host, timeoutMs = 2500) {
37
+ const r = await _getJson(host, "/api/tags", timeoutMs);
38
+ return r.ok;
39
+ }
40
+
41
+ async function startServer(host, waitSec = 20) {
42
+ try {
43
+ const child = spawn("ollama", ["serve"],
44
+ { stdio: "ignore", detached: true });
45
+ child.unref();
46
+ } catch { return false; }
47
+ const deadline = Date.now() + waitSec * 1000;
48
+ while (Date.now() < deadline) {
49
+ await sleep(500);
50
+ if (await serverUp(host)) return true;
51
+ }
52
+ return false;
53
+ }
54
+
55
+ function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
56
+
57
+ async function listModels(host) {
58
+ const r = await _getJson(host, "/api/tags", 4000);
59
+ if (!r.ok || !r.json || !Array.isArray(r.json.models)) return [];
60
+ return r.json.models.map((m) => m.name || "").filter(Boolean);
61
+ }
62
+
63
+ async function pullModel(host, model, timeoutSec = 1800) {
64
+ console.log("Installing packages...");
65
+ try {
66
+ spawnSync("ollama", ["pull", model],
67
+ { stdio: "ignore", timeout: timeoutSec * 1000 });
68
+ } catch {}
69
+ const installed = await listModels(host);
70
+ const base = model.split(":")[0];
71
+ return installed.includes(model)
72
+ || installed.some((m) => m.startsWith(base + ":"));
73
+ }
74
+
75
+ function ramGB() {
76
+ return Math.round(os.totalmem() / (1024 * 1024 * 1024));
77
+ }
78
+
79
+ async function pickModel(host, configuredModel) {
80
+ const installed = await listModels(host);
81
+ if (installed.includes(configuredModel)) return configuredModel;
82
+ let fit = null;
83
+ try {
84
+ const { recommendForRam } = require("./catalog");
85
+ fit = recommendForRam(ramGB());
86
+ } catch {}
87
+ if (fit && installed.includes(fit)) return fit;
88
+ if (fit && !installed.length) return fit; // fresh machine: pull best fit
89
+ if (installed.length) return installed[0]; // something already there
90
+ return fit || configuredModel;
91
+ }
92
+
93
+ /**
94
+ * Ensure the local engine is installed, running and has a usable model.
95
+ * Returns {ok, model, reason} - never throws.
96
+ */
97
+ async function ensureLocalEngine({ host, model } = {}) {
98
+ host = host || "http://127.0.0.1:11434";
99
+ model = model || "qwen2.5-coder:1.5b";
100
+ try {
101
+ if (!isInstalled()) {
102
+ console.log("Installing packages...");
103
+ const { installOllama } = require("./bootstrap");
104
+ if (!installOllama()) {
105
+ return { ok: false, reason:
106
+ "Local engine could not be installed automatically. "
107
+ + "Grab it from https://ollama.com/download, or pick another "
108
+ + "provider with /provider." };
109
+ }
110
+ }
111
+ let up = await serverUp(host);
112
+ if (!up) {
113
+ console.log("Starting local engine...");
114
+ up = await startServer(host);
115
+ if (!up) {
116
+ return { ok: false, reason:
117
+ `Local engine did not respond on ${host} within 20s. `
118
+ + "Try 'ollama serve' manually or check 'journalctl -u ollama'." };
119
+ }
120
+ }
121
+ const chosen = await pickModel(host, model);
122
+ const installed = await listModels(host);
123
+ const base = chosen.split(":")[0];
124
+ const have = installed.includes(chosen)
125
+ || installed.some((m) => m.startsWith(base + ":"));
126
+ if (!have) {
127
+ const okPull = await pullModel(host, chosen);
128
+ if (!okPull) {
129
+ return { ok: false, reason:
130
+ `Model ${chosen} could not be downloaded (offline?). `
131
+ + "Check your connection or /model to pick an installed one." };
132
+ }
133
+ }
134
+ return { ok: true, model: chosen };
135
+ } catch (e) {
136
+ return { ok: false, reason: e.message };
137
+ }
138
+ }
139
+
140
+ module.exports = { ensureLocalEngine, serverUp, startServer, listModels,
141
+ pullModel, pickModel, isInstalled };
package/src/ollama.js CHANGED
@@ -348,7 +348,16 @@ class OllamaProvider {
348
348
  });
349
349
  }
350
350
  );
351
- req.on("error", reject);
351
+ req.on("error", (e) => {
352
+ if (e && (e.code === "ECONNREFUSED" || /ECONNREFUSED/.test(e.message || ""))) {
353
+ reject(new Error(
354
+ `Local engine is not running at ${this.host}. `
355
+ + "Start it with 'ollama serve' (OGPT normally does this "
356
+ + "automatically at launch), or switch providers with /provider."));
357
+ } else {
358
+ reject(e);
359
+ }
360
+ });
352
361
  req.on("timeout", () => { req.destroy(); reject(new Error("Timeout")); });
353
362
  if (body) req.write(JSON.stringify(body));
354
363
  req.end();
@@ -394,7 +403,16 @@ class OllamaProvider {
394
403
  req.destroy(new Error(`No data from the local engine for ${Math.round(this.idleTimeout / 1000)}s`));
395
404
  }, this.idleTimeout);
396
405
  };
397
- req.on("error", (e) => { clearIdle(); reject(e); });
406
+ req.on("error", (e) => {
407
+ clearIdle();
408
+ if (e && e.code === "ECONNREFUSED") {
409
+ reject(new Error(
410
+ `Local engine is not running at ${this.host}. `
411
+ + "Start it with 'ollama serve' or switch providers with /provider."));
412
+ } else {
413
+ reject(e);
414
+ }
415
+ });
398
416
  req.on("close", clearIdle);
399
417
  req.write(JSON.stringify(body));
400
418
  req.end();
package/src/web.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // Same endpoints, same single-file UI, zero npm dependencies: served with
3
3
  // the plain http module and wired into the SAME agent singleton the
4
4
  // terminal uses, so dashboard and TUI share one brain and one counter.
5
+ const _PKG_VER = require("../package.json").version;
5
6
  const fs = require("fs");
6
7
  const os = require("os");
7
8
  const path = require("path");
@@ -111,7 +112,7 @@ function createDashboard(cli) {
111
112
  return json(res, 200, {
112
113
  ok: true,
113
114
  app: "OGPT",
114
- version: "0.1.8-beta(devices-all)",
115
+ version: `${_PKG_VER}(devices-all)`,
115
116
  model: name,
116
117
  provider: "Ollama",
117
118
  uptime_s: Math.round((Date.now() - STARTED_AT) / 100) / 10,
@@ -140,7 +141,7 @@ function createDashboard(cli) {
140
141
  } catch {}
141
142
  return json(res, 200, {
142
143
  ok: true,
143
- version: "0.1.8-beta(devices-all)",
144
+ version: `${_PKG_VER}(devices-all)`,
144
145
  model: name,
145
146
  model_id: name,
146
147
  provider: "Ollama",