@zhongqian97-code/ecode 0.3.6 → 0.3.8
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.
- package/dist/index.js +94 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,7 +38,11 @@ var MODEL_CONTEXT_LIMITS = {
|
|
|
38
38
|
"claude-haiku-4-5-20251001": 2e5,
|
|
39
39
|
// DeepSeek 系列(上下文较小,截断策略需更激进)
|
|
40
40
|
"deepseek-chat": 65536,
|
|
41
|
-
"deepseek-reasoner": 65536
|
|
41
|
+
"deepseek-reasoner": 65536,
|
|
42
|
+
// MiniMax 系列(baseUrl: https://api.minimax.chat/v1)
|
|
43
|
+
"MiniMax-M2.5": 1e6,
|
|
44
|
+
"MiniMax-M2.5-highspeed": 192e3,
|
|
45
|
+
"MiniMax-Text-01": 1e6
|
|
42
46
|
};
|
|
43
47
|
var DEFAULT_CONTEXT_LIMIT = 128e3;
|
|
44
48
|
function getContextLimit(model, override) {
|
|
@@ -159,6 +163,7 @@ function createOpenAIProvider(profile) {
|
|
|
159
163
|
);
|
|
160
164
|
const tcAccumulator = /* @__PURE__ */ new Map();
|
|
161
165
|
let reasoningAccumulator = "";
|
|
166
|
+
const reasoningDetailsAcc = /* @__PURE__ */ new Map();
|
|
162
167
|
for await (const chunk of response) {
|
|
163
168
|
const choice = chunk.choices[0];
|
|
164
169
|
if (!choice) continue;
|
|
@@ -166,6 +171,26 @@ function createOpenAIProvider(profile) {
|
|
|
166
171
|
if (delta.reasoning_content) {
|
|
167
172
|
reasoningAccumulator += delta.reasoning_content;
|
|
168
173
|
}
|
|
174
|
+
if (delta.reasoning_details && delta.reasoning_details.length > 0) {
|
|
175
|
+
for (const rd of delta.reasoning_details) {
|
|
176
|
+
const id = rd.id ?? "";
|
|
177
|
+
const text = rd.text ?? "";
|
|
178
|
+
if (!reasoningDetailsAcc.has(id)) {
|
|
179
|
+
reasoningDetailsAcc.set(id, {
|
|
180
|
+
type: rd.type ?? "reasoning.text",
|
|
181
|
+
id,
|
|
182
|
+
format: rd.format ?? "",
|
|
183
|
+
index: rd.index ?? 0,
|
|
184
|
+
text: ""
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const existing = reasoningDetailsAcc.get(id);
|
|
188
|
+
existing.text += text;
|
|
189
|
+
if (!delta.reasoning_content && text) {
|
|
190
|
+
reasoningAccumulator += text;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
169
194
|
if (delta.tool_calls) {
|
|
170
195
|
for (const tc of delta.tool_calls) {
|
|
171
196
|
if (!tcAccumulator.has(tc.index)) {
|
|
@@ -192,6 +217,8 @@ function createOpenAIProvider(profile) {
|
|
|
192
217
|
// 让调用方用 if (chunk.toolCalls) 做简洁判断
|
|
193
218
|
toolCalls: tcAccumulator.size > 0 ? Array.from(tcAccumulator.values()) : void 0,
|
|
194
219
|
reasoning: reasoningAccumulator || void 0,
|
|
220
|
+
// reasoningDetails 仅在流中出现过结构化推理时返回(MiniMax 兼容)
|
|
221
|
+
reasoningDetails: reasoningDetailsAcc.size > 0 ? Array.from(reasoningDetailsAcc.values()) : void 0,
|
|
195
222
|
// 将 snake_case 的原始字段映射为 camelCase,对外接口保持一致
|
|
196
223
|
usage: rawUsage ? {
|
|
197
224
|
promptTokens: rawUsage.prompt_tokens,
|
|
@@ -200,9 +227,10 @@ function createOpenAIProvider(profile) {
|
|
|
200
227
|
} : void 0
|
|
201
228
|
};
|
|
202
229
|
} else {
|
|
230
|
+
const incrementalReasoning = delta.reasoning_content ?? (delta.reasoning_details && delta.reasoning_details.length > 0 ? delta.reasoning_details.map((rd) => rd.text ?? "").join("") : void 0);
|
|
203
231
|
yield {
|
|
204
232
|
text: delta.content ?? "",
|
|
205
|
-
reasoning:
|
|
233
|
+
reasoning: incrementalReasoning,
|
|
206
234
|
done: false
|
|
207
235
|
};
|
|
208
236
|
}
|
|
@@ -258,8 +286,9 @@ var ALLOWLIST = [
|
|
|
258
286
|
"date",
|
|
259
287
|
"whoami",
|
|
260
288
|
"which",
|
|
261
|
-
"env",
|
|
262
289
|
"printenv"
|
|
290
|
+
// "env" 故意排除:env <CMD> 会执行任意子命令,不适合 allow 级别。
|
|
291
|
+
// 仅显示环境变量的 printenv 保留在白名单。
|
|
263
292
|
];
|
|
264
293
|
var DEFAULT_DANGER_LIST = [
|
|
265
294
|
"rm -rf",
|
|
@@ -284,7 +313,10 @@ var INDIRECT_EXEC_LIST = [
|
|
|
284
313
|
"python3 -c",
|
|
285
314
|
"node -e",
|
|
286
315
|
"perl -e",
|
|
287
|
-
"ruby -e"
|
|
316
|
+
"ruby -e",
|
|
317
|
+
// eval 执行任意字符串作为 shell 命令,无论 payload 内容如何都视为危险。
|
|
318
|
+
// 与 xargs/python -c 的性质相同:执行能力本身就是风险。
|
|
319
|
+
"eval"
|
|
288
320
|
];
|
|
289
321
|
function hasFindExec(cmd) {
|
|
290
322
|
return /^find(\s|$)/.test(cmd) && /\s-exec(\s|$)/.test(cmd);
|
|
@@ -348,22 +380,70 @@ function splitByControlOps(cmd) {
|
|
|
348
380
|
}
|
|
349
381
|
function stripShellWrapper(cmd) {
|
|
350
382
|
const trimmed = cmd.trim();
|
|
351
|
-
const
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
383
|
+
const shellMatch = trimmed.match(/^(bash|sh)\s+([\s\S]*)/);
|
|
384
|
+
if (!shellMatch) return trimmed;
|
|
385
|
+
let rest = shellMatch[2];
|
|
386
|
+
let hasCFlag = false;
|
|
387
|
+
while (rest.length > 0) {
|
|
388
|
+
const flagMatch = rest.match(/^(-\w+)\s*([\s\S]*)/);
|
|
389
|
+
if (!flagMatch) break;
|
|
390
|
+
const flag = flagMatch[1];
|
|
391
|
+
rest = flagMatch[2];
|
|
392
|
+
if (flag.includes("c")) {
|
|
393
|
+
hasCFlag = true;
|
|
394
|
+
rest = rest.trimStart();
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
rest = rest.trimStart();
|
|
398
|
+
}
|
|
399
|
+
if (!hasCFlag || !rest.length) return trimmed;
|
|
400
|
+
if (rest.startsWith('"')) {
|
|
401
|
+
const m = rest.match(/^"((?:[^"\\]|\\.)*)"/);
|
|
402
|
+
if (m) return m[1];
|
|
403
|
+
} else if (rest.startsWith("'")) {
|
|
404
|
+
const m = rest.match(/^'([^']*)'/);
|
|
405
|
+
if (m) return m[1];
|
|
406
|
+
} else {
|
|
407
|
+
const m = rest.match(/^(\S+)/);
|
|
408
|
+
if (m) return m[1];
|
|
409
|
+
}
|
|
410
|
+
return trimmed;
|
|
411
|
+
}
|
|
412
|
+
function stripEnvWrapper(cmd) {
|
|
413
|
+
const trimmed = cmd.trim();
|
|
414
|
+
if (!trimmed.startsWith("env ") && trimmed !== "env") return trimmed;
|
|
415
|
+
let rest = trimmed.slice(3).trimStart();
|
|
416
|
+
while (rest.length > 0) {
|
|
417
|
+
const assignMatch = rest.match(/^(\w+=\S*)\s*([\s\S]*)/);
|
|
418
|
+
if (!assignMatch) break;
|
|
419
|
+
rest = assignMatch[2].trimStart();
|
|
420
|
+
}
|
|
421
|
+
if (!rest.length) return trimmed;
|
|
422
|
+
return rest;
|
|
423
|
+
}
|
|
424
|
+
function stripCommandWrapper(cmd) {
|
|
425
|
+
const trimmed = cmd.trim();
|
|
426
|
+
const m = trimmed.match(/^command\s+([\s\S]+)/);
|
|
427
|
+
if (m) return m[1].trimStart();
|
|
357
428
|
return trimmed;
|
|
358
429
|
}
|
|
359
430
|
function classifyCommand(cmd, dangerPatterns, _depth = 0) {
|
|
360
431
|
const trimmed = cmd.trim();
|
|
361
432
|
const patterns = dangerPatterns ?? DEFAULT_DANGER_LIST;
|
|
362
433
|
if (_depth < 5) {
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
434
|
+
const shellInner = stripShellWrapper(trimmed);
|
|
435
|
+
if (shellInner !== trimmed) {
|
|
436
|
+
if (classifyCommand(shellInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
|
|
437
|
+
return "normal";
|
|
438
|
+
}
|
|
439
|
+
const envInner = stripEnvWrapper(trimmed);
|
|
440
|
+
if (envInner !== trimmed) {
|
|
441
|
+
if (classifyCommand(envInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
|
|
442
|
+
return "normal";
|
|
443
|
+
}
|
|
444
|
+
const commandInner = stripCommandWrapper(trimmed);
|
|
445
|
+
if (commandInner !== trimmed) {
|
|
446
|
+
if (classifyCommand(commandInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
|
|
367
447
|
return "normal";
|
|
368
448
|
}
|
|
369
449
|
}
|