codemaxxing 0.4.9 → 0.4.11

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
@@ -1465,8 +1465,8 @@ function App() {
1465
1465
  if (key.return) {
1466
1466
  // Auto-install Ollama if not present
1467
1467
  if (!isOllamaInstalled()) {
1468
- setWizardPullProgress({ status: "Installing Ollama...", percent: 0 });
1469
- setWizardScreen("pulling");
1468
+ setLoading(true);
1469
+ setSpinnerMsg("Installing Ollama... this may take a minute");
1470
1470
  // Run install async so the UI can update
1471
1471
  const installCmd = getOllamaInstallCommand(wizardHardware?.os ?? "linux");
1472
1472
  (async () => {
@@ -1481,17 +1481,17 @@ function App() {
1481
1481
  });
1482
1482
  });
1483
1483
  addMsg("info", "✅ Ollama installed! Proceeding to model download...");
1484
+ setLoading(false);
1484
1485
  // Small delay for PATH to update on Windows
1485
1486
  await new Promise(r => setTimeout(r, 2000));
1486
1487
  // Go back to models screen so user can pick and it'll proceed to pull
1487
1488
  setWizardScreen("models");
1488
- setWizardPullProgress(null);
1489
1489
  }
1490
1490
  catch (e) {
1491
1491
  addMsg("error", `Install failed: ${e.message}`);
1492
1492
  addMsg("info", `Try manually in a separate terminal: ${installCmd}`);
1493
+ setLoading(false);
1493
1494
  setWizardScreen("install-ollama");
1494
- setWizardPullProgress(null);
1495
1495
  }
1496
1496
  })();
1497
1497
  return;
@@ -1,4 +1,4 @@
1
- /** Check if ollama binary exists on PATH or known install locations */
1
+ /** Check if ollama binary exists on PATH, known install locations, or server is running */
2
2
  export declare function isOllamaInstalled(): boolean;
3
3
  /** Check if ollama server is responding */
4
4
  export declare function isOllamaRunning(): Promise<boolean>;
@@ -13,9 +13,9 @@ export interface PullProgress {
13
13
  percent: number;
14
14
  }
15
15
  /**
16
- * Pull a model from Ollama registry.
16
+ * Pull a model from Ollama registry via HTTP API.
17
+ * Falls back to CLI if API fails.
17
18
  * Calls onProgress with download updates.
18
- * Returns a promise that resolves when complete.
19
19
  */
20
20
  export declare function pullModel(modelId: string, onProgress?: (progress: PullProgress) => void): Promise<void>;
