code-ollama 0.13.0 → 0.14.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.
- package/dist/assets/{tui-ByqNs9kx.js → tui-TX7C0xYg.js} +304 -56
- package/dist/cli.js +222 -21
- package/package.json +11 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as LABEL, C as withSystemMessage, D as USER, E as SYSTEM, F as VERSION, I as LIST, M as SAFE, N as APPROVE, O as PLAN_GENERATION_INSTRUCTION, P as REJECT, S as resetSystemMessage, T as ASSISTANT, _ as setClearHandler, a as tick, b as loadConfig, c as appendMessage, d as deleteSessionIfEmpty, f as listSessions, g as reset, h as clear, i as WRITE_TOOLS, j as PLAN, k as AUTO, l as createSession$1, m as updateSessionModel, n as READ_TOOLS, o as color, p as loadSession, r as TOOLS, s as write, t as executeTool, u as deleteSession, v as listModels, w as HEADER_PREFIX, x as saveConfig, y as streamChat } from "../cli.js";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
|
-
import { join, relative } from "node:path";
|
|
4
3
|
import { homedir } from "node:os";
|
|
4
|
+
import { join, relative } from "node:path";
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
6
|
import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
|
|
7
7
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -9,43 +9,6 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
import { Select, Spinner } from "@inkjs/ui";
|
|
10
10
|
import { marked } from "marked";
|
|
11
11
|
import { markedTerminal } from "marked-terminal";
|
|
12
|
-
//#region src/constants/command.ts
|
|
13
|
-
var LIST = [
|
|
14
|
-
{
|
|
15
|
-
name: "/clear",
|
|
16
|
-
description: "clear the current session"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: "/model",
|
|
20
|
-
description: "switch the model"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "/search",
|
|
24
|
-
description: "configure web search"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "/exit",
|
|
28
|
-
description: "exit the application"
|
|
29
|
-
}
|
|
30
|
-
];
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/constants/decision.ts
|
|
33
|
-
var APPROVE = "approve";
|
|
34
|
-
var REJECT = "reject";
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region src/constants/mode.ts
|
|
37
|
-
var SAFE = "safe";
|
|
38
|
-
var AUTO = "auto";
|
|
39
|
-
var PLAN = "plan";
|
|
40
|
-
var LABEL = {
|
|
41
|
-
safe: "Safe",
|
|
42
|
-
auto: "Auto",
|
|
43
|
-
plan: "Plan"
|
|
44
|
-
};
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/constants/ui.ts
|
|
47
|
-
var HEADER_PREFIX = "🦙 ";
|
|
48
|
-
//#endregion
|
|
49
12
|
//#region src/components/CodeBlock/CodeBlock.tsx
|
|
50
13
|
var highlightCache = /* @__PURE__ */ new Map();
|
|
51
14
|
var CODE_BLOCK_REGEX = /^(`{3,})(\w+)?[ \t]*\n([\s\S]*?)^\1[ \t]*$/gm;
|
|
@@ -113,10 +76,100 @@ var CodeBlock = memo(function CodeBlock({ code, language, role }) {
|
|
|
113
76
|
});
|
|
114
77
|
});
|
|
115
78
|
//#endregion
|
|
79
|
+
//#region src/components/Markdown/extensions.ts
|
|
80
|
+
var LATEX_COMMANDS = {
|
|
81
|
+
"\\rightarrow": "→",
|
|
82
|
+
"\\leftarrow": "←",
|
|
83
|
+
"\\Rightarrow": "⇒",
|
|
84
|
+
"\\Leftarrow": "⇐",
|
|
85
|
+
"\\leftrightarrow": "↔",
|
|
86
|
+
"\\Leftrightarrow": "⟺",
|
|
87
|
+
"\\uparrow": "↑",
|
|
88
|
+
"\\downarrow": "↓",
|
|
89
|
+
"\\to": "→",
|
|
90
|
+
"\\gets": "←",
|
|
91
|
+
"\\times": "×",
|
|
92
|
+
"\\div": "÷",
|
|
93
|
+
"\\pm": "±",
|
|
94
|
+
"\\leq": "≤",
|
|
95
|
+
"\\geq": "≥",
|
|
96
|
+
"\\neq": "≠",
|
|
97
|
+
"\\approx": "≈",
|
|
98
|
+
"\\equiv": "≡",
|
|
99
|
+
"\\infty": "∞",
|
|
100
|
+
"\\sum": "∑",
|
|
101
|
+
"\\prod": "∏",
|
|
102
|
+
"\\sqrt": "√",
|
|
103
|
+
"\\partial": "∂",
|
|
104
|
+
"\\nabla": "∇",
|
|
105
|
+
"\\in": "∈",
|
|
106
|
+
"\\notin": "∉",
|
|
107
|
+
"\\subset": "⊂",
|
|
108
|
+
"\\supset": "⊃",
|
|
109
|
+
"\\cup": "∪",
|
|
110
|
+
"\\cap": "∩",
|
|
111
|
+
"\\emptyset": "∅",
|
|
112
|
+
"\\alpha": "α",
|
|
113
|
+
"\\beta": "β",
|
|
114
|
+
"\\gamma": "γ",
|
|
115
|
+
"\\delta": "δ",
|
|
116
|
+
"\\epsilon": "ε",
|
|
117
|
+
"\\theta": "θ",
|
|
118
|
+
"\\lambda": "λ",
|
|
119
|
+
"\\mu": "μ",
|
|
120
|
+
"\\pi": "π",
|
|
121
|
+
"\\sigma": "σ",
|
|
122
|
+
"\\tau": "τ",
|
|
123
|
+
"\\phi": "φ",
|
|
124
|
+
"\\omega": "ω",
|
|
125
|
+
"\\$": "$",
|
|
126
|
+
"\\%": "%",
|
|
127
|
+
"\\&": "&",
|
|
128
|
+
"\\#": "#",
|
|
129
|
+
"\\{": "{",
|
|
130
|
+
"\\}": "}",
|
|
131
|
+
"\\^": "^",
|
|
132
|
+
"\\_": "_",
|
|
133
|
+
"\\cdot": "·",
|
|
134
|
+
"\\ldots": "…",
|
|
135
|
+
"\\cdots": "⋯"
|
|
136
|
+
};
|
|
137
|
+
function convertLatex(math) {
|
|
138
|
+
let result = math.trim();
|
|
139
|
+
for (const [cmd, unicode] of Object.entries(LATEX_COMMANDS)) result = result.replaceAll(cmd, unicode);
|
|
140
|
+
result = result.replace(/\\frac\{([^}]*)\}\{([^}]*)\}/g, "$1/$2");
|
|
141
|
+
result = result.replace(/\^\{([^}]*)\}/g, "^$1");
|
|
142
|
+
result = result.replace(/_\{([^}]*)\}/g, "_$1");
|
|
143
|
+
result = result.replace(/\\[,;!: ]/g, " ");
|
|
144
|
+
result = result.replace(/\\[a-zA-Z]+\{([^}]*)\}/g, "$1");
|
|
145
|
+
result = result.replace(/\\[a-zA-Z]+/g, "");
|
|
146
|
+
return result.trim();
|
|
147
|
+
}
|
|
148
|
+
var inlineMathExtension = {
|
|
149
|
+
name: "inlineMath",
|
|
150
|
+
level: "inline",
|
|
151
|
+
start: (src) => src.indexOf("$"),
|
|
152
|
+
tokenizer(src) {
|
|
153
|
+
const match = /^\$([^$\n]+?)\$/.exec(src);
|
|
154
|
+
if (match) return {
|
|
155
|
+
type: "inlineMath",
|
|
156
|
+
raw: match[0],
|
|
157
|
+
math: match[1]
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
renderer(token) {
|
|
161
|
+
// v8 ignore next
|
|
162
|
+
return convertLatex(token.math ?? "");
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
116
166
|
//#region src/components/Markdown/Markdown.tsx
|
|
117
167
|
var HR_PLACEHOLDER = "__CODE_OLLAMA_HR_PLACEHOLDER__";
|
|
118
168
|
marked.use(markedTerminal({ theme: "gitHub" }));
|
|
119
|
-
marked.use({
|
|
169
|
+
marked.use({
|
|
170
|
+
extensions: [inlineMathExtension],
|
|
171
|
+
renderer: { hr: () => `${HR_PLACEHOLDER}\n` }
|
|
172
|
+
});
|
|
120
173
|
function renderMarkdown(content, hrWidth) {
|
|
121
174
|
const hr = "─".repeat(Math.max(1, hrWidth));
|
|
122
175
|
try {
|
|
@@ -704,8 +757,7 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
|
|
|
704
757
|
}, [onSubmit, resetInput]);
|
|
705
758
|
const showCommandMenu = input.startsWith("/");
|
|
706
759
|
const showFileSuggestions = !showCommandMenu && hasFileSuggestionQuery(input);
|
|
707
|
-
const handleSubmitText = useCallback(
|
|
708
|
-
await tick();
|
|
760
|
+
const handleSubmitText = useCallback((input) => {
|
|
709
761
|
if (input.startsWith("/")) return;
|
|
710
762
|
if (hasFileSuggestionQuery(input)) {
|
|
711
763
|
if (fileSuggestionRef.current) handleSelectFileSuggestion(fileSuggestionRef.current);
|
|
@@ -764,22 +816,31 @@ function hasExecutablePlan(content) {
|
|
|
764
816
|
}
|
|
765
817
|
//#endregion
|
|
766
818
|
//#region src/components/Chat/Chat.tsx
|
|
767
|
-
function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
768
|
-
const
|
|
819
|
+
function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onModeChange, sessionId }) {
|
|
820
|
+
const sessionMessages = initialMessages ?? [];
|
|
821
|
+
const [messages, setMessages] = useState(sessionMessages);
|
|
769
822
|
const [streamingMessage, setStreamingMessage] = useState(null);
|
|
770
823
|
const [isLoading, setIsLoading] = useState(false);
|
|
771
824
|
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
772
825
|
const [pendingPlan, setPendingPlan] = useState(null);
|
|
773
826
|
const [interruptReason, setInterruptReason] = useState(null);
|
|
774
827
|
const abortControllerRef = useRef(null);
|
|
828
|
+
const persistedSnapshotRef = useRef("");
|
|
775
829
|
useEffect(() => {
|
|
776
|
-
setMessages(
|
|
830
|
+
setMessages(sessionMessages);
|
|
777
831
|
setStreamingMessage(null);
|
|
778
832
|
setIsLoading(false);
|
|
779
833
|
setPendingToolCall(null);
|
|
780
834
|
setPendingPlan(null);
|
|
781
835
|
setInterruptReason(null);
|
|
836
|
+
persistedSnapshotRef.current = JSON.stringify(sessionMessages);
|
|
782
837
|
}, [sessionId]);
|
|
838
|
+
useEffect(() => {
|
|
839
|
+
const snapshot = JSON.stringify(messages);
|
|
840
|
+
if (snapshot === persistedSnapshotRef.current) return;
|
|
841
|
+
persistedSnapshotRef.current = snapshot;
|
|
842
|
+
onMessagesChange?.(messages);
|
|
843
|
+
}, [messages, onMessagesChange]);
|
|
783
844
|
const buildToolResultMessage = useCallback((toolName, result) => {
|
|
784
845
|
if (result.error?.startsWith("Tool not allowed:")) return {
|
|
785
846
|
role: SYSTEM,
|
|
@@ -1362,48 +1423,224 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1362
1423
|
});
|
|
1363
1424
|
}
|
|
1364
1425
|
//#endregion
|
|
1426
|
+
//#region src/components/SessionManager.tsx
|
|
1427
|
+
var VIEW = /* @__PURE__ */ function(VIEW) {
|
|
1428
|
+
VIEW["MAIN"] = "main";
|
|
1429
|
+
VIEW["DELETE"] = "delete";
|
|
1430
|
+
return VIEW;
|
|
1431
|
+
}(VIEW || {});
|
|
1432
|
+
var ACTION = {
|
|
1433
|
+
BACK: "back",
|
|
1434
|
+
CLOSE: "close",
|
|
1435
|
+
DELETE_MENU: "delete-menu",
|
|
1436
|
+
DELETE_PREFIX: "delete:",
|
|
1437
|
+
NEW: "new",
|
|
1438
|
+
OPEN_PREFIX: "open:"
|
|
1439
|
+
};
|
|
1440
|
+
function formatSessionLabel(session) {
|
|
1441
|
+
const timestamp = new Date(session.updatedAt).toLocaleString();
|
|
1442
|
+
return `${session.title} (${timestamp})`;
|
|
1443
|
+
}
|
|
1444
|
+
function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen }) {
|
|
1445
|
+
const [view, setView] = useState(VIEW.MAIN);
|
|
1446
|
+
const [error, setError] = useState();
|
|
1447
|
+
const [, refreshSessionList] = useState(0);
|
|
1448
|
+
const sessions = listSessions();
|
|
1449
|
+
const options = view === VIEW.DELETE ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
|
|
1450
|
+
label: `Delete ${formatSessionLabel(session)}`,
|
|
1451
|
+
value: `${ACTION.DELETE_PREFIX}${session.id}`
|
|
1452
|
+
})), {
|
|
1453
|
+
label: "Back",
|
|
1454
|
+
value: ACTION.BACK
|
|
1455
|
+
}] : [
|
|
1456
|
+
{
|
|
1457
|
+
label: "Start new session",
|
|
1458
|
+
value: ACTION.NEW
|
|
1459
|
+
},
|
|
1460
|
+
...sessions.map((session) => ({
|
|
1461
|
+
label: `${session.id === currentSessionId ? "Current: " : ""}${formatSessionLabel(session)}`,
|
|
1462
|
+
value: `${ACTION.OPEN_PREFIX}${session.id}`
|
|
1463
|
+
})),
|
|
1464
|
+
{
|
|
1465
|
+
label: "Delete a session",
|
|
1466
|
+
value: ACTION.DELETE_MENU
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
label: "Close",
|
|
1470
|
+
value: ACTION.CLOSE
|
|
1471
|
+
}
|
|
1472
|
+
];
|
|
1473
|
+
const handleChange = useCallback((value) => {
|
|
1474
|
+
switch (true) {
|
|
1475
|
+
case value === ACTION.CLOSE:
|
|
1476
|
+
onClose();
|
|
1477
|
+
break;
|
|
1478
|
+
case value === ACTION.NEW:
|
|
1479
|
+
onNew();
|
|
1480
|
+
break;
|
|
1481
|
+
case value === ACTION.DELETE_MENU:
|
|
1482
|
+
setView(VIEW.DELETE);
|
|
1483
|
+
break;
|
|
1484
|
+
case value === ACTION.BACK:
|
|
1485
|
+
setView(VIEW.MAIN);
|
|
1486
|
+
break;
|
|
1487
|
+
case value.startsWith(ACTION.DELETE_PREFIX):
|
|
1488
|
+
try {
|
|
1489
|
+
onDelete(value.slice(ACTION.DELETE_PREFIX.length));
|
|
1490
|
+
setError(void 0);
|
|
1491
|
+
refreshSessionList((key) => key + 1);
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
setError(error instanceof Error ? error.message : "Failed to delete session");
|
|
1494
|
+
}
|
|
1495
|
+
break;
|
|
1496
|
+
case value.startsWith(ACTION.OPEN_PREFIX):
|
|
1497
|
+
try {
|
|
1498
|
+
onOpen(value.slice(ACTION.OPEN_PREFIX.length));
|
|
1499
|
+
setError(void 0);
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
setError(error instanceof Error ? error.message : "Failed to open session");
|
|
1502
|
+
}
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
}, [
|
|
1506
|
+
onClose,
|
|
1507
|
+
onDelete,
|
|
1508
|
+
onNew,
|
|
1509
|
+
onOpen
|
|
1510
|
+
]);
|
|
1511
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1512
|
+
flexDirection: "column",
|
|
1513
|
+
children: [
|
|
1514
|
+
/* @__PURE__ */ jsx(Text, { children: "Sessions" }),
|
|
1515
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: view === VIEW.DELETE ? "Delete session" : "Select session" }),
|
|
1516
|
+
error && /* @__PURE__ */ jsx(Box, {
|
|
1517
|
+
marginBottom: 1,
|
|
1518
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1519
|
+
color: "red",
|
|
1520
|
+
children: error
|
|
1521
|
+
})
|
|
1522
|
+
}),
|
|
1523
|
+
/* @__PURE__ */ jsx(SelectPrompt, {
|
|
1524
|
+
options,
|
|
1525
|
+
onCancel: onClose,
|
|
1526
|
+
onChange: handleChange
|
|
1527
|
+
}, `${view}:${String(sessions.length)}`)
|
|
1528
|
+
]
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
//#endregion
|
|
1365
1532
|
//#region src/components/App.tsx
|
|
1366
1533
|
var SCREEN = /* @__PURE__ */ function(SCREEN) {
|
|
1367
1534
|
SCREEN["CHAT"] = "chat";
|
|
1368
1535
|
SCREEN["MODEL_PICKER"] = "model-picker";
|
|
1369
1536
|
SCREEN["SEARCH_SETTINGS"] = "search-settings";
|
|
1537
|
+
SCREEN["SESSION_MANAGER"] = "session-manager";
|
|
1370
1538
|
return SCREEN;
|
|
1371
1539
|
}(SCREEN || {});
|
|
1372
|
-
function
|
|
1540
|
+
function createSession(sessionId, model) {
|
|
1541
|
+
return sessionId ? loadSession(sessionId) : createSession$1(model);
|
|
1542
|
+
}
|
|
1543
|
+
function App({ sessionId }) {
|
|
1373
1544
|
const { exit } = useApp();
|
|
1374
1545
|
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
1375
1546
|
const [currentScreen, setScreen] = useState(SCREEN.CHAT);
|
|
1376
1547
|
const [mode, setMode] = useState(SAFE);
|
|
1377
|
-
const [
|
|
1548
|
+
const [activeSession, setSession] = useState(() => createSession(sessionId, loadConfig().model));
|
|
1378
1549
|
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
1550
|
+
const sessionRef = useRef(activeSession);
|
|
1551
|
+
useEffect(() => {
|
|
1552
|
+
sessionRef.current = activeSession;
|
|
1553
|
+
}, [activeSession]);
|
|
1554
|
+
useEffect(() => {
|
|
1555
|
+
return () => {
|
|
1556
|
+
const currentSession = sessionRef.current;
|
|
1557
|
+
if (!deleteSessionIfEmpty(currentSession.metadata.id) && currentSession.messages.length > 0) write(`Resume session: ${color(`code-ollama resume ${currentSession.metadata.id}`, "cyan")}\n`);
|
|
1558
|
+
};
|
|
1559
|
+
}, []);
|
|
1560
|
+
const setActiveSession = useCallback((nextSession) => {
|
|
1561
|
+
setSession((current) => {
|
|
1562
|
+
deleteSessionIfEmpty(current.metadata.id);
|
|
1563
|
+
return nextSession;
|
|
1564
|
+
});
|
|
1565
|
+
}, []);
|
|
1379
1566
|
const handleHeaderLoad = useCallback(() => {
|
|
1380
1567
|
setIsHeaderLoaded(true);
|
|
1381
1568
|
}, []);
|
|
1569
|
+
const handleCreateSession = useCallback(() => {
|
|
1570
|
+
const nextSession = createSession$1(appConfig.model);
|
|
1571
|
+
setActiveSession(nextSession);
|
|
1572
|
+
setScreen(SCREEN.CHAT);
|
|
1573
|
+
clear(nextSession.metadata.id);
|
|
1574
|
+
return nextSession;
|
|
1575
|
+
}, [appConfig.model, setActiveSession]);
|
|
1576
|
+
const handleOpenSession = useCallback((sessionId) => {
|
|
1577
|
+
if (sessionRef.current.metadata.id === sessionId) {
|
|
1578
|
+
setScreen(SCREEN.CHAT);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
setActiveSession(loadSession(sessionId));
|
|
1582
|
+
setScreen(SCREEN.CHAT);
|
|
1583
|
+
clear(sessionId);
|
|
1584
|
+
}, [setActiveSession]);
|
|
1585
|
+
const handleDeleteSession = useCallback((sessionId) => {
|
|
1586
|
+
deleteSession(sessionId);
|
|
1587
|
+
setSession((current) => {
|
|
1588
|
+
if (current.metadata.id !== sessionId) return current;
|
|
1589
|
+
return createSession$1(appConfig.model);
|
|
1590
|
+
});
|
|
1591
|
+
setScreen(SCREEN.SESSION_MANAGER);
|
|
1592
|
+
}, [appConfig.model]);
|
|
1593
|
+
const handleMessagesChange = useCallback((messages) => {
|
|
1594
|
+
setSession((current) => {
|
|
1595
|
+
const persistedMessages = messages.filter(({ content }) => content !== TURN_ABORTED_MESSAGE);
|
|
1596
|
+
if (persistedMessages.length <= current.messages.length) return current;
|
|
1597
|
+
let metadata = current.metadata;
|
|
1598
|
+
for (const message of persistedMessages.slice(current.messages.length)) metadata = appendMessage(metadata.id, message, appConfig.model);
|
|
1599
|
+
return {
|
|
1600
|
+
metadata,
|
|
1601
|
+
messages: persistedMessages
|
|
1602
|
+
};
|
|
1603
|
+
});
|
|
1604
|
+
}, [appConfig.model]);
|
|
1382
1605
|
const handleCommand = useCallback((command) => {
|
|
1383
1606
|
switch (command) {
|
|
1607
|
+
case "/session":
|
|
1608
|
+
setScreen(SCREEN.SESSION_MANAGER);
|
|
1609
|
+
break;
|
|
1384
1610
|
case "/model":
|
|
1385
1611
|
setScreen(SCREEN.MODEL_PICKER);
|
|
1386
1612
|
break;
|
|
1387
1613
|
case "/search":
|
|
1388
1614
|
setScreen(SCREEN.SEARCH_SETTINGS);
|
|
1389
1615
|
break;
|
|
1390
|
-
case "/clear":
|
|
1616
|
+
case "/clear": {
|
|
1391
1617
|
resetSystemMessage();
|
|
1392
|
-
clear();
|
|
1393
1618
|
setScreen(SCREEN.CHAT);
|
|
1394
|
-
|
|
1619
|
+
const nextSession = createSession$1(appConfig.model);
|
|
1620
|
+
setActiveSession(nextSession);
|
|
1621
|
+
clear(nextSession.metadata.id);
|
|
1395
1622
|
break;
|
|
1623
|
+
}
|
|
1396
1624
|
case "/exit":
|
|
1397
1625
|
exit();
|
|
1398
1626
|
break;
|
|
1399
1627
|
}
|
|
1400
|
-
}, [
|
|
1628
|
+
}, [
|
|
1629
|
+
appConfig.model,
|
|
1630
|
+
exit,
|
|
1631
|
+
setActiveSession
|
|
1632
|
+
]);
|
|
1401
1633
|
const handleUpdateConfig = useCallback((update) => {
|
|
1402
1634
|
setConfig((current) => ({
|
|
1403
1635
|
...current,
|
|
1404
1636
|
...update
|
|
1405
1637
|
}));
|
|
1406
1638
|
saveConfig(update);
|
|
1639
|
+
const newModel = update.model;
|
|
1640
|
+
if (newModel) setSession((current) => ({
|
|
1641
|
+
...current,
|
|
1642
|
+
metadata: updateSessionModel(current.metadata.id, newModel)
|
|
1643
|
+
}));
|
|
1407
1644
|
setScreen(SCREEN.CHAT);
|
|
1408
1645
|
}, []);
|
|
1409
1646
|
const handleClose = useCallback(() => {
|
|
@@ -1435,13 +1672,24 @@ function App() {
|
|
|
1435
1672
|
onClose: handleClose
|
|
1436
1673
|
});
|
|
1437
1674
|
break;
|
|
1675
|
+
case SCREEN.SESSION_MANAGER:
|
|
1676
|
+
screenContent = /* @__PURE__ */ jsx(SessionManager, {
|
|
1677
|
+
currentSessionId: activeSession.metadata.id,
|
|
1678
|
+
onClose: handleClose,
|
|
1679
|
+
onDelete: handleDeleteSession,
|
|
1680
|
+
onNew: handleCreateSession,
|
|
1681
|
+
onOpen: handleOpenSession
|
|
1682
|
+
});
|
|
1683
|
+
break;
|
|
1438
1684
|
case SCREEN.CHAT:
|
|
1439
1685
|
screenContent = /* @__PURE__ */ jsx(Chat, {
|
|
1686
|
+
initialMessages: activeSession.messages,
|
|
1440
1687
|
model: appConfig.model,
|
|
1441
1688
|
onCommand: handleCommand,
|
|
1689
|
+
onMessagesChange: handleMessagesChange,
|
|
1442
1690
|
mode,
|
|
1443
1691
|
onModeChange: setMode,
|
|
1444
|
-
sessionId
|
|
1692
|
+
sessionId: activeSession.metadata.id
|
|
1445
1693
|
});
|
|
1446
1694
|
break;
|
|
1447
1695
|
}
|
|
@@ -1463,15 +1711,15 @@ function App() {
|
|
|
1463
1711
|
}
|
|
1464
1712
|
//#endregion
|
|
1465
1713
|
//#region src/tui.tsx
|
|
1466
|
-
function renderApp() {
|
|
1714
|
+
function renderApp(sessionId) {
|
|
1467
1715
|
let resetKey = 0;
|
|
1468
|
-
const app = render(/* @__PURE__ */ jsx(App, {}, resetKey), {
|
|
1716
|
+
const app = render(/* @__PURE__ */ jsx(App, { sessionId }, resetKey), {
|
|
1469
1717
|
exitOnCtrlC: false,
|
|
1470
1718
|
maxFps: 60
|
|
1471
1719
|
});
|
|
1472
|
-
setClearHandler(() => {
|
|
1720
|
+
setClearHandler((nextSessionId) => {
|
|
1473
1721
|
reset();
|
|
1474
|
-
app.rerender(/* @__PURE__ */ jsx(App, {}, ++resetKey));
|
|
1722
|
+
app.rerender(/* @__PURE__ */ jsx(App, { sessionId: nextSessionId ?? sessionId }, ++resetKey));
|
|
1475
1723
|
});
|
|
1476
1724
|
}
|
|
1477
1725
|
//#endregion
|
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,60 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as runShell } from "./assets/shell-CipXM_WI.js";
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import cac from "cac";
|
|
5
|
-
import { join } from "node:path";
|
|
6
5
|
import { homedir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
7
|
import { Ollama } from "ollama";
|
|
8
|
+
import { v7 } from "uuid";
|
|
9
|
+
//#region src/constants/command.ts
|
|
10
|
+
var LIST = [
|
|
11
|
+
{
|
|
12
|
+
name: "/clear",
|
|
13
|
+
description: "clear the current session"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "/session",
|
|
17
|
+
description: "manage sessions"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "/model",
|
|
21
|
+
description: "switch the model"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "/search",
|
|
25
|
+
description: "configure web search"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "/exit",
|
|
29
|
+
description: "exit the application"
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
//#endregion
|
|
8
33
|
//#region package.json
|
|
9
34
|
var name = "code-ollama";
|
|
10
|
-
var version = "0.
|
|
35
|
+
var version = "0.14.0";
|
|
11
36
|
//#endregion
|
|
12
37
|
//#region src/constants/package.ts
|
|
13
38
|
var NAME = name;
|
|
14
39
|
var VERSION = version;
|
|
15
40
|
//#endregion
|
|
41
|
+
//#region src/constants/config.ts
|
|
42
|
+
var DIRECTORY = join(homedir(), `.${NAME}`);
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/constants/decision.ts
|
|
45
|
+
var APPROVE = "approve";
|
|
46
|
+
var REJECT = "reject";
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/constants/mode.ts
|
|
49
|
+
var SAFE = "safe";
|
|
50
|
+
var AUTO = "auto";
|
|
51
|
+
var PLAN = "plan";
|
|
52
|
+
var LABEL = {
|
|
53
|
+
safe: "Safe",
|
|
54
|
+
auto: "Auto",
|
|
55
|
+
plan: "Plan"
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
16
58
|
//#region src/constants/prompt.ts
|
|
17
59
|
var BASE_SYSTEM_PROMPT = `You are a coding assistant that helps users write, edit, and understand code. You have access to tools for reading files, writing files, running shell commands, searching code, and searching the web
|
|
18
60
|
|
|
@@ -70,6 +112,9 @@ var VIEW_RANGE = "view_range";
|
|
|
70
112
|
var WEB_SEARCH = "web_search";
|
|
71
113
|
var WEB_FETCH = "web_fetch";
|
|
72
114
|
//#endregion
|
|
115
|
+
//#region src/constants/ui.ts
|
|
116
|
+
var HEADER_PREFIX = "🦙 ";
|
|
117
|
+
//#endregion
|
|
73
118
|
//#region src/utils/agents.ts
|
|
74
119
|
var AGENTS_FILE = "AGENTS.md";
|
|
75
120
|
function loadAgentsContent() {
|
|
@@ -104,8 +149,7 @@ function withSystemMessage(messages) {
|
|
|
104
149
|
}
|
|
105
150
|
//#endregion
|
|
106
151
|
//#region src/utils/config.ts
|
|
107
|
-
var
|
|
108
|
-
var CONFIG_PATH = join(CONFIG_DIRECTORY, "config.json");
|
|
152
|
+
var CONFIG_PATH = join(DIRECTORY, "config.json");
|
|
109
153
|
var DEFAULT_HOST = "http://localhost:11434";
|
|
110
154
|
var DEFAULT_MODEL$1 = "gemma4";
|
|
111
155
|
function readFile$1() {
|
|
@@ -129,7 +173,7 @@ function saveConfig(patch) {
|
|
|
129
173
|
...readFile$1(),
|
|
130
174
|
...patch
|
|
131
175
|
};
|
|
132
|
-
mkdirSync(
|
|
176
|
+
mkdirSync(DIRECTORY, { recursive: true });
|
|
133
177
|
writeFileSync(CONFIG_PATH, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
134
178
|
}
|
|
135
179
|
//#endregion
|
|
@@ -177,8 +221,8 @@ function setClearHandler(handler) {
|
|
|
177
221
|
/**
|
|
178
222
|
* Clear the screen with Ink.
|
|
179
223
|
*/
|
|
180
|
-
function clear() {
|
|
181
|
-
clearHandler?.();
|
|
224
|
+
function clear(sessionId) {
|
|
225
|
+
clearHandler?.(sessionId);
|
|
182
226
|
}
|
|
183
227
|
/**
|
|
184
228
|
* Reset the screen with ANSI escape sequence.
|
|
@@ -187,6 +231,156 @@ function reset() {
|
|
|
187
231
|
process.stdout.write("\x1Bc\x1B[?25l");
|
|
188
232
|
}
|
|
189
233
|
//#endregion
|
|
234
|
+
//#region src/utils/session.ts
|
|
235
|
+
var SESSIONS_DIRECTORY = join(DIRECTORY, "sessions");
|
|
236
|
+
var METADATA_FILE_NAME = "metadata.json";
|
|
237
|
+
var MESSAGES_FILE_NAME = "messages.jsonl";
|
|
238
|
+
var DEFAULT_TITLE = "New session";
|
|
239
|
+
var TITLE_MAX_LENGTH = 80;
|
|
240
|
+
function getSessionDirectory(id) {
|
|
241
|
+
return join(SESSIONS_DIRECTORY, id);
|
|
242
|
+
}
|
|
243
|
+
function getMetadataPath(id) {
|
|
244
|
+
return join(getSessionDirectory(id), METADATA_FILE_NAME);
|
|
245
|
+
}
|
|
246
|
+
function getMessagesPath(id) {
|
|
247
|
+
return join(getSessionDirectory(id), MESSAGES_FILE_NAME);
|
|
248
|
+
}
|
|
249
|
+
function ensureSessionsDirectory() {
|
|
250
|
+
mkdirSync(SESSIONS_DIRECTORY, { recursive: true });
|
|
251
|
+
}
|
|
252
|
+
function ensureSessionDirectory(id) {
|
|
253
|
+
const directory = getSessionDirectory(id);
|
|
254
|
+
mkdirSync(directory, { recursive: true });
|
|
255
|
+
return directory;
|
|
256
|
+
}
|
|
257
|
+
function readMetadata(id) {
|
|
258
|
+
const path = getMetadataPath(id);
|
|
259
|
+
if (!existsSync(path)) throw new Error(`Session not found: ${id}`);
|
|
260
|
+
try {
|
|
261
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
262
|
+
} catch {
|
|
263
|
+
throw new Error(`Invalid session metadata: ${id}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function writeMetadata(metadata) {
|
|
267
|
+
ensureSessionDirectory(metadata.id);
|
|
268
|
+
writeFileSync(getMetadataPath(metadata.id), JSON.stringify(metadata, null, 2) + "\n", "utf8");
|
|
269
|
+
}
|
|
270
|
+
function readMessages(id) {
|
|
271
|
+
const path = getMessagesPath(id);
|
|
272
|
+
if (!existsSync(path)) return [];
|
|
273
|
+
const content = readFileSync(path, "utf8").trim();
|
|
274
|
+
if (!content) return [];
|
|
275
|
+
try {
|
|
276
|
+
return content.split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
277
|
+
} catch {
|
|
278
|
+
throw new Error(`Invalid session messages: ${id}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function deriveTitle(message) {
|
|
282
|
+
// v8 ignore next - title derivation is only reached for user messages
|
|
283
|
+
if (message.role !== "user") return DEFAULT_TITLE;
|
|
284
|
+
const normalized = message.content.replace(/\s+/g, " ").trim();
|
|
285
|
+
if (!normalized) return DEFAULT_TITLE;
|
|
286
|
+
return normalized.length > TITLE_MAX_LENGTH ? normalized.slice(0, TITLE_MAX_LENGTH - 1).trimEnd() + "…" : normalized;
|
|
287
|
+
}
|
|
288
|
+
function updateTitle(metadata, message) {
|
|
289
|
+
if (metadata.title !== DEFAULT_TITLE || message.role !== "user") return metadata;
|
|
290
|
+
return {
|
|
291
|
+
...metadata,
|
|
292
|
+
title: deriveTitle(message)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function createSession(model) {
|
|
296
|
+
ensureSessionsDirectory();
|
|
297
|
+
const id = v7();
|
|
298
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
|
+
const metadata = {
|
|
300
|
+
id,
|
|
301
|
+
createdAt: now,
|
|
302
|
+
updatedAt: now,
|
|
303
|
+
title: DEFAULT_TITLE,
|
|
304
|
+
model
|
|
305
|
+
};
|
|
306
|
+
ensureSessionDirectory(id);
|
|
307
|
+
writeMetadata(metadata);
|
|
308
|
+
writeFileSync(getMessagesPath(id), "", "utf8");
|
|
309
|
+
return {
|
|
310
|
+
metadata,
|
|
311
|
+
messages: []
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function listSessions() {
|
|
315
|
+
if (!existsSync(SESSIONS_DIRECTORY)) return [];
|
|
316
|
+
return readdirSync(SESSIONS_DIRECTORY, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
317
|
+
try {
|
|
318
|
+
return [readMetadata(entry.name)];
|
|
319
|
+
} catch {
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
}).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
323
|
+
}
|
|
324
|
+
function loadSession(id) {
|
|
325
|
+
return {
|
|
326
|
+
metadata: readMetadata(id),
|
|
327
|
+
messages: readMessages(id)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function appendMessage(id, message, model) {
|
|
331
|
+
ensureSessionDirectory(id);
|
|
332
|
+
let metadata = readMetadata(id);
|
|
333
|
+
metadata = updateTitle(metadata, message);
|
|
334
|
+
metadata = {
|
|
335
|
+
...metadata,
|
|
336
|
+
model,
|
|
337
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
338
|
+
};
|
|
339
|
+
appendFileSync(getMessagesPath(id), JSON.stringify(message) + "\n", "utf8");
|
|
340
|
+
writeMetadata(metadata);
|
|
341
|
+
return metadata;
|
|
342
|
+
}
|
|
343
|
+
function updateSessionModel(id, model) {
|
|
344
|
+
const metadata = {
|
|
345
|
+
...readMetadata(id),
|
|
346
|
+
model
|
|
347
|
+
};
|
|
348
|
+
writeMetadata(metadata);
|
|
349
|
+
return metadata;
|
|
350
|
+
}
|
|
351
|
+
function deleteSessionIfEmpty(id) {
|
|
352
|
+
const directory = getSessionDirectory(id);
|
|
353
|
+
if (!existsSync(directory)) return false;
|
|
354
|
+
const messagesPath = getMessagesPath(id);
|
|
355
|
+
if (existsSync(messagesPath) && readFileSync(messagesPath, "utf8").trim() !== "") return false;
|
|
356
|
+
rmSync(directory, {
|
|
357
|
+
recursive: true,
|
|
358
|
+
force: false
|
|
359
|
+
});
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
function deleteSession(id) {
|
|
363
|
+
const directory = getSessionDirectory(id);
|
|
364
|
+
if (!existsSync(directory)) throw new Error(`Session not found: ${id}`);
|
|
365
|
+
rmSync(directory, {
|
|
366
|
+
recursive: true,
|
|
367
|
+
force: false
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/utils/terminal.ts
|
|
372
|
+
var ANSI_COLOR = { cyan: ["\x1B[36m", "\x1B[39m"] };
|
|
373
|
+
function color(text, name) {
|
|
374
|
+
const [open, close] = ANSI_COLOR[name];
|
|
375
|
+
return `${open}${text}${close}`;
|
|
376
|
+
}
|
|
377
|
+
function write(text) {
|
|
378
|
+
process.stdout.write(text);
|
|
379
|
+
}
|
|
380
|
+
function writeError(text) {
|
|
381
|
+
process.stderr.write(text);
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
190
384
|
//#region src/utils/time.ts
|
|
191
385
|
var tick = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
192
386
|
//#endregion
|
|
@@ -655,9 +849,16 @@ cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model,
|
|
|
655
849
|
try {
|
|
656
850
|
await runPrompt(model, prompt);
|
|
657
851
|
} catch (error) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
852
|
+
writeError(`Error: ${error instanceof Error ? error.message : "Unknown error"}\n`);
|
|
853
|
+
process.exitCode = 1;
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
cli.command("resume <sessionId>", "Resume a saved session").action(async (sessionId) => {
|
|
857
|
+
try {
|
|
858
|
+
loadSession(sessionId);
|
|
859
|
+
await launchTui(sessionId);
|
|
860
|
+
} catch (error) {
|
|
861
|
+
writeError(`Error: ${error instanceof Error ? error.message : "Unknown error"}\n`);
|
|
661
862
|
process.exitCode = 1;
|
|
662
863
|
}
|
|
663
864
|
});
|
|
@@ -666,7 +867,7 @@ async function runPrompt(model, prompt) {
|
|
|
666
867
|
role: USER,
|
|
667
868
|
content: prompt
|
|
668
869
|
}], model);
|
|
669
|
-
|
|
870
|
+
write("\n");
|
|
670
871
|
}
|
|
671
872
|
async function processRunStream(messages, model) {
|
|
672
873
|
const assistantMessage = {
|
|
@@ -676,7 +877,7 @@ async function processRunStream(messages, model) {
|
|
|
676
877
|
for await (const chunk of streamChat(messages, model, TOOLS)) {
|
|
677
878
|
if (chunk.type === "content") {
|
|
678
879
|
assistantMessage.content += chunk.content;
|
|
679
|
-
|
|
880
|
+
write(chunk.content);
|
|
680
881
|
continue;
|
|
681
882
|
}
|
|
682
883
|
for (const toolCall of chunk.tool_calls) {
|
|
@@ -695,17 +896,17 @@ async function processRunStream(messages, model) {
|
|
|
695
896
|
}
|
|
696
897
|
}
|
|
697
898
|
async function main(args = process.argv.slice(2)) {
|
|
698
|
-
if (
|
|
699
|
-
const { renderApp } = await import("./assets/tui-ByqNs9kx.js");
|
|
700
|
-
reset();
|
|
701
|
-
renderApp();
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
cli.parse([
|
|
899
|
+
if (args.length) cli.parse([
|
|
705
900
|
"node",
|
|
706
901
|
"code-ollama",
|
|
707
902
|
...args
|
|
708
903
|
]);
|
|
904
|
+
else await launchTui();
|
|
905
|
+
}
|
|
906
|
+
async function launchTui(sessionId) {
|
|
907
|
+
const { renderApp } = await import("./assets/tui-TX7C0xYg.js");
|
|
908
|
+
reset();
|
|
909
|
+
renderApp(sessionId);
|
|
709
910
|
}
|
|
710
911
|
// v8 ignore start
|
|
711
912
|
function isEntrypoint(argv1 = process.argv[1]) {
|
|
@@ -719,4 +920,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
719
920
|
if (isEntrypoint()) main();
|
|
720
921
|
// v8 ignore stop
|
|
721
922
|
//#endregion
|
|
722
|
-
export { USER as _, tick as a,
|
|
923
|
+
export { LABEL as A, withSystemMessage as C, USER as D, SYSTEM as E, VERSION as F, LIST as I, SAFE as M, APPROVE as N, PLAN_GENERATION_INSTRUCTION as O, REJECT as P, resetSystemMessage as S, ASSISTANT as T, setClearHandler as _, tick as a, loadConfig as b, appendMessage as c, deleteSessionIfEmpty as d, listSessions as f, reset as g, clear as h, WRITE_TOOLS as i, PLAN as j, AUTO as k, createSession as l, updateSessionModel as m, main, READ_TOOLS as n, color as o, loadSession as p, TOOLS as r, write as s, executeTool as t, deleteSession as u, listModels as v, HEADER_PREFIX as w, saveConfig as x, streamChat as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
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",
|
|
@@ -46,16 +46,17 @@
|
|
|
46
46
|
"marked": "15.0.12",
|
|
47
47
|
"marked-terminal": "7.3.0",
|
|
48
48
|
"ollama": "0.6.3",
|
|
49
|
-
"react": "19.2.6"
|
|
49
|
+
"react": "19.2.6",
|
|
50
|
+
"uuid": "14.0.0"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
|
-
"@commitlint/cli": "21.0.
|
|
53
|
-
"@commitlint/config-conventional": "21.0.
|
|
53
|
+
"@commitlint/cli": "21.0.1",
|
|
54
|
+
"@commitlint/config-conventional": "21.0.1",
|
|
54
55
|
"@eslint/config-helpers": "0.6.0",
|
|
55
56
|
"@eslint/js": "10.0.1",
|
|
56
|
-
"@types/node": "25.
|
|
57
|
+
"@types/node": "25.7.0",
|
|
57
58
|
"@types/react": "19.2.14",
|
|
58
|
-
"@vitest/coverage-v8": "4.1.
|
|
59
|
+
"@vitest/coverage-v8": "4.1.6",
|
|
59
60
|
"eslint": "10.3.0",
|
|
60
61
|
"eslint-plugin-prettier": "5.5.5",
|
|
61
62
|
"eslint-plugin-simple-import-sort": "13.0.0",
|
|
@@ -64,12 +65,12 @@
|
|
|
64
65
|
"ink-testing-library": "4.0.0",
|
|
65
66
|
"lint-staged": "17.0.4",
|
|
66
67
|
"prettier": "3.8.3",
|
|
67
|
-
"publint": "0.3.
|
|
68
|
+
"publint": "0.3.21",
|
|
68
69
|
"tsx": "4.21.0",
|
|
69
70
|
"typescript": "6.0.3",
|
|
70
|
-
"typescript-eslint": "8.59.
|
|
71
|
-
"vite": "8.0.
|
|
72
|
-
"vitest": "4.1.
|
|
71
|
+
"typescript-eslint": "8.59.3",
|
|
72
|
+
"vite": "8.0.12",
|
|
73
|
+
"vitest": "4.1.6"
|
|
73
74
|
},
|
|
74
75
|
"files": [
|
|
75
76
|
"dist/"
|