mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-19 16:13:56 +00:00
Strip ANSI escapes from bash commands across all providers
Use strip-ansi (already in dep tree via Ink) in extractBashCommands to prevent terminal escape codes from leaking into dashboard bash breakdown keys. Route goose, gemini, qwen, and openclaw through extractBashCommands instead of inline split, which also gives them multi-command extraction (matching claude/codex/droid behavior).
This commit is contained in:
parent
292265bf47
commit
341aa46f78
7 changed files with 18 additions and 12 deletions
3
package-lock.json
generated
3
package-lock.json
generated
|
|
@ -12,7 +12,8 @@
|
|||
"chalk": "^5.4.1",
|
||||
"commander": "^13.1.0",
|
||||
"ink": "^7.0.0",
|
||||
"react": "^19.2.5"
|
||||
"react": "^19.2.5",
|
||||
"strip-ansi": "^7.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"codeburn": "dist/cli.js"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@
|
|||
"chalk": "^5.4.1",
|
||||
"commander": "^13.1.0",
|
||||
"ink": "^7.0.0",
|
||||
"react": "^19.2.5"
|
||||
"react": "^19.2.5",
|
||||
"strip-ansi": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.19.17",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { basename } from 'path'
|
||||
import stripAnsi from 'strip-ansi'
|
||||
|
||||
function stripQuotedStrings(command: string): string {
|
||||
return command.replace(/"[^"]*"|'[^']*'/g, match => ' '.repeat(match.length))
|
||||
}
|
||||
|
||||
export function extractBashCommands(command: string): string[] {
|
||||
if (!command || !command.trim()) return []
|
||||
export function extractBashCommands(rawCommand: string): string[] {
|
||||
if (!rawCommand || !rawCommand.trim()) return []
|
||||
|
||||
const command = stripAnsi(rawCommand)
|
||||
const stripped = stripQuotedStrings(command)
|
||||
|
||||
const separatorRegex = /\s*(?:&&|;|\|)\s*/g
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { join } from 'path'
|
|||
import { homedir } from 'os'
|
||||
|
||||
import { calculateCost } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
||||
const toolNameMap: Record<string, string> = {
|
||||
|
|
@ -93,8 +94,7 @@ function parseSession(data: GeminiSession, seenKeys: Set<string>): ParsedProvide
|
|||
const mapped = toolNameMap[tc.displayName ?? ''] ?? toolNameMap[tc.name] ?? tc.displayName ?? tc.name
|
||||
allTools.push(mapped)
|
||||
if (mapped === 'Bash' && tc.args && typeof tc.args.command === 'string') {
|
||||
const cmd = tc.args.command.split(/\s+/)[0] ?? ''
|
||||
if (cmd) bashCommands.push(cmd)
|
||||
bashCommands.push(...extractBashCommands(tc.args.command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { join } from 'path'
|
|||
import { homedir, platform } from 'os'
|
||||
|
||||
import { calculateCost, getShortModelName } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import { isSqliteAvailable, getSqliteLoadError, openDatabase, type SqliteDatabase } from '../sqlite.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
||||
|
|
@ -109,8 +110,9 @@ function extractToolsFromMessages(db: SqliteDatabase, sessionId: string): { tool
|
|||
if (mapped === 'Bash') {
|
||||
const cmd = item.toolCall?.value?.arguments?.command
|
||||
if (typeof cmd === 'string') {
|
||||
const first = cmd.split(/\s+/)[0] ?? ''
|
||||
if (first && !bashCommands.includes(first)) bashCommands.push(first)
|
||||
for (const c of extractBashCommands(cmd)) {
|
||||
if (!bashCommands.includes(c)) bashCommands.push(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { homedir } from 'os'
|
|||
|
||||
import { readSessionFile } from '../fs-utils.js'
|
||||
import { calculateCost } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
||||
const toolNameMap: Record<string, string> = {
|
||||
|
|
@ -78,8 +79,7 @@ function extractTools(content: Array<{ type?: string; name?: string; arguments?:
|
|||
const mapped = toolNameMap[block.name] ?? block.name
|
||||
tools.push(mapped)
|
||||
if (mapped === 'Bash' && block.arguments && typeof block.arguments.command === 'string') {
|
||||
const cmd = block.arguments.command.split(/\s+/)[0] ?? ''
|
||||
if (cmd) bashCommands.push(cmd)
|
||||
bashCommands.push(...extractBashCommands(block.arguments.command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { homedir } from 'os'
|
|||
|
||||
import { readSessionFile } from '../fs-utils.js'
|
||||
import { calculateCost } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
||||
const toolNameMap: Record<string, string> = {
|
||||
|
|
@ -66,8 +67,7 @@ function extractTools(parts: QwenPart[]): { tools: string[]; bashCommands: strin
|
|||
const mapped = toolNameMap[part.functionCall.name] ?? part.functionCall.name
|
||||
tools.push(mapped)
|
||||
if (mapped === 'Bash' && part.functionCall.args && typeof part.functionCall.args['command'] === 'string') {
|
||||
const cmd = (part.functionCall.args['command'] as string).split(/\s+/)[0] ?? ''
|
||||
if (cmd) bashCommands.push(cmd)
|
||||
bashCommands.push(...extractBashCommands(part.functionCall.args['command'] as string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue