code-ollama 0.12.0 → 0.13.1
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/assets/{tui-BSnwVbDN.js → tui-CCa_vqh0.js} +110 -26
- package/dist/cli.js +55 -9
- package/package.json +1 -1
|
@@ -113,10 +113,100 @@ var CodeBlock = memo(function CodeBlock({ code, language, role }) {
|
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
115
|
//#endregion
|
|
116
|
+
//#region src/components/Markdown/extensions.ts
|
|
117
|
+
var LATEX_COMMANDS = {
|
|
118
|
+
"\\rightarrow": "→",
|
|
119
|
+
"\\leftarrow": "←",
|
|
120
|
+
"\\Rightarrow": "⇒",
|
|
121
|
+
"\\Leftarrow": "⇐",
|
|
122
|
+
"\\leftrightarrow": "↔",
|
|
123
|
+
"\\Leftrightarrow": "⟺",
|
|
124
|
+
"\\uparrow": "↑",
|
|
125
|
+
"\\downarrow": "↓",
|
|
126
|
+
"\\to": "→",
|
|
127
|
+
"\\gets": "←",
|
|
128
|
+
"\\times": "×",
|
|
129
|
+
"\\div": "÷",
|
|
130
|
+
"\\pm": "±",
|
|
131
|
+
"\\leq": "≤",
|
|
132
|
+
"\\geq": "≥",
|
|
133
|
+
"\\neq": "≠",
|
|
134
|
+
"\\approx": "≈",
|
|
135
|
+
"\\equiv": "≡",
|
|
136
|
+
"\\infty": "∞",
|
|
137
|
+
"\\sum": "∑",
|
|
138
|
+
"\\prod": "∏",
|
|
139
|
+
"\\sqrt": "√",
|
|
140
|
+
"\\partial": "∂",
|
|
141
|
+
"\\nabla": "∇",
|
|
142
|
+
"\\in": "∈",
|
|
143
|
+
"\\notin": "∉",
|
|
144
|
+
"\\subset": "⊂",
|
|
145
|
+
"\\supset": "⊃",
|
|
146
|
+
"\\cup": "∪",
|
|
147
|
+
"\\cap": "∩",
|
|
148
|
+
"\\emptyset": "∅",
|
|
149
|
+
"\\alpha": "α",
|
|
150
|
+
"\\beta": "β",
|
|
151
|
+
"\\gamma": "γ",
|
|
152
|
+
"\\delta": "δ",
|
|
153
|
+
"\\epsilon": "ε",
|
|
154
|
+
"\\theta": "θ",
|
|
155
|
+
"\\lambda": "λ",
|
|
156
|
+
"\\mu": "μ",
|
|
157
|
+
"\\pi": "π",
|
|
158
|
+
"\\sigma": "σ",
|
|
159
|
+
"\\tau": "τ",
|
|
160
|
+
"\\phi": "φ",
|
|
161
|
+
"\\omega": "ω",
|
|
162
|
+
"\\$": "$",
|
|
163
|
+
"\\%": "%",
|
|
164
|
+
"\\&": "&",
|
|
165
|
+
"\\#": "#",
|
|
166
|
+
"\\{": "{",
|
|
167
|
+
"\\}": "}",
|
|
168
|
+
"\\^": "^",
|
|
169
|
+
"\\_": "_",
|
|
170
|
+
"\\cdot": "·",
|
|
171
|
+
"\\ldots": "…",
|
|
172
|
+
"\\cdots": "⋯"
|
|
173
|
+
};
|
|
174
|
+
function convertLatex(math) {
|
|
175
|
+
let result = math.trim();
|
|
176
|
+
for (const [cmd, unicode] of Object.entries(LATEX_COMMANDS)) result = result.replaceAll(cmd, unicode);
|
|
177
|
+
result = result.replace(/\\frac\{([^}]*)\}\{([^}]*)\}/g, "$1/$2");
|
|
178
|
+
result = result.replace(/\^\{([^}]*)\}/g, "^$1");
|
|
179
|
+
result = result.replace(/_\{([^}]*)\}/g, "_$1");
|
|
180
|
+
result = result.replace(/\\[,;!: ]/g, " ");
|
|
181
|
+
result = result.replace(/\\[a-zA-Z]+\{([^}]*)\}/g, "$1");
|
|
182
|
+
result = result.replace(/\\[a-zA-Z]+/g, "");
|
|
183
|
+
return result.trim();
|
|
184
|
+
}
|
|
185
|
+
var inlineMathExtension = {
|
|
186
|
+
name: "inlineMath",
|
|
187
|
+
level: "inline",
|
|
188
|
+
start: (src) => src.indexOf("$"),
|
|
189
|
+
tokenizer(src) {
|
|
190
|
+
const match = /^\$([^$\n]+?)\$/.exec(src);
|
|
191
|
+
if (match) return {
|
|
192
|
+
type: "inlineMath",
|
|
193
|
+
raw: match[0],
|
|
194
|
+
math: match[1]
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
renderer(token) {
|
|
198
|
+
// v8 ignore next
|
|
199
|
+
return convertLatex(token.math ?? "");
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
//#endregion
|
|
116
203
|
//#region src/components/Markdown/Markdown.tsx
|
|
117
204
|
var HR_PLACEHOLDER = "__CODE_OLLAMA_HR_PLACEHOLDER__";
|
|
118
205
|
marked.use(markedTerminal({ theme: "gitHub" }));
|
|
119
|
-
marked.use({
|
|
206
|
+
marked.use({
|
|
207
|
+
extensions: [inlineMathExtension],
|
|
208
|
+
renderer: { hr: () => `${HR_PLACEHOLDER}\n` }
|
|
209
|
+
});
|
|
120
210
|
function renderMarkdown(content, hrWidth) {
|
|
121
211
|
const hr = "─".repeat(Math.max(1, hrWidth));
|
|
122
212
|
try {
|
|
@@ -1213,6 +1303,9 @@ function Header({ model, onLoad }) {
|
|
|
1213
1303
|
function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
1214
1304
|
const [options, setOptions] = useState([]);
|
|
1215
1305
|
const [error, setError] = useState(null);
|
|
1306
|
+
const handleChange = useCallback((model) => {
|
|
1307
|
+
onSelect({ model });
|
|
1308
|
+
}, [onSelect]);
|
|
1216
1309
|
useInput(async (_input, key) => {
|
|
1217
1310
|
if (!error && options.length && key.return) {
|
|
1218
1311
|
await tick();
|
|
@@ -1245,7 +1338,7 @@ function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
|
1245
1338
|
return /* @__PURE__ */ jsx(SelectPrompt, {
|
|
1246
1339
|
options,
|
|
1247
1340
|
defaultValue: currentModel,
|
|
1248
|
-
onChange:
|
|
1341
|
+
onChange: handleChange,
|
|
1249
1342
|
onCancel: onClose,
|
|
1250
1343
|
children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Select a model" })
|
|
1251
1344
|
});
|
|
@@ -1290,7 +1383,7 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1290
1383
|
setView(View.Edit);
|
|
1291
1384
|
break;
|
|
1292
1385
|
case Action.Clear:
|
|
1293
|
-
onSave(void 0);
|
|
1386
|
+
onSave({ searxngBaseUrl: void 0 });
|
|
1294
1387
|
break;
|
|
1295
1388
|
case Action.Cancel:
|
|
1296
1389
|
default: onClose();
|
|
@@ -1312,7 +1405,7 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1312
1405
|
setError("URL must use http or https.");
|
|
1313
1406
|
return;
|
|
1314
1407
|
}
|
|
1315
|
-
onSave(url.toString());
|
|
1408
|
+
onSave({ searxngBaseUrl: url.toString() });
|
|
1316
1409
|
} catch {
|
|
1317
1410
|
setError("Enter a valid URL.");
|
|
1318
1411
|
}
|
|
@@ -1368,12 +1461,11 @@ var SCREEN = /* @__PURE__ */ function(SCREEN) {
|
|
|
1368
1461
|
}(SCREEN || {});
|
|
1369
1462
|
function App() {
|
|
1370
1463
|
const { exit } = useApp();
|
|
1371
|
-
const [appConfig,
|
|
1464
|
+
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
1372
1465
|
const [currentScreen, setScreen] = useState(SCREEN.CHAT);
|
|
1373
1466
|
const [mode, setMode] = useState(SAFE);
|
|
1374
1467
|
const [sessionId, setSessionId] = useState(0);
|
|
1375
1468
|
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
1376
|
-
const { model, searxngBaseUrl } = appConfig;
|
|
1377
1469
|
const handleHeaderLoad = useCallback(() => {
|
|
1378
1470
|
setIsHeaderLoaded(true);
|
|
1379
1471
|
}, []);
|
|
@@ -1396,20 +1488,12 @@ function App() {
|
|
|
1396
1488
|
break;
|
|
1397
1489
|
}
|
|
1398
1490
|
}, [exit]);
|
|
1399
|
-
const
|
|
1400
|
-
|
|
1401
|
-
...
|
|
1402
|
-
|
|
1403
|
-
}));
|
|
1404
|
-
saveConfig({ model: selected });
|
|
1405
|
-
setScreen(SCREEN.CHAT);
|
|
1406
|
-
}, []);
|
|
1407
|
-
const handleSaveSearch = useCallback((url) => {
|
|
1408
|
-
setAppConfig((currentConfig) => ({
|
|
1409
|
-
...currentConfig,
|
|
1410
|
-
searxngBaseUrl: url
|
|
1491
|
+
const handleUpdateConfig = useCallback((update) => {
|
|
1492
|
+
setConfig((current) => ({
|
|
1493
|
+
...current,
|
|
1494
|
+
...update
|
|
1411
1495
|
}));
|
|
1412
|
-
saveConfig(
|
|
1496
|
+
saveConfig(update);
|
|
1413
1497
|
setScreen(SCREEN.CHAT);
|
|
1414
1498
|
}, []);
|
|
1415
1499
|
const handleClose = useCallback(() => {
|
|
@@ -1429,21 +1513,21 @@ function App() {
|
|
|
1429
1513
|
switch (currentScreen) {
|
|
1430
1514
|
case SCREEN.MODEL_PICKER:
|
|
1431
1515
|
screenContent = /* @__PURE__ */ jsx(ModelPicker, {
|
|
1432
|
-
currentModel: model,
|
|
1433
|
-
onSelect:
|
|
1516
|
+
currentModel: appConfig.model,
|
|
1517
|
+
onSelect: handleUpdateConfig,
|
|
1434
1518
|
onClose: handleClose
|
|
1435
1519
|
});
|
|
1436
1520
|
break;
|
|
1437
1521
|
case SCREEN.SEARCH_SETTINGS:
|
|
1438
1522
|
screenContent = /* @__PURE__ */ jsx(SearchSettings, {
|
|
1439
|
-
currentUrl: searxngBaseUrl,
|
|
1440
|
-
onSave:
|
|
1523
|
+
currentUrl: appConfig.searxngBaseUrl,
|
|
1524
|
+
onSave: handleUpdateConfig,
|
|
1441
1525
|
onClose: handleClose
|
|
1442
1526
|
});
|
|
1443
1527
|
break;
|
|
1444
1528
|
case SCREEN.CHAT:
|
|
1445
1529
|
screenContent = /* @__PURE__ */ jsx(Chat, {
|
|
1446
|
-
model,
|
|
1530
|
+
model: appConfig.model,
|
|
1447
1531
|
onCommand: handleCommand,
|
|
1448
1532
|
mode,
|
|
1449
1533
|
onModeChange: setMode,
|
|
@@ -1455,13 +1539,13 @@ function App() {
|
|
|
1455
1539
|
flexDirection: "column",
|
|
1456
1540
|
children: [
|
|
1457
1541
|
/* @__PURE__ */ jsx(Header, {
|
|
1458
|
-
model,
|
|
1542
|
+
model: appConfig.model,
|
|
1459
1543
|
onLoad: handleHeaderLoad
|
|
1460
1544
|
}),
|
|
1461
1545
|
isHeaderLoaded && screenContent,
|
|
1462
1546
|
/* @__PURE__ */ jsx(Footer, {
|
|
1463
1547
|
mode,
|
|
1464
|
-
model,
|
|
1548
|
+
model: appConfig.model,
|
|
1465
1549
|
onToggleMode: handleToggleMode
|
|
1466
1550
|
})
|
|
1467
1551
|
]
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { homedir } from "node:os";
|
|
|
7
7
|
import { Ollama } from "ollama";
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "code-ollama";
|
|
10
|
-
var version = "0.
|
|
10
|
+
var version = "0.13.1";
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/constants/package.ts
|
|
13
13
|
var NAME = name;
|
|
@@ -68,6 +68,7 @@ var LIST_DIR = "list_dir";
|
|
|
68
68
|
var GREP_SEARCH = "grep_search";
|
|
69
69
|
var VIEW_RANGE = "view_range";
|
|
70
70
|
var WEB_SEARCH = "web_search";
|
|
71
|
+
var WEB_FETCH = "web_fetch";
|
|
71
72
|
//#endregion
|
|
72
73
|
//#region src/utils/agents.ts
|
|
73
74
|
var AGENTS_FILE = "AGENTS.md";
|
|
@@ -282,14 +283,19 @@ var TOOLS = [
|
|
|
282
283
|
defineTool(WEB_SEARCH, "Search the web for external or current information", { query: {
|
|
283
284
|
type: "string",
|
|
284
285
|
description: "The search query to look up"
|
|
285
|
-
} }, ["query"])
|
|
286
|
+
} }, ["query"]),
|
|
287
|
+
defineTool(WEB_FETCH, "Fetch the readable content of a webpage at the given URL", { url: {
|
|
288
|
+
type: "string",
|
|
289
|
+
description: "The full URL of the page to fetch"
|
|
290
|
+
} }, ["url"])
|
|
286
291
|
];
|
|
287
292
|
var READ_TOOLS = new Set([
|
|
288
293
|
READ_FILE,
|
|
289
294
|
LIST_DIR,
|
|
290
295
|
GREP_SEARCH,
|
|
291
296
|
VIEW_RANGE,
|
|
292
|
-
WEB_SEARCH
|
|
297
|
+
WEB_SEARCH,
|
|
298
|
+
WEB_FETCH
|
|
293
299
|
]);
|
|
294
300
|
var WRITE_TOOLS = new Set([
|
|
295
301
|
WRITE_FILE,
|
|
@@ -444,13 +450,14 @@ async function grepSearch(pattern, dirPath) {
|
|
|
444
450
|
//#endregion
|
|
445
451
|
//#region src/utils/tools/web/fetch.ts
|
|
446
452
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
453
|
+
var BASE_HEADERS = { "user-agent": `${NAME}/${VERSION}` };
|
|
447
454
|
/**
|
|
448
455
|
* Fetch text from URL with timeout and headers
|
|
449
456
|
*/
|
|
450
457
|
async function fetchText(url, headers) {
|
|
451
458
|
const response = await fetch(url, {
|
|
452
459
|
headers: {
|
|
453
|
-
|
|
460
|
+
...BASE_HEADERS,
|
|
454
461
|
...headers
|
|
455
462
|
},
|
|
456
463
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
@@ -458,6 +465,21 @@ async function fetchText(url, headers) {
|
|
|
458
465
|
if (!response.ok) throw new Error(`HTTP ${response.status.toString()}`);
|
|
459
466
|
return response.text();
|
|
460
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Fetch and parse JSON from URL with timeout and headers
|
|
470
|
+
*/
|
|
471
|
+
async function fetchJSON(url, headers = {}) {
|
|
472
|
+
const response = await fetch(url, {
|
|
473
|
+
headers: {
|
|
474
|
+
...BASE_HEADERS,
|
|
475
|
+
Accept: "application/json",
|
|
476
|
+
...headers
|
|
477
|
+
},
|
|
478
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
479
|
+
});
|
|
480
|
+
if (!response.ok) throw new Error(`HTTP ${response.status.toString()}`);
|
|
481
|
+
return response.json();
|
|
482
|
+
}
|
|
461
483
|
//#endregion
|
|
462
484
|
//#region src/utils/tools/web/utils.ts
|
|
463
485
|
/**
|
|
@@ -485,6 +507,30 @@ function truncate(value, maxLength) {
|
|
|
485
507
|
return value.length > maxLength ? `${value.slice(0, maxLength - 1).trimEnd()}…` : value;
|
|
486
508
|
}
|
|
487
509
|
//#endregion
|
|
510
|
+
//#region src/utils/tools/web/fetch-page.ts
|
|
511
|
+
var JINA_READER_BASE_URL = "https://r.jina.ai/";
|
|
512
|
+
/**
|
|
513
|
+
* Fetch readable page content via Jina Reader, with fallback to direct fetch + HTML stripping
|
|
514
|
+
*/
|
|
515
|
+
async function webFetch(url) {
|
|
516
|
+
const trimmedUrl = url.trim();
|
|
517
|
+
if (!trimmedUrl) return {
|
|
518
|
+
content: "",
|
|
519
|
+
error: "URL cannot be empty"
|
|
520
|
+
};
|
|
521
|
+
try {
|
|
522
|
+
return { content: await fetchText(`${JINA_READER_BASE_URL}${trimmedUrl}`, { Accept: "text/plain" }) };
|
|
523
|
+
} catch {}
|
|
524
|
+
try {
|
|
525
|
+
return { content: `Note: Jina Reader unavailable, falling back to raw fetch.\n\n${cleanText(stripTags(await fetchText(trimmedUrl, { Accept: "text/html" })))}` };
|
|
526
|
+
} catch (error) {
|
|
527
|
+
return {
|
|
528
|
+
content: "",
|
|
529
|
+
error: `Failed to fetch page: ${error instanceof Error ? error.message : String(error)}`
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
488
534
|
//#region src/utils/tools/web/search.ts
|
|
489
535
|
var SEARCH_RESULT_LIMIT = 5;
|
|
490
536
|
async function webSearch(query) {
|
|
@@ -496,7 +542,7 @@ async function webSearch(query) {
|
|
|
496
542
|
const { searxngBaseUrl } = loadConfig();
|
|
497
543
|
let searxngIssue = null;
|
|
498
544
|
if (searxngBaseUrl) try {
|
|
499
|
-
const searxngResults = await
|
|
545
|
+
const searxngResults = await searchSearXNG(searxngBaseUrl, trimmedQuery);
|
|
500
546
|
if (searxngResults.length) return { content: formatSearchResults("SearXNG", searxngResults) };
|
|
501
547
|
searxngIssue = "SearXNG returned no results";
|
|
502
548
|
} catch (error) {
|
|
@@ -515,14 +561,13 @@ async function webSearch(query) {
|
|
|
515
561
|
};
|
|
516
562
|
}
|
|
517
563
|
}
|
|
518
|
-
async function
|
|
564
|
+
async function searchSearXNG(baseUrl, query) {
|
|
519
565
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
520
566
|
const url = new URL(`${normalizedBaseUrl}/search`);
|
|
521
567
|
url.searchParams.set("q", query);
|
|
522
568
|
url.searchParams.set("format", "json");
|
|
523
569
|
url.searchParams.set("language", "en-US");
|
|
524
|
-
|
|
525
|
-
return normalizeResults(JSON.parse(response).results?.map((result) => ({
|
|
570
|
+
return normalizeResults((await fetchJSON(url.toString())).results?.map((result) => ({
|
|
526
571
|
title: result.title ?? "",
|
|
527
572
|
url: result.url ?? "",
|
|
528
573
|
snippet: result.content ?? ""
|
|
@@ -594,6 +639,7 @@ async function executeTool(name, args, options) {
|
|
|
594
639
|
case GREP_SEARCH: return await grepSearch(args.pattern, args.path);
|
|
595
640
|
case VIEW_RANGE: return viewRange(args.path, args.start, args.end);
|
|
596
641
|
case WEB_SEARCH: return await webSearch(args.query);
|
|
642
|
+
case WEB_FETCH: return await webFetch(args.url);
|
|
597
643
|
default: return {
|
|
598
644
|
content: "",
|
|
599
645
|
error: `Unknown tool: ${name}`
|
|
@@ -650,7 +696,7 @@ async function processRunStream(messages, model) {
|
|
|
650
696
|
}
|
|
651
697
|
async function main(args = process.argv.slice(2)) {
|
|
652
698
|
if (!args.length) {
|
|
653
|
-
const { renderApp } = await import("./assets/tui-
|
|
699
|
+
const { renderApp } = await import("./assets/tui-CCa_vqh0.js");
|
|
654
700
|
reset();
|
|
655
701
|
renderApp();
|
|
656
702
|
return;
|