@forgehive/hive-sdk 0.0.1 → 0.0.3
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 +335 -19
- package/dist/index.d.ts +27 -3
- package/dist/index.js +36 -15
- package/dist/test/getLog.test.js +8 -12
- package/dist/test/index.test.js +13 -13
- package/dist/test/metadata.test.d.ts +1 -0
- package/dist/test/metadata.test.js +352 -0
- package/dist/test/sendLog.test.js +15 -19
- package/dist/test/setQuality.test.js +10 -17
- package/package.json +1 -1
- package/src/index.ts +154 -15
- package/src/test/getLog.test.ts +9 -15
- package/src/test/index.test.ts +13 -13
- package/src/test/metadata.test.ts +467 -0
- package/src/test/sendLog.test.ts +16 -22
- package/src/test/setQuality.test.ts +11 -21
package/src/index.ts
CHANGED
|
@@ -3,6 +3,32 @@ import debug from 'debug'
|
|
|
3
3
|
|
|
4
4
|
const log = debug('hive-sdk')
|
|
5
5
|
|
|
6
|
+
// Metadata interface
|
|
7
|
+
export interface Metadata {
|
|
8
|
+
[key: string]: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Log item interface for sendLog method - flexible to accept task execution records
|
|
12
|
+
export interface LogItemInput {
|
|
13
|
+
input: unknown
|
|
14
|
+
output?: unknown
|
|
15
|
+
error?: unknown
|
|
16
|
+
boundaries?: unknown // Allow any boundary structure (task records have different format)
|
|
17
|
+
metadata?: Metadata
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Backward compatibility alias
|
|
21
|
+
export type LogItem = LogItemInput
|
|
22
|
+
|
|
23
|
+
// Configuration interface for HiveLogClient
|
|
24
|
+
export interface HiveLogClientConfig {
|
|
25
|
+
projectName: string
|
|
26
|
+
apiKey?: string
|
|
27
|
+
apiSecret?: string
|
|
28
|
+
host?: string
|
|
29
|
+
metadata?: Metadata
|
|
30
|
+
}
|
|
31
|
+
|
|
6
32
|
// API Response Types
|
|
7
33
|
export interface LogApiResponse {
|
|
8
34
|
uuid: string
|
|
@@ -13,6 +39,7 @@ export interface LogApiResponse {
|
|
|
13
39
|
output?: unknown
|
|
14
40
|
error?: unknown
|
|
15
41
|
boundaries?: Record<string, Array<{ input: unknown; output: unknown, error: unknown }>>
|
|
42
|
+
metadata?: Metadata
|
|
16
43
|
}
|
|
17
44
|
replayFrom?: string
|
|
18
45
|
createdAt: string
|
|
@@ -43,27 +70,29 @@ export class HiveLogClient {
|
|
|
43
70
|
private apiSecret: string | null
|
|
44
71
|
private host: string | null
|
|
45
72
|
private projectName: string
|
|
73
|
+
private baseMetadata: Metadata
|
|
46
74
|
private isInitialized: boolean
|
|
47
75
|
|
|
48
|
-
constructor(
|
|
49
|
-
const apiKey = process.env.HIVE_API_KEY
|
|
50
|
-
const apiSecret = process.env.HIVE_API_SECRET
|
|
51
|
-
const host = process.env.HIVE_HOST
|
|
76
|
+
constructor(config: HiveLogClientConfig) {
|
|
77
|
+
const apiKey = config.apiKey || process.env.HIVE_API_KEY
|
|
78
|
+
const apiSecret = config.apiSecret || process.env.HIVE_API_SECRET
|
|
79
|
+
const host = config.host || process.env.HIVE_HOST || 'https://www.forgehive.cloud'
|
|
52
80
|
|
|
53
|
-
this.projectName = projectName
|
|
81
|
+
this.projectName = config.projectName
|
|
82
|
+
this.baseMetadata = config.metadata || {}
|
|
54
83
|
|
|
55
|
-
if (!apiKey || !apiSecret
|
|
84
|
+
if (!apiKey || !apiSecret) {
|
|
56
85
|
this.apiKey = null
|
|
57
86
|
this.apiSecret = null
|
|
58
87
|
this.host = null
|
|
59
88
|
this.isInitialized = false
|
|
60
|
-
log('HiveLogClient in silent mode for project "%s" - missing credentials (get them at https://forgehive.
|
|
89
|
+
log('HiveLogClient in silent mode for project "%s" - missing API credentials (get them at https://www.forgehive.cloud)', config.projectName)
|
|
61
90
|
} else {
|
|
62
91
|
this.apiKey = apiKey
|
|
63
92
|
this.apiSecret = apiSecret
|
|
64
93
|
this.host = host
|
|
65
94
|
this.isInitialized = true
|
|
66
|
-
log('HiveLogClient initialized for project "%s" with host "%s"', projectName, host)
|
|
95
|
+
log('HiveLogClient initialized for project "%s" with host "%s"', config.projectName, host)
|
|
67
96
|
}
|
|
68
97
|
}
|
|
69
98
|
|
|
@@ -71,7 +100,24 @@ export class HiveLogClient {
|
|
|
71
100
|
return this.isInitialized
|
|
72
101
|
}
|
|
73
102
|
|
|
74
|
-
|
|
103
|
+
private mergeMetadata<T extends { input: unknown; metadata?: Metadata }>(logItem: T, sendLogMetadata?: Metadata): Metadata {
|
|
104
|
+
// Start with base metadata from client
|
|
105
|
+
let finalMetadata = { ...this.baseMetadata }
|
|
106
|
+
|
|
107
|
+
// Merge with logItem metadata if it exists
|
|
108
|
+
if (logItem.metadata) {
|
|
109
|
+
finalMetadata = { ...finalMetadata, ...logItem.metadata }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Merge with sendLog metadata (highest priority)
|
|
113
|
+
if (sendLogMetadata) {
|
|
114
|
+
finalMetadata = { ...finalMetadata, ...sendLogMetadata }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return finalMetadata
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async sendLog<T extends { input: unknown; metadata?: Metadata }>(taskName: string, logItem: T, metadata?: Metadata): Promise<'success' | 'error' | 'silent'> {
|
|
75
121
|
if (!this.isInitialized) {
|
|
76
122
|
log('Silent mode: Skipping sendLog for task "%s" - client not initialized', taskName)
|
|
77
123
|
return 'silent'
|
|
@@ -83,10 +129,19 @@ export class HiveLogClient {
|
|
|
83
129
|
|
|
84
130
|
const authToken = `${this.apiKey}:${this.apiSecret}`
|
|
85
131
|
|
|
132
|
+
// Merge metadata with priority: sendLog > logItem > client
|
|
133
|
+
const finalMetadata = this.mergeMetadata(logItem, metadata)
|
|
134
|
+
|
|
135
|
+
// Create enhanced logItem with merged metadata
|
|
136
|
+
const enhancedLogItem = {
|
|
137
|
+
...logItem,
|
|
138
|
+
metadata: finalMetadata
|
|
139
|
+
}
|
|
140
|
+
|
|
86
141
|
await axios.post(logsUrl, {
|
|
87
142
|
projectName: this.projectName,
|
|
88
143
|
taskName,
|
|
89
|
-
logItem: JSON.stringify(
|
|
144
|
+
logItem: JSON.stringify(enhancedLogItem)
|
|
90
145
|
}, {
|
|
91
146
|
headers: {
|
|
92
147
|
Authorization: `Bearer ${authToken}`,
|
|
@@ -106,7 +161,7 @@ export class HiveLogClient {
|
|
|
106
161
|
async getLog(taskName: string, uuid: string): Promise<LogApiResult | null> {
|
|
107
162
|
if (!this.isInitialized) {
|
|
108
163
|
log('Error: getLog for task "%s" with uuid "%s" - missing credentials', taskName, uuid)
|
|
109
|
-
throw new Error('Missing Hive API credentials or host, get them at https://forgehive.
|
|
164
|
+
throw new Error('Missing Hive API credentials or host, get them at https://www.forgehive.cloud')
|
|
110
165
|
}
|
|
111
166
|
|
|
112
167
|
try {
|
|
@@ -134,7 +189,7 @@ export class HiveLogClient {
|
|
|
134
189
|
async setQuality(taskName: string, uuid: string, quality: Quality): Promise<boolean> {
|
|
135
190
|
if (!this.isInitialized) {
|
|
136
191
|
log('Error: setQuality for task "%s" with uuid "%s" - missing credentials', taskName, uuid)
|
|
137
|
-
throw new Error('Missing Hive API credentials or host, get them at https://forgehive.
|
|
192
|
+
throw new Error('Missing Hive API credentials or host, get them at https://www.forgehive.cloud')
|
|
138
193
|
}
|
|
139
194
|
|
|
140
195
|
try {
|
|
@@ -162,7 +217,91 @@ export class HiveLogClient {
|
|
|
162
217
|
}
|
|
163
218
|
}
|
|
164
219
|
|
|
165
|
-
export const createHiveLogClient = (
|
|
166
|
-
log('Creating HiveLogClient for project "%s"', projectName)
|
|
167
|
-
return new HiveLogClient(
|
|
220
|
+
export const createHiveLogClient = (config: HiveLogClientConfig): HiveLogClient => {
|
|
221
|
+
log('Creating HiveLogClient for project "%s"', config.projectName)
|
|
222
|
+
return new HiveLogClient(config)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Configuration interface for HiveClient
|
|
226
|
+
export interface HiveClientConfig {
|
|
227
|
+
projectUuid: string
|
|
228
|
+
apiKey?: string
|
|
229
|
+
apiSecret?: string
|
|
230
|
+
host?: string
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Response types for invoke method
|
|
234
|
+
export interface InvokeResponse {
|
|
235
|
+
responsePayload: unknown
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface InvokeError {
|
|
239
|
+
error: string
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type InvokeResult = InvokeResponse | InvokeError
|
|
243
|
+
|
|
244
|
+
// Type guard to check if invoke response is an error
|
|
245
|
+
export function isInvokeError(response: unknown): response is InvokeError {
|
|
246
|
+
return response !== null && typeof response === 'object' && 'error' in response
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export class HiveClient {
|
|
250
|
+
private apiKey: string
|
|
251
|
+
private apiSecret: string
|
|
252
|
+
private host: string
|
|
253
|
+
private projectUuid: string
|
|
254
|
+
|
|
255
|
+
constructor(config: HiveClientConfig) {
|
|
256
|
+
const apiKey = config.apiKey || process.env.HIVE_API_KEY
|
|
257
|
+
const apiSecret = config.apiSecret || process.env.HIVE_API_SECRET
|
|
258
|
+
const host = config.host || process.env.HIVE_HOST || 'https://forgehive.dev'
|
|
259
|
+
|
|
260
|
+
if (!apiKey || !apiSecret) {
|
|
261
|
+
throw new Error('Missing Hive API credentials. Please provide apiKey and apiSecret, or set HIVE_API_KEY and HIVE_API_SECRET environment variables. Get them at https://forgehive.dev')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.projectUuid = config.projectUuid
|
|
265
|
+
this.host = host
|
|
266
|
+
this.apiKey = apiKey
|
|
267
|
+
this.apiSecret = apiSecret
|
|
268
|
+
|
|
269
|
+
log('HiveClient initialized for project "%s" with host "%s"', config.projectUuid, host)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async invoke(taskName: string, payload: unknown): Promise<InvokeResult | null> {
|
|
273
|
+
try {
|
|
274
|
+
const invokeUrl = `${this.host}/api/project/${this.projectUuid}/task/${taskName}/invoke`
|
|
275
|
+
log('Invoking task "%s" at %s', taskName, invokeUrl)
|
|
276
|
+
|
|
277
|
+
const authToken = `${this.apiKey}:${this.apiSecret}`
|
|
278
|
+
|
|
279
|
+
const response = await axios.post(invokeUrl, {
|
|
280
|
+
payload
|
|
281
|
+
}, {
|
|
282
|
+
headers: {
|
|
283
|
+
Authorization: `Bearer ${authToken}`,
|
|
284
|
+
'Content-Type': 'application/json'
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
log('Success: Invoked task "%s"', taskName)
|
|
289
|
+
return response.data as InvokeResult
|
|
290
|
+
} catch (e) {
|
|
291
|
+
const error = e as Error
|
|
292
|
+
log('Error: Failed to invoke task "%s": %s', taskName, error.message)
|
|
293
|
+
|
|
294
|
+
// Check if it's an axios error with response data
|
|
295
|
+
if (axios.isAxiosError(error) && error.response?.data) {
|
|
296
|
+
return error.response.data as InvokeError
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { error: error.message }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export const createHiveClient = (config: HiveClientConfig): HiveClient => {
|
|
305
|
+
log('Creating HiveClient for project "%s"', config.projectUuid)
|
|
306
|
+
return new HiveClient(config)
|
|
168
307
|
}
|
package/src/test/getLog.test.ts
CHANGED
|
@@ -6,29 +6,23 @@ jest.mock('axios')
|
|
|
6
6
|
const mockedAxios = axios as jest.Mocked<typeof axios>
|
|
7
7
|
|
|
8
8
|
describe('HiveLogClient getLog', () => {
|
|
9
|
-
const originalEnv = process.env
|
|
10
9
|
let client: HiveLogClient
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
process.env.HIVE_API_SECRET = 'test-api-secret'
|
|
19
|
-
process.env.HIVE_HOST = 'https://test-host.com'
|
|
11
|
+
const testConfig = {
|
|
12
|
+
projectName: 'test-project',
|
|
13
|
+
apiKey: 'test-api-key',
|
|
14
|
+
apiSecret: 'test-api-secret',
|
|
15
|
+
host: 'https://test-host.com'
|
|
16
|
+
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
client
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Create client instance with config
|
|
20
|
+
client = new HiveLogClient(testConfig)
|
|
23
21
|
|
|
24
22
|
// Clear all mocks
|
|
25
23
|
jest.clearAllMocks()
|
|
26
24
|
})
|
|
27
25
|
|
|
28
|
-
afterAll(() => {
|
|
29
|
-
process.env = originalEnv
|
|
30
|
-
})
|
|
31
|
-
|
|
32
26
|
describe('successful getLog', () => {
|
|
33
27
|
it('should fetch log successfully and return LogApiResponse', async () => {
|
|
34
28
|
const mockLogResponse: LogApiResponse = {
|
package/src/test/index.test.ts
CHANGED
|
@@ -18,7 +18,7 @@ describe('Hive SDK', () => {
|
|
|
18
18
|
process.env.HIVE_API_SECRET = 'test-secret'
|
|
19
19
|
process.env.HIVE_HOST = 'https://test.com'
|
|
20
20
|
|
|
21
|
-
expect(() => new HiveLogClient('test-project')).not.toThrow()
|
|
21
|
+
expect(() => new HiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
it('should create client in silent mode when HIVE_API_SECRET is missing', () => {
|
|
@@ -26,7 +26,7 @@ describe('Hive SDK', () => {
|
|
|
26
26
|
delete process.env.HIVE_API_SECRET
|
|
27
27
|
process.env.HIVE_HOST = 'https://test.com'
|
|
28
28
|
|
|
29
|
-
expect(() => new HiveLogClient('test-project')).not.toThrow()
|
|
29
|
+
expect(() => new HiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
it('should create client in silent mode when HIVE_HOST is missing', () => {
|
|
@@ -34,7 +34,7 @@ describe('Hive SDK', () => {
|
|
|
34
34
|
process.env.HIVE_API_SECRET = 'test-secret'
|
|
35
35
|
delete process.env.HIVE_HOST
|
|
36
36
|
|
|
37
|
-
expect(() => new HiveLogClient('test-project')).not.toThrow()
|
|
37
|
+
expect(() => new HiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
it('should create client in silent mode when all environment variables are missing', () => {
|
|
@@ -42,7 +42,7 @@ describe('Hive SDK', () => {
|
|
|
42
42
|
delete process.env.HIVE_API_SECRET
|
|
43
43
|
delete process.env.HIVE_HOST
|
|
44
44
|
|
|
45
|
-
expect(() => new HiveLogClient('test-project')).not.toThrow()
|
|
45
|
+
expect(() => new HiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
48
|
it('should create client successfully when all environment variables are present', () => {
|
|
@@ -50,7 +50,7 @@ describe('Hive SDK', () => {
|
|
|
50
50
|
process.env.HIVE_API_SECRET = 'test-secret'
|
|
51
51
|
process.env.HIVE_HOST = 'https://test.com'
|
|
52
52
|
|
|
53
|
-
expect(() => new HiveLogClient('test-project')).not.toThrow()
|
|
53
|
+
expect(() => new HiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
54
54
|
})
|
|
55
55
|
})
|
|
56
56
|
|
|
@@ -60,7 +60,7 @@ describe('Hive SDK', () => {
|
|
|
60
60
|
delete process.env.HIVE_API_SECRET
|
|
61
61
|
delete process.env.HIVE_HOST
|
|
62
62
|
|
|
63
|
-
const client = new HiveLogClient('test-project')
|
|
63
|
+
const client = new HiveLogClient({ projectName: 'test-project' })
|
|
64
64
|
expect(client.isActive()).toBe(false)
|
|
65
65
|
})
|
|
66
66
|
|
|
@@ -69,7 +69,7 @@ describe('Hive SDK', () => {
|
|
|
69
69
|
process.env.HIVE_API_SECRET = 'test-secret'
|
|
70
70
|
process.env.HIVE_HOST = 'https://test.com'
|
|
71
71
|
|
|
72
|
-
const client = new HiveLogClient('test-project')
|
|
72
|
+
const client = new HiveLogClient({ projectName: 'test-project' })
|
|
73
73
|
expect(client.isActive()).toBe(true)
|
|
74
74
|
})
|
|
75
75
|
})
|
|
@@ -80,7 +80,7 @@ describe('Hive SDK', () => {
|
|
|
80
80
|
process.env.HIVE_API_SECRET = 'test-secret'
|
|
81
81
|
process.env.HIVE_HOST = 'https://test.com'
|
|
82
82
|
|
|
83
|
-
const client = createHiveLogClient('test-project')
|
|
83
|
+
const client = createHiveLogClient({ projectName: 'test-project' })
|
|
84
84
|
|
|
85
85
|
expect(client).toBeInstanceOf(HiveLogClient)
|
|
86
86
|
})
|
|
@@ -90,7 +90,7 @@ describe('Hive SDK', () => {
|
|
|
90
90
|
delete process.env.HIVE_API_SECRET
|
|
91
91
|
delete process.env.HIVE_HOST
|
|
92
92
|
|
|
93
|
-
expect(() => createHiveLogClient('test-project')).not.toThrow()
|
|
93
|
+
expect(() => createHiveLogClient({ projectName: 'test-project' })).not.toThrow()
|
|
94
94
|
})
|
|
95
95
|
})
|
|
96
96
|
|
|
@@ -102,24 +102,24 @@ describe('Hive SDK', () => {
|
|
|
102
102
|
delete process.env.HIVE_API_SECRET
|
|
103
103
|
delete process.env.HIVE_HOST
|
|
104
104
|
|
|
105
|
-
silentClient = new HiveLogClient('silent-project')
|
|
105
|
+
silentClient = new HiveLogClient({ projectName: 'silent-project' })
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
it('should return "silent" for sendLog in silent mode', async () => {
|
|
109
|
-
const result = await silentClient.sendLog('test-task', {
|
|
109
|
+
const result = await silentClient.sendLog('test-task', { input: 'test' })
|
|
110
110
|
expect(result).toBe('silent')
|
|
111
111
|
})
|
|
112
112
|
|
|
113
113
|
it('should throw error for getLog in silent mode', async () => {
|
|
114
114
|
await expect(silentClient.getLog('test-task', 'test-uuid')).rejects.toThrow(
|
|
115
|
-
'Missing Hive API credentials or host, get them at https://forgehive.
|
|
115
|
+
'Missing Hive API credentials or host, get them at https://www.forgehive.cloud'
|
|
116
116
|
)
|
|
117
117
|
})
|
|
118
118
|
|
|
119
119
|
it('should throw error for setQuality in silent mode', async () => {
|
|
120
120
|
const quality = { score: 8, reason: 'test', suggestions: 'test' }
|
|
121
121
|
await expect(silentClient.setQuality('test-task', 'test-uuid', quality)).rejects.toThrow(
|
|
122
|
-
'Missing Hive API credentials or host, get them at https://forgehive.
|
|
122
|
+
'Missing Hive API credentials or host, get them at https://www.forgehive.cloud'
|
|
123
123
|
)
|
|
124
124
|
})
|
|
125
125
|
})
|