@gird/ccli 2.19.0 → 2.21.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.
Files changed (122) hide show
  1. package/README.md +4 -0
  2. package/dist/agent/ai.js.map +1 -1
  3. package/dist/agent/model-catalog.js +100 -0
  4. package/dist/agent/model-catalog.js.map +1 -0
  5. package/dist/agent/os.js +291 -2
  6. package/dist/agent/os.js.map +1 -1
  7. package/dist/agent/providers/anthropic.js +112 -4
  8. package/dist/agent/providers/anthropic.js.map +1 -1
  9. package/dist/agent/rag/db.js +142 -0
  10. package/dist/agent/rag/db.js.map +1 -0
  11. package/dist/agent/rag/embedding.js +135 -0
  12. package/dist/agent/rag/embedding.js.map +1 -0
  13. package/dist/agent/rag/indexer.js +104 -0
  14. package/dist/agent/rag/indexer.js.map +1 -0
  15. package/dist/agent/rag/parser.js +75 -0
  16. package/dist/agent/rag/parser.js.map +1 -0
  17. package/dist/agent/rag/search.js +47 -0
  18. package/dist/agent/rag/search.js.map +1 -0
  19. package/dist/cli/args/index.js +30 -0
  20. package/dist/cli/args/index.js.map +1 -1
  21. package/dist/cli/repl/index.js +4 -3
  22. package/dist/cli/repl/index.js.map +1 -1
  23. package/dist/cli/run/index.js +27 -18
  24. package/dist/cli/run/index.js.map +1 -1
  25. package/dist/cli/run/task.js +188 -32
  26. package/dist/cli/run/task.js.map +1 -1
  27. package/dist/cli/session/checkpoint.js +50 -34
  28. package/dist/cli/session/checkpoint.js.map +1 -1
  29. package/dist/cli/slash/config/account.js +84 -0
  30. package/dist/cli/slash/config/account.js.map +1 -0
  31. package/dist/cli/slash/config/alias.js +96 -0
  32. package/dist/cli/slash/config/alias.js.map +1 -0
  33. package/dist/cli/slash/config/login.js +80 -0
  34. package/dist/cli/slash/config/login.js.map +1 -0
  35. package/dist/cli/slash/config/model.js +50 -6
  36. package/dist/cli/slash/config/model.js.map +1 -1
  37. package/dist/cli/slash/config/models.js +45 -0
  38. package/dist/cli/slash/config/models.js.map +1 -0
  39. package/dist/cli/slash/index.js +25 -1
  40. package/dist/cli/slash/index.js.map +1 -1
  41. package/dist/cli/slash/system/agent.js +1 -1
  42. package/dist/cli/slash/system/agent.js.map +1 -1
  43. package/dist/cli/slash/system/compact.js +1 -1
  44. package/dist/cli/slash/system/compact.js.map +1 -1
  45. package/dist/cli/slash/system/goal.js +1 -1
  46. package/dist/cli/slash/system/goal.js.map +1 -1
  47. package/dist/cli/slash/system/help.js +6 -0
  48. package/dist/cli/slash/system/help.js.map +1 -1
  49. package/dist/cli/slash/system/indexer.js +25 -0
  50. package/dist/cli/slash/system/indexer.js.map +1 -0
  51. package/dist/cli/slash/system/plan.js +1 -1
  52. package/dist/cli/slash/system/plan.js.map +1 -1
  53. package/dist/cli/slash/system/revert.js +7 -5
  54. package/dist/cli/slash/system/revert.js.map +1 -1
  55. package/dist/cli/slash/system/upgrade.js +13 -0
  56. package/dist/cli/slash/system/upgrade.js.map +1 -0
  57. package/dist/cli/stats/index.js +6 -1
  58. package/dist/cli/stats/index.js.map +1 -1
  59. package/dist/cli/stats/usage.js +215 -0
  60. package/dist/cli/stats/usage.js.map +1 -0
  61. package/dist/cli/upgrade.js +90 -0
  62. package/dist/cli/upgrade.js.map +1 -0
  63. package/dist/config/config.js +344 -10
  64. package/dist/config/config.js.map +1 -1
  65. package/dist/config/credentials.js +104 -0
  66. package/dist/config/credentials.js.map +1 -0
  67. package/dist/config/project-context.js +135 -0
  68. package/dist/config/project-context.js.map +1 -0
  69. package/dist/harness/index.js +11 -3
  70. package/dist/harness/index.js.map +1 -1
  71. package/dist/index.js +31 -2
  72. package/dist/index.js.map +1 -1
  73. package/dist/server/webui-content.js +2 -0
  74. package/dist/server/webui-content.js.map +1 -0
  75. package/dist/server/webui.js +15 -4
  76. package/dist/server/webui.js.map +1 -1
  77. package/dist/tools/{skill-tool.js → agent/skill-tool.js} +1 -1
  78. package/dist/tools/agent/skill-tool.js.map +1 -0
  79. package/dist/tools/{todo.js → agent/todo.js} +1 -1
  80. package/dist/tools/agent/todo.js.map +1 -0
  81. package/dist/tools/agent/web-fetch.js.map +1 -0
  82. package/dist/tools/{edit-file.js → fs/edit-file.js} +8 -14
  83. package/dist/tools/fs/edit-file.js.map +1 -0
  84. package/dist/tools/fs/glob.js.map +1 -0
  85. package/dist/tools/{grep.js → fs/grep.js} +1 -1
  86. package/dist/tools/fs/grep.js.map +1 -0
  87. package/dist/tools/fs/list-dir.js +95 -0
  88. package/dist/tools/fs/list-dir.js.map +1 -0
  89. package/dist/tools/{read-file.js → fs/read-file.js} +1 -1
  90. package/dist/tools/fs/read-file.js.map +1 -0
  91. package/dist/tools/{write-file.js → fs/write-file.js} +8 -15
  92. package/dist/tools/fs/write-file.js.map +1 -0
  93. package/dist/tools/index.js +13 -11
  94. package/dist/tools/index.js.map +1 -1
  95. package/dist/tools/rag/search.js +65 -0
  96. package/dist/tools/rag/search.js.map +1 -0
  97. package/dist/tools/shared.js.map +1 -1
  98. package/dist/tools/shell/bash-background.js.map +1 -0
  99. package/dist/tools/shell/bash-output.js.map +1 -0
  100. package/dist/tools/shell/bash.js.map +1 -0
  101. package/dist/tools/shell/kill-bash.js.map +1 -0
  102. package/package.json +3 -1
  103. package/dist/tools/bash-background.js.map +0 -1
  104. package/dist/tools/bash-output.js.map +0 -1
  105. package/dist/tools/bash.js.map +0 -1
  106. package/dist/tools/edit-file.js.map +0 -1
  107. package/dist/tools/glob.js.map +0 -1
  108. package/dist/tools/grep.js.map +0 -1
  109. package/dist/tools/kill-bash.js.map +0 -1
  110. package/dist/tools/list-dir.js +0 -45
  111. package/dist/tools/list-dir.js.map +0 -1
  112. package/dist/tools/read-file.js.map +0 -1
  113. package/dist/tools/skill-tool.js.map +0 -1
  114. package/dist/tools/todo.js.map +0 -1
  115. package/dist/tools/web-fetch.js.map +0 -1
  116. package/dist/tools/write-file.js.map +0 -1
  117. /package/dist/tools/{web-fetch.js → agent/web-fetch.js} +0 -0
  118. /package/dist/tools/{glob.js → fs/glob.js} +0 -0
  119. /package/dist/tools/{bash-background.js → shell/bash-background.js} +0 -0
  120. /package/dist/tools/{bash-output.js → shell/bash-output.js} +0 -0
  121. /package/dist/tools/{bash.js → shell/bash.js} +0 -0
  122. /package/dist/tools/{kill-bash.js → shell/kill-bash.js} +0 -0
