@zhongqian97-code/ecode 0.5.35 → 0.5.37

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.
@@ -202,9 +202,229 @@ function createOpenAIProvider(profile) {
202
202
  };
203
203
  }
204
204
 
205
+ // src/providers/anthropic.ts
206
+ import https from "https";
207
+ import { URL as URL2 } from "url";
208
+ function convertMessagesToAnthropic(messages) {
209
+ let system = "";
210
+ const anthropicMessages = [];
211
+ for (let i = 0; i < messages.length; i++) {
212
+ const msg = messages[i];
213
+ if (msg.role === "system") {
214
+ system = msg.content;
215
+ continue;
216
+ }
217
+ if (msg.role === "user") {
218
+ anthropicMessages.push({ role: "user", content: msg.content });
219
+ continue;
220
+ }
221
+ if (msg.role === "tool") {
222
+ const toolResults = [];
223
+ while (i < messages.length && messages[i].role === "tool") {
224
+ const toolMsg = messages[i];
225
+ toolResults.push({
226
+ type: "tool_result",
227
+ tool_use_id: toolMsg.tool_call_id,
228
+ content: toolMsg.content
229
+ });
230
+ i++;
231
+ }
232
+ i--;
233
+ anthropicMessages.push({ role: "user", content: toolResults });
234
+ continue;
235
+ }
236
+ if (msg.role === "assistant") {
237
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
238
+ const content = [];
239
+ if (msg.content) content.push({ type: "text", text: msg.content });
240
+ for (const tc of msg.tool_calls) {
241
+ let input = {};
242
+ try {
243
+ input = JSON.parse(tc.function.arguments);
244
+ } catch {
245
+ input = {};
246
+ }
247
+ content.push({ type: "tool_use", id: tc.id, name: tc.function.name, input });
248
+ }
249
+ anthropicMessages.push({ role: "assistant", content });
250
+ } else {
251
+ anthropicMessages.push({ role: "assistant", content: msg.content ?? "" });
252
+ }
253
+ }
254
+ }
255
+ return { system, anthropicMessages };
256
+ }
257
+ function convertToolsToAnthropic(tools) {
258
+ return tools.map((tool) => ({
259
+ name: tool.function.name,
260
+ description: tool.function.description,
261
+ input_schema: tool.function.parameters
262
+ }));
263
+ }
264
+ function createAnthropicProvider(profile) {
265
+ const capabilities = Object.freeze(
266
+ Object.defineProperties({}, {
267
+ supportsTools: { value: true, writable: false, enumerable: true },
268
+ supportsReasoningStream: { value: false, writable: false, enumerable: true },
269
+ supportsImages: { value: true, writable: false, enumerable: true },
270
+ supportsJsonSchema: { value: true, writable: false, enumerable: true }
271
+ })
272
+ );
273
+ return {
274
+ capabilities,
275
+ stream(messages, tools, signal) {
276
+ return streamAnthropicResponse(profile, messages, tools, signal);
277
+ }
278
+ };
279
+ }
280
+ async function* streamAnthropicResponse(profile, messages, tools, signal) {
281
+ const { system, anthropicMessages } = convertMessagesToAnthropic(messages);
282
+ const endpointUrl = new URL2(`${profile.baseUrl.replace(/\/$/, "")}/v1/messages`);
283
+ const requestBody = {
284
+ model: profile.model,
285
+ max_tokens: 8192,
286
+ messages: anthropicMessages,
287
+ stream: true
288
+ };
289
+ if (system) requestBody.system = system;
290
+ if (tools && tools.length > 0) requestBody.tools = convertToolsToAnthropic(tools);
291
+ const bodyStr = JSON.stringify(requestBody);
292
+ yield* makeHttpsStream(endpointUrl, profile.apiKey, bodyStr, signal);
293
+ }
294
+ async function* makeHttpsStream(url, apiKey, body, signal) {
295
+ var _a;
296
+ const queue = [];
297
+ let notify = null;
298
+ function push(item) {
299
+ queue.push(item);
300
+ if (notify) {
301
+ const fn = notify;
302
+ notify = null;
303
+ fn();
304
+ }
305
+ }
306
+ const port = url.port ? parseInt(url.port, 10) : 443;
307
+ const req = https.request(
308
+ {
309
+ hostname: url.hostname,
310
+ port,
311
+ path: url.pathname + url.search,
312
+ method: "POST",
313
+ headers: {
314
+ "x-api-key": apiKey,
315
+ "anthropic-version": "2023-06-01",
316
+ "content-type": "application/json",
317
+ accept: "text/event-stream",
318
+ "content-length": String(Buffer.byteLength(body, "utf8"))
319
+ }
320
+ },
321
+ (res) => {
322
+ const statusCode = res.statusCode;
323
+ if (statusCode !== 200) {
324
+ push(new Error(`Anthropic API error: HTTP ${statusCode}`));
325
+ return;
326
+ }
327
+ const r = res;
328
+ r.on("data", (chunk) => push(chunk));
329
+ r.on("end", () => push(null));
330
+ r.on("error", (err) => push(err));
331
+ }
332
+ );
333
+ req.on("error", (err) => push(err));
334
+ if (signal) {
335
+ signal.addEventListener(
336
+ "abort",
337
+ () => {
338
+ req.destroy();
339
+ push(null);
340
+ },
341
+ { once: true }
342
+ );
343
+ }
344
+ req.write(body);
345
+ req.end();
346
+ let buffer = "";
347
+ const toolBlocks = /* @__PURE__ */ new Map();
348
+ let stopReason = null;
349
+ let inputTokens = 0;
350
+ let outputTokens = 0;
351
+ while (true) {
352
+ let item;
353
+ if (queue.length > 0) {
354
+ item = queue.shift();
355
+ } else {
356
+ await new Promise((resolve4) => {
357
+ notify = resolve4;
358
+ });
359
+ item = queue.shift();
360
+ }
361
+ if (item === null) break;
362
+ if (item instanceof Error) throw item;
363
+ buffer += item.toString("utf8");
364
+ const lines = buffer.split("\n");
365
+ buffer = lines.pop() ?? "";
366
+ for (const line of lines) {
367
+ if (!line.startsWith("data: ")) continue;
368
+ const data = line.slice(6).trim();
369
+ if (data === "[DONE]") break;
370
+ let event;
371
+ try {
372
+ event = JSON.parse(data);
373
+ } catch {
374
+ continue;
375
+ }
376
+ const eventType = event.type;
377
+ if (eventType === "message_start") {
378
+ const msg = event.message;
379
+ if ((_a = msg == null ? void 0 : msg.usage) == null ? void 0 : _a.input_tokens) inputTokens = msg.usage.input_tokens;
380
+ } else if (eventType === "content_block_start") {
381
+ const block = event.content_block;
382
+ const index = event.index;
383
+ if ((block == null ? void 0 : block.type) === "tool_use" && block.id && block.name) {
384
+ toolBlocks.set(index, { id: block.id, name: block.name, jsonAccum: "" });
385
+ }
386
+ } else if (eventType === "content_block_delta") {
387
+ const delta = event.delta;
388
+ const index = event.index;
389
+ if ((delta == null ? void 0 : delta.type) === "text_delta" && delta.text) {
390
+ yield { text: delta.text, done: false };
391
+ } else if ((delta == null ? void 0 : delta.type) === "input_json_delta" && delta.partial_json) {
392
+ const tool = toolBlocks.get(index);
393
+ if (tool) tool.jsonAccum += delta.partial_json;
394
+ }
395
+ } else if (eventType === "message_delta") {
396
+ const delta = event.delta;
397
+ stopReason = (delta == null ? void 0 : delta.stop_reason) ?? null;
398
+ const usage = event.usage;
399
+ if (usage == null ? void 0 : usage.output_tokens) outputTokens = usage.output_tokens;
400
+ } else if (eventType === "message_stop") {
401
+ const toolCalls = [...toolBlocks.values()].map((t) => ({
402
+ id: t.id,
403
+ name: t.name,
404
+ arguments: t.jsonAccum
405
+ }));
406
+ yield {
407
+ done: true,
408
+ text: "",
409
+ finishReason: stopReason,
410
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
411
+ usage: {
412
+ promptTokens: inputTokens,
413
+ completionTokens: outputTokens,
414
+ totalTokens: inputTokens + outputTokens
415
+ }
416
+ };
417
+ return;
418
+ }
419
+ }
420
+ }
421
+ yield { done: true, text: "", finishReason: stopReason };
422
+ }
423
+
205
424
  // src/providers/index.ts
