@raolin2025/claude-code-node 2.3.0 → 2.3.2
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 +39 -26
- package/src/security/enhanced-permission.js +31 -0
package/package.json
CHANGED
package/src/core/cli.js
CHANGED
|
@@ -136,8 +136,8 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
136
136
|
const top = `╭${leftLabel}${'─'.repeat(inner - leftLabel.length)}╮`
|
|
137
137
|
const bottom = `╰${'─'.repeat(inner)}╯`
|
|
138
138
|
|
|
139
|
-
const col1 =
|
|
140
|
-
const col2 =
|
|
139
|
+
const col1 = 28 // 左侧机器人列宽度
|
|
140
|
+
const col2 = 44 // 标题列
|
|
141
141
|
const col3 = inner - col1 - col2 - 2 // 信息列
|
|
142
142
|
|
|
143
143
|
// ANSI 颜色
|
|
@@ -145,14 +145,18 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
145
145
|
const CYAN = '\x1b[36m'
|
|
146
146
|
const RESET = '\x1b[0m'
|
|
147
147
|
|
|
148
|
-
//
|
|
148
|
+
// 像素风格 CC 机器人(19x13 字符,带颜色)
|
|
149
149
|
const robotRaw = [
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'│
|
|
153
|
-
'│
|
|
154
|
-
'│
|
|
155
|
-
'
|
|
150
|
+
' ┌─────────────────┐ ',
|
|
151
|
+
' │ ██ ██ │ ',
|
|
152
|
+
' │ ██████ │ ',
|
|
153
|
+
' │ ██ ██ │ ',
|
|
154
|
+
' │ │ ',
|
|
155
|
+
' │ ███████ │ ',
|
|
156
|
+
' │ ██ ██ │ ',
|
|
157
|
+
' │ ██████ │ ',
|
|
158
|
+
' │ ██ ██ ██ │ ',
|
|
159
|
+
' └─────────────────┘ ',
|
|
156
160
|
]
|
|
157
161
|
|
|
158
162
|
// 颜色化:边框蓝,眼睛/嘴巴青
|
|
@@ -163,41 +167,50 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
163
167
|
|
|
164
168
|
const robotColored = robotRaw.map(colorize)
|
|
165
169
|
|
|
166
|
-
// 在 col1
|
|
167
|
-
const
|
|
168
|
-
const
|
|
170
|
+
// 在 col1 内居中(按可见长度约 19 计算)
|
|
171
|
+
const visibleWidth = 19
|
|
172
|
+
const totalCol1 = 28
|
|
173
|
+
const leftPad = Math.floor((totalCol1 - visibleWidth) / 2)
|
|
174
|
+
const rightPad = totalCol1 - visibleWidth - leftPad
|
|
169
175
|
const robotLines = robotColored.map(line => ' '.repeat(leftPad) + line + ' '.repeat(rightPad))
|
|
170
176
|
|
|
171
|
-
//
|
|
172
|
-
const pad = (s, w, align = '
|
|
173
|
-
if (s.length
|
|
174
|
-
const
|
|
177
|
+
// 辅助函数:字符串填充
|
|
178
|
+
const pad = (s, w, align = 'left') => {
|
|
179
|
+
if (s.length > w) return s.substring(0, w)
|
|
180
|
+
const spaces = w - s.length
|
|
175
181
|
if (align === 'center') {
|
|
176
|
-
const l = Math.floor(
|
|
177
|
-
return ' '.repeat(l) + s + ' '.repeat(
|
|
182
|
+
const l = Math.floor(spaces / 2)
|
|
183
|
+
return ' '.repeat(l) + s + ' '.repeat(spaces - l)
|
|
178
184
|
}
|
|
179
|
-
return s + ' '.repeat(
|
|
185
|
+
return s + ' '.repeat(spaces)
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
|
|
188
|
+
// 标题列(居中,行数匹配 robotLines)
|
|
189
|
+
const baseTitleLines = [
|
|
183
190
|
pad('AI Code Agent', col2, 'center'),
|
|
184
191
|
pad('Node.js Edition', col2, 'center'),
|
|
185
192
|
pad('', col2, 'center'),
|
|
186
193
|
pad('─'.repeat(col2 - 2), col2, 'center'),
|
|
187
|
-
pad('/help — commands · /exit — quit', col2, 'center')
|
|
188
|
-
pad('', col2, 'center')
|
|
194
|
+
pad('/help — commands · /exit — quit', col2, 'center')
|
|
189
195
|
]
|
|
196
|
+
const titleLines = []
|
|
197
|
+
for (let i = 0; i < robotLines.length; i++) {
|
|
198
|
+
titleLines.push(i < baseTitleLines.length ? baseTitleLines[i] : pad('', col2, 'center'))
|
|
199
|
+
}
|
|
190
200
|
|
|
191
|
-
//
|
|
201
|
+
// 信息列(右对齐,填充到 robotLines 长度)
|
|
192
202
|
const sessionId = session?.id || '??????'
|
|
193
|
-
const
|
|
203
|
+
const baseInfoLines = [
|
|
194
204
|
pad(`Turns: ${session?.state?.turnCount ?? 0} • Tools: 0`, col3, 'right'),
|
|
195
205
|
pad(`Model: ${model}`, col3, 'right'),
|
|
196
206
|
pad(`Permission: ${permissionMode}`, col3, 'right'),
|
|
197
207
|
pad(`Budget: 0 / ${maxTokens ?? 200000}`, col3, 'right'),
|
|
198
|
-
pad(`Session: ${sessionId?.toString().slice(-6)}`, col3, 'right')
|
|
199
|
-
pad('', col3, 'right')
|
|
208
|
+
pad(`Session: ${sessionId?.toString().slice(-6)}`, col3, 'right')
|
|
200
209
|
]
|
|
210
|
+
const infoLines = []
|
|
211
|
+
for (let i = 0; i < robotLines.length; i++) {
|
|
212
|
+
infoLines.push(i < baseInfoLines.length ? baseInfoLines[i] : pad('', col3, 'right'))
|
|
213
|
+
}
|
|
201
214
|
|
|
202
215
|
// 构建每行:│ col1 │ col2 │ col3 │
|
|
203
216
|
const lines = [top]
|
|
@@ -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, '全局拒绝模式')
|