@flowscale/sdk 1.0.0 → 1.2.0

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 ADDED
@@ -0,0 +1,430 @@
1
+ # @flowscale/sdk — External App Guide
2
+
3
+ This guide covers building apps that run **outside** FlowScale using the HTTP transport. You can use any language or framework — Node.js scripts, Express servers, Next.js apps, Python backends calling a Node wrapper, anything that can make HTTP requests.
4
+
5
+ > **Prerequisite:** A running FlowScale AIOS instance. Default local address: `http://localhost:14173`.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @flowscale/sdk
13
+ # or
14
+ pnpm add @flowscale/sdk
15
+ ```
16
+
17
+ Requires Node.js 18+ (uses the native `fetch` API).
18
+
19
+ ---
20
+
21
+ ## Authentication
22
+
23
+ Every request requires a session. Call `login()` once to get a token, then pass it to `createClient()`. Tokens are valid for **7 days**.
24
+
25
+ ```ts
26
+ import { login, createClient } from '@flowscale/sdk'
27
+
28
+ const token = await login({
29
+ baseUrl: 'http://localhost:14173',
30
+ username: 'admin',
31
+ password: 'your-password',
32
+ })
33
+
34
+ const client = createClient({
35
+ baseUrl: 'http://localhost:14173',
36
+ sessionToken: token,
37
+ })
38
+ ```
39
+
40
+ > `login()` reads the session token from the `Set-Cookie` response header. This works in **Node.js** (`fetch` exposes `Set-Cookie`). In a browser context, the browser handles the cookie automatically and you do not need to call `login()` — just make requests with `credentials: 'include'` against `http://localhost:14173` directly.
41
+
42
+ ### Persisting the token
43
+
44
+ ```ts
45
+ import { writeFileSync, readFileSync, existsSync } from 'fs'
46
+
47
+ const TOKEN_FILE = '.flowscale-token'
48
+
49
+ async function getToken(): Promise<string> {
50
+ if (existsSync(TOKEN_FILE)) {
51
+ return readFileSync(TOKEN_FILE, 'utf-8').trim()
52
+ }
53
+ const token = await login({
54
+ baseUrl: 'http://localhost:14173',
55
+ username: 'admin',
56
+ password: process.env.FS_PASSWORD!,
57
+ })
58
+ writeFileSync(TOKEN_FILE, token)
59
+ return token
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Creating a client
66
+
67
+ ```ts
68
+ const client = createClient({
69
+ baseUrl: 'http://localhost:14173', // required
70
+ sessionToken: token, // required
71
+
72
+ // Optional:
73
+ timeout: 300_000, // ms to wait for a tool to finish (default: 5 minutes)
74
+ pollInterval: 2_000, // ms between status polls for API-engine tools (default: 2s)
75
+ })
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Listing tools
81
+
82
+ `client.tools.list()` returns all tools with `status = 'production'`. Each row is the raw DB record — the key field for running tools is `id`, and `schemaJson` (a JSON string) describes the inputs.
83
+
84
+ ```ts
85
+ const tools = await client.tools.list()
86
+
87
+ for (const tool of tools) {
88
+ console.log(tool.id, tool.name, tool.description)
89
+ }
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Inspecting a tool's inputs
95
+
96
+ The `schemaJson` field on each tool is a JSON string containing an array of `WorkflowIO` objects — one per configurable input or output node.
97
+
98
+ ```ts
99
+ const tool = await client.tools.get('your-tool-id')
100
+
101
+ interface WorkflowIO {
102
+ nodeId: string // ComfyUI node ID, e.g. "6"
103
+ nodeType: string // e.g. "CLIPTextEncode"
104
+ nodeTitle: string // human label, e.g. "Positive Prompt"
105
+ paramName: string // e.g. "text"
106
+ paramType: 'string' | 'number' | 'boolean' | 'image' | 'select'
107
+ defaultValue?: unknown
108
+ options?: string[] // for paramType 'select'
109
+ isInput: boolean // false = output node
110
+ }
111
+
112
+ const schema: WorkflowIO[] = JSON.parse(tool.schemaJson)
113
+ const inputs = schema.filter(f => f.isInput)
114
+
115
+ for (const field of inputs) {
116
+ console.log(`${field.nodeId}__${field.paramName}`, field.paramType, field.defaultValue)
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Running a tool
123
+
124
+ ### Input key format
125
+
126
+ Input keys are always **`"${nodeId}__${paramName}"`** (double underscore). You can see the exact keys by inspecting `schemaJson` as shown above, or read them from the tool's form in the FlowScale UI.
127
+
128
+ ```ts
129
+ const result = await client.tools.run('your-tool-id', {
130
+ '6__text': 'a photorealistic cat on the moon, cinematic lighting',
131
+ '7__text': 'blurry, low quality, watermark',
132
+ '5__width': 1024,
133
+ '5__height': 1024,
134
+ '3__steps': 20,
135
+ '3__cfg': 7,
136
+ })
137
+ ```
138
+
139
+ `tools.run()` blocks until the tool finishes and returns a `ToolRunResult`:
140
+
141
+ ```ts
142
+ interface ToolRunResult {
143
+ executionId: string
144
+ toolId: string
145
+ status: 'completed'
146
+ outputs: ToolOutputItem[]
147
+ }
148
+
149
+ interface ToolOutputItem {
150
+ kind: 'image' | 'video' | 'audio' | 'file'
151
+ filename: string
152
+ subfolder: string
153
+ path: string // relative URL, e.g. /api/outputs/tool-id/abc12345_output.png
154
+ }
155
+ ```
156
+
157
+ ### Accessing outputs
158
+
159
+ Output `path` values are **relative URLs**. Use `client.resolveUrl()` to get the full URL for downloading or displaying:
160
+
161
+ ```ts
162
+ for (const output of result.outputs) {
163
+ const url = client.resolveUrl(output.path)
164
+ console.log(output.kind, url)
165
+ // e.g. image http://localhost:14173/api/outputs/tool-id/abc12345_output.png
166
+ }
167
+ ```
168
+
169
+ ### Downloading an output to disk
170
+
171
+ ```ts
172
+ import { writeFileSync } from 'fs'
173
+ import path from 'path'
174
+
175
+ for (const output of result.outputs) {
176
+ const url = client.resolveUrl(output.path)
177
+ const res = await fetch(url, {
178
+ headers: { Cookie: `fs_session=${token}` },
179
+ })
180
+ const buffer = Buffer.from(await res.arrayBuffer())
181
+ writeFileSync(path.basename(output.filename), buffer)
182
+ console.log('Saved', output.filename)
183
+ }
184
+ ```
185
+
186
+ ### Progress callback
187
+
188
+ ```ts
189
+ const result = await client.tools.run('your-tool-id', inputs, {
190
+ onProgress: (status) => console.log('[progress]', status),
191
+ timeout: 120_000, // override the client default for this call
192
+ })
193
+ ```
194
+
195
+ `onProgress` is called with `'running'` when the job starts and `'completed'` when it finishes. For API-engine tools it is also called on each poll cycle.
196
+
197
+ ---
198
+
199
+ ## Error handling
200
+
201
+ `tools.run()` throws a plain `Error` on failure. The message is either the error from FlowScale or a timeout message.
202
+
203
+ ```ts
204
+ try {
205
+ const result = await client.tools.run('your-tool-id', inputs)
206
+ console.log('outputs:', result.outputs)
207
+ } catch (err) {
208
+ if (err instanceof Error) {
209
+ console.error('Tool failed:', err.message)
210
+ // Possible messages:
211
+ // - 'Tool not found'
212
+ // - 'No ComfyUI port configured for this tool'
213
+ // - 'ComfyUI reported an error'
214
+ // - 'Execution timed out after 300s'
215
+ // - Node-level error details from ComfyUI
216
+ }
217
+ }
218
+ ```
219
+
220
+ `login()` throws with the server's error message on bad credentials, disabled account, or network failure.
221
+
222
+ ---
223
+
224
+ ## Image inputs
225
+
226
+ For tools with `paramType: 'image'` inputs, you need to upload the image to the ComfyUI instance first, then pass the returned filename as the input value.
227
+
228
+ First discover which ComfyUI port the tool uses:
229
+
230
+ ```ts
231
+ const tool = await client.tools.get('your-tool-id')
232
+ const comfyPort = tool.comfyPort // e.g. 8188
233
+ ```
234
+
235
+ Then upload the image via the ComfyUI proxy:
236
+
237
+ ```ts
238
+ import { readFileSync } from 'fs'
239
+
240
+ async function uploadImage(
241
+ baseUrl: string,
242
+ token: string,
243
+ comfyPort: number,
244
+ filePath: string,
245
+ ): Promise<string> {
246
+ const fileBuffer = readFileSync(filePath)
247
+ const filename = path.basename(filePath)
248
+
249
+ const form = new FormData()
250
+ form.append('image', new Blob([fileBuffer]), filename)
251
+ form.append('overwrite', 'true')
252
+
253
+ const res = await fetch(`${baseUrl}/api/comfy/${comfyPort}/upload/image`, {
254
+ method: 'POST',
255
+ headers: { Cookie: `fs_session=${token}` },
256
+ body: form,
257
+ })
258
+ if (!res.ok) throw new Error(`Upload failed: ${res.statusText}`)
259
+ const data = await res.json() as { name: string }
260
+ return data.name // filename to pass as input
261
+ }
262
+
263
+ // Then run the tool:
264
+ const uploadedFilename = await uploadImage(
265
+ 'http://localhost:14173',
266
+ token,
267
+ tool.comfyPort,
268
+ './photo.jpg',
269
+ )
270
+
271
+ const result = await client.tools.run('your-tool-id', {
272
+ '10__image': uploadedFilename,
273
+ })
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Full example — text-to-image script
279
+
280
+ ```ts
281
+ import { login, createClient } from '@flowscale/sdk'
282
+ import { writeFileSync } from 'fs'
283
+ import path from 'path'
284
+
285
+ const BASE = 'http://localhost:14173'
286
+
287
+ async function main() {
288
+ // 1. Authenticate
289
+ const token = await login({ baseUrl: BASE, username: 'admin', password: 'your-password' })
290
+ const client = createClient({ baseUrl: BASE, sessionToken: token })
291
+
292
+ // 2. List available tools
293
+ const tools = await client.tools.list()
294
+ console.log('Available tools:', tools.map(t => `${t.name} (${t.id})`))
295
+
296
+ // 3. Inspect a tool's inputs
297
+ const tool = tools[0]
298
+ const schema = JSON.parse(tool.schemaJson)
299
+ console.log('Inputs:')
300
+ for (const field of schema.filter((f: { isInput: boolean }) => f.isInput)) {
301
+ console.log(` ${field.nodeId}__${field.paramName} (${field.paramType})`)
302
+ }
303
+
304
+ // 4. Run the tool
305
+ const result = await client.tools.run(tool.id, {
306
+ '6__text': 'a majestic mountain landscape at golden hour',
307
+ '7__text': 'blurry, noise, watermark',
308
+ '5__width': 1024,
309
+ '5__height': 1024,
310
+ '3__steps': 20,
311
+ }, {
312
+ onProgress: (s) => process.stdout.write(`\r${s}...`),
313
+ })
314
+
315
+ console.log('\nDone. Outputs:')
316
+
317
+ // 5. Download outputs
318
+ for (const output of result.outputs) {
319
+ const url = client.resolveUrl(output.path)
320
+ const res = await fetch(url, { headers: { Cookie: `fs_session=${token}` } })
321
+ const buffer = Buffer.from(await res.arrayBuffer())
322
+ const outFile = `output_${output.filename}`
323
+ writeFileSync(outFile, buffer)
324
+ console.log(` Saved ${outFile} (${output.kind})`)
325
+ }
326
+ }
327
+
328
+ main().catch(console.error)
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Full example — Express server wrapping a tool
334
+
335
+ ```ts
336
+ import express from 'express'
337
+ import { login, createClient } from '@flowscale/sdk'
338
+
339
+ const app = express()
340
+ app.use(express.json())
341
+
342
+ const BASE = 'http://localhost:14173'
343
+ let client: Awaited<ReturnType<typeof createClient>> | null = null
344
+
345
+ async function getClient() {
346
+ if (!client) {
347
+ const token = await login({ baseUrl: BASE, username: 'admin', password: process.env.FS_PASSWORD! })
348
+ client = createClient({ baseUrl: BASE, sessionToken: token })
349
+ }
350
+ return client
351
+ }
352
+
353
+ app.post('/generate', async (req, res) => {
354
+ try {
355
+ const { prompt, negativePrompt = '' } = req.body as { prompt: string; negativePrompt?: string }
356
+ const c = await getClient()
357
+
358
+ const result = await c.tools.run('your-tool-id', {
359
+ '6__text': prompt,
360
+ '7__text': negativePrompt,
361
+ })
362
+
363
+ const urls = result.outputs.map(o => ({
364
+ kind: o.kind,
365
+ url: c.resolveUrl(o.path),
366
+ }))
367
+
368
+ res.json({ executionId: result.executionId, outputs: urls })
369
+ } catch (err) {
370
+ const message = err instanceof Error ? err.message : 'Unknown error'
371
+ res.status(500).json({ error: message })
372
+ }
373
+ })
374
+
375
+ app.listen(3000, () => console.log('Server running on http://localhost:3000'))
376
+ ```
377
+
378
+ ---
379
+
380
+ ## API reference
381
+
382
+ ### `login(opts)`
383
+
384
+ ```ts
385
+ login({
386
+ baseUrl: string,
387
+ username: string,
388
+ password: string,
389
+ }): Promise<string> // returns session token
390
+ ```
391
+
392
+ Throws if credentials are invalid, the account is pending/disabled, or the server is unreachable.
393
+
394
+ ### `createClient(opts)`
395
+
396
+ ```ts
397
+ createClient({
398
+ baseUrl: string,
399
+ sessionToken: string,
400
+ timeout?: number, // default 300_000 ms
401
+ pollInterval?: number, // default 2_000 ms
402
+ }): HttpClient
403
+ ```
404
+
405
+ ### `client.resolveUrl(path)`
406
+
407
+ Prepends `baseUrl` to a relative path. Returns the path unchanged if it is already an absolute URL.
408
+
409
+ ### `client.tools.list()`
410
+
411
+ Returns all tools with `status = 'production'`. Each item includes the raw DB row with `id`, `name`, `description`, `schemaJson`, `comfyPort`, `engine`, etc.
412
+
413
+ ### `client.tools.get(id)`
414
+
415
+ Returns a single tool by ID. Throws `'Tool not found'` (HTTP 404) if it does not exist.
416
+
417
+ ### `client.tools.run(id, inputs, options?)`
418
+
419
+ ```ts
420
+ client.tools.run(
421
+ id: string,
422
+ inputs: Record<string, unknown>,
423
+ options?: {
424
+ timeout?: number,
425
+ onProgress?: (status: string) => void,
426
+ }
427
+ ): Promise<ToolRunResult>
428
+ ```
429
+
430
+ Blocks until the tool completes. Input keys are `"${nodeId}__${paramName}"`. Throws on error or timeout.
package/dist/http.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * HTTP transport for the FlowScale SDK.
3
+ *
4
+ * Use this when building apps that run **outside** FlowScale (e.g. a standalone
5
+ * web app, a Node.js script, or a CLI tool). It talks to a running FlowScale
6
+ * AIOS instance via its REST API instead of the iframe postMessage bridge.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createClient } from '@flowscale/sdk/http'
11
+ *
12
+ * const client = createClient({
13
+ * baseUrl: 'http://localhost:14173',
14
+ * sessionToken: '<your-fs_session-cookie>', // POST /api/auth/login to obtain one
15
+ * })
16
+ *
17
+ * const result = await client.tools.run('sdxl-txt2img', {
18
+ * '6.text': 'a cat on the moon',
19
+ * })
20
+ * console.log(result.outputs) // [{ kind, filename, path }]
21
+ *
22
+ * // Prepend baseUrl to relative paths for direct access:
23
+ * const imageUrl = client.resolveUrl(result.outputs[0].path)
24
+ * ```
25
+ */
26
+ import type { ToolDefinition, ToolRunResult } from './types';
27
+ export interface HttpClientOptions {
28
+ /**
29
+ * Base URL of your FlowScale AIOS instance.
30
+ * @example 'http://localhost:14173'
31
+ */
32
+ baseUrl: string;
33
+ /**
34
+ * API key generated in FlowScale Settings → API Keys.
35
+ * Sent as `Authorization: Bearer <apiKey>` on every request.
36
+ * Use this instead of `sessionToken` for keyless external access.
37
+ * @example 'fs_abc123...'
38
+ */
39
+ apiKey?: string;
40
+ /**
41
+ * Session token obtained from `POST /api/auth/login`.
42
+ * Sent as the `fs_session` cookie on every request.
43
+ * Not needed when using `apiKey`.
44
+ */
45
+ sessionToken?: string;
46
+ /**
47
+ * How long (ms) to wait for a tool execution to complete before timing out.
48
+ * @default 300_000 (5 minutes)
49
+ */
50
+ timeout?: number;
51
+ /**
52
+ * How often (ms) to poll for execution status.
53
+ * @default 2_000
54
+ */
55
+ pollInterval?: number;
56
+ }
57
+ export interface LoginOptions {
58
+ baseUrl: string;
59
+ username: string;
60
+ password: string;
61
+ }
62
+ /** Log in and return a session token without creating a full client. */
63
+ export declare function login(opts: LoginOptions): Promise<string>;
64
+ export interface HttpClient {
65
+ /** Resolve a relative output path (e.g. `/api/outputs/...`) to a full URL. */
66
+ resolveUrl(path: string): string;
67
+ tools: {
68
+ /** List all production tools. */
69
+ list(): Promise<ToolDefinition[]>;
70
+ /** Get a single tool by ID. */
71
+ get(id: string): Promise<ToolDefinition>;
72
+ /**
73
+ * Run a tool and wait for it to complete.
74
+ *
75
+ * For ComfyUI tools, inputs use `"${nodeId}__${paramName}"` keys.
76
+ * For registry tools, inputs use `"${nodeId}.${paramName}"` keys.
77
+ * Image inputs can be passed as base64 data URLs (`data:image/png;base64,...`).
78
+ */
79
+ run(id: string, inputs: Record<string, unknown>, options?: {
80
+ timeout?: number;
81
+ onProgress?: (status: string) => void;
82
+ }): Promise<ToolRunResult>;
83
+ };
84
+ }
85
+ /** Create an HTTP client connected to a FlowScale AIOS instance. */
86
+ export declare function createClient(options: HttpClientOptions): HttpClient;
87
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,MAAM,SAAS,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wEAAwE;AACxE,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAe/D;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,KAAK,EAAE;QACL,iCAAiC;QACjC,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;QAClC,+BAA+B;QAC/B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;QACzC;;;;;;WAMG;QACH,GAAG,CACD,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;SAAE,GACpE,OAAO,CAAC,aAAa,CAAC,CAAC;KAC3B,CAAC;CACH;AAMD,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAoHnE"}
package/dist/http.js ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP transport for the FlowScale SDK.
4
+ *
5
+ * Use this when building apps that run **outside** FlowScale (e.g. a standalone
6
+ * web app, a Node.js script, or a CLI tool). It talks to a running FlowScale
7
+ * AIOS instance via its REST API instead of the iframe postMessage bridge.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { createClient } from '@flowscale/sdk/http'
12
+ *
13
+ * const client = createClient({
14
+ * baseUrl: 'http://localhost:14173',
15
+ * sessionToken: '<your-fs_session-cookie>', // POST /api/auth/login to obtain one
16
+ * })
17
+ *
18
+ * const result = await client.tools.run('sdxl-txt2img', {
19
+ * '6.text': 'a cat on the moon',
20
+ * })
21
+ * console.log(result.outputs) // [{ kind, filename, path }]
22
+ *
23
+ * // Prepend baseUrl to relative paths for direct access:
24
+ * const imageUrl = client.resolveUrl(result.outputs[0].path)
25
+ * ```
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.login = login;
29
+ exports.createClient = createClient;
30
+ /** Log in and return a session token without creating a full client. */
31
+ async function login(opts) {
32
+ const res = await fetch(`${opts.baseUrl}/api/auth/login`, {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify({ username: opts.username, password: opts.password }),
36
+ });
37
+ if (!res.ok) {
38
+ const err = await res.json().catch(() => ({ error: res.statusText }));
39
+ throw new Error(err.error ?? `Login failed: HTTP ${res.status}`);
40
+ }
41
+ // Session token comes back as a Set-Cookie header (fs_session=<token>)
42
+ const cookie = res.headers.get('set-cookie') ?? '';
43
+ const match = cookie.match(/fs_session=([^;]+)/);
44
+ if (!match)
45
+ throw new Error('No session token in login response');
46
+ return match[1];
47
+ }
48
+ function sleep(ms) {
49
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
+ }
51
+ /** Create an HTTP client connected to a FlowScale AIOS instance. */
52
+ function createClient(options) {
53
+ const { baseUrl, sessionToken, apiKey, timeout: defaultTimeout = 300000, pollInterval = 2000, } = options;
54
+ if (!apiKey && !sessionToken) {
55
+ throw new Error('createClient requires either apiKey or sessionToken');
56
+ }
57
+ const authHeaders = apiKey
58
+ ? { Authorization: `Bearer ${apiKey}` }
59
+ : { Cookie: `fs_session=${sessionToken}` };
60
+ async function apiFetch(path, init) {
61
+ const res = await fetch(`${baseUrl}${path}`, {
62
+ ...init,
63
+ headers: {
64
+ 'Content-Type': 'application/json',
65
+ ...authHeaders,
66
+ ...(init?.headers ?? {}),
67
+ },
68
+ });
69
+ if (!res.ok) {
70
+ const err = await res.json().catch(() => ({ error: res.statusText }));
71
+ throw new Error(err.error ?? `HTTP ${res.status} ${path}`);
72
+ }
73
+ return res.json();
74
+ }
75
+ function resolveUrl(path) {
76
+ if (path.startsWith('http://') || path.startsWith('https://'))
77
+ return path;
78
+ return `${baseUrl}${path}`;
79
+ }
80
+ const tools = {
81
+ async list() {
82
+ return apiFetch('/api/tools?status=production');
83
+ },
84
+ async get(id) {
85
+ return apiFetch(`/api/tools/${id}`);
86
+ },
87
+ async run(id, inputs, runOptions = {}) {
88
+ const { timeout = defaultTimeout, onProgress } = runOptions;
89
+ onProgress?.('running');
90
+ // ?wait=true — server polls ComfyUI internally and returns only when done.
91
+ // For API-engine tools the server also runs to completion before responding.
92
+ // Either way we get back a finished execution row directly.
93
+ const execution = await apiFetch(`/api/tools/${id}/executions?wait=true`, {
94
+ method: 'POST',
95
+ body: JSON.stringify({ inputs }),
96
+ signal: AbortSignal.timeout(timeout),
97
+ });
98
+ if (execution.error)
99
+ throw new Error(execution.error);
100
+ // API-engine tools still return 202 immediately — poll the execution record
101
+ if (execution.status === 'running' && execution.executionId) {
102
+ const executionId = execution.executionId;
103
+ const deadline = Date.now() + timeout;
104
+ while (Date.now() < deadline) {
105
+ await sleep(pollInterval);
106
+ const row = await apiFetch(`/api/executions/${executionId}`);
107
+ if (row.status === 'completed') {
108
+ onProgress?.('completed');
109
+ let outputs = [];
110
+ try {
111
+ outputs = JSON.parse(row.outputsJson ?? '[]');
112
+ }
113
+ catch { /* empty */ }
114
+ return { executionId, toolId: id, status: 'completed', outputs };
115
+ }
116
+ if (row.status === 'error')
117
+ throw new Error(row.errorMessage ?? 'Execution failed');
118
+ onProgress?.(row.status);
119
+ }
120
+ throw new Error(`Execution timed out after ${timeout / 1000}s`);
121
+ }
122
+ if (execution.status === 'error')
123
+ throw new Error(execution.errorMessage ?? 'Execution failed');
124
+ onProgress?.('completed');
125
+ let outputs = [];
126
+ try {
127
+ outputs = JSON.parse(execution.outputsJson ?? '[]');
128
+ }
129
+ catch { /* empty */ }
130
+ return {
131
+ executionId: execution.id,
132
+ toolId: id,
133
+ status: 'completed',
134
+ outputs,
135
+ };
136
+ },
137
+ };
138
+ return { resolveUrl, tools };
139
+ }
140
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,10 +4,13 @@ export { providers } from './providers';
4
4
  export { storage } from './storage';
