flowforge-client 0.1.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/README.md +343 -0
- package/dist/index.d.mts +908 -0
- package/dist/index.d.ts +908 -0
- package/dist/index.js +811 -0
- package/dist/index.mjs +774 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ApiKeysResource: () => ApiKeysResource,
|
|
24
|
+
ApprovalsResource: () => ApprovalsResource,
|
|
25
|
+
EventsResource: () => EventsResource,
|
|
26
|
+
FlowForgeError: () => FlowForgeError,
|
|
27
|
+
FunctionsResource: () => FunctionsResource,
|
|
28
|
+
HealthResource: () => HealthResource,
|
|
29
|
+
QueryBuilder: () => QueryBuilder,
|
|
30
|
+
RunsResource: () => RunsResource,
|
|
31
|
+
ToolsResource: () => ToolsResource,
|
|
32
|
+
UsersResource: () => UsersResource,
|
|
33
|
+
createClient: () => createClient
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/builder.ts
|
|
38
|
+
var QueryBuilder = class {
|
|
39
|
+
constructor(request, basePath, responseKey) {
|
|
40
|
+
this.request = request;
|
|
41
|
+
this.basePath = basePath;
|
|
42
|
+
this.responseKey = responseKey;
|
|
43
|
+
this.params = { filters: {} };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Filter by exact match.
|
|
47
|
+
*/
|
|
48
|
+
eq(field, value) {
|
|
49
|
+
this.params.filters[field] = value;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Order results by a field.
|
|
54
|
+
*/
|
|
55
|
+
order(field, direction = "asc") {
|
|
56
|
+
this.params.orderBy = field;
|
|
57
|
+
this.params.orderDir = direction;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Limit the number of results.
|
|
62
|
+
*/
|
|
63
|
+
limit(n) {
|
|
64
|
+
this.params.limitValue = n;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Skip the first n results (for pagination).
|
|
69
|
+
*/
|
|
70
|
+
offset(n) {
|
|
71
|
+
this.params.offsetValue = n;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build query string from params.
|
|
76
|
+
*/
|
|
77
|
+
buildQueryString() {
|
|
78
|
+
const searchParams = new URLSearchParams();
|
|
79
|
+
for (const [key, value] of Object.entries(this.params.filters)) {
|
|
80
|
+
if (value !== void 0 && value !== null) {
|
|
81
|
+
searchParams.set(key, String(value));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (this.params.orderBy) {
|
|
85
|
+
searchParams.set("order_by", this.params.orderBy);
|
|
86
|
+
searchParams.set("order_dir", this.params.orderDir || "asc");
|
|
87
|
+
}
|
|
88
|
+
if (this.params.limitValue !== void 0) {
|
|
89
|
+
searchParams.set("limit", String(this.params.limitValue));
|
|
90
|
+
}
|
|
91
|
+
if (this.params.offsetValue !== void 0) {
|
|
92
|
+
searchParams.set("offset", String(this.params.offsetValue));
|
|
93
|
+
}
|
|
94
|
+
const query = searchParams.toString();
|
|
95
|
+
return query ? `?${query}` : "";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Execute the query and return multiple results.
|
|
99
|
+
*/
|
|
100
|
+
async execute() {
|
|
101
|
+
const path = `${this.basePath}${this.buildQueryString()}`;
|
|
102
|
+
const result = await this.request("GET", path);
|
|
103
|
+
if (result.error) {
|
|
104
|
+
return { data: null, error: result.error };
|
|
105
|
+
}
|
|
106
|
+
const items = result.data[this.responseKey] || [];
|
|
107
|
+
return { data: items, error: null };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Execute the query and return a single result.
|
|
111
|
+
* Returns an error if no results found.
|
|
112
|
+
*/
|
|
113
|
+
async single() {
|
|
114
|
+
const result = await this.limit(1).execute();
|
|
115
|
+
if (result.error) {
|
|
116
|
+
return { data: null, error: result.error };
|
|
117
|
+
}
|
|
118
|
+
if (result.data.length === 0) {
|
|
119
|
+
const error = {
|
|
120
|
+
message: "No results found",
|
|
121
|
+
status: 404,
|
|
122
|
+
name: "FlowForgeError"
|
|
123
|
+
};
|
|
124
|
+
return { data: null, error };
|
|
125
|
+
}
|
|
126
|
+
return { data: result.data[0], error: null };
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/resources/events.ts
|
|
131
|
+
var EventsResource = class {
|
|
132
|
+
constructor(request) {
|
|
133
|
+
this.request = request;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Send an event to trigger matching workflows.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const { data, error } = await ff.events.send('order/created', {
|
|
141
|
+
* order_id: '123',
|
|
142
|
+
* customer: 'Alice'
|
|
143
|
+
* });
|
|
144
|
+
* if (error) console.error(error.message);
|
|
145
|
+
* else console.log('Triggered runs:', data.runs);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
async send(name, data, options) {
|
|
149
|
+
return this.request("POST", "/events", {
|
|
150
|
+
name,
|
|
151
|
+
data,
|
|
152
|
+
id: options?.id,
|
|
153
|
+
user_id: options?.user_id,
|
|
154
|
+
timestamp: options?.timestamp
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get a specific event by ID.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const { data: event } = await ff.events.get('event-id');
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
async get(eventId) {
|
|
166
|
+
return this.request("GET", `/events/${eventId}`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Start building a query to select events.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* const { data: events } = await ff.events
|
|
174
|
+
* .select()
|
|
175
|
+
* .eq('name', 'order/*')
|
|
176
|
+
* .limit(10)
|
|
177
|
+
* .execute();
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
select() {
|
|
181
|
+
return new QueryBuilder(
|
|
182
|
+
this.request,
|
|
183
|
+
"/events",
|
|
184
|
+
"events"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/resources/runs.ts
|
|
190
|
+
var RunsResource = class {
|
|
191
|
+
constructor(request) {
|
|
192
|
+
this.request = request;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get a run by ID, including its steps.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const { data: run } = await ff.runs.get('run-id');
|
|
200
|
+
* console.log(run.status, run.steps);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
async get(runId) {
|
|
204
|
+
return this.request("GET", `/runs/${runId}`);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Start building a query to select runs.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* const { data: runs } = await ff.runs
|
|
212
|
+
* .select()
|
|
213
|
+
* .eq('status', 'completed')
|
|
214
|
+
* .order('created_at', 'desc')
|
|
215
|
+
* .limit(10)
|
|
216
|
+
* .execute();
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
select() {
|
|
220
|
+
return new QueryBuilder(this.request, "/runs", "runs");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Cancel a running workflow.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* const { error } = await ff.runs.cancel('run-id');
|
|
228
|
+
* if (error) console.error('Failed to cancel:', error.message);
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
async cancel(runId) {
|
|
232
|
+
return this.request(
|
|
233
|
+
"POST",
|
|
234
|
+
`/runs/${runId}/cancel`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Replay a completed or failed run.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* const { data: newRun } = await ff.runs.replay('run-id');
|
|
243
|
+
* console.log('New run ID:', newRun.id);
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
async replay(runId) {
|
|
247
|
+
return this.request("POST", `/runs/${runId}/replay`);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Wait for a run to complete (polling).
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const { data: completedRun, error } = await ff.runs.waitFor('run-id', {
|
|
255
|
+
* timeout: 60000
|
|
256
|
+
* });
|
|
257
|
+
* if (error) console.error('Timeout or error:', error.message);
|
|
258
|
+
* else console.log('Run completed with status:', completedRun.status);
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
async waitFor(runId, options) {
|
|
262
|
+
const timeout = options?.timeout ?? 6e4;
|
|
263
|
+
const interval = options?.interval ?? 1e3;
|
|
264
|
+
const start = Date.now();
|
|
265
|
+
while (Date.now() - start < timeout) {
|
|
266
|
+
const result = await this.get(runId);
|
|
267
|
+
if (result.error) {
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
if (["completed", "failed", "cancelled"].includes(result.data.status)) {
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
274
|
+
}
|
|
275
|
+
const error = {
|
|
276
|
+
message: "Timeout waiting for run to complete",
|
|
277
|
+
status: 408,
|
|
278
|
+
name: "FlowForgeError"
|
|
279
|
+
};
|
|
280
|
+
return { data: null, error };
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/resources/functions.ts
|
|
285
|
+
var FunctionsResource = class {
|
|
286
|
+
constructor(request) {
|
|
287
|
+
this.request = request;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Get a function by ID.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const { data: fn } = await ff.functions.get('my-function');
|
|
295
|
+
* console.log(fn.name, fn.is_active);
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
async get(functionId) {
|
|
299
|
+
return this.request("GET", `/functions/${functionId}`);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Start building a query to select functions.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* const { data: fns } = await ff.functions
|
|
307
|
+
* .select()
|
|
308
|
+
* .eq('is_active', true)
|
|
309
|
+
* .eq('trigger_type', 'event')
|
|
310
|
+
* .execute();
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
select() {
|
|
314
|
+
return new QueryBuilder(
|
|
315
|
+
this.request,
|
|
316
|
+
"/functions",
|
|
317
|
+
"functions"
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Create a new function.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* const { data: fn, error } = await ff.functions.create({
|
|
326
|
+
* id: 'order-processor',
|
|
327
|
+
* name: 'Order Processor',
|
|
328
|
+
* trigger_type: 'event',
|
|
329
|
+
* trigger_value: 'order/created',
|
|
330
|
+
* endpoint_url: 'http://localhost:3000/api/workflows/order',
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
async create(input) {
|
|
335
|
+
return this.request("POST", "/functions", input);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Update an existing function.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* const { error } = await ff.functions.update('my-function', {
|
|
343
|
+
* is_active: false
|
|
344
|
+
* });
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
async update(functionId, input) {
|
|
348
|
+
return this.request(
|
|
349
|
+
"PATCH",
|
|
350
|
+
`/functions/${functionId}`,
|
|
351
|
+
input
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Delete a function.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```ts
|
|
359
|
+
* const { error } = await ff.functions.delete('my-function');
|
|
360
|
+
* if (error) console.error('Failed to delete:', error.message);
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
async delete(functionId) {
|
|
364
|
+
return this.request(
|
|
365
|
+
"DELETE",
|
|
366
|
+
`/functions/${functionId}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/resources/tools.ts
|
|
372
|
+
var ToolsResource = class {
|
|
373
|
+
constructor(request) {
|
|
374
|
+
this.request = request;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get a tool by name.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```ts
|
|
381
|
+
* const { data: tool } = await ff.tools.get('send-email');
|
|
382
|
+
* console.log(tool.description, tool.requires_approval);
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
async get(toolName) {
|
|
386
|
+
return this.request("GET", `/tools/${toolName}`);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Start building a query to select tools.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* const { data: tools } = await ff.tools
|
|
394
|
+
* .select()
|
|
395
|
+
* .eq('requires_approval', true)
|
|
396
|
+
* .eq('is_active', true)
|
|
397
|
+
* .execute();
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
select() {
|
|
401
|
+
return new QueryBuilder(this.request, "/tools", "tools");
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Create a new tool.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* const { data: tool, error } = await ff.tools.create({
|
|
409
|
+
* name: 'send-email',
|
|
410
|
+
* description: 'Send an email to a recipient',
|
|
411
|
+
* parameters: {
|
|
412
|
+
* type: 'object',
|
|
413
|
+
* properties: {
|
|
414
|
+
* to: { type: 'string', description: 'Recipient email' },
|
|
415
|
+
* subject: { type: 'string', description: 'Email subject' },
|
|
416
|
+
* body: { type: 'string', description: 'Email body' },
|
|
417
|
+
* },
|
|
418
|
+
* required: ['to', 'subject', 'body'],
|
|
419
|
+
* },
|
|
420
|
+
* requires_approval: true,
|
|
421
|
+
* });
|
|
422
|
+
* ```
|
|
423
|
+
*/
|
|
424
|
+
async create(input) {
|
|
425
|
+
return this.request("POST", "/tools", input);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Update an existing tool.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* const { error } = await ff.tools.update('send-email', {
|
|
433
|
+
* requires_approval: false
|
|
434
|
+
* });
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
async update(toolName, input) {
|
|
438
|
+
return this.request("PATCH", `/tools/${toolName}`, input);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Delete a tool.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* const { error } = await ff.tools.delete('my-tool');
|
|
446
|
+
* if (error) console.error('Failed to delete:', error.message);
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
async delete(toolName) {
|
|
450
|
+
return this.request(
|
|
451
|
+
"DELETE",
|
|
452
|
+
`/tools/${toolName}`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/resources/approvals.ts
|
|
458
|
+
var ApprovalsResource = class {
|
|
459
|
+
constructor(request) {
|
|
460
|
+
this.request = request;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get an approval by ID.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```ts
|
|
467
|
+
* const { data: approval } = await ff.approvals.get('approval-id');
|
|
468
|
+
* console.log(approval.tool_name, approval.status);
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
async get(approvalId) {
|
|
472
|
+
return this.request("GET", `/approvals/${approvalId}`);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Start building a query to select approvals.
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```ts
|
|
479
|
+
* const { data: pending } = await ff.approvals
|
|
480
|
+
* .select()
|
|
481
|
+
* .eq('status', 'pending')
|
|
482
|
+
* .execute();
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
select() {
|
|
486
|
+
return new QueryBuilder(
|
|
487
|
+
this.request,
|
|
488
|
+
"/approvals",
|
|
489
|
+
"approvals"
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Approve a pending tool call.
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```ts
|
|
497
|
+
* const { error } = await ff.approvals.approve('approval-id');
|
|
498
|
+
* if (error) console.error('Failed to approve:', error.message);
|
|
499
|
+
* ```
|
|
500
|
+
*
|
|
501
|
+
* @example With modified arguments
|
|
502
|
+
* ```ts
|
|
503
|
+
* const { error } = await ff.approvals.approve('approval-id', {
|
|
504
|
+
* modifiedArguments: { amount: 50 } // Override the original amount
|
|
505
|
+
* });
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
async approve(approvalId, options) {
|
|
509
|
+
return this.request(
|
|
510
|
+
"POST",
|
|
511
|
+
`/approvals/${approvalId}/approve`,
|
|
512
|
+
{
|
|
513
|
+
modified_arguments: options?.modifiedArguments
|
|
514
|
+
}
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Reject a pending tool call.
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* ```ts
|
|
522
|
+
* const { error } = await ff.approvals.reject('approval-id', 'Too risky');
|
|
523
|
+
* if (error) console.error('Failed to reject:', error.message);
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
async reject(approvalId, reason) {
|
|
527
|
+
return this.request(
|
|
528
|
+
"POST",
|
|
529
|
+
`/approvals/${approvalId}/reject`,
|
|
530
|
+
{ reason }
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/resources/health.ts
|
|
536
|
+
var HealthResource = class {
|
|
537
|
+
constructor(request) {
|
|
538
|
+
this.request = request;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Check if the server is healthy.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```ts
|
|
545
|
+
* const { data: healthy, error } = await ff.health.check();
|
|
546
|
+
* if (healthy) console.log('Server is healthy');
|
|
547
|
+
* else console.error('Server is unhealthy:', error?.message);
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
async check() {
|
|
551
|
+
const result = await this.request("GET", "/health");
|
|
552
|
+
if (result.error) {
|
|
553
|
+
return { data: false, error: null };
|
|
554
|
+
}
|
|
555
|
+
return { data: result.data.status === "healthy", error: null };
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get server statistics.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* const { data: stats } = await ff.health.stats();
|
|
563
|
+
* console.log('Total runs:', stats.runs.total);
|
|
564
|
+
* console.log('Active functions:', stats.functions.active);
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
async stats() {
|
|
568
|
+
return this.request("GET", "/stats");
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// src/resources/users.ts
|
|
573
|
+
var UsersResource = class {
|
|
574
|
+
constructor(request) {
|
|
575
|
+
this.request = request;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* List all users (admin only).
|
|
579
|
+
*
|
|
580
|
+
* @param options - Filter options
|
|
581
|
+
* @returns List of users
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* const { data } = await ff.users.list({ includeInactive: true });
|
|
586
|
+
* console.log('Total users:', data?.total);
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
async list(options) {
|
|
590
|
+
const params = new URLSearchParams();
|
|
591
|
+
if (options?.includeInactive) {
|
|
592
|
+
params.set("include_inactive", "true");
|
|
593
|
+
}
|
|
594
|
+
const query = params.toString();
|
|
595
|
+
return this.request(
|
|
596
|
+
"GET",
|
|
597
|
+
`/users${query ? `?${query}` : ""}`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Get a user by ID (admin only).
|
|
602
|
+
*
|
|
603
|
+
* @param userId - User ID
|
|
604
|
+
* @returns User details
|
|
605
|
+
*/
|
|
606
|
+
async get(userId) {
|
|
607
|
+
return this.request("GET", `/users/${userId}`);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Create a new user (admin only).
|
|
611
|
+
*
|
|
612
|
+
* @param input - User details
|
|
613
|
+
* @returns Created user
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* ```ts
|
|
617
|
+
* const { data, error } = await ff.users.create({
|
|
618
|
+
* email: 'user@example.com',
|
|
619
|
+
* password: 'securepassword',
|
|
620
|
+
* name: 'John Doe',
|
|
621
|
+
* role: 'member'
|
|
622
|
+
* });
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
async create(input) {
|
|
626
|
+
return this.request("POST", "/users", input);
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Update a user (admin only).
|
|
630
|
+
*
|
|
631
|
+
* @param userId - User ID
|
|
632
|
+
* @param input - Fields to update
|
|
633
|
+
* @returns Updated user
|
|
634
|
+
*/
|
|
635
|
+
async update(userId, input) {
|
|
636
|
+
return this.request("PATCH", `/users/${userId}`, input);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Delete a user (admin only).
|
|
640
|
+
*
|
|
641
|
+
* @param userId - User ID
|
|
642
|
+
* @returns Success message
|
|
643
|
+
*/
|
|
644
|
+
async delete(userId) {
|
|
645
|
+
return this.request("DELETE", `/users/${userId}`);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
// src/resources/api-keys.ts
|
|
650
|
+
var ApiKeysResource = class {
|
|
651
|
+
constructor(request) {
|
|
652
|
+
this.request = request;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* List all API keys for the tenant.
|
|
656
|
+
*
|
|
657
|
+
* @param options - Filter options
|
|
658
|
+
* @returns List of API keys (without the actual key values)
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```ts
|
|
662
|
+
* const { data } = await ff.apiKeys.list();
|
|
663
|
+
* data?.keys.forEach(key => {
|
|
664
|
+
* console.log(`${key.name}: ${key.key_prefix}... (${key.key_type})`);
|
|
665
|
+
* });
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
async list(options) {
|
|
669
|
+
const params = new URLSearchParams();
|
|
670
|
+
if (options?.includeRevoked) {
|
|
671
|
+
params.set("include_revoked", "true");
|
|
672
|
+
}
|
|
673
|
+
const query = params.toString();
|
|
674
|
+
return this.request(
|
|
675
|
+
"GET",
|
|
676
|
+
`/auth/keys${query ? `?${query}` : ""}`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Get an API key by ID.
|
|
681
|
+
*
|
|
682
|
+
* @param keyId - API key ID
|
|
683
|
+
* @returns API key details (without the actual key value)
|
|
684
|
+
*/
|
|
685
|
+
async get(keyId) {
|
|
686
|
+
return this.request("GET", `/auth/keys/${keyId}`);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Create a new API key.
|
|
690
|
+
*
|
|
691
|
+
* @param input - API key configuration
|
|
692
|
+
* @returns Created API key WITH the actual key value (only returned once!)
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```ts
|
|
696
|
+
* const { data, error } = await ff.apiKeys.create({
|
|
697
|
+
* name: 'Production API Key',
|
|
698
|
+
* key_type: 'live',
|
|
699
|
+
* scopes: ['events:send', 'runs:read']
|
|
700
|
+
* });
|
|
701
|
+
*
|
|
702
|
+
* if (data) {
|
|
703
|
+
* // IMPORTANT: Store this key - it won't be shown again!
|
|
704
|
+
* console.log('New API key:', data.key); // ff_live_a1b2c3...
|
|
705
|
+
* }
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
async create(input) {
|
|
709
|
+
return this.request("POST", "/auth/keys", input);
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Revoke an API key.
|
|
713
|
+
*
|
|
714
|
+
* @param keyId - API key ID
|
|
715
|
+
* @param reason - Optional reason for revocation
|
|
716
|
+
* @returns Success message
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```ts
|
|
720
|
+
* const { error } = await ff.apiKeys.revoke('key-id', 'Compromised');
|
|
721
|
+
* if (!error) {
|
|
722
|
+
* console.log('Key revoked successfully');
|
|
723
|
+
* }
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
async revoke(keyId, reason) {
|
|
727
|
+
return this.request(
|
|
728
|
+
"DELETE",
|
|
729
|
+
`/auth/keys/${keyId}`,
|
|
730
|
+
reason ? { reason } : void 0
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/client.ts
|
|
736
|
+
function createClient(baseUrl, options = {}) {
|
|
737
|
+
const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
|
|
738
|
+
const fetchFn = options.fetch || globalThis.fetch;
|
|
739
|
+
async function request(method, path, body) {
|
|
740
|
+
const headers = {
|
|
741
|
+
"Content-Type": "application/json"
|
|
742
|
+
};
|
|
743
|
+
if (options.apiKey) {
|
|
744
|
+
headers["X-FlowForge-API-Key"] = options.apiKey;
|
|
745
|
+
}
|
|
746
|
+
try {
|
|
747
|
+
const response = await fetchFn(`${normalizedBaseUrl}/api/v1${path}`, {
|
|
748
|
+
method,
|
|
749
|
+
headers,
|
|
750
|
+
body: body ? JSON.stringify(body) : void 0
|
|
751
|
+
});
|
|
752
|
+
if (!response.ok) {
|
|
753
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
754
|
+
const error = {
|
|
755
|
+
message: errorBody.detail || `HTTP ${response.status}`,
|
|
756
|
+
status: response.status,
|
|
757
|
+
code: errorBody.code,
|
|
758
|
+
detail: errorBody,
|
|
759
|
+
name: "FlowForgeError"
|
|
760
|
+
};
|
|
761
|
+
return { data: null, error };
|
|
762
|
+
}
|
|
763
|
+
const data = await response.json();
|
|
764
|
+
return { data, error: null };
|
|
765
|
+
} catch (err) {
|
|
766
|
+
const error = {
|
|
767
|
+
message: err instanceof Error ? err.message : "Network error",
|
|
768
|
+
status: 0,
|
|
769
|
+
code: "NETWORK_ERROR",
|
|
770
|
+
detail: err,
|
|
771
|
+
name: "FlowForgeError"
|
|
772
|
+
};
|
|
773
|
+
return { data: null, error };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
events: new EventsResource(request),
|
|
778
|
+
runs: new RunsResource(request),
|
|
779
|
+
functions: new FunctionsResource(request),
|
|
780
|
+
tools: new ToolsResource(request),
|
|
781
|
+
approvals: new ApprovalsResource(request),
|
|
782
|
+
health: new HealthResource(request),
|
|
783
|
+
users: new UsersResource(request),
|
|
784
|
+
apiKeys: new ApiKeysResource(request)
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/types.ts
|
|
789
|
+
var FlowForgeError = class extends Error {
|
|
790
|
+
constructor(message, status, code, detail) {
|
|
791
|
+
super(message);
|
|
792
|
+
this.status = status;
|
|
793
|
+
this.code = code;
|
|
794
|
+
this.detail = detail;
|
|
795
|
+
this.name = "FlowForgeError";
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
799
|
+
0 && (module.exports = {
|
|
800
|
+
ApiKeysResource,
|
|
801
|
+
ApprovalsResource,
|
|
802
|
+
EventsResource,
|
|
803
|
+
FlowForgeError,
|
|
804
|
+
FunctionsResource,
|
|
805
|
+
HealthResource,
|
|
806
|
+
QueryBuilder,
|
|
807
|
+
RunsResource,
|
|
808
|
+
ToolsResource,
|
|
809
|
+
UsersResource,
|
|
810
|
+
createClient
|
|
811
|
+
});
|