package/README.md CHANGED
@@ -26,6 +26,8 @@ ccli --yes "在当前目录创建 test.txt" # 自动确认破坏性工具调
26
26
  ccli --role coder "review 这段代码" # 使用 coding 角色单次调用
27
27
  ccli serve # 启动本地 HTTP server + Web UI(默认 127.0.0.1:4000)
28
28
  ccli plugin # 列出已安装的插件包
29
+ ccli --docker my-sandbox # 使用指定的 Docker 容器作为隔离运行沙箱
30
+ ccli --remote ssh://user@host # 使用指定的 SSH 远程连接进行开发
29
31
  ```
30
32
 
31
33
  > ⚠️ `--yes` 会自动批准本次调用内**所有**需确认的工具调用(写文件、编辑文件、bash、MCP 工具),包括 `rm`、`git push` 这类高风险操作。请只在任务范围明确、且目录可丢弃或改动已提交时使用;拿不准就不加,逐项确认。
@@ -38,6 +40,8 @@ ccli plugin # 列出已安装的插件包
38
40
 
39
41
  对话里输入以 `#` 开头的一行,可把内容**追加进项目 `CCLI.md` 并立即生效**(不发给 AI),下次启动自动带上,例如 `# 本项目用 pnpm`。输入 `/memories` 可查看当前会话实际注入了哪些记忆(按层级列出)。
