@raolin2025/claude-code-node 2.6.4 → 2.6.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raolin2025/claude-code-node",
3
- "version": "2.6.4",
3
+ "version": "2.6.6",
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",
@@ -273,6 +273,8 @@ export class TelegramListener {
273
273
  this._pollTimer = null
274
274
  this._retryDelay = 1000
275
275
  this.maxRetryDelay = 30000
276
+ this._lastPollError = null // 最近一次轮询错误消息(用于去抖刷屏)
277
+ this._lastPollErrorAt = 0 // 最近一次轮询错误时间戳
276
278
  this.conversations = new ConversationState()
277
279
  this._onMessage = null
278
280
  this._handlers = {}
@@ -323,6 +325,9 @@ export class TelegramListener {
323
325
  const res = await this._fetch(url, {
324
326
  method: 'POST',
325
327
  headers: { 'Content-Type': 'application/json' },
328
+ // 长轮询本身最多等 30s(Telegram API 参数),HTTP 层超时必须更长,
329
+ // 否则代理路径(fetchViaSocks5)30s 硬超时会误杀长轮询 → HTTP request timeout 刷屏。
330
+ timeout: 60000,
326
331
  body: JSON.stringify({
327
332
  offset: this.lastUpdateId + 1,
328
333
  timeout: 30,
@@ -360,7 +365,17 @@ export class TelegramListener {
360
365
  this._retryDelay = 1000
361
366
 
362
367
  } catch (e) {
363
- log(`[TG] Poll error: ${e.message} (retry in ${this._retryDelay}ms)`)
368
+ // 错误去抖:连续相同的错误(如长轮询超时)只在首次/状态变化时打印,
369
+ // 避免 AI 处理长任务时 getUpdates 空转超时不断刷屏。
370
+ const msg = e.message || 'unknown'
371
+ const now = Date.now()
372
+ const isSame = msg === this._lastPollError
373
+ const isRecent = (now - (this._lastPollErrorAt || 0)) < 60000
374
+ if (!isSame || !isRecent) {
375
+ log(`[TG] Poll error: ${msg} (retry in ${this._retryDelay}ms)`)
376
+ this._lastPollError = msg
377
+ this._lastPollErrorAt = now
378
+ }
364
379
  await this._sleep(this._retryDelay)
365
380
  this._retryDelay = Math.min(this._retryDelay * 2, this.maxRetryDelay)
366
381
  }
@@ -271,10 +271,13 @@ export async function fetchViaSocks5(url, options = {}, proxyAddr) {
271
271
 
272
272
  return new Promise((resolve, reject) => {
273
273
  let responseData = ''
274
+ // 超时可配置(options.timeout,毫秒),默认 30s。
275
+ // Telegram getUpdates 是长轮询(最多等 30s),必须传更大的超时避免误判超时。
276
+ const timeoutMs = options.timeout || 30000
274
277
  const timeout = setTimeout(() => {
275
278
  socket.destroy()
276
279
  reject(new Error('HTTP request timeout'))
277
- }, 30000)
280
+ }, timeoutMs)
278
281
 
279
282
  socket.write(reqBuf)
280
283
  socket.on('data', (chunk) => {
package/src/core/cli.js CHANGED
@@ -592,6 +592,10 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
592
592
  let tgListener = null
593
593
  let tgChatId = null
594
594
  let tgReplyTarget = null
595
+ // 当前正在处理的请求来源('cli' | 'telegram')— 用于权限确认等按来源分支的逻辑
596
+ let currentSource = 'cli'
597
+ // 等待中的 Telegram 权限确认(resolve 回调)— 远程用户回复 y/n/a 时响应
598
+ let pendingConfirm = null
595
599
 
596
600
  // 处理输入行
597
601
  // source: 'cli' 来自终端输入, 'telegram' 来自 Telegram
@@ -599,6 +603,32 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
599
603
  const trimmed = input.trim()
600
604
  if (!trimmed) { showPrompt(); return }
601
605
 
606
+ // 记录当前请求来源,供 onConfirmTool 等按来源分支的逻辑使用
607
+ currentSource = source
608
+
609
+ // 关键:来自 Telegram 的消息,如果当前正在等待远程权限确认(pendingConfirm),
610
+ // 则把它当作确认回复(y/n/a)处理,而不是当作新的 REPL 输入。
611
+ if (source === 'telegram' && pendingConfirm) {
612
+ const confirm = pendingConfirm
613
+ pendingConfirm = null
614
+ const a = trimmed.toLowerCase()
615
+ if (a === 'a') {
616
+ try { engine.permissionChecker.allowAllForSession() } catch {}
617
+ confirm(true)
618
+ } else if (a === 'y') {
619
+ confirm(true)
620
+ } else if (a === 'n') {
621
+ confirm(false)
622
+ } else {
623
+ // 不是 y/n/a,忽略这次(不打断确认等待),但也可能是误发,继续等待
624
+ pendingConfirm = confirm
625
+ if (tgListener?.bot) {
626
+ await sendTelegram('⚠️ 请回复 y(允许一次)/ n(拒绝)/ a(本会话全部允许)', tgChatId || null).catch(() => {})
627
+ }
628
+ }
629
+ return
630
+ }
631
+
602
632
  if (trimmed.startsWith('/')) {
603
633
  const [cmd, ...rest] = trimmed.slice(1).split(' ')
604
634
  switch (cmd) {
@@ -885,6 +915,25 @@ const systemPrompt = cliArgs.systemPrompt || DEFAULT_SYSTEM_PROMPT
885
915
  if (engine.permissionChecker.sessionAllowAll) return true
886
916
 
887
917
  const snippet = JSON.stringify(input).slice(0, 120) || '(no params)'
918
+
919
+ // Telegram 远程模式:把权限确认推送到 Telegram,等待远程用户回复 y/n/a
920
+ if (currentSource === 'telegram' && tgListener?.bot) {
921
+ const promptText = `⚠️ 需要工具权限\n工具: ${toolName}\n输入: ${snippet}\n\n请回复:\ny = 允许一次\nn = 拒绝\na = 本会话全部允许`
922
+ await sendTelegram(promptText, null).catch(() => {})
923
+ return new Promise((resolve) => {
924
+ // 60 秒内未回复则自动拒绝,避免远程确认永久挂起阻塞对话
925
+ const timer = setTimeout(() => {
926
+ if (pendingConfirm === resolve) pendingConfirm = null
927
+ resolve(false)
928
+ }, 60000)
929
+ pendingConfirm = (val) => {
930
+ clearTimeout(timer)
931
+ resolve(val)
932
+ }
933
+ })
934
+ }
935
+
936
+ // 本地 CLI:用终端交互确认
888
937
  return new Promise((resolve) => {
889
938
  rl.question(`\n⚠️ Allow tool "${toolName}"?\n Input: ${snippet}\n (y/N/a) a=all session `, (answer) => {
890
939
  const a = answer.toLowerCase()