diff --git a/Plugin/unraid/include/ai.php b/Plugin/unraid/include/ai.php index 8ff7e13..c01b692 100644 --- a/Plugin/unraid/include/ai.php +++ b/Plugin/unraid/include/ai.php @@ -824,10 +824,23 @@ function vv_ai_bug_write(string $component, string $summary, string $evidence, a if ($component === '' || $summary === '' || $evidence === '') return ['ok' => false]; if (!vv_ai_scope_ok($component)) return ['ok' => false]; + // The commit as it was when the fault was seen, not when the report is read. "Is this already + // fixed" is the first question any maintainer asks, and a version captured later answers a + // different question. Slot id (host1/host2) rather than the hostname — this text is written + // to be pasted into a public issue. + $commit = ''; + $head = @file_get_contents(SCRIPTS_DIR . '/.git/HEAD'); + if (is_string($head) && preg_match('#^ref:\s*(\S+)#', trim($head), $hm)) { + $commit = substr(trim((string)@file_get_contents(SCRIPTS_DIR . '/.git/' . $hm[1])), 0, 12); + } elseif (is_string($head)) { + $commit = substr(trim($head), 0, 12); + } + $rec = [ 'component' => mb_substr($component, 0, 120), 'summary' => mb_substr($summary, 0, 300), 'evidence' => mb_substr($evidence, 0, 2000), + 'commit' => $commit, 'host' => vv_detect_host(), 'first' => time(), 'last' => time(), diff --git a/Plugin/unraid/pages/ai.php b/Plugin/unraid/pages/ai.php index acd55fa..7dd2a1d 100644 --- a/Plugin/unraid/pages/ai.php +++ b/Plugin/unraid/pages/ai.php @@ -278,6 +278,9 @@ if (is_dir('/var/log/varaverk')) { filed from the Scheduler troubleshooter
+ + @@ -536,6 +539,7 @@ if (is_dir('/var/log/varaverk')) { function renderBugs(bugs) { const wrap = $('vv-ai-bugs-wrap'); + vvBugs = bugs; if (!bugs.length) { wrap.style.display = 'none'; return; } wrap.style.display = ''; $('vv-ai-bugs-sum').innerHTML = @@ -548,6 +552,7 @@ if (is_dir('/var/log/varaverk')) {
${esc(b.component)} ${esc(b.id)}${esc(seen)} · ${esc(when)} +
${esc(b.summary)}
@@ -558,6 +563,48 @@ if (is_dir('/var/log/varaverk')) { }).join(''); } + // Copy out, never transmit. The operator reads the text before it goes anywhere, which is the + // whole reason this is a button and not a webhook — evidence is quoted log lines, and Varaverk + // logs carry share names, container names and paths. Redaction by inspection beats redaction + // by rule, and it cannot be unpublished later. + let vvBugs = []; + + function vvAiBugMarkdown(b) { + const d = t => t ? new Date(t * 1000).toISOString().slice(0, 10) : '—'; + return `### ${b.component} — ${b.summary}\n\n` + + `| | |\n|---|---|\n` + + `| Component | \`${b.component}\` |\n` + + `| Varaverk | \`${b.commit || 'unknown'}\` |\n` + + `| Host slot | ${b.host || '—'} |\n` + + `| Seen | ${b.seen}× · ${d(b.first)} → ${d(b.last)} |\n\n` + + `**Evidence**\n\n\`\`\`\n${b.evidence}\n\`\`\`\n\n` + + (b.context && b.context.asked ? `**Asked while diagnosing:** ${b.context.asked}\n\n` : '') + + (b.context && b.context.log ? `**Log:** \`${b.context.log}\`\n\n` : '') + + `_Filed automatically by the Varaverk assistant. Report ${b.id}._\n`; + } + + window.vvAiBugCopy = function (id) { + const b = vvBugs.find(x => x.id === id); + if (!b) return; + const text = vvAiBugMarkdown(b); + const done = ok => { + const btn = event && event.target; + if (btn) { btn.textContent = ok ? 'Copied' : 'Select below'; setTimeout(() => btn.textContent = 'Copy issue', 1800); } + if (!ok) { + // The clipboard API needs a secure context and the WebGUI is often plain http on the + // LAN. Falling back to a selectable box means the button always does something useful + // rather than failing silently on exactly the setups this is built for. + const box = document.getElementById('vv-ai-bug-copybox'); + box.style.display = ''; box.value = text; box.focus(); box.select(); + } + }; + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then(() => done(true)).catch(() => done(false)); + } else { + done(false); + } + }; + window.vvAiBugClose = function (id) { fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },