@forgehive/hive-sdk 0.1.6 → 0.2.0
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/README.md +318 -528
- package/dist/index.d.ts +0 -2
- package/dist/index.js +10 -66
- package/dist/test/metadata.test.js +123 -171
- package/dist/test/sendLog.test.js +199 -117
- package/package.json +1 -1
- package/src/index.ts +11 -78
- package/src/test/metadata.test.ts +143 -218
- package/src/test/sendLog.test.ts +237 -145
- package/src/test/sendLogByUuid.test.ts +0 -272
|
@@ -4,19 +4,56 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
8
|
const index_1 = require("../index");
|
|
8
|
-
// Mock axios
|
|
9
|
+
// Mock axios and fs
|
|
9
10
|
jest.mock('axios');
|
|
11
|
+
jest.mock('fs');
|
|
10
12
|
const mockedAxios = axios_1.default;
|
|
13
|
+
const mockedFs = fs_1.default;
|
|
11
14
|
describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
12
15
|
let client;
|
|
13
16
|
const testConfig = {
|
|
14
17
|
projectName: 'test-project',
|
|
18
|
+
projectUuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
15
19
|
apiKey: 'test-api-key',
|
|
16
20
|
apiSecret: 'test-api-secret',
|
|
17
|
-
host: 'https://test-host.com'
|
|
21
|
+
host: 'https://test-host.com',
|
|
22
|
+
forgeConfigPath: './forge.json'
|
|
23
|
+
};
|
|
24
|
+
const mockForgeConfig = {
|
|
25
|
+
project: {
|
|
26
|
+
name: 'test-project',
|
|
27
|
+
uuid: '550e8400-e29b-41d4-a716-446655440000'
|
|
28
|
+
},
|
|
29
|
+
tasks: {
|
|
30
|
+
'test-task': {
|
|
31
|
+
path: 'src/tasks/test.ts',
|
|
32
|
+
handler: 'testTask',
|
|
33
|
+
uuid: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
|
|
34
|
+
},
|
|
35
|
+
'complex-task': {
|
|
36
|
+
path: 'src/tasks/complex.ts',
|
|
37
|
+
handler: 'complexTask',
|
|
38
|
+
uuid: 'a45aafe3-8b01-4b58-b15d-9a96274858ee'
|
|
39
|
+
},
|
|
40
|
+
'error-task': {
|
|
41
|
+
path: 'src/tasks/error.ts',
|
|
42
|
+
handler: 'errorTask',
|
|
43
|
+
uuid: '8879271f-7e84-4748-bd11-4d81acf29fb6'
|
|
44
|
+
},
|
|
45
|
+
'metadata-task': {
|
|
46
|
+
path: 'src/tasks/metadata.ts',
|
|
47
|
+
handler: 'metadataTask',
|
|
48
|
+
uuid: 'fad2f735-ca09-4b8b-9c44-597de3641d28'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
18
51
|
};
|
|
19
52
|
beforeEach(() => {
|
|
53
|
+
// Mock fs.existsSync to return true
|
|
54
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
55
|
+
// Mock fs.readFileSync to return forge.json content
|
|
56
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockForgeConfig));
|
|
20
57
|
// Create client instance with config
|
|
21
58
|
client = new index_1.HiveLogClient(testConfig);
|
|
22
59
|
// Clear all mocks
|
|
@@ -37,23 +74,21 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
37
74
|
const result = await client.sendLog(executionRecord);
|
|
38
75
|
expect(result).toBe('success');
|
|
39
76
|
expect(mockedAxios.post).toHaveBeenCalledTimes(1);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
});
|
|
77
|
+
// Verify it uses the UUID endpoint
|
|
78
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
79
|
+
expect(callArgs[0]).toBe('https://test-host.com/api/log-ingest');
|
|
80
|
+
// Verify request body structure
|
|
81
|
+
const requestBody = callArgs[1];
|
|
82
|
+
expect(requestBody.projectUuid).toBe(testConfig.projectUuid);
|
|
83
|
+
expect(requestBody.taskUuid).toBe('f47ac10b-58cc-4372-a567-0e02b2c3d479');
|
|
84
|
+
// Verify logItem has UUID generated
|
|
85
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
86
|
+
expect(logItem.uuid).toBeDefined();
|
|
87
|
+
expect(typeof logItem.uuid).toBe('string');
|
|
88
|
+
expect(logItem.input).toEqual({ value: 'test-input' });
|
|
89
|
+
expect(logItem.output).toEqual({ result: 'test-output' });
|
|
90
|
+
expect(logItem.taskName).toBe('test-task');
|
|
91
|
+
expect(logItem.type).toBe('success');
|
|
57
92
|
});
|
|
58
93
|
it('should handle ExecutionRecord with complex boundaries', async () => {
|
|
59
94
|
mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
|
|
@@ -78,34 +113,15 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
78
113
|
};
|
|
79
114
|
const result = await client.sendLog(executionRecord);
|
|
80
115
|
expect(result).toBe('success');
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
database: [{
|
|
91
|
-
input: ['SELECT * FROM users'],
|
|
92
|
-
output: [{ id: 123 }],
|
|
93
|
-
timing: { startTime: 1000, endTime: 1100, duration: 100 }
|
|
94
|
-
}],
|
|
95
|
-
api: [{
|
|
96
|
-
input: [{ endpoint: '/auth' }],
|
|
97
|
-
output: { token: 'jwt123' },
|
|
98
|
-
timing: { startTime: 1200, endTime: 1250, duration: 50 }
|
|
99
|
-
}]
|
|
100
|
-
},
|
|
101
|
-
metadata: { environment: 'test' }
|
|
102
|
-
})
|
|
103
|
-
}, {
|
|
104
|
-
headers: {
|
|
105
|
-
Authorization: 'Bearer test-api-key:test-api-secret',
|
|
106
|
-
'Content-Type': 'application/json'
|
|
107
|
-
}
|
|
108
|
-
});
|
|
116
|
+
// Verify request structure
|
|
117
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
118
|
+
expect(callArgs[0]).toBe('https://test-host.com/api/log-ingest');
|
|
119
|
+
const requestBody = callArgs[1];
|
|
120
|
+
expect(requestBody.projectUuid).toBe(testConfig.projectUuid);
|
|
121
|
+
expect(requestBody.taskUuid).toBe('a45aafe3-8b01-4b58-b15d-9a96274858ee');
|
|
122
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
123
|
+
expect(logItem.uuid).toBeDefined();
|
|
124
|
+
expect(logItem.boundaries).toEqual(executionRecord.boundaries);
|
|
109
125
|
});
|
|
110
126
|
it('should handle ExecutionRecord with error', async () => {
|
|
111
127
|
mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
|
|
@@ -120,44 +136,51 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
120
136
|
};
|
|
121
137
|
const result = await client.sendLog(executionRecord);
|
|
122
138
|
expect(result).toBe('success');
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
error: 'Task execution failed',
|
|
130
|
-
taskName: 'error-task',
|
|
131
|
-
type: 'error',
|
|
132
|
-
boundaries: {},
|
|
133
|
-
metadata: {}
|
|
134
|
-
})
|
|
135
|
-
}, expect.any(Object));
|
|
139
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
140
|
+
const requestBody = callArgs[1];
|
|
141
|
+
expect(requestBody.taskUuid).toBe('8879271f-7e84-4748-bd11-4d81acf29fb6');
|
|
142
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
143
|
+
expect(logItem.error).toBe('Task execution failed');
|
|
144
|
+
expect(logItem.type).toBe('error');
|
|
136
145
|
});
|
|
137
|
-
it('should
|
|
146
|
+
it('should preserve existing UUID when execution record already has one', async () => {
|
|
138
147
|
mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
|
|
148
|
+
const existingUuid = '01234567-89ab-7def-8123-456789abcdef';
|
|
139
149
|
const executionRecord = {
|
|
150
|
+
uuid: existingUuid,
|
|
140
151
|
input: { value: 'test-input' },
|
|
141
152
|
output: { result: 'test-output' },
|
|
142
|
-
|
|
153
|
+
taskName: 'test-task',
|
|
143
154
|
type: 'success',
|
|
144
155
|
boundaries: {},
|
|
145
156
|
metadata: {}
|
|
146
157
|
};
|
|
147
158
|
const result = await client.sendLog(executionRecord);
|
|
148
159
|
expect(result).toBe('success');
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
161
|
+
const requestBody = callArgs[1];
|
|
162
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
163
|
+
expect(logItem.uuid).toBe(existingUuid);
|
|
164
|
+
});
|
|
165
|
+
it('should generate UUID v7 when execution record has no UUID', async () => {
|
|
166
|
+
mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
|
|
167
|
+
const executionRecord = {
|
|
168
|
+
input: { value: 'test-input' },
|
|
169
|
+
output: { result: 'test-output' },
|
|
170
|
+
taskName: 'test-task',
|
|
171
|
+
type: 'success',
|
|
172
|
+
boundaries: {},
|
|
173
|
+
metadata: {}
|
|
174
|
+
};
|
|
175
|
+
const result = await client.sendLog(executionRecord);
|
|
176
|
+
expect(result).toBe('success');
|
|
177
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
178
|
+
const requestBody = callArgs[1];
|
|
179
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
180
|
+
expect(logItem.uuid).toBeDefined();
|
|
181
|
+
expect(typeof logItem.uuid).toBe('string');
|
|
182
|
+
// UUID v7 pattern
|
|
183
|
+
expect(logItem.uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
161
184
|
});
|
|
162
185
|
});
|
|
163
186
|
describe('sendLog with additional metadata', () => {
|
|
@@ -180,22 +203,14 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
180
203
|
};
|
|
181
204
|
const result = await client.sendLog(executionRecord, sendLogMetadata);
|
|
182
205
|
expect(result).toBe('success');
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
boundaries: {},
|
|
192
|
-
metadata: {
|
|
193
|
-
recordMeta: 'from-record',
|
|
194
|
-
sharedKey: 'sendlog-value', // sendLog metadata takes priority
|
|
195
|
-
sendLogMeta: 'from-sendlog'
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
}, expect.any(Object));
|
|
206
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
207
|
+
const requestBody = callArgs[1];
|
|
208
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
209
|
+
expect(logItem.metadata).toEqual({
|
|
210
|
+
recordMeta: 'from-record',
|
|
211
|
+
sharedKey: 'sendlog-value', // sendLog metadata takes priority
|
|
212
|
+
sendLogMeta: 'from-sendlog'
|
|
213
|
+
});
|
|
199
214
|
});
|
|
200
215
|
});
|
|
201
216
|
describe('failed sendLog', () => {
|
|
@@ -226,6 +241,37 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
226
241
|
const result = await client.sendLog(executionRecord);
|
|
227
242
|
expect(result).toBe('error');
|
|
228
243
|
});
|
|
244
|
+
it('should return error when task is not found in forge.json', async () => {
|
|
245
|
+
const executionRecord = {
|
|
246
|
+
input: { value: 'test-input' },
|
|
247
|
+
taskName: 'nonexistent-task',
|
|
248
|
+
type: 'success',
|
|
249
|
+
boundaries: {},
|
|
250
|
+
metadata: {}
|
|
251
|
+
};
|
|
252
|
+
const result = await client.sendLog(executionRecord);
|
|
253
|
+
expect(result).toBe('error');
|
|
254
|
+
expect(mockedAxios.post).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
it('should return error when projectUuid is not set', async () => {
|
|
257
|
+
// Create client without projectUuid
|
|
258
|
+
const clientWithoutUuid = new index_1.HiveLogClient({
|
|
259
|
+
projectName: 'test-project',
|
|
260
|
+
apiKey: 'test-key',
|
|
261
|
+
apiSecret: 'test-secret',
|
|
262
|
+
host: 'https://test-host.com'
|
|
263
|
+
});
|
|
264
|
+
const executionRecord = {
|
|
265
|
+
input: { value: 'test-input' },
|
|
266
|
+
taskName: 'test-task',
|
|
267
|
+
type: 'success',
|
|
268
|
+
boundaries: {},
|
|
269
|
+
metadata: {}
|
|
270
|
+
};
|
|
271
|
+
const result = await clientWithoutUuid.sendLog(executionRecord);
|
|
272
|
+
expect(result).toBe('error');
|
|
273
|
+
expect(mockedAxios.post).not.toHaveBeenCalled();
|
|
274
|
+
});
|
|
229
275
|
});
|
|
230
276
|
describe('sendLog in silent mode', () => {
|
|
231
277
|
it('should return silent when client is not initialized', async () => {
|
|
@@ -245,16 +291,71 @@ describe('HiveLogClient sendLog with ExecutionRecord', () => {
|
|
|
245
291
|
expect(mockedAxios.post).not.toHaveBeenCalled();
|
|
246
292
|
});
|
|
247
293
|
});
|
|
294
|
+
describe('sendLog with unknown task name', () => {
|
|
295
|
+
it('should return error when taskName is missing and defaults to unknown-task', async () => {
|
|
296
|
+
const executionRecord = {
|
|
297
|
+
input: { value: 'test-input' },
|
|
298
|
+
output: { result: 'test-output' },
|
|
299
|
+
// taskName is missing - will default to 'unknown-task'
|
|
300
|
+
type: 'success',
|
|
301
|
+
boundaries: {},
|
|
302
|
+
metadata: {}
|
|
303
|
+
};
|
|
304
|
+
const result = await client.sendLog(executionRecord);
|
|
305
|
+
// Should return error because 'unknown-task' is not in forge.json
|
|
306
|
+
expect(result).toBe('error');
|
|
307
|
+
expect(mockedAxios.post).not.toHaveBeenCalled();
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
describe('sendLog response handling', () => {
|
|
311
|
+
it('should return response data when API returns object with uuid', async () => {
|
|
312
|
+
const responseData = {
|
|
313
|
+
uuid: 'log-uuid-123',
|
|
314
|
+
taskName: 'test-task',
|
|
315
|
+
projectName: 'test-project',
|
|
316
|
+
logItem: {},
|
|
317
|
+
createdAt: '2024-01-01T00:00:00Z'
|
|
318
|
+
};
|
|
319
|
+
mockedAxios.post.mockResolvedValueOnce({ data: responseData });
|
|
320
|
+
const executionRecord = {
|
|
321
|
+
input: { value: 'test' },
|
|
322
|
+
taskName: 'test-task',
|
|
323
|
+
type: 'success',
|
|
324
|
+
boundaries: {},
|
|
325
|
+
metadata: {}
|
|
326
|
+
};
|
|
327
|
+
const result = await client.sendLog(executionRecord);
|
|
328
|
+
expect(result).toEqual(responseData);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
248
331
|
});
|
|
249
332
|
describe('HiveLogClient getListener', () => {
|
|
250
333
|
let client;
|
|
251
334
|
const testConfig = {
|
|
252
335
|
projectName: 'test-project',
|
|
336
|
+
projectUuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
253
337
|
apiKey: 'test-api-key',
|
|
254
338
|
apiSecret: 'test-api-secret',
|
|
255
|
-
host: 'https://test-host.com'
|
|
339
|
+
host: 'https://test-host.com',
|
|
340
|
+
forgeConfigPath: './forge.json'
|
|
341
|
+
};
|
|
342
|
+
const mockForgeConfig = {
|
|
343
|
+
project: {
|
|
344
|
+
name: 'test-project',
|
|
345
|
+
uuid: '550e8400-e29b-41d4-a716-446655440000'
|
|
346
|
+
},
|
|
347
|
+
tasks: {
|
|
348
|
+
'test-task': {
|
|
349
|
+
path: 'src/tasks/test.ts',
|
|
350
|
+
handler: 'testTask',
|
|
351
|
+
uuid: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
|
|
352
|
+
}
|
|
353
|
+
}
|
|
256
354
|
};
|
|
257
355
|
beforeEach(() => {
|
|
356
|
+
// Mock fs
|
|
357
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
358
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockForgeConfig));
|
|
258
359
|
client = new index_1.HiveLogClient(testConfig);
|
|
259
360
|
jest.clearAllMocks();
|
|
260
361
|
});
|
|
@@ -273,18 +374,9 @@ describe('HiveLogClient getListener', () => {
|
|
|
273
374
|
};
|
|
274
375
|
await listener(executionRecord);
|
|
275
376
|
expect(mockedAxios.post).toHaveBeenCalledTimes(1);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
logItem: JSON.stringify({
|
|
280
|
-
input: { value: 'test-input' },
|
|
281
|
-
output: { result: 'test-output' },
|
|
282
|
-
taskName: 'test-task',
|
|
283
|
-
type: 'success',
|
|
284
|
-
boundaries: {},
|
|
285
|
-
metadata: {}
|
|
286
|
-
})
|
|
287
|
-
}, expect.any(Object));
|
|
377
|
+
// Verify it uses the UUID endpoint
|
|
378
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
379
|
+
expect(callArgs[0]).toBe('https://test-host.com/api/log-ingest');
|
|
288
380
|
});
|
|
289
381
|
it('should return a function that calls sendLog with provided metadata', async () => {
|
|
290
382
|
mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
|
|
@@ -298,20 +390,10 @@ describe('HiveLogClient getListener', () => {
|
|
|
298
390
|
metadata: { recordMeta: 'from-record' }
|
|
299
391
|
};
|
|
300
392
|
await listener(executionRecord);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
input: { value: 'test-input' },
|
|
306
|
-
output: { result: 'test-output' },
|
|
307
|
-
taskName: 'test-task',
|
|
308
|
-
type: 'success',
|
|
309
|
-
boundaries: {},
|
|
310
|
-
metadata: {
|
|
311
|
-
recordMeta: 'from-record'
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
}, expect.any(Object));
|
|
393
|
+
const callArgs = mockedAxios.post.mock.calls[0];
|
|
394
|
+
const requestBody = callArgs[1];
|
|
395
|
+
const logItem = JSON.parse(requestBody.logItem);
|
|
396
|
+
expect(logItem.metadata).toEqual({ recordMeta: 'from-record' });
|
|
315
397
|
});
|
|
316
398
|
it('should handle listener errors gracefully', async () => {
|
|
317
399
|
mockedAxios.post.mockRejectedValueOnce(new Error('Network error'));
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -266,65 +266,21 @@ export class HiveLogClient {
|
|
|
266
266
|
return 'silent'
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
const logsUrl = `${this.host}/api/tasks/log-ingest`
|
|
274
|
-
log('Sending log for task "%s" to %s', taskName, logsUrl)
|
|
275
|
-
|
|
276
|
-
const authToken = `${this.apiKey}:${this.apiSecret}`
|
|
277
|
-
|
|
278
|
-
// Merge metadata with priority: sendLog > record.metadata > client
|
|
279
|
-
const finalMetadata = this.mergeMetadata(record, metadata)
|
|
280
|
-
|
|
281
|
-
// Create logItem with merged metadata (no UUID generation for legacy method)
|
|
282
|
-
const logItem = {
|
|
283
|
-
...record,
|
|
284
|
-
taskName,
|
|
285
|
-
metadata: finalMetadata
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const response = await axios.post(logsUrl, {
|
|
289
|
-
projectName: this.projectName,
|
|
290
|
-
taskName,
|
|
291
|
-
logItem: JSON.stringify(logItem)
|
|
292
|
-
}, {
|
|
293
|
-
headers: {
|
|
294
|
-
Authorization: `Bearer ${authToken}`,
|
|
295
|
-
'Content-Type': 'application/json'
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
log('Success: Sent log for task "%s"', taskName)
|
|
300
|
-
|
|
301
|
-
// Return the full response data if available
|
|
302
|
-
if (response.data && typeof response.data === 'object' && 'uuid' in response.data) {
|
|
303
|
-
return response.data as LogApiSuccess
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return 'success'
|
|
307
|
-
} catch (e) {
|
|
308
|
-
const error = e as Error
|
|
309
|
-
log('Error: Failed to send log for task "%s": %s', taskName, error.message)
|
|
269
|
+
if (!this.projectUuid) {
|
|
270
|
+
log('Error: sendLog requires projectUuid to be set in client config')
|
|
310
271
|
return 'error'
|
|
311
272
|
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async sendLogByUuid(record: ExecutionRecord, taskUuid: string, metadata?: Metadata): Promise<'success' | 'error' | 'silent' | LogApiSuccess> {
|
|
315
|
-
if (!this.isInitialized) {
|
|
316
|
-
log('Silent mode: Skipping sendLogByUuid for task UUID "%s" - client not initialized', taskUuid)
|
|
317
|
-
return 'silent'
|
|
318
|
-
}
|
|
319
273
|
|
|
320
|
-
|
|
321
|
-
|
|
274
|
+
// Look up task UUID from forge.json
|
|
275
|
+
const taskUuid = this.getTaskUUID(taskName)
|
|
276
|
+
if (!taskUuid) {
|
|
277
|
+
log('Error: Cannot find UUID for task "%s" in forge.json', taskName)
|
|
322
278
|
return 'error'
|
|
323
279
|
}
|
|
324
280
|
|
|
325
281
|
try {
|
|
326
282
|
const logsUrl = `${this.host}/api/log-ingest`
|
|
327
|
-
log('Sending log for task
|
|
283
|
+
log('Sending log for task "%s" (UUID: %s) to %s', taskName, taskUuid, logsUrl)
|
|
328
284
|
|
|
329
285
|
const authToken = `${this.apiKey}:${this.apiSecret}`
|
|
330
286
|
|
|
@@ -338,13 +294,10 @@ export class HiveLogClient {
|
|
|
338
294
|
metadata: finalMetadata
|
|
339
295
|
}
|
|
340
296
|
|
|
341
|
-
// Create logItem with merged metadata and UUID
|
|
342
|
-
const logItem = recordWithUuid
|
|
343
|
-
|
|
344
297
|
const response = await axios.post(logsUrl, {
|
|
345
298
|
projectUuid: this.projectUuid,
|
|
346
299
|
taskUuid,
|
|
347
|
-
logItem: JSON.stringify(
|
|
300
|
+
logItem: JSON.stringify(recordWithUuid)
|
|
348
301
|
}, {
|
|
349
302
|
headers: {
|
|
350
303
|
Authorization: `Bearer ${authToken}`,
|
|
@@ -352,7 +305,7 @@ export class HiveLogClient {
|
|
|
352
305
|
}
|
|
353
306
|
})
|
|
354
307
|
|
|
355
|
-
log('Success: Sent log for task UUID
|
|
308
|
+
log('Success: Sent log for task "%s" (UUID: %s)', taskName, taskUuid)
|
|
356
309
|
|
|
357
310
|
// Return the full response data if available
|
|
358
311
|
if (response.data && typeof response.data === 'object' && 'uuid' in response.data) {
|
|
@@ -362,11 +315,12 @@ export class HiveLogClient {
|
|
|
362
315
|
return 'success'
|
|
363
316
|
} catch (e) {
|
|
364
317
|
const error = e as Error
|
|
365
|
-
log('Error: Failed to send log for task
|
|
318
|
+
log('Error: Failed to send log for task "%s": %s', taskName, error.message)
|
|
366
319
|
return 'error'
|
|
367
320
|
}
|
|
368
321
|
}
|
|
369
322
|
|
|
323
|
+
|
|
370
324
|
getListener(): (record: ExecutionRecord) => Promise<void> {
|
|
371
325
|
return async (record: ExecutionRecord) => {
|
|
372
326
|
await this.sendLog(record)
|
|
@@ -461,27 +415,6 @@ export class HiveLogClient {
|
|
|
461
415
|
return task.uuid
|
|
462
416
|
}
|
|
463
417
|
|
|
464
|
-
async sendLogByName(taskName: string, record: ExecutionRecord, metadata?: Metadata): Promise<'success' | 'error' | 'silent' | LogApiSuccess> {
|
|
465
|
-
if (!this.isInitialized) {
|
|
466
|
-
log('Silent mode: Skipping sendLogByName for task "%s" - client not initialized', taskName)
|
|
467
|
-
return 'silent'
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (!this.projectUuid) {
|
|
471
|
-
log('Error: sendLogByName requires projectUuid to be set in client config')
|
|
472
|
-
return 'error'
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const taskUuid = this.getTaskUUID(taskName)
|
|
476
|
-
if (!taskUuid) {
|
|
477
|
-
log('Error: Cannot find UUID for task "%s" in forge.json', taskName)
|
|
478
|
-
return 'error'
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Use the existing sendLogByUuid method
|
|
482
|
-
log('Sending log for task "%s" with uuid "%s"', taskName, taskUuid)
|
|
483
|
-
return await this.sendLogByUuid(record, taskUuid, metadata)
|
|
484
|
-
}
|
|
485
418
|
}
|
|
486
419
|
|
|
487
420
|
export const createHiveLogClient = (config: HiveLogClientConfig): HiveLogClient => {
|