206
425
  function createProvider(profile) {
207
- return createOpenAIProvider(profile);
426
+ const useAnthropic = profile.apiFormat === "anthropic" || profile.baseUrl.includes("anthropic");
427
+ return useAnthropic ? createAnthropicProvider(profile) : createOpenAIProvider(profile);
208
428
  }
209
429
  function resolveActiveProfile(config, providerName) {
210
430
  var _a, _b;
@@ -646,7 +866,7 @@ async function grepFiles(params) {
646
866
 
647
867
  // src/tools/web_fetch.ts
648
868
  import * as http from "http";
649
- import * as https from "https";
869
+ import * as https2 from "https";
650
870
  var WEB_FETCH_TOOL = {
651
871
  type: "function",
652
872
  function: {
@@ -807,7 +1027,7 @@ async function nodeHttpFetch(url, signal) {
807
1027
  return new Promise((resolve4, reject) => {
808
1028
  const parsed = new URL(url);
809
1029
  const isHttps = parsed.protocol === "https:";
810
- const lib = isHttps ? https : http;
1030
+ const lib = isHttps ? https2 : http;
811
1031
  const onAbort = () => {
812
1032
  req.destroy();
813
1033
  const err = new Error("The user aborted a request.");
@@ -1837,28 +2057,29 @@ ${prompt}` : prompt;
1837
2057
  };
1838
2058
  const executeToolCall = async (tc) => {
1839
2059
  let result;
1840
- if (tc.name === "bash") {
2060
+ const toolName = tc.name.toLowerCase().trim();
2061
+ if (toolName === "bash") {
1841
2062
  const tcArgs = JSON.parse(tc.arguments);
1842
2063
  result = await handleBashTool(tcArgs.command, bashDeps);
1843
- } else if (tc.name === "read") {
2064
+ } else if (toolName === "read") {
1844
2065
  const tcArgs = JSON.parse(tc.arguments);
1845
2066
  result = await readFile3(tcArgs);
1846
- } else if (tc.name === "write") {
2067
+ } else if (toolName === "write") {
1847
2068
  const tcArgs = JSON.parse(tc.arguments);
1848
2069
  result = await writeFile2(tcArgs);
1849
- } else if (tc.name === "edit") {
2070
+ } else if (toolName === "edit") {
1850
2071
  const tcArgs = JSON.parse(tc.arguments);
1851
2072
  result = await editFile(tcArgs);
1852
- } else if (tc.name === "glob") {
2073
+ } else if (toolName === "glob") {
1853
2074
  const tcArgs = JSON.parse(tc.arguments);
1854
2075
  result = await globFiles(tcArgs);
1855
- } else if (tc.name === "grep") {
2076
+ } else if (toolName === "grep") {
1856
2077
  const tcArgs = JSON.parse(tc.arguments);
1857
2078
  result = await grepFiles(tcArgs);
1858
- } else if (tc.name === "apply_patch") {
2079
+ } else if (toolName === "apply_patch") {
1859
2080
  const tcArgs = JSON.parse(tc.arguments);
1860
2081
  result = await applyPatch(tcArgs);
1861
- } else if (tc.name === "todo") {
2082
+ } else if (toolName === "todo") {
1862
2083
  const tcArgs = JSON.parse(tc.arguments);
1863
2084
  result = todo(tcArgs);
1864
2085
  } else {
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  todo,
35
35
  webFetch,
36
36
  writeFile
37
- } from "./chunk-VM35XIBY.js";
37
+ } from "./chunk-7DHMJ6NS.js";
38
38
  import {
39
39
  createSessionMetadata,
40
40
  generateTitle,
@@ -231,7 +231,10 @@ var EventBus = class {
231
231
  /** 发布事件,同步调用所有当前订阅者 */
232
232
  emit(event) {
233
233
  for (const listener of this.listeners) {
234
- listener(event);
234
+ try {
235
+ listener(event);
236
+ } catch {
237
+ }
235
238
  }
236
239
  }
237
240
  /** 添加事件监听器,返回取消订阅函数 */
@@ -493,7 +496,8 @@ var SessionRuntime = class {
493
496
  * 其他工具:直接执行,不需要审批流程。
494
497
  */
495
498
  async executeToolCall(tc, signal) {
496
- const { name, arguments: args } = tc;
499
+ const name = tc.name.toLowerCase().trim();
500
+ const args = tc.arguments;
497
501
  try {
498
502
  if (name === "bash") {
499
503
  const parsed = JSON.parse(args);
@@ -901,6 +905,6 @@ Node.js 16/18 \u8BF7\u4F7F\u7528 --web \u6216 --pipe \u6A21\u5F0F\u3002
901
905
  );
902
906
  process.exit(1);
903
907
  }
904
- const { App, React, render } = await import("./ui-VPHPVIS5.js");
908
+ const { App, React, render } = await import("./ui-XL5IDLH5.js");
905
909
  render(React.createElement(App, { config: finalConfig, version: VERSION, autoMode, registry, trustedSkillDirs, initialMessages }));
906
910
  }
@@ -26,7 +26,7 @@ import {
26
26
  todo,
27
27
  webFetch,
28
28
  writeFile
29
- } from "./chunk-VM35XIBY.js";
29
+ } from "./chunk-7DHMJ6NS.js";
30
30
  import {
31
31
  loadJobs,
32
32
  removeJob,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.5.35",
3
+ "version": "0.5.37",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",