fix: address PR review comments and security issues
- Fix command injection in bb-init.mjs by using execFileSync with argument arrays - Fix parser.ts skipAgentFilter option not being respected - Fix src/app/globals.css truncated CSS rule causing parse errors - Fix status-badge.tsx BeadStatus type import from canonical source - Fix agent-registry.ts missing 'agent' prefix in callBdAgentShow - Fix tools/bb.ts null data access for activity-lease command - Fix src/app/api/sessions/route.ts projectRoot not passed to listAgents - Update package.json test script to include all test files - Fix tailwind.config.ts content glob missing UI components - Remove .beadboard/agent/runtime/existing-agent.pid and add .gitignore rule Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
9afa3f7bbd
commit
6cdca6e7c9
11 changed files with 46 additions and 26 deletions
|
|
@ -1 +0,0 @@
|
|||
63676
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -13,3 +13,6 @@ worktrees/
|
|||
|
||||
# local screenshot artifacts
|
||||
artifacts/
|
||||
|
||||
# beadboard runtime artifacts
|
||||
.beadboard/
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "node --test tests/bootstrap.test.mjs && node --import tsx --test tests/components/sessions/sessions-header.test.ts && node --import tsx --test tests/components/sessions/agent-station-logic.test.ts && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts"
|
||||
"test": "node --test tests/bootstrap.test.mjs && node --import tsx --test tests/api/events-route.test.ts tests/api/mutations-routes.test.ts tests/api/projects-route.test.ts tests/api/sessions-route.test.ts tests/components/sessions/agent-station-logic.test.ts tests/components/sessions/sessions-header-logic.test.ts tests/components/sessions/sessions-header.test.ts tests/components/sessions/sessions-store.test.ts tests/components/shared/status-utils-visual.test.ts tests/guards/graph-responsive-contract.test.mjs tests/guards/kanban-responsive-contract.test.mjs tests/guards/no-direct-jsonl-write.test.mjs tests/guards/no-inline-style-in-kanban.test.mjs tests/guards/ui-foundation-contract.test.mjs tests/hooks/use-beads-subscription-shallow.test.ts tests/hooks/use-beads-subscription.test.ts tests/hooks/use-url-state.test.ts tests/lib/activity.test.ts tests/lib/agent-liveness.test.ts tests/lib/agent-mail.test.ts tests/lib/agent-protocol.test.ts tests/lib/agent-registry-bd.test.ts tests/lib/agent-registry.test.ts tests/lib/agent-reservations.test.ts tests/lib/agent-sessions-liveness.test.ts tests/lib/agent-sessions-state.test.ts tests/lib/agent-sessions.test.ts tests/lib/agent-takeover.test.ts tests/lib/aggregate-read.test.ts tests/lib/bd-path.test.ts tests/lib/bridge.test.ts tests/lib/coalescer.test.ts tests/lib/graph-view.test.ts tests/lib/graph.test.ts tests/lib/identity-isolation.test.ts tests/lib/issue-editor.test.ts tests/lib/kanban.test.ts tests/lib/mission-pathing.test.ts tests/lib/mutations.test.ts tests/lib/parser.test.ts tests/lib/path-overlap.test.ts tests/lib/pathing.test.ts tests/lib/project-context.test.ts tests/lib/project-scope.test.ts tests/lib/read-issues.test.ts tests/lib/read-text-retry.test.ts tests/lib/realtime-history.test.ts tests/lib/realtime.test.ts tests/lib/registry.test.ts tests/lib/scanner.test.ts tests/lib/snapshot-differ-stress.test.ts tests/lib/snapshot-differ.test.ts tests/lib/social-cards.test.ts tests/lib/swarm-cards.test.ts tests/lib/swarm-molecules-simple.test.ts tests/lib/swarm-molecules.test.ts tests/lib/watcher.test.ts tests/lib/writeback.test.ts tests/scripts/bb-init.test.ts tests/skills/beadboard-driver/generate-agent-name.test.ts tests/skills/beadboard-driver/readiness-report.test.ts tests/skills/beadboard-driver/resolve-bb.test.ts tests/skills/beadboard-driver/session-preflight.test.ts tests/skills/beadboard-driver/skill-local-runner.test.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { parseArgs } from 'node:util';
|
|||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { execSync, execFileSync } from 'node:child_process';
|
||||
|
||||
function log(obj) {
|
||||
process.stdout.write(`${JSON.stringify(obj, null, 2)}
|
||||
|
|
@ -48,20 +48,20 @@ async function resolveBbPath() {
|
|||
const tsEntry = path.join(process.cwd(), 'tools', 'bb.ts');
|
||||
try {
|
||||
await fs.access(tsEntry);
|
||||
return `npx tsx ${tsEntry}`;
|
||||
return { type: 'tsx', path: tsEntry };
|
||||
} catch {}
|
||||
|
||||
if (envRepo) {
|
||||
const p = path.join(envRepo, 'bb.ps1');
|
||||
try {
|
||||
await fs.access(p);
|
||||
return p;
|
||||
return { type: 'powershell', path: p };
|
||||
} catch {}
|
||||
|
||||
const envTs = path.join(envRepo, 'tools', 'bb.ts');
|
||||
try {
|
||||
await fs.access(envTs);
|
||||
return `npx tsx ${envTs}`;
|
||||
return { type: 'tsx', path: envTs };
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +82,9 @@ async function main() {
|
|||
|
||||
const isNonInteractive = values['non-interactive'];
|
||||
const projectRoot = values['project-root'] || process.cwd();
|
||||
const bbPath = await resolveBbPath();
|
||||
const bbResult = await resolveBbPath();
|
||||
|
||||
if (!bbPath) {
|
||||
if (!bbResult) {
|
||||
error('BB_NOT_FOUND', 'Could not resolve bb.ps1 or tools/bb.ts');
|
||||
}
|
||||
|
||||
|
|
@ -103,8 +103,6 @@ async function main() {
|
|||
}
|
||||
|
||||
try {
|
||||
const bbExec = bbPath.includes('npx tsx') ? bbPath : `powershell.exe -NoProfile -Command "& '${bbPath}'"`;
|
||||
|
||||
// Compose environment fingerprint (Rig)
|
||||
const rigId = `${os.platform()}-${os.arch()}-${os.hostname()}`;
|
||||
|
||||
|
|
@ -112,10 +110,18 @@ async function main() {
|
|||
|
||||
if (mode === 'register') {
|
||||
const role = values.role || 'agent';
|
||||
execSync(`${bbExec} agent register --name ${agentId} --role ${role} --rig ${rigId} --json`, { stdio: 'ignore', cwd: projectRoot, env });
|
||||
const registerArgs = bbResult.type === 'tsx'
|
||||
? ['tsx', bbResult.path, 'agent', 'register', '--name', agentId, '--role', role, '--rig', rigId, '--json']
|
||||
: ['agent', 'register', '--name', agentId, '--role', role, '--rig', rigId, '--json'];
|
||||
const registerCmd = bbResult.type === 'tsx' ? 'npx' : bbResult.path;
|
||||
execFileSync(registerCmd, registerArgs, { stdio: 'ignore', cwd: projectRoot, env });
|
||||
} else {
|
||||
// Start/Extend the lease to show we are now active
|
||||
execSync(`${bbExec} agent activity-lease --agent ${agentId} --json`, { stdio: 'ignore', cwd: projectRoot, env });
|
||||
const leaseArgs = bbResult.type === 'tsx'
|
||||
? ['tsx', bbResult.path, 'agent', 'activity-lease', '--agent', agentId, '--json']
|
||||
: ['agent', 'activity-lease', '--agent', agentId, '--json'];
|
||||
const leaseCmd = bbResult.type === 'tsx' ? 'npx' : bbResult.path;
|
||||
execFileSync(leaseCmd, leaseArgs, { stdio: 'ignore', cwd: projectRoot, env });
|
||||
}
|
||||
|
||||
log({
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export async function GET(request: Request): Promise<Response> {
|
|||
const communication = await getCommunicationSummary();
|
||||
const livenessMap = await getAgentLivenessMap(projectRoot, activity);
|
||||
const incursions = await calculateIncursions();
|
||||
const agentsResult = await listAgents({});
|
||||
const agentsResult = await listAgents({}, { projectRoot });
|
||||
|
||||
const feed = buildSessionTaskFeed(issues, activity, communication, livenessMap);
|
||||
|
||||
|
|
|
|||
|
|
@ -296,4 +296,3 @@ body::after {
|
|||
filter: drop-shadow(0 0 10px rgba(56, 189, 248, 0.6));
|
||||
}
|
||||
|
||||
.workflow-graph-flow .workflow-edge-cycle .react-f
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Badge } from '@/components/ui/badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { BeadStatus } from '@/lib/types';
|
||||
|
||||
type BeadStatus = 'ready' | 'in_progress' | 'blocked' | 'closed';
|
||||
type BadgeSize = 'sm' | 'md';
|
||||
|
||||
interface StatusBadgeProps {
|
||||
|
|
@ -9,11 +9,14 @@ interface StatusBadgeProps {
|
|||
size?: BadgeSize;
|
||||
}
|
||||
|
||||
const STATUS_CLASSES: Record<BeadStatus, string> = {
|
||||
ready: 'border-teal-500/30 bg-teal-500/15 text-teal-200',
|
||||
const STATUS_CLASSES: Partial<Record<BeadStatus, string>> = {
|
||||
open: 'border-teal-500/30 bg-teal-500/15 text-teal-200',
|
||||
in_progress: 'border-green-500/30 bg-green-500/15 text-green-200',
|
||||
blocked: 'border-amber-500/30 bg-amber-500/15 text-amber-200',
|
||||
deferred: 'border-slate-500/30 bg-slate-500/15 text-slate-300',
|
||||
closed: 'border-slate-500/30 bg-slate-500/15 text-slate-300',
|
||||
pinned: 'border-purple-500/30 bg-purple-500/15 text-purple-200',
|
||||
hooked: 'border-cyan-500/30 bg-cyan-500/15 text-cyan-200',
|
||||
};
|
||||
|
||||
const SIZE_CLASSES: Record<BadgeSize, string> = {
|
||||
|
|
@ -21,24 +24,30 @@ const SIZE_CLASSES: Record<BadgeSize, string> = {
|
|||
md: 'text-xs px-2.5 py-0.5',
|
||||
};
|
||||
|
||||
const STATUS_LABELS: Record<BeadStatus, string> = {
|
||||
ready: 'Ready',
|
||||
const STATUS_LABELS: Partial<Record<BeadStatus, string>> = {
|
||||
open: 'Open',
|
||||
in_progress: 'In Progress',
|
||||
blocked: 'Blocked',
|
||||
deferred: 'Deferred',
|
||||
closed: 'Closed',
|
||||
pinned: 'Pinned',
|
||||
hooked: 'Hooked',
|
||||
};
|
||||
|
||||
export function StatusBadge({ status, size = 'md' }: StatusBadgeProps) {
|
||||
const statusClass = STATUS_CLASSES[status] || 'border-slate-500/30 bg-slate-500/15 text-slate-300';
|
||||
const statusLabel = STATUS_LABELS[status] || status;
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
'rounded-md border font-semibold',
|
||||
STATUS_CLASSES[status],
|
||||
statusClass,
|
||||
SIZE_CLASSES[size]
|
||||
)}
|
||||
>
|
||||
{STATUS_LABELS[status]}
|
||||
{statusLabel}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ function trimOrEmpty(value: unknown): string {
|
|||
async function callBdAgentShow(beadId: string, projectRoot: string): Promise<AgentRecord | null> {
|
||||
const showResult = await runBdCommand({
|
||||
projectRoot,
|
||||
args: ['show', beadId, '--json'],
|
||||
args: ['agent', 'show', beadId, '--json'],
|
||||
});
|
||||
|
||||
if (!showResult.success) {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export function parseIssuesJsonl(text: string, options: ParseIssuesOptions = {})
|
|||
}
|
||||
|
||||
// Exclude agent identities from standard mission lists
|
||||
if (normalized.labels.includes('gt:agent')) {
|
||||
if (!options.skipAgentFilter && normalized.labels.includes('gt:agent')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import tailwindcssAnimate from 'tailwindcss-animate';
|
|||
|
||||
const config: Config = {
|
||||
darkMode: ['class'],
|
||||
content: ['./src/**/*.{ts,tsx}'],
|
||||
content: ['./src/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,11 @@ function printResponse(response: AnyCommandResponse, json: boolean) {
|
|||
console.log(`Agent: ${d.agent_id}\nRole: ${d.role}\nStatus: ${d.status}\nLast Seen: ${d.last_seen_at}`);
|
||||
} else if (response.command === 'agent activity-lease') {
|
||||
const d = response.data;
|
||||
if (d) {
|
||||
console.log(`✓ Activity lease extended: ${d.agent_id} (version: ${d.version})`);
|
||||
} else {
|
||||
console.log(`✓ Activity lease extended.`);
|
||||
}
|
||||
} else if (response.command === 'agent send') {
|
||||
const d = response.data;
|
||||
console.log(`✓ Message sent: ${d.message_id} (state: ${d.state})`);
|
||||
|
|
@ -176,7 +180,7 @@ async function main() {
|
|||
// we extend their lease as a side-effect of real work.
|
||||
// This provides observability WITHOUT background workers or popups.
|
||||
const targetAgent = stringArg(values.agent) || stringArg(values.from) || stringArg(values.name);
|
||||
if (targetAgent && command !== 'register') {
|
||||
if (targetAgent && command !== 'register' && command !== 'activity-lease') {
|
||||
await extendActivityLease({ agent: targetAgent }, deps).catch(() => {});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue