flowscale 2.1.0-beta.4 → 2.1.0-beta.6
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/dist/operator-server.d.ts +19 -9
- package/dist/operator-server.js +241 -64
- package/package.json +1 -1
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import type { NodePodsTransport } from './node-transport';
|
|
2
2
|
/**
|
|
3
|
-
* PodsOperatorServer — exposes a
|
|
4
|
-
*
|
|
5
|
-
* without any direct knowledge of ComfyUI.
|
|
3
|
+
* PodsOperatorServer — exposes a complete ComfyUI proxy API so that remote
|
|
4
|
+
* FlowScale apps (e.g. AI OS, Studio) can operate entirely through the operator
|
|
5
|
+
* without any direct knowledge of ComfyUI ports or URLs.
|
|
6
6
|
*
|
|
7
|
-
* Endpoints:
|
|
8
|
-
* GET /api/pods
|
|
9
|
-
* GET /api/pods/:id
|
|
10
|
-
* POST /api/pods/:id/upload
|
|
11
|
-
* POST /api/pods/:id/execute
|
|
12
|
-
* GET /api/pods/:id/view?filename=
|
|
7
|
+
* HTTP Endpoints:
|
|
8
|
+
* GET /api/pods — list all pods
|
|
9
|
+
* GET /api/pods/:id — get a single pod
|
|
10
|
+
* POST /api/pods/:id/upload — upload a file to ComfyUI
|
|
11
|
+
* POST /api/pods/:id/execute — execute workflow and wait (blocking)
|
|
12
|
+
* GET /api/pods/:id/view?filename= — proxy a ComfyUI output image
|
|
13
|
+
* GET /api/pods/:id/workflow?filename= — load a workflow JSON from ComfyUI userdata
|
|
14
|
+
* POST /api/pods/:id/queue — queue a prompt (non-blocking, returns prompt_id)
|
|
15
|
+
* GET /api/pods/:id/history/:promptId — fetch execution history for a prompt
|
|
16
|
+
* GET /api/pods/:id/object_info — proxy ComfyUI node type definitions
|
|
17
|
+
* POST /api/pods/:id/interrupt — cancel current execution
|
|
18
|
+
*
|
|
19
|
+
* WebSocket:
|
|
20
|
+
* WS /api/pods/:id/ws?clientId= — transparent tunnel to ComfyUI WS
|
|
13
21
|
*
|
|
14
22
|
* Serves on 0.0.0.0 so any machine on the LAN can reach it.
|
|
15
23
|
* Includes CORS headers for browser-based clients.
|
|
@@ -23,6 +31,8 @@ export declare class PodsOperatorServer {
|
|
|
23
31
|
start(): void;
|
|
24
32
|
stop(): void;
|
|
25
33
|
private _json;
|
|
34
|
+
/** Pipe a GET response from ComfyUI directly to the client (handles large payloads). */
|
|
35
|
+
private _proxyGet;
|
|
26
36
|
private _readBody;
|
|
27
37
|
/** Returns the port of the first running instance in the given pod. */
|
|
28
38
|
private _runningPort;
|
package/dist/operator-server.js
CHANGED
|
@@ -71,18 +71,27 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
71
71
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72
72
|
exports.PodsOperatorServer = void 0;
|
|
73
73
|
var http = __importStar(require("http"));
|
|
74
|
+
var net = __importStar(require("net"));
|
|
74
75
|
var local_pods_1 = require("./local-pods");
|
|
75
76
|
/**
|
|
76
|
-
* PodsOperatorServer — exposes a
|
|
77
|
-
*
|
|
78
|
-
* without any direct knowledge of ComfyUI.
|
|
77
|
+
* PodsOperatorServer — exposes a complete ComfyUI proxy API so that remote
|
|
78
|
+
* FlowScale apps (e.g. AI OS, Studio) can operate entirely through the operator
|
|
79
|
+
* without any direct knowledge of ComfyUI ports or URLs.
|
|
79
80
|
*
|
|
80
|
-
* Endpoints:
|
|
81
|
-
* GET /api/pods
|
|
82
|
-
* GET /api/pods/:id
|
|
83
|
-
* POST /api/pods/:id/upload
|
|
84
|
-
* POST /api/pods/:id/execute
|
|
85
|
-
* GET /api/pods/:id/view?filename=
|
|
81
|
+
* HTTP Endpoints:
|
|
82
|
+
* GET /api/pods — list all pods
|
|
83
|
+
* GET /api/pods/:id — get a single pod
|
|
84
|
+
* POST /api/pods/:id/upload — upload a file to ComfyUI
|
|
85
|
+
* POST /api/pods/:id/execute — execute workflow and wait (blocking)
|
|
86
|
+
* GET /api/pods/:id/view?filename= — proxy a ComfyUI output image
|
|
87
|
+
* GET /api/pods/:id/workflow?filename= — load a workflow JSON from ComfyUI userdata
|
|
88
|
+
* POST /api/pods/:id/queue — queue a prompt (non-blocking, returns prompt_id)
|
|
89
|
+
* GET /api/pods/:id/history/:promptId — fetch execution history for a prompt
|
|
90
|
+
* GET /api/pods/:id/object_info — proxy ComfyUI node type definitions
|
|
91
|
+
* POST /api/pods/:id/interrupt — cancel current execution
|
|
92
|
+
*
|
|
93
|
+
* WebSocket:
|
|
94
|
+
* WS /api/pods/:id/ws?clientId= — transparent tunnel to ComfyUI WS
|
|
86
95
|
*
|
|
87
96
|
* Serves on 0.0.0.0 so any machine on the LAN can reach it.
|
|
88
97
|
* Includes CORS headers for browser-based clients.
|
|
@@ -100,10 +109,11 @@ var PodsOperatorServer = /** @class */ (function () {
|
|
|
100
109
|
if (this.server)
|
|
101
110
|
return;
|
|
102
111
|
this.server = http.createServer(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
103
|
-
var url, query, pods, podMatch, pod, uploadMatch, podId,
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
var url, query, pods, podMatch, pod, uploadMatch, podId, port_1, executeMatch, podId, body, _a, _b, result, viewMatch, podId, port, workflowMatch, podId, port, filename, encoded, queueMatch, podId, port_2, body_1, historyMatch, podId, promptId, port, objectInfoMatch, podId, port, interruptMatch, podId, port_3, err_1;
|
|
113
|
+
var _this = this;
|
|
114
|
+
var _c, _d, _e, _f, _g, _h;
|
|
115
|
+
return __generator(this, function (_j) {
|
|
116
|
+
switch (_j.label) {
|
|
107
117
|
case 0:
|
|
108
118
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
109
119
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
@@ -115,92 +125,242 @@ var PodsOperatorServer = /** @class */ (function () {
|
|
|
115
125
|
}
|
|
116
126
|
url = ((_c = req.url) !== null && _c !== void 0 ? _c : '/').split('?')[0];
|
|
117
127
|
query = new URLSearchParams((_e = ((_d = req.url) !== null && _d !== void 0 ? _d : '').split('?')[1]) !== null && _e !== void 0 ? _e : '');
|
|
118
|
-
|
|
128
|
+
_j.label = 1;
|
|
119
129
|
case 1:
|
|
120
|
-
|
|
130
|
+
_j.trys.push([1, 31, , 32]);
|
|
121
131
|
if (!(req.method === 'GET' && (url === '/api/pods' || url === '/api/pods/'))) return [3 /*break*/, 3];
|
|
122
132
|
return [4 /*yield*/, this.transport.list()];
|
|
123
133
|
case 2:
|
|
124
|
-
pods =
|
|
134
|
+
pods = _j.sent();
|
|
125
135
|
return [2 /*return*/, this._json(res, 200, pods)];
|
|
126
136
|
case 3:
|
|
127
137
|
podMatch = url.match(/^\/api\/pods\/([^/]+)$/);
|
|
128
138
|
if (!(req.method === 'GET' && podMatch)) return [3 /*break*/, 5];
|
|
129
139
|
return [4 /*yield*/, this.transport.get(decodeURIComponent(podMatch[1]))];
|
|
130
140
|
case 4:
|
|
131
|
-
pod =
|
|
141
|
+
pod = _j.sent();
|
|
132
142
|
if (!pod)
|
|
133
143
|
return [2 /*return*/, this._json(res, 404, { error: 'Pod not found' })];
|
|
134
144
|
return [2 /*return*/, this._json(res, 200, pod)];
|
|
135
145
|
case 5:
|
|
136
146
|
uploadMatch = url.match(/^\/api\/pods\/([^/]+)\/upload$/);
|
|
137
|
-
if (!(req.method === 'POST' && uploadMatch)) return [3 /*break*/,
|
|
147
|
+
if (!(req.method === 'POST' && uploadMatch)) return [3 /*break*/, 8];
|
|
138
148
|
podId = decodeURIComponent(uploadMatch[1]);
|
|
139
149
|
return [4 /*yield*/, this._runningPort(podId)];
|
|
140
150
|
case 6:
|
|
141
|
-
|
|
142
|
-
return [4 /*yield*/,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
port_1 = _j.sent();
|
|
152
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
153
|
+
var _a;
|
|
154
|
+
var headers = {
|
|
155
|
+
'content-type': (_a = req.headers['content-type']) !== null && _a !== void 0 ? _a : 'application/octet-stream',
|
|
156
|
+
};
|
|
157
|
+
if (req.headers['content-length'])
|
|
158
|
+
headers['content-length'] = req.headers['content-length'];
|
|
159
|
+
if (req.headers['transfer-encoding'])
|
|
160
|
+
headers['transfer-encoding'] = req.headers['transfer-encoding'];
|
|
161
|
+
var upstreamReq = http.request({ hostname: 'localhost', port: port_1, path: '/upload/image', method: 'POST', headers: headers }, function (upstreamRes) {
|
|
162
|
+
var chunks = [];
|
|
163
|
+
upstreamRes.on('data', function (chunk) { return chunks.push(chunk); });
|
|
164
|
+
upstreamRes.on('end', function () {
|
|
165
|
+
var _a;
|
|
166
|
+
try {
|
|
167
|
+
var data = JSON.parse(Buffer.concat(chunks).toString());
|
|
168
|
+
_this._json(res, (_a = upstreamRes.statusCode) !== null && _a !== void 0 ? _a : 500, data);
|
|
169
|
+
resolve();
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
reject(e);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
upstreamRes.on('error', reject);
|
|
176
|
+
});
|
|
177
|
+
upstreamReq.on('error', reject);
|
|
178
|
+
req.pipe(upstreamReq);
|
|
150
179
|
})];
|
|
180
|
+
case 7:
|
|
181
|
+
_j.sent();
|
|
182
|
+
return [2 /*return*/];
|
|
151
183
|
case 8:
|
|
152
|
-
upstream = _k.sent();
|
|
153
|
-
return [4 /*yield*/, upstream.json()];
|
|
154
|
-
case 9:
|
|
155
|
-
data = _k.sent();
|
|
156
|
-
return [2 /*return*/, this._json(res, upstream.status, data)];
|
|
157
|
-
case 10:
|
|
158
184
|
executeMatch = url.match(/^\/api\/pods\/([^/]+)\/execute$/);
|
|
159
|
-
if (!(req.method === 'POST' && executeMatch)) return [3 /*break*/,
|
|
185
|
+
if (!(req.method === 'POST' && executeMatch)) return [3 /*break*/, 11];
|
|
160
186
|
podId = decodeURIComponent(executeMatch[1]);
|
|
161
187
|
_b = (_a = JSON).parse;
|
|
162
188
|
return [4 /*yield*/, this._readBody(req)];
|
|
163
|
-
case
|
|
164
|
-
body = _b.apply(_a, [(
|
|
165
|
-
return [4 /*yield*/, this.api.executeWorkflowAndWait(podId, body.workflow, (
|
|
166
|
-
case
|
|
167
|
-
result =
|
|
189
|
+
case 9:
|
|
190
|
+
body = _b.apply(_a, [(_j.sent()).toString()]);
|
|
191
|
+
return [4 /*yield*/, this.api.executeWorkflowAndWait(podId, body.workflow, (_f = body.options) !== null && _f !== void 0 ? _f : {})];
|
|
192
|
+
case 10:
|
|
193
|
+
result = _j.sent();
|
|
168
194
|
return [2 /*return*/, this._json(res, 200, result)];
|
|
169
|
-
case
|
|
195
|
+
case 11:
|
|
170
196
|
viewMatch = url.match(/^\/api\/pods\/([^/]+)\/view$/);
|
|
171
|
-
if (!(req.method === 'GET' && viewMatch)) return [3 /*break*/,
|
|
197
|
+
if (!(req.method === 'GET' && viewMatch)) return [3 /*break*/, 14];
|
|
172
198
|
podId = decodeURIComponent(viewMatch[1]);
|
|
173
199
|
return [4 /*yield*/, this._runningPort(podId)];
|
|
200
|
+
case 12:
|
|
201
|
+
port = _j.sent();
|
|
202
|
+
return [4 /*yield*/, this._proxyGet(res, port, "/view?".concat(query))];
|
|
203
|
+
case 13:
|
|
204
|
+
_j.sent();
|
|
205
|
+
return [2 /*return*/];
|
|
174
206
|
case 14:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
207
|
+
workflowMatch = url.match(/^\/api\/pods\/([^/]+)\/workflow$/);
|
|
208
|
+
if (!(req.method === 'GET' && workflowMatch)) return [3 /*break*/, 17];
|
|
209
|
+
podId = decodeURIComponent(workflowMatch[1]);
|
|
210
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
178
211
|
case 15:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return [2 /*return*/];
|
|
184
|
-
}
|
|
185
|
-
return [4 /*yield*/, upstream.arrayBuffer()];
|
|
212
|
+
port = _j.sent();
|
|
213
|
+
filename = (_g = query.get('filename')) !== null && _g !== void 0 ? _g : '';
|
|
214
|
+
encoded = encodeURIComponent("workflows/".concat(filename));
|
|
215
|
+
return [4 /*yield*/, this._proxyGet(res, port, "/api/userdata/".concat(encoded))];
|
|
186
216
|
case 16:
|
|
187
|
-
|
|
188
|
-
res.writeHead(200, {
|
|
189
|
-
'Content-Type': (_h = upstream.headers.get('content-type')) !== null && _h !== void 0 ? _h : 'image/png',
|
|
190
|
-
'Cache-Control': 'public, max-age=3600',
|
|
191
|
-
});
|
|
192
|
-
res.end(Buffer.from(imageBuffer));
|
|
217
|
+
_j.sent();
|
|
193
218
|
return [2 /*return*/];
|
|
194
219
|
case 17:
|
|
220
|
+
queueMatch = url.match(/^\/api\/pods\/([^/]+)\/queue$/);
|
|
221
|
+
if (!(req.method === 'POST' && queueMatch)) return [3 /*break*/, 21];
|
|
222
|
+
podId = decodeURIComponent(queueMatch[1]);
|
|
223
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
224
|
+
case 18:
|
|
225
|
+
port_2 = _j.sent();
|
|
226
|
+
return [4 /*yield*/, this._readBody(req)];
|
|
227
|
+
case 19:
|
|
228
|
+
body_1 = _j.sent();
|
|
229
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
230
|
+
var upstreamReq = http.request({
|
|
231
|
+
hostname: 'localhost',
|
|
232
|
+
port: port_2,
|
|
233
|
+
path: '/prompt', method: 'POST',
|
|
234
|
+
headers: { 'content-type': 'application/json', 'content-length': body_1.length },
|
|
235
|
+
}, function (upstreamRes) {
|
|
236
|
+
var chunks = [];
|
|
237
|
+
upstreamRes.on('data', function (c) { return chunks.push(c); });
|
|
238
|
+
upstreamRes.on('end', function () {
|
|
239
|
+
var _a;
|
|
240
|
+
try {
|
|
241
|
+
var data = JSON.parse(Buffer.concat(chunks).toString());
|
|
242
|
+
_this._json(res, (_a = upstreamRes.statusCode) !== null && _a !== void 0 ? _a : 500, data);
|
|
243
|
+
resolve();
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
reject(e);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
upstreamRes.on('error', reject);
|
|
250
|
+
});
|
|
251
|
+
upstreamReq.on('error', reject);
|
|
252
|
+
upstreamReq.write(body_1);
|
|
253
|
+
upstreamReq.end();
|
|
254
|
+
})];
|
|
255
|
+
case 20:
|
|
256
|
+
_j.sent();
|
|
257
|
+
return [2 /*return*/];
|
|
258
|
+
case 21:
|
|
259
|
+
historyMatch = url.match(/^\/api\/pods\/([^/]+)\/history\/([^/]+)$/);
|
|
260
|
+
if (!(req.method === 'GET' && historyMatch)) return [3 /*break*/, 24];
|
|
261
|
+
podId = decodeURIComponent(historyMatch[1]);
|
|
262
|
+
promptId = decodeURIComponent(historyMatch[2]);
|
|
263
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
264
|
+
case 22:
|
|
265
|
+
port = _j.sent();
|
|
266
|
+
return [4 /*yield*/, this._proxyGet(res, port, "/history/".concat(promptId))];
|
|
267
|
+
case 23:
|
|
268
|
+
_j.sent();
|
|
269
|
+
return [2 /*return*/];
|
|
270
|
+
case 24:
|
|
271
|
+
objectInfoMatch = url.match(/^\/api\/pods\/([^/]+)\/object_info$/);
|
|
272
|
+
if (!(req.method === 'GET' && objectInfoMatch)) return [3 /*break*/, 27];
|
|
273
|
+
podId = decodeURIComponent(objectInfoMatch[1]);
|
|
274
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
275
|
+
case 25:
|
|
276
|
+
port = _j.sent();
|
|
277
|
+
return [4 /*yield*/, this._proxyGet(res, port, '/object_info')];
|
|
278
|
+
case 26:
|
|
279
|
+
_j.sent();
|
|
280
|
+
return [2 /*return*/];
|
|
281
|
+
case 27:
|
|
282
|
+
interruptMatch = url.match(/^\/api\/pods\/([^/]+)\/interrupt$/);
|
|
283
|
+
if (!(req.method === 'POST' && interruptMatch)) return [3 /*break*/, 30];
|
|
284
|
+
podId = decodeURIComponent(interruptMatch[1]);
|
|
285
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
286
|
+
case 28:
|
|
287
|
+
port_3 = _j.sent();
|
|
288
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
289
|
+
var upstreamReq = http.request({ hostname: 'localhost', port: port_3, path: '/interrupt', method: 'POST' }, function (upstreamRes) { upstreamRes.resume(); upstreamRes.on('end', resolve); });
|
|
290
|
+
upstreamReq.on('error', reject);
|
|
291
|
+
upstreamReq.end();
|
|
292
|
+
})];
|
|
293
|
+
case 29:
|
|
294
|
+
_j.sent();
|
|
295
|
+
return [2 /*return*/, this._json(res, 200, { ok: true })];
|
|
296
|
+
case 30:
|
|
195
297
|
res.writeHead(404);
|
|
196
298
|
res.end('Not found');
|
|
197
|
-
return [3 /*break*/,
|
|
198
|
-
case
|
|
199
|
-
err_1 =
|
|
299
|
+
return [3 /*break*/, 32];
|
|
300
|
+
case 31:
|
|
301
|
+
err_1 = _j.sent();
|
|
200
302
|
console.error('[PodsOperatorServer] Error handling request:', err_1);
|
|
201
|
-
this._json(res, 500, { error: (
|
|
202
|
-
return [3 /*break*/,
|
|
203
|
-
case
|
|
303
|
+
this._json(res, 500, { error: (_h = err_1 === null || err_1 === void 0 ? void 0 : err_1.message) !== null && _h !== void 0 ? _h : 'Internal server error' });
|
|
304
|
+
return [3 /*break*/, 32];
|
|
305
|
+
case 32: return [2 /*return*/];
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}); });
|
|
309
|
+
// ── WebSocket tunnel: WS /api/pods/:id/ws?clientId= ─────────────────────
|
|
310
|
+
this.server.on('upgrade', function (req, socket, head) { return __awaiter(_this, void 0, void 0, function () {
|
|
311
|
+
var rawUrl, urlPath, qs, wsMatch, podId, port_4, clientId_1, upstream_1, err_2;
|
|
312
|
+
var _a, _b, _c;
|
|
313
|
+
return __generator(this, function (_d) {
|
|
314
|
+
switch (_d.label) {
|
|
315
|
+
case 0:
|
|
316
|
+
rawUrl = (_a = req.url) !== null && _a !== void 0 ? _a : '/';
|
|
317
|
+
urlPath = rawUrl.split('?')[0];
|
|
318
|
+
qs = new URLSearchParams((_b = rawUrl.split('?')[1]) !== null && _b !== void 0 ? _b : '');
|
|
319
|
+
wsMatch = urlPath.match(/^\/api\/pods\/([^/]+)\/ws$/);
|
|
320
|
+
if (!wsMatch) {
|
|
321
|
+
socket.destroy();
|
|
322
|
+
return [2 /*return*/];
|
|
323
|
+
}
|
|
324
|
+
podId = decodeURIComponent(wsMatch[1]);
|
|
325
|
+
_d.label = 1;
|
|
326
|
+
case 1:
|
|
327
|
+
_d.trys.push([1, 3, , 4]);
|
|
328
|
+
return [4 /*yield*/, this._runningPort(podId)];
|
|
329
|
+
case 2:
|
|
330
|
+
port_4 = _d.sent();
|
|
331
|
+
clientId_1 = (_c = qs.get('clientId')) !== null && _c !== void 0 ? _c : '';
|
|
332
|
+
upstream_1 = net.createConnection({ host: 'localhost', port: port_4 });
|
|
333
|
+
upstream_1.on('connect', function () {
|
|
334
|
+
var _a, _b;
|
|
335
|
+
var upgradeReq = [
|
|
336
|
+
"GET /ws?clientId=".concat(encodeURIComponent(clientId_1), " HTTP/1.1"),
|
|
337
|
+
"Host: localhost:".concat(port_4),
|
|
338
|
+
'Upgrade: websocket',
|
|
339
|
+
'Connection: Upgrade',
|
|
340
|
+
"Sec-WebSocket-Key: ".concat((_a = req.headers['sec-websocket-key']) !== null && _a !== void 0 ? _a : ''),
|
|
341
|
+
"Sec-WebSocket-Version: ".concat((_b = req.headers['sec-websocket-version']) !== null && _b !== void 0 ? _b : '13'),
|
|
342
|
+
'',
|
|
343
|
+
'',
|
|
344
|
+
].join('\r\n');
|
|
345
|
+
upstream_1.write(upgradeReq);
|
|
346
|
+
if (head && head.length > 0)
|
|
347
|
+
upstream_1.write(head);
|
|
348
|
+
// Bidirectional pipe — all WS frames pass through transparently
|
|
349
|
+
socket.pipe(upstream_1);
|
|
350
|
+
upstream_1.pipe(socket);
|
|
351
|
+
});
|
|
352
|
+
upstream_1.on('error', function () { return socket.destroy(); });
|
|
353
|
+
socket.on('error', function () { return upstream_1.destroy(); });
|
|
354
|
+
socket.on('close', function () { return upstream_1.destroy(); });
|
|
355
|
+
upstream_1.on('close', function () { return socket.destroy(); });
|
|
356
|
+
return [3 /*break*/, 4];
|
|
357
|
+
case 3:
|
|
358
|
+
err_2 = _d.sent();
|
|
359
|
+
console.error('[PodsOperatorServer] WS tunnel error:', err_2);
|
|
360
|
+
socket.write('HTTP/1.1 503 Service Unavailable\r\n\r\n');
|
|
361
|
+
socket.destroy();
|
|
362
|
+
return [3 /*break*/, 4];
|
|
363
|
+
case 4: return [2 /*return*/];
|
|
204
364
|
}
|
|
205
365
|
});
|
|
206
366
|
}); });
|
|
@@ -219,9 +379,26 @@ var PodsOperatorServer = /** @class */ (function () {
|
|
|
219
379
|
};
|
|
220
380
|
// ── Private helpers ──────────────────────────────────────────────────────
|
|
221
381
|
PodsOperatorServer.prototype._json = function (res, status, data) {
|
|
222
|
-
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
382
|
+
res.writeHead(status, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
223
383
|
res.end(JSON.stringify(data));
|
|
224
384
|
};
|
|
385
|
+
/** Pipe a GET response from ComfyUI directly to the client (handles large payloads). */
|
|
386
|
+
PodsOperatorServer.prototype._proxyGet = function (res, port, path) {
|
|
387
|
+
return new Promise(function (resolve, reject) {
|
|
388
|
+
var upstreamReq = http.request({ hostname: 'localhost', port: port, path: path, method: 'GET' }, function (upstreamRes) {
|
|
389
|
+
var _a, _b;
|
|
390
|
+
res.writeHead((_a = upstreamRes.statusCode) !== null && _a !== void 0 ? _a : 200, {
|
|
391
|
+
'Content-Type': (_b = upstreamRes.headers['content-type']) !== null && _b !== void 0 ? _b : 'application/json',
|
|
392
|
+
'Access-Control-Allow-Origin': '*',
|
|
393
|
+
});
|
|
394
|
+
upstreamRes.pipe(res);
|
|
395
|
+
upstreamRes.on('end', resolve);
|
|
396
|
+
upstreamRes.on('error', reject);
|
|
397
|
+
});
|
|
398
|
+
upstreamReq.on('error', reject);
|
|
399
|
+
upstreamReq.end();
|
|
400
|
+
});
|
|
401
|
+
};
|
|
225
402
|
PodsOperatorServer.prototype._readBody = function (req) {
|
|
226
403
|
return new Promise(function (resolve, reject) {
|
|
227
404
|
var chunks = [];
|