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 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
- constructor(request: RequestFn$2);
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
- constructor(request: RequestFn$2);
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 anySignal(signals) {
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 ? anySignal([signal, timeoutController.signal]) : timeoutController.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 anySignal(signals) {
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 ? anySignal([signal, timeoutController.signal]) : timeoutController.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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowforge-client",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "TypeScript client for FlowForge workflow orchestration",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",