Skip to content

Commit 8e9eeed

Browse files
authored
feat: agents apis (#375)
* feat: add getCardUrl API to agents client * feat: add getAuthHeaders api * fix: integration tests * chore: address pr comments * chore: tsdoc comments
1 parent 6cc20db commit 8e9eeed

4 files changed

Lines changed: 186 additions & 0 deletions

File tree

src/custom/CortiClient.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { CortiClient as BaseCortiClient } from "../Client.js";
2+
import * as core from "../core/index.js";
23
import type * as environments from "../environments.js";
4+
import { CustomAgents } from "./agents/CustomAgents.js";
35
import { CortiAuth } from "./auth/CortiAuth.js";
46
import { CustomStream } from "./stream/CustomStream.js";
57
import { CustomTranscribe } from "./transcribe/CustomTranscribe.js";
68
import { authToBaseOptions } from "./utils/authToBaseOptions.js";
79
import { type Environment, getEnvironment } from "./utils/environment.js";
10+
811
import { resolveClientOptions } from "./utils/resolveClientOptions.js";
912
import { setDefaultWithCredentials } from "./utils/withCredentialsConfig.js";
1013

@@ -41,6 +44,7 @@ export class CortiClient extends BaseCortiClient {
4144
protected override _auth: CortiAuth | undefined;
4245
protected override _stream: CustomStream | undefined;
4346
protected override _transcribe: CustomTranscribe | undefined;
47+
protected override _agents: CustomAgents | undefined;
4448

4549
private readonly _encodeHeadersAsWsProtocols: boolean | undefined;
4650

@@ -82,4 +86,33 @@ export class CortiClient extends BaseCortiClient {
8286
encodeHeadersAsWsProtocols: this._encodeHeadersAsWsProtocols,
8387
}));
8488
}
89+
90+
public override get agents(): CustomAgents {
91+
return (this._agents ??= new CustomAgents(this._options));
92+
}
93+
94+
/**
95+
* Retrieves authentication headers for API requests.
96+
*
97+
* This method returns a Headers object containing the Authorization header with a valid
98+
* bearer token and the Tenant-Name header. The token is automatically refreshed if needed.
99+
*
100+
* @returns A Promise that resolves to a Headers object with Authorization and Tenant-Name headers
101+
*
102+
* @example
103+
* ```typescript
104+
* const client = new CortiClient({ ... });
105+
* const headers = await client.getAuthHeaders();
106+
* console.log(headers.get("Authorization")); // "Bearer ..."
107+
* console.log(headers.get("Tenant-Name")); // "your-tenant"
108+
* ```
109+
*/
110+
public getAuthHeaders = async (): Promise<Headers> => {
111+
const req = await this._options.authProvider.getAuthRequest();
112+
113+
return new Headers({
114+
...(req.headers ?? {}),
115+
"Tenant-Name": await core.Supplier.get(this._options.tenantName),
116+
});
117+
};
85118
}

