@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raolin2025/claude-code-node",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
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
@@ -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 = 30 // 机器人列
140
- const col2 = 42 // 标题列
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
- // 原始机器人(宽21)
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 内居中(考虑 ESC 序列不看长度,按可见长度21 计算)
167
- const leftPad = 4 // (30 - 21) / 2 = 4.5 => 4 left, 5 right
168
- const rightPad = 5
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 = 'center') => {
173
- if (s.length >= w) return s
174
- const sp = w - s.length
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(sp / 2)
177
- return ' '.repeat(l) + s + ' '.repeat(sp - l)
182
+ const l = Math.floor(spaces / 2)
183
+ return ' '.repeat(l) + s + ' '.repeat(spaces - l)
178
184
  }
179
- return s + ' '.repeat(sp)
185
+ return s + ' '.repeat(spaces)
180
186
  }
181
187
 
182
- const titleLines = [
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 infoLines = [
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, '全局拒绝模式')