|
| 1 | +/** |
| 2 | + * Controlled live proof for a configured ONEVibe + Claude Agent SDK server. |
| 3 | + * This is opt-in: it sends one small task to the real provider and may incur |
| 4 | + * provider usage. It refuses to create a task when server-side readiness is |
| 5 | + * absent, so local/demo environments never fall back silently. |
| 6 | + */ |
| 7 | +const baseUrl = (process.env.ONEVIBE_E2E_URL ?? 'http://127.0.0.1:4311').replace(/\/$/, '') |
| 8 | +const timeoutMs = Math.max(60_000, Number(process.env.ONEVIBE_E2E_TIMEOUT_MS ?? 5 * 60_000)) |
| 9 | + |
| 10 | +type Snapshot = { |
| 11 | + id: string |
| 12 | + status: string |
| 13 | + securityContext?: { executionBoundary?: string; runtimeSessionId?: string } |
| 14 | + events: Array<{ type: string; label?: string; payload: Record<string, unknown> }> |
| 15 | +} |
| 16 | + |
| 17 | +const request = async <T>(pathname: string, init?: RequestInit) => { |
| 18 | + let response: Response |
| 19 | + try { |
| 20 | + response = await fetch(`${baseUrl}${pathname}`, { ...init, headers: { 'Content-Type': 'application/json', ...init?.headers } }) |
| 21 | + } catch (error) { |
| 22 | + throw new Error(`Cannot reach ONEVibe at ${baseUrl}${pathname}: ${error instanceof Error ? error.message : 'network failure'}`) |
| 23 | + } |
| 24 | + const body = await response.json().catch(() => ({})) as T & { error?: string } |
| 25 | + if (!response.ok) throw new Error(`${pathname} returned ${response.status}${body.error ? `: ${body.error}` : ''}`) |
| 26 | + return body |
| 27 | +} |
| 28 | + |
| 29 | +const waitForTerminalSnapshot = async (taskId: string) => { |
| 30 | + const deadline = Date.now() + timeoutMs |
| 31 | + let latest: Snapshot | undefined |
| 32 | + while (Date.now() < deadline) { |
| 33 | + latest = await request<Snapshot>(`/api/tasks/${encodeURIComponent(taskId)}`) |
| 34 | + if (['completed', 'failed', 'cancelled'].includes(latest.status)) return latest |
| 35 | + await new Promise((resolve) => setTimeout(resolve, 2_000)) |
| 36 | + } |
| 37 | + throw new Error(`Task ${taskId} did not reach a terminal state within ${timeoutMs}ms (last state: ${latest?.status ?? 'unreadable'})`) |
| 38 | +} |
| 39 | + |
| 40 | +const main = async () => { |
| 41 | + const readiness = await request<{ providers: Array<{ id: string; available: boolean; detail: string }> }>('/api/runtime') |
| 42 | + const claude = readiness.providers.find((provider) => provider.id === 'claude_sdk') |
| 43 | + if (!claude?.available) throw new Error(`Claude SDK is not available at ${baseUrl}: ${claude?.detail ?? 'runtime status unavailable'}`) |
| 44 | + const created = await request<{ id: string }>('/api/tasks', { |
| 45 | + method: 'POST', |
| 46 | + body: JSON.stringify({ |
| 47 | + prompt: 'Create exactly one file named README.md with a title and one concise sentence stating that this is a governed Claude Agent SDK validation artifact. Do not access the network, credentials, or any path outside the current workspace.', |
| 48 | + provider: 'claude_sdk', mode: 'document', projectId: 'project_onevibe', references: [], attachments: [], skills: ['document', 'security_review'], |
| 49 | + }), |
| 50 | + }) |
| 51 | + const task = await waitForTerminalSnapshot(created.id) |
| 52 | + if (task.status !== 'completed') throw new Error(`Claude SDK task ${task.id} ended ${task.status}`) |
| 53 | + if (task.securityContext?.executionBoundary !== 'host_process') throw new Error(`Expected governed host workspace boundary, found ${task.securityContext?.executionBoundary ?? 'unknown'}`) |
| 54 | + if (!task.securityContext?.runtimeSessionId) throw new Error('Claude SDK session identity was not recorded') |
| 55 | + if (!task.events.some((event) => event.type === 'run_started' && event.label === 'Claude Agent SDK started')) throw new Error('Claude SDK run-start event missing') |
| 56 | + if (!task.events.some((event) => event.type === 'run_completed' && event.label === 'Claude Agent SDK completed')) throw new Error('Claude SDK completion evidence missing') |
| 57 | + const readme = await request<{ content: string }>(`/api/tasks/${encodeURIComponent(task.id)}/file?path=README.md`) |
| 58 | + if (!readme.content.trim()) throw new Error('Expected a non-empty README.md from the Claude SDK task') |
| 59 | + const evidence = await request<{ valid: boolean }>(`/api/tasks/${encodeURIComponent(task.id)}/evidence`) |
| 60 | + if (!evidence.valid) throw new Error('Evidence chain verification failed') |
| 61 | + console.log(JSON.stringify({ taskId: task.id, status: task.status, executionBoundary: task.securityContext.executionBoundary, sessionRecorded: Boolean(task.securityContext.runtimeSessionId), evidenceValid: evidence.valid }, null, 2)) |
| 62 | +} |
| 63 | + |
| 64 | +main().catch((error: unknown) => { |
| 65 | + console.error(error instanceof Error ? error.message : error) |
| 66 | + process.exitCode = 1 |
| 67 | +}) |
0 commit comments