@ia-ccun/code-agent-cli 0.0.47-beta.2 → 0.0.47-beta.3
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/bin/cli.js
CHANGED
|
@@ -209,7 +209,8 @@ const env = {
|
|
|
209
209
|
AICODE_CLI_NAME: config.name,
|
|
210
210
|
AICODE_CLI_VERSION: localVersion || '',
|
|
211
211
|
AICODE_CLI_VERSION_CACHE: versionCachePath,
|
|
212
|
-
PI_SKIP_VERSION_CHECK: '1'
|
|
212
|
+
PI_SKIP_VERSION_CHECK: '1',
|
|
213
|
+
PI_OFFLINE: '1' // 禁用包更新检查
|
|
213
214
|
};
|
|
214
215
|
|
|
215
216
|
// 传递配置目录给 pi
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Monitor Extension - Monitor and alert on errors
|
|
3
|
+
*
|
|
4
|
+
* This extension monitors for errors during agent execution and displays
|
|
5
|
+
* formatted console alerts with instructions to follow enterprise WeChat groups.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Monitors tool execution results for errors
|
|
9
|
+
* - Monitors API responses for error patterns (including JSON format errors)
|
|
10
|
+
* - Monitors tool call outcomes for failure indicators
|
|
11
|
+
* - Displays formatted alerts with troubleshooting tips
|
|
12
|
+
* - Categorizes errors by severity
|
|
13
|
+
*
|
|
14
|
+
* Triggers on:
|
|
15
|
+
* - Tool execution errors (bash failures, file operations, etc.)
|
|
16
|
+
* - Agent runtime errors
|
|
17
|
+
* - API errors in JSON format (e.g., 500 {"type":"error",...})
|
|
18
|
+
* - Error messages in tool results
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
22
|
+
|
|
23
|
+
// Error categories with descriptions and suggested actions
|
|
24
|
+
const ERROR_CATEGORIES: Record<string, { name: string; severity: "info" | "warning" | "critical"; action: string }> = {
|
|
25
|
+
// Common error patterns
|
|
26
|
+
timeout: { name: "执行超时", severity: "warning", action: "请重试请求或增加超时时间" },
|
|
27
|
+
unauthorized: { name: "未授权访问", severity: "warning", action: "请检查 API Key 是否正确配置" },
|
|
28
|
+
forbidden: { name: "访问被拒绝", severity: "warning", action: "请检查账户权限或配额" },
|
|
29
|
+
not_found: { name: "资源不存在", severity: "info", action: "请检查请求的端点或资源路径" },
|
|
30
|
+
rate_limit: { name: "请求过于频繁", severity: "warning", action: "请降低请求频率或等待后重试" },
|
|
31
|
+
connection: { name: "连接错误", severity: "warning", action: "请检查网络连接" },
|
|
32
|
+
network: { name: "网络错误", severity: "warning", action: "请检查网络连接" },
|
|
33
|
+
econnrefused: { name: "连接被拒绝", severity: "warning", action: "请检查服务是否运行" },
|
|
34
|
+
|
|
35
|
+
// Server-side errors
|
|
36
|
+
500: { name: "服务器内部错误", severity: "critical", action: "服务端问题,请稍后重试" },
|
|
37
|
+
502: { name: "网关错误", severity: "critical", action: "上游服务异常,请稍后重试" },
|
|
38
|
+
503: { name: "服务不可用", severity: "critical", action: "服务暂时不可用,请关注服务公告" },
|
|
39
|
+
504: { name: "网关超时", severity: "critical", action: "上游服务响应超时,请稍后重试" },
|
|
40
|
+
507: { name: "存储空间不足", severity: "critical", action: "请联系管理员检查服务器资源" },
|
|
41
|
+
509: { name: "带宽限制超出", severity: "critical", action: "已达到带宽限制,请升级套餐或等待重置" },
|
|
42
|
+
|
|
43
|
+
// Client errors
|
|
44
|
+
400: { name: "请求参数错误", severity: "warning", action: "请检查请求参数是否正确" },
|
|
45
|
+
401: { name: "未授权访问", severity: "warning", action: "请检查 API Key 是否正确配置" },
|
|
46
|
+
403: { name: "访问被拒绝", severity: "warning", action: "请检查账户权限或配额" },
|
|
47
|
+
404: { name: "资源不存在", severity: "info", action: "请检查请求的端点或资源路径" },
|
|
48
|
+
408: { name: "请求超时", severity: "warning", action: "请重试请求" },
|
|
49
|
+
422: { name: "无法处理的请求", severity: "warning", action: "请检查请求格式和数据类型" },
|
|
50
|
+
429: { name: "请求过于频繁", severity: "warning", action: "请降低请求频率或等待后重试" },
|
|
51
|
+
ECONNREFUSED: { name: "连接被拒绝", severity: "critical", action: "请检查服务是否运行" },
|
|
52
|
+
ETIMEDOUT: { name: "连接超时", severity: "warning", action: "请检查网络连接或服务状态" },
|
|
53
|
+
|
|
54
|
+
// API/Model specific errors
|
|
55
|
+
"api_error": { name: "API 错误", severity: "critical", action: "API 服务异常,请稍后重试或联系管理员" },
|
|
56
|
+
"model_not_supported": { name: "模型不支持", severity: "critical", action: "请检查账户套餐是否支持该模型,或切换到支持的模型" },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// WeChat groups to follow
|
|
60
|
+
const ALERT_CHANNELS = [
|
|
61
|
+
{ name: "NUAI助手", description: "通用服务通知和帮助" },
|
|
62
|
+
{ name: "大模型服务群(服务变更消息)", description: "服务变更、维护通知" },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
function getSeverityIcon(severity: "info" | "warning" | "critical"): string {
|
|
66
|
+
return { info: "ℹ️", warning: "⚠️", critical: "🔴" }[severity];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function formatErrorAlert(title: string, message: string, details?: Record<string, unknown>): string {
|
|
70
|
+
const channelsList = ALERT_CHANNELS.map(
|
|
71
|
+
(ch) => ` 📢 ${ch.name} - ${ch.description}`
|
|
72
|
+
).join("\n");
|
|
73
|
+
|
|
74
|
+
let detailsStr = "";
|
|
75
|
+
if (details && Object.keys(details).length > 0) {
|
|
76
|
+
detailsStr = `
|
|
77
|
+
错误详情:
|
|
78
|
+
${JSON.stringify(details, null, 2)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `
|
|
82
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
83
|
+
${title}
|
|
84
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
85
|
+
${message}${detailsStr}
|
|
86
|
+
|
|
87
|
+
请关注以下企业微信群获取服务信息:
|
|
88
|
+
${channelsList}
|
|
89
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Parse JSON error format like: "500 {"type":"error","message":"..."}"
|
|
95
|
+
*/
|
|
96
|
+
function parseJsonErrorFormat(text: string): { statusCode?: string; errorType?: string; errorMessage?: string } | null {
|
|
97
|
+
// Match pattern like: "500 {"type":"error","message":"..."}" or just "{"type":"error","message":"..."}"
|
|
98
|
+
const match = text.match(/^(?:(\d{3})\s*)?(\{.*\})$/s);
|
|
99
|
+
if (match) {
|
|
100
|
+
try {
|
|
101
|
+
const json = JSON.parse(match[2]);
|
|
102
|
+
if (json && json.type === "error") {
|
|
103
|
+
return {
|
|
104
|
+
statusCode: match[1],
|
|
105
|
+
errorType: json.error?.type,
|
|
106
|
+
errorMessage: json.error?.message || json.message,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// Not valid JSON, continue with other detection
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function detectErrorCategory(errorMsg: string): { name: string; severity: "info" | "warning" | "critical"; action: string } {
|
|
117
|
+
const lowerMsg = errorMsg.toLowerCase();
|
|
118
|
+
|
|
119
|
+
// First, check for JSON error format (e.g., "500 {"type":"error",...}")
|
|
120
|
+
const jsonError = parseJsonErrorFormat(errorMsg);
|
|
121
|
+
if (jsonError) {
|
|
122
|
+
// Check for model_not_supported or similar errors
|
|
123
|
+
if (jsonError.errorMessage?.toLowerCase().includes("not support") ||
|
|
124
|
+
jsonError.errorMessage?.toLowerCase().includes("not supported") ||
|
|
125
|
+
jsonError.errorMessage?.toLowerCase().includes("model")) {
|
|
126
|
+
return ERROR_CATEGORIES["model_not_supported"];
|
|
127
|
+
}
|
|
128
|
+
if (jsonError.errorType === "api_error") {
|
|
129
|
+
return ERROR_CATEGORIES["api_error"];
|
|
130
|
+
}
|
|
131
|
+
// Fall back to status code if available
|
|
132
|
+
if (jsonError.statusCode && ERROR_CATEGORIES[jsonError.statusCode]) {
|
|
133
|
+
return ERROR_CATEGORIES[jsonError.statusCode];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check for HTTP status codes in error message
|
|
138
|
+
const statusMatch = errorMsg.match(/\b(4\d{2}|5\d{2})\b/);
|
|
139
|
+
if (statusMatch) {
|
|
140
|
+
const status = statusMatch[1];
|
|
141
|
+
if (ERROR_CATEGORIES[status]) {
|
|
142
|
+
return ERROR_CATEGORIES[status];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check for known error patterns
|
|
147
|
+
if (lowerMsg.includes("timeout") || lowerMsg.includes("timed out") || lowerMsg.includes("etimedout")) {
|
|
148
|
+
return ERROR_CATEGORIES.timeout;
|
|
149
|
+
}
|
|
150
|
+
if (lowerMsg.includes("unauthorized") || lowerMsg.includes("401")) {
|
|
151
|
+
return ERROR_CATEGORIES.unauthorized;
|
|
152
|
+
}
|
|
153
|
+
if (lowerMsg.includes("forbidden") || lowerMsg.includes("403")) {
|
|
154
|
+
return ERROR_CATEGORIES.forbidden;
|
|
155
|
+
}
|
|
156
|
+
if (lowerMsg.includes("not found") || lowerMsg.includes("404")) {
|
|
157
|
+
return ERROR_CATEGORIES.not_found;
|
|
158
|
+
}
|
|
159
|
+
if (lowerMsg.includes("rate limit") || lowerMsg.includes("429")) {
|
|
160
|
+
return ERROR_CATEGORIES.rate_limit;
|
|
161
|
+
}
|
|
162
|
+
if (lowerMsg.includes("econnrefused") || lowerMsg.includes("connection refused")) {
|
|
163
|
+
return ERROR_CATEGORIES.econnrefused;
|
|
164
|
+
}
|
|
165
|
+
if (lowerMsg.includes("connection") || lowerMsg.includes("network")) {
|
|
166
|
+
return ERROR_CATEGORIES.connection;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check for model-related errors
|
|
170
|
+
if (lowerMsg.includes("not support") || lowerMsg.includes("not supported") ||
|
|
171
|
+
lowerMsg.includes("model") || lowerMsg.includes("token plan")) {
|
|
172
|
+
return ERROR_CATEGORIES["model_not_supported"];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Default
|
|
176
|
+
return { name: "未知错误", severity: "warning", action: "请查看详细错误信息" };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default function (pi: ExtensionAPI) {
|
|
180
|
+
// Counter for error tracking
|
|
181
|
+
let errorCount = 0;
|
|
182
|
+
let toolErrorCount = 0;
|
|
183
|
+
let apiErrorCount = 0;
|
|
184
|
+
const startTime = Date.now();
|
|
185
|
+
|
|
186
|
+
// Monitor API responses for errors (this catches the JSON format errors)
|
|
187
|
+
pi.on("model_response", (event) => {
|
|
188
|
+
const { response } = event;
|
|
189
|
+
|
|
190
|
+
// Check for JSON error format in response text
|
|
191
|
+
const responseText = typeof response === "string" ? response : JSON.stringify(response);
|
|
192
|
+
const jsonError = parseJsonErrorFormat(responseText);
|
|
193
|
+
|
|
194
|
+
if (jsonError && jsonError.errorType) {
|
|
195
|
+
apiErrorCount++;
|
|
196
|
+
errorCount++;
|
|
197
|
+
|
|
198
|
+
let category: { name: string; severity: "info" | "warning" | "critical"; action: string };
|
|
199
|
+
let title: string;
|
|
200
|
+
|
|
201
|
+
// Determine error category based on error type/message
|
|
202
|
+
if (jsonError.errorMessage?.toLowerCase().includes("not support") ||
|
|
203
|
+
jsonError.errorMessage?.toLowerCase().includes("not supported") ||
|
|
204
|
+
jsonError.errorMessage?.toLowerCase().includes("model") ||
|
|
205
|
+
jsonError.errorMessage?.toLowerCase().includes("token plan")) {
|
|
206
|
+
category = ERROR_CATEGORIES["model_not_supported"];
|
|
207
|
+
title = "🔴 API 错误 - 模型不支持";
|
|
208
|
+
} else {
|
|
209
|
+
category = ERROR_CATEGORIES[jsonError.errorType] ||
|
|
210
|
+
{ name: "API 错误", severity: "critical", action: "API 服务异常,请稍后重试" };
|
|
211
|
+
title = `${getSeverityIcon(category.severity)} API 错误 - ${jsonError.errorType}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(formatErrorAlert(
|
|
215
|
+
title,
|
|
216
|
+
`错误类型: ${jsonError.errorType}\n严重程度: ${category.severity.toUpperCase()}\n建议操作: ${category.action}`,
|
|
217
|
+
{
|
|
218
|
+
statusCode: jsonError.statusCode,
|
|
219
|
+
errorType: jsonError.errorType,
|
|
220
|
+
errorMessage: jsonError.errorMessage,
|
|
221
|
+
request_id: responseText.match(/"request_id":"([^"]+)"/)?.[1],
|
|
222
|
+
}
|
|
223
|
+
));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Monitor tool execution results for errors
|
|
228
|
+
pi.on("tool_result", (event) => {
|
|
229
|
+
const { toolCallId, toolName, result } = event;
|
|
230
|
+
|
|
231
|
+
// Check if result is an error
|
|
232
|
+
const isError = result?.isError === true;
|
|
233
|
+
const content = typeof result?.content === "string" ? result.content :
|
|
234
|
+
Array.isArray(result?.content) ? result.content.map((c: any) => c.text || c.content).join("\n") : "";
|
|
235
|
+
|
|
236
|
+
if (isError || content.toLowerCase().includes("error") || content.toLowerCase().includes("failed")) {
|
|
237
|
+
toolErrorCount++;
|
|
238
|
+
errorCount++;
|
|
239
|
+
|
|
240
|
+
const category = detectErrorCategory(content);
|
|
241
|
+
const icon = getSeverityIcon(category.severity);
|
|
242
|
+
|
|
243
|
+
console.log(formatErrorAlert(
|
|
244
|
+
`${icon} 工具执行错误 - ${toolName}`,
|
|
245
|
+
`错误类型: ${category.name}\n严重程度: ${category.severity.toUpperCase()}\n工具名称: ${toolName}\n建议操作: ${category.action}`,
|
|
246
|
+
{
|
|
247
|
+
toolCallId,
|
|
248
|
+
errorMessage: content.substring(0, 500),
|
|
249
|
+
}
|
|
250
|
+
));
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Monitor tool execution end for errors
|
|
255
|
+
pi.on("tool_execution_end", (event) => {
|
|
256
|
+
const { toolCallId, toolName, result, isError } = event;
|
|
257
|
+
|
|
258
|
+
if (isError) {
|
|
259
|
+
errorCount++;
|
|
260
|
+
|
|
261
|
+
const errorMsg = typeof result === "string" ? result : JSON.stringify(result);
|
|
262
|
+
const category = detectErrorCategory(errorMsg);
|
|
263
|
+
const icon = getSeverityIcon(category.severity);
|
|
264
|
+
|
|
265
|
+
console.log(formatErrorAlert(
|
|
266
|
+
`${icon} 工具执行失败 - ${toolName}`,
|
|
267
|
+
`错误类型: ${category.name}\n严重程度: ${category.severity.toUpperCase()}\n工具名称: ${toolName}\n建议操作: ${category.action}`,
|
|
268
|
+
{
|
|
269
|
+
toolCallId,
|
|
270
|
+
errorMessage: errorMsg?.substring(0, 500),
|
|
271
|
+
}
|
|
272
|
+
));
|
|
273
|
+
|
|
274
|
+
// Debug logging
|
|
275
|
+
if (process.env.DEBUG_ERRORS) {
|
|
276
|
+
console.error("[error-monitor] Tool execution error:", {
|
|
277
|
+
toolCallId,
|
|
278
|
+
toolName,
|
|
279
|
+
result,
|
|
280
|
+
isError,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Monitor user bash events for command failures
|
|
287
|
+
pi.on("user_bash", (event) => {
|
|
288
|
+
const { command, result } = event;
|
|
289
|
+
|
|
290
|
+
if (result?.exitCode !== 0) {
|
|
291
|
+
errorCount++;
|
|
292
|
+
|
|
293
|
+
const category = detectErrorCategory(result?.stderr || "");
|
|
294
|
+
const icon = getSeverityIcon(category.severity);
|
|
295
|
+
|
|
296
|
+
console.log(formatErrorAlert(
|
|
297
|
+
`${icon} Bash 命令执行失败`,
|
|
298
|
+
`命令: ${command}\n退出码: ${result?.exitCode}\n错误类型: ${category.name}\n建议操作: ${category.action}`,
|
|
299
|
+
{
|
|
300
|
+
command,
|
|
301
|
+
exitCode: result?.exitCode,
|
|
302
|
+
stderr: result?.stderr?.substring(0, 500),
|
|
303
|
+
stdout: result?.stdout?.substring(0, 500),
|
|
304
|
+
}
|
|
305
|
+
));
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Summary on session shutdown
|
|
310
|
+
pi.on("session_shutdown", () => {
|
|
311
|
+
const uptime = Math.round((Date.now() - startTime) / 1000);
|
|
312
|
+
|
|
313
|
+
if (errorCount > 0) {
|
|
314
|
+
console.log(`
|
|
315
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
316
|
+
📊 Error Monitor 会话统计
|
|
317
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
318
|
+
总错误数: ${errorCount}
|
|
319
|
+
API 错误数: ${apiErrorCount}
|
|
320
|
+
工具错误数: ${toolErrorCount}
|
|
321
|
+
运行时长: ${uptime}秒
|
|
322
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
323
|
+
`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Always show debug info if enabled
|
|
327
|
+
if (process.env.DEBUG_ERRORS) {
|
|
328
|
+
console.log(`[error-monitor] Session ended. Total errors: ${errorCount}, API errors: ${apiErrorCount}, Tool errors: ${toolErrorCount}`);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Logs before_provider_request: messages, parameters, raw payload
|
|
9
9
|
* - Logs after_provider_response: usage stats, response content, stop reason
|
|
10
10
|
* - Logs errors with context
|
|
11
|
-
* - Console output
|
|
11
|
+
* - Console output removed (logs to file only)
|
|
12
12
|
* - Session-based log files (one file per session)
|
|
13
13
|
*/
|
|
14
14
|
|
|
@@ -105,10 +105,7 @@ ${JSON.stringify({ ...payload, messages: filteredMessages }, null, 2)}
|
|
|
105
105
|
================================================================================
|
|
106
106
|
`;
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
console.log(logEntry);
|
|
110
|
-
|
|
111
|
-
// Also save to file
|
|
108
|
+
// Save to file
|
|
112
109
|
appendFileSync(logFile, logEntry, "utf8");
|
|
113
110
|
});
|
|
114
111
|
|
|
@@ -146,10 +143,7 @@ ${JSON.stringify(response, null, 2)}
|
|
|
146
143
|
|
|
147
144
|
`;
|
|
148
145
|
|
|
149
|
-
//
|
|
150
|
-
console.log(logEntry);
|
|
151
|
-
|
|
152
|
-
// Also save to file
|
|
146
|
+
// Save to file
|
|
153
147
|
appendFileSync(logFile, logEntry, "utf8");
|
|
154
148
|
});
|
|
155
149
|
|
|
@@ -172,12 +166,9 @@ ${JSON.stringify(context, null, 2)}
|
|
|
172
166
|
|
|
173
167
|
`;
|
|
174
168
|
|
|
175
|
-
//
|
|
176
|
-
console.error(logEntry);
|
|
177
|
-
|
|
178
|
-
// Also save to file
|
|
169
|
+
// Save to file
|
|
179
170
|
appendFileSync(logFile, logEntry, "utf8");
|
|
180
171
|
});
|
|
181
172
|
|
|
182
|
-
|
|
173
|
+
// Extension enabled silently (logs to file only, no console output)
|
|
183
174
|
}
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED