From 0aea1736baee2ec76e2718a6e01caed8a1069ba3 Mon Sep 17 00:00:00 2001 From: ghreprimand <203024559+ghreprimand@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:12:35 -0500 Subject: [PATCH] Add Cookbook crash report copy action --- static/js/cookbookRunning.js | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/static/js/cookbookRunning.js b/static/js/cookbookRunning.js index 3f8e591..90ca5c6 100644 --- a/static/js/cookbookRunning.js +++ b/static/js/cookbookRunning.js @@ -33,6 +33,74 @@ function _taskBadge(task) { return { text: _statusLabel(task.status, task.type), cls: 'cookbook-task-' + task.status }; } +function _shouldOfferCrashReport(task) { + if (!task) return false; + if (task._unreachable && task.type === 'serve') return true; + return ['error', 'crashed', 'failed'].includes(task.status); +} + +function _redactCrashReportText(text) { + if (!text) return ''; + return String(text) + .replace(/\b(Bearer\s+)[A-Za-z0-9._~+/=-]{12,}/gi, '$1[redacted]') + .replace(/\b(hf_[A-Za-z0-9]{16,})\b/g, '[redacted-hf-token]') + .replace(/\b(sk-[A-Za-z0-9_-]{16,})\b/g, '[redacted-api-key]') + .replace(/\b(xox[baprs]-[A-Za-z0-9-]{16,})\b/g, '[redacted-slack-token]') + .replace(/\b(AIza[0-9A-Za-z_-]{20,})\b/g, '[redacted-google-key]') + .replace(/\b((?:HF_TOKEN|HUGGING_FACE_HUB_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY|BRAVE_API_KEY|TAVILY_API_KEY|SERPER_API_KEY|GOOGLE_API_KEY|API_KEY|TOKEN|PASSWORD)\s*=\s*)(['"]?)[^\s'"\\]+/gi, '$1$2[redacted]') + .replace(/\b(--(?:api-key|token|hf-token|password)\s+)([^\s]+)/gi, '$1[redacted]'); +} + +function _lastLines(text, count = 160) { + const clean = _redactCrashReportText(text || '').trimEnd(); + if (!clean) return '(no captured output)'; + return clean.split('\n').slice(-count).join('\n'); +} + +function _codeFence(text) { + return String(text || '').replace(/```/g, '` ` `'); +} + +function _taskHostLabel(task) { + if (!task?.remoteHost) return 'local'; + return task.remoteHost + (task.sshPort ? `:${task.sshPort}` : ''); +} + +function _taskPort(task) { + const cmd = task?.payload?._cmd || ''; + const match = cmd.match(/--port\s+(\d+)/); + return match ? match[1] : ''; +} + +function _buildCrashReport(task, outputText) { + const capturedOutput = outputText || task?.output || ''; + const cmd = _redactCrashReportText(task?.payload?._cmd || ''); + const diag = _diagnose(capturedOutput); + const started = task?.ts ? new Date(task.ts).toISOString() : ''; + const report = [ + '## Odysseus Cookbook crash report', + '', + 'Please review this report for secrets before posting it publicly.', + '', + '### Task', + `- ID: \`${task?.sessionId || task?.id || 'unknown'}\``, + `- Type: \`${task?.type || 'unknown'}\``, + `- Status: \`${task?._unreachable ? 'unreachable' : (task?.status || 'unknown')}\``, + `- Model/repo: \`${task?.payload?.repo_id || task?.name || 'unknown'}\``, + `- Host: \`${_taskHostLabel(task)}\``, + ]; + if (task?.platform) report.push(`- Platform: \`${task.platform}\``); + if (started) report.push(`- Started: \`${started}\``); + const port = _taskPort(task); + if (port) report.push(`- Port: \`${port}\``); + if (diag?.message) report.push(`- Diagnosis: ${diag.message}`); + if (cmd) { + report.push('', '### Command', '```bash', _codeFence(cmd), '```'); + } + report.push('', '### Last captured output', '```text', _codeFence(_lastLines(capturedOutput)), '```'); + return report.join('\n'); +} + // Shared state/functions injected by init() let _envState; let _sshCmd; @@ -1660,6 +1728,13 @@ export function _renderRunningTab() { _copyText(tmuxAttach); }}); } + if (_shouldOfferCrashReport(task)) { + items.push({ label: 'Copy crash report', action: 'copy-crash-report', custom: () => { + const out = (el.querySelector('.cookbook-output-pre')?.textContent || task.output || ''); + _copyText(_buildCrashReport(task, out)); + uiModule.showToast('Copied crash report'); + }}); + } // Copy the last 50 lines of the task's output/log. items.push({ label: 'Copy last 50 lines', action: 'copy-log', custom: () => { const out = (el.querySelector('.cookbook-output-pre')?.textContent || task.output || ''); @@ -1683,6 +1758,7 @@ export function _renderRunningTab() { 'register-endpoint': '', save: '', 'copy-tmux': '', + 'copy-crash-report': '', 'copy-log': '', kill: '', cancel: '',