@gird/ccli 2.1.2 → 2.3.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/README.md +9 -3
- package/dist/agent/ai.js +28 -5
- package/dist/agent/ai.js.map +1 -1
- package/dist/cli/args.js +5 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/history.js +97 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/input.js +50 -0
- package/dist/cli/input.js.map +1 -0
- package/dist/cli/repl.js +28 -74
- package/dist/cli/repl.js.map +1 -1
- package/dist/cli/run.js +106 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/slash.js +11 -1
- package/dist/cli/slash.js.map +1 -1
- package/dist/cli/stats.js +98 -0
- package/dist/cli/stats.js.map +1 -0
- package/dist/index.js +56 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,9 +17,12 @@ npm install -g @gird/ccli
|
|
|
17
17
|
## 用法
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
ccli
|
|
21
|
-
ccli --version
|
|
22
|
-
ccli --help
|
|
20
|
+
ccli # 进入交互式对话
|
|
21
|
+
ccli --version # 打印版本号
|
|
22
|
+
ccli --help # 打印帮助
|
|
23
|
+
ccli "用一句话介绍 CCLI" # 非交互式单次调用
|
|
24
|
+
cat file.ts | ccli "给这段代码加注释" # 从 stdin 读取内容作为上下文
|
|
25
|
+
ccli --yes "在当前目录创建 test.txt" # 自动确认破坏性工具调用
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
进入对话后:
|
|
@@ -28,6 +31,9 @@ ccli --help # 打印帮助
|
|
|
28
31
|
- 模型可调用的工具:读文件、列目录、搜索代码、写文件、局部编辑文件、执行 shell 命令(写入/编辑/执行操作会请你确认)。
|
|
29
32
|
- `/help` 查看可用命令,`/clear` 清空历史,`/undo [n]` 撤销最近 n 轮对话。
|
|
30
33
|
- `/save [name]` 保存当前会话,`/load <name>` 加载会话(按 `Tab` 补全/列出候选),`/sessions` 列出已保存会话。
|
|
34
|
+
- `/stats` 查看当前会话累计 token 消耗与上下文占比(估算值)。
|
|
35
|
+
- 按 `↑` / `↓` recalled 之前输入过的 prompt / 命令,历史自动保存到 `~/.ccli/history`。
|
|
36
|
+
- 粘贴多行文本时会自动合并成一条消息发送,不会被截断。
|
|
31
37
|
- 退出时自动保存 `last` 恢复点;下次启动会询问是否恢复上次会话。
|
|
32
38
|
- `/exit` 或 `/quit` 退出,也可按 Ctrl-C / Ctrl-D。
|
|
33
39
|
|
package/dist/agent/ai.js
CHANGED
|
@@ -53,19 +53,31 @@ class OpenAIClient {
|
|
|
53
53
|
* @returns 助手本回合的文本 + 它想调用的工具
|
|
54
54
|
*/
|
|
55
55
|
async chat(messages, onDelta) {
|
|
56
|
-
const stream = await this.client.chat.completions.create({
|
|
56
|
+
const stream = (await this.client.chat.completions.create({
|
|
57
57
|
model: this.model,
|
|
58
58
|
messages,
|
|
59
59
|
tools: toolDefs(),
|
|
60
60
|
stream: true,
|
|
61
|
-
|
|
61
|
+
stream_options: { include_usage: true },
|
|
62
|
+
}));
|
|
62
63
|
let content = '';
|
|
63
64
|
// 工具调用在流里是分片到达的,按 index 累加拼回完整的 id/name/arguments
|
|
64
65
|
const acc = {};
|
|
66
|
+
let usage;
|
|
65
67
|
for await (const chunk of stream) {
|
|
66
68
|
const delta = chunk.choices[0]?.delta;
|
|
67
|
-
if (!delta)
|
|
69
|
+
if (!delta) {
|
|
70
|
+
// OpenAI 流式 usage 通常在 choices 为空的最终 chunk 里
|
|
71
|
+
const u = chunk.usage;
|
|
72
|
+
if (u) {
|
|
73
|
+
usage = {
|
|
74
|
+
promptTokens: u.prompt_tokens ?? 0,
|
|
75
|
+
completionTokens: u.completion_tokens ?? 0,
|
|
76
|
+
totalTokens: u.total_tokens ?? 0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
68
79
|
continue;
|
|
80
|
+
}
|
|
69
81
|
if (delta.content) {
|
|
70
82
|
content += delta.content;
|
|
71
83
|
onDelta(delta.content);
|
|
@@ -88,7 +100,7 @@ class OpenAIClient {
|
|
|
88
100
|
.map(Number)
|
|
89
101
|
.sort((a, b) => a - b)
|
|
90
102
|
.map((i) => acc[i]);
|
|
91
|
-
return { content, toolCalls };
|
|
103
|
+
return { content, toolCalls, usage };
|
|
92
104
|
}
|
|
93
105
|
}
|
|
94
106
|
class AnthropicClient {
|
|
@@ -135,6 +147,7 @@ class AnthropicClient {
|
|
|
135
147
|
});
|
|
136
148
|
let content = '';
|
|
137
149
|
const acc = {};
|
|
150
|
+
let usage;
|
|
138
151
|
for await (const event of stream) {
|
|
139
152
|
if (event.type === 'content_block_delta') {
|
|
140
153
|
if (event.delta.type === 'text_delta') {
|
|
@@ -156,12 +169,22 @@ class AnthropicClient {
|
|
|
156
169
|
};
|
|
157
170
|
}
|
|
158
171
|
}
|
|
172
|
+
else if (event.type === 'message_delta') {
|
|
173
|
+
const u = event.usage;
|
|
174
|
+
if (u) {
|
|
175
|
+
usage = {
|
|
176
|
+
promptTokens: u.input_tokens ?? 0,
|
|
177
|
+
completionTokens: u.output_tokens ?? 0,
|
|
178
|
+
totalTokens: (u.input_tokens ?? 0) + (u.output_tokens ?? 0),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
159
182
|
}
|
|
160
183
|
const toolCalls = Object.keys(acc)
|
|
161
184
|
.map(Number)
|
|
162
185
|
.sort((a, b) => a - b)
|
|
163
186
|
.map((i) => acc[i]);
|
|
164
|
-
return { content, toolCalls };
|
|
187
|
+
return { content, toolCalls, usage };
|
|
165
188
|
}
|
|
166
189
|
}
|
|
167
190
|
/** 把 OpenAI 风格的消息列表转成 Anthropic 的 system + messages。 */
|
package/dist/agent/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/agent/ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/agent/ai.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/args.js
CHANGED
|
@@ -8,6 +8,7 @@ export function parseArgs(argv) {
|
|
|
8
8
|
const result = {
|
|
9
9
|
version: false,
|
|
10
10
|
help: false,
|
|
11
|
+
yes: false,
|
|
11
12
|
unknown: [],
|
|
12
13
|
positionals: [],
|
|
13
14
|
};
|
|
@@ -21,6 +22,10 @@ export function parseArgs(argv) {
|
|
|
21
22
|
case '--help':
|
|
22
23
|
result.help = true;
|
|
23
24
|
break;
|
|
25
|
+
case '-y':
|
|
26
|
+
case '--yes':
|
|
27
|
+
result.yes = true;
|
|
28
|
+
break;
|
|
24
29
|
default:
|
|
25
30
|
if (arg.startsWith('-')) {
|
|
26
31
|
result.unknown.push(arg);
|
package/dist/cli/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAClB,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,QAAQ,GAAG,EAAE,CAAC;YACV,KAAK,IAAI,CAAC;YACV,KAAK,WAAW;gBACZ,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACT,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,OAAO;gBACR,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;gBAClB,MAAM;YACV;gBACI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjC,CAAC;QACT,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL 输入历史持久化。
|
|
3
|
+
*
|
|
4
|
+
* 把用户在交互式会话里输入过的非空文本按行保存到 ~/.ccli/history,
|
|
5
|
+
* 下次启动时加载到 readline 的 history 数组,支持上下箭头 recalled。
|
|
6
|
+
*/
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
10
|
+
const DEFAULT_HISTORY_PATH = join(homedir(), '.ccli', 'history');
|
|
11
|
+
/** 当前使用的历史文件路径(可被测试覆盖)。 */
|
|
12
|
+
let historyPath = DEFAULT_HISTORY_PATH;
|
|
13
|
+
/** 仅供测试:临时切换历史文件路径。 */
|
|
14
|
+
export function setHistoryPathForTest(path) {
|
|
15
|
+
historyPath = path;
|
|
16
|
+
}
|
|
17
|
+
/** 仅供测试:恢复默认历史文件路径。 */
|
|
18
|
+
export function resetHistoryPathForTest() {
|
|
19
|
+
historyPath = DEFAULT_HISTORY_PATH;
|
|
20
|
+
}
|
|
21
|
+
/** 确保历史文件所在目录存在。 */
|
|
22
|
+
function ensureDir() {
|
|
23
|
+
const dir = dirname(historyPath);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 加载历史输入列表。
|
|
30
|
+
* 文件不存在或读取失败时返回空数组。
|
|
31
|
+
*/
|
|
32
|
+
export function loadHistory() {
|
|
33
|
+
if (!existsSync(historyPath)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const raw = readFileSync(historyPath, 'utf8');
|
|
38
|
+
const lines = raw
|
|
39
|
+
.split('\n')
|
|
40
|
+
.map((line) => line.trimEnd())
|
|
41
|
+
.filter((line) => line.length > 0);
|
|
42
|
+
// readline 把最新输入放在 history[0],所以文件按时间顺序加载后要反转
|
|
43
|
+
return lines.reverse();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 追加一条输入到历史文件。
|
|
51
|
+
* 空字符串或仅空白字符的输入会被忽略。
|
|
52
|
+
*/
|
|
53
|
+
export function appendHistory(line) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (trimmed.length === 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
ensureDir();
|
|
60
|
+
appendFileSync(historyPath, `${trimmed}\n`, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// 历史保存失败不打扰用户
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 清空历史文件。
|
|
68
|
+
* 用于测试或后续可能的 /clear-history 命令。
|
|
69
|
+
*/
|
|
70
|
+
export function clearHistory() {
|
|
71
|
+
try {
|
|
72
|
+
if (existsSync(historyPath)) {
|
|
73
|
+
unlinkSync(historyPath);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// 忽略清理失败
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 用完整列表覆盖历史文件。
|
|
82
|
+
* 主要用于测试或批量导入场景。
|
|
83
|
+
*/
|
|
84
|
+
export function saveHistory(lines) {
|
|
85
|
+
try {
|
|
86
|
+
ensureDir();
|
|
87
|
+
const content = lines
|
|
88
|
+
.map((line) => line.trim())
|
|
89
|
+
.filter((line) => line.length > 0)
|
|
90
|
+
.join('\n');
|
|
91
|
+
writeFileSync(historyPath, content ? `${content}\n` : '', 'utf8');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// 忽略保存失败
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/cli/history.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACH,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,aAAa,GAChB,MAAM,SAAS,CAAC;AAEjB,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAEjE,2BAA2B;AAC3B,IAAI,WAAW,GAAG,oBAAoB,CAAC;AAEvC,uBAAuB;AACvB,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAC9C,WAAW,GAAG,IAAI,CAAC;AACvB,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,uBAAuB;IACnC,WAAW,GAAG,oBAAoB,CAAC;AACvC,CAAC;AAED,oBAAoB;AACpB,SAAS,SAAS;IACd,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACvB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,GAAG;aACZ,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;aAC7B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,8CAA8C;QAC9C,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;IACX,CAAC;IACD,IAAI,CAAC;QACD,SAAS,EAAE,CAAC;QACZ,cAAc,CAAC,WAAW,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACL,cAAc;IAClB,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IACxB,IAAI,CAAC;QACD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC1B,UAAU,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,SAAS;IACb,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAe;IACvC,IAAI,CAAC;QACD,SAAS,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,KAAK;aAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACjC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,aAAa,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACL,SAAS;IACb,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL 多行输入缓冲。
|
|
3
|
+
*
|
|
4
|
+
* 把短时间内连续到达的多行文本合并成一条输入,
|
|
5
|
+
* 解决用户粘贴多行内容时被 readline 拆散的问题。
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 输入缓冲区。
|
|
9
|
+
*
|
|
10
|
+
* 每次收到一行先累积;在 debounceMs 内没有新行到达时,
|
|
11
|
+
* 把累积的多行按 \n 合并后触发回调。
|
|
12
|
+
*/
|
|
13
|
+
export class InputBuffer {
|
|
14
|
+
options;
|
|
15
|
+
lines = [];
|
|
16
|
+
timer = null;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
/** 放入一行新输入。 */
|
|
21
|
+
push(line) {
|
|
22
|
+
this.lines.push(line);
|
|
23
|
+
if (this.timer) {
|
|
24
|
+
clearTimeout(this.timer);
|
|
25
|
+
}
|
|
26
|
+
this.timer = setTimeout(() => this.flush(), this.options.debounceMs);
|
|
27
|
+
}
|
|
28
|
+
/** 立即 flush 当前缓冲区。 */
|
|
29
|
+
flush() {
|
|
30
|
+
this.timer = null;
|
|
31
|
+
const raw = this.lines.join('\n');
|
|
32
|
+
this.lines = [];
|
|
33
|
+
const input = raw.trim();
|
|
34
|
+
if (input.length === 0) {
|
|
35
|
+
this.options.onEmpty();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.options.onInput(input);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** 清空缓冲区和未触发的定时器。 */
|
|
42
|
+
clear() {
|
|
43
|
+
if (this.timer) {
|
|
44
|
+
clearTimeout(this.timer);
|
|
45
|
+
this.timer = null;
|
|
46
|
+
}
|
|
47
|
+
this.lines = [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/cli/input.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IAIA;IAHZ,KAAK,GAAa,EAAE,CAAC;IACrB,KAAK,GAA0B,IAAI,CAAC;IAE5C,YAAoB,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;IAEnD,eAAe;IACf,IAAI,CAAC,IAAY;QACb,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACzE,CAAC;IAED,sBAAsB;IACtB,KAAK;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACL,CAAC;IAED,qBAAqB;IACrB,KAAK;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IACpB,CAAC;CACJ"}
|
package/dist/cli/repl.js
CHANGED
|
@@ -10,8 +10,11 @@ import { NAME, VERSION } from '../version.js';
|
|
|
10
10
|
import { loadConfig, maskKey } from '../config/config.js';
|
|
11
11
|
import { createAIClient } from '../agent/ai.js';
|
|
12
12
|
import { handleSlash, slashCompleter } from './slash.js';
|
|
13
|
-
import { executeTool, getTool, } from '../tools/index.js';
|
|
14
13
|
import { hasLastSession, loadLastSession, saveLastSession, clearLastSession, } from './session.js';
|
|
14
|
+
import { appendHistory, loadHistory } from './history.js';
|
|
15
|
+
import { InputBuffer } from './input.js';
|
|
16
|
+
import { emptyStats, formatStats, } from './stats.js';
|
|
17
|
+
import { runAgentTurn } from './run.js';
|
|
15
18
|
const PROMPT = '› ';
|
|
16
19
|
const SYSTEM_PROMPT = '你是 CCLI,一个运行在终端里的 AI 助手。回答简洁、准确,使用中文。' +
|
|
17
20
|
'你可以调用工具读文件、列目录、搜索(read_file / list_dir / grep),' +
|
|
@@ -25,71 +28,6 @@ function question(rl, prompt) {
|
|
|
25
28
|
rl.question(prompt, (ans) => resolve(ans));
|
|
26
29
|
});
|
|
27
30
|
}
|
|
28
|
-
/**
|
|
29
|
-
* 解析工具参数用于预览;失败则返回空对象(真正执行时 executeTool 会再报错)。
|
|
30
|
-
*/
|
|
31
|
-
function safeParse(argsJson) {
|
|
32
|
-
try {
|
|
33
|
-
return JSON.parse(argsJson || '{}');
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* 驱动一个用户回合:请求模型 → 若它要调用工具则(必要时确认后)执行并回传 → 再请求 …
|
|
41
|
-
* 直到模型不再调用工具(给出最终回答)或达到轮数上限。
|
|
42
|
-
*/
|
|
43
|
-
async function runAgentTurn(ai, history, confirm) {
|
|
44
|
-
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
45
|
-
const turn = await ai.chat(history, (delta) => process.stdout.write(delta));
|
|
46
|
-
// 没有工具调用 → 这是最终回答,收尾退出
|
|
47
|
-
if (turn.toolCalls.length === 0) {
|
|
48
|
-
process.stdout.write('\n');
|
|
49
|
-
history.push({ role: 'assistant', content: turn.content });
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
// 有工具调用:先把带 tool_calls 的 assistant 消息入历史
|
|
53
|
-
history.push({
|
|
54
|
-
role: 'assistant',
|
|
55
|
-
content: turn.content || null,
|
|
56
|
-
tool_calls: turn.toolCalls.map((tc) => ({
|
|
57
|
-
id: tc.id,
|
|
58
|
-
type: 'function',
|
|
59
|
-
function: { name: tc.name, arguments: tc.arguments },
|
|
60
|
-
})),
|
|
61
|
-
});
|
|
62
|
-
// 逐个执行工具,结果以 role:'tool' 消息回传
|
|
63
|
-
for (const tc of turn.toolCalls) {
|
|
64
|
-
const tool = getTool(tc.name);
|
|
65
|
-
// 破坏性工具:执行前展示预览并请用户确认
|
|
66
|
-
if (tool?.needsConfirm) {
|
|
67
|
-
const preview = tool.preview
|
|
68
|
-
? tool.preview(safeParse(tc.arguments))
|
|
69
|
-
: `${tc.name}(${tc.arguments})`;
|
|
70
|
-
const approved = await confirm(preview);
|
|
71
|
-
if (!approved) {
|
|
72
|
-
console.log('已取消。');
|
|
73
|
-
history.push({
|
|
74
|
-
role: 'tool',
|
|
75
|
-
tool_call_id: tc.id,
|
|
76
|
-
content: '用户拒绝执行此操作。请勿重试,可询问用户希望如何继续。',
|
|
77
|
-
});
|
|
78
|
-
continue; // 不执行,继续下一个工具调用
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
console.log(`\n⚙ ${tc.name}(${tc.arguments})`);
|
|
82
|
-
const result = await executeTool(tc.name, tc.arguments);
|
|
83
|
-
history.push({
|
|
84
|
-
role: 'tool',
|
|
85
|
-
tool_call_id: tc.id,
|
|
86
|
-
content: result,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
// 继续下一轮,让模型基于工具结果接着回答
|
|
90
|
-
}
|
|
91
|
-
console.log('\n(已达到工具调用轮数上限)');
|
|
92
|
-
}
|
|
93
31
|
export async function startRepl() {
|
|
94
32
|
// 启动即解析配置:缺失就明确报错退出,不进入循环
|
|
95
33
|
const result = loadConfig();
|
|
@@ -101,11 +39,14 @@ export async function startRepl() {
|
|
|
101
39
|
const ai = createAIClient(config);
|
|
102
40
|
// 对话历史:以 system 提示词开场,之后累积 user / assistant 轮次
|
|
103
41
|
const history = [{ role: 'system', content: SYSTEM_PROMPT }];
|
|
42
|
+
let stats = emptyStats();
|
|
104
43
|
const rl = createInterface({
|
|
105
44
|
input: process.stdin,
|
|
106
45
|
output: process.stdout,
|
|
107
46
|
prompt: PROMPT,
|
|
108
47
|
completer: slashCompleter,
|
|
48
|
+
history: loadHistory(),
|
|
49
|
+
historySize: 1000,
|
|
109
50
|
});
|
|
110
51
|
console.log(`${NAME} v${VERSION}`);
|
|
111
52
|
console.log(`后端: ${config.provider} 模型: ${config.model} 密钥: ${maskKey(config.apiKey)}`);
|
|
@@ -140,13 +81,11 @@ export async function startRepl() {
|
|
|
140
81
|
});
|
|
141
82
|
// 正在等待 AI 回复时置 true,避免这期间的输入被重复处理
|
|
142
83
|
let busy = false;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
84
|
+
const INPUT_DEBOUNCE_MS = 80;
|
|
85
|
+
/** 处理一条已合并好的用户输入(斜杠命令或普通消息)。 */
|
|
86
|
+
async function processUserInput(input) {
|
|
87
|
+
// 非空输入追加到持久化历史(readline 内存历史已自动维护)
|
|
88
|
+
appendHistory(input);
|
|
150
89
|
// 斜杠命令:交给 handleSlash,不发给 AI
|
|
151
90
|
// (/model 会联网列模型,故也置 busy 防止期间重复处理)
|
|
152
91
|
if (input.startsWith('/')) {
|
|
@@ -155,6 +94,7 @@ export async function startRepl() {
|
|
|
155
94
|
await handleSlash(input, {
|
|
156
95
|
history,
|
|
157
96
|
ai,
|
|
97
|
+
stats,
|
|
158
98
|
requestExit: () => {
|
|
159
99
|
exiting = true;
|
|
160
100
|
rl.close();
|
|
@@ -169,7 +109,9 @@ export async function startRepl() {
|
|
|
169
109
|
const mark = history.length; // 出错时回滚到这里,避免半截上下文
|
|
170
110
|
history.push({ role: 'user', content: input });
|
|
171
111
|
try {
|
|
172
|
-
await runAgentTurn(ai, history, confirm);
|
|
112
|
+
await runAgentTurn({ ai, history, confirm, stats });
|
|
113
|
+
// 本回合结束,显示本次 token 消耗
|
|
114
|
+
console.log(`\n${formatStats(stats, ai.getModel())}`);
|
|
173
115
|
}
|
|
174
116
|
catch (e) {
|
|
175
117
|
console.error(`\n[请求失败] ${e?.message ?? e}`);
|
|
@@ -179,6 +121,18 @@ export async function startRepl() {
|
|
|
179
121
|
busy = false;
|
|
180
122
|
rl.prompt();
|
|
181
123
|
}
|
|
124
|
+
}
|
|
125
|
+
const inputBuffer = new InputBuffer({
|
|
126
|
+
debounceMs: INPUT_DEBOUNCE_MS,
|
|
127
|
+
onInput: (input) => void processUserInput(input),
|
|
128
|
+
onEmpty: () => rl.prompt(),
|
|
129
|
+
});
|
|
130
|
+
rl.on('line', (line) => {
|
|
131
|
+
// AI 正忙时忽略新输入
|
|
132
|
+
if (busy) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
inputBuffer.push(line);
|
|
182
136
|
});
|
|
183
137
|
// Ctrl-D(EOF)或 rl.close() 触发
|
|
184
138
|
rl.on('close', () => {
|
package/dist/cli/repl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { executeTool, getTool } from '../tools/index.js';
|
|
2
|
+
import { estimatePromptTokens, estimateTokens, } from './stats.js';
|
|
3
|
+
/** 单个用户回合内,最多允许模型连续调用工具的轮数,防止失控循环。 */
|
|
4
|
+
const MAX_TOOL_ROUNDS = 10;
|
|
5
|
+
/**
|
|
6
|
+
* 解析工具参数用于预览;失败则返回空对象(真正执行时 executeTool 会再报错)。
|
|
7
|
+
*/
|
|
8
|
+
function safeParse(argsJson) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(argsJson || '{}');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 驱动一个用户回合:请求模型 → 若它要调用工具则(必要时确认后)执行并回传 → 再请求 …
|
|
18
|
+
* 直到模型不再调用工具(给出最终回答)或达到轮数上限。
|
|
19
|
+
*/
|
|
20
|
+
export async function runAgentTurn(options) {
|
|
21
|
+
const { ai, history, confirm, stats, print = process.stdout.write.bind(process.stdout) } = options;
|
|
22
|
+
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
23
|
+
const turn = await ai.chat(history, (delta) => print(delta));
|
|
24
|
+
// 优先使用后端返回的真实 usage;不支持时降级为字符估算
|
|
25
|
+
if (turn.usage) {
|
|
26
|
+
stats.promptTokens += turn.usage.promptTokens;
|
|
27
|
+
stats.completionTokens += turn.usage.completionTokens;
|
|
28
|
+
stats.totalTokens += turn.usage.totalTokens;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const promptTokens = estimatePromptTokens(history);
|
|
32
|
+
const completionTokens = estimateTokens(turn.content);
|
|
33
|
+
stats.promptTokens += promptTokens;
|
|
34
|
+
stats.completionTokens += completionTokens;
|
|
35
|
+
stats.totalTokens += promptTokens + completionTokens;
|
|
36
|
+
}
|
|
37
|
+
// 没有工具调用 → 这是最终回答,收尾退出
|
|
38
|
+
if (turn.toolCalls.length === 0) {
|
|
39
|
+
print('\n');
|
|
40
|
+
history.push({ role: 'assistant', content: turn.content });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// 有工具调用:先把带 tool_calls 的 assistant 消息入历史
|
|
44
|
+
history.push({
|
|
45
|
+
role: 'assistant',
|
|
46
|
+
content: turn.content || null,
|
|
47
|
+
tool_calls: turn.toolCalls.map((tc) => ({
|
|
48
|
+
id: tc.id,
|
|
49
|
+
type: 'function',
|
|
50
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
51
|
+
})),
|
|
52
|
+
});
|
|
53
|
+
// 逐个执行工具,结果以 role:'tool' 消息回传
|
|
54
|
+
for (const tc of turn.toolCalls) {
|
|
55
|
+
const tool = getTool(tc.name);
|
|
56
|
+
// 破坏性工具:执行前展示预览并请用户确认
|
|
57
|
+
if (tool?.needsConfirm) {
|
|
58
|
+
const preview = tool.preview
|
|
59
|
+
? tool.preview(safeParse(tc.arguments))
|
|
60
|
+
: `${tc.name}(${tc.arguments})`;
|
|
61
|
+
const approved = await confirm(preview);
|
|
62
|
+
if (!approved) {
|
|
63
|
+
print('\n已取消。\n');
|
|
64
|
+
history.push({
|
|
65
|
+
role: 'tool',
|
|
66
|
+
tool_call_id: tc.id,
|
|
67
|
+
content: '用户拒绝执行此操作。请勿重试,可询问用户希望如何继续。',
|
|
68
|
+
});
|
|
69
|
+
continue; // 不执行,继续下一个工具调用
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
print(`\n⚙ ${tc.name}(${tc.arguments})\n`);
|
|
73
|
+
const result = await executeTool(tc.name, tc.arguments);
|
|
74
|
+
history.push({
|
|
75
|
+
role: 'tool',
|
|
76
|
+
tool_call_id: tc.id,
|
|
77
|
+
content: result,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// 继续下一轮,让模型基于工具结果接着回答
|
|
81
|
+
}
|
|
82
|
+
print('\n(已达到工具调用轮数上限)\n');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 非交互式单次调用。
|
|
86
|
+
* 以 systemPrompt 开场,把 prompt 作为 user 消息发送,执行一轮后返回。
|
|
87
|
+
*/
|
|
88
|
+
export async function runOnce(options) {
|
|
89
|
+
const { ai, prompt, confirm, print = process.stdout.write.bind(process.stdout), systemPrompt = '你是 CCLI,一个运行在终端里的 AI 助手。回答简洁、准确,使用中文。', } = options;
|
|
90
|
+
const history = [{ role: 'system', content: systemPrompt }];
|
|
91
|
+
history.push({ role: 'user', content: prompt });
|
|
92
|
+
let output = '';
|
|
93
|
+
const capturePrint = (chunk) => {
|
|
94
|
+
output += chunk;
|
|
95
|
+
print(chunk);
|
|
96
|
+
};
|
|
97
|
+
await runAgentTurn({
|
|
98
|
+
ai,
|
|
99
|
+
history,
|
|
100
|
+
confirm,
|
|
101
|
+
stats: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
102
|
+
print: capturePrint,
|
|
103
|
+
});
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/slash.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { saveSession, loadSession, listSessions, searchSessions } from './session.js';
|
|
2
|
+
import { formatStats, resetStats } from './stats.js';
|
|
2
3
|
/** 命令清单:名称 → 说明,供 /help 展示,单一数据源。 */
|
|
3
4
|
const COMMANDS = [
|
|
4
5
|
['/help', '列出所有命令'],
|
|
@@ -8,6 +9,7 @@ const COMMANDS = [
|
|
|
8
9
|
['/save [name]', '保存当前对话(省略名称自动生成)'],
|
|
9
10
|
['/load <name>', '加载会话;不完整时列出候选'],
|
|
10
11
|
['/sessions', '列出已保存的会话'],
|
|
12
|
+
['/stats', '查看本次会话 token 统计'],
|
|
11
13
|
['/exit, /quit', '退出会话'],
|
|
12
14
|
];
|
|
13
15
|
/** /model 搜索时最多列出多少个候选,避免刷屏。 */
|
|
@@ -53,7 +55,8 @@ export async function handleSlash(input, ctx) {
|
|
|
53
55
|
case 'clear':
|
|
54
56
|
// 保留第 0 条 system 提示,清掉其余历史
|
|
55
57
|
ctx.history.length = 1;
|
|
56
|
-
|
|
58
|
+
resetStats(ctx.stats);
|
|
59
|
+
console.log('(已清空对话历史和 token 统计)');
|
|
57
60
|
return true;
|
|
58
61
|
case 'undo':
|
|
59
62
|
handleUndo(arg, ctx);
|
|
@@ -70,6 +73,9 @@ export async function handleSlash(input, ctx) {
|
|
|
70
73
|
case 'sessions':
|
|
71
74
|
handleSessions();
|
|
72
75
|
return true;
|
|
76
|
+
case 'stats':
|
|
77
|
+
handleStats(ctx);
|
|
78
|
+
return true;
|
|
73
79
|
case 'exit':
|
|
74
80
|
case 'quit':
|
|
75
81
|
ctx.requestExit();
|
|
@@ -234,6 +240,10 @@ function handleSessions() {
|
|
|
234
240
|
console.log(` ${s.name.padEnd(30)} ${s.messageCount} 条 ${time}`);
|
|
235
241
|
}
|
|
236
242
|
}
|
|
243
|
+
/** /stats:打印当前累计 token 统计。 */
|
|
244
|
+
function handleStats(ctx) {
|
|
245
|
+
console.log(formatStats(ctx.stats, ctx.ai.getModel()));
|
|
246
|
+
}
|
|
237
247
|
function printHelp() {
|
|
238
248
|
console.log('可用命令:');
|
|
239
249
|
for (const [cmd, desc] of COMMANDS) {
|
package/dist/cli/slash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slash.js","sourceRoot":"","sources":["../../src/cli/slash.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"slash.js","sourceRoot":"","sources":["../../src/cli/slash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token 统计与上下文占比估算。
|
|
3
|
+
*
|
|
4
|
+
* 精确 token 数需要后端返回 usage 或引入 tokenizer(如 tiktoken)。
|
|
5
|
+
* 这里提供一个轻量级估算:按字符数折算,方便用户感知上下文压力。
|
|
6
|
+
*/
|
|
7
|
+
/** 模型最大上下文长度(供占比计算)。数值为 token 数,注释为常见标称值。 */
|
|
8
|
+
const CONTEXT_LIMIT = {
|
|
9
|
+
// Anthropic —— 200K
|
|
10
|
+
'claude-opus-4-5-20251101': 200000, // 200K
|
|
11
|
+
'claude-sonnet-4-5-20251101': 200000, // 200K
|
|
12
|
+
'claude-sonnet-4-5-20241022': 200000, // 200K
|
|
13
|
+
'claude-3-5-sonnet-20241022': 200000, // 200K
|
|
14
|
+
'claude-3-opus-20240229': 200000, // 200K
|
|
15
|
+
'claude-3-sonnet-20240229': 200000, // 200K
|
|
16
|
+
'claude-3-haiku-20240307': 200000, // 200K
|
|
17
|
+
// OpenAI / 通义千问 / DeepSeek 常见值
|
|
18
|
+
'qwen-plus': 131072, // 128K
|
|
19
|
+
'qwen-max': 32768, // 32K
|
|
20
|
+
'deepseek-chat': 65536, // 64K
|
|
21
|
+
'deepseek-coder': 65536, // 64K
|
|
22
|
+
'gpt-4o': 128000, // 128K
|
|
23
|
+
'gpt-4o-mini': 128000, // 128K
|
|
24
|
+
'gpt-4': 8192, // 8K
|
|
25
|
+
'gpt-3.5-turbo': 16385, // 16K
|
|
26
|
+
};
|
|
27
|
+
const DEFAULT_CONTEXT_LIMIT = 128000; // 128K
|
|
28
|
+
/**
|
|
29
|
+
* 粗略估算文本对应的 token 数。
|
|
30
|
+
* 混合文本按 1 token ≈ 3.5 个字符估算(中文更费 token,英文更省,取平均)。
|
|
31
|
+
*/
|
|
32
|
+
export function estimateTokens(text) {
|
|
33
|
+
if (text.length === 0)
|
|
34
|
+
return 0;
|
|
35
|
+
return Math.max(1, Math.round(text.length / 3.5));
|
|
36
|
+
}
|
|
37
|
+
/** 获取模型上下文上限;未知模型返回默认值。 */
|
|
38
|
+
export function getContextLimit(model) {
|
|
39
|
+
if (model in CONTEXT_LIMIT)
|
|
40
|
+
return CONTEXT_LIMIT[model];
|
|
41
|
+
// 尝试前缀匹配,如 qwen-plus-0919 → qwen-plus
|
|
42
|
+
for (const key of Object.keys(CONTEXT_LIMIT)) {
|
|
43
|
+
if (model.startsWith(key))
|
|
44
|
+
return CONTEXT_LIMIT[key];
|
|
45
|
+
}
|
|
46
|
+
return DEFAULT_CONTEXT_LIMIT;
|
|
47
|
+
}
|
|
48
|
+
/** 把历史消息数组转成纯文本估算 prompt token。 */
|
|
49
|
+
export function estimatePromptTokens(history) {
|
|
50
|
+
return history.reduce((sum, m) => {
|
|
51
|
+
const text = typeof m.content === 'string'
|
|
52
|
+
? m.content
|
|
53
|
+
: JSON.stringify(m.content ?? '');
|
|
54
|
+
return sum + estimateTokens(text);
|
|
55
|
+
}, 0);
|
|
56
|
+
}
|
|
57
|
+
/** 创建空统计对象。 */
|
|
58
|
+
export function emptyStats() {
|
|
59
|
+
return { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
60
|
+
}
|
|
61
|
+
/** 原地清空统计对象(保持引用不变)。 */
|
|
62
|
+
export function resetStats(stats) {
|
|
63
|
+
stats.promptTokens = 0;
|
|
64
|
+
stats.completionTokens = 0;
|
|
65
|
+
stats.totalTokens = 0;
|
|
66
|
+
}
|
|
67
|
+
/** 合并两次统计。 */
|
|
68
|
+
export function addStats(a, b) {
|
|
69
|
+
return {
|
|
70
|
+
promptTokens: a.promptTokens + b.promptTokens,
|
|
71
|
+
completionTokens: a.completionTokens + b.completionTokens,
|
|
72
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 格式化统计为可读字符串。
|
|
77
|
+
* 示例:上下文: 20 / 131,072 tokens (0.0%) 输入: 12 输出: 8
|
|
78
|
+
*/
|
|
79
|
+
export function formatStats(stats, model) {
|
|
80
|
+
const ctx = model ? getContextLimit(model) : DEFAULT_CONTEXT_LIMIT;
|
|
81
|
+
const ratio = ctx > 0 ? (stats.totalTokens / ctx) * 100 : 0;
|
|
82
|
+
return `上下文: ${stats.totalTokens.toLocaleString()} / ${ctx.toLocaleString()} tokens (${ratio.toFixed(1)}%) 输入: ${stats.promptTokens.toLocaleString()} 输出: ${stats.completionTokens.toLocaleString()}`;
|
|
83
|
+
}
|
|
84
|
+
/** 把大数字格式化为 k/M,便于阅读。 */
|
|
85
|
+
function fmtCompact(n) {
|
|
86
|
+
if (n >= 1_000_000)
|
|
87
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
88
|
+
if (n >= 1_000)
|
|
89
|
+
return `${(n / 1_000).toFixed(1)}k`;
|
|
90
|
+
return n.toLocaleString();
|
|
91
|
+
}
|
|
92
|
+
/** 更紧凑的统计格式,适合放在提示符附近。 */
|
|
93
|
+
export function formatStatsCompact(stats, model) {
|
|
94
|
+
const ctx = model ? getContextLimit(model) : DEFAULT_CONTEXT_LIMIT;
|
|
95
|
+
const ratio = ctx > 0 ? (stats.totalTokens / ctx) * 100 : 0;
|
|
96
|
+
return `上下文: ${fmtCompact(stats.totalTokens)} / ${fmtCompact(ctx)} (${ratio.toFixed(1)}%)`;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/cli/stats.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* CCLI 入口。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* ccli --version / -v
|
|
7
|
-
* ccli --help / -h
|
|
8
|
-
* ccli
|
|
9
|
-
* ccli
|
|
5
|
+
* 支持三种模式:
|
|
6
|
+
* ccli --version / -v 打印版本号
|
|
7
|
+
* ccli --help / -h 打印本帮助
|
|
8
|
+
* ccli config 打印当前解析出的配置(诊断用,密钥打码)
|
|
9
|
+
* ccli "prompt" 非交互式单次调用
|
|
10
|
+
* echo xxx | ccli "prompt" 从 stdin 读取内容作为上下文
|
|
11
|
+
* ccli --yes "prompt" 非交互式下自动确认破坏性工具
|
|
12
|
+
* ccli 启动交互式会话
|
|
10
13
|
*/
|
|
11
14
|
import { parseArgs } from './cli/args.js';
|
|
12
15
|
import { NAME, VERSION } from './version.js';
|
|
13
16
|
import { startRepl } from './cli/repl.js';
|
|
14
|
-
import { printConfig } from './config/config.js';
|
|
17
|
+
import { printConfig, loadConfig } from './config/config.js';
|
|
18
|
+
import { createAIClient } from './agent/ai.js';
|
|
19
|
+
import { runOnce } from './cli/run.js';
|
|
15
20
|
const HELP = `${NAME} v${VERSION}
|
|
16
21
|
|
|
17
22
|
用法:
|
|
@@ -23,12 +28,34 @@ const HELP = `${NAME} v${VERSION}
|
|
|
23
28
|
选项:
|
|
24
29
|
-v, --version 打印版本号
|
|
25
30
|
-h, --help 打印本帮助
|
|
31
|
+
-y, --yes 非交互式模式下自动确认破坏性工具调用
|
|
26
32
|
|
|
27
33
|
示例:
|
|
28
|
-
ccli
|
|
29
|
-
ccli config
|
|
30
|
-
ccli --version
|
|
34
|
+
ccli 启动交互式对话
|
|
35
|
+
ccli config 查看当前生效的后端 / 模型 / 密钥来源
|
|
36
|
+
ccli --version 查看版本
|
|
37
|
+
ccli "解释这段报错" 非交互式单次调用
|
|
38
|
+
cat file.ts | ccli "给这段代码加注释" 从 stdin 读取上下文
|
|
39
|
+
ccli --yes "在当前目录创建 test.txt" 自动确认工具调用
|
|
31
40
|
`;
|
|
41
|
+
/** 从 stdin 读取全部内容;若 stdin 是 TTY 则返回空字符串。 */
|
|
42
|
+
async function readStdin() {
|
|
43
|
+
if (process.stdin.isTTY) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
const chunks = [];
|
|
47
|
+
for await (const chunk of process.stdin) {
|
|
48
|
+
chunks.push(chunk);
|
|
49
|
+
}
|
|
50
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
51
|
+
}
|
|
52
|
+
/** 把命令行 prompt 和 stdin 内容合并成最终 prompt。 */
|
|
53
|
+
function buildPrompt(promptFromArgs, stdinText) {
|
|
54
|
+
if (promptFromArgs && stdinText) {
|
|
55
|
+
return `${promptFromArgs}\n\n---\n${stdinText}`;
|
|
56
|
+
}
|
|
57
|
+
return promptFromArgs || stdinText;
|
|
58
|
+
}
|
|
32
59
|
async function main() {
|
|
33
60
|
// argv 前两个是 node 路径和脚本路径,从第 3 个起才是用户输入
|
|
34
61
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -50,10 +77,26 @@ async function main() {
|
|
|
50
77
|
if (command === 'config') {
|
|
51
78
|
process.exit(printConfig());
|
|
52
79
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
const promptFromArgs = args.positionals.join(' ').trim();
|
|
81
|
+
const stdinText = (await readStdin()).trim();
|
|
82
|
+
const prompt = buildPrompt(promptFromArgs, stdinText);
|
|
83
|
+
// 有 prompt 或 stdin 时,走非交互式单次调用
|
|
84
|
+
if (prompt) {
|
|
85
|
+
const result = loadConfig();
|
|
86
|
+
if (!result.ok) {
|
|
87
|
+
console.error(result.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const { config } = result;
|
|
91
|
+
const ai = createAIClient(config);
|
|
92
|
+
const confirm = args.yes
|
|
93
|
+
? () => Promise.resolve(true)
|
|
94
|
+
: async (preview) => {
|
|
95
|
+
console.error(`\n[非交互式模式] 需要确认以下操作,请添加 --yes 自动批准:\n${preview}`);
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
await runOnce({ ai, prompt, confirm });
|
|
99
|
+
return;
|
|
57
100
|
}
|
|
58
101
|
// 无参数:进入交互式会话循环。
|
|
59
102
|
await startRepl();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|