codemaxxing 0.4.14 → 0.4.15
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/README.md +5 -0
- package/dist/index.js +35 -3
- package/dist/utils/models.d.ts +1 -1
- package/dist/utils/models.js +6 -4
- package/dist/utils/ollama.js +2 -1
- package/package.json +1 -1
- package/src/index.tsx +36 -3
- package/src/utils/models.ts +7 -6
- package/src/utils/ollama.ts +2 -1
package/README.md
CHANGED
|
@@ -46,6 +46,11 @@ curl -fsSL -o $env:TEMP\install-codemaxxing.bat https://raw.githubusercontent.co
|
|
|
46
46
|
npm update -g codemaxxing
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
If that doesn't get the latest version:
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g codemaxxing@latest
|
|
52
|
+
```
|
|
53
|
+
|
|
49
54
|
## Quick Start
|
|
50
55
|
|
|
51
56
|
### 1. Start Your LLM
|
package/dist/index.js
CHANGED
|
@@ -175,6 +175,35 @@ function App() {
|
|
|
175
175
|
pasteEvents.on("paste", handler);
|
|
176
176
|
return () => { pasteEvents.off("paste", handler); };
|
|
177
177
|
}, []);
|
|
178
|
+
// Refresh the connection banner to reflect current provider status
|
|
179
|
+
const refreshConnectionBanner = useCallback(async () => {
|
|
180
|
+
const info = [];
|
|
181
|
+
const cliArgs = parseCLIArgs();
|
|
182
|
+
const rawConfig = loadConfig();
|
|
183
|
+
const config = applyOverrides(rawConfig, cliArgs);
|
|
184
|
+
const provider = config.provider;
|
|
185
|
+
if (provider.model === "auto" || (provider.baseUrl === "http://localhost:1234/v1" && !cliArgs.baseUrl)) {
|
|
186
|
+
const detected = await detectLocalProvider();
|
|
187
|
+
if (detected) {
|
|
188
|
+
info.push(`✔ Connected to ${detected.baseUrl} → ${detected.model}`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const ollamaUp = await isOllamaRunning();
|
|
192
|
+
info.push(ollamaUp ? "Ollama running (no model loaded)" : "✗ No local LLM server found");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
info.push(`Provider: ${provider.baseUrl}`);
|
|
197
|
+
info.push(`Model: ${provider.model}`);
|
|
198
|
+
}
|
|
199
|
+
const cwd = process.cwd();
|
|
200
|
+
if (isGitRepo(cwd)) {
|
|
201
|
+
const branch = getBranch(cwd);
|
|
202
|
+
const status = getStatus(cwd);
|
|
203
|
+
info.push(`Git: ${branch} (${status})`);
|
|
204
|
+
}
|
|
205
|
+
setConnectionInfo(info);
|
|
206
|
+
}, []);
|
|
178
207
|
// Connect/reconnect to LLM provider
|
|
179
208
|
const connectToProvider = useCallback(async (isRetry = false) => {
|
|
180
209
|
const cliArgs = parseCLIArgs();
|
|
@@ -664,6 +693,7 @@ function App() {
|
|
|
664
693
|
await new Promise(r => setTimeout(r, 1000));
|
|
665
694
|
if (await isOllamaRunning()) {
|
|
666
695
|
addMsg("info", "Ollama is running.");
|
|
696
|
+
await refreshConnectionBanner();
|
|
667
697
|
return;
|
|
668
698
|
}
|
|
669
699
|
}
|
|
@@ -679,6 +709,8 @@ function App() {
|
|
|
679
709
|
addMsg("info", "Stopping Ollama...");
|
|
680
710
|
const result = await stopOllama();
|
|
681
711
|
addMsg(result.ok ? "info" : "error", result.ok ? `\u2705 ${result.message}` : `\u274C ${result.message}`);
|
|
712
|
+
if (result.ok)
|
|
713
|
+
await refreshConnectionBanner();
|
|
682
714
|
return;
|
|
683
715
|
}
|
|
684
716
|
if (trimmed === "/ollama pull") {
|
|
@@ -1037,7 +1069,7 @@ function App() {
|
|
|
1037
1069
|
}
|
|
1038
1070
|
setLoading(false);
|
|
1039
1071
|
setStreaming(false);
|
|
1040
|
-
}, [agent, exit]);
|
|
1072
|
+
}, [agent, exit, refreshConnectionBanner]);
|
|
1041
1073
|
useInput((inputChar, key) => {
|
|
1042
1074
|
// Handle slash command navigation
|
|
1043
1075
|
if (showSuggestionsRef.current) {
|
|
@@ -1367,7 +1399,7 @@ function App() {
|
|
|
1367
1399
|
const pullModels = [
|
|
1368
1400
|
{ id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
|
|
1369
1401
|
{ id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
|
|
1370
|
-
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "
|
|
1402
|
+
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
|
|
1371
1403
|
{ id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
|
|
1372
1404
|
{ id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
|
|
1373
1405
|
{ id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
|
|
@@ -1931,7 +1963,7 @@ function App() {
|
|
|
1931
1963
|
})(), themePicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Choose a theme:" }), listThemes().map((key, i) => (_jsxs(Text, { children: [i === themePickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === themePickerIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: key }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", THEMES[key].description] }), key === theme.name.toLowerCase() ? _jsx(Text, { color: theme.colors.muted, children: " (current)" }) : null] }, key))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), sessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.secondary, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Resume a session:" }), sessionPicker.map((s, i) => (_jsxs(Text, { children: [i === sessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === sessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), deleteSessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.error, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.error, children: "Delete a session:" }), deleteSessionPicker.map((s, i) => (_jsxs(Text, { children: [i === deleteSessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === deleteSessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), deleteSessionConfirm && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.warning, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: theme.colors.warning, children: ["Delete session ", deleteSessionConfirm.id, "?"] }), _jsxs(Text, { color: theme.colors.muted, children: [" ", deleteSessionConfirm.display] }), _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.error, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: theme.colors.success, bold: true, children: "[n]" }), _jsx(Text, { children: "o" })] })] })), ollamaDeletePicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Delete which model?" }), _jsx(Text, { children: "" }), ollamaDeletePicker.models.map((m, i) => (_jsxs(Text, { children: [" ", i === ollamaDeletePickerIndex ? _jsx(Text, { color: theme.colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === ollamaDeletePickerIndex ? theme.colors.primary : undefined, children: m.name }), _jsxs(Text, { color: theme.colors.muted, children: [" (", (m.size / (1024 * 1024 * 1024)).toFixed(1), " GB)"] })] }, m.name))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter to delete · Esc cancel" })] })), ollamaPullPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Download which model?" }), _jsx(Text, { children: "" }), [
|
|
1932
1964
|
{ id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
|
|
1933
1965
|
{ id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
|
|
1934
|
-
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "
|
|
1966
|
+
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
|
|
1935
1967
|
{ id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
|
|
1936
1968
|
{ id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
|
|
1937
1969
|
{ id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
|
package/dist/utils/models.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface RecommendedModel {
|
|
|
7
7
|
vramOptimal: number;
|
|
8
8
|
description: string;
|
|
9
9
|
speed: string;
|
|
10
|
-
quality: "good" | "great" | "best";
|
|
10
|
+
quality: "limited" | "good" | "great" | "best";
|
|
11
11
|
}
|
|
12
12
|
export type ModelFit = "perfect" | "good" | "tight" | "skip";
|
|
13
13
|
export interface ScoredModel extends RecommendedModel {
|
package/dist/utils/models.js
CHANGED
|
@@ -6,9 +6,9 @@ const MODELS = [
|
|
|
6
6
|
size: 2,
|
|
7
7
|
ramRequired: 8,
|
|
8
8
|
vramOptimal: 4,
|
|
9
|
-
description: "
|
|
9
|
+
description: "\u26A0\uFE0F May not support tool calling well",
|
|
10
10
|
speed: "~60 tok/s on M1",
|
|
11
|
-
quality: "
|
|
11
|
+
quality: "limited",
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
name: "Qwen 2.5 Coder 7B",
|
|
@@ -84,7 +84,7 @@ function scoreModel(model, ramGB, vramGB) {
|
|
|
84
84
|
return "tight";
|
|
85
85
|
return "skip";
|
|
86
86
|
}
|
|
87
|
-
const qualityOrder = { best: 3, great: 2, good: 1 };
|
|
87
|
+
const qualityOrder = { best: 3, great: 2, good: 1, limited: 0 };
|
|
88
88
|
const fitOrder = { perfect: 4, good: 3, tight: 2, skip: 1 };
|
|
89
89
|
export function getRecommendations(hardware) {
|
|
90
90
|
const ramGB = hardware.ram / (1024 * 1024 * 1024);
|
|
@@ -136,7 +136,9 @@ function mapLlmfitQuality(params_b) {
|
|
|
136
136
|
return "best";
|
|
137
137
|
if (params_b >= 7)
|
|
138
138
|
return "great";
|
|
139
|
-
|
|
139
|
+
if (params_b >= 5)
|
|
140
|
+
return "good";
|
|
141
|
+
return "limited";
|
|
140
142
|
}
|
|
141
143
|
/** Get recommendations using llmfit if available, otherwise fall back to hardcoded list */
|
|
142
144
|
export function getRecommendationsWithLlmfit(hardware) {
|
package/dist/utils/ollama.js
CHANGED
|
@@ -296,7 +296,8 @@ export async function stopOllama() {
|
|
|
296
296
|
/** Delete a model from disk */
|
|
297
297
|
export function deleteModel(modelId) {
|
|
298
298
|
try {
|
|
299
|
-
|
|
299
|
+
const bin = findOllamaBinary();
|
|
300
|
+
execSync(`"${bin}" rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
|
|
300
301
|
return { ok: true, message: `Deleted ${modelId}` };
|
|
301
302
|
}
|
|
302
303
|
catch (err) {
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -215,6 +215,37 @@ function App() {
|
|
|
215
215
|
return () => { pasteEvents.off("paste", handler); };
|
|
216
216
|
}, []);
|
|
217
217
|
|
|
218
|
+
// Refresh the connection banner to reflect current provider status
|
|
219
|
+
const refreshConnectionBanner = useCallback(async () => {
|
|
220
|
+
const info: string[] = [];
|
|
221
|
+
const cliArgs = parseCLIArgs();
|
|
222
|
+
const rawConfig = loadConfig();
|
|
223
|
+
const config = applyOverrides(rawConfig, cliArgs);
|
|
224
|
+
const provider = config.provider;
|
|
225
|
+
|
|
226
|
+
if (provider.model === "auto" || (provider.baseUrl === "http://localhost:1234/v1" && !cliArgs.baseUrl)) {
|
|
227
|
+
const detected = await detectLocalProvider();
|
|
228
|
+
if (detected) {
|
|
229
|
+
info.push(`✔ Connected to ${detected.baseUrl} → ${detected.model}`);
|
|
230
|
+
} else {
|
|
231
|
+
const ollamaUp = await isOllamaRunning();
|
|
232
|
+
info.push(ollamaUp ? "Ollama running (no model loaded)" : "✗ No local LLM server found");
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
info.push(`Provider: ${provider.baseUrl}`);
|
|
236
|
+
info.push(`Model: ${provider.model}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const cwd = process.cwd();
|
|
240
|
+
if (isGitRepo(cwd)) {
|
|
241
|
+
const branch = getBranch(cwd);
|
|
242
|
+
const status = getStatus(cwd);
|
|
243
|
+
info.push(`Git: ${branch} (${status})`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
setConnectionInfo(info);
|
|
247
|
+
}, []);
|
|
248
|
+
|
|
218
249
|
// Connect/reconnect to LLM provider
|
|
219
250
|
const connectToProvider = useCallback(async (isRetry = false) => {
|
|
220
251
|
const cliArgs = parseCLIArgs();
|
|
@@ -708,6 +739,7 @@ function App() {
|
|
|
708
739
|
await new Promise(r => setTimeout(r, 1000));
|
|
709
740
|
if (await isOllamaRunning()) {
|
|
710
741
|
addMsg("info", "Ollama is running.");
|
|
742
|
+
await refreshConnectionBanner();
|
|
711
743
|
return;
|
|
712
744
|
}
|
|
713
745
|
}
|
|
@@ -723,6 +755,7 @@ function App() {
|
|
|
723
755
|
addMsg("info", "Stopping Ollama...");
|
|
724
756
|
const result = await stopOllama();
|
|
725
757
|
addMsg(result.ok ? "info" : "error", result.ok ? `\u2705 ${result.message}` : `\u274C ${result.message}`);
|
|
758
|
+
if (result.ok) await refreshConnectionBanner();
|
|
726
759
|
return;
|
|
727
760
|
}
|
|
728
761
|
if (trimmed === "/ollama pull") {
|
|
@@ -1071,7 +1104,7 @@ function App() {
|
|
|
1071
1104
|
|
|
1072
1105
|
setLoading(false);
|
|
1073
1106
|
setStreaming(false);
|
|
1074
|
-
}, [agent, exit]);
|
|
1107
|
+
}, [agent, exit, refreshConnectionBanner]);
|
|
1075
1108
|
|
|
1076
1109
|
useInput((inputChar, key) => {
|
|
1077
1110
|
// Handle slash command navigation
|
|
@@ -1387,7 +1420,7 @@ function App() {
|
|
|
1387
1420
|
const pullModels = [
|
|
1388
1421
|
{ id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
|
|
1389
1422
|
{ id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
|
|
1390
|
-
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "
|
|
1423
|
+
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
|
|
1391
1424
|
{ id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
|
|
1392
1425
|
{ id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
|
|
1393
1426
|
{ id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
|
|
@@ -2204,7 +2237,7 @@ function App() {
|
|
|
2204
2237
|
{[
|
|
2205
2238
|
{ id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
|
|
2206
2239
|
{ id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
|
|
2207
|
-
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "
|
|
2240
|
+
{ id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
|
|
2208
2241
|
{ id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
|
|
2209
2242
|
{ id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
|
|
2210
2243
|
{ id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
|
package/src/utils/models.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface RecommendedModel {
|
|
|
9
9
|
vramOptimal: number; // Optimal VRAM in GB (0 = CPU fine)
|
|
10
10
|
description: string; // One-liner
|
|
11
11
|
speed: string; // e.g., "~45 tok/s on M1"
|
|
12
|
-
quality: "good" | "great" | "best";
|
|
12
|
+
quality: "limited" | "good" | "great" | "best";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export type ModelFit = "perfect" | "good" | "tight" | "skip";
|
|
@@ -25,9 +25,9 @@ const MODELS: RecommendedModel[] = [
|
|
|
25
25
|
size: 2,
|
|
26
26
|
ramRequired: 8,
|
|
27
27
|
vramOptimal: 4,
|
|
28
|
-
description: "
|
|
28
|
+
description: "\u26A0\uFE0F May not support tool calling well",
|
|
29
29
|
speed: "~60 tok/s on M1",
|
|
30
|
-
quality: "
|
|
30
|
+
quality: "limited",
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
name: "Qwen 2.5 Coder 7B",
|
|
@@ -103,7 +103,7 @@ function scoreModel(model: RecommendedModel, ramGB: number, vramGB: number): Mod
|
|
|
103
103
|
return "skip";
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
const qualityOrder: Record<string, number> = { best: 3, great: 2, good: 1 };
|
|
106
|
+
const qualityOrder: Record<string, number> = { best: 3, great: 2, good: 1, limited: 0 };
|
|
107
107
|
const fitOrder: Record<string, number> = { perfect: 4, good: 3, tight: 2, skip: 1 };
|
|
108
108
|
|
|
109
109
|
export function getRecommendations(hardware: HardwareInfo): ScoredModel[] {
|
|
@@ -168,10 +168,11 @@ function mapLlmfitFit(fit: string): ModelFit {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
function mapLlmfitQuality(params_b: number): "good" | "great" | "best" {
|
|
171
|
+
function mapLlmfitQuality(params_b: number): "limited" | "good" | "great" | "best" {
|
|
172
172
|
if (params_b >= 14) return "best";
|
|
173
173
|
if (params_b >= 7) return "great";
|
|
174
|
-
return "good";
|
|
174
|
+
if (params_b >= 5) return "good";
|
|
175
|
+
return "limited";
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
/** Get recommendations using llmfit if available, otherwise fall back to hardcoded list */
|
package/src/utils/ollama.ts
CHANGED
|
@@ -319,7 +319,8 @@ export async function stopOllama(): Promise<{ ok: boolean; message: string }> {
|
|
|
319
319
|
/** Delete a model from disk */
|
|
320
320
|
export function deleteModel(modelId: string): { ok: boolean; message: string } {
|
|
321
321
|
try {
|
|
322
|
-
|
|
322
|
+
const bin = findOllamaBinary();
|
|
323
|
+
execSync(`"${bin}" rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
|
|
323
324
|
return { ok: true, message: `Deleted ${modelId}` };
|
|
324
325
|
} catch (err: any) {
|
|
325
326
|
return { ok: false, message: `Failed to delete ${modelId}: ${err.stderr?.toString().trim() || err.message}` };
|