flowforge-client 0.3.1 → 0.3.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 +30 -0
- package/dist/index.d.mts +170 -4
- package/dist/index.d.ts +170 -4
- 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
|
@@ -15,7 +15,7 @@ declare class FlowForgeError extends Error {
|
|
|
15
15
|
constructor(message: string, status: number, code?: string | undefined, detail?: unknown | undefined);
|
|
16
16
|
}
|
|
17
17
|
type RunStatus = "pending" | "running" | "completed" | "failed" | "paused" | "cancelled";
|
|
18
|
-
type StepType = "run" | "sleep" | "ai" | "wait_for_event" | "invoke" | "send_event" | "agent";
|
|
18
|
+
type StepType = "run" | "sleep" | "ai" | "wait_for_event" | "invoke" | "send_event" | "agent" | "sub_agent";
|
|
19
19
|
type StepStatus = "pending" | "running" | "completed" | "failed" | "sleeping" | "waiting";
|
|
20
20
|
type TriggerType = "event" | "cron" | "webhook";
|
|
21
21
|
type ApprovalStatus = "pending" | "approved" | "rejected" | "expired";
|
|
@@ -23,6 +23,7 @@ interface Run {
|
|
|
23
23
|
id: string;
|
|
24
24
|
function_id: string;
|
|
25
25
|
event_id: string | null;
|
|
26
|
+
parent_run_id: string | null;
|
|
26
27
|
status: RunStatus;
|
|
27
28
|
trigger_type: string;
|
|
28
29
|
trigger_data: Record<string, unknown>;
|
|
@@ -68,12 +69,17 @@ interface FlowForgeFunction {
|
|
|
68
69
|
created_at: string;
|
|
69
70
|
updated_at: string;
|
|
70
71
|
}
|
|
72
|
+
type ToolType = "custom" | "webhook" | "builtin";
|
|
71
73
|
interface Tool {
|
|
72
74
|
id: string;
|
|
73
75
|
name: string;
|
|
74
76
|
description: string;
|
|
75
77
|
parameters: Record<string, unknown>;
|
|
78
|
+
tool_type: ToolType;
|
|
76
79
|
code: string | null;
|
|
80
|
+
webhook_url: string | null;
|
|
81
|
+
webhook_method: string;
|
|
82
|
+
webhook_headers: Record<string, string> | null;
|
|
77
83
|
is_builtin: boolean;
|
|
78
84
|
requires_approval: boolean;
|
|
79
85
|
approval_timeout: string | null;
|
|
@@ -154,6 +160,20 @@ interface ToolFilters {
|
|
|
154
160
|
interface ApprovalFilters {
|
|
155
161
|
status: ApprovalStatus;
|
|
156
162
|
}
|
|
163
|
+
interface SubAgentConfig {
|
|
164
|
+
system_prompt: string;
|
|
165
|
+
model?: string;
|
|
166
|
+
tools?: string[];
|
|
167
|
+
max_iterations?: number;
|
|
168
|
+
max_tool_calls?: number;
|
|
169
|
+
description?: string;
|
|
170
|
+
}
|
|
171
|
+
interface AgentConfig {
|
|
172
|
+
model?: string;
|
|
173
|
+
max_iterations?: number;
|
|
174
|
+
max_tool_calls?: number;
|
|
175
|
+
sub_agents?: Record<string, SubAgentConfig>;
|
|
176
|
+
}
|
|
157
177
|
interface CreateFunctionInput {
|
|
158
178
|
id: string;
|
|
159
179
|
name: string;
|
|
@@ -164,7 +184,7 @@ interface CreateFunctionInput {
|
|
|
164
184
|
is_inline?: boolean;
|
|
165
185
|
system_prompt?: string;
|
|
166
186
|
tools_config?: string[];
|
|
167
|
-
agent_config?: Record<string, unknown>;
|
|
187
|
+
agent_config?: AgentConfig | Record<string, unknown>;
|
|
168
188
|
config?: Record<string, unknown>;
|
|
169
189
|
is_active?: boolean;
|
|
170
190
|
}
|
|
@@ -183,7 +203,11 @@ interface CreateToolInput {
|
|
|
183
203
|
name: string;
|
|
184
204
|
description: string;
|
|
185
205
|
parameters: Record<string, unknown>;
|
|
206
|
+
tool_type?: ToolType;
|
|
186
207
|
code?: string;
|
|
208
|
+
webhook_url?: string;
|
|
209
|
+
webhook_method?: string;
|
|
210
|
+
webhook_headers?: Record<string, string>;
|
|
187
211
|
requires_approval?: boolean;
|
|
188
212
|
approval_timeout?: string;
|
|
189
213
|
is_active?: boolean;
|
|
@@ -191,11 +215,41 @@ interface CreateToolInput {
|
|
|
191
215
|
interface UpdateToolInput {
|
|
192
216
|
description?: string;
|
|
193
217
|
parameters?: Record<string, unknown>;
|
|
218
|
+
tool_type?: ToolType;
|
|
194
219
|
code?: string;
|
|
220
|
+
webhook_url?: string;
|
|
221
|
+
webhook_method?: string;
|
|
222
|
+
webhook_headers?: Record<string, string>;
|
|
195
223
|
requires_approval?: boolean;
|
|
196
224
|
approval_timeout?: string;
|
|
197
225
|
is_active?: boolean;
|
|
198
226
|
}
|
|
227
|
+
interface CreateCredentialInput {
|
|
228
|
+
name: string;
|
|
229
|
+
credential_type?: string;
|
|
230
|
+
value: string;
|
|
231
|
+
description?: string;
|
|
232
|
+
}
|
|
233
|
+
interface UpdateCredentialInput {
|
|
234
|
+
value?: string;
|
|
235
|
+
description?: string;
|
|
236
|
+
credential_type?: string;
|
|
237
|
+
is_active?: boolean;
|
|
238
|
+
}
|
|
239
|
+
interface Credential {
|
|
240
|
+
id: string;
|
|
241
|
+
name: string;
|
|
242
|
+
credential_type: string;
|
|
243
|
+
value_prefix: string;
|
|
244
|
+
description: string | null;
|
|
245
|
+
is_active: boolean;
|
|
246
|
+
created_at: string;
|
|
247
|
+
updated_at: string;
|
|
248
|
+
}
|
|
249
|
+
interface CredentialsResponse {
|
|
250
|
+
credentials: Credential[];
|
|
251
|
+
total: number;
|
|
252
|
+
}
|
|
199
253
|
interface SendEventInput {
|
|
200
254
|
id?: string;
|
|
201
255
|
user_id?: string;
|
|
@@ -255,6 +309,32 @@ interface ApiKeysResponse {
|
|
|
255
309
|
interface ApiKeyFilters {
|
|
256
310
|
include_revoked: boolean;
|
|
257
311
|
}
|
|
312
|
+
type RunEventType = "step_started" | "step_completed" | "step_failed" | "thinking" | "thinking_chunk" | "tool_call_started" | "tool_call_completed" | "approval_required" | "approval_resolved" | "sub_agent_started" | "sub_agent_completed" | "run_started" | "run_paused" | "run_resumed" | "run_completed" | "run_failed";
|
|
313
|
+
interface RunStreamEvent {
|
|
314
|
+
type: RunEventType;
|
|
315
|
+
data: Record<string, unknown>;
|
|
316
|
+
runId: string;
|
|
317
|
+
timestamp: string;
|
|
318
|
+
}
|
|
319
|
+
interface StreamOptions {
|
|
320
|
+
/** Include historical events on connect (default: true) */
|
|
321
|
+
includeHistory?: boolean;
|
|
322
|
+
/** Server-side stream timeout in seconds (default: 300, max: 600) */
|
|
323
|
+
timeout?: number;
|
|
324
|
+
/** AbortSignal to cancel the stream */
|
|
325
|
+
signal?: AbortSignal;
|
|
326
|
+
/** Callback for each event */
|
|
327
|
+
onEvent?: (event: RunStreamEvent) => void;
|
|
328
|
+
/** Callback when run completes or fails */
|
|
329
|
+
onComplete?: (event: RunStreamEvent) => void;
|
|
330
|
+
/** Callback on stream error */
|
|
331
|
+
onError?: (error: FlowForgeError) => void;
|
|
332
|
+
}
|
|
333
|
+
interface StreamConfig {
|
|
334
|
+
baseUrl: string;
|
|
335
|
+
apiKey?: string;
|
|
336
|
+
fetchFn: typeof fetch;
|
|
337
|
+
}
|
|
258
338
|
interface ClientOptions {
|
|
259
339
|
/**
|
|
260
340
|
* API key for authentication.
|
|
@@ -401,13 +481,44 @@ declare class EventsResource {
|
|
|
401
481
|
select(): QueryBuilder<Event, EventFilters>;
|
|
402
482
|
}
|
|
403
483
|
|
|
484
|
+
/**
|
|
485
|
+
* RunStream — SSE stream wrapper for FlowForge run events.
|
|
486
|
+
*/
|
|
487
|
+
|
|
488
|
+
declare class RunStream {
|
|
489
|
+
private runId;
|
|
490
|
+
private config;
|
|
491
|
+
private options;
|
|
492
|
+
private abortController;
|
|
493
|
+
private iteratorStarted;
|
|
494
|
+
private closed;
|
|
495
|
+
constructor(runId: string, config: StreamConfig, options?: StreamOptions);
|
|
496
|
+
/**
|
|
497
|
+
* Async iterator — use with `for await...of`.
|
|
498
|
+
*/
|
|
499
|
+
[Symbol.asyncIterator](): AsyncGenerator<RunStreamEvent>;
|
|
500
|
+
/**
|
|
501
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
502
|
+
*/
|
|
503
|
+
drain(): Promise<RunStreamEvent | null>;
|
|
504
|
+
/**
|
|
505
|
+
* Collect all events into an array.
|
|
506
|
+
*/
|
|
507
|
+
collect(): Promise<RunStreamEvent[]>;
|
|
508
|
+
/**
|
|
509
|
+
* Cancel the stream.
|
|
510
|
+
*/
|
|
511
|
+
close(): void;
|
|
512
|
+
}
|
|
513
|
+
|
|
404
514
|
/**
|
|
405
515
|
* FlowForge TypeScript Client - Runs Resource
|
|
406
516
|
*/
|
|
407
517
|
|
|
408
518
|
declare class RunsResource {
|
|
409
519
|
private request;
|
|
410
|
-
|
|
520
|
+
private streamConfig?;
|
|
521
|
+
constructor(request: RequestFn$2, streamConfig?: StreamConfig | undefined);
|
|
411
522
|
/**
|
|
412
523
|
* Get a run by ID, including its steps.
|
|
413
524
|
*
|
|
@@ -455,6 +566,25 @@ declare class RunsResource {
|
|
|
455
566
|
* ```
|
|
456
567
|
*/
|
|
457
568
|
replay(runId: string): Promise<Result<RunWithSteps>>;
|
|
569
|
+
/**
|
|
570
|
+
* Stream real-time SSE events for a run.
|
|
571
|
+
*
|
|
572
|
+
* @example Async iterator
|
|
573
|
+
* ```ts
|
|
574
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
575
|
+
* console.log(event.type, event.data);
|
|
576
|
+
* }
|
|
577
|
+
* ```
|
|
578
|
+
*
|
|
579
|
+
* @example Callback-based
|
|
580
|
+
* ```ts
|
|
581
|
+
* await ff.runs.stream('run-id', {
|
|
582
|
+
* onEvent: (e) => console.log(e.type),
|
|
583
|
+
* onComplete: (e) => console.log('Done'),
|
|
584
|
+
* }).drain();
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
stream(runId: string, options?: StreamOptions): RunStream;
|
|
458
588
|
/**
|
|
459
589
|
* Wait for a run to complete (polling).
|
|
460
590
|
*
|
|
@@ -872,6 +1002,40 @@ declare class ApiKeysResource {
|
|
|
872
1002
|
}>>;
|
|
873
1003
|
}
|
|
874
1004
|
|
|
1005
|
+
/**
|
|
1006
|
+
* FlowForge TypeScript Client - Credentials Resource
|
|
1007
|
+
*/
|
|
1008
|
+
|
|
1009
|
+
declare class CredentialsResource {
|
|
1010
|
+
private request;
|
|
1011
|
+
constructor(request: RequestFn$2);
|
|
1012
|
+
/**
|
|
1013
|
+
* List all credentials for the tenant.
|
|
1014
|
+
* Values are never returned — only masked prefixes.
|
|
1015
|
+
*/
|
|
1016
|
+
list(): Promise<Result<CredentialsResponse>>;
|
|
1017
|
+
/**
|
|
1018
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
1019
|
+
*/
|
|
1020
|
+
get(name: string): Promise<Result<Credential>>;
|
|
1021
|
+
/**
|
|
1022
|
+
* Create a new encrypted credential.
|
|
1023
|
+
*/
|
|
1024
|
+
create(input: CreateCredentialInput): Promise<Result<Credential>>;
|
|
1025
|
+
/**
|
|
1026
|
+
* Update an existing credential.
|
|
1027
|
+
* If value is provided, it will be re-encrypted.
|
|
1028
|
+
*/
|
|
1029
|
+
update(name: string, input: UpdateCredentialInput): Promise<Result<Credential>>;
|
|
1030
|
+
/**
|
|
1031
|
+
* Delete a credential.
|
|
1032
|
+
*/
|
|
1033
|
+
delete(name: string): Promise<Result<{
|
|
1034
|
+
success: boolean;
|
|
1035
|
+
message: string;
|
|
1036
|
+
}>>;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
875
1039
|
/**
|
|
876
1040
|
* FlowForge TypeScript Client - Main Client Factory
|
|
877
1041
|
*/
|
|
@@ -896,6 +1060,8 @@ interface FlowForgeClient {
|
|
|
896
1060
|
users: UsersResource;
|
|
897
1061
|
/** API Keys resource - manage API key authentication */
|
|
898
1062
|
apiKeys: ApiKeysResource;
|
|
1063
|
+
/** Credentials resource - manage encrypted service credentials */
|
|
1064
|
+
credentials: CredentialsResource;
|
|
899
1065
|
}
|
|
900
1066
|
/**
|
|
901
1067
|
* Create a FlowForge client instance.
|
|
@@ -931,4 +1097,4 @@ interface FlowForgeClient {
|
|
|
931
1097
|
*/
|
|
932
1098
|
declare function createClient(baseUrl: string, options?: ClientOptions): FlowForgeClient;
|
|
933
1099
|
|
|
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 };
|
|
1100
|
+
export { type AgentConfig, 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 SubAgentConfig, 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
|
@@ -15,7 +15,7 @@ declare class FlowForgeError extends Error {
|
|
|
15
15
|
constructor(message: string, status: number, code?: string | undefined, detail?: unknown | undefined);
|
|
16
16
|
}
|
|
17
17
|
type RunStatus = "pending" | "running" | "completed" | "failed" | "paused" | "cancelled";
|
|
18
|
-
type StepType = "run" | "sleep" | "ai" | "wait_for_event" | "invoke" | "send_event" | "agent";
|
|
18
|
+
type StepType = "run" | "sleep" | "ai" | "wait_for_event" | "invoke" | "send_event" | "agent" | "sub_agent";
|
|
19
19
|
type StepStatus = "pending" | "running" | "completed" | "failed" | "sleeping" | "waiting";
|
|
20
20
|
type TriggerType = "event" | "cron" | "webhook";
|
|
21
21
|
type ApprovalStatus = "pending" | "approved" | "rejected" | "expired";
|
|
@@ -23,6 +23,7 @@ interface Run {
|
|
|
23
23
|
id: string;
|
|
24
24
|
function_id: string;
|
|
25
25
|
event_id: string | null;
|
|
26
|
+
parent_run_id: string | null;
|
|
26
27
|
status: RunStatus;
|
|
27
28
|
trigger_type: string;
|
|
28
29
|
trigger_data: Record<string, unknown>;
|
|
@@ -68,12 +69,17 @@ interface FlowForgeFunction {
|
|
|
68
69
|
created_at: string;
|
|
69
70
|
updated_at: string;
|
|
70
71
|
}
|
|
72
|
+
type ToolType = "custom" | "webhook" | "builtin";
|
|
71
73
|
interface Tool {
|
|
72
74
|
id: string;
|
|
73
75
|
name: string;
|
|
74
76
|
description: string;
|
|
75
77
|
parameters: Record<string, unknown>;
|
|
78
|
+
tool_type: ToolType;
|
|
76
79
|
code: string | null;
|
|
80
|
+
webhook_url: string | null;
|
|
81
|
+
webhook_method: string;
|
|
82
|
+
webhook_headers: Record<string, string> | null;
|
|
77
83
|
is_builtin: boolean;
|
|
78
84
|
requires_approval: boolean;
|
|
79
85
|
approval_timeout: string | null;
|
|
@@ -154,6 +160,20 @@ interface ToolFilters {
|
|
|
154
160
|
interface ApprovalFilters {
|
|
155
161
|
status: ApprovalStatus;
|
|
156
162
|
}
|
|
163
|
+
interface SubAgentConfig {
|
|
164
|
+
system_prompt: string;
|
|
165
|
+
model?: string;
|
|
166
|
+
tools?: string[];
|
|
167
|
+
max_iterations?: number;
|
|
168
|
+
max_tool_calls?: number;
|
|
169
|
+
description?: string;
|
|
170
|
+
}
|
|
171
|
+
interface AgentConfig {
|
|
172
|
+
model?: string;
|
|
173
|
+
max_iterations?: number;
|
|
174
|
+
max_tool_calls?: number;
|
|
175
|
+
sub_agents?: Record<string, SubAgentConfig>;
|
|
176
|
+
}
|
|
157
177
|
interface CreateFunctionInput {
|
|
158
178
|
id: string;
|
|
159
179
|
name: string;
|
|
@@ -164,7 +184,7 @@ interface CreateFunctionInput {
|
|
|
164
184
|
is_inline?: boolean;
|
|
165
185
|
system_prompt?: string;
|
|
166
186
|
tools_config?: string[];
|
|
167
|
-
agent_config?: Record<string, unknown>;
|
|
187
|
+
agent_config?: AgentConfig | Record<string, unknown>;
|
|
168
188
|
config?: Record<string, unknown>;
|
|
169
189
|
is_active?: boolean;
|
|
170
190
|
}
|
|
@@ -183,7 +203,11 @@ interface CreateToolInput {
|
|
|
183
203
|
name: string;
|
|
184
204
|
description: string;
|
|
185
205
|
parameters: Record<string, unknown>;
|
|
206
|
+
tool_type?: ToolType;
|
|
186
207
|
code?: string;
|
|
208
|
+
webhook_url?: string;
|
|
209
|
+
webhook_method?: string;
|
|
210
|
+
webhook_headers?: Record<string, string>;
|
|
187
211
|
requires_approval?: boolean;
|
|
188
212
|
approval_timeout?: string;
|
|
189
213
|
is_active?: boolean;
|
|
@@ -191,11 +215,41 @@ interface CreateToolInput {
|
|
|
191
215
|
interface UpdateToolInput {
|
|
192
216
|
description?: string;
|
|
193
217
|
parameters?: Record<string, unknown>;
|
|
218
|
+
tool_type?: ToolType;
|
|
194
219
|
code?: string;
|
|
220
|
+
webhook_url?: string;
|
|
221
|
+
webhook_method?: string;
|
|
222
|
+
webhook_headers?: Record<string, string>;
|
|
195
223
|
requires_approval?: boolean;
|
|
196
224
|
approval_timeout?: string;
|
|
197
225
|
is_active?: boolean;
|
|
198
226
|
}
|
|
227
|
+
interface CreateCredentialInput {
|
|
228
|
+
name: string;
|
|
229
|
+
credential_type?: string;
|
|
230
|
+
value: string;
|
|
231
|
+
description?: string;
|
|
232
|
+
}
|
|
233
|
+
interface UpdateCredentialInput {
|
|
234
|
+
value?: string;
|
|
235
|
+
description?: string;
|
|
236
|
+
credential_type?: string;
|
|
237
|
+
is_active?: boolean;
|
|
238
|
+
}
|
|
239
|
+
interface Credential {
|
|
240
|
+
id: string;
|
|
241
|
+
name: string;
|
|
242
|
+
credential_type: string;
|
|
243
|
+
value_prefix: string;
|
|
244
|
+
description: string | null;
|
|
245
|
+
is_active: boolean;
|
|
246
|
+
created_at: string;
|
|
247
|
+
updated_at: string;
|
|
248
|
+
}
|
|
249
|
+
interface CredentialsResponse {
|
|
250
|
+
credentials: Credential[];
|
|
251
|
+
total: number;
|
|
252
|
+
}
|
|
199
253
|
interface SendEventInput {
|
|
200
254
|
id?: string;
|
|
201
255
|
user_id?: string;
|
|
@@ -255,6 +309,32 @@ interface ApiKeysResponse {
|
|
|
255
309
|
interface ApiKeyFilters {
|
|
256
310
|
include_revoked: boolean;
|
|
257
311
|
}
|
|
312
|
+
type RunEventType = "step_started" | "step_completed" | "step_failed" | "thinking" | "thinking_chunk" | "tool_call_started" | "tool_call_completed" | "approval_required" | "approval_resolved" | "sub_agent_started" | "sub_agent_completed" | "run_started" | "run_paused" | "run_resumed" | "run_completed" | "run_failed";
|
|
313
|
+
interface RunStreamEvent {
|
|
314
|
+
type: RunEventType;
|
|
315
|
+
data: Record<string, unknown>;
|
|
316
|
+
runId: string;
|
|
317
|
+
timestamp: string;
|
|
318
|
+
}
|
|
319
|
+
interface StreamOptions {
|
|
320
|
+
/** Include historical events on connect (default: true) */
|
|
321
|
+
includeHistory?: boolean;
|
|
322
|
+
/** Server-side stream timeout in seconds (default: 300, max: 600) */
|
|
323
|
+
timeout?: number;
|
|
324
|
+
/** AbortSignal to cancel the stream */
|
|
325
|
+
signal?: AbortSignal;
|
|
326
|
+
/** Callback for each event */
|
|
327
|
+
onEvent?: (event: RunStreamEvent) => void;
|
|
328
|
+
/** Callback when run completes or fails */
|
|
329
|
+
onComplete?: (event: RunStreamEvent) => void;
|
|
330
|
+
/** Callback on stream error */
|
|
331
|
+
onError?: (error: FlowForgeError) => void;
|
|
332
|
+
}
|
|
333
|
+
interface StreamConfig {
|
|
334
|
+
baseUrl: string;
|
|
335
|
+
apiKey?: string;
|
|
336
|
+
fetchFn: typeof fetch;
|
|
337
|
+
}
|
|
258
338
|
interface ClientOptions {
|
|
259
339
|
/**
|
|
260
340
|
* API key for authentication.
|
|
@@ -401,13 +481,44 @@ declare class EventsResource {
|
|
|
401
481
|
select(): QueryBuilder<Event, EventFilters>;
|
|
402
482
|
}
|
|
403
483
|
|
|
484
|
+
/**
|
|
485
|
+
* RunStream — SSE stream wrapper for FlowForge run events.
|
|
486
|
+
*/
|
|
487
|
+
|
|
488
|
+
declare class RunStream {
|
|
489
|
+
private runId;
|
|
490
|
+
private config;
|
|
491
|
+
private options;
|
|
492
|
+
private abortController;
|
|
493
|
+
private iteratorStarted;
|
|
494
|
+
private closed;
|
|
495
|
+
constructor(runId: string, config: StreamConfig, options?: StreamOptions);
|
|
496
|
+
/**
|
|
497
|
+
* Async iterator — use with `for await...of`.
|
|
498
|
+
*/
|
|
499
|
+
[Symbol.asyncIterator](): AsyncGenerator<RunStreamEvent>;
|
|
500
|
+
/**
|
|
501
|
+
* Consume the entire stream via callbacks. Returns the terminal event or null.
|
|
502
|
+
*/
|
|
503
|
+
drain(): Promise<RunStreamEvent | null>;
|
|
504
|
+
/**
|
|
505
|
+
* Collect all events into an array.
|
|
506
|
+
*/
|
|
507
|
+
collect(): Promise<RunStreamEvent[]>;
|
|
508
|
+
/**
|
|
509
|
+
* Cancel the stream.
|
|
510
|
+
*/
|
|
511
|
+
close(): void;
|
|
512
|
+
}
|
|
513
|
+
|
|
404
514
|
/**
|
|
405
515
|
* FlowForge TypeScript Client - Runs Resource
|
|
406
516
|
*/
|
|
407
517
|
|
|
408
518
|
declare class RunsResource {
|
|
409
519
|
private request;
|
|
410
|
-
|
|
520
|
+
private streamConfig?;
|
|
521
|
+
constructor(request: RequestFn$2, streamConfig?: StreamConfig | undefined);
|
|
411
522
|
/**
|
|
412
523
|
* Get a run by ID, including its steps.
|
|
413
524
|
*
|
|
@@ -455,6 +566,25 @@ declare class RunsResource {
|
|
|
455
566
|
* ```
|
|
456
567
|
*/
|
|
457
568
|
replay(runId: string): Promise<Result<RunWithSteps>>;
|
|
569
|
+
/**
|
|
570
|
+
* Stream real-time SSE events for a run.
|
|
571
|
+
*
|
|
572
|
+
* @example Async iterator
|
|
573
|
+
* ```ts
|
|
574
|
+
* for await (const event of ff.runs.stream('run-id')) {
|
|
575
|
+
* console.log(event.type, event.data);
|
|
576
|
+
* }
|
|
577
|
+
* ```
|
|
578
|
+
*
|
|
579
|
+
* @example Callback-based
|
|
580
|
+
* ```ts
|
|
581
|
+
* await ff.runs.stream('run-id', {
|
|
582
|
+
* onEvent: (e) => console.log(e.type),
|
|
583
|
+
* onComplete: (e) => console.log('Done'),
|
|
584
|
+
* }).drain();
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
stream(runId: string, options?: StreamOptions): RunStream;
|
|
458
588
|
/**
|
|
459
589
|
* Wait for a run to complete (polling).
|
|
460
590
|
*
|
|
@@ -872,6 +1002,40 @@ declare class ApiKeysResource {
|
|
|
872
1002
|
}>>;
|
|
873
1003
|
}
|
|
874
1004
|
|
|
1005
|
+
/**
|
|
1006
|
+
* FlowForge TypeScript Client - Credentials Resource
|
|
1007
|
+
*/
|
|
1008
|
+
|
|
1009
|
+
declare class CredentialsResource {
|
|
1010
|
+
private request;
|
|
1011
|
+
constructor(request: RequestFn$2);
|
|
1012
|
+
/**
|
|
1013
|
+
* List all credentials for the tenant.
|
|
1014
|
+
* Values are never returned — only masked prefixes.
|
|
1015
|
+
*/
|
|
1016
|
+
list(): Promise<Result<CredentialsResponse>>;
|
|
1017
|
+
/**
|
|
1018
|
+
* Get a credential by name (metadata only, no decrypted value).
|
|
1019
|
+
*/
|
|
1020
|
+
get(name: string): Promise<Result<Credential>>;
|
|
1021
|
+
/**
|
|
1022
|
+
* Create a new encrypted credential.
|
|
1023
|
+
*/
|
|
1024
|
+
create(input: CreateCredentialInput): Promise<Result<Credential>>;
|
|
1025
|
+
/**
|
|
1026
|
+
* Update an existing credential.
|
|
1027
|
+
* If value is provided, it will be re-encrypted.
|
|
1028
|
+
*/
|
|
1029
|
+
update(name: string, input: UpdateCredentialInput): Promise<Result<Credential>>;
|
|
1030
|
+
/**
|
|
1031
|
+
* Delete a credential.
|
|
1032
|
+
*/
|
|
1033
|
+
delete(name: string): Promise<Result<{
|
|
1034
|
+
success: boolean;
|
|
1035
|
+
message: string;
|
|
1036
|
+
}>>;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
875
1039
|
/**
|
|
876
1040
|
* FlowForge TypeScript Client - Main Client Factory
|
|
877
1041
|
*/
|
|
@@ -896,6 +1060,8 @@ interface FlowForgeClient {
|
|
|
896
1060
|
users: UsersResource;
|
|
897
1061
|
/** API Keys resource - manage API key authentication */
|
|
898
1062
|
apiKeys: ApiKeysResource;
|
|
1063
|
+
/** Credentials resource - manage encrypted service credentials */
|
|
1064
|
+
credentials: CredentialsResource;
|
|
899
1065
|
}
|
|
900
1066
|
/**
|
|
901
1067
|
* Create a FlowForge client instance.
|
|
@@ -931,4 +1097,4 @@ interface FlowForgeClient {
|
|
|
931
1097
|
*/
|
|
932
1098
|
declare function createClient(baseUrl: string, options?: ClientOptions): FlowForgeClient;
|
|
933
1099
|
|
|
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 };
|
|
1100
|
+
export { type AgentConfig, 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 SubAgentConfig, 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,
|