chatccc 0.2.108 → 0.2.110
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/package.json +63 -63
- package/src/index.ts +25 -5
- package/src/orchestrator.ts +6 -0
- package/src/wechat-platform.ts +24 -13
package/package.json
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "chatccc",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "./src/index.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"chatccc": "bin/chatccc.mjs"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"src/",
|
|
13
|
-
"bin/",
|
|
14
|
-
"scripts/postinstall-sharp-check.mjs",
|
|
15
|
-
"demo/ilink_echo_probe.ts",
|
|
16
|
-
"im-skills/",
|
|
17
|
-
"images/img_readme_*.jpg",
|
|
18
|
-
"images/img_readme_*.png",
|
|
19
|
-
"images/avatars/status_*.png",
|
|
20
|
-
"images/avatars/badges/",
|
|
21
|
-
"package.json",
|
|
22
|
-
"README.md",
|
|
23
|
-
"config.sample.json"
|
|
24
|
-
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"dev": "tsx src/index.ts",
|
|
27
|
-
"chatccc": "tsx src/index.ts",
|
|
28
|
-
"start": "tsx src/index.ts",
|
|
29
|
-
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
30
|
-
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
31
|
-
"demo:create-group": "tsx src/index.ts",
|
|
32
|
-
"demo:create-group:local": "tsx src/index.ts --local",
|
|
33
|
-
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
34
|
-
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
35
|
-
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
36
|
-
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
37
|
-
"test": "vitest run",
|
|
38
|
-
"test:watch": "vitest",
|
|
39
|
-
"postinstall": "node scripts/postinstall-sharp-check.mjs"
|
|
40
|
-
},
|
|
41
|
-
"dependencies": {
|
|
42
|
-
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
43
|
-
"@anthropic-ai/claude-agent-sdk": "0.2.133",
|
|
44
|
-
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
45
|
-
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
46
|
-
"ai": "^6.0.184",
|
|
47
|
-
"nodemailer": "^8.0.7",
|
|
48
|
-
"qrcode-terminal": "^0.12.0",
|
|
49
|
-
"sharp": "^0.34.5",
|
|
50
|
-
"tsx": "^4.0.0",
|
|
51
|
-
"ws": "^8.18.0"
|
|
52
|
-
},
|
|
53
|
-
"devDependencies": {
|
|
54
|
-
"@types/node": "^20.0.0",
|
|
55
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
56
|
-
"@types/ws": "^8.18.1",
|
|
57
|
-
"typescript": "^5.0.0",
|
|
58
|
-
"vitest": "^3.2.4"
|
|
59
|
-
},
|
|
60
|
-
"engines": {
|
|
61
|
-
"node": ">=20"
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "chatccc",
|
|
3
|
+
"version": "0.2.110",
|
|
4
|
+
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"chatccc": "bin/chatccc.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/",
|
|
13
|
+
"bin/",
|
|
14
|
+
"scripts/postinstall-sharp-check.mjs",
|
|
15
|
+
"demo/ilink_echo_probe.ts",
|
|
16
|
+
"im-skills/",
|
|
17
|
+
"images/img_readme_*.jpg",
|
|
18
|
+
"images/img_readme_*.png",
|
|
19
|
+
"images/avatars/status_*.png",
|
|
20
|
+
"images/avatars/badges/",
|
|
21
|
+
"package.json",
|
|
22
|
+
"README.md",
|
|
23
|
+
"config.sample.json"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "tsx src/index.ts",
|
|
27
|
+
"chatccc": "tsx src/index.ts",
|
|
28
|
+
"start": "tsx src/index.ts",
|
|
29
|
+
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
30
|
+
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
31
|
+
"demo:create-group": "tsx src/index.ts",
|
|
32
|
+
"demo:create-group:local": "tsx src/index.ts --local",
|
|
33
|
+
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
34
|
+
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
35
|
+
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
36
|
+
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"postinstall": "node scripts/postinstall-sharp-check.mjs"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
43
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.133",
|
|
44
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
45
|
+
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
46
|
+
"ai": "^6.0.184",
|
|
47
|
+
"nodemailer": "^8.0.7",
|
|
48
|
+
"qrcode-terminal": "^0.12.0",
|
|
49
|
+
"sharp": "^0.34.5",
|
|
50
|
+
"tsx": "^4.0.0",
|
|
51
|
+
"ws": "^8.18.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.0.0",
|
|
55
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
56
|
+
"@types/ws": "^8.18.1",
|
|
57
|
+
"typescript": "^5.0.0",
|
|
58
|
+
"vitest": "^3.2.4"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -660,8 +660,11 @@ async function startWechatSupervisor(): Promise<void> {
|
|
|
660
660
|
console.log("\n[WX] 启动微信 iLink 平台...");
|
|
661
661
|
|
|
662
662
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
663
|
+
const MAX_QR_RETRIES = 3;
|
|
664
|
+
let qrTimeoutCount = 0;
|
|
663
665
|
|
|
664
666
|
while (!wechatSignal.stopped) {
|
|
667
|
+
let isQrTimeout = false;
|
|
665
668
|
try {
|
|
666
669
|
await startWechatPlatform(
|
|
667
670
|
(text, chatId, openId, msgTimestamp, chatType, traceId) =>
|
|
@@ -669,14 +672,31 @@ async function startWechatSupervisor(): Promise<void> {
|
|
|
669
672
|
wechatSignal,
|
|
670
673
|
ILINK_REUSE_TOKEN_ON_START,
|
|
671
674
|
);
|
|
675
|
+
// 登录成功后重置计数
|
|
676
|
+
qrTimeoutCount = 0;
|
|
672
677
|
} catch (err) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
)
|
|
678
|
+
const msg = (err as Error).message ?? "";
|
|
679
|
+
isQrTimeout = msg.includes("QR 登录超时");
|
|
680
|
+
if (isQrTimeout) {
|
|
681
|
+
qrTimeoutCount++;
|
|
682
|
+
console.error(
|
|
683
|
+
`[WX] QR 登录超时 (${qrTimeoutCount}/${MAX_QR_RETRIES}): ${msg}`,
|
|
684
|
+
);
|
|
685
|
+
if (qrTimeoutCount >= MAX_QR_RETRIES) {
|
|
686
|
+
console.error(
|
|
687
|
+
`[WX] 已连续 ${MAX_QR_RETRIES} 次 QR 登录超时,放弃重试。如需重新尝试请重启 ChatCCC。`,
|
|
688
|
+
);
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
} else {
|
|
692
|
+
console.error(`[WX] 微信 iLink 崩溃: ${msg}`);
|
|
693
|
+
}
|
|
676
694
|
}
|
|
677
695
|
if (wechatSignal.stopped) break;
|
|
678
|
-
|
|
679
|
-
|
|
696
|
+
const delaySeconds = isQrTimeout ? 300 : 30;
|
|
697
|
+
const delayDesc = isQrTimeout ? "5 分钟" : "30 秒";
|
|
698
|
+
console.log(`[WX] ${delayDesc}后重试...`);
|
|
699
|
+
await sleep(delaySeconds * 1000);
|
|
680
700
|
}
|
|
681
701
|
console.log("[WX] 微信 iLink 平台已停止。");
|
|
682
702
|
}
|
package/src/orchestrator.ts
CHANGED
|
@@ -286,6 +286,12 @@ export async function handleCommand(
|
|
|
286
286
|
const content = buildCdContent(targetDir, withStats, isUpdate, sessionCwd);
|
|
287
287
|
await platform.sendCard(chatId, "新会话工作路径", content, "blue");
|
|
288
288
|
logTrace(tid, "DONE", { outcome: "cd_path", targetDir, isUpdate });
|
|
289
|
+
|
|
290
|
+
// 微信模式下,若用户没有活跃会话,自动创建新会话
|
|
291
|
+
if (platform.kind === "wechat" && !sessionInfoMap.has(chatId)) {
|
|
292
|
+
logTrace(tid, "BRANCH", { cmd: "/new", trigger: "auto_after_cd" });
|
|
293
|
+
await handleCommand(platform, "/new", chatId, openId, msgTimestamp, chatType, traceId);
|
|
294
|
+
}
|
|
289
295
|
}
|
|
290
296
|
return;
|
|
291
297
|
}
|
package/src/wechat-platform.ts
CHANGED
|
@@ -429,23 +429,34 @@ export async function startWechatPlatform(
|
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
// 请求新 QR
|
|
433
|
-
|
|
432
|
+
// 请求新 QR(5 分钟内未扫码则放弃本轮,由 supervisor 稍后重试)
|
|
433
|
+
const QR_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
434
|
+
platformLog("启动微信 iLink QR 登录(5 分钟内未扫码将自动重试)...");
|
|
434
435
|
ilinkWire = new OpenIlinkWire("", {
|
|
435
436
|
base_url: saved.baseUrl,
|
|
436
437
|
});
|
|
437
438
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
439
|
+
let qrExpiredCount = 0;
|
|
440
|
+
const loginResult = await Promise.race([
|
|
441
|
+
ilinkWire.loginWithQr({
|
|
442
|
+
on_qrcode: (content) => {
|
|
443
|
+
printScanMaterial(content);
|
|
444
|
+
},
|
|
445
|
+
on_scanned: () => {
|
|
446
|
+
platformLog("QR 已扫描,请在微信中确认登录。");
|
|
447
|
+
},
|
|
448
|
+
on_expired: (attempt, maxAttempts) => {
|
|
449
|
+
qrExpiredCount++;
|
|
450
|
+
platformLog(`QR 已过期,正在刷新 (${attempt}/${maxAttempts})。`);
|
|
451
|
+
},
|
|
452
|
+
}),
|
|
453
|
+
new Promise<never>((_, reject) =>
|
|
454
|
+
setTimeout(
|
|
455
|
+
() => reject(new Error(`QR 登录超时(${QR_LOGIN_TIMEOUT_MS / 60000} 分钟内未扫码,已刷新 ${qrExpiredCount} 次)`)),
|
|
456
|
+
QR_LOGIN_TIMEOUT_MS,
|
|
457
|
+
),
|
|
458
|
+
),
|
|
459
|
+
]);
|
|
449
460
|
|
|
450
461
|
if (!loginResult.connected) {
|
|
451
462
|
throw new Error(`微信 iLink QR 登录失败: ${loginResult.message}`);
|