kafox-cli 0.9.7 → 0.9.9
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 +18 -1
- package/src/tools.js +13 -2
- package/src/tui.js +40 -12
package/package.json
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kafox-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Kafox CLI: TUI com streaming, thinking, edit com diff, agent tools (files/shell), MCP e temas",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/kafox/kafox-cli.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://kafox.com",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/kafox/kafox-cli/issues"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"tui",
|
|
16
|
+
"ai",
|
|
17
|
+
"agent",
|
|
18
|
+
"llm",
|
|
19
|
+
"mcp"
|
|
20
|
+
],
|
|
5
21
|
"bin": {
|
|
6
22
|
"kafox": "bin/kafox.js"
|
|
7
23
|
},
|
|
@@ -14,6 +30,7 @@
|
|
|
14
30
|
"node": ">=22.5"
|
|
15
31
|
},
|
|
16
32
|
"license": "MIT",
|
|
33
|
+
"author": "Kafox <dev@kafox.com> (https://kafox.com)",
|
|
17
34
|
"dependencies": {
|
|
18
35
|
"chalk": "4.1.2",
|
|
19
36
|
"commander": "12.1.0",
|
package/src/tools.js
CHANGED
|
@@ -122,9 +122,20 @@ function toolMeta(name, hub) {
|
|
|
122
122
|
|
|
123
123
|
function resolveInside(cwd, target) {
|
|
124
124
|
const root = path.resolve(cwd);
|
|
125
|
-
const
|
|
125
|
+
const raw = String(target ?? ".");
|
|
126
|
+
// Caminho absoluto de outro sistema (ex.: "C:\Windows\..." no Linux) e
|
|
127
|
+
// NUL nunca valem: sem isso o `path.resolve` trata como nome de arquivo.
|
|
128
|
+
if (raw.includes("\0")) throw new Error(`caminho invalido: ${raw}`);
|
|
129
|
+
if (path.win32.isAbsolute(raw) || path.posix.isAbsolute(raw)) {
|
|
130
|
+
const full = path.isAbsolute(raw) ? path.resolve(raw) : null;
|
|
131
|
+
if (!full || (full !== root && !full.startsWith(root + path.sep))) {
|
|
132
|
+
throw new Error(`caminho fora do diretorio de trabalho: ${raw}`);
|
|
133
|
+
}
|
|
134
|
+
return full;
|
|
135
|
+
}
|
|
136
|
+
const full = path.resolve(root, raw);
|
|
126
137
|
if (full !== root && !full.startsWith(root + path.sep)) {
|
|
127
|
-
throw new Error(`caminho fora do diretorio de trabalho: ${
|
|
138
|
+
throw new Error(`caminho fora do diretorio de trabalho: ${raw}`);
|
|
128
139
|
}
|
|
129
140
|
return full;
|
|
130
141
|
}
|
package/src/tui.js
CHANGED
|
@@ -489,6 +489,9 @@ function initialState() {
|
|
|
489
489
|
messages: [],
|
|
490
490
|
context: [],
|
|
491
491
|
streaming: null,
|
|
492
|
+
// Texto ja comitado em finishStreaming() neste turno: usado para nao
|
|
493
|
+
// duplicar a resposta quando runAgent devolve o texto no final.
|
|
494
|
+
streamedText: "",
|
|
492
495
|
approval: null,
|
|
493
496
|
autoApprove: false,
|
|
494
497
|
busy: false,
|
|
@@ -902,8 +905,36 @@ function formatTokens(count) {
|
|
|
902
905
|
return `${(count / 1_000_000).toFixed(1)}M`;
|
|
903
906
|
}
|
|
904
907
|
|
|
905
|
-
|
|
906
|
-
|
|
908
|
+
/**
|
|
909
|
+
* Commita o texto que chegou pelo stream como mensagem `assistant`.
|
|
910
|
+
* Nao duplica quando o mesmo trecho ja foi commitado neste turno (retry do
|
|
911
|
+
* gateway repetindo tokens, `onToolStart` chamado duas vezes, etc.).
|
|
912
|
+
*/
|
|
913
|
+
function commitStreamedText(state, text) {
|
|
914
|
+
const value = String(text || "").trim();
|
|
915
|
+
if (!value) return false;
|
|
916
|
+
if (state.streamedText.includes(value)) return false;
|
|
917
|
+
const previous = [...state.messages].reverse().find((message) => message.role === "assistant");
|
|
918
|
+
if (previous && previous.text === value) return false;
|
|
919
|
+
state.messages.push({ role: "assistant", text: value });
|
|
920
|
+
state.streamedText = `${state.streamedText}${value}`;
|
|
921
|
+
return true;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Ultimo texto devolvido por `runAgent`. Quando o stream ja mostrou esse texto
|
|
926
|
+
* (commit em `finishStreaming`), nao empurra de novo: era isso que fazia a
|
|
927
|
+
* resposta aparecer duas vezes na tela.
|
|
928
|
+
*/
|
|
929
|
+
function commitFinalText(state, text, ms) {
|
|
930
|
+
const value = String(text || "").trim();
|
|
931
|
+
if (!value) return false;
|
|
932
|
+
if (state.streamedText.includes(value)) return false;
|
|
933
|
+
state.messages.push({ role: "assistant", text: value, ms });
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function estimateTokens(state) { let chars = 0;
|
|
907
938
|
for (const message of state.context) chars += String(message.content || "").length;
|
|
908
939
|
if (state.streaming) chars += String(state.streaming.text || "").length;
|
|
909
940
|
return Math.round(chars / 4);
|
|
@@ -1950,13 +1981,10 @@ async function runTUI({
|
|
|
1950
1981
|
if (result.messages) state.context = result.messages;
|
|
1951
1982
|
} else if (result.ok) {
|
|
1952
1983
|
const text = String(result.text || "").trim();
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
ms: result.ms,
|
|
1958
|
-
});
|
|
1959
|
-
}
|
|
1984
|
+
// O stream ja foi commitado em finishStreaming(): so empurra aqui se for
|
|
1985
|
+
// texto novo (ou se nao houve streaming, ex.: resposta sem onText).
|
|
1986
|
+
if (text) commitFinalText(state, text, result.ms);
|
|
1987
|
+
state.streamedText = "";
|
|
1960
1988
|
if (result.messages) state.context = result.messages;
|
|
1961
1989
|
} else {
|
|
1962
1990
|
state.messages.push({
|
|
@@ -1983,9 +2011,7 @@ async function runTUI({
|
|
|
1983
2011
|
ms: streaming.thinkingStartedAt ? Date.now() - streaming.thinkingStartedAt : 0,
|
|
1984
2012
|
});
|
|
1985
2013
|
}
|
|
1986
|
-
if (streaming.text.trim())
|
|
1987
|
-
state.messages.push({ role: "assistant", text: streaming.text.trim() });
|
|
1988
|
-
}
|
|
2014
|
+
if (streaming.text.trim()) commitStreamedText(state, streaming.text.trim());
|
|
1989
2015
|
state.streaming = { text: "", reasoning: "", thinkingStartedAt: 0 };
|
|
1990
2016
|
}
|
|
1991
2017
|
|
|
@@ -2383,4 +2409,6 @@ module.exports = {
|
|
|
2383
2409
|
planRepaint,
|
|
2384
2410
|
APPROVAL_OPTIONS,
|
|
2385
2411
|
layoutFor,
|
|
2412
|
+
commitStreamedText,
|
|
2413
|
+
commitFinalText,
|
|
2386
2414
|
};
|