comfyui-mcp 0.52.42 → 0.52.44
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/index.js +112 -39
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/pair-durability.js +7 -7
- package/dist/orchestrator/pair-durability.js.map +1 -1
- package/dist/orchestrator/pair-update-prefs.js +63 -0
- package/dist/orchestrator/pair-update-prefs.js.map +1 -0
- package/dist/orchestrator/panel-tools.js +533 -121
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/orchestrator/promoted-widget.js +52 -0
- package/dist/orchestrator/promoted-widget.js.map +1 -1
- package/dist/orchestrator/run-completion-journal.js +16 -0
- package/dist/orchestrator/run-completion-journal.js.map +1 -1
- package/dist/orchestrator/run-completion-watchdog.js +166 -68
- package/dist/orchestrator/run-completion-watchdog.js.map +1 -1
- package/dist/orchestrator/turn-origins.js +17 -0
- package/dist/orchestrator/turn-origins.js.map +1 -1
- package/dist/services/auto-update-gate.js +67 -0
- package/dist/services/auto-update-gate.js.map +1 -0
- package/dist/services/panel-auto-update.js +137 -0
- package/dist/services/panel-auto-update.js.map +1 -0
- package/dist/services/self-restart.js +63 -3
- package/dist/services/self-restart.js.map +1 -1
- package/dist/services/ui-bridge.js +15 -4
- package/dist/services/ui-bridge.js.map +1 -1
- package/package.json +1 -1
|
@@ -30,8 +30,11 @@ import { panelRecoveryContext } from "../services/panel-recovery.js";
|
|
|
30
30
|
import { isPanelAutoInstallDisabled } from "../services/panel-installer.js";
|
|
31
31
|
import { releaseOwnedPanelLock } from "../services/panel-pin-guard.js";
|
|
32
32
|
import { SelfRestarter, canSelfRestart } from "../services/self-restart.js";
|
|
33
|
+
import { tickPanelAutoUpdate } from "../services/panel-auto-update.js";
|
|
33
34
|
import { pairUrlDurability } from "./pair-durability.js";
|
|
34
35
|
import { loadOrCreatePairToken } from "./pair-token-store.js";
|
|
36
|
+
import { autoUpdateApplyAllowed, pairAutoUpdateDisclosure, pairingActiveOf, pairingTransportOf, } from "../services/auto-update-gate.js";
|
|
37
|
+
import { loadPairUpdatePrefs, savePairUpdatePrefs } from "./pair-update-prefs.js";
|
|
35
38
|
import { SessionStore, workflowIdentityParts, carryWorkflowCommandStamp } from "./session-store.js";
|
|
36
39
|
import { unreachableReason, noPanelTabReason, identityReason } from "./fence-refusal.js";
|
|
37
40
|
import { SHARED_SESSION_SCOPE, isScopeAddress, conversationTabs, shouldRetireSharedAgent, messageOrigin, normalizeHelloBackend, workflowOriginNote, } from "../services/session-scope.js";
|
|
@@ -81,7 +84,7 @@ import { PanelAgentManager, fetchSupportedModels, fetchSupportedCommands, isEffo
|
|
|
81
84
|
import { promptText } from "./error-text.js";
|
|
82
85
|
import { callToolAdmission } from "./call-tool-admission.js";
|
|
83
86
|
import { DEFERRED_PANEL_TOOLS_STEERING, withDeferredPanelToolsNote, } from "../deferred-panel-tools.js";
|
|
84
|
-
import { createPanelMcpServer, makePanelToolCtx, resolvePinTarget, secretSavedReply, setApplyMcpReload, RETRY_TOKEN_CMDS, } from "./panel-tools.js";
|
|
87
|
+
import { createPanelMcpServer, makePanelToolCtx, resolvePinTarget, secretSavedReply, setApplyMcpReload, forgetAbandonedConfirmCards, RETRY_TOKEN_CMDS, } from "./panel-tools.js";
|
|
85
88
|
import { optionsAckFrame, optionsErrorAckFrame, optionsRequestMeta, } from "./options-ack.js";
|
|
86
89
|
import { readUserMcpServers } from "../services/user-mcp-config.js";
|
|
87
90
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -124,7 +127,7 @@ import { startPanelConsoleHttpServer } from "./panel-console-http.js";
|
|
|
124
127
|
import { readComfyuiCrashLog, formatCrashNote } from "../services/crash-log.js";
|
|
125
128
|
import { QueueMonitor } from "../services/queue-monitor.js";
|
|
126
129
|
import { RunCompletions, describe as describeCorrelation, } from "./run-completion-journal.js";
|
|
127
|
-
import { createRunCompletionWatchdog, resolveHistoryCompletionImages } from "./run-completion-watchdog.js";
|
|
130
|
+
import { createRunCompletionWatchdog, resolveHistoryCompletionImages, resolveHistoryCompletionStatus, } from "./run-completion-watchdog.js";
|
|
128
131
|
import { AskAnswers, preview as previewQuestion } from "./ask-answer-journal.js";
|
|
129
132
|
import { initRunpodWatcher, getRunpodWatcher } from "../services/runpod-watch.js";
|
|
130
133
|
import { getPod } from "../services/runpod-client.js";
|
|
@@ -1054,20 +1057,25 @@ export async function runPanelOrchestrator() {
|
|
|
1054
1057
|
let pairToken = envPairToken;
|
|
1055
1058
|
let pairListenerStarted = false;
|
|
1056
1059
|
let pairTunnel = null;
|
|
1060
|
+
// Forward declaration — assigned after teardownCore exists. Declared here so
|
|
1061
|
+
// pair / apply_updates_now handlers can refresh the restarter without hitting
|
|
1062
|
+
// the TDZ if a frame arrives before that assignment.
|
|
1063
|
+
let selfRestarter = null;
|
|
1057
1064
|
/**
|
|
1058
|
-
* #
|
|
1065
|
+
* #1963 — APPLY gate for auto-update. Checking still runs.
|
|
1059
1066
|
*
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1067
|
+
* pairingActive is STICKY on `pairTunnel` (the handle exists from mint until
|
|
1068
|
+
* process exit). A live-socket test is the hole a locked-screen phone falls
|
|
1069
|
+
* through: the OS has closed the socket, the gate opened, the hostname
|
|
1070
|
+
* rotated. The pair-time toggle (default ON) is how a desk session applies
|
|
1071
|
+
* anyway; apply_updates_now is the one-shot override.
|
|
1065
1072
|
*/
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1073
|
+
const autoUpdateGateInput = () => ({
|
|
1074
|
+
activeTransport: pairingTransportOf(pairTunnel),
|
|
1075
|
+
pairingActive: pairingActiveOf(pairTunnel),
|
|
1076
|
+
deferWhilePaired: loadPairUpdatePrefs().deferWhilePaired,
|
|
1077
|
+
});
|
|
1078
|
+
const applyAutoUpdateAllowed = () => autoUpdateApplyAllowed(autoUpdateGateInput());
|
|
1071
1079
|
// #875 — the token is PERSISTED, not per-session. It used to be minted fresh on
|
|
1072
1080
|
// every run, so a self-restart (on by default, hourly npm check) invalidated the
|
|
1073
1081
|
// URL the phone had saved. The reporter experienced that as "updating the npm
|
|
@@ -2608,7 +2616,14 @@ export async function runPanelOrchestrator() {
|
|
|
2608
2616
|
// newcomer never asked the previous occupant's questions, so its answers must
|
|
2609
2617
|
// stop being recoverable and stop being pushed. closeAsks downgrades and
|
|
2610
2618
|
// unsends; it never deletes, so those answers are still reported.
|
|
2611
|
-
|
|
2619
|
+
// panel#1554 — same boundary, same reason: a confirmation card the previous
|
|
2620
|
+
// occupant abandoned must not be claimable by the newcomer. Both retirements ride
|
|
2621
|
+
// THIS one listener because the bridge takes a single takeover listener, so a
|
|
2622
|
+
// second setTabTakenOverListener call would silently replace the first.
|
|
2623
|
+
bridge.setTabTakenOverListener((tabId) => {
|
|
2624
|
+
AskAnswers.closeAsks(tabId);
|
|
2625
|
+
forgetAbandonedConfirmCards(tabId);
|
|
2626
|
+
});
|
|
2612
2627
|
bridge.setTabGoneListener((tabId, incarnation) => AskAnswers.retireDebt(tabId, incarnation));
|
|
2613
2628
|
// #694 — the bridge retains a late mutation only for commands that can come
|
|
2614
2629
|
// back as a retry token, which is the retry-token layer's own set. Installed
|
|
@@ -2914,6 +2929,12 @@ export async function runPanelOrchestrator() {
|
|
|
2914
2929
|
backendForTab,
|
|
2915
2930
|
backendOfKey: backendOf,
|
|
2916
2931
|
info: (msg) => logger.info(msg),
|
|
2932
|
+
// panel#1557 — a unique-canvas reconnect that hello'd without `backend`
|
|
2933
|
+
// joined the default conversation; claiming puts it on this one so the
|
|
2934
|
+
// pin we just wrote is not immediately invalidated at resolution.
|
|
2935
|
+
claimTab: (tab, backend) => {
|
|
2936
|
+
tabBackends.set(tab, backend);
|
|
2937
|
+
},
|
|
2917
2938
|
}));
|
|
2918
2939
|
// #884 P1 (confirming gate 2) — a hello whose `backend` is absent or unknown
|
|
2919
2940
|
// JOINS the default conversation, so its backend-qualified buffers must drain
|
|
@@ -3504,6 +3525,10 @@ export async function runPanelOrchestrator() {
|
|
|
3504
3525
|
logger.warn(`[panel-orchestrator] readiness probe failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3505
3526
|
}
|
|
3506
3527
|
}
|
|
3528
|
+
// Assigned next to the queue-status timer below. Declared here so the hello
|
|
3529
|
+
// path can reconcile owed panel_run tickets the moment the panel reconnects
|
|
3530
|
+
// (#1556) without waiting for that timer to construct it.
|
|
3531
|
+
let runCompletionWatchdog;
|
|
3507
3532
|
bridge.onPanelMessage = (event) => {
|
|
3508
3533
|
// Connect ack: the instant a panel tab connects, the orchestrator announces
|
|
3509
3534
|
// itself so "connected" means "a real agent is attending" — not merely "a
|
|
@@ -3958,6 +3983,12 @@ export async function runPanelOrchestrator() {
|
|
|
3958
3983
|
// are change-only, so a tab connecting MID-render would otherwise wait for
|
|
3959
3984
|
// the next state transition to learn a job is already running.
|
|
3960
3985
|
bridge.push(buildQueueStatusFrame(QueueMonitor.snapshot()), panelTab);
|
|
3986
|
+
// #1556 — a hello is a reconnect (or the first chance after a missed
|
|
3987
|
+
// ComfyUI WS frame). Look up every still-owed panel_run in /history NOW
|
|
3988
|
+
// and journal a completion if ComfyUI already finished it, instead of
|
|
3989
|
+
// waiting the synthesis grace. Exactly-once: the journal skips a run it
|
|
3990
|
+
// already holds.
|
|
3991
|
+
void runCompletionWatchdog?.reconcile(RunCompletions.owedCompletions().map((t) => t.promptId));
|
|
3961
3992
|
// Seed the RunPod control panel too — a tab that just connected gets the
|
|
3962
3993
|
// current pod-status frame (or a cleared one when nothing is watched).
|
|
3963
3994
|
const rpFrame = getRunpodWatcher()?.current();
|
|
@@ -4538,6 +4569,18 @@ export async function runPanelOrchestrator() {
|
|
|
4538
4569
|
if (event.type === "pair" && event.tab_id) {
|
|
4539
4570
|
const mode = event.mode === "tunnel" ? "tunnel" : "lan";
|
|
4540
4571
|
const tabId = event.tab_id;
|
|
4572
|
+
// Pair-time toggle, default ON. An explicit boolean is the only thing we
|
|
4573
|
+
// persist — a missing field leaves the last saved (or default) value.
|
|
4574
|
+
const rawDefer = event.defer_while_paired;
|
|
4575
|
+
if (typeof rawDefer === "boolean") {
|
|
4576
|
+
try {
|
|
4577
|
+
savePairUpdatePrefs({ deferWhilePaired: rawDefer });
|
|
4578
|
+
}
|
|
4579
|
+
catch (err) {
|
|
4580
|
+
logger.warn(`[panel-orchestrator] could not persist pair-update prefs: ${err instanceof Error ? err.message : String(err)}`);
|
|
4581
|
+
}
|
|
4582
|
+
selfRestarter?.requestApplyPolicyRefresh();
|
|
4583
|
+
}
|
|
4541
4584
|
void (async () => {
|
|
4542
4585
|
const token = await ensurePairListener();
|
|
4543
4586
|
let url;
|
|
@@ -4582,12 +4625,55 @@ export async function runPanelOrchestrator() {
|
|
|
4582
4625
|
(durability.survivesRestart
|
|
4583
4626
|
? "survives restart"
|
|
4584
4627
|
: `rotates on restart: ${durability.rotates.join(", ")}`));
|
|
4585
|
-
|
|
4628
|
+
const auto_update = pairAutoUpdateDisclosure(autoUpdateGateInput());
|
|
4629
|
+
bridge.push({ type: "pair_url", mode, url, durability, auto_update }, tabId);
|
|
4586
4630
|
})().catch((err) => {
|
|
4587
4631
|
bridge.push({ type: "pair_error", mode, error: err instanceof Error ? err.message : String(err) }, tabId);
|
|
4588
4632
|
});
|
|
4589
4633
|
return;
|
|
4590
4634
|
}
|
|
4635
|
+
// #1963 — flip the pair-time "Don't update while my phone is paired" toggle
|
|
4636
|
+
// after the URL is already minted (the checkbox on the QR modal).
|
|
4637
|
+
if (event.type === "set_pair_update_pref" && event.tab_id) {
|
|
4638
|
+
const tabId = event.tab_id;
|
|
4639
|
+
const rawDefer = event.defer_while_paired;
|
|
4640
|
+
if (typeof rawDefer !== "boolean") {
|
|
4641
|
+
bridge.push({ type: "pair_update_pref", ok: false, error: "defer_while_paired must be a boolean" }, tabId);
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4644
|
+
try {
|
|
4645
|
+
const prefs = savePairUpdatePrefs({ deferWhilePaired: rawDefer });
|
|
4646
|
+
bridge.push({
|
|
4647
|
+
type: "pair_update_pref",
|
|
4648
|
+
ok: true,
|
|
4649
|
+
defer_while_paired: prefs.deferWhilePaired,
|
|
4650
|
+
apply: autoUpdateApplyAllowed({
|
|
4651
|
+
...autoUpdateGateInput(),
|
|
4652
|
+
deferWhilePaired: prefs.deferWhilePaired,
|
|
4653
|
+
}),
|
|
4654
|
+
}, tabId);
|
|
4655
|
+
selfRestarter?.requestApplyPolicyRefresh();
|
|
4656
|
+
}
|
|
4657
|
+
catch (err) {
|
|
4658
|
+
bridge.push({
|
|
4659
|
+
type: "pair_update_pref",
|
|
4660
|
+
ok: false,
|
|
4661
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4662
|
+
}, tabId);
|
|
4663
|
+
}
|
|
4664
|
+
return;
|
|
4665
|
+
}
|
|
4666
|
+
// #1963 — "Update now" from the desk (or from the deferred-update notice).
|
|
4667
|
+
// One-shot: ignores the tunnel gate for this tick only.
|
|
4668
|
+
if (event.type === "apply_updates_now") {
|
|
4669
|
+
const tabId = event.tab_id;
|
|
4670
|
+
logger.info("[panel-orchestrator] apply_updates_now — checking and applying updates (bypass tunnel gate)");
|
|
4671
|
+
void selfRestarter?.updateTick({ forceApply: true });
|
|
4672
|
+
if (tabId) {
|
|
4673
|
+
bridge.push({ type: "ack", ok: true, kind: "apply_updates_now" }, tabId);
|
|
4674
|
+
}
|
|
4675
|
+
return;
|
|
4676
|
+
}
|
|
4591
4677
|
// Direct tool channel: the mobile app invokes a WHITELISTED read/download tool
|
|
4592
4678
|
// (structured data for nav lists + rig downloads) without an agent turn. Replies
|
|
4593
4679
|
// with a `tool_result` frame the client correlates by rid.
|
|
@@ -5899,9 +5985,10 @@ export async function runPanelOrchestrator() {
|
|
|
5899
5985
|
// after the grace is synthesised into the SAME journal the real frame uses.
|
|
5900
5986
|
// See run-completion-watchdog.ts for why it waits, and why the panel's frame
|
|
5901
5987
|
// still wins whenever it is coming.
|
|
5902
|
-
const
|
|
5988
|
+
const wd = createRunCompletionWatchdog({
|
|
5903
5989
|
awaiting: (promptId) => RunCompletions.awaitingCompletion(promptId),
|
|
5904
5990
|
resolveOutputs: (promptId) => resolveHistoryCompletionImages(promptId),
|
|
5991
|
+
lookupStatus: (promptId) => resolveHistoryCompletionStatus(promptId),
|
|
5905
5992
|
deliver: (payload, ticket) => {
|
|
5906
5993
|
// The SAME arrival path the panel's frame takes: correlated once, here,
|
|
5907
5994
|
// against the ticket that is still open — so the agent is told this is the
|
|
@@ -5914,13 +6001,14 @@ export async function runPanelOrchestrator() {
|
|
|
5914
6001
|
flushRunCompletions(ticket.tabId);
|
|
5915
6002
|
},
|
|
5916
6003
|
});
|
|
6004
|
+
runCompletionWatchdog = wd;
|
|
5917
6005
|
const queueStatusBroadcaster = createQueueStatusBroadcaster(() => QueueMonitor.snapshot(), (frame) => void bridge.push(frame),
|
|
5918
6006
|
// TEE, not a second drain: drainCompletions() splices, so the watchdog has
|
|
5919
6007
|
// to read the same array the broadcaster is handed rather than call it
|
|
5920
6008
|
// again — a second call would race and each consumer would see only half.
|
|
5921
6009
|
() => {
|
|
5922
6010
|
const completions = QueueMonitor.drainCompletions();
|
|
5923
|
-
|
|
6011
|
+
wd.observe(completions);
|
|
5924
6012
|
return completions;
|
|
5925
6013
|
});
|
|
5926
6014
|
// Each tick first refreshes the monitor over HTTP (GET /queue + /history
|
|
@@ -5934,7 +6022,7 @@ export async function runPanelOrchestrator() {
|
|
|
5934
6022
|
// …and only THEN expire the watchdog's arms: the broadcaster tick is what
|
|
5935
6023
|
// feeds it (the drain tee above), so ticking it first would let an
|
|
5936
6024
|
// observation sit a whole extra second before it is even armed. #1789.
|
|
5937
|
-
|
|
6025
|
+
wd.tick();
|
|
5938
6026
|
});
|
|
5939
6027
|
}, 1000);
|
|
5940
6028
|
queueStatusTimer.unref?.();
|
|
@@ -6205,9 +6293,6 @@ export async function runPanelOrchestrator() {
|
|
|
6205
6293
|
: " — remote target: installs/downloads run ON the ComfyUI host via its Manager (a local path would be the wrong filesystem; only local-FS tools like node_pack (action:'verify') are unavailable)";
|
|
6206
6294
|
logger.info(`[panel-orchestrator] ready — bridge on ws://127.0.0.1:${bridgePort}, console on ${consoleUrl}; an agent spawns per ComfyUI tab on its first message (model=${model}, comfyui=${comfyuiUrl}${pathNote})`);
|
|
6207
6295
|
let shuttingDown = false;
|
|
6208
|
-
// Forward declaration — assigned right after teardownCore exists; teardownCore
|
|
6209
|
-
// stops its timers so no update-check tick can fire mid-teardown.
|
|
6210
|
-
let selfRestarter = null;
|
|
6211
6296
|
/** Everything shutdown does EXCEPT exiting — shared with the self-restarter,
|
|
6212
6297
|
* which spawns its replacement first and exits itself after this settles. */
|
|
6213
6298
|
const teardownCore = async () => {
|
|
@@ -6334,24 +6419,12 @@ export async function runPanelOrchestrator() {
|
|
|
6334
6419
|
// …and the same for an ask answer the user actually gave that has not
|
|
6335
6420
|
// reached the agent yet (#486). Restarting would destroy it silently.
|
|
6336
6421
|
!AskAnswers.hasOutstanding() &&
|
|
6337
|
-
!QueueMonitor.isBusy()
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
// an update nobody asked for at that moment — which is what the reporter
|
|
6344
|
-
// experienced and (reasonably) blamed on the npm version.
|
|
6345
|
-
//
|
|
6346
|
-
// Deferral, not cancellation: the update check still runs and the restart
|
|
6347
|
-
// stays armed, so it fires as soon as the tunnel goes away. The cost is
|
|
6348
|
-
// that a tunnel left up indefinitely postpones updates indefinitely, so it
|
|
6349
|
-
// is DISCLOSED — in pair-durability's tunnel note, which the panel shows at
|
|
6350
|
-
// pair time. Deliberately not announced from here: this is a predicate the
|
|
6351
|
-
// restarter polls, and emitting from it would either fire repeatedly or
|
|
6352
|
-
// need its own state to suppress itself. Pair time is also where the user
|
|
6353
|
-
// is actually looking (#1077: an orchestrator log may be unreachable).
|
|
6354
|
-
!tunnelPairingLive(),
|
|
6422
|
+
!QueueMonitor.isBusy(),
|
|
6423
|
+
// #1963 — transport gate is applyAllowed, not allIdle. Idle is in-flight
|
|
6424
|
+
// work; this is "would a restart rotate the phone's tunnel hostname?"
|
|
6425
|
+
// Checking still runs; APPLY (disk + restart, and the panel pack) does not.
|
|
6426
|
+
applyAllowed: applyAutoUpdateAllowed,
|
|
6427
|
+
panelTick: async ({ apply }) => tickPanelAutoUpdate({ apply }),
|
|
6355
6428
|
announce: (text) => void bridge.push({ type: "say", text }),
|
|
6356
6429
|
teardown: teardownCore,
|
|
6357
6430
|
});
|