@zhongqian97-code/ecode 0.5.36 → 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.");
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  todo,
35
35
  webFetch,
36
36
  writeFile
37
- } from "./chunk-BRSZMOHF.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
  /** 添加事件监听器,返回取消订阅函数 */
@@ -902,6 +905,6 @@ Node.js 16/18 \u8BF7\u4F7F\u7528 --web \u6216 --pipe \u6A21\u5F0F\u3002
902
905
  );
903
906
  process.exit(1);
904
907
  }
905
- const { App, React, render } = await import("./ui-QZG4SHLC.js");
908
+ const { App, React, render } = await import("./ui-XL5IDLH5.js");
906
909
  render(React.createElement(App, { config: finalConfig, version: VERSION, autoMode, registry, trustedSkillDirs, initialMessages }));
907
910
  }
@@ -26,7 +26,7 @@ import {
26
26
  todo,
27
27
  webFetch,
28
28
  writeFile
29
- } from "./chunk-BRSZMOHF.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.36",
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",