@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/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(projectName: string) {
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 || !host) {
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.dev)', projectName)
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
- async sendLog(taskName: string, logItem: unknown): Promise<'success' | 'error' | 'silent'> {
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(logItem)
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.dev')
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.dev')
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 = (projectName: string): HiveLogClient => {
166
- log('Creating HiveLogClient for project "%s"', projectName)
167
- return new HiveLogClient(projectName)
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
  }
@@ -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
- beforeEach(() => {
13
- jest.resetModules()
14
- process.env = { ...originalEnv }
15
-
16
- // Set up environment variables
17
- process.env.HIVE_API_KEY = 'test-api-key'
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
- // Create client instance
22
- client = new HiveLogClient('test-project')
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 = {
@@ -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', { data: 'test' })
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.dev'
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.dev'
122
+ 'Missing Hive API credentials or host, get them at https://www.forgehive.cloud'
123
123
  )
124
124
  })
125
125
  })