@zhongqian97-code/ecode 0.5.44 → 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.
- package/dist/{chunk-FPB3QTP5.js → chunk-N3IDMSG5.js} +1 -1
- package/dist/chunk-SIQQG6FT.js +727 -0
- package/dist/{chunk-I4VLKL2I.js → chunk-V3JQ7HAU.js} +3 -452
- package/dist/index.js +9 -9
- package/dist/{ui-3K5WAFNO.js → ui-G7UOLPFA.js} +4 -4
- package/dist/{web-ONRJYFTB.js → web-XPP3PJJZ.js} +5 -4
- package/package.json +1 -1
- package/dist/chunk-72HNTRAP.js +0 -278
package/dist/chunk-72HNTRAP.js
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const _ew=process.emitWarning.bind(process);process.emitWarning=function(w,...a){if((w?.message??w)?.includes?.('punycode'))return;_ew(w,...a);};
|
|
3
|
-
|
|
4
|
-
// src/config.ts
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
6
|
-
import { homedir } from "os";
|
|
7
|
-
import { join } from "path";
|
|
8
|
-
function expandTilde(p) {
|
|
9
|
-
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
|
|
10
|
-
return join(homedir(), p.slice(1));
|
|
11
|
-
}
|
|
12
|
-
return p;
|
|
13
|
-
}
|
|
14
|
-
var MODEL_CONTEXT_LIMITS = {
|
|
15
|
-
// OpenAI GPT 系列
|
|
16
|
-
"gpt-4o": 128e3,
|
|
17
|
-
"gpt-4o-mini": 128e3,
|
|
18
|
-
"gpt-4-turbo": 128e3,
|
|
19
|
-
"gpt-4": 8192,
|
|
20
|
-
// 老版 GPT-4 上下文极小,需特别注意
|
|
21
|
-
"gpt-3.5-turbo": 16385,
|
|
22
|
-
// OpenAI o 系列推理模型
|
|
23
|
-
"o1": 2e5,
|
|
24
|
-
"o1-mini": 128e3,
|
|
25
|
-
"o1-preview": 128e3,
|
|
26
|
-
"o3": 2e5,
|
|
27
|
-
"o3-mini": 2e5,
|
|
28
|
-
// Anthropic Claude 系列(全线支持 200K)
|
|
29
|
-
"claude-3-5-sonnet-20241022": 2e5,
|
|
30
|
-
"claude-3-5-haiku-20241022": 2e5,
|
|
31
|
-
"claude-3-opus-20240229": 2e5,
|
|
32
|
-
"claude-3-sonnet-20240229": 2e5,
|
|
33
|
-
"claude-3-haiku-20240307": 2e5,
|
|
34
|
-
"claude-sonnet-4-6": 2e5,
|
|
35
|
-
"claude-opus-4-7": 2e5,
|
|
36
|
-
"claude-haiku-4-5-20251001": 2e5,
|
|
37
|
-
// DeepSeek 系列(上下文较小,截断策略需更激进)
|
|
38
|
-
"deepseek-chat": 65536,
|
|
39
|
-
"deepseek-reasoner": 65536,
|
|
40
|
-
// MiniMax 系列(baseUrl: https://api.minimax.chat/v1)
|
|
41
|
-
"MiniMax-M2.5": 1e6,
|
|
42
|
-
"MiniMax-M2.5-highspeed": 192e3,
|
|
43
|
-
"MiniMax-Text-01": 1e6
|
|
44
|
-
};
|
|
45
|
-
var DEFAULT_CONTEXT_LIMIT = 128e3;
|
|
46
|
-
function getContextLimit(model, override) {
|
|
47
|
-
if (override !== void 0) return override;
|
|
48
|
-
return MODEL_CONTEXT_LIMITS[model] ?? DEFAULT_CONTEXT_LIMIT;
|
|
49
|
-
}
|
|
50
|
-
var DEFAULTS = {
|
|
51
|
-
baseUrl: "https://api.openai.com/v1",
|
|
52
|
-
apiKey: "",
|
|
53
|
-
model: "gpt-4o",
|
|
54
|
-
dangerousPatterns: [
|
|
55
|
-
"rm -rf",
|
|
56
|
-
"sudo",
|
|
57
|
-
"chmod",
|
|
58
|
-
"chown",
|
|
59
|
-
"mkfs",
|
|
60
|
-
"dd",
|
|
61
|
-
"fdisk",
|
|
62
|
-
"kill",
|
|
63
|
-
"pkill",
|
|
64
|
-
"killall",
|
|
65
|
-
"reboot",
|
|
66
|
-
"shutdown",
|
|
67
|
-
"halt",
|
|
68
|
-
"curl -X DELETE",
|
|
69
|
-
"wget --delete-after"
|
|
70
|
-
],
|
|
71
|
-
logDir: void 0
|
|
72
|
-
};
|
|
73
|
-
function loadConfig() {
|
|
74
|
-
const configPath = join(homedir(), ".ecode", "config.json");
|
|
75
|
-
let fileConfig = {};
|
|
76
|
-
if (existsSync(configPath)) {
|
|
77
|
-
try {
|
|
78
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
79
|
-
fileConfig = JSON.parse(raw);
|
|
80
|
-
} catch (err) {
|
|
81
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
-
process.stderr.write(`[ecode] warning: ${configPath} parse failed (${msg}), falling back to defaults/env
|
|
83
|
-
`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
// ?? 运算符:仅在左侧为 null / undefined 时才取右侧值,
|
|
88
|
-
// 与 || 的区别在于不会跳过空字符串,保证显式设置 "" 也能生效
|
|
89
|
-
baseUrl: process.env.ECODE_BASE_URL ?? fileConfig.baseUrl ?? DEFAULTS.baseUrl,
|
|
90
|
-
apiKey: process.env.ECODE_API_KEY ?? fileConfig.apiKey ?? DEFAULTS.apiKey,
|
|
91
|
-
model: process.env.ECODE_MODEL ?? fileConfig.model ?? DEFAULTS.model,
|
|
92
|
-
// dangerousPatterns 不支持环境变量注入:命令数组通过单个环境变量传递需要转义,
|
|
93
|
-
// 容易引入歧义,因此仅支持配置文件覆盖
|
|
94
|
-
dangerousPatterns: fileConfig.dangerousPatterns ?? DEFAULTS.dangerousPatterns,
|
|
95
|
-
// logDir: ECODE_LOG_DIR 环境变量 > 配置文件 > undefined(禁用日志)
|
|
96
|
-
// expandTilde 将 ~ 替换为 homedir(),Node.js 不执行 shell 的 ~ 展开
|
|
97
|
-
logDir: (() => {
|
|
98
|
-
const raw = process.env.ECODE_LOG_DIR ?? fileConfig.logDir ?? DEFAULTS.logDir;
|
|
99
|
-
return raw ? expandTilde(raw) : raw;
|
|
100
|
-
})(),
|
|
101
|
-
// contextLimit 仅支持配置文件配置:数值类型在文件中更直观,
|
|
102
|
-
// 环境变量还需要 parseInt 转换,增加出错风险
|
|
103
|
-
contextLimit: fileConfig.contextLimit,
|
|
104
|
-
// ECODE_SYSTEM_PROMPT 环境变量优先;未设时从配置文件读取;
|
|
105
|
-
// 两者均未设时保持 undefined(由 resolveSystemPrompt 决定使用内置默认值)。
|
|
106
|
-
// 注意:空字符串 "" 是有效值(表示禁用),?? 不会跳过空字符串。
|
|
107
|
-
systemPrompt: process.env.ECODE_SYSTEM_PROMPT ?? fileConfig.systemPrompt,
|
|
108
|
-
// providers/defaultProvider 仅支持配置文件配置,不支持环境变量注入
|
|
109
|
-
providers: fileConfig.providers,
|
|
110
|
-
defaultProvider: fileConfig.defaultProvider,
|
|
111
|
-
// ECODE_WEB_TOKEN 环境变量优先;未设时从配置文件读取;
|
|
112
|
-
// 两者均未设时保持 undefined,运行时由 generateAccessToken() 生成随机令牌
|
|
113
|
-
webToken: process.env.ECODE_WEB_TOKEN ?? fileConfig.webToken
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
function saveConfig(partial) {
|
|
117
|
-
const configPath = join(homedir(), ".ecode", "config.json");
|
|
118
|
-
const configDir = join(homedir(), ".ecode");
|
|
119
|
-
mkdirSync(configDir, { recursive: true });
|
|
120
|
-
let existing = {};
|
|
121
|
-
if (existsSync(configPath)) {
|
|
122
|
-
try {
|
|
123
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
124
|
-
existing = JSON.parse(raw);
|
|
125
|
-
} catch (err) {
|
|
126
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
127
|
-
process.stderr.write(`[ecode] warning: ${configPath} parse failed (${msg}), overwriting with new config
|
|
128
|
-
`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
const merged = { ...existing, ...partial };
|
|
132
|
-
writeFileSync(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// src/sessions/metadata.ts
|
|
136
|
-
import * as crypto from "crypto";
|
|
137
|
-
import * as fs from "fs";
|
|
138
|
-
import * as path from "path";
|
|
139
|
-
function metadataPathFromLogFile(logFilePath) {
|
|
140
|
-
const base = path.basename(logFilePath, ".jsonl");
|
|
141
|
-
const dir = path.dirname(logFilePath);
|
|
142
|
-
return path.join(dir, `${base}-session.json`);
|
|
143
|
-
}
|
|
144
|
-
function createSessionMetadata(logFilePath, model, id) {
|
|
145
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
146
|
-
return {
|
|
147
|
-
id: id ?? crypto.randomUUID(),
|
|
148
|
-
startTime: now,
|
|
149
|
-
lastActivity: now,
|
|
150
|
-
cwd: process.cwd(),
|
|
151
|
-
model,
|
|
152
|
-
title: "",
|
|
153
|
-
turnCount: 0,
|
|
154
|
-
totalTokens: 0,
|
|
155
|
-
logFile: path.basename(logFilePath)
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function writeSessionMetadata(logFilePath, metadata) {
|
|
159
|
-
const metaPath = metadataPathFromLogFile(logFilePath);
|
|
160
|
-
try {
|
|
161
|
-
fs.writeFileSync(metaPath, JSON.stringify(metadata, null, 2) + "\n");
|
|
162
|
-
} catch (err) {
|
|
163
|
-
process.stderr.write(`[sessions] Failed to write metadata: ${err}
|
|
164
|
-
`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
function readSessionMetadata(metaFilePath) {
|
|
168
|
-
try {
|
|
169
|
-
const raw = fs.readFileSync(metaFilePath, "utf-8");
|
|
170
|
-
return JSON.parse(raw);
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function updateSessionMetadata(logFilePath, partial) {
|
|
176
|
-
const metaPath = metadataPathFromLogFile(logFilePath);
|
|
177
|
-
let existing = null;
|
|
178
|
-
try {
|
|
179
|
-
const raw = fs.readFileSync(metaPath, "utf-8");
|
|
180
|
-
existing = JSON.parse(raw);
|
|
181
|
-
} catch {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
writeSessionMetadata(logFilePath, { ...existing, ...partial });
|
|
185
|
-
}
|
|
186
|
-
function listSessions(logDir) {
|
|
187
|
-
try {
|
|
188
|
-
const files = fs.readdirSync(logDir);
|
|
189
|
-
const metaFiles = files.filter((f) => f.endsWith("-session.json"));
|
|
190
|
-
const sessions = [];
|
|
191
|
-
for (const file of metaFiles) {
|
|
192
|
-
const meta = readSessionMetadata(path.join(logDir, file));
|
|
193
|
-
if (meta) sessions.push(meta);
|
|
194
|
-
}
|
|
195
|
-
return sessions.sort(
|
|
196
|
-
(a, b) => b.lastActivity.localeCompare(a.lastActivity)
|
|
197
|
-
);
|
|
198
|
-
} catch {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function findSession(logDir, idOrPrefix) {
|
|
203
|
-
const sessions = listSessions(logDir);
|
|
204
|
-
return sessions.find(
|
|
205
|
-
(s) => s.id === idOrPrefix || s.id.startsWith(idOrPrefix)
|
|
206
|
-
) ?? null;
|
|
207
|
-
}
|
|
208
|
-
function generateTitle(firstUserMessage) {
|
|
209
|
-
const oneLine = firstUserMessage.replace(/\n+/g, " ").trim();
|
|
210
|
-
return oneLine.length > 50 ? oneLine.slice(0, 47) + "..." : oneLine;
|
|
211
|
-
}
|
|
212
|
-
function deleteSessionFiles(logDir, id) {
|
|
213
|
-
const session = findSession(logDir, id);
|
|
214
|
-
if (!session) return false;
|
|
215
|
-
const logFilePath = path.join(logDir, session.logFile);
|
|
216
|
-
const metaFilePath = metadataPathFromLogFile(logFilePath);
|
|
217
|
-
let deleted = false;
|
|
218
|
-
try {
|
|
219
|
-
fs.unlinkSync(logFilePath);
|
|
220
|
-
deleted = true;
|
|
221
|
-
} catch {
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
fs.unlinkSync(metaFilePath);
|
|
225
|
-
deleted = true;
|
|
226
|
-
} catch {
|
|
227
|
-
}
|
|
228
|
-
return deleted;
|
|
229
|
-
}
|
|
230
|
-
function loadMessagesFromJsonl(logFilePath) {
|
|
231
|
-
try {
|
|
232
|
-
const content = fs.readFileSync(logFilePath, "utf-8");
|
|
233
|
-
const lines = content.split("\n").filter((l) => l.trim());
|
|
234
|
-
const messages = [];
|
|
235
|
-
for (const line of lines) {
|
|
236
|
-
try {
|
|
237
|
-
const entry = JSON.parse(line);
|
|
238
|
-
if (!entry.role || entry.role === "system") continue;
|
|
239
|
-
if (entry.role === "user") {
|
|
240
|
-
messages.push({ role: "user", content: entry.content ?? "" });
|
|
241
|
-
} else if (entry.role === "assistant") {
|
|
242
|
-
const msg = {
|
|
243
|
-
role: "assistant",
|
|
244
|
-
content: entry.content ?? null
|
|
245
|
-
};
|
|
246
|
-
if (entry.tool_calls) {
|
|
247
|
-
msg.tool_calls = entry.tool_calls;
|
|
248
|
-
}
|
|
249
|
-
messages.push(msg);
|
|
250
|
-
} else if (entry.role === "tool") {
|
|
251
|
-
messages.push({
|
|
252
|
-
role: "tool",
|
|
253
|
-
tool_call_id: entry.tool_call_id ?? "",
|
|
254
|
-
content: entry.content ?? ""
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
} catch {
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return messages;
|
|
261
|
-
} catch {
|
|
262
|
-
return [];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export {
|
|
267
|
-
getContextLimit,
|
|
268
|
-
loadConfig,
|
|
269
|
-
saveConfig,
|
|
270
|
-
createSessionMetadata,
|
|
271
|
-
writeSessionMetadata,
|
|
272
|
-
updateSessionMetadata,
|
|
273
|
-
listSessions,
|
|
274
|
-
findSession,
|
|
275
|
-
generateTitle,
|
|
276
|
-
deleteSessionFiles,
|
|
277
|
-
loadMessagesFromJsonl
|
|
278
|
-
};
|