40
42
 
43
+ **项目目录结构**:CCLI 构造 system prompt 时,会自动附带当前工作目录的紧凑 2 级目录树(隐藏目录折叠、跳过 `node_modules/.git/dist/.ccli`)。这让模型在回答「分析这个项目」时,先看到全局结构而不是只钻进某个隐藏配置目录。
44
+
41
45
  **技能(自定义提示词模板)**:在 `.ccli/skills/`(项目)或 `~/.ccli/skills/`(用户)放置 `*.md` 文件即可定义一个「技能」——一段可复用的提示词,支持 `$ARGUMENTS` 参数占位。用 `/skills` 列出、`/skill <名称> [参数]` 调用(会展开成一条消息交给 AI)。md 可带 `name` / `description` 的 YAML frontmatter;同名时项目技能覆盖用户技能。技能也可以是**目录技能包** `<名>/SKILL.md` + 附属文件(脚本/模板/资料,调用时告知模型路径供 `read_file`),嵌套 `SKILL.md` 成为**子技能**。除了手动 `/skill`,**模型也会在意图匹配时自动调用技能**(frontmatter 加 `disableModelInvocation: true` 可只保留手动调用)。
42
46
 
43
47
  除了手动 `/skill`,**模型也会在意图匹配时自动调用技能**(frontmatter 加 `disableModelInvocation: true` 可只保留手动调用)。