5
5
  export { ui } from './ui';
6
6
  export { app } from './app';
7
+ export { createClient, login } from './http';
8
+ export type { HttpClient, HttpClientOptions, LoginOptions } from './http';
7
9
  export * from './types';
8
10
  /** Convenience default export with all namespaces. */
9
11
  declare const FlowScale: {
10
12
  readonly tools: {
13
+ outputRef(output: import("./types").ToolOutputItem): import("./types").OutputRef;
11
14
  list(): Promise<import("./types").ToolDefinition[]>;
12
15
  get(id: string): Promise<import("./types").ToolDefinition>;
13
16
  run(id: string, inputs: Record<string, unknown>, options?: import("./types").ToolRunOptions): Promise<import("./types").ToolRunResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,cAAc,SAAS,CAAC;AAQxB,sDAAsD;AACtD,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAkD,CAAC;AAClE,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC7C,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC1E,cAAc,SAAS,CAAC;AAQxB,sDAAsD;AACtD,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAkD,CAAC;AAClE,eAAe,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.app = exports.ui = exports.storage = exports.providers = exports.tools = exports.bridge = void 0;
17
+ exports.login = exports.createClient = exports.app = exports.ui = exports.storage = exports.providers = exports.tools = exports.bridge = void 0;
18
18
  var bridge_1 = require("./bridge");
19
19
  Object.defineProperty(exports, "bridge", { enumerable: true, get: function () { return bridge_1.bridge; } });
20
20
  var tools_1 = require("./tools");
@@ -27,6 +27,9 @@ var ui_1 = require("./ui");
27
27
  Object.defineProperty(exports, "ui", { enumerable: true, get: function () { return ui_1.ui; } });
28
28
  var app_1 = require("./app");
29
29
  Object.defineProperty(exports, "app", { enumerable: true, get: function () { return app_1.app; } });
30
+ var http_1 = require("./http");
31
+ Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return http_1.createClient; } });
32
+ Object.defineProperty(exports, "login", { enumerable: true, get: function () { return http_1.login; } });
30
33
  __exportStar(require("./types"), exports);
