@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 for real-time monitoring
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
- // Print to console
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
- // Print to console
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
- // Print to console (errors in red)
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
- console.log(`[msg-debug] Debug logging enabled. Logs will be saved to: ${logFile}`);
173
+ // Extension enabled silently (logs to file only, no console output)
183
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ia-ccun/code-agent-cli",
3
- "version": "0.0.47-beta.2",
3
+ "version": "0.0.47-beta.3",
4
4
  "description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent(pi)原理封装的编码智能体(供学习使用)。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -170,6 +170,7 @@ const FORCE_OVERWRITE_LIST = [
170
170
  'APPEND_SYSTEM.md',
171
171
  'SYSTEM.md',
172
172
  'dangerous-patterns.json',
173
+ 'settings.json',
173
174
  'extensions/working-msg.ts',
174
175
  'extensions/custom-footer.ts',
175
176
  'extensions/custom.ts',