mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-17 09:41:28 +08:00
fix: replace 40+ silent catch blocks with debug logging
Every empty catch {} in sidepanel.js, sidebar-agent.ts now logs with
[gstack sidebar] or [sidebar-agent] prefix. Chat poll 401s, stop agent,
tab poll, clear chat, SSE parse, refs fetch, stream JSON parse, queue
read/parse, process kill — all now visible in console.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,8 @@ function getGitRoot(): string | null {
|
|||||||
try {
|
try {
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
||||||
} catch {
|
} catch (err: any) {
|
||||||
|
console.debug('[sidebar-agent] Not in a git repo:', err.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +75,8 @@ async function refreshToken(): Promise<string | null> {
|
|||||||
const data = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
||||||
authToken = data.token || null;
|
authToken = data.token || null;
|
||||||
return authToken;
|
return authToken;
|
||||||
} catch {
|
} catch (err: any) {
|
||||||
|
console.error('[sidebar-agent] Failed to refresh auth token:', err.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +236,10 @@ async function askClaude(queueEntry: any): Promise<void> {
|
|||||||
|
|
||||||
// Validate cwd exists — queue may reference a stale worktree
|
// Validate cwd exists — queue may reference a stale worktree
|
||||||
let effectiveCwd = cwd || process.cwd();
|
let effectiveCwd = cwd || process.cwd();
|
||||||
try { fs.accessSync(effectiveCwd); } catch { effectiveCwd = process.cwd(); }
|
try { fs.accessSync(effectiveCwd); } catch (err: any) {
|
||||||
|
console.warn('[sidebar-agent] Worktree path inaccessible, falling back to cwd:', effectiveCwd, err.message);
|
||||||
|
effectiveCwd = process.cwd();
|
||||||
|
}
|
||||||
|
|
||||||
const proc = spawn('claude', claudeArgs, {
|
const proc = spawn('claude', claudeArgs, {
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
@@ -264,7 +269,9 @@ async function askClaude(queueEntry: any): Promise<void> {
|
|||||||
buffer = lines.pop() || '';
|
buffer = lines.pop() || '';
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!line.trim()) continue;
|
if (!line.trim()) continue;
|
||||||
try { handleStreamEvent(JSON.parse(line), tid); } catch {}
|
try { handleStreamEvent(JSON.parse(line), tid); } catch (err: any) {
|
||||||
|
console.error(`[sidebar-agent] Tab ${tid}: Failed to parse stream line:`, line.slice(0, 100), err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -275,7 +282,9 @@ async function askClaude(queueEntry: any): Promise<void> {
|
|||||||
|
|
||||||
proc.on('close', (code) => {
|
proc.on('close', (code) => {
|
||||||
if (buffer.trim()) {
|
if (buffer.trim()) {
|
||||||
try { handleStreamEvent(JSON.parse(buffer), tid); } catch {}
|
try { handleStreamEvent(JSON.parse(buffer), tid); } catch (err: any) {
|
||||||
|
console.error(`[sidebar-agent] Tab ${tid}: Failed to parse final buffer:`, buffer.slice(0, 100), err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const doneEvent: Record<string, any> = { type: 'agent_done' };
|
const doneEvent: Record<string, any> = { type: 'agent_done' };
|
||||||
if (code !== 0 && stderrBuffer.trim()) {
|
if (code !== 0 && stderrBuffer.trim()) {
|
||||||
@@ -300,7 +309,9 @@ async function askClaude(queueEntry: any): Promise<void> {
|
|||||||
// Timeout (default 300s / 5 min — multi-page tasks need time)
|
// Timeout (default 300s / 5 min — multi-page tasks need time)
|
||||||
const timeoutMs = parseInt(process.env.SIDEBAR_AGENT_TIMEOUT || '300000', 10);
|
const timeoutMs = parseInt(process.env.SIDEBAR_AGENT_TIMEOUT || '300000', 10);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try { proc.kill(); } catch {}
|
try { proc.kill(); } catch (killErr: any) {
|
||||||
|
console.warn(`[sidebar-agent] Tab ${tid}: Failed to kill timed-out process:`, killErr.message);
|
||||||
|
}
|
||||||
const timeoutMsg = stderrBuffer.trim()
|
const timeoutMsg = stderrBuffer.trim()
|
||||||
? `Timed out after ${timeoutMs / 1000}s\nstderr: ${stderrBuffer.trim().slice(-500)}`
|
? `Timed out after ${timeoutMs / 1000}s\nstderr: ${stderrBuffer.trim().slice(-500)}`
|
||||||
: `Timed out after ${timeoutMs / 1000}s`;
|
: `Timed out after ${timeoutMs / 1000}s`;
|
||||||
@@ -317,14 +328,20 @@ async function askClaude(queueEntry: any): Promise<void> {
|
|||||||
function countLines(): number {
|
function countLines(): number {
|
||||||
try {
|
try {
|
||||||
return fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean).length;
|
return fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean).length;
|
||||||
} catch { return 0; }
|
} catch (err: any) {
|
||||||
|
console.error('[sidebar-agent] Failed to read queue file:', err.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readLine(n: number): string | null {
|
function readLine(n: number): string | null {
|
||||||
try {
|
try {
|
||||||
const lines = fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean);
|
const lines = fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean);
|
||||||
return lines[n - 1] || null;
|
return lines[n - 1] || null;
|
||||||
} catch { return null; }
|
} catch (err: any) {
|
||||||
|
console.error(`[sidebar-agent] Failed to read queue line ${n}:`, err.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
@@ -337,7 +354,10 @@ async function poll() {
|
|||||||
if (!line) continue;
|
if (!line) continue;
|
||||||
|
|
||||||
let entry: any;
|
let entry: any;
|
||||||
try { entry = JSON.parse(line); } catch { continue; }
|
try { entry = JSON.parse(line); } catch (err: any) {
|
||||||
|
console.warn(`[sidebar-agent] Skipping malformed queue entry at line ${lastLine}:`, line.slice(0, 80), err.message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!entry.message && !entry.prompt) continue;
|
if (!entry.message && !entry.prompt) continue;
|
||||||
|
|
||||||
const tid = entry.tabId ?? 0;
|
const tid = entry.tabId ?? 0;
|
||||||
|
|||||||
@@ -375,7 +375,10 @@ async function pollChat() {
|
|||||||
headers: authHeaders(),
|
headers: authHeaders(),
|
||||||
signal: AbortSignal.timeout(3000),
|
signal: AbortSignal.timeout(3000),
|
||||||
});
|
});
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) {
|
||||||
|
console.warn(`[gstack sidebar] Chat poll failed: ${resp.status} ${resp.statusText}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
|
|
||||||
// Detect tab switch from server — swap chat context
|
// Detect tab switch from server — swap chat context
|
||||||
@@ -417,7 +420,9 @@ async function pollChat() {
|
|||||||
|
|
||||||
// Show/hide stop button based on agent status
|
// Show/hide stop button based on agent status
|
||||||
updateStopButton(data.agentStatus === 'processing');
|
updateStopButton(data.agentStatus === 'processing');
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Chat poll error:', err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Switch the sidebar to show a different tab's chat context */
|
/** Switch the sidebar to show a different tab's chat context */
|
||||||
@@ -464,8 +469,11 @@ function updateStopButton(agentRunning) {
|
|||||||
async function stopAgent() {
|
async function stopAgent() {
|
||||||
if (!serverUrl) return;
|
if (!serverUrl) return;
|
||||||
try {
|
try {
|
||||||
await fetch(`${serverUrl}/sidebar-agent/stop`, { method: 'POST', headers: authHeaders() });
|
const resp = await fetch(`${serverUrl}/sidebar-agent/stop`, { method: 'POST', headers: authHeaders() });
|
||||||
} catch {}
|
if (!resp.ok) console.warn(`[gstack sidebar] Stop agent failed: ${resp.status}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Stop agent error:', err.message);
|
||||||
|
}
|
||||||
// Immediately clean up UI
|
// Immediately clean up UI
|
||||||
const thinking = document.getElementById('agent-thinking');
|
const thinking = document.getElementById('agent-thinking');
|
||||||
if (thinking) thinking.remove();
|
if (thinking) thinking.remove();
|
||||||
@@ -511,13 +519,18 @@ async function pollTabs() {
|
|||||||
try {
|
try {
|
||||||
const chromeTabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
const chromeTabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
activeTabUrl = chromeTabs?.[0]?.url || null;
|
activeTabUrl = chromeTabs?.[0]?.url || null;
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.debug('[gstack sidebar] Failed to get active tab URL:', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await fetch(`${serverUrl}/sidebar-tabs${activeTabUrl ? '?activeUrl=' + encodeURIComponent(activeTabUrl) : ''}`, {
|
const resp = await fetch(`${serverUrl}/sidebar-tabs${activeTabUrl ? '?activeUrl=' + encodeURIComponent(activeTabUrl) : ''}`, {
|
||||||
headers: authHeaders(),
|
headers: authHeaders(),
|
||||||
signal: AbortSignal.timeout(2000),
|
signal: AbortSignal.timeout(2000),
|
||||||
});
|
});
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) {
|
||||||
|
console.warn(`[gstack sidebar] Tab poll failed: ${resp.status} ${resp.statusText}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!data.tabs) return;
|
if (!data.tabs) return;
|
||||||
|
|
||||||
@@ -527,7 +540,9 @@ async function pollTabs() {
|
|||||||
lastTabJson = json;
|
lastTabJson = json;
|
||||||
|
|
||||||
renderTabBar(data.tabs);
|
renderTabBar(data.tabs);
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Tab poll error:', err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTabBar(tabs) {
|
function renderTabBar(tabs) {
|
||||||
@@ -573,7 +588,9 @@ async function switchBrowserTab(tabId) {
|
|||||||
// Switch chat context + re-poll tabs
|
// Switch chat context + re-poll tabs
|
||||||
switchChatTab(tabId);
|
switchChatTab(tabId);
|
||||||
pollTabs();
|
pollTabs();
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Failed to switch browser tab:', err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Clear Chat ─────────────────────────────────────────────────
|
// ─── Clear Chat ─────────────────────────────────────────────────
|
||||||
@@ -581,8 +598,11 @@ async function switchBrowserTab(tabId) {
|
|||||||
document.getElementById('clear-chat').addEventListener('click', async () => {
|
document.getElementById('clear-chat').addEventListener('click', async () => {
|
||||||
if (!serverUrl) return;
|
if (!serverUrl) return;
|
||||||
try {
|
try {
|
||||||
await fetch(`${serverUrl}/sidebar-chat/clear`, { method: 'POST', headers: authHeaders() });
|
const resp = await fetch(`${serverUrl}/sidebar-chat/clear`, { method: 'POST', headers: authHeaders() });
|
||||||
} catch {}
|
if (!resp.ok) console.warn(`[gstack sidebar] Clear chat failed: ${resp.status}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Clear chat error:', err.message);
|
||||||
|
}
|
||||||
// Reset local state
|
// Reset local state
|
||||||
chatLineCount = 0;
|
chatLineCount = 0;
|
||||||
renderedEntryIds.clear();
|
renderedEntryIds.clear();
|
||||||
@@ -734,7 +754,9 @@ function connectSSE() {
|
|||||||
eventSource = new EventSource(url);
|
eventSource = new EventSource(url);
|
||||||
|
|
||||||
eventSource.addEventListener('activity', (e) => {
|
eventSource.addEventListener('activity', (e) => {
|
||||||
try { addEntry(JSON.parse(e.data)); } catch {}
|
try { addEntry(JSON.parse(e.data)); } catch (err) {
|
||||||
|
console.error('[gstack sidebar] Failed to parse activity event:', err.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSource.addEventListener('gap', (e) => {
|
eventSource.addEventListener('gap', (e) => {
|
||||||
@@ -745,7 +767,9 @@ function connectSSE() {
|
|||||||
banner.className = 'gap-banner';
|
banner.className = 'gap-banner';
|
||||||
banner.textContent = `Missed ${data.availableFrom - data.gapFrom} events`;
|
banner.textContent = `Missed ${data.availableFrom - data.gapFrom} events`;
|
||||||
feed.appendChild(banner);
|
feed.appendChild(banner);
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Failed to parse gap event:', err.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +804,9 @@ async function fetchRefs() {
|
|||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
footer.textContent = `${data.refs.length} refs`;
|
footer.textContent = `${data.refs.length} refs`;
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Failed to fetch refs:', err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Inspector Tab ──────────────────────────────────────────────
|
// ─── Inspector Tab ──────────────────────────────────────────────
|
||||||
@@ -1292,15 +1318,17 @@ function connectInspectorSSE() {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
inspectorShowData(data);
|
inspectorShowData(data);
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('[gstack sidebar] Failed to parse inspectResult:', err.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inspectorSSE.addEventListener('error', () => {
|
inspectorSSE.addEventListener('error', () => {
|
||||||
// SSE connection failed — inspector works without it (basic mode)
|
// SSE connection failed — inspector works without it (basic mode)
|
||||||
if (inspectorSSE) { inspectorSSE.close(); inspectorSSE = null; }
|
if (inspectorSSE) { inspectorSSE.close(); inspectorSSE = null; }
|
||||||
});
|
});
|
||||||
} catch {
|
} catch (err) {
|
||||||
// SSE not available — that's fine
|
console.debug('[gstack sidebar] Inspector SSE not available:', err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user