diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..07548df --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,158 @@ +import { useEffect, useMemo, useState } from "react"; +import { + callSkill, + loadAgentConfig, + loadSession, + sampleArgs, + signInUrl, +} from "./a2a.js"; + +export function App() { + const [config, setConfig] = useState(null); + const [session, setSession] = useState(null); + const [selectedSkillName, setSelectedSkillName] = useState(""); + const [argsText, setArgsText] = useState("{}"); + const [result, setResult] = useState(null); + const [error, setError] = useState(null); + const [running, setRunning] = useState(false); + + useEffect(() => { + loadAgentConfig() + .then((data) => { + setConfig(data); + const firstSkill = data.skills?.[0]; + if (firstSkill) { + setSelectedSkillName(firstSkill.name); + setArgsText(JSON.stringify(sampleArgs(firstSkill), null, 2)); + } + return loadSession(data).catch(() => null); + }) + .then((sessionData) => setSession(sessionData)) + .catch((err) => setError(err.message || String(err))); + }, []); + + const selectedSkill = useMemo( + () => config?.skills?.find((skill) => skill.name === selectedSkillName), + [config, selectedSkillName], + ); + + const needsSignIn = Boolean( + config?.auth?.invokeRequiresSession && session && !session.authenticated, + ); + const signInHref = useMemo( + () => (config && needsSignIn ? signInUrl(config) : null), + [config, needsSignIn], + ); + + function selectSkill(skill) { + setSelectedSkillName(skill.name); + setArgsText(JSON.stringify(sampleArgs(skill), null, 2)); + setResult(null); + setError(null); + } + + async function runSkill(event) { + event.preventDefault(); + if (!config || !selectedSkill) return; + setRunning(true); + setError(null); + setResult(null); + try { + const args = JSON.parse(argsText || "{}"); + const data = await callSkill(config, selectedSkill.name, args); + setResult(data); + } catch (err) { + setError(err.message || String(err)); + } finally { + setRunning(false); + } + } + + if (!config) { + return ( +
+

{error || "Loading A2A agent contract..."}

+
+ ); + } + + return ( +
+ + +
+
+
+

Skill runner

+

{selectedSkill?.name || "No skills found"}

+
+ + Docs + +
+ + {selectedSkill ? ( +
+