@zhongqian97-code/ecode 0.5.45 → 0.5.46

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.
@@ -4,454 +4,7 @@ import {
4
4
  createSessionMetadata,
5
5
  updateSessionMetadata,
6
6
  writeSessionMetadata
7
- } from "./chunk-72HNTRAP.js";
8
-
9
- // src/providers/openai.ts
10
- import OpenAI from "openai";
11
- function createOpenAIProvider(profile) {
12
- const THINK_END = "</think>";
13
- const openai = new OpenAI({
14
- baseURL: profile.baseUrl,
15
- apiKey: profile.apiKey
16
- });
17
- const capabilities = Object.freeze(
18
- Object.defineProperties({}, {
19
- supportsTools: { value: true, writable: false, enumerable: true, configurable: false },
20
- supportsReasoningStream: { value: true, writable: false, enumerable: true, configurable: false },
21
- supportsImages: { value: false, writable: false, enumerable: true, configurable: false },
22
- supportsJsonSchema: { value: true, writable: false, enumerable: true, configurable: false }
23
- })
24
- );
25
- return {
26
- capabilities,
27
- /**
28
- * stream 方法向 LLM 发起一次流式对话请求,返回异步可迭代的 chunk 序列。
29
- *
30
- * 实现细节:
31
- * - 使用 `Symbol.asyncIterator` + async generator 实现懒执行:
32
- * 只有调用方执行 `for await` 时才真正发起 HTTP 请求,避免浪费连接
33
- * - 内部维护两个累加器:
34
- * 1. `tcAccumulator`:按 index 聚合工具调用的分片参数(JSON 字符串)
35
- * 2. `reasoningAccumulator`:拼接思考链的所有分片文本
36
- * - 只在最终 chunk(`finish_reason !== null`)中 yield done=true 的完整信息
37
- *
38
- * @param messages 完整的对话历史,包含 user/assistant/tool 所有轮次
39
- * @param tools 可选的工具列表,不传或传空数组时不附加 tools 字段
40
- * @param signal 可选的 AbortSignal,用于取消请求
41
- */
42
- stream(messages, tools, signal) {
43
- return {
44
- [Symbol.asyncIterator]: async function* () {
45
- var _a, _b, _c;
46
- let thinkPhase = "pre";
47
- let scanningForClose = false;
48
- let closeSearchBuffer = "";
49
- let visibleTextTail = "";
50
- function rememberVisibleText(text) {
51
- if (!text) return;
52
- visibleTextTail = (visibleTextTail + text).slice(-256);
53
- }
54
- function hasNearbyVisibleOpenThink(maxDistance = 4) {
55
- const openIdx = visibleTextTail.lastIndexOf("<think>");
56
- const closeIdx = visibleTextTail.lastIndexOf(THINK_END);
57
- if (openIdx === -1 || openIdx < closeIdx) return false;
58
- return visibleTextTail.length - (openIdx + "<think>".length) <= maxDistance;
59
- }
60
- function processContent(raw) {
61
- if (!raw) return { text: "", thinking: "" };
62
- if (thinkPhase === "post") return { text: raw, thinking: "" };
63
- if (thinkPhase === "pre") {
64
- if (raw.startsWith("<think>")) {
65
- thinkPhase = "in";
66
- raw = raw.slice(7);
67
- } else {
68
- thinkPhase = "post";
69
- return { text: raw, thinking: "" };
70
- }
71
- }
72
- const endIdx = raw.indexOf(THINK_END);
73
- if (endIdx === -1) return { text: "", thinking: raw };
74
- const thinking = raw.slice(0, endIdx);
75
- thinkPhase = "post";
76
- return { text: raw.slice(endIdx + THINK_END.length), thinking };
77
- }
78
- const requestParams = {
79
- model: profile.model,
80
- messages,
81
- stream: true,
82
- stream_options: { include_usage: true }
83
- };
84
- if (tools && tools.length > 0) {
85
- requestParams.tools = tools;
86
- }
87
- const response = await openai.chat.completions.create(
88
- requestParams,
89
- signal ? { signal } : void 0
90
- );
91
- const tcAccumulator = /* @__PURE__ */ new Map();
92
- let reasoningAccumulator = "";
93
- const reasoningDetailsAcc = /* @__PURE__ */ new Map();
94
- for await (const chunk of response) {
95
- const choice = chunk.choices[0];
96
- if (!choice) continue;
97
- const delta = choice.delta;
98
- if (delta.reasoning_content) {
99
- reasoningAccumulator += delta.reasoning_content;
100
- }
101
- if (delta.reasoning_details && delta.reasoning_details.length > 0) {
102
- scanningForClose = true;
103
- for (const rd of delta.reasoning_details) {
104
- const id = rd.id ?? "";
105
- const text = rd.text ?? "";
106
- if (!reasoningDetailsAcc.has(id)) {
107
- reasoningDetailsAcc.set(id, {
108
- type: rd.type ?? "reasoning.text",
109
- id,
110
- format: rd.format ?? "",
111
- index: rd.index ?? 0,
112
- text: ""
113
- });
114
- }
115
- const existing = reasoningDetailsAcc.get(id);
116
- existing.text += text;
117
- if (!delta.reasoning_content && text) {
118
- reasoningAccumulator += text;
119
- }
120
- }
121
- }
122
- if (delta.tool_calls) {
123
- for (const tc of delta.tool_calls) {
124
- if (!tcAccumulator.has(tc.index)) {
125
- tcAccumulator.set(tc.index, {
126
- id: tc.id ?? "",
127
- name: ((_a = tc.function) == null ? void 0 : _a.name) ?? "",
128
- arguments: ""
129
- });
130
- }
131
- const existing = tcAccumulator.get(tc.index);
132
- if (tc.id) existing.id = tc.id;
133
- if ((_b = tc.function) == null ? void 0 : _b.name) existing.name = tc.function.name;
134
- existing.arguments += ((_c = tc.function) == null ? void 0 : _c.arguments) ?? "";
135
- }
136
- }
137
- const isLast = choice.finish_reason != null;
138
- let rawToProcess;
139
- if (delta.reasoning_details && delta.reasoning_details.length > 0) {
140
- rawToProcess = "";
141
- } else if (scanningForClose) {
142
- closeSearchBuffer += delta.content ?? "";
143
- const closeIdx = closeSearchBuffer.indexOf(THINK_END);
144
- if (closeIdx !== -1) {
145
- const openIdx = closeSearchBuffer.lastIndexOf("<think>", closeIdx);
146
- const hasNearbyLiteralPair = openIdx !== -1 && closeIdx - (openIdx + "<think>".length) <= 8;
147
- const afterClose = closeSearchBuffer.slice(closeIdx + THINK_END.length);
148
- rawToProcess = hasNearbyLiteralPair ? closeSearchBuffer : afterClose.length > 0 ? afterClose : closeSearchBuffer.slice(0, closeIdx);
149
- scanningForClose = false;
150
- closeSearchBuffer = "";
151
- thinkPhase = "post";
152
- } else if (isLast) {
153
- rawToProcess = closeSearchBuffer;
154
- scanningForClose = false;
155
- closeSearchBuffer = "";
156
- thinkPhase = "post";
157
- } else {
158
- rawToProcess = "";
159
- }
160
- } else {
161
- rawToProcess = delta.content ?? "";
162
- }
163
- if (thinkPhase === "post" && rawToProcess.endsWith(THINK_END) && !rawToProcess.includes("<think>") && !hasNearbyVisibleOpenThink()) {
164
- rawToProcess = rawToProcess.slice(0, -THINK_END.length);
165
- }
166
- const { text: filteredText, thinking: thinkContent } = processContent(rawToProcess);
167
- rememberVisibleText(filteredText);
168
- if (thinkContent) {
169
- reasoningAccumulator += thinkContent;
170
- }
171
- if (isLast) {
172
- const rawUsage = chunk.usage;
173
- yield {
174
- text: filteredText,
175
- done: true,
176
- finishReason: choice.finish_reason,
177
- // tcAccumulator 为空说明本轮没有工具调用,传 undefined 而非空数组,
178
- // 让调用方用 if (chunk.toolCalls) 做简洁判断
179
- toolCalls: tcAccumulator.size > 0 ? Array.from(tcAccumulator.values()) : void 0,
180
- reasoning: reasoningAccumulator || void 0,
181
- // reasoningDetails 仅在流中出现过结构化推理时返回(MiniMax 兼容)
182
- reasoningDetails: reasoningDetailsAcc.size > 0 ? Array.from(reasoningDetailsAcc.values()) : void 0,
183
- // 将 snake_case 的原始字段映射为 camelCase,对外接口保持一致
184
- usage: rawUsage ? {
185
- promptTokens: rawUsage.prompt_tokens,
186
- completionTokens: rawUsage.completion_tokens,
187
- totalTokens: rawUsage.total_tokens
188
- } : void 0
189
- };
190
- } else {
191
- const incrementalReasoning = delta.reasoning_content || thinkContent || (delta.reasoning_details && delta.reasoning_details.length > 0 ? delta.reasoning_details.map((rd) => rd.text ?? "").join("") : void 0) || void 0;
192
- yield {
193
- text: filteredText,
194
- reasoning: incrementalReasoning || void 0,
195
- done: false
196
- };
197
- }
198
- }
199
- }
200
- };
201
- }
202
- };
203
- }
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\/?$/, "").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
-
424
- // src/providers/index.ts
425
- function createProvider(profile) {
426
- const useAnthropic = profile.apiFormat === "anthropic" || profile.baseUrl.includes("anthropic");
427
- return useAnthropic ? createAnthropicProvider(profile) : createOpenAIProvider(profile);
428
- }
429
- function resolveActiveProfile(config, providerName) {
430
- var _a, _b;
431
- if (providerName !== void 0) {
432
- const profile = (_a = config.providers) == null ? void 0 : _a[providerName];
433
- if (!profile) {
434
- throw new Error(`Provider '${providerName}' not found`);
435
- }
436
- return profile;
437
- }
438
- if (config.defaultProvider) {
439
- const profile = (_b = config.providers) == null ? void 0 : _b[config.defaultProvider];
440
- if (profile) return profile;
441
- }
442
- if (config.providers) {
443
- if (config.providers["default"]) {
444
- return config.providers["default"];
445
- }
446
- const first = Object.values(config.providers)[0];
447
- if (first) return first;
448
- }
449
- return {
450
- baseUrl: config.baseUrl,
451
- apiKey: config.apiKey,
452
- model: config.model
453
- };
454
- }
7
+ } from "./chunk-SIQQG6FT.js";
455
8
 