src/custom/agents/CustomAgents.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* This file is the custom implementation of the Agents client (src/api/resources/agents/client/Client.ts)
3+
*
4+
* It extends the auto-generated Agents class and adds custom helper methods.
5+
*
6+
* All the patches marked with `// Patch: ...` comments.
7+
*/
8+
9+
import { AgentsClient } from "../../api/resources/agents/client/Client.js";
10+
import * as core from "../../core/index.js";
11+
12+
export class CustomAgents extends AgentsClient {
13+
/**
14+
* Returns the URL for the agent card JSON file.
15+
*
16+
* @param {string} agentId - The ID of the agent
17+
* @returns {Promise<URL>} A Promise that resolves to the URL for the agent card
18+
*
19+
* @example
20+
* const url = await client.agents.getAgentCardUrl("agent-123");
21+
*/
22+
public getCardUrl = async (agentId: string): Promise<URL> => {
23+
const encodedAgentId = encodeURIComponent(agentId);
24+
return new URL(
25+
`/agents/${encodedAgentId}/agent-card.json`,
26+
(await core.Supplier.get(this._options.environment)).agents,
27+
);
28+
};
29+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { CortiClient } from "../../src";
2+
import { createTestCortiClient, setupConsoleWarnSpy } from "./testUtils";
3+
4+
describe("cortiClient.agents.getCardUrl", () => {
5+
let cortiClient: CortiClient;
6+
let consoleWarnSpy: ReturnType<typeof setupConsoleWarnSpy>;
7+
8+
beforeAll(() => {
9+
cortiClient = createTestCortiClient();
10+
});
11+
12+
beforeEach(() => {
13+
consoleWarnSpy = setupConsoleWarnSpy();
14+
});
15+
16+
afterEach(() => {
17+
consoleWarnSpy.mockRestore();
18+
});
19+
20+
describe("should return correct URL for agent card", () => {
21+
it("should return a valid URL instance without errors or warnings", async () => {
22+
expect.assertions(4);
23+
24+
const agentId = "test-agent-123";
25+
26+
const url = await cortiClient.agents.getCardUrl(agentId);
27+
28+
expect(url).toBeInstanceOf(URL);
29+
expect(url.toString()).toContain(`/agents/${agentId}/agent-card.json`);
30+
expect(url.toString()).toContain(process.env.CORTI_ENVIRONMENT);
31+
expect(consoleWarnSpy).not.toHaveBeenCalled();
32+
});
33+
34+
it("should handle different agent IDs correctly", async () => {
35+
expect.assertions(7);
36+
37+
const agentIds = ["agent-1", "550e8400-e29b-41d4-a716-446655440000", "my-custom-agent"];
38+
39+
for (const agentId of agentIds) {
40+
const url = await cortiClient.agents.getCardUrl(agentId);
41+
42+
expect(url).toBeInstanceOf(URL);
43+
expect(url.toString()).toContain(`/agents/${agentId}/agent-card.json`);
44+
}
45+
46+
expect(consoleWarnSpy).not.toHaveBeenCalled();
47+
});
48+
});
49+
50+
describe("URL structure", () => {
51+
it("should return URL with correct path structure", async () => {
52+
expect.assertions(3);
53+
54+
const agentId = "test-agent";
55+
56+
const url = await cortiClient.agents.getCardUrl(agentId);
57+
58+
expect(url).toBeInstanceOf(URL);
59+
expect(url.pathname).toBe(`/agents/${agentId}/agent-card.json`);
60+
expect(consoleWarnSpy).not.toHaveBeenCalled();
61+
});
62+
});
63+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import type { CortiClient } from "../../src";
2+
import { createTestCortiClient, setupConsoleWarnSpy } from "./testUtils";
3+
4+
// Type extension to include getAuthHeaders method
5+
type CortiClientWithAuth = CortiClient & {
6+
getAuthHeaders(): Promise<Headers>;
7+
};
8+
9+
describe("cortiClient.getAuthHeaders", () => {
10+
let cortiClient: CortiClientWithAuth;
11+
let consoleWarnSpy: ReturnType<typeof setupConsoleWarnSpy>;
12+
13+
beforeAll(() => {
14+
cortiClient = createTestCortiClient() as CortiClientWithAuth;
15+
});
16+
17+
beforeEach(() => {
18+
consoleWarnSpy = setupConsoleWarnSpy();
19+
});
20+
21+
afterEach(() => {
22+
consoleWarnSpy.mockRestore();
23+
});
24+
25+
describe("should return headers with authentication", () => {
26+
it("should return headers with Bearer token and tenant name without errors or warnings", async () => {
27+
expect.assertions(4);
28+
29+
const headers = await cortiClient.getAuthHeaders();
30+
31+
expect(headers).toBeInstanceOf(Headers);
32+
expect(headers.get("Authorization")).toBeTruthy();
33+
expect(headers.get("Tenant-Name")).toBe(process.env.CORTI_TENANT_NAME);
34+
expect(consoleWarnSpy).not.toHaveBeenCalled();
35+
});
36+
});
37+
38+
describe("return value structure", () => {
39+
it("should return a Headers instance with correct structure", async () => {
40+
expect.assertions(4);
41+
42+
const headers = await cortiClient.getAuthHeaders();
43+
44+
expect(headers).toBeInstanceOf(Headers);
45+
const headerKeys = Array.from(headers.keys());
46+
expect(headerKeys).toHaveLength(2);
47+
expect(headerKeys).toContain("authorization");
48+
expect(headerKeys).toContain("tenant-name");
49+
});
50+
51+
it("should return Bearer token in Authorization header", async () => {
52+
expect.assertions(2);
53+
54+
const headers = await cortiClient.getAuthHeaders();
55+
56+
const authHeader = headers.get("Authorization");
57+
expect(authHeader).toBeTruthy();
58+
expect(authHeader?.startsWith("Bearer ")).toBe(true);
59+
});
60+
});
61+
});

0 commit comments

Comments
 (0)