@@ -1 +1 @@
1
- {"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/agent/ai.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAyErD,sBAAsB;AACtB,MAAM,UAAU,cAAc,CAAC,MAAc;IACzC,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAID;;;;;;;GAOG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/agent/ai.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AA6ErD,sBAAsB;AACtB,MAAM,UAAU,cAAc,CAAC,MAAc;IACzC,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAID;;;;;;;GAOG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * 模型目录缓存。
3
+ *
4
+ * 把当前 provider 的可用模型列表缓存到 `~/.ccli/model-cache.json`,
5
+ * 避免每次 `/model` / `/models` 都联网拉取。
6
+ *
7
+ * 缓存键为 `${provider}:${baseURL}`,区分同一个 provider 的不同兼容端点。
8
+ * 默认 TTL 24 小时;缓存缺失/过期/损坏时自动降级为实时拉取。
9
+ */
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ /** 缓存文件路径:~/.ccli/model-cache.json(懒计算,方便测试注入 HOME)。 */
14
+ function cacheFile() {
15
+ return join(homedir(), '.ccli', 'model-cache.json');
16
+ }
17
+ /** 默认缓存有效期:24 小时。 */
18
+ export const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
19
+ /**
20
+ * 计算缓存键。
21
+ * 同一个 provider 不同 baseURL(如 OpenAI 兼容服务)会分成不同桶,避免互相污染。
22
+ */
23
+ export function cacheKey(provider, baseURL) {
24
+ return baseURL ? `${provider}:${baseURL}` : provider;
25
+ }
26
+ /** 读取缓存文件;不存在或损坏返回空对象。 */
27
+ function readCache() {
28
+ const path = cacheFile();
29
+ if (!existsSync(path))
30
+ return {};
31
+ try {
32
+ const raw = readFileSync(path, 'utf8');
33
+ if (!raw.trim())
34
+ return {};
35
+ const parsed = JSON.parse(raw);
36
+ return parsed && typeof parsed === 'object' ? parsed : {};
37
+ }
38
+ catch {
39
+ return {};
40
+ }
41
+ }
42
+ /** 写入缓存文件;失败静默(不阻断会话)。 */
43
+ function writeCache(cache) {
44
+ try {
45
+ mkdirSync(join(homedir(), '.ccli'), { recursive: true });
46
+ writeFileSync(cacheFile(), JSON.stringify(cache, null, 2) + '\n', 'utf8');
47
+ }
48
+ catch {
49
+ // 缓存写入失败不抛错,下次直接联网拉取即可
50
+ }
51
+ }
52
+ /** 判断缓存条目是否在有效期内。 */
53
+ function isFresh(entry, ttlMs) {
54
+ if (!entry || !entry.cachedAt || !Array.isArray(entry.models)) {
55
+ return false;
56
+ }
57
+ try {
58
+ const cachedAt = new Date(entry.cachedAt).getTime();
59
+ return Number.isFinite(cachedAt) && Date.now() - cachedAt <= ttlMs;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ /**
66
+ * 获取模型列表:优先读缓存,未命中/过期/刷新时调 AIClient.listModels() 并落盘。
67
+ *
68
+ * @param ai 当前 AI 客户端
69
+ * @param config provider 与 baseURL(来自 config)
70
+ * @param options refresh 强制刷新;ttlMs 自定义有效期
71
+ * @returns 模型 ID 列表
72
+ */
73
+ export async function getCachedModels(ai, config, options = {}) {
74
+ const key = cacheKey(config.provider, config.baseURL);
75
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
76
+ const cache = readCache();
77
+ const entry = cache[key];
78
+ if (!options.refresh && isFresh(entry, ttlMs)) {
79
+ return entry.models;
80
+ }
81
+ const models = await ai.listModels();
82
+ cache[key] = { cachedAt: new Date().toISOString(), models };
83
+ writeCache(cache);
84
+ return models;
85
+ }
86
+ /**
87
+ * 强制清空模型缓存。
88
+ * - 不传参数:清空全部 provider。
89
+ * - 传 provider(和可选 baseURL):只清空对应桶。
90
+ */
91
+ export function clearModelCache(provider, baseURL) {
92
+ if (!provider) {
93
+ writeCache({});
94
+ return;
95
+ }
96
+ const cache = readCache();
97
+ delete cache[cacheKey(provider, baseURL)];
98
+ writeCache(cache);
99
+ }
100
+ //# sourceMappingURL=model-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-catalog.js","sourceRoot":"","sources":["../../src/agent/model-catalog.ts"],"names":[],"mappings":"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"}
package/dist/agent/os.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spawn as childSpawn } from 'node:child_process';
1
+ import childProcess from 'node:child_process';
2
2
  import { promises as fsPromises, existsSync } from 'node:fs';
3
3
  import { resolve as pathResolve, dirname as pathDirname, relative as pathRelative, join as pathJoin, sep as pathSep } from 'node:path';
4
4
  export class LocalOS {
@@ -59,11 +59,300 @@ export class LocalOS {
59
59
  return process.env.SHELL || '/bin/bash';
60
60
  }
61
61
  async spawn(command, args, options) {
62
- const proc = childSpawn(command, args, {
62
+ const proc = childProcess.spawn(command, args, {
63
63
  cwd: options?.cwd,
64
64
  env: options?.env ?? process.env
65
65
  });
66
66
  return proc;
67
67
  }
68
68
  }
69
+ import { posix } from 'node:path';
70
+ function runDockerCmd(args, stdinInput) {
71
+ return new Promise((resolve) => {
72
+ const proc = childProcess.spawn('docker', ['exec', ...args]);
73
+ let stdout = '';
74
+ let stderr = '';
75
+ proc.stdout.on('data', (data) => {
76
+ stdout += data.toString('utf8');
77
+ });
78
+ proc.stderr.on('data', (data) => {
79
+ stderr += data.toString('utf8');
80
+ });
81
+ proc.on('close', (code) => {
82
+ resolve({ stdout, stderr, code: code ?? 0 });
83
+ });
84
+ proc.on('error', (err) => {
85
+ resolve({ stdout, stderr: stderr + err.message, code: -1 });
86
+ });
87
+ if (stdinInput !== undefined) {
88
+ proc.stdin.write(stdinInput);
89
+ proc.stdin.end();
90
+ }
91
+ });
92
+ }
93
+ export class DockerOS {
94
+ container;
95
+ constructor(container) {
96
+ this.container = container;
97
+ }
98
+ async exists(path) {
99
+ const { code } = await runDockerCmd([this.container, 'test', '-e', path]);
100
+ return code === 0;
101
+ }
102
+ async isDirectory(path) {
103
+ const { code } = await runDockerCmd([this.container, 'test', '-d', path]);
104
+ return code === 0;
105
+ }
106
+ async readFile(path, _encoding = 'utf8') {
107
+ const { stdout, code, stderr } = await runDockerCmd([this.container, 'cat', path]);
108
+ if (code !== 0) {
109
+ throw new Error(`Failed to read file ${path} inside docker: ${stderr}`);
110
+ }
111
+ return stdout;
112
+ }
113
+ async writeFile(path, content, _encoding = 'utf8') {
114
+ const { code, stderr } = await runDockerCmd(['-i', this.container, 'dd', `of=${path}`], content);
115
+ if (code !== 0) {
116
+ throw new Error(`Failed to write file ${path} inside docker: ${stderr}`);
117
+ }
118
+ }
119
+ async mkdir(path, options) {
120
+ const args = [this.container, 'mkdir'];
121
+ if (options?.recursive ?? true) {
122
+ args.push('-p');
123
+ }
124
+ args.push(path);
125
+ const { code, stderr } = await runDockerCmd(args);
126
+ if (code !== 0) {
127
+ throw new Error(`Failed to mkdir ${path} inside docker: ${stderr}`);
128
+ }
129
+ }
130
+ async readdir(path) {
131
+ const { stdout, code, stderr } = await runDockerCmd([this.container, 'ls', '-A', '-1', '-p', path]);
132
+ if (code !== 0) {
133
+ throw new Error(`Failed to readdir ${path} inside docker: ${stderr}`);
134
+ }
135
+ const lines = stdout.split('\n').map(l => l.trim()).filter(Boolean);
136
+ return lines.map(line => {
137
+ const isDir = line.endsWith('/');
138
+ const name = isDir ? line.slice(0, -1) : line;
139
+ return {
140
+ name,
141
+ isDirectory: () => isDir
142
+ };
143
+ });
144
+ }
145
+ async stat(path) {
146
+ const sizeRes = await runDockerCmd([this.container, 'stat', '-c', '%s', path]);
147
+ if (sizeRes.code !== 0) {
148
+ throw new Error(`Failed to stat ${path} inside docker: ${sizeRes.stderr}`);
149
+ }
150
+ const size = parseInt(sizeRes.stdout.trim(), 10);
151
+ const isDir = await this.isDirectory(path);
152
+ return {
153
+ size: isNaN(size) ? 0 : size,
154
+ isDirectory: () => isDir
155
+ };
156
+ }
157
+ async unlink(path) {
158
+ const { code, stderr } = await runDockerCmd([this.container, 'rm', '-f', path]);
159
+ if (code !== 0) {
160
+ throw new Error(`Failed to unlink ${path} inside docker: ${stderr}`);
161
+ }
162
+ }
163
+ resolve(...paths) {
164
+ return posix.resolve(...paths);
165
+ }
166
+ dirname(path) {
167
+ return posix.dirname(path);
168
+ }
169
+ relative(from, to) {
170
+ return posix.relative(from, to);
171
+ }
172
+ join(...paths) {
173
+ return posix.join(...paths);
174
+ }
175
+ get sep() {
176
+ return '/';
177
+ }
178
+ get defaultShell() {
179
+ return '/bin/sh';
180
+ }
181
+ async spawn(command, args, options) {
182
+ const dockerArgs = ['exec'];
183
+ if (options?.cwd) {
184
+ dockerArgs.push('-w', options.cwd);
185
+ }
186
+ if (options?.env) {
187
+ for (const [k, v] of Object.entries(options.env)) {
188
+ if (v !== undefined) {
189
+ dockerArgs.push('-e', `${k}=${v}`);
190
+ }
191
+ }
192
+ }
193
+ dockerArgs.push(this.container, command, ...args);
194
+ const proc = childProcess.spawn('docker', dockerArgs);
195
+ return proc;
196
+ }
197
+ }
198
+ export function parseSshUrl(url) {
199
+ const cleaned = url.startsWith('ssh://') ? url.slice(6) : url;
200
+ const atIdx = cleaned.indexOf('@');
201
+ if (atIdx === -1) {
202
+ throw new Error(`Invalid SSH URL: ${url}. Missing 'user@host' format.`);
203
+ }
204
+ const user = cleaned.slice(0, atIdx);
205
+ const remaining = cleaned.slice(atIdx + 1);
206
+ const colonIdx = remaining.lastIndexOf(':');
207
+ if (colonIdx === -1) {
208
+ return { user, host: remaining };
209
+ }
210
+ const host = remaining.slice(0, colonIdx);
211
+ const portStr = remaining.slice(colonIdx + 1);
212
+ const port = parseInt(portStr, 10);
213
+ if (isNaN(port)) {
214
+ return { user, host };
215
+ }
216
+ return { user, host, port };
217
+ }
218
+ function escapeShellArg(arg) {
219
+ return `'` + arg.replace(/'/g, `'"'"`) + `'`;
220
+ }
221
+ function runSshCmd(sshConfig, remoteCmd, stdinInput) {
222
+ return new Promise((resolve) => {
223
+ const sshArgs = [];
224
+ if (sshConfig.port !== undefined) {
225
+ sshArgs.push('-p', String(sshConfig.port));
226
+ }
227
+ sshArgs.push('-T', `${sshConfig.user}@${sshConfig.host}`, remoteCmd);
228
+ const proc = childProcess.spawn('ssh', sshArgs);
229
+ let stdout = '';
230
+ let stderr = '';
231
+ proc.stdout.on('data', (data) => {
232
+ stdout += data.toString('utf8');
233
+ });
234
+ proc.stderr.on('data', (data) => {
235
+ stderr += data.toString('utf8');
236
+ });
237
+ proc.on('close', (code) => {
238
+ resolve({ stdout, stderr, code: code ?? 0 });
239
+ });
240
+ proc.on('error', (err) => {
241
+ resolve({ stdout, stderr: stderr + err.message, code: -1 });
242
+ });
243
+ if (stdinInput !== undefined) {
244
+ proc.stdin.write(stdinInput);
245
+ proc.stdin.end();
246
+ }
247
+ });
248
+ }
249
+ export class SshOS {
250
+ url;
251
+ sshConfig;
252
+ constructor(url) {
253
+ this.url = url;
254
+ this.sshConfig = parseSshUrl(url);
255
+ }
256
+ async exists(path) {
257
+ const { code } = await runSshCmd(this.sshConfig, `test -e ${escapeShellArg(path)}`);
258
+ return code === 0;
259
+ }
260
+ async isDirectory(path) {
261
+ const { code } = await runSshCmd(this.sshConfig, `test -d ${escapeShellArg(path)}`);
262
+ return code === 0;
263
+ }
264
+ async readFile(path, _encoding = 'utf8') {
265
+ const { stdout, code, stderr } = await runSshCmd(this.sshConfig, `cat ${escapeShellArg(path)}`);
266
+ if (code !== 0) {
267
+ throw new Error(`Failed to read file ${path} via SSH: ${stderr}`);
268
+ }
269
+ return stdout;
270
+ }
271
+ async writeFile(path, content, _encoding = 'utf8') {
272
+ const { code, stderr } = await runSshCmd(this.sshConfig, `dd of=${escapeShellArg(path)}`, content);
273
+ if (code !== 0) {
274
+ throw new Error(`Failed to write file ${path} via SSH: ${stderr}`);
275
+ }
276
+ }
277
+ async mkdir(path, options) {
278
+ const { code, stderr } = await runSshCmd(this.sshConfig, `mkdir ${options?.recursive ?? true ? '-p' : ''} ${escapeShellArg(path)}`);
279
+ if (code !== 0) {
280
+ throw new Error(`Failed to mkdir ${path} via SSH: ${stderr}`);
281
+ }
282
+ }
283
+ async readdir(path) {
284
+ const { stdout, code, stderr } = await runSshCmd(this.sshConfig, `ls -A -1 -p ${escapeShellArg(path)}`);
285
+ if (code !== 0) {
286
+ throw new Error(`Failed to readdir ${path} via SSH: ${stderr}`);
287
+ }
288
+ const lines = stdout.split('\n').map(l => l.trim()).filter(Boolean);
289
+ return lines.map(line => {
290
+ const isDir = line.endsWith('/');
291
+ const name = isDir ? line.slice(0, -1) : line;
292
+ return {
293
+ name,
294
+ isDirectory: () => isDir
295
+ };
296
+ });
297
+ }
298
+ async stat(path) {
299
+ const sizeRes = await runSshCmd(this.sshConfig, `stat -c "%s" ${escapeShellArg(path)}`);
300
+ if (sizeRes.code !== 0) {
301
+ throw new Error(`Failed to stat ${path} via SSH: ${sizeRes.stderr}`);
302
+ }
303
+ const size = parseInt(sizeRes.stdout.trim(), 10);
304
+ const isDir = await this.isDirectory(path);
305
+ return {
306
+ size: isNaN(size) ? 0 : size,
307
+ isDirectory: () => isDir
308
+ };
309
+ }
310
+ async unlink(path) {
311
+ const { code, stderr } = await runSshCmd(this.sshConfig, `rm -f ${escapeShellArg(path)}`);
312
+ if (code !== 0) {
313
+ throw new Error(`Failed to unlink ${path} via SSH: ${stderr}`);
314
+ }
315
+ }
316
+ resolve(...paths) {
317
+ return posix.resolve(...paths);
318
+ }
319
+ dirname(path) {
320
+ return posix.dirname(path);
321
+ }
322
+ relative(from, to) {
323
+ return posix.relative(from, to);
324
+ }
325
+ join(...paths) {
326
+ return posix.join(...paths);
327
+ }
328
+ get sep() {
329
+ return '/';
330
+ }
331
+ get defaultShell() {
332
+ return '/bin/sh';
333
+ }
334
+ async spawn(command, args, options) {
335
+ const sshArgs = [];
336
+ if (this.sshConfig.port !== undefined) {
337
+ sshArgs.push('-p', String(this.sshConfig.port));
338
+ }
339
+ sshArgs.push('-t', `${this.sshConfig.user}@${this.sshConfig.host}`);
340
+ let envPrefix = '';
341
+ if (options?.env) {
342
+ for (const [k, v] of Object.entries(options.env)) {
343
+ if (v !== undefined) {
344
+ envPrefix += `export ${k}=${escapeShellArg(v)} && `;
345
+ }
346
+ }
347
+ }
348
+ let remoteCmd = '';
349
+ if (options?.cwd) {
350
+ remoteCmd += `cd ${escapeShellArg(options.cwd)} && `;
351
+ }
352
+ remoteCmd += `${command} ${args.map(escapeShellArg).join(' ')}`;
353
+ sshArgs.push(envPrefix + remoteCmd);
354
+ const proc = childProcess.spawn('ssh', sshArgs);
355
+ return proc;
356
+ }
357
+ }
69
358
  //# sourceMappingURL=os.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"os.js","sourceRoot":"","sources":["../../src/agent/os.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"os.js","sourceRoot":"","sources":["../../src/agent/os.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { projectToolPairedMessages } from '../tool-projection.js';
4
4
  export class AnthropicClient {
5
5
  client;
6
6
  model;
7
+ promptCache;
7
8
  /** 保存 Anthropic SDK 实例;baseURL 仅在用户配置代理或兼容网关时生效。 */
8
9
  constructor(config) {
9
10
  this.client = new Anthropic({
@@ -11,6 +12,9 @@ export class AnthropicClient {
11
12
  baseURL: config.baseURL,
12
13
  });
13
14
  this.model = config.model;
15
+ // Anthropic Prompt Cache 默认开启;Claude 2 / instant 等老模型不支持,关闭
16
+ this.promptCache =
17
+ config.promptCache ?? !/claude-2|claude-instant/i.test(config.model);
14
18
  }
15
19
  /** 返回当前 Claude 模型名,保持与 OpenAIClient 的统一接口。 */
16
20
  getModel() {
@@ -46,7 +50,7 @@ export class AnthropicClient {
46
50
  * 再把流式事件重新聚合成 CCLI 内部统一的 AssistantTurn。
47
51
  */
48
52
  async chat(messages, onDelta, options, onReasoning, onToolArgumentDelta) {
49
- const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
53
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages, this.promptCache);
50
54
  const tools = toAnthropicTools(options?.allowedTools);
51
55
  const stream = await this.client.messages.create({
52
56
  model: this.model,
@@ -69,11 +73,16 @@ export class AnthropicClient {
69
73
  // Anthropic 流式协议:input_tokens 只出现在 message_start 事件,
70
74
  // message_delta 只带 output_tokens——两处都要读才能拼出完整 usage。
71
75
  let inputTokens = 0;
76
+ let cacheCreationTokens = 0;
77
+ let cacheReadTokens = 0;
72
78
  for await (const event of stream) {
73
79
  if (event.type === 'message_start') {
74
80
  const u = event.message?.usage;
75
- if (u)
81
+ if (u) {
76
82
  inputTokens = u.input_tokens ?? 0;
83
+ cacheCreationTokens = u.cache_creation_input_tokens ?? 0;
84
+ cacheReadTokens = u.cache_read_input_tokens ?? 0;
85
+ }
77
86
  }
78
87
  else if (event.type === 'content_block_delta') {
79
88
  if (event.delta.type === 'text_delta') {
@@ -112,6 +121,12 @@ export class AnthropicClient {
112
121
  completionTokens,
113
122
  totalTokens: promptTokens + completionTokens,
114
123
  };
124
+ if (cacheCreationTokens > 0) {
125
+ usage.cacheCreationTokens = cacheCreationTokens;
126
+ }
127
+ if (cacheReadTokens > 0) {
128
+ usage.cacheReadTokens = cacheReadTokens;
129
+ }
115
130
  }
116
131
  }
117
132
  }
@@ -128,7 +143,7 @@ export class AnthropicClient {
128
143
  * Anthropic 不接受 `role:"system"` 混在 messages 里,也要求 tool_result 作为 user
129
144
  * 内容块出现,所以这里集中做一次协议投影,避免业务层到处写 provider 分支。
130
145
  */
131
- export function toAnthropicMessages(messages) {
146
+ export function toAnthropicMessages(messages, promptCache = false) {
132
147
  const projectedMessages = projectToolPairedMessages(messages);
133
148
  const systemParts = [];
134
149
  const anthropicMessages = [];
@@ -213,7 +228,100 @@ export function toAnthropicMessages(messages) {
213
228
  }
214
229
  }
215
230
  }
216
- return { system: systemParts.join('\n\n'), messages: anthropicMessages };
231
+ // system prompt:开启缓存时转成 block 数组并在最后一个 block 上标记 cache_control
232
+ const joinedSystem = systemParts.join('\n\n');
233
+ const system = promptCache && systemParts.length > 0
234
+ ? systemParts.map((text, i) => ({
235
+ type: 'text',
236
+ text,
237
+ ...(i === systemParts.length - 1
238
+ ? { cache_control: { type: 'ephemeral' } }
239
+ : {}),
240
+ }))
241
+ : joinedSystem;
242
+ // 给历史上下文里较长且非最后的 user 消息加 cache_control。
243
+ // Anthropic 限制整段请求最多 4 个 cache_control 标记;system 已用 1 个,
244
+ // 剩余预算留给长 user 消息(通常是最划算的回退点)。
245
+ const userIndices = [];
246
+ for (let i = 0; i < anthropicMessages.length; i++) {
247
+ if (anthropicMessages[i].role === 'user') {
248
+ userIndices.push(i);
249
+ }
250
+ }
251
+ const systemCacheCount = promptCache && systemParts.length > 0 ? 1 : 0;
252
+ const remainingCacheBudget = 4 - systemCacheCount;
253
+ let applied = 0;
254
+ for (let ui = 0; ui < userIndices.length && applied < remainingCacheBudget; ui++) {
255
+ // 当前用户输入是最后一条 user 消息,不需要缓存
256
+ if (ui === userIndices.length - 1)
257
+ continue;
258
+ const idx = userIndices[ui];
259
+ const msg = anthropicMessages[idx];
260
+ const textLength = estimateUserMessageTextLength(msg.content);
261
+ if (textLength <= 1024)
262
+ continue;
263
+ const content = msg.content;
264
+ if (typeof content === 'string') {
265
+ anthropicMessages[idx] = {
266
+ role: 'user',
267
+ content: [
268
+ {
269
+ type: 'text',
270
+ text: content,
271
+ cache_control: { type: 'ephemeral' },
272
+ },
273
+ ],
274
+ };
275
+ }
276
+ else if (Array.isArray(content)) {
277
+ const lastTextIdx = content.findLastIndex((b) => b.type === 'text');
278
+ if (lastTextIdx >= 0) {
279
+ const newContent = content.slice();
280
+ const block = content[lastTextIdx];
281
+ newContent[lastTextIdx] = {
282
+ type: 'text',
283
+ text: block.text,
284
+ cache_control: { type: 'ephemeral' },
285
+ };
286
+ anthropicMessages[idx] = {
287
+ role: 'user',
288
+ content: newContent,
289
+ };
290
+ }
291
+ }
292
+ applied++;
293
+ }
294
+ return { system, messages: anthropicMessages };
295
+ }
296
+ /** 估算 user 消息里的文本长度,用于决定是否值得加 cache_control。 */
297
+ function estimateUserMessageTextLength(content) {
298
+ if (typeof content === 'string')
299
+ return content.length;
300
+ let length = 0;
301
+ for (const block of content) {
302
+ if (block.type === 'text') {
303
+ length += block.text.length;
304
+ }
305
+ else if (block.type === 'tool_result') {
306
+ const c = block.content;
307
+ if (typeof c === 'string') {
308
+ length += c.length;
309
+ }
310
+ else if (Array.isArray(c)) {
311
+ for (const inner of c) {
312
+ if (typeof inner === 'object' &&
313
+ inner &&
314
+ inner.type === 'text') {
315
+ length += inner.text?.length ?? 0;
316
+ }
317
+ else if (typeof inner === 'string') {
318
+ length += inner.length;
319
+ }
320
+ }
321
+ }
322
+ }
323
+ }
324
+ return length;
217
325
  }
218
326
  /** 把内部工具定义转成 Anthropic 的 tool 格式。
219
327
  * 传 allowedNames 时只转换这些工具(与 OpenAI 路径一致)。 */