'use client';
import React, { useState } from 'react';
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
type Sdk = 'nodejs' | 'python' | 'go' | 'java';
interface WizardState {
step: 1 | 2 | 3 | 4;
agentName: string;
agentId: string | null;
clientId: string | null;
clientSecret: string | null;
selectedSdk: Sdk;
loading: boolean;
error: string | null;
}
interface AgentCreateResponse {
agentId: string;
}
interface CredentialsResponse {
clientId: string;
clientSecret: string;
}
// ---------------------------------------------------------------------------
// SDK code snippets
// ---------------------------------------------------------------------------
function buildSnippet(
sdk: Sdk,
apiUrl: string,
clientId: string,
clientSecret: string
): string {
switch (sdk) {
case 'nodejs':
return `import { AgentIdPClient } from '@sentryagent/idp-sdk';
const client = new AgentIdPClient({
apiUrl: '${apiUrl}',
clientId: '${clientId}',
clientSecret: '${clientSecret}',
});
const { accessToken } = await client.tokens.issue();
console.log('Access token:', accessToken);`;
case 'python':
return `from sentryagent_idp import AgentIdPClient
client = AgentIdPClient(
api_url="${apiUrl}",
client_id="${clientId}",
client_secret="${clientSecret}",
)
token_response = client.tokens.issue()
print("Access token:", token_response.access_token)`;
case 'go':
return `import idp "github.com/sentryagent/idp-sdk-go"
client := idp.NewClient(idp.Config{
APIURL: "${apiUrl}",
ClientID: "${clientId}",
ClientSecret: "${clientSecret}",
})
token, err := client.Tokens.Issue(ctx)
if err != nil {
panic(err)
}
fmt.Println("Access token:", token.AccessToken)`;
case 'java':
return `AgentIdPClient client = AgentIdPClient.builder()
.apiUrl("${apiUrl}")
.clientId("${clientId}")
.clientSecret("${clientSecret}")
.build();
TokenResponse token = client.tokens().issue();
System.out.println("Access token: " + token.getAccessToken());`;
default:
return '';
}
}
// ---------------------------------------------------------------------------
// Shared UI helpers
// ---------------------------------------------------------------------------
function StepIndicator({
current,
total,
}: {
current: number;
total: number;
}): React.ReactElement {
return (
{Array.from({ length: total }, (_, i) => i + 1).map((n) => (
{n < current ? '✓' : n}
{n < total && (
)}
))}
);
}
function CopyButton({ text }: { text: string }): React.ReactElement {
const [copied, setCopied] = useState(false);
const handleCopy = async (): Promise => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
);
}
function ErrorAlert({ message }: { message: string }): React.ReactElement {
return (
{message}
);
}
// ---------------------------------------------------------------------------
// Step components
// ---------------------------------------------------------------------------
function Step1AccountSetup({
onNext,
}: {
onNext: () => void;
}): React.ReactElement {
return (
Step 1: Account Setup
Before registering your first agent, make sure you have the AgentIdP
server running.
{[
{
n: 1,
title: 'Clone the repository',
code: 'git clone https://github.com/sentryagent/sentryagent-idp.git',
},
{
n: 2,
title: 'Copy environment variables',
code: 'cp .env.example .env',
},
{
n: 3,
title: 'Start the server',
code: 'docker compose up -d && npm run db:migrate',
},
{
n: 4,
title: 'Verify the server is healthy',
code: 'curl http://localhost:3000/health',
},
].map(({ n, title, code }) => (
-
{n}
))}
);
}
function Step2RegisterAgent({
agentName,
onAgentNameChange,
agentId,
loading,
error,
onRegister,
onNext,
}: {
agentName: string;
onAgentNameChange: (v: string) => void;
agentId: string | null;
loading: boolean;
error: string | null;
onRegister: () => void;
onNext: () => void;
}): React.ReactElement {
return (
Step 2: Register Your Agent
Give your agent a name and register it with AgentIdP. You will receive a
unique Agent ID.
{error &&
}
{agentId ? (
Agent registered successfully!
) : (
)}
{agentId && (
)}
);
}
function Step3GenerateCredentials({
agentId,
clientId,
clientSecret,
loading,
error,
onGenerate,
onNext,
}: {
agentId: string;
clientId: string | null;
clientSecret: string | null;
loading: boolean;
error: string | null;
onGenerate: () => void;
onNext: () => void;
}): React.ReactElement {
return (
Step 3: Generate Credentials
Generate OAuth 2.0 client credentials for agent{' '}
{agentId}
. Store your client secret securely — it will not be shown again.
{error &&
}
{clientId && clientSecret ? (
Credentials generated. Store these securely!
Client Secret
{clientSecret}
) : (
)}
{clientId && clientSecret && (
)}
);
}
const SDK_OPTIONS: { id: Sdk; label: string; description: string }[] = [
{
id: 'nodejs',
label: 'Node.js / TypeScript',
description: 'npm install @sentryagent/idp-sdk',
},
{
id: 'python',
label: 'Python',
description: 'pip install sentryagent-idp',
},
{
id: 'go',
label: 'Go',
description: 'go get github.com/sentryagent/idp-sdk-go',
},
{
id: 'java',
label: 'Java',
description: 'Maven / Gradle — ai.sentryagent:idp-sdk:1.0.0',
},
];
function Step4SdkSelection({
selectedSdk,
onSdkChange,
clientId,
clientSecret,
apiUrl,
}: {
selectedSdk: Sdk;
onSdkChange: (sdk: Sdk) => void;
clientId: string;
clientSecret: string;
apiUrl: string;
}): React.ReactElement {
const snippet = buildSnippet(selectedSdk, apiUrl, clientId, clientSecret);
return (
Step 4: Choose Your SDK
Select your language and copy the ready-to-run code snippet below. Your
credentials are pre-filled.
{SDK_OPTIONS.map(({ id, label, description }) => (
))}
You are all set!
Your agent is registered and you have credentials. Start making
authenticated API calls using the snippet above.
);
}
// ---------------------------------------------------------------------------
// Main wizard component
// ---------------------------------------------------------------------------
interface GetStartedWizardProps {
apiUrl: string;
}
export function GetStartedWizard({
apiUrl,
}: GetStartedWizardProps): React.ReactElement {
const [state, setState] = useState({
step: 1,
agentName: '',
agentId: null,
clientId: null,
clientSecret: null,
selectedSdk: 'nodejs',
loading: false,
error: null,
});
const goToStep = (step: WizardState['step']): void => {
setState((prev) => ({ ...prev, step, error: null }));
};
const handleAgentNameChange = (value: string): void => {
setState((prev) => ({ ...prev, agentName: value }));
};
const handleSdkChange = (sdk: Sdk): void => {
setState((prev) => ({ ...prev, selectedSdk: sdk }));
};
const handleRegisterAgent = async (): Promise => {
setState((prev) => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch(`${apiUrl}/agents`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: state.agentName.trim() }),
});
if (!response.ok) {
const body = (await response.json()) as { message?: string };
throw new Error(body.message ?? `HTTP ${response.status}`);
}
const data = (await response.json()) as AgentCreateResponse;
setState((prev) => ({
...prev,
agentId: data.agentId,
loading: false,
error: null,
}));
} catch (err) {
setState((prev) => ({
...prev,
loading: false,
error: err instanceof Error ? err.message : 'Failed to register agent',
}));
}
};
const handleGenerateCredentials = async (): Promise => {
if (!state.agentId) return;
setState((prev) => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch(
`${apiUrl}/agents/${state.agentId}/credentials`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}
);
if (!response.ok) {
const body = (await response.json()) as { message?: string };
throw new Error(body.message ?? `HTTP ${response.status}`);
}
const data = (await response.json()) as CredentialsResponse;
setState((prev) => ({
...prev,
clientId: data.clientId,
clientSecret: data.clientSecret,
loading: false,
error: null,
}));
} catch (err) {
setState((prev) => ({
...prev,
loading: false,
error:
err instanceof Error
? err.message
: 'Failed to generate credentials',
}));
}
};
const stepLabels = [
'Account Setup',
'Register Agent',
'Generate Credentials',
'Choose SDK',
];
return (
{/* Step label row */}
{stepLabels.map((label, i) => (
{label}
))}
{state.step === 1 && (
goToStep(2)} />
)}
{state.step === 2 && (
void handleRegisterAgent()}
onNext={() => goToStep(3)}
/>
)}
{state.step === 3 && state.agentId && (
void handleGenerateCredentials()}
onNext={() => goToStep(4)}
/>
)}
{state.step === 4 &&
state.clientId &&
state.clientSecret && (
)}
);
}