@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 +1 -1
- package/src/core/cli.js +48 -2
- package/src/security/enhanced-permission.js +31 -0
package/package.json
CHANGED
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
|
-
|
|
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, '全局拒绝模式')
|