comfyui-mcp 0.51.10 → 0.51.12
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/orchestrator/fence-refusal.js +39 -4
- package/dist/orchestrator/fence-refusal.js.map +1 -1
- package/dist/orchestrator/panel-tools.js +43 -8
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/orchestrator/switch-hold.js +124 -0
- package/dist/orchestrator/switch-hold.js.map +1 -0
- package/package.json +1 -1
|
@@ -42,6 +42,42 @@ export function noPanelTabReason(tabId) {
|
|
|
42
42
|
* BEFORE the structural no-Origin case, or it borrows a remedy that cannot help
|
|
43
43
|
* it and is told its problem is permanent.
|
|
44
44
|
*/
|
|
45
|
+
/**
|
|
46
|
+
* The remedy for "this connection has no server-observed Origin" (panel#1065).
|
|
47
|
+
*
|
|
48
|
+
* Shared, because it was NOT shared and the two copies drifted. #1077/#1240 gave
|
|
49
|
+
* the relay path the origin it already knows from COMFYUI_URL, and updated the
|
|
50
|
+
* refusal below to say so. The OTHER surface rendering this same refusal — the
|
|
51
|
+
* `rejected` branch of describeFenceRebind in panel-tools.ts — kept the pre-fix
|
|
52
|
+
* text, which told the user to abandon the relay backend and to "report it: the
|
|
53
|
+
* relay protocol has to forward the browser's handshake Origin for this path to
|
|
54
|
+
* work at all".
|
|
55
|
+
*
|
|
56
|
+
* A user on 0.50.114 — a version that HAS the fix — followed that instruction and
|
|
57
|
+
* filed panel#1065 quoting it back. The stale sentence manufactured its own bug
|
|
58
|
+
* report, and told someone to downgrade a working setup on the way.
|
|
59
|
+
*/
|
|
60
|
+
export const NO_ORIGIN_REMEDY =
|
|
61
|
+
// The gate's OWN semantics come first, because they dissolve the fear the old
|
|
62
|
+
// wording created and the reporter's stated obstacle at once. Measured, not
|
|
63
|
+
// assumed: workflowIdentityParts requires an origin to EXIST, both call sites
|
|
64
|
+
// take only `.uuid`, and `identity.origin` is never read again — so the value
|
|
65
|
+
// is a scope, not a claim to be checked against the browser.
|
|
66
|
+
`What this gate checks is PRESENCE, not agreement: the identity needs SOME origin to be ` +
|
|
67
|
+
`scoped to, and nothing ever compares it against the browser's — so the value does not have ` +
|
|
68
|
+
`to match where the panel is served from. ` +
|
|
69
|
+
// Two causes, named separately (codex review, P1). The predicate that selects this
|
|
70
|
+
// remedy is generic, and a blanket "set COMFYUI_URL" sends a direct/LAN/pairing or
|
|
71
|
+
// non-browser caller to a change that CANNOT add a handshake origin, leaving them
|
|
72
|
+
// fenced with nothing left to try.
|
|
73
|
+
`On a RELAY connection the origin is derived from COMFYUI_URL, so an unset or unparseable ` +
|
|
74
|
+
`COMFYUI_URL is the usual cause and setting it to any valid URL supplies one; you do not have ` +
|
|
75
|
+
`to leave the relay backend, which has supplied its own origin since 0.50.67. ` +
|
|
76
|
+
`On any other path the connection never presented an Origin header at all — a non-browser ` +
|
|
77
|
+
`client, or a proxy that strips it — and what fixes that is reaching the orchestrator from the ` +
|
|
78
|
+
`browser panel itself. ` +
|
|
79
|
+
`Refreshing the tab will not change either case. Reads and non-graph tools keep working ` +
|
|
80
|
+
`meanwhile — they do not need the fence.`;
|
|
45
81
|
export function identityReason(tabId, origin, workflowUuid, failure) {
|
|
46
82
|
if (!origin && failure === "ambiguous") {
|
|
47
83
|
return ambiguousTurn(tabId, "no single tab's identity could be read");
|
|
@@ -53,10 +89,9 @@ export function identityReason(tabId, origin, workflowUuid, failure) {
|
|
|
53
89
|
// like any other. Naming a fixed cause as the "known case" would send a
|
|
54
90
|
// reader to change a backend that is no longer the reason (#1077).
|
|
55
91
|
return (`this tab's connection carries no server-observed Origin, and the workflow identity is ` +
|
|
56
|
-
`bound to one — so the fence cannot be adopted for it.
|
|
57
|
-
`
|
|
58
|
-
|
|
59
|
-
`this session, setting it to the URL the panel is served from is what supplies it.`);
|
|
92
|
+
`bound to one — so the fence cannot be adopted for it. This means the connection reached ` +
|
|
93
|
+
`the orchestrator by a path that neither observed nor was told the panel's origin. ` +
|
|
94
|
+
NO_ORIGIN_REMEDY);
|
|
60
95
|
}
|
|
61
96
|
// #1255 — this used to end "a malformed uuid, or one bound to a different
|
|
62
97
|
// origin", which describes a comparison the code does not perform.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fence-refusal.js","sourceRoot":"","sources":["../../src/orchestrator/fence-refusal.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,yEAAyE;AACzE,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,0DAA0D;AAC1D,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,uEAAuE;AACvE,6EAA6E;AAC7E,+EAA+E;AAC/E,sEAAsE;AACtE,4EAA4E;AAM5E,4EAA4E;AAC5E,SAAS,aAAa,CAAC,KAAa,EAAE,IAAY;IAChD,OAAO,CACL,2DAA2D,IAAI,SAAS,KAAK,eAAe;QAC5F,2EAA2E;QAC3E,mFAAmF;QACnF,+DAA+D,CAChE,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,OAAuB;IACtE,IAAI,OAAO,KAAK,WAAW;QAAE,OAAO,aAAa,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC;IAC/F,OAAO,mBAAmB,KAAK,wDAAwD,CAAC;AAC1F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,GAAG,KAAK,iFAAiF,CAAC;AACnG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,MAA0B,EAC1B,YAAqB,EACrB,OAAuB;IAEvB,IAAI,CAAC,MAAM,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QACvC,OAAO,aAAa,CAAC,KAAK,EAAE,wCAAwC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6EAA6E;QAC7E,8EAA8E;QAC9E,6EAA6E;QAC7E,wEAAwE;QACxE,mEAAmE;QACnE,OAAO,CACL,wFAAwF;YACxF,oFAAoF;YACpF,
|
|
1
|
+
{"version":3,"file":"fence-refusal.js","sourceRoot":"","sources":["../../src/orchestrator/fence-refusal.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,yEAAyE;AACzE,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,0DAA0D;AAC1D,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,uEAAuE;AACvE,6EAA6E;AAC7E,+EAA+E;AAC/E,sEAAsE;AACtE,4EAA4E;AAM5E,4EAA4E;AAC5E,SAAS,aAAa,CAAC,KAAa,EAAE,IAAY;IAChD,OAAO,CACL,2DAA2D,IAAI,SAAS,KAAK,eAAe;QAC5F,2EAA2E;QAC3E,mFAAmF;QACnF,+DAA+D,CAChE,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,OAAuB;IACtE,IAAI,OAAO,KAAK,WAAW;QAAE,OAAO,aAAa,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC;IAC/F,OAAO,mBAAmB,KAAK,wDAAwD,CAAC;AAC1F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,GAAG,KAAK,iFAAiF,CAAC;AACnG,CAAC;AAED;;;;;;GAMG;AACH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,gBAAgB;AAC3B,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,6DAA6D;AAC7D,yFAAyF;IACzF,6FAA6F;IAC7F,2CAA2C;IAC3C,mFAAmF;IACnF,mFAAmF;IACnF,kFAAkF;IAClF,mCAAmC;IACnC,2FAA2F;IAC3F,+FAA+F;IAC/F,+EAA+E;IAC/E,2FAA2F;IAC3F,gGAAgG;IAChG,wBAAwB;IACxB,yFAAyF;IACzF,yCAAyC,CAAC;AAE5C,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,MAA0B,EAC1B,YAAqB,EACrB,OAAuB;IAEvB,IAAI,CAAC,MAAM,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QACvC,OAAO,aAAa,CAAC,KAAK,EAAE,wCAAwC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6EAA6E;QAC7E,8EAA8E;QAC9E,6EAA6E;QAC7E,wEAAwE;QACxE,mEAAmE;QACnE,OAAO,CACL,wFAAwF;YACxF,0FAA0F;YAC1F,oFAAoF;YACpF,gBAAgB,CACjB,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,mEAAmE;IACnE,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,2EAA2E;IAC3E,0BAA0B;IAC1B,EAAE;IACF,2EAA2E;IAC3E,8EAA8E;IAC9E,0EAA0E;IAC1E,yBAAyB;IACzB,OAAO,CACL,oDAAoD,MAAM,CAAC,YAAY,CAAC,YAAY;QACpF,0CAA0C,MAAM,yCAAyC;QACzF,sFAAsF,CACvF,CAAC;AACJ,CAAC"}
|
|
@@ -38,6 +38,8 @@ import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
|
38
38
|
import { parse as parseYaml } from "yaml";
|
|
39
39
|
import { conversationOfScopeAddress, isScopeAddress, shortTabId } from "../services/session-scope.js";
|
|
40
40
|
import { NODE_ID_MESSAGE, NODE_ID_PATTERN, normalizeNodeId } from "./node-id.js";
|
|
41
|
+
import { clearSwitchHold, describeSwitchHold, recordSwitchHold, successProvesSwitchCleared, } from "./switch-hold.js";
|
|
42
|
+
import { NO_ORIGIN_REMEDY } from "./fence-refusal.js";
|
|
41
43
|
/** #884 — journal TICKETS (run completions #468, ask answers #486) must be
|
|
42
44
|
* keyed by the REAL tab a run/card was routed to: the panel reports back under
|
|
43
45
|
* that tab id, and a ticket keyed by the shared scope address a tool ctx is
|
|
@@ -3446,11 +3448,12 @@ panelGapNote = "") {
|
|
|
3446
3448
|
// the tab" — the reporter did that repeatedly, including closing and
|
|
3447
3449
|
// reopening it, and it could never have worked.
|
|
3448
3450
|
r.refusalReason && /no server-observed Origin/i.test(r.refusalReason)
|
|
3449
|
-
?
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3451
|
+
? // panel#1065 — SHARED with fence-refusal.ts, because this copy went stale.
|
|
3452
|
+
// It still described the world before #1240 gave the relay path its
|
|
3453
|
+
// origin: it told the user to abandon the relay backend and to report
|
|
3454
|
+
// a protocol gap that had already been closed. Someone did exactly
|
|
3455
|
+
// that, on a version that had the fix.
|
|
3456
|
+
NO_ORIGIN_REMEDY
|
|
3454
3457
|
: RELOAD_TAB_REMEDY}`,
|
|
3455
3458
|
};
|
|
3456
3459
|
case "adopt_error":
|
|
@@ -5232,7 +5235,14 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
5232
5235
|
await awaitReachable();
|
|
5233
5236
|
}
|
|
5234
5237
|
ensureReachable();
|
|
5235
|
-
|
|
5238
|
+
const firstTry = ok(await sendRouted(cmd, timeoutMs, observeRid));
|
|
5239
|
+
// panel#1097 — a guard-domain command that SUCCEEDS is the evidence that the
|
|
5240
|
+
// switch is over, whichever attempt lands it. Without this an ordinary
|
|
5241
|
+
// first-attempt success left the old run in place, and a later unrelated
|
|
5242
|
+
// switch inherited its age and was announced as stuck at once (codex r4).
|
|
5243
|
+
if (successProvesSwitchCleared(cmd.cmd))
|
|
5244
|
+
clearSwitchHold(ctx.tabId);
|
|
5245
|
+
return firstTry;
|
|
5236
5246
|
}
|
|
5237
5247
|
catch (err) {
|
|
5238
5248
|
// Post-reconnect retry-once: a reboot/free_vram/reconnect can drop the tab's
|
|
@@ -5246,10 +5256,30 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
5246
5256
|
// is what retrySettleMs already waits. Still gated on RETRY-SAFE commands,
|
|
5247
5257
|
// so a mutation is never re-issued on our own initiative.
|
|
5248
5258
|
if (isRetrySafeCmd(cmd) && (isTransientReconnectError(err) || isWorkflowSwitchGuardRefusal(err))) {
|
|
5259
|
+
// panel#1097 — declared out here so the refusal branch below can see it,
|
|
5260
|
+
// and REASSIGNED after ensureReachable so it names the tab the command is
|
|
5261
|
+
// actually dispatched on. Reading it before the rebind attributed a refusal
|
|
5262
|
+
// from B to A; reading it after the await let a later rebind move it again
|
|
5263
|
+
// (codex rounds 1 and 2).
|
|
5264
|
+
let holdTab = ctx.tabId;
|
|
5249
5265
|
try {
|
|
5250
5266
|
await sleep(retrySettleMs());
|
|
5251
5267
|
ensureReachable(); // rebinds a current-mode session onto the reconnected tab
|
|
5252
|
-
|
|
5268
|
+
holdTab = ctx.tabId;
|
|
5269
|
+
const retried = ok(await sendRouted(cmd, timeoutMs, observeRid));
|
|
5270
|
+
// Cleared HERE and nowhere else, and only when the failure this retried
|
|
5271
|
+
// was a SWITCH refusal. Clearing on every routed success let an unguarded
|
|
5272
|
+
// status read wipe the evidence while graph commands stayed refused
|
|
5273
|
+
// (codex round 2); clearing after a transient-reconnect retry does the
|
|
5274
|
+
// same thing one step removed, because reconnecting proves nothing about
|
|
5275
|
+
// whether the switch cleared (codex round 3).
|
|
5276
|
+
// Same rule on the retry path. A transient-RECONNECT retry that succeeds
|
|
5277
|
+
// proves nothing about the switch (codex r3), so the command still has to
|
|
5278
|
+
// be one the guard would have refused.
|
|
5279
|
+
if (isWorkflowSwitchGuardRefusal(err) && successProvesSwitchCleared(cmd.cmd)) {
|
|
5280
|
+
clearSwitchHold(holdTab);
|
|
5281
|
+
}
|
|
5282
|
+
return retried;
|
|
5253
5283
|
}
|
|
5254
5284
|
catch (err2) {
|
|
5255
5285
|
// #1027 — a switch STILL in progress is not a reconnect, and saying so
|
|
@@ -5257,13 +5287,18 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
5257
5287
|
// it is simply mid-switch. Name the actual state and the actual wait.
|
|
5258
5288
|
if (isWorkflowSwitchGuardRefusal(err2)) {
|
|
5259
5289
|
const name = typeof cmd.cmd === "string" ? cmd.cmd : "panel command";
|
|
5290
|
+
// panel#1097 — time the RUN of these refusals. "Simply retry" is right
|
|
5291
|
+
// for the fraction of a second this normally lasts and wrong for a
|
|
5292
|
+
// switch held open by a dialog, and the two were indistinguishable.
|
|
5293
|
+
const held = describeSwitchHold(recordSwitchHold(holdTab));
|
|
5260
5294
|
return fail(`${name} was NOT applied — nothing changed. The panel is still switching or ` +
|
|
5261
5295
|
`reloading the workflow on the canvas, which it refuses commands during so a ` +
|
|
5262
5296
|
`command cannot land on the wrong graph. The tab is connected and this is not a ` +
|
|
5263
5297
|
`reconnect: it normally clears in well under a second, so simply retry. If a ` +
|
|
5264
5298
|
`switch appears stuck, check the canvas — a load dialog or an unsaved-changes ` +
|
|
5265
5299
|
`prompt can hold it open awaiting the user. ` +
|
|
5266
|
-
`(${err2 instanceof Error ? err2.message : String(err2)})`
|
|
5300
|
+
`(${err2 instanceof Error ? err2.message : String(err2)})` +
|
|
5301
|
+
held);
|
|
5267
5302
|
}
|
|
5268
5303
|
// The retry also failed — surface an actionable reconnecting status rather
|
|
5269
5304
|
// than a bare transport error (#332), while still failing honestly.
|