The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 1efaca5a5f5eCommit detailsView latest

apps/dashboard/src/components/first-run-guide.tsx 111 lines · 3.7 KB · tsx
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
}