Fix: add missing getOpencodeSessionTokenUsage function to resolve redo button error
This commit is contained in:
130
chat/server.js
130
chat/server.js
@@ -7543,6 +7543,136 @@ function extractTokenUsageFromResult(result, messages, options = {}) {
|
|||||||
return estimated;
|
return estimated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOpencodeSessionTokenUsage(sessionId, cwd) {
|
||||||
|
if (!sessionId || !cwd) {
|
||||||
|
log('⚠️ getOpencodeSessionTokenUsage: Missing required parameters', { hasSessionId: !!sessionId, hasCwd: !!cwd });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cliCommand = resolveCliCommand('opencode');
|
||||||
|
const candidates = [
|
||||||
|
['session', 'info', '--id', sessionId, '--json'],
|
||||||
|
['sessions', 'info', '--id', sessionId, '--json'],
|
||||||
|
['session', 'info', sessionId, '--json'],
|
||||||
|
['session', 'usage', '--id', sessionId, '--json'],
|
||||||
|
['session', 'show', '--id', sessionId, '--json'],
|
||||||
|
];
|
||||||
|
|
||||||
|
log('🔍 getOpencodeSessionTokenUsage: Starting session token query', {
|
||||||
|
sessionId,
|
||||||
|
cwd,
|
||||||
|
cliCommand,
|
||||||
|
candidateCount: candidates.length,
|
||||||
|
candidates: candidates.map(c => c.join(' '))
|
||||||
|
});
|
||||||
|
|
||||||
|
const attemptResults = [];
|
||||||
|
|
||||||
|
for (const args of candidates) {
|
||||||
|
const cmdStr = args.join(' ');
|
||||||
|
try {
|
||||||
|
log(` → Trying: ${cliCommand} ${cmdStr}`, { sessionId });
|
||||||
|
const { stdout, stderr } = await runCommand(cliCommand, args, { timeout: 10000, cwd });
|
||||||
|
|
||||||
|
const hasStdout = stdout && stdout.trim();
|
||||||
|
const hasStderr = stderr && stderr.trim();
|
||||||
|
|
||||||
|
log(` ← Response received`, {
|
||||||
|
args: cmdStr,
|
||||||
|
hasStdout,
|
||||||
|
hasStderr,
|
||||||
|
stdoutLength: stdout?.length || 0,
|
||||||
|
stderrLength: stderr?.length || 0,
|
||||||
|
stdoutSample: stdout?.substring(0, 300),
|
||||||
|
stderrSample: stderr?.substring(0, 200)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasStdout) {
|
||||||
|
// Try JSON parsing first
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(stdout);
|
||||||
|
log(' ✓ JSON parse successful', {
|
||||||
|
args: cmdStr,
|
||||||
|
parsedKeys: Object.keys(parsed),
|
||||||
|
hasUsage: !!parsed.usage,
|
||||||
|
hasTokens: !!parsed.tokens,
|
||||||
|
hasTokensUsed: !!parsed.tokensUsed,
|
||||||
|
hasSession: !!parsed.session
|
||||||
|
});
|
||||||
|
|
||||||
|
const extracted = extractTokenUsage(parsed) || extractTokenUsage(parsed.session) || null;
|
||||||
|
const tokens = extracted?.tokens || 0;
|
||||||
|
|
||||||
|
if (typeof tokens === 'number' && tokens > 0) {
|
||||||
|
log('✅ getOpencodeSessionTokenUsage: Successfully extracted tokens from JSON', {
|
||||||
|
sessionId,
|
||||||
|
tokens,
|
||||||
|
command: cmdStr,
|
||||||
|
extractionPath: extracted?.source || 'unknown'
|
||||||
|
});
|
||||||
|
attemptResults.push({ command: cmdStr, success: true, tokens, source: 'json' });
|
||||||
|
return tokens;
|
||||||
|
} else {
|
||||||
|
const reason = typeof tokens !== 'number' ? `tokens is ${typeof tokens}, not number` : 'tokens is 0 or negative';
|
||||||
|
log(' ✗ JSON parsed but no valid token count', { args: cmdStr, tokens, reason });
|
||||||
|
attemptResults.push({ command: cmdStr, success: false, reason, parsedTokens: tokens, source: 'json' });
|
||||||
|
}
|
||||||
|
} catch (jsonErr) {
|
||||||
|
log(' ✗ JSON parse failed, trying text parse', {
|
||||||
|
args: cmdStr,
|
||||||
|
error: jsonErr.message,
|
||||||
|
stdoutSample: stdout.substring(0, 200)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to parse token count from text output
|
||||||
|
const tokenMatch = stdout.match(/total[_\s-]?tokens?\s*[:=]?\s*(\d+)/i) ||
|
||||||
|
stdout.match(/tokens?\s*[:=]?\s*(\d+)/i) ||
|
||||||
|
stdout.match(/token\s*count\s*[:=]?\s*(\d+)/i);
|
||||||
|
if (tokenMatch) {
|
||||||
|
const tokens = parseInt(tokenMatch[1], 10);
|
||||||
|
if (tokens > 0) {
|
||||||
|
log('✅ getOpencodeSessionTokenUsage: Successfully extracted tokens from text', {
|
||||||
|
sessionId,
|
||||||
|
tokens,
|
||||||
|
command: cmdStr,
|
||||||
|
pattern: tokenMatch[0]
|
||||||
|
});
|
||||||
|
attemptResults.push({ command: cmdStr, success: true, tokens, source: 'text', pattern: tokenMatch[0] });
|
||||||
|
return tokens;
|
||||||
|
} else {
|
||||||
|
log(' ✗ Text pattern matched but tokens <= 0', { args: cmdStr, tokens, pattern: tokenMatch[0] });
|
||||||
|
attemptResults.push({ command: cmdStr, success: false, reason: 'matched text pattern but tokens <= 0', parsedTokens: tokens, source: 'text' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log(' ✗ No text patterns matched', { args: cmdStr, stdoutSample: stdout.substring(0, 200) });
|
||||||
|
attemptResults.push({ command: cmdStr, success: false, reason: 'no text patterns matched', source: 'text' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const reason = !stdout ? 'no stdout' : 'stdout is empty';
|
||||||
|
log(' ✗ No stdout to parse', { args: cmdStr, reason, hasStderr });
|
||||||
|
attemptResults.push({ command: cmdStr, success: false, reason, stderr: stderr?.substring(0, 200) });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const errorDetails = {
|
||||||
|
message: err.message,
|
||||||
|
stderr: err.stderr?.substring(0, 200),
|
||||||
|
stdout: err.stdout?.substring(0, 200),
|
||||||
|
code: err.code
|
||||||
|
};
|
||||||
|
log(' ✗ Command execution failed', { args: cmdStr, error: errorDetails });
|
||||||
|
attemptResults.push({ command: cmdStr, success: false, error: errorDetails });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log('❌ getOpencodeSessionTokenUsage: All commands failed', {
|
||||||
|
sessionId,
|
||||||
|
totalAttempts: attemptResults.length,
|
||||||
|
attemptResults
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
function getProviderUsageSnapshot(providerList = null) {
|
function getProviderUsageSnapshot(providerList = null) {
|
||||||
const providers = (providerList && providerList.length)
|
const providers = (providerList && providerList.length)
|
||||||
? providerList.map((p) => normalizeProviderName(p))
|
? providerList.map((p) => normalizeProviderName(p))
|
||||||
|
|||||||
Reference in New Issue
Block a user