code-ollama 0.12.0 → 0.13.0
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.
|
@@ -1213,6 +1213,9 @@ function Header({ model, onLoad }) {
|
|
|
1213
1213
|
function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
1214
1214
|
const [options, setOptions] = useState([]);
|
|
1215
1215
|
const [error, setError] = useState(null);
|
|
1216
|
+
const handleChange = useCallback((model) => {
|
|
1217
|
+
onSelect({ model });
|
|
1218
|
+
}, [onSelect]);
|
|
1216
1219
|
useInput(async (_input, key) => {
|
|
1217
1220
|
if (!error && options.length && key.return) {
|
|
1218
1221
|
await tick();
|
|
@@ -1245,7 +1248,7 @@ function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
|
1245
1248
|
return /* @__PURE__ */ jsx(SelectPrompt, {
|
|
1246
1249
|
options,
|
|
1247
1250
|
defaultValue: currentModel,
|
|
1248
|
-
onChange:
|
|
1251
|
+
onChange: handleChange,
|
|
1249
1252
|
onCancel: onClose,
|
|
1250
1253
|
children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Select a model" })
|
|
1251
1254
|
});
|
|
@@ -1290,7 +1293,7 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1290
1293
|
setView(View.Edit);
|
|
1291
1294
|
break;
|
|
1292
1295
|
case Action.Clear:
|
|
1293
|
-
onSave(void 0);
|
|
1296
|
+
onSave({ searxngBaseUrl: void 0 });
|
|
1294
1297
|
break;
|
|
1295
1298
|
case Action.Cancel:
|
|
1296
1299
|
default: onClose();
|
|
@@ -1312,7 +1315,7 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1312
1315
|
setError("URL must use http or https.");
|
|
1313
1316
|
return;
|
|
1314
1317
|
}
|
|
1315
|
-
onSave(url.toString());
|
|
1318
|
+
onSave({ searxngBaseUrl: url.toString() });
|
|
1316
1319
|
} catch {
|
|
1317
1320
|
setError("Enter a valid URL.");
|
|
1318
1321
|
}
|
|
@@ -1368,12 +1371,11 @@ var SCREEN = /* @__PURE__ */ function(SCREEN) {
|
|
|
1368
1371
|
}(SCREEN || {});
|
|
1369
1372
|
function App() {
|
|
1370
1373
|
const { exit } = useApp();
|
|
1371
|
-
const [appConfig,
|
|
1374
|
+
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
1372
1375
|
const [currentScreen, setScreen] = useState(SCREEN.CHAT);
|
|
1373
1376
|
const [mode, setMode] = useState(SAFE);
|
|
1374
1377
|
const [sessionId, setSessionId] = useState(0);
|
|
1375
1378
|
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
1376
|
-
const { model, searxngBaseUrl } = appConfig;
|
|
1377
1379
|
const handleHeaderLoad = useCallback(() => {
|
|
1378
1380
|
setIsHeaderLoaded(true);
|
|
1379
1381
|
}, []);
|
|
@@ -1396,20 +1398,12 @@ function App() {
|
|
|
1396
1398
|
break;
|
|
1397
1399
|
}
|
|
1398
1400
|
}, [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
|
|
1401
|
+
const handleUpdateConfig = useCallback((update) => {
|
|
1402
|
+
setConfig((current) => ({
|
|
1403
|
+
...current,
|
|
1404
|
+
...update
|
|
1411
1405
|
}));
|
|
1412
|
-
saveConfig(
|
|
1406
|
+
saveConfig(update);
|
|
1413
1407
|
setScreen(SCREEN.CHAT);
|
|
1414
1408
|
}, []);
|
|
1415
1409
|
const handleClose = useCallback(() => {
|
|
@@ -1429,21 +1423,21 @@ function App() {
|
|
|
1429
1423
|
switch (currentScreen) {
|
|
1430
1424
|
case SCREEN.MODEL_PICKER:
|
|
1431
1425
|
screenContent = /* @__PURE__ */ jsx(ModelPicker, {
|
|
1432
|
-
currentModel: model,
|
|
1433
|
-
onSelect:
|
|
1426
|
+
currentModel: appConfig.model,
|
|
1427
|
+
onSelect: handleUpdateConfig,
|
|
1434
1428
|
onClose: handleClose
|
|
1435
1429
|
});
|
|
1436
1430
|
break;
|
|
1437
1431
|
case SCREEN.SEARCH_SETTINGS:
|
|
1438
1432
|
screenContent = /* @__PURE__ */ jsx(SearchSettings, {
|
|
1439
|
-
currentUrl: searxngBaseUrl,
|
|
1440
|
-
onSave:
|
|
1433
|
+
currentUrl: appConfig.searxngBaseUrl,
|
|
1434
|
+
onSave: handleUpdateConfig,
|
|
1441
1435
|
onClose: handleClose
|
|
1442
1436
|
});
|
|
1443
1437
|
break;
|
|
1444
1438
|
case SCREEN.CHAT:
|
|
1445
1439
|
screenContent = /* @__PURE__ */ jsx(Chat, {
|
|
1446
|
-
model,
|
|
1440
|
+
model: appConfig.model,
|
|
1447
1441
|
onCommand: handleCommand,
|
|
1448
1442
|
mode,
|
|
1449
1443
|
onModeChange: setMode,
|
|
@@ -1455,13 +1449,13 @@ function App() {
|
|
|
1455
1449
|
flexDirection: "column",
|
|
1456
1450
|
children: [
|
|
1457
1451
|
/* @__PURE__ */ jsx(Header, {
|
|
1458
|
-
model,
|
|
1452
|
+
model: appConfig.model,
|
|
1459
1453
|
onLoad: handleHeaderLoad
|
|
1460
1454
|
}),
|
|
1461
1455
|
isHeaderLoaded && screenContent,
|
|
1462
1456
|
/* @__PURE__ */ jsx(Footer, {
|
|
1463
1457
|
mode,
|
|
1464
|
-
model,
|
|
1458
|
+
model: appConfig.model,
|
|
1465
1459
|
onToggleMode: handleToggleMode
|
|
1466
1460
|
})
|
|
1467
1461
|
]
|
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.0";
|
|
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-ByqNs9kx.js");
|
|
654
700
|
reset();
|
|
655
701
|
renderApp();
|
|
656
702
|
return;
|