31
34
  const tools_2 = require("./tools");
32
35
  const providers_2 = require("./providers");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AACf,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AACd,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAClB,qCAAoC;AAA3B,kGAAA,OAAO,OAAA;AAChB,2BAA0B;AAAjB,wFAAA,EAAE,OAAA;AACX,6BAA4B;AAAnB,0FAAA,GAAG,OAAA;AACZ,0CAAwB;AAExB,mCAAgC;AAChC,2CAAwC;AACxC,uCAAoC;AACpC,6BAA0B;AAC1B,+BAA4B;AAE5B,sDAAsD;AACtD,MAAM,SAAS,GAAG,EAAE,KAAK,EAAL,aAAK,EAAE,SAAS,EAAT,qBAAS,EAAE,OAAO,EAAP,iBAAO,EAAE,EAAE,EAAF,OAAE,EAAE,GAAG,EAAH,SAAG,EAAW,CAAC;AAClE,kBAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AACf,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AACd,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAClB,qCAAoC;AAA3B,kGAAA,OAAO,OAAA;AAChB,2BAA0B;AAAjB,wFAAA,EAAE,OAAA;AACX,6BAA4B;AAAnB,0FAAA,GAAG,OAAA;AACZ,+BAA6C;AAApC,oGAAA,YAAY,OAAA;AAAE,6FAAA,KAAK,OAAA;AAE5B,0CAAwB;AAExB,mCAAgC;AAChC,2CAAwC;AACxC,uCAAoC;AACpC,6BAA0B;AAC1B,+BAA4B;AAE5B,sDAAsD;AACtD,MAAM,SAAS,GAAG,EAAE,KAAK,EAAL,aAAK,EAAE,SAAS,EAAT,qBAAS,EAAE,OAAO,EAAP,iBAAO,EAAE,EAAE,EAAF,OAAE,EAAE,GAAG,EAAH,SAAG,EAAW,CAAC;AAClE,kBAAe,SAAS,CAAC"}
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,11 @@
1
- import { ToolDefinition, ToolRunOptions, ToolRunResult } from './types';
1
+ import { ToolDefinition, ToolOutputItem, ToolRunOptions, ToolRunResult, OutputRef } from './types';
2
2
  export declare const tools: {
3
+ /**
4
+ * Create an output reference for chaining one tool's output into another's input.
5
+ * Pass the returned value as an image input to the next tools.run() call.
6
+ * The bridge handles the ComfyUI output→input transfer server-side.
7
+ */
8
+ outputRef(output: ToolOutputItem): OutputRef;
3
9
  /** List all tools available to this app. */
4
10
  list(): Promise<ToolDefinition[]>;
5
11
  /** Get a single tool by ID. */
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExE,eAAO,MAAM,KAAK;IAChB,4CAA4C;YAC9B,OAAO,CAAC,cAAc,EAAE,CAAC;IAIvC,+BAA+B;YACjB,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAI9C,2CAA2C;YAErC,MAAM,UACF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YACtB,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;CAiB1B,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEnG,eAAO,MAAM,KAAK;IAChB;;;;OAIG;sBACe,cAAc,GAAG,SAAS;IAI5C,4CAA4C;YAC9B,OAAO,CAAC,cAAc,EAAE,CAAC;IAIvC,+BAA+B;YACjB,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAI9C,2CAA2C;YAErC,MAAM,UACF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YACtB,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;CAiB1B,CAAC"}
package/dist/tools.js CHANGED
@@ -3,6 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tools = void 0;
4
4
  const bridge_1 = require("./bridge");
5
5
  exports.tools = {
6
+ /**
7
+ * Create an output reference for chaining one tool's output into another's input.
8
+ * Pass the returned value as an image input to the next tools.run() call.
9
+ * The bridge handles the ComfyUI output→input transfer server-side.
10
+ */
11
+ outputRef(output) {
12
+ return { __comfy_output__: { filename: output.filename, subfolder: output.subfolder } };
13
+ },
6
14
  /** List all tools available to this app. */
7
15
  async list() {
8
16
  return bridge_1.bridge.call('tools.list');
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":";;;AAAA,qCAAkC;AAGrB,QAAA,KAAK,GAAG;IACnB,4CAA4C;IAC5C,KAAK,CAAC,IAAI;QACR,OAAO,eAAM,CAAC,IAAI,CAAmB,YAAY,CAAC,CAAC;IACrD,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,OAAO,eAAM,CAAC,IAAI,CAAiB,WAAW,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,GAAG,CACP,EAAU,EACV,MAA+B,EAC/B,UAA0B,EAAE;QAE5B,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAExC,IAAI,WAAqC,CAAC;QAC1C,IAAI,UAAU,EAAE,CAAC;YACf,WAAW,GAAG,eAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE;gBACzD,MAAM,CAAC,GAAG,MAAgD,CAAC;gBAC3D,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,OAAO,MAAM,eAAM,CAAC,IAAI,CAAgB,WAAW,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACT,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":";;;AAAA,qCAAkC;AAGrB,QAAA,KAAK,GAAG;IACnB;;;;OAIG;IACH,SAAS,CAAC,MAAsB;QAC9B,OAAO,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;IAC1F,CAAC;IAED,4CAA4C;IAC5C,KAAK,CAAC,IAAI;QACR,OAAO,eAAM,CAAC,IAAI,CAAmB,YAAY,CAAC,CAAC;IACrD,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,OAAO,eAAM,CAAC,IAAI,CAAiB,WAAW,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,GAAG,CACP,EAAU,EACV,MAA+B,EAC/B,UAA0B,EAAE;QAE5B,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAExC,IAAI,WAAqC,CAAC;QAC1C,IAAI,UAAU,EAAE,CAAC;YACf,WAAW,GAAG,eAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE;gBACzD,MAAM,CAAC,GAAG,MAAgD,CAAC;gBAC3D,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,OAAO,MAAM,eAAM,CAAC,IAAI,CAAgB,WAAW,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACT,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;CACF,CAAC"}
package/dist/types.d.ts CHANGED
@@ -53,9 +53,34 @@ export interface ToolRunOptions {
53
53
  timeout?: number;
54
54
  onProgress?: (progress: number, message?: string) => void;
55
55
  }
56
+ export interface ToolOutputItem {
57
+ kind: 'image' | 'video' | 'audio' | 'file';
58
+ filename: string;
59
+ subfolder: string;
60
+ /** Relative URL served by the host — use directly in <img src> or fetch() */
61
+ path: string;
62
+ }
63
+ /**
64
+ * Pass as an image input to FlowScale.tools.run() to chain the output of one
65
+ * step into the input of the next. The bridge handles the transfer server-side.
66
+ *
67
+ * @example
68
+ * const step1 = await FlowScale.tools.run(TOOL_A, { '1__image': dataUrl })
69
+ * const step2 = await FlowScale.tools.run(TOOL_B, {
70
+ * '1__image': FlowScale.tools.outputRef(step1.outputs[0]),
71
+ * })
72
+ */
73
+ export interface OutputRef {
74
+ __comfy_output__: {
75
+ filename: string;
76
+ subfolder: string;
77
+ };
78
+ }
56
79
  export interface ToolRunResult {
57
- outputs: Record<string, unknown>;
58
- executionId?: string;
80
+ executionId: string;
81
+ toolId: string;
82
+ status: 'completed';
83
+ outputs: ToolOutputItem[];
59
84
  }
60
85
  export type ProviderName = 'fal' | 'replicate' | 'openrouter' | 'huggingface';
61
86
  export interface ProviderStatus {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flowscale/sdk",
3
- "version": "1.0.0",
4
- "description": "FlowScale EIOS SDK — build apps that run inside FlowScale AI OS",
3
+ "version": "1.2.0",
4
+ "description": "FlowScale AIOS SDK — build apps inside FlowScale or externally via HTTP",
5
5
  "license": "AGPL-3.0-only",
6
6
  "files": [
7
7
  "dist"
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "git+https://github.com/FlowScale-AI/flowscale-eios.git"
29
+ "url": "git+https://github.com/FlowScale-AI/flowscale-aios.git"
30
30
  },
31
31
  "devDependencies": {
32
32
  "typescript": "^5.8.3"