@posthog/agent 2.3.104 → 2.3.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -88,7 +88,8 @@ type LogLevel = "debug" | "info" | "warn" | "error";
88
88
  type OnLogCallback = (level: LogLevel, scope: string, message: string, data?: unknown) => void;
89
89
  interface PostHogAPIConfig {
90
90
  apiUrl: string;
91
- getApiKey: () => string;
91
+ getApiKey: () => string | Promise<string>;
92
+ refreshApiKey?: () => string | Promise<string>;
92
93
  projectId: number;
93
94
  userAgent?: string;
94
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.3.104",
3
+ "version": "2.3.110",
4
4
  "repository": "https://github.com/PostHog/code",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
package/src/agent.ts CHANGED
@@ -45,17 +45,17 @@ export class Agent {
45
45
  }
46
46
  }
47
47
 
48
- private _configureLlmGateway(overrideUrl?: string): {
48
+ private async _configureLlmGateway(overrideUrl?: string): Promise<{
49
49
  gatewayUrl: string;
50
50
  apiKey: string;
51
- } | null {
51
+ } | null> {
52
52
  if (!this.posthogAPI) {
53
53
  return null;
54
54
  }
55
55
 
56
56
  try {
57
57
  const gatewayUrl = overrideUrl ?? this.posthogAPI.getLlmGatewayUrl();
58
- const apiKey = this.posthogAPI.getApiKey();
58
+ const apiKey = await this.posthogAPI.getApiKey();
59
59
 
60
60
  process.env.OPENAI_BASE_URL = `${gatewayUrl}/v1`;
61
61
  process.env.OPENAI_API_KEY = apiKey;
@@ -74,7 +74,7 @@ export class Agent {
74
74
  taskRunId: string,
75
75
  options: TaskExecutionOptions = {},
76
76
  ): Promise<InProcessAcpConnection> {
77
- const gatewayConfig = this._configureLlmGateway(options.gatewayUrl);
77
+ const gatewayConfig = await this._configureLlmGateway(options.gatewayUrl);
78
78
  this.logger.info("Configured LLM gateway", {
79
79
  adapter: options.adapter,
80
80
  });
@@ -0,0 +1,48 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { PostHogAPIClient } from "./posthog-api";
3
+
4
+ const mockFetch = vi.fn();
5
+
6
+ vi.stubGlobal("fetch", mockFetch);
7
+
8
+ describe("PostHogAPIClient", () => {
9
+ beforeEach(() => {
10
+ vi.clearAllMocks();
11
+ });
12
+
13
+ it("refreshes once when fetching task run logs gets an auth failure", async () => {
14
+ const getApiKey = vi.fn().mockResolvedValue("stale-token");
15
+ const refreshApiKey = vi.fn().mockResolvedValue("fresh-token");
16
+ const client = new PostHogAPIClient({
17
+ apiUrl: "https://app.posthog.com",
18
+ getApiKey,
19
+ refreshApiKey,
20
+ projectId: 1,
21
+ });
22
+
23
+ mockFetch
24
+ .mockResolvedValueOnce({
25
+ ok: false,
26
+ status: 401,
27
+ statusText: "Unauthorized",
28
+ })
29
+ .mockResolvedValueOnce({
30
+ ok: true,
31
+ text: vi
32
+ .fn()
33
+ .mockResolvedValue(
34
+ `${JSON.stringify({ type: "notification", notification: { method: "foo" } })}\n`,
35
+ ),
36
+ });
37
+
38
+ const logs = await client.fetchTaskRunLogs({
39
+ id: "run-1",
40
+ task: "task-1",
41
+ } as never);
42
+
43
+ expect(logs).toHaveLength(1);
44
+ expect(getApiKey).toHaveBeenCalledTimes(1);
45
+ expect(refreshApiKey).toHaveBeenCalledTimes(1);
46
+ expect(mockFetch).toHaveBeenCalledTimes(2);
47
+ });
48
+ });
@@ -47,27 +47,63 @@ export class PostHogAPIClient {
47
47
  return host;
48
48
  }
49
49
 
50
- private get headers(): Record<string, string> {
51
- return {
52
- Authorization: `Bearer ${this.config.getApiKey()}`,
53
- "Content-Type": "application/json",
54
- "User-Agent": this.config.userAgent ?? DEFAULT_USER_AGENT,
55
- };
50
+ private isAuthFailure(status: number): boolean {
51
+ return status === 401 || status === 403;
56
52
  }
57
53
 
58
- private async apiRequest<T>(
54
+ private async resolveApiKey(forceRefresh = false): Promise<string> {
55
+ if (forceRefresh && this.config.refreshApiKey) {
56
+ return this.config.refreshApiKey();
57
+ }
58
+
59
+ return this.config.getApiKey();
60
+ }
61
+
62
+ private async buildHeaders(
63
+ options: RequestInit,
64
+ forceRefresh = false,
65
+ ): Promise<Headers> {
66
+ const headers = new Headers(options.headers);
67
+ headers.set(
68
+ "Authorization",
69
+ `Bearer ${await this.resolveApiKey(forceRefresh)}`,
70
+ );
71
+ headers.set("Content-Type", "application/json");
72
+ headers.set("User-Agent", this.config.userAgent ?? DEFAULT_USER_AGENT);
73
+ return headers;
74
+ }
75
+
76
+ private async performRequest(
59
77
  endpoint: string,
60
- options: RequestInit = {},
61
- ): Promise<T> {
78
+ options: RequestInit,
79
+ forceRefresh = false,
80
+ ): Promise<Response> {
62
81
  const url = `${this.baseUrl}${endpoint}`;
63
82
 
64
- const response = await fetch(url, {
83
+ return fetch(url, {
65
84
  ...options,
66
- headers: {
67
- ...this.headers,
68
- ...options.headers,
69
- },
85
+ headers: await this.buildHeaders(options, forceRefresh),
70
86
  });
87
+ }
88
+
89
+ private async performRequestWithRetry(
90
+ endpoint: string,
91
+ options: RequestInit = {},
92
+ ): Promise<Response> {
93
+ let response = await this.performRequest(endpoint, options);
94
+
95
+ if (!response.ok && this.isAuthFailure(response.status)) {
96
+ response = await this.performRequest(endpoint, options, true);
97
+ }
98
+
99
+ return response;
100
+ }
101
+
102
+ private async apiRequest<T>(
103
+ endpoint: string,
104
+ options: RequestInit = {},
105
+ ): Promise<T> {
106
+ const response = await this.performRequestWithRetry(endpoint, options);
71
107
 
72
108
  if (!response.ok) {
73
109
  let errorMessage: string;
@@ -87,8 +123,8 @@ export class PostHogAPIClient {
87
123
  return this.config.projectId;
88
124
  }
89
125
 
90
- getApiKey(): string {
91
- return this.config.getApiKey();
126
+ async getApiKey(forceRefresh = false): Promise<string> {
127
+ return this.resolveApiKey(forceRefresh);
92
128
  }
93
129
 
94
130
  getLlmGatewayUrl(): string {
@@ -228,12 +264,10 @@ export class PostHogAPIClient {
228
264
  */
229
265
  async fetchTaskRunLogs(taskRun: TaskRun): Promise<StoredEntry[]> {
230
266
  const teamId = this.getTeamId();
267
+ const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;
231
268
 
232
269
  try {
233
- const response = await fetch(
234
- `${this.baseUrl}/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`,
235
- { headers: this.headers },
236
- );
270
+ const response = await this.performRequestWithRetry(endpoint);
237
271
 
238
272
  if (!response.ok) {
239
273
  if (response.status === 404) {
package/src/types.ts CHANGED
@@ -126,7 +126,8 @@ export type OnLogCallback = (
126
126
 
127
127
  export interface PostHogAPIConfig {
128
128
  apiUrl: string;
129
- getApiKey: () => string;
129
+ getApiKey: () => string | Promise<string>;
130
+ refreshApiKey?: () => string | Promise<string>;
130
131
  projectId: number;
131
132
  userAgent?: string;
132
133
  }