@gird/ccli 2.15.0 → 2.18.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 (91) hide show
  1. package/README.md +22 -6
  2. package/dist/agent/ai.js +10 -345
  3. package/dist/agent/ai.js.map +1 -1
  4. package/dist/agent/providers/anthropic.js +258 -0
  5. package/dist/agent/providers/anthropic.js.map +1 -0
  6. package/dist/agent/providers/gemini.js +14 -0
  7. package/dist/agent/providers/gemini.js.map +1 -0
  8. package/dist/agent/providers/openai.js +110 -0
  9. package/dist/agent/providers/openai.js.map +1 -0
  10. package/dist/agent/tool-projection.js +57 -0
  11. package/dist/agent/tool-projection.js.map +1 -0
  12. package/dist/cli/args/index.js +4 -0
  13. package/dist/cli/args/index.js.map +1 -1
  14. package/dist/cli/clipboard-image.js +51 -0
  15. package/dist/cli/clipboard-image.js.map +1 -0
  16. package/dist/cli/image-compress.js +62 -0
  17. package/dist/cli/image-compress.js.map +1 -0
  18. package/dist/cli/image-reference.js +190 -0
  19. package/dist/cli/image-reference.js.map +1 -0
  20. package/dist/cli/repl/index.js +110 -60
  21. package/dist/cli/repl/index.js.map +1 -1
  22. package/dist/cli/run/event-renderer.js +57 -0
  23. package/dist/cli/run/event-renderer.js.map +1 -0
  24. package/dist/cli/run/hooks.js +58 -19
  25. package/dist/cli/run/hooks.js.map +1 -1
  26. package/dist/cli/run/index.js +218 -9
  27. package/dist/cli/run/index.js.map +1 -1
  28. package/dist/cli/run/task.js +58 -1
  29. package/dist/cli/run/task.js.map +1 -1
  30. package/dist/cli/run/todo-panel.js +13 -0
  31. package/dist/cli/run/todo-panel.js.map +1 -1
  32. package/dist/cli/session/checkpoint.js +252 -0
  33. package/dist/cli/session/checkpoint.js.map +1 -0
  34. package/dist/cli/session/index.js +1 -1
  35. package/dist/cli/session/index.js.map +1 -1
  36. package/dist/cli/slash/index.js +26 -2
  37. package/dist/cli/slash/index.js.map +1 -1
  38. package/dist/cli/slash/session/load.js +3 -0
  39. package/dist/cli/slash/session/load.js.map +1 -1
  40. package/dist/cli/slash/session/replay.js +118 -0
  41. package/dist/cli/slash/session/replay.js.map +1 -0
  42. package/dist/cli/slash/session/save.js +1 -0
  43. package/dist/cli/slash/session/save.js.map +1 -1
  44. package/dist/cli/slash/system/agent.js +38 -104
  45. package/dist/cli/slash/system/agent.js.map +1 -1
  46. package/dist/cli/slash/system/compact.js +26 -5
  47. package/dist/cli/slash/system/compact.js.map +1 -1
  48. package/dist/cli/slash/system/goal.js +34 -34
  49. package/dist/cli/slash/system/goal.js.map +1 -1
  50. package/dist/cli/slash/system/help.js +3 -0
  51. package/dist/cli/slash/system/help.js.map +1 -1
  52. package/dist/cli/slash/system/plan.js +140 -0
  53. package/dist/cli/slash/system/plan.js.map +1 -0
  54. package/dist/cli/slash/system/revert.js +133 -0
  55. package/dist/cli/slash/system/revert.js.map +1 -0
  56. package/dist/config/config.js +57 -4
  57. package/dist/config/config.js.map +1 -1
  58. package/dist/config/plugins.js +378 -0
  59. package/dist/config/plugins.js.map +1 -0
  60. package/dist/config/skills.js +68 -6
  61. package/dist/config/skills.js.map +1 -1
  62. package/dist/harness/index.js +334 -0
  63. package/dist/harness/index.js.map +1 -0
  64. package/dist/harness/records.js +98 -0
  65. package/dist/harness/records.js.map +1 -0
  66. package/dist/index.js +87 -42
  67. package/dist/index.js.map +1 -1
  68. package/dist/tools/bash-background.js +2 -2
  69. package/dist/tools/bash-background.js.map +1 -1
  70. package/dist/tools/bash.js +3 -2
  71. package/dist/tools/bash.js.map +1 -1
  72. package/dist/tools/edit-file.js +8 -2
  73. package/dist/tools/edit-file.js.map +1 -1
  74. package/dist/tools/glob.js +4 -3
  75. package/dist/tools/glob.js.map +1 -1
  76. package/dist/tools/grep.js +4 -3
  77. package/dist/tools/grep.js.map +1 -1
  78. package/dist/tools/index.js +20 -0
  79. package/dist/tools/index.js.map +1 -1
  80. package/dist/tools/list-dir.js +3 -2
  81. package/dist/tools/list-dir.js.map +1 -1
  82. package/dist/tools/read-file.js +3 -2
  83. package/dist/tools/read-file.js.map +1 -1
  84. package/dist/tools/shared.js.map +1 -1
  85. package/dist/tools/todo.js +17 -5
  86. package/dist/tools/todo.js.map +1 -1
  87. package/dist/tools/web-fetch.js +95 -0
  88. package/dist/tools/web-fetch.js.map +1 -0
  89. package/dist/tools/write-file.js +8 -2
  90. package/dist/tools/write-file.js.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,258 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { toolDefs } from '../../tools/index.js';
