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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemini-cli",
3
- "version": "0.3.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 createClient({ baseUrl, apiKey, timeoutMs, maxRetries }) {
27
- return new OpenAI({
28
- apiKey,
29
- baseURL: baseUrl,
30
- timeout: timeoutMs,
31
- maxRetries
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
- const data = await client.chat.completions.create(payload);
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
- const stream = await client.chat.completions.create(payload);
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 stream) {
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 || {};
@@ -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 syntheticRows = [...codeGenerationRows, ...generatingCodeRows];
2074
+ const trailingRows = [];
2075
2075
  if (msg?.loading && (msg?.liveStatus || msg?.phase)) {
2076
- const statusRows = [];
2077
2076
  pushWrappedRow(
2078
- statusRows,
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
- return normalizeActivitySpacingRows(
2089
- insertRowsAfterLastCodeRow(collapseActivityChainRows(rows, showToolDetails, copy), syntheticRows)
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, marginBottom: 1 },
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') {