@posthog/agent 1.24.0 → 1.24.2
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/LICENSE +33 -0
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +111 -156
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.js.map +1 -1
- package/dist/src/adapters/types.d.ts +1 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +7 -7
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +85 -143
- package/dist/src/agent.js.map +1 -1
- package/dist/src/agents/execution.js.map +1 -1
- package/dist/src/agents/planning.js.map +1 -1
- package/dist/src/agents/research.js.map +1 -1
- package/dist/src/file-manager.d.ts +4 -4
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +58 -59
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -1
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +70 -87
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +3 -2
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +22 -22
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/prompt-builder.d.ts +3 -3
- package/dist/src/prompt-builder.d.ts.map +1 -1
- package/dist/src/prompt-builder.js +93 -123
- package/dist/src/prompt-builder.js.map +1 -1
- package/dist/src/task-manager.d.ts +4 -4
- package/dist/src/task-manager.d.ts.map +1 -1
- package/dist/src/task-manager.js +18 -19
- package/dist/src/task-manager.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +4 -3
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +54 -59
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/template-manager.d.ts +1 -1
- package/dist/src/template-manager.d.ts.map +1 -1
- package/dist/src/template-manager.js +28 -30
- package/dist/src/template-manager.js.map +1 -1
- package/dist/src/todo-manager.d.ts +3 -3
- package/dist/src/todo-manager.d.ts.map +1 -1
- package/dist/src/todo-manager.js +24 -29
- package/dist/src/todo-manager.js.map +1 -1
- package/dist/src/tools/registry.d.ts +1 -1
- package/dist/src/tools/registry.js +60 -60
- package/dist/src/tools/registry.js.map +1 -1
- package/dist/src/tools/types.d.ts +31 -31
- package/dist/src/types.d.ts +33 -33
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/logger.d.ts +4 -4
- package/dist/src/utils/logger.d.ts.map +1 -1
- package/dist/src/utils/logger.js +8 -8
- package/dist/src/utils/logger.js.map +1 -1
- package/dist/src/workflow/config.d.ts +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +18 -18
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.d.ts +1 -1
- package/dist/src/workflow/steps/build.d.ts.map +1 -1
- package/dist/src/workflow/steps/build.js +38 -46
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +1 -1
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -1
- package/dist/src/workflow/steps/finalize.js +48 -54
- package/dist/src/workflow/steps/finalize.js.map +1 -1
- package/dist/src/workflow/steps/plan.d.ts +1 -1
- package/dist/src/workflow/steps/plan.d.ts.map +1 -1
- package/dist/src/workflow/steps/plan.js +46 -58
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.d.ts +1 -1
- package/dist/src/workflow/steps/research.d.ts.map +1 -1
- package/dist/src/workflow/steps/research.js +56 -68
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/dist/src/workflow/types.d.ts +12 -12
- package/dist/src/workflow/types.d.ts.map +1 -1
- package/dist/src/workflow/utils.d.ts +1 -1
- package/dist/src/workflow/utils.d.ts.map +1 -1
- package/dist/src/workflow/utils.js +4 -7
- package/dist/src/workflow/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/adapters/claude/claude-adapter.ts +168 -220
- package/src/adapters/claude/tool-mapper.ts +2 -2
- package/src/adapters/types.ts +1 -1
- package/src/agent.ts +444 -579
- package/src/agents/execution.ts +1 -1
- package/src/agents/planning.ts +1 -1
- package/src/agents/research.ts +1 -0
- package/src/file-manager.ts +63 -64
- package/src/git-manager.ts +88 -144
- package/src/posthog-api.ts +82 -122
- package/src/prompt-builder.ts +135 -180
- package/src/task-manager.ts +30 -38
- package/src/task-progress-reporter.ts +59 -70
- package/src/template-manager.ts +45 -98
- package/src/todo-manager.ts +30 -35
- package/src/tools/registry.ts +62 -62
- package/src/tools/types.ts +36 -36
- package/src/types.ts +71 -93
- package/src/utils/logger.ts +56 -62
- package/src/workflow/config.ts +48 -48
- package/src/workflow/steps/build.ts +113 -122
- package/src/workflow/steps/finalize.ts +182 -214
- package/src/workflow/steps/plan.ts +131 -151
- package/src/workflow/steps/research.ts +186 -205
- package/src/workflow/types.ts +36 -38
- package/src/workflow/utils.ts +34 -37
package/src/posthog-api.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Task,
|
|
3
|
+
TaskRun,
|
|
2
4
|
LogEntry,
|
|
5
|
+
SupportingFile,
|
|
3
6
|
PostHogAPIConfig,
|
|
4
7
|
PostHogResource,
|
|
5
|
-
|
|
6
|
-
TaskArtifactUploadPayload,
|
|
7
|
-
TaskRun,
|
|
8
|
-
TaskRunArtifact,
|
|
8
|
+
ResourceType,
|
|
9
9
|
UrlMention,
|
|
10
|
-
|
|
10
|
+
TaskRunArtifact,
|
|
11
|
+
TaskArtifactUploadPayload,
|
|
12
|
+
} from './types.js';
|
|
11
13
|
|
|
12
14
|
interface PostHogApiResponse<T> {
|
|
13
15
|
results?: T[];
|
|
@@ -27,31 +29,32 @@ export interface TaskRunUpdate {
|
|
|
27
29
|
|
|
28
30
|
export class PostHogAPIClient {
|
|
29
31
|
private config: PostHogAPIConfig;
|
|
32
|
+
private _teamId: number | null = null;
|
|
30
33
|
|
|
31
34
|
constructor(config: PostHogAPIConfig) {
|
|
32
35
|
this.config = config;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
private get baseUrl(): string {
|
|
36
|
-
const host = this.config.apiUrl.endsWith("/")
|
|
37
|
-
? this.config.apiUrl.slice(0, -1)
|
|
39
|
+
const host = this.config.apiUrl.endsWith("/")
|
|
40
|
+
? this.config.apiUrl.slice(0, -1)
|
|
38
41
|
: this.config.apiUrl;
|
|
39
42
|
return host;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
private get headers(): Record<string, string> {
|
|
43
46
|
return {
|
|
44
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
45
|
-
|
|
47
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
private async apiRequest<T>(
|
|
50
|
-
endpoint: string,
|
|
51
|
-
options: RequestInit = {}
|
|
53
|
+
endpoint: string,
|
|
54
|
+
options: RequestInit = {}
|
|
52
55
|
): Promise<T> {
|
|
53
56
|
const url = `${this.baseUrl}${endpoint}`;
|
|
54
|
-
|
|
57
|
+
|
|
55
58
|
const response = await fetch(url, {
|
|
56
59
|
...options,
|
|
57
60
|
headers: {
|
|
@@ -103,7 +106,7 @@ export class PostHogAPIClient {
|
|
|
103
106
|
}): Promise<Task[]> {
|
|
104
107
|
const teamId = this.getTeamId();
|
|
105
108
|
const url = new URL(`${this.baseUrl}/api/projects/${teamId}/tasks/`);
|
|
106
|
-
|
|
109
|
+
|
|
107
110
|
if (filters) {
|
|
108
111
|
Object.entries(filters).forEach(([key, value]) => {
|
|
109
112
|
if (value) url.searchParams.append(key, value);
|
|
@@ -111,16 +114,16 @@ export class PostHogAPIClient {
|
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
const response = await this.apiRequest<PostHogApiResponse<Task>>(
|
|
114
|
-
url.pathname + url.search
|
|
117
|
+
url.pathname + url.search
|
|
115
118
|
);
|
|
116
|
-
|
|
119
|
+
|
|
117
120
|
return response.results || [];
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
|
|
121
124
|
const teamId = this.getTeamId();
|
|
122
125
|
return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/`, {
|
|
123
|
-
method:
|
|
126
|
+
method: 'PATCH',
|
|
124
127
|
body: JSON.stringify(updates),
|
|
125
128
|
});
|
|
126
129
|
}
|
|
@@ -129,86 +132,59 @@ export class PostHogAPIClient {
|
|
|
129
132
|
async listTaskRuns(taskId: string): Promise<TaskRun[]> {
|
|
130
133
|
const teamId = this.getTeamId();
|
|
131
134
|
const response = await this.apiRequest<PostHogApiResponse<TaskRun>>(
|
|
132
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs
|
|
135
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/`
|
|
133
136
|
);
|
|
134
137
|
return response.results || [];
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
async getTaskRun(taskId: string, runId: string): Promise<TaskRun> {
|
|
138
141
|
const teamId = this.getTeamId();
|
|
139
|
-
return this.apiRequest<TaskRun>(
|
|
140
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,
|
|
141
|
-
);
|
|
142
|
+
return this.apiRequest<TaskRun>(`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
async createTaskRun(
|
|
145
146
|
taskId: string,
|
|
146
|
-
payload?: Partial<
|
|
147
|
-
Omit<
|
|
148
|
-
TaskRun,
|
|
149
|
-
"id" | "task" | "team" | "created_at" | "updated_at" | "completed_at"
|
|
150
|
-
>
|
|
151
|
-
>,
|
|
147
|
+
payload?: Partial<Omit<TaskRun, 'id' | 'task' | 'team' | 'created_at' | 'updated_at' | 'completed_at'>>
|
|
152
148
|
): Promise<TaskRun> {
|
|
153
149
|
const teamId = this.getTeamId();
|
|
154
|
-
return this.apiRequest<TaskRun>(
|
|
155
|
-
|
|
156
|
-
{
|
|
157
|
-
|
|
158
|
-
body: JSON.stringify(payload || {}),
|
|
159
|
-
},
|
|
160
|
-
);
|
|
150
|
+
return this.apiRequest<TaskRun>(`/api/projects/${teamId}/tasks/${taskId}/runs/`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: JSON.stringify(payload || {}),
|
|
153
|
+
});
|
|
161
154
|
}
|
|
162
155
|
|
|
163
156
|
async updateTaskRun(
|
|
164
157
|
taskId: string,
|
|
165
158
|
runId: string,
|
|
166
|
-
payload: TaskRunUpdate
|
|
159
|
+
payload: TaskRunUpdate
|
|
167
160
|
): Promise<TaskRun> {
|
|
168
161
|
const teamId = this.getTeamId();
|
|
169
|
-
return this.apiRequest<TaskRun>(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
body: JSON.stringify(payload),
|
|
174
|
-
},
|
|
175
|
-
);
|
|
162
|
+
return this.apiRequest<TaskRun>(`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`, {
|
|
163
|
+
method: "PATCH",
|
|
164
|
+
body: JSON.stringify(payload),
|
|
165
|
+
});
|
|
176
166
|
}
|
|
177
167
|
|
|
178
|
-
async setTaskRunOutput(
|
|
179
|
-
taskId: string,
|
|
180
|
-
runId: string,
|
|
181
|
-
output: Record<string, unknown>,
|
|
182
|
-
): Promise<TaskRun> {
|
|
168
|
+
async setTaskRunOutput(taskId: string, runId: string, output: Record<string, unknown>): Promise<TaskRun> {
|
|
183
169
|
const teamId = this.getTeamId();
|
|
184
|
-
return this.apiRequest<TaskRun>(
|
|
185
|
-
|
|
186
|
-
{
|
|
187
|
-
|
|
188
|
-
body: JSON.stringify({ output }),
|
|
189
|
-
},
|
|
190
|
-
);
|
|
170
|
+
return this.apiRequest<TaskRun>(`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/set_output/`, {
|
|
171
|
+
method: 'PATCH',
|
|
172
|
+
body: JSON.stringify({ output }),
|
|
173
|
+
});
|
|
191
174
|
}
|
|
192
175
|
|
|
193
|
-
async appendTaskRunLog(
|
|
194
|
-
taskId: string,
|
|
195
|
-
runId: string,
|
|
196
|
-
entries: LogEntry[],
|
|
197
|
-
): Promise<TaskRun> {
|
|
176
|
+
async appendTaskRunLog(taskId: string, runId: string, entries: LogEntry[]): Promise<TaskRun> {
|
|
198
177
|
const teamId = this.getTeamId();
|
|
199
|
-
return this.apiRequest<TaskRun>(
|
|
200
|
-
|
|
201
|
-
{
|
|
202
|
-
|
|
203
|
-
body: JSON.stringify({ entries }),
|
|
204
|
-
},
|
|
205
|
-
);
|
|
178
|
+
return this.apiRequest<TaskRun>(`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
body: JSON.stringify({ entries }),
|
|
181
|
+
});
|
|
206
182
|
}
|
|
207
183
|
|
|
208
184
|
async uploadTaskArtifacts(
|
|
209
185
|
taskId: string,
|
|
210
186
|
runId: string,
|
|
211
|
-
artifacts: TaskArtifactUploadPayload[]
|
|
187
|
+
artifacts: TaskArtifactUploadPayload[]
|
|
212
188
|
): Promise<TaskRunArtifact[]> {
|
|
213
189
|
if (!artifacts.length) {
|
|
214
190
|
return [];
|
|
@@ -218,9 +194,9 @@ export class PostHogAPIClient {
|
|
|
218
194
|
const response = await this.apiRequest<{ artifacts: TaskRunArtifact[] }>(
|
|
219
195
|
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
220
196
|
{
|
|
221
|
-
method:
|
|
197
|
+
method: 'POST',
|
|
222
198
|
body: JSON.stringify({ artifacts }),
|
|
223
|
-
}
|
|
199
|
+
}
|
|
224
200
|
);
|
|
225
201
|
|
|
226
202
|
return response.artifacts ?? [];
|
|
@@ -238,15 +214,13 @@ export class PostHogAPIClient {
|
|
|
238
214
|
|
|
239
215
|
try {
|
|
240
216
|
const response = await fetch(taskRun.log_url);
|
|
241
|
-
|
|
217
|
+
|
|
242
218
|
if (!response.ok) {
|
|
243
|
-
throw new Error(
|
|
244
|
-
`Failed to fetch logs: ${response.status} ${response.statusText}`,
|
|
245
|
-
);
|
|
219
|
+
throw new Error(`Failed to fetch logs: ${response.status} ${response.statusText}`);
|
|
246
220
|
}
|
|
247
221
|
|
|
248
222
|
const content = await response.text();
|
|
249
|
-
|
|
223
|
+
|
|
250
224
|
if (!content.trim()) {
|
|
251
225
|
return [];
|
|
252
226
|
}
|
|
@@ -254,37 +228,30 @@ export class PostHogAPIClient {
|
|
|
254
228
|
// Parse newline-delimited JSON
|
|
255
229
|
return content
|
|
256
230
|
.trim()
|
|
257
|
-
.split(
|
|
258
|
-
.map(
|
|
231
|
+
.split('\n')
|
|
232
|
+
.map(line => JSON.parse(line) as LogEntry);
|
|
259
233
|
} catch (error) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`,
|
|
262
|
-
);
|
|
234
|
+
throw new Error(`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`);
|
|
263
235
|
}
|
|
264
236
|
}
|
|
265
237
|
|
|
266
238
|
/**
|
|
267
239
|
* Fetch error details from PostHog error tracking
|
|
268
240
|
*/
|
|
269
|
-
async fetchErrorDetails(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
): Promise<PostHogResource> {
|
|
273
|
-
const teamId = projectId ? parseInt(projectId, 10) : this.getTeamId();
|
|
274
|
-
|
|
241
|
+
async fetchErrorDetails(errorId: string, projectId?: string): Promise<PostHogResource> {
|
|
242
|
+
const teamId = projectId ? parseInt(projectId) : this.getTeamId();
|
|
243
|
+
|
|
275
244
|
try {
|
|
276
|
-
const errorData = await this.apiRequest<any>(
|
|
277
|
-
|
|
278
|
-
);
|
|
279
|
-
|
|
245
|
+
const errorData = await this.apiRequest<any>(`/api/projects/${teamId}/error_tracking/${errorId}/`);
|
|
246
|
+
|
|
280
247
|
// Format error details for agent consumption
|
|
281
248
|
const content = this.formatErrorContent(errorData);
|
|
282
|
-
|
|
249
|
+
|
|
283
250
|
return {
|
|
284
|
-
type:
|
|
251
|
+
type: 'error',
|
|
285
252
|
id: errorId,
|
|
286
253
|
url: `${this.baseUrl}/project/${teamId}/error_tracking/${errorId}`,
|
|
287
|
-
title: errorData.exception_type ||
|
|
254
|
+
title: errorData.exception_type || 'Unknown Error',
|
|
288
255
|
content,
|
|
289
256
|
metadata: {
|
|
290
257
|
exception_type: errorData.exception_type,
|
|
@@ -304,9 +271,9 @@ export class PostHogAPIClient {
|
|
|
304
271
|
*/
|
|
305
272
|
async fetchResourceByUrl(urlMention: UrlMention): Promise<PostHogResource> {
|
|
306
273
|
switch (urlMention.type) {
|
|
307
|
-
case
|
|
274
|
+
case 'error':
|
|
308
275
|
if (!urlMention.id) {
|
|
309
|
-
throw new Error(
|
|
276
|
+
throw new Error('Error ID is required for error resources');
|
|
310
277
|
}
|
|
311
278
|
// Extract project ID from URL if available, otherwise use default team
|
|
312
279
|
let projectId: string | undefined;
|
|
@@ -315,26 +282,23 @@ export class PostHogAPIClient {
|
|
|
315
282
|
projectId = projectIdMatch ? projectIdMatch[1] : undefined;
|
|
316
283
|
}
|
|
317
284
|
return this.fetchErrorDetails(urlMention.id, projectId);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
case
|
|
321
|
-
case
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
case "generic":
|
|
285
|
+
|
|
286
|
+
case 'experiment':
|
|
287
|
+
case 'insight':
|
|
288
|
+
case 'feature_flag':
|
|
289
|
+
throw new Error(`Resource type '${urlMention.type}' not yet implemented`);
|
|
290
|
+
|
|
291
|
+
case 'generic':
|
|
328
292
|
// Return a minimal resource for generic URLs
|
|
329
293
|
return {
|
|
330
|
-
type:
|
|
331
|
-
id:
|
|
294
|
+
type: 'generic',
|
|
295
|
+
id: '',
|
|
332
296
|
url: urlMention.url,
|
|
333
|
-
title:
|
|
297
|
+
title: 'Generic Resource',
|
|
334
298
|
content: `Generic resource: ${urlMention.url}`,
|
|
335
299
|
metadata: {},
|
|
336
300
|
};
|
|
337
|
-
|
|
301
|
+
|
|
338
302
|
default:
|
|
339
303
|
throw new Error(`Unknown resource type: ${urlMention.type}`);
|
|
340
304
|
}
|
|
@@ -345,40 +309,36 @@ export class PostHogAPIClient {
|
|
|
345
309
|
*/
|
|
346
310
|
private formatErrorContent(errorData: any): string {
|
|
347
311
|
const sections = [];
|
|
348
|
-
|
|
312
|
+
|
|
349
313
|
if (errorData.exception_type) {
|
|
350
314
|
sections.push(`**Error Type**: ${errorData.exception_type}`);
|
|
351
315
|
}
|
|
352
|
-
|
|
316
|
+
|
|
353
317
|
if (errorData.exception_message) {
|
|
354
318
|
sections.push(`**Message**: ${errorData.exception_message}`);
|
|
355
319
|
}
|
|
356
|
-
|
|
320
|
+
|
|
357
321
|
if (errorData.stack_trace) {
|
|
358
|
-
sections.push(
|
|
359
|
-
`**Stack Trace**:\n\`\`\`\n${errorData.stack_trace}\n\`\`\``,
|
|
360
|
-
);
|
|
322
|
+
sections.push(`**Stack Trace**:\n\`\`\`\n${errorData.stack_trace}\n\`\`\``);
|
|
361
323
|
}
|
|
362
|
-
|
|
324
|
+
|
|
363
325
|
if (errorData.volume) {
|
|
364
326
|
sections.push(`**Volume**: ${errorData.volume} occurrences`);
|
|
365
327
|
}
|
|
366
|
-
|
|
328
|
+
|
|
367
329
|
if (errorData.users_affected) {
|
|
368
330
|
sections.push(`**Users Affected**: ${errorData.users_affected}`);
|
|
369
331
|
}
|
|
370
|
-
|
|
332
|
+
|
|
371
333
|
if (errorData.first_seen && errorData.last_seen) {
|
|
372
334
|
sections.push(`**First Seen**: ${errorData.first_seen}`);
|
|
373
335
|
sections.push(`**Last Seen**: ${errorData.last_seen}`);
|
|
374
336
|
}
|
|
375
|
-
|
|
337
|
+
|
|
376
338
|
if (errorData.properties && Object.keys(errorData.properties).length > 0) {
|
|
377
|
-
sections.push(
|
|
378
|
-
`**Properties**: ${JSON.stringify(errorData.properties, null, 2)}`,
|
|
379
|
-
);
|
|
339
|
+
sections.push(`**Properties**: ${JSON.stringify(errorData.properties, null, 2)}`);
|
|
380
340
|
}
|
|
381
|
-
|
|
382
|
-
return sections.join(
|
|
341
|
+
|
|
342
|
+
return sections.join('\n\n');
|
|
383
343
|
}
|
|
384
344
|
}
|