@raolin2025/claude-code-node 2.6.14 → 2.6.16
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 +1 -1
- package/src/core/cli.js +63 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raolin2025/claude-code-node",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.16",
|
|
4
4
|
"description": "Node.js AI Code Agent CLI - Zero dependencies, pure JavaScript, security hardened, multi-channel notifications, Telegram & QQ Bot remote programming, rich media upload, multi-account management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/index.js",
|
package/src/core/cli.js
CHANGED
|
@@ -599,6 +599,8 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
599
599
|
let currentSource = 'cli'
|
|
600
600
|
// 等待中的 Telegram 权限确认(resolve 回调)— 远程用户回复 y/n/a 时响应
|
|
601
601
|
let pendingConfirm = null
|
|
602
|
+
// 最近一次 /models 拉取的模型列表(供 /model <编号> 选择)
|
|
603
|
+
let modelList = []
|
|
602
604
|
|
|
603
605
|
// 处理输入行
|
|
604
606
|
// source: 'cli' 来自终端输入, 'telegram' 来自 Telegram
|
|
@@ -634,6 +636,15 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
634
636
|
}
|
|
635
637
|
|
|
636
638
|
if (trimmed.startsWith('/')) {
|
|
639
|
+
// 来自 Telegram 的命令:捕获 console 输出并回发到 Telegram
|
|
640
|
+
const isTgCmd = source === 'telegram' && tgListener?.bot
|
|
641
|
+
let tgOut = ''
|
|
642
|
+
const origLog = console.log
|
|
643
|
+
if (isTgCmd) {
|
|
644
|
+
tgOut = ''
|
|
645
|
+
console.log = (...args) => { tgOut += args.map(String).join(' ') + '\n'; origLog(...args) }
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
637
648
|
const [cmd, ...rest] = trimmed.slice(1).split(' ')
|
|
638
649
|
switch (cmd) {
|
|
639
650
|
case 'help':
|
|
@@ -646,7 +657,16 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
646
657
|
}
|
|
647
658
|
break
|
|
648
659
|
case 'model':
|
|
649
|
-
|
|
660
|
+
// /model <名字或编号> — 支持用 /models 列表里的编号切换
|
|
661
|
+
if (rest[0]) {
|
|
662
|
+
const num = parseInt(rest[0], 10)
|
|
663
|
+
if (!isNaN(num) && modelList.length > 0 && num >= 1 && num <= modelList.length) {
|
|
664
|
+
engine.config.model = modelList[num - 1]
|
|
665
|
+
} else {
|
|
666
|
+
engine.config.model = rest.join(' ')
|
|
667
|
+
}
|
|
668
|
+
console.log(`Model → ${engine.config.model}`)
|
|
669
|
+
}
|
|
650
670
|
else console.log(`Model: ${engine.config.model}`)
|
|
651
671
|
break
|
|
652
672
|
case 'models': {
|
|
@@ -668,17 +688,23 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
668
688
|
const data = await res.json()
|
|
669
689
|
const models = data.data || []
|
|
670
690
|
if (models.length === 0) { console.log('No models returned'); break }
|
|
691
|
+
// 记录模型列表,供 /model <编号> 选择
|
|
692
|
+
modelList = models.map(m => m.id || m)
|
|
671
693
|
console.log(`\nAvailable models (${models.length}):`)
|
|
672
|
-
|
|
673
|
-
const id = m.id || m
|
|
694
|
+
modelList.forEach((id, i) => {
|
|
674
695
|
console.log(` ${(i + 1).toString().padStart(2)}. ${id}`)
|
|
675
696
|
})
|
|
697
|
+
// 来自 Telegram:直接列出,不等待 CLI 交互(用 /model <名字或编号> 切换)
|
|
698
|
+
if (source === 'telegram') {
|
|
699
|
+
console.log('\nℹ️ 用 /model <名字或编号> 切换模型')
|
|
700
|
+
break
|
|
701
|
+
}
|
|
676
702
|
console.log('\nType a number to select, or model name directly:')
|
|
677
703
|
const answer = await new Promise(resolve => rl.question('> ', resolve))
|
|
678
704
|
const num = parseInt(answer, 10)
|
|
679
705
|
let selected
|
|
680
|
-
if (!isNaN(num) && num >= 1 && num <=
|
|
681
|
-
selected =
|
|
706
|
+
if (!isNaN(num) && num >= 1 && num <= modelList.length) {
|
|
707
|
+
selected = modelList[num - 1]
|
|
682
708
|
} else if (answer.trim()) {
|
|
683
709
|
selected = answer.trim()
|
|
684
710
|
}
|
|
@@ -851,6 +877,13 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
851
877
|
default:
|
|
852
878
|
console.log(`Unknown command: /${cmd}. Type /help for available commands.`)
|
|
853
879
|
}
|
|
880
|
+
} finally {
|
|
881
|
+
// 恢复 console.log 并把命令输出回发到 Telegram
|
|
882
|
+
if (isTgCmd) {
|
|
883
|
+
console.log = origLog
|
|
884
|
+
if (tgOut.trim()) await sendTelegram(tgOut.trim(), tgChatId || null)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
854
887
|
showPrompt()
|
|
855
888
|
return
|
|
856
889
|
}
|
|
@@ -933,17 +966,18 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
933
966
|
}
|
|
934
967
|
|
|
935
968
|
// ============================================================
|
|
936
|
-
//
|
|
937
|
-
// 把引擎 onDelta
|
|
969
|
+
// 流式推送 → Telegram
|
|
970
|
+
// 把引擎 onDelta 产出的生成内容(text / reasoning)实时推送到 Telegram,
|
|
938
971
|
// 让远程用户看到 AI 正在工作,而不是"以为没回应"。
|
|
939
972
|
// 采用节流 + 编辑同一消息的方式,避免刷屏和触发速率限制。
|
|
940
973
|
// ============================================================
|
|
941
|
-
const tgThinking = { buffer: '', timer: null, target: null, lastMsgId: null }
|
|
974
|
+
const tgThinking = { buffer: '', timer: null, target: null, lastMsgId: null, flushing: false }
|
|
942
975
|
|
|
943
976
|
function tgThinkingStart(target) {
|
|
944
977
|
tgThinking.target = target || null
|
|
945
978
|
tgThinking.buffer = ''
|
|
946
979
|
tgThinking.lastMsgId = null
|
|
980
|
+
tgThinking.flushing = false
|
|
947
981
|
// 先发送 typing 动作,让 Telegram 立即显示"正在输入"
|
|
948
982
|
if (tgListener?.bot && tgThinking.target) {
|
|
949
983
|
tgListener.bot.sendChatAction(tgThinking.target, 'typing').catch(() => {})
|
|
@@ -955,14 +989,17 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
955
989
|
tgThinking.buffer += text
|
|
956
990
|
// 只保留最近 8000 字符,避免无限增长
|
|
957
991
|
if (tgThinking.buffer.length > 8000) tgThinking.buffer = tgThinking.buffer.slice(-8000)
|
|
958
|
-
// 节流:2 秒内最多刷新一次
|
|
959
992
|
if (tgThinking.timer) clearTimeout(tgThinking.timer)
|
|
960
|
-
|
|
993
|
+
// 首帧快速发送(500ms 聚合首段),之后节流编辑(1.5s)
|
|
994
|
+
const delay = tgThinking.lastMsgId ? 1500 : 500
|
|
995
|
+
tgThinking.timer = setTimeout(() => tgThinkingFlush(), delay)
|
|
961
996
|
}
|
|
962
997
|
|
|
963
998
|
async function tgThinkingFlush() {
|
|
999
|
+
if (tgThinking.flushing) return // 防止并发
|
|
964
1000
|
if (tgThinking.timer) { clearTimeout(tgThinking.timer); tgThinking.timer = null }
|
|
965
1001
|
if (!tgListener?.bot || !tgThinking.target || !tgThinking.buffer) return
|
|
1002
|
+
tgThinking.flushing = true
|
|
966
1003
|
const target = tgThinking.target
|
|
967
1004
|
const body = `🧠 思考中…\n\n${tgThinking.buffer.slice(-3500)}`
|
|
968
1005
|
try {
|
|
@@ -979,6 +1016,8 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
979
1016
|
const res = await tgListener.bot.sendMessage(target, body, { parseMode: 'HTML' })
|
|
980
1017
|
tgThinking.lastMsgId = res?.message_id || null
|
|
981
1018
|
} catch {}
|
|
1019
|
+
} finally {
|
|
1020
|
+
tgThinking.flushing = false
|
|
982
1021
|
}
|
|
983
1022
|
}
|
|
984
1023
|
|
|
@@ -987,6 +1026,7 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
987
1026
|
tgThinking.buffer = ''
|
|
988
1027
|
tgThinking.lastMsgId = null
|
|
989
1028
|
tgThinking.target = null
|
|
1029
|
+
tgThinking.flushing = false
|
|
990
1030
|
}
|
|
991
1031
|
|
|
992
1032
|
// REPL 主循环 — 由 readline 原生处理回显、退格、行回绕与 Enter 提交
|
|
@@ -1061,10 +1101,10 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
1061
1101
|
tgReplyTarget = msg.replyTo || null
|
|
1062
1102
|
const text = msg.text || msg.callbackData || ''
|
|
1063
1103
|
if (!text) return
|
|
1064
|
-
//
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1104
|
+
// 普通消息和 / 命令都转给引擎(/命令由 processInputLine 处理)
|
|
1105
|
+
// 注意:tg-listener 已内部处理 /ping /status /run 等自己的命令,
|
|
1106
|
+
// 只有未识别的 / 命令(如 /models /stop /resume)才会到达这里。
|
|
1107
|
+
await processInputLine(text, 'telegram', msg.chatId)
|
|
1068
1108
|
}).catch(e => console.error(`[TG] listener error: ${e.message}`))
|
|
1069
1109
|
console.log(`✅ Telegram channel ready (bot ${tgToken.slice(0, 12)}...)`)
|
|
1070
1110
|
} catch (e) {
|
|
@@ -1087,9 +1127,9 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
1087
1127
|
// 记录全局回复目标,供权限确认 / AI 回复镜像发送到 Telegram 使用
|
|
1088
1128
|
if (msg.chatId) tgChatId = msg.chatId
|
|
1089
1129
|
if (msg.replyTo) tgReplyTarget = msg.replyTo
|
|
1090
|
-
// 来自外部的消息 → 转发到 REPL
|
|
1130
|
+
// 来自外部的消息 → 转发到 REPL 引擎(含 / 命令,由 processInputLine 处理)
|
|
1091
1131
|
const text = msg.text || ''
|
|
1092
|
-
if (text
|
|
1132
|
+
if (text) {
|
|
1093
1133
|
await processInputLine(text, msg.channel || 'external', msg.chatId)
|
|
1094
1134
|
}
|
|
1095
1135
|
},
|
|
@@ -1101,20 +1141,17 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
|
|
|
1101
1141
|
}
|
|
1102
1142
|
|
|
1103
1143
|
// ============================================================
|
|
1104
|
-
// 引擎流式回调 — 保持 CLI
|
|
1144
|
+
// 引擎流式回调 — 保持 CLI 实时输出,并把生成内容推送到 Telegram
|
|
1105
1145
|
// onDelta 会在流式生成时被调用({type:'text'|'reasoning', text})。
|
|
1146
|
+
// 注意:DeepSeek 模型 thinking 被禁用时只有 text 事件、没有 reasoning,
|
|
1147
|
+
// 因此 text 也必须推送到 Telegram,否则远程看不到任何进度。
|
|
1106
1148
|
// 设置 onDelta 后引擎不再直接写终端,因此这里需手动维持终端输出。
|
|
1107
1149
|
// ============================================================
|
|
1108
1150
|
engine.config.onDelta = ({ type, text }) => {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
// CLI 也显示思维链
|
|
1114
|
-
process.stdout.write(text)
|
|
1115
|
-
// 实时推送到 Telegram(节流)
|
|
1116
|
-
tgThinkingPush(text)
|
|
1117
|
-
}
|
|
1151
|
+
// 保持 CLI 实时输出
|
|
1152
|
+
process.stdout.write(text)
|
|
1153
|
+
// 无论 text 还是 reasoning,都实时推送到 Telegram(节流)
|
|
1154
|
+
tgThinkingPush(text)
|
|
1118
1155
|
}
|
|
1119
1156
|
|
|
1120
1157
|
console.log(buildBanner({ model, permissionMode, session, maxTokens: tokenBudget.maxTokens }))
|