3
+ import { projectToolPairedMessages } from '../tool-projection.js';
4
+ export class AnthropicClient {
5
+ client;
6
+ model;
7
+ /** 保存 Anthropic SDK 实例;baseURL 仅在用户配置代理或兼容网关时生效。 */
8
+ constructor(config) {
9
+ this.client = new Anthropic({
10
+ apiKey: config.apiKey,
11
+ baseURL: config.baseURL,
12
+ });
13
+ this.model = config.model;
14
+ }
15
+ /** 返回当前 Claude 模型名,保持与 OpenAIClient 的统一接口。 */
16
+ getModel() {
17
+ return this.model;
18
+ }
19
+ /** 只更新后续请求使用的模型名,不影响已存在的历史消息。 */
20
+ setModel(model) {
21
+ this.model = model;
22
+ }
23
+ /** Claude 3 及以后全系支持图像;老的 claude-2 / instant 不支持。 */
24
+ supportsVision() {
25
+ return !/claude-2|claude-instant/i.test(this.model);
26
+ }
27
+ /**
28
+ * Anthropic SDK 没有稳定的模型列表接口,返回常用模型。
29
+ * 用户仍可用 /model <完整名> 切换到列表外的模型。
30
+ */
31
+ async listModels() {
32
+ return [
33
+ 'claude-opus-4-5-20251101',
34
+ 'claude-sonnet-4-5-20251101',
35
+ 'claude-sonnet-4-5-20241022',
36
+ 'claude-3-5-sonnet-20241022',
37
+ 'claude-3-opus-20240229',
38
+ 'claude-3-sonnet-20240229',
39
+ 'claude-3-haiku-20240307',
40
+ ].sort();
41
+ }
42
+ /**
43
+ * 发送 Anthropic Messages 流式请求。
44
+ *
45
+ * 这里会先把 OpenAI 风格历史转换成 Anthropic 的 system/messages/tool_use 结构,
46
+ * 再把流式事件重新聚合成 CCLI 内部统一的 AssistantTurn。
47
+ */
48
+ async chat(messages, onDelta, options, onReasoning, onToolArgumentDelta) {
49
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
50
+ const tools = toAnthropicTools(options?.allowedTools);
51
+ const stream = await this.client.messages.create({
52
+ model: this.model,
53
+ max_tokens: 4096,
54
+ system,
55
+ messages: anthropicMessages,
56
+ stream: true,
57
+ ...(options?.disableTools
58
+ ? {}
59
+ : {
60
+ tools,
61
+ ...(options?.requireTools
62
+ ? { tool_choice: { type: 'any' } }
63
+ : {}),
64
+ }),
65
+ });
66
+ let content = '';
67
+ const acc = {};
68
+ let usage;
69
+ // Anthropic 流式协议:input_tokens 只出现在 message_start 事件,
70
+ // message_delta 只带 output_tokens——两处都要读才能拼出完整 usage。
71
+ let inputTokens = 0;
72
+ for await (const event of stream) {
73
+ if (event.type === 'message_start') {
74
+ const u = event.message?.usage;
75
+ if (u)
76
+ inputTokens = u.input_tokens ?? 0;
77
+ }
78
+ else if (event.type === 'content_block_delta') {
79
+ if (event.delta.type === 'text_delta') {
80
+ content += event.delta.text;
81
+ onDelta(event.delta.text);
82
+ }
83
+ else if (event.delta.type === 'thinking_delta') {
84
+ onReasoning?.(event.delta.thinking);
85
+ }
86
+ else if (event.delta.type === 'input_json_delta') {
87
+ const tc = acc[event.index];
88
+ if (tc) {
89
+ tc.arguments += event.delta.partial_json;
90
+ if (tc.name) {
91
+ onToolArgumentDelta?.(event.index, tc.name, tc.arguments);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ else if (event.type === 'content_block_start') {
97
+ if (event.content_block.type === 'tool_use') {
98
+ acc[event.index] = {
99
+ id: event.content_block.id,
100
+ name: event.content_block.name,
101
+ arguments: '',
102
+ };
103
+ }
104
+ }
105
+ else if (event.type === 'message_delta') {
106
+ const u = event.usage;
107
+ if (u) {
108
+ const promptTokens = inputTokens || (u.input_tokens ?? 0);
109
+ const completionTokens = u.output_tokens ?? 0;
110
+ usage = {
111
+ promptTokens,
112
+ completionTokens,
113
+ totalTokens: promptTokens + completionTokens,
114
+ };
115
+ }
116
+ }
117
+ }
118
+ const toolCalls = Object.keys(acc)
119
+ .map(Number)
120
+ .sort((a, b) => a - b)
121
+ .map((i) => acc[i]);
122
+ return { content, toolCalls, usage };
123
+ }
124
+ }
125
+ /**
126
+ * 把 OpenAI 风格的消息列表转成 Anthropic 的 system + messages。
127
+ *
128
+ * Anthropic 不接受 `role:"system"` 混在 messages 里,也要求 tool_result 作为 user
129
+ * 内容块出现,所以这里集中做一次协议投影,避免业务层到处写 provider 分支。
130
+ */
131
+ export function toAnthropicMessages(messages) {
132
+ const projectedMessages = projectToolPairedMessages(messages);
133
+ const systemParts = [];
134
+ const anthropicMessages = [];
135
+ const pendingToolUseIds = [];
136
+ for (const m of projectedMessages) {
137
+ if (m.role === 'system') {
138
+ if (typeof m.content === 'string') {
139
+ systemParts.push(m.content);
140
+ }
141
+ continue;
142
+ }
143
+ if (m.role === 'tool') {
144
+ // Anthropic 的 tool_result 必须放在 user 消息里
145
+ const toolCallId = m.tool_call_id ?? '';
146
+ if (!pendingToolUseIds.includes(toolCallId)) {
147
+ continue;
148
+ }
149
+ const resultText = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
150
+ anthropicMessages.push({
151
+ role: 'user',
152
+ content: [
153
+ {
154
+ type: 'tool_result',
155
+ tool_use_id: toolCallId,
156
+ content: resultText,
157
+ },
158
+ ],
159
+ });
160
+ pendingToolUseIds.splice(pendingToolUseIds.indexOf(toolCallId), 1);
161
+ continue;
162
+ }
163
+ if (m.role === 'assistant') {
164
+ const content = [];
165
+ if (typeof m.content === 'string' && m.content) {
166
+ content.push({ type: 'text', text: m.content });
167
+ }
168
+ const toolCalls = m.tool_calls;
169
+ if (Array.isArray(toolCalls)) {
170
+ for (const tc of toolCalls) {
171
+ if (!tc?.id || !tc?.function?.name) {
172
+ continue;
173
+ }
174
+ content.push({
175
+ type: 'tool_use',
176
+ id: tc.id,
177
+ name: tc.function?.name ?? '',
178
+ input: safeParse(tc.function?.arguments ?? '{}'),
179
+ });
180
+ pendingToolUseIds.push(tc.id);
181
+ }
182
+ }
183
+ anthropicMessages.push({ role: 'assistant', content });
184
+ continue;
185
+ }
186
+ if (m.role === 'user') {
187
+ if (typeof m.content === 'string') {
188
+ anthropicMessages.push({ role: 'user', content: m.content });
189
+ }
190
+ else if (Array.isArray(m.content)) {
191
+ // 多模态:把 OpenAI 风格的 text / image_url 片段翻译成 Anthropic block
192
+ const blocks = [];
193
+ for (const part of m.content) {
194
+ if (part?.type === 'text') {
195
+ blocks.push({ type: 'text', text: part.text });
196
+ }
197
+ else if (part?.type === 'image_url') {
198
+ const img = dataUrlToImageBlock(part.image_url?.url);
199
+ if (img)
200
+ blocks.push(img);
201
+ }
202
+ }
203
+ anthropicMessages.push({
204
+ role: 'user',
205
+ content: blocks.length > 0 ? blocks : '',
206
+ });
207
+ }
208
+ else {
209
+ anthropicMessages.push({
210
+ role: 'user',
211
+ content: JSON.stringify(m.content),
212
+ });
213
+ }
214
+ }
215
+ }
216
+ return { system: systemParts.join('\n\n'), messages: anthropicMessages };
217
+ }
218
+ /** 把内部工具定义转成 Anthropic 的 tool 格式。
219
+ * 传 allowedNames 时只转换这些工具(与 OpenAI 路径一致)。 */
220
+ export function toAnthropicTools(allowedNames) {
221
+ return toolDefs(allowedNames).map((t) => ({
222
+ name: t.function.name,
223
+ description: t.function.description,
224
+ input_schema: {
225
+ type: 'object',
226
+ ...t.function.parameters,
227
+ },
228
+ }));
229
+ }
230
+ /**
231
+ * 把 `data:<mime>;base64,<data>` 解析成 Anthropic 的 base64 image block。
232
+ * 非 data URL 或格式不符时返回 null(跳过该图,不阻断整段请求)。
233
+ */
234
+ function dataUrlToImageBlock(url) {
235
+ if (typeof url !== 'string')
236
+ return null;
237
+ const m = /^data:([^;]+);base64,(.+)$/s.exec(url);
238
+ if (!m)
239
+ return null;
240
+ return {
241
+ type: 'image',
242
+ source: {
243
+ type: 'base64',
244
+ media_type: m[1],
245
+ data: m[2],
246
+ },
247
+ };
248
+ }
249
+ /** 解析模型给出的工具参数;失败时回落为空对象,避免坏 JSON 破坏整段 Anthropic 请求。 */
250
+ function safeParse(json) {
251
+ try {
252
+ return JSON.parse(json);
253
+ }
254
+ catch {
255
+ return {};
256
+ }
257
+ }
258
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/agent/providers/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { OpenAIClient } from './openai.js';
2
+ /** Gemini 的 OpenAI 兼容端点(config.baseURL 未配置时的默认指向)。 */
3
+ export const DEFAULT_GEMINI_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/openai/';
4
+ /**
5
+ * Gemini 后端:走 Google 的 OpenAI 兼容协议,请求/流式聚合逻辑与
6
+ * OpenAIClient 完全一致,因此直接继承,只改默认 baseURL。
7
+ * (此前是 70 行逐字复制的 chat 循环,两处分叉风险已被继承结构消除。)
8
+ */
9
+ export class GeminiClient extends OpenAIClient {
10
+ constructor(config) {
11
+ super({ ...config, baseURL: config.baseURL || DEFAULT_GEMINI_BASE_URL });
12
+ }
13
+ }
14
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../src/agent/providers/gemini.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,sDAAsD;AACtD,MAAM,CAAC,MAAM,uBAAuB,GAChC,0DAA0D,CAAC;AAE/D;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,YAAY;IAC1C,YAAY,MAAc;QACtB,KAAK,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,uBAAuB,EAAE,CAAC,CAAC;IAC7E,CAAC;CACJ"}
@@ -0,0 +1,110 @@
1
+ import OpenAI from 'openai';
2
+ import { toolDefs } from '../../tools/index.js';
3
+ import { projectToolPairedMessages } from '../tool-projection.js';
4
+ export class OpenAIClient {
5
+ client;
6
+ model;
7
+ /** 保存 OpenAI 兼容 SDK 实例;baseURL 可指向 Qwen / DeepSeek / GLM 等兼容服务。 */
8
+ constructor(config) {
9
+ this.client = new OpenAI({
10
+ apiKey: config.apiKey,
11
+ baseURL: config.baseURL,
12
+ });
13
+ this.model = config.model;
14
+ }
15
+ /** 当前模型名只存在本地,真正请求时才写入 body.model。 */
16
+ getModel() {
17
+ return this.model;
18
+ }
19
+ /** 切换模型时不清空模型列表缓存,因为同一后端的模型列表不随选择变化。 */
20
+ setModel(model) {
21
+ this.model = model;
22
+ }
23
+ /**
24
+ * 按模型名启发式判断是否支持图像输入。OpenAI 兼容后端型号繁杂,没有统一的
25
+ * capability 接口,只能靠常见命名规律匹配(qwen-vl / *-vision / gpt-4o /
26
+ * gemini / *-4v / omni 等)。宁可漏判降级为文字,也不误判导致后端报错。
27
+ */
28
+ supportsVision() {
29
+ return /vl|vision|4o|omni|4v|gemini|internvl|llava|pixtral|claude/i.test(this.model);
30
+ }
31
+ /** 拉取后端可用模型列表(结果缓存,避免重复请求)。 */
32
+ modelCache;
33
+ async listModels() {
34
+ if (!this.modelCache) {
35
+ const list = await this.client.models.list();
36
+ this.modelCache = list.data.map((m) => m.id).sort();
37
+ }
38
+ return this.modelCache;
39
+ }
40
+ /**
41
+ * 发送整段对话历史,流式接收助手回复。
42
+ * @param messages 完整历史(含本轮 user 消息、以及可能的工具结果)
43
+ * @param onDelta 每收到一小段文本就回调(用于实时打印)
44
+ * @returns 助手本回合的文本 + 它想调用的工具
45
+ */
46
+ async chat(messages, onDelta, options, onReasoning, onToolArgumentDelta) {
47
+ const body = {
48
+ model: this.model,
49
+ messages: projectToolPairedMessages(messages),
50
+ stream: true,
51
+ stream_options: { include_usage: true },
52
+ };
53
+ if (!options?.disableTools) {
54
+ body.tools = toolDefs(options?.allowedTools);
55
+ if (options?.requireTools) {
56
+ body.tool_choice = 'required';
57
+ }
58
+ }
59
+ const stream = (await this.client.chat.completions.create(body));
60
+ let content = '';
61
+ // 工具调用在流里是分片到达的,按 index 累加拼回完整的 id/name/arguments
62
+ const acc = {};
63
+ let usage;
64
+ for await (const chunk of stream) {
65
+ const delta = chunk.choices[0]?.delta;
66
+ if (!delta) {
67
+ // OpenAI 流式 usage 通常在 choices 为空的最终 chunk 里
68
+ const u = chunk.usage;
69
+ if (u) {
70
+ usage = {
71
+ promptTokens: u.prompt_tokens ?? 0,
72
+ completionTokens: u.completion_tokens ?? 0,
73
+ totalTokens: u.total_tokens ?? 0,
74
+ };
75
+ }
76
+ continue;
77
+ }
78
+ const reasoning = delta.reasoning_content;
79
+ if (reasoning)
80
+ onReasoning?.(reasoning);
81
+ if (delta.content) {
82
+ content += delta.content;
83
+ onDelta(delta.content);
84
+ }
85
+ for (const tc of delta.tool_calls ?? []) {
86
+ const cur = (acc[tc.index] ??= {
87
+ id: '',
88
+ name: '',
89
+ arguments: '',
90
+ });
91
+ if (tc.id)
92
+ cur.id = tc.id;
93
+ if (tc.function?.name)
94
+ cur.name = tc.function.name;
95
+ if (tc.function?.arguments) {
96
+ cur.arguments += tc.function.arguments;
97
+ if (cur.name) {
98
+ onToolArgumentDelta?.(tc.index, cur.name, cur.arguments);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ const toolCalls = Object.keys(acc)
104
+ .map(Number)
105
+ .sort((a, b) => a - b)
106
+ .map((i) => acc[i]);
107
+ return { content, toolCalls, usage };
108
+ }
109
+ }
110
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/agent/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAShD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,OAAO,YAAY;IACb,MAAM,CAAS;IACf,KAAK,CAAS;IAEtB,mEAAmE;IACnE,YAAY,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,sCAAsC;IACtC,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,wCAAwC;IACxC,QAAQ,CAAC,KAAa;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,cAAc;QACV,OAAO,4DAA4D,CAAC,IAAI,CACpE,IAAI,CAAC,KAAK,CACb,CAAC;IACN,CAAC;IAED,+BAA+B;IACvB,UAAU,CAAY;IAC9B,KAAK,CAAC,UAAU;QACZ,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CACN,QAAuB,EACvB,OAA+B,EAC/B,OAAqB,EACrB,WAAoC,EACpC,mBAIS;QAET,MAAM,IAAI,GAAQ;YACd,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,yBAAyB,CAAC,QAAQ,CAAC;YAC7C,MAAM,EAAE,IAAI;YACZ,cAAc,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;SAC1C,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC7C,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;gBACxB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;YAClC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,MAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAc,CAAC,IAAI,CAAC,CAA+D,CAAC;QAExI,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,kDAAkD;QAClD,MAAM,GAAG,GAA6B,EAAE,CAAC;QACzC,IAAI,KAAwB,CAAC;QAE7B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,4CAA4C;gBAC5C,MAAM,CAAC,GAAI,KAAa,CAAC,KAAK,CAAC;gBAC/B,IAAI,CAAC,EAAE,CAAC;oBACJ,KAAK,GAAG;wBACJ,YAAY,EAAE,CAAC,CAAC,aAAa,IAAI,CAAC;wBAClC,gBAAgB,EAAE,CAAC,CAAC,iBAAiB,IAAI,CAAC;wBAC1C,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,CAAC;qBACnC,CAAC;gBACN,CAAC;gBACD,SAAS;YACb,CAAC;YAED,MAAM,SAAS,GAAI,KAAa,CAAC,iBAAiB,CAAC;YACnD,IAAI,SAAS;gBAAE,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC;YAExC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAChB,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;YAED,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;gBACtC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK;oBAC3B,EAAE,EAAE,EAAE;oBACN,IAAI,EAAE,EAAE;oBACR,SAAS,EAAE,EAAE;iBAChB,CAAC,CAAC;gBACH,IAAI,EAAE,CAAC,EAAE;oBAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;gBAC1B,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI;oBAAE,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACnD,IAAI,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;oBACzB,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;oBACvC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;wBACX,mBAAmB,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;oBAC7D,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAC7B,GAAG,CAAC,MAAM,CAAC;aACX,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;aACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAExB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACzC,CAAC;CACJ"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 严格投影工具消息:只保留「assistant.tool_calls 后紧跟对应 tool 结果」
3
+ * 的完整配对,丢弃孤儿 tool 结果与未完成的 tool_calls,避免 provider
4
+ * 因 tool_use / tool_result 不配对拒绝整段上下文。
5
+ */
6
+ export function projectToolPairedMessages(messages) {
7
+ const projected = [];
8
+ const pendingToolCallIds = [];
9
+ for (let i = 0; i < messages.length; i++) {
10
+ const m = messages[i];
11
+ if (m.role === 'tool') {
12
+ const toolCallId = m.tool_call_id;
13
+ const pendingIndex = pendingToolCallIds.indexOf(toolCallId);
14
+ if (pendingIndex < 0)
15
+ continue;
16
+ projected.push(m);
17
+ pendingToolCallIds.splice(pendingIndex, 1);
18
+ continue;
19
+ }
20
+ if (m.role === 'assistant') {
21
+ const toolCalls = m.tool_calls;
22
+ if (Array.isArray(toolCalls) && toolCalls.length > 0) {
23
+ const validToolCallIds = toolCalls
24
+ .map((tc) => tc?.id)
25
+ .filter((id) => typeof id === 'string' && id.length > 0);
26
+ const resultIds = collectFollowingToolResultIds(messages, i);
27
+ if (validToolCallIds.length !== toolCalls.length ||
28
+ !validToolCallIds.every((id) => resultIds.has(id))) {
29
+ continue;
30
+ }
31
+ projected.push(m);
32
+ pendingToolCallIds.push(...validToolCallIds);
33
+ continue;
34
+ }
35
+ }
36
+ projected.push(m);
37
+ }
38
+ return projected;
39
+ }
40
+ /**
41
+ * 从某条 assistant.tool_calls 后面连续的 tool 消息里收集结果 id。
42
+ *
43
+ * 一旦遇到非 tool 消息就停止,因为 provider 语义要求 tool_result 紧跟对应 tool_use。
44
+ */
45
+ function collectFollowingToolResultIds(messages, assistantIndex) {
46
+ const ids = new Set();
47
+ for (let i = assistantIndex + 1; i < messages.length; i++) {
48
+ const m = messages[i];
49
+ if (m.role !== 'tool')
50
+ break;
51
+ const id = m.tool_call_id;
52
+ if (id)
53
+ ids.add(id);
54
+ }
55
+ return ids;
56
+ }
57
+ //# sourceMappingURL=tool-projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-projection.js","sourceRoot":"","sources":["../../src/agent/tool-projection.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAuB;IAC7D,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,MAAM,kBAAkB,GAAa,EAAE,CAAC;IAExC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAEtB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,UAAU,GAAI,CAAS,CAAC,YAAY,CAAC;YAC3C,MAAM,YAAY,GAAG,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,YAAY,GAAG,CAAC;gBAAE,SAAS;YAC/B,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,kBAAkB,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YAC3C,SAAS;QACb,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,MAAM,SAAS,GAAI,CAAS,CAAC,UAAU,CAAC;YACxC,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,MAAM,gBAAgB,GAAG,SAAS;qBAC7B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;qBACnB,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC3E,MAAM,SAAS,GAAG,6BAA6B,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAC7D,IACI,gBAAgB,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;oBAC5C,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EACpD,CAAC;oBACC,SAAS;gBACb,CAAC;gBACD,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,kBAAkB,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;gBAC7C,SAAS;YACb,CAAC;QACL,CAAC;QAED,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,SAAS,6BAA6B,CAClC,QAAuB,EACvB,cAAsB;IAEtB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxD,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,MAAM;QAC7B,MAAM,EAAE,GAAI,CAAS,CAAC,YAAY,CAAC;QACnC,IAAI,EAAE;YAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC"}
@@ -10,6 +10,7 @@ export function parseArgs(argv) {
10
10
  help: false,
11
11
  yes: false,
12
12
  noHooks: false,
13
+ project: false,
13
14
  unknown: [],
14
15
  positionals: [],
15
16
  };
@@ -31,6 +32,9 @@ export function parseArgs(argv) {
31
32
  case '--no-hooks':
32
33
  result.noHooks = true;
33
34
  break;
35
+ case '--project':
36
+ result.project = true;
37
+ break;
34
38
  case '-r':
35
39
  case '--role': {
36
40
  const next = argv[i + 1];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/args/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmBH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAClB,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,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,KAAK,YAAY;gBACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;oBACnB,CAAC,EAAE,CAAC;gBACR,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;gBACD,MAAM;YACV,CAAC;YACD;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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/args/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAClB,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,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,KAAK,YAAY;gBACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,WAAW;gBACZ,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;oBACnB,CAAC,EAAE,CAAC;gBACR,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;gBACD,MAAM;YACV,CAAC;YACD;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,51 @@
1
+ /**
2
+ * 从系统剪贴板取图片(Ctrl-V 粘贴图片用)。
3
+ *
4
+ * 参考 Kimi Code:终端里 Cmd-V 会被终端 App 截走(程序收不到),所以走
5
+ * **Ctrl-V**——它能真正传到程序里。这里负责把剪贴板里的图像位图落成一个临时
6
+ * PNG 文件,交给已有的图片引用管线(`@路径`)发送。
7
+ *
8
+ * 目前仅实现 macOS(osascript,系统自带、无需装东西)。其它平台返回不支持,
9
+ * 后续可补 Linux(wl-paste / xclip)与 Windows(PowerShell)。
10
+ */
11
+ import { execFileSync } from 'node:child_process';
12
+ import { randomUUID } from 'node:crypto';
13
+ import { existsSync, statSync } from 'node:fs';
14
+ import { tmpdir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ /** osascript:把剪贴板内容按 PNG 写到 outPath;剪贴板无图会非零退出。 */
17
+ function readViaOsascript(outPath) {
18
+ execFileSync('osascript', [
19
+ '-e',
20
+ 'set png to (the clipboard as «class PNGf»)',
21
+ '-e',
22
+ `set f to open for access POSIX file "${outPath}" with write permission`,
23
+ '-e',
24
+ 'set eof f to 0',
25
+ '-e',
26
+ 'write png to f',
27
+ '-e',
28
+ 'close access f',
29
+ ], { stdio: ['ignore', 'ignore', 'ignore'], timeout: 5000 });
30
+ }
31
+ /**
32
+ * 取剪贴板图片,成功时返回落地的临时 PNG 路径。
33
+ * 剪贴板没有图片、平台不支持或提取失败都返回 `{ ok:false, reason }`。
34
+ */
35
+ export function readClipboardImage() {
36
+ if (process.platform !== 'darwin') {
37
+ return { ok: false, reason: '当前平台暂不支持剪贴板取图(仅 macOS)' };
38
+ }
39
+ const outPath = join(tmpdir(), `ccli-paste-${randomUUID()}.png`);
40
+ try {
41
+ readViaOsascript(outPath);
42
+ }
43
+ catch {
44
+ return { ok: false, reason: '剪贴板里没有图片' };
45
+ }
46
+ if (!existsSync(outPath) || statSync(outPath).size === 0) {
47
+ return { ok: false, reason: '剪贴板里没有图片' };
48
+ }
49
+ return { ok: true, path: outPath };
50
+ }
51
+ //# sourceMappingURL=clipboard-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/cli/clipboard-image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC,mDAAmD;AACnD,SAAS,gBAAgB,CAAC,OAAe;IACrC,YAAY,CACR,WAAW,EACX;QACI,IAAI;QACJ,4CAA4C;QAC5C,IAAI;QACJ,wCAAwC,OAAO,yBAAyB;QACxE,IAAI;QACJ,gBAAgB;QAChB,IAAI;QACJ,gBAAgB;QAChB,IAAI;QACJ,gBAAgB;KACnB,EACD,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAC3D,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAC9B,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;IAC3D,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,UAAU,EAAE,MAAM,CAAC,CAAC;IACjE,IAAI,CAAC;QACD,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 发送前给过大的图片降分辨率,省 token / 请求体。
3
+ *
4
+ * 背景(实测 qwen-vl-plus):图片 token 按分辨率算,且服务端对超大图有封顶
5
+ * (~1M 像素 ≈ 1280 token)。把最长边压到 1024,一张全屏截图能从 ~1259 token
6
+ * 降到 ~684(省 ~46%),文字基本仍可读;对按面积计费的 Claude/GPT-4o 省得更多。
7
+ *
8
+ * 实现走 macOS 自带的 `sips`(零依赖,剪贴板取图本就靠 macOS)。非 macOS 或任何
9
+ * 失败都**原样透传**——压缩只是优化,绝不能挡住发送。只在最长边超过上限时才降,
10
+ * 小图一律不动(`sips -Z` 会放大小图,必须先判断)。
11
+ */
12
+ import { execFileSync } from 'node:child_process';
13
+ import { randomUUID } from 'node:crypto';
14
+ import { existsSync, statSync } from 'node:fs';
15
+ import { tmpdir } from 'node:os';
16
+ import { join } from 'node:path';
17
+ /** 默认最长边上限(px);超过才降,小图不动。 */
18
+ export const MAX_IMAGE_EDGE = 1024;
19
+ /** 用 sips 读像素宽高;失败返回 null。 */
20
+ function sipsDimensions(path) {
21
+ try {
22
+ const out = execFileSync('sips', ['-g', 'pixelWidth', '-g', 'pixelHeight', path], { encoding: 'utf8', timeout: 5000 });
23
+ const w = /pixelWidth:\s*(\d+)/.exec(out);
24
+ const h = /pixelHeight:\s*(\d+)/.exec(out);
25
+ if (!w || !h)
26
+ return null;
27
+ return { width: Number(w[1]), height: Number(h[1]) };
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ /**
34
+ * 若图片最长边超过 maxEdge,用 sips 等比降到 maxEdge 并输出临时 PNG;
35
+ * 否则(含非 macOS、读尺寸失败、压缩失败)原样透传。
36
+ */
37
+ export function compressImageForModel(path, maxEdge = MAX_IMAGE_EDGE) {
38
+ if (process.platform !== 'darwin')
39
+ return { path, changed: false };
40
+ const from = sipsDimensions(path);
41
+ if (!from)
42
+ return { path, changed: false };
43
+ if (Math.max(from.width, from.height) <= maxEdge) {
44
+ return { path, changed: false };
45
+ }
46
+ const out = join(tmpdir(), `ccli-shrink-${randomUUID()}.png`);
47
+ try {
48
+ execFileSync('sips', ['-Z', String(maxEdge), path, '--out', out], {
49
+ stdio: ['ignore', 'ignore', 'ignore'],
50
+ timeout: 10000,
51
+ });
52
+ if (!existsSync(out) || statSync(out).size === 0) {
53
+ return { path, changed: false };
54
+ }
55
+ }
56
+ catch {
57
+ return { path, changed: false };
58
+ }
59
+ const to = sipsDimensions(out) ?? undefined;
60
+ return { path: out, changed: true, from, to };
61
+ }
62
+ //# sourceMappingURL=image-compress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-compress.js","sourceRoot":"","sources":["../../src/cli/image-compress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,6BAA6B;AAC7B,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAWnC,8BAA8B;AAC9B,SAAS,cAAc,CACnB,IAAY;IAEZ,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CACpB,MAAM,EACN,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAC/C,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CACtC,CAAC;QACF,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACjC,IAAY,EACZ,UAAkB,cAAc;IAEhC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3C,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,UAAU,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC;QACD,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE;YAC9D,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACpC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IACD,MAAM,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;IAC5C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AAClD,CAAC"}