@comfyorg/sdk 0.1.0 → 0.1.1
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 +17 -0
- package/dist/low/generated/types.gen.d.ts +9 -0
- package/dist/low/generated/zod.gen.d.ts +3 -0
- package/dist/low/generated/zod.gen.js +4 -1
- package/dist/low/transport.d.ts +7 -2
- package/dist/low/transport.js +10 -1
- package/dist/sdk/client.d.ts +8 -0
- package/dist/sdk/client.js +11 -1
- package/package.json +21 -14
package/README.md
CHANGED
|
@@ -79,6 +79,23 @@ await job.wait(); // poll to terminal (adaptive backoff); or call job.refresh()
|
|
|
79
79
|
console.log(job.status, job.outputs);
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
## Partner (API) node auth
|
|
83
|
+
|
|
84
|
+
Workflows that use partner/API nodes (Gemini, etc.) need a Comfy API key to
|
|
85
|
+
authenticate them. Pass it per submit with `apiKey`. This is **not** the same as
|
|
86
|
+
the credential you construct `Comfy` with: the constructor key authenticates
|
|
87
|
+
_you_ to the server, while this one authenticates the partner nodes _inside_ the
|
|
88
|
+
workflow (it is often the same `comfyui-…` key):
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const job = await client.run(wf, { apiKey: "comfyui-…" });
|
|
92
|
+
// or: await client.submit(wf, { apiKey: "comfyui-…" });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The SDK sends it once as `extra_data.api_key_comfy_org` alongside the workflow —
|
|
96
|
+
one key authenticates every partner node in the graph. It is never logged or
|
|
97
|
+
persisted by the SDK. Omit `apiKey` and no `extra_data` is sent at all.
|
|
98
|
+
|
|
82
99
|
## Assets and `core/ASSET`
|
|
83
100
|
|
|
84
101
|
`client.assets.fromFile(path)` / `client.assets.fromBytes(data, options)`
|
|
@@ -371,6 +371,15 @@ export type PostJobsData = {
|
|
|
371
371
|
workflow: {
|
|
372
372
|
[key: string]: unknown;
|
|
373
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* Optional typed extras submitted alongside the workflow. Closed: only the enumerated properties are accepted.
|
|
376
|
+
*/
|
|
377
|
+
extra_data?: {
|
|
378
|
+
/**
|
|
379
|
+
* API key for partner (API) nodes.
|
|
380
|
+
*/
|
|
381
|
+
api_key_comfy_org?: string;
|
|
382
|
+
};
|
|
374
383
|
};
|
|
375
384
|
headers?: {
|
|
376
385
|
/**
|
|
@@ -253,6 +253,9 @@ export declare const zGetAssetContentPath: z.ZodObject<{
|
|
|
253
253
|
export declare const zGetAssetContentResponse: z.ZodString;
|
|
254
254
|
export declare const zPostJobsBody: z.ZodObject<{
|
|
255
255
|
workflow: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
256
|
+
extra_data: z.ZodOptional<z.ZodObject<{
|
|
257
|
+
api_key_comfy_org: z.ZodOptional<z.ZodString>;
|
|
258
|
+
}, z.core.$strip>>;
|
|
256
259
|
}, z.core.$strip>;
|
|
257
260
|
export declare const zPostJobsHeaders: z.ZodObject<{
|
|
258
261
|
'Idempotency-Key': z.ZodOptional<z.ZodString>;
|
|
@@ -172,7 +172,10 @@ export const zGetAssetContentPath = z.object({
|
|
|
172
172
|
*/
|
|
173
173
|
export const zGetAssetContentResponse = z.string();
|
|
174
174
|
export const zPostJobsBody = z.object({
|
|
175
|
-
workflow: z.record(z.string(), z.unknown())
|
|
175
|
+
workflow: z.record(z.string(), z.unknown()),
|
|
176
|
+
extra_data: z.object({
|
|
177
|
+
api_key_comfy_org: z.string().optional()
|
|
178
|
+
}).optional()
|
|
176
179
|
});
|
|
177
180
|
export const zPostJobsHeaders = z.object({
|
|
178
181
|
'Idempotency-Key': z.string().optional()
|
package/dist/low/transport.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* the Python SDK (its `AsyncComfyLow`; there is no sync variant here — see
|
|
26
26
|
* the package README for why).
|
|
27
27
|
*/
|
|
28
|
-
import type { Asset, Job } from "./generated/types.gen.js";
|
|
28
|
+
import type { Asset, Job, PostJobsData } from "./generated/types.gen.js";
|
|
29
29
|
import { type RawEvent } from "./sse.js";
|
|
30
30
|
export interface RequestOptions {
|
|
31
31
|
headers?: Record<string, string>;
|
|
@@ -100,9 +100,14 @@ export declare class ComfyLow {
|
|
|
100
100
|
range?: readonly [number, number];
|
|
101
101
|
signal?: AbortSignal;
|
|
102
102
|
}): Promise<Response>;
|
|
103
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* `POST /api/v2/jobs`. `extraData` (e.g. the partner-node API key) is a
|
|
105
|
+
* sibling of `workflow` on the wire, per the spec's closed `extra_data`
|
|
106
|
+
* object; omitted entirely when not provided, never sent empty.
|
|
107
|
+
*/
|
|
104
108
|
postJobs(workflow: Record<string, unknown>, options?: {
|
|
105
109
|
idempotencyKey?: string;
|
|
110
|
+
extraData?: PostJobsData["body"]["extra_data"];
|
|
106
111
|
signal?: AbortSignal;
|
|
107
112
|
}): Promise<Job>;
|
|
108
113
|
/** `GET /api/v2/jobs/{id}` (or an absolute self link). */
|
package/dist/low/transport.js
CHANGED
|
@@ -210,13 +210,22 @@ export class ComfyLow {
|
|
|
210
210
|
return response;
|
|
211
211
|
}
|
|
212
212
|
// -- jobs -----------------------------------------------------------------
|
|
213
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* `POST /api/v2/jobs`. `extraData` (e.g. the partner-node API key) is a
|
|
215
|
+
* sibling of `workflow` on the wire, per the spec's closed `extra_data`
|
|
216
|
+
* object; omitted entirely when not provided, never sent empty.
|
|
217
|
+
*/
|
|
214
218
|
async postJobs(workflow, options = {}) {
|
|
215
219
|
const headers = {};
|
|
216
220
|
if (options.idempotencyKey) {
|
|
217
221
|
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
218
222
|
}
|
|
219
223
|
const json = { workflow };
|
|
224
|
+
// Only attach a non-empty extra_data — never serialize an empty object onto
|
|
225
|
+
// the wire (mirrors the Python low layer's truthy guard).
|
|
226
|
+
if (options.extraData && Object.keys(options.extraData).length > 0) {
|
|
227
|
+
json.extra_data = options.extraData;
|
|
228
|
+
}
|
|
220
229
|
const response = await this.request("POST", "/jobs", { headers, json, signal: options.signal });
|
|
221
230
|
return this.parseOrRaise(response, [201]);
|
|
222
231
|
}
|
package/dist/sdk/client.d.ts
CHANGED
|
@@ -57,14 +57,22 @@ export declare class Comfy {
|
|
|
57
57
|
* idempotent, pass an explicit `idempotencyKey` and reuse it. Note a reused
|
|
58
58
|
* key is *rejected*, not replayed: on reuse, catch the error and poll/list
|
|
59
59
|
* for the job the first attempt already created.
|
|
60
|
+
*
|
|
61
|
+
* Pass `apiKey` to authenticate partner (API) nodes in the workflow (for
|
|
62
|
+
* example Gemini) — it is sent once, as `extra_data.api_key_comfy_org`
|
|
63
|
+
* alongside the workflow, and is unrelated to the `Idempotency-Key`: it
|
|
64
|
+
* does not affect idempotency and is never persisted or logged by this
|
|
65
|
+
* SDK. Omit it and no `extra_data` is sent at all.
|
|
60
66
|
*/
|
|
61
67
|
submit(workflow: Workflow, options?: {
|
|
62
68
|
idempotencyKey?: string;
|
|
69
|
+
apiKey?: string;
|
|
63
70
|
signal?: AbortSignal;
|
|
64
71
|
}): Promise<Job>;
|
|
65
72
|
/** Submit, then poll to terminal (authoritative). Throws on failure. */
|
|
66
73
|
run(workflow: Workflow, options?: {
|
|
67
74
|
timeoutMs?: number;
|
|
75
|
+
apiKey?: string;
|
|
68
76
|
signal?: AbortSignal;
|
|
69
77
|
}): Promise<Job>;
|
|
70
78
|
}
|
package/dist/sdk/client.js
CHANGED
|
@@ -77,16 +77,26 @@ export class Comfy {
|
|
|
77
77
|
* idempotent, pass an explicit `idempotencyKey` and reuse it. Note a reused
|
|
78
78
|
* key is *rejected*, not replayed: on reuse, catch the error and poll/list
|
|
79
79
|
* for the job the first attempt already created.
|
|
80
|
+
*
|
|
81
|
+
* Pass `apiKey` to authenticate partner (API) nodes in the workflow (for
|
|
82
|
+
* example Gemini) — it is sent once, as `extra_data.api_key_comfy_org`
|
|
83
|
+
* alongside the workflow, and is unrelated to the `Idempotency-Key`: it
|
|
84
|
+
* does not affect idempotency and is never persisted or logged by this
|
|
85
|
+
* SDK. Omit it and no `extra_data` is sent at all.
|
|
80
86
|
*/
|
|
81
87
|
async submit(workflow, options = {}) {
|
|
82
88
|
guardUiFormat(workflow);
|
|
83
89
|
const graph = await this.materialize(workflow, options.signal);
|
|
84
90
|
const key = options.idempotencyKey ?? newIdempotencyKey();
|
|
91
|
+
// A falsy apiKey (undefined or "") means "no key" — send no extra_data,
|
|
92
|
+
// matching the Python SDK's behavior so the two stay in lockstep.
|
|
93
|
+
const extraData = options.apiKey ? { api_key_comfy_org: options.apiKey } : undefined;
|
|
85
94
|
const deadline = Date.now() + QUEUE_RETRY_BUDGET_MS;
|
|
86
95
|
for (;;) {
|
|
87
96
|
try {
|
|
88
97
|
const job = await this.low.postJobs(graph, {
|
|
89
98
|
idempotencyKey: key,
|
|
99
|
+
extraData,
|
|
90
100
|
signal: options.signal,
|
|
91
101
|
});
|
|
92
102
|
return new Job(this.low, job);
|
|
@@ -105,7 +115,7 @@ export class Comfy {
|
|
|
105
115
|
}
|
|
106
116
|
/** Submit, then poll to terminal (authoritative). Throws on failure. */
|
|
107
117
|
async run(workflow, options = {}) {
|
|
108
|
-
const job = await this.submit(workflow, { signal: options.signal });
|
|
118
|
+
const job = await this.submit(workflow, { apiKey: options.apiKey, signal: options.signal });
|
|
109
119
|
return options.timeoutMs === undefined
|
|
110
120
|
? job.result(options.signal)
|
|
111
121
|
: runWithTimeout(job, options.timeoutMs, options.signal);
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comfyorg/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TypeScript SDK for running ComfyUI workflows via the Comfy API v2 (self-hosted, Comfy Cloud, serverless).",
|
|
6
|
+
"homepage": "https://github.com/Comfy-Org/ComfyTypeScriptSDK#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/Comfy-Org/ComfyTypeScriptSDK/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Comfy-Org/ComfyTypeScriptSDK.git"
|
|
13
|
+
},
|
|
6
14
|
"files": [
|
|
7
15
|
"dist"
|
|
8
16
|
],
|
|
@@ -19,17 +27,6 @@
|
|
|
19
27
|
"default": "./dist/low/index.js"
|
|
20
28
|
}
|
|
21
29
|
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsc",
|
|
24
|
-
"generate": "openapi-ts",
|
|
25
|
-
"lint": "oxlint .",
|
|
26
|
-
"format": "oxfmt --write .",
|
|
27
|
-
"format:check": "oxfmt --check .",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
29
|
-
"test": "vitest run",
|
|
30
|
-
"test:coverage": "vitest run --coverage",
|
|
31
|
-
"check:spec-drift": "node scripts/check-spec-drift.mjs"
|
|
32
|
-
},
|
|
33
30
|
"dependencies": {
|
|
34
31
|
"eventsource-parser": "^3.0.0",
|
|
35
32
|
"hash-wasm": "^4.11.0",
|
|
@@ -48,5 +45,15 @@
|
|
|
48
45
|
"engines": {
|
|
49
46
|
"node": ">=22"
|
|
50
47
|
},
|
|
51
|
-
"
|
|
52
|
-
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"generate": "openapi-ts",
|
|
51
|
+
"lint": "oxlint .",
|
|
52
|
+
"format": "oxfmt --write .",
|
|
53
|
+
"format:check": "oxfmt --check .",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:coverage": "vitest run --coverage",
|
|
57
|
+
"check:spec-drift": "node scripts/check-spec-drift.mjs"
|
|
58
|
+
}
|
|
59
|
+
}
|