flowscale 1.0.13 → 1.0.15

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
@@ -1,6 +1,25 @@
1
1
  # Flowscale Node.js SDK
2
2
 
3
- A comprehensive Node.js SDK designed to simplify interaction with the FlowScale ComfyUI API. This library abstracts away the complexities of API calls, enabling you to effortlessly invoke workflows, retrieve outputs, manage workflow runs, and monitor system health.
3
+ A comprehensive SDK designed to simplify interaction with the FlowScale ComfyUI API. This library works in both Node.js and browser environments.
4
+
5
+ ---
6
+
7
+ ## ⚠️ Important Security Notice for Browser Usage
8
+
9
+ When using this SDK in a browser environment, your API key will be exposed to end users. This is a significant security risk. We strongly recommend:
10
+
11
+ 1. Using this SDK in a Node.js backend environment
12
+ 2. Creating a proxy API that securely handles the API key server-side
13
+
14
+ If you understand the risks and still need to use the SDK directly in the browser, you must explicitly acknowledge this by setting the `allowDangerouslyExposeApiKey` parameter:
15
+
16
+ ```javascript
17
+ const flowscale = new FlowscaleAPI(
18
+ apiKey,
19
+ apiUrl,
20
+ true // allowDangerouslyExposeApiKey - Only set to true if you understand the security risks
21
+ );
22
+ ```
4
23
 
5
24
  ---
6
25
 
@@ -29,18 +48,31 @@ To get started, import the Flowscale SDK into your project:
29
48
  ```javascript
30
49
  import { FlowscaleAPI } from 'flowscale';
31
50
 
32
- // Initialize the SDK
51
+ // Node.js Environment (Recommended)
33
52
  const apiKey = process.env.FLOWSCALE_API_KEY;
34
53
  const apiUrl = process.env.FLOWSCALE_API_URL;
35
-
36
- if (!apiKey || !apiUrl) {
37
- console.error('FLOWSCALE_API_KEY or FLOWSCALE_API_URL not set in .env');
38
- process.exit(1);
39
- }
40
-
41
54
  const flowscale = new FlowscaleAPI(apiKey, apiUrl);
55
+
56
+ // Browser Environment (Not Recommended)
57
+ const flowscale = new FlowscaleAPI(
58
+ 'your-api-key', // ⚠️ WARNING: This will be exposed to users
59
+ 'https://your-api-url.pod.flowscale.ai',
60
+ true // Explicitly acknowledge the security risk
61
+ );
42
62
  ```
43
63
 
64
+ ### Environment-Specific Considerations
65
+
66
+ #### Node.js (Recommended)
67
+ - Store API keys in environment variables
68
+ - Use `.env` files for configuration
69
+ - Full access to all SDK features
70
+
71
+ #### Browser
72
+ - API key will be visible in network requests
73
+ - Supports File/Blob uploads directly from browser
74
+ - Consider implementing a backend proxy instead
75
+
44
76
  **Environment Variables:** Add the following to your `.env` file:
45
77
 
