flowforge-client 0.3.1 → 0.3.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 +30 -0
- package/dist/index.d.mts +153 -2
- package/dist/index.d.ts +153 -2
- package/dist/index.js +282 -16
- package/dist/index.mjs +280 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,6 +151,30 @@ await ff.runs.cancel('run-id');
|
|
|
151
151
|
|
|
152
152
|
// Replay a failed run
|
|
153
153
|
const { data: newRun } = await ff.runs.replay('run-id');
|
|
154
|
+
|
|
155
|
+
// Stream real-time events (SSE)
|
|
156
|
+
for await (const event of ff.runs.stream('run-id')) {
|
|
157
|
+
console.log(event.type, event.data);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Stream with callbacks
|
|
161
|
+
await ff.runs.stream('run-id', {
|
|
162
|
+
onEvent: (e) => console.log(e.type),
|
|
163
|
+
onComplete: (e) => console.log('Done:', e.data),
|
|
164
|
+
onError: (e) => console.error('Error:', e.message),
|
|
165
|
+
}).drain();
|
|
166
|
+
|
|
167
|
+
// Collect all events into an array
|
|
168
|
+
const events = await ff.runs.stream('run-id').collect();
|
|
169
|
+
|
|
170
|
+
// Cancel a stream early
|
|
171
|
+
const stream = ff.runs.stream('run-id');
|
|
172
|
+
for await (const event of stream) {
|
|
173
|
+
if (event.type === 'step_completed') {
|
|
174
|
+
stream.close();
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
154
178
|
```
|
|
155
179
|
|
|
156
180
|
### Functions
|
|
@@ -310,6 +334,12 @@ import type {
|
|
|
310
334
|
UpdateToolInput,
|
|
311
335
|
SendEventInput,
|
|
312
336
|
|
|
337
|
+
// Streaming types
|
|
338
|
+
RunEventType,
|
|
339
|
+
RunStreamEvent,
|
|
340
|
+
StreamOptions,
|
|
341
|
+
StreamConfig,
|
|
342
|
+
|
|
313
343
|
// Result type
|
|
314
344
|
Result,
|
|
315
345
|
FlowForgeError,
|
package/dist/index.d.mts
CHANGED
|
@@ -68,12 +68,17 @@ interface FlowForgeFunction {
|
|
|
68
68
|
created_at: string;
|
|
69
69
|
updated_at: string;
|
|
70
70
|
}
|
|
71
|
+
type ToolType = "custom" | "webhook" | "builtin";
|
|
71
72
|
interface Tool {
|
|
72
73
|
id: string;
|
|
73
74
|
name: string;
|
|
74
75
|
description: string;
|
|
75
76
|
parameters: Record<string, unknown>;
|
|
77
|
+
tool_type: ToolType;
|
|
76
78
|
code: string | null;
|
|
79
|
+
webhook_url: string | null;
|
|
80
|
+
webhook_method: string;
|
|
81
|
+
webhook_headers: Record<string, string> | null;
|
|
77
82
|
is_builtin: boolean;
|
|
78
83
|
requires_approval: boolean;
|
|
79
84
|
approval_timeout: string | null;
|
|
@@ -183,7 +188,11 @@ interface CreateToolInput {
|
|
|
183
188
|
name: string;
|
|
184
189
|
description: string;
|
|
185
190
|
parameters: Record<string, unknown>;
|
|
191
|
+
tool_type?: ToolType;
|
|
186
192
|
code?: string;
|
|
193
|
+
webhook_url?: string;
|
|
194
|
+
webhook_method?: string;
|
|
195
|
+
webhook_headers?: Record<string, string>;
|
|
187
196
|
requires_approval?: boolean;
|
|
188
197
|
approval_timeout?: string;
|
|
189
198
|
is_active?: boolean;
|
|
@@ -191,11 +200,41 @@ interface CreateToolInput {
|
|
|
191
200
|
interface UpdateToolInput {
|
|
192
201
|
description?: string;
|
|
193
202
|
parameters?: Record<string, unknown>;
|
|
203
|
+
tool_type?: ToolType;
|
|
194
204
|
code?: string;
|
|
205
|
+
webhook_url?: string;
|
|
206
|
+
webhook_method?: string;
|
|
207
|
+
webhook_headers?: Record<string, string>;
|
|
195
208
|
requires_approval?: boolean;
|
|
196
209
|
approval_timeout?: string;
|
|
197
210
|
is_active?: boolean;
|
|
198
211
|
}
|
|
212
|
+
interface CreateCredentialInput {
|
|
213
|
+
name: string;
|
|
214
|
+
credential_type?: string;
|
|
215
|
+
value: string;
|
|
216
|
+
description?: string;
|
|
217
|
+
}
|
|
218
|
+
interface UpdateCredentialInput {
|
|
219
|
+
value?: string;
|
|
220
|
+
description?: string;
|
|
221
|
+
credential_type?: string;
|
|
222
|
+
is_active?: boolean;
|
|
223
|
+
}
|
|
224
|
+
interface Credential {
|
|
225
|
+
id: string;
|
|
226
|
+
name: string;
|
|
227
|
+
credential_type: string;
|
|
228
|
+
value_prefix: string;
|
|
229
|
+
description: string | null;
|
|
230
|
+
is_active: boolean;
|
|
231
|
+
created_at: string;
|
|
232
|
+
updated_at: string;
|
|
233
|
+
}
|
|
234
|
+
interface CredentialsResponse {
|
|
235
|
+
credentials: Credential[];
|
|
236
|
+
total: number;
|
|
237
|
+
}
|
|
199
238
|
interface SendEventInput {
|
|
200
239
|
id?: string;
|
|
201
240
|
user_id?: string;
|
|
@@ -255,6 +294,32 @@ interface ApiKeysResponse {
|
|
|
255
294
|
interface ApiKeyFilters {
|
|
256
295
|
include_revoked: boolean;
|
|
257
296
|
}
|
|
297
|
+
type RunEventType = "step_started" | "step_completed" | "step_failed" | "thinking" | "thinking_chunk" | "tool_call_started" | "tool_call_completed" | "approval_required" | "approval_resolved" | "run_started" | "run_paused" | "run_resumed" | "run_completed" | "run_failed";
|
|
298
|
+
interface RunStreamEvent {
|
|
299
|
+
type: RunEventType;
|
|
300
|
+
data: Record<string, unknown>;
|
|
301
|
+
runId: string;
|
|
302
|
+
timestamp: string;
|
|
303
|
+
}
|
|
304
|
+
interface StreamOptions {
|
|
305
|
+
/** Include historical events on connect (default: true) */
|
|
306
|
+
includeHistory?: boolean;
|
|
307
|
+
/** Server-side stream timeout in seconds (default: 300, max: 600) */
|
|
308
|
+
timeout?: number;
|
|
309
|
+
/** AbortSignal to cancel the stream */
|
|
310
|
+
signal?: AbortSignal;
|
|
311
|
+
/** Callback for each event */
|
|
312
|
+
onEvent?: (event: RunStreamEvent) => void;
|
|
313
|
+
/** Callback when run completes or fails */
|
|
314
|
+
onComplete?: (event: RunStreamEvent) => void;
|
|
315
|
+
/** Callback on stream error */
|
|
316
|
+
onError?: (error: FlowForgeError) => void;
|
|
317
|
+
}
|
|
318
|
+
interface StreamConfig {
|
|
319
|
+
baseUrl: string;
|
|
320
|
+
apiKey?: string;
|
|
321
|
+
fetchFn: typeof fetch;
|
|
322
|
+
}
|
|
258
323
|
interface ClientOptions {
|
|
259
324
|
/**
|
|
260
325
|
* API key for authentication.
|
|
@@ -401,13 +466,44 @@ declare class EventsResource {
|
|
|
401
466
|
select(): QueryBuilder<Event, EventFilters>;
|
|
402
467
|
}
|
|
403
468
|
|
|
469
|
+
/**
|
|
470
|
+
* RunStream — SSE stream wrapper for FlowForge run events.
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
declare class RunStream {
|
|
474
|
+
private runId;
|
|
475
|
+
private config;
|
|
476
|
+
private options;
|
|
477
|
+
private abortController;
|
|
478
|
+
private iteratorStarted;
|
|
479
|
+
private closed;
|
|
480
|
+
constructor(runId: string, config: StreamConfig, options?: StreamOptions);
|
|
481
|
+
/**
|
|
482
|
+
* Async iterator — use with `for await...of`.
|
|
483
|
+
*/
|
|
484
|
+
[Symbol.asyncIterator](): AsyncGenerator<RunStreamEvent>;
|
|
485
|
+
/**
|
|
486
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
487
|
+
*/
|
|
488
|
+
drain(): Promise<RunStreamEvent | null>;
|
|
489
|
+
/**
|
|
490
|
+
* Collect all events into an array.
|
|
491
|
+
*/
|
|
492
|
+
collect(): Promise<RunStreamEvent[]>;
|
|
493
|
+
/**
|
|
494
|
+
* Cancel the stream.
|
|
495
|
+
*/
|
|
496
|
+
close(): void;
|
|
497
|
+
}
|
|
498
|
+
|
|
404
499
|
/**
|
|
405
500
|
* FlowForge TypeScript Client - Runs Resource
|
|
406
501
|
*/
|
|
407
502
|
|
|
408
503
|
declare class RunsResource {
|
|
409
504
|
private request;
|
|
410
|
-
|
|
505
|
+
private streamConfig?;
|
|
506
|
+
constructor(request: RequestFn$2, streamConfig?: StreamConfig | undefined);
|
|
411
507
|
/**
|
|
412
508
|
* Get a run by ID, including its steps.
|
|
413
509
|
*
|
|
@@ -455,6 +551,25 @@ declare class RunsResource {
|
|
|
455
551
|
* ```
|
|
456
552
|
*/
|
|
457
553
|
replay(runId: string): Promise<Result<RunWithSteps>>;
|
|
554
|
+
/**
|
|
555
|
+
* Stream real-time SSE events for a run.
|
|
556
|
+
*
|
|
557
|
+
* @example Async iterator
|
|
558
|
+
* ```ts
|
|
559
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
560
|
+
* console.log(event.type, event.data);
|
|
561
|
+
* }
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @example Callback-based
|
|
565
|
+
* ```ts
|
|
566
|
+
* await ff.runs.stream('run-id', {
|
|
567
|
+
* onEvent: (e) => console.log(e.type),
|
|
568
|
+
* onComplete: (e) => console.log('Done'),
|
|
569
|
+
* }).drain();
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
stream(runId: string, options?: StreamOptions): RunStream;
|
|
458
573
|
/**
|
|
459
574
|
* Wait for a run to complete (polling).
|
|
460
575
|
*
|
|
@@ -872,6 +987,40 @@ declare class ApiKeysResource {
|
|
|
872
987
|
}>>;
|
|
873
988
|
}
|
|
874
989
|
|
|
990
|
+
/**
|
|
991
|
+
* FlowForge TypeScript Client - Credentials Resource
|
|
992
|
+
*/
|
|
993
|
+
|
|
994
|
+
declare class CredentialsResource {
|
|
995
|
+
private request;
|
|
996
|
+
constructor(request: RequestFn$2);
|
|
997
|
+
/**
|
|
998
|
+
* List all credentials for the tenant.
|
|
999
|
+
* Values are never returned — only masked prefixes.
|
|
1000
|
+
*/
|
|
1001
|
+
list(): Promise<Result<CredentialsResponse>>;
|
|
1002
|
+
/**
|
|
1003
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
1004
|
+
*/
|
|
1005
|
+
get(name: string): Promise<Result<Credential>>;
|
|
1006
|
+
/**
|
|
1007
|
+
* Create a new encrypted credential.
|
|
1008
|
+
*/
|
|
1009
|
+
create(input: CreateCredentialInput): Promise<Result<Credential>>;
|
|
1010
|
+
/**
|
|
1011
|
+
* Update an existing credential.
|
|
1012
|
+
* If value is provided, it will be re-encrypted.
|
|
1013
|
+
*/
|
|
1014
|
+
update(name: string, input: UpdateCredentialInput): Promise<Result<Credential>>;
|
|
1015
|
+
/**
|
|
1016
|
+
* Delete a credential.
|
|
1017
|
+
*/
|
|
1018
|
+
delete(name: string): Promise<Result<{
|
|
1019
|
+
success: boolean;
|
|
1020
|
+
message: string;
|
|
1021
|
+
}>>;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
875
1024
|
/**
|
|
876
1025
|
* FlowForge TypeScript Client - Main Client Factory
|
|
877
1026
|
*/
|
|
@@ -896,6 +1045,8 @@ interface FlowForgeClient {
|
|
|
896
1045
|
users: UsersResource;
|
|
897
1046
|
/** API Keys resource - manage API key authentication */
|
|
898
1047
|
apiKeys: ApiKeysResource;
|
|
1048
|
+
/** Credentials resource - manage encrypted service credentials */
|
|
1049
|
+
credentials: CredentialsResource;
|
|
899
1050
|
}
|
|
900
1051
|
/**
|
|
901
1052
|
* Create a FlowForge client instance.
|
|
@@ -931,4 +1082,4 @@ interface FlowForgeClient {
|
|
|
931
1082
|
*/
|
|
932
1083
|
declare function createClient(baseUrl: string, options?: ClientOptions): FlowForgeClient;
|
|
933
1084
|
|
|
934
|
-
export { type ApiKey, type ApiKeyCreated, type ApiKeyFilters, type ApiKeyType, ApiKeysResource, type ApiKeysResponse, type Approval, type ApprovalFilters, type ApprovalStatus, ApprovalsResource, type ClientOptions, type CreateApiKeyInput, type CreateFunctionInput, type CreateToolInput, type CreateUserInput, type Event, type EventFilters, EventsResource, type FlowForgeClient, FlowForgeError, type FlowForgeFunction, type FunctionFilters, FunctionsResource, HealthResource, type HealthStatus, type OrderDirection, QueryBuilder, type QueryParams, type Result, type Run, type RunFilters, type RunStatus, type RunWithSteps, RunsResource, type SendEventInput, type SendEventResponse, type Stats, type Step, type StepStatus, type StepType, type Tool, type ToolFilters, ToolsResource, type TriggerType, type UpdateFunctionInput, type UpdateToolInput, type UpdateUserInput, type User, type UserRole, UsersResource, type UsersResponse, createClient };
|
|
1085
|
+
export { type ApiKey, type ApiKeyCreated, type ApiKeyFilters, type ApiKeyType, ApiKeysResource, type ApiKeysResponse, type Approval, type ApprovalFilters, type ApprovalStatus, ApprovalsResource, type ClientOptions, type CreateApiKeyInput, type CreateCredentialInput, type CreateFunctionInput, type CreateToolInput, type CreateUserInput, type Credential, CredentialsResource, type CredentialsResponse, type Event, type EventFilters, EventsResource, type FlowForgeClient, FlowForgeError, type FlowForgeFunction, type FunctionFilters, FunctionsResource, HealthResource, type HealthStatus, type OrderDirection, QueryBuilder, type QueryParams, type Result, type Run, type RunEventType, type RunFilters, type RunStatus, RunStream, type RunStreamEvent, type RunWithSteps, RunsResource, type SendEventInput, type SendEventResponse, type Stats, type Step, type StepStatus, type StepType, type StreamConfig, type StreamOptions, type Tool, type ToolFilters, type ToolType, ToolsResource, type TriggerType, type UpdateCredentialInput, type UpdateFunctionInput, type UpdateToolInput, type UpdateUserInput, type User, type UserRole, UsersResource, type UsersResponse, createClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -68,12 +68,17 @@ interface FlowForgeFunction {
|
|
|
68
68
|
created_at: string;
|
|
69
69
|
updated_at: string;
|
|
70
70
|
}
|
|
71
|
+
type ToolType = "custom" | "webhook" | "builtin";
|
|
71
72
|
interface Tool {
|
|
72
73
|
id: string;
|
|
73
74
|
name: string;
|
|
74
75
|
description: string;
|
|
75
76
|
parameters: Record<string, unknown>;
|
|
77
|
+
tool_type: ToolType;
|
|
76
78
|
code: string | null;
|
|
79
|
+
webhook_url: string | null;
|
|
80
|
+
webhook_method: string;
|
|
81
|
+
webhook_headers: Record<string, string> | null;
|
|
77
82
|
is_builtin: boolean;
|
|
78
83
|
requires_approval: boolean;
|
|
79
84
|
approval_timeout: string | null;
|
|
@@ -183,7 +188,11 @@ interface CreateToolInput {
|
|
|
183
188
|
name: string;
|
|
184
189
|
description: string;
|
|
185
190
|
parameters: Record<string, unknown>;
|
|
191
|
+
tool_type?: ToolType;
|
|
186
192
|
code?: string;
|
|
193
|
+
webhook_url?: string;
|
|
194
|
+
webhook_method?: string;
|
|
195
|
+
webhook_headers?: Record<string, string>;
|
|
187
196
|
requires_approval?: boolean;
|
|
188
197
|
approval_timeout?: string;
|
|
189
198
|
is_active?: boolean;
|
|
@@ -191,11 +200,41 @@ interface CreateToolInput {
|
|
|
191
200
|
interface UpdateToolInput {
|
|
192
201
|
description?: string;
|
|
193
202
|
parameters?: Record<string, unknown>;
|
|
203
|
+
tool_type?: ToolType;
|
|
194
204
|
code?: string;
|
|
205
|
+
webhook_url?: string;
|
|
206
|
+
webhook_method?: string;
|
|
207
|
+
webhook_headers?: Record<string, string>;
|
|
195
208
|
requires_approval?: boolean;
|
|
196
209
|
approval_timeout?: string;
|
|
197
210
|
is_active?: boolean;
|
|
198
211
|
}
|
|
212
|
+
interface CreateCredentialInput {
|
|
213
|
+
name: string;
|
|
214
|
+
credential_type?: string;
|
|
215
|
+
value: string;
|
|
216
|
+
description?: string;
|
|
217
|
+
}
|
|
218
|
+
interface UpdateCredentialInput {
|
|
219
|
+
value?: string;
|
|
220
|
+
description?: string;
|
|
221
|
+
credential_type?: string;
|
|
222
|
+
is_active?: boolean;
|
|
223
|
+
}
|
|
224
|
+
interface Credential {
|
|
225
|
+
id: string;
|
|
226
|
+
name: string;
|
|
227
|
+
credential_type: string;
|
|
228
|
+
value_prefix: string;
|
|
229
|
+
description: string | null;
|
|
230
|
+
is_active: boolean;
|
|
231
|
+
created_at: string;
|
|
232
|
+
updated_at: string;
|
|
233
|
+
}
|
|
234
|
+
interface CredentialsResponse {
|
|
235
|
+
credentials: Credential[];
|
|
236
|
+
total: number;
|
|
237
|
+
}
|
|
199
238
|
interface SendEventInput {
|
|
200
239
|
id?: string;
|
|
201
240
|
user_id?: string;
|
|
@@ -255,6 +294,32 @@ interface ApiKeysResponse {
|
|
|
255
294
|
interface ApiKeyFilters {
|
|
256
295
|
include_revoked: boolean;
|
|
257
296
|
}
|
|
297
|
+
type RunEventType = "step_started" | "step_completed" | "step_failed" | "thinking" | "thinking_chunk" | "tool_call_started" | "tool_call_completed" | "approval_required" | "approval_resolved" | "run_started" | "run_paused" | "run_resumed" | "run_completed" | "run_failed";
|
|
298
|
+
interface RunStreamEvent {
|
|
299
|
+
type: RunEventType;
|
|
300
|
+
data: Record<string, unknown>;
|
|
301
|
+
runId: string;
|
|
302
|
+
timestamp: string;
|
|
303
|
+
}
|
|
304
|
+
interface StreamOptions {
|
|
305
|
+
/** Include historical events on connect (default: true) */
|
|
306
|
+
includeHistory?: boolean;
|
|
307
|
+
/** Server-side stream timeout in seconds (default: 300, max: 600) */
|
|
308
|
+
timeout?: number;
|
|
309
|
+
/** AbortSignal to cancel the stream */
|
|
310
|
+
signal?: AbortSignal;
|
|
311
|
+
/** Callback for each event */
|
|
312
|
+
onEvent?: (event: RunStreamEvent) => void;
|
|
313
|
+
/** Callback when run completes or fails */
|
|
314
|
+
onComplete?: (event: RunStreamEvent) => void;
|
|
315
|
+
/** Callback on stream error */
|
|
316
|
+
onError?: (error: FlowForgeError) => void;
|
|
317
|
+
}
|
|
318
|
+
interface StreamConfig {
|
|
319
|
+
baseUrl: string;
|
|
320
|
+
apiKey?: string;
|
|
321
|
+
fetchFn: typeof fetch;
|
|
322
|
+
}
|
|
258
323
|
interface ClientOptions {
|
|
259
324
|
/**
|
|
260
325
|
* API key for authentication.
|
|
@@ -401,13 +466,44 @@ declare class EventsResource {
|
|
|
401
466
|
select(): QueryBuilder<Event, EventFilters>;
|
|
402
467
|
}
|
|
403
468
|
|
|
469
|
+
/**
|
|
470
|
+
* RunStream — SSE stream wrapper for FlowForge run events.
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
declare class RunStream {
|
|
474
|
+
private runId;
|
|
475
|
+
private config;
|
|
476
|
+
private options;
|
|
477
|
+
private abortController;
|
|
478
|
+
private iteratorStarted;
|
|
479
|
+
private closed;
|
|
480
|
+
constructor(runId: string, config: StreamConfig, options?: StreamOptions);
|
|
481
|
+
/**
|
|
482
|
+
* Async iterator — use with `for await...of`.
|
|
483
|
+
*/
|
|
484
|
+
[Symbol.asyncIterator](): AsyncGenerator<RunStreamEvent>;
|
|
485
|
+
/**
|
|
486
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
487
|
+
*/
|
|
488
|
+
drain(): Promise<RunStreamEvent | null>;
|
|
489
|
+
/**
|
|
490
|
+
* Collect all events into an array.
|
|
491
|
+
*/
|
|
492
|
+
collect(): Promise<RunStreamEvent[]>;
|
|
493
|
+
/**
|
|
494
|
+
* Cancel the stream.
|
|
495
|
+
*/
|
|
496
|
+
close(): void;
|
|
497
|
+
}
|
|
498
|
+
|
|
404
499
|
/**
|
|
405
500
|
* FlowForge TypeScript Client - Runs Resource
|
|
406
501
|
*/
|
|
407
502
|
|
|
408
503
|
declare class RunsResource {
|
|
409
504
|
private request;
|
|
410
|
-
|
|
505
|
+
private streamConfig?;
|
|
506
|
+
constructor(request: RequestFn$2, streamConfig?: StreamConfig | undefined);
|
|
411
507
|
/**
|
|
412
508
|
* Get a run by ID, including its steps.
|
|
413
509
|
*
|
|
@@ -455,6 +551,25 @@ declare class RunsResource {
|
|
|
455
551
|
* ```
|
|
456
552
|
*/
|
|
457
553
|
replay(runId: string): Promise<Result<RunWithSteps>>;
|
|
554
|
+
/**
|
|
555
|
+
* Stream real-time SSE events for a run.
|
|
556
|
+
*
|
|
557
|
+
* @example Async iterator
|
|
558
|
+
* ```ts
|
|
559
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
560
|
+
* console.log(event.type, event.data);
|
|
561
|
+
* }
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @example Callback-based
|
|
565
|
+
* ```ts
|
|
566
|
+
* await ff.runs.stream('run-id', {
|
|
567
|
+
* onEvent: (e) => console.log(e.type),
|
|
568
|
+
* onComplete: (e) => console.log('Done'),
|
|
569
|
+
* }).drain();
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
stream(runId: string, options?: StreamOptions): RunStream;
|
|
458
573
|
/**
|
|
459
574
|
* Wait for a run to complete (polling).
|
|
460
575
|
*
|
|
@@ -872,6 +987,40 @@ declare class ApiKeysResource {
|
|
|
872
987
|
}>>;
|
|
873
988
|
}
|
|
874
989
|
|
|
990
|
+
/**
|
|
991
|
+
* FlowForge TypeScript Client - Credentials Resource
|
|
992
|
+
*/
|
|
993
|
+
|
|
994
|
+
declare class CredentialsResource {
|
|
995
|
+
private request;
|
|
996
|
+
constructor(request: RequestFn$2);
|
|
997
|
+
/**
|
|
998
|
+
* List all credentials for the tenant.
|
|
999
|
+
* Values are never returned — only masked prefixes.
|
|
1000
|
+
*/
|
|
1001
|
+
list(): Promise<Result<CredentialsResponse>>;
|
|
1002
|
+
/**
|
|
1003
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
1004
|
+
*/
|
|
1005
|
+
get(name: string): Promise<Result<Credential>>;
|
|
1006
|
+
/**
|
|
1007
|
+
* Create a new encrypted credential.
|
|
1008
|
+
*/
|
|
1009
|
+
create(input: CreateCredentialInput): Promise<Result<Credential>>;
|
|
1010
|
+
/**
|
|
1011
|
+
* Update an existing credential.
|
|
1012
|
+
* If value is provided, it will be re-encrypted.
|
|
1013
|
+
*/
|
|
1014
|
+
update(name: string, input: UpdateCredentialInput): Promise<Result<Credential>>;
|
|
1015
|
+
/**
|
|
1016
|
+
* Delete a credential.
|
|
1017
|
+
*/
|
|
1018
|
+
delete(name: string): Promise<Result<{
|
|
1019
|
+
success: boolean;
|
|
1020
|
+
message: string;
|
|
1021
|
+
}>>;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
875
1024
|
/**
|
|
876
1025
|
* FlowForge TypeScript Client - Main Client Factory
|
|
877
1026
|
*/
|
|
@@ -896,6 +1045,8 @@ interface FlowForgeClient {
|
|
|
896
1045
|
users: UsersResource;
|
|
897
1046
|
/** API Keys resource - manage API key authentication */
|
|
898
1047
|
apiKeys: ApiKeysResource;
|
|
1048
|
+
/** Credentials resource - manage encrypted service credentials */
|
|
1049
|
+
credentials: CredentialsResource;
|
|
899
1050
|
}
|
|
900
1051
|
/**
|
|
901
1052
|
* Create a FlowForge client instance.
|
|
@@ -931,4 +1082,4 @@ interface FlowForgeClient {
|
|
|
931
1082
|
*/
|
|
932
1083
|
declare function createClient(baseUrl: string, options?: ClientOptions): FlowForgeClient;
|
|
933
1084
|
|
|
934
|
-
export { type ApiKey, type ApiKeyCreated, type ApiKeyFilters, type ApiKeyType, ApiKeysResource, type ApiKeysResponse, type Approval, type ApprovalFilters, type ApprovalStatus, ApprovalsResource, type ClientOptions, type CreateApiKeyInput, type CreateFunctionInput, type CreateToolInput, type CreateUserInput, type Event, type EventFilters, EventsResource, type FlowForgeClient, FlowForgeError, type FlowForgeFunction, type FunctionFilters, FunctionsResource, HealthResource, type HealthStatus, type OrderDirection, QueryBuilder, type QueryParams, type Result, type Run, type RunFilters, type RunStatus, type RunWithSteps, RunsResource, type SendEventInput, type SendEventResponse, type Stats, type Step, type StepStatus, type StepType, type Tool, type ToolFilters, ToolsResource, type TriggerType, type UpdateFunctionInput, type UpdateToolInput, type UpdateUserInput, type User, type UserRole, UsersResource, type UsersResponse, createClient };
|
|
1085
|
+
export { type ApiKey, type ApiKeyCreated, type ApiKeyFilters, type ApiKeyType, ApiKeysResource, type ApiKeysResponse, type Approval, type ApprovalFilters, type ApprovalStatus, ApprovalsResource, type ClientOptions, type CreateApiKeyInput, type CreateCredentialInput, type CreateFunctionInput, type CreateToolInput, type CreateUserInput, type Credential, CredentialsResource, type CredentialsResponse, type Event, type EventFilters, EventsResource, type FlowForgeClient, FlowForgeError, type FlowForgeFunction, type FunctionFilters, FunctionsResource, HealthResource, type HealthStatus, type OrderDirection, QueryBuilder, type QueryParams, type Result, type Run, type RunEventType, type RunFilters, type RunStatus, RunStream, type RunStreamEvent, type RunWithSteps, RunsResource, type SendEventInput, type SendEventResponse, type Stats, type Step, type StepStatus, type StepType, type StreamConfig, type StreamOptions, type Tool, type ToolFilters, type ToolType, ToolsResource, type TriggerType, type UpdateCredentialInput, type UpdateFunctionInput, type UpdateToolInput, type UpdateUserInput, type User, type UserRole, UsersResource, type UsersResponse, createClient };
|
package/dist/index.js
CHANGED
|
@@ -22,11 +22,13 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ApiKeysResource: () => ApiKeysResource,
|
|
24
24
|
ApprovalsResource: () => ApprovalsResource,
|
|
25
|
+
CredentialsResource: () => CredentialsResource,
|
|
25
26
|
EventsResource: () => EventsResource,
|
|
26
27
|
FlowForgeError: () => FlowForgeError,
|
|
27
28
|
FunctionsResource: () => FunctionsResource,
|
|
28
29
|
HealthResource: () => HealthResource,
|
|
29
30
|
QueryBuilder: () => QueryBuilder,
|
|
31
|
+
RunStream: () => RunStream,
|
|
30
32
|
RunsResource: () => RunsResource,
|
|
31
33
|
ToolsResource: () => ToolsResource,
|
|
32
34
|
UsersResource: () => UsersResource,
|
|
@@ -186,10 +188,212 @@ var EventsResource = class {
|
|
|
186
188
|
}
|
|
187
189
|
};
|
|
188
190
|
|
|
191
|
+
// src/sse-parser.ts
|
|
192
|
+
async function* parseSSEStream(body) {
|
|
193
|
+
const reader = body.getReader();
|
|
194
|
+
const decoder = new TextDecoder();
|
|
195
|
+
let buffer = "";
|
|
196
|
+
let eventType = "";
|
|
197
|
+
const dataLines = [];
|
|
198
|
+
try {
|
|
199
|
+
while (true) {
|
|
200
|
+
const { done, value } = await reader.read();
|
|
201
|
+
if (done) break;
|
|
202
|
+
buffer += decoder.decode(value, { stream: true });
|
|
203
|
+
const lines = buffer.split("\n");
|
|
204
|
+
buffer = lines.pop() ?? "";
|
|
205
|
+
for (const line of lines) {
|
|
206
|
+
if (line.startsWith(":")) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (line === "") {
|
|
210
|
+
if (eventType && dataLines.length > 0) {
|
|
211
|
+
yield { event: eventType, data: dataLines.join("\n") };
|
|
212
|
+
}
|
|
213
|
+
eventType = "";
|
|
214
|
+
dataLines.length = 0;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (line.startsWith("event:")) {
|
|
218
|
+
eventType = line.slice(6).trim();
|
|
219
|
+
} else if (line.startsWith("data:")) {
|
|
220
|
+
dataLines.push(line.slice(5).trim());
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (eventType && dataLines.length > 0) {
|
|
225
|
+
yield { event: eventType, data: dataLines.join("\n") };
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
reader.releaseLock();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/types.ts
|
|
233
|
+
var FlowForgeError = class extends Error {
|
|
234
|
+
constructor(message, status, code, detail) {
|
|
235
|
+
super(message);
|
|
236
|
+
this.status = status;
|
|
237
|
+
this.code = code;
|
|
238
|
+
this.detail = detail;
|
|
239
|
+
this.name = "FlowForgeError";
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/stream.ts
|
|
244
|
+
var TERMINAL_EVENTS = /* @__PURE__ */ new Set([
|
|
245
|
+
"run_completed",
|
|
246
|
+
"run_failed"
|
|
247
|
+
]);
|
|
248
|
+
var RunStream = class {
|
|
249
|
+
constructor(runId, config, options = {}) {
|
|
250
|
+
this.runId = runId;
|
|
251
|
+
this.config = config;
|
|
252
|
+
this.options = options;
|
|
253
|
+
this.iteratorStarted = false;
|
|
254
|
+
this.closed = false;
|
|
255
|
+
this.abortController = new AbortController();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Async iterator — use with `for await...of`.
|
|
259
|
+
*/
|
|
260
|
+
async *[Symbol.asyncIterator]() {
|
|
261
|
+
if (this.iteratorStarted) {
|
|
262
|
+
throw new FlowForgeError("Stream already consumed", 0, "STREAM_CONSUMED");
|
|
263
|
+
}
|
|
264
|
+
this.iteratorStarted = true;
|
|
265
|
+
const includeHistory = this.options.includeHistory ?? true;
|
|
266
|
+
const timeout = this.options.timeout ?? 300;
|
|
267
|
+
const params = new URLSearchParams({
|
|
268
|
+
include_history: String(includeHistory),
|
|
269
|
+
timeout: String(timeout)
|
|
270
|
+
});
|
|
271
|
+
const url = `${this.config.baseUrl}/api/v1/runs/${this.runId}/stream?${params}`;
|
|
272
|
+
const headers = {
|
|
273
|
+
Accept: "text/event-stream"
|
|
274
|
+
};
|
|
275
|
+
if (this.config.apiKey) {
|
|
276
|
+
headers["X-FlowForge-API-Key"] = this.config.apiKey;
|
|
277
|
+
}
|
|
278
|
+
const signals = [this.abortController.signal];
|
|
279
|
+
if (this.options.signal) {
|
|
280
|
+
signals.push(this.options.signal);
|
|
281
|
+
}
|
|
282
|
+
let response;
|
|
283
|
+
try {
|
|
284
|
+
response = await this.config.fetchFn(url, {
|
|
285
|
+
method: "GET",
|
|
286
|
+
headers,
|
|
287
|
+
signal: signals.length === 1 ? signals[0] : anySignal(signals)
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
const error = new FlowForgeError(
|
|
291
|
+
err instanceof Error ? err.message : "Stream connection failed",
|
|
292
|
+
0,
|
|
293
|
+
"STREAM_ERROR"
|
|
294
|
+
);
|
|
295
|
+
this.options.onError?.(error);
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
const body = await response.text().catch(() => "");
|
|
300
|
+
const error = new FlowForgeError(
|
|
301
|
+
body || `HTTP ${response.status}`,
|
|
302
|
+
response.status,
|
|
303
|
+
"STREAM_HTTP_ERROR"
|
|
304
|
+
);
|
|
305
|
+
this.options.onError?.(error);
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
if (!response.body) {
|
|
309
|
+
const error = new FlowForgeError(
|
|
310
|
+
"Response body is empty",
|
|
311
|
+
0,
|
|
312
|
+
"STREAM_EMPTY"
|
|
313
|
+
);
|
|
314
|
+
this.options.onError?.(error);
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
for await (const sse of parseSSEStream(response.body)) {
|
|
319
|
+
if (this.closed) return;
|
|
320
|
+
let data;
|
|
321
|
+
try {
|
|
322
|
+
data = JSON.parse(sse.data);
|
|
323
|
+
} catch {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const event = {
|
|
327
|
+
type: sse.event,
|
|
328
|
+
data,
|
|
329
|
+
runId: this.runId,
|
|
330
|
+
timestamp: data.timestamp ?? data.ts ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
331
|
+
};
|
|
332
|
+
this.options.onEvent?.(event);
|
|
333
|
+
yield event;
|
|
334
|
+
if (TERMINAL_EVENTS.has(event.type)) {
|
|
335
|
+
this.options.onComplete?.(event);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
} catch (err) {
|
|
340
|
+
if (this.closed) return;
|
|
341
|
+
const error = new FlowForgeError(
|
|
342
|
+
err instanceof Error ? err.message : "Stream error",
|
|
343
|
+
0,
|
|
344
|
+
"STREAM_ERROR"
|
|
345
|
+
);
|
|
346
|
+
this.options.onError?.(error);
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
352
|
+
*/
|
|
353
|
+
async drain() {
|
|
354
|
+
let last = null;
|
|
355
|
+
for await (const event of this) {
|
|
356
|
+
last = event;
|
|
357
|
+
}
|
|
358
|
+
return last;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Collect all events into an array.
|
|
362
|
+
*/
|
|
363
|
+
async collect() {
|
|
364
|
+
const events = [];
|
|
365
|
+
for await (const event of this) {
|
|
366
|
+
events.push(event);
|
|
367
|
+
}
|
|
368
|
+
return events;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Cancel the stream.
|
|
372
|
+
*/
|
|
373
|
+
close() {
|
|
374
|
+
this.closed = true;
|
|
375
|
+
this.abortController.abort();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
function anySignal(signals) {
|
|
379
|
+
const controller = new AbortController();
|
|
380
|
+
for (const signal of signals) {
|
|
381
|
+
if (signal.aborted) {
|
|
382
|
+
controller.abort(signal.reason);
|
|
383
|
+
return controller.signal;
|
|
384
|
+
}
|
|
385
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
386
|
+
once: true
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return controller.signal;
|
|
390
|
+
}
|
|
391
|
+
|
|
189
392
|
// src/resources/runs.ts
|
|
190
393
|
var RunsResource = class {
|
|
191
|
-
constructor(request) {
|
|
394
|
+
constructor(request, streamConfig) {
|
|
192
395
|
this.request = request;
|
|
396
|
+
this.streamConfig = streamConfig;
|
|
193
397
|
}
|
|
194
398
|
/**
|
|
195
399
|
* Get a run by ID, including its steps.
|
|
@@ -246,6 +450,30 @@ var RunsResource = class {
|
|
|
246
450
|
async replay(runId) {
|
|
247
451
|
return this.request("POST", `/runs/${runId}/replay`);
|
|
248
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Stream real-time SSE events for a run.
|
|
455
|
+
*
|
|
456
|
+
* @example Async iterator
|
|
457
|
+
* ```ts
|
|
458
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
459
|
+
* console.log(event.type, event.data);
|
|
460
|
+
* }
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* @example Callback-based
|
|
464
|
+
* ```ts
|
|
465
|
+
* await ff.runs.stream('run-id', {
|
|
466
|
+
* onEvent: (e) => console.log(e.type),
|
|
467
|
+
* onComplete: (e) => console.log('Done'),
|
|
468
|
+
* }).drain();
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
stream(runId, options) {
|
|
472
|
+
if (!this.streamConfig) {
|
|
473
|
+
throw new Error("Stream config not available");
|
|
474
|
+
}
|
|
475
|
+
return new RunStream(runId, this.streamConfig, options);
|
|
476
|
+
}
|
|
249
477
|
/**
|
|
250
478
|
* Wait for a run to complete (polling).
|
|
251
479
|
*
|
|
@@ -732,9 +960,51 @@ var ApiKeysResource = class {
|
|
|
732
960
|
}
|
|
733
961
|
};
|
|
734
962
|
|
|
963
|
+
// src/resources/credentials.ts
|
|
964
|
+
var CredentialsResource = class {
|
|
965
|
+
constructor(request) {
|
|
966
|
+
this.request = request;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* List all credentials for the tenant.
|
|
970
|
+
* Values are never returned — only masked prefixes.
|
|
971
|
+
*/
|
|
972
|
+
async list() {
|
|
973
|
+
return this.request("GET", "/credentials");
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
977
|
+
*/
|
|
978
|
+
async get(name) {
|
|
979
|
+
return this.request("GET", `/credentials/${name}`);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Create a new encrypted credential.
|
|
983
|
+
*/
|
|
984
|
+
async create(input) {
|
|
985
|
+
return this.request("POST", "/credentials", input);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Update an existing credential.
|
|
989
|
+
* If value is provided, it will be re-encrypted.
|
|
990
|
+
*/
|
|
991
|
+
async update(name, input) {
|
|
992
|
+
return this.request("PATCH", `/credentials/${name}`, input);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Delete a credential.
|
|
996
|
+
*/
|
|
997
|
+
async delete(name) {
|
|
998
|
+
return this.request(
|
|
999
|
+
"DELETE",
|
|
1000
|
+
`/credentials/${name}`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
|
|
735
1005
|
// src/client.ts
|
|
736
1006
|
var DEFAULT_TIMEOUT = 3e4;
|
|
737
|
-
function
|
|
1007
|
+
function anySignal2(signals) {
|
|
738
1008
|
const controller = new AbortController();
|
|
739
1009
|
for (const signal of signals) {
|
|
740
1010
|
if (signal.aborted) {
|
|
@@ -778,7 +1048,7 @@ function createClient(baseUrl, options = {}) {
|
|
|
778
1048
|
}
|
|
779
1049
|
const timeoutController = new AbortController();
|
|
780
1050
|
const timeoutId = setTimeout(() => timeoutController.abort(), timeout);
|
|
781
|
-
const combinedSignal = signal ?
|
|
1051
|
+
const combinedSignal = signal ? anySignal2([signal, timeoutController.signal]) : timeoutController.signal;
|
|
782
1052
|
try {
|
|
783
1053
|
const response = await fetchFn(`${normalizedBaseUrl}/api/v1${path}`, {
|
|
784
1054
|
method,
|
|
@@ -874,35 +1144,31 @@ function createClient(baseUrl, options = {}) {
|
|
|
874
1144
|
}
|
|
875
1145
|
return {
|
|
876
1146
|
events: new EventsResource(request),
|
|
877
|
-
runs: new RunsResource(request
|
|
1147
|
+
runs: new RunsResource(request, {
|
|
1148
|
+
baseUrl: normalizedBaseUrl,
|
|
1149
|
+
apiKey: options.apiKey,
|
|
1150
|
+
fetchFn
|
|
1151
|
+
}),
|
|
878
1152
|
functions: new FunctionsResource(request),
|
|
879
1153
|
tools: new ToolsResource(request),
|
|
880
1154
|
approvals: new ApprovalsResource(request),
|
|
881
1155
|
health: new HealthResource(request),
|
|
882
1156
|
users: new UsersResource(request),
|
|
883
|
-
apiKeys: new ApiKeysResource(request)
|
|
1157
|
+
apiKeys: new ApiKeysResource(request),
|
|
1158
|
+
credentials: new CredentialsResource(request)
|
|
884
1159
|
};
|
|
885
1160
|
}
|
|
886
|
-
|
|
887
|
-
// src/types.ts
|
|
888
|
-
var FlowForgeError = class extends Error {
|
|
889
|
-
constructor(message, status, code, detail) {
|
|
890
|
-
super(message);
|
|
891
|
-
this.status = status;
|
|
892
|
-
this.code = code;
|
|
893
|
-
this.detail = detail;
|
|
894
|
-
this.name = "FlowForgeError";
|
|
895
|
-
}
|
|
896
|
-
};
|
|
897
1161
|
// Annotate the CommonJS export names for ESM import in node:
|
|
898
1162
|
0 && (module.exports = {
|
|
899
1163
|
ApiKeysResource,
|
|
900
1164
|
ApprovalsResource,
|
|
1165
|
+
CredentialsResource,
|
|
901
1166
|
EventsResource,
|
|
902
1167
|
FlowForgeError,
|
|
903
1168
|
FunctionsResource,
|
|
904
1169
|
HealthResource,
|
|
905
1170
|
QueryBuilder,
|
|
1171
|
+
RunStream,
|
|
906
1172
|
RunsResource,
|
|
907
1173
|
ToolsResource,
|
|
908
1174
|
UsersResource,
|
package/dist/index.mjs
CHANGED
|
@@ -150,10 +150,212 @@ var EventsResource = class {
|
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
+
// src/sse-parser.ts
|
|
154
|
+
async function* parseSSEStream(body) {
|
|
155
|
+
const reader = body.getReader();
|
|
156
|
+
const decoder = new TextDecoder();
|
|
157
|
+
let buffer = "";
|
|
158
|
+
let eventType = "";
|
|
159
|
+
const dataLines = [];
|
|
160
|
+
try {
|
|
161
|
+
while (true) {
|
|
162
|
+
const { done, value } = await reader.read();
|
|
163
|
+
if (done) break;
|
|
164
|
+
buffer += decoder.decode(value, { stream: true });
|
|
165
|
+
const lines = buffer.split("\n");
|
|
166
|
+
buffer = lines.pop() ?? "";
|
|
167
|
+
for (const line of lines) {
|
|
168
|
+
if (line.startsWith(":")) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (line === "") {
|
|
172
|
+
if (eventType && dataLines.length > 0) {
|
|
173
|
+
yield { event: eventType, data: dataLines.join("\n") };
|
|
174
|
+
}
|
|
175
|
+
eventType = "";
|
|
176
|
+
dataLines.length = 0;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (line.startsWith("event:")) {
|
|
180
|
+
eventType = line.slice(6).trim();
|
|
181
|
+
} else if (line.startsWith("data:")) {
|
|
182
|
+
dataLines.push(line.slice(5).trim());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (eventType && dataLines.length > 0) {
|
|
187
|
+
yield { event: eventType, data: dataLines.join("\n") };
|
|
188
|
+
}
|
|
189
|
+
} finally {
|
|
190
|
+
reader.releaseLock();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/types.ts
|
|
195
|
+
var FlowForgeError = class extends Error {
|
|
196
|
+
constructor(message, status, code, detail) {
|
|
197
|
+
super(message);
|
|
198
|
+
this.status = status;
|
|
199
|
+
this.code = code;
|
|
200
|
+
this.detail = detail;
|
|
201
|
+
this.name = "FlowForgeError";
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/stream.ts
|
|
206
|
+
var TERMINAL_EVENTS = /* @__PURE__ */ new Set([
|
|
207
|
+
"run_completed",
|
|
208
|
+
"run_failed"
|
|
209
|
+
]);
|
|
210
|
+
var RunStream = class {
|
|
211
|
+
constructor(runId, config, options = {}) {
|
|
212
|
+
this.runId = runId;
|
|
213
|
+
this.config = config;
|
|
214
|
+
this.options = options;
|
|
215
|
+
this.iteratorStarted = false;
|
|
216
|
+
this.closed = false;
|
|
217
|
+
this.abortController = new AbortController();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Async iterator — use with `for await...of`.
|
|
221
|
+
*/
|
|
222
|
+
async *[Symbol.asyncIterator]() {
|
|
223
|
+
if (this.iteratorStarted) {
|
|
224
|
+
throw new FlowForgeError("Stream already consumed", 0, "STREAM_CONSUMED");
|
|
225
|
+
}
|
|
226
|
+
this.iteratorStarted = true;
|
|
227
|
+
const includeHistory = this.options.includeHistory ?? true;
|
|
228
|
+
const timeout = this.options.timeout ?? 300;
|
|
229
|
+
const params = new URLSearchParams({
|
|
230
|
+
include_history: String(includeHistory),
|
|
231
|
+
timeout: String(timeout)
|
|
232
|
+
});
|
|
233
|
+
const url = `${this.config.baseUrl}/api/v1/runs/${this.runId}/stream?${params}`;
|
|
234
|
+
const headers = {
|
|
235
|
+
Accept: "text/event-stream"
|
|
236
|
+
};
|
|
237
|
+
if (this.config.apiKey) {
|
|
238
|
+
headers["X-FlowForge-API-Key"] = this.config.apiKey;
|
|
239
|
+
}
|
|
240
|
+
const signals = [this.abortController.signal];
|
|
241
|
+
if (this.options.signal) {
|
|
242
|
+
signals.push(this.options.signal);
|
|
243
|
+
}
|
|
244
|
+
let response;
|
|
245
|
+
try {
|
|
246
|
+
response = await this.config.fetchFn(url, {
|
|
247
|
+
method: "GET",
|
|
248
|
+
headers,
|
|
249
|
+
signal: signals.length === 1 ? signals[0] : anySignal(signals)
|
|
250
|
+
});
|
|
251
|
+
} catch (err) {
|
|
252
|
+
const error = new FlowForgeError(
|
|
253
|
+
err instanceof Error ? err.message : "Stream connection failed",
|
|
254
|
+
0,
|
|
255
|
+
"STREAM_ERROR"
|
|
256
|
+
);
|
|
257
|
+
this.options.onError?.(error);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
const body = await response.text().catch(() => "");
|
|
262
|
+
const error = new FlowForgeError(
|
|
263
|
+
body || `HTTP ${response.status}`,
|
|
264
|
+
response.status,
|
|
265
|
+
"STREAM_HTTP_ERROR"
|
|
266
|
+
);
|
|
267
|
+
this.options.onError?.(error);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
if (!response.body) {
|
|
271
|
+
const error = new FlowForgeError(
|
|
272
|
+
"Response body is empty",
|
|
273
|
+
0,
|
|
274
|
+
"STREAM_EMPTY"
|
|
275
|
+
);
|
|
276
|
+
this.options.onError?.(error);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
for await (const sse of parseSSEStream(response.body)) {
|
|
281
|
+
if (this.closed) return;
|
|
282
|
+
let data;
|
|
283
|
+
try {
|
|
284
|
+
data = JSON.parse(sse.data);
|
|
285
|
+
} catch {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const event = {
|
|
289
|
+
type: sse.event,
|
|
290
|
+
data,
|
|
291
|
+
runId: this.runId,
|
|
292
|
+
timestamp: data.timestamp ?? data.ts ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
293
|
+
};
|
|
294
|
+
this.options.onEvent?.(event);
|
|
295
|
+
yield event;
|
|
296
|
+
if (TERMINAL_EVENTS.has(event.type)) {
|
|
297
|
+
this.options.onComplete?.(event);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} catch (err) {
|
|
302
|
+
if (this.closed) return;
|
|
303
|
+
const error = new FlowForgeError(
|
|
304
|
+
err instanceof Error ? err.message : "Stream error",
|
|
305
|
+
0,
|
|
306
|
+
"STREAM_ERROR"
|
|
307
|
+
);
|
|
308
|
+
this.options.onError?.(error);
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
314
|
+
*/
|
|
315
|
+
async drain() {
|
|
316
|
+
let last = null;
|
|
317
|
+
for await (const event of this) {
|
|
318
|
+
last = event;
|
|
319
|
+
}
|
|
320
|
+
return last;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Collect all events into an array.
|
|
324
|
+
*/
|
|
325
|
+
async collect() {
|
|
326
|
+
const events = [];
|
|
327
|
+
for await (const event of this) {
|
|
328
|
+
events.push(event);
|
|
329
|
+
}
|
|
330
|
+
return events;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Cancel the stream.
|
|
334
|
+
*/
|
|
335
|
+
close() {
|
|
336
|
+
this.closed = true;
|
|
337
|
+
this.abortController.abort();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
function anySignal(signals) {
|
|
341
|
+
const controller = new AbortController();
|
|
342
|
+
for (const signal of signals) {
|
|
343
|
+
if (signal.aborted) {
|
|
344
|
+
controller.abort(signal.reason);
|
|
345
|
+
return controller.signal;
|
|
346
|
+
}
|
|
347
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
348
|
+
once: true
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return controller.signal;
|
|
352
|
+
}
|
|
353
|
+
|
|
153
354
|
// src/resources/runs.ts
|
|
154
355
|
var RunsResource = class {
|
|
155
|
-
constructor(request) {
|
|
356
|
+
constructor(request, streamConfig) {
|
|
156
357
|
this.request = request;
|
|
358
|
+
this.streamConfig = streamConfig;
|
|
157
359
|
}
|
|
158
360
|
/**
|
|
159
361
|
* Get a run by ID, including its steps.
|
|
@@ -210,6 +412,30 @@ var RunsResource = class {
|
|
|
210
412
|
async replay(runId) {
|
|
211
413
|
return this.request("POST", `/runs/${runId}/replay`);
|
|
212
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Stream real-time SSE events for a run.
|
|
417
|
+
*
|
|
418
|
+
* @example Async iterator
|
|
419
|
+
* ```ts
|
|
420
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
421
|
+
* console.log(event.type, event.data);
|
|
422
|
+
* }
|
|
423
|
+
* ```
|
|
424
|
+
*
|
|
425
|
+
* @example Callback-based
|
|
426
|
+
* ```ts
|
|
427
|
+
* await ff.runs.stream('run-id', {
|
|
428
|
+
* onEvent: (e) => console.log(e.type),
|
|
429
|
+
* onComplete: (e) => console.log('Done'),
|
|
430
|
+
* }).drain();
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
stream(runId, options) {
|
|
434
|
+
if (!this.streamConfig) {
|
|
435
|
+
throw new Error("Stream config not available");
|
|
436
|
+
}
|
|
437
|
+
return new RunStream(runId, this.streamConfig, options);
|
|
438
|
+
}
|
|
213
439
|
/**
|
|
214
440
|
* Wait for a run to complete (polling).
|
|
215
441
|
*
|
|
@@ -696,9 +922,51 @@ var ApiKeysResource = class {
|
|
|
696
922
|
}
|
|
697
923
|
};
|
|
698
924
|
|
|
925
|
+
// src/resources/credentials.ts
|
|
926
|
+
var CredentialsResource = class {
|
|
927
|
+
constructor(request) {
|
|
928
|
+
this.request = request;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* List all credentials for the tenant.
|
|
932
|
+
* Values are never returned — only masked prefixes.
|
|
933
|
+
*/
|
|
934
|
+
async list() {
|
|
935
|
+
return this.request("GET", "/credentials");
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
939
|
+
*/
|
|
940
|
+
async get(name) {
|
|
941
|
+
return this.request("GET", `/credentials/${name}`);
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Create a new encrypted credential.
|
|
945
|
+
*/
|
|
946
|
+
async create(input) {
|
|
947
|
+
return this.request("POST", "/credentials", input);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Update an existing credential.
|
|
951
|
+
* If value is provided, it will be re-encrypted.
|
|
952
|
+
*/
|
|
953
|
+
async update(name, input) {
|
|
954
|
+
return this.request("PATCH", `/credentials/${name}`, input);
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Delete a credential.
|
|
958
|
+
*/
|
|
959
|
+
async delete(name) {
|
|
960
|
+
return this.request(
|
|
961
|
+
"DELETE",
|
|
962
|
+
`/credentials/${name}`
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
|
|
699
967
|
// src/client.ts
|
|
700
968
|
var DEFAULT_TIMEOUT = 3e4;
|
|
701
|
-
function
|
|
969
|
+
function anySignal2(signals) {
|
|
702
970
|
const controller = new AbortController();
|
|
703
971
|
for (const signal of signals) {
|
|
704
972
|
if (signal.aborted) {
|
|
@@ -742,7 +1010,7 @@ function createClient(baseUrl, options = {}) {
|
|
|
742
1010
|
}
|
|
743
1011
|
const timeoutController = new AbortController();
|
|
744
1012
|
const timeoutId = setTimeout(() => timeoutController.abort(), timeout);
|
|
745
|
-
const combinedSignal = signal ?
|
|
1013
|
+
const combinedSignal = signal ? anySignal2([signal, timeoutController.signal]) : timeoutController.signal;
|
|
746
1014
|
try {
|
|
747
1015
|
const response = await fetchFn(`${normalizedBaseUrl}/api/v1${path}`, {
|
|
748
1016
|
method,
|
|
@@ -838,34 +1106,30 @@ function createClient(baseUrl, options = {}) {
|
|
|
838
1106
|
}
|
|
839
1107
|
return {
|
|
840
1108
|
events: new EventsResource(request),
|
|
841
|
-
runs: new RunsResource(request
|
|
1109
|
+
runs: new RunsResource(request, {
|
|
1110
|
+
baseUrl: normalizedBaseUrl,
|
|
1111
|
+
apiKey: options.apiKey,
|
|
1112
|
+
fetchFn
|
|
1113
|
+
}),
|
|
842
1114
|
functions: new FunctionsResource(request),
|
|
843
1115
|
tools: new ToolsResource(request),
|
|
844
1116
|
approvals: new ApprovalsResource(request),
|
|
845
1117
|
health: new HealthResource(request),
|
|
846
1118
|
users: new UsersResource(request),
|
|
847
|
-
apiKeys: new ApiKeysResource(request)
|
|
1119
|
+
apiKeys: new ApiKeysResource(request),
|
|
1120
|
+
credentials: new CredentialsResource(request)
|
|
848
1121
|
};
|
|
849
1122
|
}
|
|
850
|
-
|
|
851
|
-
// src/types.ts
|
|
852
|
-
var FlowForgeError = class extends Error {
|
|
853
|
-
constructor(message, status, code, detail) {
|
|
854
|
-
super(message);
|
|
855
|
-
this.status = status;
|
|
856
|
-
this.code = code;
|
|
857
|
-
this.detail = detail;
|
|
858
|
-
this.name = "FlowForgeError";
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
1123
|
export {
|
|
862
1124
|
ApiKeysResource,
|
|
863
1125
|
ApprovalsResource,
|
|
1126
|
+
CredentialsResource,
|
|
864
1127
|
EventsResource,
|
|
865
1128
|
FlowForgeError,
|
|
866
1129
|
FunctionsResource,
|
|
867
1130
|
HealthResource,
|
|
868
1131
|
QueryBuilder,
|
|
1132
|
+
RunStream,
|
|
869
1133
|
RunsResource,
|
|
870
1134
|
ToolsResource,
|
|
871
1135
|
UsersResource,
|