File size: 4,893 Bytes
7b92798 6b21561 7b92798 4a74c29 7b92798 4a74c29 7b92798 4a74c29 7b92798 4a74c29 7b92798 4a74c29 7b92798 4a74c29 7b92798 82c10a3 291dc87 82c10a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | import type { Decision, DemoRepo, IntentReceipt } from "./types";
/** Empty string = same origin (HF Space). Local dev: set NEXT_PUBLIC_API_URL in frontend/.env.local */
export const API_BASE = (process.env.NEXT_PUBLIC_API_URL ?? "").replace(/\/$/, "");
async function parseResponse<T>(r: Response): Promise<T> {
const text = await r.text();
if (!r.ok) {
let detail = r.statusText || `HTTP ${r.status}`;
if (text) {
try {
const err = JSON.parse(text) as { detail?: string | unknown };
if (typeof err.detail === "string") detail = err.detail;
else if (err.detail) detail = JSON.stringify(err.detail);
else detail = text.slice(0, 200);
} catch {
detail = text.slice(0, 200);
}
}
if (r.status === 0 || detail === "Failed to fetch") {
throw new Error(
`Cannot reach backend at ${API_BASE}. Start it with: cd backend && python -m uvicorn main:app --reload --port 8000`
);
}
throw new Error(detail);
}
if (!text.trim()) {
throw new Error(
`Empty response from server. Check CORS (frontend port) and that the backend is running on ${API_BASE}.`
);
}
try {
return JSON.parse(text) as T;
} catch {
throw new Error(`Invalid JSON from server: ${text.slice(0, 120)}`);
}
}
async function post<T>(path: string, body: unknown): Promise<T> {
const r = await fetch(`${API_BASE}${path}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
return parseResponse<T>(r);
}
async function get<T>(path: string): Promise<T> {
const r = await fetch(`${API_BASE}${path}`);
return parseResponse<T>(r);
}
export async function fetchDemoRepos(): Promise<{ repos: DemoRepo[] }> {
return get("/api/demo-repos");
}
export async function fetchSampleAlert(): Promise<{ text: string }> {
return get("/api/sample-alert");
}
export async function parseUpload(file: File) {
const form = new FormData();
form.append("file", file);
const r = await fetch(`${API_BASE}/api/parse-upload`, { method: "POST", body: form });
return parseResponse<{
format: string;
alert_text: string;
title: string;
finding_count: number;
cve_ids?: string[];
}>(r);
}
export function explainAlert(email: string, alert_text: string, session_id?: string) {
return post<{
analysis: Record<string, unknown>;
explanation: string;
source: string;
session_id: string;
cve_ids?: string[];
}>("/api/explain", { email, alert_text, session_id });
}
export function auditCode(email: string, code: string, session_id?: string) {
return post<{
analysis: Record<string, unknown>;
explanation: string;
source: string;
session_id: string;
}>("/api/audit-code", { email, code, session_id });
}
export function scanGithub(
email: string,
repo_url: string,
session_id?: string,
deep_scan = true
) {
return post<{
scan: Record<string, unknown>;
analysis: Record<string, unknown>;
analysis_source: string;
session_id: string;
}>("/api/github/scan", { email, repo_url, session_id, deep_scan });
}
export function planFix(
email: string,
alert_text: string,
opts?: {
user_request?: string;
session_id?: string;
simulate_attack?: boolean;
use_delegation_demo?: boolean;
}
) {
return post<{
plan: { goal: string; steps: { action: string; description?: string }[] };
decisions: Decision[];
summary: string;
armoriq_live: boolean;
intent_receipt: IntentReceipt;
plan_source: string;
session_id: string;
delegation_demo?: { scenario: string; message: string }[];
}>("/api/plan-fix", {
email,
alert_text,
user_request: opts?.user_request || "Create a safe remediation plan",
session_id: opts?.session_id,
simulate_attack: opts?.simulate_attack ?? false,
use_delegation_demo: opts?.use_delegation_demo ?? false,
});
}
export async function exportBrief(body: {
email: string;
session_id?: string;
alert_text?: string;
analysis?: Record<string, unknown> | null;
plan?: { goal: string; steps: unknown[] } | null;
decisions?: Decision[];
receipt?: IntentReceipt | null;
}) {
const r = await fetch(`${API_BASE}/api/export-brief`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const text = await r.text();
if (!r.ok) {
throw new Error(text || "Export failed");
}
return text;
}
export function authSignup(email: string) {
return post<{
email: string;
api_key: string;
plan: string;
install_snippet: string;
}>("/api/auth/signup", { email });
}
export function authMe(apiKey: string) {
return fetch(`${API_BASE}/api/auth/me`, {
headers: { Authorization: `Bearer ${apiKey}` },
}).then(parseResponse<{ email: string; plan: string; created_at: string }>);
}
|