fix: address PR bot review comments

Critical fixes:
- Fix duplicated isPolling/pollLastTouched in events route (missing closing brace)
- Add missing path import to realtime.ts (path.basename was used without import)
- Fix error.message leak in sessions and beads/read routes (security)
- Add missing NextResponse import to activity route
- Fix diffDependencies to use composite key (type:target) for accurate tracking

Code quality:
- Fix beadCounts computation in kanban-controls (was counting epic's own deps, not child issues)
- Replace require('path') with ES module imports throughout

Tests: 13/15 passing (2 contract tests remain brittle)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
openhands 2026-02-14 09:34:10 +00:00
parent 6b0e76330e
commit 664ef2892b
8 changed files with 51 additions and 23 deletions

View file

@ -1,9 +1,11 @@
import { NextResponse } from 'next/server';
import path from 'node:path';
import { activityEventBus } from '../../../lib/realtime';
function isValidProjectRoot(root: string): boolean {
try {
const resolved = require('path').resolve(root);
return require('path').isAbsolute(resolved);
const resolved = path.resolve(root);
return path.isAbsolute(resolved);
} catch {
return false;
}

View file

@ -27,12 +27,13 @@ export async function GET(request: Request): Promise<Response> {
const issues = await readIssuesFromDisk({ projectRoot, preferBd: true });
return NextResponse.json({ ok: true, issues });
} catch (error) {
console.error('[API/BeadsRead] Failed to read issues:', error);
return NextResponse.json(
{
ok: false,
error: {
classification: 'unknown',
message: error instanceof Error ? error.message : 'Failed to read issues.',
classification: 'internal_error',
message: 'An internal error occurred while reading issues.',
},
},
{ status: 500 },

View file

@ -96,6 +96,10 @@ export async function GET(request: Request): Promise<Response> {
lastTouchedVersion = nextVersion;
write(toSseFrame(issuesEventBus.emit(projectRoot, lastTouchedPath, 'changed')));
}
} finally {
isPolling = false;
}
};
const touchedPoll = setInterval(() => {
void pollLastTouched();

View file

@ -1,4 +1,5 @@
import { NextResponse } from 'next/server';
import path from 'node:path';
import { readIssuesFromDisk } from '../../../lib/read-issues';
import { activityEventBus } from '../../../lib/realtime';
import { buildSessionTaskFeed, getCommunicationSummary } from '../../../lib/agent-sessions';
@ -7,8 +8,8 @@ function isValidProjectRoot(root: string): boolean {
// Basic validation: path should not contain traversal patterns
// and should resolve to an absolute path
try {
const resolved = require('path').resolve(root);
return require('path').isAbsolute(resolved);
const resolved = path.resolve(root);
return path.isAbsolute(resolved);
} catch {
return false;
}
@ -42,8 +43,8 @@ export async function GET(request: Request): Promise<Response> {
{
ok: false,
error: {
classification: 'unknown',
message: error instanceof Error ? error.message : 'Failed to load session feed.',
classification: 'internal_error',
message: 'An internal error occurred while loading the session feed.',
},
},
{ status: 500 },