code-ollama 0.18.0 → 0.18.2
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-4cX-bKvd.js → tui-XD0Ekj5m.js} +264 -79
- package/dist/cli.js +13 -7
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as ASSISTANT, B as APPROVE, C as saveConfig, D as WARNING, E as HEADER_PREFIX, F as CATALOG, H as VERSION, I as AUTO, L as LABEL, M as USER, N as PLAN_GENERATION_INSTRUCTION, O as LIST$1, P as BACK, R as PLAN, S as loadConfig, T as withSystemMessage, U as LIST, V as REJECT, _ as checkHealth, a as color, b as pullModel, c as createSession, d as listSessions, f as loadSession, g as setClearHandler, h as reset, i as WRITE_TOOLS, j as SYSTEM, k as getTheme, l as deleteSession, m as clear, n as READ_TOOLS, o as write, p as updateSessionModel, r as TOOLS, s as appendMessage, t as executeTool, u as deleteSessionIfEmpty, v as deleteModel, w as resetSystemMessage, x as streamChat, y as listModels, z as SAFE } from "../cli.js";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join, relative } from "node:path";
|
|
@@ -520,9 +520,13 @@ function SelectPrompt({ borderStyle, children, onCancel, ...selectProps }) {
|
|
|
520
520
|
}
|
|
521
521
|
//#endregion
|
|
522
522
|
//#region src/components/SelectPrompt/SelectPromptHint.tsx
|
|
523
|
+
/**
|
|
524
|
+
* Select prompt hint component that displays:
|
|
525
|
+
* Select option (↑↓ + Enter to confirm, Esc/Ctrl+C to cancel)
|
|
526
|
+
*/
|
|
523
527
|
function SelectPromptHint({ message = "Select option", escapeLabel = "cancel" }) {
|
|
524
|
-
return /* @__PURE__ */ jsxs(
|
|
525
|
-
|
|
528
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
529
|
+
color: getTheme().colors.secondary,
|
|
526
530
|
children: [
|
|
527
531
|
/* @__PURE__ */ jsxs(Text, {
|
|
528
532
|
dimColor: true,
|
|
@@ -548,6 +552,14 @@ function SelectPromptHint({ message = "Select option", escapeLabel = "cancel" })
|
|
|
548
552
|
bold: true,
|
|
549
553
|
children: "Esc"
|
|
550
554
|
}),
|
|
555
|
+
/* @__PURE__ */ jsx(Text, {
|
|
556
|
+
dimColor: true,
|
|
557
|
+
children: "/"
|
|
558
|
+
}),
|
|
559
|
+
/* @__PURE__ */ jsx(Text, {
|
|
560
|
+
bold: true,
|
|
561
|
+
children: "Ctrl+C"
|
|
562
|
+
}),
|
|
551
563
|
/* @__PURE__ */ jsxs(Text, {
|
|
552
564
|
dimColor: true,
|
|
553
565
|
children: [
|
|
@@ -560,7 +572,7 @@ function SelectPromptHint({ message = "Select option", escapeLabel = "cancel" })
|
|
|
560
572
|
});
|
|
561
573
|
}
|
|
562
574
|
//#endregion
|
|
563
|
-
//#region src/components/PlanApproval.tsx
|
|
575
|
+
//#region src/components/PlanApproval/PlanApproval.tsx
|
|
564
576
|
var options$1 = [
|
|
565
577
|
{
|
|
566
578
|
label: "Auto - Execute tools automatically",
|
|
@@ -606,7 +618,7 @@ function PlanApproval({ planContent, onModeChange, theme = getTheme() }) {
|
|
|
606
618
|
});
|
|
607
619
|
}
|
|
608
620
|
//#endregion
|
|
609
|
-
//#region src/components/ToolApproval.tsx
|
|
621
|
+
//#region src/components/ToolApproval/ToolApproval.tsx
|
|
610
622
|
var options = [{
|
|
611
623
|
label: "Approve tool call",
|
|
612
624
|
value: APPROVE
|
|
@@ -821,7 +833,7 @@ function CommandMenu({ input, onSubmit }) {
|
|
|
821
833
|
});
|
|
822
834
|
}
|
|
823
835
|
//#endregion
|
|
824
|
-
//#region src/components/Suggestions.tsx
|
|
836
|
+
//#region src/components/Suggestions/Suggestions.tsx
|
|
825
837
|
var DEFAULT_MAX_VISIBLE_OPTIONS = 5;
|
|
826
838
|
function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_MAX_VISIBLE_OPTIONS, resetKey, onHighlight, onSelect }) {
|
|
827
839
|
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
@@ -843,17 +855,17 @@ function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_
|
|
|
843
855
|
onHighlight,
|
|
844
856
|
options
|
|
845
857
|
]);
|
|
846
|
-
useInput((
|
|
858
|
+
useInput((input, key) => {
|
|
847
859
|
if (isDisabled || !options.length) return;
|
|
848
|
-
if (key.downArrow) {
|
|
860
|
+
if (key.downArrow || input === "\x1B[B") {
|
|
849
861
|
setFocusedIndex((currentIndex) => Math.min(currentIndex + 1, options.length - 1));
|
|
850
862
|
return;
|
|
851
863
|
}
|
|
852
|
-
if (key.upArrow) {
|
|
864
|
+
if (key.upArrow || input === "\x1B[A") {
|
|
853
865
|
setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
|
|
854
866
|
return;
|
|
855
867
|
}
|
|
856
|
-
if (key.tab || key.return) onSelect(options[focusedIndex]);
|
|
868
|
+
if (key.tab || key.return || input === " " || input === "\r") onSelect(options[focusedIndex]);
|
|
857
869
|
});
|
|
858
870
|
if (!options.length) return null;
|
|
859
871
|
const visibleStart = Math.min(Math.max(0, focusedIndex - maxVisibleOptions + 1), Math.max(0, options.length - maxVisibleOptions));
|
|
@@ -1145,10 +1157,10 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
|
|
|
1145
1157
|
var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
|
|
1146
1158
|
var PLAN_CHECKLIST_REMINDER = "Then display the execution plan as an unchecked Markdown checklist only";
|
|
1147
1159
|
var PLAN_EXECUTION_REMINDER = `Do not claim success and do not call ${Array.from(WRITE_TOOLS).join(", ")} until the user approves execution`;
|
|
1148
|
-
var
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
return
|
|
1160
|
+
var InterruptReason = /* @__PURE__ */ function(InterruptReason) {
|
|
1161
|
+
InterruptReason["Interrupted"] = "interrupted";
|
|
1162
|
+
InterruptReason["Rejected"] = "rejected";
|
|
1163
|
+
return InterruptReason;
|
|
1152
1164
|
}({});
|
|
1153
1165
|
//#endregion
|
|
1154
1166
|
//#region src/components/Chat/plan.ts
|
|
@@ -1216,13 +1228,15 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1216
1228
|
abortControllerRef.current = null;
|
|
1217
1229
|
setIsLoading(false);
|
|
1218
1230
|
setStreamingMessage(null);
|
|
1219
|
-
setInterruptReason(
|
|
1231
|
+
setInterruptReason(InterruptReason.Interrupted);
|
|
1220
1232
|
setMessages((prev) => [...prev, {
|
|
1221
1233
|
role: USER,
|
|
1222
1234
|
content: TURN_ABORTED_MESSAGE
|
|
1223
1235
|
}]);
|
|
1224
1236
|
}, []);
|
|
1225
1237
|
const processStream = useCallback(async (currentMessages, executionMode = mode) => {
|
|
1238
|
+
// v8 ignore next
|
|
1239
|
+
if (!model) throw new Error("Model is required");
|
|
1226
1240
|
const controller = new AbortController();
|
|
1227
1241
|
abortControllerRef.current = controller;
|
|
1228
1242
|
const assistantMessage = {
|
|
@@ -1297,6 +1311,9 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1297
1311
|
theme
|
|
1298
1312
|
]);
|
|
1299
1313
|
const processStreamReadOnly = useCallback(async (currentMessages) => {
|
|
1314
|
+
const modelName = model;
|
|
1315
|
+
// v8 ignore next
|
|
1316
|
+
if (!modelName) throw new Error("Model is required");
|
|
1300
1317
|
const controller = new AbortController();
|
|
1301
1318
|
abortControllerRef.current = controller;
|
|
1302
1319
|
const assistantMessage = {
|
|
@@ -1327,7 +1344,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1327
1344
|
setStreamingMessage(assistantMessage);
|
|
1328
1345
|
try {
|
|
1329
1346
|
const readOnlyTools = TOOLS.filter((tool) => READ_TOOLS.has(tool.function.name));
|
|
1330
|
-
for await (const chunk of streamChat(withSystemMessage(currentMessages),
|
|
1347
|
+
for await (const chunk of streamChat(withSystemMessage(currentMessages), modelName, readOnlyTools, controller.signal)) {
|
|
1331
1348
|
// v8 ignore next 3
|
|
1332
1349
|
if (controller.signal.aborted) return;
|
|
1333
1350
|
if (chunk.type === "content") {
|
|
@@ -1363,7 +1380,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1363
1380
|
};
|
|
1364
1381
|
setStreamingMessage(planAssistantMessage);
|
|
1365
1382
|
try {
|
|
1366
|
-
for await (const chunk of streamChat(withSystemMessage(planMessages),
|
|
1383
|
+
for await (const chunk of streamChat(withSystemMessage(planMessages), modelName, [], controller.signal)) {
|
|
1367
1384
|
// v8 ignore next 3
|
|
1368
1385
|
if (controller.signal.aborted) return;
|
|
1369
1386
|
if (chunk.type === "content") {
|
|
@@ -1455,7 +1472,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1455
1472
|
content: TURN_ABORTED_MESSAGE
|
|
1456
1473
|
}]);
|
|
1457
1474
|
setIsLoading(false);
|
|
1458
|
-
setInterruptReason(
|
|
1475
|
+
setInterruptReason(InterruptReason.Rejected);
|
|
1459
1476
|
break;
|
|
1460
1477
|
}
|
|
1461
1478
|
}, [
|
|
@@ -1511,7 +1528,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1511
1528
|
marginBottom: 1,
|
|
1512
1529
|
children: /* @__PURE__ */ jsx(Text, {
|
|
1513
1530
|
color: theme.colors.error,
|
|
1514
|
-
children: interruptReason ===
|
|
1531
|
+
children: interruptReason === InterruptReason.Rejected ? `❗ Tool call rejected.` : `❗ Execution interrupted.`
|
|
1515
1532
|
})
|
|
1516
1533
|
}),
|
|
1517
1534
|
!pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(Box, {
|
|
@@ -1527,7 +1544,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1527
1544
|
});
|
|
1528
1545
|
}
|
|
1529
1546
|
//#endregion
|
|
1530
|
-
//#region src/components/Footer.tsx
|
|
1547
|
+
//#region src/components/Footer/Footer.tsx
|
|
1531
1548
|
function getModeColor(mode, theme) {
|
|
1532
1549
|
switch (mode) {
|
|
1533
1550
|
case PLAN: return theme.colors.modePlan;
|
|
@@ -1538,6 +1555,7 @@ function getModeColor(mode, theme) {
|
|
|
1538
1555
|
}
|
|
1539
1556
|
}
|
|
1540
1557
|
function Footer({ mode, model, onToggleMode, theme = getTheme() }) {
|
|
1558
|
+
const modelLabel = model || "not configured";
|
|
1541
1559
|
useInput((_, key) => {
|
|
1542
1560
|
if (key.tab && key.shift) onToggleMode();
|
|
1543
1561
|
});
|
|
@@ -1561,20 +1579,21 @@ function Footer({ mode, model, onToggleMode, theme = getTheme() }) {
|
|
|
1561
1579
|
" Model: ",
|
|
1562
1580
|
/* @__PURE__ */ jsx(Text, {
|
|
1563
1581
|
color: theme.colors.model,
|
|
1564
|
-
children:
|
|
1582
|
+
children: modelLabel
|
|
1565
1583
|
})
|
|
1566
1584
|
]
|
|
1567
1585
|
})
|
|
1568
1586
|
});
|
|
1569
1587
|
}
|
|
1570
1588
|
//#endregion
|
|
1571
|
-
//#region src/components/Header.tsx
|
|
1589
|
+
//#region src/components/Header/Header.tsx
|
|
1572
1590
|
function abbreviatePath(dir) {
|
|
1573
1591
|
const home = homedir();
|
|
1574
1592
|
return dir.startsWith(home) ? `~${dir.slice(home.length)}` : dir;
|
|
1575
1593
|
}
|
|
1576
1594
|
function Header({ model, onLoad, theme = getTheme() }) {
|
|
1577
1595
|
const directory = abbreviatePath(process.cwd());
|
|
1596
|
+
const modelLabel = model || "not configured";
|
|
1578
1597
|
useEffect(() => {
|
|
1579
1598
|
onLoad();
|
|
1580
1599
|
}, []);
|
|
@@ -1606,7 +1625,7 @@ function Header({ model, onLoad, theme = getTheme() }) {
|
|
|
1606
1625
|
dimColor: true,
|
|
1607
1626
|
children: "model:".padEnd(11)
|
|
1608
1627
|
}),
|
|
1609
|
-
/* @__PURE__ */ jsx(Text, { children:
|
|
1628
|
+
/* @__PURE__ */ jsx(Text, { children: modelLabel.padEnd(modelLabel.length + 3) }),
|
|
1610
1629
|
/* @__PURE__ */ jsx(Text, {
|
|
1611
1630
|
color: theme.colors.command,
|
|
1612
1631
|
children: "/model"
|
|
@@ -1806,11 +1825,15 @@ function ModelCustomDownloadView({ downloadDraft, notice, theme, onDraftChange,
|
|
|
1806
1825
|
onSelect: onSelectSuggestion
|
|
1807
1826
|
}),
|
|
1808
1827
|
renderNotice(),
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1828
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1829
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1830
|
+
color: theme.colors.secondary,
|
|
1831
|
+
dimColor: true,
|
|
1832
|
+
children: "Press Enter to download."
|
|
1833
|
+
}),
|
|
1834
|
+
" ",
|
|
1835
|
+
/* @__PURE__ */ jsx(ExitHint, {})
|
|
1836
|
+
] })
|
|
1814
1837
|
]
|
|
1815
1838
|
});
|
|
1816
1839
|
}
|
|
@@ -2008,13 +2031,20 @@ function ModelManager({ currentModel, onSelect, onClose, theme = getTheme() }) {
|
|
|
2008
2031
|
pullRef.current?.abort();
|
|
2009
2032
|
}, []);
|
|
2010
2033
|
useInput((input, key) => {
|
|
2011
|
-
|
|
2034
|
+
const isEscape = key.escape || input === "\x1B\x1B";
|
|
2035
|
+
const isCtrlC = key.ctrl && input === "c" || input === "";
|
|
2036
|
+
if (loadError && view !== View.Menu && (isEscape || isCtrlC)) {
|
|
2037
|
+
handleBackToMenu();
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
if (view === View.CustomDownload && (isEscape || isCtrlC)) {
|
|
2012
2041
|
setNotice(null);
|
|
2013
2042
|
setHighlightedSuggestion(null);
|
|
2014
2043
|
setView(View.Download);
|
|
2015
2044
|
return;
|
|
2016
2045
|
}
|
|
2017
|
-
|
|
2046
|
+
// v8 ignore next
|
|
2047
|
+
if (view === View.Downloading && (isEscape || isCtrlC)) cancelActivePull();
|
|
2018
2048
|
});
|
|
2019
2049
|
const handleMenuChange = useCallback((value) => {
|
|
2020
2050
|
setNotice(null);
|
|
@@ -2157,17 +2187,10 @@ function ModelManager({ currentModel, onSelect, onClose, theme = getTheme() }) {
|
|
|
2157
2187
|
color: notice.tone === "error" ? theme.colors.error : notice.tone === "success" ? theme.colors.status : theme.colors.secondary,
|
|
2158
2188
|
children: notice.text
|
|
2159
2189
|
}) : null;
|
|
2160
|
-
if (loadError && view !== View.Menu) return /* @__PURE__ */ jsxs(
|
|
2161
|
-
|
|
2162
|
-
children: [
|
|
2163
|
-
|
|
2164
|
-
children: ["Error loading models: ", loadError]
|
|
2165
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
2166
|
-
color: theme.colors.secondary,
|
|
2167
|
-
dimColor: true,
|
|
2168
|
-
children: "Press Esc to go back."
|
|
2169
|
-
})]
|
|
2170
|
-
});
|
|
2190
|
+
if (loadError && view !== View.Menu) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
2191
|
+
color: theme.colors.error,
|
|
2192
|
+
children: ["Error loading models: ", loadError]
|
|
2193
|
+
}), /* @__PURE__ */ jsx(ExitHint, {})] });
|
|
2171
2194
|
if (view === View.Downloading && downloadProgress) return /* @__PURE__ */ jsx(ModelDownloadingView, {
|
|
2172
2195
|
progress: downloadProgress,
|
|
2173
2196
|
theme,
|
|
@@ -2233,7 +2256,7 @@ function ModelManager({ currentModel, onSelect, onClose, theme = getTheme() }) {
|
|
|
2233
2256
|
});
|
|
2234
2257
|
}
|
|
2235
2258
|
//#endregion
|
|
2236
|
-
//#region src/components/SearchSettings.tsx
|
|
2259
|
+
//#region src/components/SearchSettings/SearchSettings.tsx
|
|
2237
2260
|
function SearchSettings({ currentUrl, onClose, onSave, theme = getTheme() }) {
|
|
2238
2261
|
const [view, setView] = useState("menu");
|
|
2239
2262
|
const [draftUrl, setDraftUrl] = useState(currentUrl ?? "");
|
|
@@ -2309,11 +2332,15 @@ function SearchSettings({ currentUrl, onClose, onSave, theme = getTheme() }) {
|
|
|
2309
2332
|
color: theme.colors.error,
|
|
2310
2333
|
children: error
|
|
2311
2334
|
}),
|
|
2312
|
-
/* @__PURE__ */
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2335
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2336
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2337
|
+
color: theme.colors.secondary,
|
|
2338
|
+
dimColor: true,
|
|
2339
|
+
children: "Press Enter to save."
|
|
2340
|
+
}),
|
|
2341
|
+
" ",
|
|
2342
|
+
/* @__PURE__ */ jsx(ExitHint, {})
|
|
2343
|
+
] })
|
|
2317
2344
|
]
|
|
2318
2345
|
});
|
|
2319
2346
|
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
@@ -2335,7 +2362,7 @@ function SearchSettings({ currentUrl, onClose, onSave, theme = getTheme() }) {
|
|
|
2335
2362
|
});
|
|
2336
2363
|
}
|
|
2337
2364
|
//#endregion
|
|
2338
|
-
//#region src/components/SessionManager.tsx
|
|
2365
|
+
//#region src/components/SessionManager/SessionManager.tsx
|
|
2339
2366
|
var ACTION = {
|
|
2340
2367
|
CLOSE: "close",
|
|
2341
2368
|
DELETE_MENU: "delete-menu",
|
|
@@ -2447,7 +2474,7 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen, th
|
|
|
2447
2474
|
});
|
|
2448
2475
|
}
|
|
2449
2476
|
//#endregion
|
|
2450
|
-
//#region src/components/ThemeSettings.tsx
|
|
2477
|
+
//#region src/components/ThemeSettings/ThemeSettings.tsx
|
|
2451
2478
|
function ThemeSettings({ currentTheme, onClose, onPreview, onSave }) {
|
|
2452
2479
|
const [selectedIndex, setSelectedIndex] = useState(() => {
|
|
2453
2480
|
const initialIndex = LIST$1.findIndex(({ id }) => id === currentTheme);
|
|
@@ -2549,45 +2576,45 @@ function ThemeSettings({ currentTheme, onClose, onPreview, onSave }) {
|
|
|
2549
2576
|
}
|
|
2550
2577
|
//#endregion
|
|
2551
2578
|
//#region src/components/App/constants.ts
|
|
2552
|
-
var
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
return
|
|
2579
|
+
var Screen = /* @__PURE__ */ function(Screen) {
|
|
2580
|
+
Screen["Chat"] = "chat";
|
|
2581
|
+
Screen["ModelManager"] = "model-manager";
|
|
2582
|
+
Screen["SearchSettings"] = "search-settings";
|
|
2583
|
+
Screen["SessionManager"] = "session-manager";
|
|
2584
|
+
Screen["ThemeSettings"] = "theme-settings";
|
|
2585
|
+
return Screen;
|
|
2559
2586
|
}({});
|
|
2560
2587
|
//#endregion
|
|
2561
2588
|
//#region src/components/App/hooks/useScreenRouter.ts
|
|
2562
2589
|
function useScreenRouter() {
|
|
2563
2590
|
const { exit } = useApp();
|
|
2564
|
-
const [currentScreen, setScreen] = useState(
|
|
2591
|
+
const [currentScreen, setScreen] = useState(Screen.Chat);
|
|
2565
2592
|
return {
|
|
2566
2593
|
currentScreen,
|
|
2567
2594
|
setScreen,
|
|
2568
2595
|
handleClose: useCallback(() => {
|
|
2569
|
-
setScreen(
|
|
2596
|
+
setScreen(Screen.Chat);
|
|
2570
2597
|
}, []),
|
|
2571
2598
|
handleCommand: useCallback((command, callbacks) => {
|
|
2572
2599
|
const { onCreateSession, onSetPreviewThemeId, model, theme } = callbacks;
|
|
2573
2600
|
switch (command) {
|
|
2574
2601
|
case "/session":
|
|
2575
|
-
setScreen(
|
|
2602
|
+
setScreen(Screen.SessionManager);
|
|
2576
2603
|
break;
|
|
2577
2604
|
case "/model":
|
|
2578
|
-
setScreen(
|
|
2605
|
+
setScreen(Screen.ModelManager);
|
|
2579
2606
|
break;
|
|
2580
2607
|
case "/search":
|
|
2581
|
-
setScreen(
|
|
2608
|
+
setScreen(Screen.SearchSettings);
|
|
2582
2609
|
break;
|
|
2583
2610
|
case "/theme":
|
|
2584
2611
|
onSetPreviewThemeId(theme);
|
|
2585
|
-
setScreen(
|
|
2612
|
+
setScreen(Screen.ThemeSettings);
|
|
2586
2613
|
break;
|
|
2587
2614
|
case "/clear": {
|
|
2588
2615
|
resetSystemMessage();
|
|
2589
2616
|
const nextSession = onCreateSession(model);
|
|
2590
|
-
setScreen(
|
|
2617
|
+
setScreen(Screen.Chat);
|
|
2591
2618
|
clear(nextSession.metadata.id);
|
|
2592
2619
|
break;
|
|
2593
2620
|
}
|
|
@@ -2674,7 +2701,7 @@ function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
|
|
|
2674
2701
|
activeTheme,
|
|
2675
2702
|
handleThemeClose: useCallback(() => {
|
|
2676
2703
|
setPreviewThemeId(null);
|
|
2677
|
-
setScreen(
|
|
2704
|
+
setScreen(Screen.Chat);
|
|
2678
2705
|
}, [setScreen]),
|
|
2679
2706
|
handleThemePreview,
|
|
2680
2707
|
handleThemeSave: useCallback((themeId) => {
|
|
@@ -2685,17 +2712,133 @@ function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
|
|
|
2685
2712
|
};
|
|
2686
2713
|
}
|
|
2687
2714
|
//#endregion
|
|
2715
|
+
//#region src/components/App/ReadinessCheck.tsx
|
|
2716
|
+
var ReadinessState = /* @__PURE__ */ function(ReadinessState) {
|
|
2717
|
+
ReadinessState["Checking"] = "checking";
|
|
2718
|
+
ReadinessState["Ready"] = "ready";
|
|
2719
|
+
ReadinessState["MissingModelConfig"] = "missing-model-config";
|
|
2720
|
+
ReadinessState["NoInstalledModels"] = "no-installed-models";
|
|
2721
|
+
ReadinessState["ServerUnavailable"] = "server-unavailable";
|
|
2722
|
+
ReadinessState["ModelLoadError"] = "model-load-error";
|
|
2723
|
+
return ReadinessState;
|
|
2724
|
+
}({});
|
|
2725
|
+
function getTitle(setupState) {
|
|
2726
|
+
switch (setupState) {
|
|
2727
|
+
case "server-unavailable": return "Ollama Server Unavailable";
|
|
2728
|
+
case "model-load-error": return "Connection Error";
|
|
2729
|
+
case "missing-model-config": return "No Model Configured";
|
|
2730
|
+
case "no-installed-models": return "No Model Installed";
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
function getMessage(setupState, errorMessage) {
|
|
2734
|
+
const theme = getTheme();
|
|
2735
|
+
switch (setupState) {
|
|
2736
|
+
case "checking": return /* @__PURE__ */ jsx(Text, { children: "Checking Ollama server and model setup..." });
|
|
2737
|
+
case "missing-model-config": return /* @__PURE__ */ jsxs(Text, { children: [
|
|
2738
|
+
"Select or download a model with",
|
|
2739
|
+
" ",
|
|
2740
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2741
|
+
color: theme.colors.command,
|
|
2742
|
+
children: "/model"
|
|
2743
|
+
})
|
|
2744
|
+
] });
|
|
2745
|
+
case "no-installed-models": return /* @__PURE__ */ jsxs(Text, { children: ["Download a model with ", /* @__PURE__ */ jsx(Text, {
|
|
2746
|
+
color: theme.colors.command,
|
|
2747
|
+
children: "/model"
|
|
2748
|
+
})] });
|
|
2749
|
+
case "server-unavailable": return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, { children: "Ollama server is not running or unreachable." }), /* @__PURE__ */ jsxs(Text, { children: [
|
|
2750
|
+
"Start it with ",
|
|
2751
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2752
|
+
color: theme.colors.command,
|
|
2753
|
+
children: "ollama serve"
|
|
2754
|
+
}),
|
|
2755
|
+
" ",
|
|
2756
|
+
"and restart the app"
|
|
2757
|
+
] })] });
|
|
2758
|
+
case "model-load-error": return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, { children: [
|
|
2759
|
+
"Error loading models",
|
|
2760
|
+
errorMessage ? `: ${errorMessage}` : "",
|
|
2761
|
+
"."
|
|
2762
|
+
] }), /* @__PURE__ */ jsx(Text, { children: "Fix the connection and restart the app" })] });
|
|
2763
|
+
default: return null;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
function ReadinessCheck({ errorMessage, onCommand, setupState, theme = getTheme() }) {
|
|
2767
|
+
const title = getTitle(setupState);
|
|
2768
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2769
|
+
flexDirection: "column",
|
|
2770
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
2771
|
+
borderStyle: "round",
|
|
2772
|
+
flexDirection: "column",
|
|
2773
|
+
marginBottom: 1,
|
|
2774
|
+
paddingX: 1,
|
|
2775
|
+
paddingY: 1,
|
|
2776
|
+
children: [title && /* @__PURE__ */ jsxs(Text, {
|
|
2777
|
+
bold: true,
|
|
2778
|
+
color: theme.colors.error,
|
|
2779
|
+
children: [
|
|
2780
|
+
"❗",
|
|
2781
|
+
" ",
|
|
2782
|
+
title
|
|
2783
|
+
]
|
|
2784
|
+
}), getMessage(setupState, errorMessage)]
|
|
2785
|
+
}), /* @__PURE__ */ jsx(ChatInput, {
|
|
2786
|
+
history: [],
|
|
2787
|
+
onSubmit: onCommand
|
|
2788
|
+
})]
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
//#endregion
|
|
2688
2792
|
//#region src/components/App/App.tsx
|
|
2689
2793
|
function App({ sessionId }) {
|
|
2690
2794
|
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
2691
2795
|
const [mode, setMode] = useState(SAFE);
|
|
2692
2796
|
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
2797
|
+
const [setupState, setSetupState] = useState(() => appConfig.model ? ReadinessState.Ready : ReadinessState.MissingModelConfig);
|
|
2798
|
+
const [setupErrorMessage, setSetupErrorMessage] = useState(null);
|
|
2693
2799
|
const { currentScreen, setScreen, handleClose, handleCommand } = useScreenRouter();
|
|
2694
2800
|
const { activeSession, setSession, handleCreateSession, handleOpenSession, handleDeleteSession, handleMessagesChange } = useSessionManager({
|
|
2695
2801
|
sessionId,
|
|
2696
|
-
model: appConfig.model,
|
|
2802
|
+
model: appConfig.model ?? "",
|
|
2697
2803
|
commandColor: getTheme(appConfig.theme).colors.command
|
|
2698
2804
|
});
|
|
2805
|
+
useEffect(() => {
|
|
2806
|
+
let isMounted = true;
|
|
2807
|
+
async function refreshSetupState() {
|
|
2808
|
+
if (!appConfig.model) {
|
|
2809
|
+
// v8 ignore next
|
|
2810
|
+
if (isMounted) {
|
|
2811
|
+
setSetupErrorMessage(null);
|
|
2812
|
+
setSetupState(ReadinessState.MissingModelConfig);
|
|
2813
|
+
}
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
if (currentScreen !== Screen.Chat) return;
|
|
2817
|
+
// v8 ignore next
|
|
2818
|
+
if (isMounted) {
|
|
2819
|
+
setSetupErrorMessage(null);
|
|
2820
|
+
setSetupState(ReadinessState.Checking);
|
|
2821
|
+
}
|
|
2822
|
+
try {
|
|
2823
|
+
const isHealthy = await checkHealth();
|
|
2824
|
+
if (!isMounted) return;
|
|
2825
|
+
if (!isHealthy) {
|
|
2826
|
+
setSetupState(ReadinessState.ServerUnavailable);
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
setSetupState((await listModels()).length > 0 ? ReadinessState.Ready : ReadinessState.NoInstalledModels);
|
|
2830
|
+
} catch (error) {
|
|
2831
|
+
// v8 ignore start
|
|
2832
|
+
if (!isMounted) return;
|
|
2833
|
+
setSetupErrorMessage(error instanceof Error ? error.message : String(error));
|
|
2834
|
+
setSetupState(ReadinessState.ModelLoadError);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
refreshSetupState();
|
|
2838
|
+
return () => {
|
|
2839
|
+
isMounted = false;
|
|
2840
|
+
};
|
|
2841
|
+
}, [appConfig.model, currentScreen]);
|
|
2699
2842
|
const handleUpdateConfig = useCallback((update) => {
|
|
2700
2843
|
setConfig((current) => ({
|
|
2701
2844
|
...current,
|
|
@@ -2707,7 +2850,7 @@ function App({ sessionId }) {
|
|
|
2707
2850
|
...current,
|
|
2708
2851
|
metadata: updateSessionModel(current.metadata.id, newModel)
|
|
2709
2852
|
}));
|
|
2710
|
-
setScreen(
|
|
2853
|
+
setScreen(Screen.Chat);
|
|
2711
2854
|
}, [setScreen, setSession]);
|
|
2712
2855
|
const { activeTheme, handleThemeClose, handleThemePreview, handleThemeSave, setPreviewThemeId } = useThemeSettings({
|
|
2713
2856
|
currentTheme: appConfig.theme,
|
|
@@ -2729,7 +2872,7 @@ function App({ sessionId }) {
|
|
|
2729
2872
|
}, []);
|
|
2730
2873
|
const handleChatCommand = useCallback((command) => {
|
|
2731
2874
|
handleCommand(command, {
|
|
2732
|
-
model: appConfig.model,
|
|
2875
|
+
model: appConfig.model ?? "",
|
|
2733
2876
|
theme: appConfig.theme,
|
|
2734
2877
|
onCreateSession: handleCreateSession,
|
|
2735
2878
|
onSetPreviewThemeId: setPreviewThemeId
|
|
@@ -2743,27 +2886,27 @@ function App({ sessionId }) {
|
|
|
2743
2886
|
]);
|
|
2744
2887
|
const handleDeleteSessionAndStay = useCallback((sid) => {
|
|
2745
2888
|
handleDeleteSession(sid);
|
|
2746
|
-
setScreen(
|
|
2889
|
+
setScreen(Screen.SessionManager);
|
|
2747
2890
|
}, [handleDeleteSession, setScreen]);
|
|
2748
2891
|
const handleOpenSessionAndNavigate = useCallback((sid) => {
|
|
2749
2892
|
handleOpenSession(sid);
|
|
2750
|
-
setScreen(
|
|
2893
|
+
setScreen(Screen.Chat);
|
|
2751
2894
|
}, [handleOpenSession, setScreen]);
|
|
2752
2895
|
const handleCreateSessionAndNavigate = useCallback(() => {
|
|
2753
2896
|
handleCreateSession();
|
|
2754
|
-
setScreen(
|
|
2897
|
+
setScreen(Screen.Chat);
|
|
2755
2898
|
}, [handleCreateSession, setScreen]);
|
|
2756
2899
|
let screenContent;
|
|
2757
2900
|
switch (currentScreen) {
|
|
2758
|
-
case
|
|
2901
|
+
case Screen.ModelManager:
|
|
2759
2902
|
screenContent = /* @__PURE__ */ jsx(ModelManager, {
|
|
2760
|
-
currentModel: appConfig.model,
|
|
2903
|
+
currentModel: appConfig.model ?? "",
|
|
2761
2904
|
onSelect: handleUpdateConfig,
|
|
2762
2905
|
onClose: handleClose,
|
|
2763
2906
|
theme: activeTheme
|
|
2764
2907
|
});
|
|
2765
2908
|
break;
|
|
2766
|
-
case
|
|
2909
|
+
case Screen.SearchSettings:
|
|
2767
2910
|
screenContent = /* @__PURE__ */ jsx(SearchSettings, {
|
|
2768
2911
|
currentUrl: appConfig.searxngBaseUrl,
|
|
2769
2912
|
onSave: handleUpdateConfig,
|
|
@@ -2771,7 +2914,7 @@ function App({ sessionId }) {
|
|
|
2771
2914
|
theme: activeTheme
|
|
2772
2915
|
});
|
|
2773
2916
|
break;
|
|
2774
|
-
case
|
|
2917
|
+
case Screen.SessionManager:
|
|
2775
2918
|
screenContent = /* @__PURE__ */ jsx(SessionManager, {
|
|
2776
2919
|
currentSessionId: activeSession.metadata.id,
|
|
2777
2920
|
onClose: handleClose,
|
|
@@ -2781,7 +2924,7 @@ function App({ sessionId }) {
|
|
|
2781
2924
|
theme: activeTheme
|
|
2782
2925
|
});
|
|
2783
2926
|
break;
|
|
2784
|
-
case
|
|
2927
|
+
case Screen.ThemeSettings:
|
|
2785
2928
|
screenContent = /* @__PURE__ */ jsx(ThemeSettings, {
|
|
2786
2929
|
currentTheme: appConfig.theme,
|
|
2787
2930
|
onClose: handleThemeClose,
|
|
@@ -2789,8 +2932,8 @@ function App({ sessionId }) {
|
|
|
2789
2932
|
onSave: handleThemeSave
|
|
2790
2933
|
});
|
|
2791
2934
|
break;
|
|
2792
|
-
case
|
|
2793
|
-
screenContent = /* @__PURE__ */ jsx(Chat, {
|
|
2935
|
+
case Screen.Chat:
|
|
2936
|
+
screenContent = setupState === ReadinessState.Ready ? /* @__PURE__ */ jsx(Chat, {
|
|
2794
2937
|
initialMessages: activeSession.messages,
|
|
2795
2938
|
model: appConfig.model,
|
|
2796
2939
|
onCommand: handleChatCommand,
|
|
@@ -2799,6 +2942,11 @@ function App({ sessionId }) {
|
|
|
2799
2942
|
onModeChange: setMode,
|
|
2800
2943
|
sessionId: activeSession.metadata.id,
|
|
2801
2944
|
theme: activeTheme
|
|
2945
|
+
}) : /* @__PURE__ */ jsx(ReadinessCheck, {
|
|
2946
|
+
errorMessage: setupErrorMessage,
|
|
2947
|
+
onCommand: handleChatCommand,
|
|
2948
|
+
setupState,
|
|
2949
|
+
theme: activeTheme
|
|
2802
2950
|
});
|
|
2803
2951
|
break;
|
|
2804
2952
|
}
|
|
@@ -2806,14 +2954,14 @@ function App({ sessionId }) {
|
|
|
2806
2954
|
flexDirection: "column",
|
|
2807
2955
|
children: [
|
|
2808
2956
|
/* @__PURE__ */ jsx(Header, {
|
|
2809
|
-
model: appConfig.model,
|
|
2957
|
+
model: appConfig.model ?? "",
|
|
2810
2958
|
onLoad: handleHeaderLoad,
|
|
2811
2959
|
theme: activeTheme
|
|
2812
2960
|
}),
|
|
2813
2961
|
isHeaderLoaded && screenContent,
|
|
2814
2962
|
/* @__PURE__ */ jsx(Footer, {
|
|
2815
2963
|
mode,
|
|
2816
|
-
model: appConfig.model,
|
|
2964
|
+
model: appConfig.model ?? "",
|
|
2817
2965
|
onToggleMode: handleToggleMode,
|
|
2818
2966
|
theme: activeTheme
|
|
2819
2967
|
})
|
|
@@ -2821,6 +2969,43 @@ function App({ sessionId }) {
|
|
|
2821
2969
|
});
|
|
2822
2970
|
}
|
|
2823
2971
|
//#endregion
|
|
2972
|
+
//#region src/components/ExitHint/ExitHint.tsx
|
|
2973
|
+
/**
|
|
2974
|
+
* Exit hint component that displays:
|
|
2975
|
+
* Press Esc/Ctrl+C to go back.
|
|
2976
|
+
*/
|
|
2977
|
+
function ExitHint({ action = "go back" }) {
|
|
2978
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
2979
|
+
color: getTheme().colors.secondary,
|
|
2980
|
+
children: [
|
|
2981
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2982
|
+
dimColor: true,
|
|
2983
|
+
children: "Press "
|
|
2984
|
+
}),
|
|
2985
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2986
|
+
bold: true,
|
|
2987
|
+
children: "Esc"
|
|
2988
|
+
}),
|
|
2989
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2990
|
+
dimColor: true,
|
|
2991
|
+
children: "/"
|
|
2992
|
+
}),
|
|
2993
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2994
|
+
bold: true,
|
|
2995
|
+
children: "Ctrl+C"
|
|
2996
|
+
}),
|
|
2997
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
2998
|
+
dimColor: true,
|
|
2999
|
+
children: [
|
|
3000
|
+
" to ",
|
|
3001
|
+
action,
|
|
3002
|
+
"."
|
|
3003
|
+
]
|
|
3004
|
+
})
|
|
3005
|
+
]
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
//#endregion
|
|
2824
3009
|
//#region src/tui.tsx
|
|
2825
3010
|
function renderApp(sessionId) {
|
|
2826
3011
|
let resetKey = 0;
|
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ var LIST$1 = [
|
|
|
37
37
|
//#endregion
|
|
38
38
|
//#region package.json
|
|
39
39
|
var name = "code-ollama";
|
|
40
|
-
var version = "0.18.
|
|
40
|
+
var version = "0.18.2";
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/constants/package.ts
|
|
43
43
|
var NAME = name;
|
|
@@ -329,7 +329,6 @@ function withSystemMessage(messages) {
|
|
|
329
329
|
//#region src/utils/config.ts
|
|
330
330
|
var CONFIG_PATH = join(DIRECTORY, "config.json");
|
|
331
331
|
var DEFAULT_HOST = "http://localhost:11434";
|
|
332
|
-
var DEFAULT_MODEL$1 = "gemma4";
|
|
333
332
|
function readFile$1() {
|
|
334
333
|
if (!existsSync(CONFIG_PATH)) return {};
|
|
335
334
|
try {
|
|
@@ -342,7 +341,7 @@ function loadConfig() {
|
|
|
342
341
|
const file = readFile$1();
|
|
343
342
|
return {
|
|
344
343
|
host: process.env.OLLAMA_HOST ?? file.host ?? DEFAULT_HOST,
|
|
345
|
-
model:
|
|
344
|
+
model: file.model,
|
|
346
345
|
searxngBaseUrl: file.searxngBaseUrl,
|
|
347
346
|
theme: file.theme ?? "github-dark"
|
|
348
347
|
};
|
|
@@ -357,9 +356,16 @@ function saveConfig(patch) {
|
|
|
357
356
|
}
|
|
358
357
|
//#endregion
|
|
359
358
|
//#region src/utils/ollama.ts
|
|
360
|
-
var { host
|
|
359
|
+
var { host } = loadConfig();
|
|
361
360
|
var client = new Ollama({ host });
|
|
362
|
-
async function
|
|
361
|
+
async function checkHealth() {
|
|
362
|
+
try {
|
|
363
|
+
return (await fetch(host)).ok;
|
|
364
|
+
} catch {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function* streamChat(messages, model, tools, signal) {
|
|
363
369
|
const response = await client.chat({
|
|
364
370
|
model,
|
|
365
371
|
messages,
|
|
@@ -1124,7 +1130,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1124
1130
|
else await launchTui();
|
|
1125
1131
|
}
|
|
1126
1132
|
async function launchTui(sessionId) {
|
|
1127
|
-
const { renderApp } = await import("./assets/tui-
|
|
1133
|
+
const { renderApp } = await import("./assets/tui-XD0Ekj5m.js");
|
|
1128
1134
|
reset();
|
|
1129
1135
|
renderApp(sessionId);
|
|
1130
1136
|
}
|
|
@@ -1140,4 +1146,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
1140
1146
|
if (isEntrypoint()) main();
|
|
1141
1147
|
// v8 ignore stop
|
|
1142
1148
|
//#endregion
|
|
1143
|
-
export {
|
|
1149
|
+
export { ASSISTANT as A, APPROVE as B, saveConfig as C, WARNING as D, HEADER_PREFIX as E, CATALOG as F, VERSION as H, AUTO as I, LABEL as L, USER as M, PLAN_GENERATION_INSTRUCTION as N, LIST as O, BACK as P, PLAN as R, loadConfig as S, withSystemMessage as T, LIST$1 as U, REJECT as V, checkHealth as _, color as a, pullModel as b, createSession as c, listSessions as d, loadSession as f, setClearHandler as g, reset as h, WRITE_TOOLS as i, SYSTEM as j, getTheme as k, deleteSession as l, clear as m, main, READ_TOOLS as n, write as o, updateSessionModel as p, TOOLS as r, appendMessage as s, executeTool as t, deleteSessionIfEmpty as u, deleteModel as v, resetSystemMessage as w, streamChat as x, listModels as y, SAFE as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "Ollama coding agent that runs in your terminal",
|
|
5
5
|
"author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@commitlint/cli": "21.0.1",
|
|
54
54
|
"@commitlint/config-conventional": "21.0.1",
|
|
55
55
|
"@eslint/js": "10.0.1",
|
|
56
|
-
"@types/node": "25.
|
|
56
|
+
"@types/node": "25.9.0",
|
|
57
57
|
"@types/react": "19.2.14",
|
|
58
58
|
"@vitest/coverage-v8": "4.1.6",
|
|
59
59
|
"eslint": "10.4.0",
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"lint-staged": "17.0.5",
|
|
66
66
|
"prettier": "3.8.3",
|
|
67
67
|
"publint": "0.3.21",
|
|
68
|
-
"tsx": "4.22.
|
|
68
|
+
"tsx": "4.22.2",
|
|
69
69
|
"typescript": "6.0.3",
|
|
70
|
-
"typescript-eslint": "8.59.
|
|
70
|
+
"typescript-eslint": "8.59.4",
|
|
71
71
|
"vite": "8.0.13",
|
|
72
72
|
"vitest": "4.1.6"
|
|
73
73
|
},
|