| 1 | import { describe, expect, it } from 'vitest';
|
| 2 | import { buildApi } from '../apps/api/src/app.js';
|
| 3 | import type { PublicAgentCard } from '../packages/protocol/src/index.js';
|
| 4 |
|
| 5 | describe('Agent Card onboarding API', () => {
|
| 6 | it('creates a private external profile and publishes it only after confirmation', async () => {
|
| 7 | const rows: { kind: string; recordId: string; payload: any }[] = [];
|
| 8 | const cards = new Map<string, PublicAgentCard>();
|
| 9 | const repository = {
|
| 10 | list: async () => rows,
|
| 11 | upsert: async (_operatorId: string, kind: string, recordId: string, payload: any) => {
|
| 12 | const current = rows.find((row) => row.kind === kind && row.recordId === recordId);
|
| 13 | if (current) current.payload = payload;
|
| 14 | else rows.push({ kind, recordId, payload });
|
| 15 | },
|
| 16 | publishAgent: async (_operatorId: string, card: PublicAgentCard) => {
|
| 17 | cards.set(card.agentId, card);
|
| 18 | return card;
|
| 19 | },
|
| 20 | unpublishAgent: async (_operatorId: string, agentId: string) => cards.delete(agentId),
|
| 21 | getPublishedAgent: async (agentId: string) => cards.get(agentId),
|
| 22 | } as any;
|
| 23 | const app = buildApi(undefined, undefined, repository);
|
| 24 | await app.ready();
|
| 25 | const headers = { 'x-openclasp-operator': 'owner-a' };
|
| 26 |
|
| 27 | const createdResponse = await app.inject({
|
| 28 | method: 'POST',
|
| 29 | url: '/v0.1/quickstart/agent',
|
| 30 | headers,
|
| 31 | payload: {
|
| 32 | agentName: 'Research assistant',
|
| 33 | projectName: 'My agents',
|
| 34 | description: 'Returns sourced comparisons',
|
| 35 | framework: 'LangGraph',
|
| 36 | capabilities: ['research'],
|
| 37 | },
|
| 38 | });
|
| 39 | expect(createdResponse.statusCode).toBe(200);
|
| 40 | const created = createdResponse.json();
|
| 41 | expect(created).not.toHaveProperty('card');
|
| 42 | expect(created.agent).toMatchObject({
|
| 43 | framework: 'LangGraph',
|
| 44 | agentMode: 'persistent_runtime',
|
| 45 | transport: 'direct_a2a',
|
| 46 | autoPublish: false,
|
| 47 | });
|
| 48 | expect(cards.size).toBe(0);
|
| 49 |
|
| 50 | const publicationResponse = await app.inject({
|
| 51 | method: 'POST',
|
| 52 | url: `/v0.1/agents/${created.agent.agentId}/publication`,
|
| 53 | headers,
|
| 54 | payload: { published: true },
|
| 55 | });
|
| 56 | expect(publicationResponse.statusCode).toBe(200);
|
| 57 | expect(publicationResponse.json()).toMatchObject({
|
| 58 | published: true,
|
| 59 | profileUrl: expect.stringContaining('/a/'),
|
| 60 | cardUrl: expect.stringContaining('/card.json'),
|
| 61 | verification: { status: 'verified', method: 'openclasp_oauth_account' },
|
| 62 | card: {
|
| 63 | agentMode: 'persistent_runtime',
|
| 64 | transports: [],
|
| 65 | assurance: 'oauth_authenticated',
|
| 66 | },
|
| 67 | });
|
| 68 | expect(
|
| 69 | rows.find((row) => row.kind === 'publication' && row.recordId === created.agent.agentId)
|
| 70 | ?.payload,
|
| 71 | ).toMatchObject({
|
| 72 | published: true,
|
| 73 | profileUrl: expect.stringContaining('/a/'),
|
| 74 | cardUrl: expect.stringContaining('/card.json'),
|
| 75 | });
|
| 76 | await app.close();
|
| 77 | });
|
| 78 | });
|