comfyui-mcp 0.6.1 → 0.7.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +45 -1
  3. package/ROADMAP.md +120 -0
  4. package/blog/comfyui-mcp-tdqs-case-study.md +102 -0
  5. package/design/embedded-agent-panel.md +118 -0
  6. package/dist/experimental/agent-poc.d.ts +30 -0
  7. package/dist/experimental/agent-poc.d.ts.map +1 -0
  8. package/dist/experimental/agent-poc.js +210 -0
  9. package/dist/experimental/agent-poc.js.map +1 -0
  10. package/dist/experimental/chat-handler.d.ts +34 -0
  11. package/dist/experimental/chat-handler.d.ts.map +1 -0
  12. package/dist/experimental/chat-handler.js +60 -0
  13. package/dist/experimental/chat-handler.js.map +1 -0
  14. package/dist/experimental/provider-registry.d.ts +8 -0
  15. package/dist/experimental/provider-registry.d.ts.map +1 -0
  16. package/dist/experimental/provider-registry.js +50 -0
  17. package/dist/experimental/provider-registry.js.map +1 -0
  18. package/dist/experimental/run.d.ts +3 -0
  19. package/dist/experimental/run.d.ts.map +1 -0
  20. package/dist/experimental/run.js +34 -0
  21. package/dist/experimental/run.js.map +1 -0
  22. package/dist/services/download-auth.d.ts +23 -0
  23. package/dist/services/download-auth.d.ts.map +1 -0
  24. package/dist/services/download-auth.js +78 -0
  25. package/dist/services/download-auth.js.map +1 -0
  26. package/dist/services/download-cache.d.ts +26 -0
  27. package/dist/services/download-cache.d.ts.map +1 -0
  28. package/dist/services/download-cache.js +167 -0
  29. package/dist/services/download-cache.js.map +1 -0
  30. package/dist/services/job-history.d.ts +28 -0
  31. package/dist/services/job-history.d.ts.map +1 -0
  32. package/dist/services/job-history.js +130 -0
  33. package/dist/services/job-history.js.map +1 -0
  34. package/dist/services/job-watcher.d.ts +8 -0
  35. package/dist/services/job-watcher.d.ts.map +1 -1
  36. package/dist/services/job-watcher.js +16 -20
  37. package/dist/services/job-watcher.js.map +1 -1
  38. package/dist/services/model-resolver.d.ts +2 -1
  39. package/dist/services/model-resolver.d.ts.map +1 -1
  40. package/dist/services/model-resolver.js +11 -18
  41. package/dist/services/model-resolver.js.map +1 -1
  42. package/dist/services/node-authoring.d.ts +112 -0
  43. package/dist/services/node-authoring.d.ts.map +1 -0
  44. package/dist/services/node-authoring.js +418 -0
  45. package/dist/services/node-authoring.js.map +1 -0
  46. package/dist/services/node-management.d.ts +7 -0
  47. package/dist/services/node-management.d.ts.map +1 -1
  48. package/dist/services/node-management.js +123 -8
  49. package/dist/services/node-management.js.map +1 -1
  50. package/dist/services/process-control.d.ts +45 -0
  51. package/dist/services/process-control.d.ts.map +1 -1
  52. package/dist/services/process-control.js +262 -54
  53. package/dist/services/process-control.js.map +1 -1
  54. package/dist/services/queue-manager.d.ts +8 -3
  55. package/dist/services/queue-manager.d.ts.map +1 -1
  56. package/dist/services/queue-manager.js +25 -2
  57. package/dist/services/queue-manager.js.map +1 -1
  58. package/dist/services/tunnel.d.ts +23 -0
  59. package/dist/services/tunnel.d.ts.map +1 -0
  60. package/dist/services/tunnel.js +151 -0
  61. package/dist/services/tunnel.js.map +1 -0
  62. package/dist/tools/index.d.ts.map +1 -1
  63. package/dist/tools/index.js +2 -0
  64. package/dist/tools/index.js.map +1 -1
  65. package/dist/tools/model-management.d.ts.map +1 -1
  66. package/dist/tools/model-management.js +26 -1
  67. package/dist/tools/model-management.js.map +1 -1
  68. package/dist/tools/node-authoring.d.ts +3 -0
  69. package/dist/tools/node-authoring.d.ts.map +1 -0
  70. package/dist/tools/node-authoring.js +96 -0
  71. package/dist/tools/node-authoring.js.map +1 -0
  72. package/dist/tools/node-management.d.ts.map +1 -1
  73. package/dist/tools/node-management.js +5 -1
  74. package/dist/tools/node-management.js.map +1 -1
  75. package/dist/tools/process-control.js +2 -2
  76. package/dist/tools/process-control.js.map +1 -1
  77. package/dist/tools/queue-management.js +1 -1
  78. package/dist/tools/queue-management.js.map +1 -1
  79. package/infra/cloudflare/docs-proxy.js +45 -0
  80. package/infra/cloudflare/wrangler.jsonc +19 -0
  81. package/package.json +11 -2
  82. package/scripts/gen-tool-docs.ts +357 -0
