ed243594d2
Wave 3 (Task 6-3-01): /build/[competencyId] rewritten as a real build surface —
variant statement + starter files in a live namespace sandbox, workspace file tree +
editor with save, Run/Test buttons executing bounded commands in-sandbox with
read-only TerminalFrame output (CUT-2), live TelemetryStatus pulse, Lab feedback over
the live trace, honest 503-busy/403-429-denied states with retry.
Wave 4 (Task 6-4-01): /defend/[competencyId] rewritten — DefenseSession: start ->
examiner question -> typed answers (mic capture w/ MediaRecorder consent + denied
fallback; browser-SR first-class per CUT-1) -> finish -> verdict + integrity signals
-> Grade My Work renders real rubric bars from the trace digest. Dead mock components
disposed (oral-defense-interface, assessor-results-panel, proctor-banner — G-5 class).
Wave 5 (Task 6-5-01): test_e2e_credential_flow — REAL uvicorn + REAL namespaces:
variant -> sandbox -> in-sandbox exec -> trace -> grade (seed stamped) -> coaching ->
defense -> verdict -> proctor; corpus-fixture scan of all payloads. E2E caught a real
bug: the sandboxes API dropped task_id (every HTTP-created sandbox was capture-less)
— fixed. README E2E + manual browser pass documented.
pnpm build 4/4; typecheck FULL TURBO; backend suite 397 green; ruff clean.
---ci---
phase: 6
milestone: v0.3
status: execute
requirements: {covered: [REQ-3-007, REQ-3-008], partial: []}
---/ci---
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
'use client';
|
|
|
|
import { TerminalFrame } from '@nextcraft/ui';
|
|
import type { TerminalLine } from '@nextcraft/ui';
|
|
import { TelemetryStatus } from '@nextcraft/ui';
|
|
import type { ExecResult } from '../../lib/engine-client';
|
|
|
|
/**
|
|
* Read-only exec-output panel (CUT-2): Run/Test results stream here —
|
|
* this is NOT an interactive shell. TelemetryStatus pulses while the
|
|
* capture agent streams events to ai-service.
|
|
*/
|
|
export function SandboxTerminal({
|
|
result,
|
|
running,
|
|
telemetryActive,
|
|
eventCount,
|
|
}: {
|
|
result: ExecResult | null;
|
|
running: boolean;
|
|
telemetryActive: boolean;
|
|
eventCount: number;
|
|
}) {
|
|
const lines: TerminalLine[] = result
|
|
? [
|
|
{ text: `$ ${result.cmd.join(' ')}` },
|
|
...result.stdout.split('\n').filter(Boolean).map((t) => ({ text: t })),
|
|
...result.stderr
|
|
.split('\n')
|
|
.filter(Boolean)
|
|
.map((t) => ({ text: t, error: true })),
|
|
{
|
|
text: `exit ${result.returncode} (${result.duration_s.toFixed(2)}s)`,
|
|
error: result.returncode !== 0,
|
|
},
|
|
]
|
|
: [];
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<TelemetryStatus active={telemetryActive} eventCount={eventCount} compact />
|
|
<span className="text-[10px] uppercase tracking-wide text-slate-400 dark:text-slate-500">
|
|
read-only output (interactive shell lands in v0.4)
|
|
</span>
|
|
</div>
|
|
<TerminalFrame
|
|
lines={lines}
|
|
status={running ? 'streaming' : result ? 'finished' : 'idle'}
|
|
title="Sandbox output"
|
|
/>
|
|
</div>
|
|
);
|
|
} |