| 1 | import { useState } from 'react';
|
| 2 | import { Check, ExternalLink } from 'lucide-react';
|
| 3 | import { FrameCorners } from '@/components/agent-mark';
|
| 4 |
|
| 5 | type RecordValue = Record<string, any>;
|
| 6 |
|
| 7 | type FirstRunGuideProps = {
|
| 8 | data: {
|
| 9 | agents: RecordValue[];
|
| 10 | publications: RecordValue[];
|
| 11 | runtimes: RecordValue[];
|
| 12 | };
|
| 13 | api: (path: string, init?: RequestInit) => Promise<unknown>;
|
| 14 | refreshDashboard: () => Promise<void>;
|
| 15 | };
|
| 16 |
|
| 17 | export function FirstRunGuide({ data, api, refreshDashboard }: FirstRunGuideProps) {
|
| 18 | const [working, setWorking] = useState(false);
|
| 19 | const [error, setError] = useState('');
|
| 20 | const agent = data.agents.find((candidate) => candidate.status !== 'revoked');
|
| 21 | const agentId = String(agent?.agentId ?? '');
|
| 22 | const runtime = data.runtimes.find(
|
| 23 | (candidate) => candidate.agentId === agentId && candidate.status === 'verified',
|
| 24 | );
|
| 25 | const published = data.publications.some(
|
| 26 | (candidate) => candidate.agentId === agentId && candidate.published,
|
| 27 | );
|
| 28 |
|
| 29 | if (agent && published) return null;
|
| 30 |
|
| 31 | const publishAgent = async () => {
|
| 32 | setWorking(true);
|
| 33 | setError('');
|
| 34 | try {
|
| 35 | await api(`/v0.1/agents/${encodeURIComponent(agentId)}/publication`, {
|
| 36 | method: 'POST',
|
| 37 | body: JSON.stringify({ published: true }),
|
| 38 | });
|
| 39 | await refreshDashboard();
|
| 40 | } catch (reason) {
|
| 41 | setError(reason instanceof Error ? reason.message : 'Could not publish Agent Card');
|
| 42 | } finally {
|
| 43 | setWorking(false);
|
| 44 | }
|
| 45 | };
|
| 46 |
|
| 47 | return (
|
| 48 | <section className="firstRunGuide" aria-labelledby="first-run-title">
|
| 49 | <FrameCorners />
|
| 50 | <header className="firstRunHeader">
|
| 51 | <div>
|
| 52 | <h2 id="first-run-title">Connect your first cloud agent</h2>
|
| 53 | <p>Choose its provider. OpenClasp handles the runtime connection in the background.</p>
|
| 54 | </div>
|
| 55 | <span className="firstRunCount">{agent ? (runtime ? '2/2' : '1/2') : '0/2'}</span>
|
| 56 | </header>
|
| 57 |
|
| 58 | {error ? (
|
| 59 | <div className="errorBar" role="alert">
|
| 60 | {error}
|
| 61 | </div>
|
| 62 | ) : null}
|
| 63 |
|
| 64 | {!agent ? (
|
| 65 | <div className="firstRunReview">
|
| 66 | <div className="firstRunStage">
|
| 67 | <span className="firstRunIcon">01</span>
|
| 68 | <div>
|
| 69 | <strong>Choose the agent provider</strong>
|
| 70 | <p>Botpress is available now. You enter only the agent name.</p>
|
| 71 | </div>
|
| 72 | </div>
|
| 73 | <a className="primary" href="/connect">
|
| 74 | Connect provider <ExternalLink />
|
| 75 | </a>
|
| 76 | </div>
|
| 77 | ) : !runtime ? (
|
| 78 | <div className="firstRunReview">
|
| 79 | <div className="firstRunStage">
|
| 80 | <span className="firstRunIcon">02</span>
|
| 81 | <div>
|
| 82 | <strong>Waiting for runtime verification</strong>
|
| 83 | <p>Keep the connector running. OpenClasp will verify its public HTTPS endpoint.</p>
|
| 84 | </div>
|
| 85 | </div>
|
| 86 | </div>
|
| 87 | ) : (
|
| 88 | <div className="firstRunReview">
|
| 89 | <div className="firstRunStage">
|
| 90 | <span className="firstRunIcon complete">
|
| 91 | <Check />
|
| 92 | </span>
|
| 93 | <div>
|
| 94 | <strong>Runtime verified</strong>
|
| 95 | <p>Review the agent-declared profile below, then publish it to the marketplace.</p>
|
| 96 | </div>
|
| 97 | </div>
|
| 98 | <article className="agentCardPreview">
|
| 99 | <span className="verifiedPublisher">✓ Runtime verified</span>
|
| 100 | <h3>{String(agent.name)}</h3>
|
| 101 | <p>{String(agent.description)}</p>
|
| 102 | <small>Profile details are self-declared by the connected agent.</small>
|
| 103 | </article>
|
| 104 | <button className="primary" type="button" disabled={working} onClick={publishAgent}>
|
| 105 | {working ? 'Publishing…' : 'Publish Agent Card'}
|
| 106 | </button>
|
| 107 | </div>
|
| 108 | )}
|
| 109 | </section>
|
| 110 | );
|
| 111 | }
|