@@ -0,0 +1,210 @@
1
+ import { randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { createServer } from "node:http";
3
+ import { Readable } from "node:stream";
4
+ import { pipeline } from "node:stream/promises";
5
+ import { logger } from "../utils/logger.js";
6
+ import { handleChatRequest } from "./chat-handler.js";
7
+ import { startQuickTunnel } from "../services/tunnel.js";
8
+ const DEFAULT_PORT = 8765;
9
+ const DEFAULT_HOST = "127.0.0.1";
10
+ const DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MiB
11
+ class PayloadTooLargeError extends Error {
12
+ limit;
13
+ constructor(limit) {
14
+ super(`Request body exceeds the ${limit}-byte limit`);
15
+ this.limit = limit;
16
+ this.name = "PayloadTooLargeError";
17
+ }
18
+ }
19
+ /** Convert a Node IncomingMessage into a Fetch-style Request (body size-capped). */
20
+ async function toFetchRequest(req, ctx) {
21
+ const url = `http://${ctx.host}:${ctx.port}${req.url ?? "/"}`;
22
+ const method = req.method ?? "GET";
23
+ const headers = new Headers();
24
+ for (const [key, value] of Object.entries(req.headers)) {
25
+ if (value === undefined)
26
+ continue;
27
+ if (Array.isArray(value)) {
28
+ for (const v of value)
29
+ headers.append(key, v);
30
+ }
31
+ else {
32
+ headers.set(key, value);
33
+ }
34
+ }
35
+ const hasBody = method !== "GET" && method !== "HEAD";
36
+ let body;
37
+ if (hasBody) {
38
+ const chunks = [];
39
+ let total = 0;
40
+ for await (const chunk of req) {
41
+ const buf = chunk;
42
+ total += buf.length;
43
+ // Cap the body while reading so a huge/chunked POST can't exhaust memory
44
+ // before JSON parsing (the server may be exposed via the tunnel).
45
+ if (total > ctx.maxBodyBytes) {
46
+ throw new PayloadTooLargeError(ctx.maxBodyBytes);
47
+ }
48
+ chunks.push(buf);
49
+ }
50
+ body = Buffer.concat(chunks).toString("utf-8");
51
+ }
52
+ return new Request(url, { method, headers, body });
53
+ }
54
+ /** Pipe a Fetch-style Response into a Node ServerResponse. */
55
+ async function sendFetchResponse(res, response) {
56
+ const headers = {};
57
+ response.headers.forEach((value, key) => {
58
+ headers[key] = value;
59
+ });
60
+ res.writeHead(response.status, headers);
61
+ if (response.body) {
62
+ // response.body is a web ReadableStream; bridge it to the Node response.
63
+ // Await pipeline so downstream/stream errors (e.g. client disconnect mid-
64
+ // stream) reject here instead of surfacing as an unhandled 'error' event.
65
+ const source = Readable.fromWeb(response.body);
66
+ await pipeline(source, res);
67
+ }
68
+ else {
69
+ res.end(await response.text());
70
+ }
71
+ }
72
+ /** Constant-time `Authorization: Bearer <token>` check. */
73
+ function isAuthorized(req, token) {
74
+ const header = req.headers["authorization"];
75
+ const value = Array.isArray(header) ? header[0] : header;
76
+ if (!value)
77
+ return false;
78
+ const m = /^Bearer\s+(.+)$/i.exec(value.trim());
79
+ if (!m)
80
+ return false;
81
+ const provided = Buffer.from(m[1].trim());
82
+ const expected = Buffer.from(token);
83
+ return (provided.length === expected.length && timingSafeEqual(provided, expected));
84
+ }
85
+ /**
86
+ * Start the experimental agent POC HTTP server (and optional tunnel).
87
+ * Returns a handle (incl. the bearer token). Callers gate this behind the env flag.
88
+ */
89
+ export async function startAgentPoc(options = {}) {
90
+ const envPort = process.env.COMFYUI_MCP_AGENT_PORT
91
+ ? Number(process.env.COMFYUI_MCP_AGENT_PORT)
92
+ : undefined;
93
+ const requestedPort = options.port ?? (envPort && !Number.isNaN(envPort) ? envPort : DEFAULT_PORT);
94
+ const host = options.host ?? DEFAULT_HOST;
95
+ const token = options.token ??
96
+ process.env.COMFYUI_MCP_AGENT_TOKEN ??
97
+ randomBytes(24).toString("hex");
98
+ const ctx = {
99
+ host,
100
+ port: requestedPort,
101
+ token,
102
+ maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,
103
+ };
104
+ const server = createServer((req, res) => {
105
+ void handleRequest(req, res, ctx);
106
+ });
107
+ await new Promise((resolve, reject) => {
108
+ server.once("error", reject);
109
+ server.listen(requestedPort, host, () => {
110
+ server.off("error", reject);
111
+ resolve();
112
+ });
113
+ });
114
+ const address = server.address();
115
+ const port = typeof address === "object" && address ? address.port : requestedPort;
116
+ ctx.port = port;
117
+ const localUrl = `http://${host}:${port}`;
118
+ logger.info(`[agent-poc] chat server listening on ${localUrl}/api/chat`);
119
+ logger.info(`[agent-poc] session token (send as 'Authorization: Bearer <token>'): ${token}`);
120
+ let tunnel = null;
121
+ if (options.tunnel) {
122
+ try {
123
+ tunnel = await startQuickTunnel(port);
124
+ logger.info(`[agent-poc] public URL: ${tunnel.url}`);
125
+ }
126
+ catch (err) {
127
+ const message = err instanceof Error ? err.message : String(err);
128
+ logger.error(`[agent-poc] failed to open tunnel: ${message}`);
129
+ }
130
+ }
131
+ return {
132
+ localUrl,
133
+ publicUrl: tunnel?.url ?? null,
134
+ token,
135
+ stop: async () => {
136
+ tunnel?.stop();
137
+ await new Promise((resolve) => server.close(() => resolve()));
138
+ },
139
+ };
140
+ }
141
+ async function handleRequest(req, res, ctx) {
142
+ // CORS: the tunnel + bearer token are the perimeter. `*` is safe here because
143
+ // requests are non-credentialed and must carry the Authorization header below.
144
+ res.setHeader("Access-Control-Allow-Origin", "*");
145
+ res.setHeader("Access-Control-Allow-Headers", "authorization, content-type");
146
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
147
+ if (req.method === "OPTIONS") {
148
+ res.writeHead(204);
149
+ res.end();
150
+ return;
151
+ }
152
+ // Health probe stays open (no secret, no provider spend).
153
+ if (req.method === "GET" && req.url === "/health") {
154
+ res.writeHead(200, { "content-type": "application/json" });
155
+ res.end(JSON.stringify({ status: "ok" }));
156
+ return;
157
+ }
158
+ // Everything past here requires the bearer token.
159
+ if (!isAuthorized(req, ctx.token)) {
160
+ res.writeHead(401, { "content-type": "application/json" });
161
+ res.end(JSON.stringify({ error: "Unauthorized" }));
162
+ return;
163
+ }
164
+ if (req.method === "POST" && req.url === "/api/chat") {
165
+ const contentType = req.headers["content-type"] ?? "";
166
+ if (!contentType.includes("application/json")) {
167
+ res.writeHead(415, { "content-type": "application/json" });
168
+ res.end(JSON.stringify({ error: "Expected content-type: application/json" }));
169
+ return;
170
+ }
171
+ try {
172
+ const request = await toFetchRequest(req, ctx);
173
+ const response = await handleChatRequest(request);
174
+ await sendFetchResponse(res, response);
175
+ }
176
+ catch (err) {
177
+ const message = err instanceof Error ? err.message : String(err);
178
+ logger.error(`[agent-poc] /api/chat error: ${message}`);
179
+ if (res.headersSent) {
180
+ // Streaming already started — destroy the socket rather than corrupt
181
+ // the stream protocol by appending a JSON error body to it.
182
+ res.destroy(err instanceof Error ? err : new Error(message));
183
+ }
184
+ else if (err instanceof PayloadTooLargeError) {
185
+ res.writeHead(413, { "content-type": "application/json" });
186
+ res.end(JSON.stringify({ error: message }));
187
+ }
188
+ else {
189
+ res.writeHead(500, { "content-type": "application/json" });
190
+ res.end(JSON.stringify({ error: message }));
191
+ }
192
+ }
193
+ return;
194
+ }
195
+ res.writeHead(404, { "content-type": "application/json" });
196
+ res.end(JSON.stringify({ error: "Not found" }));
197
+ }
198
+ /**
199
+ * Gated bootstrap. Only starts the POC when COMFYUI_MCP_AGENT_POC is truthy.
200
+ * Safe to call unconditionally from a side entry; a no-op otherwise.
201
+ */
202
+ export async function maybeStartAgentPoc() {
203
+ if (!process.env.COMFYUI_MCP_AGENT_POC) {
204
+ return null;
205
+ }
206
+ return startAgentPoc({
207
+ tunnel: process.env.COMFYUI_MCP_AGENT_TUNNEL === "1",
208
+ });
209
+ }
210
+ //# sourceMappingURL=agent-poc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-poc.js","sourceRoot":"","sources":["../../src/experimental/agent-poc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type LanguageModel } from "ai";
2
+ /**
3
+ * The single server-side tool for the POC. Has an `execute` so the AI SDK runs
4
+ * it on the server and feeds the result back into the model automatically.
5
+ */
6
+ export declare const tools: {
7
+ generate_image: import("ai").Tool<{
8
+ prompt: string;
9
+ width?: number | undefined;
10
+ height?: number | undefined;
11
+ }, {
12
+ status: string;
13
+ prompt: string;
14
+ width: number;
15
+ height: number;
16
+ imageUrl: string;
17
+ note: string;
18
+ }>;
19
+ };
20
+ export interface ChatHandlerOptions {
21
+ /** Override the model (mainly for tests). Defaults to the provider registry. */
22
+ model?: LanguageModel;
23
+ /** Optional `provider:model` id forwarded to the registry. */
24
+ modelId?: string;
25
+ /** System prompt for the agent. */
26
+ system?: string;
27
+ }
28
+ /**
29
+ * Handle a chat request. Accepts a Fetch-style `Request` whose JSON body is
30
+ * `{ messages: UIMessage[], model?: string }` and returns a streaming
31
+ * `Response` (UI message stream protocol).
32
+ */
33
+ export declare function handleChatRequest(req: Request, options?: ChatHandlerOptions): Promise<Response>;
34
+ //# sourceMappingURL=chat-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../src/experimental/chat-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAGnB,MAAM,IAAI,CAAC;AAiBZ;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;CAsBC,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,QAAQ,CAAC,CAkBnB"}
@@ -0,0 +1,60 @@
1
+ import { convertToModelMessages, stepCountIs, streamText, tool, } from "ai";
2
+ import { z } from "zod";
3
+ import { resolveModel } from "./provider-registry.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Experimental AI SDK chat handler for the embedded-agent-panel POC.
6
+ //
7
+ // `POST /api/chat` -> streamText({ model, messages, tools }).toUIMessageStreamResponse()
8
+ //
9
+ // Includes ONE server-side tool end-to-end (`generate_image`) which, for the
10
+ // POC, returns a stub result. Wiring the real comfyui-mcp generate tools is a
11
+ // later phase (see design/embedded-agent-panel.md, build order step 5).
12
+ //
13
+ // This module is only reached behind COMFYUI_MCP_AGENT_POC.
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * The single server-side tool for the POC. Has an `execute` so the AI SDK runs
17
+ * it on the server and feeds the result back into the model automatically.
18
+ */
19
+ export const tools = {
20
+ generate_image: tool({
21
+ description: "Generate an image from a text prompt using ComfyUI. (POC stub — returns a placeholder result instead of running a real workflow.)",
22
+ inputSchema: z.object({
23
+ prompt: z.string().describe("The text prompt to generate an image from."),
24
+ width: z.number().int().positive().optional().describe("Image width in pixels."),
25
+ height: z.number().int().positive().optional().describe("Image height in pixels."),
26
+ }),
27
+ execute: async ({ prompt, width, height }) => {
28
+ // POC stub: real implementation will enqueue a ComfyUI workflow and
29
+ // return the resulting asset id / image URL.
30
+ return {
31
+ status: "stubbed",
32
+ prompt,
33
+ width: width ?? 1024,
34
+ height: height ?? 1024,
35
+ imageUrl: "https://example.invalid/poc-placeholder.png",
36
+ note: "Placeholder result from the POC. No real workflow was executed.",
37
+ };
38
+ },
39
+ }),
40
+ };
41
+ const DEFAULT_SYSTEM = "You are an assistant embedded in ComfyUI. You can generate images with the generate_image tool.";
42
+ /**
43
+ * Handle a chat request. Accepts a Fetch-style `Request` whose JSON body is
44
+ * `{ messages: UIMessage[], model?: string }` and returns a streaming
45
+ * `Response` (UI message stream protocol).
46
+ */
47
+ export async function handleChatRequest(req, options = {}) {
48
+ const body = (await req.json());
49
+ const messages = body.messages ?? [];
50
+ const model = options.model ?? resolveModel(options.modelId ?? body.model);
51
+ const result = streamText({
52
+ model,
53
+ system: options.system ?? DEFAULT_SYSTEM,
54
+ messages: await convertToModelMessages(messages),
55
+ tools,
56
+ stopWhen: stepCountIs(5),
57
+ });
58
+ return result.toUIMessageStreamResponse();
59
+ }
60
+ //# sourceMappingURL=chat-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-handler.js","sourceRoot":"","sources":["../../src/experimental/chat-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,WAAW,EACX,UAAU,EACV,IAAI,GAIL,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,8EAA8E;AAC9E,qEAAqE;AACrE,EAAE;AACF,yFAAyF;AACzF,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,wEAAwE;AACxE,EAAE;AACF,4DAA4D;AAC5D,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,cAAc,EAAE,IAAI,CAAC;QACnB,WAAW,EACT,mIAAmI;QACrI,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;YACzE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAChF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;SACnF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;YAC3C,oEAAoE;YACpE,6CAA6C;YAC7C,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,MAAM;gBACN,KAAK,EAAE,KAAK,IAAI,IAAI;gBACpB,MAAM,EAAE,MAAM,IAAI,IAAI;gBACtB,QAAQ,EAAE,6CAA6C;gBACvD,IAAI,EAAE,iEAAiE;aACxE,CAAC;QACJ,CAAC;KACF,CAAC;CACe,CAAC;AAWpB,MAAM,cAAc,GAClB,iGAAiG,CAAC;AAEpG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAY,EACZ,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IAErC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAE3E,MAAM,MAAM,GAAG,UAAU,CAAC;QACxB,KAAK;QACL,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,cAAc;QACxC,QAAQ,EAAE,MAAM,sBAAsB,CAAC,QAAQ,CAAC;QAChD,KAAK;QACL,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;KACzB,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,yBAAyB,EAAE,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { LanguageModel } from "ai";
2
+ export declare const registry: import("ai").ProviderRegistryProvider<{
3
+ anthropic: import("@ai-sdk/anthropic").AnthropicProvider;
4
+ openai: import("@ai-sdk/openai").OpenAIProvider;
5
+ google: import("@ai-sdk/google").GoogleGenerativeAIProvider;
6
+ }, ":">;
7
+ export declare function resolveModel(id?: string): LanguageModel;
8
+ //# sourceMappingURL=provider-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-registry.d.ts","sourceRoot":"","sources":["../../src/experimental/provider-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAaxC,eAAO,MAAM,QAAQ;;;;OAInB,CAAC;AAkCH,wBAAgB,YAAY,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,CASvD"}
@@ -0,0 +1,50 @@
1
+ import { anthropic } from "@ai-sdk/anthropic";
2
+ import { google } from "@ai-sdk/google";
3
+ import { openai } from "@ai-sdk/openai";
4
+ import { createProviderRegistry } from "ai";
5
+ // ---------------------------------------------------------------------------
6
+ // Provider registry for the experimental embedded-agent-panel POC.
7
+ //
8
+ // Picks the language model per request from a single registry, keyed by a
9
+ // `provider:model` string (the AI SDK default separator). Default is Anthropic
10
+ // with the model from COMFYUI_MCP_AGENT_MODEL. Provider API keys are read from
11
+ // the usual env vars by each provider package (ANTHROPIC_API_KEY, etc.).
12
+ //
13
+ // Not part of the default MCP server — only used behind COMFYUI_MCP_AGENT_POC.
14
+ // ---------------------------------------------------------------------------
15
+ export const registry = createProviderRegistry({
16
+ anthropic,
17
+ openai,
18
+ google,
19
+ });
20
+ const DEFAULT_MODEL = "anthropic:claude-sonnet-4-5";
21
+ /**
22
+ * The set of model ids a request is allowed to select: the server default
23
+ * (COMFYUI_MCP_AGENT_MODEL or DEFAULT_MODEL) plus any explicitly allow-listed
24
+ * via COMFYUI_MCP_AGENT_ALLOWED_MODELS (comma-separated `provider:model`).
25
+ */
26
+ function allowedModels() {
27
+ const set = new Set([DEFAULT_MODEL]);
28
+ const envDefault = process.env.COMFYUI_MCP_AGENT_MODEL?.trim();
29
+ if (envDefault)
30
+ set.add(envDefault);
31
+ const extra = process.env.COMFYUI_MCP_AGENT_ALLOWED_MODELS;
32
+ if (extra) {
33
+ for (const m of extra.split(",")) {
34
+ const t = m.trim();
35
+ if (t)
36
+ set.add(t);
37
+ }
38
+ }
39
+ return set;
40
+ }
41
+ export function resolveModel(id) {
42
+ const fallback = process.env.COMFYUI_MCP_AGENT_MODEL?.trim() || DEFAULT_MODEL;
43
+ // Only honour a client-supplied model id if it is explicitly allow-listed.
44
+ // Otherwise ignore it and use the server default — this stops a leaked tunnel
45
+ // URL from letting a caller select arbitrary (expensive) models on your keys.
46
+ const requested = id?.trim();
47
+ const modelId = requested && allowedModels().has(requested) ? requested : fallback;
48
+ return registry.languageModel(modelId);
49
+ }
50
+ //# sourceMappingURL=provider-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-registry.js","sourceRoot":"","sources":["../../src/experimental/provider-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,IAAI,CAAC;AAG5C,8EAA8E;AAC9E,mEAAmE;AACnE,EAAE;AACF,0EAA0E;AAC1E,+EAA+E;AAC/E,+EAA+E;AAC/E,yEAAyE;AACzE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAE9E,MAAM,CAAC,MAAM,QAAQ,GAAG,sBAAsB,CAAC;IAC7C,SAAS;IACT,MAAM;IACN,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,6BAA6B,CAAC;AAapD;;;;GAIG;AACH,SAAS,aAAa;IACpB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAS,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE,CAAC;IAC/D,IAAI,UAAU;QAAE,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC;IAC3D,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAW;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC;IAC9E,2EAA2E;IAC3E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,SAAS,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;IAC7B,MAAM,OAAO,GACX,SAAS,IAAI,aAAa,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;IACrE,OAAO,QAAQ,CAAC,aAAa,CAAC,OAA0B,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/experimental/run.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { logger } from "../utils/logger.js";
3
+ import { maybeStartAgentPoc } from "./agent-poc.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Standalone runner for the experimental embedded-agent-panel POC.
6
+ //
7
+ // This is intentionally NOT imported by src/index.ts — the default MCP server
8
+ // (stdio / HTTP) never touches the POC. Run it explicitly, e.g.:
9
+ //
10
+ // COMFYUI_MCP_AGENT_POC=1 ANTHROPIC_API_KEY=... npx tsx src/experimental/run.ts
11
+ // COMFYUI_MCP_AGENT_POC=1 COMFYUI_MCP_AGENT_TUNNEL=1 ... node dist/experimental/run.js
12
+ // ---------------------------------------------------------------------------
13
+ async function main() {
14
+ const handle = await maybeStartAgentPoc();
15
+ if (!handle) {
16
+ logger.warn("[agent-poc] COMFYUI_MCP_AGENT_POC is not set — nothing to run. Set it to 1 to start the POC.");
17
+ return;
18
+ }
19
+ logger.info(`[agent-poc] ready at ${handle.localUrl}/api/chat`);
20
+ if (handle.publicUrl) {
21
+ logger.info(`[agent-poc] public URL: ${handle.publicUrl}`);
22
+ }
23
+ const shutdown = () => {
24
+ logger.info("[agent-poc] shutting down...");
25
+ void handle.stop().then(() => process.exit(0));
26
+ };
27
+ process.on("SIGINT", shutdown);
28
+ process.on("SIGTERM", shutdown);
29
+ }
30
+ main().catch((err) => {
31
+ logger.error("[agent-poc] fatal error", err);
32
+ process.exit(1);
33
+ });
34
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/experimental/run.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,8EAA8E;AAC9E,mEAAmE;AACnE,EAAE;AACF,8EAA8E;AAC9E,iEAAiE;AACjE,EAAE;AACF,kFAAkF;AAClF,yFAAyF;AACzF,8EAA8E;AAE9E,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CACT,8FAA8F,CAC/F,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,QAAQ,WAAW,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ export type DownloadAuth = {
2
+ type: "bearer";
3
+ token: string;
4
+ } | {
5
+ type: "basic";
6
+ username: string;
7
+ password: string;
8
+ } | {
9
+ type: "header";
10
+ header_name: string;
11
+ header_value: string;
12
+ } | {
13
+ type: "query";
14
+ query_param: string;
15
+ query_value: string;
16
+ };
17
+ export interface DownloadRequestAuth {
18
+ url: string;
19
+ headers: Record<string, string>;
20
+ }
21
+ export declare function redactUrlForLogs(url: string, extraSensitiveParams?: string[]): string;
22
+ export declare function applyDownloadAuth(url: string, auth?: DownloadAuth): DownloadRequestAuth;
23
+ //# sourceMappingURL=download-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-auth.d.ts","sourceRoot":"","sources":["../../src/services/download-auth.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAyCD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,oBAAoB,GAAE,MAAM,EAAO,GAClC,MAAM,CAaR;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,YAAY,GAClB,mBAAmB,CAgCrB"}
@@ -0,0 +1,78 @@
1
+ import { ValidationError } from "../utils/errors.js";
2
+ const TOKEN_QUERY_RE = /token|key|secret|signature|auth|password|credential/i;
3
+ const REDACTED = "[REDACTED]";
4
+ const HEADER_NAME_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
5
+ const ASCII_CONTROL_RE = /[\x00-\x1F\x7F]/;
6
+ function rejectControlChars(label, value) {
7
+ if (ASCII_CONTROL_RE.test(value)) {
8
+ throw new ValidationError(`${label} cannot contain ASCII control characters.`);
9
+ }
10
+ }
11
+ function validateDownloadAuth(auth) {
12
+ if (auth.type === "bearer") {
13
+ rejectControlChars("Bearer token", auth.token);
14
+ return;
15
+ }
16
+ if (auth.type === "basic") {
17
+ rejectControlChars("Basic auth username", auth.username);
18
+ rejectControlChars("Basic auth password", auth.password);
19
+ return;
20
+ }
21
+ if (auth.type === "header") {
22
+ if (!HEADER_NAME_RE.test(auth.header_name)) {
23
+ throw new ValidationError("Header auth header_name must be a valid HTTP header token.");
24
+ }
25
+ rejectControlChars("Header auth header_value", auth.header_value);
26
+ return;
27
+ }
28
+ if (auth.query_param.length === 0) {
29
+ throw new ValidationError("Query auth query_param must be a non-empty string.");
30
+ }
31
+ rejectControlChars("Query auth query_param", auth.query_param);
32
+ }
33
+ export function redactUrlForLogs(url, extraSensitiveParams = []) {
34
+ try {
35
+ const parsed = new URL(url);
36
+ const sensitive = new Set(extraSensitiveParams.map((p) => p.toLowerCase()));
37
+ for (const key of [...parsed.searchParams.keys()]) {
38
+ if (sensitive.has(key.toLowerCase()) || TOKEN_QUERY_RE.test(key)) {
39
+ parsed.searchParams.set(key, REDACTED);
40
+ }
41
+ }
42
+ return parsed.toString();
43
+ }
44
+ catch {
45
+ return url;
46
+ }
47
+ }
48
+ export function applyDownloadAuth(url, auth) {
49
+ if (!auth)
50
+ return { url, headers: {} };
51
+ validateDownloadAuth(auth);
52
+ if (auth.type === "bearer") {
53
+ return {
54
+ url,
55
+ headers: { Authorization: `Bearer ${auth.token}` },
56
+ };
57
+ }
58
+ if (auth.type === "basic") {
59
+ const encoded = Buffer.from(`${auth.username}:${auth.password}`).toString("base64");
60
+ return {
61
+ url,
62
+ headers: { Authorization: `Basic ${encoded}` },
63
+ };
64
+ }
65
+ if (auth.type === "header") {
66
+ return {
67
+ url,
68
+ headers: { [auth.header_name]: auth.header_value },
69
+ };
70
+ }
71
+ const parsed = new URL(url);
72
+ parsed.searchParams.set(auth.query_param, auth.query_value);
73
+ return {
74
+ url: parsed.toString(),
75
+ headers: {},
76
+ };
77
+ }
78
+ //# sourceMappingURL=download-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-auth.js","sourceRoot":"","sources":["../../src/services/download-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { copyFile, link, mkdir, readdir, rename, rm, stat, utimes } from "node:fs/promises";
2
+ export declare const downloadCacheFs: {
3
+ copyFile: typeof copyFile;
4
+ link: typeof link;
5
+ mkdir: typeof mkdir;
6
+ readdir: typeof readdir;
7
+ rename: typeof rename;
8
+ rm: typeof rm;
9
+ stat: typeof stat;
10
+ utimes: typeof utimes;
11
+ };
12
+ export interface DownloadCacheOptions {
13
+ url: string;
14
+ headers: Record<string, string>;
15
+ targetPath: string;
16
+ logUrl?: string;
17
+ }
18
+ export interface DownloadCacheResult {
19
+ targetPath: string;
20
+ usedCache: boolean;
21
+ cachePath?: string;
22
+ materializedBy?: "hardlink" | "copy";
23
+ }
24
+ export declare function downloadUrlToFile(url: string, targetPath: string, headers: Record<string, string>, logUrl?: string): Promise<void>;
25
+ export declare function downloadWithCache(options: DownloadCacheOptions): Promise<DownloadCacheResult>;
26
+ //# sourceMappingURL=download-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-cache.d.ts","sourceRoot":"","sources":["../../src/services/download-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,OAAO,EACP,MAAM,EACN,EAAE,EACF,IAAI,EACJ,MAAM,EACP,MAAM,kBAAkB,CAAC;AAa1B,eAAO,MAAM,eAAe;;;;;;;;;CAS3B,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CACtC;AA+ID,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAqB9B"}