codemini-cli 0.3.3 → 0.3.4
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/package.json +1 -3
- package/src/core/provider/openai-compatible.js +61 -16
- package/src/tui/chat-app.js +10 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codemini-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -47,9 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@cursorless/tree-sitter-wasms": "^0.5.0",
|
|
50
|
-
"@anthropic-ai/sdk": "^0.82.0",
|
|
51
50
|
"ink": "^6.3.1",
|
|
52
|
-
"openai": "^6.33.0",
|
|
53
51
|
"react": "^19.0.0",
|
|
54
52
|
"web-tree-sitter": "^0.26.7"
|
|
55
53
|
},
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
2
|
-
|
|
3
1
|
function extractTextContent(content) {
|
|
4
2
|
if (typeof content === 'string') return content;
|
|
5
3
|
if (Array.isArray(content)) {
|
|
@@ -23,13 +21,49 @@ function emptyToolCall(index) {
|
|
|
23
21
|
};
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
function
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
function createHeaders(apiKey) {
|
|
25
|
+
return {
|
|
26
|
+
'content-type': 'application/json',
|
|
27
|
+
authorization: `Bearer ${apiKey}`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildChatCompletionsUrl(baseUrl) {
|
|
32
|
+
return `${String(baseUrl || '').replace(/\/$/, '')}/chat/completions`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function parseJsonResponse(response) {
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const text = await response.text().catch(() => '');
|
|
38
|
+
throw new Error(`Gateway error ${response.status}: ${text || response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
return response.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function* iterateSseEvents(stream) {
|
|
44
|
+
const decoder = new TextDecoder();
|
|
45
|
+
let buffer = '';
|
|
46
|
+
|
|
47
|
+
for await (const chunk of stream) {
|
|
48
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
49
|
+
while (true) {
|
|
50
|
+
const lfBoundary = buffer.indexOf('\n\n');
|
|
51
|
+
const crlfBoundary = buffer.indexOf('\r\n\r\n');
|
|
52
|
+
if (lfBoundary === -1 && crlfBoundary === -1) break;
|
|
53
|
+
const useCrlf = crlfBoundary !== -1 && (lfBoundary === -1 || crlfBoundary < lfBoundary);
|
|
54
|
+
const boundary = useCrlf ? crlfBoundary : lfBoundary;
|
|
55
|
+
const separatorLength = useCrlf ? 4 : 2;
|
|
56
|
+
const rawEvent = buffer.slice(0, boundary);
|
|
57
|
+
buffer = buffer.slice(boundary + separatorLength);
|
|
58
|
+
const dataLines = rawEvent
|
|
59
|
+
.split(/\r?\n/)
|
|
60
|
+
.filter((line) => line.startsWith('data:'))
|
|
61
|
+
.map((line) => line.slice(5).trimStart());
|
|
62
|
+
const dataText = dataLines.join('\n');
|
|
63
|
+
if (!dataText || dataText === '[DONE]') continue;
|
|
64
|
+
yield JSON.parse(dataText);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
33
67
|
}
|
|
34
68
|
|
|
35
69
|
function isMiniMaxModel(model) {
|
|
@@ -238,10 +272,14 @@ export async function createChatCompletion({
|
|
|
238
272
|
timeoutMs = 90000,
|
|
239
273
|
maxRetries = 2
|
|
240
274
|
}) {
|
|
241
|
-
const client = createClient({ baseUrl, apiKey, timeoutMs, maxRetries });
|
|
242
275
|
const payload = buildPayload({ model, temperature, messages, tools });
|
|
243
|
-
|
|
244
|
-
|
|
276
|
+
const response = await fetch(buildChatCompletionsUrl(baseUrl), {
|
|
277
|
+
method: 'POST',
|
|
278
|
+
headers: createHeaders(apiKey),
|
|
279
|
+
body: JSON.stringify(payload),
|
|
280
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
281
|
+
});
|
|
282
|
+
const data = await parseJsonResponse(response);
|
|
245
283
|
const message = data?.choices?.[0]?.message || {};
|
|
246
284
|
const text = sanitizeMiniMaxText(model, extractTextContent(message.content));
|
|
247
285
|
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
@@ -282,16 +320,23 @@ export async function createChatCompletionStream({
|
|
|
282
320
|
timeoutMs = 90000,
|
|
283
321
|
maxRetries = 2
|
|
284
322
|
}) {
|
|
285
|
-
const client = createClient({ baseUrl, apiKey, timeoutMs, maxRetries });
|
|
286
323
|
const payload = buildPayload({ model, temperature, messages, tools, stream: true });
|
|
287
|
-
|
|
288
|
-
|
|
324
|
+
const response = await fetch(buildChatCompletionsUrl(baseUrl), {
|
|
325
|
+
method: 'POST',
|
|
326
|
+
headers: createHeaders(apiKey),
|
|
327
|
+
body: JSON.stringify(payload),
|
|
328
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
329
|
+
});
|
|
330
|
+
if (!response.ok || !response.body) {
|
|
331
|
+
const text = await response.text().catch(() => '');
|
|
332
|
+
throw new Error(`Gateway error ${response.status}: ${text || response.statusText}`);
|
|
333
|
+
}
|
|
289
334
|
let text = '';
|
|
290
335
|
const toolCallsByIndex = new Map();
|
|
291
336
|
let usage = null;
|
|
292
337
|
let miniMaxStreamState = { rawContent: '', visibleText: '' };
|
|
293
338
|
|
|
294
|
-
for await (const chunk of
|
|
339
|
+
for await (const chunk of iterateSseEvents(response.body)) {
|
|
295
340
|
usage = chunk?.usage || usage;
|
|
296
341
|
const choice0 = chunk?.choices?.[0] || {};
|
|
297
342
|
const delta = choice0?.delta || {};
|
package/src/tui/chat-app.js
CHANGED
|
@@ -1954,7 +1954,7 @@ export function collapseActivityChainRows(inputRows, showToolDetails, copy, maxV
|
|
|
1954
1954
|
return collapsed;
|
|
1955
1955
|
}
|
|
1956
1956
|
|
|
1957
|
-
function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
1957
|
+
export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
1958
1958
|
const rows = [];
|
|
1959
1959
|
const pushTextRows = (text) => {
|
|
1960
1960
|
const lines = String(text || '').split('\n');
|
|
@@ -2071,26 +2071,26 @@ function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
|
2071
2071
|
|
|
2072
2072
|
const codeGenerationRows = getCodeGenerationActivityRows(msg);
|
|
2073
2073
|
const generatingCodeRows = getGeneratingCodePlaceholderRows(msg, copy, contentWidth);
|
|
2074
|
-
const
|
|
2074
|
+
const trailingRows = [];
|
|
2075
2075
|
if (msg?.loading && (msg?.liveStatus || msg?.phase)) {
|
|
2076
|
-
const statusRows = [];
|
|
2077
2076
|
pushWrappedRow(
|
|
2078
|
-
|
|
2077
|
+
trailingRows,
|
|
2079
2078
|
{
|
|
2080
2079
|
kind: 'status',
|
|
2081
2080
|
text: trimText(msg.liveStatus || msg.phase, 144)
|
|
2082
2081
|
},
|
|
2083
2082
|
Math.max(8, contentWidth - 2)
|
|
2084
2083
|
);
|
|
2085
|
-
syntheticRows.push(...statusRows);
|
|
2086
2084
|
}
|
|
2087
2085
|
|
|
2088
|
-
|
|
2089
|
-
|
|
2086
|
+
const rowsWithCodePreview = insertRowsAfterLastCodeRow(
|
|
2087
|
+
collapseActivityChainRows(rows, showToolDetails, copy),
|
|
2088
|
+
[...codeGenerationRows, ...generatingCodeRows]
|
|
2090
2089
|
);
|
|
2090
|
+
return normalizeActivitySpacingRows([...rowsWithCodePreview, ...trailingRows]);
|
|
2091
2091
|
}
|
|
2092
2092
|
|
|
2093
|
-
function renderMessageRow(msg, row, idx, loaderTick) {
|
|
2093
|
+
export function renderMessageRow(msg, row, idx, loaderTick) {
|
|
2094
2094
|
if (row.kind === 'activity') {
|
|
2095
2095
|
const activity = { type: row.activityType, name: row.name, status: row.status };
|
|
2096
2096
|
const display = getActivityDisplayParts(activity);
|
|
@@ -2139,7 +2139,7 @@ function renderMessageRow(msg, row, idx, loaderTick) {
|
|
|
2139
2139
|
const dimColor = row.status === 'completed';
|
|
2140
2140
|
return h(
|
|
2141
2141
|
Box,
|
|
2142
|
-
{ key: `row-todo-${msg.id}-${idx}`, marginLeft: 2
|
|
2142
|
+
{ key: `row-todo-${msg.id}-${idx}`, marginLeft: 2 },
|
|
2143
2143
|
h(Text, { color: 'gray' }, ' '),
|
|
2144
2144
|
h(Text, { color }, marker),
|
|
2145
2145
|
h(Text, { color: 'gray' }, ' '),
|
|
@@ -2203,22 +2203,11 @@ function renderMessageRow(msg, row, idx, loaderTick) {
|
|
|
2203
2203
|
}
|
|
2204
2204
|
if (row.kind === 'status') {
|
|
2205
2205
|
const dots = '.'.repeat((loaderTick % 3) + 1);
|
|
2206
|
-
const phase = msg.phase;
|
|
2207
|
-
const color =
|
|
2208
|
-
phase === 'sending'
|
|
2209
|
-
? 'yellowBright'
|
|
2210
|
-
: phase === 'queued'
|
|
2211
|
-
? 'cyanBright'
|
|
2212
|
-
: phase === 'tooling'
|
|
2213
|
-
? 'magentaBright'
|
|
2214
|
-
: phase === 'generating'
|
|
2215
|
-
? 'greenBright'
|
|
2216
|
-
: 'cyanBright';
|
|
2217
2206
|
return h(
|
|
2218
2207
|
Box,
|
|
2219
2208
|
{ key: `row-status-${msg.id}-${idx}`, marginTop: 1 },
|
|
2220
2209
|
h(Text, { color: 'gray' }, ' '),
|
|
2221
|
-
h(Text, { color }, `${row.text}${dots}`)
|
|
2210
|
+
h(Text, { color: 'gray', dimColor: true }, `${row.text}${dots}`)
|
|
2222
2211
|
);
|
|
2223
2212
|
}
|
|
2224
2213
|
if (row.kind === 'quote') {
|