@forgehive/hive-sdk 0.0.1
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 +21 -0
- package/README.md +357 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +123 -0
- package/dist/test/getLog.test.d.ts +1 -0
- package/dist/test/getLog.test.js +144 -0
- package/dist/test/index.test.d.ts +1 -0
- package/dist/test/index.test.js +96 -0
- package/dist/test/sendLog.test.d.ts +1 -0
- package/dist/test/sendLog.test.js +111 -0
- package/dist/test/setQuality.test.d.ts +1 -0
- package/dist/test/setQuality.test.js +192 -0
- package/jest.config.js +11 -0
- package/package.json +25 -0
- package/src/index.ts +168 -0
- package/src/test/getLog.test.ts +181 -0
- package/src/test/index.test.ts +126 -0
- package/src/test/sendLog.test.ts +148 -0
- package/src/test/setQuality.test.ts +268 -0
- package/tsconfig.json +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ForgeHive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Hive SDK
|
|
2
|
+
|
|
3
|
+
A TypeScript/JavaScript SDK for interacting with the Forge Hive logging and quality assessment platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @forgehive/hive-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
or with pnpm:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @forgehive/hive-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### 1. Environment Variables
|
|
20
|
+
|
|
21
|
+
Before using the SDK, you need to set up the following environment variables:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
HIVE_API_KEY=your_api_key_here
|
|
25
|
+
HIVE_API_SECRET=your_api_secret_here
|
|
26
|
+
HIVE_HOST=https://your-hive-instance.com
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You can get your API credentials at [https://forgehive.dev](https://forgehive.dev).
|
|
30
|
+
|
|
31
|
+
### 2. Basic Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createHiveLogClient } from '@forgehive/hive-sdk'
|
|
35
|
+
|
|
36
|
+
const hiveLogger = createHiveLogClient('Personal Knowledge Management System')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API Methods
|
|
40
|
+
|
|
41
|
+
### `isActive(): boolean`
|
|
42
|
+
|
|
43
|
+
Check if the client is properly initialized with credentials.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const hiveLogger = createHiveLogClient('My Project')
|
|
47
|
+
|
|
48
|
+
if (hiveLogger.isActive()) {
|
|
49
|
+
console.log('Client is initialized with credentials')
|
|
50
|
+
// Safe to call getLog and setQuality without try/catch
|
|
51
|
+
} else {
|
|
52
|
+
console.log('Client is in silent mode')
|
|
53
|
+
// Only sendLog will work (returns 'silent')
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Returns:** `boolean` - `true` if credentials are available, `false` if in silent mode
|
|
58
|
+
|
|
59
|
+
### `sendLog(taskName: string, logItem: unknown): Promise<'success' | 'error' | 'silent'>`
|
|
60
|
+
|
|
61
|
+
Sends a log entry to Hive for a specific task.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const status = await hiveLogger.sendLog('user-authentication', {
|
|
65
|
+
input: { username: 'john_doe', timestamp: Date.now() },
|
|
66
|
+
output: { success: true, userId: 12345 },
|
|
67
|
+
boundaries: {
|
|
68
|
+
database: [
|
|
69
|
+
{
|
|
70
|
+
input: 'SELECT * FROM users WHERE username = ?',
|
|
71
|
+
output: [{ id: 12345, username: 'john_doe' }],
|
|
72
|
+
error: null
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
switch (status) {
|
|
79
|
+
case 'success':
|
|
80
|
+
console.log('Log sent successfully')
|
|
81
|
+
break
|
|
82
|
+
case 'error':
|
|
83
|
+
console.error('Failed to send log - network or API error')
|
|
84
|
+
break
|
|
85
|
+
case 'silent':
|
|
86
|
+
console.log('Running in silent mode - no credentials configured')
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Parameters:**
|
|
92
|
+
- `taskName`: Name of the task being logged
|
|
93
|
+
- `logItem`: Object containing input, output, error, and boundaries data
|
|
94
|
+
|
|
95
|
+
**Returns:** `Promise<'success' | 'error' | 'silent'>` - Status of the operation
|
|
96
|
+
|
|
97
|
+
### `getLog(taskName: string, uuid: string): Promise<LogApiResult | null>`
|
|
98
|
+
|
|
99
|
+
Retrieves a specific log entry from Hive.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
try {
|
|
103
|
+
const logData = await hiveLogger.getLog('user-authentication', 'log-uuid-123')
|
|
104
|
+
|
|
105
|
+
if (logData && !isApiError(logData)) {
|
|
106
|
+
console.log('Log retrieved:', logData.logItem)
|
|
107
|
+
} else if (logData && isApiError(logData)) {
|
|
108
|
+
console.error('API Error:', logData.error)
|
|
109
|
+
} else {
|
|
110
|
+
console.error('Failed to retrieve log')
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('Missing credentials:', error.message)
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Parameters:**
|
|
118
|
+
- `taskName`: Name of the task
|
|
119
|
+
- `uuid`: Unique identifier of the log entry
|
|
120
|
+
|
|
121
|
+
**Returns:** `Promise<LogApiResult | null>` - Log data, error object, or `null` if failed
|
|
122
|
+
**Throws:** Error when credentials are missing
|
|
123
|
+
|
|
124
|
+
### `setQuality(taskName: string, uuid: string, quality: Quality): Promise<boolean>`
|
|
125
|
+
|
|
126
|
+
Sets a quality assessment for a specific log entry.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { Quality } from '@forgehive/hive-sdk'
|
|
130
|
+
|
|
131
|
+
const quality: Quality = {
|
|
132
|
+
score: 8.5,
|
|
133
|
+
reason: 'Good performance with minor improvements needed',
|
|
134
|
+
suggestions: 'Consider optimizing the database query for better performance'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const success = await hiveLogger.setQuality('user-authentication', 'log-uuid-123', quality)
|
|
139
|
+
|
|
140
|
+
if (success) {
|
|
141
|
+
console.log('Quality assessment saved')
|
|
142
|
+
} else {
|
|
143
|
+
console.error('Failed to save quality assessment')
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Missing credentials:', error.message)
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Parameters:**
|
|
151
|
+
- `taskName`: Name of the task
|
|
152
|
+
- `uuid`: Unique identifier of the log entry
|
|
153
|
+
- `quality`: Quality assessment object with score (number), reason (string), and suggestions (string)
|
|
154
|
+
|
|
155
|
+
**Returns:** `Promise<boolean>` - `true` if successful, `false` if failed
|
|
156
|
+
**Throws:** Error when credentials are missing
|
|
157
|
+
|
|
158
|
+
## Types
|
|
159
|
+
|
|
160
|
+
### `LogApiResponse`
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
interface LogApiResponse {
|
|
164
|
+
uuid: string
|
|
165
|
+
taskName: string
|
|
166
|
+
projectName: string
|
|
167
|
+
logItem: {
|
|
168
|
+
input: unknown
|
|
169
|
+
output?: unknown
|
|
170
|
+
error?: unknown
|
|
171
|
+
boundaries?: Record<string, Array<{ input: unknown; output: unknown, error: unknown }>>
|
|
172
|
+
}
|
|
173
|
+
replayFrom?: string
|
|
174
|
+
createdAt: string
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### `Quality`
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
interface Quality {
|
|
182
|
+
score: number // Quality score (typically 0-10)
|
|
183
|
+
reason: string // Explanation for the score
|
|
184
|
+
suggestions: string // Suggestions for improvement
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `ApiError`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface ApiError {
|
|
192
|
+
error: string
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Type Guards
|
|
197
|
+
|
|
198
|
+
### `isApiError(response: unknown): response is ApiError`
|
|
199
|
+
|
|
200
|
+
Use this type guard to check if a response is an error:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { isApiError } from '@forgehive/hive-sdk'
|
|
204
|
+
|
|
205
|
+
const result = await hiveLogger.getLog('task-name', 'log-uuid')
|
|
206
|
+
|
|
207
|
+
if (result && isApiError(result)) {
|
|
208
|
+
console.error('Error:', result.error)
|
|
209
|
+
} else if (result) {
|
|
210
|
+
console.log('Success:', result.logItem)
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Debugging
|
|
215
|
+
|
|
216
|
+
The SDK uses the `debug` package for internal logging. To enable debug logs, set the `DEBUG` environment variable:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Enable all hive-sdk debug logs
|
|
220
|
+
DEBUG=hive-sdk node your-app.js
|
|
221
|
+
|
|
222
|
+
# Enable all debug logs
|
|
223
|
+
DEBUG=* node your-app.js
|
|
224
|
+
|
|
225
|
+
# Enable hive-sdk logs along with other specific loggers
|
|
226
|
+
DEBUG=hive-sdk,express:* node your-app.js
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
When debugging is enabled, you'll see detailed logs like:
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
# Normal mode (with credentials)
|
|
233
|
+
hive-sdk Creating HiveLogClient for project "Personal Knowledge Management System" +0ms
|
|
234
|
+
hive-sdk HiveLogClient initialized for project "Personal Knowledge Management System" with host "https://your-hive-instance.com" +2ms
|
|
235
|
+
hive-sdk Sending log for task "user-authentication" to https://your-hive-instance.com/api/tasks/log-ingest +100ms
|
|
236
|
+
hive-sdk Success: Sent log for task "user-authentication" +250ms
|
|
237
|
+
|
|
238
|
+
# Silent mode (missing credentials)
|
|
239
|
+
hive-sdk Creating HiveLogClient for project "Personal Knowledge Management System" +0ms
|
|
240
|
+
hive-sdk HiveLogClient in silent mode for project "Personal Knowledge Management System" - missing credentials (get them at https://forgehive.dev) +2ms
|
|
241
|
+
hive-sdk Silent mode: Skipping sendLog for task "user-authentication" - client not initialized +100ms
|
|
242
|
+
hive-sdk Error: getLog for task "user-task" with uuid "some-uuid" - missing credentials +150ms
|
|
243
|
+
|
|
244
|
+
# Error handling
|
|
245
|
+
hive-sdk Error: Failed to send log for task "another-task": Network timeout +300ms
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Error Handling
|
|
249
|
+
|
|
250
|
+
The SDK handles errors gracefully:
|
|
251
|
+
|
|
252
|
+
- **Network errors**: Logged via debug, methods return `'error'` or `false`
|
|
253
|
+
- **Authentication errors**: Logged via debug, methods return `'error'` or `false`
|
|
254
|
+
- **API errors**: Returned as `ApiError` objects (for `getLog`) or logged via debug (for other methods)
|
|
255
|
+
- **Missing credentials**:
|
|
256
|
+
- `sendLog`: Returns `'silent'` (no errors thrown)
|
|
257
|
+
- `getLog` and `setQuality`: Throw errors
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// sendLog works even without credentials (returns 'silent')
|
|
261
|
+
const hiveLogger = createHiveLogClient('My Project')
|
|
262
|
+
|
|
263
|
+
const status = await hiveLogger.sendLog('task-name', { data: 'example' })
|
|
264
|
+
if (status === 'error') {
|
|
265
|
+
console.error('Network or API error')
|
|
266
|
+
} else if (status === 'silent') {
|
|
267
|
+
console.log('Running in silent mode - no credentials')
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Error Handling Patterns
|
|
272
|
+
|
|
273
|
+
**Check credentials before API calls:**
|
|
274
|
+
```typescript
|
|
275
|
+
const hiveLogger = createHiveLogClient('My Project')
|
|
276
|
+
|
|
277
|
+
if (hiveLogger.isActive()) {
|
|
278
|
+
// Safe to use all methods
|
|
279
|
+
const logData = await hiveLogger.getLog('task', 'uuid')
|
|
280
|
+
await hiveLogger.setQuality('task', 'uuid', quality)
|
|
281
|
+
} else {
|
|
282
|
+
console.log('Running in silent mode')
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**sendLog** - Returns status strings (never throws):
|
|
287
|
+
```typescript
|
|
288
|
+
const status = await hiveLogger.sendLog('task', { data: 'test' })
|
|
289
|
+
// Returns: 'success', 'error', or 'silent'
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**getLog and setQuality** - Throw errors when credentials missing:
|
|
293
|
+
```typescript
|
|
294
|
+
try {
|
|
295
|
+
await hiveLogger.getLog('task', 'uuid')
|
|
296
|
+
await hiveLogger.setQuality('task', 'uuid', quality)
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error('Missing credentials:', error.message)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Examples
|
|
303
|
+
|
|
304
|
+
### Complete Example
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { createHiveLogClient, isApiError, Quality } from '@forgehive/hive-sdk'
|
|
308
|
+
|
|
309
|
+
async function main() {
|
|
310
|
+
// Initialize the client
|
|
311
|
+
const hiveLogger = createHiveLogClient('Personal Knowledge Management System')
|
|
312
|
+
|
|
313
|
+
// Send a log
|
|
314
|
+
const logData = {
|
|
315
|
+
input: { query: 'search for AI papers', userId: 123 },
|
|
316
|
+
output: { results: ['paper1.pdf', 'paper2.pdf'], count: 2 },
|
|
317
|
+
boundaries: {
|
|
318
|
+
search_engine: [
|
|
319
|
+
{
|
|
320
|
+
input: 'AI papers filetype:pdf',
|
|
321
|
+
output: { hits: 150, results: ['...'] },
|
|
322
|
+
error: null
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const status = await hiveLogger.sendLog('document-search', logData)
|
|
329
|
+
console.log('Log status:', status)
|
|
330
|
+
|
|
331
|
+
// Retrieve a log
|
|
332
|
+
try {
|
|
333
|
+
const retrievedLog = await hiveLogger.getLog('document-search', 'some-uuid')
|
|
334
|
+
if (retrievedLog && !isApiError(retrievedLog)) {
|
|
335
|
+
console.log('Retrieved log:', retrievedLog.logItem)
|
|
336
|
+
|
|
337
|
+
// Set quality assessment
|
|
338
|
+
const quality: Quality = {
|
|
339
|
+
score: 9.0,
|
|
340
|
+
reason: 'Excellent search results with high relevance',
|
|
341
|
+
suggestions: 'Consider adding result ranking by publication date'
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const qualitySet = await hiveLogger.setQuality('document-search', retrievedLog.uuid, quality)
|
|
345
|
+
console.log('Quality assessment saved:', qualitySet)
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('Missing credentials for getLog/setQuality:', error.message)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
main().catch(console.error)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
ISC
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface LogApiResponse {
|
|
2
|
+
uuid: string;
|
|
3
|
+
taskName: string;
|
|
4
|
+
projectName: string;
|
|
5
|
+
logItem: {
|
|
6
|
+
input: unknown;
|
|
7
|
+
output?: unknown;
|
|
8
|
+
error?: unknown;
|
|
9
|
+
boundaries?: Record<string, Array<{
|
|
10
|
+
input: unknown;
|
|
11
|
+
output: unknown;
|
|
12
|
+
error: unknown;
|
|
13
|
+
}>>;
|
|
14
|
+
};
|
|
15
|
+
replayFrom?: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ApiError {
|
|
19
|
+
error: string;
|
|
20
|
+
}
|
|
21
|
+
export interface LogApiSuccess extends LogApiResponse {
|
|
22
|
+
}
|
|
23
|
+
export type LogApiResult = LogApiSuccess | ApiError;
|
|
24
|
+
export interface Quality {
|
|
25
|
+
score: number;
|
|
26
|
+
reason: string;
|
|
27
|
+
suggestions: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function isApiError(response: unknown): response is ApiError;
|
|
30
|
+
export declare class HiveLogClient {
|
|
31
|
+
private apiKey;
|
|
32
|
+
private apiSecret;
|
|
33
|
+
private host;
|
|
34
|
+
private projectName;
|
|
35
|
+
private isInitialized;
|
|
36
|
+
constructor(projectName: string);
|
|
37
|
+
isActive(): boolean;
|
|
38
|
+
sendLog(taskName: string, logItem: unknown): Promise<'success' | 'error' | 'silent'>;
|
|
39
|
+
getLog(taskName: string, uuid: string): Promise<LogApiResult | null>;
|
|
40
|
+
setQuality(taskName: string, uuid: string, quality: Quality): Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
export declare const createHiveLogClient: (projectName: string) => HiveLogClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createHiveLogClient = exports.HiveLogClient = void 0;
|
|
7
|
+
exports.isApiError = isApiError;
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const debug_1 = __importDefault(require("debug"));
|
|
10
|
+
const log = (0, debug_1.default)('hive-sdk');
|
|
11
|
+
// Type guard to check if response is an error
|
|
12
|
+
function isApiError(response) {
|
|
13
|
+
return response !== null && typeof response === 'object' && 'error' in response;
|
|
14
|
+
}
|
|
15
|
+
class HiveLogClient {
|
|
16
|
+
constructor(projectName) {
|
|
17
|
+
const apiKey = process.env.HIVE_API_KEY;
|
|
18
|
+
const apiSecret = process.env.HIVE_API_SECRET;
|
|
19
|
+
const host = process.env.HIVE_HOST;
|
|
20
|
+
this.projectName = projectName;
|
|
21
|
+
if (!apiKey || !apiSecret || !host) {
|
|
22
|
+
this.apiKey = null;
|
|
23
|
+
this.apiSecret = null;
|
|
24
|
+
this.host = null;
|
|
25
|
+
this.isInitialized = false;
|
|
26
|
+
log('HiveLogClient in silent mode for project "%s" - missing credentials (get them at https://forgehive.dev)', projectName);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.apiKey = apiKey;
|
|
30
|
+
this.apiSecret = apiSecret;
|
|
31
|
+
this.host = host;
|
|
32
|
+
this.isInitialized = true;
|
|
33
|
+
log('HiveLogClient initialized for project "%s" with host "%s"', projectName, host);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
isActive() {
|
|
37
|
+
return this.isInitialized;
|
|
38
|
+
}
|
|
39
|
+
async sendLog(taskName, logItem) {
|
|
40
|
+
if (!this.isInitialized) {
|
|
41
|
+
log('Silent mode: Skipping sendLog for task "%s" - client not initialized', taskName);
|
|
42
|
+
return 'silent';
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const logsUrl = `${this.host}/api/tasks/log-ingest`;
|
|
46
|
+
log('Sending log for task "%s" to %s', taskName, logsUrl);
|
|
47
|
+
const authToken = `${this.apiKey}:${this.apiSecret}`;
|
|
48
|
+
await axios_1.default.post(logsUrl, {
|
|
49
|
+
projectName: this.projectName,
|
|
50
|
+
taskName,
|
|
51
|
+
logItem: JSON.stringify(logItem)
|
|
52
|
+
}, {
|
|
53
|
+
headers: {
|
|
54
|
+
Authorization: `Bearer ${authToken}`,
|
|
55
|
+
'Content-Type': 'application/json'
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
log('Success: Sent log for task "%s"', taskName);
|
|
59
|
+
return 'success';
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
const error = e;
|
|
63
|
+
log('Error: Failed to send log for task "%s": %s', taskName, error.message);
|
|
64
|
+
return 'error';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async getLog(taskName, uuid) {
|
|
68
|
+
if (!this.isInitialized) {
|
|
69
|
+
log('Error: getLog for task "%s" with uuid "%s" - missing credentials', taskName, uuid);
|
|
70
|
+
throw new Error('Missing Hive API credentials or host, get them at https://forgehive.dev');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const logUrl = `${this.host}/api/tasks/${taskName}/logs/${uuid}`;
|
|
74
|
+
log('Fetching log for task "%s" with uuid "%s" from %s', taskName, uuid, logUrl);
|
|
75
|
+
const authToken = `${this.apiKey}:${this.apiSecret}`;
|
|
76
|
+
const response = await axios_1.default.get(logUrl, {
|
|
77
|
+
headers: {
|
|
78
|
+
Authorization: `Bearer ${authToken}`,
|
|
79
|
+
'Content-Type': 'application/json'
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
log('Success: Fetched log for task "%s" with uuid "%s"', taskName, uuid);
|
|
83
|
+
return response.data;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
const error = e;
|
|
87
|
+
log('Error: Failed to fetch log for task "%s" with uuid "%s": %s', taskName, uuid, error.message);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async setQuality(taskName, uuid, quality) {
|
|
92
|
+
if (!this.isInitialized) {
|
|
93
|
+
log('Error: setQuality for task "%s" with uuid "%s" - missing credentials', taskName, uuid);
|
|
94
|
+
throw new Error('Missing Hive API credentials or host, get them at https://forgehive.dev');
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const qualityUrl = `${this.host}/api/tasks/${taskName}/logs/${uuid}/set-quality`;
|
|
98
|
+
log('Setting quality for task "%s" with uuid "%s" (score: %d) to %s', taskName, uuid, quality.score, qualityUrl);
|
|
99
|
+
const authToken = `${this.apiKey}:${this.apiSecret}`;
|
|
100
|
+
await axios_1.default.post(qualityUrl, {
|
|
101
|
+
quality
|
|
102
|
+
}, {
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: `Bearer ${authToken}`,
|
|
105
|
+
'Content-Type': 'application/json'
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
log('Success: Set quality for task "%s" with uuid "%s" (score: %d)', taskName, uuid, quality.score);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
const error = e;
|
|
113
|
+
log('Error: Failed to set quality for task "%s" with uuid "%s": %s', taskName, uuid, error.message);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.HiveLogClient = HiveLogClient;
|
|
119
|
+
const createHiveLogClient = (projectName) => {
|
|
120
|
+
log('Creating HiveLogClient for project "%s"', projectName);
|
|
121
|
+
return new HiveLogClient(projectName);
|
|
122
|
+
};
|
|
123
|
+
exports.createHiveLogClient = createHiveLogClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const index_1 = require("../index");
|
|
8
|
+
// Mock axios
|
|
9
|
+
jest.mock('axios');
|
|
10
|
+
const mockedAxios = axios_1.default;
|
|
11
|
+
describe('HiveLogClient getLog', () => {
|
|
12
|
+
const originalEnv = process.env;
|
|
13
|
+
let client;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.resetModules();
|
|
16
|
+
process.env = { ...originalEnv };
|
|
17
|
+
// Set up environment variables
|
|
18
|
+
process.env.HIVE_API_KEY = 'test-api-key';
|
|
19
|
+
process.env.HIVE_API_SECRET = 'test-api-secret';
|
|
20
|
+
process.env.HIVE_HOST = 'https://test-host.com';
|
|
21
|
+
// Create client instance
|
|
22
|
+
client = new index_1.HiveLogClient('test-project');
|
|
23
|
+
// Clear all mocks
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
process.env = originalEnv;
|
|
28
|
+
});
|
|
29
|
+
describe('successful getLog', () => {
|
|
30
|
+
it('should fetch log successfully and return LogApiResponse', async () => {
|
|
31
|
+
const mockLogResponse = {
|
|
32
|
+
uuid: 'test-uuid-123',
|
|
33
|
+
taskName: 'test-task',
|
|
34
|
+
projectName: 'test-project',
|
|
35
|
+
logItem: {
|
|
36
|
+
input: { userId: 123, action: 'login' },
|
|
37
|
+
output: { success: true, sessionId: 'abc123' },
|
|
38
|
+
error: null,
|
|
39
|
+
boundaries: {
|
|
40
|
+
database: [{ input: 'SELECT * FROM users', output: [{ id: 123 }], error: null }]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
replayFrom: 'some-replay-id',
|
|
44
|
+
createdAt: '2023-12-01T10:00:00Z'
|
|
45
|
+
};
|
|
46
|
+
// Mock successful axios response
|
|
47
|
+
mockedAxios.get.mockResolvedValueOnce({ data: mockLogResponse });
|
|
48
|
+
const result = await client.getLog('test-task', 'test-uuid-123');
|
|
49
|
+
expect(result).toEqual(mockLogResponse);
|
|
50
|
+
expect(mockedAxios.get).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(mockedAxios.get).toHaveBeenCalledWith('https://test-host.com/api/tasks/test-task/logs/test-uuid-123', {
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: 'Bearer test-api-key:test-api-secret',
|
|
54
|
+
'Content-Type': 'application/json'
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
it('should handle API error response', async () => {
|
|
59
|
+
const mockErrorResponse = {
|
|
60
|
+
error: 'Log not found'
|
|
61
|
+
};
|
|
62
|
+
mockedAxios.get.mockResolvedValueOnce({ data: mockErrorResponse });
|
|
63
|
+
const result = await client.getLog('test-task', 'non-existent-uuid');
|
|
64
|
+
expect(result).toEqual(mockErrorResponse);
|
|
65
|
+
expect((0, index_1.isApiError)(result)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('should handle minimal log response', async () => {
|
|
68
|
+
const mockMinimalResponse = {
|
|
69
|
+
uuid: 'minimal-uuid',
|
|
70
|
+
taskName: 'minimal-task',
|
|
71
|
+
projectName: 'test-project',
|
|
72
|
+
logItem: {
|
|
73
|
+
input: 'simple input'
|
|
74
|
+
},
|
|
75
|
+
createdAt: '2023-12-01T10:00:00Z'
|
|
76
|
+
};
|
|
77
|
+
mockedAxios.get.mockResolvedValueOnce({ data: mockMinimalResponse });
|
|
78
|
+
const result = await client.getLog('minimal-task', 'minimal-uuid');
|
|
79
|
+
expect(result).toEqual(mockMinimalResponse);
|
|
80
|
+
expect((0, index_1.isApiError)(result)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('failed getLog', () => {
|
|
84
|
+
it('should return null when axios throws an error', async () => {
|
|
85
|
+
// Mock axios to throw an error
|
|
86
|
+
mockedAxios.get.mockRejectedValueOnce(new Error('Network error'));
|
|
87
|
+
const result = await client.getLog('test-task', 'test-uuid');
|
|
88
|
+
expect(result).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
it('should return null when server returns 404', async () => {
|
|
91
|
+
// Mock axios to throw a 404 error
|
|
92
|
+
const notFoundError = new Error('Request failed with status code 404');
|
|
93
|
+
mockedAxios.get.mockRejectedValueOnce(notFoundError);
|
|
94
|
+
const result = await client.getLog('test-task', 'non-existent-uuid');
|
|
95
|
+
expect(result).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
it('should return null when server returns 500', async () => {
|
|
98
|
+
// Mock axios to throw a server error
|
|
99
|
+
const serverError = new Error('Server Error');
|
|
100
|
+
mockedAxios.get.mockRejectedValueOnce(serverError);
|
|
101
|
+
const result = await client.getLog('test-task', 'test-uuid');
|
|
102
|
+
expect(result).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('getLog parameters', () => {
|
|
106
|
+
it('should handle special characters in taskName and uuid', async () => {
|
|
107
|
+
const mockResponse = {
|
|
108
|
+
uuid: 'uuid-with-special-chars-!@#',
|
|
109
|
+
taskName: 'task-with-special-chars-!@#',
|
|
110
|
+
projectName: 'test-project',
|
|
111
|
+
logItem: { input: 'test' },
|
|
112
|
+
createdAt: '2023-12-01T10:00:00Z'
|
|
113
|
+
};
|
|
114
|
+
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
|
|
115
|
+
const result = await client.getLog('task-with-special-chars-!@#', 'uuid-with-special-chars-!@#');
|
|
116
|
+
expect(result).toEqual(mockResponse);
|
|
117
|
+
expect(mockedAxios.get).toHaveBeenCalledWith('https://test-host.com/api/tasks/task-with-special-chars-!@#/logs/uuid-with-special-chars-!@#', expect.any(Object));
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('isApiError type guard', () => {
|
|
122
|
+
it('should return true for ApiError objects', () => {
|
|
123
|
+
const apiError = { error: 'Something went wrong' };
|
|
124
|
+
expect((0, index_1.isApiError)(apiError)).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
it('should return false for LogApiResponse objects', () => {
|
|
127
|
+
const logResponse = {
|
|
128
|
+
uuid: 'test-uuid',
|
|
129
|
+
taskName: 'test-task',
|
|
130
|
+
projectName: 'test-project',
|
|
131
|
+
logItem: { input: 'test' },
|
|
132
|
+
createdAt: '2023-12-01T10:00:00Z'
|
|
133
|
+
};
|
|
134
|
+
expect((0, index_1.isApiError)(logResponse)).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
it('should return false for null or undefined', () => {
|
|
137
|
+
expect((0, index_1.isApiError)(null)).toBeFalsy();
|
|
138
|
+
expect((0, index_1.isApiError)(undefined)).toBeFalsy();
|
|
139
|
+
});
|
|
140
|
+
it('should return false for objects without error property', () => {
|
|
141
|
+
expect((0, index_1.isApiError)({ success: true })).toBe(false);
|
|
142
|
+
expect((0, index_1.isApiError)({ data: 'some data' })).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|