# @sentryagent/idp-sdk

Node.js SDK for the [SentryAgent.ai AgentIdP](https://sentryagent.ai) — the open-source Identity Provider for AI agents.

Handles token acquisition and caching automatically. Covers all 14 AgentIdP API endpoints.

---

## Requirements

- Node.js 18 or later (uses native `fetch`)
- A running AgentIdP server
- A registered agent with a valid `clientId` and `clientSecret`

---

## Installation

```bash
npm install @sentryagent/idp-sdk
```

---

## Quick start

```typescript
import { AgentIdPClient } from '@sentryagent/idp-sdk';

const client = new AgentIdPClient({
  baseUrl: 'http://localhost:3000',
  clientId: 'your-agent-id',       // the agent's agentId (UUID)
  clientSecret: 'your-client-secret',
});

// List agents — token is acquired and cached automatically
const { data: agents } = await client.agents.listAgents();
console.log(agents);
```

---

## Configuration

```typescript
const client = new AgentIdPClient({
  baseUrl: 'http://localhost:3000',
  clientId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  clientSecret: 'your-client-secret',
  // Optional: restrict scopes. Defaults to all four scopes.
  scopes: ['agents:read', 'tokens:read'],
});
```

| Option | Required | Description |
|--------|----------|-------------|
| `baseUrl` | Yes | Base URL of the AgentIdP server |
| `clientId` | Yes | The agent's `agentId` (UUID) |
| `clientSecret` | Yes | The credential secret |
| `scopes` | No | OAuth 2.0 scopes to request. Defaults to all four. |

---

## Token management

The SDK fetches and caches access tokens automatically. A new token is requested when the cached token is within 60 seconds of expiry.

```typescript
// Force a fresh token on the next request (e.g. after rotating credentials)
client.clearTokenCache();
```

---

## Agent Registry

```typescript
// Register a new agent
const agent = await client.agents.registerAgent({
  email: 'classifier-v2@myorg.ai',
  agentType: 'classifier',
  version: '2.0.0',
  capabilities: ['text-classification', 'sentiment-analysis'],
  owner: 'platform-team',
  deploymentEnv: 'production',
});
console.log(agent.agentId); // UUID assigned by AgentIdP

// List agents
const { data, total } = await client.agents.listAgents({ status: 'active', limit: 20 });

// Get a single agent
const agent = await client.agents.getAgent('a1b2c3d4-...');

// Update an agent
const updated = await client.agents.updateAgent('a1b2c3d4-...', {
  version: '2.1.0',
  capabilities: ['text-classification', 'sentiment-analysis', 'intent-detection'],
});

// Decommission (irreversible)
await client.agents.decommissionAgent('a1b2c3d4-...');
```

---

## Credentials

```typescript
// Generate a credential — clientSecret shown once, store it securely
const cred = await client.credentials.generateCredential('a1b2c3d4-...');
console.log(cred.clientSecret); // only available here

// List credentials
const { data: creds } = await client.credentials.listCredentials('a1b2c3d4-...');

// Rotate — same credentialId, new secret, old secret immediately invalid
const rotated = await client.credentials.rotateCredential('a1b2c3d4-...', 'cred-uuid');
console.log(rotated.clientSecret); // new secret — store immediately

// Revoke
await client.credentials.revokeCredential('a1b2c3d4-...', 'cred-uuid');
```

---

## Token operations

```typescript
// Introspect — check whether a token is active
const result = await client.tokens.introspectToken(someToken);
if (result.active) {
  console.log('Token is valid, expires at', result.exp);
} else {
  console.log('Token is expired or revoked');
}

// Revoke — immediately invalidates the token
await client.tokens.revokeToken(someToken);
```

---

## Audit log

```typescript
// Query audit events
const { data: events } = await client.audit.queryAuditLog({
  agentId: 'a1b2c3d4-...',
  action: 'token.issued',
  outcome: 'success',
  fromDate: '2026-03-01T00:00:00Z',
  toDate: '2026-03-31T23:59:59Z',
  limit: 50,
});

// Get a single event
const event = await client.audit.getAuditEvent('event-uuid');
```

---

## Error handling

All API errors are thrown as `AgentIdPError`:

```typescript
import { AgentIdPClient, AgentIdPError } from '@sentryagent/idp-sdk';

try {
  await client.agents.getAgent('non-existent-id');
} catch (err) {
  if (err instanceof AgentIdPError) {
    console.error(err.code);       // e.g. 'AgentNotFoundError'
    console.error(err.httpStatus); // e.g. 404
    console.error(err.message);    // human-readable description
  }
}
```

---

## Available scopes

| Scope | What it allows |
|-------|----------------|
| `agents:read` | Read agent records |
| `agents:write` | Create, update, decommission agents |
| `tokens:read` | Introspect tokens |
| `audit:read` | Query audit logs |

---

## License

Apache 2.0 — see [LICENSE](../LICENSE) in the repository root.
