@raolin2025/claude-code-node 2.2.9 → 2.3.1

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.2.9",
3
+ "version": "2.3.1",
4
4
  "description": "Node.js AI Code Agent CLI - Zero dependencies, pure JavaScript, security hardened, multi-channel notifications",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/core/cli.js CHANGED
@@ -232,6 +232,7 @@ Commands:
232
232
  /allow all — automatically allow all subsequent tools
233
233
  /allow reset — reset to ask mode
234
234
  /allow <tool> — allow specific tool (e.g. Bash)
235
+ /resume <session-id> — Resume a saved conversation
235
236
  /exit — Exit (also Ctrl+C)
236
237
  /quit — Same as /exit
237
238
 
@@ -249,9 +250,11 @@ const DETAILED_HELP = {
249
250
 
250
251
  session: "/session\n Show current session information:\n - Session ID\n - Session title\n - Number of messages\n - Number of tool call turns",
251
252
 
252
- sessions:"/sessions\n List all saved sessions.\n Shows session ID, title, message count, and last update time.",
253
+ sessions:"/sessions\n List all saved sessions.\n Shows session ID, title, message count, and last update time.\n\n Use /resume <session-id> to restore a saved conversation.",
253
254
 
254
- clear: "/clear\n Clear the current conversation context.\n Starts a fresh session. Previous messages are not sent to the API anymore.\n\n Note: Does not delete saved sessions.",
255
+ resume: "/resume <session-id>\n Restore a saved conversation by session ID.\n Session ID can be the full ID or the numeric index from /sessions list.\n\n Example: /resume session-1779605332906-52da0706986efa67\n Example: /resume 1 (resume the first session in the list)",
256
+
257
+ clear: "/clear\n Clear the current conversation context.\n Starts a fresh session. Previous messages are not sent to the API anymore.\n\n Note: Does not delete saved sessions.",
255
258
 
256
259
  config: "/config [key]\n Without key: show the entire config as JSON.\n With a key path: show the value for that specific path.\n\n Example: /config\n Example: /config model",
257
260
 
@@ -548,6 +551,49 @@ export async function main() {
548
551
  console.log(`Messages: ${session.messages?.length || 0}`)
549
552
  console.log(`Turns: ${engine.state.turnCount}`)
550
553
  break
554
+ case 'resume': {
555
+ if (rest.length === 0) {
556
+ console.log('Usage: /resume <session-id> (from /sessions list)')
557
+ break
558
+ }
559
+ const target = rest.join(' ')
560
+ let sessionId = target
561
+ // 支持数字序号选择(从 /sessions 列表中)
562
+ if (/^\d+$/.test(target)) {
563
+ const idx = parseInt(target, 10) - 1
564
+ const list = await sessionManager.list()
565
+ if (idx < 0 || idx >= list.length) {
566
+ console.log(`❌ Session index out of range (1-${list.length})`)
567
+ break
568
+ }
569
+ sessionId = list[idx].id
570
+ }
571
+ const loaded = await sessionManager.load(sessionId)
572
+ if (!loaded) {
573
+ console.log(`❌ Session not found: ${sessionId}`)
574
+ break
575
+ }
576
+ // 恢复会话内容
577
+ session.id = loaded.id
578
+ session.title = loaded.title
579
+ session.messages = loaded.messages
580
+ session.state = loaded.state || {}
581
+ // 恢复引擎消息历史
582
+ engine.state.messages = loaded.messages.map(m => ({
583
+ role: m.role,
584
+ content: m.content,
585
+ toolCalls: m.toolCalls,
586
+ toolCallId: m.toolCallId,
587
+ }))
588
+ // 恢复计数和预算
589
+ engine.state.turnCount = loaded.state?.turnCount || 0
590
+ if (engine.tokenBudget && loaded.state?.budgetUsed != null) {
591
+ engine.tokenBudget.used = loaded.state.budgetUsed
592
+ }
593
+ console.log(`✅ Resumed session: ${loaded.title} (${loaded.id})`)
594
+ console.log(`\n💡 Tip: Use /sessions to list, /resume <id> to switch.`)
595
+ break
596
+ }
551
597
  case 'sessions': {
552
598
  const sessions = await sessionManager.list()
553
599
  if (sessions.length === 0) console.log('No sessions found')
@@ -76,6 +76,7 @@ export class EnhancedPermissionChecker {
76
76
  this.auditLog = [] // 审计日志
77
77
  this.cwd = options.cwd || process.cwd()
78
78
  this.projectDir = options.projectDir || process.cwd()
79
+ this.sessionAllowAll = false // 新增:会话剩余时间全部自动允许
79
80
  this._maxAuditEntries = 1000
80
81
  }
81
82
 
@@ -111,6 +112,26 @@ export class EnhancedPermissionChecker {
111
112
  })
112
113
  }
113
114
 
115
+ // 会话剩余时间全部工具自动允许
116
+ allowAllForSession() {
117
+ this.sessionAllowAll = true
118
+ }
119
+
120
+ // 重置会话允许状态
121
+ resetSessionAllow() {
122
+ this.sessionAllowAll = false
123
+ }
124
+
125
+ // 允许指定工具(会话级规则)
126
+ allow(tool, pattern = '*') {
127
+ return this.addRule({
128
+ tool,
129
+ pattern,
130
+ decision: PermissionDecision.ALLOW,
131
+ reason: '用户手动允许',
132
+ })
133
+ }
134
+
114
135
  /**
115
136
  * 综合权限检查
116
137
  * @param {string} toolName — 工具名
@@ -118,6 +139,16 @@ export class EnhancedPermissionChecker {
118
139
  * @returns {Promise<{allowed: boolean, reason?: string, requiresConfirmation?: boolean, securityCheck?: object}>}
119
140
  */
120
141
  async check(toolName, input = {}) {
142
+ // 1. 会话全局允许功能 (skip ask)
143
+ if (this.sessionAllowAll) {
144
+ const securityResult = await this._securityCheck(toolName, input)
145
+ if (!securityResult.safe) {
146
+ this._log(toolName, input, false, securityResult.reason)
147
+ return { allowed: false, reason: securityResult.reason, securityCheck: securityResult }
148
+ }
149
+ this._log(toolName, input, true, '会话全局自动允许')
150
+ return { allowed: true }
151
+ }
121
152
  // 1. 模式级检查
122
153
  if (this.mode === 'deny') {
123
154
  this._log(toolName, input, false, '全局拒绝模式')