46
78
  ```plaintext
@@ -165,6 +197,51 @@ console.log('Workflow Result:', result);
165
197
 
166
198
  ---
167
199
 
200
+ ### 3.1 `executeWorkflowAsync(workflowId, data, groupId?, pollIntervalMs?, timeoutMs?)`
201
+
202
+ **Description:**
203
+ Execute a workflow and automatically wait for the result by polling the output. This is a convenience method that combines `executeWorkflow` and `getOutput` with automatic polling.
204
+
205
+ **Parameters:**
206
+ - `workflowId` *(string)*: The unique ID of the workflow.
207
+ - `data` *(object)*: Input parameters for the workflow.
208
+ - `groupId` *(string, optional)*: A custom identifier for grouping runs.
209
+ - `pollIntervalMs` *(number, optional)*: Polling interval in milliseconds (default: 1000).
210
+ - `timeoutMs` *(number, optional)*: Maximum time to wait for results in milliseconds (default: 300000 - 5 minutes).
211
+
212
+ **Usage:**
213
+ ```javascript
214
+ const workflowId = "bncu0a1kipv";
215
+ const inputs = {
216
+ "text_51536": "Prompt test",
217
+ "image_1234": { /* File or Blob of image */ }
218
+ };
219
+
220
+ try {
221
+ const result = await flowscale.executeWorkflowAsync(workflowId, inputs);
222
+ console.log('Workflow Result:', result);
223
+ } catch (error) {
224
+ if (error.message.includes('timed out')) {
225
+ console.error('Workflow took too long to complete');
226
+ } else {
227
+ console.error('Workflow error:', error);
228
+ }
229
+ }
230
+ ```
231
+
232
+ **Response Example:**
233
+ ```json
234
+ {
235
+ "status": "success",
236
+ "data": {
237
+ "download_url": "https://runs.s3.amazonaws.com/generations/...",
238
+ "generation_status": "success"
239
+ }
240
+ }
241
+ ```
242
+
243
+ ---
244
+
168
245
  ### 4. `getOutput(filename)`
169
246
 
170
247
  **Description:**
package/dist/index.d.ts CHANGED
@@ -3,7 +3,8 @@ export declare class FlowscaleAPI {
3
3
  private apiKey;
4
4
  private baseUrl;
5
5
  private client;
6
- constructor(apiKey: string, baseUrl: string);
6
+ private isNode;
7
+ constructor(apiKey: string, baseUrl: string, allowDangerouslyExposeApiKey?: boolean);
7
8
  /**
8
9
  * Checks the health status of all ComfyUI instances within the specified cluster.
9
10
  */
@@ -21,6 +22,17 @@ export declare class FlowscaleAPI {
21
22
  executeWorkflow(workflowId: string, data: {
22
23
  [key: string]: any;
23
24
  }, groupId?: string): Promise<ExecuteWorkflowResponse>;
25
+ /**
26
+ * Executes a workflow and waits for the output by polling.
27
+ * @param workflowId - The ID of the workflow to execute.
28
+ * @param data - Form data including text fields and file uploads.
29
+ * @param groupId - Optional group ID.
30
+ * @param pollIntervalMs - Optional polling interval in milliseconds (default: 1000)
31
+ * @param timeoutMs - Optional timeout in milliseconds (default: 300000 - 5 minutes)
32
+ */
33
+ executeWorkflowAsync(workflowId: string, data: {
34
+ [key: string]: any;
35
+ }, groupId?: string, pollIntervalMs?: number, timeoutMs?: number): Promise<GetOutputResponse>;
24
36
  /**
25
37
  * Retrieves the output of a specific run by providing the filename.
26
38
  * @param filename - The filename of the output to retrieve.
package/dist/index.js CHANGED
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -52,12 +41,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
52
41
  Object.defineProperty(exports, "__esModule", { value: true });
53
42
  exports.FlowscaleAPI = void 0;
54
43
  var axios_1 = __importDefault(require("axios"));
55
- var form_data_1 = __importDefault(require("form-data"));
56
- var fs_1 = __importDefault(require("fs"));
57
- var path_1 = __importDefault(require("path"));
58
44
  var FlowscaleAPI = /** @class */ (function () {
59
- function FlowscaleAPI(apiKey, baseUrl) {
45
+ function FlowscaleAPI(apiKey, baseUrl, allowDangerouslyExposeApiKey) {
46
+ if (allowDangerouslyExposeApiKey === void 0) { allowDangerouslyExposeApiKey = false; }
60
47
  var _this = this;
48
+ // Check if we're in a Node.js environment
49
+ this.isNode = typeof process !== 'undefined' &&
50
+ process.versions != null &&
51
+ process.versions.node != null;
52
+ // Warn about API key exposure in browser environment
53
+ if (!this.isNode && !allowDangerouslyExposeApiKey) {
54
+ throw new Error('WARNING: Using FlowscaleAPI in a browser environment will expose your API key to end users. ' +
55
+ 'This is a security risk. If you understand the risks and still want to proceed, ' +
56
+ 'set allowDangerouslyExposeApiKey to true. ' +
57
+ 'We strongly recommend using a backend proxy instead.');
58
+ }
61
59
  this.apiKey = apiKey;
62
60
  this.baseUrl = baseUrl;
63
61
  this.client = axios_1.default.create({
@@ -132,7 +130,7 @@ var FlowscaleAPI = /** @class */ (function () {
132
130
  switch (_a.label) {
133
131
  case 0:
134
132
  _a.trys.push([0, 2, , 3]);
135
- formData_1 = new form_data_1.default();
133
+ formData_1 = new FormData();
136
134
  _loop_1 = function (key) {
137
135
  if (data.hasOwnProperty(key)) {
138
136
  var value = data[key];
@@ -152,7 +150,10 @@ var FlowscaleAPI = /** @class */ (function () {
152
150
  for (key in data) {
153
151
  _loop_1(key);
154
152
  }
155
- headers = __assign(__assign({}, formData_1.getHeaders()), { 'X-API-KEY': this.apiKey });
153
+ headers = {
154
+ 'Content-Type': 'multipart/form-data',
155
+ 'X-API-KEY': this.apiKey,
156
+ };
156
157
  url = "/api/v1/runs?workflow_id=".concat(encodeURIComponent(workflowId)).concat(groupId ? "&group_id=".concat(encodeURIComponent(groupId)) : '');
157
158
  return [4 /*yield*/, this.client.post(url, formData_1, { headers: headers })];
158
159
  case 1:
@@ -167,6 +168,55 @@ var FlowscaleAPI = /** @class */ (function () {
167
168
  });
168
169
  });
169
170
  };
171
+ /**
172
+ * Executes a workflow and waits for the output by polling.
173
+ * @param workflowId - The ID of the workflow to execute.
174
+ * @param data - Form data including text fields and file uploads.
175
+ * @param groupId - Optional group ID.
176
+ * @param pollIntervalMs - Optional polling interval in milliseconds (default: 1000)
177
+ * @param timeoutMs - Optional timeout in milliseconds (default: 300000 - 5 minutes)
178
+ */
179
+ FlowscaleAPI.prototype.executeWorkflowAsync = function (workflowId_1, data_1, groupId_1) {
180
+ return __awaiter(this, arguments, void 0, function (workflowId, data, groupId, pollIntervalMs, timeoutMs) {
181
+ var startTime, executeResponse, outputName, output;
182
+ var _a, _b;
183
+ if (pollIntervalMs === void 0) { pollIntervalMs = 1000; }
184
+ if (timeoutMs === void 0) { timeoutMs = 300000; }
185
+ return __generator(this, function (_c) {
186
+ switch (_c.label) {
187
+ case 0:
188
+ startTime = Date.now();
189
+ return [4 /*yield*/, this.executeWorkflow(workflowId, data, groupId)];
190
+ case 1:
191
+ executeResponse = _c.sent();
192
+ // If there are no output names, throw an error
193
+ if (!((_b = (_a = executeResponse === null || executeResponse === void 0 ? void 0 : executeResponse.data) === null || _a === void 0 ? void 0 : _a.output_names) === null || _b === void 0 ? void 0 : _b.length)) {
194
+ throw new Error('No output names returned from workflow execution');
195
+ }
196
+ outputName = executeResponse.data.output_names[0];
197
+ _c.label = 2;
198
+ case 2:
199
+ if (!true) return [3 /*break*/, 5];
200
+ if (Date.now() - startTime > timeoutMs) {
201
+ throw new Error("Workflow execution timed out after ".concat(timeoutMs, "ms"));
202
+ }
203
+ return [4 /*yield*/, this.getOutput(outputName)];
204
+ case 3:
205
+ output = _c.sent();
206
+ if (output !== null) {
207
+ return [2 /*return*/, output];
208
+ }
209
+ // Wait for the polling interval
210
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, pollIntervalMs); })];
211
+ case 4:
212
+ // Wait for the polling interval
213
+ _c.sent();
214
+ return [3 /*break*/, 2];
215
+ case 5: return [2 /*return*/];
216
+ }
217
+ });
218
+ });
219
+ };
170
220
  /**
171
221
  * Retrieves the output of a specific run by providing the filename.
172
222
  * @param filename - The filename of the output to retrieve.
@@ -284,21 +334,16 @@ var FlowscaleAPI = /** @class */ (function () {
284
334
  * Helper method to append data to FormData.
285
335
  */
286
336
  FlowscaleAPI.prototype.appendFormData = function (formData, key, value) {
287
- if (value instanceof fs_1.default.ReadStream) {
288
- // It's a file stream
337
+ if (value instanceof Blob || value instanceof File) {
338
+ // Handle File and Blob objects directly
289
339
  formData.append(key, value);
290
340
  }
291
- else if (Buffer.isBuffer(value)) {
292
- // It's a Buffer
293
- formData.append(key, value, { filename: "".concat(key, ".dat") });
294
- }
295
- else if (typeof value === 'string' && fs_1.default.existsSync(value)) {
296
- // If the value is a file path
297
- var fileName = path_1.default.basename(value);
298
- formData.append(key, fs_1.default.createReadStream(value), fileName);
341
+ else if (typeof value === 'object' && value !== null) {
342
+ // Handle plain objects by stringifying
343
+ formData.append(key, JSON.stringify(value));
299
344
  }
300
345
  else {
301
- // Assume it's a string or other value
346
+ // Handle primitive values
302
347
  formData.append(key, value);
303
348
  }
304
349
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowscale",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "An NPM library for communicating with the Flowscale APIs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",