21
21
  export interface OllamaModelInfo {
@@ -11,20 +11,42 @@ function getWindowsOllamaPaths() {
11
11
  paths.push(join(localAppData, "Ollama", "ollama.exe"));
12
12
  return paths;
13
13
  }
14
- /** Check if ollama binary exists on PATH or known install locations */
14
+ /** Check if ollama binary exists on PATH, known install locations, or server is running */
15
15
  export function isOllamaInstalled() {
16
+ // Check PATH first
16
17
  try {
17
18
  const cmd = process.platform === "win32" ? "where ollama" : "which ollama";
18
19
  execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
19
20
  return true;
20
21
  }
21
- catch {
22
- // Fallback: check known install paths on Windows
23
- if (process.platform === "win32") {
24
- return getWindowsOllamaPaths().some(p => existsSync(p));
22
+ catch { }
23
+ // Check known install paths on Windows
24
+ if (process.platform === "win32") {
25
+ if (getWindowsOllamaPaths().some(p => existsSync(p)))
26
+ return true;
27
+ }
28
+ // Check if the server is responding (if server is running, Ollama is definitely installed)
29
+ try {
30
+ execSync("curl -s http://localhost:11434/api/tags", {
31
+ stdio: ["pipe", "pipe", "pipe"],
32
+ timeout: 3000,
33
+ });
34
+ return true;
35
+ }
36
+ catch { }
37
+ // Check if ollama process is running (Windows)
38
+ if (process.platform === "win32") {
39
+ try {
40
+ const result = execSync("tasklist /fi \"imagename eq ollama app.exe\" /fo csv /nh", {
41
+ stdio: ["pipe", "pipe", "pipe"],
42
+ timeout: 3000,
43
+ });
44
+ if (result.toString().toLowerCase().includes("ollama"))
45
+ return true;
25
46
  }
26
- return false;
47
+ catch { }
27
48
  }
49
+ return false;
28
50
  }
29
51
  /** Check if ollama server is responding */
30
52
  export async function isOllamaRunning() {
@@ -47,22 +69,98 @@ export function getOllamaInstallCommand(os) {
47
69
  case "windows": return "winget install Ollama.Ollama";
48
70
  }
49
71
  }
72
+ /** Find the ollama binary path */
73
+ function findOllamaBinary() {
74
+ // Try PATH first
75
+ try {
76
+ const cmd = process.platform === "win32" ? "where ollama" : "which ollama";
77
+ return execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 3000 }).toString().trim().split("\n")[0];
78
+ }
79
+ catch { }
80
+ // Check known Windows paths
81
+ if (process.platform === "win32") {
82
+ for (const p of getWindowsOllamaPaths()) {
83
+ if (existsSync(p))
84
+ return p;
85
+ }
86
+ }
87
+ return "ollama"; // fallback, hope for the best
88
+ }
50
89
  /** Start ollama serve in background */
51
90
  export function startOllama() {
52
- const child = spawn("ollama", ["serve"], {
91
+ const bin = findOllamaBinary();
92
+ const child = spawn(bin, ["serve"], {
53
93
  detached: true,
54
94
  stdio: "ignore",
55
95
  });
56
96
  child.unref();
57
97
  }
58
98
  /**
59
- * Pull a model from Ollama registry.
99
+ * Pull a model from Ollama registry via HTTP API.
100
+ * Falls back to CLI if API fails.
60
101
  * Calls onProgress with download updates.
61
- * Returns a promise that resolves when complete.
62
102
  */
63
103
  export function pullModel(modelId, onProgress) {
104
+ // Try HTTP API first (works even when CLI isn't on PATH)
105
+ return pullModelViaAPI(modelId, onProgress).catch(() => {
106
+ // Fallback to CLI
107
+ return pullModelViaCLI(modelId, onProgress);
108
+ });
109
+ }
110
+ function pullModelViaAPI(modelId, onProgress) {
111
+ return new Promise(async (resolve, reject) => {
112
+ try {
113
+ const res = await fetch("http://localhost:11434/api/pull", {
114
+ method: "POST",
115
+ headers: { "Content-Type": "application/json" },
116
+ body: JSON.stringify({ name: modelId, stream: true }),
117
+ });
118
+ if (!res.ok || !res.body) {
119
+ reject(new Error(`Ollama API returned ${res.status}`));
120
+ return;
121
+ }
122
+ const reader = res.body.getReader();
123
+ const decoder = new TextDecoder();
124
+ let buffer = "";
125
+ while (true) {
126
+ const { done, value } = await reader.read();
127
+ if (done)
128
+ break;
129
+ buffer += decoder.decode(value, { stream: true });
130
+ const lines = buffer.split("\n");
131
+ buffer = lines.pop() || "";
132
+ for (const line of lines) {
133
+ if (!line.trim())
134
+ continue;
135
+ try {
136
+ const data = JSON.parse(line);
137
+ if (data.status === "success") {
138
+ onProgress?.({ status: "success", percent: 100 });
139
+ resolve();
140
+ return;
141
+ }
142
+ if (data.total && data.completed) {
143
+ const percent = Math.round((data.completed / data.total) * 100);
144
+ onProgress?.({ status: "downloading", total: data.total, completed: data.completed, percent });
145
+ }
146
+ else {
147
+ onProgress?.({ status: data.status || "working...", percent: 0 });
148
+ }
149
+ }
150
+ catch { }
151
+ }
152
+ }
153
+ resolve();
154
+ }
155
+ catch (err) {
156
+ reject(err);
157
+ }
158
+ });
159
+ }
160
+ function pullModelViaCLI(modelId, onProgress) {
64
161
  return new Promise((resolve, reject) => {
65
- const child = spawn("ollama", ["pull", modelId], {
162
+ const bin = findOllamaBinary();
163
+ const child = spawn(bin, ["pull", modelId], {
66
164
  stdio: ["pipe", "pipe", "pipe"],
67
165
  });
68
166
  let lastOutput = "";
@@ -150,7 +248,9 @@ export async function stopOllama() {
150
248
  try {
151
249
  // First unload all models from memory
152
250
  try {
153
- execSync("ollama stop", { stdio: ["pipe", "pipe", "pipe"], timeout: 5000 });
251
+ const bin = findOllamaBinary();
252
+ const { spawnSync } = require("child_process");
253
+ spawnSync(bin, ["stop"], { stdio: "pipe", timeout: 5000 });
154
254
  }
155
255
  catch { /* may fail if no models loaded */ }
156
256
  // Kill the server process
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemaxxing",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "Open-source terminal coding agent. Connect any LLM. Max your code.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.tsx CHANGED
@@ -1489,8 +1489,8 @@ function App() {
1489
1489
  if (key.return) {
1490
1490
  // Auto-install Ollama if not present
1491
1491
  if (!isOllamaInstalled()) {
1492
- setWizardPullProgress({ status: "Installing Ollama...", percent: 0 });
1493
- setWizardScreen("pulling");
1492
+ setLoading(true);
1493
+ setSpinnerMsg("Installing Ollama... this may take a minute");
1494
1494
 
1495
1495
  // Run install async so the UI can update
1496
1496
  const installCmd = getOllamaInstallCommand(wizardHardware?.os ?? "linux");
@@ -1504,16 +1504,16 @@ function App() {
1504
1504
  });
1505
1505
  });
1506
1506
  addMsg("info", "✅ Ollama installed! Proceeding to model download...");
1507
+ setLoading(false);
1507
1508
  // Small delay for PATH to update on Windows
1508
1509
  await new Promise(r => setTimeout(r, 2000));
1509
1510
  // Go back to models screen so user can pick and it'll proceed to pull
1510
1511
  setWizardScreen("models");
1511
- setWizardPullProgress(null);
1512
1512
  } catch (e: any) {
1513
1513
  addMsg("error", `Install failed: ${e.message}`);
1514
1514
  addMsg("info", `Try manually in a separate terminal: ${installCmd}`);
1515
+ setLoading(false);
1515
1516
  setWizardScreen("install-ollama");
1516
- setWizardPullProgress(null);
1517
1517
  }
1518
1518
  })();
1519
1519
  return;
@@ -13,19 +13,41 @@ function getWindowsOllamaPaths(): string[] {
13
13
  return paths;
14
14
  }
15
15
 
16
- /** Check if ollama binary exists on PATH or known install locations */
16
+ /** Check if ollama binary exists on PATH, known install locations, or server is running */
17
17
  export function isOllamaInstalled(): boolean {
18
+ // Check PATH first
18
19
  try {
19
20
  const cmd = process.platform === "win32" ? "where ollama" : "which ollama";
20
21
  execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
21
22
  return true;
22
- } catch {
23
- // Fallback: check known install paths on Windows
24
- if (process.platform === "win32") {
25
- return getWindowsOllamaPaths().some(p => existsSync(p));
26
- }
27
- return false;
23
+ } catch {}
24
+
25
+ // Check known install paths on Windows
26
+ if (process.platform === "win32") {
27
+ if (getWindowsOllamaPaths().some(p => existsSync(p))) return true;
28
28
  }
29
+
30
+ // Check if the server is responding (if server is running, Ollama is definitely installed)
31
+ try {
32
+ execSync("curl -s http://localhost:11434/api/tags", {
33
+ stdio: ["pipe", "pipe", "pipe"],
34
+ timeout: 3000,
35
+ });
36
+ return true;
37
+ } catch {}
38
+
39
+ // Check if ollama process is running (Windows)
40
+ if (process.platform === "win32") {
41
+ try {
42
+ const result = execSync("tasklist /fi \"imagename eq ollama app.exe\" /fo csv /nh", {
43
+ stdio: ["pipe", "pipe", "pipe"],
44
+ timeout: 3000,
45
+ });
46
+ if (result.toString().toLowerCase().includes("ollama")) return true;
47
+ } catch {}
48
+ }
49
+
50
+ return false;
29
51
  }
30
52
 
31
53
  /** Check if ollama server is responding */
@@ -50,9 +72,26 @@ export function getOllamaInstallCommand(os: "macos" | "linux" | "windows"): stri
50
72
  }
51
73
  }
52
74
 
75
+ /** Find the ollama binary path */
76
+ function findOllamaBinary(): string {
77
+ // Try PATH first
78
+ try {
79
+ const cmd = process.platform === "win32" ? "where ollama" : "which ollama";
80
+ return execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 3000 }).toString().trim().split("\n")[0];
81
+ } catch {}
82
+ // Check known Windows paths
83
+ if (process.platform === "win32") {
84
+ for (const p of getWindowsOllamaPaths()) {
85
+ if (existsSync(p)) return p;
86
+ }
87
+ }
88
+ return "ollama"; // fallback, hope for the best
89
+ }
90
+
53
91
  /** Start ollama serve in background */
54
92
  export function startOllama(): void {
55
- const child = spawn("ollama", ["serve"], {
93
+ const bin = findOllamaBinary();
94
+ const child = spawn(bin, ["serve"], {
56
95
  detached: true,
57
96
  stdio: "ignore",
58
97
  });
@@ -67,16 +106,77 @@ export interface PullProgress {
67
106
  }
68
107
 
69
108
  /**
70
- * Pull a model from Ollama registry.
109
+ * Pull a model from Ollama registry via HTTP API.
110
+ * Falls back to CLI if API fails.
71
111
  * Calls onProgress with download updates.
72
- * Returns a promise that resolves when complete.
73
112
  */
74
113
  export function pullModel(
75
114
  modelId: string,
76
115
  onProgress?: (progress: PullProgress) => void
116
+ ): Promise<void> {
117
+ // Try HTTP API first (works even when CLI isn't on PATH)
118
+ return pullModelViaAPI(modelId, onProgress).catch(() => {
119
+ // Fallback to CLI
120
+ return pullModelViaCLI(modelId, onProgress);
121
+ });
122
+ }
123
+
124
+ function pullModelViaAPI(
125
+ modelId: string,
126
+ onProgress?: (progress: PullProgress) => void
127
+ ): Promise<void> {
128
+ return new Promise(async (resolve, reject) => {
129
+ try {
130
+ const res = await fetch("http://localhost:11434/api/pull", {
131
+ method: "POST",
132
+ headers: { "Content-Type": "application/json" },
133
+ body: JSON.stringify({ name: modelId, stream: true }),
134
+ });
135
+ if (!res.ok || !res.body) {
136
+ reject(new Error(`Ollama API returned ${res.status}`));
137
+ return;
138
+ }
139
+ const reader = res.body.getReader();
140
+ const decoder = new TextDecoder();
141
+ let buffer = "";
142
+ while (true) {
143
+ const { done, value } = await reader.read();
144
+ if (done) break;
145
+ buffer += decoder.decode(value, { stream: true });
146
+ const lines = buffer.split("\n");
147
+ buffer = lines.pop() || "";
148
+ for (const line of lines) {
149
+ if (!line.trim()) continue;
150
+ try {
151
+ const data = JSON.parse(line);
152
+ if (data.status === "success") {
153
+ onProgress?.({ status: "success", percent: 100 });
154
+ resolve();
155
+ return;
156
+ }
157
+ if (data.total && data.completed) {
158
+ const percent = Math.round((data.completed / data.total) * 100);
159
+ onProgress?.({ status: "downloading", total: data.total, completed: data.completed, percent });
160
+ } else {
161
+ onProgress?.({ status: data.status || "working...", percent: 0 });
162
+ }
163
+ } catch {}
164
+ }
165
+ }
166
+ resolve();
167
+ } catch (err: any) {
168
+ reject(err);
169
+ }
170
+ });
171
+ }
172
+
173
+ function pullModelViaCLI(
174
+ modelId: string,
175
+ onProgress?: (progress: PullProgress) => void
77
176
  ): Promise<void> {
78
177
  return new Promise((resolve, reject) => {
79
- const child = spawn("ollama", ["pull", modelId], {
178
+ const bin = findOllamaBinary();
179
+ const child = spawn(bin, ["pull", modelId], {
80
180
  stdio: ["pipe", "pipe", "pipe"],
81
181
  });
82
182
 
@@ -176,7 +276,9 @@ export async function stopOllama(): Promise<{ ok: boolean; message: string }> {
176
276
  try {
177
277
  // First unload all models from memory
178
278
  try {
179
- execSync("ollama stop", { stdio: ["pipe", "pipe", "pipe"], timeout: 5000 });
279
+ const bin = findOllamaBinary();
280
+ const { spawnSync } = require("child_process");
281
+ spawnSync(bin, ["stop"], { stdio: "pipe", timeout: 5000 });
180
282
  } catch { /* may fail if no models loaded */ }
181
283
 
182
284
  // Kill the server process