456
9
  // src/skills/loader.ts
457
10
  import { readFile, readdir, stat } from "fs/promises";
@@ -866,7 +419,7 @@ async function grepFiles(params) {
866
419
 
867
420
  // src/tools/web_fetch.ts
868
421
  import * as http from "http";
869
- import * as https2 from "https";
422
+ import * as https from "https";
870
423
  var WEB_FETCH_TOOL = {
871
424
  type: "function",
872
425
  function: {
@@ -1027,7 +580,7 @@ async function nodeHttpFetch(url, signal) {
1027
580
  return new Promise((resolve4, reject) => {
1028
581
  const parsed = new URL(url);
1029
582
  const isHttps = parsed.protocol === "https:";
1030
- const lib = isHttps ? https2 : http;
583
+ const lib = isHttps ? https : http;
1031
584
  const onAbort = () => {
1032
585
  req.destroy();
1033
586
  const err = new Error("The user aborted a request.");
@@ -2153,8 +1706,6 @@ ${prompt}` : prompt;
2153
1706
  }
2154
1707
 
2155
1708
  export {
2156
- createProvider,
2157
- resolveActiveProfile,
2158
1709
  isTrustedSkillPath,
2159
1710
  loadSkillsFromDir,
2160
1711
  READ_TOOL,
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  cmdSessionsInspect,
6
6
  cmdSessionsList,
7
7
  cmdSessionsReplay
8
- } from "./chunk-FPB3QTP5.js";
8
+ } from "./chunk-N3IDMSG5.js";
9
9
  import {
10
10
  APPLY_PATCH_TOOL,
11
11
  BASH_TOOL,
@@ -23,7 +23,6 @@ import {
23
23
  classifyCommand,
24
24
  createLogger,
25
25
  createLoggerAtPath,
26
- createProvider,
27
26
  editFile,
28
27
  executeBash,
29
28
  globFiles,
@@ -31,18 +30,19 @@ import {
31
30
  handleTaskTool,
32
31
  loadSkillsFromDir,
33
32
  readFile,
34
- resolveActiveProfile,
35
33
  todo,
36
34
  webFetch,
37
35
  writeFile
38
- } from "./chunk-I4VLKL2I.js";
36
+ } from "./chunk-V3JQ7HAU.js";
39
37
  import {
38
+ createProvider,
40
39
  createSessionMetadata,
41
40
  generateTitle,
42
41
  loadConfig,
42
+ resolveActiveProfile,
43
43
  updateSessionMetadata,
44
44
  writeSessionMetadata
45
- } from "./chunk-72HNTRAP.js";
45
+ } from "./chunk-SIQQG6FT.js";
46
46
 
47
47
  // src/index.ts
48
48
  import { createRequire } from "module";
@@ -814,7 +814,7 @@ if (rawArgs[0] === "web") {
814
814
  webAutoApprove = true;
815
815
  }
816
816
  }
817
- const { buildServer, generateAccessToken } = await import("./web-ONRJYFTB.js");
817
+ const { buildServer, generateAccessToken } = await import("./web-XPP3PJJZ.js");
818
818
  const token = finalConfig.webToken ?? generateAccessToken();
819
819
  const manager = new SessionManager(finalConfig);
820
820
  const __webDirname = dirname(fileURLToPath(import.meta.url));
@@ -923,6 +923,6 @@ Node.js 16/18 \u8BF7\u4F7F\u7528 --web \u6216 --pipe \u6A21\u5F0F\u3002
923
923
  );
924
924
  process.exit(1);
925
925
  }
926
- const { App, React, render } = await import("./ui-3K5WAFNO.js");
926
+ const { App, React, render } = await import("./ui-G7UOLPFA.js");
927
927
  render(React.createElement(App, { config: finalConfig, version: VERSION, autoMode, registry, trustedSkillDirs, initialMessages }));
928
928
  }
@@ -13,7 +13,6 @@ import {
13
13
  WRITE_TOOL,
14
14
  applyPatch,
15
15
  createLogger,
16
- createProvider,
17
16
  editFile,
18
17
  executeBash,
19
18
  globFiles,
@@ -22,11 +21,10 @@ import {
22
21
  handleTaskTool,
23
22
  isTrustedSkillPath,
24
23
  readFile,
25
- resolveActiveProfile,
26
24
  todo,
27
25
  webFetch,
28
26
  writeFile
29
- } from "./chunk-I4VLKL2I.js";
27
+ } from "./chunk-V3JQ7HAU.js";
30
28
  import {
31
29
  handleSkillInput,
32
30
  loadJobs,
@@ -34,12 +32,14 @@ import {
34
32
  upsertJob
35
33
  } from "./chunk-ZPGOARYH.js";
36
34
  import {
35
+ createProvider,
37
36
  createSessionMetadata,
38
37
  generateTitle,
39
38
  getContextLimit,
39
+ resolveActiveProfile,
40
40
  updateSessionMetadata,
41
41
  writeSessionMetadata
42
- } from "./chunk-72HNTRAP.js";
42
+ } from "./chunk-SIQQG6FT.js";
43
43
 
44
44
  // src/ui/index.ts
45
45
  import { default as default2 } from "react";
@@ -3,7 +3,7 @@ const _ew=process.emitWarning.bind(process);process.emitWarning=function(w,...a)
3
3
  import {
4
4
  cmdSessionsFork,
5
5
  cmdSessionsReplay
6
- } from "./chunk-FPB3QTP5.js";
6
+ } from "./chunk-N3IDMSG5.js";
7
7
  import {
8
8
  handleSkillInput,
9
9
  loadJobs,
@@ -15,8 +15,9 @@ import {
15
15
  findSession,
16
16
  listSessions,
17
17
  loadMessagesFromJsonl,
18
+ resolveActiveProfile,
18
19
  saveConfig
19
- } from "./chunk-72HNTRAP.js";
20
+ } from "./chunk-SIQQG6FT.js";
20
21
 
21
22
  // src/web/server.ts
22
23
  import Fastify from "fastify";
@@ -1604,7 +1605,7 @@ async function chatRoutes(app, opts) {
1604
1605
  if (opts.autoApprove) {
1605
1606
  sessionOpts.autoApproveNormal = true;
1606
1607
  }
1607
- if (!opts.config.apiKey) {
1608
+ if (!resolveActiveProfile(opts.config).apiKey) {
1608
1609
  return reply.code(400).send({
1609
1610
  success: false,
1610
1611
  error: "\u672A\u914D\u7F6E API Key\uFF0C\u8BF7\u5148\u70B9\u51FB\u53F3\u4E0A\u89D2\u300C\u914D\u7F6E\u300D\u6309\u94AE\u586B\u5199"
@@ -1665,7 +1666,7 @@ async function chatRoutes(app, opts) {
1665
1666
  if (manager.getSession(id)) {
1666
1667
  return reply.send({ success: true, resumed: false });
1667
1668
  }
1668
- if (!opts.config.apiKey) {
1669
+ if (!resolveActiveProfile(opts.config).apiKey) {
1669
1670
  return reply.code(400).send({
1670
1671
  success: false,
1671
1672
  error: "\u672A\u914D\u7F6E API Key\uFF0C\u8BF7\u5148\u70B9\u51FB\u53F3\u4E0A\u89D2\u300C\u914D\u7F6E\u300D\u6309\u94AE\u586B\u5199"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.5.45",
3
+ "version": "0.5.46",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",